ต้นทาง
ไม่กี่วันที่ผ่านมาฉันกำลังดูการดำเนินการตามกรอบ MVVM ขนาดเล็กยอดนิยม (เช่นเฟรมเวิร์กที่เบากว่าเช่น Avalon.js, vue.js มากกว่าเฟรมเวิร์กที่หนักกว่าเช่น AngularJs และ Emberjs) เฟรมเวิร์ก MVVM สมัยใหม่ที่ได้รับความนิยมโดยทั่วไปจะกำจัดการเชื่อมโยงแบบสองทิศทางข้อมูล (การเชื่อมโยงข้อมูลสองวิธี) ซึ่งเป็นจุดขายของเฟรมเวิร์กเอง (Ember.js ดูเหมือนจะไม่สนับสนุนการเชื่อมโยงข้อมูลแบบสองทิศทาง) และวิธีการใช้งานของการเชื่อมโยงข้อมูลสองทิศทาง ตัวอย่างเช่น anguarjs ใช้การตรวจสอบสกปรกภายในในขณะที่สาระสำคัญของการใช้งานภายในของ avalon.js คือการตั้งค่าคุณสมบัติ
เราไม่ได้ตั้งใจที่จะหารือเกี่ยวกับการใช้งานเฉพาะของการเชื่อมโยงข้อมูลแบบสองทิศทางโดยแต่ละเฟรมเวิร์กที่นี่ เราจะพูดคุยเกี่ยวกับวิธีการทั่วไปหลายประการในการใช้การเชื่อมโยงข้อมูลแบบสองทิศทางที่ส่วนหน้าและมุ่งเน้นไปที่การเลือกทางเทคนิคของ Avalon.js เพื่อใช้การเชื่อมโยงข้อมูลแบบสองทิศทาง
การใช้งานแบบดั้งเดิมของการเชื่อมโยงข้อมูลแบบสองทาง
ก่อนอื่นมาพูดถึงสิ่งที่มีผลผูกพันข้อมูลสองทิศทางส่วนหน้า พูดง่ายๆคือเลเยอร์คอนโทรลเลอร์ของเฟรมเวิร์ก (เลเยอร์คอนโทรลเลอร์นี่คือคำทั่วไปซึ่งสามารถเข้าใจได้ว่าเป็นมิดเดิลแวร์ที่ควบคุมพฤติกรรมการดูและเชื่อมต่อเลเยอร์โมเดล) และเลเยอร์แสดง UI (เลเยอร์ดู) เพื่อสร้างช่องข้อมูลแบบสองทิศทาง เมื่อทั้งสองเลเยอร์นี้เปลี่ยนแปลงเลเยอร์อื่นจะทำการเปลี่ยนแปลงที่สอดคล้องกันโดยอัตโนมัติทันที (หรือดูเหมือนจะทันที)
โดยทั่วไปการพูดเพื่อตระหนักถึงความสัมพันธ์ที่มีผลผูกพันข้อมูลสองทางนี้ (กระบวนการสหสัมพันธ์ระหว่างเลเยอร์คอนโทรลเลอร์และเลเยอร์การแสดงผล) ปัจจุบันมี สามวิธีในส่วนหน้า
1. ตรวจสอบสกปรก
2. กลไกการสังเกต
3. Encapsulate Accessor
เช็คสกปรก
เราบอกว่า AngularJS (ที่นี่หมายถึงเฉพาะ AngularJS 1.xx ซึ่งไม่ได้เป็นตัวแทนของ AngularJS 2.xx เวอร์ชัน) เป็นการใช้งานทางเทคนิคของการเชื่อมโยงข้อมูลสองทาง หลักการทั่วไปคือ AngularJS จะรักษาลำดับและวางคุณลักษณะทั้งหมดที่ต้องตรวจสอบในลำดับนี้ เมื่อเหตุการณ์เฉพาะบางอย่างเกิดขึ้น (โปรดทราบว่าสิ่งนี้ไม่ได้กำหนดเวลา แต่ถูกเรียกใช้โดยเหตุการณ์พิเศษบางอย่าง) AngularJS จะเรียกวิธี $ Digest ตรรกะภายในวิธีนี้คือการสำรวจผู้เฝ้าดูทั้งหมดเปรียบเทียบแอตทริบิวต์ที่ตรวจสอบและเปรียบเทียบว่าค่าแอตทริบิวต์มีการเปลี่ยนแปลงก่อนและหลังการเรียกใช้วิธีหรือไม่ หากมีการเปลี่ยนแปลงตัวจัดการที่เกี่ยวข้องจะถูกเรียก มีบทความมากมายบนอินเทอร์เน็ตที่วิเคราะห์หลักการการใช้งานของการเชื่อมโยงข้อมูลสองทางของ AngularJS เช่นบทความนี้และอื่น ๆ
ข้อเสียของวิธีนี้ชัดเจน ผู้เฝ้าดูการสำรวจและฝึกอบรมนั้นใช้เวลานานมากโดยเฉพาะอย่างยิ่งเมื่อจำนวนการตรวจสอบหน้าเดียวถึงลำดับความสำคัญ
กลไกการสังเกต
บล็อกเกอร์มีบทความที่พิมพ์ซ้ำและแปลการเปลี่ยนแปลงข้อมูลที่มีผลผูกพันที่เกิดขึ้นจาก Object.observe () ซึ่งหมายถึงการใช้วิธีการ Object.observe ใน ecmascript7 เพื่อตรวจสอบและสังเกตวัตถุ (หรือคุณสมบัติ) เมื่อมีการเปลี่ยนแปลงตัวจัดการที่เกี่ยวข้องจะถูกดำเนินการ
นี่เป็นวิธีที่สมบูรณ์แบบที่สุดในการตรวจสอบการเปลี่ยนแปลงข้อมูลแอตทริบิวต์ในปัจจุบัน การสนับสนุนดั้งเดิมของภาษา (เบราว์เซอร์) ไม่มีอะไรดีไปกว่านี้ สิ่งเดียวที่น่าเสียใจคือความกว้างของการสนับสนุนในปัจจุบันไม่ดีพอและจำเป็นต้องได้รับการส่งเสริมอย่างเต็มที่
ผู้เข้ายึดทรัพย์สินของ Encapsulation
มีแนวคิดของวิธีการเวทย์มนตร์ใน PHP เช่น __get () และ __set () วิธีการใน PHP มีแนวคิดที่คล้ายกันใน JavaScript แต่มันไม่ได้เรียกว่าวิธีเวทย์มนตร์ แต่เป็นอุปกรณ์เสริม ลองดูที่รหัสตัวอย่าง
var data = {ชื่อ: "erik", getName: function () {return this.name;}, setName: function (ชื่อ) {this.name = name;}};จากรหัสข้างต้นเราสามารถมองเห็นการก้าวกระโดดเช่นวิธี getName () และ setName () ในข้อมูล เราสามารถมองว่ามันเป็น accessor (หรือ accessor) ของ data.name
ในความเป็นจริงสำหรับรหัสข้างต้นหากเข้มงวดกว่านั้นจะไม่ได้รับอนุญาตให้เข้าถึงคุณสมบัติ Data.Name โดยตรง การอ่านและการเขียนทั้งหมดไปยัง data.name จะต้องผ่านวิธี data.getName () และ data.setName () ดังนั้นลองจินตนาการว่าเมื่อทรัพย์สินไม่อนุญาตให้อ่านและเขียนโดยตรง แต่ต้องอ่านและเขียนผ่าน accessor แน่นอนว่าฉันจะทำสิ่งพิเศษบางอย่างโดยการเขียนวิธี accessor ใหม่ของคุณสมบัติเช่นการตรวจสอบการเปลี่ยนแปลงมูลค่าทรัพย์สิน นี่คือหลักการของการใช้สิทธิ์คุณสมบัติเพื่อทำการผูกมัดข้อมูลสองทาง
แน่นอนว่าวิธีนี้ยังมีข้อเสีย สิ่งที่โดดเด่นที่สุดคือทุกครั้งที่มีการเพิ่มการตรวจสอบแอตทริบิวต์จะต้องเพิ่มวิธี accessor ที่สอดคล้องกันในแอตทริบิวต์นี้มิฉะนั้นการเปลี่ยนแปลงของแอตทริบิวต์นี้จะไม่ถูกจับ
Object.defineProperty Method
หลักการของกรอบ MVVM ในประเทศ avalon.js การใช้งานการเชื่อมโยงแบบสองทิศทางเป็นผู้เข้ายึดทรัพย์สิน แต่แน่นอนว่ามันจะไม่เป็นต้นฉบับเหมือนรหัสตัวอย่างข้างต้น มันใช้วิธีการคุณสมบัติมาตรฐาน defineProperty ที่กำหนดไว้ใน ECMASCRIPT 5.1 (ECMA-262) ในการตอบสนองต่อสภาวะตลาดในประเทศบางคนไม่สนับสนุน Object.defineProperty เบราว์เซอร์ระดับต่ำใช้ VBScript เพื่อความเข้ากันได้ที่สมบูรณ์แบบซึ่งแตกต่างจากเฟรมเวิร์ก MVVM อื่น ๆ ที่ค่อยๆละทิ้งการสนับสนุนสำหรับเบราว์เซอร์ต่ำสุด
ก่อนอื่นกำหนดวิธีการ defineproperty method บน MDN
เมธอด Object.defineProperty () กำหนดคุณสมบัติใหม่โดยตรงบนวัตถุหรือแก้ไขคุณสมบัติที่มีอยู่บนวัตถุและส่งคืนวัตถุ
ความหมายมีความชัดเจนและวิธีการ defineProperty เป็นวิธีโดยตรงในการกำหนดคุณสมบัติของวัตถุหรือแก้ไขคุณสมบัติของวัตถุที่มีอยู่ ต้นแบบของวิธีนี้มีดังนี้:
Object.defineProperty (obj, prop, descriptor)
ใน,
obj, คัดค้านการแก้ไข
เสาพร้อมชื่อแอตทริบิวต์ที่แก้ไขแล้ว
Descriptor คำอธิบายที่เกี่ยวข้องของแอตทริบิวต์ที่จะแก้ไข
Descriptor ต้องการวัตถุที่จะส่งผ่านค่าเริ่มต้นของมันมีดังนี้
/*** @{param} descriptor*/{กำหนดค่า: false, enumerable: false, writable: false, value: null, set: undefined, get: undefined}สามารถกำหนดค่าได้ไม่ว่าจะเป็นคุณสมบัติหรือไม่ ความหมายที่กำหนดค่าได้รวมถึง: แอตทริบิวต์สามารถลบได้ไม่ว่าจะเป็นคุณสมบัติที่สามารถเขียนได้, imumerable และกำหนดค่าของแอตทริบิวต์สามารถแก้ไขได้หรือไม่
ไม่มีการตรวจสอบไม่ว่าแอตทริบิวต์นั้นจะสามารถระบุได้หรือไม่ ความหมายของ Enumerable รวมถึง: ไม่ว่าจะสามารถข้ามผ่านไปได้สำหรับ ... ในและไม่ว่าจะสามารถรับได้ผ่านวิธี Object.keys () หรือไม่
เขียนได้ไม่ว่าแอตทริบิวต์จะสามารถเขียนใหม่ได้หรือไม่ ความหมายของการเขียนซ้ำรวมถึง: ไม่ว่าจะมีการกำหนดแอตทริบิวต์ใหม่หรือไม่
ค่าค่าเริ่มต้นของคุณสมบัติ
Set, ผู้เขียนอสังหาริมทรัพย์ (สำหรับตอนนี้นั่นก็คือ) เมื่อแอตทริบิวต์ถูกกำหนดใหม่วิธีนี้จะถูกเรียกโดยอัตโนมัติ
รับผู้อ่านทรัพย์สิน (สำหรับตอนนี้มัน) เมื่อคุณสมบัติเข้าถึงและอ่านวิธีนี้จะถูกเรียกโดยอัตโนมัติ
นี่คือรหัสตัวอย่าง
var o = {}; object.defineProperty (o, 'ชื่อ', {value: 'erik'}); console.log (object.getownpropertydescriptor (o, 'ชื่อ')); // object {value: "Erik", เขียนได้: FALSE, Enumerable: FALSE, กำหนดค่าได้: FALSE} Object.defineProperty (o, 'อายุ', {ค่า: 26, กำหนดค่า: จริง, เขียนได้: จริง}); console.log (o.age); // 26o.age = 18; console.log (o.age); // 18. เนื่องจากคุณสมบัติอายุเป็นคอนโซลที่เขียนได้ใหม่ log (object.keys (o)); - ไม่มีชื่อหรืออายุการใช้งานเป็นวัตถุที่สามารถระบุได้ defineproperty (o, 'เพศ', {ค่า: 'ชาย', เขียนได้: เท็จ}); o.sex = 'หญิง'; // การมอบหมายที่นี่เป็น console.log (o.sex) ที่ไม่มีประสิทธิภาพ // 'ชาย'; ลบ O.Sex; // false การกระทำการลบคุณสมบัติยังไม่ถูกต้องหลังจากตัวอย่างข้างต้นภายใต้สถานการณ์ปกติการใช้ Object.definePropert () ค่อนข้างง่าย
อย่างไรก็ตามยังมีสิ่งหนึ่งที่ต้องการความสนใจเพิ่มเติม เมื่อเมธอด Object.defineProperty () ตั้งค่าคุณสมบัติคุณสมบัติไม่สามารถประกาศแอตทริบิวต์ accessor (ตั้งค่าและรับ) และแอตทริบิวต์ที่เขียนได้หรือค่าในเวลาเดียวกัน หมายความว่าหากมีการตั้งค่าคุณสมบัติบางอย่างด้วยแอตทริบิวต์ที่เขียนได้หรือมูลค่าคุณสมบัตินี้จะไม่สามารถประกาศรับและตั้งค่าและในทางกลับกัน
เนื่องจากเมื่อ Object.defineProperty () ประกาศคุณสมบัติการควบคุมการเข้าถึงมากกว่าสองประเภทไม่อนุญาตให้ใช้คุณสมบัติเดียวกัน
รหัสตัวอย่าง
var o = {}, myName = 'erik'; object.defineProperty (o, 'ชื่อ', {value: myName, set: function (ชื่อ) {myName = name;}, get: function () {return myname;}});รหัสข้างต้นดูเหมือนจะไม่เป็นไร แต่จะรายงานข้อผิดพลาดเมื่อมีการดำเนินการจริงและข้อผิดพลาดจะถูกรายงานดังนี้
TypeError: คุณสมบัติไม่ถูกต้อง คุณสมบัติทั้งสองไม่สามารถมีผู้เข้าร่วมและเขียนได้หรือมีค่า #<jobch>
เนื่องจากแอตทริบิวต์ชื่อที่นี่ประกาศแอตทริบิวต์ค่าชุดและรับแอตทริบิวต์ในเวลาเดียวกันซึ่งทั้งสองอย่างนี้ให้การควบคุมการอ่านและเขียนสองรายการบนแอตทริบิวต์ชื่อ หากคุณลักษณะมูลค่าไม่ได้ประกาศที่นี่ แต่มีการประกาศคุณสมบัติที่เขียนได้ผลลัพธ์จะเหมือนกันและจะมีการรายงานข้อผิดพลาด