1、目录结构

image.png

2、安装

  1. cnpm install webpack webpack-cli babel-loader @babel/core @babel/preset-env -D

3、flow.js

  1. let fs = require('fs');
  2. let path = require('path');
  3. const { SyncHook } = require("tapable");
  4. class Compiler {
  5. constructor(options) {
  6. this.options = options;
  7. this.hooks = {
  8. run: new SyncHook(),
  9. done: new SyncHook(),
  10. };
  11. }
  12. run() {
  13. this.hooks.run.call();
  14. let modules = [];
  15. let chunks = [];
  16. let files = [];
  17. // 确定入口:根据配置中的entry找出所有的入口文件
  18. let entry = path.join(this.options.context, this.options.entry);
  19. //从入口文件出发,调用所有配置的Loader对模块进行编译,
  20. let entryContent = fs.readFileSync(entry, "utf8");
  21. let entrySource = babelLoader(entryContent);
  22. let entryModule = { id: entry, source: entrySource };
  23. modules.push(entryModule);
  24. //再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理
  25. let title = path.join(this.options.context, "./src/title.js");
  26. let titleContent = fs.readFileSync(title, "utf8");
  27. let titleSource = babelLoader(titleContent);
  28. let titleModule = { id: title, source: titleSource };
  29. modules.push(titleModule);
  30. //根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk
  31. let chunk = { name: "main", modules };
  32. chunks.push(chunk);
  33. //再把每个Chunk转换成一个单独的文件加入到输出列表
  34. let file = {
  35. file: this.options.output.filename,
  36. source: `
  37. (function (modules) {
  38. function __webpack_require__(moduleId) {
  39. var module = { exports: {} };
  40. modules[moduleId].call(
  41. module.exports,
  42. module,
  43. module.exports,
  44. __webpack_require__
  45. );
  46. return module.exports;
  47. }
  48. return __webpack_require__("./src/app.js");
  49. })(
  50. {
  51. "./src/app.js": function (module, exports, __webpack_require__) {
  52. var title = __webpack_require__("./src/title.js");
  53. console.log(title);
  54. },
  55. "./src/title.js": function (module) {
  56. module.exports = "title";
  57. },
  58. });
  59. `,
  60. };
  61. files.push(file);
  62. //在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统
  63. let outputPath = path.join(
  64. this.options.output.path,
  65. this.options.output.filename
  66. );
  67. fs.writeFileSync(outputPath, file.source,'utf8');
  68. this.hooks.done.call();
  69. }
  70. }
  71. //1.从配置文件和Shell语句中读取与合并参数,得出最终的参数
  72. let options = require('./webpack.config');
  73. //2.用上一步得到的参数初始化Compiler对象
  74. let compiler = new Compiler(options);
  75. //3.加载所有配置的插件
  76. if (options.plugins && Array.isArray(options.plugins)) {
  77. for (const plugin of options.plugins) {
  78. plugin.apply(compiler);
  79. }
  80. }
  81. //4.执行对象的run方法开始执行编译
  82. compiler.run();
  83. function babelLoader(source) {
  84. return `var sum = function sum(a, b) {
  85. return a + b;
  86. };`;
  87. }

4、webpack.config.js

  1. const path = require("path");
  2. const RunPlugin = require("./plugins/RunPlugin");
  3. const DonePlugin = require("./plugins/DonePlugin");
  4. module.exports = {
  5. context: process.cwd(),//当前的根目录
  6. mode: "development",//工作模 式 是开发模 式
  7. devtool: false,//不生成sourcemap
  8. entry: "./src/app.js",//入口文件
  9. output: {
  10. path: path.resolve(__dirname, "dist"),//输出的路径
  11. filename: "main.js",//文件名
  12. },
  13. module: {
  14. rules: [
  15. {
  16. test: /\.jsx?$/,
  17. use: {
  18. loader: "babel-loader",
  19. options: {
  20. presets: ["@babel/preset-env"],
  21. },
  22. },
  23. include: path.join(__dirname, "src"),
  24. exclude: /node_modules/,
  25. },
  26. ],
  27. },
  28. plugins: [new RunPlugin(),new DonePlugin()],
  29. devServer: {},
  30. };

5、webpack.flow.js

  1. /**
  2. * webpack的工作流程
  3. */
  4. let { SyncHook } = require("tapable");
  5. let fs = require('fs');
  6. class Compiler {
  7. constructor(options) {
  8. this.options = options;
  9. this.hooks = {
  10. run:new SyncHook(),
  11. done:new SyncHook()
  12. }
  13. }
  14. run(){
  15. let modules = [];
  16. let chunks = [];
  17. let files = [];
  18. this.hooks.run.call(); //触发run钩子执行
  19. //根据配置中的entry找出所有的入口文件
  20. let entry = path.join(this.options.context, this.options.entry);
  21. //从入口文件出发,调用所有配置的Loader对模块进行编译,再找出该模块依赖的模块,
  22. //再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;
  23. //1.读取模块内容
  24. //let sum = (a, b) => a + b;let title = require("./title");
  25. let entryContent = fs.readFileSync(entry, "utf8");
  26. let entrySource = babelLoader(entryContent);
  27. //模块module chunk代码块 file bundle 文件的关系
  28. let entryModule = { id: entry, source: entrySource };
  29. modules.push(entryModule);
  30. //把入口模块的代码转成抽象语法树AST,分析里面的import 和require依赖
  31. let title = path.join(this.options.context, "./src/title.js");
  32. let titleContent = fs.readFileSync(title, "utf8");
  33. let titleSource = babelLoader(titleContent);
  34. //模块module chunk代码块 file bundle 文件的关系
  35. let titleModule = { id: title, source: titleSource };
  36. modules.push(titleModule);
  37. //根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk
  38. let chunk = { name: "main", modules };
  39. chunks.push(chunk);
  40. //再把每个Chunk转换成一个单独的文件加入到输出列表
  41. let file = {
  42. file: this.options.output.filename,
  43. source: `
  44. (function (modules) {
  45. function __webpack_require__(moduleId) {
  46. var module = { exports: {} };
  47. modules[moduleId].call(
  48. module.exports,
  49. module,
  50. module.exports,
  51. __webpack_require__
  52. );
  53. return module.exports;
  54. }
  55. return __webpack_require__("./src/app.js");
  56. })(
  57. {
  58. "./src/app.js": function (module, exports, __webpack_require__) {
  59. var title = __webpack_require__("./src/title.js");
  60. console.log(title);
  61. },
  62. "./src/title.js": function (module) {
  63. module.exports = "title";
  64. },
  65. });
  66. `,
  67. };
  68. files.push(file);
  69. //在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统
  70. let outputPath = path.join(
  71. this.options.output.path,
  72. this.options.output.filename
  73. );
  74. fs.writeFileSync(outputPath, file.source, "utf8");
  75. //在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,
  76. //并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果
  77. this.hooks.done.call();
  78. }
  79. }
  80. /**
  81. * 1.初始化参数:从配置文件和Shell语句中读取与合并参数,得出最终的参数;
  82. */
  83. let options = require('./webpack.config');
  84. //开始编译:用上一步得到的参数初始化Compiler对象
  85. let compiler = new Compiler(options);
  86. //加载所有配置的插件,执行对象的run方法开始执行编译
  87. if(options.plugins&& Array.isArray(options.plugins)){
  88. for(const plugin of options.plugins){
  89. plugin.apply(compiler);
  90. }
  91. }
  92. //确定入口:根据配置中的entry找出所有的入口文件
  93. compiler.run();
  94. ///es6编译成es5
  95. function babelLoader(source){
  96. return `
  97. let sum = function sum(a, b){
  98. return a + b;
  99. };
  100. let title = require('./title');
  101. `;
  102. }

6、tapable.js

  1. /**
  2. * button.addEventListener('click',()=>{console.log('click')});
  3. * button.trigger('click');
  4. */
  5. //let {SyncHook} = require('tapable');
  6. class SyncHook {
  7. constructor() {
  8. this.taps = [];
  9. }
  10. tap(name, fn) {
  11. this.taps.push(fn);
  12. }
  13. call() {
  14. this.taps.forEach((tap) => tap());
  15. }
  16. }
  17. let hook = new SyncHook();
  18. //hook.addEventListener();
  19. hook.tap('some name', () => {
  20. console.log('some name');
  21. });
  22. hook.call();
  23. /*
  24. function add(){}
  25. add.call(); */
  26. //webpack-dev-server memory-fs

7、debug.js

  1. const webpack = require("webpack");
  2. const config = require("./webpack.config.js"); //1.读取配置文件
  3. debugger;
  4. const compiler = webpack(config);
  5. function compilerCallback(err, stats) {
  6. const statsString = stats.toString();
  7. console.log(statsString);
  8. }
  9. compiler.run((err, stats) => {
  10. compilerCallback(err, stats);
  11. });

8、插件

plugins/DonePlugin.js

  1. module.exports = class DonePlugin {
  2. apply(compiler) {
  3. compiler.hooks.done.tap("DonePlugin", () => {
  4. console.log("DonePlugin");
  5. });
  6. }
  7. };

plugins/RunPlugin.js

  1. module.exports = class RunPlugin {
  2. apply(compiler) {
  3. compiler.hooks.run.tap("RunPlugin", () => {
  4. console.log("RunPlugin");
  5. });
  6. }
  7. }

9、应用

src/app.js

  1. let sum = (a, b) => a + b;
  2. let title = require('./title');

src/title.js

  1. module.exports = 'title';

.gitinore

  1. # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
  2. # dependencies
  3. /node_modules
  4. /.pnp
  5. .pnp.js
  6. # testing
  7. /coverage
  8. # production
  9. /build
  10. # misc
  11. .DS_Store
  12. .env.local
  13. .env.development.local
  14. .env.test.local
  15. .env.production.local
  16. npm-debug.log*
  17. yarn-debug.log*
  18. yarn-error.log*