你曾多少次被webapck's publicPath
的配置絆倒?老實說妄均,在每次開始一個新項目的時候,我總是遇到這個問題境肾,并且花費很長時間去探索它到底是怎么工作的徽千。我閱讀了官方文檔,但并沒有什么用珍手。
所以办铡,這里我決定根據(jù)我的經(jīng)驗和理解去揭開publicPath
的神秘面紗。如果對你有幫助琳要,歡迎反饋意見寡具。
當你的publicPath配置錯誤時,通常會遇到這2個問題:
- webpack-dev-server:
live-reload
(瀏覽器自動刷新)機制沒有正常執(zhí)行稚补,比如說童叠,瀏覽器不會自動重新加載,或者加載的代碼不是最新的课幕。 - 生成的css文件厦坛,圖片、字體路徑發(fā)生報錯乍惊,打斷編譯打包的過程杜秸。
讓我們來從一個基礎的配置去分析這件事。我們有一個文件結構润绎,如下顯示:
appMain [Project Folder]
|-- src
|-- index.js
|-- index.html
|-- webpack.config.js
|-- package.json
|-- dist
// 極簡配置的webpack
module.exports = {
entry: './src/index.js',
output: {
filename: './dist/bundle.js'
}
};
// index.html中引入生成的bundle.js
<script src="./dist/bundle.js"></script>
如果我們只運行 webpack
命令撬碟,會默認執(zhí)行webpack.config.js
, 生產(chǎn)打包文件到 dist 文件夾下。
此外莉撇,如果你使用了 webpack-dev-server
啟動呢蛤,live-reload
就開始生效了,即是棍郎,任何在js源文件 [index.js] 中的修改都會立即在瀏覽器中生效其障,不需要手動刷新。
我們可以通過console.log()
打印一些消息來測試這個功能涂佃。
注意:webpack-dev-server 啟動的服務励翼,使用的并不是真實生成的包文件蜈敢,它只是監(jiān)聽你的源代碼,當它們發(fā)生變化時去重新編譯抚笔。這個修改的包是直接從內存中讀取的扶认。??
現(xiàn)在讓我們加入配置path
到webpack output 對象中,來代替在output.filename
中使用完整路徑的配置殊橙。
const resolve = require('path').resolve;
module.exports = {
entry: './src/index.js',
output: {
// seperated path and filename of generated output
path: resolve('dist'),
filename: 'bundle.js'
}
};
請注意output.path
需要使用絕對路徑而不是相對路徑辐宾,否則webpack會報錯。所以膨蛮,我們使用了path
模塊的resolve
方法叠纹。
報錯信息: Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema. \- configuration.output.path: The provided value "./dist" is not an absolute path!
在這種情況下,webpack會生成錯誤的輸出敞葛,比如有bundle.js文件誉察,但webpack-dev-server
的live-reload
會突然停止執(zhí)行。并且惹谐,關于源文件[index.js]的任何修改持偏,無論怎么刷新瀏覽器都無法顯示,盡管終端顯示源文件已經(jīng)編譯成功氨肌。
現(xiàn)在鸿秆,如果你停止你的webpack-dev-server 服務,再次執(zhí)行webpack命令怎囚,然后再重新啟動 webpack-dev-server卿叽,修改的自動更新又正常了,但是我們不能每次都這樣做吧恳守,真的神煩考婴。??
所以,是哪個環(huán)節(jié)出錯了催烘?
主要是因為當path
的未配置時沥阱,webpack-dev-config 會把它的值默認為項目的根目錄,即 ./
const resolve = require('path').resolve;
module.exports = {
entry: './src/index.js',
output: {
// 如果path未配置颗圣,默認值是 './'
path: resolve('./'),
filename: './dist/bundle.js'
}
};
所以喳钟,可以正常工作。
但當我們配置為真實的路徑在岂,比如reslove('dist')
, webpack仍然在./
位置編譯生成輸出文件。
你可以直接在瀏覽器中訪問服務的URL地址驗證這一點
http://localhost:8080/dist/bundle.js
// 總是指向我們運行webpack命令時生成的舊代碼
http://localhost:8080/bundle.js
// 總是指向最新編譯后的代碼蛮寂,在src變化后會立即重新加載
注意:這兩個打包的文件并不是完全相同的蔽午,后者因為使用dev-server會注入一些額外的代碼
所以,這里live-reload
和舊代碼
導致的問題是酬蹋,我們使用了前者打包的 bundle.js及老,卻在index.html
使用的是 webpack 命令生成的路徑抽莱。
任何代碼的改變,webpack-dev-server 會生成一個包骄恶,但地址是不同的食铐。
解決這個問題,只需要改變一下index.html
中的script:src
屬性僧鲁,一切又能正常運行了虐呻。
神速!我們理解并修復了這個問題寞秃。
進一步來說斟叼,我們使用 webpack 的publicPath
去配置 webpack-dev-server 生成的打包文件的位置,是個虛擬位置
, 而不是真實的文件系統(tǒng)春寿。
這個虛擬位置可以用來在 index.html 中定位引入生成的文件朗涩。
const resolve = require('path').resolve;
module.exports = {
entry: './src/index.js',
output: {
path: resolve('dist'),
filename: 'bundle.js',
// Add vitual path for generating the bundle files
publicPath: 'some-virtual-location'
}
};
// 更新script:src到新的虛擬位置(Virtual-Path)
<script src="./some-virtual-location/bundle.js"></script>
這樣,我們就可以在本地開發(fā)中模擬真實服務器部署環(huán)境或者CDN绑改。
一些開發(fā)者更喜歡命名為 assets
, public
, dist
, /
等等谢床,這完全看你喜歡如何維護生成的文件結構和命名方式。
底線就是厘线,index.html 中引入的script:src
的值必須與 publicPath 的配置保持一致识腿。
publicPath對CSS的其他影響 -- 圖片、字體的路徑等等
在生成最終的styles時皆的,webpack默認會把 publicPath
的配置添加到 圖片的URL覆履,字體的路徑上。
示例费薄,我添加2個文件到當前的項目中:
appMain
|-- src
|-- index.js
|-- main.css [Source CSS/SCSS file]
|-- background-image.jpg
|-- index.html
|-- webpack.config.js
|-- package.json
|-- dist
main.css (使用相對路徑引入背景圖片)
.main {
background-image: url("background.jpg");
}
我們使用了 css/scss 文件和圖片硝全,所以也要更新webpack.config
配置,添加相應的 loaders 或 plugin楞抡。
webpack.config.js
const resolve = require('path').resolve;
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: resolve('dist'),
filename: 'bundle.js',
publicPath: 'some-virtual-location/'
},
// 添加 CSS 和 Style Loader
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: 'css-loader'
})
},
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]'
}
}
]
}
]
},
// 生成單獨的樣式文件
plugins: [
new ExtractTextPlugin('styles.css')
]
};
當你運行webpack
命令時伟众,會觸發(fā)下面行為:
-
background-image.jpg
文件會被file-loader
拷貝到dist
文件夾中。 -
styles.css
會被插件 ExtractTextPlugin 根據(jù)main.css
生成到dist
文件夾中召廷。 - 生成的css文件中
background-image: url
會被自動添加publicPath
的配置凳厢。
.main {
background-image: url(some-virtual-location/background.jpg);
}
這個規(guī)則同樣適用于其他的靜態(tài)資源,比如fonts.
所以正確的配置publicPath
非常重要竞慢,Webpack 的 Loaders 和 Plugins 會使用這個配置先紫。
希望這篇文章能讓你深入的理解神秘的publicPath
是怎么工作的,怎樣正確的使用它筹煮。
歡迎在評論區(qū)寫下你的想法和意見遮精,我們可以進一步的討論。??????