欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

vue运行机制

程序员文章站 2024-02-29 23:26:40
...

vue运行机制

vue运行机制

vue核心的执行过程主要分为这几个阶段:

1)模板编译:生成可复用的render函数

2)响应式:通过Object.definedProperty监听到对象属性的get和set,实现双向绑定

3)初始渲染:执行render函数,访问data中的值,会被get监听,调用patch方法生成vdom

4)数据改变:数据发生改变会触发set,会进行更新re-render,通过patch方法对新旧vnode对比,更新视图

Vue 如何解析模板

  • 模板是什么

    • 本质:模板就是字符串

    • 与html格式很像,但是模板中是有逻辑的,可以嵌入JS变量,如v-if, v-for等

    • 视图最终还是需要由模板生成 html 来显示

    • 模板必须先要转换成JS代码

      • 有逻辑(v-if, v-for),必须用JS才能实现(图灵完备)

      • 转换为html渲染页面,必须用JS才能实现

      • 因此,模板要转换成render函数

  • render函数

    • render函数包含了模板中所有的信息,返回 vnode,解决了模板中的逻辑(v-if, v-for)问题

    • 如何找到最终生成的render函数
      找到vue源码,src/compiler/codegen/index.js,generate函数的render返回值

  • render函数与vdom

    • 模板生成 html:vm._c

    • vm._c 和 snabbdom 中的 h 函数的实现很像,都是传入标签,属性,子元素作为参数

    • Vue.js 的 vdom 实现借鉴了 snabbdom

    • updateComponent 中实现了 vdom 的 patch

    • 页面首次渲染执行 updateComponent

    • data 中每次修改属性,都会执行 updateComponent

Vue运行机制

第一步:解析模板成render函数:

  • 首先要知道render函数的生成是在打包的时候,为什么呢?webpack打包的时候我们使用到了vue-template-compiler这个loader,它的作用是将template编译成render函数,所以说编译是第一步;响应式监听是在代码执行的时候。

  • 编译过程compile分为parse,optimize,generate三部分

    • parse:将template解析成抽象语法树

    • optimize:每次重新渲染中,DOM中有一部分是不需要改变的,我们称之为static sub-trees,这一部分可以分离出来存储成常量,然后re-render的过程中也不再去渲染它,以及后边的patch过程也不再管它,比如我们常见的html的header等基本不会改变。

    • generate:generate过程会根据ast生成虚拟dom树,即vnode(在代码中是${code}),另外还会生成optimize中的静态树。

    function generate (
      ast: ASTElement | void,
      options: CompilerOptions
    ): CodegenResult {
      const state = new CodegenState(options)
      const code = ast ? genElement(ast, state) : '_c("div")'
      return {
        render: `with(this){return ${code}}`,
        staticRenderFns: state.staticRenderFns
      }
    }
    

vue demo

<div id="app">
  <p>普通属性:{{ message }}</p>
  <p>{{msg()}}</p>
  <p>{{ct}}</p>
  <input v-model="message">
  <div v-for="item in items">
      {{ item.text }}
    </div>
    <button v-on:click="bindClick">点我抓同伟</button>
</div>

// js
new Vue({
  el: '#app',
  data: {
    message: '以vue的名义',
    items: [{
        text: '达康书记'
    }, {
        text: '育良书记'
    }]
  },
  methods: {
    bindClick: function() {
        this.message = '这就抓同伟去';
    },
    msg: function() {
        return this.message + "这个方法每次都会执行";
    }
  },
  computed: {
    ct: function() {
        return this.message + "计算属性并不会每次都执行";
    }
  }
})

对应的render函数

with(this) {
    return _c('div', {
        attrs: {
            "id": "app"
        }
    },
    [_c('p', [_v("普通属性:" + _s(message))]), _v(" "), _c('p', [_v(_s(msg()))]), _v(" "), _c('p', [_v(_s(ct))]), _v(" "), _c('input', {
        directives: [{
            name: "model",
            rawName: "v-model",
            value: (message),
            expression: "message"
        }],
        domProps: {
            "value": (message)
        },
        on: {
            "input": function($event) {
                if ($event.target.composing) return;
                message = $event.target.value
            }
        }
    }), _v(" "), _l((items),
    function(item) {
        return _c('div', [_v("\n\t\t  " + _s(item.text) + "\n\t   ")])
    }), _v(" "), _c('button', {
        on: {
            "click": bindClick
        }
    },
    [_v("点我出奇迹抓同伟")])], 2)
}
  • 模板中的data信息都变成了js变量

  • 模板中的v-model,v-if,v-for变成了js中的逻辑

  • render函数返回虚拟树vnode,详情见 https://www.jianshu.com/p/fc0084c97e05

第二步:响应式开始监听

  • 通过Object.definedProperty监听到对象属性的get和set,该过程封装在Observer中

  • 将data的属性代理到vm上

第三步:首次渲染,显示页面,且绑定依赖

  • 初次渲染,执行updateComponent(负责patch),执行vm._render()

  • 执行render函数,会访问data中的数据,访问时会被响应式的get监听到,get中会收集依赖(watcher)添加到消息订阅器(Dep)中

  • 执行updateComponent,会走到dom的patch方法中

  • patch会将vdom渲染成真实dom,初次渲染完成

  • 疑问:为什么要监听get,而不是set?

    • 因为data中的很多属性,有些被用到,有些没被用到

    • 只有被用到的属性才会走get

    • 如果没走get,那么set的时候也不用关心

    • 这样可以避免不必要的渲染

第四步:data发生变化,会触发re-render

  • data发生改变,set监听到

  • set中执行updateComponent

  • updateComponent重新执行vm._render()

  • 新生成的vnode和之前的vnode对比,通过patch进行对比

  • 将差异渲染到html

参考:
https://www.cnblogs.com/dora-zc/p/11111813.html#vue-%E5%A6%82%E4%BD%95%E8%A7%A3%E6%9E%90%E6%A8%A1%E6%9D%BF

相关标签: vue