自定义 v-model
在 vue 中,v-model 是用来进行数据双向绑定的一个语法糖,比如说用在 input 上,输入框数据变化,v-model 绑定的对应的值也会跟着变化,无需单独获取。
其实 v-model 还可以在自定义组件中使用,可以被拆解为 props:value 和 event:input,也就是说组件必须接收一个 value 值以及名为 input 的自定义事件,就可以在自定义组件上使用 v-model 了。
- 新建一个组件UseVModel.vue
<template>
<input
type="text"
:value="textVal"
@input="$emit('changeMethod', $event.target.value)"
/>
<!--
1. 上面的 input 使用了 :value 而不是 v-model
2. 上面的 changeMethod 和 model.event 要对应起来
3. textVal 属性对应起来
-->
</template>
<script>
export default {
props: {
textVal: {
type: String,
default: () => '',
},
},
model: {
props: 'textVal',
event: 'changeMethod',
},
}
</script>
- 引用组件
<template>
<p>{{ name }}</p>
<UseVModel v-model="name" />
</template>
<script>
import UseVModel from '@/components/UseVModel.vue'
export default {
components: {
UseVModel,
},
data() {
return {
name: 'rmq',
}
},
}
</script>
我个人觉得有点类似于父子组件传值,父组件通过v-model传值,子组件props接收,通过
:value
,绑定值,然后通过$emit()
触发事件又传值给父组件,实现双向绑定。
- 可以不写model,但是需要改
$emit('input',$event.target.value)
。
$nextTick
$nextTick() 是在下次DOM更新循环结束之后执行延迟回调,在修改数据之后使用 $nextTick,则可以在回调中获取更新后的DOM(DOM的改变是发生在 $nextTick()之后),这个方法作用是当数据被修改后使用这个方法,会回调获取更新后的DOM再render出来。
$nextTick()作用:在下次DOM更新循环结束之后,执行延迟回调。在修改数据之后立即使用这个方法,获得更新后的DOM。
- 创建一个组件NextTick
<template>
<div>
<ul ref="ul1">
<li v-for="(item, index) in list" :key="index">{{ item }}</li>
</ul>
<button @click="change">添加</button>
</div>
</template>
<script>
export default {
data() {
return {
list: ['a', 'b', 'c'],
}
},
methods: {
change() {
this.list.push(`${Date.now()}`)
this.list.push(`${Date.now()}`)
this.list.push(`${Date.now()}`)
// 直接这样写,获取到的是渲染之前的3个
// const ul = this.$refs.ul1
// console.log(ul.childNodes.length)
//this.$nextTick()
// vue 是 异步渲染,this.$nextTick()待 DOM 渲染完成后再回调
// 页面渲染时会将 data 修改整合, 多次的 data 修改 只会渲染一次
this.$nextTick(() => {
const ul = this.$refs.ul1
console.log(ul.childNodes.length)
})
},
},
}
</script>
- 引用组件
<template>
<div class="home">
<!-- nextTick -->
<NextTick />
</div>
</template>
- 我们不用$nextTick时,是拿不到push之后的DOM节点
- 但是用$nextTick,就可以拿到渲染之后的DOM节点
slot使用
- 创建子组件SlotDemo
<template>
<div>
<a :href="url">
<slot :slotData1="slotData1"
name="rmq">父组件没有东西时显示默认内容</slot>
</a>
</div>
</template>
<script>
export default {
data () {
return {
slotData1: {
url: 'www.baidu.com1',
title: 'slot1',
subTitle: 'hello slot1',
},
}
},
props: {
url: {
type: String,
default: '',
},
},
}
</script>
- 引用
<template>
<div class="home">
<!-- slot -->
<!-- 父组件没有内容 -->
<SlotDemo :url="slotData.url"
#rmq></SlotDemo>
<!-- 父组件有内容 -->
<SlotDemo :url="slotData.url"
#rmq>
<template>{{ slotData.title }}</template>
</SlotDemo>
<!-- 父组件接收子组件传过来的slotData1的值,用 SlotProps 接收-->
<SlotDemo :url="slotData.url">
<template #rmq="SlotProps">
{{ SlotProps.slotData1.subTitle }}
</template>
</SlotDemo>
</div>
</template>
<script>
import SlotDemo from '@/components/SlotDemo.vue'
export default {
components: {
SlotDemo,
},
data() {
return {
slotData: {
url: 'www.baidu.com',
title: 'slot',
subTitle: 'hello slot',
},
}
},
}
</script>
props
和v-slot
配合也可以用来父子组件之间传值
动态、异步加载组件
- 动态组件
一个页面的构成可以动态控制
<template>
<div>
<!-- 动态组件 -->
<div v-for="(val, key) in comList" :key="key">
<component :is="val.componentName"></component>
</div>
</div>
</template>
<script>
export default {
data() {
return {
comList: {
1: { componentName: 'text' },
2: { componentName: 'text' },
3: { componentName: 'image' },
},
}
},
}
</script>
通过
<component :is="componentName"></component>
,来动态加载组件
- 异步加载组件
对于大的组件或者有异步加载组件需求的时候可以用
<template>
<div class="home">
<!-- 异步加载组件 -->
<WatchEventCom v-if="showCom"></WatchEventCom>
<button @click="showCom = true">异步加载组件</button>
<hr />
</div>
</template>
<script>
export default {
components: {
WatchEventCom: () => import('@/components/WatchEvent'), //如果同时引入2个相同组件,则不会异步加载,没有效果
Mixin,
},
data() {
return {
showCom: false,
}
},
}
</script>
- 异步加载之前
- 异步加载之后
keep-alive缓存组件
- Props:
include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
max - 数字。最多可以缓存多少组件实例。
- 用法:
<keep-alive>
包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和<transition>
相似,<keep-alive>
是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在组件的父组件链中。
当组件在<keep-alive>
内被切换,它的activated
和deactivated
这两个生命周期钩子函数将会被对应执行。
<!-- 基本 -->
<keep-alive>
<component :is="view"></component>
</keep-alive>
<!-- 多个条件判断的子组件 -->
<keep-alive>
<comp-a v-if="a > 1"></comp-a>
<comp-b v-else></comp-b>
</keep-alive>
<!-- 和 `<transition>` 一起使用 -->
<transition>
<keep-alive>
<component :is="view"></component>
</keep-alive>
</transition>
<!-- 逗号分隔字符串 -->
<keep-alive include="a,b">
<component :is="view"></component>
</keep-alive>
<!-- 正则表达式 (使用 `v-bind`) -->
<keep-alive :include="/a|b/">
<component :is="view"></component>
</keep-alive>
<!-- 数组 (使用 `v-bind`) -->
<keep-alive :include="['a', 'b']">
<component :is="view"></component>
</keep-alive>
<keep-alive :max="10">
<component :is="view"></component>
</keep-alive>
注意,
<keep-alive>
是用在其一个直属的子组件被开关的情形。如果你在其中有 v-for 则不会工作。如果有上述的多个条件性的子元素,<keep-alive>
要求同时只有一个子元素被渲染。
mixin混入
- 写一个mixin.js
export default {
data() {
return {
name: 'rmq'
}
},
mounted() {
console.log('mixin');
},
methods: {
mixinMethod() {
console.log('点击了mixin');
}
},
}
- 引入组件中
<template>
<div>
<p>name:{{name}}</p>
<p>age:{{age}}</p>
<button @click="mixinMethod">click</button>
</div>
</template>
<script>
import mixin from '../assets/mixin';
export default {
mixins: [mixin],
data () {
return {
age: '18'
};
},
}
</script>
可以看到组件中能够使用mixin,js中的data/methods等的数据方法。相当于把2个合并了。
这样可以抽离一些相同的逻辑,但是会有数据冲突
- 全局混入
// 为自定义的选项 'myOption' 注入一个处理器。
Vue.mixin({
created: function () {
var myOption = this.$options.myOption
if (myOption) {
console.log(myOption)
}
}
})
new Vue({
myOption: 'hello!'
})
// => "hello!"