从最早发布的微信小程序,到后来的支付宝小程序、钉钉小程序,字节跳动小程序、百度小程序、QQ小程序等,面对这么多套的代码,开发者去编写多套原生代码的成本显然非常高,使用H5的话体验又没有原生好,这时候只需编写一套代码,就能够适配多端的能力就显得尤为需要。
下面进入正题,给大家介绍下uni-app字节小程序的开发
- 默认头条小程序的APPID已申请成功
- 安装开发工具 百度小程序开发者工具 字节跳动开发者工具 HBuilderX 或者其他自己喜欢的IDE都可以
新建项目
可以通过 HBuilderX可视化界面 以及 vue-cli命令行 方式进行创建
下面主要介绍下通过vue-cli命令行这中方式来新建项目
- 全局安装vue-cli
npm install -g @vue/cli 复制代码
- 创建
vue create -p dcloudio/uni-preset-vue user-uni-order 复制代码
安装成功后提示选择模板,我们选择默认模板就可以了
项目整体流程
用户下单最短流首页下单-> 订单状态-> 完成支付, 如下:
综上我们需要做的页面维度: 首页,地址检索,城市选择,登录,个人中心,订单列表,webview(收费标准 , 预估价格, 订单状态, 订单详情,法律条款)
制定目录结构
┌─components //uni-app组件目录 │ └─comp-a.vue //可复用的a组件 ├─common // 通用的js&css工具等 ├─hybrid //存放本地网页的目录 ├─platforms //存放各平台专用页面的目录 ├─pages //业务页面文件存放的目录 │ ├─index │ │ └─components // 页级别组件 │ │ └─vuex // index页面vuex主要存放index的逻辑 │ │ └─index.vue // index页面 ├─static //存放应用引用静态资源(如图片、视频等) │ ├─mp-weixin //条件编译png │ │ └─a.png │ │ └─b.png ├─store // 状态统一管理,将各个页面的vuex汇总 ├─service // 汇总请求,api等 │ └─api.js // 接口api相关 │ └─config.js // 环境配置 │ └─index.js │ └─request.js // 网络请求 ├─ttcomponents // 头条小程序自定义组件存放目录 ├─main.js //Vue初始化入口文件 ├─App.vue //应用配置,用来配置App全局样式以及监听 ├─manifest.json //配置应用名称、appid、logo、版本等打包信息 └─pages.json //配置页面路由、导航条、选项卡等页面类信息 复制代码
运行项目
想运行到哪个平台小程序,首先需要把相应的APPID, IDE路径对应填写正确
npm run dev:mp-toutiao // 实时监听编译 复制代码
运行成功如下提示:
此时打开字节跳动IDE进行导入操作,就可以看见页面啦~~~
Tips:使用字节跳动编译器打开uni-app编译的小程序时,必须进行导入操作,而不是新建,因为新建会默认成代码片段,虽然也可以实时预览效果但是会导致上传功能确实
首页开发
- 页面效果
- 首页目录结构
项目中其他页面的目录结构与首页均相同,后面不做多余赘述。
├─pages │ ├─index │ │ └─components │ │ └─vuex │ │ │ └─index.js // 首页逻辑 │ │ └─index.vue 复制代码
- 我们使用vuex来管理状态,每个页面都有自己的vuex, 其中index.js存放对应页面相关逻辑,为了避免频繁切换目录,把state, mutations, actions放在一个文件下,使用时并启用vuex的模块化,如下
const IndexPage = { namespaced: true, // 启用模块化vuex state: { ... // 需要共享的状态 }, mutations: { ... // 一些方法 }, actions: { ... // 请求相关 } } export default IndexPage //最后导出IndexPage 复制代码
- 各个页面的vuex统一放在store里
import Vue from 'vue' import Vuex from 'vuex' import IndexPage from 'https://www.eyoucms.com/wxmini/doc/pages/index/vuex' import AddressSearch from 'https://www.eyoucms.com/wxmini/doc/pages/address/vuex/index' import CityListPage from 'https://www.eyoucms.com/wxmini/doc/pages/city-list/vuex/index' Vue.use(Vuex) const store = new Vuex.Store({ state: { ... // 全局共用的状态 }, mutations: { }, actions: { }, modules: { IndexPage, // 首页vuex AddressSearch, // 地址检索页vuex CityListPage, // 城市列表页vuex }, }) export default store 复制代码
- 最后,在main.js里面引用
import Vue from 'vue' import App from 'https://www.eyoucms.com/wxmini/doc/course/App' import store from 'https://www.eyoucms.com/wxmini/doc/course/store' Vue.config.productionTip = false App.mpType = 'app' const app = new Vue({ ...App, store }) app.$mount() 复制代码
完整的首页逻辑交互框架就搭建成功了,以下是开发首页时遇见的问题
首页开发遇到的问题
- 使用swiper轮播组件,写好子组件,父组件因为无效果
问题原因:引入的 import swiper from "https://www.eyoucms.com/wxmini/components/swiper/swiper" ,导致把自定义的swiper覆盖,所以不展示
解决:引入的 import uniSwiper from "https://www.eyoucms.com/wxmini/components/swiper/swiper" ,不和原组件命名冲突即可
- 转百度小程序header报错
问题原因:百度设置http请求header如果有中文字符
解决:使用条件编译,如果是百度小程序需要encodeURI 一下, 或者删除header的中文部分
- uni-app的image标签,在小程序端不支持动态引入图片
// 引不到 <image class="tip_icon" src=https://www.eyoucms.com/wxmini/doc/course/"/static/sender{{endPoint.address ? '' : '_default'}}.png"/> 复制代码
// 可以引入 <image class="tip_icon" src=https://www.eyoucms.com/wxmini/doc/course/"/static/sender.png"/> 复制代码
- uni.getLocation() 只能获取经纬度,获取不到详细地址信息
- 非 h5 平台 :key 不支持表达式 由于:key=" timer__${idx} "的写法,编译时控制台会警告,但是不影响页面
<view class="column_item" v-for="(item, idx) in item" :key="`timer__${idx}`" // 改成:key="idx" 即可 > {{item == "立即用车" ? "" : index == 1 ? "时" : index == 2 ? "分" : ""}} </view> 复制代码
- 父子组件传参,类型定义不对不提示错误信息,只展示null, 所以遇到null问题可以查看是否是类型定义不一致
- uni-app中的watch不支持监听小程序,如下
watch: { searchType (to) { if (to) { // 如果是起始地回填起始地信息否则回填目的地信息 if (to === SEARCH_TYPE.START) { this.detailAddress = this.startAddress.detailAddress || '' } else { this.detailAddress = this.endAddress.detailAddress || '' } } } } 复制代码
改成
mounted () { if (this.searchType === SEARCH_TYPE.START) { this.detailAddress = this.startAddress.detailAddress || '' } else { this.detailAddress = this.endAddress.detailAddress || '' } } 复制代码
个人中心开发
- 页面效果 个人中心主要涉及的H5页面以及小程序的授权登录功能。所以主要部分就是webview的实现。
- webview的实现
// template <web-view id='web-view' v-if='src' :src='src' @bindmessage='onmessage'></web-view> 复制代码
onLoad (options) { console.log('H5入口页获取到的参数', options) let { src, needLogin} = options if(!needLogin){ this.src = decodeURIComponent(src) return } // 需要登录的 就先获取临时token this.fetchTempToken(src) } 复制代码
如果不需要登录的H5我们直接赋值到src即可,需要登录才能正常访问的页面,首先要获取临时token,拿到临时token后回传给服务端并且采用中间页redirectUrl的形式跳转。
个人中心开发遇到的问题
- 向网页传递信息要使用头条api的bindmessage
官方说明“网页向小程序 postMessage 时,会在特定时机(小程序后退、组件销毁、分享)触发并收到消息”
// 在小程序中调起H5中的打电话功能 onmessage (e) { let { phoneNumber, name } = e.detail if(name == 'makePhoneCall'){ uni.makePhoneCall({ number: phoneNumber }) } } 复制代码
需要注意的web-view的bindmessage属性并不是实时的
- 真机拨打电话功能不能用
// 使用uni.makePhoneCall真机没反应 uni.makePhoneCall({ phoneNumber: '114'}); 复制代码
解决:改为头条api的tt开头
// 真机模拟器均可正常使用 tt.makePhoneCall({ phoneNumber: '114'}); 复制代码
登录开发
-
头条授权登录效果
-
百度授权登录效果
-
大致思路:
1.首先获取获取服务供应商的信息
2.调用 uni.getProvider 获取授权code
3.获取用户的手机号(用户登录头条app的)
4.从 @getphonenumber 回调中获取到用户信息
5.调用授权登录服务api
6.获取token, openid等信息
// template <view class="login-page"> <view class="title"> <view class="h-line"></view> <view class="page-title">授权登录更快捷</view> <view class="h-line"></view> </view> <view class="authLogin-wrapper"> <!-- #ifdef MP-BAIDU --> <button type="default" open-type="getPhoneNumber" @getphonenumber="authLoginTap" class="login authLogin">百度登录更快捷</button> <!-- #endif --> <!-- #ifdef MP-TOUTIAO --> <button type="default" class="login authLogin" open-type="getPhoneNumber" @getphonenumber="onGetPhoneNumber" >授权手机号快捷登录</button> <!-- #endif --> </view> </view> 复制代码
// 完成渲染调用授权code方法 mounted () { this.getCode() } 复制代码
// 获取授权code方法 async getCode () { const [ errorProvider, provider ] = await uni.getProvider({ service: 'oauth' }) if (errorProvider) { console.log('获取provider失败') return } const [ errLogin, data ] = await uni.login({ provider: provider.provider[0], force: true }) if (errLogin) { console.log('获取code失败') // 失败的操作,提示等 return } const { code } = data this.code = code }, // 头条获取到用户信息 async onGetPhoneNumber ({ detail }) { const { errMsg } = detail // 授权失败 if (errMsg.indexOf('auth deny') > -1) { // 取消授权进行手机验证码登录 return } try { // 调用服务授权接口 const { data } = await authLogin({ code: this.code, ...detail, }) if (data.code === SUCCESS) { // 存token, openid等操作 // 重新更新个人信息 } else { // 失败的提示等 } } catch (error) { // 登录失败异常情况处理 } }, // 百度获取到用户信息同理头条。。。 复制代码
登录开发遇到的问题
手机验证码开发时,引入 checkbox-group 报错,如下图:
原因: components : { [CheckBox.name]: CheckBox } 引入组件方式不支持
以字节跳动为例子,打开字节跳动开发者工具,在工具栏找到上传,填写版本号,发布。版本号不和上一次冲突就可以。
Tips: 前面有提过,新建代码片段是在开发者工具上是没有上传按钮的,要导入项目才可以。
上传成功后,会提示进入小程序开发者平台,现在可以看到开发者的版本。
上图二维码就可以只作为本次的体检版本来扫一扫了。
- 前置准备,在后台配置好相关线上域名
- 切换到线上环境
// 环境相关配置 export const ENV = { // 开发环境 RD: 'rd', // 测试环境 TEST: 'test', // 沙箱环境 BOX: 'box', // 线上环境 ONLINE: 'online' } // 环境切换 export function getCurrentEnv() { return ENV.onLINE // 正式环境切到online } 复制代码
- 在开发者工具中点击上传
- 去小程序开发者平台提审发布
- 发布成功后可在头条搜索栏中搜到,抖音的话目前只有安卓平台上线了小程序功能。