lucifer
full stack coder
@lucifer
1. webpack是什么
2. 实现一个最基本的webpack
3. 给webpack 添加loader功能
webpack是什么
const { commit, pre, next, showLogs, showAllLogs } = require('./git');
commit({ a: 1 }, '初始化');
commit({ a: 1, b: 2 }, '添加一个');
commit({ a: 2 }, '修改a');
commit({ a: 1 }, '删除b');
pre();
// next();
pre();
pre();
pre();
next();
module.exports = {
alwaysReturnOne() {
return 1;
}
}
如何打包 📦 下面的代码
function commit(any, msg) {
}
function goBack(stepCount) {
return (head = stepCount > head ? 0 : head - stepCount);
}
const pre = () => goBack(1);
const next = () => goBack(-1);
commit({ a: 1 }, '初始化');
commit({ a: 1, b: 2 }, '添加一个');
commit({ a: 2 }, '修改a');
commit({ a: 1 }, '删除b');
pre();
// next();
pre();
pre();
pre();
next();
const code = fs.readFileSync(filename, {
encoding: "utf-8" // 假设文件是utf-8编码
});
const wrap = new Function(code);
wrap();
我们可以这么处理
这里以commonjs模块化规范讲解
modules with dependencies
我们将一个模块抽象为一个普通的JS对象
{
dependencies: ['./git.js'],
id: 0,
filename: '/Users/lucifer/koro/src/git.test.js',
code: "通过fs api 读取的文件内容",
mapping: {
'./git.js': 1
}
}
可以看出这里git.test.js 依赖 git.js
module with dependency
{
dependencies: ['./git.js'],
id: 0,
filename: '/Users/lucifer/koro/src/git.test.js',
code: "通过fs api 读取的文件内容",
mapping: {
'./git.js': 1
}
};
{
dependencies: [],
id: 1,
filename: '/Users/lucifer/koro/src/git.js',
code: "通过fs api 读取的文件内容",
mapping: {
}
};
[
{
id: 0,
code:
'"use strict";\n\nvar _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; };\n\nvar say = _interopRequire(require("./say.js"));\n\nvar name = require("./info.js").name;\n\n\nconsole.log(say(name));',
mapping: { "./say.js": 1, "./info.js": 2 }
},
{
id: 1,
code:
'"use strict";\n\nmodule.exports = function (name) {\n return "hello " + name;\n};',
mapping: {}
},
{
id: 2,
code:
'"use strict";\n\nvar name = require("./name.js").name;\n\n\nmodule.exports = {\n name: name\n};',
mapping: { "./name.js": 3 }
},
{
id: 3,
code: '"use strict";\n\nmodule.exports = {\n name: "lucifer"\n};',
mapping: {}
}
]
modules with dependencies
解决浏览器中没有module,require,exports关键字
...
function require(name) {
return ...
}
const module = { exports: {} };
const wrap = new Function("require", "module", "exports", code); // 注意这一行
wrap(require, module, module.exports); // 注意这一行
...
bundle-core(解决了依赖问题)
function webpackRequire(id) {
const { code, mapping } = modules[id];
function require(name) {
return webpackRequire(mapping[name]);
}
const module = { exports: {} };
const wrap = new Function("require", "module", "exports", code);
wrap(require, module, module.exports);
return module.exports;
}
webpackRequire(0);
loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。 本质上,webpack loader 将所有类型的文件,转换为应用程序的依赖图(和最终的 bundle)可以直接引用的模块。
by @webpack
官方对loader的描述
官方的loader例子
module.exports = {
//...
module: {
rules: [
{
test: /\.txt$/,
use: ["raw-loader"]
}
]
}
};
值得注意的是loaders是链式的
const { compose } = require("./utils");
const applyLoaders = (fullpath, rules, content) => {
let ret = content;
let hit = false;
rules.forEach(rule => {
if (rule.test.test(fullpath)) {
hit = true;
const loaders = rule.use.map(item =>
item.loader.bind({
loaders: rule.use
})
);
ret = compose(...loaders)(content);
}
});
if (!hit) {
console.log(
`${fullpath}: You may need an appropriate loader to handle this file type.`
);
}
return ret;
};
module.exports = {
applyLoaders
};
const code = applyLoaders(absoluteEntryPath, rules, content);
集成loaders的实现
const { bundle } = require("../../src/main");
const babelLoader = require("6to5-loader"); // 这个是babel早期的一个loader
// 去除单行注释
const customLoader = content => content.replace(/\/\/.*\n/g, "");
bundle({
entry: "../examples/loaders/index.js",
output: {
path: "dist",
filename: "bundle.js"
},
module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: babelLoader
},
{
loader: customLoader
}
]
}
]
}
});
(function(modules) {
function webpackRequire(id) {
const { code, mapping } = modules[id];
function require(name) {
return webpackRequire(mapping[name]);
}
const module = { exports: {} };
const wrap = new Function("require", "module", "exports", code);
wrap(require, module, module.exports);
return module.exports;
}
webpackRequire(0);
})([
{
id: 0,
code:
'"use strict";\n\nvar _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; };\n\nvar say = _interopRequire(require("./say.js"));\n\nvar name = require("./info.js").name;\n\n\nconsole.log(say(name));',
mapping: { "./say.js": 1, "./info.js": 2 }
},
{
id: 1,
code:
'"use strict";\n\nmodule.exports = function (name) {\n return "hello " + name;\n};',
mapping: {}
},
{
id: 2,
code:
'"use strict";\n\nvar name = require("./name.js").name;\n\n\nmodule.exports = {\n name: name\n};',
mapping: { "./name.js": 3 }
},
{
id: 3,
code: '"use strict";\n\nmodule.exports = {\n name: "lucifer"\n};',
mapping: {}
}
]);
项目地址:https://github.com/azl397985856/mono-webpack
creating x from zero series地址:
https://github.com/azl397985856/mono-series
Thanks~
By lucifer