尚品汇学习笔记

1.设置@为src文件,便于在之后引入时方便操作。

        在根目录下建立jsconfig.json文件,将如下代码放进去。

{
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "@/*": ["src/*"]
    }
  },
  "exclude": ["node_modules", "dist"]
}

2.拆解页面结构

        将Header和Footer这种始终非路由组件,放在components文件夹下,建立相应文件夹,内部装有Header.vue文件。

        至于路由组件,在src文件夹下建立pages文件夹,将对应路由组件放在此文件夹下。

3.搭建组件的注意点

        1.在移动代码时,要注意样式和图片。关于样式,因为本项目利用less书写样式,在引入时,加上lang="less"。对于图片,在相应文件夹下建立images文件夹。

        2.不要忘记将reset.css,移动到public文件夹下,并引入,才能此样式生效。

4.实现Footer组件只在Home和Search组件下显示        

//利用route中的meat属性 { path:'/home', component:Home, meta:{show:true} }

5.编程式路由导航

        定义一个方法,使用push,如果需要传参,在后面加相应参数即可(params记得占位)

this.$router.push({
    name:'search',
    params:{keyword:'你好啊'}
    query:{k:this.keyword}    // query传参是键值对的形式
})

6.如何指定params参数可传可不传,如果传递空串如何解决?

        在配置路由时,在占位的后面加上一个问号即可实现。

path:'/search/:keyword?'

        传递空串的时候,用undefined解决问题。

this.$router.push({
    name:'search',
    params:{keyword:'' || undefined}
})

7.多次点击按钮传参会报错,如何解决?

        重写push方法,如果resolve或者reject没有回调函数,则添加一个回调即可。

import VueRouter from 'vue-router'
//重写push方法
let originPush = VueRouter.prototype.push
VueRouter.prototype.push = function(location,resolve,reject){
    if(resolve && reject){
        originPush.call(this,location,resolve,reject)
    } else{
        originPush.call(this,location,()=>{},()=>{})
    }
}

8.三级联动组件声明为全局组件

//全局注册组建
import TypeNav from '@/components/TypeNav'
//  传递两个参数(组件的名字,哪一个组件)
Vue.component(TypeNav.name,TypeNav)

        全局组件注册完成后,在引入时不需要import和声明components

9.对于axios进行二次封装

        在src文件夹下建立api文件夹,创建request.js文件。

        1.设置baseURL和timeout

import axios from "axios";
const requests = axios.create({
    baseURL:'/api',
    timeout:5000    //访问大于5s取消
})

        2.设置请求拦截器和响应拦截器。

//引入nprogress
import nprogress from "nprogress";
//引入nprogress样式
import 'nprogress/nprogress.css'

//设置请求拦截器,并且加上进度条。
requests.interceptors.request.use(function (config) {
    nprogress.start()
    return config;
}, function (error) {
    return Promise.reject(error);
});

// 设置响应拦截器
requests.interceptors.response.use(function (response) {
    nprogress.done()
    return response.data;
}, function (error) {
    return Promise.reject(error);
});

10.API接口统一管理

        在api文件夹下建立index.js

import requests from './request'
//三级联动axios发请求
export const reqCategoryList = () => 
    requests({ url: 'product/getBaseCategoryList', method: 'get' })

        设置访问接口函数后,在main.js中引入

//  引入reqCategoryList
import {reqCategoryList} from  '@/api'

        进行跨域代理操作,实现本地接口与目标接口连接

//配置代理跨域
    devServer: {
        proxy: {
            "/api": {
                target: "http://39.98.123.211",
            },
        },
    },

11.使用vuex完成三级联动

        1.如果项目过大,state内数据过多不便查看,可以在store文件夹下设置小库再引入。引入时不再引入state,而是引入modules:{home,search}

//创建action,mutation,state,getter对象
const state = {
    a:1
}
const actions = {}
const mutations = {}
const getters = {}

export default {
    actions,
    mutations,
    state,
    getters
}
import Vue from 'vue'
import Vuex from 'vuex'
//引入小仓库
import home from './home'
import search from './search'
Vue.use(Vuex)
export default new Vuex.Store({
    actions,
    mutations,
    modules: {
        home,
        search,
    },
    getters
})

        2.配置vuex,在home.js中完成请求数据。

//引入请求数据的函数,分别暴露一定要记得加{}
import {reqCategoryList} from  '@/api'
//创建action,mutation,state,getter对象
const state = {
    categoryList:[]
}
const actions = {
//reqCategoryList()请求回来是一个Promise函数,所以用async和await
    async categoryList({commit}){
       let result =  await reqCategoryList()    
       if(result.code == 200){    //如果请求成功,result.code就是200
           commit('CATEGORYLIST',result.data)    
       }
    }
}
const mutations = {
    CATEGORYLIST(state,categoryList){
        state.categoryList = categoryList    //将数据交给state
    }
}
const getters = {
    
}

export default {
    actions,
    mutations,
    state,
    getters
}

        3.在TypeNav中配置计算属性,再在页面中使用。

import { mapState } from "vuex"; //引入mapState要记得加{}!烦死了
 computed: {
    ...mapState({
      categoryList: (state) => state.home.categoryList,
    }),    //vuex会传递state到函数中,得到下面小库中的内容即可
  },

        4.对于1级、2级、3级标题进行遍历,得到所有标题

v-for="(c1, index) in categoryList" :key="c1.categoryId"
v-for="(c2, index) in c1.categoryChild" :key="c2.categoryId"
v-for="(c3, index) in c2.categoryChild" :key="c3.categoryId"

        5.利用js设置hover效果:对于h标题添加@mouseenter和@mouseleave事件传递参数(index),设置this.currentIndex = index;(记得设置初始currentIndex = -1)

  为父盒子div设置动态类:class="{ cur: currentIndex == index }"

  最后设置cur的backgroudcolor,即可实现hover效果。

       6.利用js实现2、3级标题的显示与隐藏:为2、3级标题设置动态style。

:style="{display:currentIndex == index? 'block' : 'none'}"

12.点击商品分类,给search传递参数

        1.首先将每个a标签上都添加一个统一的自定义属性名,便于我们判断是否点击的是a标签。

:data-categoryName="c2.categoryName"

        2.将每个a标签上也添加一个各自的id属性,便于我们判断点击的是几级标签。 

:data-category2Id = "c2.categoryId"

        3.给div加上goSearch方法,配置方法,注意在传递参数的时候要将params参数和query参数都考虑在内。

goSearch(event) {
      //  得到点击事件的标签,比如点了h1标签a就是h1标签。
      let a = event.target;
      //  通过解构赋值得到自定义属性值,属性值都是小写的,这里要注意。
      let {categoryname,category1id,category2id,category3id} = a.dataset
      //因为点击的a标签上都有categoryname,所以做if判断
      if(categoryname){
        //  准备需要传参的数据
        let location = {name:'search'}
        let query = {categoryName:categoryname}
        //  如果是点击1级标题就带一级id,之后同理
        if(category1id){
          query.category1Id = category1id
        } else if(category2id){
          query.category2Id = category2id
        }else if(category3id){
          query.category3Id = category3id
        }
        location.query = query
        //判断一下有没有params参数,如果有也放在location里
        if(this.$route.params){
          location.params = this.$route.params
        }
        //  路由跳转
        this.$router.push(location)
      }
    },

13.search页面显示TypeNav组件

        1.在search组件中,加上,但是加上之后是始终显示的,显然不符合我们的要求。

        2.对TypeNav中的商品列表进行显示与隐藏。给父级div添加v-show,让show初始为true

        3.进行路由跳转地址判断,如果跳到其他组件,就让show变为false

mounted() {
    if(this.$route.path !='/home'){
      this.show = false
    }

        4.但是这样就一直隐藏了,不能让他显示出来了,所以我们要对大盒子添加@mouseenter和@mouseleave方法。

    leaveShow() {
      this.currentIndex = -1;
      if (this.$route.path != "/home") {
        this.show = false;
      }
    },
    enterShow() {
      this.show = true;
    },

14.给商品列表添加过度效果

        1.用transition将div包住,并给一个name属性

        2.在style中,配置过度属性。

    .sort-enter {
      height: 0;
    }
    .sort-enter-to {
      height: 461px;
    }
    .sort-enter-active {
      transition: all 0.2s linear;
    }

15.用mock.js模拟数据,在ListContainer中显示

        1.在src下新建mock文件夹,里面新建banner.json floor.json,将数据粘贴进去。因为需要用到images,所以在public中新建images文件夹,将需要的图片拷贝进去。

        2.在mock文件夹下新建mockServe.js,配置mock。

import Mock from 'mockjs'
//引入banner和floor的json文件
//因为webpack对于图片是默认暴露的。
import banner from './banner.json'
import floor from './floor.json'
//使用mock来模拟接口,第一个参数是请求的接口,第二个参数是请求的数据。
Mock.mock('/mock/banner',{code:200,data:banner})
Mock.mock('/mock/floor',{code:200,data:floor})

        3.在main.js中引入mockServe,接收参数

import '@/mock/mockServe'

        4.封装mock,在api下新建mockAjax.js 

import axios from "axios";
//引入nprogress,这是加载条
import nprogress from "nprogress";
//引入nprogress样式
import 'nprogress/nprogress.css'
const mockAjax = axios.create({
    baseURL:'/mock',
    timeout:5000    //访问大于5s取消
})

//设置请求拦截器
mockAjax.interceptors.request.use(function (config) {
    nprogress.start()
    return config;
}, function (error) {
    return Promise.reject(error);
});

// 设置响应拦截器
mockAjax.interceptors.response.use(function (response) {
    nprogress.done()
    return response.data;
    // return response;
}, function (error) {
    return Promise.reject(error);
});

export default mockAjax

        5.在index.js文件中,配置mock发送请求接口

//mock数据的axios发请求
import mockAjax from './mockAjax'
export const reqBannerList = () => mockAjax({url:'/banner'})
export const reqFloorList = () => mockAjax({url:'/floor'})

        6.在ListContainer中的mounted中发送dispatch。

mounted() {

//发送获取bannerList的请求

this.$store.dispatch("getBannerList");

},

        vuex store中引入reqBannerList,配置action和mutation

import {reqBannerList,reqFloorList} from  '@/api'    //引入请求接口函数
const state = {
    bannerList:[],
    floorList:[],
}
const actions = {
    async getBannerList({commit}){
        let result =  await reqBannerList()
        if(result.code == 200){
            commit('BANNERLIST',result.data)
        }
    },
    async getFloorList({commit}){
        let result =  await reqFloorList()
        if(result.code == 200){
            commit('FLOORLIST',result.data)
        }
    },
}
const mutations = {
    BANNERLIST(state,bannerList) {
        state.bannerList = bannerList
    },
    FLOORLIST(state,floorList) {
        state.floorList = floorList
    }
}

        7.在组件中配置mapState,在banner处遍历就行了。

16.使用Swiper完成轮播图的制作

        1.安装swiper,最好安装5代比较稳定

        2.在main.js中引入swiper.css和swiper.js

import 'swiper/css/swiper.css'                import 'swiper/js/swiper.js'

        3.在组件中引入Swiper

import Swiper from "swiper"; 

        4.初始化swiper(dom中需要有swiper的盒子才可以进行初始化) 

          var mySwiper = new Swiper(".swiper-container", {
            loop: true, // 循环模式选项

            // 如果需要分页器
            pagination: {
              el: ".swiper-pagination",
              clickable:true    //点击小圆点
            },

            // 如果需要前进后退按钮
            navigation: {
              nextEl: ".swiper-button-next",
              prevEl: ".swiper-button-prev",
            },
          });

        5.初始化swiper的位置非常的关键。如果在mounted中初始化,会导致没有反应,因为会在获取到数据之前初始化。

        如果在watch中监听bannerList的变化,这样也不行。虽然这样bannerList一定是获取到数据了,但是由于v-for是异步操作,所以不能保证v-for已经完成。

        所以我们使用watch+nextTick,让dom循环结束后执行该初始化swiper

  watch: {
    bannerList: {
      handler(newValue, oldValue) {
        this.$nextTick(() => {
          var mySwiper.....
        });
      },
    },
  },

17.利用v-for生成多组floor组件

        在Floor组件中进行遍历。

18.制作Search组件

 1.静态页面

        1.发请求:在api下的index.js中,添加获取search请求,post请求要传入一个对象进去。

export const reqSearchList = (params) => requests({url:'/list',method:'post',data:params})

        2.完成vuex中的search.js,注意这里我们要传一个对象进来。

//引入请求数据的函数
import {reqSearchList} from  '@/api'
//创建action,mutation,state,getter对象
const state = {
    searchList:[],
}
const actions = {
    async getSearchList({commit},params = {}){
        let result =  await reqSearchList(params)
        if(result.code == 200){
            commit('SEARCHLIST',result.data)
        }
    },
}
const mutations = {
    SEARCHLIST(state,searchList) {
        state.searchList = searchList
    },
}
const getters = {
    goodsList(state){
        return state.searchList.goodsList || [] //如果没有访问成功,择返回空数组
    },
    attrsList(state){
        return state.searchList.attrsList || []
    },
    trademarkList(state){
        return state.searchList.trademarkList || []
    },
}

export default {
    actions,
    mutations,
    state,
    getters
}

         3.利用计算属性配置getters。

computed: {
    ...mapGetters(["goodsList", "trademarkList", "attrsList"]),
  },

        4.在页面中遍历显示,商品直接在search组件下即可,其他两个在子组件中,所以需要传参

2.vuex 

        5.在search组件中提交dispatch,但是这里要注意,因为我们要获取多次数据,但是如果像之前一样写在mounted中,就只能获取一次数据,所以我们将dispatch写为一个方法,在mounted中调用这个方法,即可开始就获取数据。(actions和mutations都在上面)

methods: {
    getSearch() {
      this.$store.dispatch("getSearchList", this.parameter);
    },
mounted() {
    this.getSearch();
  },

        6.如上可知,我们穿进去的对象,还没有配置,所以我们要在data中配置parameter对象,对象的内容就是获取过来的10个属性。

data() {
    return {
      parameter: {
        category1Id: "",
        category2Id: "",
        category3Id: "",
        categoryName: "",
        keyword: "",
        order: "",
        pageNo: 1,
        pageSize: 10,
        props: [],
        trademark: "",
      },
    };
  },

        7.如果直接把他们传进去,其实是没用的,因为他们都是初始值,根本不知到点了什么id,name,keyword。所以我们要把params和query参数都给合并到此对象中。用Object.assign。

        那么这个合并过程要在什么位置发生呢?mounted显然不行,已经挂载了。created也不行,因为这里需要用到data中的数据,所以只能在beforeMount上。

beforeMount() {
    //将query和params的参数合并到parameter参数中
    Object.assign(this.parameter, this.$route.query, this.$route.params);
  },

3.通过watch监听后发送请求 

        8.这样我们的parameter中就有了最新的参数,但是我门如何实现,点击不同内容跳转不同的数据内容呢?显然一次获取数据已经不够了,所以我们要多次获取。那么怎么能多次获取呢?用watch监听最合适不过了,一旦我们的$route变化,甭管是params还是query,只要变化都可以监听到,然后发送请求就行了。但是获取完要把数据清空,不然会带有无用数据。

watch: {
    $route(newValue, oldValue) {
      //将参数合并进来,将后两个合并到第一个。
      Object.assign(this.parameter, this.$route.query, this.$route.params);
      //只要参数变化,就获取数据
      this.getSearch();
      //但是访问2id的时候也会把3id带过去,不太好,所以先把id清空。
      this.parameter.category1Id = "";
      this.parameter.category2Id = "";
      this.parameter.category3Id = "";
    },
  },

4.面包屑的删除与添加 

        9.现在我们已经能实现点击哪跳转到新页面了,但是对于面包屑的处理我们还没有做。首先第一个就是点什么显示什么,也就是parameter中只要有categoryName或者keyword就会显示,所以使用v-if或v-show(因为切换次数会比较多所以选择v-show)。

  • {{ parameter.categoryName }}
  • {{ parameter.keyword }}
  •         10.接下来就是面包屑的删除功能,点击叉实现删除。就是对i标签设置一个点击事件,将categoryName清空就行,但是这里注意,如果给一个 ' ',还是会传过去,占有内存,所以我们给一个undefined,这样就不会传过去。

            其他的id也要设置为undefined,因为是无用的数据

            这样我们实现了点击删除,但是地址栏里的数据还是存在的,所以我们要进行新的路由跳转,先判断有没有params参数,如果有也带上。

    //删除分类名称
        deleteParameter() {
          this.parameter.categoryName = undefined;
          //同上,清id,这里可以直接设置为undefined,这样就不会将没用的数据获取过来。
          this.parameter.category1Id = undefined;
          this.parameter.category2Id = undefined;
          this.parameter.category3Id = undefined;
          this.getSearch();
          //路由跳转
          if(this.$route.params){
            this.$router.push({"name":"search",params:this.$route.params})
          }
        },

            11.删除关键字的方法和上述方法基本一致,只不过我门在清楚关键字的时候,要将输入框的内容也给清除。因为输入框的内容是在Header组件中,和Search组件是兄弟组件,所以使用全局事件总线$bus最好。在main.js中设置全局事件总线。

    beforeCreate() {
    		Vue.prototype.$bus = this //安装全局事件总线
    	},

            在Search中$emit,在Header中$on就行了。

    5.面包屑处理品牌信息

            

    你可能感兴趣的:(vue.js,前端,javascript)