wepy.js借鑒了Vue的語法風格和功能特性,對官方提供的框架進行了封裝,更貼近於MVVM架構模式,讓開發者更加容易上手,增加開發效率。(臟數據處理--是否有標識、是否有響應)
前端開發的對組件化開發應該都都很熟悉,我們就講精華的部分:
首先我們需要輸入下面的命令:
npm install wepy-cli -g // 全局安裝或更新WePY命令行工具 wepy new myproject // 在開發目錄中生成Demo開發項目 cd myproject // 切換至項目目錄 npm install // 安裝依賴 wepy build --watch // 開啟實時編譯
完成上面的命令后,你會看到類似下面的目錄結構(部分文件夾是本人自己的項目業務需要,學習不需要理會):
目錄講解:
--dis
文件夾是微信開發者工具指定的目錄(該目錄由WePY的build指令自動編譯生成,請不要直接修改該目錄下的文件)
--src
代碼編寫的目錄(該目錄為使用WePY后的開發目錄)
--components
wepy組件目錄(組件不屬於完整頁面,僅供完整頁面或其他組件引用)
--pages
wepy頁面目錄(屬於完整頁面)
--app.wpy
小程序邏輯、公共配置、公共樣式
--package.json
項目的package配置
wepy官方文檔有五個重要的提示,我們要跟着規則做,不要給自己挖坑。
-
使用
微信開發者工具
-->添加項目
,項目目錄
請選擇dist
目錄。 -
微信開發者工具
-->項目
-->關閉ES6轉ES5
。 重要:漏掉此項會運行報錯。 -
微信開發者工具
-->項目
-->關閉上傳代碼時樣式自動補全
。 重要:某些情況下漏掉此項也會運行報錯。 -
微信開發者工具
-->項目
-->關閉代碼壓縮上傳
。 重要:開啟后,會導致真機computed, props.sync 等等屬性失效。(注:壓縮功能可使用WePY提供的build指令代替,詳見后文相關介紹以及Demo項目根目錄中的wepy.config.js
和package.json
文件。) -
本地項目根目錄運行
wepy build --watch
,開啟實時編譯。(注:如果同時在微信開發者工具
-->設置
-->編輯器
中勾選了文件保存時自動編譯小程序
,將可以實時預覽,非常方便。)
用於開發的編輯器,個人喜歡vs code。開發者可以根據自己熟悉的編輯器(微信開發者工具
僅用於實時預覽和調試)。
- VS Code
1. 在 Code 里先安裝 Vue 的語法高亮插件 Vetur
。
2. 打開任意 .wpy
文件。
3. 點擊右下角的選擇語言模式,默認為純文本
。
4. 在彈出的窗口中選擇 .wpy 的配置文件關聯...
。
5. 在選擇要與 .wpy 關聯的語言模式
中選擇 Vue
。
wepy官方提供了一些代碼規范的規則,這里我列出重要的幾條:
1.變量與方法盡量使用駝峰式命名,並且注意避免使用$
開頭。
2.事件綁定語法使用優化語法代替。
* 原 bindtap="click"
替換為 @tap="click"
,原catchtap="click"
替換為@tap.stop="click"
。
* 原 capture-bind:tap="click"
替換為 @tap.capture="click"
,原capture-catch:tap="click"
替換為@tap.capture.stop="click"
。
3.事件傳參使用優化后語法代替。 原bindtap="click" data-index={{index}}
替換為@tap="click({{index}})"
。
4.自定義組件命名應避開微信原生組件名稱以及功能標簽<repeat>
。 不可以使用input、button、view、repeat
等微信小程序原生組件名稱命名自定義組件;另外也不要使用WePY框架定義的輔助標簽repeat
命名。
基於WePY的代碼:
export default class Catalog extends wepy.page //通過繼承自wepy.page的類創建頁面邏輯
methods //事件處理函數(集中保存在methods對象中)
components //支持組件化開發如下圖
注意:WePY中,在父組件template
模板部分插入駝峰式命名的子組件標簽時,不能將駝峰式命名轉換成短橫桿式命名(比如將childCom
轉換成child-com
),這與Vue中的習慣是不一致。
wepy基於原生進行優化:
(使用原生同時最多只能發起5個請求,使用wepy后沒有了這個限制)
import wepy from 'wepy'; async onLoad() { await wepy.login(); this.userInfo = await wepy.getUserInfo(); }
一個.wpy
文件可分為三大部分,各自對應於一個標簽:
- 腳本部分,即
<script></script>
標簽中的內容,又可分為兩個部分:
邏輯部分,除了config對象之外的部分,對應於原生的.js
文件;
配置部分,即config對象,對應於原生的.json
文件。
-
結構部分,即
<template></template>
模板部分,對應於原生的.wxml
文件。 -
樣式部分,即
<style></style>
樣式部分,對應於原生的.wxss
文件。
其中,小程序入口文件app.wpy
不需要template
,所以編譯時會被忽略。.wpy
文件中的script
、template
、style
這三個標簽都支持lang
和src
屬性,lang
決定了其代碼編譯過程,src
決定是否外聯代碼,存在src
屬性且有效時,會忽略內聯代碼。
代碼如下
<style lang="less" src="page1.less"></style> <template lang="wxml" src="page1.wxml"></template> <script> // some code </script>
各標簽對應的lang
值如下表所示:
標簽 | lang默認值 | lang支持值 |
---|---|---|
style | css |
css 、less 、scss 、stylus |
template | wxml |
wxml 、xml 、pug(原jade) |
script | babel |
babel 、TypeScript |
下面具體對腳本進行講解,
小程序的入口是app.wpy
<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> <style lang="less"> /** less **/ </style>
入口文件app.wpy
中所聲明的小程序實例繼承自wepy.app
類,包含一個config
屬性和其它全局屬性、方法、事件。其中config
屬性對應原生的app.json
文件,build編譯時會根據config
屬性自動生成app.json
文件,如果需要修改config
中的內容,請使用微信提供的相關API。
頁面page.wpy
<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
類,該類的主要屬性介紹如下:
屬性 | 說明 |
---|---|
config | 頁面配置對象,對應於原生的page.json 文件,類似於app.wpy 中的config |
components | 頁面組件列表對象,聲明頁面所引入的組件列表 |
data | 頁面渲染數據對象,存放可用於頁面模板綁定的渲染數據 |
methods | wxml事件處理函數對象,存放響應wxml中所捕獲到的事件的函數,如bindtap 、bindchange |
events | WePY組件事件處理函數對象,存放響應組件之間通過$broadcast 、$emit 、$invoke 所傳遞的事件的函數 |
其它 | 小程序頁面生命周期函數,如onLoad 、onReady 等,以及其它自定義的方法與屬性 |
組件com.wpy
<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
配置以及頁面特有的一些生命周期函數之外,其屬性與頁面屬性大致相同。
通過上面的總結,我們可以知道,小程序被分成三個實例:小程序實例App
、頁面實例Page
、組件實例Component
import wepy from 'wepy'; // 聲明一個App小程序實例 export default class MyAPP extends wepy.app { } // 聲明一個Page頁面實例 export default class IndexPage extends wepy.page { } // 聲明一個Component組件實例 export default class MyComponent extends wepy.component { }
App小程序實例中主要包含小程序生命周期函數、config配置對象、globalData全局數據對象,以及其他自定義方法與屬性。
在Page頁面實例中,可以通過this.$parent
來訪問App實例。
Page頁面實際上繼承自Component組件,即Page也是組件。除擴展了頁面所特有的config
配置以及特有的頁面生命周期函數之外,其它屬性和方法與Component一致,因此這里以Page頁面為例進行介紹。
注意,對於WePY中的methods屬性,因為與Vue中的使用習慣不一致,非常容易造成誤解,這里需要特別強調一下:WePY中的methods屬性只能聲明頁面wxml標簽的bind、catch事件,不能聲明自定義方法,這與Vue中的用法是不一致的。示例如下:
import wepy from 'wepy'; export default class MyComponent extends wepy.component { methods = { bindtap () { let rst = this.commonFunc(); // doSomething }, bindinput () { let rst = this.commonFunc(); // doSomething }, } //正確:普通自定義方法在methods對象外聲明,與methods平級 customFunction () { return 'sth.'; } }
需要特別注意的一點:當需要循環渲染WePY組件時(類似於通過wx:for
循環渲染原生的wxml標簽),必須使用WePY定義的輔助標簽<repeat>
,代碼如下:
<template> <!-- 注意,使用for屬性,而不是使用wx:for屬性 --> <repeat for="{{list}}" key="index" index="index" item="item"> <!-- 插入<script>腳本部分所聲明的child組件,同時傳入item --> <child :item="item"></child> </repeat> </template>
computed計算屬性、watcher監聽器跟VUE很相似。
// computed data = { a: 1 } // 計算屬性aPlus,在腳本中可通過this.aPlus來引用,在模板中可通過{{ aPlus }}來插值 computed = { aPlus () { return this.a + 1 } } // wathcer data = { num: 1 } // 監聽器函數名必須跟需要被監聽的data對象中的屬性num同名, // 其參數中的newValue為屬性改變后的新值,oldValue為改變前的舊值 watch = { num (newValue, oldValue) { console.log(`num value: ${oldValue} -> ${newValue}`) } } // 每當被監聽的屬性num改變一次,對應的同名監聽器函數num()就被自動調用執行一次 onLoad () { setInterval(() => { this.num++; this.$apply(); }, 1000) }
Props傳值:
props傳值在WePY中屬於父子組件之間傳值的一種機制,包括靜態傳值與動態傳值。
在props對象中聲明需要傳遞的值,靜態傳值與動態傳值的聲明略有不同,具體可參看下面的示例代碼。
靜態傳值
靜態傳值為父組件向子組件傳遞常量數據,因此只能傳遞String字符串類型。
在父組件template
模板部分的組件標簽中,使用子組件props對象中所聲明的屬性名作為其屬性名來接收父組件傳遞的值。
<child title="mytitle"></child> // child.wpy props = { title: String }; onLoad () { console.log(this.title); // mytitle }
動態傳值
動態傳值是指父組件向子組件傳遞動態數據內容,父子組件數據完全獨立互不干擾。但可以通過使用.sync
修飾符來達到父組件數據綁定至子組件的效果,也可以通過設置子組件props的twoWay: true
來達到子組件數據綁定至父組件的效果。那如果既使用.sync
修飾符,同時子組件props
中添加的twoWay: true
時,就可以實現數據的雙向綁定了。
注意:下文示例中的twoWay
為true
時,表示子組件向父組件單向動態傳值,而twoWay
為false
(默認值,可不寫)時,則表示子組件不向父組件傳值。這是與Vue不一致的地方,而這里之所以仍然使用twoWay
,只是為了盡可能保持與Vue在標識符命名上的一致性。
在父組件template
模板部分所插入的子組件標簽中,使用:prop
屬性(等價於Vue中的v-bind:prop
屬性)來進行動態傳值。
// parent.wpy <child :title="parentTitle" :syncTitle.sync="parentTitle" :twoWayTitle="parentTitle"></child> data = { parentTitle: 'p-title' }; // child.wpy props = { // 靜態傳值 title: String, // 父向子單向動態傳值 syncTitle: { type: String, default: 'null' }, twoWayTitle: { type: Number, default: 'nothing', twoWay: true } }; onLoad () { console.log(this.title); // p-title console.log(this.syncTitle); // p-title console.log(this.twoWayTitle); // p-title this.title = 'c-title'; console.log(this.$parent.parentTitle); // p-title. this.twoWayTitle = 'two-way-title'; this.$apply(); console.log(this.$parent.parentTitle); // two-way-title. --- twoWay為true時,子組件props中的屬性值改變時,會同時改變父組件對應的值 this.$parent.parentTitle = 'p-title-changed'; this.$parent.$apply(); console.log(this.title); // 'c-title'; console.log(this.syncTitle); // 'p-title-changed' --- 有.sync修飾符的props屬性值,當在父組件中改變時,會同時改變子組件對應的值。 }
組件之間的通信:
wepy.component
基類提供$broadcast
、$emit
、$invoke
三個方法用於組件之間的通信和交互,如:
this.$emit('some-event', 1, 2, 3, 4);
import wepy from 'wepy' export default class Com extends wepy.component { components = {}; data = {}; methods = {}; // events對象中所聲明的函數為用於監聽組件之間的通信與交互事件的事件處理函數 events = { 'some-event': (p1, p2, p3, $event) => { console.log(`${this.$name} receive ${$event.name} from ${$event.source.$name}`); } }; // Other properties }
$broadcast
事件是由父組件發起,所有子組件都會收到此廣播事件,除非事件被手動取消。
$emit
與$broadcast
正好相反,事件發起組件的所有祖先組件會依次接收到$emit
事件。
$invoke
是一個頁面或組件對另一個組件中的方法的直接調用,通過傳入組件路徑找到相應的組件,然后再調用其方法。
組件自定義事件處理:
可以通過使用.user
修飾符為自定義組件綁定事件,如:@customEvent.user="myFn"
其中,@
表示事件修飾符,customEvent
表示事件名稱,.user
表示事件后綴。
目前總共有三種事件后綴:
-
.default
: 綁定小程序冒泡型事件,如bindtap
,.default
后綴可省略不寫; -
.stop
: 綁定小程序捕獲型事件,如catchtap
; -
.user
: 綁定用戶自定義組件事件,通過$emit
觸發。注意,如果用了自定義事件,則events中對應的監聽函數不會再執行。
// index.wpy <template> <child @childFn.user="parentFn"></child> </template> <script> import wepy from 'wepy' import Child from '../components/child' export default class Index extends wepy.page { components = { child: Child } methods = { parentFn (num, evt) { console.log('parent received emit event, number is: ' + num) } } } </script> // child.wpy <template> <view @tap="tap">Click me</view> </template> <script> import wepy from 'wepy' export default class Child extends wepy.component { methods = { tap () { console.log('child is clicked') this.$emit('childFn', 100) } } } </script>
當然,所有人寫的總結都是基於自己的理解的,因此,個人覺得無論學什么技術,當自己有一定能力可以閱讀官方文檔后,最好自己把官方的文檔都看一遍,只有這樣自己的該門技術才有深入的理解。
官方文檔地址:https://tencent.github.io/wepy/document.html#/?id=api
如果你閱讀到這里了,說明你是個有追求的人,本人額外附送兩個鏈接
https://github.com/hjkcai/wepy-plugin-axios
https://github.com/youzan/zanui-weapp