在原來的文章中我們把打包的東西都存放到了dist目錄下,并沒有進行分類存儲,亂成一團,現(xiàn)在我們需要處理一下打包的路徑,讓打包后的目錄看起來更加優(yōu)雅。
代碼準備
我們先建立起這樣一個目錄結(jié)構(gòu)
├── node_modules
├── src
| ├── assets
| └── css
| └── index.css
| └── img
| └── noding.jpg
| └── js
| └── index.js
| └── index.html
├── .babelrc
├── package-lock.json
├── package.json
├── webpack.config.js
└── dist
我們在src/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="box"></div>
<i class="fa fa-bath" aria-hidden="true"></i>
<i class="fa fa-envelope-open" aria-hidden="true"></i>
<i class="fa fa-microchip" aria-hidden="true"></i>
<i class="fa fa-user-circle-o" aria-hidden="true"></i>
</body>
</html>
接下來在assets/js/index.js中輸入下面代碼
// 引入icon圖標字體
import "font-awesome/css/font-awesome.css"
// 引入圖片
import imgSrc from '../img/nodeing.jpg'
// 把圖片插入到html文件中
document.getElementById("box").innerHTML = '<img src="'+imgSrc+'" />'
然后去webpack.config.js中去修改一下入口文件位置
接下來,我們?nèi)y試一下打包效果
npm run dev
所有文件都打包到dist目錄下,接下來,我們需要去優(yōu)化webpack配置,讓打包出來的文件不那么混亂
webpack配置
把js文件分類
我們希望打包出來的文件也像src目錄一樣能夠分門別類的存放,首先,我們把本來的js單獨放進assets/js文件夾下,這時我們需要修改webpack配置文件,把輸出目錄改掉
output: {
path: path.resolve(__dirname, 'dist/assets'),
//上面這個path代表webpack默認會把所有的文件都打包進assets文件夾中
//上面這個是一個所有打包文件輸出位置的公用路徑
filename: 'js/my-first-webpack.bundle.js'
},
運行npm run dev 查看效果,發(fā)現(xiàn)dist目錄下多出了一個assets文件,并且已經(jīng)建立好了js文件夾,
app.js也被放進來了,但是,原來打包出來的那些文件還在,這時候,dist目錄就更亂了,我們希望每次打包生成的文件都是最新的,得手動去刪除上次打包出來的文件,像這種手動刪除dist目錄的操作,
我們可以交給webpack相關插件來完成,這個插件叫clean-webpack-plugin
安裝clean-webpack-plugin插件
npm install clean-webpack-plugin --save
修改webpack配置文件,引入clean-webpack-plugin插件
webpack.config.js
const path = require("path")
const HtmlWebpackPlugin = require("html-webpack-plugin")
// 引入clean-webpack-plugin插件*************************************
const CleanWebpackPlugin = require("clean-webpack-plugin")
module.exports = {
entry: "./src/assets/js/index.js",
output: {
path: path.resolve(__dirname, 'dist/assets'),
filename: 'js/app.js'
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html'
}),
// 使用插件,設置需要清除的目錄*******************************************
new CleanWebpackPlugin(['dist'])
],
devServer: {
open: true
},
module: {
rules: [
{
test: /\.js$/,
use:['babel-loader'],
exclude: path.resolve(__dirname, 'node_modules')
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
// 處理文字
{
test: /\.(eot|svg|ttf|woff|woff2)$/,
use: 'file-loader'
},
{
test: /\.(jpg|png|gif|webp|bmp)$/,
use: [{
loader: 'url-loader',
options: {
limit: 10240
}
}]
}
]
}
}
這個時候你會發(fā)現(xiàn),每次運行npm run dev,這個插件就會先把上次的dist目錄刪除,然后再新建一個dist目錄,把新打包的文件放里面
以上步驟,我們把js文件放到了assets/js目錄下,但隨之而來的就是,所有的文件都被打包到assets這個目錄下了,我們不希望index.html這個文件也被打包進assets目錄下,而是像在src目錄下一樣,它們應該是同級的,所以我們需要修改一下 index.html被輸出的路徑
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
// 這里表示往外回退一層
filename: '../index.html'
}),
new CleanWebpackPlugin(['dist'])
],
把字體文件分類
修改webpack配置文件,給字體文件增加輸出規(guī)則
// 處理文字
{
test: /\.(eot|svg|ttf|woff|woff2)$/,
use: [{
loader: "file-loader",
options: {
name: 'fonts/[name]_[hash:4].[ext]'
}
}]
},
其中,[name]、[hash]、[ext]都是可變的,name表示原來的文件名字,hash表示生成的hash值,
hash:4表示可以限定hash字符串的位數(shù),ext表示原來文件的擴展名(文件后綴)
把圖片文件分類
修改webpack配置文件,給圖片文件增加輸出規(guī)則
{
test: /\.(jpg|png|gif|webp|bmp)$/,
use: [{
loader: 'url-loader',
options: {
limit: 10240,
name: 'img/[name]_[hash:4].[ext]'
}
}]
}
因為,ulr-loader是對file-loader的封裝,其中的[name]、[hash]、[ext]表示的意思和file-loader中表示的意思是一樣的
把css文件分類
css文件是打包進js文件的,如果需要單獨打包出來,需要安裝另一個插件,extract-text-webpack-plugin
npm install extract-text-webpack-plugin --save-dev
這個插件的使用規(guī)則和其他plugin差不多,首先需要引入插件
webpack.config.js
var ExtractTextWebpackPlugin = require("extract-text-webpack-plugin")
其次,是需要實例化對象,并且傳入配置項,然后將實例化出來的這個對象加入到plugins選項中
var extractcss = new ExtractTextWebpackPlugin({
//打包輸出的路徑↓
filename: 'css/index.css'
})
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: './index.html'
}),
new CleanWebpackPlugin(['dist']),
// 這?是前面實例化得到的對象↓
extractcss
],
最后,需要修改css輸出規(guī)則
{
test: /\.css$/,
use: extractcss.extract({
fallback:'style-loader',
use:['css-loader']
})
},
修正url地址
先分析一下webpack.config.js中的devServer
devServer:{
//當服務器啟動的時候,自動幫助我們打開瀏覽器
open: true,
//服務器啟動的端口地址
port: 8090,
//訪問的本地目錄是哪個
contentBase: './dist'
}
上面所有的webpack配置好了之后,我們在終端用npm start啟動服務時,發(fā)現(xiàn)根本訪問不了dist文件,因為
1.當服務啟動的時候,CleanWebpackPlugin這個插件的配置,就會把dist目錄刪除
2.而npm start并不會再次生成dist,但是內(nèi)存中是有dist的,為什么訪問不了?
解決辦法
當服務啟動的時候,wenbpack-dev-server會把資源打包到內(nèi)存
npm start會優(yōu)先訪問內(nèi)存中output.path:path.resolve(__dirname,'dist/assets')設置的輸出路徑,而為什么訪問不了內(nèi)存中的資源?
舉例:當我們在瀏覽器中輸入http://localhost:8090/ ,訪問的是內(nèi)存中的dist/assets
當我們在瀏覽器中輸入http://localhost:8090/js/my-first-webpack.bundle.js,訪問的是內(nèi)存中的dist/assets/js/my-first-webpack.bundle.js.js
這樣的話,我們發(fā)現(xiàn),我們只可以訪問到assets目錄下的內(nèi)容,卻訪問不到和assets同級的內(nèi)容
當我們把各種類型的文件分類好以后,運行打包出來的index.html,發(fā)現(xiàn)圖片、字體、css等的路徑是不正確的,沒辦法正常工作,這個時候,我們需要用output配置項下的 publicPath來修正一下
output: {
path: path.resolve(__dirname, 'dist/assets'),
filename: 'js/app.js',
publicPath: 'assets/'
},
其中,publicPath設置的是所有靜態(tài)資源的基礎目錄,要快速理解它,可以記住下面的公式:
靜態(tài)資源最終訪問路徑 = output.publicPath + 資源loader或插件等配置路徑
舉例說明,我們在各種loader中設置的路徑如下:
// css輸出路徑
name: 'css/[name]_[hash:4].[ext]'
// 圖片輸出路徑
name: 'img/[name]_[hash:4].[ext]'
// 字體文件輸出路徑
name: 'fonts/[name]_[hash:4].[ext]'
// js文件輸出路徑
filename: 'js/app.js'
設置的output.publicPath為 "assets/", 那么最終的訪問路徑就是output.publicPath+loader設置的路徑,例如圖片路徑就會是 assets/img/[name]_[hash:4].[ext]
devServer.publicPath這個選項是webpack-dev-server服務器的訪問路徑,我們知道當webpack-dev-server啟動后,會把資源打包到內(nèi)存,你不需要關注它在內(nèi)存中的什么地方,你可以理解為打包后的資源也被輸出了,只是輸出到內(nèi)存中,你無法在硬盤上看到而已,但是它提供了一個地址來供我們訪問,devServer.publicPath就是用來設置訪問內(nèi)存中被打包出來的資源地址的,舉例說明:
output: {
path: path.resolve(__dirname, 'dist/assets'),
filename: 'js/my-first-webpack.bundle.js',
},
devServer: {
open: true,
publicPath: '/aa'
},
其他配置省略...
像上面這種配置,當服務器啟動的時候,如果想訪問到內(nèi)存中的my-first-webpack.bundle.js,那么需要通過http://localhost:8090/aa/js/my-first-webpack.bundle.js
當前的完整的webpack配置是這樣的
const path = require("path")
const HtmlWebpackPlugin = require("html-webpack-plugin")
const CleanWebpackPlugin = require("clean-webpack-plugin")
module.exports = {
entry: "./src/assets/js/index.js",
output: {
path: path.resolve(__dirname, 'dist/assets'),
filename: 'js/my-first-webpack.bundle.js',
//資源的基礎路徑,設置什么值就會在原來的路徑前面加上這個值
//注意:不是在打包輸出的文件夾前面加,而是資源在最終訪問的時候加
publicPath: 'assets/'
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: '../index.html'
}),
new CleanWebpackPlugin(['dist'])
],
devServer: {
open: true,
containtBase: 'src/'
publicPath: '/aa'
},
module: {
rules: [
{
test: /\.js$/,
use:['babel-loader'],
exclude: path.resolve(__dirname, 'node_modules')
},
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
name: 'css/[name]_[hash:4].[ext]'
}
}
]
},
// 處理?字
{
test: /\.(eot|svg|ttf|woff|woff2)$/,
use: [{
loader: "file-loader",
options: {
name: 'fonts/[name]_[hash:4].[ext]'
}
}]
},
{
test: /\.(jpg|png|gif|webp|bmp)$/,
use: [{
loader: 'url-loader',
options: {
limit: 10240,
name: 'img/[name].[ext]'
}
}]
}
]
}
}
我們來解讀幾條關鍵的配置:
1.output.path: path.resolve(__dirname, 'dist/assets'), 這條配置是打包后資源輸出的目錄,
同樣的當webpack-dev-server啟動后,資源會打包到內(nèi)存,用不太嚴謹?shù)ㄋ椎脑拋碚f你可以認為所有文件被打包到這個目錄,只是這個目錄放在內(nèi)存而不是輸出到硬盤,你訪問這個目錄里面的文件的時候,路徑和打包到硬盤是看到的路徑一樣,例如:在硬盤上 dist/assets/js/app.js, 在內(nèi)存中也通過這種目錄結(jié)構(gòu)訪問就是了
2.output.publicPath: 'assets/',是靜態(tài)資源的基礎路徑
3.devServer.publicPath: '/aa', 這個配置是服務器訪問內(nèi)存的虛擬路徑, http://localhost:8080/aa 這個地址,你可以認為是映射到了output.path設置的目錄,不太嚴謹?shù)ㄋ椎恼f,你可以認為http://localhost:8080/aa這個地址指向了這個目錄(dist/assets/),那么你在http://localhost:8080/aa后面加 /js/app.js 就相當于訪問 dist/assets/js/app.js,所以啟動以后通過http://localhost:8080/aa/js/app.js是可以訪問到這個js文件的
4.devServer.contentBase設置的是本地目錄(在內(nèi)存中找不到的時候去硬盤上找)
5.filename: '../index.html',這個在HtmlWepackPlugin中的配置,設置了被打包出來的html位置,../表示往上一層返回,那就意味著,最終打包出來的結(jié)構(gòu)是這樣的:
├── dist
| ├── assets
| ├── css
| ├── img
| ├── js
| ├── index.html
|
我們通過http://localhost:8080/aa (devServer.publicPath屬性定義的映射)能訪問到dist/assets目錄,也可以訪問該目錄下層級更深的文件,只需要在http://localhost:8080/aa 后面加文件路徑就可以了,但是卻永遠無法返回到上層目錄去訪問到index.html
當訪問不到內(nèi)存中的index.html?件,webpack-dev-server就會去訪問本地的路徑,這個本地的路徑就是devServer.containtBase: 'src/'這個選項設置的,所以npm start的時候,直接打開的是項目下的src目錄
好了,問題原因已經(jīng)找出來了,接下來我們需要修改以下webpack配置,具體的思路就是
- 輸出目錄往上挪動一層 output.path 設置成 path.resolve(__dirname, 'dist'),
- 在每個loader上加深一層 js加深一層是這樣的 assets/js/app.js 圖片加深一層是這樣的 assets/img/...,其他,同理
- ../index.html 改為當前目錄 ./index.html
- output.publicPath的值設置成和devServer.publickPath的值一樣,不然靜態(tài)資源的訪問地址會出錯,返回404
這樣做,原來打包輸出的目錄變成了dist目錄,而不是原來的dist/assets目錄, 那么http://localhost:8090/aa 這個地址指向的內(nèi)存虛擬目錄就是 dist目錄,那么通過http://localhost:8090/aa/index.html 就可以訪問到內(nèi)容了
具體完整配置如下:
const path = require("path")
const HtmlWebpackPlugin = require("html-webpack-plugin")
const CleanWebpackPlugin = require("clean-webpack-plugin")
module.exports = {
entry: "./src/assets/js/index.js",
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'assets/js/my-first-webpack.bundle.js',
publicPath: '/'
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: './index.html'
}),
new CleanWebpackPlugin(['dist'])
],
devServer: {
open: true,
publicPath: '/'
},
module: {
rules: [
{
test: /\.js$/,
use:['babel-loader'],
exclude: path.resolve(__dirname, 'node_modules')
},
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
name: 'assets/css/[name]_[hash:4].[ext]'
}
}
]
},
// 處理文字
{
test: /\.(eot|svg|ttf|woff|woff2)$/,
use: [{
loader: "file-loader",
options: {
name: 'assets/fonts/[name]_[hash:4].[ext]'
}
}]
},
{
test: /\.(jpg|png|gif|webp|bmp)$/,
use: [{
loader: 'url-loader',
options: {
limit: 10240,
name: 'assets/img/[name].[ext]'
}
}]
}
]
}
}
通常output.publicPath和devServer.publicPath設置成 "/",這樣我們就可以通過 http://localhost:8090/ 來訪問打包出來的index.html文件了。