Skip to content

实现babel-loader

本节实现babel-loader

安装插件

shell
yarn add @babel/core@^7.4.5 @babel/preset-env@^7.4.5 -D
  1. @babel/core,babel的核心模块
  2. @babel/preset-env,把高版本语法转换为低版本语法

并没有安装 babel-loader,我们自己手写一个。

再安装一个工具类loader-utils,用来拿到babel的预设

shell
yarn add loader-utils@^1.2.3 -D

代码

修改配置文件

js
// webpack\webpack-loader\webpack.config.js

let path = require('path')

module.exports = {
    mode: 'development',

    entry: './src/index.js',

    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    },

    devtool: 'source-map',

    module: {
        rules: [
            {
                test: /\.js$/,
                use: {
                    loader: 'babel-loader', // 使用我们自己写的babel-loader
                    options: {
                        presets: [ // 增加预设
                            '@babel/preset-env'
                        ]
                    }
                }
            }
        ]
    },

    resolveLoader: {
        // 找loader的时候,先从node_modules中找,找不到再从我们的loaders目录下找
        modules: ['node_modules', path.resolve(__dirname, 'loaders')]
        /* alias: { // loader的别名
            loader1: path.resolve(__dirname, 'loaders', 'loader1.js')
        } */
    }
}

新建babel-loader

js
// webpack\webpack-loader\loaders\babel-loader.js

let babel = require('@babel/core') // babel内部是用这个核心模块来转化的
let loaderUtils = require('loader-utils') // webpack loader的工具类,这里用来拿到预设

/* 
babel-loader
 需要拿到预设,告诉babel用预设转换模块
    options: {
    presets: [
        '@babel/preset-env'
    ]}
为了拿到配置中的预设,使用一个工具库,loader-utils 
}
*/
function loader(source) {
    console.log('babel-loader 加载了');
    console.log(this.resourcePath); // F:\code\note_code\webpack\webpack-loader\src\index.js

    // console.log(Object.keys(this));
    let optios = loaderUtils.getOptions(this) // 使用工具包,拿到webpack中的babel的预设
    // optios = { presets: [ '@babel/preset-env' ] }

    let cb = this.async() // webpack执行loader中自带的方法,用来返回一个回调函数
    // 异步返回结果的时候使用

    // 开始转换
    babel.transform(source, {
        ...optios,
        sourceMap: true, // 转换的映射文件
        filename: this.resourcePath.split('/').pop(), // 映射文件的名字 index.js
        // this.resourcePath = F:\code\note_code\webpack\webpack-loader\src\index.js
    }, (err, result) => { // 异步转换,所以需要使用回调返回结果
        cb(err, result.code, result.map) // 第一个参数是错误,第二个参数是返回结果
        // 同时生成一下sourceMap
    })
}

module.exports = loader

/* 
Object.keys(this) = [
  'version',                'emitWarning',      
  'emitError',              'getLogger',        
  'exec',                   'resolve',
  'getResolve',             'emitFile',
  'rootContext',            'webpack',
  'sourceMap',              'mode',
  '_module',                '_compilation',     
  '_compiler',              'fs',
  'target',                 'loadModule',       
  'context',                'loaderIndex',      
  'loaders',                'resourcePath',     
  'resourceQuery',          'async',
  'callback',               'cacheable',        
  'addDependency',          'dependency',       
  'addContextDependency',   'getDependencies',  
  'getContextDependencies', 'clearDependencies',
  'resource',               'request',
  'remainingRequest',       'currentRequest',   
  'previousRequest',        'query',
  'data'
]

*/

修改index.js

js
// webpack\webpack-loader\src\index.js

// 写一些ES6的语法,测试自己写babel-loader
class A {
    constructor() {
        this.name = 'cheny && xzz'
    }

    getName() {
        return this.name
    }
}

let a = new A()
console.log(a.getName());

打包一下看下结果,成功

image-20220206163235981

总结

babel-loader实现,关键是使用第三方包来转换es6代码

  1. loaderUtils,可以获取到webpack中预设的babel转换方式

    js
    let loaderUtils = require('loader-utils') // webpack loader的工具类,这里用来拿到预设
    
    function loader(source) {
        console.log('babel-loader 加载了');
        console.log(this.resourcePath); // F:\code\note_code\webpack\webpack-loader\src\index.js
    
        // console.log(Object.keys(this));
        let optios = loaderUtils.getOptions(this) // 使用工具包,拿到webpack中的babel的预设
        // optios = { presets: [ '@babel/preset-env' ] }
    }
    
    module.exports = loader
  2. webpack在调用loader时,我们在自定义loader上能访问到webpack的this对象,上面挂载了很多方法和属性

    js
    function loader(source) {
        // console.log(Object.keys(this));
    }
    
    module.exports = loader
    
    /* 
    Object.keys(this) = [
      'version',                'emitWarning',      
      'emitError',              'getLogger',        
      'exec',                   'resolve',
      'getResolve',             'emitFile',
      'rootContext',            'webpack',
      'sourceMap',              'mode',
      '_module',                '_compilation',     
      '_compiler',              'fs',
      'target',                 'loadModule',       
      'context',                'loaderIndex',      
      'loaders',                'resourcePath',     
      'resourceQuery',          'async',
      'callback',               'cacheable',        
      'addDependency',          'dependency',       
      'addContextDependency',   'getDependencies',  
      'getContextDependencies', 'clearDependencies',
      'resource',               'request',
      'remainingRequest',       'currentRequest',   
      'previousRequest',        'query',
      'data'
    ]
    
    */
  3. 在使用@babel/core来转换es6代码时,结果转换的方式是异步的,需要使用回调的方式把结果回传回去,可以在this上拿到回传的回调方法

    js
    let cb = this.async() // webpack执行loader中自带的方法,用来返回一个回调函数
  4. babel-loader的全部代码

    js
    let babel = require('@babel/core') // babel内部是用这个核心模块来转化的
    let loaderUtils = require('loader-utils') // webpack loader的工具类,这里用来拿到预设
    
    function loader(source) {
        console.log('babel-loader 加载了');
        console.log(this.resourcePath); // F:\code\note_code\webpack\webpack-loader\src\index.js
    
        // console.log(Object.keys(this));
        let optios = loaderUtils.getOptions(this) // 使用工具包,拿到webpack中的babel的预设
        // optios = { presets: [ '@babel/preset-env' ] }
    
        let cb = this.async() // webpack执行loader中自带的方法,用来返回一个回调函数
        // 异步返回结果的时候使用
    
        // 开始转换
        babel.transform(source, {
            ...optios,
            sourceMap: true, // 转换的映射文件
            filename: this.resourcePath.split('/').pop(), // 映射文件的名字 index.js
            // this.resourcePath = F:\code\note_code\webpack\webpack-loader\src\index.js
        }, (err, result) => { // 异步转换,所以需要使用回调返回结果
            cb(err, result.code, result.map) // 第一个参数是错误,第二个参数是返回结果
            // 同时生成一下sourceMap
        })
    }
    
    module.exports = loader

参考

https://www.bilibili.com/video/BV1a4411e7Bz?p=42