Loader本质是一个函数,它是一个转换器。webpack只能解析原生js文件,对于其他类型文件就需要loade进行转换。
Plugin它是一个插件,用于增强webpack功能。webpack在运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 webpack 提供的 API 改变输出结果 。
Plugin的配置在plugins下。类型为数组,每一项是一个 Plugin 的实例,参数都通过构造函数传入。
传入和webpack.config.js文件配置的参数,形成最终的配 置结果。
开始编译。使用上一次得到的参数初始化compiler对象,注册所有配置的插件,插件监听Webpack构建生命周期的事件节点,做出相应的反应,执行对象的run方法开始执行编译。
确定入口。从配置的entry入口,开始解析文件构建AST语法树,找出依赖,递归下去。
配置,调用所有配置的loader对文件进行转换,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理。
完成模块编译。在经过第四步使用Loader翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系。
输出资源。根据入口和模块之间的依赖关系,组装成一个个包含多个模块的Chunk,再把每个Chunk转换成单独的文件加入到输出列表,这步是可以修改输出内容的最后机会。
输必博首页出完成。在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。
这个流程是一个串行的过程,Webpack的运行流程是一个串行的过程,它的工作流程就是将各个插件串联起来。在运行过程中会广播事件,插件只需要监听它所关心的事件,就能加入到这条Webpack机制中,去改变Webpack的运作,使得整个系统扩展性良好。
HRM的原理实际上是 webpack-dev-server(WDS)和浏览器之间维护了一个websocket服务。当本地资源发生变化后,webpack会先将打包生成新的模块代码放入内存中,然后WDS向浏览器推送更新,并附带上构建时的hash,让客户端和上一次资源进行对比.
Code Splitting代码分割,是一种优化技术。它允许将一个大的chunk拆分成多个小的chunk,从而实现按需加载,减少初始加载时间,并提高应用程序的性能 。
Source Map是一种文件,它建立了构建后的代码与原始源代码之间的映射关系。通常在开发阶段开启,用来调试代码,帮助找到代码问题所在。
Webpack的Tree Shaking是一个利用ES6模块静态结构特性来去除生产环境下不必要代码的优化过程。其工作原理在于:
当Webpack分析代码时,它会标记出所有的import语句和export语句。
然后,当Webpack确定某个模块没有被导入时,它会在生成的bundle中排除这个模块的代码。
同时,Webpack还会进行递归的标记清理,以确保所有未使用的依赖项都不会出现在最终的bundle中。
为了启用Tree Shaking,需要在webpack配置文件中添加如下设置:
确保你使用的是ES6模块语法(即import和export),因为只有这样才能让Tree Shaking发挥作用。
利用缓存:利用Webpack的持久缓存功能,避免重复构建没有变化的代码
使用多进程/多线程构建 :使用thread-loader、happypack等插件可以将构建过程分解为多个进程或线程
使用DllPlugin和HardSourceWebpackPlugin: DllPlugin可以将第三方库预先打包成单独的文件,减少构建时间。HardSourceWebpackPlugin可以缓存中间文件,加速后续构建过程
使用Tree Shaking: 配置Webpack的Tree Shaking机制,去除未使用的代码,减小生成的文件体积
移除不必要的插件: 移除不必要的插件和配置,避免不必要的复杂性和性能开销
代码分割(Code Splitting):将应用程序的代码划分为多个代码块,按需加载
Tree Shaking:配置Webpack的Tree Shaking机制,去除未使用的代码
压缩代码:使用工具如UglifyJS或Terser来压缩JavaScript代码
使用生产模式:在Webpack中使用生产模式,通过设置mode: production来启用优化
使用压缩工具:使用现代的压缩工具,如Brotli和Gzip,来对静态资源进行压缩
利用CDN加速:将项目中引用的静态资源路径修改为CDN上的路径,减少图片、字体等静态资源等打包
在开发环境中,Webpack是先打包再启动开发服务器,而Vite则是直接启动,然后再按需编译依赖文件。(大家可以启动项目后检查源码Sources那里看到)
这意味着,当使用Webpack时,所有的模块都需要在开发前进行打包,这会增加启动时间和构建时间。
而Vite则采用了不同的策略,它会在请求模块时再进行实时编译,这种按需动态编译的模式极大地缩短了编译时间,特别是在大型项目中,文件数量众多,Vite的优势更为明显。
现代浏览器本身就支持ES Modules,会主动发起请求去获取所需文件。Vite充分利用了这一点,将开发环境下的模块文件直接作为浏览器要执行的文件,而不是像 Webpack 那样先打包,再交给浏览器执行。这种方式减少了中间环节,提高了效率。
当使用 ES Modules 进行开发时,开发者实际上是在构建一个依赖关系图,不同依赖项之间通过导入语句进行关联。
主流浏览器(除IE外)均支持ES Modules,并且可以通过在 script 标签中设置type=module来加载模块。默认情况下,模块会延迟加载,执行时机在文档解析之后,触发DOMContentLoaded事件前。
预构建依赖通常指的是在项目启动或构建之前,对项目中所需的依赖项进行预先的处理或构建。这样做的好处在于,当项目实际运行时,可以直接使用这些已经预构建好的依赖,而无需再进行实时的编译或构建,从而提高了应用程序的运行速度和效率。
在 Webpack 中,当一个模块或其依赖的模块内容改变时,需要重新编译这些模块。
而在 Vite 中,当某个模块内容改变时,只需要让浏览器重新请求该模块即可,这大大减少了热更新的时间。
Monorepo 是一种项目代码管理方式,指单个仓库中管理多个项目,有助于简化代码共享、版本控制、构建和部署等方面的复杂性,并提供更好的可重用性和协作性。Monorepo 提倡了开放、透明、共享的组织文化,这种方法已经被很多大型公司广泛使用,如 Google、Facebook 和 Microsoft 等。
✅ 代码隔离,研发者只需关注自己负责的仓库 ❌ 包管理按照各自owner划分,当出现问题时,需要到依赖包中进行判断并解决。
✅ 一个仓库中多个相关项目,很容易看到整个代码库的变化趋势,更好的团队协作。 ❌ 增加了非owner改动代码的风险
❌ 多个仓库都有自己的 node_modules,存在依赖重复安装情况,占用磁盘内存大。
✅ 多项目代码都在一个仓库中,相同版本依赖提升到顶层只安装一次,节省磁盘内存,
✅ 各项目单独仓库,不会出现代码被误改的情况,单个项目出现问题不会影响其他项目。
❌ 多个项目代码都在一个仓库中,没有项目粒度的权限管控,一个项目出问题,可能影响所有项目。
✅ 仓库体积小,模块划分清晰,可维护性强。 ❌ 多仓库来回切换(编辑器及命令行),项目多的话效率很低。多仓库见存在依赖时,需要手动
,操作繁琐。 ❌ 依赖管理不便,多个依赖可能在多个仓库中存在不同版本,重复安装,npm link 时不同项目的依赖会存在冲突。
✅ 多个项目都在一个仓库中,可看到相关项目全貌,编码非常方便。 ✅ 代码复用高,方便进行代码重构。 ❌ 多项目在一个仓库中,代码体积多大几个 G,
时间较长。 ✅ 依赖调试方便,依赖包迭代场景下,借助工具自动 npm link,直接使用最新版本依赖,简化了操作流程。
❌ 各项目构建、打包、代码校验都各自维护,不一致时会导致代码差异或构建差异。
❌ 多个项目间存在依赖,部署时需要手动到不同的仓库根据先后顺序去修改版本及进行部署,操作繁琐效率低。
✅ 构建性 Monorepo 工具可以配置依赖项目的构建优先级,可以实现一次命令完成所有的部署。
建议采用渐进式架构方案,即对于轻量级 Monorepo 项目,我们初期可以选择 Lerna + pnpm workspace + lerna-changelog,解决了依赖管理、发版管理等问题,为开发者带来便利;随着后续项目迭代,代码变多或多个项目间依赖关系复杂,可以很平滑的接入 Nx 来提升构建打包效率。
Pnpm 比 npm 快的原因在于其优化的文件存储方式、依赖管理方式以及并行下载能力。以下是详细介绍:
Pnpm 使用基于内容寻址的文件系统来存储磁盘上的所有文件,这意味着它不会在磁盘中重复存储相同的依赖包,即使这些依赖包被不同的项目所依赖。这种存储方式使得Pnpm在安装依赖时能够更高效地利用磁盘空间,同时也减少了下载和安装的时间。
Pnpm 在下载和安装依赖时采用了并行下载的能力,这进一步提高了安装速度。
Pnpm 还具有一些其他特性,例如节省空间的硬链接和符号链接的使用,这些都有助于提高其性能。
Lint会对代码做静态分析,检查出其中的一些结构错误或者格式错误。在前端领域中,我们常用的lint就是ESLint,它用于检查JavaScript代码是否符合规则 。
lib/linter/- 这个模块是核心的Linter类,根据配置选项进行代码验证、检查并修复问题。这个文件不做任何文件 I/O,并且完全不与console互动。
其中SourceCode指的是AST(抽象语法树),源代码字符串通过Parser解析成AST,之后ESLint就可以通过AST提供的信息与Rules对比,从而给出代码规范分析的结果,指出错误,并且还可以自动修复。
Estree是一套AST标准,Esprima基于estree标准实现了AST。Acorn,它在Exprima之后出现,也是 estree 标准的实现,但是它速度比 esprima 快,而且支持插件,可以通过插件扩展语法支持。
下面简单介绍下Espree解析器下AST的几个常见的节点,也可以在estree中查看更多详情。
Identifer 是标识符的意思,变量名、属性名、参数名等各种声明和引用的名字,都是Identifer。
statement 是语句,它是可以独立执行的单位,比如 break、continue、debugger、return 或者 if 语句、while 语句、for 语句,还有声明语句,表达式语句等。我们写的每一条可以独立执行的代码都是语句。语句末尾一般会加一个分号分隔,或者用换行分隔。
上面介绍过,ESLint 处理器可以从其他类型的文件中提取 JavaScript 代码,然后让 ESLint 对 JavaScript 代码进行检查,这就是processor的作用之一。例如,对vue类型文件做ESLint检查,processor就派上用场了。更详细的介绍可以看我另外一篇文章Processor
因为rules一直有个小问题,node的parent属性只会在节点被遍历后才能被访问到。为了解决这个问题ESLint延迟执行了Emit,这样node parent属性就可以被访问到了。相关issue:eslint/esli…
这个阶段主要用来对生成的linting problems做一些处理,例如过滤、修改之类的。
对于可以fix的规则在lint检查完后会,linting problems里会有生成的fix信息,用于自动修复问题。
其中range表示范围,text表示替换的内容。结合到一起就是,range内的字符串替换成text即完成修复。
表示替换源码字符串(中index从9到11的内容为;,即替换;;为;,替换后结果如下:
答:因为多个linting problem之间的range也就是替换的范围可能是有重叠的,如果有重叠就放到下一次来修复,下一次修复则会根据当前修复过一次的代码再继续verify,生成linting problems,以此循环直至没有problem可以修复。不过这样的循环最多修复 10 次,如果还有linting problems没修复就不修了。
Babel是一个流行的用于将新版本ES6+代码转换为向后兼容版本(ES5)代码的JavaScript编译器。它还为JSX语法提供了编译支持,还有一些其他插件可用于转换特定类型的代码 。
当 Babel 接收到源代码时,将会调用一个叫做解析器的工具,用于将源代码转换为抽象语法树(AST)。在这个过程中,解析器会识别代码中的语法结构,并将其转换为对应的节点类型。 例如,当解析器遇到一个变量声明语句时,它将会创建一个 “VariableDeclaration” 节点,并将该节点的信息存储在 AST 中。AST 是一个以节点为基础组成的树形结构,每个节点都有相应的类型、属性和子节点等信息。
一旦 AS必博首页T 被创建,Babel 将遍历整个树形结构,对每个节点进行转换。这些转换可以是插件、预设或手动创建的。转换器会检查 AST 中的每个节点,然后对其进行相应的修改或替换,以将新语法转换为旧语法。 例如,如果 Babel 遇到一个包含箭头函数的节点,而你已经启用了转换插件,该插件将会将箭头函数转换为其等效的体函数。代码转换后,Babel 将会生成一个新的 AST。
最后,Babel 将基于转换后的 AST 生成代码文本。在这个步骤中,Babel 将遍历转换后的 AST,并创建对应的代码字符串,并将这些字符串组合成一个完整的 JavaScript 文件。如果启用了代码压缩,Babel 还可以将生成的代码进行压缩。 总结来说,Babel 的原理就是将 JavaScript 源代码转换为抽象语法树(AST),然后对 AST 进行转换,生成与源代码功能相同但向后兼容的代码。Babel 提供了一个强大的生态系统,使得开发者可以轻松扩展并自定义转换器,实现自己的功能需求。
npm run start是一个常见的命令,用于启动基于 Node.js 的应用程序。这个命令实际上是一个快捷方式,它告诉 npm 运行在 package.json 文件中定义的 start 脚本。
这是一个简单的例子,实际的 start 脚本可能会包含更多步骤,比如预处理、打包、转译、加载模块绑定等。
(1)预处理器:为什么要用预处理器?它的出现是为了解决什么问题? 预处理器,其实就是 CSS 世界的“轮子”。预处理器支持我们写一种类似 CSS、但实际并不是 CSS 的语言,然后把它编译成 CSS 代码:
那为什么写 CSS 代码写得好好的,偏偏要转去写“类 CSS”呢?这就和本来用 JS 也可以实现所有功能,但最后却写 React 的 jsx 或者 Vue 的模板语法一样。
宏观设计上:我们希望能优化 CSS 文件的目录结构,对现有的 CSS 文件实现复用;
编码优化上:我们希望能写出结构清晰、简明易懂的 CSS,需要它具有一目了然的嵌套层级关系,而不是无差别的一铺到底写法;我们希望它具有变量特征、计算能力、循环能力等等更强的可编程性,这样我们可以少写一些无用的代码;
可维护性上:更强的可编程性意味着更优质的代码结构,实现复用意味着更简单的目录结构和更强的拓展能力,这两点如果能做到,自然会带来更强的可维护性。
这三点是传统 CSS 所做不到的,也正是预处理器所解决掉的问题。预处理器普遍会具备这样的特性:
它和预处理器的不同就在于,预处理器处理的是 类CSS,而 PostCss 处理的就是 CSS 本身。Babel 可以将高版本的 JS 代码转换为低版本的 JS 代码。PostCss 做的是类似的事情:它可以编译尚未被浏览器广泛支持的先进的 CSS 语法,还可以自动为一些需要额外兼容的语法增加前缀。更强的是,由于 PostCss 有着强大的插件机制,支持各种各样的扩展,极大地强化了 CSS 的能力。
提高 CSS 代码的可读性:PostCss 其实可以做类似预处理器能做的工作;
当我们的 CSS 代码需要适配低版本浏览器时,PostCss 的 Autoprefixer 插件可以帮助我们自动增加浏览器前缀;
允许我们编写面向未来的 CSS:PostCss 能够帮助我们编译 CSS next 代码;
另外作者也在找工作,欢迎公司有HC的同学内推,base地:深圳、广州或长沙。