当前前端最火热的框架当属 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。