前端-MVVM

MVVM

MVVM 由以下三个内容组成

  • View :界⾯

  • Model :数据模型

  • ViewModel :作为桥梁负责沟通 View 和 Model

    在 JQuery 时期,如果需要刷新 UI 时,需要先取到对应的 DOM 再更新UI ,这样数据和业务的逻辑就和⻚⾯有强 耦合。
    

MVVM

在 MVVM 中, UI 是通过数据驱动的,数据⼀旦改变就会相应的刷新对应的UI , UI 如果改变,也会改变对应的数据。这种⽅式
就可以在业务处理中只关⼼数据的流转,⽽⽆需直接和⻚⾯打交道。 ViewModel 只关⼼数据和业务的处理,不关⼼ View 如何处
理数据,在这种情况下, View 和 Model 都可以独⽴出来,任何⼀⽅改变了也不⼀定需要改变另⼀⽅,并且可以将⼀些可复⽤的逻
辑放在⼀个ViewModel 中,让多个 View 复⽤这个 ViewModel 。
  • 在 MVVM 中,最核⼼的也就是数据双向绑定,例如 Angluar 的脏数据检测, Vue 中的数据劫持。

脏数据检测

  • 当触发了指定事件后会进⼊脏数据检测,这时会调⽤ $digest 循环遍历所有的数据观察者,判断当前值是否和先前的值有区别,如果检测到变化的话,会调⽤ $watch 函数,然后再次调⽤ $digest 循环直到发现没有变化。循环⾄少为⼆次 ,⾄多为⼗次。
  • 脏数据检测虽然存在低效的问题,但是不关⼼数据是通过什么⽅式改变的,都可以完成任务,但是这在 Vue 中的双向绑定是存在问题的。并且脏数据检测可以实现批量检测出更新的值,再去统⼀更新 UI ,⼤⼤减少了操作 DOM的次数。所以低效也是相对的,这就仁者⻅仁智者⻅智了。

数据劫持

  • Vue 内部使⽤了 Object.defineProperty() 来实现双向绑定,通过这个函数可以监听到 set 和 get 的事件。
var data = { name: 'yck' }
observe(data)
let name = data.name // -> get value
data.name = 'yyy' // -> change value
function observe(obj) {
// 判断类型
if (!obj || typeof obj !== 'object') {
return
}
Object.keys(obj).forEach(key => {defineReactive(obj, key, obj[key])
})
}
function defineReactive(obj, key, val) {
// 递归⼦属性
observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
console.log('get value')
return val
},
set: function reactiveSetter(newVal) {
console.log('change value')
val = newVal
}
})
}
  • 以上代码简单的实现了如何监听数据的 set 和 get 的事件,但是仅仅如此是不够的,还需要在适当的时候给属性添加发布订阅
<div>
{{name}}
</div>
  • 在解析如上模板代码时,遇到 就会给属性 name 添加发布订阅。
// 通过 Dep 解耦
class Dep {
constructor() {
this.subs = []
}
addSub(sub) {
// sub 是 Watcher 实例
this.subs.push(sub)
}
notify() {
this.subs.forEach(sub => {sub.update()
})
}
}
// 全局属性,通过该属性配置 Watcher
Dep.target = null
function update(value) {
document.querySelector('div').innerText = value
}
class Watcher {
constructor(obj, key, cb) {
// 将 Dep.target 指向⾃⼰
// 然后触发属性的 getter 添加监听
// 最后将 Dep.target 置空
Dep.target = this
this.cb = cb
this.obj = obj
this.key = key
this.value = obj[key]
Dep.target = null
}
update() {
// 获得新值
this.value = this.obj[this.key]
// 调⽤ update ⽅法更新 Dom
this.cb(this.value)
}
}
var data = { name: 'yck' }
observe(data)
// 模拟解析到 `{{name}}` 触发的操作
new Watcher(data, 'name', update)
// update Dom innerText
data.name = 'yyy'
  • 接下来,对 defineReactive 函数进⾏改造
function defineReactive(obj, key, val) {
// 递归⼦属性
observe(val)
let dp = new Dep()
Object.defineProperty(obj, key, {enumerable: true,
configurable: true,
get: function reactiveGetter() {
console.log('get value')
// 将 Watcher 添加到订阅
if (Dep.target) {
dp.addSub(Dep.target)
}
return val
},
set: function reactiveSetter(newVal) {
console.log('change value')
val = newVal
// 执⾏ watcher 的 update ⽅法
dp.notify()
}
})
}
  • 以上实现了⼀个简易的双向绑定,核⼼思路就是⼿动触发⼀次属性的getter 来实现发布订阅的添加

Proxy 与 Object.defineProperty 对⽐

  • Object.defineProperty 虽然已经能够实现双向绑定了,但是他还是有缺陷的。
  • 只能对属性进⾏数据劫持,所以需要深度遍历整个对象 对于数组不能监听到数据的变化
  • 虽然 Vue 中确实能检测到数组数据的变化,但是其实是使⽤了 hack 的办法,并且也是有缺陷的。
const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)
// hack 以下⼏个函数
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
methodsToPatch.forEach(function (method) {
// 获得原⽣函数
const original = arrayProto[method]
def(arrayMethods, method, function mutator (...args) {
// 调⽤原⽣函数
const result = original.apply(this, args)
const ob = this.__ob__
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
if (inserted) ob.observeArray(inserted)
// 触发更新
ob.dep.notify()
return result
})
})
  • 反观 Proxy 就没以上的问题,原⽣⽀持监听数组变化,并且可以直接对整个对象进⾏拦截,所以 Vue 也将在下个⼤版本中使⽤ Proxy 替换Object.defineProperty
let onWatch = (obj, setBind, getLogger) => {
let handler = {
get(target, property, receiver) {
getLogger(target, property)
return Reflect.get(target, property, receiver);
},
set(target, property, value, receiver) {
setBind(value);
return Reflect.set(target, property, value);
}
};
return new Proxy(obj, handler);
};
let obj = { a: 1 }
let value
let p = onWatch(obj, (v) => {
value = v
}, (target, property) => {
console.log(`Get '${property}' = ${target[property]}`);
})
p.a = 2 // bind `value` to `2`
p.a // -> Get 'a' = 2

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/22994.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sql的count函数优化

sql的count(1)函数会执行遍历表统计符合条件的数目&#xff0c;下面有两个sql 第一条&#xff1a; select count(1) from membership_call_detail_statistics a where a.repository_id f2a4ed6b3e074e33bd99998c1def26f8 and a.statistics_date between 2023-04-01 00:00:0…

Python 程序设计入门(006)—— 列表的操作(1):列表元素的增、删、改操作

Python 程序设计入门&#xff08;006&#xff09;—— 列表的操作&#xff08;1&#xff09;&#xff1a;列表元素的增、删、改操作 目录 Python 程序设计入门&#xff08;006&#xff09;—— 列表的操作&#xff08;1&#xff09;&#xff1a;列表元素的增、删、改操作一、创…

【MacOS】mac OS 安装HP 打印机驱动 hp laserjet m1213nf mfp

先确认一下机器复印是否正常.确认一下打印机的硬件. 如果复印没问题 1. 下载v5.1集合包Hewlett-Packard 打印机驱动程序 v5.1.1 (OS X) (中国) 2. 苹果商城中,下载Pacifist&#xff0c;运行Pacifist&#xff0c;用Pacifist程序打开v5.1并安装 3. 电脑--系统偏好设置--打印…

Kafka-副本数量设置

1. ISR副本数量设置 指的是存活的副本数量 ISR 机制的另外一个相关参数是 min.insync.replicas , 可以在 broker 或者主题级别进行配置&#xff0c;代表 ISR 列表中至少要有几个可用副本。这里假设设置为 2&#xff0c;那么当可用副本数量小于该值时&#xff0c;就认为整个分…

Redis BigKey案例

面试题&#xff1a; 阿里广告平台&#xff0c;海量数据里查询某一固定前缀的key小红书&#xff0c;你如何生产上限制keys*/flushdb/flushall等危险命令以防止误删误用&#xff1f;美团&#xff0c;MEMORY USAGE命令你用过吗&#xff1f;BigKey问题&#xff0c;多大算big&#…

服务器流量

1.服务器流量分为入流量和出流量 入流量&#xff08;Inbound Traffic&#xff09;是指流向服务器的数据流量&#xff0c;也就是客户端发送到服务器的数据。这些数据可能包括请求信息、文件上传等。 出流量&#xff08;Outbound Traffic&#xff09;是指从服务器流向客户端的数…

纯css实现九宫格图片

本篇文章所分享的内容主要涉及到结构伪类选择器&#xff0c;不熟悉的小伙伴可以了解一下&#xff0c;在常用的css选择器中我也有分享相关内容。 话不多说&#xff0c;接下来我们直接上代码&#xff1a; <!DOCTYPE html> <html lang"en"><head>&l…

matlab使用教程(8)—绘制三维曲面图

1网格图和曲面图 MATLAB 在 x-y 平面中的网格上方使用点的 z 坐标来定义曲面图&#xff0c;并使用直线连接相邻的点。mesh 和surf 函数以三维形式显示曲面图。 • mesh 生成仅使用颜色来标记连接定义点的线条的线框曲面图。 • surf 使用颜色显示曲面图的连接线和面。 MATL…

实战:Prometheus+Grafana监控Linux服务器及Springboot项目

文章目录 前言知识积累什么是Prometheus什么是Grafana怎样完成数据采集和监控 环境搭建docker与docker-compose安装docker-compose编写 监控配置grafana配置prometheus数据源grafana配置dashboardLinux Host Metrics监控Spring Boot 监控 写在最后 前言 相信大家都知道一个项目…

【ES】笔记-解构赋值

文章目录 一、解构赋值二、解构数组 1. 变量赋值2. 交换变量3. 默认值4. 不完全解构5. 解构数组嵌套6. 与...运算符结合使用 三、解构对象 1. 获取成员2. 对象赋值3. 默认值4. 解构嵌套对象 四、解构函数 1. 函数的参数2. 函数返回值 四、其他解构 1. 字符串3. 其他数据类型 …

19、springboot引用配置属性或引用生成文件的属性或引用随机值

引用配置属性或引用生成文件的属性或引用随机值 ★ 使用占位符引用其他配置属性&#xff1a; 配置文件中可用${}占位符引用已有的属性&#xff0c;被引用的属性可以是&#xff1a; - 已定义的属性。 - 来自其他配置源&#xff08;比如命令行的选项参数、环境变量、系统属性等…

SpringBoot项目上传至服务器

1.服务器安装JDK1.8 通过包管理器安装 2.服务器安装数据库 参考链接&#xff1a; CentOS 7 通过 yum 安装 MariaDB - 知乎 1. 安装之后没有密码&#xff0c;所以需要设置密码&#xff0c;使用下面的语句 set password for rootlocalhost password(111111); 2.在数据库中建…

07_Vue生命周期

Vue3生命周期 配置项的方式写Vue3生命周期 组合式API的方式写生命周期&#xff1a;写在setup里 如果同时用两种方式写生命周期钩子函数&#xff0c;组合式API里边的生命周期要比用配置项写的快一点。 注意&#xff1a;一般用一种方式即可。

概念解析 | MIMO雷达中正交波形设计的概述

注1:本文系“概念解析”系列之一,致力于简洁清晰地解释、辨析复杂而专业的概念。本次辨析的概念是:MIMO雷达中正交波形设计的概述 参考资料:Sun H, Brigui F, Lesturgie M. Analysis and comparison of MIMO radar waveforms[C]//2014 International Radar Conference. IEEE, …

自建类ChatGPT服务:本地化部署与远程访问教程

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

【python】绘图代码模板

【python】绘图代码模板 pandas.DataFrame.plot( )画图函数Seaborn绘图 -数据可视化必备主题样式导入数据集可视化统计关系散点图抖动图箱线图小提琴图Pointplot群图 可视化数据集的分布绘制单变量分布柱状图直方图 绘制双变量分布Hex图KDE 图可视化数据集中的成对关系 好看的图…

Python web实战之Django用户认证详解

关键词&#xff1a; Python Web 开发、Django、用户认证、实战案例 概要 今天来探讨一下 Django 的用户认证吧&#xff01;在这篇文章中&#xff0c;我将为大家带来一些有关 Django 用户认证的最佳实践。 1. Django 用户认证 在开发 Web 应用程序时&#xff0c;用户认证是一个…

【ARM64 常见汇编指令学习 13 -- ARM 汇编 ORG 伪指令学习】

文章目录 ARM ORG 指令介绍UEFI 中对 ORG 指令的使用 ARM ORG 指令介绍 在ARM汇编中&#xff0c;"org"是一个汇编器伪指令&#xff0c;用于设置下一条指令的装入地址。"org"后面跟着的是一个表达式&#xff0c;这个表达式的值就是下一条指令的装入地址。如…

记录--基于css3写出的流光登录(注释超详细!)

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 完整效果 对基本的表单样式进行设置 这里设置了基本的表单样式&#xff0c;外层用了div进行包裹&#xff0c;重点是运用了两个i元素在后期通过css样式勾画出一条线没在聚焦文本框的时候线会过度成一个…

【雕爷学编程】MicroPython动手做(33)——物联网之天气预报2

天气&#xff08;自然现象&#xff09; 是指某一个地区距离地表较近的大气层在短时间内的具体状态。而天气现象则是指发生在大气中的各种自然现象&#xff0c;即某瞬时内大气中各种气象要素&#xff08;如气温、气压、湿度、风、云、雾、雨、闪、雪、霜、雷、雹、霾等&#xff…