與你探索classnames模組內部實現

印度大使發表於2019-03-04

前言

在寫react的時候動態寫入class比較繁瑣很囉嗦,記得有個模組可以來解決這個問題,於是乎找到了這個模組叫“classnames”,再者好奇這個模組的體積大小,然後對其進行了原始碼分析。

classnames – github

看看react如何動態新增class

如果不用第三方模組去實現動態新增class是這樣子的

const _classNames = window.people ? `active main` : `main`;
return (
  <div className={_classNames}></div>
)
複製程式碼

這算比較簡單的了,如果條件夠多的時候是很可怕的, 想想就好了!!

使用classnames動態新增class

npm安裝

npm install classnames -S
複製程式碼

看!相比前面簡單明瞭,結構再複雜一樣能應付

const sidebarOpenClass = classNames({
  main: true,
  active: window.people
});
return (
  <div className=${_classNames}></div>
)
複製程式碼
切入正題好嗎

好吧。

分析classnames內部實現

程式碼並不多,刪掉空白行和註釋只有僅僅37行的程式碼,我將在程式碼裡以註釋的形式講解程式碼含義。

(function () {
  `use strict`;
  // =>  Object.hasOwnProperty   用於判斷某個成員是否在物件內
  var hasOwn = {}.hasOwnProperty;
  function classNames () {
     // 儲存 className 值
    var classes = [];

    // 迴圈實參, arguments就是實際呼叫函式傳入的引數,類似陣列
    for (var i = 0; i < arguments.length; i++) {
      // 獲取實參value
      var arg = arguments[i];

      // 跳過false條件 => false, null, undefined, NaN, 空, ...
      if (!arg) continue;

      // 判斷傳入引數的型別
      var argType = typeof arg;

      // 如果引數的型別是 string 或者 number
      if (argType === `string` || argType === `number`) {
        // 直接追加到classes陣列後面
        classes.push(arg);

      // 如果引數是陣列並且長度大於0
      } else if (Array.isArray(arg) && arg.length) {
        // 呼叫自身函式,利用apply可以將陣列轉成字串
        var inner = classNames.apply(null, arg);

        // 現在是一個字串,隱士判斷布林值
        if (inner) {
          // 追加到陣列後面
          classes.push(inner);
        }
      // 如果傳入的引數是物件
      } else if (argType === `object`) {
        // 對object進行遍歷
        for (var key in arg) {
          // 判斷key是否存在arg物件內並且key的值隱士轉換為true
          if (hasOwn.call(arg, key) && arg[key]) {
            // 將值追加到classes陣列後面
            classes.push(key);
          }
        }
      }
    }
    // 將陣列連線成字串以空格拼接  => a b c
    return classes.join(` `);
  }

  // 如果是node.js環境執行
  if (typeof module !== `undefined` && module.exports) {
    classNames.default = classNames;
    module.exports = classNames;

  // 如果用的requirejs模組管理 AMD
  } else if (typeof define === `function` && typeof define.amd === `object` && define.amd) {
    define(`classnames`, [], function () {
    return classNames;
  });

  // 否則執行於瀏覽器環境
  } else {
    window.classNames = classNames;
  }
}());
複製程式碼

程式碼比較少,所以比較好分析。

社會社會

classnames使用姿勢

classnames使用太靈活了,演示幾個demo

demox1

import classNames from `classnames`;
const _className = classNames(`foo`);  // => `foo`
複製程式碼

demox2

const _className = classNames(`foo`, {
  bar: true
});
// => `foo bar`
複製程式碼

demox3

const _className = classNames(`foo`, {
  bar: true,
  active: false,
}, [`arr-1`, `arr-2`]);
// => foo bar arr-1 arr-2
複製程式碼

classNames對實參是沒有限制的,看原始碼就明白了,比較常用的就是react專案當中。

總結

既然有了第三方外掛的存在,就沒必要重複造輪子了,練習和學習原始碼是提高技術的好機會,classnames編寫了單元測試,覆蓋率基本是100%,所以放心使用。

原文連結

相關文章