【前后端分离实战】:揭秘SpringBoot与Vue.js的完美结合
发布时间: 2024-11-16 11:04:50 阅读量: 3 订阅数: 3
![SpringBoot与Vue地方美食分享网站](https://zuminternet.github.io/images/portal/post/2018-08-23-ZUM-Pilot-partjun/development.jpg)
# 1. 前后端分离的概念与优势
## 1.1 前后端分离基础
在传统的Web开发模式中,前端和后端紧密耦合,前端的页面中通常会嵌入大量后端的代码,导致维护困难,代码重用性差。随着技术的发展,前后端分离的架构逐渐流行起来。这种架构将前端和后端的应用解耦,前端负责页面展示与用户交互,后端负责数据处理与业务逻辑。前后端通过API接口进行通信,各自独立开发和部署。
## 1.2 前后端分离的优势
前后端分离主要有以下几个优势:
- **开发效率提升**:前后端分离后,前端开发者可以专注于用户界面和交互,后端开发者可以专注于业务逻辑和数据处理,大幅度提高开发效率。
- **团队协作简化**:不同团队可以并行工作,只需要对接好API接口即可,大大减少了沟通成本。
- **部署灵活**:前后端分离的应用可以单独部署前端和后端,也可以部署在不同的服务器上。
- **前后端解耦**:使得前后端可以在不同的平台上运行,如前端可以运行在手机、平板等设备上,后端则运行在服务器上。
前后端分离不仅仅是技术层面的分离,更多的是一种开发模式的改变,这种模式能够更好地应对现在快速变化的市场需求。在下一章节中,我们将深入探讨如何搭建SpringBoot后端环境,这是实现前后端分离后端开发的重要一步。
# 2. 搭建SpringBoot后端环境
## 2.1 SpringBoot基础理论
### 2.1.1 SpringBoot的核心特性
SpringBoot作为一个开源Java框架,自发布以来就以简单易用、快速开发著称,它极大地简化了基于Spring的应用开发过程。SpringBoot核心特性包括:
- **独立运行**:SpringBoot应用可以被打包成一个独立的Jar文件,通过`java -jar`命令就可以运行,非常适合微服务架构。
- **自动配置**:SpringBoot会根据类路径中的jar依赖,自动配置Spring应用。
- **起步依赖**:简化Maven或Gradle配置,通过引入一个特定的“starter”依赖,能够快速引入需要的依赖项。
- **内嵌服务器**:支持Tomcat、Jetty和Undertow等内嵌服务器,无需部署WAR包。
- **生产准备就绪**:提供如指标、健康检查和外部化配置等功能,为生产环境做好准备。
### 2.1.2 与Spring框架的关系和区别
SpringBoot并非一个全新的框架,它是在Spring框架的基础上构建的,其目的是简化Spring应用的初始搭建以及开发过程。其与Spring框架的关系和区别主要体现在:
- **开发速度**:SpringBoot提供了许多默认配置,从而加快了开发速度,而Spring则需要用户手动配置许多中间件。
- **兼容性**:SpringBoot兼容最新的Spring框架,保持了向后兼容性。
- **独立性**:SpringBoot应用可以独立运行,无需外部依赖。传统的Spring应用需要依赖于一个外部的Servlet容器。
## 2.2 构建RESTful API
### 2.2.1 RESTful设计原则
RESTful是一种软件架构风格,它为Web服务的设计提供了一组指导原则。RESTful API的设计原则包括:
- **客户端-服务器分离**:保持用户界面和数据处理的分离。
- **无状态通信**:每个请求应包含所有必要的信息,不需要维护会话状态。
- **统一接口**:客户端与服务器的交互都基于同一接口,使得系统更具有可预测性。
- **可缓存**:响应应当被标记为可缓存或不可缓存,以优化网络性能。
- **分层系统**:通过使用中间层来增加系统的可扩展性。
### 2.2.2 实现一个简单的CRUD API
创建一个基于RESTful原则的CRUD(创建、读取、更新、删除)API的基本步骤如下:
1. 创建SpringBoot项目:使用Spring Initializr创建一个新的SpringBoot项目,添加Web依赖。
2. 实体类创建:定义一个实体类`User`,用于表示用户信息。
```java
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String email;
// 省略getter和setter方法
}
```
3. 创建资源控制器:创建一个`UserController`类,通过`@RestController`注解表示它是一个REST控制器。
```java
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserRepository userRepository; // 注入用户仓库接口
// 创建用户
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User savedUser = userRepository.save(user);
return new ResponseEntity<>(savedUser, HttpStatus.CREATED);
}
// 获取用户列表
@GetMapping
public List<User> getUsers() {
return userRepository.findAll();
}
// 更新用户
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User userDetails) {
User user = userRepository.findById(id).orElseThrow(() -> new RuntimeException("User not found"));
user.setName(userDetails.getName());
user.setEmail(userDetails.getEmail());
User updatedUser = userRepository.save(user);
return ResponseEntity.ok(updatedUser);
}
// 删除用户
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) {
userRepository.deleteById(id);
}
}
```
4. 配置数据库:在`application.properties`中配置数据库连接。
```
spring.datasource.url=jdbc:mysql://localhost:3306/your_database
spring.datasource.username=your_username
spring.datasource.password=your_password
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
```
5. 启动类:确保启动类`Application`上有`@EnableJpaRepositories`和`@EntityScan`注解。
## 2.3 SpringBoot的数据持久化
### 2.3.1 集成JPA/Hibernate
Spring Data JPA是Spring基于ORM框架、JPA规范的基础上封装的一套JPA应用框架,让开发者能够以更加简化的代码实现对数据库的访问和操作。集成JPA/Hibernate的基本步骤如下:
1. 添加依赖:在项目的`pom.xml`文件中添加Spring Data JPA和数据库驱动的依赖。
```xml
<dependencies>
<!-- Spring Data JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- 数据库驱动,这里以MySQL为例 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
```
2. 配置数据库连接:在`application.properties`或`application.yml`中配置数据库连接信息。
3. 实体映射:定义实体类,并使用`@Entity`注解进行实体映射。
4. 仓库接口:定义继承`JpaRepository`的接口,Spring Data JPA会自动实现该接口。
### 2.3.2 数据库操作和事务管理
在SpringBoot中,数据库操作和事务管理是通过声明式的方式进行的,简化了代码编写过程。通过注解`@Transactional`可以轻松地管理事务。以下是一个事务管理的示例:
```java
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public User registerUser(User user) {
// 检查用户名是否已存在
if (userRepository.findByEmail(user.getEmail()) != null) {
throw new RuntimeException("Email already exists");
}
return userRepository.save(user);
}
}
```
在上述服务类中,我们通过`@Transactional`注解指明`registerUser`方法运行在事务上下文中,任何运行时异常都会导致事务回滚。而SpringBoot默认是使用数据库事务管理器来管理事务的,因此无需额外的配置。
以上内容,我们完成了SpringBoot环境的搭建,包括理解其核心特性以及与Spring框架的关系。同时,我们也实践了如何使用SpringBoot构建RESTful API以及如何进行数据持久化操作,实现了基础的CRUD功能并探索了JPA/Hibernate的集成。在接下来的章节中,我们将深入介绍如何构建Vue.js前端应用,并最终实现前后端的整合与部署。
# 3. 构建Vue.js前端应用
## 3.1 Vue.js基础入门
### 3.1.1 Vue.js的核心概念
Vue.js 是一个用于构建用户界面的渐进式JavaScript框架,以其简洁的API和灵活性而受到开发者们的青睐。其核心概念包括数据驱动和组件化。数据驱动意味着Vue.js在DOM操作上采用了数据绑定的方式来减少不必要的DOM操作,通过数据的改变自动更新视图。而组件化则允许开发者将用户界面分割成独立、可复用的组件,并进行高效组合。
Vue.js 的模板语法是声明式的,允许开发者使用简洁的模板语法来声明式地将数据渲染进DOM系统。模板中的数据绑定指令 `{{ }}` 可以将数据与DOM进行绑定。
### 3.1.2 模板、指令与组件的基本用法
Vue.js 的模板是基于HTML的,它添加了一些特殊的属性和指令来声明式地将数据渲染进DOM。例如,`v-bind` 指令可以动态绑定一个或多个属性,或一个组件prop到表达式:
```html
<a v-bind:href="url">Go to {{ url }}</a>
```
在这个例子中,`href` 属性的值会被绑定到Vue实例中的 `url` 数据属性上。当 `url` 的值变化时,链接也会相应地更新。
组件是Vue.js中最基础的构建块,允许你通过创建可复用的Vue实例来定义独立的功能块。组件的基本用法如下:
```***
***ponent('my-component', {
template: '<div>A custom component!</div>'
});
```
创建一个名为 `my-component` 的全局组件,它的模板内容是 `<div>A custom component!</div>`。
## 3.2 Vue.js的状态管理
### 3.2.1 Vuex的基本原理和应用
Vuex 是Vue.js的状态管理模式和库,它集中管理所有组件的状态,并以相应的规则保证状态以可预测的方式发生变化。Vuex的核心概念包括以下几个部分:
- **State**:存储状态(即数据)。
- **Getters**:类似于计算属性,用于派生出一些状态,常用于对多个状态的逻辑组合。
- **Mutations**:更改状态的唯一方式,必须是同步函数。
- **Actions**:处理异步操作,可以包含任意异步操作。
- **Modules**:将store分割成模块,每个模块拥有自己的state、mutations、actions、getters等。
Vuex 应用的一个基本例子如下:
```javascript
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++;
}
}
});
new Vue({
el: '#app',
store,
methods: {
increment() {
this.$***mit('increment');
}
}
});
```
### 3.2.2 实现全局状态管理
全局状态管理是通过Vuex进行管理,能够使得组件间共享状态变得简单。以下是一个简单的计数器应用,演示如何在Vue组件中使用Vuex来管理状态:
```html
<div id="app">
<p>{{ count }}</p>
<button @click="increment">Increment</button>
</div>
```
```javascript
// 引入Vue和Vuex
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
// 创建store实例
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++;
}
}
});
// 创建Vue实例,并将store注入
new Vue({
el: '#app',
store,
computed: {
count() {
return this.$store.state.count;
}
},
methods: {
increment() {
this.$***mit('increment');
}
}
});
```
在这个例子中,我们创建了一个store实例,并定义了一个`count`状态和一个`increment` mutation。在Vue实例中,我们将store注入,并通过计算属性和方法来访问和修改状态。
## 3.3 前后端数据交互
### 3.3.1 AJAX与Fetch API的使用
在Web开发中,前后端数据交互是不可或缺的部分。Vue.js 应用通常使用 AJAX 或者现代的 Fetch API 来进行 HTTP 请求。以下是使用Fetch API的一个基本例子:
```javascript
fetch('***')
.then(response => response.json())
.then(data => {
console.log(data);
})
.catch(error => {
console.error('There was an error!', error);
});
```
在这个例子中,`fetch` 函数发送一个GET请求到指定的URL。然后使用 `.then()` 链式调用处理响应并解析JSON数据。
### 3.3.2 Axios在项目中的集成与应用
Axios是一个基于Promise的HTTP客户端,支持浏览器和node.js环境。在Vue项目中,可以使用axios库来发送请求。以下是如何在Vue项目中安装并使用axios:
```bash
npm install axios
```
```javascript
import axios from 'axios';
axios.get('***')
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error('There was an error!', error);
});
```
在这个例子中,使用 `axios.get` 方法发起一个GET请求,并通过Promise的 `.then()` 和 `.catch()` 方法处理结果。
Axios不仅支持请求方法别名,如 `.get`, `.post`, `.put`, `.delete` 等,还支持拦截器、请求和响应的转换、请求取消等高级特性。
通过这些机制,Vue.js前端应用能够轻松实现与后端的数据交互,进而在用户界面上展示动态的内容。在下一章,我们将讨论如何在前后端分离的架构下,处理跨域资源共享(CORS)的问题,并进一步探讨如何构建单页面应用(SPA),以优化用户交互体验。
# 4. 前后端整合与部署
## 4.1 配置跨域资源共享(CORS)
### 4.1.1 CORS的工作原理
跨域资源共享(Cross-Origin Resource Sharing,CORS)是一种允许网页从不同域请求资源的机制。当一个域的网页尝试访问另一个域的资源时,由于浏览器同源策略的限制,就会发生跨域请求问题。CORS通过在HTTP头部中添加额外的字段来允许这样的跨域请求。
在CORS策略中,存在两种类型的请求:简单请求和预检请求。简单请求不会触发额外的验证,而预检请求则会触发一个OPTIONS请求,以确认服务器是否允许跨域请求。一旦服务器在响应中声明了允许的源、请求头、方法等信息,浏览器就会放行实际的请求。
### 4.1.2 在SpringBoot中配置CORS
在SpringBoot中配置CORS相对简单,可以通过几种方式实现:
#### 1. 全局CORS配置
可以在SpringBoot的配置类中使用`WebMvcConfigurer`接口来设置全局CORS策略:
```java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("***") // 允许跨域的源地址
.allowedMethods("GET", "POST", "PUT", "DELETE") // 允许跨域的HTTP方法
.allowedHeaders("*") // 允许跨域请求中的所有头信息
.allowCredentials(true); // 允许发送Cookie等认证信息
}
}
```
#### 2. 方法级别的CORS配置
也可以在控制器(Controller)的方法上使用`@CrossOrigin`注解来指定CORS策略:
```java
@RestController
@RequestMapping("/api")
public class MyController {
@CrossOrigin(origins = "***")
@GetMapping("/data")
public ResponseEntity<?> getData() {
// ...
}
// 其他方法...
}
```
#### 3. 使用CORS过滤器
另一个选择是在Web配置中添加一个自定义的CORS过滤器,这允许更复杂的配置:
```java
@Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("***");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean<>(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
```
## 4.2 构建单页面应用(SPA)
### 4.2.1 SPA的概念和优势
单页面应用(Single Page Application,SPA)是一种用户界面与后端服务器通过API进行通信的应用模型。SPA的特点是仅加载一个HTML页面,所有的操作都在这个页面上进行,通过JavaScript动态地更新DOM,从而实现页面的交互和数据加载,无需重新加载整个页面。
SPA的主要优势包括:
- **更快的页面加载时间**:由于页面只加载一次,后续操作几乎无需等待。
- **更好的用户体验**:用户界面响应更快,接近原生应用的体验。
- **前后端分离**:前后端通过API进行通信,职责更加清晰。
### 4.2.2 Vue Router的使用与配置
Vue Router是Vue.js的官方路由管理器,它允许你通过不同的路径来展示不同的组件。通过Vue Router,可以将SPA的路径映射到对应的组件。
#### 安装Vue Router
首先,通过npm安装Vue Router:
```bash
npm install vue-router
```
#### 配置路由
在Vue项目中,可以在`src`目录下创建一个名为`router`的新文件夹,并在其中创建`index.js`文件来配置路由:
```javascript
import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/components/Home'
import About from '@/components/About'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: About
}
]
})
```
在`main.js`文件中引入并使用路由:
```javascript
import Vue from 'vue'
import App from './App'
import router from './router'
new Vue({
el: '#app',
router,
template: '<App/>',
components: { App }
})
```
#### 路由导航
在Vue组件中,可以使用`<router-link>`和`<router-view>`组件来进行导航和展示相应的组件:
```html
<template>
<div id="app">
<nav>
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</nav>
<router-view/>
</div>
</template>
```
## 4.3 部署与优化
### 4.3.1 前端与后端的部署策略
部署一个前后端分离的应用时,前端和后端可以分别部署在不同的服务器上,或者使用同一个服务器但部署在不同的端口。通常情况下,前端应用会被构建为静态资源并部署在Web服务器或者CDN上,而后端应用则作为一个服务端应用运行。
#### 前端部署
在构建Vue.js前端应用时,运行以下命令来构建生产环境版本:
```bash
npm run build
```
构建完成后,会在项目目录下的`dist`文件夹中生成静态资源。这些资源可以被部署到任何Web服务器或CDN上。在Nginx中,可以通过配置静态文件服务来实现:
```nginx
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
```
#### 后端部署
后端应用可以使用Spring Boot自带的内置Tomcat来部署。在项目构建完成后,可以使用Spring Boot的Maven插件来运行打包好的应用:
```bash
java -jar target/myapp.jar
```
或者,可以将其部署为一个服务,使用操作系统的服务管理工具来管理其生命周期。
### 4.3.2 性能优化技巧
性能优化是提高用户体验和减少服务器负载的重要手段。对于前后端分离的应用来说,性能优化可以从以下几个方面入手:
#### 前端优化
1. **代码分割和懒加载**:使用懒加载来确保初始加载的包尽可能小,按需加载其他模块。
2. **压缩资源文件**:使用工具(如UglifyJS、Terser)压缩JavaScript和CSS文件。
3. **使用缓存**:给静态资源添加长期缓存头,使用Service Worker缓存资源。
4. **图片优化**:使用合适的图片格式并压缩图片大小。
#### 后端优化
1. **数据库索引优化**:合理的索引可以显著提高查询效率。
2. **缓存机制**:在频繁访问的数据上使用缓存,如使用Redis。
3. **异步处理和队列**:对于耗时任务,可以使用消息队列进行异步处理。
4. **服务端渲染**:对于首屏加载时间要求很高的页面,可以考虑服务端渲染。
#### 通用优化
1. **内容分发网络(CDN)**:使用CDN来分发静态资源,减轻服务器压力。
2. **负载均衡**:使用负载均衡器来分散请求,提高应用的可用性和可靠性。
3. **监控与日志**:实现监控和日志记录,以便及时发现和解决问题。
部署和优化是一个持续的过程,需要根据应用的实际情况不断地调整策略。
# 5. 实战案例分析
## 5.1 案例项目需求与设计
### 5.1.1 功能需求分析
在任何开发项目开始之前,理解需求是最为关键的一步。对于我们的案例项目,我们设定的用户群体是中小型企业,它们需要一个能够帮助它们管理客户关系的系统。以下是几个核心功能需求:
- **用户登录与注册**:允许企业用户注册并登录系统。
- **客户信息管理**:能够添加、修改、删除和查询客户信息。
- **报表生成**:提供自定义报表,展示客户数据的统计分析。
- **权限控制**:不同的用户角色可以访问不同的数据和功能模块。
- **数据安全**:确保客户信息的安全性,防止未授权访问。
### 5.1.2 系统架构设计
为了满足上述需求,我们将采用微服务架构来设计我们的系统。这种架构将允许我们为每个功能模块提供独立的服务,从而使系统更加模块化、易于扩展和维护。具体而言,系统将由以下几个微服务组成:
- **用户认证服务**:负责处理用户登录、注册、认证和授权。
- **客户信息服务**:管理客户信息的CRUD操作。
- **报表服务**:生成和提供报表相关的服务。
- **权限控制服务**:管理用户角色和权限。
此外,每个服务都将有自己的数据库,以确保数据的隔离和安全性。我们将使用Spring Boot来构建后端服务,并利用Vue.js和Vue Router构建单页面前端应用。
## 5.2 关键功能实现与解析
### 5.2.1 用户认证与授权
用户认证和授权是保护应用安全的核心部分。在这个案例中,我们将使用JWT(JSON Web Tokens)来实现用户认证,并利用Spring Security进行授权管理。
对于用户登录,我们将使用`/login`端点,并接收用户名和密码。登录成功后,服务器将返回一个JWT给客户端,客户端随后将这个token用于后续请求的认证。
```java
// 登录控制器
@RestController
public class AuthenticationController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@RequestMapping(value = "/login", method = RequestMethod.POST)
public ResponseEntity<?> createAuthenticationToken(@RequestBody JwtRequest authenticationRequest) throws Exception {
authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());
final String token = jwtTokenUtil.generateToken(authenticationRequest.getUsername());
return ResponseEntity.ok(new JwtResponse(token));
}
private void authenticate(String username, String password) throws Exception {
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
} catch (DisabledException e) {
throw new Exception("USER_DISABLED", e);
} catch (BadCredentialsException e) {
throw new Exception("INVALID_CREDENTIALS", e);
}
}
}
```
### 5.2.2 数据库交互与数据校验
与数据库的交互对于CRUD操作至关重要。我们将使用Spring Data JPA简化数据库操作。对于数据校验,我们将在模型层使用Hibernate的验证注解。
```java
// 客户信息模型
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@NotNull(message = "姓名不能为空")
private String name;
@NotNull(message = "联系电话不能为空")
private String phone;
// Getters and Setters
}
```
在服务层,我们将使用`@Transactional`注解来管理事务,确保数据的一致性。
```java
// 客户信息服务
@Service
@Transactional
public class CustomerService {
@Autowired
private CustomerRepository repository;
public Customer saveCustomer(Customer customer) {
if (customer.getName() == null || customer.getPhone() == null) {
throw new ValidationException("客户信息不完整");
}
return repository.save(customer);
}
}
```
## 5.3 项目回顾与总结
### 5.3.1 项目中遇到的问题及解决方法
在项目的开发过程中,我们遇到了几个主要的问题,例如:
- **微服务间的通信**:最初,我们面临微服务间通信效率低下的问题,通过引入异步消息队列(如RabbitMQ),我们解决了这一问题。
- **数据库事务管理**:在确保数据一致性方面,使用Spring的声明式事务管理帮助我们简化了代码并提高了系统的健壮性。
- **前后端分离的整合**:由于前后端分离,我们使用了CORS策略来解决跨域问题,确保前后端可以无缝整合。
### 5.3.2 实战中的经验分享与优化建议
在开发过程中,我们总结出一些宝贵的经验:
- **持续集成和持续部署(CI/CD)**:引入CI/CD流程可以极大提高开发效率,缩短产品从开发到部署的时间。
- **代码复用与模块化**:在代码库中实施模块化设计和组件化开发,可以提高代码复用率,减少重复工作。
- **性能监控与日志**:在生产环境中实施全面的性能监控和日志记录,以便于问题快速定位和解决。
对于优化建议:
- **使用缓存**:对于读操作远多于写操作的数据,使用缓存可以显著提升性能。
- **数据库查询优化**:合理使用索引,并对复杂的查询进行性能分析,优化查询语句。
- **前后端加载优化**:减小打包文件大小,使用代码分割和懒加载来优化前端加载速度。
0
0