springboot+vue-cli前后端分离demo(含踩坑记)

Posted hans774882968

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了springboot+vue-cli前后端分离demo(含踩坑记)相关的知识,希望对你有一定的参考价值。

整个demo由两个项目组成,后端项目基于springboot,前端项目基于vue-cli。整个demo基于idea。

后端项目

前置操作:配置maven。maven可以用来管理jar包。虽然不太准确,但萌新暂时可以把maven与python的pip、nodejs的npm类比。先咕着吧~

配置好maven,就开始创建项目。File==>New==>Project,创建一个Spring Boot项目。项目名“springbootdemo”。

如果遇到显示无法连接到http://start.spring.io,或者是连接超时的问题:

在这里插入图片描述

如上图,打开settings,点击check connection,在输入框输入http://start.spring.io,等一会儿,出现connection successful就解决啦~

后面就是一些选项,没啥坑,略。项目结构如图

在这里插入图片描述

然后我们会在右下角看到正在下载Spring Boot包相关的依赖,等它下载完就好~

值得注意的是,如果后续需要引入其他的包,需要修改pom.xml,然后等右下角出现一个提示框,点击”import changes“。

  • |.idea: idea项目目录,这里主要存放Intellij Idea的项目配置文件,不能删除。git的时候不要push这里的东西~
  • |.mvn: 可以删除
  • |src/main/java: 程序开发以及主程序入口
  • |src/main/resources: 配置文件
  • |src/test/java 测试程序
  • |.gitignore git的ignore文件
  • |boot-example.iml Intellij Idea项目配置文件
  • |HELP.md 帮助文档,可以删除
  • |mvnw 可以删除
  • |mvnw.cmd 可以删除
  • |pom.xml Maven的pom文件

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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>net.wsw</groupId>
    <artifactId>springbootdemo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springbootdemo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--配置thymeleaf-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

其中spring-boot-starter-web是启动web服务必不可少的。spring-boot-starter-thymeleaf是模板引擎。

新建NewController.java。与SpringbootdemoApplication.java处于同一目录。

package net.wsw.springbootdemo;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class NewController {
    @RequestMapping("/hw")
    public String hw() {
        return "hello world!";
    }

    @RequestMapping("/")
    public String index() {
        return "hello index!";
    }
}

接下来就可以点击SpringbootdemoApplication.java的绿色箭头,运行web项目。没有报错的话命令行大概长这样

在这里插入图片描述

如果杯具地8080端口被占用了(比如我),就需要改默认端口qwq。打开src/main/resources/application.properties,加一行

server.port=8088

配置thymeleaf(可选)

thymeleaf是模板引擎,在前后端分离开发的大背景下貌似没啥用的样子……但作为demo还是可以配置一下。

pom.xml新增

        <!--配置thymeleaf-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

等右下角出现一个提示框,点击”import changes“。

application.properties新增

# thymeleaf
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.check-template-location=true
spring.thymeleaf.suffix=.html
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.servlet.content-type=text/html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.cache=false

classpath:/templates/的路径在这个demo指的是”target/classes/templates/“,属于默认设置。

在src/main/resources下新建templates文件夹,然后新建upload.html

<!DOCTYPE html>
<html xmlns:th="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <title th:text="${title}"></title>
    </head>
    <body>
        <h1 th:text="${title}"></h1>
        <form action="/upload_img" method="post" enctype="multipart/form-data">
            <p><input type="file" name="file" /></p>
            <p><input type="submit" value="提交" /></p>
        </form>
    </body>
</html>

这里我们用模板引擎来做一个上传文件的demo。

新建UploadFileController.java

package net.wsw.springbootdemo;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

@Controller
public class UploadFileController {

    @RequestMapping("/upload")
    public String upload(Model model) {
        model.addAttribute("title", "上传图片demo");
        return "upload";
    }

    @RequestMapping(value = "/upload_img", method = RequestMethod.POST)
    @ResponseBody
    public String uploadImg(@RequestParam("file") MultipartFile file) {
        if (file == null || file.isEmpty()) {
            return "上传失败!请选择文件!";
        }
        File path = null;
        try {
            path = new File(ResourceUtils.getURL("classpath:").getPath());
            File upload = new File(path.getAbsolutePath(), "static/img/");
            if (!upload.exists()) {
                upload.mkdirs();
            }
            File fileUp = new File(upload.getAbsolutePath() + "/" + file.getOriginalFilename());
            file.transferTo(fileUp);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "上传成功!";
    }
}

值得注意的是,因为controller接收的参数名是file,html文件的input[type=“file”]必须保证name=“file”。

运行后端项目,上传一个文件,去看target/classes/static/img(如果原先该路径不存在,controller的代码会去新建),可以发现那个文件。

如果命令行报错是”没有接收到参数“,那是因为没有保证name=“file”。

前端项目

下载node.js并配置环境变量:略。

改成淘宝源

npm config set registry https://registry.npm.taobao.org

看现在是哪个源

npm config get registry

全局安装vue-cli

npm install -g vue-cli

查看vue脚手架是否已经安装成功

vue -V

接下来开始创建项目!项目名“sbdemo_frontend”。

可以用命令行创建,但是我们有idea这么好用的IDE,就可以免去命令行创建的痛苦啦!

Create New Project==>Static Web==>Vue.js……等等,static web那怎么没有vue.js呢?原来需要先下载插件!

1、settings==>Languages & Frameworks==>js语言版本选择es6。

2、settings==>Plugins,查找vue.js并安装。如图

在这里插入图片描述

接下来再去看看static web,发现已经可以选择vue.js啦~

后续就是一些选项,来源是在命令行构建项目时的那些选项。没啥坑点,略了。中间有一步,选择运行npm install则不需要自己专门再去一趟命令行啦~等待下载完毕所需依赖即可。

项目运行

npm run dev

值得注意的是vue-cli项目自带热部署。

项目结构如图

在这里插入图片描述

接下来设置eslint(可选)

ESLint是一个语法规则和代码风格的检查工具,可以用来保证写出语法正确、风格统一的代码。使用它可以避免低级错误和统一代码的风格。

打开settings,在搜索框搜索“eslint”,把“Enable”勾选上。

在这里插入图片描述

然后设置代码格式化快捷键。File==>Settings==>Keymap,搜索 Fix ESLint Problems。

Java已经占有一个代码格式化快捷键“Ctrl+Shift+L”,因此我们另设一个“Ctrl+Alt+;”。如图

在这里插入图片描述

终于开始写代码啦!

1、后端项目新建LoginController.java。与SpringbootdemoApplication.java处于同一目录。

package net.wsw.springbootdemo;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.HashMap;
import java.util.Map;

@Controller
public class LoginController {
    @RequestMapping("/login")
    @ResponseBody
    public Map login(String username, String password) {
        // response.setHeader("Access-Control-Allow-Origin", "*");// 部分浏览器需加
        Map<String, Object> mp = new HashMap<>();
        String u = "admin";
        String p = "admin";
        if (u.equals(username) && p.equals(password)) {
            mp.put("code", 200);
        } else {
            mp.put("code", -1);
        }
        return mp;
    }
}

2、前端和后端两个项目如何交互。

在开发的时候,前端有一个前端的服务器(nginx),后端有另一个后端的服务器(Tomcat)。开发前端内容的时候,可以把前端的请求通过前端服务器转发给后端(称为反向代理),这样就能实时观察结果,并且不需要知道后端怎么实现,而只需要知道接口提供的功能,两边的开发人员就可以各司其职了。
正向代理就是,你要访问一个网站,比如“xxx”,然后发现访问不到,于是你访问了一个能访问到“xxx”的代理服务器,让它帮你拿到你想浏览的页面。

反向代理就是,你访问了一个网站,你以为它是“xxx”,但其实它是“yy”,“yy”知道你其实是想找"xxx",就取回“xxx”的内容给你看。作为用户的你,是不知道有这个过程的,这么做是为了保护服务器,不暴露服务器的真实地址。

有一张示意图描述这两个过程

在这里插入图片描述

3、前端项目删除HelloWorld.vue,新建Index.vue和Login.vue

Index.vue

<template>
  <router-link to="/login">点击登录</router-link>
</template>

<script>
export default {
  name: 'Index'
}
</script>

<style scoped>
a {
  color: #42b983;
}
</style>

Login.vue

<template>
  <div class="login">
    <p class="info">{{ info }}</p>
    <p>用户名:<input v-model="loginForm.username" type="text" name="username" /></p>
    <p>密码:<input v-model="loginForm.password" type="password" name="password" /></p>
    <button @click="login">登录</button>
  </div>
</template>

<script>
export default {
  name: 'Login',
  data () {
    return {
      loginForm: {
        username: '',
        password: ''
      },
      info: ''
    }
  },
  methods: {
    login () {
      this.$axios.get('/login', {
        params: {
          username: this.loginForm.username,
          password: this.loginForm.password
        }
      }).then(res => {
        let dat = res.data
        if (dat.code === 200) {
          this.info = '登录成功'
        } else {
          this.info = '登录失败'
        }
      })
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
  font-weight: normal;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
</style>

src/router/index.js修改路由。

import Vue from 'vue'
import Router from 'vue-router'
import Index from '@/components/Index'
import Login from '@/components/Login'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Index',
      component: Index
    },
    {
      path: '/login',
      name: 'Login',
      component: Login
    }
  ]
})

此时运行前端项目,可以发现除了login表单点击登录按钮后报错以外,其他均已work。

4、值得注意的是,前端项目的路由和后端项目的路由是两套系统。前端项目为了能够使用axios向后端项目发请求,需要设置反向代理。

src/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'

// 设置反向代理,前端请求默认发送到 http://localhost:8083/api
import axios from 'axios'
axios.defaults.baseURL = 'http://localhost:8083/api'
// 全局注册,之后可在其他组件中通过 this.$axios 发送数据
Vue.prototype.$axios = axios

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el以上是关于springboot+vue-cli前后端分离demo(含踩坑记)的主要内容,如果未能解决你的问题,请参考以下文章

前后端分离项目——登录Token校验思路

springboot可以前后端分离吗

python框架django中结合vue进行前后端分离

nodeJS(express4.x)+vue(vue-cli)构建前后端分离详细教程

Tomcat部署前后端分离springboot项目

springboot&&vue前后端分离入门案例