专栏首页小码农学习笔记Vue 面试常考高级用法特性汇总
原创

Vue 面试常考高级用法特性汇总

高级用法特性

自定义 v-model

使用场景:在自定义的组件中,v-model 默认会利用名为 value 的 prop 和名为 input 的事件。但是像单选框、复选框等类型的输入控件,它们的 value 属性有其它用途。此时 model 选项可以用来避免这样的冲突。

下面演示一个例子(改自官方文档):

编写一个自定义的复选框组件 BaseCheckbox.vue

<template>
  <input type="checkbox" :checked="checked" @change="$emit('change', $event.target.checked)">
  <!--
      1. 上面的 input 使用了 v-bind 而不是 v-model
      2. "checked" 对应上 props 里的 checked
      3. @change 时间里的 'change' 和 model.event 里的 'change' 要对应起来,名字可以自定义
  -->
</template>

<script>
export default {
  model: {
    prop: 'checked', // 对应 props 中的 checked(名字两者可以自定义,但要前后一致)
    event: 'change'
  },
  props: {
    checked: Boolean,
    default() {
      return false
    }
  }
}
</script>

在父组件上使用 v-model 的时候:

<template>
  <h1>我是父组件</h1>
  <base-checkbox v-model="lovingVue"></base-checkbox>
</template>

<script>
import BaseCheckbox from './BaseCheckbox'

export default {
  components: {
    BaseCheckbox
  },
  data() {
    return {
      lovingVue: false
    }
  }
}
</script>

这里的 lovingVue 的值将会传入这个名为 checked 的 prop。同时当 <base-checkbox> 触发一个 change 事件并附带一个新的值的时候,这个 lovingVue 的值将会被更新。

$nextTick

Vue 是异步渲染,data 改变之后,DOM 不会立刻渲染。$nextTick 会在 DOM 渲染之后被触发,以获取最新 DOM 节点。

如下代码所示,如果不使用 $nextTick,那每次打印出来的数组长度是上一次渲染后的数组长度,而不是执行添加后的最新结果。

<template>
  <div id="app">
    <ul ref="itemList">
      <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()}`)

      // 1. 异步渲染,$nextTick 待 DOM 渲染完再回调
      // 2. 页面渲染时会将 data 的修改做整合,多次 data 修改只会渲染一次
      this.$nextTick(() => {
        // 获取 DOM 元素
        const ulElem = this.$refs.itemList
        console.log(ulElem.childNodes.length)
      })
    }
  }
}
</script>

总结一下 $nextTick 的特点:

  • 汇总 data 的修改,一次性更新视图
  • 能够减少 DOM 操作次数,提高性能

slot 插槽

基本使用

slot 的基本用法是在父组件中往子组件插入一段内容(不一定只是字符串),示例如下:

父组件引入了一个名为 SlotDemo.vue 的子组件,向其传入一个动态属性 url 以及一个类似子节点的 website.title

<template>
  <div>
    <h1>我是父组件</h1>
    <slot-demo :url="website.url">
      {{ website.title }}
    </slot-demo>
  </div>
</template>

<script>
import SlotDemo from './SlotDemo'

export default {
  components: {
    SlotDemo,
  },
  data() {
    return {
      website: {
        url: 'https://fedbook.cn/',
        title: '前端修炼小册'
      },
    }
  }
}
</script>

子组件除了在 props 中接收父组件传进来的 url,还多了一个 <slot></slot> 标签,它接收父组件中写的子节点 website.title 中的内容。

<template>
  <a :href="url">
    <slot>
      默认内容,即父组件没设置内容时,显示这句话
    </slot>
  </a>
</template>

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

作用域插槽

有时我们需要让插槽内容能够访问子组件中才有的数据,即在父组件中获取子组件 data 里的值,就需要用到作用域插槽,示例如下:

首先在子组件的 data 中定义一个 website 对象,再给 <slot></slot> 标签定义一个动态属性 slotData (名字可自定义)并赋值为 website

<template>
  <a :href="url">
    <slot :slotData="website">
      {{ website.subTitle }} <!-- 默认值显示 subTitle ,即父组件不传内容时 -->
    </slot>
  </a>
</template>

<script>
export default {
    props: ['url'],
    data() {
      return {
        website: {
          url: 'https://cn.vuejs.org/',
          title: 'Vue.js'
        }
      }
    }
}
</script>

父组件调用子组件时,现在增加了一个 <template> 标签,给它设置一个 v-slot 属性且值为 slotProps(名字可自定义)。

在新增的 <template> 标签中进行插值,例如需要获取子组件 data 中的 website.title,写法就是 slotProps.slotData.titleslotData 对应的就是子组件的 website)。

此时页面显示的就是子组件中的 title 值:

<template>
  <div>
    <h1>我是父组件</h1>
    <scoped-slot-demo :url="website.url">
      <template v-slot="slotProps">
        {{ slotProps.slotData.title }}
      </template>
    </scoped-slot-demo>
  </div>
</template>

<script>
import ScopedSlotDemo from './ScopedSlotDemo'

export default {
  components: {
    ScopedSlotDemo,
  },
  data() {
    return {
      website: {
        url: 'https://fedbook.cn/',
        title: '前端修炼小册'
      }
    }
  }
}
</script>

具名插槽

具名插槽用于子组件中有多个 slot 的场景,父组件往子组件传值时需要对应上名字,示例如下:

子组件中每个 <slot> 元素有一个特殊的 name 属性,如果不指定 name 属性则默认是 "default":

<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

父组件中,在向具名插槽提供内容的时候,我们可以在一个 <template> 元素上使用 v-slot 指令,该指令的值就要对应上子组件中 <slot> 元素的 name

任何没有被包裹在带有 v-slot<template> 中的内容都会被视为默认插槽的内容。

<template>
  <div>
    <h1>我是父组件</h1>
    <named-slot-demo>
      <template v-slot:header>
        <h1>将插入 header slot 中</h1>
      </template>
        
      <p>将插入 main slot 中,即未命名的 slot</p>
      <p>也将插入 main slot 中</p>
        
      <template v-slot:footer>
        <p>将插入 footer slot 中</p>
      </template>
    </named-slot-demo>
  </div>
</template>

<script>
import NamedSlotDemo from './NamedSlotDemo'

export default {
  components: {
    NamedSlotDemo,
  },
  data() {
    return {}
  }
}
</script>

动态组件

动态组件用于在某个区域切换显示不同组件的场景,可以通过 Vue 的 <component> 元素加一个特殊的 is 属性来实现。

代码示例:

<component :is="componentName"></component>

其中,componentName 传入需要显示的组件名。

异步组件

同步加载的组件在打包的时候只会打成一个包,如果体积过大,会导致每次进入页面初始化的时候需要加载很大的一个文件:

<script>
import FormDemo1 from './FormDemo1'
import FormDemo2 from './FormDemo2'

export default {
  component: {
    FormDemo1,
    FormDemo2
  }
}
</script>

异步加载组件是通过 import() 函数来引入组件,可以实现按需加载/异步加载大组件,从而提升网页加载性能。

<script>
export default {
  component: {
    FormDemo: () => import('./FormDemo')
  }
}
</script>

缓存组件

缓存组件的意思是在频繁切换页面时(例如 Tab 切换),不需要重复渲染,常用于 Vue 的性能优化。

通过 keep-alive 来实现缓存组件,示例如下:

<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 和 v-show 的区别

  • 控制层级不同,v-show 是通过原生的 CSS(display);keep-alive 是在 Vue 层级进行的 JS 对象的渲染
  • 实际使用中,简单的标签可以使用 v-show,复杂的组件结构(例如 Tab 切换)使用 keep-alive。

keep-alive 和 v-if 的区别

  • v-if 也是 Vue 本身机制控制的,但会销毁和重新渲染组件,也就是会频繁触发组件的 mounted 和 destroyed;而 keep-alive 的方式,Vue 会把组件缓存,不需要走渲染流程。

mixin

mixin 用于将多个组件的相同逻辑抽离出来,可以避免重复编写相同代码。

但 mixin 并不是完美的解决方案,会有一些问题:

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

Vue3 提出的 Composition API 旨在解决这些问题。


文章持续更新,本文 GitHub 前端修炼小册 已经收录,欢迎 Star。如对文章内容有不同见解,欢迎留言交流。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Vue 面试常考基础用法总结

    v-for 支持遍历数组和对象,其中 key 很重要,但不能乱写(如 random 或者 index),尽量要写和业务有关联的信息(比如 id)。

    文渊同学
  • 前端文章收藏

    前端GoGoGo
  • 个人分享--web前端学习资源分享

    6月份开始出没社区,现在差不多9月了,按照工作的说法,就是差不多过了三个月的试用期,准备转正了!一般来说,差不多到了转正的时候,会进行总结或者分享会议!那么今天...

    守候i
  • 基于react/vue生态的前端集成解决方案探索与总结

    接下来我将介绍项目的基本架构和设计思路,并使用shell脚本来实现自动化安装技术集成方案。最后会在文章的末尾附上github地址,感兴趣的朋友可以研究参考,也可...

    徐小夕
  • 驳《前端常见的Vue面试题目汇总》

    本文针对 @小明同学哟 的 《前端常见的Vue面试题目汇总》 这篇文章,提出一些错误。

    ssh_晨曦时梦见兮
  • 【干货】前端自学之路(持续更新)

    《JavaScript Dom编程艺术》 《JavaScript面向对象编程指南(基础)》 《JavaScript高级程序设计》 《高性能JavaScri...

    Ewall
  • 一年半前端人总结的大厂高频面经(附学习资源)

    作者:俊劫 https://juejin.cn/post/6942988170208215076

    用户4456933
  • 教你用200行代码写一个爱豆拼拼乐H5小游戏(附源码)

    零零总总花了半天的时间,希望对自己后面涉及H5游戏有所帮助,也希望大家通过这篇文章有所收获。

    徐小夕
  • 「前端架构」React和Vue -CTO的选择正确框架的指南

    快速总结:为项目选择正确的javascript框架或库是CTO和项目经理的基本任务。然而,选择的范围很大程度上取决于几个因素,如项目时间、学习曲线、框架性能和团...

    首席架构师智库
  • Vue 2.x折腾记 - (20) JSX在业务中的具体实践以及跟React书写的差异化

    Vue的jsx,能够支持部分vue独有的特性,比如拿到computed, 指令及自定义事件;

    CRPER
  • 2019 前端面试题汇总(主要为 Vue)

    毕业之后就在一直合肥小公司工作,没有老司机、没有技术氛围,在技术的道路上我只能独自摸索。老板也只会画饼充饥,前途一片迷茫看不到任何希望。于是乎,我果断辞职,在新...

    Fundebug
  • 轻量级工具Vite到底牛在哪, 一文全知道

    时下大热的vue框架又来了新开发环境构建工具——Vite,今天我们一起来了解一下这个新成员。

    葡萄城控件
  • 为什么43%前端开发者想学Vue.js

    根据JavaScript 2017前端库状况调查 Vue.js是开发者最想学的前端库。我在这里说明一下我为什么认为这也是和你一起通过使用Vue构建一个简单的Ap...

    笔阁
  • 史上最全的前端资源大汇总

    Tanyboye
  • 最全前端资源汇总

    一、概要 这份汇总整理,很程度上参考了GitHub最全前端资源汇总;云集前端教程、开发资源、免费书籍、手册规范、求职面试等等,旨在为前端学习 & 技能提升提供方...

    张果
  • 19年BAT常问面试题汇总:JVM+微服务+多线程+锁+高并发性能

    1、什么是 Redis?. 2、Redis 的数据类型? 3、使用 Redis 有哪些好处? 4、Redis 相比 Memcached 有哪些优势? 5、Mem...

    路人甲Java
  • 19年BAT常问面试题汇总:JVM+微服务+多线程+锁+高并发性能

    34、一个 Redis 实例最多能存放多少的 keys?List、Set、Sorted Set他们最多能存放多少元素?

    美的让人心动
  • 19年BAT常问面试题汇总:JVM+微服务+多线程+锁+高并发性能

    1、什么是 Redis?. 2、Redis 的数据类型? 3、使用 Redis 有哪些好处? 4、Redis 相比 Memcached 有哪些优势? 5、Mem...

    xjjdog
  • 009 | 快速入门Web前端开发的正确姿势

    入门标准很简单,就一条:达到能参与 Web 前端实际项目的开发水平。请注意,是实际项目,这就需要了解如今的实际项目开发都用了哪些技术栈。HTML/CSS/Jav...

    Keegan小钢

扫码关注云+社区

领取腾讯云代金券