Vue使用的扩展
1、事件总线(Bus)实现非父子组件通信
Vue2.0提供了Vuex进行非父子组件之间的通信,但在简单的场景下,可以使用一个空的Vue实例作为中央事件总线。
实现代码示例:
<div id="app"> <c1></c1> <c2></c2> </div>
var Bus = new Vue(); //为了方便将Bus(空vue)定义在一个组件中,在实际的运用中一般会新建一Bus.js Vue.component('c1',{ //这里以全局组件为例,同样,单文件组件和局部组件也同样适用 template:'<div>{{msg}}</div>', data: () => ({ msg: 'Hello World!' }), created() { Bus.$on('setMsg', content => { //监听Vue示例 Bus 的某一个事件 this.msg = content; }); } }); Vue.component('c2',{ template: '<button @click="sendEvent">Say Hi</button>', methods: { sendEvent() { Bus.$emit('setMsg', 'Hi Vue!'); //触发vue示例 bus 的某一个事件,并且将参数作为数据传过去 } } }); var app= new Vue({ el:'#app' })
在实际运用中,一般将Bus抽离出来:
// Bus.js import Vue from 'vue' const Bus = new Vue() export default Bus
组件调用时先引入
// 组件1 import Bus from './Bus' export default { data() { return { ......... } }, methods: { .... Bus.$emit('log', 120) }, }
// 组件2 import Bus from './Bus' export default { data() { return { ......... } }, mounted () { Bus.$on('log', content => { console.log(content) }); } }
但这种引入方式,经过webpack打包后可能会出现Bus局部作用域的情况,即引用的是两个不同的Bus,导致不能正常通信
1.1、推荐使用Bus的方式
(1)直接将Bus注入到Vue根对象中
import Vue from 'vue' const Bus = new Vue() var app= new Vue({ el:'#app', data:{ Bus } })
在子组件中通过this.$root.Bus.$on()、this.$root.Bus.$emit()来调用
(2)或者也可以通过将Bus放在vue示例的原型对象中使用
Vue.prototype.bus = new Vue();
每个组件都是一个Vue示例,所以每个组件都会有bus属性,在组件中可以通过 this.bus.$emit、this.bus.$on 来调用
Vue.component('c2',{ template: '<button @click="sendEvent">Say Hi</button>', methods: { sendEvent() { this.bus.$emit('setMsg', 'Hi Vue!'); } } });
参考:https://www.cnblogs.com/fanlinqiang/p/7756566.html
2、Vue 动态插入组件
代码示例:
<div id="app"> <p>{{ message }}</p > <button @click="add('a-component', '我是A')">添加A组件</button> <button @click="add('b-component', '我是B')">添加B组件</button> <component :is="item.component" :text="item.text" v-for="item in items"></component> </div> js: <script> const aComponent = Vue.extend({ props: ['text'], template: '<li>A Component: {{ text }}</li>' }) const bComponent = Vue.extend({ props: ['text'], template: '<li>B Component: {{ text }}</li>' }) new Vue({ el: '#app', data () { return { message: 'Hello Vue.js!', items: [] } }, methods: { add (name, text) { this.items.push({ component: name, text: text }) } }, components: { aComponent, bComponent } }) </script>
Vue.extend 是构造一个组件的语法器。你给它参数它给你一个组件,然后这个组件你可以作用到Vue.component 这个全局注册方法里, 也可以在任意vue模板里使用<apple>组件
var apple = Vue.extend({ .... }) Vue.component('apple',apple)
也可以作用到vue实例或者某个组件中的components属性中并在内部使用apple组件
new Vue({ components:{ apple:apple } })
5、 语法糖
语法糖是指用另一种语法替换原先的比较复杂的语法,但实现的功能一样,这种比较简单清晰的语法被称为语法糖。
vue 中的语法糖有:
- v-bind 的语法糖是:":"
- v-on 的语法糖是:"@"
- v-model 也是一个语法糖,v-model = "test" 的实际写法可以写成 :value = "test" @input = " test = $event.target.value "
- v-slot 的语法糖是:"#",该缩写只有在有参数时才能用,比如具名插槽:<template #header> </template>
6、组件中的 name 属性的作用
组件中是有 name 属性的,组件的 name 属性主要有以下几个作用:
6.1、允许组件递归地引用本身
在自身组件调用自身的时候,可以通过定义name的值进行递归调用
<div>
<div v-for="(item,index) of list" :key="index">
<detail-list></detail-list>
</div>
</div>
export default {
name: 'DetailList', //组件可以利用 name 属性递归地调用本身
props: {
list: [1,2,3]
}
}
6.2、使用 keep-alive 缓存组件
<!-- 失活的组件将会被缓存!--> // 下面的 is 后面的名字应该是组件的 name 属性值 <keep-alive> <component v-bind:is="currentTabComponent"></component> </keep-alive>
组件在全局用 Vue.component()
注册时,全局 ID 自动作为组件的 name。
<keep-alive>
要求被切换到的组件都有自己的名字,不论是通过组件的 name
选项还是局部/全局注册。
6.3、报错警告信息明确,使用 vue-devtools 开发更易于调试
指定 name 选项的另一个好处是便于调试,有名字的组件有更友好的警告信息。
另外,当在有 vue-devtools,未命名组件将显示成 <AnonymousComponent>
,这很没有语义。如果组件有 name 属性,那在 vue-devtools 中显示的将是 name 的值,组件树更有语义。
8、关于父子组件props传值的响应式修改问题
父子组件之间可以通过 props 进行传值,在父组件中修改传过去的值时,在子组件中接收到的 props 值都会随之发生改变。但是如果把 props 的值赋值给 data 中的数据时,data 中的数据不一定会随之改变。
当 props 值不是对象时,直接赋值给 data 属性,data 属性不会发生响应式改变。比如当 props 值是对象时,如果直接赋值给 data 属性,data 属性会发生响应式改变,如果将对象的某个属性或者直接将某个基本类型值赋值给 data 属性,此时子组件中该 data 属性不会发生响应式改变。
总而言之,将对象赋值给 data 属性会随之发生改变,将基本类型值赋值给 data 属性则不会发生响应式改变。
//父组件 parent.vue
<button @click="editName">点击修改数据</button>
<item01 :name-prop="name" :human-prop="human" />
data() {
return {
name: 'wen',
human: {name: '人类'}
}
},
methods: {
editName () { //点击修改传过去的 props 值
this.name = 'hai'
this.human.name = '动物'
}
}
//子组件 child.vue
props: ['nameProp','humanProp'], //props值会响应式地发生改变
data() {
return {
myName: this.nameProp, //不是对象的值直接赋值给 data 属性,此时不会发生响应式改变
myHuman: this.humanProp //对象直接赋值给 data 属性,会发生响应式改变
myHumanName: this.humanProp.name //对象属性赋值给 data 属性,也不会发生响应式改变
}
}
{{nameProp}} -- {{humanProp}} //props值 都会响应式地发生改变
{{myName}} //data属性 此时不会改变
{{myHuman.name}} //这里会改变
{{myHumanName}} //这里不会改变
请注意:是子组件中的 data 属性的数据不会发生响应式改变,但 props 属性的值都是会响应式改变的。所以如果你不需要修改传过来的值的话,你也可以直接用 props 属性的值,而不用赋值给 data 中的属性值。
8.1、如何解决子组件接收的基本值类型的数据时不会发生响应式改变的问题
上面说到如果将值类型的 props 值赋值给 data 属性时,data 属性不会随着父组件的数据的改变而发生改变,要想解决这个问题,可以通过监听 props 的值,当 props 的某个值发生改变时,再将其赋值给 data 属性。
//子组件 child.vue
{{myName}} //此时会发生响应式改变
props: ['name','human'], //props值会响应式地发生改变
data() {
return {
myName: this.name, //不是对象的值直接赋值给 data 属性,此时不会发生响应式改变
}
},
watch: {
name (newVal, oldVal) { //通过监听将新的props值赋值给data属性
this.myName = newVal; //或者: this.myName = this.name
}
}
当然,如果不需要修改传递过来的值时,你也可以直接使用接收的 prop 的值进行操作,而无需赋值给组件的 data,这样就不用监听改变了。