Skip to content

首页界面

本项目是一个电商项目的简易Demo

启动项目DEMO

  • 将项目目录中的sph放置本地

  • 进入到目录sph,安装依赖模块

    shell
    cnpm i
  • package.json

    json
    {
    	"scripts":{
    		"start":"node server.js"
    	},
    	
      "dependencies": {
        "connect-history-api-fallback": "^2.0.0",
        "express": "^4.18.2",
        "http-proxy-middleware": "^2.0.6"
      }
    }
  • 启动:

    shell
    npm start

通过脚手架搭建项目

  • 选择默认版本vue2

    shell
    vue create first # 选择默认安装 vue2 -->Default ([Vue 2] babel, eslint)

整理项目

  • 复制尚品汇图标至public->favicon.ico

  • 启动项目如果站标未发生改变,清除缓存,然后重新打开浏览器。

  • public->index.html

    html
    <!DOCTYPE html>
    <html lang="">
    <head>
    	<meta charset="utf-8">
    	<link rel="icon" href="<%= BASE_URL %>favicon.ico">
    	<title><%= htmlWebpackPlugin.options.title %></title>
    </head>
    <body>
    	<div id="app"></div>
    </body>
    </html>
  • vue.config.js

    js
    const {defineConfig} = require('@vue/cli-service')
    module.exports = defineConfig({
    	transpileDependencies: true,
    	// publicPath:"./",// 更改BASE_URL
    	devServer:{
    		open:true,
    		host:"zhangpeiyue.com",
    		port:80
    	},
    	pages: {
    		index: {
    			// page 的入口
    			entry: 'src/index.js',
    			// 设置网站标题
    			title:"尚品汇"
    		}
    	}
    })

    官方文档:https://cli.vuejs.org/zh/config/#pages

    javascript
    module.exports = {
      pages: {
        index: {
          // page 的入口
          entry: 'src/index/main.js',
          // 模板来源
          template: 'public/index.html',
          // 在 dist/index.html 的输出
          filename: 'index.html',
          // 当使用 title 选项时,
          // template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
          title: 'Index Page',
          // 在这个页面中包含的块,默认情况下会包含
          // 提取出来的通用 chunk 和 vendor chunk。
          chunks: ['chunk-vendors', 'chunk-common', 'index']
        },
        // 当使用只有入口的字符串格式时,
        // 模板会被推导为 `public/subpage.html`
        // 并且如果找不到的话,就回退到 `public/index.html`。
        // 输出文件名会被推导为 `subpage.html`。
        subpage: 'src/subpage/main.js'
      }
    }

解决esling问题

  • src->index.js

    js
    import Vue from "vue";
    import App from "@/App";
    const vm = new Vue({
    	el:"#app",
    	render:h=>h(App)
    })
  • 解决一,配置忽略:package.json

    json
    {
      ....
        "rules": {
        	// 不用的变量忽略检查
            "no-unused-vars":0,
        	// 组件命名多个字母
            "vue/multi-word-component-names": 0
        }
      .....
    }
  • 解决二:整体忽略lint检查 vue.config.js

    js
    const {defineConfig} = require('@vue/cli-service')
    module.exports = defineConfig({
    	transpileDependencies: true,
    	// 忽略lint检查
    	lintOnSave:false,
    	// publicPath:"./",// 更改BASE_URL
    	devServer:{
    		open:true,
    		host:"zhangpeiyue.com",
    		port:80
    	},
    	pages: {
    		index: {
    			// page 的入口
    			entry: 'src/index.js',
    			// 设置网站标题
    			title:"尚品汇"
    		}
    	}
    })
  • 解决三:在esling配置文件中更改 .eslintrc.js

    javascript
    module.exports = {
        root: true,
        env: {
            node: true
        },
        'extends': [
            'plugin:vue/essential',
            'eslint:recommended'
        ],
        parserOptions: {
            parser: '@babel/eslint-parser'
        },
        rules: {
            'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
            'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
            "vue/multi-word-component-names": "off",
            "vue/valid-v-slot":"off",
        }
    }

将首页放置到App.vue组件中

  • swiper.min.css放到public->css->swiper.min.css

  • 在public->index.html中引入css

    html
    <!DOCTYPE html>
    <html lang="">
    <head>
    	<meta charset="utf-8">
    	<link rel="icon" href="<%= BASE_URL %>favicon.ico">
    	<link rel="stylesheet" href="<%= BASE_URL >css/swiper.min.css">
    	<title><%= htmlWebpackPlugin.options.title %></title>
    </head>
    <body>
    	<div id="app"></div>
    </body>
    </html>
  • 把home的css放在src->pages->Home->index.vue中的style标签中

    vue
    ...
  • 把重置样式表reset.css放在index.js文件中导入

    js
    import "src/assets/css/reset.css"
  • 安装less

    javascript
    npm install less-loader less -D // -D 在开发环境中使用
  • 把图片放置在 src->assets->images中

  • 修改html中的img的src地址为正确的地址

头部与底部组件

  • src->components->Header->index.vue

    javascript
    把头部组件Header.vue从APP.vue中抽离出来,
    同时把组件内的图片单独放置在组件中的文件夹 src->components->Header->images
  • src->components->Footer->index.vue

    javascript
    把尾部组件Footer.vue从APP.vue中抽离出来
    同时把组件内的图片单独放置在组件中的文件夹 src->components->Footer->images
  • src->App.vue

    javascript
    ...

创建基本的路由

javascript
首页:------>   /
登陆: --------> /login
注册:---------->/register
搜索:------------>/search
  • 安装vue-router

    javascript
    cnpm install vue-router@3 // vue-router 3版本对vue2支持
  • 在src目录下创建4个页面 src->pages->Home,src->pages->login,src->pages->register,src->pages->search

  • src->router->index.js

    javascript
    import Vue from "vue";
    import VueRouter from "vue-router";
    import Home from "@/pages/Home";
    import Login from "@/pages/Login";
    import Register from "@/pages/Register";
    import Search from "@/pages/Search";
    Vue.use(VueRouter);
    const routes = [
    	{
    		path:"/",
    		component:Home
    	},
    	{
    		path:"/login",
    		component:Login
    	},
    	{
    		path:"/Register",
    		component:Register
    	},
    	{
    		path:"/search",
    		component:Search
    	}
    ];
    const router = new VueRouter({
    	mode:"history",
    	routes
    });
    export default router;
  • 把首页Home.vue从App.vue中抽离出来

    javascript
    html中的图片的src修改图片为 ../../assets/images/home/
  • src->App.vue

    vue
    <template>
        <!-- 项目的最外层 -->
        <div>
            <!--  头部 -->
            <Header></Header>
            <router-view/>
            <!--  底部  -->
            <Footer></Footer>
        </div>
    </template>
    
    <script>
    import Home from "@/pages/Home";
    import Header from "@/components/Header";
    import Footer from "@/components/Footer";
    export default {
        name: "App",
        components: {Footer, Header, Home}
    }
    </script>
    
    <style lang="less" scoped>
    </style>
  • src->index.js

    javascript
    import Vue from "vue";
    import App from "@/App";
    import router from "@/router";
    import "@/assets/css/reset.css";
    new Vue({
    	el:"#app",
    	router,
    	render:h=>h(App)
    })

实现导航切换

  • src->components->Header->index.vue

    javascript
    <!--1-->
    <router-link to="/login">登录</router-link>
    <!--2-->
    <router-link to="/register" class="register">免费注册</router-link>
    <!--3-->
    <router-link to="/" class="logo" title="尚品汇"> <img src="./images/logo.png" alt="">
    </router-link>
    <!--4-->
    <button @click="$router.push('/search')" class="sui-btn btn-xlarge btn-danger" type="button">搜索</button>
  • src->App.vue

    javascript
    <!-- 匹配的路由出口 -->
    <router-view></router-view>

Push方法重写

  1. 解决的问题是:如果通过编程式导航,切换到当前路由时,那么会有异常。
js
原因:由于vue-route3之后对路由地址进行了判断,且采用了promise形式(如果通过编程式导航切换的地址和原来的地址相同,会返回一个失败的promise对象)。

解决的办法:
1 使用vue-router3.1之前的
2 处理异常:
// 方式1
this.$router.push('/search').then(value=>{
    // 成功
},reason=>{
    // 失败
})
// 方式2
this.$router.push('/search').catch(()=>{})
// 方式3:使用push的回调函数处理异常,push,replace接收的第一参数是地址,第二个参数是 成功回调,第三个参数是失败回调,
this.$router.push('/search',onComplete,onAbort)
  • src->router->index.js(方式1)

    javascript
    // 1- 先将要加强的方法备份
    const nativePush = VueRouter.prototype.push;
    const nativeReplace = VueRouter.prototype.replace;
    // 2- 重写push方法,并且使用push的回调函数onAbort,对失败的情况进行空处理,就不会报错。
    VueRouter.prototype.push = function(location, onComplete, onAbort){
    	return nativePush.call(this,location, onComplete, ()=>{})
    }
    VueRouter.prototype.replace = function(location, onComplete, onAbort){
    	return nativeReplace.call(this,location, onComplete, ()=>{})
    }
    js
    可选的在 router.push 或 router.replace 中提供 onComplete 和 onAbort 回调作为第二个和第三个参数。这些回调将会在导航成功完成 (在所有的异步钩子被解析之后) 或终止 (导航到相同的路由、或在当前导航完成之前导航到另一个不同的路由) 的时候进行相应的调用。
  • src->router->index.js(方式2)

    javascript
    新建一个文件夹 utils/tools 用于存放自定义的工具 
    mkdir src->utils->pushReWrite.js

设置分类条组件

方式1:把typeNav设置为全局组件

json
// 1 src->components->TypeNav->index.vue
....从Home.vue中抽离出来

// 2 src->index.js
// 导入全局组件
import TypeNav from "@/components/TypeNav";
Vue.component("TypeNav", TypeNav);
...

// 3 src->pages->Home->index.vue
<TypeNav></TypeNav>

方式2:将typeNav放在Header中,由路由控制显示和隐藏

javascript
// 1 src->components->Header->TypeNav->index.vue
....把typeNav放在Header组件中

// 2 src->components->Header->index.vue
<template>
	<!-- 商品分类栏 -->
	<TypeNav v-if="$route.meta.isTypeNav"></TypeNav>     
</template>
import TypeNav from "@/components/Header/TypeNav";
components: { TypeNav },

使用axios获取分类条数据

  • 在 src->components->TypeNav->index.vue 组件中使用axios获取数据
javascript
npm install axios

// src->components->TypeNav->index.vue

<script>
export default {
    name: "TypeNav",
    
}
</script>
  • vue.config.js 配置代理服务器,注意重启服务
javascript
const {defineConfig} = require('@vue/cli-service')
module.exports = defineConfig({
	transpileDependencies: true,
	// 忽略lint检查
	lintOnSave:false,
	devServer:{
		open:true,
		host:"zhangpeiyue.com",
		port:80,
        // 1
		proxy:{
            // 访问"/api"的路径,统一拼接为"http://sph-h5-api.atguigu.cn/api"
			"/api":{
				target:"http://sph-h5-api.atguigu.cn",
				changeOrigin:true
			}
		}
	},
	pages: {
		index: {
			// page 的入口
			entry: 'src/index.js',
			// 设置网站标题
			title:"尚品汇"
		}
	}
})

使用 nprogress 插件

使用 nprogress 插件,渲染axios加载进度条

  • 下载nprogress https://www.npmjs.com/package/nprogress

    javascript
    npm install nprogress
  • src->components->Header->TypeNav->index.vue

    javascript
    <script>
    import axios from "axios";
    import nprogress from "nprogress";
    import "nprogress/nprogress.css"
    
    export default {
        name: "TypeNav",
        mounted() {
            // 请求地址:http://sph-h5-api.atguigu.cn/api/product/getBaseCategoryList
            // 请求方式:get
            nprogress.start();// 开始加载
            axios.get("/api/product/getBaseCategoryList")
            .then(({data}) => {
                console.log(data);
                // 加载完成
                nprogress.done();
            },err=>{
                console.log(err);
                nprogress.done;
            })
        },
    }
    </script>
  • nprogress的使用

    javascript
    Add nprogress.js and nprogress.css to your project
    //添加nprogress.js and nprogress.css
    import nprogress from "nprogress"; // 在node_modules/nprogress/component.json中的main中指定了入口文件为nprogress.js
    import "nprogress/nprogress.css"
    // 使用
    NProgress.start(); //加载
    NProgress.done();	// 加载完成

axios封装

  • 新建一个sphRequest.js文件(发送axios请求),src->request->sphRequest.js
javascript
import axios from "axios";
import nprogress from "nprogress";
import "nprogress/nprogress.css";
// 创建一个实例
const sphRequest = axios.create({
    baseURL: "/api",
    timeout: 5000
});
// 请求拦截器
sphRequest.interceptors.request.use(config => {
    nprogress.start();// 开启进度条
    return config;
});
// 响应拦截器
sphRequest.interceptors.response.use(response => {
    nprogress.done();// 结束进度条
    return response.data;// 返回响应体
},err=>{
    nprogress.done();// 结束进度条
    alert(err);//提示错误信息
    return new Promise(() => { });//中断Promise
})
// 暴漏数据
export default sphRequest;
  • 新建一个入口文件,src->request->index.js
javascript
// 引入请求
import sphRequest from "@/request/sphRequest";
// 暴漏数据
export {
    sphRequest
}
  • 在组件中使用 src->components->Header->TypeNav->index.vue
javascript
<script>
import {sphRequest} from "@/request"
export default {
    name: "TypeNav",
    mounted() {
        sphRequest.get("/product/getBaseCategoryList")
        .then(res=>{
            console.log("res",res);
        })
    },
}
</script>

API封装

建议:按照后端提供的API进行分类

javascript
新建一个api文件夹,src->api->product.js
javascript
// 导入axios
import { sphRequest } from "@/request";
// 暴漏数据
export const getBaseCategoryList = function(){
    return sphRequest("/product/getBaseCategoryList");
}

在页面中调用api方法

javascript
src->components->Header->typeNav->index.vue

...

<script>
// 导入api接口方法
import { getBaseCategoryList } from '@/api/product';
export default {
    name: "TypeNav",
    mounted() {
        getBaseCategoryList()
        .then(res=>{
            console.log("res",res);
        })
    },
}
</script>
...

渲染TypeNav分类

vue
src->components->Header->typeNav->index.vue

...
<!--  全部商品分类 -->
<div v-for="c1 in categoryList" :key="c1.categoryId" class="item">
    <h3>
        <a>{{ c1.categoryName }}</a>
    </h3>
    <div class="item-list clearfix">
        <div v-for="c2 in c1.categoryChild" :key="c2.categoryId" class="subitem">
            <dl class="fore">
                <dt>
                    <a href="">{{ c2.categoryName }}</a>
                </dt>
                <dd>
                    <em v-for="c3 in c2.categoryChild" :key="c3.categoryId">
                        <a href="">{{ c3.categoryName }}</a>
                    </em>
                </dd>
            </dl>
        </div>
    </div>
</div>
...
<script>
// 导入api接口方法
import { getBaseCategoryList } from '@/api/product';
export default {
    name: "TypeNav",
    data(){
        return{
            // 分类数据
            categoryList:[],
        }
    },
    mounted() {
        getBaseCategoryList()
        .then(res=>{
            this.categoryList = res.data.splice(0,15);
        })
    },
}
</script>
...

优化渲染内存

javascript
由于分类较多,每个分类均需要跳转,如果跳转使用组件router-link,会占用大量内存!
需要优化:使用事件委托,把router-link委托给父元素
vue
src->components->Header->typeNav->index.vue

<div @click="$router.push('/search')" class="all-sort-list2"></div>

搜索页面默认隐藏商品分类

vue
src->components->Header->TypeNav->index.vue

...
<!-- 在父元素设置离开 categroyConstricted 判断是否是首页-->
<div @mouseleave="categroyConstricted" class="nav-left">
    <h2 @mouseenter="isShowCategory=true" class="all">全部商品分类</h2>
    <div class="sort" v-show="isShowCategory">
        ...
    </div>
</div>
...
<script>
export default {
    name: "TypeNav",
    data(){
        return{
            ...
            // 是否展开分类页面,当路径为/展开,默认展开
            isShowCategory:this.$route.path === "/",
        }
    },
	...
    methods:{
        // 分类展开方法,如果路径不是/ 不展开分类
        categroyConstricted(){
            if (this.$route.path !== '/'){
                this.isShowCategory = false;
            }
        }
    },
    // 监听数据变化
    watch:{
        // 当路由路径发生变化,更新isShowCategory状态
        "$route.path":{
            handler(){
                this.isShowCategory = this.$route.path === '/';
            }
        }
    }
}
</script>
javascript

使用VueX保存数据

  1. 安装vueX
shell
npm install vuex@3 //安装vuex大版本为3
  1. 新建store入口文件 src->store->index.js
js
src->store->index.js

import Vue from "vue";
import Vuex from "vuex";
//导入模块
import product from "@/store/product";
Vue.use(Vuex);
const store = new Vuex.Store({
	modules:{
		product
	}
});
export default store;
  1. 新建product模块文件:src->store->product.js
js
// 导入api
import { getBaseCategoryList } from '@/api/product';

// 定义商品的数据状态
const state = {
    // 首页分类列表
    categoryList:[],
}
// 定义mutations
const mutations = {
    // 修改state中的首页分类列表
    UP_CATEGORY_LIST(state,categoryList){
        state.categoryList = categoryList
    }
}
// 定义actions
const actions = {
    // 使用api接口获取数据
    async getBaseCategoryListAsync({commit},num=1){
        const { data } = await getBaseCategoryList();
        commit("UP_CATEGORY_LIST",data.splice(0,num));
    }
}
// 暴漏数据,导出模块
export default {
    namespaced:true,
    state,
    mutations,
    actions,
}
  1. 在vue入口文件中导入,并挂载
js
import Vue from "vue";
import App from "@/App";
import router from "@/router";
import store from "@/store";
import "@/assets/css/reset.css";
// 设置为 false 以阻止 vue 在启动时生成生产提示
Vue.config.productionTip = false;
new Vue({
	el:"#app",
	router,
	store,
	render:h=>h(App)
})

底部分别渲染

登陆,注册页面与其它的底部不同(只是其它页面中的一部分)

把版权作为子组件抽离出来:src->components->CopyRight->index.vue

vue
// src->components->CopyRight->index.vue

<template lang="">
    ...
</template>
<script>
export default {
    name:"CopyRight",
}
</script>
<style lang="less" scoped>
	....
}
</style>

Footer父组件:src->components->Footer->index.vue

vue
<template lang="">
	<div v-if="!$route.meta.isHideFooterList" class="footer">
    	...
        <CopyRight></CopyRight>
    </div>
	<CopyRight v-else></CopyRight>
    ...
</template>

<script>
import CopyRight from '@/components/Footer/CopyRight';
export default {
    name: "Footer",
    components: {
        CopyRight,
    },
}
</script>

<style lang="less" scoped>
	....
}
</style>

路由组件:src->router->index.js

js
// 定义路由
const routes = [
    {
        path:'/',
        component:Home,
        meta:{
            isTypeNav:true,
        }
    },
    {
        path: '/login',
        component: Login,
        meta:{
            // 隐藏页脚中的列表信息
            isHideFooterList:true,
        }
    },
    {
        path: '/register',
        component: Register,
        meta: {
            // 隐藏页脚中的列表信息
            isHideFooterList: true,
        }
    },
    {
        path: '/search',
        component: Search,
        meta: {
            // 是否使用导航
            isTypeNav: true
        }
    },
]

使用swiper实现轮播图

js
* vue资源地址:https://github.com/vuejs/awesome-vue
* awesome仓库:https://github.com/surmon-china/vue-awesome-swiper
* 示例:https://v1.github.surmon.me/vue-awesome-swiper/
  1. 下载模块:使用5版本的swiper与4版本的vue-awesome
js

npm install swiper@5 vue-awesome-swiper@4
  1. 新建一个组件作为Home的子组件MainAdv
js
src->pages->Home->MainAdv->index.vue

<template lang="">
    <div>
        <swiper class="swiper" :options="swiperOption">
            <swiper-slide><img src="" /></swiper-slide>
            <swiper-slide><img src="" /></swiper-slide>
            <swiper-slide><img src="" /></swiper-slide>
            <swiper-slide><img src="" /></swiper-slide>
            <div class="swiper-pagination" slot="pagination"></div>
            <div class="swiper-button-prev" slot="button-prev"></div>
            <div class="swiper-button-next" slot="button-next"></div>
        </swiper>
    </div>
</template>
<script>
import { Swiper, SwiperSlide } from 'vue-awesome-swiper';
import 'swiper/css/swiper.css';
export default {
    name:"MainAdv",
    components:{
        Swiper,
        SwiperSlide
    },
    data() {
        return {
            swiperOption: {
                slidesPerView: 1,
                spaceBetween: 30,
                loop: true,
                pagination: {
                    el: '.swiper-pagination',
                    clickable: true
                },
                navigation: {
                    nextEl: '.swiper-button-next',
                    prevEl: '.swiper-button-prev'
                }
            }
        }
    }
}
</script>
<style lang="less">
.swiper {
    height: 100%;
    img {
        width: 100%;
    }
}
</style>

复制的swiper模版源码

vue

<template>
  <swiper class="swiper" :options="swiperOption">
    <swiper-slide>Slide 1</swiper-slide>
    <swiper-slide>Slide 2</swiper-slide>
    <swiper-slide>Slide 3</swiper-slide>
    <swiper-slide>Slide 4</swiper-slide>
    <swiper-slide>Slide 5</swiper-slide>
    <swiper-slide>Slide 6</swiper-slide>
    <swiper-slide>Slide 7</swiper-slide>
    <swiper-slide>Slide 8</swiper-slide>
    <swiper-slide>Slide 9</swiper-slide>
    <swiper-slide>Slide 10</swiper-slide>
    <div class="swiper-pagination" slot="pagination"></div>
    <div class="swiper-button-prev" slot="button-prev"></div>
    <div class="swiper-button-next" slot="button-next"></div>
  </swiper>
</template>

<script>
  import { Swiper, SwiperSlide } from 'vue-awesome-swiper'
  import 'swiper/css/swiper.css'

  export default {
    name: 'swiper-example-loop',
    title: 'Loop mode / Infinite loop',
    components: {
      Swiper,
      SwiperSlide
    },
    data() {
      return {
        swiperOption: {
          slidesPerView: 1,
          spaceBetween: 30,
          loop: true,
          pagination: {
            el: '.swiper-pagination',
            clickable: true
          },
          navigation: {
            nextEl: '.swiper-button-next',
            prevEl: '.swiper-button-prev'
          }
        }
      }
    }
  }
</script>

<style lang="scss" scoped>
  @import './base.scss';
</style>
  1. Home父组件
vue
<div class="center">
    <!--banner轮播-->
    <MainAdv></MainAdv>
</div>

<script>
import MainAdv from '@/pages/Home/MainAdv'
export default {
    name:"Home",
    components:{
        MainAdv,
    }
}
</script>

mock的使用

作用:mockjs可以帮助前端程序员快速创建API接口用于模拟数据。

  1. 下载模块
js
npm install mockjs
mock_npm源
https://www.npmjs.com/package/mockjs
mock官方文档
http://mockjs.com/
  1. 新建mook目录,提供自定义接口
js
src->mock->index.js

import Mock from "mockjs";
import focusList from "./focus.json";

// 执行之后,那么在你发送ajax请求时会被拦截。
// 拦截的条件:
// 1- 请求地址为/my
// 2- 请求方式为get
// 注意:
// 1- mock接收的第三个参数是响应体,第一个参数是地址,第二个参数是请求方式
// 2- 被拦截下来的ajax请求(xhr,fetch)在网络中是无法查看的。

Mock.mock("/my","get",{
	ok:1,
	msg:"my->get->success",
	data:focusList
})
  1. 在项目入口文件中引入mock->index.js
js
src->index.js

import "@/mock";
  1. 在vue页面中发送axios请求,请求mock中的数据
json
src->views->Home->MainAdv->index.vue

mounted(){
    axios.get("/my").then(value=>{
        console.log(value.data);
    })
}

封装mockRequest

  1. 将图片放置在public->images中
  2. 设置mock拦截
javascript
src->mock->index.js

import Mock from "mockjs";
import focusList from "./focus.json";
// 获取首页中轮播图的图片列表
// 请求方式:get
// 请求地址:
Mock.mock("http://hanser.com/adv/focus","get",{
	ok:200,
	data:focusList
})
  1. mock中的data数据 src->mock->focus.json
js
[
    {
        "id":"1",
        "imgUrl":"/images/banner1.jpg"
    },
    {
        "id":"2",
        "imgUrl":"/images/banner2.jpg"
    },
    {
        "id":"3",
        "imgUrl":"/images/banner3.jpg"
    },
    {
        "id":"4",
        "imgUrl":"/images/banner4.jpg"
    }
]
  1. mock中的data数据 src->mock->focus.json
json
[
	{
		"id": "001",
		"name": "家用电器",
		"keywords": ["节能补贴", "4K电视", "空气净化器", "IH电饭煲", "滚筒洗衣机", "电热水器"],
		"imgUrl": "/images/floor-1-1.png",
		"navList": [
			{
				"url": "http://www.atguigu.com",
				"text": "热门"
			},
			{
				"url": "http://www.atguigu.com",
				"text": "大家电"
			},
			{
				"url": "http://www.atguigu.com",
				"text": "生活电器"
			},
			{
				"url": "http://www.atguigu.com",
				"text": "厨房电器"
			},
			{
				"url": "http://www.atguigu.com",
				"text": "应季电器"
			},
			{
				"url": "http://www.atguigu.com",
				"text": "空气/净水"
			},
			{
				"url": "http://www.atguigu.com",
				"text": "高端电器"
			}
		],
		"carouselList": [
			{
				"id": "0011",
				"imgUrl": "/images/floor-1-b01.png"
			},
			{
				"id": "0012",
				"imgUrl": "/images/floor-1-b02.png"
			},
			{
				"id": "0013",
				"imgUrl": "/images/floor-1-b03.png"
			}
		],
		"recommendList": [
			"/images/floor-1-2.png",
			"/images/floor-1-3.png",
			"/images/floor-1-5.png",
			"/images/floor-1-6.png"
		],
		"bigImg": "/images/floor-1-4.png"
	},
	{
		"id": "002",
		"name": "手机通讯",
		"keywords": ["高通骁龙", "16核处理器", "超高性价比", "鸿蒙系统", "以旧换新", "免费升级"],
		"imgUrl": "/images/1_floor-1-1.png",
		"navList": [
			{
				"url": "http://www.atguigu.com",
				"text": "安卓机皇"
			},
			{
				"url": "http://www.atguigu.com",
				"text": "热销爆品"
			},
			{
				"url": "http://www.atguigu.com",
				"text": "性价比之王"
			},
			{
				"url": "http://www.atguigu.com",
				"text": "华为钜惠"
			},
			{
				"url": "http://www.atguigu.com",
				"text": "小米黑科技"
			},
			{
				"url": "http://www.atguigu.com",
				"text": "老人机"
			},
			{
				"url": "http://www.atguigu.com",
				"text": "极致奢华"
			}
		],
		"carouselList": [
			{
				"id": "0011",
				"imgUrl": "/images/1_floor-1-b01.png"
			},
			{
				"id": "0012",
				"imgUrl": "/images/1_floor-1-b02.png"
			},
			{
				"id": "0013",
				"imgUrl": "/images/1_floor-1-b03.png"
			}
		],
		"recommendList": [
			"/images/1_floor-1-2.png",
			"/images/1_floor-1-3.png",
			"/images/1_floor-1-5.png",
			"/images/1_floor-1-6.png"
		],
		"bigImg": "/images/1_floor-1-4.png"
	}
]
  1. 封装mockRequest axios请求 src->request->mockRequest.js
js
import axios from 'axios';
import nprogress from "nprogress";
import "nprogress/nprogress.css";
const mockRequest = axios.create({
	baseURL:"http://mock.com",
	timeout:5000
});
// 请求拦截
mockRequest.interceptors.request.use(config=>{
	nprogress.start();// 开启进度条
	return config;
});
// 响应拦截
mockRequest.interceptors.response.use(response=>{
	nprogress.done();// 结束进度条
	return response.data;// 返回响应体
},(err)=>{
	nprogress.done();// 结束进度条
	alert(err);
	return new Promise(()=>{});// 返回一个状态为pending的Promise痊
});
export default mockRequest;
  1. 把mockRequest请求引入request的入口文件 src->request->index.js
js
import sphRequest from "@/request/sphRequest";
import mockRequest from "@/request/mockRequest";
export {
	// 调用尚品汇相关接口
	sphRequest,
	mockRequest
}
  1. 在页面中使用mockRequest请求 src->pages->Home->FocusAdv->index.vue
javascript
<script>
import { Swiper, SwiperSlide } from 'vue-awesome-swiper'
import {mockRequest} from "@/request";
import 'swiper/css/swiper.css'
export default {
    name: "FocusAdv",
    components: {
        // 将vue-awesome-swiper提供的组件注册为当前组件
        Swiper,
        SwiperSlide
    },
    data() {
        return {
            // 配置项
            swiperOption: {
                // 显示多少屏
                slidesPerView: 1,
                // 屏之间的间隔
                spaceBetween: 1,
                // 是否循环
                loop: false,
                pagination: {
                    // 分页
                    el: '.swiper-pagination',
                    // 点击小圆点是否进行切换
                    clickable: true
                },
                autoplay: {
                    // 切换的间隔时间为3秒
                    delay: 3000,
                    // 如果设置为true,当切换到最后一个slide时停止自动切换
                    stopOnLastSlide: false,
                    // 用户操作swiper之后,是否禁止autoplay。默认为true:停止。
                    // 如果设置为false,用户操作swiper之后自动切换不会停止,每次都会重新启动autoplay。
                    // 操作包括触碰(touch),拖动,点击pagination等。
                    disableOnInteraction: true,
                },
                navigation: {
                    nextEl: '.swiper-button-next',
                    prevEl: '.swiper-button-prev'
                }
            }
        }
    },
    mounted(){
        mockRequest.get("/adv/focus").then(value=>{
            console.log(value.data);
        })
    }
}
</script>
  1. 在项目的入口文件引入mock src->index.js
js
// 导入mock
import "@/mock";

封装API

  1. 封装getfocusList api:src->api->adv.js
js
// 导入mockRequest请求
import { mockRequest } from "@/request";

const getFocusList = function(){
    return mockRequest("/adv/focus");
}

// 暴漏数据
export {
	getFocusList
}
  1. 新建一个adv 的store 数据仓库 src->store->adv.js
js
import {getFocusList} from '@/api/adv';

const state = {
    // 首页轮播图信息
    focusList: []
};

const mutations = {
    SAVE_FOCUS_LIST(state, focusList) {
        state.focusList = focusList;
    }
};

const actions = {
    async getFocusListAsync({ commit }) {
        const { data } = await getFocusList();
        commit("SAVE_FOCUS_LIST", data);
    }
}

export default {
    namespaced: true,
    state,
    mutations,
    actions
}
  1. 导入到store中入口文件中 src->store->index
js
import Vue from 'vue';
import Vuex from 'vuex';
// 导入product模块
import product from "@/store/product/index.js";
import adv from '@/store/adv/index';

// 挂载
Vue.use(Vuex);
// 定义store对象
const store = new Vuex.Store({
    modules:{
        product,
        adv
    }
});
// 导出store对象
export default store;
  1. 在页面中使用vuex store中的数据 src->Home->MainAdv->index.vue,并渲染。
vue
<template lang="">
    <div>
        <swiper class="swiper" :options="swiperOption">
            <swiper-slide v-for="item in focusList" :key="item.id" >
                <img :src="item.imgUrl" />
            </swiper-slide>
            <div class="swiper-pagination" slot="pagination"></div>
            <div class="swiper-button-prev" slot="button-prev"></div>
            <div class="swiper-button-next" slot="button-next"></div>
        </swiper>
    </div>
</template>
<script>
import { Swiper, SwiperSlide } from 'vue-awesome-swiper';
import 'swiper/css/swiper.css';
import { mapState } from 'vuex';
export default {
    name:"MainAdv",
    mounted(){
        this.$store.dispatch('adv/getFocusListAsync');
    },
    computed:{
        ...mapState("adv", ["focusList"])
    },
}
</script>
<style lang="">
    
</style>

增加advStore模块并渲染

  1. 新建src->store->adv.js文件
js
import {getFocusList} from '@/api/adv';

const state = {
    // 首页轮播图信息
    focusList: []
};

const mutations = {
    SAVE_FOCUS_LIST(state, focusList) {
        state.focusList = focusList;
    }
};

const actions = {
    async getFocusListAsync({ commit }) {
        const { data } = await getFocusList();
        commit("SAVE_FOCUS_LIST", data);
    }
}

export default {
    namespaced: true,
    state,
    mutations,
    actions
}
  1. 在入口文件引入adv模块
js
import Vue from 'vue';
import Vuex from 'vuex';
// 导入product模块
import product from "@/store/product/index.js";
import adv from '@/store/adv/index';
import todaysell from './todaysell';

// 挂载
Vue.use(Vuex);
// 定义store对象
const store = new Vuex.Store({
    modules:{
        product,
        adv,
        todaysell
    }
});
// 导出store对象
export default store;
  1. 在页面中使用store中的数据:src->pages->Home->MainAdv->index.vue
vue
<template lang="">
    <div>
        <swiper class="swiper" :options="swiperOption">
            <swiper-slide v-for="item in focusList" :key="item.id" >
                <img :src="item.imgUrl" />
            </swiper-slide>
            <div class="swiper-pagination" slot="pagination"></div>
            <div class="swiper-button-prev" slot="button-prev"></div>
            <div class="swiper-button-next" slot="button-next"></div>
        </swiper>
    </div>
</template>
<script>
import { Swiper, SwiperSlide } from 'vue-awesome-swiper';
import 'swiper/css/swiper.css';
import { mapState } from 'vuex';

export default {
    name:"MainAdv",
    components:{
        Swiper,
        SwiperSlide
    },
    data() {
        return {
            swiperOption: {
                slidesPerView: 1,
                spaceBetween: 30,
                loop: true,
                pagination: {
                    el: '.swiper-pagination',
                    clickable: true
                },
                navigation: {
                    nextEl: '.swiper-button-next',
                    prevEl: '.swiper-button-prev'
                }
            }
        }
    },
    mounted(){
        this.$store.dispatch('adv/getFocusListAsync');
    },
    computed:{
        ...mapState("adv", ["focusList"])
    },
}
</script>

<style lang="less">
.swiper {
    height: 100%;
    img {
        width: 100%;
    }
}
</style>

今日推荐

  1. 设置mock拦截数据:mock->data->todaysell.json
json
[
    {
        "id": "1",
        "imgUrl": "https://kano-1303231448.cos.ap-nanjing.myqcloud.com/hanser/20240718_073546.webp"
    },
    {
        "id": "2",
        "imgUrl": "https://kano-1303231448.cos.ap-nanjing.myqcloud.com/hanser/20240706-DSC03655.webp"
    },
    {
        "id": "3",
        "imgUrl": "https://kano-1303231448.cos.ap-nanjing.myqcloud.com/hanser/20240706-DSC03760.webp"
    },
    {
        "id": "4",
        "imgUrl": "https://kano-1303231448.cos.ap-nanjing.myqcloud.com/hanser/20240706-DSC03707.webp"
    }
]
  1. 设置mock拦截的接口:src->mock->index.js
js
import Mock from "mockjs";

import focusList from "@/mock/data/focus.json";
import todaysell from '@/mock/data/todaysell.json';
import floorList from '@/mock/data/floorList.json';

// 首页轮播图后台数据
Mock.mock("http://127.0.0.1:9090/adv/focus", "get", {
    ok: 200,
    data: focusList
})

// 今日推荐数据
Mock.mock("http://127.0.0.1:9090/today/sell","get",{
    ok: 200,
    data: todaysell
})

// 楼层的数据
Mock.mock("http://127.0.0.1:9090/floorList", "get", {
    ok: 200,
    data: floorList
})
  1. 设置今日推荐store数据:src->store->todaysell->index.js
js
import { getTodaySell } from "@/api/todaysell";

const state = {
    // 今日推荐
    topTodayList: [],
};
const mutations = {
    SAVE_TOP_TODAY_LIST(state, topTodayList) {
        state.topTodayList = topTodayList;
    }
};
const actions = {
    async getTodaySellAsync({ commit }) {
        const { data } = await getTodaySell();
        commit("SAVE_TOP_TODAY_LIST", data);
    }
}
export default {
    namespaced: true,
    state,
    mutations,
    actions
}
  1. 在入口文件中导入:src->store->index.js
js
import todaysell from './todaysell';

// 挂载
Vue.use(Vuex);
// 定义store对象
const store = new Vuex.Store({
    modules:{
        product,
        adv,
        todaysell
    }
});
  1. 在今日推荐的组件页面中使用store数据:src-pages->Home->todaysell->index.vue
vue
<template lang="">
    <!--今日推荐-->
    <div class="today-recommend">
        <div class="py-container">
            <ul class="recommend">
                <li class="clock">
                    <div class="time">
                        <img src="../../../assets/images/home/clock.png" />
                        <h3>今日推荐</h3>
                    </div>
                </li>
                <li v-for="item in topTodayList" :key="item.id" class="banner">
                    <img :src="item.imgUrl" />
                </li>
            </ul>
        </div>
    </div>
</template>

<script>
import {mapState} from 'vuex';
export default {
    name:"TodaySell",
    mounted() {
        this.$store.dispatch("todaysell/getTodaySellAsync")
    },
    computed:{
        ...mapState("todaysell", ["topTodayList"])
    }
}
</script>

<style lang="less" scoped>
    .today-recommend {
        .py-container {
            width: 1200px;
            margin: 0 auto;

            .recommend {
                height: 165px;
                background-color: #eaeaea;
                margin: 10px 0;
                display: flex;

                .clock {
                    width: 16.67%;
                    background-color: #5c5251;
                    color: #fff;
                    font-size: 18px;
                    text-align: center;

                    .time {
                        padding: 30px 0;
                    }

                    h3 {
                        margin: 9px 0;
                        font-weight: 700;
                        font-size: 18px;
                        line-height: 30.06px;
                    }
                }

                .banner {
                    width: 20.83%;

                    img {
                        width: 100%;
                        height: 100%;
                        transition: all 400ms;

                        &:hover {
                            opacity: 0.8;
                        }
                    }
                }
            }
        }
    }
</style>
  1. 在父组件中引入子组件 src->pages->Home->index.vue
vue
<template lang="">
    <div>
        ...
        <!-- 今日推荐 -->
        <TodaySell></TodaySell>
        ...
    </div>
</template>
<script>
export default {
    components:{
        MainAdv,
        TodaySell,
        Floor
    },
}
</script>
<style lang="">
    
</style>

楼层渲染

  1. 设置mock数据 src->mock->data->floorList.json
json
[
    {
        "id": "001",
        "name": "家用电器",
        "keywords": [
            "节能补贴",
            "4K电视",
            "空气净化器",
            "IH电饭煲",
            "滚筒洗衣机",
            "电热水器"
        ],
        "imgUrl": "/images/floor-1-1.png",
        "navList": [
            {
                "url": "https://yuluochenxiao.top",
                "text": "热门"
            },
            {
                "url": "https://yuluochenxiao.top",
                "text": "大家电"
            },
            {
                "url": "https://yuluochenxiao.top",
                "text": "生活电器"
            },
            {
                "url": "https://yuluochenxiao.top",
                "text": "厨房电器"
            },
            {
                "url": "https://yuluochenxiao.top",
                "text": "应季电器"
            },
            {
                "url": "https://yuluochenxiao.top",
                "text": "空气/净水"
            },
            {
                "url": "https://yuluochenxiao.top",
                "text": "高端电器"
            }
        ],
        "carouselList": [
            {
                "id": "0011",
                "imgUrl": "/images/floor-1-b01.png"
            },
            {
                "id": "0012",
                "imgUrl": "/images/floor-1-b02.png"
            },
            {
                "id": "0013",
                "imgUrl": "/images/floor-1-b03.png"
            }
        ],
        "recommendList": [
            "/images/floor-1-2.png",
            "/images/floor-1-3.png",
            "/images/floor-1-5.png",
            "/images/floor-1-6.png"
        ],
        "bigImg": "/images/floor-1-4.png"
    },
    {
        "id": "002",
        "name": "手机通讯",
        "keywords": [
            "高通骁龙",
            "16核处理器",
            "超高性价比",
            "鸿蒙系统",
            "以旧换新",
            "免费升级"
        ],
        "imgUrl": "/images/floor-1-1.png",
        "navList": [
            {
                "url": "https://yuluochenxiao.top",
                "text": "安卓机皇"
            },
            {
                "url": "https://yuluochenxiao.top",
                "text": "热销爆品"
            },
            {
                "url": "https://yuluochenxiao.top",
                "text": "性价比之王"
            },
            {
                "url": "https://yuluochenxiao.top",
                "text": "华为钜惠"
            },
            {
                "url": "https://yuluochenxiao.top",
                "text": "小米黑科技"
            },
            {
                "url": "https://yuluochenxiao.top",
                "text": "老人机"
            },
            {
                "url": "https://yuluochenxiao.top",
                "text": "极致奢华"
            }
        ],
        "carouselList": [
            {
                "id": "0011",
                "imgUrl": "/images/floor-1-b01.png"
            },
            {
                "id": "0012",
                "imgUrl": "/images/floor-1-b02.png"
            },
            {
                "id": "0013",
                "imgUrl": "/images/floor-1-b03.png"
            }
        ],
        "recommendList": [
            "/images/floor-1-2.png",
            "/images/floor-1-3.png",
            "/images/floor-1-5.png",
            "/images/floor-1-6.png"
        ],
        "bigImg": "/images/floor-1-4.png"
    }
]
  1. 封装楼层api接口getFloorList src->api->product.js
js
// 导入axios
import { sphRequest, mockRequest} from "@/request";
// 获取分类列表数据
const getBaseCategoryList = function(){
    return sphRequest("/product/getBaseCategoryList");
}
// 获取楼层数据
const getFloorList = ()=>{
    return mockRequest("/floorList")
}
// 暴漏数据
export { 
    getBaseCategoryList,
    getFloorList
}
  1. 新建楼层的store数据仓库 src->store->product.js
js
// 定义商品的数据状态
const state = {
    // 首页分类列表
    categoryList:[],
    // 楼层数据状态
    floorList:[],
}
// 定义mutations
const mutations = {
    // 修改state中的首页分类列表
    UP_CATEGORY_LIST(state,categoryList){
        state.categoryList = categoryList
    },
    // 修改state中的楼层列表
    SAVE_FLOOR_LIST(state, floorList) {
        state.floorList = floorList;
    }
}
// 定义actions
const actions = {
    // 使用api接口获取数据
    async getBaseCategoryListAsync({commit},num=1){
        const { data } = await getBaseCategoryList();
        commit("UP_CATEGORY_LIST",data.splice(0,num));
    },
    // FloorList获取数据
    async getFloorListAsync({ commit }) {
        const { data } = await getFloorList();
        commit("SAVE_FLOOR_LIST", data);
    }
}
// 暴漏数据,导出模块
export default {
    namespaced:true,
    state,
    mutations,
    actions,
}
  1. 新建楼层组件 src->pages->Home->Floor->index.vue
vue
<template lang="">
    <div class="floor">
        <div class="py-container">
            <div class="title clearfix">
                <h3 class="fl">{{floorInfo.name}}</h3>
                <div class="fr">
                    <ul class="nav-tabs clearfix">
                        <li class="{active:index===0}" v-for="(item,index) in floorInfo.navList" :key="index">
                            <a :href="item.url" data-toggle="tab">{{item.text}}</a>
                        </li>
                    </ul>
                </div>
            </div>
            <div class="tab-content">
                <div class="tab-pane">
                    <div class="floor-1">
                        <div class="blockgary">
                            <ul class="jd-list">
                                <li v-for="(item,index) in floorInfo.keywords" :key="index" >{{item}}</li>
                            </ul>
                            <img :src="floorInfo.imgUrl" />
                        </div>
                        <!-- swiper轮播图 -->
                        <div class="floorBanner">
                            <swiper class="swiper" :options="swiperOption">
                                <swiper-slide v-for="item in floorInfo.carouselList" :key="item.id">
                                    <img :src="item.imgUrl" />
                                </swiper-slide>
                                <div class="swiper-pagination" slot="pagination"></div>
                                <div class="swiper-button-prev" slot="button-prev"></div>
                                <div class="swiper-button-next" slot="button-next"></div>
                            </swiper>
                        </div>
                        <div class="split">
                            <span class="floor-x-line"></span>
                            <div class="floor-conver-pit">
                                <img :src="floorInfo.recommendList[0]" />
                            </div>
                            <div class="floor-conver-pit">
                                <img :src="floorInfo.recommendList[1]" />
                            </div>
                        </div>
                        <div class="split center">
                            <img :src="floorInfo.bigImg" />
                        </div>
                        <div class="split">
                            <span class="floor-x-line"></span>
                            <div class="floor-conver-pit">
                                <img :src="floorInfo.recommendList[2]" />
                            </div>
                            <div class="floor-conver-pit">
                                <img :src="floorInfo.recommendList[3]" />
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>
<script>
import { Swiper, SwiperSlide } from 'vue-awesome-swiper'
import 'swiper/css/swiper.css'

export default {
    name:"Floor",
    // 父向子传值
    props: ["floorInfo"],
    computed:{
        
    },
    components: {
        Swiper,
        SwiperSlide
    },
    data() {
        return {
            swiperOption: {
                slidesPerView: 1,
                spaceBetween: 30,
                loop: true,
                pagination: {
                    el: '.swiper-pagination',
                    clickable: true
                },
                navigation: {
                    nextEl: '.swiper-button-next',
                    prevEl: '.swiper-button-prev'
                }
            }
        }
    }

}
</script>
<style lang="less" scoped>

</style>
  1. Home页面中引入子组件 Floor
vue
<template lang="">
    <div>
        <!--楼层-->
        <Floor v-for="item in floorList" :key="item.id" :floorInfo="item"></Floor>
    </div>
</template>
<script>
import Floor from '@/pages/Home/Floor';
export default {
    components:{
        MainAdv,
        TodaySell,
        Floor
    },
}
</script>
<style lang="">
    
</style>

商品排行

接口一次性将所有排行的数据进行返回

  1. 新建mock数据 src->mock->data->rank.json
json
[...]
  1. 新增mock拦截请求 src->mock->index.js
js
import rankList from '@/mock/data/rank.json';
// 排行的数据
Mock.mock("http://127.0.0.1:9090/rankList", "get", {
    ok: 200,
    data: rankList
})
  1. 封装排行api getRankList :src->api->product.js
js
// 获取rank数据
const getRankList = () =>{
    return mockRequest("/rankList")
}
// 暴漏数据
export { 
    getBaseCategoryList,
    getFloorList,
    getRankList
}
  1. 封装 rankList store数据仓库 src->store->product.js
js
// 导入api
import { getBaseCategoryList, getFloorList, getRankList } from '@/api/product';

// 定义商品的数据状态
const state = {
    // 首页分类列表
    categoryList:[],
    // 楼层数据状态
    floorList:[],
    // rank的数据仓库
    rankList:[],
}
// 定义mutations
const mutations = {
    // 修改state中的首页分类列表
    UP_CATEGORY_LIST(state,categoryList){
        state.categoryList = categoryList
    },
    // 修改state中的楼层列表
    SAVE_FLOOR_LIST(state, floorList) {
        state.floorList = floorList;
    },
    // 修改rank中的数据
    SAVE_RANK_LIST(state, rankList) {
        state.rankList = rankList;
    }
}
// 定义actions
const actions = {
    // 使用api接口获取数据
    async getBaseCategoryListAsync({commit},num=1){
        const { data } = await getBaseCategoryList();
        commit("UP_CATEGORY_LIST",data.splice(0,num));
    },
    // FloorList获取数据
    async getFloorListAsync({ commit }) {
        const { data } = await getFloorList();
        commit("SAVE_FLOOR_LIST", data);
    },
    // 使用api获取rankList说几句
    async getRankListAsync({ commit }) {
        const { data } = await getRankList();
        commit("SAVE_RANK_LIST", data);
    }
}
// 暴漏数据,导出模块
export default {
    namespaced:true,
    state,
    mutations,
    actions,
}
  1. 新建rank组件:src->pages->Home->RankList->index.vue
vue
<template lang="">
    <div>
        <div class="rank">
            <div class="tab">
                <div class="tab-tit clearfix">
                    <a v-for="(item,index) in rankList" 
                    :key="item.id" 
                    @click="activeIndex=index" 
                    href="javascript:;" 
                    :class="{on:activeIndex===index}">
                        <p class="img">
                            <i></i>
                        </p>
                        <p class="text">{{item.typeName}}</p>
                    </a>
                </div>

            </div>
            <div class="content" v-for="(item,index) in rankList" :key="item.id" v-show="activeIndex===index">
                <ul>
                    <li>
                        <div v-for="info in item.productList" :key="info.id" class="img-item">
                            <p class="tab-pic">
                                <a href="#">
                                    <img :src="info.imgUrl" />
                                </a>
                            </p>
                            <div class="tab-info">
                                <div class="info-title">
                                    <a href="#">{{info.name}}</a>
                                </div>
                                <p class="info-price">定金:{{info.price}}</p>
                            </div>
                        </div>
                    </li>
                </ul>
            </div>
        </div>
    </div>
</template>
<script>
import { mapState } from "vuex";
export default {
    name:'RankList',
    data(){
        return {
            activeIndex: 0
        }
    },
    computed:{
        ...mapState("product",["rankList"])
    },
    mounted() {
        this.$store.dispatch("product/getRankListAsync");
    }
}
</script>
<style lang="less" scoped>

</style>
  1. 在父组件中引入子组件 src->Home->index.vue
vue
<template lang="">
    <div>
        
    </div>
</template>
<script>
import RankList from '@/pages/Home/RankList';
export default {
    components:{
        MainAdv,
        TodaySell,
        Floor,
        RankList
    },
}
</script>
<style lang="">
    
</style>

商品排行方式2:使用不同的接口获取不同的排行

js
1. 构建mock数据:src->mock->rank->tejia.json,src->mock->rank->xinpin.json,src->mock->rank->remai.json
2. 创建mock拦截请求:src->mock->index.js
// 热卖排行
Mock.mock("http://mock.com/product/remai","get",{
	ok:200,
	data:remai
});
// 新品排行
Mock.mock("http://mock.com/product/xinpin","get",{
	ok:200,
	data:xinpin
});
// 特价排行
Mock.mock("http://mock.com/product/tejia","get",{
	ok:200,
	data:tejia
});
3. 创建api接口:getRemaiList getTeJiaList getXinPinList
export const getRemaiList = ()=>mockRequest("/product/remai");
export const getTeJiaList = ()=>mockRequest("/product/tejia");
export const getXinPinList = ()=>mockRequest("/product/xinpin");
4. 创建store数据仓库 src->store->product.js
import {getBaseCategoryList, getFloorList, getRankList, getRemaiList, getTeJiaList, getXinPinList} from "@/api/product";

const state = {
	rankList2:[],// 第二种方案
}
const mutations = {
	SAVE_RANK_LIST2(state,rankList){
		state.rankList2 = rankList;
	}
}
const actions = {
	async getRankList2Async({commit},type){
		// type:0-remai  1- tejia  2-xinpin
		let data;
		if(type === 0)
			({data} = await getRemaiList());
		else if(type === 1)
			({data} = await getTeJiaList());
		else
			({data} = await getXinPinList());
		commit("SAVE_RANK_LIST2",data);
	},
}
export default {
	namespaced:true,
	state,
	mutations,
	actions
}
5. 创建rankList.vue组件
<template lang="">
    <div>
        ...
			<div class="tab-tit clearfix">
                <a @click="type=0" href="javascript:;" :class="{on:type===0}">
                    <p class="img">
                        <i></i>
                    </p>
                    <p class="text">热卖排行</p>
                </a>
                <a @click="type=1" href="javascript:;" :class="{on:type===1}">
                    <p class="img">
                        <i></i>
                    </p>
                    <p class="text">特价排行</p>
                </a>
                <a @click="type=2" href="javascript:;" :class="{on:type===2}">
                    <p class="img">
                        <i></i>
                    </p>
                    <p class="text">新品排行</p>
                </a>
            </div>
		...
    </div>
</template>
<script>
export default {
    name: "RankList",
    data(){
        return {
            type:0
        }
    },
    watch:{
        type:{
            handler(){
                this.$store.dispatch("product/getRankList2Async",this.type)
            },
            immediate:true
        }
    }
}
</script>
<style lang="">
    
</style>
6. 父组件引入子组件