當前前端最火熱的框架當屬 VUE,在學習 VUE 以前先來看下 VUE 的內部是如何工做的。html
咱們從最基本的頁面操做開始作起。前端
咱們來實現一個頁面,當點擊按鈕式,頁面上的數字增長或減小ios
<div class="app"> <div class="book"> 書籍:《JavaScript高級程序設計》 數量:<span class='number'>2</span> </div> <button class='addOne'>加1</button> <button class='minusOne'>減1</button> <button class='reset'>清零</button> </div>
用 jQuery 操做它很容易實現需求ajax
let log = console.log.bind(console) //把console.log 替換成 log 少打點代碼 $('.addOne').on('click',()=>{ let oldHtml = $('.number').text() let newHtml = oldHtml -0 +1 $('.number').html(newHtml) }) $('.minusOne').on('click',()=>{ let oldHtml = $('.number').text() let newHtml = oldHtml -0 -1 $('.number').html(newHtml) }) $('.reset').on('click',()=>{ $('.number').text('0') })
咱們真實的需求是,當點擊按鈕時,操做的時數據庫裏的數據,而不是直接在頁面中操做。數據庫
這裏引入一個庫axios
,能夠實如今前端模擬後臺,它有一個重要的 API:interceptors
,能夠實如今它上面 Mock 數據axios
// 咱們要的數據 let book = { name:'JavaScript高級程序設計', number:2, id:'' } axios.interceptors.response.use((response)=>{ //下面這句等價於 let {url,method,data} = response.config let {config:{url,method,data}} = response // 這裏的 data 是請求體 if(url === '/book/1' && method === 'get'){ response.data = book //這裏的 data 是響應體 }else if(url === '/book/1' && method === 'put'){ data = JSON.parse(data) Object.assign(book,data) //請求體 data,assign可實現局部更新 response.data = book //響應體 data } return response })
頁面中的數據咱們應該用佔位符代替,數據獲取到以後 更新到頁面中服務器
//剛進入頁面後的數據加載 axios.get('/book/1').then(({data})=>{ let oldHtml = $('.app').html() let newHtml = oldHtml.replace('__name__',data.name) .replace('__number__',data.number) //用真實數據替換佔位符 $('.app').html(newHtml) }) $('.app').on('click','.addOne',()=>{ let oldNumber = $('.number').text() let newNumber = oldNumber -0 +1 axios.put('/book/1',{number:newNumber}).then(({data})=>{ //請求時更新最新數據 $('.number').html(data.number) }) }) $('.app').on('click','.minusOne',()=>{ let oldNumber = $('.number').text() let newNumber = oldNumber -0 -1 axios.put('/book/1',{number:newNumber}).then(({data})=>{ $('.number').html(data.number) }) }) $('.app').on('click','.reset',()=>{ axios.put('/book/1',{number:0}).then(({data})=>{ $('.number').html(data.number) }) })
這樣的意大利麪條似的寫法,很是不利於後期維護,咱們應該用 MVC 優化下app
獲取數據,更新數據的事情交個model
去作,model
裏面有三個屬性:data
,fetch
,updata
;分別用來:data
負責存儲最新數據,fetch
負責頁面加載時向服務器獲取數據,並將數據存儲到data
中,updata
負責實時頁面操做時,更新頁面數據,並將最新數據保存到data
中。框架
let model ={ data:{ //model 內部用來存儲數據 name:'', number:0, id:'' }, fetch(id){ return axios.get(`/books/${id}`).then((response)=>{ this.data = response.data //加載更新向 axios 獲取的數據 return response }) }, updata(id,data){ return axios.put(`/books/${id}`,data).then((response)=>{ this.data = response.data //點擊按鈕向 axios 獲取最新數據,請求中的 data 是最新數據 return response }) } }
操做頁面交給view
,view
有三個屬性,分別是el
、template
、render
;el
負責視圖部分,也就是你須要操做的 DOM,template
是虛擬的html
,並經過render
去渲染。函數
let view = { el:'.app', template:` <div> <div class="book"> 書籍:《__name__》 數量:<span class='number'>__number__</span> </div> <button class='addOne'>加1</button> <button class='minusOne'>減1</button> <button class='reset'>清零</button> </div>`, render(data){ let newHtml = this.template.replace('__name__',data.name) .replace('__number__',data.number) //把佔位符替換成數據 $(this.el).html(newHtml) } }
事件相關的交給controller
操做,有兩個重要的屬性:init
、bingEvents
;初始化時須要傳入兩參數view
和model
,後面操做的都是在的view
、model
都是在controller
身上,而不是直接操做model
。
let controller = { init({view,model}){ this.view = view this.model = model this.bindEvents() this.model.fetch(1).then(()=>{ view.render(this.model.data) }) }, bindEvents(){ $(this.view.el).on('click','.addOne',this.addOne.bind(this)) //這裏 addOne 內部的 this 應該是點擊的那個元素,因此這裏要綁一下 this $(this.view.el).on('click','.minusOne',this.minusOne.bind(this)) $(this.view.el).on('click','.reset',this.reset.bind(this)) }, addOne(){ console.log(1) let oldNumber = $('.number').text() console.log(2) let newNumber = oldNumber -0 +1 console.log(3) this.model.updata(1,{number:newNumber}).then(()=>{ $('.number').html(this.model.data.number) }) console.log(4) }, minusOne(){ let oldNumber = $('.number').text() let newNumber = oldNumber -0 -1 this.model.updata(1,{number:newNumber}).then(()=>{ $('.number').html(this.model.data.number) }) }, reset(){ this.model.updata(1,{number:0}).then(()=>{ $('.number').html(this.model.data.number) }) } } controller.init({view:view,model:model})
如今是一個頁面,這也寫沒有關係,但若是有不少頁面,每一個頁面中的view
、model
、controller
都重複了,這裏把一些公用的方法寫在原型上。
在頁面中使用model
,只須要傳遞兩參數
function Model({data,resouce}){ this.data = data this.resouce = resouce } Model.prototype.updata = function(id,data){ return axios.put(`/${this.resouce}s/${id}`,data).then((response)=>{ this.data = response.data return response }) } Model.prototype.fetch = function(id){ return axios.get(`/${this.resouce}s/${id}`).then((response)=>{ this.data = response.data return response }) } let model = new Model({ data:{ name:'', number:0, id:'' }, resouce:'book' })
view
也是,頁面使用時,傳兩個參數就 ok 了
function View({el,template}){ this.el = el this.template = template } View.prototype.render = function(data){ let html = this.template for(let key in data){ //遍歷傳進來的參數,用循環替換頁面中的佔位符 html = html.replace(`__${key}__`,data[key]) } $(this.el).html(html) } let view = new View({ el:'.app', template:` <div> <div class="book"> 書籍:《__name__》 數量:<span class='number'>__number__</span> </div> <button class='addOne'>加1</button> <button class='minusOne'>減1</button> <button class='reset'>清零</button> </div>` })
Controller
公用的方法比較少,這裏就沒有優化了
理解了 MVC 以後再來看 VUE 就會很簡單,VUE 簡單來講就是 MVC 中的 V,但它和 MVC 有點區別,就是它須要model
中的數據
let view = new Vue({ el:'.app', data:{ book:{ name:'我是書籍', number:0, id:'' }, n:1 }, template:` <div> <div class="book"> 書籍:《{{book.name}}》 數量:<span class='number'>{{book.number}}</span> </div> <button class='addOne'>加1</button> <button class='minusOne'>減1</button> <button class='reset'>清零</button> </div>` }
VUE 會把data
裏的屬性提高爲 Vue
的屬性,因此下面操做能夠直接用Vue.name
操做,而不是寫Vue.data.name
,因此咱們能夠在這些屬性外面套一層book
,用Vue.book
就能夠對這些屬性進行批量操做。
Vue
沒有render
方法,那你會說它怎麼實現渲染頁面呢?
它提供了一個叫created
的方法,在裏面直接修改Vue
的data
屬性,它就會自動幫你渲染頁面
created(){ model.fetch(1).then(()=>{ this.book = model.data })
固然 VUE 的野心不止於此 ,它甚至幫你省下controllor
,你都不須要進行事件綁定
<button class='addOne' v-on:click="addOne">加1</button> <button class='minusOne' v-on:click="minusOne">減1</button> <button class='reset' v-on:click="reset">清零</button>
它在template
,v-on:click
的一個方法,它會幫你調用methods
中的方法,你只須要將點擊執行的函數寫在上面便可。
methods:{ addOne(){ model.updata(1,{number:this.book.number + (this.n-0)}) .then(()=>{ this.book = model.data }) }, minusOne(){ model.updata(1,{number:this.book.number - (this.n-0)}) .then(()=>{ this.book = model.data }) }, reset(){ model.updata(1,{number:0}) .then(()=>{ this.book = model.data }) } }
學會了 MVC 以後在來看 VUE,就變的很簡單
VUE 還實現另外一個雙向綁定的功能,我如今點擊按鈕只能+1
或-1
,若是我要實現操做+n
或減n
呢?
這裏用input
實現,在按鈕上面添加一行
<div> <input v-model='n'>N的值是<span>{{n}}</span> </div>
固然Vue
的data
中也要添加一個n
當你在input
中輸入相應值時,後面N的值會相應變化
,這就是 MVVM。