目录 § ⇧
- 1. 浅析 MVC §
- 1.1. MVC是什么 §
- 1.2. 表驱动编程 §
- 1.3. EventBus §
- 1.4. 理解模块化 §
- 2. 模块化文件导入(CSS jQuery) §
- 3. 四个需求描述 §
- 4. 实现模块 §
- 5. 抽象思维1:最小知识原则 js单一模块化 §
- 6. 抽象思维2: 以不变应万变 封装成mvc §
- 7. 抽象思维3 用数据去渲染
view = render(data)
§ - 8. 抽象思维4 表驱动编程 自动绑定事件 §
- 9. 抽象思维5 俯瞰全局 对象间通信
EventBus
§ - 10. 第二个模块的使用MVC §
- 11. 抽象思维6 事不过三 类优化代码继承 §
- 12. 合并vc §
- 13. M V 继承EventBus §
- 14. 过渡到 vue §
- 15. 总结 §
1. 浅析 MVC § ⇧
1.1 MVC是什么 § ⇧
- 设计模式
- DRY原则:
Don't Repeat Yourself
- 前端MVC变形记
什么是重复
代码级别的重复
- 三行代码写了两遍
- 就需要重构
- 函数封装
- 组件封装
- 库封装
- 造轮子
页面级别
- 类似的页面做了10遍
- 需要一个万金油写法
MVC就是万金油
- 所有页面都可以使用MVC优化代码结构
- 避免意大利面条式代码
MVC 的定义并不明确 MVC三个对象 分别做什么
- 每个模块都可以写成三个对象,分别是M、V、C
- M - Model(数据模型)负责操作所有数据相关的任务
- V - View(视图)负责所有UI(用户)界面
- C -Controller(控制器)负责监听用户事件,然后调用 M 和 V 更新数据和视图
- 给出伪代码示例
|
|
MVC 乱弹
- react MV* 单向绑定 函数式Hooks
- vue3 函数式 TS 单文件模板
- Angular2 JS Dart TS
1.2. 表驱动编程 § ⇧
表指的是
hash
表 hashTable
- 提高代码可读性 易维护
- 减少重复代码
- 可扩展性
- 可重用性
- 主干清晰
- 恒定复杂度
- 隔离变化
- 机制和策略的分离
相对于程序逻辑,处理数据更易懂
- 将设计的复杂度从程序代码转移至数据
参考
- JAVA表驱动编程总结
- C#表驱动法编程(数据驱动)
- Table-Driven Design 表驱动设计 大三上
- 表驱动法 -《代码大全》读书笔记
- 空间换时间,查表法的经典例子
- c印记(十三):表驱动编程——优美的逻辑优化者
- JS表驱动法(更优雅的写if-else、switch-case)
- Table-Driven Design
- Table-driven Approach
- table driven methods
- Table Driven Tests
- TableDrivenTests GitHub
- Chapter 18. Table-Driven Methods in Code Complete, Second Edition by
1.3. EventBus API § ⇧
给出伪代码示例
1.4. 理解模块化 § ⇧
2.四个需求描述 §
- 验证展示MVC
- 模块
- 类
- 四个需求:1. 加减乘除 2. Tab切换 3. 动画 4. 变色
3.模块化文件导入(CSS jQuery) § ⇧
模块化 脚本化加载CSS
- 在项目目录下打开终端
yarn初始化项目
|
|
npm/yarn install
|
|
- 注意包的名字都是小写,大写会导致安装错误或版本错误
- 用
npm info <name> versions
命令可以查看包的所有历史版本号
修改 package.json
|
|
发布
|
|
开发
|
|
main.js
|
|
4.实现模块 §
实现前两个模块
- 面条代码
index.html
|
|
添加数据保存功能
当用户刷新时保存数据
- 获取
localStorage.getItem
|
|
app1.js
|
|
app1.scss
|
|
- 注意宽度为
17px
(14~19px)左右的scroll bar
- 使用事件委托(jQuery):
$.on('click', () => {})
- 禁止使用逻辑与样式耦合的jQuery代码:
$.css({//...})
$.show()
$.hide()
- 使用添加/去除 样式的API:
addClass('active')
removeClass('active')
- 基于 样式和行为分离的 思想:JS只管功能不管具体样式,而CSS只管具体样式不管功能
app2.js
|
|
app2.scss
|
|
简化导入代码 import
- JavaScript modules 模块 MDN
- 使用
export default x
将一个变量默认导出给外部使用 - 使用
import from './xxx.js'
引用另一个模块导出的默认变量 - 使用
import {x} from './xxx.js'
引用另一个模块导出的名为 x 的变量
在app各自的js代码中引入css代码
- CSS的
@import
,性能低,一般不使用
实现后两个模块
-
不要用JS操作样式
-
面条代码
main.js
|
|
app3.scss
|
|
app3.js
|
|
app4.scss
|
|
app4.js
|
|
添加app2 app3数据保存功能
当用户刷新时保存数据
- 添加
localStorage
app2.js
|
|
app3.js
|
|
webStorm
报错
localStorage.setItem('app3.active', false)
参数类型是布尔值,- 不应赋值给
string
,而setItem
的取值只支持字符串 - 虽然会进行隐式转换类型为字符串
- 使用
getItem
取出的也是字符串'false'
,不严谨 - 用
localStorage.setItem('app3.active', 'no')
代替
5.抽象思维1 最小知识原则 js单一模块化 § ⇧
- 引入一个模块需要引入html、css、js
- 引入一个模块需要引入html、js
- 引入一个模块需要引入js
- 需要的越少越好
代价
- 会使得页面一开始是空白的,没内容样式:白屏不是bug,而是由于浏览器的渲染机制
- 解决方案有加菊花
gif loading
、加骨架(掘金)、加占位内容(文章标题)等 - 或用SSR
{{app1}}
{{app2}}
{{app3}}
{{app4}}
,大炮打蚊子
main.js
|
|
- ES6字符串模板
- 将不同模块的
html
标签变成字符串 放入js文件中 - 原来的
index.html
里只保留空的<div class="page"></div>
app1.js
|
|
app2.js
|
|
app3.js
|
|
app4.js
|
|
- 使用每个模块不用关心
HTML
是怎样的 - 注意
.prependTo($('body>.page'))
在webStorm
中智能显示的语法高亮,完胜VSCode
- 又有重复
const $element = $(html).appendTo($('body>.page'))
- 之后再重构插入节点
小结
- 在
index.html
只有空的结构 - 引入
main.js
的script
标签 main.js
里依次引入四个模块,不具体操作各个模块内容- 面条代码 -> 模块代码
- 代价是网速慢时,加载的页面空白(chrome模拟slow 3G网速)
6.抽象思维2 以不变应万变 封装成mvc三个对象 § ⇧
- 每个模块都可以用 m + v + c 搞定
- 每个模块不用再考虑类似的需求了?
代价
- 有多余的代码
- 特殊情况:无html的模块
每个模块都有重复的思路
|
|
- 数据相关放到 m
- 视图相关放到 v
- 其他相关放到 c
|
|
放在 v 还是放在 c
- 按目的 -> 是否在页面上看得见
- 比如既不是数据,也不是看得见的放到 c 里
~vim 重复操作快捷键 . 操作
app1.js
封装成 mvc 三个对象
|
|
- 一个缺点是从页面视图里拿数据
let n = parseInt(c.ui.number.text())
- 而不是数据模块
m.data.n
点击无效,未成功绑定事件
debugger
- 在点击事件里打印查看是否运行到
bindEvents()
- 执行了再看是否存在
c.ui.button1
|
|
c.ui.button1
是个空对象
- 说明未定义
c.ui.button1
|
|
- 之前改了代码顺序,代码运行至debugger断点处时,页面中没有button元素
- button 初始化在 render() 之前
- 在赋值
button1
的时候,就立即执行(调用)了$(#add1)
- 而此时
render()
还未执行 - 浏览器解析到函数调用,会立即调用
- 绑定事件
c.bindEvents()
在渲染页面之后v.render()
- 在定义
c.ui
里的元素前,页面里还没有渲染,所以并没有取到元素
在c中写一个初始化方法
init()
,当调用时init()
,才取到``c.ui`的属性,即 button 初始化
- 将绑定事件方法
c.bindEvents()
写进初始化方法里init()
- 即先初始化c.ui,再初始化绑定事件
c.bindEvents()
- 这样就可以实现先渲染,再初始化(绑定事件)
app1.js
|
|
刷新无效
- 第一次渲染时将不变的
html
传入了,没有用到数据中的n
const element = $(v.html).prependTo($('body>.page'))
- 在
html
中加上占位符{{number}}
- 传的时候将字符串
html
用replace()
app1.js
|
|
- 之后更新
update()
方法代替原来操作DOM - 未改变
m.data.n
,因为没有触发点击事件
|
|
再点击事件中,直接从
m.data
中获取数据,而不是在页面上读取字符串c.ui.number.text()
- 直接在事件监听函数中操作数据
- 然后在事件监听函数中重新调用渲染函数
v.render()
- 而不是之前直接操作DOM
c.ui.number.text(n)
- 不需要
update()
app1.js
|
|
渲染函数重复添加相同的节点(点击看效果)
|
|
- 每次渲染都添加,没有删除原来的,去掉
overflow: hidden;
可以看到
app1.js
m.data.n
是从localStorage.getItem('n')
中获取的一个字符串
|
|
- 添加一个标记,
render()
中未使用变量element
- 将视图渲染的结果放到
v
,初始值为null
- 每次渲染判断一下
el
,有就替换,没有再添加
app1.js
|
|
目前做到了 点击一次
- 先改数据
- 再根据数据,刷新整个自己
- bug: 点击只有一次有效
解决之前先看
v.el = $(v.html.replace('{{number}}', m.data.n)).prependTo($('body>.page'))
,怎么知道页面上有$('body>.page')
,最好是传入
- 如果页面上的
.page
未知,可以传入 - 用
c.init()
的参数去接收 - 在初始化的方法
c.init()
中渲染v.render()
一次,代替外部代码的先渲染,再初始化 - 先渲染,再做初始化的其他步骤
- 可以减少一次函数调用
- 再次体现了最小知识原则
渲染顺序 初始化顺序
- 初始化时接收一个参数,来记录渲染到页面的哪个位置
- 参数传递
init(container)
,可以是一个div标签 - 将
container
传给视图中的v.render(container)
- 不需要写死
$('body>.page')
,替换为$(container)
- 可以传递任意标签
app1.js
|
|
模块传参,导出变量
需要在初始化时,得到传入的
container
,需要在app1.js
外暴露变量
app1
怎么知道外部容器`- 在
app1.js
中无法直接得到,不可在在app1.js
里调用init()
- 暴露出变量
c
, 即导出变量export default c
|
|
app1.js
需要别的js文件来传入一个参数- 在
main.js
中导入c
,并初始化app1.js
- 搭好骨架
<section id="app1"></section>
app1
在main.js
里初始化,并接受参数`import x from “./app1”
main.js
|
|
- 写一个的容器作为骨架
x.init('#app1')
接受 id 为 app1 的容器- 同时删除
app1.js
中html
变量里 id 为 app1 的节点
index.html
|
|
- 修改对应的html
x
就是默认导出的c
,引入的就是c
的地址- 把页面中的
#app1
传入模块,之后初始化 - 用
<section id="app1"></section>
是因为css里写死了section
main.js
|
|
- 在
main.js
中导入app1.js
的c
:import x from "./app1"
- x 为导出时的变量名
- 使用
c
中的init()
方法:x.init('#app1')
,将页面上的节点传入 - 即将页面中的
#app1
传给app1.js
模块去初始化
再次运用了最小知识原则
- 模块不关心外部的 选择器 标签
解决点击只有一次有效的bug
- 事件监听没了,原因是刷新页面之后,页面上新的
button1
不是原来的引用 - 更新地址
v.el = newEl
注意每次刷新都是不同的引用 - 而绑定的监听函数是绑在替换之前初始化的
button1
节点上 - 操作
DOM
更新页面后 节点的引用改变(不是原来的节点) 绑定在节点上的事件也失效了 - 在不会动态更新的元素上绑定函数,即在不变的容器上添加事件监听
查看点击节点是否被替换,开发转工具相应节点添加属性
frank="yes"
|
|
- 如果改变了,就会消除
frank="yes"
- 确认该元素不会被更新
- 原来的
bindEvents()
中访问不到container
事件委托
绑定动态元素的事件无效 使用事件委托
- 将事件绑定到一个不会更新的标签上
- 使用事件委托
- 在
bindEvent()
如何获取到v.container
- 在
v.render()
时,记下一个变量v.container
- 目的是有一个地方可以存储
v.container
app1.js
|
|
- 视图的外部的容器监听里面的元素
- 容器不会被替换,只替换容器里面的东西
- 而监听函数就不会失效
改其他绑定事件
- 并且
c.ui
选出的元素不需要了,直接在事件委托中选择
app1.js
|
|
思路
- 每次刷新,
section
父容器(骨架)为不可变 - 每次点击时,更新整个父容器里面的
div
,原来绑定在button上的事件不存在了 - 但事件绑定在不变的父容器
section
上,并判断对应不同子元素,进行不同的操作,这就是事件委托 - 里面的引用变了,但
id
没变,每次点击:刷新子元素,重新绑定事件
反思:
MVC
带来什么好处
- 面向过程到面向对象的转变
- 模块化 各司其职
- 复杂度随着代码量平稳上升,而不是线性增长
折叠后的代码
app1.js
|
|
抽象思维2 以不变应万变 每个模块都可以用 m + v + c 搞定
- 适用所有MVC,万金油代码
- 每个页面都有数据
data
- 每个视图都有元素
el
- 每个视图都有容器
container
- 每个视图都有数据
html
- 每个视图都需要初始化
init()
- 每个视图都需要更新渲染
render()
- 每个控制器都需要初始化
init()
- 每个控制器都需要绑定事件
bindEvents()
- 没有逻辑缺失
合并视图元素
el
container
- 元素
el
和container
可以合并 container
第一个元素就是el
app1.js
|
|
简化代码
app1.js
|
|
再简化
|
|
v.container
代替了v.el
,但名字太长 改名v.el
v.el.children.length === 0
代替v.el === null
判断视图元素是否为空
|
|
7.抽象思维3 用数据去渲染 § ⇧
view = render(data)
- 比起操作
DOM
对象,拙接render简单多了 - 只要改变
data
,就可以得到对应的view
React
的诞生
代价
render
粗犷的渲染肯定比DOM操作耗费更多性能- 用虚拟
DOM
能让render
只更新改更新的地方
数据单向流动
- 同样是最小知识原则
none MVC
JS(#add1) | 数据流向 | DOM(页面) |
---|---|---|
n = span.text |
<--- |
100 |
n += 1 |
100 | |
span.text = n |
---> |
101 |
MVC(React)
JS(#add1) | 数据流向 | DOM(页面) |
---|---|---|
n = 100 |
||
render(n) |
---> |
100 |
n += 1 |
100 | |
render(n) |
---> |
101 |
用数据去渲染改代码
- 数据:
m.data.n
- 视图:
v.render(n)
v
不用知道m
的存在- 初始化
v.init(container)
和v.render(m.data.n)
分开
app1.js
|
|
重复的代码太多
- 一样的东西只写一遍
8.抽象思维4 表驱动编程-自动绑定事件 § ⇧
- 当看到大批类似但不重复的代码
- 眯起眼睛,看看到底哪些才是重要的数据
- 将重要的数据做成哈希表,代码就简单明了
- 这就是数据结构知识的红利
- 可以减少重复代码,只将重要的信息放在表里,然后利用表来编程
代价
- 没有代价
哈希表 描述在做什么
'click #add1': 'add'
:click
点击#add1
的时候,调用add
函数- 添加
add1
函数 - 重复的
v.render(m.data.n)
不抄 - 遍历哈希表 循环绑定事件
app1.js
|
|
guid报错
9.抽象思维5 俯瞰全局 对象间通信 § ⇧
- 把所有的对象看成点
- 一个点和一个点怎么通信
- 一个点和多个点怎么通信
- 多个点和多个点怎么通信
- 最终找出一个专用的点负责通信
- 这个点就是
event bus
(事件总线)
eventBus
主要功能
eventBus
主要用于对象间通信eventBus
提供了on
、off
和trigger
等API
on
用于监听事件,trigger
用域触发事件- 使用
eventBus
可以满足最小知识原则,m
和v
互相不知道对方的细节,但是却可以调用对方的功能
实现eventBus对象间通信
- 让
v
知道m
发生变化 - 如何使得
v.render(m.data.n)
和localStorage.setItem('n', m.data.n.toString())
从代码里消失 - 真实需求:
data
的 n 一变,就自动render
视图 - 监听
n
变化,直接更新数据,不关心视图 - 监听
n
变化的方法有:eventBus
vue2
的Object.definedProperty
vue3.0 proxy
Vue双向绑定原理 从vue2的Object.defineProperty到vue3的proxy
使用
eventBus
const eventBus = $({}); console.log(eventBus)
- 获取
jQuery
空对象元素的APIon
监听事件trigger
触发事件 console.log(eventBus.on)
console.log(eventBus.trigger)
- 在一个地方监听事件,在另一个地方标记触发事件
- 两个地方互相通信
m.update()
数据更新
- 将传入的参数
data
的所有可枚举的属性,覆盖到m.data
上 Object.assign(m.data, data)
- 触发标记
eventBus.trigger('m:updated')
- 在一个地方监听标记,在另一个地方标记触发事件
- 在数据 m 中更新数据,并标记触发事件
eventBus.trigger('m:updated')
app1.js
|
|
在 c 中监听触发标记
'm:updated'
- 在一个地方监听标记,在另一个地方标记触发事件
- 在数据 m 中更新数据,并标记触发事件
eventBus.trigger('m:updated')
- 在控制器 c.init 中,监听标记触发事件
eventBus.on()
,统一渲染页面v.render(m.data.n)
app1.js
|
|
debugger
过程
eventBus.trigger('m update')
中的事件名,不能有空格- 对于
m.trigger('x xx')
有空格的debugger
- 点击
add
没反应 - 找到
add() {}
函数,里面写一句console.log("add")
- 控制台里查看
add()
是否运行 - 执行到了,会调用
m.update()
,找到update() {}
函数 update() {}
函数里写一句console.log("here")
- 执行到了,触发
eventBus.trigger('m:updated')
,查看监听eventBus.on('m:updated',() => {})
- 监听是在
c.init
里,在eventBus.on('m:updated',() => {})
的回调函数中写一句console.log("here")
- 发现没有成功监听
eventBus
的事件没有调用,定位bug- 改一下事件名
xxx
,可以执行eventBus
eventBus.trigger('xxx')
中的事件名xxx
,不能有空格
小结2
- 数据
m
中 实现增删改查的方法 eventBus.trigger('xxx')
中的事件名xxx,不能有空格- 虚拟DOM改良
render
- events的代码抽象了所有操作
'click #add1': 'add'
- 即
'事件 选择器': '回调函数'
- c 中的控制函数,还原其最恰当本分的逻辑
add() {m.update({n: m.data.n += 1})}
,不用关心视图 v - 解耦
- JS简易实现eventBus
- An event bus example in jquery CodePen
- vue篇之事件总线(EventBus)
- EventBus原理深度解析
app1.js
|
|
10.第二个模块的使用MVC § ⇧
app2.js
|
|
- 不能在声明一个对象的时候,在对象的右边使用这个对象,会变成
undefined
main.js
|
|
app2.js
中导出 cexport default c
函数封装 字符模板替换
|
|
在render中使用html()调用
|
|
视图 v 中定义了 提供控制器 c 使用的回调函数
|
|
绑定自定义属性
data-index
用DOM做标记
|
|
获取标记
focus()
获取当前点击处的index是第几个- 把 data-index 写到 li 上, 在
focus()
中取dataset.index
- 更新数据
m.update({index: dataIndex})
|
|
- 用
webStorm
的local history
回顾代码,思考为什么使用MVC
none MVC
JS(#add2) | 数据流向 | DOM(页面) |
---|---|---|
$(html).appendTo |
---> |
tab Content |
DOM操作10行代码 |
tab0 | |
$li.addClass |
---> |
tab0 |
$tabContent.addClass |
---> |
tab1 |
MVC
JS(#add2) | 数据流向 | DOM(页面) |
---|---|---|
index = 0 |
||
render(n) |
---> |
tab0 |
index = 1 |
tab0 | |
render(n) |
---> |
tab1 |
MVC
复杂度稳定很小,应对复杂需求DOM
操作越复杂,代码线性增长
11.抽象思维6 事不过三 使用类优化代码 继承 § ⇧
- 同样的代码写三遍,就该抽成一个函数
- 同样的属性写三遍,就该做成共用属性(原型或类)
- 同样的原型写三遍,就该用继承
代价
- 有时候会造成继承层级太深,无法一下看懂代码
- 可以通过写文档,画类图解决
使用类优化代码
- 引入类(代替重复对象)
- 引入继承(代替重复功能)
- 路由
app2.js
代码结构
|
|
app1.js
和app2.js
重复的代码
- m create, delete, update, get
- v el, html, init, render
- c init, events, autoBindEvents
|
|
相同属性抽成公共属性 原型/类
- 注意大写
Model.js
- 实例化
const m = new Model({...})
Model.js
|
|
用类改写
app1.js
的m
import Model from './base/Model'
m.update
属性覆盖- Chrome 打出的
console.dir(m)
是构造函数 查看属于哪个类
app1.js
|
|
可以将
m.update
写到new Model
参数中
- 先改
Model.js
- 为了让实例化中的传参更明确 在类中的形参 传一个对象
option
- 对象
option.data
遍历简化代码['data', 'update', 'create', 'delete', 'get']
Model.js
|
|
- 遍历
['data', 'update', 'create', 'delete', 'get'].forEach((key) => {}
代码的复杂度是稳定的 - 无论数据规模多大,稳定地简单
- 可选链语法
?.
app1.js
的m
|
|
- 注意
update(data) {}
可以写成update: function(data) {}
- 在面向对象的构造函数中不建议写箭头函数,易错
app2.js
的m
- 可以点击Model自动引入
import Model from './base/Model'
|
|
app1.js
用类简化代码v
|
|
touch src/base/View.js
- 大写
View
- 不同的属性写到构造函数中
el
html
- 相同的写到原型/类中
init
render
通过虚拟DOM可以变成一样,之后再改render
目前,不放到共用属性里
View.js
解构赋值传参
|
|
app1.js
- 将 v 的初始化放到 c
- v 和 c 的联系紧密,共用为一个对象
- 必须在 c 中拿到 el ;拿到el后,才能初始化v
- 在
c
的init(container)
中,用c.initV()
代替v.init(container)
|
|
12.合并vc § ⇧
- v c 都有
init
,合并init
方法 - 对象合并重写 v 改成
c.v
app1.js
|
|
- 开始声明对象时就合并
- 过度到
vue
- 重构Refactor 变量名c -> view
- 确认重构 DO REFACTOR
app1.js
|
|
再次使用类 改 app2.js
修改前
app2.js
|
|
- 合并v c
- 重构对象名
view
View.js
|
|
- 重构 遍历
|
|
类继承
app1.js
|
|
main.js
|
|
- bug
n: parseFloat(localStorage.getItem('n')) || 100
- 搜索并去除 console.log, 右键
.cache
选 Mark Directory as > Excluded
13.让 M 和 V 继承 eventBus § ⇧
- 没有必要每次在实例化构造函数
new View
中声明eventBus
- 所有
eventBus
都是同一个 - 新建一个
EventBus.js
类的文件 - 偷梁换柱地使用jQuery封装的EventBus
EventBus.js
|
|
修改
app1.js
和app2.js
- 导入
import EventBus from './base/EventBus'
- 替换
const eventBus = $({})
为const eventBus = new EventBus()
- 抽像成类的好处在于,可以随时改变
EventBus
的实现 - 比如不依赖
jQuery
,用虚拟DOM
模块解耦
- 建立中间层 M V E
- 具体功能模块依赖中间模块
- 中间模块再依赖
jQuery
- 修改依赖时,只需重构中间模块,具体功能模块无需变动
- 中间模块成为胶水层 粘合底层和依赖
- 是用来隔绝底层细节
能否使
eventBus.trigger('m:updated')
直接用m.trigger('m:updated')
实现,来隐藏细节具体实现的method
- 让
Model
和View
都继承EventBus
- 实现Model中的
m.trigger('...')
,View中的this.on('...')
- 用类继承类
m.trigger
v.on
- 主流的库,以及JS原生的DOM都是这么做的
控制台
|
|
- DOM 的 EventTarget 就是 所谓的EventBus
- 这就是所有DOM元素都可以监听和触发事件的原因
- 所有DOM元素都继承于EventTarget
M V继承 EventBus
Model.js
|
|
- 先引入后继承
- 类继承,
extends
,必须在constructor
初始化里调用以下父类的初始化this._eventBus = $(window)
,即用super()
调用 - 否则报错:Missed super class constructor invocation,且只有共有属性/方法
View.js
|
|
- 之后就可以删除
app1 2
中的EventBus
引入和声明,直接用EventBus
的方法 - 注意
class View extends EventBus
后不可加括号,不能直接调用类作为函数调用:Cannot call a class as a function
['data', 'update', 'create', 'delete', 'get'].forEach()
的BUG- JS代码不能以括号开头,JS解释器会合并以括号开头的代码
|
|
- 写JS 要么每句话加分号;要么 不可以括号开头
- 不加分号报错?我还是选择不加分号
Model.js
|
|
目前的代码
src/base/EventBus.js
|
|
src/base/Model.js
|
|
src/base/View.js
|
|
src/app1.js
|
|
src/app2.js
|
|
src/main.js
|
|
14.MVC 过渡到 vue § ⇧
安装
vue
|
|
导入
|
|
报错
runtime-only built of vue
- 编译版
package.json
|
|
template
就是 html,默认用当前的替换原来的div- m 也可以并入 v
data: { n: parseFloat(localStorage.getItem('n')) || 100 },
- 自动更新
- 方法 监听函数放入
methods
- 监听数据变化 放入
watch
用
vue
改写app1.js
|
|
template
预封装html()
,样式的动态绑定JS:class
用
vue
改写app2.js
|
|
抽离数据
app1.js
|
|
app2.js
|
|
vue
也实现了EventBus
的继承
|
|
vue
没有体现抽象思维3 用数据去渲染view = render(data)
- 监听数据变化,自动局部渲染
- 元素没有被移除页面
<button id="xxx"></button>
数据改变刷新页面,id="xxx"
还在 - react显式地调用
view = render(data)
15.总结 § ⇧
- 再也不用DOM操作了,不用jQuery,直接在封装的元素上写逻辑,不需要id属性,id属性只给CSS用
- 6个抽象思维
- 抽象思维1 最小知识原则
js模块
模块解耦 - 抽象思维2 以不变应万变
m + v + c
- 抽象思维3 用数据去渲染
view = render(data)
- 抽象思维4 表驱动编程-自动绑定事件 将不同的部分作为字符串存入
hashMap
取用 - 抽象思维5 俯瞰全局
EventBus
- 抽象思维6 事不过三
class
继承
- 抽象思维1 最小知识原则
- 刻意用到项目中
- 写文章总结
参考文章
相关文章
- 无
- 作者: Joel
- 文章链接:
- 版权声明
- 非自由转载-非商用-非衍生-保持署名
- 河 掘 思 知 简