Vue2降级Vue3实际
一、降级前筹备
在正式降级前,能够提前解决一些已兼容的小批改以及被移除的API。
deep 款式穿透
历史所有的 >>>
|| /deep/
|| ::v-deep
款式穿透,更改为 :deep()
。
在 Vue 2.7 中,运行我的项目会提醒 deep
相干问题,尽管我的项目还能失常启动,但会在控制台报正告信息。
// 历史写法/deep/ .el-card__body { padding: 20px 20px 0;}// 新版写法:deep(.el-card__body){ padding: 20px 20px 0;}
inline-template 属性
移除 inline-template
标识,在 Vue 3 中,inline-tempalte
属性将会被移除,不再反对该用法了,如果必须应用可用 <script>
或者默认 Slot
代替。
slot 插槽
Vue 3 中引入了一个新的指令 v-slot
,用来示意具名插槽和默认插槽(Vue2.6 中已反对)。如果在我的项目中依然应用废除的具名/作用域插槽语法,请先将其更新至最新的语法。
v-bind
在 Vue 2 中,如果一个元素同时定义了 v-bind="object"
和一个雷同的独自的属性,那么这个独自的属性总是会笼罩 object
中的绑定。
在 Vue 3 中,v-bind=“object"
是程序敏感的,申明绑定的程序决定了它们如何合并,需确保 v-bind
先定义,再定义各个属性。
// Vue 2<div id="aaa" v-bind="{ id: 'bbb' }"></div><div id="bbb"></div> // 渲染后果// Vue 3<div id="aaa" v-bind="{ id: 'bbb' }"></div><div id="bbb"></div> // 渲染后果<div v-bind="{ id: 'bbb' }" id="aaa"></div><div id="aaa"></div> // 渲染后果
v-for
筛查所有 v-for
中应用 ref
的中央,将 ref
绑定为一个函数。
在 Vue 2 中,在 v-for
语句中应用 ref
属性时,会生成 refs
数组插入 $refs
属性中。
在 Vue 3 中,在 v-for
语句中应用 ref
属性时,将不再会主动在 $refs
中创立数组。而是将 ref
绑定到一个 function
中,在 function
中能够灵活处理 ref
。
keyCodes
在 Vue 2 中,应用数字 (即键码) 作为 v-on
的修饰符。
在 Vue 3 中,弃用了 keyCodes,能够更改为别名作为 v-on
的修饰符(Web规范中 KeyboardEvent.keyCode 已被废除)。
<input v-on:keyup.13="submit" /> // 已弃用<input v-on:keyup.enter="submit" />
Data 选项
在 Vue 2 中 ,申明 data
反对对象模式 || 函数模式。
在 Vue 3 中,对 data
的申明进行了标准化,只反对函数模式申明。
filters 过滤器
在 Vue 3 中,移除且不再反对 filters
,如果之前我的项目中须要实现过滤性能,能够通过 computed
或 methods
实现。
如果须要应用全局过滤器,能够借助 globalProperties
来注册全局过滤器。
// lib/format.jsexport default { formatCooperateStatus (status) { const map = { applied: '待声援', assigning: '指派中', partialRefuse: '从新指派', process: '合作中', refuse: '已退回', tested: '合作实现' } return map[status] || '' }}// main.js// Vue 2import Format from './lib/format.js'Vue.prototype.$format = Format// Vue 3import Format from './lib/format.js'const app = createApp(App)app.config.globalProperties.$format = Format
watch 监听器
筛查所有 watch
监听,如果监听参数为数组,须要设置 deep: true
。
在 Vue 3 中,只有当数组被替换时,回调才会触发,如果想要数组在产生扭转时被 Vue 辨认到,则必须指定 deep
选项。
watch: { checkedMethod: { handler (curValue) { this.onChange(curValue) }, deep: true, immediate: true }}
$children
在 Vue 3 中,移除了 $children
实例,如果须要拜访子组件实例可用 $refs
实现。
$destroy
在 Vue 3 中,移除了 $destroy
实例,不应该手动治理单个 Vue
组件的生命周期。
二、构建工具
从 Vue CLI 迁徙为 Vite,Vue 3 举荐应用 Vite
作为构建工具,Vite
缩短了开发服务器的启动工夫,它实现了按需编译,不再须要期待整个利用编译实现。
PS: Vite 须要 Node.js 版本 >= 12.0.0
迁徙过程
1. 装置
npm i vite -g
2. 构建新的vite我的项目
npm create vite@latestcd xxxnpm installnpm run dev
构建的初始我的项目构造如下:
用原始我的项目的 src
文件替换新构建我的项目的 src
目录,而后再持续接下来的重构操作。
3. 迁徙package文件及配置项
依据我的项目需要迁徙 package.json文件
,并移除 Vue CLI
相干依赖项。
初始文件如下,Vite
默认构建的我的项目为 Vue 3 我的项目。
批改 vite.config.js
文件,vue.config.js --> vite.config.js
初始文件配置如下
批改文件配置,例:
import { resolve } from 'path'import { defineConfig } from 'vite'import vue from '@vitejs/plugin-vue'// https://vitejs.dev/config/export default defineConfig({ plugins: [vue()], resolve: { alias: { // 配置门路别名 '@': resolve('src') }, }, server: { open: true, host: XXX, port: XXX, https: false, proxy: { '/api': { target: XXX, secure: false, changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, ''), }, }, '/casefiles': { target: XXX, secure: false, changeOrigin: true }})
4. 更新环境变量
process.env
--> import.meta.env
Vite
在一个非凡的 import.meta.env
对象上裸露环境变量。
5. 文件引入
@vue/cli
中反对无扩展名的 vue文件导入,加不加 .vue
后缀都会被正确辨认。
在 Vite
的设计中,import xxx from "./xxx.vue"
能力正确导入,必须确保单个文件组件的所有导入都以扩展名结尾。
6. 批改CommonJS语法
Vite
应用 ES Modules
作为模块化计划,因而不反对应用 require
形式来导入模块。
三、HTML 文件与入口文件
1. index.html
./public/index.html
--> ./index.html
Vite
我的项目的 HTML文件
是放在我的项目根目录下的,同时在 Vite
中,JavaScript
应用程序不再是主动注入的,main.js
文件须要手动引入。
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Vite + Vue</title> </head> <body> <div id="app"></div> <script type="module" src="/src/main.js"></script> </body></html>
2. main.js
创立并挂载根实例
创立实例,new Vue
--> createApp()
挂载形式,$mount()
--> mount()
增加全局属性和办法,Vue.prototype
--> app.config.globalProperties
// Vue 2import Vue from 'vue'import 'element-ui/lib/theme-chalk/index.css'import ElementUI from 'element-ui'import App from './App'import router from './router'import Vuex from 'vuex'import store from './store'import '@/assets/styles/common.scss'import '@/assets/styles/main.scss'import '@/assets/fonts/iconfont.css'import './lib/filters.js'import Format from './lib/format.js'Vue.prototype.$format = FormatVue.config.productionTip = falseVue.use(ElementUI)Vue.use(Vuex)new Vue({ router, store, render: h => h(App)}).$mount('#app')// Vue 3import { createApp } from 'vue'import App from './App.vue'import 'element-plus/dist/index.css'import ElementPlus from 'element-plus'import locale from 'element-plus/lib/locale/lang/zh-cn'import * as ElementPlusIconsVue from '@element-plus/icons-vue'import router from './router'import store from './store'import '@/assets/styles/common.scss'import '@/assets/styles/main.scss'import '@/assets/fonts/iconfont.css'import Format from './lib/format.js'const app = createApp(App)app.config.globalProperties.$format = Formatfor (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component)}app.use(ElementPlus, { locale } ).use(router).use(store)app.mount('#app')
四、语法和API
以下只是简略例举了几个罕用的变动,对于语法和API的具体变更能够参见Vue官网文档:Vue2迁徙
v-model
排查没有修饰符的 v-model
,别离将 prop
和 event
命名更改为 modelValue
和 update:modelValue
。
v-bind
的 .sync
修饰符和组件的 model
选项已移除,更改为 v-model:value
。
<template\>
移除没有指令的 <template>
,Vue 3 对组件的写法做了调整,反对多个根节点。
在 Vue 2 中,<template>
标签不能增加 key
属性,而是在其子元素上增加 key
。
在 Vue 3 中,key
应该被设置在 <template>
标签上。
// Vue 2<template v-for="item in list"> <span :key="item.value">...</span></template>// Vue 3<template v-for="item in list" :key="item.id"> <span>...</span></template>
key
在 Vue 2 中,倡议在 v-if
/ v-else
/ v-else-if
上增加 key
。
在 Vue 3 中,Vue 会主动生成惟一 key
(仍旧承受历史写法,然而不再被举荐应用)。
// Vue 2<div v-if="isAdmin" key="aaa"> aaa </div><div v-else key="bbb"> bbb </div>// Vue 3<div v-if="isAdmin"> aaa </div><div v-else> bbb </div>
v-if && v-for
v-if
和 v-for
在同一个元素身上应用时的优先级产生了变动。
在 Vue 3 中,如果在一个元素上同时应用 v-if
和 v-for
,v-if
的优先级高于 v-for
(不举荐这样应用)。
functional
在 Vue 3 中,{ functional: true }
选项已从通过函数创立的组件中移除,移除配置中 { functional: true }
和模板中 <template functional>
的 functional
。
defineAsyncComponent 辅助函数
在 Vue 3 中异步组件通过 defineAsyncComponent
办法创立。
// Vue 2const asyncComponent = () => import('./async-component.vue')// Vue 3const asyncComponent = defineAsyncComponent(() => import('./async-component.vue'))
defineAsyncComponent
中 component
选项更名为 loader
,同时 loader
函数移除了 resolve
和 reject
参数,必须手动返回 Promise
。
// Vue 2const asyncComponent = (resolve, reject) => { ...}// Vue 3const asyncComponent = defineAsyncComponent( () => new Promise((resolve, reject) => { ... }))
.native 修饰符
在 Vue 3 中,移除了 v-on: event.native
修饰符。(PS: Vue 3 中新增 emits
选项,所有未在组件 emits
选项中定义的事件作为原生事件增加到子组件的根元素中,除非子组件选项中设置了 inheritAttrs: false
)
// Vue 2<my-component v-on:close="handleComponentEvent" v-on:click.native="handleNativeClickEvent"/>// Vue 3<my-component v-on:close="handleComponentEvent" v-on:click="handleNativeClickEvent"/>// MyComponent.vue<script> export default { emits: ['close'] }</script>
$scopedSlots
在 Vue 3 中,移除了 $scopedSlots
,对立了 $scopedSlots
和 $slots
,所有插槽都通过 $slots
作为函数裸露。
$listeners
在 Vue 3 中,曾经弃用 $listeners
对象,将事件监听器并入 $attrs
,作为 $attrs
的一部分。
$attrs
在 Vue 3 中,$attrs
将蕴含传递给组件的所有属性,包含 class
和 style
属性 。
$set() 和 $delete()
在 Vue 2 中,批改某一些数据,视图是不能及时从新渲染的,因而提供了一些变异的办法,比方 $set
、$delete
。
在 Vue 3 中,移除了 $set
和 $delete
,基于代理的变化检测曾经不再须要它们了。
<el-input v-model="form.no" placeholder="请输出编号"></el-input>// Vue 2export default { data() { return{ form:{ no:'11' } } } mounted () { this.form.no = 'CaseNo' // 视图不变 this.$set(this.form, 'no', 'CaseNo') // 视图更新 }}// Vue3export default { data() { return{ form:{ no:'11' } } } mounted() { this.form.no = 'CaseNo' // 视图更新 }}
Events API
在 Vue 3 中,移除了 $on
、 $off
和 $once
这三个事件相干的API,不再反对事件发射器接口,能够应用内部库来实现事件总线,例如 mitt
。
// @/lib/bus.jsimport mitt from 'mitt'const bus = new mitt()export default bus// 应用import Bus from '@/lib/bus'export default { mounted () { Bus.off('loadMore') Bus.on('loadMore', this.initActivity) }}
Mixin
在 Vue 2 中,data
的合并是深拷贝模式。
在 Vue 3 中,当组件的 data
与 mixin
或 extends
的 data
进行合并时,只进行浅拷贝。
watch 监听器
在 Vue 3 中,watch
不再反对点分隔字符串门路,能够将监听的参数更改为 computed
。
Composition API
Options API
--> Composition API
在 Vue 2 中,应用的是 Options API
,代码逻辑比拟扩散,可读性差,可维护性差。
在 Vue 3 中,应用的是 Composition API
,代码逻辑明显,可维护性更高。
因为波及到所有页面和组件,批改起来变更过大,同时目前 Vue 3 也是兼容了 Options
写法,所以这部分代码构造能够先不必更改。之后新增的页面和组件能够依照 Vue 3 新增的 composition API
构造来写,其余内容能够前期逐渐批改。
详情请参考 Vue 3 官网文档
五、生命周期
vue2 | vue3 (Options) | vue3 (Composition) |
---|---|---|
beforeCreate | beforeCreate | setup()外部 |
created | created | setup()外部 |
beforeMount | beforeMount | onBeforeMount |
mounted | mounted | onMounted |
beforeUpdate | beforeUpdate | onBeforeUpdate |
updated | updated | onUpdated |
beforeDestroy | beforeUnmount | onBeforeUnmount |
destroyed | unmounted | onUnmounted |
六、路由
Vue 2 应用的是 router3.x
的API,换成 Vue3 须要用 router4.x
的API。
Vue Router 从 v3 到 v4 在迁徙的过程中可能改变中央比拟多,这里只是简略例举了几个常见的变动,详情能够参考 官网文档。
1. 装置
npm install vue-router@4
2. new Router 变成 createRouter
new Router()
--> createRouter 函数
3. 破除了mode选项配置
history
--> createWebHistory
hash
--> createWebHashHistory
abstract
--> createMemoryHistory
// v3import Router from 'vue-router'Vue.use(Router)const router = new Router({ mode: 'history', routes})export default router// v4import { createRouter, createWebHistory } from 'vue-router'const router = createRouter({ history: createWebHistory(), routes})export default router
4. 勾销(*)通配符路由
// v3{ path: '*', name: '404', component: NotFound, meta: { title: '404' }}// v4{ path: '/:pathMatch(.*)*', name: '404', component: NotFound, meta: { title: '404' }}
5. 新增useRoute、useRouter
路由信息, this.$route
--> useRoute()
操作路由, this.$router
--> useRouter()
// Options写法this.$router.push({ path: this.$route.path, query: query })// Composition写法import { useRoute, useRouter } from 'vue-router'export default defineComponent ({ setup(props, ctx) { const route = useRoute() const router = useRouter() Router.push({ path: Route.path, query: query }) }})
七、状态治理
1. 装置
npm install vuex@next
2. 创立store对象并导出store
new Vuex.Store()
--> createStore()
// store/index.jsimport Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({ state: { }, mutations: { }, actions: { }})import {createStore} from 'vuex'export default createStore({ state: { }, mutations: { }, actions: { }})// main.js...import store from './store'...app.use(store)
3. 获取 store 实例对象
this.$store
--> useStore()
// Options写法const user = this.$store.user// Composition写法import { useStore } from 'vuex'export default defineComponent ({ setup(props, ctx) { const Store = useStore() const user = Store.state.user }})
八、我的项目依赖
将我的项目中所应用到的 UI框架 和 第三方插件 切换成对应的 Vue 3 版本,相应用法可能也须要变更。
Element UI 降级为 Element Plus
PS: Element UI 降级为 Element Plus 后,可能会产生很多破坏性的更改,界面款式须要从新调整。
以下简略例举了几个常见的变动,详情能够参考 官网文档。
Icon 图标
// Element UI<i class="el-icon-plus"></i>// Element Plus<el-icon> <Plus /></el-icon>
Dialog 对话框
:visible.sync
--> v-model
// Element UI<el-dialog title="提醒" :visible.sync="dialogVisible" :before-close="handleClose"> ...</el-dialog>// Element Plus<el-dialog v-model="dialogVisible" title="提醒" :before-close="handleClose"> ...</el-dialog>
Pagination 分页
:current-page.sync
--> v-model:currentPage
// Element UI<el-pagination layout="total, sizes, prev, pager, next, jumper" :page-sizes="[10, 20, 50, 100]" @size-change="handleSizeChange" :total="total" :page-size="limit" :current-page.sync="filter.page" @current-change="changePage"><el-pagination>// Element Plus<el-pagination layout="total, sizes, prev, pager, next, jumper" :page-sizes="[10, 20, 50, 100]" @size-change="handleSizeChange" :total="total" :page-size="filter.limit" v-model:currentPage="filter.page" @current-change="changePage"><el-pagination>
Button 按钮
size:medium / small / mini
--> large / default / small
text:type="text"
--> text: boolean
// Element UI<el-button type="text" disabled>文字按钮</el-button>// Element Plus<el-button text disabled>文字按钮</el-button>
Message 音讯提醒
// Element UIthis.$message.success('增加胜利')// Element Plus// 引入import { ElMessage } from "element-plus"// 应用ElMessage.success('增加胜利') // 应用
Message Box 弹框
// Element UIthis.$confirm('是否确认删除该文件?', '提醒', { confirmButtonText: '确定', cancelButtonText: '勾销', type: 'warning'}).then(async () => { ...})// Element Plus// 引入import { ElMessageBox } from "element-plus"// 应用ElMessageBox.confirm('是否确认删除该文件?', '提醒', { confirmButtonText: '确定', cancelButtonText: '勾销', type: 'warning'}).then(async () => { ...})
九、VS Code 扩大
Vetur
--> Volar
。
十、其余
我的项目代码编译无误胜利启动后,需点击 所有界面 进行测试,在启动的时候,Vite
并不会打包源码,而是在浏览器申请路由时才会进行打包,而且也仅仅打包以后路由的源码,故当某个子页面出错时可能我的项目启动、运行时都失常。
同时也须要查看代码中的业务是否正确,降级过后可能会对原有代码逻辑产生影响,例如表单校验等性能,业务逻辑须要逐个比对和测试。