核心要点
- MVC(模型-视图-控制器)设计模式是一种强大的方法,用于组织JavaScript代码,通过清晰地分离关注点来增强可维护性和可读性。
- 与可能强加特定实现的框架不同,MVC模式提供了一个灵活的结构,允许开发人员更轻松地适应和扩展其应用程序。
- 使用企鹅的演示说明了如何在原生JavaScript中应用MVC来系统地处理用户交互、数据管理和UI更新。
- MVC模式的持久性和适应性使其成为希望以严谨且可扩展的方式磨练编程技能的开发人员的宝贵财富。
- MVC模式的关键组件包括:用于管理数据的模型、用于处理显示的视图以及用于协调用户输入和应用程序输出的控制器,每个组件都有不同的职责,确保代码模块化和组织良好。
- 本文强调了干净代码的重要性以及避免与框架的依赖锁定,提倡将MVC作为一种策略,以保持代码的可管理性和可扩展性,因为应用程序会随着发展而增长和演变。
设计模式通常被整合到流行的框架中。例如,模型-视图-控制器(MVC)设计模式无处不在。在JavaScript中,很难将框架与设计模式分离。通常,特定的框架会带有其自己对这种设计模式的解释。框架带有观点,每个框架都强迫你以某种方式思考。
现代框架决定了MVC模式的具体实现方式。当所有解释都不同时,这会令人困惑,从而增加噪音和混乱。当任何代码库采用多个框架时,就会产生令人沮丧的混乱。我心中的问题是,有没有更好的方法?
MVC模式适用于客户端框架,但现代框架会发生变化。当今的现代化会随着时间的推移而消亡。在这种情况下,我想探索替代方案,看看一点纪律能带我们到哪里。
阅读现代JavaScript,随时了解JavaScript不断变化的世界! 阅读本书 MVC模式本身可以追溯到几十年前。这使其成为一个值得您投入编程技能的设计模式。MVC模式是一个可以独立存在的模式。问题是,这能带我们走多远?
等等,这是另一个框架吗?
首先,我想消除这个常见的误解:设计模式不是框架。设计模式是一种解决代码问题的严谨方法。这需要一定的技能水平,并将责任放在程序员身上。设计模式分离关注点并促进编写干净的代码。
框架则不同,因为它不必遵循任何设计模式。区分框架和模式的一种方法是寻找好莱坞原则。好莱坞原则就是:“别打电话给我们,我们会打电话给你。”任何时候都有一个依赖项决定你何时使用它,它就是一个框架。框架很像好莱坞,你不能决定做什么或如何做。事实上,开发人员就像演员一样,在被要求行动时遵循剧本。
避免客户端框架有很多很好的理由:
- 框架增加了解决方案的复杂性和风险
- 您会遇到依赖锁定,这会导致代码难以维护
- 随着新的流行框架的出现,很难重写现有的遗留代码
MVC模式
MVC设计模式起源于20世纪70年代和80年代的施乐Smalltalk研究项目。这是一个经受了时间考验的用于前端图形用户界面的模式。该模式来自桌面应用程序,但已被证明对Web应用程序也很有效。
其核心是MVC设计模式是关于关注点的清晰分离。其目的是使解决方案清晰易懂。任何想要进行特定更改的程序员都可以轻松找到正确的位置。
企鹅演示
企鹅!可爱又毛茸茸的,是地球上最可爱的生物之一。事实上,它们非常可爱,有17种不同的企鹅,并非所有企鹅都生活在南极洲的环境中。
是时候做一个企鹅的演示了!我将展示一个页面上显示几种物种的牌组。为此,我想使用MVC设计模式和一点纪律。我将使用极限编程方法来使用单元测试和简单的方法来解决手头的问题。最后,您应该能够翻阅几只企鹅,每只企鹅都有自己的数据和个人资料图片。
在本例结束时,您应该已经学习了足够多的知识,可以在纯JavaScript中使用MVC设计模式。该模式本身非常易于测试,因此可以预期良好的单元测试。
出于跨浏览器兼容性的原因,我将在此演示中坚持使用ES5。使用经过验证的语言特性与这种永久的设计模式相结合是有意义的。
你准备好了吗?让我们拭目以待。
骨架
演示将包含三个主要部分:控制器、视图和模型。每个部分都有其自身的关注点和需要解决的问题。
下面是其外观的可视化效果:
PenguinController处理事件,是视图和模型之间的中介。它会计算出用户执行操作(例如,单击按钮或按下一个键)时会发生什么。客户端特定的逻辑可以放在控制器中。在一个有很多事情要做的更大的系统中,您可以将其分解成模块。控制器是事件的入口点,也是视图和数据之间唯一的调解者。
PenguinView关心DOM。DOM是您用来进行HTML操作的浏览器API。在MVC中,除了视图之外,没有其他部分关心更改DOM。视图可以附加用户事件,但将事件处理问题留给控制器。视图的主要指令是更改用户在屏幕上看到的状态。对于此演示,视图将使用纯JavaScript进行DOM操作。
PenguinModel关心数据。在客户端JavaScript中,这意味着Ajax。MVC模式的一个优点是您现在有一个用于服务器端Ajax调用的单一位置。这使得不熟悉该解决方案的其他程序员更容易上手。此设计模式中的模型只关心来自服务器的JSON或对象。
一种反模式是违反这种内在的关注点分离。例如,模型一定不能关心HTML。视图一定不能关心Ajax。控制器必须充当调解者,而无需担心实现细节。
我发现使用这种模式时,开发人员一开始怀有良好的意图,但会泄露关注点。将所有内容都变成Web组件并最终变成一团糟是很诱人的。重点放在功能和面向用户的关注点上。但是,功能关注点与功能关注点不同。
在编程中,我喜欢的是对功能关注点进行清晰的分离。每个单独的编程问题都会得到一种一致的解决方法。当您阅读代码时,这使其更易于理解。其目的是编写易于理解的代码,以便其他人也能做出积极的贡献。
如果没有一个您可以看到和触摸的真实示例,那它就不是一个很好的演示。因此,不用多说,下面是一个CodePen,展示了企鹅的演示:
查看SitePoint (@SitePoint)在CodePen上的笔A Demo of Penguins。
说得够多了,是时候写代码了。
控制器
视图和模型是控制器使用的两个组件。控制器在其构造函数中包含完成工作所需的所有组件:
<code>var PenguinController = function PenguinController(penguinView, penguinModel) { this.penguinView = penguinView; this.penguinModel = penguinModel; }; </code>
构造函数使用控制反转并以此方式注入模块。此模式使您可以注入满足高级契约的任何组件。将其视为一种抽象代码与实现细节的好方法。此模式使您能够使用纯JavaScript编写干净的代码。
然后,用户事件会以这种方式连接和处理:
<code>PenguinController.prototype.initialize = function initialize() { this.penguinView.onClickGetPenguin = this.onClickGetPenguin.bind(this); }; PenguinController.prototype.onClickGetPenguin = function onClickGetPenguin(e) { var target = e.currentTarget; var index = parseInt(target.dataset.penguinIndex, 10); this.penguinModel.getPenguin(index, this.showPenguin.bind(this)); }; </code>
请注意,此事件使用当前目标来获取存储在DOM中的状态。在这种情况下,DOM会告诉您有关其当前状态的所有信息。DOM的当前状态是用户在浏览器上看到的内容。您可以将状态数据存储在DOM本身中,只要控制器不更改状态即可。
触发事件后,控制器会获取数据并说明接下来会发生什么。this.showPenguin()回调很有趣:
<code>PenguinController.prototype.showPenguin = function showPenguin(penguinModelData) { var penguinViewModel = { name: penguinModelData.name, imageUrl: penguinModelData.imageUrl, size: penguinModelData.size, favoriteFood: penguinModelData.favoriteFood }; penguinViewModel.previousIndex = penguinModelData.index - 1; penguinViewModel.nextIndex = penguinModelData.index + 1; if (penguinModelData.index === 0) { penguinViewModel.previousIndex = penguinModelData.count - 1; } if (penguinModelData.index === penguinModelData.count - 1) { penguinViewModel.nextIndex = 0; } this.penguinView.render(penguinViewModel); }; </code>
控制器计算每个企鹅的索引并告诉视图呈现它。它从模型中获取数据并将其转换为视图理解和关心的对象。
以下是显示企鹅时快乐路径的单元测试:
<code>var PenguinController = function PenguinController(penguinView, penguinModel) { this.penguinView = penguinView; this.penguinModel = penguinModel; }; </code>
PenguinViewMock与真实实现具有相同的契约。这使得编写单元测试和进行断言成为可能。断言来自Node断言,也存在于Chai断言中。这使您可以编写可以在Node和浏览器上运行的测试。
请注意,控制器不关心实现细节。它使用视图提供的契约,例如this.render()。这就是编写干净代码所需的纪律。控制器可以相信每个组件都能做到它所说的那样。这增加了透明度,使代码更易于阅读。
视图
视图只关心DOM元素和连接事件,例如:
<code>PenguinController.prototype.initialize = function initialize() { this.penguinView.onClickGetPenguin = this.onClickGetPenguin.bind(this); }; PenguinController.prototype.onClickGetPenguin = function onClickGetPenguin(e) { var target = e.currentTarget; var index = parseInt(target.dataset.penguinIndex, 10); this.penguinModel.getPenguin(index, this.showPenguin.bind(this)); }; </code>
当它更改用户看到的状态时,实现如下所示:
<code>PenguinController.prototype.showPenguin = function showPenguin(penguinModelData) { var penguinViewModel = { name: penguinModelData.name, imageUrl: penguinModelData.imageUrl, size: penguinModelData.size, favoriteFood: penguinModelData.favoriteFood }; penguinViewModel.previousIndex = penguinModelData.index - 1; penguinViewModel.nextIndex = penguinModelData.index + 1; if (penguinModelData.index === 0) { penguinViewModel.previousIndex = penguinModelData.count - 1; } if (penguinModelData.index === penguinModelData.count - 1) { penguinViewModel.nextIndex = 0; } this.penguinView.render(penguinViewModel); }; </code>
请注意,其主要关注点是将视图模型数据转换为HTML并更改状态。第二个是连接点击事件并让控制器充当入口点。状态更改后,事件处理程序会附加到DOM。此技术一次性处理事件管理。
为了测试这一点,我们可以验证元素是否已更新并更改了状态:
<code>var PenguinViewMock = function PenguinViewMock() { this.calledRenderWith = null; }; PenguinViewMock.prototype.render = function render(penguinViewModel) { this.calledRenderWith = penguinViewModel; }; // Arrange var penguinViewMock = new PenguinViewMock(); var controller = new PenguinController(penguinViewMock, null); var penguinModelData = { name: 'Chinstrap', imageUrl: 'http://chinstrapl.jpg', size: '5.0kg (m), 4.8kg (f)', favoriteFood: 'krill', index: 2, count: 5 }; // Act controller.showPenguin(penguinModelData); // Assert assert.strictEqual(penguinViewMock.calledRenderWith.name, 'Chinstrap'); assert.strictEqual(penguinViewMock.calledRenderWith.imageUrl, 'http://chinstrapl.jpg'); assert.strictEqual(penguinViewMock.calledRenderWith.size, '5.0kg (m), 4.8kg (f)'); assert.strictEqual(penguinViewMock.calledRenderWith.favoriteFood, 'krill'); assert.strictEqual(penguinViewMock.calledRenderWith.previousIndex, 1); assert.strictEqual(penguinViewMock.calledRenderWith.nextIndex, 3); </code>
这解决了所有主要问题,更改状态和连接事件。但是,数据从哪里来?
模型
在MVC中,所有模型关心的都是Ajax。例如:
<code>var PenguinView = function PenguinView(element) { this.element = element; this.onClickGetPenguin = null; }; </code>
请注意,模块XMLHttpRequest被注入到构造函数中。这是一种让其他程序员知道此模型需要哪些组件的方法。如果模型需要的不仅仅是简单的Ajax,您可以使用更多模块来表示这一点。此外,使用单元测试,我可以注入与原始模块具有完全相同契约的模拟。
是时候根据索引获取企鹅了:
<code>PenguinView.prototype.render = function render(viewModel) { this.element.innerHTML = '<h3 id="viewModel-name">' + viewModel.name + '</h3>' + '<img alt="' + viewModel.name + '" src="'%20+%20viewModel.imageUrl%20+%0A%20%20%20%20%20%20'">' + '<p><b>Size:</b> ' + viewModel.size + '</p>' + '<p><b>Favorite food:</b> ' + viewModel.favoriteFood + '</p>' + '<a href="https://www.php.cn/link/f0b875eb6cff6fd5f491e6b6521c7510"> ' data-penguin-index="' + viewModel.previousIndex + '">Previous</a> ' + '<a href="https://www.php.cn/link/f0b875eb6cff6fd5f491e6b6521c7510"> ' data-penguin-index="' + viewModel.nextIndex + '">Next</a>'; this.previousIndex = viewModel.previousIndex; this.nextIndex = viewModel.nextIndex; // Wire up click events, and let the controller handle events var previousPenguin = this.element.querySelector('#previousPenguin'); previousPenguin.addEventListener('click', this.onClickGetPenguin); var nextPenguin = this.element.querySelector('#nextPenguin'); nextPenguin.addEventListener('click', this.onClickGetPenguin); nextPenguin.focus(); }; </code>
这指向一个端点并从服务器获取数据。我们可以通过使用单元测试模拟数据来测试这一点:
<code>var ElementMock = function ElementMock() { this.innerHTML = null; }; // Stub functions, so we can pass the test ElementMock.prototype.querySelector = function querySelector() { }; ElementMock.prototype.addEventListener = function addEventListener() { }; ElementMock.prototype.focus = function focus() { }; // Arrange var elementMock = new ElementMock(); var view = new PenguinView(elementMock); var viewModel = { name: 'Chinstrap', imageUrl: 'http://chinstrap1.jpg', size: '5.0kg (m), 4.8kg (f)', favoriteFood: 'krill', previousIndex: 1, nextIndex: 2 }; // Act view.render(viewModel); // Assert assert(elementMock.innerHTML.indexOf(viewModel.name) > 0); assert(elementMock.innerHTML.indexOf(viewModel.imageUrl) > 0); assert(elementMock.innerHTML.indexOf(viewModel.size) > 0); assert(elementMock.innerHTML.indexOf(viewModel.favoriteFood) > 0); assert(elementMock.innerHTML.indexOf(viewModel.previousIndex) > 0); assert(elementMock.innerHTML.indexOf(viewModel.nextIndex) > 0); </code>
如您所见,模型只关心原始数据。这意味着使用Ajax和JavaScript对象。如果您不清楚纯JavaScript中的Ajax,有一篇文章包含更多信息。
单元测试
对于任何纪律,获得保证所做的工作都很重要。MVC设计模式并不规定如何解决问题。设计模式为您提供了一套广泛的边界,使您能够编写干净的代码。这使您免受依赖压迫。
对我来说,这意味着为每个用例提供一套完整的单元测试。测试提供了有关代码如何有用的指导。这使其对任何想要进行特定更改的程序员来说都是开放和诱人的。
随意查看整套单元测试。我认为这将帮助您理解这种设计模式。每个测试都是针对特定用例的;将其视为细粒度的关注点。单元测试帮助您独立地考虑每个编码问题并解决此问题。MVC中这种功能关注点的分离通过每个单元测试都体现出来。
展望未来
企鹅的演示只包含了展示MVC有多么有用的基本可行概念。但是,您可以迭代许多改进:
- 添加一个显示所有企鹅列表的屏幕
- 添加键盘事件,以便您可以翻阅企鹅,还可以添加滑动功能
- 一个SVG图表来可视化数据,选择任何数据点,例如企鹅的大小
当然,我的读者,您可以进一步改进此演示。这些只是一些想法,您可以展示这种设计模式的强大功能。
结论
我希望您能看到MVC设计模式和一点纪律能带您到哪里。一个好的设计模式会在不碍事的同时促进编写干净的代码。它会在解决手头问题时让您专注于任务。它会让您成为一个更好、更高效的程序员。
在编程中,其目的是紧密关注手头的问题,同时消除冗余。编程的艺术是一次解决一个问题。在MVC中,这意味着一次解决一个功能性问题。
作为一名开发人员,很容易相信自己是逻辑的,并且不处理情绪。事实是,当您一次遇到太多问题时,您会感到沮丧。这是我们所有人必须应对的正常人类反应。事实上,沮丧会以负面方式影响代码质量。当这种感觉抓住您并主导您的工作时,它不再是关于逻辑了。随着解决方案承担更多风险和复杂的依赖关系,这可能会令人沮丧。
我喜欢的是专注于单一关注点。一次解决一个问题并获得积极的反馈。这样,您就可以保持专注、高效并避免无意义的事情。
本文由Vildan Softic同行评审。感谢所有SitePoint的同行评审者,使SitePoint的内容达到最佳状态!
关于JavaScript MVC设计模式的常见问题
JavaScript MVC设计模式的意义是什么?
JavaScript中的模型-视图-控制器(MVC)设计模式至关重要,因为它有助于以简洁和系统的方式组织代码。它将应用程序的关注点分成三个相互关联的组件。模型处理数据和业务逻辑,视图管理数据的显示,控制器处理用户输入。这种分离允许高效的代码管理、更轻松的调试和改进的可扩展性。
MVC模式如何提高代码的可读性和可维护性?
MVC模式通过隔离职责来增强代码的可读性和可维护性。MVC模式的每个组件都有不同的作用。这种分离意味着开发人员可以处理各个组件而不会影响其他组件。它还使查找和修复错误、更新功能或重构代码变得更容易,因为一个组件中的更改不会影响其他组件。
你能解释MVC模式中模型的作用吗?
MVC模式中的模型负责管理数据和业务逻辑。它从数据库检索数据、操作数据并更新数据。模型独立于用户界面,不直接与视图或控制器交互。相反,当其状态发生变化时,它会向它们发送通知。
MVC模式中视图的功能是什么?
MVC模式中的视图负责将数据显示给用户。它从模型接收数据并以用户友好的格式呈现数据。视图不直接与模型交互。相反,它从控制器接收更新。
控制器如何促进MVC模式?
MVC模式中的控制器充当模型和视图之间的中介。它处理用户输入并相应地更新模型和视图。当用户与视图交互时,控制器会解释输入并对模型进行必要的更改。它还会更新视图以反映这些更改。
MVC模式如何增强可扩展性?
MVC模式通过分离关注点来增强可扩展性。这种分离允许开发人员修改或扩展一个组件而不会影响其他组件。例如,如果您需要更改数据显示方式,您可以修改视图而无需接触模型或控制器。这种模块化使得随着时间的推移更容易扩展和发展您的应用程序。
MVC模式可以与其他JavaScript框架一起使用吗?
是的,MVC模式可以与各种JavaScript框架一起使用,例如AngularJS、Ember.js和Backbone.js。这些框架提供了一种实现MVC模式的结构化方法,使构建复杂的应用程序更容易。
在JavaScript中实现MVC模式的挑战是什么?
由于JavaScript的动态特性,在JavaScript中实现MVC模式可能具有挑战性。它需要对语言有很好的理解,并需要仔细规划以确保模型、视图和控制器正确分离并正确交互。此外,管理这些组件之间的更新可能很复杂。
MVC模式如何支持团队开发?
MVC模式通过允许不同的开发人员同时处理不同的组件来支持团队开发。例如,一个开发人员可以处理模型,而另一个开发人员可以处理视图。这种关注点的分离不仅提高了生产力,而且还减少了由于代码重叠而导致冲突或错误的可能性。
MVC模式可以用于开发移动应用程序吗?
是的,MVC模式可以用于开发移动应用程序。它提供了一种结构化的应用程序开发方法,使管理复杂的移动应用程序更容易。许多流行的移动开发框架,如React Native和Ionic,都支持MVC模式。
以上是香草JavaScript中的MVC设计模式的详细内容。更多信息请关注PHP中文网其他相关文章!

JavaScript字符串替换方法详解及常见问题解答 本文将探讨两种在JavaScript中替换字符串字符的方法:在JavaScript代码内部替换和在网页HTML内部替换。 在JavaScript代码内部替换字符串 最直接的方法是使用replace()方法: str = str.replace("find","replace"); 该方法仅替换第一个匹配项。要替换所有匹配项,需使用正则表达式并添加全局标志g: str = str.replace(/fi

本教程向您展示了如何将自定义的Google搜索API集成到您的博客或网站中,提供了比标准WordPress主题搜索功能更精致的搜索体验。 令人惊讶的是简单!您将能够将搜索限制为Y

利用轻松的网页布局:8个基本插件 jQuery大大简化了网页布局。 本文重点介绍了简化该过程的八个功能强大的JQuery插件,对于手动网站创建特别有用

因此,在这里,您准备好了解所有称为Ajax的东西。但是,到底是什么? AJAX一词是指用于创建动态,交互式Web内容的一系列宽松的技术。 Ajax一词,最初由Jesse J创造

核心要点 JavaScript 中的 this 通常指代“拥有”该方法的对象,但具体取决于函数的调用方式。 没有当前对象时,this 指代全局对象。在 Web 浏览器中,它由 window 表示。 调用函数时,this 保持全局对象;但调用对象构造函数或其任何方法时,this 指代对象的实例。 可以使用 call()、apply() 和 bind() 等方法更改 this 的上下文。这些方法使用给定的 this 值和参数调用函数。 JavaScript 是一门优秀的编程语言。几年前,这句话可

该帖子编写了有用的作弊表,参考指南,快速食谱以及用于Android,BlackBerry和iPhone应用程序开发的代码片段。 没有开发人员应该没有他们! 触摸手势参考指南(PDF) Desig的宝贵资源

jQuery是一个很棒的JavaScript框架。但是,与任何图书馆一样,有时有必要在引擎盖下发现发生了什么。也许是因为您正在追踪一个错误,或者只是对jQuery如何实现特定UI感到好奇


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

SublimeText3 英文版
推荐:为Win版本,支持代码提示!

安全考试浏览器
Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

禅工作室 13.0.1
功能强大的PHP集成开发环境

DVWA
Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中

mPDF
mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),