ฉันเชื่อว่าการมีความเข้าใจเกี่ยวกับแรงจูงใจเบื้องหลังการกลายพันธุ์และการกระทำอนุญาตให้เราตัดสินได้ดีขึ้นว่าจะใช้เมื่อไหร่และอย่างไร นอกจากนี้ยังช่วยให้โปรแกรมเมอร์ปราศจากภาระความไม่แน่นอนในสถานการณ์ที่ "กฎ" กลายเป็นคลุมเครือ หลังจากให้เหตุผลเล็กน้อยเกี่ยวกับจุดประสงค์ของพวกเขาฉันก็สรุปได้ว่าถึงแม้จะมีวิธีที่ผิดในการใช้ Actions and Mutations แต่ฉันไม่คิดว่ามันมีวิธีการที่เป็นที่ยอมรับ
ก่อนอื่นเรามาลองทำความเข้าใจว่าทำไมเราถึงผ่านการกลายพันธุ์หรือการกระทำ
ทำไมต้องผ่านแผ่นความร้อนในตอนแรก ทำไมไม่เปลี่ยนสถานะโดยตรงในส่วนประกอบ?
การพูดอย่างเคร่งครัดคุณสามารถเปลี่ยนstate
ได้โดยตรงจากส่วนประกอบของคุณ นี่state
เป็นเพียงวัตถุ JavaScript และไม่มีอะไรน่าอัศจรรย์ที่จะคืนค่าการเปลี่ยนแปลงที่คุณทำไว้
// Yes, you can!
this.$store.state['products'].push(product)
อย่างไรก็ตามด้วยการทำเช่นนี้คุณจะทำให้การกลายพันธุ์ของรัฐกระจายไปทั่วสถานที่ คุณสูญเสียความสามารถเพียงแค่เปิดโมดูลเดียวที่อยู่อาศัยสถานะและอย่างรวดเร็วดูชนิดของการดำเนินการที่สามารถนำไปใช้กับมัน การกลายพันธุ์แบบรวมศูนย์จะช่วยแก้ปัญหานี้ได้แม้ว่าจะมีค่าใช้จ่ายสำหรับแผ่นบางสำเร็จรูป
// so we go from this
this.$store.state['products'].push(product)
// to this
this.$store.commit('addProduct', {product})
...
// and in store
addProduct(state, {product}){
state.products.push(product)
}
...
ฉันคิดว่าถ้าคุณเปลี่ยนบางสิ่งบางอย่างให้สั้นลงด้วยสำเร็จรูปคุณจะต้องการให้ชิ้นส่วนเล็ก ๆ ดังนั้นฉันจึงสันนิษฐานว่าการกลายพันธุ์นั้นหมายถึงการห่อหุ้มบาง ๆ รอบ ๆ การดำเนินงานดั้งเดิมในรัฐโดยแทบไม่มีตรรกะทางธุรกิจ กล่าวอีกนัยหนึ่งการกลายพันธุ์นั้นมีความหมายว่าส่วนใหญ่จะใช้เหมือนเซทเทอร์
ตอนนี้คุณได้รวมการกลายพันธุ์ของคุณไว้ที่ศูนย์กลางแล้วคุณมีภาพรวมที่ดีขึ้นเกี่ยวกับการเปลี่ยนแปลงสถานะของคุณและเนื่องจากการใช้เครื่องมือ (vue-devtools) ของคุณก็รับรู้ถึงตำแหน่งนั้น ๆ นอกจากนี้ยังควรคำนึงว่าปลั๊กอินของ Vuex หลายตัวไม่ได้ดูสถานะโดยตรงเพื่อติดตามการเปลี่ยนแปลงพวกเขาค่อนข้างพึ่งพาการกลายพันธุ์สำหรับสิ่งนั้น การเปลี่ยนแปลง "ไม่อยู่ในขอบเขต" ของรัฐจึงไม่สามารถมองเห็นได้
ดังนั้นmutations
, actions
สิ่งที่เป็นอยู่แล้วแตกต่างกันอย่างไร
การดำเนินการเช่นการกลายพันธุ์ยังอยู่ในโมดูลของร้านค้าและสามารถรับstate
วัตถุได้ ซึ่งหมายความว่าพวกเขายังสามารถกลายพันธุ์ได้โดยตรง แล้วประเด็นของการมีทั้งคู่คืออะไร? หากเราให้เหตุผลว่าการกลายพันธุ์จะต้องมีขนาดเล็กและเรียบง่ายก็หมายความว่าเราจำเป็นต้องมีวิธีการอื่นในการสร้างตรรกะทางธุรกิจที่ซับซ้อนยิ่งขึ้น การกระทำเป็นวิธีการที่จะทำเช่นนี้ และเนื่องจากตามที่เราได้กำหนดไว้ก่อนหน้านี้ vue-devtools และปลั๊กอินต่างตระหนักถึงการเปลี่ยนแปลงผ่านการกลายพันธุ์เพื่อให้สอดคล้องกันเราควรใช้การกลายพันธุ์จากการกระทำของเรา นอกจากนี้เนื่องจากการกระทำมีขึ้นเพื่อให้ครอบคลุมและตรรกะที่พวกเขาแค็ปซูลอาจไม่ตรงกันก็ทำให้รู้สึกว่าการกระทำก็จะทำแบบอะซิงโครนัสตั้งแต่เริ่มต้น
มักเน้นว่าการกระทำอาจไม่ตรงกันในขณะที่การกลายพันธุ์มักไม่ใช่ คุณอาจตัดสินใจเห็นความแตกต่างเป็นสิ่งบ่งชี้ว่าการกลายพันธุ์ควรใช้กับสิ่งใดก็ตามที่มีการซิงโครนัส อย่างไรก็ตามคุณจะพบปัญหาบางอย่างถ้าเช่นคุณต้องการคอมมิทมากกว่าหนึ่งการกลายพันธุ์ (ซิงโครนัส) หรือถ้าคุณต้องการทำงานกับ Getter จากการกลายพันธุ์ของคุณเนื่องจากฟังก์ชันการกลายพันธุ์จะไม่ได้รับเก็ตเตอร์และการกลายพันธุ์เป็นอาร์กิวเมนต์ ...
... ซึ่งนำไปสู่คำถามที่น่าสนใจ
เหตุใดการกลายพันธุ์จึงไม่ได้รับ Getters
ฉันยังไม่พบคำตอบที่น่าพอใจสำหรับคำถามนี้ ฉันได้เห็นคำอธิบายบางอย่างโดยทีมหลักที่ฉันพบว่าที่สงสัยที่สุด หากฉันสรุปการใช้งานของพวกเขา Getters นั้นตั้งใจที่จะคำนวณส่วนขยาย (และแคชบ่อย) ให้เป็นสถานะ กล่าวอีกนัยหนึ่งพวกเขายังคงเป็นรัฐแม้ว่าจะต้องมีการคำนวณล่วงหน้าและโดยปกติพวกเขาจะอ่านอย่างเดียว อย่างน้อยก็เป็นวิธีที่กระตุ้นให้ใช้งาน
ดังนั้นการป้องกันมิวเตชั่นจากการเข้าถึง Getters โดยตรงหมายความว่าหนึ่งในสามสิ่งจำเป็นในขณะนี้หากเราจำเป็นต้องเข้าถึงการทำงานบางอย่างที่เสนอโดยหลัง: (1) การคำนวณสถานะที่ Getter ทำซ้ำนั้นอยู่ที่ไหนสักแห่งที่สามารถเข้าถึงได้ เพื่อการกลายพันธุ์ (กลิ่นเหม็น) หรือ (2) การคำนวณตามตัวอักษร (หรือผู้ทะเยอทะยานที่เกี่ยวข้อง) นั้นถูกส่งผ่านเป็นอาร์กิวเมนต์ที่ชัดเจนเพื่อการกลายพันธุ์ (ขี้ขลาด) หรือ (3) ตรรกะของ Getter นั้นซ้ำกันโดยตรงภายในการกลายพันธุ์ ไม่มีประโยชน์เพิ่มเติมของการแคชตามที่ Getter จัดให้ (stench)
ต่อไปนี้เป็นตัวอย่างของ (2) ซึ่งในสถานการณ์ส่วนใหญ่ที่ฉันพบดูเหมือนว่าตัวเลือก "น้อยที่สุด"
state:{
shoppingCart: {
products: []
}
},
getters:{
hasProduct(state){
return function(product) { ... }
}
}
actions: {
addProduct({state, getters, commit, dispatch}, {product}){
// all kinds of business logic goes here
// then pull out some computed state
const hasProduct = getters.hasProduct(product)
// and pass it to the mutation
commit('addProduct', {product, hasProduct})
}
}
mutations: {
addProduct(state, {product, hasProduct}){
if (hasProduct){
// mutate the state one way
} else {
// mutate the state another way
}
}
}
สำหรับฉันดูเหมือนว่าข้างต้นไม่เพียง แต่จะซับซ้อน แต่ยังค่อนข้าง "รั่ว" เนื่องจากบางส่วนของรหัสที่อยู่ในการดำเนินการอย่างชัดเจน oozing จากตรรกะภายในของการกลายพันธุ์
ในความคิดของฉันนี่เป็นข้อบ่งชี้ของการประนีประนอม ฉันเชื่อว่าการอนุญาตให้การกลายพันธุ์เพื่อรับ Getters นำเสนอความท้าทายบางอย่างโดยอัตโนมัติ มันอาจเป็นได้ทั้งการออกแบบของ Vuex เองหรือเครื่องมือ (vue-devtools et al) หรือเพื่อรักษาความเข้ากันได้แบบย้อนหลังบางอย่างหรือการรวมกันของความเป็นไปได้ที่ระบุไว้ทั้งหมด
สิ่งที่ฉันไม่เชื่อก็คือการส่ง Getters ไปยังการกลายพันธุ์ของคุณเองนั้นเป็นสัญญาณที่บ่งบอกว่าคุณกำลังทำอะไรผิดไป ฉันเห็นว่าเป็นเพียง "การปะแก้" หนึ่งในข้อบกพร่องของกรอบงาน