前言(升级的难言之隐)

为什么项目上会有vue2升级到vue3的需求?vue2.0不香了吗?

首先回答,vue2.0依旧还是很香的。原因只是笔者的项目最早只有一个管理系统使用2.0搭建,后续添加门户使用最新的vue3.0搭建。然而需求总是出人意料的出现,门户与管理系统共用顶部菜单及个人中心,这样两个系统合二为一就势必要迈出脚步。既然要写出这篇文章,那我们就从头到尾讲述一下vue2.0 + element-uivue3.0 + element-plus的一个过程及遇到的问题。

声明:vue3.0版本推荐使用组合式api但兼容vue2.0版本的选项式api语法,故本文只讲解vue3.0对vue2.0中不相兼容的地方。

Vue的内容切换

  • vue3创建vue实例全局注册element-plus
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
//通过createApp方法生成vue实例
const app = createApp(App)
//app.use()注册全局组件
app.use(ElementPlus)
//app.mount(dom)将app实例挂载在dom元素上
app.mount('#app')

//vue2.0注册实例的方法
//import Vue from 'vue';
//import Element from 'element-ui';
//Vue.use(Element, { size: 'small', zIndex: 3000 });
//new Vue({
//    el: "#app"
//});

  • vue3的全局属性注册(尤其是lodash)
import lodash from 'lodash'
const app = createApp(App)
app.config.globalProperties.lodash= lodash
//vue3.0注册lodash为_ 是不生效的,因为vue3.0内置了_ 为自身的属性
//Vue.prototype._= lodash
  • vue3的组件传参props以及事件传递
<script setup>
import { defineEmits , defineProps ,defineExpose} from 'vue'
//通过defineProps接受并定义默认props
const props = defineProps({
  defaultValue:{
    type:String,
    default:''
  }
})
console.log(props.defaultValue)
//通过defineEmits定义emit事件 ,使用emit调用emit触发success事件
const emit = defineEmits(['success'])
emit('success' , attrs)
const Click = () => {
	console.log('点击事件')
}
defineExpose({Click })
//通过defineExpose暴露子组件的方法可以使得父组件通过ref选中组建并调用子组件的Click方法
</script>
  • vue-router的beforeRouterLeave等api属性在路由页面的子组件内使用setup语法亦可以出发
    在vue2.0中我们使用vue-router的beforeRouterLeave只能在定义路由表引用的vue文件内触发此钩子函数,在当前路由页面内引用子组件并且在子组件内使用beforeRouterLeave是不会触发的。
    使用setup语法时使用onBeforeRouteLeave在路由页面的子组件内亦可以触发。下面我们可以在一个子组件中分别使用两种script标签同时使用验证是否执行。最后是组合式api在子组件触发了路由离开前的拦截。
<script>
export default {
  beforeRouteLeave(to,from,next){
	console.log('选项式api触发')
  }
}
</script>
<script setup>
import {onBeforeRouteLeave} from 'vue-router'
onBeforeRouteLeave((to,from,next)=>{
	console.log('组合式api触发')
})
</script>

讲到这了,不由得想起了我们的主题,vue2.0 -> vue3.0的升级,既然是升级自然是免不了项目中带有2.0的选项式代码,总不能为了在子组件中拦截路由离开就将子组件的2.0全部修改为3.0的语法,于是便有了一个的想法。一个vue组件内写两个scrip标签,子组件内原有使用选项式api的不做修改,新增一个组合式api的scrip标签,确实可以达到子组件内拦截路由离开的需,代码示意如下:

<script>
import {request} from './api'
//引入一个接口请求
export default {
  data(){
	return {}
  },
  methods:{
  	click(){
  		this.request()
  	},
  	request(){
  	  let params ={id:'1'}
  	  request(params )
  	}
  }
}
</script>
<script setup>
import {onBeforeRouteLeave} from 'vue-router'
onBeforeRouteLeave((to,from,next)=>{
	console.log('子组件拦截路由离开')
})
</script>

注意:可以看出代码中在原有的选项式api中引入了一个request请求,而在methods中定义了一个与request相同命名的事件,在不添加setup语法的script标签时是可以这样重名使用的,当引入了setup语法时,原有的request请求就会直接注册在this上,这时使用this.request会直接调用接口请求而不是调用methods中的request方法。

  • template标签的支持
    在vue2.0中使用空的template标签可以渲染template标签内的元素,vue3.0除最外层空的template标签则不行
<template>
//最外层vue的template标 签
  <div>
    hello
    <template>
    //如果空的template标签不添加插槽,或者是判断条件,那么在vue3.0中不会显示出word
      <div>
        word
      </div>
    </template>
  </div>
</template>
  • 废弃了$set方法,因为使用proxy代理替换了Object.defineProperty劫持对象的方法,可以监听捕捉到对象的变化
  • vue3的style样式深度选择器
//vue2.0的深度选择器
/deep/.el-table{
	height:500px
}
::v-deep.el-table{
	width:
}
//vue3.0的深度选择器
::v-deep(.el-table){
	width:
}
:deep(.el-table){
	width:
}
  • 自定义指令生命周期的变化
//vue3.0指令的生命周期
created(el, bindings, vnode, preVnode) {
  console.log("created");
},
beforeMount() {
  console.log("beforeMount");
},
mounted() {
  console.log("mounted");
},
beforeUpdate() {
  console.log("beforeUpdate");
},
updated() {
  console.log("updated");
},
beforeUnmount() {
  console.log("beforeUnmount");
},
unmounted() {
  console.log("unmounted");
}
//vue2.0的生命周期
bind(el, bindings) {
  console.log("bind");
},
inserted(el, bindings) {
  console.log("inserted");
},
update(el, bindings) {
  console.log("update");
},
componentUpdated(el, bindings) {
  console.log("componentUpdated");
},
unbind(el) {
  console.log("unbind");
}

Element组件的切换

  • icon组件
    plus对原有ui的添加class显示icon的语法依旧兼容支持,新增icons包,可单个引入icon组件
<el-icon :size="20">
    <Edit />
</el-icon>
  • slot插槽的使用
    plus改掉了原有的插槽写法,默认插槽#default,具名插槽#name
 <el-dialog v-model="dialogVisible" title="Tips" width="30%" :before-close="handleClose" >
	<template #footer>
		<span class="dialog-footer">
	    	<el-button @click="dialogVisible = false">Cancel</el-button>
	     	<el-button type="primary" @click="dialogVisible = false">Confirm</el-button>
	   	</span>
	 </template>
  </el-dialog>
 <el-table-column fixed="right" label="Operations" width="120">
	<template #default="scope">
	   <el-button link type="primary"	size="small" @click.prevent="deleteRow(scope.$index)" >
	     Remove
	   </el-button>
	 </template>
</el-table-column>
  • dialog等组件visible的绑定
    plus采用v-model或者model-value的绑定方式去渲染对话框等组件
<el-dialog v-model="dialogVisible" title="Tips" width="30%" :before-close="handleClose" ></el-dialog>
<el-dialog :model-value="dialogVisible" title="Tips" width="30%" :before-close="handleClose" ></el-dialog>
  • form表单新增size属性,可直接控制form组件嵌套的element组件size
  • form表单的校验方法validate回调的第二个参数invalidFields返回的是所有的的表单项而非是校验不通过的表单项(可能是笔者安装的版本较早存在的bug)
  • 当form内部嵌套了el-radio组件但是el-radio的v-model没有绑定form的表单model中得值,本地开发环境下正常切换radio可以使用,打包到线上切换radio无法点击切换。最后将el-radio的v-model切换为form绑定的model对象中的参数才可以切换radio

谢谢大家的阅读!!!

Logo

前往低代码交流专区

更多推荐