最近一直在做vue2的技术栈升级,于是心血来潮,就想要不写篇文章总结一下vue2和vue3在使用上的不同吧,于是乎,我们这就开始吧!
首先说明一下,vue3有多种写法,本文使用setup语法糖,不考虑defineComponent的情况。
slot
的用法vue2
<el-tooltip placement="top">
<div slot="content">自定义content信息div>
<el-button>Top centerel-button>
el-tooltip>
vue3
<Tooltip placement="top">
<template #content>
<span>自定义content信息span>
template>
<Button>Top centerButton>
Tooltip>
props
的用法vue2
// vue2 使用option api风格,在props内定义父组件传入的props
export default {
props: {
title: {
type: String,
default: '这是标题'
}
},
}
vue3
<script setup lang="ts">
import { withDefaults, defineProps } from 'vue';
// vue3 使用defineProps定义父组件传入的props,同时可使用withDefaults定义属性默认值
const props = withDefaults(defineProps<{
title: string;
}>(), {
title: '这是标题'
});
</script>
data
数据的定义和修改vue2
export default {
data () {
return {
title: '新增',
formData: {
username: '',
password: ''
}
}
},
methods: {
changeTitle () {
this.title = '编辑'
},
changeFormData () {
this.$set(formData, 'username', '章三')
this.$set(formData, 'password', '000000')
}
}
}
vue3
import { ref, reactive } from 'vue';
const title = ref<string>('新增');
const formData = reactive({
username: '',
password: ''
})
const changeTitle = () => {
title.value = '编辑'
}
const changeFormData = () => {
formData.username = '章三'
formData.password = '000000'
}
emit
事件定义vue2
export default {
methods: {
submit () {
const data = {}
this.$emit('change', data)
}
}
}
vue3
import { defineEmits } from 'vue';
const emit = defineEmits(['change'])
const submit = () => {
const data = {}
emit('change', data)
}
computed
的使用vue2
export default {
computed: {
userNames (userList) {
return userList.map(user => user.name)
}
}
}
vue3
import { computed, ref } from 'vue';
const userList = ref<User[]>([])
const userNames = computed(() => userList.value.map(user => user.name))
// 下面为需要传值的情况
const userNames = computed(() => (userListParams: User[]) => {
return userListParams.map(user => user.name)
});
watch
的使用vue2
export default {
watch: {
userList: {
handler: function (val) {
// todo
},
deep: true,
immediate: true
}
},
}
vue3
import { watch, ref } from 'vue';
const userList = ref<User[]>([])
watch(
// 这里也可以直接写userList.value
() => userList.value,
() => {
// todo
},
{
deep: true,
immediate: true
}
);
vue2
不需要额外的设置,直接通过this.$refs
调用即可
父组件
<List ref="listRef" />
export default {
mounted () {
this.$refs.listRef?.getList()
}
}
vue3
需要子组件通过 defineExpose方法先暴露出来父级需要用到的方法
父组件
<List ref="listRef" />
import { ref, onMounted } from 'vue';
const listRef = ref()
onMounted(() => {
listRef.value?.getList()
})
子组件
import { defineExpose } from 'vue';
const getList = () => {
// TODO
}
defineExpose({
getList
})
vue2
直接使用this.$router
调用录音方法, 使用 this.$route
获取路由参数
export default {
mounted () {
const params = this.$route.query
this.$router.push({
path: '/page/list',
query: {
name: 'dxxxxx'
}
})
}
}
vue3
需要引入 useRouter
与useRoute
并执行hooks后使用
import { onMounted } from 'vue';
import { useRouter, useRoute } from 'vue-router';
const router = useRouter();
const route = useRoute();
onMounted(() => {
const params = route.query
router.push({
path: '/page/list',
query: {
name: 'dxxxxx'
}
})
})
vue2
父组件
export default {
provide () {
return {
deleteFn: (id) => {
// TODO
},
userName: '张三'
}
},
}
子组件使用
export default {
inject: ['userName', 'deleteFn'],
}
vue3
父组件
import { provide, ref } from 'vue';
const deleteFn = (id: string) => {
// TODO
}
const userName = ref<string>('张三')
provide('deleteFn', deleteFn);
provide('userName', userName);
子组件使用
import { inject, ref } from 'vue';
const deleteFn = inject('deleteFn');
const userName = inject('userName');
vue2 八个
beforeCreate created beforeMount mounted
beforeUpdate updated beforeDestroy destroyed
vue3 六个
onBeforeMount onMounted onBeforeUpdate onUpdated onBeforeUnmount onUnmounted
说明:vue2中的beforeCreate和created对应的其实是vue3的setup
如果使用的是vue3 setup语法糖来写组件,而有需要使用页面路由拦截来做一些事情的时候,处理起来会比较麻烦,需要结合vue3 defineComponent的
写法来实现。当然,也可以直接整个页面都使用defineComponent
而不是setup语法糖
vue2
export default {
beforeRouteEnter (to, from, next) {
// TODO
next()
},
}
vue3
需要注意的是,项目中可能因为重复引入vue和多个script标签而导致eslint报错,这时需要禁用掉当前行的eslint检查。
<script setup lang="ts">
import {
ref, onMounted,
// 这里是为了解决eslint报错问题
// eslint-disable-next-line import/no-duplicates
} from 'vue';
...
script>
<script lang="ts">
// 这里是为了解决eslint报错问题
// eslint-disable-next-line import/first, import/no-duplicates
import { defineComponent } from 'vue';
export default defineComponent({
beforeRouteEnter(to, from, next) {
// TODO
next();
},
});
script>
vue2
<component
v-for="item in componentList"
:is="item.name"
:ref="`${item.name}-ref`"
:key="item.name"
>component>
import PageA from './components/page-a.vue'
import PageB from './components/page-b.vue'
export default {
data () {
return {
componentList: [
{
label: '页面A',
// value对应组件名
name: 'page-a'
},
{
label: '页面B',
name: 'page-b'
},
],
currentPage: 'page-a'
}
},
mounted () {
// 获取ref
this.$refs[`${currentPage}-ref`]?.getData()
}
}
vue2
vue3无法使用name直接用于component,需要传入真实的组件
<component
v-for="item in componentList"
:is="item.component"
:ref="(el) => setTabRef(el, item.name)"
:key="item.name"
>component>
import { ref, reactive, onMounted } from 'vue';
import PageA from './components/page-a.vue'
import PageB from './components/page-b.vue'
const componentList = ref([
{
label: '页面A',
// value对应组件名
name: 'pageA',
// 这里需要传入真实的组件
component: PageA
},
{
label: '页面B',
name: 'pageB',
// 这里需要传入真实的组件
component: PageB
}
])
// 存储动态子组件的ref
const tabRefs = reactive<any>({});
const currentPage = ref('pageA')
const setTabRef = (el: any, key: string) => {
tabRefs[key] = el;
};
onMounted(() => {
// 获取ref
tabRefs[currentPage]?.getData()
})