从0到1掌握Egg Vue Webpack Boilerplate:构建企业级服务端渲染应用的完整指南
引言:为什么选择Egg Vue Webpack Boilerplate?
你是否还在为前端渲染的SEO问题头疼?是否在寻找一个既能支持服务端渲染又能实现客户端渲染(CSR)的全能框架?Egg Vue Webpack Boilerplate正是为解决这些痛点而生的企业级解决方案。作为基于Egg.js、Vue.js和Webpack的整合方案,它提供了一站式的服务端渲染和前端渲染解决方案,让你能够轻松构建高性能、易维护的现代化Web应用。
读完本文,你将能够:
- 理解Egg Vue Webpack Boilerplate的核心架构与优势
- 快速搭建完整的开发环境
- 掌握服务端渲染与CSR两种渲染模式的实现方式
- 学会配置路由、控制器和服务
- 熟练使用Webpack进行项目构建与优化
- 部署生产环境并进行性能调优
项目概述:Egg Vue Webpack Boilerplate是什么?
Egg Vue Webpack Boilerplate是一个基于Egg.js、Vue.js和Webpack的企业级工程骨架项目,专注于服务端渲染和客户端渲染(CSR)的无缝整合。该项目由easy-team开发维护,提供了丰富的功能和灵活的配置选项,适用于构建各种规模的Web应用。
核心功能概览
| 功能 | 描述 | 应用场景 |
|---|---|---|
| 服务端渲染 | 在服务器端生成完整的HTML页面 | 博客、新闻站点等需要SEO的内容型网站 |
| 客户端渲染(CSR) | 在浏览器中动态生成页面内容 | 后台管理系统、交互密集型应用 |
| 单页面应用(SPA) | 基于Vue Router的前端路由系统 | 需要复杂状态管理的应用 |
| 多页面应用(MPA) | 传统的多页面架构 | 内容分散、页面间关联性低的网站 |
| 自动构建 | 基于Webpack的自动化构建流程 | 开发环境热更新、生产环境优化 |
| 国际化支持 | 内置i18n解决方案 | 多语言网站 |
项目架构
Egg Vue Webpack Boilerplate采用分层架构设计,清晰分离关注点,提高代码可维护性:
这种架构的优势在于:
- 前后端分离:前端代码和后端代码清晰分离,但又能无缝协作
- 服务端渲染:提升首屏加载速度和SEO表现
- 模块化开发:基于Egg.js的模块化设计,便于团队协作
- 灵活的渲染策略:根据页面需求选择最合适的渲染方式
环境搭建:从零开始配置开发环境
系统要求
在开始之前,请确保你的开发环境满足以下要求:
- Node.js: v12.0.0 或更高版本
- npm: v6.0.0 或更高版本
- Git: 用于版本控制和克隆仓库
快速开始
1. 克隆项目仓库
git clone https://gitcode.com/gh_mirrors/eg/egg-vue-webpack-boilerplate.git
cd egg-vue-webpack-boilerplate
2. 安装依赖
npm install
提示:如果你在中国境内,建议使用淘宝npm镜像以提高安装速度:
npm install --registry=https://registry.npmmirror.com
3. 启动开发服务器
npm run dev
启动成功后,你可以通过以下地址访问应用:
- 前台博客系统: http://localhost:7001
- 后台管理系统: http://localhost:7001/admin
项目结构详解
让我们深入了解项目的目录结构,这对于理解整个项目的组织方式至关重要:
egg-vue-webpack-boilerplate/
├── app/ # 应用代码目录
│ ├── controller/ # 控制器目录
│ ├── service/ # 服务目录
│ ├── model/ # 数据模型目录
│ ├── router.js # 路由配置
│ ├── middleware/ # 中间件目录
│ ├── extend/ # 框架扩展
│ ├── view/ # 视图模板目录
│ └── web/ # 前端资源目录
│ ├── page/ # 页面组件
│ ├── component/ # 通用组件
│ ├── asset/ # 静态资源
│ └── framework/ # 前端框架代码
├── config/ # 配置文件目录
├── test/ # 测试代码目录
├── webpack.config.js # Webpack配置
├── package.json # 项目依赖
└── README.md # 项目说明文档
这个结构遵循了Egg.js的"约定优于配置"原则,同时为Vue前端开发提供了清晰的组织方式。
核心概念:理解Egg Vue Webpack Boilerplate的关键
Egg.js框架基础
Egg.js是基于Koa.js的企业级Node.js框架,它提供了一套完整的Web开发解决方案。在Egg Vue Webpack Boilerplate中,Egg.js负责后端逻辑处理、路由分发和服务端渲染协调。
控制器(Controller)
控制器是处理用户请求的核心,负责接收请求、处理业务逻辑并返回响应。以下是一个简单的控制器示例:
// app/controller/blog/home.js
'use strict';
const egg = require('egg');
module.exports = class HomeController extends egg.Controller {
async ssr() {
// 调用服务层获取数据
const result = this.service.article.getArticleList();
// 使用服务端渲染模式渲染页面
await this.ctx.render('blog/home.js', result);
}
async csr() {
// 调用服务层获取数据
const result = this.service.article.getArticleList();
// 使用CSR模式渲染页面
await this.ctx.renderClient('blog/home.js', result);
}
};
服务(Service)
服务层封装了复杂的业务逻辑,提供可复用的功能单元。服务可以被多个控制器调用,实现代码复用。
中间件(Middleware)
中间件是处理HTTP请求的拦截器,可以在请求到达控制器之前或响应发送给客户端之前进行一些通用处理,如日志记录、身份验证等。
Vue.js前端架构
Vue.js作为前端框架,负责页面的视图层渲染和用户交互。在本项目中,Vue既可以在服务端渲染完整页面,也可以在客户端进行动态交互。
Webpack构建系统
Webpack是项目的构建工具,负责处理前端资源,包括JavaScript、CSS、图片等,并将它们打包成浏览器可识别的格式。项目中的webpack.config.js文件定义了构建规则和入口文件。
// webpack.config.js部分内容
module.exports = {
entry: {
'blog/home': 'app/web/page/blog/home/index.vue',
'blog/category': 'app/web/page/blog/category/category.vue',
'admin/home/home': 'app/web/page/admin/home/home.vue',
// 更多入口...
},
// 其他配置...
};
快速上手:构建你的第一个页面
创建控制器
首先,我们需要创建一个控制器来处理用户请求:
// app/controller/demo.js
'use strict';
module.exports = app => {
class DemoController extends app.Controller {
async index() {
const { ctx } = this;
// 获取数据
const data = {
title: '我的第一个页面',
content: '使用Egg Vue Webpack Boilerplate构建',
list: ['item1', 'item2', 'item3']
};
// 渲染页面
await ctx.render('demo/index.js', data);
}
}
return DemoController;
};
配置路由
接下来,在路由配置文件中添加新的路由规则:
// app/router.js
'use strict';
module.exports = app => {
const { router, controller } = app;
// 已有的路由...
router.get('/demo', controller.demo.index); // 添加这一行
};
创建Vue页面组件
然后,创建对应的Vue页面组件:
<!-- app/web/page/demo/index.vue -->
<template>
<div class="demo-page">
<h1>{{ title }}</h1>
<p>{{ content }}</p>
<ul>
<li v-for="(item, index) in list" :key="index">{{ item }}</li>
</ul>
</div>
</template>
<script>
export default {
// 在服务端渲染时执行
asyncData() {
// 这里可以获取初始数据
return {};
},
data() {
return {
title: '',
content: '',
list: []
};
}
};
</script>
<style scoped>
.demo-page {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
h1 {
color: #333;
}
ul {
list-style-type: none;
padding: 0;
}
li {
padding: 8px 0;
border-bottom: 1px solid #eee;
}
</style>
访问新页面
启动开发服务器后,访问http://localhost:7001/demo即可看到新创建的页面。
npm run dev
深入理解:服务端渲染与CSR渲染模式
服务端渲染
服务端渲染是指在服务器端完成Vue组件的渲染,生成完整的HTML页面,然后将其发送给客户端。这种方式的主要优势是:
- 更好的SEO:搜索引擎可以直接抓取完整的页面内容
- 更快的首屏加载:用户无需等待JavaScript加载完成即可看到页面内容
- 更好的性能表现:在低性能设备上也能提供较好的用户体验
在项目中,使用服务端渲染非常简单,只需在控制器中调用ctx.render方法:
async ssr() {
const result = this.service.article.getArticleList();
await this.ctx.render('blog/home.js', result); // 服务端渲染
}
客户端渲染(CSR)
客户端渲染是传统的SPA模式,服务器只返回一个空的HTML骨架,然后由客户端JavaScript动态生成页面内容。这种方式的优势是:
- 更好的交互体验:页面切换无需刷新,提供更流畅的用户体验
- 减轻服务器负担:渲染工作转移到客户端
- 更灵活的前端开发:可以充分利用前端生态系统
在项目中,使用CSR只需调用ctx.renderClient方法:
async csr() {
const result = this.service.article.getArticleList();
await this.ctx.renderClient('blog/home.js', result); // CSR渲染
}
如何选择合适的渲染模式?
| 因素 | 服务端渲染 | CSR |
|---|---|---|
| SEO需求 | 高 | 低 |
| 首屏加载速度 | 快 | 慢 |
| 交互复杂度 | 低 | 高 |
| 服务器负载 | 高 | 低 |
| 开发复杂度 | 高 | 低 |
建议使用场景:
- 服务端渲染:博客、新闻、文档等内容型页面
- CSR:后台管理系统、社交应用等交互密集型应用
路由配置:构建灵活的页面导航
Egg Vue Webpack Boilerplate的路由系统由两部分组成:Egg.js的后端路由和Vue Router的前端路由。
后端路由配置
后端路由主要负责请求分发和渲染模式选择,配置文件位于app/router.js:
// app/router.js示例
module.exports = app => {
const { router, controller } = app;
// 博客首页 - 服务端渲染模式
router.get('/', controller.blog.home.ssr);
router.get('/blog', controller.blog.home.ssr);
// 博客首页 - CSR模式
router.get('/blog/csr', controller.blog.home.csr);
// 文章列表API
router.get('/blog/list', controller.blog.home.list);
// 后台管理系统 - SPA应用
router.get('/admin(/.+)?', controller.admin.admin.home);
// 单页面应用
router.get('/spa(/.*)?', controller.spa.index.index);
};
路由配置的基本格式是:
router.METHOD(PATH, CONTROLLER_ACTION);
其中:
- METHOD:HTTP请求方法,如get、post、put等
- PATH:URL路径,可以包含参数和通配符
- CONTROLLER_ACTION:对应的控制器方法
前端路由配置
对于单页面应用(SPA),如后台管理系统,还需要配置Vue Router:
// app/web/page/admin/router.js示例
import Vue from 'vue';
import Router from 'vue-router';
import Home from './home/home.vue';
import Article from './article/article.vue';
import Category from './category/category.vue';
Vue.use(Router);
export default function createRouter() {
return new Router({
mode: 'history',
routes: [
{
path: '/admin',
redirect: '/admin/home'
},
{
path: '/admin/home',
component: Home
},
{
path: '/admin/article',
component: Article
},
{
path: '/admin/category',
component: Category
}
]
});
}
Webpack配置:优化你的构建流程
Webpack是项目的构建核心,负责处理各种前端资源。项目根目录下的webpack.config.js文件包含了所有构建相关的配置。
入口配置
entry选项定义了Webpack的构建入口点:
// webpack.config.js
module.exports = {
entry: {
'blog/home': 'app/web/page/blog/home/index.vue',
'blog/category': 'app/web/page/blog/category/category.vue',
'admin/home/home': 'app/web/page/admin/home/home.vue',
'admin/login/login': 'app/web/page/admin/login/login.vue',
// 单页面前端渲染
spa: 'app/web/page/spa/index.js',
// html前端渲染
'html/simple': 'app/web/page/html/simple.vue',
'html/spa': 'app/web/page/html/spa.js',
// asset前端渲染
'asset/simple': 'app/web/page/asset/simple.vue',
'asset/spa': 'app/web/page/asset/spa.js',
'test': 'app/web/page/test/test.vue'
},
// 其他配置...
};
每个入口对应一个页面或功能模块,Webpack会从这些入口文件开始,递归解析所有依赖,最终生成对应的输出文件。
插件配置
插件可以扩展Webpack的功能,项目中常用的插件包括:
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlWebpackHarddiskPlugin = require('html-webpack-harddisk-plugin');
module.exports = {
// ...其他配置
plugins: [
new HtmlWebpackPlugin({
alwaysWriteToDisk: true,
chunks: ['runtime','common', 'html/spa'],
filename: '../view/spa.tpl',
template: './app/web/view/layout.tpl'
}),
new HtmlWebpackPlugin({
alwaysWriteToDisk: true,
chunks: ['runtime','common', 'html/simple'],
filename: '../view/simple.tpl',
template: './app/web/view/layout.tpl'
}),
new HtmlWebpackHardiskPlugin({
outputPath: path.resolve(__dirname, 'app/view')
})
]
};
这些插件主要用于生成HTML模板文件,支持热更新和磁盘持久化。
开发与生产环境配置
项目通过不同的npm脚本区分开发和生产环境:
// package.json
{
"scripts": {
"dev": "egg-bin dev", // 开发环境
"build": "easy build prod", // 生产环境构建
"start": "egg-scripts start" // 启动生产环境服务器
}
}
开发环境(npm run dev)特点:
- 热模块替换(HMR):代码修改后无需刷新页面即可看到效果
- 详细的错误信息:便于调试
- 未压缩的代码:保留原始代码格式
生产环境(npm run build && npm run start)特点:
- 代码压缩与混淆:减小文件体积,提高安全性
- 静态资源优化:图片压缩、CSS提取等
- 性能优化:代码分割、懒加载等
状态管理:使用Vuex管理应用状态
对于复杂的应用,尤其是单页面应用,状态管理变得尤为重要。Egg Vue Webpack Boilerplate集成了Vuex,提供集中式的状态管理方案。
创建Vuex Store
// app/web/page/admin/store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
import article from './modules/article';
import category from './modules/category';
Vue.use(Vuex);
export default function createStore() {
return new Vuex.Store({
modules: {
article,
category
},
strict: process.env.NODE_ENV !== 'production'
});
}
定义模块
// app/web/page/admin/store/modules/article.js
const state = {
list: [],
current: null,
loading: false,
error: null
};
const mutations = {
setList(state, list) {
state.list = list;
},
setCurrent(state, article) {
state.current = article;
},
setLoading(state, loading) {
state.loading = loading;
},
setError(state, error) {
state.error = error;
}
};
const actions = {
async fetchList({ commit }, params) {
commit('setLoading', true);
try {
const response = await axios.get('/admin/api/article/list', { params });
commit('setList', response.data);
commit('setError', null);
} catch (err) {
commit('setError', err.message);
} finally {
commit('setLoading', false);
}
},
// 其他action...
};
export default {
namespaced: true,
state,
mutations,
actions
};
在组件中使用
<template>
<div class="article-list">
<loading v-if="loading" />
<error-message v-if="error" :message="error" />
<div v-else>
<article-item v-for="item in list" :key="item.id" :article="item" />
</div>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex';
export default {
computed: {
...mapState('article', ['list', 'loading', 'error'])
},
methods: {
...mapActions('article', ['fetchList'])
},
mounted() {
this.fetchList({ page: 1, limit: 10 });
}
};
</script>
服务层设计:构建健壮的后端服务
服务层是Egg.js应用的核心,负责实现业务逻辑和数据处理。良好的服务设计可以提高代码复用性和可维护性。
创建服务
// app/service/article.js
'use strict';
const egg = require('egg');
module.exports = class ArticleService extends egg.Service {
constructor(ctx) {
super(ctx);
this.database = this.ctx.app.db.collection('articles');
}
/**
* 获取文章列表
* @param {Object} params - 查询参数
* @param {number} params.page - 页码
* @param {number} params.limit - 每页数量
* @returns {Promise<Array>} 文章列表
*/
async getArticleList(params = {}) {
const { page = 1, limit = 10 } = params;
const skip = (page - 1) * limit;
try {
const total = await this.database.countDocuments();
const articles = await this.database.find()
.sort({ createdAt: -1 })
.skip(skip)
.limit(limit)
.toArray();
return {
list: articles,
pagination: {
total,
page,
limit,
pages: Math.ceil(total / limit)
}
};
} catch (error) {
this.ctx.logger.error('获取文章列表失败:', error);
throw new Error('获取文章列表失败');
}
}
/**
* 获取文章详情
* @param {string} id - 文章ID
* @returns {Promise<Object>} 文章详情
*/
async getArticleDetail(id) {
if (!id) {
throw new Error('文章ID不能为空');
}
try {
const article = await this.database.findOne({ _id: id });
if (!article) {
throw new Error('文章不存在');
}
return article;
} catch (error) {
this.ctx.logger.error(`获取文章${id}详情失败:`, error);
throw new Error('获取文章详情失败');
}
}
// 其他服务方法...
};
在控制器中使用服务
// app/controller/blog/home.js
async ssr() {
try {
// 调用服务层获取数据
const result = await this.service.article.getArticleList(this.ctx.query);
// 渲染页面
await this.ctx.render('blog/home.js', result);
} catch (error) {
// 错误处理
this.ctx.status = 500;
await this.ctx.render('error', { message: error.message });
}
}
部署与优化:将应用推向生产环境
构建生产版本
在部署到生产环境之前,需要先构建优化后的生产版本:
npm run build
这个命令会执行以下操作:
- 清除之前的构建产物
- 使用Webpack打包前端资源,包括代码压缩、Tree Shaking等优化
- 生成适合生产环境的静态资源
启动生产服务器
构建完成后,可以启动生产环境服务器:
npm run start
或者使用后台模式启动:
npm run backend
性能优化策略
1. 代码分割
Webpack支持自动代码分割,将代码分割为多个小块,实现按需加载:
// 在路由配置中使用动态import
const Article = () => import('./article/article.vue');
export default new Router({
routes: [
{
path: '/admin/article',
component: Article // 自动代码分割
}
]
});
2. 静态资源优化
- 使用CDN加速静态资源访问
- 图片压缩和懒加载
- 使用适当的缓存策略
3. 服务器优化
- 使用PM2等进程管理工具
- 配置适当的工作进程数
- 启用Gzip压缩
// config/config.prod.js
module.exports = app => {
const config = {};
// 配置工作进程数
config.cluster = {
listen: {
port: 7001,
workers: 4, // 根据CPU核心数调整
}
};
// 启用Gzip压缩
config.middleware = ['compression'];
config.compression = {
threshold: 1024 // 大于1KB的响应才压缩
};
return config;
};
实战案例:构建一个完整的博客系统
现在,让我们综合运用前面所学的知识,构建一个简单但功能完整的博客系统。
功能规划
我们的博客系统将包含以下功能:
- 文章列表展示
- 文章详情查看
- 分类浏览
- 后台管理界面
- 文章发布与编辑
数据模型设计
首先,设计文章的数据模型:
// app/model/article.js
module.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;
const articleSchema = new Schema({
title: { type: String, required: true },
content: { type: String, required: true },
category: { type: Schema.Types.ObjectId, ref: 'Category' },
author: { type: Schema.Types.ObjectId, ref: 'User' },
summary: { type: String },
coverImage: { type: String },
tags: [{ type: String }],
status: { type: String, enum: ['draft', 'published', 'archived'], default: 'draft' },
viewCount: { type: Number, default: 0 },
commentCount: { type: Number, default: 0 },
createdAt: { type: Date, default: Date.now },
updatedAt: { type: Date, default: Date.now }
});
// 更新时自动更新updatedAt字段
articleSchema.pre('save', function(next) {
if (this.isModified()) {
this.updatedAt = Date.now();
}
next();
});
return mongoose.model('Article', articleSchema);
};
服务实现
// app/service/blog.js
'use strict';
module.exports = app => {
class BlogService extends app.Service {
// 获取博客首页文章列表
async getHomeArticles(page = 1, limit = 10) {
return this.ctx.service.article.getArticleList({
page,
limit,
status: 'published'
});
}
// 获取分类文章
async getArticlesByCategory(categoryId, page = 1, limit = 10) {
return this.ctx.service.article.getArticleList({
page,
limit,
status: 'published',
category: categoryId
});
}
// 搜索文章
async searchArticles(keyword, page = 1, limit = 10) {
return this.ctx.service.article.getArticleList({
page,
limit,
status: 'published',
search: keyword
});
}
}
return BlogService;
};
控制器实现
// app/controller/blog/home.js
async index() {
const { ctx } = this;
const { page = 1, category } = ctx.query;
try {
let result;
if (category) {
// 获取分类文章
result = await this.service.blog.getArticlesByCategory(category, page);
} else {
// 获取首页文章
result = await this.service.blog.getHomeArticles(page);
}
// 获取所有分类
const categories = await this.service.category.getAllCategories();
// 渲染页面
await ctx.render('blog/home.js', {
...result,
categories,
currentCategory: category
});
} catch (error) {
ctx.logger.error('博客首页加载失败:', error);
ctx.status = 500;
await ctx.render('error', { message: '加载文章失败,请稍后重试' });
}
}
前端页面实现
<!-- app/web/page/blog/home/index.vue -->
<template>
<div class="blog-home">
<div class="container">
<div class="row">
<!-- 主内容区 -->
<div class="col-md-8">
<div class="articles">
<article-item v-for="article in list" :key="article._id" :article="article" />
</div>
<!-- 分页 -->
<pagination
:current="pagination.page"
:total="pagination.total"
:limit="pagination.limit"
@change="handlePageChange"
/>
</div>
<!-- 侧边栏 -->
<div class="col-md-4">
<category-nav
:categories="categories"
:current="currentCategory"
@change="handleCategoryChange"
/>
<search-box @search="handleSearch" />
<popular-articles />
</div>
</div>
</div>
</div>
</template>
<script>
import ArticleItem from '../../../component/article/Item.vue';
import Pagination from '../../../component/common/Pagination.vue';
import CategoryNav from '../../../component/blog/CategoryNav.vue';
import SearchBox from '../../../component/blog/SearchBox.vue';
import PopularArticles from '../../../component/blog/PopularArticles.vue';
export default {
components: {
ArticleItem,
Pagination,
CategoryNav,
SearchBox,
PopularArticles
},
data() {
return {
list: [],
pagination: {
page: 1,
limit: 10,
total: 0,
pages: 0
},
categories: [],
currentCategory: ''
};
},
methods: {
handlePageChange(page) {
// 处理分页变化
this.$router.push({
path: this.$route.path,
query: { ...this.$route.query, page }
});
},
handleCategoryChange(category) {
// 处理分类变化
this.$router.push({
path: this.$route.path,
query: { ...this.$route.query, category, page: 1 }
});
},
handleSearch(keyword) {
// 处理搜索
this.$router.push({
path: '/search',
query: { keyword, page: 1 }
});
}
}
};
</script>
<style scoped>
.blog-home {
padding: 20px 0;
}
.articles {
margin-bottom: 30px;
}
</style>
总结与展望
Egg Vue Webpack Boilerplate为构建现代化Web应用提供了强大的基础架构,它整合了Egg.js的后端能力和Vue.js的前端生态,同时通过Webpack实现了高效的构建流程。本文详细介绍了从环境搭建到项目部署的全过程,希望能帮助你快速掌握这个强大的开发框架。
核心要点回顾
- 双渲染模式:灵活选择服务端渲染和CSR,平衡SEO和用户体验
- 分层架构:清晰分离控制器、服务、模型等关注点
- 路由系统:结合Egg.js后端路由和Vue Router前端路由
- Webpack构建:优化资源处理和打包流程
- 状态管理:使用Vuex管理复杂应用状态
- 服务设计:封装业务逻辑,提高代码复用性
进阶学习路径
- 深入Egg.js生态:学习中间件开发、插件编写等高级特性
- Vue性能优化:掌握组件优化、虚拟列表等技术
- 微服务架构:将应用拆分为更小的微服务
- CI/CD流程:构建自动化测试和部署流程
- 监控与日志:实现完善的应用监控和日志分析
Egg Vue Webpack Boilerplate是一个持续发展的项目,随着Web技术的不断进步,它也在不断更新和完善。建议定期查看项目文档和更新日志,以获取最新的功能和最佳实践。
最后,鼓励你动手实践,通过实际项目来巩固所学知识。只有在实践中,你才能真正理解和掌握这个强大框架的精髓。
祝你开发顺利,构建出优秀的Web应用!
附录:常用命令参考
| 命令 | 描述 |
|---|---|
npm run dev | 启动开发服务器,支持热更新 |
npm run build | 构建生产环境资源 |
npm run start | 启动生产环境服务器 |
npm run backend | 后台启动生产环境服务器 |
npm run stop | 停止生产环境服务器 |
npm run test | 运行测试用例 |
npm run lint | 代码风格检查 |
npm run fix | 自动修复代码风格问题 |
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



