微信小程序wepy框架筆記小結(jié)
該框架是騰訊內(nèi)部基于小程序的開發(fā)框架,設(shè)計思路基本參考VUE,開發(fā)模式和編碼風(fēng) 格上80%以上接近VUE
優(yōu)勢
組件化開發(fā)
小程序雖然有標(biāo)簽可以實(shí)現(xiàn)組件復(fù)用,但僅限于模板片段層面的復(fù)用,業(yè)務(wù)代碼與交互事件 仍需在頁面處理。無法實(shí)現(xiàn)組件化的松耦合與復(fù)用的效果。
wepy組件示例
// index.wpy
<template>
<view>
<panel>
<h1 slot="title"></h1>
</panel>
<counter1 :num="myNum"></counter1>
<counter2 :num.sync="syncNum"></counter2>
<list :item="items"></list>
</view>
</template>
<script>
import wepy from 'wepy';
import List from '../components/list';
import Panel from '../components/panel';
import Counter from '../components/counter';
export default class Index extends wepy.page {
config = {
"navigationBarTitleText": "test"
};
components = {
panel: Panel,
counter1: Counter,
counter2: Counter,
list: List
};
data = {
myNum: 50,
syncNum: 100,
items: [1, 2, 3, 4]
}
}
</script>
支持加載外部NPM包
小程序較大的缺陷是不支持NPM包,導(dǎo)致無法直接使用大量優(yōu)秀的開源內(nèi)容,wepy在編譯過程當(dāng)中,會遞歸 遍歷代碼中的require然后將對應(yīng)依賴文件從node_modules當(dāng)中拷貝出來,并且修改require為相對路徑, 從而實(shí)現(xiàn)對外部NPM包的支持
單文件模式,使得目錄結(jié)構(gòu)更加清晰
小程序官方目錄結(jié)構(gòu)要求app必須有三個文件app.json,app.js,app.wxss,頁面有4個文件 index.json,index.js,index.wxml,index.wxss。而且文 件必須同名。 所以使用wepy開發(fā)前后開發(fā)目錄對比如下:
官方DEMO:
project ├── pages | ├── index | | ├── index.json index 頁面配置 | | ├── index.js index 頁面邏輯 | | ├── index.wxml index 頁面結(jié)構(gòu) | | └── index.wxss index 頁面樣式表 | └── log | ├── log.json log 頁面配置 | ├── log.wxml log 頁面邏輯 | ├── log.js log 頁面結(jié)構(gòu) | └── log.wxss log 頁面樣式表 ├── app.js 小程序邏輯 ├── app.json 小程序公共設(shè)置 └── app.wxss 小程序公共樣式表
使用wepy框架后目錄結(jié)構(gòu):
project └── src ├── pages | ├── index.wpy index 頁面配置、結(jié)構(gòu)、樣式、邏輯 | └── log.wpy log 頁面配置、結(jié)構(gòu)、樣式、邏輯 └──app.wpy 小程序配置項(xiàng)(全局樣式配置、聲明鉤子等)
如何開發(fā)
快速起步
安裝
npm install wepy-cli -g
創(chuàng)建項(xiàng)目
wepy new myproject
切換至項(xiàng)目目錄
cd myproject
實(shí)時編譯
wepy build –watch
目錄結(jié)構(gòu) ├── dist 微信開發(fā)者工具指定的目錄
├── node_modules ├── src 代碼編寫的目錄 | ├── components 組件文件夾(非完整頁面) | | ├── com_a.wpy 可復(fù)用組件 a | | └── com_b.wpy 可復(fù)用組件 b | ├── pages 頁面文件夾(完整頁面) | | ├── index.wpy 頁面 index | | └── page.wpy 頁面 page | └── app.wpy 小程序配置項(xiàng)(全局樣式配置、聲明鉤子等) └── package.json package 配置
wepy和VUE主要區(qū)別
1.二者均支持props、data、computed、components、methods、watch(wepy中是watcher), 但wepy中的methods僅可用于頁面事件綁定,其他自定義方法都要放在外層,而VUE中所有方法均放在 methods下
2.wepy中props傳遞需要加上.sync修飾符(類似VUE1.x)才能實(shí)現(xiàn)props動態(tài)更新,并且父組件再 變更傳遞給子組件props后要執(zhí)行this.$apply()方法才能更新
3.wepy支持?jǐn)?shù)據(jù)雙向綁定,子組件在定義props時加上twoway:true屬性值即可實(shí)現(xiàn)子組件修改父組 件數(shù)據(jù)
4.VUE2.x推薦使用eventBus方式進(jìn)行組件通信,而在wepy中是通過broadcast,broadcast,emit,$invoke 三種方法實(shí)現(xiàn)通信
· 首先事件監(jiān)聽需要寫在events屬性下:
``` bash
import wepy from 'wepy';
export default class Com extends wepy.component {
components = {};
data = {};
methods = {};
events = {
'some-event': (p1, p2, p3, $event) => {
console.log(`${this.name} receive ${$event.name} from ${$event.source.name}`);
}
};
// Other properties
}
```
· $broadcast:父組件觸發(fā)所有子組件事件
· $emit:子組件觸發(fā)父組件事件
· $invoke:子組件觸發(fā)子組件事件
5.VUE的生命周期包括created、mounted等,wepy僅支持小程序的生命周期:onLoad、onReady等
6.wepy不支持過濾器、keep-alive、ref、transition、全局插件、路由管理、服務(wù)端渲染等VUE特性技術(shù)
進(jìn)階介紹
.wpy文件說明
一個.wpy文件可分為三大部分,各自對應(yīng)于一個標(biāo)簽:
腳本部分,即標(biāo)簽中的內(nèi)容,又可分為兩個部分:
邏輯部分,除了config對象之外的部分,對應(yīng)于原生的.js文件;
配置部分,即config對象,對應(yīng)于原生的.json文件。
結(jié)構(gòu)部分,即模板部分,對應(yīng)于原生的.wxml文件。
樣式部分,即樣式部分,對應(yīng)于原生的.wxss文件。
.wpy文件中的script、template、style這三個標(biāo)簽都支持lang和src屬性,lang決定了其代碼編譯過程,src決定是否外聯(lián)代碼,存在src屬性且有效時,會忽略內(nèi)聯(lián)代碼。
| 標(biāo)簽 | lang默認(rèn)值 | lang支持值 |
|---|---|---|
| style | css | css、less、sass、stylus |
| template | wxml | wxml、xml、pug(原jade) |
| script | babel | babel、TypeScript |
普通組件引用
當(dāng)頁面需要引入組件或組件需要引入子組件時,必須在.wpy文件的
<template>
<!-- 以`<script>`腳本部分中所聲明的組件ID為名命名自定義標(biāo)簽,從而在`<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中的組件都是靜態(tài)組件,是以組件ID作為唯一標(biāo)識的,每一個ID都對應(yīng)一個組件實(shí)例,當(dāng)頁面引入兩個相同ID的組件時,這兩個組件共用同一個實(shí)例與數(shù)據(jù),當(dāng)其中一個組件數(shù)據(jù)變化時,另外一個也會一起變化。
如果需要避免這個問題,則需要分配多個組件ID和實(shí)例。
組件的循環(huán)渲染
1.4.6新增
當(dāng)需要循環(huán)渲染W(wǎng)ePY組件時(類似于通過wx:for循環(huán)渲染原生的wxml標(biāo)簽),必須使用WePY定義的輔助標(biāo)簽
<template>
<!-- 注意,使用for屬性,而不是使用wx:for屬性 -->
<repeat for="{{list}}" key="index" index="index" item="item">
<!-- 插入<script>腳本部分所聲明的child組件,同時傳入item -->
<child :item="item"></child>
</repeat>
</template>
computed 計算屬性
computed計算屬性,是一個有返回值的函數(shù),可直接被當(dāng)作綁定數(shù)據(jù)來使用。因此類似于data屬性,代碼中可通過this.計算屬性名來引用,模板中也可通過{{ 計算屬性名 }}來綁定數(shù)據(jù)。
需要注意的是,只要是組件中有任何數(shù)據(jù)發(fā)生了改變,那么所有計算屬性就都會被重新計算。
data = {
a: 1
}
// 計算屬性aPlus,在腳本中可通過this.aPlus來引用,在模板中可通過{{ aPlus }}來插值
computed = {
aPlus () {
return this.a + 1
}
}
watcher 監(jiān)聽器
通過監(jiān)聽器watcher能夠監(jiān)聽到任何數(shù)值屬性的數(shù)值更新。監(jiān)聽器在watch對象中聲明,類型為函數(shù),函數(shù)名與需要被監(jiān)聽的data對象中的數(shù)值屬性同名,每當(dāng)被監(jiān)聽的數(shù)值屬性改變一次,監(jiān)聽器函數(shù)就會被自動調(diào)用執(zhí)行一次。
監(jiān)聽器適用于當(dāng)數(shù)值屬性改變時需要進(jìn)行某些額外處理的情形。
data = {
num: 1
}
// 監(jiān)聽器函數(shù)名必須跟需要被監(jiān)聽的data對象中的數(shù)值屬性num同名,
// 其參數(shù)中的newValue為數(shù)值屬性改變后的新值,oldValue為改變前的舊值
watch = {
num (newValue, oldValue) {
console.log(`num value: ${oldValue} -> ${newValue}`)
}
}
// 每當(dāng)被監(jiān)聽的數(shù)值屬性num改變一次,對應(yīng)的同名監(jiān)聽器函數(shù)num()就被自動調(diào)用執(zhí)行一次
onLoad () {
setInterval(() => {
this.num++;
this.$apply();
}, 1000)
}
props 傳值
靜態(tài)傳值
靜態(tài)傳值為父組件向子組件傳遞常量數(shù)據(jù),因此只能傳遞String字符串類型。
在父組件template模板部分的組件標(biāo)簽中,使用子組件props對象中所聲明的屬性名作為其屬性名來接收父組件傳遞的值。
<child title="mytitle"></child>
// child.wpy
props = {
title: String
};
onLoad () {
console.log(this.title); // mytitle
}
動態(tài)傳值
動態(tài)傳值是指父組件向子組件傳遞動態(tài)數(shù)據(jù)內(nèi)容,父子組件數(shù)據(jù)完全獨(dú)立互不干擾。但可以通過使用.sync修飾符來達(dá)到父組件數(shù)據(jù)綁定至子組件的效果,也可以通過設(shè)置子組件props的twoWay: true來達(dá)到子組件數(shù)據(jù)綁定至父組件的效果。那如果即使用.sync修飾符,同時子組件props中添加的twoWay: true時,就可以實(shí)現(xiàn)數(shù)據(jù)的雙向綁定了。
注意:下文示例中的twoWay為true時,表示子組件向父組件單向動態(tài)傳值,而twoWay為false(默認(rèn)值,可不寫)時,則表示子組件不向父組件傳值。這是與Vue不一致的地方,而這里之所以仍然使用twoWay,只是為了盡可能保持與Vue在標(biāo)識符命名上的一致性。
在父組件template模板部分所插入的子組件標(biāo)簽中,使用:prop屬性(等價于Vue中的v-bind:prop屬性)來進(jìn)行動態(tài)傳值。
// parent.wpy
<child :title="parentTitle" :syncTitle.sync="parentTitle" :twoWayTitle="parentTitle"></child>
data = {
parentTitle: 'p-title'
};
// child.wpy
props = {
// 靜態(tài)傳值
title: String,
// 父向子單向動態(tài)傳值
syncTitle: {
type: String,
default: 'null'
},
twoWayTitle: {
type: Number,
default: 50,
twoWay: true
}
};
onLoad () {
console.log(this.title); // p-title
console.log(this.syncTitle); // p-title
console.log(this.twoWayTitle); // 50
this.title = 'c-title';
console.log(this.$parent.parentTitle); // p-title.
this.twoWayTitle = 60;
this.$apply();
console.log(this.$parent.parentTitle); // 60. --- twoWay為true時,子組件props中的屬性值改變時,會同時改變父組件對應(yīng)的值
this.$parent.parentTitle = 'p-title-changed';
this.$parent.$apply();
console.log(this.title); // 'p-title';
console.log(this.syncTitle); // 'p-title-changed' --- 有.sync修飾符的props屬性值,當(dāng)在父組件中改變時,會同時改變子組件對應(yīng)的值。
}
組件通信與交互
用于監(jiān)聽組件之間的通信與交互事件的事件處理函數(shù)需要寫在組件和頁面的events對象中
- broadcastbroadcastbroadcast事件是由父組件發(fā)起,所有子組件都會收到此廣播事件,除非事件被手動取消。事件廣播的順序?yàn)閺V度優(yōu)先搜索順序
- emitemitemit與broadcast正好相反,事件發(fā)起組件的所有祖先組件會依次接收到broadcast正好相反,事件發(fā)起組件的所有祖先組件會依次接收到emit事件。
- invokeinvokeinvoke是一個頁面或組件對另一個組件中的方法的直接調(diào)用,通過傳入組件路徑找到相應(yīng)的組件,然后再調(diào)用其方法。
比如,想在頁面Page_Index中調(diào)用組件ComA的某個方法:
this.$invoke('ComA', 'someMethod', 'someArgs');
如果想在組件ComA中調(diào)用組件ComG的某個方法:
this.$invoke('./../ComB/ComG', 'someMethod', 'someArgs');
組件自定義事件處理函數(shù)
可以通過使用.user修飾符為自定義組件綁定事件,如:@customEvent.user=”myFn”
其中,@表示事件修飾符,customEvent 表示事件名稱,.user表示事件后綴。
目前總共有三種事件后綴:
.default: 綁定小程序冒泡型事件,如bindtap,.default后綴可省略不寫;
.stop: 綁定小程序捕獲型事,如catchtap;
.user: 綁定用戶自定義組件事件,通過$emit觸發(fā)。
// 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 組件內(nèi)容分發(fā)插槽
WePY中的slot插槽作為內(nèi)容分發(fā)標(biāo)簽的空間占位標(biāo)簽,便于在父組件中通過對相當(dāng)于擴(kuò)展板卡的內(nèi)容分發(fā)標(biāo)簽的“插拔”,更為靈活、方便地對子組件進(jìn)行內(nèi)容分發(fā)。
具體使用方法是,首先在子組件template模板部分中聲明slot標(biāo)簽作為內(nèi)容插槽,同時必須在其name屬性中指定插槽名稱,還可設(shè)置默認(rèn)的標(biāo)簽內(nèi)容;然后在引入了該帶有插槽的子組件的父組件template模板部分中聲明用于“插拔”的內(nèi)容分發(fā)標(biāo)簽。
注意,這些父組件中的內(nèi)容分發(fā)標(biāo)簽必須具有slot屬性,并且其值為子組件中對應(yīng)的插槽名稱,這樣父組件內(nèi)容分發(fā)標(biāo)簽中的內(nèi)容會覆蓋掉子組件對應(yīng)插槽中的默認(rèn)內(nèi)容。
在Panel組件中有以下模板:
<view class="panel"> <slot name="title">默認(rèn)標(biāo)題</slot> <slot name="content">默認(rèn)內(nèi)容</slot> </view>
在父組件中使用Pannel子組件時,可以這樣使用:
<panel> <view slot="title">新的標(biāo)題</view> <view slot="content"> <text>新的內(nèi)容</text> </view> </panel>
混合
默認(rèn)式混合
對于組件data數(shù)據(jù),components組件,events事件以及其它自定義方法采用默認(rèn)式混合,即如果組件未聲明該數(shù)據(jù),組件,事件,自定義方法等,那么將混合對象中的選項(xiàng)將注入組件這中。對于組件已聲明的選項(xiàng)將不受影響。
// 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); // foo defined by testMix.
}
}
兼容式混合
對于組件methods響應(yīng)事件,以及小程序頁面事件將采用兼容式混合,即先響應(yīng)組件本身響應(yīng)事件,然后再響應(yīng)混合對象中響應(yīng)事件。
// mixins/test.js
import wepy from 'wepy';
export default class TestMixin extends wepy.mixin {
methods = {
tap () {
console.log('mix tap');
}
};
onShow() {
console.log('mix 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
// mix onshow
// ----- when tap
// index tap
// mix tap
攔截器
可以使用全域攔截器配置API的config、fail、success、complete方法,參考示例:
import wepy from 'wepy';
export default class extends wepy.app {
constructor () {
this.intercept('request', {
config (p) {
p.timestamp = +new Date();
return p;
},
success (p) {
console.log('request success');
return p;
},
fail (p) {
console.log('request error');
return p;
}
});
}
}
WePY數(shù)據(jù)綁定方式
WePY使用臟數(shù)據(jù)檢查對setData進(jìn)行封裝,在函數(shù)運(yùn)行周期結(jié)束時執(zhí)行臟數(shù)據(jù)檢查,一來可以不用關(guān)心頁面多次setData是否會有性能上的問題,二來可以更加簡潔去修改數(shù)據(jù)實(shí)現(xiàn)綁定,不用重復(fù)去寫setData方法。
this.title = 'this is title';
在函數(shù)運(yùn)行周期之外的函數(shù)里去修改數(shù)據(jù)需要手動調(diào)用$apply方法
setTimeout(() => {
this.title = 'this is title';
this.$apply();
}, 3000);
優(yōu)化事件參數(shù)傳遞
// 官方
<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 bindtap="tapName({{index}}, 'wepy', 'otherparams')"> Click me! </view>
methods: {
tapName (id, title, other, event) {
console.log(id, title, other)// output: 1, wepy, otherparams
}
}
改變數(shù)據(jù)綁定方式
保留setData方法,但不建議使用setData執(zhí)行綁定,修復(fù)傳入undefined的bug,并且修改入?yún)⒅С郑?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';
}
重要提醒
- 使用微信開發(fā)者工具–>添加項(xiàng)目,項(xiàng)目目錄請選擇dist目錄。
- 微信開發(fā)者工具–>項(xiàng)目–>關(guān)閉ES6轉(zhuǎn)ES5。 重要:漏掉此項(xiàng)會運(yùn)行報錯。
- 微信開發(fā)者工具–>項(xiàng)目–>關(guān)閉上傳代碼時樣式自動補(bǔ)全。 重要:某些情況下漏掉此項(xiàng)也會運(yùn)行報錯。
- 微信開發(fā)者工具–>項(xiàng)目–>關(guān)閉代碼壓縮上傳。 重要:開啟后,會導(dǎo)致真機(jī)computed, props.sync 等等屬性失效。(注:壓縮功能可使用WePY提供的build指令代替,詳見后文相關(guān)介紹以及Demo項(xiàng)目根目錄中的wepy.config.js和package.json文件。)
- 本地項(xiàng)目根目錄運(yùn)行wepy build –watch,開啟實(shí)時編譯。(注:如果同時在微信開發(fā)者工具–>設(shè)置–>編輯器中勾選了文件保存時自動編譯小程序,將可以實(shí)時預(yù)覽,非常方便。)
注意
WePY中的methods屬性只能聲明頁面wxml標(biāo)簽的bind、catch事件,不能聲明自定義方法
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
javascript多行字符串的簡單實(shí)現(xiàn)方式
之前我們給大家講訴過javascript多行字符串的7種解決辦法,今天來給大家分享一個更簡單的方法,非常的實(shí)用,大家一定要仔細(xì)看看哦。2015-05-05
總結(jié)JavaScript的正則與其他語言的不同之處
我接觸過不少語言,我很看重一門語言的正則表達(dá)式是否強(qiáng)大,還有正則與語法的結(jié)合是否緊密.在這一點(diǎn)上,JavaScript做的還不錯,至少有正則字面量.當(dāng)然,最強(qiáng)大的還是Perl.但最近發(fā)現(xiàn)JavaScript中的正則有幾個不同于其他語言的地方,下面一起來看下。2016-08-08
JavaScript中英文字符長度統(tǒng)計方法示例【按照中文占2個字符】
這篇文章主要介紹了JavaScript中英文字符長度統(tǒng)計方法,涉及javascript針對中英文字符的匹配與運(yùn)算相關(guān)操作技巧,需要的朋友可以參考下2017-01-01
JavaScript剩余操作符Rest Operator詳解
在本篇文章里小編給各位分享的是關(guān)于JavaScript剩余操作符Rest Operator知識點(diǎn)用法總結(jié),有需要的朋友們跟著學(xué)習(xí)下。2019-07-07
JS/jQuery實(shí)現(xiàn)獲取時間的方法及常用類完整示例
這篇文章主要介紹了JS/jQuery實(shí)現(xiàn)獲取時間的方法及常用類,結(jié)合完整實(shí)例形式分析了javascript針對日期時間的獲取、轉(zhuǎn)換、計算與檢測相關(guān)操作技巧,需要的朋友可以參考下2019-03-03
JavaScript中的this基本問題實(shí)例小結(jié)
這篇文章主要介紹了JavaScript中的this基本問題,結(jié)合實(shí)例形式總結(jié)分析了JavaScript中this的功能、常見用法與操作注意事項(xiàng),需要的朋友可以參考下2020-03-03
理解JavaScript中的適配器模式Adapter?Pattern
這篇文章主要介紹了理解JavaScript中的適配器模式,適配器模式即Adapter?Pattern,是作為兩個不兼容的接口之間的橋梁。這種類型的設(shè)計模式屬于結(jié)構(gòu)型模式,下文更多相關(guān)介紹需要的小伙伴可以參考一下2022-04-04
JavaScript中String和StringBuffer的速度之爭
很多Javascript書籍中都是類比于Java說String在頻繁的和大量的字符串連接方面的效率是不如StringBuffer的。2010-04-04

