《MVC转Vue》

548 阅读3分钟

在已经写好的MVC设计模式下,改成Vue:

一.下载

yarn add vue

二. 引入

import Vue from 'vue'

三. 改写

1. MVC

const m = new Model({
  data: {
    n: parseInt(localStorage.getItem("n")),
  },
  update(value) {
    Object.assign(m.data, value);
    m.trigger("m:update");
    localStorage.setItem("n", m.data.n);
  },
});

//合并v和c,重命名为view,用class,省略对象名view.放到一个函数里
const init = (el) => {
  new View({
    el: el,   //容器,由外部传进来
    data: m.data,   //数据
    html: `   
        <div>
          <div class="output">
            <span id="number">{{n}}</span>
          </div>
          <div class="actions">
            <button id="add1">+1</button>
            <button id="minus1">-1</button>
            <button id="multi2">*2</button>
            <button id="divide2">÷2</button>
          </div>
        </div>
      `,
    render(data) {   //渲染
      const n = data.n;
      if (this.el.children.length !== 0) {
        this.el.empty();
      }
      $(this.html.replace("{{n}}", n)).prependTo(this.el);
    },
    events: {        //表驱动
      "click #add1": "add",
      "click #minus1": "minus",
      "click #multi2": "multi",
      "click #divide2": "div",
    },
    add() {
      m.update({ n: m.data.n + 1 });
    },
    minus() {
      m.update({ n: m.data.n - 1 });
    },
    multi() {
      m.update({ n: m.data.n * 2 });
    },
    div() {
      m.update({ n: m.data.n / 2 });
    },
  });
};
export default init;

在main.js里调用init函数:

init('#app1')  //传容器,el

2. Vue

import "./app1.css";
import Vue from "vue";

const init = (el) => {
  new Vue({
    el: el,
    data: { n: parseInt(localStorage.getItem("n")) },
    template: `
      <section id='app1'>
        <div class="output">
          <span id="number">{{n}}</span>
        </div>
        <div class="actions">
          <button @click="add">+1</button>
          <button @click="minus">-1</button>
          <button @click="multi">*2</button>
          <button @click="div">÷2</button>
        </div>
      </section>
    `,
    methods: {
      add() {
        this.n += 1;
      },
      minus() {
        this.n -= 1;
      },
      multi() {
        this.n *= 2;
      },
      div() {
        this.n /= 2;
      },
    },
    watch: {
      n() {
        localStorage.setItem("n", this.n);
      },
    },
  });
};
export default init;

1. el

需要一个容器

2. data

Vue认为对象m没必要,数据就直接存成data这个对象

3. template

就是MVC里的html。注意:我们原来的容器是index.html里写好的<section id='app1'></section> 但是Vue的template会把外边的section替换成字符串里的最外层div,所以字符串里也写和外边一样的容器。还把id带上是因为css样式是根据id找的。

4. methods

把事件触发执行的函数都放在methods里。函数里改变数据时,本来是this.data.n,但是Vue认为data多余,所以写成 this.n 即可。

5. @click

绑定事件不需要再用jQ获取元素和遍历events表去绑定了。只需要在template里的特定标签上加一个属性,@click='add' ,表示点击该标签元素触发add函数。

6. watch

表示监听,当n发生变化时,就把它存进localStorage里。

四. 总结

原来我们需要引入三个文件实现一个模块。逐渐模块化,只引入一个js就可以。这种模块化可以使各个模块相互独立,解耦,不会互相影响。一个模块用MVC,另一个用Vue,完全没问题。

如MVC里的events,把原来一个一个绑定事件,改写成遍历表。表里是每个事件不重复的内容。 Vue里的 methods , watch 也是一种表,以watch为例,只需要告诉我什么东西变化时做什么,其他逻辑Vue都写好了。
如MVC中的对象m,v,c,eventBus在不同的模块中都有一些共同的属性,就抽象成类。让Model类和View类继承EventBus类,就不再需要eventBus对象了。
在MVC里,比起从页面取元素,改变,再放回页面,直接render更简单,即根据n,每次n改变时,就重新渲染页面。

但是这个思想在Vue里没有应用。我们只是改变了n,并没有调用render,那这个变化是怎么应用到页面中的呢?Vue认为每次都要render很麻烦,于是在n变化时,就自动地把这个变化渲染到页面,而且Vue的更新页面不是全部更新,是哪里变化,就局部地重新渲染哪里。

React是显式调用render的