从零开始带你撸一个小程序

从零开始带你撸一个小程序

微信推出小程序已经有些天了,相信同学们或多或少都把玩体验过小程序了。今天从零学习前端开发专栏就手把手带你,从零开始撸一个小程序。

P.S. 正式开始前先声明,本教程示例仅供前端萌新学习体验,闲杂人等可以退散啦。

小程序的注册(可跳过)

小程序是面向企业、政府、媒体等其他组织开放注册的,注册时必须提供相应的主体信息,所以假如你是不代表任何组织没注册过任何公司的独立开发者,就可以直接跳过这一步啦,虽然不注册就很遗憾的不能在真机体验调试自己写的小程序,不过最重要的是学习知识不是么?

给有条件的同学的传送门: 小程序注册图文教程

如果你之前有玩过微信公众号的话,注册和配置流程其实都大同小异。

小程序本地开发环境搭建

小程序使用微信Web开发者工具作为IDE。微信Web开发者工具是微信官方使用 NW.js 开发的一个跨平台桌面端应用,好坏先不做评价,至少能用。虽然现在小程序已经有了一些第三方的开发工具,有更多Hack以及更少的限制,但为了教程的简洁这里就不再赘述,有兴趣的同学可以自己去玩玩看。

微信Web开发者工具下载

第三方小程序开发工具

微信Web开发者工具的安装更是简单,不停点下一步就好了。

创建小程序项目

按照微信官方 简易教程 的指引,创建一个新的小程序项目。

我们需要打开微信Web开发者工具,扫码登陆,选择开发本地小程序,然后点击新建项目,填入你的小程序AppID,项目名称,目录等等。

其实不管你有没有注册小程序,为了方便调试最好都选择无AppID,如果选择无AppID一些API的返回都是本地模拟的,并且没有请求远程服务器的域名限制,反而方便你开发调试。

创建项目的时候我们可以勾选创建quick start项目,来当作我们编写代码的模板,其实这个quick start项目的代码就是微信官方的简易教程中的示例代码,当然如果你有心的话,也可以手动跟着教程把代码敲一遍。

开发者工具简介

创建好项目并打开之后,我们会看到一个类似或者其实根本就是Chrome打开开发者工具并启用移动端模拟的界面。

左边有3个选项卡【编辑】【调试】【项目】,开发的过程中大多数时间里我们都是在【编辑】【调试】之间来回切。

切换到【编辑】选项卡,我们就能看到小程序项目目录以及代码编辑界面。

小程序示例开发

接下来我们会在quick start项目代码的基础上开发一个获取用户位置并显示天气的小程序,一边学习理解小程序的框架设计,一边尝试动手编写代码。

首先每一个小程序都需要声明一个 App对象

//app.js
App({
  onLaunch: function () {
    //调用API从本地缓存中获取数据
    var logs = wx.getStorageSync('logs') || []
    logs.unshift(Date.now())
    wx.setStorageSync('logs', logs)
  getUserInfo:function(cb){
    var that = this;
    if(this.globalData.userInfo){
      typeof cb == "function" && cb(this.globalData.userInfo)
    }else{
      //调用登录接口
      wx.login({
        success: function () {
          wx.getUserInfo({
            success: function (res) {
              that.globalData.userInfo = res.userInfo;
              typeof cb == "function" && cb(that.globalData.userInfo)
  globalData:{
    userInfo:null

假如你之前了解过React/Vue任意一个框架的话,应该会看到很多他们的影子:

//React
class Timer extends React.Component {
  constructor(props) {
    super(props);
    this.state = { secondsElapsed: 0 };
  tick() {
    this.setState(prevState => ({
      secondsElapsed: prevState.secondsElapsed + 1
  componentDidMount() {
    this.interval = setInterval(() => this.tick(), 1000);
  componentWillUnmount() {
    clearInterval(this.interval);
  render() {
    return React.createElement(
      "div",
      null,
      "Seconds Elapsed: ",
      this.state.secondsElapsed
//Vue
var vm = new Vue({
  data: {
    a: 1
  created: function () {
    // `this` 指向 vm 实例
    console.log('a is: ' + this.a)
  mounted: function () {
    console.log('mounted!')
  updated: function () {
    console.log('updated!')
  destroyed: function () {
    console.log('destroyed!')

每一个小程序都会有一个app.json的配置文件,这里面其实有很多好玩的地方:

{
  "pages":[//小程序包含的页面
    "pages/index/index",//页面路径
    "pages/logs/logs"
  "window":{
    "backgroundTextStyle":"light",
    "navigationBarBackgroundColor": "#2f3b53",//标题栏背景色(这也许是小程序和移动端Web应用最大的不同了)
    "navigationBarTitleText": "Weather",//标题栏文字
    "navigationBarTextStyle":"white"//标题栏文字颜色
  "tabBar": {//配置小程序选项卡,是的,你不需要写多余的导航栏代码,只需要配置一段JSON即可
    "selectedColor":"#73d0f4", //Tab栏选中文字的颜色
    "list": [{//选项卡列表,最多可以配置5项
      "pagePath": "pages/index/index",//对应页面地址
      "iconPath": "assets/images/home_n.png",//Tab选项卡默认图标
      "selectedIconPath": "assets/images/home.png",//Tab选中后的图标
      "text": "首页"//Tab文字
    }, {
      "pagePath": "pages/logs/logs",
      "iconPath": "assets/images/box_n.png",
      "selectedIconPath": "assets/images/box.png",
      "text": "日志"

你可以在quick start项目的基础上把app.json文件修改成如上内容,当然你也可以参考 官方配置文档 自己折腾一会儿。

另外补充一下,Tab栏的图标当然不是凭空变出来的,你可以在 easyicon 或者其他类似的网站下载你需要的图标,具体风格可以参考微信底部导航,需要注意的是你要为每个图标准备两个文件,一个默认显示,一个在选中时显示。

制作这样图标文件的最简单办法是在PS中打开图标文件,选择图像->调整->去色并另存为。

小程序页面

小程序的所有页面一般都被保存在pages文件夹里,每一个独立的页面也有自己的文件夹,每个页面都包含自己的js/json/wxml/wxss文件,不管每个文件里有没有内容,你创建的页面只要一次调试编译之后这四个文件都会自动生成。

.js和.json文件可以延伸刚刚介绍过的app.js/app.json文件的概念,页面的这两个文件分别储存每个独立页面的逻辑和配置信息。

.wxml 可以理解为.html的阉割版,用来编写小程序页面的内容。

.wxss 可以理解为.css的阉割版,用来设计小程序页面的样式。

接下来我们再动手写几行代码,首先把pages/index/index.wxml改写成如下内容:

<!--index.wxml-->
<!--view是wxml中最基本的一种视图容器,你可以把它理解为div一类的东西-->
<view class="container">
  <!--bindtap类似于vue里的v-on:click,用于处理页面元素的事件绑定,待会儿我们会在index.js里看到一个bindViewTap的方法-->
  <view  bindtap="bindViewTap" class="userinfo">
  <!--{{userInfo.avatarUrl}}这类的双花括号和vue简直是一模一样了,用来显示js中的变量,当然也可以包含一些简单的运算和逻辑判断,image你就理解为img标签好了-->
    <image class="userinfo-avatar" src="{{userInfo.avatarUrl}}" background-size="cover"></image>
    <!--text可以理解为p标签,当然只是为了方便理解,上述所有的view/image/text等都是小程序预置的界面组件-->
    <text class="userinfo-nickname">{{userInfo.nickName}}</text>
  </view>
  <!--wx:if完全可以理解为vue当中的v-if,哈哈,其实不如去学学vue吧,weather是一个对象变量,可以用点来访问其属性值-->
  <view wx:if="{{weather.location}}" class="weather">
    <text class="city">{{weather.location.name}}</text>
    <text class="condition">{{weather.now.text}}</text>
    <text class="temp">{{weather.now.temperature}} ℃</text>
  </view>
</view>

接下来在index.wxss的底部添加下列内容,我承认我偷懒了,你可以稍微花点功夫认真设计一下你小程序的外观样式:

.weather {
  margin-top: 50px;
  display: flex;
  flex-direction: column;
.city,.condition,.temp {
  width:100%;
  margin-top: 10px;

wxss支持不完全的css选择器,引入了名为rpx的尺寸单位等,有关wxss具体可以查看 wxss文档

最后到了重头戏,让我们打开index.js,将其改写为如下内容:

//index.js
//获取应用实例
var app = getApp()
Page({
  data: {
    userInfo: {},
    //这里新增了一个weather变量
    weather: {}
  //事件处理函数,这就是刚刚在index.wxml当中绑定的事件函数
  bindViewTap: function() {
    //将函数的内容改写为我们自定义的一个方法
    this.getLocation()
  //这个自定义方法调用了wx.getLocation和wx.request两个小程序API,用来获取用户位置信息并从远程服务器请求相关天气数据
  getLocation: function () {
    //示例中有很多这样that = this的代码,但我发现最新的开发者工具是支持ES6的,你完全可以用autobinding一类的语法糖来避免这些冗余的绑定代码
    var that = this
    //这是小程序的一个API,用来获取用户的地理位置
    wx.getLocation({
      type: 'wgs84',
      success: function(res) {
        var latitude = res.latitude
        var longitude = res.longitude
        //wx.request用来发起向远程服务器的请求
        wx.request({
          //wx.request访问的远程网址必须是https,这里使用的是很棒的心知天气的API,可以免费注册
          url: 'https://api.thinkpage.cn/v3/weather/now.json?key=xxxxxxxxxxx&location='+latitude+':'+longitude, 
          success: function(res) {
            console.log(res.data.results[0])
            //setData方法可以理解为React当中的setState方法,用来修改我们在开头定义的weather变量,你不能直接通过data.weather来修改,那样的操作会破坏数据绑定
            that.setData({
              weather:res.data.results[0]
  //onLoad是页面的一个生命周期函数,类似于App对象中的,小程序的页面和应用对象都有一系列相关的生命周期函数
  onLoad: function () {
    console.log('onLoad')
    //这里再啰嗦一下,因为对象中的方法this默认指向undefined,所以我们需要手动指定this,这段代码还可以写成:
    * app.getUserInfo(function(userInfo){
    *    //更新数据
    *    this.setData({
    *      userInfo:userInfo
    *    })
    * }.bind(this))
    * 或者使用es6写成:
    * app.getUserInfo((userInfo) => {
    *    this.setData({
    *      userInfo:userInfo
    *    })
    var that = this
    //调用应用实例的方法获取全局数据
    app.getUserInfo(function(userInfo){
      //更新数据
      that.setData({
        userInfo:userInfo