ปฏิกิริยาใน Vue
เผยแพร่แล้ว: 2022-03-10ในบทความนี้ เราจะมาดูปฏิกิริยาใน Vue มันทำงานอย่างไร และเราจะสร้างตัวแปรปฏิกิริยาโดยใช้วิธีการและฟังก์ชันที่สร้างขึ้นใหม่ได้อย่างไร บทความนี้มุ่งเป้าไปที่นักพัฒนาที่เข้าใจวิธีการทำงานของ Vue 2.x เป็นอย่างดี และต้องการทำความคุ้นเคยกับ Vue 3 ใหม่
เราจะสร้างแอปพลิเคชันง่ายๆ เพื่อให้เข้าใจหัวข้อนี้มากขึ้น รหัสสำหรับแอพนี้มีอยู่ใน GitHub
โดยค่าเริ่มต้น JavaScript จะไม่ตอบสนอง ซึ่งหมายความว่าหากเราสร้างตัวแปร boy
และอ้างอิงในส่วน A ของแอปพลิเคชันของเรา จากนั้นดำเนินการแก้ไข boy
ในส่วน B ส่วน A จะไม่อัปเดตด้วยค่าใหม่ของ boy
let framework = 'Vue'; let sentence = `${framework} is awesome`; console.log(sentence) // logs "Vue is awesome" framework = 'React'; console.log(sentence) //should log "React is awesome" if 'sentence' is reactive.
ตัวอย่างข้างต้นเป็นตัวอย่างที่สมบูรณ์แบบของลักษณะที่ไม่ตอบสนองของ JavaScript ดังนั้น เหตุใดการเปลี่ยนแปลงจึงไม่สะท้อนในตัวแปร sentence
ใน Vue 2.x นั้น props
, computed
และ data()
ล้วนมีปฏิกิริยาตามค่าเริ่มต้น ยกเว้นคุณสมบัติที่ไม่มีอยู่ใน data
เมื่อมีการสร้างส่วนประกอบดังกล่าว ซึ่งหมายความว่าเมื่อคอมโพเนนต์ถูกฉีดเข้าไปใน DOM คุณสมบัติที่มีอยู่ในออบเจ็กต์ data
ของคอมโพเนนต์เท่านั้นที่จะทำให้คอมโพเนนต์อัปเดตหากคุณสมบัติดังกล่าวเปลี่ยนแปลงและเมื่อใด
ภายใน Vue 3 ใช้อ็อบเจ็กต์ Proxy
(คุณลักษณะ ECMAScript 6) เพื่อให้แน่ใจว่าคุณสมบัติเหล่านี้เป็นรีแอกทีฟ แต่ยังคงมีตัวเลือกให้ใช้ Object.defineProperty
จาก Vue 2 สำหรับการสนับสนุน Internet Explorer (ECMAScript 5) วิธีการนี้กำหนดคุณสมบัติใหม่โดยตรงบนวัตถุ หรือแก้ไขคุณสมบัติที่มีอยู่บนวัตถุ และส่งกลับวัตถุ
เมื่อมองแวบแรกและเนื่องจากพวกเราส่วนใหญ่รู้อยู่แล้วว่าการเกิดปฏิกิริยาไม่ใช่เรื่องใหม่ใน Vue จึงอาจดูเหมือนไม่จำเป็นต้องใช้คุณสมบัติเหล่านี้ แต่ Options API ก็มีข้อจำกัดเมื่อคุณต้องจัดการกับแอปพลิเคชันขนาดใหญ่ที่มีฟังก์ชันที่ใช้ซ้ำได้ในหลายๆ อย่าง ส่วนของแอปพลิเคชัน ด้วยเหตุนี้ Composition API ใหม่จึงได้รับการแนะนำเพื่อช่วยในเรื่องตรรกะที่เป็นนามธรรม เพื่อให้อ่านและบำรุงรักษาฐานโค้ดได้ง่ายขึ้น นอกจากนี้ ในตอนนี้ เราสามารถสร้างปฏิกิริยาของตัวแปรได้อย่างง่ายดายโดยไม่คำนึงถึงประเภทข้อมูลโดยใช้คุณสมบัติและวิธีการใหม่
เมื่อเราใช้ตัวเลือก setup
ซึ่งทำหน้าที่เป็นจุดเริ่มต้นสำหรับ Composition API ออบเจ็กต์ data
คุณสมบัติที่ computed
และ methods
จะไม่สามารถเข้าถึงได้เนื่องจากอินสแตนซ์ของคอมโพเนนต์ยังไม่ถูกสร้างขึ้นเมื่อดำเนินการ setup
ซึ่งทำให้ไม่สามารถใช้ประโยชน์จาก ปฏิกิริยาในตัว ในคุณลักษณะเหล่านี้ใน setup
ได้ ในบทช่วยสอนนี้ เราจะเรียนรู้เกี่ยวกับวิธีการทั้งหมดที่เราสามารถทำได้
วิธีการทำปฏิกิริยา
ตามเอกสาร วิธีการ reactive
ซึ่งเทียบเท่ากับ Vue.observable()
ใน Vue 2.6 จะมีประโยชน์เมื่อเราพยายามสร้างวัตถุทั้งหมดที่มีคุณสมบัติเป็นปฏิกิริยา (เช่นวัตถุ data
ในตัวเลือก เอพีไอ) ภายใต้ประทุน ออบเจ็กต์ data
ใน Options API ใช้วิธีนี้เพื่อทำให้คุณสมบัติทั้งหมดในนั้นเกิดปฏิกิริยา
แต่เราสามารถสร้างวัตถุปฏิกิริยาของเราเองได้ดังนี้:
import { reactive } from 'vue' // reactive state let user = reactive({ "id": 1, "name": "Leanne Graham", "username": "Bret", "email": "[email protected]", "address": { "street": "Kulas Light", "suite": "Apt. 556", "city": "Gwenborough", "zipcode": "92998-3874", "geo": { "lat": "-37.3159", "lng": "81.1496" } }, "phone": "1-770-736-8031 x56442", "website": "hildegard.org", "company": { "name": "Romaguera-Crona", "catchPhrase": "Multi-layered client-server neural-net", "bs": "harness real-time e-markets" }, "cars": { "number": 0 } })
ที่นี่ เรานำเข้าวิธีการ reactive
จาก Vue จากนั้นเราประกาศตัวแปร user
ของเราโดยส่งค่าไปยังฟังก์ชันนี้เป็นอาร์กิวเมนต์ ในการทำเช่นนั้น เราได้ทำให้ user
มีปฏิกิริยา ดังนั้น หากเราใช้ user
ในแม่แบบของเรา และหากวัตถุหรือคุณสมบัติของวัตถุนี้ควรเปลี่ยนแปลง ค่านี้จะได้รับการอัปเดตโดยอัตโนมัติในแม่แบบนี้
ref
เช่นเดียวกับที่เรามีวิธีการในการทำให้วัตถุมีปฏิกิริยา เราก็จำเป็นต้องมีวิธีหนึ่งเพื่อสร้าง ค่าดั้งเดิมแบบสแตนด์อโลนอื่นๆ (สตริง บูลีน ค่าที่ไม่ได้กำหนด ตัวเลข ฯลฯ) และอาร์เรย์ที่ทำปฏิกิริยา ในระหว่างการพัฒนา เราจะทำงานร่วมกับประเภทข้อมูลอื่นๆ เหล่านี้ในขณะที่ต้องการให้มีปฏิกิริยาตอบสนองด้วย วิธีแรกที่เราอาจนึกถึงคือการใช้ reactive
และส่งผ่านค่าของตัวแปรที่เราต้องการให้เกิดปฏิกิริยา
import { reactive } from 'vue' const state = reactive({ users: [], });
เนื่องจาก reactive
มีคอนเวอร์ชันเชิงปฏิกิริยาเชิงลึก user
ในฐานะทรัพย์สินก็จะมีปฏิกิริยาเช่นกัน ด้วยเหตุนี้จึงบรรลุเป้าหมายของเรา ดังนั้น user
จะอัปเดตทุกที่ที่ใช้ในเทมเพลตของแอปดังกล่าวเสมอ แต่ด้วยคุณสมบัติ ref
เราสามารถสร้างตัวแปรใดๆ ที่มีประเภทข้อมูลใด ๆ ที่เป็นปฏิกิริยาโดยการส่งผ่านค่าของตัวแปรนั้นไปยัง ref
เมธอดนี้ใช้ได้กับอ็อบเจ็กต์เช่นกัน แต่จะซ้อนอ็อบเจ็กต์ลึกกว่าเมื่อใช้เมธอด reactive
หนึ่งระดับ
let property = { rooms: '4 rooms', garage: true, swimmingPool: false } let reactiveProperty = ref(property) console.log(reactiveProperty) // prints { // value: {rooms: "4 rooms", garage: true, swimmingPool: false} // }
ภายใต้ประทุน ผู้ ref
ใช้อาร์กิวเมนต์นี้ที่ส่งผ่านไปยังมันและแปลงเป็นวัตถุด้วยคีย์ของ value
ซึ่งหมายความว่า เราสามารถเข้าถึงตัวแปรของเราได้โดยการเรียก variable.value
และเรายังสามารถแก้ไขค่าของตัวแปรได้ด้วยการเรียกมันในลักษณะเดียวกัน
import {ref} from 'vue' let age = ref(1) console.log(age.value) //prints 1 age.value++ console.log(age.value) //prints 2
ด้วยวิธีนี้ เราสามารถนำเข้าผู้ ref
ไปยังองค์ประกอบของเราและสร้างตัวแปรปฏิกิริยาได้:
<template> <div class="home"> <form @click.prevent=""> <table> <tr> <th>Name</th> <th>Username</th> <th>email</th> <th>Edit Cars</th> <th>Cars</th> </tr> <tr v-for="user in users" :key="user.id"> <td>{{ user.name }}</td> <td>{{ user.username }}</td> <td>{{ user.email }}</td> <td> <input type="number" name="cars" v-model.number="user.cars.number" /> </td> <td> <cars-number :cars="user.cars" /> </td> </tr> </table> <p>Total number of cars: {{ getTotalCars }}</p> </form> </div> </template> <script> // @ is an alias to /src import carsNumber from "@/components/cars-number.vue"; import axios from "axios"; import { ref } from "vue"; export default { name: "Home", data() { return {}; }, setup() { let users = ref([]); const getUsers = async () => { let { data } = await axios({ url: "data.json", }); users.value = data; }; return { users, getUsers, }; }, components: { carsNumber, }, created() { this.getUsers(); }, computed: { getTotalCars() { let users = this.users; let totalCars = users.reduce(function(sum, elem) { return sum + elem.cars.number; }, 0); return totalCars; }, }; </script>
ที่นี่ เรานำเข้าผู้ ref
เพื่อสร้างตัวแปร users
ที่มีปฏิกิริยาในองค์ประกอบของเรา จากนั้นเรานำเข้า axios
เพื่อดึงข้อมูลจากไฟล์ JSON ในโฟลเดอร์ public
และนำเข้าส่วนประกอบ carsNumber
ซึ่งเราจะสร้างในภายหลัง สิ่งต่อไปที่เราทำคือสร้างตัวแปร users
ที่มีปฏิกิริยาโดยใช้วิธีการ ref
เพื่อให้ users
สามารถอัปเดตทุกครั้งที่การตอบสนองจากไฟล์ JSON ของเราเปลี่ยนแปลง
นอกจากนี้เรายังได้สร้างฟังก์ชัน getUser
ที่ดึงอาร์เรย์ users
จากไฟล์ JSON ของเราโดยใช้ axios และเรากำหนดค่าจากคำขอนี้ให้กับตัวแปร users
สุดท้าย เราได้สร้างคุณสมบัติที่คำนวณซึ่งคำนวณจำนวนรถยนต์ทั้งหมดที่ผู้ใช้ของเรามี ตามที่เราได้แก้ไขในส่วนเทมเพลต
สิ่งสำคัญคือต้องทราบว่าเมื่อเข้าถึงคุณสมบัติการ ref
ที่ส่งคืนในส่วนเทมเพลตหรือนอก setup()
คุณสมบัติเหล่านี้จะถูกเปิดออกโดยอัตโนมัติ ซึ่งหมายความว่า refs
ที่เป็นอ็อบเจ็กต์จะยังคงต้องการ .value
เพื่อที่จะเข้าถึงได้ เนื่องจาก users
คืออาร์เรย์ เราจึงสามารถใช้ users
เท่านั้น ไม่ใช่ users.value
ใน getTotalCars
ในส่วนเทมเพลต เราแสดงตารางที่แสดงข้อมูลของผู้ใช้แต่ละคน พร้อมด้วยส่วนประกอบ <cars-number />
ส่วนประกอบนี้ยอมรับเสา cars
ที่แสดงในแถวของผู้ใช้แต่ละคนตามจำนวนรถที่พวกเขามี ค่านี้จะอัปเดตเมื่อใดก็ตามที่มูลค่าของ cars
เปลี่ยนแปลงใน user object ซึ่งเป็นลักษณะที่อ็อบเจ็กต์ data
หรือคุณสมบัติที่ computed
จะทำงานหากเราทำงานกับ Options API
toRefs
เมื่อเราใช้ Composition API ฟังก์ชัน setup
จะยอมรับสองอาร์กิวเมนต์: props
และ context
props
นี้ถูกส่งผ่านจากส่วนประกอบไปยัง setup()
และทำให้สามารถเข้าถึงอุปกรณ์ประกอบฉากที่ส่วนประกอบมีจากภายใน API ใหม่นี้ วิธีนี้มีประโยชน์อย่างยิ่งเพราะช่วยให้สามารถ ทำลายโครงสร้างของวัตถุ ได้โดยไม่สูญเสียปฏิกิริยา
<template> <p>{{ cars.number }}</p> </template> <script> export default { props: { cars: { type: Object, required: true, }, gender: { type: String, required: true, }, }, setup(props) { console.log(props); // prints {gender: "female", cars: Proxy} }, }; </script> <style></style>
ในการใช้ค่าที่เป็นอ็อบเจ็กต์จาก props
ใน Composition API ในขณะเดียวกันก็รักษาความสามารถในการเกิดปฏิกิริยา เราใช้ toRefs
วิธีนี้ใช้วัตถุปฏิกิริยาและแปลงเป็นวัตถุธรรมดาซึ่งแต่ละคุณสมบัติของวัตถุปฏิกิริยาเดิมจะกลายเป็นตัว ref
นี่หมายความว่า cars
รองรับ...
cars: { number: 0 }
… ตอนนี้จะกลายเป็นสิ่งนี้:
{ value: cars: { number: 0 }
ด้วยเหตุนี้ เราจึงสามารถใช้ประโยชน์จาก cars
ภายในส่วนใดก็ได้ของ API การตั้งค่าในขณะที่ยังคงรักษาปฏิกิริยาได้
setup(props) { let { cars } = toRefs(props); console.log(cars.value); // prints {number: 0} },
เราสามารถดูตัวแปรใหม่นี้ได้โดยใช้ watch
ของ Composition API และตอบสนองต่อการเปลี่ยนแปลงนี้ไม่ว่าเราจะต้องการอย่างไรก็ตาม
setup(props) { let { cars } = toRefs(props); watch( () => cars, (cars, prevCars) => { console.log("deep ", cars.value, prevCars.value); }, { deep: true } ); }
toRef
กรณีการใช้งานทั่วไปอื่นๆ ที่เราอาจเผชิญคือ การส่งผ่านค่า ที่ไม่จำเป็นต้องเป็นอ็อบเจ็กต์ แต่เป็นประเภทข้อมูลประเภทหนึ่งที่ทำงานกับการ ref
(array, number, string, boolean, ฯลฯ) ด้วย toRef
เราสามารถสร้างคุณสมบัติปฏิกิริยา (เช่น ref
) จากวัตถุปฏิกิริยาต้นทาง การทำเช่นนี้จะช่วยให้มั่นใจได้ว่าคุณสมบัติจะยังคงมีปฏิกิริยาและจะอัปเดตทุกครั้งที่มีการเปลี่ยนแปลงแหล่งที่มาของพาเรนต์
const cars = reactive({ Toyota: 1, Honda: 0 }) const NumberOfHondas = toRef(state, 'Honda') NumberOfHondas.value++ console.log(state.Honda) // 1 state.Honda++ console.log(NumberOfHondas.value) // 2
ที่นี่ เราสร้างวัตถุรีแอกทีฟโดยใช้วิธี reactive
ด้วยคุณสมบัติ Toyota
และ Honda
. นอกจากนี้เรายังใช้ toRef
เพื่อสร้างตัวแปรปฏิกิริยาจาก Honda
จากตัวอย่างข้างต้น เราจะเห็นได้ว่าเมื่อเราอัปเดต Honda
โดยใช้วัตถุ cars
ที่มีปฏิกิริยาหรือ NumberOfHondas
ค่าจะได้รับการอัปเดตในทั้งสองกรณี
วิธีนี้คล้ายคลึงกันและยังแตกต่างจากวิธี toRefs
ที่เรากล่าวถึงข้างต้นในแง่ที่ว่ามันรักษาการเชื่อมต่อกับแหล่งที่มาและสามารถใช้สำหรับสตริง อาร์เรย์ และตัวเลขได้ ต่างจาก toRefs
เราไม่จำเป็นต้องกังวลเกี่ยวกับการมีอยู่ของคุณสมบัติในแหล่งที่มาในขณะที่สร้าง เพราะหากคุณสมบัตินี้ไม่มีอยู่ในขณะที่สร้างการ ref
นี้ และส่งคืน null
แทน มันจะยังคงถูกเก็บไว้ เป็นคุณสมบัติที่ถูกต้อง โดยมีรูปแบบของ watcher
ดังนั้นเมื่อค่านี้เปลี่ยนแปลง การ ref
นี้ที่สร้างโดยใช้ toRef
จะได้รับการอัปเดตด้วย
นอกจากนี้เรายังสามารถใช้วิธีนี้เพื่อสร้าง คุณสมบัติปฏิกิริยา จาก props
ซึ่งจะมีลักษณะดังนี้:
<template> <p>{{ cars.number }}</p> </template> <script> import { watch, toRefs, toRef } from "vue"; export default { props: { cars: { type: Object, required: true, }, gender: { type: String, required: true, }, }, setup(props) { let { cars } = toRefs(props); let gender = toRef(props, "gender"); console.log(gender.value); watch( () => cars, (cars, prevCars) => { console.log("deep ", cars.value, prevCars.value); }, { deep: true } ); }, }; </script>
ที่นี่ เราสร้างผู้ ref
ที่อิงตามคุณสมบัติ gender
ที่ได้รับจาก props
สิ่งนี้มีประโยชน์เมื่อเราต้องการดำเนินการเพิ่มเติมกับส่วนประกอบเฉพาะ
บทสรุป
ในบทความนี้ เราได้ดูว่าปฏิกิริยาใน Vue ทำงานอย่างไรโดยใช้วิธีการและฟังก์ชันที่เพิ่งแนะนำใหม่จาก Vue 3 เราเริ่มต้นด้วยการดูว่าปฏิกิริยาคืออะไรและ Vue ใช้ประโยชน์จากวัตถุ Proxy
เบื้องหลังเพื่อบรรลุเป้าหมายนี้อย่างไร นอกจากนี้เรายังมองว่าเราสามารถสร้างวัตถุปฏิกิริยาโดยใช้ reactive
และวิธีสร้างคุณสมบัติปฏิกิริยาโดยใช้ ref
สุดท้าย เราได้ดูวิธีการแปลงวัตถุปฏิกิริยาเป็นวัตถุธรรมดา ซึ่งแต่ละคุณสมบัติเป็นตัว ref
ที่ชี้ไปยังคุณสมบัติที่สอดคล้องกันของวัตถุดั้งเดิม และเราเห็นวิธีสร้างการ ref
สำหรับคุณสมบัติบนวัตถุต้นทางปฏิกิริยา
แหล่งข้อมูลเพิ่มเติม
- “พร็อกซี” (วัตถุ), MDN Web Docs
- “ความรู้พื้นฐานด้านปฏิกิริยา”, Vue.js
- “อ้างอิง”, Vue.js
- “การลงทะเบียนวงจรชีวิตภายใน
setup
”, Vue.js