webpack 工程环境搭建
环境搭建基本步骤
新建项目,比如 webpack-study
判断单个前端,还是团队
处理安装依赖
首先优化安装依赖镜像源,根目录新建文件 .npmrc
(可选)
registry=https://registry.npm.taobao.org
安装 webpack 基本环境
npm init -y
npm install webpack webpack-cli --save-dev
在根目录新建 webpack.config.js
和入口文件 src/index.js
,目录结构
webpack-study
|- /dist
|- main.js
|- index.html
|- /node_modules
|- /src
|- index.js
|- .npmrc
|- package.json
|- package-lock.json
|- webpack.config.js
选择合适的样式 静态资源(图片第三方字体) es6+(vue react ts)
比如样式:借助 css-loader
处理 css 的语法,借助 style-loader
使用css
如果使用 less, sass 之类的,需要安装对应的 less-loader
, sass-loader
, 如
npm install less less-loader --save-dev
然后在 webpack.config.js 中添加
module.exports = {
module: {
rules: [
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader'],
},
],
},
};
这里需要注意,因为一个 loader 只做一件事情,
less-loader
处理.less
需要依赖less
插件所以需要同时安装less
和less-loader
, 参考:https://webpack.docschina.org/loaders/less-loader
postcss
处理编译 css
一般常用 autoprefixer
自动添加 css 浏览器前缀和 cssnano
压缩 css 代码, 需要安装 postcss-loader
,postcss
,autoprefixer
,cssnano
npm install --save-dev postcss-loader postcss autoprefixer cssnano
新建 postcss.config.js
文件配置
module.exports = {
plugins: [require('autoprefixer'), require('cssnano')],
};
然后在 webpack.config.js 中添加
module.exports = {
module: {
rules: [
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'postcss-loader', 'less-loader'],
},
],
},
};
此时基本配置已完成,但是 为了兼容到目标浏览器,需要配置下
browserslist
第一种写法,在 package.json
中添加
{
"browserslist": ["last 2 versions", "> 1%"]
}
另外一种,直接新建 .browserslistrc
使用换行作为条件组合
last 2 versions
> 1%
第三种写法(不推荐),直接在 postcss.config.js
以参数传入
module.exports = {
plugins: [
require('autoprefixer')({
overrideBrowserslist: ['last 2 versions', '> 1%'],
}),
],
};
last 2 versions
表示浏览器最近两个大版本,比如可以写成last 2 chrome version
, 最新 chrome 浏览器版本 96.0.4664.93(正式版本),那么久可以兼容到 96版和95版> 1%
表示浏览器份额大于 1% 的浏览器
完整参考:https://github.com/browserslist/browserslist#full-list
browserslist的数据来源 http://browserl.ist/
执行 npx browserslist
可以查看支持的浏览器版本
mini-css-extract-plugin
提取 css 到单独的文件
安装
npm install --save-dev mini-css-extract-plugin
注意 module
和 plugins
都要配置
const miniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
module: {
rules: [
{
test: /\.less$/,
use: [
miniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'less-loader',
],
},
],
},
plugins: [
new miniCssExtractPlugin({
filename: '[name].css',
}),
],
};
如何编写一个自定义 loader
新增 loader 文件 myLoaders/replace-loader.js
// loader 是一个函数
//! 不可以是箭头函数
// 必须有返回值,返回一个 str buffer
// 支持配置 this.query
// 如何返回多个信息 this.callback
// 如何处理异步 this.async
// 如何处理多个 loader
module.exports = function (source) {
console.log(this.query);
return source.replace(/hello/g, 'hi');
};
和 loader 文件 myLoaders/replace-async-loader.js
// loader 是一个函数
//! 不可以是箭头函数
// 必须有返回值,返回一个 str buffer
// 支持配置 this.query
// 如何返回多个信息 this.callback
// 如何处理异步 this.async
// 如何处理多个 loader
module.exports = function (source) {
console.log(this.query);
// const msg = source.replace(/index/g, this.query.info)
// return this.callback(null, msg)
const callback = this.async();
setTimeout(() => {
const msg = source.replace(/index/g, this.query.info);
return callback(null, msg);
}, 2000);
};
即可在 webpack.config.js
中使用了
module.exports = {
resolveLoader: {
modules: ['node_modules', './myLoaders'], // 在配置的目录下寻找 module
},
module: {
rules: [
{
test: /\.js$/,
use: [
'replace-loader',
{
loader: 'replace-async-loader',
options: {
info: 'lzw',
},
},
],
},
],
},
};
Loader 最佳实践
实现 style-loader
, css-loader
, less-loader
的功能,对应的文件
myLoaders/k-less-loader
文件
// 把 less 语法 编译成 css
const less = require('less');
module.exports = function (source) {
less.render(source, (error, output) => {
this.callback(error, output.css);
});
};
myLoaders/k-css-loader
文件
// 把 css 转 字符串
module.exports = function (source) {
return JSON.stringify(source);
};
myLoaders/k-style-loader
文件
// 动态创建 style
// 把 source 塞进 style
// 把 style 放进文件 head 中
module.exports = function (source) {
return `
const tag = document.createElement('style')
tag.innerHTML = ${source}
document.head.appendChild(tag)
`;
};
这样就可以在 webpack.config.js
中使用了
module.exports = {
resolveLoader: {
modules: ['node_modules', './myLoaders'], // 在配置的目录下寻找 module
},
module: {
rules: [
{
test: /\.less$/,
use: ['k-style-loader', 'k-css-loader', 'k-less-loader'],
},
],
},
};
图片处理
webpack 4.x
使用 file-loader
, url-loader
处理图片,安装
url-loader
包含了file-loader
,所以可以只使用url-loader
npm i -D file-loader url-loader
然后在 webpack.config.js
中配置
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif|webp)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
public: '../images', // css 中添加资源的目录
outputPath: 'images', // 输出目录
limit: 8192, // 字节,小于 8K 的图片转 base64
},
}, // 还是使用 webpack 4.x 的方法
// type: 'asset/inline' // webpack 5.0 的用法 相当于 url-loader
},
],
},
};
webpack 5.x
改用资源模块(asset module),直接 webpack.config.js
中配置
module.exports = {
output: {
assetModuleFilename: 'images/[hash][ext][query]', // webpack 5.x 图片等资源输出目录
},
module: {
rules: [
{
test: /\.(png|jpe?g|gif|webp)$/,
type: 'asset/inline', // webpack 5.0 的用法 相当于 url-loader
},
],
},
};
第三方字体处理
在 webpack.config.js
中配置
module.exports = {
module: {
rules: [
{
test: /\.(woff|woff2|svg|eot)$/,
// 使用 webpack 4.x 的方法
// use: {
// loader: 'file-loader',
// options: {
// name: '[name].[ext]',
// public: '../fonts', // css 中添加资源的目录
// outputPath: 'fonts', // 输出目录
// }
// },
// webpack 5.0 的用法 相当于 file-loader
type: 'asset/resource',
generator: {
filename: 'fonts/[hash][ext][query]',
},
},
],
},
};
配置后,即可在 css 中使用
@font-face {
font-family: 'webfont';
font-display: swap;
src: url('../fonts/webfont.woff') format('woff');
}
body {
font-family: 'webfont';
}
webpack 文件指纹策略
module.exports = {
output: {
// 输出的文件名称, 多出口 name 对应 entry 里的: index/a
filename: '[name]-[hash:4]-[chunkhash:4]-[contenthash:4].js',
},
};
[hash]
是以项目为单位,项目内容改变,则生成 hans 改变[chunkhash]
以 chunk 为单位,任意一个文件内容改变,则整个 chunk 组的模块 hash 都改变[contenthash]
以自身内容为单位 一般来说,css 文件使用contenthash
, js 文件使用chunkhash
使用 clean-webpack-plugin
清空构建目录
安装
npm i -D clean-webpack-plugin
在 webpack.config.js
中配置后,执行构建时会自动清空 dist
目录老的文件
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
plugins: [new CleanWebpackPlugin()],
};
多页面打包通用方案
首先需要规划好目录,比如 list, index 页面目录
webpack-study
|- /src
|- /index
|- index.html
|- index.js
|- /list
|- index.html
|- index.js
然后需要安装下 glob
文件匹配工具
npm i -D glob
新增 webpack.mpa.config.js
, 可以添加命令行 "mpa": "webpack --config ./webpack.mpa.config.js"
到 package.json
const path = require('path');
const htmlWebpackPlugin = require('html-webpack-plugin');
const miniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const glob = require('glob');
const setMPA = () => {
const entry = {};
const htmlWebpackPlugins = [];
const entryFiles = glob.sync(path.join(__dirname, './src/*/index.js'));
entryFiles.map((item, index) => {
const entryFile = entryFiles[index];
const match = entryFile.match(/src\/(.*)\/index\.js$/);
const pageName = match[1];
entry[pageName] = entryFile;
htmlWebpackPlugins.push(
new htmlWebpackPlugin({
template: path.join(__dirname, `./src/${pageName}/index.html`),
filename: `${pageName}/index.html`,
chunks: `[${pageName}]`, // 对应 entry 里的: index/list
}),
);
});
return {
entry,
htmlWebpackPlugins,
};
};
const { entry, htmlWebpackPlugins } = setMPA();
module.exports = {
entry,
output: {
path: path.resolve(__dirname, './mpa'), // 输出的文件目录,必须是绝对路径
filename: 'js/[name]-[chunkhash:8].js', // 输出的文件名称, 多出口 name 对应 entry 里的: index/list
assetModuleFilename: 'images/[hash][ext][query]', // webpack 5.x 图片等资源输出目录
},
mode: 'development', // none development production
// loader
module: {
rules: [
//...
{
test: /\.css$/,
// 多个 loader 情况下,执行顺序是自后往前的
use: ['style-loader', 'css-loader'],
},
{
test: /\.less$/,
use: [
miniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'less-loader',
],
},
{
test: /\.(png|jpe?g|gif|webp)$/,
type: 'asset/resource', // webpack 5.0 的用法 相当于 url-loader
},
{
test: /\.(woff|woff2|svg|eot)$/,
// webpack 5.0 的用法 相当于 file-loader
type: 'asset/resource',
generator: {
filename: 'fonts/[hash][ext][query]',
},
},
],
},
// plugin
plugins: [
...htmlWebpackPlugins,
new CleanWebpackPlugin(),
new miniCssExtractPlugin({
filename: 'css/[name]-[contenthash:4].css',
}),
],
};
整体上就是通过 setMPA
函数动态生成 entry
和 htmlWebpackPlugins
以达到多页面无需手动配置