Vue3上手指南

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

# 4. Composition API

# 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

# 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

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

# 5. 全部模块使用 TypeScript 重构

  • 原因:从开发维护的角度看,Vue 2 是使用 Flow.js 来做类型校验。但现在 Flow.js 已经停止维护了,整个社区都在全面使用 TypeScript 来构建基础库,Vue 团队也不例外。
  • 好处:类型系统带来了更方便的提示,并且让我们的代码能够更健壮。

# 6. 新的组件

# 6.1 Fragment

  • 这个更像是一种概念,它的意思就相当于创建页面时,给了一个虚拟根标签 VNode,因为我们知道在 Vue2 里面,我们是有根标签这个概念的。但是到来 Vue3,它是自动给你创建个虚拟根标签 VNodeFragment),所以可以不要根标签。
  • 好处就是减少标签层级,减小内存占用
<template>
  <div>
    Vue2
  </div>
</template>

<template>
  Vue3
</template>
1
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

# 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

# 7. 源码管理

  • Vue 2 (opens new window) 内部所有的模块都是揉在一起的,这样做会导致不好扩展的问题;Vue 3 (opens new window) 利用拆包,使用最近流行的 monorepo 管理方式,响应式、编译和运行时全部独立。
  • 在 Vue 3 的组织架构中,响应式独立了出来。而 Vue 2 的响应式只服务于 Vue,Vue 3 的响应式就和 Vue 解耦了,你甚至可以在 Node.js 和 React 中使用响应式。

img

# 三、生态

# 1. Vite

  • 官方文档:https://cn.vitejs.dev/ (opens new window)
  • Vite 是下一代的前端工具链,和 Vue 也不是强绑定,Vite 的竞品是 Webpack。
  • Vite 主要提升的是开发的体验。现代浏览器已经默认支持了 ES6 的 import 语法,Vite 就是基于这个原理来实现的。具体来说,在调试环境下,我们不需要全部预打包,只是把你首页依赖的文件,依次通过网络请求去获取,整个开发体验得到巨大提升,做到了复杂项目的秒级调试和热更新。

bundler.37740380.png

esm.3070012d.png

# 2. 路由

# 3. 状态管理

# 4. VSCode 插件

  • Volar (Vue Language Features):Vetur 只支持 Vue2,Volar 只支持 Vue3,两者冲突。

# 5. hooks 函数

# 四、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
Last Updated: 2023/04/22, 23:39:26
彩虹
周杰伦