39·灵魂前端工程师养成-MVC

2022-11-08 17:05:13 浏览数 (1)

  • MVC设计模式

  • 先写一个意大利面条式的烂代码
  • 实现第一个模块-加减乘除按钮
  • 实现第二个模块-点击标签实现切换
  • 实现第三个模块-方块变成圆动画
  • 实现第四个模块-点击圆会渐变
  • app2数据保存
  • app3数据保存
  • 最小化知识点
  • MVC以不变应万变
  • 使用类优化代码的Model
  • 使用类优化代码的View
  • 合并V和C

-曾老湿, 江湖人称曾老大。 -笔者QQ:133411023、253097001 -笔者交流群:198571640 -笔者微信:z133411023


-多年互联网运维工作经验,曾负责过大规模集群架构自动化运维管理工作。 -擅长Web集群架构与自动化运维,曾负责国内某大型金融公司运维工作。 -devops项目经理兼DBA。 -开发过一套自动化运维平台(功能如下): 1)整合了各个公有云API,自主创建云主机。 2)ELK自动化收集日志功能。 3)Saltstack自动化运维统一配置管理工具。 4)Git、Jenkins自动化代码上线及自动化测试平台。 5)堡垒机,连接Linux、Windows平台及日志审计。 6)SQL执行及审批流程。 7)慢查询日志分析web界面。


MVC设计模式


什么是MVC

MVC是个框,什么都能往里装

MVC主要目的就是为了减少重复性的工作,重复性的代码。

代码级别的重复 你把相同的三行代码写了两遍 那么你就应该重构它

页面级别 你把类似的页面做了10遍 那么你就应该相处一个万金油的写法

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


如果我们不学MVC会怎么样

意大利面条式代码 老手程序要为了鄙视烂代码,将其称为面条式代码

你将变成外包程序员 不停重复自己,不懂得抽象 只会调用API,不能提升自己 只会写业务,不会封装,更不会造轮子,更不会加薪


那我学还不行嘛

MVC介绍 每个模块都可以写成三个对象,分别是:M、V、C M: Model(数据模型)负责操作所有数据 V: View (视图)负责所有UI界面 C: Controller (控制器)负责其他

就这? 嗯呢,就这 MVC没有严格的定义 M、V、V分别要做什么也是很随意的,大概对就行

在程序猿的世界里,很多定义,都是模糊的定义,所以很多程序猿对这些定义的认知都多少有些偏差,不相同的地方,唯一一点认同的一样的地方,那就是M、V、C这三个单词分别是啥,一点偏差都没有,没有任何分歧。

先写一个意大利面条式的烂代码


初始化项目

抄淘宝的viewport

代码语言:javascript复制
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name=viewport content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
    <title>曾老湿MVC</title>
</head>
<body>
   <script src="main.js"></script> 
</body>
</html>

实现需求:把一个页面分4块,每一个块里面有一个功能 1.左上角的块,实现100数字的加减乘除 2.右上角的块,实现tab标签的切换,并且高亮 3.左下角的块,实现一个形状的动画 4.右下角的块,实现一个颜色的渐变


创建4个块

代码语言:javascript复制
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name=viewport content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
    <title>曾老湿MVC</title>
</head>
<body>
    <div id="app1"></div>
    <div id="app2"></div>
    <div id="app3"></div>
    <div id="app4"></div>
   <script src="main.js"></script> 
</body>
</html>

第一个块制作按钮

代码语言:javascript复制
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name=viewport content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
    <title>曾老湿MVC</title>
</head>
<body>
    <div id="app1">
        <div class="output">
            <span>100</span>
        </div>
        <div class="actions">
            <button> 1</button>
            <button>-1</button>
            <button>*2</button>
            <button>÷2</button>
        </div>
    </div>
    <div id="app2"></div>
    <div id="app3"></div>
    <div id="app4"></div>
   <script src="main.js"></script> 
</body>
</html>


JS引入CSS

代码语言:javascript复制
import './app1.css'

CSS添加选择器

先把选择器写好,再加功能,然后给app1加一个边框

代码语言:javascript复制
#app1{
    border: 1px solid red;
}
#app1 .output{}
#app1 .actions{}


写一个CSS的reset用JS引入

代码语言:javascript复制
*{margin: 0;padding: 0;}
代码语言:javascript复制
import './reset.css'
import './app1.css'


让第一个app占屏幕的四分之一

代码语言:javascript复制
#app1{
    border: 1px solid red;
    width: 50vw;
    height: 50vh;
}
#app1 .output{}
#app1 .actions{}


新的方式引入jQuery

首先使用npm或者yarn安装jQuery

代码语言:javascript复制
#方法一
MacBook-pro:mvc-demo-1 driverzeng$ yarn init -y
MacBook-pro:mvc-demo-1 driverzeng$ yarn global add  jquery

代码语言:javascript复制
#方法二
MacBook-pro:mvc-demo-1 driverzeng$ npm i jquery

会发现多了几个东西

然后我们调用jQuery

代码语言:javascript复制
import './reset.css'
import './app1.css'
import $ from 'jQuery'

实现第一个模块-加减乘除按钮


先给4个按钮加上id

代码语言:javascript复制
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name=viewport content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
    <title>曾老湿MVC</title>
</head>
<body>
    <div id="app1">
        <div class="output">
            <span id="number">100</span>
        </div>
        <div class="actions">
            <button id="add1"> 1</button>
            <button id="minus1">-1</button>
            <button id="mul2">*2</button>
            <button id="divide2">÷2</button>
        </div>
    </div>
    <div id="app2"></div>
    <div id="app3"></div>
    <div id="app4"></div>
   <script src="main.js"></script> 
</body>
</html>

使用js获取4个button

代码语言:javascript复制
import './reset.css'
import './app1.css'
import $ from 'jQuery'

const $button1 = $('#add1')
const $button2 = $('#minus1')
const $button3 = $('#mul2')
const $button4 = $('#divide2')

添加事件-实现加法

代码语言:javascript复制
import './reset.css'
import './app1.css'
import $ from 'jQuery'

const $button1 = $('#add1')
const $button2 = $('#minus1')
const $button3 = $('#mul2')
const $button4 = $('#divide2')
const $number = $('#number')

$button1.on("click",()=>{
    let n = parseInt($number.text())
    n  = 1
    $number.text(n)
})


添加事件-实现减法

代码语言:javascript复制
import './reset.css'
import './app1.css'
import $ from 'jQuery'

const $button1 = $('#add1')
const $button2 = $('#minus1')
const $button3 = $('#mul2')
const $button4 = $('#divide2')
const $number = $('#number')

$button1.on("click",()=>{
    let n = parseInt($number.text())
    n  = 1
    $number.text(n)
})

$button2.on("click",()=>{
    let n = parseInt($number.text())
    n -= 1
    $number.text(n)
})


添加事件-实现乘法

代码语言:javascript复制
import './reset.css'
import './app1.css'
import $ from 'jQuery'

const $button1 = $('#add1')
const $button2 = $('#minus1')
const $button3 = $('#mul2')
const $button4 = $('#divide2')
const $number = $('#number')

$button1.on("click",()=>{
    let n = parseInt($number.text())
    n  = 1
    $number.text(n)
})

$button2.on("click",()=>{
    let n = parseInt($number.text())
    n -= 1
    $number.text(n)
})

$button3.on("click",()=>{
    let n = parseInt($number.text())
    n *= 2
    $number.text(n)
})


添加事件-实现除法

代码语言:javascript复制
import './reset.css'
import './app1.css'
import $ from 'jQuery'

const $button1 = $('#add1')
const $button2 = $('#minus1')
const $button3 = $('#mul2')
const $button4 = $('#divide2')
const $number = $('#number')

$button1.on("click",()=>{
    let n = parseInt($number.text())
    n  = 1
    $number.text(n)
})

$button2.on("click",()=>{
    let n = parseInt($number.text())
    n -= 1
    $number.text(n)
})

$button3.on("click",()=>{
    let n = parseInt($number.text())
    n *= 2
    $number.text(n)
})

$button4.on("click",()=>{
    let n = parseInt($number.text())
    n /= 2
    $number.text(n)
})


当用户刷新的时候,还是那个数字不变回100

代码语言:javascript复制
import './reset.css'
import './app1.css'
import $ from 'jQuery'

const $button1 = $('#add1')
const $button2 = $('#minus1')
const $button3 = $('#mul2')
const $button4 = $('#divide2')
const $number = $('#number')
const n = localStorage.getItem('n')
$number.text(n || 100)

$button1.on("click",()=>{
    let n = parseInt($number.text())
    n  = 1
    localStorage.setItem('n',n)
    $number.text(n)
})

$button2.on("click",()=>{
    let n = parseInt($number.text())
    n -= 1
    localStorage.setItem('n',n)
    $number.text(n)
})

$button3.on("click",()=>{
    let n = parseInt($number.text())
    n *= 2
    localStorage.setItem('n',n)
    $number.text(n)
})

$button4.on("click",()=>{
    let n = parseInt($number.text())
    n /= 2
    localStorage.setItem('n',n)
    $number.text(n)
})

实现第二个模块-点击标签实现切换


先添加两个列表

代码语言:javascript复制
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name=viewport content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
    <title>曾老湿MVC</title>
</head>
<body>
    <div id="app1">
        <div class="output">
            <span id="number">100</span>
        </div>
        <div class="actions">
            <button id="add1"> 1</button>
            <button id="minus1">-1</button>
            <button id="mul2">*2</button>
            <button id="divide2">÷2</button>
        </div>
    </div>
    <div id="app2">
        <ol class="tab-bar">
            <li>1</li>
            <li>2</li>
        </ol>
        <ol class="tab-content">
            <li>内容1</li>
            <li>内容2</li>
        </ol>
    </div>
    <div id="app3"></div>
    <div id="app4"></div>
   <script src="main.js"></script> 
</body>
</html>

接下来做一下CSS的reset

代码语言:javascript复制
*{box-sizing: border-box;margin: 0;padding: 0;}
*::before,*::after{box-sizing: border-box;}
ol,ul{list-style: none;}

修改css样式

app2.css

代码语言:javascript复制
#app2{
    border: 1px solid blue;
    width: 50vw;
    height: 50vh;
}
#app2 .tab-bar{}
#app2 .tab-content{}

在js中引入

代码语言:javascript复制
import './app2.css'


做flex布局

因为我们想要把这个功能放在右上角,所以我们需要创建一个div包裹住这四个app

代码语言:javascript复制
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name=viewport content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
    <title>曾老湿MVC</title>
</head>
<body>
    <div class="page">
        <div id="app1">
            <div class="output">
                <span id="number">100</span>
            </div>
            <div class="actions">
                <button id="add1"> 1</button>
                <button id="minus1">-1</button>
                <button id="mul2">*2</button>
                <button id="divide2">÷2</button>
            </div>
        </div>
        <div id="app2">
            <ol class="tab-bar">
                <li>1</li>
                <li>2</li>
            </ol>
            <ol class="tab-content">
                <li>内容1</li>
                <li>内容2</li>
            </ol>
        </div>
        <div id="app3"></div>
        <div id="app4"></div>
    </div>
   <script src="main.js"></script> 
</body>
</html>

我们不能把page的CSS写在app1里也不能写在app2里,所以我们需要创建一个新的。所以我们再创建一个全局的CSS

代码语言:javascript复制
body  > .page{
    display: flex;
}


修改全局

因为我们每个框,都占四分之一,所以我们把重复代码写到全局css中,那我们先把每个app的div换成section

index.html

代码语言:javascript复制
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name=viewport content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
    <title>曾老湿MVC</title>
</head>
<body>
    <div class="page">
        <section id="app1">
            <div class="output">
                <span id="number">100</span>
            </div>
            <div class="actions">
                <button id="add1"> 1</button>
                <button id="minus1">-1</button>
                <button id="mul2">*2</button>
                <button id="divide2">÷2</button>
            </div>
        </section>
        <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>
        <section id="app3"></div>
        <section id="app4"></div>
    </div>
   <script src="main.js"></script> 
</body>
</html>

然后再写全局

global.css

代码语言:javascript复制
body{
    overflow: hidden;
}
body  > .page{
    display: flex;
    flex-wrap: wrap;
}
body  > .page > section{
    width: 50vw;
    height: 50vh;
    border: 1px solid gray;
}

app1.css

代码语言:javascript复制
#app1{
    border: 1px solid red;
}
#app1 .output{}
#app1 .actions{}

app2.css

代码语言:javascript复制
#app2{
    border: 1px solid blue;
}
#app2 .tab-bar{}
#app2 .tab-content{}

模块化

对应的app创建对应的css和js,所以我们把代码重新移动一下

main.js

代码语言:javascript复制
import './reset.css'
import './global.css'
import './app1.css'
import './app2.css'
import './app3.css'
import './app4.css'
import './app1.js'
import './app2.js'
import './app3.js'
import './app4.js'

app1.js

代码语言:javascript复制
import $ from 'jQuery'
const $button1 = $('#add1')
const $button2 = $('#minus1')
const $button3 = $('#mul2')
const $button4 = $('#divide2')
const $number = $('#number')
const n = localStorage.getItem('n')
$number.text(n || 100)

$button1.on("click",()=>{
    let n = parseInt($number.text())
    n  = 1
    localStorage.setItem('n',n)
    $number.text(n)
})

$button2.on("click",()=>{
    let n = parseInt($number.text())
    n -= 1
    localStorage.setItem('n',n)
    $number.text(n)
})

$button3.on("click",()=>{
    let n = parseInt($number.text())
    n *= 2
    localStorage.setItem('n',n)
    $number.text(n)
})

$button4.on("click",()=>{
    let n = parseInt($number.text())
    n /= 2
    localStorage.setItem('n',n)
    $number.text(n)
})


实现功能

app2.js

代码语言:javascript复制
import $ from 'jquery'
const $tabBar =  $('#app2 .tab-bar')
const $tabConten = $('#app2 .tab-content')

$tabBar.on('click','li',(e)=>{
    const $li = $(e.currentTarget)
    const index = $li.index()
    $tabConten.children()
    .eq(index).css({display:'block'})
    .siblings().css({display:'none'})
})

点1,就是内容1 点2,就是内容2

但是。。。这样的代码不要用,不要使用js直接操作css,所以我们需要换个思想

只要一点击,就add一个active的class,那么另外一个就remove这个active的class

代码语言:javascript复制
import $ from 'jquery'
const $tabBar =  $('#app2 .tab-bar')
const $tabConten = $('#app2 .tab-content')

$tabBar.on('click','li',(e)=>{
    const $li = $(e.currentTarget)
    const index = $li.index()
    $tabConten.children()
    .eq(index).addClass('active')
    .siblings().removeClass('active')
})
代码语言:javascript复制
#app2{
    border: 1px solid blue;
}
#app2 .tab-bar{
    display: flex;
}
#app2 .tab-bar  > li{
    border: 3px solid black;
    width: 50%;
}

#app2 .tab-content{}

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


内容切换完成了,再给标签加个颜色

app2.js

代码语言:javascript复制
import $ from 'jquery'
const $tabBar =  $('#app2 .tab-bar')
const $tabConten = $('#app2 .tab-content')

$tabBar.on('click','li',(e)=>{
    const $li = $(e.currentTarget)
    $li
        .addClass("selected")
        .siblings()
        .removeClass('selected')
    const index = $li.index()
    $tabConten
        .children()
        .eq(index)
        .addClass('active')
        .siblings()
        .removeClass('active')
})

app2.css

代码语言:javascript复制
#app2{
    border: 1px solid blue;
}
#app2 .tab-bar{
    display: flex;
}
#app2 .tab-bar  > li{
    border: 1px solid gray;
    width: 50%;
}

#app2 .tab-bar  > li.selected{
    background: palevioletred;
    color: white;
}

#app2 .tab-content{}

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


添加默认点击效果

app2.js

代码语言:javascript复制
import $ from 'jquery'
const $tabBar =  $('#app2 .tab-bar')
const $tabContent = $('#app2 .tab-content')

$tabBar.on('click','li',(e)=>{
    const $li = $(e.currentTarget)
    $li
        .addClass("selected")
        .siblings()
        .removeClass('selected')
    const index = $li.index()
    $tabContent
        .children()
        .eq(index)
        .addClass('active')
        .siblings()
        .removeClass('active')
})

$tabBar.children().eq(0).trigger('click')

实现第三个模块-方块变成圆动画


创建js和css并引入

main.js

代码语言:javascript复制
import './reset.css'
import './global.css'
import './app1.css'
import './app2.css'
import './app3.css'
import './app4.css'
import './app1.js'
import './app2.js'
import './app3.js'

添加方块的div

代码语言:javascript复制
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name=viewport content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
    <title>曾老湿MVC</title>
</head>
<body>
    <div class="page">
        <section id="app1">
            <div class="output">
                <span id="number">100</span>
            </div>
            <div class="actions">
                <button id="add1"> 1</button>
                <button id="minus1">-1</button>
                <button id="mul2">*2</button>
                <button id="divide2">÷2</button>
            </div>
        </section>
        <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>
        <section id="app3">
            <div class="square"></div>
        </section>
        <section id="app4"></section>
    </div>
   <script src="main.js"></script> 
</body>
</html>

监听点击事件

代码语言:javascript复制
import $ from 'jquery'
const $square = $('#app3 .square')

$square.on('click',()=>{
    $square.toggleClass('active')
})

添加动画

代码语言:javascript复制
#app3{

}

#app3 .square{
    border: 1px solid gray;
    width: 10vw;
    height: 10vw;
    margin-top: 10vw;
    margin-left: 10vw;
    transition: transform 1s;
}

#app3 .square.active{
    transform: translateX(15vw);
}


加边框到全局css

代码语言:javascript复制
body{
    overflow: hidden;
}
body  > .page{
    display: flex;
    flex-wrap: wrap;
}
body  > .page > section{
    width: 50vw;
    height: 50vh;
    border: 1px solid gray;
}

实现第四个模块-点击圆会渐变


添加app4的css和js

创建并引入

代码语言:javascript复制
import './reset.css'
import './global.css'
import './app1.css'
import './app2.css'
import './app3.css'
import './app4.css'
import './app1.js'
import './app2.js'
import './app3.js'
import './app4.js'


写样式

代码语言:javascript复制
#app4{

}
#app4 .circle{
    border: 1px solid green;
    width: 20vw;
    height: 20vw;
    border-radius: 50%;
}


加动画

代码语言:javascript复制
#app4{

}
@keyframes change{
    0%{
        background: pink;
    }
    30%{
        background: plum;
    }
    60%{
        background: powderblue;
    }
    100%{
        background:blue;
    }
}
#app4 .circle{
    border: 1px solid green;
    width: 20vw;
    height: 20vw;
    border-radius: 50%;
    animation: change 2s infinite alternate linear;
}


修改鼠标移动才变

同样,添加一个active的类,然后用鼠标激活

代码语言:javascript复制
#app4{

}
@keyframes change{
    0%{
        background: pink;
    }
    30%{
        background: plum;
    }
    60%{
        background: powderblue;
    }
    100%{
        background:blue;
    }
}
#app4 .circle{
    border: 1px solid green;
    width: 20vw;
    height: 20vw;
    border-radius: 50%;
}
#app4 .circle.active{
    animation: change 2s infinite alternate linear;
}

监听鼠标事件

代码语言:javascript复制
import $ from 'jquery'
const $circle = $('#app4 .circle')
$circle.on('mouseenter',()=>{
    $circle.addClass('active')
}).on('mouseleave',()=>{
    $circle.removeClass('active')
})

app2数据保存

我们要保证,用户将标签切换到2的时候,即便是刷新页面还在2


修改app2的JS

代码语言:javascript复制
import $ from 'jquery'
const $tabBar =  $('#app2 .tab-bar')
const $tabContent = $('#app2 .tab-content')
const localKey = 'app2.index'
const index =  localStorage.getItem(localKey) || 0

$tabBar.on('click','li',(e)=>{
    const $li = $(e.currentTarget)
    $li
        .addClass("selected")
        .siblings()
        .removeClass('selected')
    const index = $li.index()
    localStorage.setItem(localKey,index)
    $tabContent
        .children()
        .eq(index)
        .addClass('active')
        .siblings()
        .removeClass('active')
})

$tabBar.children().eq(index).trigger('click')

app3数据保存

我们要让正方形的位置保持不变,即便是刷新了,那么照样在原来的位置,除非鼠标点击,才会移动


修改app3的JS

代码语言:javascript复制
import $ from 'jquery'
const $square = $('#app3 .square')
const localKey = 'app3.active'
const active = localStorage.getItem(localKey) === 'yes'

$square.toggleClass('active',active)

$square.on('click',()=>{
    if($square.hasClass('active')){
        $square.removeClass('active')
        localStorage.setItem(localKey,'no')
    }else{
        $square.addClass('active')
        localStorage.setItem(localKey,'yes')
    }
})

最小化知识点


修改app1.js

我们把html的内容,全部放到对应的JS中

app1.js

代码语言:javascript复制
import $ from 'jQuery'
const html = `
        <section id="app1">
            <div class="output">
                <span id="number">100</span>
            </div>
            <div class="actions">
                <button id="add1"> 1</button>
                <button id="minus1">-1</button>
                <button id="mul2">*2</button>
                <button id="divide2">÷2</button>
            </div>
        </section>
`
const $element = $(html).prependTo($('body>.page'))
const $button1 = $('#add1')
const $button2 = $('#minus1')
const $button3 = $('#mul2')
const $button4 = $('#divide2')
const $number = $('#number')
const n = localStorage.getItem('n')


$number.text(n || 100)

$button1.on("click",()=>{
    let n = parseInt($number.text())
    n  = 1
    localStorage.setItem('n',n)
    $number.text(n)
})

$button2.on("click",()=>{
    let n = parseInt($number.text())
    n -= 1
    localStorage.setItem('n',n)
    $number.text(n)
})

$button3.on("click",()=>{
    let n = parseInt($number.text())
    n *= 2
    localStorage.setItem('n',n)
    $number.text(n)
})

$button4.on("click",()=>{
    let n = parseInt($number.text())
    n /= 2
    localStorage.setItem('n',n)
    $number.text(n)
})

修改app2.js

app2.js

代码语言:javascript复制
import $ from 'jquery'
const 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>
`
const $element = $(html).appendTo($('body>.page'))
const $tabBar =  $('#app2 .tab-bar')
const $tabContent = $('#app2 .tab-content')
const localKey = 'app2.index'
const index =  localStorage.getItem(localKey) || 0

$tabBar.on('click','li',(e)=>{
    const $li = $(e.currentTarget)
    $li
        .addClass("selected")
        .siblings()
        .removeClass('selected')
    const index = $li.index()
    localStorage.setItem(localKey,index)
    $tabContent
        .children()
        .eq(index)
        .addClass('active')
        .siblings()
        .removeClass('active')
})

$tabBar.children().eq(index).trigger('click')

修改app3.js

app3.js

代码语言:javascript复制
import $ from 'jquery'
const html = `
<section id="app3">
<div class="square"></div>
</section>
`
const $element = $(html).appendTo($('body>.page'))

const $square = $('#app3 .square')
const localKey = 'app3.active'
const active = localStorage.getItem(localKey) === 'yes'

$square.toggleClass('active',active)

$square.on('click',()=>{
    if($square.hasClass('active')){
        $square.removeClass('active')
        localStorage.setItem(localKey,'no')
    }else{
        $square.addClass('active')
        localStorage.setItem(localKey,'yes')
    }
})

修改app4.js

app4.js

代码语言:javascript复制
import $ from 'jquery'
const html = `
<section id="app4">
<div class="circle"></div>
</section>
`
const $element = $(html).appendTo($('body>.page'))
const $circle = $('#app4 .circle')
$circle.on('mouseenter',()=>{
    $circle.addClass('active')
}).on('mouseleave',()=>{
    $circle.removeClass('active')
})

修改index.html

代码语言:javascript复制
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name=viewport content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
    <title>曾老湿MVC</title>
</head>
<body>
    <div class="page"></div>
   <script src="main.js"></script> 
</body>
</html>

功能全部实现了,但是...是 辣鸡代码

MVC以不变应万变


使用MVC实现第一个模块

首先我们要做以下操作: - 所有数据相关的都放到m - 所有视图相关的都放到v - 其他的都放到c

index.html

代码语言:javascript复制
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name=viewport content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
    <title>曾老湿MVC</title>
</head>
<body>
    <div class="page">
        <section id="app1"></section>
    </div>
   <script src="main.js"></script> 
</body>
</html>

main.js

代码语言:javascript复制
import './reset.css'
import './global.css'
import './app1.css'
import './app2.css'
import './app3.css'
import './app4.css'
import x from './app1.js'
import './app2.js'
import './app3.js'
import './app4.js'

x.init('#app1')

app1.js

代码语言:javascript复制
import $ from 'jQuery'
const eventBus = $({})
// 数据相关都放到m
const m = {
    data: {
        n:parseInt(localStorage.getItem('n'))
    },
    create(){},
    delete(){},
    update(data){
        Object.assign(m.data,data)
        eventBus.trigger('m_updated')
        localStorage.setItem('n',m.data.n)
    },
    select(){}
}
// 视图相关都放到v
const v = {
    el: null,
    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="mul2">*2</button>
            <button id="divide2">÷2</button>
        </div>
    </div>
`,
    init(container){
        v.el = $(container)
    },
    render(n){
        if(v.el.children.length !== 0) v.el.empty()
        $(v.html.replace('{{ n }}',n)).appendTo(v.el)
    }
}

// 其他都放到c
const c = {
    init(container){
        v.init(container)
        v.render(m.data.n)
        c.autoBindEvents()
        eventBus.on('m_updated',()=>{
            v.render(m.data.n)
        })
    },
    events:{
        'click #add1' : 'add',
        'click #minus1' : 'minus',
        'click #mul2' : 'mul',
        'click #divide2' : 'divide'
    },
    add(){
        m.update({n:m.data.n  1})
    },
    minus(){
        m.update({n:m.data.n -1})
    },
    mul(){
        m.update({n:m.data.n *2})
    },
    divide(){
        m.update({n:m.data.n /2})
    },
    autoBindEvents(){
        for(let key in c.events){
            const value = c[c.events[key]]
            const spaceIndex = key.indexOf(' ')
            const part1 = key.slice(0,spaceIndex  1)
            const part2 = key.slice(spaceIndex)
            v.el.on(part1,part2,value)
        }
    }
}

export default c

写好代码后,我们把代码折叠起来,会发现,代码里就三个对象,m v c


直接把刚才的MVC套用给第二个模块

我们还是要做以下操作: - 所有数据相关的都放到m - 所有视图相关的都放到v - 其他的都放到c

但是我们可以直接把写好的代码抄过来,再改

index.html

代码语言:javascript复制
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name=viewport content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
    <title>曾老湿MVC</title>
</head>
<body>
    <div class="page">
        <section id="app1"></section>
        <section id="app2"></section>
    </div>
   <script src="main.js"></script> 
</body>
</html>

main.js

代码语言:javascript复制
import './reset.css'
import './global.css'
import './app1.css'
import './app2.css'
import './app3.css'
import './app4.css'
import x from './app1.js'
import y from './app2.js'
import './app3.js'
import './app4.js'

x.init('#app1')
y.init('#app2')

app2.js

代码语言:javascript复制
import $ from 'jquery'
const eventBus = $({})
// 数据相关都放到m
const localKey = 'app2.index'
const m = {
    data: {
        index: parseInt(localStorage.getItem(localKey) || 0)
    },
    create(){},
    delete(){},
    update(data){
        Object.assign(m.data,data)
        eventBus.trigger('m_updated')
        localStorage.setItem('index',m.data.index)
    },
    select(){}
}
// 视图相关都放到v
const v = {
    el: null,
    html:(index) =>{
        return `
    <div>
    <ol class="tab-bar">
        <li class="${index === 0 ? 'selected' :''}" data-index="0">1</li>
        <li class="${index === 1 ? 'selected' :''}" data-index="1">2</li>
    </ol>
    <ol class="tab-content">
        <li class="${index === 0 ? 'active' :''}">内容1</li>
        <li class="${index === 1 ? 'active' :''}">内容2</li>
    </ol>
    </div>
`
    },
    init(container){
        v.el = $(container)
    },
    render(index){
        if(v.el.children.length !== 0) v.el.empty()
        $(v.html(index)).appendTo(v.el)
    }
}
// 其他都放到c
const c = {
    init(container){
        v.init(container)
        v.render(m.data.index)
        c.autoBindEvents()
        eventBus.on('m_updated',()=>{
            v.render(m.data.index)
        })
    },
    events:{
        'click .tab-bar li' : 'x'
    },
    x(e){
        const index = parseInt(e.currentTarget.dataset.index)
        m.update({index:index})
    },
    autoBindEvents(){
        for(let key in c.events){
            const value = c[c.events[key]]
            const spaceIndex = key.indexOf(' ')
            const part1 = key.slice(0,spaceIndex  1)
            const part2 = key.slice(spaceIndex)
            v.el.on(part1,part2,value)
        }
    }
}
export default c

使用类优化代码的Model

我们把公共的属性,抽出来

编程思想,事不过三: 同样的代码写三遍,就应该抽出来写成一个函数 同样的属性写三遍,就应该抽出来写成共用属性(原型或类) 同样的原型写三遍,就应该用集成

代价: 有的时候会造成继承层级太深,无法一下看懂代码 可以通过写文档,或者画图来解决


抽tmd

先创建一个base目录,然后在下面创建Model.js

代码语言:javascript复制
class Model{
    create(){}
    delete(){}
    update(){}
    select(){}
}

所有的类都有增删改查四个属性

添加警告

代码语言:javascript复制
class Model{
    create(){
        if(console && console.error)console.error('你还没有实现create')
    }
    delete(){
        if(console && console.error)console.error('你还没有实现delete')
    }
    update(){
        if(console && console.error)console.error('你还没有实现update')
    }
    select(){
        if(console && console.error)console.error('你还没有实现select')
    }
}

可以简化

代码语言:javascript复制
class Model{
// ?. 可选链
    create(){
        console?.error?.('你还没有实现create')
    }
    delete(){
        console?.error?.('你还没有实现delete')
    }
    update(){
        console?.error?.('你还没有实现update')
    }
    select(){
        console?.error?.('你还没有实现select')
    }
}

最新语法,有很多编辑器都不支持。

model如何使用呢?

代码语言:javascript复制
class Model{
    create(){
        console && console.error && console.error('你还没有实现create')
    }
    delete(){
        console && console.error && console.error('你还没有实现delete')
    }
    update(){
        console && console.error && console.error('你还没有实现update')
    }
    select(){
        console && console.error && console.error('你还没有实现select')
    }
}

const m = new Model()

m.create()
m.delete()
m.update()
m.select()

传递 data

代码语言:javascript复制
class Model{
    constructor(options){
        this.data =  options.data
    }
    create(){
        console && console.error && console.error('你还没有实现create')
    }
    delete(){
        console && console.error && console.error('你还没有实现delete')
    }
    update(){
        console && console.error && console.error('你还没有实现update')
    }
    select(){
        console && console.error && console.error('你还没有实现select')
    }
}

export default Model

修改app1的JS

上面我们的Model完成了,那么就可以修改app1的js了

app1.js

代码语言:javascript复制
import $ from 'jQuery'
import Model from './base/Model.js'
const eventBus = $({})
// 数据相关都放到m

const m = new Model({
    data: {
        n:parseInt(localStorage.getItem('n'))
    },
    update: function(data){
        Object.assign(m.data,data)
        eventBus.trigger('m_updated')
        localStorage.setItem('n',m.data.n)
    }
})

// 视图相关都放到v
const v = {
    el: null,
    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="mul2">*2</button>
            <button id="divide2">÷2</button>
        </div>
    </div>
`,
    init(container){
        v.el = $(container)
    },
    render(n){
        if(v.el.children.length !== 0) v.el.empty()
        $(v.html.replace('{{ n }}',n)).appendTo(v.el)
    }
}

// 其他都放到c
const c = {
    init(container){
        v.init(container)
        v.render(m.data.n)
        c.autoBindEvents()
        eventBus.on('m_updated',()=>{
            v.render(m.data.n)
        })
    },
    events:{
        'click #add1' : 'add',
        'click #minus1' : 'minus',
        'click #mul2' : 'mul',
        'click #divide2' : 'divide'
    },
    add(){
        m.update({n:m.data.n  1})
    },
    minus(){
        m.update({n:m.data.n -1})
    },
    mul(){
        m.update({n:m.data.n *2})
    },
    divide(){
        m.update({n:m.data.n /2})
    },
    autoBindEvents(){
        for(let key in c.events){
            const value = c[c.events[key]]
            const spaceIndex = key.indexOf(' ')
            const part1 = key.slice(0,spaceIndex  1)
            const part2 = key.slice(spaceIndex)
            v.el.on(part1,part2,value)
        }
    }
}

export default c

Model.js

代码语言:javascript复制
class Model{
    constructor(options){
        ['data','update','create','delete','select'].forEach((key)=>{
            if(key in options){
                this[key] = options[key]
            }
        })
    }
    create(){
        console && console.error && console.error('你还没有实现create')
    }
    delete(){
        console && console.error && console.error('你还没有实现delete')
    }
    update(){
        console && console.error && console.error('你还没有实现update')
    }
    select(){
        console && console.error && console.error('你还没有实现select')
    }
}

export default Model

修改app2的JS

app2.js

代码语言:javascript复制
import $ from 'jquery'
import Model from './base/Model.js'
const eventBus = $({})
// 数据相关都放到m
const localKey = 'app2.index'
const m = new Model({
    data: {
        index: parseInt(localStorage.getItem(localKey) || 0)
    },
    update(data){
        Object.assign(m.data,data)
        eventBus.trigger('m_updated')
        localStorage.setItem('index',m.data.index)
    },
})
// 视图相关都放到v
const v = {
    el: null,
    html:(index) =>{
        return `
    <div>
    <ol class="tab-bar">
        <li class="${index === 0 ? 'selected' :''}" data-index="0">1</li>
        <li class="${index === 1 ? 'selected' :''}" data-index="1">2</li>
    </ol>
    <ol class="tab-content">
        <li class="${index === 0 ? 'active' :''}">内容1</li>
        <li class="${index === 1 ? 'active' :''}">内容2</li>
    </ol>
    </div>
`
    },
    init(container){
        v.el = $(container)
    },
    render(index){
        if(v.el.children.length !== 0) v.el.empty()
        $(v.html(index)).appendTo(v.el)
    }
}
// 其他都放到c
const c = {
    init(container){
        v.init(container)
        v.render(m.data.index)
        c.autoBindEvents()
        eventBus.on('m_updated',()=>{
            v.render(m.data.index)
        })
    },
    events:{
        'click .tab-bar li' : 'x'
    },
    x(e){
        const index = parseInt(e.currentTarget.dataset.index)
        m.update({index:index})
    },
    autoBindEvents(){
        for(let key in c.events){
            const value = c[c.events[key]]
            const spaceIndex = key.indexOf(' ')
            const part1 = key.slice(0,spaceIndex  1)
            const part2 = key.slice(spaceIndex)
            v.el.on(part1,part2,value)
        }
    }
}
export default c

使用类优化代码的View

代码语言:javascript复制
class  View{
    constructor({el,html,render}){
        this.el = el
        this.html = html
        this.render = render
    }
}

export default View

修改app1的JS

app1.js

代码语言:javascript复制
import $ from 'jQuery'
import Model from './base/Model.js'
import View from './base/View.js'
const eventBus = $({})
// 数据相关都放到m

const m = new Model({
    data: {
        n:parseInt(localStorage.getItem('n'))
    },
    update: function(data){
        Object.assign(m.data,data)
        eventBus.trigger('m_updated')
        localStorage.setItem('n',m.data.n)
    }
})

// 视图相关都放到v


// 其他都放到c
const c = {
    v:null,
    initV(){
        c.v = new View({
            el:c.container,
            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="mul2">*2</button>
                    <button id="divide2">÷2</button>
                </div>
            </div>
        `,
            render(n){
                if(c.v.el.children.length !== 0) c.v.el.empty()
                $(c.v.html.replace('{{ n }}',n)).appendTo(c.v.el)
            }
        })
    },
    init(container){
        c.container = container
        this.initV()
        c.v.render(m.data.n)
        c.autoBindEvents()
        eventBus.on('m_updated',()=>{
            c.v.render(m.data.n)
        })
    },
    events:{
        'click #add1' : 'add',
        'click #minus1' : 'minus',
        'click #mul2' : 'mul',
        'click #divide2' : 'divide'
    },
    add(){
        m.update({n:m.data.n  1})
    },
    minus(){
        m.update({n:m.data.n -1})
    },
    mul(){
        m.update({n:m.data.n *2})
    },
    divide(){
        m.update({n:m.data.n /2})
    },
    autoBindEvents(){
        for(let key in c.events){
            const value = c[c.events[key]]
            const spaceIndex = key.indexOf(' ')
            const part1 = key.slice(0,spaceIndex  1)
            const part2 = key.slice(spaceIndex)
            c.v.el.on(part1,part2,value)
        }
    }
}

export default c

合并V和C


修改app1的JS

app1.js

代码语言:javascript复制
import $ from 'jQuery'
import Model from './base/Model.js'
const eventBus = $({})
// 数据相关都放到m

const m = new Model({
    data: {
        n:parseInt(localStorage.getItem('n'))
    },
    update: function(data){
        Object.assign(m.data,data)
        eventBus.trigger('m_updated')
        localStorage.setItem('n',m.data.n)
    }
})
// 其他都放到c
const view = {
    el: null,
    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="mul2">*2</button>
            <button id="divide2">÷2</button>
        </div>
    </div>
`,
    init(container){
        view.el = $(container)
        view.render(m.data.n)
        view.autoBindEvents()
        eventBus.on('m_updated',()=>{
            view.render(m.data.n)
        })
    },
    render(n){
        if(view.el.children.length !== 0) view.el.empty()
        $(view.html.replace('{{ n }}',n)).appendTo(view.el)
    },
    events:{
        'click #add1' : 'add',
        'click #minus1' : 'minus',
        'click #mul2' : 'mul',
        'click #divide2' : 'divide'
    },
    add(){
        m.update({n:m.data.n  1})
    },
    minus(){
        m.update({n:m.data.n -1})
    },
    mul(){
        m.update({n:m.data.n *2})
    },
    divide(){
        m.update({n:m.data.n /2})
    },
    autoBindEvents(){
        for(let key in view.events){
            const value = view[view.events[key]]
            const spaceIndex = key.indexOf(' ')
            const part1 = key.slice(0,spaceIndex  1)
            const part2 = key.slice(spaceIndex)
            view.el.on(part1,part2,value)
        }
    }
}

export default view

修改app2的JS

app2.js

代码语言:javascript复制
import $ from 'jquery'
import Model from './base/Model.js'
const eventBus = $({})
// 数据相关都放到m
const localKey = 'app2.index'
const m = new Model({
    data: {
        index: parseInt(localStorage.getItem(localKey) || 0)
    },
    update(data){
        Object.assign(m.data,data)
        eventBus.trigger('m_updated')
        localStorage.setItem(localKey,m.data.index)
    },
})
// 其他都放到c
const view = {
    el: null,
    html:(index) =>{
        return `
    <div>
    <ol class="tab-bar">
        <li class="${index === 0 ? 'selected' :''}" data-index="0">1</li>
        <li class="${index === 1 ? 'selected' :''}" data-index="1">2</li>
    </ol>
    <ol class="tab-content">
        <li class="${index === 0 ? 'active' :''}">内容1</li>
        <li class="${index === 1 ? 'active' :''}">内容2</li>
    </ol>
    </div>
`
    },
    render(index){
        if(view.el.children.length !== 0) view.el.empty()
        $(view.html(index)).appendTo(view.el)
    },
    init(container){
        view.el = $(container)
        view.render(m.data.index)
        view.autoBindEvents()
        eventBus.on('m_updated',()=>{
            view.render(m.data.index)
        })
    },
    events:{
        'click .tab-bar li' : 'x'
    },
    x(e){
        const index = parseInt(e.currentTarget.dataset.index)
        m.update({index:index})
    },
    autoBindEvents(){
        for(let key in view.events){
            const value = view[view.events[key]]
            const spaceIndex = key.indexOf(' ')
            const part1 = key.slice(0,spaceIndex  1)
            const part2 = key.slice(spaceIndex)
            view.el.on(part1,part2,value)
        }
    }
}
export default view

重构View.js

我们合并之后,View的功能就多了,现在重构一下

app2.js

代码语言:javascript复制
import $ from 'jquery'
import Model from './base/Model.js'
import View from './base/View.js'
const eventBus = $({})
// 数据相关都放到m
const localKey = 'app2.index'
const m = new Model({
    data: {
        index: parseInt(localStorage.getItem(localKey) || 0)
    },
    update(data){
        Object.assign(m.data,data)
        eventBus.trigger('m_updated')
        localStorage.setItem(localKey,m.data.index)
    },
})
// 其他都放到c
const init = (el)=>{
    const view = new View({
        el: el,
        eventBus: eventBus,
        data:m.data,
        html:(index) =>{
            return `
        <div>
        <ol class="tab-bar">
            <li class="${index === 0 ? 'selected' :''}" data-index="0">1</li>
            <li class="${index === 1 ? 'selected' :''}" data-index="1">2</li>
        </ol>
        <ol class="tab-content">
            <li class="${index === 0 ? 'active' :''}">内容1</li>
            <li class="${index === 1 ? 'active' :''}">内容2</li>
        </ol>
        </div>
    `
        },
        render(data){
            const index = data.index
            if(this.el.children.length !== 0) this.el.empty()
            $(this.html(index)).appendTo(this.el)
        },
        events:{
            'click .tab-bar li' : 'x'
        },
        x(e){
            const index = parseInt(e.currentTarget.dataset.index)
            m.update({index:index})
        },
    
    })
}
export default init

View.js

代码语言:javascript复制
import $ from 'jquery'
class  View{
    //constructor({el,html,render,data,eventBus,events}){
    constructor(options){
        Object.assign(this,options)
        this.el = $(this.el)
        this.render(this.data)
        this.autoBindEvents()
        this.eventBus.on('m_updated',()=>{
            this.render(this.data)
        })
    }
    autoBindEvents(){
        for(let key in this.events){
            const value = this[this.events[key]]
            const spaceIndex = key.indexOf(' ')
            const part1 = key.slice(0,spaceIndex  1)
            const part2 = key.slice(spaceIndex)
            this.el.on(part1,part2,value)
        }
    }
}

export default View

0 人点赞