WePY 1.X 項目的創建與使用
前端開發框架和環境都是需要 Node.js ,先安裝 node.js 開發環境,WePY 借鑒了 Vue.js(后文簡稱Vue)的語法風格和功能特性,vue的運行是要依賴於 node 的 npm 的管理工具來實現,安裝過程前面寫過,這里就不再寫了。
WePY的安裝或更新都通過 npm 進行。
全局安裝或更新WePY命令行工具:
npm install wepy-cli -g
在開發目錄中生成Demo開發項目:
# 初始化項目,使用 wepy list 查看項目模板 # 1.7.0 之前的版本使用 wepy new myproject # 1.7.0 以后使用下面 $ wepy init standard myproject
注意:這是有內容的文件夾(有各種示例),可以建空的文件夾
$ wepy init empty name #空文件夾(自己定義的名稱)
過程中暫時可以一路回車,后續如果遇到要做具體的項目的時候,可以根據項目需要填寫。
切換至項目目錄:
$ cd myproject
安裝依賴:
$ npm install
安裝完成之后可以看見,文件里面多出了依賴文件夾
開啟實時編譯:
$ wepy build --watch
可以看到項目中多了一個 dist 目錄,然后在小程序開發工具中打開 dist 目錄,就可以看到,剛才創建的項目。
更多命令用法,見wepy-cli文檔
WePY項目的目錄結構
├── dist 小程序運行代碼目錄(該目錄由WePY的build指令自動編譯生成,請不要直接修改該目錄下的文件) ├── node_modules ├── src 代碼編寫的目錄(該目錄為使用WePY后的開發目錄) | ├── components WePY組件目錄(組件不屬於完整頁面,僅供完整頁面或其他組件引用) | | ├── com_a.wpy 可復用的WePY組件a | | └── com_b.wpy 可復用的WePY組件b | ├── pages WePY頁面目錄(屬於完整頁面) | | ├── index.wpy index頁面(經build后,會在dist目錄下的pages目錄生成index.js、index.json、index.wxml和index.wxss文件) | | └── other.wpy other頁面(經build后,會在dist目錄下的pages目錄生成other.js、other.json、other.wxml和other.wxss文件) | └── app.wpy 小程序配置項(全局數據、樣式、聲明鈎子等;經build后,會在dist目錄下生成app.js、app.json和app.wxss文件) └── package.json 項目的package配置
添加項目
1.7.0
之后的版本init
新生成的代碼包會在根目錄包含 project.config.json
文件,之前生成的代碼包可能不存在 project.config.json
文件。 檢查根目錄是否存在該文件。
如果存在,使用微信開發者工具
-->添加項目
,項目目錄
請選擇項目根目錄即可根據配置完成項目信息自動配置。
如果不存在,建議手動創建該文件后再添加項目。project.config.json
文件內容如下:
{ "description": "project description", "setting": { "urlCheck": true, "es6": false, "postcss": false, "minified": false }, "compileType": "miniprogram", "appid": "touristappid", "projectname": "Project name", "miniprogramRoot": "./dist" }
es6
: 對應關閉ES6轉ES5
選項,關閉。 重要:未關閉會運行報錯。
postcss
: 對應關閉上傳代碼時樣式自動補全
選項,關閉。 重要:某些情況下漏掉此項也會運行報錯。
minified
: 對應關閉代碼壓縮上傳
選項,關閉。重要:開啟后,會導致真機computed, props.sync 等等屬性失效。(注:壓縮功能可使用WePY提供的build指令代替,詳見后文相關介紹以及Demo項目根目錄中的wepy.config.js
和package.json
文件。)
urlCheck
: 對應不檢查安全域名
選項,開啟。 如果已配置好安全域名則建議關閉。
代碼規范
-
變量與方法盡量使用駝峰式命名,並且注意避免使用
$
開頭。 以$
開頭的標識符為WePY框架的內建屬性和方法,可在JavaScript腳本中以this.
的方式直接使用,具體請參考API文檔。 -
小程序入口、頁面、組件文件名的后綴為
.wpy
;外鏈的文件可以是其它后綴。 具體請參考wpy文件說明。 -
使用ES6語法開發。 框架在ES6(ECMAScript 6)下開發,因此也需要使用ES6開發小程序,ES6中有大量的語法糖可以讓我們的代碼更加簡潔高效。
-
使用Promise。 框架默認對小程序提供的API全都進行了 Promise 處理,甚至可以直接使用
async/await
等新特性進行開發。啟用Promise方法 -
事件綁定語法使用優化語法代替。
- 原
bindtap="click"
替換為@tap="click"
,原catchtap="click"
替換為@tap.stop="click"
。 - 原
capture-bind:tap="click"
替換為@tap.capture="click"
,原capture-catch:tap="click"
替換為@tap.capture.stop="click"
。 - 更多
@
符用法,參見組件自定義事件。
- 原
-
事件傳參使用優化后語法代替。 原
bindtap="click" data-index="{{index}}"
替換為@tap="click({{index}})"
。 -
自定義組件命名應避開微信原生組件名稱以及功能標簽
<repeat>
。 不可以使用input、button、view、repeat
等微信小程序原生組件名稱命名自定義組件;另外也不要使用WePY框架定義的輔助標簽repeat
命名。有關repeat
的詳細信息,請參見循環列表組件引用。
wepy.config.js配置文件說明
執行wepy new demo
后,會生成類似下面這樣的配置文件。
let prod = process.env.NODE_ENV === 'production'; module.exports = { 'target': 'dist', 'source': 'src', 'wpyExt': '.wpy', 'compilers': { less: { 'compress': true }, /*sass: { 'outputStyle': 'compressed' }, postcss: { plugins: [ cssnext({ browsers:['iOS 9', 'Android 4.4'] }) ] },*/ babel: { 'presets': [ 'es2015', 'stage-1' ], 'plugins': [ 'transform-export-extensions', 'syntax-export-extensions', 'transform-runtime' ] } }, 'plugins': { } }; if (prod) { // 壓縮sass module.exports.compilers['sass'] = {'outputStyle': 'compressed'}; // 壓縮less module.exports.compilers['less'] = {'compress': true}; // 壓縮js module.exports.plugins = { 'uglifyjs': { filter: /\.js$/, config: { } }, 'imagemin': { filter: /\.(jpg|png|jpeg)$/, config: { 'jpg': { quality: 80 }, 'png': { quality: 80 } } } }; }
wpyExt: 缺省值為'.wpy',IDE默認情況下不會對此文件類型進行高亮處理,這種情況下,除了按照前文代碼高亮
部分的介紹進行設置之外,還可以直接將相關文件的后綴名由.wpy
修改為.vue
(因為與Vue的高亮規則一樣),然后將此選項修改為.vue
,也能解決部分IDE中代碼高亮的問題。
compilers: compilers為1.3.1
版本之后的功能,如果需要使用其它語法,請先配置compilers
,然后再安裝相應的compilers。目前支持 wepy-compiler-less
, wepy-compiler-postcss
,wepy-compiler-sass
、wepy-compiler-babel
、wepy-compiler-pug
,其他compiler持續開發中......
對應各compiler請參考各自文檔:
sass: sass編譯配置,參見這里。
less: less編譯配置,參見這里。
postcss: postcss編譯配置,參見這里。
stylus: stylus編譯配置,參見這里。
babel: babel編譯配置,參見這里。
typescript: typescript編譯配置,參見這里。
plugins: plugins為 1.1.6
版本之后的功能,目前支持js壓縮 wepy-plugin-ugliyjs
、圖片壓縮 wepy-plugin-imagemin
,其他plugin持續開發中......
.wpy文件說明
.wpy
文件的編譯過程過下:
一個 .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 、postcss |
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
配置以及頁面特有的一些生命周期函數之外,其屬性與頁面屬性大致相同。
實例
通過前文的介紹可知,在 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 { } // 聲明一個Component組件實例 export default class MyComponent extends wepy.component { }
App小程序實例
App小程序實例中主要包含小程序生命周期函數、config配置對象、全局數據對象,以及其他自定義方法與屬性。
import wepy from 'wepy'; export default class MyAPP extends wepy.app { customData = {}; // 自定義數據 customFunction () { } //自定義方法 onLaunch () {} onShow () {} config = {} // 對應 app.json 文件 globalData = {} // 全局數據對象 }
在Page頁面實例中,可以通過 this.$parent
來訪問App實例。
wepy獲取全局變量 globalData:
// 頁面中 console.log(this.$parent.globalData) // 組件中 console.log(this.$parent.$parent.globalData)
Page頁面實例和Component組件實例
由於Page頁面實際上繼承自Component組件,即Page也是組件。除擴展了頁面所特有的config
配置以及特有的頁面生命周期函數之外,其它屬性和方法與Component一致,因此這里以Page頁面為例進行介紹。
import wepy from 'wepy'; // export default class MyPage extends wepy.page { export default class MyComponent extends wepy.component { customData = {} // 自定義數據 customFunction () {} //自定義方法 onLoad () {} // 在Page和Component共用的生命周期函數 onShow () {} // 只在Page中存在的頁面生命周期函數 config = {}; // 只在Page實例中存在的配置數據,對應於原生的page.json文件 data = {}; // 頁面所需數據均需在這里聲明,可用於模板數據綁定 components = {}; // 聲明頁面中所引用的組件,或聲明組件中所引用的子組件 mixins = []; // 聲明頁面所引用的Mixin實例 computed = {}; // 聲明計算屬性(詳見后文介紹) watch = {}; // 聲明數據watcher(詳見后文介紹) methods = {}; // 聲明頁面wxml中標簽的事件處理函數。注意,此處只用於聲明頁面wxml中標簽的bind、catch事件,自定義方法需以自定義方法的方式聲明 events = {}; // 聲明組件之間的事件處理函數 }
注意,對於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對象中 customFunction () { return 'sth.'; } }; } // 正確示例 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.'; } }
組件
原生小程序支持js模塊化,但彼此獨立,業務代碼與交互事件仍需在頁面處理。無法實現組件化的松耦合與復用的效果。
普通組件引用
當頁面需要引入組件或組件需要引入子組件時,必須在.wpy
文件的<script>
腳本部分先import組件文件,然后在components
對象中給組件聲明唯一的組件ID,接着在<template>
模板部分中添加以components
對象中所聲明的組件ID進行命名的自定義標簽以插入組件。如:
/** project └── src ├── components | └── child.wpy ├── pages | ├── index.wpy index 頁面配置、結構、樣式、邏輯 | └── log.wpy log 頁面配置、結構、樣式、邏輯 └──app.wpy 小程序配置項(全局公共配置、公共樣式、聲明鈎子等) **/ // index.wpy <template> <!-- 以`<script>`腳本部分中所聲明的組件ID為名命名自定義標簽,從而在`<template>`模板部分中插入組件 --> <child></child> </template> <script> import wepy from 'wepy'; //引入組件文件 import Child from '../components/child'; export default class Index extends wepy.component { //聲明組件,分配組件id為child components = { child: Child }; } </script>
需要注意的是,WePY中的組件都是靜態組件,是以組件ID作為唯一標識的,每一個ID都對應一個組件實例,當頁面引入兩個相同ID的組件時,這兩個組件共用同一個實例與數據,當其中一個組件數據變化時,另外一個也會一起變化。
如果需要避免這個問題,則需要分配多個組件ID和實例。代碼如下:
<template> <view class="child1"> <child></child> </view> <view class="child2"> <anotherchild></anotherchild> </view> </template> <script> import wepy from 'wepy'; import Child from '../components/child'; export default class Index extends wepy.component { components = { //為兩個相同組件的不同實例分配不同的組件ID,從而避免數據同步變化的問題 child: Child, anotherchild: Child }; } </script>
注意:WePY中,在父組件template模板部分插入駝峰式命名的子組件標簽時,不能將駝峰式命名轉換成短橫桿式命名(比如將childCom轉換成child-com),這與Vue中的習慣是不一致。
組件的循環渲染
當需要循環渲染WePY組件時(類似於通過 wx:for
循環渲染原生的wxml標簽),必須使用WePY定義的輔助標簽<repeat>
,代碼如下:
/** project └── src ├── components | └── child.wpy ├── pages | ├── index.wpy index 頁面配置、結構、樣式、邏輯 | └── log.wpy log 頁面配置、結構、樣式、邏輯 └──app.wpy 小程序配置項(全局樣式配置、聲明鈎子等) **/ // index.wpy <template> <!-- 注意,使用for屬性,而不是使用wx:for屬性 --> <repeat for="{{list}}" key="index" index="index" item="item"> <!-- 插入<script>腳本部分所聲明的child組件,同時傳入item --> <child :item="item"></child> </repeat> </template> <script> import wepy from 'wepy'; // 引入child組件文件 import Child from '../components/child'; export default class Index extends wepy.component { components = { // 聲明頁面中要使用到的Child組件的ID為child child: Child } data = { list: [{id: 1, title: 'title1'}, {id: 2, title: 'title2'}] } } </script>
頁面可以引入組件,而組件還可以引入子組件。一個頁面引入若干組件后,組件結構如下圖:
如上圖所示,Page_Index頁面引入了ComA、ComB、ComC三個組件,同時ComA組件和ComB組件又有自己的子組件ComD、ComE、ComF、ComG、ComH。
computed 計算屬性
-
類型:
{ [key: string]: Function }
-
詳細:
computed
計算屬性,是一個有返回值的函數,可直接被當作綁定數據來使用。因此類似於 data
屬性,代碼中可通過 this.計算屬性名
來引用,模板中也可通過{{ 計算屬性名 }}
來綁定數據。
需要注意的是,只要是組件中有任何數據發生了改變,那么所有計算屬性就都會被重新計算。
-
示例:
data = { a: 1 } // 計算屬性aPlus,在腳本中可通過this.aPlus來引用,在模板中可通過{{ aPlus }}來插值 computed = { aPlus () { return this.a + 1 } }
watcher 監聽器
-
類型:
{ [key: string]: Function }
-
詳細:
通過監聽器watcher
能夠監聽到任何屬性的更新。監聽器在watch
對象中聲明,類型為函數,函數名與需要被監聽的 data
對象中的屬性同名,每當被監聽的屬性改變一次,監聽器函數就會被自動調用執行一次。
監聽器適用於當屬性改變時需要進行某些額外處理的情形。
-
示例:
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) }
wepy 具體在什么地方要用 this.$apply()
異步更新數據,手動刷新dom的時候使用 this.$apply()。
比如data里面你定義了一個x='',然后你在自定義的方法里面用this.x=200 之后,需要用this.$apply()來進行數據綁定。這樣你在view中綁定data中的x變量時,才會有200,不然就是空
不過有個前提,method里面的方法是不用這個的,但methods里面只能放bindtap這類方法,所以你自己定義的其他方法,或者寫在onshow等生命周期或者watch屬性里的時候,就必須得用this.$apply()。
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);
用於監聽組件之間的通信與交互事件的事件處理函數需要寫在組件和頁面的 events
對象中,如:
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
$broadcast
事件是由父組件發起,所有子組件都會收到此廣播事件,除非事件被手動取消。事件廣播的順序為廣度優先搜索順序,如上圖,如果頁面 Page_Index
發起一個 $broadcast
事件,那么按先后順序依次接收到該事件的組件為:ComA、ComB、ComC、ComD、ComE、ComF、ComG、ComH。如下圖:
$emit
$emit
與 $broadcast
正好相反,事件發起組件的所有祖先組件會依次接收到 $emit
事件。如果組件 ComE 發起一個 $emit
事件,那么接收到事件的先后順序為:組件ComA、頁面Page_Index。如下圖:
$invoke
$invoke
是一個頁面或組件對另一個組件中的方法的直接調用,通過傳入組件路徑找到相應的組件,然后再調用其方法。
比如,想在頁面 Page_Index
中調用組件 ComA 的某個方法:
this.$invoke('ComA', 'someMethod', 'someArgs');
如果想在組件ComA中調用組件ComG的某個方法:
this.$invoke('./../ComB/ComG', 'someMethod', 'someArgs');
組件自定義事件處理函數
可以通過使用 .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>
slot 組件內容分發插槽
WePY中的 slot
插槽作為內容分發標簽的空間占位標簽,便於在父組件中通過對相當於擴展板卡的內容分發標簽的“插拔”,更為靈活、方便地對子組件進行內容分發。
具體使用方法是:首先在子組件 template
模板部分中聲明 slot
標簽作為內容插槽,同時必須在其 name
屬性中指定插槽名稱,還可設置默認的標簽內容;然后在引入了該帶有插槽的子組件的父組件 template
模板部分中聲明用於“插拔”的內容分發標簽。
注意,這些父組件中的內容分發標簽必須具有 slot
屬性,並且其值為子組件中對應的插槽名稱,這樣父組件內容分發標簽中的內容會覆蓋掉子組件對應插槽中的默認內容。
另外,要特別注意的是,父組件中一旦聲明了對應於子組件插槽的內容分發標簽,即便沒有內容,子組件插槽中的默認內容也不會顯示出來,只有刪除了父組件中對應的內容分發標簽,才能顯示出來。
示例:
在 Panel
子組件中有以下模板:
<view class="panel"> <slot name="title">默認標題</slot> <slot name="content">默認內容</slot> </view>
在父組件中使用 Pannel
子組件時,可以這樣使用:
<panel> <view slot="title">新的標題</view> <view slot="content"> <text>新的內容</text> </view> </panel>
第三方組件
WePY允許使用基於WePY開發的第三方組件,開發第三方組件規范請參考wepy-com-toast。
Mixin 混合
混合可以將組之間的可復用部分抽離,從而在組件中使用混合時,可以將混合的數據,事件以及方法注入到組件之中。混合分為兩種:
- 默認式混合
- 兼容式混合
默認式混合
對於組件 data
數據,components
組件,events
事件以及其它自定義方法采用默認式混合,即如果組件未聲明該數據,組件,事件,自定義方法等,那么將混合對象中的選項將注入組件這中。對於組件已聲明的選項將不受影響。
// mixins/test.js import wepy from 'wepy'; export default class TestMixin extends wepy.mixin { data = { foo: 'foo defined by page', bar: 'bar defined by testMix' }; methods: { tap () { console.log('mix tap'); } } } // pages/index.wpy import wepy from 'wepy'; import TestMixin from './mixins/test'; export default class Index extends wepy.page { data = { foo: 'foo defined by index' }; mixins = [TestMixin]; onShow() { console.log(this.foo); // foo defined by index console.log(this.bar); // bar defined by testMix } }
兼容式混合
對於組件 methods 響應事件,以及小程序頁面事件將采用兼容式混合,即先響應組件本身響應事件,然后再響應混合對象中響應事件。注意,這里事件的執行順序跟Vue中相反,Vue中是先執行mixin中的函數, 再執行組件本身的函數。
// mixins/test.js import wepy from 'wepy'; export default class TestMixin extends wepy.mixin { methods = { tap () { console.log('mixin tap'); } }; onShow() { console.log('mixin onshow'); } } // pages/index.wpy import wepy from 'wepy'; import TestMixin from './mixins/test'; export default class Index extends wepy.page { mixins = [TestMixin]; methods = { tap () { console.log('index tap'); } }; onShow() { console.log('index onshow'); } } // 先響應組件本身響應事件,然后再響應混合對象中響應事件 // index onshow // mixin onshow // ----- when tap // index tap // mixin tap
WXS (WeiXin Script)
WePY 從 1.7.x
版本開始支持 wxs 語法,但語法與原生 wxs 稍有出入。
/** project └── src ├── wxs | └── mywxs.wxs wxs 文件 ├── pages | └── index.wpy 頁面 └──app.wpy **/ // mywxs.wxs module.exports = { text: 'This is from wxs', filter: function (num) { return num.toFixed(2); } }; // index.wpy <template> <text>{{m1.text}}</text> <text>{{m1.filter(num)}}</text> </template> <script> import wepy from 'wepy'; import mywxs from '../wxs/mywxs.wxs'; export default class Index extends wepy.page { data = { num: 10 }; wxs = { m1: mywxs } }; </script>
注意
- wxs是基於原生的wxs去實現的,只是通過編譯把現在的語法編譯為原生語法。
- wxs必須是外鏈文件。並且后綴為
.wxs
。 - wxs引入后只能在template中使用,不能在script中使用。
interceptor 攔截器
可以使用WePY提供的全局攔截器對原生API的請求進行攔截。
具體方法是配置API的config、fail、success、complete回調函數。參考示例:
import wepy from 'wepy'; export default class extends wepy.app { constructor () { //攔截request請求 this.intercept('request', { // 發出請求時的回調函數 config (p) { // 對所有request請求中的OBJECT參數對象統一附加時間戳屬性 p.timestamp = new Date().getTime(); 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); } }); } }
數據綁定
原生小程序的數據綁定方式
原生小程序通過 Page
提供的 setData
方法來綁定數據
WePY數據綁定方式
WePY使用臟數據檢查對 setData 進行封裝,在函數運行周期結束時執行臟數據檢查,一來可以不用關心頁面多次setData是否會有性能上的問題,二來可以更加簡潔去修改數據實現綁定,不用重復去寫setData方法。代碼如下:
this.title = 'this is title';
需注意的是,在異步函數中更新數據的時,必須手動調用 $apply
方法,才會觸發臟數據檢查流程的運行。如:
setTimeout(() => { this.title = 'this is title'; this.$apply(); }, 3000);
其它優化細節
1. wx.request 接收參數修改
點這里查看官方文檔
// 原生代碼: wx.request({ url: 'xxx', success: function (data) { console.log(data); } }); // WePY 使用方式 wepy.request('xxxx').then((d) => console.log(d));
2. 優化事件參數傳遞
點這里查看官方文檔
// 原生的事件傳參方式: <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。 <view @tap="tapName({{index}}, 'wepy', 'otherparams')"> Click me! </view> methods: { tapName (id, title, other, event) { console.log(id, title, other) // output: 1, wepy, otherparams } }
3. 改變數據綁定方式
保留setData方法,但不建議使用setData執行綁定,修復傳入 undefined
的bug,並且修改入參支持: this.setData(target, value)
this.setData(object)
點這里查看官方文檔
// 原生代碼: <view> {{ message }} </view> onLoad: function () { this.setData({ message: 'hello world' }); } // WePY <view> {{ message }} </view> onLoad () { this.message = 'hello world'; }
4. 組件代替模板和模塊
點這里查看官方文檔
// 原生代碼: <!-- item.wxml --> <template name="item"> <text>{{text}}</text> </template> <!-- index.wxml --> <import src="item.wxml"/> <template is="item" data="{{text: 'forbar'}}"/> <!-- index.js --> var item = require('item.js') // WePY <!-- /components/item.wpy --> <text>{{text}}</text> <!-- index.wpy --> <template> <com></com> </template> <script> import wepy from 'wepy'; import Item from '../components/item'; export default class Index extends wepy.page { components = { com: Item } } </script>
更多功能及特性,請參考官方文檔:https://wepyjs.github.io/wepy-docs/1.x/#/
微信小程序WePY框架開發資源匯總:awesome-wepy