它来了,它来了,vue3.0带着组合式api来了

举报
街角小林 发表于 2022/10/30 20:45:31 2022/10/30
【摘要】 开头9月19号,万众期待的vue3.0如期而至,事实上很多人很早就已经体验过了,但是本人比较懒,再加上没有正式发布也不能在项目上使用,所以一直没去尝试,只在一些零星的文章上看到了它大概会有一些什么新功能,现在它的正式发布意味着已经推荐在新项目上使用它了,毕竟相对于2.0,它的优点还是非常明显的,虽然现在配套的一些工具还没完成,但是在一些小项目上使用应该没有什么问题。目前我还没有实际的去使用...

开头

9月19号,万众期待的vue3.0如期而至,事实上很多人很早就已经体验过了,但是本人比较懒,再加上没有正式发布也不能在项目上使用,所以一直没去尝试,只在一些零星的文章上看到了它大概会有一些什么新功能,现在它的正式发布意味着已经推荐在新项目上使用它了,毕竟相对于2.0,它的优点还是非常明显的,虽然现在配套的一些工具还没完成,但是在一些小项目上使用应该没有什么问题。

目前我还没有实际的去使用过它,本文是在看完其提案文档、api文档及一些文章后进行总结的,难免会有问题,此外,本文跟其他类似的介绍vue组合式api的文章没有什么区别,如果看过其他的本文可以忽略~。

根据官方的说法,vue3.0的变化包括性能上的改进、更小的 bundle 体积、对 TypeScript 更好的支持、用于处理大规模用例的全新 API,全新的api指的就是本文主要要说的组合式api。

如果你的项目还没有或无法升级到3.0版本,不要遗憾,也是可以使用的,2.x 的版本,可以通过 @vue/composition-api插件来在项目中使用组合式api,这个插件主要提供了vue3.0对象里新增的一些接口,在以后升级到3.0,只需把引用从该插件改到vue对象就好了,目前也有一些同时支持2.x和3.x版本的实用的工具库(如vueusevue-composable),所以放心大胆的去使用它吧。

一种新东西的出现肯定有它的原因,组合式api在react里出现的更早,也就是react hooks,vue引入的原因肯定不是因为react有所以我也得有,抄袭就更谈不上了,框架的发展肯定都是奔着更先进的方向去的,比如之前的双向绑定、虚拟DOM等等。

我个人在之前2.x的使用上主要有以下两点问题:

1.对于稍大一点的项目,复用是很重要的,使用这种模块化的框架不复用,那是极大的浪费,在2.x中,vue复用的方式主要是组件、mixin,如果不涉及到模板和样式的逻辑上的复用一般都会用mixin,但是mixin用起来是很痛苦的,因为代码提取到mixin里后你基本想不起它里面有啥了,存在多个mixin你甚至不知道来自于哪个,每次都需要打开mixin文件看,而且很容易会造成重复声明的问题。

2.虽说一个组件最好只做一件事,但是实际上这是比较困难的,组件或多或少都做了好几件事,因为vue的书写方式是选项组织式,数据和数据在一块,方法和方法在一块,其他的属性也是一样,所以一个相对独立的功能被分散在到处,很难区分,提取复用逻辑也很麻烦,这在一个vue文件代码量很大的时候是很可怕的。

组合式api的出现就能解决以上两个问题,此外,它也对TypeScript类型推导更加友好。

在具体使用上,对vue单文件来说,模板部分和样式部分基本和以前没有区别,组合式api主要影响的是逻辑部分。来看一个最简单的例子:

<template>
	<div @click="increment">{{data.count}}</div>
</template>
<script>
import { reactive } from 'vue'
export default {
    setup() {
        const data = reactive({
            count: 0
        })
        function increment() {
            data.count++
        }
        return {
            data,
            increment
        }
    }
}
</script>

实现的效果就是点击数字,数字自增,可以看到很明显的一个变化是多了一个setup函数,之前熟悉的datamethods属性都不见了,需要注意的是在模板里要用到的数据都要在setup函数里显式返回。

例子虽小,但是已经能看出它是怎么解决上面的问题了,比如你想复用这个计数,只需要把:

const data = reactive({
    count: 0
})
function increment() {
    data.count++
}

提取成一个函数就可以了,实际上就是把原来分散在各个选项里的东西都放到一起,再加上没有了this,这样每个独立的功能都可以提取成函数,为了进行区分,函数名一般以use开头,如useCountsetup函数实际上就成了以类方式组织代码里的constructor函数或一般的init函数,用来调用各个use函数。

如果你还不是很习惯这种方式的话,实际上2.x的选项方式仍然可以继续使用,组合式api会先于选项进行解析,所以组合式api里不能访问选项里的属性,setup函数返回的属性可以在选项里通过this来访问,但是最好还是不要混用。

常用api介绍

setup

export default {
    setup() {
        return {}
    }
}

setup函数是新增的一个选项,用来使用组合式api,它会在beforeCreate生命周期钩子之前被调用,一些知识点:

1.可以返回一个对象,对象的属性会合并到模板渲染的上下文中;

2.可以返回一个函数;

3.第一个参数是props,注意使用props不能解构,第二个参数是一个上下文对象,暴露了一些其他常用属性;

reactive

import { reactive } from 'vue'
const data = reactive({
  count: 0,
})

reactive函数使一个对象变成响应式,返回的data基本相当于以前的data选项。

watchEffect

import { reactive, watchEffect } from 'vue'
const data = reactive({
  count: 0,
})
watchEffect(() => {
    alert(this.count)
})

watchEffect用来监听数据的变化,它会立即执行一次,之后会追踪函数里面用到的所有响应式状态,当变化后会重新执行该回调函数。

computed

import { reactive, computed } from 'vue'
const data = reactive({
  count: 0,
})
const double = computed(() => {
    return data.count * 2
})

相当于之前的computed选项,创建一个依赖于其他状态的状态,需要注意的是返回的是一个对象,称作ref

{
    value: xxx//这个才是你需要的值
}

引用的实际值是该对象的value属性,为什么要这么做原因很简单,因为说到底computed只是个函数,如果返回的是个基本类型的值,比如这里返回的是个0,那么即使后续data.count改变了,double仍然是0,因为JavaScript是基本类型是按值传递的,而对象是按引用传递的,所以这样获取到的value始终是最新的值。

除了可以传getter函数,也可以传一个带有settergetter属性的对象创建一个可修改的计算值。

ref

import { ref } from 'vue'
let count = ref(0)

除了通过computed返回ref,也可以直接创建,但是也可以直接通过reactive来实现响应式:

let data = reactive({
    count: 0
})

refreactive存在一定的相似性,所以需要完全理解它们才能高效的在各种场景下选择不同的方式,它们之间最明显的区别是ref使用的时候需要通过.value来取值,reactive不用。

它们的区别也可以这么理解,ref是使某一个数据提供响应能力,而reactive是为包含该数据的一整个对象提供响应能力。

在模板里使用ref和嵌套在响应式对象里时不需要通过.value,会自己解开:

<template>
	<div>{{count}}</div>
</template>
<script>
let data = reactive({
    double: count * 2
})
</script>

除了响应式ref还有一个引用DOM元素的ref,2.x里面是通过this.$refs.xxx来引用,但是在setup里面没有this,所以也是通过创建一个ref来使用:

<template>
	<div ref="node"></div>
</template>
<script>
import { ref, onMounted } from 'vue'
export default {
    setup() {
        const node = ref(null)
        onMounted(() => {
            console.log(node.value)// 此处就是dom元素
        })
        return {
            node
        }
    }
}
</script>

toRefs

如果你从一个组合函数里返回了响应式对象,在使用函数里把它解构进行使用是会丢失响应性的,解决这个问题你可以把原封不动的返回:

import { reactive } from 'vue'
function useCounter () {
    let data = reactive({
        count: 0
    })
    return data
}
export default {
    setup() {
        // let {count} = useCounter(),这样是不行的
        // return {,这样也是不行的
        //    ...useCounter()
        // }
        return {
            count: useCounter()//这样才行
        }
    }
}

除此之外还有一个办法就是使用toRefstoRefs会把一个响应式对象的每个属性都转换为一个ref

import { toRefs, reactive } from 'vue'
function useCounter () {
    let data = reactive({
        count: 0
    })
    return toRefs(data)
}
export default {
    setup() {
        let {count} = useCounter()
        return {
            count
        }
    }
}

setup函数里返回的数据使用toRefs转换一下是有好处的:

import { toRefs, reactive } from 'vue'
export default {
    setup() {
        let data = reactive({
            count: 0
        })
        return {
            data
        }
    }
}

如果这样的话在模板里使用需要通过data.count来引用count的值,如果使用toRefs的话可以直接使用count

<script>
import { toRefs, reactive } from 'vue'
export default {
    setup() {
        let data = reactive({
            count: 0
        })
        return {
            ...toRefs(data)
        }
    }
}
</script>
<template>
	<div>{{count}}</div>
</template>

生命周期函数

import { onUpdated, onMounted } from 'vue'
export default {
    setup() {
        onUpdated(() => {
            console.log(1)
        }
        onMounted(() => {
            console.log(1)
        }
    }
}

只需要将之前的生命周期改成onXXX的形式即可,需要注意的是createdbeforeCreate两个钩子被删除了,生命周期函数只能在setup函数里使用。

除此之外,还有一些其他api,建议详细通读一遍官方文档,然后在实际使用中加以巩固。

结尾

使用组合式api还是需要一点时间来适应的,首先需要能区分refreactive,不要在基本类型和应用类型之间搞混、响应式和非响应式对象之间迷路,其次就是如何拆分好每一个use函数,组合式api带来了更好的代码组织方式,但也更容易把代码写的更难以维护,比如setup函数巨长。

简单总结一下升级思路,data选项里的数据通过reactive进行声明,通过...toRefs()返回;computedmounted等选项通过对应的computedonMounted等函数来进行替换;methods里的函数随便在哪声明,只要在setup函数里返回即可。

通过这一套组合式api,使用范围其实已经不限于在vue里,完全可以拎出来用在其他地方:

import { reactive, watchEffect } from '@vue/composition-api'
let data = reactive({
    count: 0
})
watchEffect(() => {
    renderTemplate(data)//你的模板渲染函数
})

好了,不说了,我要去尝试一下它了。

参考文章

https://vue-composition-api-rfc.netlify.app/zh/

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

举报
请填写举报理由
0/200