Fork me on GitHub

webpack4笔记(8)-简单实现

初探webpack的内部工作流程

首先用webpack4做一个小demo,可以无配置构建

1
2
yarn init -y
yarn add webpack webpack-cli -D

修改package.json

1
2
3
"scripts": {
"start": "webpack --mode development"
},

建立src文件夹,创建a.js和index.js两个文件

a.js

1
modules.exports = '这是一个测试'

index.js

1
2
const str = require('./a.js');
console.log(str);

执行 yarn start,此时多了一个dist目录,里面输出一个main.js,
看看编译后的样子,并简化代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 精简版本
(function(modules) {
// The module cache
var installedModules = {};
// require方法
function __webpack_require__(moduleId) {
// 检查模块是否在缓存中
if(installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
// 创建一个新的module,并切放到缓存中
var module = installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
}
// 执行module函数
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
// 标志模块已经加载
module.l = true;
// 返回module的exports属性
return module.exports;
}
})({
"./src/a.js": (function(module, exports) {
eval("modules.exports = '这是一个测试'\n\n//# sourceURL=webpack:///./src/a.js?");
}),
"./src/index.js": (function(module, exports) {
eval("const str = __webpack_require__(/*! ./a.js */ \"./src/a.js\");\nconsole.log(str);\n\n//# sourceURL=webpack:///./src/index.js?");
})

...

// 加载模块入口,返回exports
return __webpack_require__(__webpack_require__.s = "./src/index.js");
})
  • 编译后的代码包在一个自执行函数内,函数中的modules其实就是下面()内的对象{}
  • modules[moduleId].call()这句是将下面()内对应的key执行,modules[‘./src/index.js’] ()得到eval解析的代码

简单实现一个打包工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#! /usr/bin/env node    
const entry = './src/index.js'; // 入口文件
const output = './dist/main.js' // 出口文件
const fs = require('fs');
const path = require('path');
let script = fs.readFileSync(entry, 'utf8');
const results = []; // 打包结果
const ejs = require('ejs');

// 如果有require引用的依赖,那就需要替换处理依赖
script = script.replace(/require\(['"](.+?)["']\)/g, function () {
let name = path.join('./src/', arguments[1]);
let content = fs.readFileSync(name, 'utf8');
results.push({
name,
content
});
return `require('${name}')`;
});

// 这里的模板其实就是dist/main.js里的核心代码
let template = `
(function (modules) {
function require(moduleId) {
var module = {
exports: {}
};
modules[moduleId].call(module.exports, module, module.exports, require);
return module.exports;
}
return require("<%-entry%>");
})
({
"<%-entry%>": (function (module, exports, require) {
eval(\`<%-script%>\`);
})
<%for(let i=0;i<results.length;i++){
let mod = results[i];%>,
"<%-mod.name%>": (function (module, exports, require) {
eval(\`<%-mod.content%>\`);
})
<%}%>
});
`;

// result为替换后的结果,最终要写到output中
let result = ejs.render(template, {
entry,
script,
results
});

try {
fs.writeFileSync(output, result);
} catch(e) {
console.log('编译失败', e);
}
console.log('编译成功');

再实现一个简单的loader,style-loader作用是创建style标签添加到html中
loader其实就是函数,所以写一个styleLoader方法

1
2
3
4
5
6
7
8
let styleLoader = function(src) {
// src就是样式中的内容
return `
let style = document.createElement('style');
style.innerHTML = ${JSON.stringify(src).replace(/(\\r)?\\n/g, '')}; // 这里用JSON.stringify处理字符串不能换行的问题
document.head.appendChild(style);
`;
};

修改pack.js

1
2
3
4
5
6
7
script = script.replace(/require\(['"](.+?)['"]\)/g, function() {
let name = path.join('src', arguments[1]); // ./src/a.js
let content = fs.readFileSync(name, 'utf8');
// 如果有css文件,就进行编译
+ if (/\.css$/.test(name)) {
+ content = styleLoader(content);
+ }

参考文章:https://juejin.im/post/5ac9d8286fb9a028c97a5420

本文标题:webpack4笔记(8)-简单实现

文章作者:tongtong

发布时间:2019年04月16日 - 21:04

最后更新:2019年04月28日 - 20:04

原始链接:https://ilove-coding.github.io/2019/04/16/webpack4笔记(8)-简单实现/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

坚持原创技术分享,您的支持将鼓励我继续创作!
-------------本文结束-------------