一、项目前期介绍
1、项目运行
安装node,安装wepy . npm install wepy-cli -g
命令行执行 npm install 安装依赖包
命令行执行 npm run dev 编译,产生小程序项目目录 dist.开启实时编译。(注:如果同时在微信开发者工具-->设置-->编辑设置中勾选了文件保存时自动编译小程序,将可以实时预览,非常方便。)
使用微信开发者工具-->添加项目,项目目录请选择dist目录。
(1)微信开发者工具-->项目-->关闭ES6转ES5。 __ 重要:漏掉此项会运行报错。
(2)微信开发者工具-->项目-->关闭上传代码时样式自动补全。 __ 重要:某些情况下漏掉此项也会运行报错。
(3)微信开发者工具-->项目-->关闭代码压缩上传。 __ 重要:开启后,会导致真机computed, props.sync 等等属性失效。(注:压缩功能可使用WePY提供的build指令代替,详见后文相关介绍以及Demo项目根目录中的wepy.config.js和package.json文件。)
2、vscode配置
首选项-->设置
{
"git.ignoreMissingGitWarning": true,
"editor.fontSize": 14,
"files.associations": {
".vue": "vue",
".wpy": "vue",
".wxml": "html",
".wxss": "css",
"*.wxs": "javascript"
},
"emmet.syntaxProfiles": {
"vue-html": "html",
"vue": "html"
},
"vetur.validation.template": false,
"editor.tabSize": 2
}
安装工具插件wpy-beautify,vetur, Vue 2 Snippets,vscode wxml,vscode-wechat,WeApp Snippets,Prettier formatter for Visual Studio Code等js,html,css相关格式化和自动补全插件.
3、开发技术介绍
wepyjs 框架:wepyjs文档
vuejs:https://cn.vuejs.org/v2/guide/
es6:http://es6.ruanyifeng.com/
redux中文文档:http://www.redux.org.cn/
小程序文档:https://mp.weixin.qq.com/debug/wxadoc/dev/component/
ui使用weui:https://github.com/Tencent/weui-wxss
demo 参考官网组件demo和小程序UIdemo:https://mp.weixin.qq.com/debug/wxadoc/dev/demo.html
weweb小程序转h5/ios/android框架:https://github.com/wdfe/weweb
二、项目结构
大致结构如下,在src下编辑,dist是打包后的目录。
app.wpy是启动文件,相当于小程序中的app文件,设置全局配置。
page 是页面目录,components 是组件目录,mixins是混合组件目录,store是redux文件目录,iamges 是图片字体目录 ,style是全局样式目录,util是工具函数目录,test是mock数据目录。
├── dist
├── node_modules
├── src
├── component
├── images
├── pages
├── profile
├── profile.wpy
├── orderDetail
├── payedOrderDetail
├── payedOrderDetail.wpy
├── unpayedOrderDetail
├── unpayedOrderDetail.wpy
├── refundOrderDetail
├── refundOrderDetail.wpy
├── styles
├── util
├── app.wpy
├── template.wpy
三、注意事项:
1、页面
(1)一个页面要单独放在一个文件夹下,否则打包编译后会出错
(2)每个页面都要在app.wpy配置
// 页面结构
// 对应于原生的.wxss
<style lang="less">
</style>
// 对应于原生的.wxml
<template lang="wxml">
<view>sdfdff</view>
<counter1></counter1>
</template>
<script>
import wepy from 'wepy';
import Counter from '../components/counter';
export default class Page extends wepy.page {
// 对应于原生的.json
config = {}
// 对应于原生的.js
components = {counter1: counter1}
data = { // 渲染数据对象,存放可用于页面模板绑定的渲染数据}
events = { //WePY组件事件处理函数对象,存放响应组件之间通过$broadcast、$emit、$invoke所传递的事件的函数}
methods = { //WePY中的methods属性只能声明页面wxml标签的bind、catch事件,不能声明自定义方法}
// 自定义方法和method平级
onLoad() {}
// 其他生命周期函数(onShow、onHide)
}
</script>
2、本地图片只能用<image>标签才能显示。如用background-image,url应为线上地址。
3、this.$apply()
WePY使用脏数据检查对setData进行封装,在函数运行周期结束时执行脏数据检查。但异步函数中更新数据的时候,必须手动调用$apply方法才能触发脏数据检查。
4、组件的使用
(1)子组件
// child.wpy
<script>
import wepy from 'wepy'
export default class child extends wepy.component {
// props为数据传递的参数
props = {
data: Object
}
components = { // 引用的组件}
data = {
// 渲染数据对象,存放可用于页面模板绑定的渲染数据
}
events = {
//WePY组件事件处理函数对象,存放响应组件之间通过$broadcast、$emit、$invoke所传递的事件的函数
}
methods = {
//事件处理函数(集中保存在methods对象中)}
}
</script>
(2)父组件:
<child :data.sync = "data"></child>
import Child from '../component/child'
components = {
child:Child
}
(3)组件间通信方式
1)$emit :子组件 ----> 父组件
2)$broadcast:父组件 ----> 子组件
3)$invoke:子组件 ----> 子组件
举个emit的栗子
// 子组件 (orderContent-cancelOrder为事件名, evt.currentTarget.dataset.item.pay为携带的数据)
methods = {
cancelOrder(evt) {
this.$emit('orderContent-cancelOrder', evt.currentTarget.dataset.item.pay)
},
}
// 父组件
events = {
'orderContent-cancelOrder': (args) => {
....
}
}
(4)组件slot
slot相当于一个插槽,在组件里先占位。在页面引入组件的时候,将页面中组件标签里对应的slot内容插入组件。
下面是引用一个弹窗组件的栗子
场景:需要一个【确认删除弹框】和一个【确认取消弹框】
思路:因为弹窗的样式一样,文案类似,只是按钮上绑定的事件不同,所以在组件中将slot留出来放置按钮。页面引用组件时,根据不同的情况判断用哪个slot。
// 组件 PopupComponent
<view class="popup-ten" wx:if="{{isStatus===9}}">
<block>
<view class="title">{{popData.title}}</view>
<view class="content">{{popData.text}}</view>
<slot name="btn"></slot>
<view class="guanbi" @tap="cancelHandle">
<icon type="cancel" size="40" color="white"></icon>
</view>
</block>
</view>
// 页面引用
<PopupComponent :popData.sync="popData" class="pop">
<view slot="btn" class="btn">
<button class="self-btn" catchtap="cancelOption" data-num="{{payNumber}}">{{popData.leftBtn}}</button>
<button class="launch-btn" catchtap="confirmCancel" wx:if="{{payStatus===0}}">{{popData.rightBtn}}</button>
<button class="launch-btn" catchtap="confirmDelete" wx:if="{{payStatus===2}}">{{popData.rightBtn}}</button>
</view>
</PopupComponent>
好处:这样可以将事件写在页面中,减少了页面与组件的通信,也使组件更具有通用性。
5、函数节流
场景:提交订单时重复点击造成短时间触发多次请求
思路:设置全局变量count,每次点击时根据count来决定是否发送请求(API.requestWrap(param3))。count每次点击后10秒清零。
// 设置全局变量,避免段时间出发多次请求
if (this.count === 0) {
this.count += 1
this.$apply()
API.requestWrap(param3)
const that = this
setTimeout(() => {
that.count = 0
}, 10000)
}
5、ios真机border显示不全
如图,部分年级的上border显示不全(年级标签的border是1rpx)
反复尝试,发现问题是活动名称与课程名称之间的1rpx分割线问题,如果将分割线的高度改为2rpx则无此bug。猜想是1rpx渲染成0.5px之后四舍五入被挤掉了。
以下是修改之前的分割线的css
.horizontalLine {
margin: 0 auto;
width: 686rpx;
height: 1rpx;
background-color: #ece9e9;
margin-top: 22rpx;
margin-bottom: 22rpx;
}
经修改,将1rpx高度变为2rpx,设置显示1rpx的下边框,这样就解决了年级标签的border显示不全的问题。
.horizontalLine {
margin: 0 auto;
width: 686rpx;
height: 2rpx;
border-bottom: 1rpx solid #ece9e9;
margin-top: 22rpx;
margin-bottom: 22rpx;
}
四、待优化
1、异步嵌套的优化
使用async、await简化代码