一、常见高级特性

  • 自定义 v-model
  • $nextTick
  • slot
  • 动态、异步组件
  • keep-alive
  • mixin

二,$nextTick

基本原理:

  1. Vue是异步渲染,data 改变之后,DOM 不会立即被渲染
  2. $nextTick => 等到 dom 渲染完毕再进行回调,获取最新的 dom
  3. 页面渲染时会将 data 的修改做整合,多次 data 修改只会渲染一次。

拿到 DOM 元素的节点:
在对应的 DOM 元素上面设置 ref=”xxx”, 使用 this.$ref.xxx 即可获取到对应的 DOM 元素.

三,slot

03-vue高级特性 - 图11,基本使用:让父组件往子组件中插入一段内容,即把父组件里面的一些内容,传递给子组件,取代子组件里面slot里面的相应内容

  1. <template>
  2. <div>
  3. <p>vue 高级特性</p>
  4. <hr>
  5. <SlotDemo :url="website.url">
  6. {{website.title}}
  7. </SlotDemo>
  8. </div>
  9. </template>
  10. <script>
  11. import SlotDemo from './SlotDemo'
  12. export default {
  13. components: {
  14. SlotDemo
  15. },
  16. data() {
  17. return {
  18. name: '双越',
  19. website: {
  20. url: 'http://imooc.com/',
  21. title: 'imooc',
  22. subTitle: '程序员的梦工厂'
  23. }
  24. }
  25. }
  26. }
  27. </script>
<template>
    <a :href="url">
        <slot>
            默认内容,即父组件没设置内容时,这里显示
        </slot>
    </a>
</template>

<script>
export default {
    props: ['url'],
    data() {
        return {}
    }
}
</script>

这个时候,slot 里面的内容就会变为 http://imooc.com/

2,作用域插槽:将子组件里面的 data 数据抛给父组件,让父组件可以获取使用到子组件抛出来的数据
关键点:
在子组件里面里面定义一个自定义属性slotData,父组件里面通过引入子组件,在其子组件里面写一个 template, 给 v-slot 取一个名字slotProps, 通过 slotProps.slotData.title 来获取到对应的子组件data里面的数据

<template>
    <div>
        <p>vue 高级特性</p>
        <hr>
        <ScopedSlotDemo :url="website.url">
            <!---{{website.title}}--->
            <template v-slot="slotProps">
                {{slotProps.slotData.title}}
            </template>
        </ScopedSlotDemo>
    </div>
</template>

<script>
import SlotDemo from './SlotDemo'

export default {
    components: {
        ScopeSlotDemo
    },
    data() {
        return {
            name: '双越',
            website: {
                url: 'http://imooc.com/',
                title: 'imooc',
                subTitle: '程序员的梦工厂'
            }
        }
    }
}
</script>
<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>

3,具名插槽

<!--- NameSlot 组件 ---->
<div class-"container">
  <header>
    <slot name="header"></slot>
  </header>

  <main>
    <slot></slot>
  </main>

  <footer>
    <slot name="footer"></slot>
  </footer>
</div>
<NameSlot>

  <!---- 缩写 <template #header> ----->
  <template v-slot:header>
    <h1>将插入 header slot 中</h1>
  </template>

  <p>将插入到 main slot 中,即未命名的 slot </p>

   <template v-slot:footer>
    <h1>将插入 footer slot 中</h1>
  </template>
</NameSlot>

四,动态组件

情景:一个新闻详情页面,里面的内容由 text 组件,img组件,video组件 组合而成,但是内容不确定,这个时候,应该如何正确的渲染页面的内容?
答:使用动态组件进行处理

如下面例子所示,动态组件的用法,,NextTickName 是 data 里面定义的一个变量,对应组件的名字,通过改变组件的名字,就可以动态的加载不同的组件内容,渲染不同的的页面内容。

// 父组件

<template>
    <div>
        <!--<component :is="NextTickName"/>--->
        <!-- 动态组件 -->
        <div v-for="(val, key) in newsData" :key="key">
            <component :is="val.type"/>
        </div>
    </div>
</template>

<script>
import NextTick from './NextTick'

export default {
    components: {
        NextTick
    },
    data() {
        return {
           // NextTickName: "NextTick"
             newsData: {
              1:{
              type: 'text
            },
            2:{
              type: 'text
            },
               3:{
              type: 'img
            },
            4:{
              type: 'video
            }
          }
        }
    }
}
</script>

五,异步组件

import 函数
按需加载,异步加载大组件

<template>
    <div>
        <!-- 异步组件 -->
        <FormDemo v-if="showFormDemo"/>
        <button @click="showFormDemo = true">show form demo</button>
    </div>
</template>

<script>
// import FormDemo from '../BaseUse/FormDemo'  这样的引入方式是同步引入,打包的时候,会打包所有的组件

export default {
    components: {
        // FormDemo // 同步引入
          FormDemo: () => import('../BaseUse/FormDemo') // import 一个函数,属于异步组件的引入方式
    },
    data() {
        return {
            showFormDemo: false
        }
    }
}
</script>

六,vue如何缓存组件 - keep-alive

缓存组件
场景:频繁切换,不需要重复渲染
出现地方之一: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>

// 使用了keep-alive之后的情况:
     打印结果: A mounted->B mounted ->C mounted

// 不使用 keep-alive的情况
     打印结果: A mounted -> A destory
                      B mounted -> B destory
                        C mounted -> C destory
// KeepAliveStageA

<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

<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

<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>

重点:
使用了 keep-alive 之后,组件在进行切换的时候,上一个组件就不会被销毁,下一次点击的时候就不需要重新渲染一遍这个组件,提高了性能。

七,mixin

  • 多个组件有相同的逻辑,抽离出来
  • mixin 并不是完美解决方案,会有一些问题
  • Vue 3 提出的 Composition API 旨在解决这些问题

min 示例讲解:

// 主入口文件 index.vue
// 引入对应的 MixinDemo 组件

<template>
    <div>
        <MixinDemo/>
    </div>
</template>

<script>
import MixinDemo from './MixinDemo'

export default {
    components: {
        MixinDemo
    },
    data() {
        return {
        }
    }
}
</script>
// MixinDemo.vue 文件
// 通过 mixins: [myMixin] 的方式来引入 myMixin 组件里面抽离出来的一些公共属性和公共方法,以便可以在此使用
// city 属性就是 myMixin 里面的内容

<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() {
        console.log('component mounted', this.name)
    }
}
</script>
// mixin.js 
// 用来抽离出一些公共的属性和方法,以便可以在其他组件里面使用这些对应的公共内容,避免在其他文件里面写重复的
     代码
// 使用了 export 的方法,导出对应的内容(data,methods, 生命周期函数等)

<script>
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)
    }
}
</script>

mixin 的问题

  1. 变量来源不明确,不利于阅读(代码可读性差)
  2. 多 mixin 可能会造成命名冲突(不同 mixin 混合可能会出现变量覆盖,函数同名,功能错误等问题)
  3. mixin 和组件可能会出现多对多的关系,复杂度较高

八,相关的面试技巧

  1. 可以不太深入,但是必须知道
  2. 熟悉基本用法,了解使用场景
  3. 最好能和自己的项目经验结合起来