参考黑马程序员的教程黑马程序员前端微信小程序开发教程,微信小程序从基础到发布全流程_企业级商城实战(含uni-app项目多端部署)_哔哩哔哩_bilibili,感谢黑马程序员。
基础知识
实现本地生活案例
效果如下:
1.一共有三个页面,先创建三个页面,主要是在app.json中,微信开发者工具中的json无法写注释,所以我在这里写注释,以//开头,参考时不要把注释也复制进json文件中。
在app.json中将背景色、小程序标题、以及tabBar设置完毕了。
{ //配置页面 "pages":[ "pages/home/home", "pages/message/message", "pages/contact/contact" ], "window":{ "backgroundTextStyle":"light", //用于设置小程序的背景色,这里设置成了蓝色 "navigationBarBackgroundColor": "#2b4b6b", //用于配置小程序的标题“本地生活” "navigationBarTitleText": "本地生活", //设置小程序标题的字体颜色 "navigationBarTextStyle":"white" }, //这里配置tabbar的相关信息 "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" } ] }, "style": "v2", "sitemapLocation": "sitemap.json" }
2.主要实现的页面在home页面中,home.js如下:
两个数组分别用来存放轮播图和九宫格的数据
// pages/home/home.js Page({ /** * 页面的初始数据 */ data: { // 存放轮播图数据的数组 swiperList:[], // 存放九宫格数据 gridList:[] }, // 获取轮播图数据的方法 getSwiperList(){ wx.request({ url: 'https://www.escook.cn/slides', method:'GET', success:(res)=>{ console.log(res); this.setData({ swiperList:res["data"] }) } }) }, //获取九宫格数据的方法 getGridList(){ wx.request({ url: 'https://www.escook.cn/categories', method:"GET", success:(res)=>{ this.setData({ gridList:res["data"] }) } }) }, /** * 生命周期函数--监听页面加载 */ onLoad(options) { this.getSwiperList(); this.getGridList(); }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady() { }, /** * 生命周期函数--监听页面显示 */ onShow() { }, /** * 生命周期函数--监听页面隐藏 */ onHide() {}, /** * 生命周期函数--监听页面卸载 */ onUnload() {}, /** * 页面相关事件处理函数--监听用户下拉动作 */ onPullDownRefresh() {}, /** * 页面上拉触底事件的处理函数 */ onReachBottom() {}, /** * 用户点击右上角分享 */ onShareAppMessage() {} })
3.home.wxml的内容如下:
其实比较简单,但是最关键的还是wxss的配置,用于设置布局。
<!--pages/home/home.wxml--> <!-- 轮播图区域 --> <!--indicator开启轮播图的小圆点 circular使轮播图是衔接滚动,即滚动到最后一个之后再滚动就滚动到第一个了 --> <swiper indicator-dots circular> <!-- 这里的wx:key="id"指的是,key等于item["id"],id是item里就有的属性 --> <swiper-item wx:for="{{swiperList}}" wx:key="id"> <image src="{{item['image']}}"></image> </swiper-item> </swiper> <!-- 轮播图区域 --> <!-- 九宫格区域 --> <view class="grid-list"> <view class="grid-item" wx:for="{{gridList}}" wx:key="id"> <image src="{{item['icon']}}"></image> <text>{{item["name"]}}</text> </view> </view> <!-- 图片区域 --> <view class="img-box"> <!-- 加了mode="widthFix"后,图片可以自动在宽度不变的情况下,高度调成自适应的值 --> <image src="/images/link-01.png" mode="widthFix"></image> <image src="/images/link-02.png" mode="widthFix"></image> </view>
4.home.wxss的内容:
(1)flex模型
(2)box-sizing: border-box;设置盒子模型为border-box后padding和border的值就不会在影响元素的宽高,相当于把padding和border的值都算在content里。
/* pages/home/home.wxss */ swiper { height: 350rpx; } swiper image{ width: 100%; height: 100%; } /* Flexbox 是 flexible box 的简称(注:意思是“灵活的盒子容器”),是 CSS3 引入的新的布局模式。它决定了元素如何在页面上排列,使它们能在不同的屏幕尺寸和设备下可预测地展现出来。 */ .grid-list{ display: flex; flex-wrap: wrap;/*允许换行*/ /* 构成左侧和上侧的边框 */ border-left: 1rpx solid #efefef; border-top: 1rpx solid #efefef; } .grid-item{ width: 33.33%; height: 200rpx; display: flex; /* 改变主轴的方向,使得每个小格子的内部是纵向的布局 */ flex-direction: column; /* 用于实现小格子内部的内容为居中 */ align-items: center; justify-content: center; /* 给九宫格的每个元素加右侧和下侧的边框, 不加左侧和上侧是因为由最外层的边框构成左侧和上侧 */ border-right: 1rpx solid #efefef; border-bottom: 1rpx solid #efefef; /* 默认情况下,盒子都是content box,加了border之后,一行就放不下三个格子了 ,所以改成border-box */ box-sizing: border-box; /* 盒子模型是指:外边距(margin)+ border(边框) + 内边距(padding)+ content(内容),可以把每一个容器,比如div,都看做是一个盒子模型。比如你给一个div设置宽高为500px,但实际你设置的只是content,之后你又设置了padding:10px;border:1px solid red; 没有设置box-sizing:border-box属性,宽高会加上padding和border的值,需要我们手动去计算,减去padding和border的值,并调整content的值,以免超过给定的宽高加了box-sizing:border-box属性,padding和border的值就不会在影响元素的宽高,相当于把padding和border的值都算在content里 */ } .grid-item image{ width: 60rpx; height: 60rpx; } .grid-item text{ font-size: 24rpx; /* 九宫格中的文本与图片隔一段间距 */ margin-top: 10rpx; } .img-box{ display: flex; /* 使得离九宫格有一定的距离 */ padding: 20rpx 10rpx; /*使得在弹性盒对象的元素中的各项周围留有空白: */ justify-content: space-around; } .img-box image{ /* 让图片之间有一定的距离 */ /* 剩余10%的区域出来,100%-45%*2=10% */ width: 45%; }
项目资料链接:localLife.rar - 蓝奏云 (lanzouj.com),若第一个访问不上可以访问第二个:http://gofile.me/6TtvQ/QgjYLKaCW 访问密码:locallife
页面导航
声明式导航
声明式导航指的是在页面上声明一个
1.导航到tabBar页面
tabBar 页面指的是被配置为 tabBar 的页面。在使用
(1)url 表示要跳转的页面的地址,必须以 / 开头
(2)open-type 表示跳转的方式,必须为 switchTab
<navigator url="/pages/message/message" open-type="switchTab">导航到消息页面</navigator>
2.导航到非tabBar页面
非 tabBar 页面指的是没有被配置为 tabBar 的页面。在使用
(1)url 表示要跳转的页面的地址,必须以 / 开头
(2)open-type 表示跳转的方式,必须为 navigate。为了简便,在导航到非 tabBar 页面时,open-type="navigate" 属性可以省略。
<navigator url="/pages/message/message">导航到非tabBar</navigator>
3.后退导航
如果要后退到上一页面或多级页面,则需要指定 open-type 属性和 delta 属性,其中:
(1)open-type 的值必须是 navigateBack,表示要进行后退导航
(2)delta 的值必须是数字,表示要后退的层级。为了简便,如果只是后退到上一页面,则可以省略 delta 属性,因为其默认值就是 1。
<navigator open-type='navigateBack' delta='1'>返回上一页</navigator>
编程式导航
调用小程序的导航API,实现页面的跳转。
1.导航到 tabBar 页面
调用wx.switchTab(Object object)
方法,可以跳转到 tabBar 页面。其中 Object 参数对象的属性列表如下:
//页面结构 <button bindtap="gotoMessage">跳转到消息页面</button> //通过编程式导航,跳转到message页面 gotoMessage(){ wx.switchTab({ url:'/pages/message/message' }) }
2.导航到非tabBar页面
调用wx.navigateTo(Object object)
方法,可以跳转到非 tabBar 的页面。其中 Object 参数对象的属性与上图类似。只是url的路径是非tabBar页面的路径
3.后退导航
调用 wx.navigateBack(Object object)
方法,可以返回上一页面或多级页面。其中 Object 参数对象可选的属性列表如下:
可以不用传object参数给wx.navigateBack()
.
导航传参
声明式导航传参
navigator 组件的 url 属性用来指定将要跳转到的页面的路径。同时,路径的后面还可以携带参数:
(1)参数与路径之间使用 ? 分隔
(2)参数键与参数值用 = 相连
(3)不同参数用 & 分隔
例如:<navigator url="/pages/info/info?name=zs&age=20">跳转到info页面</navigator>
编程式导航传参
方式与声明式导航类似,也是在url里面构造。调用 wx.navigateTo(Object object)
方法跳转页面时,也可以携带参数,代码示例如下:
获得参数
通过声明式导航传参或编程式导航传参所携带的参数,可以直接在 onLoad 事件中直接获取到,示例代码如下:
onLoad:function(options){ console.log(options); }
传参只有给非tabBar页面传参时才有效,给tabBar页面传参的话,tabBar页面是收不到参数的。
下拉刷新
1.定义:下拉刷新是移动端的专有名词,指的是通过手指在屏幕上的下拉滑动操作,从而重新加载页面数据的行为。
2.启用下拉刷新:
(1)全局开启下拉刷新:在 app.json 的 window 节点中,将 enablePullDownRefresh
设置为 true
(2)局部开启下拉刷新:在页面的 .json 配置文件中,将 enablePullDownRefresh
设置为 true
在实际开发中,推荐使用第 2 种方式,为需要的页面单独开启下拉刷新的效果。
3.配置下拉刷新窗口的样式
在全局或页面的 .json 配置文件中,通过 backgroundColor 和 backgroundTextStyle 来配置下拉刷新窗口的样式,其中:
(1)backgroundColor
用来配置下拉刷新窗口的背景颜色,仅支持16 进制的颜色值
(2)backgroundTextStyle
用来配置下拉刷新 loading
的样式,仅支持 dark
和 light
下面是backgroundColor="#efefef"即浅灰色并且backgroundTextStyle="dark"时的效果。
4.监听页面的下拉刷新事件
在页面的 .js 文件中,通过 onPullDownRefresh()
函数即可监听当前页面的下拉刷新事件。
例如,在页面的 wxml 中有如下的 UI 结构,点击按钮可以让 count 值自增 +1:
然后下拉刷新之后就让count归零:
onPullDownRefresh:function(){ this.setData({ count:0 }) wx.stopPullDownRefresh(); }
5.停止下拉刷新的效果
当处理完下拉刷新后,下拉刷新的 loading 效果会一直显示,不会主动消失,所以需要手动隐藏下拉刷新的 loading 效果。此时,调用 wx.stopPullDownRefresh() 可以停止当前页面的下拉刷新。
上拉触底
1.定义:上拉触底是移动端的专有名词,通过手指在屏幕上的上拉滑动操作,从而加载更多数据的行为。
2.监听页面的上拉触底事件:在页面的 .js 文件中,通过 onReachBottom()
函数即可监听当前页面的上拉触底事件。示例代码如下
3.配置上拉触底距离:上拉触底距离指的是触发上拉触底事件时,滚动条距离页面底部的距离。可以在全局或页面的 .json 配置文件中,通过 onReachBottomDistance 属性来配置上拉触底的距离。小程序默认的触底距离是 50px,在实际开发中,可以根据自己的需求修改这个默认值。
4.案例实现:上拉触底调用随机获取颜色。一共有六个步骤:
①定义获取随机颜色的方法 ②在页面加载时获取初始数据 ③渲染 UI 结构并美化页面效果
④在上拉触底时调用获取随机颜色的方法 ⑤添加 loading 提示效果 ⑥对上拉触底进行节流处理
(1)步骤一:定义获取随机颜色的方法
data: { count:0, colorList:[] } getColors(){ wx.request({ url: 'https://www.escook.cn/api/color', method:'GET', // {data:res}指直接获取res中的data的属性值 success:({data:res})=>{ console.log(res); this.setData({ colorList:[...this.data.colorList,...res.data] }) } }) },
获得的数据格式:
(2)步骤二:在页面加载时获取初始数据
onLoad(options) { this.getColors(); },
(3)步骤三:渲染 UI 结构并美化页面效果
页面<view wx:for="{{colorList}}" wx:key="index" class="num-item" style="background-color: rgba({{item}});">{{item}}</view>
wxss:
.num-item{ border: 1rpx solid #efefef; border-radius: 8rpx; line-height: 200rpx; margin:15rpx; text-align: center; text-shadow: 0rpx 0rpx 5rpx #fff; box-shadow: 1rpx 1rpx 6rpx #aaa; }
(4)步骤四:上拉触底时获取随机颜色
//初始形式是下面这种,我们得用带冒号的形式 // onReachBottom() { // }, onReachBottom:function(){ this.getColors(); },
(5)步骤五:添加loading提示效果:完善getColors函数
getColors(){ wx.showLoading({title: '颜色数据加载中'}) wx.request({ url: 'https://www.escook.cn/api/color', method:'GET', // {data:res}指直接获取res中的data的属性值 success:({data:res})=>{ console.log(res); this.setData({ colorList:[...this.data.colorList,...res.data] }) }, complete:()=>{ wx.hideLoading(); } }) },
(6)步骤六:对上拉触底进行节流处理
比如数据还在加载的时候,连续触底多次,那么就会请求多个数据,这个时候就要添加节流操作。节流处理分为三步:
①在 data 中定义 isloading 节流阀。false 表示当前没有进行任何数据请求,true 表示当前正在进行数据请求。
②在 getColors() 方法中修改 isloading 节流阀的值。在刚调用 getColors 时将节流阀设置 true,在网络请求的 complete 回调函数中,将节流阀重置为 false。
③在 onReachBottom 中判断节流阀的值,从而对数据请求进行节流控制。如果节流阀的值为 true,则阻止当前请求,如果节流阀的值为 false,则发起数据请求
Page({ data: { colorList:[], isloading:false }, getColors(){ this.setData({ isloading:true }) wx.showLoading({title: '颜色数据加载中'}) wx.request({ url: 'https://www.escook.cn/api/color', method:'GET', // {data:res}指直接获取res中的data的属性值 success:({data:res})=>{ console.log(res); this.setData({ colorList:[...this.data.colorList,...res.data] }) }, complete:()=>{ this.setData({ isloading:false }) wx.hideLoading(); } }) }, /** * 生命周期函数--监听页面加载 */ onLoad(options) {this.getColors();}, /** * 页面上拉触底事件的处理函数 */ //初始形式是下面这种,我们得用带冒号的形式 // onReachBottom() { // }, onReachBottom:function(){ if(!this.data.isloading){//只有节流阀为false的时候才发起请求 this.getColors(); } } })
自定义编译模式
默认的编译模式下,每次编译都是跳转到首页,若我们想编译完之后直接跳转到我们希望的页面(也就是修改过的页面),我们可以自定义编译模式。
生命周期
定义
生命周期(Life Cycle)是指一个对象从创建 -> 运行 -> 销毁的整个阶段,强调的是一个时间段。我们可以把每个小程序运行的过程,也概括为生命周期:小程序的启动,表示生命周期的开始;小程序的关闭,表示生命周期的结束;中间小程序运行的过程,就是小程序的生命周期
分类
在小程序中,生命周期分为两类,分别是:
1.应用生命周期:指小程序从启动 -> 运行 -> 销毁的过程。
2.页面生命周期:特指小程序中,每个页面的加载 -> 渲染 -> 销毁的过程。
其中,页面的生命周期范围较小,应用程序的生命周期范围较大,如图所示:
生命周期函数
定义
生命周期函数是由小程序框架提供的内置函数,会伴随着生命周期,自动按次序执行。
生命周期函数的作用:允许程序员在特定的时间点,执行某些特定的操作。例如,页面刚加载的时候,可以在 onLoad
生命周期函数中初始化页面的数据。
注意:生命周期强调的是时间段,生命周期函数强调的是时间点。
分类
1.应用的生命周期函数
特指小程序从启动 -> 运行 -> 销毁期间依次调用的那些函数。在app.js文件中声明:
(1)onLaunch:function(options){}
小程序初始化完成时,执行此函数,全局只触发一次、可以做一些初始化的工作。
(2)onShow:function(options){}
小程序启动,或从后台进入前台显示时触发。
(3)onHide:function(){}
小程序从前台进入后台时触发
2.页面的生命周期函数
特指小程序中,每个页面从加载 -> 渲染 -> 销毁期间依次调用的那些函数。小程序的页面生命周期函数需要在页面的 .js 文件中进行声明。
(1)onLoad:function(options){}
监听页面加载,一个页面只调用1次
(2)onShow:function(){}
监听页面显示
(3)onReady:function(){}
监听页面初次渲染完成,一个页面只调用1次
(4)onHide:function(){}
监听页面隐藏
(5)onUnload:function(){}
监听页面卸载,一个页面只调用1次
WXS脚本
概述
1.WXS(WeiXin Script)是小程序独有的一套脚本语言,结合 WXML,可以构建出页面的结构。
2.WXS的应用场景:wxml 中无法调用在页面的 .js 中定义的函数,但是,wxml 中可以调用 wxs 中定义的函数。因此,小程序中 wxs 的典型应用场景就是“过滤器”(就是显示之前做一下处理)。
3.wxs 和 JavaScript 的关系:
虽然 wxs 的语法类似于 JavaScript,但是 wxs 和 JavaScript 是完全不同的两种语言
(1)wxs 有自己的数据类型:number 数值类型、string 字符串类型、boolean 布尔类型、object 对象类型、function 函数类型、array 数组类型、date 日期类型、regexp 正则
(2)wxs 不支持类似于 ES6 及以上的语法形式。不支持:let、const、解构赋值、展开运算符、箭头函数、对象属性简写、etc...
支持:var 定义变量、普通 function 函数等类似于 ES5 的语法
(3)wxs 遵循 CommonJS 规范
①module 对象 ②require() 函数 ③module.exports 对象
基础语法
1.内嵌 wxs 脚本
wxs 代码可以编写在 wxml 文件中的
wxml 文件中的每个
<view>{{m1.toUpper(username)}}</view> <wxs module="m1"> <!--将文本转为大写形式zx -> ZX --> module.exports.toUpper = function(str){ return str.toUpperCase(); } </wxs>
2.定义外联的wxs脚本
wxs 代码还可以编写在以 .wxs 为后缀名的文件内,就像 javascript 代码可以编写在以 .js 为后缀名的文件中一样。示例代码如下:
//tools.wxs文件 function toLower(str){ return str.toLowerCase(); } module.exports = {toLower:toLower}
3. 使用外联的 wxs 脚本
在 wxml 中引入外联的 wxs 脚本时,必须为
<!-- 调用m2模块中的方法--> <view>{{m2.toLower(country)}}</view> <!-- 引用外联的tools.wxs脚本,并命名为m2--> <wxs src="../../utils/tools.wxs" module="m2"></wxs>
WXS的特点
1.与 JavaScript 不同
为了降低 wxs(WeiXin Script)的学习成本, wxs 语言在设计时借大量鉴了 JavaScript 的语法。但是本质上,wxs 和 JavaScript 是完全不同的两种语言!
2.不能作为组件的事件回调
wxs 典型的应用场景就是“过滤器”,经常配合 Mustache 语法进行使用,例如:<view>{{m2.toLower(country)}}</view>
。
但是,在 wxs 中定义的函数不能作为组件的事件回调函数。例如,下面的用法是错误的:<button bindtap="m2.toLower">按钮</button>
3.隔离性
隔离性指的是 wxs 的运行环境和其他 JavaScript 代码是隔离的。体现在如下两方面:①wxs 不能调用 js 中定义的函数 ②wxs 不能调用小程序提供的 API
4. 性能好
在 iOS 设备上,小程序内的 WXS 会比 JavaScript 代码快 2 ~ 20 倍。在 android 设备上,二者的运行效率无差异。
本地生活案例v2
点击九宫格进入shoplist页面,shoplist页面的标题根据传入的参数改变。
1.改变具体页面的标题
navigator传参数
<!-- 九宫格区域 --> <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页面调用微信的API来改变标题:
onLoad(options) { this.setData({query:options}) }, onReady() { wx.setNavigationBarTitle({ title: this["data"]["query"]["title"], }) },
2.列表页面的 API 接口,以分页的形式,加载指定分类下商铺列表的数据:
(1)接口地址,https://www.escook.cn/categories/:cate_id/shops,URL 地址中的 :cate_id 是动态参数,表示分类的 Id
(2)请求方式为GET请求,请求参数:① _page 表示请求第几页的数据 ② _limit 表示每页请求几条数据
其中header里面有X-Total-Count属性,其值就是total,将它赋值给data里的total。
完整代码如下:
// pages/shoplist/shoplist.js Page({ data: { query:{}, shopList:[], page:1, pageSize:10, total:0, loading:false }, onLoad(options) { this.setData({query:options}); this.getShopList(); }, getShopList(stopfresh){ //配置节流阀 this.setData({loading:true}); //展示loading效果 wx.showLoading({ 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 //内容为数字的字符串减去0就是number类型的 }) }, complete:()=>{ this.setData({loading:false}); wx.hideLoading(); stopfresh && stopfresh();//因为是异步的,所以关闭刷新必须在这里进行,只有传了这个函数时才会调用 } }) }, onReady() { wx.setNavigationBarTitle({ title: this["data"]["query"]["title"], }) }, onUnload() {}, onPullDownRefresh() { this.setData({ page:1,//初始化为第一页 shopList:[], total:0 }); this.getShopList(wx.stopPullDownRefresh); }, /** * 页面上拉触底事件的处理函数 */ onReachBottom() { //判断是否请求完毕 if(this["data"]["page"]*this["data"]["pageSize"]>=this["data"]["total"]){ wx.showToast({ title:"到底了" }); return; } //当节流阀为false的时候才去请求数据 if(!this["data"]["loading"]){ this.setData({page:this["data"]["page"]+1}); this.getShopList(); } }, })
wxss如下:
/* pages/shoplist/shoplist.wxss */ .shop-item{ display: flex; padding: 15rpx; border: 1rpx solid #efefef; border-radius: 8rpx; margin: 15rpx; box-shadow: 1rpx 1rpx 15rpx #ddd; } .thumb image{ width: 250rpx; height: 250rpx; /* 这样上一张图片与下一张图片之间就不会有间距了 */ display: block; margin-right: 15rpx; } .info{ /* 弹性布局 */ display: flex; /* 纵向方向 */ flex-direction: column; /* 文字分散对齐 */ justify-content: space-around; font-size: 24rpx; } .shop-title{ font-weight: bold; }
具体代码文件:locallifev2.rar - 蓝奏云 (lanzouj.com),若打不开则打开这个:http://gofile.me/6TtvQ/wQRNA7e2i 密码是:locallifev2
自定义组件
创建组件
①在项目的根目录中,鼠标右键,创建 components -> test 文件夹
②在新建的 components -> test 文件夹上,鼠标右键,点击“新建 Component”
③键入组件的名称之后回车,会自动生成组件对应的 4 个文件,后缀名分别为 .js,.json, .wxml 和 .wxss
注意:为了保证目录结构的清晰,建议把不同的组件,存放到单独目录中,例如:
引用组件
组件的引用方式分为“局部引用”和“全局引用”,顾名思义:
1.局部引用:组件只能在当前被引用的页面内使用,在页面的 .json 配置文件中引用组件的方式,叫做“局部引用”。示例代码如下:
2.全局引用:组件可以在每个小程序页面中使用,在 app.json 全局配置文件中引用组件的方式,叫做“全局引用”。示例代码如下:
tips:
根据组件的使用频率和范围,来选择合适的引用方式:如果某组件在多个页面中经常被用到,建议进行“全局引用;如果某组件只在特定的页面中被用到,建议进行“局部引用”
组件与页面的区别
从表面来看,组件和页面都是由 .js、.json、.wxml 和 .wxss 这四个文件组成的。但是,组件和页面的 .js 与 .json 文件有明显的不同:
1.组件的 .json 文件中需要声明 "component": true 属性
2.组件的 .js 文件中调用的是 Component() 函数
3.组件的事件处理函数需要定义到 methods 节点中
组件的样式
1.组件样式隔离:
默认情况下,自定义组件的样式只对当前组件生效,不会影响到组件之外的 UI 结构,如图所示:
1.组件 A 的样式不会影响组件 C 的样式
2.组件 A 的样式不会影响小程序页面的样式
3.小程序页面的样式不会影响组件 A 和 C 的样式
好处:
①防止外界的样式影响组件内部的样式
②防止组件的样式破坏外界的样式
2.组件样式隔离的注意点:(1)app.wxss 中的全局样式对组件无效 (2)只有 class 选择器会有样式隔离效果,id 选择器、属性选择器、标签选择器不受样式隔离的影响,即在app.wxss中使用id 选择器、属性选择器、标签选择器会影响到组件内部的样式
建议:在组件和引用组件的页面中建议使用 class 选择器,不要使用 id、属性、标签选择器!
3.修改组件的样式隔离选项
默认情况下,自定义组件的样式隔离特性能够防止组件内外样式互相干扰的问题。但有时,我们希望在外界能够控制组件内部的样式,此时,可以通过 styleIsolation 修改组件的样式隔离选项,用法如下:
//在组件的.js文件中新增如下配置 Component({ options:{ styleIsolation:'isolated' } })
或者在组件的.json文件中新增如下配置
{ "styleIsolation":"isolated" }
styleIsolation 的可选值:
数据、方法和属性
1.data 数据:在小程序组件中,用于组件模板渲染的私有数据,需要定义到 data 节点中,示例如下:
Component({ data:{ count:0 } })
2.methods 方法:在小程序组件中,事件处理函数和自定义方法需要定义到 methods 节点中,示例代码如下:
methods: { // 点击事件处理函数 addCount() { if (this.data.count >= this.properties.max) return this.setData({ count: this.data.count + 1, max: this.properties.max + 1 }) this._showCount() }, _showCount() { wx.showToast({ title: 'count是' + this.data.count, icon: 'none' }) }, showInfo() { console.log(this.data) console.log(this.properties) console.log(this.data === this.properties) } }
3.properties 属性:在小程序组件中,properties 是组件的对外属性,用来接收外界传递到组件中的数据,示例代码如下:
//在js文件中 properties: { // 第一种方式:简化的方式 // max: Number // 第二种方式:完整的定义方式 max: { type: Number, value: 10 } }, //在wxml文件中: <view>max属性的值是:{{max}}</view>
4.data 和 properties 的区别:
在小程序的组件中,properties 属性和 data 数据的用法相同,它们都是可读可写的,只不过:
(1)data 更倾向于存储组件的私有数据 (2)properties 更倾向于存储外界传递到组件中的数据
5.使用 setData 修改 properties 的值:
由于 data 数据和 properties 属性在本质上没有任何区别,因此 properties 属性的值也可以用于页面渲染,或使用 setData 为 properties 中的属性重新赋值,示例代码如下:
数据监听器
一、介绍
数据监听器用于监听和响应任何属性和数据字段的变化,从而执行特定的操作。它的作用类似于 vue 中的 watch 侦听器。在小程序组件中,数据监听器的基本语法格式如下:
Component({ observers:{ '字段A,字段B':function(字段A的新值,字段B的新值){ //do something } } })
举例:
1.组件的 UI 结构如下:
<view>{{n1}}+{{n2}} = {{sum}}</view> <button size="mini" bindtap="addN1">n1自增</button> <button size="mini" bindtap="addN2">n2自增</button>
2.组件的js如下:
Component({ /** * 组件的属性列表 */ properties: {}, /** * 组件的初始数据 */ data: { n1:0, n2:0, sum:0 }, observers:{ 'n1,n2':function(n1,n2){ this.setData({sum:n1+n2}); } }, /** * 组件的方法列表 */ methods: { addN1(){this.setData({n1:this["data"]["n1"]+1})}, addN2(){this.setData({n2:this["data"]["n2"]+1})}, } })
二、监听的具体用法
1.数据监听器支持监听对象中单个或多个属性的变化,示例语法如下:
比如data中有一个对象为rgb,observers可以监听三个子属性的变化
Component({ data:{ rgb:{r:0,g:0,b:0}, fullColor:'0,0,0' }, observers:{ 'rgb.r,rgb.g,rgb.b':function(r,g,b){ this.setData({ fullColor:`${r},${g},${b}` }) } } })
2.监听对象中所有属性的变化:
如果某个对象中需要被监听的属性太多,为了方便,可以使用通配符 ** 来监听对象中所有属性的变化,示例代码如下:
observers:{ 'rgb.**':function(r,g,b){ this.setData({ fullColor:`${r},${g},${b}` }) } }
纯数据字段
一、介绍
纯数据字段指的是那些不用于界面渲染的 data 字段。
应用场景:例如有些情况下,某些 data 中的字段既不会展示在界面上,也不会传递给其他组件,仅仅在当前组件内部使用。带有这种特性的 data 字段适合被设置为纯数据字段。好处:纯数据字段有助于提升页面更新的性能。
二、使用规则
在 Component 构造器的 options 节点中,指定 pureDataPattern 为一个正则表达式,字段名符合这个正则表达式的字段将成为纯数据字段,示例代码如下:
Component({ options:{ //指定所有_开头的数据字段为纯数据字段 pureDataPattern:/^_/ }, data:{ a:true,//普通数据字段 _b:true//纯数据字段 } })
组件的生命周期
一、生命周期函数
二、组件主要的生命周期函数:
在小程序组件中,最重要的生命周期函数有 3 个,分别是 created、attached、detached。它们各自的特点如下:
- 组件实例刚被创建好的时候,created 生命周期函数会被触发
(1)此时还不能调用 setData
(2)通常在这个生命周期函数中,只应该用于给组件的 this 添加一些自定义的属性字段
2.在组件完全初始化完毕、进入页面节点树后, attached 生命周期函数会被触发
(1)此时, this.data 已被初始化完毕
(2)这个生命周期很有用,绝大多数初始化的工作可以在这个时机进行(例如发请求获取初始数据)
3.在组件离开页面节点树后, detached 生命周期函数会被触发
(1)退出一个页面时,会触发页面内每个自定义组件的 detached 生命周期函数
(2)此时适合做一些清理性质的工作
三、使用声明周期函数
1.在小程序组件中,生命周期函数可以直接定义在 Component 构造器的第一级参数中,也可以在 lifetimes 字段内进行声明(这是推荐的方式,其优先级最高)。示例代码如下:
四、组件所在页面的生命周期
有时,自定义组件的行为依赖于页面状态的变化,此时就需要用到组件所在页面的生命周期。比如说一个组件显示的颜色是rgb三个参数决定的,组件每次被渲染时都会显示出随机生成的rgb从而显示出不同的颜色,这个时候就需要组件所在页面的生命周期。
例如:每当触发页面的 show 生命周期函数的时候,我们希望能够重新生成一个随机的 RGB 颜色值。
在自定义组件中,组件所在页面的生命周期函数有如下 3 个,分别是:
组件所在页面的生命周期函数,需要定义在 pageLifetimes 节点中,示例代码如下:
那么每当触发页面的 show 生命周期函数的时候,我们希望能够重新生成一个随机的 RGB 颜色值。步骤如下:
插槽
在自定义组件的 wxml 结构中,可以提供一个
一、单个插槽
在小程序中,默认每个自定义组件中只允许使用一个
二、多个插槽
在小程序的自定义组件中,需要使用多
示例代码如下:
定义多个插槽:可以在组件的 .wxml 中使用多个
在使用带有多个插槽的自定义组件时,需要用 slot 属性来将节点插入到不同的
组件通信
一、父子组件之间通信的 3 种方式
(1)属性绑定:用于父组件向子组件的指定属性设置数据,仅能设置 JSON 兼容的数据
(2)事件绑定:用于子组件向父组件传递数据,可以传递任意数据
(3)获取组件实例:父组件还可以通过 this.selectComponent() 获取子组件实例对象,这样就可以直接访问子组件的任意数据和方法。
(1)属性绑定(父传子)
属性绑定用于实现父向子传值,而且只能传递普通类型的数据,无法将方法传递给子组件。父组件的示例代码如下:
//父组件的data结点 data:{count:0} //父组件的wxml结构 <my-test3 count="{{count}}"></my-test3>
子组件在 properties 节点中声明对应的属性并使用。示例代码如下:
//子组件的properties节点 properties:{count:Number} //子组件的wxml结构 <text>子组件中的count值为:{{count}}</text>
(2)事件绑定(子传父)
事件绑定用于实现子向父传值,可以传递任何类型的数据。使用步骤如下:
①在父组件的 js 中,定义一个函数,这个函数即将通过自定义事件的形式,传递给子组件
//在父组件中定义syncCount方法,将来这个方法会被传递给子组件,供子组件进行调用 syncCount(e){ this.setData({ count:e["detail"]["value"] }) }
②在父组件的 wxml 中,通过自定义事件的形式,将步骤 1 中定义的函数引用,传递给子组件
<!--使用bind:自定义事件名称(推荐)--> <my-test3 count="{{count}}" bind:sync="syncCount"></my-test3> <!--或在bind后面直接写上自定义事件名称--> <my-test3 count="{{count}}" bindsync="syncCount"></my-test3>
③在子组件的 js 中,通过调用 this.triggerEvent('自定义事件名称', { /* 参数对象 */ }) ,将数据发送到父组件
④在父组件的 js 中,通过 e.detail 获取到子组件传递过来的数据
(3)获取组件实例(子传父或者父传子)
可在父组件里调用 this.selectComponent("id或class选择器") ,获取子组件的实例对象,从而直接访问子组件的任意数据和方法。调用时需要传入一个选择器,例如 this.selectComponent(".my-component")。
behaviors
一、概念
behaviors 是小程序中,用于实现组件间代码共享的特性,类似于 Vue.js 中的 “mixins”。
二、behaviors的工作方式
每个 behavior 可以包含一组属性、数据、生命周期函数和方法。组件引用它时,它的属性、数据和方法会被合并到组件中。每个组件可以引用多个 behavior,behavior 也可以引用其它 behavior。
三、创建Behavior
调用 Behavior(Object object) 方法即可创建一个共享的 behavior 实例对象,供所有的组件使用:
module.exports = Behavior({ data: { username: 'zs' }, properties: {}, methods: {} })
四、导入并使用behavior
//使用require()导入需要的自定义behavior模块 const myBehavior = require('../../behaviors/my-behavior') Component({ //将导入的behavior实例对象,挂载到behaviors数组节点中,即可生效 behaviors: [myBehavior], //组件的其他节点 })
五、behavior中所有可用的节点
若behavior中有生命周期函数,比如created,那么behavior中的created会比引用了behavior的组件的created函数先触发。
使用npm包
目前,小程序中已经支持npm安装第三方包,但是有三个限制:1.不支持依赖于Node.js内置库的包2.不支持依赖于浏览器内置对象的包3.不支持依赖于C++插件的包
Vant weapp
Vant Weapp是有赞前端团队开源的一套小程序UI程序库。
安装
具体可以参考vant小程序官网。
1.通过npm安装(建议制定版本为@1.3.3)
2.构建npm包
3.修改app.json
全局数据共享
类似于Redux,小程序中也有用于全局数据共享的插件,即mobx。
在小程序中,可使用mobx-miniprogram配合mobx-miniprigram-bindings实现全局数据共享,其中:(1)mobx-miniprogram用来创建Store实例对象(2)mobx-miniprogram-bindings用来把Store中的共享数据或方法、绑定到组件或页面中使用。
安装
需要使用npm环境安装,命令为:npm install --save mobx-miniprogram@4.13.2 mobx-miniprogram-bindings@1.2.1
,安装完毕之后,删除miniprogram_npm目录,重新构建npm。
使用
1.在项目的根目录下创建文件夹store,其下创建store.js,创建store实例:
//在这个JS文件中,专门来创建store的示例对象 import {action, observable} from 'mobx-miniprogram'; export const store = observable({ // 数据字段 numA:1, numB:2, //计算属性,用get修饰计算属性,这样sum的值只能是可读的而不可写 get sum(){ return this.numA+this.numB; }, //actions方法,用来修改store中的数据 updateNum1:action(function(step){ this.numA += step; }), updateNum2:action(function(step){ this.numB += step; }) });
actions方法用来修改store中的数据,注意是写法,函数被action()包裹的。
2.在页面中引用创建的示例,比如在message中,页面布局如下:
<view>{{numA}} + {{numB}} = {{sum}}</view> <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>
message.js如下:
// pages/message/message.js import {createStoreBindings} from 'mobx-miniprogram-bindings'; import {store} from '../../store/store'; Page({ data: {}, onLoad(options) { this.sotreBindings = createStoreBindings(this,{ store, //代表数据源 fields:['numA','numB','sum'],//代表绑定哪些字段 actions:['updateNum1']//代表绑定哪些方法 }) }, tiaozhuandaoContact(){ wx.navigateTo({ url: '/pages/nottabBar/nottabBar?name=zs&age=20', }) }, onReady() {}, onShow() {}, onHide() {}, /** * 生命周期函数--监听页面卸载 */ onUnload() { this.sotreBindings.destroyStoreBindings();//在页面卸载时解除绑定 }, /** * 页面相关事件处理函数--监听用户下拉动作 */ onPullDownRefresh() {}, /** * 页面上拉触底事件的处理函数 */ onReachBottom() {}, /** * 用户点击右上角分享 */ onShareAppMessage() {}, btnHandler1(e){ this.updateNum1(e["target"]["dataset"]["step"]); console.log(e["target"]["dataset"]["step"]); } })
3.若要在组件中使用store里的变量以及方法,比如number.js
// components/numbers/numbers.js import {storeBindingsBehavior} from 'mobx-miniprogram-bindings'; import {store} from '../../store/store' Component({ behaviors:[storeBindingsBehavior], storeBindings:{ store,//数据源 fields:{ numA:'numA',//第一种数据绑定方式 numB:()=>store["numB"],//第二种数据绑定方式 sum:(store)=>store["sum"]//第三种数据绑定方式 }, actions:{ updateNum2:'updateNum2' } }, /** * 组件的属性列表 */ properties: {}, /** * 组件的初始数据 */ data: {}, /** * 组件的方法列表 */ methods: { btnHandler2(e){ this.updateNum2(e["target"]["dataset"]["step"]) } } })
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY