目录
1. wepy项目构建
简单介绍:wepy是一个微信小程序框架,支持模块化开发,开发风格类似Vue.js。可搭配redux使用,能同时打包出web和小程序。
全局安装或更新WePY命令行工具
npm install -g wepy-cli
创建项目
wepy init standard my-project[项目名]
PS I:\H5\WeiXinProgram> wepy init standard wepy-demo
? Project name [wepy-demo]
? AppId [appid]
? Project description [A WePY project]
? Author [author]
? Use ESLint to lint your code? No ---选择Yes,会对代码格式校验
? Use Redux in your project? No ---选择Yes,可以使用Redux框架语法,目录会多出store目录
? Use web transform feature in your project? Yes ---选择Yes会有index.template.html
切换至项目目录
cd wepy-demo[项目目录]
安装依赖
npm install
开启实时编译
npm run dev
构建项目完整目录
2-wepy文件使用
1、app.wpy文件介绍
// template、style、script三大标签,有lang、src属性,当src属性存在文件,那么其内部代码会被忽略。
// app.apy小程序入口文件
@import "./styles/gb750"; --- 编译成app.wxss文件,可以外部引用
import wepy from 'wepy'
import 'wepy-async-function' --- 使用Promise引入
export default class extends wepy.app { //该处是wepy.app,无类名
config = { --- 编译成app.json文件
pages: [
'pages/index'
],
window: {
backgroundTextStyle: 'light',
navigationBarBackgroundColor: '#fff',
navigationBarTitleText: 'WeChat',
navigationBarTextStyle: 'black'
}
}
constructor () {
super()
// 两个中间件
this.use('requestfix') // requestfix: 修复小程序请求并发问题。
this.use('promisify') // promisify:使用wepy.xxx的方式请求小程序原生API都将Promise化。(需要我们手动加上)
};
customData = {}
customFunction () {}
globalData = {} ---全局数据
onLaunch () {}
onShow () {}
}
2、pages目录下的文件介绍
// pages目录下存放主页面,代码编写与app.wpy相似,不同之处如下:
// 因为app.wpy不需要template,但pages目录下的页面需要
// 在Pages下的页面实例中,可以通过this.$parent来访问App实例。
// Page页面继承自Component组件,即Page也是组件。除扩展了页面所特有的config配置以及特有的页面生命周期函数之外,
其它属性和方法与Component一致。
--- 编译成index.wxml文件,只有pages目录下的template会编译成wxml文件
---组件标签
import wepy from 'wepy' ---一定要引入
import Counter from '../components/counter' ---引入组件
import testMixin from '../mixins/test' ---引入混合组件
export default class Index extends wepy.page {
// export default class MyComponent extends wepy.component
customData = {} // 自定义数据
customFunction () {} //自定义方法
onLoad () {} // 在Page和Component共用的生命周期函数
onShow () {} // 只在Page中存在的页面生命周期函数
config = {} // 只在Page实例中存在的配置数据,对应于原生的page.json文件
data = {...this.customData} // 页面所需数据均需在这里声明,可用于模板数据绑定
components = {
// 为两个相同组件的不同实例分配不同的组件ID,从而避免数据同步变化的问题 counter1 : Counter;counter2 : Counter;} ---{组件标签名 : 引入组件名;}
// WePY中,在父组件template模板部分插入驼峰式命名的子组件标签时,不能将驼峰式命名转换成短横杆式命名(比如将childCom转换成child-com),这与Vue中的习惯是不一致。
//声明页面中所引用的组件,或声明组件中所引用的子组件
mixins = [testMixin] // 声明页面所引用的Mixin实例
computed = {} // 声明计算属性,是一个有返回值的函数,可直接被当作绑定数据来使用。
watch = {} // 声明数据watcher
methods = {} // 声明页面wxml中标签的事件处理函数。注意,此处只用于声明页面wxml中标签的bind、catch事件,自定义方法需以自定义方法的方式声明
events = {} // 声明组件之间的事件处理函数
}
3、components目录下的文件介绍
// components目录下存放组件
// 页面可以引入组件,而组件还可以引入子组件。
import wepy from 'wepy'
// 引入child组件文件
import Child from '../components/child';
export default class List extends wepy.component {
//该处是wepy.component,且加上类名加以区分
components = {
// 声明页面中要使用到的Child组件的ID为child
child: Child
}
}
4、mixins目录下的文件介绍
// mixins是放混合组件的地方,用于复用不同组件中的相同功能。
// 例如:MyMixin.js
import wepy from 'wepy'
export default class MyMixin extends wepy.mixin {
//该处是wepy.mixin,且加上类名加以区分
}
// mycom.js
import MyMixin from './mymixin';
export class MyCom extends wepy.component {
mixins = [MyMixin];
}
5、wepy.config.js文件介绍
// wepy.config.js是webpack配置文件
// 该文件可配置环境变量来改变运行时的参数
wpyExt: '.wpy', ---文件后缀名设置
eslint: false, ---关闭eslint校验
resolve: {
alias: {
counter: path.join(__dirname, 'src/components/counter'),
'@': path.join(__dirname, 'src') //配置文件路径代码
},
aliasFields: ['wepy', 'weapp'],
modules: ['node_modules']
},
一、 wepy项目中使用async await
官方指出链接需要在该文件下配置如下语句:
babel: {
"presets": [
"env"
],
"plugins": [
"transform-export-extensions",
"syntax-export-extensions"
]
}
二、 wepy根据环境变量来改变运行时的参数
3-wepy优化之处
1. wx.request 接收参数修改
// 原生代码:
wx.request({
url: 'xxx',
success: function (data) {
console.log(data);
}
});
// WePY 使用方式, 需要开启 Promise 支持
wepy.request('xxxx').then((d) => console.log(d));
// async/await 的使用方式, 需要开启 Promise 和 async/await 支持
async function request () {
let d = await wepy.request('xxxxx');
console.log(d);
}
2. 优化事件参数传递
// 原生的事件传参方式:
Click me!
Page({
tapName: function (event) {
console.log(event.currentTarget.dataset.id)// output: 1
console.log(event.currentTarget.dataset.title)// output: wepy
console.log(event.currentTarget.dataset.other)// output: otherparams
}
});
// WePY 1.1.8以后的版本,只允许传string。
// 事件响应以及组件通讯事件参数顺序调整,将$event移至末尾,即最后一个参数为事件参数。
Click me!
methods: {
tapName (id, title, other, event) {
console.log(id, title, other)// output: 1, wepy, otherparams
}
}
// 蒙层弹窗出现与隐藏
...
...
data = {
flags: {
layerRule: true,
layerPrize: true,
...
}
}
//出现
showLayer (e,layerName) {
let key = layerName.currentTarget.dataset.wpyshowlayerA; //优化data-,此时dataset结点后的字段名框架自动生成,
为wpy + 函数名(小写) + 大写26个字母中的一个,
由于我上面只传了一个参数,则此时e代表的就是此时传的第一个参数名。
// 记住:最后一个才会是事件名,所有的事件都绑在最后一个参数上。
this.flags[key] = false;
},
//消失
hideLayer (e,layerName) {
let key = layerName.currentTarget.dataset.wpyhidelayerA;
this.flags[key] = true;
},
3.动态绑定class
// 在vue中动态绑定class
// 在wepy中,要使用微信原生的绑定语法
// 其中 class-a 是不需要动态绑定的class, 双括号中才是需要绑定的class
4.新增interceptor拦截器
可以使用WePY提供的全局拦截器对原生API的请求进行拦截。
具体方法是配置API的config、fail、success、complete回调函数。参考示例:
import wepy from 'wepy';
export default class extends wepy.app {
constructor () {
// this is not allowed before super()
super();
// 拦截request请求
this.intercept('request', {
// 发出请求时的回调函数
config (p) {
// 对所有request请求中的OBJECT参数对象统一附加时间戳属性
p.timestamp = +new Date();
console.log('config request: ', p);
// 必须返回OBJECT参数对象,否则无法发送请求到服务端
return p;
},
// 请求成功后的回调函数
success (p) {
// 可以在这里对收到的响应数据对象进行加工处理
console.log('request success: ', p);
// 必须返回响应数据对象,否则后续无法对响应数据进行处理
return p;
},
//请求失败后的回调函数
fail (p) {
console.log('request fail: ', p);
// 必须返回响应数据对象,否则后续无法对响应数据进行处理
return p;
},
// 请求完成时的回调函数(请求成功或失败都会被执行)
complete (p) {
console.log('request complete: ', p);
}
});
}
}
4-wepy组件通信
组件传值
// wepy.component基类提供$broadcast、$emit、$invoke三个方法用于组件之间的通信和交互
· $broadcast:父组件触发所有子组件事件
· $emit:子组件触发父组件事件
· $invoke:子组件触发子组件事件
注意:可以以$标识符来获取wepy框架内建属性和方法。$name:String: 组件名称。
$broadcast使用案例:
$broadcast事件是由父组件发起,所有子组件都会收到此广播事件,除非事件被手动取消。事件广播的顺序为广度优先搜索顺序。
// index.wpy(pages页面) ---父组件
组件通信
---子组件标签
import List from '../components/list'
export default class Index extends wepy.page {
components = {
list: List
}
methods = {
communicate () {
this.$broadcast('index-broadcast')
}
}
}
// list.wpy(components页面) ---子组件
// events对象中所声明的函数为用于监听组件之间的通信与交互事件的事件处理函数
events = {
'index-broadcast': (...args) => {
let $event = args[args.length - 1]
console.log(`${this.$name} receive ${$event.name} from ${$event.source.name}`) // list receive index-broadcast from undefined
}
}
$emit使用案例:
$emit与$broadcast正好相反,事件发起组件的所有祖先组件会依次接收到$emit事件。
下面通过这个例子来说明
// index.wpy(pages页面) ---父组件
测试组件
计数组件1:
//自定义组件绑定事件使用.user,其中@表示事件修饰符,
index-emit 表示事件名称,.user表示事件后缀。
// 目前总共有三种事件后缀:
// .default: 绑定小程序冒泡型事件,如bindtap,.default后缀可省略不写;
// .stop: 绑定小程序捕获型事件,如catchtap;
// .user: 绑定用户自定义组件事件,通过$emit触发。注意,如果用了自定义事件,则events中对应的监听函数不会再执行。
计数组件2:
methods = {
counterEmit (...args) {
let $event = args[args.length - 1]
console.log(`${this.$name} receive ${$event.name} from ${$event.source.$name} counterEmit`) // Index receive index-emit from counter1 counterEmit
}
}
events = {
'index-emit': (...args) => {
let $event = args[args.length - 1]
console.log(`${this.$name} receive ${$event.name} from ${$event.source.$name}`) // Index receive index-emit from counter2
}
}
// count.wpy(components页面) ---子组件
+
-
{{num}}
methods = {
plus () {
this.num = this.num + 1
console.log(this.$name + ' plus tap')
this.$emit('index-emit')
},
minus () {
this.num = this.num - 1
console.log(this.$name + ' minus tap')
}
}
$invoke使用案例:
$invoke是一个页面或组件对另一个组件中的方法的直接调用,通过传入组件路径找到相应的组件,然后再调用其方法。
比如,想在页面Page_Index中调用组件ComA的某个方法:
this.$invoke('ComA', 'someMethod', 'someArgs');
如果想在组件ComA中调用组件ComG的某个方法:
this.$invoke('./../ComB/ComG', 'someMethod', 'someArgs');
5-wepy的API使用
wepy 封装的属性,可以获取globalData、$wxapp等
1.$instance 全局实例封装
//wepy.app Class
//属性
1.$wxapp:Object 等同于 getApp()
2.$pages:List 所有列表页面
3.$interceptors:List 所有拦截器列表
//方法
4.$intercept:(api:String, Probider:Object) 使用拦截器对原生API请求进行拦截
5.use(middleWare:String|Function) 使用中间件
//wepy.component Class
//属性
1.$name:String 组件名称
2.$isComponent:Boolean 是否是组件,如果是页面,此值为false
3.$wxpage:Object 小程序原生page
4.$parent:Page|App 组件的父组件,如果当前是组件是page对象,那么$parent的值为App对象
5.$root:Page 组件所在的Page对象,如果当前组件是Page对象,那么$root的值就是自己本身。
6.$coms:List 组件的子组件列表
7.$mixins:Array[Mixin] 组件所注入的Mixin对象
8.data:Object 组件需要响应的事件列表
9.methods:List 组件需要响应的事件列表
10.props:List 组件允许传递的props列表
11.events:List 组件通信时所需要的事件表现
//方法
1.setData(key:String|Object, [value:Object]) 对原有小程序的setData的封装(wepy的赃查检流程会自动执行setData操作,一般不需要使用)
2.getCurrentPages()
3.$getComponent(com:String) 通过组件名称路径查找组件对象
4.$invoke(com:String|Component) 调用另一组件的方法。优先调用methods中方法,如果方法不存在,则调用组件的自定义方法,调用自定义方法时,不会传递事件$event。
5.$broadcast(eventName:String,[args]) 组件发起一个广播事件
向所有子组件发起一个广播事件,事件会依次传播直至所有子组件遍历完毕或者事件被手动终止传播。
6.$emit(eventName:String,[args]) 组件发起一个冒泡事件
向父组件发起一个冒泡事件,事件会向上冒泡直至Page或者者事件被手动终止传播。
7.$apply([func:Function]) 组件发起脏检查
正常流程下,改变数据后,组件会在流程结束时自动触发脏检查。 在异步或者回调流程中改变数据时,需要手动调用$apply方法。
this.userName = 'Gcaufy';
this.$apply();
this.$apply(() => {
this.userName = 'Gcaufy';
});
8.$nextTick(func:Function) 组件数据绑定完成后的回调事件
数据绑定后的回调事件,在不传入function时,返回一个promise对象
this.userName = 'Gcaufy';
this.$nextTick(function () {
console.log('UI updated');
});
this.userName = 'Gcaufy';
this.$nextTick().then(function () {
console.log('UI updated');
});
//wepy.page Class
//属性 全部属性继承自wepy.component
//方法
1.$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);
}
2.$redirect(url:String|Object, [params:Object]) wx.redirectTo的封装方法
this.$redirect('./page2', {a: 1, b: 2});
this.$redirect({
url: './pages?a=1&b=2'
});
3.$navigate(url:String|Object,[params:Object]) wx.navigateTo的封装方法
4.$switch(url:String|Object) wx.switchTab的封装方法
// wepy.event Class 小程序事件封装类
new wepy.event(name:String, source:Component, type:String)
//属性
1.name(String) 事件名称
当事件为小程序原生事件时,如tap,change等,name值为system。 当事件为用户自定事件或者组件通信事件时,如$emit,$broadcast等,name值为自定事件的名称。
2.source(Component) 事件来源组件
无论是小程序原生事件还是自定事件,都会有对应的事件来源组件。
3.type(String) 事件类型
$emit事件中,type值为emit。bindtap事件中,type值为tap。
//方法
1.$destory() 终止事件传播
在$emit或者$broadcast事件中,调用$destroy事件终止事件的传播。
2.$transfor(wxevent:Object) 将内部小程序事件的属性传递到当前事件
6-wepy注意事项
与Vue开发不同之处
1、methods方法使用不同:methods方法中只用于声明页面wxml中标签的bind、catch事件,自定义方法需以自定义方法的方式声明。
2、命名规范不同:template里面组件组件标签命名使用驼峰式命名(即comChild),而不是短横杠式命名(com-child)。
3、响应事件顺序不同:对于组件methods响应事件,以及小程序页面事件将采用兼容式混合,
即先响应组件本身响应事件,然后再响应混合对象(mixin)中响应事件。
注意,这里事件的执行顺序跟Vue中相反,Vue中是先执行mixin中的函数, 再执行组件本身的函数。
4、wepy中也有computed,props,slot,data,watch等vue中有的一些属性(没有filter, directive)
props,slot,data,watch和vue基本无异,其中computed计算属性是无法传参的。
5、wepy中props传递需要加上.sync修饰符(类似VUE1.x)才能实现props动态更新,
并且父组件再变更传递给子组件props后要执行this.$apply()方法才能更新。
关于props动态传值,可以通过设置子组件props的twoWay: true来达到子组件数据绑定至父组件的效果。
那如果既使用.sync修饰符,同时子组件props中添加的twoWay: true时,就可以实现数据的双向绑定了。
6、wepy支持数据双向绑定,子组件在定义props时加上twoway:true属性值即可实现子组件修改父组件数据。
7、VUE2.x推荐使用eventBus方式进行组件通信,而在wepy中是通过$broadcast,$emit,$invoke 三种方法实现通信。
8、VUE的生命周期包括created、mounted等,wepy仅支持小程序的生命周期:onLoad、onReady等。
9、wepy不支持过滤器、keep-alive、ref、transition、全局插件、路由管理、服务端渲染等VUE特性技术。
与原生开发不同之处
1、数据绑定写法不一:this.title = 'this is title'; 替换 this.setData({title: 'this is title'});
注意:在异步函数中更新数据的时候,必须手动调用$apply方法,才会触发脏数据检查流程的运行。
setTimeout(() => {
this.title = 'this is title';
this.$apply();
}, 3000);
2、组件的循环渲染新增repeat标签,其中该标签不能添加类名,即不能添加样式。
3、wepy框架对原生API请求进行封装了,可以使用拦截器就行拦截。
4、wepy框架封装的方法都是Promise,不是Object,一些原生方法返回的是Object,可以直接获取到方法的返回对象。
7-wepy报错记录
1. 记录一:wepy.getUpdateManager()是Promise,不是对象
8-wepy踩坑记录
官方已经特别指出并给出解决办法
1、在部份机型上使用display: flex;会出现兼容性问题
官方指出链接
解决办法:使用less时,建议加上autoprefix插件,步骤如下:
一、安装插件
npm install less-plugin-autoprefix --save-dev
二、配置插件 在wepy.config.js中加入
const LessPluginAutoPrefix = require('less-plugin-autoprefix');
compilers: {
less: {
compress: true,
plugins: [new LessPluginAutoPrefix({browsers: ['Android >= 2.3', 'Chrome > 20', 'iOS >= 6']})]
}
一些自己遇到的问题以及给出解决办法
1、BindInput与async冲突
微信小程序的bindinput:键盘输入时触发,event.detail = {value, cursor, keyCode},keyCode 为键值,2.1.0 起支持,处理函数可以直接 return 一个字符串,将替换输入框的内容。
当回调函数被async修饰,返回的会是promise,这导致输入框内容被替换。
只好先调用一个普通的函数,然后再调用async函数。
// template
// methods input的内容会被改变
methods = {
async searchInput(e) {
let value = e.detail.value;
// some code using await
// ……
}
}
// fix
methods = {
searchInput(e) {
let value = e.detail.value;
// ……
this.f();
// ……
}
}
// 自定义方法直接定义在类中,不能放在methods下
async f()
{
// ……
}