Sencha Touch 2 官方文档翻译之 History Support(访问历史支持)

前言:

 

SenchaTouch 2的访问历史和路由支持堪称独有特色,也让它的用户体验向Native应用更加靠近了一步。通俗的讲,访问历史和路由这两个功能(深链接只是路由功能的一个应用)就是在浏览器环境中,把单页面应用程序模拟成多页面交互的效果,而且还无需刷新页面。

 

这篇文章的英文原址是http://docs.sencha.com/touch/2-0/#!/guide/history_support

原文标题是:Routing, Deep Linking and theBack Button路由、深链接以及后退按钮

Sencha Touch 交流QQ213119459欢迎您的加入。


 

Routing, DeepLinking and the Back Button

路由、深链接和后退按钮


注:为方便起见,文中所有出现 Sencha Touch的地方均以 ST简写替代。


Sencha Touch 2 comes with fullyhistory and deep-linking support. This gives your web applications 2 massivebenefits:

ST2带来了全面的访问历史和深链接支持,这将赋予我们的应用程序两个好处:


l  The back button works inside yourapps, navigating correctly and quickly between screens without refreshing thepage

(浏览器的)后退按钮将会在你的应用程序内部发挥作用,从而实现在(应用程序内部的)界面屏幕之间快速正确的导航,而不会刷新整个页面。

l  Deep-linking enables your users tosend a link to any part of the app and have it load the right screen

深链接功能使得用户可以把应用程序的任意部分作为链接发送出去,当这个链接被点击时页面会加载到正确的内容(而不是应用程序的初始页面)

The result is an application thatfeels much more in tune with what users expect from native apps, especially onAndroid devices with the built-in back button fully supported.

结果就是你的web应用程序也可以给人带来如同本地应用一般的舒畅体验,尤其是对Android设备来说,后退按钮可以得到最完整的功能支持。


Setting up routes

设置路由


Setting up history support for your apps is prettystraightforward and is centered around the concept of routes. Routes are asimple mapping between urls and controller actions - whenever a certain type ofurl is detected in the address bar the corresponding Controller action iscalled automatically. Let's take a look at a simple Controller:

以路由功能为基础,为应用程序设置访问历史功能支持变得很容易。路由其实实现的就是url和控制器动作之间的映射,当地址栏中的一个特定类型url被检测到的时候,相应的控制其动作就会被自动调用,我们来看一个简单的控制器例子:


Ext.define('MyApp.controller.Products', {

   extend: 'Ext.app.Controller',

 

   config: {

       routes: {

           'products/:id': 'showProduct'

        }

   },

 

   showProduct: function(id) {

       console.log('showing product ' + id);

   }

});


By specifying the routes above, the Main controllerwill be notified whenever the browser url looks like "#products/123".For example, if your application is deployed onto http://myapp.com, any urlthat looks like http://myapp.com/#products/123, http://myapp.com/#products/456or http://myapp.com/#products/abc will automatically cause your showProductfunction to be called.

通过上述路由的定义,当浏览器url是类似#products/123的时候,Main控制器(译者注:怀疑作者写错了,应该是Products控制器)就会被通知。例如,你的应用程序部署在http://myapp.com这个路径,那么凡是类似http://myapp.com/#products/123,http://myapp.com/#products/456,http://myapp.com/#products/abc这样的url都会自动调用你的showProduct方法。


When the showProduct function is called this way,it is passed the 'id' token that was parsed out of the url. This happensbecause we used ':id' in the route - whenever a route contains a ':' it willattempt to pull that information out of the url and pass it into your function.Note that these parsed tokens are always strings (because urls are alwaysstrings themselves), so hitting a route like 'http://myapp.com/#products/456'is the same as calling showProduct('456').

当showProduct方法这样被调用的时候,其实它被传递了id这个参数,这是因为我们在路由中使用了“:id”,当一个路由包含了“:”的时候,他会被视作参数名然后把对应的值从url中获取出来并传给你的函数。注意这些解析出来的参数总是以string类型出现的,因为url本身就是个string类型。所以访问一个类似http://myapp.com/#products/456这样的路由其实就等于调用了showProduct('456')方法。


You can specify any number of routes and yourroutes can each have any number of tokens - for example:

你可以定义多个路由,每个路由也可以包含多个参数,比如:


Ext.define('MyApp.controller.Products', {

   extend: 'Ext.app.Controller',

 

   config: {

       routes: {

           'products/:id': 'showProduct',

           'products/:id/:format': 'showProductInFormat'

        }

   },

 

   showProduct: function(id) {

       console.log('showing product ' + id);

   },

 

   showProductInFormat: function(id, format) {

       console.log('showing product ' + id + ' in ' + format + ' format');

   }

});


The second route accepts urls like#products/123/pdf, which will route through to the showProductInFormat functionand console log 'showing product 123 in pdf format'. Notice that the argumentsare passed into the function in the order they appear in the route definition.

第二个路由接受像#products/123/pdf这样的url,它将被指向showProductInFormat函数并且输出'showing product 123 in pdf format'结果。注意传入函数的参数顺序与路由中定义的顺序保持一致。


Of course, your Controller function probably won'tactually just log a message to the console, it can do anything needed by yourapp - whether it's fetching data, updating the UI or anything else.

 

当然你的控制器函数不会只是输出一段调试信息,你可以根据需要做任何事情,获取数据、更新界面或者其他什么的都行。


Advanced Routes

路由进阶


By default, wildcards in routes match any sequenceof letters and numbers. This means that a route for"products/:id/edit" would match the url"#products/123/edit" but not "#products/a ,fd.sd/edit" -the second contains a number of letters that don't qualify (space, comma, dot).

默认情况下,路由中的通配符可以匹配任意连续的字母和数字字符串。比如"products/:id/edit"这个路由可以匹配"#products/123/edit",但是却不能匹配"#products/a,fd.sd/edit",后面这个url包含了一些非法字符(空格,逗号,点号)


Sometimes though we want the route to be able tomatch urls like this, for example if a url contains a file name we may want tobe able to pull that out into a single token. To achieve this we can pass aconfiguration object into our Route:

但是有时候我们还是需要路由能够匹配类似的url,比如我们的url包含一个文件名,我们想把它取出来。这时可以传递一个配置对象到路由中去:


Ext.define('MyApp.controller.Products', {

   extend: 'Ext.app.Controller',

 

   config: {

       routes: {

           'file/:filename': {

               action: 'showFile',

               conditions: {

                   ':filename': "[0-9a-zA-Z\.]+"

               }

           }

        }

   },

 

   //opens a new window to show the file

   showFile: function(filename) {

       window.open(filename);

   }

});


So instead of an action string we now have aconfiguration object that contains an 'action' property. In addition, we addeda conditions configuration which tells the :filename token to match anysequence of numbers and letters, along with a period ('.'). This means ourroute will now match urls like http://myapp.com/#file/someFile.jpg, passing'someFile.jpg' in as the argument to the Controller's showFile function.

也就是说可以使用一个包含了action属性的配置对象来取代action字符串。除此之外,我们给这这个对象添加一个conditions属性,这个属性定义了:filename这个参数可以匹配英文数字、阿拉伯数字外加一个英文点号。这样我们的路由现在就可以匹配像http://myapp.com/#file/someFile.jpg这样的url了,它会把someFile.jpg作为参数传递给控制器的showFile函数。


Restoring State

还原状态


One challenge that comes with supporting historyand deep linking is that you need to be able to restore the full UI state ofthe app to make it as if the user navigated to the deep-linked page him orherself. This can sometimes be tricky but is the price we pay for making lifebetter for the user.

伴随访问历史支持和深链接而来的挑战就是你得想办法还原整个UI界面的状态,来使得整个界面看起来像是用户自己一步一步导航过去似的。这的确很棘手,但却是我们为了提高用户体验而不得不付出的代价。


Let's take the simple example of loading a productbased on a url like http://myapp.com/#products/123. Let's update our ProductsController from above:

我们来看一个小例子,怎样加载http://myapp.com/#products/123这个链接指向的页面内容,代码是从上面Products控制器改动而来:


Ext.define('MyApp.controller.Products', {

   extend: 'Ext.app.Controller',

 

   config: {

       refs: {

           main: '#mainView'

        },

 

       routes: {

           'products/:id': 'showProduct'

        }

   },

 

   /**

     * Endpoint for'products/:id' routes. Adds a product details view (xtype = productview)

     * into the main viewof the app then loads the Product into the view

     *

     */

   showProduct: function(id) {

        varview = this.getMain().add({

           xtype: 'productview'

        });

 

       MyApp.model.Product.load(id, {

           success: function(product) {

               view.setRecord(product);

           },

           failure: function() {

               Ext.Msg.alert('Could not load Product ' + id);

           }

        });

   }

});


Here our 'products/:id' url endpoint results in theimmediate addition of a view into our app's main view (which could be aTabPanel or other Container), then uses our product model (MyApp.model.Product)to fetch the Product from the server. We added a callback that then populatesthe product detail view with the freshly loaded Product. We render the UIimmediately (as opposed to only rendering it when the Product has been loaded)so that we give the user visual feedback as soon as possible.

'products/:id'这个路由映射的目标应该是立刻把一个detail视图加入到应用程序的main视图(可能是一个tabpanel或者其他container)里面去,之后通过product数据模型(MyApp.model.Product)从服务器取回product信息,并在回调里把最新获取到的数据发布到product的detail视图上,然后立刻渲染UI界面(与之相对照的是在Product被加载之后才渲染)这样我们就以最快速度给了用户他们想要的数据。


Each app will need different logic when it comes torestoring state for a deeply-linked view. For example, the Kitchen Sink needsto restore the state of its NestedList navigation as well as rendering thecorrect view for the given url. To see how this is accomplished in both Phoneand Tablet profiles check out the showView functions in the Kitchen Sink's app/controller/phone/Main.jsand app/controller/tablet/Main.js files.

每个应用程序在为深链接还原状态的时候都有不同的逻辑。比如Kitchen Sink需要还原他的级联列表导航栏并渲染url指定的视图。想知道在Phone和Tablet两个Profile中是怎么实现的,请参考Kitchen Sink的app/controller/phone/Main.js和app/controller/tablet/Main.js文件中showView函数。


Sharing urls acrossDevice Profiles

跨设备配置共享url


In most cases you'll want to share the exact sameroute structure between your Device Profiles. This way a user using your Phoneversion can send their current url to a friend using a Tablet and expect thattheir friend will be taken to the right place in the Tablet app. This generallymeans it's best to define your route configurations in the superclass of thePhone and Tablet-specific Controllers:

大部分情况下,我们都希望在不同设备配置之间共享同样的路由架构。这样的话一个使用手机界面的用户可以把自己当前的url发送给使用平板电脑界面的朋友,并且还能保证朋友在平板电脑上看到的也是同样内容。这意味着我们最好在一个父类中定义route配置,然后由手机和平板的控制器分别继承该父类的配置:


Ext.define('MyApp.controller.Products', {

   extend: 'Ext.app.Controller',

 

   config: {

       routes: {

           'products/:id': 'showProduct'

        }

   }

});


Now in your Phone-specific subclass you can justimplement the showProduct function to give a Phone-specific view for the givenproduct:

现在phone专用的子类当中你只需要继承这个showProduct函数并显示一个手机下专用的视图即可:


Ext.define('MyApp.controller.phone.Products', {

   extend: 'MyApp.controller.Products',

 

   showProduct: function(id) {

       console.log('showing a phone-specific Product page for ' + id);

   }

});


And in your Tablet-specific subclass just do thesame thing, this time showing a tablet-specific view:

在平台专用的子类当中也要做同样的事情,只不过这次调用的是一个平台电脑下专用的视图:


Ext.define('MyApp.controller.tablet.Products', {

   extend: 'MyApp.controller.Products',

 

   showProduct: function(id) {

       console.log('showing a tablet-specific Product page for ' + id);

   }

});


There are some exceptions to this rule, usually todo with linking to navigation states. The Kitchen Sink example has phone andtablet specific views - on both profiles we use a NestedList for navigation butwhereas on the Tablet NestedList only takes up the left hand edge of the screen,one the Phone it fills the screen. In order to make the back button work asexpected on phones, each time we navigate in the NestedList we push the new urlinto the history, which means that the Phone-specific controller has oneadditional route. Check out the app/controller/phone/Main.js file for anexample of this.

也有一些例外情况,比如Kitchen Sink例子当中有phone和tablet两个专用视图,两个profile中都有一个级联列表作为导航栏,但是平板电脑上的导航栏是一直固定在左侧不动,而手机上的则是充满整个屏幕。为了使手机上的后退按钮可以正常工作,每次我们在手机级联列表中导航的时候都要设定一个新的url到访问历史当中去,这样手机专用的控制器就会多一个额外的路由,具体可以看一下app/controller/phone/Main.js文件当中的代码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值