gpt4 book ai didi

mvvm - 如何使用 jquery-mobile 和 Knockoutjs 构建 web 应用程序

转载 作者:行者123 更新时间:2023-12-03 05:03:22 24 4
gpt4 key购买 nike

我想构建一个移动应用程序,除了 html/css 和 JavaScript 之外别无他物。虽然我对如何使用 JavaScript 构建 Web 应用程序有很好的了解,但我想我可能会研究一下像 jquery-mobile 这样的框架。

起初,我认为 jquery-mobile 只不过是一个针对移动浏览器的小部件框架。与 jquery-ui 非常相似,但适用于移动世界。但我注意到 jquery-mobile 不止于此。它带有一堆架构,让您可以使用声明性 html 语法创建应用程序。因此,对于最容易想到的应用程序,您不需要自己编写一行 JavaScript(这很酷,因为我们都喜欢少工作,不是吗?)

为了支持使用声明性 html 语法创建应用程序的方法,我认为将 jquery-mobile 与knockoutjs 结合使用是一个不错的选择。 Knockoutjs 是一个客户端 MVVM 框架,旨在将 WPF/Silverlight 中已知的 MVVM 超能力引入 JavaScript 世界。

对我来说,MVVM 是一个新世界。虽然我已经阅读了很多关于它的内容,但我以前从未真正使用过它。

所以这篇文章是关于如何一起使用 jquery-mobile 和knockoutjs 来构建一个应用程序。我的想法是写下我在看了几个小时后想出的方法,并让一些 jquery-mobile/knockout yoda 对其进行评论,向我展示为什么它很糟糕以及为什么我不应该首先进行编程地方 ;-)

html

jquery-mobile 在提供页面的基本结构模型方面做得很好。虽然我很清楚我可以在之后通过 ajax 加载我的页面,但我决定将所有页面都保存在一个 index.html 文件中。在这个基本场景中,我们讨论的是两个页面,因此掌握最重要的内容应该不会太难。

<!DOCTYPE html> 
<html>
<head>
<title>Page Title</title>
<link rel="stylesheet" href="libs/jquery-mobile/jquery.mobile-1.0a4.1.css" />
<link rel="stylesheet" href="app/base/css/base.css" />
<script src="libs/jquery/jquery-1.5.0.min.js"></script>
<script src="libs/knockout/knockout-1.2.0.js"></script>
<script src="libs/knockout/knockout-bindings-jqm.js" type="text/javascript"></script>
<script src="libs/rx/rx.js" type="text/javascript"></script>
<script src="app/App.js"></script>
<script src="app/App.ViewModels.HomeScreenViewModel.js"></script>
<script src="app/App.MockedStatisticsService.js"></script>
<script src="libs/jquery-mobile/jquery.mobile-1.0a4.1.js"></script>
</head>
<body>

<!-- Start of first page -->
<div data-role="page" id="home">

<div data-role="header">
<h1>Demo App</h1>
</div><!-- /header -->

<div data-role="content">

<div class="ui-grid-a">
<div class="ui-block-a">
<div class="ui-bar" style="height:120px">
<h1>Tours today (please wait 10 seconds to see the effect)</h1>
<p><span data-bind="text: toursTotal"></span> total</p>
<p><span data-bind="text: toursRunning"></span> running</p>
<p><span data-bind="text: toursCompleted"></span> completed</p>
</div>
</div>
</div>

<fieldset class="ui-grid-a">
<div class="ui-block-a"><button data-bind="click: showTourList, jqmButtonEnabled: toursAvailable" data-theme="a">Tour List</button></div>
</fieldset>

</div><!-- /content -->

<div data-role="footer" data-position="fixed">
<h4>by Christoph Burgdorf</h4>
</div><!-- /header -->
</div><!-- /page -->

<!-- tourlist page -->
<div data-role="page" id="tourlist">

<div data-role="header">
<h1>Bar</h1>
</div><!-- /header -->

<div data-role="content">
<p><a href="#home">Back to home</a></p>
</div><!-- /content -->

<div data-role="footer" data-position="fixed">
<h4>by Christoph Burgdorf</h4>
</div><!-- /header -->
</div><!-- /page -->

</body>
</html>

JavaScript

那么让我们来看看有趣的部分——JavaScript!

当我开始考虑对应用程序进行分层时,我想到了几件事(例如可测试性、松散耦合)。我将向您展示我如何决定拆分我的文件并评论诸如为什么我在走的时候选择一件事而不是另一件事...

应用程序.js
var App = window.App = {};
App.ViewModels = {};

$(document).bind('mobileinit', function(){
// while app is running use App.Service.mockStatistic({ToursCompleted: 45}); to fake backend data from the console
var service = App.Service = new App.MockedStatisticService();

$('#home').live('pagecreate', function(event, ui){
var viewModel = new App.ViewModels.HomeScreenViewModel(service);
ko.applyBindings(viewModel, this);
viewModel.startServicePolling();
});
});

App.js 是我的应用程序的入口点。它创建 App 对象并为 View 模型提供命名空间(即将推出)。它监听 jquery-mobile 提供的 mobileinit 事件。

如您所见,我正在创建某种 ajax 服务的实例(我们稍后会介绍)并将其保存到变量“service”中。

我还为主页连接了 pagecreate 事件,在该事件中我创建了一个 viewModel 的实例,该实例获取传入的服务实例。这一点对我来说至关重要。如果有人认为,这应该有所不同,请分享您的想法!

关键是, View 模型需要对服务(GetTour/、SaveTour 等)进行操作。但我不想让 ViewModel 知道更多关于它的信息。例如,在我们的例子中,我只是传入了一个模拟的 ajax 服务,因为后端还没有开发。

我应该提到的另一件事是 ViewModel 对实际 View 的了解为零。这就是我从 pagecreate 处理程序中调用 ko.applyBindings(viewModel, this) 的原因。我想让 View 模型与实际 View 分开,以便更容易地测试它。

App.ViewModels.HomeScreenViewModel.js
(function(App){
App.ViewModels.HomeScreenViewModel = function(service){
var self = {}, disposableServicePoller = Rx.Disposable.Empty;

self.toursTotal = ko.observable(0);
self.toursRunning = ko.observable(0);
self.toursCompleted = ko.observable(0);
self.toursAvailable = ko.dependentObservable(function(){ return this.toursTotal() > 0; }, self);
self.showTourList = function(){ $.mobile.changePage('#tourlist', 'pop', false, true); };
self.startServicePolling = function(){
disposableServicePoller = Rx.Observable
.Interval(10000)
.Select(service.getStatistics)
.Switch()
.Subscribe(function(statistics){
self.toursTotal(statistics.ToursTotal);
self.toursRunning(statistics.ToursRunning);
self.toursCompleted(statistics.ToursCompleted);
});
};
self.stopServicePolling = disposableServicePoller.Dispose;

return self;
};
})(App)

虽然您会发现大多数使用对象字面量语法的 Knockoutjs View 模型示例,但我使用的是带有“self”辅助对象的传统函数语法。基本上,这是一个品味问题。但是当你想要一个可观察的属性来引用另一个时,你不能一次性写下对象字面量,这会降低它的对称性。这就是我选择不同语法的原因之一。

下一个原因是我之前提到的可以作为参数传递的服务。

这个 View 模型还有一件事,我不确定我是否选择了正确的方式。我想定期轮询ajax服务以从服务器获取结果。所以,我选择实现 startServicePolling/stopServicePolling 方法来这样做。这个想法是在 pageshow 上开始轮询,并在用户导航到不同页面时停止轮询。

您可以忽略用于轮询服务的语法。这是 RxJS 的魔法。请确保我正在轮询它并使用返回的结果更新可观察属性,如您在 Subscribe(function(statistics){..}) 部分中看到的那样。

App.MockedStatisticsService.js

好的,只剩下一件事要向您展示。这是实际的服务实现。我不会在这里详细介绍。它只是一个在调用 getStatistics 时返回一些数字的模拟。我使用另一种方法 mockStatistics 在应用程序运行时通过浏览器 js 控制台设置新值。
(function(App){
App.MockedStatisticService = function(){
var self = {},
defaultStatistic = {
ToursTotal: 505,
ToursRunning: 110,
ToursCompleted: 115
},
currentStatistic = $.extend({}, defaultStatistic);;

self.mockStatistic = function(statistics){
currentStatistic = $.extend({}, defaultStatistic, statistics);
};

self.getStatistics = function(){
var asyncSubject = new Rx.AsyncSubject();
asyncSubject.OnNext(currentStatistic);
asyncSubject.OnCompleted();
return asyncSubject.AsObservable();
};

return self;
};
})(App)

好吧,我写的比我最初计划写的要多得多。我的手指受伤了,我的狗要我带它们去散步,我感到筋疲力尽。我确信这里有很多东西遗漏了,而且我输入了一堆拼写错误和语法错误。如果有什么不清楚的可以对我大喊大叫,我稍后会更新帖子。

该帖子可能看起来不是一个问题,但实际上是!我希望你分享你对我的方法的看法,如果你认为它是好是坏,或者我是否遗漏了一些东西。

更新

由于这篇文章非常受欢迎,并且因为有几个人要求我这样做,我把这个例子的代码放在了github上:

https://github.com/cburgdorf/stackoverflow-knockout-example

趁热吃!

最佳答案

Note: As of jQuery 1.7, the .live() method is deprecated. Use .on() to attach event handlers. Users of older versions of jQuery should use .delegate() in preference to .live().



我正在做同样的事情( knockout + jquery mobile)。我正在尝试写一篇关于我所学知识的博客文章,但同时这里有一些提示。请记住,我也在尝试学习 knockout/jquery 移动版。

View 模型和页面

每个 jQuery Mobile 页面仅使用一 (1) 个 View 模型对象。否则,您可能会遇到多次触发的点击事件问题。

查看模型并单击

仅将 ko.observable-fields 用于 View 模型点击事件。

ko.apply 绑定(bind)一次

如果可能:只为每个页面调用一次 ko.applyBinding 并使用 ko.observable 而不是多次调用 ko.applyBinding。

页面隐藏和 ko.cleanNode

记得清理页面隐藏上的一些 View 模型。
ko.cleanNode 似乎干扰了 jQuery Mobiles 渲染 - 导致它重新渲染 html。如果在页面上使用 ko.cleanNode,则需要删除数据角色并在源代码中插入呈现的 jQuery Mobile html。
$('#field').live('pagehide', function() {
ko.cleanNode($('#field')[0]);
});

页面隐藏并点击

如果您绑定(bind)到点击事件 - 记得清理 .ui-btn-active。完成此操作的最简单方法是使用以下代码片段:
$('[data-role="page"]').live('pagehide', function() {
$('.ui-btn-active').removeClass('ui-btn-active');
});

关于mvvm - 如何使用 jquery-mobile 和 Knockoutjs 构建 web 应用程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6089727/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com