springboot+vue項(xiàng)目從第一行代碼到上線(xiàn)部署全流程
一、引言
相信很多前端工程師或后端工程師都有一個(gè)全棧夢(mèng),或者想了解一下自己寫(xiě)的代碼是如何融入到整個(gè)項(xiàng)目的,或者想知道自己的“另一半”為什么會(huì)寫(xiě)出那么臭的代碼……
后端工程師會(huì)想:“前端小子怎么回事,我“費(fèi)盡心思”傳的那么幾十個(gè)數(shù)據(jù),他怎么就展示了一兩個(gè)?”
于此同時(shí)前端工程師看著“費(fèi)盡心思”的數(shù)據(jù)暗想,“byd,怎么每個(gè)接口的數(shù)據(jù)傳輸?shù)母袷蕉疾灰粯?,還有你登錄接口為什么要求用戶(hù)輸入自己的ID主鍵?”
于是兩者氣憤的去找了全棧的流程,想找出對(duì)方代碼“又臭又長(zhǎng)”的原因,機(jī)緣巧合下他們共同點(diǎn)開(kāi)了這一篇博客……
本文將帶領(lǐng)大家走完一個(gè)springboot+vue前后端分離項(xiàng)目的全流程,以盡可能簡(jiǎn)短的語(yǔ)言,幫助大家理解項(xiàng)目的前后端交互和上線(xiàn)部署
注:本人為大二在校生,代碼風(fēng)格參考黑馬程序員和尚硅谷,有所不足,敬請(qǐng)斧正
二、項(xiàng)目效果展示
項(xiàng)目連接?:120.27.247.221:80 (電腦打開(kāi)哈)
部分項(xiàng)目效果展示——
登錄頁(yè)面

主頁(yè)

個(gè)人信息頁(yè)面

發(fā)表文章頁(yè)面

注:這是一個(gè)簡(jiǎn)單的小網(wǎng)站,也是用其部分代碼來(lái)帶大家走完全流程的上線(xiàn)網(wǎng)站,里面小bug不少(其實(shí)是懶得改,不是)
三、項(xiàng)目準(zhǔn)備
注:這里不包括插件和依賴(lài)噢,用到的話(huà)后面會(huì)說(shuō)明的
后端部分——
IDEA專(zhuān)業(yè)版、jdk17、Mysql-8.0.31、Maven-3.6.1
前端部分——
VSCode、node.js
部署部分——
云服務(wù)器ECS(或者隨便什么服務(wù)器了)
四、部分項(xiàng)目結(jié)構(gòu)的分析 后端部分
1)項(xiàng)目的總體結(jié)構(gòu)
項(xiàng)目分為三個(gè)模塊,分別是common、pojo、server三個(gè)模塊

三個(gè)模塊有各自的作用和存放的項(xiàng)目文件,使項(xiàng)目更加清晰有條理
2)common模塊

common模塊負(fù)責(zé)管理在整個(gè)項(xiàng)目都可以使用到的方法等,如utils就可能放置一些jwt工具,oss工具之類(lèi)的,exception中放置處理異常的方法,json中放置json轉(zhuǎn)換器等
3)pojo模塊

pojo模塊負(fù)責(zé)管理項(xiàng)目中全局可以用到的實(shí)體類(lèi)等,比如Admin、User等實(shí)體類(lèi)
4)server模塊

server模塊負(fù)責(zé)管理一些具體的業(yè)務(wù)邏輯,比如登錄功能,發(fā)表文章功能等,這里需要特殊說(shuō)明的是“controller、service、dao”三層架構(gòu),是后端項(xiàng)目常用的項(xiàng)目結(jié)構(gòu),controller層負(fù)責(zé)規(guī)定方法請(qǐng)求的url,service層負(fù)責(zé)處理方法的具體邏輯,比如信息校驗(yàn)之類(lèi)的,dao層負(fù)責(zé)依據(jù)方法的需求處理數(shù)據(jù)庫(kù),這三層架構(gòu)層層遞進(jìn),是springboot項(xiàng)目的核心
前端部分
注:這個(gè)項(xiàng)目的前端結(jié)構(gòu)挺草率,畢竟本人主要從事后端,但是基本的結(jié)構(gòu)還是能分清的,小項(xiàng)目夠用

這個(gè)雖然比較亂,但是基本的結(jié)構(gòu)還是可以介紹一下的
1.public
放置的是項(xiàng)目用到的一部分靜態(tài)資源(特別是那種可以復(fù)用的資源,比如其他大佬寫(xiě)的炫酷頁(yè)面)
2.api
放置的是調(diào)用后端接口的方法,比如下面這一坨代碼
import request from '@/utils/request'
import type { userLogin } from '@/types/userLogin'
// 枚舉管理注冊(cè)模塊接口地址
enum API {
// 注冊(cè)接口地址
REGISTER = '/user/register',
GET_CODE = '/auth',
VERIFY_CODE = '/auth',
LOGIN = '/user/login',
FIND_PASSWORD = '/user/changePasswordByEmail',
}
//注冊(cè)函數(shù)
export const goRegister = (user: userLogin) => {
// 直接傳遞 user 對(duì)象
return request.post(API.REGISTER, user)
}
export const getCode = (email: string) => {
return request
.post(`${API.GET_CODE}/send-code?email=${encodeURIComponent(email)}`) // 將 email 作為查詢(xún)參數(shù)
.then((response) => response.data)
.catch((error) => {
console.error('獲取驗(yàn)證碼失敗:', error)
throw error // 拋出錯(cuò)誤以便調(diào)用者處理
})
}
export const verifyCode = (email: string, code: string) => {
return request
.post(
`${API.VERIFY_CODE}/verify-code?email=${encodeURIComponent(email)}&code=${encodeURIComponent(code)}`
) // 將 email 作為查詢(xún)參數(shù)
.then((response) => response.data)
.catch((error) => {
console.error('校驗(yàn)驗(yàn)證碼失敗:', error)
throw error // 拋出錯(cuò)誤以便調(diào)用者處理
})
}其中比如/user/register都是后端提供的接口的路徑,前端在這里調(diào)用后端的接口,goRegister()便是前端通過(guò)封裝后端接口得出的一個(gè)方法
3.components
其中一般封裝全局組件,比如你隨便打開(kāi)一個(gè)頁(yè)面,你滑動(dòng)頁(yè)面但是一直保持不動(dòng)的那一部分,或者是你一個(gè)需要重復(fù)使用的組件,比如后面要介紹的富文本編輯器
4.pages
其中是一個(gè)個(gè)的頁(yè)面,比如你現(xiàn)在退出了閱讀我這篇文章回到了主頁(yè),主頁(yè)和讀我這篇文章的頁(yè)面就是兩個(gè)不同的“page”
5.router
里面管理的是每個(gè)頁(yè)面的路由,你現(xiàn)在可以看一下你瀏覽器的導(dǎo)航欄,是不是有類(lèi)似http://xxx/xxx/xxx這里的/xxx就是一個(gè)路由的路徑它會(huì)帶你去到某一個(gè)“page”
6.style
里面是放置的一些全局可以用到的樣式,比如這個(gè)小項(xiàng)目就是在里面放了清楚全局默認(rèn)樣式的樣式
7.types
里面放置一些實(shí)體類(lèi),用來(lái)接收后端傳來(lái)的數(shù)據(jù),或者往后端去傳數(shù)據(jù)
8.utils
顧名思義,里面是一些工具,比如封裝一個(gè)我們自己的axios,或者一些解析token的工具
累死我了,先寫(xiě)這么多吧
牛馬是累不死的,繼續(xù)寫(xiě)
五、前后端的跨域配置
這一部分來(lái)解決連后端跨域配置的問(wèn)題,相信很多小伙伴有一個(gè)問(wèn)題,前端是如何找到后端接口的,為什么前端用的“/user”是自己電腦上的后端項(xiàng)目規(guī)定的“/user”,如果小編電腦上正好也定義了一個(gè)“/user”呢,為什么不會(huì)調(diào)用小編電腦上的“/user”接口。其實(shí)如果在前后端項(xiàng)目上如果不做配置,就是你后端寫(xiě)了一個(gè)“/user”,前端想用也是找不到“/user”的,那么怎么可以讓彼此找到呢?我們只需要做一些小小的配置即可
后端配置
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 允許訪(fǎng)問(wèn)的路徑
.allowedOrigins("http://localhost:5173") // 允許的源
.allowedHeaders("*") // 允許的請(qǐng)求頭
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 允許的方法
.allowCredentials(true); // 是否允許發(fā)送憑證
}這是有關(guān)跨域的一部分配置,允許了"http://localhost:5173"的連接,而"http://localhost:5173"正是本地前端的運(yùn)行端口
前端
//配置代理跨域
server: {
proxy: {
'/api': {
target: "http://localhost:8080",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}這里是前端的配置,它規(guī)定了前端去哪里去找后端接口,去"http://localhost:8080"找,這正是后端的端口
簡(jiǎn)而言之,后端允許前端去連接,前端也愿意去連接后端,這就成了
六、部分功能的前后端交互
1.登錄功能
后端部分
jwt工具
package com.qac.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;
public class JwtUtils {
private final static String signKey = "Admin";//管理員登錄signKey
private final static Long expire = 7200000L;//過(guò)期時(shí)間為12個(gè)小時(shí)
/**
* 生成JWT令牌
* @param claims JWT第二部分負(fù)載 payload 中存儲(chǔ)的內(nèi)容
*/
public static String generateJwt(Map<String, Object> claims){
String jwt = Jwts.builder()
.addClaims(claims)
.signWith(SignatureAlgorithm.HS256, signKey)
.setExpiration(new Date(System.currentTimeMillis() + expire))
.compact();
return jwt;
}
/**
* 解析JWT令牌
* @param jwt JWT令牌
* @return JWT第二部分負(fù)載 payload 中存儲(chǔ)的內(nèi)容
*/
public static Claims parseJWT(String jwt){
Claims claims = Jwts.parser()
.setSigningKey(signKey)
.parseClaimsJws(jwt)
.getBody();
return claims;
}
}這個(gè)工具其實(shí)沒(méi)必要去理解它的每一部分的底層是怎么生成的,大家只需要明白,如果登錄成功它會(huì)生成一個(gè)令牌,之后我們每次調(diào)用后端的方法都會(huì)先驗(yàn)證是否攜帶的這個(gè)令牌,如果沒(méi)有就不讓調(diào)用,這個(gè)令牌會(huì)在瀏覽器本地存儲(chǔ)(前端去做),另外提一點(diǎn),這個(gè)令牌的校驗(yàn)是通過(guò)攔截器完成的,攔截器如下
package com.qac.interceptor;
import com.alibaba.fastjson.JSONObject;
import com.qac.result.Result;
import com.qac.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* jwt令牌校驗(yàn)的攔截器
*/
@Component
@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws Exception {
//1.獲取請(qǐng)求url
String url=req.getRequestURL().toString();
LoginCheckInterceptor.log.info("請(qǐng)求的url--{}",url);
//獲取請(qǐng)求頭中的token
String jwt=req.getHeader("token");
if(!StringUtils.hasLength(jwt)){
LoginCheckInterceptor.log.info("token為空,未登錄...");
Result error=Result.error("NOT_LOGIN");
//這里不是json格式,要轉(zhuǎn)化
String notLogin= JSONObject.toJSONString(error);
res.getWriter().write(notLogin);
return false;
}
//解析token,解析失敗就登錄失敗
try {
JwtUtils.parseJWT(jwt);
}catch (Exception e){
e.printStackTrace();
LoginCheckInterceptor.log.info("登錄失敗");
Result error=Result.error("NOT_LOGIN");
//這里不是json格式,要轉(zhuǎn)化
String notLogin= JSONObject.toJSONString(error);
res.getWriter().write(notLogin);
return false;
}
LoginCheckInterceptor.log.info("token合法,放行");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle....");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterHandle....");
}
}還是那句話(huà),不理解底層沒(méi)關(guān)系,知道它的作用就可以了
到這里還有一個(gè)關(guān)于攔截器的小問(wèn)題,那就是它會(huì)不會(huì)把登錄注冊(cè)的接口給攔截了,如果沒(méi)有做任何配置的話(huà),答案是會(huì)的,會(huì)出現(xiàn)這種情況“你想登錄的話(huà)就得先登錄,不能登錄的話(huà)就不能登錄”,我們只需要做一些配置就可以了——
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginCheckInterceptor)
.addPathPatterns("/user/**")
.excludePathPatterns("/user/login")
.excludePathPatterns("/user/avatar")
.excludePathPatterns("/user/register")
.excludePathPatterns("/user/changePasswordByEmail")
.excludePathPatterns("/auth/**");
}這里的意思是.excludePathPatterns("/user/login")這類(lèi)的接口不會(huì)被攔截器攔截,就算你沒(méi)有登錄也可以進(jìn)去
controller層
@ApiOperation("用戶(hù)登錄")
@PostMapping("/login")
private Result login(@RequestBody UserLogin userLogin){
log.info("員工登錄:{}",userLogin);
User user=service.login(userLogin);
//登錄成功
if (user!=null){
HashMap<String, Object> claims = new HashMap<>();
claims.put("id",user.getId());
claims.put("email",user.getEmail());
String jwt= JwtUtils.generateJwt(claims);//jwt中包含了登錄信息
return Result.success(jwt);
}
return Result.error("用戶(hù)名或密碼錯(cuò)誤");
}正如前文所訴,這里規(guī)定了用戶(hù)登錄接口的url,其中userLogin便是前端傳來(lái)的數(shù)據(jù),里面封裝了前端傳來(lái)的郵箱和密碼,后端封裝的userLogin類(lèi)如下
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserLogin implements Serializable {
public String email;
public String password;
}service層
@Override
public User login(UserLogin userLogin) {
return dao.getByUserEmailAndPassword(userLogin);
}這里也是直接選擇調(diào)用了dao層的方法去數(shù)據(jù)庫(kù)查詢(xún)數(shù)據(jù)了
dao層
<select id="getByUserEmailAndPassword" resultType="com.qac.entity.User">
select *from user where email =#{email} AND password = #{password};
</select>查一下數(shù)據(jù)庫(kù)中有沒(méi)有這個(gè)用戶(hù)
在以上的后端處理完之后,我們來(lái)梳理一下傳給前端的是什么數(shù)據(jù),我們會(huì)發(fā)現(xiàn),后端最終傳給前端的都是類(lèi)似于Result.success(jwt)的數(shù)據(jù),這里解釋一下Result是封裝的一個(gè)類(lèi),其中的.success()也是其中的一個(gè)靜態(tài)方法,如下
package com.qac.result;
import lombok.Data;
import java.io.Serializable;
/**
* 后端統(tǒng)一返回結(jié)果
* @param <T>
*/
@Data
public class Result<T> implements Serializable {
private Integer code; //編碼:1成功,0和其它數(shù)字為失敗
private String msg; //錯(cuò)誤信息
private T data; //數(shù)據(jù)
public static <T> Result<T> success() {
Result<T> result = new Result<T>();
result.code = 1;
return result;
}
public static <T> Result<T> success(T object) {
Result<T> result = new Result<T>();
result.data = object;
result.code = 1;
return result;
}
public static <T> Result<T> error(String msg) {
Result result = new Result();
result.msg = msg;
result.code = 0;
return result;
}
}有了這個(gè)類(lèi)以后后端傳給前端的數(shù)據(jù)就是“規(guī)規(guī)矩矩”的了,就不會(huì)出現(xiàn)本文引言中前端工程師的抱怨了,OK,前端工程師看到這里就可以撤了(開(kāi)玩笑的,千萬(wàn)別撤)。
當(dāng)前端調(diào)用了后端的這個(gè)接口之后,它會(huì)得到什么具體的數(shù)據(jù)呢?可以在controller層看到,傳的有效數(shù)據(jù)就是那個(gè)生成的jwt,其中包涵了用戶(hù)的email,id等內(nèi)容,還有就是告訴前端是登錄成功了還是失敗了,OK,后端傳來(lái)了這么完美的數(shù)據(jù),讓我們看看前端工程師如何處理吧
前端部分
這次的前端工程師倒是很開(kāi)心,因?yàn)楹蠖瞬恍枰脩?hù)給他傳用戶(hù)的主鍵ID了
嗯......不開(kāi)玩笑,前端寫(xiě)一個(gè)登錄頁(yè)面就寫(xiě)了五百多行代碼,這里就只挑重要的代碼展示了
1.后端登錄接口的封裝
import request from '@/utils/request'
import type { userLogin } from '@/types/userLogin'
// 枚舉管理注冊(cè)模塊接口地址
enum API {
LOGIN = '/user/login',
}
export const goLogin = (email: string, password: string) => {
return request.post(API.LOGIN, { email, password })
}這里有人可能會(huì)問(wèn),request是個(gè)啥,其實(shí)它是封裝過(guò)的一個(gè)axios對(duì)象,這個(gè)一兩句說(shuō)不清楚,自己先看一下代碼——
import router from '@/router'
import axios from 'axios'
import {globals} from "@/main"
const serverUrl=globals.$config?.serverUrl || 'http://120.27.247.221:8090'
const request = axios.create({
baseURL: serverUrl,
timeout: 200000
})
//請(qǐng)求攔截器
request.interceptors.request.use((config: any) => {
// 可以通過(guò)請(qǐng)求頭攜帶公共參數(shù)
config.headers['content-type'] = 'application/json'
const token = JSON.parse(localStorage.getItem('user') || '{}')
config.headers['token'] = token
return config
})
//相應(yīng)攔截器
request.interceptors.response.use(
(response: any) => {
if (response.data.msg === 'NOT_LOGIN') {
router.push('/home')
}
return response.data
},
(error: { message: string | undefined }) => {
//處理http網(wǎng)絡(luò)錯(cuò)誤
return Promise.reject(new Error(error.message))
}
)
//對(duì)外暴露axios
export default request這一部分的信息量也不小,我原理不說(shuō),給大家說(shuō)一下這一段代碼是什么作用
const serverUrl=globals.$config?.serverUrl || 'http://120.27.247.221:8090'
這個(gè)是獲取后端接口的地址,現(xiàn)在說(shuō)太早,等項(xiàng)目部署部分再說(shuō)哈
const request = axios.create({
baseURL: serverUrl,
timeout: 200000
})后端的同學(xué)可以認(rèn)為這是創(chuàng)建了一個(gè)axios類(lèi)的對(duì)象,然后又給他重寫(xiě)了一下方法,之后我們用request就是用axios,而且是我們自己的axios
//請(qǐng)求攔截器
request.interceptors.request.use((config: any) => {
// 可以通過(guò)請(qǐng)求頭攜帶公共參數(shù)
config.headers['content-type'] = 'application/json'
const token = JSON.parse(localStorage.getItem('user') || '{}')
config.headers['token'] = token
return config
})- 這一段給你們看看詳細(xì)的解釋?zhuān)乙膊恢涝趺唇M織語(yǔ)言了
- 這行代碼從瀏覽器的
localStorage中獲取名為user的數(shù)據(jù),并嘗試將其解析為 JavaScript 對(duì)象。如果localStorage中沒(méi)有user,則使用空對(duì)象作為默認(rèn)值。 - 這里假設(shè)
user對(duì)象中可能包含一個(gè)token屬性,通常用于身份驗(yàn)證。
//相應(yīng)攔截器
request.interceptors.response.use(
(response: any) => {
if (response.data.msg === 'NOT_LOGIN') {
router.push('/home')
}
return response.data
},
(error: { message: string | undefined }) => {
//處理http網(wǎng)絡(luò)錯(cuò)誤
return Promise.reject(new Error(error.message))
}
)這里可以再調(diào)用方法時(shí)判斷瀏覽器本地有沒(méi)有有效的token,如果沒(méi)有就router.push('/home'),就是讓他返回登錄頁(yè)面
2.登錄功能的部分html代碼和方法的調(diào)用
<form class="form1" @submit.prevent="handleLogin" v-show="login&&!register">
<p class="heading">登錄</p>
<el-input
v-model="user.email"
style="width: 250px; height: 45px"
placeholder="郵箱"
/>
<div v-show="!validateEmail(user.email)" class="warning">請(qǐng)輸入有效的郵箱格式!</div>
<el-input
v-model="user.password"
style="width: 250px; height: 45px"
type="password"
placeholder="密碼"
show-password
/>
<div v-show="!validatePassword(user.password)" class="warning">
密碼至少8個(gè)字符,包含數(shù)字和特殊字符!
</div>
<div class="align">
<span class="function" @click="toFind">找回密碼</span>
<span class="function" @click="toRegister">注冊(cè)賬號(hào)</span>
</div>
<button class="btn" type="submit" >登錄</button>
</form>這里重點(diǎn)看一下登錄的按鈕(最后一行代碼哈),這意味著點(diǎn)擊之后會(huì)執(zhí)行提交表單的方法,也就是方法handleLogin()(第一行代碼哈)
下面是handleLogin()方法
const handleLogin = async () => {
if (validateEmail(user.email) && validatePassword(user.password)) {
try {
const response = await goLogin(user.email, user.password)
console.log('Login successful:', response)
if (response.data == null) {
alert('郵箱或密碼錯(cuò)誤')
} else {
localStorage.setItem('user', JSON.stringify(response.data))
alert('登錄成功')
router.push('mainArticle')
}
} catch (error) {
console.error('Login failed:', error)
}
} else {
alert('郵箱或密碼格式錯(cuò)誤')
}
}可以看到,調(diào)用的封裝后端登錄接口的方法----goLogin,并且將用戶(hù)輸入的email、password傳到了后端,此外,把response.data保存到了本地,也就是把后端傳來(lái)的jwt保存到了本地,登錄成功之后就router.push('mainArticle'),即跳轉(zhuǎn)到主頁(yè)
至此前后端在登錄功能上的本地聯(lián)調(diào)就此結(jié)束
現(xiàn)在只是寫(xiě)了登錄接口前后端邏輯,以后可能會(huì)把,個(gè)人信息管理或者是富文本編輯器的功能再寫(xiě)出來(lái),也可能不會(huì)寫(xiě),或許永遠(yuǎn)不會(huì)寫(xiě),或許明天就寫(xiě)
七、上線(xiàn)部署
上線(xiàn)部署應(yīng)該是最簡(jiǎn)單的一部分,但同時(shí)也是最神秘的一部分或者說(shuō)是最難找資料的一部分,這里我把完整的流程給大家
準(zhǔn)備工作
1.準(zhǔn)備一個(gè)云服務(wù)器,我選擇的是阿里云ECS云服務(wù)器
這里給大家留一個(gè)小門(mén)檻,去搞一臺(tái)ECS云服務(wù)器(實(shí)際上是本人不想寫(xiě)這個(gè)了)
2.先進(jìn)入你的這個(gè)頁(yè)面哈,點(diǎn)擊“安全組”

3.然后點(diǎn)擊管理規(guī)則

4.把下面的端口全都放行了,點(diǎn)那個(gè)快速添加的按鈕可以放行端口噢

5.接下來(lái)就可以遠(yuǎn)程連接你的云服務(wù)器了
首次連接應(yīng)該會(huì)讓你設(shè)置密碼啥的,反正很簡(jiǎn)單,這里就跳過(guò)了哈

6.進(jìn)入云服務(wù)器,在云服務(wù)器上下載一個(gè)寶塔面板,去瀏覽器上就可以下載
網(wǎng)址是 寶塔面板下載,免費(fèi)全能的服務(wù)器運(yùn)維軟件 (bt.cn)

7.因?yàn)槲业姆?wù)器是windows所以下載Windows版的
注:當(dāng)時(shí)也是windows版的教程很少,我也廢了很大功夫

8.打開(kāi)之后應(yīng)該是這樣的,可以自己設(shè)置賬號(hào)密碼

9.設(shè)置好之后在自己的電腦瀏覽器打開(kāi)面板地址(是在自己的瀏覽器不是云服務(wù)器中噢)
注:我這里密碼忘了,不讓我登了,大家一定要記好自己的密碼呀

OK,解決了,隨手告訴大家怎么找回密碼吧,下面便是教程,親測(cè)有用如何修改Windows面板密碼教程 - Windows面板 - 寶塔面板論壇 (bt.cn)
登入之后是這個(gè)界面

10.點(diǎn)擊軟件商店去下載一些需要的東西,這里大家抄作業(yè)就行了
注:如果大家碰到了下載慢的問(wèn)題,重新啟動(dòng)面板就可以解決了

11.之后我們來(lái)配置一下數(shù)據(jù)庫(kù),添加一個(gè)數(shù)據(jù)庫(kù)

12.點(diǎn)擊管理,會(huì)跳轉(zhuǎn)到新的網(wǎng)址(如果報(bào)404等錯(cuò)誤有兩個(gè)原因,1.安全組沒(méi)有放行,2.沒(méi)有按照我的軟件去安裝)

13.導(dǎo)入你的sql文件

14.秉著教人教到會(huì)的原則,我教一下大家如何去導(dǎo)出本地?cái)?shù)據(jù)庫(kù)到sql文件
右鍵表格-->導(dǎo)入/導(dǎo)出-->將數(shù)據(jù)導(dǎo)出到文件

15.按照我這個(gè)選擇去導(dǎo)出哈,不然容易導(dǎo)出奇奇怪怪的數(shù)據(jù)

16.jdk的配置
寶塔上只能下載jdk1.8或以下,但是我們的版本是jdk17,所以我們要在本地上傳jdk17

點(diǎn)擊打開(kāi)即可上傳
后端部署
1.我們部署時(shí)部署的是項(xiàng)目的jar包,我們可以通過(guò)Maven項(xiàng)目的package功能得到j(luò)ar包,點(diǎn)擊package進(jìn)行打包

2.打包完成后我們拿出jar包,可能會(huì)生成多個(gè)jar包,我們只拿有配置文件的模塊的那個(gè)jar包,這里是Server模塊

3.把項(xiàng)目的配置文件也拿出來(lái),這個(gè)項(xiàng)目是application-dev.yml

4.將java項(xiàng)目的jdk環(huán)境配置一下(之前上傳的jdk17)

5.自己把jar包和配置文件上傳到云服務(wù)器哈,和上傳jdk的方法一樣
6.改一下項(xiàng)目的配置文件,將數(shù)據(jù)庫(kù)的配置該成云服務(wù)器上的數(shù)據(jù)庫(kù),劃紅圈的那一部分哈

7.部署JAVA項(xiàng)目
點(diǎn)擊添加Java項(xiàng)目,按照如下配置即可,注意項(xiàng)目的jar路徑要是你的jar包的路徑,而且配置文件要和jar包的位置同級(jí),端口要是沒(méi)有被占用的端口

至此后端就部署完成了!
前端部署
1.首先要改一下你的前端項(xiàng)目跨域配置
這里的目標(biāo)url要是http://+你的云服務(wù)器公網(wǎng)ip+你的后端項(xiàng)目端口

2.之后將前端項(xiàng)目打包
在終端打開(kāi)輸入指令
npm run build
如果你的項(xiàng)目又語(yǔ)法錯(cuò)誤就先改正,不然是打包不了的

3.將打包好的文件上傳到云服務(wù)器
就是畫(huà)圈的文件“dist”

4.部署PHP項(xiàng)目
點(diǎn)擊添加站點(diǎn),域名即是你的云服務(wù)器公網(wǎng)ip,根目錄是你上傳“dist”的目錄
這里注意一下dist目錄下不能再有一個(gè)dist目錄,如果會(huì)報(bào)錯(cuò)的話(huà),最好檢查一下自己的目錄結(jié)構(gòu)是否正確

至此線(xiàn)上部署完成,完結(jié)撒花嘍!
八、結(jié)語(yǔ)
本文介紹了springboot+vue項(xiàng)目的從代碼編寫(xiě)到上線(xiàn)的全流程,制作不易,如果幫助到大家的話(huà)可以高抬貴手給一個(gè)贊,感謝
以后有機(jī)會(huì)的話(huà),我會(huì)給大家將一些其他內(nèi)容,比如大數(shù)據(jù)項(xiàng)目全流程,或者AI編程之類(lèi)的,后會(huì)有期了!
到此這篇關(guān)于springboot+vue項(xiàng)目從第一行代碼到上線(xiàn)部署全流程的文章就介紹到這了,更多相關(guān)springboot vue項(xiàng)目上線(xiàn)部署內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot+Vue項(xiàng)目部署上線(xiàn)的實(shí)現(xiàn)示例
- SpringBoot + Vue 項(xiàng)目部署上線(xiàn)到Linux 服務(wù)器的教程詳解
- SpringBoot+Vue項(xiàng)目打包部署完整步驟教程
- vue打包部署到springboot的實(shí)現(xiàn)示例
- 打包部署若依(RuoYi)SpringBoot后端和Vue前端圖文教程
- vue打包部署到springboot并通過(guò)tomcat運(yùn)行的操作方法
- SpringBoot+Vue 前后端合并部署的配置方法
- 部署vue+Springboot前后端分離項(xiàng)目的步驟實(shí)現(xiàn)
相關(guān)文章
json-lib將json格式的字符串,轉(zhuǎn)化為java對(duì)象的實(shí)例
下面小編就為大家?guī)?lái)一篇json-lib將json格式的字符串,轉(zhuǎn)化為java對(duì)象的實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-03-03
解讀動(dòng)態(tài)數(shù)據(jù)源dynamic-datasource-spring-boot-starter使用問(wèn)題
這篇文章主要介紹了解讀動(dòng)態(tài)數(shù)據(jù)源dynamic-datasource-spring-boot-starter使用問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
SpringBoot使用Nacos進(jìn)行application.yml配置管理詳解
Nacos是阿里巴巴開(kāi)源的一個(gè)微服務(wù)配置管理和服務(wù)發(fā)現(xiàn)的解決方案,下面我們來(lái)看看在SpringBoot中如何使用Nacos進(jìn)行application.yml配置管理吧2025-03-03
基于Spring?Boot的線(xiàn)程池監(jiān)控問(wèn)題及解決方案
這篇文章主要介紹了基于Spring?Boot的線(xiàn)程池監(jiān)控方案,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03
spring boot配置多個(gè)請(qǐng)求服務(wù)代理的完整步驟
這篇文章主要給大家介紹了關(guān)于spring boot配置多個(gè)請(qǐng)求服務(wù)代理的完整步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用spring boot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11
Springboot重寫(xiě)addInterceptors()方法配置攔截器實(shí)例
這篇文章主要介紹了Springboot重寫(xiě)addInterceptors()方法配置攔截器實(shí)例,spring?boot拋棄了復(fù)雜的xml配置,我們可以自定義配置類(lèi)(標(biāo)注@Configuration注解的類(lèi))來(lái)實(shí)現(xiàn)WebMvcConfigurer接口,并重寫(xiě)addInterceptors()方法來(lái)配置攔截器,需要的朋友可以參考下2023-09-09
MybatisPlus插件自動(dòng)維護(hù)更新和創(chuàng)建時(shí)間方式
這篇文章主要介紹了MybatisPlus插件自動(dòng)維護(hù)更新和創(chuàng)建時(shí)間方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-04-04
記錄一次connection reset 錯(cuò)誤的解決全過(guò)程
這篇文章主要介紹了記錄一次connection reset 錯(cuò)誤的解決全過(guò)程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04

