vue源码理解之模板编译和组件化

一:模板编译
1、模板编译的主要目标是将模板(template)转换为渲染函数(render)

在这里插入图片描述
template => render()

2、模板编译必要性
Vue 2.0需要用到VNode描述视图以及各种交互,手写显然不切实际,因此用户只需编写类似HTML代码的Vue模板,通过编译器将模板转换为可返回VNode的render函数

3、体验模板编译
带编译器的版本中,可以使用template或el的方式声明模板,06-1-compiler.html

(function anonymous(
) {
with(this){return _c('div',{attrs:{"id":"demo"}},[_c('h1',[_v("Vue模板编
译")]),_v(" "),_c('p',[_v(_s(foo))]),_v(" "),_c('comp')],1)}
})

输出结果大致如下:

(function anonymous() {
with(this){return _c('div',{attrs:{"id":"demo"}},[
  _c('h1',[_v("Vue模板编译")]),
  _v(" "),_c('p',[_v(_s(foo))]),
  _v(" "),_c('comp')],1)}
})

元素节点使用createElement创建,别名_c
本文节点使用createTextVNode创建,别名_v
表达式先使用toString格式化,别名_s
其他渲染helpers:src\core\instance\render-helpers\index.js

4、整体流程

compileToFunctions
若指定template或el选项,则会执行编译,platforms\web\entry-runtime-with-compiler.js

5、编译过程
编译分为三步:解析、优化和生成,src\compiler\index.js

6、解析模板编译过程
实现模板编译共有三个阶段:解析、优化和生成

  • 解析 - parse
    解析器将模板解析为抽象语法树,基于AST可以做优化或者代码生成工作。
    调试查看得到的AST,/src/compiler/parser/index.js,结构如下
    在这里插入图片描述
    解析器内部分了HTML解析器、文本解析器和过滤器解析器,最主要是HTML解析器
  • 优化 - optimize
    优化 - optimize
    优化器的作用是在AST中找出静态子树并打上标记。静态子树是在AST中永远不变的节点,如纯文本节点。

标记静态子树的好处:
每次重新渲染,不需要为静态子树创建新节点
虚拟DOM中patch时,可以跳过静态子树

  • 代码生成 - generate
    将AST转换成渲染函数中的内容,即代码字符串。
    generate方法生成渲染函数代码,src/compiler/codegen/index.js
`_c('div',{attrs:{"id":"demo"}},[
_c('h1',[_v("Vue.js测试")]),
_c('p',[_v(_s(foo))])
])`
  • 典型指令的实现:v-if、v-for
    着重观察几个结构性指令的解析过程
    解析v-if:parser/index.js
    processIf用于处理v-if解析
    解析结果:
    在这里插入图片描述
    代码生成,codegen/index.js
    genIfConditions等用于生成条件语句相关代码
    生成结果:
"with(this){return _c('div',{attrs:{"id":"demo"}},[
(foo) ? _c('h1',[_v(_s(foo))]) : _c('h1',[_v("no title")]),
  _v(" "),_c('abc')],1)}"

二:组件化机制
1、组件声明

Vue.component()
initAssetRegisters(Vue) src/core/global-api/assets.js
组件注册使用extend方法将配置转换为构造函数并添加到components选项

2、组件实例创建及挂载
观察生成的渲染函数

"with(this){return _c('div',{attrs:{"id":"demo"}},[
  _c('h1',[_v("虚拟DOM")]),_v(" "),
  _c('p',[_v(_s(foo))]),_v(" "),
  _c('comp') // 对于组件的处理并无特殊之处
],1)}"

3、整体流程
首先创建的是根组件,首次_render()时,会得到整棵树的VNode结构
整体流程:new Vue() => $mount() => vm._render() => createElement() => createComponent()
=》 patch =》createElm =》 createComponent()

4、创建自定义组件VNode

_createElement src\core\vdom\create-element.js
_createElement实际执行VNode创建的函数,由于传入tag是非保留标签,因此判定为自定义组件通过
createComponent去创建
createComponent src/core/vdom/create-component.js
创建组件VNode,保存了上一步处理得到的组件构造函数,props,事件等

注意组件钩子安装和组件tag指定规则

5、创建自定义组件实例
根组件执行更新函数时,会递归创建子元素和子组件,入口createElm

createEle() core/vdom/patch.js line751
首次执行_update()时,patch()会通过createEle()创建根元素,子元素创建研究从这里开始

createComponent core/vdom/patch.js line144
自定义组件创建

// 组件实例创建、挂载
if (isDef(i = i.hook) && isDef(i = i.init)) {
  i(vnode, false /* hydrating */)
}
if (isDef(vnode.componentInstance)) {
  // 元素引用指定vnode.elm,元素属性创建等
  initComponent(vnode, insertedVnodeQueue)
  // 插入到父元素
  insert(parentElm, vnode.elm, refElm)
  if (isTrue(isReactivated)) {
    reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm)
 }
  return true
}

6、结论:
组件创建顺序自上而下
组件挂载顺序自下而上

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

javascript_good

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值