从MVC到MVVM

1.jpg

项目上使用 axios 的拦截器是在 jsbin.com 中进行的,同时要把jsbin的JavaScript改成es6 若是你在本地实验会发现拦截器不起做用,这是正常的。 你若是想让本地的拦截器也起做用,就须要本身写 server.js 来响应全部请求javascript

MVC是什么鬼

axios

axios是ajax的库css

如何用 axios GET 请求 /xxx?id=1html

1.axios.get('/xxx?id=1')
2.axios.get('/xxx', {id:1})
3.axios.get('/xxx', {params: {id:1}} )
4.axios({method:'get', url: '/xxx?id=1'})
5.axios('/xxx?id=1');
复制代码

之前咱们使用ajax能够这么用:前端

1.$.ajax({
    url: '/xxx'
    method: 'post'
})

2.$.post('/xxx',data)

3.$.gat('/xxx')

复制代码

使用axios的用法:vue

1.axios.post()

2.axios.get()

3.axios.put()

4.axios.patch()

5.axios.deleate()
复制代码

1.比 jQuery.ajax功能更多java

2.除了ajax功能以外,没有其余功能(更专一)node

有了替代品以后jQuery就能够死掉了ios

第一个版本不加入MVC,用JQ写

第一个版本es6

  • 为何减0,是把字符串转成numberajax

  • interceptors是拦截机,意思就是往...里面植入一个东西或者拦截一个东西

  • 在真正返回response以前使用一个函数,这个函数会对response作一个修改

  • 不作服务器,直接把前端的数据写死,也就是mock

  • 咱们有没有办法根据请求的URl来mock不一样的数据呢?能够,咱们去获取一下这个response的config就好了,config里面有一个重要的属性url,method和data注意这里的data是请求data,这里的if ...else... 仍是和nodejs很像的,像nodejs的路由

let config = response.config
 let {method,url,data} = config
复制代码

上面的2行能够用es6的解构语法把它们合并成一行,这样咱们就同时获得了4个变量分别是config,method,url,data

let {config:{method,url,data}} = response
复制代码

如今咱们就能够去伪造数据,response.data返回的就是咱们须要的数据,html中的书名和数量不能写死,要从response.data里去取

怎样把响应的数据塞到get的Promise的成功回到里呢?

能够: let data = response.data,可是用es6的语法就是回调的参数{data},就能够拿到data了,log这个data

接着就是把这个data的数据弄到html中app里面

axios.get('/book/javascript').then(({data})=>{
 let oldHtml = $('#app').html()
 let newHtml = oldHtml.replace('__name__',data.name).replace('__number__',data.number)
 $('#app').html(newHtml)
})
复制代码

这时候会发现,点击加1按钮不行了,为何由于以前的html被更改了,因此要作一下事件委托

由以前的

$('#addOne').on('click',function(){
  var oldNumber = $('#number').text() -0
  var newNumber = oldNumber + 1
  $('#number').text(newNumber)
})

$('#minusOne').on('click',function(){
  var oldNumber = $('#number').text() -0
  var newNumber = oldNumber - 1
  $('#number').text(newNumber)
})

$('#reset').on('click',function(){
  var oldNumber = $('#number').text() -0
  var newNumber = 0
  $('#number').text(newNumber)
})
复制代码

改成:

$('#app').on('click','#addOne',function(){
  var oldNumber = $('#number').text() -0
  var newNumber = oldNumber + 1
  $('#number').text(newNumber)
})

$('#app').on('click','#minusOne',function(){
  var oldNumber = $('#number').text() -0
  var newNumber = oldNumber - 1
  $('#number').text(newNumber)
})

$('#app').on('click','#reset',function(){
  var oldNumber = $('#number').text() -0
  console.log(oldNumber)
  var newNumber = 0
  $('#number').text(newNumber)
})
复制代码

它的意思就是在点击#app里面的任何一个元素的时候,只要这个元素元素符合"#addOne"(或其余)就会触发事件

上面作的内容为:用ajax发请求,再用ajax获取到数据,而后再将数据替换到页面中的

第一个版本追加:当点击加1的时候,咱们须要真实的发一个post请求去加1,而不是直接在页面上改

第一个版本追加

$('#app').on('click','#addOne',function(){
  var oldNumber = $('#number').text() -0
  var newNumber = oldNumber + 1
  axios.put('/book/javascript',{  // 加上的东西
    number: newNumber 
  })
  $('#number').text(newNumber)
})
复制代码

也就是说,先把{number: newNumer}数据push到服务器,若是push成功了,再改页面上的数据

假的服务器axios同时也要对put的作出响应,当请求的url是'/book/javascript'而且method为get的时候说明是想要获取旧的数据,就response旧的数据;当请求的路径是'/book/javascript'而且method是put的时候,确定要push一个新的数据,因而响应就要修改旧的数据,这个数据就是书的数量,全部要要有个书的变量,而且设置初始状态

assign的语法就是部分更新,asssgn就是批量的赋值,同时assign能够进行屡次赋值,后面的会覆盖前面的

回到代码中理解assign,传的是什么data(data是请求的data),assign就会把data覆盖到book上,同时book会存住数据的变化,而后把data做为数据(book上的数据)经过book返回给你,注意这里的data是字符串,要转成对象

let data = JSON.parse(data)
 Object.assign(book,data);  // 请求的data  
 response.data = book       // 响应的data
复制代码

这时候点击按钮加1,控制台就会发送一个put请求

这样基本的功能就实现了,可是代码存在一个问题,就是是意大利面条式代码,代码逻辑结构不清晰

第二个版本用MVC去改写

第二个版本

首先要把以前假的服务器的那一块包装成一个函数叫fakeData

所谓MVC就是把代码分割成3快功能职责划分明确的代码

首先改MVC的Model

声明一个model的对象,用来处理和数据相关的操做,model里面要有一个获取数据的函数fetch,一旦获取数据成功就把数据存到model上,记住要return这个response,只有return了,后面才能接着then.也要有一个更新数据的函数updata,它也和fetch同样一旦put成功就会返回一个Promise,成功了就调成功的回调 那咱们的数据存在哪里呢?因此model上还得有一个叫data的属性,用来放数据的初始值

而后就不要去管怎么去获取说数据了

axios.get('/book/javascript') => .then(({data})=>{
复制代码

改成:

model.fetch('javascript')
复制代码

也不用去管怎么去更新数据了

axios.put('/book/javascript',{
    number: newNumber 
  })
复制代码

改成:

model.update({number: newNumber })
复制代码

它返回的是一个Promise对象,能够继续then,

model.update({number: newNumber })
  $('#number').text(newNumber)
复制代码

改成:

model.update({number: newNumber }).then(()=>{
      $('#number').text(newNumber)
  })
复制代码

接下来去改MVC的View

全部和html相关的操做交给view来作,操做的元素是什么el,内容是什么,一开始内容是写在html里面,其实应该交给view来初始化,做为view的字符串模板

何时去作初始化呢?render(data)函数会去找到template而且把它里面的占位符替换为model传来的data,渲染页面的时候会从model那里获得这个data

let view = {
  el: "#app",
  template: `
    <div>
      书名: __name__
      数量: <span id = "number">__number__</span>
    </div>
    <div>
      <button id = "addOne">加1</button>
      <button id = "minusOne">减1</button>
      <button id = "reset">置0</button>
    </div>
  `,
  render(){
    let html = this.template.replace('__name__',data.name).replace('__number__',data.number)
    $(this.el),html(html)
  }
}
复制代码

这样以前旧的代码:

let newHtml = oldHtml.replace('__name__',data.name).replace('__number__',data.number).replace('__name__',data.name)
 $('#app').html(newHtml)
复制代码

就能够被替换为:

let oldHtml = $('#app').html()
复制代码

更新view的代码:

$('#number').text(newNumber)
复制代码

也改成:

view.render(data)
复制代码

那么这个data哪里来的?

咱们能够从更新的成功回调里面去取.这个data = response.data

model.update({number: newNumber }).then(({data})=>{
      view.render(data)
  })
复制代码

也能够从model上去取:

由于model把response.data记到本身的data上了

let model = { 

+++
update(data){
 let id = this.data.id
    return  axios.put(`/book/{id}`,data).then((response)=>{
      this.data = response.data
      return response
    })
+++
复制代码

因此把全部的

view.render(data) => view.render(model.data)
复制代码

回顾一下:

model会去获取一个数据,在返回数据以前,它会把response.data记到本身的data上;更新也是,返回更新的数据以前会把response.data记到本身的data上

view接收一个data,而后就把模板字符串更新渲染到#app里面

model会去获取叫javascript的书获取成功后,就把它的数据给view渲染一下

接下来再写controller

view和model以外的东西都交给controller来作,分为初始化和事件绑定bindEvents,bindEvents就是对view的元素进行事件绑定

('#app')就是`(this.view.el)`

把3个对按钮的事件,封装成函数放在controller上

日常经过log(数字),二分法排bug

bindEvents(){
    // 外面的this是controller
    $(this.view.el).on('click','#addOne',this.addOne)  // 这里的this是什么
    $(this.view.el).on('click','#minusOne',this.minusOne)
    $(this.view.el).on('click','#reset',this.reset)
  }
复制代码

外面的this是controller若是想要它里面的this也是controller就得用bind()绑定,这样就能保证每个this表明controller

bindEvents(){
    $(this.view.el).on('click','#addOne',this.addOne.bind(this)  //  这里的this是被点击的元素,最好看一下jQ的on事件的click的this是什么
    $(this.view.el).on('click','#minusOne',this.minusOne.bind(this))
    $(this.view.el).on('click','#reset',this.reset.bind(this))
  }
复制代码

继续迭代第二个版本加入类

让对象变成构造函数

对第二个版本继续迭代,若是实际中有不少页面,那么每一个也页面都要写一次M,V,C这3个对象,而后,每次写对象都要写不少重复的代码是否是很麻烦?

那如何解决这个问题呢?

咱们应该把共同的属性写到一个class里面或者构造函数,把共用的代码写到它的原型上面(要知道哪些是须要传的参数),特有属性放到它的构造函数里面,可是是经过传参的形式

例子中的model的data是属于特有的属性,因此要放到它构造函数的里面,而fetch和update是公寓属性得放到model的构造函数Model的共有属性上

请求的路径的book也能够不用写死,而是能够去从参数那里获取

这样写的好处就是:

咱们每次使用Model的代码就能够简化成只要写2个属性,而且还能够造一个关于车的Model等

let bookModel = new Model({
  data:{
    name: '',
    id : '',
    number: 0
  },
  resource: 'book'
})
booModel.fetch('javascript')

let carModel = new Model({
  data: {
    ...
  }
  resource: 'car'
})
复制代码

再构造一个View的构造函数出来:

每个view的el是不同的,每个view的template也是不同的,只有render是共有属性放到View的原型上,el和template做为私有属性应该放到View的构造函数上

template的__name__和__number__也不能写死,得去遍历data

View.prototype.render = function(data){
  let html = this.template
  for(let key in data){
    html = html.replace(`__${key}__`,data[key])
  }
  $(this.el).html(html)
}
复制代码

把View的el和templat绑定到它的this上

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)
}
复制代码

注意: this.xxx = xxx是一个很是重要的操做,一旦忘了记,就会报undefined

把MVC的view改成vue

把MVC的view改成vue

先去bootcdn上引入vue的cdn到页面上

而后把以前构造的View删除,而后把new view 改成new vue,这样基本就能够了

可是语法得改一下

1.把__name__改成{{name}}也就是说vue的作标记会不同

2.这个new出来的view会强制让把data传给它,不要传给model,它须要这个data,这个view须要根据这个data去初始化template

3.template只能有一个根元素,若是有2个,那么vue只会看第一个,也就是说最外面只需套一个div

vue就是把MVC作一下升级,只要把MVC搞清楚了,Vue的原理就搞清楚了

4.那render怎么办?没有render了,vue会有一个自动render的机制,以前写的MVC在controller中view会render数据data,引入Vue以后就不须要render了,由于data已经放到vue上了,初始化vue会帮咱们作

深度截图_选择区域_20190806105740.png

5.那当咱们获取到数据以后怎么去更新view呢?vue是没有render函数的,按道理说应该去改view的data

以前的

this.view.render(this.model.data) 
复制代码

是否是这样改?

this.view.data = this.model.data
复制代码

6.Vue的一个重要特色,view会把data的属性升级到当前的view实例上面,也就是说有原来的view.data.name升级到了view.name,view.data.id升级到了view.name,view.data.number升级到了view.number 也就是说不是改view.data仍是直接去改view

this.model.fetch('javascript').then(()=>{
      this.view.name = this.model.data.name
      this.view.id = this.model.data.id
      this.view.number= this.model.data.number
    })
复制代码

也就是说一旦改动了view上的data的name,id,number,那么html就会自变,不用管render,vue会自动render

到目前为止引入vue以后和以前的MVC比较变化就是:

1>.忘掉render

2>.把data放到view上

3>.咱们须要改什么只要改data就好了

上面的写法感受比较麻烦有简洁一点的写法吗?

let view = new Vue({
  el: "#app",
  data:{
    book:{
      name: '未命名',
      number: 0,
      id: ''
    }
  },
  +++
复制代码

这样写vue会把book当作view的属性而不是view.data的属性

this.model.fetch('javascript').then(()=>{
      this.view.book = this.model.data
    })
复制代码

注意:改了以后记得改模板字符换里面的变量的层级从属关系

let view = new Vue({
  el: "#app",
  data:{
    book:{
      name: '未命名',
      number: 0,
      id: ''
    }
  },
  template: `
  <div>
    <div>
      书名: 《{{book.name}}》
      数量: <span id = "number">{{book.number}}</span>
    </div>
    <div>
      <button id = "addOne">加1</button>
      <button id = "minusOne">减1</button>
      <button id = "reset">置0</button>
    </div>
  </div>
  `
})
复制代码

vue把同步html这件事情变得很简单了,使得咱们把注意力从关注dom中抽离出来,vue会负责去拿到数据而后去渲染页面,使得咱们只须要去关注获取数据更改数据就好了,其余的事情交给vue就能够了

并且vue很厉害的地方就是vue不会刷新整个html,它只改该改的地方,能够作到拿到数据变化,只局部更新数据变化对应页面,而后局部渲染页面.做为对比,以前用MVC写的页面会从新更新所有的html

7.Vue的野心不止于此,Vue甚至可让咱们作到不须要controller,controller最重要的一件事就是绑定事件,若是Vue有一种语法能够更方便的绑定事件呢? 把以前放在controller上全部的工具函数,放到view的methods属性里面

那当用户点击按钮的时候怎么触发view里面的methods里面的addOne函数,minusOne函数, reset()函数呢?Vue有它本身的绑定事件的语法:

<button id = "addOne" v-on:click="addOne">加1</button>
复制代码

意思就是当用户点击这个按钮的时候就触发addOne函数,id也没用了!已经不须要选择元素了,直接在元素上就完成了事件绑定

修改完就是:

<div>
    <div>
      书名: 《{{book.name}}》
      数量: <span id = "number">{{book.number}}</span>
    </div>
    <div>
      <button v-on:click="addOne">加1</button>
      <button v-on:click="minusOne">减1</button>
      <button v-on:click="reset">置0</button>
    </div>
  </div>
复制代码

也不必用jQ去获取text了,直接去改view,让this.data.book.number + 1 ,可是Vue的语法不是这样的 vue会把data上的属性偷偷的放到本身身上,就变成了this.book.number + 1

methods: {
 addOne(){
     var oldNumber = $('#number').text() -0
     var newNumber = oldNumber + 1
     this.model.update({number: newNumber }).then(()=>{
     this.view.render(this.model.data)
    })
  }
复制代码

==>

methods: {
   addOne(){
     model.update({number: this.book.number + 1}).then(()=>{
     this.view.book = this.model.data
    })
  }```,
   minusOne(){
     this.model.update({number: this.book.number - 1 }).then(()=>{
     this.view.book = this.model.data
    })
  },
   reset(){
     this.model.update({number: this.book.number - 1}).then(()=>{
     this.view.book = this.model.data
    })
   }
  }
复制代码

注意:

i.这里的this.book.就是this.data.book,Vue会把data里面的属性偷偷的放到本身身上

ii.model前不须要加this,由于Vue是无论model层的它不想操这个心,这个model是外面的model,是变量model不是属性model,因此把this删掉,直接用model这个变量,model仍是以前的MVC的model,可是controller被合并了,view被语法糖了

iii.更新数据成功后,也不须要rendel,只须要修改data上的数据就好了

iv.发现vue好像一直在作的事情就是更新model而后从Model那里拿数据,没有DOM

v.Vue的文档给了咱们第一步初始化的操做,在view里面加一个方法,created(){},通常能够在created函数中调用ajax获取页面初始化所需的数据

created(){
    model.fetch('javascript').then(()=>{
      this.book = model.data
    })
  }
复制代码

model.fetch('javascript')就是发起get请求,服务器给予响应,响应的内容为data,而后就把数据赋给view的book.name变成新的number,name变成新的name,id变成新的id,而后Vue就会自动更新页面.

当点击加1的时候,已经绑定了事件为addOne,因此就会自动调用addOne,addOne会让model去updata,而后会获得新的数据data,而后把这个新的数据给view的book

这样就只须要作赋值和取值,就好了.有新的数据就赋值给book,想取book的值时候就this.book.numbe.如:

addOne(){
       model.update({number: this.book.number+1 }).then(()=>{ //想取book的时候
       this.view.book =  this.model.data     // 有新的数据就赋值给book
 })
复制代码

Vue的好处就是让以前的vue变得更智能(能够自动render),同时MVC的C能够合并到Vue里面(methods放工具函数,created方法用来调用ajax获取页面初始化所需的数据)

双向绑定和MVVM

什么是双向绑定

咱们不加1减1,而是加n减n

咱们须要一个n放到data上面

把n放到页面的input上,通常用v-model

把methods里面的1改成this.n(字符串须要减0)

当咱们在输入框中输入5的时候,n的值会立刻变成5,这就是双向绑定

以前并无用户的输入,只要渲染出来就好了,渲染只是一种单向绑定

注释:

一开始是1很好理解.直接根据data去渲染就好了

当咱们在输入框中输入5的时候,首先Vue会发现n变了,因而就把data的属性n变成5.n变成5以后Vue发现 n的值是:{{n}}这里的n也得变,因而就变了

从图中看出:

单向绑定: 若是只是从内存到页面的render的过程就是单向绑定

双向绑定: 若是render以后,当用户改输入框中的值的时候,还能反着映射到内存.那么这就是双向绑定

什么是MVVM

Vue属于自动化的MVC(不须要view.render(model.data)Vue会自动帮咱们render)

1.由于Vue把MVC的controllerg改成 ==> 放到它的methods属性里面(M) 2.同时把以前的MVC的初始化controller(就是把view和model当参数传给controller)改成 ==> 放到它的created方法里面(C)

因此Vue就是一个从MVC到MVVM的过程

使用Vue作3个小组件

axios代替了ajax,vue代替了dom先关的操做,因此jQ就...

用框架显示做者牛x,用原生js显示本身牛x

用了vue就不要去碰dom

第一个小组件: Popover 组件

Popover组件

第二个小组件: Slides 组件

Slides组件

  • v-on:click="go(x)" 不只支持加函数名表示执行函数还支持函数名加参数的形式

  • v-bind:style = ""表示在div上绑定一个style属性

  • v-on表示事件监听; v-bind表示绑定

  • Vue的调试不能用控制台调试须要用html调试,如: {transformValue}

第三个小组件: Tab组件

Tab组件

  • jsbin能够download到本地用编辑器打开

知乎上的一个问题:前端搞那么多工具框架库,是让开发更简单,仍是更复杂?

变复杂了,同时变容易了。复杂的反义词是简单。 容易的反义词是困难。电锯很复杂,可是用电锯砍树很容易。斧头很简单,可是用斧头砍树很困难。若是电锯坏了,你就哭吧。由于修电锯太复杂了。 若是斧头坏了,磨一磨斧刃或者换根柄就行了。由于斧头简单。若是你只须要砍几棵树,买个电锯是否是划不来?电锯还须要用电,你砍树的地方首先得有电你才能用电锯,否则你就得柴油发电了。对普通人家来讲,仍是常备斧子吧。通常来讲复杂的东西会有一堆须要记的东西(写在电锯的说明书上)你看 Vue、React、Angular 的说明书有多厚就知道了。而简单的东西则记住一些简单的规则就好了:好比不要用斧头砍本身或其余人。你喜欢容易,仍是简单?大部分人喜欢复杂而容易的东西。

文章连接

  • 会用Vue不能说明咱们牛X,只能说Vue的做者牛X

  • 若是只是作很简单的事情,买电锯是划不来的,学习Vue的过程就是买电锯,学习Vue的过程是须要好久的,如今才能大概理解Vue就是MVC的升级版,MVC的M要作ajax,MVC的V是操做页面的DOm树,C是控制流程的学习了算法数据结构和jS的基本语法,这样才能勉强算理解Vue,理解Vue的过程就是买电锯的花费,就是学习了解Vue须要掌握的基础知识包括ES6语法

  • 大部分人喜欢复杂而容易的东西。好比你很喜欢开车,掌握方向盘和油门刹车就能够去想去的地方,可是若是让你去了解汽车的构造原理,了解为何打方向盘车就会转弯,挂挡踩油门车为何就有动力等就会变得特别复杂,跟不想学

  • 学基础就是为了能了解MVC,之后学习其余框架同样要用到MVC的知识,MVC的M同样会用到ajax,MVC的V同样会用到DOM,MVC的C同样会用到数据结构,算法以及面向对象的知识

相关文章
相关标签/搜索