回到部落格

2024-02-19

【React-Vue】Vue.js的state管理-Ref()與DOM

【React-Vue】Vue.js的state管理-Ref()與DOM

原文發表於 Medium

之前,一位精通Vue的前端前輩對我說:「React開發者不就只會用一堆的useState嗎?」。這我懂,因為最初看到使用一堆useState的code時候,我覺得整段的程式碼彷彿失去了靈性⋯

import React, { useState } from ‘react’;

const MyComponent = () => {  
  const [count, setCount] = useState(0);  
  const [text, setText] = useState(‘Initial text’);  
  const [isActive, setIsActive] = useState(false);  
  const [data, setData] = useState([]);  
  const [color, setColor] = useState(‘#3498db’);  
  
  // 某些操作邏輯、handler、helper function …  
  
  return (  
    <div>  
      <p>Count: {count}</p>  
      <p>Text: {text}</p>  
      <p>Active: {isActive ? ‘Yes’ : ‘No’}</p>  
      <ul>  
        {data.map((item, index) => (  
          <li key={index}>{item}</li>  
        ))}  
      </ul>  
      <div style={{ backgroundColor: color, width: ‘100px’, height: ‘100px’ }}>  
        {/*剩餘的其他東西,按鈕、slide等等*/}  
      </div>  
    </div>  
  );  
};  
  
export default MyComponent;

當然,這其實是可以解決的問題,例如考慮使用useReducer Hook,或者使用Redux等狀態管理套件(可參考文章:【React新手村】What is Redux? ),會讓複雜度與後續維護成本下降不少。

但後來,我覺得使用多個useState管理狀態,其實也滿直觀的。我們可以等到等到組件的功能完善後,再把整個state改寫成簡潔的useReducer ,這樣開發前期也比較不麻煩(但我最後極可能懶得改寫)。

Vue的狀態管理又是怎麼樣呢?接下來我們就一個簡單的案例,來看看Vue的寫法:

//index.vue

<template>  
  <div :style="{ backgroundColor: bgColor }">  
    <button @click="changeColor">切換背景顏色</button>  
  </div>  
</template>  
  
<script setup>  
import {  ref } from "vue";  
const bgColor = ref('#3498db');  
  
  
const changeColor = () => {  
  bgColor.value = randomColor();  
};  
  
const randomColor = () => {  
  const colors = [  
    '#f44336',  
    '#e91e63',  
    '#9c27b0',  
    '#673ab7',  
    '#3f51b5',  
    '#2196f3',  
    '#03a9f4',  
    '#00bcd4',  
    '#009688',  
    '#4caf50',  
    '#8bc34a',  
    '#cddc39',  
    '#ffeb3b',  
    '#ffc107',  
    '#ff9800',  
    '#ff5722',  
  ];  
  return colors[Math.floor(Math.random() * colors.length)];  
};  
</script>  
  
<style>  
h1 {  
  font-size: 24px;  
  margin: 20px;  
}  
  
input {  
  padding: 10px;  
  margin: 20px;  
  font-size: 16px;  
}  
  
button {  
  padding: 10px 20px;  
  margin: 20px;  
  font-size: 16px;  
  background-color: #3498db;  
  color: #fff;  
}  
</style>

閱讀 Vue檔有個訣竅,身為React的我們可把<template>部分當作是React Functional component 所 return 的渲染架構內容。<script>的部分則是 return 以上部分的程式碼,這樣就比較好懂了。

觀察上方程式碼中的<script>可以發現:關鍵數值bgColor被包覆在ref函數裡面。只要使用ref包覆起來的值,可以更改.value值的方式來更新該值,同時也會刷新<template> ,Vue藉由這項功能就可以創造具有互動性的網頁。

補充:上方的 _< script>_標籤中有個 _setup_ 屬性,它是Vue.js 3.2推出的語法糖,讓我們設定資料更加直觀方便,寫起來也更有React的風格。有關 _< script setup>_的相關說明,請見官方的 說明

補充:上方的 _< script>_標籤中有個 _setup_ 屬性,它是Vue.js 3.2推出的語法糖,讓我們設定資料更加直觀方便,寫起來也更有React的風格。有關 _< script setup>_的相關說明,請見官方的 說明

到此做個小結:

//Vue.js
const bgColor = ref('#3498db');

const changeColor = () => {  
  bgColor.value = randomColor();  
};  
  
  
//效果等同於下方的程式碼  
//React JSX  
  
  const [bgColor, setBgColor] = useState('#3498db');  
  
  const changeColor = () => {  
    setBgColor(randomColor());  
  };

乍看之下ref還滿像React的useRef(),但其實兩者有一些不小的差異,這點將會再寫一篇文章來討論。

奇怪的DOM屬性

以上只是小菜一碟,因為在我看來Vue在script部分並沒有什麼怪異的地方,反而更多的怪僻是展現在template裏頭。例如,Vue在DOM標籤如<div><input><button>上面加入許多讓人費解的屬性,像上述的案例中:

<template> <div :style="{ backgroundColor: bgColor }"> <button @click="changeColor">切換背景顏色</button> </div> </template>
```jsx參照如下  
    <div style={{ backgroundColor: bgColor }}>  
      <button onClick={changeColor}>切換背景顏色</button>  
    </div>  
```

欸,那個:style確定沒有打錯嗎?那個@click又是什麼碗糕啊?!

  • :style 指令:用於動態連結元件的style樣式,讓樣式可以隨著某個指定數據做改變。在此範例中,div 元素的背景顏色會根據 bgColor 變數的值而改變。
    • @click 指令:用於監聽元件的點擊事件。在此範例中,當使用者點擊按鈕時,會呼叫 changeColor 函數。

這些特殊的屬性被用來連接<Script>中的資料,除了上述這些用或者當前綴的狀況外,有時還看得到V-開頭的DOM屬性。相對於React DOM元素接近原始HTML的語法,不熟悉的人在頭一次看到這種特殊的寫法時,還是很驚嚇的,。

總之,在我看來:

  • Vue特點:將運行的精髓融入到特殊的DOM屬性上,使用這些屬性來處理需要被渲染的數據。
    • React特點:設計更多的邏輯函數、Hooks來處理需要被渲染的數據。

Vue擁護者認為React檔案中存在很多很繁複的JS語法,而熟悉React的人也覺得Vue檔案裡那些沒看過的Attribute很嚇人。雙方彼此的特點導致了React寫起來像是寫 JS而比較不像傳統前端,而Vue則更貼近HTML的傳統撰寫邏輯。

雙方都有大量的擁護者,該說是「人各有癖」嗎?

回到一開始的命題,React的狀態管理方式看起來很噁心嗎?我們來看看Vue的版本:

<template> <div> <p>Count: {{ count }}</p> <p>Text: {{ text }}</p> <p>Active: {{ isActive ? 'Yes' : 'No' }}</p> <ul> <li v-for="(item, index) in data" :key="index">{{ item }}</li> </ul> <div :style="{ backgroundColor: color, width: '100px', height: '100px' }"> </div> </div> </template>
<script setup>  
const count = ref(0);  
const text = ref('Initial text');  
const isActive = ref(false);  
const data = ref([]);  
const color = ref('#3498db');  
  
// 某些操作邏輯、methods、helper function …  
</script>

啊不就也一堆 ref() ?(被揍)

其實今天我原本還想講v-model的,其實它才是Vue的一大重點。但礙於篇幅,只能期待下篇啦!

謝謝各位的閱讀,跟各位討三個掌聲,成就資助我繼續寫下去的動力唷!