前言
本文純屬個人平時實踐過程中的一些經驗總結,算是一點點小技巧吧,不是多麼高明的技術,如果對你有幫助,那麼不勝榮幸。
本文不涉及罕見API
使用方法等,大部分內容都是基於對vue的一些實踐而已。由於涉嫌投機取巧,可能會帶來一些不符合規範的副作用,請根據專案要求酌情使用。
-
多個頁面都使用的到方法,放在
vue.prototype
上會很方便剛接觸
vue
的時候做過一件傻事,因為封裝了一個非同步請求介面post
,放在post.js
檔案裡面,然後在每個需要使用非同步請求的頁面引入import port from './xxxx/xxxx/post' 複製程式碼
如果只是這樣,還沒什麼,我們可以寫好一個頁面以後再複製,可以保證每個頁面都有上面的語句。但是如果每個檔案所在的目錄層級不一樣呢?
// 假設正常是這樣 import port from '../xxxx/xxxx/post' // 目錄加深一級,就變成這樣 import port from '../../xxxx/xxxx/post' // 再加深一級的樣子 import port from '../../../xxxx/xxxx/post' 複製程式碼
當然,這個時候,我們可以用 別名
@/xxxx/post
,但是還是少不了要每個頁面引用。 那我們來看看,用vue.prototype
有多方便? 首先,你得在vue
的入口檔案(vue-cli
生成的專案的話,預設是/src/main.js
)裡面做如下設定import port from './xxxx/xxxx/post' vue.prototype.$post = post 複製程式碼
這樣,我們就可以在所有的
vue
元件(頁面)裡面使用this.post()
方法了,就像vue
的親兒子一樣tip: 把方法掛在到
prototype
上的時候,最好加一個$
字首,避免跟其他變數衝突til again: 不要掛載太多方法到
prototype
上,只掛載一些使用頻率非常高的 -
需要響應的資料,在獲取到介面資料的時候,先設定
大家有沒有很經常碰到這樣都一種情況,在迴圈列表的時候,我們需要給列表項一個控制顯示的屬性,如 是否可刪除,是否已選中等等,而後端介面一般不會返回這種欄位,因為這屬於純前端展示的,跟後端沒啥關係,比如後端給的資料如下
[ {name: 'abc', age: 18}, {name: 'def', age: 20}, {name: 'ghi', age: 22}, ] 複製程式碼
我們不妨假設以上資料為學生列表
然後我們需要渲染這個列表,在每一項後面顯示一個勾選按鈕,如果使用者打勾,則這個按鈕是綠色,預設這個按鈕是灰色,這個時候,上表是沒有滿足這個渲染條件的資料,而如果我們在使用者打勾的時候,再去新增這個資料的話,正常的做法是無法及時響應的。
如果我們在獲取到資料的時候,先給陣列的每一項都加一個是否打勾的標示,就可以解決這個問題,我們假設我們獲取到的資料是
res.list
res.list.map(item => { item.isTicked = false }) 複製程式碼
這麼做的原理是
vue
無法對不存在的屬性作響應,所以我們在獲取到資料的時候,先把需要的屬性加上去,然後在賦值給data
, 這樣data
接收到資料的時候,已經是存在這個屬性了,所以會響應。當然還有其他方法可以實現。不過對於一個強迫症來說,我還是比較傾向於這種做法 -
封裝全域性基於
promise
的非同步請求方法看過很多專案的原始碼,發現大部分的非同步請求都是直接使用
axios
之類的方法,如下axios({ method: 'post', url: '/user/12345', data: { firstName: 'Fred', lastName: 'Flintstone' } }) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); }); 複製程式碼
如果有跨域,或者需要設定
http
頭等,還需要加入更多的配置,而這些配置,對於同一個專案來說,基本都是一樣的,不一樣的只有url
跟引數,既然這樣,那我嗎為什麼不把它封裝成一個方法呢?function post (url,param) { return axios({ method: 'post', url: url, data: param ... axios 的其他配置 }) } 複製程式碼
tip: 這裡原來我多用了一層promise包起來,對簡單的需求來說是太多餘了,感覺掘金使用者
@日月為易。
指出再結合第一點,我們就可以再任意
vue
例項中這樣使用let param = { firstName: 'Fred', lastName: 'Flintstone' } this.post('/user/12345',param) .then(...) .catch(...) 複製程式碼
有沒有比原始的簡單很多呢?如果你的專案支援
async
await
,還可以這樣用let param = { firstName: 'Fred', lastName: 'Flintstone' } let res = await this.post('/user/12345',param) console.log(res) // res 就是非同步返回的資料 複製程式碼
tip:
await
關鍵字必須在被 async 修飾的函式裡面使用
-
如果你覺得有時候,你真的需要父子元件共享一個值,不如試試傳個引用型別過去
vue
的父子元件傳值,有好多種方法,這裡就不一一列舉了,但是今天我們要了解的,是利用javascript
的引用型別特性,還達到另一種傳值的目的假設有這麼一個需求,父元件需要傳 3 個值到子元件,然後再子元件裡面改動後,需要立馬再父元件上作出響應,我們通常的做法上改完以後,通過
this.$emit
發射事件,然後再父元件監聽對應的事件,然而這麼做應對一兩個資料還好,如果傳的資料多了,會累死人。 我們不妨把這些要傳遞的資料,包再一個物件/陣列 裡面,然後在傳給子元件<subComponent :subData="subData"></subComponent> 複製程式碼
data () { return { subData: { filed1: 'field1', filed2: 'field2', filed3: 'field3', filed4: 'field4', filed5: 'field5', } } } 複製程式碼
這樣,我們在子元件裡面改動
subData
的內容,父元件上就能直接作出響應,無需this.$emit
或vuex
而且如果有其他兄弟元件的話,只要兄弟元件也有繫結這個subData
,那麼兄弟元件裡面的subData
也能及時響應tip: 首先,這麼做我個人上感覺有點不符合規範的,如果沒有特別多的資料,還是乖乖用
this.$emit
吧,其次,這個資料需要有特定的條件才能構造的出來,並不是所有情況都適用。 -
非同步請求的引數在
data
裡面構造好,用一個物件包起來,會方便很多有做過類似
ERP
型別的系統的同學,一定碰到過這樣的一個場景,一個列表,有 N 個過濾條件,這個時候通常我們這麼繫結<input type="text" v-model="field1"> <input type="text" v-model="field2"> <input type="text" v-model="field3"> .... <input type="text" v-model="fieldn"> 複製程式碼
data () { return { field1: 'value1', field2: 'value2', field3: 'value3', ... fieldn:'valuen' } } 複製程式碼
然後提交資料的時候這樣:
var param = { backend_field1: this.field1, backend_field2: this.field2, backend_field3: this.field3, ... backend_fieldn: this.fieldn } this.post(url,param) 複製程式碼
如你看到的,每次提交介面,都要去構造引數,還很容易遺漏,我們不妨這樣:先去介面文件裡面看一下後端需要的欄位名稱,然後
<input type="text" v-model="queryParam.backend_field1"> <input type="text" v-model="queryParam.backend_field2"> <input type="text" v-model="queryParam.backend_field3"> .... <input type="text" v-model="queryParam.backend_fieldn"> ``` ```javascript data () { return { queryParam:{ backend_field1: 'value1' backend_field2: 'value2' backend_field3: 'value3' ... backend_fieldn: 'valuen' } } } ``` 然後提交資料的時候這樣: ```javascript this.post(url,this.queryParam) ``` 是的,這樣做也是有侷限性的,比如你一個資料在 2 個地方共用,比如前端元件繫結的是一個陣列,你需要提交給後端的是 2 個字串(例:`element ui` 的時間控制元件),不過部分特殊問題稍微處理一下,也比重新構建一個引數簡單不是嗎? 複製程式碼
-
data
裡面的資料多的時候,給每個資料加一個備註,會讓你後期往回看的時候很清晰續上一點,
data
裡面有很多資料的時候,可能你寫的時候是挺清晰的,畢竟都是你自己寫的東西,可是過了十天半個月,或者別人看你的程式碼,相信我,不管是你自己,還是別人,都是一頭霧水(記憶力超出常人的除外),所以我們不妨給每個資料後面加一個備註data () { return { field1: 'value1', // 控制xxx顯示 field2: 'value2', // 頁面載入狀態 field3: [], // 使用者列表 ... fieldn: 'valuen' // XXXXXXXX } } 複製程式碼
-
邏輯複雜的內容,儘量拆成元件
假設我們有一個這樣的場景:
<div> <div>姓名:{{user1.name}}</div> <div>性別:{{user1.sex}}</div> <div>年齡:{{user1.age}}</div> ...此處省略999個欄位... <div>他隔壁鄰居的阿姨家小狗的名字:{{user1.petName}}</div> </div> <-- 當然,顯示中我們不會傻到不用 v-for,我們假設這種情況無法用v-for --> <div> <div>姓名:{{user2.name}}</div> <div>性別:{{user2.sex}}</div> <div>年齡:{{user2.age}}</div> ...此處省略999個欄位... <div>他隔壁鄰居的阿姨家小狗的名字:{{user2.petName}}</div> </div> 複製程式碼
這種情況,我們不妨把[使用者]的程式碼,提取到一個元件裡面: 假設如下程式碼,在
comUserInfo.vue
<template> <div> <div>姓名:{{user.name}}</div> <div>性別:{{user.sex}}</div> <div>年齡:{{user.age}}</div> ...此處省略999個欄位... <div>他隔壁鄰居的阿姨家小狗的名字:{{user.petName}}</div> </div> </template> <script > export default { props:{ user:{ type:Object, default: () => {} } } } </script> 複製程式碼
然後原來的頁面可以改成這樣(省略掉匯入和註冊元件,假設註冊的名字是
comUserInfo
):<comUserInfo :user="user1"/> <comUserInfo :user="user2"/> 複製程式碼
這樣是不是清晰很多?不用看註釋,都能猜的出來,這是2個使用者資訊模組, 這樣做,還有一個好處就是出現錯誤的時候,你可以更容易的定位到錯誤的位置。
-
如果你只在子元件裡面改變父元件的一個值,不妨試試
$emit('input')
,會直接改變v-model
我們正常的父子元件通訊是 父元件通過
props
傳給子元件,子元件通過this.$emit('eventName',value)
通知父元件繫結在@eventName
上的方法來做相應的處理。 但是這邊有個特例,vue
預設會監聽元件的input
事件,而且會把子元件裡面傳出來的值,賦給當前繫結到v-model
上的值正常用法 - 父元件
<template> <subComponent :data="param" @dataChange="dataChangeHandler"></subComponent> </template> <script > export default { data () { return { param:'xxxxxx' } }, methods:{ dataChangeHandler (newParam) { this.param = newParam } } } </script> 複製程式碼
正常用法 - 子元件
<script > export default { methods:{ updateData (newParam) { this.$emit('dataChange',newParam) } } } </script> 複製程式碼
利用預設
input
事件 - 父元件<template> <subComponent v-model="param"></subComponent> </template> 複製程式碼
利用預設
input
事件 - 子元件<script > export default { methods:{ updateData (newParam) { this.$emit('input',newParam) } } } </script> 複製程式碼
這樣,我們就能省掉父元件上的一列席處理程式碼,
vue
會自動幫你處理好tip: 這種方法只適用於改變單個值的情況,且子元件對父元件只需簡單的傳值,不需要其他附加操作(如更新列表)的情況。
補充一個
this.$emit('update:fidldName',value)
方法 (感謝掘金使用者@日月為易。
指出) 具體用法如下:父元件
<subComponent field1.sync="param1" field2.sync="param2"></subComponent> 複製程式碼
子元件
<script > export default { methods:{ updateData1 (newValue) { this.$emit('update:field1',newValue) }, updateData2 (newValue) { this.$emit('update:field2',newValue) } } } </script> 複製程式碼
該方法,個人認為比較適用於 要更新的資料不能繫結在
v-model
的情況下,或者要雙向通訊的資料大於 1 個(1個也可以用,但我個人更推薦input
的方式, 看個人喜好吧),但又不會很多的情況下. -
conponents
放在Vue options
的最上面不知道大家有沒有這樣的經歷: 匯入元件,然後在也頁面中使用,好的,報錯了,為啥?忘記註冊元件了,為什麼會經常忘記註冊元件呢?因為正常的一個
vue
例項的結構大概是這樣的:import xxx form 'xxx/xxx' export default { name: 'component-name', data () { return { // ...根據業務邏輯的複雜程度,這裡省略若干行 } }, computed: { // ...根據業務邏輯的複雜程度,這裡省略若干行 }, created () { // ...根據業務邏輯的複雜程度,這裡省略若干行 }, mounted () { // ...根據業務邏輯的複雜程度,這裡省略若干行 }, methods () { // ...根據業務邏輯的複雜程度,這裡省略若干行 }, } 複製程式碼
我不知道大家正常是把
components
屬性放在哪個位置,反正我之前是放在最底下,結果就是導致經常犯上述錯誤。後面我把
components
調到第一個去了import xxx form 'xxx/xxx' export default { components: { xxx }, // 省略其他程式碼 } 複製程式碼
從此以後,媽媽再也不用擔心我忘記註冊元件了,匯入和註冊都在同一個位置,想忘記都難。
-
大部分情況下,生命週期裡面,不要有太多行程式碼,可以封裝成方法,再呼叫
看過很多程式碼,包括我自己之前的,在生命週期裡面洋洋灑灑的寫了一兩百行的程式碼,如:把頁面載入的時候,該做的事,全部寫在
created
裡面,導致整個程式碼難以閱讀,完全不知道你在頁面載入的時候,做了些什麼, 這個時候,我們不妨把那些邏輯封裝成方法,然後在生命週期裡面直接呼叫:created () { // 獲取使用者資訊 this.getUserInfo() // 獲取系統資訊 this.getSystemInfo() // 獲取配置 this.getConfigInfo() }, methods:{ // 獲取使用者資訊 getUserInfo () {...}, // 獲取系統資訊 getSystemInfo () {...}, // 獲取配置 getConfigInfo () {...}, } 複製程式碼
這樣是不是一眼就能看的出,你在頁面載入的時候做了些什麼?
tip: 這個應該算是一個約定俗成的規範吧,只是覺得看的比較多這樣寫的,加上我自己初學的時候,也這麼做了,所以寫出來,希望新入坑的同學能避免這個問題
-
少用
watch
,如果你覺得你好多地方都需要用到watch
,那十有八九是你對vue
的API
還不夠了解vue
本身就是一個資料驅動的框架,資料的變動,能實時反饋到檢視上去,如果你想要根據資料來控制試圖,正常情況一下配合computed
服用就能解決大部分問題了,而檢視上的變動,我們一般可以通過監聽input
change
等事件,達到實時監聽的目的, 所以很少有需求使用到watch
的時候,至少我最近到的十來個專案裡面,是沒有用過watch
當然,並不是說watch
是肯定沒用處,vue
提供這個api,肯定是有他的道理,也有部分需求是真的需要用到的,只是我覺得應該很少用到才對,如果你覺得到處都得用到的話, 那麼我覺得 十有八九你應該多去熟悉一下computed
和vue
的其他api
了
最後
本文的github地址 歡迎隨意star,follow, 和 不隨意的 issue
另外,github上還有其他一些關於前端的教程和元件, 有興趣的童鞋可以看看,你們的支援就是我最大的動力。