目錄結構
總覽
-
api 目錄用於存放 api 請求,文件名與模型名稱基本一致,文件名使用小駝峰,方法名稱與后端 restful 控制器一致.
-
-
enums 目錄存放 常量,與后端的常量目錄對應
-
- icons 目錄用於存放圖標,element-ui 提供的圖標實在是太少啦。所以我通常會使用 阿里的 iconfont
- lang 目錄存放多語言
- layouts 目錄存放布局
-
- 上面展示的是一個后台系統,empty 為一個空布局。用於登錄頁面,其他頁面則使用 default 布局。布局不需要過多介紹,寫過 laravel blade 都很熟悉了。這里的布局需要和 vue-router 配合使用
-
- mixins 類似 php 的 trait, 但是它更強大,完整貼合 vue 組件的生命周期
-
plugins 目錄存放插件配置,比如 axios,vue-lazy 等 (這是從 nuxt 中學到的概念)
-
- router 目錄存放與 前端路由相關的配置,總體來說類似於 laravel 的 api 層
-
store 目錄即 vuex 的目錄,類似於前端的 model. 其文件與后端 model 相匹配,采用小駝峰命名
-
- utils 目錄存放輔助函數
- views 為業務視圖層,相信后端同學也很熟悉。其由 vue-router 直接調度
- main.js 為 app 的入口,類似於后端的 index.php
- components 目錄,存放組件。通常是一些可復用的組件會單獨存放在該目錄
總體來說,已后端的 mvc 思想來看現代的前端項目是非常的自然的。后端的 model 對應前端的 store, 后端的 router 對應前端的 router, 后端的 controller + views 對應前端的 views.
基礎規范
就目前來說 vue 項目很少用到 class, 因此 .js 文件通常都是一個 module, 所以文件名使用小駝峰的形式命名。如果有類文件,則類文件使用大駝峰的形式命名.
.vue 文件 可以使用 中划線和大駝峰兩種命名方式,參考了 element/iview/nuxt 項目之后,推薦統一使用中划線命名.
所有的文件夾名稱統一使用中划線命名
引入 vue 組件時文件時需要轉換成大駝峰
import 'TestTest' from '@/components/test-test'
在 template 使用時依舊使用中划線
<test-test />
其他規范如變量命名和使用規范 使用 eslint 的 standard 即可很好的解決.
前端存在很多的事件 如 change/input/upload/sumit 等等,相應的處理推薦使用 handle + 事件名稱,如 handleChange
生命周期
vue-router 解析當前用戶鍵入的 url, 然后匹配合適的視圖組件加載.
着重介紹一下 我對 views 目錄下的視圖組件的理解,已修改地址為例
script 部分既控制器部分,其請求數據,然后注入到 view 中,就像后端的 mvc 一樣。只不過 vue 將 vc 其寫入到了一個文件中。這樣理解對於寫過后端的同學顯得更加的自然
控制器如何獲取數據?
在過去的 vue 項目中,我們可能會見到這樣的寫法
// ... views/address/edit.vue created () { axios.get('/addresses/1') .then((response) => { this.list = resposne.data }) } //...
這種寫法無異於后端在控制器中寫 sql 語句一樣,在工程化實踐中不推薦這么做,后端通過 model 來獲取數據會更加的優雅自然。在 vue 項目中,model 既 vuex, 因此推薦這么做
如果你對我說的東西一臉懵逼,那么你可以看一下 vuex 的文檔。我現在做的就是用后端熟悉的概念,來描述前端項目的最佳實踐
// ... views/address/edit.vue 控制器+視圖 computed: { address: () => this.$store.address.itemBy[1] // 從store模型中取出我們想要數據 } // ...
// ... store/modules/address.js 數據源 export default { state: { itemBy: {} }, actions: { ... }, mutations: { ... } } // ...
store 中的數據從哪里來?
數據當然是從后端的數據庫中獲取,我們不能讓前端直接訪問我們的數據庫,因此我們會提供 api 讓前端訪問.store 中存在一個發起 api 請求的地方,既 action.
// ... store/modules/address.js 模型 export default { state: { itemBy: {} }, actions: { async fetchItem({ commit, state }, { id }) { // 對axios和api進行了簡單的封裝,使api請求更加語義化 cosnt { data } = await address.show(id) // action只能通過提交commit來修改state,具體原因請查看vuex文檔 (其實我也忘了為啥 (╯﹏╰)) commit('SET_ITEM', data) } }, mutations: { SET_ITEM: (state, item) => { state.itemBy[item.id] = item } } } // ...
這樣我們的模型中就有數據啦
什么時候去調用 fetchItem
去請求后端呢?
https://router.vuejs.org/zh/guide/advanced/data-fetching.html vue-router 文檔的解答
這里不推薦在 vue 原始的生命周期中去調用初始化請求,可能會帶來 數據還沒有獲取到,template 卻已經被渲染。會造成一些數據不存在的異常,推薦在 vue-router 的生命周期中去請求數據
// ... views/address/edit.vue async beforeRouteEnter (to, from, next) { // 等待模型數據加載完畢,才繼續進行vue組件的生命周期 await store.dispatch('fetchItem', to.params.id) next() } created () { // 不推薦在這里調用 fetchItem } //...
到這里你可能發現,這和你平時寫的 vue 有些不一樣,沒有類似 this.data = response.data
這種操作。類似這種操作其實類似賦值操作,或者稱為副作用,其引入了時間的概念,使數據的管理變的復雜。直觀的體現就是我們可能會有這種多余的 if(data)
判斷.
當然副作用是難以避免的,但是我們可以統一的管理他們。類似上面的代碼就是一套我覺得還不錯的方法。從 view 的角度看,數據是固有存在存在的,其不需要關心是否是否已經被加載完畢,且 store 中的不可被 view 修改,既數據只能單向流動
在 store 中統一管理數據的另外一個好處就是方便持久化
view 層如何修改數據源?
上面的描述實際上表達了一種 發布與訂閱的模式,從 store 到 view 的數據流是嚴格單向數據流動.
view 層不允許直接修改 store 中的數據,但是 view 層卻可以通過發送 action 來影響數據源.
比如初始化時的 dispatch 的 action, 各種 event 觸發的 dispatch. 當數據源發生改變時,作為訂閱者的 view 層會非常自然的重新渲染.
這種設計和父子組件類似,vue 中子組件不允許直接修改父組件 props 到子組件的數據,只能通過向父組件 emit event. 在 view 和 store 之間,這種設計依然合理.
這也意味着應用中所有的數據都遵循相同的生命周期,這樣可以讓應用變得更加可預測且容易理解。
上面的圖很好的闡述了這種開發模式。引自 https://github.com/sorrycc/blog/issues/1
view 層再深入
view 層的 script 部分,除了充當了傳統的 controller, 起到初始化的作用外,實際上還做了更多的事情.
先從 data 部分說起,在 view 層會有一些狀態需要記錄,如 菜單的展開或收起,彈窗的彈出與關閉。對於這樣的狀態的管理,一種做法就是將存儲在 data 部分.
也有人將所有的狀態 也放在 store 中的 state 維護。既 state 分為狀態和數據 兩種類型.
view 層的 script 更重要的部分,是其到了一個交互反饋的作用,既類似下面的代碼
<template>
<button @click="handleSubmit"/>
</template>
<script>
export default {
data: {},
methods: {
handleSubmit() {
}
}
}
</script>
關於 css 部分,由於個人不了解 css, 也不清楚 css 的業界規范及在 vue 上的最佳實踐,因此不做過多介紹.
總結一下
在前面的介紹中,store 和 api 目錄是和數據掛鈎的,當數據庫定下來,這一部分也就定了下來.
views/components 和業務 (ui) 掛鈎,需要等設計稿確定后,這一部分才能確定下來.
PS 設計稿的圖層通常就是組件的拆分規范?
使用 vuex 存儲數據的另一個好處就是可以無縫的切換到 ssr 框架 nuxt
views 和 store 之間是一種訂閱和發布的模式.
兩個問題
store 中的 state 應該如何組織? 對於 api 請求,我們經常會看到這樣的 json 數據
// post { id: 1, title: xxxx, content: xxxx, user: { id: xxx, nickname: xxx, avatar: xxx, }, comments: [ { id: xxx, user_id: xxx, content: xxx, user: { // ... } }, { //.... } ] }
上面的數據結構復雜,嵌套深入。如果我們將他們一股腦的存在 post 的 state 中,會造成數據過於集中,冗余. comments 無法獨立化更新等等問題。使得前端 scheme/orm, 數據組織的規范化變的迫切需要
但是 vue 在這方面沒有很好的規范和最佳實踐. react 在這方面比較不錯的實踐 https://github.com/paularmstrong/normalizr
如何設計良好規范的 compoents?
組件的設計在業務層非常的重要,在下一篇我會介紹一下我總結出的一些實踐