Vue 进阶系列(三)之Render函数原理及实现
栏目: JavaScript · 发布时间: 5年前
内容简介:根据第一篇文章介绍的响应式原理,如下图所示。在初始化阶段,本质上发生在在更新阶段,
根据第一篇文章介绍的响应式原理,如下图所示。
在初始化阶段,本质上发生在 auto run
函数中,然后通过 render
函数生成 Virtual DOM
, view
根据 Virtual DOM
生成 Actual DOM
。因为 render
函数依赖于页面上所有的数据 data
,并且这些数据是响应式的,所有的数据作为组件 render
函数的依赖。一旦这些数据有所改变,那么 render
函数会被重新调用。
在更新阶段, render
函数会重新调用并且返回一个新的 Virtual Dom
,新旧 Virtual DOM
之间会进行比较,把diff之后的最小改动应用到 Actual DOM
中。
Watcher负责收集依赖,清除依赖和通知依赖。在大型复杂的组件树结构下,由于采用了精确的依赖追踪系统,所以会避免组件的过度渲染。
Actual DOM 和 Virtual DOM
Actual DOM 通过document.createElement('div')生成一个DOM节点。
document.createElement('div') // 浏览器原生对象(开销大) "[object HTMLDivElement]" 复制代码
Virtual DOM 通过 vm.$createElement('div')生成一个JS对象,VDOM对象有一个表示div的tag属性,有一个包含了所有可能特性的data属性,可能还有一个包含更多虚拟节点的children列表。
vm.$createElement('div') // 纯JS对象(轻量) { tag: 'div', data: { attrs: {}, ...}, children: [] } 复制代码
因为Virtual DOM的渲染逻辑和Actual DOM解耦了,所以有能力运行在的非浏览器环境中,这就是为什么Virtual DOM出现之后混合开发开始流行的原因,React Native 和 Weex能够实现的原理就是这个。
JSX和Template
JSX和Template都是用于声明DOM和state之间关系的一种方式,在Vue中,Template是默认推荐的方式,但是也可以使用JSX来做更灵活的事。
JSX更加动态化,对于使用编程语言是很有帮助的,可以做任何事,但是动态化使得编译优化更加复杂和困难。
Template更加静态化并且对于表达式有更多约束,但是可以快速复用已经存在的模板,模板约束意味着可以在编译时做更多的性能优化,相对于JSX在编译时间上有着更多优势。
实例1:实现example组件
要求使用如下
<example :tags="['h1', 'h2', 'h3']"></example> 复制代码
要求输出如下
<div> <h1>0</h1> <h2>1</h2> <h3>2</h3> </div> 复制代码
上面这个需求可以通过 render
函数来做,官方提供了 createElement
函数用来生成模板。 createElement('div', {}, [...])
可接受的参数如下。
// @returns {VNode} createElement( // {String | Object | Function} // 一个 HTML 标签字符串,组件选项对象,或者 // 解析上述任何一种的一个 async 异步函数。必需参数。 'div', // {Object} // 一个包含模板相关属性的数据对象 // 你可以在 template 中使用这些特性。可选参数。 { }, // {String | Array} // 子虚拟节点 (VNodes),由 `createElement()` 构建而成, // 也可以使用字符串来生成“文本虚拟节点”。可选参数。 [ '先写一些文字', createElement('h1', '一则头条'), createElement(MyComponent, { props: { someProp: 'foobar' } }) ] ) 复制代码
知道了用法之后,就可以在 render
中返回 createElement
生成的虚拟节点,外层是 div
,内层是三个锚点标题 h1 h2 h3
,所以内层需要遍历下,使用两个 createElement
就可以完成了。
通常使用 h
作为 createElement
的别名,这是 Vue
的通用惯例,也是 JSX
的要求。
实现如下
<!--引用--> <script src="../node_modules/vue/dist/vue.js"></script> <!--定义template --> <div id="app"> <example :tags="['h1', 'h2', 'h3']"></example> </div> <script> // 定义example组件 Vue.component('example', { props: ['tags'], render (h) { // 第二个参数是一个包含模板相关属性的数据对象,可选参数 // 子虚拟节点(VNodes)参数可以传入字符串或者数字, // 通过createElement生成,可选参数 return h('div', this.tags.map((tag, i) => h(tag, i))) } }) // 实例化 new Vue({ el: '#app' }) </script> 复制代码
实例2:实现动态的 <example>
组件
要求如下
- 实现一个
Foo
组件渲染<div>foo</div>
,实现一个Bar
组件渲染<div>bar</div>
。 - 实现一个
<example>
组件,根据属性ok
动态渲染Foo
组件或者Bar
组件。如果属性ok
是true
,那么最终的渲染应该是<div>foo</div>
。 - 实现一个按钮控制属性
ok
,通过这个属性让<example>
在Foo
或者Bar
之间切换。
根据上面的要求,在模板中调用 <example>
组件,然后定义 <button>
组件,同时绑定属性 ok
。
实现如下
<!--引用--> <script src="../node_modules/vue/dist/vue.js"></script> <!--定义template --> <div id="app"> <!--绑定属性ok--> <example :ok="ok"></example> <!--绑定点击事件--> <button @click="ok = !ok">toggle</button> </div> <script> // 定义Foo const Foo = { render (h) { return h('div', 'foo') } } // 定义Bar const Bar = { render (h) { return h('div', 'bar') } } // 定义example组件 // 根据ok属性动态切换 Vue.component('example', { props: ['ok'], render (h) { return h(this.ok ? Foo : Bar) } }) // 实例化 new Vue({ el: '#app', data: { ok: true } }) </script> 复制代码
实例3:实现组件
要求如下
- 实现一个
withAvatarURL
函数,要求传入一个带有url
属性的组件,返回一个接收username
属性的高阶组件,这个高阶组件主要负责获取相应的头像URL。 - 在API返回之前,高阶组件将占位符URL
http://via.placeholder.com/200x200
传递给内部组件。
例子如下
const SmartAvatar = withAvatarURL(Avatar) // 使用这个方式 <smart-avatar username="vuejs"></smart-avatar> // 替换下面的方式 <avatar url="/path/to/image.png"></avatar> 复制代码
withAvatarURL
函数返回一个对象,接收 username
属性,在生命周期 created
获取头像URL。 Avatar
对象接收 src
属性, src
的内容从 withAvatarURL
中获取,然后展示在上。实例化的时候,传入新定义的组件名 SmartAvatar
。
实现如下
<!--引用--> <script src="../node_modules/vue/dist/vue.js"></script> <!--定义template--> <div id="app"> <smart-avatar username="vuejs"></smart-avatar> </div> <script> // 获取头像URL function fetchURL (username, cb) { setTimeout(() => { // 获取头像并回传 cb('https://avatars3.githubusercontent.com/u/6128107?v=4&s=200') }, 500) } // 传递的InnerComponent const Avatar = { props: ['src'], template: `<img :src="src">` } function withAvatarURL (InnerComponent) { return { props: ['username'], inheritAttrs: false, // 2.4 only,组件将不会把未被注册的props呈现为普通的HTML属性 data () { return { url: null } }, created () { // 获取头像URL并回传给this.url fetchURL(this.username, url => { this.url = url }) }, render (h) { return h(InnerComponent, { attrs: this.$attrs, // 2.4 only,获取到没有使用的注册属性 props: { src: this.url || 'http://via.placeholder.com/200x200' } }) } } } const SmartAvatar = withAvatarURL(Avatar) // 实例化,新构造组件名为SmartAvatar或smart-avatar new Vue({ el: '#app', components: { SmartAvatar } }) </script> 复制代码
本文内容参考自VUE作者尤大的付费视频
Vue官网之渲染函数 & JSX交流
本人Github链接如下,欢迎各位Star
我是木易杨,现在是网易高级前端工程师,目前维护了一个高级前端进阶群,欢迎加入。接下来让我带你走进高级前端的世界,在进阶的路上,共勉!
以上所述就是小编给大家介绍的《Vue 进阶系列(三)之Render函数原理及实现》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 函数编程基本原理介绍
- Fishhook替换C函数的原理
- 交叉熵损失函数到底是什么原理?
- 【译】JavaScript 中的函数式编程原理
- jQuery $ 原理及extend函数源码实现
- 深度学习常用损失函数总览:基本形式、原理、特点
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Mathematica Cookbook
Sal Mangano / O'Reilly Media / 2009 / GBP 51.99
As the leading software application for symbolic mathematics, Mathematica is standard in many environments that rely on math, such as science, engineering, financial analysis, software development, an......一起来看看 《Mathematica Cookbook》 这本书的介绍吧!