uniapp黑马项目总结

编程入门 行业动态 更新时间:2024-10-24 18:25:37

uniapp<a href=https://www.elefans.com/category/jswz/34/1766169.html style=黑马项目总结"/>

uniapp黑马项目总结

在做完了两个vue项目之后,我开始了小程序的学习,由于开学等因素影响进度一直都是断断续续的。最终在开学一周多的时间结束了uniapp项目的练习。于是我选择了黑马商场做为微信小程序uniapp的练手。

uniapp是一个基于vue.js开发的一个前端框架,可以发布各个平台,本项目是开发一个微信小程序,使用HbuilderX中uniapp的内置的uni.ui模块。使用了sass,练习了对微信小程序开发的一套相对完整的流程,还有用git提交代码到gitee。

小程序中一个不同于网页的地址在于tabBar栏,微信小程序中,tabBar栏可以通过配置生成,也方便管理。只要在根目录中的 pages.json 配置文件,新增 tabBar 的配置节点即可

{"tabBar": {"selectedColor": "#C00000","list": [{"pagePath": "pages/home/home","text": "首页","iconPath": "static/tab_icons/home.png","selectedIconPath": "static/tab_icons/home-active.png"},{"pagePath": "pages/cate/cate","text": "分类","iconPath": "static/tab_icons/cate.png","selectedIconPath": "static/tab_icons/cate-active.png"},{"pagePath": "pages/cart/cart","text": "购物车","iconPath": "static/tab_icons/cart.png","selectedIconPath": "static/tab_icons/cart-active.png"},{"pagePath": "pages/my/my","text": "我的","iconPath": "static/tab_icons/my.png","selectedIconPath": "static/tab_icons/my-active.png"}]}
}

之后再修改一个导航条的模式效果,也是在pages.json中,修改globalStyle节点

{"globalStyle": {"navigationBarTextStyle": "white","navigationBarTitleText": "黑马优购","navigationBarBackgroundColor": "#C00000","backgroundColor": "#FFFFFF"}
}

然后就是配置网络请求了,因为小程序中是不支持axios的,wx.request()又功能简单,不能支持拦截器等,所以这个项目老师用了一个自己写的第三方包来发起网络请求的,官方文档如下:

@escook/request-miniprogram - npm

再main.js入口文件中配置,引入再绑定到uni顶级对象上,再加个请求和响应拦截器

import { $http } from '@escook/request-miniprogram'uni.$http = $http
// 配置请求根路径
$http.baseUrl = ''// 请求开始之前做一些事情
$http.beforeRequest = function (options) {uni.showLoading({title: '数据加载中...',})
}// 请求完成之后做一些事情
$http.afterRequest = function () {uni.hideLoading()
}

轮播图

发起请求,再动态把图片的src等属性渲染到页面上,用小程序自带的swiper标签。

小程序分包

考虑到首次启动的加载时间,可以采用分包,而且微信小程序也对分包的大小有检测,所以我们是把tabBar相关的页面(home,cate,cart,my)放在主包,再把其它页面放在分包里(goods_detail,goods_list,search)。配置分包如下:

1、先在根目录中,创建分包的根目录,命名subpkg

2、再在pages.json中,与pages平级配置subPackages节点

{"pages": [{"path": "pages/home/home","style": {}},{"path": "pages/cate/cate","style": {}},{"path": "pages/cart/cart","style": {}},{"path": "pages/my/my","style": {}}],"subPackages": [{"root": "subpkg","pages": []}]
}

3、之后可在分包目录下,新建页面,并选择小程序分包,即可自动配置

 封闭uni.$showMsg()方法

使用原生uni.showToast({})来提示用户,要配置配置对象,所以封闭全局方法来简化之后的使用

// 封装的展示消息提示的方法
uni.$showMsg = function (title = '数据加载失败!', duration = 1500) {uni.showToast({title,duration,icon: 'none',})
}

分类导航区域

渲染然后再使用uni.switchTab()跳转页面

<!-- 分类导航区域 -->
<view class="nav-list"><view class="nav-item" v-for="(item, i) in navList" :key="i" @click="navClickHandler(item)"><image :src="item.image_src" class="nav-img"></image></view>
</view>
// nav-item 项被点击时候的事件处理函数
navClickHandler(item) {// 判断点击的是哪个 navif (item.name === '分类') {uni.switchTab({url: '/pages/cate/cate'})}
}

三级分类,左右侧滚动

<template><view><view class="scroll-view-container"><!-- 左侧的滚动视图区域 --><scroll-view class="left-scroll-view" scroll-y :style="{height: wh + 'px'}"><view class="left-scroll-view-item active">xxx</view><view class="left-scroll-view-item">xxx</view><view class="left-scroll-view-item">xxx</view><view class="left-scroll-view-item">xxx</view><view class="left-scroll-view-item">xxx</view><view class="left-scroll-view-item">多复制一些节点,演示纵向滚动效果...</view></scroll-view><!-- 右侧的滚动视图区域 --><scroll-view class="right-scroll-view" scroll-y :style="{height: wh + 'px'}"><view class="left-scroll-view-item">zzz</view><view class="left-scroll-view-item">zzz</view><view class="left-scroll-view-item">zzz</view><view class="left-scroll-view-item">zzz</view><view class="left-scroll-view-item">多复制一些节点,演示纵向滚动效果</view></scroll-view></view></view>
</template>

 这里有一个bug,就是每点一次左边的一级分类,再滚动右侧的二三级分类,再点一点一级分类,会发现右边的滚动条并不是在顶部,所以要动态绑定一个scroll-y属性,然后每一次点击了一级分类,重置为0

data() {return {// 滚动条距离顶部的距离scrollTop: 0}
}
<!-- 右侧的滚动视图区域 -->
<scroll-view class="right-scroll-view" scroll-y :style="{height: wh + 'px'}" :scroll-top="scrollTop"></scroll-view>
// 选中项改变的事件处理函数
activeChanged(i) {this.active = ithis.cateLevel2 = this.cateList[i].children// 让 scrollTop 的值在 0 与 1 之间切换this.scrollTop = this.scrollTop === 0 ? 1 : 0// 可以简化为如下的代码:// this.scrollTop = this.scrollTop ? 0 : 1
}

封闭my-search组件

由于搜索组件要在很多页面都要进行重用,所以这里可以封闭为一个组件

1、在根目录下新建一个components,再新建组件

2、直接在结构中以标签的方式直接使用自定义组件

<my-search></my-search>

在分类页面中,加上了自定义搜索组件后,右侧分类下面会不能滑动到底部,因为样式的原因,所以还需要在挂载的时候就要减去搜索组件的大小

onLoad() {const sysInfo = uni.getSystemInfoSync()// 可用高度 = 屏幕高度 - navigationBar高度 - tabBar高度 - 自定义的search组件高度this.wh = sysInfo.windowHeight - 50
}

为了增加组件的通用性,我们可以通过props来定义属性,以方便以后使用的时候可以通过传入参数,让组件更个性化

1、通过props来定义两个变量

props: {// 背景颜色bgcolor: {type: String,default: '#C00000'},// 圆角尺寸radius: {type: Number,// 单位是 pxdefault: 18}
}

2、再动态的绑定stype属性

<view class="my-search-container" :style="{'background-color': bgcolor}"><view class="my-search-box" :style="{'border-radius': radius + 'px'}"><uni-icons type="search" size="17"></uni-icons><text class="placeholder">搜索</text></view>
</view>

搜索框自动获取焦点

这个项目的搜索框的实现是先用一个view然后再点击跳转到搜索页面实现的,所以跳转后自动获取焦点,以增强用户体验

在components中的uni-search-bar中uni-search-bar.vue中,data中的show与showSync的值改为true即可,但是这里直接改源码不好

搜索框的防抖

经典防抖,每次输入后,500毫秒内要是有新的输入事件,再不断重启延时器

input(e) {// 清除 timer 对应的延时器clearTimeout(this.timer)// 重新启动一个延时器,并把 timerId 赋值给 this.timerthis.timer = setTimeout(() => {// 如果 500 毫秒内,没有触发新的输入事件,则为搜索关键词赋值this.kw = e.valueconsole.log(this.kw)}, 500)
}

关键字数组的顺序

最近搜索的应该放在前面,可以用计算属性,再把这个数组复制一下再反转

computed: {historys() {// 注意:由于数组是引用类型,所以不要直接基于原数组调用 reverse 方法,以免修改原数组中元素的顺序// 而是应该新建一个内存无关的数组,再进行 reverse 反转return [...this.historyList].reverse()}
}

解决关键词重复问题

在保存关键词为历史记录的方法中,把这个数组转为set对象,因为set对象没有重复的元素

再移除对应元素,再添加元素,再转为数组即可

// 保存搜索关键词为历史记录
saveSearchHistory() {// this.historyList.push(this.kw)// 1. 将 Array 数组转化为 Set 对象const set = new Set(this.historyList)// 2. 调用 Set 对象的 delete 方法,移除对应的元素set.delete(this.kw)// 3. 调用 Set 对象的 add 方法,向 Set 中添加元素set.add(this.kw)// 4. 将 Set 对象转化为 Array 数组this.historyList = Array.from(set)
}

数据的持久化存储

把对象用JSON.stringify转为json,存在本地

 uni.setStorageSync('kw', JSON.stringify(this.historyList))

把json用JSON.parse转为对象,拿到数据

onLoad() {this.historyList = JSON.parse(uni.getStorageSync('kw') || '[]')
}

再练习vuex

在根目录下store中store.js

导入vue与vuex,再安装为vue插件,创建store实例对象,向外暴露store对象,也可以使用别的模块中的数据

// 1. 导入 Vue 和 Vuex
import Vue from 'vue'
import Vuex from 'vuex'// 2. 将 Vuex 安装为 Vue 的插件
Vue.use(Vuex)// 3. 创建 Store 的实例对象
const store = new Vuex.Store({// TODO:挂载 store 模块modules: {m_cart: moduleCart,},
})// 4. 向外共享 Store 的实例对象
export default store

最后再从入口文件中把store挂载到vue实例上

// 1. 导入 store 的实例对象
import store from './store/store.js'const app = new Vue({...App,// 2. 将 store 挂载到 Vue 实例上store,
})
app.$mount()

为了模块化,我们可以再建cart.js,为了语义化,可以开启命名空间

export default {// 为当前模块开启命名空间namespaced: true,// 模块的 state 数据state: () => ({// 购物车的数组,用来存储购物车中每个商品的信息对象// 每个商品的信息对象,都包含如下 6 个属性:// { goods_id, goods_name, goods_price, goods_count, goods_small_logo, goods_state }cart: [],}),// 模块的 mutations 方法mutations: {},// 模块的 getters 属性getters: {},
}

在页面中把store映射到当前页面

// 按需导入 mapMutations 这个辅助方法
import { mapMutations } from 'vuex'export default {methods: {// 把 m_cart 模块中的 addToCart 方法映射到当前页面使用...mapMutations('m_cart', ['addToCart']),},
}

在store中mutations使用commit调用mutations中的方法

 // 通过 commit 方法,调用 m_cart 命名空间下的 saveToStorage 方法thismit('m_cart/saveToStorage')

为tabBar设置数字徽标

先把映射一下

再把使用方法更新下标为2,即第3个tabBar的上徽标

// 按需导入 mapGetters 这个辅助方法
import { mapGetters } from 'vuex'export default {data() {return {}},computed: {// 将 m_cart 模块中的 total 映射为当前页面的计算属性...mapGetters('m_cart', ['total']),},
}
methods: {setBadge() {// 调用 uni.setTabBarBadge() 方法,为购物车设置右上角的徽标uni.setTabBarBadge({index: 2, // 索引text: this.total + '' // 注意:text 的值必须是字符串,不能是数字})}
}

然后在挂载的时候和更新数量的时候用一下就行

因为很多页面都要用,所以这里可以使用混入,在根目标下新建一个mixins文件夹,然后把代码封装到一个单独的js文件,在四个tabBar页面中导入即可

也可再加一个监听属性,可以全局改变tabBar的值

  watch: {// 监听 total 值的变化total() {// 调用 methods 中的 setBadge 方法,重新为 tabBar 的数字徽章赋值this.setBadge()},},
// 导入自己封装的 mixin 模块
import badgeMix from '@/mixins/tabbar-badge.js'export default {// 将 badgeMix 混入到当前的页面中进行使用mixins: [badgeMix],// 省略其它代码...
}

动态更新勾选的商品数量

商品数量 = 每一项已勾选的商品所选数量相加

这里使用了reduce方法

// 勾选的商品的总数量
checkedCount(state) {// 先使用 filter 方法,从购物车中过滤器已勾选的商品// 再使用 reduce 方法,将已勾选的商品总数量进行累加// reduce() 的返回值就是已勾选的商品的总数量return state.cart.filter(x => x.goods_state).reduce((total, item) => total += item.goods_count, 0)
}

购物车项左滑删除UI效果

      <!-- 滑动删除效果 --><uni-swipe-action><block v-for="(goods, i) in cart" :key="i"><!-- uni-swipe-action-item 可以为其子节点提供滑动操作的效果。需要通过 options 属性来指定操作按钮的配置信息 --><uni-swipe-action-item :right-options="options" @click="swipeActionClickHandler(goods)"><my-goods :goods="goods" :show-radio="true" :show-num="true" @radio-change="radioChangeHandler" @num-change="numChangeHandler"></my-goods></uni-swipe-action-item></block></uni-swipe-action>

用ui-swipe-action组件,这里的item项,用:right-options来设置,之前是options属性

 options: [{text: '删除', // 显示的文本内容style: {backgroundColor: '#C00000' // 按钮的背景颜色}}]

实现选择收货地址

<!-- 选择收货地址的盒子 -->
<view class="address-choose-box" v-if="JSON.stringify(address) === '{}'"><button type="primary" size="mini" class="btnChooseAddress" @click="chooseAddress">请选择收货地址+</button>
</view>
// 选择收货地址async chooseAddress() {// 1. 调用小程序提供的 chooseAddress() 方法,即可使用选择收货地址的功能//    返回值是一个数组:第 1 项为错误对象;第 2 项为成功之后的收货地址对象const [err, succ] = await uni.chooseAddress().catch(err => err)// 2. 用户成功的选择了收货地址if (err === null && succ.errMsg === 'chooseAddress:ok') {// 为 data 里面的收货地址对象赋值this.address = succ}}

之前的API点击取消后,再次点选择地址不会再弹出是否确认授权,但是现在本来也就没有弹出框了,所以就没有了这个问题

三秒自动跳转到登录页面

若在没有登录情况下结算,会提示先登录,并会自动3秒后跳转到登录页面

1、展示倒计时的信息

// 展示倒计时的提示消息
showTips(n) {// 调用 uni.showToast() 方法,展示提示消息uni.showToast({// 不展示任何图标icon: 'none',// 提示的消息title: '请登录后再结算!' + n + ' 秒后自动跳转到登录页',// 为页面添加透明遮罩,防止点击穿透mask: true,// 1.5 秒后自动消失duration: 1500})
}

2、在data中声明秒数

data() {return {// 倒计时的秒数seconds: 3}
}

3、延迟导航到my页面

两个问题,一个是跳转之后,计时器还在,所以要在跳转之前清楚定时器,二是为了防止之后的数据不出错,重置秒数

// 延迟导航到 my 页面
delayNavigate() {// 把 data 中的秒数重置成 3 秒this.seconds = 3this.showTips(this.seconds)this.timer = setInterval(() => {this.seconds--if (this.seconds <= 0) {clearInterval(this.timer)uni.switchTab({url: '/pages/my/my'})return}this.showTips(this.seconds)}, 1000)
}

保存跳转的路径,在登录后跳转

在vuex中备好一个重定向对象

redirectInfo: null

然后在跳转之前

把重定向的对象给vuex

mutations: {// 更新重定向的信息对象updateRedirectInfo(state, info) {state.redirectInfo = info}
}

在映射之后,使用方法

// 跳转到 my 页面uni.switchTab({url: '/pages/my/my',// 页面跳转成功之后的回调函数success: () => {// 调用 vuex 的 updateRedirectInfo 方法,把跳转信息存储到 Store 中this.updateRedirectInfo({// 跳转的方式openType: 'switchTab',// 从哪个页面跳转过去的from: '/pages/cart/cart'})}})

然后在my-login组件中,在调用接口登录成功后,调用返回页面方式

// 调用登录接口,换取永久的 token
async getToken(info) {// 省略其它代码...// 判断 vuex 中的 redirectInfo 是否为 null// 如果不为 null,则登录成功之后,需要重新导航到对应的页面this.navigateBack()
}
// 返回登录之前的页面
navigateBack() {// redirectInfo 不为 null,并且导航方式为 switchTabif (this.redirectInfo && this.redirectInfo.openType === 'switchTab') {// 调用小程序提供的 uni.switchTab() API 进行页面的导航uni.switchTab({// 要导航到的页面地址url: this.redirectInfo.from,// 导航成功之后,把 vuex 中的 redirectInfo 对象重置为 nullcomplete: () => {this.updateRedirectInfo(null)}})}
}

微信支付

个人开发者是不能用微信支付相关的api的,所以这方面的几个api没法试,大概就是发请求判断是否支付成功再进行下一步操作

git的简单使用

1、创建分支

git checkout -b settle

2、写完该分支的代码后,先提交到暂存区,再提交到本地

git add .
git commit -m "完成了登录和支付功能的开发"

3、先转到mastwr分支,再合并分支,再push到远程

git checkout master
git merge settle
git push

4、删除本地的分支

git branch -d settle

分布

1、在HBuilderX,上面的分布,选微信小程序

 2、从微信开发者工具右上点发布

 3、完成微信小程序版本管理的基本信息步骤,即可。审核过后可正式上线

发布为android App

1、HBuilder X上,点开mainifest.json文件进行配置

 2、点击App图标配置,设置图标

 3、点分布中原生app-云打包

 4、勾选一下打包的配置

 5、在控制台看打包进度,比较慢一点,然后会有一个链接,打开目录,下载里面的apk安装包,安装到android手机中即可。

不过没有做多端适配,所以app的一些功能不能完整的正常运行,比如微信支付,收货地址等

其实上这个小程序也差不多半个月的时间完整了,很多都是之前vue的一些语法,只不过在一些细节上实现有不同。

更多推荐

uniapp黑马项目总结

本文发布于:2024-03-08 20:30:46,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1722230.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:黑马   项目   uniapp

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!