上一章咱们介绍了关于Vue实例中一些基本用法,可是组件、自定义指令、Render函数这些放到了本章来介绍,缘由是它们要比前面讲的要难一些,组件是Vue.js最核心的功能,学习使用组件也是必不可少的知识点。html
在咱们学习组件以前,更深刻的了解下Vue实例,它除了data数据对象属性外,Vue实例还暴露了一些有用的实例属性和方法,它们都有前缀$,以便与用户定义的属性区分开来,详细适用方法能够查阅官方API文档。vue
实例属性:node
实例方法 / 数据:webpack
实例方法 / 事件:web
实例方法 / 生命周期:spring
组件是Vue.js中最强大的功能之一,核心目标是为了可重用性高,减小重复性开发。组件须要注册才可使用,注册有全局注册和局部注册两种方式,全局注册的组件能够在任何Vue实例上均可以使用。Vue实例中使用compoents选项来注册局部组件,局部组件只能在当前实例中使用,组件中也可使用compoents选项来注册子组件,使组件能够嵌套使用。vuex
全局注册,代码示例以下:express
<div id="app"> <my-component></my-component> </div> <script> Vue.component('my-component', { template: '<div>这里是组件内容</div>' }) var app = new Vue({ el: '#app' }) </script>
渲染后的结果是:数组
<div id="app"> <div>这里是组件内容</div> </div>
局部注册,代码示例以下:服务器
<div id="app"> <my-component></my-component> </div> <script> var app = new Vue({ el: '#app', components: { 'my-component': {template:'<div>这里是组件内容</div>'} } }) </script>
组件中除了template选项外,还能够像Vue实例那样使用其余选项,好比data、methods、computed等,data选项必须是函数,必须return返回才有效。
代码实例以下:
<div id="app"> <my-component></my-component> </div> <script> Vue.component('my-component', { template: '<div>这里是组件内容</div>', data: function(){ return { message: '组件内容' //组件内部定义的数据 } } }) var app = new Vue({ el: '#app' }) </script>
prop传递数据:
Vue实例或父组件中调用子组件时,一般须要向子组件传递数据,这个过程须要经过prop来实现,组件中提供了props选项来接收参数,props的值分两种,一种是字符串数组,另外一种是对象,使用对象方式实际项目中最为常见,代码示例以下:
<div id="app"> <my-component message="来自父组件的数据"></my-component> </div> <script> Vue.component('my-component', { props: ['message'], template: '<div>{{ message }}</div>' }) var app = new Vue({ el: '#app' }) </script>
一般父组件中传递的数据并非写死的,而是来自父级的动态数据,这时可使用指令v-bind来动态绑定prop的值,当父组件的数据变化时也会传递给子组件,上面例子中props的值使用的是字符串数组方式,下面咱们使用另外一种对象方式接收,代码实例以下:
<div id="app"> <input type="test" v-model="msg"> <my-component :message="msg"></my-component> </div> <script> Vue.component('my-component', { props: { message: String }, template: '<div>子组件中显示:{{ message }}</div>' }) var app = new Vue({ el: '#app', data: { msg: '' } }) </script>
数据验证:
当props值为对象时,定义参数类型type,包括String、Number、Boolean、Object、Array、Function,参数也能够设置初始值default,定义是否必传参数required:true,还有自定义验证函数等,当prop验证失败时,在开发版本下会在控制台抛出一条警告。
代码实例以下:
<script> Vue.component('my-component', { props: { propA: Number, //必须是数字类型 propB: [String, Number], //必须是字符串或数字类型 propC: { //布尔类型,若是未传入,默认值为true type: Boolean, default: true }, propD: { //数字类型,必传参数 type: Number, required: true }, propE: { //若是是数组或对象类型,默认值必须是一个函数来返回 type: Array, default: function () { return []; } }, propF: { //自定义一个验证函数 validator: function () { return value > 10; } } } }) </script>
自定义事件:
上面咱们知道了父组件向子组件传递数据时使用prop来完成,这里说明一下prop值属于引用类型,当改变prop值会直接影响父组件,重复使用组件时直接改变prop值时就失去了复用的目的。那么子组件中向父组件传递数据要怎么处理呢,这里咱们就用到了自定义事件,自定义事件首先要在父组件中经过v-on监听一个事件,事件钩子函数在父组件实例中建立,在子组件中经过this.$emit()来触发这个自定义事件,$emit()方法的第一个参数是自定义事件的名称,后面参数为要传递的数据,后面参数能够为空或多个,代码实例以下:
<div id="app"> <p>总数:{{ total }}</p> <my-component @inccount="handleInc"></my-component> </div> <script> Vue.component('my-component', { template: '<div><button @click="handleIncrease">+1</button></div>', data: function(){ return { counter: 0 } }, methods: { handleIncrease: function(){ this.counter++; this.$emit('inccount', this.counter); //触发父组件中自定义事件 } }, }) var app = new Vue({ el: '#app', data: { total: 0 }, methods: { handleInc: function (count){ this.total = count; } } }) </script>
组件上使用v-model:
前面章节中咱们讲过v-model是一个特殊的语法糖,实际它等同于input自定义事件,咱们这里经过v-model来建立自定义的表单输入组件,进行数据双向绑定,代码实例以下:
<div id="app"> <p>总数:{{ total }}</p> <my-component v-model="total"></my-component> </div> <script> Vue.component('my-component', { props: ['value'], template: '<div><input :value="value" @input="updateValue"></div>', methods: { updateValue: function(){ this.$emit('input', event.target.value); } }, }) var app = new Vue({ el: '#app', data: { total: 0 } }) </script>
非父子组件通讯:
在实际业务中,除了父子组件通讯外,还有不少非父子组件通讯的场景,好比兄弟组件和跨多级组件,Vue.js中提供了一个方法,建立一个空的Vue实例做为中央事件总线(bus),也就是一个中介,初始化Vue实例时,监听这个中介事件来完成本身的业务逻辑。除了它Vue还提供了一个更好的解决方案 vuex状态管理插件。
Vue.js 实现了内容分发,使用slot元素做为承载分发内容的出口,混合父组件内容与子组件的模板时使用。
单个Slot:
在子组件内使用特殊的<slot>元素就能够开启一个slot默认插槽,在父组件模板中调用子组件标签内的全部内容将替换子组件的<slot>元素内的内容,代码实例以下:
<div id="app"> <my-component> <p>分发的内容</p> <p>更多分发的内容</p> </my-component> </div> <script> Vue.component('my-component', { template: '\ <div>\ <slot>\ <p>若是父组件没有插入内容,我将做为默认出现</p>\ </slot>\ </div>' }) var app = new Vue({ el: '#app' }) </script>
渲染后的结果是:
<div id="app"> <div> <p>分发的内容</p> <p>更多分发的内容</p> </div> </div>
具名Slot:
子组件模板中有时咱们须要多个插槽,slot元素中指定name属性,能够分发多个内容,具名Slot能够与单个Slot共存,自 2.6.0 起咱们能够在一个 <template> 元素上使用 v-slot 指令,代码实例以下:
<div id="app"> <my-component> <h2 slot="header">标题</h2> <p>分发的内容</p> <p>更多分发的内容</p> <h5 slot="footer">底部信息</h5> </my-component> <!-- 自 2.6.0 起新推荐的语法 --> <my-component> <template v-slot:header> <h2>标题</h2> </template> <p>分发的内容</p> <p>更多分发的内容</p> <template v-slot:footer> <h5>底部信息</h5> </template> </my-component> </div> <script> Vue.component('my-component', { template: '\ <div>\ <div class="header">\ <slot name="header"><slot>\ </div>\ <div class="main">\ <slot><slot>\ </div>\ <div class="footer">\ <slot name="footer"><slot>\ </div>\ </div>' }) var app = new Vue({ el: '#app' }) </script>
编译做用域:
父级模板里的全部内容都是在父级做用域中编译的;子模板里的全部内容都是在子做用域中编译的。
做用域插槽:
做用域插槽是一种特殊的Slot,使用slot-scope特性能够接收传递给插槽的 prop,自 2.6.0 起已废弃的使用 slot-scope 特性语法,新推荐的语法在<slot>元素的一个特性绑定子组件内部数据,这个特性绑定称为插槽prop,在父组件中v-slot具名插槽带一个值类定义咱们提供的插槽prop的名字,代码实例以下:
<div id="app"> <my-component :user="{firstName:'newfirst',lastName:'newlast'}"> <!-- user名字能够随意定义 --> <template v-slot:default="user"> {{user.lastName}} </template> <template v-slot:main="data"> {{data.message}} </template> </my-component> </div> <script> Vue.component('my-component', { data() { return { message: '子组件内部消息' } }, props: { user: { type: Object, default: function () { return { firstName: 'first', lastName: 'last' }; } } }, template: '\ <div>\ <div>\ <slot :lastName="user.lastName">\ {{user.firstName}}\ </slot>\ </div>\ <div>\ <slot name="main" :message="message">\ </slot>\ </div>\ </div>' }) var app = new Vue({ el: '#app' }) </script>
咱们已经介绍过了许多Vue内置的指令,好比 v-if、 v-show 等,这些内置指令能知足咱们绝大部分业务需求,不过在须要一些特殊功能时,咱们仍然但愿对DOM底层进行操做,这时咱们就要用到自定义指令来完成。
注册自定义指令:
自定义指令的注册方法也分全局注册和局部注册,跟组件注册方式很像,代码实例以下:
// 注册一个全局自定义指令
Vue.directive('focus', {
inserted: function (el) {
el.focus()
}
})
//在组件中注册局部自定义指令
var app = new Vue({
el: '#app',
directives: {
focus: {
inserted: function (el) {
el.focus()
}
}
}
})
钩子函数:
一个指令定义对象能够提供以下几个钩子函数,每一个都是可选的。
钩子函数参数:
指令钩子函数会被传入如下参数:
下面是结合了以上参数的一个具体示例,代码实例以下:
<div id="hook-arguments-example" v-demo:foo.a.b="message"></div> Vue.directive('demo', { bind: function (el, binding, vnode) { var s = JSON.stringify el.innerHTML = 'name: ' + s(binding.name) + '<br>' + 'value: ' + s(binding.value) + '<br>' + 'expression: ' + s(binding.expression) + '<br>' + 'argument: ' + s(binding.arg) + '<br>' + 'modifiers: ' + s(binding.modifiers) + '<br>' + 'vnode keys: ' + Object.keys(vnode).join(', ') } }) new Vue({ el: '#hook-arguments-example', data: { message: 'hello!' } })
什么是Render函数,Vue.js2.x开始使用了Virtual Dom(虚拟DOM)来更新DOM节点,提高渲染性能,Vue.js编译时会把template模板解析为Virtual Dom,Vue.js也提供了Render函数选项,即渲染函数,使用 JavaScript 代替模板功能。组件的template基本上知足咱们业务需求,但有些场景中,使用Virtual Dom会更简单。(这个咱们暂时用不到,若是想了解的朋友能够去官方文档中进一步学习)
Vue.js