• 电商前台项目(二):完成Home首页模块业务


    一、项目开发的步骤

    1、书写静态页面(HTML,CSS)
    2、拆分组件
    3、获取服务器的数据动态展示
    4、完成相应的动态业务逻辑

    经分析,Home首页可以拆分为7个组件,分别是:TypeNav三级联动导航,ListContainer,Recommend,Rank,Like,Floor,Brand

    二、Home首页拆分静态组件

    1.完成TypeNav 三级联动组件:全局组件

    三级联动(导航部分)Home,Search,Detail组件都在使用,可以把它注册为全局组件,全局组件一般都创建在components里,这里放错了

    好处:只需要注册一次,就可以在项目任意地方使用
    在这里插入图片描述
    在这里插入图片描述

    2.完成其余静态组件:局部组件

    1、创建组件,组件文件夹 => index.vue
    2、导入结构(html) 样式(css) ,查看图片位置有无错误进行修改(最好每个组件文件夹内都新建一个images文件夹,然后把相应的图片拿过来)
    3、引入组件 => 注册组件 => 使用组件

    在这里插入图片描述

    三、请求服务器数据的准备工作

    1.axios二次封装

    向服务器发请求的方式有:XMLHttpRequest,fetch,JQ,axios

    这里用axios,安装:脚手架目录下 npm i axios

    1、为什么需要二次封装axios?
    因为要用到 请求和响应拦截器。请求拦截器:可以在发请求之前处理一些业务。响应拦截器:当服务器数据返回以后,可以处理一些事情

    2、在项目当中经常会出现 api 文件夹,一般是放关于【axios】请求的
    baseURL:'/api',:基础路径,发请求的时候,路径当中会出现基础api
    timeout:5000,: 代表请求超时的时间5s,在5s之内没有响应就失败了

    3、axios基础不好,可以参考 git | axios 文档,后边要补一下axios的内容

    //src/api/request.js
    //本文件是对axios的二次封装
    import axios from 'axios';
    
    //1.利用axios对象的方法create,创建一个axios实例
    const requests = axios.create({
        //配置对象
        baseURL: '/api',  //基础路径,发请求时路径中的api
        timeout: 5000  //代表请求超时的时间为5s
    });
    
    //2.请求拦截器:在发请求之前,请求拦截器可以检测到,可以在请求发出去之前做一些事情
    requests.interceptors.request.use((config) => {
        //config:配置对象,对象里面有一个属性很重要,headers请求头
        return config;
    });
    
    //3.响应拦截器
    requests.interceptors.response.use(
        (res) => {
            //成功的回调函数,服务器响应数据回来以后,响应拦截器可以检测到,可以做一些事情
            return res.data;
        },
        (error) => {
            //响应失败的回调函数
            return Promise.reject(new Error('faile'));
        });
    
    export default requests;
    
    • 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

    2.api接口统一管理

    项目很小:完全可以在组件的生命周期函数中发请求
    项目大,有很多接口:axios.get(‘xxx’)

    // src/api/index.js
    //本文件用于:API的统一管理
    import requests from './request';
    
    //三级联动(导航部分)的接口
    // /api/product/getBaseCategoryList get 无参数
    export const reqCategoryList = function () {
        //发请求:axios发请求返回结果是Promise对象
        return requests({
            url: '/product/getBaseCategoryList',
            method: 'get'
        });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    可以去main.js里测试是否能发送ajax请求

    import { reqCategoryList } from './api';
    reqCategoryList();
    
    • 1
    • 2

    1、跨域问题

    什么是跨域:协议,域名,端口号不同的请求,称之为跨域

    从这里http://localhost:8081/#/home ----前端项目本地服务器
    向这里发请求 http://gmall-h5-api.atguigu.cn ---- 后台服务器

    2、跨域的解决方案:JSONP,CROS,配置代理
    具体配置代理怎么搞详见:脚手架配置代理

    在这里插入图片描述

    3.nprogress进度条的使用

    安装nprogress插件 npm i nprogress

    只要项目当中发请求,进度条就开始往前动,服务器数据返回之后,进度条就结束

    用在 请求和响应拦截器中 src/api/request.js

    nprogress.start方法:进度条开始
    nprogress.done方法:进度条结束

    在这里插入图片描述

    四、Vuex模块化开发

    vuex是官方提供的一个插件,状态管理库,集中式管理项目中组件共用的数据。

    切记,并不是全部项目都需要vuex
    如果项目很小,完全不需要vuex
    如果项目很大,组件很多,数据很多,数据维护很费劲,需要vuex

    安装vuex npm i vuex@3,详情去看Vuex笔记

    1、本项目采用Vuex模块化开发,每个模块建立自己的小仓库,然后小仓库引入到大仓库里并放到Vuex实例身上(Vuex.Store)

    在这里插入图片描述

    2、去main.js里引入大仓库,并把$store放到每个组件实例身上

    在这里插入图片描述

    五、TypeNav导航三级联动

    TypeNav是全局注册组件,一般都放在components文件夹中

    1.三级联动展示数据

    (1)组件挂载完毕后dispatch给Vuex

    在这里插入图片描述

    (2)去home仓库请求数据

    1、actions执行的时候,要通过api里面的接口函数调用,向服务器发请求,获取服务器的数据,需要把之前的api引入进来,在这里发请求就是要调用这个reqCategoryList函数,如果请求成功(code===200),那么把数据交给mutations进行处理
    2、mutations中把数据给state
    3、state中覆盖掉初始值,注意要根据接口的返回值来初始化,服务器返回对象,初始值就是对象,服务器返回数组,初始值就是数组。这里因为后台数据响应体是数组,故初始值为一个空数组

    在这里插入图片描述

    (3)TypeNav接收数据

    写法1:使用mapState来搞个计算属性,使用对象形式,这里和之前学的不一样的地方是,属性值写成一个函数,参数是大仓库的state,可以顺着找到home的state。这样的话我们就拿到了home里请求到的数据,可以直接使用插值语法在页面使用了。

    这里要注意,由于仓库中数据默认值为空,一开始其实computed读到的是空,也就是在mounted中是读不到的,但是当异步请求到数据时,仓库中数据改变,也就是computed依赖的值发生了改变,那么此时根据computed的特征,get函数会重新触发,属性值改变,导致模板重新解析。所以我们才能够把异步请求到的数据传给子组件使用。(子组件先挂载,父组件再挂载)

    在这里插入图片描述
    写法2:当然啊,这里还有另一种写法,那就是首先小仓库home开启命名空间namespaced:true

    在这里插入图片描述
    然后dispatch时就要变成模块名/actions方法名,注意只要开启了命名空间,就要写home/categoryList,然后计算属性写数组或写法1的函数都行:

    在这里插入图片描述

    (4)把数据渲染到页面上

    观察后台数据的结构,大概是这样子的:

    [
        //一级数据
        {
           [
            //二级数据
            {
                [ 三级数据{id,name},{},{}],
                id,
                name
            },
            {},
            {}
           ],
           id,
           name 
        },
        {},
        {}
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    数组里有好多对象(一级数据),对象里有数组,数组里还有对象(二级数据),对象里还有数组,数组里还有对象(三级数据)
    在这里插入图片描述
    一级数据是大的表单,
    在这里插入图片描述

    2.一级分类动态添加背景颜色

    (1)采用css样式实现

    so easy,但是没有挑战性,我们来搞点难的

    .item:hover{
    	background:skyblue;
    }
    
    • 1
    • 2
    • 3

    (2)通过JS实现

    给一级分类添加鼠标经过事件和鼠标离开事件,鼠标离开老师搞了个事件委托,我觉得没必要啊,把鼠标离开放到大div上(一级分类的div)就行吧,这样只要鼠标在二级和三级数据上,一级数据就会保持高亮。

    1、先设置一个响应式属性,存储用户鼠标移上哪一个一级分类currentIndex = -1 ;代表鼠标谁都没有移上去。

      <div class="item bo" 
          v-for="(c1,index) in categoryList" 
           :key="c1.categoryId"
           @mouseleave="leaveIndex">
               <h3 @mouseenter="changeIndex(index)" 
               :class="{cur: currentIndex === index}">
                   <a href="">{{c1.categoryName}}a>
               h3>
               ......
      div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2、然后鼠标移上去时触发回调,拿到当前index给currentIndex,这样的话添加的:class="{cur: currentIndex === index}就会变成true,从而cur样式生效(css去写个背景色)

     data() {
        return {
          // 响应式属性,存储用户鼠标移上哪一个一级分类
          currentIndex: -1, // 代表鼠标谁都没有移上去
        };
      },
      methods: {
        // 鼠标进入修改响应式数据currentIndex属性
        changeIndex(index) {
          // index 鼠标移上某一个一级分类的元素的索引值
          this.currentIndex = index;
        },
        // 一级分类鼠标移出的事件回调
        leaveIndex(){
          // 鼠标移出currentIndex=-1
          this.currentIndex =-1;
        }
      },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3、鼠标离开后currentIndex置为-1,就不会触发样式cur

    3.JS控制二三级数据显示和隐藏

    先把原来的css删掉(display:none和hover display:block)
    然后用原生JS实现可以,用v-show实现也可以

    在这里插入图片描述

    4.三级联动的防抖和节流

    正常:事件触发的非常频繁,而且每一次的触发,回调函数都要去执行(如果时间很短,而函数内部有计算,那么很可能出现浏览器卡顿)

    (1)防抖和节流是什么

    防抖:前面的所有的触发都被取消,最后一次执行在规定的事件之后才会触发,也就是说如果连续的快速触发,只会执行一次 ----------------------当事件被触发后,延迟 n 秒后再执行回调,返回的是一个函数

    const result = _.debounce(function(){
        
    },1000)
    
    • 1
    • 2
    • 3

    节流:在规定的时间范围内不会重复触发回调,只有大于这个时间间隔才会触发回调,把频繁触发变为少量触发,返回的是一个函数

    const result = _.throttle(function(){
        
    },1000)
    
    • 1
    • 2
    • 3

    防抖(回城):用户操作频繁,无关规定时间长短,期间只执行一次。
    节流(平a):用户操作很频繁,但是把频繁的操作变成少量操作【可以给浏览器充裕的时间解析代码】即规定时间执行一次。

    (2)三级联动导航节流

    鼠标来回滑动的时候,把频繁触发变成少量触发,进行节流

    // 这种引入的方式,是把lodash全部功能函数引入,使用时_.throttle
    import _ from 'lodash';
    // 最好的引入方式:按需加载
    import throttle from 'lodash/throttle'
    ...
    
      methods:{
    	  // 鼠标进入修改响应式数据currentIndex属性
    	  //这里不能写简写形式,返回的结果就是一个函数嘛
    	  changeIndex:throttle(function(index){
    	    // index 鼠标移上某一个一级分类的元素的索引值
    	     this.currentIndex = index;
    	  },50),
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    这里的throttle回调函数别用箭头函数,可能出现上下文this问题

    5.三级联动路由跳转与传参(目标search)

    这里要用到自定义属性,忘了去翻:js自定义属性笔记

    (1)需求分析

    实现:当你点击某个分类(a链接)的时候,会从home模块跳转到search模块,并且把分类的名字categoryName和ID传递给search模块,然后search模块拿到这些参数向服务器发请求展示相应数据

    路由跳转可以用,但是这样的话会生成好多组件,页面会卡(这块儿不太懂为啥route-rlink是组件)

    如果只用编程式导航,要给每个a加点击事件,也不太行

    所以最好的解决方案是编程式导航+事件委派

    (2)编程式导航+事件委派

    事件委派可以避免给每个a去添加点击事件,方法是在包含所有a标签的父标签里(一级数据的父标签即可)添加点击事件,这样点里边的任意标签都会事件冒泡到父标签,触发点击事件

    在这里插入图片描述
    但是这里也存在一些的问题:
    1、事件委派,是把全部的子节点【h3,dt,dl,em,a】的事件委派给父亲节点
    2.、只有点击a标签的时候,才会进行路由跳转
    我们怎么设置只有点击到a标签的时候才进行路由跳转呢?

    解决方案:自定义属性
    给所有a标签添加自定义属性data-categoryname,然后使用事件对象event拿到当前点击对象event.targetevent.target.dataset,返回的是该元素身上的所有自定义属性,并使用解构赋值来接收,如果categoryname这个属性不为空,那么就说明点击的是a标签

    3、如何获取参数【1,2,3级分类的产品的名字和ID】?
    名字好说,解构赋值都已经拿到了,主要是id怎么搞?

    解决方案:还是自定义属性,分别给每一级别数据的a标签添加自定义属性

    在这里插入图片描述
    使用解构赋值接收,然后做个判断,如果拿到一级数据的id,就传一级数据,二级传二级,以此类推,最后写出来的点击事件函数长这样:

    goSearch(event) {
        //拿到当前点击对象的所有自定义属性,使用解构赋值接收
         let { categoryname, category1id, category2id, category3id, } = event.target.dataset;
         //判断,如果当前点击对象有categoryname这个自定义属性,就说明是a标签(因为没有肯定返回undefined)
    
         if (categoryname) {  //如果是a标签,就跳转并传参
             //1.首先定义一个query参数对象,先把categoryname拿过来
             let querydj = { categoryName: categoryname };
             //2.判断,如果收到了一级数据的id,就给querydj添加属性和值,以此类推
             if (category1id) {
                 querydj.category1Id = category1id; //添加属性
             } else if (category2id) {
                 querydj.category2Id = category2id; //添加属性
             } else {
                 querydj.category3Id = category3id; //添加属性
             }
             //这样querydj拿到了类别名字和id,就可以传参了
             this.$router.push({
                 name: 'sousuo',
                 query: querydj   //把上面定义的那玩意儿拿过来
             });
         };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    6.Search模块中三级联动的显示与隐藏

    (1)实现显示和隐藏效果

    我们要做的是在home主页中保持显示三级联动模块,在其他组件中(如Search)默认上来隐藏,然后鼠标移入显示,鼠标离开隐藏。怎么搞?
    1、我们可以在TtpeNav组件中加入一个数据show用来决定组件的显示和隐藏,默认值为true

    在这里插入图片描述
    2、利用v-show和数据show来判断是否显示三级导航的内容部分
    在这里插入图片描述
    3、当从Home进入到Search的时候,TypeNav会再重新挂载,所以当进入Search的时候,让show变成false,而且要判断只要不是往主页跳,挂载完都要隐藏(这样的话能够保证主页的三级导航一直显示,避免bug)

    在这里插入图片描述
    4、利用事件委托,外边包个div,保证鼠标移入和移出整个这一大坨内容时功能正常

    在这里插入图片描述

    5、配置回调函数,离开时加个判断,在home中就一直显示,其他组件鼠标离开即隐藏

    //4.1鼠标进入时显示三级导航一二三级分类
    enterShow() {
        this.show = true;
    },
    //4.2鼠标离开时隐藏三级导航一二三级分类
    leaveShow() {
        //4.2.1如果在home组件中,就一直显示,其他组件鼠标离开即隐藏
        if (this.$route.name !== 'zhuye')
            this.show = false;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    (2)添加过渡动画

    使用transition标签包住带有v-show属性的标签,记得加个name,然后去写css样式

    // 三级导航内容部分过渡动画
    //过渡动画开始的状态
    .sort-enter,
    .sort-leave-to {
        height: 0px;
    }
    
    //过渡动画的结束状态
    .sort-enter-to,
    .sort-leave {
        height: 461px;
    }
    
    //定义动画时间,速率
    .sort-enter-active,
    .sort-leave-active {
        overflow: hidden;
        transition: all .1s linear;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    7.解决三级导航ajax请求重复发送的bug

    要点:所有只触发一次的行为,最好都写到app.vue中。这是因为App是唯一的根组件,它的mounted只会执行一次,且App组件早就先执行的。

    之前我们是把下面这段代码写到TypeNav组件的mounted钩子中,但是这样只要home和search来回切换,就重新发起TypeNav中的数据请求,这样连续发送ajax请求不太行啊,所以我们把请求放到App.vue中,这样请求只会发送一次,想用数据直接去Vuex仓库拿就欧了

    //通知Vuex发送请求,获取数据,存储于仓库中
    this.$store.dispatch('home/categoryList');
    
    • 1
    • 2

    8.合并params与query参数

    我们往Search组件跳转有两种方式,一种是通过搜索关键字跳转,另一种是点击三级导航的链接跳转,三级导航传的是query参数,搜索传的是params参数

    在这里插入图片描述
    但是这两种方式只能传一个地方的组件,如果先三级导航跳转再搜索的话,搜索传的params参数和空query参数会覆盖三级导航传的query参数和空params参数
    所以我们可以两边都通过一个判断来实现参数的合并,就是params和query参数同时到url中。

    这里有个疑惑的点,当this.$route.paramsthis.$route.query为空对象时,为什么也能进if里??因为我上来直接点链接(此时明明query是空),也能跳转,好奇怪,空对象是true吗,如果是的话,那这个判断条件完全可以不写。
    发现问题原因:空对象既不是true也不是false。空对象或者空数组,都是构造函数的实例化对象,ta们就算没有自定义的属性或者元素,但是其本身是有定义好的属性和方法的,所以写在if里也能够执行条件体。所以这里的if判断写了个寂寞

    在这里插入图片描述

    解决小问题:这里我的url里一直不显示params参数,最后发现配置路由规则时,占位符名字要和传的params参数的属性名一致……否则就不显示

    六、开发Home首页中的ListContainer、Floor组件

    由于接口返回的后端数据只有商品菜单分类数据(三级联动),对于ListContainer组件与Floor组件数据服务器没有提供,我们这里要用到mock模拟数据。mock能够模拟后台的数据,但是只是在前端自己用,前端mock数据不会和服务器进行任何通信

    1.mock搭建模拟数据

    mock模拟数据:如果你想mock数据,需要用到一个插件mockjs:安装npm i mockjs

    1、在项目src文件夹中创建mock文件夹--------提供假数据的文件夹

    2、准备JSON数据的两个文件(记得格式化一下)
    (1)首页广告轮播数据: src/mock/banner.json,数据来自老师给的文件
    (2)首页楼层数据: src/mock/floor.json,数据来自老师给的文件

    3、把mock数据需要的图片(包括轮播图和floor)放置到public/images文件夹中【public文件夹在打包的时候,会把相应的资源原封不动打包到dist文件夹中】

    4、在mock文件夹下创建mockServe.js,并利用mockjs模块搞出来假数据,这里要注意url路径是以/mock开头的,后边配置axios时要改一下基础路径

    //  本文件路径:src/mock/mockServe.js
    //引入mockjs模块
    import Mock from 'mockjs';
    
    //引入JSON数据格式(JSON数据格式没有对外暴露,但是可以引入)
    //webpack默认对外暴露,不用单独写暴露:图片,JSON数据格式
    import banner from './banner.json';
    import floor from './floor.json';
    
    //使用Mock对象的mock函数模拟数据:第一个参数:请求的地址,  第二个参数:请求的数据
    Mock.mock("/mock/banner", { code: 200, data: banner });
    Mock.mock('/mock/floor', { code: 200, data: floor });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    5、在main.js里引入src/mock/mockServe.js,让配置假数据一上来就先执行一下

    //引入模拟数据的js文件,让它先执行一下
    import './mock/mockServe';
    
    • 1
    • 2

    2.mock虚拟数据的ajax请求

    之前二次封装的axios是用来给后台服务器发送ajax请求的,那虚拟数据该如何请求?很简单,只需要在api文件夹中再配置一个axios,只把其中的baseURL属性值替换成我们在src/mock/mockServe.js文件中配置的数据路径/mock

    在这里插入图片描述
    在这里插入图片描述
    然后去统一管理API的文件(src/api/index.js)中配置轮播图部分的接口:

    在这里插入图片描述

    3.获取Banner轮播图的数据

    和前边获取TypeNav三级导航数据的操作是一样的,只不过那个是真发送ajax请求从后台拿数据,这个是从我们自己搞的假数据里拿数据

    和之前获取三级联动的数据是一个套路
    1、首先挂载完毕后组件派发action,调用仓库中的getBannerList函数

    //   src/pages/Home/ListContainer  
    this.$store.dispatch('home/getBannerList');  // 调用仓库中的这个函数
    
    • 1
    • 2

    2、去Vuex中配置getBannerList函数,通过vuex的actions发起ajax请求,将数据存储在仓库中的state。

    在这里插入图片描述
    3、组件接收数据,使用mapState语法糖

    4.制作ListContainer轮播图

    轮播图可以用这个插件:swiper的基本使用
    安装:npm i swiper@5

    使用swiper要分三步:
    1.引入相应的包
    2.页面结构必须先有(给轮播图添加静态效果)
    3.有结构后再new Swiper实例(给轮播图添加动态效果)

    (1)引入相应的包(js和css)

    JS在每个组件内引入:

    在这里插入图片描述

    css在main.js里引入:由于floor组件和listContainer组件都用到了轮播图,所以我们在main.js里引入swiper的样式,这样的话就都哪个组件都可以用swiper的样式了

    //引入轮播图插件样式swiper
    import 'swiper/css/swiper.css';
    
    • 1
    • 2

    (2)搭建轮播图页面结构

    carousel单词是轮播图的意思
    这里做个铺垫:v-for 遍历mock返回的数据,数据返回后才会渲染遍历到页面

    在这里插入图片描述

    (3)给轮播图添加动态效果

    如果把new Swiper直接放到mounted里边儿,行吗?

    在这里插入图片描述
    不行。因为dispatch去Vuex中请求数据,在actions中发送ajax请求是一个异步操作(用到了async,去看代码),这样的话,会导致new Swiper先执行,然后再去请求数据,再把数据v-for放到轮播图页面结构上。这样的话页面结构还没完整就new Swiper了,会无法正常显示(页面结构必须先生成再new Swiper)

    解决方法1:定时器(不推荐,因为发送ajax请求的时间不确定,所以定时器时间不好把握)

    mounted() {
        //挂载完毕后通过Vuex发送ajax请求,将数据存储在仓库中
        this.$store.dispatch('home/getBannerList');
        setTimeout(() => {
            var mySwiper = new Swiper(".swiper-container", {
                loop: true,
                cssMode: true,
                navigation: {
                    nextEl: ".swiper-button-next",
                    prevEl: ".swiper-button-prev",
                },
                pagination: {
                    el: ".swiper-pagination",
                    clickable: true, // 点击小球的时候也切换
                },
                mousewheel: true,
                keyboard: true,
            });
        }, 1000);
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    解决方法2:watch和$nextTick(用这个)
    watch:数据监听,监听已有数据的变化
    1、监听bannerList数据的变化,因为这条数据由空数组变为数组里面有mock数据
    2、通过watch监听bannerList属性的属性值的变化
    3、如果执行handler方法,代表组件实例身上这个属性的属性值数组已经有了mock数据
    4、但是这里 结构没有渲染完成 ,现在只能保证数据已经有了,但是没法保证v-for已经执行完成了

    $nextTick:在下次 DOM 更新,循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。当执行这个回调的时候,保证服务器的数据回来了,v-for执行完毕了【轮播图的结构已经完成了】。$nextTick可以保证页面中的结构一定是有的,经常和很多插件一起使用【都需要DOM存在】

    //   文件:src/pages/Home/ListContainer
    watch: {
        //监听bannerList数据的变化:由空数组变成mock数据
         bannerList: {
             //当bannerList从空数组变成有数据时(ajax请求成功),执行handler函数
             //但是,当前函数执行的时机只能保证数据有了,但是v-for是否执行结束不知道
             //而v-for执行完毕的时候,页面才能有结构
             handler(newVal, oldVal) {
                 //nextTick能保证页面结构先渲染出来,然后再执行回调函数
                 this.$nextTick(function () {
                     var mySwiper = new Swiper(".swiper-container", {
                         loop: true,
                         cssMode: true,
                         navigation: {
                             nextEl: ".swiper-button-next",
                             prevEl: ".swiper-button-prev",
                         },
                         pagination: {
                             el: ".swiper-pagination",
                             clickable: true, // 点击小球的时候也切换
                         },
                         mousewheel: true,
                         keyboard: true,
                     });
                 })
             }
         }
     }
    
    • 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

    但是mounted+$nextTick就不行,必须点别的再点回来才显示轮播图功能,为啥???

    5.开发floor组件

    组件首先还是拿数据,步骤和之前的一样:配置api接口 => 在actions请求数据,存储到Vuex中 => dispatch给Vuex => 拿到数据渲染到页面上

    (1)从mock拿数据

    1、配置api接口

    //   src/api/index.js
    //3.floor部分的接口
    //  /mock/floor
    export const reqFloorList = () => mockRequests.get('/floor');
    
    • 1
    • 2
    • 3
    • 4

    2、数据存储到仓库

    //  src/store/home/index.js
    import { reqCategoryList, reqBannerList, reqFloorList } from '@/api';
    //本文件用于配置home模块的数据
    export const home = {
        namespaced: true,
        state: {
            //服务器返回对象,初始值就是对象,服务器返回数组,初始值就是数组
            categoryList: [],   //三级联动的数据
            bannerList: [],   //上边轮播图的数据
            floorList: []    //floor的数据
        },
        actions: {
            //通过api/index.js里面的接口函数调用,向服务器发请求,获取服务器数据
            //1.获取三级联动部分的数据
            async categoryList(context) {
                //await后面等待结果,如果返回结果,再往下走?
                let result = await reqCategoryList();
                if (result.code === 200) {
                    context.commit('CATEGORYLIST', result.data);
                }
            },
            //2.获取轮播图部分的数据
            async getBannerList(context) {
                let result = await reqBannerList();
                if (result.code === 200) {
                    context.commit('GETBANNERLIST', result.data);
                }
            },
            //3.获取floor部分的数据
            async getFloorList({ commit: dj }) {   //解构赋值,起名dj
                let result = await reqFloorList();
                if (result.code === 200) {
                    dj('GETFLOORLIST', result.data);
                }
            }
        },
        mutations: {
            //1.操作三级联动的数据
            CATEGORYLIST(state, value) {
                state.categoryList = value.slice(0, 15);
            },
            //2.操作轮播图的数据
            GETBANNERLIST(state, value) {
                state.bannerList = value;
            },
            //3.操作floor的数据
            GETFLOORLIST(state, value) {
                state.floorList = value;
            }
        },
        getters: {}
    }
    
    • 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

    2、dispatch给Vuex
    去哪里dispatch呢?这个要看我们的数据和home页的结构,mock数据里是数组包两个对象[{},{}],所以应该有两个Floor组件,而且每个里面的数据是不一样的,如果去floor组件里dispatch,那么没法拿里面的数据,而且两个数据怎么区分?所以要去home里dispatch
    在这里插入图片描述
    然后使用mapState拿到数据

    (2)Home中的数据传给Floor

    数据在home中,传给Floor——父子组件通信使用props
    第一个floor用数组里的第一个对象,第二个floor用数组里的第二个对象

    在这里插入图片描述

    (3)数据渲染到Floor页面

    在这里插入图片描述

    Floor已经拿到了数据,这块儿就不难了,根据数据的结构和html结构把它们分别用v-for或插值语法渲染到页面上就行了,这里边值得注意的一个地方是这里的轮播图。

    轮播图的三步使用:1.导包 2.页面结构先有 3.new Swiper实例后有

    在这里插入图片描述
    和前边不同的是,这里我们可以把new Swiper实例放在mounted里,这是因为Floor里的数据都是Home组件传过来的,而发请求是在父组件Home里面挂载完毕发的,所以传过来的数据是请求好的数据,也就是说Floor组件里面没有任何异步操作,所以挂载完毕之后页面结构就会先有,然后就直接new Swiper就行了。(之前我们是在当前组件ListContainer的内部发请求以及动态的渲染结构,必须watch+$nextTick

    <script>
    import Swiper from 'swiper';
    export default {
        name: 'Floor',
        props: ['eachFloor'],
        mounted() {
            new Swiper(".swiper-container", {
                loop: true,
                cssMode: true,
                navigation: {
                    nextEl: ".swiper-button-next",
                    prevEl: ".swiper-button-prev",
                },
                pagination: {
                    el: ".swiper-pagination",
                    clickable: true, // 点击小球的时候也切换
                },
                mousewheel: true,
                keyboard: true,
            });
        }
    }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    (4)把轮播图封装为全局组件

    以后在开发项目的时候,如果看到某一个组件在很多地方都使用,你把它变成全局组件,注册一次,可以在任意地方使用,公用的组件 | 非路由组件放到components文件夹中。结构,样式,行为要几乎一样才能封装成全局组件,大家一起公用

    我们发现ListContainer中的轮播图和Floor中的轮播图长得差不多,唯一的区别就是数据不同,还有就是ListContainer里用的watch+$nextTick(因为要异步请求数据),Floor是直接写道mounted里,其实Floor也可以用watch+$nextTick,但是由于Floor中的数据是直接拿Home请求好的,所以要加上immediate: true(不管数据变没变先调用handeler),这样的话,轮播图组件就可以封装成以下样子:

    src/conponents/Carousel
    样式在main.js里已经引入了,这里可以直接用
    <template>
        <div class="swiper-container" id="floor1Swiper">
            <div class="swiper-wrapper">
                <div class="swiper-slide" v-for="carousel in carousels" :key="carousel.id">
                    <img :src=carousel.imgUrl>
                div>
            div>
            
            <div class="swiper-pagination">div>
    
            
            <div class="swiper-button-prev">div>
            <div class="swiper-button-next">div>
        div>
    template>
    
    <script>
    import Swiper from 'swiper';
    export default {
        name: 'Carousel',
        props: ['carousels'],
        watch: {
            //监听carousels数据的变化
            carousels: {
                immediate: true,  //不管数据变没变,上来先执行一次handler
                //当bannerList从空数组变成有数据时(ajax请求成功),执行handler函数
                //但是,当前函数执行的时机只能保证数据有了,但是v-for是否执行结束不知道
                //而v-for执行完毕的时候,页面才有结构
                handler(newVal, oldVal) {
                    //nextTick能保证页面结构先渲染出来,然后再执行回调函数
                    this.$nextTick(function () {
                        var mySwiper = new Swiper(".swiper-container", {
                            loop: true,
                            cssMode: true,
                            navigation: {
                                nextEl: ".swiper-button-next",
                                prevEl: ".swiper-button-prev",
                            },
                            pagination: {
                                el: ".swiper-pagination",
                                clickable: true, // 点击小球的时候也切换
                            },
                            mousewheel: true,
                            keyboard: true,
                        });
                    })
                }
            }
        }
    };
    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
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    封装好后去main.js注册全局组件,然后ListContainer和Floor就可以直接用了。
    在使用时,把对应的轮播图信息传过去就行,使用props接收

    在这里插入图片描述
    到这里Home首页就完结了,继续加油奥里给!!!!!

  • 相关阅读:
    天振股份上市首日破发:市值蒸发约8亿元,方庆华夫妇为实控人
    LLM在text2sql上的应用
    【Java项目-飞翔的小鸟】附源码
    精品基于Java的社区团购系统SSM
    Part 11:Pandas的索引index所具备的四大性能
    Map<String, Object> 和 com.fasterxml.jackson.databind.node.ObjectNode区别
    【Transformers】第 6 章:用于标记分类的微调语言模型
    【数据结构与算法(C语言)】离散事件模拟- 单链表和队列的混合实际应用
    基于STM32的智能小车方案设计
    数字机器人如何更好的助力智慧政务?这里或许有你想要的答案
  • 原文地址:https://blog.csdn.net/weixin_42044763/article/details/126745745