了解到了前后端分离的技术后,非常眼热的想试一下,在前几篇文章中分别整理了SSM框架整合、Vue的使用、ElementUI的使用、Axios的使用,今天终于通过一个小Demo实现了前后端分离,这是“全栈”路上的一小步,却是我个人的一大步,冲!
0.准备工作
准备工作一定是要放在前面的,这次的准备工作大家可以先看看大纲:
关于SSM框架的知识点
关于Vue的知识点
准备
当你掌握了Vue-CLI、ElementUI、SSM框架、Axios后,就可以来着手准备实现前后端分离了,关于前后端分离的概念已经提过很多次了:前后端分离就是前端从后端中分离出来,由之前的JSP实现业务逻辑变成了前端后端各自编写业务逻辑,前端只需要将需要的数据从后端方面获取就行,而后端再也不用兼职美工切图,进行前端开发了,只需要将准备好的数据以JSON的格式发送给前端等待接收就好了。
前后端分离的好处除了提供了大量的前端岗位,这句划掉,让前端程序员更加有价值,后端程序员更加专注于自己的业务逻辑,同时前后端分离使得前端版本迭代再也不用和后端商量,只需要忙自己的就可以,唯一麻烦的就是定义接口,这是前端程序员和后端程序员日常争吵甩锅80%的原因。
在正式开始前后端分离人不分离的“全栈”开发前,你需要:
SSM框架整合能力、Tomcat的配置使用、Vue-CLI的使用能力、Bootstrap或ElementUI前端组件的使用、Node.js的使用(npm还是要会敲的)、至少一门数据库语言
掌握这些后,我们一起来看一下前后端分离的实现👇
1.前端实现
首先我们需要前端的界面,这个Demo实现了登录功能并且在登陆后可以显示登录者的USERNAME,功能很简单,我们现在画界面,首先建立一个新的Vue-CLI工程,工程目录如下:
这里router、views文件夹以及ststic目录下的data.json都是我们自己新建的,首先配置路由:
import Vue from 'vue'
import VueRouter from 'vue-router'
import Main from '../views/Main'
import Login from '../views/Login'
Vue.use(VueRouter);
export default new VueRouter({
mode: 'history',
routes: [
{
path: '/main',
name: 'main',
component: Main
},
{
path: '/login',
name: 'login',
component: Login
}
]
});
路由简单配置一下,传递参数我们使用params,所以在路由这里体现不出来,然后是main.js:
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router/index'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(router);
Vue.use(ElementUI);
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
在main.js中配置好路由后,紧随其后的APP.vue:
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
APP.vue中只有一个<router-view>来显示页面,接下来是重头戏,Login.vue页面:
<template>
<div>
<el-form ref="loginForm" :model="form" :rules="rules" label-width="80px " class="login-box">
<h3 class="login-title">欢迎登录</h3>
<el-form-item label="账号" prop="username">
<el-input type="text" placeholder="请输入账号" v-model="form.username"/>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input type="password" placeholder="请输入密码" v-model="form.password"/>
</el-form-item>
<el-form-item>
<el-button type="primary" v-on:click="onSubmit('loginForm')">登录</el-button>
</el-form-item>
</el-form>
<el-dialog
title="温馨提示"
:visible.sync="dialogVisible"
width="30%"
:before-close="handleClose">
<span>请输入账号和密码</span>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="dialogVisible = false">确定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
export default {
name: "Login",
data() {
return {
form: {
username: '',
password: ''
},
//表单验证,需要在el-form-item元素中增加prop属性
rules: {
username: [
{required: true, message: '账号不可为空', trigger: 'blur'}
],
password: [
{required: true, message: '密码不可为空', trigger: 'blur'}
]
},
//对话框显示和隐藏
dialogVisible: false
}
},
methods: {
onSubmit(formName) {
//为表单绑定验证功能
this.$refs[formName].validate((vaild) => {
if (vaild) {
//使用vue-router 路由指导到指定页面,该方式称为编程式导航
this.$router.push("/main");
} else {
this.dialogVisible = true;
return false;
}
});
}
}
}
</script>
<style lang="scss" scoped>
.login-box {
border: 1px solid #DCDFE6;
width: 350px;
margin: 180px auto;
padding: 35px 35px 15px 35px;
box-shadow: 1px 1px 15px 1px black;
}
</style>
我们使用了ElementUI中的组件,进行了一点小小的改动,现在只要是输入内容就可以跳转:
然后是主页面:
<template>
<div>
<h1>首页</h1>
</div>
</template>
<script>
export default {
name: "Main"
}
</script>
<style scoped>
</style>
接下来进行后端页面的代码编写👇
2.后端实现
新建一个程序,这是目录:
这个结构就是紫薇星上的SSM——整合SSM框架中的架构,可以直接拿来用,我还是重新把代码贴一遍,讲解部分可以去文章中查看,首先是pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>background-json</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.34</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>3.0-alpha-1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.0-M1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2.1-b03</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.71</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
<!--配置JDK版本,必须配置!-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>9.0.4</maven.compiler.source>
<maven.compiler.target>9.0.4</maven.compiler.target>
</properties>
</project>
然后是web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<session-config>
<session-timeout>15</session-timeout>
</session-config>
</web-app>
这时我们建立一个新的数据库,只存放两个属性:
然后是pojo类:
package com.test.pojo;
public class User {
private String realUsername;
private String realPassword;
public User() {
}
public User(String realUsername, String realPassword) {
this.realUsername = realUsername;
this.realPassword = realPassword;
}
public String getRealUsername() {
return realUsername;
}
public void setRealUsername(String realUsername) {
this.realUsername = realUsername;
}
@Override
public String toString() {
return "User{" +
"realUsername='" + realUsername + '\'' +
", realPassword='" + realPassword + '\'' +
'}';
}
public String getRealPassword() {
return realPassword;
}
public void setRealPassword(String realPassword) {
this.realPassword = realPassword;
}
}
mapper中我们只定义一个方法:
package com.test.mapper;
import com.test.pojo.User;
import java.util.List;
public interface UserMapper {
public List<User> selectUsers();
}
然后配置mybatis-config.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="com.test.pojo"/>
</typeAliases>
<mappers>
<mapper class="com.test.mapper.UserMapper"/>
</mappers>
</configuration>
配置数据库连接文件:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/vue_ssm?useSSL=true&useUnicode=true&charsetEncoding=utf8
jdbc.username=#你的账号#
jdbc.password=#你的密码#
然后是整合spring和mybatis:
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--关联数据库文件-->
<context:property-placeholder location="classpath:database.properties"/>
<!--<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource"></bean>-->
<!--数据库连接池-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!--c3p0私有属性-->
<property name="maxPoolSize" value="30"/>
<property name="minPoolSize" value="10"/>
<!--关闭连接后不自动commit-->
<property name="autoCommitOnClose" value="false"/>
<!--获取连接超时时间-->
<property name="checkoutTimeout" value="10000"/>
<!--连接失败重试次数-->
<property name="acquireRetryAttempts" value="2"/>
</bean>
<!--SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--绑定Mybatis的配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!--配置dao接口扫描包,动态实现dao接口注入到Spring中-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--注入sqlSessionFactory-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!--要扫描的dao包-->
<property name="basePackage" value="com.test.mapper"/>
</bean>
</beans>
UserMapper.xml实现sql:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.test.mapper.UserMapper">
<select id="selectUsers" resultType="User">
select * from vue_ssm.user;
</select>
</mapper>
接下来是service,同理只有一个方法:
package com.test.service;
import com.test.pojo.User;
import java.util.List;
public interface UserService {
public List<User> selectUsers();
}
然后UserServiceImpl实现:
package com.test.service;
import com.test.mapper.UserMapper;
import com.test.pojo.User;
import java.util.List;
public class UserServiceImpl implements UserService{
private UserMapper mapper;
public UserServiceImpl() {
}
public UserServiceImpl(UserMapper mapper) {
this.mapper = mapper;
}
public UserMapper getMapper() {
return mapper;
}
@Override
public String toString() {
return "UserServiceImpl{" +
"mapper=" + mapper +
'}';
}
public void setMapper(UserMapper mapper) {
this.mapper = mapper;
}
@Override
public List<User> selectUsers() {
return mapper.selectUsers();
}
}
整合spring和service:
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--扫描Service下的包-->
<context:component-scan base-package="com.test.service"/>
<!--将业务类注入到Spring,通过注解或者配置-->
<bean id="UserServiceImpl" class="com.test.service.UserServiceImpl">
<property name="mapper" ref="userMapper"/>
</bean>
<!--声明式事务配置-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
然后是controller层,同样的只有一个方法:
package com.test.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.test.pojo.User;
import com.test.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class UserController {
@Autowired
@Qualifier("UserServiceImpl")
private UserService userService;
@RequestMapping("/selectUser")
public String list() throws JsonProcessingException {
List<User> list =userService.selectUsers();
ObjectMapper toJson = new ObjectMapper();
String json =toJson.writeValueAsString(list);
return json;
}
}
这里使用@RestController将返回值变为字符串而不是跳转,再来整合spring-mvc:
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--扫描包-->
<context:component-scan base-package="com.test.controller"/>
<!--注解驱动-->
<mvc:annotation-driven/>
<!--静态资源过滤-->
<mvc:default-servlet-handler/>
<!--视图解析器-->
<!--<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">-->
<!-- <property name="prefix" value="/WEB-INF/jsp/"/>-->
<!-- <property name="suffix" value=".jsp"/>-->
<!--</bean>-->
<!--这一段是SpringMVC的自动处理乱码配置-->
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="utf-8"/>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnEmptyBeans" value="false"/>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
</beans>
最后将这些整合在applicationContext.xml中:
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="classpath:spring-mapper.xml"/>
<import resource="classpath:spring-service.xml"/>
<import resource="classpath:spring-mvc.xml"/>
</beans>
这时我们配置Tomcat:
看一下是否可以输出:
没有问题,启动Tomcat先不要关闭,接下来准备连接前后端分离👇
3.进行连接
在编写后端代码的时候我们就要准备定义点后端分离的接口格式了,最终决定是传递一个数组对象,数组中包括多个User对象,每个对象有realUsername和realPassword两个属性,接下来需要对前端代码进行一点修改。
首先是Login.vue:
<template>
<div>
<el-form ref="loginForm" :model="form" label-width="80px " class="login-box">
<h3 class="login-title">欢迎登录</h3>
<el-form-item label="账号" prop="username">
<el-input type="text" placeholder="请输入账号" v-model="form.username"/>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input type="password" placeholder="请输入密码" v-model="form.password"/>
</el-form-item>
<el-form-item>
<el-button type="primary" v-on:click="onSubmit(form.username, form.password)">登录</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: "Login",
data(){
return{
form: {
username: '',
password: ''
},
json:[
{
realUsername: '',
realPassword: ''
}
]
}
},
created() {
axios.get('http://127.0.0.1:8080/selectUser').then(successResponse=>{
this.json = successResponse.data;
});
},
methods: {
onSubmit(username, password) {
var falg = false;
for (let i = 0; i < this.json.length; i++) {
if (username == this.json[i].realUsername && password == this.json[i].realPassword) {
this.$router.push({
path: '/main',
name: 'main',
params: {
name: username
}
});
falg = true;
}
}
if (falg === false){
alert("失败!");
return false;
}
}
}
}
</script>
<style lang="scss" scoped>
.login-box {
border: 1px solid #DCDFE6;
width: 350px;
margin: 180px auto;
padding: 35px 35px 15px 35px;
box-shadow: 1px 1px 15px 1px black;
}
</style>
主要增加了created钩子函数,在data和methods刚初始化好就进行数据的异步加载,加载的url为本机的8080端口下的selectUser路径,与刚才检测JSON返回值的路径是一个,这样才能拿到需要的数据;
为了避免冲突,从表单中获取的数据为username和password,从后端获取的数据为realUsername和realPassword,这样就可以区分并进行下一步操作;
因为传过来的数据是数组,所以methods中对于数组进行循环遍历,判断是否为数据库中正确的数据,然后返回;
上面我们说到视图跳转传递参数用的是params,没有用router-link和query,这三种方法都可以,但是都有需要注意的点,通过动态路由router-link来传:
//路由配置文件中 配置动态路由
{
path: '/detail/:id',
name: 'Detail',
component: Detail
}
//跳转时页面
var id = 1;
this.$router.push('/detail/' + id)
//跳转后页面获取参数
this.$route.params.id
需要注意得是:
- to前面一定要加
:
,一定要记得加!记得加!加! - to后面
{
中的name值(这里是userid
)要与路由中的name值一致
通过query来传:
//路由配置文件中
{
path: '/detail',
name: 'Detail',
component: Detail
}
//跳转时页面
this.$router.push({
path: '/detail',
query: {
name: '张三',
id: 1,
}
})
//跳转后页面获取参数对象
this.$route.query
需要注意的是:
- 路由配置中不用配置参数
- 传输的值会在url后面以参数的形式显示出来,刷新页面数据不变
通过params来传:
//路由配置文件中
{
path: '/detail',
name: 'Detail',
component: Detail
}
//跳转时页面
this.$router.push({
name: 'Detail',
params: {
name: '张三',
id: 1,
}
})
//跳转后页面获取参数对象
this.$route.params
需要注意的是:
- 跳转时页面除了path和params外,必须要有name属性
- 传输的值不会显示,刷新页面数据会发生改变
然后我们修改Main.vue页面:
<template>
<div>
<h1>首页</h1>
<span>{{username}}</span>
</div>
</template>
<script>
export default {
name: "Main",
data(){
return{
username: this.$route.params.name
}
}
}
</script>
<style scoped>
</style>
通过params获取数据并赋值给页面,这时我们就可以进行测试了,但是,一般人都会遇到第一个问题:页面加载不出来,这是因为端口没有修改,前端的页面要和后端的页面不同,如果不修改则都是8080,就会报错;
修改后大家就会遇到第二个问题:跨域!一般来说,会使用过滤器或拦截器来进行跨域请求的处理,但是在springMVC4.2版本之后,使用注解@CrossOrigin就可以解决跨域问题,在后端代码UserController中修改:
package com.test.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.test.pojo.User;
import com.test.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class UserController {
@Autowired
@Qualifier("UserServiceImpl")
private UserService userService;
@RequestMapping("/selectUser")
@CrossOrigin
public String list() throws JsonProcessingException {
List<User> list =userService.selectUsers();
ObjectMapper toJson = new ObjectMapper();
String json =toJson.writeValueAsString(list);
return json;
}
}
如果注解放在类上,此类中所有方法启用跨域支持,如果放在方法上,仅此方法启用跨域支持。如果发现注解不起作用,那么要注意是springMVC的版本要在4.2或以上版本才支持@CrossOrigin,在Controller注解上方添加@CrossOrigin注解后,仍然出现跨域问题,解决方案之一就是:在@RequestMapping注解中没有指定Get、Post方式,具体指定后问题解决。
解决了跨域问题我们来看一下最终效果👇
4.最终效果
简单的前后端分离效果就通过这个Demo来实现啦,一通百通的你应该对如何进行一个前后端分离的项目有点数了,继续保持学习,向着“全栈”进发!我们下次见👋