赞
踩
Home页面有7个子组件。
步骤:
1)创建组件(命名为TypeNav,全局组件一般放在components文件夹中)
2)全局注册组件(在main.js文件中)
- // 三级联动组件(全局组件)
- import TypeNav from './components/TypeNav'
- // 全局组件:第一个参数是全局组件的名字,第二个参数是哪个组件
- Vue.component(TypeNav.name,TypeNav)
3)使用组件(在home组件中使用TypeNav)
步骤一样,先创建组件,再在Home组件中引入和注册并使用
说明:
1)本项目是一个前后台分离的项目: 前台应用与后台应用
2)后台应用负责处理前台应用提交的请求, 并给前台应用返回json数据
3)前台应用负责展现数据, 与用户交互, 与后台应用交互
1)先安装axios
npm install -S axios nprogress
2)axios一般存放在api文件夹(自己创建)里面
index.js---对api进行统一管理
ajax.js---二次封装axios
二次封装的原因:在本项目中需要使用到请求拦截器和响应拦截器
请求拦截器:在发请求之前处理一些业务
响应拦截器:在服务器数据返回后,处理一些事情
- 在/api/ajax.js
- // 二次封装axios
- import axios from 'axios'
-
- const service = axios.create({
- baseURL: '/api',//基础路径
- timeout: 15000//连接请求超时时间
- })
-
- // 请求拦截器
- service.interceptors.request.use((config) => {
- // 必须返回配置对象
- return config;
- })
-
- // 响应拦截器
- service.interceptors.response.use(
- (response) => {
- // 返回响应体数据
- return response.data
- }, (error) => {
- // 统一处理一下错误
- alert(`请求出错: ${error.message || '未知错误'}`)
- // 后面可以选择不处理或处理
- return Promise.reject(error)
- })
-
- export default service
3)api接口的统一管理
项目很小:可以在组件的生命周期函数里发请求
项目大:使用axios.get('xxx')
- // 对接口的统一管理
- import ajax from './ajax.js'
-
- // 获取商品的三级分类列表
- // /api/product/getBaseCategoryList get 无参数
- export const reqBaseCategoryList=()=>{
- return ajax({url:'api/product/getBaseCategoryList',method:'get'})
- }
- // export const reqBaseCategoryList=()=>ajax.get(`api/product/getBaseCategoryList`)
4)引入进度条
先安装cnpm install --save nprogress
注意:
1)vue2使用vuex3 , vue3使用vuex4
2)项目小的,可以不用vuex;项目大的,组件多的,需要使用vuex
操作流程:给每一个模块创建小仓库,再把小仓库引入到大仓库中
步骤:
1)先创建大仓库(store/index.js),再创建小仓库(store/home.js)
- //在store/home.js中
- import { reqBaseCategoryList } from '@/api'
- const home={
- state:{
- baseCateGoryList:[]
- },
- mutations:{
- RECEIVE_BASE_CATEGORY_LIST(state,list){
- list.shift()//去除数组的第一个元素
- state.baseCateGoryList=list
- },
- },
- actions:{
- async getBaseCateGoryList({commit}){
- const result=await reqBaseCategoryList()
- if(result.code==200){
- commit('RECEIVE_BASE_CATEGORY_LIST', result.data)
- }
- }
- },
- getters:{},
- namespaced:true
- }
- export default home
2)在大仓库里引入小仓库home
- 在store/index.js
- // 大仓库
- import Vue from 'vue'
- import Vuex from 'vuex'
- // 需要使用插件一次
- Vue.use(Vuex)
-
- import home from './home'
- // 对外暴露store类的一个实例
- export default new Vuex.Store({
- // 实现Vuex仓库模块化开发存储数据
- modules:{
- home,
- }
- })
3)在main.js中,引入store
- // 引入仓库
- import store from './store'
-
- new Vue({
- render: h => h(App),
-
- // 注册路由
- router:router,
- // 注册仓库:
- store
- }).$mount('#app')
1)当组件挂载完毕后,可以向服务器发送请求
- //在TypeNav/index.vue中
-
- mounted(){
- this.$store.dispatch('home/getBaseCateGoryList')
- },
2)去home仓库请求数据
actions在执行时,会调用api里面的接口函数,向服务器发请求,调用api里的reqBaseCategoryLis函数发送。
请求成功后,把数据给mutations处理。
再把数据给state。
3)TypeNav接收数据
4)渲染数据
注意
1. home仓库使用了namespaced,开启了命名空间,在使用仓库时,记得添加/home
2. 在使用vuex模块化时,需要向外暴露一个对象,不需要用到createStore。所以给home里面命名一下,并向外暴露。
3.注意vuex3和vuex4的区别 。
步骤:
1)首先设置一个动态属性 currentindex=-1 ,表示鼠标都没有移上去。
- //在TypeNav/index.vue中
- data() {
- return {
- // 响应式属性,存储用户鼠标移上哪一个一级分类
- currentIndex: -1, // 代表鼠标谁都没有移上去
- };
- },
2) 添加一个less属性,设置背景颜色
3)给一级分类添加鼠标进入和离开事件。进入时,需要携带参数索引号,表示是哪一个一级分类。
当 :class="{cur: currentIndex === index} 为真时,就给当前的一级分类添加背景颜色。
- //在TypeNav/index.vue中
- <div class="item" v-for="(c1, index) in baseCateGoryList" :key="c1.categoryId"
- @mouseleave="leaveIndex">
- <h3 @mouseenter="changeIndex(index)" :class="{ cur: currentIndex === index }" >
- <a>{{ c1.categoryName }}</a>
- </h3>
- </div>
-
-
-
- methods: {
- // 鼠标进入修改响应式数据currentIndex属性
- changeIndex(index) {
- // index 鼠标移上某一个一级分类的元素的索引值
- this.currentIndex = index;
- },
- // 一级分类鼠标移出的事件回调
- leaveIndex(){
- // 鼠标移出currentIndex=-1
- this.currentIndex =-1;
- }
- },
4)使用原生JS实现二三级分类的显示与隐藏(利用三元表达式实现)
防抖:前面的触发都取消,最后一次执行在规定时间之后才会触发。也就是说如果连续快速的触发,只会执行最后一次。
节流:在规定间隔时间内,不会重复触发回调,只有大于了时间间隔,才会触发,把频繁触发变为少量触发。
本项目中,鼠标不停的在三级联动上来回切换,进行节流操作,把频繁触发变为少量触发。
这里需要使用 lodash,一般都下载好了的。并且进行按需加载。
- //在TypeNav/index.vue中
- //按需引入节流
- import throttle from "lodash/throttle";
-
- methods: {
- // 鼠标进入修改响应式数据currentIndex属性
- // throttle回调函数不要用箭头函数,防止出现上下文this问题
- changeIndex: throttle(function (index) {
- // index:鼠标移上某一个一级分类的元素的索引值
- this.currentIndex = index;
- }, 50),
- },
需求:当点击某个分类时,进行路由跳转,并把分类的名字和id传给search,然后search拿到参数向服务器发请求,显示相应的数据。
路由跳转的两种方式:声明式导航和编程式导航。
如果使用声明式导航,需要使用<router-link></router-link>,并且<router-view>会生成好多组件,会出现页面的卡顿。
如果使用编程式导航,需要给每一个a添加点击事件。
这里使用编程式导航+事件委派。
事件委派:在父节点上添加事件监听器,利用事件冒泡影响每一个子节点。
注意两个问题:
1)事件委派,是把全部的子节点的事件委派给父亲节点,如何确定我们点击的一定是a标签
利用自定义属性,给子节点中的a标签添加自定义属性data-categoryName,其余子节点没有。
2)确定了点击的是a标签,如何区分是一级,二级,三级分类的标签
同样也是利用自定义属性,给子节点中的a标签添加自定义属性data-category1Id、data-category2Id、data-category3Id。
步骤:先判断是不是a标签,是则进一步判断是哪一个分类。
1)先给一级分类的父标签添加点击事件,进行路由跳转。
2)给每一分类级的a标签添加自定义属性。
3)编写跳转函数
- //进行路由跳转
- // 从home跳转到search
- goSearch(event){
- //获取当前事件的触发对象
- let element=event.target;
- //利用节点的dataset属性,获取节点的自定义属性(要小写)
- let { categoryname, category1id, category2id, category3id }=element.dataset;
- //判断,如果categoryname为真,则表示当前点击的是a标签
- if(categoryname){
- //整理路由跳转的参数
- let location={name:'Search'};//记得在router/index.js中给search命名
- // 小写的是自定义属性值,大写的是新添加的属性值
- // 需要获取小写的自定义属性值给大写的新添加的属性值
- let query={categoryName:categoryname};
- // 判断是一级,二级,三级哪一个分类
- if(category1id){
- query.category1Id=category1id;
- }else if(category2id){
- query.category2Id=category2id
- }else {
- query.category3Id=category3id
- }
- //此时query里面就有了name和id
- //再把query给到location
- location.query=query;
- //现在进行传参
- this.$router.push(location)
-
-
- }
- }
步骤:
1)先给TypeNav组件添加一个属性show,表示显示与隐藏三级联动。
2)给三级联动使用v-show,进行显示与隐藏。
3)在搜索页面,最开始三级联动是隐藏的,只有当鼠标移上去,才显示出来。
所以,当组件挂载完毕后,如果不是home组件,就隐藏三级联动
4) 鼠标在全部商品分类上移入移出,进行三级联动的显示与隐藏。
这里可以调用事件委派,给透明封装一个大盒子,在大盒子上进行鼠标移入移出事件。
5)添加过渡动画
注意:
1)组件或者元素务必要有v-if或者v-show指令
2)记得加一个name,使用transition标签包住
- // 过渡动画的样式
- // 过渡动画开始的状态(进入)
- .sort-enter {
- height: 0px;
- }
- // 过渡动画结束状态(进入)
- .sort-enter-to {
- height: 461px;
- }
- // 定义动画时间、速率
- .sort-enter-active {
- transition: all 0.5s linear;
- }
操作:最开始的时候,发送请求是在TypeNav组件中的mounted钩子函数中,现在把请求放入到App.vue文件中。因为最先执行的就是App.vue,这样请求就只会发送一次!!!
从首页向Search组件跳转有两种方式:
1.通过搜索关键字跳转(传的是params参数)
2.点击三级导航的链接跳转(传的是query参数)
如果既点击了搜索按钮,又点击了三级导航的链接,那么点击的后者的参数会覆盖前者的参数,所以需要进行参数的合并,这样两个参数都可以传递!!!
- //在Header.vue中
- methods:{
- //在搜索框输入params参数后,点击搜索按钮进行跳转
- search(){
- // this.$router.push(`/search/${this.keyword}`)
- //这里进行参数合并,query和params
- let location={name:'Search',params:{keyword:this.keyword||undefined}}
- location.query=this.$route.query;
- this.$router.push(location)
- }
- }
在TypeNav/index.js中
首先使用mock搭建模拟数据
步骤:
1)先安装:npm install mockjs
2)在src文件夹中创建mock文件夹,用来存放json假数据
3)在mock文件夹中准备假数据,引入ListContainer、Floor的数据
4)在public下创建一个images文件夹,把mock数据需要的图片存放进去
5)在mock文件夹下创建mockServe.js,通过Mock.mock方法进行模拟数据
6)在main.js里引入src/mock/mockServe.js,让配置假数据执行一次
7)在api文件夹中创建mockAjax.js文件,模拟发送请求。
里面的内容与原先二次封装的request.js文件中内容一样,但是要注意,记得把基础路径改为'/mock',因为我们是模拟发送请求
8)在src/api/index.js文件中,进行api的统一管理,模拟轮播图的接口
其次,获取轮播图数据
1)在组件挂载完毕后,向服务器发送请求
2)使用vuex三连环
3)接收数据
然后,绘制轮播图
这里需要使用swiper插件
安装swiper插件:npm i swiper --save
使用swiper的三步骤:
1.引入相应的包
2.页面结构必须先有(给轮播图添加静态效果)
3.有结构后再new Swiper实例(给轮播图添加动态效果)
步骤:
1.引包
1)在main.js中引入css(这样组件都可以使用)
- // 引入swiper样式
- import 'swiper/css/swiper.css'
2)在listContainer组件中引入swiper
- // 引包
- import Swiper from 'swiper'
2.搭建轮播图页面的结构
3.添加动态效果(重点)
这里要注意,不能直接在mounted里面写,一般情况下在里面写是正确的。但这里不行,这里有dispatch,涉及到异步语句,需要发请求,拿数据,导致v-for遍历的时候,结构还没有完全,所以还不能实例化swiper。
如果直接在里面写,会先实例化swiper,再搭建结构,这样是不符合swiper操作步骤的。
页面结构必须先生成,再new Swiper!!!
解决方法:
1)使用定时器包裹swiper,但是有问题,因为发送ajax请求的时间不确定,所以定时器时间不好把握。
2)使用watch监听,但是只使用watch,只能监听到数据的变化,不能判断v-for已经执行完毕了,所以也不行。
3)使用watch+$nextTick,在监听到数据回来后,结构完成后,使用$nextTick来实例化swiper,就可以完成动态效果渲染。
所以使用方法三。
- //在Home/ListContainer.vue
- watch:{
- // 监听banners数据的变化,有空数组变为数组里有4个元素
- banners:{
- immediate:true,
- handler(newValue,oldValue){
- // 通过监听banners属性值的变化,如果执行了Hanler,则表示数据有了,
- //但是只能保证有数据了,不能保证v-for已经执行完毕
- //只有v-for执行完后,才有结构,才能实例化swiper
- //所以这里需要添加$nextTick
- // nextTick:在下次DOM更新, 循环结束之后,执行延迟回调。在 修改数据之后 立即使用这个方法,获取更新后的DOM。
- this.$nextTick(()=>{
- // 下面的是直接复制的swiper里面的代码
- var mySwiper = new Swiper ('.swiper', {
- loop: true, // 循环模式选项
- autoplay:true,//自动切换
- // 如果需要分页器
- pagination: {
- el: '.swiper-pagination',
- clickable:true,
- },
- // 如果需要前进后退按钮
- navigation: {
- nextEl: '.swiper-button-next',
- prevEl: '.swiper-button-prev',
- },
- })
- })
- }
- }
- }
开发floor组件
和前面一样,先写静态,之后发请求(写api),写完api之后就写仓库三连环,仓库存储数据后,组件捞数据,捞完之后展示。
1)配置api接口
2)仓库三连环存储数据
在store/home.js中写vuex三连环
3)派发action
注意,这里不能在floor组件里面派发,因为这里有两个floor组件,需要使用v-for遍历,所以在他的父组件home组件去派发。
4)收取数据
因为现在这个数据在父组件home里,子组件floor想要得到数据,就得使用组件间的通信,这里使用父向子传。
使用props传递。
5)渲染数据到页面
6)floor轮播图制作
注意在这里就可以直接在mounted里面实例化swiper。
因为这里请求是父组件发的,父组件通过props传递过来,并且结构已经有了,才执行的mounted
封装Carsouel全局组件
因为在ListContainer和Floor中都用到了carsouel轮播图,我们可以封装成一个全局组件,这样方便使用。
封装成为全局组件,注意里面的内容都是一样的,所以需要修改一下原先的内容。
由于floor组件的是父亲传过来的值,所以里面数据已经有了,所以数据没有变化,无法通过watch监听到,所以要使用immediate,立即监听。
由于ListContainer组件的v-for结构还不能确定已经完成了,所以要使用nextTick
然后两者一结合,写在components文件夹里面创建的一个Carousel文件夹中。
这样当需要使用轮播图的时候,直接引入这个文件就可以了。
注意:全局组件记得在main.js中引入并注册!!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。