UI-Router:为什么开发者都不喜欢Angular.js内置的路由

Angular.js 是一个用来构建“富客户端”的神奇JavaScript框架。但是事实却是许多开发者却不使用其内置的路由模块。反而使用AngularUI项目的 UI-Router模块来代替之。

这是因为UI-Router有两个重要的特性:

  • 多样化视图
  • 嵌入式视图

这篇文章将解释这两个特性,和运用现实生活的例子展现这两个特性的重要性。

为什么你需要使用UI-Router

多样化视图

大多数的应用程序都可以分解为一个一个区块。最简单的情况,一个应用程序有头部(header),主体内容(main content area),以及一个尾部(footer)。

通常一个应用程序会有一个额外的侧边栏(sidebar )在页面的左边或者右边。

整体结构如下图所示:

应用结构图

大多数用例中,这些区块将同时显示在页面上。Angular.js 的内置路由ngRoute只允许一个视图(ng-view)出现在页面上。这样限制的情况下,人们可以使用包含页面(ng-include)或者 其他的变通方法为应用创建一个布局(layout)或主页(master page)。UI-Router支持多样化视图,并且每一个视图都有自己相应的控制,所以每个区块都是封装好,可以复用到整个应用程序需要的地方。

嵌入式视图

常见的例子中,一个应用的嵌入式页面一般是主页的详情页面,更具体的说,就是列表的详情页面。许多应用程序,都有列表页面,点击其中一个列表元素,可以进入到列表的详情页面。更进一步说,你点击列表中一个行的连接,进入一个 可查看 详情页面或是一个 可编辑 的表单。

如下图所示:


嵌入式视图示意图

如果列表页面和详情页面是单独分开的(或者他们被Angujar.js回调),使用Angular.js的内置路由ngRoute 是非常容易完成的。然而,如果你想要保持列表不变,而详情页面出现在列表的右边或者下面,这样就变得非常具有挑战性了。

需要澄清的是,这样的要求可以使用ngRoute来完成。但是你需要让两个控制器(一个用于列表,一个用于显示和隐藏详情)共享一个视图。这样的结果不是理想的,因为我们想要列表和详情页面有各自的控制器和视图,并且职责单一(显示列表或者显示列表项目的详情)。封装这些用户接口模块到它们各自的视图,这样我们就有更多的“可组合UI”,允许我们将各个区块整合到一起,或者根据需求拆分。嵌入式视图,不仅能够让这些视图同时出现,还能让一个视图嵌入到另一个视图中。

历史

Angular.js首次发布ngRoute的时候,是有类似功能的路由存在的。这样路由包含在Backbone.js中,以及独立路由库History.jsSammy.js。总是,他们映射一个路由或者是URL所需要运行的JavaScript代码,当URL改变时,需要将其增加到浏览器的历史记录中,防止按回退按钮不会破坏路由。

最终获胜的JavaScriptMV框架,是想Ember.js*Durandal.js **这样,创建出更健壮的路由,以支持多样化视图和内嵌式视图,并且在内部使用“状态机”设计模式。

AngularJS官方回应称,从1.1.6版本将ngRoute从angular.js核心中删除(更多的说法是1.2)。ngRoute依然可以从AngularJS的官网上获得,但是它早已不在核心之中。

AngularJS的社区认为,更受欢迎的路由库是AngularUI 项目的UI-Router

AngularJS的几个顾问包括Rob Eisenberg(Durandal.js 和Aurelia的创建者),正在重写ngRoute,并声称最终将在某个时间点一直回来,预期的版本是Angular.js 2.0。(注1)

如果你想了解更详细的历史和各种路由的优缺点,你可以查看Angular.js 2.0的路由设计公开文档。(需翻墙)

https://docs.google.com/document/d/1I3UC0RrgCh9CKrLxeE4sxwmNSBl3oSXQGt9g3KZnTJI/edit#heading=h.xgjl2srtytjt

这个文档你可以点击右上方的绿色按钮,选择建议模式(suggesting)和查看模式(viewing),使页面更清晰。

建议模式
查看模式

安装

使用UI-Router,基于Angular.js 1.2.x或Angular.13.x,你可以通过以下一种方式获得其JavaScript源码:

下载

下载源码文件,或者混淆压缩后版本:

bower install

$ bower install angular-ui-router

npm install

$ npm install angular-ui-router

引入文件

引入angular-ui-router.jsangular-ui-router.min.js到你的index.html,必须Angualr.js核心文件之后,如下:

<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js">
</script><script src="js/angular-ui-router.min.js"></script>

(注2)

引入依赖

将“ui.router”依赖添加到你的主Angular.js module中。

var myApp = angular.module('myApp', ['ui.router']);

注意:是ui.router不是ui-router,后者是许多人经常犯的错误。

路由状态机

UI-Router 引进了状态机设计模式,抽象高于传统的路由。路由成了状态,URL就成了状态的一个简单属性。

var app = angular.module('demo', ['ui.router']);
 
app.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
    $urlRouterProvider.otherwise('/');
 
    $stateProvider
        .state('home', {
            url:'/',
            templateUrl: 'templates/home.html',
            controller: 'HomeController'
        })
        .state('about', {
            url:'/about',
            templateUrl: 'templates/about.html',
            controller: 'AboutController'
        })
 
}]);

当你想通过ui-sref创建一个链接是,使用的是状态而不是URL。

使用:
<a ui-sref="home">Home</a>

放弃:
<a href="#/">Home</a>

在上面的例子中,ui-sref可以这样理解:uiAngularUI项目所有指令的前缀,sref是包装了传统HTML锚点标签的href属性和状态判断。

控制器中使用

下面例子展示的是,如果在一个控制器中重定向一个状态。

$scope.redirectToAbout = function(){
    $state.go('about'); 
}

$stateProvider 替换 $routeProvider

当使用UI-Router时,为Angular.js服务注入路由支持,就由$routeProvider和 *** ngRoute变成了$stateProvider***。

$urlRouterProvider

$urlRouterProvider 在这里有两个主要目的。一是建立一个默认路由,用于管理未知的URL(统一跳转到某处)。

app.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
    $urlRouterProvider.otherwise('/');
    ...
}]);

二是监听浏览器地址栏URL的变化,重定向到路由定义的状态中。

app.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
    $urlRouterProvider
        .when('/legacy-route', {
            redirectTo: '/'
        });
}]);

总之,$urlRouterProvider让我们处理状态机抽象的$stateProvider没有检测到的情况。

现在你对UI-Router有了一个基本认识,UI-Router的这些特性,让它比ngRoute更好。

UI-Router实践

我们将先看到一个嵌入式视图的例子,然后在看到一个多样化视图的例子。之后,我们再重头看怎样将二者一起应用到一个现实世界的例子。

UI-Router嵌入式视图案例

UI-Router嵌入式视图的列表详情页面。这个例子显示的是一个电视节目的列表。

电视节目列表

如果你点击其中一行,你可以看到这行的详情描述。


节目详情

index.html

AngularJS的应用程序是单页应用程序,视图是插入到shell页中的。这里就是我们的shell页——index.html:

<!doctype html>
<html id="data-ng-app" data-ng-app="demo">
    <head>
        <meta charset="utf-8">
        <title>ui router demo</title>
        <style type="text/css">
            .selected{background-color: #efefef; width:120px; } 
            .detail{width: 300px;margin: 30px;border-top: 1px solid #efefef;}
        </style>
        <!-- IE8-HTML5: https://code.google.com/p/html5shiv/ -->
        <script src="js/libs/html5shiv.js"></script>
 
    </head>
    <body id="index">
 
        <!-- Angular UI Router Directive for template insertion -->
        <div id="content" ui-view></div>
 
        <script src="js/libs/angular.js"></script>
        <script src="js/libs/underscore.js"></script>
        <script src="js/libs/angular-ui-router.js"></script>        
        <script src="js/main.js"></script>      
    </body>
</html>

UI-Router 将第一级视图或是父视图(在例子中是shows.html)显示在<div id="content" ui-view></div>这个div之中。

主页视图(templates/shows.html)

shows.html是列表页面。

<ul>
    <li ui-sref-active="selected" ng-repeat="show in shows">
        <a ui-sref="shows.detail({id: show.id})">{{show.name}}</a>
    </li>
</ul>
 
<div class="detail" ui-view></div>

正如前面所提到的,index.html中有一个ui-view属性指令,当相应的路由被请求时,视图(shows.html)则会渲染在这个div中。

请注意,这里有另一个ui-view嵌入在shows.html中。这个ui-view代表的是一个当父视图已渲染之后,再出现的子视图。在这个例子中,是shows-detail.html。

显示详情视图(templates/shows-detail.html)

shows-detail.html是详情页面。

<h3>{{selectedShow.name}}</h3>
<p>
    {{selectedShow.description}}
</p>
</code>

控制器

下面是各个视图相应的控制器。

ShowsController

ShowsControllerShowsService加载一个内存中的数组显示。

app.controller('ShowsController', ['$scope','ShowsService', function($scope, ShowsService) {
    $scope.shows = ShowsService.list();
 }]);
ShowsDetailController

ShowsDetailControllerShowsService获取要显示项的id,并设置给$scope.selectedShow

app.controller('ShowsDetailController', ['$scope','$stateParams', 'ShowsService', function($scope, $stateParams, ShowsService) {
        $scope.selectedShow = ShowsService.find($stateParams.id);
 }]);

配置

我们需要使用$stateProvider配置UI-Router

当我们按照父状态名.子状态名的方式定义一个状态,UI-Router便知道子状态是内嵌在父状态中的。

app.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
    $urlRouterProvider.otherwise('/shows');
 
    $stateProvider
        .state('shows', {
            url:'/shows',
            templateUrl: 'templates/shows.html',
            controller: 'ShowsController'
        })
        .state('shows.detail', {
            url: '/detail/:id',
            templateUrl: 'templates/shows-detail.html',
            controller: 'ShowsDetailController'
        });
}]);

真正伟大的是,嵌入式视图是列表控制器实现列表和详情相关的部分,而详情控制器只负责显示详情。

演示如何解耦合,我们只需要更改如有配置,将嵌入式页面更改为独立的两个虚拟页(一个列表路由,一个详情路由)。更具体的说,就是将shows.detail更改为detail

$stateProvider
    .state('shows', {
        url:'/shows',
        templateUrl: 'templates/shows.html',
        controller: 'ShowsController'
    })
    .state('detail', {
        url: '/detail/:id',
        templateUrl: 'templates/shows-detail.html',
        controller: 'ShowsDetailController'
    });
    ...

并且将链接状态的地方由<a ui-sref="shows.detail({id: show.id})">{{show.name}}</a>,更改为<a ui-sref="detail({id: show.id})">{{show.name}}</a>

现在我们的例子,变成了连个独立的页面分别显示。

Service

ShowsService在这个例子中,使我们的数据访问层。它的职责就是保持一个数组在内存中,使用underscore.js(注3)非常容易实现这点。

app.factory('ShowsService',function(){
    var shows = [{
        id: 1,
        name: 'Walking Dead',
        description: 'The Walking Dead is an American post-apocalyptic horror drama television series developed by Frank Darabont. It is based on the comic book series of the same name by Robert Kirkman, Tony Moore, and Charlie Adlard. It stars Andrew Lincoln as sheriff\'s deputy Rick Grimes, who awakens from a coma to find a post-apocalyptic world dominated by flesh-eating zombies.'
    },
    {
        id: 2,
        name: 'Breaking Bad',
        description: 'Breaking Bad is an American crime drama television series created and produced by Vince Gilligan. The show originally aired on the AMC network for five seasons, from January 20, 2008 to September 29, 2013. The main character is Walter White (Bryan Cranston), a struggling high school chemistry teacher who is diagnosed with inoperable lung cancer at the beginning of the series.'   
    },
    {
        id: 3,
        name: '7D', 
        description: 'The 7D is an American animated television series produced by Disney Television Animation, and broadcast on Disney XD starting in July 7, 2014. It is a re-imagining of the titular characters from the 1937 film Snow White and the Seven Dwarfs by Walt Disney Productions'
    }];
 
 
    return {
        list: function(){
            return shows;
        },
        find: function(id){
            return _.find(shows, function(show){return show.id == id});
        }
    }
 });

UI-Router多样化视图案例

下面这个例子有多个区块在一个页面,有headercontentfooter。它们被UI-Router用多样化视图所管理。

多样化视图案例

这是一些主导航,和在这个应用中根据用户导航填充的各式各样的虚拟页。

子页

index.html

<!DOCTYPE html>
 <html class="no-js">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>Index</title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
 
 
    </head>
    <body ng-app="demo">
 
       <div ui-view="header"></div>
       <div ui-view="content"></div>
       <div ui-view="footer"></div>
 
    <script src="/js/bower_components/angular/angular.js"></script>
    <script src="/js/bower_components/angular-ui-router/release/angular-ui-router.js"></script>
    <script src="/js/main.js"></script>
 
    </body>
</html>

请注意,这里的ui-view属性指令都被赋予了一个名字:headercontentfooter。这些名字是当我们在配置路由,要指定view/template和控制器需要作用于页面上的那个区块时引用。

Templates/Views

这些模版是直接简单的例子。Header.html有一些导航。这些导航使用ui-sref指令导航到指定的状态。

partials/header.html
<div class="ul">
    <li><a ng-href="/">Home</a></li>    
    <li ui-sref-active="active"><a ui-sref="dashboard">Dashboard</a></li>
    <li ui-sref-active="active"><a ui-sref="campaigns">Campaigns</a></li>
</div>
partials/content.html
<p>This is the default content.</p>
partials/footer.html
<p>This is the footer.</p>
partials/dashboard.html
<h2>Dashboard</h2>
partials/campaigns.html
<h2>Campaigns</h2>

当相应的路由被请求时,将用相应的模版,如dashboard.htmlcampaigns.html替换掉content.html

配置

像上一个例子中那样,我们使用$stateProvider配置状态(路由)。

下面的关键是注意,一个url不是一个只有一个templateUrlcontroller属性了,而是使用views属性,赋予它一个各自的templateUrlcontroller的集合。

所以原来这样:

.state('home',{
        url: '/',
        templateUrl: '/templates/partials/header.html',
        controller: 'HomeController'
    })

就变成了这样:

.state('home',{
        url: '/',
        views: {
            'header': {
                templateUrl: '/templates/partials/header.html',
                controller: 'HeaderController'
            },
            'content': {
                templateUrl: '/templates/partials/content.html',
                controller: 'ContentController' 
            },
            'footer': {
                templateUrl: '/templates/partials/footer.html',
                controller: 'FooterController'
            }
        }
    })

下面是完整的代码。(请注意:为了保持完整性,我把不需要控制器的写在上面)

var app = angular.module('demo', ['ui.router']);
 
app.config(function($stateProvider, $urlRouterProvider){
 
    $urlRouterProvider.otherwise('/');
 
    $stateProvider
    .state('home',{
        url: '/',
        views: {
            'header': {
                templateUrl: '/templates/partials/header.html'
            },
            'content': {
                templateUrl: '/templates/partials/content.html' 
            },
            'footer': {
                templateUrl: '/templates/partials/footer.html'
            }
        }
    })
 
    .state('dashboard', {
        url: '/dashboard',
        views: {
            'header': {
                templateUrl: '/templates/partials/header.html'
            },
            'content': {
                templateUrl: 'templates/dashboard.html',
                controller: 'DashboardController'
            }
        }
 
    })
 
    .state('campaigns', {
        url: '/campaigns',
        views: {
            'content': {
                templateUrl: 'templates/campaigns.html',
                controller: 'CampaignController'
            },
            'footer': {
                templateUrl: '/templates/partials/footer.html'
            }
        }
 
    })
});

另外需要注意的是,如果我没有填充一个区块或试图,那么用户导航到该路由时,将不会显示。这是不理想的,并且违反DRY原则(注4)。所以接下来的部门,我们将看到如何使用内嵌式视图去除冗余。

UI-Router的内嵌式视图和多样化视图案例

现在我们了解了这些伟大的特性(内嵌式视图和多样化视图),让我们用这些特性一起应用到一个真实世界的应用程序中。

配置

因为视图的模板与多样化视图的例子相同,所用我们复用它的配置。

var app = angular.module('demo', ['ui.router']);
 
app.config(function($stateProvider, $urlRouterProvider){
 
    $urlRouterProvider.otherwise('/');
 
    $stateProvider
    .state('app',{
        url: '/',
        views: {
            'header': {
                templateUrl: '/templates/partials/header.html'
            },
            'content': {
                templateUrl: '/templates/partials/content.html' 
            },
            'footer': {
                templateUrl: '/templates/partials/footer.html'
            }
        }
    })
 
    .state('app.dashboard', {
        url: 'dashboard',
        views: {
            'content@': {
                templateUrl: 'templates/dashboard.html',
                controller: 'DashboardController'
            }
        }
 
    })
 
    .state('app.campaigns', {
        url: 'campaigns',
        views: {
            'content@': {
                templateUrl: 'templates/campaigns.html',
                controller: 'CampaignController'
            }
        }
 
    })
 
    .state('app.subscribers', {
        url: 'subscribers',
        views: {
            'content@': {
                templateUrl: 'templates/subscribers.html',
                controller: 'SubscriberController'      
            }
        }
 
    })
    .state('app.subscribers.detail', {
        url: '/:id',
        /*
        templateUrl: 'templates/partials/subscriber-detail.html',
        controller: 'SubscriberDetailController'
        */
 
        views: {
            'detail@app.subscribers': {
                templateUrl: 'templates/partials/subscriber-detail.html',
                controller: 'SubscriberDetailController'        
            }
        }
 
    });
 
});

我们在/路由上建立一个默认的状态app。在app路由上定义默认的内容区块,头部区块和尾部区块。然后,我们想在这个应用中定义被的路由,只需要在app后使用.的语法,如:app.campaigns。请注意,我们只需要替换内容区块(ui-view='content'),除非我们想要改变头部和尾部。因为这些视图都是定义在app路由之下的。

状态名

在上面的代码中,最难理解的概念是状态名中的@语法。状态名的这个语法可以做如下解释:

写一个状态名,需要回答两个问题:

  1. 当路由被请求时,我应该拿我的模板去替换那个区块?更具体的说,状态名就是ui-view属性指令的值。下面一个例子展示了ui-view属性指令和它对应的区块:
  • ui-view='content' = content
  • ui-view='header' = header
  • ui-view='footer' = footer
  1. 哪里可以找到ui-view所指向的区块?
  • ui-view使用的不是直接的templateUrl,而是包含该模板的状态
  • ui-view和视图区块包含在应用程序的壳模板(index.html)中时,因为index.html没有定义任何状态,你应该设置为空字符串或者不设置。

把这两个放在一起说,这个如同question1@question2的语法,更具体的说,其实是区块@状态名

所以,你需要在index.html页面上找内容区块时,你需要这样写:content@

  • 这之后的两个都是在@符后是空白的状态名,说明他们都是在index.html上的区块。

如果你要找到subscribers.html上的详情内容区块,你需要这样写:detail@app.subscribers

壳页面(index.html)

在上面的例子中,index.html并没有发生改变,只是简单地定义了各个区块:头部,尾部和内容。

Views/Templates

Header (partials/header.html)

Header的更新,是通过ui-sref引用嵌入的状态。如.campaigns不是调用campaigns状态,而是根据当前状态,推断出.campaigns的父状态。

<div class="ul">
    <li><a ng-href="/">Home</a></li>    
    <li ui-sref-active="active"><a ui-sref=".dashboard">Dashboard</a></li>
    <li ui-sref-active="active"><a ui-sref=".campaigns">Campaigns</a></li>
    <li ui-sref-active="active"><a ui-sref=".subscribers">Subscribers</a></li>
</div>

下面是一个新用户的模板的例子。

partials/subscribers.html
<h2>Subscribers</h2>
 
<ul>
    <li ng-repeat="subscriber in subscribers">
 
        <a ui-sref=".detail({id: subscriber.id})" > {{subscriber.name}}</a>
        {{subscriber.email}}
    </li>
</ul>
 
<div ui-view="detail"></div>
partials/subscriber-detail.html
{{selected.description}}

结论

掌握这样的状态名语法是有些困难的。但是有一个健壮的路由的好处是,允许你封装view/controller对组成你的用户界面。我觉得这样的困难是有价值的。所以,放心使用最后一个例子作为起点,构建一个神奇的,可维护的应用。让我知道你是否使用UI-Router,或者其他什么问题,请评论。

译者注

  • 注1: 2.0最近发布了,还没去看有没有回来
  • 注2: 由于国内网络的不可抗因素,不建议使用谷歌的cdn
  • 注3: Underscore.js 是一个javascript工具库,提供了一整套函数式编程支持。在angular中使用参考
  • 注4:DRY原则,Don't Repeat Yourself。不要重复自身,即降调代码是去冗余,同样的代码只写一次,不同地方调用。

原文地址:http://www.funnyant.com/angularjs-ui-router/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容

  • 【JS-6】 如何使用ui-router? 小课堂【武汉分院第137期】 分享人:徐恒 目录 1.背景介绍 2.知...
    爱上Shu的小刺猬阅读 1,911评论 2 5
  • 大家好,我是IT修真院深圳分院第04期学员,一枚正直善良的web程序员。 今天给大家分享一下,修真院官网 js任务...
    仿佛锋阅读 1,717评论 0 0
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,544评论 25 707
  • 一,名家推荐(美) 威廉•尼科尔斯世界图书出版公司 它以全球化的视野,将经济学和管理学的经典理论融为一体,将理论和...
    娇之语阅读 556评论 0 0
  • 我坐在位置上看你们的表演,你的专项就是跳舞。果然是让人激动,大哥和源是表演一场斗争。 大家都看到的是他们两个人的友...
    爱梦的我阅读 105评论 0 0