# Vue 2 迁移到 Vue 3
从2向3升级,建议先从选项式写法进行改造,其更加偏向于2的写法,有更强的兼容性
# 三方库升级
你可以直接一次性升级所有的三方库,引入支持三的版本,如果有些包难以替换,可以将 vue 等级定为 "vue": "^3.2.6",并使用 "@vue/compat": "^3.2.6"
,来最大程度支持 vue2。
将 vuex
升级至 v4 (opens new window)
将 vue-router
升级至 v4 (opens new window)
...
如果是用 uniapp 开发的话,上面的问题还是比较轻松的,选择 vue3 模式后 uniapp 会集成这些的对应版本,只需要注意变更的 api 和引入方式就可以了。
# 全局属性
vue3 移除了 prototype 属性
因为 vue2 是通过 new Vue来创建实例的,可以通过直接在原型上添加属性 Vue.prototype.$$tool = tool
;使每一个实例都具有 $$tool
属性。
但是 vue3 是通过 createApp 函数创建的。
1 如果是选项式 API,官方文档中有 app.config.globalProperties 这个代替方案。 2 如果是组合式 API,直接用 provide/inject
# Filters(过滤器)
Filters 是之前 vue 借鉴 anglar 而使用的,但因为存在性能原因,在 vue3 中被移除了。
如果有使用 Filters 需要用计算属性和 methods 来替换。
# 双向绑定v-modal
v-modal api 在 vue3 中也做了变更
首先,属性 value
已重命名为 modelValue
写法变为这样:
props: {
modelValue: String // previously was `value: String`
},
emits: ['update:modelValue'],
methods: {
changePageTitle(title) {
this.$emit('update:modelValue', title)
}
}
2
3
4
5
6
7
8
9
10
11
很酷的事情是现在可以有多个 v-model 自定义值。例如
v-model:valueA
,v-model:valueB
# v-if 和 v-for
在 vue3 中同时使用 v-if 和 v-for 会强制报错,因为性能的原因 vue3 将这种使用方式禁用了。
必须手动处理数据源,或使用计算属性。
# 事件总线
在 Vue2 中,可以使用 Vue 实例通过 vm.on 和 vm.off 创建全局 EventBus。从 Vue 3 开始,不可能再这样做了,这两个方法已经被移出了。
可以使用第三方库来代替。比如 Mitt,如果是 uniapp 的话,还可以用 uniapp 自带的 uni.emit 方法。
# 插槽写法变更
Vue3 将不支持 slot="xxx"
的用法 ,请使用 v-slot:xxx
用法。(而且必须要加在 template 标签上)
<!-- Vue2 支持的用法 -->
<view slot="left" class="city">
<!-- ... -->
</view>
<!-- Vue3 支持的用法 -->
<template v-slot:left>
<view class="city">
<!-- ... -->
</view>
</template>
2
3
4
5
6
7
8
9
10
11
# uniapp 的变更
# 1.静态资源的导入(vite)
除了在 js 模块中,不支持使用绝对地址引用(运行可能会成功,但打包后路径会不对,应该需要进行服务器配置,小程序完全不支持)。比如以下两种方式是不被支持的:
# 1.css中使用绝对引用
@font-face {
font-family: "iconfont";
/* 不支持 */
src: url('@/static/iconfont/iconfont.woff2?t=1666164241556') format('woff2'),
}
2
3
4
5
解决方法:使用相对路径
注意
在小程序中使用相对路径也不行,小程序不支持在 wxss 中引入本地路径,需要转化为网络路径或 base64
# 2.template 块中使用绝对引用(因为vite的原因打包后可能会丢失,我试的时候是这样,小程序没影响)
<image
src="@/static/images/shezhi.png"
></image>
2
3
解决方法:
1.使用相对引用(h5和小程序通用)
<image
src="../../static/images/shezhi.png"
></image>
2
3
2.先导入到 js 中(h5和小程序通用)
先引入到到 js,这种方式是可以的。
::: notice
如果不是 setup 语法糖,就需要先在模板中注册
:::
<image
:src="shezhiImg"
></image>
<script setup>
import shezhiImg from '@/static/images/shezhi.png'
</script>
2
3
4
5
6
7
3.使用 vite (opens new window) 提供的静态资源导入方法(仅h5)
好处是可以封装出一个公方法使用
<image
:src="getImageUrl('shezhi')"
></image>
<script>
const getImageUrl = (url, suffix = 'png') => {
return new URL(`../../static/${url}.${suffix}`, import.meta.url).href
}
</script>
2
3
4
5
6
7
8
9
上面那个方法只能 h5
使用因为 new url
相当于 window.location.href
,于是用 import.meta.globEager
做了迂回
该方法是直接导入所有模块,并且是同步导入,返回结果直接通过 for...in循环就可以操作
import { computed } from 'vue'
<image :src="getAssetsImages('shezhi','png')" />
let getAssetsImages = (name, type = 'png') => {
/**
* 获取本地图
* @param name // 文件名 如 home-bg
* @param type // 文件类型 如 png jpg
* @returns {*|string}
*/
const path = `/src/static/images/${name}.${type}`
const modules = import.meta.globEager('/src/static/images/*')
return modules[path].default
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
4.vite 静态资源编译 base64
默认为 4kb,uniapp 下需要创建 vite.config.js 引入uniapp 配置
import {
defineConfig
} from 'vite';
import uni from '@dcloudio/vite-plugin-uni';
export default defineConfig({
plugins: [uni()],
build: {
assetsInlineLimit: 10000
}
});
2
3
4
5
6
7
8
9
10
11
# 2.需要根 html
转3之后必须要有一个根 html 文件
# 3.只支持 ES6 模块
所有的 commonJS 都需要变为 ES6 写法。
// 之前 - Vue 2, 使用 commonJS
var utils = require("../../../common/util.js");
// 之后 - Vue 3, 只支持 ES6 模块
import utils from "../../../common/util.js";
// 之前 - Vue 2, 依赖如使用 commonJS 方式导出
module.exports.X = X;
// 之后 - Vue 3, 只支持 ES6 模块
export default { X };
2
3
4
5
6
7
8
9
10
11
# 4.Api Promise 化
调用结果的方式变更
一些 promise 化的 api 调用的结果不一样了。
比如 uni.showPromise
- Vue3 中,调用成功会进入 then 方法,调用失败会进入 catch 方法
- Vue2 中,调用无论成功还是失败,都会进入 then 方法,返回数据的第一个参数是错误对象,第二个参数是返回数据