首頁>技術>

這個文章其實很簡單, 只要能說明composition的好處,就是極好的,我們用一個非常簡單的萬金油場景,比如我們有一個非常簡單的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>

需求複雜之後,就會多出watch,computed,inject,provide等配置,這個.vue檔案也會逐漸增大

Option的缺陷--反覆橫跳

相信大部分同學都維護過超過200行的.vue元件,新增或者修改一個需求,就需要分別在data,methods,computed裡修改 ,捲軸反覆上下移動,我稱之為『反覆橫跳』 比如我們簡單的加個拍腦門的需求 加個累加器 ,這種寫程式碼上下反覆橫條的感覺, 相信大家都懂的,

動畫演示

<template>  <div id="app">    <h1 @click="add">LSP {{count}}號 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

反覆橫跳的本質,在於功能的分塊組織,以及程式碼量太大了,如果我們能把程式碼控制在一屏,自然就解決了,vue2裡的解決方案,是使用mixin來混合, 我們抽離一個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(){ ...  },...}

這樣確實拆分了程式碼,但是有一個很嚴重的問題,就是不開啟counter.js,App.vue裡的this上,count,add這些屬性,是完全不知道從哪來的,你不知道是mixin,還是全域性install,還是Vue.prototype.count設定的,資料來源完全模糊,除錯爽死你,這也是option的一個大問題,this是個黑盒,template裡寫的count和double,完全不知道從哪來的

mixin命名衝突

如果有兩個mixin,就更有意思了,比如我們又有一個需求,實時顯示滑鼠的座標位置x,並且有一個乘以2的計算屬性湊巧也叫double,再整一個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)  }}

這是是一個獨立維護的mixin,可能在N個地方用到,他根本不知道會不會有人和他衝突,然後用一下

import Counter from './counter'import Mouse from './mouse'export default {  mixins:[Counter,Mouse],  ...... }

兩個mixin裡都有double這個數,尷尬,看效果 ,lsp的count被覆蓋了 很尷尬,而且在App.vue這裡,你完全不知道這個double到底是哪個,除錯很痛苦

Composition

composition就是為了解決這個問題存在的,透過組合的方式,把零散在各個data,methods的程式碼,重新組合,一個功能的程式碼都放在一起維護,並且這些程式碼可以單獨拆分成函式 ,也就是大帥的這兩個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>

利用函式我們可以吧功能完整獨立地拆分成模組或者函式,方便組織程式碼,並且解決了mixin混亂的問題

比如我們的累加器 ,抽離一個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    }  }}

再來一個滑鼠位置也不在話下,而且可以很好地利用解構賦值的別名,解決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

到這裡應該就把大帥的文章缺的程式碼補了一下,不過有的同學可能,還有一個小小的吐槽,那就是setup函式最後的return也是集中的,如果行數太多,一樣會橫條一下下,這個好解決,因為本身我們可以吧todos也抽離成函式,這樣setup就全部是資料的來源,非常精簡絲滑

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    }  }}

是不是賊爽呢,如果有些同學就是不想啥都抽離,還是覺得統一return很麻煩, 我們可以使用vue3的setup script功能,把setup這個配置也最佳化掉 一個功能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>

12
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • Python 與MongoDB和Redis叢集的讀寫