微信小程序开发笔记

0、杂记

0.1、在实际的开发中,图片资源不会存储在小程序的目录中,因为小程序的大小不能超过1MB(现在改为2M)。超过则无法真机运行和发布项目。我们应该将图片都存放在服务器上,让小程序通过网络来加载图片资源。
0.2、在wxss中,本地资源是无法使用的,比如:background-image,如果使用本地的图片是无法显示的,可以使用网络图片来代替本地图片,同时要加上background-size属性,属性值的单位为rpx。
0.3、小程序自适应尺寸单位rpx,在微信小程序中,尺寸单位可以使用px,也可以使用rpx,使用rpx可以使组件自适应屏幕的高度和宽度,但是使用px则不会。(官网:规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素)。在开发过程中,到底使用rpx还是px?主要取决于业务需求,也就是需要元素随着移动设备尺寸的变化而变化,还是让元素始终保持不变,所以需要具体问题,具体分析。比如:一个元素的width、heigth、margin、 font-size,在很多的时候,需要随着设备的尺寸不同,而动态发生变化,从而保持页面元素之间的布局可以保持在一定的比例关系,在这种情况下就应该使用rpx,所以rpx作为小程序的自适应单位,它非常适合来控制图片的宽和高以及元素之间的间距。但是border相关属性不需要随着移动设备的尺寸变化而动态发生变化,因为,如果border动态变化,那么会在屏幕尺寸较大的手机上会变的很粗,这个并不是我们想要的结果,所以应当将border相关属性的单位设置为px。
0.4、关于分辨率和物理像素:在微信小程序的模拟器中,都给出了每种机型的分辨率,需要注意的是,这里的分辨率指的是逻辑分辨率pt,而非物理分辨率,以iphone6为例,模拟器的逻辑分辨率是375px×667px,设备像素比(Dpr:Device Pixel Ratio)为2,而iphone6的物理分辨率是1334px×750px,以上的意思是:iphone6的水平方向上有375个像素点,竖直方向上有667个像素点,而每个逻辑像素点包含2个物理像素点。所以开发人员一定要注意逻辑像素和物理像素的区别,1物理像素不等于1px,通常UI设计人员做出来的设计图的像素是物理像素。假设有一张图片的宽度是750像素(物理像素),我们想让这张图片充满整个页面,如果直接设置在页面里面将图片的宽度设置为750px,是不对的,正确的设置方法是为750rpx或者350px。

1、给container添加背景颜色显示问题

微信小程序开发笔记_第1张图片
微信小程序整个页面设置样式问题

2、image组件9种图片裁剪与4种缩放的模式

如果一个元素的宽和高分别设置成340rpx和100%(在iphone6下就是750rpx),而雪糕图片的素材原始高度分别为600px和750px。

在现实的项目中,我们经常需要面对原始图片的尺寸和设计图里的尺寸不一样的情况(尤其是原始图片高度是未知和不固定的情况,比如动态从网络获取图片)。在这种情况下,我们必须要有所舍弃,或放弃等比例,或者裁剪掉图片的一部分。接受不完美,这也是编程中很重要的心态,如和选择,需要看业务上的需求。

小程序的image组件提供了4种缩放模式和9种裁剪模式,来支持我们的选择。

小程序页面设计的框架结构MINA(MVVM)

MINA(MINA IS NOT APP)就是微信小程序开发使用的框架

整个系统分为两块:视图层(View) 和 逻辑层(App Service)。
MINA可以让数据与视图保持同步非常简单。当做数据修改的时候,只需要在逻辑层修改数据,视图层就会做相应的更新。(通过this.setData进行同步数据)

3、page页面的生命周期

什么是页面的生命周期?如同人的成长需要分为出生、童年、青年、中年、老年一样,一个页面从创建到卸载,同样会经历以下5个周期:

  • 加载
  • 显示
  • 渲染
  • 隐藏
  • 卸载

MINA框架分别提供了5个生命周期函数来监听这个5个特定的生命周期,以方便开发人员可以在这些特定的时刻执行一些自己的代码逻辑,它们分别是:

  • onLoad 监听页面加载,一个页面只能会调用一次。
  • onShow 监听页面显示,每次打开页面都会被调用
  • onReady 监听页面初次渲染完成,一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互
  • onHide 监听页面隐藏
  • onUnload 监听页面卸载

可以在Page()方法中去演示这些页面的生命周期

Page({
    data:{},
    onLoad:function(options){
        console.log('onLoad:页面被加载')
    },
    onShow: function() {
        console.log('onShow函数被加载')
    },
    onReady: function() {
        console.log('onReady函数被加载')
    },
    onHide: function() {
        console.log('onHide函数被加载')
    },
    onUnload: function() {
        console.log('onUnload函数被加载')
    }
})

onHide和onUnload这两个函数需要执行一些API操作,比如页面执行tab栏切换页面、navigateTo方法、或者使用小程序切换后台的按钮时会执行onHide函数;当页面执行redirectTo或navigateBack的时候会执行onUnload函数。

当然我们还可以添加任意的数据和函数到这个Page方法的Object参数中,在页面的函数中用this就可以访问这些自定义的数据或者函数

以下内容你不需要立马完全弄明白,不过以后它会有帮助。

生命周期

微信小程序开发笔记_第2张图片
Page实例的生命周期

通过以上图解可知

  • onLoad 、onShow和onReady是按照先后顺序,依次执行
  • onLoad、onReady在整个页面的生命周期中只会执行一次(除非这个页面被执行onUnload卸载掉了)
  • onHide和onShow在一次生命周期内可能会执行多次。
  • 除了页面的First Render第一次渲染,页面还有可能会Rerender 再次渲染多次,数据更新会造成页面的重新渲染,这里要注意的是,小程序仅在第一次 First Render完成后,提供了监听函数onReady,对于以后的Rerednder并没有提供相应的监听函数。所以,onReady仅用来监听“第一次渲染”完成。

4、数据绑定

小程序借鉴了比如像Angular、vue这些流行框架的思想,采用数据绑定的机制来做数据的初始化和更新,不同于像Angular和Vue的双向数据绑定,小程序仅实现了单向数据绑定,即支持从逻辑层传递到视图层的数据绑定,反之则不行。

小程序使用Page方法参数里的data变量作为数据绑定的桥梁,写在data里的数据,被称为数据绑定的初始化数据。

数据的绑定有以下两种:

  • 一种是初始化数据的数据绑定,通常将这些数据直接写在Page方法参数的data对象下面。
  • 另外一种是使用setData方法来做数据绑定,这中方式也可以理解为数据更新。这样的数据更新将引起页面的重新渲染(Rerender)

说明:小程序的脚本是运行在JSCore中,JSCore是一个没有DOM的环境,所以小程序只能使用数据绑定来做数据的相关操作。

4.1、初始化数据绑定
小程序使用Mustache语法双大括号{{}}在微信,wxml组件里进行数据的绑定,通过Page实例的生命周期,解释一下初始化数据绑定的过程

当页面执行了onShow函数后,逻辑层会收到一个通知(Notify),随后逻辑层会将data对象以json的形式发动到view视图层(Send Initial Data),视图层接收到初始化数据后,开始第一次渲染,显示初始化数据(First Render),最终将数据呈现在开发者的眼前。

说明:如果数据绑定是作用在组件的属性中,比如,一定要在{{}}外边加上双引号,否则小程序会报错,如果是内容型的数据,则不需要加双引号,比如{{data}}

4.2、查看数据绑定对象

通过AppData面板来查看和调试数据绑定变量,AppData下的数据以页面为组织单位,更改这里的某一项数据的值,都是实时进行更新的。

4.3、数据绑定更新

可以通过setData函数来做数据绑定,这种方法可以理解为“数据更新”。setData方法位于Page对象的原型链上:Page.prototype.setData。大多数情况下,我们使用this.setData的方式来调用这个方法。

setData的参数接受一个对象,以key和value的形式将this.data中的key对应的值设置成value。

上面的话要注意两点:

  • setData 会改变this.data变量里相同key的值。
  • setData执行后会通知逻辑层执行Rerenader,并立刻重新渲染视图层。

this.setData所绑定或者更新的数据,并不要求在this.data中已预先定义。

Page({
    data:{
        
    },
    onLoad: function(){
        var objData = {
            obj: {
                text:"hello"
            },
            name:"小明"
        }
        this.setData({
            pageData: objData
        })
    }
})
 {{pageData.obj.text}} 

5、列表渲染 wx:for

标签没有实质意义,它并不是组件,所以我们称作“标签”,它仅仅是一个包装,不会在页面内被渲染,在这里可以理解为一个JavaScript编程语言中的括号,在block标签中被包裹的元素将被重复渲染。

wx:for-item="item" 指定数组当前子元素的变量名,我们将元素的变量名指定为item,当然这个变量名可以更改。

如果不定义item数组子元素的变量名,依然是可以正常显示的,原因是小程序默认子元素的变量名就是item。

wx:for-index="idx" 指定当前元素在数组中索引号的变量名,我们命名为idx。

wx:for 并不是一定要作用在block标签上的,也可以作用在view组件上,一样可以照常运行,但是并不推荐使用view等组件来做列表渲染,因为我们希望标签或者组件元素是语义明确的,view组件通常被用来当做视图容器或者是区域的分隔,它有它的使命,不应该滥用。

6、事件

要从一个页面跳转到另外一个页面,需要使用事件来响应点击某个动作。

什么是事件?

事件定义:事件是视图层(wxml)到逻辑层的通讯方式。简单理解:事件可以让我们在js里处理一些用户在界面上的一些操作,并对这些操作做出反馈。比如:点击一个按钮,从一个页面跳转到另外一个页面,在这个过程中,需要在js里调用MINA框架的API,使从一个页面跳转到另外一个页面。

想要实现页面跳转的这个机制,需要做两件事情

  • 在组件上注册事件,告诉小程序要监听哪个组件的什么事件?
  • 在js中编写事件处理函数响应事件,也就是说,监听到事件后,需要编写自己的业务。
6.1、冒泡事件和非冒泡事件

冒泡事件是指某个组件上的事件被触发后,事件还会向父级元素传递,一直到页面的顶级元素。

非冒泡事件则不会向父级元素传递事件。

bind和catch的区别

bind不会阻止事件的传播,而catch会阻止事件继续向父节点传播。

7、导航

小程序提供了5个导航API,从而帮助开发者实现页面的跳转

  • wx.navigateTo 保留当前页面,跳转到应用内的某个页面,使用wx.navigateBack可以返回到原页面
  • wx.redirectTo 关闭当前页面,跳转到应用内的某个页面
  • wx.switchTab 只能用于跳转到带有 tabBar 页面,并关闭其他所有非 tabBar 页面
  • wx.navigateBack 关闭当前页面,返回上一页面或多级页面。可通过 getCurrentPages() 获取当前的页面栈,决定需要返回几层
  • wx.reLaunch 关闭所有页面,打开到应用内的某个页面

wx.navigateTowx.redirectTo的区别

tapHandle: function (){
    wx.navigateTo({
      url: '../main/main?name=小明',
    })
    // wx.redirectTo({
    //   url: '../footer/footer',
    // })
  },
  onUnload: function(){
    console.log("page is unload")
  },
  onHide: function (event) {
    console.log("page is hide")
  }

使用 wx.redirectTo 实现跳转,将打印输出 page is unload,但是不会输出page is hide,跳转到的页面没有返回按钮,无法返回到之前的页面了。

使用wx.navigateTo 实现跳转,将打印输出 page is hide,但是不输出 page is unload,页面左上角出现一个可以返回到之前页面的按钮。

所以总结出wx.redirectTo是将关闭当前页面并将页面卸载,无法返回到之前的页面,而wx.navigateTo仅仅是隐藏当前页面,还可以再次返回到之前的被隐藏的页面。

再来考虑一个问题,当wx.navigateTo从一个页面跳转到另外一个页面后(从A页面跳转到B页面),再从B页面返回到A页面时,B页面会执行onHide还是onUnload呢?答案是执行B页面的onUnload函数,也就是说当从子页面返回到父页面时,子页面会被卸载。因为这样设计就不会造成大量的子页面残留在小程序中了。(早期的小程序是不会卸载的,后来在版本更新的时候更改的。)

7.1、小程序最多只能有5层页面

当我们使用wx.navigateTo 从父页面跳转到子页面后,就形成了2个页面层级,可以继续在子页面里使用wx.navigateTo 跳转到子页面。

但是小程序强制规定,只允许有最多5层父子页面,事实上,太多的页面将严重影响用户的产品体验,建议页面最多不要超过3层。

wx.redirectTo不存在这个问题,因为当跳转到另外一个页面上后,上一个页面被强制卸载掉了。

8、小程序的模块化

如果所有的数据都写在js文件中,这样会污染了我们的业务层,我们应该把这些数据分离到一个单独的js文件中,

在项目的根目录下新建一个文件夹,命名为data,然后再data目录下新建一个js文件,命名为data.js。

// data.js

var postList = [
    {
        date:'1111',
        title:'hahha'
    }
]

然后将之前js文件中data里面的数据剪切到data.js中。所以我们提取出来的数据文件data.js可以看做是小程序的一个模块。同时我们需要在data.js中使用module.exports向外部暴露一个接口

// data.js

var postList = [
    {
        data:'1111',
        title:'hahha'
    }
]

module.exports = {
    postList:postList
}

然后在其他js文件中引用这个data.js模块,

// 其它js文件
var dataObj = require('../data/data.js')

Page({
    data:{
        
    },
    onLoad: funciton (){
        this.setData({
            postList:dataObj.postList
        })
    }
})

使用require 引用js模块时,要注意以下几点:

  • 被引用的文件一定要带有拓展名.js,这一点是不同于页面路径的。
  • path路径不可以使用绝对路径,否则会报错,应该使用相对路径。
  • 在JavaScript文件中声明的变量和函数只在该文件中有效,不同的文件可以声明相同名字的变量和函数,不会相互影响。

9、小程序的模板化

我们通常可以将一些公共的、经常使用的业务逻辑提取成一个公共的函数,当在多个地方需要使用函数时,只需要要调用这个函数即可。

事实上,有一句话是这么描述软件开发的:编程世界里遇到的绝大多数问题都可以用封装的思想来解决。

// post-item-tpl.wxml


模板相关的内容必须包裹在标签中,使用name属性来指定模板的名称

// post.wxml
// 使用import来引用模板