vue3代碼編寫

  • 2022 年 7 月 16 日
  • 筆記

vue3代碼編寫

團隊內的vue3已經升級一年,在這一年中vue也在不停的更新,為了最大化組合式api帶來的優勢,便需要合理規範代碼的編寫方式…

1.從vue2到vue3

  • vue2組件採用配置式API,導致同樣的功能塊分散,並和其他的功能塊混合。我們希望統一功能塊的代碼可以放在(封裝)一起,在空間上增加可讀性:

  • 總的來說,就是將一個大型的vue2代碼塊變成許多小型的vue2塊:

假設一個頁面中有三個功能模塊,分別是A,B,C,那麼一下就是寫法上的不同

1// vue2
2
3export default {
4  data() {
5    return {
6      dataA: dataA,
7      dataB: dataB,
8      dataC: dataC
9    }
10  },
11  computed: {
12    computedA() {
13      return dataA
14    },
15    computedB() {
16      return dataB
17    },
18    computedC() {
19      return dataC
20    }
21  },
22  mounted() {
23    this.methodA()
24    this.methodB()
25    this.methodC()
26  },
27  methods: {
28    async methodA() {
29      console.log(computedA)
30    },
31    async methodB() {
32      console.log(computedB)
33    },
34    async methodC() {
35      console.log(computedC)
36    }
37  }
38}
39
40// vue3
41
42export default {
43  setup() {
44    // A
45    const dataA = ref()
46    const computedA = computed(() => {
47      return dataA.value
48    })
49    const methodA = () => {
50      console.log(computedA.value)
51    }
52    onMounted(() => {
53      methodA()
54    })
55
56    // B
57    const dataB = ref()
58    const computedB = computed(() => {
59      return dataB.value
60    })
61    const methodB = () => {
62      console.log(computedB.value)
63    }
64    onMounted(() => {
65      methodB()
66    })
67
68
69    // C
70    const dataC = ref()
71    const computedC = computed(() => {
72      return dataC.value
73    })
74    const methodC = () => {
75      console.log(computedC.value)
76    }
77    onMounted(() => {
78      methodC()
79    })
80
81    return {
82      dataA: dataA,
83      computedA,
84      methodA,
85      dataB: dataB,
86      computedB,
87      methodB,
88      dataC: dataC
89      computedC,
90      methodC,
91    }
92  }
93}

可以看到原來只能在vue2中配置一次的屬性(computed等),setup中可以多次調用,從而實現了同一功能代碼的整合,並且你還可以將某些可復用的攜帶vueApi的代碼封裝:

1// C.js
2export default () => {
3  const dataC = ref()
4  const computedC = computed(() => {
5    return dataC.value
6  })
7  const methodC = () => {
8    console.log(computedC.value)
9  }
10  onMounted(() => {
11    methodC()
12  })
13
14  return {
15    dataC: dataC
16    computedC,
17    methodC
18  }
19}
20
21// 調用
22import C from ‘c.js’
23
24const c = C()

2. 代碼編寫優化

如上,我們已經不需要上下滾動編輯器以調試代碼了,但是有兩個問題希望可以解決。一個是寫法上更加靈活了也會導致混亂的問題,如果沒有遵循一定的規範,則代碼會變成面向過程似的結構,一篇到底;第二個是每個需要在模板中使用的變量與方法都需要收到return,相對麻煩

  • 藉助reactive和toRefs,重新規整代碼

1export default {
2  setup() {
3    // A
4    const A = reactive({
5      dataA: dataA,
6      computedA: computed(() => {
7        return dataA.value
8      }),
9      methodA() {
10        console.log(computedA.value)
11      }
12    })
13    onMounted(() => {
14      A.methodA()
15    })
16
17    // B
18    const B = reactive({
19      dataB: dataB,
20      computedB: computed(() => {
21        return dataB.value
22      }),
23      methodB() {
24        console.log(computedB.value)
25      }
26    })
27    onMounted(() => {
28      B.methodB()
29    })
30
31    return {
32      …toRefs(A),
33      …toRefs(B)
34    }
35  }
36}

這樣寫的好處在於,同一塊的功能被包裹在一個reactive中相對獨立(當然不同模塊之間可以相互調用),代碼結構更加的清晰,而且只需return整個模塊,模塊中新增的變量會自動導出在模板中可用

3. setup語法糖的發展

1. css變量

vue提出在單文件中使用當前實例的變量

1<script>
2export default {
3  setup () {
4    return {
5      opacity0,
6      font: {
7      weight100
8      }
9    }
10  }
11}
12
</script>
13
14<style>
15div {
16  opacityv-bind(opacity);
17  font-weightv-bind(‘font.weight’);
18}
19
</style>

在此語法糖之前,想通過變量修改樣式都需要通過在模板中直接綁定樣式對象或者以修改類名的方式來實現,現在可以直接使用css樣式變量了,不得不說是個很奈斯的語法糖

2. <script setup>和ref文檔

vue提出在單文件組件中引入<script setup>的類型,可以自動將所有頂級變量聲明暴露給模板使用,同時可以消除ref.value的寫法

1<script setup>
2// 引入的 Foo 組件可以直接在 template 里使用了!
3import Foo from ‘./Foo.vue’
4// 就像在普通的 setup() 中一樣編寫代碼,無須return變量
5ref: a = 1
6// 該變量可以像普通變量那樣使用
7console.log(a++)
8// 想要獲取到原本的變量的話需要在變量前面加一個💲符號
9console.log($count.value)
10const fn = () => {}
11<script>

至此許多具有爭議的論點被廣大開發者提出:
從寫法上,確實實使得代碼更加的簡潔,減少了組件聲明以及手動暴露變量的代碼,這也是編者比較認可的,但是確實帶來不少的問題

  • 根據文檔中提出,部分情況還是只能使用普通的<script>標籤,如:

    • 部分選項(inheritAttrs)無法聲明
    • 聲明只需要執行一次的代碼
    • 不支持使用render函數
  • 無法像普通的<script>標籤那樣只暴露部分變量,暴露所有變量必將導致生成的代碼變大,而暴露變量的問題,上面我們已經用toRefs解決大部分問題,相比之下,反而會產生下面的問題:

1<script setup>
2const data = reactive({
3  a1,
4  b2
5})
6// 因為語法糖自動暴露data,而模板中使用a變量的時候需要寫data.a,多了一層
7
8// 如果想在模板中直接使用a變量,則需要解構:
9const { a, b } = …toRefs(data)
10// 這樣又變成,每新增一個變量都要手動解構,與最開始的return無異,而且還要寫a.value
11
12// 如果想規避這個問題就不能使用reactive,則每個基礎數據都必須使用ref
13const a = ref(1)
14ref: b = 2
15// 這樣一來,又分散了代碼,產生了ref.value的問題
16<script>

  • 針對ref的語法糖,已經不在是javascript了,原來的vue正因為是逐漸式的框架帶來的便利,甚至於所寫的代碼可以直接在瀏覽器中運行(只要引入vue.js),而這樣的創新語法(至少對於vue來說),則只能存在於編譯之前,一時難以接受,並且遭到大家的反對。

  • 有一就有二,如果維持這樣的創新,那麼將來大家寫的就不再是js,而是被vue『挾持』的js了。況且這些語法糖只能在setup語法糖中使用,不利於封裝

總之大家的看法各異,在代碼編寫上必將產生不同的選擇,編者也擔心vue生態或許會分散,從而解決問題的效率變低

3. 新的ref文檔

因為以上提出的各種原因,以及社區開發者的反對意見,舊的ref語法糖最終還是被廢棄,而新的ref語法糖重新進入實驗

1<script setup>
2// 在setup語法糖中引入了全局變量$ref,直接使用可以不寫.value
3let a = $ref(1)
4// a可以直接使用不加.value
5a++
6</script>

此語法糖雖然遵守了js的語法,但是還是只能在setup語法糖中才能使用,而且不支持在單文件組件外使用,沒有讓這種語法生效在Vue的特定環境之外,所有編者還是有些失落

讓vue成為開發者編寫js的中間平台,對開發者來說始終會產生一些心理負擔,【改變js的編寫方式】與【改變編寫的內容】之間的區別,還是讓開發者小心翼翼的考慮,爭取不會有一天被vue『套住』。

只是可憐那波使用了第一波語法糖的開發人員,所幸團隊內還沒有在情況明朗之前貿然使用

4. ref語法糖改進文檔

新的ref語法糖過多久就又被改進了,改進版主要是把全局變量改為只有$這倆變量了

1<script setup>
2import { ref } from ‘vue’
3
4const a = $(ref(1))
5// a可以直接使用不加.value
6a++
7// $$可以得到a的ref變量
8console.log($$(a))
9
10// 也就是說
11const aRef = ref(1)
12const a = $(aRef)
13// a可以直接使用不加.value
14a++
15console.log(aRef === $$(a))
16</script>
17

暈,目前為止,所有ref語法糖都只能看看,不敢在項目里用