IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Kagol发表于 2023-10-19 22:23:57
    love 0

    你好,我是 Kagol。

    前言

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

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

    image.png

    虽然 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,组件代码无需修改

    1 创建 Vue2 项目

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

    // 安装 Vue CLI
    npm install -g @vue/cli
    
    // 创建 Vue2 项目
    vue create vue2-demo

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

    🎉  Successfully created project vue2-demo.
    👉  Get started with the following commands:
    
    $ cd vue2-demo
    $ yarn serve

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

    yarn serve

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

    App running at:
    - Local:   http://localhost:8080/ 
    - Network: http://192.168.1.102:8080/

    效果如下

    图片

    2 使用 ElementUI 搭建表格、表单

    安装 VueRouter

    npm i vue-router@3

    main.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

    <template>
      <div id="app">
        <img alt="Vue logo" src="./assets/logo.png">
    
        <p>
          <!-- use the router-link component for navigation. -->
          <!-- specify the link by passing the `to` prop. -->
          <!-- `<router-link>` will render an `<a>` tag with the correct `href` attribute -->
          <router-link to="/">Home</router-link>
          <router-link to="/form">Form</router-link>
          <router-link to="/list">List</router-link>
        </p>
        <!-- route outlet -->
        <!-- component matched by the route will render here -->
        <router-view></router-view>
      </div>
    </template>

    安装 ElementUI

    npm i element-ui

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

    典型表单:https://element.eleme.io/#/zh-CN/component/form#dian-xing-bia...

    <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>

    效果如下

    image.png

    表格页面也一样。

    src/views/ListPage.vue

    <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>

    效果如下

    image.png

    首页可以放一张轮播图。

    src/views/HomePage.vue

    <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>

    效果如下

    image.png

    参考:

    • 表单:https://element.eleme.io/#/zh-CN/component/form
    • 表格:https://element.eleme.io/#/zh-CN/component/table
    • 轮播:https://element.eleme.io/#/zh-CN/component/carousel

    3 使用 OpenTiny Vue 替换一个组件

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

    安装 @opentiny/vue@2

    npm i @opentiny/vue@2

    image.png

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

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

    src/views/FormPage.vue

    <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>

    效果如下

    image.png

    4 使用 OpenTiny Vue 替换一个页面

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

    FormPage 页面一共有以下组件:

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

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

    <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>

    效果如下

    image.png

    5 使用 OpenTiny Vue 替换整体应用

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

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

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

    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-,一步到位!

    image.png

    效果如下

    首页轮播

    image.png

    表单

    image.png

    表格

    image.png

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

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

    6 使用 gogocode 升级到 Vue3

    安装 gogocode:

    npm install gogocode-cli -g

    转换源码:

    gogocode -s ./src/ -t gogocode-plugin-vue -o ./src/

    升级依赖:

    gogocode -s package.json -t gogocode-plugin-vue -o package.json

    升级 TinyVue 组件库到 3.0 版本

    npm i @opentiny/vue@3

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

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

    首页轮播

    image.png

    表单

    image.png

    表格

    image.png

    遇到的问题

    问题一: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) 代码顺序即可

    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 与我们交流!

    本文涉及到的源码链接:

    • https://github.com/opentiny/cross-framework-component

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

    • vue2 项目在 vue2 分支
    • vue3 项目在 vue3 分支

    关于 OpenTiny

    OpenTiny 是一套华为云出品的企业级组件库解决方案,适配 PC 端 / 移动端等多端,涵盖 Vue2 / Vue3 / Angular 多技术栈,拥有主题配置系统 / 中后台模板 / CLI 命令行等效率提升工具,可帮助开发者高效开发 Web 应用。

    image.png

    核心亮点:

    1. 跨端跨框架:使用 Renderless 无渲染组件设计架构,实现了一套代码同时支持 Vue2 / Vue3,PC / Mobile 端,并支持函数级别的逻辑定制和全模板替换,灵活性好、二次开发能力强。
    2. 组件丰富:PC 端有80+组件,移动端有30+组件,包含高频组件 Table、Tree、Select 等,内置虚拟滚动,保证大数据场景下的流畅体验,除了业界常见组件之外,我们还提供了一些独有的特色组件,如:Split 面板分割器、IpAddress IP地址输入框、Calendar 日历、Crop 图片裁切等
    3. 配置式组件:组件支持模板式和配置式两种使用方式,适合低代码平台,目前团队已经将 OpenTiny 集成到内部的低代码平台,针对低码平台做了大量优化
    4. 周边生态齐全:提供了基于 Angular + TypeScript 的 TinyNG 组件库,提供包含 10+ 实用功能、20+ 典型页面的 TinyPro 中后台模板,提供覆盖前端开发全流程的 TinyCLI 工程化工具,提供强大的在线主题配置平台 TinyTheme

    欢迎加入 OpenTiny 开源社区。

    添加微信小助手:opentiny-official,一起参与共建!

    OpenTiny 官网:https://opentiny.design/

    Vue组件库:https://opentiny.design/tiny-vue

    Angular组件库:https://opentiny.design/tiny-ng

    OpenTiny 代码仓库:https://github.com/opentiny/ (欢迎 Star ⭐)

    往期文章推荐

    • ✨GaoNeng:我是如何为OpenTiny贡献新组件的?
    • ✨xiaoy:但因热爱,愿迎万难,OpenTiny 社区增加一枚前端程序媛贡献者
    • ✨贡献者招募:前端Vuer,请收好这份《Vue组件单元测试》宝典,给自己多一些安全感
    • 🎉OpenTiny Vue 3.10.0 版本发布:组件 Demo 支持 Composition 写法,新增4个新组件
    • 🎉OpenTiny 前端组件库正式开源啦!面向未来,为开发者而生
    • 🎉从自研走向开源的 TinyVue 组件库
    • 🌈一个 OpenTiny,Vue2 Vue3 都支持!
    • 🌈历史性的时刻!OpenTiny 跨端、跨框架组件库正式升级 TypeScript,10 万行代码重获新生!
    • 🌈如何启动我的第一次开源贡献(如果你之前没有参加过开源贡献,请阅读这篇文章)


沪ICP备19023445号-2号
友情链接