vue 2.0ソースコード分析の理解応答式アーキテクチャ


分かち合う前に
以前に紹介したのですが、どうやってobserverwatcherを実現しますか?続けて書きたいですが、vue 2.0は空から出てきますので、直接vue 2.0を見てください.この文章は会社でシェアして、やっと書きました.私たちは最も洗練されたコードを使って、vue 2.0応答式のアーキテクチャを復元し、以前書いたvueソースの分析を実現するために、どうやってobserverとwatchを実現するかを今回の共有の参考にすることができます.見なくてもいいですが、Object.definePropertyを知っておいたほうがいいです.
この文章は何を共有しますか
vue 2.0の応答式アーキテクチャを理解すると、次の図です.
ちなみに彼がレイクより速い理由の一つを紹介します.
本分は何を実現しますか
const demo = new Vue({
  data: {
    text: "before",
  },
  //   template   
{{text}}
render(h){ return h('div', {}, [ h('span', {}, [this.__toString__(this.text)]) ]) } }) setTimeout(function(){ demo.text = "after" }, 3000)
対応する仮想domは beforeから
after
に変えられます.始めましょう.
第一歩は、dataの下の全ての属性をobservableに変更します.
コードを見てみましょう.
    class Vue {
      constructor(options) {
        this.$options = options
        this._data = options.data
        observer(options.data, this._update)
        this._update()
      }
      _update(){
        this.$options.render()
      }
    }


    function observer(value, cb){
      Object.keys(value).forEach((key) => defineReactive(value, key, value[key] , cb))
    }

    function defineReactive(obj, key, val, cb) {
      Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: ()=>{},
        set:newVal=> {
          cb()
        }
      })
    }

    var demo = new Vue({
      el: '#demo',
      data: {
        text: 123,
      },
      render(){
        console.log("  render ")
      }
    })

     setTimeout(function(){
       demo._data.text = 444
     }, 3000)
デモのために、私たちは最も簡単な状況だけを考えています.vueのソースコードの分析を見たら、どうやってobserverとwatchを実現すればいいのかよく分かります.
    var demo = new Vue({
      el: '#demo',
      data: {
        text: 123,
      },
      render(){
        console.log("  render ")
      }
    })
dataの中のすべての属性をobserverに置いて、dataの中の属性、例えばtextを変えて、_update()の関数を呼び出して再レンダリングします.これはどのようにしてできますか?私達は実際には値が賦課されていると知っています.dataの下のtextに値を与えたらset関数がトリガされます.このとき_updateを呼んだらOKですが、
     setTimeout(function(){
       demo._data.text = 444
     }, 3000)
demo._data.textdemo.textを使っていません.大丈夫です.代理店を追加します.
      _proxy(key) {
        const self = this
        Object.defineProperty(self, key, {
          configurable: true,
          enumerable: true,
          get: function proxyGetter () {
            return self._data[key]
          },
          set: function proxySetter (val) {
            self._data[key] = val
          }
        })
      }
次に、Vueconstructorに次のような文を加えます.
    Object.keys(options.data).forEach(key => this._proxy(key))
第1のステップは、まずここで述べてください.dataのいずれかの属性の値が変更され、_updateのトリガを引き起こして再レンダリングされます.属性は明らかに正確ではありません.
第二歩、第一歩はなぜ正確ではないかを詳しく述べます.
たとえば、下記のコードを考慮します.
    new Vue({
      template: `
        
name: {{name}}
age: {{age}}
`, data: { name: 'js', age: 24, height: 180 } }) setTimeout(function(){ demo.height = 181 }, 3000)

template data name ageheight , , ? !, , !!

DOM

,template render ( , , ), render DOM, DOM

    function VNode(tag, data, children, text) {
      return {
        tag: tag,
        data: data,
        children: children,
        text: text
      }
    }

    class Vue {
      constructor(options) {
        this.$options = options
        const vdom = this._update()
        console.log(vdom)
      }
      _update() {
        return this._render.call(this)
      }
      _render() {
        const vnode = this.$options.render.call(this)
        return vnode
      }
      __h__(tag, attr, children) {
        return VNode(tag, attr, children.map((child)=>{
          if(typeof child === 'string'){
            return VNode(undefined, undefined, undefined, child)
          }else{
            return child
          }
        }))
      }
      __toString__(val) {
        return val == null ? '' : typeof val === 'object' ? JSON.stringify(val, null, 2) : String(val);
      }
    }


    var demo = new Vue({
      el: '#demo',
      data: {
        text: "before",
      },
      render(){
        return this.__h__('div', {}, [
          this.__h__('span', {}, [this.__toString__(this.text)])
        ])
      }
    })
が したら、 が します.
     {
       tag: 'div',
       data: {},
       children:[
         {
           tag: 'span',
           data: {},
           children: [
             {
               children: undefined,
               data: undefined,
               tag: undefined,
               text: '' //           before,                ,      
             }
           ]
         }
       ]
     }
これは の も な DOMであり、taghtmlタグ であり、dataclassおよびstyleなどのラベル の を み、childenはサブノードであり、 DOMに しては しない. の に ります.つまり、render の でvueインスタンスに している が かります. はちょっと いにくいですが、コードを ます.
    var demo = new Vue({
      el: '#demo',
      data: {
        text: "before",
        name: "123",
        age: 23
      },
      render(){
        return this.__h__('div', {}, [
          this.__h__('span', {}, [this.__toString__(this.text)])
        ])
      }
    })
このコードのように、rendertextだけに しています.nameageに していません.ですから、textが したら、 たちは にrender をトリガして、 DOMを させてもいいです.( りはこの DOMと の DOMとのマッチングです.その 、 の はDOMです.あとで うしかないです.)どうすればいいかを に えてみます.
、'touch'は を る.
の に ります.datadefineReactiveを っています.data の を するとsetがトリガされます.data の を るとgetがトリガされます.そうです. で を ってもいいです.renderを に してみます.data のどの がトリガされたかを てみます. たちはgetをトリガしました.renderdata のどの に しているかを ることができます.その 、これらの を にしてもいいです.これらの が わるたびに、renderをトリガします. のステップは に4つのサブ で された です.( には、renderだけではなく、いずれかの の は、 の の によって き こされ、 の 、つまりcomputedwatchの でもmobxのコアでもある)

たちは に するクラスを いています. data のオブジェクトはrender に する がありますので、 はdefineReactiveの に されます.
    class Dep {
      constructor() {
        this.subs = []
      }
      add(cb) {
        this.subs.push(cb)
      }
      notify() {
        console.log(this.subs);
        this.subs.forEach((cb) => cb())
      }
    }
    function defineReactive(obj, key, val, cb) {
      const dep = new Dep()
      Object.defineProperty(obj, key, {
        //   
      })
    }
そして、render を してtouchに すると、 getが され、renderの をsubsの に することができます.setの は、notifyのすべてのsubs の を します.renderの が まれます.これで します. は、すべてのコードを します.
    function VNode(tag, data, children, text) {
      return {
        tag: tag,
        data: data,
        children: children,
        text: text
      }
    }

    class Vue {
      constructor(options) {
        this.$options = options
        this._data = options.data
        Object.keys(options.data).forEach(key => this._proxy(key))
        observer(options.data)
        const vdom = watch(this, this._render.bind(this), this._update.bind(this))
        console.log(vdom)
      }
      _proxy(key) {
        const self = this
        Object.defineProperty(self, key, {
          configurable: true,
          enumerable: true,
          get: function proxyGetter () {
            return self._data[key]
          },
          set: function proxySetter (val) {
            self._data.text = val
          }
        })
      }
      _update() {
        console.log("     ");
        const vdom = this._render.call(this)
        console.log(vdom);
      }
      _render() {
        return this.$options.render.call(this)
      }
      __h__(tag, attr, children) {
        return VNode(tag, attr, children.map((child)=>{
          if(typeof child === 'string'){
            return VNode(undefined, undefined, undefined, child)
          }else{
            return child
          }
        }))
      }
      __toString__(val) {
        return val == null ? '' : typeof val === 'object' ? JSON.stringify(val, null, 2) : String(val);
      }
    }

    function observer(value, cb){
      Object.keys(value).forEach((key) => defineReactive(value, key, value[key] , cb))
    }

    function defineReactive(obj, key, val, cb) {
      const dep = new Dep()
      Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: ()=>{
          if(Dep.target){
            dep.add(Dep.target)
          }
          return val
        },
        set: newVal => {
          if(newVal === val)
            return
          val = newVal
          dep.notify()
        }
      })
    }
    function watch(vm, exp, cb){
      Dep.target = cb
      return exp()
    }

    class Dep {
      constructor() {
        this.subs = []
      }
      add(cb) {
        this.subs.push(cb)
      }
      notify() {
        this.subs.forEach((cb) => cb())
      }
    }
    Dep.target = null


    var demo = new Vue({
      el: '#demo',
      data: {
        text: "before",
      },
      render(){
        return this.__h__('div', {}, [
          this.__h__('span', {}, [this.__toString__(this.text)])
        ])
      }
    })


     setTimeout(function(){
       demo.text = "after"
     }, 3000)
を てみます.
たちはDep.targetを します. のgetですか?それとも を す のgetですか?
    function watch(vm, exp, cb){
      Dep.target = cb
      return exp()
    }
Dep.targetの は、フラグに し、get
       get: () => {
          if (Dep.target) {
            dep.add(Dep.target)
          }
          return val
        },
すればいいです. までその を たらよく かりますか?
め りをつける
はとても きです.Vue 2.0 のコードは をよくするために、 な で れます.でも、コード の はVue 2.0と じようにreact、vue 2.0 で してくれます. に レンダリングします.reactは の を します. の をします. えば、 したreactはどのように が いですか? ( )、 びreactはなぜimmutble.js reactを してpure renderを しなければならない 、bind(this) れた です.vue 2.0 がベストを くしてくれました.また、ラベルのような のclass に して、vue 2.0は レンダリング にdiffをする は しません.vue 2.0は が したreactよりも い です.ここで、 きなのはstarにあげるように えています. があれば、コメントエリアにコメントしてもいいですよ.


<div class=「col-md-3」>
<div class=「row」id=「ad」>

<div id=「right-1」class=「col-lg-12 col-sm-4 col-xs-4 ad」


<div id=「right-2」class=「col-lg-12 col-sm-4 col-xs-4 ad」


<div id=「right-3」class=「col-lg-12 col-sm-4 col-xs-4 ad」