文章目录
微信小程序
微信小程序开发app安装、使用
微信开发者工具 app下载
https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html
一般选择稳定版就可以,然后根据本机电脑选择对应的版本。比如我的是Windows64。
注册、登录
https://mp.weixin.qq.com/
小程序开发主页
https://mp.weixin.qq.com/wxamp/index/index?lang=zh_CN
1、项目基础
1.1项目目录
1.2启动
- 项目—>新建项目—>目录(选择项目文件)—>AppID–>小程序—>不使用云服务、不使用模版。
- 详情—>本地设置—>调试基础库(选择版本)—>勾选:不校验合法域名、web-view(业务域名)、TLS版本以及HTTPS证书。
- 确认开发权限(把微信号发给管理者,添加项目的开发权限)
- 微信扫码快捷登录
- 工具—>构建npm—>构建成功
6.普通编译 —>添加编译模式。
2、tabBar配置
在 app.json 中,windows 同级的位置,写入:
{
"windows": {
...
},
"tabBar": {
"color": "#909192",
"selectedColor": "#0600B8",
"list": [{
"pagePath": "pages/home/home",
"text": "首页",
"iconPath": "images/bar/bar1_1.png",
"selectedIconPath": "images/bar/bar1.png"
},{
"pagePath": "pages/menu/menu",
"text": "菜单",
"iconPath": "images/bar/bar2_2.png",
"selectedIconPath": "images/bar/bar2.png"
},{
"pagePath": "pages/new/new",
"text": "新品",
"iconPath": "images/bar/bar3_3.png",
"selectedIconPath": "images/bar/bar3.png"
},{
"pagePath": "pages/kefu/kefu",
"text": "小二",
"iconPath": "images/bar/bar4_4.png",
"selectedIconPath": "images/bar/bar4.png"
}]
}
}
#pagePath 页面路径
#text tabBar文字信息
#iconPath tabBar图片路径
#selectedIconPath tabBar选中的图片路径
在pages的同级目录下新建images文件,在里面新建bar文件放图片
3、配置页面
在app.json:
{
"pages": [
"pages/home/home"
],
...
}
4、页面文件介绍
pages 目录下,每个页面就是一个文件夹,一个页面包含wxml、wxss、js和json文件,其中:
wxml:相当于html文件
wxss:相当于css文件
#1、标签
标签分单双标签,书写格式与html完全一样,但在微信小程序里,没有div、span、p、h1-h6这些标签,它只有非常简单的两个最基本的标签:view(即:div) 与 text(即:span)。当然,还有其他经过封装的组件,我们后面会再做介绍。
#2、JavaScript
微信小程序与vue.js框架用法大致相同,同属MVVM框架,都是由数据驱动视图更新。因此,js事件和变量的书写,也会与原生JS稍有差异。
5、数据绑定
在 home.wxml 页面写入:
<view>{{msg}}</view>
在 home.js 写入:
Page({
data: {
msg: '你好,世界'
}
})
如此,便完成了数据绑定。
可以看到,动态变化的数据,我们统一放在data里面。这里跟vue做个区别,vue中规定组件内data必须是函数,而小程序不用,使用一个对象表示即可。
小程序中,我们要记住,凡是调用data中的数据,在wxml文件使用时,就需要加 {{}},这语法便是著名的:mustache语法,也称胡子语法。
6、双向数据绑定
#1、修改data中的数据
小程序中,使用 this.setData() 来修改data中的数据。
#2、方法绑定
一个标签需要调用一个函数(或称:方法),需要在标签上绑定事件,如绑定触摸事件(即:pc上的点击事件):
<button bindtap="tapFn">按钮</button>
然后在js中:
Page({
data: {
...
},
tapFn(){
// do something...
}
})
#3、实现双向数据绑定
我们来实现双向数据绑定:
在 home.wxml 中:
<input value="{{msg}}" bindinput="iptFn"></input>
<view>{{msg}}</view>
在js中:
Page({
data: {
msg: '你好,世界'
},
// input值被修改时触发的函数
iptFn(e){
this.setData({
msg: e.detail.value
})
}
//修改数据是同步的,但视图更新是异步的
})
#4、事件传值bindtap、data-xxx
<view data-thisIndex="{{index}}" bindtap="bindViewTap">
删除
</view>
bindViewTap(e) {
let _index = e.currentTarget.dataset.thisIndex;
console.log(_index );
}
7、 列表循环的基础用法
<view>
<view wx:for="{{list}}" wx:key="index">{{item}}</view>
</view>
wx:key可以不写,但会报警告
wx:key 的值有三种写法
1、wx:key=“property” ,其中property是代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。类似于字典的key值
2、wx:key=“*this”, 保留关键字 *this 代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字
3、wx:key=“index”,代表每一项的索引
比较推荐*this
自定义列表渲染:
当有多个循环时,可以写wx:for-index=“ind”,wx:for-item=“items”,来区分每一个循环。
如
<view>
<view wx:for="{{list}}" wx:key="*this" wx:for-item="items" wx:for-index="ind">{{ind}} {{items}}
</view>
</view>
ind/items都是自定义名称,其中的ind为索引,items为数组每一项。
8、显示与隐藏
if 和 hidden
wx:if 有更高的切换消耗,hidden有更高的初始渲染消耗
例1
<text wx:if="{{isShare==false}}" bindtap="bindAccount" >点我分享</text>
<text wx:if="{{isShare==true}}" >分享成功</text>
例2
<text wx:if="{{isShareStatus==0}}" bindtap="bindAccount" >点我分享</text>
<text wx:elif="{{isShareStatus==1}}" >分享成功</text>
<text wx:else>分享失败</text>
例3
<view hidden="{{isShow}}">显示与隐藏</view>
hidden与if相反,isShow的值为false的时候上面的view才会显示
data: {
isShow:false
}
9、事件汇总
bindtap 点击事件(冒泡的)
catchtap 点击事件(非冒泡的)
bindinput 键盘输入事件
bindconfirm 回车事件
bindfocus 输入框聚焦事件
bindblur 输入框失焦事件
bindchange 值改变事件
触摸事件
bindtouchstart 动作开始
bindtouchend 动作结束
bindtouchmove 触摸移动
bindtouchcancel 触摸动作被打断
表单重置事件
<form bindreset="formReset" >
<button type="warn" form-type="reset">重置</button>
</form>
表单提交事件
<form bindsubmit="formSubmit"" >
<button type="primary" form-type="submit"">提交</button>
</form>
10、使用模板
类似于vue的插槽
在pages > home 下新建文件夹 templates ,然后在里面新建test.wxml :
<!-- 模板1 -->
<template name="box1">
<view bindtap="btnFn">box1 :{{content}}</view>
</template>
<!-- 模板2 -->
<template name="box2">
<view bindtap="btnFn">box2 :{{content}}</view>
</template>
<!-- 模板3 -->
<template name="box3">
<view bindtap="btnFn">box3 :{{content}}</view>
</template>
在 home.wxml中:
<import src="./templates/test" />
<template is="box3" data="{{content: txt}}"></template>
这里通过import标签引入模板,template标签使用模板,is属性指定对应的模板,data属性传值
在 home.js中:
Page({
data: {
txt: 999
},
btnFn() {
console.log('打印');
}
})
以上在需要使用模板的 home.js 里面定义了内容txt 和方法btnFn
11、WXS 模块
WXS 代码可以编写在 wxml 文件中的 标签内,或以 .wxs 为后缀名的文件内。
模块
每一个 .wxs 文件和 标签都是一个单独的模块。
每个模块都有自己独立的作用域。即在一个模块里面定义的变量与函数,默认为私有的,对其他模块不可见。
一个模块要想对外暴露其内部的私有变量与函数,只能通过 module.exports 实现。
wxs写法1:
在 pages > home 文件夹下新建 tools.wxs 文件,并定义方法fn,通过module.exports 导出
function fn (a,b){
return a+b
}
module.exports ={
fn:fn
}
home.wxml 中 通过src属性引入模块,module属性定义当前模块名称
<wxs src="./tools.wxs" module="foo"></wxs>
<view>{{foo.fn(1,4)}}</view>
写法二:
在home.wxml 中:
<wxs module="koo">
function add (val){
var a = val + 1
return a
}
module.exports={
add:add
}
</wxs>
<view >{{koo.add(0)}}</view>
12、组件化开发
- 在app.json 中创建组件
和创建页面一样,只是把pages改成components
新增 “components/product/product”
{
"pages": [
"pages/home/home",
"components/product/product"
],
.....
}
- 组件中写入结构样式,并声明
在product.wxml中:
写好页面结构
<view class="product_list">
<view wx:for="{{showArr}}" wx:key="*this" class="product_list_box">
<image class="img" src="{{item.imgSrc}}"></image>
<view class="txt">{{item.imgTxt}}</view>
</view>
</view>
在product.js中:
写好数据,图片我放在images文件下的home文件
// 注意注意:这里的Page()方法改成了Component()方法
Component({
data: {
showArr: [{
imgTxt: "卡布奇诺",
imgSrc:"../../images/home/logo1.jpg"
},
{
imgTxt: "白咖啡",
imgSrc:"../../images/home/logo2.jpeg"
},
{
imgTxt: "拿铁咖啡",
imgSrc:"../../images/home/logo3.jpeg"
}
]
},
})
在product.wxss中:
写好样式
.product_list{
display: flex;
padding: 20rpx 2%;
justify-content: space-between;
flex-wrap: wrap;
}
.product_list_box{
width: 49%;
box-shadow: 0 0 6rpx #ccc;
border-radius: 10rpx;
overflow: hidden;
margin-bottom: 20rpx;
}
.product_list_box .img{
width: 100%;
display: block;
height: 280rpx;
}
.product_list_box .txt{
height: 50rpx;
line-height: 50rpx;
text-align: center;
background-color: #eee;
}
在product.json中:
声明组件 “component”: true
{
"usingComponents": {},
"component": true
}
- 调用组件
在home.json 中:
注册组件
{
"usingComponents": {
"product-block": "/components/product/product"
}
}
在home.wxml 中 :
引入组件
<product-block />
或:
<product-block></product-block>
13、组件传值
考虑到组件需要复用,那么数据应该在父组件传过来。
父传子
我们将该组件的数据迁移到父组件 Home.js 中,然后通过父传子传过来:
父组件
在home.wxml :
<product-block showArr="{{showArr}}" ></product-block>
在home.js :
Page({
data: {
showArr: [{
imgTxt: "卡布奇诺",
imgSrc: "../../images/home/logo1.jpg"
},
{
imgTxt: "白咖啡",
imgSrc: "../../images/home/logo2.jpeg"
},
{
imgTxt: "拿铁咖啡",
imgSrc: "../../images/home/logo3.jpeg"
}
]
}
})
子组件
在product.js :
Component({
//通过properties接收父组件传过来的值,类似于vue的props
properties:{
showArr:{
//定义数据类型
type:Array,
//定义数据的初始值
value:[]
}
}
})
子传父
我们可以通过子组件的自定义事件this.triggerEvent ,点击product 组件中的任意一项,来触发父组件的事件。
子组件
在product.wxml :
<view class="product_list">
<view wx:for="{{showArr}}" wx:key="*this" class="product_list_box" bindtap="productFn">
<image class="img" src="{{item.imgSrc}}"></image>
<view class="ve">{{item.imgTxt}}</view>
</view>
</view>
在product.js :
Component({
//组件中写方法需要写上methods
methods:{
productFn(){
//子传父通过triggerEvent, 类似vue的$emit
this.triggerEvent('eventFn','触发了事件')
}
}
})
父组件
在home.wxml :
<!-- 标签上使用bind来接收子组件传过来的事件 -->
<product-block showArr="{{showArr}}" bind:eventFn="homeEvent"></product-block>
在home.js :
Page({
...,
// 页面无需写methods
homeEvent(val) {
console.log(val.detail);
}
})
点击之后,控制台打印了 :触发了事件。
14、全局变量globalData
定义全局变量globalData,在app.js中:
// app.js
App({
globalData: {
name: '张三'
}
})
调用的方法有:
【方法一(推荐)】通过调用app获取,需要在第一行声明app的实例
const app = getApp()
Page({
onLoad: function () {
console.log(app.globalData.name);
},
})
【方法二】直接通过getApp().globalData获取,不需要写const app = getApp()实例
Page({
onLoad: function () {
console.log(getApp().globalData.name);
},
})
修改global的方法【借助方法一】:
app.globalData.name = "李四";
15、路由跳转
1、跳转到非tabBar 的页面(js)
wx.navigateTo({
url: '/pages/nav/nav'
})
// url携带参数的形式:
wx.navigateTo({
url: '/pages/nav/nav?pid=123',
})
// 如何获取参数?
onLoad: function (options) {
console.log(options.pid) // 123
},
// 携带复杂参数的形式
wx.navigateTo({
url: '/pages/nav/nav?pid=123',
//跳转成功,通过eventChannel向被打开页面传送数据
success:function(res){
res.eventChannel.emit('pageInfo',{name:'用户'})
}
})
// 接收复杂参数的形式
onLoad: function (options) {
console.log(options.pid); //123
const channel = this.getOpenerEventChannel()
//使用getOpenerEventChannel()方法,通过.on方法获取
channel.on('pageInfo',function(data){
console.log(data); //{name: "用户"}
})
},
// 当前页反向发射数据到上一页
onLoad: function (options) {
const channel = this.getOpenerEventChannel()
//使用getOpenerEventChannel()方法,通过.emit方法触发
channel.emit('fanxiang',{arr:[11,22,33]})
},
// 上一页接收反向发射的数据
wx.navigateTo({
url: '/pages/nav/nav?pid=123',
// events中写反向发射的函数
events:{
fanxiang(res){
console.log(res.arr); //[11,22,33]
}
}
})
2、跳转到非tabbar页面(wxml)
<navigator url="/pages/logs/logs"></navigator>
3、跳转到tabbar页面(js)
wx.switchTab({
url: '/pages/user/user'
})
16、小程序生命周期
页面生命周期
onLoad > onShow > onReady >onHide > onShow > onUnload
组件生命周期
Component({
lifetimes: {
created : function() {
// 在组件实例刚刚被创建时执行 1.6.3版本以上
},
attached: function() {
// 在组件实例进入页面节点树时执行 1.6.3版本以上
},
detached: function() {
// 在组件实例被从页面节点树移除时执行 1.6.3版本以上
},
ready: function() {
// 在组件在视图层布局完成后执行 1.6.3版本以上
},
moved: function() {
// 在在组件实例被移动到节点树另一个位置时执行 1.6.3版本以上
}
},
// 以下是旧式的定义方式,可以保持对 <2.2.3 版本基础库的兼容
attached: function() {
// 在组件实例进入页面节点树时执行
},
detached: function() {
// 在组件实例被从页面节点树移除时执行
},
// ...
})
组件所在页面的生命周期:
Component({
pageLifetimes: {
show: function() {
// 页面被展示 2.2.3版本以上
},
hide: function() {
// 页面被隐藏 2.2.3版本以上
},
resize: function(size) {
// 页面尺寸变化 2.4.0版本以上
}
}
})