Vue CLI3搭建的項(xiàng)目中路徑相關(guān)問(wèn)題的解決
這是開(kāi)頭
最近在試水 Vue CLI 3,并且嘗試配置一個(gè)多頁(yè)面(多應(yīng)用)項(xiàng)目出來(lái),期間又遇到各種路徑問(wèn)題,于是...于是有了下面的嘮叨。
以下都是基于 Vue CLI 3 來(lái)舉例說(shuō)明的,使用 2.x 版本的其實(shí)也類(lèi)似
首先,參考 官方文檔對(duì)靜態(tài)資源處理的說(shuō)明,并通過(guò)自己的實(shí)踐,可以總結(jié)出以下內(nèi)容
靜態(tài)資源可以通過(guò)兩種方式進(jìn)行處理:
1、以下情況下,資源不會(huì)被 webpack 處理,而是被直接拷貝:
- 放置在 public 目錄下,即使未被使用。
- 通過(guò)絕對(duì)路徑被引用,即以 / 開(kāi)頭的路徑。
2、以下情況下,資源會(huì)被 webpack 處理(URL的resolve、minify、uglify、轉(zhuǎn) base64 等):
- 使用 JavaScript 導(dǎo)入。
- 在 template/CSS 中通過(guò)相對(duì)路徑(即以 . 開(kāi)頭或直接以文件(夾)名開(kāi)頭)被引用。
- URL 以 ~ 開(kāi)頭,其后的任何內(nèi)容都會(huì)作為一個(gè)模塊請(qǐng)求被解析。
- URL 以 @ 開(kāi)頭,它也會(huì)作為一個(gè)模塊請(qǐng)求被解析(@ 是在 webpack 設(shè)置的 alias)。
我們應(yīng)該根據(jù)實(shí)際情況去選擇我們要引用的資源是否要被處理,然后用對(duì)應(yīng)的、正確的方式去引用它們以達(dá)到目的。以下對(duì)使用絕對(duì)路徑和相對(duì)路徑的方法和注意事項(xiàng)進(jìn)行描述。
使用絕對(duì)路徑
默認(rèn)情況下,Vue CLI 會(huì)假設(shè)你的應(yīng)用是被部署在一個(gè)域名的根路徑上(對(duì)應(yīng)選項(xiàng) baseUrl: '/'
),例如 https://www.my-app.com/
。如果應(yīng)用被部署在一個(gè)子路徑上,你就需要用這個(gè)選項(xiàng)指定這個(gè)子路徑。例如,如果你的應(yīng)用被部署在 https://www.my-app.com/my-app/,則設(shè)置 baseUrl 為 /my-app/。正因?yàn)橐陨系目赡芮闆r,我們應(yīng)該在打算引用純靜態(tài)資源(那些不被webpack處理的資源,一般就是 public 目錄下的資源)的時(shí)候,都確保使用 baseUrl 作為 URL 的開(kāi)頭,以下列舉在不同文件中配合 baseUrl 選項(xiàng)寫(xiě)絕對(duì)路徑的使用方法和注意事項(xiàng):
在入口html文件中使用
我們可以使用lodash template 語(yǔ)法插入 baseUrl:
<link rel="icon" href="<%= BASE_URL %>favicon.ico" rel="external nofollow" >
在 *.vue 中使用
我們可以通過(guò) Vue CLI 提供的客戶端環(huán)境變量 process.env.BASE_URL 來(lái)獲取 baseUrl:
/* 在需要的組件中定義 baseUrl,然后在 <template> 下使用 */ <template> <div id="app"> <img :src="imgUrl"> <img :src="`${baseUrl}imgs/my_image.png`"> </div> </template> <script> export default { name: 'App', data() { return { baseUrl: process.env.BASE_URL, isBigImg: Math.random() > 0.5 } } computed: { // 動(dòng)態(tài)地獲取不同的靜態(tài)資源 imgUrl() { if (this.isBigImg) { return `${baseUrl}imgs/my_image_big.png` } else { return `${baseUrl}imgs/my_image.png` } } } }; </script> /* 個(gè)人建議可以在全局定義,減去在每個(gè)組件內(nèi)定義的麻煩 Vue.prototype.$baseUrl = process.env.BASE_URL // 在 <template> 下使用 <img :src="`${$baseUrl}imgs/my_image.png`">
在其他 js 模塊中使用
import axios from 'axios'; const baseUrl = process.env.BASE_URL; axios.defaults.baseURL = `http://www.example.com${baseUrl}api/`
在樣式文件中使用(以 sass/scss 為例)
因?yàn)?sass 文件中無(wú)法獲取環(huán)境變量或 webpack 內(nèi)的配置,于是最直接的方法就是自定義一個(gè)變量,然后在每個(gè)需要使用到它的文件引用它。
// config.scss $baseUrl: "/"; // icon.scss @import "config" .icon-test { display: inline-block; background: url($baseUrl + 'imgs/icon_test.png') no-repeat; width: 10px; height: 10px; }
這樣做還是有比較大的麻煩:
- 如果生產(chǎn)環(huán)境和開(kāi)發(fā)環(huán)境的 baseUrl 不同,每次轉(zhuǎn)換環(huán)境去編譯都要去手動(dòng)修改這個(gè)變量,十分之麻煩而且可能出現(xiàn)錯(cuò)誤;
- 兩處地方相同的定義,不方便代碼的維護(hù);
- 在后續(xù)講到的關(guān)于 相對(duì)路徑 的坑會(huì)涉及到,每次引用 config.scss 的路徑并不一定是一樣的,且很容易出現(xiàn)編譯錯(cuò)誤;
那么,有沒(méi)有什么辦法能避免人工操作、避免多次的定義并且避免使用可能潛在錯(cuò)誤的引用呢?幸虧的確是有的! sass-loader 提供了一個(gè) data 選項(xiàng),可以為全局注入變量或樣式文件;
// vue.config.js const baseUrl = process.env.NODE_ENV === 'production' ? '/sub/' : '/'; module.exports = { baseUrl, css: { loaderOptions: { sass: { data: `$baseUrl: "${baseUrl}";` } } } }
這樣我們就可以在全局的 `sass` 文件中使用 `$baseUrl` 這個(gè)變量了,而且在只定義一次的情況下,能根據(jù)編譯環(huán)境變化而變化。
使用相對(duì)路徑
使用相對(duì)路徑也會(huì)存在一些坑,接下來(lái)會(huì)列舉常見(jiàn)的關(guān)于相對(duì)路徑的坑與解決方法:
JavaScript 動(dòng)態(tài)引用資源,編譯沒(méi)報(bào)錯(cuò),但頁(yè)面上請(qǐng)求返回 404
有時(shí)候我們需要使用 JavaScript 動(dòng)態(tài)的引用某些資源,且希望這些資源被 webpack 一同打包,我們先看這種做法:
computed: { background () { return `./bgs/${this.id}.jpg` } }
我們會(huì)發(fā)現(xiàn)打包沒(méi)報(bào)錯(cuò),但是在頁(yè)面上可以發(fā)現(xiàn)這些資源的請(qǐng)求都是 404。這是因?yàn)轭?lèi)似 ./bgs/${this.id}.jpg 這樣的動(dòng)態(tài)字符串在打包階段不會(huì)被 webpack 識(shí)別為依賴(lài),資源也就不會(huì)被打包了。為了讓 webpack 識(shí)別這些依賴(lài),我們可以這樣做:
computed: { background () { return require('./bgs/' + this.id + '.jpg') } }
通過(guò)使用 require() 讓 webpack 將括號(hào)內(nèi)的 URL 識(shí)別為一個(gè)依賴(lài)并傳入對(duì)應(yīng)的 loader 進(jìn)行處理。
要特別注意,以上的例子中,./bgs/ 目錄下的所有圖片都會(huì)被打包,因?yàn)?webpack 無(wú)法得知頁(yè)面在運(yùn)行時(shí)會(huì)使用哪張圖片,所以 webpack 會(huì)把所有的圖片都打包了。
在 sass 中使用相對(duì)路徑引用圖片或字體文件,編譯報(bào)錯(cuò)
先來(lái)看一個(gè)例子:
// 文件目錄 // src // |--assets // | | // | |-fonts // | | |- iconfont.eot // | | // | |-css // | | // | |-iconfont.scss // | // |--app.vue
// iconfont.scss @font-face { font-family: "iconfont"; src: url("../fonts/iconfont.eot"); ... }
// app.vue <style lang="scss"> @import './assets/css/iconfont.scss' </style>
往往我們?cè)诖虬臅r(shí)候會(huì)報(bào)錯(cuò)(以上例子會(huì)報(bào)錯(cuò)),說(shuō)找不到 iconfont.eot。 sass-loader 文檔中有對(duì) url() 進(jìn)行了單獨(dú)的說(shuō)明:
Since Sass/libsass does not provide url rewriting, all linked assets must be relative to the output.
If you're just generating CSS without passing it to the css-loader, it must be relative to your web root.
If you pass the generated CSS on to the css-loader, all urls must be relative to the entry-file (e.g. main.scss).
大致意思就是, sass-loader 并不提供 url 的重寫(xiě),所有的 scss 文件被 sass-loader 處理成最終的 CSS 后(編譯過(guò)程中 url 不會(huì)被重寫(xiě)即保持原樣),再傳遞給 css-loader 處理。也就是說(shuō),所有的 url 都是相對(duì)于輸出的!在 Vue CLI 搭建的項(xiàng)目中,它們都是相對(duì)于使用這些 scss 文件的 vue 文件的。對(duì)于上例,是相對(duì)于 app.vue 的,因此報(bào)錯(cuò)。我們會(huì)很自然的會(huì)希望路徑的引用是相對(duì)于 scss 文件本身的,sass-loader 文檔中也給出了解決方案:
Add the missing url rewriting using the resolve-url-loader. Place it before the sass-loader in the loader chain.
Library authors usually provide a variable to modify the asset path. bootstrap-sass for example has an $icon-font-path. Check out this working bootstrap example.
第一個(gè)方法:使用 resolve-url-loader 來(lái)彌補(bǔ) sass-loader 缺失的 url 重寫(xiě)功能,注意要放到 sass-loader 以前調(diào)用。
第二個(gè)方法:Library 作者一般都會(huì)提供變量,用來(lái)設(shè)置資源路徑,如 bootstrap-sass 可以通過(guò) $icon-font-path 來(lái)設(shè)置。參見(jiàn)this working bootstrap example。
這樣看來(lái)解決的思路有兩種:
- 寫(xiě) url 的時(shí)候就寫(xiě) vue 文件相對(duì)于資源的路徑。這種方法較為暴力,當(dāng)項(xiàng)目層級(jí)復(fù)雜了之后容易寫(xiě)錯(cuò)路徑(加上現(xiàn)有的編輯器、IDE應(yīng)該認(rèn)為你寫(xiě)的路徑是錯(cuò)誤的)。當(dāng)同個(gè) scss 文件被多個(gè)不同層級(jí)的 vue 文件引用的時(shí)候,這種暴力的方法就行不通了!
- 使用第三方庫(kù)補(bǔ)充 sass-loader 的路徑重寫(xiě)功能,讓路徑的引用是相對(duì)于當(dāng)前 scss 文件本身的。這個(gè)方法能較好的解決問(wèn)題。
在這里提供一下我喜歡的方法。與其考慮 讓路徑的引用是相對(duì)于 scss 文件本身 或 讓路徑直接相對(duì)于 vue 文件,我們可以換個(gè)思路,讓所有路徑都是以根目錄往下找,并讓 webpack 對(duì)路徑進(jìn)行重寫(xiě),但是直接用 /src/ 這種絕對(duì)路徑的寫(xiě)法會(huì)讓這些資源不被 webpack 打包。在前文提及到的,webpack 有個(gè)強(qiáng)大的機(jī)制,也就是 ~,通過(guò)在 url 前面添加 ~ 可以告訴 webpack 要把它當(dāng)做一個(gè)模塊來(lái)處理,也就是會(huì)被 webpack 打包。配合 webpack 提供的別名 @(/src),我們可以對(duì)上例做修改:
// iconfont.scss @font-face { font-family: "iconfont"; src: url("~@/assets/fonts/iconfont.eot"); ... }
這樣子,通過(guò) webpack 對(duì)模塊的處理,可以正確通過(guò)編譯!這樣做的好處是可大大避免書(shū)寫(xiě)相對(duì)路徑可能產(chǎn)生的錯(cuò)誤,每次只需“無(wú)腦”從根目錄往下找就是了,又可以減小依賴(lài)、減少配置項(xiàng)。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- vue3.0 項(xiàng)目搭建和使用流程
- 如何搭建一個(gè)完整的Vue3.0+ts的項(xiàng)目步驟
- Vue項(xiàng)目環(huán)境搭建詳細(xì)總結(jié)
- vue-cli3搭建項(xiàng)目的詳細(xì)步驟
- 用Vue-cli搭建的項(xiàng)目中引入css報(bào)錯(cuò)的原因分析
- 詳解使用vue腳手架工具搭建vue-webpack項(xiàng)目
- VSCode搭建vue項(xiàng)目的實(shí)現(xiàn)步驟
- vue-cli3.0 腳手架搭建項(xiàng)目的過(guò)程詳解
- 使用Vue-cli 3.0搭建Vue項(xiàng)目的方法
- vite+vue3項(xiàng)目初始化搭建的實(shí)現(xiàn)步驟
相關(guān)文章
詳解使用vue腳手架工具搭建vue-webpack項(xiàng)目
本篇文章主要介紹了詳解使用vue腳手架工具搭建vue-webpack項(xiàng)目,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-05-05Vue數(shù)據(jù)更新但頁(yè)面沒(méi)有更新的多種情況問(wèn)題及解決
這篇文章主要介紹了Vue數(shù)據(jù)更新但頁(yè)面沒(méi)有更新的多種情況問(wèn)題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07基于vue實(shí)現(xiàn)簡(jiǎn)易打地鼠游戲
這篇文章主要為大家詳細(xì)介紹了基于vue實(shí)現(xiàn)簡(jiǎn)易打地鼠游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-08-08vue里使用create,mounted調(diào)用方法的正確姿勢(shì)說(shuō)明
這篇文章主要介紹了vue里使用create,mounted調(diào)用方法的正確姿勢(shì),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04微前端qiankun主應(yīng)用與子應(yīng)用之間的跳轉(zhuǎn)示例
這篇文章主要為大家介紹了微前端qiankun主應(yīng)用與子應(yīng)用之間的跳轉(zhuǎn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08解決vue報(bào)錯(cuò):Do?not?mutate?vuex?store?state?outside?mutati
這篇文章主要介紹了解決vue報(bào)錯(cuò):Do?not?mutate?vuex?store?state?outside?mutation?handlers問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05