MVC的故事

MVC的故事

現(xiàn)在的前端學(xué)習(xí)者,會有一個斷層酪穿,知識的斷層凳干,為什么vue, react 會出現(xiàn),好像他一開始就出現(xiàn)在哪里一樣

所謂的老前端被济,眼見這些年前端怎么發(fā)展救赐,怎么逐步完善,怎么逐步變成今天所謂的框架

然而只磷,在一定程度上经磅,兩者表現(xiàn)上看起來沒有什么區(qū)別泌绣,對一些前端新人來說,他們更年輕更有精力预厌,用相對成熟的vue阿迈,react擰著螺絲的經(jīng)歷甚至比老前端更為豐富。

當(dāng)然配乓,這只是表象仿滔,當(dāng)脫離了所謂的框架,新人還會寫出具有mvc思想的代碼嗎犹芹?

前端MVC的開始

2010 年崎页,Backbone.js 第一版發(fā)布,它是基于MVX思想的(X可以是任何東西)腰埂,那時候用Backbone的人飒焦,用意大利面條來形容沒有組織的代碼,因為這些代碼長長短短屿笼,還互相交織牺荠,你中有我,我中有你驴一。
然而似乎在三年后國內(nèi)休雌,才開始使用這個東西「味希可見英文世界的前端知識杈曲,一直都是領(lǐng)先于中文世界的。
當(dāng)然現(xiàn)在Backbone已經(jīng)沒人用了胸懈。 大家從backbone到了ng到了vue.js 0.8

意大利面條代碼: 如果不從第一行看到最后一行担扑,就不知道干什么的

function fetchDb() {
  return axios.get('/books/1')
}

function saveDb(newData) {
  return axios.put('/books/1', newData)
}

var template = `
<div>
  書名:《__name__》,
  數(shù)量:<span id="number">__number__</span>
</div>
<div class="actions">
  <button id="increaseByOne">加1</button>
  <button id="decreaseByOne">減1</button>
</div>
`

fetchDb().then((response) => {
  let result = response.data
  $('#app').html(
    template.replace('__number__', result.number)
      .replace('__name__', result.name)
  )

  //加1
  $('#increaseByOne').on('click', (e) => {
    let oldResult = parseInt($('#number').text(), 10)
    let newResult = oldResult + 1
    saveDb({number: newResult}).then(function({data}) {
      console.log(data)
      $('#number').text(data.number)
    })
  })
  
  //減1
  $('#decreaseByOne').on('click', (e) => {
    let oldResult = parseInt($('#number').text(), 10)
    let newResult = oldResult - 1
    saveDb({number: newResult}).then(({data}) => {
      $('#number').text(data.number)
    })
  })

如果一段代碼趣钱,你從頭到尾一行行看的很流暢官扣,額那這基本就是面條代碼了

MVC來了

一些高端的程序員發(fā)現(xiàn)骤视,意大利面條代碼總是可以分為三類:

  1. 專門操作遠程數(shù)據(jù)的代碼(fetchDb 和 saveDb 等等)
  2. 專門呈現(xiàn)頁面元素的代碼(innerHTML 等等)
  3. 其他控制邏輯的代碼(點擊某按鈕之后做啥的代碼)

為什么分成這三類呢?因為我們前端抄襲了后端的分類思想妨马,后端代碼也經(jīng)常分為三類:

  1. 專門操作 MySQL 數(shù)據(jù)庫的代碼
  2. 專門渲染 HTML 的代碼
  3. 其他控制邏輯的代碼(用戶請求首頁之后去讀數(shù)據(jù)庫棒旗,然后渲染 HTML 作為響應(yīng)等等)

這些思路經(jīng)過慢慢的演化场航,最終被廣大程序員完善為 MVC 思想唱蒸。

  1. M 專門負(fù)責(zé)數(shù)據(jù)
  2. V 專門負(fù)責(zé)表現(xiàn)
  3. C 負(fù)責(zé)其他邏輯

如果我們來反思一下敷矫,會發(fā)現(xiàn)這個分類是無懈可擊的:

  1. 每個網(wǎng)頁都有數(shù)據(jù)
  2. 每個網(wǎng)頁都有表現(xiàn)(具體為 HTML)
  3. 每個網(wǎng)頁都有其他邏輯
于是乎,MVC 成了經(jīng)久不衰的設(shè)計模式(設(shè)計模式就是「套路」的意思)

讓我們看看以上代碼經(jīng)過MVC洗滌后變成什么樣子:

let model = {
  data: {
    number: 0,
    name: ''
  },
  fetch(id) {
    return axios.get(`/books/${id}`).then((response)=>{
      this.data = response.data
    })
  },
  update(newData) {
    let id = this.data.id
    return axios.put(`/books/${id}`, newData).then(({data})=>{
      this.data = data
    })
  }
}

let view = {
  el: '#app',
  template: `
    <div>
      書名:《__name__》低矮,
      數(shù)量:__number__
    </div>
    <div class="actions">
      <button id="increaseByOne">加1</button>
    </div>
  `,
  render(data) {
    let html = this.template.replace('__name__', data.name)
      .replace('__number__', data.number)
    console.log(data)
    $(this.el).html(html)
  }
}

let controller = {
  init({ view, model}) {
    this.view = view
    this.model = model
    this.view.render(this.model.data)
    this.bindEvents()
    console.log(1)
    this.fetchModel()
    console.log(2)
  },
  events: [
    { type: 'click', selector: '#increaseByOne', fnName: 'add' },
    { type: 'click', selector: '#decreaseByOne', fnName: 'minus' },
    { type: 'click', selector: '#square', fnName: 'square' },
    { type: 'click', selector: '#cube', fnName: 'cube' },
  ],
  bindEvents() {
    this.events.map((event)=>{
      $(this.view.el).on(event.type, event.selector, this[event.fnName].bind(this))
    })
  },
  add(){
    let newData = {number: this.model.data.number + 1}
    this.updateModel(newData)
  },
  fetchModel(){
    this.model.fetch(1).then(() => {
      this.view.render(this.model.data)
    })
  },
  updateModel(newData){
    this.model.update(newData).then(()=>{
      this.view.render(this.model.data)
    })
  }
}

是不是看命名就很MVC

它改進了以下幾點:

  1. 把意大利面條變成三塊有結(jié)構(gòu)有組織的對象:model、view 和 controller
  2. model 只負(fù)責(zé)存儲數(shù)據(jù)被冒、請求數(shù)據(jù)军掂、更新數(shù)據(jù)
  3. view 只負(fù)責(zé)渲染 HTML(可接受一個 data 來定制數(shù)據(jù))
  4. controller 負(fù)責(zé)調(diào)度 model 和 view
看起來代碼變多了轮蜕,但是我們似乎寫了很多公共方法,可以用類封裝起來蝗锥,成為所謂的模版代碼(俗稱面向?qū)ο螅?/h5>
  let model = new Model()
  let controller = new Controller()
  let view = new View()

等等跃洛! 好像發(fā)現(xiàn)了新姿勢, 好像讀音都一樣的

   let view = new View() ===  let view = new Vue()

沒有錯,其實vue就是這么來的终议。

var view = new Vue({
  el: '#app',
  data: {
    name: 'vue',
  },
  template: `<div id='app'>hello {{data.name}}</div>`
})

雙向綁定

其實仔細(xì)看看汇竭,以上改進的mvc代碼有一個很嚴(yán)重的BUG,每次 render 都會更新 #app 的 innerHTML穴张,這可能會丟失用戶的寫在頁面某個 input 里面的數(shù)據(jù)细燎。

這有兩個解決辦法:

  1. 用戶只要輸入了什么,就記錄在 JS 的 data 里(數(shù)據(jù)綁定的初步思想出現(xiàn)了)
  2. 不要粗暴的操作 innerHTML皂甘,而是只更新需要更新的部位(虛擬 DOM 的初步思想出現(xiàn)了)

后來的后來玻驻,vue0.8出現(xiàn)了,他就是簡化版的Angular偿枕,他借鑒了Angular的雙向綁定思想璧瞬,真的就把new View變成了new Vue

Vue 的雙向綁定(也是 Angular 的雙向綁定)有這些功能:

  1. 只要 JS 改變了 view.number 或 view.name 或 view.n (注意 Vue 把 data 里面的 number、name 和 n 放到了 view 上面渐夸,沒有 view.data 這個東西)嗤锉, HTML 就會局部更新
  2. 只要用戶在 input 里輸入了值,JS 里的 view.n 就會更新

看起來很魔法墓塌,其實還是思想瘟忱,其實我們自己也可以輕松實現(xiàn)個雙向綁定。不就是檢測到數(shù)據(jù)變動后桃纯,自動render了一次酷誓,對吧。發(fā)布訂閱者模式了解一下

Vue 還有很多其他功能态坦,使得 Controller 顯得多余:

  created(){ },
  methods: {
    add() {}
  }

從此以后盐数,事情變得容易了很多。 vue已經(jīng)magic一般的幫我們實現(xiàn)了雙向綁定伞梯,你也了解了雙綁的原理玫氢,用起來游刃有余。人們稱之為MVVM

單向綁定

又有一批程序員谜诫,他們發(fā)現(xiàn)「雙向綁定」有一點點反「組件化」漾峡,在跨組件的雙向綁定變得很奇怪。于是喻旷,他們發(fā)明了單向綁定生逸,在跨組件時使用,保證了數(shù)據(jù)的流向,讓數(shù)據(jù)變得可控槽袄,組件耦合度降低烙无。

但是不得不提,局部使用雙向綁定是非常爽的遍尺。這就是為什么我們在用react的時候截酷,input還是喜歡讓他雙綁起來更舒服一點。

雙綁怎么玩

順帶提一提雙向綁定乾戏,其實也走過了好幾代迂苛。

  1. dirty check,也就是將觸發(fā)數(shù)據(jù)變化的onChange改寫了鼓择,在改寫的onChange中render(Angular 1.X)
  2. getter setter, 利用ECMAScript 2015中提出的getter setter三幻,可以輕松實現(xiàn)在get和set Value的時候順便render一下。 但有一個Bug惯退,你無法監(jiān)聽不存在的key (Vue)
  3. proxy赌髓, 利用ECMAScript 2017中新增的proxy,可以為對象定義基本操作的自定義行為催跪,也可以完美解決上一代setter的bug锁蠕,下一代Vue就要用這個重寫了哦
好吧,到這兒故事講完了懊蒸。有啥用荣倾,理解MVX后,擰螺絲更加自信了唄骑丸。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末舌仍,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子通危,更是在濱河造成了極大的恐慌铸豁,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件菊碟,死亡現(xiàn)場離奇詭異节芥,居然都是意外死亡,警方通過查閱死者的電腦和手機逆害,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門头镊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人魄幕,你說我怎么就攤上這事相艇。” “怎么了纯陨?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵坛芽,是天一觀的道長留储。 經(jīng)常有香客問我,道長靡馁,這世上最難降的妖魔是什么欲鹏? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮臭墨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘膘盖。我一直安慰自己胧弛,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布侠畔。 她就那樣靜靜地躺著结缚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪软棺。 梳的紋絲不亂的頭發(fā)上红竭,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天,我揣著相機與錄音喘落,去河邊找鬼茵宪。 笑死,一個胖子當(dāng)著我的面吹牛瘦棋,可吹牛的內(nèi)容都是我干的稀火。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼赌朋,長吁一口氣:“原來是場噩夢啊……” “哼凰狞!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起沛慢,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤赡若,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后团甲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逾冬,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年伐庭,在試婚紗的時候發(fā)現(xiàn)自己被綠了粉渠。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡圾另,死狀恐怖霸株,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情集乔,我是刑警寧澤去件,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布坡椒,位于F島的核電站,受9級特大地震影響尤溜,放射性物質(zhì)發(fā)生泄漏倔叼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一宫莱、第九天 我趴在偏房一處隱蔽的房頂上張望丈攒。 院中可真熱鬧,春花似錦授霸、人聲如沸巡验。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽显设。三九已至,卻和暖如春辛辨,著一層夾襖步出監(jiān)牢的瞬間捕捂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工斗搞, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留指攒,地道東北人。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓榜旦,卻偏偏與公主長得像幽七,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子溅呢,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,077評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 大家好澡屡,我是IT修真院深圳分院第3期的學(xué)員,一枚正直純潔善良的前端程序員咐旧,今天給大家分享一下驶鹉,修真院官網(wǎng)前端工程師...
    大大頭大閱讀 6,067評論 0 4
  • 一:什么是閉包?閉包的用處铣墨? (1)閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)室埋。在本質(zhì)上,閉包就 是將函數(shù)內(nèi)部和函數(shù)外...
    xuguibin閱讀 9,620評論 1 52
  • 曾經(jīng)在訓(xùn)練所的AM認(rèn)證的課程聽到一句話:「把自己定位成顧問伊约、問題解決者姚淆,而不是推銷東西的業(yè)務(wù)÷怕桑」 覺得這句話說的很...
    rusty6kimo閱讀 184評論 0 1
  • 安裝 1. Update Homebrew’s package database. brew update 2. ...
    ROBIN2015閱讀 318評論 1 1
  • 有幾個關(guān)系比較好的舍友腌逢,現(xiàn)在我們都已經(jīng)畢業(yè)一年了。不例外的被家長拖到相親的行列了超埋,說起來我們宿舍也挺奇葩的搏讶,整個宿...
    清風(fēng)一笑閱讀 309評論 0 0