Vue2升级Vue3踩坑日记(一)

217 阅读2分钟

前言

最近在着手升级项目,遇到不少坑,浪费了很多时间,简单记录一下。

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。 接下来继续升级,踩坑还在继续。。。