小程序架构和配置

47 阅读9分钟

小程序的架构模型

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

 宿主环境为了执行小程序的各种文件: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)进行中转交互

小程序的配置文件

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

为什么这样做呢?

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

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

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

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

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

developers.weixin.qq.com/miniprogram…

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

developers.weixin.qq.com/miniprogram…

 app.json:全局配置;

 page.json:页面配置;

全局app配置文件

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

developers.weixin.qq.com/miniprogram…

pages: 页面路径列表

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

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

window: 全局的默认窗口展示

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

tabBar: 顶部tab栏的展示

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

AppService线程,渲染线程,Raster教程

Flutter->React native

配置案例实现

我们来做如下的效果:

页面page配置文件

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

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

developers.weixin.qq.com/miniprogram…

sitemap.js

{
  "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
  "rules": [{
  "action": "allow",
  "page": "*"
  }]
}

favor.json

{
  "usingComponents": {},
  "enablePullDownRefresh": true
}

app.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"
}

profile针对页面进行配置的

<!--pages/profile/profile.wxml-->
<text>pages/profile/profile.wxml</text>


<view class="list">
  <block wx:for="{{listCount}}" wx:key="*this">
    <view>列表数据:{{ item }}</view>
  </block>
</view>

profile.json

{
  "usingComponents": {},
  "navigationBarTitleText": "个人信息",
  "navigationBarBackgroundColor": "#f00",
  "enablePullDownRefresh": true,下拉刷新
  "onReachBottomDistance": 100
}

// 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 函数 注册小程序示例

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

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

developers.weixin.qq.com/miniprogram…

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

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

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

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

App函数的参数

// app.js
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'})
  },
  onShow(options) {
    // 作用一: 判断小程序的进入场景
    console.log("onShow:", options);
  },
  onHide() {
    console.log("onHide");
  }
})


// 页面中
// 1.初体验: favor
// 2.页面配置/下拉刷新/上拉加载: profile
// 3.在页面中, 使用app中的数据: order

作用一:判断打开场景

小程序的打开场景较多:

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

developers.weixin.qq.com/miniprogram…

如何确定场景?

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

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

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

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

// 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.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);
  }
})

作用三 – 生命周期函数

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

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

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

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

onLaunch

1.进行用户登录

2.保存的护具token/userinfo

3.发送一些必要的网络请求

注册页面 – Page函数

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

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

developers.weixin.qq.com/miniprogram…

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

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

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

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

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

注册Page时做什么呢?

Page页面的生命周期

// 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");
  }
})

index.wxml

<!--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>

developers.weixin.qq.com/miniprogram…

上拉和下拉的监听

监听页面的下拉刷新和上拉加载更多:

 步骤一:配置页面的json文件;

 步骤二:代码中进行监听;

二. 注册App实例的函数,以及注册该实例时,通常可能进行哪些操作?

  • 判断小程序的进入场景
  • 监听生命周期函数
    • 在对应的生命周期函数中执行刚开始的业务逻辑 比如登录操作或者初始化时请求全局的网络数据
    • App()实例只有一个 所以可以创建一个globalData的对象 存放全局的共享数据

三. 注册Page实例的函数,以及注册该实例时,通常可能进行哪些操作?

  1. 在生命周期函数中发送网络请求
  2. 初始化数据 方便页面展示
  3. 监听wxml中的事件
  4. 进行页面滚动 下拉加载 上拉刷新的事件监听

四. 根据文档,自己查找3个组件进行练习(比如Swiper等组件)

五. 什么是rpx?rpx如何进行的屏幕适配?

rpx: responsive pixel : 可以根据屏幕宽度进行自适应 规定屏幕宽度为750rpx

  • 建议开发中将 iPhone6 作为视觉稿的标准
    • iPhone6 屏幕宽度为375px 750物理像素 所以 750rpx = 375px = 750物理像素
    • 1rpx = 0.5px
    • 因此如果想定义一个100px宽度的view 则需要设置width为 200rpx

下面的预习

六. wx:if和hidden属性有什么区别?开发中如何选择?

wx:if是 组件是否渲染

hidden指的是hidden属性是否添加

开发中选择:

  • 如果操作很频繁 则使用hidden
  • 如果不频繁 则使用 wx:if

七. wx:for为什么需要绑定key?绑定key的方式有哪些?

为什么要绑定key:

  • 当我们希望处于同一层的VNode 进行插入 删除 新增 节点时 可以更好的进行节点的复用 就需要key属性来判断

绑定key的方式有哪些:

  • 字符串: 表示 for循环array中item的某个属性(property) 该property是列表中的唯一的字符串或数字
  • 保留关键字 *this 表示item本身 此时item本身是唯一的字符串或数字

八. WXS的作用是什么?如何使用?

作用:

  • 小程序的一套脚本语言 和JavaScript基本一致
  • 为了在wxml中调用函数来处理对应的数据

如何使用:

  • 写在 wxs标签中
  • 写在 .wxs结尾的文件中 以cjs的方式导出 以cjs的方式在wxml文件中引入 进行使用