高级前端进阶(id:FrontendGaoji)
作者:木易杨,资深前端工程师,前网易工程师,13K star Daily-Interview-Question 作者
这是Vue进阶系列的第三篇,前面两篇可以点击页尾左下角的“阅读原文”观看。
根据第一篇文章介绍的响应式原理,如下图所示。
在初始化阶段,本质上发生在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 通过document.createElement('div')生成一个DOM节点。
1document.createElement('div')
2
3// 浏览器原生对象(开销大)
4"[object HTMLDivElement]"
Virtual DOM 通过 vm.$createElement('div')生成一个JS对象,VDOM对象有一个表示div的tag属性,有一个包含了所有可能特性的data属性,可能还有一个包含更多虚拟节点的children列表。
1vm.$createElement('div')
2
3// 纯JS对象(轻量)
4{ tag: 'div', data: { attrs: {}, ...}, children: [] }
因为Virtual DOM的渲染逻辑和Actual DOM解耦了,所以有能力运行在的非浏览器环境中,这就是为什么Virtual DOM出现之后混合开发开始流行的原因,React Native 和 Weex能够实现的原理就是这个。
JSX和TemplateJSX和Template都是用于声明DOM和state之间关系的一种方式,在Vue中,Template是默认推荐的方式,但是也可以使用JSX来做更灵活的事。
JSX更加动态化,对于使用编程语言是很有帮助的,可以做任何事,但是动态化使得编译优化更加复杂和困难。
Template更加静态化并且对于表达式有更多约束,但是可以快速复用已经存在的模板,模板约束意味着可以在编译时做更多的性能优化,相对于JSX在编译时间上有着更多优势。
实例1:实现example组件要求使用如下
1
要求输出如下
1
2 0
3 1
4 2
5
上面这个需求可以通过render函数来做,官方提供了createElement 函数用来生成模板。createElement('div', {}, [...])可接受的参数如下。
1// @returns {VNode}
2createElement(
3 // {String | Object | Function}
4 // 一个 HTML 标签字符串,组件选项对象,或者
5 // 解析上述任何一种的一个 async 异步函数。必需参数。
6 'div',
7
8 // {Object}
9 // 一个包含模板相关属性的数据对象
10 // 你可以在 template 中使用这些特性。可选参数。
11 {
12
13 },
14
15 // {String | Array}
16 // 子虚拟节点 (VNodes),由 `createElement()` 构建而成,
17 // 也可以使用字符串来生成“文本虚拟节点”。可选参数。
18 [
19 '先写一些文字',
20 createElement('h1', '一则头条'),
21 createElement(MyComponent, {
22 props: {
23 someProp: 'foobar'
24 }
25 })
26 ]
27)
知道了用法之后,就可以在render中返回createElement生成的虚拟节点,外层是div,内层是三个锚点标题h1 h2 h3,所以内层需要遍历下,使用两个createElement就可以完成了。
通常使用h作为createElement的别名,这是Vue的通用惯例,也是JSX的要求。
实现如下
1实例2:实现动态的``组件
2
3
4
5
6
7
8
9
要求如下
实现一个Foo组件渲染
foo
,实现一个Bar组件渲染
bar
。
实现一个组件,根据属性ok动态渲染Foo组件或者Bar组件。如果属性ok是true,那么最终的渲染应该是
foo
。
实现一个按钮控制属性ok,通过这个属性让在Foo或者Bar之间切换。
根据上面的要求,在模板中调用
实现如下
1实例3:实现组件
2
3
4
5
6
7
8
9
10
11
12
13
14
要求如下
实现一个withAvatarURL函数,要求传入一个带有url属性的组件,返回一个接收username属性的高阶组件,这个高阶组件主要负责获取相应的头像URL。
在API返回之前,高阶组件将占位符URLhttp://via.placeholder.com/200x200传递给内部组件。
例子如下
1const SmartAvatar = withAvatarURL(Avatar)
2
3// 使用这个方式
4
5
6// 替换下面的方式
7
8
withAvatarURL函数返回一个对象,接收username属性,在生命周期created获取头像URL。Avatar对象接收src属性,src的内容从withAvatarURL中获取,然后展示在上。实例化的时候,传入新定义的组件名SmartAvatar。
实现如下
1
2
3
4
5
6
7
8
9



