# Vue_Program **Repository Path**: chen-hongxin/Vue_Program ## Basic Information - **Project Name**: Vue_Program - **Description**: 一个适用于手机端的 Vue 项目 - **Primary Language**: JavaScript - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2021-05-12 - **Last Updated**: 2022-05-01 ## Categories & Tags **Categories**: Uncategorized **Tags**: Vue ## README # 购物街 ## 介绍   一个适用于手机端的 Vue 小项目,旨在锻炼开发能力
  该项目所用的各种数据来自于“蘑菇街” ## 使用说明 命令行输入 - `npm install` 安装所需要的包 - `npm run serve` 运行程序 **注意:不要使用鼠标滚动滚动,而应该使用点击拖动(与手机端手指点击拖动类似)** ## 接口文档 | 路径 | 方法 | get 参数 | 备注 | | --------- | ---- | -------- | ---------------- | | /home/multidata | GET | | 请求首页轮播图和推荐行的数据 | | /home/data | GET | type, page | 请求首页商品数据 | | /detail | GET | iid | 请求商品详情页的商品详情数据 | | /recommend | GET | | 请求商品详情页的推荐模块的商品数据 | | /category | GET | | 请求分类页面的商品数据 | ## 项目基本设置 ### 1.1 目录结构 - assets - common - components -> common/content - network - router - store - views -> home/category/cart/profile/detail ### 1.2 设置 CSS 初始化和全局样式(assets -> css) - base.css - initialize.css ### 1.3 axios 的封装(network -> request.js) - 创建 axios 实例 - 拦截响应,仅返回其中的 data 数据 - 根据传入的 config 发送请求,调用者通过.then 获取结果
![输入图片说明](https://images.gitee.com/uploads/images/2021/0916/105613_0186ad76_8035370.jpeg "1631717455(1).jpg") ## 项目开发细节 ## 一、公共组件(components -> common/content) ### 1.1 tabbar 底部导航栏的封装 - 封装 TabBar - 封装 TabBarItem - 响应点击切换的设计 - MainTabBar 组件对 TabBar 和 TabBarItem 重新包装 ### 1.2 navbar 顶部展示栏的封装 - 封装的 navbar 组件包含三个插槽:left、center、right - 设置 navbar 相关的样式 - 直接使用 navbar 实现首页的顶部展示栏
![输入图片说明](https://images.gitee.com/uploads/images/2021/0916/110552_bc2536f7_8035370.jpeg "1631761467(1).jpg") ### 1.3. swiper 轮播图的封装 - 封装 Swiper - 封装 SwiperItem - HomeSwiper 组件对 Swiper 和 SwiperItem 重新包装 - 使用 HomeSwiper 组件时,传入 banners 数据进行展示 ### 1.4 滚动的封装 Scroll - 学习 BetterScroll 的使用 - 安装 better-scroll - 封装一个独立的组件,用于作为滚动组件:Scroll - 组件内代码的封装: - 1. 创建 BetterScroll 对象,并且传入 DOM 和选项(probeType、click、pullUpLoad)
![输入图片说明](https://images.gitee.com/uploads/images/2021/0917/112535_c1115bc6_8035370.jpeg "1631848956(1).jpg") - 2. 监听 scroll 事件,该事件会返回一个 position(监听滚动) * `probeType: 0/1/2(手指滚动) / 3(只要是滚动)` * `scroll.on('scroll', (position) => {})` * Home.vue 和 Scroll.vue 之间进行通信 * Home.vue 将 `probeType` 设置为 3 * Scroll.vue 需要通过 `$emit`, 实时将事件发送到 Home.vue - 3. `click: false` * button 可以监听点击 * div 不可以 - 4. 监听 pullingUp 事件,监听到该事件进行上拉加载更多 * `pullUpLoad: true` * `scroll.on('pullingUp', () => {})` - 5. 封装刷新的方法:this.scroll.refresh() - 6. 封装滚动的方法:this.scroll.scrollTo(x, y, time) - 7. 封装完成刷新的方法:this.scroll.finishedPullUp
![输入图片说明](https://images.gitee.com/uploads/images/2021/0917/112959_2be7626b_8035370.jpeg "1631849355(1).jpg") ### 1.5 封装 tabControl - 独立组件的封装 - 使用 TabControl 组件时,通过 props 传入 titles 数据进行展示 - 监听点击,通过 `currentIndex` 判断当前选中是哪一个 tab, 则该 tab 的文字与 `border-bottom` 样式颜色均变色 ### 1.6 goods 商品展示列表的封装 - 展示商品列表,封装 GoodsList 组件 * `props: goods` * `v-for`: `goods` 取出数据,传入 GoodListItem 组件进行展示
![GoodsListItem](https://images.gitee.com/uploads/images/2021/0917/111158_c4281ad1_8035370.jpeg "1631848247(1).jpg") - 列表中每一个商品,封装 GoodsListItem 组件 * `props: goodsItem ` *` goodsItem` 取出数据, 并且使用正确的 `div/span/img` 基本标签进行展示
![GoodsListItem](https://images.gitee.com/uploads/images/2021/0917/111550_711e1ba5_8035370.jpeg "1631848471(1).jpg") - 设置相关样式 - 使用 GoodsList 组件时,传入相关的商品数据进行展示 ### 1.7 返回顶部 - 封装 BackTop 组件 - 定义一个常量,用于决定在什么数值下显示 BackTop 组件 - 监听滚动,决定 BackTop 的显示和隐藏 - 监听 BackTop 的点击,点击时,调用 scrollTo 返回顶部 ## 二、首页界面(views -> home) ### 1.1 请求首页数据 - 封装请求首页数据的方法(network -> home.js)
![输入图片说明](https://images.gitee.com/uploads/images/2021/0916/112831_5add2272_8035370.jpeg "1631762695(1).jpg") - 发送数据请求 - 将 banner 数据放在 banners 变量中 - 将 recommend 数据放在 recommends 变量中 - 将不同 type 的 data 数据放在 goods 变量的不同对象中(根据 type 和 page 请求商品数据)
![输入图片说明](https://images.gitee.com/uploads/images/2021/0916/113121_03db2631_8035370.jpeg "1631762774.jpg")
![输入图片说明](https://images.gitee.com/uploads/images/2021/0916/113131_60e87f34_8035370.jpeg "1631762774(1).jpg") ### 1.2 封装 RecommendView(home -> childComps) - 传入 recommends 数据,进行展示 ### 1.3 封装 FeatureView(home -> childComps) - 独立组件封装FeatureView - 展示一张图片即可 ### 1.4 tabControl 的吸顶效果 #### 1.4.1 获取到 tabControl 的 `offsetTop` * 必须知道滚动到多少时, 开始有吸顶效果, 这个时候就需要获取 tabControl 的 `offsetTop` * 但是, 如果直接在 `mounted` 中获取 tabControl 的 `offsetTop`, 那么值是不正确(因为此时可能在图片还没加载完的情况下就计算出了高度) * 如何获取正确的值了? * 监听 HomeSwiper 中 img 的加载完成 * 加载完成后, 发出事件, 在 Home.vue 中, 获取正确的值 * 为了不让 HomeSwiper 多次发出事件,可以使用 `isLoad` 的变量进行状态的记录 * 注意: 区分这里不进行多次调用和 debounce(防抖)的区别 #### 1.4.2 监听滚动, 动态的改变 tabControl 的样式 * 问题:动态的改变 tabControl 的样式时, 会出现两个问题: * 问题一: 下面的商品内容, 会突然上移(tabControl 设置了 fixed后,脱离了标准流) * 问题二: tabControl 虽然设置了 `fixed`, 但是也随着 Better-Scroll 一起滚出去了
(Better-Scroll 通过改变 `translate` 属性进行滚动,所以设置了 `fixed` 属性的标签也会滚动出去) * 其他方案来解决停留问题: * 在 scroll 标签上面, 多复制了一份 tabControl(新) 组件对象, 利用它来实现停留效果(需要设置定位,否则会被盖住) * 当用户滚动超过 offsetTop 时, tabControl(新) 显示出来 * 否则, tabControl(新) 隐藏起来 ### 1.5 backTop 回到顶部 #### 1.5.1 封装 BackTop 组件 #### 1.5.2 BackTop 组件的显示和隐藏 * Home 组件的 `data` 中 新建属性 `isShowBackTop: false` * 监听滚动, 拿到滚动的位置: * 若 `-position.y > 1000`,则 `isShowBackTop: true` * 即 `isShowBackTop = -position.y > 1000` #### 1.5.3 如何监听组件的点击 * 不可以直接监听 back-top 的点击, 必须添加修饰.native
![输入图片说明](https://images.gitee.com/uploads/images/2021/0917/105300_4a9f560a_8035370.jpeg "backTop.jpg") * 回到顶部 * scroll 对象, `scroll.scrollTo(x, y, time)`
![输入图片说明](https://images.gitee.com/uploads/images/2021/0917/105447_2e088ac8_8035370.jpeg "scrollTo.jpg") * `this.$refs.scroll.scrollTo(0, 0, 300)`
![输入图片说明](https://images.gitee.com/uploads/images/2021/0917/105504_16fa8714_8035370.jpeg "backClick.jpg") ### 1.6 上拉加载更多 - 通过 Scroll 监听上拉加载更多
![输入图片说明](https://images.gitee.com/uploads/images/2021/0916/202445_c5d70320_8035370.jpeg "1631795032.jpg") - 在 Home 中加载更多的数据 - 请求数据完成后,调动 finishedPullUp
![输入图片说明](https://images.gitee.com/uploads/images/2021/0916/203230_6c3acb75_8035370.jpeg "1631795272(1).jpg")
![输入图片说明](https://images.gitee.com/uploads/images/2021/0916/203309_3e0e798f_8035370.jpeg "1631795382(1).jpg")
![输入图片说明](https://images.gitee.com/uploads/images/2021/0916/203252_83091a0b_8035370.jpeg "1631795413(1).jpg") ### 1.7 解决首页中可滚动区域的问题 * Better-Scroll 在决定有多少区域可以滚动时, 是根据 `scrollerHeight` 属性决定 * `scrollerHeight` 属性是根据放 Better-Scroll 的 content 中的子组件的高度 * 但是我们的首页中, 刚开始在计算 `scrollerHeight` 属性时, 是没有将图片计算在内的 * 所以计算出来的高度是错误的(1300+) * 后来图片加载进来之后有了新的高度, 但是 scrollerHeight 属性并没有进行更新. * 所以滚动出现了问题 * 如何解决这个问题了? * 监听每一张图片是否加载完成, 只要有一张图片加载完成了, 执行一次 refresh() * 如何监听图片加载完成了? * 原生的 js 监听图片: `img.onload = function() {}` * Vue 中监听: `@load='方法'`
![输入图片说明](https://images.gitee.com/uploads/images/2021/0917/121454_9f65530d_8035370.jpeg "1631851987(1).jpg") * 调用 scroll 的 `refresh()` * 如何将 GoodsListItem.vue 中的事件传入到 Home.vue 中 * 因为涉及到非父子组件的通信, 所以这里我们选择了**事件总线** * bus -> 总线 * `Vue.prototype.$bus = new Vue()` * `this.bus.emit('事件名称', 参数)`
![输入图片说明](https://images.gitee.com/uploads/images/2021/1108/181804_cbdf97f7_8035370.jpeg "1636366568(1).jpg") * `this.bus.on('事件名称', 回调函数(参数))`
![输入图片说明](https://images.gitee.com/uploads/images/2021/0917/154724_1de8a9f2_8035370.jpeg "1631864632(1).jpg") * 问题一: refresh 找不到的问题 * 第一: 在 Scroll.vue 中, 调用 this.scroll 的方法之前, 判断 this.scroll 对象是否有值
eg: `this.scroll && this.scroll.scrollTo(x, y, time);` * 第二: 在 mounted 生命周期函数中使用 this.$refs.scroll 而不是 created 中
(因为 created 在模板渲染成html前调用,组件还未挂载,this.scroll 可能为空;而 mounted 在模板渲染成html后调用) * 问题二: 对于refresh非常频繁的问题, 进行防抖操作 * 防抖 debounce / 节流 throttle(研究一下) * 防抖函数起作用的过程: * 如果我们直接执行 refresh, 那么 refresh 函数会被执行 30 次. * 可以将 refresh 函数传入到 debounce 函数中, 生成一个新的函数. * 之后在调用非常频繁的时候, 就使用新生成的函数. * 而新生成的函数, 并不会非常频繁的调用, 如果下一次执行来的非常快, 那么会将上一次取消掉 ```js debounce(func, delay) { let timer = null return function (...args) { if (timer) clearTimeout(timer) timer = setTimeout(() => { func.apply(this, args) }, delay) } }, ``` ### 1.8 让 Home 保持原来的状态 #### 1.8.1 让 Home 不要随意销毁掉 * keep-alive #### 1.8.2 让 Home 中的内容保持原来的位置 * 离开时, 保存一个位置信息 saveY
![输入图片说明](https://images.gitee.com/uploads/images/2021/0917/162156_e25a4f09_8035370.jpeg "1.jpg") * 进来时, 将位置设置为原来保存的位置 saveY 信息即可
![输入图片说明](https://images.gitee.com/uploads/images/2021/0917/162213_db50c515_8035370.jpeg "1631866844(1).jpg") * 注意: 最好回来时, 进行一次 refresh() ## 项目界面(仅展示部分) ### 1. 首页 ![购物街首页](https://images.gitee.com/uploads/images/2021/0914/165936_54c2687f_8035370.jpeg "购物街首页.jpg") ### 2. 商品详情 ![商品详情](https://images.gitee.com/uploads/images/2021/0914/170336_4c023e62_8035370.jpeg "商品详情页面.jpg") ### 3. 分类界面 ![分类界面](https://images.gitee.com/uploads/images/2021/0921/160224_348cf1e2_8035370.jpeg "分类.jpg") ### 4. 购物车 ![购物车](https://images.gitee.com/uploads/images/2021/0914/170350_f69870f8_8035370.jpeg "购物车界面.jpg") ### 5. 个人中心 ![个人中心](https://images.gitee.com/uploads/images/2021/0921/160302_55eb691c_8035370.jpeg "个人中心.jpg")