参考:自定义组件
1、在根目录新建文件夹 components ,然后在 components 文件夹下新建组件文件夹 test ;在 test 文件夹右键点击新建Component,输入组件名称 test 之后会生成对应的 4 个文件:test.js、test.json、test.wxml、test.wxss;
1、全局引用:在 app.json 全局配置文件中引入组件,在 window 同级新增下面配置
{
"pages":[],
"winodw":{},
"usingComponents": {
"test":"/components/test/test"
}
}
2、局部引用:在页面的 .json 文件中引入组件
{
"usingComponents": {
"test":"/components/test/test"
}
}
使用:在页面 wxml 文件中直接使用
<test></test>
1、组件的 .json 文件中需要声明 “component”: true;
2、组件的 .js 文件中调用的是 Component({}) 函数;
3、组件的事件处理函数要定义到 methods 节点上;
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 的组件
1、data:组件的私有数据;
2、methods:组件的事件处理函数和自定义方法都放在这里,自定义方法建议下划线开头,与事件处理函数区分开;
3、properties:用来接收外界传递到组件中的数据;
<test min="10"></test>
//data平级
properties: {
//完整格式
min:{
type:Number, //类型
value:10 //默认值
}
//简写
min:Number //类型
}
获取 properties 传递的数据可以直接 this.properties.min
来获取;
跟 vue 的不同,小程序的 data 和 properties 都是可读可写的;在组件里面可以对 properties 传递进来的对象重新赋值,这一点和 vue 里面的 props 是不一样的; data 和 properties的侧重点不一样:data 侧重组件私有数据,properties 侧重外界传递的数据;
data 和 properties 在本质上是一样的东西,只是认为的区分了他们的功能;所以对于 properties 的操作很操作 data 的方式是一样的(读取、修改);
数据监听器用于监听和响应属性和数据的变化,从而执行特定的操作;类似 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 里面的属性发生变化就会触发监听;
纯数字字段:指的是那些不用于界面渲染的 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, // 纯数据字段
})
}
}
})
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() {
// 在组件实例被从页面节点树移除时执行
},
}
})
1、由于组件的某些行为会依赖页面的状态变化,所有需要在组件的 js 文件中监听页面的变化,这种可以被组件访问的生命周期就是 组件所在页面的生命周期;
show:组件所在的页面被展示时执行
hide:组件所在的页面被隐藏时执行
resize:组件所在的页面尺寸变化时执行
2、定义生命周期:必须定义在 pageLifetimes 节点中;
Component({
pageLifetimes: {
show: function() {
// 页面被展示
},
hide: function() {
// 页面被隐藏
},
resize: function(size) {
// 页面尺寸变化
}
}
})
1、单个插槽:跟 vue 里面插槽差不多,也是占位符的作用;小程序中默认每个自定义组件只能使用一个插槽
占位
//组件 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>
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 点击事件中传值整个 data
btn(){
this.triggerEvent("info",this.data)
}
这个流程跟 vue 的子组件传值给父组件其实是差不多的;
3、获取组件实例
父组件可以通过 this.selectComponent() 方法获取子组件的实例,这也可以访问子组件的任何数据和方法;this.selectComponent() 方法接收一个选择器(id、class),不可以使用其他选择器;
//wxml
<test class="test"></test>
//js
onLoad: function (options) {
const testChild = this.selectComponent('.test')
console.log(testChild)
testChild.setData({num:1});//调用子组件的setDate方法修改数据
testChild.test();//调用子组件的方法
}
这样就可以在父组件里面访问子组件的数据、调用子组件的方法(也可以调用子组件的 setData 方法来修改数据);
是小程序中用于实现代码共享的一个方式,类似于 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 有一些限制:
不支持依赖于 node.js 内置库的包(fs、path)
不支持依赖于浏览器内置对象的包(JQ)
不支持依赖于 C++ 插件的包
这是一套开源的小程序组件 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 节点中引入需要的组件,然后就可以全局使用了;
1、在根节点的内部定义一个 css 变量,然后在使用的时候借助 var 来引用;
<style>
html{
--theme:"red"
}
.box{
background:var(--theme)
}
</style>
需要注意的是,定义的变量必须 “–” 开头,它也是有作用域范围的,定义在那个区域下,就只能在这个区域里引用才会生效;
2、css 变量定制Vant 主题
在 app.wxss 文件中,写入变量名,变量名可参考:定制主题;只需要自定义的变量名跟这里需要修改的变量名一致,就可以直接改变原有的样式;
//app.wxss
page{
--button-border-radius:10px;
}
小程序中官方提供的异步 API 都是基于回调函数实现的,这样很容易导致回调地狱,代码的可读性、维护性都很低;
1、主要依赖 miniprogram-api-promise 这个第三方 npm 包
npm i --save miniprogram-api-promise@1.0.4
2、在入口文件(app.js)需要调用一次 promisifyAll() 方法
//app.js
import {promisifyAll} from 'miniprogram-api-promise';
const wxp = wx.p = {};
promisifyAll(wx,wxp);
这里 wxp 存放的就是 Promise 化之后的小程序 API,在使用的时候就可以直接使用 wx.p 来调用 Promise 化的 API 了;
async getInfo(){
const {data:res} = await wx.p.request({
url:"https://www.escook.cn/api/get",
method:"GET",
data:{
name:"sz",
age:20
}
})
console.log(res)
}
也可叫状态管理,解决小程序组件之间数据共享问题;常见的数据共享有: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;
根目录创建文件夹 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 为外部传的参数;
引入 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();
},
})
引入 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.age
sum:'sum'
},
actions:{
updateAge:"updateAge"
}
},
/**
* 组件的方法列表
*/
methods: {
btn(){
this.updateAge()
}
}
})
把完整的小程序项目按需求划分为不同的模块,在构建中打包成不同的子包,用户在使用时可以按需加载;这样的好处是:
优化小程序首次启动的下载时间
多团队共同开发时更好的进行解耦
1、项目构造
主包:包含项目的启动页或者 TabBar 页面,以及一些公共资源
分包:当前分包相关的页面和资源
2、加载规则
小程序启动时默认会下载主包和启动主包内页面(tabBar),在进入某个分包页面时才会下载对应的分包,下载完之后展示;
3、大小限制
整个小程序的主包+所有分包大小不能超过 16 M,主包或单个分包大小不能超过 2 M;如果超过则小程序发包会失败!
在 app.json 文件中通过 subpackages 来声明分包,声明之后会自动创建相应的文件:
//pages平级
"subpackages":[
{
"root": "package1",
"name": "p1",
"pages":[
"pages/dog/dog",
"pages/cat/cat"
]
}
]
root 声明这个分包的名称,name 是分包的别名,pages 是分包里面的页面;生成文件如下:
每个包的大小我们可以在微信开发工具-详情-基本信息里面查看;
1、小程序会按 subpackages 的配置进行分包, subpackages 以外的目录将会打包到主包中;
2、主包也有自己的 pages ,也就是最外层的 pages;
3、tabBar 页面必须在主包里面;
4、分包之间不能相互嵌套;
1、主包内资源可以被分包引用;
2、分包内的资源只能当前分包引用,其他分包和主包无法引用;
本质上也是分包,但是它比较特殊,可以独立于主包和其他分包而单独运行;
默认情况下,用户打开小程序只能从主包进入,然后再去访问分包;独立分包则是用户可以跳过主包,直接进入分包中;一个小程序可以有多个独立分包;
1、定义独立分包
其实跟简单,独立分包跟普通分包在配置上基本一样,唯一区别是在 app.json 配置独立分包的时候需要在这个包里面添加一个配置:independent:true;
"subpackages":[
{
"root": "package1",
"pages":[
"pages/dog/dog",
"pages/cat/cat"
],
"independent": true
}
]
注意:独立分包里面不能引用主包里面的资源;
在进入主页面的时候会触发分包进行预下载行为,这样是为了提升分包的启动速度;在 app.json 里面,使用 preLoadRule 节点定义分包的预下载规则;
"preloadRule": {
"pages/home/home": {
"network": "all",
"packages": ["package1"]
}
},
pages/home/home
是主页面路径,network 是指定预下载网络模式:all、wifi,packages 是配置需要预下载的分包的,可以是分包的 root 或者 name;
注意:同一个分包(主包)中的页面享有共同的预下载大小限额 2M,限额会在工具中打包时校验
可参考:https://developers.weixin.qq.com/miniprogram/dev/framework/ability/custom-tabbar.html
1、在 app.json 中的 tabBar 项指定 custom 字段,同时其余 tabBar 相关配置(list)也补充完整,保证低版本的兼容;
2、添加 tabBar 代码文件,根目录创建固定文件夹 custom-tab-bar ,这个文件夹小程序会自动访问,然后在文件夹下创建一个 index 组件;
3、借助 Vant weapp 来自定义导航组件;
如果没有使用组件库,可以参考小程序官网自定义 tabBar 最后面的一个链接,可以直接打开一个小程序项目;