小程序开发基础

课程目标

  1. 学习⼩程序基本原理,对⽬前⾏业内⼩程序有基本理解;
  2. 掌握微信⼩程序基本API;
  3. 掌握微信⼩程序发布流程;

知识要点

⼩程序机制介绍

什么是⼩程序

⼩程序页⾯本质上是⽹页:

  1. 使⽤技术栈与⽹页开发是⼀致的,都⽤到HTML、CSS和JS;
  2. 区别:不⽀持浏览器API,只能⽤微信提供的API;

外部代码通过⼩程序这种形式,在⼿机 App ⾥⾯运⾏:微信、⽀付宝, ⼩程序可以视为只能⽤微信等 APP 作为载体打开和浏览的⽹站。

⼩程序的发展历程

  1. 微信⼩程序形态:
    1. ⼩程序从业务形式上更像是公众号开发的演变产物;
    2. 早期微信通过 sdk 的形式,增强了开发者开发公众号⽹页的能⼒;
    3. ⼩程序的诞⽣是微信本身迈向平台化超级 App 的业务⾏为,并且帮助⽤户更好的实现了「轻量级 Web App」;
  2. 开发标准:
    1. 最初微信⼩程序⾃⼰定义了⼀套”标准“,最开始的框架甚⾄没有组件、没有 npm,和 Web ⽣态严重脱节;
    2. 由于特殊的双线程模型与四不像的语法,开发者苦不堪⾔,⼩程序的开放只是对三⽅业务的开放⽽已;
  3. 商家涌⼊:
    1. ⼩程序业务的开放性 ->平台型 App;
    2. ⽐如:⽀付宝⼩程序、百度⼩程序、淘宝⼩程序、360⼩程序、快应⽤……
    3. ⼩程序设计⽬的:⼤多数选择了和微信类似的架构、框架,更多不是从技术角度考虑,⽽是想尽可能蹭微信⼩程序的福利,让开发者可以更快的投放到⾃⼰的平台;

原⽣微信⼩程序框架介绍

⼩程序的⽬录结构

⼯程的⼯作⽬录中包含以下⽂件:

技术选型

渲染界⾯的技术⽅案:

  1. ⽤纯客户端原⽣技术渲染;
  2. ⽤纯 Web 技术渲染;
  3. ⽤客户端原⽣技术与 Web 技术结合的混合技术(简称 Hybrid 技术)渲染;

⽅案对⽐:

  1. 开发门槛:Web 门槛低,Native 也有像 RN 这样的框架⽀持;
  2. 体验:Native 体验⽐ Web 要好太多,Hybrid 在⼀定程度上⽐ Web 接近原⽣体验;
  3. 版本更新:Web ⽀持在线更新,Native 则需要打包到微信⼀起审核发布;
  4. 管控和安全:Web 可跳转或是改变页⾯内容,存在⼀些不可控因素和安全风险;

⽅案确定:

  1. ⼩程序的宿主环境是微信等⼿机 APP,⽤纯客户端原⽣技术来编写⼩程序,那么⼩程序代码每次都需要与⼿机 APP 代码⼀起发版❎;
  2. Web ⽀持有⼀份副本资源包放在云端,通过下载到本地,动态执⾏后即可渲染出界⾯,但纯 Web 技术在⼀些复杂的交互上可能会⾯临⼀些性能问题❎;
    1. 在 Web 技术中,UI 渲染跟脚本执⾏都在⼀个单线程中执⾏,这就容易导致⼀些逻辑任务抢占 UI 渲染的资源。
  3. 两者结合起来的 Hybrid 技术来渲染⼩程序,⽤⼀种近似 Web 的⽅式来开发,并且可以实现在线更新代码✅;
    1. 扩展 Web 的能⼒。⽐如像输⼊框组件(input, textarea)有更好地控制键盘的能⼒;
    2. 体验更好,同时也减轻 WebView 的渲染⼯作;
    3. ⽤客户端原⽣渲染内置⼀些复杂组件,可以提供更好的性能;
双线程模型

⼩程序的渲染层和逻辑层分别由 2 个线程管理:

  1. 视图层 -> WebView 进⾏渲染;
  2. 逻辑层 -> JsCore 线程运⾏ JS脚本;

设计⽬的:为了管控和安全等问题,阻⽌开发者使⽤⼀些,例如浏览器的window对象,跳转页⾯、操作DOM、动态执⾏脚本的开放性接⼝;使⽤沙箱环境提供纯 JavaScript 的解释执⾏环境

  1. 客户端系统:JavaScript 引擎;
  2. iOS: JavaScriptCore 框架;
  3. 安卓:腾讯 x5 内核提供的 JsCore ;

⼩程序双线程模型:

  • 逻辑层:创建⼀个单独的线程去执⾏ JavaScript,在这⾥执⾏的都是有关⼩程序业务逻辑的代码,负责逻辑处理、数据请求、接⼝调⽤等;
  • 视图层:界⾯渲染相关的任务全都在 WebView 线程⾥执⾏,通过逻辑层代码去控制渲染哪些界⾯。⼀个⼩程序存在多个界⾯,所以视图层存在多个 WebView 线程;
  • JSBridge 起到架起上层开发与Native(系统层)的桥梁,使得⼩程序可通过API使⽤原⽣的功能,且部分组件为原⽣组件实现,从⽽有良好体验;
数据驱动视图变化

问题:JS 逻辑代码放到单独的线程去运⾏,在 Webview 线程⾥没法直接操作 DOM。开发者如何实现动态更改界⾯呢?
DOM 的更新通过简单的数据通信来实现

逻辑层和视图层的通信会由 Native (微信客户端)做中转,逻辑层发送⽹络请求也经由 Native 转发。
JS 对象模拟 DOM 树 -> ⽐较两棵虚拟 DOM 树的差异 -> 把差异应⽤到真正的 DOM 树上。

  1. 在渲染层把 WXML 转化成对应的 JS 对象;
  2. 在逻辑层发⽣数据变更的时候,通过宿主环境提供的 setData ⽅法把数据从逻辑层传递到 Native,再转发到渲染层;
  3. 经过对⽐前后差异,把差异应⽤在原来的 DOM 树上,更新界⾯;
事件的处理

视图层需要进⾏交互,这类反馈应该通知给开发者的逻辑层,需要将对应的处理状态呈现给⽤户。
视图层的功能只是进⾏渲染,因此对于事件的分发处理,微信进⾏了特殊的处理,将所有的事件拦截后,丢到逻辑层交给JS处理。

事件的派发处理包括事件捕获和冒泡两种:
通过native传递给 JSCore,通过 JS 来响应响应的事件之后,对 Dom 进⾏修改,改动会体现在虚拟 Dom 上,然后再进⾏真实的渲染。

运⾏机制

⼩程序启动机制:

  1. 冷启动:⽤户⾸次打开或⼩程序被微信主动销毁后再次打开的情况,此时⼩程序需要重新加载启动。
  2. 热启动:假如⽤户已经打开过某⼩程序,然后在⼀定时间内再次打开该⼩程序,此时⽆需重新启动,只需将后台状态的⼩程序切换到前台;

注意:

  • ⼩程序没有重启的概念;
  • 当⼩程序进⼊后台,客户端会维持⼀段时间的运⾏状态,超过⼀定时间后(⽬前是5分钟)会被微信主动销毁;
  • 当短时间内(5s)连续收到两次以上收到系统内存告警,会进⾏⼩程序的销毁;

⼩程序框架对⽐

小程序原生语法
  1. ⽬前⼩程序⽣态⽀持开发者利⽤前端部分⽣态开发应⽤的;
  2. ⽬前⼩程序已经能够做到前端⼯程化,并且植⼊前端⽣态中已有的⼀些理念,例如状态管理、CLI ⼯程化等等,与早期 npm 能⼒的缺失、只能通过模板渲染实现组件化不可同⽇⽽语;
  3. 当业务的需求只有投放到微信或者⽀付宝⼩程序时,原⽣语法可以成为前端程序员们的⼀个选择;前端能⼒基本都可以在⼩程序上复⽤(如状态管理库颗粒化管理组件状态、TS等);
增强型框架

指⼩程序引⼊ npm 之后,有了更加开放的能⼒所带来的收益;

以⼩程序原⽣语法为主,在逻辑层引⼊了增强语法来优化应⽤性能或者提供更便捷的使⽤⽅法;

Example:腾讯开源的 omix 框架为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 逻辑层
create.Page(stroe, {
// 声明依赖
use: [logs],
computed: {
logsLength() {
return this.logs.length
}
},
onLoad: function () {
// 响应式,自动更新视图
this.store.data.logs = (wx.getStorageSync('logs') || []).map(log => {
return util.formatTime(new Date(log))
})
setTimeout(() => {
// 响应式,自动更新视图
this.store.data.logs[0] = 'Changed!'
}, 1000)
}
})
1
2
3
4
5
6
// 视图层
<view class="container log-list">
<block wx:for="{{logs}}" wx:for-item="log">
<text class="log-item">{{index + 1}}. {{log}}</text>
</block>
</view>
  1. 整体保留⼩程序已有的语法,但在此基础之上,对它进⾏了扩充和增强;
  2. ⽐如引⼊了 Vue 中⽐较有代表性的 computed,⽐如能够直接通过 this.store.data.logs[0] = 'Changed' 修改状态。可以说是在⼩程序原⽣半 Vue 半 React 的语法背景下,彻底将其 Vue 化的⼀种⽅案;

使⽤增强型框架优势:

  1. 可以在只引⼊极少依赖,并且保留对⼩程序认知的情况下,⽤更加舒爽的语法来写代码;
  2. 对于⽬标只投放到特定平台⼩程序的开发者或者⾮专业前端⽽⾔是⽐较好的选择之⼀;因为你只需要关注很少的新增⽂档和⼩程序⾃身的⽂档就⾜够了,底层不需要考虑;
转化类框架

⽬的:让开发者⼏乎不⽤感受⼩程序原⽣语法,更⼤程度对接前端已有⽣态,并且可以实现「⼀码多端」的业务诉求,只是最后的构建产物为⼩程序代码。

编译时

通过编译分析的⽅式,将开发者写的代码转换成⼩程序原⽣语法。

以 Rax 编译时和 Taro 2.0 为例,⾯向开发者的语法是类 React 语法,开发者通过写有⼀定语法限制的 React 代码,最后转换产物 1:1 转换成对应的⼩程序代码。

以⼀段简单的代码为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// rax
import { createElement, useEffect, useState } from 'rax';
import View from 'rax-view';
export default function Home() {
const [name, setName] = useState('world');
useEffect(() => {
console.log('Here is effect.');
}, [])
return <View>Hello {name}</View>;
}

// 转化为小程序的代码
// 逻辑层
import { __create_component__, useEffect, useState } from 'jsx2mp-runtime/dist/ali.esm.js'
function Home() {
const [name, setName] = useState('world');
useEffect(() => {
console.log('Here is effect.');
}, []);
this._updateData({
_d0: name
}); }
Component(__create_component__(Home));
// 视图层
<block a:if="{{$ready}}">
<view class="__rax-view">{{_d0}}</view>
</block>
  1. 开发者虽然写的是类 React 语法,但是转换后的代码和渐进增强型框架⾮常类似;
  2. 开发者可以⽐较清晰的看出编译前后代码的对应关系;

编译时⽅案会通过 AST 分析,将开发者写的 JSX 中 return 的模板部分构建到视图层,剩余部分代码保留,然后通过运⾏时垫⽚模拟 React 接⼝的表现。

优势:

  1. 运⾏时性能损耗低;
  2. ⽬标代码明确,开发者所写即所得;
  3. 运⾏时、编译时优化:⽐如框架会给予开发者更多的语法⽀持以及默认的性能优化处理,⽐如避免多次 setData,亦或是⻓列表优化等等;

劣势:

  1. 语法限制⾼:需要完全命中开发者在模板部分所⽤到的所有语法,语法受限,如由于是 1:1 编译转换,开发者在开发的时候还是不得不去遵循⼩程序的开发规范,⽐如⼀个⽂件中定义只能定义⼀个组件之类的;
运行时

相⽐于上⾯的编译时,最⼤的优势是可以⼏乎没有任何语法约束的去完成代码编写。

通过在逻辑层模拟 DOM/BOM API,将这些创建视图的⽅法转换为维护⼀棵 VDOM tree,再将其转换成对应 setData 的数据,最后通过预置好的模板递归渲染出实际视图。

优势:没有语法限制;
劣势:以⼀定的性能损耗来换取更为全⾯的 Web 端特性⽀持;

微信⼩程序

微信小程序基本内容

代码 github 地址:https://github.com/wechat-miniprogram/miniprogram-demo

基础

官⽅⽂档:https://developers.weixin.qq.com/miniprogram/dev/framework/

⼩程序代码组成:

  • WXML:(WeiXin Markup Language)
  • WXSS:(WeiXin Style Sheets)
  • WXS:(WeiXin Script)

⼩程序框架:

  1. 逻辑层
    1. 官⽅⽂档:https://developers.weixin.qq.com/miniprogram/dev/framework/app-service/
    2. 使⽤ JavaScript 引擎为⼩程序提供开发者 JavaScript 代码的运⾏环境以及微信⼩程序的特有功能;
    3. 开发者写的所有代码最终将会打包成⼀份 JavaScript ⽂件,并在⼩程序启动的时候运⾏,直到⼩程序销毁。这⼀⾏为类似 ServiceWorker,所以逻辑层也称之为 App Service;
    4. 增加 App 和 Page ⽅法,进⾏程序注册页⾯注册
    5. 增加 getApp 和 getCurrentPages ⽅法,分别⽤来获取 App 实例和当前页⾯栈;
    6. 提供丰富的 API,如微信⽤户数据,扫⼀扫,⽀付等微信特有能⼒;
    7. 提供模块化能⼒,每个页⾯有独⽴的作⽤域
    8. ⼩程序框架的逻辑层并⾮运⾏在浏览器中,因此 JavaScript 在 web 中⼀些能⼒都⽆法使⽤,如 window,document 等。
  2. 视图层
    1. 官⽅⽂档:https://developers.weixin.qq.com/miniprogram/dev/framework/view/

基础核⼼

  1. ⼩程序运⾏时
    1. ⼩程序⽣命周期:热启动 == 后台切前台
    2. 更新机制
      1. 启动时同步更新
        1. 定期检查⼩程序版本;
        2. 长时间未使⽤⼩程序;
      2. 启动时异步更新
        1. 打开发现有新版本,异步下载,下次冷启动时加载新版本;
      3. 开发者⼿动更新
        1. wx.getUpdateManager
  2. ⾃定义组件
  3. 代码注⼊
    1. 按需注⼊:"lazyCodeLoading": "requiredComponents";⼩程序仅注⼊当前页⾯需要的⾃定义组件和页⾯代码,在页⾯中必然不会⽤到的⾃定义组件不会被加载和初始化;
    2. ⽤时注⼊:在开启「按需注⼊」特性的前提下,指定⼀部分⾃定义组件不在⼩程序启动时注⼊,⽽是在真正渲染的时候才进⾏注⼊,使⽤占位组件在需要渲染但注⼊完成前展示;
  4. 分包加载
    1. 原则
      1. 声明 subpackages 后,将按 subpackages 配置路径进⾏打包,subpackages 配置路径外的⽬录将被打包到 app(主包) 中;
      2. app(主包)也可以有⾃⼰的 pages(即最外层的 pages 字段);
      3. subpackage 的根⽬录不能是另外⼀个 subpackage 内的⼦⽬录;
      4. tabBar 页⾯必须在 app(主包)内
    2. 独⽴分包
      1. 开发者可以按需将某些具有⼀定功能独⽴性的页⾯配置到独⽴分包中。当⼩程序从普通的分包页⾯启动时,需要⾸先下载主包;
      2. 独⽴分包运⾏时,App 并不⼀定被注册,因此 getApp() 也不⼀定可以获得 App 对象;基础库 2.2.4 版本开始 getApp ⽀持 [allowDefault] 参数,在 App 未定义时返回⼀个默认实现。当主包加载,App 被注册时,默认实现中定义的属性会被覆盖合并到真正的 App 中;
  5. ⼩程序如何调试?
    1. vconsole;
    2. sourceMap;
    3. 实时⽇志:重写log,使⽤wx.getRealtimeLogManager封装,在运营后台“开发->开发管理->运维中⼼->实时⽇志”查看;
    4. errno:针对API的cb err进⾏状态码的判断,便于针对业务场景语义化展示;
  6. ⼩程序如何兼容版本?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    // 1. 版本号比较
    const version = wx.getSystemInfoSync().SDKVersion

    if (compareVersion(version, '1.1.0') >= 0) {
    wx.openBluetoothAdapter()
    } else {
    // 如果希望用户在最新版本的客户端上体验您的小程序,可以这样子提示
    wx.showModal({
    title: '提示',
    content: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。'
    })
    }
    // 2. API是否存在
    if (wx.openBluetoothAdapter) {
    wx.openBluetoothAdapter()
    } else {
    // 如果希望用户在最新版本的客户端上体验您的小程序,可以这样子提示
    wx.showModal({
    title: '提示',
    content: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。'
    })
    }
    // 3. wx.canIUse
    wx.showModal({
    success: function(res) {
    if (wx.canIUse('showModal.success.cancel')) {
    console.log(res.cancel)
    }
    }
    })
    // 4. 设置最低基础库版本:运营后台设置最低基础库版本
框架

官⽅⽂档:https://developers.weixin.qq.com/miniprogram/dev/reference/

  1. ⼩程序配置
    1. 全局配置
      1. 根⽬录下app.json;
    2. 页⾯配置
      1. 在page页⾯中对应的json⽂件,权重最⾼;
      2. 原先在根⽬录下的app.json中window内属性,在页⾯json中⽆需添加window;
    3. sitemap 配置
      1. 根⽬录下sitemap.json;
  2. 框架接⼝
    1. ⼩程序App

      1. App:必须在 app.js 中调⽤,必须调⽤且只能调⽤⼀次
        1. onLaunch
        2. onShow
        3. onHide
        4. onError
        5. onPageNotFound
        6. onUnhandledRejection
        7. onThemeChange
        8. 其他:可以添加任意的函数或数据变量到 Object 参数中,app.js中⽤ this 可以访问; (Tips:⾮原⽣事件最好不要⽤on开头)
      2. getApp:外部访问App中数据的⽅式
    2. 页⾯

      1. Page:在页⾯级别中的”app.js”
        1. data
        2. ⽣命周期事件
          1. onLoad:加载时触发
          2. onReady:渲染完成触发
          3. onShow
          4. onHide
          5. onUnload
        3. 页⾯事件处理事件
          1. onPullDownRefresh
          2. onReachBottom
          3. onPageScroll:监听页⾯滚动
          4. onAddToFavorites:添加到收藏并⾃定义收藏内容
          5. onShareAppMessage:转发事件
          6. onShareTimeline:转发朋友圈
          7. onResize
          8. onTabItemTap
          9. onSaveExitState:页⾯销毁前
        4. 组件事件处理
          1. wxml中绑定的⾃定义事件
          2. Page.route
          3. Page.prototype.setData
            1. 注意:可以以数据路径来改变数组中的某⼀项或对象的某个属性,如 array[2].messagea.b.c.d,并且不需要在 this.data 中预先定义。
        5. 页⾯间通信使⽤ wx.navigateTo 打开,这两个页⾯间将建⽴⼀条数据通道:
          1. 被打开的页⾯可以通过 this.getOpenerEventChannel() ⽅法来获得⼀个 EventChannel 对象;
          2. wx.navigateTo 的 success 回调中也包含⼀个 EventChannel 对象;
            1. 这两个 EventChannel 对象间可以使⽤ emit 和 on ⽅法相互发送、监听事件;
      2. getCurrentPage
        1. 获取当前页⾯栈,数组中第⼀个元素为⾸页,最后⼀个元素为当前页⾯;
        2. 场景:
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25
          26
          27
          28
          29
          30
          // 1. 进入小程序非默认首页时,需要执行对应操作
          Page({
          onShow() {
          let page = getCurrentPages(); // 当前页面栈
          if (page.length === 1) {
          // TODO
          }
          }
          })
          // 跨页面赋值
          let pages = getCurrentPages();// 当前页面栈
          let prevPage = pages[pages.length - 2];// 上一页面
          prevPage.setData({
          // 直接给上一页面赋值
          });
          // 3. 页面跳转后自动刷新
          wx.switchTab({
          url: '../index/index',
          success: function (e) {
          const page = getCurrentPages().pop(); // 当前页面
          if (page == undefined || page == null) return;
          page.onLoad();
          }
          })

          // 4. 获取当前页面相关信息
          let pages = getCurrentPages(); // 当前页面栈
          // 当前页面为页面栈的最后一个元素
          let prevPage = pages[pages.length - 1]; // 当前页面
          console.log( prevPage.route) // 举例:输出为 ‘pages/index/index’
    3. ⾃定义组件(参考上⽂)

      1. Component
      2. Behavior
    4. 模块化

      1. require
        1. 引⼊module.export或者 export暴露出的接⼝,需要引⼊模块⽂件相对于当前⽂件的相对路径,或npm模块名,或npm模块路径。不⽀持绝对路径
      2. module:当前模块对象
      3. export:module.export的引⽤
      4. requirePlugin:引⽤插件
      5. requireMiniProgram:引⽤当前⼩程序
    5. 基础功能

      1. console

        1. console.debug
        2. console.error
        3. console.log
        4. console.info
        5. console.warn
        6. console.group
        7. console.groupEnd
      2. 定时器

        1. setTimeout
        2. clearTimeout
        3. setInterval
        4. clearInterval
      3. WXML

        1. 数据绑定

          1. 数据绑定使⽤ Mustache 语法(双⼤括号)包起来,与Page⾥data变量绑定起来;
          2. ⽀持类型
            1. 变量:<view> {{ message }} </view>
            2. 属性:<view id="item-{{id}}"> </view>
            3. 控制属性:<view wx:if="{{condition}}"> </view>
            4. 关键字(在双引号间):<checkbox checked="{{false}}"> </checkbox>
            5. 运算:<view> {{a + b}} + {{c}} + d </view>
            6. 逻辑:<view wx:if="{{length > 5}}"> </view>
            7. etc……
        2. 列表渲染

          1
          2
          3
          4
          5
          6
          7
          8
          9
          Page({
          data: {
          array: [{
          message: 'foo',
          }, {
          message: 'bar'
          }]
          }
          })
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          <!--默认数组的当前项的下标变量名默认为 index,数组当前项的变量名默认为 item-->
          <view wx:for="{{array}}">
          {{index}}: {{item.message}}
          </view>
          <!--手动指定-->
          <view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName">
          {{idx}}: {{itemName.message}}
          </view>
          <!--重复渲染代码块-->
          <block wx:for="{{[1, 2, 3]}}">
          <view> {{index}}: </view>
          <view> {{item}} </view>
          </block>
        3. 条件渲染

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          <view wx:if="{{length > 5}}"> 1 </view>
          <view wx:elif="{{length > 2}}"> 2 </view>
          <view wx:else> 3 </view>
          <!--block-->
          <block wx:if="{{true}}">
          <view> view1 </view>
          <view> view2 </view>
          </block>
          <!-- wx:if vs hidden-->
          <!-- wx:if 有更高的切换消耗而 hidden 有更高的初始渲染消耗。因此,如果需要频繁切换的情景下,用 hidden 更好,如果在运行时条件不大可能改变则 wx:if -->
        4. 模版

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          <!--
          index: int
          msg: string
          time: string
          -->
          <template name="msgItem">
          <view>
          <text> {{index}}: {{msg}} </text>
          <text> Time: {{time}} </text>
          </view>
          </template>
          // 使用模版
          <template is="msgItem" data="{{...item}}"/>
          1
          2
          3
          4
          5
          6
          7
          8
          9
          Page({
          data: {
          item: {
          index: 0,
          msg: 'this is a template',
          time: '2016-09-15'
          }
          }
          })
        5. 引用

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          <!-- 只会 import 目标文件中定义的  template,而不会 import 目录文件的 import template -->

          <!-- template item.wxml -->
          <template name="item">
          <text>{{text}}</text>
          </template>

          <import src="item.wxml"/>
          <template is="item" data="{{text: 'forbar'}}"/>

          <!--include 可以将目标文件除了 <template/> <wxs/> 外的整个代码引入-->

          <!-- index.wxml -->
          <include src="header.wxml"/>
          <view> body </view>
          <include src="footer.wxml"/>

          <!-- header.wxml -->
          <view> header </view>

          <!-- footer.wxml -->
          <view> footer </view>
      4. WXS

        1. 模块

        2. 可以编写在 wxml ⽂件中的 <wxs> 标签内,或以 .wxs 为后缀名的⽂件内;

        3. wxs⽀持module、src标签,src为相对路径;

        4. 每个 wxs 模块均有⼀个内置的 module 对象;

        5. 在wxs中,可以引⼊新的wxs,或者使⽤require引⼊;

        6. 变量

          1. WXS 中的变量均为值的引⽤;
          2. 没有声明的变量直接赋值使⽤,会被定义为全局变量;
          3. 如果只声明变量⽽不赋值,则默认值为 undefined;
          4. var表现与javascript⼀致,会有变量提升。
        7. 注释

        8. 运算符

          1. 同 JS 一致
        9. 语句

          1. 同 JS 一致,支持 if else if else、swithc、for、while
        10. 数据类型

          1. number : 数值
          2. string :字符串
          3. boolean:布尔值
          4. object:对象
          5. function:函数
          6. array : 数组
          7. date:⽇期
          8. regexp:正则
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25
          26
          27
          28
          29
          30
          31
          32
          33
          34
          35
          36
          37
          38
          39
          40
          41
          42
          43
          44
          45
          // 如何区分数据类型
          // 1. constructor
          var number = 10;
          console.log( "Number" === number.constructor );

          var string = "str";
          console.log( "String" === string.constructor );

          var boolean = true;
          console.log( "Boolean" === boolean.constructor );

          var object = {};
          console.log( "Object" === object.constructor );

          var func = function(){};
          console.log( "Function" === func.constructor );

          var array = [];
          console.log( "Array" === array.constructor );

          var date = getDate();
          console.log( "Date" === date.constructor );

          var regexp = getRegExp();
          console.log( "RegExp" === regexp.constructor );

          // 2. typeof
          var number = 10;
          var boolean = true;
          var object = {};
          var func = function(){};
          var array = [];
          var date = getDate();
          var regexp = getRegExp();

          console.log( 'number' === typeof number );
          console.log( 'boolean' === typeof boolean );
          console.log( 'object' === typeof object );
          console.log( 'function' === typeof func );
          console.log( 'object' === typeof array );
          console.log( 'object' === typeof date );
          console.log( 'object' === typeof regexp );

          console.log( 'undefined' === typeof undefined );
          console.log( 'object' === typeof null );
组件

官⽅⽂档:https://developers.weixin.qq.com/miniprogram/dev/component/
参考代码内容实践基础组件及扩展能⼒

API

官⽅⽂档:https://developers.weixin.qq.com/miniprogram/dev/api/

参考代码内容接⼝部分业务中常⽤:

  • 基础:⼩程序应⽤级事件;
  • ⻚⾯交互:路由、跳转、转发;
  • 样式:导航栏、背景、tabBar;
  • 操作:下拉刷新、滚动、动画;
  • 其他:⽀付、LBS、设备、开放接⼝;

微信⼩程序发布、上线流程 & devTools

协同⼯作

参考官⽹

devtools

强烈建议阅读官⽹devtools ,掌握基本的IDE操作

补充知识点

面试常见问题

框架相关

  1. 为什么要分包?
    1. ⽬前⼩程序分包⼤⼩有以下限制:
      1. 整个⼩程序所有分包⼤⼩不超过 20M;
      2. 单个分包/主包⼤⼩不能超过 2M;
    2. 对⼩程序进⾏分包,可以优化⼩程序⾸次启动的下载时间,以及在多团队共同开发时可以更好地解耦协作;
  2. 如何提升⼩程序SEO?
    1. 官⽅⽂档:
      https://developers.weixin.qq.com/miniprogram/dev/framework/search/seo.html
    2. ⼩程序⾥跳转的⻚⾯ (url) 可被直接打开;
    3. ⻚⾯跳转优先采⽤navigator组件;
    4. 清晰简洁的⻚⾯参数;
    5. 配置⼩程序sitemap;
    6. 必要的时候才请求⽤户进⾏授权、登录、绑定⼿机号等;
    7. 我们不收录 web-view 中的任何内容;
    8. 设置⼀个清晰的标题和⻚⾯缩略图;
  3. 如何进⾏⻚⾯间通信?
    1. 使⽤ wx.navigateTo 打开,这两个⻚⾯间将建⽴⼀条数据通道:
      1. 被打开的⻚⾯可以通过 this.getOpenerEventChannel() ⽅法来获得⼀个 EventChannel 对象;
      2. wx.navigateTosuccess 回调中也包含⼀个 EventChannel 对象;
      3. 这两个 EventChannel 对象间可以使⽤ emiton ⽅法相互发送、监听事件;

性能相关

  1. ⼩程序启动流程
    官⽅⽂档
  2. ⼩程序切换⻚⾯流程
    官⽅⽂档
  3. 如何提升⼩程序性能
    1. 启动时性能优化
      1. 代码包体积优化
      2. 代码注⼊优化
      3. ⾸屏渲染优化
      4. 其他优化
    2. 运⾏时性能优化;
      1. 合理使⽤setState
      2. 渲染性能优化
      3. ⻚⾯切换优化
      4. 资源加载优化
      5. 内存优化

开发相关

  1. 如何提升开发效率
    1. 开发环境:
      1. 开启热重载;
      2. 开发环境下关闭域名校验;
      3. 请求开启Mock;
      4. 局部编译;
    2. 账号:
      1. 申请测试号,只需访问申请地址,就可以开发调试;
  2. 如何分析⼩程序性能?
    1. 真机:使⽤微信安卓客户端(开发者),具体操作:
      https://developers.weixin.qq.com/miniprogram/dev/devtools/performancetool.html
    2. devTools:调试器中audits,类似于chrome中的lighthouse;
    3. 分析包依赖: 删除⽆依赖的⽂件;
  3. 如何进⾏埋点?
    1. 开发者⼯具上可以编辑和调试⾃定义分析的数据上报功能,点击菜单栏中的 “⼯具 - ⾃定义分析” 即可弹窗打开⾃定义分析;
  4. 如何进⾏⼩程序上传、发布及⾃动化测试?
    1. devTools:⾃带发布集成;
    2. 使⽤miniprogram-ci ;(除⾮集成进⾃动化部署外,其余不建议使⽤,记得打开安全设置 CLI/HTTP 调⽤功能):
      https://developers.weixin.qq.com/miniprogram/dev/devtools/ci.html