简单介绍:wepy是一个微信小程序框架,支持模块化开发,开发风格相似Vue.js。可搭配redux使用,能同时打包出web和小程序。vue
全局安装或更新WePY命令行工具
npm install -g wepy-cli
node
建立项目
wepy init standard my-project[项目名]
webpack
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[项目目录]
git
安装依赖
npm install
github
开启实时编译
npm run dev
web
构建项目完整目录
// template、style、script三大标签,有lang、src属性,当src属性存在文件,那么其内部代码会被忽略。 // app.apy小程序入口文件 <style lang="less"> @import "./styles/gb750"; --- 编译成app.wxss文件,能够外部引用 </style> <script> --- 编译成app.js文件 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 () {} } </script>
// pages目录下存放主页面,代码编写与app.wpy类似,不一样之处以下: // 由于app.wpy不须要template,但pages目录下的页面须要 // 在Pages下的页面实例中,能够经过this.$parent来访问App实例。 // Page页面继承自Component组件,即Page也是组件。除扩展了页面所特有的config配置以及特有的页面生命周期函数以外, 其它属性和方法与Component一致。 <template> --- 编译成index.wxml文件,只有pages目录下的template会编译成wxml文件 <counter1></counter1> ---组件标签 <counter2></counter2> </template> <script> 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 = {} // 声明组件之间的事件处理函数 } </script>
// components目录下存放组件 // 页面能够引入组件,而组件还能够引入子组件。 <template> <!-- 注意,使用for属性,而不是使用wx:for属性 --> <repeat for="{{list}}" key="index" index="index" item="item"> <!-- 插入<script>脚本部分所声明的child组件,同时传入item --> <child :item="item"></child> </repeat> </template> import wepy from 'wepy' // 引入child组件文件 import Child from '../components/child'; <script> export default class List extends wepy.component { //该处是wepy.component,且加上类名加以区分 components = { // 声明页面中要使用到的Child组件的ID为child child: Child } } </script>
// 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]; }
// 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'] },
官方指出连接须要在该文件下配置以下语句:npm
babel: { "presets": [ "env" ], "plugins": [ "transform-export-extensions", "syntax-export-extensions" ] }
官方指出连接json
// 原生代码: 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); }
// 原生的事件传参方式: <view data-id="{{index}}" data-title="wepy" data-other="otherparams" bindtap="tapName"> Click me! </view> 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移至末尾,即最后一个参数为事件参数。 <view @tap="tapName({{index}}, 'wepy', 'otherparams')"> Click me! </view> methods: { tapName (id, title, other, event) { console.log(id, title, other)// output: 1, wepy, otherparams } } // 蒙层弹窗出现与隐藏 <view @tap="showLayer('layerRule')"></view> <view @tap="showLayer('layerPrize')"></view> ... <view hidden='{{flags.layerRule}}'> <image src="" @tap="hideLayer('layerRule')"/> </view> <view hidden='{{flags.layerPrize}}'> <image src="" @tap="hideLayer('layerPrize')"/> </view> ... 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; },
// 在vue中动态绑定class <div class="class-a" :class="{true ? 'class-b': 'class-c'}"></div> // 在wepy中,要使用微信原生的绑定语法 <view class="class-a {{true ? 'class-b' : 'class-c'}}"> // 其中 class-a 是不须要动态绑定的class, 双括号中才是须要绑定的class
可使用WePY提供的全局拦截器对原生API的请求进行拦截。
具体方法是配置API的config、fail、success、complete回调函数。参考示例:redux
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); } }); } }
组件传值
// wepy.component基类提供$broadcast、$emit、$invoke三个方法用于组件之间的通讯和交互 · $broadcast:父组件触发全部子组件事件 · $emit:子组件触发父组件事件 · $invoke:子组件触发子组件事件 注意:能够以$标识符来获取wepy框架内建属性和方法。$name:String: 组件名称。
$broadcast使用案例:
$broadcast事件是由父组件发起,全部子组件都会收到此广播事件,除非事件被手动取消。事件广播的顺序为广度优先搜索顺序。
// index.wpy(pages页面) ---父组件 <template> <button @tap="communicate" size="mini">组件通讯</button> <list></list> ---子组件标签 </template> <script> import List from '../components/list' export default class Index extends wepy.page { components = { list: List } methods = { communicate () { this.$broadcast('index-broadcast') } } } </script> // list.wpy(components页面) ---子组件 <script> // 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 } } </script>
$emit使用案例:
$emit与$broadcast正好相反,事件发起组件的全部祖先组件会依次接收到$emit事件。
下面经过这个例子来讲明
// index.wpy(pages页面) ---父组件 <template> <panel> <view class="title" slot="title">测试组件</view> <text class="testcounter">计数组件1: </text> <view class="counterview"> <counter1 @index-emit.user="counterEmit" /> //自定义组件绑定事件使用.user,其中@表示事件修饰符, index-emit 表示事件名称,.user表示事件后缀。 // 目前总共有三种事件后缀: // .default: 绑定小程序冒泡型事件,如bindtap,.default后缀可省略不写; // .stop: 绑定小程序捕获型事件,如catchtap; // .user: 绑定用户自定义组件事件,经过$emit触发。注意,若是用了自定义事件,则events中对应的监听函数不会再执行。 </view> <text class="testcounter">计数组件2: </text> <view class="counterview"> <counter2 :num.sync="mynum"></counter2> </view> </panel> </template> <script> 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 } } </script> // count.wpy(components页面) ---子组件 <template> <view class="counter {{style}}"> <button @tap="plus" size="mini"> + </button> <button @tap="minus" size="mini"> - </button> <text class="count" :class="{red: num > 55, green: num < 45}"> {{num}} </text> </view> </template> <script> 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') } } </script>
$invoke使用案例:
$invoke是一个页面或组件对另外一个组件中的方法的直接调用,经过传入组件路径找到相应的组件,而后再调用其方法。
好比,想在页面Page_Index中调用组件ComA的某个方法:
this.$invoke('ComA', 'someMethod', 'someArgs');
若是想在组件ComA中调用组件ComG的某个方法:
this.$invoke('./../ComB/ComG', 'someMethod', 'someArgs');
wepy 封装的属性,能够获取globalData、$wxapp等
1.$instance 全局实例封装
//wepy.app Class //属性 1.$wxapp:Object 等同于 getApp() 2.$pages:List<Page> 全部列表页面 3.$interceptors:List<Object> 全部拦截器列表 //方法 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<Component> 组件的子组件列表 7.$mixins:Array[Mixin] 组件所注入的Mixin对象 8.data:Object 组件须要响应的事件列表 9.methods:List<Function> 组件须要响应的事件列表 10.props:List<Props> 组件容许传递的props列表 11.events:List<Function> 组件通讯时所须要的事件表现 //方法 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) 将内部小程序事件的属性传递到当前事件
与Vue开发不一样之处
一、methods方法使用不一样:methods方法中只用于声明页面wxml中标签的bind、catch事件,自定义方法需以自定义方法的方式声明。 二、命名规范不一样:template里面组件组件标签命名使用驼峰式命名(即comChild),而不是短横杠式命名(com-child)。 三、响应事件顺序不一样:对于组件methods响应事件,以及小程序页面事件将采用兼容式混合, 即先响应组件自己响应事件,而后再响应混合对象(mixin)中响应事件。 注意,这里事件的执行顺序跟Vue中相反,Vue中是先执行mixin中的函数, 再执行组件自己的函数。 四、wepy中也有computed,props,slot,data,watch等vue中有的一些属性(没有filter, directive) props,slot,data,watch和vue基本无异,其中computed计算属性是没法传参的。 五、wepy中props传递须要加上.sync修饰符(相似VUE1.x)才能实现props动态更新, 而且父组件再变动传递给子组件props后要执行this.$apply()方法才能更新。 关于props动态传值,能够经过设置子组件props的twoWay: true来达到子组件数据绑定至父组件的效果。 那若是既使用.sync修饰符,同时子组件props中添加的twoWay: true时,就能够实现数据的双向绑定了。 六、wepy支持数据双向绑定,子组件在定义props时加上twoway:true属性值便可实现子组件修改父组件数据。 七、VUE2.x推荐使用eventBus方式进行组件通讯,而在wepy中是经过$broadcast,$emit,$invoke 三种方法实现通讯。 八、VUE的生命周期包括created、mounted等,wepy仅支持小程序的生命周期:onLoad、onReady等。 九、wepy不支持过滤器、keep-alive、ref、transition、全局插件、路由管理、服务端渲染等VUE特性技术。
与原生开发不一样之处
一、数据绑定写法不一:this.title = 'this is title'; 替换 this.setData({title: 'this is title'}); 注意:在异步函数中更新数据的时候,必须手动调用$apply方法,才会触发脏数据检查流程的运行。 setTimeout(() => { this.title = 'this is title'; this.$apply(); }, 3000); 二、组件的循环渲染新增repeat标签,其中该标签不能添加类名,即不能添加样式。 三、wepy框架对原生API请求进行封装了,可使用拦截器就行拦截。 四、wepy框架封装的方法都是Promise,不是Object,一些原生方法返回的是Object,能够直接获取到方法的返回对象。
官方已经特别指出并给出解决办法
官方指出连接
解决办法:使用less时,建议加上autoprefix插件,步骤以下:
npm install less-plugin-autoprefix --save-dev
const LessPluginAutoPrefix = require('less-plugin-autoprefix'); compilers: { less: { compress: true, plugins: [new LessPluginAutoPrefix({browsers: ['Android >= 2.3', 'Chrome > 20', 'iOS >= 6']})] }
一些本身遇到的问题以及给出解决办法
微信小程序的bindinput:键盘输入时触发,event.detail = {value, cursor, keyCode},keyCode 为键值,2.1.0 起支持,处理函数能够直接 return 一个字符串,将替换输入框的内容。
当回调函数被async修饰,返回的会是promise,这致使输入框内容被替换。
只好先调用一个普通的函数,而后再调用async函数。
// template <input bindinput="searchInput" name="input" placeholder="输入搜索信息"/> // 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() { // …… }