Webpack Loader原始碼導讀之less-loader

hihl發表於2018-01-17

原文地址:hiihl.com/articles/20…

本篇是Webpack Loader原始碼導讀系列中關於less-loader的解讀,主要闡述loader的工作,less編譯部份的內容未來將單獨講解。

原始碼結構

原始碼 v4.0.5,src目錄如下:

src
|____cjs.js
|____createWebpackLessPlugin.js
|____formatLessError.js
|____getOptions.js
|____index.js
|____processResult.js
|____removeSourceMappingUrl.js
|____stringifyLoader.js
複製程式碼

options說明

進入index.js首先看到的是getOptions(loaderContext),這一步的作用是將webpack相關配置及loader的query或options部份配置合併,得到編譯過程的可選項

  • paths 模組解析的路徑,預設使用webpack的resolver,必須是個絕對路徑陣列;舉例,在less中我們想引用node_modules下bootstrap的less檔案,可以這樣寫 @import "~bootstrap/less/bootstrap";,預設的模組解析將和webpack一致,但如果loader配置paths,則webpack的解析路徑和alias配置在這裡將無效。
  • plugins 陣列,編譯時使用的外掛,已有的外掛見plugins
  • sourceMap 原始碼對映,需要同css-loader一同配置;可以是boolean型別,也可以是物件,sourceMap相關配置如下:
    • {String} sourceMapFilename,對應lessc中屬性值為String的--source-map選項;
    • {String} sourceMapRootPath,對應lessc的--source-map-rootpath選項;
    • {String} sourceMapURL,對應lessc的--source-map-url選項;
    • {String} sourceMapBasePath,對應lessc的--source-map-basepath選項;
    • {Boolean} sourceMapLessInline 對應lessc的--source-map-less-inline選項;
    • {Boolean} sourceMapMapInline 對應lessc的--source-map-map-inline選項;
    • {Boolean} outputSourceFiles 對應lessc的--source-map-map-inline選項;注意在less-loader中會將該值預設設定成true

less-loader其他的配置,都可以從less的配置選項中找到,全部轉成駝峰式即可。

有兩個重要的配置globalVarsmodifyVars,這兩個是用於新增或修改less變數的,一起看個例子。

main.less

@import "./other.less";

.box:extend(.hotpink) {
  width: @boxWidth;
  height: @boxHeight;
}
複製程式碼

other.less

@boxHeight: 10px;

.hotpink {
  background: hotpink;
  width: @boxWidth;
}
複製程式碼

上面的例子中,other.less中定義了變數@boxHeight在main.less中會使用到,值為10px;main.less和other.less中都使用到了@boxWidth,但是並沒有定義; 現在我們在webpack.config.js中配置

{
  loader: "less-loader",
  query: {
    sourceMap: true,
    globalVars: {
      "boxWidth": '200px'
    },
    modifyVars: {
      "boxHeight": '200px'
    }
  }
}
複製程式碼

最後編譯沒有出錯,結果為

.hotpink,
.box {
  background: hotpink;
  width: 200px;
}
.box {
  width: 200px;
  height: 200px;
}
複製程式碼

在這個例子中,globalVars的作用相當於給每個less檔案頂部增加一行@boxWidth: 200px,所以編譯出來的width都為200px,而modifyVars的作用相當於在 每個檔案底部增加一行@boxHeight: 200px,這樣就會覆蓋已有的@boxHeight: 10px,所以最後編譯出來的height是200px而不是10px;

這有什麼作用呢? 有了這兩個配置項,我們就可以把部份樣式抽出變數,通過不同的變數組合成不同的主題,例如: default.less

@primary-color: #003cee;
複製程式碼

themes/pink.js

module.exports = {
  "@primary-color": 'pink'
};
複製程式碼

webpack.config.js

{
  loader: 'less-loader',
  query: {
    modifyVars: require('./themes/pink')
  }
}
複製程式碼

loader中解析了配置以後,就直接呼叫了less的render方法進行編譯,render方法有三個入參var render = function (input, options, callback), 第三個引數是個callback,看render.js原始碼可以知道,如果不傳callback,則render方法會返回一個promise。

編譯結果處理

看下processResult.js如何處理編譯結果,resultPromise就是前面提到的render返回的promise。

function processResult(loaderContext, resultPromise) {
  const { callback } = loaderContext;

  resultPromise
    .then(({ css, map, imports }) => {
      imports.forEach(loaderContext.addDependency, loaderContext);
      return {
        // Removing the sourceMappingURL comment.
        // See removeSourceMappingUrl.js for the reasoning behind this.
        css: removeSourceMappingUrl(css),
        map: typeof map === 'string' ? JSON.parse(map) : map,
      };
    }, (lessError) => {
      throw formatLessError(lessError);
    })
    .then(({ css, map }) => {
      callback(null, css, map);
    }, callback);
}
複製程式碼

編譯後的結果包括css,map和imports三個,css是less編譯成的css內容,map則是sourceMap相關資訊,imports是編譯過程中所有的依賴檔案路徑。 拿到編譯結果後,首先是呼叫addDependency把所有imports中的檔案新增到依賴裡面,這個方法的作用時在watch模式時,依賴的這些檔案變化時會出發編譯更新。 然後是removeSourceMappingUrl(css),這個方法的作用是移除結果中的sourceMappingURL=,理由是less-loader無法知道最終的sourceMap會在哪裡。 最後呼叫callback(null, css, map)把結果傳給下一個loader去執行,less-loader的的工作就完成了。

下一篇我們將繼續將css-loader,如何繼續處理less-loader編譯出來的結果。

相關文章