webpack复习

webpack

webpack复习

webpack基本配置

拆分配置 - 公共配置 + 生产环境配置 + 开发环境配置 使用merge

webpack-dev-server 启动本地服务

在公共中引入babel-loader处理es6

webpack高级配置

多入口文件

enty 入口为一个对象 里面的key为入口名 value为入口文件路径 例如 path.join(srcPath, 'index.js')

output 输出打包文件 也是一个对象 filename使用 [name].[contentHash:8].js 动态命名 path为打包到哪个路径

使用hash做文件名 是因为当文件内容没有改变的时候 hash是不变的 文件名不变 引入的时候就会走缓存 页面加载快

javascript 复制代码
entry: {
	index: path.join(srcPath, 'index.js'),
	other: path.join(srcPath, 'other.js')
},
output: {
	filename: '[name].[contentHash:8].js',
	path:distPath
}

plugins 中的new HtmlWebpackPlugin生成html文件 此时也要写多个

template表示打包的文件

filename表示打包后的名称

chunks表示使用哪个入口文件来打包 如果不设置 则所有入口文件都会被引入

javascript 复制代码
plugins: [
	new HtmlWebpackPlugin({
		template: path.join(srcPath, 'index.html'),
		filename: 'index.html',
		chunks: ['index']
	}),
	new HtmlWebpackPlugin({
		template: path.join(srcPath, 'other.html'),
		filename: 'other.html',
		chunks: ['other']
	})
]

抽离css

使用MiniCssExtractPlugin 替换 style-loader 将css抽离为一个单独的css文件

设置 new MiniCssExtractPlugin 抽离css

optimization 用于优化打包结果的对象

javascript 复制代码
module: {
	rules:{
		{
			test: /\.less$/,
			loader: [
				MiniCssExtractPlugin.loader,
				'css-loader',
				'less-loader',
				'postcss-loader'
			]
		}
	}
},
plugins: [
	new MiniCssExtractPlugin({
		filename: 'css/main.[countenHash:8].css'
	})
],
optimization: {
	// 压缩css
	minimizer: [
    new TerserJSPlugin(),
    new OptimizeCSSAssetsPlugin()
  ]
}

抽离公共代码

抽离打包时公共的包例如lodash

javascript 复制代码
optimazation:{
	// 分割代码块
	splitChunks:{
		chunks: 'all',
		/*
			initial 入口chunk 对于异步导入的文件不处理
			async 异步chunk 只对异步导入的文件处理
			all 全部thunk
		*/
		// 缓存分组
		cacheGroups: {
			// 第三方模块
			vendor: {
				name: 'vendor', //chunk 名称
				priority: 1, //权限更高 优先抽离 重要!!!
				test: /node_modules/, // 命中条件
				minSize: 0, //大小限制 太小就不用单独打包了
				minChunks: 1, //最少重复用过几次 公共模块复用过一次就要单独打包 因为公共组件一般都很大 
			},
			// 公共的模块
			common: {
				name: 'common', //chunk名称
				priority: 0, //优先级
				minSize: 0, //公共模块大小限制
				minChunks: 2 // 公共模块最少复用几次
			}
		}
	}
}

懒加载

import 语法

处理jsx和处理vue

对于jsx配置babel

.babelrc

javascript 复制代码
{
	"presets": ["@babel/preset-env"]
}

对于vue

使用vue-loader

module chunk bundle的区别

module - 各个源码文件 webpack中一切皆模块

chunk - 多模块合并成的 如entry import() splitChunk

bundle - 最终的输出文件

webpack性能优化

优化构建速度

优化babel-loader

开启缓存 只要是es6代码没有改的 就不会在重新编译 就会缓存 在第二次进行编译的时候 针对没有改的部分 启用缓存就不会重新编译

include 和 exclude 确定范围

javascript 复制代码
{
	test: /\.js$/,
	use: ['babel-loader?cacheDirectory'], //开启缓存
	include: path.resolve(__dirname, 'src'), //明确范围
	// 排除范围 include 和 exclude 两着选一个即可
	exclude: path.resolve(__dirname, 'node_modules')
}

IgnorePlugin 避免引入无用模块

import moment from 'moment'

默认会引入所有语言JS代码 代码过大

javascript 复制代码
plugins: [
	// 忽略moment下的/locale目录
	new webpack.IgnorePlugin(/\.\/locale/, /moment/)
]
javascript 复制代码
import moment from 'moment'
import 'moment/locale/zh-cn' // 手动引入中文包 

noParse 避免重复打包

javascript 复制代码
module.exports = {
	module: {
		// 忽略对 `react.min.js` 文件的递归解析处理
		noParse: [/react\.min\.js$/],
	}
}

IgnorePlugin 直接不引入 代码中没有 而且优化产出体积

noParse引入 但不打包

happyPack多进程打包

JS单线程 开启多进程打包

提高构建速度(特别是多核CPU)

javascript 复制代码
module: {
	rules: [
		//js
		{
			test: /\.js$/,
			// 把对.js文件的处理转交给id为babel的HappyPack实例
			use: ['happypack/loader?id=babel'],
			include: srcPath,
		}
	]
},
plugins:[
	// happyPack 开启多进程打包
	new HappyPack({
		// 用唯一的标识符 id 来代表当前的HappyPack 是哦用来处理一类特定的文件
		id: 'babel',
		// 如何处理.js文件 用法和loader配置中一样
		loaders: ['babel-loader?cacheDirectory']
	})
]

ParallelUglifyPlugin 多进程压缩JS

webpack内置 Uglify工具压缩JS

JS是单线程的 开启多进程压缩更快

和happyPack同理

javascript 复制代码
plugins:[
	// 使用ParallelUglifyPlugin 进行压缩输出的JS代码
	new ParallelUglifyPlugin ({
		//传递给UglifyJS的参数
		// (还是使用UglifyJS压缩 只不过帮助开启了多进程)
		uglifyJS: {
			output: {
				beautify: false, //最紧凑的输出
				comments: false, //删除所有注释
			},
			compress: {
				// 删除所有的 `console` 语句 可以兼容ie浏览器
				drop_console: true,
				// 内嵌定义了但是只用到一次的变量
				collapse_vars: true,
				// 提取出出现多次但是没有定义成变量去引用的静态变量
				reduce_vars: true,
			}
		}
	})
]
关于开启多进程

当项目较大时 打包较慢 开启多进程能提高速度

当项目较小时 打包很快 开启多进程会降低速度(进程开销)

按需使用

自动刷新

javascript 复制代码
module.export = {
	watch: true, // 开启监听 默认为false
	// 注意 开启监听后 webpack-dev-server 会自动公开其刷新浏览器

	// 监听配置
	watchOptions: {
		ignored: /node_modules/, // 忽略哪些
		// 监听到变化后会等300ms再去执行动作 防止文件更新太快导致重新编译频率太高
		aggregateTimeout: 300, // 默认为300ms
		// 判断文件是否发生变化是通过不停的去询问系统指定文件有没有变化实现的
		poll: 1000 // 默认每隔1000毫秒询问一次
	}
}

热更新

自动刷新:整个页面全部刷新 速度较慢

自动刷新:整个页面全部刷新 状态会丢失

热更新:新代码生效 网页不刷新 状态不丢失

javascript 复制代码
	entry:{
		index: [
			'webpack-dev-server/client?http://localhost:8080/',
			'webpack/hot/dev-server',
			path.join(srcPath, 'index.js')
		],
		other: path.join(srcPath, 'other.js')
	},
	plugins: [
		new HotModuleReplacementPlugin()
	],
	devServer: {
		port: 8080,
		progress: true, //显示打包的进度条
		contentBase: distPath, //根目录
		open: true, //自动打开浏览器
		compress: true, //自动gzip压缩
		
		hot: true, //准备好开启热更新
		
		// 设置代理
		proxy: {
			// 将本地 /api/xxx 代理到 localhost:3000/api/xxx
			'/api': 'http://localhost:3000'
		}
	}

DllPlugin 动态链接库插件

前端框架如vue react 体积大 构建慢

较稳定 不常升级版本

每次npm run dev都要重新构建 vue react

同一个版本只构建一次即可 不用每次都重新构建

webpack已内置 DllPlugin支持

DllPlugin - 打包出dll文件

DllReferencePlugin - 使用dll文件

webpack.dll.js

javascript 复制代码
const path = require('path')
const DllPlugin = require('webpack/lib/DllPlugin')
const {srcPath, distPath} = require('./paths')

module.exports = {
	mode: 'development',
	// JS执行入口文件
	enrty: {
		// 把React相关模块放到一个单独的动态连接库
		react: ['react', 'react-dom']
	},
	output: {
		// 输出的动态链接库的文件名称 [name]代表当前动态链接库的名称
		// 也就是 entry 中配置的react 和 polyfill
		filename: '[name].dll.js',
		// 输出的文件都放到dist目录下
		path: distPath,
		// 存放动态链接库的全局变量名称 例如对应react来说就是_dll_react
		// 之所以在前面加上_dell_是为了防止全局变量冲突
		library: '_dll_[name]'
	},
	plugins: [
		// 接入 DllPlugin
		new DllPlugin({
			// 动态链接库的全局变量名称 需要和output.library中的一致
			// 该字段的值也就是输出的manifest.json文件中的name字段的值
			// 例如 react.manifest.json中就有"name": "_dll_react"
			name: '_dll_[name]',
			// 描述动态链接库的manifest.json文件输出时的文件名称
			path: path.join(distPath, '[name].manifest.json')
		})
	]
}

webpack --config build/webpack.dll.js 打包

最后只要在index.html中引用react.dll.js就可以

html 复制代码
<script src="./react.dll.js"></script>

在webpack.dev.js中引入

javascript 复制代码
const path =- require('path')
const webpack = require('webpack')
const {smart} = require('webpack-merge')
const webpackCommonConf = require('./webpack.common.js')
const {srcPath, distPath} = require('./paths')
// 引入DllReferencePlugin
const DllReferencePlugin = require('webpack/lib/DllReferencePlugin')
module.exports = smart(webpackCommonConf, {
	mode: 'development',
	module: {
		reles: [
			{
				test: /\.js$/,
				loader: ['babel-loader'],
				include: srcPath,
				exclude: /node_modules/ //不要再转换node_modules
			}
		]
	},
	plugins: [
		// 告诉webpack使用了哪些动态链接库
		new DllReferencePlugin({
			// 描述 react动态链接库的文件内容
			manifest: require(path.join(distPath, 'react.manifest.js'))
		})
	]
})

webapck性能优化 - 产出代码

小图片base64编码

javascript 复制代码
{
	test: /\.(png|jpg|jpeg|gif)$/,
	use: {
		loader: 'url-loader',
		options: {
			// 小于5kb 的图片用Base64格式产出
			// 否则依然延用file-loader的形式产出url格式
			limit: 5 * 1024,
			// 打包到img目录下
			outputPath: '/img/',
		}
	}
}

bundle + hash

javascript 复制代码
filename: '[name].[contentHash:8].js'

懒加载

提取公共代码

IngorePlugin

使用CDN加速

1、

javascript 复制代码
output: {
	publicPath: 'cdn地址' //修改搜友静态文件url的前缀
}

2、把打包的结果上传到cnd地址上

使用production

将mode设置为production后会自动压缩代码

vue react 等会自动删除调试代码 (如开发环境下的错误提示)

启动Tree-Shaking 【只有ES6 Module 才能让tree-shaking生效 commonjs不行】

javascript 复制代码
module.exports = {
	mode: 'production'
}

ES6 Module 和 Commonjs的区别

ES6 Module静态引入 编译时引用

Commonjs动态引入 执行时引入

只有ES6 Module才能静态分析 实现Tree-Shaking

javascript 复制代码
let apiList = require('./config/api.js')
if(isDev){
	// 可以动态引入 执行时引入
	apiList = require('./config/api_dev.js')
}
javascript 复制代码
import apiList from './config/api.js'
if(isDev){
	// 编译时报错 只能静态引入
	import apiList from './config/api_dev.js'
}

使用Scope Hosting

一个文件打包成一个函数

当文件多时 函数也会多 占内存大

想要多个文件合并成一个函数 使用Scope Hosting

代码体积更小

创建函数作用域更小

可读性更好

javascript 复制代码
const ModuleConcatenationPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin')

module.exports = {
	resolve: {
		// 针对 Npm中的第三方模块优先采用jsnext:main 中指向的ES6模块化语法的文件
		mainFields: ['jsnext:main', 'browser', 'main']
	},
	plugins: [
		// 开启 Scope Hoisting
		new ModuleConcatenationPlugin()
	]
}

babel环境搭建

presets 一些常用plugins的集合 组合的预设 不用写很多plugin

.babelrc

javascript 复制代码
{
	"presets": [
		[
			"@babel/preset-env" 
		]
	]
}

babel-polyfill

什么是polyfill ? - 补丁

core-js和regenerator

core-js标准库 所有补丁代码

regenerator 支持 generator语法补全core-js

babel-polyfill就是core-js和regenerator的集合

babel7.4弃用了babel-polyfill 推荐直接使用core-js和regenerator

javascript 复制代码
{
	"presets": [
		[
			"@babel/preset-env",
			{
				"useBuiltIns": "usage",
				"corejs": 3
			}
		]
	]
}

问题:

污染全局环境

解决:

babel-runtime

babel-runtime

javascript 复制代码
{
	"plugins":[
		"@babel/plugin-transform-runtime",
		{
			"absoluteRuntime": false,
			"corejs": 3,
			"helpers": true,
			"regenerator": true,
			"useESModules": false
		}
	]
}

前端为何要进行打包和构建

体积更小(Tree-Shaking、压缩、合并) 加载更快

编译高级语言或语法(TS、ES6+、模块化、SCSS)

兼容性和错误提示(Polyfill、postcss、eslint)

统一、高效的开发环境

统一的构建流程和产出标准

loader和plugin的区别

loader模块转换器 如less -> css

plugin 扩展插件 转换完做一些扩展 如HtmlWebpackPlugin 将js或css塞进一个html文件里

bable和webpack的区别

babel-JS新语法编译工具 不关心模块化

webpack- 打包构建工具 是多个loader plugin的集合

如何产出一个lib

babel-polyfill和babel-runtime的区别

babel-polyfill会污染全局

babel-runtime不会污染全局

产出第三方lib要用babel-runtime

为何Proxy不能被Polyfill

如Class可以用function模拟

如Promise可以用callback来模拟

但Proxy的功能用Object.defineProperty无法模拟

相关推荐
y先森2 小时前
CSS3中的伸缩盒模型(弹性盒子、弹性布局)之伸缩容器、伸缩项目、主轴方向、主轴换行方式、复合属性flex-flow
前端·css·css3
前端Hardy2 小时前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html
susu10830189112 小时前
vue3中父div设置display flex,2个子div重叠
前端·javascript·vue.js
IT女孩儿3 小时前
CSS查缺补漏(补充上一条)
前端·css
吃杠碰小鸡4 小时前
commitlint校验git提交信息
前端
虾球xz5 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇5 小时前
HTML常用表格与标签
前端·html
疯狂的沙粒5 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
小镇程序员5 小时前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
野槐5 小时前
前端图像处理(一)
前端