學習筆記-DAY01-VUE

凱撒·陌森發表於2020-09-23
學習筆記-DAY01-VUE


前言

第一次嘗試在網路上寫筆記,不夠成熟,暫時只為了以後能及時翻查而寫


一、Vue中的鉤子函式&監聽&方法的呼叫時機?

  • 鉤子(create):頁面載入時發請求(重新整理)
  • 監聽(watch):是被監聽的欄位又發生改變的時候傳送請求
  • 方法(method): 不發請求,而是等待被呼叫

二、點選上一頁、下一頁後重新整理,頁面依舊可以跳回當前頁,而非首頁

大致思路:在監聽函式watch中記錄當前的頁數:page,把page放到位址列,保證位址列的值與searchParam(data中的引數,儲存位址列"?"後面的資料)是一致的:

searchParams: {
         key: "",
         page: 1,
         filterParams: {}
},

鉤子函式

created(){
    //獲取位址列的key的值
    const key = ly.getUrlParam("key")
    //判斷是否有搜尋條件
    if(!key){
        alert("請求提供搜尋條件!")
        return
    }
    //把key值賦值給searchParams中的key
    //this.searchParams.key = key

    //獲取位址列location的search
    const searchStr = location.search.substring(1)
    //將searchStr轉成json物件
    const searchObj = ly.parse(searchStr)
    //判斷searchObj中是否有page屬性
    searchObj.page = searchObj.page || 1
    //判斷searchObj中是否有filterParams屬性
    searchObj.filterParams = searchObj.filterParams || {}
    //把searchObj物件值覆蓋給searchParams
    this.searchParams = searchObj

    //向伺服器發起請求
    this.loadSearchPage()
},

監聽器

watch: {
	"searchParams.page": {
	    handler() {
	        //把page值放到位址列,保證位址列的值與searchParams是一致的
	        //將searchParams轉成字串
	        const searchStr = ly.stringify(this.searchParams)
	        //替換location的search,注意:只要location的search發生了變化,頁面就會自動重新整理
	        //location.search = searchStr
	
	        //指定新的位址列url
	        const newUrl = location.origin+location.pathname+"?"+searchStr
	        //使用history修改位址列,可以不重新整理頁面
	        window.history.replaceState(null, null, newUrl)
	
	        //向伺服器發起請求
	        this.pageChange()
	    }
	},

上面的程式碼中特別注意一下的是:

  1. window.history 代替了 location.search,因為使用後者頁面會自動重新整理一次,使用者體驗不好
  2. const newUrl = location.origin+location.pathname+"?"+searchStr在這裡插入圖片描述

三、點選篩選條件後,被點選的引數能夠新增到位址列中

在method中新增一個方法:

clickFilterParams(key, value){
    //先把filterParams物件賦值給另外一個地址的物件
    const {...newFilterParams} = this.searchParams.filterParams
    newFilterParams[key] = value
    this.searchParams.filterParams = newFilterParams

    //將searchParams轉成字串
    const searchStr = ly.stringify(this.searchParams)
    //指定新的位址列url
    const newUrl = location.origin+location.pathname+"?"+searchStr
    //使用history修改位址列,可以不重新整理頁面
    window.history.replaceState(null, null, newUrl)
}

這裡需要注意我們用的是:

  1. this.searchParams.filterParams[key] =
    value而不是this.searchParams.filterParams.key =
    value,用後面的寫法將得不到我們想要的結果,因為key是一個變數,後面的寫法相當於把key當做一個常量了。
  2. List item
    const {…newFilterParams} 表示我們有可能需要多個引數,代表一個另外的一個地址, 如果直接寫
 const {...newFilterParams} = this.searchParams.filterParams

表示的還是原來的地址。新的地址變化,即可觸發監聽事件

四、點選事件

@click="clickFilterParams(k, o.id || o)

<div class="fl value">
    <ul class="type-list">
        <li v-for="o in v" :key="o.id || o" @click="clickFilterParams(k, o.id || o)">
            <a>{{o.name || o}}</a>
        </li>
    </ul>
</div>

在這裡插入圖片描述
注意之後鉤子函式中需要判斷一下filterParams屬性是否為空

//判斷searchObj中是否有filterParams屬性
searchObj.filterParams = searchObj.filterParams || {}

新增監聽事件

"searchParams.filterParams": {
   handler() {
       //向伺服器發起請求
       this.loadSearchPage()
   }
}

五、過濾條件查詢

在這裡插入圖片描述在條件過濾時我們要變換寫法
在這裡插入圖片描述根據以上圖示改寫JAVA程式碼中的service:

/**
 - 查詢條件封裝
*/
private QueryBuilder createQueryBuilder(SearchRequest request) {
   //建立一個過濾條件查詢物件
   BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
   //新增搜尋查詢
   boolQuery.must(QueryBuilders.multiMatchQuery(request.getKey(), "all", "spuName").operator(Operator.AND));
   //得到過濾條件
   Map<String, Object> filterParams = request.getFilterParams();
   //判斷是否有過濾條件
   if(CollectionUtils.isNotEmpty(filterParams)){
       //遍歷過濾條件
       filterParams.entrySet().forEach(entry->{
           //得到key
           String key = entry.getKey();
           //對key進行處理
           if(StringUtils.equals(key, "分類")){
               key = "categoryId";
           }else if(!StringUtils.equals(key, "brandId")){
               key = "specs."+key+".keyword";
           }
           //得到value
           Object value = entry.getValue();
           //新增過濾查詢
           boolQuery.filter(QueryBuilders.termQuery(key, value));
       });
   }
   return boolQuery;
}

結果展示:特有屬性會有多個,共同屬性只會展示一個
在這裡插入圖片描述類比下京東,好像效果不太一樣,哈哈,這是後面的麵包屑功能,以後再說吧
在這裡插入圖片描述新增一個選項所有唯一的資料隱藏

computed: {
    remainFilterConditions(){
        //定義一個計算屬性返回值物件
        const obj = {}
        //遍歷filterConditions
        for (let key in this.filterConditions) {
            //判斷如果當前map的value的長度是否大於1
            if(this.filterConditions[key].length > 1){
                //將長度大於1的過濾條件賦值給新定義的計算屬性
                obj[key] = this.filterConditions[key]
            }
        }
        return obj
    }
},

六、頁面靜態化

不再進入資料庫查詢,從頭到尾只查詢一次,後面都不需要重複載入,減輕伺服器壓力,京東為每一個spu都做了一個靜態頁
在這裡插入圖片描述
修改我們的a標籤屬性,讓它跳轉到我們希望它跳轉的位置,後面可以切換到nginx實現真正的跳轉

<a :href="'/item/'+item.id+'.html'" target="_blank"><img :src="item.selectedSku.image" height="200"/></a>

七、Thymeleaf

特點:將一個靜態頁面做成動態之餘,還可以將一個動態頁面做成靜態化(兩個功能並沒有關聯)
spring官方推薦到的文件並不包括jsp,springBoot特別推薦Thymeleaf
第一步:匯入相關的依賴包

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

第二步:Thymeleaf的配置

  • 檢視器配置
    檢視解析器是作用是什麼:返回到頁面拼接的前字尾,你要返回哪個頁面你只要寫頁面的名稱即可。
    預設字首:classpath:/templates/
    預設字尾:.html
    所以如果我們返回檢視:hello,會指向到 classpath:/templates/hello.html
  • 修改快取配置:
    凡是要經過編譯器編譯的快取就會比較小,html頁面不需要編譯,所需快取比較大,因此一定要將快取改為false
# 關閉Thymeleaf的快取
spring.thymeleaf.cache=false

第三步:搭建靜態資源伺服器

  • 搭建一個新的服務(Moudle):ly-page
    注意:1. 這裡不需要經過閘道器 2. 不需要連線資料庫,通過feign獲取資料

  • 匯入依賴

<dependencies>
   <dependency>
       <groupId>com.alibaba.cloud</groupId>
       <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
       <version>2.1.0.RELEASE</version>
   </dependency>
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
   </dependency>
   <dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-openfeign</artifactId>
   </dependency>
   <dependency>
       <groupId>com.leyou</groupId>
       <artifactId>ly-client-item</artifactId>
       <version>1.0-SNAPSHOT</version>
   </dependency>
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-thymeleaf</artifactId>
   </dependency>
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-test</artifactId>
   </dependency>
   <dependency>
       <groupId>com.leyou</groupId>
       <artifactId>ly-common</artifactId>
       <version>1.0-SNAPSHOT</version>
   </dependency>
</dependencies>

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

第四步:配置檔案:application.yml

server:
  port: 8084
spring:
  application:
    name: page-service
  thymeleaf:
    cache: false
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848

第五步:啟動類

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class LyPageApplication {
    public static void main(String[] args) {
        SpringApplication.run(LyPageApplication.class, args);
    }
}

這裡不需要閘道器,閘道器更多的起到的是一個安全的作用,在服務來獲取資料的時候起到保障作用。

第六步 : 分析渲染商品詳情頁面

在這裡插入圖片描述在這裡插入圖片描述找不到頁面,我們需要提供這樣一個路徑:
在這裡插入圖片描述這樣的話只能返回一個頁面,改寫一下

@Controller
public class PageController {

    @GetMapping("/item/{spuId}.html")
    public String toItemPage(@PathVariable("spuId") Long spuId){
        return "item";
    }


}

在這裡插入圖片描述
修改nginx的配置檔案:

在這裡插入圖片描述在這裡插入圖片描述分析靜態頁
在這裡插入圖片描述在這裡插入圖片描述
根據item.html頁面渲染所需要的佔位符得知,需要如下這些資料:

categories:三個分類物件的集合
brand:品牌物件
spuName:spu的名稱
subTitle:spu的副標題
detail:商品詳情物件
skus:spu下對應的sku的物件集合
specs:規格組物件列表
其中每個規格組物件中有個params屬性,params屬性型別為List

接下來怎麼提供這些資料:?

第七步 : 分析feign介面

現在唯一的條件就是spuId。

第一:根據spuId獲取SpuDTO物件,裡面要包含SpuDeail物件和sku物件的集合兩個屬性。

第二:根據品牌id查詢品牌物件。

第三:根據三個分類id的集合查詢分類物件的集合。

第四:根據第三級分類id查詢出當前分類下所有的規格引數物件集合,而且要包含規格引數集合屬性。

最後貼一下完整的VUE程式碼:

var vm = new Vue({
     el: "#searchApp",
     data: {
         ly,
         //搜尋條件和過濾條件
         searchParams: {
             key: "",
             page: 1,
             filterParams: {}
         },
         //過濾條件結果
         filterConditions: {},
         //商品資料列表
         itemsList: [],
         //總記錄數
         totalCount: 0,
         //總頁數
         totalPages: 0,
         //定義一個控制更多和收起按鈕的布林值
         showMore: false
     },
     computed: {
         remainFilterConditions(){
             //定義一個計算屬性返回值物件
             const obj = {}
             //遍歷filterConditions
             for (let key in this.filterConditions) {
                 //判斷如果當前map的value的長度是否大於1
                 if(this.filterConditions[key].length > 1){
                     //將長度大於1的過濾條件賦值給新定義的計算屬性
                     obj[key] = this.filterConditions[key]
                 }
             }
             return obj
         }
     },
     created(){
         //獲取位址列的key的值
         const key = ly.getUrlParam("key")
         //判斷是否有搜尋條件
         if(!key){
             alert("請求提供搜尋條件!")
             return
         }
         //把key值賦值給searchParams中的key
         //this.searchParams.key = key

         //獲取位址列location的search
         const searchStr = location.search.substring(1)
         //將searchStr轉成json物件
         const searchObj = ly.parse(searchStr)
         //判斷searchObj中是否有page屬性
         searchObj.page = searchObj.page || 1
         //判斷searchObj中是否有filterParams屬性
         searchObj.filterParams = searchObj.filterParams || {}
         //把searchObj物件值覆蓋給searchParams
         this.searchParams = searchObj

         //向伺服器發起請求
         this.loadSearchPage()
     },
     watch: {
         "searchParams.page": {
             handler() {
                 //把page值放到位址列,保證位址列的值與searchParams是一致的
                 //將searchParams轉成字串
                 const searchStr = ly.stringify(this.searchParams)
                 //替換location的search,注意:只要location的search發生了變化,頁面就會自動重新整理
                 //location.search = searchStr

                 //指定新的位址列url
                 const newUrl = location.origin+location.pathname+"?"+searchStr
                 //使用history修改位址列,可以不重新整理頁面
                 window.history.replaceState(null, null, newUrl)

                 //向伺服器發起請求
                 this.pageChange()
             }
         },
         "searchParams.filterParams": {
             handler() {
                 //向伺服器發起請求
                 this.loadSearchPage()
             }
         }
     },
     methods: {
         //向伺服器發起請求獲取渲染搜尋頁面的資料
         loadSearchPage(){
             ly.http.post("/search/load/search/page", this.searchParams)
                 .then((resp)=>{
                     this.filterConditions = resp.data.filterConditions
                     //對商品列表資料中的skus進行轉換
                     resp.data.itemsList.forEach(spu=>{
                         spu.skus = JSON.parse(spu.skus)
                         //給每個spu物件中新增一個預設選中的sku物件
                         spu.selectedSku = spu.skus[0]
                     })
                     //vue中事件,只能對原始屬性起作用
                     this.itemsList = resp.data.itemsList
                     this.totalCount = resp.data.totalCount
                     this.totalPages = resp.data.totalPages
                 })
         },
         pageChange(){
             ly.http.post("/search/page/change", this.searchParams)
                 .then((resp)=>{
                     //對商品列表資料中的skus進行轉換
                     resp.data.forEach(spu=>{
                         spu.skus = JSON.parse(spu.skus)
                         //給每個spu物件中新增一個預設選中的sku物件
                         spu.selectedSku = spu.skus[0]
                     })
                     //vue中事件,只能對原始屬性起作用
                     this.itemsList = resp.data
                 })
         },
         prePage(){
             if(this.searchParams.page>1){
                 this.searchParams.page--
             }
         },
         nextPage(){
             if(this.searchParams.page<this.totalPages){
                 this.searchParams.page++
             }
         },
         clickFilterParams(key, value){
             //先把filterParams物件賦值給另外一個地址的物件
             const {...newFilterParams} = this.searchParams.filterParams
             newFilterParams[key] = value
             this.searchParams.filterParams = newFilterParams

             //將searchParams轉成字串
             const searchStr = ly.stringify(this.searchParams)
             //指定新的位址列url
             const newUrl = location.origin+location.pathname+"?"+searchStr
             //使用history修改位址列,可以不重新整理頁面
             window.history.replaceState(null, null, newUrl)
         }
     },
     components: {
         lyTop: () => import("./js/pages/top.js")
     }
 });