国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Vue源碼解析(四)-components組件

zlyBear / 974人閱讀

摘要:組件初始化渲染本文以局部組件的注冊(cè)方式介紹組件的初始化渲染,如下源碼解析一模版渲染介紹過(guò),初始化時(shí)根據(jù)函數(shù)生成函數(shù),本文函數(shù)會(huì)調(diào)用,判斷是注冊(cè)過(guò)的組件,因此以組件的方式生成生成的函數(shù)會(huì)調(diào)用本例,在屬性中注冊(cè)過(guò),因此以組件的

組件初始化渲染

本文以局部組件的注冊(cè)方式介紹組件的初始化渲染,demo如下

new Vue({
  el: "#app",
  template: 
  `
father component!
`, components:{ "my-component": { template: "
children component!
" } } })

1、Vue源碼解析(一)-模版渲染介紹過(guò),vue初始化時(shí)根據(jù)template函數(shù)生成render函數(shù),本文render函數(shù)會(huì)調(diào)用vm._c("my-component"),_createElement判斷"my-component是注冊(cè)過(guò)的組件,因此以組件的方式生成vnode

updateComponent = function () {
  vm._update(vm._render(), hydrating);
};

//template生成的render函數(shù)vm._render會(huì)調(diào)用vm._c("my-component")
vm._c = function (a, b, c, d) { return createElement(vm, a, b, c, d, false); };

function _createElement(){
    //本例tag=‘my-component’,‘my-component’在components屬性中注冊(cè)過(guò),因此以組件的方式生成vnode
    if (isDef(Ctor = resolveAsset(context.$options, "components", tag))) {
      vnode = createComponent(Ctor, data, context, children, tag);
    }
}

//本例Ctor參數(shù){template: "
children component1!
"} function createComponent (Ctor){ //Vue構(gòu)造函數(shù) var baseCtor = context.$options._base; if (isObject(Ctor)) { //生成VuComponent構(gòu)造函數(shù) //此處相當(dāng)于Ctor = Vue.extend({template: "
children component1!
"}), Vue.extend后面有介紹; Ctor = baseCtor.extend(Ctor); } //將componentVNodeHooks上的方法掛載到vnode上,組件初次渲染會(huì)用到componentVNodeHooks.init var data = {} mergeHooks(data); var vnode = new VNode( ("vue-component-" + (Ctor.cid) + (name ? ("-" + name) : "")), data, undefined, undefined, undefined, context, { Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children } ); } //component初始化和更新的方法,此處先介紹init var componentVNodeHooks = { init(vnode){ //根據(jù)Vnode生成VueComponent實(shí)例 var child = vnode.componentInstance = createComponentInstanceForVnode(vnode); //將VueComponent實(shí)例掛載到dom節(jié)點(diǎn)上,本文是掛載到節(jié)點(diǎn) child.$mount(hydrating ? vnode.elm : undefined, hydrating); } }

2、調(diào)用vm._update將vnode渲染為瀏覽器dom,主要方法是遍歷vnode的所有節(jié)點(diǎn),根據(jù)節(jié)點(diǎn)類型調(diào)用相關(guān)的方法進(jìn)行解析,本文主要介紹components的解析方法createComponent:根據(jù)vnode生成VueComponent(繼承Vue)對(duì)象,
調(diào)用Vue.prototype.$mount方法渲染dom

function createElm (vnode, insertedVnodeQueue, parentElm, refElm, nested) {
    //組件vnode節(jié)點(diǎn)渲染方法
    if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
      return
    }
    //Vue源碼解析(一)中介紹過(guò)普通vnode節(jié)點(diǎn)渲染步驟 
    //根據(jù)vnode節(jié)點(diǎn)生成瀏覽器Element對(duì)象
    vnode.elm = nodeOps.createElement(tag, vnode);
    var children = vnode.children;
    //遞歸將vnode子節(jié)點(diǎn)生成Element對(duì)象
    createChildren(vnode, children, insertedVnodeQueue);
    //將生成的vnode.elm插入到瀏覽器的父節(jié)點(diǎn)當(dāng)中
    insert(parentElm, vnode.elm, refElm);
}

function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) {
    var i = vnode.data;
    if (isDef(i = i.hook) && isDef(i = i.init)) {
      //i就是上面的componentVNodeHooks.init方法
      i(vnode, false /* hydrating */, parentElm, refElm);
    }
    if (isDef(vnode.componentInstance)) {
        initComponent(vnode, insertedVnodeQueue);
        if (isTrue(isReactivated)) {
          reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm);
        }
        return true
    }
}  

function createComponentInstanceForVnode (){
    var options = {
      _isComponent: true,
      parent: parent,
      propsData: vnodeComponentOptions.propsData,
      _componentTag: vnodeComponentOptions.tag,
      _parentVnode: vnode,
      _parentListeners: vnodeComponentOptions.listeners,
      _renderChildren: vnodeComponentOptions.children,
      _parentElm: parentElm || null,
      _refElm: refElm || null
    };
    //上面提到的VueComponent構(gòu)造函數(shù)Ctor,相當(dāng)于new VueComponent(options)
    return new vnode.ComponentOptions.Ctor(options)
}

3 、new VueComponent和new Vue的過(guò)程類似,本文就不再做介紹

全局注冊(cè)組件

上文提到過(guò) Vue.extend方法(繼承Vue生成VueComponent構(gòu)造函數(shù))此處多帶帶介紹一下

  Vue.extend = function (extendOptions) {
    var Super = this;
    var Sub = function VueComponent (options) {
      this._init(options);
    };
    //經(jīng)典的繼承寫法
    Sub.prototype = Object.create(Super.prototype);
    Sub.prototype.constructor = Sub;
    Sub.options = mergeOptions(
      Super.options,
      extendOptions
    );
    return Sub
  };

通過(guò)Vue.component也可以全局注冊(cè)組件,不需要每次new vue的時(shí)候多帶帶注冊(cè),demo如下:

var globalComponent = Vue.extend({
    name: "global-component",
    template: "
global component!
" }); Vue.component("global-component", globalComponent); new Vue({ el: "#app", template: `
`, components:{ "my-component": { template: "
children component!
" } } })

vue.js初始化時(shí)會(huì)先調(diào)用一次initGlobalAPI(Vue),給Vue構(gòu)造函數(shù)掛載上一些全局的api,其中又會(huì)調(diào)用到
initAssetRegisters(Vue),其中定義了Vue.component方法,具體看下其實(shí)現(xiàn)

  var ASSET_TYPES = [
      "component",
      "directive",
      "filter"
  ];
  //循環(huán)注冊(cè)ASSET_TYPES中的全局方法
  ASSET_TYPES.forEach(function (type) {
    Vue[type] = function (
      id,
      definition
    ) {
      if (!definition) {
        return this.options[type + "s"][id]
      } else {
        /* istanbul ignore if */
        if (process.env.NODE_ENV !== "production" && type === "component") {
          validateComponentName(id);
        }
        if (type === "component" && isPlainObject(definition)) {
          definition.name = definition.name || id;
          definition = this.options._base.extend(definition);
        }
        if (type === "directive" && typeof definition === "function") {
          definition = { bind: definition, update: definition };
        }
        //全局的組件、指令和過(guò)濾器都掛載在Vue.options上
        this.options[type + "s"][id] = definition;
        return definition
      }
    };
  });

  Vue.prototype._init = function (options) {
      vue初始化時(shí)將options參數(shù)和Vue.options組裝為vm.$options
      vm.$options = mergeOptions(
        //Vue.options
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      );
  }

本例組裝后的vm.$option.components值如下,proto中前3個(gè)屬性是內(nèi)置全局組件

組件通信 prop

在 Vue 中,父子組件的關(guān)系可以總結(jié)為 prop 向下傳遞,事件向上傳遞。父組件通過(guò) prop 給子組件下發(fā)數(shù)據(jù),子組件通過(guò)事件給父組件發(fā)送消息.先看看prop是怎么工作的。demo如下:

new Vue({
  el: "#app",
  template:
  `
father component!
`, components:{ "my-component":{ props: ["message"], template: "{{ message }}" } } })

1、template生成的render函數(shù)包含:_c("my-component",{attrs:{"message":"hello!"}})]
2、render => vnode => VueComponent,上文提到的VueComponent的構(gòu)造函數(shù)調(diào)用了Vue.prototype._init,并且入?yún)ption.propsData:{message: "hello!"}
3、雙向綁定中介紹過(guò)Vue初始化時(shí)會(huì)對(duì)data中的所有屬性調(diào)用defineReactive方法,對(duì)data屬性進(jìn)行監(jiān)聽;
VueComponent對(duì)propsData也是類似的處理方法,initProps后propsData中的屬性和data一樣也是響應(yīng)式的,propsData變化,相應(yīng)的view也會(huì)發(fā)生改變

function initProps (vm, propsOptions) {
    for (var key in propsOptions){
        //defineReactive參照Vue源碼解析(二)
        defineReactive(props, key, value);
        //將propsData代理到vm上,通過(guò)vm[key]訪問(wèn)propsData[key]
        proxy(vm, "_props", key);
    }
}

4、propsData是響應(yīng)式的了,但更常用的是動(dòng)態(tài)props,按官網(wǎng)說(shuō)法:“我們可以用v-bind來(lái)動(dòng)態(tài)地將prop綁定到父組件的數(shù)據(jù)。每當(dāng)父組件的數(shù)據(jù)變化時(shí),該變化也會(huì)傳導(dǎo)給子組件”,那么vue是如何將data的變化傳到給自組件的呢,先看demo

var vm = new Vue({
  el: "#app",
  template:
  `
`, data(){ return{ parentMsg:"hello" } }, components:{ "my-component":{ props: ["message"], template: "{{ message }}" } } }) vm.parentMsg = "hello world"

5、雙向綁定中介紹過(guò)vm.parentMsg變化,會(huì)觸發(fā)dep.notify(),通知watcher調(diào)用updateComponent;
又回到了updateComponent,之后的dom更新過(guò)程可以參考上文的組件渲染邏輯,只是propsData值已經(jīng)是最新的vm.parentMsg的值了

//又見(jiàn)到了。。所有的dom初始化或更新都會(huì)用到
updateComponent = function () {
  vm._update(vm._render(), hydrating);
};

Vue.prototype._update = function (vnode, hydrating) {
    var prevVnode = vm._vnode;
    vm._vnode = vnode;
    //Vue源碼解析(一)介紹過(guò)dom初始化渲染的源碼
    if (!prevVnode) {
      // initial render
      vm.$el = vm.__patch__(
        vm.$el, vnode, hydrating, false /* removeOnly */,
        vm.$options._parentElm,
        vm.$options._refElm
      );
    } else {
      // 本文介紹dom更新的方法
      vm.$el = vm.__patch__(prevVnode, vnode);
    }
}

Vue源碼解析(一)介紹過(guò)vm.__patch__中dom初始化渲染的邏輯,本文再簡(jiǎn)單介紹下vm.__patch關(guān)于component更新的邏輯:

function patchVnode (oldVnode, vnode){
    //上文介紹過(guò)componentVNodeHooks.init,此處i=componentVNodeHooks.prepatch
    var data = vnode.data;
    if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) {
        i(oldVnode, vnode);
    }
}
var componentVNodeHooks = {
  init(){},
  prepatch: function prepatch (oldVnode, vnode) {
    var options = vnode.componentOptions;
    var child = vnode.componentInstance = oldVnode.componentInstance;
    //更新組件
    updateChildComponent(
      child,
      //此時(shí)的propsData已經(jīng)是最新的vm.parentMsg
      options.propsData, // updated props
      options.listeners, // updated listeners
      vnode, // new parent vnode
      options.children // new children
    );
  }
}

function updateChildComponent (vm, propsData){
    //將vm._props[key]設(shè)置為新的propsData[key]值,從而觸發(fā)view層的更新
    var props = vm._props;
    props[key] = validateProp(key, vm.$options.props, propsData, vm);
}
emit

子組件向父組件通信需要用到emit,先給出demo

var vm = new Vue({
  el: "#app",
  template:
  `
`, methods:{ receiveFn(msg){ console.log(msg) } }, components:{ "my-component":{ template: "
child
", mounted(){ this.$emit("rf","hello") } } } })

本例中子組件mount結(jié)束會(huì)觸發(fā)callHook(vm, "mounted"),調(diào)用this.$emit("rf","hello"),從而調(diào)用父組件的receiveFn方法

Vue.prototype.$emit = function (event) {
    //本例cbs=vm._events["rf"] = receiveFn,vm._events涉及v-on指令解析,以后有機(jī)會(huì)詳細(xì)介紹下
    var cbs = vm._events[event];
    //截取第一位之后的參數(shù)
    var args = toArray(arguments, 1);
    //執(zhí)行cbs
    cbs.apply(vm, args);
}
event bus

prop和emit是父子組件通信的方式,非父子組件可以通過(guò)event bus(事件總線)實(shí)現(xiàn)

var bus = new Vue();
var vm = new Vue({
  el: "#app",
  template:
  `
`, components:{ "my-component-1":{ template: "
child1
", mounted(){ bus.$on("event",(msg)=>{ console.log(msg) }) } }, "my-component-2":{ template: "
child2
", mounted(){ bus.$emit("event","asd") } } } })

emit方法上文已經(jīng)介紹過(guò),主要看下on方法,其實(shí)就是將fn注冊(cè)到vm._events上

  Vue.prototype.$on = function (event, fn) {
    var vm = this;
    if (Array.isArray(event)) {
      for (var i = 0, l = event.length; i < l; i++) {
        this.$on(event[i], fn);
      }
    } else {
      (vm._events[event] || (vm._events[event] = [])).push(fn);
    }
    return vm
  };

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/89782.html

相關(guān)文章

  • Vue源碼解析(六)-vue-router

    摘要:是響應(yīng)式的,當(dāng)瀏覽器路由改變時(shí),的值也會(huì)相應(yīng)的改變的作用是清楚了,但頁(yè)面內(nèi)容的變化是怎么實(shí)現(xiàn)的呢下面再介紹下的作用。 先上一段簡(jiǎn)單的demo,本文根據(jù)此demo進(jìn)行解析 Vue.use(VueRouter) const router = new VueRouter({ routes: [ { path: /home, component: {template: ...

    蘇丹 評(píng)論0 收藏0
  • vue源碼解析-事件機(jī)制

    摘要:直接寫了組件機(jī)制。今天看了下的關(guān)于事件的機(jī)制。源碼都是基于最新的。綁定了事件回調(diào)函數(shù)的。初始化的時(shí)候,將中的方法代理到的同時(shí)修飾了事件的回調(diào)函數(shù)。對(duì)于事件有兩個(gè)底層的處理邏輯。 上一章沒(méi)什么經(jīng)驗(yàn)。直接寫了組件機(jī)制。感覺(jué)涉及到的東西非常的多,不是很方便講。今天看了下vue的關(guān)于事件的機(jī)制。有一些些體會(huì)。寫出來(lái)。大家一起糾正,分享。源碼都是基于最新的Vue.js v2.3.0。下面我們來(lái)看...

    LuDongWei 評(píng)論0 收藏0
  • vue@2.0源碼學(xué)習(xí)---組件究竟是什么

    摘要:定義一個(gè)組件如下打印如下再回過(guò)頭看,可以發(fā)現(xiàn)他做的工作就是擴(kuò)展一個(gè)構(gòu)造函數(shù),并將這個(gè)構(gòu)造函數(shù)添加到現(xiàn)在我們已經(jīng)可以回答最開始的問(wèn)題的組件是什么的組件其實(shí)就是擴(kuò)展的構(gòu)造函數(shù),并且在適當(dāng)?shù)臅r(shí)候?qū)嵗癁閷?shí)例。 vue@2.0源碼學(xué)習(xí)---組件究竟是什么 本篇文章從最簡(jiǎn)單的情況入手,不考慮prop和組件間通信。 Vue.component vue文檔告訴我們可以使用Vue.component(...

    PiscesYE 評(píng)論0 收藏0
  • vue學(xué)習(xí)筆記(

    摘要:提供了兩種向組件傳遞參數(shù)的方式。子路由項(xiàng)路徑不要使用開頭,以開頭的嵌套路徑會(huì)被當(dāng)作根路徑。路由實(shí)例的方法這里學(xué)習(xí)兩個(gè)路由實(shí)例的方法和。實(shí)際上,是通過(guò)不同的將這些資源加載后打包,然后輸出打包后文件。 一、vue-router 1、簡(jiǎn)介 我們經(jīng)常使用vue開發(fā)單頁(yè)面應(yīng)用程序(SPA)。在開發(fā)SPA過(guò)程中,路由是必不可少的部分,vue的官方推薦是vue-router。單頁(yè)面應(yīng)用程序看起來(lái)好像...

    frank_fun 評(píng)論0 收藏0
  • vue學(xué)習(xí)筆記(

    摘要:提供了兩種向組件傳遞參數(shù)的方式。子路由項(xiàng)路徑不要使用開頭,以開頭的嵌套路徑會(huì)被當(dāng)作根路徑。路由實(shí)例的方法這里學(xué)習(xí)兩個(gè)路由實(shí)例的方法和。實(shí)際上,是通過(guò)不同的將這些資源加載后打包,然后輸出打包后文件。 一、vue-router 1、簡(jiǎn)介 我們經(jīng)常使用vue開發(fā)單頁(yè)面應(yīng)用程序(SPA)。在開發(fā)SPA過(guò)程中,路由是必不可少的部分,vue的官方推薦是vue-router。單頁(yè)面應(yīng)用程序看起來(lái)好像...

    lwx12525 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<