(四)Vue-渲染过程

Vue 的不同构建版本

完整版:同时包含编译器和运行时的版本。
编译器:用来将模板字符串编译成为 JavaScript 渲染函数的代码,体积大、效率低。
运行时:用来创建 Vue 实例、渲染并处理虚拟 DOM 等的代码,体积小(小大约 30%)、效率高。基本上就是除去编译器的代码。

UMD:UMD 版本通用的模块版本,支持多种模块方式。 vue.js 默认文件就是运行时 + 编译器的UMD 版本。
CommonJS(cjs):CommonJS 版本用来配合老的打包工具比如 Browserify 或 webpack 1。
ES Module:从 2.6 开始 Vue 会提供两个 ES Modules (ESM) 构建文件,为现代打包工具提供的版本。

  • ESM 格式被设计为可以被静态分析,所以打包工具可以利用这一点来进行“tree-shaking”并将用不到的代码排除出最终的包。
  • ES6 模块与 CommonJS 模块的差异

基于Vue 2.6版本

入口

  • el 不能是 body 或者 html 标签
  • 如果没有 render,把 template 转换成 render 函数
  • 如果有 render 方法,直接调用 mount 挂载 DOM
// 1. el 不能是 body 或者 html 
if (el === document.body || el === document.documentElement) {   
      process.env.NODE_ENV !== 'production' && warn( `Do not mount Vue to 
           <html> or <body> - mount to normal elements instead.` )
      return this 
}

const options = this.$options if (!options.render) { 
    // 2. 把 template/el 转换成 render 函数 …… 
}
    
// 3. 调用 mount 方法,挂载 DOM 
return mount.call(this, el, hydrating)

初始化流程

  1. src/platform/web/entry-runtime-with-compiler.js 中引用了 './runtime/index'
  2. src/platform/web/runtime/index.js
  • 设置 Vue.config
  • 设置平台相关的指令和组件
    指令 v-model、v-show
    组件 transition、transition-group
  • 设置平台相关的 patch 方法(打补丁方法,对比新旧的 VNode)
  • 设置 $mount 方法,挂载 DOM
// install platform runtime directives & components extend(Vue.options.directives, platformDirectives) extend(Vue.options.components, platformComponents) 

// install platform patch function 
Vue.prototype.__patch__ = inBrowser ? patch : noop

 // public mount method Vue.prototype.$mount = function ( 
    el?: string | Element, 
    hydrating?: boolean ): Component { 
    el = el && inBrowser ? query(el) : undefined 
    return mountComponent(this, el, hydrating) 
}
  1. src/platform/web/runtime/index.js 中引用了 'core/index'
  2. src/core/index.js
  • 定义了 Vue 的静态方法
  • initGlobalAPI(Vue)
  1. src/core/index.js 中引用了 './instance/index'
  2. src/core/instance/index.js
  • 定义了 Vue 的构造函数
// 此处不用 class 的原因是因为方便,后续给 Vue 实例混入实例成员 
function Vue (options) { 
    if (process.env.NODE_ENV !== 'production' && 
      !(this instanceof Vue) ) {
          warn('Vue is a constructor and should be called with the `new` keyword') 
    }

    // 调用 _init() 方法 
    this._init(options) 
}

// 注册 vm 的 _init() 方法,初始化 
vm initMixin(Vue) 
// 注册 vm 的 $data/$props/$set/$delete/$watch 
stateMixin(Vue) 
// 初始化事件相关方法 
// $on/$once/$off/$emit 
eventsMixin(Vue) 
// 初始化生命周期相关的混入方法 
// _update/$forceUpdate/$destroy 
lifecycleMixin(Vue) 
// 混入 render 
// $nextTick/_render 
renderMixin(Vue)

导出Vue涉及的四个模块

  1. src/platforms/web/entry-runtime-with-compiler.js
  • web 平台相关的入口
  • 重写了平台相关的 $mount() 方法
  • 注册了 Vue.compile() 方法,传递一个 HTML 字符串返回 render 函数
  1. src/platforms/web/runtime/index.js
  • web 平台相关
  • 注册和平台相关的全局指令:v-model、v-show
  • 注册和平台相关的全局组件: v-transition、v-transition-group
  • 全局方法:
    patch:把虚拟 DOM 转换成真实 DOM
    $mount:挂载方法
  1. src/core/index.js
  • 与平台无关
  • 设置了 Vue 的静态方法,initGlobalAPI(Vue)
  1. src/core/instance/index.js
  • 与平台无关
  • 定义了构造函数,调用了 this._init(options) 方法
  • 给 Vue 中混入了常用的实例成员

首次渲染过程

  1. 实例化Vue对象的时候。会调用this._init()进行一些初始化, 同时会调用vm.$mount函数。

这里的vm.$mount函数 首先调用src/platforms/web/entry-runtime-with-compiler.js 里面重写的$mount() 方法。

  1. 重写的vm.$mount函数: 通过Vue.compile() 方法,返回 render 和 staticRenderFns 函数, 存储在$options里面。 然后调用原始的vm.$mount函数。
  • 这里的render:如果用户定义,返回用户定义的; 否则返回模板生成的。
  • 原始的vm.$mount函数 是src/platforms/web/runtime/index.js 里面定义的
  • src/platforms/web/runtime/index.js里面同时定义了vm.patch方法把虚拟Dom转成真实Dom
  1. 原始的vm.$mount函数:调用 mountComponent方法。
    mountComponent定义在/src/core/instance/lifecycle.js
  2. mountComponent方法主要做了4件事:
  • 触发beforeMount钩子
  • 定义updateComponent方法, 注意这里只是定义, 触发是Watcher触发的。
  • 创建Watcher实例, 把updateComponent方法作为参数传入。
  • 触发mounted钩子
    4.1 updateComponent方法比较重要, 主要做了2件事:
    vm._rander渲染虚拟Dom。
    vm._rander其实就是调用第二步$options中的render方法
    vm._update更新,把虚拟Dom转成真实Dom 。
    vm._update其实就是调用src/platforms/web/runtime/index.js中的定义的vm.__patch__方法, 同时记录vm.$el
    4.2 Watcher构造方法
    会把传入的updateComponent参数赋值给this.getter。
    this.get()方法里面调用this.getter。
    立即调用this.get(), 进而触发渲染。

Demo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容