Vue Object.defineProperty 및 ProxyVue 양 방향 데이터 바 인 딩 실현

양 방향 데이터 바 인 딩 은 보기=>데이터,데이터=>보기 의 업데이트 과정 일 뿐 입 니 다.

다음 방안 에서 의 실현 방향:
  • Vue 의 구조 함 수 를 정의 하고 이 함 수 를 초기 화 합 니 다(my Vue.prototype.init)
  • 데이터 계층 의 업데이트 실현:데이터 납치,obverse 함수 재 작성 data 의 set 와 get(my Vue.prototype.obsever)
  • 시각 계층 의 업데이트 실현:구독 자 모드,하나의 Watcher 함 수 를 정의 하여 DOM 에 대한 업데이트 실현(Watcher)
  • 데이터 와 보기 층 을 연결 하고 명령 v-bind,v-model,v-click(my Vue.prototype.compile)
  • Vue 인 스 턴 스 생 성(new myVue)
  • 1.object.defineproperty 방식 으로 양 방향 데이터 바 인 딩 실현
    
    <!DOCTYPE html>
    <html>
     
    <head>
     <title>myVue</title>
     <style>
      #app{
      text-align: center;
     }
    </style>
    </head>
     
    <body>
     <div id="app">
      <form>
       <input type="text" v-model="number" />
       <button type="button" v-click="increment">  </button>
      </form>
      <h3 v-bind="number"></h3>
     </div>
    </body>
    <script>
     
     //     myVue    
     function myVue(option) {
      this._init(option)
     }
     
     myVue.prototype._init = function (options) { //         
      this.$options = options // options             ,  el,data,methods
      this.$el = document.querySelector(options.el) // el  #app, this.$el id app Element  
      this.$data = options.data // this.$data = {number: 0}
      this.$methods = options.methods // this.$methods = {increment: function(){}}
     
     
      // _binding   model view     ,          Watcher   。 model   ,             ,  view      
      this._binding = {}
     
      this._obsever(this.$data)
      this._compile(this.$el)
     }
     
     //     :    
     myVue.prototype._obsever = function (obj) {
      let _this = this
      Object.keys(obj).forEach((key) => { //   obj  
       if (obj.hasOwnProperty(key)) { //    obj        key  
        _this._binding[key] = [] //        ,_binding = {number: []}        new Watcher
       }
       let value = obj[key]
       if (typeof value === 'object') { //       ,     
        _this._obsever(value)
       }
       Object.defineProperty(_this.$data, key, {
        enumerable: true,
        configurable: true,
        get: () => { //    value  
         return value
        },
        set: (newVal) => { //    value  
         if (value !== newVal) {
          value = newVal
          _this._binding[key].forEach((item) => { //  number   ,  _binding[number]      Watcher    
           item.update() //   Watcher     update      DOM
          })
         }
        }
       })
      })
     }
     
     //      :       ,    DOM      
     function Watcher(el, data, key, attr) {
      this.el = el //      DOM  
      this.data = data // this.$data   : {number: 0, count: 0}
      this.key = key //       ,   "number"
      this.attr = attr //       ,   "innerHTML","value"
     
      this.update()
     }
     //    H3.innerHTML = this.data.number;  number   ,     update  ,     DOM       
     Watcher.prototype.update = function () {
      this.el[this.attr] = this.data[this.key]
     }
     
     //  view model    ,    (v-bind,v-model,v-clickde) 
     myVue.prototype._compile = function (el) { // root  id app Element  ,         
      let _this = this
      let nodes = Array.prototype.slice.call(el.children) //             
      nodes.map(node => {
       if (node.children.length && node.children.length > 0) { //          ,     
        _this._compile(node)
       }
       if (node.hasAttribute('v-click')) { //    v-click  ,      onclick  ,  increment  , number++
        let attrVal = node.getAttribute('v-click')
        node.onclick = _this.$methods[attrVal].bind(_this.$data) // bind  data     method          
       }
     
       //    v-model  ,     INPUT  TEXTAREA,      input  
       if (node.hasAttribute('v-model') && (node.tagName === 'INPUT' || node.tagName === 'TEXTAREA')) {
        let attrVal = node.getAttribute('v-model')
     
        _this._binding[attrVal].push(new Watcher(
         node, //     DOM   
         _this.$data,
         attrVal, // v-model     
         'value'
        ))
        node.addEventListener('input', () => {
         _this.$data[attrVal] = node.value //  number     node value    ,         
        })
       }
       if (node.hasAttribute('v-bind')) {
        let attrVal = node.getAttribute('v-bind')
        _this._binding[attrVal].push(new Watcher(
         node,
         _this.$data,
         attrVal, // v-bind     
         'innerHTML'
        ))
       }
      })
     }
     
     
     window.onload = () => { //                  ,           
      new myVue({
       el: '#app',
       data: {
        number: 0,
        count: 0
       },
       methods: {
        increment() {
         this.number++
        },
        incre() {
         this.count++
        }
       }
      })
     }
    </script>
     
    </html>
    2.Proxy 양 방향 데이터 바 인 딩 실현
    
    <!DOCTYPE html>
    <html>
     
    <head>
     <title>myVue</title>
     <style>
      #app{
      text-align: center;
     }
    </style>
    </head>
     
    <body>
     <div id="app">
      <form>
       <input type="text" v-model="number" />
       <button type="button" v-click="increment">  </button>
      </form>
      <h3 v-bind="number"></h3>
     </div>
    </body>
    <script>
     
     //     myVue    
     function myVue(option) {
      this._init(option)
     }
     
     myVue.prototype._init = function (options) { //         
      this.$options = options // options             ,  el,data,methods
      this.$el = document.querySelector(options.el) // el  #app, this.$el id app Element  
      this.$data = options.data // this.$data = {number: 0}
      this.$methods = options.methods // this.$methods = {increment: function(){}}
     
      this._binding = {}
      this._obsever(this.$data)
      this._complie(this.$el)
     
     }
     
    //     :    
    myVue.prototype._obsever = function (data) {
      let _this = this
      let handler = {
       get(target, key) {
        return target[key]; //       key  
       },
       set(target, key, newValue) {
        let res = Reflect.set(target, key, newValue); //            
        _this._binding[key].map(item => {
         item.update();
        });
        return res;
       }
      };
      //             this.$data, this.$data       ,     this.$data     ,            handler      
      this.$data = new Proxy(data, handler);
     }
     
     //  view model    ,    (v-bind,v-model,v-clickde) 
     myVue.prototype._complie = function (el) { // el  id app Element  ,         
      let _this = this
      let nodes = Array.prototype.slice.call(el.children) //             
     
      nodes.map(node => {
       if (node.children.length && node.children.length > 0) this._complie(node)
       if (node.hasAttribute('v-click')) { //    v-click  ,      onclick  ,  increment  , number++
        let attrVal = node.getAttribute('v-click')
        node.onclick = _this.$methods[attrVal].bind(_this.$data) // bind  data     method          
       }
     
       //    v-model  ,     INPUT  TEXTAREA,      input  
       if (node.hasAttribute('v-model') && (node.tagName === 'INPUT' || node.tagName === 'TEXTAREA')) {
        let attrVal = node.getAttribute('v-model')
        
        console.log(_this._binding)
        if (!_this._binding[attrVal]) _this._binding[attrVal] = []
        _this._binding[attrVal].push(new Watcher(
         node, //     DOM   
         _this.$data,
         attrVal, // v-model     
         'value',
        ))
        node.addEventListener('input', () => {
         _this.$data[attrVal] = node.value //  number     node value    ,         
        })
       }
       if (node.hasAttribute('v-bind')) {
        let attrVal = node.getAttribute('v-bind')
        if (!_this._binding[attrVal]) _this._binding[attrVal] = []
        _this._binding[attrVal].push(new Watcher(
         node,
         _this.$data,
         attrVal, // v-bind     
         'innerHTML',
        ))
       }
     
      })
     }
     //       ,    DOM      
     function Watcher(el, data, key, attr) {
      this.el = el //      DOM  
      this.data = data //       this.$data   : {number: 0, count: 0}
      this.key = key //       ,   "num"
      this.attr = attr //       ,   "innerHTML","value"
     
      this.update()
     }
     //    H3.innerHTML = this.data.number;  number   ,     update  ,     DOM       
     Watcher.prototype.update = function () {
      this.el[this.attr] = this.data[this.key]
     }
     
     window.onload = () => { //                  ,           
      new myVue({
       el: '#app',
       data: {
        number: 0,
        count: 0
       },
       methods: {
        increment() {
         this.number++
        },
        incre() {
         this.count++
        }
       }
      })
     }
    </script>
     
    </html>
    3.위의 코드 를 class 의 쓰기 로 변경 합 니 다.
    
    <!DOCTYPE html>
    <html>
     
    <head>
     <title>myVue</title>
     <style>
      #app{
      text-align: center;
     }
    </style>
    </head>
     
    <body>
     <div id="app">
      <form>
       <input type="text" v-model="number" />
       <button type="button" v-click="increment">  </button>
      </form>
      <h3 v-bind="number"></h3>
     </div>
    </body>
    <script>
     
     class MyVue {
      constructor(options) { //          
       this.$options = options // options             ,  el,data,methods
       this.$el = document.querySelector(options.el) // el  #app, this.$el id app Element  
       this.$data = options.data // this.$data = {number: 0}
       this.$methods = options.methods // this.$methods = {increment: function(){}}
     
       this._binding = {}
       this._obsever(this.$data)
       this._complie(this.$el)
      }
      _obsever (data) { //     :    
       let _this = this
       let handler = {
        get(target, key) {
         return target[key]; //       key  
        },
        set(target, key, newValue) {
         let res = Reflect.set(target, key, newValue); //            
         _this._binding[key].map(item => {
          item.update();
         });
         return res;
        }
       };
       //             this.$data, this.$data       ,     this.$data     ,            handler      
       this.$data = new Proxy(data, handler);
      }
      _complie(el) { // el  id app Element  ,         
       let _this = this
       let nodes = Array.prototype.slice.call(el.children) //             
     
       nodes.map(node => {
        if (node.children.length && node.children.length > 0) this._complie(node)
        if (node.hasAttribute('v-click')) { //    v-click  ,      onclick  ,  increment  , number++
         let attrVal = node.getAttribute('v-click')
         node.onclick = _this.$methods[attrVal].bind(_this.$data) // bind  data     method          
        }
     
        //    v-model  ,     INPUT  TEXTAREA,      input  
        if (node.hasAttribute('v-model') && (node.tagName === 'INPUT' || node.tagName === 'TEXTAREA')) {
         let attrVal = node.getAttribute('v-model')
         if (!_this._binding[attrVal]) _this._binding[attrVal] = []
         _this._binding[attrVal].push(new Watcher(
          node, //     DOM   
          _this.$data,
          attrVal, // v-model     
          'value',
         ))
         node.addEventListener('input', () => {
          _this.$data[attrVal] = node.value //  number     node value    ,         
         })
        }
        if (node.hasAttribute('v-bind')) {
         let attrVal = node.getAttribute('v-bind')
         if (!_this._binding[attrVal]) _this._binding[attrVal] = []
         _this._binding[attrVal].push(new Watcher(
          node,
          _this.$data,
          attrVal, // v-bind     
          'innerHTML',
         ))
        }
     
       })
      }
     }
     
     class Watcher {
      constructor (el, data, key, attr) {
       this.el = el //      DOM  
       this.data = data //       this.$data   : {number: 0, count: 0}
       this.key = key //       ,   "num"
       this.attr = attr //       ,   "innerHTML","value"
       this.update()
      }
     
      update () {
       this.el[this.attr] = this.data[this.key]
      }
     }
     
     
     
     window.onload = () => { //                  ,           
      new MyVue({
       el: '#app',
       data: {
        number: 0,
        count: 0
       },
       methods: {
        increment() {
         this.number++
        },
        incre() {
         this.count++
        }
       }
      })
     }
    </script>
     
    </html>
    이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

    좋은 웹페이지 즐겨찾기