文章目录
一、自定义组件1、创建组件2、引用组件3、组件和页面区别4、组件样式5、data、methods、properties6、小程序的 data 和 properties 区别7、数据监听器(observers )8、纯数据字段(pureDataPattern)9、组件的生命周期(lifetimes)10、组件所在页面生命周期(pageLifetimes )11、插槽12、父子组件数据传递(properties 、triggerEvent())13、behaviors(混入)二、使用 npm 包1、Vant Weapp2、css变量定制样式三、小程序 API 的 Promise 化1、为什么要 Pronise 化2、实现 Promise 化3、调用 Promise 化的 API四、全局数据共享(mobx-miniprogram、mobx-miniprogram-bindings )1、什么是全局数据共享2、创建 Store 实例对象3、在页面中使用 Store(createStoreBindings )4、在组件中使用(storeBindingsBehavior)五、分包(subpackages )1、分包后规则2、分包基本用法3、打包原则4、引用原则5、独立分包(independent)6、分包预下载(preloadRule)六、自定义 tabBar1、配置一、自定义组件
参考:自定义组件
1、创建组件
1、在根目录新建文件夹 components ,然后在 components 文件夹下新建组件文件夹 test ;在 test 文件夹右键点击新建Component,输入组件名称 test 之后会生成对应的 4 个文件:test.js、test.json、test.wxml、test.wxss;
2、引用组件
1、全局引用:在 app.json 全局配置文件中引入组件,在 window 同级新增下面配置
{"pages":[],"winodw":{},"usingComponents": {"test":"/components/test/test"}}
2、局部引用:在页面的 .json 文件中引入组件
{"usingComponents": {"test":"/components/test/test"}}
使用:在页面 wxml 文件中直接使用
<test></test>
3、组件和页面区别
1、组件的 .json 文件中需要声明 “component”: true;
2、组件的 .js 文件中调用的是 Component({}) 函数;
3、组件的事件处理函数要定义到 methods 节点上;
4、组件样式
1、组件样式隔离:组件之间的样式互不影响,组件和页面的样式也是相对独立的;这也可以防止组件和页面之间样式污染;
注意:
1、由于组件的样式隔离在 app.wxss 中定义的全局样式对组件是无效的;
2、只有 class 选择器会被隔离,id选择器、属性选择器、标签选择器不受样式隔离影响;
2、修改样式隔离选项
在组件中通过 stylelsolation 修改组件的样式隔离选项
Component({options:{stylelsolation : isolated}})
或者在组件的 .json 文件中配置
{"component": true,"stylelsolation": "isolated","usingComponents": {}}
isolated:开启样式隔离
apply-shared:页面样式会影响到组件,但是组件的样式不会影响到页面
shared:页面样式会影响到组件,组件的样式也会影响到其他页面和设置为 shared、apply-shared 的组件
5、data、methods、properties
1、data:组件的私有数据;
2、methods:组件的事件处理函数和自定义方法都放在这里,自定义方法建议下划线开头,与事件处理函数区分开;
3、properties:用来接收外界传递到组件中的数据;
<test min="10"></test>
//data平级properties: {//完整格式min:{type:Number, //类型value:10 //默认值}//简写min:Number //类型}
获取 properties 传递的数据可以直接this.properties.min
来获取;
6、小程序的 data 和 properties 区别
跟 vue 的不同,小程序的 data 和 properties 都是可读可写的;在组件里面可以对 properties 传递进来的对象重新赋值,这一点和 vue 里面的 props 是不一样的; data 和 properties的侧重点不一样:data 侧重组件私有数据,properties 侧重外界传递的数据;
data 和 properties 在本质上是一样的东西,只是认为的区分了他们的功能;所以对于 properties 的操作很操作 data 的方式是一样的(读取、修改);
7、数据监听器(observers )
数据监听器用于监听和响应属性和数据的变化,从而执行特定的操作;类似 vue 里面的 watch ;小程序的语法如下:
1、observers 可以一次监听多个对象,中间逗号隔开,在function的参数中对应的是对象的最新值
//data平级observers: {'numberA, numberB': function(newNumberA, newNumberB) {// 在 numberA 或者 numberB 被设置时,执行这个函数this.setData({sum: newNumberA+ newNumberB})}}
2、observers 还可以监听对象的属性值
//data平级observers: {'numberA.value, numberB.value': function(newNumberA, newNumberB) {// 在 numberA 或者 numberB 被设置时,执行这个函数this.setData({sum: newNumberA + newNumberB})}}
3、监听对象中所以属性的变化,可以使用通配符 “**”
//data平级observers: {'obj.**': function(obj) {console.log(obj);}}
只要 obj 里面的属性发生变化就会触发监听;
8、纯数据字段(pureDataPattern)
纯数字字段:指的是那些不用于界面渲染的 data 数据,也不会传递给其他组件,仅在当前组件内部使用;
好处:有助于提升页面更新的性能;
1、设置纯数据字段
在组件的 Cpmponent 函数中的 options 节点,指定 pureDataPattern 为一个正则表达式,字段名符合这个规则的将成为纯数据字段;
Component({options: {pureDataPattern: /^_/ // 指定所有 _ 开头的数据字段为纯数据字段},data: {a: true, // 普通数据字段_b: true, // 纯数据字段},methods: {myMethod() {this.data._b // 纯数据字段可以在 this.data 中获取this.setData({c: true, // 普通数据字段_d: true, // 纯数据字段})}}})
9、组件的生命周期(lifetimes)
1、小程序很神奇,它的组件生命周期跟页面生命周期(onLoad、onReady、onShow、onHide、onUnload)和应用生命周期(onLaunch、onShow、onHide)完全不一样:
created:在组件实例刚刚被创建时执行,还不能调用 setData,只能给组件的 this 添加一些自定义的属性字段;
attached:在组件实例进入页面节点树时执行,还未渲染,this.data 已经初始化完毕,可以做一些初始化工作(发请求获取初始化数据);
ready:在组件在视图层布局完成后执行,刚被渲染完成之后;
moved:在组件实例被移动到节点树另一个位置时执行;
detached:在组件实例被从页面节点树移除时执行,适合做一些清理性质的工作;
error:每当组件方法抛出错误时执行;
2、定义生命周期函数:将生命周期函数定义在 lifetimes(最新-建议) 字段内
Component({lifetimes: {attached: function() {// 在组件实例进入页面节点树时执行},detached: function() {// 在组件实例被从页面节点树移除时执行},}})
10、组件所在页面生命周期(pageLifetimes )
1、由于组件的某些行为会依赖页面的状态变化,所有需要在组件的 js 文件中监听页面的变化,这种可以被组件访问的生命周期就是组件所在页面的生命周期;
show:组件所在的页面被展示时执行
hide:组件所在的页面被隐藏时执行
resize:组件所在的页面尺寸变化时执行
2、定义生命周期:必须定义在 pageLifetimes 节点中;
Component({pageLifetimes: {show: function() {// 页面被展示},hide: function() {// 页面被隐藏},resize: function(size) {// 页面尺寸变化}}})
11、插槽
1、单个插槽:跟 vue 里面插槽差不多,也是占位符的作用;小程序中默认每个自定义组件只能使用一个插槽<slot>
占位
//组件 test<view><view>这是一个组件</view>//对于不确定的内容,使用 slot 占位,具体内容由组件的使用者决定<slot></slot></view>//调用<test><view>这个内容会渲染到插槽位置</view></test>
2、启用多个插槽:小程序中如果想使用多个插槽,需要在组件中进行配置
Component({options: {multipleSlots: true // 组件使用多 slot}})
多个插槽需要给 slot 定义一个 name 来区分
//组件 test<view><slot name="head"></slot><view>这是一个组件</view>//对于不确定的内容,使用 slot 占位,具体内容由组件的使用者决定<slot name="foot"></slot></view>//调用<test><view slot="head">这是头部的内容</view><view slot="foot">这是底部的内容</view></test>
12、父子组件数据传递(properties 、triggerEvent())
1、属性绑定
父组件向子组件的指定属性设置数据,仅能设置 JSON 兼容的数据,无法传递方法;
子组件在 properties 节点上声明对应的属性并使用:
//子组件properties: {min:{type:Number,value:0}}//父组件<test min="2"></test>
子组件可以对 properties 的属性值进行修改,但是不会直接传递给父组件,如果想传递给父组件就需要用到下面的方法:
2、事件绑定
子组件向父组件传递数据,可以传递任何数据;
父组件 js 文件中定义事件,并在 wxml 文件中绑定事件,子组件调用 this.triggerEvent(事件名,参数) 函数将数据发送给父组件,然后父组件中通过 e.detail 获取到数据:
//父组件 js,定义一个方法接收子组件通过info事件传递的值getChildInfo(e){console.log(e.detail);}//父组件 wxml,定义一个方法 info 给子组件使用<test bind:info="getChildInfo"></test>//还可以这样绑定,不过推荐使用上面那种<test bindinfo="getChildInfo"></test>//子组件传值,在btn 点击事件中传值整个 databtn(){this.triggerEvent("info",this.data)}
这个流程跟 vue 的子组件传值给父组件其实是差不多的;
3、获取组件实例
父组件可以通过 this.selectComponent() 方法获取子组件的实例,这也可以访问子组件的任何数据和方法;this.selectComponent() 方法接收一个选择器(id、class),不可以使用其他选择器;
//wxml<test class="test"></test>//jsonLoad: function (options) {const testChild = this.selectComponent('.test')console.log(testChild)testChild.setData({num:1});//调用子组件的setDate方法修改数据testChild.test();//调用子组件的方法}
这样就可以在父组件里面访问子组件的数据、调用子组件的方法(也可以调用子组件的 setData 方法来修改数据);
13、behaviors(混入)
是小程序中用于实现代码共享的一个方式,类似于 vue 中的 mixins ;每个 behaviors 都有自己的属性、数据、生命周期函数、方法,组件引用它的时候这些属性、数据、方法会被合并到组件中;
每个组件都有可以引入多个 behaviors,behaviors之间也可以相互引用;
1、创建 behaviors
调用 Behaviors 方法就可以创建一个共享的 behaviors 实例对象,供所有组件使用:根目录创建 behaviors 文件夹,然后创建一个 test.js
module.exports = Behaviors({data:{},properties:{},methods:{}})
2、导入和使用
在组件中使用 behaviors ,使用 require() 方法来引入,挂载之后就可以使用 behaviors 中提供的数据和方法;
//导入const test = require("../../behavior/test");//挂载,可挂载多个,逗号隔开Component({behaviors:[test]})
3、behaviors 与组件优先级
data:
若同名的数据字段都是对象类型,会进行对象合并
其余情况会进行数据覆盖,覆盖规则为:组件 > 父 behavior > 子 behavior 、 靠后的 behavior > 靠前的 behavior。(优先级高的覆盖优先级低的,最大的为优先级最高)
properties 、methods:
若组件本身有这个属性或方法,则组件的属性或方法会覆盖 behavior 中的同名属性或方法
若组件本身无这个属性或方法,则在组件的 behaviors 字段中定义靠后的 behavior 的属性或方法会覆盖靠前的同名属性或方法;
若存在嵌套引用 behavior 的情况,则规则为:父 behavior 覆盖 子 behavior 中的同名属性或方法
生命周期:生命周期函数不会相互覆盖,而是在对应触发时机被逐个调用
对于不同的生命周期函数之间,遵循组件生命周期函数的执行顺序
对于同种生命周期函数:behavior 优先于组件执行,子 behavior 优先于 父 behavior 执行,靠前的 behavior 优先于 靠后的 behavior 执行
如果同一个 behavior 被一个组件多次引用,它定义的生命周期函数只会被执行一次
优先级规则参考:同名字段的覆盖和组合规则
二、使用 npm 包
小程序支持使用 npm 第三方安装包,来提升小程序的开发效率,但是小程序中使用 npm 有一些限制:
不支持依赖于 node.js 内置库的包(fs、path)
不支持依赖于浏览器内置对象的包(JQ)
不支持依赖于 C++ 插件的包
1、Vant Weapp
这是一套开源的小程序组件 ui 库,使用的是 MIT 开源协议,对于商业使用比较友好;官网地址:https://vant-contrib.gitee.io/vant-weapp/#/home
可参考官网的快速上手
1、npm 安装 Vant Weapp
打开终端 - npm安装:npm i @vant/weapp -S --production
这里需要看看有没有 package.json 文件,没有的话需要先执行指令 npm init -y 来初始化一个;
2、构建 npm(没有安装依赖都需要重新构建)
打开微信开发者工具,点击 工具 -> 构建 npm,并勾选 使用 npm 模块 选项,构建完成后,即可引入组件;
小程序无法时候 node_module 这个文件里面的内容,所以需要构建成一个 miniprogram_npm 文件,并且每次构建都需要删除这个miniprogram_npm 文件;
3、修改 app.js
将 app.json 中的 “style”: “v2” 去除,小程序的新版基础组件强行加上了许多样式,难以覆盖,不关闭将造成部分组件样式混乱;
4、使用 Vant
安装完成之后在 app.json 文件中的 usingComponents 节点中引入需要的组件,然后就可以全局使用了;
2、css变量定制样式
1、在根节点的内部定义一个 css 变量,然后在使用的时候借助 var 来引用;
<style>html{--theme:"red"}.box{background:var(--theme)}</style>
需要注意的是,定义的变量必须 “–” 开头,它也是有作用域范围的,定义在那个区域下,就只能在这个区域里引用才会生效;
2、css 变量定制Vant 主题
在 app.wxss 文件中,写入变量名,变量名可参考:定制主题;只需要自定义的变量名跟这里需要修改的变量名一致,就可以直接改变原有的样式;
//app.wxsspage{--button-border-radius:10px;}
三、小程序 API 的 Promise 化
1、为什么要 Pronise 化
小程序中官方提供的异步 API 都是基于回调函数实现的,这样很容易导致回调地狱,代码的可读性、维护性都很低;
2、实现 Promise 化
1、主要依赖 miniprogram-api-promise 这个第三方 npm 包
npm i --save miniprogram-api-promise@1.0.4
2、在入口文件(app.js)需要调用一次 promisifyAll() 方法
//app.jsimport {promisifyAll} from 'miniprogram-api-promise';const wxp = wx.p = {};promisifyAll(wx,wxp);
这里 wxp 存放的就是 Promise 化之后的小程序 API,在使用的时候就可以直接使用 wx.p 来调用 Promise 化的 API 了;
3、调用 Promise 化的 API
async getInfo(){const {data:res} = await wx.p.request({url:"/api/get",method:"GET",data:{name:"sz",age:20}})console.log(res)}
四、全局数据共享(mobx-miniprogram、mobx-miniprogram-bindings )
1、什么是全局数据共享
也可叫状态管理,解决小程序组件之间数据共享问题;常见的数据共享有:Vuex、Redux、Mobx
1、小程序中可以使用 mobx-miniprogram 配合 mobx-miniprogram-bindings 实现全局数据共享
mobx-miniprogram:用来创建 Store 实例对象
mobx-miniprogram-bindings:把 Store 实例对象里面的数据、方法绑定到组件或页面中使用
2、安装 mobx 相关包
npm i --save mobx-miniprogram@4.13.2 mobx-miniprogram-bindings@1.2.1
安装完成之后重新构建 npm;
2、创建 Store 实例对象
根目录创建文件夹 store,在文件夹里创建一个 store.js 文件;
//专门创建store实例对象import {observable, action } from "mobx-miniProgram"export const store = observable({userName:'xxs',age:20,//计算属性get sum(){return age++;},//action 修改store 数据updateAge:action(function(val){this.age = 18;})})
1、get 定义计算属性:只需要定义一个方法,然后以 get 开头,表明这个计算属性是只读的;
2、action 方法来修改 store 里面数据:外部想修改 store 只能通过调用 action 方法来修改;val 为外部传的参数;
3、在页面中使用 Store(createStoreBindings )
引入 store 和 createStoreBindings ,在 onLoad 自定义一个对象 storeBindings 调用 createStoreBindings 方法将 store、fields(字段、计算属性)、actions(方法)绑定到 this (当前页面) 上;在 onUnload 里面调用自定义对象 storeBindings 提供的销毁方法 destoryStoreBindings;
<view>{{name}}:{{age}}</view><van-button bindtap="btn1" class="my-button">+1</van-button>
在页面中可以直接通过花括号 {{}} 使用绑定后的 store 字段;
import {createStoreBindings } from 'mobx-miniprogram-bindings';import {store } from "../../store/store";Page({data: {},/*** 生命周期函数--监听页面加载*/onLoad: function (options) {this.storeBindings = createStoreBindings(this,{store, //指定绑定的数据源fields:['age','name'],//字段、计算属性actions:['updateAge'] // 方法})},/*** 生命周期函数--监听页面卸载*/onUnload: function () {this.storeBindings.destoryStoreBindings();},//点击事件btn1(e){this.updateAge();},})
4、在组件中使用(storeBindingsBehavior)
引入 store 和 storeBindingsBehavior,提供behaviors 数组将进入的 storeBindingsBehavior 绑定;
<view>{{name}}:{{age}}</view><van-button bindtap="btn1" class="my-button">+1</van-button>
import {storeBindingsBehavior } from 'mobx-miniprogram-bindings'import {store } from '../../store/store'Component({behaviors:[storeBindingsBehavior],//通过storeBindingsBehavior实现自动绑定storeBindings:{store,//指定数据源fields:{//绑定的字段、计算属性name:"name",//前面的name是自定义字段,可以自己随意修改age:(store)=>store.age,//也可以 ()=>store.agesum:'sum'},actions:{updateAge:"updateAge"}},/*** 组件的方法列表*/methods: {btn(){this.updateAge()}}})
五、分包(subpackages )
把完整的小程序项目按需求划分为不同的模块,在构建中打包成不同的子包,用户在使用时可以按需加载;这样的好处是:
优化小程序首次启动的下载时间
多团队共同开发时更好的进行解耦
1、分包后规则
1、项目构造
主包:包含项目的启动页或者 TabBar 页面,以及一些公共资源
分包:当前分包相关的页面和资源
2、加载规则
小程序启动时默认会下载主包和启动主包内页面(tabBar),在进入某个分包页面时才会下载对应的分包,下载完之后展示;
3、大小限制
整个小程序的主包+所有分包大小不能超过 16 M,主包或单个分包大小不能超过 2 M;如果超过则小程序发包会失败!
2、分包基本用法
在 app.json 文件中通过 subpackages 来声明分包,声明之后会自动创建相应的文件:
//pages平级"subpackages":[ {"root": "package1","name": "p1","pages":["pages/dog/dog","pages/cat/cat"]}]
root 声明这个分包的名称,name 是分包的别名,pages 是分包里面的页面;生成文件如下:
每个包的大小我们可以在微信开发工具-详情-基本信息里面查看;
3、打包原则
1、小程序会按 subpackages 的配置进行分包, subpackages 以外的目录将会打包到主包中;
2、主包也有自己的 pages ,也就是最外层的 pages;
3、tabBar 页面必须在主包里面;
4、分包之间不能相互嵌套;
4、引用原则
1、主包内资源可以被分包引用;
2、分包内的资源只能当前分包引用,其他分包和主包无法引用;
5、独立分包(independent)
本质上也是分包,但是它比较特殊,可以独立于主包和其他分包而单独运行;
默认情况下,用户打开小程序只能从主包进入,然后再去访问分包;独立分包则是用户可以跳过主包,直接进入分包中;一个小程序可以有多个独立分包;
1、定义独立分包
其实跟简单,独立分包跟普通分包在配置上基本一样,唯一区别是在 app.json 配置独立分包的时候需要在这个包里面添加一个配置:independent:true;
"subpackages":[ {"root": "package1","pages":["pages/dog/dog","pages/cat/cat"],"independent": true }]
注意:独立分包里面不能引用主包里面的资源;
6、分包预下载(preloadRule)
在进入主页面的时候会触发分包进行预下载行为,这样是为了提升分包的启动速度;在 app.json 里面,使用 preLoadRule 节点定义分包的预下载规则;
"preloadRule": {"pages/home/home": {"network": "all","packages": ["package1"]}},
pages/home/home
是主页面路径,network 是指定预下载网络模式:all、wifi,packages 是配置需要预下载的分包的,可以是分包的 root 或者 name;
注意:同一个分包(主包)中的页面享有共同的预下载大小限额 2M,限额会在工具中打包时校验
六、自定义 tabBar
可参考:https://developers./miniprogram/dev/framework/ability/custom-tabbar.html
1、配置
1、在 app.json 中的 tabBar 项指定 custom 字段,同时其余 tabBar 相关配置(list)也补充完整,保证低版本的兼容;
2、添加 tabBar 代码文件,根目录创建固定文件夹 custom-tab-bar ,这个文件夹小程序会自动访问,然后在文件夹下创建一个 index 组件;
3、借助 Vant weapp 来自定义导航组件;
如果没有使用组件库,可以参考小程序官网自定义 tabBar 最后面的一个链接,可以直接打开一个小程序项目;