什么是组件?
-
组件 => 可复用的Vue实例,且带有一个名字
-
例如:下拉菜单组件 => html + CSS + JS
-
通常一个应用会以一颗嵌套的组件树来表示:

局部组件 & 全局组件
- 使用局部组件:1. 建子 2. 挂子 3. 用子
- ps:在组件中data必须是一个函数,返回一个对象,一个组件的data选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝
- 使用全局组件:通过
Vue.component(组件名, {})创建全局组件,此时该全局组件可以在任意模板(template)中使用
<div id="app">
//3.使用子组件
<App></App>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
//App组件 html + css + js
//创建全局组件
Vue.component('Vheader',{
template:`<div>我是导航组件</div>`
})
Vue.component('Vaside',{
template:`<div>我是侧边栏</div>`
})
const Vbtn = {
template:`<button>按钮</button>`
}
const Vcontent = {
data() {
return {
}
},
template:`'
<div>
我是内容组件
<Vbtn></Vbtn>
<Vbtn></Vbtn>
<Vbtn></Vbtn>
</div>`,
components: {
Vbtn
}
}
//使用局部组件的打油诗:声子 挂子 用子
//1. 创建组件
//注意:在组件中这个data必须是一个函数,返回一个对象
const App = {
data() {
return {
msg: '我是App组件'
}
},
components: {
Vcontent
},
template:`
<div>
<Vheader></Vheader>
<div>
<Vaside/>
<Vcontent/>
</div>
</div>`,
methods: {
handleClick () {
this.msg = '学习局部组件';
}
},
}
new Vue({
el:'app',
data:{
},
components: {
//2. 挂载子组件
App
}
})
</script>
组件通信
-
父传子:通过Prop向子组件传递数据
<div id="app"> <App></App> </div> <script src="../node_modules/vue/dist/vue.js"></script> <script> //全局组件 //父传子:通过props来进行通信 //1. 在子组件中声明props接收在父组件挂载的属性 //2. 可以在子组件的template中任意使用 //3. 在父组件绑定自定义的属性 Vue.component('Child' , { template:` <div> <h2>我是一个子组件</h2> <h3>{{childData}}<h3> </div>`, props:['childData'] }) const App = { data() { return { msg: '我是父组件传来的值' } }, template: ` <div> <Child :childData = 'msg'></Child> </div>`, } new Vue({ el: 'app', data: { }, components: { App } }) </script>单向数据流
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。
额外的,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
这里有两种常见的试图变更一个 prop 的情形:
-
这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。 在这种情况下,最好定义一个本地的 data property 并将这个 prop 用作其初始值:
props: ['initialCounter'], data: function () { return { counter: this.initialCounter } } -
这个 prop 以一种原始的值传入且需要进行转换。 在这种情况下,最好使用这个 prop 的值来定义一个计算属性:
props: ['size'], computed: { normalizedSize: function () { return this.size.trim().toLowerCase() } }
-
-
子传父:监听子组件事件,使用事件抛出一个值
<div id="app"> <App></App> </div> <script src="../node_modules/vue/dist/vue.js"></script> <script> //全局组件 //子往父传值 //在父组件中 绑定自定义事件 //在子组件中触发原生的事件 在事件函数通过this.$emit触发自定义的事件 Vue.component('Child', { template: ` <div> <input type = "text" @input = 'handleInput'/> </div>`, methods: { handleInput(e) { const val = e.target.value; this.$emit('inputHandler', val); } }, }) const App = { data() { return { newVal: '' } }, methods: { input(newVal) { this.newVal = newVal; } }, template: ` <div> <div class = 'father'>数据:{{newVal}}</div> <Child @inputHandler = 'input'></Child> </div>`, new Vue({ el: 'app', data: { }, components: { App } }) </script> -
平行组件
在开发中,可能会存在没有关系的组件通信,比如有个博客内容显示组件,还有一个表单提交组件,我们现在提交数据得到博客内容组件显示,这样的操作,应用上述两种已经学习过的通信方式可能很难办到,为了解决这种问题,在vue中我们可以使用bus,创建中央事件总线
<div id="app"> <App></App> </div> <script src="../node_modules/vue/dist/vue.js"></script> <script> const bus = new Vue(); //中央事件总线 bus Vue.component('B', { data() { return { count: 0 } }, template: ` <div>{{count}}</div>`, created() { //$on 绑定事件 bus.$on('add',(n) => { this.count += n; }) }, }) Vue.component('A', { data() { return { } }, template: ` <div> <button @click = 'handleClick'>加入购物车</button> </div>`, methods: { handleClick() { //触发绑定的函数 //$emit 触发事件 bus.$emit('add', 1); } }, }) const App = { data() { return { } }, template: ` <div> <A></A> <B></B> </div>`, } new Vue({ el: 'app', data: { }, components: { App } }) </script> -
其他的组件通信方式:父组件provide来提供变量,然后子组件中通过inject来注入变量,无论组件嵌套多深
provide选项应该是一个对象或返回一个对象的函数。该对象包含可注入其子孙的 propertyinject选项应该是:一个字符串数组,或一个对象,对象的 key 是本地的绑定名
<div id="app"> <App></App> </div> <script src="../node_modules/vue/dist/vue.js"></script> <script> //provide & inject //父组件 provide来提供变量,然后在子组件中通过inject来注入变量,无论组件嵌套多深 Vue.component('B', { data() { return { count: 0 } }, inject: ['msg'], created() { console.log(this.msg); }, template: ` <div>{{msg}}</div>`, }) Vue.component('A', { data() { return { } }, created() { console.log(this.$parent); }, template: ` <div> <B></B> </div>`, }) const App = { data() { return { tile:"Isaac" } }, template: ` <div> <A></A> </div>`, } new Vue({ el: '#app', data: { }, provide() { return { msg: '老爹的数据' } }, components: { App } }) </script>
插槽
-
匿名插槽:子组件定义slot插槽,但并未具名,因此也可以说是默认插槽,只要在父元素中插入的内容,默认加入到这个插槽中去
<div id="app"> <App></App> </div> <script src="../node_modules/vue/dist/vue.js"></script> <script> Vue.component('MBtn', { template:` <button> <slot></slot> </button>` }) const App = { data() { return { } }, template: ` <div> <MBtn>登录</MBtn> <MBtn>注册</MBtn> </div>`, } new Vue({ el: '#app', data: { }, components: { App } }) </script> -
具名插槽:具名插槽可以出现在不同的地方,不限制出现的次数,只要匹配了name那么这些内容就会被插入到这个name的槽中去
<div id="app"> <App></App> </div> <script src="../node_modules/vue/dist/vue.js"></script> <script> Vue.component('MBtn', { template: ` <button> <slot name='login'></slot> <slot name='submit'></slot> <slot name='register'></slot> </button>` }) const App = { data() { return { } }, template: ` <div> <MBtn> <template slot='login'> <a href="#">登录</a> </template> </MBtn> <MBtn> <template slot='submit'>提交</template> </MBtn> <MBtn> <template slot='register'>注册</template> </MBtn> </div>`, } new Vue({ el: '#app', data: { }, components: { App } }) </script>