目录:
- 回顾:我们现在的编程模式。
- 一道惊雷:hooks 应运而生。
- Vue 的 hooks,composition-api 的基本使用。
- 使用体会与心得,我是怎么理解 composition-api 的。
- 编程范式:面向对象与函数式编程。
- 最后,我听到的值得分享的前辈的建议。
1、 回顾:我们现在的编程模式
首先这里有一个问题,我们现在的框架不是已经很完善了吗?为什么要学习 composition-api ?
回答这个问题之前就得先看看我们现在的编程模式,我们现在的模式编程有什么特点?以及开发中会遇到哪些问题?
要回答这个问题,就得回到我们现在框架上来,去了解它,让我们回到它最初的起点,看看当我们要创建一个应用的时候:
// main.js
// 我们实例化了一个 Vue 类
new Vue({
render: (h) => h(App),
}).$mount('#app');
当我们编写一个组件时
<template>
<div></div>
</template>
<script>
// 我们返回了一个对象
export default {
data() {
},
computed: {
},
methods: {
},
};
</script>
可以看出我们现在的编程模式完全基于面向对象
的思想进行开发,根据面向对象的编程特性,封装、继承、多态,其实这就已经满足了我们日常开发的几乎所有需求。面向对象确实很全面,但面向对象有没有缺点呢?或者说面向对象在前端开发中有没有缺点呢?
- 绑定关系冗长、复杂,易读性差
this
的指向容易混乱,导致出错的概率大大增加
2、一道惊雷:hooks 应运而生
基于以上开发的问题,react
在 19 年 2 月 4 日发布了首个颠覆性解决方案 hooks
在 v16.8.0 正式发布,正如 react
的当初做的一样,hooks
的出现再一次颠覆了前端的编程思维,既然面向对象我不好控制,那我就不用了; 既然 this
指向不明确,那我们就把 this
给干掉,当 this
指针头颅落地的时刻,也标志着面向对象在前端的死亡,至此,大航海时代正式到来。在引爆了前端开发的模式之后,Vue
紧赶慢赶,总算在20
年9
月18
日正式发布了 Vue 3.0
,one-piece终于正式面向大众!Vue 交出了自己的对 hooks 的答卷,composition-api 终于发布。随后也在不断优化更新中升级打怪,也就是在今年的 22
年 2
月 7
日,Vue 3
终于正式更名 vue-core
。果实能力终于觉醒!
3、composition-api 的基本使用
需要说明的是我们在项目中还没有使用 Vue3,而是在2.x项目中引入了@vue/composition-api 来进行composition-api 的开发,这样做的原因如下:
- proxy 的兼容性虽然已占了 93% , 但是另外 2% 的用户还是要考虑一下的,毕竟刚出来嘛
- 也是最大的原因,目前项目已经很稳定,重构代价巨大,因此,既能够使用 composition-api 又不用大型手术的方案最合理 。
下面正式开始使用,
最基础的创建一个响应式变量
// composition-api
import {
ref, reactive, defineComponent
} from '@vue/composition-api';
export default defineComponent({
setup() {
const num = ref(0)
const people = reactive({
name: "Bob",
age: 18
})
// !!! 注意:ref创建的值必须使用 .value 访问
console.log(num.value) // 0
console.log(people) // { name: "Bob",age: 18 }
return {
num,
people
}
}
})
为dom元素添加事件
<template>
<div @click="tapMe">BUTTON</div>
</template>
<script>
// composition-api
import {
defineComponent
} from '@vue/composition-api';
export default defineComponent({
setup() {
const tapMe = () => {
console.log(111)
}
return {
tapMe
}
}
})
</script>
以及通信相关 props, computed,$emit ...
// composition-api
import {
defineComponent, ref,computed
} from '@vue/composition-api';
export default defineComponent({
props: {
text: {
type: String,
default: '--'
}
},
setup(props, { emit }) {
const { text } = props
const money = ref(0)
const moneyText = computed(() => `${money.value}元`)
const send = () => {
emit('change', moneyText)
}
return {
money,
moneyText,
send
}
}
})
最后获取根组件的实例
// composition-api
import {
defineComponent
} from '@vue/composition-api';
export default defineComponent({
setup(props, { root }) {
const toPageIndex = () => {
root.$router.push({
path: '/index'
})
}
return {
toPageIndex
}
}
})
从篇幅就能看出来使用还是很简单的,但是问题来了,我也没看出这有啥好处啊,代码量也没减少啊,不就是写法不一样了吗?而且所有代码都挤在 setup 函数里,也太难受了,你确定 composition-api 不是为了 kpi 而诞生的?别急,我们细细来品。
4、 我对 composition-api 的理解
- 代码量确实没有变化,但是逻辑更清晰了,可读性也更高了
官网上其实就有了非常好的解释,下图就是一个我们之前代码的编辑之后的样子:
每个颜色代表各个业务逻辑相关逻辑,可以看到,代码全糅杂在一起,如果哪位老哥的代码量过大,写了一两千行的代码,相信也是没几个人愿意去看这样的代码了。
用代码对比就能知道,我们举个基本页面的例子
用代码说话就是,如果在之前我们会这么写
export default {
data() {
return {
banner: '',
tab: {
index: 0
},
list: []
}
},
computed: {
},
methods: {
clickBanner() {
console.log('clickBanner')
},
setBanner(value) {
this.banner = value
},
changeTab(value) {
this.tab.index = value
},
setList(list) {
this.list = list
},
clickItem(item) {
console.log('clickItem', item)
}
},
};
但是我们在使用了 composition-api 之后就会是这样
import {
ref, reactive, defineComponent
} from '@vue/composition-api';
export default defineComponent({
setup() {
// banner 图相关的逻辑
const banner = ref('')
const clickBanner = () {
console.log('clickBanner')
}
const setBanner = (value) {
banner.value = value
}
// tab 相关的逻辑
const tab = reactive({
index: 0
})
const changeTab = (value) {
tab.index = value
}
// list 相关的逻辑
const list = ref([])
const setList = (list) => {
list.value = list
},
const clickItem => (item) {
console.log('clickItem', item)
}
return {
// banner 图相关的逻辑
banner,
clickBanner,
setBanner,
// tab 相关的逻辑
tab,
changeTab,
// list 相关的逻辑
list,
setList,
clickItem,
}
}
})
每个业务代码相关的逻辑都紧紧站在一起,这样代码是不是就很清晰易读了。那仅仅就这样就可以吗?我们把组件拆分粒度更细一点不也一样吗?我们把 banner, tabs,List, 等组件一个个拆分,细粒度化组件。
这样做确实是可以的,我们在之前的业务开发中也是这么做的,但是后来产品加需求了,比如说:
我这个banner可能是个轮播,可能是个图片
我甚至我的每个页面的 List 的 UI 长得不一样,甚至于我的 Item 都不一样,且还会有一些其它逻辑,比如风云榜需求 List 的前 3 名需要单独拿出来展示
我这个页面还要个 PC 端,banner pc端用的字段还不一样
既然要分 pc 移动端,那就得实现 UI 与 逻辑 分离,我们之前为了解决这个问题,提出了 UI 组件与业务组件的解决方案,
如上图,问题确实解决了,但是,我们会发现一个问题,业务粒度拆分越来越细,层级越来越高,组件嵌套越来越繁杂。我们好像进入了无限套娃的模式。在实际开发中发现这样会带来一个最简单的问题,如果你要将一个页面UI改造,你就得处理各种通信,组件各种拆分,太复杂了,有可能你宁愿 ctrl + cv
一套代码也不愿意把组件拆细,更愿意维护 pc、 移动端两套代码。
那 composition-api 就能解决吗?我可以肯定的告诉你,是的 !
我们稍微改造一下代码,自定义一下hooks
// hooks.js
import {
ref, reactive
} from '@vue/composition-api';
// banner 图相关的逻辑
export function useBanner () {
const banner = ref('')
const clickBanner = () {
console.log('clickBanner')
}
const setBanner = (value) {
banner.value = value
}
return {
banner,
clickBanner,
setBanner,
}
}
// tab 相关的逻辑
export function useTab() {
const tab = reactive({
index: 0
})
const changeTab = (value) {
tab.index = value
}
return {
tab,
changeTab
}
}
// component.js
import {
defineComponent
} from '@vue/composition-api';
import { useBanner, useTab } from './hooks'
export default defineComponent({
setup() {
// banner 图相关的逻辑
const {
banner,
clickBanner,
setBanner,
} = useBanner()
// banner 图相关的逻辑
const {
tab,
changeTab
} = useTab()
return {
banner,
clickBanner,
setBanner,
tab,
changeTab,
// list 相关的逻辑
// ...
}
}
})
这样我们就实现了UI与组件的完全分离,并且肉眼可见的代码更清晰,而且由于是函数引用的,我们可以只引入我业务相关的代码,代码是不是就很清晰了,而且由于引入与使用明确,cli 在打包 tree-shaking 期间可以更好地压缩代码量。
5、点题:编程范式 - 面向对象与函数式编程
我们刚刚看到了 composition-api 在 UI 与逻辑分离上的精彩表现。但是为什么我们刚开始的面向对象就不行呢?这就要说回编程范式的问题了
我们在说到面向对象的时候,总是会想办法抽象出一个类,而类有两个最基本的概念,一个是属性,另外一个就是方法。将类实例化之后,返回一个个我们需要的对象,实例化返回的对象会继承继承所有类的属性,而对象绑定的方法本质是为了改变对象的状态。而到了我们的框架里面,状态变成了 data,方法变成了 methods, methods 和 data 强相关了,而 data 又和 我们的页面 UI 有着强绑定的关系,绑在一条船上,所以这就是为什么分不开的原因。
而 composition-api 是函数式编程的产物,函数式编程的特点就是只关心输入和输出。而函数天生就有着隔离性, 在 composition-api 的体现上就是反正我一定有一个返回值给你,对页面 UI 来说,只需要告诉它需要和哪个返回值绑定就行了。而 setup 就会处理好输出和页面 UI 的映射关系,这也就是为什么 composition-api为什么必须在 setup 里面的原因。
6、值得分享的前辈建议
- 注意身体
不是开玩笑,很多前辈都有职业病,不规范使用键盘导致鼠标手、久坐痔疮、长期低头伏案的颈椎病...。这里想提醒大家和自己:注意锻炼,记得喝水。
- 不必不如BAT
这句话是想说,我们有时候强行给自己设了上限,总觉得别人是我们永远无法达到的目标,比如可能认为自己的技术永远都不如 bat 的开发人员,这里我们需要一些精神胜利法,我们觉得,其实我们和 bat 的他们是一样的,他们只不过比我们多懂一点技术而已,比如他们可能会 更早使用 composition-api, 那我们就也学会;他们可能 ts 用的更好,那我就也去学会它;可能他们底层更扎实,那我们就补足,这样我们总有一天也能达到相应的水平
The End
大家互勉!