范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

前端面试题webpack编译流程

  webpack 编译流程初始化参数:从配置文件和 Shell 语句中读取并合并参数,得出最终的配置对象 用上一步得到的参数初始化 Compiler 对象 加载所有配置的插件 执行对象的 run 方法开始执行编译 根据配置中的entry找出入口文件 从入口文件出发,调用所有配置的Loader对模块进行编译 再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理 根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk 再把每个 Chunk 转换成一个单独的文件加入到输出列表 在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统
  在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果
  1.1entry​
  srcentry1.js let title = require("./title") console.log("entry12", title)
  srcentry2.js let title = require("./title.js") console.log("entry2", title)
  src	itle.js module.exports = "title" 1.2loader.js​loader 的本质就是一个函数,一个用于转换或者说翻译的函数 把那些 webpack 不认识的模块 less sass baxx 转换为 webpack 能认识的模块 js json
  loaderslogger1-loader.js function loader1(source) {   //let name= "entry1";   return source + "//logger1" //let name= "entry1";//logger1 } module.exports = loader1
  loaderslogger2-loader.js function loader2(source) {   //let name= "entry1";   return source + "//logger2" //let name= "entry1";//logger2 } module.exports = loader2 1.3 plugin.js​
  pluginsdone-plugin.js class DonePlugin {   apply(compiler) {     compiler.hooks.done.tap("DonePlugin", () => {       console.log("done:结束编译")     })   } } module.exports = DonePlugin
  pluginsrun1-plugin.js class RunPlugin {   apply(compiler) {     //在此插件里可以监听run这个钩子     compiler.hooks.run.tap("Run1Plugin", () => {       console.log("run1:开始编译")     })   } } module.exports = RunPlugin
  pluginsrun2-plugin.js class RunPlugin {   apply(compiler) {     compiler.hooks.run.tap("Run2Plugin", () => {       console.log("run2:开始编译")     })   } } module.exports = RunPlugin 1.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") debugger const 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=production   const argv = process.argv.slice(2)   const shellOptions = argv.reduce((shellOptions, options) => {     // options = "--mode=development"     const [key, value] = options.split("=")     shellOptions[key.slice(2)] = value     return shellOptions   }, {})   console.log("shellOptions=>", shellOptions)   const finalOptions = { ...options, ...shellOptions }   //2.用上一步得到的参数初始化 `Compiler` 对象   const compiler = new Compiler(finalOptions)   //3.加载所有配置的插件   const { plugins } = finalOptions   for (let plugin of plugins) {     //订阅钩子     plugin.apply(compiler)   }   return compiler } module.exports = webpack 1.7 Compilation​
  Compiler.js const { SyncHook } = require("tapable") const Compilation = require("./Compilation") const fs = require("fs") const path = require("path") // Compiler 模块是 webpack 的主要引擎 class Compiler {   constructor(options) {     this.options = options     this.hooks = {       run: new SyncHook(), //在开始编译之前调用       done: new SyncHook(), //在编译完成时执行     }   }   run(callback) {     this.hooks.run.call() //在编译开始前触发run钩子执行     //在编译的过程中会收集所有的依赖的模块或者说文件     //stats指的是统计信息 modules chunks  files=bundle assets指的是文件名和文件内容的映射关系     const onCompiled = (err, stats, fileDependencies) => {       // 10.在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统       for (let filename in stats.assets) {         let filePath = path.join(this.options.output.path, filename)         fs.writeFileSync(filePath, stats.assets[filename], "utf8")       }       callback(err, { toJson: () => stats })       for (let fileDependency of fileDependencies) {         //监听依赖的文件变化,如果依赖的文件变化后会开始一次新的编译         fs.watch(fileDependency, () => this.compile(onCompiled))       }     }     this.hooks.done.call() //在编译完成时触发done钩子执行     //调用compile方法进行编译  开始一次新的编译     this.compile(onCompiled)   }   //开启一次新的编译   compile(callback) {     //每次编译 都会创建一个新的Compilation实例     let compilation = new Compilation(this.options, this)     compilation.build(callback)   } } module.exports = Compiler 1.8 Compilation​
  Compilation.js const path = require("path") const fs = require("fs") const parser = require("@babel/parser") const types = require("@babel/types") const traverse = require("@babel/traverse").default const generator = require("@babel/generator").default const baseDir = normalizePath(process.cwd()) function normalizePath(path) {   return path.replace(//g, "/") } class Compilation {   constructor(options, compiler) {     this.options = options // 配置参数     this.options.context = this.options.context || normalizePath(process.cwd())     this.compiler = compiler     this.modules = [] //这里放置本次编译涉及的所有的模块     this.chunks = [] //本次编译所组装出的代码块     this.assets = {} // 存放输出的文件 key是文件名,值是文件内容     this.files = [] //代表本次打包出来的文件     this.fileDependencies = new Set() //本次编译依赖的文件或者说模块   }   build(callback) {     //5.根据配置中的entry找出入口文件     let entry = {}     //格式化入口文件     if (typeof this.options.entry === "string") {       entry.main = this.options.entry     } else {       entry = this.options.entry     }     // 对入口进行遍历     for (let entryName in entry) {       //获取入口文件的绝对路径       let entryFilePath = path.posix.join(baseDir, entry[entryName])       //把此入口文件添加到文件依赖列表中       this.fileDependencies.add(entryFilePath)       //6.从入口文件出发,调用所有配置的Loader对模块进行编译       let entryModule = this.buildModule(entryName, entryFilePath)       // this.modules.push(entryModule)       // 8.根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk       let chunk = {         name: entryName, //入口名称         entryModule, //入口的模块 ./src/entry.js         modules: this.modules.filter((module) => module.names.includes(entryName)), //此入口对应的模块       }       this.chunks.push(chunk)     }      // 9.再把每个 Chunk 转换成一个单独的文件加入到输出列表     this.chunks.forEach((chunk) => {       const filename = this.options.output.filename.replace("[name]", chunk.name)       this.files.push(filename)       //组装chunk       this.assets[filename] = getSource(chunk)     })     callback(       null,       {         modules: this.modules,         chunks: this.chunks,         assets: this.assets,         files: this.files,       },       this.fileDependencies     )   }   /**    * 编译模块    * @param {*} name 模块所属的代码块(chunk)的名称,也就是entry的name entry1 entry2    * @param {*} modulePath 模块的绝对路径    */   buildModule(entryName, modulePath) {     //1.读取文件的内容     let rawSourceCode = fs.readFileSync(modulePath, "utf8")     //获取loader的配置规则     let { rules } = this.options.module     //根据规则找到所有的匹配的loader 适用于此模块的所有loader     let loaders = []     rules.forEach((rule) => {       //用模块路径匹配正则表达式       if (modulePath.match(rule.test)) {         loaders.push(...rule.use)       }     })     //调用所有配置的Loader对模块进行转换      let transformedSourceCode = loaders.reduceRight((sourceCode, loaderPath) => {       const loaderFn = require(loaderPath)       return loaderFn(sourceCode)     }, rawSourceCode)      //7.再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理     //获取当前模块,也就是 ./src/entry1.js的模块ID     let moduleId = "./" + path.posix.relative(baseDir, modulePath)     //创建一个模块ID就是相对于根目录的相对路径 dependencies就是此模块依赖的模块     //name是模块所属的代码块的名称,如果一个模块属于多个代码块,那么name就是一个数组     let module = { id: moduleId, dependencies: new Set(), names: [entryName] }     this.modules.push(module)     let ast = parser.parse(transformedSourceCode, { sourceType: "module" })     //Visitor是babel插件中的概念,此处没有     traverse(ast, {       CallExpression: ({ node }) => {         //如果调用的方法名是require的话,说明就要依赖一个其它模块         if (node.callee.name === "require") {           // .代表当前的模块所有的目录,不是工作目录           let depModuleName = node.arguments[0].value //"./title"           let depModulePath           //获取当前的模块所在的目录           if (depModuleName.startsWith(".")) {             //暂时先不考虑node_modules里的模块,先只考虑相对路径             const currentDir = path.posix.dirname(modulePath)             //要找当前模块所有在的目录下面的相对路径             depModulePath = path.posix.join(currentDir, depModuleName)             //此绝对路径可能没有后续,需要尝试添加后缀             // 获取配置的扩展名后缀             const extensions = this.options.resolve.extensions             //尝试添加后缀 返回最终的路径             depModulePath = tryExtensions(depModulePath, extensions)           } else {             //如果不是以.开头的话,就是第三方模块             depModulePath = require.resolve(depModuleName)           }           //把依赖的模块路径添加到文件依赖列表           this.fileDependencies.add(depModulePath)           //获取此依赖的模块的ID, 也就是相对于根目录的相对路径           let depModuleId = "./" + path.posix.relative(baseDir, depModulePath)           //修改语法树,把依赖的模块名换成模块ID           node.arguments[0] = types.stringLiteral(depModuleId)           //把依赖的模块ID和依赖的模块路径放置到当前模块的依赖数组中           module.dependencies.add({ depModuleId, depModulePath })         }       },     })     //转换源代码,把转换后的源码放在_source属性,用于后面写入文件     let { code } = generator(ast)     module._source = code     ;[...module.dependencies].forEach(({ depModuleId, depModulePath }) => {       //判断此依赖的模块是否已经打包过了或者说编译 过了       let existModule = this.modules.find((module) => module.id === depModuleId)       if (existModule) {         existModule.names.push(entryName)       } else {         let depModule = this.buildModule(entryName, depModulePath)         this.modules.push(depModule)       }     })     return module   } } /**  *  * @param {*} modulePath  * @param {*} extensions  * @returns  */ function tryExtensions(modulePath, extensions) {   if (fs.existsSync(modulePath)) {     return modulePath   }   for (let i = 0; i < extensions.length; i++) {     let filePath = modulePath + extensions[i]     if (fs.existsSync(filePath)) {       return filePath     }   }   throw new Error(`找不到${modulePath}`) } function getSource(chunk) {   return `   (() => {     var modules = {       ${chunk.modules         .filter((module) => module.id !== chunk.entryModule.id)         .map(           (module) => `           "${module.id}": module => {             ${module._source}           }         `         )         .join(",")}     };     var cache = {};     function require(moduleId) {       var cachedModule = cache[moduleId];       if (cachedModule !== undefined) {         return cachedModule.exports;       }       var module = cache[moduleId] = {         exports: {}       };       modules[moduleId](module, module.exports, require);       return module.exports;     }     var exports = {};     (() => {       ${chunk.entryModule._source}     })();   })();   ` } module.exports = Compilation
  webpack2.js 总结​1. 文件作用​webpack.js 文件​
  webpack 方法 接收 webpack.config.js 参数,返回 compiler 实例 初始化参数 始化 Compiler 对象实例 加载所有配置的插件 Compiler文件​Compiler 模块是  webpack 的主要引擎 constructor 方法 : 初始化一些 hooks run 方法 执行插件订阅的一系列 hooks 创建 Compilation 实例并执行实例的 build(onCompiled)方法(开启一次新的编译) onCompiled 回调在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统执行 compiler.run 方法的回调,传入 info监听依赖的文件变化,如果依赖的文件变化后会开始一次新的编译 Compilation 文件​
  build 方法 .根据配置中的 entry 找出入口文件 从入口文件出发,调用所有配置的 Loader 对模块进行编译 再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理 根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk 再把每个 Chunk 转换成一个单独的文件加入到输出列表 执行成功后的回调 2. 流程总结​初始化参数:​初始化参数 :从配置文件和 Shell 语句中读取并合并参数,得出最终的配置对象(命令行优先级高) 开始编译​用上一步得到的参数 初始化 Compiler 对象 初始化 options 参数和 hooks ( run: new SyncHook(), //在开始编译之前调用...) 加载 所有配置的 插件 : 在配置中找到 plugins 数组 遍历 plugins 执行每个插件的 apply 方法,并把 compiler 实例传进去(每个插件都有一个 apply 方法) 执行 compiler.hooks.run.tap等方法注册事件 执行 compiler实例的  run 方法 开始执行编译 整个过程伴随着触发插件的注册个各种钩子函数 this.hooks.done.call()... 开启一次新的编译,创建一个新的 Compilation 实例 执行实例的 build 方法,传入完成的回调 编译模块​根据配置中的 entry 找出入口文件 格式化入口文件,变成对象形式 对入口进行遍历,获取入口文件的绝对路径,添加到文件依赖列表中 loader 转换 :从入口文件出发,调用所有配置的 Loader 对模块进行转换 (最终返回 module 对象) 读取处理文件的内容 根据规则找到所有的匹配的 loader 调用所有配置的 Loader 对模块进行转换(从上到下,从右向左) 获取当前模块模块 id,相对于根目录的相对路径 创建一个 module 对象 const  module =  {
  id : "./src/entry1.js" , //相对于根目录的相对路径
  dependencies :[{ depModuleId :./ src/ title. js, depModulePath : "xxx" }], //dependencies就是此模块依赖的模块
  names :[ "entry1" ], // name是模块所属的代码块的名称,如果一个模块属于多个代码块,那么name就是一个数组
  2.
  _source : "xxx" , //存放对应的源码   } 编译模块分析依赖 ,再 递归遍历 本步骤直到所有入口 依赖模块 的文件都经过了本步骤的处理 将 loader 编译后的代码调用 parse 转换为 ast 遍历语法树,如果存在 require 或者 import,说明就要依赖一个其它模块 获取依赖模块的绝对路径,添加到文件依赖列表中 获取此依赖的模块的 ID, 也就是相对于根目录的相对路径 修改语法树,把依赖的模块名换成模块 ID 把依赖的模块 ID 和依赖的模块路径放置到当前模块 module 的依赖数组中 调用 generator(ast),把转换后的源码放在 module._source 属性,用于后面写入文件 遍历module.dependencies,递归构建 module,构建好的存储到 this.modules 上,如果第二个入口也依赖该模块,直接取用,只需要给该模块的 name 属性上添加上入口信息 输出资源​组装 chuck 对象: 组装 const chuck = {   name : "entry1" , //入口名称   entryModule, //入口的模块的module {id,name,dependencies,_source}   modules : [{}], // 入口依赖模块的集合   } this.chunks.push(chunk)生成 bundle 文件​把每个 Chunk 转换成一个单独的文件加入到输出列表获取要生成的文件名称并把文件名添加到 this.files 中获取文件内容并给 this.assets 对象执行 compilation.build 方法的回调 写入文件​在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统

无翅鱼来了!谈谈转基因工程给生活带来的影响日前,中科院水生所桂建芳院士团队发布最新研究成果,经一年多的努力,他们根据基因技术,利用鱼类精准育种技术创制出了无刺喜头鱼。那我们来说说转基因工程,转基因工程,也称为生物工程,是指深切缅怀2023年1月逝世的17位共和国两院院士从2023年1月1日2023年1月31日,统计有17位两院院士逝世。其中,中科院共5名院士,工程院共12名院士。2022年逝世的中国两院院士17位逝世院士中,最大年龄99岁,最小年无人问津的指令也能实现精准定位控制!导读S7200SMARTCPU提供了四种开环运动控制方法脉冲串输出(PTO)内置在CPU的速度和位置控制。此功能仅提供脉冲串输出,方向和限值控制必须通过应用程序使用PLC中集成的或图集花海大片上映!梅城春光梅景醉游人春节已过,寒意仍未褪去,梅江的梅花已悄悄绽放。枝头梅花盛开,分外好看。走进位于周溪河畔的客家公园里,还未见梅树,便已闻到一股清香。香味深处,一抹红色在深绿的山坡晕染开。梅景引得众多南沙紫花风铃木盛开,粉紫色花海火爆出圈春天将近,浪漫成海的盛花期将如期而至,百花争奇斗艳,陆续登场。位于南沙东涌镇马克村的紫花风铃木花开正盛,粉紫色的花海在这个初春火爆出圈,前来打卡的人络绎不绝。在这个季节,安排一场说拍到手机没电!广州2023年第一批花海,刷屏了!图源网友Cycble立春将至,花开报春梅花樱花郁金香广州2023第一批花海美出天际,你打卡了吗?一起徜徉春日花海感受广州花花世界的美好郁金香花1。192。10白云山云台花园云台花园媒体人新疆队外援克莱蒙斯ampampamp摩尔特里已到球队法尔2月初回归直播吧1月30日讯据媒体人卢鹏儒报道,新疆队外援克莱蒙斯和摩尔特里已到新疆,法尔预计2月初回归。原文写道新疆男篮在大年初四集结,在主帅邱彪的率领下,按照训练计划紧锣密鼓地开启了第三魔术师谈争议漏吹塔图姆都笑了因为他很惊讶裁判没有吹直播吧1月30日讯湖人昨日在客场121125加时不敌凯尔特人,常规时间最后一回合,詹姆斯突破上篮被塔图姆打手,但是裁判漏吹引发巨大争议。魔术师更推写道在输给凯尔特人的比赛中,裁判在2023年石家庄市篮球超级联赛昨晚落幕2023年石家庄市篮球超级联赛昨晚落幕篮球盛宴热血澎湃河北银行队获得联赛冠军1月29日晚,2023年石家庄市篮球超级联赛,在河北体育馆上演了决赛的巅峰对决,为省会球迷奉献一场精彩的詹皇队友加盟辽篮?杨鸣眼光的确很独,杜锋怕啥来啥CBA第3阶段的比赛就要开始了,目前卫冕冠军辽宁男篮,也是做出了很重要的决定,因为莫兰德伤势仍然没有恢复,无法进行有球训练,据传莫兰德已经,无缘辽篮的第三阶段比赛,那么辽篮内线,仍男篮揪出2大水货,或无缘12人大名单,乔帅心里清楚男篮揪出2大水货,或无缘12人大名单,乔帅心里清楚球迷都知道,中国男篮是公布了18人的大名单,如今中国男篮,现在可能会在2月中旬,公布最后的12人大名单,到时候肯定会有6名球员遭到
新岗位曝光,体育局官宣丁宁新职务,薪资曝光,不负刘国梁期待久违了丁宁,2月14日情人节,丁宁再一次登上了热搜,丁宁的这一次热搜并不是因为恋情,而是因为丁宁收获了另外一个好消息。众所皆知,在退役后,丁宁返回了学校读书,看到丁宁日常生活的点滴李铁案即将大结局?李平康李璇同步发声,球迷瓜保熟吗?2月14日,相关部门针对李铁的调查已经持续3个月有余,在此期间,不断有教练球员被带走调查,甚至包括足协的高层官员!除此之外,有人为了躲避相关部门的调查,已经提前跑路。从目前的情况来男篮罚球有多差?乔帅不满六大核心仅1人达标周琦郭艾伦差劲中国男篮目前正在上海进行集训,备战不久后的世预赛,新帅乔尔杰维奇上任第一次带训就发现不少问题,尤其是基本功方面,为此要求所有球员苦练运动和罚球,从细节做起。而据北京体育广播报道,乔打疯了!69分!联手欧文东契奇!阿德也来了!森林狼客场挑战独行侠,124比121,以三分优势带走胜利。赢家是森林狼,热度却感觉全在独行侠这里。大概是因为欧文的表现太炸裂了,前三节底角抽烟,仅有10分入账,末节却单节入账26分掌握这八句口头禅,想变穷都难导语当你张嘴的那一刻,我就知道你是穷人还是富人。穷人有穷人的一套语言系统。富人有富人的一套语言系统。这两套语言系统就像两条平行线互不相交。当你想要成为富人,那么就要先拥有富人的那套华润入主赋能中药老字号,昆药集团聚焦研发创新,打造精品国药(报告出品方分析师信达证券刘嘉仁)一公司分析1公司概况昆药集团股份有限公司(以下简称昆药或公司)成立于1951年3月,2000年12月在上海证券交易所上市。公司拥有丰富制药经验,是增量提质推动北海高端服务业加快发展政府工作报告解读今年市政府工作报告提出要开拓思路勇于创新,在高端服务业扩增量提质量上花功夫用实劲,推动高端服务业加快发展。当前,高端服务业已成为我市经济稳增长的重要支撑,彰显出强大韧性和蓬勃活力。奶茶杯可乐罐国家烟草专卖局重拳打击非法电子烟视频加载中国家烟草专卖局近期开展奶茶杯可乐罐等调味电子烟整治工作,查破各类案件593起追究刑事责任188人查获各类奶茶杯可乐罐72。38万支,依法严厉打击该类违法犯罪工作取得明显成多地中小学迎来新学期(2月13日,天津市和平区第五幼儿园的小朋友在学习手工制作。当日,多地学校迎来春季新学期,并开展形式多样的开学第一课活动。新华社记者赵子硕摄)(2月13日,在北京小学大兴分校亦庄学生活随笔老井老井吉亚超随着家门口的那口老井被掩埋,十里八村再也没有一口老井了。封井的那天,正好我在家,老井的四周围满了人,却出奇的安静,像是在为一位德高望重的老者送行。几位七八十岁的老人坐在不他觉得在互联网世界失联了!杭州的小张反映,去年10月份,自己的微信号被封了。一直到现在,他还是想不明白为什么。1818黄金眼微信号被封感觉与互联网世界失联,小伙想问为什么小张我没有办法给别人发消息了,我已经