Vue高级特性
自定义v-model
当我们需要在自定义的组件上使用v-model
属性时,就需要自己去实现父子组件里的v-model
通信了。用props
传值,子组件将需要改变的值接收,然后使用model
添加自定义事件将其绑定在标签上。
// 父组件
<template>
<p>
{{name}}
</p>
<Son v-model="name" />
</template>
<scirpt>
import Son from './Son'
export default {
components:{
Son
},
data(){
return {
name:'asuhe'
}
}
}
</scirpt>
// 子组件
<template>
<input type="text" :value="text" @input="$emit('change',$event.target.value)" >
</template>
<scirpt>
export default {
model:{
prop:'name', // 对应 props的name
event:'change'
}
props:{
name:{
type:String,
default:1
}
}
}
</scirpt>
$nextTick
因为vue是异步渲染的。在其运作过程中,data数据改变后不会立马渲染DOM,而是用Document.createDocumentFragment
创建文档碎片将多次data数据修改后的所有DOM操作整合成一次再去插入DOM中进行渲染,这也是vue性能优秀的原因之一。
在DOM渲染之后,vue提供了一个$nextTick
函数,它会在DOM渲染之后触发,我们可以利用它来获取最新的DOM节点。
<template>
<p>
{{name}}
</p>
</template>
<script>
export default {
data(){
return {
name:'zhangsan'
}
},
mounted(){
this.$nextTick(()=>{
console.log('调用了$nextTick');
})
}
}
</script>
slot
当一个组件会被多次使用,且里面大部分内容不变仅有非常少部分的结构改变时,可以使用slot插槽,将其理解为占位符。该种通信方式适用于父组件给子组件传递数据,但它与其它通信不同的是,它可以传递结构给子组件,子组件中的slot
标签本质上就是一个占位符。若父组件给其传递template
则使用父组件传递过来的template
,否则使用默认定义的template
。插槽又分为默认插槽、具名插槽和作用域插槽。
默认插槽约定成俗只能有一个,具名插槽就是在默认插槽的基础上加上name
属性唯一标识这个插槽,这样父组件在传递数据的时候可以根据名字精准传递到指定的插槽中。
作用域插槽中子组件的slot可以通过 属性传递值给父组件,然后父组件可以根据不同需求改变这个slot内部的显示结构,把子组件的值,传给父组件固定的区域进行操作。父组件的数据是给子组件展示的。子组件展示过程当中,数据的结构由父组件决定的。
默认插槽
//子组件放置插槽
<slot></slot>
<slot name="asu"></slot>
//父组件传递数据
<template>
<button>点击<button>
</template>
<template slot="asu">
<a href="http://asuhe.fun"></a>
</template>
作用域插槽
//父组件
<template slot-scope="{todo,index}">
<span v-if="todo.isOver" style="color:hotpink">√ {{todo.content}}</span> //父组件控制子组件的样式
</template>
//子组件
<slot :todo="todo" :index="index">
{{todo.content}}
</slot>
作用域插槽接收子组件数据时的指令:
-
slot
属性弃用,具名插槽通过指令参数v-slot:插槽名
的形式传入,可以简化为#插槽名
-
slot-scope
属性弃用,作用域插槽通过v-slot:xxx="slotProps"
的slotProps来获取子组件传出的属性 -
v-slot
属性只能在<template>
上使用,但在【只有默认插槽时】可以在组件标签上使用
注意事项
- 默认插槽名为
default
,可以省略default
直接写v-slot
, 缩写为#时不能不写参数,写成#default
(这点所有指令都一样,v-bind、v-on) - 多个插槽混用时,
v-slot
不能省略default
- 同样可以通过解构获取
v-slot={user}
,还可以重命名v-slot="{user: newName}"
和定义默认值v-slot="{user = '默认值'}"
- 插槽名可以是动态变化的
v-slot:[slotName]
动态组件
所谓动态组件就是在一个页面中,各个组件的组成是不一样的。但是在同一个路由下,里面的二级路由组合可能经常变动。有些组件我们选择展示,有些组件我们选择不展示。这种时候就可以使用动态组件。
// 动态组件 Son
<template>
<p>asuhe</p>
</template>
// 父组件
<template>
// 用 Component 标签表示动态组件 is为动态组件的名称
<Component :is="NextTickName"></Component>
/* 不能直接让 is 为具体的组件名称,必须为变量
错误用法 报错 <Component is="Son"></Component> */
</template>
<script>
import Son from './Son'
export default {
components:{
Son
},
data(){
return {
NextTickName:'Son'
}
}
}
</script>
异步组件
在某些场景下不需要加载全部的组件,我们只需要加载部分必要组件,当某些功能被使用到了我们就可以再去加载那些组件。这时我们就可以利用异步加载组件的技术,使用import()
函数来实现这个功能,可以对我们的页面有非常大的性能提升。路由懒加载就是利用这个原理
// 异步的组件
<template>
<p>AsyncComponent</p>
</template>
// 父组件
<template>
<Son v-if="show"></Son>
<button @click="load">点击加载子组件</button>
</template>
<script>
// 同步引入组件 import Son from './Son'
export default {
components:{
// 异步加载组件
Son:()=> import('./Son')
},
data(){
return {
show:false
}
},
methods:{
load(){
this.show = true;
}
}
}
</script>
keep-alive
正常情况下当我们切换组件时,组件对象会被销毁。有时我们不希望如此,使用keep-alive
可以将组件缓存下来。当我们在组件里来回切换的时候组件实例会被保存,不会被销毁所以也不用重复渲染,能够极大提升页面性能。
// 保活组件
<template>
<p>AsyncComponent</p>
</template>
<template>
<p></p>
<keep-alive>
</keep-alive>
</template>
<script>
import Son from './Son'
export default {
components:{
Son
},
data(){
return {
}
}
}
</script>
mixin
html、js、css相同时我们会封装组件。单个组件里js代码重复我们会封装函数。当不同的组件js代码重复 封装混合时,我们就可以使用minx
混入技术,重用js代码。新建一个myminxi.js文件 在js文件中暴露一个对象 对象内部可以有data methods computed… 会将js文件中暴露出的数据 方法等混入到组件内部。
使用
//在组件内部引入 import myminxi from './myminxi.js'
//使用mixins:[mymixin] 例如:
import {mixin} from './mymixin'
export default {
name: 'Daughter',
mixins:[mixin],
data(){
money:1000
}
},
-------
// myminx.js
export const mixin = {
methods: {
borrowMoney (count) {
this.money -= count
},
gaveMoney (count) {
this.money -= count
// 给父组件增加count
this.$parent.money += count
}
}
}
mixin技术的特点
- 可以提高代码复用,优化性能
- 变量来源不明确,不利于阅读
- 多mixin可能会造成命名冲突
- mixin的组件可能会出现多对多的关系,复杂度较高