环境准备
注册账号
访问注册页面注册⻚⾯,完成注册(建议使用新邮箱)。
获取APPID
登录微信小程序,找到开发
、开发设置
获取APPID。
开发工具
第一个微信小程序
打开开发者工具
登录需要扫码登录
新建项目
填写项目信息,复制获取的AppID,后端服务暂时选择不使用云服务。
开发工具
结构目录
⼩程序框架的⽬标是通过尽可能简单、⾼效的⽅式让开发者可以在微信中开发具有原⽣APP体验的服务。
文件结构与传统web对比
- 传统web:
- 结构 HTML
- 样式 CSS
- 逻辑 Javascript
- 配置 ⽆
- 微信⼩程序
- 结构 HTML
- 样式 WXSS
- 逻辑 Javascript
- 配置 JSON
基本目录结构
- pages :页面文件夹
- index :首页文件夹
- index.js :逻辑文件
- index.json :配置文件
- index.wxml :配置文件
- index.wxss :样式文件
- index :首页文件夹
- logs :日志页面
- logs.js
- logs.json
- logs.wxml
- logs.wxss
- utils :第三方工具js文件夹
- util.js
- app.js :项目的全局入口文件
- app.json :全局配置文件
- app.wxss :全局样式文件
- project.config.json :项目配置文件 AppID
- sitemap.json :微信索引配置文件
配置文件
配置文件不能出现注释
全局配置文件 app.json
小程序根目录下的 app.json 文件用来对微信小程序进行全局配置,决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等。
完整配置项说明请参考小程序全局配置
以下是一个包含了部分常用配置选项的 app.json :
{
"pages": [
"pages/index/index",
"pages/logs/index"
],
"window": {
"navigationBarTitleText": "Demo",
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "WeChat",
"navigationBarTextStyle":"black"
},
"tabBar": {
"list": [{
"pagePath": "pages/index/index",
"text": "首页"
}, {
"pagePath": "pages/logs/index",
"text": "日志"
}]
},
"networkTimeout": {
"request": 10000,
"downloadFile": 10000
},
"debug": true,
"navigateToMiniProgramAppIdList": [
"wxe5f52902cf4de896"
]
}
页面配置 page.json
这⾥的page.json
其实⽤来表⽰⻚⾯⽬录下的page.json
这类和⼩程序⻚⾯相关的配置。开发者可以独⽴定义每个⻚⾯的⼀些属性,如顶部颜⾊、是否允许下拉刷新等等。每一个小程序页面也可以使用同名 .json
文件来对本页面的窗口表现进行配置,页面中配置项会覆盖app.json
的 window
中相同的配置项。
完整配置项说明请参考小程序页面配置
sitemap 配置
通过 sitemap.json 配置,或者管理后台页面收录开关来配置其小程序页面是否允许微信索引。当开发者允许微信索引时,微信会通过爬虫的形式,为小程序的页面内容建立索引。当用户的搜索词条触发该索引时,小程序的页面将可能展示在搜索结果中。 爬虫访问小程序内页面时,会携带特定的 user-agent:mpcrawler 及场景值:1129。需要注意的是,若小程序爬虫发现的页面数据和真实用户的呈现不一致,那么该页面将不会进入索引中。sitemap
模板语法
WXML(WeiXin Markup Language)是框架设计的⼀套标签语⾔,结合基础组件、事件系统,可以构建出⻚⾯的结构。
数据绑定
普通写法
<view>{{name}}</view>
Page({
data:{
name:'丽丝'
}
})
组件属性
<view id="item-{{id}}"></view>
Page({
data:{
id:0
}
})
bool类型
不要直接写checked="false"
,其计算结果是⼀个字符串
<checkbox checked="{{false}}"></checkbox>
运算
三元运算
<view hidden="{{ flag ? true : false }}"></view>
<view>
{{10%2===0 ? '偶数' : '奇数'}}
</view>
算术运算
<view>
{{a+b}}+{{c}}+d
</view>
Page({
data:{
a:1,
b:3,
c:5
}
})
逻辑判断
<view wx:if="{{length > 5}}"></view>
字符串运算
<view>{{"hello" + name}}</view>
列表渲染(遍历)
wx:for
项的变量名默认为 item
wx:for-item
可以指定数组当前元素的变量名
下标变量名默认为 index
wx:for-index
可以指定数组当前下标的变量名
wx:key
⽤来提⾼数组渲染的性能
wx:key
绑定的值有如下选择:
string
类型,表⽰循环项中的唯⼀属性- 保留字
*this
,它的意思是item
本⾝,*this
代表的必须是唯⼀的字符串和数组。
<view>
<view wx:for="{{ list }}" wx:for-item="item" wx:for-index="index" wx:key="id">
索引:{{ index }}
--
值:{{ item.name }}
</view>
</view>
<view>
<view>对象循环</view>
<view wx:for="{{person}}" wx:for-item="value" wx:for-index="key" wx:key="age">
属性:{{key}}
--
值:{{value}}
</view>
</view>
block
渲染⼀个包含多节点的结构块?block最终不会变成真正的dom元素
<view>
<view>对象循环</view>
<block wx:for="{{person}}" wx:for-item="value" wx:for-index="key" wx:key="age" class="my_list">
属性:{{key}}
--
值:{{value}}
</block>
</view>
条件渲染
wx:if
使用wx:if="{{condtion}}"
判断是否需要渲染代码块。
<view>条件渲染</view>
<view wx:if="{{true}}">显示</view>
<view wx:if="{{false}}">隐藏</view>
<view wx:if="{{true}}">1</view>
<view wx:elif="{{false}}">2</view>
<view wx:else>3</view>
hidden
<view hidden="{{condition}}"> True </view>
频繁切换使用 hidden
,不频繁则 wx:if
事件的绑定
- wxml页面
<view>输入:
<input type="text" bindinput="handleInput"/>
</view>
<view>
{{num}}
</view>
- js page
Page({
/**
* 页面的初始数据
*/
data: {
num:0
},
// 输入框测试
handleInput(e){
this.setData({
num:e.detail.value
});
console.log(e.detail.value);
},
})
事件传值通过标签⾃定义属性的⽅式和 value
<button bindtap="handletap" data-operation="{{1}}">+</button>
<button bindtap="handletap" data-operation="{{-1}}">-</button>
<view>
{{num}}
</view>
// 加减
handletap(e){
const nums = e.currentTarget.dataset.operation;
this.setData({
num:this.data.num += nums
})
},
样式 WXSS
WXSS(WeiXin Style Sheets)是⼀套样式语⾔,⽤于描述 WXML的组件样式。
与CSS相⽐,WXSS扩展的特性有:
- 响应式⻓度单位 rpx
- 样式导⼊
尺寸单位
尺寸单位
- rpx(responsive pixel): 可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。
设备 | rpx换算px (屏幕宽度/750) | px换算rpx (750/屏幕宽度) |
---|---|---|
iPhone5 | 1rpx = 0.42px | 1px = 2.34rpx |
iPhone6 | 1rpx = 0.5px | 1px = 2rpx |
iPhone6 Plus | 1rpx = 0.552px | 1px = 1.81rpx |
建议: 开发微信小程序时设计师可以用 iPhone6 作为视觉稿的标准。
注意: 在较小的屏幕上不可避免的会有一些毛刺,请在开发时尽量避免这种情况。
样式导⼊
wxss
中直接就⽀持,样式导⼊功能。也可以和less
中的导⼊混⽤。使⽤ @import
语句可以导⼊外联样式表,只⽀持相对路径。
/** common.wxss **/
view{
background-color: rgb(235, 235, 235);
}
.userinfo {
display: flex;
flex-direction: column;
align-items: center;
}
.userinfo-avatar {
width: 128rpx;
height: 128rpx;
margin: 20rpx;
border-radius: 50%;
}
/**index.wxss**/
@import "../../styles/common.wxss";
选择器
目前支持的选择器有:
选择器 | 样例 | 样例描述 |
---|---|---|
.class | .intro | 选择所有拥有 class=“intro” 的组件 |
#id | #firstname | 选择拥有 id=“firstname” 的组件 |
element | view | 选择所有 view 组件 |
element, element | view, checkbox | 选择所有文档的 view 组件和所有的 checkbox 组件 |
::after | view::after | 在 view 组件后边插入内容 |
::before | view::before | 在 view 组件前边插入内容 |
常见组件
<!-- text -->
<text user-select decode>text 123</text>
<!-- images -->
<view class="img">
<image mode="widthFix" lazy-load src="https://konachan.net/sample/417260b75396b82c6206e044a50e4cc9/Konachan.com%20-%20318761%20sample.jpg"></image>
</view>
<!-- swiper 轮播图 -->
<swiper autoplay="true" interval="1500" circular="true" indicator-dots="true" indicator-color="#0094ff" indicator-active-color="#ff0094">
<swiper-item>
<image mode="widthFix" lazy-load src="https://konachan.net/sample/417260b75396b82c6206e044a50e4cc9/Konachan.com%20-%20318761%20sample.jpg"></image>
</swiper-item>
<swiper-item>
<image mode="widthFix" lazy-load src="https://konachan.net/sample/417260b75396b82c6206e044a50e4cc9/Konachan.com%20-%20318761%20sample.jpg"></image>
</swiper-item>
<swiper-item>
<image mode="widthFix" lazy-load src="https://konachan.net/sample/417260b75396b82c6206e044a50e4cc9/Konachan.com%20-%20318761%20sample.jpg"></image>
</swiper-item>
</swiper>
<!-- navigator -->
<navigator hover-class="navigator-hover" url="/pages/logs/logs">log</navigator>
<!-- rich-text 标签字符串 -->
<rich-text nodes="{{html}}"></rich-text>
<!-- button -->
<button>默认按钮</button>
<button size="mini">迷你按钮</button>
<button type="primary">primary 按钮</button>
<button type="warn">warn按钮</button>
<button type="warn" plain>plain按钮</button>
<button open-type="contact">contact</button>
<button open-type="share">share</button>
<button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber">getPhoneNumber</button>
<button open-type="getUserInfo" bindgetuserinfo="getUserInfo">getUserInfo</button>
<button open-type="launchApp">launchApp</button>
<button open-type="openSetting">openSetting</button>
<button open-type="feedback">feedback</button>
<!-- 字体图标 -->
<view class="" hover-class="none" hover-stop-propagation="false">
<icon type="success" size="23" color="red"></icon>
</view>
<!-- radio 单选框 -->
<radio-group bindchange="handleChange">
<radio class="" color="red" value="male">男</radio>
<radio class="" color="red" value="female">女</radio>
</radio-group>
<view class="" hover-class="none" hover-stop-propagation="false">您选中的是:{{gender}}</view>
<!-- checkbox 多选框 -->
<checkbox-group bindchange="handleItemChange">
<checkbox class="" value="{{item.value}}" wx:for="{{list}}" wx:key="id">{{item.name}}</checkbox>
</checkbox-group>
<view>选中的水果:{{checkdeList}}</view>
// pages/search/search.js
Page({
/**
* 页面的初始数据
*/
data: {
html: '<div><p>demo</p></div>',
gender: '',
list: [{
id: 0,
name: '🍎',
value: 'apple'
}, {
id: 1,
name: '🍌',
value: 'banana'
}, {
id: 2,
name: '🍊',
value: 'orange'
}],
checkdeList: []
},
// 单选框事件
handleChange(e) {
let gender = e.detail.value;
this.setData({
gender: gender == "male" ? "男" : "女"
});
},
// 多选框事件
handleItemChange(e) {
const checkdeList = e.detail.value;
this.setData({
checkdeList
})
},
getPhoneNumber(e) {
console.log(e);
},
getUserInfo(e) {
console.log(e);
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function(options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function() {
}
})
自定义组件
创建自定义组件
⼩程序允许我们使⽤⾃定义组件的⽅式来构建⻚⾯。在微信开发者⼯具中快速创建组件的⽂件结构,项目根目录下新建components/Tabs
文件夹,然后右键Tabs
文件夹,新建Component
,生成 json
wxml
wxss
js
4个文件。
声明组件
需要在组件的json
⽂件中进⾏⾃定义组件声明
{
"component": true,
"usingComponents": {}
}
编辑组件
在组件的wxml
⽂件中编写组件模板,在wxss
⽂件中加⼊组件样式slot
表⽰插槽,类似vue
中的slot
,注意:在组件wxss
中不应使用ID选择器、属性选择器和标签名选择器。
<view class="tabs">
<view class="tabs_title">
<view wx:for="{{tabs}}" wx:key="id" class="title_item {{item.isActive?'active':''}}" bindtap="hanldeItemTap" data-index="{{index}}">
{{item.name}}
</view>
</view>
<view class="tabs_content">
<slot></slot>
</view>
</view>
注册组件
在组件的 js ⽂件中,需要使⽤Component()
来注册组件,并提供组件的属性定义、内部数据和⾃定义⽅法
// components/Tabs/Tabs.js
Component({
/**
* 组件的属性列表
*/
properties: {
tabs: {
type: Array,
value: []
}
},
/**
* 组件的初始数据
*/
data: {},
/**
* 组件的方法列表
*/
methods: {
hanldeItemTap(e) {
const { index } = e.currentTarget.dataset;
this.triggerEvent("itemChange", { index });
}
}
})
声明引入自定义组件
在⻚⾯的json
⽂件中进⾏引⽤声明。还要提供对应的组件名和组件路径。
{
"usingComponents": {
"Tabs": "../../components/Tabs/Tabs"
}
}
页面中使用自定义组件
<Tabs tabs="{{tabs}}" binditemChange="handleItemChange">
<block wx:if="{{tabs[0].isActive}}">0</block>
<block wx:elif="{{tabs[1].isActive}}">1</block>
<block wx:elif="{{tabs[2].isActive}}">2</block>
<block wx:else>3</block>
</Tabs>
父子组件之间的传值
父组件向子组件传值
- 首先在父组件的
json
文件中定义子组件
{
"usingComponents": {
"StepDevice": "/components/StepDevice/StepDevice"
}
}
- 之后在父组件
wxml
中使用定义的组件
<StepDevice stepNum="{{item.shopNum}}"/>
-
子组件规定的写法
获取定义的数据使用:
this.properties.stepNum
改变数据的写法:this.setData({stepNum: 2 })
Component({
properties:{
stepNum: {
type: Number,
value: 1
}
},
// 子组件的生命周期函数
lifetimes: {
// 在组件实例进入页面节点树时执行
attached: function () {
console.log(this.properties.stepNum)
},
},
})
子组件向父组件传值
- 子组件的写法
如果向父组件传值使用到的固定写法this.triggerEvent('父组件定义的事件名称',{传递的数据})
也是在事件触发的时候向父组件传递数据
<!-- 子组件的写法 -->
<text class="add-btn" bindtap="addBtn">+</text>
<!-- 父组件 -->
<StepDevice bind:changeStepNum="stepChange"/>
<!-- stepChange 为父组件用来获取子组件的数据的方法 -->
- 子组件方法
this.triggerEvent('父组件定义的事件名称',{传递的数据})
父组件定义的事件名称就是bind
绑定的事件名称这里的就是changeStepNum
Component({
properties: {
stepNum: {
type: Number,
value: 1
}
},
methods:{
// 给父组件传值
stepChange() {
this.triggerEvent("changeStepNum", {
stepNum: this.properties.stepNum
})
},
// 每次点击的时候就把 数据传递给父组件
addBtn() {
this.stepChange()
}
}
})
- 父组件的写法
stepChange
为父组件用来获取子组件的数据的方法
<!-- 父组件 -->
<StepDevice bind:changeStepNum="stepChange"/>
<!-- stepChange 为父组件用来获取子组件的数据的方法 -->
- 获取数据
数据保存在 e.detail
中
stepChange(e) {
const getStepNum = e.detail.stepNum
console.log(getStepNum)
},
生命周期
分为应⽤⽣命周期和⻚⾯⽣命周期
应用生命周期
注册小程序。接受一个 Object
参数,其指定小程序的生命周期回调等。
App() 必须在 app.js
中调用,必须调用且只能调用一次。不然会出现无法预期的后果。
Object object
属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
---|---|---|---|---|---|
onLaunch | function | 否 | 生命周期回调——监听小程序初始化。 | ||
onShow | function | 否 | 生命周期回调——监听小程序启动或切前台。 | ||
onHide | function | 否 | 生命周期回调——监听小程序切后台。 | ||
onError | function | 否 | 错误监听函数。 | ||
onPageNotFound | function | 否 | 页面不存在监听函数。 | 1.9.90 | |
onUnhandledRejection | function | 否 | 未处理的 Promise 拒绝事件监听函数。 | 2.10.0 | |
onThemeChange | function | 否 | 监听系统主题变化 | 2.11.0 | |
其他 | any | 否 | 开发者可以添加任意的函数或数据变量到 Object 参数中,用 this 可以访问 |
页面生命周期
注册小程序中的一个页面。接受一个 Object
类型参数,其指定页面的初始数据、生命周期回调、事件处理函数等。
属性 | 类型 | 默认值 | 必填 | 说明 |
---|---|---|---|---|
data | Object | 页面的初始数据 | ||
options | Object | 页面的组件选项,同 Component 构造器 中的 options ,需要基础库版本 2.10.1 | ||
onLoad | function | 生命周期回调—监听页面加载 | ||
onShow | function | 生命周期回调—监听页面显示 | ||
onReady | function | 生命周期回调—监听页面初次渲染完成 | ||
onHide | function | 生命周期回调—监听页面隐藏 | ||
onUnload | function | 生命周期回调—监听页面卸载 | ||
onPullDownRefresh | function | 监听用户下拉动作 | ||
onReachBottom | function | 页面上拉触底事件的处理函数 | ||
onShareAppMessage | function | 用户点击右上角转发 | ||
onShareTimeline | function | 用户点击右上角转发到朋友圈 | ||
onAddToFavorites | function | 用户点击右上角收藏 | ||
onPageScroll | function | 页面滚动触发事件的处理函数 | ||
onResize | function | 页面尺寸改变时触发,详见 响应显示区域变化 | ||
onTabItemTap | function | 当前是 tab 页时,点击 tab 时触发 | ||
其他 | any | 开发者可以添加任意的函数或数据到 Object 参数中,在页面的函数中用 this 可以访问 |