? 本质上,webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个 依赖图(dependency graph),然后将你项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源,用于展示你的内容。
1.webpack的依赖于node环境的,所以电脑必须有Node环境。
node -v 查看
npm -v 查看
2.安装webpack之前需要先安装一个npm的包管理的配置文件
npm init -y
3.安装webpack和webpack-cli
npm i webpack webpack-cli -d (局部)
npm i webpack webpack-cli -g (全局,不推荐)
//获取当前的目录
const {resolve} = require('path');//node用法
module.exports ={
//入口配置项
entry:'./index.js',
//出口项配置
output:{
filename:'built.js',
path:resolve(__dirname,'build')
},
//设置为开发环境
mode:'development',
}
webpack
之后,根目录出现build文件夹安装typescript环境npm i typescript -g
,然后tsc -v
查看版本号,并初始化typescript环境tsc --init
将index.js文件改成index.ts并在根目录新建src文件里components文件夹新建一个组件并在index.ts文件引入并使用。
运行webpack
之后报错,因为我们没有指定好模块的解析方式,我们需要跟着官网文档使用resolve.extensions。webpack.config.js文件追加了
resolve: {
extensions: ['.js', '.ts'],
},
npm i html-webpack-plugin
并在webpack.config.js文件使用后
const HtmlWebpackPlugin = require('html-webpack-plugin');
plugins: [
new HtmlWebpackPlugin({
template: "./public/index.html",
filename: "index.html",
}),
],
npm i react react-dom @types/react @types/react-dom @babel/preset-react
安装依赖
decorator
语法, 需要在tsconfig.ts中进行如下设置"experimentalDecorators": trueimport React from 'react'
import ReactDom from 'react-dom'
ReactDom.render(
//这里react脚手架中引入的时app.js组件,这里直接写死了
<h1>Hello React</h1>,
document.getElementById("root")
)
"jsx": "react"
webpack
之后报错,要处理.tsx格式的执行,因为浏览器只能识别js文件,需要执行npm i ts-loader
之后webpack.config.js增加module配置,修改resolve配置module:{
rules:[
{
test: /\.(js?|ts?|tsx?|jsx?)$/,
use: [
{
loader: 'babel-loader',
options: {
// 开启babel的缓存
cacheDirectory: true,
presets: [
[
'@babel/preset-env',
{
// useBuiltIns: usage 会根据配置的浏览器兼容,实现了按需添加
useBuiltIns: 'usage',
corejs: 3,
// 不以commonjs打包,方便tree-shaking
modules: false,
},
],
'@babel/preset-react',
'@babel/preset-typescript',
],
plugins: [
['@babel/plugin-proposal-decorators', { legacy: true }],
['@babel/plugin-proposal-class-properties'],
],
},
},
],
},
]
},
resolve:{
extensions:['.ts','.tsx','...']
},
根目录新建
1).gitignore文件——总会有些文件无需纳入git的管理,也不希望它们出现在未跟踪文件列表,想忽略文件的作用
node_modules
**/node_modules
.DS_Store
doc/userName.md
/dist
? 2)public文件夹存放项目的静态资源
? 3)build文件夹存放webpack的配置文件
? 4)src存放项目项目的代码文件
在build文件里面新增三个文件webpack.common.js、webpack.dev.js、webpack.prod.js用于区分环境的配置,两个环境有够公共部分使用第三方工具webpack-merge
将公共配置导入对应文件中,功能类似于 JavaScript 的 Object.assign()。
// build/webpack.prod.js
const { merge } = require('webpack-merge')
const path = require('path')
const common = require('./webpack.common')
const { PROJECT_PATH } = require('../constant')
module.exports = merge(common, {
mode: 'production',
devtool: false,
output: {
filename: 'js/[name].[contenthash:8].js',
path: path.resolve(PROJECT_PATH, './dist')
},
})
// build/webpack.dev.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { merge } = require("webpack-merge");
const baseConfig = require("./webpack.common.js");
//获取当前的目录
const { resolve } = require("path"); //node用法
const PROJECT_PATH = resolve(__dirname, "../"); // 项目根路径
module.exports = merge(baseConfig, {
mode: "development",
output: {
filename: "js/[name].js",
path: resolve(__dirname, "dist"),
},
module: {
rules: [{ test: /\.tsx$/, use: ["ts-loader"], exclude: /node_modules/ }],
},
});
哈希值区别
hash:
? 每次修改任何一个文件,所有文件名的hash至都将改变,所以一旦修改了任何一个文件,整个项目的文件缓存都将失效
chunkHash:
? 根据chunk生成的hash值,如果打包来源于同一个chunk,那么hash值就一样,chunkHash不适用于同一chunk的文件,如一个js文件导入了一个css文件,他们属于同一个chunk,因此若只修改了js,最终打包出来的文件cs和js都会变成一个新的hash
contenthash:
? 根据文件内容生成hash值,不同文件的hash值一定不一样(只要文件内容不做修改,一定是同一个hash,有变动则会替换成另外的),这样就令浏览器只清楚掉变动文件的缓存(只有改动的文件重命名了)
npm i cross-env
配置统一的Node环境变量
使用 process.env.NODE_ENV获取当前环境
"scripts": {
"dev": "cross-env NODE_ENV=development webpack-dev-server -- config build/webpack.dev.js",
"build": "cross-env NODE_ENV=production webpack -- config build/webpack.prod.js",
}
npm i webpack-dev-server
提供本地服务参考链接
devServer的配置功能如下:
historyApiFallback: 使用HTML5 History API时,index.html
可能需要提供页面来代替任何404
响应
static: 该配置项允许配置从目录提供静态文件的选项(默认是 ‘public’ 文件夹)
client: 允许在浏览器中设置日志级别,
proxy: 当拥有单独的 API 后端开发服务器并且希望在同一域上发送 API 请求时,代理某些 URL 可能会很有用。
host:服务ip
port:服务端口
stats:设为errors-only表示终端只打印错误类型的日志,不会打印warning以及其他信息影响阅读
compress:设为true表示启用gzip压缩,加快网站打开速度
open:设为true表示第一次启动项目时自动打开默认浏览器
hot:设为true表示启用服务热替换配置
clientLogLevel:设为none表示去除多余网页console信息
…
// build/webpack.dev.js
devServer: {
historyApiFallback: true,
// noInfo: true,
static: [
{
directory: path.join(__dirname, '../public'),
// serveIndex: {} (options for the `serveIndex` option you can find https://github.com/expressjs/serve-index)
// serveIndex: true,
},
],
client: {
overlay: {
errors: true,
warnings: false,
},
logging: 'error',
progress: true,
reconnect: 3,
},
hot: true,
compress: true,
// open: true, //在mac上为true才会打开浏览器,故在node启动文件又添加了open
open: {
app: {
name: 'google-chrome',
arguments: ['--new-window'],
},
},
proxy: {
'/': {
},
},
},
为了实现js的热替换,此时需要在入口文件的执行代码最开始处配置一下 module.hot.accept
逻辑
npm i @types/webpack-env
:包含 webpack 的 api 声明文件// index.tsx
if (module && module?.hot) {
module?.hot.accept();
}
进度条npm i webpackbar
展示
// build/webpack.common.js
const WebpackBar = require('webpackbar')
module.exports = {
plugins: [
new WebpackBar({
name: 'Link Startou!!!',
color: '#52c41a'
}),
]
}
分别运行npm run dev和npm run build查看效果
配置css——webpack支持原生js和html打包,需要下载第三方包并配置
npm i style-loader
:将 js 文件中引入的 css 代码插入到 html 模板文件,使网页可以正常展示样式
npm i mini-css-extract-plugin
:和 style-loader 功能一样,只是打包后会单独生成 css 文件而非直接写在 html 文件中,用于生产环境,开发环境不需要另外生成文件使用 style-loader 即可
npm i css-loader
:令 js 可以通过 import 或者 require 等命令导入 css 代码
压缩优化css
npm i css-minimizer-webpack-plugin
压缩生产环境打包后的css文件
optimization
属性,专门用于存放优化打包的配置,minimizer属性存放一个数组,里可以存放用于代码压缩的插件,minimize 置 true 表示启用 minimizer 配置浏览器兼容问题
npm i postcss-loader
:与 sass/less 不同,不是预处理器,相当于一个工具箱,可以使用它配合插件去转换css
npm i postcss-preset-env
:将最新的 css 语法转换为目标环境的浏览器能够理解的语法,不用考虑浏览器兼容问题,以前需要配合 autoprefixer 第三方包自动补全前缀,现在新版本已经内置autoprefixer功能
处理css的时候需要添加浏览器前缀插件的选择结合上面代码autoprefixer可为css添加浏览器前缀
1)根目录新建.browserslistrc文件或者在package.json配置browserslist属性
? 2)配置 browserslist
字段会导致 webpack-dev-server
的热更新功能直接失效,为了避免这种情况需要给 webpack 配上 target
属性——webpack.dev.js中增加 target: ‘web’,webpack.prod.js中增加 target: ‘browserslist’
// package.json文件
{
"browserslist": [ //注意是一个数组
"last 1 version",
"> 1%",
"maintained node versions"
]
}
//.browserslistrc文件
last 2 versions # 所有浏览器兼容到最后两个版本根据CanIUse.com追踪的版本
> 2% # 全球超过0.5%人使用的浏览器,可以通过 caniuse.com 查看不同浏览器不同版本占有率
// build/webpack.prod.js
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
module.exports = merge(common, {
// ...other
optimization: {
minimize: true,
minimizer:[
new CssMinimizerPlugin()
]
}
})
// build/webpack.common.js
const getCssLoaders = () => {
const isDevelopment = process.env.NODE_ENV === 'development'
const isProduction = process.env.NODE_ENV === 'production'
const cssLoaders = [
isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
modules: {
localIdentName: "[local]--[hash:base64:5]"
},
sourceMap: isDevelopment,
}
}
]
// 开发环境一般用chrom不会有问题,防止开发环境下看样式有一堆前缀影响查看,因此只在生产环境使用
isProduction && cssLoaders.push({
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
isProduction && [
'postcss-preset-env',
{
//开启自动添加前缀功能,有些功能是默认关闭的,如栅格样式一些浏览器不支持所以默认关闭了,这里手动打开
autoprefixer: {
grid: true
}
}
]
]
}
}
})
return cssLoaders
}
module: {
rules: [
{
test: /\.css$/,
use: [...getCssLoaders()]
},
}
npm i less-loader
将less文件编译成css文件,再做上述css文件的打包工作。npm i less
// build/webpack.common.js
module: {
rules: [
{
test:/\.less$/,
use:['style-loader','css-loader','less-loader']
},
}
打包图片资源及打包img标签中的图片
npm i url-loader
npm i file-loader
处理打包图片资源module: {
rules: [
{
test: /\.(png|jpe?g|gif|webp)$/,
// 要使用一个loader
// 下载 url-loader file-loader
dependency: { not: ['url'] },
use:[
{ loader: 'url-loader',
options: {
// 图片大小小于8kb,就会被base64处理
// 优点:减少请求数量(减轻服务器压力)
// 缺点:图片体积会更大(文件请求速度更慢)
limit: 8 * 1024,
// 问题:因为url-loader默认使用es6模块化解析,而html-loader引入图片是commonjs
// 解析时会出现问题:[object Module]
// 解决:关闭url-loader的es6模块化,使用commonjs解析
esModule:false,
// 给图片进行重命名
// [hash:10]取图片的hash前10位
// [ext]取文件原来拓展名
name:'[hash:10].[ext]'
},}
],
type: 'javascript/auto'
},{
test: /\.html$/,
// 处理html文件的img图片(负责引入img,从而能被url-loader进行处理)
loader: 'html-loader',
}
}
asset/resource
发送一个单独的文件并导出 URL。之前通过使用 file-loader
实现。asset/inline
导出一个资源的 data URI。之前通过使用 url-loader
实现。asset/source
导出资源的源代码。之前通过使用 raw-loader
实现。asset
在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader
,并且配置资源体积限制实现。// build/webpack.prod.js
//在output中统一Asset Module 输出路径
output: {
publicPath: '/',
filename: 'js/[name].[contenthash:8].js',
path: pathResolve('../dist'),
assetModuleFilename: 'images/[name].[contenthash:8].[ext]',
},
// build/webpack.common.js
module: {
rules: [
{
// 处理图片资源 webpack5图片新打包方法
test: /\.(png|jpe?g|gif|webp)$/,
// webpack5中使用assets-module(url-loader已废弃)
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 10 * 1024,// 小于10kb的图片会被base64处理
}
},
generator: {
// 将图片文件输出到 static/imgs 目录中
// 将图片文件命名 [hash:8][ext][query]
// [hash:8]: hash值取8位
// [ext]: 使用之前的文件扩展名
// [query]: 添加之前的query参数
filename: "static/imgs/[hash:8][ext][query]",
publicPath: './'
}
},{
test: /\.html $/,
// 处理html文件的img图片(负责引入img,从而能被url-loader处理)
// webpack5中使用 html-withimg-loader代替loader: 'html-loader'
loader: 'html-withimg-loader'
}
}
打包统一字体
// build/webpack.common.js
module: {
rules: [
{
// 加载字体图标
test: /\.(eot|ttf|svg|woff)$/,
type: "asset/resource",
generator: {
// 输出到 font 目录中,占位符 [name] 保留原始文件名,
// [hash] 防止出现相同文件名无法区分,[ext] 拿到后缀名
filename: "font/[name].[hash:6][ext]",
},
},
],
},
npm install mini-css-extract-plugin --save-dev
npm install css-minimizer-webpack-plugin --save-dev
// build/webpack.common.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
module.exports = {
optimization: {
minimizer: [
// 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释
// `...`,
new CssMinimizerPlugin(),
],
},
plugins: [new MiniCssExtractPlugin()],
};
terser-webpack-plugin
:用去去除生产环境的无用js代码,webpack5 之后自带,不需要另行安装,直接引入使用即可
@preserve
标记console.log
函数去除// build/webpack.prod.js
const TerserPlugin = require("terser-webpack-plugin")
module.exports = merge(common, {
optimization: {
minimize: true,
minimizer:[
new TerserPlugin({
extractComments: false,
terserOptions: {
compress: { pure_funcs: ['console.log'] },
}
}),
]
}
})
clean-webpack-plugin
:清除上一次打包的 dist 目录内容,防止文件残留// build/webpack.common.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
...
plugins: [
...
new CleanWebpackPlugin(),
],
}
缓存机制——大大增快二次编译速度,webpack5 已内置该功能【cache机制及配置参考】
cache.type:缓存类型,值为 memory 或 filesystem,分别代表基于内存的临时缓存,以及基于文件系统的持久化缓存
cache.buildDependencies:全局缓存失效的一种机制,配置 {config: [__filename]},表示当配置文件内容或配置文件依赖的模块文件发生变化时,当前的构建缓存即失效
// build/webpack.common.js
module.exports = {
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename],
},
},
}
// 在react中比较常见的是React.lazy
const MyComponent = React.lazy(() => import('Components/MyComponent'))
将第三方依赖打包独立chunk,需要在webpack额外进行配置
? splitChunks:代码分割相关配置
? splitChunks.chunks:选择哪些内容进行优化,如果为 all 时表示即使同步和异步的代码也可以共享thunk
? minSize:生成chunk的最小大小(以字节为单位)
// build/webpack.dev.js
module.exports = merge(common, {
optimization: {
minimize: false,
minimizer: [],
splitChunks: {
chunks: 'all',
minSize: 0,
},
},
})
// build/webpack.prod.js
module.exports = merge(common, {
optimization: {
// ...other
splitChunks: {
chunks: 'all',
minSize: 0,
},
}
})