Vue 3.0 新特性及使用方法
介绍
2020年9月19日凌晨,尤雨溪大大正式发布了 Vue.js 3.0 版本,代号:One Piece。此框架新的主要版本提供了更好的性能、更小的捆绑包体积、更好的 TypeScript 集成、用于处理大规模用例的新 API,并为框架未来的长期迭代奠定了坚实的基础。
3.0 版本的开发周期长达两年多,期间产生了 30+ RFCs、2600+ commits、628 pull requests,以及核心仓库之外的大量开发和文档工作。
Vue 3.0 的发布标志着此框架整体上已处于可用状态。尽管框架的某些子项目可能仍需要进一步的开发才能达到稳定状态(特别是 devtools 中的路由和 Vuex 集成),不过现在仍然是开始使用 Vue 3 启动新项目的合适时机。官方还鼓励库作者现在可以开始升级项目以支持 Vue 3。
what is RFC?
RFC(Request For Comments) - 即请求评议,旨在为新功能进入框架提供一致且受控的路径。
The "RFC" (request for comments) process is intended to provide a consistent and controlled path for new features to enter the framework.
Many changes, including bug fixes and documentation improvements can be implemented and reviewed via the normal GitHub pull request workflow.
Some changes though are "substantial", and we ask that these be put through a bit of a design process and produce a consensus among the Vue core team and the community.
RFC 为新功能的引入提供了统一可控的途径,利于在Vue核心团队和社区中进行方案的讨论和优化,最终达成共识。
The RFC life-cycle
An RFC goes through the following stages:
- Pending: when the RFC is submitted as a PR.
- Active: when an RFC PR is merged and undergoing implementation.
- Landed: when an RFC's proposed changes are shipped in an actual release.
- Rejected: when an RFC PR is closed without being merged.
如何参与
提出RFC pull request前,先在issue中讨论该问题,然后提出RFC PR,PR中包含一个 RFC的markdown文件(非实际代码),经过讨论后核心团队最终将决定是否接受或拒绝该RFC。RFC的提出者并不一定需要自己去实现它(当然欢迎实现)。
创建Vue3.0项目
- 通过脚手架 vite 安装:
npm init vite-app hello-vue3 # OR yarn create vite-app hello-vue3
Vite is an opinionated web dev build tool that serves your code via native ES Module imports during dev and bundles it with Rollup for production.
Vite目前仅支持 Vue 3.x以上,这意味着你不能使用不兼容Vue 3的组件库
目前基于Vue的第三方组件库兼容Vue 3的情况:
Ant Design Vue:支持 Vue 3.0 的 2.0.0 测试版已发布
ElementUI:尚未支持
MintUI:尚未支持
iView(ViewUI):尚未支持
Vue2-leaflet:很明显不支持
- 通过脚手架 vue-cli 安装:
首先全局更新最新版的 Vue CLI,4.5.0以上版本支持 Vue3:
npm install -g @vue/cli # OR yarn global add @vue/cli
vue create hello-vue3
# select vue 3 preset
Vue2.x 项目升级为 Vue3.x项目
最简单的方法,就是使用vue-cli 3.0,创建一个新的项目,然后将原有的项目源码拷到新的项目中。
Vue3中兼容Vue2中定义组件的写法,所以只需要将入口文件 main.js
中创建Vue实例的代码替换为使用Vue3中新引入的 createApp
方法,来创建应用程序实例的方式即可。
Vue 2 main.js:
import Vue from 'vue'
import App from './App.vue'
new Vue({
render: h => h(App),
}).$mount('#app')
Vue 3 main.js:
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
值得注意的Vue3 新特性
Composition API(组合 API):
当组件变得越来越大时,逻辑关注点的列表也会增长。这可能会导致组件难以阅读和理解,且碎片化使得理解和维护复杂组件变得困难。选项的分离掩盖了潜在的逻辑问题。此外,在处理单个逻辑关注点时,我们必须不断地“跳转”相关代码的选项块。
如果能够将与同一个逻辑关注点相关的代码配置在一起会更好,于是 Composition API 应运而生。
使用Composition api的位置被称为setup
setup组件选项
setup
组件选项在创建组件之前执行,一旦 props 被解析,并充当合成 API 的入口点。
注意:由于在执行 setup 时尚未创建组件实例,因此在 setup 选项中没有 this。这意味着,除了 props 之外,你将无法访问组件中声明的任何属性——本地状态、计算属性或方法。
// src/components/UserRepositories.vue `setup` function
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted } from 'vue'
// in our component
export default {
setup (props) {
const repositories = ref([]) // 定义一个变量
const getUserRepositories = async () => { // 定义一个方法
repositories.value = await fetchUserRepositories(props.user)
}
onMounted(getUserRepositories) // 生命周期钩子 当实例mounted后调用getUserRepositories方法
return {
repositories, // 返回一个data
getUserRepositories // 返回一个method
}
}
}
单文件组件 Composition API 语法糖 (<script setup>):
当组件可以使用组合API后,setup
往往成为了唯一会用到的组件属性,因此利用语法糖简化setup
的写法
<template>
<button @click="inc">{{ count }}</button>
</template>
// Composition API
<script>
export default {
setup() {
const count = ref(0)
const inc = () => count.value++
return {
count,
inc,
}
},
}
</script>
// 使用了 Composition API 语法糖:
<script setup>
import { ref } from 'vue'
export const count = ref(0)
export const inc = () => count.value++
</script>
单文件组件状态驱动的 CSS 变量 (<style vars>):
有能力在运行时根据组件状态来动态更新样式
<template>
<div class="text">hello</div>
</template>
<script>
export default {
data() {
return {
color: 'red'
}
}
}
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
单文件组件 <style scoped> 现在可以包含全局规则或只针对插槽内容的规则:
带有scoped
属性的style 不再只能作用域当前单文件组件,通过深度选择器、插槽选择器、全局选择器拥有了更改其他范围样式的能力。
<style scoped>
/* deep selectors */
::v-deep(.foo) {}
/* shorthand */
:deep(.foo) {}
/* targeting slot content */
::v-slotted(.foo) {}
/* shorthand */
:slotted(.foo) {}
/* one-off global rule */
::v-global(.foo) {}
/* shorthand */
:global(.foo) {}
</style>
Vue 3的重大改变
引入createApp
背景:
从技术上讲,Vue 2 没有“app”的概念,我们定义的应用程序只是通过 new Vue() 创建的根 Vue 实例。从同一个 Vue 构造函数创建的每个根实例共享相同的全局配置,因此全局配置使得在测试期间很容易意外地污染其他测试用例。用户需要仔细存储原始全局配置,并在每次测试后恢复 (例如重置 Vue.config.errorHandler)。有些 API 像 Vue.use 以及 Vue.mixin 甚至连恢复效果的方法都没有,这使得涉及插件的测试特别棘手。
createApp:
import { createApp } from 'vue'
const app = createApp({})
调用 createApp 返回一个应用实例,应用程序实例暴露当前全局 API 的子集,任何全局改变 Vue 行为的 API 现在都会移动到应用实例上,以下是当前全局 API 及其相应实例 API 的表:
[图片上传失败...(image-c4a9d6-1601349914900)]
所有其他不全局改变行为的全局 API 现在被命名为 exports。
全局和内部API已重构为可 tree-shakable
Tree shaking 是一个通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code) 行为的术语。
它依赖于ES2015中的 import 和 export 语句,用来检测代码模块是否被导出、导入,且被 JavaScript 文件使用。
在现代 JavaScript 应用程序中,我们使用模块打包(如webpack或Rollup)将多个 JavaScript 文件打包为单个文件时自动删除未引用的代码。这对于准备预备发布代码的工作非常重要,这样可以使最终文件具有简洁的结构和最小化大小。
2.x 语法:
// 全局 API Vue.nextTick() 不能tree-shaking
import Vue from 'vue'
Vue.nextTick(() => {
// 一些和DOM有关的东西
})
3.x语法:
全局 API 现在只能作为 ES 模块构建的命名导出进行访问,如果模块绑定器支持 tree-shaking,则 Vue 应用程序中未使用的全局 api 将从最终捆绑包中消除,从而获得最佳的文件大小。
import { nextTick } from 'vue'
nextTick(() => {
// 一些和DOM有关的东西
})
受影响的API:
- Vue.nextTick
- Vue.observable (用Vue.reactive替换)
- Vue.version
- Vue.compile
- Vue.set
- Vue.delete
组件上 v-model 用法已更改
- 自定义
v-model
时,prop和事件默认名称已更改:
prop:value
->modelValue
event:input
->update:modelValue
-
.sync
和组件的model
选项已移除,可用v-model
作为替代 - 现在可以在同一个组件上使用多个 v-model 进行双向绑定;
- 现在可以自定义 v-model 修饰符
比如自定义v-model.capitalize
,绑定为字符串第一个字母的大写
<template v-for> 和非 - v-for 节点上 key 用法已更改
-
Vue 2.x 建议在 v-if/v-else/v-else-if 的分支中使用 key,Vue 3.x 中仍能正常工作,但不再建议,因为没有为条件分支提供 key 时,也会自动生成唯一的 key。
-
在 Vue 2.x 中 <template> 标签不能拥有 key,在 Vue 3.x 中 key 则应该被设置在 <template> 标签上。
在同一元素上使用的 v-if 和 v-for 优先级已更改
- Vue 3.x 中v-if 会拥有比 v-for 更高的优先级。
由于语法上存在歧义,建议避免在同一元素上同时使用两者,比如利用计算属性筛选出列表。
v-bind="object" 现在排序敏感
- Vue 2.x 如果一个元素同时定义了 v-bind="object" 和一个相同的单独的 property,那么这个单独的 property 总是会覆盖 object 中的绑定。
- Vue 3.x 声明绑定的顺序决定了它们如何合并。
// 2.x中 id最终为red 3.x中 id为blue
<div id="red" v-bind="{ id: 'blue' }"></div>
v-for 中的 ref 不再注册 ref 数组
-
Vue 2 中,在 v-for 里使用 ref属性时,从$refs中获取的相应属性会是一个ref数组。
-
Vue 3中则将ref绑定到一个更灵活的函数上 (ele) => { ...//保存ele的操作 }:
template:
<div v-for="item in list" :ref="setItemRef"></div>
script:
import { ref, onBeforeUpdate, onUpdated } from 'vue' export default { setup() { let itemRefs = [] const setItemRef = el => { itemRefs.push(el) } onBeforeUpdate(() => { itemRefs = [] }) onUpdated(() => { console.log(itemRefs) }) return { itemRefs, setItemRef } } }
官方库的支持情况
所有的官方库和工具现在都支持 Vue 3,但大多数仍然处于 beta 状态,并在 NPM 的 next dist 标签下发。计划在 2020 年底前稳定所有项目,并将其转换为使用 latest 的 dist 标签。
Vue Cli
从 v4.5.0 开始,vue-cli 现在提供了内置选项,可在创建新项目时选择 Vue 3 预设。现在可以升级 vue-cli 并运行 vue create 来创建 Vue 3 项目。
Vue Router
Vue Router 4.0 提供了 Vue 3 支持,并有许多突破性的变化。
Vuex
Vuex 4.0 提供了 Vue 3 支持,其 API 与 3.x 基本相同。唯一的突破性变化是插件的安装方式。
Devtools Extension
正在开发一个新版本的 Devtools,目前只支持Vue 3。
IDE 支持
推荐使用 VSCode 和官方拓展 Vetur,Vetur为 Vue 3 提供了全面的 IDE 支持