最近在試水Vue CLI 3
,而且嘗試配置一個多頁面(多應用)項目出來,期間又遇到各類路徑問題,因而...因而有了下面的嘮叨。
如下都是基於Vue CLI 3
來舉例說明的,使用2.x
版本的其實也相似
首先,參考 官方文檔對靜態資源處理的說明,並經過本身的實踐,能夠總結出如下內容css
靜態資源能夠經過兩種方式進行處理:html
如下狀況下,資源不會被 webpack 處理,而是被直接拷貝:vue
/
開頭的路徑。如下狀況下,資源會被 webpack 處理(URL的resolve、minify、uglify、轉 base64 等):webpack
JavaScript
導入。template/CSS
中經過相對路徑(即以 .
開頭或直接以文件(夾)名開頭)被引用。~
開頭,其後的任何內容都會做爲一個模塊請求被解析。@
開頭,它也會做爲一個模塊請求被解析(@
是在 webpack 設置的 alias
)。咱們應該根據實際狀況去選擇咱們要引用的資源是否要被處理,而後用對應的、正確的方式去引用它們以達到目的。如下對使用絕對路徑和相對路徑的方法和注意事項進行描述。ios
默認狀況下,Vue CLI 會假設你的應用是被部署在一個域名的根路徑上(對應選項 baseUrl: '/'
),例如 https://www.my-app.com/
。若是應用被部署在一個子路徑上,你就須要用這個選項指定這個子路徑。例如,若是你的應用被部署在 https://www.my-app.com/my-app/
,則設置 baseUrl
爲 /my-app/
。正由於以上的可能狀況,咱們應該在打算引用純靜態資源(那些不被webpack處理的資源,通常就是 public
目錄下的資源)的時候,都確保使用 baseUrl
做爲 URL 的開頭,如下列舉在不一樣文件中配合 baseUrl
選項寫絕對路徑的使用方法和注意事項:git
咱們可使用 lodash template 語法插入 baseUrl
:github
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
*.vue
中使用咱們能夠經過 Vue CLI
提供的客戶端環境變量 process.env.BASE_URL
來獲取 baseUrl
:web
/* 在須要的組件中定義 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: { // 動態地獲取不一樣的靜態資源 imgUrl() { if (this.isBigImg) { return `${baseUrl}imgs/my_image_big.png` } else { return `${baseUrl}imgs/my_image.png` } } } }; </script> /* 我的建議能夠在全局定義,減去在每一個組件內定義的麻煩 Vue.prototype.$baseUrl = process.env.BASE_URL // 在 <template> 下使用 <img :src="`${$baseUrl}imgs/my_image.png`">
import axios from 'axios'; const baseUrl = process.env.BASE_URL; axios.defaults.baseURL = `http://www.example.com${baseUrl}api/`
sass/scss
爲例)由於 sass
文件中沒法獲取環境變量或 webpack 內的配置,因而最直接的方法就是自定義一個變量,而後在每一個須要使用到它的文件引用它。bootstrap
// 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; }
這樣作仍是有比較大的麻煩:axios
baseUrl
不一樣,每次轉換環境去編譯都要去手動修改這個變量,十分之麻煩並且可能出現錯誤;config.scss
的路徑並不必定是同樣的,且很容易出現編譯錯誤;那麼,有沒有什麼辦法能避免人工操做、避免屢次的定義而且避免使用可能潛在錯誤的引用呢?幸好的確是有的! sass-loader
提供了一個 data
選項,能夠爲全局注入變量或樣式文件;
// vue.config.js const baseUrl = process.env.NODE_ENV === 'production' ? '/sub/' : '/'; module.exports = { baseUrl, css: { loaderOptions: { sass: { data: `$baseUrl: "${baseUrl}";` } } } }
這樣咱們就能夠在全局的 `sass` 文件中使用 `$baseUrl` 這個變量了,並且在只定義一次的狀況下,能根據編譯環境變化而變化。
使用相對路徑也會存在一些坑,接下來會列舉常見的關於相對路徑的坑與解決方法:
JavaScript
動態引用資源,編譯沒報錯,但頁面上請求返回 404
有時候咱們須要使用 JavaScript
動態的引用某些資源,且但願這些資源被 webpack 一同打包,咱們先看這種作法:
computed: { background () { return `./bgs/${this.id}.jpg` } }
咱們會發現打包沒報錯,可是在頁面上能夠發現這些資源的請求都是 404
。這是由於相似 ./bgs/${this.id}.jpg
這樣的動態字符串在打包階段不會被 webpack 識別爲依賴,資源也就不會被打包了。爲了讓 webpack 識別這些依賴,咱們能夠這樣作:
computed: { background () { return require('./bgs/' + this.id + '.jpg') } }
經過使用 require()
讓 webpack 將括號內的 URL 識別爲一個依賴並傳入對應的 loader
進行處理。
要特別注意,以上的例子中,
./bgs/
目錄下的全部圖片都會被打包,由於 webpack 沒法得知頁面在運行時會使用哪張圖片,因此 webpack 會把全部的圖片都打包了。
先來看一個例子:
// 文件目錄 // 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>
每每咱們在打包的時候會報錯(以上例子會報錯),說找不到 iconfont.eot
。 sass-loader
文檔中有對 url()
進行了單獨的說明:
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 的重寫,全部的 scss
文件被 sass-loader
處理成最終的 CSS
後(編譯過程當中 url
不會被重寫即保持原樣),再傳遞給 css-loader
處理。也就是說,全部的 url
都是相對於輸出的!在 Vue CLI
搭建的項目中,它們都是相對於使用這些 scss
文件的 vue
文件的。對於上例,是相對於 app.vue
的,所以報錯。咱們會很天然的會但願路徑的引用是相對於 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.
第一個方法:使用 resolve-url-loader 來彌補 sass-loader
缺失的 url 重寫功能,注意要放到 sass-loader
之前調用。
第二個方法:Library 做者通常都會提供變量,用來設置資源路徑,如 bootstrap-sass 能夠經過 $icon-font-path 來設置。參見this working bootstrap example。
這樣看來解決的思路有兩種:
vue
文件相對於資源的路徑。這種方法較爲暴力,當項目層級複雜了以後容易寫錯路徑(加上現有的編輯器、IDE應該認爲你寫的路徑是錯誤的)。當同個 scss
文件被多個不一樣層級的 vue
文件引用的時候,這種暴力的方法就行不通了!sass-loader
的路徑重寫功能,讓路徑的引用是相對於當前 scss
文件自己的。這個方法能較好的解決問題。在這裏提供一下我喜歡的方法。與其考慮 讓路徑的引用是相對於 scss
文件自己 或 讓路徑直接相對於 vue
文件,咱們能夠換個思路,讓全部路徑都是以根目錄往下找,並讓 webpack 對路徑進行重寫,可是直接用 /src/
這種絕對路徑的寫法會讓這些資源不被 webpack 打包。在前文說起到的,webpack 有個強大的機制,也就是 ~
,經過在 url 前面添加 ~
能夠告訴 webpack 要把它當作一個模塊來處理,也就是會被 webpack 打包。配合 webpack 提供的別名 @
(/src
),咱們能夠對上例作修改:
// iconfont.scss @font-face { font-family: "iconfont"; src: url("~@/assets/fonts/iconfont.eot"); ... }
這樣子,經過 webpack 對模塊的處理,能夠正確經過編譯!這樣作的好處是可大大避免書寫相對路徑可能產生的錯誤,每次只需「無腦」從根目錄往下找就是了,又能夠減少依賴、減小配置項。
若是有什麼地方我理解錯誤歡迎你們指出!