Skip to content

小程序的架构模型

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

宿主环境为了执行小程序的各种文件:wxml文件、wxss文件、js文件

当小程序基于WebView环境下时,WebView的 JS逻辑、DOM树创建、CSS解析、样式计算、Layout、Paint (Composite)都发生

在同一线程,在 WebView 上执行过多的 JS 逻辑可能阻塞渲染,导致界面卡顿。

以此为前提,小程序同时考虑了性能与安全,采用了目前称为「双线程模型」的架构。

双线程模型:

WXML模块和WXSS样式运行于 渲染层,渲染层使用WebView线程渲染(一个程序有多个页面,会使用多个WebView的线程)。

JS脚本(app.js/home.js等)运行于 逻辑层,逻辑层使用JsCore运行JS脚本。

这两个线程都会经由微信客户端(Native)进行中转交互。

image-20230709170001715

小程序的配置文件

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

为什么这样做呢?

这样做可以更有利于我们的开发效率;

并且可以保证开发出来的小程序的某些风格是比较一致的;

比如导航栏 – 顶部TabBar,以及页面路由等等。

常见的配置文件有哪些呢?

project.config.json:项目配置文件, 比如项目名称、appid等;

我们一般不会去改动这个文件,如果需要改的话,在详情=>本地设置进行修改,修改完会自动同步到这个文件中

https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html

sitemap.json:小程序搜索相关的;

https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html

app.json:全局配置;

page.json:页面配置;

全局app配置文件

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

https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html

image-20230709183924165

pages: 页面路径列表

用于指定小程序由哪些页面组成,每一项都对应一个页面的 路径(含文件名) 信息。

小程序中所有的页面都是必须在pages中进行注册的。

window: 全局的默认窗口展示

用户指定窗口如何展示, 其中还包含了很多其他的属性

其中backgroundTextStyle是下拉 loading 的样式,仅支持 dark / light,但是默认是不开启下拉刷新的,如果想要开启下来刷新,需要来到页面,比如favor页面的favor.json文件开启下拉刷新

pages/favor/favor.json

json
{
  "usingComponents": {},
  "enablePullDownRefresh": true // 开启下拉刷新
}

tabBar: 顶部tab栏的展示

具体属性稍后我们进行演示

配置案例实现

app.json

json
{
  "pages": [
    "pages/index/index",
    "pages/favor/favor",
    "pages/order/order",
    "pages/profile/profile",
    "pages/01test/index"
  ],
  "window": {
    "backgroundTextStyle": "dark",
    "navigationBarBackgroundColor": "#ff8189",
    "navigationBarTitleText": "弘源旅途",
    "navigationBarTextStyle": "white"
  },
  "tabBar": {
    "selectedColor": "#ff8189",
    "list": [
      {
        "text": "首页",
        "pagePath": "pages/index/index",
        "iconPath": "assets/tabbar/home.png",
        "selectedIconPath": "assets/tabbar/home_active.png"
      },
      {
        "text": "收藏",
        "pagePath": "pages/favor/favor",
        "iconPath": "assets/tabbar/category.png",
        "selectedIconPath": "assets/tabbar/category_active.png"
      },
      {
        "text": "订单",
        "pagePath": "pages/order/order",
        "iconPath": "assets/tabbar/cart.png",
        "selectedIconPath": "assets/tabbar/cart_active.png"
      },
      {
        "text": "我的",
        "pagePath": "pages/profile/profile",
        "iconPath": "assets/tabbar/profile.png",
        "selectedIconPath": "assets/tabbar/profile_active.png"
      }
    ]
  },
  "style": "v2",
  "sitemapLocation": "sitemap.json"
}

页面page配置文件

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

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

https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/page.html

pages/profile/profile.json

json
{
  "usingComponents": {}, // 使用组件要在这里注册
  "navigationBarTitleText": "个人信息",
  "navigationBarBackgroundColor": "#f00",
  "enablePullDownRefresh": true, // 开启下拉刷新
  "onReachBottomDistance": 100 // 距离底部100就加载更多数据,默认是0
}

pages/profile/profile.js

javascript
// pages/profile/profile.js
Page({
  data: {
    avatarURL: "",
    listCount: 30
  },

  // 监听下拉刷新
  onPullDownRefresh() {
    console.log("用户进行下拉刷新~");

    // 模拟网络请求: 定时器
    setTimeout(() => {
      this.setData({ listCount: 30 })

      // API: 停止下拉刷新
      wx.stopPullDownRefresh({
        success: (res) => {
          console.log("成功停止了下拉刷新", res);
        },
        fail: (err) => {
          console.log("失败停止了下拉刷新", err);
        }
      })
    }, 1000)
  },

  // 监听页面滚动到底部
  onReachBottom() {
    console.log("onReachBottom");
    this.setData({
      listCount: this.data.listCount + 30
    })
  }
})

注册小程序 – App函数

每个小程序都需要在 app.js 中调用 App 函数注册小程序实例

在注册时, 可以绑定对应的生命周期函数;

在生命周期函数中, 执行对应的代码;

https://developers.weixin.qq.com/miniprogram/dev/reference/api/App.html

我们来思考:注册App时,我们一般会做什么呢?

1.判断小程序的进入场景

2.监听生命周期函数,在生命周期中执行对应的业务逻辑,比如在某个生命周期函数中进行登录操作或者请求网络数据;

3.因为App()实例只有一个,并且是全局共享的(单例对象),所以我们可以将一些共享数据放在这里;

作用一:判断打开场景

小程序的打开场景较多:

常见的打开场景:群聊会话中打开、小程序列表中打开、微信扫一扫打开、另一个小程序打开

https://developers.weixin.qq.com/miniprogram/dev/reference/scene-list.html

如何确定场景?

在onLaunch和onShow生命周期回调函数中,会有options参数,其中有scene值;

app.js

onLaunch只会执行一次,而onShow会执行多次

javascript
App({
    onShow(options) {
        // 作用一: 判断小程序的进入场景
        console.log("onShow:", options);
	},
})

作用二:定义全局App的数据

作用二:可以在Object中定义全局App的数据

定义的数据可以在其他任何页面中访问:

app.js

javascript
App({
  // 作用二: 共享数据
  // 数据不是响应式, 这里共享的数据通常是一些固定的数据
  globalData: {
    token: "",
    userInfo: {}
  },
})

pages/order/order.js

javascript
// pages/order/order.js
Page({
  data: {
    userInfo: {}
  },

  onLoad() {
    // 获取共享的数据: App实例中数据
    // 1.获取app实例对象
    const app = getApp()

    // 2.从app实例对象获取数据
    const token = app.globalData.token
    const userInfo = app.globalData.userInfo
    console.log(token, userInfo);

    // 3.拿到token目的发送网络请求
    
    // 4.将数据展示到界面上面
    // this.data.userInfo = userInfo
    this.setData({ userInfo })
    console.log(this.data.userInfo);
  }
})

pages/order/order.wxml

html
<!--pages/order/order.wxml-->
<text>pages/order/order.wxml</text>
<view class="user">
  <view class="name">name: {{ userInfo.nickname }}</view>
  <view class="level">level: {{ userInfo.level }}</view>
</view>

作用三 – 生命周期函数

作用三:在生命周期函数中,完成应用程序启动后的初始化操作

比如登录操作(这个后续会详细讲解);

比如读取本地数据(类似于token,然后保存在全局方便使用)

比如请求整个应用程序需要的数据;

app.js

javascript
App({
    globalData: {
        token: "",
        userInfo: {}
      },
      onLaunch(options) {
        // 0.从本地获取token/userInfo
        const token = wx.getStorageSync("token")
        const userInfo = wx.getStorageSync("userInfo")

        // 1.进行登录操作(判断逻辑)
        if (!token || !userInfo) {
          // 将登录成功的数据, 保存到storage
          console.log("登录操作");
          wx.setStorageSync("token", "kobetoken")
          wx.setStorageSync("userInfo", { nickname: "kobe", level: 100 })
        }

        // 2.将获取到数据保存到globalData中
        this.globalData.token = token
        this.globalData.userInfo = userInfo


        // 3.发送网络请求, 优先请求一些必要的数据
        // wx.request({ url: 'url'})
     },
})

注册页面 – Page函数

小程序中的每个页面, 都有一个对应的js文件, 其中调用Page函数注册页面示例

在注册时, 可以绑定初始化数据、生命周期回调、事件处理函数等。

https://developers.weixin.qq.com/miniprogram/dev/reference/api/Page.html

我们来思考:注册一个Page页面时,我们一般需要做什么呢?

1.在生命周期函数中发送网络请求,从服务器获取数据;

2.初始化一些数据,以方便被wxml引用展示;

3.监听wxml中的事件,绑定对应的事件函数;

4.其他一些监听(比如页面滚动、上拉刷新、下拉加载更多等);

html
<!--pages/01_初体验/index.wxml-->
<view class="banner">
  <swiper circular autoplay indicator-dots="{{true}}">
    <block wx:for="{{banners}}" wx:key="acm">
      <swiper-item>
        <!-- image组件默认宽度和高度: 320x240 -->
        <image mode="widthFix" src="{{item.image}}"></image>
      </swiper-item>
    </block>
  </swiper>
</view>

<view class="counter">
  <view>当前计数: {{ counter }}</view>
</view>

<view class="buttons">
  <button bindtap="onBtn1Click">按钮1</button>

  <block wx:for="{{btns}}" wx:key="*this">
    <button 
      class="btn" 
      style="background: {{item}};"
      bindtap="onBtnClick"
      data-color="{{item}}"
    >
      {{ item }}
    </button>
  </block>
</view>


<view class="list">
  <block wx:for="{{30}}" wx:key="*this">
    <view>列表数据:{{ item }}</view>
  </block>
</view>
javascript
// pages/01_初体验/index.js
Page({
  data: {
    banners: [],
    recommends: [],

    // 2.作用二: 定义本地固定的数据
    counter: 100,

    btns: ["red", "blue", "green", "orange"]
  },
  // 1.作用一: 发送网络请求, 请求数据
  onLoad() {
    console.log("onLoad");

    // 发送网络请求
    wx.request({
      url: "http://123.207.32.32:8000/home/multidata",
      success: (res) => {
        const data = res.data.data
        const banners = data.banner.list
        const recommends = data.recommend.list
        this.setData({ banners, recommends })
      }
    })
  },

  // 3.绑定wxml中产生事件后的回调函数
  onBtn1Click() {
    console.log("onBtn1Click");
  },
  onBtnClick(event) {
    console.log("btn click:", event.target.dataset.color);
  },

  // 4.绑定下拉刷新/达到底部/页面滚动
  onPullDownRefresh() {
    console.log("onPullDownRefresh");
  },
  onReachBottom() {
    console.log("onReachBottom");
  },
  onPageScroll(event) { // 页面滚动
    console.log("onPageScroll:", event);
  },

  // 生命周期函数: 
  onShow() {
    console.log("onShow");
  },
  onReady() {
    console.log("onReady");
  },
  onHide() {
    console.log("onHide");
  },
  onUnload() {
    console.log("onUnload");
  }
})

Page页面的生命周期

img