美文网首页
vue 高级特性

vue 高级特性

作者: 烁烁0925 | 来源:发表于2021-02-18 09:36 被阅读0次

    一 自定义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 高级特性

          本文链接:https://www.haomeiwen.com/subject/hxjgxltx.html