Webpack less-loader

2023-05-22 09:18 更新

webpack 將 Less 編譯為 CSS 的 loader。

快速開始

首先,你需要先安裝 less 和 less-loader:

$ npm install less less-loader --save-dev

然后將該 loader 添加到 webpack 的配置中去,例如:

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.less$/i,
        use: [
          // compiles Less to CSS
          'style-loader',
          'css-loader',
          'less-loader',
        ],
      },
    ],
  },
};

接著使用你習(xí)慣的方式運行 webpack。

可選項

名稱 類型 默認(rèn)值 描述
lessOptions {Object|Function} { relativeUrls: true } Less 的可選項。
additionalData {String|Function} undefined 在入口文件起始或末尾添加 Less 代碼。
sourceMap {Boolean} compiler.devtool 是否生成 source map。
webpackImporter {Boolean} true 是否啟用默認(rèn)的 webpack importer。
implementation {Object|String} less 配置 Less 使用的實現(xiàn)庫

lessOptions

類型: ?Object|Function? 默認(rèn)值: { relativeUrls: true }

通過 lessOptions 屬性,你可以設(shè)置 loader options 中的任意特定的選項值給 less-loader。所有可用的選項值請參看 Less 命令行可選參數(shù)文檔。由于我們是通過編程的方式將這些選項傳遞給 Less,因此您需要將破折號(dash-case)轉(zhuǎn)換為駝峰值(camelCase)后傳遞它們。

Object

使用對象(Object)的形式傳遞 options 給 Less。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.less$/i,
        use: [
          {
            loader: 'style-loader',
          },
          {
            loader: 'css-loader',
          },
          {
            loader: 'less-loader',
            options: {
              lessOptions: {
                strictMath: true,
              },
            },
          },
        ],
      },
    ],
  },
};

Function

允許根據(jù) loader 的 context 來設(shè)置 options,再傳遞給 Less。

module.exports = {
  module: {
    rules: [
      {
        test: /\.less$/i,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'less-loader',
            options: {
              lessOptions: (loaderContext) => {
                // 更多可用的屬性見 https://webpack.js.org/api/loaders/
                const { resourcePath, rootContext } = loaderContext;
                const relativePath = path.relative(rootContext, resourcePath);

                if (relativePath === 'styles/foo.less') {
                  return {
                    paths: ['absolute/path/c', 'absolute/path/d'],
                  };
                }

                return {
                  paths: ['absolute/path/a', 'absolute/path/b'],
                };
              },
            },
          },
        ],
      },
    ],
  },
};

additionalData

類型: ?String|Function? 默認(rèn)值: undefined

在實際入口文件的起始位置添加 Less 代碼。 這種情況下,less-loader 只會追加并不會覆蓋文件內(nèi)容。

當(dāng)你的 Less 變量依賴環(huán)境變量時這個屬性將非常有用:

? 由于你注入了代碼,因此它將破壞入口文件的源映射關(guān)系。通常有比這更簡單的解決方案,例如多個 Less 入口文件。

String

module.exports = {
  module: {
    rules: [
      {
        test: /\.less$/i,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'less-loader',
            options: {
              additionalData: `@env: ${process.env.NODE_ENV};`,
            },
          },
        ],
      },
    ],
  },
};

Function

Sync
module.exports = {
  module: {
    rules: [
      {
        test: /\.less$/i,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'less-loader',
            options: {
              additionalData: (content, loaderContext) => {
                // 更多可用的屬性見 https://webpack.js.org/api/loaders/
                const { resourcePath, rootContext } = loaderContext;
                const relativePath = path.relative(rootContext, resourcePath);

                if (relativePath === 'styles/foo.less') {
                  return '@value: 100px;' + content;
                }

                return '@value: 200px;' + content;
              },
            },
          },
        ],
      },
    ],
  },
};
Async
module.exports = {
  module: {
    rules: [
      {
        test: /\.less$/i,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'less-loader',
            options: {
              additionalData: async (content, loaderContext) => {
                // More information about available properties https://webpack.js.org/api/loaders/
                const { resourcePath, rootContext } = loaderContext;
                const relativePath = path.relative(rootContext, resourcePath);

                if (relativePath === 'styles/foo.less') {
                  return '@value: 100px;' + content;
                }

                return '@value: 200px;' + content;
              },
            },
          },
        ],
      },
    ],
  },
};

sourceMap

類型: ?Boolean? 默認(rèn)值: 取決于 compiler.devtool 的值

默認(rèn)生成的 source map 取決于 compiler.devtool 的值。除了值等于 eval 和 false 外,其他值都能生成 source map

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.less$/i,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              sourceMap: true,
            },
          },
          {
            loader: 'less-loader',
            options: {
              sourceMap: true,
            },
          },
        ],
      },
    ],
  },
};

webpackImporter

類型:?Boolean? 默認(rèn)值:true

啟用/禁用 webpack 默認(rèn)的 importer。

在某些情況下,這樣做可以提高性能,但是請慎用,因為可能會使得 aliases 和以 ~ 開頭的 @import 規(guī)則失效。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.less$/i,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'less-loader',
            options: {
              webpackImporter: false,
            },
          },
        ],
      },
    ],
  },
};

implementation

類型:?Object | String?

? less-loader 已兼容 Less 3 和 Less 4。

特殊的 implementation 選項決定使用 Less 的哪個實現(xiàn)。重載本地安裝的 less 的 peerDependency 版本。

此選項只對下游的工具作者有效,以便于 Less 3 到 Less 4 的過渡。

Object

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.less$/i,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'less-loader',
            options: {
              implementation: require('less'),
            },
          },
        ],
      },
    ],
  },
};

String

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.less$/i,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'less-loader',
            options: {
              implementation: require.resolve('less'),
            },
          },
        ],
      },
    ],
  },
};

示例

常規(guī)用法

將 less-loader、css-loader 和 style-loader 串聯(lián)起來使用可立即將所有樣式應(yīng)用于 DOM。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.less$/i,
        use: [
          {
            loader: 'style-loader', // 從 JS 中創(chuàng)建樣式節(jié)點
          },
          {
            loader: 'css-loader', // 轉(zhuǎn)化 CSS 為 CommonJS
          },
          {
            loader: 'less-loader', // 編譯 Less 為 CSS
          },
        ],
      },
    ],
  },
};

不幸的是,Less 并沒有將所有選項 1 對 1 映射為 camelCase(駝峰值)。如有疑問,請檢查執(zhí)行文件并搜索破折號選項。

Source maps

為了生成 CSS 的 source map, 你需要在 loader 的可選項中設(shè)置 sourceMap 屬性。如果沒設(shè)置的話 loader 將會繼承你 webpack 中為生成 source map 設(shè)置的屬性值 devtool。

webpack.config.js

module.exports = {
  devtool: 'source-map', // 任何類似于 "source-map" 的  devtool 值都可以
  module: {
    rules: [
      {
        test: /\.less$/i,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              sourceMap: true,
            },
          },
          {
            loader: 'less-loader',
            options: {
              sourceMap: true,
            },
          },
        ],
      },
    ],
  },
};

如果你想在 Chrome 中修改原始的 Less 文件,可以參考這篇不錯的博客。這篇博客雖然寫的 Sass,但也適合于 Less。

生產(chǎn)環(huán)境

在生產(chǎn)環(huán)境中推薦使用 MiniCssExtractPlugin 來提取樣式表到專門的文件中,這樣你的樣式就不需要依賴 JavaScript。

導(dǎo)入

首先我們會嘗試使用內(nèi)置 less 解析邏輯,然后再使用 webpack 解析邏輯(alias 和 ~)。

webpack 解析器

webpack 提供了一種 解析文件的高級機制。如果 less 不能解析 @import 的話,less-loader 作為 Less 的插件將所有的查詢結(jié)果傳遞給 webpack 解析器。因此你可以從 node_modules 中導(dǎo)入 Less 模塊。

@import "bootstrap/less/bootstrap";

~ 用法已被廢棄,可以從代碼中刪除(我們建議這么做),但是我們會因為一些歷史原因一直支持這種寫法。 為什么你可以移除它呢?loader 首先會嘗試以相對路徑解析 @import,如果它不能被解析,loader 將會嘗試在 node_modules 中解析 @import。 只要在包名前加上 ~,告訴 webpack 在 modules 中進行查找。

@import "~bootstrap/less/bootstrap";

可以通過 resolve.byDependency 修改默認(rèn)解析器配置:

webpack.config.js

module.exports = {
  devtool: 'source-map', // "source-map" 類的 devtool 都是可以的
  module: {
    rules: [
      {
        test: /\.less$/i,
        use: ['style-loader', 'css-loader', 'less-loader'],
      },
    ],
  },
  resolve: {
    byDependency: {
      // 更多的配置項可以在這里找到 https://webpack.js.org/configuration/resolve/
      less: {
        mainFiles: ['custom'],
      },
    },
  },
};

在其前面加上 ? 很關(guān)鍵,因為 ?/ 會解析到根目錄。webpack 需要區(qū)分 bootstrap 和 ?bootstrap,因為 CSS 和 Less 文件沒有用于導(dǎo)入相對路徑文件的特殊語法。寫 @import“ file” 等同于 @import“ ./file”;

Less Resolver

如果指定 paths 選項,將從指定的 paths 中搜索模塊,這是 less 的默認(rèn)行為。paths 應(yīng)該是具有絕對路徑的數(shù)組:

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.less$/i,
        use: [
          {
            loader: 'style-loader',
          },
          {
            loader: 'css-loader',
          },
          {
            loader: 'less-loader',
            options: {
              lessOptions: {
                paths: [path.resolve(__dirname, 'node_modules')],
              },
            },
          },
        ],
      },
    ],
  },
};

插件

想要使用 插件,只需要簡單設(shè)置下 plugins 選項即可, 具體配置如下:

webpack.config.js

const CleanCSSPlugin = require('less-plugin-clean-css');

module.exports = {
  ...
    {
      loader: 'less-loader',
      options: {
        lessOptions: {
          plugins: [
            new CleanCSSPlugin({ advanced: true }),
          ],
        },
      },
    },
  ...
};
?? 使用 pluginManager.webpackLoaderContext 屬性可以訪問自定義插件中的 loader context。
module.exports = {
  install: function (less, pluginManager, functions) {
    functions.add('pi', function () {
      // Loader context is available in `pluginManager.webpackLoaderContext`

      return Math.PI;
    });
  },
};

提取樣式

通過 webpack 打包 CSS 有很多好處,比如給引用圖片和字體文件路徑添加 hash, 在開發(fā)環(huán)境可以 模塊熱替換。另一方面,在生產(chǎn)環(huán)境,根據(jù) JS 來控制應(yīng)用樣式表不是一個好的方式,可能會導(dǎo)致延遲渲染,甚至可能會出現(xiàn)閃爍現(xiàn)象。因此,在你最終的生產(chǎn)環(huán)境中將它們拆分成單獨的文件來存放通常是比較好的選擇。

有兩種從 bundle 中提取樣式表的方式:

  • ?extract-loader? (簡單,但得專門指定 css-loader 的 output)
  • ?MiniCssExtractPlugin? (較復(fù)雜,但適用于所有的場景)

CSS modules 陷阱

Less 和 CSS modules 有一個已知的問題,關(guān)于 url(...) 語句中的相對文件路徑,看下這個問題的解釋。

貢獻

如果你還沒有看過我們的貢獻者指南請先花點時間看一下。

許可

MIT


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號