原文地址:我的博客:vue源码分析——运行机制
执行机制
1、执行 new Vue({}),初始化,初始化生命周期,绑定 props,data,methods,computed,watch 等属性,还有注册相关全局方法(如destory 等)
2、然后根据实例化是否有 render 函数,有就直接进入下一步,没有就看有没有 template,然后模板编译转成 render 函数
3、然后 render 函数执行生成 VNode,这个过程中会读取相应的数据,也就是会将动态数据通过 Object.defineProperty 进行 getter 和 setter 的设置,来实现响应式和依赖收集
4、render 生成 VNode 树,通过_update 进行调用 patch 来将VNode 转化为真实 DOM,首次渲染
5、之后的数据变化 set 会通知依赖进行调用 Watcher 实例(当前依赖)的 run 方法重新获取(get)新的数据,并调用 Watcher 实例(当前依赖)绑定的 updateComponent 方法再次调用_render 生成新的 VNode,以及调用_update,进行 patch比较 来生成新的 DOM 渲染
补充
我们写的每一个 vue 文件,其实都是一个 component 组件,那么每一个组件都有生命周期。所以说是不是每一个组件都会调用 new Vue({})呢?答案:是的
当 patch 在处理 VNode 的时候,遇到组件 VNode,会调用 createComponent 方法,组件的构造器都是继承 Vue 的,所以相当于是生成了一个 Vue 实例,以及执行了挂载操作。
new vnode.componentOptions.Ctor(options); child.$mount(hydrating ? vnode.elm : undefined, hydrating);
具体详情可以参考 Virtaul DOM 篇章
运行版和完整版
首先我们可以看到 vue 源码打包出来的主要分为两种,一种是不带 runtime,一种是带 runtime 的,那么为什么需要两个版本呢?这两个版本有什么区别呢
// 以es模块机制为例
'web-full-esm': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.esm.js'),
format: 'es',
alias: { he: './entity-decoder' },
banner
},
'web-runtime-esm': {
entry: resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.esm.js'),
format: 'es',
banner
}
由上面的打包入口文件,可以看出,两者最大的区别就是是否带有编译器的代码,即将模板转成 render 函数的功能
编译器的作用就是将源代码转成目标代码
为什么这个功能可以省去呢
首先 vue 模板是不会被浏览器识别的,所以转成 render 函数这个操作是必须的!!!但是将这个操作放在哪里执行是可以选择的!!!
如果将这个功能通过 vue-loader 这样的工具实现,直接生成 render 函数给浏览器,那么就不需要加载完整版的 vue 文件,即减小浏览器引入 vue 文件的体积大小,从而提升性能
使用运行版开发的两种方式
- 直接写 render 函数
- 借助 vue-loader 能工具实现编译器的工作
因此使用 runtime 版本更加合理!!!
具体 vue 是如何不同的处理的,请看《生命周期》篇