【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。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章