在vue2中我们抽取代码的方式只有mixin一种方式,mixin确实也成功起到了代码抽离的作用,但是这种方式也带来了许多问题。尤其在大型项目中几十个甚至更多个mixin一起混入那将是灾难。
mixin带来的问题
1、如果我们要去查看一段代码逻辑,这段代码刚好又是mixin混入的,运气好能在第一个mixin文件中找到,运气不好可能需要连续找四五个mixin才能找到你要看的代码逻辑。
2、当你找到你想要的代码逻辑,你发现逻辑里面有个变量需要分析一下,当前文件一搜索,空空如也。然后你又开始在一层层mixin中查找这个变量。
3、混入很多mixin的时候,本地开发热更新也会明显变慢,改动一句代码就触发十几个mixin一起加载,导致开发体验很差
4、变量容易被覆盖,如果mixin中有一个很常见的变量,例如list,很多人在自己vue文件可能也会存在list这个变量,但是他并没有去查看mixin,那这个变量就被覆盖了,可能就导致mixin中的逻辑出现了问题
上面罗列了一些使用
mixin中常见的一些问题,可能还有一些没有提及的,但是我们还是不能否认mixin的作用,在小型项目中合理使用mixin还是可以很好的抽离代码逻辑
Composition API
Composition API是vue3推出来的新的代码组合方式,中文翻译过来的意思就是组合式 API,那何为组合式 API呢?
组合式 API 就是以函数的方式,将响应式变量和代码逻辑组合在一起,对外暴露响应式变量和方法。官方也称这种函数为
hook,跟react中的hook是没有差别的,但是内部实现是完全不同的,vue团队也说了是借鉴了react
有了
Composition API这种代码组织方式,完全可以取代之前的mixin的功能,并且解决了mixin所带来的那些问题。使用函数的方式也让前端开发者开发体验更舒服
虽然Composition API是vue3推出来的新功能,但是vue团队把这个功能也更新到了vue2中,这里给vue团队点赞👍
在 Vue2 中使用 Composition API
在vue2中使用Composition API需要根据vue2的版本来区分对待。在vue2.7版本之前,Composition API是作为vue2的一个插件来提供的,开发者需要在项目中安装Composition API的npm包,然后作为插件安装即可。相关的api也要从这个npm包中导入
vue2.7以后官方把Composition API的功能集成进了vue里面,开发者不需要再手动安装npm包了,直接使用即可,相关的api也直接可以从vue中导入。
vue2.7版本之前使用 Composition API
// 安装
+ npm i @vue/composition-api
// 在main.js中使用插件
+ import Vue from 'vue'
+ import CompositionApi from '@vue/composition-api'
+ Vue.use(CompositionApi)
// 在hook中使用
+ import { ref, onMounted } from '@vue/composition-api'
+ const useAge = (initialAge) => {
+ const age = ref(initialAge)
+ const getAge = () => {
+ return age.value
+ }
+ onMounted(() => {
+ console.log(`age:${age.value}`)
+ })
+ }
// vue文件中使用
+ export default {
+ setup() {
+ const { age, getAge } = useAge(18)
+ }
+ }
vue2.7版本使用 Composition API
// 2.7版本不需要安装
- npm i @vue/composition-api
// 2.7版本不需要在main.js中使用插件
- import Vue from 'vue'
- import CompositionApi from '@vue/composition-api'
- Vue.use(CompositionApi)
// 在hook中使用
- import { ref, onMounted } from '@vue/composition-api'
+ import { ref, onMounted } from 'vue'
+ const useAge = () => {
+ const age = ref(18)
+ const getAge = () => {
+ return age.value
+ }
+ onMounted(() => {
+ console.log(`age:${age.value}`)
+ })
+ }
// vue文件中使用
+ export default {
+ setup() {
+ const { age, getAge } = useAge(18)
+ }
+ }
如果在
vue2.7使用了Composition API插件,就会导致插件使用ref等响应式api的时候会被同时收集两次而导致意想不到的问题。所以在vue2中使用的时候首先就是要确认自己的vue版本信息。
查看自己的vue版本信息的时候不能单纯的看
package.json中的版本,因为很多项目的vue版本都不是2.7,但是项目又没有锁定版本号。当新人或者重新拉取项目安装依赖的时候,实际上安装的vue版本是最新的2.7。我们最好在node_modules中找到对应的包,然后去查看对应包的package.json的version。
Composition API 使用(踩坑)指南
以下所有的的试例vue版本默认都是在vue2.7版本中
Composition API获取this
在vue里面我们很多方法都依赖当前的组件实例,但是在Composition API中this就是个普通的变量了,不再是当前的组件实例,那如果要在Composition API获取组件实例该怎么做呢?
vue对外暴露了一个getCurrentInstance方法可以在setup中获取到当前的组件实例。这个方法只能在setup中使用,非setup中使用会报错。
但是getCurrentInstance方法在vue2和vue3中是有些区别的
vue2中getCurrentInstance().proxy才是组件实例
vue3中getCurrentInstance()就可以直接获取到组件实例了
import { getCurrentInstance } from 'vue'
export default {
setup() {
const { age, getAge } = useAge(18)
const vm = getCurrentInstance()
}
}
思考
setup中也可以将当前的this指向组件实例,为啥vue内部没有这样做呢?
在vue2中的options中不管在哪this都会指向当前的组件实例,这是vue2内部的特殊处理,这种改变this指向本身就有点破坏了js的编程规范,也会让刚接触的vue的人感到疑惑,加上在setup中基本上使用不到this了,所以让this回归他原本的作用也更贴合我们的编程规范
关于Ref API
在Composition API中我们设置一个响应式变量主要是通过ref和reactive两个API来实现了。在vue3中响应式变量是通过Proxy重新实现,弥补了vue2响应式的一些不足,比如对象未定义的属性直接添加不会加入响应式,数组通过下标访问不会触发副作用等等。但是在vue2中虽然把Composition API更新过来了,不过内部的响应式还是使用的getter和setter,所以vue2中使用Composition API还是有之前的响应式丢失问题。
再回过头来聊这个refapi,在Composition API中优先还是推荐使用ref,一个是使用ref需要通过它的value属性去读区值,我们可以很好辨别当前哪些是响应式变量哪些是非响应式变量。当前社区里面也是更推荐使用ref而非reactive,但是都不是绝对的。
上面我们提到过怎么获取组件实例,如果我们通过组件实例去访问一个ref变量的时候,是不需要读取ref的value属性,直接使用就可以,组件实例会自动帮我们进行一个类似解包的操作
关于组件实例中
ref响应式变量的“自动解包”
其实内部的原理是很简单的,vue内部对setup的返回值进行了一层Proxy拦截,在setter中帮我们给value附值,在getter帮我们读取value的值
“解包”原理
const proxyRef = new Proxy(objectWithRefs, {
get: (target, key, receiver) => unref(Reflect.get(target, key, receiver)),
set: (target, key, value, receiver) => {
const oldValue = target[key]
if (isRef(oldValue) && !isRef(value)) {
oldValue.value = value
return true
} else {
return Reflect.set(target, key, value, receiver)
}
},
})
const { setup } = options
const setupResult = proxyRef(setup())
在 Composition API中获取使用的组件或者dom元素
之前我们获取dom元素都是通过在组件或者dom元素上设置一个ref属性,然后通过this.$refs.设置的ref名称来获取,但是因为setup中this不再指向当前组件实例了,所以我们获取dom也换了方式
<template>
<div>
<h1 ref="titleRef">h1</h1>
<home-page ref="homePageRef"/>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
// 设置和ref相同的名称,会自动绑定上template中设置的ref
const titleRef = ref()
const homePageRef = ref()
return { titleRef, homePageRef }
}
}
</script>