文章目录
本篇的目的
- 本篇教程是手把手教你从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,
- 参数
i
是install
的简写,
--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默认只处理js
和json
,其他模块就需要用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
多个接口,首先安装express
和fs
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
babel-loader
是webpack与babel的通信桥梁,@babel/preset-env把es6转成es5@babel/preset-env
里包含了es6,7,8转es5的规则- 默认的Babel只支持一些基础的特性转换,高级的转换需要对象的转换需要
core-js/stable
和regenerator-runtime/runtime
, - 关于
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模块之间的关系,并合成一个cssstyle-loader
会吧css-loader生成的内容,以style挂载到页面的head部分sass-loader
把sass语法转换成csspostcss-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,
}),
]