webpack
webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个 依赖图(dependency graph),然后将你项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源,用于展示你的内容。 一个简单的webpack配置由五部分组成:入口,出口,loader,plugin,model
入口
入口表示一个webpack程序 该重那个文件开始执行,一般分为单入口和多入口。
//单入口
module.exports={
entry:'index.js'
}
modules.exports={
entry:['index.js','index1.js']
}
//多入口
//多入口就对多出口,用于分离自身引用和第三方应用,有利于快速的提高项目的性能
module.exports={
entry:{
main:'index.js',
others:"other.js"
},
output:{
filename:'[name].[hash:6].js',//多出口
path:path.resolve(__dirname__,'dist')
}
}
出口
出口就是通过webpack打包后生成的文件,我们可以通过配置,去设置输出文件的名称结构,文件地址等
module.exports={
entry:{
main:'index.js',
others:"other.js"
},
output:{
filename:'[name].[hash:6].js',//多出口
path:path.resolve(__dirname__,'dist')
}
}
loader
loader可以对于程序中的源代码进行转换,将浏览器或者webpack不能识别的文件类型或者语言转换成可以识别的文件,比如es6语法,less,scss语法,图片转换等。 常见的loader使用:
- css-loader、style-loader
module.export={
entry:'index.js',
output:{
filename:"index.js",
path:path.resolve(__dirname__,'dist')
},
module:{
rules:[
{test:/\.css/,
use:['style-loader' ,/解析js中的css,通过创建sytle标签的形式去 插入到html中
'css-loader',//用户将css解析到js中,还可以通过css-loader的配置去开启css的
//模块化
//{
//loader:"css-loader",
//options:{
//module:true
//}
//}
]
}
]
}
}
- 使用scss-loader,less-loader
//scss-loader
module.exports={
module:{
rules:[
{test:/\.scss/,use:[
'style-loader','css-loader','scss-loader'
]}
]
}
}
//less-loader
module.exports={
module:{
rules:[
{test:/\.less/,use:[
'style-loader','css-loader','less-loader'
]}
]
}
}
- 使用postcss-loader
module.exports={
module:{
rules:[
rules:[
{test:/\.css/,use:[
'style-loader','css-loader','postcss-loader'
]}
]
]
}
}
plugin
可以对于webpack中的功能进行扩展,即loader不能满足开发需求的时候使用。创建的plugin有: HTMLWebpackPlugin,LimitChunkCountPlugin,HotModuleReplacementPlugin,cleanwebpackPlugin,copyWebpackPlugin,MinicssExtractWbebpackPlugin,OptimizeCSSPlugin,CompressionWebpackPlugin
- webpackPlugin
const htmlwebpackplugin= require('html-webpack-plugin')
const {cleanWebpackPlugin} = require('clean-webpack-plugin')
modules.export={
plugins:[
new cleanWebpackPlugin(),
new HtmlwebpackPlugin({
filename:"index.html", //生成的html的名称
template:"./src/index.html",用哪个模板shegnchegnhtml
})
]
}
2.静态资源的拷贝(copy-webpack-plugin)
plugins:[
new CopyWebpackPlugin({
patterns:[{from:''移动的文件地址'',to:''移动的目标地址""},]
})
]
3.webpack的内置设置全局变量的插件(ProvidePlugin)
const webpack = require('webpack')
plugins:[
new webpack.ProvidePlugin({
vue:'vue',
_map:['loadsh','map'] //取单个属性,第一参数是module 第二是propoty
})
]
4.css样式抽离
const miniCssExarctPlugin=require('mini-css-exarct-plugin')
module:{
rules:[
test:/\.css$/,
use:[miniCssExarctPlugin.loader,'css-loader',{loader:"postcss-loader",options:{plugins:function(){return [require('autoprefixer')({overrideBrowserslist}:['defaults'])]}}}]
]
}
plugins:[new miniCssExarctPlugin({filename:"css/[name].css"})]
5.css样式压缩
const OptimizeCssPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
entry: './src/index.js',
//....
plugins: [
new OptimizeCssPlugin()
],
}
6.多入口打包
export.modules={
entry:{
index:"./index.js",
login:"./login.js"
},
output:{
filename:"[name].[hash:8].js",
path:path.resolve(__dirname,'dist')
},
plugins:[
new HtmlWebpackPlugin({
template:"./index.html", //打包的模板
filename:"index.html" , //打包后的文件名称
chunks:['index'] //指定打包后的html加载那个打包后的文件 ,不指定则全部加载
}),
new HtmlWebpackPlugin({
template:"./login.html", //打包的模板
filename:"login.html", //打包后的文件名称
chunks:['login']
}),
]
}
7.定义环境变量(webpack.DefinePlugin)
new webpack.DefinePlugin({
'_DEV': 'dev' , //字符串
'_FLAG': 'true' //FLAG 是个布尔类型
}),
//index.js
if(_DEV=='dev'){
//逻辑
}
8.html压缩
new HtmlWebpackPlugin({
template:"./index.html", //打包的模板
filename:"index.html" , //打包后的文件名称
minify:{
removeComments:true//移除html中的注释
collapseWhitespace:true //移除空白
minifycss:true //压缩内联css
}
}),
9.js压缩(webpack5自带)
webpack4
// 引入 ParallelUglifyPlugin 插件
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
module.exports = {
plugins: [
// 使用 ParallelUglifyPlugin 并行压缩输出JS代码
new ParallelUglifyPlugin({
// 传递给 UglifyJS的参数如下:
uglifyJS: {
output: {
/*
是否输出可读性较强的代码,即会保留空格和制表符,默认为输出,为了达到更好的压缩效果,
可以设置为false
*/
beautify: false,
/*
是否保留代码中的注释,默认为保留,为了达到更好的压缩效果,可以设置为false
*/
comments: false
},
compress: {
/*
是否在UglifyJS删除没有用到的代码时输出警告信息,默认为输出,可以设置为false关闭这些作用
不大的警告
*/
warnings: false,
/*
是否删除代码中所有的console语句,默认为不删除,开启后,会删除所有的console语句
*/
drop_console: true,
/*
是否内嵌虽然已经定义了,但是只用到一次的变量,比如将 var x = 1; y = x, 转换成 y = 5, 默认为不
转换,为了达到更好的压缩效果,可以设置为false
*/
collapse_vars: true,
/*
是否提取出现了多次但是没有定义成变量去引用的静态值,比如将 x = 'xxx'; y = 'xxx' 转换成
var a = 'xxxx'; x = a; y = a; 默认为不转换,为了达到更好的压缩效果,可以设置为false
*/
reduce_vars: true
}
}
}),
]
}
ParallelUglifyPlugin插件还有其他属性:
test: 使用正则去匹配哪些文件需要被 ParallelUglifyPlugin 压缩,默认是 /.js$/.
include: 使用正则去包含被 ParallelUglifyPlugin 压缩的文件,默认为 [].
exclude: 使用正则去不包含被 ParallelUglifyPlugin 压缩的文件,默认为 [].
cacheDir: 缓存压缩后的结果,下次遇到一样的输入时直接从缓存中获取压缩后的结果并返回,cacheDir 用于配置缓存存放的目录路径。默认不会缓存,想开启缓存请设置一个目录路径。
workerCount: 开启几个子进程去并发的执行压缩。默认是当前运行电脑的 CPU 核数减去1。
sourceMap: 是否为压缩后的代码生成对应的Source Map, 默认不生成,开启后耗时会大大增加,一般不会将压缩后的代码的
sourceMap发送给网站用户的浏览器。
devtool
devtool是将我们源码和打包后的代码做一个映射,devtool有如下几种值
- eval:速度最快,使用eval包裹模块代码,
会显示对应源文件的行数
- souce-map :产生.map的文件。
会显示行列信息
- hidden-source-map:和souce-map类型 但是不会和源码关联
只显示打包后输出信息所有行数
- cheap:较快不包含列信息
- module:第三方的模块 包含loader中的sourcemap(jsx babel的sourecemap)
- inline:将.map作为dataUrl嵌入到打包后的文件, 不会单独的生成.map文件 造成bundle的文件过大
会显示输出信息所在行数
这几种值我们也可以组合使用
//开发环境建议
devtool:cheap-module-eval-sourece-map//souce-map
//生产环境不建议开启
devServer
我们可以通过webpack-dev-server 创建的小型服务,通过小型服务可以去访问我们的项目页面。
- 我们通过npx webpack server 去开启服务
//让服务运行在9000端口 同时打开浏览器
npx webpack server --port 9000 --open
- 通过webpack.config.js 中的devServer 进行设置
module.exports={
devServer:{
post:9000,
open:true,
compress:true//开启压缩
}
}
resolve
resolve中我们可以配置我路径的别名,第三方模块的查找路径 以及我们引入的文件从那些后缀开始解析。
export.modules={
resolve:{
module:[],//配置第三方模块的查找地址,默认node_module
alias:{//配置别名
'@':'./src'
},
extensions:['.web.js','.wx.js','js'] //导入文件的时候可以省略后缀,程序自动查找 按照extensions 的顺序查找。
}
}
其他指令参考webpack-dev-server官网
webpack中的性能优化
1.speed-measure-webpack-plugin 插件可以查看打包时各个插件loader的执行时间
const SpeedMeasurePlugin=require('speed-measure-webpack-plugin')
const smp=new SpeedMeasurePlugin()
const config={
entry:{},
output:{},
module:{},
plugins:{}
}
module.expores=smp.warp(config)
//打包的时候可以看到 各个插件和loader的时间
2.exclude和include exclude的优先级比include的优先级高。但是在使用的时候建议使用include 减少转译时候文件资源的加载数量
module:{
rules:[{
test:/.jsx/,
use:['babel-loader'],
include:[path.reslove(__dirname,'src')]
}]
}
3.cache-loader 通过cache-loader 将资源加载到磁盘 后续构建的时候直接从磁盘中读取,添加在要缓存loader的前面
module:{
rules:[{
test:/.jsx/,
use:['cache-loader','babel-loader'],
include:[path.reslove(__dirname,'src')]
}]
}
4.happypack(项目复杂的时候使用) 通过happypack的方式开启webpack同一时间节点开启多个任务提高 构建速度
const Happypack = require('happypack');
module:{
rules:[{
test:/.jsx/,
use: 'Happypack/loader?id=js',//id是唯一的标识
include:[path.reslove(__dirname,'src')]
},{
test:/.css/
use: 'Happypack/loader?id=css',//id是唯一的标识
}]
}
plugins:[
new Happypack({
id: 'js', //和rule中的id=js对应
//将之前 rule 中的 loader 在此配置
use: ['babel-loader'] //必须是数组
})
new Happypack({
id: 'css', //和rule中的id=css对应
//将之前 rule 中的 loader 在此配置
use: ['style-loader','css-loader','postcss-loader'] //必须是数组
})
]
//注意如果css中加载了postcss-loader的时候 则要在项目根路径下添加postcss.config.js
//postcss.config.js
module.exports = {
plugins: [
require('autoprefixer')()
]
}
5.thread-loader(开启单独的工作池,和happypack类似) thread-loader的使用是放在要被优化的loader之前,优化的loader不能产生新文件,不能使用定制的loader api 优化的loader无法获取webpack的选项
npm i thread-loader -D
module:{
rules:[{
test:/.jsx/,
use:[''thread-loader','cache-loader','babel-loader'],
include:[path.reslove(__dirname,'src')]
}]
}
6.HardSourceWebpackPlugin(中间缓存) 首次构建时间不变后续构建时间提高80%
//webpack.config.js
var HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
module.exports = {
//...
plugins: [
new HardSourceWebpackPlugin()
]
}
7.noParse module.noParse 可以是的我们将不需要转换和解析的第三方模块(该模块内部没有amd规范comnand)进行排除,这样提高了webpack的构建速度
module.exports={
module:{
noParse:/vue|loadsh/
}
}
8.externals (通过cdn手动引入,通过import 在程序中引入(得下载对应的依赖)但是不走webpack的打包,也就是我们可以正常的去编码,后续打包的时候不讲我们使用的依赖打包 直接使用cdn)
<html>
<head>
</head>
<body>
<script src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
</body>
</html>
module.exports={
externals:{
//cdn引入后全局有个jQuery
jquery:'jQuery'
}
}
8.抽离公共公共代码 optimization.splitChunks
module.exports={
optimization:{
concatenateModules: false,
splitChunks: {//分割代码块
maxInitialRequests:6, //默认是5
cacheGroups: {
vendor: {
//webpack默认第三方依赖 进vendor缓存组
priority: 1,
name: 'vendor',
test: /node_modules/,
chunks: 'initial',
minSize: 100,
minChunks: 1 //重复引入了几次
},
'lottie-web': { //自定义缓存组
name: "lottie-web", // 单独将 react-lottie 拆包
priority: 5, // 权重需大于`vendor`
test: /[/]node_modules[/]lottie-web[/]/, //分割代码的地址
chunks: 'initial', //
minSize: 100,
minChunks: 1 //重复引入了几次
},
default:{ //默认缓存组,防止重复的代码
}
}
}
}
9.自身的tree-shaking
babel7
babel是代码解析和语法转换的工具 可以将es的高级语法转成向后兼容的语法,以适配当前的浏览器或者旧版本的浏览器。 babel的作用:1.语法转换,2.ployfill 提供不同浏览器对于es6语法的缺失的环境,3源码转换 使用babel的时候就下载@babel/core 这是babel的核心库 @babel/cli 为babel提供了babel的命令 可以再cli中使用babel这个命令。 通过预设或者插件的方式让babel的能加完善或者满足的我们实际的开发需求.babel的配置文件为.babelrc
{
“presets”:['@babel/preset-env'],
plugins:['@babel/plugin-transform-runtime']
}
//browerslistrc 添加我们适配的浏览器 超过90%的用户使用的浏览器
>0.25%
not dead
@babel/preset-env 是对于我们使用的浏览器中缺失的功能进行polyfill 使得我们的浏览器很好的对于高级的语法的支持 @babel/polyfill 则是为不识别转换后的高级语法 提供识别环境(但是会造成污染全局环境)
//webpack入口添加 或者再入口js中引用
entry:[
require.solve('./polyfills'),
path.resove(./index.js)
]
//polyfills
import '@/babel-polyfill‘ //这种引入的方式会造成 将所有的polyfill的包都引进来浪费资源
polyfill/按需引入
//.babelrc
{
“presets”:[['@babel/preset-env',{
"useBuiltIns":"usage", //使用的时候 babel检查代码按需引入,这种方式也需要下载@babel/polyfill
"corejs":3
}]],
上面的方式 已经很好的解决了 老旧版本浏览器对于es6语法的支持,以及 浏览器环境缺失高级语法转换环境的环境补充,以及解析后输出文件体积过大的问题,但是,现在还有一个问题就是 我们代码中使用了多个一样的高级语法 就会出现inject多次,比如 我们使用了10此次 class 则再编译转换的时候 会inject10 这显然是不合适的 @/babel/plugin-transform-runtime(开发)(@babel/runtime 生产) 解决了这个问题,可以重复使用注入的帮助程序
//.babelrc
{
"presets":[['@babel/preset-env'],{
"useBuiltIns":'usage', //按需引入
"corejs":3
}],
plugins:[
["@babel/plugin-transform-runtime",{
"corejs":3
}],"@/babel-plugin-react" //解析转换jsx语法
]
}
//插件和预设
插件在预设之前,插件的执行是从前往后,预设是从后往前
代码分隔和异步加载补充
代码分隔分为
- 通过入口
entry:{
index:"./index.js",
about:"./aboout.js"
},
output:{
filename:"[name].js",
path:path.resolve(paht.__dirname__,'dist')
}
2.防止重复
entry:{
index:"./index.js",
},
output:{
filename:"[name].js",
path:path.resolve(paht.__dirname__,'dist')
},
opizization:{
splitChunks:{
chunks:'all'
}
}
vue组件中的异步路由和加载方式
我们通过import的方式去加载动态路由 点击对应的路由会加载对应的chunks
webpack中的配置
outpath:{
path:path.resolve(__dirname__,'dist')
filename:"[name].js"
chunkFilename:"[name].js"
},
optimization: {
splitChunks: {
chunks: 'all',
},
},