前端电商购物网站PC端案例的一些问题

网站首页与布局

 

网站首页分为 3 个部分,顶部导航栏,底部网站信息栏,正文部分。 

要求一:不同导航菜单底部网站信息栏显示与隐藏。

 解决方法:路由元信息 meta 字段

新增路由元信息 meta 字段 show

{
      path: '/',
      name: 'Home',
      component: Home,
      meta:{show:true}  // 添加路由元信息meta 字段
    },
    {
      path: '/register',
      name: 'Register',
      component: Register,
      meta:{show:false}
    },

App.vue,在底部网站信息栏 通过  v-show="$route.meta.show" 控制。

<template>
  <div id="app">
    <Header />
    <router-view />
    <Footer v-show="$route.meta.show" />
  </div>
</template>

要求二:点击商品分类跳转到分类页

这个组件采用的是 element ui 的导航。

商品分类组件  api =>  vuex => 组件中派发action =>  从仓库中取数据

1、请求api

export const reqCategoryList = ()=> {
    return requests.get('/product/getBaseCategoryList')
}

2、vuex 中请求(因为需要存储和公用的数据太多,因此在大仓库中建立了小仓库)

在大仓库中建立了小仓库

 home 小仓库中写请求

// home 小仓库
import {reqCategoryList} from '@/api'
export default {
    state: {categoryList:[], },
    mutations: {
        CATEGORYLIST(state,categoryList){ state.categoryList = categoryList}
    },
    actions: {
        async categoryList({commit}){
           let res = await reqCategoryList() // 因为 axios 返回的是一个promise 所以这里用 await 这样返回的结果就是对象
           if (res.status == 200) { commit("CATEGORYLIST",res.data.data)}
        }
    },

3、根组件中派发 action,在根组件APP.vue 执行一次是为了减少请求次数。

mounted(){
    this.$store.dispatch("categoryList");  // 获取三级分类 typeNav
  }

 4、获取到的数据在仓库,然后在组件中使用。

computed: {
    ...mapState({
      categoryList: (state) => {
        return state.home.categoryList;
      },
    }),
  },

点击每个分类会跳转到该分类的商品页

1、不能使用声明式导航,roter-link 相当于VueComponent类的实例对象,一瞬间new VueComponent很多实例(1000+),很消耗内存,因此导致卡顿会出现卡顿(分类较多)

2、不能直接使用编程式导航,原因每一个导航都有一个回调函数,太多了

解决:在父节点上使用 事件委派 + 编程式导航

 其他常见问题

描述: 编程式路由跳转到当前路由(参数不变), 会抛出NavigationDuplicated的警告错误

vue-router3.1.0之后, 引入了push()的promise的语法, 如果没有通过参数指定回调函数就返回一个promise来指定成功/失败的回调, 且内部会判断如果要跳转的路径和参数都没有变化, 会抛出一个失败的promise

解决1: 在跳转时指定成功或失败的回调函数, 通过catch处理错误

// catch()处理错误
this.$router.push(`/search/${this.keyword}`).catch(() => {})
// 指定成功的回调函数
this.$router.push(`/search/${this.keyword}`, () => {})
// 指定失败的回调函数
this.$router.push(`/search/${this.keyword}`, undefined, () => {})

解决2: 修正Vue原型上的push和replace方法

let originreplace = VueRouter.prototype.replace
VueRouter.prototype.replace = function(location,resolve,reject){
  if(resolve && reject){
    originreplace.call(this,location,resolve,reject)
  }else{
    originreplace.call(this,location,()=>{},()=>{})
  }
}

如何指定params参数可传可不传?

path: '/search/:keyword?'

 指定params参数时可不可以用pathparams配置的组合?

不可以,用path和params配置的组合, 只能用name和params配置的组合query配置可以与path或name进行组合使用。

如果指定nameparams配置, params中数据是一个"", 无法跳转

解决1: 不指定params

解决2: 指定params参数值为undefined

路由组件能不能传递props数据?

可以: 可以将query或且params参数映射/转换成props传递给路由组件对象

props: (route)=>({keyword1:route.params.keyword, keyword2: route.query.keyword })

params参数:路由需要占位,程序就崩了,属于URL当中一部分
query参数:路由不需要占位,写法类似于ajax当中query参数 

导航守卫是什么?

导航守卫是vue-router提供的下面2个方面的功能:

        a. 监视路由跳转   -->回调函数

        b. 控制路由跳转

应用:

        a. 在跳转到界面前, 进行用户权限检查限制(如是否已登陆)

        b. 在界面离开前, 做收尾工作

导航守卫分类

全局守卫: 针对任意路由跳转;

a. 全局前置守卫;在准备跳转到某个路由组件之前 (在开发中用的比较多)

router.beforeEach((to, from, next) => {// before enter each route component
          
        })

to: 目标route

from: 起始route

next: 函数

                next(): 执行下一个守卫回调, 如果没有跳转到目标路由

                next(false)/不执行: 跳转流程在当前处中断, 不会跳转到目标路由组件

                next(path): 跳转到指定的另一个路由

 b. 全局后置守卫;在跳转到某个路由组件之后

router.afterEach((to, from) => {
          
        })

路由独享的守卫:前置守卫

beforeEnter: (to, from, next) => {
	        
	    }

组件守卫: 只针对当前组件的路由跳转;a. 进入  b. 更新  c. 离开

在当前组件对象被创建前调用, 不能直接访问this(不是组件对象)。但可以通过next(component => {}), 在回调函数中访问组件对象;

 beforeRouteEnter (to, from, next) {
          next(component => {})
        },

当前组件对象将要更新前调用, 可以访问this

 beforeRouteUpdate (to, from, next) {
          
        },

在当前组件离开前调用, 可以访问this

beforeRouteLeave (to, from, next) {
          next()
        }

只有登录了,才可以才能查看交易/支付/个人中心界面

// 所有需要检查登陆的路由路径的数组
const checkPaths = ['/trade', '/pay', '/center']
/* 注册全局前置拦截器 */
router.beforeEach((to, from, next) => {
  const targetPath = to.path
  if (checkPaths.some(path => targetPath.indexOf(path)===0)) {
    if (!store.state.user.userInfo.token) {
      return next(`/login?redirect=${targetPath}`)
    }
  }
  next()
})

只有没有登陆,才能查看登陆界面

path: '/login',
  component: Login,
  beforeEnter(to, from, next) {
    if (store.state.user.userInfo.token) {// 如果已登陆, 直播跳转到首页
      next('/')
    } else {
      next()
    }
  }
}

只有携带的skuNum以及sessionStorage中有skuInfo数据, 才能查看添加购物车成功的界面

{
  path: '/addcartsuccess',
  component: AddCartSuccess,
  props: route => route.query,
  beforeEnter: (to, from, next) => {
    const {skuId, skuNum} = to.query
    const skuInfo = JSON.parse(window.sessionStorage.getItem('SKU_INFO'))
    if (skuNum>0 && skuInfo && skuInfo.id) {
      next()
    } else {
      next('/')
    }
  }
},

只能从购物车界面, 才能跳转到交易界面

{
  path: '/trade',
  component: Trade,
  beforeEnter: (to, from, next) => {
    if (from.path!=='/shopcart') {
      next({path: '/shopcart'})
    } else {
      next()
    }
  }
}

只能从交易界面, 才能跳转到支付界面

{
  path: '/pay',
  component: Pay,
  beforeEnter: (to, from, next) => {
    if (from.path!=='/trade') {
      next({path: '/trade'})
    } else {
      next()
    }
  }
},

只有从支付界面, 才能跳转到支付成功的界面

export default {
  name: 'PaySuccess',
  beforeRouteEnter: (to, from, next) => {
    if (from.path!=='/pay') {
      next({path: '/pay'})
    } else {
      next()
    }
  }
}

图片懒加载


1. 图片懒加载特点说明
(1)    还没有加载得到目标图片时, 先显示loading图片
(2)    在<img>进入可视范围才加载请求目标图片

2. 下载依赖
npm install vue-lazyload

3.引入并配置loading图片

import VueLazyload from 'vue-lazyload'
import loading from '@/assets/images/loading.gif'
// 在图片界面没有进入到可视范围前不加载, 在没有得到图片前先显示loading图片
Vue.use(VueLazyload, { // 内部自定义了一个指令lazy
  loading,  // 指定未加载得到图片之前的loading图片
})

4.对异步获取的图片实现懒加载

<img v-lazy="goods.defaultImg" />

路由懒加载

当打包构建应用时,JS包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了,本质就是Vue 的异步组件在路由组件上的应用,需要使用动态import语法, 也就是import()函数

const Home = () => import('@/pages/Home')
const Search = () => import('@/pages/Search')
const Detail = () => import('@/pages/Detail')

组件通信

组件通信方式1:props

场景:父子通信

父亲给子组件传递数据:

父组件将需要传递的数据通过属性传值的方式(key:{xxxx})的方式传递给子组件,子组件 通过this.props.key的方式获取参数。

props 的三种书写方法:['arry']     { type:arry }   { type:arry, default: [ ] }  

如果父组件给子组件传递数据是一个函数,那么它的本质其实是子组件给父组件传递数据

特殊情况:路由传递props
1:布尔值类型,把路由中params参数映射为组件props数据
2:对象,静态数据,很少用
3:函数,可以把路由中params|query参数映射为组件props数据

组件通信方式2:自定义事件 $on  \    $emit

场景:子父通信

父:

<Son @parclick="handler"></Son>

子:

<button @click="$emit('parclick', '传递给父的参数')"></button>

组件通信方式3:全局事件总线 $bus

组件实例的原型的原型指向的Vue.prototype

Vue.prototype.$bus = this

组件通信方式4:vuex

组件通信方式:slot

场景:父子通信

默认插槽
具名插槽
作用域插槽


组件通信方式5:$listeners与$attrs


他们两者是组件实例的属性,可以获取到父组件给子组件传递props与自定义事件。

// parent.vue
<m-child :data-status="dataStatus" :data-message="dataMessage"></m-child>

export default {
	data(){
		return {
			dataStatus: '123',
			dataMessage: '456'
		}
	}
}
// m-child.vue
<m-grandron v-bind="$attrs"></m-grandron>
export default {
	props:{
		dataStatus: String,
	},
	data(){
		return {}
	},
	created(){
		console.log('dataStatus',this.dataStatus)  //  123
		console.log('dataMessage',this.dataStatus)  //  456
	}
}
// m-grandron.vue
export default {
	data(){
		return {}
	},
	created(){
		console.log('dataStatus',this.$attrs)  //  {dataMessage:456} 因为child组件中props声名了dataStatus所以这里$attrs就不含有dataStatus
	}
}
// parent.vue
<m-child @customEvent="ev_customEvent"></m-child>

export default {
	data(){
		return {
			dataStatus: '123',
			dataMessage: '456'
		}
	},
	methods:{
		ev_customEvent(){
			console.log('my name is parent!')
		}
	}
}
// m-child.vue
<m-grandron v-on="$listeners" @customEvent="ev_customEvent"></m-grandron>
export default {
	props:{
		dataStatus: String,
	},
	data(){
		return {}
	},
	methods:{
		ev_customEvent(){
			console.log('my name is child!')
		}
	}
}
// m-grandron.vue
<button @click="$emit('customEvent')">click me!</button>
export default {
	data(){
		return {}
	},
}


// 控制台
// my name is child!
// my name is parent!

事件

1:原生DoM----button可以绑定系统事件---click单机事件等等

<button @click=handler>点击</button>


2:组件标签---event可以绑定系统事件(不起作用:因为属于自定义事件)需要加 .native (可以把自定义事件变为原生DOM事件) 当前原生DOMclick事件,其实是给子组件的根节点 div 绑定了点击事件---利用到事件委派。

<Event @click.native=handler></Event>

promise.all( )

该方法用于将多个Promise实例,包装成一个新的Promise实例。

let p1 = new Promise((resolve, reject) => { resolve('成功')})
let p2 = new Promise((resolve, reject) => { resolve('success')})
let p3 = Promise.reject('失败')
// 全部成功时成功
Promise.all([p1, p2]).then((result) => {
  console.log(result)               //['成功', 'success']
}).catch((error) => { console.log(error)})
// 有一个失败就失败
Promise.all([p1,p3,p2]).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)      // 失败
})

往会话存对象(本地相同)

需要先把要存的对象转换成字符串。

sessionStorage.setItem("SKUINFO",JSON.stringify(this.skuInfo)

获取会话中存储的数据

computed:{
    skuInfo(){
      return JSON.parse(sessionStorage.getItem('SKUINFO'))
    }
  },

不同的页面获取用户信息

问题:如果已经登录则需要获取用户信息需要向服务器发送请求,服务器返回用户信息,展示在页面。如果没有登录,则不显示用户信息,显示请登录。

1、获取用户信息,api 请求方式,仓库中创建一个 action ,派发 action 获取用户信息。

2、不可能在每个组件页面 mounted 中派发 action 。

3、在 路由前置守卫中判断如果没有没有用户信息,就派发 action 获取用户信息。

 不用VUEX在组件中发请求,请求接口统一管理

1.在 main.js 中引入,把所有请求 api 暴露出来挂载到 vue 原型上面,类似 $bus 

 2.组件中使用

要求:未登录点击 我的订单,登录之后跳转到我的订单

在 router.js 全局守卫中当中,router.beforeEach,判断没有登录时,把在未登录时想要去的页面路径存储在地址中(路由)。

 

然后在登录组件中。 判断路由当中是否包含 query 参数指定路由,如果没有就跳转到 home 首页

 

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值