Vue3上手指南
wen 2022/8/31 Vue
# 一、前言
本文主要聊的一些东西:
- Vue3 的全新特性;
- Vue2 和 Vue3 的一些区别;
- Vue2 开发者如何快速上手 Vue3。
# 二、Vue2 vs Vue3
# 1. 新特征概括
- RFC 机制;
- 全新的响应式系统;
- Composition API;
- 全部模块使用 TypeScript 重构;
- 新的组件;
- 源码管理。
# 2. RFC 机制
- RFC 是什么:和代码无关,是 Vue 团队开发的工作方式。关于 Vue 的新语法或者新功能的讨论,都会先在 GitHub 上公开征求意见,邀请社区所有的人一起讨论。
- GitHub 链接:https://github.com/vuejs/rfcs (opens new window)。
- 这个改变让 Vue 社区更加有活力。
# 3. 全新的响应式系统
- 总的来说就是使用 Proxy 代替 Object.defineProperty。
# 3.1 Object.defineProperty
- 使用 get、set 来进行数据劫持,修改,从而实现响应式。
- 由于 get、set 方式,所以只能捕获到属性读取和修改操作,当新增、删除属性时,捕获不到,导致界面也不会更新。
- 直接通过下标修改数组,界面也不会自动更新。
# 3.2 Proxy
- Proxy 是什么,是代理,同时还有个 Window 上的内置对象 Reflect(反射)。
- 通过 Proxy(代理):拦截对象中任意属性的变化,包括:属性值的读写、属性的添加、属性的删除等。
- 通过 Reflect(反射):对源对象的属性进行操作。
const p = new Proxy(data, {
// 读取属性时调用
get(target, propName) {
return Reflect.get(target, propName);
},
// 修改属性或添加属性时调用
set(target, propName, value) {
return Reflect.set(target, propName, value);
},
// 删除属性时调用
deleteProperty(target, propName) {
return Reflect.deleteProperty(target, propName);
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 4. Composition API
- Vue3 的组件可以按两种不同的风格书写:选项式 API (Options API) 和组合式 API (Composition API)。
- 什么是组合式 API?- Vue 官方 (opens new window)。
# 4.1 简单来说
- 不会再看见满屏的 this 了;
- 解决了过去组件过长时,Options Api 带来的难以维护的问题;
- 逻辑可以整块复用;
- 所有 API 都是 import 引入的,对 Tree- shaking 很友好,没用到功能,打包的时候会被清理掉,减小包的大小。
# 4.2 setup
- 新的 setup 选项在组件被创建之前执行,一旦 props 被解析完成,它就将被作为组合式 API 的入口。
- 可以当做 Vue2 的 beforeCreate 和 created 生命周期用。
- 可直接写 await 语法。
- SFC 单文件组件中直接使用
<script lang="ts" setup>
即可,或者也可以结合export default
使用。
<script lang="ts" setup>
const result = await Http.get('/getUserInfo')
</script>
// or
export default {
setup(props, context) {
// Attribute (非响应式对象,等同于 $attrs)
console.log(context.attrs)
// 插槽 (非响应式对象,等同于 $slots)
console.log(context.slots)
// 触发事件 (方法,等同于 $emit)
console.log(context.emit)
// 暴露公共 property (函数)
console.log(context.expose)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 4.3 ref
- ref 用来创建基本数据类型的响应式数据。
- template 中默认直接调用响应式数据显示数据,script 中需要使用
.value
调用。 - 和 react ref 差不多,react 是
.current
获取值,vue3 是.value
。 - ref 的本质是通过 reactive 创建的,
ref(10) => reactive({ value: 10 })
。究其原因,还是基本数据类型无法拦截它的变化。
# 4.4 reactive
- reactive 用来创建引用数据类型的响应式数据。
- reactive 的本质是将每一层的数据都解析成
proxy对象
。 - 直接解构,响应性会丢失,需要用
toRefs
包裹。引用类型直接改变引用地址也会导致响应式丢失。
import { reactive, toRefs } from 'vue'
const book = reactive({
author: 'Vue Team',
title: 'Vue 3 Guide',
description: 'You are reading this book right now ;)',
})
let { author, title } = toRefs(book)
title.value = 'Vue 3 Detailed Guide' // 使用 .value 作为标题,现在是 ref
console.log(book.title) // 'Vue 3 Detailed Guide'
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 4.5 生命周期
- 整体区别不大,把 setup 当 created 用,其它的生命周期就当改了个名。
<script setup lang="ts">
import { onMounted } from 'vue';
const getUserInfo = () => {
console.log('the component is now mounted.');
};
onMounted(getUserInfo);
</script>
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
Vue2 | Vue3 |
---|---|
beforeCreate | Not needed* |
created | Not needed* |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
# 4.6 watch & watchEffect
- watch:功能和 vue2 一致、watch(监听参数,变化回调,配置参数)。
import { ref, reactive, watch } from 'vue'
const counter1 = ref(0)
const counter2 = ref(0)
// 监听多个
watch([counter1, counter2], (newValue, oldValue) => {
console.log('The new counter1 value is: ' + counter1.value)
console.log('The new counter2 value is: ' + counter2.value)
})
const obj = reactive({
name: 'Wen',
age: 23
})
// 深度监听对象
watch(obj, (newValue, oldValue) => {
console.log('The new obj value is: ' + obj.value)
}, {
deep: true,
immediate: true
})
// watch监听单个属性
watch(() => obj.name, (newValue, oldValue) => {
console.log('The new obj value is: ' + obj.value)
}, {
deep: true,
immediate: true
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
- watchEffect:类似 React -- useEffect,但是不需要写依赖项,只要我们回调中引用了响应式的属性。
- watch & watchEffect 的区别:【1】同一个功能的两种不同形态,底层的实现是一样的;【2】watch 可以获取到新值与旧值(更新前的值),而 watchEffect 是拿不到的;【3】watch 显式指定依赖源,watchEffect 自动收集依赖源;【4】可以把 watchEffect 理解为是配置了{ immediate: true } 的 watch。
- 使用场景:推荐在大部分时候用 watch 显式的指定依赖以避免不必要的重复触发。watchEffect 适用于一些逻辑相对简单,依赖源和逻辑强相关的场景(或者懒惰的场景 )。
# 4.7 computed
- 作用和 vue2 无差异;更加灵活,可以在定义响应式变量时声明。
import { ref, computed } from 'vue'
const counter = ref(0)
const twiceTheCounter = computed(() => counter.value * 2)
// get set 写法
const plusOne = computed({
get: () => counter.value + 1,
set: (val) => {
counter.value = val - 1
},
})
// set
plusOne.value = 1
console.log(counter.value) // 0
// get
counter.value++
console.log(counter.value) // 1
console.log(twiceTheCounter.value) // 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 5. 全部模块使用 TypeScript 重构
- 原因:从开发维护的角度看,Vue 2 是使用 Flow.js 来做类型校验。但现在 Flow.js 已经停止维护了,整个社区都在全面使用 TypeScript 来构建基础库,Vue 团队也不例外。
- 好处:类型系统带来了更方便的提示,并且让我们的代码能够更健壮。
# 6. 新的组件
# 6.1 Fragment
- 这个更像是一种概念,它的意思就相当于创建页面时,给了一个虚拟根标签
VNode
,因为我们知道在 Vue2 里面,我们是有根标签这个概念的。但是到来 Vue3,它是自动给你创建个虚拟根标签VNode
(Fragment
),所以可以不要根标签。 - 好处就是减少标签层级,减小内存占用。
<template>
<div>
Vue2
</div>
</template>
<template>
Vue3
</template>
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 6.2 Teleport
- Teleport 是一种能够将我们的模板渲染至指定 DOM 节点,不受父级 style、v-show 等属性影响,但 data、props 数据依旧能够共用的技术。
- 主要解决的问题:其实就是可以不考虑你写在什么位置,Teleport 节点挂载在其他指定的 DOM 节点下,完全不受父级 style 样式影响。
// body
<Teleport to="body">
<Loading />
</Teleport>
// id 定位
<teleport to="#app">
<Loading />
</teleport>
// class 定位
<teleport to=".app">
<Loading />
</teleport>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 6.3 Suspense
- 是异步组件,更方便开发有异步请求的组件。大家都知道在渲染组件之前进行一些异步请求是很常见的事,Suspense 组件提供了一个方案,允许将等待过程提升到组件树中处理,而不是在单个组件中。
- 通过进行引入 defineAsyncComponent,配合 Suspense 进行更多操作,可用于 loading 和骨架屏相关。
// template
<Suspense>
<template #default>
<AsyncComponent></AsyncComponent>
</template>
<template #fallback>
<div>loading...</div>
</template>
</Suspense>
// script
const AsyncComponent = defineAsyncComponent(() => import('./asyncComponent.vue'))
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 7. 源码管理
- Vue 2 (opens new window) 内部所有的模块都是揉在一起的,这样做会导致不好扩展的问题;Vue 3 (opens new window) 利用拆包,使用最近流行的 monorepo 管理方式,响应式、编译和运行时全部独立。
- 在 Vue 3 的组织架构中,响应式独立了出来。而 Vue 2 的响应式只服务于 Vue,Vue 3 的响应式就和 Vue 解耦了,你甚至可以在 Node.js 和 React 中使用响应式。
# 三、生态
# 1. Vite
- 官方文档:https://cn.vitejs.dev/ (opens new window)。
- Vite 是下一代的前端工具链,和 Vue 也不是强绑定,Vite 的竞品是 Webpack。
- Vite 主要提升的是开发的体验。现代浏览器已经默认支持了 ES6 的 import 语法,Vite 就是基于这个原理来实现的。具体来说,在调试环境下,我们不需要全部预打包,只是把你首页依赖的文件,依次通过网络请求去获取,整个开发体验得到巨大提升,做到了复杂项目的秒级调试和热更新。
# 2. 路由
- Vue Router:https://router.vuejs.org/zh/ (opens new window)。(v4.x)
# 3. 状态管理
- Vuex:https://vuex.vuejs.org/ (opens new window)(v4.x)
- pinia:https://pinia.vuejs.org/ (opens new window)(官方推荐)
# 4. VSCode 插件
- Volar (Vue Language Features):Vetur 只支持 Vue2,Volar 只支持 Vue3,两者冲突。
# 5. hooks 函数
- Vue3 的 hooks 函数相当于 Vue2 的 mixin,不同在与 hooks 是函数。
- Vue3 的 hooks 函数可以帮助我们提高代码的复用性,让我们能在不同的组件中都利用 hooks 函数。
- hooks 库:【1】vueuse:https://github.com/vueuse/vueuse (opens new window);【2】ahooks-vue:https://dewfall123.github.io/ahooks-vue/ (opens new window);【3】v3hooks:https://github.com/yanzhandong/v3hooks (opens new window)。
# 四、Vue3 的创建
# 1. Vue 官网
npm init vue@latest
1
# 2. Vite
npm create vite@latest
1
# 3. Vue CLI
npm install -g @vue/cli
npm update -g @vue/cli
vue create vue-cli-demo
1
2
3
2
3