1.【vue-router源码】八、源码router.go、解析router.back、函数router.forward源码解析
2.Unlua源码解析(附二) 源码中的源码重要类及核心函数逐行解释
3.Kswapd 源码解析
4.Vue源码解析(2)-$mount实现
5.Vscode-nls源码解析-NLS国际化实现
6.Redux(4.0.4)源码解析
【vue-router源码】八、router.go、解析router.back、函数node.js 项目源码router.forward源码解析
vue-router源码系列文章带您深入了解vue-router的源码内部实现。我们关注的解析是router.go、router.back、函数router.forward这三个函数的源码源码解析。阅读本文前,解析请确保您熟悉vue-router的函数基本使用,如需了解,源码可访问vue-router官网。解析
go函数允许用户在历史记录中前进或后退,函数参数delta为正则表示前进,为负则表示后退。
go函数内部调用history.go方法,触发popstate事件。在createWebHistory函数中,通过useHistoryListeners注册popstate事件监听器。此监听器在执行history.go后被激活。
popstate监听函数执行时,调用listeners中的listener。listener在setupListeners方法中被添加,用于根据地址栏的url进行首次跳转。该跳转由push方法完成,进而执行pushWithRedirect方法,最后调用finalizeNavigation,最终调用markAsReady方法。
在markAsReady方法内,调用setupListeners中的一个方法,添加一个监听函数。这个监听函数与push过程相似,但若出现导航取消、冗余或位置错误等错误信息,需要将历史记录回退至相应位置。
go执行流程:通过调用history.go方法触发popstate事件,该事件处理与push过程相似,但在错误处理方面,会将历史记录回退。
back函数实现原理等同于go(-1),即后退一步。
forward函数实现原理等同于go(1),即前进一步。
总结,go、back、forward方法通过调用history.go方法,触发popstate事件,事件处理与push过程相似,电话呼网页源码但在处理错误信息时,会将历史记录回退到正确位置。
Unlua源码解析(附二) 源码中的重要类及核心函数逐行解释
源码解析:重要类及核心函数逐行解释 1. FClassDesc 该类用于描述一个类,包含类名、类大小和继承关系等信息。 2. FFunctionDesc 对应UE中的UFunction,存储更详细信息,如参数、元数据,允许FFunctionDesc调用方法。 3. FProporityDesc 描述参数,并提供参数在Lua和C++间转换的辅助方法。 4. FFieldDesc 用于描述字段的类。 5. FReflectionRegistry 用于注册反射信息,借助UE反射接口加载类。 6. FLuaContext 全局类,负责绑定Lua对象和实现Lua与C++间的交互。 7. LuaCore 包含很多关键方法,如注册类、注册方法,是Unlua的核心类。 8. UUnLuaManager 集成绑定Lua与C++的多种方法。FReflectionRegistry内重要方法
2.1 RegisterClass
-: 通过UE反射接口尝试加载指定类。 : 调用RegisterClass方法。2.2 RegisterClass
-: 若无参数,返回。 -: 获取并检查类的类型信息,仅当类型为Struct时继续。 -: 若已注册,使用注册信息;否则注册新信息,返回。2.3 RegisterClassInternal
存名称和Struct到FClassDesc字典,便于后续使用。 -: 创建FClassDesc并记录相关信息。 -: 遍历父类,记录父类名称和Struct。2.4 GetClassChain
获取类的继承链,OutChain表示类及其父类。LuaCore内重要方法
3.1 Global_RegisterClass
读取类型信息,注册类。3.2 RegisterClass
记录反射信息,创建元表,便于Lua与C++交互。3.3 RegisterClassInternal
创建元表,设置元方法,记录全局表中。3.4 RegisterClassCore
创建元表,设置元方法,记录元表信息。3.5 SetTableForClass
将类元表放入全局表。源码外泄如何立案3.6 Class_Index
处理类索引方法。3.7 GetField
获取字段或方法。3.8 GetFunctionList
获取模块内所有方法。3.9 PushObjectCore
创建并绑定Lua对象。3. NewLuaObject
创建Lua表表示UObject。FLuaContext内重要方法
4.1 FindExportedReflectedClass
通过名称查找导出的反射类。4.2 NotifyUObjectCreated
: 存储新创建的Object。 : 尝试绑定Lua到Object。4.3 TryToBindLua
绑定Lua模块到UObject。UUnLuaManager内重要方法
5.1 Bind
新UObject实例创建时,创建Lua对象并绑定。5.2 BindInternal
实现Lua绑定UObject的关键函数。 方法涵盖模块名与C++对象关联、覆盖C++函数、处理动画覆盖等。Kswapd 源码解析
kswapd是Linux内核中的一个内存回收线程,主要用于内存不足时回收内存。初始化函数为kswapd_init,内核为每个节点分配一个kswapd进程。每个节点的pg_data_t结构体中维护四个成员变量,用于管理kswapd线程。
在初始化后,每个节点的kswapd线程进入睡眠状态。唤醒时机主要在被动唤醒和主动唤醒两种场景:被动唤醒是内存分配进程唤醒并完成异步内存回收后,对节点内存环境进行平衡度检查,若平衡则线程短暂休眠ms后主动唤醒。主动唤醒是内存回收策略调用kswapd,对节点进行异步内存回收,让节点达到平衡状态。
内存回收包括快速和直接两种方式,但系统周期性调用kswapd线程平衡不满足要求的节点,因为有些任务内存分配不允许阻塞或激活I/O访问,回收内存相当于亡羊补牢,系统利用空闲时间进行内存回收是必要的。
kswapd线程通过module_init(kswapd_init)创建,一般处于睡眠状态等待被唤醒,当系统内存紧张时,会唤醒kswapd线程,调整不平衡节点至平衡状态。
kswapd函数包含alloc_order、reclaim_order和classzone_idx三个变量,用于控制线程执行流程。kswapd_try_to_sleep函数判断是否睡眠并让出CPU控制权,同时是线程唤醒的入口。balance_pgdat函数是实际内存回收操作,涉及内存分配失败后唤醒kswapd线程,调用此函数对指定节点进行异步内存回收。
kswapd_shrink_node函数通过shrink_node对低于sc->reclaim_idx的非平衡zone区域进行回收。
总结kswapd执行流程,其生命周期与Linux操作系统相似,用源码乘法计算平时处于睡眠状态让出CPU控制权。在内存紧张时被唤醒,有被动唤醒和周期性主动唤醒两种时机。被动唤醒发生在内存分配任务获取不到内存时,表明系统内存环境紧张,主动唤醒则是内存回收策略的执行。线程周期性唤醒在被动唤醒后的短暂时间内,原因在于系统内存环境紧张,需要在这段时间内进行内存回收。
Vue源码解析(2)-$mount实现
在上一节中,我们了解到Vue实例的创建过程中,构造函数会执行_init()函数,其中关键步骤是调用vm.$mount(vm.$options.el),这标志着实例已开始挂载到DOM。$mount是Vue渲染的核心函数。
本章节我们将深入探讨Vue的渲染过程,但会跳过一些细节,以便在后续章节中详细剖析。首先,理解Vue的两种构建方式是关键:独立构建(包含template编译器)和运行时构建(不包含模板编译器)。独立构建支持服务端渲染,而运行时构建体积更小。
接下来,我们开始分析Vue源码。$mount方法的实现与平台和构建方式相关,这里我们关注运行时版本。在src/platforms/web/entry-runtime-with-compiler.js中,$mount被添加到Vue原型上,它接收el参数,可能是字符串或DOM元素。
当el为字符串时,会通过query方法将其转换为DOM节点。然后判断el不能为body或html,以防止意外覆盖。如果没有render函数,会根据template生成render,同时处理多模板形式。getOuterHTML函数获取el的内容和DOM。
$mount最终调用mount函数,这个过程涉及核心的mountComponent方法,生成虚拟Node并实例化渲染Watcher,其回调中调用updateComponent更新DOM。这部分在core/instance/lifecycle.js中,会检查render函数并处理特殊情况,如未定义或使用template语法的runtime-only版本。
updateComponent是渲染和更新的核心函数,由Watcher(在'src/core/observer/watch.js'定义)在数据变化时调用。Watcher在初始化时执行回调,当数据更新时也执行。整个过程体现了观察者模式,源码管控系统$mount中调用updateComponent的过程涉及template到render的转换,以及初次渲染或数据变更时的调用。
虽然我们已经概述了$mount的流程,但关于render函数的编译步骤并未深入讲解。编译过程包括添加web平台特性、解析template为AST、优化节点、生成render函数字符串并缓存。下一节将详细剖析这五个步骤的源码实现,敬请期待。
Vscode-nls源码解析-NLS国际化实现
探究vscode-nls源码解析,深入了解其实现细节。
NLS,自然语言字符串,vscode插件使用NLS进行国际化处理。
初始化时,通过initializeSettings函数根据vscode配置初始化options与resolvedBundles,此过程涉及languagePackSupport、messageFormat等。
调用nls.config,源码位于src/node/main.ts,此过程中重点在于处理opts.messageFormat与opts.bundleFormat。messageFormat类型有三种,bundleFormat类型有两种,若需进行bundle处理,则应调整nls.config输入形式。
在需要国际化的文件中,调用nls.loadMessageBundle,将当前文件路径作为参数传递,若messageFormat设置为both或bundle,将执行特定代码。
tryFindMetaDataHeaderFile成功返回,需在打包脚本中调用vscode-nls-dev中的nls.bundleMetaDataFiles函数生成nls.metadata.header.json文件。
初次获取bundle时,resolvedBundles为空数组,首次获取必定为undefined。通过loadNlsBundle内部的findInTheBoxBundle方法,读取nls.bundle.zh-cn.json文件,此文件需由vscode-nls-dev的bundleLanguageFiles函数打包所有in文件,读取数据为vscode当前语言信息。
如何获取文件的国际化信息?在插件中使用返回的createScopedLocalizeFunction,传入key、message、args,key转为number类型作为messages的索引,返回messages[key]。理解过程在vscode-nls插件使用场景中,文件国际化信息即通过此函数调用获取。
若设置messageFormat为.file,resolveLanguage则负责查找对应文件的in.json文件,通过readJsonFileSync读取,之后createScopedLocalizeFunction与bundle方式相同。
初次import vscode-nls,bundle将所有文件的国际化信息存储在resolvedBundles数组中,后续文件读取信息直接从数组中获取。而file方式则每次读取对应.in.json文件来获取信息。
Redux(4.0.4)源码解析
Redux源码解析 Redux源代码解析旨在清晰展示其核心组件及工作流程,力求用最简洁的语言阐述每个关键部分的功能。Redux提供了一个状态管理库,以管理应用的全局状态。以下是Redux核心组件的主要解析: createStore.js export default function createStore(reducer, preloadedState, enhancer) createStore函数是Redux的核心,负责创建一个状态存储对象。它可以接受三个参数:reducer(减少操作函数)、预加载状态(初始状态)和增强器(可选参数,用于添加额外功能)。 getState 获取当前状态,操作简单直接。 subscribe 向监听列表中添加监听函数,返回取消监听函数。在调用dispatch时订阅或取消订阅,不会影响正在进行的dispatch。下一次dispatch时,将使用订阅列表的最新快照。 dispatch 执行reducer获取最新状态,并依次执行监听队列中的函数。 replaceReducer 替换当前的reducer。执行后,dispatch一次更新状态。一般不常用。 observable 未见实际应用,可能用于特定场景。使用了symbol-observable包,对于熟悉该包的开发者来说,此部分可能有更多探索空间。 utils 包括actionTypes.js、isPlainObject.js、warning.js等辅助函数。actionTypes.js定义了Redux保留的私有操作类型,用于确保操作的正确处理。isPlainObject.js用于判断action对象是否为原生对象。warning.js用于抛出错误,保持代码质量。 applyMiddleware.js 通过createStore(reducer,applyMiddleware(...middleware))执行,返回带有中间件增强的dispatch。精简后,代码更加清晰。 compose.js 实现中间件的串联,依次增强dispatch流程。使用函数式编程技巧,代码简洁高效。 bindActionCreators.js 将单个或多个ActionCreator转化为dispatch(action)的函数集合,简化Action的使用方式。 combineReducers.js 将多个reducer整合为一个,调整state结构,便于管理和操作。 整体而言,Redux的源码解析展示了其如何通过一系列核心组件实现状态管理的流程,从创建store到管理state、执行reducer、中间件串联,直至整合多个reducer,提供了一套高效、模块化的状态管理方案。理解这些组件及其功能是掌握Redux并能灵活应用的关键。Lodash 源码解读(二)
继续深入解析 Lodash 源码中的 arrayLikeKeys 函数。该函数的目的是判断一个值是否为数组或类似数组结构,同时收集其可枚举的索引。函数通过一系列逻辑判断,包括或且非运算,来确定是否需要存储索引并初始化相应的数组。值得注意的是,虽然for...in通常用于遍历对象,但当值为稀疏数组时,它并不遍历所有索引,这就需要通过while循环独立处理索引。
函数还需处理Safari 9的严格模式下 arguments.length 的特殊情况,以避免错误的索引收集。isArguments、isBuffer、isIndex、isTypedArray 等辅助函数的引入,使得理解 arrayLikeKeys 的全貌变得复杂,因为它们各自依赖于isObjectLike和getTag等底层函数。这些辅助函数的实现细节在本文中暂未详述,它们在后续章节会有更深入的探讨。
要完全掌握 Lodash 的源码,理解这些关键函数的交互至关重要。下一部分将更深入解析 isBuffer、isIndex、isTypedArray 等函数的实现,敬请期待。
剖析Linux内核源码解读之《实现fork研究(一)》
Linux内核源码解析:深入探讨fork函数的实现机制(一)
首先,我们关注的焦点是fork函数,它是Linux系统创建新进程的核心手段。本文将深入剖析从用户空间应用程序调用glibc库,直至内核层面的具体过程。这里假设硬件平台为ARM,使用Linux内核3..3和glibc库2.版本。这些版本的库和内核代码可以从ftp.gnu.org获取。
在glibc层面,针对不同CPU架构,进入内核的步骤有所不同。当glibc准备调用kernel时,它会将参数放入寄存器,通过软中断(SWI) 0x0指令进入保护模式,最终转至系统调用表。在arm平台上,系统调用表的结构如下:
系统调用表中的CALL(sys_clone)宏被展开后,会将sys_clone函数的地址放入pc寄存器,这个函数实际由SYSCALL_DEFINEx定义。在do_fork函数中,关键步骤包括了对父进程和子进程的跟踪,以及对子进程进行初始化,包括内存分配和vfork处理等。
总的来说,调用流程是这样的:应用程序通过软中断触发内核处理,通过系统调用表选择并执行sys_clone,然后调用do_fork函数进行具体的进程创建操作。do_fork后续会涉及到copy_process函数,这个函数是理解fork核心逻辑的重要入口,包含了丰富的内核知识。在后续的内容中,我将深入剖析copy_process函数的工作原理。
PHP7源码之array_unique函数分析
以下源码基于 PHP 7.3.8
array array_unique ( array array[,intarray[,intsort_flags = SORT_STRING ] ) (PHP 4 >= 4.0.1, PHP 5, PHP 7) array_unique — 移除数组中重复的值 参数说明: array:输入的数组。 sort_flag:(可选)排序类型标记,用于修改排序行为,主要有以下值: SORT_REGULAR - 按照通常方法比较(不修改类型) SORT_NUMERIC - 按照数字形式比较 SORT_STRING - 按照字符串形式比较 SORT_LOCALE_STRING - 根据当前的本地化设置,按照字符串比较。
array_unique 函数的源代码在 /ext/standard/array.c 文件中。由于篇幅过长,完整代码不在这里贴出来了,可以参见 GitHub 贴出的源代码。
定义变量
首先是定义变量,array_unique 函数默认使用 PHP_SORT_STRING 排序,PHP_SORT_STRING 在 /ext/standard/php_array.h 头文件中定义。
可以看到和开头PHP函数的sort_flag 参数默认的预定义常量 SORT_STRING 很像。
compare_func_t cmp 这行代码没看懂,不清楚是做什么的。compare_func_t 在 /Zend/zend_types.h 中定义:应该是定义了一个指向int 型返回值且带有两个指针常量参数的函数指针类型,没有查到相关资料,先搁着,继续往下看。
参数解析
ZEND_PARSE_PARAMETERS_START(1, 2),第一个参数表示必传参数个数,第二个参数表示最多参数个数,即该函数参数范围是 1-2 个。
数组元素个数判断
这段代码很容易看懂,当数组为空或只有 1 个元素时,无需去重操作,直接将array 拷贝到新数组 return_value来返回即可。
分配持久化内存
这一步只有当sort_type 为 PHP_SORT_STRING 时才执行。在下面可以看到调用 zend_hash_init 初始化了 array,调用 zend_hash_destroy 释放持久化的内存。
设置比较函数
进行具体比较顺序控制的函数指针是cmp,是通过向 php_get_data_compare_func 传入 sort_type 和 0 得到的,sort_type 也就是 SORT_STRING 这样的标记。
php_get_data_compare_func 在 array.c 文件中定义(即与 array_unique 函数同一文件),代码过长,这里只贴出默认标记为 SORT_STRING 的代码:
在前面的代码中,我们可以看到,cmp = php_get_data_compare_func(sort_type, 0); 的第二个参数,即参数 reverse 的值为 0,也就是当 sort_type 为 PHP_SORT_STRING 时,调用的是 php_array_data_compare_string 函数,即 SORT_STRING 采用 php_array_data_compare_string 进行比较。继续展开 php_array_data_compare_string 函数:
可以得到这样一条调用链:
string_compare_function 是一个 ZEND API,在 /Zend/zend_operators.c 中定义:
可以看到,SORT_STRING 使用 zend_binary_strcmp 函数进行字符串比较。下面的代码是 zend_binary_strcmp 的实现(也在 /Zend/zend_operators.c 中):
上面的代码是比较两个字符串。也就是SORT_STRING 排序方式的底层实现是 C 语言的 memcmp,即它对两个字符串从前往后,按照逐个字节比较,一旦字节有差异,就终止并比较出大小。
数组排序
这段代码初始化一个新的数组,然后将值拷贝到新数组,然后调用zend_sort 排序函数对数组进行排序。排序算法在 /Zend/zend_sort.c 中实现,注释有这样一句话:
Derived from LLVM's libc++ implementation of std::sort.
这个排序算法是基于LLVM 的 libc++ 中的 std::sort 实现的,算是快排的优化版,当元素数小于等于时有特殊的优化,当元素数小于等于 5 时直接通过 if else 嵌套判断排序。代码就不贴出来了。
数组去重
回到array_unique 上,继续看代码:
遍历排序好的数组,然后删除重复的元素。
众周所知,快排的时间复杂度是O(nlogn),因此,array_unique 函数的时间复杂度是O(nlogn)。array_unique 底层调用了快排算法,加大了函数运行的时间开销,当数据量很大时,会导致整个函数的运行较慢。
Vue3源码细读——ref
深入解析Vue3中ref的实现细节
在Vue3源码中,ref相关功能主要集中在'packages/reactivity/src/ref.ts'文件里。
在该文件中,ref的使用与处理主要依赖于最后一个函数的调用:`createRef(value, false)`。通过此函数,可以创建或更新ref实例。
接下来,让我们深入探讨`createRef`函数。它首先判断传入的参数是否已经是一个ref实例,如果是,则直接返回;否则,将返回一个`RefImpl`实例。进一步了解`RefImpl`构造函数,我们发现它包含了`isShallow`和`isReadonly`两个关键属性,它们负责判断ref实例的浅度和是否为只读。
通过阅读源码,我们了解到在控制台log中出现的`_value`和`_rawValue`函数。这些函数用于方便进行判断和对比,尤其是`_rawValue`记录了ref的原始值,以避免不必要的更新,比如在值未发生变化时,节省了性能损耗。实践一下,例如页面上的button点击修改值,然后使用watch监控ref,你会发现watch并未执行。
在源码中,还隐含了`trackRefValue`和`triggerRefValue`两个函数。它们分别在`ref.ts`文件内声明,分别负责跟踪和触发ref值的变化。
让我们继续深入到`trackRefValue`函数,它主要负责跟踪ref值的变化。`activeEffect`的概念在这里出现,它在ref的读取操作中扮演关键角色。当值发生变化时,Vue通过关联`activeEffect`实现响应式更新。具体来说,当我们进行第一次读取时,会将这种关联关系存储起来(通常使用Set数据结构)。改变值时,通过这些关联进行更新(响应式),达到响应式效果。
至此,关于Vue3中ref源码的解读暂时告一段落。如果有任何错误或需要进一步讨论的地方,欢迎大神们指出,同时,我也期待着自己的进步。