爲什麼前端要使用框架?(小白向)

親愛的觀衆老婆們好~最近在團隊裏組織了一次內容關於React的分享,然然後端有同窗反映未能理解爲什麼前端須要使用框架,框架究竟解決了什麼問題。javascript

回想剛入坑前端的時候,也接觸了一些PHP,當了解到其實PHP能夠生成HTML模板以後,對前端的信仰幾乎崩潰。既而後端就能夠渲染前端的話,前端價值到底在哪?感受頁面就是隨便切一下就行,根本不必用框架,當時着實迷茫了好久。前端

後來對前端領域接觸深了,再通過大神的指點,總算是理解爲什麼前端須要使用框架。根據本身的理解,再結合分享時後端同窗的疑問,因而有了這篇文章。本文相對小白向,只是經過虛擬一個項目說明問題,還望各位大神不吝賜教。java

開始新項目

產品: 來來來,新項目來啦。最近發現用戶喜歡擼貓,咱們來個在線雲擼貓!頁面要求有貓圖,點一下計數加一點當擼了一次!多久能上線??後端

這個簡單啊!
設計模式

厲害的我連jQuery都不用,原生上!bash

<body>
    <img src="1.jpg" alt="">
    <p>0</p>
    <script>
        const img = document.querySelector('img');
        const p = document.querySelector('p');
        let count = 0;
        img.addEventListener('click', function() {
          count += 1;
          p.innerHTML = count;
        })
    </script>
</body>複製代碼

簡單易懂,對不對!閉包

需求變動

產品: 線上大受歡迎,然而要改需求了爸爸!如今要兩隻喵,點擊要分開算哦,各自顯示就好。你最棒了,我知道你確定能夠的!app

看在叫了爸爸的份上,仍是給他改一下吧。然而第一個版本多粗糙啊,各類污染全局變量,第二版我要寫得棒棒的!框架

<body>
<section class="catSection">
    <img src="cat1.jpg" alt="" class="catImg">
    <p class="catCount">0</p>
</section>
<section class="catSection">
    <img src="cat2.jpg" alt="" class="catImg">
    <p class="catCount">0</p>
</section>
<script>
    (function() {
      const catSection = document.querySelectorAll('.catSection');
      catSection.forEach((section) => {
        let count = 0;
        const catImg = section.querySelector('.catImg');
        const catCount = section.querySelector('.catCount');
        catImg.addEventListener('click', () => {
          count++;
          catCount.innerHTML = count;
        })
      })
    })()
</script>複製代碼

沒有污染全局變量,還能夠擴展需求,後續產品就算要求再多的貓我都能hold住!
函數

再一次需求變動

產品: 父皇,你聽我說啊,就最後一次,真的最後一次。如今貓兩隻不夠啊,但太多的話展現又很差看。如今需求作一個列表,列表有N只貓,點那隻貓展現那隻貓,每次只展現意志,並且貓要有對應的名字哦,點擊也要分開統計!其實跟之前差很少?就改改就行了。明天就要哦!

我封裝得好好的功能,你跟我說改需求?並且改得面目全非跟我說差很少?

然而,吐槽歸吐槽,需求仍是要作的。並且需求明天就要,代碼寫得再好也可能立刻改功能,代碼仍是實現功能就算了吧。

具體代碼由各位看官本身實現(建議先停下來,動手去實現這個需求),這裏我就再也不上代碼了。極可能咱們此次寫的代碼,就不會太考慮什麼全局變量污染,也不考慮封裝的問題,逐漸趨向於實現功能就行了,由於需求太多了,時間和精力限制了去寫「好」代碼。

固然了,產品經理說需求最後一次改,都是騙人的。下次可能會有點擊到某個值時,自動切換貓,動態添加貓等等的新需求。咱們如今這樣組織代碼的形式,是典型的」意大利麪式「代碼(簡單說,就是各類東西整合在一塊兒,層級不分,難以維護)。這種代碼寫起來直觀,但往後的維護是至關難的。寫出上述例子後,不妨隔兩天再去看看可否輕易理解那一份代碼。本身寫的代碼尚且如此,他人的更不在話下了。

那麼,這種隨着項目愈來愈複雜,邏輯愈來愈多,咱們該怎麼寫代碼呢?

更好地組織代碼

前端其實有一種說法:咱們如今的」新「東西,都是其餘領域玩過的。雖然看起來很氣人,但這也是事實。當現狀不知如何處理時,不妨參考下其餘領域的解決方案。離前端比較近的就是後端了,那麼後端是怎麼管理的呢?最典型的設計就是MVC了。那麼,前端能不能借鑑呢?

說幹就幹,以上面的需求爲例,咱們試着用MVC的方式組織一下代碼,看下和你剛纔寫的有什麼不一樣。

先來M,也就是Model,數據層,對外提供接口能夠獲取相關的數據。這麼組織的話,是否是蠻好懂的:

const model = (function() {

  //相關數據
  const _model = {
    catLists: [
      {
        src: '1.jpg',
        name: 'cat1',
        count: 0
      },
      {
        src: '2.jpg',
        name: 'cat2',
        count: 0
      }
    ],
    targetCatIndex: 0, //目前可被點擊的是哪知喵在catLists中的索引
  };

  //獲取getCatLists
  function getCatLists() {
    return _model.catLists;
  }

  //獲取目標對象
  function getTargetCatObj() {
    return _model.catLists[_model.targetCatIndex];
  }
  //修改targetCatIndex
  function setTargetCatIndex(name) {
    _model.catLists.some((catObj, index) => {
      if (catObj.name === name) {
        _model.targetCatIndex = index;
        return true
      }
    })
  }

  //目標對象點擊數+1
  function addTargetCatCount() {
    const catObj = getTargetCatObj();
    catObj.count += 1;
  }

  return {
    getCatLists,
    getTargetCatObj,
    setTargetCatIndex,
    addTargetCatCount
  }
})();複製代碼

在自執行函數中,設計了一個對象命名爲_model,經過閉包存儲它。自執行函數返回一個對象,其中包含四個函數。四個函數執行後,能夠返回或修改_model中對應的數據。經過註釋看其實仍是挺清楚的。

跟着是V,也就是view層,負責頁面渲染。這個可能複雜一點,但不想把它弄得太繁瑣,不如就兩個方法吧。就一個初始化的init()和負責更新視圖的render()方法就好啦。

先肯定HTML模板:

<ul class="catList"></ul>
<section class="clickArea">
    <img src="" class="catImage">
    <p class="catCount"></p>
    <p class="catName"></p>
</section>複製代碼

再組織一下view層的代碼:

const view = (function() {

  //獲取各個須要操做的DOM節點
  const img = document.querySelector('.catImage');
  const name = document.querySelector('.catName');
  const count = document.querySelector('.catCount');

  //初始化頁面
  function init(catLists, targetObj) {
    const list = document.querySelector('.catList');
    const fragment = document.createDocumentFragment();
    //爲ul添加對應的li
    for (let i = 0, len = catLists.length; i < len; i++) {
      const li = document.createElement('li');
      const name = catLists[i].name;
      li.innerHTML = name;
      li.addEventListener('click', function() {
        //以後會有controller相關的代碼,其實就是換一隻可點擊的喵
        controller.changeTargetCat(name);
      });
      fragment.appendChild(li);
    }
    list.appendChild(fragment);
    img.addEventListener('click', function() {
      //以後會有controller相關的代碼,其實就是計數+1
      controller.addCount(name);
    });
    render(targetObj);
  }

  //從新渲染頁面
  function render(targetObj) {
    img.src = targetObj.src;
    name.innerHTML = targetObj.name;
    count.innerHTML = targetObj.count;
  }

  return {
    init,
    render
  }
})();複製代碼

view層的代碼其實也很簡單的,和model層的套路差很少,經過自執行函數結合閉包存儲以後要操做的節點,對外暴露由兩個方法組成的對象,分別是initrenderinit用於初始化話頁面,render用於從新渲染頁面。裏面調用了controller,其實就是以後要介紹的controller。

最後是C層,也就是controller,主要是用於邏輯相關的處理,算是整個設計裏面的大腦。不過因爲這項目比較簡單,因此代碼反而是最簡單的:

const controller = {
  addCount(name) {
    //經過model的接口增長目標對象的計數
    model.addTargetCatCount(name);
    controller.renderView();
  },
  changeTargetCat(name) {
    //經過model的接口修改目標索引
    model.setTargetCatIndex(name);
    controller.renderView();
  },
  init() {
    //經過model的接口獲取相關數據
    const { getCatLists, getTargetCatObj } = model;
    //傳參並命令view層初始化
    view.init(getCatLists(), getTargetCatObj());
  },
  renderView() {
    //傳參並命令view層從新渲染
    view.render(model.getTargetCatObj());
  }
};複製代碼

controller的設計實際上是比較簡陋的,只是一個包含了四個方法的對象。其中addCount對應點擊加一的操做,changeTargetCat對應換貓的操做。上述兩個方法實際上是改變了數據的,而只要數據發生了變化,一概調用renderView從新渲染。

至此主要代碼已經寫完了,以後調用一下controller.init();,就能夠開心的擼貓,完成需求了。

若是以前你動手實現了上述需求的話,不妨對比一下咱們設計代碼上的差異。也許你寫的代碼仍是以前那種「麪條式」代碼,但它也是可用的啊,並且還不用這麼多代碼呢,長得也還算能維護的樣子,爲什麼要用這種繁瑣的方式去阻止代碼呢?

然而,按照以前說的,產品可能提更多的需求,下次可能會有點擊到某個值時,自動切換貓,動態添加貓等等的新需求時,你如今的代碼組織形式可否很快地完成需求?當往後修改某些需求時,不當心觸發了潛藏的bug(常常有的狀況),又是否能快速定位出問題並快速改好呢?

「麪條式」代碼常常是數據、視圖與處理邏輯耦合起來的,很容易牽一髮而動全身,當業務至關複雜的時候,開發可能還好說,維護簡直是不可想象的。而你可能已經觀察到了,遵循MVC設計的代碼,雖然繁瑣,但各層是徹底分開的,儘管數據與視圖能夠直接調用對方的接口進行交互,但都是必須經過控制層來作統一處理。數據、視圖與處理邏輯解耦以後,代碼的可閱讀性與可維護性都是一個飛躍。經過犧牲空間(多寫代碼)來換取代碼的可維護性與可拓展性,這是一筆劃算的買賣。

小結

說了半天,好像尚未說出爲什麼前端須要使用框架。然而在複雜的項目中,你贊成我經過MVC組織代碼會比「麪條式」代碼好嗎?若是贊成的話,將我剛纔代碼中不變的部分抽象起來(組件通信、報錯處理等),千方百計提升渲染的性能(使用Virtual Dom),若是認爲前端和其餘不同,數據和視圖仍是能夠進行受控的交互(即MVVM),那麼整合起來,不就是一個框架了嗎?

其實必需要認可,人的腦力是有限的,一款產品的需求多是無限的。當這款產品已經讓你沒法掌握每一個細節,每位參與開發的同窗只能掌握局部細節,而其餘部分只管調用是必然的事實。可是,如何肯定其餘部分可信,調用不會出bug呢?這時候就該使用框架了。框架較大程度上能約束與規範每位開發者的行爲,不按照框架的規定極可能就會報錯,這樣多人協做就有了基本的保證。

可是,不是說使用框架就是最佳實踐。當項目不復雜的時候(好比一次性的活動頁),咱們有足夠能力去掌握項目的細節,那麼使用框架反而不是好的選擇。畢竟再好的框架在性能上都會有損失,而被框架的條條框框約束着,也老是使人不喜的。

簡單地說:腦子不夠,框架來湊;本身組織很差代碼,靠框架來給咱們組織。閱讀至此殊爲不易,感謝各位看官,但願本文對你有一點幫助,謝謝!

參考資料

JavaScript 設計模式

相關文章
相關標籤/搜索