Vue工程化最佳實踐

目錄結構

總覽javascript

  • api目錄用於存放 api請求,文件名與模型名稱基本一致,文件名使用小駝峯, 方法名稱與後端restful控制器一致.php

  • enums 目錄存放 常量, 與後端的常量目錄對應css

  • icons目錄用於存放圖標, element-ui提供的圖標實在是太少啦.因此我一般會使用 阿里的iconfonthtml

  • lang目錄存放多語言前端

  • layouts目錄存放佈局vue

    • 上面展現的是一個後臺系統, empty爲一個空佈局.用於登陸頁面, 其餘頁面則使用default佈局. 佈局不須要過多介紹,寫過laravel blade都很熟悉了.這裏的佈局須要和vue-router配合使用
  • mixins 相似php的trait, 可是它更強大, 完整貼合vue組件的生命週期java

  • plugins 目錄存放插件配置, 好比 axios,vue-lazy等 (這是從nuxt中學到的概念)react

  • router目錄存放與 前端路由相關的配置,整體來講相似於laravel的api層ios

  • store 目錄即vuex的目錄, 相似於前端的model. 其文件與後端model相匹配,採用小駝峯命名laravel

  • 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去請求後端呢?

router.vuejs.org/zh/guide/ad… 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之間,這種設計依然合理.

這也意味着應用中全部的數據都遵循相同的生命週期,這樣可讓應用變得更加可預測且容易理解。

上面的圖很好的闡述了這種開發模式. 引自 github.com/sorrycc/blo…

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設計稿的圖層一般就是組件的拆分規範 😋

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在這方面比較不錯的實踐 github.com/paularmstro…

如何設計良好規範的compoents?

組件的設計在業務層很是的重要,在下一篇我會介紹一下我總結出的一些實踐

相關文章
相關標籤/搜索