Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webpack源码分析之三:loader #10

Open
laughing-pic-zhu opened this issue Jun 7, 2018 · 0 comments
Open

webpack源码分析之三:loader #10

laughing-pic-zhu opened this issue Jun 7, 2018 · 0 comments
Labels
webpack webpack 源码

Comments

@laughing-pic-zhu
Copy link
Owner

laughing-pic-zhu commented Jun 7, 2018

前言

在webpack特性里面,它可以支持将非javaScript文件打包,但前面写到webpack的模块化打包只能应用于含有特定规范的JavaScript文件。本次介绍的loader则是用来解决这类问题的。本文章loader的实现基于code-splitting

功能分析

举个例子:

webpack.config.js中的配置loader

	module: {
            rules: [
                {
                    test: /\.js$/,
                    loader: "test-loader!test-loader2"
                }
            ]
        }

业务代码中的内联loader

require('d!c');

分析:

我们需要将这些loader解析成可运行的函数,并在parse模块解析语法树之前运行掉这些loader函数

所以我们需要:

  1. resolve模块:分析出module对应的loader字符串,并解析出各个loader的绝对路径
  2. buildDeps模块:通过文件路径获取需要运行的loader函数,将其压入队列,之后再依次按序递归出loader函数运行,如果是异步loader,则要通过回调函数来递归下一个loader函数。

实现

resolve 模块

实现思路:

  1. 将配置内的loaders,shell命令的loaders,require/import的内联loader从前至后组成一个数组。配置内的loaders需要正则匹配test属性,来获取配置内的loader字符串。所有loader字符串内部又可以截取'!',获取完整的loader。
  2. 分析并解析该数组内的字符串,获取各个loader的绝对路径。并返回解析好的字符串。这块的实现和文件打包类似。

最终require内的字符串如下

/Users/zhujian/Documents/workspace/webpack/simple-webpack/example/node_modules/d.js!
/Users/zhujian/Documents/workspace/webpack/simple-webpack/example/node_modules/test-loader/index.js!
/Users/zhujian/Documents/workspace/webpack/simple-webpack/example/node_modules/test-loader2/index.js!
/Users/zhujian/Documents/workspace/webpack/simple-webpack/example/node_modules/c.js

buildDeps模块

实现思路:

  1. 解析文件路径,并获取需要运行的loader函数,存入数组
  2. 数组在通过pop函数一个个递归,考虑到存在异步loader函数的情况,需要为运行函数提供async,以及callback的上下文。具体的上下文可参考Loader API

loader递归逻辑如下:

	nextLoader.apply(null, content);
	function nextLoader() {
        const args = Array.prototype.slice.apply(arguments);
        if (loaderFunctions.length > 0) {
            const loaderFunction = loaderFunctions.pop();
            let async = false;
            const context = {
                fileName,
                options,
                debug: options.debug,
                async: function () {
                    async = true;
                    return nextLoader;
                },
                callback: function () {
                    async = true;
                    nextLoader.apply(null, arguments)
                }
            };
            const resVal = loaderFunction.apply(context, args);
            if (!async) {
                nextLoader(resVal);
            }
        } else {
            callback(null, args[0])
        }
    }

测试

将以上3个loader,test-loader,test-loader2,异步loader d.js,打包c.js

test-loader

module.exports = function(content) {
    return content+"\nexports.answer = 42;\n"
}

test-loader2

module.exports = function(content) {
    return content+"\nexports.test2 = test2;\n"
}

异步loader d.js

module.exports = function (content) {
    const d = 'd';
    this.async();
    const b = content + "\nexports.d = 2000;\n";
    setTimeout(this.callback.bind(this, b), 2000);
}

c.js

const c = 'c';

module.exports = c;

最终打包出来的c.js的代码如下

....
/* 1 */
/***/(function(module, exports,__webpack_require__) {
const c = 'c';

module.exports = c;

exports.test2 = test2;

exports.answer = 42;

exports.d = 2000;

/***/}
....

代码实现

本人的简易版webpack实现simple-webpack

(完)

@laughing-pic-zhu laughing-pic-zhu added the webpack webpack 源码 label Jun 7, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
webpack webpack 源码
Projects
None yet
Development

No branches or pull requests

1 participant