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 鸿蒙内核源码分析(总目录) | 百万汉字注解 ...
随机推荐
- Kattis -I Can Guess the Data Structure!
I Can Guess the Data Structure! There is a bag-like data structure, supporting two operations: 1 x1 ...
- js(Mandango:壮汉专用,电影院划位工具)
Mandango:壮汉专用,电影院划位工具 <body onload="initSeats();"> <div style="margin-top:75 ...
- Svn 提交新文件
1.右击文件: 2.按图做:
- [luogu] P3210 [HNOI2010]取石头游戏(贪心)
P3210 [HNOI2010]取石头游戏 题目描述 A 公司正在举办一个智力双人游戏比赛----取石子游戏,游戏的获胜者将会获得 A 公司提供的丰厚奖金,因此吸引了来自全国各地的许多聪明的选手前来参 ...
- WinServer-AD域控入门
计算机账户和用户账户的区别 域控中不需要事先建立计算机账户,但必须建立登录用户账户. 计算机只要知道域控管理员或者授权管理账户,就可以利用此账户为所有计算机加域. 计算机加域成功之后,都会在AD管理里 ...
- 怎样制作C#安装程序
近期须要制作一个C#安装.在网上找了一些资料发现都不是非常完整,最后自己综合了一些资料,而且通过亲自检測,最后成功完毕C#打包成安装程序(打包成最简单的一种安装程序.假设须要更高的功能请自己在开发). ...
- hello world to php( mac 配置 xmapp virtual host)
一.安装xmapp.安装完以后查看,服务是否都能启动(数据库和server) 二.配置自己的virtualhost 1.系统host文件加入server的域名(在浏览器中输入域名后会先通过 ...
- linux 磁盘分区,主分区,扩展分区,逻辑分区以sata接口为例
以sata接口(依据linux内核检測其顺序 sda,sdb...)为例, 1, 硬盘的限制,最多仅仅能设置4个分区(主分区+扩展分区),路径例如以下, /dev/sda1 /dev/sda2 ...
- 一个人的旅行 HDU杭电2066【dijkstra算法 || SPFA】
pid=2066">http://acm.hdu.edu.cn/showproblem.php? pid=2066 Problem Description 尽管草儿是个路痴(就是在杭电 ...
- bzoj2150: 部落战争(匈牙利)
2150: 部落战争 题目:传送门 题解: 辣鸡数据..毁我AC率 先说做法,很容易就可以看出是二分图匹配的最小路径覆盖(可能是之前不久刚做过类似的题) 一开始还傻逼逼的去直接连边然后准备跑floyd ...