AngularJS作为经典前端框架,在企业级后台管理系统开发中应用广泛。实战开发的核心并非单纯堆砌技术,而是“以业务需求为导向,用合理技术解决实际问题”——从项目初始化搭建规范架构,到用户管理、数据表格、表单提交等核心功能落地,再到性能优化与上线部署,每一步都需兼顾“功能完整性、代码可维护性、用户体验”。本文以“电商后台管理系统”为案例,带您走完AngularJS项目开发全流程,掌握实战中的关键技术与避坑技巧。
一、实战准备:项目初始化与架构搭建
在写一行业务代码前,需先完成“环境配置、架构设计、基础封装”,这是保证项目长期可维护的前提。本案例基于AngularJS 1.8.x,结合Webpack 5构建工具,搭建标准化项目架构。
1. 环境配置与依赖安装
(1)初始化项目与安装核心依赖
# 1. 创建项目目录并初始化package.json
mkdir angularjs-ecommerce-admin && cd angularjs-ecommerce-admin
npm init -y
# 2. 安装核心依赖
# AngularJS核心库
npm install angular@1.8.3 --save
# 路由(替代原生路由,支持嵌套路由)
npm install angular-ui-router@1.0.30 --save
# HTTP请求拦截与辅助(可选,简化请求处理)
npm install angular-http-auth@1.5.3 --save
# UI组件库(可选,快速搭建界面,如AngularJS Material)
npm install angular-material@1.2.2 --save
# 3. 安装开发依赖(构建与调试)
# Webpack构建工具
npm install webpack@5.88.2 webpack-cli@5.1.4 webpack-dev-server@4.15.1 --save-dev
# Babel(处理ES6+语法兼容)
npm install @babel/core@7.22.9 @babel/preset-env@7.22.9 babel-loader@9.1.3 --save-dev
# HTML/CSS加载器
npm install html-loader@4.2.0 css-loader@6.8.1 style-loader@3.3.3 --save-dev
# 静态资源处理
npm install file-loader@6.2.0 url-loader@4.1.1 --save-dev
(2)Webpack配置(webpack.config.js)
核心配置目标:支持模块化开发、ES6+语法兼容、热更新调试、资源压缩。
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 需额外安装:npm i html-webpack-plugin@5.5.3 -D
module.exports = {
entry: './src/app/index.js', // 项目入口文件
output: {
path: path.resolve(__dirname, 'dist'), // 打包输出目录
filename: 'js/[name].[contenthash].js', // 输出JS文件名(带哈希,避免缓存)
clean: true // 每次打包清空dist目录
},
devtool: process.env.NODE_ENV === 'development' ? 'source-map' : 'hidden-source-map', // 开发环境生成source-map便于调试
devServer: {
port: 8080, // 开发服务器端口
open: true, // 自动打开浏览器
hot: true, // 热更新
proxy: {
// 接口代理(解决跨域问题)
'/api': {
target: 'http://localhost:3000', // 后端API地址
changeOrigin: true,
pathRewrite: { '^/api': '' } // 移除请求路径中的/api前缀
}
}
},
module: {
rules: [
// 处理JS文件:用Babel转换ES6+语法
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
},
// 处理CSS文件
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
// 处理HTML文件(作为模板引入)
{
test: /\.html$/,
use: 'html-loader'
},
// 处理图片等静态资源
{
test: /\.(png|jpg|jpeg|gif|svg)$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 8 * 1024 // 小于8KB的资源转base64,减少请求
}
},
generator: {
filename: 'assets/images/[name].[hash][ext]'
}
}
]
},
plugins: [
// 生成HTML入口文件,自动引入打包后的JS/CSS
new HtmlWebpackPlugin({
template: './src/index.html', // 模板HTML路径
filename: 'index.html',
minify: process.env.NODE_ENV === 'production' ? {
removeComments: true, // 移除注释
collapseWhitespace: true // 压缩空格
} : false
})
],
resolve: {
// 配置模块别名,简化引入路径
alias: {
'@': path.resolve(__dirname, 'src'),
'core': path.resolve(__dirname, 'src/core'),
zhiq.zhaopin.com/question/11490965
zhiq.zhaopin.com/question/11490966
zhiq.zhaopin.com/question/11490967
zhiq.zhaopin.com/question/11490970
zhiq.zhaopin.com/question/11490975
zhiq.zhaopin.com/question/11490976
zhiq.zhaopin.com/question/11490977
zhiq.zhaopin.com/question/11490981
zhiq.zhaopin.com/question/11490982
zhiq.zhaopin.com/question/11490983
zhiq.zhaopin.com/question/11490984
zhiq.zhaopin.com/question/11490985
zhiq.zhaopin.com/question/11490987
zhiq.zhaopin.com/question/11490989
zhiq.zhaopin.com/question/11490991
zhiq.zhaopin.com/question/11490993
zhiq.zhaopin.com/question/11490998
zhiq.zhaopin.com/question/11491000
zhiq.zhaopin.com/question/11491004
zhiq.zhaopin.com/question/11491005
'business': path.resolve(__dirname, 'src/business')
},
// 配置可省略的文件后缀
extensions: ['.js', '.html', '.css']
}
};
(3)入口文件与基础模板
入口JS(src/app/index.js):初始化AngularJS模块,依赖核心模块与路由。
import angular from 'angular';
import 'angular-ui-router';
import 'angular-material';
import 'angular-material/angular-material.css'; // 引入UI组件库样式
// 引入核心模块(后续实现)
import coreModule from '@/core/core.module';
// 引入业务模块(后续实现,如用户模块、商品模块)
import userModule from '@/business/user/user.module';
import productModule from '@/business/product/product.module';
// 初始化应用主模块
const appModule = angular.module('ecommerceAdmin', [
'ui.router', // 路由模块
'ngMaterial', // UI组件库
coreModule.name, // 核心模块
userModule.name, // 用户业务模块
productModule.name // 商品业务模块
]);
// 配置主路由(空白父路由,嵌套业务路由)
appModule.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
$stateProvider.state('app', {
url: '/app',
abstract: true, // 抽象路由,不可直接访问
template: '<div ui-view></div>' // 子路由视图占位符
});
// 默认跳转登录页(未登录时)
$urlRouterProvider.otherwise('/login');
}]);
// 应用启动钩子:初始化用户状态(如检查本地存储的登录信息)
appModule.run(['$rootScope', 'UserStateService', function($rootScope, UserStateService) {
UserStateService.init(); // 初始化用户状态(后续实现)
// 监听路由变化,判断是否需要登录
$rootScope.$on('$stateChangeStart', function(event, toState) {
const isLoginRequired = toState.data?.requireLogin ?? true; // 路由是否需要登录
const isLoggedIn = UserStateService.getState().isLoggedIn; // 用户是否已登录
if (isLoginRequired && !isLoggedIn) {
event.preventDefault(); // 阻止当前路由跳转
$rootScope.$state.go('login'); // 跳转到登录页
}
});
}]);
基础HTML模板(src/index.html):定义页面骨架,引入打包后的资源。
<!DOCTYPE html>
<html lang="zh-CN" ng-app="ecommerceAdmin">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>电商后台管理系统</title>
<!-- 引入第三方图标库(可选,如Font Awesome) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css">
</head>
<body>
<!-- 路由视图占位符:所有页面内容将渲染到这里 -->
<div ui-view></div>
</body>
</html>
二、核心功能实战:从登录到业务模块落地
后台管理系统的核心功能围绕“用户权限控制、数据管理、表单操作”展开。本节以“登录页”“用户列表页”“商品编辑页”三个高频场景为例,完整实现从前端交互到后端接口联调的全流程。
1. 场景1:用户登录(权限控制入口)
登录功能是后台系统的“第一道门”,需实现“表单验证、登录状态存储、路由拦截”三大核心逻辑。
(1)核心依赖封装:用户状态服务(src/core/services/UserStateService.js)
负责管理用户登录状态、token存储、权限判断,后续所有需登录的功能都依赖此服务。
import angular from 'angular';
export default angular.module('coreModule.userStateService', [])
.factory('UserStateService', ['$rootScope', '$http', '$localStorage', function($rootScope, $http, $localStorage) {
// 私有状态:存储用户信息、登录状态、token
let state = {
userInfo: $localStorage.userInfo || null, // 从本地存储恢复用户信息
token: $localStorage.token || null, // 从本地存储恢复token
isLoggedIn: !!$localStorage.token, // 是否已登录(根据token判断)
isLoading: false // 登录/登出请求加载状态
};
// 公有方法
const actions = {
// 初始化用户状态(应用启动时调用)
init: function() {
// 若有token,设置HTTP请求头(所有请求携带token)
if (state.token) {
$http.defaults.headers.common['Authorization'] = `Bearer ${state.token}`;
}
},
// 用户登录:参数为登录表单数据(username/password)
login: function(loginData) {
if (state.isLoading) return Promise.reject('正在登录中,请稍后');
state.isLoading = true;
return $http.post('/api/auth/login', loginData)
.then(response => {
const { userInfo, token } = response.data;
// 更新状态
state.userInfo = userInfo;
state.token = token;
state.isLoggedIn = true;
state.isLoading = false;
// 存储到本地存储(持久化,刷新页面不丢失)
$localStorage.userInfo = userInfo;
$localStorage.token = token;
// 设置HTTP请求头(后续请求自动携带token)
$http.defaults.headers.common['Authorization'] = `Bearer ${token}`;
// 触发登录成功事件(通知其他组件更新视图,如导航栏)
$rootScope.$broadcast('user:login:success', userInfo);
return userInfo;
})
.catch(error => {
state.isLoading = false;
// 统一处理登录错误(如账号密码错误、服务器异常)
const errorMsg = error.data?.errorMsg || '登录失败,请重试';
$rootScope.$broadcast('user:login:fail', errorMsg);
return Promise.reject(errorMsg);
});
},
// 用户登出:清除状态与本地存储
logout: function() {
if (state.isLoading) return Promise.reject('正在登出中,请稍后');
state.isLoading = true;
return $http.post('/api/auth/logout')
.finally(() => {
// 无论登出API是否成功,都清除状态
state.userInfo = null;
state.token = null;
state.isLoggedIn = false;
state.isLoading = false;
// 清除本地存储
delete $localStorage.userInfo;
delete $localStorage.token;
// 清除HTTP请求头
delete $http.defaults.headers.common['Authorization'];
// 触发登出成功事件
$rootScope.$broadcast('user:logout:success');
});
},
// 获取当前用户状态(对外提供只读副本,避免外部直接修改)
getState: function() {
return angular.copy(state);
}
};
return actions;
}])
.name; // 导出模块名,供核心模块依赖
(2)登录控制器与视图
登录控制器(src/business/auth/controllers/LoginCtrl.js):处理登录表单交互逻辑。
import angular from 'angular';
import loginTemplate from '../views/login.html'; // 引入登录视图模板
export default angular.module('authModule.loginCtrl', [])
.controller('LoginCtrl', ['$scope', '$state', 'UserStateService', function($scope, $state, UserStateService) {
// 登录表单数据初始化
$scope.loginForm = {
username: '',
password: ''
};
// 表单验证错误信息
$scope.errorMsg = '';
// 加载状态(控制按钮禁用与加载动画)
$scope.isLoading = false;
// 监听用户状态服务的加载状态
$scope.$watch(() => UserStateService.getState().isLoading, function(isLoading) {
$scope.isLoading = isLoading;
});
// 监听登录失败事件(来自UserStateService)
$scope.$on('user:login:fail', function(event, errorMsg) {
$scope.errorMsg = errorMsg;
});
// 登录提交方法
$scope.submitLogin = function() {
// 表单前端验证(避免空账号/密码提交)
if (!$scope.loginForm.username.trim()) {
$scope.errorMsg = '请输入用户名';
return;
}
if (!$scope.loginForm.password.trim()) {
$scope.errorMsg = '请输入密码';
return;
}
// 调用用户状态服务的登录方法
UserStateService.login($scope.loginForm)
.then(() => {
// 登录成功,跳转到首页
$state.go('app.dashboard');
})
.catch(errorMsg => {
$scope.errorMsg = errorMsg;
});
};
}])
// 配置登录路由
.config(['$stateProvider', function($stateProvider) {
$stateProvider.state('login', {
url: '/login',
template: loginTemplate,
controller: 'LoginCtrl',
data: {
requireLogin: false // 登录页不需要登录权限
}
});
}])
.name;
登录视图(src/business/auth/views/login.html):基于AngularJS Material实现简洁登录界面。
<div layout="row" layout-align="center center" style="height: 100vh; background: #f5f5f5;">
<md-card style="width: 400px; padding: 24px;">
<md-card-title>
<md-card-title-text>
<h2 class="md-display-1">电商后台登录</h2>
</md-card-title-text>
</md-card-title>
<md-card-content>
<!-- 登录表单 -->
<form name="loginForm" novalidate>
<!-- 用户名输入框 -->
<md-input-container class="md-block">
<label>用户名</label>
<input type="text" ng-model="loginForm.username" required>
<div ng-messages="loginForm.username.$error" ng-if="loginForm.username.$touched">
<div ng-message="required">请输入用户名</div>
</div>
</md-input-container>
<!-- 密码输入框 -->
<md-input-container class="md-block">
<label>密码</label>
<input type="password" ng-model="loginForm.password" required>
<div ng-messages="loginForm.password.$error" ng-if="loginForm.password.$touched">
<div ng-message="required">请输入密码</div>
</div>
</md-input-container>
<!-- 错误提示(如账号密码错误</doubaocanvas>