初识小程序-基础语法

小程序

基本知识

项目文件

1   .json 后缀的 JSON 配置文件
2   .wxml 后缀的 WXML 模板文件
3   .wxss 后缀的 WXSS 样式文件
4   .js 后缀的 JS 脚本逻辑文件

JSON 配置

小程序配置 app.json

app.json 是对当前小程序的全局配置,包括了小程序的所有页面路径、界面表现、网络超时时间、底部 tab 等;

{
  "pages": [
    "pages/index/index",
    "pages/logs/index"
  ],
  "window": {
    "navigationBarTitleText": "Demo"
  },
  "tabBar": {
    "list": [{
      "pagePath": "pages/index/index",
      "text": "首页"
    }, {
      "pagePath": "pages/logs/logs",
      "text": "日志"
    }]
  },
  "networkTimeout": {
    "request": 10000,
    "downloadFile": 10000
  },
  "debug": true
}

pages字段 —— 用于描述当前小程序所有页面路径,这是为了让微信客户端知道当前你的小程序页面定义在哪个目录。
window字段 —— 小程序所有页面的顶部背景颜色,文字颜色定义在这里的

app.json 配置项列表

属性  类型  必填  描述
pages       String Array    是   设置页面路径
window  Object  否   设置默认页面的窗口表现
tabBar  Object  否   设置底部 tab 的表现
networkTimeout   Object 否   设置网络超时时间
debug    Boolean    否   设置是否开启 debug 模式

pages:
接受一个数组,每一项都是字符串,来指定小程序由哪些页面组成。
数组的第一项代表小程序的初始页面。小程序中新增/减少页面,都需要对 pages 数组进行修改。

window:
用于设置小程序的状态栏、导航条、标题、窗口背景色。

属性  类型  默认值     描述
navigationBarBackgroundColor
HexColor        #000000
导航栏背景颜色,如"#000000"

navigationBarTextStyle
String  white
导航栏标题颜色,仅支持 black/white

navigationBarTitleText
String
导航栏标题文字内容

backgroundColor
HexColor        #ffffff
窗口的背景色

backgroundTextStyle
String  dark
下拉背景字体、loading 图的样式,仅支持 dark/light

enablePullDownRefresh
Boolean false
是否开启下拉刷新

onReachBottomDistance
Number  50
页面上拉触底事件触发时距页面底部距离,单位为px

tabBar
如果小程序是一个多 tab 应用(客户端窗口的底部或顶部有 tab 栏可以切换页面),可以通过 tabBar 配置项指定 tab 栏的表现,以及 tab 切换时显示的对应页面。
Tip:
1 当设置 position 为 top 时,将不会显示 icon
2 tabBar 中的 list 是一个数组,只能配置最少2个、最多5个 tab,tab 按数组的顺序排序。

属性  类型  必填  默认值 描述
color       HexColor        是
tab 上的文字默认颜色

selectedColor   HexColor    是
tab 上的文字选中时的颜色

backgroundColor HexColor    是
tab 的背景色

borderStyle     String
否       black
tabbar上边框的颜色, 仅支持 black/white

list    Array       是
tab 的列表,详见 list 属性说明,最少2个、最多5个 tab

position        String  否
bottom    可选值 bottom、top


其中 list 接受一个数组,数组中的每个项都是一个对象,其属性值如下:
属性  类型  必填  说明
pagePath     String 是
页面路径,必须在 pages 中先定义

text    String  是
tab 上按钮文字

iconPath        String  否
图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px,当 postion 为 top 时,此参数无效,不支持网络图片

selectedIconPath    String  否
选中时的图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px ,当 postion 为 top 时,此参数无效

networkTimeout:
可以设置各种网络请求的超时时间。

属性  类型  必填  说明
request Number  否
wx.request的超时时间,单位毫秒,默认为:60000

connectSocket   Number  否
wx.connectSocket的超时时间,单位毫秒,默认为:60000

uploadFile  Number  否
wx.uploadFile的超时时间,单位毫秒,默认为:60000

downloadFile    Number  否
wx.downloadFile的超时时间,单位毫秒,默认为:60000

debug:
可以在开发者工具中开启 debug 模式,在开发者工具的控制台面板,调试信息以 info 的形式给出,其信息有Page的注册,页面路由,数据更新,事件触发 。 可以帮助开发者快速定位一些常见的问题。

page.json

每一个小程序页面也可以使用.json文件来对本页面的窗口表现进行配置。 页面的配置比app.json全局配置简单得多,只是设置 app.json 中的 window 配置项的内容,页面中配置项会覆盖 app.json 的 window 中相同的配置项。

页面的.json只能设置 window 相关的配置项,以决定本页面的窗口表现,所以无需写 window 这个键,如:

属性  类型  默认值 描述
navigationBarBackgroundColor
HexColor    #000000
导航栏背景颜色,如"#000000"

navigationBarTextStyle
String  white
导航栏标题颜色,仅支持 black/white

navigationBarTitleText
String
导航栏标题文字内容

backgroundColor
HexColor        #ffffff
窗口的背景色

backgroundTextStyle
String  dark
下拉背景字体、loading 图的样式,仅支持 dark/
light

enablePullDownRefresh
Boolean     false
是否开启下拉刷新,详见页面相关事件处理函数。

disableScroll
Boolean     false
设置为 true 则页面整体不能上下滚动;只在 page.json 中有效,无法在 app.json 中设置该项

onReachBottomDistance
Number  50
页面上拉触底事件触发时距页面底部距离,单位为px

逻辑层(App Service)

小程序开发框架的逻辑层由 JavaScript 编写。
逻辑层将数据进行处理后发送给视图层,同时接受视图层的事件反馈。

注册程序

App() 函数用来注册一个小程序。接受一个 object 参数,其指定小程序的生命周期函数等;
object参数说明:

属性  类型  描述  触发时机
onLaunch        Function
生命周期函数--监听小程序初始化
当小程序初始化完成时,会触发 onLaunch(全局只触发一次)

onShow  Function
生命周期函数--监听小程序显示
当小程序启动,或从后台进入前台显示,会触发 
onShow

onHide  Function
生命周期函数--监听小程序隐藏
当小程序从前台进入后台,会触发 onHide

onError Function
错误监听函数
当小程序发生脚本错误,或者 api 调用失败时,会触发 onError 并带上错误信息

其他  Any
开发者可以添加任意的函数或数据到 Object 参数中,用 this 可以访问

前台、后台定义: 当用户点击左上角关闭,或者按了设备 Home 键离开微信,小程序并没有直接销毁,而是进入了后台;当再次进入微信或再次打开小程序,又会从后台进入前台。需要注意的是:只有当小程序进入后台一定时间,或者系统资源占用过高,才会被真正的销毁。

onLaunch, onShow 参数

字段  类型  说明
path    String
打开小程序的路径

query       Object
打开小程序的query

scene       Number
打开小程序的场景值

shareTicket     String
shareTicket,详见 获取更多转发信息

referrerInfo        Object
当场景为由从另一个小程序或公众号或App打开时,返回此字段

referrerInfo.appId      String
来源小程序或公众号或App的 appId,详见下方说明

referrerInfo.extraData      Object
来源小程序传过来的数据,scene=1037或1038时支持

以下场景支持返回 referrerInfo.appId:

场景值     场景  appId 信息含义
1020
公众号 profile 页相关小程序列表
返回来源公众号 appId

1035
公众号自定义菜单
返回来源公众号 appId

1036
App 分享消息卡片
返回来源应用 appId

1037
小程序打开小程序
返回来源小程序 appId

1038
从另一个小程序返回
返回来源小程序 appId

1043
公众号模板消息
返回来源公众号 appId

getApp()
全局的 getApp() 函数可以用来获取到小程序实例。

// other.js
var appInstance = getApp()
console.log(appInstance.globalData) // I am global data

注意:
• App() 必须在 app.js 中注册,且不能注册多个。
• 不要在定义于 App() 内的函数中调用 getApp() ,使用 this 就可以拿到 app 实例。
• 不要在 onLaunch 的时候调用 getCurrentPages(),此时 page 还没有生成。
• 通过 getApp() 获取实例之后,不要私自调用生命周期函数。

场景值

场景值ID 说明
1001 发现栏小程序主入口
1005 顶部搜索框的搜索结果页
1006 发现栏小程序主入口搜索框的搜索结果页
1007 单人聊天会话中的小程序消息卡片
1008 群聊会话中的小程序消息卡片
1011 扫描二维码
1012 长按图片识别二维码
1013 手机相册选取二维码
1014 小程序模版消息
1017 前往体验版的入口页
1019 微信钱包
1020 公众号 profile 页相关小程序列表
1022 聊天顶部置顶小程序入口
1023 安卓系统桌面图标
1024 小程序 profile 页
1025 扫描一维码
1026 附近小程序列表
1027 顶部搜索框搜索结果页“使用过的小程序”列表
1028 我的卡包
1029 卡券详情页
1030 自动化测试下打开小程序
1031 长按图片识别一维码
1032 手机相册选取一维码
1034 微信支付完成页
1035 公众号自定义菜单
1036 App 分享消息卡片
1037 小程序打开小程序
1038 从另一个小程序返回
1039 摇电视
1042 添加好友搜索框的搜索结果页
1043 公众号模板消息
1044 带 shareTicket 的小程序消息卡片(详情)
1047 扫描小程序码
1048 长按图片识别小程序码
1049 手机相册选取小程序码
1052 卡券的适用门店列表
1053 搜一搜的结果页
1054 顶部搜索框小程序快捷入口
1056 音乐播放器菜单
1057 钱包中的银行卡详情页
1058 公众号文章
1059 体验版小程序绑定邀请页
1064 微信连Wi-Fi状态栏
1067 公众号文章广告
1068 附近小程序列表广告
1071 钱包中的银行卡列表页
1072 二维码收款页面
1073 客服消息列表下发的小程序消息卡片
1074 公众号会话下发的小程序消息卡片
1078 连Wi-Fi成功页
1089 微信聊天主界面下拉
1090 长按小程序右上角菜单唤出最近使用历史
1092 城市服务入口

注册页面

Page() 函数用来注册一个页面。接受一个 object 参数,其指定页面的初始数据、生命周期函数、事件处理函数等。
object 参数说明:

属性  类型  描述
data    Object
页面的初始数据

onLoad  Function
生命周期函数--监听页面加载

onReady Function
生命周期函数--监听页面初次渲染完成

onShow  Function
生命周期函数--监听页面显示

onHide  Function
生命周期函数--监听页面隐藏

onUnload        Function
生命周期函数--监听页面卸载

onPullDownRefresh       Function
页面相关事件处理函数--监听用户下拉动作

onReachBottom       Function
页面上拉触底事件的处理函数

onShareAppMessage       Function
用户点击右上角转发

onPageScroll        Function
页面滚动触发事件的处理函数

onTabItemTap    Function
当前是 tab 页时,点击 tab 时触发

其他      Any
开发者可以添加任意的函数或数据到 object 参数中,在页面的函数中用 this 可以访问

object 内容在页面加载时会进行一次深拷贝,需考虑数据大小对页面加载的开销

//index.js
Page({
  data: {
    text: "This is page data."
  },
  onLoad: function(options) {
    // Do some initialize when page load.
  },
  onReady: function() {
    // Do something when page ready.
  },
  onShow: function() {
    // Do something when page show.
  },
  onHide: function() {
    // Do something when page hide.
  },
  onUnload: function() {
    // Do something when page close.
  },
  onPullDownRefresh: function() {
    // Do something when pull down.
  },
  onReachBottom: function() {
    // Do something when page reach bottom.
  },
  onShareAppMessage: function () {
   // return custom share data when user share.
  },
  onPageScroll: function() {
    // Do something when page scroll
  },
  onTabItemTap(item) {
    console.log(item.index)
    console.log(item.pagePath)
    console.log(item.text)
  },
  // Event handler.
  viewTap: function() {
    this.setData({
      text: 'Set some data for updating view.'
    }, function() {
      // this is setData callback
    })
  },
  customData: {
    hi: 'MINA'
  }
})

初始化数据
初始化数据将作为页面的第一次渲染。data 将会以 JSON 的形式由逻辑层传至渲染层,所以其数据必须是可以转成 JSON 的格式:字符串,数字,布尔值,对象,数组。
渲染层可以通过 WXML 对数据进行绑定。
示例代码:

<view>{{text}}</view>
<view>{{array[0].msg}}</view>
Page({
  data: {
    text: 'init data',
    array: [{msg: '1'}, {msg: '2'}]
  }
})

生命周期函数
• onLoad: 页面加载
◦ 一个页面只会调用一次,可以在 onLoad 中获取打开当前页面所调用的 query 参数。
• onShow: 页面显示
◦ 每次打开页面都会调用一次。
• onReady: 页面初次渲染完成
◦ 一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互。
◦ 对界面的设置如wx.setNavigationBarTitle请在onReady之后设置。详见生命周期
• onHide: 页面隐藏
◦ 当navigateTo或底部tab切换时调用。
• onUnload: 页面卸载
◦ 当redirectTo或navigateBack的时候调用。

页面相关事件处理函数
• onPullDownRefresh: 下拉刷新
◦ 监听用户下拉刷新事件。
◦ 需要在app.json的window选项中或页面配置中开启enablePullDownRefresh。
◦ 当处理完数据刷新后,wx.stopPullDownRefresh可以停止当前页面的下拉刷新。
• onReachBottom: 上拉触底
◦ 监听用户上拉触底事件。
◦ 可以在app.json的window选项中或页面配置中设置触发距离onReachBottomDistance。
◦ 在触发距离内滑动期间,本事件只会被触发一次。
• onPageScroll: 页面滚动
◦ 监听用户滑动页面事件。
◦ 参数为 Object,包含以下字段:
scrollTop Number
页面在垂直方向已滚动的距离(单位px)

onShareAppMessage: 用户转发
◦ 只有定义了此事件处理函数,右上角菜单才会显示“转发”按钮
◦ 用户点击转发按钮的时候会调用
◦ 此事件需要 return 一个 Object,用于自定义转发内容
自定义转发字段

字段  说明  默认值
title       转发标题    当前小程序名称
path    转发路径
当前页面 path ,必须是以 / 开头的完整路径

示例代码

Page({
  onShareAppMessage: function () {
    return {
      title: '自定义转发标题',
      path: '/page/user?id=123'
    }
  }
})

事件处理函数
除了初始化数据和生命周期函数,Page 中还可以定义一些特殊的函数:事件处理函数。
示例代码:

<view bindtap="viewTap"> click me </view>

Page({
  viewTap: function() {
    console.log('view tap')
  }
})

Page.prototype.setData()
setData 函数用于将数据从逻辑层发送到视图层(异步),同时改变对应的 this.data 的值(同步)。
setData() 参数格式
字段 类型 必填 描述 最低版本
data Object 是 这次要改变的数据
callback Function 否 回调函数 1.5.0
注意:
1 直接修改 this.data 而不调用 this.setData 是无法改变页面的状态的,还会造成数据不一致。
2 单次设置的数据不能超过1024kB,请尽量避免一次设置过多的数据。
3 请不要把 data 中任何一项的 value 设为 undefined ,否则这一项将不被设置并可能遗留一些潜在问题。

示例代码

<!--index.wxml-->
<view>{{text}}</view>
<button bindtap="changeText"> Change normal data </button>
<view>{{num}}</view>
<button bindtap="changeNum"> Change normal num </button>
<view>{{array[0].text}}</view>
<button bindtap="changeItemInArray"> Change Array data </button>
<view>{{object.text}}</view>
<button bindtap="changeItemInObject"> Change Object data </button>
<view>{{newField.text}}</view>
<button bindtap="addNewField"> Add new data </button>


//index.js
Page({
  data: {
    text: 'init data',
    num: 0,
    array: [{text: 'init data'}],
    object: {
      text: 'init data'
    }
  },
  changeText: function() {
    // this.data.text = 'changed data'  // bad, it can not work
    this.setData({
      text: 'changed data'
    })
  },
  changeNum: function() {
    this.data.num = 1
    this.setData({
      num: this.data.num
    })
  },
  changeItemInArray: function() {
    // you can use this way to modify a danamic data path
    this.setData({
      'array[0].text':'changed data'
    })
  },
  changeItemInObject: function(){
    this.setData({
      'object.text': 'changed data'
    });
  },
  addNewField: function() {
    this.setData({
      'newField.text': 'new data'
    })
  }
})

路由

页面路由
在小程序中所有页面的路由全部由框架进行管理。
页面栈
框架以栈的形式维护了当前的所有页面。

getCurrentPages()
getCurrentPages() 函数用于获取当前页面栈的实例,以数组形式按栈的顺序给出,第一个元素为首页,最后一个元素为当前页面。

路由方式
对于路由的触发方式以及页面生命周期函数如下:
路由方式 触发时机 路由前页面 路由后页面
初始化
小程序打开的第一个页面
onLoad, onShow

打开新页面
调用 API wx.navigateTo 或使用组件 <navigator open-type="navigateTo"/>
onHide
onLoad, onShow

页面重定向
调用 API wx.redirectTo 或使用组件 <navigator open-type="redirectTo"/>
onUnload
onLoad, onShow

页面返回
调用 API wx.navigateBack 或使用组件<navigator open-type="navigateBack">或用户按左上角返回按钮
onUnload
onShow

Tab 切换
调用 API wx.switchTab 或使用组件 <navigator open-type="switchTab"/> 或用户切换 Tab

重启动
调用 API wx.reLaunch 或使用组件 <navigator open-type="reLaunch"/>
onUnload
onLoad, onShow

Tips:
• navigateTo, redirectTo 只能打开非 tabBar 页面。
• switchTab 只能打开 tabBar 页面。
• reLaunch 可以打开任意页面。
• 页面底部的 tabBar 由页面决定,即只要是定义为 tabBar 的页面,底部都有 tabBar。
• 调用页面路由带的参数可以在目标页面的onLoad中获取。

模块化

文件作用域
在 JavaScript 文件中声明的变量和函数只在该文件中有效;不同的文件中可以声明相同名字的变量和函数,不会互相影响。
通过全局函数 getApp() 可以获取全局的应用实例,如果需要全局的数据可以在 App() 中设置,如:

// app.js
App({
  globalData: 1
})

// a.js
// The localValue can only be used in file a.js.
var localValue = 'a'
// Get the app instance.
var app = getApp()
// Get the global data and change it.
app.globalData++

模块化
可以将一些公共的代码抽离成为一个单独的 js 文件,作为一个模块。模块只有通过 module.exports 或者 exports 才能对外暴露接口;
注意的是:
• exports 是 module.exports 的一个引用,因此在模块里边随意更改 exports 的指向会造成未知的错误。所以更推荐开发者采用 module.exports 来暴露模块接口,除非你已经清晰知道这两者的关系。
• 小程序目前不支持直接引入 node_modules , 开发者需要使用到 node_modules 时候建议拷贝出相关的代码到小程序的目录中。

// common.js
function sayHello(name) {
  console.log(`Hello ${name} !`)
}
function sayGoodbye(name) {
  console.log(`Goodbye ${name} !`)
}

module.exports.sayHello = sayHello
exports.sayGoodbye = sayGoodbye

在需要使用这些模块的文件中,使用 require(path) 将公共代码引入,require 暂时不支持绝对路径
var common = require('common.js')
Page({
  helloMINA: function() {
    common.sayHello('MINA')
  },
  goodbyeMINA: function() {
    common.sayGoodbye('MINA')
  }
})

视图层

WXML(WeiXin Markup language) 用于描述页面的结构。
WXS(WeiXin Script) 是小程序的一套脚本语言,结合 WXML,可以构建出页面的结构。
WXSS(WeiXin Style Sheet) 用于描述页面的样式。
组件(Component)是视图的基本组成单元。

WXML

数据绑定:

<!--wxml-->
<view> {{message}} </view>

// page.js
Page({
  data: {
    message: 'Hello MINA!'
  }
})

组件属性(需要在双引号之内)
<view id="item-{{id}}"> </view>
Page({
  data: {
    id: 0
  }
})

控制属性(需要在双引号之内)
<view wx:if="{{condition}}"> </view>
Page({
  data: {
    condition: true
  }
})

关键字(需要在双引号之内)
true:boolean 类型的 true,代表真值。
false: boolean 类型的 false,代表假值。
<checkbox checked="{{false}}"> </checkbox>

三元运算
<view hidden="{{flag ? true : false}}"> Hidden </view>

算数运算
<view> {{a + b}} + {{c}} + d </view>
Page({
  data: {
    a: 1,
    b: 2,
    c: 3
  }
})
view中的内容为 3 + 3 + d

逻辑判断
<view wx:if="{{length > 5}}"> </view>

字符串运算
<view>{{"hello" + name}}</view>
Page({
  data:{
    name: 'MINA'
  }
})

数据路径运算
<view>{{object.key}} {{array[0]}}</view>
Page({
  data: {
    object: {
      key: 'Hello '
    },
    array: ['MINA']
  }
})

数组
<view wx:for="{{[zero, 1, 2, 3, 4]}}"> {{item}} </view>
Page({
  data: {
    zero: 0
  }
})

对象
<template is="objectCombine" data="{{for: a, bar: b}}"></template>
Page({
  data: {
    a: 1,
    b: 2
  }
})
最终组合成的对象是 {for: 1, bar: 2}
也可以用扩展运算符 ... 来将一个对象展开
<template is="objectCombine" data="{{...obj1, ...obj2, e: 5}}"></template>
Page({
  data: {
    obj1: {
      a: 1,
      b: 2
    },
    obj2: {
      c: 3,
      d: 4
    }
  }
})
最终组合成的对象是 {a: 1, b: 2, c: 3, d: 4, e: 5}。

列表渲染:

<!--wxml-->
<view wx:for="{{array}}"> {{item}} </view>

// page.js
Page({
  data: {
    array: [1, 2, 3, 4, 5]
  }
})

wx:for
在组件上使用 wx:for 控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。
默认数组的当前项的下标变量名默认为 index,数组当前项的变量名默认为 item
<view wx:for="{{array}}">
  {{index}}: {{item.message}}
</view>
Page({
  data: {
    array: [{
      message: 'foo',
    }, {
      message: 'bar'
    }]
  }
})
使用 wx:for-item 可以指定数组当前元素的变量名,
使用 wx:for-index 可以指定数组当前下标的变量名:
<view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName">
  {{idx}}: {{itemName.message}}
</view>
wx:for 也可以嵌套,下边是一个九九乘法表
<view wx:for="{{[1, 2, 3, 4, 5, 6, 7, 8, 9]}}" wx:for-item="i">
  <view wx:for="{{[1, 2, 3, 4, 5, 6, 7, 8, 9]}}" wx:for-item="j">
    <view wx:if="{{i <= j}}">
      {{i}} * {{j}} = {{i * j}}
    </view>
  </view>
</view>

block wx:for
类似 block wx:if,也可以将 wx:for 用在<block/>标签上,以渲染一个包含多节点的结构块。例如:
<block wx:for="{{[1, 2, 3]}}">
  <view> {{index}}: </view>
  <view> {{item}} </view>
</block>

wx:key
如果列表中项目的位置会动态改变或者有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态(如 <input/> 中的输入内容,<switch/> 的选中状态),需要使用 wx:key 来指定列表中项目的唯一的标识符。
wx:key 的值以两种形式提供
    1   字符串,代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。
    2   保留关键字 *this 代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字,如:
当数据改变触发渲染层重新渲染的时候,会校正带有 key 的组件,框架会确保他们被重新排序,而不是重新创建,以确保使组件保持自身的状态,并且提高列表渲染时的效率。
如不提供 wx:key,会报一个 warning, 如果明确知道该列表是静态,或者不必关注其顺序,可以选择忽略。

条件渲染:

<!--wxml-->
<view wx:if="{{view == 'WEBVIEW'}}"> WEBVIEW </view>
<view wx:elif="{{view == 'APP'}}"> APP </view>
<view wx:else="{{view == 'MINA'}}"> MINA </view>

// page.js
Page({
  data: {
    view: 'MINA'
  }
})

wx:if
在框架中,使用 wx:if="{{condition}}" 来判断是否需要渲染该代码块:
<view wx:if="{{condition}}"> True </view>
也可以用 wx:elif 和 wx:else 来添加一个 else 块:
<view wx:if="{{length > 5}}"> 1 </view>
<view wx:elif="{{length > 2}}"> 2 </view>
<view wx:else> 3 </view>

block wx:if
因为 wx:if 是一个控制属性,需要将它添加到一个标签上。如果要一次性判断多个组件标签,可以使用一个 <block/> 标签将多个组件包装起来,并在上边使用 wx:if 控制属性。
<block wx:if="{{true}}">
  <view> view1 </view>
  <view> view2 </view>
</block>

模板:

<!--wxml-->
<template name="staffName">
  <view>
    FirstName: {{firstName}}, LastName: {{lastName}}
  </view>
</template>

<template is="staffName" data="{{...staffA}}"></template>
<template is="staffName" data="{{...staffB}}"></template>
<template is="staffName" data="{{...staffC}}"></template>

// page.js
Page({
  data: {
    staffA: {firstName: 'Hulk', lastName: 'Hu'},
    staffB: {firstName: 'Shang', lastName: 'You'},
    staffC: {firstName: 'Gideon', lastName: 'Lin'}
  }
})

定义模板
使用 name 属性,作为模板的名字。然后在<template/>内定义代码片段,如:
<!--
  index: int
  msg: string
  time: string
-->
<template name="msgItem">
  <view>
    <text> {{index}}: {{msg}} </text>
    <text> Time: {{time}} </text>
  </view>
</template>

使用模板
使用 is 属性,声明需要的使用的模板,然后将模板所需要的 data 传入,如:
<template is="msgItem" data="{{...item}}"/>
Page({
  data: {
    item: {
      index: 0,
      msg: 'this is a template',
      time: '2016-09-15'
    }
  }
})

模板拥有自己的作用域,只能使用 data 传入的数据以及模版定义文件中定义的 <wxs /> 模块。

事件:

<view bindtap="add"> {{count}} </view>

Page({
  data: {
    count: 1
  },
  add: function(e) {
    this.setData({
      count: this.data.count + 1
    })
  }
})

事件分为冒泡事件和非冒泡事件:
    1   冒泡事件:当一个组件上的事件被触发后,该事件会向父节点传递。
    2   非冒泡事件:当一个组件上的事件被触发后,该事件不会向父节点传递。

引用
WXML 提供两种文件引用方式import和include。

import可以在该文件中使用目标文件定义的template,如:
在 item.wxml 中定义了一个叫item的template:
<!-- item.wxml -->
<template name="item">
  <text>{{text}}</text>
</template>
在 index.wxml 中引用了 item.wxml,就可以使用item模板:
<import src="item.wxml"/>
<template is="item" data="{{text: 'forbar'}}"/>

import 有作用域的概念,即只会 import 目标文件中定义的 template,而不会 import 目标文件 import 的 template。

include
include 可以将目标文件除了 <template/> <wxs/> 外的整个代码引入,相当于是拷贝到 include 位置,如:
<!-- index.wxml -->
<include src="header.wxml"/>
<view> body </view>
<include src="footer.wxml"/>
<!-- header.wxml -->
<view> header </view>
<!-- footer.wxml -->
<view> footer </view>

WXS

注意
1 wxs 不依赖于运行时的基础库版本,可以在所有版本的小程序中运行。
2 wxs 与 javascript 是不同的语言,有自己的语法,并不和 javascript 一致。
3 wxs 的运行环境和其他 javascript 代码是隔离的,wxs 中不能调用其他 javascript 文件中定义的函数,也不能调用小程序提供的API。
4 wxs 函数不能作为组件的事件回调。
5 由于运行环境的差异,在 iOS 设备上小程序内的 wxs 会比 javascript 代码快 2 ~ 20 倍。在 android 设备上二者运行效率无差异。

模块

每一个 .wxs 文件和 <wxs> 标签都是一个单独的模块。
每个模块都有自己独立的作用域。即在一个模块里面定义的变量与函数,默认为私有的,对其他模块不可见。
一个模块要想对外暴露其内部的私有变量与函数,只能通过 module.exports 实现。

.wxs 文件

示例代码:
// /pages/comm.wxs

var foo = "'hello world' from comm.wxs";
var bar = function(d) {
  return d;
}
module.exports = {
  foo: foo,
  bar: bar
};

module 对象
每个 wxs 模块均有一个内置的 module 对象。
属性
• exports: 通过该属性,可以对外共享本模块的私有变量与函数。

require函数

在.wxs模块中引用其他 wxs 文件模块,可以使用 require 函数。
引用的时候,要注意如下几点:
    •   只能引用 .wxs 文件模块,且必须使用相对路径。
    •   wxs 模块均为单例,wxs 模块在第一次被引用时,会自动初始化为单例对象。多个页面,多个地方,多次引用,使用的都是同一个 wxs 模块对象。
    •   如果一个 wxs 模块在定义之后,一直没有被引用,则该模块不会被解析与运行。
    示例代码:
    
// /pages/tools.wxs
var foo = "'hello world' from tools.wxs";
var bar = function (d) {
  return d;
}
module.exports = {
  FOO: foo,
  bar: bar,
};
module.exports.msg = "some msg";
// /pages/logic.wxs

var tools = require("./tools.wxs");

console.log(tools.FOO);
console.log(tools.bar("logic.wxs"));
console.log(tools.msg);
<!-- /page/index/index.wxml -->

<wxs src="./../logic.wxs" module="logic" />

变量

概念
    •   WXS 中的变量均为值的引用。
    •   没有声明的变量直接赋值使用,会被定义为全局变量。
    •   如果只声明变量而不赋值,则默认值为 undefined。
    •   var表现与javascript一致,会有变量提升。

var foo = 1;
var bar = "hello world";
var i; // i === undefined

注释

<!-- wxml -->
<wxs module="sample">
// 方法一:单行注释

/*
方法二:多行注释
*/

/*
方法三:结尾注释。即从 /* 开始往后的所有 WXS 代码均被注释

var a = 1;
var b = 2;
var c = "fake";

</wxs>

运算符

var a = 10, b = 20;
// 加法运算
console.log(30 === a + b);
// 减法运算
console.log(-10 === a - b);
// 乘法运算
console.log(200 === a * b);
// 除法运算
console.log(0.5 === a / b);
// 取余运算
console.log(10 === a % b);
    •   加法运算(+)也可以用作字符串的拼接。
    
一元运算符
var a = 10, b = 20;

// 自增运算
console.log(10 === a++);
console.log(12 === ++a);
// 自减运算
console.log(12 === a--);
console.log(10 === --a);

位运算符
示例代码:
var a = 10, b = 20;
// 左移运算
console.log(80 === (a << 3));
// 无符号右移运算
console.log(2 === (a >> 2));
// 带符号右移运算
console.log(2 === (a >>> 2));
// 与运算
console.log(2 === (a & 3));
// 异或运算
console.log(9 === (a ^ 3));
// 或运算
console.log(11 === (a | 3));

比较运算符
示例代码:
var a = 10, b = 20;
// 小于
console.log(true === (a < b));
// 大于
console.log(false === (a > b));
// 小于等于
console.log(true === (a <= b));
// 大于等于
console.log(false === (a >= b));

等值运算符
示例代码:
var a = 10, b = 20;
// 等号
console.log(false === (a == b));
// 非等号
console.log(true === (a != b));
// 全等号
console.log(false === (a === b));
// 非全等号
console.log(true === (a !== b));

二元逻辑运算符
示例代码:
var a = 10, b = 20;
// 逻辑与
console.log(20 === (a && b));
// 逻辑或
console.log(10 === (a || b));

语句

if (表达式) 语句;
else 语句;

if (表达式) 
  语句;
else 
  语句;

if (表达式) {
  代码块;
} else {
  代码块;
}

switch (表达式) {
  case 变量:
    语句;
  case 数字:
    语句;
    break;
  case 字符串:
    语句;
  default:
    语句;
}

for (语句; 语句; 语句)
  语句;

for (语句; 语句; 语句) {
  代码块;
}

while (表达式)
  语句;

while (表达式){
  代码块;
}

do {
  代码块;
} while (表达式)

数据类型

    •   number : 数值
    •   string :字符串
    •   boolean:布尔值
    •   object:对象
    •   function:函数
    •   array : 数组
    •   date:日期
    •   regexp:正则

基础类库

console
console.log 方法用于在 console 窗口输出信息。它可以接受多个参数,将它们的结果连接起来输出。

Math
    •   abs
    •   acos
    •   asin
    •   atan
    •   atan2
    •   ceil
    •   cos
    •   exp
    •   floor
    •   log
    •   max
    •   min
    •   pow
    •   random
    •   round
    •   sin
    •   sqrt
    •   tan
    
    JSON
方法
    •   stringify(object): 将 object 对象转换为 JSON 字符串,并返回该字符串。
    •   parse(string): 将 JSON 字符串转化成对象,并返回该对象。
    
    Number
属性
    •   MAX_VALUE
    •   MIN_VALUE
    •   NEGATIVE_INFINITY
    •   POSITIVE_INFINITY
    
    Date
    
    Global

WXSS

WXSS 具有 CSS 大部分特性。同时为了更适合开发微信小程序,WXSS 对 CSS 进行了扩充以及修改。
与 CSS 相比,WXSS 扩展的特性有:
• 尺寸单位
• 样式导入

样式导入
使用@import语句可以导入外联样式表,@import后跟需要导入的外联样式表的相对路径,用;表示语句结束。

/** common.wxss **/
.small-p {
  padding:5px;
}
/** app.wxss **/
@import "common.wxss";
.middle-p {
  padding:15px;
}

内联样式
框架组件上支持使用 style、class 属性来控制组件的样式。
• style:静态的样式统一写到 class 中。style 接收动态的样式,在运行时会进行解析,请尽量避免将静态的样式写进 style 中,以免影响渲染速度。
<view style="color:{{color}};" />
• class:用于指定样式规则,其属性值是样式规则中类选择器名(样式类名)的集合,样式类名不需要带上.,样式类名之间用空格分隔。
<view class="normal_view" />

选择器

选择器     样例  样例描述
.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 组件前边插入内容

全局样式与局部样式
定义在 app.wxss 中的样式为全局样式,作用于每一个页面。在 page 的 wxss 文件中定义的样式为局部样式,只作用在对应的页面,并会覆盖 app.wxss 中相同的选择器。

组件

布局:

子控件排列方向:水平/垂直
flex-direction:column/row

view

视图容器。
属性名     类型  默认值 说明  最低版本
hover-class
String
none
指定按下去的样式类。当 hover-class="none" 时,没有点击态效果

hover-stop-propagation
Boolean
false
指定是否阻止本节点的祖先节点出现点击态
1.5.0

hover-start-time
Number
50
按住后多久出现点击态,单位毫秒

hover-stay-time
Number
400
手指松开后点击态保留时间,单位毫秒

scroll-view
可滚动视图区域。

属性名 类型  默认值 说明
scroll-x
Boolean
false
允许横向滚动

scroll-y
Boolean
false
允许纵向滚动

upper-threshold
Number
50
距顶部/左边多远时(单位px),触发 scrolltoupper 事件

lower-threshold
Number
50
距底部/右边多远时(单位px),触发 scrolltolower 事件

scroll-top
Number
设置竖向滚动条位置

scroll-left
Number
设置横向滚动条位置

scroll-into-view
String
值应为某子元素id(id不能以数字开头)。设置哪个方向可滚动,则在哪个方向滚动到该元素

scroll-with-animation
Boolean
false
在设置滚动条位置时使用动画过渡

enable-back-to-top
Boolean
false
iOS点击顶部状态栏、安卓双击标题栏时,滚动条返回顶部,只支持竖向

bindscrolltoupper
EventHandle
滚动到顶部/左边,会触发 scrolltoupper 事件

bindscrolltolower
EventHandle
滚动到底部/右边,会触发 scrolltolower 事件

bindscroll
EventHandle
滚动时触发,event.detail = {scrollLeft, scrollTop, scrollHeight, scrollWidth, deltaX, deltaY}

swiper
滑块视图容器。

属性名 类型  默认值 说明  最低版本
indicator-dots
Boolean
false
是否显示面板指示点

indicator-color
Color
rgba(0, 0, 0, .3)
指示点颜色
1.1.0

indicator-active-color
Color
#000000
当前选中的指示点颜色
1.1.0

autoplay
Boolean
false
是否自动切换

current
Number
0
当前所在滑块的 index

current-item-id
String
""
当前所在滑块的 item-id ,不能与 current 被同时指定
1.9.0

interval
Number
5000
自动切换时间间隔

duration
Number
500
滑动动画时长

circular
Boolean
false
是否采用衔接滑动

vertical
Boolean
false
滑动方向是否为纵向

previous-margin
String
"0px"
前边距,可用于露出前一项的一小部分,接受 px 和 rpx 值
1.9.0

next-margin
String
"0px"
后边距,可用于露出后一项的一小部分,接受 px 和 rpx 值
1.9.0

display-multiple-items
Number
1
同时显示的滑块数量
1.9.0

skip-hidden-item-layout
Boolean
false
是否跳过未显示的滑块布局,设为 true 可优化复杂情况下的滑动性能,但会丢失隐藏状态滑块的布局信息
1.9.0

bindchange
EventHandle
current 改变时会触发 change 事件,event.detail = {current: current, source: source}

bindanimationfinish
EventHandle
动画结束时会触发 animationfinish 事件,event.detail 同上
1.9.0

movable-area
基础库 1.2.0 开始支持,低版本需做兼容处理
movable-view 的可移动区域
注意:movable-area 必须设置width和height属性,不设置默认为10px

cover-view
基础库 1.4.0 开始支持,低版本需做兼容处理
覆盖在原生组件之上的文本视图,可覆盖的原生组件包括map、video、canvas、camera,只支持嵌套cover-view、cover-image。

cover-image
基础库 1.4.0 开始支持,低版本需做兼容处理
覆盖在原生组件之上的图片视图,可覆盖的原生组件同cover-view,支持嵌套在cover-view里。

icon
图标。

属性名     类型  默认值 说明
type
String
icon的类型,有效值:success, success_no_circle, info, warn, waiting, cancel, download, search, clear

size
Number
23
icon的大小,单位px

color
Color
icon的颜色,同css的color

text
文本。

rich-text
基础库 1.4.0 开始支持,低版本需做兼容处理
富文本。

属性  说明  类型  必填  备注
name    标签名 String  是
支持部分受信任的HTML节点

attrs   属性  Object  否
支持部分受信任的属性,遵循Pascal命名法

children    子节点列表   Array   否
结构和nodes一致

progress
进度条。

属性名 类型  默认值 说明  最低版本
percent
Float
无
百分比0~100

show-info
Boolean
false
在进度条右侧显示百分比

stroke-width
Number
6
进度条线的宽度,单位px

color
Color
#09BB07
进度条颜色 (请使用 activeColor)

activeColor
Color

已选择的进度条的颜色

backgroundColor
Color

未选择的进度条的颜色

active
Boolean
false
进度条从左往右的动画

active-mode
String
backwards
backwards: 动画从头播;forwards:动画从上次结束点接着播

button
按钮。

checkbox-group
多项选择器,内部由多个checkbox组成。

form
表单,将组件内的用户输入的<switch/> <input/> <checkbox/> <slider/> <radio/> <picker/> 提交。
当点击 <form/> 表单中 formType 为 submit 的 <button/> 组件时,会将表单组件中的 value 值进行提交,需要在表单组件中加上 name 来作为 key。

input
输入框。

label
用来改进表单组件的可用性,使用for属性找到对应的id,或者将控件放在该标签下,当点击时,就会触发对应的控件。
for优先级高于内部控件,内部有多个控件的时候默认触发第一个控件。
目前可以绑定的控件有:<button/>, <checkbox/>, <radio/>, <switch/>。

picker
从底部弹起的滚动选择器,现支持五种选择器,通过mode来区分,分别是普通选择器,多列选择器,时间选择器,日期选择器,省市区选择器,默认是普通选择器。

picker-view
嵌入页面的滚动选择器

radio-group
单项选择器,内部由多个<radio/>组成。

slider
滑动选择器。

switch
开关选择器。

textarea
多行输入框。

navigator
页面链接。

audio
音频。

image
图片。

video
视频。

camera
系统相机。

live-player
基础库 1.7.0 开始支持,低版本需做兼容处理
实时音视频播放。

live-pusher
基础库 1.7.0 开始支持,低版本需做兼容处理
实时音视频录制。

map
地图。

canvas
画布。

自定义组件

创建自定义组件
类似于页面,一个自定义组件由 json wxml wxss js 4个文件组成。要编写一个自定义组件,首先需要在 json 文件中进行自定义组件声明(将 component 字段设为 true 可这一组文件设为自定义组件):
{
"component": true
}
同时,还要在 wxml 文件中编写组件模版,在 wxss 文件中加入组件样式,它们的写法与页面的写法类似。
代码示例:

<!-- 这是自定义组件的内部WXML结构 -->
<view class="inner">
  {{innerText}}
</view>
<slot></slot>
/* 这里的样式只应用于这个自定义组件 */
.inner {
  color: red;
}

Component({
  properties: {
    // 这里定义了innerText属性,属性值可以在组件使用时指定
    innerText: {
      type: String,
      value: 'default value',
    }
  },
  data: {
    // 这里是一些组件内部数据
    someData: {}
  },
  methods: {
    // 这里是一个自定义方法
    customMethod: function(){}
  }
})

使用自定义组件
使用已注册的自定义组件前,首先要在页面的 json 文件中进行引用声明。此时需要提供每个自定义组件的标签名和对应的自定义组件文件路径:
{
  "usingComponents": {
    "component-tag-name": "path/to/the/custom/component"
  }
}

分包加载

某些情况下,开发者需要将小程序划分成不同的子包,在构建时打包成不同的分包,用户在使用时按需进行加载。
在构建小程序分包项目时,构建会输出一个或多个功能的分包,其中每个分包小程序必定含有一个主包,所谓的主包,即放置默认启动页面/TabBar 页面,以及一些所有分包都需用到公共资源/JS 脚本,而分包则是根据开发者的配置进行划分。
在小程序启动时,默认会下载主包并启动主包内页面,如果用户需要打开分包内某个页面,客户端会把对应分包下载下来,下载完成后再进行展示。
目前小程序分包大小有以下限制:
• 整个小程序所有分包大小不超过 4M
• 单个分包/主包大小不能超过 2M

基础库

兼容

可以通过 wx.getSystemInfo 或者 wx.getSystemInfoSync 获取到小程序的基础库版本号。
也可以通过 wx.canIUse 详情 来判断是否可以在该基础库版本下直接使用对应的API或者组件

兼容方式 - 接口
对于新增的 API,可以用以下代码来判断是否支持用户的手机。

if (wx.openBluetoothAdapter) {
  wx.openBluetoothAdapter()
} else {
  // 如果希望用户在最新版本的客户端上体验您的小程序,可以这样子提示
  wx.showModal({
    title: '提示',
    content: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。'
  })
}

兼容方式 - 参数
对于 API 的参数或者返回值有新增的参数,可以判断用以下代码判断。

wx.showModal({
  success: function(res) {
    if (wx.canIUse('showModal.cancel')) {
      console.log(res.cancel)
    }
  }
})

兼容方式 - 组件
对于组件,新增的组件或属性在旧版本上不会被处理,不过也不会报错。如果特殊场景需要对旧版本做一些降级处理,可以这样子做。

Page({
  data: {
    canIUse: wx.canIUse('cover-view')
  }
})
<video controls="{{!canIUse}}">
  <cover-view wx:if="{{canIUse}}">play</cover-view>
</video>

运行机制

运行机制
小程序启动会有两种情况,一种是「冷启动」,一种是「热启动」。 假如用户已经打开过某小程序,然后在一定时间内再次打开该小程序,此时无需重新启动,只需将后台态的小程序切换到前台,这个过程就是热启动;冷启动指的是用户首次打开或小程序被微信主动销毁后再次打开的情况,此时小程序需要重新加载启动。

更新机制
小程序冷启动时如果发现有新版本,将会异步下载新版本的代码包,并同时用客户端本地的包进行启动,即新版本的小程序需要等下一次冷启动才会应用上。

性能

性能 Trace 工具

性能面板
从微信 6.5.8 开始,我们提供了性能面板让开发者了解小程序的性能。开发者可以在开发版小程序下打开性能面板,打开方法:进入开发版小程序,进入右上角更多按钮,点击「显示性能窗口」。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容

  • 配置文件 app.json的配置(全局) {// 用来配置页面的路径"pages":["pages/index/i...
    Q轩哥阅读 26,995评论 2 31
  • 微信小程序在无论在功能、文档及相关支持方面,都是优于前面几种微信账号类型,它提供了很多原生程序才有的接口,使得我们...
    伍华聪_开发框架阅读 1,574评论 0 53
  • 转载链接 注:本文转载知乎上的回答 作者:初雪 链接:https://www.zhihu.com/question...
    pengshuangta阅读 28,308评论 9 295
  • 昨天看了一下微信小程序官方文档,总结一下自己学习的个人心得. 首先从官方文档给的框架说起,微信小程序官方文档给出了...
    Mr大大大阅读 47,039评论 9 68
  • 我的喉咙不舒服,这是一种扁桃体肿大的感觉,当我用手去摸我的脖子的时候,感觉脖子粗。咽口水的时候会觉得不顺滑。 我知...
    aimingn阅读 234评论 0 0