Vue.js 组件
组件用于封装页面的部分功能,将功能的结构、样式、逻辑代码封装为整体。
提高功能的复用性与可维护性,更好的专注于业务逻辑。
组件使用时为自定义 HTML 标签形式,通过组件名作为自定义标签名。
组件注册
全局注册
- 全局注册的组件在注册后可以用于任意实例或组件中。
Vue.component('组件名',{ 选项对象 })
- 注意:全局注册必须设置在根Vue实例创建之前。
组件基础
- 本质上,组件是可复用的Vue实例,所以它们可与new Vue 接收相同的选项,例如 data、methods以及生命周期钩子等。
- 仅有的例外是像el这样根实例特有的选项。
命名规则
- kebab-case(串串命名):‘my-component’
- PascalCase(每一个单词首字母大写): ‘MyComponent’
- 注意无论采用哪种命名方式,在DOM中都只有 kebab-case 可以使用
template选项
- template 选项用于设置组件的结构,最终被引入根实例或其他组件中。
单向数据流
父组件向子组件传值
父子组件间所有的prop都是单向下行绑定的。
- 注意,如果prop为数组或对象时,子组件操作将会影响到父组件的状态。
怎样理解单向数据流?
-
Vue 的单向数据流:指数据一般从父组件传到子组件,子组件没有权利直接修改父组件传来的数据,即子组件从 props 中直接获取的数据,只能请求父组件修改数据再传给子组件。父级属性值的更新会下行流动到子组件中。
-
为什么不能子组件直接修改父组件传来的值呢?父组件的值可能会不断发生变化,那么如果我们子组件对父组件传来的值比如说 props 有一个 number,子组件收到了 number=1,在收到后,子组件直接改变number 的值为 5,去做些事情,但还未做时父组件数据更新了,传过来一个值 3,也就是说子组件刚将其变为 5,父组件又把它变成了 3,可能影响子组件的使用。说的官方一些,就是父组件的值更新时,子组件中 props 的值也会发生更新。
-
在子组件中直接用 v-model 绑定父组件传过来的数据是不合理的,如果希望修改父组件传给子组件的值:
(1)在子组件 data 中创建一个变量获取 props 中的值,再改变这个 data 中的值。
(2)子组件使用 $emit 发出一个事件,让父组件接收去修改这个值。
data选项
- data选项用于存储组件的数据,与根实例不同,组件的data选项必须为函数,数据设置在返回值对象中
- 这种实现方式是为了确保每个组件实例可以维护一份被返回对象的独立拷贝,不会相互影响。
局部注册(提高可维护性)
- 直接在vue根实例中创建:
new Vue({
el: '#myApp',
data: {
},
methods: {
},
components: {
'my-com-a': {
template: `
<div>
<h3>{{title}}</h3>
<p>{{content}}</p>
</div>
`,
data() {
return {
title: '组件 A 标题',
content: '组件A 内容'
}
}
},
MyComB: {
template: `
<div>
<h3>{{title}}</h3>
<p>{{content}}</p>
</div>
`,
data() {
return {
title: '组件 B 标题',
content: '组件 B 内容'
}
}
}
}
})
- 单独配置组件的选项对象:
var MyComA = {}
var MyComB = {}
new Vue({
el:'#app',
components:{
'my-com-a':MyComA,
'my-com-b':MyComB
}
})
- ES6的对象属性简写(当属性名与要使用变量名相同时直接写上去):
new Vue({
el:'#app',
components:{
MyComA,
MyComB
}
})
关于VueComponent:
-
子组件的本质是一个名为VueComponent的构造函数,且不是程序员定义的。是Vue.extend生成的。
-
我们只需要写<子组件名/>或<子组件名><子组件名/>,Vue解析时会帮我们创建子组件的实例对象,及Vue帮我们执行的:new VueComponent(options)。
-
特别注意:每次调用Vue.extend。返回的都是一个全新的VueComponent!!!!!
-
关于this指向:
(1)组件配置中
data函数、methods中的函数、watch中的函数、computed中的函数 他们的this均是【VueComponent实例对象】。
(2)new Vue(options)配置中:
data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】
- VueComponent的实例对象,简称vc(也可称为组件实例对象) Vue的实例对象,简称vm。
组件通信
父组件向子组件传值
- 通过子组件的props选项接收父组件的传值
Vue.component('my-component',{
props:['title'],
template:'<h3>{{title}}</h3>'
})
- 注意props不要与data存在同名属性
Props命名规则
- 建议prop命名使用camelCase,父组件绑定时使用kebab-case。
子组件向父组件传值
子向父传值需要通过自定义事件实现。
购物车案例:
-
商品为子组件,购物车为父组件,父组件需要统计商品个数,就需要在子组件个数变化时传值给父组件。
-
子组件数据变化时,通过$emit()触发自定义事件。
Vue.component('product-item',{
...
methods:{
countIns(){
this.$emit('count-change');
this.count++;
}
}
})
- 自定义时间名称建议使用kebab-case
- 父组件监听子组件的自定义事件,并设置处理程序
<div id="app">
...
<product-item
...
@count-change="totalCount++"
></product-item>
</div>
自定义事件传值
- 子组件在事件触发时可以向父组件传值。
- 父组件在监听事件时需要接收子组件传递的数据。
- 父组件的操作方式
// 方案一 直接传数据
<div id="app">
...
<product-item
...
@count-change="totalCount += $event"
></product-item>
</div>
// 方案二 通过处理程序
<div id="app">
...
<product-item
...
@count-change="onCountChange" // 这个函数定义在父组件的methods中
></product-item>
</div>
new Vue({
...
methods:{
onCountChange(productCount){
this.totalCount += productCount // 这个productCount只是一个形参用来接 收this.$emit()传来的数据,它可以随意 写
}
}
})
非父子组件传值
非父子组件指的是兄弟组件或完全无关的两个组件。
兄弟组件传值
兄弟组件可以通过父组件进行数据中转
<body>
<script src='https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js'></script>
<div id='myApp'>
<!-- 父组件接收子组件A的数据 -->
<my-com-a @value-change=" value = $event"></my-com-a>
<!-- 父组件将数据传递给子组件B -->
<my-com-b :value="value"></my-com-b>
</div>
<!-- 组件模板 -->
<template id='myTem1'>
<div>
组件a的内容:{{value}}
<button @click="$emit('value-change',value)">发送</button>
</div>
</template>
<template id='myTem2'>
<div>
组件B接收到:{{value}}
</div>
</template>
<script>
// 子组件A,发送数据
Vue.component('myComA', {
template: '#myTem1',
data() {
return {
value: "这是组件a的数据"
}
},
})
// 子组件B,接收数据
Vue.component('myComB', {
props: ['value'],
template: '#myTem2',
data() {
return {
}
}
})
new Vue({
el: '#myApp',
data: {
// 用于数据中转
value: ''
},
methods: {
}
})
</script>
</body>
-
当组件嵌套关系复杂时,根据组件关系传值会比较繁琐。
-
组件为了数据中转,data中会存在许多与当前组件功能无关的数据。
EventBus
- EventBus(事件总线)是一个独立的事件中心,用于管理不同组件间的传值操作。
- EventBus通过一个新的Vue实例来管理组件传值操作,组件通过给实例注册事件、调用事件来实现数据传递。
操作步骤:
- 发送数据的组件触发bus事件,接收的组件给bus注册对应事件。
- 给bus注册对应事件通过$on()操作