【webpack】webpack構建流程筆記(三)

前言

  • 第一篇tapable,第二篇ast,第三篇loader

webpack構建流程

  • 從配置文件和命令行獲取參數
  • 創建Complier對象
  • 執行Compiler的run方法創建Compliation
  • 尋找入口文件,調用所有配置的loader對所有模塊進行編譯。
  • 經loader編譯完後得到每個模塊最終內容及依賴關係。
  • 根據入口和模塊依賴關係,組裝包含多模塊的chunk,把每個chunk轉換成單獨文件加入輸出列表。這步是修改輸出內容的最後機會。

寫個loader插件

  • 首先是webpack配置,一般可以通過配置resolveLoader來定位自己loader的位置:
  resolveLoader: {
            modules: [path.resolve(__dirname, 'loaders'), 'node_modules']
 },
  • 然後在配置目錄下新建2個js文件。
function myloader(source) {
    return source + '//1111'
}

module.exports = myloader
function myloader2(soucrce) {
    return soucrce + '//222'
}

module.exports = myloader2
  • 在入口文件裏寫個console.log('yehuozhili')
  • rules對其進行配置:
   rules: [{
                test: /\.js$/,
                use: [{
                    loader: 'myloader'
                }, {
                    loader: 'myloader2'
                }],
                exclude: /node_modules/,
  • 最後啓用webpack進行編譯。得到輸出結果:
eval("console.log('yehuozhili')//222//1111\n\n//# sourceURL=webpack:///./src/index.js?");
  • 可以看見是從下到上走的。**所以第一個loader返回的內容,必須是js。**中間loader返回啥不用管。
  • 除了配置webpack目錄,想使用自定義Loader還可以直接在loader上配置絕對路徑或者使用npm link連接自定義loader。

loaderAPI

緩存功能

  • 通過調用this.cacheable來使得loader是否有緩存,默認true。
function myloader(source) {
    this.cacheable(true)
    return source + '//1111'
}
  • 簡單說就是webpack多次編譯時文件已經走過這個loader就不會走了,只有修改過的文件纔會走。

異步功能

  • 有些時候,寫的loader需要異步操作才能取得返回值,比如讀一個文件,這時就得用異步api。
let fs = require('fs')
const path = require('path')

function myloader2(source) {
    let cb = this.async()
    fs.readFile(path.resolve(__dirname, 'yehuo.txt'), 'utf8', (err, content) => {
        cb(err, source + content)
    })
    return
}

module.exports = myloader2
  • 異步api不會影響調用順序,反而更能提高速度。

拿到二進制文件

  • webpack默認是把代碼轉成utf-8格式讓你在loader裏拿到。可以設置raw使得拿到二進制文件,這個對處理圖片等文件很有用。
function myloader2(source) {
    console.log(source)
    return source
}
myloader2.raw = true

module.exports = myloader2

拿到配置項

  • 很多時候,loader會有配置選項,可以使用loader-utils拿到配置選項:
let loaderUtils = require('loader-utils')
function myloader2(source) {
    let options = loaderUtils.getOptions(this)
    console.log(options)
    return source
}
module.exports = myloader2
  • 配置:
{
  loader: 'myloader2',
	 options: {
	           msg: '一個msg配置'
	    }
}

實現babel-loader

  • 實現babel-loader並沒有想的那麼複雜,複雜功能babel都做好了,只要調就行。
const babel = require('@babel/core')

function myloader(source) {
    let options = {
        presets: [["@babel/preset-env"]],
        sourceMap: true,
        filename: this.resourcePath.split('\\').pop()
    }
    console.log(options)
    let { code, map, ast } = babel.transform(source, options)
    return this.callback(null, code, map, ast)

}

module.exports = myloader
  • callback和上面的async其實一個玩意,除了轉換後的代碼,還可以傳map和ast,因爲webpack本來就要生成ast,傳了後就用生成的。map是用來放sourcemap調試用的。options裏的filename就是編譯前的文件名,會根據映射找編譯前的代碼。這個options是babel的配置項。this.resourcePath是動態獲取路徑。
  • 這樣就完成了轉換。可以寫一段帶箭頭函數的,只用我們手寫的loader,看編譯後結果是不是變成了function了。

實現file-loader

  • 首先看正版file-loader的功能。自動生成個hash名圖片然後拷貝過去,這樣在顯示的時候就會通過src找到這個路徑。
let img = new Image()
let src = require('./logo.jpg')
img.src = src.default
document.body.appendChild(img)
  • loader部分:
let loaderUtils = require('loader-utils')
function myloader2(source) {
    let filename = loaderUtils.interpolateName(this, "[hash].[ext]", { content: source })
    console.log(filename)
    this.emitFile(filename, source)
    return `
    exports.__esModule=true;
    exports[Symbol.toStringTag]='Module';
    exports.default="${filename}"`
}
myloader2.raw = true
module.exports = myloader2
  • 爲什麼返回這個,可以看一下前面寫的一篇webpack打包規律。
  • 實測ok。

實現url-loader

  • 看一下正版url-loader會有個配置,把一定大小以內的圖片轉成base64。
 use: [
      {
           loader: 'url-loader',
           options: {
               limit: 100 * 1024
           }
       }
   ]
  • loader部分
let loaderUtils = require('loader-utils')
function myloader3(source) {
    let { limit } = loaderUtils.getOptions(this)
    limit = parseInt(limit)
    if (!limit || source.length < limit) {
        let base64 = `data:image/png;base64,${source.toString('base64')}`
        return `
            exports.__esModule=true;
            exports[Symbol.toStringTag]='Module';
            exports.default="${base64}"`
    } else {
        let fileLoader = require('./myloader2')
        return fileLoader.call(this, source)
    }
}
myloader3.raw = true
module.exports = myloader3
  • 大於設置的值傳給前面寫的file-loader執行,小於的值把圖片轉成base64返回。實測ok。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章