《MVC》

134 阅读4分钟

一. 什么是MVC

MVC就是一种设计模式,设计模式:程序员写了一段很好的代码,觉得别人也用得到,那就给这种写法取个名字吧,比如适配器模式。

MVC就是万金油,所有页面都可以使用MVC来优化代码结构。

MVC没有严格的定义,每个人的理解都稍有不同。如果面试问什么是MVC?可以回答MVC的英文,中文解释。

每个模块都可以写成三个对象,分别是M V C.

  • M : Model 数据模型,负责操作所有数据。
  • V :View 视图,负责所有UI界面。
  • C :Controller 控制器,负责其他。

二. 利用MVC优化代码

1. 需求

在一个页面里写四个模块,分别完成不同的功能。 (这里我们全部使用ES6的写法,parcel会把所有转换成ES5,不用担心)。

1. 一些新写法:

  • 引入CSS:以前在html里通过link标签引入CSS。现在在JS文件里引入CSS:

    import "./app1.css";
    

    把reset代码全部写在一个文件里reset.css,然后在JS里引入

    import "./reset.css";
    
  • 引入jquery: 以前在html里用script标签,src=一个cdn地址。

    第二种先进的方法:使用yarn/npm(两个都可以,如果一个不好用试另一个) 新建一个终端,输入命令:

    yarn init -y
    

    初始化yarn,会生成一个package.json文件,里边做了一些初始化(不用管,这是为了防止之后可能出现的错误)

    然后:

    yarn add jquery
    

    会生成三个文件,node_modules,package.json,yarn.lock

    或者使用npm:

    npm init
    npm i jquery
    

    然后在JS 文件里引入:import $ from 'jquery'

我们一般不在css文件里引另一个css(@import),这样做性能低

2. 四个模块的功能:

1. app1

功能:简易计算器

用到了jquery的哪些API:

let n=$num.text()  //读元素的文本
$num.text(n)  //写文本

刷新页面时,数字会变成一开始定好的那个数字。为了实现保存,使用localStorage。每次改变数字时,存储localStorage.setItem("n", n); 在前边先从本地存储中读,然后写在页面中。

const $btn1 = $("#add1");
const $btn2 = $("#minus1");
const $btn3 = $("#multi2");
const $btn4 = $("#divide2");
const $num = $("#number");

let n = localStorage.getItem("n");
$num.text(n || 100);

$btn1.on("click", () => {
  let n = Number($num.text());
  n += 1;
  localStorage.setItem("n", n);
  $num.text(n);
});
$btn2.on("click", () => {
  let n = Number($num.text());
  n -= 1;
  localStorage.setItem("n", n);
  $num.text(n);
});

$btn3.on("click", () => {
  let n = Number($num.text());
  n *= 2;
  localStorage.setItem("n", n);
  $num.text(n);
});
$btn4.on("click", () => {
  let n = Number($num.text());
  n /= 2;
  localStorage.setItem("n", n);
  $num.text(n);
});

2. app2: Tab切换

导航栏两个li,点1,出现内容1;点2,出现内容2。

所有操作系统滚动条的宽度一般是14~19px.有可能把滚动条的宽度算进去,导致页面布局错误。

//index.html
<section id="app2">
    <ol class="tab-bar">
        <li>1</li>
        <li>2</li>
    </ol>
    <ol class="tab-content">
        <li>内容1</li>
        <li>内容2</li>
    </ol>
</section>

我们把整个页面做成flex布局,让每个模块各占四分之一。但是发现app2被挤到下边去了,算了两个section和整个页面的宽度差是17px,那就是把滚动条的宽度算进去了。把body的样式:overflow:hidden,去掉滚动条。

在写app2的JS时,如果和app1的JS写在一起,没有必要,因为这完全是两个不同的功能。模块化:把一段独立的代码放到一个文件里,然后引入这个文件即可。所以我们新建一个app1.js,把app1的JS代码拷进文件里,在main.js里import './app1.js'

新建一个app2.js:

import $ from "jquery";
const $tabBar = $("#app2 .tab-bar");
const $tabContent = $("#app2 .tab-content");
$tabBar.on("click", "li", (e) => {
  const $li = $(e.currentTarget);
  const index = $li.index();
  $tabContent
    .children()
    .eq(index)
    .css({ display: "block" })  //也可以写成.show()
    .siblings()
    .css({ display: "none" });  //也可以写成.hide()  ,效果相同
});

把监听事件绑定到ol上,通过第二个参数,点击li时,触发事件。这样可以只写一次事件监听,不需要把每个li元素都写一次。监听到事件时:

  1. 首先要知道是哪个li元素被点击了。有两种API:

    e.target
    
    e.currentTarget
    

    如果li标签里还有一个span标签的话,点击这个span元素,通过e.target找到的是span,通过e.currentTarget找到的是on()里边指定的li元素。因为我们要知道被点击的li是第几个,不关心li里边的span。所以我们使用e.currentTarget。这个目前是DOM元素,用$()封装成jquery对象。

  2. 得到这个li的排行

    $li.index()  //0,1...
    
  3. 去下边的内容区找到对应排行的内容:

    找到$tabContent的所有儿子们,通过.eq(index)得到排行是index的那个儿子,把它展示出来,同时把它的其余兄弟姐妹隐藏。

永远不要用.css() , .hide() , .show()这种操作CSS的API

那应该怎么写呢?

$tabContent
    .children()
    .eq(index)
    .addClass('active')  //点击哪个li,哪个li对应的内容就被添加一个active类
    .siblings()
    .removeClass('active') //同时,其余内容被删掉这个active类

那出现和隐藏怎么控制呢?样式不关JS管,是css的事。JS只负责功能,不管你长什么样子。而css只负责样式,不管功能。 样式与行为分离

在css里,有active类的li元素设为block。

#app2 .tab-content li.active {
  display: block;
}

3. app3 : 一个正方形,点击,往右移动到一个位置;再点击,回去

//index.html
<section id="app3">
    <div class="square"></div>
</section>
//app3.js
const $square = $("#app3 .square");
$square.on("click", () => {
  $square.toggleClass("active");
});

在JS里,不操作样式,只负责添加类。点击正方形时:给他添加一个类,在css里,有这个类的square,往右移动一段距离。那怎么做到再次点击回到原位置呢?要再监听一个点击事件吗?不需要,只需要在点击事件里,先判断有没有这个类,如果没有,就添加类(随之往右移动);如果有这个类,就删掉这个类(随之动画消失,回到原位置)。jquery中有内置这个功能:$square.toggleClass("active")有这个类,就删掉;没有就加上

4. app4 : 一个圆形,鼠标移进去就开始变色,移出去停止变色

同上,在JS里监听事件,如果监听到鼠标进去(mouseenter),就添加active类。监听到鼠标移出(mouseleave),就删除active类。

$circle
  .on("mouseenter", () => {
    $circle.addClass("active");
  })
  .on("mouseleave", () => {
    $circle.removeClass("active");
});

在css里添加样式,如果circle有active类,就添加变色动画。