
记录下vue原理中的一些知识点
本文最后更新于 2025-01-25,文章内容可能已经过时。
生命周期
生命周期
,就是vue实例
从创建
到销毁
的一个过程
,主要分为创建
、挂载
、更新
、销毁
四个阶段!
大致流程就是:
vue实例初始化
初始化响应式数据
获取模版节点
对模版进行编译
挂载模版替换$el元素
数据发生变化
,找到属性Dep依赖中所存放的Watcher
,通知并更新
diff新旧虚拟dom比对,找到最小差异,重新渲染真实DOM
beforeCreate:
实例创建
后, 在数据初始化之前
调用!- 创建实例,会
初始化Events事件,以及生命周期!
- 创建实例,会
created:
响应式数据初始化后调用
!- 其次会
初始化响应式数据
,在这个阶段,会对options中的数据进行劫持
,并通过defineProperty
属性描述符,来对每一个属性添加get和set方法
,以及会为每一个属性添加一个Dep依赖对象
,方便后面数据变化时通知和更新
操作!
- 其次会
beforeMount: 挂载dom之前
调用- 需要获取模版(
template
),根据el
,或者template
来找到对应的模版
,进行模版编译
!
- 需要获取模版(
mounted:
挂载dom后
,调用该钩子函数,- 此时会
获取编译好的模版
,来替换vue中的$el元素,进行挂载
!
- 此时会
beforeUpdate:
当响应式数据发生变化
时,dom更新之前
,会调用此钩子函数,- 此阶段
dom
中的数据
还是更新之前
的状态!
- 此阶段
updated:
dom
中的数据更新之后调用
,- 此时
dom
中的数据已被更新
!
- 此时
beforeDestory:
在实例销毁前调用- 此时还
可以访问实例中的属性和方法,以及dom
!
- 此时还
destoryed:
实例销毁
之后,dom已被卸载
!
模版渲染原理
在
vue
中通常编写的模版
,在运行时
,会将模版编译为一个render函数
,会将一些模版
中特殊的语法进行转换
,如指令
,动态插值表达式
等相关特属语法,转换
为浏览器能够认识的语法
!
<template>
<!--app 静态节点 -->
<div class="app">
<!--动态节点{{}}-->
<span class="name">{{ name }}</span>
<span class="age">{{ age }}</span>
</div>
</template>
<script>
export default{
name: "test",
data(){
return {
name: "张三",
age: 18
}
}
}
</script>
模版编译过程:
- 将
模版template
转换为AST抽象语法树
:AST抽象语法树
,就是通过js对象
来描述的模版元素
以及属性
和文本
内容!AST抽象语法树
,就是将语法表达式
,通过对象树
的形式
来描述该语法内部所包含的内容
!AST抽象语法树
,js代码解析
,vue模版解析
,打包构建工具
中都用到了AST语法树
!
- 对
AST语法树
进行静态标记优化
:- 将
模版
中不涉及到动态内容的节点做静态标记
,关注需要动态变化的节点
部分,减少不必要的DOM渲染
! - 比如
模版
中,用到的指令
,或者是响应式数据
等,其它静态节点元素
,会被标记为静态
,后期只关注更新动态节点
!
- 将
- 生成(
generate
)真实dom
:diff
找到新旧DOM差异
,通过Render
函数,渲染和更新
,替换为真实DOM
!
响应式原理
vue
中的响应式
,就是数据
与视图
之间的一个绑定关系
,当数据发生变化
时,vue
会通知视图进行更新
,而视图
中的响应式数据发生变化时
,则会更新对应的数据!
MVVM模式
mvvm
模式是一种规范
,由mvc
模式转换而来
!
M(Model)
代表的数据层
,响应式数据
!
V(View)
代表的是视图层!
VM(Vue)
代表的vue实例
,主要负责数据层
与视图层
之间的响应式关系!
Vue2中的响应式
vue2
中的的响应式
,主要是通过defineProperty
方法实现的,该方法用来定义对象属性描述
的,可以控制属性
一些操作行为(是否可枚举,是否可配置,是否可删除
)等相关属性配置
,其中还包含了两个方法,get
和set
方法,当获取对象中对应的属性
时,会触发getter操作
,当对对象中的属性重新赋值
时,会触发setter操作
,通过这两个方法,可以有效的监听对象属性"获取"和"编辑"的监听
!
响应式数据初始化
在初始化
vue
实例时,vue
会对options
中的数据进行响应式处理
,通过Observer
方法进行数据劫持
,如果是对象
或者是数组对象
时,会通过迭代
的方式调用 defineReactive 方法
给每一个属性
添加getter
和setter
方法,最后将数据代理到vue实例上
!
class Vue{
construcotr( options ){
this.$options = options;
this.$el = options.el;
this.$data = options.data;
// 数据劫持
Observer( this.$data );
// 将数据代理到vm实例上
Proxy(this)
// 模版编译
complier(this, el);
}
}
Observer数据劫持
function observer( obj ){
if( typeof data !== "Object" ){
return
}
for(let key in data){
defineReactive(data, key, data[key]);
}
}
defineReactive 添加 get和 set
function defineReactive(target, key, value){
Object.defineProperty(target, key, {
get(){
// 将watcher添加到dep中
if(Dep.target){
Dep.depend();
}
return value;
},
set( val ){
if( value !== val ){
value = val;
}
// 获取Dep依赖,调用watcher回调函数操作
if(Dep.target){
Dep.notify();
}
}
})
}
Dep
是vue
中的一个依赖对象
,主要负责响应式数据依赖收集
,当响应式属性值发生变化
时,会通知更新操作
!
响应式数据
:data props computed watch
以及props
!
依赖收集
依赖
指的是响应式数据
,比如template
模版中动态绑定
的属性
,或者展示
的一些属性
,这些都是属于依赖
部分, 而这些依赖属性
都会绑定一个watcher类
,这个watcher类
,主要负责监听响应式依赖属性变化的后续更新操作
!
<template>
<div class="app">
<!--属性依赖 name age -->
<div>
姓名: {{ name }}
年龄: {{ age }}
体重: {{ weghit }}
</div>
</div>
</template>
<script>
export default{
data(){
return {
name: "张三", // key1 watcher1
age: 18, // key2 watcher2
weghit: 120 // key3 watcher3
}
}
}
</script>
另外,
Watcher对象
由Dep依赖
对象来进行管理
,当数据发生变化
时,会触发Dep
操作,执行对应的Watcher回调函数
!
Dep deps = [Watcher1, Watcher2]
Dep deps = [Watcher3]
代码实现
Watcher监听响应式数据
class Watcher{
constructor( vm, key, update ){
this.$vm = vm;
this.key = key;
this.updateFun = update;
Dep.target = this; // 将watcher和依赖进行关联
vm[key]; // 触发getter操作
Dep.target = null;
}
updater(){
this.updateFun.call(this, this.$vm[key])
}
}
Dep依赖收集
class Dep{
// target 就是每个属性所对应的Watcher对象
static target = null;
constructor(){
this.deps = [];
}
// getter 触发时,如果target存在,将Watcher添加到Deps依赖中
addDeps( dep ){
this.deps.push(dep);
}
// setter 触发时,会执行Watcher中的更新操作
notify(){
this.deps.forEach(watcher => watcher.updater());
}
}
defineReactive响应式函数
function defineReactive(target, key, value){
const dep = new Dep();
Object.defineProperty(target, key, {
get(){
// 将watcher添加到dep中
if(Dep.target){
dep.addDeps(Dep.target);
}
return value;
},
set( val ){
if( value !== val ){
value = val;
}
// 获取Dep依赖,调用watcher回调函数操作
if(Dep.target){
dep.notify();
}
}
})
}
总结
Watcher
:Watcher
主要负责响应式数据依赖监听
,且每个属性key
都会有一个Watcher对象
!
Dep
:Dep
是一个依赖对象
,当数据被获取
时,dep
会将Watcher对象
添加到依赖数组
中!- 当
数据发生变化
时,会遍历依赖数组
,执行Watcher对象中的更新
操作!
defineReactive
:defineReactive
主要用来给属性
添加getter
和setter
操作!- 触发
getter
时,会调用Dep.addDeps
来收集依赖
! - 触发
setter
时,会调用Dep.notify
来更新依赖
!
父子组件生命周期执行顺序
在
vue
中,父组件
和子组件
生命周期之间的加载过程
,主要是在父组件
挂载(beforeMount
)之前,会将子组件进行挂载!
加载渲染过程
父
beforeCreate
-> 父created
-> 父beforeMount
-> 子beforeCreate
-> 子created
-> 子beforeMount
-> 子mounted
-> 父mounted
子组件更新过程
父
beforeUpdate
-> 子beforeUpdate
-> 子updated
-> 父updated
销毁过程
父
beforeDestroy
-> 子beforeDestroy
-> 子destroyed
-> 父destroyed