亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

vue中怎么實現directive功能

發布時間:2021-07-09 15:09:38 來源:億速云 閱讀:143 作者:小新 欄目:web開發

這篇文章主要介紹了vue中怎么實現directive功能,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

構思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-textString
key對應scope對象中的屬性名稱String
filters過濾器名稱列表Array
definition該指令的定義,如text對應的函數Function
argument從attr中解析出來的參數(只支持一個參數)String
update更新directive時調用 typeof def === 'function' ? def : def.updateFunction
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功能”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

靖远县| 海盐县| 绿春县| 衡山县| 正安县| 松江区| 北票市| 和田县| 宝坻区| 北流市| 正安县| 武胜县| 改则县| 革吉县| 太原市| 长阳| 凯里市| 铜梁县| 广丰县| 泗水县| 宜黄县| 绿春县| 澄迈县| 荆门市| 常熟市| 吴旗县| 高阳县| 三亚市| 陈巴尔虎旗| 苍南县| 宝鸡市| 正定县| 内乡县| 贡嘎县| 山西省| 青田县| 高青县| 自贡市| 安岳县| 潞西市| 巴中市|