在 小程序 官网,进入 开发 => 开发设置 => 即可看到 AppID
小程序官方建议把小程序的页面,都存放在 pages 目录
每个页面都有 四个文件
app.json 文件 就是 小程序 的配置文件
app.json 是 当前 小程序的全局配置文件,包含了小程序的所有页面路径、窗口外观、界面表面、底部 tab等
{
// pages 是 用来记录当前小程序所有页面的路径
"pages":[
"pages/index/index",
"pages/logs/logs"
],
// window 全局定义小程序所有页面的背景色、文字颜色等
"window":{
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "Weixin",
"navigationBarTextStyle":"black"
},
// style 全局定义小程序组件所使用的样式版本
"style": "v2",
// sitemapLocation 用来指明 sitemap.json 的存放位置
"sitemapLocation": "sitemap.json"
}
project.config.json 是配置文件,用来记录我们 对小程序开发工具所作的个性化配置,
微信现在已经开放了小程序内搜素,效果类似于 PC 网页 的 SEO ,sitemap.json 文件用来 配置小程序页面是否允许微信索引
当开发者允许微信索引时,微信会通过爬虫的形式,为小程序的页面内容建立索引。当用户的索引关键字和页面的索引匹配成功的时候,小程序的页面将可能展示在搜索结果中
{
"desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
"rules": [{
// action 是否允许 被微信索引,allow 是,disallow 不是
"action": "allow",
// page * 是所有页面 都被 微信索引
"page": "*"
}]
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Iwm8uzkK-1620460593891)(第一章原生微信小程序.assets/image-20210415211439926.png)]
微信小程序 的 调试器 ,会显示一个 黄色的警告,说 什么什么什么 将被索引
如果 不想看到 这个东西
可以在project.config.json 的 setting 中,配置字段 checkSiteMap 为 false
小程序中的每一个页面,可以使用 .json 文件 来对 本页面的窗口外观进行配置,页面中的配置项会覆盖 app.json 的 window 中相同的配置项
只要在 app.json => pages 中 新增页面的存放路径,小程序开发者工具即可帮助我们自动创建对应的页面文件
{
"pages":[
"pages/index/index",
"pages/logs/logs"
# 在这里 继续 添加就可以
],
"window":{
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "Weixin",
"navigationBarTextStyle":"black"
},
"style": "v2",
"sitemapLocation": "sitemap.json"
}
只需要调整 app.json => pages 数组中页面路径的前后顺序,即可修改项目的首页,小程序会把排在第一位的页面,当作首页进行渲染
wxml 是 小程序 框架设计的一套 标签语言,用来构建小程序页面的结果,其作用类似于网页开发中的 html
区别
app.js
是 整个小程序项目的入口文件 调用 App()函数来启动整个小程序
页面的 .js 文件
是 页面的入口文件,通过调用 Page() 函数来创建并运行页面
普通的 .js文件
是普通的功能模块文件,用来封装 公共 的函数或属性 供页面使用
宿主环境 指的是 程序运行所必须的依赖环境
微信 是小程序 的宿主环境,小程序是基于微信进行安装的
小程序的宿主环境 包含的内容?
小程序中 通信的主体 是 渲染层 和 逻辑层
都是由 微信客户端进行转发
小程序 启动过程
页面渲染过程
前四个 是最常用的
view 就是 html 的 div
scroll-view 可滚动的视图区域,经常实现滚动列表效果
swiper 轮播图组件
实现横向布局
flex
必须要给容器 加一个 宽度和高度
<swiper class="swiper-container" indicator-dots>
<swiper-item>
<view>Aview>
swiper-item>
<swiper-item>
<view>Bview>
swiper-item>
<swiper-item>
<view>Cview>
swiper-item>
swiper>
/* 轮播图 */
.swiper-container{
height: 150px;
}
.swiper-container swiper-item {
height: 100%;
line-height: 150px;
text-align: center;
}
.swiper-container swiper-item:nth-child(1) view{
background-color: aqua;
}
.swiper-container swiper-item:nth-child(2) view{
background-color: red;
}
.swiper-container swiper-item:nth-child(3) view{
background-color: green;
}
属性 | 类型 | 默认值 | 说明 |
---|---|---|---|
indicator-dots | boolean | false | 是否显示面板指示点 |
indicator-color | color | rgba() | 指示点的颜色 |
indicator-active-color | color | #000000 | 当前选中的指示点的颜色 |
autoplay | boolean | false | 是否自动切换 |
interval | number | 5000 | 自动切换时间间隔 |
circular | boolean | false | 是否采用衔接滑动 |
text 就是一个 行内元素
rich-text 富文本组件
button
image
navigator
默认 不指定 src 的 图片组件,也占据一定的宽度和高度
mode值 | 说明 |
---|---|
scaleToFill | 默认(缩放模式),不保持纵横比缩放图片,使图片的宽度完全拉甚至填满image元素 |
aspectFit | 缩放模式,保持纵横比缩放图片,使图片的长边能完全显示出来 |
aspectFill | 缩放模式,保持纵横比缩放图片,只保证图片的短边能完全显示出来 |
widthFix | 缩放模式,宽度不变,高度自动变化,保持原图宽高比不变 |
heightFix | 缩放模式,高度不变,宽度自动变化,保持原图宽高不变 |
上传代码 => 提交审核 => 发布
获取小程序码方式
登录小程序管理后台 => 设置 => 基本设置 => 基本信息 => 小程序码及线下物料下载
1、小程序 后台 查看
登录进去后台 点击 统计
2、小程序数据助手
手机微信搜素,小程序数据助手
mustache 语法,用 双大括号 即可
{{要绑定数据的名称}}
Page({
/**
* 页面的初始数据
*/
data: {
info:'hello,world'
},
)}
<view>{{info}}view>
属性绑定 是和 vue 不一样的
Page({
/**
* 页面的初始数据
*/
data: {
info:'hello,world',
randomNum:Math.random()*10 // 生成 十以内的 随机数
},
)}
<view>{{randomNum > = 5 '随机数字大于等于5' : '随机数小于5' }}</view>
{{randomNum * 100}}
事件 是 渲染层到逻辑层的通讯方式,通过事件可以将用户在渲染层产生的行为,反馈到逻辑层进行业务的处理
党事件回调触发的时候,会收到一个 事件对象 event
target 是 触发该事件的源头组件,currentTarget是当前事件所绑定的组件
<view class="outer-view" bindtap="click">
<button>按钮button>
view>
对于 view 而言
e.target 是 button 组件
e.currentTarget 是 view 组件
<button type="primary" bindtap="btnTapHandler">按钮button>
// pages/test/test.js
Page({
/**
* 页面的初始数据
*/
data: {
info:'hello,world'
},
// 定义按钮的事件处理程序
btnTapHandler(e){
console.log(e);
},
)}
data: {
info: 'hello,world',
count: 0,
},
// 定义按钮的事件处理程序
btnTapHandler(e) {
this.setData({
count: this.data.count + 1
})
},
可以为 组件 提供 data-* 自定义 属性传参,其中 * 代表的是参数的名字
<button bindtap="btnHandler" data-info="{{2}}">事件传参button>
最终 info 会被解析为参数的名字
数值2 会被解析为 参数的值
btnHandler(e){
// dataset 是一个对象,包含了所有通过data-* 传递过来的参数项
console.log(e.target.dataset)
// 访问具体的值
console.log(e.target.dataset.info)
}
<input bindinput="inputHandle" class="input1">input>
// 当输入 input 的时候
inputHandle(e){
// e.detail.value 是变化过后,文本框最新的值
console.log(e.detail.value)
},
<view>实现 input 和 data 的数据同步view>
<input bindinput="inputHandle2" class="input1" value="{{msg}}">input>
data: {
info: 'hello,world',
count: 0,
msg: '你好'
},
// 实现 input 和 data 的数据同步
inputHandle2(e) {
this.setData({
msg: e.detail.value
})
},
<view wx:if="{{type === 1}}">男view>
<view wx:elif="{{type === 2}}">女view>
<view wx:else>保密view>
如果要 要一次性控制多个组件的展示与隐藏 , 可以使用 一个 标签将多个组件包装起来,并在 标签上 使用 wx:if 控制属性
<block wx:if="{{true}}">
<view>1view>
<view>2view>
block>
在实际中,block 标签并不会渲染到页面上,他只是起到一个临时包裹的作用
在小程序中,直接使用 hidden="{{控制条件}}" 也能控制 元素的显示与隐藏
<view hidden="{{ condition }}"> 条件 true 隐藏,false 显示view>
<view wx:for="{{arr1}}">
索引时:{{index}} , item 是 {{item}}
view>
<view wx:for="{{arr1}}" wx:for-index="ind" wx:for-item="arritem">
索引时:{{ind}} , item 是 {{arritem}}
view>
<view wx:for="{{arr1}}" wx:for-index="ind" wx:for-item="arritem" wx:key="ind">
索引时:{{ind}} , item 是 {{arritem}}
view>
wx:key 不需要{{ }} ,直接写 就可以
wxss 对 css 进行了 扩充以及修改,以适应微信小程序的开发
rpx 的实现原理 非常简单,鉴于不同设备屏幕的 大小不同,为了实现屏幕的自动适配,rpx 吧 所有设备的屏幕,在宽度上 等分为 750份,即当前屏幕的总宽度为 750rpx
在 iPhone 6 上,屏幕宽度为 375px ,共有 750 个 物理像素,等分为 750rpx
750rpx = 375px = 750 物理像素
官方建议:开发小程序时,设计师可以用 iPhone6 作为 视觉稿的标签
在 iPhone6 状态下, 1rpx=0.5px ,1px = 2rpx
开发举例,在 iPhone 6 上 如果要绘制 宽 100px 高20px,换算成 rpx单位,宽高分别为200rpx和40rpx
使用 wxss 提供的 @import 语法,可以导入外联的样式表
@import 后 跟上 需要 导入的外联样式表的 相对路径 要用 分号 表示结束
定义在 app.wxss 的样式, 叫做 全局样式,他会应用到 这个小程序内 所有的页面
在 页面的 .wxss 文件写的 css 都是 句部样式,只会作用域当前页面
小程序的组成部分: 导航栏区域 背景区域(下拉才会显示) 主题区域
属性名 | 类型 | 默认值 | 说明 |
---|---|---|---|
navigationBarTitleText | String | 字符串 | 导航栏标题文字内容 |
navigationBarBackgroundColor | 16进制颜色 | #000000 | 导航栏背景颜色 |
navigationBarTextStyle | String | white | 导航栏标题颜色,black/white |
backgroundColor | 16进制 | #ffffff | 窗口的背景色 |
backgroundTextStyle | String | dark | 下拉loading的样式 dark/light |
enablePullDownRefresh | boolean | false | 全局是否开启下拉刷新 |
onReachBottomDistance | number | 50 | 页面上拉触底事件触发时页面底部距离,单位px |
"navigationBarBackgroundColor": "#2b4b6b",
"enablePullDownRefresh": true
模拟器并不能100% 还原 手机的效果,具体要以手机预览为准
当我们 快 滚动到 页面最底部的时候,就要加载下一页的数据
"onReachBottomDistance":80
# 默认 时 50 px 如果没有特殊需求,默认是不 修改这个的
移动端常见的效果,用于实现多页面的快速切换
底部 tabbar 顶部 tabbar
"tabBar": {
"list": [{
"pagePath": "pages/test/test",
"text": "text"
},{
"pagePath": "pages/list/list",
"text": "list"
}]
},
# 和 window 是 同一级的
app.json 可以全局配置 小程序 的 每个页面的窗口外观
每个页面的 .json 配置文件,就可以对 特定页面的 窗口外观
页面配置 会 覆盖掉 全局的页面配置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6gjYOFHY-1620460593895)(第一章原生微信小程序.assets/image-20210417080619668.png)]
// 发起 get 请求
getInfo(){
wx.request({
url: 'https://www.escook.cn/api/get',
method:'get',
data:{
name:'zs',
age:20
},
success:(res)=>{
console.log(res);
}
})
},
改变 methods 的值,换成 post 即可
找到 页面 .js 的 onload 钩子函数 就行
我们可以在 详情 本地设置 里面 取消 请求接口的校验
跨域 问题 只存在于基于浏览器的 web 开发中,由于小程序的宿主环境不是浏览器,而是微信客户端,所以小程序中不存在跨域的问题
Ajax 技术的核心 是依赖于 浏览器 的 XMLHttpRequest 这个对象,由于 小程序的 宿主环境 是 微信客户端,所以 小程序中 不能 叫做 发起Ajax 请求,而是叫做 发起网络数据请求
去除 调试器 ,黄色 警告
在 个性化配置.json 文件中, setting 节点下 加入 “checkSiteMap”:false
"window":{
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#2b4b6b",
"navigationBarTitleText": "本地生活",
"navigationBarTextStyle":"white"
},
# app.json
"tabBar": {
"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"
}
]
},
onLoad: function (options) {
this.getSwiperList()
},
// 获取轮播图数据
getSwiperList(){
wx.request({
url: 'https://www.escook.cn/slides',
success:(res)=>{
console.log(res);
this.setData({
swiper:res.data
})
}
})
},
声明式,在页面上声明一个 navigator 标签
编程式导航 , 调用小程序的导航 API ,实现页面跳转
在使用 navigator 组件跳转到指定的 tabBar 页面时,需要指定 url 属性 和 open-type 属性
<navigator url="/pages/message/message" open-type="switchTab">点击跳转navigator>
<navigator url="/pages/info/info" open-type="navigate">点击跳转到非tabBar页面navigator>
如果我们需要后退到上一页面,或者是 多级页面,则需要指定 open-type 属性 和 delta 属性,其中
open-type 的值 必须是 navigateBack ,表示 后退
delta 的值 必须是 数字,表示后退的层级
<navigator open-type="navigateBack" delta="1">后退一级navigator>
如果我们只需要 后退一个页面,则可以省略 delta
<button bindtap="gotoMessage">惦记我跳转到 message 页面button>
// gotoMessage
gotoMessage(){
wx.switchTab({
url: '/pages/message/message',
})
},
wx.navigateTo
wx.navigateBack
可以在 url 后谜案 拼接参数
<navigator url="/pages/info/info?name=zs&age=20" open-type="navigate">点击跳转到非tabBar页面 传参navigator>
wx.navigateTo
和上面一样,也可以 拼接
<navigator url="/pages/info/info?name=zs&age=20" open-type="navigate">点击跳转到非tabBar页面 传参navigator>
onLoad: function (options) {
console.log(options);//{name: "zs", age: "20"}
},
下拉刷新是移动端的专有名词,指的是通过手指在屏幕上的下拉滑动操作,从而 从新加载页面数据 的 行为
实际开发中,推荐使用第二种,并不是所有页面都需要下拉刷新
{
"usingComponents": {},
"enablePullDownRefresh": true,
"backgroundColor": "#efefef",
"backgroundTextStyle": "dark"
}
我们可以通过 页面的 .js 文件中的 onPullDownRefresh 事件,监听 下拉刷新的操作
当我们处理完 下拉刷新后,下拉刷新的 loading 效果 会一直显示,不会主动消失,所以 需要我们 手动处理,在后面调用 wx.stopPullDownRefresh() 就可以了
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
console.log('出发了下拉刷新');
// 停止下拉刷新
wx.stopPullDownRefresh()
},
上拉触底,是移动端专有名词,通过手指在屏幕上的上拉滑动操作,从而 加载更多数据 的行为
在移动端,分页 太丑了, 所以 采用 上拉触底进行 分页
前提是 页面的高度,足够长
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
console.log('上拉触底');
},
这个时候一般 用 节流 处理 一下
默认是 50,也可以在 页面 .json 自定义配置
"onReachBottomDistance":150
在联系我们页面实现这个案例效果
/**
* 页面的初始数据
*/
data: {
colorList: [],
},
// 获取颜色
getColors() {
wx.request({
url: 'https://www.escook.cn/api/color',
success: ({
data: res
}) => {
console.log(res);
this.setData({
colorList:[...this.data.colorList,...res.data]
})
}
})
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
this.getColors()
},
<view wx:for="{{colorList}}" wx:key="index" class="num-item" style="background-color:rgba({{item}})"></view>
.num-item{
border: 1px solid #efefef;
border-radius: 8rpx;
line-height: 200rpx;
margin: 15px;
text-align: center;
text-shadow: 0rpx 0rpx 5rpx #fff;
box-shadow: 1rpx 1rpx 6rpx #aaa;
height: 200rpx;
}
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
this.getColors()
},
// 获取颜色
getColors() {
wx.showLoading({
title: '数据加载中...',
})
wx.request({
url: 'https://www.escook.cn/api/color',
success: ({
data: res
}) => {
console.log(res);
this.setData({
colorList:[...this.data.colorList,...res.data]
})
},
// 因为我们要不论 是 失败还是成功 都要隐藏 loading
complete:()=>{
wx.hideLoading()
}
})
},
在加载中,如果触底,也会 发起请求,这样太浪费性能了
当我们 编写 信息 页 的时候,编译后,是先进入首页,然后 我们在点击 进入 信息页
我们可以 设置 编译 旁边的 编译模式
创建 => 运行 => 销毁 =>
是由小程序框架的内置函数,会伴随着生命周期 自动按次序执行
生命周期函数的作用:允许程序员在 特点的时间点,执行某些特定的操作。
App({
// 小程序初始化完成时,执行此函数,全局只会触发一次,可以做一些初始化的工作
onLaunch:function(options){
// 一般对 小程序的 本地数据 进行操作
},
// 小程序启动,或者 从 后台进入前台显示 的时候触发
onShow:function(options){},
// 小程序从前台进入后台
onHide:function(){}
})
Page({
onLoad:function(options){
// 监听页面加载,一个页面只调用一次
// 用的最多
},
onShow:function(){
// 监听页面显示
},
onReady:function(){
// 页面初次渲染完成,一个页面只调用一次
// // 用的最多
},
onHide:function(){
// 页面隐藏
},
onUnload:function(){
// 页面卸载,一个页面只调用一次
}
})
wxml 中无法调用在页面的 .js 中定义的函数,但是 wxml 中可以调用 wxs 中定义的函数。因此,小程序中 wxs 的典型应用场景,就是 “过滤器”
# 1、wxs 有自己的数据类型
# number 数值类型,string 字符串类型、布尔类型、object 对象类型、function 函数类型、array 数组类型、date 日期类型、regexp 正则
# 2、wxs 不支持类似于 es6 及 以上的语法形式
# 不支持:let const 解构赋值 展开运算符 箭头函数 对象属性简写等等
# 支持:var 普通 function 函数等 es5
# 3、wxs 遵循 CommonJS 规范
# module 对象
# require() 函数
# module.exports 对象
wxs 代码可以编写在 wxml 文件中的 标签内,就像 js 代码 可以编写在 html 文件中的 标签内一样
wxml 文件中 的每个 标签,必须提供 module 属性,用来指定当前 wxs 的模块名称,方便再 wxml 中 访问模块中的成员
<view>{{m1.toUpper(username)}}view>
<wxs module="m1">
module.exports.toUpper = function(str){
return str.toUpperCase()
}
wxs>
wxs 代码还可以编写再 以 .wxs为后缀名的文件内 , 就像 JavaScript代码可以编写再 以 .js 后缀名的文件中一样
# 一般定义再 utils 文件夹内
// tools.wxs 文件
function toLower(str){
return str.toLowerCase()
}
module.exports = {
toLower:toLower
}
<view>{{m2.toLower(str)}}</view>
<wxs module="m2" src="../../utils/tool.wxs"></wxs>
# 1、必须 用 module 执行 模块的名称
# 2、src 用来指定 要引入的脚本的路径,且必须是 相对路径
<view class="grid-list">
<navigator class="grid-item" wx:for="{{gridList}}" wx:key="id" url="/pages/shoplist/shoplist?id={{item.id}}&title={{item.name}}">
<image src="{{item.icon}}">image>
<text>{{item.name}}text>
navigator>
view>
# shoplist
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
this.setData({
query: options
})
this.getShopList()
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
wx.setNavigationBarTitle({
title: this.data.query.title
})
},
wx.request({
url: `https://www.escook.cn/categories/${this.data.query.id}/shops`,
method: 'GET',
data: {
_page: this.data.page,
_limit: this.data.pageSize
},
success: (res) => {
this.setData({
shopList: [...this.data.shopList, ...res.data],
total: res.header['X-Total-Count'] - 0
})
},
complete: () => {
// 隐藏 loading 效果
wx.hideLoading()
this.setData({ isloading: false })
// wx.stopPullDownRefresh()
// cb && cb()
}
})
"onReachBottomDistance": 200,
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
if (this.data.page * this.data.pageSize >= this.data.total) {
// 证明没有下一页的数据了
return wx.showToast({
title: '数据加载完毕!',
icon: 'none'
})
}
// 判断是否正在加载其他数据
if (this.data.isloading) return
// 页码值 +1
this.setData({
page: this.data.page + 1
})
// 获取下一页数据
this.getShopList()
},
定义一个节流阀
关闭下拉刷新 是一个 有可能需要的,有可能不需要的,我们可以给他传递一个回调函数,如果传递了这个函数,那就调用,没有就不调用
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
// 需要重置关键的数据
this.setData({
page: 1,
shopList: [],
total: 0
})
// 重新发起数据请求
this.getShopList(() => {
wx.stopPullDownRefresh()
})
},
const formatNumber = n => {
n = n.toString()
return n[1] ? n : '0' + n
}
module.exports = {
formatTime: formatTime
}
<view class="info">
<text class="shop-title">{{item.name}}text>
<text>电话:{{tools.splitPhone(item.phone)}}text>
<text>地址:{{item.address}}text>
<text>营业时间:{{item.businessHours}}text>
view>
view>
<wxs src="../../utils/tools.wxs" module="tools">wxs>
用 mp-04 项目
每个组件要放到单独的文件夹内,以便区分
// 要在那个页面 引用自定义组件,就在那个 页面的 .json 文件中写
{
"usingComponents": {
"my-test1":"/components/test1/test1"
}
}
<!-- 使用 局部自定义组件 -->
<my-test1></my-test1>
在 app.json 全局配置文件中 全局引用组件
{
"pages": [
"pages/home/home",
"pages/message/message",
"pages/contact/contact"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#13A7A0",
"navigationBarTitleText": "WeChat",
"navigationBarTextStyle": "black"
},
"usingComponents":{
"my-test2":"/components/test2/test2"
}
}
<my-test2>my-test2>
在组件和引用组件的页面中,建议使用class选择器,不要使用ID、属性、标签选择器
默认情况下,自定义组件的样式 隔离特性 能够 防止组件内外样式互相干扰的问题,但有时候,我们希望外界能够控制组件内容的样式,可以这样设置
# 在组件的 .js 文件中 新增如何配置
Component({
options:{
"styleIsolation":'isolated'
}
})
# 或者 在 组件 的 .json 文件中 新增如何配置
{
"styleIsolation":'isolated'
}
可选值 | 默认值 | 描述 |
---|---|---|
isolated | 是 | 启用样式隔离,外面的class样式不会影响组件 |
apply-shared | 否 | 页面 wxss样式将会影响到自定义组件,但自定义组件wxss 不会影响页面 |
shared | 否 | 页面 wxss 样式 可以影响 自定义组件,自定义组件 的wxss 也会影响页面 和 其他 设置 了 apply-shared 或者 shared 的 自定义组件 |
/**
* 组件的初始数据
*/
data: {
count:0
},
/**
* 组件的初始数据
*/
data: {
count: 0
},
/**
* 组件的方法列表
*/
methods: {
// 定义 自定义 组件的方法
addCount() {
this.setData({
count: this.data.count + 1
})
this._showCount()
},
// 自定义方法 建议用 _ 开头
_showCount() {
wx.showToast({
title: 'count是' + this.data.count,
icon: 'none'
})
}
}
<view>count的值是:{{count}}view>
<button bindtap="addCount">点我+1button>
在小程序组件中,properties 是组件的对外属性,用来接收外界传递到组件中的数据
Component({
/**
* 组件的属性列表
*/
properties: {
max: {
type: Number, // 定义父组件传递过来的参数类型必须是 数字
value: 10 // 定义默认值
}
// 简化的声明方式 就是 max:Number
},
)}
<my-test1 max="10"></my-test1>
if (this.data.count >= this.properties.max) return
在小程序的组件中,properties 属性 和 data 数据的用法相同,他们都是可读可写的,只不过
由于data 数据 和 properties 属性 在本质上没有任何区别,因此 properties 属性的值,也可以用于页面渲染,或者使用 setData 为 properties 中的属性 重新赋值
Component({
properties:{
max:Number
},
methods:{
addCount(){
this.setData({ max: this.properties.max +1 })
// 使用 setData 修改 properties
}
}
})
数据监听器 用于 监听和响应任何属性和数据字段的变化,从而执行特定的操作 它的作用类似于 vue 中的 watch 侦听器。在小程序组件中,数据监听器的基本语法如下
Component({
observers:{
'字段A,字段B':function(字段A的新值,字段B的新值){
// 操作
}
}
})
<view>{{n1}} +{{n2}} = {{sum}}view>
<button size="mini" bindtap="addN1">n1自增button>
<button size="mini" bindtap="addN2">n2自增button>
// components/test1/test1.js
Component({
/**
* 组件的初始数据
*/
data: {
count: 0,
n1:0,
n2:0,
sum:0
},
/**
* 组件的方法列表
*/
methods: {
// n1 自增
addN1(){this.setData({n1:this.data.n1+1})},
addN2(){this.setData({n2:this.data.n2+1})},
},
observers:{
'n1,n2':function(n1,n2){
this.setData({sum:n1+n2})
}
}
})
Component({
observers:{
'对象.属性A,对象.属性B':function(属性A的新值,属性B的新值){
// 触发此监听器的 3 种 情况
// 为属性A赋值 使用 setData 设置 this.data.对象.属性A 的时候触发
// 为属性B赋值 使用 setData 设置 this.data.对象.属性B 的时候触发
// 直接为对象赋值 使用 setData 设置 this.data.对象 的时候触发
// do something
}
}
})
有个三个按钮,分别是 R、G、B,还有 一个 view 设置它的背景颜色,背景颜色 为 我们设置的 RGB ,当我们点击 RGB 的时候,他会有 自增加 的效果
新建 test3 自定义组件
methods: {
changeR() {
this.setData({
'rgb.r': this.data.rgb.r + 5 > 255 ? 255 : this.data.rgb.r + 5
})
},
}
observers: {
'rgb.r,rgb.g,rgb.b': function (r, g, b) {
this.setData({
fullColor: `${r},${g},${b}`
})
}
}
如果某个对象种 需要被监听的 属性太多,为了方便,可以使用 通配符 ** 来监听对象种所有属性的变化
observers: {
// 'rgb.r,rgb.g,rgb.b': function (r, g, b) {
// this.setData({
// fullColor: `${r},${g},${b}`
// })
// }
// 监听 rgb 对象的 所有属性的变化
'rgb.**':function(obj){
console.log(obj);//{r: 0, g: 0, b: 5}
this.setData({
fullColor:`${obj.r},${obj.g},${obj.b}`
})
}
}
不用于页面 渲染 的 data 字段
某些 data 种的字段,既不会展示在页面上,也不会传递给其他组件,仅仅在当前组件内部使用,带有这种特性的 data 字段适合被设置为纯数据字段
好处 有利于提升页面更新的性能
Component({
options:{
// 指定所有 _ 开头的数据字段为纯数据字段
pureDataPattern: /^_/
},
data:{
a:true, // 普通数据字段
_b:true // 纯 数据字段
}
})
_rgb
生命周期函数 | 参数 | 描述说明 |
---|---|---|
created | 无 | 在组件实例刚刚被创建时执行 |
attached | 无 | 在组件实例进入页面节点树的时候执行 |
ready | 无 | 在组件在视图层布局完成后执行 |
moved | 无 | 在组件实例被移动到节点树另一个位置的时候执行 |
detached | 无 | 在组件实例被页面节点树移除的时候执行 |
error | object error | 每当组件方法抛出错误时执行 |
created、attached、detached
attached 是 刚刚被放到 组件U I 结构中,还没有渲染
渲染完成以后会 执行 ready
1、组件实例 刚刚 被创建好的时候,created 生命周期函数会触发
此时不能调用 setData
通常在这个生命周期函数中,只应该用于给 组件的 this 添加一些自定义的属性字段
2、在组件 完全初始化完毕,进入页面节点树后,attached 生命周期函数触发
此时 this.data 已经被 初始化完毕
一般 都是在 这个生命周期 函数 中 请求数据
3、在组件 里开页面节点树以后,detached 生命周期函数会触发
退出一个页面时,会触发页面内每个自定义组件的 detached 生命周期函数
此时适合做一些清理性质的工作
再新版的小程序中,自定义组件的生命周期 会 放在这个节点下
Component({
// 推荐写法
lifetimes:{
attached(){},// 组件实例进入页面节点,初始化完成
detached(){},// 组件实例被从页面节点树 移除 时执行
}
})
有时候 自定义 组件的行为 依赖于 页面状态的变化,此时需要用到组件所在页面的生命周期
例如: 每当 触发 页面的 show 生命周期函数的 时候,我们希望重新 生成一个新的 随机的 RGB 颜色值
生命周期函数 | 参数 | 说明 |
---|---|---|
show | 无 | 组件所在的页面被展示的时候执行 |
hide | 无 | 组件所在的页面被隐藏的时候执行 |
resize | Object Size | 组件所在的页面尺寸变化的时候执行 |
Component({
pageLifetimes: {
show: function () {}, // 页面展示
hide: function () {}, // 页面隐藏
resize: function (size) {} // 页面尺寸发生变化
},
// components/test3/test3.js
Component({
pageLifetimes: {
show: function () {
this._randomColor()
}, // 页面展示
hide: function () {}, // 页面隐藏
resize: function (size) {} // 页面尺寸发生变化
},
/**
* 组件的属性列表
*/
properties: {
},
/**
* 组件的初始数据
*/
data: {
rgb: {
r: 0,
g: 0,
b: 0
},
fullColor: '0,0,0'
},
/**
* 组件的方法列表
*/
methods: {
_randomColor(){
this.setData({
rgb:{
r:Math.floor(Math.random()*256),
g:Math.floor(Math.random()*256),
b:Math.floor(Math.random()*256),
}
})
},
changeR() {
this.setData({
'rgb.r': this.data.rgb.r + 5 > 255 ? 255 : this.data.rgb.r + 5
})
},
changeG() {
this.setData({
'rgb.g': this.data.rgb.g + 5 > 255 ? 255 : this.data.rgb.g + 5
})
},
changeB() {
this.setData({
'rgb.b': this.data.rgb.b + 5 > 255 ? 255 : this.data.rgb.b + 5
})
}
},
observers: {
// 'rgb.r,rgb.g,rgb.b': function (r, g, b) {
// this.setData({
// fullColor: `${r},${g},${b}`
// })
// }
// 监听 rgb 对象的 所有属性的变化
'rgb.**': function (obj) {
console.log(obj);
this.setData({
fullColor: `${obj.r},${obj.g},${obj.b}`
})
}
}
})
再自定义组件的 wxml 结构中 可以提供 一个 节点
就是一个占位符
自定义 组件的 wxml
<slot>slot>
// 使用者
<my-test2>
<view>内不</view>
</my-test2>
Component({
options:{
multipleSlots:true,// 启用多个插槽
pureDataPattern:/^_/,// 纯数据字段
},
)}
自定义组件
<slot name="before">slot>
<view>固定文本view>
<slot name="after">slot>
引用自定义组件
<my-test2>
<view slot="before">第一个view>
<view slot="after">第22个view>
my-test2>
父组件可以 使用 this.selectComponent() 获取子组件实例对象
这样就可以访问子组件的任意数据和方法
// 父组件的 data 节点
data:{
count:0
}
// 父组件的 结构
<my-test3 count="{{count}}"></my-test3>
Component({
/**
* 组件的属性列表
*/
properties: {
count:Number
},
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
}
})
<view> {{count}} </view>
countAdd(){
this.setData({
count:this.properties.count + 1
})
}
子向父
1.在 父组件 的 .js 种,定义一个函数,这个函数 即将通过自定义事件的形式,传递给子组件
2.在 父组件 的 wxml 中 通过自定义事件的形式,将步骤1 中定义的函数引用,传递给子组件
3.在 子组件 的 js 中,通过调用 this.triggerEvent(‘自定义事件名称’,{/*参数对象 */}) 将数据发送到父组件
4.在父组件的 js 中 通过 e.detail 获取到子组件传递过来的数据
第一步
# 父组件的 .js
syncCount(){
console.log()
}
第二步
父组件的 wxml
<my-test4 count="{{count}}" bind:sync="syncCount">my-test4>
<my-test4 count="{{count}}" bindsync="syncCount">my-test4>
第三步
# 子组件的 .js
countAdd(){
this.setData({
count:this.properties.count + 1
})
// 触发自定义事件,数值给父组件
this.triggerEvent('sync',{value:this.properties.count})
}
第四步
# 父组件
syncCount(e){
console.log(e.detail.value)
},
可在父组件里 调用 this.selectComponent(“id或者class选择器”),获取子组件的实例对象,从而直接访问子组件的任意数据和方法。
注意 调用的时候 需要传入一个选择器
<my-test4 count="{{count}}" bind:sync="syncCount" class="customA" id="cA">my-test4>
<button bindtap="getChild">button>
getchild(){
const child = this.selectComponent(".customA")
child.setData({ count : child.properties.count + 1 })
child.addCount() 调用子组件的 addCount 方法
}
是小程序中,用于实现组件间代码共享的特性,类似于 vue.js 的 mixins
每个behavior可以包含以组属性、数据、生命周期和方法,当组件引用它的时候,它的属性、数据、和方法会被合并到组件中
调用 Behavior() 就可以了
module.exports = Behavior({
// 属性节点
properties:{},
// 私有数据节点
data:{username:'zs'},
// 事件处理函数
methods:{},
// 其他节点
})
在组件中,使用 require() 方法 导入 需要的 behavior ,挂在后即可访问 behavior 中的数据和方法
// components/test4/test4.js
const myBehavior = require('../../behaviors/my-behavior')
Component({
// 挂在 混入
behaviors: [myBehavior],
/**
* 组件的属性列表
*/
)}
最常用的是 properties data methods behaviors
可用的节点 | 类型 | 是否必填 | 描述 |
---|---|---|---|
properties | Object Map | 否 | 同组件的属性 |
data | Object | 否 | 同组件的数据 |
methods | Object | 否 | 同自定义组件的方法 |
behaviors | String Array | 否 | 引入其他的 behavior |
created | |||
attached | |||
ready | |||
moved | |||
detached |
导入 mp_05
vant weapp 是 有赞前端团队 开源 的一套 小程序 UI 组件库,助力于开发者快速搭建小程序应用
npm init -y
教程
看文档
:root {
--main-bg-color:brown;
}
element {
background-color: var(--main-bg-color);
}
/* 在 app.wxss中 */
page {
/* 定制 */
--button-danger-background-color:#c0000;
--button-danger-border-color:#d6000;
}
为什么使用 page 节点?
因为有 作用域,小程序的 根节点 就是 page
怎么知道的 代码 这么写 ?
主题颜色配置文件
默认情况下,小程序的 官方 提供的 异步 api 都是基于回调函数 实现的
代码的可读性、维护性差
api 的 promise 化 ,指的是 通过 额外的配置,将官方提供的、基于回调函数的异步 api,升级 改造为 基于 promise 的异步 api,从而提高代码的可读性、维护性,避免回调地狱的问题
npm install --save [email protected]
每次 npm 安装完成 建议 都要 删除 miniprogram_npm 这个文件夹,重新构建 npm 才可以使用
//app.js
import {
promisifyAll
} from 'miniprogram-api-promise'
const wxp = wx.p = {}
promisifyAll(wx, wxp)
<vant-button type="danger" bindtap="getInfo">var按钮</vant-button>
# 在页面的 .js 文件中,定义 tap 事件处理程序
async getInfo(){
const { data:res } = await wx.p.request({
method:'get',
url:'https://www.escook.cn/api/get',
data:{name:'zs',age:20}
})
console.log(res)
}
又叫做 状态 管理,是 为了解决组件之间数据共享 的问题
常见的全局数据共享方案有 vuex redux mobX
可以使用 mobx-miniprogram 配和 mobx-miniprogram-bindings 实现全局数据共享
mobx-miniprogram 用来 创建 store 实例对象
mobx-miniprogram-bindings 用来把 Store 中的共享 数据 或者方法,绑定到组件或者 页面中 使用
npm install --save [email protected] [email protected]
别忘了 删了文件,重新构建 npm
在跟目录下,创建 store 文件夹,创建 store.js 文件
// 这个 文件,专门用来创建 store 的实例对象
import {
observable
} from 'mobx-miniprogram'
export const store = observable({
// 数据字段
numA: 1,
numB: 2
})
// 这个 文件,专门用来创建 store 的实例对象
import {
observable
} from 'mobx-miniprogram'
export const store = observable({
// 数据字段
numA: 1,
numB: 2,
// 计算属性
// get 表示 只读,不能被赋值
get sum(){
return this.numA + this.numB
}
})
// 这个 文件,专门用来创建 store 的实例对象
import {
observable,
action
} from 'mobx-miniprogram'
export const store = observable({
// 数据字段
numA: 1,
numB: 2,
// 计算属性
// get 表示 只读,不能被赋值
get sum() {
return this.numA + this.numB
},
// actions 用来 修改 store 中的数据
updateNum1: action(function (step) {
this.numA = this.numA + step
}),
updateNum2: action(function (step) {
this.numB = this.numB + step
}),
})
// pages/message/message.js
import {
createStoreBindings
} from 'mobx-miniprogram-bindings'
import {
store
} from '../../store/store'
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
this.storeBindings = createStoreBindings(this, {
// this 表示的 要 把 store 挂在到那个页面上
// fields 表示要把那些属性 绑定到 页面上
// actions 表示要把那些 修改 store 属性的方法 绑定到页面上
store,
fields: ['numA','numB','sum'],
actions: ['updateNum1']
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
this.storeBindings.detroyStoreBindings()
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})
btnHdandler1(e){
this.updateNum1(e.target.dataset.step)
},
btnHdandler2(e){
this.updateNum1(e.target.dataset.step)
},
<view>{{numA}} +{{numB}} = {{sum}}view>
<button bindtap="btnHdandler1" data-step="{{1}}">+1button>
<button bindtap="btnHdandler2" data-step="{{-1}}">-1button>
// components/numbers/numbers.js
import {
storeBindingsBehavior
} from 'mobx-miniprogram-bindings'
import {
store
} from '../../store/store'
Component({
// 通过 storeBindingsBehavior 来实现自动绑定
behaviors: [storeBindingsBehavior],
storeBindings: {
store, // 指定要绑定的 store
fields: {
// 指定要绑定的字段数据
numA: () => store.numA, // 绑定字段的 第一种方式
numB: (store) => store.numB, // 绑定字段的 第二种方式
sum: 'sum' // 绑定字段的第三种方式 也是最常用的
},
actions: {
// 指定要绑定的方法
updateNum2: 'updateNum2'
}
},
/**
* 组件的属性列表
*/
properties: {
},
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
}
})
<view>{{numA}} +{{numB}} = {{sum}}view>
<button bindtap="btnHdandler1" data-step="{{1}}">+1button>
<button bindtap="btnHdandler2" data-step="{{-1}}">-1button>
methods: {
btnHdandler1(e) {
this.updateNum2(e.target.dataset.step)
},
btnHdandler2(e) {
this.updateNum2(e.target.dataset.step)
},
}
分包是 把一个完整的小程序项目,按照需求 划分为 不同的子包,在构建的时候打包成不同的分包,用户在使用的时候 按需加载
前 只有一个包
后 主包 一般 只包含当前 项目的 启动页面 和 tabbar 页面,以及所有分包都需要的一些公共资源
分包: 只包含和当前分包有关的页面和私有资源
1.小程序启动的时候,默认会下载主包 并 启动 主包 内页面
tabbar 页面 必须放到主包种
2.当用户进入 分包 某个页面的时候,客户端会把对应的分包下载下来,下载完成后在进行展示
非 tabBar 页面可以按照功能的不同,划分为不同的分包之后,按需下载
|——app.js
|——app.json
|——apo.wxss
|——pages // 主包的所有页面
|——index
|——logs
|——packageA //第一个包
|——pages // 第一个分包的所有页面
|——cat
|——dog
|——packageB //第2个包
|——pages // 第2个分包的所有页面
|——apple
|——banana
|——utils
# app.json subpackages 节点中声明分包的结构
{
"pages":[
// 主包的所有页面
],
"subpackages":[
// 它通过 subpackages 节点,声明分包的结构
{
"root":"packageA",// 第一个分包的根目录
"pages":[
// 当前分包下,页面的相对存放路径
"pages/cat/cat",
"pages/dog/dog"
]
},
{
"root":"packageB",// 第2个分包的根目录
"name":"pack2",// 分包的别名
"pages":[
// 当前分包下,页面的相对存放路径
"pages/apple/apple",
"pages/banana/banana"
]
}
]
}
在 root 下面 加一个 name 属性即可
查看分包的体积: 详情=> 基本信息 =>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3pOBXbW7-1620460593899)(第一章原生微信小程序.assets/image-20210418161428783.png)]
1.主包 无法引用 分包内的私有资源
2.分包之间 不能相互引用 私有资源
3.分包 可以引用 主包的公共资源
独立分包本质上也是分包,只不过他比较特殊,可以独立于主包和其他分包而单独运行
独立分包不依赖于 主包
普通分包 依赖于 主包
开发者可以按需,将某些具有一定功能独立型的页面 配置到 独立分包中,原因如下
一个小程序可以有多个独立分包
|——app.js
|——app.json
|——apo.wxss
|——pages // 主包的所有页面
|——index
|——logs
|——moduleA //第一个独立分包
|——pages // 第一个独立分包的所有页面
|——cat
|——dog
|——moduleB //第2个 独立分包
|——pages // 第2个独立分包的所有页面
|——apple
|——banana
|——utils
# app.json subpackages 节点中声明分包的结构
{
"pages":[
// 主包的所有页面
],
"subpackages":[
// 它通过 subpackages 节点,声明分包的结构
{
"root":"moduleA",// 第一个独立分包的根目录
"independent":true, // 声明当前 moduleA为独立分包
"pages":[
// 当前分包下,页面的相对存放路径
"pages/cat/cat",
"pages/dog/dog"
]
},
{
"root":"packageB",// 第2个分包的根目录
"name":"pack2",// 分包的别名
"pages":[
// 当前分包下,页面的相对存放路径
"pages/apple/apple",
"pages/banana/banana"
]
}
]
}
独立分包和普通分包以及主包之间,是相互隔绝的,不能相互引用彼此资源
有进入小程序的某个页面时,由框架自动预下载可能需要的分包,从而提升进入后续分包页面的启动速度
# app.json preloadRule
{
"preloadRule":{
// 分包 预下载的规则
"pages/contact/contact":{
// 触发 分包 预下载 的页面路径
// networ 表示再 指定的网络模式下进行预下载
// 可选值为 all wifi ,默认值 wifi
"network":"all",
// packages 表示 进入页面后,预下载那些分包
// 可以 通过 root 或者 name 指定预下载那些分包
"packages":["pkgA"]
}
}
}
同一分包中的页面 享有 共同的预下载大小限额是 2M
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wll4SooA-1620460593901)(第一章原生微信小程序.assets/image-20210418163813369.png)]
参考文档
1、在 app.json 中的 tabBar 项指定 custom 字段,同时其余 tabBar 相关配置也补充完整。所有 tab 页的 json 里需声明 usingComponents 项,也可以在 app.json 全局开启。
2、在根目录中,新建 custom-tab-bar 文件夹
新建 index 自定义组件,注意 直接 新建就行,不用再新建文件夹了
1、注册组件
# app.json 或者 index.json
"usingComponents": {
"van-tabbar": "@vant/weapp/tabbar/index",
"van-tabbar-item": "@vant/weapp/tabbar-item/index"
}
2、拷贝 js wxml
看文档
https://vant-contrib.gitee.io/vant-weapp/#/custom-style
再消息 item 里面定义即可
如何监听 sum 可以利用 sum
https://developers.weixin.qq.com/miniprogram/dev/extended/utils/mobx.html
把 active 放在 store 里面