本文将之前采用Vue2.6开发的todoList小项目改造成为Vue3.0编写,并介绍一下2.x和3.x之间写法的不同之处。 Vue3.x适配大部分Vue2.x的组件配置,也就是说以前我们在Vue2.x针对组件的一些配置项,例如:
1export default { 2 name: 'test', 3 components: {}, 4 props: {}, 5 data () { 6 return {} 7 }, 8 created(){}, 9 mounted () {}, 10 watch:{}, 11 methods: {} 12}
在Vue3.x中也是可以适配的,对应的相关生命周期方法也可正常执行,但是Vue3.x的一大核心是引入了Vue Composition API[4](组合式API),这使得组件的大部分内容都可以通过setup()方法进行配置,同时Vue Composition API在Vue2.x也可以使用,需要通过安装@vue/composition-api来使用:
1npm install @vue/composition-api 2... 3import VueCompositionApi from '@vue/composition-api'; 4 5Vue.use(VueCompositionApi);
下面主要介绍一下采用Vue Composition API来改造采用2.x开发的todoList项目时的新老代码对比。
如何创建一个Vue3.0的项目
首先,安装vue cli的最新版本,一般是vue cli 4,安装成功后,调用:
vue create myapp
创建一个基于Vue2.x的项目,然后进入项目的根目录,执行:
vue add vue-next
然后就会自动安装vue-cli-plugin-vue-next[5]插件,完毕之后,myapp项目就会变成一个基于Vue3.0Beta版本的项目框架。
根实例初始化:
在2.x中通过new Vue()的方法来初始化:
1import App from './App.vue' 2new Vue({ 3 store, 4 render: h => h(App) 5}).$mount('#app')
在3.x中Vue不再是一个构造函数,通过createApp方法初始化:
1import App from './App.vue' 2createApp(App).use(store).mount('#app')
ref或者reactive代替data中的变量: 在2.x中通过组件data的方法来定义一些当前组件的数据:
1... 2data() { 3 return { 4 name: 'test', 5 list: [], 6 } 7}, 8...
在3.x中通过ref或者reactive创建响应式对象:
1import {ref,reactive} from 'vue' 2... 3setup(){ 4 const name = ref('test') 5 const state = reactive({ 6 list: [] 7 }) 8 return { 9 name, 10 state 11 } 12} 13...
ref将给定的值创建一个响应式的数据对象并赋值初始值(int或者string),reactive可以直接定义复杂响应式对象。 methods中定义的方法也可以写在setup()中: 在2.x中methods来定义一些当前组件内部方法:
1... 2methods: { 3 fetchData() { 4 5 }, 6} 7...
在3.x中直接在setup方法中定义并return:
1... 2setup(){ 3 const fetchData = ()=>{ 4 console.log('fetchData') 5 } 6 7 return { 8 fetchData 9 } 10} 11...
无法使用EventBus: 在2.x中通过EventBus的方法来实现组件通信:
1var EventBus = new Vue() 2Vue.prototype.$EventBus = EventBus 3... 4this.$EventBus.$on() this.$EventBus.$emit()
在3.x中移除了$on, $off等方法(参考rfc[6]),而是推荐使用mitt[7]方案来代替:
1import mitt from 'mitt' 2const emitter = mitt() 3// listen to an event 4emitter.on('foo', e => console.log('foo', e) ) 5// fire an event 6emitter.emit('foo', { a: 'b' })
由于3.x中不再支持prototype的方式给Vue绑定静态方法,可以通过app.config.globalProperties.mitt = () => {}方案。
setup()中使用props和this:
在2.x中,组件的方法中可以通过this获取到当前组件的实例,并执行data变量的修改,方法的调用,组件的通信等等,但是在3.x中,setup()在beforeCreate和created时机就已调用,无法使用和2.x一样的this,但是可以通过接收setup(props,ctx)的方法,获取到当前组件的实例和props:
1export default { 2 props: { 3 name: String, 4 }, 5 setup(props,ctx) { 6 console.log(props.name) 7 ctx.emit('event') 8 }, 9}
注意ctx和2.x中this并不完全一样,而是选择性地暴露了一些property,主要有[attrs,emit,slots]。
watch来监听对象改变
2.x中,可以采用watch来监听一个对象属性是否有改动:
1... 2data(){ 3 return { 4 name: 'a' 5 } 6}, 7watch: { 8 name(val) { 9 console.log(val) 10 } 11} 12...
3.x中,在setup()中,可以使用watch来监听:
1... 2import {watch} from 'vue' 3setup(){ 4 let state = reactive({ 5 name: 'a' 6 }) 7 watch( 8 () => state.name, 9 (val, oldVal) => { 10 console.log(val) 11 } 12 ) 13 state.name = 'b' 14 return { 15 state 16 } 17} 18...
在3.x中,如果watch的是一个数组array对象,那么如果调用array.push()方法添加一条数据,并不会触发watch方法,必须重新给array赋值:
1let state = reactive({ 2 list: [] 3 }) 4 watch( 5 () => state.list, 6 (val, oldVal) => { 7 console.log(val) 8 } 9 ) 10 11 state.list.push(1) // 不会触发watch 12 13 state.list = [1] // 会触发watch
此问题不知是否是Vue3.x特意加上的,有待正式版出来后在验证。 computed计算属性: 2.x中:
1... 2computed: { 3 storeData () { 4 return this.$store.state.storeData 5 }, 6}, 7...
3.x中:
1... 2import {computed} from 'vue' 3setup(){ 4 const storeData = computed(() => store.state.storeData) 5 6 return { 7 storeData 8 } 9} 10...
参考资料
[1] 点击体验 [2] Vue.js2.6版本todoList [3] Vue.js3.0版本todoList [4] Vue Composition API [5] vue-cli-plugin-vue-next [6] rfc [7] mitt
