本篇的目的

  • 本篇教程是手把手教你从0到1新建webpack5工程化项目,源码的github地址
    https://github.com/hyj0703/webpack,如稍有助益,麻烦点颗免费的小星星。

1、webpack基础

1.1、webpack开发环境搭建

Webpack是模块打包工具,是工程化,自动化思想在前端开发中的体现,
1.1.1、初始化一个项目
项目安装 (推荐)新建一个文件夹并进入,init是初始化命令,-y参数可以免去中间的配置过程,直接一步初始化成功。

npm init -y

1.1.2、安装webpack,

  • 参数 iinstall的简写,
    - -D--save-dev的简写,仅在开发环境才用的包,会被注册在package.json里的devDependencies
    - -S--save的简写,会打包到生成的包里面,是发布内容的一部分,会被注册在package.json里的dependencies
npm i -D webpack webpack-cli //webpack5
npm i -D webpack@4 webpack-cli@3 //webpack4

以下大部分的npm包安装,如果想适配webpack4,需要指定版本,指定的版本比现有版本低一个版本。

1.1.3、如果想通过命令执行npm run dev来启动webpack,需要在package.json里加入下面代码。”scripts“的对象里可以配置一些打包命令。dev是命令的名称,webpack是打包命令具体执行的语句,--config是指定配置的文件,紧跟在后面的是配置文件名。详见下文章节 >> 1.3、开发及生产环境分离

"scripts": {
	"dev": "webpack --config ./webpack.config.dev.js",
},

1.1.4、新建src文件夹,目录结构如下图。 新建src/index.js ,这是入口文件,写业务代码的地方
在这里插入图片描述

// index.js
console.log("hello webpack")

1.1.5、执行打包命令npm run dev后,成功后会发现在目录下多了一个dist文件夹,目录如下图,里面有个main_xxxxxx.js,能打包成功是因为有下文1.1.6的配置
在这里插入图片描述
1.1.6、在根目录下新建文件webpack.config.dev.js,输入下面的默认配置,path是必备的配置项目绝对路径的插件,__dirname指当前文件的绝对路径

const path = require("path");
module.exports = {
	// 必填 webpack执行打包的唯一入口
	entry: {
    	main: [path.resolve(__dirname, './src/index.js')],
  	},
	output: {
		// 将所有依赖的模块合并输出到main_xxxxxx.js,xxxxxx为随机生成的6位hash码
		//当内容有改变时,hash会变化,防止缓存原因导致修改不更新
		filename: 'js/[name]_[contenthash:6].js',
		// 输出文件的存放路径, 必须是绝对路径
		path: path.resolve(__dirname, "./dist")
	}
}

1.2、webpack配置核心

1.2.1,-entry
指定webpack打包入口,webpack执行构建的第一步将从entry开始

//多入口 entry是个对象,最终会在输出的文件夹里生成两个文件,base.js和main.js
entry:{
    main: [path.resolve(__dirname, './src/index.js')],
}

1.2.2, -output
打包转换后的文件输出到磁盘位置:

// 多入口的处理
output: {
	filename: "[name][contenthash:6].js", // 利用占位符,文件名称不重复
	path: path.resolve(__dirname, "dist") // 输出文件到磁盘的目录,必须是绝对路径
}

1.2.3,-mode
Mode用来指定当前的打包环境

  • production 生产模式
  • development 开发模式

如果没有设置,webpack会将mode的默认值设置为production

1.2.4, -loader
模块解析,模块转换器
webpack是模块打包工具,而模块不仅仅是js,还可以是css,图片或者其他格式
但是webpack默认只处理jsjson,其他模块就需要用loader

1.2.5, -module
模块配置,在webpack里一切皆模块,用来配置需要的匹配规则及使用哪种loader转换器

module:{
	rules:[
		test:/\.xxx$/,//指定匹配规则
		use:{
			loader:'xxx-load'//指定使用的loader
		}
	]
}

1.3、开发及生产环境分离

在根目录下新建2个文件,分别是webpack.config.dev.js和webpack.config.pro.js.

  • webpack.config.dev.js是开发环境的配置,具体代码查看下文章节 >> 6、开发环境的配置代码
  • webpack.config.pro.js是线上环境的配置,具体代码查看下文章节 >> 7、生产环境的配置代码
  • 会在下文的每段代码的注释中,标注本段代码属于哪种环境的配置

修改package.json文件里的"scripts"指令

  • dev 是在开发环境下,生成一个dist目录的文件夹
  • server 是在开发环境下,在浏览器中打开一个热更新的页面
  • build 是在生产环境下,生成一个build目录的文件夹
  • zip 是在生产环境下,打一个zip压缩包
webpack5
"scripts": {
    "dev": "webpack --config ./webpack.config.dev.js",
    "server": "webpack-dev-server --config ./webpack.config.dev.js --env debug",
    "build": "webpack --config ./webpack.config.pro.js",
    "zip": "webpack --config ./webpack.config.pro.js --env zip"
  },

1.4、处理静态资源

静态资源包括字体,图片等,可直接使用的资源。
webpack5 通过添加4中新的资源模块类型,来替换所有这些loader

  • asset/resource 发送一个单独的文件,并导出URL(之前通过file-loader实现)
  • asset/inline 导出一个资源的data URI (之前通过url-loader实现)
  • asset/source 导出资源的源代码 (之前通过raw-loader实现)
  • asset 在导出一个data URI和一个单独的文件自由选择 (之前通过url-loader,并且配置资源体积实现)

webpack4 通常使用:

  • raw-loader 将文件导入为字符串
  • url-loader 将文件作为data URI内联到bundle中
  • file-loader 将文件发送到输出目录

webpack5代码

//代码放入文件webpack.config.dev.js webpack.config.pro.js
module: {
	rules: [
		{
			test:/\.(png|jpe?g|gif)/,
			type:'asset',
			parser: {
				dataUrlCondition: {
					maxSize: 3*1024
				}
			},
			generator:{
				filename:'images/[name][contenthash:3].[ext]'
			}
		},
		{
			test:/\.(svg|eot|ttf|woff|woff2)$/,
			type:'asset/resource',
			generator:{
				filename:'font/[name].[ext]'
			}
		}
	]
}

webpack4代码

npm i -D file-loader

//代码放入文件webpack.config.dev.js webpack.config.pro.js
module: {
	rules: [
		{
			test: /\.(png|jpe?g|gif)$/,
			//use使用一个loader可以用对象,字符串,两个loader需要用数组
			use:{
				loader: "file-loader",
				//options额外的配置,比如资源名称
				options: {
					//placeholder 占位符 [name]老资源模块的名称 [ext]老资源模块的后缀
					name: "[name]_[contenthash].[ext]",
					//打包后存放的位置
					outputPath: "images/"
				}
			}
		},
        {
          test: /\.(eot|ttf|woff|woff2|svg)$/,
          use: "file-loader",
        }
	]
}

url-loader是file-loader的加强版本 npm i -D url-loader

url-loader内部使用了file-loader,但是遇到小于10000,即8kb的png、jpg、jpeg、gif文件会转换成base64格式的字符串,并打包到css和js里。对于小体积的图片比较适合

module: {
	rules: [
		{
			test: /\.(png|jpe?g|gif)$/,
			use: {
				loader: "url-loader",
				options: {
					name: "[name]_[hash].[ext]",
					outputPath: "images/",
					//小于10000,即8kb,才转换成base64
					limit:10000
				}
			}
		}
	]
}

2、webpack热门插件

扩展插件,在webpack打包流程中的特定时机注入扩展逻辑,来改变打包结果,或者做你想要的事情。

2.1、 HtmlWebpackPlugin

htmlwebpackplugin会在打包结束后,自动生成一个html文件,并把打包生成的js模块引入到该html中

npm i -D html-webpack-plugin

配置,新建src/index.html文件,title引用了htmlWebpackPlugin配置的标题,方便在webpack的配置中直接修改标题

<title><%= htmlWebpackPlugin.options.title %></title>
//代码放入文件webpack.config.dev.js webpack.config.pro.js
module.exports = {
	plugins: [
		new htmlWebpackPlugin({
			title: "My App", //标题
			filename: "index.html",//输出的文件名,默认是index.html
			template: "./src/index.html"//模板文件路径
		})
	]
}

2.2 、clean-webpack-plugin

清理上次打包生成的dist无用代码

npm i -D clean-webpack-plugin

//代码放入文件webpack.config.dev.js webpack.config.pro.js
const { CleanWebpackPlugin } = require("clean-webpack-plugin")
plugins:[
	new CleanWebpackPlugin({
		//打包之前清理一次,删掉上次打包生成的dist文件夹
		cleanOnceBeforeBuildPatterns: [
			path.resolve(__dirname,'./dist')
		]
	})
]

3、提升开发效率的利器

3.1、WebpackDevServer

每次改完代码都需要重新打包一次,打开浏览器刷新,很麻烦。使用webpack-dev-server来实时刷新页面,代码刚改完,页面就刷新完成了。

npm i -D webpack-dev-server

在package.json文件中的指令为

"scripts": {
	"server": "webpack-dev-server --config ./webpack.config.dev.js --env.debug",
}

在webpack.config.dev.js配置

//代码放入文件webpack.config.dev.js
module.exports = {
	devServer: {
		contentBase: "./dist",//指定被访问html页面所在的目录的
		open:true,// 指运行npm run server指令后,自动在浏览器里打开一个页面
		port:8081 // 指定打开的页面的端口为8081,也可以指定其他端口
	}
}

3.2、本地mock数据,解决跨域

前后端分离,wepack搞定开发期mock测试数据,启动一个本地服务,mock多个接口,首先安装expressfs

npm i express fs -D

  • express是基于nodejs平台的一个极简web开发框架。
  • fs模块用于对系统文件及目录进行读写操作。
    在src文件夹里创建一个server文件夹,server文件夹下再新建两个文件,分别叫data1.js和data2.js,用来放入测试数据。可以建多个文件,每个文件里也可以有多个接口。代码如下
//data1.js
module.exports = function(app){
	app.get('/api',function(req,res){
		res.jsonp({name:'lisi',age:11})
	})
	app.get('/api/about',function(req,res){
		res.jsonp({name:'zhangsan',age:40})
	})
}
//data2.js
module.exports = function(app){
	app.get('/api/home',function(req,res){
		res.jsonp({name:'李四',age:11})
	})
	app.get('/api/list',function(req,res){
		res.jsonp({name:'张三',age:40})
	})
}

在src目录下新建server执行文件,用来自动加载刚才新建的测试数据,这里的代码无需修改,即可自动加载全部的接口及数据。

// server.js
const express = require('express') //nodejs的web框架
const app = express() //实例化框架
const fs = require('fs') //操作文件的模块
const dir = './server' //接口路径

//readdirSync 该方法返回一个包含指定目录下所有文件名称的数组对象
const list = fs.readdirSync(dir).map((v) => {
  require(dir + '/' + v)(app) //拼接出文件路径后,导入执行,同时传入app
})

app.listen('9092') //监听端口

在命令行里进入src文件夹后,再运行指令node server.js,出现跨域报错时,通过修改webpack.config.js设置服务器代理的方法解决

devServer:{
	...
	proxy: {
		//凡是api这个接口,都会被代理到target的路径上
		'/api': {
			target: 'http://localhost:9092',
			//发送请求头host会被设置为target
            changeOrigin:true
		}
	}
}

在业务代码里,就能通过上面的数据接口,读取到返回的jsonp数据了。

3.3 、Babel处理ES6

Babel是javascript编译器,能将ES6代码转换成ES5代码,下面安装

npm i babel-loader @babel/preset-env -D
npm i core-js regenerator-runtime -S

  1. babel-loader是webpack与babel的通信桥梁,@babel/preset-env把es6转成es5
  2. @babel/preset-env里包含了es6,7,8转es5的规则
  3. 默认的Babel只支持一些基础的特性转换,高级的转换需要对象的转换需要core-js/stableregenerator-runtime/runtime
  4. 关于core-js@3的详细介绍请看https://www.cnblogs.com/sefaultment/p/11631314.html
//代码放入文件webpack.config.dev.js webpack.config.pro.js
entry:{
	main:[path.resolve(__dirname, './src/index.js')]
	//打包成功后,会生成两个文件,base.js和main.js
},
output:{
	filename: "js/[name]_[contenthash:6].js",// 多模块导出,[name]指entry中的base和main,[hash:6]指生成6位hash值
	path:path.resolve(__dirname, './dist')
},
module:{
	rules: [
		{
			test:/\.jsx?$/,//适配js和jsx
			use: {
				loader: 'babel-loader'
			}
		}
	]
}
  • 在package.json里配置browserslist,
  • babel会根据所需适配的浏览器目标进行对应的转换,浏览器目标可通过browserslist属性设置
  • 具体配置参考https://www.npmjs.com/package/browserslist。
"browserslist": [
	"> 1%", //全球超过1%使用的浏览器
	"last 2 versions" //所有的浏览器兼容到最近的2个版本
]

按需加载,减少冗余,根目录下新建.babelrc文件,

//.babelrc
{
	"presets": [
		[
			"@babel/preset-env",
			{
				"corejs": 3,//新版本需要指定核心库版本
				"useBuiltIns": "usage" //按需注入
			}
		]
	]
}

3.4 、css文件的处理

  • css-loader分析css模块之间的关系,并合成一个css
  • style-loader会吧css-loader生成的内容,以style挂载到页面的head部分
  • sass-loader 把sass语法转换成css
  • postcss-loader 样式自动添加前缀

npm i -D style-loader css-loader sass-loader node-sass postcss-loader postcss-preset-env

//代码放入文件webpack.config.dev.js webpack.config.pro.js
module: {
	rules: [
		{
			test: /\.(css|scss)$/,
			//loader是有顺序的,从后往前
			use:[
				'style-loader',//在页面插入css样式
				'css-loader',//抽取css样式
				'postcss-loader',//样式前缀自动补全
				'sass-loader' //sass当做css技术栈
			]
		}		
	]
}

根目录下新建postcss.config.js

const postcssPresetEnv = require('postcss-preset-env')
module.exports = {
	plugins: [
		//使用postcss为样式自动补齐浏览器前缀,浏览器的版本限制在package.json里的browserslist配置
		postcssPresetEnv(),
	]
}

4、webpack性能优化—优化开发体验

4.1、优化loader配置

  • 推荐include,缩小loader的处理范围,下面的配置只在src文件夹下使用loader转译器
include:path.resolve(__dirname, "./src")

4.2、优化resolve.modules配置

resolve.modules用于配置webpack去哪些目录下寻找第三方模块,默认是[‘node_modules’],我们的第三方模块都安装在了项目根目录下,就可以直接指明这个路径

//代码放入文件webpack.config.dev.js webpack.config.pro.js
module.exports = {
	resolve:{
		modules: [path.resolve(__dirname, "./node_modules")]
	}
}

4.3、优化resolve.alias配置

resolve.alias配置通过别名来将原导入路径映射成一个新的导入路径

//代码放入文件webpack.config.dev.js webpack.config.pro.js
resolve:{
	alias: {
		react: path.resolve(
			__dirname,
			"./node_modules/react/umd/react.production.min.js"
	 	),
		"react-dom": path.resolve(
			__dirname,
			"./node_modules/react-dom/umd/react-dom.production.min.js"
	 	)
	}
}

4.4、优化resolve.extensions配置

resolve.extensions在导入语句没带文件后缀时,webpack会自动带上后缀,去尝试查找文件是否存在,但是在查找的时候,会耗费一定的打包时间,默认值:

extensions:['.js','.json','.jsx','.ts']
  • 后缀尝试列表尽量的小
  • 导入语句尽量带上后缀

4.5、优化文件监听的性能

//代码放入文件webpack.config.dev.js
module.export = {
	watchOptions: {
		//不监听的node_modules目录下的文件
		ignored: /node_nodules/,
	}
}

采用这种方法优化后,Webpack消耗的内存和CPU将会大大减少

4.6、HMR 热模块替换

自动及时更新代码,自动重编译,只适合开发模式。

//代码放入文件webpack.config.dev.js
devServer: {
	hot:true,
	hotOnly:true,//即便HMR不生效,浏览器也不自动刷新,就开启hotOnly
}
plugins:[
	new webpack.HotModuleReplacementPlugin()
]

4.7、sourceMap

源代码与打包后的代码的映射关系,通过sourceMap定位到源代码关闭的话可以在配置文件里devtool: “none”

//代码放入文件webpack.config.dev.js
devtool:"eval-cheap-module-source-map",// webpack5开发环境配置
devtool:"cheap-module-eval-source-map",// webpack4开发环境配置
//线上环境不推荐开启

打包后预览页面,还能看到我们的源代码
在这里插入图片描述

4.8、文件缓存

webpack5内置了cache缓存机制,直接配置即可

cache在开发模式下会设置cache.type = ‘momory’ 在生产模式下会被禁用掉
type值可选为:momery使用内容缓存 filesystem使用文件缓存,当type="filesystem"时,需要设置cacheDirectory时才生效,用于设置你需要的东西缓存放在哪里

//代码放入文件webpack.config.dev.js
module.exports = {
	cache: {
		type: 'filesystem',
		cacheDirectory: path.join(__dirname, 'node_modules/.cac/webpack')
	}
}

webpack4使用hard-source-webpack-plugin
项目中的第三方库,基本不更新,打包的时候分离出来,提升打包速度。

//代码放入文件webpack.config.dev.js
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')
plugins = [
	new HardSourceWebpackPlugin()
]

4.9、调试工具

会在页面中添加一个按钮,点击会出现一个调试器,输出console信息,及各种报错信息

npm i -D vconsole-webpack-plugin

在文件webpack.config.dev.js中加入如下代码

//代码放入文件webpack.config.dev.js
const vConsolePlugin = require("vconsole-webpack-plugin")
plugins:[
	new vConsolePlugin({
		enable: true,
		filter: ['base']
	})
]

5、webpack性能优化—优化输出质量

5.1、借助MiniCssExtractPlugin完成抽离css

单独生成css,可以和js并行下载,提高页面加载效率,与HMR热模块替换冲突,所以只在mode为production模式下打包使用

npm i mini-css-extract-plugin -D

//代码放入文件webpack.config.pro.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
module:{
	reles: [
		{
			test: /\.scss$/,
			use: [
				MiniCssExtractPlugin.loader,
				"css-loader",
				"postcss-loader",
				"sass-loader"
			]
		}
	]
},
plugins: [
	new MiniCssExtractPlugin({
		filename: "css/[name]_[contenthash:6].css",
		chunkFilename: "[id].css"
	})
]

5.2、压缩css和js

webpack5代码

npm i -D mini-css-extract-plugin css-minimizer-webpack-plugin

//代码放入文件webpack.config.pro.js
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
	optimization:{
		minimizer:[
			new CssMinimizerPlugin(),
			new TerserPlugin()
		]
	}
}

webpack4代码

  • 借助optimize-css-assets-webpack-plugin
  • 借助cssnano,详细配置参数请查看https://cssnano.co/guides/optimisations

npm i -D cssnano optimize-css-assets-webpack-plugin

//代码放入文件webpack.config.pro.js
new OptimizeCSSAssetsPlugin({
	cssProcessor: require("cssnano"),//引入cssnano配置压缩选项
	cssProcessorOptions: {
		discardComments: { removeAll: true }
	}
})

5.3、压缩HTML

  • 借助html-webpack-plugin
//代码放入文件webpack.config.pro.js
new htmlWebpackPlugin({
	title: "My App", //标题
	filename: "index.html",//输出的文件名,默认是index.html
	template: "./src/index.html"//模板文件路径
	minify: {
		//压缩HTML文件
		removeComments: true,//移除HTML中的注释
		collapseWhitespace: true, // 删除空白符和换行符
		minifyCSS: true //压缩内联css
	}
})

5.4、tree Shaking

“摇树”,清除无用css,js(Dead Code)
只支持import方式引入,不支持commonjs的方式引入

//package.json
”sideEffects“:false,//正常对所有模块进行tree shaking
//或者在数组里排除不需要tree shaking的模块,应采用数组排除,不然css抽取为单独文件将失效
// 仅生产模式有效,需要配合usedExports
”sideEffects“:["*.css","*.scss","*.less","core-js","regenerator-runtime","*.jpg","*.png","*.gif",'react','react-dom']

webpack5在生产模式下,自动开启tree shaking

//代码放入文件webpack.config.pro.js
module.exports = {
	optimization: {
		usedExports: true, //只导出被使用的模块
		minimize: true, //启动压缩
		concatenateModules: true, //模块合并
		sideEffects: true, //开启副作用,去掉没使用的代码
	}
}

webpack4代码

npm i glob-all purify-css purifycss-webpack -D

//代码放入文件webpack.config.pro.js
optimization:{
	usedExports: true //哪些导出的模块被使用了,再做打包
}
//代码放入文件webpack.config.pro.js
const PurifyCss = require("purifycss-webpack")
const glob = require("glob-all")
plugins:[
	// 清除无用 css
	new PurifyCss({
		paths: glob.sync([
			// 要做 CSS Tree Shaking 的路径文件
			path.resolve(__dirname, './src/*.html'),// 请注意,我们同样需要对 html 文件进行 tree shaking
			path.resolve(__dirname, './src/*.js')
		])
	})
]

5.5、代码分割 code Splitting

打包后,如果只生成一个main.js的话,代码体积很大,不利于下载,没有合理利用浏览器资源。使用代码分割后,根据配置会生成几个文件,可以同时下载,充分利用了浏览器的资源。

//代码放入文件webpack.config.pro.js
optimization:{
	splitChunks: {
		chunks: "all", // 所有的 chunks 代码公共的部分分离出来成为一个单独的文件
		cacheGroups:{
			//缓存组
			react: {
				test: /react|react-dom/,
				name: 'react',
				minChunks: 1,
			}
		}
	}
}

分割前输出的文件结构见上面章节 >> 1.1.5
分割后输出的文件结构如下,多分出来了react_xxxxxx.js及其他
在这里插入图片描述

5.6、图片压缩

压缩图片的方法,即tinypng和image-minimizer-webpack-plugin插件

一,webpack4可用,基于 tinypng.com 封装的一个支持nodejs、命令行 和webpack的图片压缩工具。首先需要去
https://tinypng.com/developers这个网站申请key,申请方法为:获取并输入全名和邮箱,点击按钮就会生成API key,拷贝到项目配置中。
在这里插入图片描述

npm i -D tinypng-webpack-plugin

在文件webpack.config.pro.js中加入如下代码

//代码放入文件webpack.config.pro.js
const tinyPngWebpackPlugin = require("tinypng-webpack-plugin")
plugins:[
	new tinyPngWebpackPlugin({
		key: [
	        '----------这里填入申请到的API key--------',
	        '----------这里填入申请到的API key--------',
	        '----------这里填入申请到的API key--------',
	        '----------这里填入申请到的API key--------',
	        '----------这里填入申请到的API key--------',
		],
		ext: ['png','jpeg','jpg']
	})
]

二,webpack5可用,安装webpack的npm包压缩

npm i -D image-minimizer-webpack-plugin imagemin
npm i -D imagemin-gifsicle imagemin-jpegtran imagemin-optipng
imagemin-webp imagemin-svgo

//代码放入文件webpack.config.pro.js
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin")
optimization: {
    usedExports: true, //只导出被使用的模块
    minimize: true, //启动压缩
    concatenateModules: true, //模块合并
    sideEffects: true, //开启副作用功能
    minimizer: [new CssMinimizerPlugin(),//压缩css
      new ImageMinimizerPlugin({//压缩图片
        minimizer:{
          implementation: ImageMinimizerPlugin.imageminMinify,
          options: {
            plugins: [
              ['gifsicle', { interlaced: true }],
              ['jpegtran', { progressive: true }],
              ['optipng', { optimizationLevel: 5 }],
            ],
          },
        }
    })], 
  },

5.7、压缩打包

把最后生成的文件夹打包成zip压缩包

npm i -D filemanager-webpack-plugin

在webpack.config.pro.js文件中加入如下代码

//代码放入文件webpack.config.pro.js
const FilemanagerWebpackPlugin = require("filemanager-webpack-plugin")
webpack5代码
plugins:[
	new FilemanagerWebpackPlugin({
		events: {
			onEnd: {
				copy: [//拷贝代码
					{//拷贝代码的源,即我们打包生成的文件夹
						source:path.resolve(__dirname, './build'),
						//拷贝到一个临时的文件夹里
						destination: path.resolve(__dirname,'./tmp_for_zip/dist')
					}
				],
				archive: [
					{//生成压缩包的源,即是我们刚刚拷贝的文件夹
						source:path.resolve(__dirname,'./tmp_for_zip'),
						//压缩后的包名及格式
						destination: path.resolve(__dirname,'./dist.zip')
					}
				],
				//删除刚刚新建的临时文件夹
				delete: [path.resolve(__dirname,'./tmp_for_zip')]
			}
		}
	})
]
webpack4代码
plugins:[
	new FilemanagerWebpackPlugin({
		onEnd: {
			copy: [//拷贝代码
				{//拷贝代码的源,即我们打包生成的文件夹
					source:path.resolve(__dirname, './build'),
					//拷贝到一个临时的文件夹里
					destination: path.resolve(__dirname,'./tmp_for_zip/dist')
				}
			],
			archive: [
				{//生成压缩包的源,即是我们刚刚拷贝的文件夹
					source:path.resolve(__dirname,'./tmp_for_zip'),
					//压缩后的包名及格式
					destination: path.resolve(__dirname,'./dist.zip')
				}
			],
			//删除刚刚新建的临时文件夹
			delete: [path.resolve(__dirname,'./tmp_for_zip')]
		}
	})
]

5.8、雪碧图

  • 把小图合成一张大图的功能,sprite文件夹下的每个子文件夹都会合成一个雪碧图,有多少个子文件夹就有多少张雪碧图,有利于控制雪碧图的大小和数量。不在sprite文件夹下的图片则不合成雪碧图
  • 文件结构如下,a.png和b.png会被合成sprite.src-img1.png
  • c.png和d.png会被合成sprite.src-img2.png
└─src
    │     
    └─sprite
        │  
        ├─img1
        │      a.png
        │      b.png
        │      
        └─img2
                c.png
                d.png

npm i -D postcss-sprites postcss

在文件postcss.config.js中,添加如下代码

const path = require('path')
const sprites = require('postcss-sprites')
const postcss = require('postcss')

module.exports = {
  plugins: [
    sprites({
      filterBy: ({ path: imgPath }) => {
        //非sprite文件夹下的图片不合并
        const [first, second] = path.dirname(imgPath).split(path.sep).reverse()
        return first == 'sprite' || second === 'sprite'
          ? Promise.resolve()
          : Promise.reject()
      },
      groupBy: ({ path: imgPath }) => {
        //以sprite文件夹下的子目录作为分组,子目录下的图片和合并成一张雪碧图
        const [first, second, third] = path
          .dirname(imgPath)
          .split(path.sep)
          .reverse()
        const { name } = path.parse(imgPath)
        if (first === 'sprite') {
          return Promise.resolve(`${second}-${name}`)
        } else if (second === 'sprite') {
          return Promise.resolve(`${third}-${first}`)
        } else {
          return Promise.reject()
        }
      },
      hooks: {
        onUpdateRule: (
          rule,
          token,
          { coords, ratio, spriteWidth, spriteHeight, spriteUrl }
        ) => {
          //start:修改自postcss-sprites/lib/core中的updateRule方法
          const posX = -Math.abs(coords.x / ratio)
          const posY = -Math.abs(coords.y / ratio)
          const sizeX = spriteWidth / ratio
          const sizeY = spriteHeight / ratio
          token
            .cloneAfter({
              type: 'decl',
              prop: 'background-image',
              value: `url(./${spriteUrl})`,
            })
            .cloneAfter({
              prop: 'background-position',
              value: `${posX}px ${posY}px`,
            })
            .cloneAfter({
              prop: 'background-size',
              value: `${sizeX}px ${sizeY}px`,
            })
          //end:修改自postcss-sprites/lib/core中的updateRule方法

          //start:若原始样式中没有设置width或height,则根据图片大小自动添加width或height属性
          const dimensions = ['width', 'height']
          rule.some(({ prop }) => {
            dimensions.some((targetProp, idx) => {
              if (prop === targetProp) {
                dimensions.splice(idx, 1)
              }
            })
          })
          dimensions.forEach((prop) => {
            const val = coords[prop]
            rule.insertAfter(
              rule.last,
              postcss.decl({
                prop: prop,
                value: `${val}px`,
              })
            )
          })
          //end:若原始样式中没有设置width或height,则根据图片大小自动添加width或height属性
        },
      },
      relativeTo: 'rule',
      spritePath: './src/img',
      spritesmith: {
        padding: 4,
      },
      stylesheetPath: './src/css',
    }),
  ],
}

5.9、px转rem,适配不同屏幕大小

通过上面的代码把css里的px转换成了rem,在index.html的head中加入下面的代码,即可兼容不同屏幕

<script>
      document.documentElement.style.fontSize =
        (document.documentElement.clientWidth || document.body.clientWidth) /
          7.5 +
        'px'
      window.addEventListener('resize', () => {
        document.documentElement.style.fontSize =
          (docuemnt.documentElement.clientWidth || document.body.clientWidth) /
            7.5 +
          'px'
      })
    </script>

需文件postcss.config.js中的如下代码配合

npm i -D postcss-px2rem

const px2rem = require('postcss-px2rem')
module.exports = {
  plugins: [
    px2rem({
      remUnit: 100,
    }),
  ]

8、项目源码的github地址

https://github.com/hyj0703/webpack

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐