说说axios和mvc

作者: 不爱举铁的伪文青不是好前端 | 来源:发表于2019-03-30 15:12 被阅读2次

    首先Vue里面用到了mvc,那么要理解Vue就要先从mvc讲起
    接下来我们通过写一个图书信息展示来理解
    html

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width">
      <script src="https://code.jquery.com/jquery-3.1.0.js"></script>
      <title>JS Bin</title>
    </head>
    <body>
      <div id="app">
        <div>
          书名:"JavaScript高级程序设计"
          数量:<span id="number">2</span>
        </div>
        <button id="add">加一</button>
        <button id="minus">减一</button>
        <button id="reset">归零</button>
      </div>
    </body>
    </html>
    

    js

    $("#app").on("click","#add",function(){
      var oldNumber=$("#number").text()
      var newNumber = oldNumber-0+1
      $("#number").text(newNumber)
    })
    
    $("#app").on("click","#minus",function(){
      var oldNumber=$("#number").text()
      var newNumber = oldNumber-0-1
      $("#number").text(newNumber)
    })
    
    $("#app").on("click","#reset",function(){
      var newNumber = 0
      $("#number").text(newNumber)
    })
    

    上面的代码是直接把数据写进页面里的,我们可以发一个请求来得到这个数据再写到页面里,这里我们引入axios来请求数据,并在真正返回response之前用axios的拦截机拦截一下,然后修改response之后再返回response

    axios.interceptors.response.use(function(response){
        let {url,method,data}=response.config
        if(url === '/books/1' && method === 'get'){
          response.data = {
            name:'JavaScript高级程序设计',
            number:2,
            id:1
          }
        }
      return response
    })
    
    axios.get('/books/1').then((response)=>{
      let{data} = response
      let originalHtml = $('#app').html()
      let newHtml = originalHtml.replace('__name__',data.name)
      .replace('__number__',data.number)
      $('#app').html(newHtml)
    })
    

    这里先用axios发一个get请求,然后拦截机里的response.config可以拿到请求的url,method,data,如果请求的url是“/books/1”而且方法是get那么就返回数据,同时原始html里的number,name用__name____number__代替。当请求成功拿到response之后,拿到里面的data,然后把data里面的name和number的值替换之前的__name____number__
    然后每次点击按钮,能不能也发一个put请求,然后通过响应再改数值,还是用axios来put请求

    $("#app").on("click","#add",function(){
      var oldNumber=$("#number").text()
      var newNumber = oldNumber-0+1
      axios.put('/books/1',{number:newNumber}).then(()=>{
         $("#number").text(newNumber)
      })
    })
    
    var book={
      name:'JavaScript高级程序设计',
      number:2,
      id:1
    }
    axios.interceptors.response.use(function(response){
        let {url,method,data}=response.config
        if(url === '/books/1' && method === 'get'){
          response.data = book
        }else if(url === '/books/1' && method === 'put'){
          data = JSON.parse(data)
          Object.assign(book,data)
          console.log(book)
          response.data = book
        }
      return response
    })
    

    把newNumber当做data一起请求过去,然后为这个put做一个路由,当url和method都满足后,先把请求的data变成对象,然后用Object.assign这个api把book里面的number替换掉,再返回这个book。
    到这里我们已经完成了一个人“意大利面条式代码”,长短交错看起来很乱,而且数据视图还有逻辑都是想到哪里写哪里,这就有了引入mvc的需求。
    “m”就是“model”负责数据,
    “v”就是view负责页面样式,
    “c”就是controller负责逻辑,

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

    model首先有一个data初始值,然后fetch函数发一个get请求,然后取到响应的data再把它赋值给model的data,updata函数也是一样的。

    let view = {
      el:'#app',
      template:`
        <div>
          书名:《__name__》
          数量:<span id="number">__number__</span>
        </div>
        <button id="add">加一</button>
        <button id="minus">减一</button>
        <button id="reset">归零</button>
      `,
      render(data){
        let html = this.template.replace('__name__',data.name)
        .replace("__number__",data.number)
        $(this.el).html(html)
      }
    }
    

    view里面有操作的元素el:'#app',然后是HTML模板,render函数接受一个data然后用data里面的name和number来替代模板HTML里的对应元素,再把新的HTML渲染到el里面

    let controller = {
      init(options){              //init接受一个对象{view:view,model:model}
        let view = options.view
        let model = options.model
        this.view = view                      //把view和model绑到controller上面
        this.model = model
        this.view.render(this.model.data)    //页面做一个初始化,把model里面data的初始值渲染到页面
        this.bindEvents()                  //绑定事件
        this.model.fetch(1).then(()=>{
          this.view.render(this.model.data)   
        })                  //拿到拦截机返回的response然后赋值到model的data上,render函数渲染拿到的data到页面
      },
      addOne(){   
        var oldNumber=$("#number").text()
        var newNumber = oldNumber-0+1
        this.model.updata({number:newNumber})   //这里的this是addOne,后面bind(this)之后才是controller
          .then(()=>{
          this.view.render(this.model.data)
        })
      },
      minus(){   
        var oldNumber=$("#number").text()
        var newNumber = oldNumber-0-1
        this.model.updata({number:newNumber})
          .then(()=>{
          this.view.render(this.model.data)
        })
      },
      reset(){
        var newNumber = 0
        this.model.updata({number:0})
          .then(()=>{
          this.view.render(this.model.data)
        })
      },
      bindEvents(){
        $(this.view.el).on("click",'#add',this.addOne.bind(this))  //第一个this是controller,bind(this)让addOne里面的this和外面的一样都是controller
        $(this.view.el).on("click","#minus",this.minus.bind(this))
        $(this.view.el).on("click","#reset",this.reset.bind(this))
      }
    }
    

    这样mvc三部分就写完了!
    那么为了避免每一个页面都要重新写一遍这三个模型,于是想到可以写一个构造函数,然后每次就只用new就可以了
    思路是把私有的属性放到构造函数里,调用的时候传进去,把公用属性放到原型里

    function Model(options){
      this.data = options.data
      this.resource = options.resource
    }
    Model.prototype.fetch = function(id){
      return axios.get(`/${this.resource}s/${id}`).then((response) => {
          this.data = response.data
          console.log(this.data)
          return response
        })
    }
    Model.prototype.update = function(data){
      let id = this.data.id 
        return axios.put(`/${this.resource}s/${id}`, data).then((response) => {
          this.data = response.data 
          console.log('response')
          console.log(response)
          return response
        })
    }
    
    let model = new Model({
      data: {
        name: '',
        number: 0,
        id: ''
      },
      resource: 'book'
    })
    
    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>
        书名:《__name__》
        数量:<span id=number>__number__</span>
        </div>
        <div>
          <button id="addOne">加1</button>
          <button id="minusOne">减1</button>
          <button id="reset">归零</button>
        </div>
      `
    })
    

    接下来引入Vue,Vue强制把data传给view,因为Vue需要用data来初始化
    template,不需要render。Vue会把当前model的data里的所有属性更新到View上面,所以直接修改View的值view.book = model.data这样直接替换了之前的render函数,只需要更新view里的data,就会自动更新HTML。

    let view =new Vue({
      el:'#app',
      data:{
        book:{   
          name:'未命名',
          number:0,
          id:"" 
        },
        n:1
      },
      template:`
        <div>
        <div>
          书名:《{{book.name}}》
          数量:<span id="number">{{book.number}}</span>
        </div>
        <div>
           <input v-model="n" />      
          N 的值是 {{n}}
        </div>
        <div>
            <button v-on:click='addOne'>加N</button>   //绑定事件
            <button v-on:click='minus'>减N</button>
            <button v-on:click='reset'>归零</button>
        </div>
        </div>
      `,
      created(){
        model.fetch(1).then(()=>{
          this.book = model.data
        })
      },
      methods:{
        addOne(){   
          model.updata({
            number:this.book.number+(this.n-0)
          })
            .then(()=>{
              view.book = model.data
          })
        },
        minus(){   
          model.updata({
            number:this.book.number-(this.n-0)
          })
            .then(()=>{
              view.book = model.data
          })
        },
        reset(){
          model.updata({
            number:0
          })
            .then(()=>{
              view.book = model.data
          })
        }
      }
    })
    

    可以使用v-model指令在<input>元素上进行双向数据绑定。
    双向数据绑定:之前我们可以通过book.number=3来修改内存值,然后Vue根据修改的内存值去更新<span>里的数字,只就是单向绑定。如果我们引入<input>标签,当我们修改<span>标签里的数字,这时Vue会根据修改的数据来修改内存值,这就是双向绑定。

    相关文章

      网友评论

        本文标题:说说axios和mvc

        本文链接:https://www.haomeiwen.com/subject/wkprbqtx.html