一、vue3有了解过吗?能说说跟vue2的区别吗?
1. Vue3介绍
关于vue3的重构背景,尤大是这样说的:
「Vue 新版本的理念成型于 2018 年末,当时 Vue 2 的代码库已经有两岁半了。比起通用软件的生命周期来这好像也没那么久,但在这段时期,前端世界已经今昔非比了
在我们更新(和重写)Vue 的主要版本时,主要考虑两点因素:首先是新的 JavaScript 语言特性在主流浏览器中的受支持水平;其次是当前代码库中随时间推移而逐渐暴露出来的一些设计和架构问题」
简要就是:
- 利用新的语言特性(es6)
- 解决架构问题
哪些变化:
- 速度更快
- 体积减少
- 更易维护
- 更接近原生
- 更易使用
⑴. 速度更快
vue3相比vue2
- 重写了虚拟Dom实现
- 编译模板的优化
- 更高效的组件初始化
- undate性能提高1.3~2倍
- SSR速度提高了2~3倍
⑵. 体积更小
通过webpack的tree-shaking功能,可以将无用模块“剪辑”,仅打包需要的
能够tree-shaking,有两大好处:
- 对开发人员,能够对vue实现更多其他的功能,而不必担忧整体体积过大
- 对使用者,打包出来的包体积变小了
vue可以开发出更多其他的功能,而不必担忧vue打包出来的整体体积过多
⑶. 更易维护
compositon Api
- 可与现有的Options API一起使用
- 灵活的逻辑组合与复用
- Vue3模块可以和其他框架搭配使用
更好的Typescript支持
VUE3是基于typescipt编写的,可以享受到自动的类型定义提示
编译器重写
⑷. 更接近原生
可以自定义渲染 API
⑸. 更易使用
- 响应式 Api 暴露出来
- 轻松识别组件重新渲染原因
2. Vue3新增特性
Vue 3 中需要关注的一些新功能包括:
- framents
- Teleport
- composition Api
- createRenderer
⑴. framents
在 Vue3.x 中,组件现在支持有多个根节点
<!-- Layout.vue -->
<template>
<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>
</template>
⑵. Teleport
Teleport 是一种能够将我们的模板移动到 DOM 中 Vue app 之外的其他位置的技术,就有点像哆啦A梦的“任意门”
在vue2中,像 modals,toast 等这样的元素,如果我们嵌套在 Vue 的某个组件内部,那么处理嵌套组件的定位、z-index 和样式就会变得很困难
通过Teleport,我们可以在组件的逻辑位置写模板代码,然后在 Vue 应用范围之外渲染它
<button @click="showToast" class="btn">打开 toast</button>
<!-- to 属性就是目标位置 -->
<teleport to="#teleport-target">
<div v-if="visible" class="toast-wrap">
<div class="toast-msg">我是一个 Toast 文案</div>
</div>
</teleport>
⑶. createRenderer
通过createRenderer,我们能够构建自定义渲染器,我们能够将 vue 的开发模型扩展到其他平台
我们可以将其生成在canvas画布上
关于createRenderer,我们了解下基本使用,就不展开讲述了
import { createRenderer } from '@vue/runtime-core'
const { render, createApp } = createRenderer({
patchProp,
insert,
remove,
createElement,
// ...
})
export { render, createApp }
export * from '@vue/runtime-core'
⑷. composition Api
composition Api,也就是组合式api,通过这种形式,我们能够更加容易维护我们的代码,将相同功能的变量进行一个集中式的管理
关于compositon api的使用,这里以下图展开
简单使用:
export default {
setup() {
const count = ref(0)
const double = computed(() => count.value * 2)
function increment() {
count.value++
}
onMounted(() => console.log('component mounted!'))
return {
count,
double,
increment
}
}
}
3. 非兼容变更
⑴. Global API
- 全局 Vue API 已更改为使用应用程序实例
- 全局和内部 API 已经被重构为可 tree-shakable
⑵. 模板指令
- 组件上 v-model 用法已更改
- 和 非 v-for节点上key用法已更改
- 在同一元素上使用的 v-if 和 v-for 优先级已更改
- v-bind=“object” 现在排序敏感
- v-for 中的 ref 不再注册 ref 数组
⑶. 组件
- 只能使用普通函数创建功能组件
- functional 属性在单文件组件 (SFC)
- 异步组件现在需要 defineAsyncComponent 方法来创建
⑷. 渲染函数
- 渲染函数API改变
- $scopedSlots property 已删除,所有插槽都通过 $slots 作为函数暴露
- 自定义指令 API 已更改为与组件生命周期一致
- 一些转换 class 被重命名了:
- v-enter -> v-enter-from
- v-leave -> v-leave-from
- 组件 watch 选项和实例方法 $watch不再支持点分隔字符串路径,请改用计算函数作为参数
- 在 Vue 2.x 中,应用根容器的 outerHTML 将替换为根组件模板 (如果根组件没有模板/渲染选项,则最终编译为模板)。VUE3.x 现在使用应用程序容器的 innerHTML。
⑸. 其他小改变
- destroyed 生命周期选项被重命名为 unmounted
- beforeDestroy 生命周期选项被重命名为 beforeUnmount
- [prop default工厂函数不再有权访问 this 是上下文
- 自定义指令 API 已更改为与组件生命周期一致
- data 应始终声明为函数
- 来自 mixin 的 data 选项现在可简单地合并
- attribute 强制策略已更改
- 一些过渡 class 被重命名
- 组建 watch 选项和实例方法 $watch不再支持以点分隔的字符串路径。请改用计算属性函数作为参数。
- 没有特殊指令的标记 (v-if/else-if/else、v-for 或 v-slot) 现在被视为普通元素,并将生成原生的 元素,而不是渲染其内部内容。
- 在Vue 2.x 中,应用根容器的 outerHTML 将替换为根组件模板 (如果根组件没有模板/渲染选项,则最终编译为模板)。Vue 3.x 现在使用应用容器的 innerHTML,这意味着容器本身不再被视为模板的一部分。
⑹. 移除 API
- keyCode 支持作为 v-on 的修饰符
- o n , on, on,off和$once 实例方法
- 过滤filter
- 内联模板 attribute
- $destroy 实例方法。用户不应再手动管理单个Vue 组件的生命周期。
二、Vue3.0的设计目标是什么?做了哪些优化
1. 设计目标
不以解决实际业务痛点的更新都是耍流氓,下面我们来列举一下Vue3之前我们或许会面临的问题
- 随着功能的增长,复杂组件的代码变得越来越难以维护
- 缺少一种比较「干净」的在多个组件之间提取和复用逻辑的机制
- 类型推断不够友好
- bundle的时间太久了
而 Vue3 经过长达两三年时间的筹备,做了哪些事情?
我们从结果反推
- 更小
- 更快
- TypeScript支持
- API设计一致性
- 提高自身可维护性
- 开放更多底层功能
⑴. 更小
Vue3移除一些不常用的 API
引入tree-shaking,可以将无用模块“剪辑”,仅打包需要的,使打包的整体体积变小了
⑵. 更快
主要体现在编译方面:
- diff算法优化
- 静态提升
- 事件监听缓存
- SSR优化
⑶. 更友好
vue3在兼顾vue2的options API的同时还推出了composition API,大大增加了代码的逻辑组织和代码复用能力
2. 优化方案
vue3从很多层面都做了优化,可以分成三个方面:
- 源码
- 性能
- 语法 API
⑴. 源码
源码可以从两个层面展开:
- 源码管理
- TypeScript
⑵. 性能
vue3是从什么哪些方面对性能进行进一步优化呢?
- 体积优化
- 编译优化
- 数据劫持优化
⑶. 语法 API
这里当然说的就是composition API,其两大显著的优化:
- 优化逻辑组织
- 优化逻辑复用
三、Vue3.0性能提升主要是通过哪几方面体现的?
1. 编译阶段
回顾Vue2,我们知道每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把用到的数据property记录为依赖,当依赖发生改变,触发setter,则会通知watcher,从而使关联的组件重新渲染
因此,Vue3在编译阶段,做了进一步优化。主要有如下:
diff算法优化
静态提升
事件监听缓存
SSR优化
⑴. diff算法优化
vue3在diff算法中相比vue2增加了静态标记
关于这个静态标记,其作用是为了会发生变化的地方添加一个flag标记,下次发生变化的时候直接找该地方进行比较
⑵. 静态提升
Vue3中对不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用
这样就免去了重复的创建节点,大型应用会受益于这个改动,免去了重复的创建操作,优化了运行时候的内存占用
⑵. 事件监听缓存
默认情况下绑定事件@click
行为会被视为动态绑定,所以每次都会去追踪它的变化
⑷. SSR优化
当静态内容大到一定量级时候,会用createStaticVNode方法在客户端去生成一个static node,这些静态node,会被直接innerHtml,就不需要创建对象,然后根据对象渲染
2. 源码体积
相比Vue2,Vue3整体体积变小了,除了移出一些不常用的API,再重要的是Tree shanking
任何一个函数,如ref、reavtived、computed等,仅仅在用到的时候才打包,没用到的模块都被摇掉,打包的整体体积变小
3. 响应式系统
vue2中采用 defineProperty来劫持整个对象,然后进行深度遍历所有属性,给每个属性添加getter和setter,实现响应式
vue3采用proxy重写了响应式系统,因为proxy可以对整个对象进行监听,所以不需要深度遍历
- 可以监听动态属性的添加
- 可以监听到数组的索引和数组length属性
- 可以监听删除属性
四、Vue3.0里为什么要用 Proxy API 替代 defineProperty API ?
1. Object.defineProperty
定义:Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象
为什么能实现响应式:
通过defineProperty 两个属性,get及set
get:
属性的 getter 函数,当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入 this 对象(由于继承关系,这里的this并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值
set:
属性的 setter 函数,当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this 对象。默认为 undefined
小结:
- 检测不到对象属性的添加和删除
- 数组API方法无法监听到
- 需要对每个属性进行遍历监听,如果嵌套对象,需要深层监听,造成性能问题
2. proxy
Proxy的监听是针对一个对象的,那么对这个对象的所有操作会进入监听操作,这就完全可以代理所有属性了
(在 ES6 面试题会会详细讲解 Proxy 的功能…)
3. 总结
- Object.defineProperty只能遍历对象属性进行劫持
- Proxy直接可以劫持整个对象,并返回一个新对象,我们可以只操作新的对象达到响应式目的
五、Vue3.0 所采用的 Composition Api 与 Vue2.x 使用的 Options Api 有什么不同?
1. 开始之前
Composition API 可以说是Vue3的最大特点,那么为什么要推出Composition Api,解决了什么问题?
通常使用Vue2开发的项目,普遍会存在以下问题:
- 代码的可读性随着组件变大而变差
- 每一种代码复用的方式,都存在缺点
- TypeScript支持有限
以上通过使用Composition Api都能迎刃而解
2. Options Api
Options API,即大家常说的选项API,即以vue为后缀的文件,通过定义methods,computed,watch,data等属性与方法,共同处理页面逻辑
用组件的选项 (data、computed、methods、watch) 组织逻辑在大多数情况下都有效;然而,当组件变得复杂,导致对应属性的列表也会增长,这可能会导致组件难以阅读和理解
3. Composition Api
在 Vue3 Composition API 中,组件根据逻辑功能来组织的,一个功能所定义的所有 API 会放在一起(更加的高内聚,低耦合)
即使项目很大,功能很多,我们都能快速的定位到这个功能所用到的所有 API
4. 对比
面对Composition Api与Options Api进行两大方面的比较
逻辑组织
逻辑复用
⑴. 逻辑组织
Options API
假设一个组件是一个大型组件,其内部有很多处理逻辑关注点,这种碎片化使得理解和维护复杂组件变得困难
选项的分离掩盖了潜在的逻辑问题。此外,在处理单个逻辑关注点时,我们必须不断地“跳转”相关代码的选项块
Compostion API
而Compositon API正是解决上述问题,将某个逻辑关注点相关的代码全都放在一个函数里,这样当需要修改一个功能时,就不再需要在文件中跳来跳去
再来一张图进行对比,可以很直观地感受到 Composition API在逻辑组织方面的优势,以后修改一个属性功能的时候,只需要跳到控制该属性的方法中即可
⑵. 逻辑复用
在Vue2中,我们是用过mixin去复用相同的逻辑
下面举个例子,我们会另起一个mixin.js文件
export const MoveMixin = {
data() {
return {
x: 0,
y: 0,
};
},
methods: {
handleKeyup(e) {
console.log(e.code);
// 上下左右 x y
switch (e.code) {
case "ArrowUp":
this.y--;
break;
case "ArrowDown":
this.y++;
break;
case "ArrowLeft":
this.x--;
break;
case "ArrowRight":
this.x++;
break;
}
},
},
mounted() {
window.addEventListener("keyup", this.handleKeyup);
},
unmounted() {
window.removeEventListener("keyup", this.handleKeyup);
},
};
然后在组件中使用
<template>
<div>
Mouse position: x {{ x }} / y {{ y }}
</div>
</template>
<script>
import mousePositionMixin from './mouse'
export default {
mixins: [mousePositionMixin]
}
</script>
使用单个mixin似乎问题不大,但是当我们一个组件混入大量不同的 mixins 的时候
mixins: [mousePositionMixin, fooMixin, barMixin, otherMixin]
会存在两个非常明显的问题:
- 命名冲突
- 数据来源不清晰
现在通过Compositon API这种方式改写上面的代码
import { onMounted, onUnmounted, reactive } from "vue";
export function useMove() {
const position = reactive({
x: 0,
y: 0,
});
const handleKeyup = (e) => {
console.log(e.code);
// 上下左右 x y
switch (e.code) {
case "ArrowUp":
// y.value--;
position.y--;
break;
case "ArrowDown":
// y.value++;
position.y++;
break;
case "ArrowLeft":
// x.value--;
position.x--;
break;
case "ArrowRight":
// x.value++;
position.x++;
break;
}
};
onMounted(() => {
window.addEventListener("keyup", handleKeyup);
});
onUnmounted(() => {
window.removeEventListener("keyup", handleKeyup);
});
return { position };
}
可以看到,整个数据来源清晰了,即使去编写更多的 hook 函数,也不会出现命名冲突的问题
小结:
- 在逻辑组织和逻辑复用方面,Composition API是优于Options API
- 因为Composition API几乎是函数,会有更好的类型推断。
- Composition API对 tree-shaking 友好,代码也更容易压缩
- Composition API中见不到this的使用,减少了this指向不明的情况
- 如果是小型组件,可以继续使用Options API,也是十分友好的
六、说说Vue 3.0中Treeshaking特性?举例说明一下?
1. 是什么
Tree shaking 是一种通过清除多余代码方式来优化项目打包体积的技术,专业术语叫 Dead code elimination
简单来讲,就是在保持代码运行结果不变的前提下,去除无用的代码
在Vue2中,无论我们使用什么功能,它们最终都会出现在生产代码中。主要原因是Vue实例在项目中是单例的,捆绑程序无法检测到该对象的哪些属性在代码中被使用到
而Vue3源码引入tree shaking特性,将全局 API 进行分块。如果您不使用其某些功能,它们将不会包含在您的基础包中
2. 如何做
Tree shaking是基于ES6模板语法(import与exports),主要是借助ES6模块的静态编译思想,在编译时就能确定模块的依赖关系,以及输入和输出的变量
Tree shaking无非就是做了两件事:
- 编译阶段利用ES6 Module判断哪些模块已经加载
- 判断那些模块和变量未被使用或者引用,进而删除对应代码
3. 作用
通过Tree shaking,Vue3给我们带来的好处是:
- 减少程序体积(更小)
- 减少程序执行时间(更快)
- 便于将来对程序架构进行优化(更友好)
七、用Vue3.0 写过组件吗?如果想实现一个 Modal你会怎么设计?
1. 组件设计
组件就是把图形、非图形的各种逻辑均抽象为一个统一的概念(组件)来实现开发的模式
现在有一个场景,点击新增与编辑都弹框出来进行填写,功能上大同小异,可能只是标题内容或者是显示的主体内容稍微不同
这时候就没必要写两个组件,只需要根据传入的参数不同,组件显示不同内容即可
这样,下次开发相同界面程序时就可以写更少的代码,意义着更高的开发效率,更少的 Bug和更少的程序体积
2. 需求分析
实现一个Modal组件,首先确定需要完成的内容:
- 遮罩层
- 标题内容
- 主体内容
- 确定和取消按钮
主体内容需要灵活,所以可以是字符串,也可以是一段 html 代码
特点是它们在当前vue实例之外独立存在,通常挂载于body之上
除了通过引入import的形式,我们还可通过API的形式进行组件的调用
还可以包括配置全局样式、国际化、与typeScript结合
3. 实现流程
首先看看大致流程:
- 目录结构
- 组件内容
- 实现 API 形式
- 事件处理
- 其他完善
其他完善:
关于组件实现国际化、与typsScript结合,大家可以根据自身情况在此基础上进行更改
未完待续…
更多推荐
Vue3 面试题
发布评论