前言
最近在着手升级项目,遇到不少坑,浪费了很多时间,简单记录一下。
1、升级i18n并重写
安装
npm install vue-i18n@next --save
在I18n的升级中,发现Vue3不同于Vue2的点有很多,首先不能通过app.use的方法去全局使用i18n。为了较大程度兼容原来Vue2的部分,自己定义了一个全局变量$t来尽可能不动之前的代码。又在i18n中全局定义了一个t方法来兼容写在 script 标签中的国际化。
let i18n;
export function initVueI18N() {
i18n = createI18n({
locale,
messages,
});
i18n.t = (key) => {
const strKey = key.split(".")[0];
const strValue = key.split(".").length > 0 && key.split(".")[1];
// 根据自己的写法来定
return messages[locale][strKey][strValue];
};
return i18n;
}
全局对象注册$t和页面使用i18n.t
// main.js
import { initVueI18N } from "./vueI18n";
app.config.globalProperties.$t = i18n.t;
// 子页面
import i18N from "../i18n/i18n.js";
i18n.t('XXX')
2、升级draggable
在vue-draggable升级之处,我还抱有幻想,没有升级vue-draggable,发现页面写有Draggable的都没有渲染。果断选择升级。
npm i vuedraggable@4.1.0 --save
升级完之后发现使用起来并不是很顺手。
// 原先的版本
<Draggable v-model="myArray">
<div v-for="element in myArray" :key="element.id">{{element.name}}</div>
</Draggable>
// 4.1以上版本
<Draggable :list="list" item-key="id" :animation="300" :sort='false' :group="{name: 'article',pull:'clone'}" @end="end1" class="dragArea1">
<template #item="{ element, index }">
<div class="list-item">
<div class="list-complete-item-handle">{{element.name}}</div>
</div>
</template>
</Draggable>
首先原来是option的写法,要改成插槽的形式,且插槽内定义的变量只能是element和index!!原本代码结构是多层遍历的插槽写法,现阶段只能将每一部分差分成组件,一层一层传值。(插槽还不能嵌套插槽,不然编译会报错)
其次,写完发现还是不好使,结果上网一查,说和compat不兼容,就把compat从webpack中删了,结果一堆报错。
3、移除@vue/compat
@vue/compat 是 Vue 3 的迁移构建,它提供了可配置的 Vue 2 兼容行为。比如
// 最常见的$set
this.$set()
// 组件或路由的懒加载使用
resolve => require(['./HelloWorld.vue'],resolve)
都会进行兼容并编译成功。但删除@vue/compat后,能编译成功,但都会在控制台报错,修改如下
// 全局定义一个$set
app.config.globalProperties.$set = (target:any, key:any, value:any) => {
if (!target || !key) return
// 判断target对象是否是Proxy代理对象
if (!(target instanceof Proxy)) return
if (value === undefined) return
target[key] = value
}
// 用defineAsyncComponent去加载组件或者路由
defineAsyncComponent(() => import('./HelloWorld.vue'))
4、对于mount函数的研究
由于后端是C#,要兼容vue2中写的cshtml页面,所以在app.mount挂载根节点的时候,不能将根节点清楚。
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.mount('#app')
将Vue3中的打断点发现根节点上的元素都被清空了
app.mount = (containerOrSelector) => {
const container = normalizeContainer(containerOrSelector);
if (!container)
return;
const component = app._component;
// 没有走下面的if语句,并没有将container中的innerHTML保存下来
if (!isFunction(component) && !component.render && !component.template) {
// __UNSAFE__
// Reason: potential execution of JS expressions in in-DOM template.
// The user must make sure the in-DOM template is trusted. If it's
// rendered by the server, the template should not contain any user data.
component.template = container.innerHTML;
}
// clear content before mounting
// 注意这里会将原先节点清空
container.innerHTML = '';
const proxy = mount(container, false, container instanceof SVGElement);
if (container instanceof Element) {
container.removeAttribute('v-cloak');
container.setAttribute('data-v-app', '');
}
return proxy;
};
解决方法
import { createApp } from 'vue/dist/vue.esm-browser'
import App from './App.vue'
const app = createApp(App)
app.mount('#app')
打个断点看一下mount函数变化
app.mount = (containerOrSelector) => {
const container = normalizeContainer(containerOrSelector);
if (!container)
return;
const component = app._component;
// 走了if语句,将内容保存进template
if (!isFunction(component) && !component.render && !component.template) {
// __UNSAFE__
// Reason: potential execution of JS expressions in in-DOM template.
// The user must make sure the in-DOM template is trusted. If it's
// rendered by the server, the template should not contain any user data.
component.template = container.innerHTML;
}
// clear content before mounting
container.innerHTML = '';
const proxy = mount(container, false, container instanceof SVGElement);
if (container instanceof Element) {
container.removeAttribute('v-cloak');
container.setAttribute('data-v-app', '');
}
return proxy;
};
不难得出,import { createApp } from 'vue/dist/vue.esm-browser'可以在createApp的时候写template。 接下来继续升级,踩坑还在继续。。。