在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称(OpenSource Name):addyosmani/backbone-mobile-search开源软件地址(OpenSource Url):https://github.com/addyosmani/backbone-mobile-search开源编程语言(OpenSource Language):JavaScript 100.0%开源软件介绍(OpenSource Introduction):#Flickly Mobile *This app was further refactored 21/12 A complete Backbone.js + jQuery Mobile sample app using AMD for separation of modules, Require.js for dependency management + template externalisation and Underscore for templating. The app allows you to search for images using the Flickr API, lookup individual photos in more detail, bookmark any state for results or photos, supports pagination and more. ##Uses:
Note: This application needs to be run on a HTTP server, local or otherwise. To remove this requirement, simply switch from using external templates via Require.js/the text plugin to inline ones. ##Snippets from the upcoming tutorial for this app: Writing Modular Desktop & Mobile Applications With Backbone.js (in no particular order) ###Why use Require.js with Backbone? In case you haven't used it before, Require.js is a popular script loader written by James Burke - a developer who has been quite instrumental in helping shape the AMD module format, which we'll discuss more shortly. Some of Require.js's capabilities include helping to load multiple script files, helping define modules with or without dependencies and loading in non-script dependencies such as text files. So, why use Require.js with Backbone?. Although Backbone is excellent when it comes to providing a sanitary structure to your applications, there are a few key areas where some additional help could be used:
Require.js is compatible with the AMD (Asynchronous Module Definition) format, a format which was born from a desire to write something better than the 'write lots of script tags with implicit dependencies and manage them manually' approach to development. In addition to allowing you to clearly declare dependencies, AMD works well in the browser, supports string IDs for dependencies, declaring multiple modules in the same file and gives you easy-to-use tools to avoid polluting the global namespace.
Think about the GMail web-client for a moment. When users initially load up the page on their first visit, Google can simply hide widgets such as the chat module until a user has indicated (by clicking 'expand') that they wish to use it. Through dynamic dependency loading, Google could load up the chat module only then, rather than forcing all users to load it when the page first initializes. This can improve performance and load times and can definitely prove useful when building larger applications. I've previously written a detailed article covering both AMD and other module formats and script loaders here (http://addyosmani.com/writing-modular-js) in case you'd like to explore this topic further. The takeaway is that although it's perfectly fine to develop applications without a script loader or clean module format in place, it can be of significant benefit to consider using these tools in your application development. ###A brief tutorial on writing AMD modules with Require.js As discussed above, the overall goal for the AMD format is to provide a solution for modular JavaScript that developers can use today. The two key concepts you need to be aware of when using it with a script-loader are a define( module_id /*optional*/, [dependencies] /*optional*/, definition function /*function for instantiating the module or object*/ ); As you can tell by the inline comments, the Back to the define signature, the dependencies argument represents an array of dependencies which are required by the module you are defining and the third argument ('definition function') is a function that's executed to instantiate your module. A barebone module (compatible with Require.js) could be defined using // A module ID has been omitted here to make the module anonymous define(['foo', 'bar'], // module definition function // dependencies (foo and bar) are mapped to function parameters function ( foo, bar ) { // return a value that defines the module export // (i.e the functionality we want to expose for consumption) // create your module here var myModule = { doStuff:function(){ console.log('Yay! Stuff'); } } return myModule; });
// Consider 'foo' and 'bar' are two external modules // In this example, the 'exports' from the two modules loaded are passed as // function arguments to the callback (foo and bar) // so that they can similarly be accessed require(['foo', 'bar'], function ( foo, bar ) { // rest of your code here foo.doSomething(); }); Defining AMD Modules Using Require.js //Todo: explain and demonstrate how to wrap views etc. inside AMD modules and how that all works. require(['app/myModule'], function( myModule ){ // start the main module which in-turn // loads other modules var module = new myModule(); module.doStuff(); }); Modular desktop applications with Require.js and BackboneMy side-project TodoMVC now includes a modular Backbone example using AMD and Require.js (thanks to Thomas Davis) for anyone wishing to look at a sample without any of the jQuery Mobile code included: https://github.com/addyosmani/todomvc/tree/master/todo-example/backbone+require This covers how to wrap your views, models, modules etc. using AMD and also cleanly demonstrates handling dependency management as well as Flickly does. ###External [Underscore/Handlebars/Mustache] templates using Require.js Moving your [Underscore/Mustache/Handlebars] templates to external files is actually quite straight-forward. As this application makes use of Require.js, I'll discuss how to implement external templates using this specific script loader. RequireJS has a special plugin called text.js which is used to load in text file dependencies. To use the text plugin, simply follow these simple steps:
require.config( { paths: { 'backbone': 'libs/AMDbackbone-0.5.3', 'underscore': 'libs/underscore-1.2.2', 'text': 'libs/require/text', 'jquery': 'libs/jQuery-1.7.1', 'json2': 'libs/json2', 'datepicker': 'libs/jQuery.ui.datepicker', 'datepickermobile': 'libs/jquery.ui.datepicker.mobile', 'jquerymobile': 'libs/jquery.mobile-1.0' }, baseUrl: 'app' } );
require(['js/app', 'text!templates/mainView.html'], function(app, mainView){ // the contents of the mainView file will be // loaded into mainView for usage. } );
With Underscore.js's micro-templating (and jQuery) this would typically be: HTML: <script type="text/template" id="mainViewTemplate"> <% _.each( person, function( person_item ){ %> <li><%= person_item.get("name") %></li> <% }); %> </script> JS: var compiled_template = _.template( $('#mainViewTemplate').html() ); With Require.js and the text plugin however, it's as simple as saving your template into an external text file (say, mainView.html) and doing the following: require(['js/app', 'text!templates/mainView.html'], function(app, mainView){ var compiled_template = _.template( mainView ); } ); That's it!. You can then go applying your template to a view in Backbone doing something like: collection.someview.el.html( compiled_template( { results: collection.models } ) ); All templating solutions will have their own custom methods for handling template compilation, but if you understand the above, substituting Underscore's micro-templating for any other solution should be fairly trivial. Note: You may also be interested in looking at https://github.com/ZeeAgency/requirejs-tpl. It's an AMD-compatible version of the Underscore templating system that also includes support for optimization (pre-compiled templates) which can lead to better performance and no evals. I have yet to use it myself, but it comes as a recommended resource. ###Backbone and jQuery Mobile: Resolving the routing conflicts The first major hurdle developers typically run into when building Backbone applications with jQuery Mobile is that both frameworks have their own opinions about how to handle application navigation. Backbone's routers offer an explicit way to define custom navigation routes through Backbone.Router, whilst jQuery Mobile encourages the use of URL hash fragments to reference separate 'pages' or views in the same document. jQuery Mobile also supports automatically pulling in external content for links through XHR calls meaning that there can be quite a lot of inter-framework confusion about what a link pointing at '#/photo/id' should actually be doing. Some of the solutions that have been previously proposed to work-around this problem included manually patching Backbone or jQuery Mobile. I discourage opting for these techniques as it becomes necessary to manually patch your framework builds when new releases get made upstream. There's also https://github.com/azicchetti/jquerymobile-router, which tries to solve this problem differently, however I think my proposed solution is both simpler and allows both frameworks to cohabit quite peacefully without the need to extend either. What we're after is a way to prevent one framework from listening to hash changes so that we can fully rely on the other (e.g. Backbone.Router) to handle this for us exclusively. Using jQuery Mobile this can be done by setting: $.mobile.hashListeningEnabled = false; prior to initializing any of your other code. I discovered this method looking through some jQuery Mobile commits that didn't make their way into the official docs, but am happy to see that they are now covered here http://jquerymobile.com/test/docs/api/globalconfig.html in more detail. The next question that arises is, if we're preventing jQuery Mobile from listening to URL hash changes, how can we still get the benefit of being able to navigate to other sections in a document using the built-in transitions and effects supported?. Good question. This can now be solve by simply calling var url = '#about', effect = 'slideup', reverse = false, changeHash = false; $.mobile.changePage( url , { transition: effect}, reverse, changeHash ); In the above sample, Note: For some parallel work being done to explore how well the jQuery Mobile Router plugin works with Backbone, you may be interested in checking out https://github.com/Filirom1/jquery-mobile-backbone-requirejs. ###Getting started Once you feel comfortable with the Backbone fundamentals (http://msdn.microsoft.com/en-us/scriptjunkie/hh377172.aspx) and you've put together a rough wireframe of the app you may wish to build, start to think about your application architecture. Ideally, you'll want to logically separate concerns so that it's as easy as possible to maintain the app in the future. Namespacing For this application, I opted for the nested namespacing pattern. Implemented correctly, this enables you to clearly identify if items being referenced in your app are views, other modules and so on. This initial structure is a sane place to also include application defaults (unless you prefer maintaining those in a separate file). window.mobileSearch = window.mobileSearch || { views: { appview: new AppView }, routers:{ workspace:new Workspace() }, utils: utils, defaults:{ resultsPerPage: 16, safeSearch: 2, maxDate:'', minDate:'01/01/1970' } } Models In the Flickly application, there are at least two unique types of data that need to be modelled - search results and individual photos, both of which contain additional meta-data like photo titles. If you simplify this down, search results are actually groups of photos in their own right, so the application only requires:
Views The views we'll need include an application view, a search results view and a photo view. Static views or pages of the single-page application which do not require a dynamic element to them (e.g an 'about' page) can be easily coded up in your document's markup, independant of Backbone. Routers A number of possible routes need to be taken into consideration:
###jQuery Mobile: Going beyond mobile application development The majority of jQM apps I've seen in production have been developed for the purpose of providing an optimal experience to users on mobile devices. Given that the framework was developed for this purpose, there's nothing fundamentally wrong with this, but many developers forget that jQM is a UI framework not dissimilar to jQuery UI. It's using the widget factory and is capable of being used for a lot more than we give it credit for. If you open up Flickly in a desktop browser, you'll get an image search UI that's modelled on Google.com, however, review the components (buttons, text inputs, tabs) on the page for a moment. The desktop UI doesn't look anything like a mobile application yet I'm still using jQM for theming mobile components; the tabs, date-picker, sliders - everything in the desktop UI is re-using what jQM would be providing users on mobile devices. Thanks to some media queries, the desktop UI can make optimal use of whitespace, expanding component blocks out and providing alternative layouts whilst still making use of jQM as a component framework. The benefit of this is that I don't need to go pulling in jQuery UI separately to be able to take advantage of these features. Thanks to the recent ThemeRoller my components can look pretty much exactly how I would like them to and users of the app can get a jQM UI for lower-resolutions and a jQM-ish UI for everything else. The takeaway here is just to remember that if you're not (already) going through the hassle of conditional script/style loading based on screen-resolution (using matchMedia.js etc), there are simpler approaches that can be taken to cross-device component theming. |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论