学习笔记-DAY01-VUE

学习笔记-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")
     }
 });
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页