实操指南|如何用 OpenTiny Vue 组件库从 Vue 2 升级到 Vue 3

前言

根据 Vue 官网文档的说明,Vue2 的终止支持时间是 2023 年 12 月 31 日,这意味着从明年开始:

  • Vue2 将不再更新和升级新版本,不再增加新特性,不再修复缺陷

虽然 Vue3 正式版本已经发布快3年了,但据我了解,现在依然还有很多业务在使用 Vue2,迟迟没有升级 Vue3。

为什么要等到 Vue2 彻底停止维护,才考虑升级 Vue3 这个如此重要的问题呢???

本文是一篇 Vue2 升级 Vue3 的指南,主要包含以下部分:

  1. 使用 Vue CLI 搭建 Vue2 工程

  2. 使用 ElementUI 搭建表格、表单

  3. 使用 OpenTiny Vue 替换一个组件

  4. 使用 OpenTiny Vue 替换一个页面

  5. 使用 OpenTiny Vue 替换整个应用

  6. 使用 gogocode 升级到 Vue3,组件代码无需修改

创建 Vue 2项目

先用 Vue CLI 创建一个 Vue2 项目(也可以使用 Vite 配合 @vitejs/plugin-vue2vite-plugin-vue2 插件)。

shell 复制代码
// 安装 Vue CLI
npm install -g @vue/cli

// 创建 Vue2 项目
vue create vue2-demo

输出以下信息说明项目创建成功

shell 复制代码
🎉  Successfully created project vue2-demo.
👉  Get started with the following commands:

$ cd vue2-demo
$ yarn serve

创建好之后可以执行以下命令启动项目

shell 复制代码
yarn serve

输出以下命令说明启动成功

arduino 复制代码
App running at:
- Local:   http://localhost:8080/ 
- Network: http://192.168.1.102:8080/

效果如下

使用 ElementUI 搭建表格、表单

安装 VueRouter

shell 复制代码
npm i vue-router@3

main.js

js 复制代码
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'

const router = new VueRouter({
  routes: [
    {
      path: '/',
      component: () => import('./components/HomePage.vue')
    },
    {
      path: '/form',
      component: () => import('./components/FormPage.vue')
    },
    {
      path: '/list',
      component: () => import('./components/ListPage.vue')
    }
  ]
})

Vue.config.productionTip = false
Vue.use(VueRouter)

new Vue({
  router,
  render: h => h(App),
}).$mount('#app')

App.vue

html 复制代码
<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">

    <p>
      
      
      
      <router-link to="/">Home</router-link>
      <router-link to="/form">Form</router-link>
      <router-link to="/list">List</router-link>
    </p>
    
    
    <router-view></router-view>
  </div>
</template>

安装 ElementUI

shell 复制代码
npm i element-ui

在 src/views/FormPage.vue 中使用 ElementUI 组件,从 ElementUI 官网组件 demo 里面拷贝代码即可。

典型表单:element.eleme.io/#/zh-CN/com...

html 复制代码
<template>
<el-form ref="form" :model="form" label-width="80px">
  <el-form-item label="活动名称">
    <el-input v-model="form.name"></el-input>
  </el-form-item>
  <el-form-item label="活动区域">
    <el-select v-model="form.region" placeholder="请选择活动区域">
      <el-option label="区域一" value="shanghai"></el-option>
      <el-option label="区域二" value="beijing"></el-option>
    </el-select>
  </el-form-item>
  <el-form-item label="活动时间">
    <el-col :span="11">
      <el-date-picker type="date" placeholder="选择日期" v-model="form.date1" style="width: 100%;"></el-date-picker>
    </el-col>
    <el-col class="line" :span="2">-</el-col>
    <el-col :span="11">
      <el-time-picker placeholder="选择时间" v-model="form.date2" style="width: 100%;"></el-time-picker>
    </el-col>
  </el-form-item>
  <el-form-item label="即时配送">
    <el-switch v-model="form.delivery"></el-switch>
  </el-form-item>
  <el-form-item label="活动性质">
    <el-checkbox-group v-model="form.type">
      <el-checkbox label="美食/餐厅线上活动" name="type"></el-checkbox>
      <el-checkbox label="地推活动" name="type"></el-checkbox>
      <el-checkbox label="线下主题活动" name="type"></el-checkbox>
      <el-checkbox label="单纯品牌曝光" name="type"></el-checkbox>
    </el-checkbox-group>
  </el-form-item>
  <el-form-item label="特殊资源">
    <el-radio-group v-model="form.resource">
      <el-radio label="线上品牌商赞助"></el-radio>
      <el-radio label="线下场地免费"></el-radio>
    </el-radio-group>
  </el-form-item>
  <el-form-item label="活动形式">
    <el-input type="textarea" v-model="form.desc"></el-input>
  </el-form-item>
  <el-form-item>
    <el-button type="primary" @click="onSubmit">立即创建</el-button>
    <el-button>取消</el-button>
  </el-form-item>
</el-form>
</template>
<script>
  export default {
    data() {
      return {
        form: {
          name: '',
          region: '',
          date1: '',
          date2: '',
          delivery: false,
          type: [],
          resource: '',
          desc: ''
        }
      }
    },
    methods: {
      onSubmit() {
        console.log('submit!');
      }
    }
  }
</script>

效果如下

表格页面也一样。

src/views/ListPage.vue

html 复制代码
<template>
  <div>
    <div class="filter-bar">
      <el-select v-model="value" placeholder="请选择">
        <el-option
          v-for="item in options"
          :key="item.value"
          :label="item.label"
          :value="item.value"
        >
        </el-option>
      </el-select>
      <el-date-picker v-model="value1" type="daterange"> </el-date-picker>
      <el-input
        v-model="search"
        placeholder="输入关键字搜索"
        style="width: 300px"
      />
      <el-button>搜索</el-button>
    </div>
    <el-table :data="tableData" style="width: 100%">
      <el-table-column prop="date" label="日期" width="180"> </el-table-column>
      <el-table-column prop="name" label="姓名" width="180"> </el-table-column>
      <el-table-column prop="address" label="地址"> </el-table-column>
    </el-table>
  </div>
</template>

<script>
export default {
  data() {
    return {
      value1: '',
      options: [
        {
          value: '选项1',
          label: '王小虎'
        },
        {
          value: '选项2',
          label: '张三'
        },
        {
          value: '选项3',
          label: '李小萌'
        },
        {
          value: '选项4',
          label: '令狐冲'
        }
      ],
      value: '',
      search: '',
      tableData: [
        {
          date: '2016-05-02',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1518 弄'
        },
        {
          date: '2016-05-04',
          name: '张三',
          address: '上海市普陀区金沙江路 1517 弄'
        },
        {
          date: '2016-05-01',
          name: '李小萌',
          address: '上海市普陀区金沙江路 1519 弄'
        },
        {
          date: '2016-05-03',
          name: '令狐冲',
          address: '上海市普陀区金沙江路 1516 弄'
        }
      ]
    }
  }
}
</script>

<style lang="less" scoped>
.filter-bar {
  display: flex;

  & > * {
    margin-right: 20px;
  }
}
</style>

效果如下

首页可以放一张轮播图。

src/views/HomePage.vue

html 复制代码
<template>
  <el-carousel>
    <el-carousel-item v-for="item in 4" :key="item">
      <img :src="`https://picsum.photos/1350/900?random=${item}`" style="width: 100%;">
    </el-carousel-item>
  </el-carousel>
</template>

<style>
  .el-carousel__item h3 {
    color: #475669;
    font-size: 14px;
    opacity: 0.75;
    line-height: 150px;
    margin: 0;
  }

  .el-carousel__item:nth-child(2n) {
     background-color: #99a9bf;
  }
  
  .el-carousel__item:nth-child(2n+1) {
     background-color: #d3dce6;
  }
</style>

效果如下

参考:

****

使用 OpenTiny Vue 替换一个组件

OpenTiny Vue 的组件都是支持按需引入的,一开始我们步子不要迈得太大,先尝试替换一个 Button 组件。

安装 @opentiny/vue@2

shell 复制代码
npm i @opentiny/vue@2

表单页面里面有两个按钮,我们尝试将其替换成 OpenTiny Vue 的 Button 组件。

替换的步骤很简单,不需要修改现有的代码,只需要增加4行代码即可。

src/views/FormPage.vue

diff 复制代码
<template>
<el-form ref="form" :model="form" label-width="80px">
  <el-form-item label="活动名称">
    <el-input v-model="form.name"></el-input>
  </el-form-item>
  ...
  <el-form-item>
    <el-button type="primary" @click="onSubmit">立即创建</el-button>
    <el-button>取消</el-button>
  </el-form-item>
</el-form>
</template>
<script>
+  import { Button } from '@opentiny/vue'

  export default {
+    components: {
+      ElButton: Button
+    },
    data() {
      return {
        form: {
          name: '',
          ...
        }
      }
    },
    methods: {
      onSubmit() {
        console.log('submit!');
      }
    }
  }
</script>

效果如下

使用 OpenTiny Vue 替换一个页面

接下来我们步子逐渐迈大一点,将整个 FormPage 页面的 ElementUI 组件全部替换成 OpenTiny Vue 的组件。

FormPage 页面一共有以下组件:

  • Button
  • Form
  • FormItem
  • Input
  • Select
  • Option
  • Col
  • DatePicker
  • TimePicker
  • Switch
  • CheckboxGroup
  • Checkbox
  • RadioGroup
  • Radio

替换的方式和前面替换 Button 组件一模一样,只需要多加一些组件。

diff 复制代码
<template>
<el-form ref="form" :model="form" label-width="80px">
  <el-form-item label="活动名称">
    <el-input v-model="form.name"></el-input>
  </el-form-item>
  ...
  <el-form-item>
    <el-button type="primary" @click="onSubmit">立即创建</el-button>
    <el-button>取消</el-button>
  </el-form-item>
</el-form>
</template>
<script>
  import {
    Button,
+    Form,
+    FormItem,
+    Input,
+    Select,
+    Option,
+    Col,
+    DatePicker,
+    TimePicker,
+    Switch,
+    CheckboxGroup,
+    Checkbox,
+    RadioGroup,
+    Radio,
  } from '@opentiny/vue'

  export default {
    components: {
      ElButton: Button,
+      ElForm: Form,
+      ElFormItem: FormItem,
+      ElInput: Input,
+      ElSelect: Select,
+      ElOption: Option,
+      ElCol: Col,
+      ElDatePicker: DatePicker,
+      ElTimePicker: TimePicker,
+      ElSwitch: Switch,
+      ElCheckboxGroup: CheckboxGroup,
+      ElCheckbox: Checkbox,
+      ElRadioGroup: RadioGroup,
+      ElRadio: Radio,
    },
    data() {
      return {
        form: {
          name: '',
          ...
        }
      }
    },
    methods: {
      onSubmit() {
        console.log('submit!');
      }
    }
  }
</script>

效果如下

使用 OpenTiny Vue 替换整体应用

最后一步就是使用 OpenTiny Vue 替换整个应用的 ElementUI。

我们可以用前面的方法进行替换,但考虑到整个应用的页面众多,我们采取另一种方式。

我们已经全局注册了 ElementUI 组件库,接下来我们全局注册 OpenTiny Vue 组件库。

diff 复制代码
import Vue from 'vue'
- import ElementUI from 'element-ui'
- import 'element-ui/lib/theme-chalk/index.css'
+ import TinyVue from '@opentiny/vue'
import App from './App.vue'
import VueRouter from 'vue-router'

- Vue.use(ElementUI)
+ Vue.use(TinyVue)

const router = new VueRouter({
  routes: [
    ...
  ]
})

Vue.config.productionTip = false
Vue.use(VueRouter)

new Vue({
  router,
  render: (h) => h(App)
}).$mount('#app')

然后全局替换 el-tiny-,一步到位!

效果如下

首页轮播

表单

表格

是不是非常丝滑,更丝滑的还在后面!

接下来我们将借助一款神器:gogocode,实现 Vue2 项目平滑升级 Vue3。

使用 gogocode 升级到 Vue3

安装 gogocode:

shell 复制代码
npm install gogocode-cli -g

转换源码:

shell 复制代码
gogocode -s ./src/ -t gogocode-plugin-vue -o ./src/

升级依赖:

shell 复制代码
gogocode -s package.json -t gogocode-plugin-vue -o package.json

升级 TinyVue 组件库到 3.0 版本

shell 复制代码
npm i @opentiny/vue@3

组件代码无需做任何修改,完成 Vue2 项目平滑升级到 Vue3 🎉

执行 npm run dev 命令启动项目,除了 Vue 版本号变化之后,其他任何效果都没有变化。

首页轮播

表单

表格

遇到的问题

问题一:error 'v-model' directives require no argument vue/no-v-model-argument

解决方法:修改 FormPage.vue 中的 v-model:value 为 v-model 即可

问题二:Failed to resolve component: router-link

解决方案:修改 main.js 中 use(router) 代码顺序即可

diff 复制代码
window.$vueApp = Vue.createApp(App)

window.$vueApp.mount('#app')
import * as Vue from 'vue'
import TinyVue from '@opentiny/vue'
import App from './App.vue'
import * as VueRouter from 'vue-router'

- window.$vueApp.use(TinyVue)

const router = VueRouter.createRouter({
  history: VueRouter.createWebHashHistory(),
  routes: [
    ...
  ],
})

window.$vueApp = Vue.createApp(App)
+ window.$vueApp.use(TinyVue)
+ window.$vueApp.use(router) // 这一行代码需要放到 mount 之前
window.$vueApp.mount('#app')
window.$vueApp.config.globalProperties.routerAppend = (path, pathToAppend) => {
  return path + (path.endsWith('/') ? '' : '/') + pathToAppend
}

- window.$vueApp.use(router)

如果你在升级 Vue3 的过程中遇到任何问题,欢迎在评论区进行讨论,也欢迎添加 OpenTiny 小助手 opentiny-official 与我们交流!

本文涉及到的源码链接:

Element 升级 OpenTiny 的 demo 项目在 packages/element-to-opentiny 子包里。

  • vue2 项目在 vue2 分支

  • vue3 项目在 vue3 分支

关于 OpenTiny

OpenTiny 是一套企业级 Web 前端开发解决方案,提供跨端、跨框架的UI组件库,适配 PC 端 / 移动端等多端,支持 Vue2 / Vue3 / Angular 多技术栈,拥有灵活扩展的低代码引擎,包含主题配置系统 / 中后台模板 / CLI 命令行等丰富的效率提升工具,可帮助开发者高效开发 Web 应用。

核心亮点:

  • 跨端跨框架: 使用 Renderless 无渲染组件设计架构,实现了一套代码同时支持 Vue2 / Vue3,PC / Mobile 端,并支持函数级别的逻辑定制和全模板替换,灵活性好、二次开发能力强。

  • 组件丰富:PC 端有100+组件,移动端有30+组件,包含高频组件 Table、Tree、Select 等,内置虚拟滚动,保证大数据场景下的流畅体验,除了业界常见组件之外,我们还提供了一些独有的特色组件,如:Split 面板分割器、IpAddress IP 地址输入框、Calendar 日历、Crop 图片裁切等。

  • 低代码引擎:低代码引擎使能开发者定制低代码平台。它是低代码平台的底座,提供可视化搭建页面等基础能力,既可以通过线上搭配组合,也可以通过下载源码进行二次开发,实时定制出自己的低代码平台。适用于多场景的低代码平台开发,如:资源编排、服务端渲染、模型驱动、移动端、大屏端、页面编排等。

  • 配置式组件: 组件支持模板式和配置式两种使用方式,适合低代码平台,目前团队已经将 OpenTiny 集成到内部的低代码平台,针对低码平台做了大量优化。

  • 周边生态齐全: 提供了基于 Angular + TypeScript 的 TinyNG 组件库,提供包含 10+ 实用功能、20+ 典型页面的 TinyPro 中后台模板,提供覆盖前端开发全流程的 TinyCLI 工程化工具,提供强大的在线主题配置平台 TinyTheme。


欢迎加入 OpenTiny 开源社区。添加微信小助手:opentiny-official 一起参与交流前端技术~

OpenTiny 官网:opentiny.design/

OpenTiny 代码仓库:github.com/opentiny/

TinyVue 源码: github.com/opentiny/ti...

TinyEngine 源码: github.com/opentiny/ti...

欢迎进入代码仓库 Star🌟TinyEngine、TinyVue、TinyNG、TinyCLI~

如果你也想要共建,可以进入代码仓库,找到 good first issue标签,一起参与开源贡献~

往期文章推荐

1、低代码引擎 TinyEngine 正式发布!!!

2、必不可少的UI组件一------组件的基础知识

3、OpenTiny Vue 3.10.0 版本发布:组件 Demo 支持 Composition 写法,新增4个新组件

4、前端Vuer,请收好这份《Vue组件单元测试》宝典

5、OpenTiny 前端组件库正式开源啦!面向未来,为开发者而生

6、从自研走向开源的 TinyVue 组件库

7、我要做开源,提交我的第一个PR

相关推荐
浮华似水12 分钟前
Javascirpt时区——脱坑指南
前端
王二端茶倒水15 分钟前
大龄程序员兼职跑外卖第五周之亲身感悟
前端·后端·程序员
_oP_i20 分钟前
Web 与 Unity 之间的交互
前端·unity·交互
钢铁小狗侠22 分钟前
前端(1)——快速入门HTML
前端·html
凹凸曼打不赢小怪兽1 小时前
react 受控组件和非受控组件
前端·javascript·react.js
狂奔solar1 小时前
分享个好玩的,在k8s上部署web版macos
前端·macos·kubernetes
qiyi.sky1 小时前
JavaWeb——Web入门(8/9)- Tomcat:基本使用(下载与安装、目录结构介绍、启动与关闭、可能出现的问题及解决方案、总结)
java·前端·笔记·学习·tomcat
清云随笔1 小时前
axios 实现 无感刷新方案
前端
鑫宝Code1 小时前
【React】状态管理之Redux
前端·react.js·前端框架
忠实米线2 小时前
使用pdf-lib.js实现pdf添加自定义水印功能
前端·javascript·pdf