1.webpack是什么?
- 最開始是一個基于node的js打包工具丑搔。
- 簡單來說,webpack是一個(bundle)模塊打包工具,處理模塊之間的依賴同時生成對應(yīng)模塊的靜態(tài)資源提揍。
2.為什么使用webpack啤月?
- 項目中html文件可能會引用十幾個js文件,而且得按照一定的順序劳跃,js文件之間存在依賴關(guān)系谎仲,同時對于es6+等語法,less刨仑,sass等css預(yù)處理都不能很好的解決郑诺。此時需要借助一些工具處理這些問題
打包例子如下:
// 初始化
npm init
// 安裝webpack,webpack-cil的作用是可以在命令行內(nèi)使用webpack指令或者npx webpack指令
npm install webpack webpack-cli --save-dev
- 文件目錄如下:
- 在index.html引入打包后的main.js文件
<script src="../dist/main.js"></script> <!--這是打包之后默認(rèn)生成的main.js文件-->
- header.js導(dǎo)出一個頭部模塊
function header() {
const el = document.createElement('div')
el.innerText = '這是導(dǎo)航模塊'
document.body.appendChild(el)
}
module.exports = header
- content.js導(dǎo)出內(nèi)容模塊
function content() {
const el = document.createElement('div')
el.innerText = '這是內(nèi)容模塊'
document.body.appendChild(el)
}
module.exports = content
- 將這兩個模塊引入index.js
var Header = require('./header.js');
var Content = require('./content.js');
new Header()
new Content()
- 執(zhí)行打包命令
// npx 是node 5.2版本以上自帶的 并且查看局部版本號的
npx webpack src/index.js
// 直接使用webpack 需要全局安裝webpack,且可以修改打包輸出文件名
webpack src/index.js --output dist/bundle.js
-
頁面效果如下
上述操作就相當(dāng)于我們把header.js模塊和content.js模塊合并到了index.js模塊杉武,對這合并后的index.js模塊打包成main.js辙诞,然后供index.html引用,這就是webpack的打包原理轻抱。
webpack打包原理
它會把項目當(dāng)成一個整體飞涂,通過一個入口文件(index.js),從這個文件開始找到項目中所有依賴祈搜,然后在內(nèi)部創(chuàng)建一個依賴圖(dependencygraph)较店,用于映射到項目需要的每個模塊,最后打包成一個或多個瀏覽器可識別的js文件容燕。
現(xiàn)在已經(jīng)成功的使用webpack進行打包啦梁呈,但是在終端輸入命令,感覺好煩哦蘸秘,還好山人自有妙計官卡,且往下看看。
3.通過配置文件使用webpack
- 在當(dāng)前根目錄下新建配置文件webpack.config.js,從入口和出口配置
// 輸出路徑秘血,必須是絕對路徑味抖,這里需要使用node的核心模塊path,調(diào)用模塊的resolve方法
const path = require('path')
module.exports = {
entry:'./src/index.js', // 入口文件
// 輸出文件
output:{
publicPath:'/', // 打包輸出根路徑
// publicPath:'gttp://cdn.com.cn',
filename:'[name].bundle.js',
path:path.resolve(__dirname,'dist')
},
}
- publicPath:項目發(fā)布時灰粮,配置對應(yīng)的域名參數(shù)
- filename:打包輸出文件名,可以是bundle.js默認(rèn)生成的main.js,如果打包多個文件忍坷,可以改成占位符粘舟,這樣打包出來的名字就是entry配置的
- path:打包出的文件放在哪個文件下面
- __dirname:node的變量熔脂,即webpck.config.js當(dāng)前所在文件夾的目錄,這個就是生成的bundle文件夾的路徑,一般都是叫dist,生成的文件名字叫做bundle.js
-
配置完成柑肴,運行命令時會自動引用webpack.config.js文件中的配置選項霞揉,運行如下:
注意:如果配置的默認(rèn)webpack.config.js名字修改為webpackconfig.js,此時運行命令
// 告訴webpack 按照webpackconfig.js里面的規(guī)則打包
npx webpack --config webpackconfig.js
- 通過pack.json 文件配置對應(yīng)的命令
注意:package.json中的script會按你設(shè)置的命令名稱來執(zhí)行對應(yīng)的命令
// 執(zhí)行npm run build晰骑,會自動執(zhí)行webpackj進行打包
"scripts": {
"build":"webpack"
},
總結(jié)(webpack三種使用方法)
- 全局安裝執(zhí)行命令
// index.js 是入口文件
webpack index.js
- 本地安裝執(zhí)行命令
npx webpack index.js
- 在pack.json 中配置命令并且借助配置文件間接執(zhí)行webpack指令
// 在script 目錄下使用webpack,會在當(dāng)前的工程目錄下找webpack,這就是為什么不用加npx
npm run build => webpack
4.Loaders介紹
1适秩,開始之前,先看看下面的圖片是否能正常顯示硕舆?
- 在當(dāng)前項目下新建img.js秽荞,引入一張下載并放到對應(yīng)的目錄文件下
import avater from '../assets/images/avater.jpg'
function Img(){
var img = new Image();
img.src=avater;
img.classList.add('avater');
document.body.append(img)
}
export default Img;
- 在index.js中引入img模塊,并創(chuàng)建一個該構(gòu)造函數(shù)的實例
import Img from './img'
new Img();
- 運行打包命令npm run build
- 結(jié)果報錯:
出錯原因是webpack只知道怎么打包js文件抚官,不知道怎么打包其他類似jpg文件扬跋。那么webpack打包除了js以外的文件都需要借助loaders。
loaders是webpack最強大的功能之一凌节,通過不同的loader钦听,webpack有能力調(diào)用外部的腳本或工具,實現(xiàn)對不同格式的文件(如圖片)倍奢,字體圖標(biāo)的處理朴上,例如把less轉(zhuǎn)為css,將ES66卒煞、ES7等語法轉(zhuǎn)化為當(dāng)前瀏覽器能識別的語法等功能
Loaders需要單獨安裝并且需要在webpack.config.js中的modules配置項下進行配置余指,Loaders的配置包括以下幾方面:
- test:一個用以匹配loaders所處理文件的拓展名的正則表達式(必須)
- loader:loader的名稱(必須)
- include/exclude:手動添加必須處理的文件(文件夾)或屏蔽不需要處理的文件(文件夾)(可選);
- options:為loaders提供額外的設(shè)置選項(可選)
圖片打包loader
- file-loader
- 安裝對應(yīng)的loader
npm install --save-dev file-loader
- 配置參數(shù)
module:{
rules:[{
test: /\.(png|jpe?g|gif)$/i, // 匹配圖片格式名
use:[
{
loader:'file-loader', // 使用file-loader
options: {
name:'[name].[hash].[ext]' //配置打包名字和格式
}
}
]
}]
},
-
運行npm run build 后顯示如下:
- url-loader
- 安裝
npm install --save-dev url-loader
- 將上面的file-loader 替換成url-loader
- 運行 npm run build
- 結(jié)果如下:
url-loader
它會將圖片轉(zhuǎn)化成base64 圖片跷坝,將圖片打包到j(luò)s文件中
雖然不用httpq請求圖片酵镜,但是假如生成的js文件過大的情況下,會影響頁面加載性能
url-loader的最佳使用方法:
給圖片一個限制limit屬性柴钻,當(dāng)圖片小于某一個值的時候淮韭,打包到j(luò)s文件中,否則超出這個限制贴届,則和file-loader一樣靠粪,將圖片打包成對應(yīng)的圖片,放到dist目錄下
rules:[{
test: /\.(png|jpe?g|gif)$/i,
use:[
{
loader:'url-loader',
options: {
/**placeholder 占位符,
* [name] 名字是原來圖片對應(yīng)的名字
* [ext] 圖片對應(yīng)的格式
* outputPath:表示打包到dist對應(yīng)的images下
* limit:2048 指的是2kb 204800 即200kb毫蚓,如果小于200kb則會使用url-loader 打包成base64
* 到j(luò)s文件中占键,大于200kb的則不會打包到j(luò)s文件中,而是按照設(shè)置的目錄打包成圖片
* */
name:'[name]_[hash].[ext]',
outputPath:'images/',
limit:10240
}
}
]
運行打包結(jié)果元潘,生成單獨的圖片
注意:雖然我們只需使用url-loader畔乙,但url-loader是依賴于file-loader的,需要兩個一起安裝
樣式打包loader
如果我們要加載一個css文件翩概,需要安裝配置style-loader和css-loader:
npm install style-loader css-loader --save-dev
- 在項目里面配置一下規(guī)則
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
}
- 我們在src文件夾下新建css文件夾牲距,該文件夾內(nèi)新建index.css文件:
// index.css
body{
background: red;
}
- 此時運行結(jié)果會發(fā)現(xiàn)背景變成了紅色
配置項目中常用的less
// 安裝less 及對應(yīng)的less-loader
npm install less less-loader --save-dev
- 增加less的rules
{
test: /\.less$/,
use: [
'style-loader',
{
loader:'css-loader',
options:{
importLoaders:2// 作用于 @import的資源之前」有多少個 loader 0 => 無 loader(默認(rèn)); 1 => postcss-loader; 2 => postcss-loader, less-loader
// modules:true
}
},
'less-loader',
'postcss-loader'
], // compiles Less to CSS
},
- style-loader :會將對應(yīng)的樣式掛在在dom節(jié)點上
- less-loader:解析less語法
- postcss-loader:自動生成css3的瀏覽器前綴
loader 執(zhí)行順序:從下到上返咱,從右到左,先執(zhí)行l(wèi)ess-loader文件進行翻譯牍鞠,然后給到css-loader咖摹,最后掛載到style-loader掛載到dom上
- importLoaders:保證不管在js文件中importless文件還是less文件中導(dǎo)入子less文件,都能執(zhí)行下面的是在一個less文件導(dǎo)入另外的less文件难述,postcss-loader萤晴,less-loader在進行,然后給到css-loader
- modules:開啟css模塊化打包胁后,類似與全局或者局部
- postcss-loader與autoprefixer配合自動補全css前綴
npm install autoprefixer postcss-loader --save-dev
- 具體查看postcss-loader
https://www.webpackjs.com/loaders/postcss-loader/
// postcss.config.js
module.exports = {
plugins: [
require('autoprefixer')]
}
結(jié)果會在加上前綴
- 在css文件夾下創(chuàng)建avater.less
// avater.less
body{
.avater{
width: 200px;
height: auto;
border: 1px solid #ccc;
transform: translate(100px,100px);
}
}
- 在img.js中引入avater.less:
import './css/avater.less'
- 結(jié)果入下圖
還有諸如字體圖標(biāo)loader店读、字體loader等就不一一列出來了,感興趣的可前往webpack官網(wǎng)查看择同,都是一樣的套路两入。
5.插件(Plugins)
插件(Plugins)是用來拓展Webpack功能的,它們會在整個構(gòu)建過程中生效敲才,執(zhí)行相關(guān)的任務(wù)裹纳,類似與vue,react的生命周期概念,在構(gòu)建過程中某些時刻紧武,做些事情剃氧,讓打包更加便捷。
插件的使用流程:
- 安裝
- 引入
- 在plugins里面實列化
1.HtmlWebpackPlugin
功能:在打包結(jié)束后阻星,會自動生成一個html文件,并把打包生成的js自動引入到這個html文件中
還可以通過配置模板屬性朋鞍,打包生成的html文件與src目錄下的index.html文件一致
// 安裝
npm install --save-dev html-webpack-plugin
- webpack.config.js中我們引入了HtmlWebpackPlugin插件,并配置了引用了我們設(shè)置的模板妥箕,如下:
const path = require('path')
// 引入插件
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode:'development',
entry:'./src/index.js',
module:{
rules:[{
test: /\.(png|jpe?g|gif)$/i,
use:[
{
loader:'url-loader',
options: {
name:'[name].[hash].[ext]',
limit:2048 /*:2048 指的是2kb 204800 即200kb滥酥,如果小于200kb則會使用url-loader 打包成base64 到j(luò)s文件中,
大于200kb的則不會打包到j(luò)s文件中畦幢,而是按照設(shè)置的目錄打包成圖片*/
}
}
]
}, {
test: /\.css$/,
use: [ 'style-loader', 'css-loader']
},
{
test: /\.less$/,
use: [ 'style-loader', 'css-loader','less-loader','postcss-loader']
}
]
},
plugins:[
new HtmlWebpackPlugin({
template:'src/index.html' //根據(jù)自己的指定的模板文件來生成特定的 html 文件坎吻。這里的模板類型可以是任意你喜歡的模板,可以是 html, jade, ejs, hbs, 等等宇葱,但是要注意的是瘦真,使用自定義的模板文件時,需要提前安裝對應(yīng)的 loader黍瞧, 否則webpack不能正確解析
})
],
output:{
filename:'[name].js',
path:path.resolve(__dirname,'dist')
}
}
- 先將將目錄下dist文件刪除然后我們使用npm run build進行打包诸尽,你會發(fā)現(xiàn),dist文件夾和html文件都會自動生成印颤,如下:
CleanWebpackPlugin
在打包的之前您机,清除上一次的dist文件夾下的打包生成的js為文件,也可以配置清理/dist文件夾
// 安裝
npm install clean-webpack-plugin --save-dev
//引入清除文件插件
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
//實例化,參數(shù)為目錄,這里面無參數(shù)往产,默認(rèn)刪除的是output.path被碗!
我的output配置項里的路徑就是dist目錄某宪。
ew CleanWebpackPlugin()
output:{
filename:'[name].js',
path:path.resolve(__dirname,'dist')
}
注意:webpack 4.32.2 以上的寫法仿村,
新版本的插件需要進行解構(gòu),默認(rèn)刪除的是index.html里面未引用的文件
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
6.構(gòu)建本地服務(wù)器
使用 webpack-dev-server能夠根據(jù)代碼的變化兴喂,自動打包并刷新頁面效果蔼囊,提升開發(fā)效率
webpack-dev-server配置本地服務(wù)器
npm i webpack-dev-server --save-dev
devServer作為webpack配置選項中的一項,以下是它的一些配置選項:
- contentBase :設(shè)置服務(wù)器所讀取文件的目錄衣迷,當(dāng)前我們設(shè)置為"./dist"
- open:true 是否自動打開新的瀏覽器窗口
- port :設(shè)置端口號畏鼓,如果省略,默認(rèn)為8080
- inline :設(shè)置為true壶谒,當(dāng)源文件改變時會自動刷新頁面
- historyApiFallback:設(shè)置為true云矫,所有的跳轉(zhuǎn)將指向index.html
- 將這些配置加到webpack.config.js文件上,如下:
devServer:{ //
contentBase: path.join(__dirname, 'dist'),
open: true, // 自動打開瀏覽器
port: "8088", // 設(shè)置端口號為8088,默認(rèn)是8080
hot:true, // 熱模塊
hotOnly:true, //html 熱加載,html 失效的時候,如果是false ,會重新幫你刷新頁面瓶蝴,一般也買你失效绰姻,不需要去刷新,所以這里值為true
proxy:{} // 設(shè)置代理
},
- 在package.json文件中scripts添加啟動命令:
"dev":"webpack-dev-server"
- 啟動項目
npm run dev
其他webpack配置本地服務(wù)器方法
- 在package.json 中 配置--watch 啟動之后源内,監(jiān)聽打包的js文件發(fā)生變化就會自動執(zhí)行打包,這個無法axios請求以及自動更新
"build": "webpack --watch",
- 利用node自己搭建一個服務(wù)器
- 安裝webpack 的中間件 middleware
npm install express webpack-dev-middleware -D
- 在package.json 配置命令
"scripts": {
"build": "webpack",
"server": "node server.js"
}
- 在項目根目錄下新建一個server.js文件
// server.js
const express = require('express');
const webpack= require('webpack');
const webpackDevMiddlewear = require('webpack-dev-middleware');
const config = require('./webpack.config.js');
const complier = webpack(config); // 進行編譯
// 在node中使用webpack
const app = express();
app.use(webpackDevMiddlewear(complier,{
publicPath:config.output.publicPath // 打包輸出的路徑就是config
}))
app.listen(3000,(()=> {
console.log('server is runnin')
}))
-
啟動結(jié)果
總結(jié):會發(fā)現(xiàn)這個沒有webpack-dev-server智能,每次修改頁面需要手動刷新頁面才能更新,所以項目中一般用webpack-dev-server
Source Maps調(diào)試配置
作為開發(fā)腮敌,代碼調(diào)試當(dāng)然少不了,那么問題來了俏扩,經(jīng)過打包后的文件糜工,你是不容易找到出錯的地方的,Source Map就是用來解決這個問題的录淡。
通過如下配置捌木,我們會在打包時生成對應(yīng)于打包文件的.map文件,使得編譯后的代碼可讀性更高赁咙,更易于調(diào)試
module.exports = {
mode:'production', // 改成production 則是生產(chǎn)環(huán)境钮莲,會打包壓縮
devtool:'source-map', // development 環(huán)境下,會默認(rèn)是關(guān)閉de,線上cheap-module-source-map
entry: {main:'./src/index.js'}
// 熱加載配置
devServer:{
contentBase:'./dist',
open:false,
port: "8088", // 設(shè)置端口號為8088
hot:true, // 熱模塊
hotOnly:true //html 熱加載彼水,html 失效的時候崔拥,如果是false ,會重新幫你刷新頁面,一般也買你失效凤覆,不需要去刷新链瓦,所以這里值為true
},
}
- 比如在index.js中寫入錯誤代碼
consoel.log('1111')
- 配置好后,我們再次運行npm run build進行打包,這時我們會發(fā)現(xiàn)在dist文件夾中多出了一個main.js.map文件如下:
- 并且執(zhí)行npm run dev,SourceMap 會建立打包代碼與源代碼的一個映射概關(guān)系慈俯,它是一個映射 關(guān)系渤刃,他知道dist目錄下main.js文件報錯代碼對映的實際上對于的是src目錄下SourceMap/index.js文件中的第8行代碼報錯,結(jié)果如下
如果我們的代碼有bug,在瀏覽器的調(diào)試工具中會提示錯誤出現(xiàn)的位置贴膘,這就是devtool: 'source-map'配置項的作用卖子。
source-map配置優(yōu)化
- source-map 參數(shù),具體看官網(wǎng)刑峡,關(guān)閉用none
- source-map:會降低打包速度洋闽,打包過程中會生成main.bundle.js.map映射文件,降低打包速度
- inline-source-map:區(qū)別 對應(yīng)的main.bundle.js.map文件會以base64格式打包到對應(yīng)的js文件中,也就是不會單獨生成.map文件
- inline-cheap-source-map:只要告訴第幾行出錯了突梦,打包速度會提高诫舅,只管業(yè)務(wù)代碼,他的打包速度比較快
- cheap-module-source-map:不止管業(yè)務(wù)代碼里面的錯誤宫患,還會管css刊懈,loader里面的錯誤,但是速度比較慢
- eval-source-map:這個速度比較快娃闲,eval并且會將代碼的映射概關(guān)系打包到main.js中虚汛,但是在大型中可能不全。且不會生產(chǎn)map.js文件
- ==cheap-module-eval-source-map== 建議在開發(fā)環(huán)境下畜吊,打包速度比較快且比較全
- production 線上代碼:==cheap-module-source-map== 提示更全面點
具體參數(shù)配置在官網(wǎng):
打開官網(wǎng)
瀏覽器調(diào)式插件:source-map
熱更新(HotModuleReplacementPlugin)
可以在我們修改代碼后自動刷新預(yù)覽效果
- 使用方法:
- devServer配置項中添加hot: true參數(shù)泽疆。
- 因為HotModuleReplacementPlugin是webpack模塊自帶的,所以引入webpack后玲献,在plugins配置項中直接使用即可殉疼。
7.babel
Babel其實是一個編譯JavaScript的平臺,它可以編譯代碼幫你達到以下目的:
讓你能使用最新的JavaScript代碼(ES6捌年,ES7...)瓢娜,而不用管新標(biāo)準(zhǔn)是否被當(dāng)前使用的瀏覽器完全支持;
讓你能使用基于JavaScript進行了拓展的語言礼预,比如React的JSX眠砾;
// 安裝es6語法
npm install --save-dev babel-loader @babel/core
npm install @babel/preset-env --save-dev
{ test: /\.js$/,
exclude: /node_modules/, // 檢測到j(luò)s文件,排除node_modules下js文件,進行語法轉(zhuǎn)化
loader: "babel-loader" ,
options:{
"presets": ["@babel/preset-env"]
}
}
現(xiàn)在我們已經(jīng)可以支持ES6語法轉(zhuǎn)換ES5啦
// index.ja寫下面代碼
const str = '1223333'
let nameArr = ['violet','122','3333']
nameArr.map((el,idx) => {
console.log(el,idx)
})
// 執(zhí)行打包命令 在目錄中生成dist
webpack
(因為執(zhí)行npm run dev 會將打包的dist目錄放放置在內(nèi)存中托酸,隱藏起來褒颈,此時我們打開打包后的main.js,最末尾會看到語法轉(zhuǎn)化)励堡,如下:
但是map函數(shù)或者對象在低版本瀏覽器里面是沒有的谷丸,還需要將這些低版本瀏覽器里沒有的函數(shù)或者對象補充進去,這就需要使用@babel/polyfill
npm install --save @babel/polyfill
- 在index.js中導(dǎo)入
import "@babel/polyfill";
執(zhí)行webpack
你會發(fā)現(xiàn)這中全局注入的方式应结,會增大main.js的體積
注入@babel/polyfill前刨疼。main.js只有20.2kib
注入后泉唁。main.js 419kib
體積變大的原因是這種全局注入方式,會將所有的將函數(shù)或者變量在低版本的補充揩慕,包括promise亭畜, symbol 這類新增的內(nèi)建對象的注入,因此如果能實現(xiàn)按需注入迎卤,就可以減小體積啦
@babel/polyfill優(yōu)化方法:
可以設(shè)置useBuiltIns:'usage' 類似與根據(jù)你的業(yè)務(wù)代碼進行替換拴鸵,業(yè)務(wù)代碼里面用到什么就注入@babel/polyfill,按需引入,優(yōu)化打包體積
同時止吐,使用這個屬性webpack會在打包時自動引入@babel/polyfill宝踪,不需要手動引入
{ test: /\.js$/,
exclude: /node_modules/, // 檢測到j(luò)s文件侨糟,排除node_modules下js文件,進行語法轉(zhuǎn)化
loader: "babel-loader" ,
options:{
"presets": [["@babel/preset-env",{
targets: {
edge: "17",
firefox: "60",
chrome: "67", // 項目會打包運行在chorme 版本67 的上面碍扔,在這個版本號或者以上版本是否支持es6,如果支持秕重,則是不必要進行注入轉(zhuǎn)換
safari: "11.1",
},
useBuiltIns:'usage' // 根據(jù)業(yè)務(wù)代碼用到的函數(shù)不同,按需替換
}]]
}
}
打包后體積變成 main.js 45.9kib
此外還可以在設(shè)置targets的瀏覽器版本號,決定是否要轉(zhuǎn)換代碼溶耘,比如chorme 67 以上對Es6語法都支持所以打包后的體積二拐,又精簡成 20.3kib啦,如下圖
@babel/runtime 配置
雖然polyfill是按需引入的凳兵,但是會污染全局命名空間百新,當(dāng)你寫的是公共庫時,可能會與使用者本地的方法產(chǎn)生沖突庐扫。
當(dāng)我們開發(fā)UI組件庫或者在開發(fā)第三方庫時就要需要使用@babel/runtime和@babel/plugin-transform-runtime饭望,能夠有效的避免,會以閉包的形式注入形庭,不會污染全局的環(huán)境
npm install --save @babel/runtime-corejs2
npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime
配置代碼
options:{
"plugins": [[
"@babel/plugin-transform-runtime",
{
"absoluteRuntime": false,
"corejs": 2, // 一般寫2
"helpers": true,
"regenerator": true,
"useESModules": false
}
]]
// "presets": [["@babel/preset-env",{
// targets: {
// edge: "17",
// firefox: "60",
// chrome: "67", // 項目會打包運行在chorme 版本67 的上面铅辞,在這個版本號或者以上版本是否支持es6,如果支持萨醒,則是不必要進行注入轉(zhuǎn)換
// safari: "11.1",
// },
// useBuiltIns:'usage' // 根據(jù)業(yè)務(wù)代碼用到的函數(shù)斟珊,按需替換
// }]]
}
}
babele配置提取
- 在項目根目錄下新建一個.babelrc文件
這里面的內(nèi)容對應(yīng)的就是babel-loader options里面的內(nèi)容
// .babelrc 使用時把注釋刪掉,該文件不能添加注釋
{
"plugins": [[
"@babel/plugin-transform-runtime",
{
"absoluteRuntime": false,
"corejs": 2, // 一般寫2
"helpers": true,
"regenerator": true,
"useESModules": false
}
]]
}
更詳細babel7的可以看這篇文章的介紹
link
8.webpack項目優(yōu)化及拓展
1.環(huán)境模式區(qū)分打包配置
- 修改目錄結(jié)構(gòu)如下:
- 使用webapxk-merge將公共配置提取出來
npm install webpack-merge --save-dev
// webpack.prod.js
// 引入合并插件
const merge = require('webpack-merge');
const baseConfig = require('./webpack.base');
const prodConfig = {
mode: 'production', // 在development開發(fā)環(huán)境中富纸,默認(rèn)sourceMaps devtool默認(rèn)是false的
devtool: 'cheap-module-source-map',
}
module.exports = merge(baseConfig, prodConfig)
// webpack.dev.js
// 引入合并插件
const merge = require('webpack-merge');
const baseConfig = require('./webpack.base');
const path = require('path')
const devConfig = {
mode: 'development', // 在development開發(fā)環(huán)境中囤踩,默認(rèn)sourceMaps devtool默認(rèn)是false的
devtool: 'cheap-module-eval-source-map',
devServer: { //
contentBase: path.join(__dirname, 'dist'),
open: false, // 自動打開瀏覽器
port: "8088", // 設(shè)置端口號為8088
hot: true, // 熱模塊
hotOnly: true, //html 熱加載,html 失效的時候晓褪,如果是false ,會重新幫你刷新頁面堵漱,一般也買你失效,不需要去刷新辞州,所以這里值為true
proxy: {} // 設(shè)置代理
},
optimization: {
usedExports: true // tree shaking
}
}
module.exports = merge(baseConfig, devConfig);
// webapck.base.js
const path = require('path')
// 引入插件
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 引入CleanWebpackPlugin插件
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry:'./src/index.js',
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html' //根據(jù)自己的指定的模板文件來生成特定的 html 文件怔锌。這里的模板類型可以是任意你喜歡的模板,可以是 html, jade, ejs, hbs, 等等,但是要注意的是埃元,使用自定義的模板文件時涝涤,需要提前安裝對應(yīng)的 loader, 否則webpack不能正確解析
}),
new CleanWebpackPlugin(), //實例化岛杀,參數(shù)為目錄
],
module: {
rules: [{
test: /\.(png|jpe?g|gif)$/i,
use: [
{
loader: 'url-loader',
options: {
name: '[name].[hash].[ext]',
limit: 2048 /*:2048 指的是2kb 204800 即200kb阔拳,如果小于200kb則會使用url-loader 打包成base64 到j(luò)s文件中,
大于200kb的則不會打包到j(luò)s文件中类嗤,而是按照設(shè)置的目錄打包成圖片*/
}
}
]
}, {
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader', 'postcss-loader']
},
{
test: /\.js$/,
exclude: /node_modules/, // 檢測到j(luò)s文件糊肠,排除node_modules下js文件,進行語法轉(zhuǎn)化
loader: "babel-loader"
}
]
},
output:{
publicPath:'/',
filename:'[name].js',
path: path.resolve(process.cwd(), 'dist') // __dirname: 當(dāng)前模塊的目錄名,而 process.cwd() 當(dāng)前Node.js進程執(zhí)行時的工作目錄,就是根目錄遗锣,放到上一層dist目錄下
}
}
wepack.base.js中放置共有的配置货裹,這里注意因為配置文件放在build里面,路徑發(fā)生了變化精偿,所以需要將輸出地址修改弧圆,表示打包的dist文件放到項目根目錄
path.resolve(process.cwd(), 'dist')
- 修改package.json,配置打包環(huán)境
"scripts": {
"dev": "webpack-dev-server --config ./build/webpack.dev.js",
"build": "webpack --config ./build/webpack.prod.js"
},
- 執(zhí)行命令,你會發(fā)現(xiàn)項目打包和啟動都o(jì)k
2.tree shaking(搖樹優(yōu)化笔咽,剔除無用的代碼)
- 在根目錄下新建一個untils.js
// untils.js
const a = () =>{
console.log('a')
}
const b = () =>{
console.log('b')
}
const c = () =>{
console.log('c')
}
export {
a,
b,
c
}
- 在index.js中引入a
import {a} from './untils.js'
a();
- 我們可以看到 在 utils.js中我們定義了三個方法搔预, 在index.js中引入了方法a,也就是說方法b和方法c都是沒有使用的叶组。 我們看一下打包之后的結(jié)果
tree shaking 是一個術(shù)語拯田,通常用于描述移除 JavaScript 上下文中的未引用代碼(dead-code)。其實就是說把我們項目中沒有使用到的代碼在打包的時候丟棄掉甩十,只保留我們引入了的JS代碼和css代碼
==注意:它依賴于 ES2015 模塊系統(tǒng)中的靜態(tài)結(jié)構(gòu)特性船庇,例如 import 和 export。不支持CommonJs的引入枣氧,因為這個是CommonJs動態(tài)加載模塊==
開發(fā)環(huán)境配置tree shaking
- 在webpack.config.js中增加一個optimization 屬性 設(shè)置usedExports:true
mode:'development',
optimization:{
usedExports:true
},
- 在在package.json 設(shè)置 "sideEffects"溢十,一般設(shè)置為false
{
"name": "review",
"version": "1.0.0",
"description": "",
"main": "index.js",
"sideEffects":["@babel/pollyfill"],
"scripts": {
"build": "webpack",
"dev": "webpack-dev-server",
"server": "node server.js"
},
注意:如果項目中有使用babel/pollyfill 文件,這個文件沒有導(dǎo)出任何的內(nèi)容达吞,three shaking
- 打包的時候會忽略點,需要做特殊的設(shè)置sideEffects":["@babel/pollyfill","*.css"]這類文件都沒有導(dǎo)出文件张弛,這樣設(shè)置就會忽略這個,一般設(shè)置為false
- 打包結(jié)果如下,在開發(fā)環(huán)境下酪劫,方便代碼調(diào)試吞鸭,不會刪除代碼,但是會多出一個used屬性
在production環(huán)境下
mode:'production',
- 在在package.json 設(shè)置 "sideEffects"覆糟,一般設(shè)置為false
{
"name": "review",
"version": "1.0.0",
"description": "",
"main": "index.js",
"sideEffects":["@babel/pollyfill"],
"scripts": {
"build": "webpack",
"dev": "webpack-dev-server",
"server": "node server.js"
},
- 去掉optimization
- 打包會發(fā)現(xiàn)只有a被調(diào)用刻剥,其他的代碼都被刪除
3.codesplit(代碼分割)
Webpack 文件分離包括兩個部分,一個是 Bundle 的分離滩字,一個是 Code 代碼的分離:
Bundle splitting: 實際上就是創(chuàng)建多個更小的文件造虏,并行加載御吞,以獲得更好的緩存效果;主要的作用就是使瀏覽器并行下載漓藕,提高下載速度陶珠。并且運用瀏覽器緩存,只有代碼被修改享钞,文件名中的哈希值改變了才會去再次加載揍诽。
Code splitting: 只加載用戶最需要的部分,其余的代碼都遵從懶加載的策略栗竖;主要的作用就是加快頁面加載速度暑脆,不加載不必要加載的東西。
比如我們在項目中使用lodash這個庫狐肢,未進行代碼分割配置
打包結(jié)果:
每次打包全部依賴都被打包到 main.xxx.js 中去添吗,大小是 1.36mib,在項目中業(yè)務(wù)代碼可能會很大处坪,每次打包生成的文件根资,影響頁面打開速度
Bundle Split 的主要任務(wù)是將多個引用的包和模塊進行分離,避免全部依賴打包到一個文件下
- Webpack 4 中需要使用到 optimization.splitChunks 進行同步代碼分割的配置:
// webpack.base.js
splitChunks:{ // 分片打包 同窘,其默認(rèn)值是對異步代碼打包
chunks:'all',
minSize: 30000, // 30kb
maxSize: 0, // 50kb
minChunks: 1, // chunks 在項目中引用的次數(shù),滿足條件則會進行分割部脚,此時為2 想邦,但是只引用了一次,則不會進行分割
maxAsyncRequests: 5, // 同時加載的模塊數(shù)最多是5個
maxInitialRequests: 3, // 整個網(wǎng)頁首頁或者入口文件引入的庫做代碼分割委刘,超過3個也就不會進行分割
automaticNameDelimiter: '~', // 打包生成的js 文件~鏈接符 vendors-lodash.js
automaticNameMaxLength: 30,
name: true, // 生成的下面對應(yīng)的fliename
cacheGroups: { // 緩存組:對應(yīng)的分割生成所在組內(nèi),緩存組丧没,將符合要求的文件打包生成一個文件
vendors: {
test: /[\\/]node_modules[\\/]/, // 匹配node_modules 里面的js,將其打包分割
priority: -10,
filename:'vendors.js'
},
default: { // 所有的文件都符合defaul 至于放到vendors 這個組內(nèi)還是default 組內(nèi),取決于priority锡移,-10 高于-20,則會打包到vebdors
priority: -20, // 引入兩個文件呕童,但是a 文件使用過b ,在打包a的時候,a用到的b ,會使用之前打包的b
reuseExistingChunk: true, 如果一個模塊已經(jīng)被打包過淆珊,那么再使用這個模塊則會被忽略夺饲,直接復(fù)用之前的代碼
filename:'common.js' // 當(dāng)引用的文件不在node_modules 中,會打包到defalt 中設(shè)置的common.js 中
}
}
}
optimization.splitChunks 的意思是將所有來自 node_modules 中的依賴全部打包分離出來施符,這個時候我們再看打包的文件是個什么樣子:
增加了 splitChunks 的配置往声,我們第三方模塊都被打包到了 vendors~main.xxx.js 中去了,這個文件大小有 1.36mib戳吝,而入口文件 main.xxx.js 則只有9.24kib
- splitChunks的主要有以下配置
webpack 會對異步代碼自動分割
異步代碼import語法浩销,類似路由的按需加載,無需做任何配置听哭,會自動代碼分割
- 安裝@babel/plugin-syntax-dynamic-import
// 用以解析識別import()動態(tài)導(dǎo)入語法
npm install @babel/plugin-syntax-dynamic-import --save-dev
- 在index.js中寫入以下代碼:
function getComponent () {
// 里面的注釋打包生成的name,生成對應(yīng)得 vendors~lodash.js 慢洋,默認(rèn)生成vendors~main.js
return import(/* webpackChunkName: 'loadsh' */'lodash').then(({default:_}) =>{
var element = document.createElement('div');
element.innerHTML = _.join(['vlolet','lee2'],'-');
return element
})
}
getComponent().then((res) => {
document.body.appendChild(res)
})
- 在bablerc中配置
{
presets: [
["@babel/preset-env", {
targets: {
chrome:"67"
},
useBuiltIns: "usage"
}],
"@babel/preset-react"
],
plugins: ["@babel/plugin-syntax-dynamic-import"]
}
- 打包結(jié)果:
css代碼分割
- 在當(dāng)前目錄下塘雳。有一個css文件夾,引用了css,但是打包并沒有生成獨立的css文件普筹,而是打包到j(luò)s文件中啦
// index.css
body{
background: plum;
}
div{
font-size: 20px;
color: blueviolet;
margin: 0 aut0;
}
.avater{
width: 100px;
}
- css代碼分割需要借助插件MiniCssExtractPlugin
- 因為只能在生成環(huán)境使用粉捻,所以需要將css相關(guān)打包規(guī)則移到各自對應(yīng)的環(huán)境中,并進行壓縮
// 分割代碼并壓縮css代碼
npm install mini-css-extract-plugin optimize-css-assets-webpack-plugin --save-dev
// 安裝js壓縮代碼
npm install terser-webpack-plugin --save-dev
// webpack.prod.js中配置插件
// 分割插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// 壓縮css代碼
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
// js壓縮代碼
const TerserJSPlugin = require('terser-webpack-plugin');
module: { // 配置對應(yīng)的css打包
rules: [
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader, // 使用插件的loader 替換style-loader
options: { // 配置對應(yīng)的參數(shù)
publicPath: '../',
hmr: process.env.NODE_ENV === 'development',
},
},
'css-loader',
],
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader', 'postcss-loader']
},
]
},
optimization: {
runtimeChunk: { //解決打包兩次 contHash 值不一樣的情況
name: 'runtime'
},
minimize: true,
minimizer: [ new OptimizeCSSAssetsPlugin({})], // css 自動合并與壓縮 TerserJSPlugin這個壓縮js代碼
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].css', // dist 目錄下index.html直接引用的會默認(rèn)生成filename name.css 斑芜,間接的使用的文件會生成chunkFilename
chunkFilename: 'css/[name].css',
ignoreOrder: false,
}),
],
- 修改tree shaking package.json中
// 前面有提到過肩刃,css文件并沒有輸出模塊,所以不進行tree shaking優(yōu)化
"sideEffects": ["*.css"],
- 打包結(jié)果會生成一個壓縮的main.css:
MiniCssExtractPlugin,這個插件不會熱更新杏头,不支持html,會降低開發(fā)效率盈包,一般在線上環(huán)境打包使用這個,在配置css壓縮時會覆蓋掉webpack默認(rèn)的優(yōu)化設(shè)置醇王,導(dǎo)致JS代碼無法壓縮呢燥,所以還需要把JS代碼壓縮插件導(dǎo)入進來 terser-webpack-plugin
4.preload和prefetch及懶加載
- webpackPrefetch: 等頁面核心業(yè)務(wù)加載完成后,空閑時間去加載一些懶加載的代碼
- webpackPreload: 通常用于本頁面要用到的關(guān)鍵資源寓娩,包括關(guān)鍵js叛氨、字體、css文件棘伴。preload將會把資源得下載順序權(quán)重提高寞埠,使得關(guān)鍵數(shù)據(jù)提前下載好,優(yōu)化頁面打開速度焊夸。
通過import 這個語法仁连,會提升加載速度,類似于vue 及react路由的概念阱穗,懶加載實際上并不是webpack的概念饭冬,而是es的語法,webpack只不過是能夠識別這種語法進行代碼分割而已
- 例如在index.js中寫入以下代碼
// preloading
document.addEventListener('click', () => {
const element = document.createElement('div');
element.innerText = '123'
document.body.appendChild(element);
})
- npm run build 打開dist目錄下html文件
- 在瀏覽器控制臺
- 看代碼使用率 Ctrl+ shift+ p show Coverage 點擊錄制按鈕揪阶,變紅昌抠,刷新頁面,查看main.js代碼使用率
-
效果如下圖:
main.js使用率15.2%
- 將這代點擊才加載的代碼放到異步組件鲁僚,做出下面的修改
- 在src下炊苫,新建一個click.js
// click.js
function getComponent () {
const element = document.createElement('div');
element.innerText = 'dell'
document.body.appendChild(element);
}
export default getComponent
- 將index.js代碼修改
//改成異步組件
document.addEventListener('click', () => {
import('./click.js').then(({default:func}) => {
func()
})
})
- 打包,查看代碼使用率蕴茴,改異步組件劝评,使用率變成46.9%
Ctrl+ shift+ p show Coverage 看代碼使用率
,代碼使用率高倦淀,頁面速度快蒋畜,webpack希望你多寫異步組件,多寫異步代碼撞叽,放到異步組件里面去姻成,
但是異步加載或者懶加載可能影響用戶體檢插龄,可以使用webpackPrefetch等提升體驗
同步會有緩存
- 常用場景:頁面彈窗,如果能在在網(wǎng)絡(luò)空閑時間科展,加載登錄頁面均牢,這樣既不會在主業(yè)務(wù)邏輯加載的時候,影響頁面速度才睹,又能在點擊的時候徘跪,快速打開
- 使用方法:
在import 語法中添加一下語法
import(/* webpackPrefetch: true */ 'LoginModal');
import(/* webpackPreload: true */ 'ChartingLibrary');
5.shamming
shimming簡單翻譯是墊片。在開發(fā)中琅攘,經(jīng)常在一個文件中引入很多第三方庫或者自己寫的庫垮庐,每個js文件用到的庫都要引入,讓人很繁瑣坞琴,但又不能不引入哨查。webpack內(nèi)置插件ProvidePlugin能幫助我們解決上面遇到的問題。
舉個列子:比如在下面文件使用lodash.js
npm install lodash --save-dev
- src目錄下新建一個test.js
// test.js
const nameCount = () => {
let b = _.join(['123', 'abc', 'violet2'],'------------')
console.log(b)
}
export default nameCount
注意雖然index.js中導(dǎo)入了lodash剧辐,但是webpack 打包 是模塊化打包寒亥,通過import 引入的只能在當(dāng)前模塊里面使用,這樣可以降低模塊的耦合性
// index.js
import nameCount from './test'
nameCount()
-
npm start
在webpack.base.js
const webpack = require('webpack'); // webpack內(nèi)置插件荧关,需引入webpack,
new webpack.ProvidePlugin({ // // 幫助第三方模塊或者js文件結(jié)合自身需要溉奕,使用_ 的組件自動引用lodash模塊
_: 'lodash',
}),
ProvidePlugin插件作用是自動加載模塊,而不必到處 import 或 require羞酗。只要在ProvidePlugin插件中配置了變量腐宋。凡是在源碼中用到_變量,在webpack打包時檀轨,就會文件最前面引入import _ from 'loadsh'就不要我們自己手動引入了。
6. webpack 與瀏覽器緩存(Caching)
項目開發(fā)完成欺嗤,我們會將生成的dist 文件放到服務(wù)器環(huán)境参萄,為了解決瀏覽器緩存的問題,我們可以在生成環(huán)境輸出設(shè)置[contenthash]這個值是根據(jù)輸出的時候煎饼,內(nèi)容發(fā)生變化就會去更新hash值讹挎,不變化的js文件會緩存到瀏覽器中
// webapck.prod.js
output: { // 考慮去掉瀏覽器緩存更新的問題
// publicPath:'gttp://cdn.com.cn', //但是一般我們做好的項目都會上傳到線上,域名下到文件吆玖,這個時候自動生成的index.html里面的js路徑不是我想要到筒溃,我想要在路徑前面加上域名
filename: 'js/[name].[contenthash].js', // 打包文件名,可以是bundle.js默認(rèn)生成的main.js,如果打包多個文件沾乘,可以改成占位符怜奖,這樣打包出來的名字就是entry配置的main,sub
chunkFilename: 'js/[name].[contenthash].js',
},
緩存失效問題
當(dāng)js內(nèi)容沒變更的時候,但是由于模塊與模塊之間的對應(yīng)關(guān)系發(fā)生變化翅阵,兩次打包contenthash的不一樣歪玲,此時可能老版本的用戶引用的緩存文件會找報錯迁央,解決這個問題,去webpac配置 runtimeChunk
// webpack.prod.js
optimization: {
runtimeChunk: { // webpack 直接可以使用這個內(nèi)置特性 //解決打包兩次 contHash 值不一樣的情況
name: 'runtime'
},
}
-
打包結(jié)果如下:
webpack 會把main.js 與vender.js 中滥崩,在版本不同的情況下岖圈,manifest內(nèi)置的模塊及js與jis直接的關(guān)系發(fā)生變化,這個runtime.js
就是會把manifest 處理關(guān)系的代碼抽離出來钙皮,避免影響業(yè)務(wù)代碼和模塊代碼
7.打包分析工具介紹
- 官網(wǎng)鏈接
https://github.com/webpack/analyse
- 第一種在項目中生成stats.json
- 在package.json 中打包命令配置
--profile --json > stats.json
"scripts": {
"start": "webpack-dev-server --config ./build/webpack.dev.js",
"dev": "webpack --profile --json > stats.json --config ./build/webpack.dev.js",
"build": "webpack --config ./build/webpack.prod.js"
},
- 執(zhí)行npm run dev 將生成的stats.json 在 http://webpack.github.io/analyse/ 打開這個文件
-
能夠 看打包的關(guān)系以及過程的分析
- Bundle Analysis
//其他分析工具
https://webpack.js.org/guides/code-splitting/#bundle-analysis
- 例如:webpack-chart 通過打開生成的stats.json
- webpack-bundle-analyzer插件蜂科,這個比較常用,且查看的信息比較全