Sencha Touch 历史支持

  1. 云栖社区>
  2. 博客>
  3. 正文

Sencha Touch 历史支持

sp42 2016-04-03 17:07:00 浏览640
展开阅读全文

路由、深链接以及后退按钮 Routing, Deep Linking and the Back Button

Sencha Touch 2 提供完整的历史与深链接的支持,使得我们的 Web 应用拥有以下两点大的好处:

  • 浏览器的“后退”按钮在你的应用中有效了,也就是说,虽然按下了后退键,但浏览器并不会立刻刷新,而是仍停留在当前的页面中。
  • 可以使用“深链接”了,也就是说,可以分配的一个 url,直达制定的页面(在同一个页面中)。

上述功能综述之,就是为了更好地与原生程序相贴近,务求达到无差别的用户体验——这一点,尤其体现在能够提供“返回键”的 Android 机器上面。

设置路由器 Setting up routes

为你的应用安排的历史记录可以说一点都不困难,主要集中在如何理解的路由器(routes)的概念之上。路由器,简言之,用于定义某一 url 与用户控制器动作之间的映射关系——所谓映射关系,只要在浏览器的地址栏中输入一个地址,对应的控制器便会自动执行某组逻辑动作。咱们看看一个简单的控制器:

    Ext.define('MyApp.controller.Products', {
        extend: 'Ext.app.Controller',

        config: {
            routes: {
                'products/:id': 'showProduct'
            }
        },

        showProduct: function(id) {
            console.log('showing product ' + id);
        }
    });

通过指定 {@link Ext.app.Controller#routes routes},当诸如“#products/123” 这样的 url 输入到浏览器时就会通知 Main 控制器。详细点说,就是你的域名是 http://myapp.cpm,然后任何的 http://myapp.com/#products/123、 http://myapp.com/#products/456 或 http://myapp.com/#products/abc 这般的链接,其实终归自动进入到 showProduct 函数的执行环境中去。

当 showProduct 被执行时,其参数就是那个 url 解析而致的。有否注意到路由器中的“:id”部分,因为我们约定,有冒号“:”即表示后面跟的是为一个参数,路由器内部解析好 url 就会得到这个参数然后送入到你所写的函数中去。值得注意的是,该参数总是字符类型(无需多言,url 本身亦是字符类型),于是浏览器得到的“http://myapp.com/#products/456 or http://myapp.com/#products/abc”亦即等于调用 showProduct('456')。

路由器合法的格式多种多样,构成了不同参数的映射关系,例如:

    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');
        }
    });

第一个路由很简单,前面已经说了说;第二个路由接纳 #products/123/pdf 这样的输入,转到 showProductInFormat 函数,便在在控制台中记录了 showing product 123 in pdf format 这样的输出。另外,id、format 也是按照路由定义中的顺序送入函数中。

当然了,实际中你写的并不像我们这里教学例子的那么简单。大家想该怎么做了吗?就是在控制器中写你的业务逻辑,获取数据啊、更新UI啊的任务……

高级路由器 Advanced Routes

路由器支持通贝符(wildcards)。默认下,通贝符表示不分字符或数字。假设“products/:id/edit”可以接纳“#products/123/edit”但不接纳“#products/a ,fd.sd/edit”,因为后者包含的空格、逗号、句号均不在通贝符涵盖的类型之列。

不过有时我们想自定义复杂一些的规制,——这没问题,我们分配一个配置项对象给 routes 对象而不是字符串,好比如下:

    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);
        }
    });

当然不直接分配方法名称的名称还需要说明你要调用哪个函数,就写在配置项对象的 action 属性中。此外关键的是,条件配置项 {@link Ext.app.Route#conditions conditions} 其 key 为对应的 token,然后 value 为期望的规制。结果,假设一 url 系  http://myapp.com/#file/someFile.jpg,路由器解析后执行 showFile 函数,并送入“someFile.jpg”的参数。

复原状态 Restoring State

前面为大家介绍了定义路由器映射规制的相关知识后,接下来,就是详细讲讲具体的应用了。因为既然是“单页面的应用程序”的缘故,所以我们不轻易地产生一个 url 对应一张页面——如果我们进入首页后,一步步查找分类然后再找到那笔记录,——岂不是很累?所以说,由框架设定一套 url 规制便很有必要了。我们看看下面一个简单的例子,比如还是进入 http://myapp.com/#products/123,我们修改一下 Product 控制器如下:

    Ext.define('MyApp.controller.Products', {
        extend: 'Ext.app.Controller',

        config: {
            refs: {
                main: '#mainView'
            },

            routes: {
                'products/:id': 'showProduct'
            }
        },

        /**
         * products/:id 路由器的终点。获取一个货物的详细信息然后 push 的新视图。Endpoint for 'products/:id' routes. Adds a product details view (xtype = productview)
         * into the main view of the app then loads the Product into the view
         *
         */
        showProduct: function(id) {
            var view = 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);
                }
            });
        }
    });

这里的 products/:id 终点就会立刻添加一新视图到主视图中(如非导航类可以是其他的 TabPanel 控件),进而由 model 类(MyApp.model.Product)向服务器获取货物的详细信息。这一过程是异步的过程,所以我们分配了两个回调函数,分别对应成功(渲染 货物 UI)与失败的情形。

不同的应用有不同的复原状体逻辑,一些深入链接的情形。官方例子 Kitchen Sink 就是一个很好的例子,尤其它采用了  NestedList 方式的导航很能说明问题,并且有手机和平板的两种适应场景。具体怎么办到的?请到 Kitchen Sink 例子 app/controller/phone/Main.js and app/controller/tablet/Main.js 的 showView() 源码看看吧。

Sharing urls across Device Profiles

无论你有多少套<a href="#!/guide/profiles">设备描述 Device Profiles</a>,但一般来说你都会只使用路由结构,也就是说,你在 iPhone 上看到一页面,发觉不错,然后想共享该 url 给别人的话,那么直接把 url 复制给别人即可,无须担心人家的是否手机抑或平板。因此,建议分别在手机子类上写一套路由的配置,然后在平板子类上写一套路由的配置:

    Ext.define('MyApp.controller.Products', {
        extend: 'Ext.app.Controller',
        config: {
            routes: {
                'products/:id': 'showProduct'
            }
        }
    });

手机版的显示方式有所不同,于是,我们写出 showProduct 函数的手机版实现:

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

        showProduct: function(id) {
            console.log('showing a phone-specific Product page for ' + id);
        }
    });
平板电脑的话,在其子类中写出特定的实现:

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

        showProduct: function(id) {
            console.log('showing a tablet-specific Product page for ' + id);
        }
    });

网友评论

登录后评论
0/500
评论
sp42
+ 关注