vue.js|尚品汇学习笔记

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'})

【vue.js|尚品汇学习笔记】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.vuex5.在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.面包屑处理品牌信息

      推荐阅读