官方文档
快速入门
安装
npm install wepy-cli -g
生成工程
wepy new myproject
进入工程目录
cd myproject
实时编译项目
wepy build --watch
注意事项:
- 使用
微信开发者工具
新建项目,本地开发选择dist
目录。 -
微信开发者工具
-->项目-->关闭ES6转ES5。重要:漏掉此项会运行报错。 -
微信开发者工具
-->项目-->关闭上传代码时样式自动补全 重要:某些情况下漏掉此项会也会运行报错。 -
微信开发者工具
-->项目-->关闭代码压缩上传 重要:开启后,会导致真机computed, props.sync 等等属性失效。 - 本地项目根目录运行
wepy build --watch
,开启实时编译。
代码规范:
- 变量与方法使用尽量使用驼峰式命名,避免使用$
开头。 以$
开头的方法或者属性为框架内建方法或者属性,可以被使用,使用前请参考API文档。 - 入口,页面,组件的命名后缀为.wpy
。外链的文件可以是其它后缀。 请参考wpy文件说明 - 使用ES6语法开发。 框架在ES6下开发,因此也需要使用ES6开发小程序,ES6中有大量的语法糖可以让我们的代码更加简洁高效。
- 使用Promise: 框架默认对小程序提供的API全都进行了 Promise 处理,甚至可以直接使用async/await等新特性进行开发。
- 事件绑定语法使用优化语法代替: 原bindtap="click"替换为@tap="click",原catchtap="click"替换为@tap.stop="click"。更多@
符用法,参见组件自定义事件。
事件传参使用优化后语法代替: 原bindtap="click" data-index={{index}}替换为@tap="click({{index}})"。 - 自定义组件命名应避开微信原生组件以及功能标签
。 不可以使用input, button, view, repeat等命名自定义组件。更多repeat用法,参见循环列表组件引用。
笔记
1. 组件
组件页面入口继承自wepy.component
,属性与页面属性一样,除了不需要config
以及页面特有的一些小程序事件等等。
2. 实例
小程序在 WePY 中,被分为三个实例,App,Page,Component。其中Page实例继承自Component。声明方式如下:
import wepy from 'wepy';
// 声明一个App文件
export default class MyAPP extends wepy.app {
}
// 声明一个Page文件
export default class IndexPage extends wepy.page {
}
// 声明一个组件文件
export default class MyComponent extends wepy.component {
}
- App 实例中只包含小程序生命周期函数以及自定义方法与属性
import wepy from 'wepy';
export default class MyAPP extends wepy.app {
customData = {};
customFunction () { }
onLaunch () {}
onShow () {}
config = {}; // 对应 app.json 文件
}
在 Page 实例中,可以通过this.$parent来访问 App 实例。
- Page 和 Component 实例
Page 实例中只包含小程序页面生命周期函数,自定义方法与属性以及特有属性。
import wepy from 'wepy';
// export default class MyPage extends wepy.page {
export default class MyPage extends wepy.component {
customData = {};
customFunction () {}
onLoad () {} // 只在 Page 实例中会存在页面生命周期函数
onShow () {} // 只在 Page 实例中会存在页面生命周期函数
// 特有属性示例
config = {}; // 对应page.json文件,只在 Page 实例中存在
data = {}; // 页面所需数据均需在这里声明
components = {}; // 声明页面所引用的子组件
mixins = []; // 声明页面所引用的Mixin实例
computed = {}; // 声明[计算属性](https://wepyjs.github.io/wepy/#/?id=computed-%e8%ae%a1%e7%ae%97%e5%b1%9e%e6%80%a7)
watch = {}; // 声明数据watcher
methods = {}; // 声明页面响应事件。注意,此处只用于声明页面bind,catch事件,自定义方法需以自定义方法的方式声明
events = {}; // 声明组件之间的事件传递
}
对于 methods 属性,因为与Vue的使用习惯不一致,一直存在一个误区,这里的 methods 属性只声明页面bind,catch事件,不能声明自定义方法
3. 普通组件引用
当页面或者组件需要引入子组件时,需要在页面或者script中的components给组件分配唯一id,并且在template中添加
WePY中的组件都是静态组件,是以组件ID作为唯一标识的,每一个ID都对应一个组件实例,当页面引入两个相同ID组件时,这两个组件共用同一个实例与数据,当其中一个组件数据变化时,另外一个也会一起变化。 如果需要避免这个问题,则需要分配多个组件ID和实例
4. 循环列表组件引用
1.4.6新增
当想在wx:for中使用组件时,需要使用辅助标签
5.computed 计算属性
类型:
{ [key: string]: Function }
详细: 计算属性可以直接当作绑定数据,在每次脏检查周期中。在每次脏检查流程中,只要有脏数据,那么computed 属性就会重新计算。
data = {
a: 1
};
computed = {
aPlus () {
return this.a + 1;
}
}
6. watcher
- 类型: { [key: string]: Function }
- 详细: 通过watcher我们能监听到任何数值属性的数值更新。
data = {
num: 1
};
watch = {
num (newValue, oldValue) {
console.log(`num value: ${oldValue} -> ${newValue}`)
}
}
onLoad () {
setInterval(() => {
this.num++;
this.$apply();
}, 1000)
}
7. Props传值
静态传值
使用静态传值时,子组件会接收到字符串的值。
// parent.wpy
data = {
parentTitle: 'p-title'
};
// child.wpy
props = {
title: String,
syncTitle: {
type: String,
default: 'null'
},
twoWayTitle: {
type: Number,
default: 50,
twoWay: true
}
};
onLoad () {
console.log(this.title); // p-title
console.log(this.syncTitle); // p-title
console.log(this.twoWayTitle); // 50
this.title = 'c-title';
console.log(this.$parent.parentTitle); // p-title.
this.twoWayTitle = 60;
console.log(this.$parent.parentTitle); // 60. --- twoWay为true时,子组件props修改会改变父组件对应的值
this.$parent.parentTitle = 'p-title-changed';
console.log(this.title); // 'p-title';
console.log(this.syncTitle); // 'p-title-changed' --- 有sync属性的props,当父组件改变时,会影响子组件的值。
}
组件通信与交互
wepy.component
基类提供三个方法$broadcast
,$emit
,$invoke
,因此任一页面或任一组件都可以调用上述三种方法实现通信与交互,如
this.$emit('some-event', 1, 2, 3, 4);
组件的事件监听需要写在events
属性下,如:
import wepy from 'wepy';
export default class Com extends wepy.component {
components = {};
data = {};
methods = {};
events = {
'some-event': (p1, p2, p3, $event) => {
console.log(`${this.name} receive ${$event.name} from ${$event.source.name}`);
}
};
// Other properties
}
- $broadcast
broadcast
事件是由父组件发起,所有子组件都会收到此广播事件,除非事件被手动取消。事件广播的顺序为广度优先搜索顺序 - $emit
$emit
与$broadcast
正好相反,事件发起组件的父组件会依次接收到$emit事件,如上图,如果E发起一个$emit
事件,那么接收到事件的先后顺序为:A, Page_Index。
- $invoke
$invoke
是一个组件对另一个组件的直接调用,通过传入的组件路径找到相应组件,然后再调用其方法。 如果想在Page_Index中调用组件A的某个方法:
this.$invoke('ComA', 'someMethod', 'someArgs');
如果想在组件A中调用组件G的某个方法:
this.$invoke('./../ComB/ComG', 'someMethod', 'someArgs');
组件自定义事件
1.4.8新增
可以使用@customEvent.user
绑定用户自定义组件事件。其中,@
表示事件修饰符,customEvent
表示事件名称,.use
r表示事件后缀。
目前有三种后缀:
.default: 绑定小程序冒泡事件事件,如bindtap。
.stop: 绑定小程序非冒泡事件,如catchtap。
.user: 绑定用户自定义组件事件,通过$emit触发。
拦截器
可以使用全域拦截器配置API的config、fail、success、complete方法,参考示例:
import wepy from 'wepy';
export default class extends wepy.app {
constructor () {
this.intercept('request', {
config (p) {
p.timestamp = +new Date();
return p;
},
success (p) {
console.log('request success');
return p;
},
fail (p) {
console.log('request error');
return p;
}
});
}
}
数据绑定
小程序数据绑定方式
小程序通过Page提供的setData方法去绑定数据,如:this.setData({title: 'this is title'});
因为小程序架构本身原因,页面渲染层和JS逻辑层分开的,setData操作实际就是JS逻辑层与页面渲染层之间的通信,那么如果在同一次运行周期内多次执行setData操作时,那么通信的次数是一次还是多次呢?这个取决于API本身的设计。
wepy数据绑定方式
wepy使用脏数据检查对setData
进行封装,在函数运行周期结束时执行脏数据检查,一来可以不用关心页面多次setData是否会有性能上的问题,二来可以更加简洁去修改数据实现绑定,不用重复去写setData
方法。代码如下:
this.title = 'this is title';
但需注意,在函数运行周期之外的函数里去修改数据需要手动调用$apply
方法。如:
setTimeout(() => {
this.title = 'this is title';
this.$apply();
}, 3000);
App方法
-
use(middleWare:String|Function)
:使用中间件。
当前内置两个中间件:
requestfix
: 修复小程序请求并发问题。
promisify
:使用wepy.xxx的方式请求小程序原生API都将Promise化。
使用方法如下:
this.use('requestfix');
this.use('promisify');
-
intercept(api:String, provider:Object)
:使用拦截器对原生API请求进行拦截。
格式如下:
// app.js
constructor () {
super();
this.intercept('request', {
config (p) {
p.timestamp = +new Date();
},
success (obj) {
console.log('request success');
}
});
}
wepy.component Class
组件基类
属性
- $name:String: 组件名称。
- $isComponent:Boolean:是否是组件,如果是页面,此值为false。
- $wxpage:Object: 小程序原生page。
- $parent:Page|App:组件的父组件,如果当前是组件是Page对象,那么$parent的值为App对象。
- $root:Page: 组件所在的Page对象,如果当前组件是Page对象,那么$root的值就是自己本身。
- $coms:List
:组件的子组件列表。 - $mixins:Array[Mixin]:组件所注入的Mixin对象。
- data:Object: 组件需要绑定的数据。
- methods:List
:组件需要响应的事件列表。 - props:List
:组件允许传递的props列表。 - events:List
:组件通信时所需要的事件表现。
方法
-
setData(key:String|Object, [value:Object])
:对原有小程序的setData的封装。
因为WePY的脏查检流程会自动执行setData
操作,所以通常情况下不需要使用此方法。
getCurrentPages():相当于全局方法getCurrentPages()。
$getComponent(com:String):通过组件名称路径查找组件对象。
$broadcast(eventName:String, [args]):组件发起一个广播事件。
向所有子组件发起一个广播事件,事件会依次传播直至所有子组件遍历完毕或者事件被手动终止传播。
- $emit(eventName:String, [args]):组件发起一个冒泡事件。
向父组件发起一个冒泡事件,事件会向上冒泡直至Page或者者事件被手动终止传播。
- $apply([func:Function]):组件发起脏检查。
正常流程下,改变数据后,组件会在流程结束时自动触发脏检查。 在异步或者回调流程中改变数据时,需要手动调用$apply方法。
wepy.page Class
页面类,继承自wepy.component
,拥有页面所有的属性与方法。
属性
全部属性继承自wepy.component。
方法
-
$preload(key:String|Object, [value:Object])
:给页面加载preload数据
加载preload数据后,跳转至另一个页面时,在onLoad方法中可以获取到上个页面的preload数据。
使用方法如下:
// page1.js this.$preload('userName': 'Gcaufy'); this.$redirect('./page2'); // page2.js onLoad (params, data) { console.log(data.preload.userName); }
-
$redirect(url:String|Object, [params:Object])
:wx.redirectTo的封装方法。
使用方法如下:
this.$redirect('./page2', {a: 1, b: 2}); this.$redirect({ url: './pages?a=1&b=2' });
$navigate(url:String|Object, [params:Object])
:wx.navigateTo的封装方法$switch(url:String|Object)
:wx.switchTab的封装方法
预加载
请求数据是在onLoad
中进行,但是小程序的 page 1跳转到 page 2,再到 page 2 的 onLoad
是存在一个 300ms ~ 400ms 的延时.
wepy扩展了生命周期,添加了onPrefetch
事件,会在 redirect
之时被主动调用。同时给onLoad
事件添加了一个参数,用于接收预加载或者是预查询的数据:
// params
// data.from: 来源页面,page1
// data.prefetch: 预查询数据
// data.preload: 预加载数据
onLoad (params, data) {}
预加载数据示例:
// page1.wpy 预先加载 page2 需要的数据。
methods: {
tap () {
this.$redirect('./page2');
}
},
onLoad () {
setTimeout(() => {
this.$preload('list', api.getBigList())
}, 3000)
}
// page2.wpy 直接从参数中拿到 page1 中预先加载的数据
onLoad (params, data) {
data.preload.list.then((list) => render(list));
}
预查询数据示例:
// page1.wpy 使用封装的 redirect 方法跳转时,会调用 page2 的 onPrefetch 方法
methods: {
tap () {
this.$redirect('./page2');
}
}
// page2.wpy 直接从参数中拿到 onPrefetch 中返回的数据
onPrefetch () {
return api.getBigList();
}
onLoad (params, data) {
data.prefetch.then((list) => render(list));
}
参考