生命周期,就是vue实例创建销毁的一个过程,主要分为创建挂载更新销毁四个阶段!

生命周期

大致流程就是:

  • vue实例初始化

  • 初始化响应式数据

  • 获取模版节点

  • 对模版进行编译

  • 挂载模版替换$el元素

  • 数据发生变化找到属性Dep依赖中所存放的Watcher通知并更新

  • diff新旧虚拟dom比对,找到最小差异,重新渲染真实DOM

  1. beforeCreate: 实例创建后, 在数据初始化之前调用!
    • 创建实例,会初始化Events事件,以及生命周期!
  2. created: 响应式数据初始化后调用!
    • 其次会初始化响应式数据,在这个阶段,会对options中的数据进行劫持,并通过defineProperty属性描述符,来对每一个属性添加get和set方法,以及会为每一个属性添加一个Dep依赖对象,方便后面数据变化时通知和更新操作!
  3. beforeMount: 挂载dom之前调用
    • 需要获取模版(template),根据el,或者template来找到对应的模版,进行模版编译!
  4. mounted: 挂载dom后,调用该钩子函数,
    • 此时会获取编译好的模版,来替换vue中的$el元素,进行挂载!
  5. beforeUpdate:响应式数据发生变化时,dom更新之前,会调用此钩子函数,
    • 此阶段dom中的数据还是更新之前的状态!
  6. updated: dom中的数据更新之后调用
    • 此时dom中的数据已被更新!
  7. beforeDestory: 在实例销毁前调用
    • 此时还可以访问实例中的属性和方法,以及dom!
  8. 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>

模版编译过程:

  1. 模版template转换为AST抽象语法树:
    • AST抽象语法树,就是通过js对象描述的模版元素以及属性文本内容!
    • AST抽象语法树,就是将语法表达式,通过对象树形式描述该语法内部所包含的内容!
    • AST抽象语法树js代码解析vue模版解析打包构建工具中都用到了AST语法树!
  2. AST语法树进行静态标记优化:
    • 模版不涉及到动态内容的节点做静态标记,关注需要动态变化的节点部分,减少不必要的DOM渲染!
    • 比如模版中,用到的指令,或者是响应式数据等,其它静态节点元素,会被标记为静态,后期只关注更新动态节点!
  3. 生成(generate)真实dom:
    • diff找到新旧DOM差异,通过Render函数,渲染和更新替换为真实DOM!

响应式原理

vue中的响应式,就是数据视图之间的一个绑定关系,当数据发生变化时,vue通知视图进行更新,而视图中的响应式数据发生变化时,则会更新对应的数据!

MVVM模式

mvvm模式是一种规范,由mvc模式转换而来!

M(Model) 代表的数据层响应式数据!

V(View)代表的是视图层!

VM(Vue) 代表的vue实例,主要负责数据层视图层之间的响应式关系!

Vue2中的响应式

vue2中的的响应式,主要是通过defineProperty方法实现的,该方法用来定义对象属性描述的,可以控制属性一些操作行为(是否可枚举,是否可配置,是否可删除)等相关属性配置,其中还包含了两个方法,getset 方法,当获取对象中对应的属性时,会触发getter操作,当对对象中的属性重新赋值时,会触发setter操作,通过这两个方法,可以有效的监听对象属性"获取"和"编辑"的监听!

响应式数据初始化

在初始化vue实例时,vue会对options中的数据进行响应式处理,通过Observer方法进行数据劫持,如果是对象或者是数组对象时,会通过迭代的方式调用 defineReactive 方法给每一个属性添加 gettersetter 方法,最后将数据代理到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();
            }
        }
    })
}

Depvue中的一个依赖对象,主要负责响应式数据依赖收集,当响应式属性值发生变化时,会通知更新操作!

响应式数据data props computed watch 以及props

依赖收集

依赖指的是响应式数据,比如template模版中动态绑定属性,或者展示的一些属性,这些都是属于依赖部分, 而这些依赖属性都会绑定一个watcher类,这个watcher类,主要负责监听响应式依赖属性变化的后续更新操作!

<template>
  <div class="app">
     <!--属性依赖 name age -->
     <span class="name">{{ name }}</span>
     <span class="age">{{ age }}</span>
  </div>
</template>