Vue中拆分視圖層代碼的5點建議

示例代碼託管在:http://www.github.com/dashnowords/blogs前端

博客園地址:《大史住在大前端》原創博文目錄vue

華爲雲社區地址:【你要的前端打怪升級指南】git

分享一篇尤大大演講鎮樓:「2019 JSConf.Asia - 尤雨溪」在框架設計中尋求平衡angularjs

一.框架的定位

框架一般只是一種設計模式的實現,它並不意味着你能夠在開發中避免全部分層設計工做。github

SPA框架幾乎都是基於MVCMVVM設計模式而創建起來的,這些模式都只是宏觀的分層設計,當代碼量開始隨着項目增大而增多時,問題就會愈來愈多。許多企業內部的項目仍然在使用angularjs1.X,你會發現許多controller的體積大到使人髮指,稍有經驗的團隊會利用好angularjs1構建的controller,service,filter以及路由和消息機制來完成基本的拆分和解耦,這已經能讓他們的開發能力中等體量的項目,每每只有掌握了angularjs1玩法精髓——directive的隊伍,纔可以在應付大型項目時使代碼保持足夠的清晰度,固然這只是在代碼形態和模塊劃分上的工做,至關於代碼的骨骼,想要讓業務邏輯自己更加清晰,就須要更高級的建模設計知識來對業務邏輯進行分層,例如領域驅動模型。若是你仍然在使用angularjs1.x的版本進行開發,能夠參考【如何重構Controller】進行基本的分層拆分設計。vuex

有趣的是一些團隊認爲沒法承載大型項目是angularjs1.x的原罪,與他們的開發水平無關,因而將但願寄託於擁有自動化工具加持的現代化SPA框架,然而若是有機會觀察你就會發現,許多項目對新框架的使用方式和以前並無本質的差異,只不過是把之前臃腫到不行的代碼又換了一種形式塞進了前端工程裏,而後藉着ES6語法和新型框架自己的簡潔性,開始沾沾自喜地認爲這是本身重構的功勞。express

請記住,若是不進行結構設計,即使使用最新版本的最熱門的框架,寫出來的代碼依舊會是一團亂麻。後端

二. Vue開發中的script拆分優化

Vue框架爲例,在工程化工具和vue-loader的支撐下,主流的開發模式是基於*.vue這種單文件組件形態的。一個典型的vue組件包含以下幾個部分:設計模式

<template>
   <!--視圖模板-->
</template>

<script>
    /*編寫組件腳本*/
    export default {
        name:'component1'
    }
</script>

<style>
    /*編寫組件樣式*/
</style>

script的部分一般包含有交互邏輯業務邏輯數據轉換以及DOM操做,若是不加整理,很容易變得混亂不堪。*.vue文件的本質是View層代碼,它應該儘量輕量幷包含與視圖有關的信息,即特性聲明事件分發,其餘的代碼理論上都應該剝離出去,這樣當項目體量增大後,維護起來就更容易聚焦關鍵信息,下面就如何進行腳本代碼拆分提供一些思路,有一些多是很基本的原則,爲儘量完整就放在一塊兒,你並不須要從最開始就採納全部的建議。

1.組件劃分

這是View層減重的基礎,將可共用的視圖組件剝離出去,改成消息機制進行通訊,甚至直接剝離出包含視圖和業務代碼的業務邏輯組件,均可以有效地拆分View層,下降代碼的複雜度。

2.剝離業務邏輯代碼

script中最大的一部分通常是業務邏輯,首先將業務邏輯代碼剝離爲獨立的[name].business.js模塊,這樣作的直觀好處就是減輕了View層,另外一方面是解除了業務邏輯和頁面之間的強綁定關係,若是其餘頁面也涉及到這塊業務邏輯中的個別方法,就能夠直接進行復用,最後就是當項目逐漸複雜,你決定引入vuex來進行狀態管理時View層會相對更容易修改。

一段包含基本增刪改查邏輯的組件大概是下面的樣子:

<script>
    export default{
        name:'XXX',
        methods:{
            handleClickCreate(){},
            handleClickEdit(){},
            handleClickRefresh(){},
            handleClickDelete(){},
            sendCreate(){},
            sendEdit(){},
            sendGetAll(){},
            sendDelete(){}
        }
    }
</script>

簡易的剝離方式是將交互邏輯保留在視圖層,將業務邏輯部分代碼放在另外一個模塊中,而後利用ES6擴展運算符將其加入到組件實例的方法中,以下所示:

<script>
    import OrderBusiness from './Order.business.js';
    export default{
        name:'XXX',
        methods:{
            ...OrderBusiness,
            handleClickCreate(){},
            handleClickEdit(){},
            handleClickRefresh(){},
            handleClickDelete(){},
        }
    }
</script>

這種方式只是一種形態上的模塊化拆分,並無對業務邏輯自己進行梳理。另外一種方式是構建獨立的業務邏輯服務,保留在View層中的代碼很容易轉換爲使用vuex時的編碼風格:

<script>
    import OrderBusiness from './Order.business.js';
    export default{
        name:'XXX',
        methods:{
            handleClickCreate(){
                OrderBusiness.sendCreate();
            },
            handleClickEdit(){
                OrderBusiness.sendEdit();
            },
            handleClickRefresh(){
                OrderBusiness.sendGetAll();
            },
            handleClickDelete(){
                OrderBusiness.sendDelete();
            }
        }
    }
</script>

筆者的建議是,前面三個示例隨着項目體量的增加能夠實現漸進式的修改。

3. 剝離數據轉換代碼

在先後端分離的開發模式下,前端所須要的數據支持須要從後端請求得到,但請求來的原始數據一般都是沒法直接使用的,甚至有可能引起代碼報錯,例如時間多是以時間戳形式傳過來的,或者你的代碼須要取用某個對象屬性時,後臺同窗卻在該屬性上掛了一個默認值NULL等,另外一方面,開發過程當中的接口改動是沒法避免的,因此在代碼結構的設計上,應該儘量將可能變化的部分聚合起來。

比較實用的作法就是爲每個接口創建一個Transformer函數,從後臺請求來的數據先通過Transformer函數變換爲前臺可以流通使用的數據結構,並在必要的屬性上添加適當的默認值防止報錯,你能夠盡情地在此使用Lodash.js等函數工具來加工和重組本身須要的數據,即便最初後臺傳給你的數據不須要加工,也能夠保留一個透傳函數或是模塊說明以提醒其餘協做開發者在面對這種場景時採用相似的作法,它的功能就是爲邏輯層提供直接可用的數據。當前端代碼愈來愈重時,TransformerRequest部分能夠很方便地移動到中間層。

4. 善用computed和filters處理數據展現

對原始數據的轉換並不能覆蓋全部場景,這就須要在定製展現的場景中利用computedfilters,它們均可以用來在不改變數據的狀況下更改展現結果,例如將數據中的0或1轉換爲未完成已完成,或者是將時間戳和當前時間做比較後改成可讀性更高的剛剛,1分鐘前,1小時前,1天前等等,這些開發場景中是不能採用強行賦值來處理的,這是就可使用計算屬性computed或過濾器filters來處理,它們的區別是computed通常用於組件內部,不具備通用性,而filters通常用於可複用的場景,能夠經過下面的形式來定義一個展現效果爲首字母大寫的全局過濾器:

Vue.filter('capitalize', function (value) {
  if (!value) return '';
  value = value.toString();
  return value.charAt(0).toUpperCase() + value.slice(1);
})

當項目中使用vuex來進行狀態管理時,computed一般會等價替換爲state中的getter

5. 使用directive處理DOM操做

儘管Vue提供了refs這個接口來實如今邏輯層直接操做DOM,但咱們應當儘量避免將複雜的DOM操做放在這裏,有時候頁面上DOM變化的場景較多,將每一個變化都使用數據驅動的方式顯然是不合理的,這時就須要用到指令特性directive,它經常使用來補充實現一些業務邏輯無關的DOM變化(業務邏輯相關的變化大都經過數據綁定進行了自動關聯)。directive的基本用法能夠直接參考【官方指南】,須要注意的是許多初級開發者都不太在乎內存泄漏的問題,在directive的使用中須要格外注意這一點,一般咱們會在bind事件鉤子中綁定事件並使用屬性持有這個監聽函數,並在unbind鉤子中解除對同一個監聽函數的綁定,即便沒有使用自定義指令,你也須要創建在必要時解綁監聽器的編碼習慣:

Vue.directive('clickoutside',{
      bind:function (el, binding){
          //定義監聽器
          function handler(e) {
              if (el.contains(e.target)) {
                  return false;
              }
              if (binding.expression){
                  binding.value(e);
              }
          }

          el.__clickOutSide__ = handler;
          document.addEventListener('click', handler);
      },
      unbind:function (el) {
          document.removeEventListener('click',el.__clickOutSide__);
          delete el.__clickOutSide__ ;
      }
  });

demo中提供了一個簡單的directive示例,你能夠用它來作練習。

相關文章
相關標籤/搜索