與你探索classnames模塊內部實現

前言

在寫react的時候動態寫入class比較繁瑣很囉嗦,記得有個模塊能夠來解決這個問題,因而乎找到了這個模塊叫「classnames」,再者好奇這個模塊的體積大小,而後對其進行了源碼分析。node

classnames - githubreact

看看react如何動態添加class

若是不用第三方模塊去實現動態添加class是這樣子的git

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

這算比較簡單的了,若是條件夠多的時候是很可怕的, 想一想就行了!!github

使用classnames動態添加class

npm安裝npm

npm install classnames -S
複製代碼

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

const sidebarOpenClass = classNames({
  main: true,
  active: window.people
});
return (
  <div className=${_classNames}></div>
)
複製代碼

切入正題好嗎

好吧。瀏覽器

分析classnames內部實現

代碼並很少,刪掉空白行和註釋只有僅僅37行的代碼,我將在代碼裏以註釋的形式講解代碼含義。bash

(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;
  }
}());
複製代碼

代碼比較少,因此比較好分析。app

社會社會

classnames使用姿式

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

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%,因此放心使用。

原文連接

相關文章
相關標籤/搜索