Vue 源码剖析-模板编译和组件化

1、模板编译

  • 模板编译的主要目的是将模板 (template) 转换为渲染函数 (render)
<div>
	<h1@click="handler">title</h1>
	<p>some content</p>
</div>
  • 渲染函数 render
render (h) {
	return h('div', [
		h('h1', { on: { click: this.handler} }, 'title'),
		h('p', 'some content')  
	])
}
  • 模板编译的作用
    • Vue 2.x 使用 VNode 描述视图以及各种交互,用户自己编写 VNode 比较复杂
    • 用户只需要编写类似 HTML 的代码 - Vue 模板,通过编译器将模板转换为返回 VNode 的render 函数
    • .vue 文件会被 webpack 在构建的过程中转换成 render 函数

体验模板编译的结果

  • 带编译器版本的 Vue.js 中,使用 template 或 el 的方式设置模板
<div id="app">
	<h1>Vue<span>模板编译过程</span></h1>
	<p>{{ msg }}</p>
	<comp@myclick="handler"></comp>
</div>
<scriptsrc="../../dist/vue.js"></script><script>
Vue.component('comp', {
	template: '<div>I am a comp</div>'  
})
const vm = new Vue({
	el: '#app',
	data: {
		msg: 'Hello compiler'    
	},
	methods: {
		handler () {
			console.log('test')      
		}
	}  
})
console.log(vm.$options.render)
</script>
  • 编译后 render 输出的结果
(function anonymous() {
	with (this) {
		return_c(
			"div",   
			{ attrs: { id: "app" } },      	
			[
				_m(0),
				_v(" "),
				_c("p", [_v(_s(msg))]),
				_v(" "),
				_c("comp", { on: { myclick: handler} }),     
			],
			1    
		);  
	}
});
  • _c 是 createElement() 方法,定义的位置 instance/render.js 中

  • 相关的渲染函数(_开头的方法定义),在 instance/render-helps/index.js 中

  • 把 template 转换成 render 的入口 src\platforms\web\entry-runtime-with-compiler.js

Vue Template Explorer

  • vue-template-explorer
    • Vue 2.6 把模板编译成 render 函数的工具
  • vue-next-template-explorer
    • Vue 3.0 beta 把模板编译成 render 函数的工具模板

模板编译过程

  • 解析、优化、生成

编译的入口

  • src\platforms\web\entry-runtime-with-compiler.js
Vue.prototype.$mount=function (
......
// 把 template 转换成 render 函数
const { render, staticRenderFns } = compileToFunctions(template, {
outputSourceRange: process.env.NODE_ENV!=='production',
shouldDecodeNewlines
,shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments  }, this)
options.render=render
options.staticRenderFns=staticRenderFns
......
)
  • 调试 compileToFunctions() 执行过程,生成渲染函数的过程
    • compileToFunctions: src\compiler\to-function.js
    • complie(template, options):src\compiler\create-compiler.js
    • baseCompile(template.trim(), finalOptions):src\compiler\index.js
      在这里插入图片描述

解析 - parse

  • 解析器将模板解析为抽象语树 AST,只有将模板解析成 AST 后,才能基于它做优化或者生成代码字符串。
    • src\compiler\index.js
const ast = parse(template.trim(), options) //src\compiler\parser\index.js 
parse()
  • 查看得到的 AST tree
    https://astexplorer.net/#/gist/30f2bd28c9bbe0d37c2408e87cabdfcc/1cd0d49beed22d3fc8e2ade0177bb22bbe4b907c
  • 结构化指令的处理
    • v-if 最终生成单元表达式
      v-if/v-for 结构化指令只能在编译阶段处理,如果我们要在 render 函数处理条件或循环只能使用js 中的 if 和 for
Vue.component('comp', {
	data: () {
		return {
			msg: 'my comp'
		}
	},
	render (h) {
		if (this.msg) {
			return h('div', this.msg)    
		}
		return h('div', 'bar')
	}
})

优化 - optimize

  • 优化抽象语法树,检测子节点中是否是纯静态节点
  • 一旦检测到纯静态节点,例如:
    hello整体是静态节点
    永远不会更改的节点
    • 提升为常量,重新渲染的时候不在重新创建节点
    • 在 patch 的时候直接跳过静态子树

生成 - generate

组件化机制

  • 组件化可以让我们方便的把页面拆分成多个可重用的组件
  • 组件是独立的,系统内可重用,组件之间可以嵌套
  • 有了组件可以像搭积木一样开发网页
  • 下面我们将从源码的角度来分析 Vue 组件内部如何工作
    • 组件实例的创建过程是从上而下
    • 组件实例的挂载过程是从下而上

组件声明

  • 复习全局组件的定义方式
Vue.component('comp', {
	template: '<h1>hello</h1>'
})
  • Vue.component() 入口
    • 创建组件的构造函数,挂载到 Vue 实例的 vm.options.component.componentName =Ctor
  • 组件构造函数的创建
  • 调试 Vue.component() 调用的过程
<div id="app">
</div>
<scriptsrc="../../dist/vue.js"></script>
<script>
const Comp = Vue.component('comp', {
	template: '<h2>I am a comp</h2>'  
})
const vm = new Vue({
	el: '#app',
	render (h) {
		return h(Comp)    
	}  
})
</script>

组件创建和挂载

组件 VNode 的创建过程

  • 创建根组件,首次 _render() 时,会得到整棵树的 VNode 结构
  • 整体流程:new Vue() --> $mount() --> vm._render() --> createElement() --> createComponent()
  • 创建组件的 VNode,初始化组件的 hook 钩子函数

组件实例的创建和挂载过程

  • Vue._update() --> patch() --> createElm() --> createComponent()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值