Vue2与Vue3 setup的使用差异与对比
最近一直在做vue2的技术栈升级,于是心血来潮,就想要不写篇文章总结一下vue2和vue3在使用上的不同吧
最近一直在做vue2的技术栈升级,于是心血来潮,就想要不写篇文章总结一下vue2和vue3在使用上的不同吧,于是乎,我们这就开始吧!
首先说明一下,vue3有多种写法,本文使用setup语法糖,不考虑defineComponent的情况。
主要内容
1.slot
的用法
vue2
<el-tooltip placement="top">
<!-- vue2 slot使用方式是任意标签设置属性【slot="slot名称"】 -->
<div slot="content">自定义content信息</div>
<el-button>Top center</el-button>
</el-tooltip>
vue3
<Tooltip placement="top">
<!-- vue3 slot使用方式是template标签设置属性【#slot名称】 -->
<template #content>
<span>自定义content信息</span>
</template>
<Button>Top center</Button>
</Tooltip>
2.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>
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) {
// 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
}
);
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 = () => {
// TODO
}
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) => {
// 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');
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) {
// 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>
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',
// 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()
})
想法(仅个人)
- vue2升级vue3的时间成本和技术代价有点高了,感觉这也是vue的一个缺点吧
- vue3开放了多种写法,虽然选择比较自由,但不同写法又有不同的缺点,感觉这也是vue做的不够好的地方。比如defineComponent需要最后返回所有的数据和方法,不然template无法使用;比如setup语法糖下,无法使用路由拦截…
- 砥砺前行,vue的道路还很长~
更多推荐
所有评论(0)