AngularJS仪表盘路由实例:模块化设计
你是否在开发AngularJS应用时遇到过路由混乱、视图嵌套复杂的问题?本文将通过一个仪表盘实例,展示如何使用ui-router实现模块化路由设计,让你的应用结构更清晰、维护更简单。读完本文,你将掌握:如何设计嵌套路由结构、使用命名视图实现复杂布局、通过resolve解决数据依赖,以及如何优化路由性能。
为什么选择ui-router?
AngularJS自带的ngRoute模块仅支持平面路由,无法满足复杂应用的需求。而ui-router作为AngularJS生态中事实上的路由解决方案(The de-facto solution to flexible routing with nested views in AngularJS),提供了更强大的功能:
- 支持嵌套视图(Nested Views)
- 允许命名视图(Named Views)
- 提供状态机管理(State Management)
- 支持复杂的路由参数和解析(Resolve)
项目核心的状态管理功能由src/stateProvider.ts实现,而视图渲染则通过src/directives/viewDirective.ts中的ui-view指令完成。
仪表盘应用路由设计
我们将构建一个包含以下功能的仪表盘应用:
- 顶部导航栏(固定)
- 侧边菜单(可折叠)
- 主内容区(根据路由动态变化)
- 数据统计卡片(复用组件)
路由结构设计
使用mermaid展示我们的路由结构:
这个结构中,dashboard是抽象状态(abstract),作为其他子状态的父状态,负责布局框架的渲染。
实现步骤
1. 引入ui-router
首先,确保在你的项目中引入ui-router。推荐使用国内CDN:
<script src="https://cdn.bootcdn.net/ajax/libs/angular.js/1.7.9/angular.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/angular-ui-router/1.0.29/angular-ui-router.min.js"></script>
2. 定义主模块
angular.module('dashboardApp', ['ui.router'])
3. 配置路由状态
使用$stateProvider配置应用状态,代码组织如下:
angular.module('dashboardApp')
.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
// 默认路由
$urlRouterProvider.otherwise('/dashboard/home');
// 定义路由状态
$stateProvider
// 抽象状态 - 仪表盘布局
.state('dashboard', {
abstract: true,
url: '/dashboard',
templateUrl: 'templates/dashboard.html',
controller: 'DashboardController',
controllerAs: 'vm'
})
// 首页
.state('dashboard.home', {
url: '/home',
views: {
'content': {
templateUrl: 'templates/dashboard-home.html',
controller: 'HomeController',
controllerAs: 'home'
}
}
})
// 统计数据页面
.state('dashboard.stats', {
url: '/stats',
views: {
'content': {
templateUrl: 'templates/dashboard-stats.html',
controller: 'StatsController',
controllerAs: 'stats',
resolve: {
// 预加载统计数据
statsData: ['StatsService', function(StatsService) {
return StatsService.getDashboardData();
}]
}
}
}
})
// 销售统计子页面
.state('dashboard.stats.sales', {
url: '/sales',
views: {
'stats-content': {
templateUrl: 'templates/stats-sales.html',
controller: 'SalesStatsController',
controllerAs: 'sales'
}
}
});
}]);
在上面的代码中,我们使用了抽象状态dashboard作为布局容器,它的模板文件templates/dashboard.html包含了整个应用的框架结构。
4. 创建布局模板
templates/dashboard.html内容如下:
<div class="dashboard-container">
<!-- 顶部导航栏 -->
<header class="dashboard-header">
<h1>企业数据仪表盘</h1>
<nav class="main-nav">
<!-- 导航链接 -->
<a ui-sref-active="active" ui-sref="dashboard.home">首页</a>
<a ui-sref-active="active" ui-sref="dashboard.stats">统计</a>
<a ui-sref-active="active" ui-sref="dashboard.settings">设置</a>
</nav>
</header>
<div class="dashboard-body">
<!-- 侧边栏 -->
<aside class="dashboard-sidebar">
<ul class="sidebar-menu">
<li ng-repeat="item in vm.menuItems">
<a ui-sref="{{item.state}}">{{item.label}}</a>
</li>
</ul>
</aside>
<!-- 主内容区 - 命名视图 -->
<main class="dashboard-content">
<ui-view name="content"></ui-view>
</main>
</div>
<!-- 页脚 -->
<footer class="dashboard-footer">
© 2025 企业数据平台 - 版权所有
</footer>
</div>
这里的<ui-view name="content"></ui-view>就是我们定义的命名视图,对应状态配置中的views: { 'content': { ... } }部分。
5. 实现控制器
DashboardController负责管理整个仪表盘的共享状态,如侧边栏折叠/展开:
angular.module('dashboardApp')
.controller('DashboardController', ['$scope', function($scope) {
const vm = this;
// 侧边栏状态
vm.sidebarCollapsed = false;
// 切换侧边栏
vm.toggleSidebar = function() {
vm.sidebarCollapsed = !vm.sidebarCollapsed;
};
// 菜单数据
vm.menuItems = [
{ label: '首页', state: 'dashboard.home', icon: 'home' },
{ label: '统计数据', state: 'dashboard.stats', icon: 'bar-chart' },
{ label: '设置', state: 'dashboard.settings', icon: 'settings' },
{ label: '个人资料', state: 'dashboard.profile', icon: 'user' }
];
}]);
6. 子状态实现 - 统计数据页面
统计数据页面需要展示多个数据卡片,我们使用命名视图实现复杂布局:
// 销售统计子页面
.state('dashboard.stats.sales', {
url: '/sales',
views: {
'stats-content': {
templateUrl: 'templates/stats-sales.html',
controller: 'SalesStatsController',
controllerAs: 'sales',
resolve: {
salesData: ['StatsService', function(StatsService) {
return StatsService.getSalesData();
}]
}
},
'chart@sales': {
templateUrl: 'templates/components/sales-chart.html',
controller: 'SalesChartController',
controllerAs: 'chart'
},
'summary@sales': {
templateUrl: 'templates/components/sales-summary.html',
controller: 'SalesSummaryController',
controllerAs: 'summary'
}
}
});
对应的模板文件中使用多个命名视图:
<div class="stats-container">
<div class="row">
<div class="col-md-8">
<ui-view name="chart"></ui-view>
</div>
<div class="col-md-4">
<ui-view name="summary"></ui-view>
</div>
</div>
<div class="row">
<ui-view name="stats-content"></ui-view>
</div>
</div>
高级功能实现
1. 使用Resolve预加载数据
在src/stateProvider.ts中定义的resolve功能允许我们在控制器实例化之前预加载数据:
.state('dashboard.stats', {
url: '/stats',
views: {
'content': {
templateUrl: 'templates/dashboard-stats.html',
controller: 'StatsController',
controllerAs: 'stats',
resolve: {
// 预加载统计数据
statsData: ['StatsService', function(StatsService) {
return StatsService.getDashboardData();
}],
// 可以有多个resolve
userPermissions: ['AuthService', function(AuthService) {
return AuthService.getPermissions();
}]
}
}
}
});
然后在控制器中直接使用这些预加载的数据:
.controller('StatsController', ['statsData', 'userPermissions', function(statsData, userPermissions) {
const vm = this;
vm.stats = statsData;
vm.permissions = userPermissions;
}]);
2. 状态切换动画
利用AngularJS的动画系统和ui-router的状态切换事件,实现视图切换动画:
/* 添加过渡动画样式 */
.view-container {
position: relative;
}
.view-frame {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.view-enter, .view-leave {
transition: all 0.3s ease;
}
.view-enter {
opacity: 0;
transform: translate3d(100%, 0, 0);
}
.view-enter-active {
opacity: 1;
transform: translate3d(0, 0, 0);
}
.view-leave {
opacity: 1;
transform: translate3d(0, 0, 0);
}
.view-leave-active {
opacity: 0;
transform: translate3d(-100%, 0, 0);
}
在应用中配置动画:
angular.module('dashboardApp')
.animation('.view-frame', ['$animateCss', function($animateCss) {
return {
enter: function(element, done) {
return $animateCss(element, {
from: { opacity: 0, transform: 'translate3d(100%, 0, 0)' },
to: { opacity: 1, transform: 'translate3d(0, 0, 0)' },
duration: 0.3
}).start().done(done);
},
leave: function(element, done) {
return $animateCss(element, {
from: { opacity: 1, transform: 'translate3d(0, 0, 0)' },
to: { opacity: 0, transform: 'translate3d(-100%, 0, 0)' },
duration: 0.3
}).start().done(done);
}
};
}]);
3. 动态参数与状态监听
监听路由参数变化,实现数据的动态更新:
.controller('SalesStatsController', ['$scope', '$stateParams', 'SalesService', function($scope, $stateParams, SalesService) {
const vm = this;
// 加载销售数据
function loadSalesData() {
SalesService.getSalesByPeriod($stateParams.period || 'week')
.then(function(data) {
vm.salesData = data;
});
}
// 初始加载
loadSalesData();
// 监听参数变化
$scope.$on('$stateParamsChange', function(event, params) {
if (params.period) {
loadSalesData();
}
});
}]);
性能优化建议
-
使用抽象状态共享布局:避免重复定义相同的布局结构。
-
合理使用resolve:只在必要时使用resolve,避免过多的数据预加载影响页面加载速度。
-
状态嵌套优化:不要创建过深的状态嵌套,建议最多3层。
-
利用onEnter/onExit:在src/stateProvider.ts中定义的
onEnter和onExit回调可以处理状态进入和退出时的逻辑,如资源加载和清理。
.state('dashboard.stats', {
url: '/stats',
views: { /* ... */ },
onEnter: ['$modal', function($modal) {
// 进入状态时打开模态框
$modal.open({ /* ... */ });
}],
onExit: ['$modalStack', function($modalStack) {
// 退出状态时关闭所有模态框
$modalStack.dismissAll();
}]
});
- 使用懒加载:对于大型应用,可以考虑使用ocLazyLoad等库实现控制器和模板的懒加载。
总结
通过本文的实例,我们展示了如何使用ui-router构建一个模块化的AngularJS仪表盘应用。关键要点包括:
- 使用抽象状态构建布局框架
- 利用命名视图实现复杂布局
- 通过resolve预加载数据
- 状态嵌套和参数管理
- 状态切换动画和过渡效果
这种模块化的路由设计不仅使代码结构更清晰,也提高了代码的复用性和可维护性。通过合理规划状态结构,你可以构建出更复杂、更强大的AngularJS应用。
项目的完整代码结构可以在src/目录下查看,更多高级用法请参考官方文档和源代码注释。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



