本文共 7261 字,大约阅读时间需要 24 分钟。
注意下面是vue2的自定义v-model组件使得子组件和父组件的其他dom实现双向绑定,但是vue2.0的实现方式有诸多缺点
比如:
子组件CustomVModel.vue
<template> <!-- 例如:vue 颜色选择 --> <input type="text" :value="content" @input="$emit('changeEvent', $event.target.value)" > <!-- 1. 上面的 input 使用了 :value 而不是 v-model 2. 上面的 changeEvent 和 changeEvent 要对应起来 --></template><script>export default { model: { prop: 'content', // 这个content和下面的props里面的content是自己取的但是两者要对应一样 event: 'changeEvent' }, props: { content: String, default() { //默认值 return '' } }}</script>
父组件index.vue
<template> <div> <!-- <p>vue 高级特性</p> <hr> --> <!-- 自定义 v-model --> <p>{ { name}}</p> <!-- --> <CustomVModel v-model="name"/> </div></template><script>import CustomVModel from './CustomVModel'export default { components: { CustomVModel }, data() { return { name: '123', } }}</script>
根据上图的比较,我们得知vue3.0实现自定义v-model必须具备以下条件
必须具备modelValue属性
在子组件中ValidateInput:
vue3.0的context 是 setup() 的第二个形参,它是一个上下文对象,可以通过 context 来访问Vue的实例 this
在父组件中:
$nextTick会在DOM渲染之后被触发,以获取最新的DOM节点
<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()}`) const ulElem = this.$refs.ul1 //获取上面的DOM ul元素 console.log('没有用$nextTick '+ulElem.childNodes.length ) // 1. 异步渲染,$nextTick 待 DOM 渲染完再回调 // 3. 页面渲染时会将 data 的修改做整合,多次 data 修改只会渲染一次 this.$nextTick(() => { // 获取 DOM 元素 const ulElem = this.$refs.ul1 //获取上面的DOM ul元素 console.log(' 用了$nextTick '+ ulElem.childNodes.length ) }) }}}</script>
点第一次
一句话来概括就是将组件以变量的形式进行渲染
语法如下
<template><div> <component :is="HelloWorldName"/></div></template>import HelloWorld from './HelloWorld'export default { components: { HelloWorld }, data() { return { HelloWorldName: "HelloWorld", } }}</script>
异步加载组件的简单理解就是什么时候需要用到什么时候就加载
异步组件不需要用import引入了 在下面是这种方式: FormDemo: () => import(’…/…/BaseUse/FormDemo’) 在components里面引入的
点击这个按钮就会出现FormDemo组件(此组件就不贴代码了)
<template> <div> <FormDemo v-if="showFormDemo"/> <button @click="showFormDemo = true">show form demo</button> </div></template><script>export default { components: { FormDemo: () => import('../../BaseUse/FormDemo'),// 异步加载组件 }, data() { return { showFormDemo: false } }}</script>
先从字面意思 上理解,坚持活着,这里的主语是组件,其实本质上就是缓存组件的意思,缓存频繁切换的组件,使其不重复渲染。可以优化vue项目的性能
下面的实例营造了这样一个场景:有三个按钮A,B,C,分别点击不同的按钮,下面会出现不同的 组件,其实本质上就是一个简单的tabBar
将要渲染的三个组件 KeepAliveStageA,
KeepAliveStageB, KeepAliveStageC 用 keep-alive 组件包裹,他们就不会被销毁 了,除非页面被关闭
KeepAlive.vue
<template> <div> <button @click="changeState('A')">A</button> <button @click="changeState('B')">B</button> <button @click="changeState('C')">C</button> <keep-alive> <!-- tab 切换 --> <KeepAliveStageA v-if="state === 'A'"/> <KeepAliveStageB v-if="state === 'B'"/> <KeepAliveStageC v-if="state === 'C'"/> </keep-alive> </div></template><script>import KeepAliveStageA from './KeepAliveStateA'import KeepAliveStageB from './KeepAliveStateB'import KeepAliveStageC from './KeepAliveStateC'export default { components: { KeepAliveStageA, KeepAliveStageB, KeepAliveStageC }, data() { return { state: 'A' } }, methods: { changeState(state) { this.state = state } }}</script>
KeepAliveStateA.vue
<template> <p>state A</p></template><script>export default { mounted() { // eslint-disable-next-line console.log('A mounted') }, destroyed() { // eslint-disable-next-line console.log('A destroyed') }}</script>
KeepAliveStageB.vue
<template> <p>state B</p></template><script>export default { mounted() { // eslint-disable-next-line console.log('B mounted') }, destroyed() { // eslint-disable-next-line console.log('B destroyed') }}</script>
KeepAliveStageC.vue
<template> <p>state C</p></template><script>export default { mounted() { // eslint-disable-next-line console.log('C mounted') }, destroyed() { // eslint-disable-next-line console.log('C destroyed') }}</script>
多个组件之间有相同的逻辑,抽离出来。
这里其实思想和react的自定义hook一模一样
vue3中也有自定义hook的功能了
mixin语法有如下缺点:
朋友们不需要担心,现在vue3出来了,里面也有自定义hook了,完全可以代替mixin
下面一个场景就是,如果多个组件都需要使用到showName方法,就可以showName方法写到一个单独的js文件中,但是此js文件用的是vue的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.js引入到要渲染的组件,这个组件用到minxin的showName方法和city属性
<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: 'lisi', major: 'English' } }, methods: { }, mounted() { // eslint-disable-next-line console.log('component mounted', this.name) }}</script>
下面的自定义hook函数传入参数是一个dom节点,监听document中的点击事件,看点击的区域是否在这个节点内部
import { ref, onMounted, onUnmounted,Ref } from "vue";const useClickOutside = (elementRef: Ref<null | HTMLElement>) => { const isClickOutside = ref(false) const handler = (e: MouseEvent) => { if (elementRef.value) { if (!elementRef.value.contains(e.target as HTMLElement)) { isClickOutside.value = false }else{ isClickOutside.value = true } } } onMounted(() => { // console.log(123132132); document.addEventListener('click', handler) // console.log(isClickOutside.value); }) onUnmounted(() => { document.removeEventListener('click', handler) }) return isClickOutside}export default useClickOutside
const isClickOutside = useClickOutside(dropRef); watch(isClickOutside, () => { // console.log(isClickOutside.value); if (isOpen.value && !isClickOutside.value) { console.log("watch"); isOpen.value = false; } });
转载地址:http://jyue.baihongyu.com/