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

来源:https://www.cnblogs.com/dashnowords/archive/2019/08/14/11354469.html
-Advertisement-
Play Games

示例代碼托管在: "http://www.github.com/dashnowords/blogs" 博客園地址: "《大史住在大前端》原創博文目錄" 華為雲社區地址: "【你要的前端打怪升級指南】" [TOC] 分享一篇尤大大演講鎮樓: "「2019 JSConf.Asia 尤雨溪」在框架設計中尋 ...


目錄

示例代碼托管在:http://www.github.com/dashnowords/blogs

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

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

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

一.框架的定位

框架通常只是一種設計模式的實現,它並不意味著你可以在開發中避免所有分層設計工作。

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

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

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

二. 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示例,你可以用它來做練習。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本視頻為 Google Flutter 團隊的軟體工程師 Xiao Yu 在 2018 谷歌開發者大會做的演講,演講題目是《Flutter 的性能測試和理論》。 這個視頻里將會通過近半個小時的視頻和演示帶大家瞭解 Flutter 應用渲染時的時間消耗,瞭解這些之後會更好的幫助開發者們發現應用的性能問 ...
  • 前兩天在論壇上看到一個問題,大意是怎麼在UBUNTU下使用NDK r20編譯FFmpeg。我第一反應是不該用r20,因為我在很早前用過沒有gcc版本的NDK,發現有很多問題不能編譯,就立馬回覆了個使用r12b來編譯。回到家我細想了一下,如果我就是想要用r20的NDK編譯最新的FFmpeg呢?不如我們 ...
  • 文章轉載自:http://www.pythonheidong.com/blog/article/3310/ 註:本文來自“安卓公車” Android面試題 1. 下列哪些語句關於記憶體回收的說明是正確的? (b ) A、 程式員必須創建一個線程來釋放記憶體 B、 記憶體回收程式負責釋放無用記憶體 C、 記憶體 ...
  • 文章轉載自:http://www.pythonheidong.com/blog/article/3307/ 上一篇文章列出了共32道IOS面試題: http://www.cnblogs.com/fkdd/archive/2012/03/13/2394724.html 下麵從第一題開始解答: 題目:1 ...
  • Javascript 有一個 main thread 主線程和 call-stack 調用棧(執行棧),所有的任務都會被放到調用棧等待主線程執行。 JS調用棧採用的是後進先出的規則,當函數執行的時候,會被添加到棧的頂部,當執行棧執行完成後,就會從棧頂移出,直到棧內被清空。 Javascript單線程 ...
  • 一、CSS顯示模式​ 1.在HTML中HTML將所有的標簽分為兩類,分別是容器類和文本級。在CSS中CSS也將所有的標簽分為兩類,分別是塊級元素和行內元素 2.什麼是塊級元素呢?什麼是行內元素​? (1)塊級元素會獨占一行 (2)行內元素不會獨占一行 3.塊級元素 p div h ul ol dl ...
  • 作為一個致力於前端開發的人員,能夠熟練掌握javascript的原理和機制是每個小白的必經之路,這也是最痛苦的。有人說前端功力好不好最主要的就是看對js的掌握能力,有人說十年也啃不完一門javascript。而我們能夠知道的是:javascript是每個前端學習的核心技能(想想之後的node.js、 ...
  • 今天,一個技術群里小朋友提出一個問題: 我默念心中的萬能公式,答案一下就出來了: a; 報錯(f.b is not a function); a; b; 這下子出題人產生了疑惑,你是控制台敲出來的吧! 但其實,原型鏈真的很簡單。話不多說,開始表演! 首先,我們簡單介紹一下,實例化一個對象(new)到 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...