那個(gè)忙了一夜的Vue3動(dòng)畫很好,就是太短了
(給前端大學(xué)加星標(biāo),提升前端技能.)
轉(zhuǎn)自:?蝸牛老濕_大圣
這個(gè)文章其實(shí)很簡單, 只要能說明composition的好處,就是極好的,我們用一個(gè)非常簡單的萬金油場景,比如我們有一個(gè)非常簡單的to do list
回顧Option
<template>
??<div?id="app">
????<input?type="text"?v-model="val"?@keyup.enter="addTodo">
????<ul>
??????<li?v-for="todo?in?todos"?:key="todo.id">{{todo.title}}li>
????ul>
??div>
template>
<script>
export?default?{
??data(){
????return{
??????val:'',
??????todos:[?
????????{id:0,?title:'吃飯',?done:false},
????????{id:1,?title:'睡覺',?done:false},
????????{id:2,?title:'lsp',?done:false},
??????]
????}
??},
??methods:{
????addTodo(){
??????this.todos.push({
????????id:this.todos.length,
????????title:this.val,
????????done:false
??????})
??????this.val?=?''
????}
??}
}
script>
需求復(fù)雜之后,就會(huì)多出watch,computed,inject,provide等配置,這個(gè).vue文件也會(huì)逐漸增大

Option的缺陷--反復(fù)橫跳
相信大部分同學(xué)都維護(hù)過超過200行的.vue組件,新增或者修改一個(gè)需求,就需要分別在data,methods,computed里修改 ,滾動(dòng)條反復(fù)上下移動(dòng),我稱之為『反復(fù)橫跳』 比如我們簡單的加個(gè)拍腦門的需求 加個(gè)累加器 ,這種寫代碼上下反復(fù)橫條的感覺, 相信大家都懂的,?

動(dòng)畫演示
<template>
??<div?id="app">
????<h1?@click="add">LSP?{{count}}號(hào)?double?is{{double}}h1>
????<input?type="text"?v-model="val"?@keyup.enter="addTodo">
????<ul>
??????<li?v-for="todo?in?todos"?:key="todo.id">{{todo.title}}li>
????ul>
??div>
template>
<script>
import?Counter?from?'./counter'
export?default?{
??mixins:[Counter],
??data(){
????return{
??????count:1,
??????val:'',
??????todos:[?
????????{id:0,?title:'吃飯',?done:false},
????????{id:1,?title:'睡覺',?done:false},
????????{id:2,?title:'lsp',?done:false},
??????]
????}
??},
??computed:?{
????double()?{
??????return?this.count?*?2
????}
??},
??methods:{
????addTodo(){
??????this.todos.push({
????????id:this.todos.length,
????????title:this.val,
????????done:false
??????})
??????this.val?=?''
????},
????add(){
??????this.count++
????}
??}
}
script>
Option的缺陷:mixin和this
反復(fù)橫跳的本質(zhì),在于功能的分塊組織,以及代碼量太大了,如果我們能把代碼控制在一屏,自然就解決了,vue2里的解決方案,是使用mixin來混合, 我們抽離一個(gè)counter.js
export?default?{
??data()?{
????return?{
??????count:1
????}
??},
??computed:?{
????double()?{
??????return?this.count?*?2
????}
??},
??methods:{
????add(){
??????this.count++
????}
??}
}
在App.vue中
import?Counter?from?'./counter'
export?default?{
??mixins:[Counter],
??data(){
?...
??},
...
}
這樣確實(shí)拆分了代碼,但是有一個(gè)賊嚴(yán)重的問題,就是不打開counter.js,App.vue里的this上,count,add這些屬性,是完全不知道從哪來的,你不知道是mixin,還是全局install,還是Vue.prototype.count設(shè)置的,數(shù)據(jù)來源完全模糊,調(diào)試爽死你,這也是option的一個(gè)大問題,this是個(gè)黑盒,template里寫的count和double,完全不知道從哪來的
mixin命名沖突
如果有兩個(gè)mixin,就更有意思了,比如我們又有一個(gè)需求,實(shí)時(shí)顯示鼠標(biāo)的坐標(biāo)位置x,并且有一個(gè)乘以2的計(jì)算屬性湊巧也叫double,再整一個(gè)mixin
export?default?{
??data()?{
????return?{
??????x:0
????}
??},
??methods:{
????update(e){
??????this.x?=?e.pageX
????}
??},
??computed:{
????double(){
??????return?this.x*2
????}
??},
??mounted(){
????window.addEventListener('mousemove',?this.update)
??},
??destroyed(){
????window.removeEventListener('mousemove',?this.update)
??}
}
這是是一個(gè)獨(dú)立維護(hù)的mixin,可能在N個(gè)地方用到,他根本不知道會(huì)不會(huì)有人和他沖突,然后用一下
import?Counter?from?'./counter'
import?Mouse?from?'./mouse'
export?default?{
??mixins:[Counter,Mouse],
??......?
}
兩個(gè)mixin里都有double這個(gè)數(shù),尷尬,看效果 ,lsp的count被覆蓋了 很尷尬,而且在App.vue這里,你完全不知道這個(gè)double到底是哪個(gè),調(diào)試很痛苦
Composition
composition就是為了解決這個(gè)問題存在的,通過組合的方式,把零散在各個(gè)data,methods的代碼,重新組合,一個(gè)功能的代碼都放在一起維護(hù),并且這些代碼可以單獨(dú)拆分成函數(shù) ,也就是大帥的這兩個(gè)gif

我們用vue3演示一下功能,具體api就不解釋了 直接vue3文檔搞起就可以
<template>
??<div?id="app">
????<input?type="text"?v-model="val"?@keyup.enter="addTodo">
????<ul>
??????<li?v-for="todo?in?todos"?:key="todo.id">{{todo.title}}li>
????ul>
??div>
template>
<script>
import?{reactive,?ref,?toRefs}?from?'vue'
export?default?{
??setup(){
????let?val?=?ref('')
????let?todos?=?reactive([?
????????{id:0,?title:'吃飯',?done:false},
????????{id:1,?title:'睡覺',?done:false},
????????{id:2,?title:'lsp',?done:false},
????])
????function?addTodo(){
??????todos.push({
????????id:todos.length,
????????title:val.value,
????????done:false
??????})
??????val.value?=?''
????}
????return?{val,?todos,?addTodo}
??}
}
script>
利用函數(shù)我們可以吧功能完整獨(dú)立的拆分成模塊或者函數(shù),方便組織代碼,并且解決了mixin混亂的問題
比如我們的累加器 ,抽離一個(gè)counter.js
import?{ref,?computed}?from?'vue'
export?default?function?useCounter(){
????let?count?=?ref(1)
????function?add(){
????????count.value++
????}
????let?double?=?computed(()=>count.value*2)
????return?{count,?double,?add}
}
直接使用
import?{reactive,?ref,?toRefs}?from?'vue'
+?import?useCounter?from?'./counter'
export?default?{
??setup(){
????let?val?=?ref('')
?...
+?????let?{count,double,add}?=?useCounter()?
????return?{
??????val,?todos,?addTodo,
+?????count,double,add
????}
??}
}
再來一個(gè)鼠標(biāo)位置也不在話下,而且可以很好地利用解構(gòu)賦值的別名,解決mixin的命名沖突問題 mouse.js
import?{ref,?onMounted,?onUnmounted,?computed}?from?'vue'
export?default?function?useMouse(){
??let?x?=?ref(0)
??function?update(e){
????x.value?=?e.pageX
??}
??let?double?=?computed(()=>x.value*2)
??onMounted(()=>{
????window.addEventListener('mousemove',?update)
??})
??onUnmounted(()=>{
????window.removeEventListener('mousemove',?update)
??})
??return?{x,?double}
??
}
模板里直接用doubelX
let?{count,double,add}?=?useCounter()?
let?{x,?double:doubleX}?=?useMouse()
return?{
??val,?todos,?addTodo,
??count,double,add,
??x,doubleX
}
script setup
到這里應(yīng)該就把大帥的文章缺的代碼補(bǔ)了一下,不過有的同學(xué)可能,還有一個(gè)小小的吐槽,那就是setup函數(shù)最后的return也是集中的,如果行數(shù)太多,一樣會(huì)橫條一下下,這個(gè)好解決,因?yàn)楸旧砦覀兛梢园蓆odos也抽離成函數(shù),這樣setup就全部是數(shù)據(jù)的來源,非常精簡絲滑
import?useCounter?from?'./counter'
import?useMouse?from?'./mouse'
import?useTodo?from?'./todos'
export?default?{
??setup(){
????let?{?val,?todos,?addTodo?}?=?useTodo()
????let?{count,double,add}?=?useCounter()?
????let?{x,?double:doubleX}?=?useMouse()
????return?{
??????val,?todos,?addTodo,
??????count,double,add,
??????x,doubleX
????}
??}
}
是不是賊爽呢,如果有些同學(xué)就是不想啥都抽離,還是覺得統(tǒng)一return很麻煩, 我們可以使用vue3的setup script功能,把setup這個(gè)配置也優(yōu)化掉 一個(gè)功能export一次
<script?setup>
import?useCounter?from?'./counter'
import?useMouse?from?'./mouse'
import?useTodo?from?'./todos'
let?{?val,?todos,?addTodo?}?=?useTodo()
export?{val,?todos,?addTodo}
let?{count,double,add}?=?useCounter()
export?{count,double,add}
let?{x,?double:doubleX}?=?useMouse()
export?{x,doubleX}
script>
具體看這里:
https://github.com/vuejs/rfcs/blob/sfc-improvements/active-rfcs/0000-sfc-script-setup.md


