網站子目錄部署VUE webpack 打包資源文件路徑的正確引用方式


webpack 是目前使用最為火熱的打包工具,各大知名的框架類庫都用其打包,國內使用最近也火熱起來。它在單頁應用和類庫打包上幫助許多人從代碼管理中解脫了出來,成為了當下風靡一時的打包工具。

但是坑也很多,比如說圖片,字體等文件的路徑。

剛開始用webpack的同學很容易掉進圖片打包這個坑里,比如打包出來的圖片地址不對或者有的圖片並不能打包進我們的目標文件夾里。明明在開發階段都是好好的,一但發布到線上,就出現各種404,

一般來說,webpack打包的SPA程序,發布到網站的根目錄下都不會出現太多問題,但是發布到網站的子目下,就會出現各種資源文件找不到的情況。

這種文件,我在以下這個知乎的帖子里曾經做過詳細的回答

知乎用戶:vuejs怎么在服務器部署?

似乎所有問題都解決了,但有一個問題沒有解決,就是如果我在css里引了了圖片資源,webpack並不能很好的處理這里面的資源路徑

比如我把spa部署在 https://www.wx2share.com/m/ 
“m”文件夾是網站下的一個子目錄,

如果我在開發的時候,寫了如下css代碼

.content { background: url('/static/img/1.jpg') } 

首先,我們修改(tips:我的配置文件是通過vue-cli生成的)

build: { env: require('./prod.env'), index: path.resolve(__dirname, '../dist/index.html'), assetsRoot: path.resolve(__dirname, '../dist'), assetsSubDirectory: 'static', assetsPublicPath: '/m/', //這里指定publicPath 的路徑為我子目錄文件名,一般默認為'/' productionSourceMap: true, }, 

webpack publicPath 參數是用來,幫助你為項目中的所有資源指定一個基礎路徑,一但設定值以后,所有代碼中通過requrie 或import 方式引入的資源文件,在build以后,都回指向類似 '/m/static/xxx.xx'

比如:

logo: require('@/assets/v.png') <img :src="logo"> 

編譯以后,

<div class="logo-warp"> <img src="/m/static/img/logo_small.f457a3f.png"> </div> 

可以看到,webpack 已經正確的把資源文件的路徑里 加上了/m ,保證了資源文件引用正確,這里順便提一點,publicpath 還可以設置在cdn的url

如果 publicPath: 'https://cdn.youdomin.com/' 這樣編譯以后,src=''https://cdn.youdomin.com//static/img/logo_small.f457a3f.png 

這樣你只要把static整個文件夾托管的你的cdn上就可以了非常方便。但是publicPath 一定用絕對路徑,絕對路徑,絕對路徑(重要的事說3遍) 千萬不要用相對路徑,如果你把publicPath設為'./',哪你啟用路由以后,https://www.wx2share.com/m/ 這樣的url進入程序的,不會有問題,因為這個時候,static文件正好在當前目錄下,但是當你類似用這樣的網址來訪問的時候 ,https://www.wx2share.com/m/sh... 資源文件又找不到了,因為這個時候./的指向的目錄是

/m/show/static/ 很明顯你的資源文件全在 /m/static下,所以又404了。

上面的方法基本解決了大部分的問題,唯一不能解決的就是文章開頭提到的,css中引入的資源文件的問題了

通過publicpath的設置,並不到改變css中引用資源文件的路徑,上面實例代碼中的css編譯后,還是

.content { background: url('/static/img/1.jpg') } 

是乎webpack 並不處理css中的文件路徑,這樣的結果就是發布以后頁面上所有通過css引入的資源文件全部不能正常顯示了。前段時間我采用了一個簡單又粗爆的方法來解決這個問題,哪就是,絕對不用css引入任何文件。當然你也可以手動修改webpack編譯過的文件,把通過查找替換 把/static 替換為/m/static 如果你不嫌累的慌。

最近又開始了一個新的SPA應用,引入了一個第三方css,里面好多通過css引入的圖片文件 ,讓我不得不下定決心來解決這個問題了

TIPS: 以下內容的配置文件修改全部是基於由vue-cli生成的配置文件,如果你是用自己寫的配置文件請注意區別!

我首先想到,如果開發的時候我就是指定在“m/”子目錄下,不就能解決大部分問題了,這樣和線上的根目錄文件對應了,buid以后就不會有哪么多問題 ,

第一步:修改dev模式上的publicPath

dev: { env: require('./dev.env'), port: 8082, autoOpenBrowser: true, assetsSubDirectory: 'static', assetsPublicPath: '/m/', //把dev模式下的publicPath也設為 m proxyTable: {}, cssSourceMap: false } yarn run dev 

以后,訪問 http://localhost:8082/m/ 結果好么,直接給我來了個 can not get /m/

但是如果我直接訪問http://localhost:8082/m/index.html 是可以的,而且功能基本正常,是就vue的router不起作用了,在瀏覽器地址欄里直接敲入 http://loalhost:8082/m/view/1 然后回車,這樣的地址就全部404了,看來是dev sever沒有把,所有鏈接從新定向到 /m/index.html上,但是publicPath設置是正確的了,現在要解決的問題就是,無論在瀏覽器地址欄里輸入什么網址,都讓它重定向到 /m/index.html 就可以解決所有問題了,

webpack 的dev sever 是什么,用什么來實現的,能過查看buid/dev-server.js

發現是用express 來實現web server,通過加載webpack-dev-middleware 來實現實時編譯,所有請求都轉發給它了,所以,在node_modules下找到它的源代碼,看了老半天,看不出所以然來,只能debug了,看它到底是怎么運作的,電腦上沒有調試的工具,只好,通過打log的方法來調試了。

function webpackDevMiddleware(req, res, next) { function goNext() { if(!context.options.serverSideRender) return next(); return new Promise(function(resolve) { shared.ready(function() { res.locals.webpackStats = context.webpackStats; resolve(next()); }, req); }); } if(req.method !== "GET") { return goNext(); } var filename = getFilenameFromUrl(context.options.publicPath, context.compiler, req.url); console.log(filename) //關鍵就是這里,只有filename不等於 false的時候才進入真正的處理階段 if(filename === false) return goNext(); //下面還有好多代碼,不粘貼了, 既然這里返回 false 

我們進入 getFileNameFromUrl 這個函數看看,為什么會false

function getFilenameFromUrl(publicPath, outputPath, url) { var filename; console.log(publicPath, outputPath, url) //我在這里打了個log // localPrefix is the folder our bundle should be in var localPrefix = urlParse(publicPath || "/", false, true); var urlObject = urlParse(url); // publicPath has the hostname that is not the same as request url's, should fail if(localPrefix.hostname !== null && urlObject.hostname !== null && localPrefix.hostname !== urlObject.hostname) { return false; } // publicPath is not in url, so it should fail if(publicPath && localPrefix.hostname === urlObject.hostname && url.indexOf(publicPath) !== 0) { return false; //就這里return false了 } // strip localPrefix from the start of url if(urlObject.pathname.indexOf(localPrefix.pathname) === 0) { filename = urlObject.pathname.substr(localPrefix.pathname.length); } if(!urlObject.hostname && localPrefix.hostname && url.indexOf(localPrefix.path) !== 0) { return false; } // and if not match, use outputPath as filename return querystring.unescape(filename ? pathJoin(outputPath, filename) : outputPath); } 

看上面代碼,我在進入函數的頭部打了一個log,看看傳入的參數到底是什么

當我訪問 http://localhost:8082/m/ 的時候 控制台里輸出

發現 publicPath, outputPath, url,三個參數的值分別為:

/m/ F:workspacewx2share-pwadist /index.html

終於發現

if(publicPath && localPrefix.hostname === urlObject.hostname && url.indexOf(publicPath) !== 0) {

    return false; //就這里return false了 }

url.index.of(publicPath) !== 0 這一個條件成立了 相當於 '/index.html/'.indexOf('/m/') 肯定不會===0 啊,

但是這個‘/index.html' 這個參數的值哪里來的,回到上一段代碼中,發現是req.url里傳過來的,

但我明明訪問的是/m/ 哪,req.url 應該等於 '/m/'啊,這是什么時候給重定向的呢,看來在這個之前,已經有過一次重定向了

回到dev-server.js文件,發現在,use devMiddlewarre 之前,還引入了一個connect-history-api-fallback的中間件,看來唯一能重定向的地方只有這里了,

app.use(require('connect-history-api-fallback')()); // 服務器部署 webpack 打包的靜態資源 app.use(devMiddleware); // 使用熱更新, 如果編譯出現錯誤會實時展示編譯錯誤 app.use(hotMiddleware); 

打開connect-history-api-fallback的代碼,終於發現了

rewriteTarget = options.index || '/index.html'; logger('Rewriting', req.method, req.url, 'to', rewriteTarget); req.url = rewriteTarget; next(); 這個的代碼,當你不作配置的時候,默認重定向到根目錄的/index.html上,找到原因了,修改就簡單了 // 處理 history API 的回退情況(如果在線上環境中,也需要服務器做相應處理) app.use(require('connect-history-api-fallback')({ index: '/m/index.html' })); 

把options.index的值設為 /m/index.html 不就可以了吧,這樣所有的請求,都會轉發到/m/index.html了

再次打開http://localhost:8082/m/ 一切全部正常了,build以后,發現在線上,也完全正常,再也不會找不到css中引入的資源文件了

一個重點差點忘記提了

就是現在寫css代碼里,引入static文件中的文件,直接要寫加上publicPath的路徑,

就是原來寫成

.content { background: url('/static/img/1.jpg') } 

要在編碼階段全部寫成

 
.content { background: url('/m/static/img/1.jpg') //直接帶上發布是的絕對路徑
}
轉載自:https://segmentfault.com/a/1190000012135787}



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM