SpringBoot+Vue项目实例开发及部署
目錄
一、SpringBoot快速上手
1、SpringBoot介紹
2、SpringBoot特點
3、快速創建SpringBoot應用
4、SpringBoot開發熱部署
二、Web開發基礎
1、Web入門
2、控制器
3、路由映射
4、參數傳遞
三、Web開發進階
1、靜態資源訪問
2、文件上傳原理
3、攔截器
四、構建RESTful服務
1、RESTful介紹
2、RESTful特點
3、RESTful API
4、HTTP狀態碼
5、SpringBoot實現RESTful API
6、Swagger
使用Swagger生成Web API文檔
配置Swagger
使用Swagger2進行接口測試
Swagger常用注解
五、MybatisPlus快速上手
1、ORM介紹
2、MyBatis-Plus介紹
添加依賴
全局配置
Mybatis CRUD 注解
Mybatis CRUD 操作
Mybatis-Plus 注解
Mybatis-Plus CRUD操作
六、多表查詢及分頁查詢
多表查詢
多表查詢操作
條件查詢
分頁查詢
七、Vue框架快速上手
1、前端環境準備
2、Vue框架介紹
MVVM模式:
Vue快速入門:
八、Vue組件化開發
1、NPM簡介
2、NodeJS安裝
3、NPM使用
4、Vue Cli使用
5、組件化開發
6、組件的構成
九、第三方組件 Element-UI
1、組件間的傳值
2、Element-UI介紹
3、第三方圖標庫
十、Axios網絡請求
?1、Axios簡介
2、發送網絡請求
發送GET請求
發生POST請求
異步回調問題(async/await)
其他請求方式
與Vue整合
3、Axios使用
為什么會出現跨域問題
跨域問題解決方法
十一、前端路由VueRouter
1、VueRouter安裝與使用
VueRouter安裝:
創建路由組件
聲明路由連接和占位標簽
創建路由模塊
掛載路由模塊
實現效果
2、VueRouter進階
路由重定向
嵌套路由
動態路由
編程式導航
導航守衛
十二、狀態管理VueX
1、VueX介紹
2、狀態管理
最簡單的store
State
對象展開運算符
Getter
Mutation
Action
Module
總結
十三、前端數據模擬Mock.js
1、Mock.js介紹
2、基本使用
3、核心方法
4、數據生成規則
十四、企業級后臺集成方案
1、vue-element-admin介紹
十五、跨域認證
1、Session認證
2、Token認證
3、JWT
4、JWT組成
Header
Payload
Signature
5、JWT的特點
6、JWT的實現
十七、Springboot+Vue云端環境配置
1、安裝MySQL
2、安裝Nginx
3、配置JDK
十八、Springboot+Vue項目部署
1、部署Vue項目
2、打包運行Java程序
一、SpringBoot快速上手
1、SpringBoot介紹
- SpringBoot是由Pivotal團隊提供的基于Spring的全新框架,旨在簡化Spring應用的初始搭建和開發過程。
- SpringBoot是所有基于Spring開發項目的起點。
- SpringBoot就是盡可能地簡化應用開發的門檻,讓應用開發、測試、部署變得更加簡單。
2、SpringBoot特點
- 遵循“約定優于配置”的原則,只需要很少的配置或使用默認的配置。
- 能夠使用內嵌的Tomcat、Jetty服務器,不需要部署war文件。
- 提供定制化的啟動器starter,簡化Maven配置,開箱即用。
- 純Java配置,沒有代碼生成,也不需要XML配置。
- 提供了生產級的服務監控方案,如安全監控、應用監控、健康檢測等。
3、快速創建SpringBoot應用
- 利用IDEA的Spring Initializr創建SpringBoot項目
- 我們開發web項目,所以添加web依賴
- 這樣就會自動將web項目所需要的依賴下載并引入
- 點擊完成項目就新建成功
- 接下來測試一下項目運行
- 在項目新建一個controller包,里面專門用來存放controller控制器
- @RestController public class IndexController {@GetMapping("/index")public String index(){return "歡迎訪問首頁";} }
- 接著運行主程序?
- 接下來查看控制臺
- 這樣就運行成功
- 我們在瀏覽器訪問一下http://localhost:8888/index
- 這樣就說明項目可以正常訪問了
4、SpringBoot開發熱部署
- 在實際的項目開發調試過程中會頻繁地修改后臺類文件,導致需要重新編譯、重新啟動,整個過程非常麻煩,影響開發效率。
- SpringBoot提供了spring-boot-devtools組件,使得無須手動重啟SpringBoot應用即可重新編譯、啟動項目,大大縮短編譯啟動的時間。
- devtools會監聽classpath下文件的變動,觸發Restart類加載器重新加載該類,從而實現類文件和屬性文件的熱部署。
- 并不是所有的更改都需要重啟應用(如靜態資源、視圖模板),可以通過設置spring.devtools.restart.exclude屬性來指定一些文件或目錄的修改不用重啟應用。
- 實現步驟:
- 在pom.xml配置文件中添加dev-tools依賴。
- 使用optional=true表示依賴不會傳遞,即該項目依賴devtools;其他項目如果引入此項目生成的jar包,則不會包含devtools。
- <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional></dependency>
- 依賴引入后在application.yml中配置devtools
- spring:devtools:restart:enabled: true #熱部署生效additional-paths: src/main/java #設置重啟目錄exclude: static/** #設置classpath目錄下的WEB-INF文件夾內容修改不重啟
- 配置后還不行,因為IDEA還需要在設置中配置項目自動運行
- 打開Settings頁面,在左邊的菜單欄依次找到
- Build、Execution、Deployment、Compile,勾選Build project automatically
- 按Ctrl+Shift+Alt+/ 快捷鍵調出Maintenance頁面,單擊Registry,勾選
- compiler.automake.allow.when.app.running復選框
- 如果IDEA版本比較搞,可能沒有出現這個復選框,那么嘗試以下方法
- 在Settings中拉到最下面找到Advanced Settings
- 然后找到Allow auto-make to start even if developed application is currently running
- 然后勾選
- 這樣開發熱部署就配置完成。
二、Web開發基礎
1、Web入門
- SpringBoot將傳統Web開發的MVC、JSON、Tomcat等框架整合,提供了spring-boot-starter-web組件,簡化了Web應用配置。
- 創建SpringBoot項目勾選SpringWeb選項后,會自動將spring-boot-starter-web組件加入到項目中。
- spring-boot-starter-web啟動器主要包括WEB、WebMVC、JSON、Tomcat等基礎依賴組件,作用是提供Web開發場景所需要的所有底層依賴。
- WebMVC為Web開發的基礎框架,JSON為JSON數據解析組件,Tomcat為自帶的容器依賴。
- <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
2、控制器
- SpringBoot提供了@Controller和@RestController兩種注解來標識此類負責接收和處理HTTP請求
- 如果請求的是頁面和數據,使用@Controller注解即可;如果只是請求數據,則可以使用@RestController注解
- @Controller的用法
- 下面示例中返回了index頁面和name的數據,在前端頁面中可以通過${name}參數獲取后臺返回的數據并顯示。
- @Controller通常與Thymeleaf模板引擎結合使用
- @Controller public class IndexController {@GetMapping("/index")public String index(ModelMap map){map.addAttribute("name","張三");return "index";} }
- @RestController的用法
- 默認情況下,@RestController注解會將返回的對象數據轉換為JSON格式
- @RestController public class IndexController {@GetMapping("/user")public User getUser(){User user = new User();user.setUsername("張三");user.setPassword("123");return user;} }
3、路由映射
- @RequestMapping注解主要負責URL的路由映射。它可以添加在Controller類或者具體的方法上。
- 如果添加在Controller類上,則這個Controller中的所有路由映射都將會加上此映射規則,如果添加在方法上,則只對當前方法生效。
- @RequestMapping注解包含很多屬性參數來定義HTTP的請求映射規則。常用的屬性參數如下:
- value:請求URL的路徑,支持URL模板、正則表達式
- method:HTTP請求方法(POST/GET)
- consumes:請求的媒體類型(Content-Type),如application/json
- produces:響應的媒體類型
- params,headers:請求的參數及請求頭的值
- value和method比較常用,其他三個比較少用
- @RequestMapping的value屬性用于匹配URL映射,value支持簡單表達式
- @RequestMapping("/user")
- @RequestMapping支持使用通配符匹配URL,用于統一映射某些URL規則類似的請求:
- @RequestMapping("/getJson/*.json"),當在瀏覽器中請求/getJson/a.json或者/getJson/b.json時,都會匹配到后臺的json方法。
- @RequestMapping的通配符匹配非常簡單實用,支持"*"、"?"、"**"等通配符:
- "*":匹配任意字符;
- "**":匹配任意路徑;
- "?":匹配單個字符。
- 有通配符的優先低于沒有通配符的,比如/user/add.json比/user/*.json優先匹配。
- 有"**"通配符的優先級低于"*"通配符的。
4、參數傳遞
- @RequestParam將請求參數綁定到控制器的方法參數上,接收的參數來自HTTP請求體或請求URL的QueryString,當請求的參數名稱與Controller的業務方法參數名稱一致時,@RequestParam可以省略。
- @RequestParam注解一添加,這個方法就必須加參數,如果URL沒有參數則會報錯。如果想解除可不添加參數,可以在注解后面加上required=false,如:
- @RequestParam( value = "xxx",required = false)
- @RestController public class IndexController {// http://localhost:8888/index?nickname=zhangsan@GetMapping("/index")public String index(@RequestParam("nickname") String name){return "你好"+name;} }
- 上面的代碼請求參數和方法參數名稱不一致,所以需要使用@RequestParam繼續綁定
- @RestController public class IndexController {// http://localhost:8888/index?nickname=zhangsan@GetMapping("/index")public String index(String nickname){return "你好"+nickname;} }
- 上面的代碼請求參數和方法參數名稱一致,所以不需要使用@RequestParam進行綁定
- @RestController public class IndexController {// http://localhost:8888/index?nickname=zhangsan&phone=123@GetMapping("/index")public String index(String name,String phone){return "你好"+name+phone;} }
- 上面代碼是請求多個參數,URL使用“&”進行拼接,方法上再增加一個參數
- @PathVaraible:用于處理動態的URL,URL的值可以作為控制器中處理方法的參數。
- 如果是Rest風格的URI,比如動態URI,/xxxx/yyyyy,其中yyyy代表一個ID,而這個ID可能是一個數據用用戶表的ID,那么就需要用@PathVaraible將該URI的值傳給controller方法參數
- @GetMapping("/getUser/{id}")public String getUser(@PathVariable String id){System.out.println("id->"+id);return "getUser";}
- @RequestBody接收的參數是來自requestBody中,即請求體。一般用于處理非Content-Type:application/x-www-form-urlencoded編碼格式的數據:
- 比如:application/json、application/xml等類型的數據。?
- // http://localhost:8888/getUser@PostMapping("/getUser")public String getUser(@RequestBody User user){System.out.println(user);return "getUser";}
三、Web開發進階
1、靜態資源訪問
- 使用IDEA創建SpringBoot項目,會默認創建classpath:/static/目錄,靜態資源一般放在這個目錄下面即可。
- 如果默認的靜態資源過濾策略不能滿足開發需求,也可以自定義靜態資源過濾策略。
- 過濾規則為/static/**,靜態資源位置為classpath:/static/
- 在appliction.yml文件中直接定義過濾規則和靜態資源位置:
- spring:mvc:static-path-pattern: /static/**web:resources:static-locations: classpath:/static/ #靜態資源
2、文件上傳原理
- 表單的enctype屬性規定在發送到服務器之前應該如何對表單數據進行編碼。
- 當表單的enctype="application/x-www-form-urlencoded"(默認)時,form表單中的數據格式為:key=value&key=value
- 當表單的enctype="multipart/form-data"時,其傳輸數據形式如下:
-
SpringBoot實現文件上傳功能?
-
Springboot工程嵌入的tomcat限制了請求的文件大小,每個文件的配置最大為1MB,單次請求的文件的總數不能大于10MB。
-
要更改這個默認值需要在配置文件application.yml中加入兩個配置:
- spring:servlet:multipart:max-file-size: 10GB #文化最大10Gmax-request-size: 10GB #單次請求文件總數不能超過10G
-
當表單的enctype="multipart/form-data"時,可以使用MultipartFile獲取上傳的文件數據,再通過transferTo方法將其寫入磁盤中:
- @RestController public class FileController {/*** 默認定位到的當前用戶目錄("user.dir")(即工程根目錄)* JVM就可以據"user.dir" + "你自己設置的目錄" 得到完整的路徑(即絕對路徑)*/// private static final String UPLOADED_FOLDER = System.getProperty("user.dir")+"/upload/";@PostMapping("/upload")public String upload(MultipartFile file, HttpServletRequest request)throws Exception{System.out.println("文件大小:"+file.getSize());System.out.println("文件的ContentType:"+file.getContentType());System.out.println("文件的初始名字:"+file.getOriginalFilename());String path = request.getServletContext().getRealPath("/upload/");System.out.println("獲取web服務器的運行目錄:"+path);saveFile(file,path);return "upload success";}public void saveFile(MultipartFile f,String path)throws IOException{File upDir = new File(path);if (!upDir.exists()){upDir.mkdir();}File file = new File(path+f.getOriginalFilename());f.transferTo(file);} }
-
3、攔截器
- 攔截器在Web系統中非常常見,對于某些全局統一的操作,我們可以把它提取到攔截器中實現。總結起來,攔截器大致有以下幾種使用場景:
- 權限檢查:如登錄檢測,進入處理程序檢測是否登錄,如果沒有,則直接返回登錄頁面。
- 性能監控:有時系統在某段時間莫名其妙很慢,可以通過攔截器在進入處理程序之前記錄開始時間,在處理完后記錄結束時間,從而得到該請求的處理時間。
- 通用行為:讀取cookies得到用戶信息并將用戶對象放入請求,從而方便后續流程使用,還有提取Locale、Theme信息等,只要是多個處理程序都需要的,即可使用攔截器實現。
- Springboot定義了HandlerInterceptor接口來實現自定義攔截器的功能
- HandlerInterceptor接口定義了preHandle、postHandle、afterCompletion三種方法,通過重寫這三種方法實現請求前、請求后等操作。
- 攔截器定義:
- public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("LoginInterceptor");return true;} }
- 攔截器注冊:
- addPathPatterns方法定義攔截的地址
- excludePathPatterns定義排除某些地址不被攔截
- 添加的一個攔截器沒有addPathPattern任何一個url則默認攔截所有請求
- 如果沒有excludePathPatterns任何一個請求,則默認不放過如何一個請求。
- @Configuration // Configuration必須要添加 public class WebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()); // registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/user/**"); // 攔截所有user下面的接口} }
四、構建RESTful服務
1、RESTful介紹
-
RESTful是目前流行的互聯網軟件服務架構設計風格。
-
REST(Representational State Transfer,表述性狀態轉移) 一詞是由Roy Thomas Fielding在2000年的博士論文中提出的,它定義了互聯網軟件服務的架構原則,如果一個架構符合REST原則,則稱之為RESTful架構。
-
REST并不是一個標準,它更像一組客戶端和服務端交互時的架構理念和設計原則,基于這種架構理念和設計原則的Web API更加簡潔、更有層次。
2、RESTful特點
- 每一個URL代表一個資源。
- 客戶端使用GET、POST、PUT、DELETE四種表示操作方式的動詞對服務端資源進行操作:
- GET用于獲取資源
- POST用于新建資源(也可以用于更新資源)
- PUT用于更新資源
- DELETE用于新建資源
- 通過操作資源的表現形式來實現服務端請求操作。
- 資源的表現形式是JSON或HTML。
- 客戶端與服務端之間的交互在請求之間是無狀態的,從客戶端到服務端的每個請求都包含必需的信息。
3、RESTful API
- 符合RESTful規范的Web API需要具備如下兩個關鍵特性:
- 安全性:安全的方法被期望不會產生如何副作用,當我們使用GET操作獲取資源時,不會引起資源本身的改變,也不會引起服務器狀態的改變。
- 冪等性:冪等的方法保證了重復進行一個請求和一次請求的效果相同(并不是指響應總是相同的,而是指服務器上資源的狀態從第一次請求后就不再改變了),在數學上冪等性是指N次變換和一次變換相同。
4、HTTP狀態碼
- HTTP狀態碼就是服務器向用戶返回的狀態碼和提示信息,客戶端的每一次請求,服務器都必須回應,回應包括HTTP狀態碼和數據兩部分
- HTTP定義了40個標準狀態碼。可用于傳達客戶端請求的結果。狀態碼分為以下五個類別:
- 1xx:信息,通信傳輸協議級信息
- 2xx:成功,表示客戶端的請求已成功接受
- 3xx:重定向,表示客戶端必須執行一些其他操作才能完成其請求
- 4xx:客戶端錯誤,此類錯誤狀態碼指向客戶端
- 5xx:服務器錯誤,服務器負責這些錯誤狀態碼
5、SpringBoot實現RESTful API
- 在RESTful架構中,每個網址代表一種資源,所以URL中建議不要包含動詞,只包含名詞即可,而且所用的名詞往往與數據庫的表格名對應
- 用戶管理模塊API示例:
-
HTTP Method接口地址接口描述 POST /user 創建用戶 GET /user/id 根據id獲取用戶信息 PUT /user 更新用戶 DELETE /user/id 根據id刪除對應用戶
-
- ???????? @RestController public class UserController {// @PathVariable:注:路由內屬性與形參相同可簡寫,// 若不相同,則需要加上對應的名稱綁定,如:@PathVariable("id")@GetMapping("/user/{id}")public String getUserById(@PathVariable int id){System.out.println(id);return "根據ID獲取用戶信息";}@PostMapping("/user")public String save(User user){return "添加用戶";}@PutMapping("/user")public String update(User user){return "更新用戶";}@DeleteMapping("/user/{id}")public String deleteById(@PathVariable int id){System.out.println(id);return "根據id刪除用戶";} }
6、Swagger
- Swagger是一個規范和完整的框架,用于生成、描述、調用和可視化RESTful風格的Web服務,是非常流行的API表達工具。
- Swagger能夠自動生成完善的RESTful API文檔,同時并根據后臺代碼的修改同步更新,同時提供完整的測試頁面來調試API。
-
使用Swagger生成Web API文檔
- 在Springboot項目中集成Swagger很簡單,只需要在項目中引入springfox-swagger2和springfox-swagger-ui依賴即可。
- <!-- swagger --><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.9.2</version></dependency><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId><version>2.9.2</version></dependency>
-
配置Swagger
- 注意:swagger版本不一樣,配置也不一樣(本人使用的是2.9.2版本)
- @Configuration // 告訴spring容器,這個類是一個配置類 @EnableSwagger2 // 啟用Swagger2功能 public class Swagger2Config {@Beanpublic Docket createRestApi(){return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()// com包下的所有API都交給Swagger2管理.apis(RequestHandlerSelectors.basePackage("com")).paths(PathSelectors.any()).build();}// API文檔頁面顯示信息private ApiInfo apiInfo(){return new ApiInfoBuilder().title("文件系統項目API") // 標題.description("學習Springboot+Vue項目") // 描述.build();} }
- 如果項目報以下的錯誤
- 這是因為Springboot2.6.x以后與Swagger有版本沖突問題,所以需要在yml文件或properties文件中添加以下配置
- # yml文件: mvc:pathmatch:matching-strategy: ant_path_matcher# properties文件: spring.mvc.pathmatch.matching-strategy=ant_path_matcher
- 這是因為Springboot2.6.x以后與Swagger有版本沖突問題,所以需要在yml文件或properties文件中添加以下配置
-
使用Swagger2進行接口測試
- 啟動項目訪問http://localhost:8888/swagger-ui.html,即可打開自動生成的可視化測試頁面。
- 這個頁面就是成功打開了。
-
Swagger常用注解
- Swagger提供了一系列注解來描述接口信息,包括接口說明、請求方法、請求參數、返回信息等。
五、MybatisPlus快速上手
1、ORM介紹
- ORM(Object Relational Mapping,對象關系映射)是為了解決面向對象與關系數據庫存在的互不匹配現象的一種技術。
- ORM通過使用描述對象和數據庫之間映射的元數據將程序中的對象自動持久化到關系數據庫中。
- ORM框架的本質是簡化編程中操作數據庫的編碼。
2、MyBatis-Plus介紹
- MyBatis是一款優秀的數據持久層ORM框架,被廣泛地應用于應用系統。
- MyBatis能夠非常靈活地實現動態SQL,可以使用XML或注解來配置和映射原生信息,能夠輕松地將Java的POJO(Plain Ordinary Java Object,普通Java對象)與數據庫中的表和字段進行映射關聯。
- MyBatis-Plus是一個MyBatis的增強工具,在MyBatis的基礎上做了增強,簡化了開發。
-
添加依賴
- <!-- mybatisPlus 依賴 --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.2</version></dependency><!-- mysql驅動依賴 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!-- 數據庫連接池Druid --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.13-SNSAPSHOT</version></dependency>
-
全局配置
- 配置數據庫相關信息。
- spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/mydb?useSSL=falseusername: rootpassword: 646681 mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
- 添加@MapperScan注解
- 配置數據庫相關信息。
-
Mybatis CRUD 注解
-
Mybatis CRUD 操作
- @Mapper public interface UserMapper {// 增加用戶@Insert("insert into user(username,password,birthday) values (#{username},#{password},#{birthday})")int add(User user);// 根據id刪除用戶@Delete("delete from user where id=#{id}")int delete(int id);// 更新用戶信息@Update("update user set username=#{username},password=#{password},birthday=#{birthday} where id=#{id}")int update(User user);// 根據id查詢用戶@Select("select * from user where id=#{id}")User findById(int id);// 查詢所有用戶@Select("select * from user")List<User> findAll();}
- 以上是Mybatis的CRUD操作
-
Mybatis-Plus 注解
- @TableName,當表名與實體類名稱不一致時,可以使用@TableName注解進行關聯。
- @TableField,當表中字段名稱與實體類屬性不一致時,使用@TableField進行關聯。
- @TableId,用于標記表中的主鍵字段,MybatisPlus也提供了主鍵生成策略。
- 以上就是MybatisPlus注解的使用
-
Mybatis-Plus CRUD操作
- 在mapper類中繼承BaseMapper<T>,T 表示要操作的entity對象
- @Mapper public interface UserMapper extends BaseMapper<User> {}
-
要操作User對象,就將User傳給BaseMapper
-
BaseMapper中提供了很多操作,如增刪改查等等。
-
?直接調用查詢方法即可,如需知道更多方法的操作使用,請到Mybatis-Plus官網查看
六、多表查詢及分頁查詢
多表查詢
- 實現復雜關系映射,可以使用@Results注解、@Result注解、@One注解、@Many注解組合完成復雜關系的配置。
-
多表查詢操作
- User類(對應數據庫t_user表)
- @TableName("t_user") public class User {@TableId(type = IdType.AUTO)private int id;private String username;private String password;private String birthday;// 描述用戶的所有訂單@TableField(exist = false) // 表示在數據庫表中不存在,不需要做映射private List<Order> orders;public User() {}public User(String username, String password, String birthday) {this.username = username;this.password = password;this.birthday = birthday;}....省略了getter和setter方法@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", password='" + password + '\'' +", birthday='" + birthday + '\'' +'}';} }
- Order類(對應數據庫t_order表)
- @TableName("t_order") public class Order {private int id;@TableField("order_time")private String orderTime;private double total;@TableField(exist = false)private User user;public Order() {}public Order(String orderTime, double total, User user) {this.orderTime = orderTime;this.total = total;this.user = user;}....省略了getter和setter方法@Overridepublic String toString() {return "Order{" +"id=" + id +", orderTime='" + orderTime + '\'' +", total=" + total +", user=" + user +'}';} }
- UserMapper接口
- @Mapper public interface UserMapper extends BaseMapper<User> {// 根據id查用戶@Select("select * from t_user where id=#{id}")User selectById(int id);// 查詢用戶及其所有的訂單@Select("select * from t_user")@Results({@Result(column = "id",property = "id"), // column:字段,property:映射到實體類的屬性@Result(column = "username",property = "username"),@Result(column = "password",property = "password"),@Result(column = "birthday",property = "birthday"),@Result(column = "id",property = "orders",javaType = List.class,many = @Many(select = "com.org.mapper.OrderMapper.selectByUid")// 通過user的id去orderMapper的selectByUid查詢所以該用戶的訂單)})List<User> selectAllUserAndOrders(); }
- OrderMapper接口
- @Mapper public interface OrderMapper extends BaseMapper<Order> {@Select("select * from t_order where uid=#{uid}")List<Order> selectByUid(int uid);// 查詢所有的訂單,同時查詢訂單用戶@Select("select * from t_order")@Results({@Result(column = "id",property = "id"),@Result(column = "order_time",property = "orderTime"),@Result(column = "total",property = "total"),@Result(column = "uid",property = "user",javaType = User.class,one=@One(select = "com.org.mapper.UserMapper.selectById")// 根據order的uid到UserMapper的selectById方法中查詢該訂單所屬的用戶),})List<Order> selectAllOrderAndUser();}
- UserController
- @RestController public class UserController {@Autowiredprivate UserMapper userMapper;@GetMapping("/user")public List<User> query() {// 查詢用戶及其所有訂單List<User> userList = userMapper.selectAllUserAndOrders();return userList;}}
- OrderController
- @RestController public class OrderController {@Autowiredprivate OrderMapper orderMapper;// 查詢訂單及其所屬的用戶@GetMapping("/order/findAll")public List findAll(){List<Order> orderList = orderMapper.selectAllOrderAndUser();return orderList;} }
- 效果展示如下:
- 查詢所有用戶及其所有訂單
- 查詢訂單及該訂單所屬用戶
條件查詢
- 使用QueryWrapper來創建條件
- 用法如下代碼所示:
- @GetMapping("/user")public List<User> query() {// 條件查詢QueryWrapper<User> queryWrapper = new QueryWrapper<>(); // 創建實例queryWrapper.eq("username","tom"); // 查詢username為tom的用戶List<User> userList = userMapper.selectList(queryWrapper); // 將條件作為參數傳入查詢方法,若無條件,則在方法傳入nullreturn userList;}
- 更多條件如下圖所示:
-
條件條件實現功能例子 eq 等于 = 例: eq("name", "老王")--->name = '老王' ne 不等于 <> 例: ne("name", "老王")--->name <> '老王' gt 大于 > 例: gt("age", 18)--->age > 18 ge 大于等于 >= 例: ge("age", 18)--->age >= 18 lt 小于 < 例: lt("age", 18)--->age < 18 le 小于等于 <= 例: le("age", 18)--->age <= 18 between BETWEEN 值1 AND 值2 例: between("age", 18, 30)--->age between 18 and 30 notBetween NOT BETWEEN 值1 AND 值2 例: notBetween("age", 18, 30)--->age not between 18 and 30 like LIKE '%值%' 例: like("name", "王")--->name like '%王%' notLike NOT LIKE '%值%' 例: notLike("name", "王")--->name not like '%王%' likeLeft LIKE '%值' 例: likeLeft("name", "王")--->name like '%王' likeRight LIKE '值%' 例: likeRight("name", "王")--->name like '王%' isNull 字段 IS NULL 例: isNull("name")--->name is null isNotNull 字段 IS NOT NULL 例: isNotNull("name")--->name is not null in 字段 IN (v0, v1, ...) 例: in("age", 1, 2, 3)--->age in (1,2,3) notIn 字段 NOT IN (value.get(0), value.get(1), ...) 例: notIn("age",{1,2,3})--->age not in (1,2,3) - 更多操作可自行到Mybatis-Plus官網查看
-
分頁查詢
- 編寫配置類
- @Configuration public class MybatisPlusConfig {@Beanpublic MybatisPlusInterceptor paginationInterceptor(){MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL); // 數據庫類型interceptor.addInnerInterceptor(paginationInnerInterceptor);return interceptor;} }
- 編寫分頁代碼
- // 分頁查詢@GetMapping("/user/findByPage")public IPage findByPage(){// 設置起始值及每頁條數Page<User> page = new Page<>(0,2);IPage iPage = userMapper.selectPage(page,null); // 第一個參數是page,第二個參數是查詢條件return iPage;}
- 效果展示
七、Vue框架快速上手
1、前端環境準備
- 編碼工具:VSCode
- 依賴管理:NPM
- 項目構建:VueCli
2、Vue框架介紹
- Vue.js是一套用于構建用戶界面的漸進式框架。
- Vue.js提供了MVVM數據綁定和一個可組合的組件系統,具有簡單、靈活的API。
- 其目標是通過盡可能簡單的API實現響應式的數據綁定和可組合的視圖組件。
-
MVVM模式:
- MVVM是Model-View-ViewModel的縮寫,它是一種基于前端開發的架構模式,其核心是提供對View和ViewModel的雙向數據綁定。
- Vue提供了MVVM風格的雙向數據綁定,核心是MVVM中的VM,也就是ViewModel,ViewModel負責連接View和Model,保證視圖和數據的一致性。
-
Vue快速入門:
- 導入vue.js的script腳本文件
- <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
- 在頁面中聲明一個將要被vue所控制的DOM區域,既MVVM中的View
- <div id="app">{{ message }} </div>
- 創建vm實例對象(vue實例對象)
- const vm = {// 指定數據源:即MVVM中的Modeldata: function(){return{message: 'Hello Vue!'}} }const app = Vue.createApp(hello) app.mount('#app) // 指定當前vue實例要控制頁面的哪個區域
- 本人使用的是Vue3語法,想了解Vue更多知識及操作可到Vue官網學習!!
- 導入vue.js的script腳本文件
八、Vue組件化開發
1、NPM簡介
- NPM(Node Package Manager)是一個NodeJS包管理和分發工具。
- NPM以其優秀的依賴管理機制和龐大的用戶群體,目前已經發展成為整個JS領域的依賴管理工具。
- NPM最常見的用法就是用于安裝和更新依賴。要使用NPM,首先需要安裝Node工具。
2、NodeJS安裝
- NodeJS是一個基于Chrome V8引擎的JavaScript運行時環境。
- Node中包含了NPM包管理工具。
- 下載地址:https://nodejs.org/zh-cn/
3、NPM使用
4、Vue Cli使用
- Vue Cli是Vue官方提供的構建工具,通常稱為腳手架。
- 用于快速搭建一個帶有熱重載(代碼修改后不必刷新頁面即可呈現修改后的效果)及構建生產版本等功能的單頁面應用。
- Vue Cli基于webpack構建,也可以通過項目內的配置文件進行配置。
- 安裝:
- npm install -g @vue/cli
- 具體步驟:
- 以Vue3為例子演示
5、組件化開發
- 組件(Component)是Vue.js最強大的功能之一。組件可以擴展HTML元素,封裝可重用的代碼。
- Vue的組件系統允許我們使用小型、獨立和通常可復用的組件構建大型應用。
6、組件的構成
- Vue中規定組件的后綴名是.vue
- 每個.vue組件都由三部分組成:
- template:組件的模板結構,可以包含HTML標簽及其他的組件
- script:組件的JavaScript代碼
- style:組件的樣式
-
想了解Vue Cli項目的更多知識及操作可到Vue官網學習!!
九、第三方組件 Element-UI
1、組件間的傳值
- 組件可以由內部的Data提供數據,也可以由父組件通過prop的方式傳值。
- 兄弟組件之間可以通過Vuex等統一數據源提供數據共享。
- 目前Element-UI成熟的版本是基于Vue2.x,所以使用Element-UI要創建Vue2.x項目
- 基本數據渲染
- 列表數據渲染
-
更多語法可在Vue官網學習!!
2、Element-UI介紹
- Element-UI是國內餓了么公司提供的一套開源前端框架,簡潔優雅,提供了Vue、React、Angular等多個版本
- 文檔地址:https://element.eleme.cn/#/zh-CN
- 安裝:
- npm i element-ui
- 以上即為安裝成功
- 引入Element-UI:
- import Vue from 'vue' import App from './App.vue' import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; Vue.use(ElementUI); // 全局注冊Vue.config.productionTip = falsenew Vue({render: h => h(App), }).$mount('#app')
- 簡單使用Element-UI
- <template><el-table:data="tableData"style="width: 100%":row-class-name="tableRowClassName"><el-table-columnprop="date"label="日期"width="180"></el-table-column><el-table-columnprop="name"label="姓名"width="180"></el-table-column><el-table-columnprop="address"label="地址"></el-table-column></el-table> </template><script> export default {name:"Hello",props:[],data() {return {tableData: [{date: '2016-05-02',name: '王小虎',address: '上海市普陀區金沙江路 1518 弄',}, {date: '2016-05-04',name: '王小虎',address: '上海市普陀區金沙江路 1518 弄'}, {date: '2016-05-01',name: '王小虎',address: '上海市普陀區金沙江路 1518 弄',}, {date: '2016-05-03',name: '王小虎',address: '上海市普陀區金沙江路 1518 弄'}]}},methods: {tableRowClassName({row, rowIndex}) {if (rowIndex === 1) {return 'warning-row';} else if (rowIndex === 3) {return 'success-row';}return '';}} } </script><style>.el-table .warning-row {background: oldlace;}.el-table .success-row {background: #f0f9eb;} </style>
- 更多組件和樣式可參考官方文檔:https://element.eleme.cn/#/zh-CN
3、第三方圖標庫
- 由于Element-UI提供的字體圖符較少,一般會采用其他圖標庫,如著名的FontAwesome。
- FontAwesome提供了675個可縮放的矢量圖標,可以使用CSS所提供的所有特性對它們進行更改,包括大小、顏色、陰影或者其他任何支持的效果。
- 文檔地址:https://fontawesome.dashgame.com/
- 安裝:
- npm install font-awesome
- 使用:
- import 'font-awesome/css/font-awesome.min.css';
- <i class="fa fa-camera-retro"></i> fa-camera-retro
- 更多圖標可參考官網:https://fontawesome.dashgame.com/
十、Axios網絡請求
?1、Axios簡介
- 在實際項目開發中,前端頁面所需要的數據往往需要從服務器獲取,這必然涉及與服務器的通信。
- Axios是一個基于promise網絡請求庫,作用于node.js和瀏覽器中。
- Axios在瀏覽器端使用XMLHttpRequests發送網絡請求,并能自動完成JSON數據的轉換。
- 安裝:
- npm install axios
- 地址:https://www.axios-http.cn/
- 引入:
- import axios from 'axios';
2、發送網絡請求
-
發送GET請求
- 方式一
- // 向給定id的用戶發起請求 axios.get('http://localhost:8080/user?id=123').then(function(response){// 處理成功情況console.log(response);}).catch(function(error){// 處理失敗情況console.log(error);}).then(function(){// 總是會執行});
- ?方式二
- // 上述請求也可以按以下方式完成axios.get('http://localhost:8080/user',{params:{id:123}}).then(function(response){// 處理成功情況console.log(response);}).catch(function(error){// 處理失敗情況console.log(error);}).then(function(){// 總是會執行});
- 方式一
-
發生POST請求
- axios.post('http://localhost:8080/user',{firstName: 'Fred',lastName: 'Flintstone' }) .then(function(response){console.log(response); }) .catch(function(error){console.log(error); });
-
異步回調問題(async/await)
- 可以取代上面的異步編程
- // 支持async/await 用法 async function getUser(){try{// 不需要.then就可以之間拿到response響應數據const response = await.axios.get('http://localhost:8080/user?id=1');console.log(response);}catch(error){console.error(error);} }
-
其他請求方式
-
??參考官方文檔:https://axios-http.com/zh/docs/req_config
-
-
與Vue整合
-
在實際項目開發中,幾乎每個組件中都會用到axios發起的數據請求。此時會遇到如下兩個問題:
-
每個組件中都需要導入axios
-
每次發送請求都需要填寫完整的請求路徑
-
-
可以通過全局配置的方法解決上述問題:
- // 在main.js中導入axios,然后再繼續配置 import axios from 'axios'// 配置請求根路徑 axios.defaults.baseURL = 'http://xxx'// 將axios作為全局的自定義屬性,每個組件可以在內部直接訪問(Vue3) app.config.globalProerties.$http = axios// 將axios作為全局的自定義屬性(不一定$http,可以自定義),每個組件可以在內部直接訪問(Vue2) Vue.prototype.$http = axios //例如:直接在其他組件中寫:this.$http.get()就可以實現axios的功能
-
3、Axios使用
-
Vue項目的網絡請求一般在created里面做,這樣頁面一創建就會發送網絡請求。
- created:function(){axios.get("http://localhost:8888/user").then((response)=>{ // 箭頭函數:ES6語法,它的作用:它的作用域繼承于父級(也就是Vue實例),如果用function(response),作用域就不是Vue實例console.log(response.data);this.tableData = response.data;console.log(this.tableData);}).catch(function(error){console.log(error);})},
-
當我們運行項目后,控制臺顯示報錯了,這個的意思是沒有明確授權同源策略,導致被CORS策略阻止。
-
為什么會出現跨域問題
-
為了保證瀏覽器的安全,不同源的客戶端本在沒有明確授權的情況下,不能讀寫對方資源,稱為同源策略,同源策略是瀏覽器安全的基石。
-
同源策略是一種約定,它是瀏覽器最核心也最基本的安全功能。
-
所謂同源(即指在同一個域)就是兩個頁面具有相同的協議(protocol),主機(host)和端口號(port)。
-
當一個請求url的協議、域名、端口三者之間任意一個與當前頁面url不同即為跨域,此時無法讀取非同源網頁的Cookie,無法向非同源地址發送Ajax請求或者Axios請求。
-
-
跨域問題解決方法
- CORS(Cross-Origin Resource Sharing)是由W3C制定的一種跨域資源共享技術標準,其目的就是為了解決前端的跨域請求。
- CORS可以在不破壞既有規則的情況下,通過后端服務器實現CORS接口,從而實現跨域通信。
- CORS將請求分為兩類:簡單請求和非簡單請求,分別對跨域通信提供支持。
- 簡單請求:(滿足以下條件的請求即為簡單請求,不滿足即為復雜請求)
- 請求方法:GET、POST、HEAD
- 除了以下的請求頭字段之外,沒有自定義的請求頭:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type
- Content-Type的值只有以下三種:
- text/plain
- multipart/form-data
- application/x-www-form-urlencoded
-
簡單請求的服務器處理
-
對于簡單請求,CORS的策略是請求時在請求頭中增加一個Origin字段。
-
服務器收到請求后,根據該字段判斷是否允許該請求訪問,如果允許,則在HTTP頭信息中添加Access-Control-Allow-Origin字段。
-
如果沒有配置跨域,是不會有Access-Control-Allow-Origin字段的。
-
-
非簡單請求
-
對于非簡單請求的跨源請求,瀏覽器會在真實請求發出前增加一次OPTION請求,稱為預檢請求(preflight request)。
-
預檢請求將真實請求的信息,包括請求方法、自定義頭字段、源信息添加到HTTP頭信息字段中,詢問服務器是否允許這樣的操作。
-
例如一個GET請求:
-
Access-Control-Request-Method表示請求使用的HTTP方法,Access-Control-Request-Headers包含請求的自定義頭字段。
-
服務器收到請求時,需要分別對Origin、Access-Control-Request-Method、Access-Control-Request-Headers進行驗證,驗證通過后,會在返回HTTP頭信息中添加:
- Access-Control-Allow-Methods、Access-Control-Allow-Headers:真實請求允許的方法、允許使用的字段。
- Access-Control-Allow-Credentials:是否允許用戶發送、處理Cookie。
- Access-Control-Max-Age:預檢請求的有效期,單位為秒,有效期內不會重復發送預檢請求。
- 當預檢請求通過后,瀏覽器才會發送真實請求到服務器。這樣就實現了跨域資源的請求訪問。
-
- Springboot中配置CORS
- 方式一:
- @Configuration public class CorsConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**") // 允許跨域訪問的路徑.allowedOrigins("*") // 允許跨域訪問的源.allowedMethods("POST","GET","PUT","OPTIONS","DELETE") // 允許請求的方法.maxAge(16800) // 預檢間隔時間.allowedHeaders("*") // 允許頭部設置.allowCredentials(true); // 是否發送Cookie} }
- 方式二:
- 方式一:
- ???配置好后重新訪問就可以請求成功了
十一、前端路由VueRouter
1、VueRouter安裝與使用
- Vue路由vue-router是官方的路由插件,能夠輕松的管理SPA項目中組件的切換。
- Vue的單頁面應用是基于路由和組件的,路由用于設定訪問路徑,并將路徑和組件映射起來。
- vue-router目前有3.x版本和4.x版本:
- vue-router3.x只能結合vue2進行使用;
- vue-router4.x只能結合vue3進行使用。
-
VueRouter安裝:
- vue-router@3在vue2中使用 npm install vue-router@3vue-router@4在vue3中使用 npm install vue-router@4
-
創建路由組件
-
聲明路由連接和占位標簽
- 可以使用<router-link>標簽來聲明路由鏈接,并使用<router-view>標簽來聲明路由占位符,示例如下:
- <template><div id="app"><!-- 聲明路由鏈接 --><router-link to="/teleplay">電視劇</router-link><router-link to="/film">電影</router-link><router-link to="/variety">綜藝</router-link><!-- 聲明路由占位標簽 --><router-view></router-view></div> </template>
-
創建路由模塊
- 在項目src目錄下創建一個router目錄,在里面新建一個index.js路由模塊
- 然后編寫以下代碼
- import VueRouter from "vue-router"; import Vue from "vue"; import Teleplay from '../components/Teleplay.vue' import Film from '../components/Film.vue' import Variety from '../components/Variety.vue'// 將VueRouter設置為Vue組件 Vue.use(VueRouter)const router = new VueRouter({// 指定hash屬性與組件的對應關系routes:[{path:'/teleplay',component:Teleplay},{path:'/film',component:Film},{path:'/variety',component:Variety}] })export default router
- 在項目src目錄下創建一個router目錄,在里面新建一個index.js路由模塊
-
掛載路由模塊
- 在main.js中導入并掛載router
- import Vue from 'vue' import App from './App.vue' import router from './router/index'Vue.config.productionTip = falsenew Vue({render: h => h(App),// router: router // 如果名稱和屬性一致可以直接寫router即可router }).$mount('#app')
-
實現效果
2、VueRouter進階
-
路由重定向
- 路由重定向指的是:用戶在訪問地址A的時候,強制用戶跳轉到地址C,從而展示特定的組件頁面。
- 通過路由規則的redirect屬性,指定一個新的路由地址,可以很方便地設置路由的重定向。
- // 將VueRouter設置為Vue組件 Vue.use(VueRouter)const router = new VueRouter({// 指定hash屬性與組件的對應關系routes:[// 當用戶訪問時,直接跳轉訪問teleplay{path:'/',redirect:'/teleplay'},{path:'/teleplay',component:Teleplay},{path:'/film',component:Film},{path:'/variety',component:Variety}] })export default router
-
嵌套路由
- 在Teleplay.vue組件中,聲明toplist和newlist的子路由鏈接以及子路由占位符,示例代碼如下:
- 在Teleplay中嵌套子路由:
- <template><div><h1>電視劇</h1><!-- 子路由鏈接 --><router-link to="/teleplay/toplist">推薦</router-link><router-link to="/teleplay/newlist">最新</router-link><hr><router-view></router-view></div> </template>
- 在index.js路由模塊中,導入需要的組件,并使用children屬性聲明子路由規則:
- const router = new VueRouter({// 指定hash屬性與組件的對應關系routes:[// 當用戶訪問時,直接跳轉訪問teleplay{path:'/',redirect:'/teleplay'},{path:'/teleplay',component:Teleplay,// 通過children屬性,嵌套聲明子路由children:[{path:'toplist',component: Toplist},{path:'newlist',component: Newlist}]},{path:'/film',component:Film},{path:'/variety',component:Variety},] })export default router
- 在Teleplay中嵌套子路由:
- 在Teleplay.vue組件中,聲明toplist和newlist的子路由鏈接以及子路由占位符,示例代碼如下:
-
動態路由
- 以下有三個路由鏈接,分別跳轉不同的電影界面
- 新建一個電影詳情頁Filmdetail.vue
- 然后在router中添加路由鏈接
- {path:'/film',component:Film,children:[{path:':id',component:Filmdetail},] },
- 這樣就能實現傳參
- 為了簡化路由參數的獲取形式,vue-router允許在路由規則中開啟props傳參。
- {path:'/film',component:Film,children:[{path:':id',component:Filmdetail,props:true},] },
- <template><div><h1>電影詳情頁</h1><!-- 獲取動態的id值 --><p>電影{{id}}</p></div> </template><script>export default{// 組件名稱name:'Filmdetail',props:["id"] // 定義屬性名稱} </script>
-
這樣就可以根據自己自定義的屬性來獲取傳過來的參數。
- 新建一個電影詳情頁Filmdetail.vue
- 以下有三個路由鏈接,分別跳轉不同的電影界面
-
編程式導航
-
聲明式編程式 <router-link to="..."> router.push(...) - 除了使用<router-link> 創建a標簽來定義導航鏈接,我們還可以借助router的實例方法,通過編寫代碼來實現。
- 想要導航到不同的URL,則使用router.push方法。這個方法會向history棧添加一個新的記錄,所以,當用戶點擊瀏覽器后退按鈕時,則回到之前的URL。
- 當你點擊<router-link>時,這個方法會在內部調用,所以說,點擊<router-link to="...">等同于調用router.push(....)。
- <template><div><h1>電影</h1><router-link to="/film/1">電影1</router-link><router-link to="/film/2">電影2</router-link><router-link to="/film/3">電影3</router-link><!-- 路由占位標簽 --><router-view></router-view><button v-on:click="gotoFilmdetail(3)">跳轉到電影3</button></div> </template><script> export default{methods:{gotoFilmdetail:function(id){this.$router.push('/filmdetail/'+id); // 跳轉到特定的url(必須有聲明的url) }} } </script>
-
-
導航守衛
- vue-router提供的導航守衛主要用來攔截導航,讓它完成跳轉或取消。
- to:Route:即將要進入的目標路由。
- from:Route:當前導航正要離開的路由。
- next:在守衛方法中如果聲明了next形參,則必須調用next()函數,否則不允許用戶訪問任何一個路由。
- 直接放行:next()
- 強制其跳轉到登錄頁面:next('/login')
- 強制其停留在當前頁:next(false)
- 使用router.beforeEach注冊一個全局前置守衛:
- router.beforeEach((to,from,next)=>{if(to.path === '/main' && !isAuthenticated){next('/login');}else{next();} });
十二、狀態管理VueX
1、VueX介紹
- 對于組件化開發來說,大型應用的狀態往往跨越多個組件。在多層嵌套的父子組件之間傳遞狀態已經十分麻煩,而Vue更是沒有為兄弟組件提供直接共享數據的辦法。
- 基于這個問題,許多框架提供了解決方案——使用全局的狀態管理器,將所有分散的共享數據交由狀態管理器保管,Vue也不例外。
- VueX是一個專門Vue.js應用程序開發的狀態管理庫,采用集中式存儲管理應用的所有組件的狀態。
- 簡單的說,VueX用于管理分散在Vue各個組件中的數據。
- 安裝:
- 這是vue2版本 npm install vuex@3這是vue3版本 npm install vuex@4
2、狀態管理
- 每一個Vuex應用的核心都是一個store,與普通的全局對象不同的是,基于Vue數據與視圖綁定的特點,當store中的狀態發生變化時,與之綁定的視圖也會被重新渲染。
- store中的狀態不允許被直接修改,改變store中的狀態的唯一途徑就是顯式地提交(commit)mutation,這可以讓我們方便地跟蹤每一個狀態的變化。
- 在大型復雜應用中,如果無法有效地跟蹤到狀態的變化,將會對理解和維護代碼帶來極大的困擾。
- Vuex中有5個重要的概念:State、Getter、Mutation、Action、Module。
-
最簡單的store
- 安裝Vuex之后,讓我們來創建一個store。創建過程直接了當——僅需要提供一個初始state對象和一些mutation:
- 在src目錄下新建一個store目錄,在store目錄下新建一個index.js來創建store
- 在index.js中寫如下代碼:
- import Vue from "vue"; import Vuex from "vuex";Vue.use(Vuex); const store = new Vuex.Store({state:{count:0,},mutations:{increment(state){state.count++;}} })export default store;
- 在main.js中導入store這樣組件就都可以使用store
- // 導入store import store from './store/index'new Vue({render: h => h(App),store:store }).$mount('#app')
- 現在就可以通過store.state來獲取狀態對象,并通過store.commit方法觸發狀態變更:
- <template><div><h1>TestStore</h1><h2>{{this.$store.state.count}}</h2><button @click="add">+1</button></div> </template><script> export default{name:'TestStore',methods:{add(){this.$store.commit("increment"); }} } </script>
- 通過提交mutation的方式,而非直接改變store.state.count,是因為外面想要更明確地追蹤到狀態的變化。這個簡單的約定能夠讓你的意圖更加明顯,這樣你在閱讀代碼的時候更容易地解讀應用內部的狀態改變。此外,這樣讓我們有機會去實現一些能記錄每次狀態改變,保存狀態快照的調試工具。有了它,甚至可以實現如時間穿梭般的調試體驗。
- 由于store中的狀態是響應式的,在組件中調用store中的狀態簡單到僅需要在計算屬性中返回即可。觸發變化也僅僅是在組件的methods中提交mutation。
- 在src目錄下新建一個store目錄,在store目錄下新建一個index.js來創建store
- 安裝Vuex之后,讓我們來創建一個store。創建過程直接了當——僅需要提供一個初始state對象和一些mutation:
-
State
- State用于維護所有應用層的狀態,并確保應用只有唯一的數據源
- const store = new Vuex.Store({state:{count:0},mutations:{increment(state){state.count++;}} })
- 在組件中,可以直接使用this.$store.count訪問數據,也可以先用mapState輔助函數將其映射下來。
- 直接使用this.$store.count訪問數據
- <template><div><h1>TestStore</h1><h2>{{this.$store.state.count}}</h2></div> </template>
- 先用mapState輔助函數將其映射下來
- import {mapState} from 'vuex'export default{name: 'TestStore',computed: mapState({// 箭頭函數可使代碼更加簡練count: state => state.count,// 傳字符串參數 'count' 等同于 'state => state.count'countAlias: 'count',// 為了能夠使用'this'獲取局部狀態,必須使用常規函數countPlusLocalState(state){return state.count + this.localCount}}) }
- 注意:當映射的計算屬性的名稱與state的子節點名稱相同時,我們也可以給mapState傳一個字符串數組。
- computed: mapState([// 映射 this.count 為 store.state.count'count' ])
- 直接使用this.$store.count訪問數據
- State用于維護所有應用層的狀態,并確保應用只有唯一的數據源
-
對象展開運算符
- mapState函數返回的是一個對象。我們如何將它與局部計算屬性混合使用呢?通常,我們需要使用一個工具函數將多個對象合并為一個,以使我們可以將最終對象傳給computed屬性。但是自從有了對象展開運算符,我們可以極大地簡化寫法:
- computed:{// 這是自定義的一個計算屬性localComputed(){....},// 使用對象展開運算符("...")將此對象混入到外部對象中...mapState({// ...}) }
- mapState函數返回的是一個對象。我們如何將它與局部計算屬性混合使用呢?通常,我們需要使用一個工具函數將多個對象合并為一個,以使我們可以將最終對象傳給computed屬性。但是自從有了對象展開運算符,我們可以極大地簡化寫法:
-
Getter
- Getter維護由State派生的一些狀態,這些狀態隨著State狀態的變化而變化
- const store = new Vuex.Store({state:{todos:[{id:1,text:"吃飯",done:true},{id:2,text:"睡覺",done:false}]},getters:{doneTodos: (state) => {return state.todos.filter(todo => todo.done===false);}} })
- 在組件中,可以直接使用this.$store.getters.doneTodos,也可以先用mapGetters輔助函數將其映射下來,代碼如下:
- <script> import { mapGetters } from 'vuex'; export default{name:'TestStore',computed: {// 使用對象展開運算符將getter混入computed對象中...mapGetters(['doneTodos'])} } </script>
- <template><div><ul><li v-for="todo in doneTodos" :key="todo.id">{{todo.text}}</li></ul></div> </template>
- Getter維護由State派生的一些狀態,這些狀態隨著State狀態的變化而變化
-
Mutation
- Mutation提供修改State狀態的方法
- const store = new Vuex.Store({state:{count:0},mutations:{increment(state){state.count++;}} })
- 在組件中,可以直接使用store.commit來提交mutation
- methods: {increment(){this.$store.commit('increment')console.log(this.$store.state.count)} }
- 也可以先用mapMutation輔助函數
- methods:{...mapMutations(['increment', // 將'this.increment()'映射為'this.$store.commit('increment')'// 'mapMutations'也支持載荷:(可以傳入額外的參數,即為載荷)'incrementBy' // 將'this.incrementBy(amount)'映射為'this.$store.commit('incrementBy',amount)']), }
- Mutation提供修改State狀態的方法
-
Action
- Action類似Mutation,不同在于:
- Action不能直接修改狀態,只能通過提交mutation來修改,Action可以包含異步操作
- const store = createStore({state: {count: 0;},mutations: {increment(state){state.count ++;}},actions: {increment(context){context.commit('increment');}} })
- 在組件中,可以直接使用this.$store.dispatch('xxx')分發action,或者使用mapActions輔助函數先將其映射下來
- methods:{...mapActions(['increment', // 將'this.increment()' 映射為 'this.$store.dispatch('increment')'// 'mapActions' 也支持載荷:'incrementBy' // 將 'this.incrementBy(amount)' 映射為 'this.$store.dispatch('incrementBy',amount)']),...mapActions({add: 'increment' // 將'this.add()' 映射為 'this.$store.dispatch('increment')'}) }
- Action類似Mutation,不同在于:
-
Module
- 由于使用單一狀態樹,當項目的狀態非常多時,store對象就會變得十分臃腫。因此,Vuex允許我們將store分割成模塊(Module)。
- 每個模塊擁有獨立的State、Getter、Mutation和Action,模塊之中還可以嵌套模塊,每一級都有著相同的結構。
- const moduleA = {state: ()=>({...}),mutations: {...},actions:{...},getters: {...} }const moduleB = {state: ()=>({...}),mutations: {...},actions:{...} }const store = createStore({modules: {a: moduleA,b: moduleB} })store.state.a // ——>moduleA的狀態 store.state.b // ——>moduleB的狀態
-
總結
- 作為一個狀態管理器,首先要有保管狀態的容器——State
- 為了滿足衍生數據和數據鏈的需求,從而有了Getter
- 為了可以“顯式地”修改狀態,所以需要Mutation
- 為了可以“異步地”修改狀態(滿足AJAX等異步數據交互),所以需要Action
- 最后,如果應用有成百上千個狀態,放在一起會顯得十分龐雜,所以分模塊管理(Module)也是必不可少的
- Vuex并不是Vue應用開發的必選項,在使用時,應先考慮項目的規模和特點。有所選擇地進行取舍,對于小型應用來說,完全沒有必要引入狀態管理,因為會帶來更多的開發成本。
十三、前端數據模擬Mock.js
1、Mock.js介紹
- Mock.js是一款前端開發中攔截Ajax請求再生成隨機數據響應的工具,可以用來模擬服務器響應。
- 優點:非常簡單方便、無侵入性,基本覆蓋常用的接口數據類型。
- 支持生成隨機的文本、數字、布爾值、日期、郵箱、鏈接、圖片、顏色等。
- 安裝:
- npm install mockjs
2、基本使用
- 在項目src目錄下創建mock目錄,新建index.js文件
- // 引入mock.js import Mock from 'mockjs'// 設置延遲時間 // Mock.setup({ // timeout: 400 // })// 使用mock.js模擬數據 Mock.mock('/product/search',{"ret": 0,"data": {"mtime": "@datatime", // 隨機生成日期時間"score|1-800": 800, // 隨機生成1-800的數字"rank|1-100": 100, // 隨機生成1-100的數字"stars|1-5": 5, // 隨機生成1-5的數字"nickname": "@cname", // 隨機生成中文名字"img": "@image('200x100','#ffcc33','#FFF','png','Fast Mock')"// 尺寸 背景顏色 文字顏色 圖片格式 圖片文字 }})
- 然后在main.js中導入,否則無法使用
- import './mock/index'
- 組件中調用mock.js中模擬的數據接口,這時返回的response就是mock.js中用Mock.mock('url',data)中設置的data
- import axios from 'axios'export default{mounted:function(){axios.get("/product/search").then(res=>{console.log(res)})} }
- 效果如下:
3、核心方法
- Mock.mock(rurl?, rtype?, template|function(options))
- rurl:表示需要攔截的URL,可以是URL字符串或URL正則
- rtype:表示需要攔截的Ajax請求類型。例如GET、POST、PUT、DELETE等。
- template:表示數據模板,可以是對象或字符串
- function:表示用于生成響應數據的函數。
- 設置延時請求到數據
- // 延時400ms請求到數據 Mock.setup({timeout: 400 })// 延時200-600ms請求到數據 Mock.setup({timeout: '200-600' })
4、數據生成規則
- mock的語法規范包含兩層規范:數據模板(DTD)、數據占位符(DPD)
- 數據模板中的每個屬性由3部分構成:屬性名name、生成規則rule、屬性值value:
- // 屬性名 name // 生成規則 rule // 屬性值 value 'name|rule': value
- 屬性名和生成規則之間用豎線|分隔,生成規則是可選的,有7中格式:
- 更多詳細知識可在mockJS官網了解
十四、企業級后臺集成方案
1、vue-element-admin介紹
- vue-element-admin是一個后臺前端解決方案,它基于vue和element-ui實現。
- 內置了i18國際化解決方案,動態路由,權限驗證,提煉了典型的業務模型,提供了豐富的功能組件。
- 可以快速搭建企業級中后臺產品原型。
- 地址:https://panjiachen.github.io/vue-element-admin-site/zh/guide/
- 在github上把項目下載下來https://github.com/PanJiaChen/vue-admin-template
- 具體使用可在官網查看文檔。
十五、跨域認證
1、Session認證
互聯網服務離不開用戶認證。一般是一下流程:
- 用戶向服務器發送用戶名和密碼。
- 服務器驗證通過后,在當前對話(session)里面保存相關數據,比如用戶角色、登錄時間等。
- 服務器向用戶返回一個session_id,寫入用戶的Cookie。
- 用戶隨后的每一次請求,都會通過Cookie,將session_id傳回服務器。
- 服務器收到session_id,找到前期保存的數據,由此得知用戶的身份。
- session認證過程:
- session認證的方式應用非常普遍,但也存在一些問題,擴展性不好,如果是服務器集群,或者是跨域的服務導向結構,就要求session數據共享,每臺服務器都能夠讀取session,針對此種問題有兩種方案:
- 一種是session數據持久化,寫入數據庫或者別的持久層。各種服務收到請求后,都向持久層請求數據。這種方案的優點是架構清晰,缺點是工程量比較大。
- 另一種是服務器不再保存session數據,所有數據都保存在客戶端,每次請求都發回服務器。Token認證就是這種方案的一個代表。
2、Token認證
Token是在服務端產生的一串字符串,是客戶端訪問資源接口(API)時所需要的資源憑證,流程如下:
- 客戶端使用用戶名跟密碼請求登錄,服務端收到請求,去驗證用戶名與密碼
- 驗證成功后,服務端會簽發一個token并把這個token發送給客戶端
- 客戶端收到token以后,會把它存儲起來,比如放在cookie里或者localStorage里
- 客戶端每次向服務端請求資源的時候需要帶著服務端簽發的token
- 服務端收到請求,然后去驗證客戶端請求里面帶著的token,如果驗證成功,就向客戶端返回請求的數據
- token認證流程:
- Token認證的特點:
- 基于token的用戶認證是一種服務端無狀態的認證方式,服務端不用存放token數據
- 用解析token的計算時間換取session的存儲空間,從而減輕服務器的壓力,減少頻繁的查詢數據庫
- token完全由應用管理,所以它可以避開同源策略CORS
3、JWT
- JSON Web Token(簡稱JWT)是一個token的具體實現方式,是目前最流行的跨域認證解決方案。
- JWT的原理是,服務器認證以后,生成一個JSON對象,發回給用戶,具體如下:
- 用戶與服務端通信的時候,都要發回這個JSON對象。服務器完全只靠這個對象認定用戶身份。
- 為了防止用戶篡改數據,服務器在生成這個對象的時候,會加上簽名。
4、JWT組成
- JWT由三部分組成,依次如下:
- Header(頭部)
- Payload(負載)
- Signature(簽名)
- 三部分最終組合完成一個完整的字符串,中間用“.”分隔,如下:
- Header.Payload.Signature
-
Header
- Header部分是一個JSON對象,描述JWT的元數據
- alg屬性表示簽名的算法(algorithm),默認是HMAC SHA256(寫成HS256)
- typ屬性表示這個令牌(token)的類型(type),JWT令牌統一寫為JWT
- 最后,將上面的JSON對象使用Base64URL算法轉成字符串
- Header部分是一個JSON對象,描述JWT的元數據
-
Payload
- Payload部分也是一個JSON對象,用來存放實際需要傳遞的數據。JWT規定了7個官方字段,供選用。
- iss(issuer):簽發人
- exp(expiration time):過期時間
- sub(subject):主題
- aud(audience):受眾
- nbf(Not Before):生效時間
- iat(Issued At):簽發時間
- jti(JWT ID):編號
- 注意,JWT默認不加密的,任何人都可以讀到,所以不要把秘密信息放在這個部分。
- 這個JSON對象也要使用Base64URL算法轉成字符串。
- Payload部分也是一個JSON對象,用來存放實際需要傳遞的數據。JWT規定了7個官方字段,供選用。
-
Signature
- Signature部分是對前兩部分的簽名,防止數據篡改。
- 首先,需要指定一個密鑰(secret)。這個密鑰只有服務器才知道,不能泄露給用戶。
- 然后,使用Header里面指定的簽名算法(默認是HMAC SHA256),按照下面的公式產生簽名。
5、JWT的特點
- 客戶端收到服務器返回的JWT,可以儲存在Cookie里面,也可以儲存在localStorage。
- 客戶端每次與服務器通信,都要帶上這個JWT,可以把它放在Cookie里面自動發送,但是這樣不能跨域。
- 更好的做法是放在HTTP請求的頭信息"Authorization"字段里面,單獨發送。
6、JWT的實現
- 加入依賴
- <!--JWT--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency>
- 生成和解析token的工具類
- public class JwtUtil {// 7天過期private static long expire = 604800;// 32位秘鑰private static String secret = "abcdfghiabcdfghiabcdfghiabcdfghi";// 生成tokenpublic static String generateToken(String username){Date now = new Date();Date expiration = new Date(now.getTime() + 1000 * expire);return Jwts.builder().setHeaderParam("type","JWT") // 設置Header.setSubject(username) // 設置負載.setIssuedAt(now) // 設置生效時間.setExpiration(expiration) // 設置過期時間.signWith(SignatureAlgorithm.HS512,secret) // 指定簽名算法.compact();}// 解析tokenpublic static Claims getClaimsByToken(String token){return Jwts.parser().setSigningKey(secret) // 傳密鑰,查看是否有篡改.parseClaimsJws(token) // token是否正確.getBody();} }
- 生成token
- @PostMapping("/login")public String login(@RequestBody User user){ // 用@RequestBody接收,前臺就需要發送的是json格式String token = JwtUtil.generateToken(user.getUsername());return token;}
- 解析token
- @GetMapping("/info")public String info(String token){String username = JwtUtil.getClaimsByToken(token).getSubject(); // 解析token獲取用戶名return username;}
- 這樣就簡單實現了生成token和解析token
十七、Springboot+Vue云端環境配置
- 以下部署基于Centos7 系統環境(使用XShell鏈接和Xftp進行文件傳輸)
1、安裝MySQL
- 卸載Centos7自帶的mariadb
- # 查找 是否安裝了mariadb rpm -qa|grep mariadb # mariadb-libs-5.5.52-1.el7.x86_64 # 卸載(版本根據自己系統自帶的自行更改) rpm -e mariadb-libs-5.5.52-1.el7.x86_64 --nodeps
-
解壓MySQL
- # 創建mysql安裝包存放點 mkdir /usr/server/mysql # 解壓 tar xvf mysql-5.7.34-1.el7.x86_64.rpm-bundle.tar
-
執行安裝
- # 切換到安裝目錄 cd /usr/server/mysql/ yum -y install libaio yum -y install libncurses* yum -y install perl perl-devel # 安裝 rpm -ivh mysql-community-common-5.7.34-1.el7.x86_64.rpm rpm -ivh mysql-community-libs-5.7.34-1.el7.x86_64.rpm rpm -ivh mysql-community-client-5.7.34-1.el7.x86_64.rpm rpm -ivh mysql-community-server-5.7.34-1.el7.x86_64.rpm
-
啟動MySQL
- #啟動mysql systemctl start mysqld.service #查看生成的臨時root密碼 cat /var/log/mysqld.log | grep password
-
修改初始的隨機密碼
- # 登錄mysql mysql -u root -p Enter password: #輸入在日志中生成的臨時密碼 # 更新root密碼 設置為root(密碼自行定義,后面遠程連接需要用到) set global validate_password_policy=0; set global validate_password_length=1; set password=password('root');
-
授予遠程連接權限
- # 讓數據庫支持遠程連接 grant all privileges on *.* to 'root' @'%' identified by 'root'; # 刷新 flush privileges;
-
控制命令
- #mysql的啟動和關閉 狀態查看 systemctl stop mysqld systemctl status mysqld systemctl start mysqld #建議設置為開機自啟動服務 systemctl enable mysqld #查看是否已經設置自啟動成功 systemctl list-unit-files | grep mysqld
-
關閉防火墻
- firewall-cmd --state #查看防火墻狀態 systemctl stop firewalld.service #停止firewall systemctl disable firewalld.service #禁止firewall開機啟動
2、安裝Nginx
- 安裝Nginx
- yum install epel-release yum update yum -y install nginx
- nginx命令
- systemctl start nginx #開啟nginx服務 systemctl stop nginx #停止nginx服務 systemctl restart nginx #重啟nginx服務
3、配置JDK
- 到官網https://www.oracle.com/java/technologies/downloads/#java8下載jdk版本
- 解壓
- tar -zvxf jdk-8u131-linux-x64.tar.gz
- 編輯 /etc/profile 文件
- vi /etc/profile # 文件末尾增加 export JAVA_HOME=/usr/server/jdk1.8.0_351 export PATH=${JAVA_HOME}/bin:$PATH
- 執行source命令,使配置立即生效
- source /etc/profile
- 檢查是否安裝成功
- java -version
?
十八、Springboot+Vue項目部署
1、部署Vue項目
- 打包Vue項目
- 在vue項目的目錄,執行以下代碼
- npm run build
- 將生成的dist目錄上傳至服務器 /usr/vue/dist(路徑自定義)
- 配置Nginx
- 進入到/etc/nginx/conf.d目錄,創建vue.conf文件,內容如下
- server {listen 80;server_name locahost;location / {root /usr/app/dist;index index.html;} }
- 使配置生效
- nginx -s reload
2、打包運行Java程序
- 在右邊的maven中雙擊package,會自動打包在項目路徑文件夾的/target文件夾下
- 因為springboot有內置tomcat容器,這點比較方便,省去了tomcat的部署。我們到時候直接可以直接
把jar包扔到linux上 - # 表示讓java程序在后臺運行,然后生成運行日志,方便查看項目是否報錯。 nohup java -jar shop-0.0.1-SNAPSHOT.jar > logName.log 2>&1 &
項目正常運行了,說明部署成功了,這樣項目所有人就可以訪問了。
?
總結
以上是生活随笔為你收集整理的SpringBoot+Vue项目实例开发及部署的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SICP读书笔记 2.5
- 下一篇: 中小尺寸OLED面板面临价格战,中国手机