邂逅小程序开发

678 阅读10分钟

简介

微信小程序是一种不需要下载安装即可使用的应用,它实现了应用“触手可及”的梦想,用户扫一扫或搜一下即可打开应用。

小程序是微信生态的一部分, 它提供了一种更加 方便和高效 的用户交互方式.

好处

  1. 用户使用的便捷性
  • 普通的APP我们需要的使用过程:了解APP - 下载 - 打开 - 注册 - 使用
  • 小程序版本的APP的使用过程:了解APP - 打开(扫码/搜索) - 使用
  1. 释放手机内存空间

    • 对于手机空间不大的人来说, 安装很多App会带来灾难, 经常面临空间不足
    • 小程序不需要安装, 而且有规定的大小(目前不能超过8M,包括在使用分包的情况下)
  2. 让手机桌面更加简洁

    • 不需要下载App, 就不需要花时间来管理自己的App了
  3. 一端开发,多端运行(iOS端、Android端),并且底层可以调用原生的各种接口

准备 --- 申请APPID

登录微信公众平台进行申请

  1. 一个账号对应一个小程序
  2. 企业、政府、媒体、其他组织主体可以注册50个小程序
  3. 个体户和个人类型主体可注册5个小程序
  4. 每个个体户和个人类型主体可以免费拥有2个云的存储开发空间

小程序组成

LuxzAD.png

  • 页面布局 --- WXML (类似于HTML)

  • 页面样式 --- WXSS (类似于CSS, 某些不支持,某些做了增强)

  • 页面脚本 --- JavaScript + WXS (WeixinScript)

LuxCdT.png

LuD8Wz.png

app.json --- 全局配置文件

{
  // 所有的小程序页面都需要在这里进行注册
  // 数组的每一项都是对应页面的json文件所在的路径地址
  "pages": [
    // 数组的第一项会被作为整个小程序的首页被加载
    "pages/home/home",
    "pages/about/about"
  ]
}

初体验

数据绑定

<!--
  在wxml中,有2个基本的容器组件
  view <-> div
  text <-> span
-->
<view>
  <!-- 在wxml中绑定数据使用的是mustache语法 -->
  {{ msg }}
</view>
// 对于每一个页面都需要调用Page函数并传递配置对象
// 对于App组件,则需要调用App函数并传递配置对象
// 在解析的时候,小程序会自动去创建对应的Page实例或App实例
Page({
  // 在data选项中定义页面的状态
  // 注意: 这里是对象不是函数,小程序创建实例的时候data并没有像vue一样放到组件实例的原型上
  data: {
    msg: 'Hello World'
  }
})

循环遍历

<!-- wx:for是小程序提供的循环指令 -->
<!-- wx:for后边需要加上变量,如果跟上的是字符串,那么会将字符串转换为字符数组后再进行遍历 -->
<view wx:for="{{ users }}" wx:key="name">
  <!-- item是循环的每一项 -> 小程序默认提供的变量 -->
  {{ item.name }} --- {{ item.age }}
</view>

事件绑定

<view>
  {{ counter }}
</view>

<!-- button的size属性可以调整button的大小, 默认宽度为100% -->
<!--
  在小程序中 事件名都是以bind开头的 全部小写的
  bindtap -> 屏幕轻击事件
  注意: 事件绑定的时候不需要使用mustache语法,只有在进行属性值绑定的时候才需要使用mustache语法
-->
<button size="mini" bindtap="handleTap">+</button>
Page({
  data: {
    counter: 0
  },

  // 直接在配置对象中定义对应的事件响应函数
  handleTap() {
    // 和React一样,需要调用setData函数后,界面才会发生更新
    // 注意: 是setData方法,不是setState方法
    this.setData({
      counter: this.data.counter + 1
    })
  }
})

mvvm

L1L1xW.png

小程序使用了和vue类似的MVVM模型,其中充当ViewModel的是小程序原生框架MINA框架

MVVM为什么好用呢?

  • DOM Listeners: ViewModel层可以将DOM的监听绑定到Model层
  • Data Bindings: ViewModel层可以将数据的变量, 响应式的反应到View层

MVVM架构将我们从 命令式编程 转移到 声明式编程

配置

小程序的很多开发需求被规定在了配置文件中

  • 这样做可以更有利于我们的开发效率
  • 并且可以保证开发出来的小程序的某些风格是比较一致

在小程序中,主要有以下四种配置文件

文件名功能
project.config.json项目配置文件, 比如项目名称、appid等, 类似于package.json,jsconfig.json,tsconfig.json的功能
用以对编辑器的行为和执行环境进行统一的配置(尤其是在多人协作开发的时候)
默认不需要修改,如果需要修改在小程序的IDE的详情中进行配置,project.config.json会同步更新
sitemap.json默认情况下,小程序会开启搜索功能
这就意味着微信会使用爬虫为我们的小程序设置检索索引,以提升小程序的曝光率
如果我们某些页面不需要被微信爬虫爬取的时候,可以修改该文件
app.json全局配置文件
page.json页面配置文件
每一个page都有自己单独的页面配置文件

全局配置

全局配置比较多, 我们这里将几个比较重要的. 完整的查看官方文档

pages: 页面路径列表

  • 用于指定小程序由哪些页面组成,每一项都对应一个页面的 路径(含文件名) 信息
  • 小程序中所有的页面都是必须在pages中进行注册的

window: 全局的默认窗口展示

"window": {
  // 导航栏背景色 --- 只支持 HexColor
  "navigationBarBackgroundColor": "#ff5777",
  // 导航栏文本颜色 --- white | black
  "navigationBarTextStyle": "white",
  // 导航栏标题
  "navigationBarTitleText": "小程序",
  // 下拉列表的背景色
  "backgroundColor": "#f2f4f5",
  // 下拉列表的loading颜色 --- dark | light
  "backgroundTextStyle": "dark",
  // 是否允许下拉刷新 --- ios默认情况下就可以下拉刷新,Android默认情况下不可以下拉刷新
  "enablePullDownRefresh": true
}

tabBar: 底部tab栏的展示

"tabBar": {
  // 被选中是tab的颜色
  "selectedColor": "#ff5777",
   // 底部tab列表,个数为[2, 5]
  // 只有在list数组中注册过的页面定部才会存在tab栏
  // 如果一个页面并没有在list数组中进行注册,那么这个页面底部并不会出现tab栏
  "list": [
    {
      // 跳转路径
      "pagePath": "pages/home/home",
      // 显示文本
      "text": "首页",
      // 图片路径
      "iconPath": "assets/tabbar/home.png",
      // 选中时候图片路径
      "selectedIconPath": "assets/tabbar/home_active.png"
    },

    {
      "pagePath": "pages/profile/profile",
      "text": "设置",
      "iconPath": "assets/tabbar/profile.png",
      "selectedIconPath": "assets/tabbar/profile_active.png"
    }
  ]
}

页面配置

每一个小程序页面也可以使用 .json 文件来对本页面的窗口表现进行配置

页面中配置项在当前页面会覆盖 app.json 的 window 中相同的配置项

{
  "usingComponents": {},
  // 注意:这里的配置是顶层配置选项,不是windows的子配置选项
  "navigationBarTitleText": "分类",
  "enablePullDownRefresh": false
}

双线程模型

小程序的宿主环境是微信客户端

宿主环境为了执行小程序的各种文件:wxml文件、wxss文件、js文件,提供了小程序的双线程模型

L1dObD.png

  • WXML模块和WXSS样式运行于 渲染层,渲染层使 用WebView线程渲染 (一个程序有多个页面,会使 用多个WebView的线程)
  • JS脚本(app.js/home.js等)运行于 逻辑层,逻辑 层使用JsCore运行JS脚本 (沙盒环境运行JS代码,不允许执行任何和浏览器相关的接口,比如跳转页面、操作DOM等)
  • 两个线程都会经由微信客户端(Native)进行中转 交互

wxml和DOM树

wxml等价于一棵DOM树,也可以使用一个JS对象来模拟, 而这个JS对象就是我们所说的虚拟DOM对象

L1eGM6.png

初始化渲染

WXML可以先转成JS对象(虚拟DOM树),再渲染出真正的DOM树

L1eLVT.png

数据发生变化

通过setData把msg数据从“Hello World”变 成“Goodbye”

  • 产生的JS对象对应的节点就会发生变化
  • 此时可以对比前后两个JS对象得到变化的部分 --- DOM Diff算法
  • 然后把这个差异应用到原来的Dom树上
  • 从而达到更新UI的目的,这就是数据驱动的原理

L1ePkE.png

界面渲染的整体流程

  1. 在渲染层,宿主环境会把WXML转化成对应的JS对象;

  2. 将JS对象再次转成真实DOM树,交由渲染层线程渲染;

  3. 数据变化时,逻辑层提供最新的变化数据,JS对象发生变化比较进行diff算法对比;

  4. 将最新变化的内容反映到真实的DOM树中,更新UI;

小程序启动流程

L1e53W.png

App的生命周期函数

每个小程序都需要在 app.js 中调用 App 方法注册小程序示例

在注册时, 可以绑定对应的生命周期函数, 在生命周期函数中, 执行对应的代码

// 调用App方法注册App组件
App({
    // 页面初始化完成的时候执行的函数
    // 当一个小程序退出到后台后的2小时,是一直存在的,进程并没有被杀死
    // 所以此时如果再次进入小程序只会执行onShow方法,并不会执行onLanch方法
    onLaunch() {
      console.log('页面初始化')
    
      // 可以在这里获取用户信息
      wx.getUserInfo({
          success(res) {
            console.log(res)
          }
      })
    },

    onShow() {
      console.log('界面显示')
    },

    onHide() {
      console.log('界面隐藏')
    },

    onError() {
      console.log('界面发生了错误')
    }
})

在App中常做的操作

  1. 判断小程序的进入场景 , 常见的打开场景:群聊会话中打开、小程序列表中打开、微信扫一扫打开、另一个小程序打开

​ 在onLaunch和onShow生命周期回调函数中,会有options参数,其中有scene值, 可以用来判断小程序进入的场景值

App({
  onLaunch(options) {
    console.log(options.scene)
  },

  onShow(options) {
    console.log(options.scene)
  }
})
  1. 监听生命周期函数,在生命周期中执行对应的业务逻辑,比如在某个生命周期函数中获取微信用户的信息

获取微信用户的基本信息的方式:

  • wx.getUserInfo

    wx.getUserInfo({
      success(res) {
        console.log(res)
      }
    })
    
  • button组件 – 将open-type改成getUserInfo,并且绑定bindgetuserinfo事件去获取

<button
  open-type="getUserInfo"
  bindgetuserinfo="handleUserInfo"
  size="mini"
>
  click me
</button>
Page({
  handleUserInfo(e) {
    console.log(e.detail.userInfo)
  }
})
  • 使用open-data组件展示用户信息 --- 注意: 是展示,不是获取
<open-data type="userNickName" />

<!-- 这个展示出来的结果是一个图片 -->
<open-data  type="userAvatarUrl" />
  1. 因为App()实例只有一个,并且是全局共享的(单例对象),所以我们可以将一些共享数据放在这里
// App.js
App({
  // key的名称是自定义的,但是一般取名为globalData
  globalData: {
    username: 'Klaus',
    age: 24
  }
})

// home.js
// getApp方法可以获取 App组件的实例对象
const app = getApp()
console.log(app.globalData)

Page生命周期函数

在每一个页面的Page.js中可以绑定对应的生命周期函数, 在生命周期函数中, 执行对应的代码

Page({
  // 监听页面加载
  onLoad() {
    console.log('onLoad')
  },

  // 页面显示出来
  onShow() {
    // 在生命周期中的this的值是当前Page实例
    console.log(this)
    console.log('onShow')
  },

  // 第一次被渲染完成
  onReady() {
    console.log('onReady')
  },

  // 页面隐藏
  onHide() {
    console.log('onHide')
  },

  // 页面卸载
  onUnload() {
    console.log('onUnload')
  }
})

在Page中常做的操作

  1. 在生命周期函数中发送网络请求,从服务器获取数据;
  2. 初始化一些数据,以方便被wxml引用展示
  3. 监听wxml中的事件,绑定对应的事件函数
  4. 其他一些监听(比如页面滚动、上拉刷新、下拉加载更多等)
Page({
  // 初始化数据
  data: {
    user: {}
  },

  onShow() {
   // 发生网络请求
   wx.request({
     // 1. 默认情况下,在小程序中所有请求的接口都需要在控制台(网页端)进行配置,否则会无法请求
     // 2. 在开发的过程中,为了调试方便,可以在详情选项卡中暂时关闭合法域名校验,此时就可以正常发生网络请求
     url: 'http://httpbin.org/get?name=klaus&age=23',
     // success方法推荐使用箭头函数进行定义
     // 如果使用箭头函数进行定义,内部的this就是当前的Page实例
     // 如果使用的是普通的function定义方式,那么内部的this就是undefined (因为小程序内部没有globalThis)
     success: res => {
      //  console.log(this)

       // 具体获取的数据可以在小程序开发工具的AppData选项卡中进行查看 
       this.setData({
         user: res.data.args
       })
     }
   })  
  },

  // 绑定自定义事件
  handleTap() {
    // 自定义事件中的this依旧是Page实例
    console.log(this)
  },

  // 内置事件
  onPageScroll(detail) {
    console.log(detail.scrollTop)
  },

  onReachBottom() {
    console.log('触底了')
  },

  onPullDownRefresh() {
    console.log('下拉刷新')
  }
})

LHWjUU.png