PageSourceSearch

https://pitt-wu-blog.vercel.app/assets/js/b2b34254.7799c3b3.js

js pitt-wu-blog.vercel.app collected 2026-10-03 07:31:18 UTC 17,021 bytes, 1 lines download raw bytes

1"use strict";(globalThis.webpackChunkdocusaurus_blog=globalThis.webpackChunkdocusaurus_blog||[]).push([[5187],{8453:(e,n,i)=>{i.d(n,{R:()=>d,x:()=>s});var r=i(6540);const t={},a=r.createContext(t);function d(e){const n=r.useContext(a);return r.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function s(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:d(e.components),r.createElement(a.Provider,{value:n},e.children)}},9431:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>o,contentTitle:()=>s,default:()=>p,frontMatter:()=>d,metadata:()=>r,toc:()=>l});const r=JSON.parse('{"id":"Knowledge/Frontend-Frameworks/Vue/vue-two-way-data-binding","title":"[Hard] \ud83d\udcc4 Two-way Data Binding","description":"1. Please explain the underlying principle of how Vue2 and Vue3 each implement two-way binding","source":"@site/docs/Knowledge/Frontend-Frameworks/Vue/two-way-data-binding.md","sourceDirName":"Knowledge/Frontend-Frameworks/Vue","slug":"/vue-two-way-data-binding","permalink":"/docs/vue-two-way-data-binding","draft":false,"unlisted":false,"editUrl":"https://github.com/wuzhe0912/blog-docusaurus/tree/master/docs/Knowledge/Frontend-Frameworks/Vue/two-way-data-binding.md","tags":[{"inline":true,"label":"Vue","permalink":"/docs/tags/vue"},{"inline":true,"label":"Quiz","permalink":"/docs/tags/quiz"},{"inline":true,"label":"Hard","permalink":"/docs/tags/hard"}],"version":"current","frontMatter":{"id":"vue-two-way-data-binding","title":"[Hard] \ud83d\udcc4 Two-way Data Binding","slug":"/vue-two-way-data-binding","tags":["Vue","Quiz","Hard"]},"sidebar":"knowledge","previous":{"title":"[Medium] Vue3 Static Hoisting","permalink":"/docs/static-hoisting"},"next":{"title":"[Easy] Element Properties","permalink":"/docs/element-properties"}}');var t=i(4848),a=i(8453);const d={id:"vue-two-way-data-binding",title:"[Hard] \ud83d\udcc4 Two-way Data Binding",slug:"/vue-two-way-data-binding",tags:["Vue","Quiz","Hard"]},s=void 0,o={},l=[{value:"1. Please explain the underlying principle of how Vue2 and Vue3 each implement two-way binding",id:"1-please-explain-the-underlying-principle-of-how-vue2-and-vue3-each-implement-two-way-binding",level:2},{value:"Vue2 implementation",id:"vue2-implementation",level:3},{value:"1. Data hijacking",id:"1-data-hijacking",level:4},{value:"2. Dependency collection",id:"2-dependency-collection",level:4},{value:"3. Dispatching updates",id:"3-dispatching-updates",level:4},{value:"Vue2 example",id:"vue2-example",level:4},{value:"Vue2 limitations",id:"vue2-limitations",level:4},{value:"Vue3 implementation",id:"vue3-implementation",level:3},{value:"1. Proxy-based data hijacking",id:"1-proxy-based-data-hijacking",level:4},{value:"2. More efficient dependency tracking",id:"2-more-efficient-dependency-tracking",level:4},{value:"3. More precise update triggering",id:"3-more-precise-update-triggering",level:4},{value:"Vue3 example",id:"vue3-example",level:4},{value:"Vue2 vs Vue3 comparison",id:"vue2-vs-vue3-comparison",level:3},{value:"Conclusion",id:"conclusion",level:3},{value:"2. Why does Vue3 use <code>Proxy</code> instead of <code>Object.defineProperty</code>?",id:"2-why-does-vue3-use-proxy-instead-of-objectdefineproperty",level:2},{value:"Main reasons",id:"main-reasons",level:3},{value:"1. Stronger interception capability",id:"1-stronger-interception-capability",level:4},{value:"2. Native array index tracking",id:"2-native-array-index-tracking",level:4},{value:"3. Native support for dynamic property add/delete",id:"3-native-support-for-dynamic-property-adddelete",level:4},{value:"4. Better performance model",id:"4-better-performance-model",level:4},{value:"5. Simpler internal implementation",id:"5-simpler-internal-implementation",level:4},{value:"Why Vue2 did not use Proxy",id:"why-vue2-did-not-use-proxy",level:3},{value:"Practical comparison",id:"practical-comparison",level:3},{value:"Summary",id:"summary",level:3},{value:"Reference",id:"reference",level:2}];function c(e){const n={a:"a",blockquote:"blockquote",code:"code",h2:"h2",h3:"h3",h4:"h4",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,a.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.h2,{id:"1-please-explain-the-underlying-principle-of-how-vue2-and-vue3-each-implement-two-way-binding",children:"1. Please explain the underlying principle of how Vue2 and Vue3 each implement two-way binding"}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsx)(n.p,{children:"Please explain the underlying mechanism of two-way binding in Vue2 and Vue3."}),"\n"]}),"\n",(0,t.jsx)(n.p,{children:"To understand Vue two-way binding, you need to understand how the reactivity system works and how Vue2 and Vue3 implement it differently."}),"\n",(0,t.jsx)(n.h3,{id:"vue2-implementation",children:"Vue2 implementation"}),"\n",(0,t.jsxs)(n.p,{children:["Vue2 uses ",(0,t.jsx)(n.code,{children:"Object.defineProperty"})," to implement reactivity. It wraps object properties with ",(0,t.jsx)(n.code,{children:"getter"})," and ",(0,t.jsx)(n.code,{children:"setter"}),", so reads/writes can be tracked."]}),"\n",(0,t.jsx)(n.h4,{id:"1-data-hijacking",children:"1. Data hijacking"}),"\n",(0,t.jsxs)(n.p,{children:["When component data is initialized in Vue2, Vue traverses each property and converts it into ",(0,t.jsx)(n.code,{children:"getter"}),"/",(0,t.jsx)(n.code,{children:"setter"})," via ",(0,t.jsx)(n.code,{children:"Object.defineProperty"}),", so reads and writes can be observed."]}),"\n",(0,t.jsx)(n.h4,{id:"2-dependency-collection",children:"2. Dependency collection"}),"\n",(0,t.jsxs)(n.p,{children:["When a render function executes and reads reactive properties, the ",(0,t.jsx)(n.code,{children:"getter"})," runs. Vue records dependencies so affected components can be notified later."]}),"\n",(0,t.jsx)(n.h4,{id:"3-dispatching-updates",children:"3. Dispatching updates"}),"\n",(0,t.jsxs)(n.p,{children:["When data changes, the ",(0,t.jsx)(n.code,{children:"setter"}
1)," runs. Vue notifies dependent watchers/components and triggers re-render to update DOM."]}),"\n",(0,t.jsx)(n.h4,{id:"vue2-example",children:"Vue2 example"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-js",children:"function defineReactive(obj, key, val) {\n  Object.defineProperty(obj, key, {\n    enumerable: true,\n    configurable: true,\n    get: function reactiveGetter() {\n      console.log(`get ${key}: ${val}`);\n      return val;\n    },\n    set: function reactiveSetter(newVal) {\n      console.log(`set ${key}: ${newVal}`);\n      val = newVal;\n    },\n  });\n}\n\nconst data = { name: 'Pitt' };\ndefineReactive(data, 'name', data.name);\n\nconsole.log(data.name); // triggers getter\ndata.name = 'Vue2 Reactivity'; // triggers setter\n"})}),"\n",(0,t.jsx)(n.h4,{id:"vue2-limitations",children:"Vue2 limitations"}),"\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.code,{children:"Object.defineProperty"})," has several limitations:"]}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"Cannot detect property addition/deletion"})," without ",(0,t.jsx)(n.code,{children:"Vue.set()"})," / ",(0,t.jsx)(n.code,{children:"Vue.delete()"})]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"Cannot detect direct array index mutation"})," reliably without patched array methods"]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"Performance overhead"})," from deep traversal and upfront getter/setter definition"]}),"\n"]}),"\n",(0,t.jsx)(n.h3,{id:"vue3-implementation",children:"Vue3 implementation"}),"\n",(0,t.jsxs)(n.p,{children:["Vue3 uses ES6 ",(0,t.jsx)(n.code,{children:"Proxy"}),", which provides broader interception capability and better performance characteristics."]}),"\n",(0,t.jsx)(n.h4,{id:"1-proxy-based-data-hijacking",children:"1. Proxy-based data hijacking"}),"\n",(0,t.jsxs)(n.p,{children:["Vue3 wraps whole objects with ",(0,t.jsx)(n.code,{children:"new Proxy"})," instead of defining getter/setter per key. This allows interception of more operations, including property add/delete."]}),"\n",(0,t.jsx)(n.h4,{id:"2-more-efficient-dependency-tracking",children:"2. More efficient dependency tracking"}),"\n",(0,t.jsxs)(n.p,{children:["Vue3 no longer needs upfront getter/setter definition for every property. Proxy traps can handle operations dynamically (e.g., ",(0,t.jsx)(n.code,{children:"get"}),", ",(0,t.jsx)(n.code,{children:"set"}),", ",(0,t.jsx)(n.code,{children:"has"}),", ",(0,t.jsx)(n.code,{children:"deleteProperty"}),", etc.)."]}),"\n",(0,t.jsx)(n.h4,{id:"3-more-precise-update-triggering",children:"3. More precise update triggering"}),"\n",(0,t.jsx)(n.p,{children:"Vue3 can track dependencies more precisely and reduce unnecessary re-renders."}),"\n",(0,t.jsx)(n.h4,{id:"vue3-example",children:"Vue3 example"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-js",children:"function reactive(target) {\n  const handler = {\n    get(target, key, receiver) {\n      const result = Reflect.get(target, key, receiver);\n      console.log(`get ${key}: ${result}`);\n      return result;\n    },\n    set(target, key, value, receiver) {\n      const success = Reflect.set(target, key, value, receiver);\n      console.log(`set ${key}: ${value}`);\n      return success;\n    },\n  };\n\n  return new Proxy(target, handler);\n}\n\nconst data = reactive({ name: 'Vue 3' });\n\nconsole.log(data.name);\ndata.name = 'Vue 3 Reactivity';\nconsole.log(data.name);\n"})}),"\n",(0,t.jsx)(n.h3,{id:"vue2-vs-vue3-comparison",children:"Vue2 vs Vue3 comparison"}),"\n",(0,t.jsxs)(n.table,{children:[(0,t.jsx)(n.thead,{children:(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.th,{children:"Feature"}),(0,t.jsx)(n.th,{children:"Vue2"}),(0,t.jsx)(n.th,{children:"Vue3"})]})}),(0,t.jsxs)(n.tbody,{children:[(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:"Core mechanism"}),(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"Object.defineProperty"})}),(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"Proxy"})})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:"Detect new properties"}),(0,t.jsxs)(n.td,{children:["\u274c requires ",(0,t.jsx)(n.code,{children:"Vue.set()"})]}),(0,t.jsx)(n.td,{children:"\u2705 native support"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:"Detect property deletion"}),(0,t.jsxs)(n.td,{children:["\u274c requires ",(0,t.jsx)(n.code,{children:"Vue.delete()"})]}),(0,t.jsx)(n.td,{children:"\u2705 native support"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:"Detect array index assignment"}),(0,t.jsx)(n.td,{children:"\u274c limited"}),(0,t.jsx)(n.td,{children:"\u2705 native support"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:"Performance"}),(0,t.jsx)(n.td,{children:"upfront deep walk"}),(0,t.jsx)(n.td,{children:"lazy + more efficient"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:"Browser support"}),(0,t.jsx)(n.td,{children:"IE9+"}),(0,t.jsx)(n.td,{children:"no IE11 support"})]})]})]}),"\n",(0,t.jsx)(n.h3,{id:"conclusion",children:"Conclusion"}),"\n",(0,t.jsxs)(n.p,{children:["Vue2 uses ",(0,t.jsx)(n.code,{children:"Object.defineProperty"}),", which works but has known limitations.\nVue3 uses ",(0,t.jsx)(n.code,{children:"Proxy"}),", giving a more complete and flexible reactivity system with better performance."]}),"\n",(0,t.jsxs)(n.h2,{id:"2-why-does-vue3-use-proxy-instead-of-objectdefineproperty",children:["2. Why does Vue3 use ",(0,t.jsx)(n.code,{children:"Proxy"})," instead of ",(0,t.jsx)(n.code,{children:"Object.defineProperty"}),"?"]}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsxs)(n.p,{children:["Why did Vue3 cho
1ose ",(0,t.jsx)(n.code,{children:"Proxy"})," over ",(0,t.jsx)(n.code,{children:"Object.defineProperty"}),"?"]}),"\n"]}),"\n",(0,t.jsx)(n.h3,{id:"main-reasons",children:"Main reasons"}),"\n",(0,t.jsx)(n.h4,{id:"1-stronger-interception-capability",children:"1. Stronger interception capability"}),"\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.code,{children:"Proxy"})," can intercept many operations, while ",(0,t.jsx)(n.code,{children:"Object.defineProperty"})," only covers property get/set."]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-js",children:"// Operations Proxy can intercept\nconst handler = {\n  get() {},\n  set() {},\n  has() {},\n  deleteProperty() {},\n  ownKeys() {},\n  getOwnPropertyDescriptor() {},\n  defineProperty() {},\n  preventExtensions() {},\n  getPrototypeOf() {},\n  isExtensible() {},\n  setPrototypeOf() {},\n  apply() {},\n  construct() {},\n};\n"})}),"\n",(0,t.jsx)(n.h4,{id:"2-native-array-index-tracking",children:"2. Native array index tracking"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-js",children:"// Vue2 limitation\nconst arr = [1, 2, 3];\narr[0] = 10; // often not tracked in Vue2-style defineProperty model\n\n// Vue3\nconst arr2 = reactive([1, 2, 3]);\narr2[0] = 10; // tracked\n"})}),"\n",(0,t.jsx)(n.h4,{id:"3-native-support-for-dynamic-property-adddelete",children:"3. Native support for dynamic property add/delete"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-js",children:"// Vue2 needs special API\nVue.set(obj, 'newKey', 'value');\n\n// Vue3 native\nconst obj = reactive({});\nobj.newKey = 'value';\ndelete obj.newKey;\n"})}),"\n",(0,t.jsx)(n.h4,{id:"4-better-performance-model",children:"4. Better performance model"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-js",children:"// Vue2: deep traversal + defineReactive for each key\nfunction observe(obj) {\n  Object.keys(obj).forEach((key) => {\n    defineReactive(obj, key, obj[key]);\n    if (typeof obj[key] === 'object') {\n      observe(obj[key]);\n    }\n  });\n}\n\n// Vue3: proxy wrapper\nfunction reactive(obj) {\n  return new Proxy(obj, handler);\n}\n"})}),"\n",(0,t.jsx)(n.h4,{id:"5-simpler-internal-implementation",children:"5. Simpler internal implementation"}),"\n",(0,t.jsx)(n.p,{children:"Vue3 reactivity core is cleaner and easier to maintain."}),"\n",(0,t.jsx)(n.h3,{id:"why-vue2-did-not-use-proxy",children:"Why Vue2 did not use Proxy"}),"\n",(0,t.jsxs)(n.p,{children:["Mainly ",(0,t.jsx)(n.strong,{children:"browser compatibility"}),":"]}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsx)(n.li,{children:"Vue2 (released in 2016) needed broad support including IE"}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"Proxy"})," cannot be fully polyfilled"]}),"\n",(0,t.jsx)(n.li,{children:"Vue3 dropped IE11 support, so Proxy became viable"}),"\n"]}),"\n",(0,t.jsx)(n.h3,{id:"practical-comparison",children:"Practical comparison"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-js",children:"// ===== Vue2 limitations =====\nconst vm = new Vue({\n  data: {\n    obj: { a: 1 },\n    arr: [1, 2, 3],\n  },\n});\n\n// not reliably reactive in Vue2 without special APIs\nvm.obj.b = 2;\ndelete vm.obj.a;\nvm.arr[0] = 10;\nvm.arr.length = 0;\n\n// Vue2 workaround\nVue.set(vm.obj, 'b', 2);\nVue.delete(vm.obj, 'a');\nvm.arr.splice(0, 1, 10);\n\n// ===== Vue3 native support =====\nconst state = reactive({\n  obj: { a: 1 },\n  arr: [1, 2, 3],\n});\n\nstate.obj.b = 2;\ndelete state.obj.a;\nstate.arr[0] = 10;\nstate.arr.length = 0;\n"})}),"\n",(0,t.jsx)(n.h3,{id:"summary",children:"Summary"}),"\n",(0,t.jsxs)(n.p,{children:["Vue3 uses ",(0,t.jsx)(n.code,{children:"Proxy"})," to:"]}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsx)(n.li,{children:"\u2705 Provide complete reactivity coverage (add/delete/index changes)"}),"\n",(0,t.jsx)(n.li,{children:"\u2705 Improve performance (less upfront traversal)"}),"\n",(0,t.jsx)(n.li,{children:"\u2705 Simplify implementation"}),"\n",(0,t.jsx)(n.li,{children:"\u2705 Improve developer experience (fewer special APIs)"}),"\n"]}),"\n",(0,t.jsx)(n.p,{children:"Tradeoff: no support for legacy IE11, which is acceptable for modern web targets."}),"\n",(0,t.jsx)(n.h2,{id:"reference",children:"Reference"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsx)(n.li,{children:(0,t.jsx)(n.a,{href:"https://v2.vuejs.org/v2/guide/reactivity.html",children:"Vue 2 Reactivity in Depth"})}),"\n",(0,t.jsx)(n.li,{children:(0,t.jsx)(n.a,{href:"https://vuejs.org/guide/extras/reactivity-in-depth.html",children:"Vue 3 Reactivity in Depth"})}),"\n",(0,t.jsx)(n.li,{children:(0,t.jsx)(n.a,{href:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty",children:"MDN - Object.defineProperty"})}),"\n",(0,t.jsx)(n.li,{children:(0,t.jsx)(n.a,{href:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy",children:"MDN - Proxy"})}),"\n",(0,t.jsx)(n.li,{children:(0,t.jsx)(n.a,{href:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect",children:"MDN - Reflect"})}),"\n"]})]})}function p(e={}){const{wrapper:n}={...(0,a.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(c,{...e})}):c(e)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.