前端面试题-工程化-webpack 编译流程

webpack 编译流程

  1. 初始化参数:从配置文件和 Shell 语句中读取并合并参数,得出最终的配置对象
  2. 用上一步得到的参数初始化 Compiler 对象
  3. 加载所有配置的插件
  4. 执行对象的 run 方法开始执行编译
  5. 根据配置中的entry找出入口文件
  6. 从入口文件出发,调用所有配置的Loader对模块进行编译
  7. 再找出该模块依赖的模块 , 再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理
  8. 根据入口和模块之间的依赖关系 , 组装成一个个包含多个模块的 Chunk
  9. 再把每个 Chunk 转换成一个单独的文件加入到输出列表
  10. 在确定好输出内容后 , 根据配置确定输出的路径和文件名 , 把文件内容写入到文件系统
在以上过程中 , Webpack 会在特定的时间点广播出特定的事件 , 插件在监听到感兴趣的事件后会执行特定的逻辑 , 并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果

前端面试题-工程化-webpack 编译流程

文章插图
 
1.1entry?srcentry1.js
let title = require("./title")console.log("entry12", title)srcentry2.js
let title = require("./title.js")console.log("entry2", title)srctitle.js
module.exports = "title"1.2loader.js?
  • loader 的本质就是一个函数 , 一个用于转换或者说翻译的函数
  • 把那些 webpack 不认识的模块 less sass baxx 转换为 webpack 能认识的模块 js json
loaderslogger1-loader.js
【前端面试题-工程化-webpack 编译流程】function loader1(source) {//let name= 'entry1';return source + "//logger1" //let name= 'entry1';//logger1}module.exports = loader1loaderslogger2-loader.js
function loader2(source) {//let name= 'entry1';return source + "//logger2" //let name= 'entry1';//logger2}module.exports = loader21.3 plugin.js?pluginsdone-plugin.js
class DonePlugin {Apply(compiler) {compiler.hooks.done.tap("DonePlugin", () => {console.log("done:结束编译")})}}module.exports = DonePluginpluginsrun1-plugin.js
class RunPlugin {apply(compiler) {//在此插件里可以监听run这个钩子compiler.hooks.run.tap("Run1Plugin", () => {console.log("run1:开始编译")})}}module.exports = RunPluginpluginsrun2-plugin.js
class RunPlugin {apply(compiler) {compiler.hooks.run.tap("Run2Plugin", () => {console.log("run2:开始编译")})}}module.exports = RunPlugin1.4 webpack.config.js?webpack.config.js
const path = require("path")const Run1Plugin = require("./plugins/run1-plugin")const Run2Plugin = require("./plugins/run2-plugin")const DonePlugin = require("./plugins/done-plugin")module.exports = {mode: "development",devtool: false,context: process.cwd,entry: {entry1: "./src/entry1.js",entry2: "./src/entry2.js",},output: {path: path.resolve(__dirname, "dist"),filename: "[name].js",},resolve: {extensions: [".js", ".jsx", ".tx", ".tsx"],},module: {rules: [{test: /.js$/,use: [path.resolve(__dirname, "loaders/loader2.js"), path.resolve(__dirname, "loaders/loader1.js")],},],},plugins: [new DonePlugin(), new Run2Plugin(), new Run1Plugin()],}1.5debugger.js?debugger.js
const fs = require("fs")const webpack = require("./webpack2")// const webpack = require("webpack")const webpackConfig = require("./webpack.config")debuggerconst compiler = webpack(webpackConfig)//4.执行`Compiler`对象的 run 方法开始执行编译compiler.run((err, stats) => {if (err) {console.log(err)} else {//stats代表统计结果对象const statsJson = JSON.stringify(stats.toJson({// files: true, //代表打包后生成的文件assets: true, //其实是一个代码块到文件的对应关系chunks: true, //从入口模块出发 , 找到此入口模块依赖的模块 , 或者依赖的模块依赖的模块 , 合在一起组成一个代码块modules: true, //打包的模块 每个文件都是一个模块}))fs.writeFileSync("./statsJson.json", statsJson)}})1.6 webpack.js?webpack2.js
const Compiler = require("./Compiler")function webpack(options) {// 1.初始化参数:从配置文件和 Shell 语句中读取并合并参数,得出最终的配置对象//argv[0]是Node程序的绝对路径 argv[1] 正在运行的脚本// node debugger --mode=productionconst argv = process.argv.slice(2)const shellOptions = argv.reduce((shellOptions, options) => {// options = '--mode=development'const [key, value] = options.split("=")shellOptions[key.slice(2)] = valuereturn shellOptions}, {})console.log("shellOptions=>", shellOptions)const finalOptions = { ...options, ...shellOptions }//2.用上一步得到的参数初始化 `Compiler` 对象const compiler = new Compiler(finalOptions)//3.加载所有配置的插件const { plugins } = finalOptionsfor (let plugin of plugins) {//订阅钩子plugin.apply(compiler)}return compiler}module.exports = webpack


推荐阅读