Vue 工程化最佳實踐

目錄結構

總覽javascript

 

 

 

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

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

    •  
  • icons 目錄用於存放圖標,element-ui 提供的圖標實在是太少啦。因此我一般會使用 阿里的 iconfont
  • lang 目錄存放多語言
  • layouts 目錄存放佈局
    •  
    • 上面展現的是一個後臺系統,empty 爲一個空佈局。用於登陸頁面,其餘頁面則使用 default 佈局。佈局不須要過多介紹,寫過 laravel blade 都很熟悉了。這裏的佈局須要和 vue-router 配合使用
  • mixins 相似 php 的 trait, 可是它更強大,完整貼合 vue 組件的生命週期
  • plugins 目錄存放插件配置,好比 axios,vue-lazy 等 (這是從 nuxt 中學到的概念)html

    •  
  • 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

 

基礎規範

就目前來講 vue 項目不多用到 class, 所以 .js 文件一般都是一個 module, 因此文件名使用小駝峯的形式命名。若是有類文件,則類文件使用大駝峯的形式命名.java

.vue 文件 可使用 中劃線和大駝峯兩種命名方式,參考了 element/iview/nuxt 項目以後,推薦統一使用中劃線命名.react

全部的文件夾名稱統一使用中劃線命名ios

引入 vue 組件時文件時須要轉換成大駝峯 import 'TestTest' from '@/components/test-test'laravel

在 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?

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

 vuejs
相關文章
相關標籤/搜索