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 鸿蒙内核源码分析(总目录) | 百万汉字注解 ...
随机推荐
- hdu 1754 I Hate It【线段树】
维护一个最大值 #include<cstdio> #include<cstring> #include<iostream> #include<algorith ...
- Xcode 下“ did not have any applicable content ”分析及解决
问题的产生 a.新建项目时选的iPhone b.为了做成图片启动,按照惯例去掉了LaunchStoryboard的引用,建了个LaunchImage的资源,属性里随便勾了一个,找了张匹配的图拖了过去 ...
- 关于C++程序运行程序是出现的this application has requested the runtime to terminate it in an unusual way. 异常分析
今天运行程序是出现了this application has requested the runtime to terminate it in an unusual way. 的异常报告,以前也经常 ...
- CorelDRAW X8制作金属质感3D立体按钮
本教程教您使用CorelDRAW X8制作金属质感3D立体按钮.绘图中主要应用渐变填充技巧为立体按钮表现物体质感和丰富的色彩变化,最后实现的效果也是不错的,是很实用的案例,教程难度一般,完成图如下: ...
- Kattis - bela
Bela Young Mirko is a smart, but mischievous boy who often wanders around parks looking for new idea ...
- FaceBook SDK登录功能实现(Eclipse)
由于公司游戏要进行海外推广,所以要我们接入FBSDK 实现登录,分享,投放,所以写这篇文章,也算是个工作总结.1.资料 (1).FB SDK github源码地址为 (2): [FB SDK中文接入文 ...
- Vue.mixin Vue.extend(Vue.component)的原理与区别
1.本文将讲述 方法 Vue.extend Vue.mixin 与 new Vue({mixins:[], extend:{}})的区别与原理 先回顾一下 Vue.mixin 官网如下描述: Vue. ...
- 有效解决ajax传中文时,乱码的情况,php处理接收到的值
在抽奖环节时,需把获奖名单通过ajax的post方式传输给php后台进行储存,但是php接收到的值确是乱码.在百度之后并没有找到合适的解决方法. 则使用js的encodeURI函数可以有效解决,但不知 ...
- [luogu 2568] GCD (欧拉函数)
题目描述 给定整数N,求1<=x,y<=N且Gcd(x,y)为素数的数对(x,y)有多少对. 输入输出格式 输入格式: 一个整数N 输出格式: 答案 输入样例#1: 4 输出样例#1: 4 ...
- 单元测试Struts2Spring项目的Action和Service(包含源码)
最近,认真实践了单元测试Struts2.Spring等Java项目,今天特意写的是单元测试Struts2Spring项目的Action和Service. 由于已经写过不少Web开发框架单元测试的代码, ...