<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 中directive功能的簡單實現

        來源:懂視網 責編:小采 時間:2020-11-27 22:22:17
        文檔

        vue 中directive功能的簡單實現

        vue 中directive功能的簡單實現:2018年首個計劃是學習vue源碼,查閱了一番資料之后,決定從第一個commit開始看起,這將是一場持久戰!本篇介紹directive的簡單實現,主要學習其實現的思路及代碼的設計(directive和filter擴展起來非常方便,符合設計模式中的 開閉原則 )。 構思API
        推薦度:
        導讀vue 中directive功能的簡單實現:2018年首個計劃是學習vue源碼,查閱了一番資料之后,決定從第一個commit開始看起,這將是一場持久戰!本篇介紹directive的簡單實現,主要學習其實現的思路及代碼的設計(directive和filter擴展起來非常方便,符合設計模式中的 開閉原則 )。 構思API

        2018年首個計劃是學習vue源碼,查閱了一番資料之后,決定從第一個commit開始看起,這將是一場持久戰!本篇介紹directive的簡單實現,主要學習其實現的思路及代碼的設計(directive和filter擴展起來非常方便,符合設計模式中的 開閉原則 )。

        構思API

        <div id="app" sd-on-click="toggle | .button">
         <p sd-text="msg | capitalize"></p>
         <p sd-class-red="error" sd-text="content"></p>
         <button class="button">Toggle class</button>
        </div>
        var app = Seed.create({
         id: 'app',
         scope: {
         msg: 'hello',
         content: 'world',
         error: true,
         toggle: function() {
         app.scope.error = !app.scope.error;
         }
         }
        });

        實現功能夠簡單吧--將scope中的數據綁定到app中。

        核心邏輯設計

        指令格式

        以 sd-text="msg | capitalize" 為例說明:

        1. sd 為統一的前綴標識
        2. text 為指令名稱
        3. capitalize 為過濾器名稱

        其中 | 后面為過濾器,可以添加多個。 sd-class-red 中的red為參數。

        代碼結構介紹

        main.js 入口文件

        // Seed構造函數
        const Seed = function(opts) {
        };
        // 對外暴露的API
        module.exports = {
         create: function(opts) {
         return new Seed(opts);
         }
        };
        directives.js
        module.exports = {
         text: function(el, value) {
         el.textContent = value || '';
         }
        };
        filters.js
        module.exports = {
         capitalize: function(value) {
         value = value.toString();
         return value.charAt(0).toUpperCase() + value.slice(1);
         }
        };

        就這三個文件,其中directives和filters都是配置文件,很易于擴展。

        實現的大致思路如下:

        1.在Seed實例創建的時候會依次解析el容器中node節點的指令

        2.將指令解析結果封裝為指令對象,結構為:

        屬性 說明 類型
        attr 原始屬性,如 sd-text String
        key 對應scope對象中的屬性名稱 String
        filters 過濾器名稱列表 Array
        definition 該指令的定義,如text對應的函數 Function
        argument 從attr中解析出來的參數(只支持一個參數) String
        update 更新directive時調用 typeof def === 'function' ? def : def.update Function
        bind 如果directive中定義了bind方法,則在 bindDirective 中會調用 Function
        el 存儲當前element元素 Element

        3.想辦法執行指令的update方法即可,該插件使用了 Object.defineProperty 來定義scope中的每個屬性,在其setter中觸發指令的update方法。

        核心代碼

        const prefix = 'sd';
        const Directives = require('./directives');
        const Filters = require('./filters');
        // 
        結果為[sd-text], [sd-class], [sd-on]的字符串 const selector = Object.keys(Directives).map((name) => `[${prefix}-${name}]`).join(','); const Seed = function(opts) { const self = this, root = this.el = document.getElementById(opts.id), // 篩選出el下所能支持的directive的nodes列表 els = this.el.querySelectorAll(selector), bindings = {}; this.scope = {}; // 解析節點 [].forEach.call(els, processNode); // 解析根節點 processNode(root); // 給scope賦值,觸發setter方法,此時會調用與其相對應的directive的update方法 Object.keys(bindings).forEach((key) => { this.scope[key] = opts.scope[key]; }); function processNode(el) { cloneAttributes(el.attributes).forEach((attr) => { const directive = parseDirective(attr); if (directive) { bindDirective(self, el, bindings, directive); } }); } };

        可以看到核心方法 processNode 主要做了兩件事一個是 parseDirective ,另一個是 bindDirective 。

        先來看看 parseDirective 方法:

        function parseDirective(attr) {
         if (attr.name.indexOf(prefix) == -1) return;
         // 解析屬性名稱獲取directive
         const noprefix = attr.name.slice(prefix.length + 1),
         argIndex = noprefix.indexOf('-'),
         dirname = argIndex === -1 ? noprefix : noprefix.slice(0, argIndex),
         arg = argIndex === -1 ? null : noprefix.slice(argIndex + 1),
         def = Directives[dirname]
         // 解析屬性值獲取filters
         const exp = attr.value,
         pipeIndex = exp.indexOf('|'),
         key = (pipeIndex === -1 ? exp : exp.slice(0, pipeIndex)).trim(),
         filters = pipeIndex === -1 ? null : exp.slice(pipeIndex + 1).split('|').map((filterName) => filterName.trim());
         return def ? {
         attr: attr,
         key: key,
         filters: filters,
         argument: arg,
         definition: Directives[dirname],
         update: typeof def === 'function' ? def : def.update
         } : null;
        }

        以 sd-on-click="toggle | .button" 為例來說明,其中attr對象的name為 sd-on-click ,value為 toggle | .button ,最終解析結果為:

        {
         "attr": attr,
         "key": "toggle",
         "filters": [".button"],
         "argument": "click",
         "definition": {"on": {}},
         "update": function(){}
        }

        緊接著調用 bindDirective 方法

        /**
         * 數據綁定
         * @param {Seed} seed Seed實例對象
         * @param {Element} el 當前node節點
         * @param {Object} bindings 數據綁定存儲對象
         * @param {Object} directive 指令解析結果
         */
        function bindDirective(seed, el, bindings, directive) {
         // 移除指令屬性
         el.removeAttribute(directive.attr.name);
         // 數據屬性
         const key = directive.key;
         let binding = bindings[key];
         if (!binding) {
         bindings[key] = binding = {
         value: undefined,
         directives: [] // 與該數據相關的指令
         };
         }
         directive.el = el;
         binding.directives.push(directive);
         if (!seed.scope.hasOwnProperty(key)) {
         bindAccessors(seed, key, binding);
         }
        }
        /**
         * 重寫scope西鄉屬性的getter和setter
         * @param {Seed} seed Seed實例
         * @param {String} key 對象屬性即opts.scope中的屬性
         * @param {Object} binding 數據綁定關系對象
         */
        function bindAccessors(seed, key, binding) {
         Object.defineProperty(seed.scope, key, {
         get: function() {
         return binding.value;
         },
         set: function(value) {
         binding.value = value;
         // 觸發directive
         binding.directives.forEach((directive) => {
         // 如果有過濾器則先執行過濾器
         if (typeof value !== 'undefined' && directive.filters) {
         value = applyFilters(value, directive);
         }
         // 調用update方法
         directive.update(directive.el, value, directive.argument, directive);
         });
         }
         });
        }
        /**
         * 調用filters依次處理value
         * @param {任意類型} value 數據值
         * @param {Object} directive 解析出來的指令對象
         */
        function applyFilters(value, directive) {
         if (directive.definition.customFilter) {
         return directive.definition.customFilter(value, directive.filters);
         } else {
         directive.filters.forEach((name) => {
         if (Filters[name]) {
         value = Filters[name](value);
         }
         });
         return value;
         }
        }

        其中的bindings存放了數據和指令的關系,該對象中的key為opts.scope中的屬性,value為Object,如下:

        {
         "msg": {
         "value": undefined,
         "directives": [] // 上面介紹的directive對象
         }
        }

        數據與directive建立好關系之后, bindAccessors 中為seed的scope對象的屬性重新定義了getter和setter,其中setter會調用指令update方法,到此就已經完事具備了。

        Seed構造函數在實例化的最后會迭代bindings中的key,然后從opts.scope找到對應的value, 賦值給了scope對象,此時setter中的update就觸發執行了。

        下面再看一下 sd-on 指令的定義:

        {
         on: {
         update: function(el, handler, event, directive) {
         if (!directive.handlers) {
         directive.handlers = {};
         }
         const handlers = directive.handlers;
         if (handlers[event]) {
         el.removeEventListener(event, handlers[event]);
         }
         if (handler) {
         handler = handler.bind(el);
         el.addEventListener(event, handler);
         handlers[event] = handler;
         }
         },
         customFilter: function(handler, selectors) {
         return function(e) {
         const match = selectors.every((selector) => e.target.matches(selector));
         if (match) {
         handler.apply(this, arguments);
         }
         }
         }
         }
        }

        發現它有customFilter,其實在 applyFilters 中就是針對該指令做的一個單獨的判斷,其中的selectors就是[".button"],最終返回一個匿名函數(事件監聽函數),該匿名函數當做value傳遞給update方法,被其handler接收,update方法處理的是事件的綁定。這里其實實現的是事件的代理功能,customFilter中將handler包裝一層作為事件的監聽函數,同時還實現事件代理功能,設計的比較巧妙!

        總結

        以上所述是小編給大家介紹的vue 中directive的簡單實現,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網站的支持!

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

        文檔

        vue 中directive功能的簡單實現

        vue 中directive功能的簡單實現:2018年首個計劃是學習vue源碼,查閱了一番資料之后,決定從第一個commit開始看起,這將是一場持久戰!本篇介紹directive的簡單實現,主要學習其實現的思路及代碼的設計(directive和filter擴展起來非常方便,符合設計模式中的 開閉原則 )。 構思API
        推薦度:
        標簽: 功能 VUE 簡單
        • 熱門焦點

        最新推薦

        猜你喜歡

        熱門推薦

        專題
        Top
        主站蜘蛛池模板: 免费无码又爽又黄又刺激网站| 亚洲国产综合人成综合网站00| 亚洲成av人无码亚洲成av人| 天天影视色香欲综合免费| 亚洲精品自产拍在线观看动漫| 三上悠亚电影全集免费| 亚洲欧洲美洲无码精品VA| 久久国产精品国产自线拍免费| 亚洲精品无码不卡在线播HE| 波多野结衣免费一区视频| 亚洲va中文字幕无码久久不卡| 精品亚洲永久免费精品| 97亚洲熟妇自偷自拍另类图片| 久久久99精品免费观看| 亚洲成人免费网址| 日韩在线免费播放| 黄网站色视频免费在线观看的a站最新 | 日本视频一区在线观看免费| 亚洲国产视频网站| 永久免费毛片手机版在线看| 国产成人亚洲午夜电影| 国产AV无码专区亚洲AV手机麻豆| 中文字幕手机在线免费看电影 | 亚洲VA成无码人在线观看天堂| 久久精品人成免费| 亚洲精品无码久久久久久| 亚洲 另类 无码 在线| a毛片免费在线观看| 亚洲国产成人精品无码一区二区| 女性自慰aⅴ片高清免费| 最好2018中文免费视频| 婷婷精品国产亚洲AV麻豆不片 | 国产美女被遭强高潮免费网站| j8又粗又长又硬又爽免费视频| 国产av无码专区亚洲av桃花庵| 成人无码区免费A片视频WWW| 国产亚洲综合视频| 亚洲综合日韩中文字幕v在线| 好吊妞788免费视频播放| a级毛片免费在线观看| 中文字幕亚洲综合久久综合|