1、 Loader是什么?
1汪拥、我們之前打包的都是js文件,下面試試打包一個(gè)圖片文件篙耗。
首先將一個(gè)圖片文件放進(jìn)src目錄下迫筑,接著添加index.js的模塊引入代碼:
var Header = require('./header.js');
var Sidebar = require('./sidebar.js');
var Content = require('./content.js');
var Logo = require('./logo.png');
new Header();
new Sidebar();
new Content();
執(zhí)行npm run bundle后發(fā)現(xiàn)報(bào)錯(cuò),原因是webpack是默認(rèn)知道如何處理js模塊的宗弯,但是不知道圖片這種文件該如何去打包铣焊。所以我們應(yīng)該主動(dòng)告訴他如何去打包圖片文件,這就需要我們自己去配置文件了罕伯。
webpack.config.js:
const path = require('path');
module.exports = {
mode: 'production', //意思是打包后的文件被壓縮曲伊,我們不配置mode的話默認(rèn)值是被壓縮,但是會警告追他。
entry: './src/index.js',
//module的意思是當(dāng)不知道如何去打包的時(shí)候坟募,webpack就會去模塊這個(gè)配置里面去找
module: {
//rules是規(guī)則,是一個(gè)數(shù)組
rules: [{
//假設(shè)我們打包的模塊是以.png結(jié)尾的文件
test: /\.png$/,
//就應(yīng)該這么去打包:用一個(gè)file-loader來打包該文件邑狸。(首先需要安裝file-loader這個(gè)工具)
use: {
loader: 'file-loader'
}
}]
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
安裝file-loader工具: npm install file-loader -D
安裝完成之后執(zhí)行打包命令懈糯,發(fā)現(xiàn)可以正常打包成功。
可以發(fā)現(xiàn)webpack把該圖片也打包進(jìn)了dist文件夾里面
我們嘗試在index.js文件中console.log(Logo)单雾,然后重新打包赚哗,完成之后在控制發(fā)現(xiàn)打印出了圖片被打包之后的文件名。
其實(shí)Loader就是一個(gè)打包方案硅堆,它知道對于某一個(gè)特定的文件屿储,webpack應(yīng)該如何進(jìn)行打包,本身webpack是不知道該如何對一些文件進(jìn)行處理的渐逃,但是Loader知道够掠,所以webpack去求助于Loader就可以了。
2茄菊、例子:將打包后的圖片插入到頁面中
將三個(gè)模塊的js文件都刪除疯潭,修改index.js中的代碼:
import Logo from './logo.png';
var img = new Image();
img.src = Logo;
var root = document.getElementById('root');
root.append(img);
重新打包,完成之后圖片插入到id為root的dom下面了面殖。
本節(jié)主要介紹的就是Loader的作用:webpack不能識別非js結(jié)尾的模塊竖哩,就需要通過Loader讓webpack識別出來。
類似的像我們之前import以vue結(jié)尾的文件脊僚,Webpack也是不能識別的相叁,所以也需要通過loader來幫助它識別。
2、 使用Loader打包靜態(tài)資源(圖片篇)
1钝荡、我們前面對圖片進(jìn)行了打包,可是打包過后的文件名字是一個(gè)很長的字符串舶衬,我們希望打包后的文件名和打包之前一樣埠通。
配置文件webpack.config.js
const path = require('path');
module.exports = {
mode: 'production',
entry: './src/index.js',
module: {
rules: [{
test: /\.png$/,
use: {
loader: 'file-loader',
//使用loader的時(shí)候,可以配置一些參數(shù),這些參數(shù)放在options這個(gè)對象中
options: {
name: '[name].[ext]' //[name]表示打包之前的名字, [ext]表示打包之前的后綴
}
}
}]
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
重新打包過后文件目錄:
[name].[ext]這種配置的語法叫做placeholder,中文叫占位符。
我們可以在test配置中一次添加多個(gè)后綴:
2姜骡、我們希望打包后的圖片文件被保存在一個(gè)images文件夾里面蝉绷。
配置文件:
const path = require('path');
module.exports = {
mode: 'production',
entry: './src/index.js',
module: {
rules: [{
test: /\.(png|jpg|gif)$/,
use: {
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/' //意思就是將以png或jpg或gif結(jié)尾的打包文件打包過后放在dist文件夾下的images文件夾下面
}
}
}]
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
打包完成之后的目錄結(jié)構(gòu):
file-loader的配置特別多,以后會遇到很多圖片打包的問題租副,如果不知道如何去處理,可以去file-loader的文檔查找。
3渗柿、url-loader
url-loader除了能做file-loader的工作之外,還能做一個(gè)別的事情脖岛。
將配置文件的file-loader改成url-loader:
const path = require('path');
module.exports = {
mode: 'production',
entry: './src/index.js',
module: {
rules: [{
test: /\.(png|jpg|gif)$/,
use: {
loader: 'url-loader', //url-loader可以完成file-loader能完成的任何功能
options: {
name: '[name].[ext]',
outputPath: 'images/'
}
}
}]
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
首先嘗試下能否打包朵栖,發(fā)現(xiàn)報(bào)錯(cuò)。原因是我們沒有安裝url-loader柴梆。執(zhí)行安裝命令:npm install url-loader -D
安裝成功之后陨溅,執(zhí)行打包命令,打包成功后的目錄文件如下:
發(fā)現(xiàn)圖片文件沒有被打包绍在,但是頁面卻將圖片顯示了出來门扇。去控制臺查看發(fā)現(xiàn),圖片的地址不是外部引入的偿渡,而是一個(gè)base64的內(nèi)容:
原因是url-loader和file-loader不一樣臼寄,會把我們的圖片轉(zhuǎn)換成一個(gè)base64的字符串,然后直接放到bundle.js里面溜宽,而不是單獨(dú)生成一個(gè)圖片文件脯厨。
優(yōu)點(diǎn):圖片打包到j(luò)s里面,實(shí)際上加載好了js坑质,頁面就出來了合武,也就不用額外去請求一個(gè)圖片的地址了。省了一次http請求涡扼。
缺點(diǎn):如果圖片很大稼跳,打包生成的js文件也會很大,那么加載js的時(shí)間就會很長吃沪,所以一開始頁面可能很長時(shí)間什么都顯示不出來汤善。
所以url-loader的最佳使用場景是:如果一個(gè)圖片很小,就幾kb,那么打包成base64的形式是非常好的選擇红淡。沒必要讓幾kb的圖片去發(fā)http請求不狮,很浪費(fèi)時(shí)間。如果圖片很大在旱,那么最好使用file-loader打包到dist目錄下摇零,不要打包到j(luò)s里面。因?yàn)檫@樣可以讓bundle.js快速加載完成桶蝎,頁面可以快速的顯示出來驻仅,不然bundle.js會很大,導(dǎo)致頁面很久才能顯示出來登渣。解決辦法:
添加配置:
const path = require('path');
module.exports = {
mode: 'production',
entry: './src/index.js',
module: {
rules: [{
test: /\.(png|jpg|gif)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 2048 //意思是如果圖片的大小超過了2048個(gè)字節(jié)的話噪服,那么就會像file-loader一樣,把它打包到dist文件夾下胜茧。小于2048個(gè)字節(jié)即小于2kb的話粘优,會把它打包成一個(gè)base64的字符串。放到bundle.js里面呻顽。
}
}
}]
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
3敬飒、 使用Loader打包靜態(tài)資源(樣式篇1)
1、接著之前的圖片芬位,我們需要圖片大小是150x150无拗,這時(shí)我們就需要一個(gè)樣式文件來修飾這張圖片。
index.css:
.logo {
width: 150px;
height: 150px;
}
index.js:
import Logo from './logo.png';
import './index.css';
var img = new Image();
img.src = Logo;
img.classList.add('logo');
var root = document.getElementById('root');
root.append(img);
此時(shí)圖片的樣式是不會改變的昧碉,我們需要告訴webpack去打包這個(gè)css文件英染。打包c(diǎn)ss文件要用到兩個(gè)loader,style-loader和css-loader被饿。執(zhí)行npm install命令安裝這兩個(gè)loader四康。然后配置webpack.config.js。
webpack.config.js:
const path = require('path');
module.exports = {
mode: 'production',
entry: './src/index.js',
module: {
rules: [{
test: /\.(png|jpg|gif)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 2048 //意思是如果圖片的大小超過了2048個(gè)字節(jié)的話狭握,那么就會像file-loader一樣闪金,把它打包到dist文件夾下。小于2048個(gè)字節(jié)即小于2kb的話论颅,會把它打包成一個(gè)base64的字符串哎垦。放到bundle.js里面。
}
}
}, {
test: /\.css$/,
use: ['style-loader', 'css-loader']
}]
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
執(zhí)行打包命令恃疯,成功之后頁面中圖片的大小就是150x150啦漏设。
- css-loader:
添加一個(gè)logo.css文件:
.logo {
width: 150px;
height: 150px;
}
index.css:
@import './logo.css';
index.js文件不變,此時(shí)執(zhí)行打包命令也能正常地實(shí)現(xiàn)效果今妄。
css-loader作用:css-loader會幫我們分析出幾個(gè)css文件之間的關(guān)系郑口,最終把這幾個(gè)Css文件合并成一段css鸳碧。
- style-loader
作用:在得到css-loader生成的css內(nèi)容之后,style-loader會把這些內(nèi)容掛載到頁面的head部分犬性,
2瞻离、有時(shí)候我們用的不是css編寫樣式, 而是使用less或者scss等比較新的技術(shù)來編寫乒裆。
刪除logo.css文件套利,修改index.css文件為index.scss:
body {
.logo {
width: 150px;
height: 150px;
}
}
index.js中引入index.scss。
此時(shí)缸兔,很顯然是不能打包成功的,此時(shí)還需要借助sass.loader來幫助我們編譯scss文件吹艇。安裝命令:
npm install sass-loader node-sass -D
安裝成功以后修改配置:
const path = require('path');
module.exports = {
mode: 'production',
entry: './src/index.js',
module: {
rules: [{
test: /\.(png|jpg|gif)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 2048
}
}
}, {
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader']
}]
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
執(zhí)行打包命令即可成功惰蜜。
注意:在webpack的配置里面,loader是有先后順序的受神,執(zhí)行順序是從下到上抛猖,從右到左。所以當(dāng)我們?nèi)ゴ虬粋€(gè)scss文件的時(shí)候鼻听,首先會去執(zhí)行sass-loader财著,對代碼進(jìn)行一個(gè)翻譯,翻譯成css代碼以后給css-loader執(zhí)行撑碴,都處理好了以后再給style-loader掛載到頁面上撑教。
3、使用css3的時(shí)候醉拓,自動(dòng)幫助我們添加廠商前綴-------postcss-loader伟姐。
首先npm install postcss-loader -D進(jìn)行安裝;
接著安裝一個(gè)插件亿卤,npm install autoprefixer -D愤兵;
在lessons目錄下創(chuàng)建一個(gè)postcss.config.js文件
module.exports = {
plugins: [
require('autoprefixer')
]
}
配置文件:
const path = require('path');
module.exports = {
mode: 'production',
entry: './src/index.js',
module: {
rules: [{
test: /\.(png|jpg|gif)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 2048
}
}
}, {
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'sass-loader',
'postcss-loader'
]
}]
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
index.scss:
body {
.logo {
width: 150px;
height: 150px;
transform: translate(200px, 200px);
}
}
最后執(zhí)行npm run bundle打包,可以發(fā)現(xiàn)對于不支持該屬性的瀏覽器會自動(dòng)給我們添加了css3瀏覽器廠商前綴排吴。就是postcss-loader里面的autoprefixer插件幫我們添加的秆乳。
4、 使用Loader打包靜態(tài)資源(樣式篇2)
1钻哩、介紹一下css-loader的配置
我們?nèi)绻枰獙σ氲腸ss-loader進(jìn)行配置的時(shí)候屹堰,就不要用字符串的形式寫,用一個(gè)對象來包裹街氢。options: {importLoaders: 2}意思是當(dāng)我們在scss文件中也用了@import 引入scss文件的時(shí)候双藕。
對于index.js里面引入的index.scss文件,他會依次執(zhí)行從下往上的loader過程阳仔。但是打包index.scss的過程中忧陪,index.scss內(nèi)部又額外引入了一個(gè)scss文件扣泊,那么額外引入的scss文件可能就不會走下面的postcss-loader和sass-loader過程,而是直接去走css-loader的過程嘶摊。如果我們也希望額外引入的scss文件也走下面兩個(gè)loader的流程延蟹。那么我們就需要使用importLoaders,并且給它設(shè)置成2叶堆。
2阱飘、Css打包的模塊化
看一個(gè)例子
目錄結(jié)構(gòu):
createLogo.js:
import Logo from './logo.png';
function createLogo() {
var img = new Image();
img.src = Logo;
img.classList.add('logo');
var root = document.getElementById('root');
root.append(img);
}
export default createLogo;
index.js:
import Logo from './logo.png';
import './index.scss';
import createLogo from './createLogo.js';
createLogo();
var img = new Image();
img.src = Logo;
img.classList.add('logo');
var root = document.getElementById('root');
root.append(img);
最后進(jìn)行一次打包,效果圖如下:
可以發(fā)現(xiàn)這兩個(gè)圖片的樣式一模一樣虱颗。所以發(fā)現(xiàn)雖然我們只在index.js中引入了index.scss文件沥匈,但是createLogo.js里面的函數(shù)創(chuàng)造的圖片樣式也是一樣的。這是因?yàn)槲覀儤邮竭@么引入的話就是全局的忘渔,從而會導(dǎo)致一些問題高帖。從而引入了CSS Module的概念,即CSS 模塊化的概念:表示這個(gè)CSS只在這個(gè)模塊里有效畦粮。
webpack.config.js:
const path = require('path');
module.exports = {
mode: 'production',
entry: './src/index.js',
module: {
rules: [{
test: /\.(png|jpg|gif)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 2048
}
}
}, {
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
modules: true //意思就是開啟css的模塊化打包
}
},
'sass-loader',
'postcss-loader'
]
}]
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
index.js:
import Logo from './logo.png';
import style from './index.scss';
import createLogo from './createLogo.js';
createLogo();
var img = new Image();
img.src = Logo;
img.classList.add(style.logo);
var root = document.getElementById('root');
root.append(img);
再次打包之后散址,發(fā)現(xiàn)此時(shí)兩個(gè)圖片的樣式就不一樣了。
如果想要另一個(gè)圖片的樣式也一樣宣赔,修改createLogo.js文件:
import Logo from './logo.png';
import style from './index.scss';
function createLogo() {
var img = new Image();
img.src = Logo;
img.classList.add(style.logo);
var root = document.getElementById('root');
root.append(img);
}
export default createLogo;
再次打包即可预麸。
使用上述的方法帶來的好處就是能夠保證各個(gè)模塊之間樣式的獨(dú)立,能夠避免很多的問題儒将。
3吏祸、如何使用webpack打包字體文件。
刪除一些文件后的目錄如下:
下載幾個(gè)iconfont圖標(biāo)下來钩蚊,將里面的.eot犁罩、.ttf、.svg两疚、.woff結(jié)尾的四個(gè)文件復(fù)制到src目錄下新建的font文件夾中床估,然后將iconfont.css的代碼復(fù)制到index.scss中。然后修改iconfont字體的引用路徑诱渤。
目錄結(jié)構(gòu):
index.scss中的代碼就是iconfont.css的代碼丐巫。
index.js:
import './index.scss';
var root = document.getElementById('root');
root.innerHTML = '<div class="iconfont icon-jiantou">abc</div>';
此時(shí)執(zhí)行打包命令,發(fā)現(xiàn)報(bào)錯(cuò)勺美。這是因?yàn)閣ebpack會去打包index.scss文件递胧,但是index.scss文件里又引入了很多類型的字體文件,這個(gè)字體文件webpack不知道該如何去打包赡茸。所以我們應(yīng)該去配置中告訴它該如何去打包缎脾。
配置文件:
const path = require('path');
module.exports = {
mode: 'production',
entry: './src/index.js',
module: {
rules: [{
test: /\.(png|jpg|gif)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 2048
}
}
}, { //打包字體文件配置
test: /\.(eot|ttf|svg|woff)$/,
use: {
loader: 'file-loader'
}
}, {
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}]
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
執(zhí)行打包命令即可。注意別忘了去掉模塊化打包配置占卧,否則沒有效果:
5遗菠、使用plugins讓打包更快捷
1联喘、我們dist目錄下有一個(gè)index.html文件,假如我們將dist文件夾刪除辙纬,再進(jìn)行打包豁遭,打包之后的dist文件夾里面沒有index.html文件,每次都需要手工添加該文件。這就需要一個(gè)插件幫助我們自動(dòng)生成它。
首先npm install html-webpack-plugin -D安裝該插件片林。
webpack.config.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin'); //引入該插件
module.exports = {
mode: 'production',
entry: './src/index.js',
module: {
rules: [{
test: /\.(png|jpg|gif)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 2048
}
}
}, {
test: /\.(eot|ttf|svg|woff)$/,
use: {
loader: 'file-loader'
}
}, {
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}]
},
plugins: [new HtmlWebpackPlugin()], //配置plugins
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
此時(shí)我們打包生成的dist目錄下就有一個(gè)index.html文件了
HtmlWebpackPlugin 作用: 會在打包結(jié)束后,自動(dòng)生成一個(gè)html文件闪幽,并把打包生成的js自動(dòng)引入到這個(gè)html文件中。
但是目前該文件里面沒有我們需要的id等于root的div標(biāo)簽涡匀。如果我們需要自動(dòng)添加該標(biāo)簽盯腌,可以這么配置。
首先渊跋,在src目錄下腊嗡,新建一個(gè)index.html文件着倾,添加代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
webpack.config.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'production',
entry: './src/index.js',
module: {
rules: [{
test: /\.(png|jpg|gif)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 2048
}
}
}, {
test: /\.(eot|ttf|svg|woff)$/,
use: {
loader: 'file-loader'
}
}, {
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}]
},
plugins: [new HtmlWebpackPlugin({
template: 'src/index.html' //意思是當(dāng)打包完成之后會生成一個(gè)html拾酝,此時(shí)生成的html是以src/index.html為模板生成的。
})],
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
然后執(zhí)行打包命令卡者,此時(shí)dist目錄下的index.html文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="root"></div>
<script type="text/javascript" src="bundle.js"></script></body>
</html>
頁面也有效果了蒿囤。
plugin 作用:可以在webpack運(yùn)行到某個(gè)時(shí)刻的時(shí)候,幫你做一些事情崇决。有點(diǎn)類似vue的生命周期函數(shù)
2材诽、我們修改一下webpack.confitg.js,將output中的filename修改為dist.js恒傻。再次打包脸侥。
打包完成之后發(fā)現(xiàn)雖然index.html中引用的js文件也變成了dist.js,但是dist文件夾中之前打包的bundle.js卻還存在盈厘,有時(shí)候這樣會出現(xiàn)一些問題睁枕。所以我們需要先將bundle.js刪除,再進(jìn)行打包沸手。要實(shí)現(xiàn)這個(gè)功能外遇,我們就需要一個(gè)插件:cleanWebpackPlugin
首先進(jìn)行安裝:npm install clean-webpack-plugin -D
接著進(jìn)行配置:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin'); //引入該插件
module.exports = {
mode: 'production',
entry: './src/index.js',
module: {
rules: [{
test: /\.(png|jpg|gif)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 2048
}
}
}, {
test: /\.(eot|ttf|svg|woff)$/,
use: {
loader: 'file-loader'
}
}, {
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}]
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new CleanWebpackPlugin() //表示在打包之前會使用CleanWebpackPlugin這個(gè)插件幫助我們?nèi)h除dist目錄下的所有內(nèi)容
],
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
dist目錄:
可以發(fā)現(xiàn)之前的dist.js就不存在了,只有bundle.js了契吉。而且頁面也有效果跳仿。
6、Entry和Output的基礎(chǔ)配置
1捐晶、我們將webpack.config.js文件中的output的filename刪除菲语,再次打包妄辩。可以發(fā)現(xiàn)webpack打包的js文件默認(rèn)名為main.js谨究。
entry: './src/index.js'這個(gè)寫法和圖片
的寫法是一樣的恩袱。entry對象中main的意思是'./src/index.js'打包后生成的文件對應(yīng)的名字是main.js。
2胶哲、有個(gè)新需求畔塔,希望把src目錄下的index.js反復(fù)打包兩次,生成兩個(gè)文件鸯屿,第一個(gè)叫main.js澈吨,第二個(gè)叫sub.js。
如果此時(shí)我們output中的filename設(shè)置成bundle.js寄摆,可以發(fā)現(xiàn)會打包錯(cuò)誤谅辣。想要解決這個(gè)問題,我們可以將bundle替換成一個(gè)占位符[name]婶恼,name的意思就是打包entry的key值桑阶,
webpack.config.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
mode: 'production',
entry: {
main: './src/index.js',
sub: './src/index.js'
},
module: {
rules: [{
test: /\.(png|jpg|gif)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 2048
}
}
}, {
test: /\.(eot|ttf|svg|woff)$/,
use: {
loader: 'file-loader'
}
}, {
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}]
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new CleanWebpackPlugin()
],
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
}
}
執(zhí)行打包命令之后可以看到成功打包了兩個(gè)js文件:
3、有時(shí)候會把打包完的文件index.html給后端勾邦,作為后端的一個(gè)入口文件蚣录。但是會把js文件上傳到CDN這樣一個(gè)域名下面。此時(shí)html文件的引入地址就需要修改了眷篇。如何實(shí)現(xiàn)打包完之后自動(dòng)在前面添加CDN的域名萎河。
webpack.config.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
mode: 'production',
entry: {
main: './src/index.js',
sub: './src/index.js'
},
module: {
rules: [{
test: /.(png|jpg|gif)/,
use: {
loader: 'file-loader'
}
}, {
test: /.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}]
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new CleanWebpackPlugin()
],
output: {
publicPath: 'http://cdn.com.cn', //給index.html文件引入地址之前添加CDN地址
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
}
}
重新打包之后查看index.html文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="root"></div>
<script type="text/javascript" src="http://cdn.com.cn/main.js"></script><script type="text/javascript" src="http://cdn.com.cn/sub.js"></script></body>
</html>
所以,如果我們的項(xiàng)目后臺用index.html蕉饼,而靜態(tài)資源放到cdn上的情況下虐杯,此時(shí)就要用到output的publicPath這個(gè)配置項(xiàng);
7昧港、SourceMap的配置
刪除一些文件后的目錄結(jié)構(gòu):
index.js:
console.log('hello world');
webpack.config.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
mode: 'production',
devtool: 'none', //當(dāng)前處于開發(fā)者模式擎椰,SourceMap默認(rèn)是打開的,此配置可以將它關(guān)閉
entry: {
main: './src/index.js'
},
module: {
rules: [{
test: /\.(png|jpg|gif)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 2048
}
}
}, {
test: /\.(eot|ttf|svg|woff)$/,
use: {
loader: 'file-loader'
}
}, {
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}]
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new CleanWebpackPlugin()
],
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
}
}
此時(shí)我們將sourcemap關(guān)閉了,如果js文件代碼編寫錯(cuò)了创肥,打包之后控制臺報(bào)錯(cuò)达舒,只能看出是打包后的js文件某一行出錯(cuò),而不能知道打包前是哪個(gè)文件出錯(cuò)瓤的。
解決辦法:
webpack.config.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
mode: 'production',
devtool: 'source-map', //開啟sourcemap
entry: {
main: './src/index.js'
},
module: {
rules: [{
test: /\.(png|jpg|gif)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 2048
}
}
}, {
test: /\.(eot|ttf|svg|woff)$/,
use: {
loader: 'file-loader'
}
}, {
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}]
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new CleanWebpackPlugin()
],
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
}
}
此時(shí)打包過后休弃,控制臺報(bào)錯(cuò)會指出具體哪個(gè)js文件的哪一行出錯(cuò)了。而且dist目錄下也多出了一個(gè)main.js.map文件圈膏,里面就是一些映射關(guān)系塔猾。
devtool的各個(gè)配置值的含義:
- source-map: 可以看到它的build是--,表示打包速度比較慢稽坤。
- inline-source-map: 也會將錯(cuò)誤指示到具體的源文件中丈甸,不過它對比source-map糯俗,沒有map.js文件。其實(shí)用這個(gè)配置的話睦擂,他會把這個(gè)
map文件通過dataUrl的方式直接寫在main.js里面得湘。 - cheap前綴:只指出錯(cuò)誤在哪一行即可,不用精確到哪一列顿仇。打包性能會得到一定提升淘正。還有一個(gè)作用:如果寫了它,那么sourmap只針對業(yè)務(wù)代碼臼闻,比如我們的業(yè)務(wù)代碼只在index.js中鸿吆,那么我們的打包只會映射這個(gè)文件和打包生成的main.js之間的關(guān)系。不會管引入的一些第三方模塊loader的錯(cuò)誤如何做映射述呐。如果也想第三方模塊的錯(cuò)誤映射的話惩淳,可以加module
- eval:是打包速度最快并且性能最好的一種打包方式, 但是如果代碼比較復(fù)雜乓搬,eval提示的錯(cuò)誤可能不會那么全面思犁。
最佳實(shí)踐:如果在開發(fā)環(huán)境(development)中使用sourcemap的話,建議使用cheap-module-eval-source-map這種形式进肯,這種方式提示的錯(cuò)誤比較全激蹲,同時(shí)打包的速度也是比較快的。如果代碼要放到線上了坷澡,此時(shí)就應(yīng)該處于生產(chǎn)環(huán)境(production)托呕,一般上線環(huán)境的代碼沒有必要讓它有一個(gè)sourcemap的映射含蓉,直接放到線上即可频敛。如果我們也想出現(xiàn)錯(cuò)誤以后能夠快速的定位問題,那么此時(shí)可以配置成cheap-module-source-map馅扣,這樣提示效果會更好斟赚。
8. 使用WebpackDevServer 提升開發(fā)效率
1、我們希望修改了src目錄下的源代碼后差油,webpack自動(dòng)地去幫我們打包拗军,而不需要我們用命令行的形式去手動(dòng)打包。
方法一:
修改package.json:
意思是webpack會幫我們?nèi)ケO(jiān)聽它打包的文件蓄喇,只要打包的文件變化发侵,就會去自動(dòng)打包。我們可以通過修改index.js打印的東西去控制臺查看就可以發(fā)現(xiàn)妆偏。
方法二:
我們第一次執(zhí)行npm run watch的時(shí)候刃鳄,自動(dòng)幫我們實(shí)現(xiàn)打包,同時(shí)自動(dòng)打開瀏覽器钱骂,同時(shí)開可以去模擬服務(wù)器上面的一些特性叔锐。
安裝webpackdevserver挪鹏,命令:npm install webpack-dev-server -D
package.json:
webpack.config.js:
執(zhí)行:npm run start 來啟動(dòng)Webpack devserve
可以發(fā)現(xiàn)這個(gè)時(shí)候就自動(dòng)幫我們起了一個(gè)localhost:8080的服務(wù)器了。
可以在瀏覽器打開它愉烙,同時(shí)修改源文件也會自動(dòng)打包并且自動(dòng)刷新瀏覽器讨盒。
小知識點(diǎn):
我們?yōu)槭裁匆_一個(gè)服務(wù)器?
寫過vue或react的都知道步责,有時(shí)候前端發(fā)送ajax請求返顺,如果通過文件的形式在瀏覽器直接打開html文件的話,這時(shí)候如果要發(fā)送ajax請求是不可以的蔓肯。因?yàn)橐笏诘膆tml必須在一個(gè)服務(wù)器上通過http協(xié)議的方式打開创南,這就是借助webpack devser開啟一個(gè)服務(wù)器的原因。此時(shí)發(fā)送ajax請求就沒有問題了省核。
我們平時(shí)使用vue或react的腳手架工具它都會幫我們?nèi)ラ_啟一個(gè)服務(wù)器稿辙,實(shí)際上大多數(shù)都是直接使用Webpack devserver幫助我們開啟出來的。
2气忠、之前比較老的腳手架工具里面沒有使用webpackdevser這個(gè)工具邻储,而是自己實(shí)現(xiàn)了一個(gè)類似這個(gè)功能的工具。
package.json添加配置:
"scripts": {
"bundle": "webpack",
"watch": "webpack --watch",
"start": "webpack-dev-server",
"server": "node server.js"
}
意思就是我運(yùn)行npm run middleware的時(shí)候旧噪,我想自己寫一個(gè)服務(wù)器吨娜,這個(gè)服務(wù)器如果監(jiān)聽到src目錄下的內(nèi)容有改變,它會像webpackdevserver一樣自動(dòng)的幫我們?nèi)ブ貑⑦@個(gè)服務(wù)器淘钟,更新網(wǎng)頁上的內(nèi)容宦赠。
- 安裝express幫助我們快速去創(chuàng)建一個(gè)服務(wù)器, 服務(wù)器要去監(jiān)聽Webpack的變化,然后幫我們重新的去打包米母,所以還需要借助一個(gè)Webpack的開發(fā)中間件webpack-dev-middleware:npm install express webpack-dev-middleware -D
- webpack.config.js:
然后在lesson目錄下創(chuàng)建一個(gè)server.js文件:
const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const config = require('./webpack.config.js');
const complier = webpack(config); //webpack函數(shù)傳入config勾扭,結(jié)果返回一個(gè)編譯器
const app = express();
app.use(webpackDevMiddleware(complier, {
publicPath: config.output.publicPath //意思就是只要文件發(fā)生改變了,complier就會重新運(yùn)行铁瞒。重新運(yùn)行生成的文件對應(yīng)的打包輸出內(nèi)容的publicPath就是config.output.publicPath
}))
app.listen(3000, () => {
console.log('server is running');
});
運(yùn)行npm run server命令妙色,發(fā)現(xiàn)成功打包,瀏覽器中也答應(yīng)出內(nèi)容慧耍。我們修改index.js打印的內(nèi)容身辨,刷新瀏覽器后發(fā)現(xiàn)控制臺也跟著改變了,說明又自動(dòng)打包了芍碧。只不過目前沒有webpackdevserver那么智能煌珊。想要實(shí)現(xiàn)那么智能的效果需要耗費(fèi)很多的精力,我們只需要了解可以這么寫就行泌豆。
9. Hot Module Replacement(熱模塊替換HMR)
在使用webpackdevserver的時(shí)候定庵,打包之后我們可以發(fā)現(xiàn)沒有dist這個(gè)目錄。實(shí)際上webpackdevserver還是會對src目錄下的代碼進(jìn)行打包,但是打包生成的文件不會放在dist目錄下洗贰,而是放在電腦內(nèi)存下面找岖。這樣可以有效提升打包的速度,讓我們的開發(fā)更快敛滋。
刪除server.js文件许布,修改一些配置
目錄:
webpack.config.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
devtool: 'cheap-module-eval-source-map', //開啟sourcemap
entry: {
main: './src/index.js'
},
devServer: {
contentBase: './dist',
open: true, //啟動(dòng)webpackdevser的時(shí)候(即 npm run start)會自動(dòng)幫我們?nèi)ゴ蜷_一個(gè)瀏覽器,然后自動(dòng)訪問服務(wù)器的地址绎晃。
port: 10000
},
module: {
rules: [{
test: /\.(png|jpg|gif)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 2048
}
}
}, {
test: /\.(eot|ttf|svg|woff)$/,
use: {
loader: 'file-loader'
}
}, {
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}, { //增加一個(gè)css loader
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
}]
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new CleanWebpackPlugin()
],
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
}
}
package.json:
index.js:
import './style.css';
var btn = document.createElement('button');
btn.innerHTML = '新增';
document.body.appendChild(btn);
btn.onclick = function() {
var div = document.createElement('div');
div.innerHTML = 'item';
document.body.appendChild(div);
}
style.css:
div:nth-of-type(odd) {
background: yellow;
}
瀏覽器打開這個(gè)項(xiàng)目蜜唾,我們多次點(diǎn)擊新增按鈕,頁面效果:
此時(shí)我們?nèi)バ薷膕tyle.css文件庶艾,將顏色改為blue袁余。發(fā)現(xiàn)頁面中的div不存在了。我們希望之前渲染的元素不要修改咱揍,只修改樣式颖榜,此時(shí)我們就可以借助HMR來幫助我們實(shí)現(xiàn)該功能。
webpack.config.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const webpack = require('webpack'); //引入webpack
module.exports = {
mode: 'development',
devtool: 'cheap-module-eval-source-map',
entry: {
main: './src/index.js'
},
devServer: {
contentBase: './dist',
open: true,
port: 10000,
hot: true, //開啟HMR功能
hotOnly: true //即便HRM功能沒有生效煤裙,我也不讓瀏覽器自動(dòng)的刷新
},
module: {
rules: [{
test: /\.(png|jpg|gif)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 2048
}
}
}, {
test: /\.(eot|ttf|svg|woff)$/,
use: {
loader: 'file-loader'
}
}, {
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}, {
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
}]
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new CleanWebpackPlugin(),
new webpack.HotModuleReplacementPlugin() //使用webpack的HMR功能
],
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
}
}
此時(shí)npm run start重新運(yùn)行webpack掩完,發(fā)現(xiàn)再次修改顏色就不會產(chǎn)生上述問題了。
多個(gè)js文件模塊之間不想相互影響時(shí)硼砰,也可以使用HMR,配置都差不多
當(dāng)我們在代碼里引入其它的模塊的時(shí)候且蓬,如果我們希望這個(gè)模塊的代碼發(fā)生了變化,只去更新這個(gè)模塊這部分的代碼题翰,那就需要使用HMR恶阴。HMR的優(yōu)點(diǎn):方便我們調(diào)試css和js
10. 使用Babel處理ES6的語法(1)
安裝babel-loader插件npm install --save-dev babel-loader @babel/core
-
添加webpack.config.js配置:
安裝babel/preset-env插件幫我們將es6翻譯成es5:npm install @babel/preset-env --save-dev
這個(gè)時(shí)候我們寫es6語法就會被翻譯成es5的語法了。但是這樣配置語法只是翻譯了一部分豹障,還有一些沒有翻譯的語法在低版本瀏覽器中還是不能識別冯事。我們還需要一個(gè)polyfill插件幫我們將一些變量或者對象在低版本瀏覽器中的補(bǔ)充。安裝命令:npm install --save @babel/polyfill
在業(yè)務(wù)代碼index.js的最頂部引入:import "@babel/polyfill";
重新npx webpack進(jìn)行打包沼填,打包之后發(fā)現(xiàn)main.js的文件比之前大了很多桅咆,這就是polyfill要去彌補(bǔ)之前低版本瀏覽器不存在的一些內(nèi)容括授。實(shí)際中我們不希望它把所有缺失的內(nèi)容都保存進(jìn)main.js中坞笙,只需要保存一些我們用了但是低版本瀏覽器缺失的內(nèi)容。增加配置:
使用npx webpack進(jìn)行打包荚虚,可以發(fā)現(xiàn)main.js小了很多薛夜。
可以單獨(dú)將babel的options寫到.babelrc文件下:
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage"
}
]
]
}
此時(shí)webpack.config.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
entry: './src/index.js',
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}, {
test: /\.jpg$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 2048
}
}
}, {
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new CleanWebpackPlugin()
],
output: {
path: path.resolve(__dirname, 'bundle'),
filename: '[name].js'
}
}
index.js:
import "@babel/polyfill";
const arr = [
new Promise(() => {}),
new Promise(() => {})
];
arr.forEach((item) => {
console.log(item);
})
11. 使用Babel處理ES6的語法(2)
1、指定瀏覽器的版本
webpack.config.js:
{
test: /\.js$/,
exclude: /node_modules/,
loader:'babel-loader',
options: {
"presets": [['@babel/preset-env', {
targets: {
chrome: '67' //意思是本次項(xiàng)目會運(yùn)行在67版本以上的chrome瀏覽器上版述,這樣打包的文件更小梯澜,因?yàn)?7版本時(shí)有一些用到es6的代碼不需要polyfill補(bǔ)充,因?yàn)榇藭r(shí)該瀏覽器可能已經(jīng)識別了渴析。
},
useBuiltIns: 'usage'
}]]
}
}
打包之后發(fā)現(xiàn)main.js更小了晚伙。
注意:如果在編寫一些業(yè)務(wù)代碼的時(shí)候吮龄,需要用到babel闷板,配置方案可以參考在這之前講的那一套配置即可颜武。
2奋单、開發(fā)一個(gè)類庫碧绞、第三方模塊或者一個(gè)組件庫的時(shí)候用babelpolyfill這個(gè)方案實(shí)際上時(shí)會有問題的其兴。因?yàn)樗g一些es6語法時(shí)會通過全局變量的方式來注入砚著,會污染到全局環(huán)境伴嗡。所以要打包一個(gè)ui組件庫等情況下的時(shí)候要換一種配置的方式窄潭。
- 安裝插件:npm install --save-dev @babel/plugin-transform-runtime
- 安裝插件:npm install --save @babel/runtime
- 安裝插件:npm install --save @babel/runtime-corejs2
通過babel文檔查找配置迅皇,webpack.config.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const webpack = require('webpack');
module.exports = {
mode: 'production',
devtool: 'cheap-module-eval-source-map',
entry: {
main: './src/index.js'
},
devServer: {
contentBase: './dist',
port: 10000,
hot: true
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader:'babel-loader',
options: {
// "presets": [['@babel/preset-env', {
// targets: {
// chrome: '67' //意思是本次項(xiàng)目會運(yùn)行在67版本以上的chrome瀏覽器上昧辽,這樣打包的文件更小,因?yàn)?7版本時(shí)有一些用到es6的代碼不需要polyfill補(bǔ)充登颓,因?yàn)榇藭r(shí)該瀏覽器可能已經(jīng)識別了搅荞。
// },
// useBuiltIns: 'usage'
// }]]
"plugins": [["@babel/plugin-transform-runtime", {
"corejs": 2,
"helpers": true,
"regenerator": true,
"useESModules": false
}]]
}
},
{
test: /\.(jpg|png|gif)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 10240
}
}
}, {
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}, {
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
}]
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new CleanWebpackPlugin(),
new webpack.HotModuleReplacementPlugin()
],
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
}
}
index.js:
const arr = [
new Promise(() => {}),
new Promise(() => {})
];
arr.forEach((item) => {
console.log(item);
})
配置完畢,打包即可框咙。該方案適合打包一個(gè)ui組件庫等情況取具。
3、當(dāng)babel的配置很多的時(shí)候扁耐,可以在根目錄下創(chuàng)建一個(gè).babelrc文件暇检,將配置代碼放進(jìn)去。
.babelrc:
{
"plugins": [["@babel/plugin-transform-runtime", {
"corejs": 2,
"helpers": true,
"regenerator": true,
"useESModules": false
}]]
}
webpack.config.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const webpack = require('webpack');
module.exports = {
mode: 'production',
devtool: 'cheap-module-eval-source-map',
entry: {
main: './src/index.js'
},
devServer: {
contentBase: './dist',
port: 10000,
hot: true
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader:'babel-loader',
},
{
test: /\.(jpg|png|gif)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 10240
}
}
}, {
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}, {
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
}]
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new CleanWebpackPlugin(),
new webpack.HotModuleReplacementPlugin()
],
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
}
}
配置完畢婉称,打包即可块仆。
11. Webpack 實(shí)現(xiàn)對React框架代碼的打包
使用babel/preset-react插件來實(shí)現(xiàn)對react的jsx語法進(jìn)行打包。在babel中查找相應(yīng)的文檔:
- 要編寫react的代碼王暗,就要安裝react:npm install react react-dom --save
- 安裝命令:npm install @babel/preset-react -D
添加配置
webpack.config.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const webpack = require('webpack');
module.exports = {
mode: 'production',
devtool: 'cheap-module-eval-source-map',
entry: {
main: './src/index.js'
},
devServer: {
contentBase: './dist',
port: 10000,
hot: true
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader:'babel-loader',
},
{
test: /\.(jpg|png|gif)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 10240
}
}
}, {
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}, {
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
}]
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new CleanWebpackPlugin(),
new webpack.HotModuleReplacementPlugin()
],
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
}
}
.babelrc:
{
presets: [
[
"@babel/preset-env", { //再轉(zhuǎn)換一下es6的代碼
targets: {
chrome: "67"
},
useBuiltIns: "usage"
}
],
"@babel/preset-react" //先轉(zhuǎn)換一下react的代碼
]
}
index.js:
import '@babel/polyfill'; //打包業(yè)務(wù)邏輯代碼
import React, { Component } from 'react';
import ReactDom from 'react-dom';
class App extends Component {
render() {
return <div>hello world</div>
}
}
ReactDom.render(<App />, document.getElementById('root'));
執(zhí)行打包命令后悔据,App這個(gè)類就成功渲染進(jìn)dom中了。
Tree Shaking 概念詳解
index.js:
import '@babel/polyfill';
import React, { Component } from 'react';
import ReactDom from 'react-dom';
class App extends Component {
render() {
return <div>hello world</div>
}
}
ReactDom.render(<App />, document.getElementById('root'));
實(shí)際上當(dāng)前版本的Webpack比較新俗壹,如果在webpack.config.js里面配置了babel-loader的相關(guān)內(nèi)容(其實(shí)我們已經(jīng)把它移到.babelrc這個(gè)文件中了)科汗,如果我們對presets下的useBuiltIns設(shè)置了usage這樣的一個(gè)配置參數(shù)的話。那么我們index.js文件中不去引入babel/polyfill也是可以的绷雏。
2头滔、
目錄:
index.js:
import {add} from './math.js';
add(1, 2);
math.js:
export const add = (a, b) => {
console.log(a + b);
}
export const minus = (a, b) => {
console.log(a - b);
}
執(zhí)行npx webpack,控制臺打印出3涎显。但是我們?nèi)ain.js里面發(fā)現(xiàn)我們沒有import的minus方法也打包了坤检。這是沒有必要的,因?yàn)槲覀兊臉I(yè)務(wù)代碼只引入了add方法期吓,將minus也打包會導(dǎo)致打包后的main.js過大早歇。最理想的打包方式就是我們引入什么就打包什么。解決辦法:
webpack在2.0版本以后提出了Tree shaking這個(gè)概念:實(shí)際上就是把一個(gè)模塊里沒用的東西都搖晃(shaking表示搖晃)掉。一個(gè)模塊可以理解成一個(gè)樹箭跳,比如說math.js文件是一個(gè)模塊晨另,里面會導(dǎo)出很多的內(nèi)容,這些內(nèi)容可以理解成一個(gè)小的樹形結(jié)構(gòu)谱姓。而我們在index.js中只引入樹的一部分拯刁,只需要把引入的那一部分打包即可。
注意: Tree-shaking只支持ES module模塊引入的方式引入逝段。
配置Webpack.config.js:
mode設(shè)置成 'development'的時(shí)候是沒有Tree shanking這個(gè)功能的垛玻,所以我們需要自己配置:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const webpack = require('webpack');
module.exports = {
mode: 'development',
devtool: 'cheap-module-eval-source-map',
entry: {
main: './src/index.js'
},
devServer: {
contentBase: './dist',
port: 10000,
hot: true
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader:'babel-loader',
},
{
test: /\.(jpg|png|gif)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 10240
}
}
}, {
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}, {
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
}]
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new CleanWebpackPlugin(),
new webpack.HotModuleReplacementPlugin()
],
optimization: { //在開發(fā)環(huán)境中配置Tree shaking
usedExports: true //意思就是去打包引入的模塊
},
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
}
}
package.json:
小知識:
{
"name": "webpackDemo",
"sideEffects": false,
"version": "1.0.0",
"description": "",
"main": "postcss.config.js",
"dependencies": {
"@babel/polyfill": "^7.4.4",
"@babel/runtime-corejs2": "^7.5.4",
"autoprefixer": "^9.6.1",
"clean-webpack-plugin": "^3.0.0",
"core-js": "^3.1.4",
"css-loader": "^3.0.0",
"file-loader": "^4.0.0",
"html-webpack-plugin": "^3.2.0",
"node-sass": "^4.12.0",
"postcss-loader": "^3.0.0",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"sass-loader": "^7.1.0",
"style-loader": "^0.23.1",
"url-loader": "^2.0.1",
"webpack": "^4.35.3",
"webpack-cli": "^3.3.5",
"webpack-dev-server": "^3.7.2"
},
"private": true,
"devDependencies": {
"@babel/core": "^7.5.4",
"@babel/plugin-transform-runtime": "^7.5.0",
"@babel/preset-env": "^7.5.4",
"@babel/preset-react": "^7.0.0",
"babel-loader": "^8.0.6"
},
"scripts": {
"start": "webpack-dev-server"
},
"keywords": [],
"author": "",
"license": "ISC"
}
"sideEffects": false, 意思是比如index.js中引入了babel/polyfill文件,由于它沒有導(dǎo)出任何東西奶躯,用了Tree-shaking后導(dǎo)致打包會忽略它帚桩。因?yàn)楫?dāng)前index.js中沒有引入像babel/polyfill這樣的包,所以設(shè)置成false嘹黔。表示沒有需要特殊處理的東西账嚎。
重新執(zhí)行打包,發(fā)現(xiàn)還是把minus給打包了儡蔓,這是因?yàn)樵陂_發(fā)環(huán)境中生成的代碼需要做一些調(diào)試郭蕉,如果Tree-shaking把一些代碼刪除掉的話
,在做調(diào)試的時(shí)候喂江,代碼對應(yīng)的sourcemap的行數(shù)就都錯(cuò)了召锈。所以Tree-shaking還是會保留這些代碼。不過從下圖可以看出获询,它已經(jīng)提示我們了導(dǎo)出的只用了add這個(gè)模塊涨岁。
production環(huán)境下:此時(shí)Tree-shaking就會生效了。配置如下:
webpack.config.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const webpack = require('webpack');
module.exports = {
mode: 'production',
devtool: 'cheap-module-source-map',
entry: {
main: './src/index.js'
},
devServer: {
contentBase: './dist',
port: 10000,
hot: true
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader:'babel-loader',
},
{
test: /\.(jpg|png|gif)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 10240
}
}
}, {
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}, {
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
}]
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new CleanWebpackPlugin(),
new webpack.HotModuleReplacementPlugin()
],
//該模式下Tree-shaking的配置自動(dòng)就會寫好了吉嚣,我們不用自己配webpack.config.js
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
}
}
再次執(zhí)行打包后發(fā)現(xiàn)Tree-shaking就生效了:
二. Develoment 和 Production 模式的區(qū)分打包
1梢薪、當(dāng)我們項(xiàng)目開發(fā)完畢,就需要使用 production模式打包上線尝哆。兩種打包模式的差異:
- 開發(fā)環(huán)境中sourcemap是非常全的秉撇,這樣的話再開發(fā)環(huán)境下能夠幫我們快速的定位問題。production模式下秋泄,由于已經(jīng)要上線了琐馆,此時(shí)sourcemap就不是那么重要了,此時(shí)的sourcemap會更加簡潔一些或者我們通過生成一個(gè).map文件來進(jìn)行存儲印衔。
- 開發(fā)環(huán)境下我們的代碼不需要進(jìn)行壓縮啡捶,方便我們查看打包后的代碼。一旦代碼要上線奸焙,此時(shí)我們就希望代碼被壓縮,所以production模式下代碼是被壓縮過的。
2与帆、我們切換兩種模式的時(shí)候了赌,需要經(jīng)常修改webpack.config.js文件,這樣很麻煩玄糟。解決辦法:
- 首先將 development 模式下的 webpack 配置文件命名為 webpack.dev.js 勿她。
在根目錄下新建一個(gè) webpack.prod.js 文件,里面放置我們線上環(huán)境下的webpack配置代碼阵翎。
webpack.dev.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
entry: './src/index.js',
devtool: 'cheap-module-eval-source-map',
devServer: {
contentBase: './dist',
port: 10000
},
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}, {
test: /\.jpg$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 2048
}
}
}, {
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}, {
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
}]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new CleanWebpackPlugin()
],
optimization: { //在開發(fā)環(huán)境中配置Tree shaking
usedExports: true //意思就是去打包引入的模塊
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
}
}
webpack.prod.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
mode: 'production',
entry: './src/index.js',
devtool: 'cheap-module-source-map',
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}, {
test: /\.jpg$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 2048
}
}
}, {
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}, {
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
}]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new CleanWebpackPlugin()
],
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
}
}
package.json:
dev: 如果要啟動(dòng)devserver進(jìn)行開發(fā)的話逢并,那么就是使用開發(fā)環(huán)境的webpack配置
build: 如果打包線上文件的話,就使用webpack.prod.js這個(gè)配置
此時(shí)運(yùn)行npm run dev郭卫,進(jìn)行的是開發(fā)環(huán)境的webpack打包配置砍聊。運(yùn)行npm run build,執(zhí)行的是生產(chǎn)環(huán)境的打包配置贰军。
3玻蝌、上面的兩種打包方案的文件中存在大量的重復(fù)代碼,優(yōu)化方案:
- 根目錄下創(chuàng)建webpack.common.js文件词疼。
- 將兩者共有的代碼都賦值到該文件下俯树,并將之前兩個(gè)文件共有的代碼刪除。
- 引入一個(gè)第三方模塊: cnpm install webpack-merge -D贰盗,幫助我們將common的代碼和兩個(gè)打包文件代碼做一個(gè)融合许饿。
webpack.common.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
entry: './src/index.js',
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}, {
test: /\.jpg$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 2048
}
}
}, {
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}, {
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
}]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new CleanWebpackPlugin()
],
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
}
}
webpack.dev.js:
const merge = require('webpack-merge');
const commonConfig = require('./webpack.common.js');
const devConfig = {
mode: 'development',
devtool: 'cheap-module-eval-source-map',
devServer: {
contentBase: './dist',
port: 10000
},
optimization: { //在開發(fā)環(huán)境中配置Tree shaking
usedExports: true //意思就是去打包引入的模塊
}
}
module.exports = merge(commonConfig, devConfig); //將自有的和公共的做一個(gè)結(jié)合
webpack.prod.js:
const merge = require('webpack-merge');
const commonConfig = require('./webpack.common.js');
const prodConfig = {
mode: 'production',
devtool: 'cheap-module-source-map'
}
module.exports = merge(commonConfig, prodConfig);
此時(shí)能夠正常進(jìn)行兩種不同方案的打包。
注意:如果將三個(gè)文件都放在一個(gè)文件夾下面舵盈,此時(shí)package.json文件的命令路徑需要修改米辐,比如在build文件夾下:
"dev": "webpack-dev-server --config ./build/webpack.dev.js"
此時(shí)應(yīng)該修改output配置:
三. Webpack 和 Code Splitting
Code Splitting:指的是代碼分割。
此時(shí)的目錄結(jié)構(gòu):
因?yàn)槲覀兿M榭创虬蟮奈募a书释,所以不能用npm run dev來打包翘贮,添加命令:
1、例子
- 安裝一個(gè)插件:cnpm install lodash --save 它是一個(gè)功能集合爆惧,提供很多方法狸页,可以高性能地去幫助我們使用字符串拼接的一些函數(shù)等。
index.js添加代碼:
import _ from 'lodash';
console.log(_.join(['a', 'b', 'c'])); //a,b,c
打包后打印出a,b,c扯再。
假如我們index.js的業(yè)務(wù)邏輯很多芍耘,打包的話會把工具庫和業(yè)務(wù)邏輯都打包到main.js文件中。此時(shí)雖然也能正常運(yùn)行熄阻,但是main.js會比較大斋竞,導(dǎo)致加載時(shí)間很長。還有就是假如我們修改了業(yè)務(wù)代碼秃殉,用戶要重新去加載main.js坝初,此時(shí)又要等很長的時(shí)間浸剩,這樣就導(dǎo)致用戶體驗(yàn)很差。
解決辦法:
- src目錄下新建一個(gè)lodash.js文件鳄袍,添加代碼:
import _ from 'lodash';
window._ = _; //加載了一個(gè)lodash,又將lodash掛載到了全局的下劃線上面绢要,這樣的話我們在其它地方就可以是使用下劃線這個(gè)變量了。
webpack.common.js添加配置:
entry: {
lodash: './src/lodash.js',
main: './src/index.js'
}
再次打包拗小,運(yùn)行正常重罪。
此時(shí)打包后分成了兩個(gè)js文件,這帶來的好處就是當(dāng)我們修改了業(yè)務(wù)代碼哀九,用戶只需要加載打包后的業(yè)務(wù)js代碼即可剿配,不用加載庫的代碼。
在項(xiàng)目中對代碼公用部分進(jìn)行拆分來提升項(xiàng)目運(yùn)行的速度阅束,也就是Code Splitting呼胚。
2、上面的例子我們是自己做的公用代碼拆分围俘,它不夠智能砸讳。webpack通過它自帶的一些插件可以智能地幫助我們做一些拆分工作。
- 刪除lodash.js文件界牡,修改index.js文件:
import _ from 'lodash';
console.log(_.join(['a', 'b', 'c'])); //a,b,c
- 添加weback.common.js配置:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
entry: {
main: './src/index.js'
},
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}, {
test: /\.jpg$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 2048
}
}
}, {
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}, {
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
}]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new CleanWebpackPlugin()
],
optimization: {
splitChunks: {
chunks: 'all' //意思就是我要幫你去做代碼分割了
}
},
output: {
path: path.resolve(__dirname, '../dist'),
filename: '[name].js'
}
}
此時(shí)執(zhí)行npm run dev-build打包命令簿寂,發(fā)現(xiàn)dist文件夾下面打包了兩個(gè)js文件,一個(gè)是業(yè)務(wù)邏輯js宿亡,還有一個(gè)是類庫js常遂。
這種代碼分割配置適合上面這種同步模塊的分割。
3挽荠、webpack中的代碼分割不僅僅適合上面這種同步模塊的分割克胳。異步的代碼也能進(jìn)行分割,而且不需要添加上面的那種配置就會自動(dòng)進(jìn)行圈匆。
index.js:
function getComponent() {
return import('lodash').then(( { default: _ } ) => {
var element = document.createElement('div');
element.innerHTML = _.join(['z', 't'], '-');
return element;
})
};
getComponent().then( element => {
document.body.appendChild(element);
});
webpack.config.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
entry: {
main: './src/index.js'
},
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}, {
test: /\.jpg$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 2048
}
}
}, {
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}, {
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
}]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new CleanWebpackPlugin()
],
// optimization: {
// splitChunks: {
// chunks: 'all' //意思就是我要幫你去做代碼分割了
// }
// },
output: {
path: path.resolve(__dirname, '../dist'),
filename: '[name].js'
}
}
執(zhí)行打包命令漠另,此時(shí)webpack對于這種異步的代碼也能智能地進(jìn)行分割。
四. SplitChunksPlugin 配置參數(shù)詳解
webpack的代碼分割底層使用了SplitChunksPlugin這個(gè)插件跃赚。
上面打包之后的dist文件夾:
1笆搓、 我們希望把 0.js 改一個(gè)名字
修改index.js文件:
function getComponent() {
//下面的意思就是異步地引入lodash這樣一個(gè)庫,當(dāng)我做代碼分割的時(shí)候纬傲,單獨(dú)給lodash進(jìn)行打包的時(shí)候满败,給它起個(gè)名字叫l(wèi)odash
return import(/* webpackChunkName: "lodash" */'lodash').then(( { default: _ } ) => {
var element = document.createElement('div');
element.innerHTML = _.join(['z', 't'], '-');
return element;
})
};
getComponent().then( element => {
document.body.appendChild(element);
});
- 安裝一個(gè)插件幫我們識別上面那種魔法字符串寫法:cnpm install --save-dev @babel/plugin-syntax-dynamic-import
- .babelrc添加配置:
{
"presets":[
[
"@babel/preset-env",
{
"useBuiltIns": "usage"
}
],
"@babel/preset-react"
],
"plugins": ["@babel/plugin-syntax-dynamic-import"]
}
執(zhí)行 npm run dev-build 命令后,
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
entry: {
main: './src/index.js'
},
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}, {
test: /\.jpg$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 2048
}
}
}, {
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}, {
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
}]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new CleanWebpackPlugin()
],
optimization: {
splitChunks: {
chunks: 'all', //意思就是我要幫你去做代碼分割了
cacheGroups: {
vendors: false,
default: false
}
}
},
output: {
path: path.resolve(__dirname, '../dist'),
filename: '[name].js'
}
}
執(zhí)行打包命令后叹括,dist文件夾下:
2算墨、SplitChunks配置介紹:
splitChunks: {
chunks: "async", //在做代碼分割的時(shí)候,只對異步代碼生效汁雷。
minSize: 30000, //發(fā)現(xiàn)引入的塊大于30000個(gè)字節(jié)的話净嘀,就會幫我們做代碼分割
minChunks: 1, //當(dāng)一個(gè)Chunk被至少用了多少次的時(shí)候报咳,才對他進(jìn)行代碼分割
maxAsyncRequests: 5, //同時(shí)加載的模塊數(shù),最多是5個(gè)
maxInitialRequests: 3, //整個(gè)網(wǎng)站首頁進(jìn)行加載的時(shí)候面粮,或者說入口文件進(jìn)行加載的時(shí)候少孝,入口文件可能會引入其它的js文件继低,入口文件如果做代碼分割熬苍,最多只能分割出3個(gè)。此處一般不修改
automaticNameDelimiter: '~', //組和文件名之間連接的符號
name: true, //打包生成的名字通過cacheGroup設(shè)置有效袁翁。此處一般不變
cacheGroups: { //如果都符合下面?zhèn)z個(gè)組的要求柴底,那么誰的priority值大,就用誰的
vendors: { //vendors組
test: /[\\/]node_modules[\\/]/, //如果是從node_modules引入的
priority: -10
},
default: { //這個(gè)組里面沒有test粱胜,意思就是所有的模塊都符合要求
minChunks: 2,
priority: -20,
reuseExistingChunk: true //如果一個(gè)模塊已經(jīng)被打包了柄驻,再打包就會忽略這個(gè)模塊,直接使用之前使用的那么模塊就可以
}
}
}
對同步的代碼進(jìn)行分割:
splitChunks: {
chunks: "all", //1 同步和異步代碼都會做分割焙压,initial表示對同步代碼做分割
minSize: 30000,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
vendors: { //2
test: /[\\/]node_modules[\\/]/,
priority: -10,
filename: 'vendors.js' //打包后的名字不帶main,只有vendors
},
default: false
}
}
五. Lazy Loading 懶加載鸿脓,Chunk 是什么?
1涯曲、實(shí)現(xiàn)一個(gè)懶加載的行為
index.js:
function getComponent() {
return import(/* webpackChunkName: "lodash" */'lodash').then(( { default: _ } ) => {
var element = document.createElement('div');
element.innerHTML = _.join(['z', 't'], '-');
return element;
})
};
document.addEventListener('click', () => {
getComponent().then( element => {
document.body.appendChild(element);
});
})
webpack.common.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
entry: {
main: './src/index.js'
},
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}, {
test: /\.jpg$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 2048
}
}
}, {
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}, {
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
}]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new CleanWebpackPlugin()
],
optimization: {
splitChunks: {
chunks: 'all', //意思就是我要幫你去做代碼分割了
cacheGroups: {
vendors: false,
default: false
}
}
},
output: {
path: path.resolve(__dirname, '../dist'),
filename: '[name].js'
}
}
執(zhí)行打包命令野哭,此時(shí)控制臺的network只加載了main.js:
再頁面點(diǎn)擊之后,才加載lodash.js文件:
上面這種做法的好處是能夠讓頁面的加載速度更快幻件,只加載該頁面用到的js代碼拨黔。
可以使用async函數(shù)來實(shí)現(xiàn)上面的效果,index.js:
async function getComponent() {
const { default: _ } = await import(/* webpackChunkName: "lodash" */'lodash');
const element = document.createElement('div');
element.innerHTML = _.join(['z', 't'], '-');
return element;
}
document.addEventListener('click', () => {
getComponent().then( element => {
document.body.appendChild(element);
});
})
2绰沥、Chunk
Webpack打包過后生成了幾個(gè)js文件篱蝇,每個(gè)js文件我們都把它叫做一個(gè)Chunk。
六. 打包分析徽曲,Preloading, Prefetching
1零截、打包分析:當(dāng)我們使用Webpack進(jìn)行打包之后,我們可以借助打包分析的工具來對我們的打包文件進(jìn)行一定的分析秃臣,然后看打包是否合理涧衙。
打包分析網(wǎng)址
https://github.com/webpack/analyse
上面網(wǎng)址中的教程:
運(yùn)行:npx webpack --profile --json > stats.json 會在根目錄下生成一個(gè)stats.json,該文件就是對打包過程的一個(gè)描述甜刻。
生成該文件以后绍撞,我們就可以借助一些工具來分析里面的內(nèi)容了:進(jìn)入上面的網(wǎng)址,點(diǎn)擊首頁的鏈接得院,就會進(jìn)入一個(gè)頁面傻铣,進(jìn)入之后將stats.json文件上傳上去就會幫我們進(jìn)行打包分析了。
除了這個(gè)分析方法之外祥绞,還有很多別的方法非洲,可以去webpack的文檔中查看鸭限。
七. CSS 文件的代碼分割
index.js
import './style.css';
- 在src目錄下創(chuàng)建style.css文件,添加代碼:
body {
background-color: green;
}
執(zhí)行打包后發(fā)現(xiàn)雖然dist目錄下沒有css文件两踏,但是頁面卻又效果败京。這是因?yàn)閣ebpack在做打包的時(shí)候會把Css文件直接打包到j(luò)s里面。
1梦染、希望實(shí)現(xiàn)Css文件單獨(dú)打包進(jìn)dist文件夾下面赡麦。
借助 MiniCssExtractPlugin 這個(gè)插件來進(jìn)來css代碼分割。
- 安裝:
npm install --save-dev mini-css-extract-plugin
- webpack.common.js添加配置:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: {
main: './src/index.js'
},
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}, {
test: /\.jpg$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
limit: 2048
}
}
}, {
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader, //1
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}, {
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'] //2
}]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({})
],
optimization: {
splitChunks: {
chunks: 'all'
}
},
output: {
path: path.resolve(__dirname, '../dist'),
filename: '[name].js'
}
}
此時(shí)執(zhí)行打包命令后發(fā)現(xiàn)dist目錄下成功打包出了css文件帕识。
2泛粹、做Css代碼的壓縮
const TerserJSPlugin = require('terser-webpack-plugin');
// 安裝這兩個(gè)插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
// 配置壓縮
optimization: {
minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[id].css',
}),
],
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
],
},
};
今天回顧下webpack的相關(guān)知識點(diǎn),發(fā)現(xiàn)了這篇文章寫的相當(dāng)詳細(xì)肮疗,順手摘過來記錄下晶姊,整體看下來的大概1個(gè)小時(shí)左右,備份下
原文鏈接:https://blog.csdn.net/sinat_40697723/article/details/95349783