Vue组件化开发
组件:是一个独立显示的视图单元,在构建大型应用时,我们通常会把可复用性强的部分。. (例如:头部导航、侧边栏等需要在多个页面展示的部分)提取出来,最终组合成一个完整的页面。. 在vue中,组件是可复用的vue实例。全局组件:注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中,全局组件必须写在Vue实例创建之前。:::warning全局组件缺点:组件注册之后无法被回收,一直保
一、组件基础
1、组件介绍
组件:是一个独立显示的视图单元,在构建大型应用时,我们通常会把可复用性强的部分。
. (例如:头部导航、侧边栏等需要在多个页面展示的部分)提取出来,最终组合成一个完整的页面。
. 在vue中,组件是可复用的vue实例。
2、组件的定义
①组件的注册:
-
全局组件:注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中,全局组件必须写在Vue实例创建之前。
:::warning
全局组件缺点:组件注册之后无法被回收,一直保存在内存中。
::: -
局部组件:使用对象定义组件,在vue实例对象上使用components属性声明本模块的局部组件,在components对象中,属性名就是自定义组件名,属性值就是该组件的选项对象。
②组件的引用
- 引用方式类似于标签,用尖括号包裹组件名
③组件的复用
- 每个组件都可以任意次数的复用
- 每个组件都是独立的,它们都有自己的实例,每个组件只能有一个根元素
④组件引用规则
- 由于HTML对于大小写是不敏感的,我们在DOM模板中需要使用kebab-case写法
- 在单文件组件、字符串模板和 JSX 中,组件引用时需要使用PascalCase写法
⑤组件的闭合
- 在 DOM 模板中,建议使用标签闭合组件
- 在单文件组件、字符串模板和 JSX 中,建议使用自闭和组件:
<body>
<div id="app">
<my-com></my-com>
<one></one>
<two></two>
</div>
<script>
//注册全局组件,new Vue之前,一旦注册,一直占用内存
//优点:调用速度快;缺点:占用内存
//命名不区分大小写,用-连接命名
Vue.component("my-com",{
template : "<div>我是全局组件</div>"
})
//注册局部组件
let com1={
template : "<div>我是局部组件1</div>"
}
let com2={
template : "<div>我是局部组件2</div>"
}
new Vue({
el : "#app",
data : {
text : ""
},
//挂载到vue实例的components上
components:{
'one':com1,
'two':com2
}
});
</script>
</body>
3、组件与实例的共性分析
Vue的实例与组件的关系:通过new Vue创建的Vue的根实例,SPA应用中,只会创建一个Vue根实例,组件都是通过这个根实例启动的
4、组件中的data属性
在组件中data属性与实例的data属性声明有所不同,vue实例中的data可以是一个对象,但组件中的data必须是一个返回值为object对象的函数
<body>
<div id="app">
<my-com></my-com>
<one></one>
<two></two>
</div>
<script>
//注册全局组件,new Vue之前,一旦注册,一直占用内存
//优点:调用速度快;缺点:占用内存
//命名不区分大小写,用-连接命名
Vue.component("my-com",{
template : "<div>我是全局组件</div>"
})
//注册局部组件
let com1={
template : "<div>我是局部组件1{{msg}}</div>",
//组件没有el属性
//组件的data属性必须是一个函数,而且必有有返回值,返回值是对象
data:function() {
return {
msg : "!"
}
}
}
let com2={
template : "<div>我是局部组件2</div>"
}
new Vue({
el : "#app",
data : {
text : ""
},
//挂载到vue实例的components上
components:{
'one':com1,
'two':com2
}
});
</script>
</body>
5、组件的生命周期
二、组件的特性
1、prop属性
①操作过程单向数据流
单向数据流的过程是:用户访问view,view发出用户交互的Action,在Action里对state进行更新。state更新后,会触发View更新页面。这样的数据总是单向流转,便于维护
传递数据是单向的,数据总是由父组件传递到子组件。子组件在内部维护着自己的状态,它无权修改父组件传递给它的数据。如果我们在子组件修改props,vue将会报出警告。这样更有利于组件间的解耦。
②单向数据流的意义
如果一个子组件修改了父组件的数据,那么该父组件下的其它子组件的数据也会发生改变。
③props 属性
组件中的特殊属性,用于接收来自父组件的数据。可以以数组或对象的方式声明。
对象类型声明方式:
当无需对属性进行更多限定时可以采用数组类型定义多个属性。
数组类型声明方式:
props中声明的属性是全局属性,可以直接在模板、method、计算属性中使用this访问,使用方法与data类似。
④父组件props 属性传递
单向数据流:父组件prop的更新会向下流动到子组件,子组件中所有的prop都会更新,使得子组件同步更新。但子组件不要更新父组件数据。
prop的命名规则:在定义prop时可以使用camelCase和kebab-case两种方式。但在模板中引入组件时,由于html是忽略大小写的,因此只能使用kebab-case方式引入。
:::warning
父组件props 属性传递的注意事项:
许多场景下props数据属性并非只读属性,需要修改:
设定props的观察属性,接收父组件传递参数的克隆对象
父组件传递的参数变化后,子组件props属性自动更新,但子组件并不知道更新的事件点
设定props的观察属性,监控数据变化,从而实现局部刷新
:::
<body>
<div id="app">
<parent></parent>
</div>
<script>
let child={
//接收父组件的参数
props:['msg'],
template : `<div>我是子组件,{{msg}}</div>`,
}
let parent={
//给子组件传递参数
template : `<div>我是父组件<child v-bind:msg="msg"></child></div>`,
data() {// =data:function(){
return{
msg : '来自父组件的问候'
}
},
components : {
'child':child
}
}
new Vue({
el : "#app",
data : {
},
components : {
'parent':parent
}
});
</script>
</body>
<body>
<div id="app">
<parent></parent>
</div>
<script>
let child = {
//接收父组件的参数
props: ['stu'],
template: `
<div><h2>个人信息</h2>
<div style="border: 1px solid black;width: 200px">
<!-- 可以绑定对象的属性-->
<p>姓名:<input v-model="user.name"></p>
<p>性别:<input v-model="stu.sex"></p>
<p>年龄:{{ stu.age }}</p>
<p>地址:{{ stu.address }}</p>
</div>
</div>`,
data (){
return {
//初始化user对象
user : {}
}
},
watch:{
stu:function (val) {
// 对象克隆,把val对象里的属性和方法都复制到user对象里,新对象内存地址和val不一样,达到解绑的目的
this.user = Object.assign({},val);
},
}
}
let parent = {
//给子组件传递参数
template: `
<div><h2>信息表</h2>
<table>
<tr>
<th>姓名</th>
<th>性别</th>
<th>年龄</th>
<th>地址</th>
<th>操作</th>
</tr>
<tr v-for="stu in dataList">
<td>{{ stu.name }}</td>
<td>{{ stu.sex }}</td>
<td>{{ stu.age }}</td>
<td>{{ stu.address }}</td>
<td>
<!-- 将stu对象传入edit方法-->
<button @click="edit(stu)">修改</button>
</td>
</tr>
</table>
<!-- 子组件默认不显示,点击编辑后更改show的值后显示,接收的stu对象来自data-->
<child v-show="show" v-bind:stu="stu"></child>
</div>`,
data() {// =data:function(){
return {
dataList: [{name: '李雷', sex: '男', age: 20, address: '宁波'}, {name: '韩梅梅', sex: '女', age: 18, address: '长春'}],
show: false,
stu: ''
}
},
methods: {
edit(stu) {
//子对象显示
this.show = true;
//将传入的stu对象赋值给data中的stu
this.stu = stu;
}
},
components: {
'child': child
}
}
new Vue({
el: "#app",
data: {},
components: {
'parent': parent
}
});
</script>
</body
2、动态组件
父组件对应的子组件不确定,需要通过捕获用户的操作决定动态加载组件
component组件:vue内置抽象组件,代表为具体命名的组件
<body>
<div id="app">
<parent></parent>
</div>
<script>
let child1={
props:['msg'],
template : `<div>我是子组件1</div>`,
}
let child2={
props:['msg'],
template : `<div>我是子组件2</div>`,
}
let parent={
template : `<div>我是父组件
<button @click="show(1)">组件1</button><button @click="show(2)">组件2</button>
<!-- :is后是一个变量,变量的值是组件的名字-->
<component :is='currentView'></component>
</div>`,
data() {// =data:function(){
return{
msg : '来自父组件的问候',
currentView: 'child2'
}
},
methods:{
show(i){
this.currentView='child'+i;
},
},
components : {
'child1':child1,
'child2':child2
}
}
new Vue({
el : "#app",
data : {
},
components : {
'parent':parent
}
});
</script>
</body>
3、递归组件
组件在自己的模板中调用自己渲染,它们必须有name属性,在组件中通过name属性调用本组件标签
<body>
<div id="app">
<my-com></my-com>
</div>
<script>
let myCom={
//递归组件的模板中必须包含自己
//递归组件必须有结束条件
template : `<div><button @click="getChild">递归组件</button><com v-if="show"></com></div>`,
//必须给组件起名字,必须有name属性
name : 'com',
data(){
return{
show : false,
}
},
methods:{
getChild(){
this.show=!this.show;
}
}
}
new Vue({
el : "#app",
data : {},
components : {
'my-com':myCom
}
});
</script>
</body>
4、异步组件
vue允许以工厂函数的方式定义组件,这个工厂函数会异步解析组件定义,只有在组件需要渲染的时候才会触发该工厂函数,且把结果缓存起来供未来重新渲染。
<body>
<div id="app">
<my-com></my-com>
</div>
<script>
//异步组件第一个的参数是一个函数,函数的返回值是一个组件对象
//第二个参数是一个对象,对象中的属性是组件的一些配置
Vue.component('my-com',function (resolve,reject) {
//异步组件的特点:只有在使用的时候才会加载
setTimeout(function () {
resolve({
template : `<div>我是异步组件</div>`
})
},3000)
})
new Vue({
el : "#app",
data : {},
});
</script>
</body>
5、组件api接口
vue组件的api接口:通过this指针调用(Vue实例对象也具备)
数据属性类:
DOM属性类:
数据操作类:
<body>
<div id="app">
<div ref="nb">宁波</div>
<button @click="getRef">获取</button>
<br><input v-model="msg">
<button @click="getMsg">获取</button>{{abc}}
</div>
<script>
new Vue({
el : "#app",
data : {
msg:'',
},
methods:{
getMsg(){
//获取input的值
//console.info(this.$data.msg);
//js写法不会双向绑定
// this.abc='ok';
// console.info(this.abc);
this.$set(this,'abc','ok');
},
getRef(){
//获取dom元素,必须有ref属性,否则获取不到
console.info(this.$refs.nb)
}
}
})
</script>
</body>
三、组件通信
1、发布订阅者模式
①父子通信信息流:
②发布订阅者模式:
用于定义发布者对象与订阅者对象的一对一或一对多的关系,当发布者对象状态发生改变时,所有依赖于它的订阅者对象都会得到通知。
. 1.支持广播通信
. 2.发布者和订阅者低耦合
③发布订阅者模式实现机制
2、父子通信
父组件向子组件传递数据:在引用子组件时,通过v-bind绑定自定义属性,通过props传递给子组件,子组件中通过props接收。
- 传递数据通过props属性
- 数据变更通过子组件的观察属性监控
3、子父通信
子组件向父组件传递数据:
- 子组件通过$emit触发自定义事件,第一个参数为事件名,第二个参数为要传递的参数。
- 父组件监听子组件的事件,通过参数取到。
<body>
<div id="app">
<parent></parent>
</div>
<script>
let child1={
template:`<div>
<h2>子组件1</h2>
<button @click="send">发送</button>
</div>`,
methods:{
send(){
// 子组件通过$emit触发自定义事件,第一个参数为事件名,第二个参数为要传递的参数。
this.$emit('sendMsg','我是子组件1');
}
}
}
let child2={
template:`<div>
<h2>子组件2</h2>
<button @click="send">发送</button>
</div>`,
methods:{
send(){
//发布消息
this.$emit('sendMsg','我是子组件2');
}
}
}
let parent={
template:`<div>
<h2>父组件</h2>
<child1 @sendMsg="getMsg"></child1>
<child2 @sendMsg="getMsg"></child2>
</div>`,
methods:{
//父组件监听子组件的事件,通过参数取到。
getMsg(msg){
console.info(msg);
}
},
components:{
'child1':child1,
'child2':child2
}
}
new Vue({
el : "#app",
data : {
},
components : {
'parent':parent
}
})
</script>
</body>
4、非父子通信
方案一:
创建一个Vue实例作为中央事件总线,通过它来监听( o n ) 和触发 ( on)和触发( on)和触发(emit)事件。适用于组件间全部通信方式。
<body>
<div id="app">
<parent></parent>
</div>
<script>
let bus=new Vue();//bus当成事件总线对象
let child1={
template:`<div>
<h2>子组件1</h2>
<button @click="send">发送</button>
</div>`,
methods:{
send(){
// 子组件通过$emit触发自定义事件,第一个参数为事件名,第二个参数为要传递的参数。
bus.$emit('sendMsg','我是子组件1');
}
}
}
let child2={
template:`<div>
<h2>子组件2</h2>
</div>`,
methods:{
},
mounted(){
//监听子组件1的事件,通过参数取到。
bus.$on('sendMsg',function(msg){
console.info(msg);
})
}
}
let parent={
template:`<div>
<h2>父组件</h2>
<child1 ></child1>
<child2 ></child2>
</div>`,
components:{
'child1':child1,
'child2':child2
}
}
new Vue({
el : "#app",
data : {
},
components : {
'parent':parent
}
})
</script>
</body>
方案二:
• 1.子组件1先把数据传递给父组件,
• 2.父组件接受到数据后动态绑定给子组件2
• 3.子组件2通过props接受
方案三:
使用状态管理工具vuex
四、内置组件
1、component组件
component:渲染一个“元组件”为动态组件,根据is属性的值,来决定哪个组件被渲染
使用场景:tab切换
<body>
<div id="app">
<parent></parent>
</div>
<script>
let child1={
props:['msg'],
template : `<div>我是子组件1</div>`,
}
let child2={
props:['msg'],
template : `<div>我是子组件2</div>`,
}
let parent={
template : `<div>我是父组件
<ul>
<li><button @click="show(1)">组件1</button></li>
<li><button @click="show(2)">组件2</button></li>
</ul>
<!-- :is后是一个变量,变量的值是组件的名字-->
<component :is='currentView'></component>
</div>`,
data() {// =data:function(){
return{
msg : '来自父组件的问候',
currentView: 'child2'
}
},
methods:{
show(i){
this.currentView='child'+i;
},
},
components : {
'child1':child1,
'child2':child2
}
}
new Vue({
el : "#app",
data : {
},
components : {
'parent':parent
}
});
</script>
</body>
:::warning
适用场景:需要多次访问、多次离开的组件
:::
2、keep-alive组件
keep-alive组件:会缓存不活动的组件实例,而不是销毁它们,这样当我们再次访问不活动的组件时,不需要请求数据渲染页面,大大地提高了性能。
<keep-alive include="['Home','view']">
<component :is="view"></component>
</keep-alive>
<keep-alive>
<Big-Num v-if="this.count > -1">非负数</Big-Num>
<Small-Num v-else>负数</Small-Num>
</keep-alive>
keep-alive组件的模糊匹配:可以通过表达式设定那些组件被缓存。通过如下属性设定
. include 只有名称匹配的组件会被缓存,传入字符串或正则表达式或数组
. exclude 不会被缓存的组件,传入字符串或正则表达式或数组
. max 最多可以缓存的组件实例个数,传入数字类型
<keep-alive include="a,b"></keep-alive>
<keep-alive :exclude="/a|b/"></keep-alive>
<keep-alive :max=5></keep-alive>
keep-alive缓存的组件的特定生命周期
• activated:切换至该组件时触发
• deactivated:从该组件切换出去时触发
:::warning
适用场景:需要多次访问、多次离开的组件
:::
3、solt组件
slot插槽适用于结构相同、内容存在差异的场景
<body>
<div id="app">
<parent></parent>
</div>
<script>
let child={
template : `<div>子组件<br>
<slot></slot><br>
<slot name='one'></slot><br>
<slot name='two'></slot><br>
<slot name='three' :msg="msg"></slot><br>
</div>`,
data(){
return {
msg:'我是子组件的数据'
}
}
}
let parent={
template:`<div>
<h2>父组件</h2>
<child>
<!-- 以下写法一样,默认插槽,不会覆盖-->
<span style="color: red">我是默认插槽1</span>
<template><span style="color: red">我是默认插槽2</span></template>
<!-- 以下写法一样,默认插槽,会覆盖-->
<template v-slot:default><span style="color: red">我是默认插槽3</span></template>
<template v-slot><span style="color: red">我是默认插槽4</span></template>
<template #default><span style="color: red">我是默认插槽5</span></template>
<!-- 以下是具名插槽-->
<template #one><span style="color: red">我是具名插槽one</span></template>
<template v-slot:two><span style="color: red">我是具名插槽two</span></template>
<!-- 作用域插槽,通过prop获取子组件传出的属性-->
<template v-slot:three="prop"><span style="color: red">我是具名插槽three,{{prop.msg}}</span></template>
</child>
</div>`,
components:{
'child':child
}
}
new Vue({
el : "#app",
data : {
},
components : {
'parent':parent
}
})
</script>
</body>
更多推荐
所有评论(0)