我们在开发微信小程序通常会因为实例和数据池的数据推送而烦恼
通俗的说这样一个痛点就是,什么时候setData,在哪setData,怎么把实例的数据中取出来进行setData
那么有没有一种方法
当我们对变量或实例修改后,就可以自动的渲染界面而不用再setData呢?
当然有!
分析需求测试可行性
在微信小程序中
Page({
data:{
//存放数据
test:{
content:"helloworld"
}
}
})
<block>{{test.content}}</block>
即可显示出“helloworld”
这表示前端页面对数据具备多层访问
的能力
继续,如果是访问一个不存在的数据呢
<block>{{test.content2.nodata}}</block>
我们将得到一个为空的页面,并没有任何报错,即是具备访问安全
性的
那么我们把一个对象放在data中也能正常访问吗?经过测试这也是可行的。
也就是说我们完全可以把任何Object
放在data中。
通过阅读微信小程序的开发文档,我们可以知道
如果你放置一个对象在data中,在前端进行渲染时会进行
JSON.stringify + JSON.parse
对数据进行一次操作后再安全
的引用,但不会修改源数据
所以,如果我们在data中放置一个类,也是能被正常引用的
class Person{
constructor(name){
this.name = name;
}
}
let myPerson = new Person("张三");
let pageInstance;
Page({
data:{
person:myPerson
},
onLoad(){
pageInstance = this;
}
})
<block>{{person.name}}</block>
前端取值时等同于
JSON.parse(JSON.stringify(data)).?person.?name => "张三"
其中
.?
表示安全取值
那么如果需要修改名字则需要
myPerson.name = "李四";
pageInstance.setData({
person:myPerson
})
总结需求
我们需要在设置名字的同时就能够立即响应到页面,也就是说
myPerson.name = "李四";//的同时,页面就得到了更新
解决方案 Proxy + 语法欺骗
- 我们可以通过Proxy对类的实例进行链式(深度)监听,并生成事件响应来进行setData
- 但Proxy会造成类的实例方法提示功能丢失,并不方便Coding,所以需要进行构造函数的语法欺骗
代码部分
我们先来看一下经过修改后的实例
// pages/myPage/myPage.js
const { EasyPage, LaunchEnv, EasyModule } = require("../../lib/EasyAPP/EasyAPP");
class Person{
constructor(name){
this.name = name;
//语法欺骗,同时为该类进行链式(深度)监听
return new EasyModule(this);
}
}
class MyPage extends EasyPage{
async onShow() {//同微信小程序自带的onShow方法
if(await Platform.isLogin_sync()){
if(Platform.getUser().credits == undefined){
//获取用户Credits
Platform.getUser().getCredits();
}
}
}
}
new LaunchEnv({
page:new MyPage (),
data:{
Person:new Person("张三");
}
})
可以看到,我们替换了微信原生的使用Page()
来启动页面的方式
通过定义Mine对象,来设置页面的事件。
再通过new LaunchEnv()
来生成界面,并定义它的的数据,而这次,我们简单明了的传入了一个Person的对象
如果此时任何引用去修改person的name属性,都将自动发起界面的更新
同时,在微信小程序的热更新下,这种链接性也不会丢失。
如此一来,我们可以将编程模式和前端数据“完全隔离”开来,而不必将功能混杂在各个页面中,我们只需潜心开发我们的业务相关的类,在任何时候,任何地方甚至控制台,都能修改数据并显示在屏幕上。
前端需要什么数据直接访问即可。
位于"../../lib/EasyAPP/EasyAPP"
下的代码EasyAPP.js
在之后的章节中,将对代码进行功能刨析
// Auther MingZeY
// Email: 1552904342@qq.com
/**
* 微信小程序自带的一些APP方法提示接口
*/
class AppInterface{
/**
* 生命周期回调——监听小程序初始化。
* 小程序初始化完成时触发,全局只触发一次。参数也可以使用 wx.getLaunchOptionsSync 获取。
* 参数:与 wx.getLaunchOptionsSync 一致
*/
onLaunch(obj){}
/**
* 生命周期回调——监听小程序启动或切前台。
* 小程序启动,或从后台进入前台显示时触发。也可以使用 wx.onAppShow 绑定监听。
* 参数:与 wx.onAppShow 一致
*/
onShow(obj){}
/**
* 生命周期回调——监听小程序切后台。
* 小程序从前台进入后台时触发。也可以使用 wx.onAppHide 绑定监听。
*/
onHide(){}
/**
* 错误监听函数。
* 小程序发生脚本错误或 API 调用报错时触发。也可以使用 wx.onError 绑定监听。
* 参数:与 wx.onError 一致
*/
onError(error){}
/**
* 页面不存在监听函数。
* 1.9.90
* 小程序要打开的页面不存在时触发。也可以使用 wx.onPageNotFound 绑定监听。注意事项请参考 wx.onPageNotFound。
* 参数:与 wx.onPageNotFound 一致
*/
onPageNotFound(obj){}
/**
* 未处理的 Promise 拒绝事件监听函数。
* 2.10.0
* 小程序有未处理的 Promise 拒绝时触发。也可以使用 wx.onUnhandledRejection 绑定监听。注意事项请参考 wx.onUnhandledRejection。
* 参数:与 wx.onUnhandledRejection 一致
*/
onUnhandledRejection(obj){}
/**
* 监听系统主题变化
* 2.11.0
* 系统切换主题时触发。也可以使用 wx.onThemeChange 绑定监听。
* 参数:与 wx.onThemeChange 一致
*/
onThemeChange(obj){}
}
/**
* 微信小程序自带的一些Page方法提示接口
*/
class PageInterface{
data(){}
/**
* 页面加载时触发。一个页面只会调用一次,可以在 onLoad 的参数中获取打开当前页面路径中的参数。
*/
onLoad(query){}
onShow(){}
onReady(){}
onHide(){}
onUnload(){}
onPullDownRefersh(){}
onReachBottom(){}
onShareAppMessage(){}
onShareTimeline(){}
onAddToFavorites(){}
onPageScroll(){}
onResize(){}
onTabItemTap(){}
onSaveExitState(){}
}
/**
* APP 主类
*/
class EasyAPP extends AppInterface{
constructor(){
super();
}
/**
* 创建页面,但不创建Page实例,只有onLoad事件触发后才能有相关操作
*/
launch(){
let that = this;
let config = {};
let keys = Object.getOwnPropertyNames(Object.getPrototypeOf(this));
for(let key of keys){
config[key] = function(){
that[key]();
}
}
App(config);
}
}
/**
* Page 主类
*/
class EasyPage extends PageInterface{
constructor(){
super();
this.pageInstance = undefined;
this.dataContiner = this.data() || {};
}
launch(passData = {}){
let that = this;
let config = {};
//载入数据
Object.assign(that.dataContiner,passData);
Object.assign(config,{
data:that.dataContiner
})
//载入方法
let keys = Object.getOwnPropertyNames(Object.getPrototypeOf(that));
if(keys.indexOf("onLoad") == -1){
//修复没有Load无法刷新界面的问题
keys.push("onLoad");
}
for(let key of keys){
if(key == "onLoad"){
config[key] = function(...args){
that.pageInstance = this;
//TODO 更新/挂载数据
this.setData(that.dataContiner);
that["onLoad"].apply(that,args);
}
continue;
}
if(key == "data"){
continue;
}
config[key] = function(...args){
that.pageInstance = this;
that[key].apply(that,args);
}
}
Page(config);
}
setData(data){
if(this.pageInstance == undefined){
// console.log(`[Warn] ${this.constructor.name} 的 Page 对象实例未生成!更新界面失败,操作该页面以重新获得 Page 对象`);
return;
}
Object.assign(this.dataContiner,data);
this.pageInstance.setData(data);
}
}
/**
* 数据虚拟空间对象
*/
class DataVM{
constructor(obj,listener = function(){}){
this.source = obj;
this.proxy = this.bind();
this.listeners = [listener];
return this.proxy;
}
update(t,k,v,r){
console.log("更新渲染界面!");
for(let f of this.listeners){
f(t,k,v,r);
}
}
bind(){
let that = this;
let getCache = [];
let handler = {
get:function(t,k,r){
if(k == "__DataVM"){
return that;
}
if(k == "__Refersh"){
that.update(t,k,undefined,r);
return;
}
if(k == "__NOTRACK"){
return t;
}
if(t == that.source){
getCache = [];
}
getCache.push(t[k]);
let isRepeat = function(o){
for(let i = 0; i < getCache.length-1; i++){
if(o instanceof Object && getCache[i] == o){
return true;
}
}
return false;
}
if(typeof t[k] == "object" && t[k].__DataVM == undefined){
if(isRepeat(t[k])){
// console.warn("循环访问对象,已拒绝");
return undefined;
}
return reactive(t[k]);
}else{
return t[k];
}
},
set:function(t,k,v,r){
t[k] = v;
//唤起更新事件
that.update(t,k,v,r);
return true;
}
}
let reactive = function(obj){
return new Proxy(obj,handler);
}
return reactive(this.source);
}
getProxy(){
return this.proxy;
}
getSource(){
return this.source;
}
addListener(f){
this.listeners.push(f);
}
}
class EasyModule extends DataVM{}
class LaunchEnv{
constructor(obj){
let {
page,
data,
} = obj;
this.page = page;
this.data = data;
this.build();
}
async build(){
let page = this.page;
let data = this.data;
if(!page instanceof EasyPage){
throw Error();
}
page.launch(data);
//绑定数据更新事件
for(let name in data){
let targetEnv = data[name];
let dataBuilder = function(key,value){
let obj = {};
obj[key] = value;
return obj;
}
if(targetEnv.__DataVM instanceof DataVM){
let vm = targetEnv.__DataVM;
vm.addListener((target,key,value,reciver) => {
page.setData(dataBuilder(
name,
vm.getProxy()
))
})
}
}
}
}
module.exports.EasyAPP = EasyAPP;
module.exports.EasyPage = EasyPage;
module.exports.EasyModule = EasyModule;
module.exports.DataVM = DataVM;
module.exports.LaunchEnv = LaunchEnv;
网友评论