[VueJS] V1 与 V2 组件实体之差异
狂贺! 终于正式发布!
关于 Vue 2.0 的新特性,作者也在官方 Blog - ()一文中叙述地相当详细,这里就不多说。
如果你也与我一样是从 V1 就开始接触的开发者,一定都知道 VueJS 最核心的一部分是 Component,而 Component 是由实体 (VueInstance) 来实现。我们谈谈 Vue 1.x 与 2.x 组件实体的差异。
Vue 2.0 组件实体注册
像上面这样的网站,我们可以将它抽象化为一棵「组件树」,而每个组件树都会有个根节点,或称为根实体 (root Vueinstance)。
那么,每个 Vue 组件树的根实体其实是透过 Vue
这个建构函式所产生:
将 Vue 组件与实体 DOM 结合的方式有两种,一种是直接写在 el
option 内:
而另一种方式则是透过 $mount
来指定节点:
这部分跟 Vue 1.x 的注册是完全一样的,但是需要注意的是,在 vue 1.x 允许开发者以 <body>
或 <html>
作为根实体的挂载点,但到了 VueJS 2.0 后,只能透过 独立的节点挂载 ,如: div 等。否则会产生错误,警告讯息如下:
“Do not mount Vue to <html> or <body> - mount to normal elements instead.“
换成用独立的 DOM 节点,如 <div id="app"></div>
,就可以正常运作了。
Vue 2.0 组件实体的生命周期
基本上 Vue 2.0 实体生命周期中,大部分的阶段都与 Vue 1.x 是一样的,最大的不同在于 lifecyclehook 名称的改变,以及在组件被挂载 mounted
之后,还新增了 beforeUpdate
以及 updated
这两组侦测更新的 hook。
vue 1.x 的 init
变成 beforeCreate
, beforeCompiled
改为 beforeMount
。而原本的 complied
与 ready
则是统一收敛成 mounted
。
另外需要注意的是,若组件本身是透过 server-siderendering 的话,除了 beforeCreate
以及 created
以外的所有 hook 都不会被呼叫(https://vuejs.org/api/#Options-Lifecycle-Hooks)。
有关组件 V-DOM 的重新渲染与更新后面再提,其他部分则与 Vue 1.x 大同小异。
Vue 2.0 组件与模板的编译 - RenderFunction
在大部分情况下,透过组件的 template
属性,或是直接写在 HTML 中就已经足够操作你的组件了。不过若是你想完全透过 JavaScript 来操作你的组件,那么可以使用 render 这个 function 直接来写底层的 virtual-DOM 来取代 template
属性。 VueJS 2.0 的 virtual DOM 机制,是采用 这个 virtual DOM 的 library 来实作的。
可以使用 createElement
这个 function 来建立你的组件内容:
// @returns {VNode}
createElement(
// {String | Object | Function}
// An HTML tag name, component options, or function
// returning one of these. Required.
'div',
// {Object}
// A data object corresponding to the attributes
// you would use in a template. Optional.
{
// (see details in the next section below)
},
// {String | Array}
// Children VNodes. Optional.
[
createElement('h1', 'hello world')
createElement(MyComponent, {
props: {
someProp: 'foo'
}
}),
'bar'
]
)
官方也提供了一个完整的 render function 范例:
var getChildrenTextContent = function (children) {
return children.map(function (node) {
return node.children
? getChildrenTextContent(node.children)
: node.text
}).join('')
}
Vue.component('anchored-heading', {
render: function (createElement) {
// create kebabCase id
var headingId = getChildrenTextContent(this.$slots.default)
.toLowerCase()
.replace(/\\W+/g, '-')
.replace(/(^\\-|\\-$)/g, '')
return createElement(
'h' + this.level,
[
createElement('a', {
attrs: {
name: headingId,
href: '#' + headingId
}
}, this.$slots.default)
]
)
},
props: {
level: {
type: Number,
required: true
}
}
})
当然,你可能跟我一样觉得一层又一层的 createElement
看了总是让人厌烦,你也可以透过这个 Plugin: ,来做 JSX 语法的转换,如果你曾是 react 应用程序的开发者,应该对 JSX 语法不陌生。写起来会像这样:
import AnchoredHeading from './AnchoredHeading.vue'
new Vue({
el: '#demo',
render (h) {
return (
<AnchoredHeading level={1}>
<span>Hello</span> world!
</AnchoredHeading>
)
}
})
在预设情况中,VueJS 2.0 会将 template
内的 HTML 透过 parse 转换成 AST,再自动转换优化成 renderfunction 去建立 virtual DOM。在建立 virtual DOM 之后,透过 observe 机制与数据进行绑定,再compile 成实体的 DOM 并渲染至网页上:
前面说过,VueJS 2.0 会将 template
内的 HTML 自动编译成 render function,下面这是官方文件以 Vue.compile
提供的 demo:
<!-- template -->
<div>
<h1>I'm a template!</h1>
<p v-if="message">
{{ message }}
</p>
<p v-else>
No message.
</p>
</div>
// render:
function anonymous() {
with(this){return _h('div',[_m(0),(message)?_h('p',[_s(message)]):_h('p',["No message."])])}
}
// staticRenderFns:
_m(0): function anonymous() {
with(this){return _h('h1',["I'm a template!"])}
}
Vue 2.0 组件的追踪变化
最后,我们来看看组件内状态的追踪变化。有写过 VueJS 1.x 的朋友应该知道,组件实体内有个 option 叫 data
,这个 data
对象就是用来存放组件内状态/数据的地方。
与 Vue 1.x 相同的地方是,data
对象透过 Object.defineProperty()
来为组件内各属性设定「getter」与「setter」。就在 data 属性被存取修改时,会透过 getter/setter 来通知对象内属性的变化,当先前设定好的 setter 被呼叫的时候,会去触发 watcher 重新计算,也就会导致 DOM 的更新与重新渲染。
与 Vue 1.x 不同的是,Vue 1.x 是透过 directive 来重新渲染 DOM 内容:
而 Vue 2.0 在通知 watcher 更新时,会去呼叫前面介绍的「renderfunction」与更新后的 data 去做更新后再次渲染,概念与 1.x 大致相同。但更新 DOM 的手法不同,减少了不必要的比对,也因此大幅度提升了性能。
【第二期读书会送书活动正在进行中】
【您可能感兴趣的文章】
二、Git学习之路
七、前端技能图谱
九、优化你的DOM
前端圈--打造专业的前端技术会议
为web前端开发者提供技术分享和交流的平台
打造一个良好的前端圈生态,推动web标准化的发展
官网:http://fequan.com
微博:fequancom | QQ群:41378087
长按二维码关注我们
投稿:content@fequan.com
赞助合作:apply@fequan.com