项目框架vue2.x版本升级到vue3.0版本

注意:升级前,node版本需要在8.9的版本以上!

本文目录:

  • 为什么要升级框架?
  • 主要模块升级对应版本
  • vue2.x与vue3.x的区别
  • 升级过程中遇到的问题
  • 项目发布

一、为什么要升级框架?

  1. 放弃 Object.defineProperty ,使用更快的原生 Proxy
  2. diff算法优化使性能比vue2.x快1.2~2倍
  3. 组合/CompositionAPI(类似于React Hooks
  4. 模板可以有多个根元素
  5. 按需编译,体积比vue2.x更小

二、主要模块升级对应版本

模块 原始版本 升级版本
node.js v8.x v14.17.6
脚手架 @vue-cli 2.x @vue/cli 4.x
vue ^2.5.2 ^3.0.0
vue-router ^3.1.3 ^4.0.12
vuex ^3.0.1 ^4.0.0
ant-design-vue ^1.5.3 ^2.7.8

三、vue2.x与vue3.x的区别

3.1 vue-cli2 VS vue/cli4

3.1.1 基本文件目录的区别

下列表格中括号里为举例说明

配置项 vue2.x vue3.x
环境变量 根目录config文件夹下创建xx.env.js(test.env.js)文件 根目录创建以env(.env.test) 开头的文件
webpack 根目录build文件夹下创建webpack(webpack.base.config.js)配置文件 根目录vue.config.js文件中配置
index.html                 初始化后在根目录下 初始化后在public文件夹下
babel 根目录下.babelrc文件 根目录下.babel.config.js文件
3.1.2 配置代理

vue-cli2:项目根目录config文件夹下的index.js文件

module.exports = {
  dev: {
    proxyTable: {
      "/tsapi/": { //代理地址
        target: 'http://keynote-test.zhonganonline.com/tsapi/', //需要代理的地址
        changeOrigin: true, //是否跨域
        secure: false,
        baseURL: 'http://keynote-test.zhonganonline.com/tsapi/',
        pathRewrite: {
          '^/tsapi/': '/'
        }
      }
    }
  }
}

vue/cli4:项目根目录vue.config.js文件

module.exports = {
  devServer: {
    port: 8080,
    host: 'za.zhonganonline.com',
    https: true,
    open: true,
    proxy: {
      '/tsapi/': {
        target: 'http://keynote-test.zhonganonline.com/tsapi/',
        changeOrigin: true,
        pathRewrite: {
          '^/tsapi/': '/'
        }
      }
    }
  }
};

注意服务配置位置

3.1.3 配置别名

vue-cli2:项目根目录下build文件夹下webpack.base.conf.js文件

module.exports = {
  resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
      '@static': resolve('static')
    }
  },
}

vue/cli4: 项目根目录vue.config.js文件

module.exports = {
  chainWebpack: (config) => {
      config.resolve.alias
        // 将项目根目录中,src的路径配置为别名@
        .set('@', resolve('src'))
        .set('common', resolve('src/common'))
        .set('utils', resolve('src/utils'));
  }
};

通过链式操作进行更细颗粒度的webpack配置

3.1.4 启动命令

vue-cli2:项目根目录vue.config.js文件

"scripts": {
    "dev": "cross-env VUE_APP_DOMAIN='chinese' webpack-dev-server --inline --progress --config build/webpack.dev.conf.js"
    "build": "cross-env VUE_APP_DOMAIN='chinese' node build/build.js"
  },

vue/cli4: 项目根目录vue.config.js文件

"scripts": {
    "serve": "cross-env VUE_APP_DOMAIN='chinese' vue-cli-service serve",
    "build": "cross-env VUE_APP_DOMAIN='chinese' vue-cli-service build"
  },

vue2到vue3将 webpack-dev-server 换成了vue-cli-servicevue-cli-service 其实执行的是 node_modules/.bin/vue-cli-service,也是基于 webpack-dev-server 实现的。而 webpack-dev-server又是基于node的express框架实现。

3.2 语法区别

3.2.1main.js模块

vue2.x

import Vue from 'vue';
import Antd from 'ant-design-vue';
Vue.use(Antd);
new Vue({
  el: '#app',
  components: { App },
  template: '<App/>'
})

vue3.0

import { createApp } from 'vue';
import Antd from 'ant-design-vue';
import App from './App.vue';
createApp(App).use(Antd).mount('#app');
  • vue3.0无需引入整个vue模块,只需按需引入createApp注册组件即可
  • vue3.0中createApp方法会返回一个新的应用实例,该实例不会被其他实例全局配置属性污染
  • vue3.0应用模块可以使用链式调用的方式
3.2.2 router模块

vue2.x

import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)

const routes = [
  {
    path: '/',
    component: () => import('../components/template/indexQuery/index.vue'),
    name: 'indexQuery'
  }
]

export default new Router({ routes })

vue3.0

import { createRouter, createWebHashHistory } from 'vue-router';

const routes = [
  {
    path: '/',
    component: () => import('../components/template/indexQuery/index.vue'),
    name: 'indexQuery'
  }
]
export const router = createRouter({
  history: createWebHashHistory(),
  routes // 路由对象
});
  • createAppcreateRouter不再是一个类而是一组函数
  • mode: 'history' 配置已经 history 配置所取代,根据使用模式,使用不同的函数代替
    history: createWebHistory()
    hash: createWebHashHistory()
    abstract: createMemoryHistory() // 与浏览器分离的路由模式
    

3.2.3 响应式API与组合式API对比

项目中还未应用,待后期补充


3.3 Antv组件中Api升级

3.3.1 Form表单

antvue1.x 中提供了 Form、FormModel 两个表单组件, 2.0 版本中将 Form、FormModel 进行合并,保留了 FormModel 的使用方式,改名成 Form。

ant1.x

dom节点:<a-form :form="form"></a-form>
data() {
    return {
      form: this.$form.createForm(this, { name: "coordinated" })
    }
}

validateFields((err, value) => { if (!err) {}}); // 使用form对象的属性方法

ant2.x

dom节点:<a-form :model="postData" ref="formRef"></a-form>
methods:validateFields().then(values => {});// 使用form对象的属性方法
3.3.2 图标升级

ant1.x版本全量引入图标icon增加了打包产物的尺寸,ant2.x版本中实现按需引入的方式,减少了打包后的文件体积
ant1.x

<a-icon type="smile" />

ant2.x

<template>
  <smile-outlined />
</template>
<script>
  import SmileOutlined from '@ant-design/icons-vue/SmileOutlined';
  export default {
    components: {
      SmileOutlined,
    },
  };
</script>
3.3.3 常用组件调整API
  • v-model 更改成 v-model:value
    涉及组件:Radio、Mentions、CheckboxGroup、Rate、DatePicker、Select

ant1.x

<a-input v-model="postData.dicChinese"/>

ant2.x

<a-input v-model:value="postData.dicChinese"/>
  • v-model 更改成 v-model:checked
    涉及组件: CheckableTag、Checkbox、Switch

  • v-model 更改成 v-model:visible
    涉及组件: Tag、Popconfirm、Popove、Tooltip、Moda、Dropdown

3.3.4 其他一些变化

在开发过程中经常碰到请求到的数据直接赋值后数据类型变成了Proxy,这导致我们用不了这种数据。有两种办法改变数据类型:

  1. 通过vue中的响应式对象可使用toRaw()方法获取原始对象
import { toRaw } from 'vue'
const trueUserList=toRaw(data) 
  1. 通过json序列化
JSON.parse(JSON.stringify(data)) 

四、升级过程中遇到的问题

4.1 代码规范

由于各个项目代码格式都不一致,每个人的代码写法也不一样,导致项目代码看起来十分混乱且不美观,故而在新版本项目里结合vscode插件Prettier - Code formatter实现代码保存时自动格式化。

4.1.1 为什么选择Prettier?Prettier和Eslint有什么区别?

Eslint主要负责质量检查、风格检查,但Prettier只负责风格检查。两者的作用都是为了让项目中的代码规范能够统一,只不过方向不一致。

4.1.2 conventional commit

conventional commit为约定式提交。为什么使用约定式提交呢?

  • 自动化生成CHANGELOG日志
  • 基础提交类型,自动决定语义化的版本变更
  • 通知相关合作人员变更
  • 触发构建和部署流程
  • 后续开发人员可以看到一个更加结构化的提交历史,降低对项目做贡献的难度
    代码提交规范具体流程
4.1.3 Prettier配置实现ctrl+s保存自动格式化代码
  • 安装Prettier - Code formatter插件


    插件.png
  • 选择格式化工具


    格式化工具选择.png

    选择prettier.png
  • 设置vscode Ctrl+s保存代码时自动格式化代码


    配置保存自动格式化代码.png
vscode配置保存格式化.png
  • 配置团队中规定好的格式(项目根目录中新建.prettierrc.js文件,文件内部写入格式)
{
  "semi": true, // 需要分号
  "singleQuote": true, // 引号为单引号
  "trailingComma": "none" // 代码结尾不加逗号
}

tips:prettier格式化与eslint规范冲突的问题
按照我们的习惯可以更改eslint配置,也可以修改prettier配置,比如我的项目里习惯代码块结束加分号,但是eslint会产生代码报错。这种情况我们可以在eslint里rules配置项中加上 semi: 0 ,意思是不校验分号。同理我们也可以在prettier配置里修改 "semi": false 项。
其他配置项均可参考以上配置

4.2 项目启动报错

yarn serve启动项目的时候报错TypeError: this.setDynamic is not a function或者Plugin/Preset files are not allowed to export ob*jects, only functions
原因是vue里使用的是最新的babel7的版本,以@babel开头的包,但是我们升级前的项目里写的babel配置都是用的旧版本,所以在babel.config.js里需要把babel配置项都改成最新的即可,如下代码:

module.exports = {
  presets: [['@babel/preset-env', { modules: false }]],
  plugins: ['@babel/plugin-transform-runtime'],
  comments: false,
  env: {
    test: {
      presets: ['@babel/preset-env'],
      plugins: ['istanbul']
    }
  }
};

遇到类似babel的问题应该在babel配置项或者版本兼容性入手。

4.3 接入前端规范扫描

在我们的代码上传打包发版之前,需要接入前端静态扫描,以确保我们的项目在ship平台发布成功。入口:wiki前端静态扫描接入手册

五、项目发布

到这里,项目框架代码升级已经成功了,并且启动正常。现在要将最新的代码上传到git,再去ship平台进行部署。在ship平台构建过程中,遇到一些问题。

  1. 第一次打包时遇到报错Cannot read properties of undefined (reading 'NormalModule')
    推断是因为package.json文件中的一些包版本过高,而项目中使用的公司镜像版本低,所以当前的公司镜像中没有此类高版本的包。故而在Dockerfile文件中将镜像版本升级至 base-registry.zhonganinfo.com/env/node-nginx:v16.13.0-centos7-ngx1.20

  2. 第二次打包遇到报错,是因为包安装不成功。将Dockerfile文件中的 run npm install 换成 run npm install -legacy-peer-deps 。此步骤是因为npm7现在默认安装peerDependencies,升级过程中可能有一些模块安装时产生了版本冲突进而中断安装过程。这里使用-legacy-peer-deps参数绕过peerDependencies自动安装,这个参数会告诉NPM忽略项目中引入的相同模块不同版本的问题并继续安装,保证各个依赖间对自身所用不同版本模块共存

  3. 第三次打包再次遇到报错 Cannot read properties of undefined (reading 'NormalModule'),推测是因为vue.config.js文件中webpack配置有些问题,尝试删除less、less-loader等依赖

  4. 中间省略无数次打包,调整webpack配置项写法,均有报错

  5. 打包遇到报错 Cannot read properties of undefined (reading 'preProcessor'),直接把vue.config.js文件中webpack配置项中的pluginOptions项删除

  6. 第n次打包遇到报错 Cannot read properties of undefined (reading 'style-resources-loader',到这里已经快崩溃了,不知道到底哪里出了问题,看了下package.json文件中各个依赖版本,尝试将webpack降级

  7. 再次打包满心激动以为胜利就在眼前,结果又又又又报错了~,但是仔细一看是eslint报错,说是ant2中的v-model:value写法eslint校验不通过,所以在eslint配置中rules对象里加上一个取消eslint校验 'vue/no-v-model-argument': 'off' 。至此项目上线构建打包流程走通并部署成功

注:在部署构建阶段,本地开启和打包项目均无任何问题,一到上线打包就有报错。构建阶段数次崩溃,还好最后解决了所有问题,又进了一小步

至此,项目框架从vue2到vue3升级全部完成。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容