从Script到Code Blocks、Code Behind到MVC、MVP、MVVM
从Script到Code Blocks、Code Behind到MVC、MVP、MVVM引言前端架构的进化之路想象一下你正在搭建一个乐高城堡。最初你只是把积木随意堆在一起Script方式后来你把城堡分成底座、塔楼、城门等模块Code Blocks再后来你开始画设计图让不同人负责不同部分Code Behind。最后你甚至发明了一套积木标准让所有城堡的组件可以互换MVC/MVP/MVVM。这就是前端架构的进化史。## 第一阶段Script——混沌初开的时代在Web早期所有代码都混在HTML文件中。JavaScript像散落的乐高积木哪里需要就放哪里。html!-- 1995年的典型页面 --htmlbody h1我的第一个网页/h1 p iddemo点击按钮改变文本/p button onclickchangeText()点击我/button script // 直接写在HTML里的JavaScript function changeText() { document.getElementById(demo).innerHTML 文本被改变了; } /script/body/html这种方式的问题显而易见-难以维护代码混在一起就像把所有衣服塞进一个抽屉-重复代码同样功能要在不同页面重复写-全局污染所有变量都在全局作用域## 第二阶段Code Blocks——模块化的萌芽开发者开始把JavaScript拆分成独立文件就像把不同物品放进不同抽屉。javascript// utils.js - 工具函数模块function formatDate(date) { // 格式化日期 let year date.getFullYear(); let month date.getMonth() 1; let day date.getDate(); return ${year}-${month.toString().padStart(2, 0)}-${day.toString().padStart(2, 0)};}// 导出函数供其他模块使用window.formatDate formatDate;// calculator.js - 计算模块function add(a, b) { return a b;}function multiply(a, b) { return a * b;}window.add add;window.multiply multiply;html!-- 使用模块的页面 --htmlhead script srcutils.js/script script srccalculator.js/script/headbody script // 调用模块中的函数 let today new Date(); console.log(formatDate(today)); // 输出格式化的日期 console.log(add(3, 5)); // 输出8 /script/body/html这种方式的进步在于-逻辑分离不同功能放在不同文件-复用性提高可以在多个页面使用相同模块-命名空间通过对象减少全局污染## 第三阶段Code Behind——代码后置的革新Code Behind模式把业务逻辑从视图HTML中彻底分离。这种模式在ASP.NET中非常流行。python# views.py - 视图逻辑层from flask import Flask, render_template, requestapp Flask(__name__)# 业务逻辑计算商品总价def calculate_total_price(items): total 0 for item in items: total item[price] * item[quantity] return totalapp.route(/checkout, methods[POST])def checkout(): # 获取用户购物车数据 cart_items request.json.get(items, []) # 调用业务逻辑 total calculate_total_price(cart_items) # 返回计算结果 return render_template(checkout.html, total_pricetotal)html!-- checkout.html - 纯视图模板 --!DOCTYPE htmlhtmlbody h1购物车结算/h1 !-- 视图只负责展示不包含业务逻辑 -- p总金额{{ total_price }} 元/p !-- 所有交互逻辑交给Code Behind处理 -- button onclicksubmitOrder()提交订单/button script function submitOrder() { // 通过AJAX请求后端 fetch(/checkout, { method: POST, body: JSON.stringify({items: cartData}) }); } /script/body/htmlCode Behind的核心思想视图View只负责展示代码Code负责逻辑。## 第四阶段MVC——Model-View-ControllerMVC把应用分为三个核心组件-Model管理数据和业务逻辑-View负责数据显示-Controller处理用户输入协调Model和Viewpython# MVC示例简单的待办事项应用# Model - 数据层class TodoModel: def __init__(self): self.todos [] def add_todo(self, task): self.todos.append({task: task, completed: False}) def get_all(self): return self.todos# View - 视图层class TodoView: def display_todos(self, todos): print(\n 待办事项列表 ) for i, todo in enumerate(todos, 1): status ✓ if todo[completed] else print(f{i}. [{status}] {todo[task]}) def get_user_input(self): return input(\n输入新任务或输入q退出)# Controller - 控制层class TodoController: def __init__(self): self.model TodoModel() self.view TodoView() def run(self): while True: # 显示当前数据 self.view.display_todos(self.model.get_all()) # 获取用户输入 task self.view.get_user_input() if task.lower() q: break # 处理数据 self.model.add_todo(task)# 启动应用if __name__ __main__: app TodoController() app.run()MVC的优势-职责明确每个组件只做一件事-可测试性Model和Controller可以独立测试-可扩展性可以轻松替换View或Model## 第五阶段MVP——Model-View-PresenterMVP是MVC的变体主要区别在于View和Presenter的关系更紧密。javascript// MVP模式示例用户登录功能// Model - 数据层class LoginModel { constructor() { this.username ; this.password ; } validateCredentials(username, password) { // 模拟验证逻辑 if (username admin password 123456) { return { success: true, message: 登录成功 }; } return { success: false, message: 用户名或密码错误 }; }}// View - 视图层抽象接口class LoginView { constructor() { this.usernameInput document.getElementById(username); this.passwordInput document.getElementById(password); this.loginButton document.getElementById(loginBtn); this.messageElement document.getElementById(message); } getUsername() { return this.usernameInput.value; } getPassword() { return this.passwordInput.value; } showMessage(message, isSuccess) { this.messageElement.textContent message; this.messageElement.style.color isSuccess ? green : red; } clearInputs() { this.usernameInput.value ; this.passwordInput.value ; }}// Presenter - 控制器class LoginPresenter { constructor(view, model) { this.view view; this.model model; // 绑定事件 this.view.loginButton.addEventListener(click, () this.handleLogin()); } handleLogin() { const username this.view.getUsername(); const password this.view.getPassword(); // 调用Model进行验证 const result this.model.validateCredentials(username, password); // 更新View this.view.showMessage(result.message, result.success); if (result.success) { this.view.clearInputs(); } }}// 初始化const model new LoginModel();const view new LoginView();const presenter new LoginPresenter(view, model);MVP的特点-View完全被动所有逻辑由Presenter处理-测试友好可以Mock View进行测试-适合复杂UI特别适合需要精细控制视图的场景## 第六阶段MVVM——Model-View-ViewModelMVVM是当前最流行的模式通过数据绑定实现自动同步。python# 使用Tkinter实现MVVM示例import tkinter as tkfrom tkinter import ttk# Model - 数据层class UserModel: def __init__(self): self._name self._age 0 self._observers [] def add_observer(self, observer): self._observers.append(observer) def notify_observers(self, property_name, value): for observer in self._observers: observer.update(property_name, value) property def name(self): return self._name name.setter def name(self, value): self._name value self.notify_observers(name, value) property def age(self): return self._age age.setter def age(self, value): self._age value self.notify_observers(age, value)# ViewModel - 视图模型class UserViewModel: def __init__(self, model): self.model model self.name tk.StringVar() self.age tk.StringVar() self.greeting tk.StringVar() # 绑定模型变化 model.add_observer(self) def update(self, property_name, value): if property_name name: self.name.set(value) elif property_name age: self.age.set(str(value)) self._update_greeting() def on_name_changed(self, *args): self.model.name self.name.get() def on_age_changed(self, *args): try: age int(self.age.get()) self.model.age age except ValueError: pass def _update_greeting(self): self.greeting.set(f你好{self.model.name}你今年{self.model.age}岁。)# View - 视图层class UserView: def __init__(self, root, view_model): self.view_model view_model # 创建UI组件 frame ttk.Frame(root, padding10) frame.grid(row0, column0, sticky(tk.W, tk.E, tk.N, tk.S)) # 姓名输入 ttk.Label(frame, text姓名:).grid(row0, column0, stickytk.W) name_entry ttk.Entry(frame, textvariableview_model.name) name_entry.grid(row0, column1) view_model.name.trace(w, view_model.on_name_changed) # 年龄输入 ttk.Label(frame, text年龄:).grid(row1, column0, stickytk.W) age_entry ttk.Entry(frame, textvariableview_model.age) age_entry.grid(row1, column1) view_model.age.trace(w, view_model.on_age_changed) # 显示问候语 ttk.Label(frame, textvariableview_model.greeting).grid(row2, column0, columnspan2)# 运行应用if __name__ __main__: root tk.Tk() root.title(MVVM示例) model UserModel() view_model UserViewModel(model) view UserView(root, view_model) root.mainloop()MVVM的核心优势-双向绑定View和ViewModel自动同步-测试方便ViewModel可以独立于View测试-组件化适合大型应用的模块化开发## 总结架构进化给我们的启示从Script到MVVM这不仅是技术的演进更是思维方式的变革1.从混乱到有序代码组织方式越来越清晰2.从紧耦合到松耦合组件间依赖越来越弱3.从过程式到声明式关注点从如何做转向做什么4.从手动到自动数据同步从手动操作变为自动绑定选择哪种模式取决于项目需求-小项目Script或简单模块化就够了-中型项目Code Behind或MVC是不错的选择-复杂应用MVVM或MVP更适合记住没有银弹每种模式都有其适用场景。关键是要理解背后的设计思想而不是盲目追求最新的架构。架构的进化永远不会停止但分离关注点和降低耦合这两个原则将永远适用。

相关新闻

BP神经网络在车牌识别系统中的优化实践

BP神经网络在车牌识别系统中的优化实践

1. 项目背景与核心价值 车牌识别作为智能交通系统的核心技术之一,在停车场管理、违章抓拍、高速公路收费等场景中发挥着关键作用。传统基于图像处理的车牌识别方案在复杂光照、污损车牌等场景下识别率骤降,而BP神经网络凭借其强大的非线性映射能力&#…

2026/9/22 23:23:07 阅读更多 →
如何用REFramework实现游戏模组的零代码开发:终极可视化编程指南

如何用REFramework实现游戏模组的零代码开发:终极可视化编程指南

如何用REFramework实现游戏模组的零代码开发:终极可视化编程指南 【免费下载链接】REFramework Mod loader, scripting platform, and VR support for all RE Engine games 项目地址: https://gitcode.com/GitHub_Trending/re/REFramework 在游戏模组开发领域…

2026/9/24 5:07:56 阅读更多 →
如何在Windows上免费实现本地离线LaTeX公式识别:MixTeX完整指南

如何在Windows上免费实现本地离线LaTeX公式识别:MixTeX完整指南

如何在Windows上免费实现本地离线LaTeX公式识别:MixTeX完整指南 【免费下载链接】MixTeX-Latex-OCR MixTeX multimodal LaTeX, ZhEn, and, Table OCR. It performs efficient CPU-based inference in a local offline on Windows. 项目地址: https://gitcode.com/…

2026/9/24 7:47:06 阅读更多 →

最新新闻

WHU-RS19遥感图像分类实战:从数据集加载到模型微调的避坑指南

WHU-RS19遥感图像分类实战:从数据集加载到模型微调的避坑指南

简介:面向遥感图像分类与深度学习入门人群的WHU-RS19数据集,提供19种土地利用类型的已标注卫星图像,涵盖机场、海滩、桥梁、商业区、沙漠、农田等常用类别,可直接用于图像分类模型训练与精度评估,也可作为基准数据集检…

2026/9/24 21:15:19 阅读更多 →
Python+OpenCV+PyQt答题卡识别系统:从透视校正到桌面软件封装

Python+OpenCV+PyQt答题卡识别系统:从透视校正到桌面软件封装

简介:一套基于Python、OpenCV与PyQt的答题卡识别完整项目,适合毕业设计、期末大作业或课程设计阶段需要快速搭建可演示软件的学生参考。项目内包含全部源代码、训练与测试数据集、答辩PPT以及毕业实习报告,代码注释完整,部署流程简…

2026/9/24 21:15:19 阅读更多 →
ARP协议与ARP欺骗:从原理到防御实战指南

ARP协议与ARP欺骗:从原理到防御实战指南

出门左转再右转,你的电脑只要连了局域网,基本都绕不开ARP协议这四个字。它安静得像是网络里的背景音,平时没人注意,直到某天有人用ARP欺骗把你的网关地址“抢走”,你才会发现:为什么我Wi-Fi信号满格&#x…

2026/9/24 21:15:19 阅读更多 →
避坑指南:选购西安停车场系统需关注的售后维度

避坑指南:选购西安停车场系统需关注的售后维度

西安停车场系统选型:如何评估本地化服务与长期持有成本在西安地区进行停车管理方案的规划或改造时,决策者常常面临硬件功能趋同、后期维护成本难以预估的挑战。除了考察车牌识别率、通行效率等基础技术指标外,售后响应速度与费用结构的透明度…

2026/9/24 21:15:19 阅读更多 →
OpenLayers v3.18.1 补丁版本解析:圆形几何绘制起点修复与 HiDPI 矢量瓦片旋转修正

OpenLayers v3.18.1 补丁版本解析:圆形几何绘制起点修复与 HiDPI 矢量瓦片旋转修正

OpenLayers v3.18.1 补丁版本解析:圆形几何绘制起点修复与 HiDPI 矢量瓦片旋转修正 【免费下载链接】openlayers OpenLayers 项目地址: https://gitcode.com/gh_mirrors/op/openlayers v3.18.1 是 OpenLayers 针对 v3.18.0 引入的两处回归(regres…

2026/9/24 21:15:19 阅读更多 →
世界时UT与UTC的区别:从地球自转、闰秒到卫星导航的完整解析

世界时UT与UTC的区别:从地球自转、闰秒到卫星导航的完整解析

你打开手机看一眼时间,屏幕上显示的是“协调世界时(UTC)”,这几乎已经是现代社会的默认答案。但如果把时间退回一百年,天文台发布的整点信号叫“世界时(Universal Time, UT)”,它来自…

2026/9/24 21:14:18 阅读更多 →

日新闻

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为…

2026/9/24 0:00:19 阅读更多 →
单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

简介:一份基于单细胞RNA测序数据的细胞类型注释算法研究Python毕业设计源码,针对计算机相关专业正在做毕设或需要项目实战的学习者,可用于课程设计与期末大作业。项目代码完整、经导师指导评审通过,可直接运行,覆盖数据…

2026/9/24 0:00:19 阅读更多 →
C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

第一次在项目里被反射卡住,是在一个老旧的WinForms模块里:几十个类依赖PropertyChanged通知,运行时反射读属性、发通知,每次启动慢半拍不说,一上.NET Native/AOT裁剪模式几乎全面崩盘。后来我把这段逻辑全部改成C#源生…

2026/9/24 0:00:19 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →