组件化就是将一个页面拆成一个个可复用功能块(组件),方便扩展和复用.
注意:在实际开发中,我们并不会用以下方式开发组件,而是采用 vue-cli 创建 .vue 模板文件的方式开发,以下方法只是为了让大家理解什么是组件。
组件使用的三个步骤:
创建组件构造器: 调用Vue.extend()方法,Vue2之后可以将extend中的对象在注册组件时传入注册组件 使用 Vue.component() 方法注册全局组件,所有Vue实例都可使用在Vue实例中使用components注册局部组件,只有注册的Vue实例才能使用,注册的Vue实例的父实例不能使用该局部组件 使用组件 注意局部组件的作用域子组件中不能直接访问父组件的数据组件是可复用的 Vue 实例,一般称new出来的Vue实例为根组件.
组件名定义时如果使用的是驼峰命名法,在使用名字时要改为短横线分隔命名法每个组件必须只有一个根元素 <div id="vue"> <ul> <!--若不想从vue的data数据中读值,可以直接使用aaa="xxx"向组件的aaa属性赋值--> <!-- =号左边的 aaa 为 props 定义的属性名,右边的为v-for遍历出的item --> <my-component-li v-for="item in items" v-bind:aaa="item"></my-component-li> </ul> </div> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script type="text/javascript"> const cpnc1 = Vue.extend({ template: ` <h1>啊啊啊啊</h1> ` }) // 组件中注册组件 const cpnc2 = Vue.extend({ template: ` <h1>哈哈哈哈哈</h1> <cpn1></cpn1> `, //组件构造器中注册组件, components:{ cpn1: cpnc1 } }) //注册组件 cpn1为组件名 Vue.component('cpn1',cpnc1) // 使用语法糖注册组件,{}就是传入extend的对象 Vue.component('my-component-li', { //使用 props 属性向子组件传递数据,通过v-bind绑定属性 props: ['aaa'], //组件的模板 template: '<li>Hello {{aaa}}</li>' }); var vm = new Vue({ el: '#vue', data: { items: ["张三", "李四", "王五"] } }); const app= new Vue({ el: '#app', //注册局部组件 components: { // cpn为使用组件的标签名,cpnc为组件构造器 cpn: cpnc2 // 使用语法糖可以省略 extend cpu2: { template: ` <h1>哈哈哈哈哈</h1> <cpn1></cpn1> `, components:{ cpn1: cpnc1 } } } }) </script>避免在子组件中直接改变props属性的值,而是在子组件中使用data或计算属性接收props属性的值后再进行双向绑定或修改
<div id="vue"> <ul> <!--若不想从vue的data数据中读值,可以直接使用aaa="xxx"向组件的aaa属性赋值--> <!-- =号左边的 aaa 为 props 定义的属性名,右边的为v-for遍历出的item --> <my-component-li v-for="item in items" v-bind:aaa="item"></my-component-li> </ul> </div> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script type="text/javascript"> Vue.component('my-component-li', { //props可使用数组或对象 //使用数组只能指定变量名 //props: ['aaa'], //使用对象时可指定类型,默认值,是否必传 //类型是对象或数组时,默认值必须是函数 // 还可验证自定义类型 props:{ // 只指定类型 aaa: String, // 指定类型和默认值,必传值 bbb: { type: String, default: 'aaaaaa', required: true } cage: Number, cpp: { type: Array, default(){ return [] } }, propE: { type: Object, // 对象或数组默认值必须从一个工厂函数获取 default: function () { return { message: 'hello' } } }, // 自定义验证函数 propF: { validator: function (value) { // 这个值必须匹配下列字符串中的一个 return ['success', 'warning', 'danger'].indexOf(value) !== -1 } } } template: '<li>Hello {{aaa}}</li>', data(){ return { //将 props中的aaa赋值给data daaa: this.aaa } } }); var vm = new Vue({ el: '#vue', data: { items: ["张三", "李四", "王五"] } }); </script>HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。所以使用驼峰命名法的 props 名需要使用其等价的短横线分隔命名
Vue.component('blog-post', { // 在 JavaScript 中是 驼峰 的 props: ['postTitle'], template: '<h3>{{ postTitle }}</h3>' }) <!-- 在 HTML 中是 短横线分隔 的 --> <blog-post post-title="hello!"></blog-post>有时候我们需要父子组件之间可以直接访问,而不是仅仅传值通信
在 Vue.js 中我们使用 元素作为承载分发内容的出口,作者称其为 插槽,可以应用在组合组件的场景中,相当于占位符,提高组件的扩展性.
单插槽 <!-- 1.插槽可以设置默认值 2.使用时如果有多个值,同时放入到组件进行替换时,一起作为替换元素 --> Vue.component('chacao', { template: '<div> <slot></slot> <slot><h1>默认值</h1></slot> </div>' }); <div id="vue"> <chacao></chacao> <chacao><h1>传入的值<h1></chacao> <chacao> <h1>传入的值1<h1> <h1>传入的值2<h1> <h1>传入的值3<h1> </chacao> </div> 多插槽,指定name,指定具名插槽替换slot的name属性值 和 替换元素的slot属性值相等时,替换指定插槽
Vue.component('chacao', { template: '<div> <slot name="left"><h1>左<h1></slot> <slot name="right"><h1>右<h1></slot> </div>' }); <div id="vue"> <chacao> <h1 slot="left">指定插槽替换<h1> </chacao> </div>插槽的作用域:
定义一个名为 todo 的待办事项组件, 该组件中放置了两个插槽,分别为 todo-title 和 todo-items
Vue.component('todo', { template: '<div> <slot name="todo-title"></slot> <ul> <slot name="todo-items"></slot> </ul> </div>' });定义一个名为 todo-title 的待办标题组件
Vue.component('todo-title', { props: ['title'], template: '<div>{{title}}</div>' });定义一个名为 todo-items 的待办内容组件
Vue.component('todo-items', { props: ['item', 'index'], template: '<li>{{index + 1}}. {{item}}</li>' });初始化
<div id="vue"> <todo> <todo-title slot="todo-title" title="今日动漫推荐"></todo-title> <todo-items slot="todo-items" v-for="(item, index) in todoItems" v-bind:item="item" v-bind:index="index" :key="index"></todo-items> </todo> </div> var vm = new Vue({ el: '#vue', data: { todoItems: ['《刀剑神域3》', '《关于我转生成为史莱姆这件事》', '《实力至上主义教室》'] } });父模板中的变量会使用父组件的属性,上面的div属于根组件的模板,会从根组件寻找变量而不是从子组件中寻找
slot-scope:作用域插槽 :让替换插槽的内容能够访问子组件中的数据
常用于父组件对子组件数据渲染方式不满意的情况下,获取到子组件的数据自行展示.
<div id="vue"> <todo> <!-- 1.最好在template上使用slot-scope,不然有的版本可能不支持 2.slot-scope声明一个变量,接收插槽中使用的变量 --> <template slot-scope="slot"> {{slot.data}} </template> </todo> </div> Vue.component('todo', { template: '<div> <slot :data="todoItems"></slot> </div>', data(){ return{ todoItems: ['《刀剑神域3》', '《关于我转生成为史莱姆这件事》', '《实力至上主义教室》'] } } }); var vm = new Vue({ el: '#vue' });