8-2 MVC(六大思维)

思维1.最小知识原则

  • 不需要知道别人要做什么内容,只需要提供接口即可
  • 当:引入一个模块需要引入 html、css、js
    创建四个模块:
    1.需要在index.html中link引入CSS
    2.需要script引入JS
    3.需要分别写入四个模块的HTML标签
  • 当:引入一个模块需要引入 html、js
    将app.css独立文件,引入app.js中,由JS引入CSS,省掉link引入
    import "./app1.css";
  • 当:引入一个模块只需要引入 js
    将index.html中的每个app的HTML标签模块放入app.js中,然后插入到HTML文件里
    这样index.html中就只需要script引入一个JS文件
const html =`
HTML app 标签内容
`
const $element = $(html).appendTo($('body>.page'))
  • 经过上面的简化,别人在写代码的时候只需要:
    1.写一个空的div.page,然后引入一个main.js
    2.然后在main.js中引入四个模块的JS文件
    3.不需要担心这四个模块的内容,因为这4个模块会自己照顾好自己
  • 模块化为这一点奠定了基础
    之前的代码就是一团糟,什么代码都在一个文件里面,每次找起来都非常麻烦

1.2 代价

  • 这样做会使得页面一开始是空白的,没内容没样式
    当网速慢的时候:什么都没有,因为JS加载引入都需要时间
  • 解决方法很多,比如加菊花、加骨架、加占位内容等
    1.菊花:gif loading(加载中)
    操作:在main.js文件最后加一句img.remove(),只要前面的模块JS文件都加载好了,就把图片删掉
    优点:不需要做任何优化,有这个gif图片用户就会感到安心,空白页面用户等不了
    2.骨架:页面的框架结构(没有内容)
    3.占位内容:加载中…、文章标题
  • 也有人选择用 SSR技术来解决,现在没必要学习

1.3 最小知识原则的优点

  • 如果把所有的代码都写在一个文件里,过了几天之后就完全不记得自己写的什么了
  • 模块化封装很好的时候,看一眼就知道大概结构内容是什么

思维2.以不变应万变

  • 既然每个模块都可以用 m + v + c 搞定
  • 那么每个模块我就都这样写就好啦
  • 不用再思考类似的需求该怎么做了
  • 代价:
    1.有时候会有一些多余用不到的代码
    2.有时候遇到特殊情况不知道怎么变通,比如没有 html 的模块怎么做 mvc

2.1 理解MVC的思维

  • 写个m对象,v对象,c对象
  • 给app.js文件中的代码加上大概框架注释
    如:app1.js文件
    1.引入(import)css文件和jQuery
    2.初始化html
    3.寻找重要元素
    4.初始化数据
    5.将数据渲染到页面
    6.绑定鼠标事件
    主要分为两个方向:绑定事件+渲染数据
  • 将app1.js中的内容按照MVC的逻辑来理一下
    1.所有与数据相关的都放到对象M
    2.所有跟视图相关(用户看得见的)的都放到对象V
    3.其他的都放到对象C
const m = {}
const v = {}
const c = {}

2.2 对象V

  • 引入的html标签可以放到v中
    注意:不能用变量了,就变成属性,key是html,value是字符串
  • 把HTML渲染到page,appendTo()
    放到对象V中作为函数render(),其中的html变量就要改为 v.html
    后面初始化就是:v.render()
  • 将数据渲染到页面
    函数updata

2.3 对象M

  • 初始化数据

2.4 对象C

  • 绑定事件和寻找重要元素都属于其他操作,放到C中
  • 找到的重要元素作为一个新对象ui放到对象C中,key前面就不需要$了
  • 绑定事件 bindEvent()函数

2.5 Bug

  • 用MVC的方式修改了代码之后,会出现很多问题
  • 需要修改变量名,修改函数的调用逻辑
  • 因此需要不断调试修改

2.6 传参(app1中的bug)

  • app1中将元素添加到 body .page中
  • 我们可以用户自定义添加的位置,标签名设定为:container,则需要传入参数
    render(container)、appendTo($(container))
  • 这样才初始化 c.init() 的时候就必须要得到一个东西
  • 如何得到参数?
    答:只能把c暴露出去:export default c
    不再需要c.init()
  • 此时app1.js就不是自己运行自己,而是需要别人传参才能运行起来
  • 在main.js中修改为:
    import x from "./app1.js"
    引入这个x就是c的地址
  • 在main.js中给一个container
    x.innit('#app1')
    意思是:把页面中的app1传给模块去初始化

2.7 绑定事件

  • 问题
    1.就是app1.js中html字符串中的内容,在进行一次运算后重新渲染一次
    2.html重新渲染替换这也导致绑定在上面的事件不见了,也就是无法进行第二次操作
  • 解决
    1.需要这个容器 container 里有东西是不可变的,是不被替换掉的
    2.将section不放在字符串中,而是index.html中
    3.这样每次渲染替换也只是替换section里面的内容,而section是不变的
  • 如此
    1.索性把事件绑定在section标签上,进行事件委托
    2.然后在onclick里面判断点击的是不是 #add1 等子元素
v.container.on('click','#add1',()=>{
  m.data.n += 1
  v.render()
})

思维3 表驱动编程

  • 当你看到大批类似但不重复的代码
  • 眯起眼睛,看看到底哪些才是重要的数据
  • 把重要的数据做成哈希表,你的代码就简单了
  • 这是数据结构知识给我们的红利

思维4 事不过三

  • 同样的代码写三遍,就应该抽成一个函数
  • 同样的属性写三遍,就应该做成共用属性(原型或类)
  • 同样的原型写三遍,就应该用继承
  • 代价:
    1.有的时候会造成继承层级太深,无法一下看懂代码
    2.可以通过写文档、画类图解决

思维5 俯瞰全局

  • 把所有的对象看成点
  • 一个点和一个点怎么通信
  • 一个点和多个点怎么通信
  • 多个点和多个点怎么通信
  • 最终我们找出一个专用的点负责通信
  • 这个点就是 event bus(事件总线)

5.1 eventBus(思维6中会使用)

1.声明

  • const eventBus = $(window)
  • eventBus里面是一个我们传过去的window,全局变量
  • 因为我们根本不需要对象,而是需要对象的两个API
    1.on 方法——监听事件
    2.trigger 方法——触发事件
  • 也就是说eventBus有一个on函数,还有一个trigger函数

2.对m进行增删改查

  • 由于操作是对于m中的n进行操作
  • 则在m对象中添加一个改函数 :updata()
    也就是给我一个什么值,我就把n改成对应的值(所有属性都修改)
  • m更新了之后就会触发一个更新事件
update(data){
  //把data的所有属性全部给m.data
  Object.assign(m.data,data)
  //大吼一声,我更新了
  eventBus.trigger('m:updated')//字符串事件不能加空格
}
  • 监听这个事件,在c对象的init中添加一个监听
    一旦m更新了,就render()
eventBus.on('m:data',()=>{
  v.render(m.data.n)
})
  • 操作后,再调用add、minus等函数的时候就不需要每次都render
    注意:由于只有update才能监听事件,因此n的操作需要updata来进行
add(){
  m.update(data{n: m.data.n + 1})
}

思维6 view = render(data)

  • 所有的视图就是把数据给渲染一下
  • 这个思维造成了react的诞生
  • 比起操作 DOM 对象,直接 render 简单多了
  • 只要改变 data,就可以得到对应的 view
  • 代价
    1.render 粗犷的渲染肯定比 DOM 操作浪费性能
    (只要有改变就重新渲染页面和找到DOM对应元素然后添加/删除class,肯定是后者更加节省性能)
    2.还好我们后面会用到虚拟 DOM
    3.虚拟 DOM 能让 render 只更新该更新的地方 (不会整个页面全部渲染)

1.理解

  • 在JS中写代码,但操作的是DOM(也就是页面)

1.2 以操作 +1 功能为例子

  • 获取页面中的span的text
  • 然后 n+=1
  • 然后将 n 放到 text 里面
  • 数据流向示意图(DOM --> JS --> DOM)
    在这里插入图片描述

1.3 react思想

  • 每次更新都要进行这个操作非常的麻烦
  • 简化数据流向(JS --> DOM)
    1.首先声明 n = 100
    2.然后渲染,页面上得到100的span
    3.如果点击一下,就 n+=1
    4.然后再渲染一次,页面上就得到 101的span
  • 特点:数据流向一直是左(JS)到右(DOM)
    没有右到左
    在这里插入图片描述

1.4 以app1为例

  • 从本地数据库中拿到n
  • 初始化的时候进行第一次渲染
    v.render(m.data.n)——视图等于渲染数据
  • 当用户点击 +1 的时候更新这个n,进行第二次渲染
  • 往后点击每一个操作,都会进行一次渲染,然后JS把n传给DOM

1.4.1 问题1:把一堆事件绑定改成哈希表

  • 当进行上面的修改之后,出现了一个问题——重复!
  • 除了个别参数和运算不同,代码结构都是一样的
v.el.on('click','#add1',()=>{
  m.data.n += 1
  v.render(m.data.n)
})
  • 简化思想:把一样的东西都隐藏掉,只留下需要的东西
  • 解决:
    1.声明一个对象events,把操作的思想写在里面
    (点击 #add1,就调用add函数,然后声明函数,其他操作同理)
    2.只写不同的,重复的都不抄
events:{
'click #add1': 'add'
'click #minus1': 'minus'}
add(){
  m.data.n += 1
}
minus(){
  m.data.n -= 1
}
  • 写好对象和函数后,声明一个 autoBindEvents 函数
    1.获取到c中的events,然后进行遍历:for(let key in events)
    2.遍历获取到了所有的key,那么使用空格来分割,得到用户想要的操作click 和 add等
autoBindEvents(){
  for(let key in events){
  //得到events里面对应的value,然后去c对象里找对象的函数
    const value = c[c.events[key]]
    const spaceIndex = key.indexOf(' ')
    const part1 = key.slice(0,spaceIndex)
    const part2 = key.slice(spaceIndex+1)//不要空格
    v.el.on(part1,part2,value)
  }
}

1.4.2 问题2:如何简化在每次操作1次之后就渲染

  • 真实需求:n一变,就自动渲染视图
  • 解决:监听n的变化
    1.vue的API
    2.eventBus 对象间通信
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值