webpack的编译过程
#### 初始化
1. 此阶段,webpack会将CLI参数、配置文件、默认配置进行融合,形成一个最终的配置对象。对配置的处理过程是依托一个第三方库 yargs 完成的。此阶段相对比较简单,主要是为接下来的编译阶段做必要的准备目前,可以简单的理解为,初始化阶段主要用于产生一个最终的配置。
#### 编译
1. 创建chunk
1. chunk是webpack在内部构建过程中的一个概念,译为块,它表示通过某个入口找到的所有依赖的统称。根据入口模块(默认为./src/index.js)创建一个chunk
2. 每个chunk都有至少两个属性:
1. name:默认为main
2. id:唯一编号,开发环境和name相同,生产环境是一个数字,从0开始
2. 构建所有依赖模块![](https://file.jishuzhan.net/article/1783482828806164482/cb541aa70dd3f8febe2bafe090ba1895.webp)
3. 产生chunk assets
1. 在第二步完成后,chunk中会产生一个模块列表,列表中包含了模块id和模块转换后的代码。
接下来,webpack会根据配置为chunk生成一个资源列表,即chunk assets资源列表可以理解为是生成到最终文件的文件名和文件内容![](https://file.jishuzhan.net/article/1783482828806164482/660651d6bf3443fc3588cab49f1da630.webp)
4. 合并chunk assets
1. 将多个chunk的assets合并到一起,并产生一个总的hash![](https://file.jishuzhan.net/article/1783482828806164482/2fc1213f901a1fb8d55c87be01d8a15f.webp)
2.
#### 输出
1. 此步骤非常简单,webpack将利用node中的fs模块(文件处理模块),根据编译产生的总的assets,生成相应的文件。![](https://file.jishuzhan.net/article/1783482828806164482/58dd2c58ee163587e068c87aa92f1153.webp)
#### 总过程
1. 当敲下命令后首先开始初始化,即读取命令参数,然后导入配置文件合并配置对象,把形成的配置对象交给编译器,编译器可能有一个chunk,也可能又多个chunk,但至少肯定有一个,每一个chunk里面要构建好自己的模块,它把chunk里面所有的相关模块找到,具体实现方式通过入口文件一个个找,找到相关的模块之后,他会生成一个资源列表,就是最终这个chunk会对应哪些资源生成出来,每个chunk会有自己的id名字和hash,然后把每个chunk生成的资源合并成一个完整的资源,并且生成一个完整的hash,最终根据完整的资源列表输出到文件,因为合并的chunk块记录的很详细,文件名是什么,文件内容是什么,很容易就输出到文件了,这就是整个webpack的编译过程。如果详细的讨论,主要在生成多个模块。![](https://file.jishuzhan.net/article/1783482828806164482/45a930903952bd904f1ef04ed6a1f47c.webp)
设计术语
- module:模块,分割的代码单元,webpack中的模块可以是任何内容的module文件,不仅限于JS。
- chunk:webpack内部构建模块的块,一个chunk中包含多个模块,这些模块是从入口模块通过依赖分析得来的。
- bundle:chunk构建好模块后会生成chunk的资源清单,清单中的每一项就是一个bundle,可以认为bundle就是最终生成的文件
- hash:最终的资源清单所有内容联合生成的hash值
- chunkhash:chunk生成的资源清单内容联合生成的hash值
- chunkname:chunk的名称,如果没有配置则使用main
- id:通常指chunk的唯一编号,如果在开发环境下构建,和chunkname相同;如果是生产环境下构建,则使用一个从0开始的数字进行编号
webpack的核心概念
- Entry:入口,Webpack 执行构建的第一步将从 Entry 开始,可抽象成输入。告诉webpack要使用哪个模块作为构建项目的起点,默认为./src/index.js
- output :出口,告诉webpack在哪里输出它打包好的代码以及如何命名,默认为./dist
- Module:模块,在 Webpack 里一切皆模块,一个模块对应着一个文件。Webpack 会从配置的 Entry 开始递归找出所有依赖的模块。
- Chunk:代码块,一个 Chunk 由多个模块组合而成,用于代码合并与分割。
- Loader:模块转换器,用于把模块原内容按照需求转换成新内容。
- Plugin:扩展插件,在 Webpack 构建流程中的特定时机会广播出对应的事件,插件可以监听这些事件的发生,在特定时机做对应的事情。
Webpack的基本功能有哪些?
- 代码转换:TypeScript 编译成 JavaScript、SCSS 编译成 CSS 等等
- 文件优化:压缩 JavaScript、CSS、html 代码,压缩合并图片等
- 代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载
- 模块合并:在采用模块化的项目有很多模块和文件,需要构建功能把模块分类合并成一个文件
- 自动刷新:监听本地源代码的变化,自动构建,刷新浏览器
- 代码校验:在代码被提交到仓库前需要检测代码是否符合规范,以及单元测试是否通过
- 自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统。
gulp/grunt 与 webpack的区别是什么?
- 三者都是前端构建工具,grunt和gulp在早期比较流行,现在webpack相对来说比较主流,不过一些轻量化的任务还是会用gulp来处理,比如单独打包CSS文件等。
- grunt和gulp是基于任务和流(Task、Stream)的。类似jQuery,找到一个(或一类)文件,对其做一系列链式操作,更新流上的数据, 整条链式操作构成了一个任务,多个任务就构成了整个web的构建流程。
- webpack是基于入口的。webpack会自动地递归解析入口所需要加载的所有资源文件,然后用不同的Loader来处理不同的文件,用Plugin来扩展webpack功能。
怎么配置单页应用?怎么配置多页应用?
- 单页应用可以理解为webpack的标准模式,直接在entry中指定单页应用的入口即可,这里不再赘述。
- 多页应用的话,可以使用webpack的 AutoWebPlugin来完成简单自动化的构建,但是前提是项目的目录结构必须遵守他预设的规范。多页应用中要注意的是:每个页面都有公共的代码,可以将这些代码抽离出来,避免重复的加载。比如,每个页面都引用了同一套css样式表。随着业务的不断扩展,页面可能会不断的追加,所以一定要让入口的配置足够灵活,避免每次添加新页面还需要修改构建配置
Loader机制的作用是什么?
- webpack默认只能打包js文件,配置里的module.rules数组配置了一组规则,告诉 Webpack 在遇到哪些文件时使用哪些 Loader 去加载和转换打包成js。注意:
- use属性的值需要是一个由 Loader 名称组成的数组,Loader 的执行顺序是由后到前的;
- 每一个 Loader 都可以通过 URL querystring 的方式传入参数,例如css-loader?minimize中的minimize告诉css-loader要开启 CSS 压缩。
常用loader
- file-loader 把⽂件输出到⼀个⽂件夹中,在代码中通过相对 URL 去引⽤输出的⽂件。
- url-loader 和 file-loader 类似,但是能在⽂件很⼩的情况下以 base64 的⽅式把⽂件内容注⼊到代码中去。
- source-map-loader 加载额外的 Source Map ⽂件,以⽅便断点调试。
- image-loader 加载并且压缩图⽚⽂件。
- babel-loader 将ES6转化为ES5。
- css-loader 加载 CSS,⽀持模块化、压缩、⽂件导⼊等特性。
- style-loader 把 CSS 代码注⼊到 JavaScript 中,通过 DOM 操作去加载 CSS。
- eslint-loader 通过 ESLint 检查 JavaScript 代码。
- less-loader 可以打包处理.less相关的文件。
- sass-loader 可以打包处理.scss相关的文件。
webpack怎么对vue的编译(快手)
- 说明Vue项目中,Webpack的作用是打包构建Vue组件、资源等,输出浏览器可执行的代码。
- 介绍在Vue项目中,使用vue-loader来解析Vue的单文件组件(.vue文件)。
- 解释vue-loader会将单文件组件的模板编译为render函数,脚本部分编译为JS代码,样式提取为CSS代码。
- 举例说明vue-loader对组件模板的编译过程,使用了@vue/compiler-sfc的compileTemplate方法。
- 概述对组件脚本的处理,会经过babel转译,解析ES6等代码为ES5。
- 描述对样式的处理,使用CSS加载器提取并处理为浏览器可用的CSS。
- 解析出组件之间的依赖关系,输出给Webpack进行模块打包。
- 总结Webpack通过vue-loader解析组件,输出经过优化的浏览器可执行代码。
- 下面就是可以详细回答的内容:
在Vue项目中,Webpack的作用是对代码进行模块打包和构建,最终输出浏览器可以直接执行的JavaScript代码。为了实现这个目的,Webpack需要解析Vue的单文件组件,也就是以.vue结尾的文件。
Webpack通过vue-loader来专门处理Vue组件的解析。vue-loader会将单文件组件分割成三部分,分别是模板代码、脚本代码和样式代码。对于模板部分,vue-loader会使用@vue/compiler-sfc中的compileTemplate方法把模板编译成render函数,这是Vue实际运行时使用的渲染逻辑。对于脚本部分,会使用babel对其进行编译和转换,输出符合ES5标准的JavaScript代码。样式部分则会通过CSS加载器进行处理,最终提取并生成浏览器可用的CSS样式。
在解析组件的过程中,vue-loader还会分析出组件之间的依赖关系,例如组件导入了其它子组件等。这些依赖关系的信息会输出给Webpack,用于其进一步进行模块Resolve和依赖收集,最终生成浏览器可执行的JavaScript代码。
所以,Webpack通过vue-loader解析Vue组件,对不同部分应用不同的加载器进行转换和处理,输出一个优化且浏览器可执行的JavaScript包,以及所需的CSS、资源等,从而实现了对整个Vue项目的构建和打包。这大大简化了Vue项目的开发流程。
## Plugin(插件)的作用是什么?
1. Plugin 是用来扩展 Webpack 功能的,通过在构建流程里注入钩子实现,它给Webpack 带来了很大的灵活性。
2. Webpack 是通过plugins属性来配置需要使用的插件列表的。plugins属性是一个数组,里面的每一项都是插件的一个实例,在实例化一个组件时可以通过构造函数传入这个组件支持的配置属性。
## 常见Plugins
1. HtmlWbpackPlugin自动在打包结束后生成html文件,并引入bundle.js
2. cleanwebPackPlugin打包自动删除上次打包文件
## 如何提高webpack的构建速度?
1. 在多入口情况下使用SplitChunksPlugin来提取公共代码
2. 通过externals配置来提取常用库
3. 利用 DIPlugin和DIReferencePlugin预编译资源模块,通过DilPlugin来对那些我们引用但是绝对不会修改的npm包来进行预编译,再通过DIReferencePlugin将预编译的模块加载进来。
4. Happypack:将loader由单线程转为多线程加速编译
5. 使用webpack-uglify-parallel来提升uglifyPlugin的压缩速度。原理上webpack-uglify-parallel采用了多核并行压缩来提升压缩速度(多核构建项目)
## 如何利用webpack优化前端性能
1. 用webpack优化前端性能是指优化webpack的输出结果,让打包的最终结果在浏览器运行快速高效。
2. 压缩代码,删除多余代码注释,简化代码的写法
1. 可以利用webpack的uglifyjs-webpack-plugin和webpack-parallel-uglify-plugin来压缩JS文件
2. 利用optimize-css-assets-webpack-plugin来压缩css
3. 利用CDN加速
1. 在构建过程中,将引用的静态资源路径修改为CDN上对应的路径。
2. 可以利用webpack对于output参数和各loader的publicPath参数来修改资源路径。
4. 剔除多余代码
1. Tree shaking 摇树优化
1. 可以通过在启动webpack时追加参数--optimize-minimize 来实现
2. CodeSplitting 代码分割
1. 将代码按路由维度或者组件分块(chunk),这样做到按需加载,同时可以充分利用浏览器缓存。
5. 提取公共代码
1. SplitChunksPlugin插件来进行公共模块抽取利用浏览器缓存可以长期缓存这些无需频繁变动的公共代码:
## 文件指纹是什么?
1. 文件指纹是打包之后的文件后缀名。
2. chunkhash:和webpack打包的chunk有关,不同的entry会生出不同的chunkhash。
3. js后缀名:filename:'\[name\]\[chunkhash:8\].js',
4. contenthash:根据文件内容来定义hash,文件内容不变,则其不变。
5. css后缀名:filename:'\[name\]\[contenthash:8\].css',
6. hash:和整个项目构建有关,只要项目文件有修改,整个构建的hash值就会修改。
7. img后缀名:name:'\[name\]\[hash:8\].\[ext\]',
## source map是什么?
1. 将编译,打包,压缩后的代码映射回源码的过程,打包压缩后的代码不具有良好的可读性,想要调试源码就需要soucre map。**线上环境:避免在生产中使用inline-和eval-,因为他们会增加bundle体积大小,并降低整体性能。**
1. hidden-source-map:借助第三方错误监控平台Sentry使用。
2. nosources-source-map:只会显示具体行数以及查看源代码的错误栈。安全性比source map高。
3. source map通过nginx设置将.map文件只对白名单开放(公司内网)。
## 描述一下编写loader或plugin的思路
1. loader像一个"翻译官",把读到的源文件内容转义成新的文件内容,并且每个loader通过链式操作,把源文件一步步翻译成想要的样子。编写loader要遵循单一原则,每个loader只做一种"转义"工作,每个loader拿到的是源文件内容(source),可以通过返回值的方式将处理后的内容输出,也可以调用this.callback()方法,将内容返回给webpack。还可以通过this.async()生成一个callback函数,再用它将处理后的内容输出去。此外webpack还为开发者准备了开发loader的工具函数集------loader-utils。
2. plugin编写比较灵活。webpack在运行的生命周期中会广播出许多事件,plugin可以监听这些事件,在合适的时机通过webpack提供的API改变输出结果。
## 文件监听原理是什么?
1. 在发现源码发生变化时,自动重新构建出新的输出文件。
2. **原理**
1. 轮询判断文件的最后编辑时间是否变化,如果某个文件发生了变化,并不会立刻告诉监听者,而是先缓存起来,等aggregateTimeout后再执行。
3. **方式**
1. 启动 webpack 命令时,带上 --watch 参数。
2. 在配置 webpack.config.js 中设置 watch:true。
4. **缺点**
1. 每次需要手动刷新浏览器。
## 什么是webpack热更新原理?
1. webpack的热更新又称热替换(Hot Module Replacement),缩写为HMR。 这个机制可以做到不用刷新浏览器而将新变更的模块替换掉旧的模块。
2. HMR的核心就是客户端从服务端拉去更新后的文件,准确的说是 chunk diff (chunk 需要更新的部分),实际上 WDS 与浏览器之间维护了一个Websocket,当本地资源发生变化时,WDS 会向浏览器推送更新,并带上构建时的 hash,让客户端与上一次资源进行对比。客户端对比出差异后会向 WDS 发起Ajax请求来获取更改内容(文件列表、hash),这样客户端就可以再借助这些信息继续向 WDS 发起jsonp请求获取该chunk的增量更新。
## 如何对bundle体积进行监控和分析?
1. VSCode中有一个插件Import Cost可以帮助我们对引入模块的大小进行实时监测,还可以使用webpack-bundle-analyzer生成bundle的模块组成图,显示所占体积。
2. bundlesize工具包可以进行自动化资源体积监控。
## Bable原理是什么?
1. 大多数JavaScript Parser遵循estree规范,Babel 最初基于acorn项目(轻量级现代 JavaScript 解析器) Babel大概分为三大部分:
2. 转换
1. 访问 AST 的节点进行变换操作生产新的 AST。
1. 词法分析:将代码(字符串)分割为token流,即语法单元成的数组。
2. 语法分析:分析token流(上面生成的数组)并生成 AST。
3. 生成
1. 以新的 AST 为基础生成代码。Taro就是利用 babel 完成的小程序语法转换。
4. 解析
1. 将代码转换成 AST。