<address id="ttjl9"></address>

      <noframes id="ttjl9"><address id="ttjl9"><nobr id="ttjl9"></nobr></address>
      <form id="ttjl9"></form>
        <em id="ttjl9"><span id="ttjl9"></span></em>
        <address id="ttjl9"></address>

          <noframes id="ttjl9"><form id="ttjl9"></form>

          首頁

          Swagger-的使用(詳細教程)

          前端達人


          前言

          作為后端開放人員,最煩的事就是自己寫接口文檔和別人沒有寫接口文檔,不管是前端還是后端開發,多多少少都會被接口文檔所折磨,前端會抱怨后端沒有及時更新接口文檔,而后端又會覺得編寫接口文檔太過麻煩。Swagger 可以較好的接口接口文檔的交互問題,以一套標準的規范定義接口以及相關的信息,就能做到生成各種格式的接口文檔,生成多種語言和客戶端和服務端的代碼,以及在線接口調試頁面等等。只需要更新 Swagger 描述文件,就能自動生成接口文檔,做到前端、后端聯調接口文檔的及時性和便利性。

          一、簡介

          官網:https://swagger.io/

          Swagger 是一個規范且完整的框架,用于生成、描述、調用和可視化 RESTful 風格的 Web 服務。

          Swagger 的目標是對 REST API 定義一個標準且和語言無關的接口,可以讓人和計算機擁有無須訪問源碼、文檔或網絡流量監測就可以發現和理解服務的能力。當通過 Swagger 進行正確定義,用戶可以理解遠程服務并使用最少實現邏輯與遠程服務進行交互。與為底層編程所實現的接口類似,Swagger 消除了調用服務時可能會有的猜測。

          Swagger 的優勢

          • 支持 API 自動生成同步的在線文檔:使用 Swagger 后可以直接通過代碼生成文檔,不再需要自己手動編寫接口文檔了,對程序員來說非常方便,可以節約寫文檔的時間去學習新技術。
          • 提供 Web 頁面在線測試 API:光有文檔還不夠,Swagger 生成的文檔還支持在線測試。參數和格式都定好了,直接在界面上輸入參數對應的值即可在線測試接口。

          二、基本使用

          1. 導入相關依賴

          通過在項目中引入 Springfox,可以掃描相關的代碼,生成該描述文件,進而生成與代碼一致的接口文檔和客戶端代碼。

           <!-- swagger --> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-spring-web</artifactId> <version>2.9.2</version> </dependency> <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> 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16

          2. 編寫配置文件

          在配置文件 config 目錄下,添加 swagger 的配置文件 SwaggerConfig.java

          @Configuration // 配置類 @EnableSwagger2 // 開啟 swagger2 的自動配置 public class SwaggerConfig { } 
          
          • 1
          • 2
          • 3
          • 4

          這個時候 Swagger 已經算是整合到項目之中了,可以啟動下服務,輸入:http://localhost:8080/swagger-ui.html# (這里我的項目端口是 8868 ,項目路徑是 /mike,所以我打開的文檔地址為 http://localhost:8868/mike/swagger-ui.html# )即可查看 swagger 文檔。

          在這里插入圖片描述
          可以看到 Swagger 文檔中大概有這四類信息

          • 基本信息
          • 接口信息
          • 實體類信息

          2.1 配置基本信息

          Swagger 有自己的實例 Docket,如果我們想要自定義基本信息,可以使用 docket 來配置 swagger 的基本信息,基本信息的設置在 ApiInfo 這個對象中。

          Swagger 默認的基本信息展示

          在這里插入圖片描述

          ApiInfo 中默認的基本設置

          • title:Api Documentation
          • description:Api Documentation
          • version:1.0
          • termsOfServiceUrl:urn:tos
          • contact:無
          • license:Apache 2.0
          • licenseUrl:http://www.apache.org/licenses/LICENSE-2.0

          SwaggerConfig.java 配置文件添加以下內容:

           @Bean public Docket docket() { // 創建一個 swagger 的 bean 實例 return new Docket(DocumentationType.SWAGGER_2) // 配置基本信息 .apiInfo(apiInfo()) ; } // 基本信息設置 private ApiInfo apiInfo() { Contact contact = new Contact( "米大傻", // 作者姓名 "https://blog.csdn.net/xhmico?type=blog", // 作者網址 "777777777@163.com"); // 作者郵箱 return new ApiInfoBuilder() .title("多加辣-接口文檔") // 標題 .description("眾里尋他千百度,慕然回首那人卻在燈火闌珊處") // 描述 .termsOfServiceUrl("https://www.baidu.com") // 跳轉連接 .version("1.0") // 版本 .license("Swagger-的使用(詳細教程)") .licenseUrl("https://blog.csdn.net/xhmico/article/details/125353535") .contact(contact) .build(); } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25

          重啟服務,打開 Swagger 文檔,基本信息改變如下所示:

          在這里插入圖片描述

          2.2 配置接口信息

          默認情況下,Swagger 是會展示所有的接口信息的,包括最基礎的 basic-error 相關的接口

          在這里插入圖片描述

          有時候我們希望不要展示 basic-error-controller 相關的接口,或者是說這想要顯示某些接口,比如說:user-controller 下的接口,由該怎么去實現呢?這個時候就需要設置 掃描接口

           @Bean public Docket docket() { // 創建一個 swagger 的 bean 實例 return new Docket(DocumentationType.SWAGGER_2) // 配置接口信息 .select() // 設置掃描接口 // 配置如何掃描接口 .apis(RequestHandlerSelectors //.any() // 掃描全部的接口,默認 //.none() // 全部不掃描 .basePackage("com.duojiala.mikeboot.controller") // 掃描指定包下的接口,最為常用 //.withClassAnnotation(RestController.class) // 掃描帶有指定注解的類下所有接口 //.withMethodAnnotation(PostMapping.class) // 掃描帶有只當注解的方法接口 ) .paths(PathSelectors .any() // 滿足條件的路徑,該斷言總為true //.none() // 不滿足條件的路徑,該斷言總為false(可用于生成環境屏蔽 swagger) //.ant("/user/**") // 滿足字符串表達式路徑 //.regex("") // 符合正則的路徑 ) .build(); } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24

          可根據自己的需求去設置對應的配置,這里我就不再一一贅述了,以上是我所設置的配置,重啟服務,打開 Swagger 文檔,接口信息改變如下所示:

          在這里插入圖片描述

          可以看到之前 basic-error-controller 相關的接口已經沒有了

          2.3 配置分組信息

          Swagger 默認只有一個 default 分組選項,如果沒有設置,所有的接口都會顯示在 default `分組下,如果功能模塊和接口數量一多,就會顯得比較凌亂,不方便查找和使用。

          swagger 文檔中組名默認是 default,可通過 .groupName(String )

          在這里插入圖片描述

           @Bean public Docket docket() { // 創建一個 swagger 的 bean 實例 return new Docket(DocumentationType.SWAGGER_2) .groupName("mike") // 修改組名為 "mike" ; } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7

          修改后:
          在這里插入圖片描述
          如果需要配置多個組的話,就需要配置多個 docket() 方法,這里我就簡單寫兩組,代碼如下:

           /**
               * 展示 controller 包下所有的接口
               */ @Bean public Docket docket1() { // 創建一個 swagger 的 bean 實例 return new Docket(DocumentationType.SWAGGER_2) .groupName("mike") // 修改組名為 "mike" // 配置接口信息 .select() // 設置掃描接口 // 配置如何掃描接口 .apis(RequestHandlerSelectors .basePackage("com.duojiala.mikeboot.controller") // 掃描指定包下的接口,最為常用 ) .paths(PathSelectors .any() // 滿足條件的路徑,該斷言總為true ) .build(); } /**
               * 展示路徑為 /error 的所有接口(基礎接口)
               */ @Bean public Docket docket2() { // 創建一個 swagger 的 bean 實例 return new Docket(DocumentationType.SWAGGER_2) .groupName("yank") // 修改組名為 "yank" // 配置接口信息 .select() // 設置掃描接口 // 配置如何掃描接口 .apis(RequestHandlerSelectors .any() // 掃描全部的接口,默認 ) .paths(PathSelectors .ant("/error") // 滿足字符串表達式路徑 ) .build(); } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
          • 27
          • 28
          • 29
          • 30
          • 31
          • 32
          • 33
          • 34
          • 35
          • 36
          • 37
          • 38
          • 39

          重啟服務,打開 Swagger 文檔,接口信息改變如下所示:

          組名為 mike 的文檔中只有 user-controller 相關的接口信息

          在這里插入圖片描述

          組名為 yank 的文檔中只有 basic-error-controller 相關的接口信息

          在這里插入圖片描述


          3. 控制 Swagger 的開啟

          在開發或者測試環境下,我們開啟 swagger 會方便前端和后端的交互,但是如果在生產環境下也開啟 swagger 的話,是會將接口暴露出去的,有極大風險,如何讓 swagger 根據不同的環境來決定是否開啟?

          這里我準備了四個項目的配置文件,devtest、pro 三個環境的配置文件僅是端口上的不同

          在這里插入圖片描述

          • application.yml -------------------------- 全局配置文件
          • application-dev.yml -------------------- 開發環境配置文件
          • application-test.yml -------------------- 測試環境配置文件
          • application-pro.yml -------------------- 生產環境配置文件

          application.yml 內容如下,用于指定選擇的環境:

          spring: profiles: active: dev 
          
          • 1
          • 2
          • 3

          可以通過代碼判斷此時是在什么環境:dev、testpro,如果是在 pro 生產環境,則關閉 swagger。

           /**
               * swagger 配置
               * @param environment 環境
               */ @Bean public Docket docket(Environment environment) { // 設置環境范圍 Profiles profiles = Profiles.of("dev","test"); // 如果在該環境返回內則返回:true,反之返回 false boolean flag = environment.acceptsProfiles(profiles); // 創建一個 swagger 的 bean 實例 return new Docket(DocumentationType.SWAGGER_2) .enable(flag) // 是否開啟 swagger:true -> 開啟,false -> 關閉 ; } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17

          在 application.yml 全局配置文件中環境指向 dev 時,是可以打開 swagger 的

          在這里插入圖片描述
          如果我將 application.yml 全局配置文件中環境指向 pro 時,就不能打開 swagger 了,提示 Could not render e, see the console

          在這里插入圖片描述

          4. 常用注解使用

          之前有說 Swagger 會將接口請求或者相應的實體類信息展示在 Models 下的,比如我 UserController.java 下有一個接口如下所示:

           @PostMapping(value = "/query-user-info") public ResponseBean queryUserInfo(@RequestBody @Validated IdReq req) { return ResponseBean.success(userService.queryUserInfo(req)); } 
          
          • 1
          • 2
          • 3
          • 4

          它的請求體是 IdReq,響應是 ResponseBean,Models 展示這兩個實體類信息如下:

          在這里插入圖片描述
          前端可通過看這個 Models 知道后端定義實體類的信息。

          @ApiModel

          該注解是作用于類上面的,是用來描述類的一些基本信息的。

          相關屬性:

          • value:提供類的一個備用名,如果不設置,默認情況下將使用 class 類的名稱
          • description:對于類,提供一個詳細的描述信息
          • parent:這個屬性用于描述的是類的一些父類信息
          • discriminator:這個屬性解釋起來比較麻煩,因為這個類主要體現在斷言當中
          • subTypes:可以通過這個屬性,指定我們想要使用的子類

          譬如:這個為給 IdReq 這個類添加該注解

          @Data @NoArgsConstructor @AllArgsConstructor @ApiModel(value = "Id請求體") public class IdReq { private String id; } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7

          在這里插入圖片描述
          可以看到這里的名字從 IdReq 變成 Id請求體 了

          @ApiModelProperty

          它的作用是添加和操作屬性模塊的數據。

          該注解的使用詳情可參見博客:@ApiModelProperty注解的用法

          這里我還是以 IdReq 類為例,為該類的屬性添加說明

          @Data @NoArgsConstructor @AllArgsConstructor @ApiModel(value = "Id請求體") public class IdReq { @ApiModelProperty("主鍵id") private String id; } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8

          在這里插入圖片描述
          可以看到這里對該字段有一個備注說明。

          @ApiOperation

          該注解用來對某個方法/接口進行描述

          該注解的使用詳情可參見博客:Swagger @ApiOperation 注解詳解

          這里我以 UserController 下的接口為例:

           @PostMapping(value = "/query-user-info") @ApiOperation(value = "根據id查詢用戶詳情") public ResponseBean queryUserInfo(@RequestBody @Validated IdReq req) { return ResponseBean.success(userService.queryUserInfo(req)); } 
          
          • 1
          • 2
          • 3
          • 4
          • 5

          在這里插入圖片描述
          可以看見該接口就多了對其的描述信息。

          @ApiParam

          該注解使用在方法上或者參數上,字段說明,表示對參數的添加元數據(說明或者是否必填等)

          相關屬性:

          • name:參數名
          • value:參數說明
          • required:是否必填

          這里我以 UserController 下的接口為例:

           @PostMapping(value = "/query-user-infos") @ApiOperation(value = "條件查詢用戶信息") public ResponseBean queryUserInfos( // name 用戶名稱 不必填 @ApiParam(value = "用戶名稱", required = false) @RequestParam(required = false) String name, // gender 用戶性別 必填 @ApiParam(value = "用戶性別", required = true) @RequestParam(required = true) GenderEnum gender ) { return ResponseBean.success(userService.queryUserInfos(name,gender)); } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10

          在這里插入圖片描述
          這里會展示請求參數的備注信息,以及是否必填等。

          5. 接口調用

          使用 swagger 除了讓前后端交互變得方便,也讓接口的請求變得簡單,只需要填寫好請求所需要的參數信息,便可直接發起請求。

          比如說接口 /user/query-user-info

          點擊 Try it out

          在這里插入圖片描述
          設置好請求所需的參數,點擊 Execute 執行

          在這里插入圖片描述

          就能看到接口響應的結果了

          在這里插入圖片描述

          接口 /user/query-user-infos 也差不多

          在這里插入圖片描述
          在這里插入圖片描述


          三、進階使用

          1. 添加請求頭

          有時候我們的接口是需要獲取請求頭信息的,這樣的話就還需要在 swagger 配置中添加請求頭的配置。

           @Bean public Docket docket() { // 設置請求頭 List<Parameter> parameters = new ArrayList<>(); parameters.add(new ParameterBuilder() .name("token") // 字段名 .description("token") // 描述 .modelRef(new ModelRef("string")) // 數據類型 .parameterType("header") // 參數類型 .defaultValue("default value") // 默認值:可自己設置 .hidden(true) // 是否隱藏 .required(false) // 是否必須 .build()); // 創建一個 swagger 的 bean 實例 return new Docket(DocumentationType.SWAGGER_2) .groupName("mike") // 修改組名為 "mike" // 配置接口信息 .select() // 設置掃描接口 // 配置如何掃描接口 .apis(RequestHandlerSelectors .basePackage("com.duojiala.mikeboot.controller") // 掃描指定包下的接口,最為常用 ) .paths(PathSelectors .any() // 滿足條件的路徑,該斷言總為true ) .build() // 添加請求頭參數 .globalOperationParameters(parameters); } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
          • 27
          • 28
          • 29
          • 30
          • 31

          比如接口:

           @GetMapping(value = "/get-token") @ApiOperation(value = "獲取請求頭中的token信息") public void getToken( @RequestHeader(value = "token",required = false) String token ) { // 直接獲取 token 信息 System.out.println("token = " + token); // 通過代碼獲取 ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (servletRequestAttributes != null) { HttpServletRequest request = servletRequestAttributes.getRequest(); String header = request.getHeader("token"); System.err.println("header = " + header); } } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16

          在這里插入圖片描述
          可以看到這個接口已經可以去設置請求頭了,調用接口

          在這里插入圖片描述
          后端也能獲取到。

          藍藍設計建立了UI設計分享群,每天會分享國內外的一些優秀設計,如果有興趣的話,可以進入一起成長學習,請加藍小助,微信號:ben_lanlan,報下信息,藍小助會請您入群。歡迎您加入噢~~希望得到建議咨詢、商務合作,也請與我們聯系01063334945。


          分享此文一切功德,皆悉回向給文章原作者及眾讀者.
          免責聲明:藍藍設計尊重原作者,文章的版權歸原作者。如涉及版權問題,請及時與我們取得聯系,我們立即更正或刪除。


          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務、UI設計公司、界面設計公司、UI設計服務公司、數據可視化設計公司、UI交互設計公司、高端網站設計公司、UI咨詢、用戶體驗公司、軟件界面設計公司


          前端藝術之毛玻璃-傾斜-日歷

          前端達人

          前端藝術之毛玻璃-傾斜-日歷

          描述

          項目 描述
          開發語言 HTML、JavaScript、CSS
          dyCalendarJS、vanilla-tilt
          Edge 108.0.1462.54 (正式版本) (64 位)

          該項目中需要使用到的庫有:

          1. dyCalendarJS
            vanilla-tilt.js 是 JavaScript 中的一個平滑的 3D 傾斜庫。
          2. vanilla-tilt
            dyCalendarJS 是一個用于創建日歷的 JavaScript 庫,您可以在博客和網站中免費使用它。

          如果你在觀看本篇文章前并沒有對這兩個庫進行了解,歡迎移步至我的另外兩篇文章進行學習:

          1. JavaScript 庫之 vanilla-tilt(一個平滑的 3D 傾斜庫)
          2. JavaScript 庫之 dyCalendarJS(日歷)

          項目

          該項目文件中我已對代碼進行了注釋。如遇不懂的地方,請嘗試查看相關注釋。

          效果

          效果

          效果

          index.html

          <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>毛玻璃-傾斜-日歷</title> <!-- 導入自定義 CSS 文件 --> <link rel="stylesheet" href="./index.css"> <!-- 導入 dycalendar.css --> <link rel="stylesheet" href="../dycalendar.min.css"> </head> <body> <div id="calendar" class="dycalendar-container"></div> <!-- 導入 dycalendar.js --> <script src="../dycalendar.min.js"></script> <!-- 導入 vanilla-tilt.js --> <script src="../vanilla-tilt.js"></script> <script> // 繪制日歷 dycalendar.draw({ target: '#calendar', // 指定用于創建日歷的 HTML 容器 type: 'month', // 設置日歷的類型 prevnextbutton: 'show', // 顯示 "<" 及 ">" 按鈕 highlighttoday: true // 高亮顯示當前日期 }) // 為目標元素添加傾斜效果 VanillaTilt.init(document.querySelector('#calendar'), { target: '#calendar', // 指定需要添加傾斜效果的目標元素 scale: 0.8, // 鼠標懸停于目標元素上時,目標元素的放縮倍數 glare: true, // 是否設置反光效果 'max-glare': 0.6 // 設置反光效果的強度 }) </script> </body> </html> 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
          • 27
          • 28
          • 29
          • 30
          • 31
          • 32
          • 33
          • 34
          • 35
          • 36
          • 37
          • 38
          • 39

          index.css

          *{ /* 去除元素默認的內外邊距 */ margin: 0px; padding: 0px; /* 
              設置邊框時將壓縮內容區域,而不會向外擴張。
              也就是說,為某個元素設置邊框并不會改變其寬高。
               */ box-sizing: border-box; } body{ /* 顯示區域的最小高度為顯示窗口的高度 */ min-height: 100vh; /* 設置該元素內部元素居中顯示 */ display: flex; justify-content: center; align-items: center; /* 設置該元素的背景顏色 */ background-color: #161623; } body::before{ /* 若需要正常使用偽元素,必須為其設置 content 屬性 */ content: ''; width: 400px; height: 400px; /* 設置顏色漸變效果 */ background: linear-gradient(#ffc107,#e91e63); /* 設置邊框圓角,當該屬性的值為 50% 時元素邊框將顯示為一個圓 */ border-radius: 50%; /* 
              為該元素設置絕對定位,阻止該元素遮擋日歷
              (定位元素可以設置 z-index 來調節顯示順序,
              z-index 的值越高,顯示優先級越大)。
               */ position: absolute; top: 10%; left: 20%; z-index: -1; } body::after{ content: ''; width: 300px; height: 300px; position: absolute; background: linear-gradient(#2196f3,#31ff38); border-radius: 50%; top: 45%; left: 55%; z-index: -1; } #calendar{ /* 設置日歷的寬高 */ width: 400px; height: 400px; color: #fff; /* 
              設置日歷的背景元素,為產生毛玻璃效果,這里將背景顏色設置為白色,
              將透明度設置為 0.1(透明度的取值范圍為 0~1,取值越接近 1 ,顏色
              越不透明)。
               */ background-color: rgb(255, 255, 255, 0.1); /* 
              設置 blur 過濾器,該過濾器可以將背景模糊化,參數中的
              像素值設定越高,顯示得越是模糊。
              */ backdrop-filter: blur(50px); /* 分別設置日歷的四條邊框,使日歷顯示得更為立體 */ border-top: 1px solid rgb(255, 255, 255, 0.5); border-left: 1px solid rgb(255, 255, 255, 0.5); border-right: 1px solid rgb(255, 255, 255, 0.2); border-bottom: 1px solid rgb(255, 255, 255, 0.2); border-radius: 5px; /* 設置日歷的內邊距 */ padding: 0px 20px; /* 
              設置日歷周邊的陰影效果,box-shadow 接收的值(如下)分別為
              陰影的 X 偏移量、陰影的 Y 偏移量、擴散半徑、陰影顏色。
              */ box-shadow: 5px 10px 10px rgb(0, 0, 0, 0.1); } /* 
          這里存在許多在 HTML 文件中沒有看到的類名,這是因為這些標簽
          是 dyCalendarJS 通過 JavaScript 動態創建的元素,如果有需要對
          日歷中的某些元素的樣式進行改變,可以通過瀏覽器的 檢查 功能來查看
          JavaScript 創建的元素并對其樣式進行適當的修改。
          */ /* 
          有些元素需要通過修改傳遞給 dycalendar.draw() 的配置對象中的
          部分屬性才能夠被發現。
          */ /* 設置日歷的頭部部分的樣式 */ #calendar .dycalendar-header{ margin-top: 60px; font-size: 20px; } /*
           設置日歷 "<" 及 ">" 按鈕的樣式,應用該樣式時請將 
           傳遞給 dycalendar.draw() 的配置對象中的 prevnextbutton 
           屬性的值設置為 true 。
           */ #calendar .dycalendar-header .prev-btn,
          #calendar .dycalendar-header .next-btn{ width: 40px; height: 30px; background-color: rgb(255, 255, 255, 0.15); /* 設置文本對其方式及行高以使 ">" 及 "<" 居中顯示 */ text-align: center; line-height: 30px; /* 設置上下方向的外邊距為 0px,設置左右方向的外邊距為 5px */ margin: 0px 5px; } #calendar .dycalendar-body table{ width: 100%; height: 100%; margin-top: 50px; } /* tr:nth-child(1) 選擇 table 標簽中的第一個 tr 元素 */ /* 設置日歷中星期(星期幾)標識的樣式 */ #calendar .dycalendar-body table tr:nth-child(1) td{ background-color: rgb(255, 255, 255, 0.15); margin-bottom: 20px; } #calendar .dycalendar-body table td{ border-radius: 3px; /* 設置鼠標懸停時的指針樣式 */ cursor: pointer; } /* 
          :hover 偽類選擇器用于設置鼠標懸停在指定元素時,
          某個元素的樣式
          */ #calendar .dycalendar-today-date,
          #calendar .dycalendar-body table td:hover{ color: #000; /* 
              使用 !important 提升該屬性在多個設置了該屬性的選擇器
              中的權重
              */ background-color: #fff !important; } 
          

          使用SpringBoot一小時快速搭建一個簡單后臺管理(增刪改查)(超詳細教程)

          前端達人

           最近也是臨近期末了,各種的期末大作業,后臺管理也是很多地方需要用到的,為了方便大家能快速上手,快速搭建一個簡單的后臺管理,我花了兩天時間整理了一下

          我會從0開始介紹,從數據庫的設計到前端頁面的引入最后到后端代碼的編寫,你只需要會一點前端的基礎和ssm的基礎就能快速上手搭建一個簡單的后臺管理

          本次案例分兩篇文章教學:

          (第一篇):數據表設計,前端框架引入和編寫前端頁面,搭建基本的springboot項目,引入前端到springboot項目中,在瀏覽器顯示

          (第二篇):后端代碼的設計,這部分邏輯涉及的比較多,所以單獨放一篇出來講,代碼從0手敲講解,保證你能學會,完成增刪改查的功能

          各大技術基礎教學、實戰開發教學(最新更新時間2021-12-4)

          目錄

          前言和環境介紹

          數據庫

          H+前端框架

          基本介紹 

          H+框架引入

          編寫后臺表格頁面

          modal彈出框原理

          搭建后端

          基本介紹

          創建SpringBoot項目

          基本配置

          引入前端

          測試項目(測試一下頁面是否能顯示)

          Gitee開源項目地址(本次項目源碼)

          各大技術基礎教學、實戰項目開發教學


          前言和環境介紹

          無論是做app,網站,還是小程序,都少不了后臺管理

          那么對于前端不是很會,后端也是只會一些的人來說,如何快速搭建一個簡單的后臺管理系統呢,哎別急,今天就來教大家簡單快速搭建一個后臺管理系統

          首先,簡單介紹一下我的開發環境

          工具 用處
          H+ 前端框架,直觀的教程文檔,非常實用
          SpringBoot 后端框架,簡單上手,搭建快
          MySQL 數據庫
          IDEA 非常強大的編譯器
          Ajax 異步請求,前端向后端發送請求
          thymeleaf 模板引擎,實時渲染頁面,基于HTML
          HBuild X 前端編譯器,用其他的也可以,看自己

          好了,環境介紹完畢,我們先從前端界面做起

          數據庫

          一個后臺管理,肯定少不了數據,不然怎么叫后臺管理呢

          這里我是用的是MySQL數據庫,當然你使用其他的也行,不過后面在SpringBoot中要做不同的配置

          在MySQL中新建一個user數據庫,新建一個t_user表,字段如下,id,用戶名,昵稱,密碼id記得設置為自增長模式

          H+前端框架

          基本介紹 

          官網地址:H+ 后臺主題UI框架 - 主頁

          H+是一個非常強大的前端開源框架,開箱即用,不需要過多的配置,里面有非常多組件,具體就不一一介紹了,有興趣的自己去看看

          我們要做的是后臺管理,所以我們直接找到表格,可以看到有很多樣式選擇,我們選擇一個簡單點

          這里為了方便快速搭建我選擇基本表格,當時你們可以根據自己喜歡來選擇



          H+框架引入

          HBuild X官網下載地址(無需安裝,解壓即用)

          打開HBuild X編譯器(你用其他的也可以,沒影響)

          在左側空白處新建一個項目(快捷鍵ctrl+n),選擇基于HTML普通項目,添加項目名稱,選好路徑,點擊創建

           一個基本的前端項目就創建好了

           接著,將H+框架的css,js,font等靜態資源全部復制項目下



          編寫后臺表格頁面

          找到H+框架中基本表格的源碼,復制代碼到index.htm下

          再對其進行一點修改,去掉右上角的工具欄,加上增刪改查的按鈕

          修改之后的頁面如下,用到了H+框中的表格、表單、按鈕、字體圖標庫,就不詳細介紹了,有興趣可以自己看看前端代碼,我們著重講解js和后端的搭建

          同時,我們還需要一個彈出框,當點擊添加和修改的時候會彈出一個表單框

          我們在H+前端框架的表單中找到彈出框示例,復制代碼做點修改

          修改后如下:



          modal彈出框原理

          HTML頁面代碼比較多,就不放上來了,底部Gitee倉庫完整的項目,主要講解一下上面這個modal彈出框怎么實現的就行

          首先,每個modal彈出框都有唯一的標識ID屬性,這里我們有兩個,一個添加用戶,一個修改用戶(里面的表單代碼我沒放出來,比較多,文章底部Gitee倉庫我上傳了完整開源項目)

          
                          
          1. <!-- 添加用戶的彈出框 -->
          2. <div id="modal-form-add" class="modal fade" aria-hidden="true">
          3. </div>
          
                      
          1. <!-- 修改用戶的彈出框 -->
          2. <div id="modal-form-update" class="modal fade" aria-hidden="true">
          3. </div>

          我們為每個修改和刪除按鈕都添加一個類名標識(這里為什么不用ID,是因為ID只能唯一標識,添加按鈕可以用ID,但是修改和刪除不能用ID只能用class,因為有多個修改和刪除按鈕,添加按鈕只有一個)

          我們在js文件夾下面新建一個myJS文件夾,存放自己編寫的js代碼,新建一個index.js,添加以下代碼

          
                          
          1. // 監聽添加按鈕事件(通過id屬性監聽)
          2. $('#addUserBtn').click(function() {
          3. // 添加按鈕被點擊之后,展示modal框
          4. $('#modal-form-add').modal('show');
          5. })
          6. // 監聽修改按鈕事件(通過class屬性監聽)
          7. $('.updateUserBtn').click(function() {
          8. // 修改按鈕被點擊之后,展示modal框
          9. $('#modal-form-update').modal('show');
          10. })

          然后再index.html底部引入index.js即可

          就可以實現點擊添加和修改按鈕會彈出modal表單框,簡單前端管理頁面就搭建完畢了,接下來是后端

          搭建后端

          基本介紹

          SpringBoot是目前非常主流的后端框架,簡化新spring應用的初始搭建以及開發過程,搭建快,省去了編寫大量配置文件的過程

          創建SpringBoot項目

          打開IDEA,選擇spring Initializr,點擊下一步

           添加主包名稱,java version版本選擇8,點擊下一步

          在左側找到這五個依賴,勾選上,點擊下一步

          添加項目名稱(默認為之前填寫的主包名稱),項目路徑自己選擇,點擊完成

          右下角處選擇自動導入pom.xml依賴(沒有的話直接跳過)

          一個基本的SpringBoot項目就創建完畢了



          基本配置

          在搭建項目之前,先做一些基本的配置,如數據源(連接MySQL)、static靜態資源映射(避免被攔截)、設置端口號(避免沖突)等

          刪除原有的application.properties文件,新建一個application.yml文件(兩種都可以,yml文件方便一點),添加以下配置信息

          
                      
          1. spring:
          2. # 數據源,連接MySQL數據庫
          3. datasource:
          4. url: jdbc:mysql://localhost:3306/user?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
          5. username: 數據庫用戶名
          6. password: 數據庫密碼
          7. driver-class-name: com.mysql.cj.jdbc.Driver
          8. # JPA配置,打印sql語句
          9. jpa:
          10. show-sql: true
          11. properties:
          12. hibernate:
          13. format_sql: true
          14. # mvc配置,映射html頁面
          15. mvc:
          16. static-path-pattern: /**
          17. view:
          18. prefix: /
          19. suffix: .html
          20. # thymeleaf模板引擎配置,設置編碼,false取消緩存
          21. thymeleaf:
          22. encoding: UTF-8
          23. cache: false
          24. server:
          25. # 修改啟動端口號
          26. port: 8081
          27. # 靜態資源映射路徑
          28. web:
          29. resources:
          30. static-locations: classpath:/static/

          在項目目錄下新建一個config包(存在基本配置類),新建一個MyWebMVCConfig類,代碼如下

          
                      
          1. @Configuration
          2. public class MyWebMVCConfig implements WebMvcConfigurer {
          3. @Override
          4. public void addResourceHandlers(ResourceHandlerRegistry registry) {
          5. System.out.println("==========靜態資源攔截!============");
          6. //將所有/static/** 訪問都映射到classpath:/static/ 目錄下
          7. registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
          8. }
          9. }


          引入前端

          把剛剛在HBuildX寫好的前端頁面引入到IDEA中,首先復制除了html的其他所有文件到static文件夾下

          接著復制index.html到templates文件夾下

          然后打開index.html,在所有訪問靜態資源的鏈接前面加上/static/

          測試項目(測試一下瀏覽器是否能顯示頁面)

          想項目目錄下新建一個controller包,新建一個IndexCtroller類,添加以下代碼

          
                      
          1. @RestController
          2. public class IndexController {
          3. @RequestMapping(value = "/index") // 訪問路徑
          4. public ModelAndView toIndex() {
          5. // 返回templates目錄下index.html
          6. ModelAndView view = new ModelAndView("index");
          7. return view;
          8. }
          9. }

           點擊啟動項目,選哪個都可以,我一般選第二個debug模式啟動,方式debug調試

           等待啟動完成之后,打開瀏覽器,輸入 localhost:8081/index,頁面成功顯示

          第一篇文章到這里介紹完畢,第二篇正在火速撰寫中······

          Gitee開源項目地址(本次項目源碼)

          SpringBoot項目教學合集: CSDN中的所有SpringBoot項目開源,持續更新新項目、新教學文章

          各大技術基礎教學、實戰項目開發教學

          各大技術基礎教學、實戰開發教學(最新更新時間2021-11-28)_Bald programmer的博客-CSDN博客

          文章知識點與官方知識檔案匹配,可進一步學習相關知識


          藍藍設計建立了UI設計分享群,每天會分享國內外的一些優秀設計,如果有興趣的話,可以進入一起成長學習,請加藍小助,微信號:ben_lanlan,報下信息,藍小助會請您入群。歡迎您加入噢~~希望得到建議咨詢、商務合作,也請與我們聯系01063334945。


          分享此文一切功德,皆悉回向給文章原作者及眾讀者.
          免責聲明:藍藍設計尊重原作者,文章的版權歸原作者。如涉及版權問題,請及時與我們取得聯系,我們立即更正或刪除。


          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務UI設計公司、界面設計公司、UI設計服務公司、數據可視化設計公司、UI交互設計公司、高端網站設計公司、UI咨詢、用戶體驗公司、軟件界面設計公司

          JS中的 回調函數(callback)

          前端達人

          目錄

          1.什么是回調函數(callback)呢?

          2.回調函數有哪些特點?

          3.回調函數中this的指向問題

          4.為什么要用到回調函數?

          5.回調函數和異步操作的關系是什么?回調函數是異步么?


          1.什么是回調函數(callback)呢?

          把函數當作一個參數傳到另外一個函數中,當需要用這個函數是,再回調運行()這個函數.

          回調函數是一段可執行的代碼段,它作為一個參數傳遞給其他的代碼,其作用是在需要的時候方便調用這段(回調函數)代碼。(作為參數傳遞到另外一個函數中,這個作為參數的函數就是回調函數)

          理解:函數可以作為一個參數傳遞到另外一個函數中。

          
          
          1. <script>
          2. function add(num1, num2, callback) {
          3. var sum = num1 + num2;
          4. callback(sum);
          5. }
          6. function print(num) {
          7. console.log(num);
          8. }
          9. add(1, 2, print); //3
          10. </script>

          分析:add(1, 2, print);中,函數print作為一個參數傳入到add函數中,但并不是馬上起作用,而是var sum = num1 + num2;運行完之后需要打印輸出sum的時候才會調用這個函數。(這個作為參數傳遞到另外一個函數中,這個作為參數的函數就是回調函數.

          匿名回調函數:

          
          
          1. <script>
          2. function add(num1, num2, callback) {
          3. var sum = num1 + num2;
          4. callback(sum);
          5. }
          6. add(1, 2, function (sum) {
          7. console.log(sum); //=>3
          8. });
          9. </script>

          2.回調函數有哪些特點?

          1.不會立即執行

          回調函數作為參數傳遞給一個函數的時候,傳遞的只是函數的定義并不會立即執行。和普通的函數一樣,回調函數在調用函數數中也要通過()運算符調用才會執行。

          2.回調函數是一個閉包

          回調函數是一個閉包,也就是說它能訪問到其外層定義的變量。

          3.執行前類型判斷

          在執行回調函數前最好確認其是一個函數。

          
          
          1. <script>
          2. function add(num1, num2, callback) {
          3. var sum = num1 + num2;
          4. //判定callback接收到的數據是一個函數
          5. if (typeof callback === 'function') {
          6. //callback是一個函數,才能當回調函數使用
          7. callback(sum);
          8. }
          9. }
          10. </script>

          3.回調函數中this的指向問題

          注意在回調函數調用時this的執行上下文并不是回調函數定義時的那個上下文,而是調用它的函數所在的上下文。

          舉例:

          
          
          1. <script>
          2. function createData(callback){
          3. callback();
          4. }
          5. var obj ={
          6. data:100,
          7. tool:function(){
          8. createData(function(n){
          9. console.log(this,1111); //window 1111
          10. })
          11. }
          12. }
          13. obj.tool();
          14. </script>

          分析:this指向是 離它最近的或者嵌套級別的 function/方法的調用者,這里離它最近的function是

          function(n),會回到上面的callback()中,這時候調用者就不是obj而是window。

           解決回調函數this指向的方法1:箭頭函數

          回調函數(若回調函數是普通函數時)當參數傳入另外的函數時,若不知道這個函數內部怎么調用回調函數,就會出現回調函數中的this指向不明確的問題(就比如上面例子中this指向的不是obj而是window)。所以 把箭頭函數當回調函數,然后作為參數傳入另外的函數中就不會出現this指向不明的問題。

          
          
          1. <script>
          2. function createData(callback){
          3. callback();
          4. }
          5. var obj ={
          6. data:100,
          7. tool:function(){
          8. createData((n)=>{
          9. this.data = n;
          10. })
          11. }
          12. }
          13. obj.tool();
          14. console.log(obj.data);
          15. </script>

           分析:回調函數用箭頭函數寫之后,this指向很明確,就是 離它最近的或者嵌套級別的 function/方法的調用者,所以這里是 obj 。

          解決回調函數this指向的方法2:var self = this;

          
          
          1. <script>
          2. function createData(callback){
          3. callback(999);
          4. }
          5. var obj ={
          6. data:100,
          7. tool:function(){
          8. var self = this; //這里的this指向obj,然后當一個變量取用
          9. createData(function(n){
          10. self.data = n;
          11. })
          12. }
          13. }
          14. obj.tool();
          15. console.log(obj.data);
          16. </script>

          4.為什么要用到回調函數?

          有一個非常重要的原因 —— JavaScript 是事件驅動的語言。這意味著,JavaScript 不會因為要等待一個響應而停止當前運行,而是在監聽其他事件時繼續執行。來看一個基本的例子:

          
          
          1. <script>
          2. function first() {
          3. console.log(1);
          4. }
          5. function second() {
          6. console.log(2);
          7. }
          8. first();
          9. second();
          10. </script>

           分析:正如你所料,first 函數首先被執行,隨后 second 被執行 —— 控制臺輸出:1  2

          但如果函數 first 包含某種不能立即執行的代碼會如何呢?例如我們必須發送請求然后等待響應的 API 請求?為了模擬這種狀況,我們將使用 setTimeout,它是一個在一段時間之后調用函數的 JavaScript 函數。我們將函數延遲 500 毫秒來模擬一個 API 請求,新代碼長這樣:

          
          
          1. <script>
          2. function first() {
          3. // 模擬代碼延遲
          4. setTimeout(function () { //所以function(){console.log(1)}是回調函數
          5. console.log(1);
          6. }, 500);
          7. }
          8. function second() {
          9. console.log(2);
          10. }
          11. first();
          12. second();
          13. </script>

          分析:這里 function(){console.log(1)}函數當作一個參數傳入setTimeout函數中,因為setTimeout是官方提供得一個函數,里面有很多復雜的業務程序,所以函數 function(){console.log(1)}傳入后,不一定馬上運行,要setTimeout里面要運行到function(){console.log(1)}時才會運行該函數參數,那是不是整個程序就一直等setTimeout運行?不是的?。?!

          整個程序運行結果為: 2  1 ,并不是原先的1 2 .即使我們首先調用了 first() 函數,我們記錄的輸出結果卻在 second() 函數之后。

          這不是 JavaScript 沒有按照我們想要的順序執行函數的問題,而是 JavaScript 在繼續向下執行 second() 之前沒有等待 first() 響應的問題。回調正是確保一段代碼執行完畢之后再執行另一段代碼的方式

          5.回調函數和異步操作的關系是什么?回調函數是異步么?

          定義:回調函數被認為是一種高級函數,一種被作為參數傳遞給另一個函數的高級函數?;卣{函數的本質是一種模式(一種解決常見問題的模式),因此回調函數也被稱為回調模式。 

          簡而言之:一個函數在另一個函數中被調用。而且可以當參數傳給其他函數。

          所以: 回調函數和異步操作的關系是沒有關系?。?! 

          那為什么很多的異步操作都有回填函數????

          問:你所知道的異步操作,是回調的作用么???  并不是。

          回調:更多的可以理解為一種業務邏輯把           異步編程:JS代碼的執行順序       

          簡單理解:callback 顧名思義  打電話回來的意思

          eg1:你點外賣,剛好你要吃的食物沒有了,于是你在店老板那里留下了你的電話,過了幾天店里有了,店員就打了你的電話,然后你接到電話后就跑到店里買了。在這個例子里,你的電話號碼就叫回調函數,你把電話留給店員就叫登記回調函數,店里后來有貨了叫做觸發了回調關聯的事件,店員給你打電話叫做調用回調函數,你到店里去取貨叫做響應回調事件。

          eg2:再比如,你發送一個axios 請求,請求成功之后,觸發成功的回調函數,請求失敗觸發失敗的回調函數。這里面的回調函數更像是一個工具,后臺通過這個工具告訴你,你成功了抑或是失敗了。這里面的所有異步操作都和回調沒關系,真正的異步是then方法。




          藍藍設計建立了UI設計分享群,每天會分享國內外的一些優秀設計,如果有興趣的話,可以進入一起成長學習,請加藍小助,微信號:ben_lanlan,報下信息,藍小助會請您入群。歡迎您加入噢~~希望得到建議咨詢、商務合作,也請與我們聯系01063334945。


          分享此文一切功德,皆悉回向給文章原作者及眾讀者.
          免責聲明:藍藍設計尊重原作者,文章的版權歸原作者。如涉及版權問題,請及時與我們取得聯系,我們立即更正或刪除。


          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 平面設計服務、UI設計公司、界面設計公司、UI設計服務公司、數據可視化設計公司、UI交互設計公司、高端網站設計公司、UI咨詢、用戶體驗公司、軟件界面設計公司

          THREE.JS實現看房自由(VR看房)

          前端達人


          一、前言

          概述:基于WebGL的三維引擎,目前是國內資料最多、使用最廣泛的三維引擎,可以制作一些3D可視化項目

          在這里插入圖片描述

          目前隨著元宇宙概念的爆火,THREE技術已經深入到了物聯網、VR、游戲、數據可視化等多個平臺,今天我們主要基于THREE實現一個三維的VR看房小項目

          二、基礎知識

          在這里插入圖片描述

          Three.js一般分為三個部分:場景、相機、渲染器,這三個主要的分支就構成了THREE.JS的主要功能區,這三大部分還有許多細小的分支,這些留到我們后續抽出一些章節專門講解一下。

          在這里插入圖片描述

          工作流程:場景——相機——渲染器

          實際生活中拍照角度立方體網格模型和光照組成了一個虛擬的三維場景,相機對象就像你生活中使用的相機一樣可以拍照,只不過一個是拍攝真實的景物,一個是拍攝虛擬的景物。拍攝一個物體的時候相機的位置和角度需要設置,虛擬的相機還需要設置投影方式,當你創建好一個三維場景,相機也設置好,就差一個動作“咔”,通過渲染器就可以執行拍照動作。

          三、場景

          概述:場景主要由網絡模型與光照組成,網絡模型分為幾何體與材質

          3.1 網絡模型

          幾何體就像我們小時候學我們就知道點線面體四種概念,點動成線,線動成面,面動成體,而材質就像是是幾何體上面的涂鴉,有不同的顏色、圖案…

          例子如下:

          在這里插入圖片描述

          //打造酷炫三角形 for (let i = 0; i < 50; i++) { const geometry = new THREE.BufferGeometry(); const arr = new Float32Array(9); for (let j = 0; j < 9; j++) { arr[j] = Math.random() * 5; } geometry.setAttribute('position', new THREE.BufferAttribute(arr, 3)); let randomColor = new THREE.Color(Math.random(), Math.random(), Math.random()); const material = new THREE.MeshBasicMaterial({ color: randomColor, transparent: true, opacity:0.5, }); const mesh = new THREE.Mesh(geometry, material); scene.add(mesh); } 
                  
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17

          [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-UlBSgxKr-1666681292595)(https://gitee.com/riskbaby/picgo/raw/master/blog/202209211037215.png#pic_center)]

          const geometry = new THREE.BoxGeometry(100, 100, 100); const material = new THREE.MeshStandardMaterial({ color: 0x0000ff }); const cube = new THREE.Mesh(geometry, material); scene.add(cube); 
                  
          • 1
          • 2
          • 3
          • 4

          在這里插入圖片描述

          const geometry = new THREE.ConeGeometry(5, 15, 32);//底面半徑 高 側邊三角分段 const material = new THREE.MeshStandardMaterial({ color: 0x0000ff }); const clone = new THREE.Mesh(geometry, material); scene.add(clone); 
                  
          • 1
          • 2
          • 3
          • 4

          3.2 光照

          3.2.1 環境光

          概念:光照對three.js的物體全表面進行光照測試,有可能會發生光照融合

          在這里插入圖片描述

          //環境光 const ambient = new THREE.AmbientLight(0x404040); scene.add(ambient); 
                  
          • 1
          • 2
          • 3

          3.2.2 平行光

          概念:向特定方向發射的光,太陽光也視作平行的一種,和上面比較,物體變亮了

          //平行光  顏色 強度 const directionalLight = new THREE.DirectionalLight(0xffffff, 1); directionalLight.position.set(100, 100, 100);//光源位置 directionalLight.target = cube;//光源目標 默認 0 0 0 scene.add(directionalLight); 
                  
          • 1
          • 2
          • 3
          • 4
          • 5

          3.2.3 點光源

          概念:由中間向四周發射光、強度比平行光小

          在這里插入圖片描述

          // 顏色 強度 距離 衰退量(默認1) const pointLight = new THREE.PointLight(0xff0000, 1, 100, 1); pointLight.position.set(50, 50, 50); scene.add(pointLight); 
                  
          • 1
          • 2
          • 3
          • 4
          • 5

          3.2.4 聚光燈

          概念:家里面的節能燈泡,強度較好

          在這里插入圖片描述

          //聚光燈 const spotLigth = new THREE.PointLight(0xffffff); spotLigth.position.set(50, 50, 50); spotLigth.target = cube; spotLigth.angle = Math.PI / 6; scene.add(spotLigth); 
                  
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6

          3.2.5 半球光

          概念:光源直接放置于場景之上,光照顏色從天空光線顏色漸變到地面光線顏色

          在這里插入圖片描述

          //半球光 const light = new THREE.HemisphereLight(0xffffbb, 0x080820, 1);//天空 場景  scene.add(light); 
                  
          • 1
          • 2
          • 3

          四、相機

          4.1 正交相機

          在這里插入圖片描述

          參數(屬性) 含義
          left 渲染空間的左邊界
          right 渲染空間的右邊界
          top 渲染空間的上邊界
          bottom 渲染空間的下邊界
          near near屬性表示的是從距離相機多遠的位置開始渲染,一般情況會設置一個很小的值。 默認值0.1
          far far屬性表示的是距離相機多遠的位置截止渲染,如果設置的值偏小小,會有部分場景看不到。 默認值1000
          let width = window.innerWidth; let height = window.innerHeight; const camera = new THREE.OrthographicCamera(width / - 2, width / 2, height / 2, height / - 2, 1, 1000); scene.add(camera); camera.position.set(100, 200, 100); 
                  
          • 1
          • 2
          • 3
          • 4
          • 5

          4.2 透視相機

          在這里插入圖片描述

          參數 含義 默認值
          fov fov表示視場,所謂視場就是能夠看到的角度范圍,人的眼睛大約能夠看到180度的視場,視角大小設置要根據具體應用,一般游戲會設置60~90度 45
          aspect aspect表示渲染窗口的長寬比,如果一個網頁上只有一個全屏的canvas畫布且畫布上只有一個窗口,那么aspect的值就是網頁窗口客戶區的寬高比 window.innerWidth/window.innerHeight
          near near屬性表示的是從距離相機多遠的位置開始渲染,一般情況會設置一個很小的值。 0.1
          far far屬性表示的是距離相機多遠的位置截止渲染,如果設置的值偏小,會有部分場景看不到 1000
          let width = window.innerWidth; let height = window.innerHeight; const camera = new THREE.PerspectiveCamera(45, width / height, 1, 1000); camera.position.set(150, 100, 300); camera.lookAt(scene.position); 
                  
          • 1
          • 2
          • 3
          • 4
          • 5

          五、渲染器

          概述:從WEBGL的角度來看,three就是對它的進一步封裝,想要進一步了解渲染器這方面的知識點還需要了解一下WEBGL,這里我們就不做過多介紹了。

          六、貼圖紋理

          6.1 基礎介紹

          概述:這部分對于我們是否能夠給別人呈現一個真實的渲染場景來說,很重要,比如下面一個普普通通的正方體,我們只要一加上貼圖,立馬不一樣了。

          以前

          在這里插入圖片描述

          之后

          在這里插入圖片描述

          6.2 環境貼圖

          概述:目前有許許多多的貼圖,比如基礎、透明、環境、法線、金屬、粗糙、置換等等,今天我們呢主要講解一下環境和一點 HDR處理

          THREE的世界里面,坐標抽x、y、z的位置關系圖如下所示:

          2

          紅、綠、藍分別代表x、z、y,我們的貼圖就是在px nx py ny pz nz這六個方向防止一張圖片,其中p就代表坐標軸的正方向

          CubeTextureLoader:加載CubeTexture的一個類。 內部使用ImageLoader來加載文件。

          //場景貼圖 const sphereTexture = new THREE.CubeTextureLoader().setPath('./textures/course/environmentMaps/0/'); const envTexture= sphereTexture.load([ 'px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg' ]); //場景添加背景 scene.background = envTexture; //場景的物體添加環境貼圖(無默認情況使用) scene.environment = envTexture; const sphereGeometry = new THREE.SphereGeometry(5, 30, 30); const sphereMaterial = new THREE.MeshStandardMaterial({ roughness: 0,//設置粗糙程度 metalness: 1,//金屬度 envMap:envTexture, }); const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial); scene.add(sphere); 
                  
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25

          gif圖片有點大上傳不了,我就截了幾張圖

          在這里插入圖片描述

          在這里插入圖片描述

          在這里插入圖片描述

          請添加圖片描述

          請添加圖片描述

          6.3 HDR處理

          概述:高動態范圍圖像,相比普通的圖像,能夠提供更多的動態范圍和圖像細節,一般被運用于電視顯示產品以及圖片視頻拍攝制作當中。

          在這里插入圖片描述

          import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader; const rgbeLoader = new RGBELoader().setPath('./textures/course/hdr/'); //異步加載 rgbeLoader.loadAsync('002.hdr').then((texture) => { //設置加載方式 等距圓柱投影的環境貼圖 texture.mapping = THREE.EquirectangularReflectionMapping; scene.background = texture; }) 
                  
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9

          七、拓展

          7.1 坐標系

          概述:坐標軸能夠更好的反饋物體的位置信息,紅、綠、藍分別代表x、z、y

          在這里插入圖片描述

          const axesHelper = new THREE.AxesHelper(20);//里面的數字代表坐標抽長度 scene.add(axesHelper); 
                  
          • 1
          • 2

          7.2 控制器

          概述:通過鼠標控制物體和相機的移動、旋轉、縮放

          導包

          import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' 
                  
          • 1

          應用

          const controls = new OrbitControls(camera, renderer.domElement) 
                  
          • 1

          自旋轉

          在這里插入圖片描述

          controls.autoRotate = true 
                  
          • 1

          必須在render函數調用update實時更新才奏效

          7.3 自適應

          概述:根據屏幕大小自適應場景
          在這里插入圖片描述

          //自適應屏幕 window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight
              camera.updateProjectionMatrix() renderer.setSize(window.innerWidth, window.innerHeight) renderer.setPixelRatio(window.devicePixelRatio) }) 
                  
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7

          設置相機的寬高比、重新更新渲染相機、渲染器的渲染大小、設備的像素比

          7.4 全屏響應

          概述:雙擊進入全屏,再次雙擊/ESC退出全屏

          在這里插入圖片描述

          window.addEventListener('dblclick', () => { let isFullScreen = document.fullscreenElement if (!isFullScreen) { renderer.domElement.requestFullscreen() } else { document.exitFullscreen() } }) 
                  
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9

          7.5 信息面板

          概述;通過操作面板完成界面的移動物體的相關應用

          鏈接:https://www.npmjs.com/package/dat.gui

          //安裝npm npm install --save dat.gui //如果出現...標記錯誤,安裝到開發依賴就可以了 npm i --save-dev @types/dat.gui 
                  
          • 1
          • 2
          • 3
          • 4

          1

          //界面操作 const gui = new dat.GUI(); //操作物體位置 gui .add(cube.position, 'x') .min(0) .max(10) .step(0.1) .name('X軸移動') .onChange((value) => { console.log('修改的值為' + value); }) .onFinishChange((value) => { console.log('完全停止' + value); }); //操作物體顏色 const colors = { color: '#0000ff', }; gui .addColor(colors, 'color') .onChange((value) => { //修改物體顏色 cube.material.color.set(value); }); 
                  
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26

          7.6 頻率檢測

          概述:檢測幀率

          導包

          import Stats from 'three/addons/libs/stats.module.js'; 
                  
          • 1

          應用

          const stats = new Stats(); document.body.appendChild(stats.dom); 
                  
          • 1
          • 2

          自變化

          20221021_110002

          stats.update() 
                  
          • 1

          必須在render函數調用update實時更新才奏效

          7.7 導航網格

          概述:底部二維平面的網格化,幫助我們更好的創建場景

          在這里插入圖片描述

          const gridHelper = new THREE.GridHelper(10, 20)//網格大小、細分次數 scene.add(gridHelper) 
                  
          • 1
          • 2

          八、源碼

          //導入包 import * as THREE from 'three'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'; import * as dat from 'dat.gui'; import Stats from 'three/addons/libs/stats.module.js'; let scene,camera,renderer //場景 scene = new THREE.Scene(); //坐標抽 const axesHelper = new THREE.AxesHelper(20); scene.add(axesHelper); //場景貼圖 const sphereTexture = new THREE.CubeTextureLoader().setPath('./textures/course/environmentMaps/0/'); const envTexture= sphereTexture.load([ 'px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg' ]); //場景添加背景 scene.background = envTexture; //場景的物體添加環境貼圖(無默認情況使用) scene.environment = envTexture; const sphereGeometry = new THREE.SphereGeometry(5, 30, 30); const sphereMaterial = new THREE.MeshStandardMaterial({ roughness: 0,//設置粗糙程度 metalness: 1,//金屬度 envMap:envTexture, }); const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial); scene.add(sphere); //光照 const ambient = new THREE.AmbientLight(0xffffff); scene.add(ambient); const directionalLight = new THREE.DirectionalLight(0xffffff, 0.05); directionalLight.position.set(10,10,10); directionalLight.lookAt(scene.position); scene.add( directionalLight ); //相機 camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 2000, ); camera.position.set(10,10,20); camera.lookAt(scene.position); scene.add(camera); //渲染器 renderer = new THREE.WebGLRenderer({ //防止鋸齒 antialias: true, }); renderer.setSize(window.innerWidth, window.innerHeight); // renderer.setClearColor(0xb9d3ff, 1); document.body.appendChild(renderer.domElement); //鼠標控制器 const controls = new OrbitControls(camera, renderer.domElement); //阻尼 必須在 render函數調用  controls.update(); controls.dampingFactor = true; controls.autoRotate=true const stats=new Stats() document.body.appendChild(stats.dom); function render () { renderer.render(scene, camera); requestAnimationFrame(render); controls.update();//調用 stats.update() } render(); //全屏操作 window.addEventListener('dblclick', () => { //查詢是否全屏 let isFullScene = document.fullscreenElement; console.log(isFullScene); if (!isFullScene) { renderer.domElement.requestFullscreen(); } else { document.exitFullscreen(); } }) //自適應 window.addEventListener('resize', () => { //寬高比 camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(window.devicePixelRatio);//設置像素比 }) //界面操作 const gui = new dat.GUI(); //操作物體位置 gui .add(sphere.position, 'x') .min(0) .max(10) .step(0.1) .name('X軸移動') .onChange((value) => { console.log('修改的值為' + value); }) .onFinishChange((value) => { console.log('完全停止' + value); }); //操作物體顏色 const colors = { color: '#0000ff', }; gui .addColor(colors, 'color') .onChange((value) => { //修改物體顏色 sphere.material.color.set(value); }); 
                  
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
          • 27
          • 28
          • 29
          • 30
          • 31
          • 32
          • 33
          • 34
          • 35
          • 36
          • 37
          • 38
          • 39
          • 40
          • 41
          • 42
          • 43
          • 44
          • 45
          • 46
          • 47
          • 48
          • 49
          • 50
          • 51
          • 52
          • 53
          • 54
          • 55
          • 56
          • 57
          • 58
          • 59
          • 60
          • 61
          • 62
          • 63
          • 64
          • 65
          • 66
          • 67
          • 68
          • 69
          • 70
          • 71
          • 72
          • 73
          • 74
          • 75
          • 76
          • 77
          • 78
          • 79
          • 80
          • 81
          • 82
          • 83
          • 84
          • 85
          • 86
          • 87
          • 88
          • 89
          • 90
          • 91
          • 92
          • 93
          • 94
          • 95
          • 96
          • 97
          • 98
          • 99
          • 100
          • 101
          • 102
          • 103
          • 104
          • 105
          • 106
          • 107
          • 108
          • 109
          • 110
          • 111
          • 112
          • 113
          • 114
          • 115
          • 116
          • 117
          • 118
          • 119
          • 120
          • 121
          • 122
          • 123
          • 124
          • 125
          • 126
          • 127
          • 128
          • 129
          • 130
          • 131
          • 132
          • 133
          • 134
          • 135
          • 136
          • 137
          • 138
          • 139
          • 140
          • 141
          • 142
          • 143

          WebSocket

          前端達人

          目錄

          一,什么是websocket

          二,websocket的原理

          三,websocket與http的關系

          四,websocket解決的問題

          1.http存在的問題

          2.long poll(長輪詢)

          3.Ajax輪詢

          4.websocket的改進


          一,什么是websocket

          • WebSocket是HTML5下一種新的協議(websocket協議本質上是一個基于tcp的協議)
          • 它實現了瀏覽器與服務器全雙工通信,能更好的節省服務器資源和帶寬并達到實時通訊的目的
          • Websocket是一個持久化的協議

          二,websocket的原理

          1. websocket約定了一個通信的規范,通過一個握手的機制,客戶端和服務器之間能建立一個類似tcp的連接,從而方便它們之間的通信
          2. 在websocket出現之前,web交互一般是基于http協議的短連接或者長連接
          3. websocket是一種全新的協議,不屬于http無狀態協議,協議名為"ws"

          三,websocket與http的關系

           相同點:

          1. 都是基于tcp的,都是可靠性傳輸協議
          2. 都是應用層協議

          不同點:

          1. WebSocket是雙向通信協議,模擬Socket協議,可以雙向發送或接受信息
          2. HTTP是單向的
          3. WebSocket是需要瀏覽器和服務器握手進行建立連接的
          4. 而http是瀏覽器發起向服務器的連接,服務器預先并不知道這個連接

           聯系:

          • WebSocket在建立握手時,數據是通過HTTP傳輸的。但是建立之后,在真正傳輸時候是不需要HTTP協議的

          總結(總體過程):

          1. 首先,客戶端發起http請求,經過3次握手后,建立起TCP連接;http請求里存放WebSocket支持的版本號等信息,如:Upgrade、Connection、WebSocket-Version等;
          2. 然后,服務器收到客戶端的握手請求后,同樣采用HTTP協議回饋數據;
          3. 最后,客戶端收到連接成功的消息后,開始借助于TCP傳輸信道進行全雙工通信。

          四,websocket解決的問題

          1.http存在的問題

          • http是一種無狀態協議,每當一次會話完成后,服務端都不知道下一次的客戶端是誰,需要每次知道對方是誰,才進行相應的響應,因此本身對于實時通訊就是一種極大的障礙
          • http協議采用一次請求,一次響應,每次請求和響應就攜帶有大量的header頭,對于實時通訊來說,解析請求頭也是需要一定的時間,因此,效率也更低下
          • 最重要的是,需要客戶端主動發,服務端被動發,也就是一次請求,一次響應,不能實現主動發送

          2.long poll(長輪詢)

          • 對于以上情況就出現了http解決的第一個方法——長輪詢
          • 基于http的特性,簡單點說,就是客戶端發起長輪詢,如果服務端的數據沒有發生變更,會 hold 住請求,直到服務端的數據發生變化,或者等待一定時間超時才會返回。返回后,客戶端又會立即再次發起下一次長輪詢
          • 優點是解決了http不能實時更新的弊端,因為這個時間很短,發起請求即處理請求返回響應,實現了“偽·長連接”
          • 張三取快遞的例子,張三今天一定要取到快遞,他就一直站在快遞點,等待快遞一到,立馬取走

           從例子上來看有個問題:

          1. 假如有好多人一起在快遞站等快遞,那么這個地方是否足夠大,(抽象解釋:需要有很高的并發,同時有很多請求等待在這里)
          • 總的來看:
          • 推送延遲。服務端數據發生變更后,長輪詢結束,立刻返回響應給客戶端。

          • 服務端壓力。長輪詢的間隔期一般很長,例如 30s、60s,并且服務端 hold 住連接不會消耗太多服務端資源。

          3.Ajax輪詢

          • 基于http的特性,簡單點說,就是規定每隔一段時間就由客戶端發起一次請求,查詢有沒有新消息,如果有,就返回,如果沒有等待相同的時間間隔再次詢問
          • 優點是解決了http不能實時更新的弊端,因為這個時間很短,發起請求即處理請求返回響應,把這個過程放大n倍,本質上還是request = response
          • 舉個形象的例子(假設張三今天有個快遞快到了,但是張三忍耐不住,就每隔十分鐘給快遞員或者快遞站打電話,詢問快遞到了沒,每次快遞員就說還沒到,等到下午張三的快遞到了,but,快遞員不知道哪個電話是張三的,(可不是只有張三打電話,還有李四,王五),所以只能等張三打電話,才能通知他,你的快遞到了)

           從例子上來看有兩個問題:

          1. 假如說,張三打電話的時間間隔為10分鐘,當他收到快遞前最后一次打電話,快遞員說沒到,他剛掛掉電話,快遞入庫了(就是到了),那么等下一次時間到了,張三打電話知道快遞到了,那么這樣的通訊算不算實時通訊?很顯然,不算,中間有十分鐘的時間差,還不算給快遞員打電話的等待時間(抽象的解釋:每次request的請求時間間隔等同于十分鐘,請求解析相當于等待)
          2. 假如說張三所在的小區每天要收很多快遞,每個人都采取主動給快遞員打電話的方式,那么快遞員需要以多快的速度接到,其他人打電話占線也是問題(抽象解釋:請求過多,服務端響應也會變慢)
          • 總的來看,Ajax輪詢存在的問題:
          1. 推送延遲。

          2. 服務端壓力。配置一般不會發生變化,頻繁的輪詢會給服務端造成很大的壓力。

          3. 推送延遲和服務端壓力無法中和。降低輪詢的間隔,延遲降低,壓力增加;增加輪詢的間隔,壓力降低,延遲增高

          4.websocket的改進

          一旦WebSocket連接建立后,后續數據都以幀序列的形式傳輸。在客戶端斷開WebSocket連接或Server端中斷連接前,不需要客戶端和服務端重新發起連接請求。在海量并發及客戶端與服務器交互負載流量大的情況下,極大的節省了網絡帶寬資源的消耗,有明顯的性能優勢,且客戶端發送和接受消息是在同一個持久連接上發起,實現了“真·長鏈接”,實時性優勢明顯。

           

          WebSocket有以下特點:

          • 是真正的全雙工方式,建立連接后客戶端與服務器端是完全平等的,可以互相主動請求。而HTTP長連接基于HTTP,是傳統的客戶端對服務器發起請求的模式。
          • HTTP長連接中,每次數據交換除了真正的數據部分外,服務器和客戶端還要大量交換HTTP header,信息交換效率很低。Websocket協議通過第一個request建立了TCP連接之后,之后交換的數據都不需要發送 HTTP header就能交換數據,這顯然和原有的HTTP協議有區別所以它需要對服務器和客戶端都進行升級才能實現(主流瀏覽器都已支持HTML5)




          藍藍設計建立了UI設計分享群,每天會分享國內外的一些優秀設計,如果有興趣的話,可以進入一起成長學習,請加藍小助,微信號:ben_lanlan,報下信息,藍小助會請您入群。歡迎您加入噢~~希望得到建議咨詢、商務合作,也請與我們聯系01063334945。


          分享此文一切功德,皆悉回向給文章原作者及眾讀者.
          免責聲明:藍藍設計尊重原作者,文章的版權歸原作者。如涉及版權問題,請及時與我們取得聯系,我們立即更正或刪除。


          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務UI設計公司、界面設計公司、UI設計服務公司、數據可視化設計公司、UI交互設計公司、高端網站設計公司、UI咨詢、用戶體驗公司、軟件界面設計公司

          前后端的身份認證

          前端達人

          1、Web 開發模式

          目前主流的 Web 開發模式有兩種,分別是:

          • 基于服務端渲染的傳統 Web 開發模式
          • 基于前后端分離的新型 Web 開發模式

          1.1、服務端渲染的 Web 開發模式

                  服務端渲染的概念:服務器發送給客戶端的 HTML 頁面,是在服務器通過字符串的拼接,動態生成的。因此,客戶端不需要使用 Ajax 這樣的技術額外請求頁面的數據。代碼示例如下:

          1.2、服務端渲染的優缺點 

          優點:

          •  前端耗時少。因為服務器端負責動態生成 HTML 內容,瀏覽器只需要直接渲染頁面即可。尤其是移動端,更省電。
          • 有利于SEO。因為服務器端響應的是完整的 HTML 頁面內容,所以爬蟲更容易爬取獲得信息,更有利于 SEO。

          缺點:

          • 占用服務器端資源。即服務器端完成 HTML 頁面內容的拼接,如果請求較多,會對服務器造成一定的訪問壓力。
          • 不利于前后端分離,開發效率低。使用服務器端渲染,則無法進行分工合作,尤其對于前端復雜度高的項目,不利于項目高效開發。

          1.3、前后端分離的 Web 開發模式

                  前后端分離的概念:前后端分離的開發模式,依賴于 Ajax 技術的廣泛應用。簡而言之,前后端分離的 Web 開發模式,就是后端只負責提供 API 接口,前端使用 Ajax 調用接口的開發模式。

          1.4、前后端分離的優缺點

          優點:

          • 開發體驗好。前端專注于 UI 頁面的開發,后端專注于api 的開發,且前端有更多的選擇性。
          • 用戶體驗好。Ajax 技術的廣泛應用,極大的提高了用戶的體驗,可以輕松實現頁面的局部刷新。
          • 減輕了服務器端的渲染壓力。因為頁面最終是在每個用戶的瀏覽器中生成的。

          缺點:

                  不利于 SEO。因為完整的 HTML 頁面需要在客戶端動態拼接完成,所以爬蟲對無法爬取頁面的有效信息。(解決方案:利用 Vue、React 等前端框架的 SSR server side render)技術能夠很好的解決 SEO 問題?。?/span>

          1.5、如何選擇 Web 開發模式

          不談業務場景而盲目選擇使用何種開發模式都是耍流氓。

          • 比如企業級網站,主要功能是展示而沒有復雜的交互,并且需要良好的 SEO,則這時我們就需要使用服務器端渲染;
          • 而類似后臺管理項目,交互性比較強,不需要考慮 SEO,那么就可以使用前后端分離的開發模式。

                  另外,具體使用何種開發模式并不是絕對的,為了同時兼顧首頁的渲染速度前后端分離的開發效率,一些網站采用了首屏服務器端渲染 其他頁面前后端分離的開發模式。

          2、身份認證

          2.1、什么是身份認證

                  身份認證Authentication)又稱“身份驗證”、“鑒權”,是指通過一定的手段,完成對用戶身份的確認

          • 日常生活中的身份認證隨處可見,例如:高鐵的驗票乘車,手機的密碼或指紋解鎖,支付寶或微信的支付密碼等。
          • 在 Web 開發中,也涉及到用戶身份的認證,例如:各大網站的手機驗證碼登錄、郵箱密碼登錄、二維碼登錄等。

          2.2、為什么需要身份認證

                  身份認證的目的,是為了確認當前所聲稱為某種身份的用戶,確實是所聲稱的用戶。例如,你去找快遞員取快遞,你要怎么證明這份快遞是你的。

                  在互聯網項目開發中,如何對用戶的身份進行認證,是一個值得深入探討的問題。例如,如何才能保證網站不會錯誤的將“馬云的存款數額”顯示到“馬化騰的賬戶”上。

          2.3、不同開發模式下的身份認證

          對于服務端渲染前后端分離這兩種開發模式來說,分別有著不同的身份認證方案:

          • 服務端渲染推薦使用 Session 認證機制
          • 前后端分離推薦使用 JWT 認證機制

          3、Session 認證機制

          3.1、HTTP 協議的無狀態性

          • 了解 HTTP 協議的無狀態性是進一步學習 Session 認證機制的必要前提。
          • HTTP 協議的無狀態性,指的是客戶端的每次 HTTP 請求都是獨立的,連續多個請求之間沒有直接的關系,服務器不會主動保留每次 HTTP 請求的狀態

          3.2、如何突破 HTTP 無狀態的限制 

                  對于超市來說,為了方便收銀員在進行結算時給 VIP 用戶打折,超市可以為每個 VIP 用戶發放會員卡。

          注意:現實生活中的會員卡身份認證方式,在 Web 開發中的專業術語叫做 Cookie 

          3.3、什么是 Cookie

          • Cookie 存儲在用戶瀏覽器中的一段不超過 4 KB 的字符串。它由一個名稱Name)、一個Value)和其它幾個用于控制 Cookie 有效期、安全性、使用范圍可選屬性組成。
          • 不同域名下的 Cookie 各自獨立,每當客戶端發起請求時,會自動當前域名下所有未過期的 Cookie 一同發送到服務器。

          Cookie的幾大特性:

          1. 自動發送
          2. 域名獨立
          3. 過期時限
          4. 4KB 限制

          3.4、Cookie 在身份認證中的作用

                  客戶端第一次請求服務器的時候,服務器通過響應頭的形式,向客戶端發送一個身份認證的 Cookie,客戶端會自動將 Cookie 保存在瀏覽器中。

                  隨后,當客戶端瀏覽器每次請求服務器的時候,瀏覽器會自動將身份認證相關的 Cookie通過請求頭的形式發送給服務器,服務器即可驗明客戶端的身份。

          3.5、Cookie 不具有安全性 

                  由于 Cookie 是存儲在瀏覽器中的,而且瀏覽器也提供了讀寫 Cookie 的 API,因此 Cookie 很容易被偽造,不具有安全性。因此不建議服務器將重要的隱私數據,通過 Cookie 的形式發送給瀏覽器。

          注意:千萬不要使用 Cookie 存儲重要且隱私的數據!比如用戶的身份信息、密碼等。 

          3.6、提高身份認證的安全性

                  為了防止客戶偽造會員卡,收銀員在拿到客戶出示的會員卡之后,可以在收銀機上進行刷卡認證。只有收銀機確認存在的會員卡,才能被正常使用。

          這種“會員卡 + 刷卡認證”的設計理念,就是 Session 認證機制的精髓。 

          3.7、Session 工作原理

           

          4、在 Express 中使用 Session 認證

          4.1、安裝 express-session 中間件

          在 Express 項目中,只需要安裝 express-session 中間件,即可在項目中使用 Session 認證:

          npm install express-session

          4.2、配置 express-session 中間件

                  express-session 中間件安裝成功后,需要通過 app.use() 注冊 session 中間件,示例代碼如下:

          
          
          1. // 導入 session 中間件
          2. const session = require('express-session')
          3. // 配置 session 中間件
          4. app.use(
          5. session({
          6. secret: 'itheima', // secret 屬性的值可以為任意字符串
          7. resave: false, // 固定寫法
          8. saveUninitialized: true, // 固定寫法
          9. })
          10. )

          4.3、向 session 存數據

                  當 express-session 中間件配置成功后,即可通過 req.session 來訪問和使用 session 對象,從而存儲用戶的關鍵信息:

          
          
          1. // 登錄的 API 接口
          2. app.post('/api/login', (req, res) => {
          3. // 判斷用戶提交的登錄信息是否正確
          4. if (req.body.username !== 'admin' || req.body.password !== '000000') {
          5. return res.send({ status: 1, msg: '登錄失敗' })
          6. }
          7. // TODO_02:請將登錄成功后的用戶信息,保存到 Session 中
          8. // 注意:只有成功配置了 express-session 這個中間件之后,才能夠通過 req 點出來 session 這個屬性
          9. req.session.user = req.body // 用戶的信息
          10. console.log(req.body)
          11. req.session.islogin = true // 用戶的登錄狀態
          12. res.send({ status: 0, msg: '登錄成功' })
          13. })

          4.4、從 session 取數據

          可以直接從 req.session 對象上獲取之前存儲的數據,示例代碼如下:

          
          
          1. // 獲取用戶姓名的接口
          2. app.get('/api/username', (req, res) => {
          3. // TODO_03:請從 Session 中獲取用戶的名稱,響應給客戶端
          4. if (!req.session.islogin) {
          5. return res.send({ status: 1, msg: 'fail' })
          6. }
          7. res.send({
          8. status: 0,
          9. msg: 'success',
          10. username: req.session.user.username,
          11. })
          12. })

          4.5、清空 session

          調用 req.session.destroy() 函數,即可清空服務器保存的 session 信息。

          
          
          1. // 退出登錄的接口
          2. app.post('/api/logout', (req, res) => {
          3. // TODO_04:清空 Session 信息
          4. req.session.destroy()
          5. res.send({
          6. status: 0,
          7. msg: '退出登錄成功',
          8. })
          9. })

          4.6、完整示例

          index.html 

          
          
          1. <!DOCTYPE html>
          2. <html lang="en">
          3. <head>
          4. <meta charset="UTF-8">
          5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
          6. <title>后臺主頁</title>
          7. <script src="./jquery.js"></script>
          8. </head>
          9. <body>
          10. <h1>首頁</h1>
          11. <button id="btnLogout">退出登錄</button>
          12. <script>
          13. $(function () {
          14. // 頁面加載完成后,自動發起請求,獲取用戶姓名
          15. $.get('/api/username', function (res) {
          16. // status 為 0 表示獲取用戶名稱成功;否則表示獲取用戶名稱失敗!
          17. if (res.status !== 0) {
          18. alert('您尚未登錄,請登錄后再執行此操作!')
          19. location.href = './login.html'
          20. } else {
          21. alert('歡迎您:' + res.username)
          22. }
          23. })
          24. // 點擊按鈕退出登錄
          25. $('#btnLogout').on('click', function () {
          26. // 發起 POST 請求,退出登錄
          27. $.post('/api/logout', function (res) {
          28. if (res.status === 0) {
          29. // 如果 status 為 0,則表示退出成功,重新跳轉到登錄頁面
          30. location.href = './login.html'
          31. }
          32. })
          33. })
          34. })
          35. </script>
          36. </body>
          37. </html>

          login.html

          
          
          1. <!DOCTYPE html>
          2. <html lang="en">
          3. <head>
          4. <meta charset="UTF-8">
          5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
          6. <title>登錄頁面</title>
          7. <script src="./jquery.js"></script>
          8. </head>
          9. <body>
          10. <!-- 登錄表單 -->
          11. <form id="form1">
          12. <div>賬號:<input type="text" name="username" autocomplete="off" /></div>
          13. <div>密碼:<input type="password" name="password" /></div>
          14. <button>登錄</button>
          15. </form>
          16. <script>
          17. $(function () {
          18. // 監聽表單的提交事件
          19. $('#form1').on('submit', function (e) {
          20. // 阻止默認提交行為
          21. e.preventDefault()
          22. // 發起 POST 登錄請求
          23. $.post('/api/login', $(this).serialize(), function (res) {
          24. // status 為 0 表示登錄成功;否則表示登錄失??!
          25. if (res.status === 0) {
          26. location.href = './index.html'
          27. } else {
          28. alert('登錄失?。?)
          29. }
          30. })
          31. })
          32. })
          33. </script>
          34. </body>
          35. </html>

          app.js

          
          
          1. // 導入 express 模塊
          2. const express = require('express')
          3. // 創建 express 的服務器實例
          4. const app = express()
          5. // TODO_01:請配置 Session 中間件
          6. const session = require('express-session')
          7. app.use(
          8. session({
          9. secret: 'itheima',
          10. resave: false,
          11. saveUninitialized: true,
          12. })
          13. )
          14. // 托管靜態頁面
          15. app.use(express.static('./pages'))
          16. // 解析 POST 提交過來的表單數據
          17. app.use(express.urlencoded({ extended: false }))
          18. // 登錄的 API 接口
          19. app.post('/api/login', (req, res) => {
          20. // 判斷用戶提交的登錄信息是否正確
          21. if (req.body.username !== 'admin' || req.body.password !== '000000') {
          22. return res.send({ status: 1, msg: '登錄失敗' })
          23. }
          24. // TODO_02:請將登錄成功后的用戶信息,保存到 Session 中
          25. // 注意:只有成功配置了 express-session 這個中間件之后,才能夠通過 req 點出來 session 這個屬性
          26. req.session.user = req.body // 用戶的信息
          27. console.log(req.body)
          28. req.session.islogin = true // 用戶的登錄狀態
          29. res.send({ status: 0, msg: '登錄成功' })
          30. })
          31. // 獲取用戶姓名的接口
          32. app.get('/api/username', (req, res) => {
          33. // TODO_03:請從 Session 中獲取用戶的名稱,響應給客戶端
          34. if (!req.session.islogin) {
          35. return res.send({ status: 1, msg: 'fail' })
          36. }
          37. res.send({
          38. status: 0,
          39. msg: 'success',
          40. username: req.session.user.username,
          41. })
          42. })
          43. // 退出登錄的接口
          44. app.post('/api/logout', (req, res) => {
          45. // TODO_04:清空 Session 信息
          46. req.session.destroy()
          47. res.send({
          48. status: 0,
          49. msg: '退出登錄成功',
          50. })
          51. })
          52. // 調用 app.listen 方法,指定端口號并啟動web服務器
          53. app.listen(80, function () {
          54. console.log('Express server running at http://127.0.0.1:80')
          55. })

          5、JWT 認證機制

          5.1、了解 Session 認證的局限性

                  Session 認證機制需要配合 Cookie 才能實現。由于 Cookie 默認不支持跨域訪問,所以,當涉及到前端跨域請求后端接口的時候,需要做很多額外的配置,才能實現跨域 Session 認證。

          注意:

          • 當前端請求后端接口不存在跨域問題的時候,推薦使用 Session 身份認證機制。
          • 當前端需要跨域請求后端接口的時候,不推薦使用 Session 身份認證機制,推薦使用 JWT 認證機制。

          5.2、什么是 JWT

          JWT(英文全稱:JSON Web Token)是目前最流行跨域認證解決方案

          5.3、JWT 工作原理

          總結:用戶的信息通過 Token 字符串的形式,保存在客戶端瀏覽器中。服務器通過還原 Token 字符串的形式來認證用戶的身份。 

          5.4、JWT 組成部分

          JWT 通常由三部分組成,分別是 Header(頭部)、Payload(有效荷載)、Signature(簽名)。

          三者之間使用英文的“.”分隔,格式如下:

          下面是 JWT 字符串的示例: 

          5.5、JWT 三個部分各自代表的含義 

          JWT 的三個組成部分,從前到后分別是 HeaderPayloadSignature

          其中:

          • Payload 部分才是真正的用戶信息,它是用戶信息經過加密之后生成的字符串。
          • Header 和 Signature 安全性相關的部分,只是為了保證 Token 的安全性。

          5.6、JWT 使用方式 

          客戶端收到服務器返回的 JWT 之后,通常會將它儲存在 localStorage 或 sessionStorage 中。

          此后,客戶端每次與服務器通信,都要帶上這個 JWT 的字符串,從而進行身份認證。推薦的做法是把 JWT 放在 HTTP 請求頭的 Authorization 字段中,格式如下:

           

          6、在 Express 中使用 JWT

          6.1、安裝 JWT 相關的包

          運行如下命令,安裝如下兩個 JWT 相關的包:

          npm install jsonwebtoken express-jwt

          其中:

          • jsonwebtoken 用于生成 JWT 字符串
          • express-jwt 用于將 JWT 字符串解析還原成 JSON 對象

          6.2、導入 JWT 相關的包

          使用 require() 函數,分別導入 JWT 相關的兩個包:

          
          
          1. // 安裝并導入 JWT 相關的兩個包,分別是 jsonwebtoken 和 express-jwt
          2. const jwt = require('jsonwebtoken')
          3. const expressJWT = require('express-jwt')

          6.3、定義 secret 密鑰

                  為了保證 JWT 字符串的安全性,防止 JWT 字符串在網絡傳輸過程中被別人破解,我們需要專門定義一個用于加密解密的 secret 密鑰:

          • 當生成 JWT 字符串的時候,需要使用 secret 密鑰對用戶的信息進行加密,最終得到加密好的 JWT 字符串
          • 當把 JWT 字符串解析還原成 JSON 對象的時候,需要使用 secret 密鑰進行解密
          
          
          1. // 定義 secret 密鑰,建議將密鑰命名為 secretKey,本質上就是一個字符串
          2. const secretKey = 'itheima No1 ^_^'

          6.4、在登錄成功后生成 JWT 字符串

          調用 jsonwebtoken 包提供的 sign() 方法,將用戶的信息加密成 JWT 字符串,響應給客戶端:

          
          
          1. // 登錄接口
          2. app.post('/api/login', function (req, res) {
          3. // 將 req.body 請求體中的數據,轉存為 userinfo 常量
          4. const userinfo = req.body
          5. // 登錄失敗
          6. if (userinfo.username !== 'admin' || userinfo.password !== '000000') {
          7. return res.send({
          8. status: 400,
          9. message: '登錄失?。?,
          10. })
          11. }
          12. // 登錄成功
          13. // TODO_03:在登錄成功之后,調用 jwt.sign() 方法生成 JWT 字符串。并通過 token 屬性發送給客戶端
          14. // 參數1:用戶的信息對象
          15. // 參數2:加密的秘鑰
          16. // 參數3:配置對象,可以配置當前 token 的有效期
          17. // 記?。呵f不要把密碼加密到 token 字符中
          18. const tokenStr = jwt.sign({ username: userinfo.username }, secretKey, { expiresIn: '30s' })
          19. res.send({
          20. status: 200,
          21. message: '登錄成功!',
          22. token: tokenStr, // 要發送給客戶端的 token 字符串
          23. })
          24. })

          6.5、將 JWT 字符串還原為 JSON 對象

          • 客戶端每次在訪問那些有權限接口的時候,都需要主動通過請求頭中的 Authorization 字段,將 Token 字符串發送到服務器進行身份認證。
          • 此時,服務器可以通過 express-jwt 這個中間件,自動將客戶端發送過來的 Token 解析還原成 JSON 對象:
          
          
          1. // 使用 app.use() 來注冊中間件
          2. // expressJWT({ secret: secretKey }) 就是用來解析 Token 的中間件
          3. // .unless({ path: [/^\/api\//] }) 用來指定哪些接口不需要訪問權限
          4. app.use(expressJWT({ secret: secretKey }).unless({ path: [/^\/api\//] }))

          6.6、使用 req.user 獲取用戶信息

          當 express-jwt 這個中間件配置成功之后,即可在那些有權限的接口中,使用 req.user 對象,來訪問從 JWT 字符串中解析出來的用戶信息了,示例代碼如下: 

          
          
          1. // 這是一個有權限的 API 接口
          2. app.get('/admin/getinfo', function (req, res) {
          3. // TODO_05:使用 req.user 獲取用戶信息,并使用 data 屬性將用戶信息發送給客戶端
          4. console.log(req.user)
          5. res.send({
          6. status: 200,
          7. message: '獲取用戶信息成功!',
          8. data: req.user, // 要發送給客戶端的用戶信息
          9. })
          10. })


          藍藍設計建立了UI設計分享群,每天會分享國內外的一些優秀設計,如果有興趣的話,可以進入一起成長學習,請加藍小助,微信號:ben_lanlan,報下信息,藍小助會請您入群。歡迎您加入噢~~希望得到建議咨詢、商務合作,也請與我們聯系01063334945。


          分享此文一切功德,皆悉回向給文章原作者及眾讀者.
          免責聲明:藍藍設計尊重原作者,文章的版權歸原作者。如涉及版權問題,請及時與我們取得聯系,我們立即更正或刪除。


          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 平面設計服務UI設計公司、界面設計公司、UI設計服務公司、數據可視化設計公司、UI交互設計公司、高端網站設計公司、UI咨詢、用戶體驗公司、軟件界面設計公司


          jsoup的使用

          前端達人

          一、jsoup概述

          jsoup 是一款基于 Java 的HTML解析器,它提供了一套非常省力的API,不但能直接解析某個URL地址、HTML文本內容,而且還能通過類似于DOM、CSS或者jQuery的方法來操作數據,所以 jsoup 也可以被當做爬蟲工具使用。


          二、相關概念簡介

          • Document :文檔對象。每份HTML頁面都是一個文檔對象,Document 是 jsoup 體系中最頂層的結構。
          • Element:元素對象。一個 Document 中可以著包含著多個 Element 對象,可以使用 Element 對象來遍歷節點提取數據或者直接操作HTML。
          • Elements:元素對象集合,類似于List<Element>。
          • Node:節點對象。標簽名稱、屬性等都是節點對象,節點對象用來存儲數據。
          • 類繼承關系:Document 繼承自 Element ,Element 繼承自 Node。
          • 一般執行流程:先獲取 Document 對象,然后獲取 Element 對象,最后再通過 Node 對象獲取數據。
            在這里插入圖片描述

          三、獲取文檔(Document)

          獲得文檔對象 Document 一共有4種方法,分別對應不同的獲取方式。

          正式開始之前,我們需要導入有關 jar 包。

          <dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>1.15.1</version> </dependency> 
          
          • 1
          • 2
          • 3
          • 4
          • 5

          3.1)從URL中加載文檔對象(常用)

          使用 Jsoup.connect(String url).get()方法獲取(只支持 http 和 https 協議):

          Document doc = Jsoup.connect("http://csdn.com/").get(); String title = doc.title(); System.out.println(title); 
          
          • 1
          • 2
          • 3
          • 4

          connect(String url)方法創建一個新的 Connection并通過.get()或者.post()方法獲得數據。如果從該URL獲取HTML時發生錯誤,便會拋出 IOException,應適當處理。

          Connection 接口還提供一個方法鏈來解決特殊請求,我們可以在發送請求時帶上請求的頭部參數,具體如下:

          Document doc = Jsoup.connect("http://csdn.com") .data("query", "Java") .userAgent("Mozilla") .cookie("auth", "token") .timeout(8000) .post(); System.out.println(doc); 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7

          想獲得完整的響應對象和響應碼?我們可以使用execute()方法:

          // 獲得響應對象 Connection.Response response = Jsoup.connect("http://csdn.com").execute(); int code = response.statusCode(); // 輸出狀態碼:200 System.out.println(code); 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6

          3.2)從本地文件中加載文檔對象

          可以使用靜態的Jsoup.parse(File in, String charsetName) 方法從文件中加載文檔。其中in表示路徑,charsetName表示編碼方式,示例代碼:

          File input = new File("/tmp/input.html"); Document doc = Jsoup.parse(input, "UTF-8"); System.out.println(doc); 
          
          • 1
          • 2
          • 3

          3.3)從字符串文本中加載文檔對象

          使用靜態的Jsoup.parse(String html) 方法可以從字符串文本中獲得文檔對象 Document ,示例代碼:

          String html = "<html><head><title>First parse</title></head>" + "<body><p>Parsed HTML into a doc.</p></body></html>"; Document doc = Jsoup.parse(html); System.out.println(doc); 
          
          • 1
          • 2
          • 3
          • 4
          • 5

          3.4)從<body>片斷中獲取文檔對象

          使用Jsoup.parseBodyFragment(String html)方法.

          String html = "<p>Lorem ipsum.</p>"; Document doc = Jsoup.parseBodyFragment(html); // doc 此時為:<body> <p>Lorem ipsum.</p></body> Element body = doc.body(); System.out.println(body); 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6

          parseBodyFragment 方法創建一個新的文檔,并插入解析過的HTML到body元素中。假如你使用正常的 Jsoup.parse(String html) 方法,通常也能得到相同的結果,但是明確將用戶輸入作為 body 片段處理是個更好的方式。

          Document.body() 方法能夠取得文檔body元素的所有子元素,與 doc.getElementsByTag("body")相同。


          四、選擇元素(Element)

          解析文檔對象并獲取數據一共有 2 種方式,分別為 DOM方式、CSS選擇器方式,我們可以選擇一種自己喜歡的方式去獲取數據,效果一樣。

          4.1)DOM方式

          將HTML解析成一個Document之后,就可以使用類似于DOM的方法進行操作。

          // 獲取csdn首頁所有的鏈接 Document doc = Jsoup.connect("http://csdn.com").get(); Elements elements = doc.getElementsByTag("body"); Elements contents = elements.first().getElementsByTag("a"); for (Element content : contents) { String linkHref = content.attr("href"); String linkText = content.text(); System.out.print(linkText+"\t"); System.out.println(linkHref); } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12

          說明

          Elements這個對象提供了一系列類似于DOM的方法來查找元素,抽取并處理其中的數據。具體如下:

          4.1.1)查找元素

          • getElementById(String id):通過id來查找元素
          • getElementsByTag(String tag):通過標簽來查找元素
          • getElementsByClass(String className):通過類選擇器來查找元素
          • getElementsByAttribute(String key) :通過屬性名稱來查找元素,例如查找帶有href元素的標簽。
          • siblingElements():獲取兄弟元素。如果元素沒有兄弟元素,則返回一個空列表。
          • firstElementSibling():獲取第一個兄弟元素。
          • lastElementSibling():獲取最后一個兄弟元素。
          • nextElementSibling():獲取下一個兄弟元素。
          • previousElementSibling():獲取上一個兄弟元素。
          • parent():獲取此節點的父節點。
          • children():獲取此節點的所有子節點。
          • child(int index):獲取此節點的指定子節點。

          4.1.2)獲取元素數據

          • attr(String key):獲取單個屬性值
          • attributes():獲取所有屬性值
          • attr(String key, String value):設置屬性值
          • text():獲取文本內容
          • text(String value):設置文本內容
          • html():獲取元素內的HTML內容
          • html(String value):設置元素內的HTML內容
          • outerHtml():獲取元素外HTML內容
          • data():獲取數據內容(例如:script和style標簽)
          • id():獲得id值(例:<p id="goods">衣服</p>
          • className():獲得第一個類選擇器值
          • classNames():獲得所有的類選擇器值
          • tag():獲取元素標簽
          • tagName():獲取元素標簽名(如:<p>、<div>等)

          4.1.3)操作HTML文本

          • append(String html):在末尾追加HTML文本
          • prepend(String html):在開頭追加HTML文本
          • html(String value):在匹配元素內部添加HTML文本。

          4.2)CSS選擇器方式

          可以使用類似于CSS選擇器的語法來查找和操作元素,常用的方法為select(String selector)。

          Document doc = Jsoup.connect("http://csdn.com").get(); // 獲取帶有 href 屬性的 a 元素 Elements elements = doc.select("a[href]"); for (Element content : elements) { String linkHref = content.attr("href"); String linkText = content.text(); System.out.print(linkText + "\t"); System.out.println(linkHref); } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11

          4.2.1)說明

          select()方法在Document、ElementElements對象中都可以使用,而且是上下文相關的,因此可實現指定元素的過濾,或者采用鏈式訪問。

          select() 方法將返回一個Elements集合,并提供一組方法來抽取和處理結果。

          4.2.2)select(String selector)方法參數簡介

          • tagname: 通過標簽查找元素,例如通過"a"來查找<a>標簽。
          • #id: 通過ID查找元素,比如通過#logo查找<p id="logo">。
          • .class: 通過class名稱查找元素,比如通過.titile查找<p class="titile">。
          • ns|tag: 通過標簽在命名空間查找元素,比如使用 fb|name 來查找 <fb:name> 。
          • [attribute]: 利用屬性查找元素,比如通過[href]查找<a href="...">。
          • [^attribute]: 利用屬性名前綴來查找元素,比如:可以用[^data-] 來查找帶有HTML5 dataset屬性的元素。
          • [attribute=value]: 利用屬性值來查找元素,比如:[width=500]
          • [attribute^=value][attribute$=value][attribute*=value]: 利用匹配屬性值開頭、結尾或包含屬性值來查找元素,比如通過[href*=/path/]來查找<a href="a/path/c.html">。
          • [attribute~=regex]: 利用屬性值匹配正則表達式來查找元素,比如通過 img[src~=(?i)\.(png|jpe?g)]來匹配所有的png或者jpg、jpeg格式的圖片。
          • *: 通配符,匹配所有元素。

          4.2.3)參數屬性組合使用

          • el#id: 元素+ID,比如: div#logo
          • el.class: 元素+class,比如: div.masthead
          • el[attr]: 元素+class,比如 a[href]匹配所有帶有 href 屬性的 a 元素。
          • 任意組合,比如:a[href].highlight匹配所有帶有 href 屬性且class="highlight"的 a 元素。
          • ancestor child: 查找某個元素下子元素,比如:可以用.body p 查找在"body"元素下的所有 p元素
          • parent > child: 查找某個父元素下的直接子元素,比如:可以用div.content > p 查找 p 元素,也可以用body > * 查找body標簽下所有直接子元素
          • siblingA + siblingB: 查找在A元素之前第一個同級元素B,比如:div.head + div
          • siblingA ~ siblingX: 查找A元素之前的同級X元素,比如:h1 ~ p
          • el, el, el:多個選擇器組合,查找匹配任一選擇器的唯一元素,例如:div.masthead, div.logo

          4.2.4)特殊參數:偽選擇器

          • :lt(n): 查找哪些元素的同級索引值(它的位置在DOM樹中是相對于它的父節點)小于n,比如:td:lt(3) 表示小于三列的元素
          • :gt(n):查找哪些元素的同級索引值大于n``,比如: div p:gt(2)表示哪些div中有包含2個以上的p元素
          • :eq(n): 查找哪些元素的同級索引值與n相等,比如:form input:eq(1)表示包含一個input標簽的Form元素
          • :has(seletor): 查找匹配選擇器包含元素的元素,比如:div:has(p)表示哪些div包含了p元素
          • :not(selector): 查找與選擇器不匹配的元素,比如: div:not(.logo) 表示不包含 class=logo 元素的所有 div 列表
          • :contains(text): 查找包含給定文本的元素,搜索不區分大不寫,比如: p:contains(jsoup)
          • :containsOwn(text): 查找直接包含給定文本的元素
          • :matches(regex): 查找哪些元素的文本匹配指定的正則表達式,比如:div:matches((?i)login)
          • :matchesOwn(regex): 查找自身包含文本匹配指定正則表達式的元素
          • 注意:上述偽選擇器索引是從0開始的,也就是說第一個元素索引值為0,第二個元素index為1等

          五、獲取數據(Node)

          在獲得文檔對象并且指定查找元素后,我們就可以獲取元素中的數據。
          這些訪問器方法都有相應的setter方法來更改數據。

          • .attr(String key) :獲得屬性的值。
          • .text():獲得元素中的文本。
          • .html():獲得元素或屬性內部的HTML內容(不包括本身)。
          • .outerHtml():獲得元素或屬性完整的HTML內容。
          • .id():獲得元素id屬性值。
          • className():獲得元素類選擇器值。
          • .tagName():獲得元素標簽命名。
          • .hasClass(String className):檢查這個元素是否含有一個類選擇器(不區分大小寫)。
          String html = "<p><a ><b>example</b></a> link.</p>"; Document doc = Jsoup.parse(html); // 查找第一個<a>元素 Element link = doc.select("a").first(); // 輸出:example String text = link.text(); // 輸出:http://csdn.com/ String href = link.attr("href"); // 輸出:<b>example</b> String aHtml = link.outerHtml(); // 輸出:<a ><b>example</b></a> String aOuterHtml = link.outerHtml(); 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14


          六、修改數據

          在解析了一個Document對象之后,你可能想修改其中的某些屬性值,并把它輸出到前臺頁面或保存到其他地方,jsoup對此提供了一套非常簡便的接口(支持鏈式寫法)。

          6.1)設置屬性的值

          當以下方法針對Element對象操作時,只有一個元素會受到影響。當針對Elements對象進行操作時,可能會影響到多個元素。

          • .attr(String key, String value):設置標簽的屬性值。
          • .addClass(String className):增加類選擇器選項
          • .removeClass(String className):刪除對應的類選擇器
          Document doc = Jsoup.connect("http://csdn.com").get(); // 復數,Elements Elements elements = doc.getElementsByClass("text"); // 單數,Element Element element = elements.first(); // 復數對象,所有 class="text" 的元素都將受到影響 elements.attr("name","goods"); // 單數對象,只有一個元素會受到影響(鏈式寫法) element.attr("name","shop") .addClass("red"); 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12

          6.2)修改元素的HTML內容

          可以使用Element中的HTML設置方法具體如下:

          • .html(String value):這個方法將先清除元素中的HTML內容,然后用傳入的HTML代替。
          • .prepend(String value):在元素添加html內容。
          • .append(String value):在元素添加html內容。
          • .wrap(String value):對元素包裹一個外部HTML內容,將元素置于新增的內容中間。
          Document doc = Jsoup.connect("http://csdn.com").get(); Element div = doc.select("div").first(); div.html("<p>csdn</p>"); div.prepend("<p>a</p>"); div.append("<p>good</p>"); // 輸出:<div"> <p>a</p> <p>csdn</p> <p>good</p> </div> Element span = doc.select("span").first(); span.wrap("<li><a href='...'></a></li>"); // 輸出: <li><a href="..."> <span>csdn</span> </a></li> 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11

          6.3)修改元素的文本內容

          對于傳入的文本,如果含有像 <> 等這樣的字符,將以文本處理,而非HTML。

          • .text(String text) :清除元素內部的HTML內容,然后用提供的文本代替。
          • .prepend(String first):在元素添加文本節點。
          • Element.append(String last):在元素添加文本節點。
           // <div></div> Element div = doc.select("div").first(); div.text(" one "); div.prepend(" two "); div.append(" three "); // 輸出: <div> two one three </div> 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7

          七、其他功能

          6.1)相對路徑轉絕對路徑

          問題描述
          你有一個包含相對URLs路徑的HTML文檔,現在需要將這些相對路徑轉換成絕對路徑的URLs。

          解決方式

          1. 確保在你解析文檔時有指定base URI路徑。
          2. 然后使用 abs: 屬性前綴來取得包含base URI的絕對路徑。代碼如下:
          Document doc = Jsoup.connect("http://www.open-open.com").get(); Element link = doc.select("a").first(); // 輸出:/ String relHref = link.attr("href"); // 輸出:http://www.open-open.com/ String absHref = link.attr("abs:href"); 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8

          說明

          在HTML元素中,URLs經常寫成相對于文檔位置的相對路徑,如:<a href="/download">...</a>。當你使用 .attr(String key) 方法來取得a元素的href屬性時,它將直接返回在HTML源碼中指定的值。

          假如你需要取得一個絕對路徑,需要在屬性名前加 abs: 前綴,這樣就可以返回包含根路徑的URL地址attr("abs:href")。因此在解析HTML文檔時,定義base URI非常重要。

          如果你不想使用abs: 前綴,還有一個方法能夠實現同樣的功能 .absUrl(String key)

          6.2)消除不受信任的HTML (防止XSS攻擊)

          問題描述

          在某些網站中經常會提供用戶評論的功能,但是有些不懷好意的用戶,會搞一些腳本到評論內容中,而這些腳本可能會破壞整個頁面的行為,更嚴重的是獲取一些機要信息,此時需要清理該HTML,以避免跨站腳本攻擊(XSS)。

          解決方式
          使用clean()方法清除惡意代碼,但需要指定一個配置的 Safelist(舊版本中是Whitelist),通常使用Safelist.basic()即可。Safelist的工作原理是將輸入的 HTML 內容單獨隔離解析,然后遍歷解析樹,只允許已知的安全標簽和屬性輸出。

          String unsafe = "<p><a  οnclick='attack()'>Link</a></p>"; // 輸出: <p><a  >Link</a></p> String safe = Jsoup.clean(unsafe, Safelist.basic()); System.out.println(safe); 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6

          說明

          jsoup的Safelist不僅能夠在服務器端對用戶輸入的HTML進行過濾,只輸出一些安全的標簽和屬性,還可以限制用戶可以輸入的標簽范圍。

          6.2)消除不受信任的HTML (防止XSS攻擊)

          問題描述

          在某些網站中經常會提供用戶評論的功能,但是有些不懷好意的用戶,會搞一些腳本到評論內容中,而這些腳本可能會破壞整個頁面的行為,更嚴重的是獲取一些機要信息,此時需要清理該HTML,以避免跨站腳本攻擊(XSS)。

          解決方式
          使用clean()方法清除惡意代碼,但需要指定一個配置的 Safelist(舊版本中是Whitelist),通常使用Safelist.basic()即可。Safelist的工作原理是將輸入的 HTML 內容單獨隔離解析,然后遍歷解析樹,只允許已知的安全標簽和屬性輸出。

          String unsafe = "<p><a  οnclick='attack()'>Link</a></p>"; // 輸出: <p><a  >Link</a></p> String safe = Jsoup.clean(unsafe, Safelist.basic()); System.out.println(safe); 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6

          說明

          jsoup的Safelist不僅能夠在服務器端對用戶輸入的HTML進行過濾,只輸出一些安全的標簽和屬性,還可以限制用戶可以輸入的標簽范圍。

          6.3)jsoup使用代理

          Connection.Response execute = Jsoup.connect("http://csdn.net/") .proxy("12.12.12.12", 1080) // 使用代理 .execute(); 
          
          • 1
          • 2
          • 3

          如果你讀完覺得有收獲,不妨點個贊~




          藍藍設計建立了UI設計分享群,每天會分享國內外的一些優秀設計,如果有興趣的話,可以進入一起成長學習,請加藍小助,微信號:ben_lanlan,報下信息,藍小助會請您入群。歡迎您加入噢~~希望得到建議咨詢、商務合作,也請與我們聯系01063334945。


          分享此文一切功德,皆悉回向給文章原作者及眾讀者.
          免責聲明:藍藍設計尊重原作者,文章的版權歸原作者。如涉及版權問題,請及時與我們取得聯系,我們立即更正或刪除。


          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務、UI設計公司、界面設計公司、UI設計服務公司、數據可視化設計公司、UI交互設計公司、高端網站設計公司、UI咨詢、用戶體驗公司、軟件界面設計公司

          前端大屏常用的幾種適配方案

          前端達人

          方 案 實現方式 優點 缺點
          vm vh 1.按照設計稿的尺寸,將px按比例計算轉為vw和vh 1.可以動態計算圖表的寬高,字體等,靈活性較高 2.當屏幕比例跟 ui 稿不一致時,不會出現兩邊留白情況 1.每個圖表都需要單獨做字體、間距、位移的適配,比較麻煩
          scale 1.通過 scale 屬性,根據屏幕大小,對圖表進行整體的等比縮放 1.代碼量少,適配簡單2.一次處理后不需要在各個圖表中再去單獨適配 3.文字,圖片等大小均能自動適配 1.因為是根據 ui 稿等比縮放,當大屏跟 ui 稿的比例不一樣時,會出現周邊留白情況2.當縮放比例過大時候,字體會有一點點模糊,就一點點3.當縮放比例過大時候,事件熱區會偏移。
          插件v-scale-screen 是使用 css 屬性 transform 實現縮放效果的一個大屏自適應組件,通過 scale 進行等比例計算,達到等比例縮放的效果 可以通過api調整原稿的寬高

          方案一:vw vh

          1.當屏幕正好為16:9的時候
          2.當屏幕的尺寸比例大于 16:9 (左右拉長)
          在這里插入圖片描述
          3.當屏幕的尺寸比例小于 16:9 時(左右變窄或者上下拉高)

          在這里插入圖片描述
          實現方法:
          css 方案 - sass
          utils.scss

          // 使用 scss 的 math 函數,https://sass-lang.com/documentation/breaking-changes/slash-div @use "sass:math"; // 默認設計稿的寬度 $designWidth: 1920; // 默認設計稿的高度 $designHeight: 1080; // px 轉為 vw 的函數 @function vw($px) { @return math.div($px, $designWidth) * 100vw; } // px 轉為 vh 的函數 @function vh($px) { @return math.div($px, $designHeight) * 100vh; } 復制代碼 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18

          路徑配置只需在vue.config.js里配置一下utils.scss的路徑,就可以全局使用了
          vue.config.js

          module.exports = { css: { loaderOptions: { sass: { prependData: `@import "@/assets/css/utils.scss";` } } }, }.vue 中使用 <template> <div class="box"> </div> </template> <script> export default{ name: "Box", } </script> <style lang="scss" scoped="scoped"> /* 
           直接使用 vw 和 vh 函數,將像素值傳進去,得到的就是具體的 vw vh 單位   
           */ .box{ width: vw(300); height: vh(100); font-size: vh(16); background-color: black; margin-left: vw(10); margin-top: vh(10); border: vh(2) solid red; } </style> 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
          • 27
          • 28
          • 29
          • 30
          • 31
          • 32
          • 33
          • 34
          • 35

          css 方案 - less

          utils.less
          @charset "utf-8"; // 默認設計稿的寬度 @designWidth: 1920; // 默認設計稿的高度 @designHeight: 1080; .px2vw(@name, @px) { @{name}: (@px / @designWidth) * 100vw; } .px2vh(@name, @px) { @{name}: (@px / @designHeight) * 100vh; } .px2font(@px) { font-size: (@px / @designWidth) * 100vw; } 路徑配置在vue.config.js里配置一下utils.less <style lang="less" scoped="scoped"> /* 
           直接使用 vw 和 vh 函數,將像素值傳進去,得到的就是具體的 vw vh單位   
           */ .box{ .px2vw(width, 300); .px2vh(height, 100); .px2font(16); .px2vw(margin-left, 300); .px2vh(margin-top, 100); background-color: black; } </style> 定義 js 樣式處理函數 // 定義設計稿的寬高 const designWidth = 1920; const designHeight = 1080; // px轉vw export const px2vw = (_px) => { return (_px * 100.0) / designWidth + 'vw'; }; export const px2vh = (_px) => { return (_px * 100.0) / designHeight + 'vh'; }; export const px2font = (_px) => { return (_px * 100.0) / designWidth + 'vw'; }; 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
          • 27
          • 28
          • 29
          • 30
          • 31
          • 32
          • 33
          • 34
          • 35
          • 36
          • 37
          • 38
          • 39
          • 40
          • 41
          • 42
          • 43
          • 44
          • 45
          • 46
          • 47
          • 48
          • 49
          • 50
          • 51

          屏幕變化后,圖表自動調整
          這種使用方式有個弊端,就是屏幕尺寸發生變化后,需要手動刷新一下才能完成自適應調整

          為了解決這個問題,你需要在各個圖表中監聽頁面尺寸變化,重新調整圖表,在 vue 項目中,也可以借助element-resize-detector,最好封裝個 resize 的指令,在各圖表中就只要使用該指令就可以了,畢竟作為程序員,能偷懶就偷懶
          解決方案一

          1. 安裝 element-resize-detector
          npm install element-resize-detector --save 
          
          • 1
          1. 封裝成自定義指令使用
          // directive.js import * as ECharts from "echarts"; import elementResizeDetectorMaker from "element-resize-detector"; import Vue from "vue"; const HANDLER = "_vue_resize_handler"; function bind(el, binding) { el[HANDLER] = binding.value ? binding.value : () => { let chart = ECharts.getInstanceByDom(el); if (!chart) { return; } chart.resize(); }; // 監聽綁定的div大小變化,更新 echarts 大小 elementResizeDetectorMaker().listenTo(el, el[HANDLER]); } function unbind(el) { // window.removeEventListener("resize", el[HANDLER]); elementResizeDetectorMaker().removeListener(el, el[HANDLER]); delete el[HANDLER]; } // 自定義指令:v-chart-resize 示例:v-chart-resize="fn" Vue.directive("chart-resize", { bind, unbind }); 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          1. main.js 中引入
           import '@/directive/directive'; 
          
          • 1
          • 2
          1. html 代碼中使用
          <template> <div class="linechart"> <div ref="chart" v-chart-resize class="chart"></div> </div> </template> 
          
          • 1
          • 2
          • 3
          • 4
          • 5

          這里要注意的是,圖表中如果需要 tab 切換動態更新圖表數據,在更新數據時一定不要用 echarts 的 dispose 方法先將圖表移除,再重新繪制,因為 resize 指令中掛載到的圖表實例還是舊的,就監聽不到新的 chart 元素的 resize 了,更新數據只需要用 chart 的 setOption 方法重新設置配置項即可。
          解決方案二
          1.在echarts中可以echarts.init(chatDom).resize()來解決寬高的自適應問題

           let chatDom = document.getElementById('main'); let myChart = this.$echarts.init(chatDom); //根據父盒子的尺寸調整echarts的大小 setTimeout(() => { window.addEventListener('resize', () => { this.$echarts.init(chatDom).resize(); }); }, 20); 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9

          2.在DataV中可以添加key來解決

          <dv-water-level-pond :config="config2" :key="key" ref="pie2" /> data(){ return { key: 1 } }, mounted() { this.pieOutlineFunc(); }, methods: { pieOutlineFunc() { var _this = this; window.addEventListener('resize', function (e) { _this.$nextTick(() => { console.log(_this.$refs.pie2); _this.key++; }); }); } } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20

          方案二:csale

          通過 css 的 scale 屬性,根據屏幕大小,用js監測屏幕的大小變化對圖表進行整體的等比縮放,從而達到自適應效果
          當屏幕的尺寸比例剛好是 16:9 時,頁面能剛好全屏展示,內容占滿顯示器
          在這里插入圖片描述
          當屏幕尺寸比例大于 16:9 時,上下左右留白,左右占滿并居中,顯示比例保持 16:9

          在這里插入圖片描述
          當屏幕尺寸比例大于 16:9 時,頁面左右留白,上下占滿并居中,顯示比例保持 16:9
          在這里插入圖片描述
          上代碼
          html

          <template> <div class="screen-root"> <div class="screen" id="screen"> <div class="div1"> <h1>11111111111</h1> </div> <div class="div2"> <h1>2222222222</h1> </div> <div class="div3"> <h1>3333333333</h1> </div> </div> </div> </template> 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15

          js

          <script> export default { mounted() { // 初始化自適應  ----在剛顯示的時候就開始適配一次 this.handleScreenAuto(); // 綁定自適應函數   ---防止瀏覽器欄變化后不再適配 window.onresize = () => this.handleScreenAuto(); }, deleted() { window.onresize = null; }, methods: { // 數據大屏自適應函數 handleScreenAuto() { const designDraftWidth = 1920; //設計稿的寬度 const designDraftHeight = 1080; //設計稿的高度 // 根據屏幕的變化適配的比例 const scale = document.documentElement.clientWidth / document.documentElement.clientHeight < designDraftWidth / designDraftHeight ? document.documentElement.clientWidth / designDraftWidth : document.documentElement.clientHeight / designDraftHeight; // 縮放比例 document.querySelector('#screen').style.transform = `scale(${scale}) translate(-50%,-50%)`; return 1; } } }; </script> 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
          • 27
          • 28
          • 29
          • 30

          css

          <style lang="scss" scoped> /*
            除了設計稿的寬高是根據您自己的設計稿決定以外,其他復制粘貼就完事
          */ h1 { color: red; font-size: 50px; } .screen-root { height: 100vh; width: 100vw; .screen { display: inline-block; width: 1920px; //設計稿的寬度 height: 1080px; //設計稿的高度 transform-origin: 0 0; position: absolute; left: 50%; top: 50%; border: 2px solid rgb(31, 210, 145); box-sizing: border-box; display: flex; .div1 { background-color: #fff; height: 100%; text-align: center; flex: 0 1 30%; } .div2 { background-color: rgb(133, 14, 14); height: 100%; text-align: center; flex: 0 1 40%; } .div3 { background-color: rgb(61, 6, 188); height: 100%; text-align: center; flex: 0 1 30%; } } } </style> 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
          • 27
          • 28
          • 29
          • 30
          • 31
          • 32
          • 33
          • 34
          • 35
          • 36
          • 37
          • 38
          • 39
          • 40
          • 41
          • 42
          • 43

          方案三:插件v-scale-screen

          它其實也是通過 scale 進行等比例計算放大和縮小的,和方案二的原理是一樣的,還可以通過api調整樣式,源碼地址和對應的API
          在這里插入圖片描述
          使用方法:
          1.vue2請使用v-scale-screen@1.0.0版本,vue3請使用v-scale-screen@2.0.0版本

          npm install v-scale-screen@1.0.0 -save
          # or
          yarn add v-scale-screen 
          
          • 1
          • 2
          • 3

          2.使用-vue2中使用插件導入,vue3以組件導入

          vue2 // main.js import VScaleScreen from 'v-scale-screen' Vue.use(VScaleScreen) 組件內使用 //html <v-scale-screen width="1920" height="1080" :boxStyle="boxStyle"> <div> <v-chart>....</v-chart> <v-chart>....</v-chart> <v-chart>....</v-chart> <v-chart>....</v-chart> <v-chart>....</v-chart> </div> </v-scale-screen> //js data() { return { boxStyle: { backgroundColor: 'green' }, } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          vue3 <v-scale-screen width="1920" height="1080"> <div> <v-chart>....</v-chart> <v-chart>....</v-chart> <v-chart>....</v-chart> <v-chart>....</v-chart> <v-chart>....</v-chart> </div> </v-scale-screen> <script> import VScaleScreen from 'v-scale-screen' export default { components:{ VScaleScreen } } </script> 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          1. vue2演示地址



            著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

            藍藍設計建立了UI設計分享群,每天會分享國內外的一些優秀設計,如果有興趣的話,可以進入一起成長學習,請加藍小助,微信號:ben_lanlan,報下信息,藍小助會請您入群。歡迎您加入噢~~希望得到建議咨詢、商務合作,也請與我們聯系01063334945。


            分享此文一切功德,皆悉回向給文章原作者及眾讀者.
            免責聲明:藍藍設計尊重原作者,文章的版權歸原作者。如涉及版權問題,請及時與我們取得聯系,我們立即更正或刪除。


            藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 平面設計服務、UI設計公司、界面設計公司、UI設計服務公司、數據可視化設計公司、UI交互設計公司、高端網站設計公司、UI咨詢、用戶體驗公司、軟件界面設計公司

          Laravel + Vue 3(Vite、TypeScript)SPA 設置

          前端達人

          在本教程中,我將向大家展示如何使用 Laravel + Vue 3 使用 typescript 和 Vite 設置你自己的單頁應用程序。

          這是在 Laravel 項目中添加 PWA 的手動方法。我們不會使用 InertiaJS 或其他類似的東西,我們也不會混合使用。我們將手動實現我們自己的 VueJS 前端。

          第 1 步:讓我們創建我們的 Laravel 項目

          composer create-project laravel/laravel laravel-vue-manual 
          
          • 1

          第 2 步:設置前端

          在我們的 laravel 項目中,讓我們使用 yarn 運行一個命令,并選擇 vue 和 typescript。

          yarn create vite 
          
          • 1

          將項目名稱設置為:FrontEndApp
          選擇:Vue
          選擇:TypeScript

          然后轉到我們的FrontEndApp目錄并運行yarn或yarn install安裝依賴項。

          配置 Vite

          讓我們配置我們的 vite 配置FrontEndApp\vite.config.ts

          import { defineConfig } from "vite"; import vue from "@vitejs/plugin-vue"; export default ({ mode }) => { // 檢查是否開發 const isDevelopment = mode === "development"; return defineConfig({ server: { port: 3000, }, build: { // 生成的文件將添加到此處 outDir: "./../public/app", }, // 也將更改基于模式的基礎 base: isDevelopment ? "/" : "/app/", plugins: [vue()], }); }; 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20

          然后讓我們更改build腳本FrontEndApp\package.json,這樣每次我們構建它時都會替換以下文件public/app:

          { ... "scripts": { "dev": "vite", "build": "vue-tsc --noEmit && vite build --emptyOutDir", "preview": "vite preview" }, ... } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9

          現在,如果我們在FrontEndApp中運行yarn build,它應該在laravel項目的根目錄中的public文件夾中創建一個名為 app 的文件夾。

          在這里插入圖片描述

          第 3 步:設置 Laravel 路由

          讓我們設置我們的 laravel 路由,以便我們可以訪問我們剛剛創建的文件。

          讓我們編輯這個文件 routes\web.php

          <?php
          
          use Illuminate\Support\Facades\Route; Route::get('/', function () { return view('welcome'); }); Route::get('/app/{any}', function () { $path = public_path('app/index.html'); abort_unless(file_exists($path), 400, 'Page is not Found!'); return file_get_contents($path); }) ->name('FrontEndApp'); 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15

          現在,如果我們http://127.0.0.1:8000/app在瀏覽器中打開,我們現在可以看到我們的應用程序已啟動。

          第 4 步:設置腳本

          我們將在我們的根項目目錄中添加一個開發包,并同時調用它。我們用它來一次運行 2 個或更多命令。

          安裝:

          yarn add -D concurrently 
          
          • 1

          如果我們想要自動工作,不想每次使用時都重新構建frontednapp,我們要做的是在package.json項目的根目錄中添加一個新腳本。

          { ... "scripts": { ... "front:serve": "cd FrontEndApp && yarn dev", "front:build": "cd FrontEndApp && yarn build", "serve": "concurrently \"php artisan serve --port=8080\" \"yarn front:serve\"", "deploy": "yarn setup && yarn front:build && php artisan migrate" }, ... } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11

          這樣, running yarn serve將同時運行 127.0.0.1:8080 和 localhost:3000。你現在可以同時使用這兩個項目。

          完成 FrontEndApp 的工作后,你可以運行 yarn deploy 以構建我們的前端。

          結論

          我相信這也是大家可以在 laravel 項目中添加 pwa 的一種方式,這樣你就可以將它們保存在一個項目中。

          考慮到這一點,你可以添加routes到你的 FrontEndApp 項目中,還可以添加狀態管理器PiniaJA,例如 等等。




          文章來源:csdn

          著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

          藍藍設計建立了UI設計分享群,每天會分享國內外的一些優秀設計,如果有興趣的話,可以進入一起成長學習,請加藍小助,微信號:ben_lanlan,報下信息,藍小助會請您入群。歡迎您加入噢~~希望得到建議咨詢、商務合作,也請與我們聯系01063334945。


          分享此文一切功德,皆悉回向給文章原作者及眾讀者.
          免責聲明:藍藍設計尊重原作者,文章的版權歸原作者。如涉及版權問題,請及時與我們取得聯系,我們立即更正或刪除。


          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 平面設計服務、UI設計公司、界面設計公司、UI設計服務公司、數據可視化設計公司、UI交互設計公司、高端網站設計公司、UI咨詢、用戶體驗公司、軟件界面設計公司

          日歷

          鏈接

          個人資料

          藍藍設計的小編 http://www.syprn.cn

          存檔

          亚洲va欧美va天堂v国产综合