1. 自定义 v-model
v-model 可以在组件上使用以实现双向绑定。
vue2实现
父组件内容
<p>{{name}}</p>
<CustomVModel v-model="name" />
子组件内容
<template>
<!-- 例如:vue颜色选择器 -->
<input
type="text"
:value="text1"
@input="$emit('change1',$event.target.value)"
/>
<!--
1.上面的 input 使用了:value而不是v-model
2.上面的 change 和 model.event 要对应起来
3.text 属性对应起来
-->
</template>
<script>
export default {
model: {
props: "text1", //对应props text1
event: "change1",
},
props: {
text1: String,
default() {
return "";
},
},
};
</script>
vue3实现
父组件内容
<template>
<div>
<CustomInput v-model="message" /> {{ message }}
</div>
</template>
<script setup>
import CustomInput from './components/CustomInput.vue'
const message = ref('')
</script>
子组件内容
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template>
<script setup>
defineProps({
modelValue:{
type:String,
default:''
}
})
defineEmits(['update:modelValue'])
</script>
2.nextTick
等待下一次 DOM 更新刷新的工具方法。
vue 2 实现
<template>
<div id="app">
<ul ref="ul1">
<li v-for="item in list" :key="item">
<span> {{item}}</span>
</li>
</ul>
<button @click="addItem">添加一项</button>
</div>
</template>
<script>
export default {
name: "NextTick",
data() {
return {
list: ["a", "b", "c", "d"],
};
},
methods: {
addItem() {
this.list.push(`${Math.random() * 100}`);
this.list.push(`${Math.random() * 100}`);
this.list.push(`${Math.random() * 100}`);
//1.异步渲染,$nextTick 待 DOM 渲染完再回调
//2. 页面渲染时会将 data 的修改做整合,多次 data 修改只会渲染一次
this.$nextTick(() => {
//获取 DOM 元素
const ulElem = this.$refs.ul1;
//eslint
console.log(ulElem.childNodes.length);
});
},
},
};
</script>
vue 3实现
<template>
<div id="app">
<ul ref="ulElem"><li v-for="item in list" :key="item">
<span> {{item}}</span>
</li></ul>
<button @click="addItem">添加一项</button>
</div>
</template>
<script setup>
import { nextTick } from "vue";
const list = ref(["a", "b", "c", "d"])
const ulElem = ref('')
const addItem = () =>{
list.value.push(`${Math.random() * 100}`);
list.value.push(`${Math.random() * 100}`);
list.value.push(`${Math.random() * 100}`);
//1.异步渲染,$nextTick 待 DOM 渲染完再回调
//2. 页面渲染时会将 data 的修改做整合,多次 data 修改只会渲染一次
nextTick(() =>{
//获取 DOM 元素
//eslint
console.log(ulElem.value.children.length);
})
}
</script>
3.slot
vue
1.普通插槽
父组件
<template>
<SlotDemo :url="website.url">
{{website.title}}
</SlotDemo>
</template>
<script>
export default{
data() {
return {
name: "demo",
website: {
url: "http://imooc.com/",
title: "imooc",
subTitle: "程序员的梦工厂",
}
};
},
}
</script>
子组件
<template>
<a :href="url">
<slot>
默认内容,即父组件没设置内容时,这里显示
</slot>
</a>
</template>
<script>
export default {
props: ["url"],
data() {
return {};
},
};
</script>
2. 作用域插槽
父组件
<template>
<ScopedSlotDemo :url="website.url">
<template v-slot="slotProps">
{{slotProps.slotData.title}}
</template>
</ScopedSlotDemo>
</template>
<script>
export default{
data() {
return {
name: "demo",
website: {
url: "http://imooc.com/",
}
};
},
}
</script>
子组件
<template>
<a :href="url">
<slot :slotData="website">
{{website.subTitle}}
<!-- 默认值显示 subTitle ,即父组件不传内容时 -->
</slot>
</a>
</template>
<script>
export default {
props: ["url"],
data() {
return {
website: {
title: "wangEditor",
subTitle: "轻量级富文本编辑器",
},
};
},
};
</script>
3. 具名插槽
父组件
<template>
<NameSlotDemo>
<template #header>
header
</template>
body
<template #footer>
footer
</template>
</NameSlotDemo>
</template>
子组件
<template>
<div>
<slot name="header"></slot>
<slot></slot>
<slot name="footer"></slot>
</div>
</template>
4.动态、异步组件
动态组件
需要根据数据,动态渲染的场景,即组件类型不确定的时候使用
<component :is="BarChart" />
异步组件
需要时就加载,不需要时就不用加载
同步组件
import SlotDemo from './components/SlotDemo.vue'
import ScopedSlotDemo from './components/ScopedSlotDemo.vue'
import NameSlotDemo from './components/NameSlotDemo.vue'
异步组件
export default {
components: {
NameSlotDemo:() => import('./NameSlotDemo.vue')
},
}
5.keep-alive
在切换时创建新的组件实例通常是有意义的,但在这个例子中,我们的确想要组件能在被“切走”的时候保留它们的状态。要解决这个问题,我们可以用 内置组件将这些动态组件包装起来
<!-- 非活跃的组件将会被缓存! -->
<KeepAlive>
<component :is="activeComponent" />
</KeepAlive>
6.mixin
- 多个组件有相同逻辑,抽离出来
- mixin 并不少完美的解决方案,会有一些问题
- Vue 3 提出的 Composition API 旨在解决这些问题
将部分逻辑抽离,然后再和不同的页面进行混合, mixin.js 就是抽离出来的公共逻辑
// 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)
}
}
// 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>