Vue


vue2 和 vue3 双向绑定原理

<h3>问题:</h3> <p>我们都听过“数据绑定”这个词,其关键在于监听数据的变化,对于obj这个对象,怎么知道obj.name发生了改变呢?</p> <pre><code class="language-javascript">var obj = { name: &amp;quot;Andy&amp;quot; }</code></pre> <hr /> <h3>defineProperty:</h3> <p>ES5 提供了 Object.defineProperty ,该方法可以在一个对象上定义新属性,或者修改现有属性</p> <p><strong>语法:</strong> Object.defineProperty(obj, prop, descriptor)</p> <p><strong>参数:</strong> obj: 要在其上定义属性的对象。 prop: 要定义或修改的属性的名称。 descriptor: 将被定义或修改的属性的描述符。</p> <p><img src="https://www.showdoc.com.cn/server/api/attachment/visitFile?sign=939059eb475fd30d0b3da68f11588f18" alt="" /></p> <hr /> <h3>getter 和 setter:</h3> <pre><code class="language-javascript">        var obj = {}         var value = null         Object.defineProperty(obj, &amp;quot;name&amp;quot;, {             get: function () {                 console.log('执行了get')                 return value;             },             set: function (newValue) {                 console.log('执行了set')                 value = newValue;             }         })         obj.name = &amp;quot;Andy&amp;quot; // 执行了set         console.log(obj.name); // 执行了get  // Andy</code></pre> <p>封装一下:</p> <pre><code class="language-javascript">        function Encapsulation() {             var value = null             var namelist = []             Object.defineProperty(this, 'name', {                 get: function () {                     console.log('执行了get')                     return value;                 },                 set: function (value) {                     console.log('执行了set')                     value = value;                     namelist.push({                         name: value                     })                 }             })             this.getList = function () {                 return namelist;             }         }         var enc = new Encapsulation();         enc.name; // 执行了get         enc.name = &amp;quot;Andy&amp;quot;; // 执行了set         enc.name = &amp;quot;Curry&amp;quot;; // 执行了set         console.log(enc.getList()); // [{ name: Andy }, { name: Curry }]</code></pre> <p>重新封装一下:</p> <pre><code class="language-javascript">    &amp;lt;span id=&amp;quot;container&amp;quot;&amp;gt;1&amp;lt;/span&amp;gt;     &amp;lt;button id=&amp;quot;button&amp;quot;&amp;gt;点击加 1&amp;lt;/button&amp;gt;     &amp;lt;script&amp;gt;         function watch(obj, name, func) {             var value = obj[name];             Object.defineProperty(obj, name, {                 get: function () {                     return value;                 },                 set: function (newValue) {                     value = newValue;                     func(value)                 }             });         }         var obj = {             value: 1         }         watch(obj, &amp;quot;value&amp;quot;, function (newvalue) {             document.getElementById('container').innerHTML = newvalue;         })         document.getElementById('button').addEventListener(&amp;quot;click&amp;quot;, function () {             obj.value++         });     &amp;lt;/script&amp;gt;</code></pre> <hr /> <h3>Proxy:</h3> <p>defineProperty 只能定义 get 和 set 行为,ES6 提供了 Proxy 构造函数,可以定义更多的行为</p> <p><strong>语法:</strong> var proxy = new Proxy(target, handler)</p> <p>target 参数表示所要拦截的目标对象,handler参数用来定制拦截行为</p> <pre><code class="language-javascript">        var proxy = new Proxy({}, {             get: function (obj, prop) {                 console.log('执行get')                 return obj[prop];             },             set: function (obj, prop, value) {                 console.log('执行set')                 obj[prop] = value;             }         });         proxy.name = &amp;quot;Andy&amp;quot;; // 执行set         console.log(proxy.name); // 执行get     // Andy</code></pre> <p>除了 get 和 set ,proxy 可以拦截多达 13 种操作,比如 has(target, propKey),可以拦截 propKey in proxy 的操作,返回一个布尔值。</p> <pre><code class="language-javascript">        // 使用 has 方法隐藏某些属性,不被 in 运算符发现         var handler = {             has(target, key) {                 if (key[0] === '_') {                     return false;                 }                 return key in target;             }         };         var target = {             _prop: 'foo',             prop: 'foo'         };         var proxy = new Proxy(target, handler);         console.log('_prop' in proxy); // false</code></pre> <p>apply 方法拦截函数的调用:</p> <pre><code class="language-javascript">        var target = function () {             return '1+1=2';         };         var handler = {             apply: function () {                 return '1+1=3';             }         };         var p = new Proxy(target, handler);         console.log(p()) // 1+1=3</code></pre> <p>ownKeys:可以拦截对象自身属性的读取操作</p> <pre><code class="language-javascript">        let target = {             _name: 'Andy',             _age: '21',             address: 'XMU'         };         let handler = {             ownKeys(target) {                 return Reflect.ownKeys(target).filter(key =&amp;gt; key[0] !== '_');             }         };         let proxy = new Proxy(target, handler);         console.log(Object.keys(proxy)) // [&amp;quot;address&amp;quot;]         console.log(Object.values(proxy)) // [&amp;quot;XMU&amp;quot;]         console.log(Object.entries(proxy)) // [&amp;quot;address&amp;quot;, &amp;quot;XMU&amp;quot;]</code></pre> <p>最后使用 proxy 写一下 watch :</p> <pre><code class="language-javascript">        function watch(target, func) {             var proxy = new Proxy(target, {                 get: function (target, prop) {                     return target[prop];                 },                 set: function (target, prop, value) {                     target[prop] = value;                     func(prop, value);                 }             });             return proxy;         }         var obj = {             value: 1         }         var newObj = watch(obj, function (key, newvalue) {             if (key == 'value') document.getElementById('container').innerHTML = newvalue;         })         document.getElementById('button').addEventListener(&amp;quot;click&amp;quot;, function () {             newObj.value += 1         });</code></pre>

页面列表

ITEM_HTML