omi-mp-create源码加注
omi-mp-create是dntzhang写的小程序框架,主要功能是实现全局状态自动更新和页面间通信,传送门。
代码虽然简单但是注释不多读起来还是需要一点时间理解,因此在上面加入了个人理解的注释方便查看~
在里面还用到的一个监听更改库obaa
/*!
* omi-mp-create v0.1.0 by dntzhang
* Github: https://github.com/Tencent/omi
* MIT Licensed.
*/
import obaa from './obaa'
import mitt from './mitt'
// 页面构造器
// 在onload阶段执行下面的
// 把data深拷贝到oData
// 赋值store(这个应该没用的吧。。。除非主动改变了onLoad中的this指向,但是这有啥用)
function _Page(option) {
const onLoad = option.onLoad
option.onLoad = function (e) {
this.store = option.store //这行被我注释了
this.oData = JSON.parse(JSON.stringify(option.data))
observe(this)
onLoad && onLoad.call(this, e)
}
Page(option)
}
// 组件构造器
// 在ready阶段执行
// 把data深拷贝到oData
// 如果没有store的话,就从当前页面中的store里面取
function _Component(option) {
const ready = option.ready
option.ready = function () {
const page = getCurrentPages()[getCurrentPages().length - 1]
this.store = option.store || page.store
this.oData = JSON.parse(JSON.stringify(option.data))
observe(this)
ready && ready.call(this)
}
Component(option)
}
// 把类似 "#-a-0-c" 的字符串处理成 "a[0].c"这种能够用于小程序渲染页面的路径(索引)
// 大概因为这样写后面的代码就不用管是object还是Array都用 - 连接反正都能直接塞进去了
// 作者应该是为了使用 obaa 监听变更才需要这个函数把path转换小程序的格式
function fixPath(path) {
let mpPath = ''
const arr = path.replace('#-', '').split('-')
arr.forEach((item, index) => {
if (index) {
if (isNaN(parseInt(item))) {
mpPath += '.' + item
} else {
mpPath += '[' + item + ']'
}
} else {
mpPath += item
}
})
return mpPath
}
// 使用obaa监听 oData
// 让对 oData的修改能够自动使用setData更新到视图上
function observe(ele) {
let timeout = null
let patch = {} // patch 是要应用到视图上的变更
// 下面的索引指的是patch的key值
// 如 a[2].c 这样的能够符合小程序setData规范的key
obaa(ele.oData, (prop, value, old, path) => {
clearTimeout(timeout)
if (prop.indexOf('Array-push') === 0) {
// 如果是在数组上push元素
// 把新的值赋值到 patch上
let dl = value.length - old.length
for (let i = 0; i < dl; i++) {
patch[fixPath(path + '-' + (old.length + i))] = value[(old.length + i)]
}
} else if (prop.indexOf('Array-') === 0) {
// 如果是非 push 的其他数组方法
// 把索引用新值替代
patch[fixPath(path)] = value
} else {
// 不是数组操作就根据path-prop索引到对应的值进行赋值
patch[fixPath(path + '-' + prop)] = value
}
// 这里作者使用 setTimeout 0 的方法把setData操作放到异步队列中而不是立即同步执行
// 在setData后重新把patch置为空
// 配合上面的clearTimeout可以避免频繁setData的问题
// 同步修改数据的过程中所有的patch都是同一个对象
// 在修改一结束后才会使用 setData(patch) 一次性把所有的更改更新到视图上
timeout = setTimeout(() => {
ele.setData(patch)
patch = {}
}, 0)
})
}
// 一个全局的store的引用
// 等同于app.globalData.store
let globalStore = null
function create(store, option) {
// 页面构造函数
if (arguments.length === 2) {
// 如果option里面有data
// 把option.data深拷贝到store.data对象中
if (option.data && Object.keys(option.data).length > 0) {
Object.assign(store.data, JSON.parse(JSON.stringify(option.data)))
}
// 初始化store.instances
if (!store.instances) {
store.instances = {}
}
// 把store绑定在全局变量app.globalData上
getApp().globalData && (getApp().globalData.store = store)
// 函数中的globalStore也指向 store
globalStore = store
// 页面data绑定store.data
option.data = store.data
observeStore(store)
const onLoad = option.onLoad
option.onLoad = function (e) {
// 把全局的store绑定为页面的属性
this.store = store
// 以页面的路由作为key值
// 把当前页面的绑定在store.instances上
store.instances[this.route] = []
store.instances[this.route].push(this)
// 调用页面原有的onLoad
onLoad && onLoad.call(this, e)
}
Page(option)
}
// 组件构造函数(下面的store代表一个组件的option)
else {
const ready = store.ready
store.ready = function () {
// 找到组件对应的页面page
this.page = getCurrentPages()[getCurrentPages().length - 1]
// 把组件所属页面page的store绑定到当前组件上
this.store = this.page.store
// 用深拷贝把全局store.data 中的属性绑定给 组件的store.data
// 这里用深拷贝大概是因为作者认为不应该用修改this.store.data来影响全局
// 而应该使用全局的store.data直接修改
// 我觉得这个深拷贝可以直接去掉?那样就不用在组件内再引入一次store了吧
// 应该是为了遵循小程序不能够直接修改this.data的原则
store.data && Object.assign(this.store.data, JSON.parse(JSON.stringify(store.data)))
// 把糅合后的store.data赋值给组件的data
this.setData.call(this, this.store.data)
// 放入当前页面路由的监听队列
this.store.instances[this.page.route].push(this)
// 调用原有的ready
ready && ready.call(this)
}
Component(store)
}
}
// 监听全局的store
// 类似上面的observe方法,不同的是这里直接监听的store
// 而且使用 _update更新全局视图而不是 this.setData的简单更新
function observeStore(store) {
let timeout = null
let patch = {}
obaa(store.data, (prop, value, old, path) => {
clearTimeout(timeout)
if (prop.indexOf('Array-push') === 0) {
let dl = value.length - old.length
for (let i = 0; i < dl; i++) {
patch[fixPath(path + '-' + (old.length + i))] = value[(old.length + i)]
}
} else if (prop.indexOf('Array-') === 0) {
patch[fixPath(path)] = value
} else {
patch[fixPath(path + '-' + prop)] = value
}
timeout = setTimeout(() => {
_update(patch)
patch = {}
}, 0)
})
}
function _update(kv) {
for (let key in globalStore.instances) {
// 对 instances 中的所有页面的所有实例都调用setData更新视图
// 这里的instances中包含所有的页面
// 每个页面对应的数组中包含这个页面实例本身及它所包含的组件实例
globalStore.instances[key].forEach(ins => {
ins.setData.call(ins, kv)
})
}
// 这里还调用了一个全局的onChange方法
// 大概是留给开发者监听的store?
// 但是好像没啥用
globalStore.onChange && globalStore.onChange(kv)
}
create.Page = _Page
create.Component = _Component
create.obaa = obaa
// 提供一个mitt的引用
create.mitt = mitt
// 初始化一个mitt(供全局使用)
create.emitter = mitt()
export default create
omi-mp-create源码加注的更多相关文章
- obaa源码加注
这个是dntzhang写的用于监听变量更改的库obaa,加上一点注释方便理解~ 传送门 /* obaa 1.0.0 * By dntzhang * Github: https://github.com ...
- ElasticStack系列之十六 & ElasticSearch5.x index/create 和 update 源码分析
开篇 在ElasticSearch 系列十四中提到的问题即 ElasticStack系列之十四 & ElasticSearch5.x bulk update 中重复 id 性能骤降,继续这个问 ...
- 鸿蒙内核源码分析(互斥锁篇) | 比自旋锁丰满的互斥锁 | 百篇博客分析OpenHarmony源码 | v27.02
百篇博客系列篇.本篇为: v27.xx 鸿蒙内核源码分析(互斥锁篇) | 比自旋锁丰满的互斥锁 | 51.c.h .o 进程通讯相关篇为: v26.xx 鸿蒙内核源码分析(自旋锁篇) | 自旋锁当立贞 ...
- Android 网络框架之Retrofit2使用详解及从源码中解析原理
就目前来说Retrofit2使用的已相当的广泛,那么我们先来了解下两个问题: 1 . 什么是Retrofit? Retrofit是针对于Android/Java的.基于okHttp的.一种轻量级且安全 ...
- Fresco 源码分析(三) Fresco服务端处理(3) DataSource到Producer的适配器逻辑以及BitmapMemoryCacheProducer处理的逻辑
4.3.1.2.1 Producer和DataSource之间适配器处理的逻辑 还是从程序的入口开始说吧 CloseableProducerToDataSourceAdapter.create() 源 ...
- RxJava系列6(从微观角度解读RxJava源码)
RxJava系列1(简介) RxJava系列2(基本概念及使用介绍) RxJava系列3(转换操作符) RxJava系列4(过滤操作符) RxJava系列5(组合操作符) RxJava系列6(从微观角 ...
- 用尽洪荒之力学习Flask源码
WSGIapp.run()werkzeug@app.route('/')ContextLocalLocalStackLocalProxyContext CreateStack pushStack po ...
- 鸿蒙内核源码分析(文件句柄篇) | 深挖应用操作文件的细节 | 百篇博客分析OpenHarmony源码 | v69.01
百篇博客系列篇.本篇为: v69.xx 鸿蒙内核源码分析(文件句柄篇) | 深挖应用操作文件的细节 | 51.c.h.o 文件系统相关篇为: v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么说 ...
- 鸿蒙内核源码分析(静态站点篇) | 五一哪也没去就干了这事 | 百篇博客分析OpenHarmony源码 | v52.02
百篇博客系列篇.本篇为: v52.xx 鸿蒙内核源码分析(静态站点篇) | 五一哪也没去就干了这事 | 51.c.h.o 前因后果相关篇为: v08.xx 鸿蒙内核源码分析(总目录) | 百万汉字注解 ...
随机推荐
- 苹果操作系统名称演变史 新名称macOS
历史回顾 发布年代 名称 序号 1994-1999 Classic Mac OS 1-9 2001-2011 Mac OS X 10.0-Lion 2012-2015 OS X Mountain Li ...
- ZBrush中如何对模型进行减面操作
Decimation Master是ZBrush 4R8自带的一个插件.中文名叫减面大师.其功能非常强大,也非常的方便,可以帮助我们提高效率,减少电脑资源损耗.作为一名3D美术师是必须掌握的一个技术. ...
- [读书笔记] Python 数据分析 (八)画图和数据可视化
ipython3 --pyplot pyplot: matplotlib 画图的交互使用环境
- oracle数据库服务介绍
共有7个服务,这七个服务的含义分别为: 1. Oracle ORCL VSS Writer Service:Oracle卷映射拷贝写入服务,VSS(Volume Shadow Copy Service ...
- SpringMVC上传文件(图片)并保存到本地
SpringMVC上传文件(图片)并保存到本地 小记一波~ 基本的MVC配置就不展示了,这里给出核心代码 在spring-mvc的配置文件中写入如下配置 <bean id="multi ...
- webpack的热更新
webpack的热更新是如何做到的?说明其原理? webpack的热更新又称热替换(Hot Module Replacement),缩写为HMR. 这个机制可以做到不用刷新浏览器而将新变更的模块替换掉 ...
- 【codeforces 235B】Let's Play Osu!
[题目链接]:http://codeforces.com/problemset/problem/235/B [题意] 让你玩一个游戏,游戏结果由一个长度为n的01字符组成; 这个结果的分数与连续的1的 ...
- 【codeforces 508E】Artur and Brackets
[题目链接]:http://codeforces.com/problemset/problem/508/E [题意] 让你构造一个括号字符串; 使得每个从左往右数第i个左括号在这个括号序列中与之匹配的 ...
- SELECT使用子查询
SELECT使用子查询 SELECT使用子查询,该子查询会执行多次, 次数是由记录数量决定.效率比较低,不推荐使用. //查询部门编号,工资大于等于2000的人数, //工资小于2000的人 ...
- JavaWeb初学者session的使用
使用request对象的getSession()获取session,如果session不存在则创建一个 HttpSession session = request.getSession();将数据存储 ...