当前位置: 首页 > news >正文

MVVM前端设计模式的发展与应用


在MVC模式中,随着代码量越来越大,主要用来处理各种逻辑和数据转化的Controller首当其冲,变得非常庞大,MVC的简写变成了Massive-View-Controller(意为沉重的Controller)

我曾经接手老项目,springMVC + Hibernate技术栈,更能体现朴素的mvc,其中有些controller层代码8000+行,一个方法体1000+行。

在使用Hibernate\mybatisPlus\Spring data GPA这种完全的ORM框架、且不使用三层架构的项目中,controller过大这一缺点更加明显。因为了三层架构中service层分担业务逻辑。而由重量级ORM框架直接生成的service层只做与实体有关的、对实体数据部分的程序逻辑。

hibernate主要是要在controller通过前端http传来的参数动态拼接sql来处理逻辑,如果view的参数直接传到service处理,相当于直接和model接触了

  • MVC:

  • MVC+三层架构

所以有人想到把Controller的数据和逻辑处理部分从中抽离出来,用一个专门的对象去管理,这个对象就是ViewModel,。当人们去尝试这种方式时,发现Controller中的代码变得非常少,变得易于测试和维护,只需要Controller和ViewModel做数据绑定即可。

3.开发解耦(举两个例子):
a.一人负责逻辑实现、另一人负责UI实现;
b.敏捷开发时,会发经常发不是等后端做好了接口我们再去开发,不过在没有接口的情况下通常我们可以把Controller和View完成。

将整个前端页面分成View,Controller,Modal,视图上发生变化,通过Controller(控件)将响应传入到Model(数据源),由数据源改变View上面的数据。

通过数据来驱动视图,开发者只需要关心数据变化,DOM操作被封装了。

  • 但这是mvvm的特性吗?封装dom?这么一来只有浏览器前端可以用mvvm了?

可以看到MVVM分别指View,Model,View-Model,View通过View-Model的DOM Listeners将事件绑定到Model上,而Model则通过Data Bindings来管理View中的数据,View-Model从中起到一个连接桥的作用。

一、ViewModel将改变传给Controller,Controller定位到需要修改的View部分并改变这部分
二、ViewModel包揽了Controller的数据处理功能之後,由于Controller的存在,原本的一个大视图的控制与处理,被Controller切割成了很多小的ViewModel和Model的编写问题,每一个View、ViewModel和Model组成了一个单独的单元,问题规模变小,问题就更容易处理了
三、数据绑定是,将视图中的某个小组件(View)、小组件的处理函数(ViewModel)、小组件相关的数据(Model)互相绑定起来。原本大Controller的处理是收到一个事件後,Controller需要从整个Model的顶端一层层下来,找到对应的数据进行计算,计算完成後,又从document对象上,一层层找下来,找到对应的小组件进行修改。每一个数据查找和视图修改操作,都需要从全局空间找下来。进行数据绑定之後,所有的这些都在局部空间操作,不再经过全局空间,所以代码看起来就舒服很多

  • 分层的时候是不是不应只看到组件,而是从逻辑上把另一方的接口也算上?如数据层是数据库+缓存+实体,model的话还算上mapper操作和service逻辑

MVVM用ViewModel层代替了controller,

  • mvvm目前只有B端资料

VUE实现原理

三个核心点:

  1. 响应式:vue如何监听data的属性变化
  2. 模板解析:vue的模板是如何被解析的
  3. 渲染:vue模板是如何被渲染成HTML的

响应式:

对于MVVM来说,data一般是放在一个对象当中,就比如这样:

var obj = {name: 'zhangsan',age: 25}

当我们访问或修改obj的属性的时候,比如:

console.log(obj.name)  //访问obj.age = 22    //修改

但是这样的操作vue本身是没有办法感知到的,那么应该如何让vue知道我们进行了访问或是修改的操作呢?
那就要使用Object.defineProperty

var vm = {}var data = {name: 'zhangsan',age: 20}var key, valuefor (key in data) {(function (key) {Object.defineProperty(vm, key, {get: function () {console.log('get', data[key]) // 监听return data[key]},set: function (newVal) {console.log('set', newVal) // 监听data[key] = newVal}})})(key)}

通过Object.defineProperty将data里的每一个属性的访问与修改都变成了一个函数,在对象的getter和setter方法中我们即可监听到data的属性发生了改变。

模板解析

模板本质上是一串字符串,它看起来和html的格式很相像,实际上有很大的区别,因为模板本身还带有逻辑运算,比如v-if,v-for等等,但它最后还是要转换为html来显示。

<div id="app"><div><input v-model="title"><button v-on:click="add">submit</button></div><div><ul><li v-for="item in list">{{item}}</li></ul></div></div>

模板在vue中必须转换为JS代码,原因在于:在前端环境下,只有JS才是一个图灵完备语言,才能实现逻辑运算,以及渲染为html页面。

  • 图灵完备语言

这里就引出了vue中一个特别重要的函数——render

render函数中的核心就是with函数。

with函数将某个对象添加到作用域链的顶部,如果在 statement中有某个未使用命名空间的变量,跟作用域链中的某个属性同名,则这个变量将指向这个属性值。

举个例子:

var obj = {name: 'zhangsan',age: 20,getAddress: function () {alert('beijing')}}function fn1() {with(obj) {alert(age)alert(name)getAddress()}}fn1()

with将obj这个对象放在了自己函数的作用域链的顶部,当执行下列函数时,就会自动到obj这个对象去寻找同名的属性。

而在render函数中,with的用法是这样:

<div id="app"><div><input v-model="title"><button v-on:click="add">submit</button></div><div><ul><li v-for="item in list">{{item}}</li></ul></div></div>
// 对应的js文件var data = {title: '',list: []}// 初始化 Vue 实例var vm = new Vue({el: '#app',data: data,methods: {add: function () {this.list.push(this.title)this.title = ''}}})with(this){  // this 就是 vmreturn _c('div',{attrs:{"id":"app"}},[_c('div',[_c('input',{directives:[{name:"model",rawName:"v-model",value:(title),expression:"title"}],domProps:{"value":(title)},on:{"input":function($event){if($event.target.composing)return;title=$event.target.value}}}),_v(" "),_c('button',{on:{"click":add}},[_v("submit")])]),_v(" "),_c('div',[_c('ul',_l((list),function(item){return _c('li',[_v(_s(item))])}))])])}

在一开始,因为new操作符,所以this指向了vm,通过with我们将vm这个对象放在作用域链的顶部,因为在函数内部我们会多次调用vm内部的属性,所以使用with可以缩短变量长度,提供系统运行效率。

其中的_c函数表示的是创建一个新的html元素,其基本用法为:

_c(element,{attrs},[children...])

其中的element表示所要创建的html元素类型,attrs表示所要创建的元素的属性,children表示该html元素的子元素。

_v函数表示创建一个文本节点,_l函数表示创建一个数组。

最终render函数返回的是一个虚拟DOM。

渲染为html

模板渲染为html分为两种情况,第一种是初次渲染的时候,第二种是渲染之后数据发生改变的时候,它们都需要调用updateComponent,其形式如下:

vm._update(vnode){const prevVnode = vm._vnodevm._vnode = vnodeif (!prevVnode){vm.$el = vm.__patch__(vm.$el,vnode)} else {vm.$el = vm.__patch__(prevVnode,vnode)}
}function updateComponent(){vm._update(vm._render())
}

首先读取当前的虚拟DOM——vm._vnode,判断其是否为空,若为空,则为初次渲染,将虚拟DOM全部渲染到所对应的容器当中(vm.$el),若不为空,则是数据发生了修改,通过响应式我们可以监听到这一情况,使用diff算法完成新旧对比并修改。

拓展

更新节点

Vue 节点中更新节点内容,可以使用两种方式:

  • 在组件内部监听节点事件,在外部触发事件。
  • 使用 Pinan,Vuex 等状态管理器

相关文章:

MVVM前端设计模式的发展与应用

在MVC模式中&#xff0c;随着代码量越来越大&#xff0c;主要用来处理各种逻辑和数据转化的Controller首当其冲&#xff0c;变得非常庞大&#xff0c;MVC的简写变成了Massive-View-Controller&#xff08;意为沉重的Controller&#xff09; 我曾经接手老项目&#xff0c;sprin…...

redis:二、缓存击穿的定义、解决方案(互斥锁、逻辑过期)的优缺点和适用场景、面试回答模板和缓存雪崩

缓存击穿的定义 缓存击穿是一种现象&#xff0c;具体就是某一个数据过期时&#xff0c;恰好有大量的并发请求过来&#xff0c;这些并发的请求可能会瞬间把DB压垮。典型场景就是双十一等抢购活动中&#xff0c;首页广告页面的数据过期&#xff0c;此时刚好大量用户进行请求&…...

php的Url 安全的base64编码解码类

/*** Url安全的Base64编码方法* author JerryLi* version 20231217*/ final class UrlSafeB64Fun{/*** 编码* param string $sData 原始字符串* return string*/static public function encode(string $sData): string{$aTmp base64_encode($sData);return strtr($aTmp, [>…...

安全CDN有什么作用,安全CDN工作原理是什么?

一、CDN的应用场景 CDN技术可以应用于各种类型的网站和应用程序&#xff0c;特别是对于以下几种场景&#xff0c;CDN的作用尤为明显&#xff1a; 1. 高流量网站&#xff1a;对于流量较大的网站&#xff0c;CDN可以将网站的内容分发到全球各地的节点上&#xff0c;从而分担服务…...

Mysql高可用|索引|事务 | 调优

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 文章目录 前言sql语句的执行顺序关键词连接名字解释sql语句 面试坑点存储引擎MYSQL存储引擎 SQL优化索引索引失效索引的数据结构面试坑点 锁事务四大特性事务的隔离级别MVCC 读写分离面试坑…...

电机驱动开发

最近在搞电机驱动程序&#xff0c;感觉很简单&#xff0c;实际操作却发现里面还有很多猫腻&#xff08;细节&#xff09;。 电机在嵌入式设备中非常常见&#xff0c;例如云台的转动&#xff0c;都是靠电机来驱动的。 电机常见分步进电机、直流电机&#xff0c;相对来说步进电机…...

基于PaddleNLP的深度学习对文本自动添加标点符号(一)

前言 目前以深度学习对文本自动添加标点符号研究很少&#xff0c;已知的开源项目并不多&#xff0c;详细的介绍就更少了&#xff0c;但对文本自动添加标点符号又在古文识别语音识别上有重大应用。 基于此&#xff0c;本文开始讲解基于PaddleNLP的深度学习对文本自动添加标点符号…...

“Java已死、前端已凉”?尊嘟假嘟?

一、为什么会出现“Java已死、前端已凉”的言论 “Java已死、前端已凉”的言论出现&#xff0c;主要是由于以下几个原因&#xff1a; 技术更新迅速&#xff1a;随着互联网技术的发展&#xff0c;新的编程语言和技术不断涌现。Java和前端技术作为广泛应用的技术&#xff0c;面临…...

双向无线功率传输系统MATLAB仿真

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 模型简介&#xff1a; 初级侧转换器通过双向 AC/DC 转换器从电网获取电力&#xff0c;并由直流线电压 Vin 供电&#xff0c;而拾波侧被视为连接到 EV&#xff0c;并由连接到任一存储的单独直流源 Vout 表示或…...

火山引擎DataLeap:助你实现从数据研发1.0到数据研发3.0的跨越

更多技术交流、求职机会&#xff0c;欢迎关注字节跳动数据平台微信公众号&#xff0c;回复【1】进入官方交流群 近日&#xff0c;火山引擎开发者社区 Meetup 第 12 期暨超话数据专场在深圳举办&#xff0c;本次活动主题为“数智化转型背景下的火山引擎大数据技术揭秘 ”&#x…...

DevOps 和人工智能 – 天作之合

如今&#xff0c;人工智能和机器学习无处不在&#xff0c;所以它们开始在 DevOps 领域崭露头角也毫不令人意外。人工智能和机器学习正在通过自动化任务改变 DevOps&#xff0c;并使各企业的软件开发生命周期更高效、更深刻和更安全。我们在 DevOps 趋势中简要讨论过这一问题&am…...

基于主动安全的AIGC数据安全建设

面对AIGC带来的数据安全新问题&#xff0c;是不是就应该一刀切禁止AIGC的研究利用呢&#xff1f;答案是否定的。要发展AIGC&#xff0c;也要主动积极地对AIGC的数据安全进行建设。让AIGC更加安全、可靠的为用户服务。为达到此目的&#xff0c;应该从三个方面来开展AIGC的数据安…...

Java 程序的命令行解释器

前几天我写了一个简单的词法分析器项目&#xff1a;https://github.com/MarchLiu/oliva/tree/main/lora-data-generator 。 通过词法分析快速生成 lora 训练集。在这个过程中&#xff0c;我需要通过命令行参数给这个 java 程序传递一些参数。 这个工作让我想起了一些不好的回忆…...

从事开发近20年,经历过各种技术的转变和进步

1、jsp、javabean、servlet、jdbc。 2、Struts1、hibernate、spring。 3、webwork、ibatis、spring 4、Struts2、mybatis、spring 5、spring mvc &#xff0c;spring全家桶 6、dubbo&#xff0c;disconf 微服务&#xff0c;soa 7、springboot 全家桶 8、docker 9、dock…...

unet v2学习笔记

unet v2介绍&#xff1a; UNet v2开源&#xff01;比UNet显存占用更少、参数更少&#xff0c;猛涨20个mIoU 代码&#xff1a;https://github.com/yaoppeng/U-Net_v2 模型96m。 实际测试&#xff0c;1060显卡&#xff0c;256*256&#xff0c;需要13ms。 速度慢于rvm人脸分割…...

MQ入门—centos 7安装RabbitMQ 安装

三&#xff1a;RabbitMQ 安装 1.环境准备 Linux 的 CentOS 7.x 版本。Xftp 传输安装包到 Linux。Xshell 连接 Linux&#xff0c;进行解压安装。 RabbitMQ安装包 链接&#xff1a;https://pan.baidu.com/s/1ZYVI4YZlvMrj458jakla9A 提取码&#xff1a;dyto xshell安装包 链接&…...

虾皮Shopee商品详情API:电商实时数据获取的关键

随着互联网的普及和电子商务的快速发展&#xff0c;电商行业已经成为全球范围内最具影响力和前景的产业之一。在电商行业中&#xff0c;商品详情API接口是实现快速、准确获取商品信息的关键技术之一。本文将介绍获得虾皮Shopee根据ID取商品详情 API在电商行业里的重要性&#x…...

VUE中的8种常规通信方式

文章目录 1.props传递数据(父向子)2.$emit触发自定义事件&#xff08;子向父&#xff09;3.ref&#xff08;父子&#xff09;4.EventBus&#xff08;兄弟组件&#xff09;5.parent或root&#xff08;兄弟组件&#xff0c;有共同祖辈&#xff09;6.attrs和listeners&#xff08;…...

overleaf 加载pdf格式的矢量图时,visio 图片保存为pdf格式,如何确保pdf页面大小和图片一致

Overleaf支持多种矢量图形格式&#xff0c;其中一些常见的包括&#xff1a; PDF&#xff08;Portable Document Format&#xff09;&#xff1a; PDF是一种常见的矢量图形格式&#xff0c;Overleaf可以直接加载和显示PDF文件。许多绘图工具和LaTeX生成的图形都可以导出为PDF格式…...

西南科技大学数字电子技术实验四(基本触发器逻辑功能测试及FPGA的实现)预习报告

一、计算/设计过程 说明:本实验是验证性实验,计算预测验证结果。是设计性实验一定要从系统指标计算出元件参数过程,越详细越好。用公式输入法完成相关公式内容,不得贴手写图片。(注意:从抽象公式直接得出结果,不得分,页数可根据内容调整) (1)D触发器 特征方程: Q…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

逻辑回归:给不确定性划界的分类大师

想象你是一名医生。面对患者的检查报告&#xff08;肿瘤大小、血液指标&#xff09;&#xff0c;你需要做出一个**决定性判断**&#xff1a;恶性还是良性&#xff1f;这种“非黑即白”的抉择&#xff0c;正是**逻辑回归&#xff08;Logistic Regression&#xff09;** 的战场&a…...

MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例

一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

MongoDB学习和应用(高效的非关系型数据库)

一丶 MongoDB简介 对于社交类软件的功能&#xff0c;我们需要对它的功能特点进行分析&#xff1a; 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具&#xff1a; mysql&#xff1a;关系型数据库&am…...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序

一、开发准备 ​​环境搭建​​&#xff1a; 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 ​​项目创建​​&#xff1a; File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

微信小程序云开发平台MySQL的连接方式

注&#xff1a;微信小程序云开发平台指的是腾讯云开发 先给结论&#xff1a;微信小程序云开发平台的MySQL&#xff0c;无法通过获取数据库连接信息的方式进行连接&#xff0c;连接只能通过云开发的SDK连接&#xff0c;具体要参考官方文档&#xff1a; 为什么&#xff1f; 因为…...

Python ROS2【机器人中间件框架】 简介

销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

LLMs 系列实操科普(1)

写在前面&#xff1a; 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容&#xff0c;原视频时长 ~130 分钟&#xff0c;以实操演示主流的一些 LLMs 的使用&#xff0c;由于涉及到实操&#xff0c;实际上并不适合以文字整理&#xff0c;但还是决定尽量整理一份笔…...

JS手写代码篇----使用Promise封装AJAX请求

15、使用Promise封装AJAX请求 promise就有reject和resolve了&#xff0c;就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...