<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
        問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
        當前位置: 首頁 - 科技 - 知識百科 - 正文

        vue底層事件邏輯解析

        來源:懂視網 責編:小采 時間:2020-11-27 19:40:10
        文檔

        vue底層事件邏輯解析

        vue底層事件邏輯解析:這次給大家帶來vue底層事件邏輯解析,使用vue底層事件的注意事項有哪些,下面就是實戰案例,一起來看一下。<p id="app"> <p id="test1" @click="click1">click1</p> &
        推薦度:
        導讀vue底層事件邏輯解析:這次給大家帶來vue底層事件邏輯解析,使用vue底層事件的注意事項有哪些,下面就是實戰案例,一起來看一下。<p id="app"> <p id="test1" @click="click1">click1</p> &

        這次給大家帶來vue底層事件邏輯解析,使用vue底層事件的注意事項有哪些,下面就是實戰案例,一起來看一下。

        <p id="app">
         <p id="test1" @click="click1">click1</p>
         <p id="test2" @click.stop="click2">click2</p>
         <my-component v-on:click.native="nativeclick" v-on:componenton="parentOn">
         </my-component>
        </p>
        </body>
        <script src="vue.js"></script>
        <script type="text/javascript">
        var Child = {
         template: '<p>A custom component!</p>'
        } 
        Vue.component('my-component', {
         name: 'my-component',
         template: '<p>A custom component!<p @click.stop="toParent">test click</p></p>',
         components: {
         Child:Child
         },
         created(){
         console.log(this);
         },
         methods: {
         toParent(){
         this.$emit('componenton','toParent')
         }
         },
         mounted(){
         console.log(this);
         }
        })
         new Vue({
         el: '#app',
         data: function () {
         return {
         heihei:{name:3333},
         a:1
         }
         },
         components: {
         Child:Child
         },
         methods: {
         click1(){
         alert('click1')
         },
         click2(){
         alert('click2')
         },
         nativeclick(){
         alert('nativeclick')
         },
         parentOn(value){
         alert(value)
         }
         }
        })
        </script>

        上面的demo中一共有四個事件。基本涵蓋了vue中最經典的事件的四種情況

        普通html元素上的事件

        好吧。想想我們還是一個個來看。如果懂vue組件相關的機制會更容易懂。那么首先我們看看最簡單的第一、二個(兩個事件只差了個修飾符):

        <p id="test1" @click="click1">click1</p>

        這是簡單到不能在簡單的一個點擊事件。

        我們來看看建立這么一個簡單的點擊事件,vue中發生了什么。

        1:new Vue()中調用了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);//調用了bind方法,我們再看看bind
         {
         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)//通過返回函數修飾了事件的回調函數。綁定了事件回調函數的this。并且讓參數自定義。更加的靈活
         : fn.call(ctx, a)
         : fn.call(ctx)
         }
         // record original fn length
         boundFn._length = fn.length;
         return boundFn
        }

        總的來說。vue初始化的時候,將method中的方法代理到vue[key]的同時修飾了事件的回調函數。綁定了作用域。

        2:vue進入compile環節需要將該p變成ast(抽象語法樹)。當編譯到該p時經過核心函數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//假如沒有修飾符。直接返回回調函數
         ? 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];//處理修飾符數組,例如.stop就在回調函數里加入event.stopPropagation()再返回。實現修飾的目的
         // 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函數簡單明了,如果事件函數有修飾符。就處理完修飾符,添加修飾符對應的函數語句。再返回。這個過程還會單獨對native修飾符做特殊處理。這個等會說。compile完后自然就render。我們看看render函數中這塊區域長什么樣子:

        代碼如下:


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

        一目了然。最后在虛擬dom-》真實dom的時候。會調用核心函數:

        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 }//此處綁定點擊事件
         : capture
         );
        }

        組件上的事件

        好了下面就是接下來的組件上的點擊事件了。可以預感到他走的和普通的html元素應該是不同的道路。事實也是如此:

        <my-component v-on:click.native="nativeclick" v-on:componenton="parentOn">
         </my-component>

        最簡單的一個例子。兩個事件的區別就是一個有.native的修飾符。我們來看看官方.native的作用:在原生dom上綁定事件。好吧。很簡單。我們跟隨源碼看看有何不同。這里可以往回看看我少的可憐的上一章組件機制。vue中的組件都是擴展的vue的一個新實例。在compile結束的時候你還是可以發現他也是類似的一個樣子。如下圖:

        代碼如下:

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

        可以看到加了.native修飾符的會被放入nativeOn的數組中。等待后續特殊處理。等不及了。我們直接來看看特殊處理。render函數在執行時。如果遇到組件。看過上一章的可以知道。會執行

        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的函數。這里就是componenton事件
         // replace with listeners with .native modifier
         data.on = data.nativeOn;//正常的data.on會被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
        }

        整段代碼關于事件核心操作:

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

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

        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);
         }
        }

        發現組件上的沒有.native的修飾符調用的是$on方法。這個好熟悉。進入到$on,$emit大致想到是一個典型的觀察者模式的事件。看看相關$on,$emit代碼。我加點注解:

        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);//當emit的時候調用該事件。注意上面說的vue在初始化的守候。用bind修飾了事件函數。所以組件上掛載的事件都是在父作用域中的
         }
         }
         return vm
         };

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

        var bus = new Vue()
        // 觸發組件 A 中的事件
        bus.$emit('id-selected', 1)
        // 在組件 B 創建的鉤子中監聽事件
        bus.$on('id-selected', function (id) {
         // ...
        })

        是不是豁然開朗。

        相信看了本文案例你已經掌握了方法,更多精彩請關注Gxl網其它相關文章!

        推薦閱讀:

        Vue做出分頁器

        怎樣使用nodeJS模塊

        聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

        文檔

        vue底層事件邏輯解析

        vue底層事件邏輯解析:這次給大家帶來vue底層事件邏輯解析,使用vue底層事件的注意事項有哪些,下面就是實戰案例,一起來看一下。<p id="app"> <p id="test1" @click="click1">click1</p> &
        推薦度:
        標簽: VUE 事件 解析
        • 熱門焦點

        最新推薦

        猜你喜歡

        熱門推薦

        專題
        Top
        主站蜘蛛池模板: 拔擦拔擦8x华人免费久久| 日韩精品免费电影| 亚洲精品久久久www| 亚洲电影中文字幕| 亚洲爆乳成av人在线视菜奈实| 叮咚影视在线观看免费完整版| 欧美大尺寸SUV免费| 国产成A人亚洲精V品无码| 精品久久久久久亚洲中文字幕| 日本在线免费观看| 亚洲精品国产va在线观看蜜芽| 免费无遮挡无遮羞在线看 | 亚洲精品在线视频观看| 免费福利在线观看| 亚洲日本一区二区三区在线不卡| 色老板亚洲视频免在线观| 久艹视频在线免费观看| 亚洲黄色在线观看视频| 国产一级高青免费| 国产性生交xxxxx免费| 国产成人精品亚洲日本在线 | 日本高清免费aaaaa大片视频| 久久亚洲色WWW成人欧美| 免费视频精品一区二区三区| 四虎永久免费地址在线网站| 亚洲国产成人综合| 在线观看人成视频免费无遮挡 | 国产亚洲精品欧洲在线观看| 大地资源在线观看免费高清| 亚洲AV无码专区在线观看成人| 久久亚洲国产成人影院网站 | 亚洲国产成人影院播放| 亚洲av日韩专区在线观看| 成人午夜18免费看| 91亚洲一区二区在线观看不卡| 精品国产免费人成电影在线观看 | 亚洲国产精品免费观看 | 亚洲色欲一区二区三区在线观看| 久久精品熟女亚洲av麻豆| 中文亚洲AV片不卡在线观看| a级毛片毛片免费观看永久|