Vue2与Vue3 setup的使用差异与对比

最近一直在做vue2的技术栈升级,于是心血来潮,就想要不写篇文章总结一下vue2和vue3在使用上的不同吧,于是乎,我们这就开始吧!

Vue2与Vue3 setup的使用差异与对比_第1张图片

首先说明一下,vue3有多种写法,本文使用setup语法糖,不考虑defineComponent的情况。

主要内容

1.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>

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
需要引入 useRouteruseRoute并执行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()
})

想法(仅个人)

  1. vue2升级vue3的时间成本和技术代价有点高了,感觉这也是vue的一个缺点吧
  2. vue3开放了多种写法,虽然选择比较自由,但不同写法又有不同的缺点,感觉这也是vue做的不够好的地方。比如defineComponent需要最后返回所有的数据和方法,不然template无法使用;比如setup语法糖下,无法使用路由拦截…
  3. 砥砺前行,vue的道路还很长~

你可能感兴趣的:(vue,javascript,前端,vue.js)