尤大的 Vue3.0 已经发布有一阵子了, 已经很成熟了。今天想起来,是时候该上手体验一波了。
目录
一、Vue3.0 亮点
- Performance:性能更比Vue 2.0强;
- Tree shaking support:可以将无用模块“剪辑”,仅打包需要的;
- Composition API:组合式API;
- Fragment, Teleport, Suspense:“碎片”,Teleport 即 Protal 传送门,“悬念”;
- Better TypeScript support:更优秀的Ts支持;
- Custom Renderer API:暴露了自定义渲染API;
二、Vue3.0 项目初始化
-
使用 vue-cli 脚手架
1. 安装 vue-cli 脚手架
$ npm install -g @vue/cli
安装成功后,使用
vue -V
命令,查看是否安装成功:$ vue -V @vue/cli 4.5.9
如果没有安装成功或者是还是2.0版本的,那么我们要将他升级到 3.0。
先将已有的vue-cli
卸载,然后重新安装即可。$ npm uninstall vue-cli -g
2. 创建 vue3.0 项目
$ vue create vue3-demo
在出现的命令交互窗口选择 Manually select features:
然后勾选:Router、Vuex、CSS Pre-processors 和 Linter / Formatter等内容。
回车后根据自己的习惯选择好,就开始创建项目。
选择vue版本:
安装完成之后就可以直接创建 vue3.0 的项目了,通过命令进行后续操作了。
通过下面两个命令就可以启动 vue3.0 的项目了。$ cd vue3-demo $ npm run serve
成功运行项目:
-
使用 Vite 创建 Vue3.0 项目(详细实例应用请移步至:Vue3应用之使用Vite搭建Vue3项目)
1. 全局安装Vite
npm install -g create-vite-app
2. 使用Vite创建Vue3项目
create-vite-app projectName
3. 安装依赖运行项目
cd vite-app npm install (or `yarn`) npm run dev (or `yarn dev`)
4. 引入 Vue Router4 路由
-
查看 vue-router 所有版本号
npm info vue-router versions
-
安装最新的 vue-router
yarn add vue-router@4.0.2
项目运行成功:
-
下面就开始干我们的正事了。。。
三、Vue3.0 新特性分析
1. 创建实例
在 Vue3 中每个 Vue 应用都是通过用 createApp
函数创建一个新的实例,不在通过 new
的方式进行创建:
Vue3创建实例的方式:
// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
createApp(App).use(store).use(router).mount('#app')
Vue2 通过函数式创建实例:、
new Vue({
el: '#app',
router,
store,
render: h => h(App)
})
2. 创建路由
现在创建路由实例需要手动引入 createRouter
方法,创建 history
模式路由也需要手动引入 createWebHistory
方法,这达到 Tree-Shaking
的目的,即不会把所有的 api
都打包进来,只会打包你用到的 api
,vue3 将都会使用这种形式。
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
3. 响应式数据和事件绑定
Vue 3.0 中初始化状态通过 setup()
方法,定义状态需要调用 ref()
方法。
这就跟在 vue2 中有很大的不同,vue2 中我们是使用选项的方式来创建 data
、methods
、watch
和 computed
的。
<template>
<div>
{{count}}
{{str}}
<button @click="add">add</button>
</div>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
const count = ref(0); // 声明count,初始值为 0
const str = ref('hello'); // 声明str,初始值为 'hello'
const add = () => { // 定义一个事件,用来更新count状态
count.value ++; // 更新count值的时候不能直接使用count++,而应使用 count.value++
}
return {
count,
str,
add
}
},
}
</script>
4. 使用 reactive
声明响应式数据
使用 reactive
来一次声明多个变量
import { reactive } from 'vue'
export default {
setup () {
// 引入 reactive,同时定义多个变量
const state = reactive({
count: 0,
str: 'hello'
})
// 现在访问变量,不能使用 .value 方式访问了
const add = () => {
// state.count.value++ // 错误
state.count++
}
return {
state,
add
}
}
}
reactive
和 ref
比较
-
reactive
是接收一个普通对象,返回该对象的响应式代理,它等同于 2.x 中的Vue.observable()
。const obj = reactive({ count: 0 }) // obj 此时是一个响应式的对象 // 访问或修改,直接基于 obj.count
-
ref
也是接收一个参数并返回一个响应式且可改变的ref
对象,一般参数是基础类型。
如果传入的参数是一个对象,将会调用 reactive 方法进行深层响应转换。ref 对象拥有一个指向内部值的单一属性 .value,即当你要访问它的值时,需要 .value 拿到它的值。但是如果是在 setup 中返回且用到模板中时,在 {{}} 里不需要加 .value 访问,在返回时已经自动解套。setup() { return { count: ref(0), // 这里返回,在模板中无需 .value 访问值 } },
5. watch
和 computed
在 Vue3 中使用监听器和计算属性,也需要手动引入,且 watch
和 computed
都需要在 setup
中进行。
import { ref, watch, computed } from 'vue';
export default {
setup() {
const count = ref(0);
const add = () => {
count.value ++;
}
// 监听器 watch 同样是一个方法,它包含 2 个参数,2 个参数都是 function
// 第一个参数是监听的值,count.value 表示当 count.value 发生变化就会触发监听器的回调函数,即第二个参数,第二个参数可以执行监听时候的回调
watch(
() => count.value,
(val, oldVal) => {
console.log(`new count: ${val},old count: ${oldVal}`);
}
);
// 计算属性 computed 是一个方法,里面需要包含一个回调函数,当我们访问计算属性返回结果时,会自动获取回调函数的值:
const doubleCount = computed(() => {
return count.value * 2;
});
return {
count,
add,
doubleCount
}
},
}
如果是 2 个以上的监听属性:
watch(
[refA, () => refB.value],
([a, b], [prevA, prevB]) => {
console.log(`a is: ${a}`)
console.log(`b is: ${b}`)
}
)
6. 获取路由信息
vue3.0 中使用 getCurrentInstance
方法获取当前组件实例,然后通过 ctx
属性获取当前上下文,ctx.$router
是路由实例,而 ctx.$router.currentRoute
就包含当前路由信息。
import { getCurrentInstance } from 'vue'
export default {
setup () {
const { ctx } = getCurrentInstance()
console.log(ctx.$router.currentRoute.value)
}
}
7. Vuex
在学习 Vue3 的 Vuex 4 之前,先来看一下 Vue2 的 Vuex 3:
可以发现创建 store
实例的方式改变了,vue2 中是使用 new 的方式进行创建的
export default new Vuex.Store({
// ...
})
看完 Vue2 的 Vuex 3,那么继续回到我们 Vue3 的使用。
-
定义 Vuex 状态
在
state
中创建了一个状态,在mutations
中添加修改该状态的方法,在actions
中提交mutation
的方法,而不是直接变更状态。import { createStore } from 'vuex' export default createStore({ state: { count: 0 }, mutations: { ADD(state) { state.count++; } }, actions: { add({ commit }){ commit('ADD') } }, modules: { } })
-
更新 Vuex 状态
方式一:
在 xx.vue 页面中,通过计算属性使用Vuex
状态;
在具体事件中通过store.dispatch
方法触发 Action。<template> <div> <div>state from vuex {{count}}</div> <button @click="add">add</button> </div> </template> <script> import { computed } from "vue"; import { useStore } from 'vuex'; // 引入 useStore 方法返回 store 实例 export default { setup() { const store = useStore() const count = computed(() => store.state.count) const add = () => { store.dispatch('add') } return { count, add, }; }, }; </script>
方式二:
通过获取当前组件实例ctx
,使用ctx.$store.commit
直接分发mutations
,但是mutation
有个限制:必须同步执行,而Action
就不受约束,可以在action
内部执行异步操作。<template> <div> <div>state from vuex {{count}}</div> <button @click="add">add</button> </div> </template> <script> import { computed, getCurrentInstance } from "vue"; export default { setup() { const { ctx } = getCurrentInstance() const count = computed(() => ctx.$store.state.count) const add = () => { ctx.$store.commit('ADD') } return { count, add, }; }, }; </script>
四、值得注意的新特性
Vue 3 中需要关注的一些新功能包括:
- 组合式 API
- Teleport
- 片段
- 触发组件选项
- createRenderer API 来自 @vue/runtime-core 创建自定义渲染器
- 单文件组件组合式 API 语法糖 (
<script setup>
) 实验性 - 单文件组件状态驱动的 CSS 变量 (
<style vars>
) 实验性 - 单文件组件 <
style scoped
> 现在可以包含全局规则或只针对插槽内容的规则
五、非兼容的变更
Global API
- 全局 Vue API 已更改为使用应用程序实例
- 全局和内部 API 已经被重构为可 tree-shakable
模板指令
- 组件上
v-model
用法已更改 <template v-for>
和非v-for
节点上key
用法已更改- 在同一元素上使用的
v-if
和v-for
优先级已更改 v-bind="object"
现在排序敏感v-for
中的ref
不再注册ref
数组
组件
- 只能使用普通函数创建功能组件
functional
属性在单文件组件 (SFC)<template>
和functional
组件选项被抛弃- 异步组件现在需要
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
不再支持以点分隔的字符串路径。请改用计算属性函数作为参数。 <template>
没有特殊指令的标记 (v-if
/else-if
/else
、v-for
或v-slot
) 现在被视为普通元素,并 将生成原生的 元素,而不是渲染其内部内容。- 在 Vue 2.x 中,应用根容器的
outerHTML
将替换为根组件模板 (如果根组件没有模板/渲染选项,则最终编译为模板)。Vue 3.x 现在使用应用容器的innerHTML
,这意味着容器本身不再被视为模板的一部分。
以上内容如果想要详细了解,请移步至 v3 迁移指南。
六、基于Vue的第三方组件库兼容Vue3.0的情况
截止目前仅有 Ant Design of Vue 支持 Vue3.0 的 2.0.0测试版 已发布。
- Element-UI:暂不支持;
- Mint UI:暂不支持;
- View UI(iView):暂不支持;
20210107 更新:
基于 Vue3 的组件库 element-plus
已经正式发布:Element Plus
如果想要研究一下它的源码和架构,请移步至:GitHub - element-plus
七、Vite 与 Vue CLI
Has Vite Made Vue CLI Obsolete?
原文:https://vuejsdevelopers.com/2020/12/07/vite-vue-cli/
作者:Anthony Gore
Vue 生态系统中有一个新构建工具 Vite
,它的开发服务器比 Vue CLI
快 10 ~ 100倍。
快10~100倍?是不是很好奇,那是不是意味着 Vue CLI
即将过时?下面将会对这两种构建工具进行对比分析。
Vue CLI 概述
Vue CLI
是使用标准构建工具和最佳实践配置快速建立基于Vue的项目的不可或缺的工具。
主要功能包括:
- 工程脚手架
- 带热模块重载的开发服务器
- 插件系统
- 用户界面
但是需要主要的是:Vue CLI
是构建在 Webpack
之上的,因此开发服务器和构建功能和性能都将是 Webpack
的超集。
Vite 概述
与 Vue CLI
类似,Vite
也是一个提供基本项目脚手架和开发服务器的构建工具。
然而,Vite
并不是基于 Webpack
的,它有自己的开发服务器,利用浏览器中的原生ES
模块。这种架构使得Vite
比Webpack
的开发服务器快了好几个数量级。Vite
采用Rollup
进行构建,速度也更快。
但是 Vite
目前还处于测试阶段,可以看出 Vite
项目的目的并不是像 Vue CLI
那样的一体化工具,而是专注于提供一个快速的开发服务器和基本的构建工具。
Vite 的优点
Vite
开发服务器至少会比 Webpack
快10倍左右。对于一个基本的项目来说,与2.5秒相比,开发构建/重新构建的时间相差250ms。
在一个较大的项目中,这种差异会变得更加明显。Webpack
开发服务器在构建/重新构建时可能会慢到25-30秒,有时甚至更慢。与此同时,Vite
开发服务器可能会以恒定的250ms的速度为同一个项目提供服务。
Vite 为什么这么快?
-
Webpack
开发服务器架构
Webpack
的工作方式是:它通过解析应用程序中的每一个import
和require
,将整个应用程序构建成一个基于JavaScript
的捆绑包,并在运行时转换文件(例如Sass
、TypeScript
、SFC
)。
这都是在服务器端完成的,依赖的数量和改变后构建/重新构建的时间之间有一个大致的线性关系。 -
Vite
开发服务器架构
Vite
不捆绑应用服务器端。相反,它依赖于浏览器对JavaScript
模块的原生支持(也就是ES
模块,这是一个比较新的功能)。
浏览器将在需要时通过HTTP
请求任何JS
模块,并在运行时进行处理。Vite
开发服务器将按需转换任何文件(如Sass
、TypeScript
、SFC
)。
这种架构避免了服务器端对整个应用的捆绑,并利用浏览器高效的模块处理,提供了一个明显更快的开发服务器。
当你对应用程序进行
code-split
和tree-shake
动时,Vite
的速度会更快,因为它只加载它需要的模块,即使是在开发阶段。这与Webpack
不同,在Webpack
中,代码拆分只对生产包有利。
Vite 的缺点
由于Vite
使用了JavaScript
模块,所以最好让依赖关系也使用JavaScript
模块。虽然大多数现代JS包都提供了这一点,但一些老的包可能只提供CommonJS
模块。
Vite
可以将CommonJS
转换为JavaSript
模块,但在一些边缘情况下它可能无法做到。当然,它还需要支持JavaScript
模块的浏览器。
与 Webpack/Vue CLI
不同,Vite
无法创建针对旧版浏览器、web components
等的捆绑包。
而且,与 Vue CLI
不同,开发服务器和构建工具是不同的系统,导致在生产与开发中可能出现不一致的行为。
Vue CLI vs Vite
Vue CLI 总结:
Vue CLI 优点 | Vue CLI 缺点 |
---|---|
经历过战斗考验,可靠 | 开发服务器速度与依赖数量成反比 |
与Vue 2兼容 | |
可以捆绑任何类型的依赖关系 | |
插件生态系统 | |
可以针对不同的目标进行构建 |
Vite 总结:
Vite 优点 | Vite 缺点 |
---|---|
开发服务器比Webpack快10-100倍 | 只能针对现代浏览器(ES2015+) |
将code-splitting作为优先事项 | 与CommonJS模块不完全兼容 |
处于测试阶段,目前仅支持Vue 3 | |
最小的脚手架不包括Vuex、路由器等 | |
不同的开发服务器与构建工具 |
总结
对于有经验的Vue开发者来说,Vite
是一个很好的选择,因为它的开发服务器速度快得离谱,让Webpack
看起来像史前时代。
但是,对于一些 Vue 新开发人员来说,或者对于使用遗留模块和需要复杂构建的大型项目来说,Vue CLI
很可能在目前仍然是必不可少的。
Vite 的未来
虽然上面的分析总结都比较主要集中在 Vite
和 Vue CLI
的现状上,但仍有几点需要考虑注意:
- 仅当浏览器中的
JavaScript
模块支持得到改善时,Vite
才会有所改善; - 随着JS生态系统的追赶,更多的软件包将支持
JavaScript
模块,减少Vite
无法处理的边缘情况; - 目前
Vite
仍处于测试阶段 => 功能可能会有变化; - 有可能
Vue CLI
最终会结合Vite
,这样就不用再使用其中一个了。
值得注意的是,
Vite
并不是唯一一个利用浏览器中JavaScript
模块的开发服务器项目。还有更著名的 Snowpack 甚至可能会挤掉Vite
的发展。时间可能会证明这一点。
八、项目总结
今天做一个小项目,想要使用 Vue3.0
+ Element-ui
,发现 Element-ui
并不支持在 Vue3 中使用。查看源码发现,Vue3 在插件 install
函数的入参从 Vue原型(类)
改成了 app(Vue的实例e)
,所以导致 Element-ui
中的 Vue.prototype.*
,这样的代码已经全部失效了。所以目前看来,Element-ui
是不能兼容 Vue3 的了。
但是 AntD2.0 测试版 支持 Vue 3.0 已发布。大家可以尝试一下。
Vue3.0 使用总结基本就是这些,后面还会不断学习补充,希望与大家一起进步。加油!