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

資訊專(zhuān)欄INFORMATION COLUMN

vue源碼解析-事件機(jī)制

LuDongWei / 300人閱讀

摘要:直接寫(xiě)了組件機(jī)制。今天看了下的關(guān)于事件的機(jī)制。源碼都是基于最新的。綁定了事件回調(diào)函數(shù)的。初始化的時(shí)候,將中的方法代理到的同時(shí)修飾了事件的回調(diào)函數(shù)。對(duì)于事件有兩個(gè)底層的處理邏輯。

上一章沒(méi)什么經(jīng)驗(yàn)。直接寫(xiě)了組件機(jī)制。感覺(jué)涉及到的東西非常的多,不是很方便講。今天看了下vue的關(guān)于事件的機(jī)制。有一些些體會(huì)。寫(xiě)出來(lái)。大家一起糾正,分享。源碼都是基于最新的Vue.js v2.3.0。下面我們來(lái)看看vue中的事件機(jī)制:
老樣子還是先上一段貫穿全局的代碼,常見(jiàn)的事件機(jī)制demo都會(huì)包含在這段代碼中:

click1
click2

上面的demo中一共有四個(gè)事件?;竞w了vue中最經(jīng)典的事件的四種情況

普通html元素上的事件

好吧。想想我們還是一個(gè)個(gè)來(lái)看。如果懂vue組件相關(guān)的機(jī)制會(huì)更容易懂。那么首先我們看看最簡(jiǎn)單的第一、二個(gè)(兩個(gè)事件只差了個(gè)修飾符):

click1

這是簡(jiǎn)單到不能在簡(jiǎn)單的一個(gè)點(diǎn)擊事件。
我們來(lái)看看建立這么一個(gè)簡(jiǎn)單的點(diǎn)擊事件,vue中發(fā)生了什么。
1:new Vue()中調(diào)用了initState(vue):看代碼

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) {
    initData(vm);
  } else {
    observe(vm._data = {}, true /* asRootData */);
  }
  if (opts.computed) { initComputed(vm, opts.computed); }
  if (opts.watch) { initWatch(vm, opts.watch); }
}

//接著看看initMethods
function initMethods (vm, methods) {
  var props = vm.$options.props;
  for (var key in methods) {
    vm[key] = methods[key] == null ? noop : bind(methods[key], vm);//調(diào)用了bind方法,我們?cè)倏纯碽ind
    {
      if (methods[key] == null) {
        warn(
          "method "" + key + "" has an undefined value in the component definition. " +
          "Did you reference the function correctly?",
          vm
        );
      }
      if (props && hasOwn(props, key)) {
        warn(
          ("method "" + key + "" has already been defined as a prop."),
          vm
        );
      }
    }
  }
}

//我們接著看看bind

function bind (fn, ctx) {
  function boundFn (a) {
    var l = arguments.length;
    return l
      ? l > 1
        ? fn.apply(ctx, arguments)//通過(guò)返回函數(shù)修飾了事件的回調(diào)函數(shù)。綁定了事件回調(diào)函數(shù)的this。并且讓參數(shù)自定義。更加的靈活
        : fn.call(ctx, a)
      : fn.call(ctx)
  }
  // record original fn length
  boundFn._length = fn.length;
  return boundFn
}

總的來(lái)說(shuō)。vue初始化的時(shí)候,將method中的方法代理到vue[key]的同時(shí)修飾了事件的回調(diào)函數(shù)。綁定了作用域。

2:vue進(jìn)入compile環(huán)節(jié)需要將該div變成ast(抽象語(yǔ)法樹(shù))。當(dāng)編譯到該div時(shí)經(jīng)過(guò)核心函數(shù)genHandler:

function genHandler (
  name,
  handler
) {
  if (!handler) {
    return "function(){}"
  }

  if (Array.isArray(handler)) {
    return ("[" + (handler.map(function (handler) { return genHandler(name, handler); }).join(",")) + "]")
  }

  var isMethodPath = simplePathRE.test(handler.value);
  var isFunctionExpression = fnExpRE.test(handler.value);

  if (!handler.modifiers) {
    return isMethodPath || isFunctionExpression//假如沒(méi)有修飾符。直接返回回調(diào)函數(shù)
      ? handler.value
      : ("function($event){" + (handler.value) + "}") // inline statement
  } else {
    var code = "";
    var genModifierCode = "";
    var keys = [];
    for (var key in handler.modifiers) {
      if (modifierCode[key]) {
        genModifierCode += modifierCode[key];//處理修飾符數(shù)組,例如.stop就在回調(diào)函數(shù)里加入event.stopPropagation()再返回。實(shí)現(xiàn)修飾的目的
        // left/right
        if (keyCodes[key]) {
          keys.push(key);
        }
      } else {
        keys.push(key);
      }
    }
    if (keys.length) {
      code += genKeyFilter(keys);
    }
    // Make sure modifiers like prevent and stop get executed after key filtering
    if (genModifierCode) {
      code += genModifierCode;
    }
    var handlerCode = isMethodPath
      ? handler.value + "($event)"
      : isFunctionExpression
        ? ("(" + (handler.value) + ")($event)")
        : handler.value;
    return ("function($event){" + code + handlerCode + "}")
  }
}

genHandler函數(shù)簡(jiǎn)單明了,如果事件函數(shù)有修飾符。就處理完修飾符,添加修飾符對(duì)應(yīng)的函數(shù)語(yǔ)句。再返回。這個(gè)過(guò)程還會(huì)多帶帶對(duì)native修飾符做特殊處理。這個(gè)等會(huì)說(shuō)。compile完后自然就render。我們看看render函數(shù)中這塊區(qū)域長(zhǎng)什么樣子:

_c("div",{attrs:{"id":"test1"},on:{"click":click1}},[_v("click1")]),_v(" "),_c("div",{attrs:{"id":"test2"},on:{"click":function($event){$event.stopPropagation();click2($event)}}}

一目了然。最后在虛擬dom-》真實(shí)dom的時(shí)候。會(huì)調(diào)用核心函數(shù):

function add$1 (
  event,
  handler,
  once$$1,
  capture,
  passive
) {
  if (once$$1) {
    var oldHandler = handler;
    var _target = target$1; // save current target element in closure
    handler = function (ev) {
      var res = arguments.length === 1
        ? oldHandler(ev)
        : oldHandler.apply(null, arguments);
      if (res !== null) {
        remove$2(event, handler, capture, _target);
      }
    };
  }
  target$1.addEventListener(
    event,
    handler,
    supportsPassive
      ? { capture: capture, passive: passive }//此處綁定點(diǎn)擊事件
      : capture
  );
}
組件上的事件

好了下面就是接下來(lái)的組件上的點(diǎn)擊事件了??梢灶A(yù)感到他走的和普通的html元素應(yīng)該是不同的道路。事實(shí)也是如此:


  

最簡(jiǎn)單的一個(gè)例子。兩個(gè)事件的區(qū)別就是一個(gè)有.native的修飾符。我們來(lái)看看官方.native的作用:在原生dom上綁定事件。好吧。很簡(jiǎn)單。我們跟隨源碼看看有何不同。這里可以往回看看我少的可憐的上一章組件機(jī)制。vue中的組件都是擴(kuò)展的vue的一個(gè)新實(shí)例。在compile結(jié)束的時(shí)候你還是可以發(fā)現(xiàn)他也是類(lèi)似的一個(gè)樣子。如下圖:

_c("my-component",{on:{"componenton":parentOn},nativeOn:{"click":function($event){nativeclick($event)}}

可以看到加了.native修飾符的會(huì)被放入nativeOn的數(shù)組中。等待后續(xù)特殊處理。等不及了。我們直接來(lái)看看特殊處理。render函數(shù)在執(zhí)行時(shí)。如果遇到組件??催^(guò)上一章的可以知道。會(huì)執(zhí)行

function createComponent (
  Ctor,
  data,
  context,
  children,
  tag
) {
  if (isUndef(Ctor)) {
    return
  }

  var baseCtor = context.$options._base;

  // plain options object: turn it into a constructor
  if (isObject(Ctor)) {
    Ctor = baseCtor.extend(Ctor);
  }

  // if at this stage it"s not a constructor or an async component factory,
  // reject.
  if (typeof Ctor !== "function") {
    {
      warn(("Invalid Component definition: " + (String(Ctor))), context);
    }
    return
  }

  // async component
  if (isUndef(Ctor.cid)) {
    Ctor = resolveAsyncComponent(Ctor, baseCtor, context);
    if (Ctor === undefined) {
      // return nothing if this is indeed an async component
      // wait for the callback to trigger parent update.
      return
    }
  }

  // resolve constructor options in case global mixins are applied after
  // component constructor creation
  resolveConstructorOptions(Ctor);

  data = data || {};

  // transform component v-model data into props & events
  if (isDef(data.model)) {
    transformModel(Ctor.options, data);
  }

  // extract props
  var propsData = extractPropsFromVNodeData(data, Ctor, tag);

  // functional component
  if (isTrue(Ctor.options.functional)) {
    return createFunctionalComponent(Ctor, propsData, data, context, children)
  }

  // extract listeners, since these needs to be treated as
  // child component listeners instead of DOM listeners
  var listeners = data.on;//listeners緩存data.on的函數(shù)。這里就是componenton事件
  // replace with listeners with .native modifier
  data.on = data.nativeOn;//正常的data.on會(huì)被native修飾符的事件所替換

  if (isTrue(Ctor.options.abstract)) {
    // abstract components do not keep anything
    // other than props & listeners
    data = {};
  }

  // merge component management hooks onto the placeholder node
  mergeHooks(data);

  // return a placeholder vnode
  var name = Ctor.options.name || tag;
  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 }
  );
  return vnode
}

整段代碼關(guān)于事件核心操作:

var listeners = data.on;//listeners緩存data.on的函數(shù)。這里就是componenton事件
// replace with listeners with .native modifier
data.on = data.nativeOn;//正常的data.on會(huì)被native修飾符的事件所替換

經(jīng)過(guò)這兩句話。.native修飾符的事件會(huì)被放在data.on上面。接下來(lái)data.on上的事件(這里就是nativeclick)會(huì)按普通的html事件往下走。最后執(zhí)行target.add("",""")掛上原生的事件。而先前的data.on上的被緩存在listeneners的事件就沒(méi)著么愉快了。接下來(lái)他會(huì)在組件init的時(shí)候。它會(huì)進(jìn)入一下分支:

function initEvents (vm) {
  vm._events = Object.create(null);
  vm._hasHookEvent = false;
  // init parent attached events
  var listeners = vm.$options._parentListeners;
  if (listeners) {
    updateComponentListeners(vm, listeners);
  }
}

function updateComponentListeners (
  vm,
  listeners,
  oldListeners
) {
  target = vm;
  updateListeners(listeners, oldListeners || {}, add, remove$1, vm);
}

function add (event, fn, once$$1) {
  if (once$$1) {
    target.$once(event, fn);
  } else {
    target.$on(event, fn);
  }
}

發(fā)現(xiàn)組件上的沒(méi)有.native的修飾符調(diào)用的是$on方法。這個(gè)好熟悉。進(jìn)入到$on,$emit大致想到是一個(gè)典型的觀察者模式的事件??纯聪嚓P(guān)$on,$emit代碼。我加點(diǎn)注解:

Vue.prototype.$on = function (event, fn) {
    var this$1 = this;

    var vm = this;
    if (Array.isArray(event)) {
      for (var i = 0, l = event.length; i < l; i++) {
        this$1.$on(event[i], fn);
      }
    } else {
      (vm._events[event] || (vm._events[event] = [])).push(fn);//存入事件
      // optimize hook:event cost by using a boolean flag marked at registration
      // instead of a hash lookup
      if (hookRE.test(event)) {
        vm._hasHookEvent = true;
      }
    }
    return vm
  };

Vue.prototype.$emit = function (event) {
    var vm = this;
    console.log(vm);
    {
      var lowerCaseEvent = event.toLowerCase();
      if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
        tip(
          "Event "" + lowerCaseEvent + "" is emitted in component " +
          (formatComponentName(vm)) + " but the handler is registered for "" + event + "". " +
          "Note that HTML attributes are case-insensitive and you cannot use " +
          "v-on to listen to camelCase events when using in-DOM templates. " +
          "You should probably use "" + (hyphenate(event)) + "" instead of "" + event + ""."
        );
      }
    }
    var cbs = vm._events[event];
    console.log(cbs);
    if (cbs) {
      cbs = cbs.length > 1 ? toArray(cbs) : cbs;
      var args = toArray(arguments, 1);
      for (var i = 0, l = cbs.length; i < l; i++) {
        cbs[i].apply(vm, args);//當(dāng)emit的時(shí)候調(diào)用該事件。注意上面說(shuō)的vue在初始化的守候。用bind修飾了事件函數(shù)。所以組件上掛載的事件都是在父作用域中的
      }
    }
    return vm
  };

看了上面的$on,$emit用法下面這個(gè)demo也就瞬間秒解了(一個(gè)經(jīng)常用的非父子組件通信):

var bus = new Vue()
// 觸發(fā)組件 A 中的事件
bus.$emit("id-selected", 1)
// 在組件 B 創(chuàng)建的鉤子中監(jiān)聽(tīng)事件
bus.$on("id-selected", function (id) {
  // ...
})

是不是豁然開(kāi)朗。

又到了愉快的總結(jié)時(shí)間了。segementfault的編輯器真難用。內(nèi)容多就卡。哎。煩??ǖ臅r(shí)間夠看好多肥皂劇了。
總的來(lái)說(shuō)。vue對(duì)于事件有兩個(gè)底層的處理邏輯。
1:普通html元素和在組件上掛了.native修飾符的事件。最終EventTarget.addEventListener() 掛載事件
2:組件上的,vue實(shí)例上的事件會(huì)調(diào)用原型上的$on,$emit(包括一些其他api $off,$once等等)

荊軻刺秦王。下次見(jiàn)

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

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

相關(guān)文章

  • Vue.js 源碼學(xué)習(xí)筆記

    摘要:實(shí)際上,我在看代碼的過(guò)程中順手提交了這個(gè),作者眼明手快,當(dāng)天就進(jìn)行了修復(fù),現(xiàn)在最新的代碼里已經(jīng)不是這個(gè)樣子了而且狀態(tài)機(jī)標(biāo)識(shí)由字符串換成了數(shù)字常量,解析更準(zhǔn)確的同時(shí)執(zhí)行效率也會(huì)更高。 最近饒有興致的又把最新版?Vue.js?的源碼學(xué)習(xí)了一下,覺(jué)得真心不錯(cuò),個(gè)人覺(jué)得 Vue.js 的代碼非常之優(yōu)雅而且精辟,作者本身可能無(wú) (bu) 意 (xie) 提及這些。那么,就讓我來(lái)吧:) 程序結(jié)構(gòu)梳...

    darkbaby123 評(píng)論0 收藏0
  • Vue.js 源碼學(xué)習(xí)筆記

    摘要:實(shí)際上,我在看代碼的過(guò)程中順手提交了這個(gè),作者眼明手快,當(dāng)天就進(jìn)行了修復(fù),現(xiàn)在最新的代碼里已經(jīng)不是這個(gè)樣子了而且狀態(tài)機(jī)標(biāo)識(shí)由字符串換成了數(shù)字常量,解析更準(zhǔn)確的同時(shí)執(zhí)行效率也會(huì)更高。 最近饒有興致的又把最新版?Vue.js?的源碼學(xué)習(xí)了一下,覺(jué)得真心不錯(cuò),個(gè)人覺(jué)得 Vue.js 的代碼非常之優(yōu)雅而且精辟,作者本身可能無(wú) (bu) 意 (xie) 提及這些。那么,就讓我來(lái)吧:) 程序結(jié)構(gòu)梳...

    jsdt 評(píng)論0 收藏0
  • Vue原理】Event - 白話版

    摘要:寫(xiě)文章不容易,點(diǎn)個(gè)贊唄兄弟專(zhuān)注源碼分享,文章分為白話版和源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于版本如果你覺(jué)得排版難看,請(qǐng)點(diǎn)擊下面鏈接或者拉到下面關(guān)注公眾號(hào)也可以吧原理白話版事件是我最感興趣的東西之 寫(xiě)文章不容易,點(diǎn)個(gè)贊唄兄弟專(zhuān)注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于...

    kumfo 評(píng)論0 收藏0
  • 讀?VuePress(四)插件機(jī)制的設(shè)計(jì)

    前言 從 9 月份開(kāi)始,vuepress 源碼進(jìn)行了重新設(shè)計(jì)和拆分。先是開(kāi)了個(gè) next 分支,后來(lái)又合并到 master 分支,為即將發(fā)布的 1.x 版本做準(zhǔn)備。 最主要的變化是:大部分的全局功能都被拆分成了插件的形式,以可插拔的方式來(lái)支撐 vuepress 的運(yùn)作,這一點(diǎn)很像 webpack。 具體架構(gòu)如下: showImg(https://user-gold-cdn.xitu.io/2019...

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

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

0條評(píng)論

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