微信小程序入门学习
简介
微信小程序官网开发文档
微信小程序是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播,同时具有出色的使用体验
其他小程序
- 支付宝小程序
- 百度小程序
- qq小程序
- 今日头条+抖音小程序
环境准备
第一个微信小程序
打开微信开发者工具
第一次登录微信开发者工具是需要微信扫码登录的
新建小程序项目
选择测试账号进行小程序的开发
到此,微信小程序创建成功
获取微信小程序昵称成功!
修改appid
在项目的文件目录中找到“project.config.json”这个文件,点击打开,在右侧的代码区域里面找到“appid”,直接修改值,修改成功之后,保存,appid就修改好了
微信开发者工具的介绍
小程序的调试
页面样式的调试
页面调试成功,按ctrl+S自动保存编译,模拟器展示调试后的样式与效果
预览
清除缓存
清除本地的缓存和用户数据
修改微信小程序的信息
点击详情按钮,
查看微信小程序在不同的机型下的显示
查看网络状态
新建文件和文件夹
小程序的结构目录
结构 | 传统web | 微信小程序 |
---|---|---|
结构 | HTML | WXML |
样式 | CSS | WXSS |
逻辑 | Javascript | Javascript |
配置 | 无 | JSON |
通过对比得出,传统web是三层结构,而微信小程序是四层结构,多了一层配置.json
基本的项目目录
小程序配置文件
一个小程序应用程序会包括最基本的两种配置文件。一种是全局的app.json和页面自己的page.json
全局配置app.json
官网介绍的全局配置
pages:——用于描述当前小程序所有页面路径,让微信客户端知道当前你的小程序页面定义在哪个目录
window:——定义小程序所有页面的顶部背景颜色,文字颜色等
{
"pages": [
"pages/index/index",//表示当前页面定义在pages/index的目录下,表示在小程序中是启动页面
"pages/logs/logs"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "WeChat",
"navigationBarTextStyle": "black"
},
"style": "v2",
"sitemapLocation": "sitemap.json"
}
window的详细介绍
创建文件的快捷方式
(以下方式只在微信开发者工具中有效)
新增页面提示iPage “pages/main/main” has not been registered yet.
在main.js中输入Page,创建Page构造函数
tabbar的配置
tabbar:标签页
如果小程序是一个多 tab 应用(客户端窗口的底部或顶部有 tab 栏可以切换页面),可以通过 tabBar 配置项指定 tab 栏的表现,以及 tab 切换时显示的对应页面。
在小程序目录下app.json中配置
tabBar官网的介绍
tabBar和pages是同级
app.json中配置如下
{
"pages":[
"pages/test/test",
"pages/index/index",
"pages/logs/logs"
],
"window":{
"backgroundTextStyle":"dark",
"navigationBarBackgroundColor": "#0094ff",
"navigationBarTitleText": "我的应用",
"navigationBarTextStyle":"white",
"enablePullDownRefresh":true,
"backgroundColor":"#FFFF00"
},
"tabBar":{
"list":[
{
"pagePath":"pages/index/index",
"iconPath":"images/icon_consult.png",
"text":"首页"
},
{
"pagePath":"pages/test/test",
"iconPath":"images/icon_invest.png",
"text":"项目管理"
}
]
},
"style": "v2",
"sitemapLocation": "sitemap.json"
}
效果如下:
官网介绍
页面配置
页面配置中只能设置 app.json 中 window 对应的配置项,以决定本页面的窗口表现,所以无需写 window 这个属性
官网介绍
修改导航栏标题navigationBarTitleText
效果如下:
sitemap 的配置
官网介绍
view和text的简介
text标签
- 文本标签
- 只能嵌套text
- 长按文字可以复制,只有该标签有这个功能
- 可以对空格回车进行编码
数据绑定
在页面.js文件Page函数的data属性里面定义属性和属性值
Page({
// 页面中显示的数据
data:{
msg:'Hello WiXIN',
num:1000,
flag:true,
person:{
age:73,
height:145,
weight:160,
name:'富婆'
},
isChecked:true
}
})
在页面中使用{{属性名}}获取属性值
<!--pages/main/main.wxml-->
<!-- 字符串 -->
<view>{{msg}}</view>
<!-- 数字类型 -->
<view>{{num+1}}</view>
<!-- boolean类型 -->
<view>{{flag}}</view>
<!-- 对象类型 -->
<view>{{person.name}}</view>
<view>{{person.height}}</view>
<view>{{person.weight}}</view>
<view>{{person.age}}</view>
<!-- 在标签的属性中使用 -->
<view data-num="{{person.age}}">12</view>
<!-- 使用boolean类型充当属性,checked.
字符串和花括号之间一定不要存在空格,否则会导致识别失败
以下的写法就是错误的示范
<checkbox checked=" {{isChecked}}"></checkbox>
无论isChecked是true和false都是显示为选中的状态
-->
<view>
<checkbox checked="{{isChecked}}"></checkbox>
</view>
官网介绍
运算
Page({
data:{
msg:'Hello'
}
})
<view>
<view>{{msg}}</view>
<!-- 运算=>表达式
1.可以在花括号中加入表达式 -- "语句"
2.表达式
指的是一些简单、运算、数字运算、字符串、拼接、逻辑运算
2.1数字的加减
2.2字符串拼接
2.3三元表达式
3.语句
3.1复杂的代码段
3.2 if else
3.3 switch
3.4 do while
3.5 for
-->
<view>{{1+1}}</view>
<view>{{'1'+'1'}}</view>
<view>{{11%2==0?'偶数':'奇数'}}</view>
</view>
官网介绍
数组和列表渲染
官网介绍
wx:for
在组件上使用 wx:for 控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。
默认数组的当前项的下标变量名默认为 index,数组当前项的变量名默认为 item
修改如下:
控制台警告消除
完整代码:
<view>
<!-- 列表渲染
wx:for="{{数组或者对象}}"
wx:for-item="循环项的名称"
wx:for-index="循环项的索引"
wx:key="唯一的值"用来提高列表渲染的性能
wx:key 绑定一个普通的字符串的时候,那么这个字符串名称,肯定是循环数组中的对象的唯一属性
wx:key="*this" 表示你的数组是一个普通的数组,*this表示是循环项
[1,2,3]
-->
<!-- 如不提供 wx:key,会报一个 warning -->
<!-- -->
<view wx:for="{{list}}" wx:for-item="a" wx:for-index="i" wx:key="i">
索引:{{i}}--值{{a.name}}
</view>
</view>
Page({
data:{
list:[
{
id:0,
name:'猪八戒'
},
{
id:1,
name:'沙和尚'
},
{
id:2,
name:'孙悟空'
}
]
}
})
对象属性的渲染
完整代码
<view>
<!-- 对象循环
wx:for="对象" wx:for-item="对象的值" wx:for-index="对象的属性"
循环对象的时候最好把item和index的名称都修改一下
wx:for-item="value" wx:for-index="index"
-->
<view wx:for="{{person}}" wx:key="index">
属性:{{index}}——
值:{{item}}
</view>
</view>
Page({
data:{
list:[
{
id:0,
name:'猪八戒'
},
{
id:1,
name:'沙和尚'
},
{
id:2,
name:'孙悟空'
}
],
person:{
age:73,
height:145,
weight:160,
name:'富婆'
}
}
})
block标签
完整代码
<view>
<!-- block:
1.占位符的标签
2.写代码的时候,可以看到这个标签存在
3.页面渲染,小程序会把它移除掉 -->
<block>
<view>123</view>
</block>
</view>
条件渲染
官网介绍
<view>
<!-- 条件渲染
1. wx:if="{{true/false}}"
if,else,if else
wx:if
wx:elif
wx:else
2 hidden
2.1在标签上直接加上属性hidden
2.2 hidden="{{true}}"
3.使用场景
3.1在标签不是频繁的切换显示,优先使用 wx:if
直接把标签从页面结构给移除掉
3.2在标签频繁的切换显示的时候,优先使用hidden
通过添加样式的方式来切换显示
hidden属性不要和样式display一起使用
-->
<view>条件渲染</view>
<view wx:if="{{true}}">123</view>
<view>隐藏</view>
<view wx:if="{{false}}">123</view>
<view>if和else if 和else</view>
<view wx:if="{{true}}">1</view>
<view wx:elif="{{false}}">2</view>
<view wx:else>3</view>
<view hidden style="display:none">hidden隐藏</view>
<view hidden="{{false}}">hidden属性</view>
<view hidden style="display:flex">hidden属性和display一块使用后的结果</view>
</view>
效果如下
小程序的事件绑定
小程序中绑定事件,通过bind关键字来实现。如bindtap、bindinput、bindchange等
不同的组件支持不同的事件,具体看**组件**的说明即可
<!--
1.需要给input标签绑定input事件
绑定关键字bindinput
2.获取输入框的值,通过事件源对象来获取
e.detail.value获取输入框的值
3.把输入框的值赋值到data当中,不能直接this.data.num=e.detail.value
正确的写法:
this.setData({
num:e.detail.value
})
-->
<input type="text" bindinput="handleInput"/>
<view>{{num}}</view>
Page({
data:{
num:0
},
//输入框的Input事件的执行逻辑
handleInput(e){
this.setData({
num:e.detail.value
});
}
})
自定义属性的绑定
<!--
点击事件bindtap
无法在小程序当中的事件中直接传参
通过自定义属性的方式来传递参数
-->
<button bindtap="handletap" data-operation="{{1}}">+</button>
<button bindtap="handletap" data-operation="{{-1}}">-</button>
<view>{{num}}</view>
Page({
data:{
num:0
},
//加减按钮的事件
handletap(e){
// console.log(e);
//获取自定义属性operation
const operation=e.currentTarget.dataset.operation;
this.setData({
num:this.data.num+operation
});
}
})
页面如下:
点击+号按钮,数字增加,减号按钮,数字减1
样式WXSS
官网介绍
在index.js中添加如下代码
Page({
});
在index.wxss中添加如下代码
/*
小程序中不需要主动引入样式文件
需要把页面中某些元素的单位由px改为rpx
当前的设计稿750x
750px=750rpx
1px=1rpx
屏幕宽度改为375px
375px=750rpx
1px=2rpx
1rpx=0.5px
存在一个设计稿宽度414或者未知
1设计稿 page存在一个元素宽度100px
2.拿以上的需求去实现不同宽度的页面适配
page px=750rpx
1px=750rpx/page
100px=750rpx*100/page
假设page=375px
利用一个属性calc属性 css和wxss都支持一个属性
750和rpx中间不要留空格
运算符的两边也不要留空格
*/
view{
/* width: 200rpx; */
height: 200rpx;
background-color: aqua;
font-size: 40rpx;
/* 以下代码写法是错误 */
/* width: 750rpx*100/375; */
/*正确的写法 */
width:calc(750rpx*100/375);
}
在index.wxml中添加如下代码:
<view>
rpx
</view>
效果如下:
样式导入
在项目的根目录下新建style文件夹,在style文件夹下面创建style.wxss
view{
color: aqua;
font-size:32rpx;
}
在项目的demo/demo的路径的文件中引入
/* 引入代码是通过@import
路径只能写相对路径
*/
@import '../../style/style.wxss';
<view>test</view>
效果如下:
选择器
小程序不支持通配符*
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
nth-child(n) view:nth-child(2) // 选择某个索引的标签
选择器介绍
组件
官网介绍
image
由于小程序打包上线的大小限制在2M,所以很多静态资源要放在网络上,图片大小要进行限制,尽量使用网络图片。
官网介绍
完整代码
<!-- image图片标签
src:指定要加载的图片的路径
图片存在默认的宽度和高度320*240
原图大小为154*148
mode决定图片内容,如何和图片标签宽高做适配
1.scaleToFill:默认值,缩放模式,不保持纵横比缩放图片,使图片的宽高完全拉伸至填满 image 元素.
aspectFit:保持宽高比,缩放模式,确保图片的长边能完全显示出来,页面轮播图常用
aspectFill 缩放模式,保持纵横比缩放图片,保证图片的短边能完全显示出来,少用
widthFix 以前web图片的宽度指定之后,高度会自动按比例来调整 ,常用
bottom:类似以前的web的background-position
2.小程序当中的图片直接就支持懒加载 lazy-load
lazy-load 会自己判断,当图片出现在视口上下三屏的高度之内的时候,自己开始加载图片
-->
<image src="https://ae01.alicdn.com/kf/H6fc552e2e37f481eab87ed382c435744r.jpg" mode="bottom" lazy-load/>
Page({
})
image{
box-sizing: border-box;
border: 1px solid red;
width: 300px;
height: 500px;
}
效果如下:
swiper
官网介绍
轮播图
全部代码:
<!-- 1.轮播图外层容器swiper
2.每一个轮播项swiper-item
3.swiper标签存在默认样式
3.1 width:100%
3.2 height:150px
image存在默认宽度和高度320*240
swiper高度无法实现由内容撑开
4.先找出来原图的宽度和高度,等比例给swiper定宽度和高度
原图的宽度和高度1125*352
swiper 宽度/swiper高度=原图的宽度/原图的高度
swiper 宽度=swiper高度*原图的宽度/原图的高度
height:100vw*352/1125
autoplay:自动轮播
interval:修改轮播时间
circular:衔接轮播
indicator-dots:显示面板指示点(索引器,分页器,指示器)
indicator-color :指示点颜色(指示器未选择的颜色)
indicator-active-color:指示点颜色(指示器选择的颜色)
-->
<swiper autoplay interval="3000" circular indicator-dots indicator-color="#0094ff" indicator-active-color="#ff9400">
<swiper-item>
<image src="http://img.alicdn.com/imgextra/i2/42/O1CN01ARtwr41CBHErSCzQY_!!42-0-luban.jpg" mode="widthFix"/>
</swiper-item>
<swiper-item>
<image src="https://aecpm.alicdn.com/tps/i4/TB1pgxYJXXXXXcAXpXXrVZt0FXX-640-200.jpg" mode="widthFix"/>
</swiper-item>
<swiper-item>
<image src="https://img.alicdn.com/tps/i4/TB1v2mGIEH1gK0jSZSySuttlpXa.jpg" mode="widthFix"/>
</swiper-item>
</swiper>
/* pages/swiper/swiper.wxss */
swiper{
width: 100%;
height: calc(100vw*352/1125);
}
image{
width: 100%;
}
效果如下:
navigator
官网介绍
导航组件类似超链接标签a
<!-- 导航组件navigator
块级元素,默认会换行,可以直接加宽度和高度
url:要跳转的页面路径,绝对路径,相对路径
target要跳转到当前的小程序,还是其他的小程序的页面
self:默认值,自己小程序的页面
miniProgram:其他小程序的页面
open-type :跳转方式
navigate:默认值,保留当前页面,跳转到应用内的某个页面,但是不能跳转到tabbar页面
redirect:关闭当前页面,跳转到应用内的某个页面,但是不允许跳转到tabbar页面
switchTab:跳转到tabBar页面,并关闭其他所有非tabBar页面
reLaunch:关闭所有页面,打开到应用内的某个页面
navigateBack:关闭当前页面,返回上一页面或多级页面,可通过getCurrentPages()获取当前的页面栈,决定需要返回几层
exit:推出小程序,target="miniProgram"时生效
-->
<navigator url="/pages/swiper/swiper" open-type="navigate">轮播图页面</navigator>
<navigator url="/pages/index/index" open-type="switchTab">直接跳转到tabbar页面</navigator>
<navigator url="/pages/swiper/swiper" open-type="redirect">轮播图页面redirect</navigator>
<navigator url="/pages/index/index" open-type="reLaunch">relaunch可以直接跳转到tabbar页面</navigator>
页面效果如下:
rich-text
接收标签字符串
富文本
官网介绍
demo2.wxml中代码
<!-- rich-text富文本标签
node属性来实现
1.接收标签字符串
2.接收对象数组
-->
<rich-text class="text" nodes="{{html}}"></rich-text>
demo2.js中代码
// pages/demo2/demo2.js
Page({
data:{
html:'<div style="color:hotpink;font-size:32px;font-weight:700;">123</div>'
}
})
页面效果:
接收对象数组
Page({
data:{
html:[
{
//div标签 由name属性来指定
name:"div",
//标签上的属性 class style
attrs:{
class:"my_div",
style:"color:red;"
},
//子节点children 要接收的数据类型和nodes第二种渲染方式的数据类型一致
children:[
{
name:"p",
attrs:{
},
children:[
{
//放文本
type:"text",
text:"hello"
}
]
}
]
}
]
}
})
<!-- rich-text富文本标签
node属性来实现
1.接收标签字符串(最常用的)
2.接收对象数组
-->
<rich-text nodes="{{html}}"></rich-text>
效果如下:
button按钮
样式
<!-- button标签
外观的属性
size:控制按钮的大小,默认值是default,mini 小尺寸
type:控制按钮的颜色,default:灰色,primary:绿色,warn:红色
plain:按钮是否镂空,背景色透明
loading:名称前是否带 loading 图标
-->
<button>默认按钮</button>
<button size="mini">mini默认按钮</button>
<button type="primary">primary按钮</button>
<button type="warn">warn按钮</button>
<button type="warn" plain>plain按钮</button>
<button type="primary" loading>loading按钮</button>
开放能力
<!-- button开放能力,open-type 的合法值 -->
<!-- contact 直接打开 客服对话功能,需要在微信小程序的后台配置
share 转发当前的小程序到微信朋友中,不能把小程序分享到朋友圈中
getPhoneNumber 获取当前用户的手机号码信息,结合一个事件来使用,不是企业的小程序账号,没有权限来获取用户的手机号码,绑定一个事件bindgetphonenumber,在事件回调函数中通过参数来获取信息,获取到的信息,已经加密过了,需要用户自己搭建小程序的后台服务器,在后台服务器中进行解析手机号码,返回到小程序中就可以看到信息了
getUserInfo 获取用户的个人信息,使用方法类似获取用户的手机号码,可以直接获取不存在加密的字段
launchApp 在小程序当中直接打开app,需要先在app中通过app的某个链接打开小程序,在小程序中再通过这个功能重新打开app。
openSetting 打开小程序内置的授权页面,授权页面只会出现用户曾经点击过的授权
feedback 打开小程序内置的意见反馈页面,只能通过真机调试来打开-->
<button open-type="contact">打开客服会话</button>
<button open-type="share">share</button>
<button open-type="getPhoneNumber" bindgetphonenumber="bindgetphonenumber">getPhoneNumber</button>
<button open-type="getUserInfo" bindgetuserinfo="getUserInfo">getUserInfo</button>
<button open-type="launchApp">launchApp</button>
<button open-type="openSetting">openSetting</button>
<button open-type="feedback">feedback</button>
// pages/demo3/demo3.js
Page({
//获取用户的手机号码信息
bindgetphonenumber(e) {
console.log(e);
},
//获取用户个人信息
getUserInfo(e){
console.log(e);
}
})
效果如下:
icon标签
官网介绍
<!--pages/demo/demo.wxml-->
<!-- 小程序中的字体图标
type图标的类型
success|success_no_circle|info|warn|waiting|cancel|download|search|clear
size 大小
color 图标的颜色
-->
<icon type="success" size="23" color="hotpink"></icon>
<icon type="success_no_circle" size="23" color="hotpink"></icon>
<icon type="info" size="23" color="hotpink"></icon>
<icon type="warn" size="23" color="hotpink"></icon>
<icon type="waiting" size="23" color="hotpink"></icon>
<icon type="cancel" size="23" color="hotpink"></icon>
<icon type="download" size="23" color="hotpink"></icon>
<icon type="search" size="23" color="hotpink"></icon>
<icon type="clear" size="23" color="hotpink"></icon>
显示如下
radio
代码如下:
Page({
data: {
gender: ''
},
check(e) {
//获取单选框中的值
let gender = e.detail.value;
//把值赋值给data中的数据
this.setData({
// gender: gender
gender
});
}
})
<!-- radio单选框
1.radio标签必须要和父元素radio-group来使用
2.value选中的复选框的值
3.需要给radio-group绑定change事件
4.需要在页面中显示选中的值
bindchange绑定radio选中事件
color:radio选中的单选按钮颜色
-->
<radio-group bindchange="check">
<radio value="男" color="red">男</radio>
<radio value="女">女</radio>
</radio-group>
<view>您选中的是{{gender}}</view>
checkbox复选框
官网介绍
<view>
<checkbox-group bindchange="handleChange">
<checkbox wx:for="{{list}}" wx:key="id" value="{{item.value}}">{{item.name}}</checkbox>
</checkbox-group>
<text>选中的水果:{{checkedList}}</text>
</view>
// pages/demo5/demo5.js
Page({
data:{
list:[
{
id:0,
name:"🍇",
value:"葡萄"
},
{
id:1,
name:"🍊",
value:"苹果"
},
{
id:2,
name:"🍌",
value:"香蕉"
}
],
checkedList:[]
},
//复选框的选中事件
handleChange(e){
//获取被选中的复选框的值
const checkedList=e.detail.value;
//进行赋值
this.setData({
checkedList:checkedList
});
}
})
效果如下:
自定义组件
小程序允许我们使用自定义组件的方式来构建页面
官网介绍
创建组件
1.在项目的根目录下创建components文件夹
2.点击components文件夹,鼠标点击右键
类似于页面,一个自定义组件由 json wxml wxss js 4个文件组成
使用组件
1.在需要使用组件的页面的.json文件中引用
{
"usingComponents": {
"Tabs":"../../components/Tabs/Tabs"
}
}
Tabs:组件的名称,"…/…/components/Tabs/Tabs":组件的位置(项目的根目录下的components/Tabs文件夹下)
<!-- 调用组件 -->
<Tabs></Tabs>
效果如下:
向组件中添加内容
<!--components/Tabs/Tabs.wxml-->
<view class="tabs">
<view class="tabs_title">
<view class="title_item active">首页</view>
<view class="title_item">原创</view>
<view class="title_item">分类</view>
<view class="title_item">关于</view>
</view>
<view class="tabs_content">内容</view>
</view>
/* components/Tabs/Tabs.wxss */
.tabs{
}
.tabs_title{
display: flex;
padding: 10rpx;
}
.title_item{
flex:1;
display: flex;
/* 水平对齐 */
justify-content: center;
/* 垂直对齐 */
align-items: center;
}
.active{
color: red;
border-bottom: 10rpx solid currentColor;
}
.tabs_content{
}
显示如下:
动态添加组件样式
<view class="tabs">
<view class="tabs_title">
<!-- data-index:自定义属性名为index -->
<view wx:for="{{tabs}}" wx:key="index" class="title_item {{item.isActive?'active':''}}" bindtap="handleItemTap" data-index="{{index}}">
{{item.name}}
</view>
</view>
<view class="tabs_content">内容</view>
</view>
.tabs{
}
.tabs_title{
display: flex;
padding: 10rpx;
}
.title_item{
/* 对象的子元素都有相同的长度 */
flex: 1;
/* 弹性布局 */
display: flex;
/* 项目位于容器的中心 */
justify-content: center;
align-items: center;
}
.active{
color: red;
border-bottom: 10rpx solid currentColor;
}
// components/Tabs/Tabs.js
Component({
/**
* 组件的属性列表
*/
properties: {
},
/**
* 组件的初始数据
*/
data: {
tabs:[
{
id:0,
name:"首页",
isActive:true
},
{
id:1,
name:"原创",
isActive:false
},
{
id:2,
name:"分类",
isActive:false
},
{
id:3,
name:"关于",
isActive:false
}
]
},
/**
* 组件的方法列表
* 1.页面的.js文件中存放事件回调函数的时候,存放在data同层级下
* 2.组件.js文件中存放事件回调函数的时候,必须要存放在methods中
*/
methods: {
handleItemTap(e){
// 1.绑定点击事件,需要在methods中绑定
// 2.获取被点击的索引
// 3.获取原数组
//4.对数组循环
//5.给每一个循环项选中属性改为false
//6.给当前的索引的项添加激活选中效果
//获取自定义属性值
// const index=e.currentTarget.dataset.index;
//2.获取索引
/*下面的写法是解构,解构:对复杂类型进行解构的时候,
复制了一份变量的引用而已。最严谨的做法,重新拷贝一份数组,
再对这个数组的备份进行处理,不要直接修改this.data.数据
*/
const {index}=e.currentTarget.dataset;//{index:0} 等于 e.currentTarget.dataset.index
//3.获取data中的数组
let {tabs}=this.data;
/* let {tabs}=this.data;等于let tabs=this.data.tabs */
//4.循环
/*
[].forEach遍历数组,遍历数组的时候,修改了v,也会导致源数组被修改
*/
//tabs.forEach((v,i)=>i==index?v.isActive=true:v.isActive=false);
/**
* 第二种遍历数组,修改isActive值的方法
*/
tabs.forEach(function(item,i){
if(i===index){
item.isActive = true
}else{
item.isActive = false
}
})
this.setData({
tabs:tabs
});
}
}
})
效果如下:
组件传值
父组件向子组件传值
通过properties接收父组件传递的值
- 在子组件中定义父组件要传递的属性
// components/Tabs/Tabs.js
Component({
/**
* 组件的属性列表
* 里面存放的是要从父组件中接收的数据
*/
properties: {
//要接收的数据的名称
a:{
//type:要接收的数据类型
type:String,
//value:默认值
value:""
}
},
/**
* 组件的初始数据
*/
data: {
tabs:[
{
id:0,
name:"首页",
isActive:true
},
{
id:1,
name:"原创",
isActive:false
},
{
id:2,
name:"分类",
isActive:false
},
{
id:3,
name:"关于",
isActive:false
}
]
},
/**
* 组件的方法列表
* 1.页面的.js文件中存放事件回调函数的时候,存放在data同层级下
* 2.组件.js文件中存放事件回调函数的时候,必须要存放在methods中
*/
methods: {
handleItemTap(e){
// 1.绑定点击事件,需要在methods中绑定
// 2.获取被点击的索引
// 3.获取原数组
//4.对数组循环
//5.给每一个循环项选中属性改为false
//6.给当前的索引的项添加激活选中效果
//获取自定义属性值
// const index=e.currentTarget.dataset.index;
//2.获取索引
/*下面的写法是解构,解构:对复杂类型进行解构的时候,复制了一份变量的引用而已。最严谨的做法,重新拷贝一份数组,再对这个数组的备份进行处理,不要直接修改this.data.数据
*/
const {index}=e.currentTarget.dataset;//{index:0}
//3.获取data中的数组
let {tabs}=this.data;
/* let {tabs}=this.data;等于let tabs=this.data.tabs */
//4.循环
/*
[].forEach遍历数组,遍历数组的时候,修改了v,也会导致源数组被修改
*/
tabs.forEach((v,i)=>i==index?v.isActive=true:v.isActive=false);
this.setData({
tabs:tabs
});
}
}
})
2.父组件传值
<!-- 调用组件 -->
<!-- 父组件(页面)向子组件传递数据,通过标签属性的方式来传递
在子组件上接收
把这个数据当成是data中的数据直接用即可 -->
<Tabs a="123"></Tabs>
3.子组件页面获取传递的值
<view>父组件传递的值:{{a}}</view>
<view class="tabs">
<view class="tabs_title">
<!-- data-index:自定义属性名为index -->
<view wx:for="{{tabs}}" wx:key="index" class="title_item {{item.isActive?'active':''}}" bindtap="handleItemTap" data-index="{{index}}">
{{item.name}}
</view>
</view>
<view class="tabs_content">内容</view>
</view>
4.效果如下:
子组件向父组件传值
- 在子组件内触发父组件的自定义事件,并传递参数
- 父组件在引用的子组件上添加一个自定义事件
完整代码如下:
子组件页面
<!-- <view>父组件传递的值:{{a}}</view> -->
<view class="tabs">
<view class="tabs_title">
<!-- data-index:自定义属性名为index -->
<view wx:for="{{tabs}}" wx:key="index" class="title_item {{item.isActive?'active':''}}" bindtap="handleItemTap" data-index="{{index}}">
{{item.name}}
</view>
</view>
<view class="tabs_content">内容</view>
</view>
子组件.js文件
// components/Tabs/Tabs.js
Component({
/**
* 组件的属性列表
* 里面存放的是要从父组件中接收的数据
*/
properties: {
//要接收的数据的名称
a: {
//type:要接收的数据类型
type: String,
//value:默认值
value: ""
},
tabs: {
type: Array,
value: []
}
},
/**
* 组件的初始数据
*/
data: {
},
methods: {
handleItemTap(e) {
const {
index
} = e.currentTarget.dataset; //{index:0}
/*点击事件的触发
触发父组件中的自定义事件,同时传递数据给父组件
this.triggerEvent("父组件自定义事件的名称",要传递的参数);
*/
this.triggerEvent('itemChange', {
index
});
}
}
})
父组件页面
<!-- 调用组件 -->
<!-- 父组件(页面)向子组件传递数据,通过标签属性的方式来传递
在子组件上接收
把这个数据当成是data中的数据直接用即可 -->
<!-- 子向父传递数据,通过事件的方式传递
在子组件的标签上加上一个自定义事件 -->
<Tabs binditemChange="handleItemChange" tabs="{{tabs}}"></Tabs>
父组件.js
Page({
data:{
tabs:[
{
id:0,
name:"首页",
isActive:true
},
{
id:1,
name:"原创",
isActive:false
},
{
id:2,
name:"分类",
isActive:false
},
{
id:3,
name:"关于",
isActive:false
}
]
},
//自定义事件,用来接收子组件传递的数据
handleItemChange(e){
//接收传递过来的参数
const {index}=e.detail;
console.log({index});
let {tabs}=this.data; // 等于 let tabs = this.data.tabs
tabs.forEach((v,i)=>i==index?v.isActive=true:v.isActive=false);
/**
* 可以使用如下遍历方式,效果是一样的
*/
// const tabs= this.data.tabs
// tabs.forEach(function(item,i){
// if(i===index){
// item.isActive = true
// }else{
// item.isActive = false
// }
// })
this.setData({
tabs:tabs
});
}
})
自定义组件slot
<!-- <view>父组件传递的值:{{a}}</view> -->
<view class="tabs">
<view class="tabs_title">
<!-- data-index:自定义属性名为index -->
<view wx:for="{{tabs}}" wx:key="index" class="title_item {{item.isActive?'active':''}}" bindtap="handleItemTap" data-index="{{index}}">
{{item.name}}
</view>
</view>
<view class="tabs_content">
<!-- slot标签其实就是一个占位符,插槽,
等到父组件调用子组件的时候,再传递标签过来,最终这些被传递的
标签就会替换slot插槽的位置 -->
<slot></slot>
</view>
</view>
<!-- 调用组件 -->
<!-- 父组件(页面)向子组件传递数据,通过标签属性的方式来传递
在子组件上接收
把这个数据当成是data中的数据直接用即可 -->
<!-- 子向父传递数据,通过事件的方式传递
在子组件的标签上加上一个自定义事件 -->
<Tabs binditemChange="handleItemChange" tabs="{{tabs}}">
<block wx:if="{{tabs[0].isActive}}">0</block>
<block wx:elif="{{tabs[1].isActive}}">1</block>
<block wx:elif="{{tabs[2].isActive}}">2</block>
<block wx:else>3</block>
</Tabs>
自定义组件
应用生命周期
App({
//1应用第一次启动的就会触发的事件
onLaunch(){
//在应用第一次启动的时候,获取用户的个人信息
console.log('onLaunch');
},
// 2 应用被用户看到
onShow(){
// 对应用的数据或者页面效果重置
console.log('onShow');
},
// 3 应用被隐藏
onHide(){
// 暂停或者清除定时器
console.log('onHide');
},
//4 . 应用代码发生了报错的时候,就会触发
onError(err){
// 在应用发生代码报错的时候,收集用户的错误信息,通过异步请求将错误的信息发送到后台去
console.log(err);
console.log("onError");
},
// 5. 页面找不到就会触发
// 应用第一次启动的时候,如果找不到第一个入口页面,才会触发
onPageNotFound(){
//如果页面不存在了。通过js的方式来重新跳转页面,重新跳到第二个首页
wx-wx.navigateTo({
url: '',
success: (result) => {},
})
console.log("onPageNotFound");
}
})
页面生命周期
// pages/demo/demo.js
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
console.log("onLoad")
//onLoad发送异步请求来初始化页面数据
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
console.log("onShow")
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
console.log("onReady")
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
console.log("onHide")
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
console.log("onUnload")
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
console.log("onPullDownRefresh")
},
/**
* 页面上拉触底事件的处理函数
* 需要让页面出现上下滚动才能触发上拉触底事件
*/
onReachBottom: function () {
console.log("onReachBottom")
//上拉加载下一页数据
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
console.log("onShareAppMessage")
},
/**
* 页面滚动 就可以触发
*/
onPageScroll:function(){
console.log("onPageScroll")
},
/**
* 页面的尺寸发生改变的时候触发
* 小程序 发生了横屏竖屏 切换的时候触发
* 需要在app.json的window中添加
* pageOrientation属性,属性值为auto
*/
onResize:function(){
console.log("onResize")
},
/**
* 1、必须要求当前页面也是tabbar页面
* 2、点击的自己的tab item的时候才触发
*/
onTabItemTap:function(){
console.log("onTabItemTap")
}
})
执行顺序,onLoad——》onShow——》onReady——》onHide
onPullDownRefresh需要在app.json中添加enablePullDownRefresh,设置属性值为true,才能下拉刷新,并且触发下拉刷新函数onPullDownRefresh
入门案例
小程序的第三方的框架
- 腾讯 wepy 类似vue
- 美团 mpvue 语法类型vue
- 京东 taro 类似 react
- 滴滴 chameleon
- uni-app 类似vue
- 原生框架 MINA(微信小程序)
https://www.bilibili.com/video/BV1nE41117BQ?p=46
新建小程序项目
填入自己的appid
搭建目录结构
- styles 存放公共样式
- components 存放组件
- lib 存放第三方库
- utils 自己的帮助库
- request 自己的接口帮助库
搭建项目的页面
- 首页 index
- 分类页面 category
- 商品列表页面 goods_list
- 商品详情页面 goods_detail
- 购物车页面 cart
- 收藏页面 collect
- 订单页面 order
- 搜索页面 search
- 个人中心页面 user
- 意见反馈页面 feedback
- 登录页面 login
- 授权页面 auth
- 结算页面 pay
接口地址
https://www.showdoc.com.cn/128719739414963?page_id=2513235043485226
阿里巴巴图标库下载图标
搭建tabbar
在app.json中配置tabbar
{
"pages":[
"pages/index/index",
"pages/category/category",
"pages/goods_list/goods_list",
"pages/goods_detail/goods_detail",
"pages/cart/cart",
"pages/collect/collect",
"pages/order/order",
"pages/search/search",
"pages/user/user",
"pages/feedback/feedback",
"pages/login/login",
"pages/auth/auth",
"pages/pay/pay"
],
"window":{
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#FF0000",
"navigationBarTitleText": "微信小程序电商",
"navigationBarTextStyle":"white"
},
"tabBar": {
"color": "#999",
"selectedColor": "#FF0000",
"backgroundColor": "#fff",
"list": [{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "./images/index.png",
"selectedIconPath": "./images/index1.png"
},
{
"pagePath": "pages/category/category",
"text": "分类",
"iconPath": "./images/menu.png",
"selectedIconPath": "./images/menu1.png"
},
{
"pagePath": "pages/cart/cart",
"text": "购物车",
"iconPath": "./images/cart.png",
"selectedIconPath": "./images/cart1.png"
},
{
"pagePath": "pages/user/user",
"text": "我的",
"iconPath": "./images/person.png",
"selectedIconPath": "./images/person1.png"
}
]
},
"style": "v2",
"sitemapLocation": "sitemap.json"
}
app.wxss中定义主题颜色
/**app.wxss**/
/* 去除页面元素的内边距和外边距 */
page,view,text,swiper,swiper-item,image,navigator{
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* 主题颜色 通过变量实现
1 less中存在变量这个知识点
2 原生的css和wxss也是支持变量
*/
page{
/* 定义主题颜色 */
--themeColor:#FF0000;
/* 定义统一字体大小,假设设计稿大小是375px
1px=2rpx
14px=28rpx
*/
font-size: 28rpx;
}
使用主题颜色
/**index.wxss**/
view{
/* 使用主题颜色 */
color:var(--themeColor);
}
效果如下
分包
官网介绍
分包的原因:
- 小程序压缩包体积不能大于2M,否则无法发布
- 实际开发中小程序体积如果大于2M,就需要使用分包机制进行发布上传
- 分包可解决2M限制,并且能分包加载内容,提高性能
- 分包后单个包的体积不能大于2M
- 分包后所有包的体积不能大于20M
分包形式
- 常规分包
- 独立分包
- 分包预下载
常规分包
- 开发者通过在app.json 中添加 subPackages字段声明分包结构
- 特点:
- 加载小程序的时候先加载主包,当需要访问分包的页面时候才加载分包内容
- 分包的页面可以访问主包的文件,数据,图片等资源
- 主包:
- 主包来源:除了分包以外的内容都会被打包到主包中
- 通常放置启动页(小程序的登录/首页等)/tabBar页面
{
"pages":[
"pages/mapSearch/mapSearch",
"pages/investmentBusiness/investmentBusiness",
"pages/analysis/analysis",
"pages/resourceManagement/resourceManagement",
"pages/logs/logs"
],
"subPackages": [{
"root": "pagesA",
"pages":[
"targetList/targetList",
"talkProjectList/talkProjectList",
"talkProjectShow/talkProjectShow",
"outDiscussManager/outDiscussManager",
"outDiscussRecord/outDiscussRecord",
"outDiscussAdd/outDiscussAdd",
"outDiscussShow/outDiscussShow",
"visitDiscussManager/visitDiscussManager",
"visitDiscussRecord/visitDiscussRecord",
"visitDiscussShow/visitDiscussShow",
"signcontract/signcontract",
"signcontractShow/signcontractShow",
"contractnotStart/contractnotStart",
"contractnotStartShow/contractnotStartShow",
"contractnotFinish/contractnotFinish",
"projectOperation/projectOperation",
"targetEnterprise/targetEnterprise"
]
}],
"window":{
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "test",
"navigationBarTextStyle":"black"
},
"tabBar": {
"selectedColor": "#3c90f7",
"list":[{
"pagePath": "pages/mapSearch/mapSearch",
"text": "检索",
"iconPath": "/images/map1.png",
"selectedIconPath": "/images/map1.png"
},
{
"pagePath": "pages/investmentBusiness/investmentBusiness",
"text": "业务",
"iconPath": "/images/investmentBusiness.png",
"selectedIconPath": "/images/investmentBusiness.png"
},
{
"pagePath": "pages/analysis/analysis",
"text": "分析",
"iconPath": "/images/analysis.png",
"selectedIconPath": "/images/analysis.png"
},
{
"pagePath":"pages/resourceManagement/resourceManagement",
"text": "管理",
"iconPath": "/images/resourceManagement.png",
"selectedIconPath": "/images/resourceManagement.png"
}]
},
"useExtendedLib": {
"weui":true
},
"style": "v2",
"sitemapLocation": "sitemap.json"
}
分包成功后显示
分包后页面的跳转
独立分包
- 设置independent为true
- 特点:
- 独立分包可单独访问分包的内容,不需要下载主包
- 独立分包不能依赖主包或者其他包的内容 - 使用场景:
- 通常某些页面和当前小程序的其他页面关联不大的时候可进行独立分包
- 如:临时加的广告页或者活动页
分包预下载
- app.json中设置preloadRule选项
- key(页面路径):{packages:[预下载包的包名||预下载的包的根路径])}
{
"pages": ["pages/index"],
"subpackages": [
{
"root": "important",
"pages": ["index"],
},
{
"root": "sub1",
"pages": ["index"],
},
{
"name": "hello",
"root": "path/to",
"pages": ["index"]
},
{
"root": "sub3",
"pages": ["index"]
},
{
"root": "indep",
"pages": ["index"],
"independent": true
}
],
"preloadRule": {
"pages/index": {
"network": "all",
"packages": ["important"]
},
"sub1/index": {
"packages": ["hello", "sub3"]
},
"sub3/index": {
"packages": ["path/to"]
},
"indep/index": {
"packages": ["__APP__"]
}
}
}