千層套路 - Vue 3.0 初始化源碼探秘

關注若川視野, 回復"pdf" 領取資料,回復"1",可加群長期交流學習

劉崇楨,微醫云服務團隊前端工程師,左手抱娃、右手持家的非典型碼農。

9 月初 Vue.js 3.0 正式發布,代號 "One Piece"。大秘寶都擺到眼巴前了,再不扒拉扒拉就說不過去了。那我們就從初始化開始。

目標:

  • 弄清楚 createApp(App).mount("#app") 到底做了什么

  • 弄清楚 Vue3.0 的初始化渲染是怎么樣的過程

能收獲到什么:

  • 了解 Vue3.0 的初始化過程

  • 介紹一個閱讀 Vue3.0 源碼的入口和方向

先跑起來

vue-next 代碼克隆到本地,打開 package.jsonscripts dev 末尾加上 --sourcemap

然后 ?yarn devvue 目錄下的 ?dist ?打包出了一份 ?vue.global.js 和相應的 sourcemap 文件。這樣方便我們一步一步調試代碼,查看程序在 call Stack 中的每一步調用。

查看 vue 官方給出的 demo,發現 vue 的使用分為 classiccomposition,我們先用 classic 方式,實現一個最簡單的 demo。

const app = {data () {return {counter: 1}}
}
Vue.createApp(app).mount("#app")

ok,頁面跑起來了。我們就在這段代碼打個斷點,然后一步一步的調試,觀察createApp(App).mount("#app")到底做了什么,了解Vue3.0的初始化過程。

在這之前,簡單了解一下整體的背景,我們這次主要涉及到 runtime 運行時的代碼。

runtime-dom

我們先跟著代碼進入:createApp(App).mount("#app");

這個 createApp() 來自 runtime-dom,我們通過這個圖可以看到他大致做的事情:return 了一個注冊了 mount 方法 app。這樣我們的 demo 至少能跑起來不報錯。

createApp 調用了 ensureRenderer 方法,他確保你能得到一個 renderer 渲染器。renderer 是通過調用創建渲染器的 createRenderer 來生成的,這個 createRenderer 來自于 runtime-core,后面我們會看到。

而這個 rendererOptions 是什么呢?

const?rendererOptions?=?extend({?patchProp,?forcePatchProp?},?nodeOps);export?const?nodeOps:?Omit<RendererOptions<Node,?Element>,?"patchProp">?=?{insert:?(child,?parent,?anchor)?=>?{parent.insertBefore(child,?anchor?||?null);},remove,createElement,createText,//?...
};

是不是就是一些 DOM API 的高階封裝,這個在 vue 的生態中,叫平臺特性。vue 源碼中的平臺特性就是針對 web 平臺的。如果開發者想要在別的平臺上運行 vue,比如 mpvue、weex,就不需要 fork 源碼庫改源碼了,直接把 nodeOps 中的方法按著平臺的特性逐一實現就可以了。這也是 createRenderer 等跨平臺的代碼放到 runtime-core 中的原因。

當然 runtime-dom 遠遠不只圖中這些東西,我們先大致過一下初始化過程,以對 vue3.0 有一個大致的了解。

runtime-core

緊接著,進入 runtime-core,創建渲染器

我們注意 baseCreateRenderer 這個 fn,2000 多行的代碼量,里面的東西都是渲染的核心代碼,從平臺特性 options 取出相關 API,實現了 patch、處理節點、處理組件、更新組件、安裝組件實例等等方法,最終返回了一個對象。這里我們看到了【2】中渲染器調用的 createApp 方法,他是通過 createAppAPI 創建的。代碼進入 createAppAPI

這里我們又看見了熟悉的 Vue2.x 中的 API,掛載在 app 上面。

至此,Vue.createApp(app).mount("#app"),創建 app 實例的流程,終于在【7】中 return app 告一段落,我們拿到了【2】中的 app 實例。

大致瞄一眼 app ,我們可以在 apiCreateApp.ts 中找到其實現

初次渲染 .mount("#app")

上面的介紹中,其實有兩處 .mount 的實現,一處是在 runtime-dom【2】中的 mount,我們叫他 dom-mount。一處是【7】中的 mount,我們叫他 core-mount

dom-mount的實現:

const?{?mount?}?=?app;?//?先暫存'core-mount'
app.mount?=?(containerOrSelector:?Element?|?string):?any?=>?{const?container?=?normalizeContainer(containerOrSelector);?//?#app?dom?節點if?(!container)?return;const?component?=?app._component;if?(!isFunction(component)?&&?!component.render?&&?!component.template)?{component.template?=?container.innerHTML;?//?平臺特性的邏輯}//?clear?content?before?mountingcontainer.innerHTML?=?"";const?proxy?=?mount(container);?//?執行'core-mount'container.removeAttribute("v-cloak");return?proxy;
};

dom-mount 并不是重寫 core-mount,而是提取了平臺特性的邏輯。比如上面如果 component 不是 function,又沒有 rendertemplate,就讀取 dom 節點內部的 html 作為渲染模板。

然后再執行 core-mountmount(container)

代碼很簡單,就兩步:

  • 創建根組件的 vnode

  • 渲染這個 vnode

創建根組件的vnode

創建 vnode,是一個初始化 vnode 的過程,這個階段中,下面的這些屬性被初始化為具體的值(還有很多屬性沒有羅列,都是初始值)。

vnode 描述不同的事物時,他的屬性值也各不相同,這些在 vnode 初始化階段確定的屬性在渲染組件時,能帶來非常重要的效率提升。

  • type,標識 VNode 的種類

  1. html 標簽的描述,type 屬性就是一個字符串,即標簽的名字

  2. 組件的描述,type 屬性就是引用組件類(或函數)本身

  3. 文本節點的描述,type 屬性就是 null

  • patchFlag,標識組件變化的地方

  • shapeFlagVNode 的標識,標明 VNode 屬于哪一類,demo 中的shapeFlag4STATEFUL_COMPONENT,有狀態的組件。

packages/shared/src/shapeFlags.ts中,定義了這些通過將十進制數字 1 左移不同的位數得來的枚舉值。

export?const?enum?ShapeFlags?{ELEMENT?=?1,?//?1?-?html/svg?標簽FUNCTIONAL_COMPONENT?=?1?<<?1,?//?2?-?函數式組件STATEFUL_COMPONENT?=?1?<<?2,?//?4?-?有狀態組件TEXT_CHILDREN?=?1?<<?3,?//?8ARRAY_CHILDREN?=?1?<<?4,?//?16SLOTS_CHILDREN?=?1?<<?5,?//?32TELEPORT?=?1?<<?6,?//?64SUSPENSE?=?1?<<?7,?//?128COMPONENT_SHOULD_KEEP_ALIVE?=?1?<<?8,?//?256?-?需要被?keepAlive?的有狀態組件COMPONENT_KEPT_ALIVE?=?1?<<?9,?//?512?-?已經被?keepAlive?的有狀態組件COMPONENT?=?ShapeFlags.STATEFUL_COMPONENT?|?ShapeFlags.FUNCTIONAL_COMPONENT?//?組件
}

為什么為 VNode 標識這些枚舉值呢?在 Vue2.xpatch 過程中,代碼通過 createElm 區分 VNode 是 html 還是組件或者 text 文本。

所以 Vue2.xpatch 是一個試錯過程,在這個階段是有很大的性能損耗的。Vue3.0 把對 VNode 的判斷放到了創建的時候,這樣在 patch 的時候就能避免消耗性能的判斷。

最終,我們看一下 vnode 的結構

export?interface?VNode<HostNode?=?RendererNode,HostElement?=?RendererElement,ExtraProps?=?{?[key:?string]:?any?}
>?{/***?@internal*/__v_isVNode:?true?//?一個始終為?true?的值,有了它,我們就可以判斷一個對象是否是?VNode?對象/***?@internal?內部屬性*/[ReactiveFlags.SKIP]:?truetype:?VNodeTypesprops:?(VNodeProps?&?ExtraProps)?|?nullkey:?string?|?number?|?nullref:?VNodeNormalizedRef?|?nullscopeId:?string?|?null?//?SFC?onlychildren:?VNodeNormalizedChildrencomponent:?ComponentInternalInstance?|?nulldirs:?DirectiveBinding[]?|?nulltransition:?TransitionHooks<HostElement>?|?null//?DOM?相關el:?HostNode?|?nullanchor:?HostNode?|?null?//?fragment?anchortarget:?HostElement?|?null?//?teleport?targettargetAnchor:?HostNode?|?null?//?teleport?target?anchorstaticCount:?number?//?number?of?elements?contained?in?a?static?vnode//?suspense?支持?suspense?的屬性suspense:?SuspenseBoundary?|?nullssContent:?VNode?|?nullssFallback:?VNode?|?null//?optimization?only?優化模式中使用的屬性shapeFlag:?numberpatchFlag:?numberdynamicProps:?string[]?|?nulldynamicChildren:?VNode[]?|?null//?application?root?node?onlyappContext:?AppContext?|?null
}

渲染這個vnode

ok,書接上回,我們拿到 根組件的 VNode,接下來執行到 render 函數。

render 的核心邏輯就是 patch 函數。

patch 函數

patch 有兩種含義: 1)整個虛擬 dom 映射到真實 dom 的過程;2)patch 函數。我們這里講的是函數。

patch 就是 render 渲染組件的關鍵邏輯,【5】中 baseCreateRenderer 2000 行左右的代碼,主要是為了 patch 服務的。

//?patching?&?not?same?type,?unmount?old?tree
if?(n1?&&?!isSameVNodeType(n1,?n2))?{anchor?=?getNextHostNode(n1)unmount(n1,?parentComponent,?parentSuspense,?true)n1?=?null
}
//?對于前后節點類型不同的,vue 是直接卸載之前的然后重新渲染新的,不會考慮可能的子節點復用。
...const?{?type,?ref,?shapeFlag?}?=?n2
switch?(type)?{?//?根據節點類型?type?分發到不同的?processcase?Text:processText(n1,?n2,?container,?anchor)breakcase?Comment:processCommentNode(n1,?n2,?container,?anchor)breakcase?Static:...case?Fragment:?...default:?//?根據不同的節點標識?shapeFlag?分發到不同的?processif?(shapeFlag?&?ShapeFlags.ELEMENT)?{?processElement(...)?}?else?if?(shapeFlag?&?ShapeFlags.COMPONENT)?{processComponent(...)...

patch 根據節點 VNode(4.1 創建的根組件的 vnode) 的 typeshapeFlags 執行不同的 process

  1. type:Text 文本

  2. type:Comment 注釋

  3. type:Static 靜態標簽

  4. type:Fragment 片段:VNode 的類型是 Fragment,就只需要把該 VNode 的子節點渲染到頁面。有了他,就沒有只能有一個根節點的限制,也可以做到組件平級遞歸

  5. shapeFlags:ShapeFlags.ELEMENT 原生節點,html/svg 標簽

  6. shapeFlags:ShapeFlags.COMPONENT 組件節點

  7. shapeFlags:ShapeFlags.TELEPORT 傳送節點,將組件渲染的內容傳送到制定的 dom 節點中

  8. shapeFlags:ShapeFlags.SUSPENSE 掛起節點(異步渲染)

Vue3 新增組件 - Fragment、Teleport、Suspense,可見此鏈接 (https://www.yuque.com/hugsun/vue3/component)

我們的 demo 中的根組件 VNodeshapeFlag4(0100)ShapeFlags.COMPONENT(0110),按位與后結果為非零,代碼會進入 processCompoent

processXXX

processXXX 是對掛載(mount)和更新(update)補丁的統一操作入口。

processXXX 會根據節點是否是初次渲染,進行不同的操作。

  • 如果沒有老的 VNode,就掛載組件(mount)。首次掛載,遞歸創建真實節點。

  • 如果有老的 VNode,就更新組件(update)。更新補丁的的渲染系統的介紹放到下下篇來介紹。

掛載

創建組件內部實例

內部實例也會暴露一些實例屬性給其他更高級的庫或工具使用。組件實例屬性很多很重要也能幫助理解,可以在 packages/runtime-core/src/component.ts 查看實例的接口聲明 ComponentInternalInstance。很壯觀啊,啪的一下 100 多行屬性的定義,主要包括基本屬性、響應式 state 相關、suspense 相關、生命周期鉤子等等

安裝組件實例
  1. 初始化 props 和 slots

  2. 安裝有狀態的組件,這里會初始化組件的響應式

【15】setupStatefulComponent,調用了 setup(props, setupContext)

如果沒有 setup 時會調用 applyOptions,應用 vue2.xoptions API,最終對 data() 的響應式處理也是使用 vue3.0reactive

上面講過,安裝組件實例觸發響應式初始化就發生在這里,具體怎么觸發的,這塊又是一個千層套路,放到下一篇中。

【16】主要是根據 template 拿到組件的 render 渲染函數和應用 vue2.xoptions API

我們看一下 template 模板編譯后生成的 render 函數。

我們大致看下生成的 render 函數,有幾點需要注意

  1. 這里的 render 函數執行后的返回是組件的 VNode

  2. _createVNode 函數,用于創建 VNode

  3. _createVNode函數的入參,typepatchFlagsdynamicProps

function?_createVNode(type:?VNodeTypes?|?ClassComponent?|?typeof?NULL_DYNAMIC_COMPONENT,?//?type,標識?VNode?的種類props:?(Data?&?VNodeProps)?|?null?=?null,children:?unknown?=?null,patchFlag:?number?=?0,?//?標記節點動態變化的地方dynamicProps:?string[]?|?null?=?null,?//?動態?propsisBlockNode?=?false
):?VNode?{?...?}

createVNode 在創建根節點的時候就出現過,用于創建虛擬 DOM。這個是內部使用的 API,面向用戶的 API 還是h函數。

export?function?h(type:?any,?propsOrChildren?:?any,?children?:?any):?VNode?{?...?}

h 的實現也是調用 createVNode,但是沒有 patchFlagdynamicPropsisBlockNode 這三個參數。也就是 h 是沒有 optimization 的,應該是因為這三個參數,讓用戶自己算容易出錯。

看來這個 patchFlags 有點意思,標識組件變化的地方,用于 patch 的 diff 算法優化

export?const?enum?PatchFlags?{TEXT?=?1,?//?動態文字內容CLASS?=?1?<<?1,?//?[2]動態?class?綁定STYLE?=?1?<<?2,?//?[4]動態樣式PROPS?=?1?<<?3,?//?[8]動態?props,不是?class?和?style?的動態?propsFULL_PROPS?=?1?<<?4,?//?[16]有動態的 key,也就是說 props 對象的 key 不是確定的。key 變化時,進行一次 full diffHYDRATE_EVENTS?=?1?<<?5,?//?[32]STABLE_FRAGMENT?=?1?<<?6,?//?[64]children?順序確定的?fragmentKEYED_FRAGMENT?=?1?<<?7,?//?[128]children?中有帶有?key?的節點的?fragmentUNKEYED_FRAGMENT?=?1?<<?8,?//?[256]沒有?key?的?children?的?fragmentNEED_PATCH?=?1?<<?9,?//?[512]DYNAMIC_SLOTS?=?1?<<?10,?//?[1024]動態的插槽//?SPECIAL?FLAGS?-------------------------------------------------------------//?以下是特殊的?flag,負值HOISTED?=?-1,?//?表示他是靜態節點,他的內容永遠不會改變BAIL?=?-2,?//?用來表示一個節點的?diff?應該結束
}

之所以使用位運算,是因為

  • | 來進行復合,TEXT | PROPS得到0000 1001,即十進制 9。標識他既有動態文字內容,也有動態 props。

  • & 進行 check,patchFlag & TEXT0000 1001 & 0000 0001,得到0000 0001,只要結果大于 0,就說明屬性命中。

  • 方便擴展、計算更快...

patchFlag 被賦值到 VNode 的屬性中,他在后面更新節點時會被用到。為了配合代碼的正常流轉,先放一放,代碼繼續 F10。如果你去調試代碼,會發現這真的是千層套路啊,一直 shift + F11 跳出代碼到懷疑人生,才終于回到 mountComponent...

總結一下 setupComponent 安裝組件實例,主要做了什么事情:initProps、initSlots、響應式初始化、得到模板的 render 函數等等。

回顧前文,跳出到【13】,setup 安裝組件實例后,下一步是 setupRenderEffect 激活渲染函數的副作用

激活渲染函數的副作用 setupRenderEffect

實現基于【21】,effect 副作用,意味著響應式數據變化后引起的變更。effect 源自 reactive,傳入一個 fn 得到一個 reactiveEffect

effect 的入參 componentEffect 是一個命名函數,會立即執行。componentEffect 執行過程中,觸發響應式數據的 getter 攔截,會在全局數據響應關系倉庫記錄當前componentEffect。在響應式對象發生改變時,派發更新,執行componentEffect

回到componentEffect

function?componentEffect()?{if?(!instance.isMounted)?{let?vnodeHook:?VNodeHook?|?null?|?undefinedconst?{?el,?props?}?=?initialVNodeconst?{?bm,?m,?parent?}?=?instance//?beforeMount?hook?生命周期鉤子函數if?(bm)?{invokeArrayFns(bm)}...//?subTree?根節點的?subTree,通過?renderComponentRoot?根據?render?生成的?vnode//大家回憶一下 render 是什么?是不是根組件的 template 編譯后得到的好多_createVNode 的渲染器函數?const?subTree?=?(instance.subTree?=?renderComponentRoot(instance))...//?更新patch(null,?subTree,?container,?...)...if?(m)?{?//?parent?的?mounted?執行之前,先執行?subTree?的?patchqueuePostRenderEffect(m,?parentSuspense)}...instance.isMounted?=?true?//?標志實例已掛載}?else?{?...?}
}

執行前面編譯后得到的渲染函數 render,生成subTree: vnode

最后執行 patch,上文中渲染根節點的 vnode 時執行過 patch,這里就進入了一個大循環,根據組件的 childrentypeshapeFlagbaseCreateRenderer 會繼續進行各種 processXXX 處理,直至基于 平臺特性DOM 操作 掛載到各自的父節點中。

這個順序是深度遍歷的過程,子節點的 patch 完成之后再進行父節點的 mounted

patch 循環 && subTree 一覽

//?subTree?的?模板?template
<div?id="app"><h1>composition-api</h1><p?@click="add"?:attr-key="counter">{{counter}}</p><p?:class="{'counter':?counter?%?2}">{{doubleCounter}}</p>
</div>//?patchFlag:?64?
// STABLE_FRAGMENT = 1 << 6, // 64 表示:children 順序確定的 fragment
//?shapeFlag:?16
//?ARRAY_CHILDREN?=?1?<<?4,?//?16?
  1. 觀察上面這個模板,Vue2.x 中的模板只能有一個根元素,Vue3.0 的這個 demo 中有三個根元素,這得益于新增的 fragment 組件。

  2. vnode 標識出來 patchFlag:64,表示 children 順序確定的 fragment

  3. vnode 標識出來 shapeFlag:16,表示當前節點是一個孩子數組。

  4. vnode 標識出來 dynamicChildren,標識動態變化的孩子節點。顯然是兩個 p 標簽,可以想象這個數組的元素也是當前呈現的 vnode,只不過具體屬性值不同罷了

等等,還有 4 嗎,我不知道...

當然還有,processxxx 中一般都會判斷是掛載還是更新,更新的時候就會用到 patchFlag,比如 patchElement... 下次一定

等等,還有 5 嗎,我不知道...

當然還有,第五層我就已經裂開了啊...

あ:あげない??????あ:不給你哦~?????????????
い:いらない,????い:不要了啦~?????????????
う:うごけない????う:動不了了~?????????????
え:えらべない????え:不會選嘛~?????????????
お:おせない??????お:按不到耶~?[裂開][裂開][裂開]

剛看源碼不久,只能靠 F11 、參考其他文檔,憑自己的理解寫出這樣的文章,肯定有很多理解不對的地方,希望得到批判指正。

附錄

  • Vue3初始化.drawio (https://www.yuque.com/office/yuque/0/2020/drawio/441847/1605880555730-4e18923f-c087-4082-af06-ec51986ba658.drawio?from=https%3A%2F%2Fwww.yuque.com%2Fdocs%2Fshare%2F64bd5cdc-3086-4154-a447-04032d161830%3F%23)

推薦閱讀

我在阿里招前端,我該怎么幫你?(現在還可以加模擬面試群)
如何拿下阿里巴巴 P6 的前端 Offer
如何準備阿里P6/P7前端面試--項目經歷準備篇
大廠面試官常問的亮點,該如何做出?
如何從初級到專家(P4-P7)打破成長瓶頸和有效突破
若川知乎問答:2年前端經驗,做的項目沒什么技術含量,怎么辦?
若川知乎高贊:有哪些必看的 JS庫?

末尾

你好,我是若川,江湖人稱菜如若川,歷時一年只寫了一個學習源碼整體架構系列~(點擊藍字了解我)

  1. 關注若川視野,回復"pdf" 領取優質前端書籍pdf,回復"1",可加群長期交流學習

  2. 我的博客地址:https://lxchuan12.gitee.io?歡迎收藏

  3. 覺得文章不錯,可以點個在看呀^_^另外歡迎留言交流~

小提醒:若川視野公眾號面試、源碼等文章合集在菜單欄中間【源碼精選】按鈕,歡迎點擊閱讀,也可以星標我的公眾號,便于查找

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/276351.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/276351.shtml
英文地址,請注明出處:http://en.pswp.cn/news/276351.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

css網頁布局兼容性有哪些要點與訣竅

IE vs FFCSS 兼容要點&#xff1a;DOCTYPE 影響 CSS 處理FF: div 設置 margin-left, margin-right 為 auto 時已經居中, IE 不行FF: body 設置 text-align 時, div 需要設置 margin: auto(主要是 margin-left,margin-right) 方可居中FF: 設置 padding 后, div 會增加 height 和…

js 下拉底部加載|滑輪滾動到頁面底部ajax加載數據的實例

轉載鏈接&#xff1a;http://www.cnblogs.com/thinksley/archive/2013/05/12/3074237.html 滾動下拉到頁面底部加載數據是很多瀑布流網站的做法&#xff0c;那來看看配合jsonp是如何實現的吧&#xff0c;小菜總結記錄之用&#xff0c;高手勿噴。 當然本例子采用的是jquery庫&…

python并行for循環_Python并行執行for循環

簡介在介紹如何最簡單地利用 python 實現并行前&#xff0c;我們先來看一個簡單的代碼。words [apple, bananan, cake, dumpling]for word in words:print word上面的例子中&#xff0c;我們用一個 for 循環打印出 words 列表中的每個單詞。問題來了&#xff0c;這里我們打印完…

C語言之指針與數組總結

和指針相關的問題口訣1&#xff1a; 1. 地址變量得地址&#xff0c;得誰地址指向誰 和指針相關的問題要畫圖: 內容變量畫房子&#xff0c;指針畫箭頭 ---->口 ---------------------------------------------------- 和指針相關的兩個特殊運算符&#xff1a; 一、"&…

2020年大前端技術趨勢解讀

導Lead語如今的前端早已不再拘泥于滿足頁面展示&#xff0c;而是開始延展到通過全棧來閉環產品。這表明前端已經有能力透過業務深入產業&#xff0c;繼而影響商業結果。這種表象的改變背后是本質的轉變&#xff0c;從更為宏觀的角度來說&#xff0c;前端正在通過持續的技術革新…

HTML默認樣式表CSS屬性

轉載鏈接&#xff1a;http://www.xiao-a.com/index.php/archives/440.html 開始的時候 *{margin:0;padding:0;}&#xff0c;當需要使用邊距的時候&#xff0c;就需要還原HTML默認CSS值了。以前一直在找這份 文檔&#xff0c;今天偶然在w3上看到了。除了inline和block的定義&…

第六集 MSF構思階段項目團隊的組建

第六集 MSF構思階段項目團隊的組建__Note轉載于:https://www.cnblogs.com/zencorn/archive/2009/10/18/1585495.html

lc濾波器是利用電感的感抗_你對LC諧振電路你都了解嗎

根據在電路中電感器L和電容C的連接方式不同&#xff0c;可以有兩種LC諧振電路&#xff0c;LC并聯諧振電路和LC串聯諧振電路。LC并聯、串聯諧振電路在應用中的變化較多&#xff0c;是電路中分析的一個難點&#xff0c;只有掌握LC并聯、串聯電路的阻抗特性等基本概念&#xff0c;…

給小程序再減重 30% 的秘密?(京喜小程序首頁瘦身實踐)

前言—在 web 開發場景&#xff0c;減少代碼體積雖然是性能優化的一個方向&#xff0c;還沒到錙銖必較的程度。但是在小程序場景&#xff0c;由于代碼包上傳階段限制了主包 2M 和總包 16M&#xff08;近期微信官方正在內測將總包上限調整至 20M &#xff09;的尺寸&#xff0c;…

rfc mail content-type

轉載鏈接&#xff1a;http://www.w3.org/Protocols/rfc1341/0_TableOfContents.html RFC 郵件正文類型列表&#xff1a; Note: this is a hypertext versionof RFC1341 which has been obsoletedby RFC1521, of which no hypertextversion currently exists. Text …

Coolite Toolkit入門指南

Coolite Toolkit 簡介 Coolite Toolkit 是一個支持ASP.NET AJAX的Web控件Coolite Toolkit是基于跨瀏覽器的ExtJS 庫開發而來的&#xff0c;并且簡化了開發步驟&#xff0c;包含有豐富的Ajax運用Coolite Toolkit和ExtJS 都是開源的可能通過SVN直接獲取Coolite 的代碼簡單的說,就…

本周ASP.NET英文技術文章推薦[10/21 – 10/27]

這一篇是《本周ASP.NET英文技術文章推薦》系列的第一篇&#xff0c;在這個系列中&#xff0c;我將介紹5-10篇比較有價值的、本周發布的、與ASP.NET相關的英文技術文章&#xff0c;幫助各位朋友從良莠不齊的大量文章中挑出一些我認為非常有價值閱讀的&#xff0c;在進行一段簡要…

vim 離線安裝_VIM學習筆記 插件列表(Plugin)

由于zhihu的垃圾編輯器不支持表格&#xff0c;請查看以下完整格式&#xff1a;http://yyq123.github.io/learn-vim/learn-vim-plugin.html說明&#xff1a;本列表完全基于作者的主觀體驗&#xff0c;既不客觀也不完整&#xff1b;建議使用vim-plug或Vundle等插件管理器&#xf…

3 年前端面經和他在創業公司的成長歷程

在掘金上當了幾年的伸手黨&#xff0c;最近也準備輸出一些自己的東西。關于我首先介紹一下我自己&#xff0c;17 年畢業于一所 211 學校&#xff0c;但是由于大學四年馳騁在召喚師峽谷&#xff0c;畢業時也沒有找到一份大廠的工作&#xff0c;隨便找了一家創業公司簽了三方就去…

Spring.NET學習筆記9——打造簡易的依賴注入框架(練習篇) Level 100

我們在第三篇中學習里一個簡易的IoC框架。今天我們接著上次的程序&#xff0c;實現帶參數構造函數對象的實例和屬性的注入 。  我們知道可以通過反射獲取類的構造函數及參數(GetConstructors方法)&#xff1b;可以獲取屬性和屬性的類型(GetProperties方法)。通過Activator的C…

PHP 利用Mail_MimeDecode類提取郵件正文

參考鏈接&#xff1a;http://blog.csdn.net/dmtnewtons_blog/article/details/18765289 rfc mail content-type&#xff1a; 參考鏈接&#xff1a;http://blog.csdn.net/dmtnewtons_blog/article/details/19327105 根據主流的郵件解析類Mail_MimeDecode&#xff0c;提取郵件正…

android 單元測試

首先AndroidManifest.xml View Code <?xml version"1.0" encoding"utf-8"?> <manifest xmlns:android"http://schemas.android.com/apk/res/android"package"com.travelsky.test" android:versionCode"1"androi…

申萬一級行業日指數_基金收評 | 指數震蕩走弱,軍工股成兩市主線!后期行情如何?...

收評君復盤日記(2020年9月21日)三大指數集體收跌&#xff0c;北向資金全天大幅凈流出近65億元&#xff0c;軍工板塊表現強勢。盤面回顧9月21日&#xff0c;兩市全天高開低走&#xff0c;早盤指數弱勢震蕩&#xff0c;三大指數盤中一度翻紅&#xff0c;但隨后震蕩走弱&#xff0…

若川的2016年度總結,畢業工作

可以點擊上方的標簽若川的故事、年度總結&#xff0c;查看往期文章有讀者反饋說看我年度總結系列比我源碼系列更有啟發。所以打算把2016-2018的年度總結發布到公眾號聲明原創&#xff0c;希望對大家有所啟發。&#xff08;雖然我的每一年都過得非常普通...&#xff09;以下是正…

jQuery之Ajax

轉載鏈接&#xff1a;http://cargoj.iteye.com/blog/1008047 1 . jQuery幫助之Ajax請求&#xff08;一&#xff09;jQuery.ajax(options) 2 . jQuery幫助之Ajax請求&#xff08;二&#xff09;jQuery.get(url,[data],[callback] 3 . jQuery幫助之Ajax請求&#xff08;三&am…