组件系统
组件可以扩展 HTML
元素,封装可重用的代码。
vue.js的核心组件:
- 模板(template)——模板声明了数据和最终展现给用户的DOM之间的映射关系。
- 初始数据(data)——一个组件的初始数据状态。对于可复用的组件来说,通常是私有的状态。
- 接受的外部参数(props)——组件之间通过参数来进行数据的传递和共享。参数默认是单向绑定(由上至下),但也可以显式声明为双向绑定。
-
方法(methods)——对数据的改动操作一般都在组件的方法内进行。可以通过
v-on
指令将用户输入事件和组件方法进行绑定。 -
生命周期钩子函数(lifecycle hooks)——一个组件会触发多个生命周期钩子函数,比如
created
、attached
、destroyed
等。在这些钩子函数中,我们可以封装一些自定义的逻辑。和传统的MVC相比,这可以理解为Controller
的逻辑被分散到了这些钩子函数中。 - 私有资源(assets):Vue.js当中将用户自定义的指令、过滤器、组件等统称为资源。由于全局注册资源容易导致命名冲突,一个组件可以声明自己的私有资源。私有资源只有该组件和它的子组件可以调用。
- 除此之外,同一棵组件树之内的组件之间还可以通过内建的事件API来进行通信。Vue.js提供了完善的定义、复用和嵌套组件的API,让开发者可以像搭积木一样用组件拼出整个应用的界面。这个思路的可行性在Facebook开源的React当中也得到了印证。
基于构建工具的单文件组件格式
Vue.js的核心库只提供基本的API,本身在如何组织应用的文件结构上并不做太多约束。但在构建大型应用时,推荐使用 Webpack+vue-loader
这个组合以使针对组件的开发更高效。
Webpack是由Tobias Koppers开发的一个开源前端模块构建工具。它的基本功能是将以模块格式书写的多个JavaScript文件打包成一个文件,同时支持CommonJS和AMD格式。但让它与众不同的是,它提供了强大的loader API来定义对不同文件格式的预处理逻辑,从而让我们可以将CSS、模板,甚至是自定义的文件格式当做JavaScript模块来使用。Webpack 基于loader还可以实现大量高级功能,比如自动分块打包并按需加载、对图片资源引用的自动定位、根据图片大小决定是否用base64内联、开发时的模块热替换等等,可以说是目前前端构建领域最有竞争力的解决方案之一。
其他特性
Vue.js还有几个值得一提的特性:
-
异步批量DOM更新:当大量数据变动时,所有受到影响的
watcher
会被推送到一个队列中,并且每个watcher
只会推进队列一次。这个队列会在进程的下一个 “tick” 异步执行。这个机制可以避免同一个数据多次变动产生的多余DOM操作,也可以保证所有的DOM写操作在一起执行,避免DOM读写切换可能导致的layout。 -
动画系统:Vue.js提供了简单却强大的动画系统,当一个元素的可见性变化时,用户不仅可以很简单地定义对应的CSS
Transition
或Animation
效果,还可以利用丰富的JavaScript钩子函数进行更底层的动画处理。 - 可扩展性:除了自定义指令、过滤器和组件,Vue.js还提供了灵活的mixin机制,让用户可以在多个组件中复用共同的特性。
使用Prop传递数据
组件实例的作用域是孤立的。这意味着不能(也不应该)在子组件的模板内直接引用父组件的数据。要让子组件使用父组件的数据,我们需要通过子组件的 props
选项。
子组件要显式地用 props
选项声明它期待获得的数据。
Vue.component({
props: ['message'],
template: '<span>{{message}}</span>'
});
父子组件通信
Vue 中子组件可以通过通过事件和父组件进行通信的。向父组件发消息是通过 this.$dispatch
,而向子组件发送消息是通过 this.$boardcast
。
数据绑定的实现原理
Vue 把双向绑定称作 reactive,可以翻译为响应式数据绑定。内部是通过 ES5 定义的 getter
和 setter
方法实现的,所以不支持 IE8 及以下浏览器,这种实现方式有两个容易犯错的地方:
- 如果在 data 上直接添加和删除属性是无法被检测到的,一般删除是不会的,但是可能会动态添加,这个时候应该通过
vm.$set(“name”, value)
的方式来添加。 - 无法检测到对象内部的变化,也就是只能检测 data 的属性变化,如果
data.a
是一个对象,那么data.a.b = 1
这种变化是无法被检测到的。这种情况下应该创建一个新的对象并赋值给 data.a 就行了。
异步更新机制
Vue 对DOM的更新是异步的! 这个异步是在一个异步队列中进行的,不过这个异步队列会在当前的 Event Loop
中执行完,所以如果修改了 Data 立刻去DOM中做查询操作是不对的,这个时候DOM还没有更新,正确的做法是这样做:
vm.msg = 'new message' // change data
vm.$el.textContent === 'new message' // false
Vue.nextTick(function () {
vm.$el.textContent === 'new message' // true
})
或者这样:
vm.$nextTick(function () {
this.$el.textContent === 'new message' // true
})