一. 什么是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
功能:简易计算器

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元素都写一次。监听到事件时:
-
首先要知道是哪个li元素被点击了。有两种API:
e.targete.currentTarget如果li标签里还有一个span标签的话,点击这个span元素,通过
e.target找到的是span,通过e.currentTarget找到的是on()里边指定的li元素。因为我们要知道被点击的li是第几个,不关心li里边的span。所以我们使用e.currentTarget。这个目前是DOM元素,用$()封装成jquery对象。 -
得到这个li的排行
$li.index() //0,1... -
去下边的内容区找到对应排行的内容:
找到$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类,就添加变色动画。