.
  • 最新新闻
  • 安卓手机app开发用什么软件_app开发_大鱼科技
    搅局者来了,他叫小程序(微信、支付宝),也叫直达服务(小米)。江湖的玩法变了。 右下角的墨迹天气,是工具类App里的老炮,用户每天开一次、每次瞅一眼的打开方式,让他很头疼。「这怎么卖广告?」 为了挣钱,他在【天气】下面放了一坨新闻,还搞了个【时景】的UGC体系,提升停留时长。又在【我】的页面放了一堆广告入口。
  • 制作手机app大概费用多少钱_app开发_大鱼科技
    有时为了突出名称的意义,可能会跟品牌不一致,但网民搜索又可能搜索不到,这时关键字优化就很重要了,核心优化点在于:内容描述中必须包含核心关键字,并获得较好排名,如搜索『简历』,你的简历相关的小程序是否比较靠前。
  • 开发一个app需要多少钱费用_app开发_大鱼科技
    微信的表情雨让中国人的社交充满了人情味儿。当你在对话框输入“么么哒”或者“想你了”,聊天页面会掉下相应的表情雨,成为大多情侣互传情愫的标准动作。中国传统情人节日“七夕节”已至,情侣们在微信“秀恩爱”的方式又多了一种——黄金红包。
  • 什么是小程序_小程序开发需要多少钱_app开发_大鱼科技
    什么是小程序:小程序是一种不需要下载安装即可使用的应用,它实现了应用“触手可及”的梦想,用户扫一扫或者搜一下即可打开应用。也体现了“用完即走”的理念,用户不关心是否安装太多应用的问题。应用将无处不在,随时随地可用,但又无需安装卸载。
  • 看牌器 透视在哪里买_app开发_大鱼科技
    这个微信小程序的创意始于2012年,当时仅仅开发了Win平台桌面应用,随着移动互联网发展,开发技术栈难度降低
  • 做一个平台app需要多少钱_app开发_大鱼科技
    访问次数:昨日访问小程序内所有页面总次数,多个页面之间的跳转、同一页面的重复访问计为多次访问。 访问人数:昨日访问小程序内所有页面的总用户数,同一用户多次访问不重复计。 新访问用户数:首次访问小程序页面的用户数,同一用户多次访问不重复计。
  • 一天能赚2万的黑方法_app开发_大鱼科技
    没深思小程序最近一个月内更新7次背后是微信内部前所未有的力度在支持小程序(公众号都不曾有此殊遇),特别是微信刚公布2017年Q1月活账号破9.38亿。面对如此巨大的流量洼地,我们如何借助小程序来低成本/0成本获取用户?
  • 网赌破解器哪里下载_app开发_大鱼科技
    小程序定位 对于公司而言,小程序究竟是一个什么产品定位?以目前的大环境而言,无非就是几个关键点: 蹭一波热点,吸引眼球,做小程序这件事情本身就是一个吸引眼球的事情; 延伸产品线,对已有产品线做一个开辟,确保整个线的完整性; 借助小程序,赶上这趟车,营销导流;
  • 定制一款app大概需要多少钱_app开发_大鱼科技
    很多人不禁要对此举的意义发问:以微信的算法而出的搜索结果,岂不是严重遏制了新生小程序的后来者居上?这样即使定义了关键词,对推广而言并没有实质性的意义?微信官方自然也想到了与之相辅相成的策略,我们甚至可以认为后来的这步棋才是微信的本来目的。那就是于6月中旬首次出现于人们视野中的「搜索关键词广告」
  • 软件外包公司_app开发_大鱼科技
    代码的复杂度是评估一个项目的重要标准之一。较低的复杂度既能减少项目的维护成本,又能避免一些不可控问题的出现。然而在日常的开发中却没有一个明确的标准去衡量代码结构的复杂程度,大家只能凭着经验去评估代码结构的复杂程度,比如,代码的程度、结构分支的多寡等等。当前代码的复杂度到底是个什么水平?什么时候就需要我们去优化代码结构、降低复杂度?这些问题我们不得而知。 因此,我们需要一个明确的标准去衡量代码的复杂度。
  • 热门新闻
制作app平台需要多少钱_app开发_大鱼科技
时间:2021-01-10 08:57:23
制作app平台需要多少钱_app开发_大鱼科技

哈喽,今天我们聊聊小程序的状态管理~(有这玩意吗)

我们要实现什么

很简单,实现一个全局响应式的globalData,任何地方修改=>全局对应视图数据自动更新。

并且我希望在此过程中尽量不去change原有的代码逻辑。

为啥要实现

写过小程序的都知道,状态管理一直是小程序的一大痛点。

由于小程序官方没有一个全局状态管理机制,想要使用全局变量只能在app.js里调用App()创建一个应用程序实例,然后添加globalData属性。但是,这个globalData并不是响应式的,也就是说在某个页面中修改了其某个值(如果初始化注入到data中)无法完成视图更新,更别说全局页面和组件实例的更新了。

当前的主流做法

我们先来了解下当下比较流行的方案。

我们以westore为例,这是鹅厂出的一款覆盖状态管理、跨页通讯等功能的解决方案,主要流程是通过自维护一个store(类似vuex)组件,每当页面或组件初始化时注入并收集页面依赖,在合适的时候手动update实现全局数据更新。提供的api也很简洁,但是如果使用的话需要对项目原有代码做一些侵入式的改变。比如说一:创建页面或组件时只能通过该框架的api完成。二:每次改变全局对象时都要显式的调用this.update()以更新视图。

其他一些方案也都是类似的做法。但我实在不想重构原项目(其实就是懒),于是走上了造轮子的不归路。

准备工作

正式开始前,我们先理一下思路。我们希望实现

  1. 将globalData响应式化。

  2. 收集每个页面和组件data和globalData中对应的属性和更新视图的方法。

  3. 修改globalData时通知所有收集的页面和组件更新视图。

其中会涉及到发布订阅模式,这块不太记得的可以看看我之前的文章哟。

Talk is cheap. Show me the code.

说了这么多,也该动动手了。

首先,我们定义一个调度中心Observer用来收集全局页面组件的实例依赖,以便有数据更新时去通知更新。 但这里有个问题,收集整个页面组件实例未免太浪费内存且影响初始化渲染(下面的obj),如何优化呢?

// 1.Observer.js

export default class Observer {

constructor() {

this.subscribers = {};

}

add (key, obj) { // 添加依赖 这里存放的obj应该具有哪些东东?

if (!this.subscribers[key]) this.subscribers[key] = [];

this.subscribers[key].push(obj);

}

delete () { // 删除依赖

// this.subscribers...

}

notify(key, value) { // 通知更新

this.subscribers[key].forEach(item => {

if (item.update && typeof item.update === 'function') item.update(key, value);

});

}

}

Observer.globalDataObserver = new Observer(); // 利用静态属性创建实例(相当于全局唯一变量)

复制代码

相信很多同学想到了,其实我们只需要收集到页面组件中data和更新方法(setData)就够了,想到这里,不妨自定义一个Watcher类(上面的obj),每次页面组件初始化时new Watcher(),并传入需要的数据和方法,那我们先完成初始化注入的部分。

// 2.patcherWatcher.js

// 相当于mixin了Page和Component的一些生命周期方法

import Watcher from './Watcher';

function noop() {}

const prePage = Page;

Page = function() {

const obj = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};

const _onLoad = obj.onLoad || noop;

const _onUnload = obj.onUnload || noop;

obj.onLoad = function () {

const updateMethod = this.setState || this.setData; // setState可以认为是diff后的setData

const data = obj.data || {};

// 页面初始化添加watcher 传入方法时别忘了绑定this指向

this._watcher = this._watcher || new Watcher(data, updateMethod.bind(this));

return _onLoad.apply(this, arguments);

};

obj.onUnload = function () {

// 页面销毁时移除watcher

this._watcher.removeObserver();

return _onUnload.apply(this, arguments);

};

return prePage(obj);

};

// 。。。下面省略了Component的写法,基本上和Page差不多

复制代码

接着,根据我们的计划,完成Watcher的部分。这里会对传入的data做层过滤,我们只需要和globalData对应的属性(reactiveData),并在初始化时注入Observer。

// 3.Watcher.js

import Observer from './Observer';

const observer = Observer.globalDataObserver;

let uid = 0; // 记录唯一ID

export default class Watcher {

constructor() {

const argsData = arguments[0] ? arguments[0] : {};

this.$data = JSON.parse(JSON.stringify(argsData));

this.updateFn = arguments[1] ? arguments[1] : {};

this.id = ++uid;

this.reactiveData = {}; // 页面data和globalData的交集

this.init();

}

init() {

this.initReactiveData();

this.createObserver();

}

initReactiveData() { // 初始化reactiveData

const props = Object.keys(this.$data);

for(let i = 0; i < props.length; i++) {

const prop = props[i];

if (prop in globalData) {

this.reactiveData[prop] = getApp().globalData[prop];

this.update(prop, getApp().globalData[prop]); // 首次触发更新

}

}

}

createObserver() { // 添加订阅

Object.keys(this.reactiveData) props.forEach(prop => {

observer.add(prop, this);

});

}

update(key, value) { // 定义observer收集的依赖中的update方法

if (typeof this.updateFn === 'function') this.updateFn({ [key]: value });

}

removeObserver() { // 移除订阅 通过唯一id

observer.delete(Object.keys(this.reactiveData), this.id);

}

}

复制代码

最后,利用Proxy完成一个通用的响应式化对象的方法。

这里有个小细节,更改数组时set会触发length等一些额外的记录,这里就不细说了,有兴趣的同学可以了解尤大在vue3.0的是如何处理的(避免多次 trigger)。

// 4.reactive.js

import Observer from './Observer';

const isObject = val => val !== null && typeof val === 'object';

function reactive(target) {

const handler = {

get: function(target, key) {

const res = Reflect.get(target, key);

return isObject(res) ? reactive(res) : res; // 深层遍历

},

set: function(target, key, value) {

if (target[key] === value) return true;

trigger(key, value);

return Reflect.set(target, key, value);

}

};

const observed = new Proxy(target, handler);

return observed;

}

function trigger(key, value) { // 有更改记录时触发更新 => 会调用所有Watcher中update方法

Observer.globalDataObserver.notify(key, value);

}

export { reactive };

复制代码

最后的最后,在app.js引用就好啦。

// app.js

require('./utils/patchWatcher');

const { reactive } = require('./utils/Reactive');

App({

onLaunch: function (e) {

this.globalData = reactive(this.globalData); // globalData响应式化

// ...

},

// ...

globalData: { /*...*/ }

复制代码

总结

综上,我们一步一步从 页面组件初始化注入=>定义Watcher类=>将Watcher收集到Observer中 并在此触发更新=>app.js全局引入 这几个步骤完成globalData的响应式化,结果是通过新增4个文件 app.js3行代码(包括注释等共100多行代码),几乎以零侵入的方式完成,并且实现了功能分离,具有一定的可扩展性。

时间仓促,文中肯定会有一些不够严谨的地方,欢迎大家指正和讨论。

感谢阅读的你!