在实际的开发中一个页面往往有很多部分组成,我们通常会把这些部分抽离成不同区块单独进行开发,这种抽离成不同区块的开发方式就是组件化开发的思想;组件化就是根据ui界面划分出来、为了拆分vue实例代码量的可复用部分代码;根据不同的功能做成不同的组件,需要什么功能,再调用什么组件就好
vue-router
产生的页面本质上也是组件,主要承载当前页面的 HTML 结构,数据处理展示等常规业务;整个文件较大,基本不会被复用,还原设计稿就行比较容易写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
//第一种方式 Vue.component('myli',Vue.extend({ template:'<li>我是第一种定义组件的方法,全局注册</li>' })) <myli></myli>//在html中使用 //第二种方式 Vue.component('mydiv',{ template:'<div>我是第二种定义组件的方法</div>' }) <mydiv></mydiv>//在html中使用 //第三种方式 //在html中写模板 <template id='myp'> <div> <span>我是第三种方式 局部注册</span> </div> </template> //在html中使用定义的组件 <login></login> //vue中定义组件 let temp=new Vue({ el:'#temp', components:{ login:{ template:'#myp' }, com1//相当于com1:com1,es6属性增强写法 } }) //第三种方式升级版,也是最常用的写法 //1.创建模板文件template.js <template class='temp'> .... </template> <script> ... </script> <style> ... </style> //2.引入组件,注册组件 import temp from './template.js' let temp=new Vue({ el:'#temp', components:{ temp } }) //在需要的地方展示 <temp></temp> |
Data:用于存放数据和状态;与实例中的data不太一样,因为组件需要在多个地方使用,如果还是像实例中那样定义data,那么所有组件就会公用一个data,一个组件改变数据就会改变data的值,就会引起所有组件发生改变;但是如果每次调用组件的时候我们都返回一个新对象,那每个组件都会有自己专属的data,这样一个组件修改data里的数据就不会影响其他组件,所以组件的data必须是返回对象的一个函数
1 2 3 4 5 6 7 8 9 10 11 |
<script type="text/javascript"> export default { name:'DetailsBanner', data(){ return { mydatapush:[], flg:true } } } </script> |
Prop: 定义组件可以接受哪些配置,组件的核心功能由它来确定,主要接收父组件传递过来的数据;定义了组件可以接收哪些可配置的属性。主要是用来接收父组件传递的数据。props接收属性时可以是数组形式,也可以是对象形式。如果写通用组件最好还是写对象的形式,这样可以设置属性的类型和默认值,也可以自定义校验属性的值;
修改props里的值:props是单向数据流,官方是不推荐直接在props里修改数据的,如果非要修改数据,最好还是通过$commit()触发父组件内方法,让父组件来修改;这里需要注意,当你修改的是引用类型的值时,你修改的是栈里边对应的数据,从而所有组件的数据都会发生改变,又因为修改引用类型的值并没有直接修改当前这个引用,所以vue并不会报错,尽管可以不报错也能修改引用类型的值,但是还是不建议这么操作因为prop定义时就是单向数据流......
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//props是数组形式 props:['name','age'] //props是对象形式 props:{ name:{ type:String, default:'请输入名字', required: true //是否必填 }, propV:{//自定义props验证函数 validator:function (value){ return ['success', 'warning', 'danger'].indexOf(value) !== 1 } } } |
Slot:插槽;我们的组件往往需要在相同部分展示不同的结构内容,我们不能把每一部分都写死,所以slot就应运而生,solt插槽相当于一个占位符,可以在你使用组件的时候自己决定每一部分该展示什么,使整个组件具有更强的拓展性;slot是vue提供的一个占位标签,可以在任何位置直接使用
后备插槽:当组件中一部分不确定的时候,我们可以预留一个插槽,在需要的地方插入合适的内容就好
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
//一个提交按钮插件 //定义组件 <template id='submitButton'> <button type='submit'> <slot></slot> </button> </template> //使用组件 <div id="myslotParent"> <submit-button></submit-button> //不需要显示内容的按钮,slot就不会被替换 <submit-button>提交</submit-button> //需要显示提交的按钮,slot就会被替换成提交 </div> //注册组件 let mysolt = new Vue({ el:'#myslotParent', components:{ submitButton:{ template:'#submitButton' } } }) |
具名插槽:在创建组件的时候往往会很多不确定的部分,这时候就会用到很多插槽,那么如何才能分清那个插槽对应那一部分呢?给slot添加name属性,在使用组件定义这一部分时使用template标签,在template标签上使用v-slot:slot的name值
就可以(vue2.6以上版本,以下版本直接在需要的标签上使用slot: slot的name值
就行)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
//定义组件 <template id="hasName"> <div> <slot name='header'></slot> /*1.给slot添加name名*/ <slot></slot><!-- 没有name的,会默认name值是default --> <slot name='footer'></slot> </div> </template> //使用组件 <div id="myslotParent"> <has-name> <template v-slot:header> /*2.在template标签设置v-slot属性*/ <p>我是头部</p> </template > <template v-slot:default> //匿名插槽可以简写为'#:default',这里的default不能省 <p>我是中间部分</p> </template> <template #footer> //具名插槽可以缩写成'#:名字' <p>我是尾部</p> </template> </has-name> </div> //注册组件 let mysolt = new Vue({ el:'#myslotParent', components:{ hasName:{ template:'#hasName' } } }) |
作用域插槽:作用域插槽就是带有数据的插槽;一般我们定义完插槽,在向插槽填充内容的时候,数据都是由当前这个页面提供的(为方便理解,也可称为父组件),然而在一些组件中,子组件也会提供一些数据,这些带有数据的插槽就称之为作用域插槽;
由于父级模板里的所有内容都是在父级作用域中编译的,子模板里的所有内容都是在子作用域中编译的,也就是说父子组件不能够直接互相拿取数据,那么我们又如何在父组件里获取子组件当前部分的数据呢?slot为我们供了一种叫插槽prop的东西:在slot中绑定属性拿到需要的数据(<slot v-bind:user='userdata'/>
),然后再在父组件中通过带值得v-slot
来定义我们提供的插槽 prop 的名字 (<template v-slot:default='mydata'/>
),如此一来就可以在父组件里使用当前插槽带来数据了;(注意vue2.6以上就废除了 slot-scope
改用v-slot:default=String
方式了)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
//定义组件 <template id="hasMessage"> <div> 我是作用域插槽 <slot :user='userdata'></slot> /*1.在slot中将用到的数据绑定一个属性里*/ </div> </template> //使用组件 <div id="myslotParent"> <has-message> <template #default='getdata'> /*2.在template里通过v-slot将slot复制给一个变量getdata*/ <p>{{getdata.user.name}}</p> /*3.在需要的地方提取需要的值*/ </template> </has-message> </div> //注册组件 let mysolt = new Vue({ el:'#myslotParent', components:{ hasMessage:{ template:'#hasMessage', data(){ return { userdata:{ name:'我是子组件的插槽prop数据的名字' } } } } } }) |
Event:组件的事件;在一些情况下我们会通过子组件的一些事件来触发父组件做一些操作;在子组件触发事件的时候通过$emit('myevent')将自定义事件提交给父组件vue实例,在父组件里通过@myevent来监听子组件提交上来的事件;注意这里提交事件最好不要使用驼峰命名,由于大小写的问题会监听不到,推荐使用纯小写或者'base-name
'形式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
//定义组件 <template id='submitButton'> <button type='submit' @click='sontempclick'> /*1.子组件触发事件*/ <slot>wwwwwwwwwwwwwww</slot> </button> </template> //使用组件 <div id="myslotParent"> <submit-button @sonclicked='getsonclick'></submit-button> /*3.在父组件中监听自定义的事件*/ <submit-button>提9999999交</submit-button> </div> //注册组件 let mysolt = new Vue({ el:'#myslotParent', components:{ submitButton:{ template:'#submitButton', data(){ return { sondata:'我是子组件的数据' } }, methods:{ sontempclick(){//子组件点击触发的方法 alert(666); this.$emit('sonclicked') /*2.向父组件的vue实例提交自定义事件*/ } } } }, methods:{ getsonclick(){/*4.父组件监听到子组件事件触发时执行的方法*/ console.log(777); } } }) |
组件间的通信方式有很多种,大致分为父子组件传值和非父子组件传值;可以查看VUE组件间的几种通信方式