关键词:state、Vuex、context.root
从广义上来说,我觉得组件间通信的应用场景,无非就是父子组件间的通信和非父子组件间(兄弟、祖孙……)的通信。其实说白了,所谓的通信,就是控制数据的流动,也就是控制“状态”;我倒是挺认可有限状态机模型的。如果进一步细分,大致是这样几种:
props
和$emit
完成,如果放在3.0,就是props
和context.emit
。从直观感受上说,父子组件之间的关系,有点CSS那种一层层叠加的感觉;如果从面向对象理论的角度说,有点类似于组合委托,拥有共同的生命周期。activated
和deactivated
钩子的配合。router的params和query都可以承担这个职责。说起总线,要么就是创建一个空的Vue实例,然后用$emit
和$on
进行传递:
// bus.js
import Vue from 'vue'
export const bus = new Vue()
// parent.vue
created () {
bus.$on('foo', (bar) => {// do sth...})
}
// child.vue
bus.$emit('foo', bar)
但是这种写法的限制比较多,要求监听的对象(在这里是父组件)一定要先于被监听的对象创建;而且中途销毁子组件也会影响监听。
或者像官方文档上写的那样,创建一个store:
var store = {
debug: true,
state: {
message: 'Hello!'
},
setMessageAction (newValue) {
if (this.debug) console.log('setMessageAction triggered with', newValue)
this.state.message = newValue
},
clearMessageAction () {
if (this.debug) console.log('clearMessageAction triggered')
this.state.message = ''
}
}
不过在这种简单的应用场景下,还有更简单的方案;我们只是需要一个状态的容器而已,不是吗?所以……还记得2.6的新API,Vue.observable
吗?
让一个对象可响应。Vue 内部会用它来处理
data
函数返回的对象。返回的对象可以直接用于渲染函数和计算属性内,并且会在发生改变时触发相应的更新。也可以作为最小化的跨组件状态存储器,用于简单的场景。
在 Vue 2.x 中,被传入的对象会直接被
Vue.observable
改变,它和被返回的对象是同一个对象。在 Vue 3.x 中,则会返回一个可响应的代理,而对源对象直接进行修改仍然是不可响应的。因此,为了向前兼容,我们推荐始终操作使用Vue.observable
返回的对象,而不是传入源对象。
我们先设想一个最简单的非父子组件间通信的场景。这个时候,我们可以用Vue.observable
创建一个容器,然后就可以代替之前的bus了:
import Vue from 'vue'
export const bus = Vue.observable({
foo: 'bar'
})
在3.0里,state
,从某种意义上,是可以用来取代Vue.observable
的:
import Vue from 'vue'
import { plugin, state } from 'vue-function-api'
Vue.use(plugin)
export const bus = state({
foo: 'bar'
})
需要注意的是,这里一定要有一个Vue.use
的过程。这个不同于函数式的组件。不过,在3.0里,state
的作用并不仅止于这些。它的真正意义,大概是作为包装对象的另一面,作为共享状态的容器只是其中一个应用场景罢了:
如果你依然想创建一个没有包装的响应式对象,可以使用
state
API(和 2.x 的Vue.observable()
等同)。
说完这个,说说Vuex的用法。我们都知道,Vuex和router是挂载在根实例上的,所以我们可以用$store
、$router
、$route
来调用它们。这一点在main里体现得很明显:
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
但是到了3.0,$已经被setup代替了。那么,我们应该怎么使用呢?可以看一个例子,这个是来自vue-function-api的issue的写法:
setup (props, { root }) {
const fields = computed(() => root.$store.state.fields)
}
不过,为了支持3.0,Vuex和router的API显然是会有变动的。按照作者的说法,未来的API可能会类似于这样:
import { useStore, useComputed } from 'vuex';
setup (props, { root }) {
const store = useStore();
const filed = useComputed('state.field');
}
目前为了达到这个效果(也就是所谓的FOP,面向未来编程),可以按照作者的说法,搞一个tricky的适配;这个我也在demo里写了:
const store = new Vuex.Store({//...})
export function userStore() {
return store;
}
当然,也有dalao自己实现了一个版本。虽然我并不认识他……但是我觉得很有意思,就分享给大家:传送门。
Vue 3.0 function-based API尝鲜(一):前言
Vue 3.0 function-based API尝鲜(二):配置与启动
Vue 3.0 function-based API尝鲜(三):包装对象
Vue 3.0 function-based API尝鲜(四):值得一提的watch
Vue 3.0 function-based API尝鲜(五):生命周期
Vue 3.0 function-based API尝鲜(七):This与Refs