小程序框架

一、概述

小程序框架将整个系统划分为视图层逻辑层

       视图层是由框架设计的标签语言WXML(weixin markup language) 和用于描述WXML组件样式的WXSS(weixin style sheets)组成,就像HTML与CSS一样。

       逻辑层是一套运行在本地JavaScript引擎的JavaScript代码,在此基础上框架实现了一套模块化机制,让每个JS文件有独立的作用域和模块化能力,这套模块化机制遵循CommonJS规范

小程序开发流程与HTML+CSS+JS开发模式的区别:

       在小程序中没有DOM的概念,在本地的JS引擎中也没有window、document等对象,因此我们不能通过DOM来操作页面。
       小程序中的视图层和逻辑层的交互是通过数据绑定事件响应实现的,这是一种单向绑定的机制。这套机制需要首先将逻辑层和视图层的数据和事件进行绑定,当需要修改页面时,逻辑层只需要用特定的setData方法修改已绑定的数据,这时框架会自动触发WXML重新渲染,达到逻辑层对视图层的控制。当框架接收到用户交互操作时,会根据视图层绑定的事件,执行逻辑层中对应的事件函数,达到逻辑层对视图层的响应。

WXML和WXSS在渲染时会被框架解析为不同端的本地渲染文件,这样可满足一套代码在多处运行,并且能最大化的接近原生APP。

二、目录结构

2.1 在app.json中可以指定默认启动页面地址.app.json是小程序配置文件。app.json在程序加载时加载,负责对小程序的全局配置。

2.2 pages:pages负责注册小程序页面,必须填写,value值为一个包含页面路径的数组,用来指定小程序由哪些页面构成,每一项由页面“路径+文件名”组成。pages数组中页面路径不需要填写文件后缀名,渲染时,框架会自动寻找路径.wxml,wxss、.js、.json这四个文件进行整合。路径中的文件名可以和目录名不一致,但最好保持一样。小程序中增加页面、删除页面,都需要对pages进行修改,并且重启项目。

2.3 window配置:window负责设置小程序状态栏、导航栏、标题、窗口背景色等系统级样式。

2.4 tabBar配置:当程序顶部或底部需要菜单栏时,我们可以通过配置tabBar快速实现,tabBar是个非必填项目。

2.5 networkTimeout配置:小程序中各种网络请求API的超时时间只能通过networkTimeout统一设置,不能在API单独设置。

2.6 debug配置:此配置项控制是否开启debug模式,默认是关闭的。开启debug模式后,在开发者工具的控制面板,调试信息以info的形式输出。

示例代码:

{
  "commentjson":{
    "key0":"json文件仅仅包含数据,json语法中是没有注释语法的",
    "pages":"pages为页面路径设置",
    "window":"默认页面的窗口设置",
    "tabBar":"tabBar底部tab设置,其中tabBar.list应为数组,且至少包含两项",
    "networkTimeout":"networkTimeout用于设置网络请求API的超时时间",
    "debug":"debug设置是否为debug模式",
    "navigationBarBackgroundColor":"导航栏背景颜色",
    "navigationBarTextStyle":"导航栏标题颜色",
    "navigationBarTitleText":"导航栏标题文字内容",
    "backgroundTextStyle":"下拉背景字体、loading图的样式,仅支持dark/light",
    "enablePullDownRefresh":"是否开启下拉刷新,默认false,开启后,当用户下拉时会触发onPullDownRefresh事件",
    "list":"tab的列表,必填,数组,最少2个,最多5个,数组中每一项是一个对象,代表一个tab的相关配置",
    "pagePath":"页面路径,必须在pages中先定义,必填",
    "position":"tab在顶部或底部显示",
    "iconPath":"tab上icon图片的相对路径,icon大小限制为40kb,必填",
    "selectedIconPath":"选中时图片的相对路径,icon大小限制为40kb,必填项",
    "uploadFile":"设置wx.uploadFile的超时时间,单位毫秒",
    "downloadFile":"设置wx.downloadFile的超时时间,单位毫秒"
  },
  "pages":[
    "pages/index/index",
    "pages/logs/logs"
  ],
  "window":{
    "backgroundTextStyle":"light",
    "navigationBarBackgroundColor": "#ff83fa",
    "navigationBarTitleText": "Abel_xu_01",
    "navigationBarTextStyle":"white",
    "enablePullDownRefresh":true
    },
    "tabBar":{
      "color":"black",
      "selectedColor":"blue",
      "backgroundColor":"#ffffff",
      "borderStyle":"black",
      "position": "bottom",
      "list":[
        {
          "iconPath": "pages/index/images/one.png",
          "selectedIconPath":"pages/index/images/one.png",
          "pagePath":"pages/index/index",
          "text":"首页"
        },
        {
          "iconPath": "pages/index/images/one.png",
          "selectedIconPath": "pages/index/images/one.png",
          "pagePath": "pages/index/index",
          "text":"搜索"
        },
        {
          "iconPath": "pages/index/images/one.png",
          "selectedIconPath": "pages/index/images/one.png",
          "pagePath": "pages/index/index",
          "text":"列表"
        }
      ]
    },
    "networkTimeout":{
      "request": 60000,
      "connectSocket": 60000
    },
    "debug":false
}

2. 在index.wxml中,设置默认页面的“HTML”

<!--index.wxml-->
<view class="container">
  <view class="userinfo">
    <button wx:if="{{!hasUserInfo && canIUse}}" open-type="getUserInfo" bindgetuserinfo="getUserInfo"> 获取头像昵称 </button>
    <block wx:else>
      <image bindtap="bindViewTap" class="userinfo-avatar" src="{{userInfo.avatarUrl}}" mode="cover"></image>
      <text class="userinfo-nickname">{{userInfo.nickName}}</text>
    </block>
  </view>
  <view class="usermotto">
    <text class="user-motto">{{"我的第一个小程序"}}</text>
  </view>
  <view bindtap='countClick'>我是默认页面,你点击了{{count}}次</view>
</view>

3. 在index.js文件中,写入逻辑控制【小程序逻辑app.js】

//index.js
//获取应用实例
const app = getApp()

Page({
  data: {
    motto: 'Hello shuai,welcome to you',
    userInfo: {},
    hasUserInfo: false,
    canIUse: wx.canIUse('button.open-type.getUserInfo'),
    count:0
  },
  //事件处理函数
  bindViewTap: function() {
    wx.navigateTo({
      url: '../logs/logs'
    })
  },
  onLoad: function () {
    if (app.globalData.userInfo) {
      this.setData({
        userInfo: app.globalData.userInfo,
        hasUserInfo: true
      })
    } else if (this.data.canIUse){
      // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
      // 所以此处加入 callback 以防止这种情况
      app.userInfoReadyCallback = res => {
        this.setData({
          userInfo: res.userInfo,
          hasUserInfo: true
        })
      }
    } else {
      // 在没有 open-type=getUserInfo 版本的兼容处理
      wx.getUserInfo({
        success: res => {
          app.globalData.userInfo = res.userInfo
          this.setData({
            userInfo: res.userInfo,
            hasUserInfo: true
          })
        }
      })
    }
  },
  getUserInfo: function(e) {
    console.log(e)
    app.globalData.userInfo = e.detail.userInfo
    this.setData({
      userInfo: e.detail.userInfo,
      hasUserInfo: true
    })
  },
  countClick:function(){
    this.setData({
      count:this.data.count+1
    });
  }
})

在一个完整的小程序中,文件主要分为框架主体文件和页面文件。框架程序主体文件是系统级别文件,一个项目只有一份,分别是app.json(小程序整体配置)、app.js(逻辑)和app.wxss(样式),小程序启动时只会执行一次。这三个文件必须放在项目根目录,且文件名是app,其中app.json和app.js是必须的。一个小程序有一个或多个页面,一个页面由.wxml、.wxss、.js、.json四个文件组成,分别控制页面的结构、样式、逻辑和配置,其中.wxml和.js文件是必须的,按照框架规定,同一个页面的这四个文件必须具有相同的文件名和路径。

小程序中逻辑文件分为页面逻辑文件小程序逻辑文件,app.js便是小程序逻辑文件,在这个文件中,我们可以通过App()函数注册小程序生命周期函数、全局方法和全局属性,已注册的小程序实例可以在其他逻辑层代码中通过getApp()方法获取。

3.1 注册小程序

     App()函数用于注册一个小程序,参数为一个Object对象,在这个参数对象中我们可以注册自定义方法和属性供全局使用,就像在quick start项目中,利用了App()注册了用户登录信息。App()函数必须在app.js中注册,且不能注册多个。
   

   属性                                                                           描述
onLaunch生命周期函数,监听小程序初始化。当小程序初始化完成时,就会触发onLaunch,onLoaunch事件全局只会触发一次
onShow生命周期函数,监听小程序显示。当小程序启动,或者从后台进入前台时都会触发onShow
onHide生命周期函数,监听小程序隐藏。当小程序从前台进入后台时触发
其他开发者可以添加任意的函数或数据到Object参数中,这些属性会被注册到小程序对象中,其他逻辑文件可以通过getApp()函数获取已注册的小程序实例。
备注当启动一个小程序时,首先先一次触发onLaunch和onShow方法,然后通过app.json的pages属性注册相应的页面,最后根据默认路径加载首页;当用户点击左上角关闭,或者按了设备home按钮离开微信时,小程序并没有直接销毁,而是进入了后台,这两种情况都会触发onHide方法;当在此唤醒微信小程序时,即从后台进入前台,这时会触发onShow方法。只有当小程序进入后台一定时间,或者系统资源占用过高,才会被真正销毁。

注册小程序代码如下:

//app.js
App({
  //小程序初始化完成时触发
  onLaunch: function () {
    // 展示本地存储能力
    var logs = wx.getStorageSync('logs') || []
    logs.unshift(Date.now())
    wx.setStorageSync('logs', logs)
    // 登录
    wx.login({
      success: res => {
        // 发送 res.code 到后台换取 openId, sessionKey, unionId
      }
    })
    // 获取用户信息
    wx.getSetting({
      success: res => {
        if (res.authSetting['scope.userInfo']) {
          // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
          wx.getUserInfo({
            success: res => {
              // 可以将 res 发送给后台解码出 unionId
              this.globalData.userInfo = res.userInfo

              // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
              // 所以此处加入 callback 以防止这种情况
              if (this.userInfoReadyCallback) {
                this.userInfoReadyCallback(res)
              }
            }
          })
        }
      }
    })
  },
  //小程序启动或从后台进入前台时触发
  onShow:function(){

  },
  //小程序从前台进入后台时触发
  onHide:function(){

  },
  globalData: {
    userInfo: null
  }
})

3.2 获取小程序实例

      注册小程序后,在其他逻辑文件中,可以通过全局函数getApp()获取小程序实例。
      例如:     

//index.js
//获取应用实例
const app = getApp()

//在App()注册的函数中,可以通过使用this直接获取App实例,而不用getApp()方法。通过getApp()方法
//获取实例后,可以获取注册的属性、调用注册的方法,但不要私自调用生命周期函数,这样会打乱项目逻辑,
//除非相当有把握。

4. 全局样式(app.wxss)

    app.wxss是全局样式表,对项目中每个页面都有效,可将一些系统级别的统一样式风格写入这个文件,页面渲染时,框架页中的.wxss文件样式会覆盖app.wxss中相同的选择器样式。

5. 框架页面文件

    小程序中一个框架页面包含四个文件,同一框架页面的4个文件必须具有相同的路径与文件名,进入小程序时或页面跳转时,小程序会根据app.json配置的路径找到对应的资源进行渲染。

5.1 页面配置文件

      页面配置文件与框架配置文件一样,是一个json文件,与框架配置文件不同的是,页面文件是非必要的,同时页面配置文件的配置项只有window,控制当前页面的窗口表现。渲染页面时,页面中window配置项会覆盖app.json中的相同配置项。由于页面的.json只能配置window相关属性,编写时只需直接写出属性,不用写window这个键。示例代码如下:

{
 "backgroundTextStyle":"light",
 "navigationBarBackgroundColor": "#ff83fa",
 "navigationBarTitleText": "Abel_xu_01",
 "navigationBarTextStyle":"white",
 "enablePullDownRefresh":true
}

5.2 页面逻辑文件

     页面逻辑文件主要功能有:设置初始化数据,注册当前页面生命周期函数,注册事件处理函数等。小程序的逻辑层文件是javascript文件,所有的逻辑文件,包括app.js,最终将会打包成一个js文件,在小程序启动时运行,直到小程序销毁。在小程序中,每个js文件都有独立的作用域,并具备模块化能力。

5.2.1 注册页面

     在页面逻辑文件中需要通过Page()函数注册页面,指定页面的初始数据、生命周期函数、事件处理函数等,参数为Object对象,其属性如下:

属性                                                                             描述
data页面的初始数据,数据格式必须是可转换成JSON的对象类型。当页面第一次渲染时,data会以JSON的形式由逻辑层传至渲染层,渲染层可以通过WXML对数据进行绑定
onLoad生命周期函数,页面加载时触发。接受页面参数,可以获取wx.navigateTo、wx.redirectTo及页面中的query参数
onShow生命周期函数,页面显示时触发。每次打开页面都会调用一次
onReady生命周期函数,页面初次渲染完成时触发
onHide生命周期函数,页面隐藏时触发
onUnload生命周期函数,页面卸载时触发
onPullDownRefresh页面相关时间处理函数。使用时,需要将app.json配置中的window的ennabelPullDownRefersh属性设置为true。当处理完数据刷新后,可以调用wx.stopPullDownRefersh方法停止当前页面的下拉刷新
onReachBottom页面上拉触底事件的处理函数
其他开发者可以添加任意的函数或数据到Object参数中,可以用this访问这些函数和数据

示例代码清单

// pages/static/second/second.js
Page({

  /**
   * 页面的初始数据
   */
  data: {
    hello:""
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {

  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady: function () {

  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function () {

  },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide: function () {

  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload: function () {

  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh: function () {

  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom: function () {

  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {

  },

  /**
   * 自定义函数,可与渲染层中的组件进行事件绑定
   */
  sayHello:function(){
    //触发视图层重新渲染
    this.setDate({
      hello: "how are you"
    });
  },

  /**
   * 自定义数据
   */
  customData:{
    name:"微信小程序"
  }
})

页面的生命周期函数比小程序的生命周期函数复杂,搞清楚其执行顺序能够避免在不恰当的生命周期函数中调用还未创建的对象或方法,小程序框架以栈的形式维护了当前的所有页面,当发生路由切换时,页面栈和生命周期函数的关系如下:

小程序初始化”:index页面入栈,依次触发onLoad、onShow、onReady方法

打开新页面”:新页面入栈,依次触发新页面onLoad、onShow、onReady方法

页面重定向”:当前页面出栈并卸载(触发onUnload方法),新页面入栈,依次触发onLoad、onShow、onReady

页面返回”:页面不断出栈并卸载,触发当前弹出页面onLoad方法,直到返回目标页面,新页面入栈,触发onShow方法

tab切换”:当前页面出栈但不卸载,仅触发onHide方法,新页面入栈,如果当前页面是新加载的,触发onLoad、onShow、onReady方法,如果当前页面已加载过,仅触发onShow方法

程序从前台到后台”:触发当前页面onHide方法,触发App onHide方法

程序从后台到前台”:触发小程序onShow方法,紧接着触发当前页面onShow方法

小结:总的来说,如果页面在生命周期中,只会触发onShow和onLoad方法,只有加载和卸载时才会触发onLoad、onReady和onUnload方法,而触发页面卸载只有页面返回以及页面重定向两种操作。

5.2.2 获取当前页面栈

函数描述
getCurrentPages()获取当前页面栈的实例,页面栈以数组形式按栈顺序给出,第一个元素为首页,最后一个元素为当前页面。

示例代码清单:

getPageName:function(){
    //获取页面栈
    var pages=getCurrentPages();
    //获取当前页面对象
    var currentPage=pages[pages.length-1];
    //获取首页
    var firstPage=pages[0];
  }

5.2.3 事件处理函数

      页面对象中注册的函数可以和视图层中的组件进行绑定,当达到触发条件时,就会执行page中定义的相应事件,这类自定义函数统称为事件处理函数。
     小程序中组件的事件分为通用事件和特殊事件。

5.2.4 触发视图层渲染

     页面首次加载时,框架会结合初始化数据渲染页面,在逻辑层中则需要主动调用Page.prototype.setData()方法,而不能直接修改Page的data值,这样不仅无法直接触发视图层渲染,还会造成数据不一致。当Page.prototype.setData()被调用时,会将数据从逻辑层发送到视图层触发视图层重绘,同时会修改Page的data值。setData()接受一个Object对象参数,方法会自动将this.data中的key对应的值变成Object参数中key对应的值。当Object参数key对应的值和this.data中key对应的值一致时,将不会触发视图层渲染。在项目中我们一定要保证视图层和逻辑层的数据一致。
    Object参数的key值,可以按数据路径的形式给出,如array[0].info、obj.key.subkey,并且这样使用时,不需要在this.data中预先定义。

<!--pages/static/second/second.wxml-->
<view bindtap='getPageName'>点击查看当前页面信息:{{message}}</view>
<view>{{text}}</view>
<button bindtap='changeText'>修改文本数据</button>
<view>{{object.subObject.objectText}}</view>
<button bindtap='changeObjectText'>修改对象数据</button>
<view>{{array[0].arrayText}}</view>
<button bindtap='changeArrayText'>修改数组数据</button>
<view>{{newField.newFieldText}}</view>
<button bindtap='addNewData'>添加新字段</button>
// pages/static/second/second.js
Page({
  /**
   * 页面的初始数据
   */
  data: {
    message:"欢迎来到小程序修炼手册",
    text:"文本一",
    object:{
      subObject:{
        objectText:'object data'
      }
    },
    array:[
      {arrayText:'array data'}
    ]
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {

  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady: function () {

  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function () {

  },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide: function () {

  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload: function () {

  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh: function () {

  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom: function () {

  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {

  },

  /**
   * 自定义函数,可与渲染层中的组件进行事件绑定
   */
  getPageName:function(){
    //获取页面栈
    var pages = getCurrentPages();
    //获取当前页面对象
    var currentPage=pages[pages.length-1];
    //获取首页
    var firstPage=pages[0];
    this.setData({
      message:"hah"
    });
  },
  changeText:function(){
    this.setData({
      text:'将文本数据变为帅爷'
    });
  },
  changeObjectText:function(){
    this.setData({
      /*按路径索引 */
      'object.subObject.objectText':'new object data'
    });
  },
  changeArrayText:function(){
    this.setData({
      /*按路径索引 */
      'array[0].arrayText':'new array data'
    });
  },
  addNewData:function(){
    this.setData({
      /**修改一个已绑定,但未在data中定义的数据 */
      'newField.newFieldText':'add new data'
    });
  },
  /**
   * 自定义数据
   */
  customData:{
    name:"微信小程序"
  }
})

5.2.5 页面生命周期

      页面的生命周期整体关系着页面视图层线程和页面逻辑层线程,注册页面时,Object参数中很多属性都是生命周期函数,这些函数的调用和页面生命息息相关。

                      Page实例的生命周期
 

上图,线程启动后视图层和逻辑层相互监听,当逻辑层线程触发onLoad、onShow方法后把初始化数据data传送给视图层线程。当视图层完成第一次渲染后触发逻辑层onReady方法,表示页面已准备妥当,之后便可以通过setData方法主动触发视图层渲染。当页面调往后台时,触发onHide方法,此时逻辑线程并没有销毁。当页面从后台回到前台时,触发onShow方法,最后页面销毁触发onUnload方法。
在路由方式中,只有页面返回和页面重定向会结束当前页面生命周期,当进入一个已加载的页面时只会触发onShow方法,不会触发onReady方法以及onReady方法。

 

 

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值