由於公司的前端開始轉向 VueJS
,最近開始使用這個框架進行開發,遇到一些問題記錄下來,以備後用。
主要寫一些 官方手冊 上沒有寫,但是實際開發中會遇到的問題,需要一定知識基礎。
涉及技術棧
正文:
polyfill 與 transform-runtime
首先,vue-cli
為我們自動新增了 babel-plugin-transform-runtime
這個外掛,該外掛多數情況下都運作正常,可以轉換大部分 ES6
語法。
但是,存在如下兩個問題:
- 非同步載入元件時,會產生
polyfill
程式碼冗餘 - 不支援對全域性函式與例項方法的
polyfill
兩個問題的原因均歸因於 babel-plugin-transform-runtime
採用了沙箱機制來編譯我們的程式碼(即:不修改宿主環境的內建物件)。
由於非同步元件最終會被編譯為一個單獨的檔案,所以即使多個元件中使用了同一個新特性(例如:Object.keys()
),那麼在每個編譯後的檔案中都會有一份該新特性的 polyfill
拷貝。如果專案較小可以考慮不使用非同步載入,但是首屏的壓力會比較大。
不支援全域性函式(如:Promise
、Set
、Map
),Set
跟 Map
這兩種資料結構應該大家用的也不多,影響較小。但是 Promise
影響可能就比較大了。
不支援例項方法(如:'abc'.include('b')
、['1', '2', '3'].find((n) => n 等等),這個限制幾乎廢掉了大部分字串和一半左右陣列的新特性。
一般情況下
babel-plugin-transform-runtime
能滿足大部分的需求,當不滿足需求時,推薦使用完整的babel-polyfill
。
替換 babel-polyfill
首先,從專案中移除 babel-plugin-transform-runtime
解除安裝該依賴:
1 |
npm un babel-plugin-transform-runtime -D |
修改 babel
配置檔案
1 2 3 4 5 6 7 8 |
// .babelrc { //... "plugins": [ // - "transform-runtime" ] //... } |
然後,安裝 babel-polyfill
依賴:
1 |
npm i babel-polyfill -D |
最後,在入口檔案中匯入
1 2 |
// src/main.js import 'babel-polyfill' |
ES6 import 引用問題
在 ES6
中,模組系統的匯入與匯出採用的是引用匯出與匯入(非簡單資料型別),也就是說,如果在一個模組中定義了一個物件並匯出,在其他模組中匯入使用時,匯入的其實是一個變數引用(指標),如果修改了物件中的屬性,會影響到其他模組的使用。
通常情況下,系統體量不大時,我們可以使用 JSON.parse(JSON.stringify(str))
簡單粗暴地來生成一個全新的深度拷貝的 資料物件。不過當元件較多、資料物件複用程度較高時,很明顯會產生效能問題,這時我們可以考慮使用 Immutable.js。
鑑於這個原因,進行復雜資料型別的匯出時,需要注意多個元件匯入同一個資料物件時修改資料後可能產生的問題。
此外,模組定義變數或函式時即便使用let
而不是const
,在匯入使用時都會變成只讀,不能重新賦值,效果等同於用const
宣告。
在 Vue 中使用 Pug 與 Less
安裝依賴
Vue
中使用 vue-loader
根據 lang
屬性自動判斷所需要的 loader
,所以不用額外配置 Loader
,但是需要手動安裝相關依賴:
1 2 |
npm i pug -D npm i less-loader -D |
還是相當方便的,不用手動修改 webpack
的配置檔案新增 loader
就可以使用了
使用
pug
還是pug-loader
?sass
兩種語法的loader
如何設定?
— 請參考 前處理器 · vue-loader
使用
1 |
.action(v-if='hasRight') ul li 編輯 li 刪除 |
定義全域性函式或變數
許多時候我們需要定義一些全域性函式或變數,來處理一些頻繁的操作(這裡拿 AJAX
的異常處理舉例說明)。但是在 Vue
中,每一個單檔案元件都有一個獨立的上下文(this
)。通常在異常處理中,需要在檢視上有所體現,這個時候我們就需要訪問 this
物件,但是全域性函式的上下文通常是 window
,這時候就需要一些特殊處理了。
簡單粗暴型
最簡單的方法就是直接在 window
物件上定義一個全域性方法,在元件內使用的時候用 bind
、call
或 apply
來改變上下文。
定義一個全域性異常處理方法:
1 2 3 4 5 6 7 8 |
// errHandler.js window.errHandler = function () { // 不能使用箭頭函式 if (err.code && err.code !== 200) { this.$store.commit('err', true) } else { // ... } } |
在入口檔案中匯入:
1 2 |
// src/main.js import 'errHandler.js' |
在元件中使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// xxx.vue export default { created () { this.errHandler = window.errHandler.bind(this) }, method: { getXXX () { this.$http.get('xxx/xx').then(({ body: result }) => { if (result.code === 200) { // ... } else { this.errHandler(result) } }).catch(this.errHandler) } } } |
優雅安全型
在大型多人協作的專案中,汙染 window
物件還是不太妥當的。特別是一些比較有個人特色的全域性方法(可能在你寫的元件中幾乎處處用到,但是對於其他人來說可能並不需要)。這時候推薦寫一個模組,更優雅安全,也比較自然,唯一不足之處就是每個需要使用該函式或方法的元件都需要進行匯入。
使用方法與前一種大同小異,就不多作介紹了。 ̄ω ̄=
Moment.JS 與 Webpack
在使用 Moment.js
遇到一些問題,發現最終打包的檔案中將 Moment.js
的全部語言包都打包了,導致最終檔案徒然增加 100+kB。查了一下,發現可能是 webpack
打包或是 Moment.js
資源引用問題(?),目前該問題還未被妥善處理,需要通過一些 trick
來解決這個問題。
在 webpack
的生產配置檔案中的 plugins
欄位中新增一個外掛,使用內建的方法類 ContextReplacementPlugin 過濾掉 Moment.js
中那些用不到的語言包:
1 2 |
// build/webpack.prod.conf.js new webpack.ContextReplacementPlugin(/moment[\\/]locale$/, /^\.\/(zh-cn)$/) |
解決方案採自 oleg-nogin@webpack/webpack#3128。
問題討論詳見 GitHub Issue: moment/moment#2373、webpack/webpack#3128。
自定義路徑別名
可能有些人注意到了,在 vue-cli
生成的模板中在匯入元件時使用了這樣的語法:
1 |
import Index from '@/components/Index' |
這個 @
是什麼東西?後來改配置檔案的時候發現這個是 webpack
的配置選項之一:路徑別名。
我們也可以在基礎配置檔案中新增自己的路徑別名,比如下面這個就把 ~
設定為路徑 src/components
的別名:
1 2 3 4 5 6 7 8 9 10 11 |
// build/webpack.base.js { resolve: { extensions: ['.js', '.vue', '.json'], alias: { 'vue$': 'vue/dist/vue.esm.js', '@': resolve('src'), '~': resolve('src/components') } } } |
然後我們匯入元件的時候就可以這樣寫:
1 2 3 4 5 |
// import YourComponent from 'YourComponent' // import YourComponent from './YourComponent' // import YourComponent from '../YourComponent' // import YourComponent from '/src/components/YourComponent' import YourComponent from '~/YourComponent' |
既解決了路徑過長的麻煩,又解決了相對路徑的煩惱,方便很多吧!ヾ(゚∀゚ゞ)
CSS 作用域與模組
元件內樣式
通常,元件中 標籤裡的樣式是全域性的,在使用第三方 UI 庫(如:
Element
)時,全域性樣式很可能影響 UI 庫的樣式。我們可以通過新增 scoped
屬性來使 style
中的樣式只作用於當前元件:
1 2 3 4 5 6 |
<style lang="less" scoped> @import 'other.less'; .title { font-size: 1.2rem; } </style> |
在有
scoped
屬性的style
標籤內匯入其他樣式,同樣會受限於作用域,變為元件內樣式。複用程度較高的樣式不建議這樣使用。
另,在元件內樣式中應避免使用元素選擇器,原因在於元素選擇器與屬性選擇器組合時,效能會大大降低。
— 兩種組合選擇器的測試:classes selector,elements selector
匯入樣式
相對於 style
使用 scoped
屬性時的元件內樣式,有時候我們也需要新增一些全域性樣式。當然我們可以用沒有 scoped
屬性的 style
來寫全域性樣式。但是相比較,更推薦下面這種寫法:
1 2 3 4 5 6 7 8 9 |
/* 單獨的全域性樣式檔案 */ /* style-global.less */ body { font-size: 10px; } .title { font-size: 1.4rem; font-weight: bolder; } |
然後在入口檔案中匯入全域性樣式:
1 2 |
// src/main.js import 'style-global.less'/pre> |
獲取表單控制元件值
通常我們可以直接使用 v-model
將表單控制元件與資料進行繫結,但是有時候我們也會需要在使用者輸入的時候獲取當前值(比如:實時驗證當前輸入控制元件內容的有效性)。 這時我們可以使用 @input
或 @change
事件繫結我們自己的處理函式,並傳入 $event
物件以獲取當前控制元件的輸入值:
1 2 3 |
<code> <input type="text"/> </code> |
1 2 3 4 5 6 7 8 |
change (e) { let curVal = e.target.value if (/^\d+$/.test(curVal)) { this.num = +curVal } else { console.error('%s is not a number!', curVal) } } |
當然,如果 UI 框架採用
Element
會更簡單,它的事件回撥會直接傳入當前值。
v-for 的使用 tips
v-for
指令很強大,它不僅可以用來遍歷陣列、物件,甚至可以遍歷一個數字或字串。
基本語法就不講了,這裡講個小 tips:
索引值
在使用 v-for
根據物件或陣列生成 DOM
時,有時候需要知道當前的索引。我們可以這樣:
- {{ key }} – {{ item }}
但是,在遍歷數字的時候需要注意,數字的 value
是從 1 開始,而 key
是從 0 開始:
- {{ k }}-{{ v }}
2.2.0+
的版本里,當在元件中使用v-for
時,key
現在是必須的。
模板的唯一根節點
與 JSX
相同,元件中的模板只能有一個根節點,即下面這種寫法是 錯誤 的:
Title
我們需要用一個塊級元素把他包裹起來:
Title
專案路徑配置
由於 vue-cli
配置的專案提供了一個內建的靜態伺服器,在開發階段基本不會有什麼問題。但是,當我們把程式碼放到伺服器上時,經常會遇到靜態資源引用錯誤,導致介面一片空白的問題。
這是由於 vue-cli
預設配置的 webpack
是以站點根目錄引用的檔案,然而有時候我們可能需要把專案部署到子目錄中。
我們可以通過 config/index.js
來修改檔案引用的相對路徑:
1 2 3 4 5 |
build.assetsSubDirectory: 'static' build.assetsPublicPath: '/' dev.assetsSubDirectory: 'static' dev.assetsPublicPath: '/' |
我們可以看到匯出物件中 build
與 dev
均有 assetsSubDirectory
、assetsPublicPath
這兩個屬性。
其中 assetsSubDirectory 指靜態資原始檔夾,也就是打包後的 js
、css
、圖片等檔案所放置的資料夾,這個預設一般不會有問題。
assetsPublicPath 指靜態資源的引用路徑,預設配置為 /
,即網站根目錄,與 assetsSubDirectory 組合起來就是完整的靜態資源引用路徑 /static
。
寫到這裡解決方法已經很明顯了,只要把根目錄改為相對目錄就好了:
1 2 |
build.assetsSubDirectory: 'static' build.assetsPublicPath: './' |
沒錯!就是一個 .
的問題。ㄟ( ▔, ▔ )ㄏ
To be continue…
文章還在完善中,歡迎大家一起討論 Vue.JS 開發中遇到的一些問題哈 (゚▽゚)/
話說收藏好多,你確定收藏了會記得看嗎_(:зゝ∠)_
讀一讀開發的時候至少會有個印象,點個贊打卡啦~
原文:VueJS 開發常見問題集錦