Webpack(三)静态资源处理与多页面实现方案

474 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情

Webpack 是一个前端资源加载/打包工具。它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源。

Webpack 系列专栏地址 进入

本文demo地址查看

本文主要写了 图片的处理、压缩,第三方字体处理,多页面打包的集成方案。

图片的处理

使用file-loaderurl-loader 处理图片文件。

file-loader

使用图片的场景:

  • js 操作 dom
  • css background样式
  • html img标签

新建 images 目录,用来存放图片。

js 操作 dom

index.js:

import pic from "./images/1.jpg";

const img = new Image();
img.src = pic;  
console.log(pic);  //图片路径

const tag = document.getElementById("app");
tag.append(img);

安装file-loader

npm i file-loader -D

webpack.config.js配置:

			{
				test: /\.(jpg|png|gif|webp)$/,
				use: "file-loader",
			},

执行npx webpacknpm run build打包命令,dist目录下多了我们引入的图片,运行html文件,图片也在页面中显示。不过图片的命名有点丑,下面通过配置改变命名的问题。

修改配置:

{
	test: /\.(jpg|png|gif|webp)$/,
	// use: "file-loader",
	use: {
		loader: "file-loader",
		options: {
			name: "[name].[ext]",
                        outputPath: "images", // 图片资源存放目录
		},
	},
},

使用[name]名称占位符 和 [ext]后缀名占位符优化,打包出来的图片名称和原名称保持一致。outputPath参数为图片资源提供一个单独的目录。所有图片资源文件将被打包进images目录下。

css 背景图

index.less 中添加代码:

    // background-color: green;
    background-image: url(../images/1.jpg);

执行打包命令,发现背景由原来的绿色变成了背景图片,没有任何问题。

对css文件做目录管理:

接下来,尝试把打包后的css文件也单独放到一个style文件夹下(之前的步骤,打包完成的css文件是直接存放在了dist根目录下),修改配置项:

	plugins: [
		new minicssPlugin({
                	// filename: "index.css",
                          // 将css文件放到style目录下
			filename: "style/index.css",
		}),
	],

再次打包,就会发现问题,找不到背景图片了,打包后的css代码是 background-image: url(images/1.jpg);。此时相当于在style目录下找这个图片,那肯定是找不到的。

此时正确的路径应该是 ../images/1.jpg,给file-loader添加 publicPath 字段,可以解决这个问题。

{
	test: /\.(jpg|png|gif|webp)$/,
	// use: "file-loader",
	use: {
		loader: "file-loader",
		options: {
			name: "[name].[ext]",
			outputPath: "images", // 图片资源存放目录
			publicPath: "../images", // 图片资源引入位置
		},
	},
},

打包后背景图的资源路径的值应该是 options.publicPath + options.name的值,也就是 ../images/1.jpg,即我们期望返回的路径。

此时查看页面,背景图已经正常显示,但是 js 创建的 img 标签的路径却无法找到,图片不能正常显示,因为html文件路径是在dist根目录的,在根目录下寻找图片../images/1.jpg也是肯定找不到的。需要对html文件也做目录管理。

总结:当对css文件做目录管理的时候,会出现和图片资源路径对不上的情况,此时需要上面的特殊处理。

url-loader

url-loader 是基于 file-loader的,不同的是 url-loader可以将图片转换成 base64 形式,直接放在css中。

安装:

npm i url-loader -D

配置;

{
	test: /\.(jpg|png|gif|webp)$/,
	// use: "file-loader",
	use: {
		loader: "url-loader",
		options: {
			name: "[name].[ext]",
			outputPath: "images", // 图片资源存放目录
			publicPath: "../images", // 图片资源引入位置
			limit: 3 * 1024, // 单位:字节
		},
	},
},

部分配置项和file-loader是一致的,limit参数可以过滤掉一些太大的图片,图片所占空间小于设定的值才会被编译成base64。一般情况下,让这个值为 3kb是比较合理的。

图片压缩

使用 image-webpack-loader 进行图片压缩。

安装:

cnpm install image-webpack-loader

配置:

必须在 url-loaderfile-loader之前使用,所以配置在后面。

{
	test: /\.(jpg|png|gif|webp)$/,
	// use: "file-loader",
	use: [
		{
                    loader: 'url-loader',
                    // ...    
		},
		'image-webpack-loader',
	],
},

字体处理

处理字体同样使用 file-loader 这个loader。

新建 src/font 目录,存放字体文件。index.less 中引入字体文件并使用:

@font-face {
  font-family: "Alifont";
  src: url("../font/Alibaba_PuHuiTi_2.0_105_Heavy_105_Heavy.ttf"), 
      url("../font/3tTvB4rcK7Bp5NvvbilQp.woff2") format("woff2"),
      url("../font/3tTvB4rcK7Bp5NvvbilQp.woff") format("woff");
  font-display: swap;
}

body {
  font-family: "Alifont";
}

配置:

{
	test: /\.(woff|woff2|eot|ttf|svg)$/,
	use: {
		loader: 'file-loader',
		options: {
			name: '[name].[ext]',
			outputPath: 'font',
			publicPath: '../font', // 想要的正确路径
		},
	},
},

多页面实现方案

普通方式

普通的方式创建一个多页面:

新建 src/detail.js

console.log('detail page')

新建 public/detail.html,当作详情页模板:

<div id="detail"></div>

修改 webpack.config.js配置:

// 新增detail入口
entry: {
	index: './src/index.js',
	detail: './src/detail.js',
},

plugins: [
       // 增加
	new htmlWebpackPlugin({
		template: './public/detail.html',
		filename: 'detail.html',
		chunks: ['detail']
	})
],

多页面应用新增一个页面,需要以上的步骤。感觉上述的步骤有些繁琐,不太友好,下面来实现一个多页面应用通用方案:

通用方案

新建 mpa.config.js 配置文件,主要负责生成 entry 实例化htmlWebapckPlugin,使用自定义的 setSPA()方法完成自动化的内容。一些基础配置可以和 webpack.config.js 保持一致。

将打包出口的目录由dist改为mpa

	output: {
		path: resolve(__dirname, './mpa'),
		filename: '[name].js',
	},

需要遵守一些目录结构上的约定,src下直接创建目录命名即为页面的名称,目录下放index.html index.js文件,这样做主要是和images font 等目录做区分。如下图创建了三个页面 index login list

捕获.PNG

安装 glob ,给下面的函数使用:

npm install glob -D

glob 是node的一个模块,用于匹配符合规则的文件路径,以数组形式返回

主要的 setSPA() 函数编写,要完成以下功能:

  • 查询页面入口文件 及 相应的html模板
  • 提取页面入口名称,用于entry及chunkName
  • 实例化 htmlWebpackPlugin 存放在一个数组中
const setMPA = () => {
	const entry = {}
	const htmlwebpackplugins = []

	// 查询页面入口文件 及 相应的html模板
	// 提取页面入口名称,用于entry及chunkName
	// new htmlWebpackPlugin
	const entryPath = glob.sync('./src/*/index.js')
	console.log('entryPath----', entryPath) // ['./src/index/index.js', './src/list/index.js' ...]
	entryPath.map((item) => {
		const entryName = item.match(/src\/(.*)\/index\.js$/)[1]
		console.log(entryName) // index list login
		entry[entryName] = item
		// console.log('entry-----', entry)
		htmlwebpackplugins.push(
			// 在这里实例化是没有意义的,最终还要放在 plugins 中展开
			new htmlWebpackPlugin({
				template: join(__dirname, `./src/${entryName}/index.html`), // 使用 join 拼接成绝对路径
				filename: `${entryName}.html`,
				chunks: [entryName],
			})
		)
	})

	return {
		entry,
		htmlwebpackplugins,
	}
}

解析出返回值:

const { entry, htmlwebpackplugins } = setMPA()

setMPA() 方法返回内容写进配置文件:

module.exports = {
	mode: 'development',
	entry,
	plugins: [
		new CleanWebpackPlugin(),
		new minicssPlugin({
			// filename: "index.css",
			// 将css文件放到style目录下
			filename: 'style/index.css',
		}),
                ...htmlwebpackplugins
	],
 }

配置一个新的打包命令,指向 mpa.config.js 配置文件进行打包

package.json

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack",
    // ++
    "mpa": "webpack --config ./mpa.config.js"
  },

执行打包命令 npm run mpa,正常输出bundle文件到mpa目录下。

捕获.PNG

setMPA() 动态的生成了 entry 入口配置和 html-webpack-plugin 插件的配置,如果想新增一个页面,直接在src下按约定好的目录规则新建目录及文件,再次打包就可以很方便的自动生成我们想要的结果。