小程序
一、起步
1、开发者工具
2、开发者账户
- 注册地址:下一步下一步的注册
- 成功后进入页面,点开发=>开发设置,就可以看到AppID账号
3、创建项目
- 新建文件夹
- 打开开发工具,将文件地址输入,不选择云开发
- 然后就会自动初始化项目文件
4、预览项目
- 点开发工具预览功能,生成二维码
- 手机微信扫描查看
- 体验人员管理,在注册地址成功页面的成员管理进行配置
二、开发
1、文件介绍
-
project.content.js
:项目配置文件- 建议在详情内选项修改
- 自己可以改
AppID
和projectname
-
sitemap.json
:配置小程序索引,基本都是可索引 -
App.js
:注册小程序 -
app.json
:全局配置pages
:配置路由
那个路由在上面就那个为首页
window
:配置窗口表现,
导航栏颜色,字体,字色,窗口背景色,下拉loading,
是否下拉刷新,上拉到顶部距离,屏幕旋转
entryPagePath
:不按照pages顺序,指定首页- 替他略
-
app.wxss
:就是css
文件:微信不能使用ID选择器 -
pages:里面是页面文件
-
static:自己创建的文件夹,放置静态文件
2、开发语法
1、创建页面
- 在pages创建文件加,然后选中右键选中新增page,就会自动生成页面的文件,并在app.json里面注册路由
view
对于div
,text
对应span
- 适配:
1px = 2rpx
;设计图为750 - 注意:公共样式添加:
page { heigth: 100% }
2、当前页窗口配置
将app.json
里window的属性之间拿到当前页面的index.json
{
"usingComponents": {},
"navigationBarTitleText": "Hello" // 当前页面直接设置
// 局部优先级大于全局
}
js
动态修改
wx.setNavigationBarTitle({ title: '旭大王' })
// 文档 => api => 交互 => 导航栏
// 还有其他控制
3、数据绑定
1、创建初始化数据:
-
在页面文件下
index.js
里面page对象的data里面创建数据 -
可以通过调试器的appData查看初始化数据
-
视图层:
<text>{{ msg }}</text>
-
视图层:
<view class="{{ classList.one }}, {{ active ? 'active' : '' }}"></view>
-
js
:console.log(this.data.msg)
2、修改数据
js修改数据
:this.setDate({ msg: 'success' })
;同步
4、事件绑定
- 会冒泡:
<button bindTop="handleTap">按钮</button
- 不会冒泡:
<button catch="handleTap">按钮</button>
- 绑定的事件和data平级
Tap 相当于 click
touchstart
:手指点击:e.touches[0].clientY
touchmove
:手指移动touchend
:手指抬起
通过距离判断实现上拉自定义动画
style="transform: {{ coverTansform }}"
target
:可能是当前元素,可能是父元素
currentTarget
:肯定是当前元素
detail.value
:表单组件返回的value
事件入参:
<view
id="dataSet"
data-alpha-beta="1"
data-alphaBeta="2"
bindtap="bindViewTap"
> DataSet Test </view>
Page({
bindViewTap:function(event){
event.currentTarget.id === dataSet
event.currentTarget.dataset.alphaBeta === 1 // - 会转为驼峰写法
event.currentTarget.dataset.alphabeta === 2 // 大写会转为小写
cosnt value = event.detail.value // 表单组件返回的value
}
})
5、条件渲染
<view wx.if="{{ isUser }}">有用户</view>
<view wx.else >无用户</view>
6、列表渲染
-
语法:
wx:for
-
默认数组的当前项的下标变量名默认为
index
,数组当前项的变量名默认为item
<view wx:for="{{array}}"> {{index}}: {{item.message}} </view> <!-- 定义index, item别名 --> <view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName" > {{idx}}: {{itemName.message}} </view> <!-- key使用 --> <!-- arr=[{ name: 1 }, { name: 2 }] --> <view wx:for="{{ arr }}" wx:key="name"> {{ item.name }} </view>
7、自定义组件
-
新建components文件夹,文件夹创建
nav
文件,右键选中新建component -
新建后出现的4个文件和页面文件一样,但是不会自动添加路由
-
引入注册:在页面的
.json
文件里usingComponents
配置{ "usingComponents": { "HeadBox": "/components/HeadBox/HeadBox" } }
8、组件传值
组件间传值:无法传值
父传子:
<view>
父组件
<Child title="传入值">子组件</Child>
</view>
// 子组件的js里面会有properties, 接收传值
// 和vue的props类似,value 相当于 default
properties: {
title: {
type: String,
value: '默认值'
}
}
// 且properties的值会自带同步给data
子传父
- 父创建事件传递给子
- 子调用父事件进行传参
- 父事件通过事件进行回调
- 还可以通过promise的resolve触发then完成反向触发
9、父子事件
-
父传子:
<!-- 父组件 --> <component-tag-name bindmyevent="onMyEvent" />
// 父组件 onMyEvent(e) { console.log(e.detail.name) }
<!-- 子组件 --> <button bindtap="onTap">点击这个按钮将触发“myevent”事件</button>
// 子组件 onTap() { this.triggerEvent('myevent', { name: 'xu' }) }
-
子传父:
- 给父组件给子组件定义id
- 父组件通过
const son = this.selectComponent("#id")
获取子组件 - 再通过
son.go()
调用子组件go方法 - 组件列表使用时就不能使用了,需要通过其他方式实现
-
其他方式
- 使用第三方插件
pubsub-js
import Pubsub from 'pubsub-js' // 订阅 const pubsub = Pubsub.subscribe("订阅名", (msg, data) => { console.log(msg) // 这里将会输出对应设置的 pubsubID console.log(data) // 这里将会输出对应设置的参数 }) // 取消订阅 Pubsub.unsubscribe(pubsub) // 取消全部订阅 PubSub.clearAllSubscriptions()
import Pubsub from 'pubsub-js' // 发布 Pubsub.publish("订阅名", data) // 或 PubSub.publishSync("订阅名", data)
- 使用第三方插件
10、模板
-
创建templates文件夹,和pages同级
-
创建test文件夹,并新建页面
-
页面里面
wxml
写模板,模板样式协助<!-- 模板 --> <template name="msgItem"> <view class="username"> <text>username: {{ name }} </text> </view> </template>
/* 模板样式 */ .msgItem { height: 40px; border: 1px solid red; } .username { line-height: 30px; font-size: 16px; }
-
页面使用模板
<import src="/templates/test/test.wxml" /> <view> <template is="msgItem" data="{{...item}}"/> <!-- item = { name: '旭大王' } --> </view>
@import "/templates/test/test.wxss" /* 下面定义的样式会覆盖模板样式,注意命名 */
3、路由和导航组件
-
编程式跳转
-
wx.navigateTo()
wx.navigateTo({ url: '/pages/index/index', // 必须加根路径 events: { callback: (data) => { console.log(data) } } // 被打开页面给当前页面反馈 // 其他还有success回调,fail失败回调,complete跳转回调 success: (res) => { res.eventChannel.emit('reverse', { name: '大王' }) } }) // 会保留跳转记录
// 被打开页面 onLoad(options) { const eventChannel = this.getOpenerEventChannel() eventChannel.emit('callback', { name: '旭' }); // 调用上个页面的回调 eventChannel.on('reverse', (data) => { console.log(data) }); // 本页面给上个页面的回调 }
-
wx.navigateBack
:相当于history.go()
-
wx.redirectTo()
:不传值,不保存记录 -
wx.reLaunch()
:关闭其他,然后跳转,不传值 -
wx.switchTab()
:关闭其他,跳转到tabar页面
-
-
进行传值
上面的方法以及可以实现路由上下两个组件进行传参
通过get方式传参,传递query(缺点, 传递有限)
-
获取传值
获取get参数:
onLoad(options) {}
:options就是get传递的query -
tabBar
底部导航 -
全局
tabBar
就是直接在
app.json
里面定义tabBar
属性:和window,pages同级在文档 => 框架 => 全局配置 =>
tabBar
里面有说明顶部无法使用icon,会有下边框,且
2 <= list.length <= 5
"tabBar": { "backgroundColor": "#f0f0f0", "color": '#333', "selectedColor": "#d43c33", "position": "botton/top", "custom": false, "list": [ { "pagePath": "tabBar导航地址", "text": "tab文字", "iconPath": "未选中icon图片链接", "selectedIconPath": "选中icon图片链接" } ] }
-
自定义
tabBar
(可以局部,也可以全局)- 设置
custom
为true即可,然后配置的list
就会失效 - 在
pages
同级别下创建custom-tab-bar
文件夹并创建index组件 - 然后就可以在组件里面写自定义
tabBar
组件 - 注意不要使用
view
标签,可能会导致页面覆盖tabBar
- 使用
cover-view
和cover-image
组件替代view
- 设置
4、生命周期
页面生命周期
- 初始化:
onLoad() {}
:只执行一次 - 页面显示:
onShow() {}
- Dom渲染完成:
onReady() {}
:只执行一次 - 页面隐藏:
onHide() {}
;页面隐藏再次显示会触发onShow() {}
- 页面卸载(页面销毁):
onUnload() {}
组件生命周期:看自定义组件文档
5、数据store
1、数据store管理
-
在
app.js
文件创建globalData
对象存储store全局数据 -
在局部使用
const appInstance = getApp()
获取const appInstance = getApp(); Page({ onLoad() { console.log(appInstance.globalData.name) appInstance.globalData.name = "hello" } })
2、数据本地存储
和js
本地存储差不多:
同步:wx.setStorageSync
,多个异步方法wx.setStorage
同步:wx.getStorageSync
,多个异步方法wx.getStorage
还有remove, clear
6、授权
-
首次登录触发授权
<button bindgetuserinfo="handleGetInfo" open-type="getUserInfo">获取授权</button> <!-- 通过bindgetuserinfo获取信息 -->
-
授权后就可以使用
wx.getuserInfo()
;重复获取信息wx.getUserInfo({ success: () => {}, // 成功回调 fail() {} // 失败回调,错误不会给用户看到 complete() {} // 执行回调无论成败 }) // 用于授权后组件内onLoad周期内使用
7、组件
1、基础组件
小程序内置组件有轮播图,按钮等等原生组件
swapper
:previous-margin
调整前偏距swapper
:next-margin
调整后偏距
2、icon使用
- 使用案例icon,建立项目,和使用class名
- 在static里创建
iconfont
文件夹=>index.wxss
- 将icon提供的链接打开的代码复制到刚刚的
index.wxss
文件 - 在
app.wxss
里面引入@import "/static/iconfont/index.wxss"
- 使用:
<text class="iconfont icon-type-name"></text>
3、可滚动视图
微信内置组件scroll-view
实现上下滚动,左右滚动,下拉刷新等等页面滚动功能
8、请求
-
接口请求使用
wx.request()
进行 -
初始化请求会放在
onLoad
或onReady
里面 -
url
地址必须是完整的,且在小程序管理页面(就是注册成功后页面),开发 => 开发设置 => 服务器域名设置 => 添加服务器域名 -
最多添加20个域名,并发不能超过10个
-
必须是
https
协议,不是的话可以先在开发工具详情里面设置不校验也不需要去小程序管理页面添加域名,可以本地调试了
-
文件上传
wx.UploadTask()
-
文件下载
wx.downloadFile()
-
cookie携带:
- 通过登录时获取,存储到本地数据或者store
- 登录页面调整其他页面需要关闭其他页面调整,用于触发其他页面
onLoad
- 其他页面
onLoad
获取cookie,发送请求,将cookie放在head属性里面 wx.request
文档里head和url
同级
-
封装
// utils/request.js
9、内网穿透
- 解决真机调试的时候接口没有服务的问题
- 下载
uTools
软件进行操作 => 插件中心 => 内网穿透 - 然后配置就可以实现真机调试了,但不能预览调试
10、npm
使用
- 项目文件打开
cmd
运行npm init
- 开发者工具详情 => 项目配置 => 使用
npm
包 - 开发者工具 工具=> 构建
npm
- 然后就可以使用
npm
第三方插件了
11、获取登录凭证
- 使用
wx.login({ success(res) { console.log(res.code) } })
- 然后将code发送给后端
- 后端通过code生成验证token, 然后拥有了登录验证的功能
12、小程序分包
- 将大于
2M
小于16M
的包进行拆分
1、常规分包
-
在
app.json
里面配置subpackages
;这是一个数组"subpackages": [ { "root": "packageA", "pages": [ "pages/cat", "pages/dog" ] }, { "root": "packageB", "name": "pack2", "pages": [ "pages/apple", "pages/banana" ] } ]
-
root
:包的根路径名称 -
name
:包的别名 -
pages
:包的路由配置 -
然后和主包pages同级创建包根路径名称文件夹做分包,如:
packageA
-
然后
pageageA
里面也创建pages
文件夹,和主包一样结构 -
分包可以使用主包资源,路径修改下就可以了🌙
-
主包跳转分包是路由根路径为分包名
2、独立分包
-
就是在需要独立分包的配置加
"independent": true
{ "root": "packageB", "name": "pack2", "pages": [ "pages/apple", "pages/banana" ], "independent": true }
-
独立分的包无法使用主包资源,路由跳转和常规分包一样
3、分包预加载
-
在
app.json
里的preloadRule
里面进行配置"preloadRule": { "pages/index": { "network": "all", "packages": ["packageA"] }, "sub1/index": { "packages": ["packageA", "packageB"] }, "sub3/index": { "packages": ["packageB"] }, "indep/index": { "packages": ["__APP__"] } }
-
"pages/index"
:打开路由路径或打开包index路径 -
network
:指定网络模式,wifi还是其他 -
packages
:分包的root
或name
名称,其中主包叫__APP__
13、自定义组件详解
1、组件插槽
-
子组件模板
<view class='child'> <view>这是子组件</view> <slot></slot> </view>
-
父组件
<view class='father'> <component-tag-name> <!-- 这部分内容将被放置在组件 <slot> 的位置上 --> <view>这里是插入到组件slot中的内容</view> </component-tag-name> </view>
-
页面使用
<view> <Father><Child /></Father> </view>
-
多个slot和具名
<!-- child --> <view class='child'> <view>这是子组件</view> <slot name='slotA'></slot> <slot name='slotB'></slot> </view> <!-- father --> <view class'father'> <!-- 将propA和propB传递给子组件props --> <component-tag-name prop-a="{{dataFieldA}}" prop-b="{{dataFieldB}}" > <view slot="slotA">"slotA"中的内容</view> <view slot="slotB">"slotB"中的内容</view> </component-tag-name> </view>
2、数据
组件data里定义的数据
- 加
_
前缀:纯数据字段,不会被渲染,不会传递给其他组件 - 不加前缀:正常字段
3、数据监听
observers
=== watch
observers: {
// 监听obj所有值变化
'obj.**': function (obj) { console.log(obj) }
}
4、生命周期
created
===created
attached
===mounted
detached
===destroyed
5、组件父子传值
<!-- father -->
<view class'father'>
<!-- 将propA和propB传递给子组件props -->
<component-tag-name
prop-a="{{dataFieldA}}"
prop-b="{{dataFieldB}}"
></component-tag-name>
</view>
6、组件父子事件
<view class="father">
<component-tag-name bindmyevent="onMyEvent" />
</view>
// 父组件
onMyEvent(e) { console.log(e.detail.name) }
// 子组件
handleBtn() {
this.triggerEvent('myevent', { name: 'xu' })
}
三、开发经验
1、input获取值
<view>
<input type="text" id="text" bindinput="handleInput"/>
<input type="password" id="password" bindinput="handleInput"/>
</view>
handleInput(e) {
const type = e.currentTarget.id
this.setData({
[type]: e.detail.value
})
}
2、消息提示
- 微信原生提示:
wx.showToast({ title: '成功' })
- 还有替他提示:文档 =>
api
=> 界面 => 交互
3、video多个播放
-
video多个播放不符合逻辑
-
我们需要video播放下一个停止当前上一个video
-
wx.createVideoContext
:创造一个VideoContext
对象来操作video -
使用案例
<view wx:for="{{array}}" wx:key="src"> <video bindplay="handlePlay" src="{{item.src}}" id="{{item.data.id}}" /> </view>
handlePlay(e) { const vid = e.currentTaget.id; // 判断是否时同一个视频 && 判断上一个video控件是否存在 if (this.vid !== vid && this.VideoContent) { this.VideoContent.stop(); //停止视频播放 } this.vid = vid this.VideoContext = wx.createVideoContext(vid); }
4、image代替video
- 和原生一样使用video的
poster属性添加img链接
- 创建一个image和video同级
- 通过
wx:if="{{vid === item.data.vid}}"
判断video是否隐藏,实现优化 - 此时也解决了多个视频同时播放的问题
5、视频面积大小
使用video的object-fit
的属性设置
6、下拉刷新
- scroll-view
scroll-view
的refresher-enabled开启下拉刷新scroll-view
的bindrefresherrefresh
监视下拉是否触发scroll-view
的下拉可控状态:refresher-triggered
- 页面也有下拉刷新
- 页面有
onPullDownRefresh
生命周期函数,监视下拉触发 - 下拉需要全局或局部设置
enablePullDownRefresh
为true - 全局在
page.json
的window里面设置 - 局部就在
json
里面设置 - 页面下拉不需要可控状态还原
7、上拉加载
scroll-view
的上拉加载:bindscrolltolower
属性触发- 页面的上拉加载:
onReachBottom
生命周期函数进行监视触发
8、分享好友
-
button设置
open-type
为share就可以触发分享好友功能 -
用户点击页面上方头部分享按钮
头部分享菜单是创建
onShareAppMessage
生命周期函数才会有点击菜单后触发
onShareAppMessage
通过form判断是按钮触发的分享,还是头部菜单触发分享
-
自定义分享内容
onShareAppMessage
可以返回一个对象,用来自定义分享title: 标题 path: 分享页面路径
imageUrl
:分享图片
9、音乐播放
音乐播放使用背景音频功能:文档 => api
=> 媒体 => 背景音频
效果,关闭后还能继续播放
关键:BackgroundAudioManager
音频关键:在onLoad() {}
里面添加 播放监听,暂停监听,停止监听
播放,暂停,停止,音乐播放自然停止都是BackgroundAudioManager
的方法
还有进入更新监听事件,获取播放进度
播放进度/总时长 * 进度条宽长度 = 当前进度条宽长度
10、动画使用
-
通过
wx.createAnimation
创建一个动画对象,通常在onload
初始化时创建 -
将创建的对象添加到
this
上面,方便下面绑定动画时使用onload() { this.animation = wx.createAnimation({ duration: 2000 }) // createAnimation是可以进行传参配置的 }
-
创建动画
dom
<view animation="{{ animationData }}"></view>
-
创建动画值
data: { animationData: {} }
-
将动画对象赋值给动画值,触发
dom
的动画效果handleBtn() { // 先定义需要的动画效果,如旋转45度 this.animation.rotate(45) this.setData({ // export调用动画 animationData: this.animation.export() }) // 此时已经实现了动画调用 }
-
多动画同时调用
handleBtn() { this.animation.rotate(45).scale(2,2) this.setData({ animationData: this.animation.export() }) }
-
多动画依次调用
handleBtn() { // 使用step实现动画依次加载 // step停止上一次动画动作 this.animation.rotate(45).step().scale(2,2).step() this.setData({ animationData: this.animation.export() }) }
-
动画还原:就是将动画效果还原成初始状态