Skip to content

1.小程序中的通信模型

image-20230608195330568|600

不存在虚拟 dom 的概念:

微信小程序在渲染层和逻辑层之间采用了跨平台的通信机制。在小程序的渲染层,使用的是基于 Web 技术的 WebView 进行渲染,类似于在网页中展示 HTML 和 CSS。 WebView 负责处理渲染和绘制,但并不具备完整的浏览器功能。

虚拟 DOM(Virtual DOM)是一种在前端框架中常见的技术,用于提高 DOM 操作的效率。它通过在内存中创建 DOM 树的副本,并在更新时进行比较,减少实际 DOM 操作的次数,从而提高性能。然而,在微信小程序中,并没有直接暴露虚拟 DOM 给开发者使用。

微信小程序的渲染层并没有提供类似虚拟 DOM 的概念和接口。相反,小程序框架提供了基于组件化开发的方式,通过数据绑定和响应式更新来管理和更新视图。当数据发生变化时,框架会自动更新相关的组件和页面。

虽然微信小程序没有显式的虚拟 DOM 概念,但小程序框架通过内部的优化机制和批量更新的方式,尽可能地减少对实际 DOM 的操作,以提升性能和渲染效率。开发者可以专注于数据的处理和业务逻辑,而无需过多关注底层的 DOM 操作细节。

2.配置文件 json

看了基础文档之后,对于微信小程序的理解,以及它相比于 vue 的一些区别:

小程序对于配置文件比较讲究,有全局的 json 配置和每个页面的 json 配置,其中每个页面的单独配置可以覆盖全局的配置

image-20230608211025079

(1)app.json 是全局配置

(2)project.config.json 是项目配置

(3)sitemap.json 就是配置能不能被检索,没什么用

每个页面的 json 文件是专门配置本页面的窗口相关的属性的, 如果重复了的话,它只会去覆盖 app.json 中 window 这一个选项的内容!

app.json 的 pages 选项需要配置每一个页面的路径,这样小程序开发者工具就会帮我们自动创建页面的基本的四个文件和文件夹,同时需要注意的是,pages 中的第一个会默认作为首页

3.小程序与 web 的区别理解

首先微信小程序和 web 项目是不一样的,它的逻辑层和渲染层要通过微信客户端进行连接交互,而 web 项目是通过浏览器

微信小程序的所有 dom 标签都是组件,都是被微信官方封装过的,不同于传统意义上的 html 标签,功能都相对较为更加丰富,所以小程序是不存在传统的 dom 标签的,这也使得这些标签可以实现类似 vue 的模板语法等功能

image-20230608212056850|400

image-20230608212119227|450

image-20230608212259545|425

image-20230608212343669|425

4.小程序的内置组件(相当于 html 标签)

小程序中的主要的组件类别:

1.视图容器类组件:view,scroll-view,swiper 和 swiper-item

2.基础内容组件:text,rich-text

(1)text 有 selectable 属性,可以实现长按选中文本的效果,也只有这一个组件可以实现这个效果

(2)rich-text 有 nodes 属性,nodes=“html 标签内容”,这里就相当于是 v-html 了

3.其他常用组件:button,image,navigator

(1)button 有 type,size 属性,还有 plain 指定为镂空样式,相当于是 element-ui 的组件那种

(2)image 有 mode 属性,可以指定缩放模式,类似于 css 的 background-size 的 cover 和 contain 属性那些

scaleToFill 属性相当于 cover,而 spectFit 属性相当于 contain

(3)navigator 的链接属性不是 href,而是 url,这是和 a 标签很大的区别

注意:实际上小程序中说 dom 对象是不合理的,应该说组件,小程序中万物皆组件!因为小程序不是基于浏览器环境的,没有 html。

5.小程序的 API

image-20230608213508098|725

6.小程序核心概念理解

1.每个页面的 js 文件负责页面对象的构建

基本结构:

js
Page({

	data:{

	变量:”值”

	},

	method1(){

	},

	method2(){

	},

	钩子1(){

	}

})

2.微信小程序是不存在双向绑定,js 操作方式与 vue 指令有较大不同

(1) 数值绑定:没有 v-bind:属性=“”这种东西,取而代之的是这种插值表达式,可以用在 dom 标签中和页面中,实现绑定 js 的数据到 html

(2)事件绑定:没有 v-on:click=这种东西,取而代之的是 bind:tap,简写方式是 bindtap,这实现了 html 到 js 的反向通讯,但是绑定的函数和传值和 vue 还有很大的不同,bindtap=“test”这里的 “”里面的内容会被全部解析成函数名(同时也要注意不要有空格什么的,否则也会报错找不到对应的 method),要传参需要定义 data-**=“”作为 dom 的自定义属性,在 js 的函数中会默认获取 dom 的一些内容,例如 test(e){}这个 e.target.dataset.** 就代表了星星的值,那么还可以在“”中用里面写 js 的内容变量等,而 e.target 代表了当前 dom 对象

(3) 双向绑定(表单):没有 v-model=“”这种东西,因为没有双向绑定的特性,但是对于 input 标签在微信小程序中也可以模拟出来 v-model 的双向绑定的特点,例子:

js
<input value="{{msg}}" bindinput="handleInput"></input>

data:{mag:”你好“}

handleInput(e){

  this.setData{

msg:e.detail.value //注意:e.detail.value就是用户在 input 框输入的数据,并且只有 input 框才可以使用这种方式获取值,value 是特定的属性

  }

}

这里不需要写 data-变量,因为在微信小程序中 input 事件绑定的函数会默认获取 value 属性的值,不同于其他的标签

(4)条件判断:没有 v-if=“”,取而代之的是 wx:if=“”wx:elif=“”wx:else=“”

(5)循环遍历:没有 v-for=“”,取而代之的是 wx:for=

例子:

html
<view wx:for="{{rankList}}" wx:key="index"> {{item}} </view>

注意:wx:for 不需要(item,index) in list,默认每项就叫做 item,直接使用即可,key 就叫做 index

如果是 wx:for 嵌套 wx:for 的结构,那么也是里层的叫 item,外层的也叫 item,可以直接取到对应的!

注意:wx:for-itemwx:for-index 属性

wx:for-itemwx:for-index 是在 wx:for 循环中可选的属性

3.小程序中对于数据的获取与操作

三种取值方式的区别:

(1)最常用的获取值的方式:e.target.dataset.变量

js
lookMore(e){
  this.setData({
    moreIndex:e.target.dataset.index
  })
},

(2)很多时候,只能用 e.target.dataset.变量 的方式获取,e.detail.value 的方式只适用于 input 的时候,而 e.currentTarget.dataset.变量 这其实和 e.target.dataset.变量 基本一样,只有一点差别,源于 currentTarget 和 target 的区别

currentTarget 和 target 的区别:

image-20230612163121736|675

(3)小程序中在 js 中操作 data 中的数据

  • 取数据:this.data.变量

  • 设置数据:

js
this.setData{

	变量:数值

}

7.小程序路由

1.声明式路由导航

小程序中没有 vue-router,主要是通过 navigator 这个组件来实现的,相当于 router 中的 router-link,同样有声明式导航和编程式导航两种,声明式导航又分为导航到 tabbar 和导航到其他非 tabbar 页面,tabbar 的 opentype=“switchTab”,而其他页面为 opentype=“navigate”这是默认的,可以省略。

还有一种是后退导航 opentype=“nagivateBack” 同时还有 delta 属性表示后退的层级,默认为 1,可以省略

2.编程式路由导航

1.导航到 tabbar:

js
wx.switchTab({

url:””

})

image-20230608225841537|700

2.导航到非 tabbar:

js
wx.navigateTo({

​	url:””

})

3.后退导航:

js
wx.navigateBack({

​	url:””

})

3.导航传参

直接在声明式或者路由式导航的 url 后面拼接用?a1=1&a2=2 这样子即可,和 router 的 query 传参类似

在目标页面的 onLoad 钩子的参数中可以进行接收

js
onLoad(options){

	//options就是传过来的参数

}

8.页面的内置事件

页面事件,是小程序内置的事件函数,和生命周期钩子是同级的 api 函数

1.下拉刷新事件

json 文件可以设置刷新的启停用,还有刷新的背景样式

在 js 文件中的具体 api 应用:

js
/**
   * 页面相关事件处理函数--监听用户下拉动作
   */
onPullDownRefresh: function () { //和钩子函数同级
    // console.log('触发了message页面的下拉刷新')
    this.setData({
        count: 0
    })
    wx.stopPullDownRefresh() //必须调用停止刷新的 api
},

2.上拉触底事件

可以在 json 中配置上拉触底的距离,默认是 50px

js
onReachBottom:function () { //和钩子函数同级
    // console.log('触发了message页面的下拉刷新')
  	wx.showLoading({title:”数据加载中…”})
    //进行数据请求
    getData().then(()=>{
        wx.hideLoading()//在请求完成时调用wx.hideLoading()//隐藏加载效果
    })

},

注意:这里应当在加载数据的时候调用加载中…的弹窗 api

wx.showLoading({title:”数据加载中…”})

此外还有其他状态弹窗:wx.showToast({title:”成功”,icon”none”})

3.节流处理

对上拉触底进行节流处理

原因:有可能我们在很短时间内触发很多次触底事件,那这样会不断地请求,并且只会返回最后一次的数据

解决:我们应当进行节流处理,也就是正在进行网络请求时拒绝再次请求(之前我所理解的节流是,冷却施法,也就是一段时间之后才可以再次请求,那么这里的一段时间就是请求开始到完成的时间!)

js
// pages/contact/contact.js
Page({
  /**
   * 页面的初始数据
   */
  data: {
    colorList: [],
    isloding: false, //节流标志变量
  },

  getColors() {
    this.setData({
      isloding: true, //正在发起网络请求
    });
    // 需要展示 loading 效果
    wx.showLoading({
      title: "数据加载中...",
    });
    wx.request({
      url: "https://www.escook.cn/api/color",
      method: "get",
      success: ({ data: res }) => {
        this.setData({
          colorList: [...this.data.colorList, ...res.data],
        });
      },
      complete: () => {
        wx.hideLoading();
        this.setData({
          isloding: false, //请求完成之后,停止节流
        });
      },
    });
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    this.getColors();
  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom: function () {
    if (this.data.isloding) return; //如果正在进行网络请求,就直接结束,不进行再次请求

    this.getColors();
  },
});

9.小程序生命周期

自定义 编译模式:

image-20230608232754966|550

小程序的生命周期分为应用生命周期和页面生命周期:

image-20230608233246314|700

在 app.js 中使用应用生命周期

js
//app.js
App({
  /**
   * 当小程序初始化完成时,会触发 onLaunch(全局只触发一次)
   */
  onLaunch: function () {
    // console.log('onLaunch')
  },

  /**
   * 当小程序启动,或从后台进入前台显示,会触发 onShow
   */
  onShow: function (options) {
    // console.log('onShow')
  },

  /**
   * 当小程序从前台进入后台,会触发 onHide
   */
  onHide: function () {
    // console.log('onHide')
  },

  /**
   * 当小程序发生脚本错误,或者 api 调用失败时,会触发 onError 并带上错误信息
   */
  onError: function (msg) {
    // console.log('onError')
  },
});

image-20230608233433416|350

在 xxx.js 中使用页面生命周期:

js
// pages/home/home.js
Page({
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {},

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

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

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

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

用的多的:

  • onLoad:初始化页面的数据、转存页面接收到的参数 options

  • onReady:修改页面里面的内容,有一些相关的方法,比如修改页面标题

10.wxs

wxs 的应用场景

wxml 中无法调用在页面的 .js 中定义的函数(只是能用事件绑定进行函数的绑定而已),但是 wxml 中可以调用 wxs 中定义的函数

因此,小程序中 wxs 的典型应用场景就是“过滤器”

(单独总结)

11.小程序自定义组件

自定义组件的概念:

注:小程序与 vue 的最大区别就是,vue 的所有页面都是由组件构成的(只是有父组件和子组件之分),小程序的页面是由 页面+自定义组件 构成的

在小程序中,页面只是最外面的那一层,代码的比重应当并不大,但其实本质上也是组件

小程序中页面和组件的区别:

页面是一个页面的大容器,相当于父组件,需要定义在 app.json 中,需要路由去跳转过去

组件是页面的零部件,相当于子组件,不需要定义在 app.json 中,只是为了可复用地应用在页面中,但是需要定义在每个页面的 xxx.json 中,这时 xxx.json 的作用类似于 vue 中的 component 选项!

具体的明显区别:

image-20230608235022541|300

但是组件也同样是由四个基本文件组成的

image-20230608234734026|300

组件需要被引用才可以进行使用,类似于 vue 中的 components 配置项!

局部引用:

![image-20230608234935550](/Users/songshu/Library/Application Support/typora-user-images/image-20230608234935550.png)

全局引用:

image-20230608234954740|575

12.组件的样式隔离

本质上页面也是组件,所以这里就指的是组件和组件之间,不一定要是页面

默认情况下组件和外界毫不相干,互不影响,但是注意只有 class 类样式有组件隔离的效果,其他都没有,所以尽量在引用了组件和组件自己的 wxss 文件都用 class 选择器,否则是会相互影响的,而且 app.wxss 也只有 class 选择器不会影响组件的样式

可以在组件的 js 或者 json 文件中,修改隔离的配置,使可以相互影响,或单向影响:

image-20230608235910384|625

styleIsolation 的可选值

可选值默认值描述
isolated表示启用样式隔离,在自定义组件内外,使用 class 指定的样式将不会相互影响(形成隔离)
apply-shared表示页面 wxss 样式将影响到自定义组件,但自定义组件 wxss 中指定的样式不会影响页面(页面——>组件)
shared表示页面 wxss 样式将影响到自定义组件,自定义组件 wxss 中指定的样式也会影响页面和其他设置了 apply-shared 或 shared 的自定义组件(页面<——>组件)

如果想要不隔离,就配置为 apply-shared 或者 shared

shared 之后,组件中如果使用了某些组件库,那么就可以在组件中定义 class 修改这个组件库的样式了

注意:样式隔离只针对 class 样式,就算是 app.wxss 文件的 class 样式也会被样式隔离

不受样式隔离的内容有哪些?

在小程序中,有几个方面的样式不受样式隔离的限制,它们可以影响其他组件或全局样式。这些方面包括:

  1. 全局样式:全局样式定义在 app.wxss 文件或项目根目录下的其他样式文件中,它们不受组件样式隔离的限制,可以影响整个小程序的样式。
  2. ID 选择器:ID 选择器(如 #my-id)是全局唯一的选择器,它们不受组件样式隔离的限制,可以通过 ID 选择器选择和修改任何元素的样式。
  3. 通用样式选择器:通用选择器(如 *)也是全局的,它们会匹配所有元素,不受组件样式隔离的影响。
  4. 样式继承:某些样式属性具有继承性,即子元素会继承父元素的样式。例如,colorfont-size 等属性在小程序中具有继承性,这意味着父元素的样式可能会影响子元素。
  5. 嵌套选择器:如果在一个组件的样式中使用了嵌套选择器(如 .parent .child),并且父组件和子组件同时引入了这个样式,那么该样式可能会影响其他组件中匹配同样选择器的元素。
  6. css 变量:CSS 变量本身是全局的,它们的作用域不受样式隔离的影响。

小程序组件默认设置了样式隔离,但是我想在某个组件中使用 app.wxss 中定义的类选择器怎么办呢?

如果您想在组件中使用 app.wxss 中定义的类选择器,可以通过在组件的样式文件(.wxss 文件)中引入 app.wxss 文件来实现。例如,在组件的样式文件中使用 @import 导入全局样式文件:

css
@import "../../app.wxss";

/* 组件的其他样式 */

这样,您就可以在组件的样式中使用 app.wxss 中定义的类选择器,并确保样式隔离的限制。

13.组件的写法

xxx.js 的基本格式

js
Component({
  data: {},
  methods: {},
});

1.methods

methods 分为两种:

  • 事件处理函数:用于和事件绑定的函数

  • 自定义方法:就是普通的函数,建议用_开头

2.properties

Component 内有 properties:{}选项,里面的定义和 data 一样,和 vue 的 props 类似,但是不同的是 properties 是可读可写的,而 props 只可以读

本质上 properties 和 data 两者是一样的,只是 data 习惯性存储私有的,properties 习惯性存储外界传入的,两者里面的变量不能重名

properties 调用:this.properties.变量

设置值:同样使用 this.setData({

变量:值

})

3.组件传值

如何给组件传值?和 vue 一样的

html
<test-component msg="aaa"></test-component>

4.observers

数据监听器 observers 选项,类似于 vue 中的 watch 侦听器!只是写法不一样而已。

注意变量 1 和 2 都是定义在 data 里面的数据

js
observers:{
  '变量1,变量2':function(e1,e2){
    this.setData({变量3:e1+e2})
  }
}

注意:e1,e2 默认就是获取的变量 1 和变量 2 变化后的值,默认在使用 setData 设置变量 1 和变量 2 的值的时候进行触发

注意:还可以监听对象的属性,和变量同理,但是触发是会在改变整个对象或改变对象属性时都触发,而不是必须改变对象属性

image-20230609001825718|650

image-20230609001920338|700

注意:这里的rgb.**就相当于监听整个 rgb 对象了!

其中**被称作通配符

5.纯数据字段

定义:不需要展示在页面上的数据,也不用传递给其他组件,只用于 js 逻辑的内部运算

好处:纯数据字段有助于提升页面更新的性能,因为不需要去响应式地检查他们的变化

使用方法:

定义 options 选项,里面写上 pureDataPattern 属性,这代表了 data 选项中纯数据字段的命名规则

例子:

image-20230611221241295|725

此时定义的是以_开头的变量命名为纯数据字段,所以_b为纯数据字段

14.组件的生命周期

image-20230611221654133|675

组件的生命周期中 created 时还不能调用 setData 函数,这个周期也不常用

attached 相当于页面的 onLoad 周期(即 mounted),此时页面完全初始化完毕

而 detached 相当于页面的 onUnload 周期(即 unmounted)

注意:组件的生命周期可以定义在 lifetimes 选项中,这是推荐的方式,因为这里面的优先级最高,会覆盖掉定义在外面的生命周期

image-20230611225938668|675

组件所在的页面的生命周期:

自定义组件的行为可能依赖于页面状态的变化,这就需要组件所在的页面的生命周期,大概就相当于组件对于页面的一个监听器的作用了(根据页面的变化触发不同的函数操作)

image-20230611232323953|675

组件所在的页面的生命周期定义在 pageLifetimes 周期中

show 也是比较常用的,当宿主页面被展示时,我们可以进行一些操作

15.插槽 slot

1.单个插槽

和 vue 一样,直接定义位置并传入即可

image-20230612003709453|750

2.多个插槽(具名插槽)

由于小程序组件默认只允许有一个插槽

所以我们需要在 options 选项中开启配置项

image-20230612003801475|700

定义插槽:

image-20230612005227505|675

使用插槽:

image-20230612005239705|700

16.父子通信

和 vue 中基本一样,只是方法名字等略有不同

1.父到子:proprties

使用 proprties 属性,实现父组件传子组件,但是只能传普通的数据,不能传递方法

2. 子到父:自定义事件

使用绑定自定义事件的方法,实现子组件传父组件

sync 事件为我们的自定义事件:

image-20230612011937334|725

在子组件中使用 triggerEvent 方法触发自定义事件,并传参过去

类似于 vue 中的 this.$emit 方法

image-20230612012544500|600

在父组件的回调函数中接收传过来的参数:

image-20230612012658300|700

3.子到父:获取组件实例

类似于 vue 中的 this.$refs

这里使用 this.selectComponent 方法

image-20230612013046273|775

注:本质上是在父组件中操控子组件中的数据,因为就算调用了子组件中的方法,也只能是操作子组件的数据,对父组件没什么影响!

但是子组件本身也是显示在父组件中的,所以也相当于操作了页面中的数据!

4.behaviors

这个用于实现组件之间共享代码的,类似于 vue 中的 mixins

每一个 behaviors 对象可以包含 propties、data、methods 和生命周期函数(和组件的生命周期相同)等内容,当被使用时会自动并入组件

注意:behaviors 不能用于 Page 页面

定义:

image-20230612013625499|625

导入并使用:

image-20230612013640732|650

同名字段的组合和覆盖规则:

image-20230612013732147|525

如果冲突了,会比较复杂,下面是官方文档说明:

https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/behaviors.html

17.vant 组件库的使用

小程序对于 npm 的支持:image-20230612013851527|300 所以可能我们很多包要自己去网上下载,并放入项目中使用

vant 组件库

image-20230612013946833|300

这是一个适用于 vue 和微信小程序等多个框架的一个构建 app 的组件库!

下载并引入:

1.初始化一个 package.json 文件

bash
npm init -y

2.安装 vant weapp

bash
npm i @vant/weapp@1.3.3 -S --production

3.构建 npm 包

4.修改 app.json

删除“style”:“v2”这个配置,否则可能样式冲突

注:具体使用方法可以看官方文档,还是要以官方文档为准!

需要注意的是我们可以在 app.wxss 文件中定义 css 变量从而修改 vant 的默认样式

Vant Weapp 支持使用 CSS 变量来实现定制主题。

关于 CSS 变量的基本用法,请参考 MDN 文档:https://developer.mozilla.org/zh-CN/docs/Web/CSS/Using_CSS_custom_properties

有点类似于 less,scss 的操作,但是这是原生 css 的用法:css 变量!

实际操作:

--加上变量名字即可组成为一个 css 变量名

在根节点 page(相当于 html 里面的 body)内定义 css 变量,这里面的变量是顶级作用域的

我们自己定义了 css 变量值,就可以覆盖 Vant Weapp 中默认的那个变量样式!

image-20230612014228400|675

所有 Vant Weapp 可用的颜色变量,请参考 Vant 官方提供的配置文件:https://github.com/youzan/vant-weapp/blob/dev/packages/common/style/var.less

18.网络请求 wx.request

wx.request 是用于网络请求的,但是它不是 promise 风格,使用的时候传参较为复杂,并且容易造成回调函数中嵌套回调函数,形成回调地狱!

image-20230612014726599|675

我们一般会将其进行封装再使用:

首先 npm init -y 生成 package.json 文件,然后下载 miniprogram-api-promise 库,然后构建 npm

image-20230612015047469

image-20230612014936458|775

引入并注册:

app.js

js
//app.js
import { promisifyAll } from "miniprogram-api-promise";

const wxp = (wx.p = {}); //wxp是一个常量,wx.p是wx对象的一个属性,它们指向同一个空对象 ——> 连续赋值
promisifyAll(wx, wxp); //把wx对象上面的api都Promise化,然后挂载到wxp这个对象上面,然后可以通过wx.p去调用Promise的函数(因为wxp不是全局对象,所以只能用wx对象调用 ——>巧妙的设计)

App({
  //省略
});

使用:这里结合了 await 和 async 实现了同步写法,现在和 axios 使用起来很像

js
// pages/home/home.js
Page({
  data: {},
  async getInfo() {
    //{data: res}的意思是,取出结构返回的Promise对象(其实这个对象就是 res 罢了)的data属性,并重命名为res(不过还需要再.data才是真正的数据)
    const { data: res } = await wx.p.request({
      //wx.p.request的返回值是Promise对象,可以用async和await修饰,就不需要写回调函数了(不需要在success里面写,因为这个getInfo()方法本身就是一个异步的方法)
      method: "GET",
      url: "https://www.escook.cn/api/get",
      data: {
        name: "zs",
        age: 20,
      },
    });
    console.log(res);
    console.log(res.data);
  },
});

以前的写法:

js
Page({
    data: {
    },
    getInfo() {
        wx.request({
            url: 'https://www.escook.cn/api/get',
            data: {
                name: 'zs',
                age: 20
            }
            method: 'get',
            success: ({ data: res }) => {
            this.setData({
                colorList: [...this.data.colorList, ...res.data]
            })
        },
            complete: () => {
                wx.hideLoading()
                this.setData({
                    isloding: false
                })
            }
    	})
	}
})

19.全局数据共享(状态管理)

使用 mobx:

image-20230612015748781|625

bash
npm install --save mobx-miniprogram@4.13.2 mobx-miniprogram-bindings@1.2.1
image-20230612015849181

定义 store.js

js
// 在这个 JS 文件中,专门来创建 Store 的实例对象
// 按需导入相关方法
import { observable, action } from "mobx-miniprogram";

export const store = observable({
  // 数据字段
  numA: 1,
  numB: 2,
  activeTabBarIndex: 0,
  // 计算属性:只有store里面可以定义计算属性(这个计算属性类似于 vue),前面要加一个get修饰符,意味着这个属性是只读的
  // 这里是计算属性sum
  get sum() {
    return this.numA + this.numB; //this指的就是store对象
  },
  // actions 函数:专门用来定义 用来修改 store 中数据的值 的方法
  updateNum1: action(function (step) {
    //updateNum1方法,用action包裹起来,作为action的参数
    this.numA += step;
  }),
  updateNum2: action(function (step) {
    //updateNum2方法
    this.numB += step;
  }),
  updateActiveTabBarIndex: action(function (index) {
    //updateActiveTabBarIndex方法
    this.activeTabBarIndex = index;
  }),
});

使用 store 的方法和数据

在 页面中使用 store

在 message.js 中引入和使用

js
// pages/message/message.js
import { createStoreBindings } from "mobx-miniprogram-bindings";
import { store } from "../../store/store";

Page({
  /**
   * 页面的初始数据
   */
  data: {},

  /**
   * 生命周期函数--监听页面加载:进行store的绑定
   */
  onLoad: function (options) {
    //createStoreBindings方法
    //第一个参数:this,代表页面Page的实例对象,意思是需要将store中的内容绑定给这个实例
    //第二个参数:需要绑定的内容,会直接绑定到Page对象上面
    //返回值为storeBindings,作为Page对象的属性,后面可以用于清理绑定关系
    this.storeBindings = createStoreBindings(this, {
      store, //数据源,会把store的内容绑定到Page上
      fields: ["numA", "numB", "sum"], //绑定的字段,按需定义
      actions: ["updateNum1"], //绑定的方法,按需定义
    });
  },

  btnHandler1(e) {
    // console.log(e)
    // 直接调用我们绑定的store中的方法
    this.updateNum1(e.target.dataset.step); //e.target.dataset.xxx可以取到传入的事件函数的参数值
  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload: function () {
    this.storeBindings.detroyStoreBindings(); //清理绑定的内容
  },
});

message.wxml 在页面上使用

html
<!--pages/message/message.wxml-->
<view>{{numA}} + {{numB}} = {{sum}}</view>
<!--data-xxx为事件传参,step是自己定义的参数名-->
<van-button type="primary" bindtap="btnHandler1" data-step="{{1}}"
  >numA + 1</van-button
>
<van-button type="danger" bindtap="btnHandler1" data-step="{{-1}}"
  >numA - 1</van-button
>

在 组件中使用 store

注:小程序中的页面和组件的 store 绑定方式完全不同的,自成一派!

在 number.js 中引入和使用

js
// components/numbers/numbers.js
import { storeBindingsBehavior } from "mobx-miniprogram-bindings"; //和Page中用的方法不一样
import { store } from "../../store/store";

Component({
  behaviors: [storeBindingsBehavior], //使用 behavios 选项进行代码的复用和嵌入,通过storeBindingsBehavior来实现自动绑定
  storeBindings: {
    //配置storeBindings选项
    store, //数据源
    fields: {
      // 映射的名字(任意):store中的名字
      numA: "numA", //直接写字段名就可以了,最方便
      numB: "numB",
      sum: "sum",
    },
    actions: {
      updateNum2: "updateNum2",
    },
  },

  /**
   * 组件的方法列表
   */
  methods: {
    btnHandler2(e) {
      this.updateNum2(e.target.dataset.step);
    },
  },
});

number.js

js
<!--components/numbers/numbers.wxml-->
<view>{{numA}} + {{numB}} = {{sum}}</view>
<van-button type="primary" bindtap="btnHandler2" data-step="{{1}}">numB + 1</van-button>
<van-button type="danger" bindtap="btnHandler2" data-step="{{-1}}">numB - 1</van-button>

20.分包

1.基本概念与使用

分包就是 把小程序的所有页面,按照需求划分为不同的子包,按需加载,可以加快小程序的启动速度,并且在开放时也方便明确分工

分包前:

image-20230612021100175|700

分包后:

image-20230612021113349|725

注:这里 TabBar 页面指的是可以通过 TabBar 跳转到的页面!即主要页面(TabBar 是底部的导航栏)

访问策略:主包的公共资源可以被任意分包访问,但是分包的私有资源只有自己这个包才可以访问,不能相互访问!

分包 在加载时使用懒加载的规则,小程序启动的时候只会启动主包内的页面,其余分包在被访问到的时候再进行加载,类似于路由懒加载!

分包 d1 体积限制:

如果使用了分包,所有分包加起来不能超过 20m,每个不能超过 2m,但是仅限于分包,这个大小限制和主包无关

所以图片资源要很小才行,最好放在云端!

查看分包大小:

image-20230612021518929 分包的文件夹设置策略和配置策略

image-20230612021729170|650

打包原则:

image-20230612021922933|575

2.独立分包

本质上也是分包,但是特殊在它不需要依赖于主包,可以独立地运行,而普通的分包必须要先加载主包才可以运行

使用场景:在只需要访问分包,不需要访问主包时,可以使用独立分包!

可以有多个独立分包(有点类似于单开了多个小主包,可以和主包平起平坐的感觉,但是它本质上是分包)

配置策略:开启一个 independent 选项为 true 即可

image-20230612022333499|800

特别注意:独立分包同样是私有资源,它不能和其他分包互相访问,特别注意的是它不能访问主包中的公共资源!

分包预下载:相当于对于分包页面的提前加载,当进入某个页面时,就开启后面分包的加载,提升后面的访问速度

image-20230612022634408|750

分包预下载的大小限制

image-20230612022714422|750

app.json 实例

json
{
  "pages": [
    "pages/home/home",
    "pages/message/message",
    "pages/contact/contact"
  ],
  "preloadRule": {
    //分包预下载
    "pages/contact/contact": {
      //进入contact页面时就进行下载
      "packages": ["p1"],
      "network": "all"
    }
  },
  "subpackages": [
    //配置分包
    {
      "root": "pkgA", //路径查找的根文件夹
      "name": "p1", //别名
      "pages": ["pages/cat/cat", "pages/dog/dog"]
    },
    {
      "root": "pkgB",
      "name": "p2",
      "pages": ["pages/apple/apple"],
      "independent": true //独立分包
    }
  ],
  "window": {
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#13A7A0",
    "navigationBarTitleText": "WeChat",
    "navigationBarTextStyle": "black"
  },
  "tabBar": {
    //这里面就是所有tabBar页面,每个 pagePath对应了pages选项配置的每个主页面
    "list": [
      {
        "pagePath": "pages/home/home",
        "text": "首页",
        "iconPath": "/images/tabs/home.png",
        "selectedIconPath": "/images/tabs/home-active.png"
      },
      {
        "pagePath": "pages/message/message",
        "text": "消息",
        "iconPath": "/images/tabs/message.png",
        "selectedIconPath": "/images/tabs/message-active.png"
      },
      {
        "pagePath": "pages/contact/contact",
        "text": "联系我们",
        "iconPath": "/images/tabs/contact.png",
        "selectedIconPath": "/images/tabs/contact-active.png"
      }
    ]
  },
  "sitemapLocation": "sitemap.json",
  "usingComponents": {
    "van-button": "@vant/weapp/button/index",
    "my-numbers": "./components/numbers/numbers",
    "van-tabbar": "@vant/weapp/tabbar/index",
    "van-tabbar-item": "@vant/weapp/tabbar-item/index"
  }
}

对于 tabbar 的定义

在 app.json 中,配置 tabBar 选项

“custom”: true 表示开启自定义 tabBar

“list” 不用动,这些定义不会用于 tabBar 的渲染,但是为了低版本兼容和声明 tabBar 页面应当保留这个定义

image-20230612022835344

tabbar 的文件夹名是固定的,叫做 custom-tab-bar

小程序会自动读取这个目录下的内容,作为 tabBar,自动渲染到页面的相应的位置

我们可以使用 vant 提供的 tabbar 组件,非常方便

自定义 tabbar 组件的案例:

实现的效果:

image-20230612163741630

1.导入 vant 的 tabBar 组件

app.json

json
"usingComponents": {
    "van-button": "@vant/weapp/button/index",
    "my-numbers": "./components/numbers/numbers",
    "van-tabbar": "@vant/weapp/tabbar/index", //tabBar组件
    "van-tabbar-item": "@vant/weapp/tabbar-item/index" //tabBar组件
}

下面都是 custom-tab-bar 文件夹的内容

2.index.wxml

html
<!--custom-tab-bar/index.wxml-->
<!--active-color可以修改选中项的文本颜色值-->
<van-tabbar active="{{active}}" bind:change="onChange" active-color="#13A7A0">
  <!--info属性是图标右上角的数字标点的值,但是只有message需要这个值-->
  <van-tabbar-item
    wx:for="{{list}}"
    wx:key="index"
    info="{{item.info ? item.info : ''}}">
    <!--多个插槽:slot="icon"是未选中时的图标,slot="icon-active"是选中时的图标-->
    <image
      slot="icon"
      src="{{item.iconPath}}"
      mode="aspectFit"
      style="width: 25px; height: 25px;" />
    <image
      slot="icon-active"
      src="{{item.selectedIconPath}}"
      mode="aspectFit"
      style="width: 25px; height: 25px;" />
    {{item.text}}
  </van-tabbar-item>
</van-tabbar>

3.index.js

js
// custom-tab-bar/index.js
import { storeBindingsBehavior } from "mobx-miniprogram-bindings";
import { store } from "../store/store";

Component({
  //修改样式作用域
  options: {
    styleIsolation: "shared", //注意:在自定义组件中,需要把作用域改为共享的,才可以去改写vant组件中的样式
  },
  //组件绑定store
  behaviors: [storeBindingsBehavior],
  storeBindings: {
    store,
    fields: {
      //绑定store中的数据
      sum: "sum",
      active: "activeTabBarIndex",
    },
    actions: {
      //绑定store中的方法
      updateActive: "updateActiveTabBarIndex",
    },
  },
  //数据监听器
  observers: {
    sum: function (val) {
      //监听sum数据,准备为info进行赋值,val是sum最新的值(自动获取的)
      this.setData({
        "list[1].info": val, //赋值给info
      });
    },
  },

  /**
   * 组件的初始数据
   */
  data: {
    // active: 0, //要移到store中进行公共存储,否则每次用wx.switchTab跳转页面之后,active就会重新赋值为0了,那么图标和页面就不同步了
    list: [
      //根据list数组的数据去渲染每一个item项
      {
        pagePath: "/pages/home/home",
        text: "首页",
        iconPath: "/images/tabs/home.png",
        selectedIconPath: "/images/tabs/home-active.png",
      },
      {
        pagePath: "/pages/message/message",
        text: "消息",
        iconPath: "/images/tabs/message.png",
        selectedIconPath: "/images/tabs/message-active.png",
        info: 0, //需要渲染数字徽标的项
      },
      {
        pagePath: "/pages/contact/contact",
        text: "联系我们",
        iconPath: "/images/tabs/contact.png",
        selectedIconPath: "/images/tabs/contact-active.png",
      },
    ],
  },

  /**
   * 组件的方法列表
   */
  methods: {
    //change事件绑定的函数
    onChange(event) {
      // event.detail(额外的信息)的值为当前选中项的tabBar索引(等价于在tabBar页面列表中的索引),用于给active重新赋值
      // this.setData({ active: event.detail })
      this.updateActive(event.detail); //使用 store 中的方法更新索引值
      wx.switchTab({
        //wx.switchTab方法可以进行页面的跳转!
        url: this.data.list[event.detail].pagePath, //event.detail就是tabBar索引,得到对应的 tabbar 数据对象,.pagePath得到的就是url
      });
    },
  },
});

4.index.wxss

css
/* custom-tab-bar/index.wxss */
.van-tabbar-item {
  /*在父节点上去定义变量(如果父节点有这个变量,子节点就可以直接用了,因为设置了隔离策略为 styleIsolation: 'shared',即不隔离)*/
  --tabbar-item-margin-bottom: 0; /*利用css变量修改vant的默认样式,让图标和文字间隙为0*/
}

5.store.js:主要是为了记录当前所处的 tabbar 页面的索引值

js
// 在这个 JS 文件中,专门来创建 Store 的实例对象
import { observable, action } from "mobx-miniprogram";

export const store = observable({
  // 数据字段
  numA: 1,
  numB: 2,
  activeTabBarIndex: 0,
  // 计算属性
  get sum() {
    return this.numA + this.numB;
  },
  // actions 函数,专门来修改 store 中数据的值
  updateNum1: action(function (step) {
    this.numA += step;
  }),
  updateNum2: action(function (step) {
    this.numB += step;
  }),
  updateActiveTabBarIndex: action(function (index) {
    //用于自定义的tabBar使用,更新索引值,这样就知道当前处于哪个 tabbar 了
    this.activeTabBarIndex = index;
  }),
});