开放平台:
公众平台:
开发原理不同
运行方式不同
常见的属性:
WXSS(WeiXin Style Sheets)是一套样式语言,用来决定 WXML 的组件应该怎么显示;
WXSS 具有 CSS 大部分特性;
WXSS 对 CSS 进行了扩充以及修改,以适应微信小程序的开发;
与 CSS 相比,WXSS 扩展的特性有:
rpx(responsive pixel): 是微信小程序独有的、解决屏幕自适应的尺寸单位
全局样式
定义在 app.wxss 中的样式为全局样式,作用于每一个页面。
局部样式
在 page 的 wxss 文件中定义的样式为局部样式,只作用在对应的页面,并会覆盖 app.wxss 中相同的选择器。
注意:当局部样式的权重大于或等于全局样式的权重时,才会覆盖全局的样式效果!
小程序根目录下的 app.json 文件用来对微信小程序进行全局配置,它决定了页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等。
在app.json配置文件中,最主要的配置节点是:
"window": {
"backgroundTextStyle": "dark",
"backgroundColor": "#000000",
"navigationBarBackgroundColor": "#124231",
"navigationBarTitleText": "鲜果时蔬",
"navigationBarTextStyle": "black",
"enablePullDownRefresh": true,
"onReachBottomDistance": 100
},
tabBar 是移动端应用常见的页面效果,用于实现多页面的快速切换;
小程序中通常将其分为底部tabBar和顶部tabBar
注意:tabBar中,只能配置最少2个、最多5个 tab 页签,当渲染顶部tabBar的时候,不显示icon,只显示文本
小程序中,app.json 中的 window节点,可以全局配置小程序中每个页面的窗口表现;
如果某些小程序页面,想要拥有特殊的窗口表现,此时,“页面级别的.json配置文件”就可以实现这种需求;
总结:页面级别配置优先于全局配置生效。
注意:页面的.json只能设置 window 相关的配置项,以决定本页面的窗口表现
在小程序中,包含两种类型的生命周期:
其中,页面的生命周期范围较小,应用程序的生命周期范围较大,如图所示:
生命周期函数:是由小程序框架提供的内置函数,会伴随着生命周期,自动按次序执行;
生命周期函数的作用:允许程序员在特定的生命周期时间点上,执行某些特定的操作。例如,页面刚加载的时候,在生命周期函数中自动发起数据请求,获取当前页面的数据;
注意:生命周期强调的是时间段,生命周期函数强调的是时间点。
app.js 是小程序执行的入口文件,在 app.js 中必须调用 App() 函数,且只能调用一次。其中,App() 函数是用来注册并执行小程序的。
App(Object) 函数接收一个Object参数,可以通过这个Object参数,指定小程序的生命周期函数。
App({
// 小程序初始化完成时,执行此函数,可以做一些初始化的工作。
onLaunch: function(options) {},
// 小程序显示到屏幕上的时候,执行此函数。
onShow : function(options) {},
// 小程序被最小化的时候,执行此函数。
onHide : function() {},
//小程序报错的时候触发
onError : function(){}
})
每个小程序页面,必须拥有自己的 .js 文件,且必须调用 Page() 函数,否则报错。其中Page() 函数用来注册小程序页面。
Page(Object) 函数接收一个Object参数,可以通过这个Object参数,指定页面的生命周期函数。
Page({
onLoad : function(options) {}, // 监听页面加载
onShow : function() {}, // 监听页面显示
onReady : function() {}, // 监听页面初次渲染完成
onHide : function() {}, // 监听页面隐藏
onUnload: function() {} // 监听页面卸载
})
小程序中每个页面,由4部分组成,其中 .js 文件内可以定义页面的数据、生命周期函数、其它业务逻辑;
如果要在.js文件内定义页面的数据,只需把数据定义到 data 节点下即可
Page({
data: {
info: 'init data', // 字符串类型的数据
array: [{msg: '1'}, {msg: '2'}] // 数组类型的数据
}
})
把data中的数据绑定到页面中渲染,使用 Mustache 语法(双大括号)将变量包起来即可;
<view>{{要绑定数据名称}}view>
Page({
data: {
id: 0
}
})
<view id="item-{{id}}"> view>
在组件中绑定一个事件处理函数。如bindtap,当用户点击该组件的时候会在该页面对应的Page中找到相应的事件处理函数。
<view id="tapTest" data-hi="WeChat" bindtap="tapName"> Click me! view>
在相应的Page定义中写上相应的事件处理函数,参数是event。
Page({
tapName: function(event) {
console.log(event)
}
})
监听文本框的数据变化
在文本框的 input 事件处理函数中,通过事件参数 event,能够访问到文本框的最新值
event.detail.value
修改data中的数据
通过 this.setData(dataObject) 方法,可以给页面中的 data 数据重新赋值
inputName: function (event) {
this.setData({
msg: event.detail.value // 为 data 中的 msg 重新赋值
})
}
小程序中的事件传参比较特殊,不能在为组件绑定事件的同时,为事件处理函数传递参数
因为小程序会把 bindtap 后指定的值,统一当作事件名称来处理;
通过 data- * 自定义属性传参
如果要在组件触发事件处理函数的时候,传递参数,可以为组件提供 data-* 自定义属性传参。
<wxs src="./../tools.wxs" module="tools" />
<view> {{tools.msg}} view>
<view> {{tools.bar(tools.FOO)}} view>
在.wxs模块中引用其他 wxs 文件模块,可以使用 require 函数。
引用的时候,要注意如下几点:
只能引用 .wxs 文件模块,且必须使用相对路径。
wxs 模块均为单例,wxs 模块在第一次被引用时,会自动初始化为单例对象。多个页面,多个地方,多次引用,使用的都是同一个 wxs 模块对象。
如果一个 wxs 模块在定义之后,一直没有被引用,则该模块不会被解析与运行。
< wxs> 标签
module 属性
module 属性是当前 标签的模块名。在单个 wxml 文件内,建议其值唯一。有重复模块名则按照先后顺序覆盖(后者覆盖前者)。不同文件之间的 wxs 模块名不会相互覆盖。
module 属性值的命名必须符合下面两个规则:
src 属性
src 属性可以用来引用其他的 wxs 文件模块。
引用的时候,要注意如下几点:
注意
在小程序中,使用 wx:if="{{condition}}" 来判断是否需要渲染该代码块
<view wx:if="{{condition}}"> True view>
也可以用 wx:elif 和 wx:else 来添加一个 else 块:
<view wx:if="length > 5">1view>
<view wx:elif="length > 6">2view>
<view wx:else>3view>
因为 wx:if 是一个控制属性,需要将它添加到一个标签上。如果要一次性判断多个组件标签,可以使用一个 标签将多个组件包装起来,并在上边使用 wx:if 控制属性
<block wx:if="{{true}}">
<view> view1 view>
<view> view2 view>
block>
注意: 并不是一个组件,它仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性
在小程序中,直接使用 hidden="{{condition}}" 也能控制元素的显示与隐藏
<view hidden="{{condition}}"> 条件为 true 隐藏,条件为 false 显示 view>
在组件上使用 wx:for 控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件
默认数组的当前项的下标变量名默认为 index,数组当前项的变量名默认为 item
<view wx:for="{{array}}" wx:key="index">
索引是:{{index}} 当前项是:{{item}}
view>
data: {
array:['红楼梦','三国演义','西游记','水浒传']
}
<view wx:for="{{array}}" wx:key="index" wx:for-index="idx" wx:for-item="itemName">
索引是:{{idx}} 当前项是:{{itemName}}
view>
类似 block wx:if,也可以将 wx:for 用在标签上,以渲染一个包含多节点的结构块
<block wx:for="{{[1, 2, 3]}}">
<view> {{index}}: view>
<view> {{item}} view>
block>
key 在列表循环中的作用
如果列表中项目的位置会动态改变或者有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态(如 < input/> 中的输入内容,< checkbox/>的选中状态),需要使用 wx:key 来指定列表中项目的唯一的标识符
当数据改变触发渲染层重新渲染的时候,会校正带有 key 的组件,框架会确保他们被重新排序,而不是重新创建,以确保使组件保持自身的状态,并且提高列表渲染时的效率
概念:下拉刷新是移动端更新列表数据的交互行为,用户通过手指在屏幕上自上而下的滑动,可以触发页面的下拉刷新,更新列表数据。
应用场景:在移动端,数据列表是常见的页面效果,更新列表数据是最基本的页面需求,相比于按钮刷新、定时刷新来说,下拉刷新的用户体验方便友好,已经成为移动端刷新列表数据的最佳解决方案。
两种方式:
需要在 app.json 的 window 选项中或页面配置中修改 backgroundColor 和 backgroundTextStyle 选项
为页面添加 onPullDownRefresh() 函数,可以监听用户在当前页面的下拉刷新事件,这个函数在对应的 .js 里面
当处理完下拉刷新后,下拉刷新的 loading 效果会一直显示,不会主动消失,所以需要手动隐藏下拉刷新的 loading 效果。此时,调用
wx.stopPullDownRefresh() 可以停止当前页面的下拉刷新
概念:在移动端,随着手指不断向上滑动,当内容将要到达屏幕底部的时候,页面会随之不断的加载后续内容,直到没有新内容为止,我们称之为上拉加载更多。上拉加载更多的本质就是数据的分页加载。
应用场景:在移动端,列表数据的分页加载,首选的实现方式就是上拉加载更多。
可以在 app.json 的 window 选项中或页面配置中设置触底距离 onReachBottomDistance。单位为px,默认触底距离为 50px
Page({
onPageScroll(scroll){
console.log(scroll.scrollTop)
}
})
监听用户点击页面内转发按钮( 组件 open-type=“share”)或右上角菜单“转发”按钮的行为,并自定义转发内容。其中 Object 参数说明如下
Page({
/**
* 用户点击右上角分享
*/
onShareAppMessage: function (share) {
console.log(share.from);//获取用户是以哪种方式进行分享 menu:右上角菜单,button:页面按钮
console.log(share.target);//如果是右上角菜单触发,那么这个值就是undefined,如果是button触发,那么这个值就是触发的button
},
})
如果是button触发分享,需要定义button组件
<button open-type='share'>分享button>
同时,此转发事件需要 return 一个 Object,用于自定义转发内容,返回内容如下
Page({
onShareAppMessage: function (res) {
...
return {
title: '自定义转发标题',
path: '/page/user?id=123',
imageUrl: '转发的图片地址'
}
}
})
点击 tab 时触发,其中 Object 参数说明如下
在对应的页面的js文件中注册 onTabItemTap 监听
page({
...
onTabItemTap(obj){
//每次点击对应的页面就能调用对应页面的这个监听
}
})
页面导航就是页面之间的跳转,小程序中页面之间的导航有两种方式:
非 tabBar 页面指的是没有被当作 tabBar 进行切换的页面
<navigator url='/pages/info/info'>去info页面</navigator>
注意:上述代码使用 url 属性指定要跳转到的页面路径;其中,页面路径应该以 / 开头,且路径必须提前在 app.json 的 pages 节点下声明,才能实现正常的跳转
tabBar 页面指的是被当作 tabBar 进行切换的页面(配置在"tabBar"中的list中的页面)。如果 navigator 组件单纯使用 url 属性,无法导航到 tabBar 页面,需要结合open-type 属性进行导航
<navigator url="/pages/list/lists" open-type='switchTab'>去tabBar页面navigator>
关于 navigator 组件的更多用法,请翻阅官方文档
https://developers.weixin.qq.com/miniprogram/dev/component/navigator.html
如果要后退到上一页面或多级页面,需要把 open-type 设置为 navigateBack,同时使用 delta 属性指定后退的层数
<navigator open-type='navigateBack' delta='1'>
返回上一页
</navigator>
通过 wx.navigateTo(Object object) 方法,可以跳转到应用内的某个页面。但是不能跳到 tabbar 页面
Object 参数对象的属性列表如下:
<view bindtap='tapHandler'>前进view>
tapHandler:function(){
wx.navigateTo({
url: '/pages/list/lists',//跳转的路径
success:function(){//成功后的回调
console.log('success')
},
fail:function(){//失败后的回调
console.log('fail')
},
complete:function(){//不管成功还是失败都会回调
console.log('complete')
}
})
}
通过 wx.switchTab(Object object) 方法,可以跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面。其中 Object 参数对象的属性列表如下
<view bindtap='tapHandler'>前进view>
tapHandler:function(){
wx.switchTab({
url: '/pages/list/lists',//跳转的路径
success:function(){//成功后的回调
console.log('success')
},
fail:function(){//失败后的回调
console.log('fail')
},
complete:function(){//不管成功还是失败都会回调
console.log('complete')
}
})
}
通过 wx.navigateBack(Object object) 方法,关闭当前页面,返回上一页面或多级页面。其中 Object 参数对象的属性列表如下:
<view bindtap='tapHandler'>前进view>
tapHandler:function(){
wx.navigateBack({
delta: 1,//返回的页面数
success:function(){//成功后的回调
console.log('success')
},
fail:function(){//失败后的回调
console.log('fail')
},
complete:function(){//不管成功还是失败都会回调
console.log('complete')
}
})
}
navigator 组件的 url 属性用来指定导航到的页面路径,同时路径后面还可以携带参数,参数与路径之间使用 ?分隔,参数键与参数值用 = 相连,不同参数用 & 分隔
去logs页面navigator>
wx.navigateTo(Object object) 方法的 object 参数中,url 属性用来指定需要跳转的应用内非 tabBar 的页面的路径, 路径后可以带参数。参数与路径之间使用 ? 分隔,参数键与参数值用 = 相连,不同参数用 & 分隔
wx.navigateTo({
url: '/pages/logs/logs?name=zs&age=20',
})
不论是声明式导航还是编程式导航,最终导航到的页面可以在 onLoad 生命周期函数中接收传递过来的参数。
onLoad: function(options) {
console.log(options) // options 就是导航传递过来的参数
}
注意: 导航栏的页面不支持此参数传递
小程序每次修改代码并编译后,会默认从首页进入,但是在开发阶段,我们经常会针对特定的页面进行开发,为了方便编译后直接进入对应的页面,可以配置自定义编译模式,步骤如下
每个微信小程序需要事先设置一个通讯域名,小程序只可以跟指定的域名进行网络通信
服务器域名请在 「小程序后台-开发-开发设置-服务器域名」 中进行配置,配置时需要注意:
在微信开发者工具中,可以临时开启 「开发环境不校验请求域名、TLS 版本及 HTTPS 证书」 选项,跳过服务器域名的校验。此时,在微信开发者工具中及手机
开启调试模式时,不会进行服务器域名的校验
注意:在服务器域名配置成功后,建议开发者关闭此选项进行开发,并在各平台下进行测试,以确认服务器域名配置正确
调用 wx.request(Object object) 方法发起 get 请求
wx.request({// 请求的URL地址,必须基于 HTTPS 协议
url: 'https://www.liulongbin.top:8082/api/get',
// 发送到服务器的数据
data: { name: 'zs', age: 20 },
method: 'GET',
// 成功之后的回调函数
success: function(result) {
//result.data 就能获取服务器返回的数据
console.log(result)
}
})
调用 wx.request(Object object) 方法发起 post 请求
wx.request({// 请求的URL地址,必须基于 HTTPS 协议
url: 'https://www.liulongbin.top:8082/api/post',
// 发送到服务器的数据
data: { name: 'zs', age: 20 },
method: 'POST',
// 成功之后的回调函数
success: function(result) {
//result.data 就能获取服务器返回的数据
console.log(result)
}
})
小程序中没有跨域限制
在普通网站开发中,由于浏览器的同源策略限制,存在数据的跨域请求问题,从而衍生出了 JSONP 和 CORS 两种主流的跨域问题解决方案。但是,小程序的内部运行机制与网页不同,小程序中的代码并不运行在浏览器中,因此小程序开发中,不存在数据的跨域请求限制问题
关于微信小程序更多的数据请求内容,请翻阅 wx.request() 的相关文档:
https://developers.weixin.qq.com/miniprogram/dev/api/network/request/wx.request.html
创建组件
引用组件
使用样式美化组件
组件对应 wxss 文件的样式,只对组件 wxml 内的节点生效。编写组件样式时,需要注意以下几点:
在自定义组件外界定义组件样式
默认外界是不能修改自定义组件内部的样式
在自定义组件中的js中设置向外暴露的属性externalClasses
/* 组件 custom-component.js */
Component({
externalClasses: ['my-class']
})
<custom-component class="my-class">这段文本的颜色由组件外的 class 决定custom-component>
在外界page页面使用的时候,把暴露出去的类名当作当前组件的属性,然后来设置 类选择器
<custom-component my-class="red-text" />
<custom-component my-class="large-text" />
<custom-component my-class="red-text large-text" />
.red-text {
color: red;
}
.large-text {
font-size: 1.5em;
}
小程序组件中的 data,和小程序页面中的 data 用法一致,只不过:
和页面不同,组件的事件处理函数,必须定义在 methods 节点中。
Component({
methods: {
// 按钮的点击事件处理函数
btnHandler: function() {}
}
})
properties 的作用
Component({
properties: {
// 完整的定义方式
propA: { // 属性名
type: String, // 属性类型
value: '' // 属性默认值
},
propB: String // 简化的定义方式
}
})
注意:type 的可选值为 Number,String、Boolean、Object、Array、null(表示不限制类型)
可以使用数据绑定的形式,向子组件的属性传递动态数据
<!-- 引用组件的页面模板 -->
<view>
<component-tag-name prop-a="{{dataFieldA}}" prop-b="{{dataFieldB}}">
</component-tag-name>
</view>
在以上例子中,组件的属性 propA 和 propB 将收到页面传递的数据。页面可以通过 setData 来改变绑定的数据字段。
注意:在定义 properties 时,属性名采用驼峰写法(propertyName);在 wxml 中,指定属性值时,则对应使用连字符写法(property-name=“attr value”),应用于数据绑定时,采用驼峰写法(attr="{{propertyName}}")。
小程序中,properties 的值是可读可写的,它的用法与 data 几乎一致,因此可以通过 setData 修改 properties 中任何属性的值,不会影响父组件中的值
properties: {
count: Number
},
methods: {
add: function() {
this.setData({ count: this.properties.count + 1 })
}
}
数据监听器可以用于监听和响应任何属性和数据字段的变化,从而执行特定的操作。作用类似于 vue 中的 watch
数据监听器从小程序基础库版本 2.6.1 开始支持。
数据监听器的基本语法格式如下:
Component({
observers: {
'字段A, 字段B': function(字段A的新值,字段B的新值) {
// do something 只要字段被修改,就会调用这个监听
}
}
})
数据结构
Component({
/**
* 组件的属性列表
*/
properties: {
product:{
type:Object,
value:{
imgUrl:{
type: String,
value:''
},
title:{
type:String,
value:''
}
}
}
}
点击按钮修改product里面的属性值
methods: {
updateImgHandler:function(){
this.setData({
'product.title': "程序员"
});
},
updateTitleHandler:function(){
this.setData({
'product.imgUrl': 'www.itcast.cn'
});
}
}
监听的代码
observers:{
'product.**': function (newPro){
console.log(newPro)
}
}
注意:虽然product对象可以通过 . 的方式去进行设置(例如:this.properties.product.title=“程序员”,但是这种方式不会触发监听,我们需要通过setData的方式才能触发监听
组件的生命周期,指的是组件自身的一些函数,这些函数在特殊的时间点或遇到一些特殊的框架事件时被自动触发
其中,最重要的生命周期是 created, attached, detached ,包含一个组件实例生命流程的最主要时间点
Component({
/**
* 生命周期--创建时候
*/
created:function(){
console.log('自定义组件被创建了');
console.log(arguments);
},
/**
* 生命周期--被加载完毕
*/
attached:function(){
console.log('自定义组件被加载完毕了,在这里可以做一些初始化工作');
console.log(arguments);
},
/**
* 生命周期--页面布局完成
*/
ready:function(){
console.log('自定义组件页面布局完成');
console.log(arguments);
},
/**
* 生命周期--页面布局完成
*/
detached:function(){
console.log('自定义组件在页面节点移除');
console.log(arguments);
},
/**
* 生命周期--页面加载错误
*/
detached: function () {
console.log('自定义组件页面加载错误');
console.log(arguments);
}
})
生命周期方法可以直接定义在 Component 构造器的第一级参数中。
自小程序基础库版本 2.2.3 起,组件的的生命周期也可以在 lifetimes 字段内进行声明(这是推荐的方式,其优先级最高)
Component({
lifetimes: {
attached() {}, // 在组件实例进入页面节点树时执行
detached() {}, // 在组件实例被从页面节点树移除时执行
},
// 以下是旧式的定义方式,可以保持对 <2.2.3 版本基础库的兼容
attached() {}, // 在组件实例进入页面节点树时执行
detached() {}, // 在组件实例被从页面节点树移除时执行
// ...
})
有一些特殊的声明周期,它们并非与组件有很强的关联,但有时组件需要获知,以便组件内部处理,这样的生命周期称为 ‘组件所在页面的生命周期’ ,在 pageLifetimes 定义字段中,其中可用的声明周期包含:
Component({
/**
* 定义组件所在页面生命周期函数
*/
pageLifetimes:{
/**
* 组件所在的页面被展示时执行
*/
show:function(){
console.log('组件所在页面展示了');
},
/**
* 组件所在的页面被隐藏时执行
*/
hide:function(){
console.log('组件所在页面隐藏了');
},
/**
* 组件所在的页面尺寸变化时执行
*/
resize:function(){
console.log('组件所在页面尺寸变化了');
}
}
})
默认插槽
在组件的 wxml 中可以包含 slot 节点,用于承载组件使用者提供的 wxml 结构
默认情况下,一个组件的 wxml 中只能有一个slot。需要使用多 slot 时,可以在组件 js 中声明启用
注意:小程序中目前只有默认插槽和多个插槽,暂不支持作用域插槽
引用组件的页面结构
<btn-zidingyi >
<view>插入到模板中的代码</view>
<view><input type="password"></input></view>
</btn-zidingyi>
组件的页面结构
<view>
<!-- 组件插槽,能把组页面的内容显示在自定义组件的插槽内 -->
<slot></slot>
</view>
这样的话,< btn-zidingyi> 里面的内容就会被插入到 组件页面的< slot>内
启用多个插槽
在组件中,需要使用多 slot 时,可以在组件 js 中声明启用。示例代码如下:
Component({
options:{
multipleSlots: true
}
})
定义多个插槽
可以在组件的 wxml 中使用多个 slot 标签,以不同的 name 来区分不同的插槽。示例代码如下:
//组件被引用的页面
<view>
<list-zidingyi>
<view slot="one">插入到list模板中第一个view</view>
<view slot="two">插入到list模板中第二个view</view>
</list-zidingyi>
</view>
//组件的页面
<view>
<slot name="one"></slot>
</view>
<view>
<slot name="two"></slot>
</view>
组件之间的三种基本通信方式
this.selectComponent(string)
在小程序的组件中,调用 this.selectComponent(string),可以返回指定组件的实例对象。示例代码如下:
component-a>
//父组件的 .js 文件中,可以调用 selectComponent 函数并指定 id 或 class 选择器,获取子组件对象
Page({
onLoad(){
// 切记下面参数不能传递标签选择器(component-a),不然返回的是 null
var component = this.selectComponent('.customA'); // 也可以传递 id 选择器 #cA
console.log(component);
}
})
1.在引用组件的页面,给组件绑定事件名称
<user bind:userTapHandler="userTapHandler"></user>
这里是由子组件触发,通过triggerEvent去触发这个事件,我们需要用bind:开头
2.在引用组件的js里面,定义userTapHandler函数
Page({
userTapHandler:function(e){
console.log(e);//e就是传递过来的事件对象,可以通过e.detail来获取传递过来的值
}
})
3.给组件 user 绑定点击事件
<image id='imgId' src='../../assets/images/home-active.png' bindtap='getImageUrlHandler' data-url="home-active.png">image>
4.在组件的js文件中的 methods 里面定义tap事件
Component({
/**
* 组件的方法列表
*/
methods: {
getImageUrlHandler:function(e){
var dataUrl = e.currentTarget.dataset.url;
//通过 this.triggerEvent 来触发父组件的自定义事件,第一个参数是事件名称,第二个参数是传递的值,第三个是配置项,是否冒泡|是否捕获
this.triggerEvent('userTapHandler', { url: dataUrl});
}
}
})
WePY 是腾讯官方出品的一个小程序快速开发框架,对原生小程序的开发模式进行了再次封装,更贴近于 MVVM 架构模式,并支持ES6/7的一些新特性,同时语法
风格更接近于 Vue.js,使用 WePY 框架能够提高小程序的开发效率
注意:WePY 只是小程序的快速开发框架之一,市面上还有诸如 mpvue 之类的小程序开发框架也比较流行
WePY 相比于原生小程序开发,拥有众多的开发特性和优化方案,例如:
1.安装 WePY 框架
npm install wepy-cli -g
2.初始化 WePY 项目
wepy init standard myProject
”wepy init” 是固定写法,代表要初始化 wepy 项目;”standard” 代表模板类型为标准模板,可以运行 ”wepy list” 命令查看所有可用的项目模板; ”myproject” 为自定义的项目名称。
3.安装 WePY 项目依赖项
npm install
4.编译
wepy build --watch
自动编译生成小程序项目,生成的小程序项目默认被存放于 dist 目录中
一个 .wpy 文件可分为三大部分,各自对应于一个标签:
.wpy 文件中的 script、template、style 这三个标签都支持 lang 和 src 属性,lang 决定了其代码编译过程,src 决定是否外联代码,存在 src 属性且有效时,会忽略内联代码。
文件后缀为.wpy,可共用 Vue 的高亮规则,但需要手动设置。如下是 VS Code 中实现代码高亮的相关设置步骤:
入口文件 app.wpy 中所声明的小程序实例继承自 wepy.app 类,包含一个 config 属性和其它全局属性、方法、事件。在 build 编译期间:
<script>
import wepy from 'wepy';
export default class extends wepy.app {
config = {
"pages":[
"pages/index/index"
],
"window":{
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "WeChat",
"navigationBarTextStyle": "black"
}
};
onLaunch() {
console.log(this);
}
}
</script>
<script>
import wepy from 'wepy';
import Counter from '../components/counter';
export default class Page extends wepy.page {
config = {};
components = {counter1: Counter};
data = {};
methods = {};
events = {};
onLoad() {};
// Other properties
}
</script>
<template lang="wxml">
<view>
</view>
<counter1></counter1>
</template>
<style lang="less">
/** less **/
</style>
页面文件page.wpy中所声明的页面实例继承自wepy.page类,该类的主要属性介绍如下:
<template lang="wxml">
<view> </view>
</template>
<script>
import wepy from 'wepy';
export default class Com extends wepy.component {
components = {};
data = {};
methods = {};
events = {};
// Other properties
}
</script>
<style lang="less">
/** less **/
</style>
组件文件com.wpy中所声明的组件实例继承自wepy.component类,除了不需要config配置以及页面特有的一些生命周期函数之外,其属性与页面属性大致相同。
创建 home 首页
在 src -> pages 目录下,创建 home.wpy 页面,并创建页面的基本代码结构,示例代码如下:
<template></template>
<script>
import wepy from 'wepy'
export default class Home extends wepy.page {
config = {}
methods = {}
}
</script>
<style></style>
设置默认首页
如果要把刚创建的 home.wpy 设置为默认首页,需要打开 src -> app.wpy 入口文件,将 home.wpy 的页面路径,注册到 config -> pages 数组中,并调整为数组的第一项即可,示例代码如下:
<script>
import wepy from 'wepy'
import 'wepy-async-function'
export default class extends wepy.app {
config = {
pages: ['pages/home', 'pages/index’]
}
}
</script>
在创建 .wpy 页面的时候,要注意以下事项:
原生小程序使用 bindtap、bindinput 等绑定事件处理函数,在 wepy 框架中,优化了事件绑定机制,支持类似于 Vue.js 的事件绑定语法,绑定事件可以采用如下
<template>
<view @tap="tapHandler">点我</view>
</template>
<script>
import wepy from 'wepy'
export default class Home extends wepy.page{
config={}
methods={
tapHandler:function(){
console.log('点击了');
}
}
}
</script>
如果 @ 符号绑定的事件处理函数需要传参,可以采用优化的写法
原 bindtap="clickHandler" data-index={{index}} 替换为 @tap="click({{index}})"
<template>
<view @tap="tapHandler({{2}})">点我</view>
</template>
<script>
import wepy from 'wepy'
export default class Home extends wepy.page{
config={}
methods={
tapHandler:function(data,event){
console.log(data);
}
}
}
</script>
.wpy 页面中的私有数据,需要定义到 data 节点中,页面上可以使用双大括号语法 {{ }} 渲染 data 中的数据
<template>
<view @tap="tapHandler({{msg}})">{{msg}}</view>
</template>
<script>
import wepy from 'wepy'
export default class Home extends wepy.page{
...
data={
msg: 'hello world'
}
}
</script>
实现步骤如下:
<template>
<view>
<input type="text" value="{{msg}}" @input="inputHandler">
</view>
</view>
</template>
<script>
import wepy from 'wepy'
export default class Home extends wepy.page{
config={}
data={
msg: 'hello world'
}
methods={
inputHandler:function(e){
//通过e.detail.value 可以获取到用户输入的值
this.data.msg = e.detail.value;
console.log(this.data.msg);
}
}
}
</script>
在 WePY 中使用 wxs 脚本的方式如下:
<template>
<view>{{homeWxs.random(10,20)}}</view>
</template>
<script>
import wepy from 'wepy'
//需要模块导入
import homeWxs from '../wxs/home.wxs'
export default class Home extends wepy.page{
...
//需要在class类中进行注册
wxs={
homeWxs: homeWxs
}
}
</script>
默认使用 wepy-cli 创建的项目,不支持使用 ES7 的 async 和 await 来简化 Promise API 的调用。需要手动开启此功能:
打开 src -> app.wpy,找到 constructor() 构造函数,在构造函数中代码的最后一行,添加 this.use(‘promisify’) 即可
constructor() {
super()
this.use('requestfix')
this.use('promisify') // 添加此行代码,即可启用 async 和 await
}
WePY 框架对原生小程序做了封装,之前通过 wx 调用的 API,都可以直接使用 wepy 进行调用,例如:wx.request() 是原生小程序的数据请求 API,现在可以直接通过 wepy.request() 发起网络数据请求
利用 Promise 的.then 方式来获取数据
<template>
<view>
<button type="primary" @tap.stop="requestHandler">请求</button>
</view>
</template>
<script>
import wepy from 'wepy'
import homeWxs from '../wxs/home.wxs'
export default class Home extends wepy.page{
...
methods={
requestHandler:function(){
wepy.request({
url:'https://www.liulongbin.top:8082/api/get?id=1&username=itheima',
method: 'GET'
}).then(res=>{
//成功后调用
console.log(res);
//弹出土司,提示请求成功
wepy.showToast({
title:res.data.message,
icon:'none',
duration:1000
});
}).catch(error =>{
wepy.showToast({
title:'请求失败',
icon:'none',
duration:1000
});
})
}
}
}
</script>
利用 async await 来实现
<template>
<view>
<button type="primary" @tap.stop="requestHandlerAsync">async请求</button>
</view>
</template>
<script>
import wepy from 'wepy'
import homeWxs from '../wxs/home.wxs'
export default class Home extends wepy.page{
...
methods={
async requestHandlerAsync(){
let res = await wepy.request({
url: 'https://www.liulongbin.top:8082/api/get?id=1&username=itheima',
method: 'get'
});
wepy.showToast({
title: res.data.message,
icon: 'none',
duration: 1000
})
}
}
}
</script>
通过 wepy.request() 方法发起 Post 请求,与get不同的点在于,post请求的参数需要设置进去,不能跟在请求路径后面
利用 Promise 的.then 方式来获取数据
<template>
<view>
<button type="primary" @tap.stop="requestHandler">请求</button>
</view>
</template>
<script>
import wepy from 'wepy'
import homeWxs from '../wxs/home.wxs'
export default class Home extends wepy.page{
...
methods={
requestHandler:function(){
wepy.request({
url:'https://www.liulongbin.top:8082/api/post',
method: 'POST',
data: {name:'itheima',age: 18}
}).then(res=>{
//成功后调用
console.log(res);
//弹出土司,提示请求成功
wepy.showToast({
title:res.data.message,
icon:'none',
duration:1000
});
}).catch(error =>{
wepy.showToast({
title:'请求失败',
icon:'none',
duration:1000
});
})
}
}
}
</script>
利用 async await 来实现
<template>
<view>
<button type="primary" @tap.stop="requestHandlerAsync">async请求</button>
</view>
</template>
<script>
import wepy from 'wepy'
import homeWxs from '../wxs/home.wxs'
export default class Home extends wepy.page{
...
methods={
async requestHandlerAsync(){
let res = await wepy.request({
url:'https://www.liulongbin.top:8082/api/post',
method: 'POST',
data: {name:'itheima',age: 18}
});
wepy.showToast({
title: res.data.message,
icon: 'none',
duration: 1000
})
}
}
}
</script>
在异步函数中更新数据的时候,页面检测不到数据的变化,必须手动调用 this.$apply 方法。作用是强制页面重新渲染
<template>
<view>
<button type="primary" @tap.stop="requestHandlerAsync">async请求</button>
</view>
<view>
{{msg}}
</view>
</template>
<script>
import wepy from 'wepy'
import homeWxs from '../wxs/home.wxs'
export default class Home extends wepy.page{
...
data={
msg: ''
}
methods={
async requestHandlerAsync(){
let res = await wepy.request({
url:'https://www.liulongbin.top:8082/api/post',
method: 'POST',
data: {name:'itheima',age: 18}
});
wepy.showToast({
title: res.data.message,
icon: 'none',
duration: 1000
})
this.msg = res.data;
this.$apply();//刷新页面
}
}
}
</script>