标签:不能 ade star 串行 content 类型 它的 实用 多个
Webpack
是一个模块打包工具,在Webpack
里一切文件皆模块。通过loader
转换文件,通过plugin
注入钩子,最后输出由多个模块组合的文件。Webpack
专注构建模块化项目。Webpack
可以看作是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript
模块以及其他一些不能被浏览器直接运行的扩展语音(如:Scss
,TypeScript
等),并将其打包为合适的格式以供浏览器使用。
Grunt/Gulp
是一种能够优化前端开发流程的工具,而Webpack
是一种模块化的解决方案。
Grunt/Gulp
的工作方式是:在一个配置文件中,指明某些文件进行类似编译/组合/压缩等任务的具体步骤,之后工具可以自动帮你完成这些任务Webpack
的工作方式是:把项目当作是一个整体,通过指定的入口文件,Webpack
会从这个入口文件开始找到项目所有的依赖文件,然后使用loader
处理它们,最后打包成一个或多个浏览器能够识别的JavaScript
文件Grunt/Gulp
需要将整个前端构建过程拆分成多个task
,合理控制所有task
的调用关系Webpack
需要定义好入/出口,并需要清楚对于不同类型资源应该用什么loader解析编译Grunt/Gulp
是基于任务和流(task
和stream
)的。类似jQuery
,找到一个(或一类)文件,对其做一系列的链式操作,更新流上的数据,整条链式操作构成了一个任务,多个任务就构成了整个Web的构建流程。Webpack
是基于入口的。Webpack
会自动的递归解析入口所需要加载的所有资源文件,然后用不同的loader
来处理不同的文件,用pulgin
扩展Webpack
功能。
Grunt/Gulp
更像是后端开发者的思路,需要对整个流程了如指掌。Webpack
更倾向于前端开发者的思路。
Webpack
的运行流程是一个串行的过程,从启动到结束会依次执行以下步骤:
shell
语句中读取与合并参数,得到最终参数;Compiler
对象,加载所有配置的插件,执行对象的run
方法开始执行编译;entry
找出所有的入口文件;loader
对模块进行编译。再找出该模块依赖的模块,再递归本步骤,直到所有入口依赖的文件都经过本步骤的处理;loader
编译完所有模块后,得到每个模块被编译后的最终内容以及它们之间的依赖关系;Chunk
,再把每个Chunk
转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;在以上过程中,Webpack
会在特定的的时间点广播特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑。并且插件可以调用Webpack
提供的API改变Webpack
的运行结果。
Webpack
打包出来的文件webpack
在进行模块的依赖分析的时候,代码分割出来的代码块Webpack
中,一切皆模块,一个模块对应一个文件webpack
打包过程的方式,插件含有apply
属性的JavaScript
对象,apply
属性会被webpack compiler
调用,并且compiler
对象可以在整个编译生命周期内访问loader
直译为“加载器",Webpack
将一切文件视为模块,但是Webpack
原生只能解析JavaScript
和JSON
类型文件。如果想加载解析其他类型文件,就会用到loader
。所以loader
是让Webpack
拥有加载和解析非JavaScript
文件的能力plugin
直译为”插件“,plugin
可以扩展Webpack
的功能,让Webpack
具有更多的灵活性。在Webpack
运行的生命周期中会广播许多事件,plugin
可以监听这些事件,在合适的时机通过Webpack
提供的API
改变输出结果loader
在module rules
中配置,也就说它作为模块解析规则存在。类型为Array
,每一项都是一个Object
,里面描述了什么类型的文件(test),使用什么加载(loader)和使用的参数(options)plugin
单独在plugins
中单独配置。类型为Array
,每项都是一个plugin
的实例,参数是通过构造函数传入base64
的方式将内容注入到代码中ES6
转成ES5
CSS
,支持模块化/压缩/文件导入等特性CSS
代码注入到JavaScript
中,通过DOM
操作去加载CSS
ESlint
检查JavaScript
代码html
入口文件,并引用对应的外部资源JavaScript
代码CSS
文件为了使用tree shaking
,需要满足以下条件:
import
和export
)package.json
文件中,添加sideEffects
入口这种方式是通过package.json
的sideEffects
属性来实现的。
{
"sideEffects": false
}
「副作用」的定义是,在导入时会执行特殊行为的代码,而不是仅仅暴露一个export
或多个export
。举例说明,例如polyfill
,它影响全局作用域,并且通常不提供export
。
注意,任何导入的文件都会受到tree shaking
的影响。这意味着,如果在项目中使用类似css-loader
并导入CSS
文件,则需要将其添加到 side effect 列表中,以免在生产模式中无意中将它删除:
{
"sideEffects": [‘*.css‘]
}
从 webpack 4 开始,也可以通过 "mode" 配置选项轻松切换到压缩输出,只需设置为 "production"。
也可以在命令行接口中使用--optimize-minimize
标记,来使用UglifyjsPlugin
。
code splitting
的必要性
code splitting
,打包后单文件提交较大,加载时长较长,影响用户体验code splitting
,经常修改业务代码,重新打包后,浏览器不能进行缓存,导致性能较差,影响用户体验import _ from ‘lodash‘;
webpack.common.js
配置如下:
....
optimization: {
splitChunks: {
chunks: ‘all‘
}
}
....
配置后,会将公用类库进行打包,生成一个vendors~main.js
文件。
function getComponent() {
return import(‘lodash‘).then(({ default: _ }) => {
var element = document.createElement(‘div‘);
element.innerHTML = _.join([‘Clear‘, ‘love‘], ‘‘);
return element;
})
}
getComponent().then(element => {
document.body.appendChild(element);
})
webpack-dev-server
使用内存来存储Webpack
开发环境下打包的文件,并且可以使用模块热更新,它比传统的http
服务对开发更加简单高效。
模块热更新是Webpack
是的一个功能,它可以使得代码修改以后不需刷新浏览器就可以更新,是高级版的自动刷新浏览器。devServer
通过hot
属性可以控制模块热更替。
const webpack = require(‘webpack‘);
const path = require(‘path‘);
let env = process.env.NODE_ENV == "development" ? "development" : "production";
const config = {
mode: env,
devServer: {
hot:true
},
plugins: [
new webpack.HotModuleReplacementPlugin(), //热加载插件
]
}
module.exports = config;
"script": {
"start": "NODE_EVN=development webpack-dev-server --config webpack-devlop-config.js --hot"
}
Webpack
的热更新有称为热替换(Hot Module Replacement),缩写为HMR
。这个机制可以实现不刷新浏览器而将新变更的模块替换旧的模块。原来如下:
server
端和client
端都做了哪些具体工作:Webpack
的watch
模式下,文件系统中某一个文件发生修改,Webpack
监听到文件变化,根据配置文件对模块重新编译打包,并将打包后的代码通过简单的JavaScript
对象保存在内存中。Webpack-dev-server
和Webpack
之间的接口交互,而在这一步,主要是dev-server
的中间件Webpack-dev-middleware
和Webpack
之间的交互,Webpack-dev-middleware
调用Webpack
暴露的API
对代码变化进行监控,并且告诉webpack
,将代码打包到内存中。Webpack-dev-server
对文件变化的一个监控,这一步不同于第一步,并不是监控代码变化重新打包。当我们在配置文件中配置了devServer.watchContentBase
为true
的时候,Server
会监听这些配置文件夹中静态文件的变化,变化后会通知浏览器端对应用进行live reload
。注意,这儿是浏览器刷新,和HMR
是两个概念。webpack-dev-server
代码的工作,该步骤主要是通过sockjs
(webpack-dev-server 的依赖)在浏览器端和服务端之间建立一个websocket
长连接,将Webpack
编译打包的各个阶段的状态信息告知浏览器端,同时也包括第三步中Server
监听静态文件变化的信息。浏览器端根据这些socket
消息进行不同的操作。当然服务端传递的最主要信息还是新模块的hash
值,后面的步骤根据这一hash
值来进行模块热替换。webpack-dev-server/client
端并不能够请求更新的代码,也不会执行热更模块操作,而把这些工作又交回给了Webpack
,webpack/hot/dev-server
的工作就是根据webpack-dev-server/client
传给它的信息以及dev-server
的配置决定是刷新浏览器呢还是进行模块热更新。当然如果仅仅是刷新浏览器,也就没有后面那些步骤了。HotModuleReplacement.runtime
是客户端HMR
的中枢,它接收到上一步传递给他的新模块的hash
值,它通过JsonpMainTemplate.runtime
向server
端发送Ajax
请求,服务端返回一个json
,该json
包含了所有要更新的模块的hash
值,获取到更新列表后,该模块再次通过jsonp
请求,获取到最新的模块代码。这就是上图中 7、8、9 步骤。HMR
成功与否的关键步骤,在该步骤中,HotModulePlugin
将会对新旧模块进行对比,决定是否更新模块,在决定更新模块后,检查模块之间的依赖关系,更新模块的同时更新模块间的依赖引用。HMR
失败后,回退到live reload
操作,也就是进行浏览器刷新来获取最新打包代码。使用最新稳定版本的webpack
、node
、npm
等,较新的版本更够建立更高效的模块树以及提高解析速度。
由于loader
对文件的转换操作很耗时,所以需要让尽可能少的文件被loader
处理。我们可以通过以下3方面优化loader
配置:
cacheDirectory
选项开启缓存include
、exclude
来减少被处理的文件// webpack.common.js
module: {
rules: [
{
test:/\.js$/,
//babel-loader支持缓存转换出的结果,通过cacheDirectory选项开启
loader:‘babel-loader?cacheDirectory‘,
//只对项目根目录下的src 目录中的文件采用 babel-loader
include: [path.resolve(‘src‘)],
//排除 node_modules 目录下的文件,node_modules 目录下的文件都是采用的 ES5 语法,没必要再通过 Babel 去转换
exclude: path.resolve(__dirname, ‘node_modules‘)
}
]
}
Webpack 4
移除了CommonsChunkPlugin
取而代之的是两个新的配置项optimization.splitChunks
和optimization.runtimeChunk
来简化代码分割的配置。
通过设置 optimization.splitChunks.chunks: "all"
来启动默认的代码分割配置项。
当满足如下条件时,webpack 会自动打包 chunks:
node_modules
30kb
, 如果此模块是按需加载,并行请求的最大数量小于等于5optimization: {
splitChunks: {
chunks: ‘async‘, // all async initial 是否对异步代码进行的代码分割
minSize: 30000, // 引入模块大于30kb才进行代码分割
maxSize: 0, // 引入模块大于Xkb时,尝试对引入模块二次拆分引入
minChunks: 1, // 引入模块至被使用X次后才进行代码分割
maxAsyncRequests: 5, //
maxInitialRequests: 3,
automaticNameDelimiter: ‘~‘, // 模块间的连接符,默认为"~"
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10 // 优先级,越小优先级越高
},
default: { // 默认设置,可被重写
minChunks: 2,
priority: -20,
reuseExistingChunk: true // 如果本来已经把代码提取出来,则重用存在的而不是重新产生
}
}
}
}
减少编译的整体大小,以提高构建性能。尽量保持chunks
小巧。
thread-loader
可以将非常耗性能的loaders转存到worker pool
中。
不要使用太多的workers
,因为Node.js
的runtime
和loader
有一定的启动开销。最小化workers
和主进程间的模块传输。进程间通讯(IPC)是非常消耗资源的。
对于一些性能开销较大的loader
之前可以添加cache-loader
,启用持久化缓存。
使用package.json
中的postinstall
清楚缓存目录。
使用DllPlugin
将更新不频繁的代码进行单独编译。这将改善引用程序的编译速度。即使它增加了构建过程的复杂度。
利用DllPlugin
和DllReferencePlugin
预编译资源模块, 通过DllPlugin
来对那些我们引用但是绝对不会修改的npm
包来进行预编译,再通过DllReferencePlugin
将预编译的模块加载进来。
以下几步可以提高解析速度:
resolve.modules
、resolve.extensions
、resolve.mainFiles
、resolve.desciriptionsFiles
中类目的数量,因为它们会增加文件系统的调用次数。symlinks
,可以设置resolve.symlinks: false
plugins
,并且没有指定context
信息,可以设置resolve.cacheWithContext: false
以下几个实用的工具通过在内存中进行代码的编译和资源的提供,但并不写入磁盘来提高性能:
webpack-dev-server
webpack-hot-middleware
webpack-dev-middleware
需要注意在不同的devtool
的设置,会导致不同的性能差异。
eval
具有最好的性能,但不能帮你转义代码cheap-source-map
选择来提高性能eval-source-map
配置进行增量编译 在大多数情况下,cheap-module-eval-source-map
是最好的选择。
某些实用工具,plugins
和loaders
都只能在构建生产环境时才使用。例如,在开发时使用UglifyJsPlugin
来压缩和修改代码是没有意义的。以下这些工具在开发中通常被排除在外:
UglifyJsPlugin
ExtractTextPlugin
[hash]/[chunkhash]
AggressiveSplittingPlugin
AggressiveMergingPlugin
ModuleConcatenationPlugin
webpack
只会在文件系统中生成已更新的chunk
。应当在生成入口chunk
时,尽量减少入口chunk
的体积,以提高性能。
不要为了非常小的性能增益,牺牲了你应用程序的质量!!请注意,在大多数情况下优化代码质量,比构建性能更重要。
当进行多个编译时,以下工具可以帮助到你:
parallel-webpack
: 它允许编译工作在woker
池中进行。cache-loader
: 缓存可以在多个编译之间共享。项目中的preset/plugins
数量最小化
fork-ts-checker-webpack-plugin
进行类型检查loaders
时跳过类型检查ts-loader
时,设置happyPackMode: true
以及 transpileOnly: true
node-sass
中有个来自Node.js
线程池的阻塞线程的bug。当使用thread-loader
时,需要设置workParallelJobs: 2
用Webpack
优化前端性能是指优化Webpack
输出结果,让打包的结果在浏览器运行快速高效。
Webpack
的UglifyJsPlugin
和ParallelUglifyPlugin
来压缩JavaScript
代码。利用css-loader?minimize
来压缩CSS
imagemin-webpack-plugin
等图片资源压缩插件,对引用的图片资源进行压缩处理url-loader
加载解析图片资源时,可以通过配置options limit
参数,将较小的图片资源转换成base64
格式,减少http
请求CDN
加速。在构建过程中,将引用的静态资源路径修改为CDN
上对应的路径。可以利用Webpack
对于output
参数和各个loader
的publicPath
参数来修改资源路径Webpack
时追加参数--optimize-minimize
来实现CommonJS
模块化规范,所以打包后的最后结果也要支持该规则npm
模块使用者的环境是不确定的,很有可能并不支持ES6
,所以打包的最后结果应该是采用ES5
编写的。并且如果ES5
是经过转换的,请最好连同SourceMap
一同上传npm
包大小应该是尽量小(有些仓库会限制包大小)CommonJS
模块化规范的解决方案: 设置output.libraryTarget=‘commonjs2‘
使输出的代码符合CommonJS2
模块化规范,以供给其它模块导入使用babel-loader
把ES6
代码转换成ES5
的代码。再通过开启devtool: ‘cheap-module-eval-source-map‘
输出SourceMap以发布调试npm
包大小尽量小的解决方案:Babel
在把ES6
代码转换成ES5
代码时会注入一些辅助函数,最终导致每个输出的文件中都包含这段辅助函数的代码,造成了代码的冗余。解决方法是修改.babelrc
文件,为其加入transform-runtime
插件npm
模块中的解决方案:使用externals
配置项来告诉Webpack
哪些模块不需要打包const ExtractTextPlugin = require(‘extract-text-webpack-plugin‘);
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({ use: [‘css-loader‘] }) // 提取出chunk中的css到单独的文件中
}
]
},
plugins: [
new ExtractTextPlugin({ filename: ‘index.css‘ })
]
}
标签:不能 ade star 串行 content 类型 它的 实用 多个
原文地址:https://www.cnblogs.com/vagrant2814154894/p/12320729.html