最近一直在做vue2的技术栈升级,于是心血来潮,就想要不写篇文章总结一下vue2和vue3在使用上的不同吧,于是乎,我们这就开始吧!
首先说明一下,vue3有多种写法,本文使用setup语法糖,不考虑defineComponent的情况。
主要内容
1.slot 的用法
vue2
<el-tooltip placement="top">
<div slot="content">自定义content信息</div>
<el-button>Top center</el-button>
</el-tooltip>
vue3
<Tooltip placement="top">
<template #content>
<span>自定义content信息</span>
</template>
<Button>Top center</Button>
</Tooltip>
2.props 的用法
vue2
export default {
props: {
title: {
type: String,
default: '这是标题'
}
},
}
vue3
<script setup lang="ts">
import { withDefaults, defineProps } from 'vue';
const props = withDefaults(defineProps<{
title: string;
}>(), {
title: '这是标题'
});
</script>
3.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'
}
4.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)
}
5.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)
});
6.watch 的使用
vue2
export default {
watch: {
userList: {
handler: function (val) {
},
deep: true,
immediate: true
}
},
}
vue3
import { watch, ref } from 'vue';
const userList = ref<User[]>([])
watch(
() => userList.value,
() => {
},
{
deep: true,
immediate: true
}
);
7. 父组件调用子组件内的方法
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 = () => {
}
defineExpose({
getList
})
8. router路由参数获取与路由跳转
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'
}
})
})
9. provide与inject
vue2 父组件
export default {
provide () {
return {
deleteFn: (id) => {
},
userName: '张三'
}
},
}
子组件使用
export default {
inject: ['userName', 'deleteFn'],
}
vue3 父组件
import { provide, ref } from 'vue';
const deleteFn = (id: string) => {
}
const userName = ref<string>('张三')
provide('deleteFn', deleteFn);
provide('userName', userName);
子组件使用
import { inject, ref } from 'vue';
const deleteFn = inject('deleteFn');
const userName = inject('userName');
10. 生命周期函数
vue2 八个
beforeCreate created beforeMount mounted beforeUpdate updated beforeDestroy destroyed
vue3 六个
onBeforeMount onMounted onBeforeUpdate onUpdated onBeforeUnmount onUnmounted
说明:vue2中的beforeCreate和created对应的其实是vue3的setup
11. 页面路由拦截
如果使用的是vue3 setup语法糖来写组件,而有需要使用页面路由拦截来做一些事情的时候,处理起来会比较麻烦,需要结合vue3 defineComponent的 写法来实现。当然,也可以直接整个页面都使用defineComponent 而不是setup语法糖
vue2
export default {
beforeRouteEnter (to, from, next) {
next()
},
}
vue3 需要注意的是,项目中可能因为重复引入vue和多个script标签而导致eslint报错,这时需要禁用掉当前行的eslint检查。
<script setup lang="ts">
import {
ref, onMounted,
} from 'vue';
...
</script>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
beforeRouteEnter(to, from, next) {
next();
},
});
</script>
12. 动态循环component组件如何设置ref来使用子组件中的方法?
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',
name: 'page-a'
},
{
label: '页面B',
name: 'page-b'
},
],
currentPage: 'page-a'
}
},
mounted () {
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',
name: 'pageA',
component: PageA
},
{
label: '页面B',
name: 'pageB',
component: PageB
}
])
const tabRefs = reactive<any>({});
const currentPage = ref('pageA')
const setTabRef = (el: any, key: string) => {
tabRefs[key] = el;
};
onMounted(() => {
tabRefs[currentPage]?.getData()
})
想法(仅个人)
- vue2升级vue3的时间成本和技术代价有点高了,感觉这也是vue的一个缺点吧
- vue3开放了多种写法,虽然选择比较自由,但不同写法又有不同的缺点,感觉这也是vue做的不够好的地方。比如defineComponent需要最后返回所有的数据和方法,不然template无法使用;比如setup语法糖下,无法使用路由拦截…
- 砥砺前行,vue的道路还很长~
|