源码地址:https://gitee.com/cyp926/webpack-project.git
"webpack": "^5.46.0",
"webpack-cli": "^4.7.2",
一、webpack
1.1 webpack是什么
webpack是一种前端资源构建工具,一个静态模块打包器.在webpack看来,前端的所有资源文件(js/json/css/image/less/sass...)都会作为模块处理。它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源
配置文件: webpack.config.js
1.2 五个核心内容
- entry:入口。webpack是基于模块的,使用webpack首先需要指定模块解析入口(entry),webpack从入口开始根据模块间依赖关系递归解析和处理所有资源文件。
- output:输出。源代码经过webpack处理之后的最终产物。
- loader:模块转换器。本质就是一个函数,在该函数中对接收到的内容进行转换,返回转换后的结果。因为 Webpack 只认识 JavaScript,所以 Loader 就成了翻译官,对其他类型的资源进行转译的预处理工作。
- plugin:扩展插件。基于事件流框架 Tapable,插件可以扩展 Webpack 的功能,在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
- mode 模式。通过选择 development 或 production 之中的一个,来设置 mode 参数,你可以启用相应模式下的 webpack 内置的优化
1.3 下载webpack的插件
npm init -y
npm i -D webpack webpack-cli
1.4 基本使用
const path = require('path')
module.exports = {
// https://webpack.docschina.org/configuration/mode/
// 打包模式 development | production
mode: 'development',
// 项目入口
entry: './src/index.js',
// 项目出口
output: {
path: path.resolve(path.dirname(__dirname), 'dist'),
// js/打包的位置, [name]默认的名称为main ,-[hash:8] 拼接hash
filename: 'js/[name]-[hash:8].js'
}
}
运行打包命令行
- npx webpack
- npx webpack --mode development (不写配置情况下)
- npx webpack 需要打包的文件 -o 打包的文件位置与名称 --mode development (不写配置情况下)
快捷配置
我们可以直接在package.json中配置:"build":"webpack"
1.5 打包html
- 安装:html-webpack-plugin
1)引入
const HtmlWebpackPlugin = require('html-webpack-plugin')
2)打包压缩html(打包多个,就实例化new多个)
plugins: [
new HtmlWebpackPlugin({
title: '欢迎来到Webpack',打包设置标题 (可选项)
template:'./src/index.html', //打包的文件
filename:'demo.html', //打包后的名称 (可选项)
chunks:[], //指定html中使用的js文件 (可选项)
minify:{ //压缩 (可选项)
// 移除空格
collapseWhitespace:true,
// 移除注释
removeComments:true,
}
}),
]
3)模板中使用定义的title
<title><%= htmlWebpackPlugin.options.title %></title>
1.6 开发服务器 devServer
- npm i -D webpack-dev-server@3 (注意这里我们用到的是3版本的)
- 命令 npx webpack serve
// 在webpack5 需要加上这个选项才会自动刷新页面
target:"web"
配置devServer
devServer:{
contentBase: path.resolve(__dirname, '../dist'),// 运行代码目录(可选)
port:3001, //端口号
compress:true,//自动更新 (可选)
open:true//自动打开浏览器 (可选)
watchOptions: { ignored: /node_modules/}, // 忽略文件 (可选)
host: 'localhost', // 域名(可选) 默认就是localhost
clientLogLevel: 'none', // 不要显示启动服务器的日志信息
overlay: false, // 如果出错,不要全屏提示
// 服务器代理 --> 解决开发环境跨域问题
proxy: {
// 一旦devServer服务器接受到 /api开头的请求,就会把请求转发到另一个服务器
'/api': {
target: 'http://localhost:3000',
// 发送请求时,请求路径重写: 将/api 去除
pathRewrite: {
'^/api': ''
}
}
}
}
package.json中的scripts中配置命令
"serve": "webpack-dev-server --config ./config/webpack.config.js"
1.7 打包css,sass等样式
1.7.1 打包css
webpack默认只支持js的打包,不支持其它类型,为了让它支持样式的打包就需要加载一些loader
- npm i -D css-loader style-loader
/*
一个用 loader:css-loader
一个以上 use:[]
从右往左
*/
module:{
rules:[
{
test: /\.css$/,
use: ['style-loader','css-loader']
}
]
}
1.7.2 打包less或者sass
- less npm i -D less-loader
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader'
]
}
- sass npm i -D node-sass sass-loader
{
test: /\.sass$/,
use: [
'style-loader',
'css-loader',
'sass-loader'
]
}
1.7.3 提取抽离css为单独文件
- npm i -D mini-css-extract-plugin
const miniCssExtractPlugin = require('mini-css-extract-plugin')
module:{
rules:[
{
test: /\.css$/,
use: [
miniCssExtractPlugin.loader, //写在第一行
'css-loader']
},
{
test: /\.css$/,
use: [
miniCssExtractPlugin.loader, //写在第一行
'css-loader',
'sass-loader'
]
},
]
},
plugins:[
new miniCssExtractPlugin({filename:'demo.css'})
]
1.7.4css3兼容处理(变异成各个浏览器支持的---前缀--)
- npm i -D postcss-loader postcss-preset-env
- package.json配置兼容浏览器
"browserslist": [
">0.2%",
"last 2 version",
"not dead"
]
- 新建文件postcss.config.js
module.exports={
plugins:[
require('postcss-preset-env')()
]
}
- 引入loader
{
test: /\.css$/,
use: [
miniCssExtractPlugin.loader,
'css-loader',
'postcss-loader'
]
},
1.7.5 压缩css
- npm i -D optimize-css-assets-webpack-plugin
plugins:[
new optimizeCssAssetsWebpackPlugin()
]
1.7.6 css去掉无用的代吗
- npm i -D purgecss-webpack-plugin
new PurgecssWebpackPlugin({
paths:glob.sync(pattern:`${PATH.src}/**/*`,options:{nodir:true})
})
1.8 webpack打包资源
- npm i -D url-loader file-loader html-loader
1.8.1 背景图打包
module: {
rules: [
{
test: /\.(png|jpeg|jpg|gif)$/,
loader: 'url-loader',
options: {
publicPath:'./images/',
outputPath: 'imgs/',
name:'[name][hash].[ext]',//命名hash+名称
limit:1024*8,//限制8k一下转base64
}
},
// 也可以这么写图片处理
{
test: /\.(png|jpeg|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
// 图片小于8kb,就会被base64处理
// 优点: 减少请求数量(减轻服务器压力)
// 缺点:图片体积会更大(文件请求速度更慢)
limit: 8 * 1024,
name: 'static/imgs/[name].[ext]',
publicPath: '/dist'
}
}
]
},
]
}
1.8.2 html图 html-loader
module: {
rules: [
{
test: /\.(html)$/,
loader: 'html-loader',
}
]
}
1.8.3 其他文件打包
像项目中字体资源是不需要进行打包处理的,可以直接的通过复制方式给打包到目标目录中
module: {
rules: [
{
// exclude 排查资源
exclude:/\.(js|json|html|css|less|scss|png|jpg)$/,
loader:'file-loader',
options:{
outputPath: 'font/', //打包位置
publicPath:'./font', //寻找位置
name:'[name][hasg].[ext]',//命名hash+名称
}
}
]
}
- 也可以这样处理静态资源
- npm i -D copy-webpack-plugin
const CopyPlugin = require('copy-webpack-plugin') plugins: [ new CopyPlugin({ patterns: [ { // 来源 from: path.resolve(__dirname, '../src/iconfont/'), // 目标 to: path.resolve(__dirname, '../dist/iconfont') }, { // 来源 from: path.resolve(__dirname, '../src/iconfont/'), // 目标 to: path.resolve(__dirname, '../dist/iconfont') } ] }) ]
1.9 eslint代码规范(airbnb)
- npm i -D eslint-loader eslint eslint-config-airbnb-base eslint-plugin-import
- package.json
"eslintConfig":{
"extends":"airbnb-base"
}
- webpack.config.js
{
test: /\.js$/,
//只检查自己写的代码,不检查第三方的
exclude:/node_modules/,
loader:'eslint-loader'
},
1.10 js兼容处理
webpack在不需要引入任何loader可以对于js进行打包处理,但是它不会对于js兼容性进行任务的处理,而我们编写的项目是需要在不同的浏览器中运行的,此时就需要对于js的兼容性在打包过程中进行对应的处理。使用babel来完成对应的js兼容处理
- npm i -D babel-loader @babel/core @babel/preset-env core-js@3
module: {
rules: [
// js兼容处理
{
test: /\.js$/,
// 排除
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
// 按需加载 inital enter uage
useBuiltIns: 'usage',
// 指定core-js版本
corejs: 3,
// 兼容性做到哪个版本的浏览器
targets: {
chrome: '80',
firefox: '50',
ie: '9',
safari: '10',
edge: '17'
}
}
]
]
}
}
]
}
二、环境优化
开发环境
- 打包构建速度
- 优化代码调试
生产优化(树摇)
- 代码运行性能
HMR模块热替换
devServer:{
port:3001,
compress:true,//自动更新
open:true,//自动打开浏览器
hot:true//HMR模块热替换
}
树摇去掉没用到的js代码
- tree-shaking webpack自帶
- es6 导出
- 必须是 production
可以:在package.json文件中添加了 "sideEffects": false 表示所有代码都没有副作用(所有代码都可以进行tree shaking)
问题:可能会把 css的@babel/polyfill (副作用)文件删除掉
解决:"sideEffects":["*.css"]
css去掉无用的代吗
- npm i -D purgecss-webpack-plugin
new PurgecssWebpackPlugin({
paths:glob.sync(pattern:`${PATH.src}/**/*`,options:{nodir:true})
})
代码分割
1、通过配置入口文件来进行分割包,这是最简单和最直接的方式,但是这种方式有一定缺点,可能造成重复引用的模块重复被打包;
2、使用splitChunksPlugin来进行公共模块的提取(被optimizition.splitchunk取代);
3、通过动态代码加载来分割代码,使用import()方法,调用import() 之处,被作为分离的模块起点,意思是被请求的模块和它引用的所有子模块,会分离到一个单独的 chunk 中;
entry多入口实现分割
entry: {
main: './src/js/index.js',
print: './src/js/print.js'
},
output: {
filename: "js/[name]-[contentHash].js",
path: path.resolve(__dirname, "dist")
}
optimization配置实现分割
- 过大的js文件拆分成多个,实现并行加载,提高加载速度(把工具库jq,lodash和业务逻辑拆分开)
可以将node_modules中代码单独打包一个chunk最终输出
chunks: 表示显示块的范围,有三个可选值:initial(初始块 同步)、async(按需加载块)、all(全部块),默认为all;
optimization: {
splitChunks: {
chunks: 'all'
}
},
externals忽略不打入的包
例如项目中使用从 CDN 引入 jQuery,而不是把它打包进来使用
externals: {
jquery: 'jQuery'
}
路径别名与导入后缀省略
// 解析模块的规则
resolve: {
// 配置解析模块路径别名:优点简写路径,缺点路径没有提示
alias: {
// 定义一个@变量,可在import引入时使用
'@': path.resolve(__dirname, '../src')
'$css': path.resolve(__dirname, '../src/css')
},
// 配置省略文件路径的后缀名称 import '@/index'
// 如果省略,建议文件名称不要重名了
extensions: ['.js', '.json', '.vue']
}
Source Maps调试配置
作为开发,代码调试当然少不了,那么问题来了,经过打包后的文件,你是不容易找到出错的地方的,Source Map就是用来解决这个问题的。
devtool: 'source-map' // 会生成对于调试的完整的.map文件,但同时也会减慢打包速度
三、代码
相关代码
webpack.config.js
// 采用commonjs
const path = require('path')
const htmlWebpackPlugin = require('html-webpack-plugin')
const miniCssExtractPlugin = require('mini-css-extract-plugin')
const optimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
module.exports = {
mode: 'production', //生产:development,开发:production
/*
1、entry:
1、单入口,字符串表示-----打包一个chunk,生成一个build
entry:'./src/index.js',
2、多入口,数组------打包成一个chunk,生成一个build
entry:['./src/index.js','./src/cc.js'],
3、对象的写法 -----有几个文件生成几个chunk,生成几个build
entry:{
one:'./src/one.js',
two:'./src/two.js'
}
*/
entry: './src/index.js',
output: {
filename: '[name].js',
// **对象多入口写法entry: filename:'[name].js',
path: path.resolve(__dirname, 'dist'),
},
// loader 处理非js资源 如html,css,img
module: {
rules: [
/*
一个用 loader:css-loader
一个以上 use:[]
从右往左
{
test: /\.css$/,
use: [
'style-loader',
'css-loader']
},
*/
{
test: /\.css$/,
use: [
miniCssExtractPlugin.loader,
'css-loader',
'postcss-loader'
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader'
]
},
{
test: /\.sass$/,
use: [
'style-loader',
'css-loader',
'sass-loader'
]
},
{
test: /\.(png|jpg|jpeg)$/,
loader: 'url-loader',
options: {
publicPath:'./images/',
outputPath: 'imgs/',
name:'[name][hasg].[ext]',//命名hash+名称
limit:1024*8,//限制8k一下转base64
}
},
{
test: /\.(html)$/,
loader: 'html-loader',
},
{
// exclude 排查资源
exclude:/\.(js|json|html|css|less|scss|png|jpg)$/,
loader:'file-loader',
options:{
outputPath: 'font/', //大包位置
publicPath:'./font', //寻找位置
name:'[name][hasg].[ext]',//命名hash+名称
}
},
// eslint 只检查js
{
test: /\.js$/,
//只检查自己写的代码,不检查第三方的
exclude:/node_modules/,
loader:'eslint-loader',
options:{
fix:true,//自动修复
}
},
]
},
// plugin插件,执行范围更广的任务 打包到开发压缩
plugins: [
// 打包压缩html(打包多个就实例化new多个)
new htmlWebpackPlugin({
template: './src/index.html', //打包的文件
filename: 'demo.html', //打包后的名称
chunks: [], //指定使用的js文件
minify: { //压缩
// 移除空格
collapseWhitespace: true,
// 移除注释
removeComments: true,
}
}),
new miniCssExtractPlugin({ filename: 'demo.css' }),
new optimizeCssAssetsWebpackPlugin()
],
// 在webpack5 需要加上这个选项才会自动刷新页面
target:"web",
devServer:{
port:3001,
compress:true,//自动更新
open:true,//自动打开浏览器
hot:true//HMR模块热替换
}
}
postcss.config.js
module.exports={
plugins:[
require('postcss-preset-env')()
]
}
相关依赖文件
package.json
{
"name": "webpack5s",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev":"webpack serve --mode development --port 3000",
"build":"webpack --mode production"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"css-loader": "^6.2.0",
"eslint": "^7.31.0",
"eslint-config-airbnb-base": "^14.2.1",
"eslint-loader": "^4.0.2",
"eslint-plugin-import": "^2.23.4",
"file-loader": "^6.2.0",
"html-loader": "^2.1.2",
"html-webpack-plugin": "^5.3.2",
"less": "^4.1.1",
"less-loader": "^10.0.1",
"mini-css-extract-plugin": "^2.1.0",
"optimize-css-assets-webpack-plugin": "^6.0.1",
"post-loader": "^2.0.0",
"postcss-loader": "^6.1.1",
"postcss-preset-env": "^6.7.0",
"style-loader": "^3.2.1",
"url-loader": "^4.1.1",
"webpack": "^5.46.0",
"webpack-cli": "^4.7.2",
"webpack-dev-server": "^3.11.2"
},
"browserslist": [
">0.2%",
"last 2 version",
"not dead"
],
"eslintConfig": {
"extends": "airbnb-base"
}
}