1.精读ãwebreflowã
2.精读《Monorepo 的源码精读优势》
3.Vue2源码解析?2?初始化
4.如何精读或泛读别人编写的程序源代码?
5.v51.04 鸿蒙内核源码分析(ELF格式) | 应用程序入口并非main | 百篇博客分析OpenHarmony源码
精读ãwebreflowã
ç½é¡µéæï¼åæµï¼æ¯é»ç¢æµç æ§çéè¦åå ä¹ä¸ï¼ç»åWhatforceslayout/reflowè¿ç¯æç« ä¸å¼ç¨ï¼æ´çä¸ä¸åæµçèµ·å ä¸ä¼åæèãåç¨è¿å¼ ç»å ¸å¾ï¼
ç½é¡µæ¸²æä¼ç»åDOM->CSSOM->Layout(éæorreflow)->Paint(éç»)->Composite(åæ)ï¼å ¶ä¸Compositeå¨ç²¾è¯»ãæ·±å ¥äºè§£ç°ä»£æµè§å¨åã详ç»ä»ç»è¿ï¼æ¯å¨GPUè¿è¡å æ åã
é£ä¹æé¤JSãDOMãCSSOMãCompositeå¯è½å¯¼è´çæ§è½é®é¢å¤ï¼å©ä¸çå°±æ¯æ们è¿æ¬¡å ³æ³¨çéç¹ï¼reflowäºãä»é¡ºåºä¸å¯ä»¥çåºæ¥ï¼éæåä¸å®éç»ï¼èéç»ä¸ä¸å®è§¦åéæã
æ¦è¿°ä»ä¹æ¶åä¼è§¦åLayout(reflow)å¢ï¼ä¸è¬æ¥è¯´ï¼å½å ç´ ä½ç½®åçååæ¶å°±ä¼ãä½ä¹ä¸å°½ç¶ï¼å 为æµè§å¨ä¼èªå¨å并æ´æ¹ï¼å¨è¾¾å°æ个æ°éææ¶é´åï¼ä¼å并为ä¸æ¬¡reflowï¼èreflowæ¯æ¸²æ页é¢çéè¦ä¸æ¥ï¼æå¼æµè§å¨å°±ä¸å®ä¼è³å°reflowä¸æ¬¡ï¼æ以æ们ä¸å¯è½é¿å reflowã
é£ä¸ºä»ä¹è¦æ³¨æreflow导è´çæ§è½é®é¢å¢ï¼è¿æ¯å 为æäºä»£ç å¯è½å¯¼è´æµè§å¨ä¼å失æï¼å³ææè½å并reflowæ¶æ²¡æå并ï¼è¿ä¸è¬åºç°å¨æ们ç¨jsAPI访é®æ个å ç´ å°ºå¯¸æ¶ï¼ä¸ºäºä¿è¯æ¿å°çæ¯ç²¾ç¡®å¼ï¼ä¸å¾ä¸æå触åä¸æ¬¡reflowï¼å³ä¾¿åå¨for循ç¯éã
å½ç¶ä¹ä¸æ¯æ¯æ¬¡è®¿é®å ç´ ä½ç½®é½ä¼è§¦åreflowï¼å¨æµè§å¨è§¦åreflowåï¼ææå·²æå ç´ ä½ç½®é½ä¼è®°å½å¿«ç §ï¼åªè¦ä¸å触åä½ç½®çååï¼ç¬¬äºæ¬¡å¼å§è®¿é®ä½ç½®å°±ä¸ä¼è§¦åreflowï¼å ³äºè¿ä¸ç¹ä¼å¨åé¢è¯¦ç»å±å¼ãç°å¨è¦è§£éçæ¯ï¼è¿ä¸ªâ触åä½ç½®çååâï¼å°åºæåªäºï¼
æ ¹æ®Whatforceslayout/reflowææ¡£çæ»ç»ï¼ä¸å ±æè¿ä¹å ç±»ï¼
è·å¾çå模åä¿¡æ¯elem.offsetLeft,elem.offsetTop,elem.offsetWidth,elem.offsetHeight,elem.offsetParent
elem.clientLeft,elem.clientTop,elem.clientWidth,elem.clientHeight
elem.getClientRects(),elem.getBoundingClientRect()
è·åå ç´ ä½ç½®ã宽é«çä¸äºæ段é½ä¼å¯¼è´reflowï¼ä¸åå¨ç»è¿ä¸è¯´ï¼å 为åªè¦è·åè¿äºä¿¡æ¯ï¼é½å¿ é¡»reflowæè½ç»åºåç¡®çå¼ã
æ»å¨elem.scrollBy(),elem.scrollTo()
elem.scrollIntoView(),elem.scrollIntoViewIfNeeded()
elem.scrollWidth,elem.scrollHeight
elem.scrollLeft,elem.scrollTop访é®åèµå¼
对scrollLeftèµå¼çä»·äºè§¦åscrollToï¼ææ导è´æ»å¨äº§ççè¡ä¸ºé½ä¼è§¦åreflowï¼ç¬è æ¥äºä¸äºèµæï¼ç®å主è¦æ¨æµæ¯æ»å¨æ¡åºç°ä¼å¯¼è´å¯è§åºååçªï¼æ以éè¦reflowã
focus()elem.focus()(æºç )
å¯ä»¥æ ¹æ®æºç çä¸ä¸æ³¨éï¼ä¸»è¦æ¯è¿ä¸æ®µï¼
//Ensurewehavecleanstyle(includingforceddisplaylocks).GetDocument().UpdateStyleAndLayoutTreeForNode(this)å³å¨èç¦å ç´ æ¶ï¼è½ç¶æ²¡ææ¿å ç´ ä½ç½®ä¿¡æ¯çè¯æ±ï¼ä½æä¸å®è¦è¢«èç¦çå ç´ è¢«éèæè 移é¤äºï¼æ¤æ¶å¿ é¡»è°ç¨UpdateStyleAndLayoutTreeForNodeéæéç»å½æ°ï¼ç¡®ä¿å ç´ ç¶ææ´æ°åæè½ç»§ç»æä½ã
è¿æä¸äºå ¶ä»elementAPIï¼
elem.computedRole,elem.computedName
elem.innerText(æºç )
innerTextä¹éè¦éæåæè½æ¿å°æ£ç¡®å 容ã
è·åwindowä¿¡æ¯window.scrollX,window.scrollY
window.innerHeight,window.innerWidth
window.visualViewport.height/width/offsetTop/offsetLeft(æºç )
åå ç´ çº§å«ä¸æ ·ï¼ä¸ºäºæ¿å°æ£ç¡®å®½é«åä½ç½®ä¿¡æ¯ï¼å¿ é¡»éæã
documentç¸å ³document.scrollingElementä» éç»
document.elementFromPoint
elementFromPointå 为è¦æ¿å°ç²¾ç¡®ä½ç½®çå ç´ ï¼å¿ é¡»éæã
Formç¸å ³inputElem.focus()
inputElem.select(),textareaElem.select()
focusãselect触åéæçåå åelem.focus类似ã
é¼ æ äºä»¶ç¸å ³mouseEvt.layerX,mouseEvt.layerY,mouseEvt.offsetX,mouseEvt.offsetY(æºç )
é¼ æ ç¸å ³ä½ç½®è®¡ç®ï¼å¿ é¡»ä¾èµä¸ä¸ªæ£ç¡®çæå¸ï¼æä»¥å¿ é¡»è§¦åreflowã
getComputedStylegetComputedStyleé常ä¼å¯¼è´éæåéç»ï¼æ¯å¦è§¦åéæåå³äºæ¯å¦è®¿é®äºä½ç½®ç¸å ³çkeyçå ç´ ã
Rangeç¸å ³range.getClientRects(),range.getBoundingClientRect()
è·åéä¸åºåç大å°ï¼å¿ é¡»reflowæè½ä¿é精确æ§ã
SVG大éSVGæ¹æ³ä¼å¼åéæï¼å°±ä¸ä¸ä¸æ举äºï¼æ»ä¹ä½¿ç¨SVGæä½æ¶ä¹è¦åæä½domä¸æ ·è°¨æ ã
contenteditable被设置为contenteditableçå ç´ å ï¼å æ¬å°å¾åå¤å¶å°åªè´´æ¿å¨å ï¼å¤§éæä½é½ä¼å¯¼è´éæã(æºç )
精读Whatforceslayout/reflowä¸é¢å¼ç¨äºå ç¯å ³äºreflowçç¸å ³æç« ï¼ç¬è æå 个éè¦çæ»ç»ä¸ä¸ã
repaint-reflow-restylerepaint-reflow-restyleæå°ç°ä»£æµè§å¨ä¼å°å¤æ¬¡domæä½å并ï¼ä½åIEçå ¶ä»å æ ¸æµè§å¨å°±ä¸ä¿è¯æè¿æ ·çå®ç°äºï¼å æ¤ç»åºäºä¸ä¸ªå®å ¨åæ³ï¼
//badvarleft=,top=;el.style.left=left+"px";el.style.top=top+"px";//betterel.className+="theclassname";//orwhentopandleftarecalculateddynamically...//betterel.style.cssText+=";left:"+left+"px;top:"+top+"px;";æ¯å¦ç¨ä¸æ¬¡classNameçä¿®æ¹ï¼æä¸æ¬¡cssTextçä¿®æ¹ä¿è¯æµè§å¨ä¸å®è§¦åä¸æ¬¡éæãä½è¿æ ·å¯ç»´æ¤æ§ä¼éä½å¾å¤ï¼ä¸å¤ªæ¨èã
avoidlargecomplexlayoutsavoidlargecomplexlayoutséç¹å¼ºè°äºè¯»åå离ï¼é¦å çä¸é¢çbadcaseï¼
functionresizeAllParagraphsToMatchBlockWidth(){ //Putsthebrowserintoaread-write-read-writecycle.for(vari=0;i<paragraphs.length;i++){ paragraphs[i].style.width=box.offsetWidth+'px';}}å¨for循ç¯ä¸ä¸æ访é®å ç´ å®½åº¦ï¼å¹¶ä¿®æ¹å ¶å®½åº¦ï¼ä¼å¯¼è´æµè§å¨æ§è¡N次reflowã
è½ç¶å½JavaScriptè¿è¡æ¶ï¼åä¸å¸§ä¸çæææ§å¸å±å¼é½æ¯å·²ç¥çï¼ä½å½ä½ 对å¸å±åäºä¿®æ¹åï¼åä¸å¸§ææå¸å±å¼ç¼åé½ä¼ä½åºï¼å æ¤å½ä¸æ¬¡è·åå¼æ¶ï¼ä¸å¾ä¸éæ°è§¦åä¸æ¬¡reflowã
è读åå离çè¯ï¼å°±ä»£è¡¨äºéä¸è¯»ï¼è½ç¶è¯»ç次æ°è¿æ¯é£ä¹å¤ï¼ä½ä»ç¬¬äºæ¬¡å¼å§å°±å¯ä»¥ä»å¸å±ç¼åä¸æ¿æ°æ®ï¼ä¸ç¨è§¦åreflowäºã
å¦å¤è¿æå°flexå¸å±æ¯ä¼ ç»floatéæé度快å¾å¤ï¼3msvsmsï¼ï¼æ以è½ç¨flexåçå¸å±å°±å°½éä¸è¦ç¨floatåã
reallyfixinglayoutthrashingreallyfixinglayoutthrashingæå°äºç¨fastdomå®è·µè¯»åå离ï¼
ids.forEach(id=>{ fastdom.measure(()=>{ consttop=elements[id].offsetTopfastdom.mutate(()=>{ elements[id].setLeft(top)})})})fastdomæ¯ä¸ä¸ªå¯ä»¥å¨ä¸å离代ç çæ åµä¸ï¼å离读åæ§è¡çåºï¼å°¤å ¶éåç¨å¨reflowæ§è½ä¼ååºæ¯ãæ¯ä¸ä¸ªmeasureãmutateé½ä¼æ¨å ¥æ§è¡éåï¼å¹¶å¨window.requestAnimationFrameæ¶æºæ§è¡ã
æ»ç»åæµæ æ³é¿å ï¼ä½éè¦æ§å¶å¨æ£å¸¸é¢çèå´å ã
æ们éè¦å¦ä¹ 访é®åªäºå±æ§ææ¹æ³ä¼å¯¼è´åæµï¼è½ä¸ä½¿ç¨å°±ä¸è¦ç¨ï¼å°½éåå°è¯»åå离ãå¨å®ä¹è¦é¢ç¹è§¦ååæµçå ç´ æ¶ï¼å°½éä½¿å ¶è±ç¦»ææ¡£æµï¼åå°åæµäº§ççå½±åã
讨论å°åæ¯ï¼ç²¾è¯»ãwebreflowã·Issue#·dt-fe/weekly
å¦æä½ æ³åä¸è®¨è®ºï¼è¯·ç¹å»è¿éï¼æ¯å¨é½ææ°ç主é¢ï¼å¨æ«æå¨ä¸åå¸ãå端精读-å¸®ä½ çéé è°±çå 容ã
çæ声æï¼èªç±è½¬è½½-éåç¨-éè¡ç-ä¿æç½²åï¼åæå ±äº«3.0许å¯è¯ï¼
åæï¼/post/精读《Monorepo 的优势》
本周深入研究的文章是《Monorepo 的诸多益处》。本文将通过一个实际案例,源码精读探讨Monorepo在开发中的源码精读重要性和解决的问题。尽管直接介绍工具或迁移方法的源码精读文章不少,这篇文章凭借其讲述的源码精读Android与iOS开发过程中对Monorepo的引入,提供了更具普适性的源码精读好看的html源码下载视角。
作者选择这篇文章是源码精读因为它通过PDF服务PSPDFKit的演化历程,说明了从单平台到多平台时,源码精读如何从两个独立仓库(PSPDFKit-Android和Core)合并,源码精读最终实现代码共享和一致性。源码精读在初始阶段,源码精读Android和iOS的源码精读代码因平台差异而分开,但这样导致了版本管理和冲突处理的源码精读复杂性。随着项目发展,源码精读团队意识到使用Monorepo能更好地管理代码库,源码精读提高协作效率。
Monorepo的优势在于,当代码库中存在关联性时,源码整合提供了更高效的调试手段。它有助于工程化的统一,让开发人员更专注于业务逻辑,而非版本管理。然而,它也存在挑战,如仓库大小、CI测试时间增长以及空间浪费等问题。理想的Monorepo设计需要清晰的结构,统一的配置,以及便捷的推荐系统实战源码模块引用和依赖管理。
尽管Lerna作为Monorepo的常见工具,可能在通用性上牺牲了部分灵活性,但针对特定环境,比如基于Webpack和TypeScript的项目,可以优化配置,进一步简化流程。别名映射和symlink的选择也需要根据项目需求来权衡。
对于是否需要Monorepo,以及是否有特定需求,读者可以参考文章末尾的讨论链接,每周的主题交流都在进行中。前端精读致力于提供高质量的内容,欢迎大家参与讨论,共同学习和分享。
请注意,所有内容遵循Creative Commons Attribution-NonCommercial-ShareAlike 3.0许可证,欢迎自由转载,但需保持署名。关注前端精读微信公众号,获取更多技术资讯和深度阅读推荐。
Vue2源码解析?2?初始化
活着,最有意义的事情,就是不遗余力地提升自己的认知,拓展自己的认知边界。在搭建源码调试环境一节中,我们已经找到了Vue的构造函数,接下来开始探索Vue初始化的流程。
一个小测试在精读源码之前,android租房源码我们可以在一些重要的方法内打印一下日志,熟悉一下这些关键节点的执行顺序。(执行npmrundev后,源码变更后会自动生成新的Vue.js,我们的测试html只需要刷新即可)
在初始化之前,Vue类的构建过程?在此过程中,大部分都是原型方法和属性,意味着实例vm可以直接调用
注意事项:
1、以$为前缀的属性和方法,在调用_init原型方法的那一刻即可使用
2、以_为前缀的原型方法和属性,谨慎使用
3、本章旨在了解Vue为我们提供了哪些工具(用到时,深入研究,不必要在开始时花过多精力,后边遇到时会详细说明)
4、类方法和属性在newVue()前后都可以使用,原型方法和属性只能在newVue()后使用
定义构造函数//src/core/instance/index.jsfunctionVue(options){ //形式上很简单,就是一个_init方法this._init(options)}挂载原型方法:_init//src/core/instance/init.jsVue.prototype._init=function(options?:Object){ }挂载与state相关的原型属性和原型方法//src/core/instance/state.jsconstdataDef={ }dataDef.get=function(){ returnthis._data}constpropsDef={ }propsDef.get=function(){ returnthis._props}Object.defineProperty(Vue.prototype,'$data',dataDef)Object.defineProperty(Vue.prototype,'$props',propsDef)Vue.prototype.$set=setVue.prototype.$delete=delVue.prototype.$watch=function(expOrFn:string|Function,cb:any,options?:Object):Function{ //略}挂载与事件相关的原型方法//src/core/instance/events.jsconsthookRE=/^hook:/Vue.prototype.$on=function(event:string|Array<string>,fn:Function):Component{ }Vue.prototype.$once=function(event:string,fn:Function):Component{ }Vue.prototype.$off=function(event?:string|Array<string>,fn?:Function):Component{ }Vue.prototype.$emit=function(event:string):Component{ }挂载与生命周期相关的原型方法//src/core/instance/lifecycle.jsVue.prototype._update=function(vnode:VNode,hydrating?:boolean){ }Vue.prototype.$forceUpdate=function(){ }Vue.prototype.$destroy=function(){ }挂载与渲染相关的原型方法//installruntimeconveniencehelpersinstallRenderHelpers(Vue.prototype)Vue.prototype.$nextTick=function(fn:Function){ }Vue.prototype._render=function():VNode{ }挂载Vue类方法和类属性//src/core/global-api/index.js//configconstconfigDef={ }configDef.get=()=>configObject.defineProperty(Vue,'config',configDef)Vue.util={ warn,extend,mergeOptions,defineReactive}Vue.set=setVue.delete=delVue.nextTick=nextTick//2.6explicitobservableAPIVue.observable=<T>(obj:T):T=>{ observe(obj)returnobj}Vue.options=Object.create(null)ASSET_TYPES.forEach(type=>{ Vue.options[type+'s']=Object.create(null)})Vue.options._base=Vueextend(Vue.options.components,builtInComponents)initUse(Vue)//挂载类方法use,用于安装插件(特别特别重要)initMixin(Vue)//挂载类方法mixin,用于全局混入(在Vue3中被新特性取代)initExtend(Vue)//实现Vue.extend函数initAssetRegisters(Vue)//实现Vue.component,Vue.directive,Vue.filter函数挂载平台相关的属性,挂载原型方法$mount//src/platforms/web/runtime/index.js//installplatformspecificutilsVue.config.mustUseProp=mustUsePropVue.config.isReservedTag=isReservedTagVue.config.isReservedAttr=isReservedAttrVue.config.getTagNamespace=getTagNamespaceVue.config.isUnknownElement=isUnknownElement//installplatformruntimedirectives&componentsextend(Vue.options.directives,platformDirectives)extend(Vue.options.components,platformComponents)//installplatformpatchfunctionVue.prototype.__patch__=inBrowser?patch:noopconsole.log('挂载$mount方法')//publicmountmethodVue.prototype.$mount=function(el?:string|Element,hydrating?:boolean):Component{ }拓展$mount方法//src/platforms/web/entry-runtime-with-compiler.jsconstmount=Vue.prototype.$mount//保存之前定义的$mount方法Vue.prototype.$mount=function(el?:string|Element,hydrating?:boolean):Component{ //执行拓展内容returnmount.call(this,el,hydrating)//执行最初定义的$mount方法}Vue的初始化过程(很重要哦!!!)熟悉了初始化过程,就会对不同阶段挂载的实例属性了然于胸,了解Vue是如何处理options中的数据,将初始化流程抽象成一个模型,asp动力源码从此,当你看到用户编写的options选项,都可以在这个模型中演练。
前边我们提到过,Vue的构造函数中只调用了一个_init方法
执行_init方法//src/core/instance/init.jsVue.prototype._init=function(options?:Object){ constvm:Component=this//此刻,Vue的实例已经创建,只是雏形,但Vue的所有原型方法可以调用//aflagtoavoidthisbeingobserved//(observe会在后面的响应式章节详细说明)vm._isVue=true//mergeoptionsif(options&&options._isComponent){ //在后面的Vue组件章节会详细说明//optimizeinternalcomponentinstantiation//sincedynamicoptionsmergingisprettyslow,andnoneofthe//internalcomponentoptionsneedsspecialtreatment.initInternalComponent(vm,options)}else{ vm.$options=mergeOptions(//合并optionsresolveConstructorOptions(vm.constructor),//主要处理包含继承关系的实例()options||{ },vm)}//exposerealselfvm._self=vminitLifecycle(vm)//初始化实例中与生命周期相关的属性initEvents(vm)//处理父组件传递的事件和回调initRender(vm)//初始化与渲染相关的实例属性callHook(vm,'beforeCreate')//调用beforeCreate钩子,即执行beforeCreate中的代码(用户编写)initInjections(vm)//resolveinjectionsbeforedata/props获取注入数据initState(vm)//初始化props、methods、data、computed、watchinitProvide(vm)//resolveprovideafterdata/props提供数据注入callHook(vm,'created')//执行钩子created中的代码(用户编写)if(vm.$options.el){ //DOM容器(通常是指定id的div)vm.$mount(vm.$options.el)//将虚拟DOM转换成真实DOM,然后插入到DOM容器内}}initLifecycle:初始化与生命周期相关的实例属性//src/core/instance/init.jsVue.prototype._init=function(options?:Object){ }0initEvents(vm):处理父组件传递的事件和回调//src/core/instance/init.jsVue.prototype._init=function(options?:Object){ }1initRender(vm):初始化与渲染相关的实例属性//src/core/instance/init.jsVue.prototype._init=function(options?:Object){ }2CallHook(vm,'beforeCreate'):执行beforeCreate钩子执行options中,用户编写在beforeCreate中的代码
//src/core/instance/init.jsVue.prototype._init=function(options?:Object){ }3initInjections(vm):resolveinjectionsbeforedata/props获取注入数据//src/core/instance/init.jsVue.prototype._init=function(options?:Object){ }4initState(vm):初始化props、methods、data、computed、watch(划重点啦!!!)//src/core/instance/init.jsVue.prototype._init=function(options?:Object){ }5initProps:初始化props此处概念比较多,propsData、props、vm._props、propsOptions,后续会结合实例来分析其区别,此处只做大概了解。vb多级树源码
//src/core/instance/init.jsVue.prototype._init=function(options?:Object){ }6initMethods:初始化methods//src/core/instance/init.jsVue.prototype._init=function(options?:Object){ }7initData:初始化data//src/core/instance/init.jsVue.prototype._init=function(options?:Object){ }8initComputed:初始化computed选项//src/core/instance/init.jsVue.prototype._init=function(options?:Object){ }9initWatch:初始化watchcreateWatcher:本质上执行了vm.$watch(expOrFn,handler,options)
//src/core/instance/state.jsconstdataDef={ }dataDef.get=function(){ returnthis._data}constpropsDef={ }propsDef.get=function(){ returnthis._props}Object.defineProperty(Vue.prototype,'$data',dataDef)Object.defineProperty(Vue.prototype,'$props',propsDef)Vue.prototype.$set=setVue.prototype.$delete=delVue.prototype.$watch=function(expOrFn:string|Function,cb:any,options?:Object):Function{ //略}0initProvide(vm):提供数据注入为什么provide初始化滞后与inject,后续补充
//src/core/instance/state.jsconstdataDef={ }dataDef.get=function(){ returnthis._data}constpropsDef={ }propsDef.get=function(){ returnthis._props}Object.defineProperty(Vue.prototype,'$data',dataDef)Object.defineProperty(Vue.prototype,'$props',propsDef)Vue.prototype.$set=setVue.prototype.$delete=delVue.prototype.$watch=function(expOrFn:string|Function,cb:any,options?:Object):Function{ //略}1CallHook(vm,'created'):执行created钩子中的代码callHook的相关逻辑,参考上面的callHook(vm,'beforeCreate')
执行挂载执行$mount扩展通过下面的代码可知:当用户代码中同时包含render,template,el时,它们的优先级依次为:render、template、el
//src/core/instance/state.jsconstdataDef={ }dataDef.get=function(){ returnthis._data}constpropsDef={ }propsDef.get=function(){ returnthis._props}Object.defineProperty(Vue.prototype,'$data',dataDef)Object.defineProperty(Vue.prototype,'$props',propsDef)Vue.prototype.$set=setVue.prototype.$delete=delVue.prototype.$watch=function(expOrFn:string|Function,cb:any,options?:Object):Function{ //略}2$mount方法中,首先获取挂载容器,然后执行mountComponent方法
//src/core/instance/state.jsconstdataDef={ }dataDef.get=function(){ returnthis._data}constpropsDef={ }propsDef.get=function(){ returnthis._props}Object.defineProperty(Vue.prototype,'$data',dataDef)Object.defineProperty(Vue.prototype,'$props',propsDef)Vue.prototype.$set=setVue.prototype.$delete=delVue.prototype.$watch=function(expOrFn:string|Function,cb:any,options?:Object):Function{ //略}3//src/core/instance/state.jsconstdataDef={ }dataDef.get=function(){ returnthis._data}constpropsDef={ }propsDef.get=function(){ returnthis._props}Object.defineProperty(Vue.prototype,'$data',dataDef)Object.defineProperty(Vue.prototype,'$props',propsDef)Vue.prototype.$set=setVue.prototype.$delete=delVue.prototype.$watch=function(expOrFn:string|Function,cb:any,options?:Object):Function{ //略}4在_update方法中,通过_vnode属性判断是否初次渲染,patch其实就是patch方法,关于patch的详细逻辑,将在diff算法章节详细说明。
//src/core/instance/state.jsconstdataDef={ }dataDef.get=function(){ returnthis._data}constpropsDef={ }propsDef.get=function(){ returnthis._props}Object.defineProperty(Vue.prototype,'$data',dataDef)Object.defineProperty(Vue.prototype,'$props',propsDef)Vue.prototype.$set=setVue.prototype.$delete=delVue.prototype.$watch=function(expOrFn:string|Function,cb:any,options?:Object):Function{ //略}5原文:/post/如何精读或泛读别人编写的程序源代码?
读代码这事,先要分是精读还是泛读。从学习的目的来看,一定要精读一定量的经典代码。而精读是指每行都读懂,不看代码脑子里就能勾画出程序的基本结构。这里有个很形象的状态,精读代码时会满脑子都是代码,放不下,甚至睡觉前脑子里也是代码。但这一篇里主要不是关注如何精读代码的,而是关于如何在工作中掌握既有代码的,等价于泛读。现存的很多系统往往很大,几十万行的可能也只算普通。这时候一旦加入了这样一个项目,那么如何去读代码?下面说点个人体会。读这类代码前,先得把规格大致弄清楚,而不能上来就读,比如:对于应用型程序,你要先大致整清楚它的使用方法。如果其中有涉及到领域知识,比如:流程、财会等,那也最好预先有些认识。这类东西从代码里反推回来是不太可能的。我个人感觉这对读程序是个很大的障碍,你不知道编码规则,却去读编码的程序,总是会云里雾里,这时候反倒不是因为程序难,而是因为不知道程序中所包含的专业知识。在这一步里,最好能抽取出来几个典型的应用场景,这在后面有用。一旦开始接触代码,那要先弄清楚代码的基本静态结构。如:包构成、类构成等。这里涉及一个层次问题。一下子把层次探的太深,就容易盯在细节上出不来。有设计文档的项目,大致上可以通过包来界定这个层次。没设计文档的就可怕了,只能靠自己划分,最好不要超过个,超过了真记不住。在静态结构这步,要弄清楚每个部分的核心职责,可以简单,最好能记住。接下来就要用到上面的典型场景了。要在典型场景下考察上面的静态结构是如何发挥作用的。典型场景下用到的接口往往就是关键的接口,要整清楚,他们的定义和作用。也要整清楚,典型场景下数据流的变迁。这步骤算是弄清楚代码的时序。很像UML里的Sequence图。但牵涉到数据的时候,一般需要对数据的规格有所了解。接下来要关注进程、线程的结构。比如:都是什么时候开始、什么时候结束的,在上述典型场景下都负责干什么。上述四步(规格、静态结构、典型场景、进程线程)完成后,对程序的第一次泛读完成。检验标准很简单,这时应该能够单靠纸笔描述出程序典型场景的Sequence图。干这事儿的时候,要抑制自己的求知欲,因为总是很想在调试器里通过call stack把一个功能的实现细节整清楚,但至少在第一个层次里,可以先不要这样。第一次泛读后,就要进入深掘的过程,针对的对象应该是自己会负责的部分。这部分功能往往会隐藏在某个接口之下。这时候一般来讲可以放过功能型的模块,比如:XML解析的模块等。其他部分可以认为是需要把之前所说的四个步骤再重复一下。但这时候要关注细节和调用堆栈了。不管是在那个读代码的层次,有两个基本技巧总是需要的,一个是要掌握具体程序里内嵌的Log机制,要能看Log,必要时可能还得加Log;一个是基本调试方法。调试很难展开,《软件调试》一书写了多页。但只停留在设个断点等他停下来这个层次上还是会有点欠缺的。条件断点、多线程调试、多进程时的调试还是要知道一点的。程序类型太多,因此估计读程序的方法也很多。上面只是个人的一点经验,欢迎补充。
v. 鸿蒙内核源码分析(ELF格式) | 应用程序入口并非main | 百篇博客分析OpenHarmony源码
鸿蒙内核源码分析(ELF格式篇) | 应用程序入口并非main
深入解析ELF格式与鸿蒙源码的关系,探寻应用程序入口的奥秘。本文将带你从一段简单的C代码开始,跟踪其编译成ELF格式后的神秘结构,揭秘ELF的组成与内部运作机制。
以E:\harmony\docker\case_code_目录下的main.c文件为例,通过编译生成ELF文件,运行后使用readelf -h命令查看应用程序头部信息。了解ELF文件的全貌,从ELF头信息、段信息、段区映射关系、区表等多方面深入探讨。
ELF格式文件由四大部分组成:头信息、段信息、段区映射关系和区表。头信息包含关键元数据,如文件类型、字节顺序、文件大小等;段信息描述了可执行代码和数据段的属性和位置;段区映射关系展示了段与区的关联;区表则存储了每个区的详细信息。
通过readelf -l命令,可以观察到段信息及其在程序中的作用,如初始化数组、动态链接、栈区等。在运行时,不同段以特定方式映射到内存中,实现代码的加载和执行。
在深入分析后,发现应用程序的真正入口并非通常理解的main函数,而是一个名为_start的特殊函数。这揭示了鸿蒙内核在启动时的执行流程,以及如何在ELF格式中组织和加载代码。
本文以ELF格式为切入点,带你全面理解鸿蒙内核源码的组织结构与运行机制。通过百万汉字注解,带你精读内核源码,深入挖掘其地基。在Gitee仓(gitee.com/weharmony/ker...)同步注解,共同探索鸿蒙研究站(weharmonyos)的奥秘。