2024-02-19
【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>上面加入許多讓人費解的屬性,像上述的案例中:
```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的一大重點。但礙於篇幅,只能期待下篇啦!
謝謝各位的閱讀,跟各位討三個掌聲,成就資助我繼續寫下去的動力唷!