自定义v-model
//index.vue
<!-- 自定义 v-model -->
<p>{{name}}</p>
<CustomVModel v-model="name"/>
<template>
<input type="text"
:value="text1"
@input="$emit('change1', $event.target.value)"
>
<!--
1. 上面的 input 使用了 :value 而不是 v-model
2. 上面的 change1 和 model.event 的值要对应起来
3. text1 属性对应起来
-->
</template>
<script>
export default {
model: {
prop: 'text1', // 对应 props text1
event: 'change1'
},
props: {
text1:{
type:String,
default() {
return ''
}
}
}
}
</script>
$nextTick & refs
$nextTick
- Vue是异步渲染
- data改变后,DOM不会立刻渲染
- $nextTick会在DOM渲染之后被触发,以获取最新DOM节点
异步渲染,$nextTick 待 DOM 渲染完再回调。 页面渲染时会将 data 的修改做整合,多次 data 修改只会渲染一次
<template>
<div id="app">
<ul ref="ul1">
<li v-for="(item, index) in list" :key="index">
{{item}}
</li>
</ul>
<button @click="addItem">添加一项</button>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
list: ['a', 'b', 'c']
}
},
methods: {
addItem() {
this.list.push(`${Date.now()}`)
this.list.push(`${Date.now()}`)
this.list.push(`${Date.now()}`)
// 1. 异步渲染,$nextTick 待 DOM 渲染完再回调
// 3. 页面渲染时会将 data 的修改做整合,多次 data 修改只会渲染一次
this.$nextTick(() => {
// 获取 DOM 元素
const ulElem = this.$refs.ul1
// eslint-disable-next-line
console.log( ulElem.childNodes.length )
})
}
}
}
</script>
slot
- 基本使用:
//index
<SlotDemo :url="website.url">
{{website.title}} //插槽内容
</SlotDemo>
//SlotDemo.vue
<template>
<a :href="url">
<slot>
默认内容,即父组件没设置内容时,这里显示 // 插槽默认值
</slot>
</a>
</template>
<script>
export default {
props: ['url'],
data() {
return {}
}
}
</script>
- 作用域插槽:
//index.vue
<ScopedSlotDemo :url="website.url">
<template v-slot="slotProps"> //定义一个参数接收传来的值
{{slotProps.slotData.title}} //使用
</template>
</ScopedSlotDemo>
//ScopedSlotDemo.vue
<template>
<a :href="url">
<slot :slotData="website"> // 定义一个值用来传给父组件
{{website.subTitle}} <!-- 默认值显示 subTitle ,即父组件不传内容时 -->
</slot>
</a>
</template>
<script>
export default {
props: ['url'],
data() {
return {
website: {
url: 'http://wangEditor.com/',
title: 'wangEditor',
subTitle: '轻量级富文本编辑器'
}
}
}
}
</script>
- 具名插槽
index.vue
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
// 具有具名插槽的组件
//
<div class="container">
<header>
<slot name="header"></slot>
</header>
// 任何一种写法均可
// 方法一
<p>A paragraph for the main content.</p>
<p>And another one.</p>
// 方法二
<template v-slot:default>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</template>
<footer>
<slot name="footer"></slot>
</footer>
</div>
跟
v-on和v-bind一样,v-slot也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符#。例如v-slot:header可以被重写为#header:
动态组件
- 用法
:is="compontent-name"
<component :is="NextTickName"/>
...
export default {
components: {
NextTick
},
data() {
return {
NextTickName: "NextTick", //这里定义变量,值与使用的组件名一致
}
}
}
//使用示例
<div v-for="(val,key) in newsData" :key="key">
<component :is="val.type"/>
</div>
...
data() {
return {
newsData:{
1:{
type: 'text'
},
2:{
type: 'text'
}
3:{
type: 'image'
}
}
}
}
异步组件(Vue性能优化之一)
- import()函数
- 按需加载,异步加载大组件
<!-- 异步组件 -->
<FormDemo v-if="showFormDemo"/> // 定义异步组件
<button @click="showFormDemo = true">show form demo</button> // 设置按钮,点击事件触发showFormDemo为true
<script>
//import FormDemo from '../BaseUse/FormDemo' //设置同步组件
export default {
components: {
FormDemo: () => import('../BaseUse/FormDemo'),//设置异步组件
},
}
</script>
keep-alive(Vue性能优化之一)
- 缓存组件
- 频繁切换,不需要重复渲染
- Vue常见性能优化
语法:
<keep-alive>...</keep-alive>
// 组件和动态组件结合使用案例
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>
mixin
- 多个组件有相同逻辑,抽离出来
- mixin不是完美解决方案,会有一些问题
- Vue3提出的Composition API 旨在解决这些问题
//index.vue
<template>
<div>
<MixinDemo/>
</div>
</template>
<script>
import MixinDemo from './MixinDemo'
export default {
components: {
MixinDemo
},
}
</script>
// MixinDemo.vue
<template>
<div>
<p>{{name}} {{major}} {{city}}</p>
<button @click="showName">显示姓名</button>
</div>
</template>
<script>
import myMixin from './mixin'
export default {
mixins: [myMixin], // 可以添加多个,会自动合并起来
data() {
return {
name: '双越',
major: 'web 前端'
}
},
methods: {
},
mounted() {
// eslint-disable-next-line
console.log('component mounted', this.name)
}
}
</script>
// mixin.js
export default {
data() {
return {
city: '北京'
}
},
methods: {
showName() {
// eslint-disable-next-line
console.log(this.name)
}
},
mounted() {
// eslint-disable-next-line
console.log('mixin mounted', this.name)
}
}
mixin的问题
- 变量来源不明确,不利于阅读
- 多mixin可能会造成命名冲突
- minin和组件可能出现多对多的关系,复杂度较高