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 鸿蒙内核源码分析(总目录) | 百万汉字注解 ...
随机推荐
- Django框架详解之url
Django基本命令 下载Django pip3 install django 创建一个django project django-admin.py startproject cms 当前目录下会生成 ...
- zookeeper+kafka集群搭建
一.ZK集群安装. 解压安装包后进入conf目录,conf/zoo_sample.cfg拷贝一份命名为zoo.cfg,同时也放在conf下面. zookeeper配置文件: # The number ...
- html5+css3相关知识
一:HTML5 1.html中的meta标签 定义针对搜索引擎的关键词: <meta name="keywords" content="HTML, CSS, XML ...
- SASS概览
1.安装: sass需要使用ruby,首先安装ruby,之后: gem install sass 编译: sass input.scss output.css 2.快速入门: 变量: .scss 变量 ...
- Redis-server在windows下闪退
在win7下使用Redis(windows版)很简单,只需要去Git上下载一个压缩包,解压运行即可.但是前段时间发现win10下双击redis-server既然闪退.非常不解... 在观察了错误日志才 ...
- js中“原生”map
var map = {}; // Map map = new HashMap(); map[key] = value; // map.put(key, value); var value = map[ ...
- fensorflow 安装报错 DEPENDENCY ERROR
1.错误信息 DEPENDENCY ERROR The target you are trying to run requires an OpenSSL implementation. Your sy ...
- WinServer-IIS-身份验证\SSL设置
匿名身份验证:不需任何加密,用的最广泛 基本身份验证:需用户名和密码,采用BASE-64加密,结合SSL证书才比较安全,加密方式很弱 windows身份验证:内网用,结合域控使用 摘要式身份验证:结合 ...
- 【动态树问题】LCT学习笔记
我居然还不会LCT QAQ真是太弱了 必须学LCT QAQ ------------------线割分是我www------------ LinkCut-Tree是基于Splay(由于Splay能够非 ...
- 初始化的数值(int、double等)(一)
首先考虑一个具有几个构造函数的MyClass类.如果我们决定在这个类的私有部分加入一个新的数据成员,称为int_data_: class MyClass { public: MyClass() : i ...