记一次使用 vue-composition-api 开发网页的经历

项目地址:https://github.com/CaoMeiYouRen/shirakami-haruka-button

演示页面:https://haruka.fun

在本次的项目开发中,本人也是尽可能抛弃 Vue2 的配置式写法,全面转向 Vue3 的函数式写法。

两者从功能上来讲是完全一致的,所以改造的过程还是比较顺利的,并没有遇到特别困难的情况。

而我认为真正麻烦的地方在于需要区分响应式对象和普通对象。

这里需要专门提一下 refreactive 的区别。

使用 ref 可以对所有类型的数据返回一个响应式代理,但是需要通过 xxx.value 来访问;而 reactive 只能转换对象,但是可以直接访问值。

由于经过 reactive 转换的值的类型和原来是一致的,所以在这种情况下更难判断当前调用的是响应式对象还是普通对象,因此我建议将响应式对象均使用 ref 来转换,这样能够借助类型系统区分响应式对象和普通对象。虽然在书写上会有点麻烦,但是对于带来的逻辑清晰,这点代价还是值得的。

由于官方也推荐把组合式 api 的命名采用类似 react hook 的风格,所以 vue composition api 写的函数也是使用 useXXX 格式命名的,例如

/**
 * 返回 window 的大小
 *
 * @author CaoMeiYouRen
 * @date 2020-10-10
 * @export
 * @returns
 */
export function useOnWindowResize() {
    function getSize() {
        const height = window.innerHeight || document.documentElement.clientHeight
        const width = window.innerWidth || document.documentElement.clientWidth
        return {
            height,
            width,
        }
    }
    const height = ref(0)
    const width = ref(0)
    function handler() {
        const size = getSize()
        height.value = size.height
        width.value = size.width
    }
    window.addEventListener('resize', handler)
    onMounted(() => {
        handler()
    })
    onUnmounted(() => {
        window.removeEventListener('resize', handler)
    })
    function remove() {
        window.removeEventListener('resize', handler)
    }
    return {
        height,
        width,
        remove,
    }
}

//调用时 
const { width } = useOnWindowResize()

这样可以有利于区分组合式 api 函数和普通函数。

然后说一个点,没有使用到生命周期钩子的组合式 api ,也可以在全局调用,并不只能在 setup() 函数中执行。

同时,也因为这个原因,生命周期钩子必须同步调用,不能异步调用。这个问题实际上很好理解,如果异步执行的话,在 setup 期间,Vue 就不知道你调用了哪些生命周期钩子。

由于 vue 的组合式 api 可以在条件语句中执行,因此完全可以传入一个参数来标记当前是全局调用还是在 Vue 中调用。

在之后的研究中发现,可以使用 getCurrentInstance() 函数来判断当前是全局调用还是在 Vue 中调用,如果为 Vue 中,则会返回一个 Vue 实例,否则返回 null ,因此可以借此来判断。

基于此,可以开发一个超简单的 vuex ,而 vuex 的 next 版本也是基于此进行了很多优化。如果只是想全局调用少量数据的话,确实可以直接使用响应式对象来完成操作。

最后,就像在 react hook 中需要时刻注意 hook 会多次执行一样, vue 组合式函数也要牢记只会执行一次。尤其是要注意计算属性的使用。很多时候会写成给响应式对象赋值,而实际上是想写计算属性。

const arr = ref([])
//错误
const length = ref(arr.value.length)

//正确
const length = computed(() => arr.value.length)

vue 的响应式 api 一定还有着更多有趣的功能等待着大家开发,期待进一步的深入了解