ปฏิกิริยาใน 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