import { computed } from '@vue/composition-api' const count = ref(1) // computed接收一个函数作为入参 const plusOne = computed(() => count.value + 1) console.log(plusOne.value) // 2 plusOne.value++ // 错误,plusOne是只读的!
或者也可以是个对象,可以使用具有get和set功能的对象来创建可写ref对象。
const count = ref(1) // computed接收一个对象作为入参 const plusOne = computed({ get: () => count.value + 1, set: val => { count.value = val - 1 } }) plusOne.value = 1 console.log(count.value) // 0
watch
watch(source, cb, options?)
该watchAPI与2.x this.$watch(以及相应的watch选项)完全等效。
观察单一来源
观察者数据源可以是返回值的getter函数,也可以直接是ref:
// watching a getter函数 const state = reactive({ count: 0 }) watch( () => state.count, // 返回值的getter函数 (count, prevCount,onCleanup) => { /* ... */ } ) // directly watching a ref const count = ref(0) watch( count, // 也可以直接是ref (count, prevCount,onCleanup) => { /* ... */ })
watch多个来源
观察者还可以使用数组同时监视多个源:
const me = reactive({ age: 24, name: 'gk' }) // reactive类型的 watch( [() => me.age, () => me.name], // 监听reactive多个数据源,可以传入一个数组类型,返回getter函数 ([age, name], [oldAge, oldName]) => { console.log(age) // 新的 age 值 console.log(name) // 新的 name 值 console.log(oldAge) // 旧的 age 值 console.log(oldName) // 新的 name 值 }, // options { lazy: true //默认 在 watch 被创建的时候执行回调函数中的代码,如果lazy为true ,怎创建的时候,不执行! } ) setInterval(() => { me.age++ me.name = 'oldMe' }, 7000000) // ref类型的 const work = ref('web') const addres = ref('sz') watch( [work,address], // 监听多个ref数据源 ([work, addres], [oldwork, oldaddres]) => { //...... }, { lazy: true } )
watch和组件的生命周期绑定,当组件卸载后,watch也将自动停止。在其他情况下,它返回停止句柄,可以调用该句柄以显式停止观察程序:
// watch 返回一个函数句柄,我们可以决定该watch的停止和开始! const stopWatch = watch( [work,address], // 监听多个ref数据源 ([work, addres], [oldwork, oldaddres]) => { //...... }, { lazy: true } ) // 调用停止函数,清除对work和address的监视 stopWatch()
在 watch 中清除无效的异步任务
<div> <svg-icon icon-class="search"></svg-icon> <input v-focus placeholder="搜索、关键词" v-model="searchText" /> </div>
setup(props, { attrs, slots, parent, root, emit, refs }){ const CancelToken = root.$http.CancelToken const source = CancelToken.source() // 定义响应式数据 searchText const searchText = ref('') // 向后台发送异步请求 const getSearchResult = searchText => { root.$http.post("http://test.happymmall.com/search",{text:searchText}, { cancelToken: source.token }).then(res => { // ..... }); return source.cancel } // 定义 watch 监听 watch( searchText, (searchText, oldSearchText, onCleanup) => { // 发送axios请求,并得到取消axios请求的 cancel函数 const cancel = getSearchResult(searchText) // 若 watch 监听被重复执行了,则会先清除上次未完成的异步请求 onCleanup(cancel) }, // watch 刚被创建的时候不执行 { lazy: true } ) return { searchText } }
最后
vue3新增 Composition API。新的 API 兼容 Vue2.x,只需要在项目中单独引入 @vue/composition-api 这个包就能够解决我们目前 Vue2.x中存在的个别难题。比如:如何组织逻辑,以及如何在多个组件之间抽取和复用逻辑。基于 Vue 2.x 目前的 API 我们有一些常见的逻辑复用模式,但都或多或少存在一些问题:
这些模式包括:
Mixins
高阶组件 (Higher-order Components, aka HOCs)
Renderless Components (基于 scoped slots / 作用域插槽封装逻辑的组件)
总体来说,以上这些模式存在以下问题:
模版中的数据来源不清晰。举例来说,当一个组件中使用了多个 mixin 的时候,光看模版会很难分清一个属性到底是来自哪一个 mixin。HOC 也有类似的问题。
命名空间冲突。由不同开发者开发的 mixin 无法保证不会正好用到一样的属性或是方法名。HOC 在注入的 props 中也存在类似问题。
性能。HOC 和 Renderless Components 都需要额外的组件实例嵌套来封装逻辑,导致无谓的性能开销。
vue3中,新增 Composition API。而且新的API兼容 Vue2.x,只需要在项目中,单独引入 @vue/composition-api 这个包就可以,就能够解决我们目前 以上大部分问题。同时,如果我直接升级到 Vue3.x,我要做的事情会更多,只要当前项目中使用到的第三方ui库,都需要重新改造,以及升级后的诸多坑要填!刚开始的时候,我就是直接在当前脚手架的基础上 vue add vue-next 安装升级,但是只要是有依赖第三方生态库的地方,就有许多的坑。。。
Vue3.x 没有导出默认对象 export default,在第三方生态中,常用Vue.xxx()来进行依赖,现在这些语法需要重写,工作量可不小啊!
如果是新团队、小型的项目,可以尝试使用vue3进行尝试开发,慢慢过度,当 Vue3.x 正式 发布 后,而且周边生态跟上来了,就可以直接用vue3了!