Vue 3 新特性简述

vue 都发布新版本 vue3 了,不跟上时代怎么可以

本文结合 ts,且根据笔者自己的使用体验


目录大致如下

  1. 构建
  2. 路由
  3. 构造选项
  4. ref
  5. 钩子函数
  6. 标签属性
  7. teleport
  8. 总结
  9. 参考文章

构建

与 vue2 使用 webpack 不同,vue3 可以使用 vite 进行构建

Vite,一个基于浏览器原生 ES imports 的开发服务器。利用浏览器去解析 imports,在服务器端按需编译返回,完全跳过了打包这个概念,服务器随起随用。同时不仅有 Vue 文件支持,还搞定了热更新,而且热更新的速度不会随着模块增多而变慢。针对生产环境则可以把同一份代码用 rollup 打。虽然现在还比较粗糙,但这个方向我觉得是有潜力的,做得好可以彻底解决改一行代码等半天热更新的问题。

简单说就是,在生产环境使用模块,省略打包步骤,开发环境可以秒级启动

安装 vite,只需要执行以下命令

1
npm i -g create-vite-app

安装成功后,可以通过两个命令来构建

1
2
3
create-vite-app [projectName]
// 或
cva [projectName]

即可在当前目录下构建一个基于 vite 的 vue3 新项目

之后通过命令行 cd 进入该项目目录,执行

1
npm install

初始化 node_modules 之后,再运行

1
npm run dev

即可启动项目预览了,通常会启动在 3000 端口

但是,当在项目中使用 ts 时,ts 无法识别 vue 后缀的文件,这时候可以在项目的 src 目录下新建一个文件 shims.d.ts 来处理识别问题

1
2
3
4
5
6
// shims.d.ts
declare module '*.vue' {
import { ComponentOptions } from 'vue'
const componentOptions: ComponentOptions
export default componentOptions
}

当静态报错为”无法识别 vue 后缀的文件”时,打开该 ts,再返回原代码,即可解决红线报错

路由

先来看一个 vue2 的路由示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import Vue from 'vue'
import Router from 'vue-router'
import login from '@/components/others/login'

Vue.use(Router)

const router = new Router({
mode: 'history',
routes: [
{
path: '/login',
name: 'login',
component: login
},
],
})
export default router

可见,vue2 中声明一个路由,是使用 new 操作符来新建一个 Router 对象,并向其构造函数中传入 options 来达成的

现在再给出一个 vue3 的路由示例

1
2
3
4
5
6
7
8
9
10
11
12
import { createWebHistory, createRouter } from 'vue-router'
import Home from './views/Home.vue'

const history = createWebHistory()
const router = createRouter({
history,
routes: [
{ path: '/', component: Home },
],
})

export default router

细节上,vue3 采用了 createRouter 函数来创建一个 Router 对象,对用户隐藏了内部细节,但仍然要求用户传入一个 options,且该 options 也主要包括 moderoutes 两个选项

同时,在 vue3 中,声明一个路由模式,由字符串改为了函数返回值,具体对应如下

vue2 写法 vue3 写法
mode 字段 history 字段
mode: ‘hash’ history: createWebHashHistory()
mode: ‘history’ history: createWebHistory()
mode: ‘memory’ history: createMemoryHistory()

整体上大同小异,使用时注意细节即可

构造选项

一个 vue2 的 script 标签写法,大致有如下格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
export default {
props:{
a:{
type: String,
default: '233'
},
},
data(){
return{
...data
}
},
mounted(){
// ...
// and other lifecycle
},
methods:{
xxx(){}
}
}

诸如此类

但是在 vue3 中,除 props 写法照旧之外,其余均可以通过 setup 方法进行处理

1
2
3
4
5
6
7
8
9
10
11
export default {
props:{
// ...
},
setup(){
const a = ref('233')
return {
a
}
}
}

setup 方法中,所有通过 return 暴露出的变量,都等效于 vue2 中 data 暴露的变量;所有通过 return 暴露出的函数,都等效于 vue2 中 methods 暴露的函数

而生命周期钩子,也可以直接写在 setup 中,如 mounted

1
2
3
4
5
6
7
8
9
10
11
// vue2
mounted(){
// some code
}

// vue3
setup(){
onMounted(()=>{
// some code
})
}

一种类似于 DOM 事件中 click -> onClick 的转化,将 mounted 转化为了 onMounted,此时 onMounted 接受一个函数作为参数

除此之外,要在 setup 中使用传递到组件上的参数,在 vue2 中只需要使用 this.xxx 就可以访问到,而在 vue3 中必须要在 setup 的形参列表中声明后才可以使用

setup 接受两个参数,第一个是绑定参数 props,第二个是上下文 context

1
2
3
4
5
6
setup(props, context){
const {xxx} = props
return {
xxx
}
}

等效于 vue2 中的

1
2
3
4
5
6
7
8
export default {
props:['xxx'],
data(){
return {
xxx: this.xxx
}
}
}

那么,在 props 中未声明的绑定参数,都去哪了呢?

在 vue2 中,可以通过 this.$attrs 来访问到其余的参数

而在 vue3 中,要通过 context.attrs 来访问

注意:

  1. 在父组件中绑定到子组件的事件,不会出现在 props 里
  2. props不支持事件,支持其余属性,不在props里的都会到attrs里,但 props 要先声明
  3. props里有多种类型,attrs只有字符串

如果不希望子组件继承父组件给出的参数,也可以显式指定不继承

1
2
3
export default {
inheritAttrs: false
}

注意 inheritAttrs: false 选项不会影响 styleclass 的绑定

综上,假如有一个需求是,父组件会向子组件中传入若干参数,而只希望绑定变量名为 xxx 以外的其余变量,那么可以有如下写法

1
2
3
4
5
6
setup(props, context){
const {xxx, ...rest} = context.attrs
return {
rest
}
}

然后在模板中,要绑定的地方,写入 v-bind="rest" 即可绑定所有参数

ref

细心的朋友应该已经注意到了,上面出现一个名为 ref 的用法,且用法形似一个函数

熟悉 ES6 的 proxy 的话,可以看出 ref 其实就是返回一个对传入数据的代理,这个代理拦截了 get 和 set 操作,并在 set 的时候通知视图更新

熟悉 react 的朋友,是不是感觉和 useState 很像?其实就是把 getter/setter 返回到同一个对象身上而已,2333

设在以下代码段中使用 ref

1
const visible = ref([true,true])

则有以下特点

  1. 可以使用 const 声明代理对象,反正是这个对象不能变,不是里面的代理内容不能变

  2. 在 script 标签中读取数组内容时,必须使用 visible.value 才能取得数组,直接访问 visible 取得的是代理对象

    尽可能不要直接修改代理内容

  3. 在 template 标签(模板)中读取数组内容时,直接访问 visible 即可得到数组内容

  4. 当模板中引用的代理对象的 set 代理,发现代理内容被更新时,会自动通知视图进行更新

  5. ref 代理自带 vue2 的 data 做不到的深度监听,比如修改数组项、或对象的字段

钩子函数

vue3 新增了一个重要的钩子函数 watchEffect

当然,同时可以使用 vue2 的 watch 属性,不赘述了

watchEffect 的主要效果是,注册一个侦听,该侦听会在声明时立刻执行,并在之后所有生命周期函数被调用前,或 setup 函数被调用时执行

一般写法如下

1
2
3
4
5
6
7
8
watchEffect(onInvalidate => {
// work
onInvalidate(()=>{

})
},{
// options
})

watchEffect 接受两个参数,其中第一个参数必选,第二个参数可选

第一个参数是每次执行时的执行函数,该函数会提供一个入参,表示侦听执行失败时的善后函数,类似于 catch 语句块中的 finally。此处的入参 onInvalidate 也接受一个函数,表示善后时执行的内容

第二个参数表示 watchEffect 的配置,一般用于配置 watchEffect 执行的时机

例如以下代码

1
2
3
4
5
watchEffect(()=>{
// work
},{
flush: 'post'
})

options 中的字段 flush,默认值是 'pre',表示在钩子函数执行之就执行侦听,所以如果想在之后执行,就要手动设置为 'post'

注意,会在所有生命周期函数被调用后执行,意味着它有可能在 beforeCreate 生命周期之前,或 created 之前就被执行,所以请注意控制其执行时机

标签属性

也许有人觉得,写一个 setup 函数,和 vue2 好像没有什么本质差别

但是,vue3 可以在 script 标签上另加一个新属性: setup

当写一个 script 标签的时候,可以写作如下形式

1
2
3
<script lang="ts" setup>
xxx
</script>

此时 script 标签内部的所有内容,都被视为运行在 setup 函数中

setup 的两个参数怎么办呢?直接写在属性上就好

1
2
3
<script lang="ts" setup="props,context">
xxx
</script>

这样就可以在 script 标签中正常使用 propscontext

那原来在 setup 中的 return 导出怎么办呢?答案是可以直接 export

例如原来有如下 setup

1
2
3
4
setup(){
const data=ref(0);
return {data}
}

现在可以直接写作如下模样

1
2
3
4
5
6
<script lang="ts" setup="props,context">
export default {
// 原有 vue 配置
}
export const data = ref(0)
</script>

之后的使用与上例相同,直接在模板中引用即可

但是,虽然看起来很装逼,不过该写法尚未得到广泛支持,反正到2021年2月16日为止,我在我的编辑器里是用不了,2333

teleport

vue3 还有一个重要的新组件: teleport

它用来将新建的渲染挂载到页面上的某个目标,而不是原定位置

考虑以下情况:我们需要制作一个弹窗功能,希望弹窗弹出后,其在 DOM 树的位置是 body 的直接子级

那么可以得到如下代码

1
2
3
4
5
6
7
8
9
<template>
<template v-if="visible">
<teleport to="body">
<div class="dialog">
对话框内容
</div>
</teleport>
</template>
</template>

那么,上述模板在检测到 visible 变为 true 之后,要渲染内部的内容

内部第一层是 teleport,其 to 属性为 body,那么会将 teleport 内部的所有内容,挂载到 body 上,成为其最后一个子结点

发现了吧?其实 teleport 最重要的就是 to 属性,该属性接受一个 CSS 选择器,指定要挂载的位置

选择器重复怎么办?自己看看 JQuery 吧(

总结

vue3 还是搞出来一些很好玩的东西,有空多研究一下

而且和 ts 相性很好,又吸收了 react 的优点,未来超过 react 不是没有可能

但是!!!太新的特性,使用的时候要先检查一下有没有得到支持了,不要改完代码才发现没支持,又要改回去

这里我要点名表扬 git reset --hard [version],存档合理的话,这简直是神技,2333

参考文章

如何看待 Web 开发构建工具 Vite? - 阿里巴巴淘系技术

响应式计算和侦听 - watchEffect


感谢阅读

--It's the end.Thanks for your read.--