源碼地址: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"
}
}