webpack学习笔记

webpack构建流程:

  1. 初始化参数,从配置文件和shell语句中读取和合并参数,得到最终的参数
  2. 开始编译,用上一步得到的参数初始化compiler这个对象,加载所有配置的插件,执行对象的run方法
  3. 确定入口,根据配置中的 entry 找出所有的入口文件
  4. 编译模块:从入口文件出发,调用所有配置的Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理
  5. 完成模块编译:在经过第四步使上oader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系
  6. 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
  7. 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统,在以上过程中,webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用

基础配置

plugins:可以在webpack运行到某个阶段时帮我们做一些事情,可以监听事件并执行特定的功能,有点像vue的生命周期
webpack.config.js中

const path = requeire('path')

module.exports = {
	mode:'development',
	entry:path.join(__dirname,'src','index.js')//把哪个文件作为打包入口
	output: {
		publicPath:'http://cdn.xxx.com/',
		path: path.join(__dirname,'dist')//把打包好的文件放在哪个路径,一般是dist
		filename:'boundle.js'//也可以用占位符[name].js·
	},
	plugins:[
		new HtmlWebpackPlugin({//打包完毕之后,帮忙自动生成html文件,再把打包后的结果注入到html之中
			template:path.join()__dirname,'src','index.html'),//把src底下index.html作为模板文件来打包,把打包好的js自动放进html里
			filename:'boundle.js'
		})
		new CleanWebpackPlugin()//自动删除dist文件夹
	],
	module:{//解析模块的规则
		rules:[
			{
				test:/\.js$/,
				loader:['babel-loader],//es6转化为es5
				include:path.join(__dirname,'src'),//只处理哪些代码
				exclude:/node_modules/,   //排除哪些代码
			}
		]
	},
	devServer: {
		port:8000,
		static:path.join(__dirname,'dist)//把dist目录作为静态资源的文件夹
	}
	
}

package.json文件中

'script':{
	"build":"webpack"//输入npm run build,运行webpack打包
	"dev":"webpack-dev-seerver"//实时编译代码,会把打包生成的文件放在内存里
},

.babelrc文件中

{//预设,
	"presets":["@babel/preset-env"]
}

拆分配置,合并配置

项目中常常会遇到,生产环境,测试环境,开发环境配置不同的情况,我们可以提取这些环境中的公共部分,写进webpack.base.config.js
在webpack.dev.config.js和webpack.prod.config.js中引入webpack.base.config.js

const commonConfig = require('./webpack.base.config');
const {smart:merge} = require('webpack-merge');//用smart方法合并配置,改名为merge

const prodConfig = {
	mode:'production'
}

module.exports = merge(commonConfig,prodConfig)
"scripts": {
	"build:dev":"webpack --config ./build/webpack.dev.config.js",//开发
	"build":"webpack --config ./build/webpack.prod.config.js",//生产

loader

Loader 是用于对模块的源代码进行转换的插件。
webpack本身是不认识图片,css,vue之类的东西的,只认识js,所以我们需要引入loader来处理各种模块

处理图片

webpack.dev.config.js

//开发环境
const devConfig = {
	module:{//解析模块的规则
		rules:[
			{
				test:/\.(png|jpg)$/,
				loader:'file-loader ',//把图片移到dist目录下,直接访问url来拿图
			}
		]
	},
}
//生产环境
const prodConfig = {
	module:{//解析模块的规则
		rules:[
			{
				test:/\.(png|jpg)$/,
				use:{
					loader:'url-loader ',//小图转化为base64,减少一次http请求
					options:{//大图依然像file-loader一样,单独打包到img文件夹里,发请求,防止页面首次渲染时间太慢
						limit: 5*1024
					}
				}
			}
		]
	},
}

index.js

import axios from 'axios'
axios.get('api/Yixiantong/getHomeDatas')//把api/Yixiantong rewrite成...
	.then(({data})=>{console.log(data)})

样式

webpack不能直接处理css
webpack.dev.config.js中

module.exports = {
	module:{//解析模块的规则
		rules:[
			{
				test:/\.css$/,
				loader:[
					'style-loader',//把样式插进style标签里
					'css-loader',//处理css之间的依赖关系
					'postcss-loader',//处理css3
					'sass-loader',//处理预处理器sass
				]
			},		
		]
	},

posts.config.js

moudle.exports = {
	plugins:[
		require('autoprefixer')

package.json

"browserslist": [//适配这些浏览器
		"> 1%",//选择全球使用率超过 1% 的浏览器版本。
		"last 2 versions"//选择每个浏览器的最近两个版本。
],

plugin

占坑
Plugin可以在Webpack的构建流程中插入自定义的代码,以实现各种功能,如性能优化、代码分割、资源管理等。是对webpack的拓展

UglifyJsPlugin:压缩和混淆JavaScript代码。
OptimizeCSSPlugin:压缩CSS文件。
TerserPlugin:压缩JavaScript代码,替代UglifyJsPlugin。
SplitChunksPlugin:动态创建和分割代码块。
CommonsChunkPlugin:提取公共代码到单独的文件。
FileManagerPlugin:管理和复制文件。
CopyWebpackPlugin:复制文件到输出目录。
ImageminPlugin:压缩图片文件。
VuePlugin:为Vue.js项目提供额外功能。
BundleAnalyzerPlugin:分析打包结果,优化打包体积。
ProgressPlugin:显示构建进度。

webpack-dev-server使用

webpack-dev-server可以用来转发请求
webpack.dev.config.js中

const devConfig = {
	mode:'development',
	devServer:{
		port:8000,//服务器气筒的端口8080
		contentBase:'./dist',//服务器静态资源文件夹
		progress:true,//打包时显示进度条
		open:true,//启动服务器后,自动打开浏览器
		compress:true,//开启gzip压缩
		proxy:{//请求转发
			'/api/Yixiantong': {
				target:'http://study.jsplus.com',
				pathRewite:{
					'^/api':''
				},
				changeOrigin:true
			}
		}
	}
}

多入口

webpack.config.js中

 module.exports = {
 	entry:{
 		index:'./src/index.js',
 		other:'./src.other/js'
 	},
 	output:{
 		filename:'[name].js',//打包后的文件名,[name] 示入口的名称,即 index 或 other。
 		path:path.resolve(__dirname,'../dist')
 	},
	plugins:[
		new HtmlWebpackPlugin({
			template:'./src/index.html',
			filename:'index.html',
			chunks:['index'],//chunk:由模块组成的代码块
		}),
		new HtmlWebpackPlugin({
			template:'./src/other',
			filename:'other.html'.
			chunks:['other'],
		}),
	],

抽离css

我们想在生产环境中,把样式抽离成css文件
webpack.prod.config.js中

const MiniCSSExtractPlugin =  require('mini-css-extract-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const OptimizeCssAssetsWepackPlugin = require('optimize-css-assets-wepack-plugin');

module.exports = {
	module:{
		rules:[
			{
				test:/\.css$/,
				loader:[
					MiniCSSExtractPlugin.loader,//换成minicss
					'css-loader',//处理css之间的依赖关系
					'postcss-loader',//处理css3
					'sass-loader',//处理预处理器sass
				]
			},		
		]
	},
	plugins:[
		new MiniCSSExtractPlugin({//根据文件内容生成的哈希值,只使用哈希值的前8个字符。
			filename:'css/main.[contentHash:8].css'
		}),
	],
	optimization:{
		minimizer:[
			new TerserPlugin(),//压缩js
			new OptimizeCssAssetsWepackPlugin(),//压缩css

这样做的结果就是,原本的css被压缩,比如注释掉的css都没有被打包进去,
css没有被插入进style标签,而是生成了css/main.abcd1234.css文件,并在link中引入

公共代码

  1. 公共模块
    公共模块的代码不需要重复打包,单独抽离成一个公共模块的文件,然后饮用即可
  2. 第三方模块
    第三方模块的代码一般不会轻易改变,不需要在业务代码改变之后再重新打包,单独抽离成一个第三方模块的文件,然后引用即可

一般在生产环境做这样的配置
webpack.prod.config.js中

 module.exports = {
  	entry:{
 		index:'./src/index.js',
 		other:'./src.other/js'
 	},
 	output:{
 		filename:'[name].[conetntHash:8]js',
 		path:path.resolve(__dirname,'../dist')
 	},
	plugins:[
		new HtmlWebpackPlugin({
			template:'./src/index.html',
			filename:'index.html',
			//html使用的js文件
			chunks:['index','vendor','common'],//vendor是第三方模块,common是公共模块
		}),
		new HtmlWebpackPlugin({
			template:'./src/other',
			filename:'other.html'.
			chunks:['other','common'],//common是公共模块
		}),
	],
	optimization:{
		splitChunks:{//代码分割
			chunks:'all'
			//all:对同步异步代码都做代码分割
			//async:只对异步代码做代码分割
			//initial:只对同步代码做代码分割
			//同步代码,例如import lodash from 'lodash'
			//异步代码,例如 import('lodash'),用import函数
			cacheGroups:{
				// 第三方模块
				vendor: {
					// 每个组的名字
					name: 'vendor',
					// 优先级,优先级越高,越先检测处理
					// 第三方模块 可能也会被作为公共模块来检测处理,通过高优先级,达到先被当做 第三方模块 来检测处理
					priority: 1// 检则方法,例如:检测模块是否来自 node_modules
					test: /node_modules/,//来自npm安装
					// 实际开发中,可以写 5*1024,也就是 5kb
					// 但这里为了看到 代码分割 的效果,我们把值沒置到最小,也就是0			  
					minSize: 0// 检测模块被引用了几次
					// 对于 第三方模块而言,引用1次就应该单独打包
					// 对于 公共模块 而言,引用2次以上就应该单独打包
					minChunks: 1,
				},
				// 公共模块
				common: {
					// 每个组的名字
					name: 'common'
					// 优先级,优先级越高,越先检测处理
					priority: 0// 实际开发中,可以写5*1024,也就是5kb
					// 但这里为了看到 代码分割 的效果,我们把值设置到最小,也就是0
				}
		}
	}

sourceMap

建立打包生成的代码与源代码之间的关系,方便改bug

module.exports={
	devtool:'eval-source-map',//开启sourcemap,关闭设为false
	//eval:打包速度最快,转化成eval()语法
	//source-map:把映射关系写入bundle.js.map文件里
	//inline-source-map:把映射关系写入bundle.js文件内最后一行,base64字符串
	//cheap:只记录哪行出错,不记录那列出错,不记录loader第三方模块出啥错

HMR模块热更新

const webpack = require('webpack')
module.exports = {
  // ...
  devServer: {
    // 开启 HMR 特性
    hot: true
    // hotOnly: true
  }
}

//指定模块发生更新的时候进行HRM
if(module.hot){
    module.hot.accept('./util.js',()=>{
        console.log("util.js更新了")
    })
}

原理
通过webpack-dev-server创建两个服务器:提供静态资源的服务(express)和Socket服务
express server 负责直接提供静态资源的服务(打包后的资源直接被浏览器请求和解析)
socket server 是一个 websocket 的长连接,双方可以通信
当 socket server 监听到对应的模块发生变化时,会生成两个文件.json(manifest文件)和.js文件(update chunk)
通过长连接,socket server 可以直接将这两个文件主动发送给客户端(浏览器)
浏览器拿到两个新的文件后,通过HMR runtime机制,加载这两个文件,并且针对修改的模块进行更新

性能优化

webpack的性能优化主要包括两部分,一是打包体积减少,一是打包速度加快
因为webpack构建流程其中有个步骤是所有的模块进行编译处理,那么我们可以在以下几点上做优化处理

  1. 缩小查找文件的范围,用alias,extensions等配置缩小范围
  2. 减少需要解解析的文件,使用noparse配置告诉webpack排除忽略指定文件,不对他们进行解析,
  3. 避免去重复的编译第三方库,打包到单独文件中,不会跟着业务文件一起重新打包,
  4. webpack在对代码进行压缩打包时,如果有多个js文件需要被压缩,她会一个个进行压缩,可以使用thread loader插件来开启多个子进程,采用并行的方式对多个js文件进行压缩打包。

参考:https://www.bilibili.com/video/BV1By4y177gX/?p=7&share_source=copy_web&vd_source=64698271e543eb5149301ff926e69c40

与vite对比

webpack:webpack-dev-server在项目启动时会把所有的文件都build一遍,不管模块是否被执行,都要编译和打包到bundle里
vite:启动的时候不需要打包,不需要拆分模块的依赖,不需要编译
预构建:当你首次启动 vite 时,Vite 在本地加载你的站点之前预构建了项目依赖,保存在node_modules/.vite中,可以减少请求次数
默认情况下,Vite会将package. json中生产依赖dependencies的部分启用依赖预编译,即会先对该依赖进行编译,然后将编译后的文件缓存在内存中 (node_modules/.vite文件下),在启动DevServer时直接请求该缓存内容。
将非 ESM 规范的代码转换为符合 ESM 规范的代码;
将第三方依赖内部的多个文件合并为一个,减少 http 请求数量
预构建这一步由 esbuild 执行,用go编写,go的操作是纳米级,js操作是毫秒级。
浏览器需要某个模块时,发送请求,根据需要对模块的内容进行编译,由于浏览器支持module,会自动的向依赖的module发送请求
热更新时,仅需让浏览器重新请求这个改动了的模块就行(webpack会把所有的模块都重新编译一遍)
注意:依赖预构建仅适用于开发模式,在生产构建中,将使用 @rollup/plugin-commonjs。

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值