簡單介紹: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() { // …… }