小程序开发-自定义组件的扩展
一个开始
由于本人喜欢封装组件做到高内聚,这样的好处是,拿来就用,如果封装一个组件,需要外部耦合,那么将没法做到很好的复用,因为耦合的部分需要每次重新开发。
最近遇到了一个业务场景是这样,如图:
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吧~~~
如果有什么建议的,欢迎留言哈~~~
如果要转载,请附上原文链接哈~~~