Vue组件化
组件化的诞生
由于之前的传统页面编写方式存在的问题:
所以Vue提供了组件化的方式: 每个组件有自己相应的css、html、js等代码,体现了组件的封装,解决了传统方式存在的问题
组件化的基本使用
具体步骤:
- 使用组件构造器创建组件
- 注册组件
- 使用组件
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
| <div id="app"> <mycpn></mycpn> </div> <div id="app2"> <cpn></cpn> </div> <script src="../js/vue.js"></script> <script> const cpnc = Vue.extend({ template:` <div> <h2>我是标题</h2> <p>我是内容</p> </div>` }) Vue.component('mycpn',cpnc) const app = new Vue({ el:'#app', data:{ message:'你好啊' } }) const app2 = new Vue({ el:'#app2', data:{ message:'你好啊' }, components:{ cpn:cpnc } }) </script>
|
语法糖写法:Vue.component('mycpn',{template:
我是标题
})
组件的嵌套
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
| <body> <div id="root"> <school></school> <hr> </div> <script type="text/javascript"> Vue.config.productionTip = false; const student = Vue.extend({ template:` <div> <h2>学生姓名:{{stuName}}</h2> <h2>学生年龄:{{age}}</h2> </div>`, data(){ return{ stuName:'小王', age:18 } }, })
const school = Vue.extend({ template:` <div> <h2>学校名称:{{schoolName}}</h2> <h2>学校地址:{{address}}</h2> <student></student> </div>`, data(){ return{ schoolName:'家里蹲', address:'屋头' } }, components:{ student } })
new Vue({ el:'#root', components:{ school } }) </script> </body>
|
模板的抽离
1 2 3 4 5 6 7 8
| <template id="cpn"> <div> <h2>我是标题</h2> </div> </template>
template:'#cpn'
|
父子组件的通信
父组件向子组件通信
通过props向子组件传递数据
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
|
props:['name','sex','age'] 简单接收
props:{ name:String, age:Number, sex:String }
props:{ name:{ type:String, required:true }, age:{ type:Number, default:99 }, sex:{ type:String, required:true } }
<Student :name="李四" :sex="女" :age="18"/>
|
子组件向父组件通信
通过自定义事件向父组件发送数据
子组件:
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
| <template> <div class="stu"> <h2>学生名:{{name}}</h2> <h2>性别:{{sex}}</h2> <button @click="sendName">把学生名传给app</button> <button @click="unbind">解绑事件</button> </div> </template>
<script> export default { name:'Student', data(){ return{ name:'张三', sex:'男' } }, methods:{ sendName(){ this.$emit('getName',this.name); }, unbind(){ this.$off('getName'); } } } </script>
|
父组件:
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> <div class="app"> <h1>{{msg}}</h1> <Student v-on:getName="getStudentName"/> <student ref="student" @click.native="show"/> </div> </template>
<script> import Student from './components/Student.vue' export default { name:'App', components: { Student }, data() { return { msg:'你好啊!!!' } }, methods:{ getStudentName(name){ console.log("学生名:"+name); } }, mounted() { this.$refs.student.$on('getName',this.getStudentName) }, } </script>
|
父子组件之间的访问
父组件访问子组件
使用$children或$refs
this.$children获得子组件数组
this.refs获取标有ref属性的子组件数组,若是原生DOM则获取的是原生DOM元素
子组件访问父组件
使用$parent
this.$parent获得父组件(不常用)
访问根组件
使用$root
this.$root获得根组件
组件之间的访问
全局事件总线
可以任意组件间进行通信
总线特点:
1. 对于所有组件都可见
2. 可以调用$on、$off、$emit
Vue的原型对象刚好符合(最好放在这里)
在main.js中布局全局事件总线
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
const vm = new Vue({ el:'#app', render: h => h(App), beforeCreate(){ Vue.prototype.$bus = this } })
|
进行组件间通信
组件1(接收方,给总线绑定事件)
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> <div class="stu"> <h2>学生名:{{name}}</h2> <h2>性别:{{sex}}</h2> </div> </template>
<script> export default { name:'Student', data(){ return{ name:'张三', sex:'男' } }, mounted(){ this.$bus.$on('hello',((data)=>{ console.log('我是Student组件,收到了数据',data) })) }, beforeDestroy(){ this.$bus.$off('hello') } } </script>
<style> .stu{ background-color: aquamarine; } </style>
|
组件2发送数据方(调用接收方给总线绑定的事件)
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
| <template> <div class="school"> <h2>学校名称:{{schoolName}}</h2> <h2>学校地址:{{address}}</h2> <button @click="sendSchoolName">把学校名给student组件</button> </div> </template>
<script> export default { name:'School', data(){ return{ schoolName:'家里蹲', address:'屋头' } }, methods:{ sendSchoolName(){ this.$bus.$emit('hello', this.schoolName) } }
} </script>
<style> .school{ background-color: aquamarine; } </style>
|
消息订阅与发布
同样用于任意组件间通信
基于pubsub-js
安装pubsub-js : npm i pubsub-js
引入js :import pubsub from ‘pubsub-js’
接收数据方需要订阅消息:
1 2 3 4 5 6 7 8
| mounted(){ this.pubId = pubsub.subscribe('hello',(msgName,data)=>{ console.log('有人发布了'+msgName+'消息',+data) }) }, beforeCreate(){ pubsub.unsubscribe(this.pubId) }
|
发送数据方需要发布消息:
1 2 3 4 5
| methods:{ sendSchoolName(){ pubsub.publish('hello',666) } }
|
nextTick
语法:this.$nextTick('xxx',数据)
作用:在下一次DOM更新结束后执行其指定的回调
slot插槽
插槽的作用:
- 组件的插槽也是为了让我们封装的组件更加具有扩展性。
- 让使用者可以决定组件内部的一些内容到底展示什么。
1.默认插槽
子组件:
1 2 3 4 5 6 7
| <template> <div class="category"> <h3>{{title}}分类</h3> <!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) --> <slot>我是一些默认值,当使用者没有传递具体结构时,我会出现</slot> </div> </template>
|
父组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <template> <div class="container"> <Category title="美食" > <img src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt=""> </Category>
<Category title="游戏" > <ul> <li v-for="(g,index) in games" :key="index">{{g}}</li> </ul> </Category>
<Category title="电影"> <video controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video> </Category> </div> </template>
|
2.具名插槽
子组件中的插槽加个name,以便引用
1 2
| <slot name="center">我是一些默认值,当使用者没有传递具体结构时,我会出现1</slot> <slot name="footer">我是一些默认值,当使用者没有传递具体结构时,我会出现2</slot>
|
父组件中的组件用slot属性指明插入哪个插槽(同样可以用v-slot:center)
1 2 3 4
| <Category title="美食" > <img slot="center" src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt=""> <a slot="footer" href="http://www.baidu.com">更多美食</a> </Category>
|
3.作用域插槽
子组件可以通过slot向插槽使用者传输数据
1 2 3 4 5 6
| <template> <div class="category"> <h3>{{title}}分类</h3> <slot :games="games" msg="hello">我是默认的一些内容</slot> </div> </template>
|
父组件 必须用template标签包裹,不然接收不到数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <template> <div class="container">
<Category title="游戏"> <template scope="wht"> <ul> <li v-for="(g,index) in wht.games" :key="index">{{g}}</li> </ul> </template> </Category>
<Category title="游戏"> <template scope="{games}"> <ol> <li style="color:red" v-for="(g,index) in games" :key="index">{{g}}</li> </ol> </template> </Category>
</div> </template>
|
mixin混入
混合js文件
1 2 3 4 5 6 7
| export const mixin = { methods:{ showName(){ alert(this.name) } } }
|
局部混合
1 2 3 4 5 6 7 8 9 10 11 12
| //引入混合(局部混合) import {mixin} from '../mixin' export default { name:'Student', data(){ return{ name:'张三', sex:'男' } }, mixins:[mixin] }
|
全局混入
1 2 3
| //全局混合 import {mixin} from './mixin' Vue.mixin(mixin)
|