vue 高级特性

一 自定义v-model属性

vue在表单等domm模型中默认绑定了v-model的特性,而父组件中封装了有input的子组件,那么如何使父子组件都能进行双向绑定呢?这里提供一个案例:

父组件有一个custom-model子组件:

<template>
    <div>
        <p>{{name}}</p>
        <custom-model v-model="name" />
    </div>
</template>

<script>
import customModel from './CustomModel';
export default {
    components: {
        customModel
    },
    data() {
        return {
            name: ''
        }
    }
}
</script>

子组件custom-model:

<template>
    <input type="text" 
      :value="text1" 
      @input="$emit('change1', $event.target.value)" 
    />
    <!-- 上面的input使用了:value而非v-model -->
    <!-- 上面的change1和model.event对应起来 -->
    <!-- text1属性对应起来 -->
</template>

<script>
export default {
    model: {
        prop: 'text1',
        event: 'change1'
    },
    props: {
        text1: String,
        default() {
            return ''
        }
    }
}
</script>

父组件中的子组件自定义v-model,该值会与子组件model中的prop进行双向数据绑定,同时提供event作为事件名进行事件注册。

二 $nextTick

由于vue是异步渲染的,所以在修改vue的data等数据之后,等所有函数执行完才会进行dom渲染,所以在函数执行中,要在dom渲染后执行外面要包裹一层$nextTick,下面有一个案例:

<template>
    <div>
        <ul ref="ul1">
            <li v-for="(item,index) in list" :key="index">
                {{item}}
            </li>
        </ul>
        <button @click="add">add</button>
    </div>
</template>

<script>
export default {
    data() {
        return {
            list: ['a', 'b', 'c']
        }
    },
    methods: {
        add() {
            this.list.push(`${Date.now()}`)
            this.list.push(`${Date.now()}`)
            this.list.push(`${Date.now()}`)
            // 异步渲染  $nextTick等待dom渲染完成后执行
            // 页面渲染时将会对data的修改做整合,多次data修改只会修改一次
            this.$nextTick(() => {
                const ul1 = this.$refs.ul1
                console.log(ul1.childNodes.length)
            })
        }
    }
}
</script>

如果去掉this.$nextTick,在函数中打印的dom长度就不是6而是3,可以尝试一下。

三 slot插槽

高级的插槽使用作用域插槽和具名插槽,案例:

父组件

<template>
    <slot-name>
        <!-- 作用域插槽 -->
        <template v-slot="slotProps">
            {{slotProps.slotData.name}}
        </template>
        <!-- 具名插槽 -->
        <template v-slot:footer>
            <p>aaa</p>
        </template>
    </slot-name>
</template>

<script>
import slotName from './slotName';
export default {
    components: {
        slotName
    }
}
</script>

子组件

<template>
    <div>
        <a :href="website.url">
            <slot :slotData="website">
                哈哈哈
            </slot>
        </a>
        <slot name="footer"></slot>
    </div>
</template>

<script>
export default {
    data() {
        return {
            website: {
                url: 'http://www.baidu.com',
                name: '百度'
            }
        }
    }
}
</script>

子组件在slot中传入slotData属性和对应的value,到父组件v-slot的名称.slotData属性就是子组件website的值,这里注意一下在具名插槽footer里的插槽不能使用slotData的值,所以交作用域插槽,只是在这个slot内可以使用的变量

四 动态组件,异步组件

动态组件:

<Component :is="'text'" />

在子组件不确定的情况下,通过字符串传参表示所对应的类的名称,组件能够直接渲染出来,照常引用、注册组件,使用component标签并添加:is属性,属性值为组件名

异步组件:

普通组件采用import xx from xx的方式加载,为同步加载,当项目需要引入很大的组件时,同步加载性能会十分差,体验也很不理想,所以此时需要采用异步加载组件的方法。
方法:直接在components里定义组件名,使用import()方法

export default{
    components:{
        ${组件名}:()=>import(${组件路径})
    }
}

能够在组件初始化时不用渲染而等到需要使用的时候再进行渲染,在路由的配置中经常使用。

五 keep-alive

使用场景:频繁切换,不需要重复渲染的组件
被包裹的组件会被缓存,在频繁切换但不需要重复渲染的情况下使用,通常是vue其中一个性能优化的方向
用法:需要频繁切换的组件外层添加keep-alive标签,添加后组件切换时不会被销毁,会缓存起来,大幅提高渲染性能。
案例:
这是父组件,会同时切换两个组件

<template>
    <div>
        <keep-alive>
            <template v-if="data === 'c'">
                <c /> 
            </template>
            <template v-if="data === 'd'">
                <d />
            </template>
        </keep-alive>
        <button @click="handle">提交</button>
    </div>
</template>

<script>
import c from './c';
import d from './d';
export default {
    components: {
        c,
        d
    },
    data() {
        return {
            data: 'c'
        }
    },
    methods: {
        handle() {
            this.data === 'c' ? this.data = 'd' : this.data = 'c'
        }
    }
}
</script>

其中c组件和d组件基本相同只展示其中一个

<template>
  <div>c</div>
</template>

<script>
export default {
    mounted() {
        console.log('c mounted')
    },
    destroyed() {
        console.log('c destoryed')
    }
}
</script>
test

如果没有keep-alive,那么切换的时候destoryed函数也会执行,但是有的情况destoryed函数就不执行了,原因就在于keep-alive会缓存组件,组件在其中不会销毁。

六 mixin

组件抽离公共逻辑
多个组件有相同的逻辑可以抽离出来的时候就可以使用。但是目前mixin并不是完美的解决方案,会有一些问题。现在的Vue3意在解决这些问题。
案例:

<template>
   <div>
       {{a}}--{{b}}
       <button @click="handle">提交</button>
   </div>
</template>

<script>
import myMixins from './mixins';
export default {
   mixins: [ myMixins ],
   data() {
       return {
           a: 'aaa'
       }
   }
}
</script>

export default {
    data() {
        return {
            b: 'bbb'
        }
    },
    methods: {
        handle() {
            console.log(this.b)
        }
    }
}

mixin目前存在的缺点:

  • 变量来源不明确,不利于阅读。
  • 多mixin可能会造成命名冲突。
  • mixin可能存在多对多的关系,复杂度较高。

七 watch进阶

从我们刚开始学习Vue的时候,对于侦听属性,都是简单地如下面一般使用:

watch:{
    a(){
     //doSomething
    }
}

实际上,Vue对watch提供了很多进阶用法。

handler函数

以对象和handler函数的方式来定义一个监听属性,handler就是处理监听变动时的函数:

watch:{
    a:{
        handler:'doSomething'
    }
},
methods:{
    doSomething(){
        //当 a 发生变化的时候,做些处理
    }
}

handler有啥用?是多此一举么?用途主要有两点:

  • 将处理逻辑抽象出去了,以method的方式被复用
  • 给定义下面两个重要属性留出了编写位置

deep属性

当watch的是一个Object类型的数据,如果这个对象内部的某个值发生了改变,并不会触发watch动作!

也就是说,watch默认情况下,不监测内部嵌套数据的变动。但是很多情况下,我们是需要监测的!

为解决这一问题,就要使用deep属性:

watch:{
    obj:{
        handler:'doSomething',
        deep:true
    }
},
methods:{
    doSomething(){
        //当 obj 发生变化的时候,做些处理
    }
}

deep属性默认为false,也就是我们常用的watch模式。

immediate属性

watch 的handler函数通常情况下只有在监听的属性发生改变时才会触发。

但有些时候,我们希望在组件创建后,或者说watch被声明和绑定的时候,立刻执行一次handler函数,这就需要使用immediate属性了,它默认为false,改为true后,就会立刻执行handler。

watch:{
    obj:{
        handler:'doSomething',
        deep:true,
        immediate:true
    }
},
methods:{
    doSomething(){
        //当 obj 发生变化的时候,做些处理
    }
}

同时执行多个方法

使用数组可以设置多项,形式包括字符串、函数、对象

watch: {
   // 你可以传入回调数组,它们会被逐一调用
   a: [
       
     'handle1',
       
     function handle2 (val, oldVal) { /* ... */ },
       
     {
       handler: function handle3 (val, oldVal) { /* ... */ },
       /* ... */
     }
       
   ],
   
 }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 本文使用的Vue版本:2.6.10 Vue为我们提供了很多高级特性,学习和掌握它们有助于提高你的代码水平。 一、w...
    liujiangblog阅读 391评论 1 1
  • Vue高级特性 未经同意 禁止转载 自定义v-model $nextTick 1. vue是异步渲染 2. dat...
    吾名刘斩仙阅读 325评论 0 0
  • v-model $nextTick 汇总data的修改,一次性做视图更新减少DOM操作次数,提高性能 slot 父...
    卷村队队员阅读 124评论 0 0
  • 1、使用v-model进行父子传值 2、nextTick()异步加载dom元素 3、使用slot,分为匿名插槽、作...
    枸杞_7422阅读 113评论 0 0
  • 基本使用 父组件在引用的子组件中添加内容 子组件 slotDemo.vue 作用域插槽 父组件可以拿到子组件的数据...
    loushumei阅读 773评论 0 6