成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

(解析)vue源碼解讀

mingde / 860人閱讀

摘要:前言為什么要做源碼解讀我們新到一個環(huán)境,第一件事情就是熟悉環(huán)境熟悉項目,這個很考驗閱讀源碼的能力以及耐心。構造函數(shù)拉到最下面,導出的是一個構造函數(shù)。

前言

A: 為什么要做源碼解讀?
Q: 我們新到一個環(huán)境,第一件事情就是熟悉環(huán)境熟悉項目,這個很考驗閱讀源碼的能力以及耐心。vue是個很好的庫,知名度高,對js的學習具有向上性,所以搞清楚邏輯是有好處的。
A: 閱讀源碼的程度?
Q: 我們完全沒必要從頭到尾細細品味,只需要知道一些核心的實現(xiàn)就好了,畢竟vue也算是個產(chǎn)品,我們沒必要搞清楚一個產(chǎn)品,我們只需要知道產(chǎn)品的核心就夠了,多余的也是業(yè)務代碼。(相對來說)

開始 Vue應用的實例化以及掛載 main.js
new Vue({                      // 初始化vue實例
  //components: { App }        // vue1.0的寫法
  render: h => h(App)          // 最先執(zhí)行,返回一個符合component的對象,vue2.0的寫法
})
.$mount("#app")                // 掛載vue實例到 ‘#app’
render函數(shù)
render: h => h(App) 
就是
render:function(h){
    return h(App)
}
即
render: function (createElement) {
    return createElement(App)
}
找到Vue引用源文件(debug順序)
import Vue from "vue"

找到

node-modules/vue

打開package.json 找到

"main": "dist/vue.runtime.common.js"

"main"是 npm模塊曝光的主要文件.

打開vue.runtime.common.js
ctrl/command + a, ctrl/command + k, ctrl/command + 1
,快捷鍵把所有方法折疊

你會發(fā)現(xiàn) update執(zhí)行了2次,我明明只初始化了一次vue實例,為什么update2次了呢?原因在下方表明。

構造函數(shù)

拉到最下面,

module.exports = Vue;

導出的是一個Vue構造函數(shù)。
當前文件搜索 Vue 找到 構造函數(shù)

function Vue (options) {
  if (process.env.NODE_ENV !== "production" &&
    !(this instanceof Vue)
  ) {
    warn("Vue is a constructor and should be called with the `new` keyword");
  }
  this._init(options); // 調(diào)用 初始化方法
}
初始化函數(shù)
var uid$3 = 0;
vm._uid = uid$3++; //每個vue實例 擁有唯一id,從0開始 ++

// 合并初始化vue實例的參數(shù)(入?yún)ptions和默認參數(shù))
vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      );

initLifecycle();// 初始化實例生命周期相關的參數(shù)
使用Object.create(null)用來獲取一個沒有原型鏈的對象類型

initEvents(); // 初始化實例事件觸發(fā)相關的參數(shù)
initRender(); // 初始化實例渲染相關的參數(shù)

//create的準備工作做好了,觸發(fā)beforeCreate的生命周期
callHook(vm, "beforeCreate");

initState(); // 初始化實例狀態(tài)

// 狀態(tài)也初始化好了,觸發(fā)create的生命周期,所以 create和 breforCreate的區(qū)別就在 create的時候  有狀態(tài)。
callHook(vm, "created");

至此,vue實例的初始化完成,然后掛載到節(jié)點

掛載實例到節(jié)點
// 將mount("#app") => query("#app") 查找到dom對象,賦值給vue.$el
// 觸發(fā)beforeMount的生命周期,所以beforeMount 和 create的區(qū)別就在  beforeMount的時候 有掛載節(jié)點。
callHook(vm, "beforeMount");

// 拿到當前將要掛載的Vnode(虛擬dom對象)
vm._render()  vm.$vnode

// (更新)渲染頁面
vm._update(vm._render(), hydrating);
vm.__patch__(); 
createElm(); // 按照虛擬dom生成真實dom
createElm函數(shù)
// 這是一個遞歸方法,vue實例的初始化是創(chuàng)建一個根節(jié)點,然后再將render函數(shù)傳入的組件掛載,這就是流程圖執(zhí)行2次update的原因。
// 如果是組件,則去做 組件的初始化
if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
  return
}
// 已經(jīng)刪減,只留主要邏輯,判斷虛擬dom 的 tag屬性
if (isDef(tag)) {
  {
    // 遍歷虛擬dom的子節(jié)點,并且創(chuàng)建,然后遞歸當前方法。
    createChildren(vnode, children, insertedVnodeQueue); 
    if (isDef(data)) {
      invokeCreateHooks(vnode, insertedVnodeQueue);
    }
    insert(parentElm, vnode.elm, refElm);
  }
}
// 如果沒有子節(jié)點的,直接創(chuàng)建dom,然后插入
 else if (isTrue(vnode.isComment)) {
  vnode.elm = nodeOps.createComment(vnode.text);
  insert(parentElm, vnode.elm, refElm);
} else {
  vnode.elm = nodeOps.createTextNode(vnode.text);
  insert(parentElm, vnode.elm, refElm);
}
// 觸發(fā)Mount的生命周期
callHook(vm, "mounted");

至此就是 Vue根節(jié)點初始化掛載和渲染的流程.

Vue數(shù)據(jù)更新

首先 我們改造下 app.vue,像官網(wǎng)一樣,我們新增一個雙向綁定的文本框。
現(xiàn)在我們知道了,第一次的update只是掛載了根節(jié)點,那么我們新增了文本框的組件其實是在第二次init的時候初始化的。
我們可以著重看第二次的流程,搞清楚,數(shù)據(jù)的監(jiān)聽與更新。

initState函數(shù)

在每次vue實例初始化的時候 都會執(zhí)行initState,這里面做了vue實例 數(shù)據(jù)的監(jiān)聽。

function initState (vm) {
  vm._watchers = [];
  var opts = vm.$options;
  if (opts.props) { initProps(vm, opts.props); }
  if (opts.methods) { initMethods(vm, opts.methods); }
  if (opts.data) {   // 這里判斷vm.$options.data,從而執(zhí)行initData()或者 直接 監(jiān)聽 vm._data={}作為根狀態(tài) 
    initData(vm);
  } else {
    observe(vm._data = {}, true /* asRootData */);
  }
  if (opts.computed) { initComputed(vm, opts.computed); }
  if (opts.watch && opts.watch !== nativeWatch) {
    initWatch(vm, opts.watch);
  }
}

vm.$options.data 哪里來的呢?
是在 Vue._init方法中

if (options && options._isComponent) {
      // optimize internal component instantiation
      // since dynamic options merging is pretty slow, and none of the
      // internal component options needs special treatment.
      initInternalComponent(vm, options);
    } else {
      vm.$options = mergeOptions(     // 這個方法中  給data賦值,也就是我們render中 的  data(){return{//我們組件的數(shù)據(jù)}}
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      );
    }
initData函數(shù)
function initData (vm) {
  var data = vm.$options.data;
  // 把從render函數(shù)返回的data函數(shù)對象賦值給data,然后data.call(this,this),也就是vm.data();拿到data返回值
  data = vm._data = typeof data === "function"      
    ? getData(data, vm)
    : data || {};
  // observe data
  observe(data, true /* asRootData */);
}
function getData (data, vm) {
  pushTarget();
  try {
    return data.call(vm, vm)
  } catch (e) {
    handleError(e, vm, "data()");
    return {}
  } finally {
    popTarget();
  }
}

function observe (value, asRootData) {
  var ob;
  if (hasOwn(value, "__ob__") && value.__ob__ instanceof Observer) {
    ob = value.__ob__;
  } else if (
    shouldObserve &&
    !isServerRendering() &&
    (Array.isArray(value) || isPlainObject(value)) &&
    Object.isExtensible(value) &&
    !value._isVue
  ) {
    ob = new Observer(value);   // 返回一個 新建的 Observer
  }
  return ob
}
Observer對象(監(jiān)聽者)
var Observer = function Observer (value) {
  this.value = value;
  this.dep = new Dep(); // 這里的dep 稍后會說
  this.vmCount = 0;
  def(value, "__ob__", this);
  if (Array.isArray(value)) {
    var augment = hasProto
      ? protoAugment
      : copyAugment;
    augment(value, arrayMethods, arrayKeys);
    this.observeArray(value); 
  } else {
    this.walk(value);
  }
};
// 如果是對象,則按照key來劫持
Observer.prototype.walk = function walk (obj) {
  var keys = Object.keys(obj);
  for (var i = 0; i < keys.length; i++) {
    defineReactive(obj, keys[i]);
  }
};

// 如果是數(shù)組,就遍歷每個數(shù)組元素,再每個元素再判斷是否為數(shù)組,對象
Observer.prototype.observeArray = function observeArray (items) {
  for (var i = 0, l = items.length; i < l; i++) {
    observe(items[i]);
  }
};
數(shù)據(jù)劫持
function defineReactive (
  obj,
  key,
  val,
  customSetter,
  shallow
) {
  var dep = new Dep(); // 這里的dep 稍后會說
    
  var property = Object.getOwnPropertyDescriptor(obj, key);
  if (property && property.configurable === false) {
    return
  }

  // cater for pre-defined getter/setters
  var getter = property && property.get;
  if (!getter && arguments.length === 2) {
    val = obj[key];
  }
  var setter = property && property.set;

  var childOb = !shallow && observe(val);
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      var value = getter ? getter.call(obj) : val;
      if (Dep.target) {
        dep.depend(); // 這里的dep 稍后會說
        if (childOb) {
          childOb.dep.depend();
          if (Array.isArray(value)) {
            dependArray(value);
          }
        }
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      var value = getter ? getter.call(obj) : val;
      /* eslint-disable no-self-compare */
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return
      }
      /* eslint-enable no-self-compare */
      if (process.env.NODE_ENV !== "production" && customSetter) {
        customSetter();
      }
      if (setter) {
        setter.call(obj, newVal);
      } else {
        val = newVal;
      }
      childOb = !shallow && observe(newVal);
      dep.notify(); // 這里的dep 稍后會說
    }
  });
}
依賴收集

我們在使用vue的時候,data方法會返回對象,包含了所有我們想要觀察的數(shù)據(jù)屬性,同樣 vue也會幫我們監(jiān)聽這些屬性的變化,但是,假如我們在data中設置了多個屬性,但是在模板中只使用了1個,又會如何呢?我們在腳本中設置value2的值(this.value3= "hello world"),那么vue監(jiān)聽到變化 還會去通知模板重新渲染么?

new Vue({
    template: 
        `
value1: {{value1}}
`, data: { value1: "value1", value2: "value2", value3: "value3", ... } });

自然是不會的,vue很聰明的使用了依賴收集
Dep : 一個訂閱者的容器,可以增加或刪除訂閱者,可以向訂閱者發(fā)送消息;
Watcher : 訂閱者類。它在初始化時可以接受getter, callback兩個函數(shù)作為參數(shù)。getter用來計算Watcher對象的值。當Watcher被觸發(fā)時,會重新通過getter計算當前Watcher的值,如果值改變,則會執(zhí)行callback.

Dep(訂閱者容器) Watcher對象(訂閱者) 小結

未完待續(xù)。。。

文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉載請注明本文地址:http://systransis.cn/yun/98862.html

相關文章

  • 入口文件開始,分析Vue源碼實現(xiàn)

    摘要:一方面是因為想要克服自己的惰性,另一方面也是想重新溫故一遍。一共分成了個基礎部分,后續(xù)還會繼續(xù)記錄。文章中如果有筆誤或者不正確的解釋,也歡迎批評指正,共同進步。最后地址部分源碼 Why? 網(wǎng)上現(xiàn)有的Vue源碼解析文章一搜一大批,但是為什么我還要去做這樣的事情呢?因為覺得紙上得來終覺淺,絕知此事要躬行。 然后平時的項目也主要是Vue,在使用Vue的過程中,也對其一些約定產(chǎn)生了一些疑問,可...

    Karrdy 評論0 收藏0
  • 入口文件開始,分析Vue源碼實現(xiàn)

    摘要:一方面是因為想要克服自己的惰性,另一方面也是想重新溫故一遍。一共分成了個基礎部分,后續(xù)還會繼續(xù)記錄。文章中如果有筆誤或者不正確的解釋,也歡迎批評指正,共同進步。最后地址部分源碼 Why? 網(wǎng)上現(xiàn)有的Vue源碼解析文章一搜一大批,但是為什么我還要去做這樣的事情呢?因為覺得紙上得來終覺淺,絕知此事要躬行。 然后平時的項目也主要是Vue,在使用Vue的過程中,也對其一些約定產(chǎn)生了一些疑問,可...

    nidaye 評論0 收藏0
  • vue源碼解讀-目錄結構

    摘要:目錄結構構建相關的文件,一般情況下我們不需要動鉤子別名配置 目錄結構 ├── scripts ------------------------------- 構建相關的文件,一般情況下我們不需要動│ ├── git-hooks ------------------------- git鉤子│ ├── alias.js -------------------------- 別名配...

    philadelphia 評論0 收藏0
  • Vue 2.0】核心源碼解讀 -- 不定期更新

    摘要:觀察員由模板解析指令創(chuàng)建的觀察員負責模板中的更新視圖操作。觀察員種類目前了解情況來看主要分三類視圖指令的計算屬性的用戶自定義的 介紹 關于 Vue.js 的原理一直以來都是一個話題。經(jīng)過幾天的源碼學習和資料介紹,我將一些個人理解的經(jīng)驗給寫下來,希望能夠與大家共勉。 附上GITHUB源碼地址, 如果有任何不解 可以在 文章下面提出或者寫下issue, 方便大家回答和學習, 有興趣可以St...

    sunsmell 評論0 收藏0
  • 前方來報,八月最新資訊--關于vue2&3的最佳文章推薦

    摘要:哪吒別人的看法都是狗屁,你是誰只有你自己說了才算,這是爹教我的道理。哪吒去他個鳥命我命由我,不由天是魔是仙,我自己決定哪吒白白搭上一條人命,你傻不傻敖丙不傻誰和你做朋友太乙真人人是否能夠改變命運,我不曉得。我只曉得,不認命是哪吒的命。 showImg(https://segmentfault.com/img/bVbwiGL?w=900&h=378); 出處 查看github最新的Vue...

    izhuhaodev 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<