Webpack是Javascrpit程序的一个静态模型捆绑器。当webpack处理你的应用程序时,它在内部建立了一个依赖关系图,映射了项目中依赖的每个模块,并生成多个捆绑包。
在模块化设计中,开发者将程序拆散成离散的功能块,称之为模块。每个模块都有自己独立的作用域,方便验证、调试和测试。编写良好的模块,可以提供更好的抽象和封装,这样在应用程序中的每个模块都有一致的设计原则和明确的目的。
一、前言
webpack也用了一段时间了,在这里总结一下webpack的知识点。这个系列主要分为一下三个部分
- 在本地使用webpack搭建一个项目
- 测试本地项目,并发布一个NPM包
- 使用GITHUB的Action,自动发布NPM包
二、准备工作
- 安装 noode
- 新建一个文件夹,作为项目的根目录,以blog1997为例
然后在命令行(CMD)中,进入项目根目录,初始化项目
之后,根据提示,输入相关信息npm init
然会在项目的根目录下,会创建一个package.json文件,在这里,你可以随意修改项目的相关配置。This utility will walk you through creating a package.json file. It only covers the most common items, and tries to guess sensible defaults. _ See `npm help init` for definitive documentation on these fields and exactly what they do. _ Use `npm install <pkg>` afterwards to install a package and save it as a dependency in the package.json file. _ Press ^C at any time to quit. package name: (blog1997) // 项目名称,默认是文件夹名称,回车即可 version: (1.0.0) // 项目的版本号,默认1.0.0,回车即可 description: // 项目的描述,默认是空字符串,回车即可 entry point: (index.js) // NPM包的入口文件,会在下一章介绍,回车即可 test command: // 测试命令,回车即可 git repository: // github仓库的地址,回车即可 keywords: // 项目的关键字,回车即可 author: // 作者信息,回车即可 license: (ISC)
{ "name": "blog1997", // 对应之前输入的项目名称 "version": "1.0.0", // 对应之前输入的version:(1.0.0) "description": "", // 对应之前输入的description "main": "index.js", // 对应之前输入的entry point "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", // 对应之前输入的author "license": "ISC" }
- 在项目根目录中创建一个文件夹src,并创建index.js文件
三、安装webapck
目前使用的版本是webpack ^5.18.0, webpack-cli ^4.4.0, webpack-dev-server ^3.11.2npm i --save-dev webpack webpack-cli webpack-dev-server
- webpack-cli 为开发者提供了一些灵活的命令,加速开发者自定义webpack项目
- webpack-dev-server 提供了一个实时重载的开发服务器,但只能在开发过程中使用
- --save-dev表示实在开发模式中使用的包,npm会将包的信息放在package.json文件中的devDependencies属性中,默认值是--save,包的信息会存放在dependencies属性中。
然后配置webpack,在项目的根目录中,创建一个文件webpack.config.js(名字可以自定义)const path = require("path"); module.exports = { mode: 'development', // 设置为开发模式, 值:development|production entry: "./src/index.js", // 项目模块的入口(索引)文件 output: { // 输出打包模块的配置 path: path.resolve(__dirname, "dist"), // 表示项目根目录下的dist文件夹 filename: "bundle.js" }, devServer: { port: 8081 // 可以自定义服务器端口 } }
四、安装SASS
npm install --save-dev style-loader css-loader sass-loader sass
- sass 是Dart sass的发行版。提供了一些Node.js API和一些命令行命令。
- sass-loader 将scss或sass文件编译成css
- css-loader 将css解析到JavaScript代码中,并处理其中的相关依赖
- style-loader 将css注入到DOM中的Style标签里
然后配置webpack.config.jsmodule.exports = { entry: "./src/index.js", module: { rules: [{ test: /\.s?[ac]ss$/, // 匹配css、scss和sass文件的正则 use: [{ loader: 'style-loader' }, { loader: 'css-loader' }, { loader: 'sass-loader' }] }], }, }
五、安装babel
目前使用的版本是:@babel/core ^7.12.10,@babel/preset-env,^7.12.11,babel-loader,^8.2.2npm install --save-dev @babel/preset-env babel-loader @babel/core
配置一下babel,在项目根目录中创建文件 .babel.json
useBuiltIns: "entry" | "usage" | false,默认是false,这个选项用于配置 @babel/preset-env是怎么处理 polyfills的{ "presets": [ [ "@babel/preset-env", { "targets": { "ie": "10", "chrome": "67" }, "useBuiltIns": "usage", "corejs": "3.6.5" } ] ] }
entry: 会根据targets环境的不同,将 import "core-js/stable;"**和 **import "regenerator-runtime/runtime" 声明替换为特定的import
usage: 在每个文件中为polyfill添加特定的导入。相同的polyfill只会添加一次。
false:不自动添加polyfill 查看更多@babel/preset-env的 配置
配置webapck.config.jsmodule.exports = { ... module: { rules: [{ test: /\.?js$/, // 匹配js文件的正则表达式 exclude: /(node_modules|bower_components)/, // 不对node_modules和bower_components文件夹下的js文件进行处理 use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } } }
六、html-webpack-plugin
之前安装的babel和sass都属于webpack的loader范畴,用于转换某些特定类型的模块。而插件执行的任务更加广泛,包括:打包优化,资源管理,注入环境变量。
目前使用的版本是5.2.0。然后在项目根目录中新建一个文件app.htmlnpm install html-webpack-plugin --save-dev
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title><%= htmlWebpackPlugin.options.title %></title> </head> <body></body> </html>
**<%= htmlWebpackPlugin.options.title %>**会读取webpack.config.js配置文件中的Title属性。
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
...
plugins: [
new HtmlWebpackPlugin({
title: 'Blog1997', // 标题
template: 'app.html' // 模板文件路径
})
]
}
修改package.json
{
"name": "blog1997",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "webpack serve --config webpack.config.js"
}
...
}
运行 npm run dev 就可以在本地打开开发服务器了,我们之前创建的src/index.js会被webapck打包,并添加到模板中。如下图所示
七、file-loader
npm install --save-dev file-loader
file-loader可以帮助你打包一些文件,如JPG、PNG和TTF等。
配置webpack.config.js
module.exports = {
...
module: {
rules: [
{
test: /\.(png|svg|jpg|gif)$/, // 匹配文件的正则表达式
use: [
{
loader: 'file-loader?name=./img/[name].[ext]'
}
]
}
]
}
}
查看更多关于file-loader的 配置
八、分割JS代码
在中型或大型项目中,前端的代码量也是非常大的。如果将所有的JS代码打包在一个文件中,文件的加载时非常慢的,用户的初次体验也不是很友好。所以,我们在这种情况下,需要分割代码,将代码打包在不同的文件中,按需加载。
npm install @babel/plugin-syntax-dynamic-import --save-dev
修改 .babel.json 配置
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"ie": "10",
"chrome": "67",
},
"useBuiltIns": "usage",
"corejs": "3.6.5"
}
]
],
"plugins": ["@babel/plugin-syntax-dynamic-import"]
}
修改 webapck.config.js 配置
const path = require("path");
module.exports = {
mode: 'development',
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].bundle.js"
}
}
修改代码
// chat.js
export default {
say () {
// ...
}
}
// main.js
button.onclick = () => {
import(/* webpackChunkName: 'dynamic' */ './chat.js').then(chat=> {
chat.default.say()
})
}
九、clean-webpack-plugin
在使用webpack-server服务或打包之前,清空dist文件夹
npm i --save-dev clean-webpack-plugin
修改 webpack.config.js
const path = require("path");
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
mode: 'development',
...
plugins: [
new CleanWebpackPlugin({
cleanStaleWebpackAssets: true
})
]
}
十、uglify js Webpack plugin
用于压缩JS代码,并去掉注释部分
npm install uglifyjs-webpack-plugin --save-dev
配置 webpack.config.js
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
mode: 'development',
...
optimization: {
minimizer: [new UglifyJsPlugin({
uglifyOptions: {
output: {
comments: false,
beautify: false
},
compress: true,
warning: false
}
})]
}
}
十一、webpack-merge
webpack分为开发环境和编译环境,有时候我们需要根据环境的需求,对webpack进行不同的配置。这时,我们就可以使用webpack-merge提取出配置中的公共部分。
首先在项目根目录下(也可以创建一个文件夹)创建三个文件 webpack.common.js、webpack.dev.js和webpack.prod.js
webpack.common.js内容如下
const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
// 入口js文件的位置
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, '../dist/'),
filename: "[name].bundle.js"
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules)/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"]
}
}
},
{
test: /\.(sa|sc|c)ss$/i,
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader',
}, {
loader: 'sass-loader'
}
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [{
loader: 'file-loader?name=./img/[name].[ext]'
}]
}
]
},
plugins: [
new CleanWebpackPlugin({
cleanStaleWebpackAssets: true
})
],
optimization: {
// 压缩js文件的相关设置
minimizer: [new UglifyJsPlugin({
uglifyOptions: {
output: {
comments: false,
beautify: false
},
compress: true,
warning: false
}
})],
splitChunks: {
chunks: 'all',
}
},
// 定义变量
resolve: {
extensions: ['.js'],
alias: {
'~': path.resolve(__dirname, '../src/') // 将~ 定义为项目根目录
}
}
}
webpack.dev.js内容如下
const merge = require('webpack-merge')
const common = require('./webpack.common.js')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = merge(common, {
entry: "./src/index.js",
mode: 'development',
devServer: {
hot: true,
port: 8088,
historyApiFallback: true
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
// HTML模板的相关设置
new HtmlWebpackPlugin({
title: 'blog1997',
template: 'app.html'
})
]
})
webpack.prod.js内容如下
const merge = require('webpack-merge')
const common = require('./webpack.common.js')
module.exports = merge(common, {
mode: 'production'
})
最后修改一下 package.json
{
"scripts": {
"dev": "webpack-dev-server --config ./config/webpack.dev.js",
"build": "webpack --config ./config/webpack.prod.js"
}
}
十二、webpack-bundle-analyzer
使用交互式可缩放树图,可视化输出文件的大小。
npm i --save-dev webpack-bundle-analyzer
配置 webpack.prod.js
const merge = require('webpack-merge')
const common = require('./webpack.common.js')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = merge(common, {
mode: 'production',
plugins: [
new BundleAnalyzerPlugin({
generateStatsFile: true,
analyzerMode: 'static'
}),
]
})
当你在运行完 npm run build 之后,浏览器会自动弹出如下页面
十三、其他小技巧
手动指定loader
import styleContent from 'file-loader!extract-loader!css-loader!./mathquill.css'
就先总结到这吧,更多关于webpack的详细配置以及使用,请参阅 webpack官网