一個開始
由於本人喜歡封裝組件做到高內聚,這樣的好處是,拿來就用,如果封裝一個組件,需要外部耦合,那么將沒法做到很好的復用,因為耦合的部分需要每次重新開發。
最近遇到了一個業務場景是這樣,如圖:
1. 頁面展示主頁,主頁可以瀏覽,也可以點擊去其他頁面,主頁有登錄按鈕,登錄按鈕點擊顯示登錄view(注意:登錄不是跳去登錄頁,而是在當前頁做view切換)。
2. 登錄view中可以填寫用戶名、密碼,可以登錄、可以返回(返回到主頁view)。
愉快的開發起開
1. 首先將主頁是小程序的一個頁面,由於我希望登錄模塊是通用的,既然不是新的頁面,就做成一個組件。
2. 登錄組件命名為login-view,愉快的開發完畢。
3. 接下來頁面引用組件,並在wxml中使用
<view class="home-container" wx:if="{{!showLogin}}"></view> <login-view wx:else></login-view>
4. 就這樣,定義data:showLogin, 用其控制是否切換到登錄view。
5. 當主頁中點擊登錄時,將showLogin = true。
6. 很棒,展示了登錄界面。填寫用戶名、密碼、點擊登錄,成功!。
7. 很開心,第一次測試成功了,接着第二次測試。
8. 這是第二次測試,點擊登錄,到登錄界面,輸入用戶名,這時候點擊了返回。
9. 再次點擊登錄來到了登錄界面,發現剛剛輸入的用戶名不見了
10. 留下了沒有技術的淚水,emmm...
尋找失蹤的用戶名
接下來展示思考,終於想到了為什么:
wx:if是dom的移除與添加,並不是顯示與隱藏,因此,在切換showLogin = false時,login-view組件被移除了,當再次點擊登錄,showLogin = true時,login-view組件又重新掛在。
好,失蹤的用戶名找到了,是wx:if將它奪走了。那么該怎么解決?
兩個過程
我解決此問題用了兩個過程,分別是:完成與完善。
過程一(完成):如何解決組件data丟失問題
分析:信息丟失是因為組件的注銷與重新掛載,這是不可避免的,組件注銷后,內容永遠也保存不了。要解決的就是把組件內容保存起來
解決辦法: 信息存儲在page中,因為page是一直生存的,組件是會注銷的。因此page中的data是不會隨着組件注銷而消失的。那么如何將信息存放在page中?
如下:
1. 將userName和password直接放在page的data中,並且接收input事件去改變值
<login-view userName="{{userName}}" password="{{password}}" bindinput="onInput"></login-view>
2. 在page中聲明onInput,當組件中觸發此函數,則改變userName和password的值
Page({ data: { userName: '', password: '', }, // 組件中觸發事件,修改輸入框的值 onInput(e) { const {key, value} = e.detail; this.setData({ [key]: value, }); }, });
3. 在login-view組件中,接收userName和password值,並渲染到login-view.wxml中去,這里就不再貼代碼了。
思考:這樣修改有什么問題?我認為這樣的組件不能稱之為一個優秀的組件,為什么?
答:我希望我的組件是拿來就用的,那么這樣修改,我怎么做到拿來就用?這樣的組件已與page高耦合,沒有page中的邏輯,此組件無法運行,因此我不能這樣該。
該怎樣:那么我該怎樣該達到我緩存數據的目的呢?
過程二(完善):開發低耦合高內聚的靈活性組件
首先看完善后的業務上該怎么用
<login-view></login-view>
wxml里直接這樣使用,那么page的js里呢?答案:不需要任何代碼。這么神奇么?就是這么神奇。
思路: 保存原來代碼不變,login-view做了任何login-view該做的事情,數據存儲、數據更新都在組件內完成。我要做的是做到組件重新掛在,數據緩存。該怎樣做?
同樣,利用page data緩存數據,但是不需要主動去定義,在login-view組件里做手腳:
const Base = require('./wx-component.js'); Component(Base({ name: 'login', $$data: { userName: '', password: '', }, methods: { onInput(e) { const key = e.currentTarget.dataset.key; const value = e.detail.value; this.setData({ [key]: value, }); }, }, }));
以上代碼,引用了wx-component.js,暴露一個方法,將原本傳遞給Component的options對象傳遞給他,Base(options),然后再將其返回值傳遞給Component,Component(Base(options))。做了一層包裝。就這樣,就完成了數據緩存,再次切換login-view時,數據依舊保存着。
那么看到這里讀者要問:wx-component是什么鬼?$$data又是什么?
三個BUFF
wx-component.js是自己封裝的一個組件增強的擴展函數,他可以輕松賦予你三個特殊能力。
1. 逆向數據綁定
什么意思?我們都知道,page中的data可以傳遞給組件使用,並且page中data更新,組件中的view頁同步更新,但是組件中的data更新不會反射到page中。
那么這第一個buff就是:組件data更新引發page中data的更新,換句話說,page的data中會時時存儲組件data的一個副本。因此我叫他逆向數據綁定。
代碼如下:
const Base = require('./wx-component.js'); Component(Base({ // 聲明增強組件的name name: 'login', // 聲明逆向數據綁定的data $$data: { userName: '', password: '', }, }));
name是必須聲明的,使用增強組件功能,需要聲明唯一name值,比如‘login’,$$data屬性是逆向數據綁定的所有數據,其他不需要逆向數據綁定的數據依舊放在data中。
這樣就完成了component.$$data -> page.data的功能,在組件setData改變userName和password的值會同步到page.data中去。
那你怎么可以在page中獲取到他呢?在page中用this.data.$[name]格式,此例子中為:this.data.$login就獲取到了逆向數據的對象。
2. 組件數據緩存
有了逆向數據綁定,你可能猜到了,就用這種方法可以把數據存儲到page中了,那么在組件注銷后重新掛載,我們可以拿到此份數據來進行渲染到組件。
這部分代碼在wx-component.js中做體現,業務中並不需要做額外的事情。
3. 方法暴露
很多時候,組件內部的一些方法,page中其實也需要用到的, 比如:tab組件,點擊某個tab,則變為激活狀態,在page中可能也是需要主動去改變某個tab的激活狀態,因此需要用到組件內部方法。
當然組件內部方法是有辦法獲取的,官方文檔有介紹如何獲取組件實例,獲取到組件實例,當然就可以使用其方法,但這樣很麻煩的,我可以提供更好的方法。
const Base = require('./wx-component.js'); Component(Base({ name: 'login', $$data: { userName: '', password: '', }, // 定義向page暴露的方法 $parentMethods: { reset() { this.setData({ userName: '', password: '', }); }, }, }));
如上用$parentMethods對象來暴露給page方法,在page中可以直接調用
Page({ onReset() { // $login來獲取暴露的方法 this.$login.reset(); } })
最后的最后
這就是wx-component的功能,功能並不是通用的,只適合部分業務場景。如果你的業務放好需要這樣做,你就可以用到它。
如果對你有幫助,點一個star吧~~~
如果有錯誤的地方,請提issue吧~~~
如果有什么建議的,歡迎留言哈~~~
如果要轉載,請附上原文鏈接哈~~~