本文已參與好文召集令活動,點擊查看:後端、大前端雙賽道投稿,2萬元獎池等你挑戰!css
本文主要講述的是某種心路歷程: 一枚小白成長到大廠須要的高級前端開發工程師。html
技術文,乾貨多,放心觀看呢。前端
「來,喝!誰也別是個慫瓜蛋哈!」vue
說完,感情深,一口一口悶。node
喂喂,這人均低消¥1600
,你這麼喝下去怕不是要把我喝垮咯。react
咱們leader層以上的人都被喊進了小黑屋開會,主題只有一個:要裁人。 這場看起來很是熱鬧的聚會,我自掏腰包攛的。你說,一家上市公司,要我給裁人名單,我該怎麼作。webpack
「老大,來,喝一個。我在老大這學到了不少,技術是真牛哈!」web
碰杯,幹了一個。面試
「我一直覺得 Redis
是那些寫 Java
的人要搞的事情,沒想到老大帶着咱們把Redis
給搞出來了。這下之後都不用看那些服務端的臉色了! 老大,走一個!」算法
繼續碰杯,走一個。 目前應該就屬我喝了最多的錢下肚。
「老大,我是負責搞調優的。我之前只知道作作http緩存
,搞搞懶加載
什麼的。沒想到在老大這裏學到了高了好幾個維度的調優方法。來,老大陪我幹一個。」
喂喂,不會已經上頭了吧?不該該是你陪老大幹一個嘛。 好吧,繼續碰杯,再走一個。
「喂喂,你們聽我講一下,我給你們講一個老大的糗事如何?」
團隊裏爲數很少的妹紙,果真無論哪一行的女生都喜歡分享八卦這種東西。有人唱戲,天然有人捧場。一波波起鬨下,一個小故事出來了。
「我剛來公司不久的時候,老大老是把我提交的代碼給打回來,甚至有次,我提交了6次代碼,每次都被打回來了。而後我就哭了,一半是氣的,一半是怕的。我記得我當時還特地問了問大家,老大是否是特別嚴格,會把大家提交的代碼打回來。而後大家告訴我,大家都沒有被老大打回過。」
我開始捂住本身的臉,這種事情都能拿出來講,也不光彩吧?
「而後吧,這都不是重點。重點是,那天晚上老大竟然請我吃飯了。白天打回個人代碼有多狠,晚上請吃飯就有多卑微。老大邊吃飯邊給我道歉,說個人代碼寫得不符合標準,頁面render
的次數多了。例如:」
function Com (){
const [price,setPrice] = useState(0); // 初始化0 第1次
useEffect(()=>{
// fetch data
setPrice(10.8); // 拿到真實價格 第2次
},[price])
return <div>¥{price}</div>
}
// 應該改爲一下風格
Api.fetch().then(
(props)=>{
render(React.memo(Com),props)
}
)
function Com (props){
const [price,setPrice] = useState(props.price); // 初始化0 第1次
useEffect(()=>{
// code
},[price])
return <div>¥{price}</div>
}
複製代碼
「大家你們看看,雖然我以爲有道理,但由於這就打回了6次,老大,求你好好作我的吧😂。 最後說着道歉的話再繼續讓我修改代碼。」
噢,耶!大章第一個沒忍住,喝進去的酒都噴了出來。 我知道,你們就留我在原地尷尬😅。我依然保持着笑容,微笑着面對着慘淡的世界。
「哎,大家都等等,等等! 這算個啥? 有時候視覺都會給大家切那種毛玻璃效果的小圖吧?然而,有一次老大以爲咱們使用圖片資源太多了,就逼着咱們用css寫出毛玻璃的效果。 大家說,老大是否是個狠人!」
<div class="mark"> 老大喜歡手動整的毛玻璃效果</div>
複製代碼
$img: 'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/388f7a44af6b4ea3af893a1fcbfadd71~tplv-k3u1fbpfcp-watermark.image';
body {
height: 100vh;
display: flex;
background-image: url($img);
}
.mark {
position: relative;
margin: auto;
width: 500px;
height: 400px;
background-color: rgba(255, 255, 255, 0.5);
overflow: hidden;
z-index: 10;
line-height: 300px;
text-align: center;
color: #FFFFFF;
font-size: 20px;
&::before {
content: "";
position: absolute;
background-position: top;
filter: blur(8px);
background-image: url($img);
background-repeat: no-repeat;
background-attachment: fixed;
background-size: cover;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: -1;
}
}
複製代碼
啊,喂,喂!我記得我沒逼你啊!咱們不是共同探討的嗎?
「哎呀,大家這跟個人事比起來都正常,不算啥好伐?」
不擅言辭的小白,咱們組少數幾個女生,也是最漂亮的一個。上海本地兒人,姓白,對穿搭很在行。因此,自從她加入咱們第一天,來找她「滋事」的人就很多。有的是借工做上的原油來找她,有的甚至就乾脆路過都要找她閒聊幾句。哪怕她不怎麼擅長應付這些人,但依然天天都有人……
唉,顏值即正義啊!可沒想到小白也要吐槽我嗎?
「大家是不知道,公司不是發了郵件說要組織舞蹈隊,找老大報名。我吧沒敢直接問老大,老大也不安排我,而後我天天穿不一樣的好看的衣服,就但願老大看見了能把我報名報上去。可結果呢,大家知道有多氣嗎!」
突然地,特別安靜了一下子。大章着急催着。
「小白,你快接着說啊!你們都好奇你有多氣呢!」
「結果嘞!我記得我換第三套衣服的時候,也就是第三天,老大忽然告訴我,叫我之後晚上要早點下班,不要總加班。原來老大是覺得我脫單處對象了!」
「噗~!」
好幾個嘴裏還喝着的沒忍住,都噴了。而後笑了一地……
是啊,這羣人太可愛了。我能帶出這樣一波人,我很知足。
終究仍是大章選擇離開。這是我跟大章商量好的,我給他推薦一個很好的機會,他還能拿到這邊的 N+1
賠償。意外的是,小白來找我了。
小白說她的同學好友在隔壁團隊,已經獲得消息她在被裁名單上。小白想本身離開,而後拜託我去把她撈過來。有種一命換一命的感受。
小白還說,由於她的名字裏帶個「白」字,因此叫她小小白。小白求了我2天,這件事情得分開說。我不會主動讓小白離開,是由於小白的技術還不夠,不能和大章同樣能出去獨當一面。若是是小白因本身的事提出離職,我或許沒辦法阻攔,但這種一命換一命的作法,我實在沒辦法苟同。
能正常執行需求,代碼書寫經驗足。
能理解經常使用技術棧的原理,並在代碼層面寫出性能較好的代碼,且能hold住一個項目。
能獨擋一面。能及時解決線上響應的問題,並能提供技術解決方案。
能單獨承擔商業解決方案。
技術上廣度要有,還要有深度。管理方面能hold住業務,能搞好團隊建設,能提高團隊總體實力水平。
大章已經資深邊緣徘徊了,而小白還處於中級階段。至於小小白,只在初級階段。
最終,小白和小小白請我吃了碗酸辣粉,搞定了我。小小白,22歲,工做了2年。20歲就名校畢業,真的是個學霸了。一碗酸辣粉的時間就把優化算法給我講了透。
基於混沌時間序列分析的神經網絡瞭解一下?
噢,不懂是吧?換個方式說,這種優化算法主要用途之一是用來預測。例如用來預測下一個月內的用戶行爲,分析出用戶畫像。你手機裏那些購物APP是否是都有按你的喜愛推薦商品的功能?爲啥購物APP能知道你喜歡哪一類商品?其中的手段之一就是預測。
我了個去,人才啊!我突然以爲我廟小了。我想推薦小小白去算法團隊,被拒絕了。不是被算法團隊拒絕了,是被小小白拒絕了。小小白說要在我這修煉半年,修煉好了就去大廠,讓曾經那些看不起她以爲她菜的人仰着頭看她。
好咯。年輕真好,那就開始修煉吧。我很願意看見半年後小小白來打個人臉,去打那些人的臉。Flag就立在這了。
作好一件事情,成就一批人。 這是我一直在作的事情。
人來了以後,我作了第一次技術分析。沒別的,先把React Hooks原理搞懂。
renderWithHooks
ReactCurrentDispatcher
對象mountWorkInProgressHook
生成hooks鏈表mountState
來初始化 useState
dispatchAction
來控制無狀態組件的更新mountEffect
初始化 useEffect
mountMemo
初始化 useMemo
mountRef
初始化 useRef
updateWorkInProgressHook
找到對應的 hooks
更新 hooks
鏈表updateState
獲得最新的 state
updateEffect
更新 updateQueue
updateMemo
判斷 deps
,獲取or更新緩存值update
獲取 ref
對象先舉個栗子:
import {useState,useEffect,memo} from 'react';
const Com = React.memo(({name})=><div>{name}</div>)
function App(){
const [ num , setNumber ] = useState(0);
const [ name , setName ] = useState('小白');
const handerClick=()=>{
for(let i=0; i<5;i++ ){
setTimeout(() => {
setNumber(num+1)
console.log(num)
}, 1000)
}
}
useEffect(()=>{
setName(num % 2 ? '小白' : '小小白')
},[num])
return <div> <Com name={name}/> <button onClick={ handerClick } >{ num }</button> </div>
}
複製代碼
咱們去看 useState
的源碼: react/src/ReactHooks.js
export function useState(initialState){
const dispatcher = resolveDispatcher(); // 1
return dispatcher.useState(initialState);
}
複製代碼
噢,useState(initialState)
等價於 dispatcher.useState(initialState)
。 dispatcher
從中文意思上是 調度員 的意思。 也就是說你調用 useState
的時候只是通知了調度員去調度真正的 useState
。
dispatcher
又是什麼?看源碼。
function resolveDispatcher() {
const dispatcher = ReactCurrentDispatcher.current
return dispatcher
}
複製代碼
噢,dispatcher
是從 ReactCurrentDispatcher
身上來。咱們來把這個此分析一下,react 當前的(current)調度員(Dispatcher)。
也就是說,到這裏 Dispatcher
就已經安排好了。
就是說,你的 App 組件 是何時被調用的? React 16 版本的架構能夠分爲三層:
Scheduler
(調度層):調度任務的優先級,高優任務優先進入協調器Reconciler
(協調層):構建 Fiber 數據結構,比對 Fiber 對象找出差別, 記錄 Fiber 對象要進行的 DOM 操做Renderer
(渲染層):負責將發生變化的部分渲染到頁面上咱們知道 render
一個組件 首先要構建 組件的 Fiber 鏈表
。因此咱們來看協調層的源碼:react-reconciler/src/ReactFiberBeginWork.js
renderWithHooks(
null, // current Fiber
workInProgress, // workInProgress Fiber
Component, // 函數組件自己
props, // props
context, // 上下文
renderExpirationTime,// 渲染 ExpirationTime
);
……
renderWithHooks(
current, // current Fiber
workInProgress, // workInProgress Fiber
Component, // 函數組件自己
props, // props
context, // 上下文
renderExpirationTime,// 渲染 ExpirationTime
);
複製代碼
咱們先看 renderWithHooks
幾個咱們咱們最熟悉的參數。Component
是函數自己,props
是咱們傳給函數組件的信息,context
表明當前的上下文。
那,有木有可能咱們的 Component
就是在 renderWithHooks
方法裏被調用的?接着看源碼(精簡了一下)。
function renderWithHooks( current, workInProgress, Component, props, secondArg, nextRenderExpirationTime, ) {
renderExpirationTime = nextRenderExpirationTime;
currentlyRenderingFiber = workInProgress;
workInProgress.memoizedState = null;
workInProgress.updateQueue = null;
workInProgress.expirationTime = NoWork;
// 3 很重要!
ReactCurrentDispatcher.current =
current === null || current.memoizedState === null
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate;
let children = Component(props, secondArg); // 2
// code ...
ReactCurrentDispatcher.current = ContextOnlyDispatcher;
renderExpirationTime = NoWork;
currentlyRenderingFiber = null;
currentHook = null
workInProgressHook = null;
didScheduleRenderPhaseUpdate = false;
return children; // end
}
複製代碼
噢,renderWithHooks
的返回值是 children, 而 children = Component(props, secondArg);
破案了,咱們的函數組件就是在 renderWithHooks
被調用且最終 return
回來。
咱們再回到 3 ,ReactCurrentDispatcher.current
是否是前面沒解釋清楚的 調度員 的歸宿?! 解釋一下這行代碼: 當 current
爲 null
或者 current
的 memoizedState
屬性爲 null
就把 HooksDispatcherOnMount
賦值給咱們的調度員, 不然就把HooksDispatcherOnUpdate
賦值給咱們的調度員。
從這兩名稱上又能看出個大概來,一個是 Mount
的 調度員,一個是 Update
的調度員。那也就是說,初始化 hooks
的時候就是 Mount
調度員,要更新的時候就是 Update
調度員?!
ok,案子到這算是破了80%了。
workInProgress
: 從名稱分析,就是工做進度 或者 正在進行中的工做 的意思吧? 那它是個對象吧? 那對象身上確定會有一些屬性用來描述不一樣信息對吧?
workInProgress.memoizedState
:
useState
,保存 state
信息useEffect
,保存 effect
對象useMemo
, 保存緩存的值
和 deps
useRef
, 保存 ref
對象。也就是說,workInProgress.memoizedState 存放的是 咱們所使用的hooks 的信息。
這裏的 workInProgress.updateQueue
後面再提。
useState
的時候發生了什麼。先看精簡源碼。
function mountState( initialState ){
const hook = mountWorkInProgressHook();
//若是 initialState爲函數,則執行initialState函數。
if (typeof initialState === 'function') {
initialState = initialState();
}
hook.memoizedState = hook.baseState = initialState;
const queue = (hook.queue = {
pending: null, // 待更新的內容
dispatch: null, // 調度函數
lastRenderedReducer: basicStateReducer,
lastRenderedState: initialState, // 最新一次渲染的 state
});
// 負責更新的函數
const dispatch = (queue.dispatch = (dispatchAction.bind(
null,
currentlyRenderingFiber,
queue,
)))
return [hook.memoizedState, dispatch];
}
複製代碼
噢,這個代碼明顯要更容易分析一些。
const hook = mountWorkInProgressHook();
initialState
進行判別。接着將 initialState
賦值給 hook.memoizedState
和 hook.baseState
queue
,信息看註釋。const [x,setX] = useState(initialState);
複製代碼
function dispatchAction<S, A>( fiber: Fiber, queue: UpdateQueue<S, A>, action: A, ) 複製代碼
對照上述代碼,S 表明 什麼? A 表明什麼?
setX
就是調用了 dispatchAction
吧? 源碼中顯示 dispatchAction
已經有了 currentlyRenderingFiber
, queue
兩個參數了,那 setX
傳入的參數應該就是第三個參數 action
了吧?
dispatchAction
到底幹了什麼?function dispatchAction(fiber, queue, action) {
// code ...
// step 1 : 初始化要更新的信息
const update= {
expirationTime,
suspenseConfig,
action,
eagerReducer: null,
eagerState: null,
next: null,
}
// 斷定是否是首次更新
const pending = queue.pending;
if (pending === null) { // 證實第一次更新
update.next = update;
} else { // 不是第一次更新
update.next = pending.next;
pending.next = update;
}
queue.pending = update;
const alternate = fiber.alternate;
// 判斷當前是否在渲染階段
if ( fiber === currentlyRenderingFiber || (alternate !== null && alternate === currentlyRenderingFiber)) {
// code ...
} else {
// code ...
// 剩下的事情交由 調度層 去完成。
scheduleUpdateOnFiber(fiber, expirationTime);
}
}
複製代碼
唉,大體看看Fiber對象上有哪些屬性吧。
type Fiber = {
/************************ DOM 實例相關 *****************************/
// 標記不一樣的組件類型, 值詳見 WorkTag
tag: WorkTag,
// 組件類型 div、span、組件構造函數
type: any,
// 實例對象, 如類組件的實例、原生 dom 實例, 而 function 組件沒有實例, 所以該屬性是空
stateNode: any,
/************************ 構建 Fiber 樹相關 ***************************/
// 指向本身的父級 Fiber 對象
return: Fiber | null,
// 指向本身的第一個子級 Fiber 對象
child: Fiber | null,
// 指向本身的下一個兄弟 iber 對象
sibling: Fiber | null,
// 在 Fiber 樹更新的過程當中,每一個 Fiber 都會有一個跟其對應的 Fiber
// 咱們稱他爲 current <==> workInProgress
// 在渲染完成以後他們會交換位置
// alternate 指向當前 Fiber 在 workInProgress 樹中的對應 Fiber
alternate: Fiber | null,
/************************ 狀態數據相關 ********************************/
// 即將更新的 props
pendingProps: any,
// 舊的 props
memoizedProps: any,
// 舊的 state
memoizedState: any,
/************************ 反作用相關 ******************************/
// 該 Fiber 對應的組件產生的狀態更新會存放在這個隊列裏面
updateQueue: UpdateQueue<any> | null,
// 用來記錄當前 Fiber 要執行的 DOM 操做
effectTag: SideEffectTag,
// 存儲要執行的 DOM 操做
firstEffect: Fiber | null,
// 單鏈表用來快速查找下一個 side effect
nextEffect: Fiber | null,
// 存儲 DOM 操做完後的副租用 好比調用生命週期函數或者鉤子函數的調用
lastEffect: Fiber | null,
// 任務的過時時間
expirationTime: ExpirationTime,
// 當前組件及子組件處於何種渲染模式 詳見 TypeOfMode
mode: TypeOfMode,
};
複製代碼
在 React 16 中,將整個任務拆分紅了一個一個小的任務進行處理,每個小的任務指的就是一個 Fiber 節點的構建。
至於Fiber鏈表。
React
經過鏈表結構找到下一個要執行的任務單元。 要構建鏈表結構,須要知道每個節點的:
父級節點
是誰子級節點
是誰,要知道他的下一個兄弟節點
是誰。上面已經把Fiber 對象 身上掛的屬性挪列很詳細了。須要你去瞅瞅。
當全部DOM的Fiber對象生成完畢,那須要執行DOM操做的Fiber就會構建出Fiber鏈表。至於構建Fiber 鏈表的原理是什麼,以下代碼(不是源碼,只是爲了看得更清晰,手動寫了一波。但願你有空也手動寫一遍):
import React from "react"
const jsx = (
<div id="a"> <div id="b1"> <div id="c1"> <div id="d1"></div> <div id="d2"> <div id="e1"></div> <div id="e2"></div> </div> </div> <div id="c2"></div> </div> <div id="b2"></div> </div>
)
const container = document.getElementById("root")
/** * 1. 爲每個節點構建 Fiber 對象 * 2. 構建 Fiber 鏈表 * 3. 提交 Fiber 連接 */
// 建立根元素 Fiber 對象
const workInProgressRoot = {
stateNode: container,
props: {
children: [jsx]
}
}
let nextUnitOfWork = workInProgressRoot
function workLoop(deadline) {
// 若是下一個要構建的執行單元存在而且瀏覽器有空餘時間
while (nextUnitOfWork && deadline.timeRemaining() > 0) {
// 構建執行單元並返回新的執行單元
nextUnitOfWork = performUnitOfWork(nextUnitOfWork)
}
// 若是全部的執行單元都已經構建完成
if (!nextUnitOfWork) {
// 進入到第二個階段 執行 DOM 操做
commitRoot()
}
}
// Fiber 工做的第一個階段
function performUnitOfWork(workInProgress) {
// 構建階段向下走的過程
// 1. 建立當前 Fiber 節點的 DOM 對象並存儲在 stateNode 屬性中
// 2. 構建子級 Fiber 對象
beginWork(workInProgress)
// 若是子級存在
if (workInProgress.child) {
// 返回子級 構建子級的子級
return workInProgress.child
}
// 開始構建階段向上走的過程
// 若是父級存在
while (workInProgress) {
// 構建 Fiber 鏈表
completeUnitOfWork(workInProgress)
// 若是同級存在
if (workInProgress.sibling) {
// 返回同級 構建同級的子級
return workInProgress.sibling
}
// 同級不存在 退回父級 看父級是否有同級
workInProgress = workInProgress.return
}
}
function beginWork(workInProgress) {
// 若是 Fiber 對象沒有存儲其對應的 DOM 對象
if (!workInProgress.stateNode) {
// 建立 DOM 對象並存儲在 Fiber 對象中
workInProgress.stateNode = document.createElement(workInProgress.type)
// 爲 DOM 對象添加屬性
for (let attr in workInProgress.props) {
if (attr !== "children") {
workInProgress.stateNode[attr] = workInProgress.props[attr]
}
}
}
// 建立子級 Fiber 對象
if (Array.isArray(workInProgress.props.children)) {
// 記錄上一次建立的子級 Fiber 對象
let previousFiber = null
// 遍歷子級
workInProgress.props.children.forEach((child, index) => {
// 建立子級 Fiber 對象
let childFiber = {
type: child.type,
props: child.props,
return: workInProgress,
effectTag: "PLACEMENT"
}
// 第一個子級掛載到父級的 child 屬性中
if (index === 0) {
workInProgress.child = childFiber
} else {
// 其餘子級掛載到本身的上一個兄弟的 sibling 屬性中
previousFiber.sibling = childFiber
}
// 更新上一個子級
previousFiber = childFiber
})
}
}
function completeUnitOfWork(workInProgress) {
let returnFiber = workInProgress.return
if (returnFiber) {
// 鏈頭上移
if (!returnFiber.firstEffect) {
returnFiber.firstEffect = workInProgress.firstEffect
}
// lastEffect 上移
if (!returnFiber.lastEffect) {
returnFiber.lastEffect = workInProgress.lastEffect
}
// 構建鏈表
if (workInProgress.effectTag) {
if (returnFiber.lastEffect) {
returnFiber.lastEffect.nextEffect = workInProgress
} else {
returnFiber.firstEffect = workInProgress
}
returnFiber.lastEffect = workInProgress
}
}
}
// Fiber 工做的第二階段
function commitRoot() {
// 獲取鏈表中第一個要執行的 DOM 操做
let currentFiber = workInProgressRoot.firstEffect
// 判斷要執行 DOM 操做的 Fiber 對象是否存在
while (currentFiber) {
// 執行 DOM 操做
currentFiber.return.stateNode.appendChild(currentFiber.stateNode)
// 從鏈表中取出下一個要執行 DOM 操做的 Fiber 對象
currentFiber = currentFiber.nextEffect
}
}
// 在瀏覽器空閒的時候開始構建
requestIdleCallback(workLoop)
複製代碼
自從上次分享了 React Hooks 相關的東西,安生了2個星期。小小白刻苦學習,公司加班寫需求,回家繼續加班寫代碼。
我是否是逼得人太緊了?
狗叫了(個人手機響鈴聲)。
「你怎麼回事,運營說線上出故障了!剛答應你留我的,你就這樣報答個人……」
啪!我把電話掛了。啥也沒說,直接掛了。我迅速回到位置上,小小白淚流滿面就差哭暈在工位上了。我這時候是否是應該噴一句: MMP,出了事就知道哭,出了問題你卻是第一時間告訴我啊!
事情是這樣的,小小白作了一個活動頁,內容是讓用戶點擊屏幕搶紅包。問題是,用戶能夠點到了就搶到了紅包,理論上只要手速夠快,屏幕能識別用戶的手勢就能搶到。也就是說你點1千次,就能搶到1千個紅包。 固然了,這樣的說法太誇張。用戶最多搶到200塊的紅包,後面搶再多也不會超過200塊錢。
用戶手速不可能快到那個程度,可小小白的活動頁,搶到三、4個紅包的時候動畫就顯得很卡頓。用戶明明點選到了卻沒搶到紅包,問題就被活動運營上報了。
瞭解事情大概後,個人電話已經撥給某個運營老大。
「老鐵,這個問題狀況如何了?」
「目前已經有幾十筆投訴了,不過預計接下來流量很大。我如今已經讓手下暫停推廣這個活動頁了。你那邊怎麼搞?」
「老鐵,這樣。你先別讓暫停推廣手段,你那邊的流程照常進行,半小時內不要拿真正的活動地址,你能夠先整個預熱活動。」
電話那邊沉默了十幾秒。我知道她會幫個人。
「你能肯定半小時後一切正常嗎?」
「能。」
「那事情要是搞定了,跟我約會。」
啪!手機丟進抽屜,神特麼約會,上帝來了也別打擾我!
我走到小小白,她還趴着那哭。小白在安慰,看到我過來。
「老大,怎麼作?」
「讓開。」
我坐在小白的位置上。
「鎖屏密碼。」
「110gaosuwoyaodidiao」 (110告訴我要低調)
我是記得小白今天是有一個自動埋點的SDK要上線的,不過預計是晚上,白天還在作最後的迴歸。
「小白,我能夠相信你嗎?」
「能夠。」
我打開vscode,在sdk上面埋伏了一段駭客代碼。主要作了幾件事情:
這樣作很危險。沒有測試,沒有驗證。甚至不合法。 但沒有比如今更差勁的結果了。
commit 代碼。直接越過測試上預發環境(上線前的模擬線上環境)。
「小白,拿你手機出來。」
我看了下時間,已通過去15分鐘了。這時候小小白中止哭了,應該是被我嚇的。
「小小白,5分鐘內把測試那邊的機器拿來跑一下。能作到嗎?」
小小白機械式的點了點頭,隨後跑去拿手機了。
我盯着手機時間,21分鐘的時候,上線。小小白卻忽然拉住我。
「老大,這裏有個小米6,仍是有點卡頓。」
我看了一眼,點擊紅包的時候會有輕微的抖動。隨即沒理小小白,上線。用個人權限強行開了一個自測迭代上線。
操做完之後,就是排隊等待上線了。我忽然問了句小小白。
「小小白,此次我能相信你嗎?」
我看到了小小白眼裏的慌張,但她仍是咬了咬下嘴脣,對我點了點頭。我回位置上拿出手機,老闆的未接數量嗆死了個人屏幕。
我走到老闆辦公室,敲開了門。老闆把水杯摔到門口。
「你如今牛逼了啊!不把我放眼裏了啊!運營老大把事情捅到業務方那邊,問題都上升到歐總(CEO)那了。你他嗎躲什麼躲,第一時間解決問題知道嗎?」
唉。運營部也真是一地雞毛,那我運營老鐵是個北方妹紙,估計就是個運營老四老五,至於那個老大,我和他鬧過矛盾。此次估計就是他把事情捅上去了。
我僞裝淡定的關上門,隔絕一下外面的聲音。個人直覺告訴我,個人那些娃確定都在直勾勾的看着我。
「老闆,到底發生什麼事了。」
「你手底下的人搞出故障了,你還問我什麼事?!」
我看了下時間,差很少了。我當着老闆面,拿出手機,撥電話給老鐵,打開外音。
「喂,啥事?我如今忙着搞活動。」
「不是出故障了嗎?」
「出故障了?你嗎? 我這邊活動正常在進行,沒聽到誰說出了問題呀。」
「好了。那你先忙。」
我掛掉電話,眼睛疑惑的看着老闆:「老闆,我今天好像就是一個新活動頁上線了,騙用戶搶紅包的。」
說完。我拿出手機,裝模做樣找到活動頁,本身玩了一下子。玩過了以後玩把遊戲結果給老闆看。
「老闆,這不是挺好的嘛?」
接着我又裝模做樣打給小小白。
「喂,今天的活動頁是你作的吧? 你拿幾個手機過來,我在總監辦公室這。」
意料以外,小小白進來了,我看她臉色竟然看不出一絲哭過痕跡,要多淡定就有多淡定。我讓小小白每一個機器都演示了一遍。
「老闆,你從哪聽風就是雨?活動頁開局有幾十單投訴不是很正常嗎?我剛在跟我丈母孃聊彩禮的事兒,沒注意我那個手機響,至於嗎?」
從老闆辦公室出來,我清晰的聽見了身後小小白大口吸氣的聲音。
「怕了?走,今天有上線需求的人一塊兒吃個夜宵。」
半夜,線上所有驗證完畢。 擼串的時候,我告訴你們。
「出了問題要淡定,了不得就是被開除了。那個總監看我不順眼不是一天兩天了,我就敢懟他! 你們不只要有寫代碼的能力,更重要的事要有解決問題的能力……」
其實有些事情你們都心領神會,小小白喝了點啤酒,酒量差,又哭了一回。
「故障」事件過去之後,小小白不是纏着小白就是纏着我,目的是想知道如何解決那個搶紅包卡頓的問題。
咱們來分析一下。卡頓現象,通常能夠經過用戶反饋或性能監控來發現。好比咱們接到用戶投訴說活動頁卡致使搶不了紅包了,而後在性能平臺上查看卡頓指標後,發現頁面出現連續 10 幀超過 40ms ,這就屬於嚴重卡頓。
如何處理呢?
先肯定一下是否是服務端出了數據問題。這些能夠經過抓包或者查看服務端異常日誌能夠作到。
若是問題出在前端,通常是兩種情形:瀏覽器的主線程與合成線程調度不合理,以及計算耗時操做。
拿這個活動頁(紅包雨)來講,用戶投訴說卡頓,那是直觀感覺,大機率和動畫效果有關。動畫頗有不少種,但主要仍是是css計算。
瀏覽器的主線程
主要負責運行 JavaScript
,計算 CSS 樣式
,元素佈局
,合成線程主要負責繪製UI
。當 width、height、margin、padding
等做爲 transition
值動畫時,主線程壓力很大,由於計算 CSS 樣式
也是耗時的。此時如何用 transform
來代替直接設置 margin、padding
。
舉個栗子。加入表明紅包的那個DOM元素 margin-left: 0px
,最終紅包在 margin-left: 100px
的時候被點擊了,那這個過程假如step = 1,那從0 ~ 100 就須要瀏覽器主線程計算100次,每次計算完都要合成線程繪製到 GPU 再渲染到屏幕上。 這個過程,瀏覽器壓力可想而知有多大。
但若是用 tranform:translate(-100px,0)
,瀏覽器主線程計算1次,合成線程就能 0 直接繪製100 。 其中差距可想而知。
另外一方面,就是計算耗時操做。
Virtual DOM
的出現就是爲了解決頻繁操做DOM致使的性能卡頓問題。若是要操做100個DOM
,那先把操做信息存在Virtual DOM
上,待全部操做完畢再講Virtual DOM
一次性更新到真實DOM上。 React 和 Vue走的不正式 Virtual DOM 路線嗎?
js計算。例如搶紅包有大有小金額,js是單線程,已經在響應用戶操做了,你這時候在要js去計算,那不就只能讓js單線程先計算金額,用戶的行爲暫時放一邊,你說用戶感受不到卡頓就有鬼了。這類問題解決方法有兩種。
這裏有個知識點提一下。
頁面是一幀一幀繪製出來的,當每秒繪製的幀數達到 60 時,頁面是流暢的,小於這個值時,用戶會感受到卡頓。
1s 60幀,每一幀分到的時間是 1000/60 ≈ 16 ms,若是每一幀執行的時間小於16ms,就說明瀏覽器有空餘時間。
若是任務在剩餘的時間內沒有完成則會中止任務執行,繼續優先執行主任務,也就是說 requestIdleCallback
老是利用瀏覽器的空餘時間執行任務。
某個週末,上午11點。我還在睡夢中(太累了),小小白打來電話。
「喂,老大你還在睡覺吶!小白說要打羽毛球,把你們都叫上了,就差你了。」
啪!我把電話扔了。 還沒過一下子,手機開始奪命連環call模式。好,我輸了,我錯了,我撿回來……
「喂,最好給我一個不噴你的理由。不然拉黑!」
「&%@!¥%*(·」
「說人話!」
「我說你個死渣男,昨晚又在哪鬼混了?以前答應個人約會你怕不是早就忘記了吧?!」
好吧。運營老鐵你好,我錯了。你都知道我是個死渣男,那忘記了是個人錯嗎?
「老鐵,我叫你老鐵,你會找老鐵約會嗎?」
「就算不約會,吃個飯就這麼難嗎?」
電話那頭忽然很正經的來了這麼一句。我感受彷佛有點不太對頭。
「我立刻要滾蛋了,你就不能請我吃頓飯嗎?你抽點時間吃個飯仍是能夠的吧?」
我趕緊電話掛掉,微信羣已經爆炸了。組織架構調整,運營老大升VP了,運營老鐵要離職了。個人睡意頓時全無,又一個很是令我瘋狂的想法出如今腦海裏。
這傻姑娘不會是懼怕運營老大找她的麻煩,清算上次的帳了。上次的事情已經定性了,就是個誤會。但VP是有很大權限的,要是翻案……
因此,成年人作任何事情都是須要付出代價的,職場也不例外。我很慶幸我有一些關係很鐵的同事,他們更像是個人朋友。
新的週一,老鐵已經走了。呵,這要是沒領導贊成,哪能這麼快就走了。我看着我手底下這些娃,都在負責各自手上的事。我再看看領導辦公室,我突然間也有了決斷。我也是時候該離開了。
就跟小小白約定的那樣,等半年吧。因而,我開始了瘋狂的技術分享之路。
其實,前端性能最重要的指標是「快
」,實在快不了的再選擇「做弊或者欺詐
」的手段。
快
方面:
請求資源
Html、js、css、圖片等靜態資源本質上都從服務器上獲取。可服務器響應客戶端請求是須要時間的,返回的資源體積越大,耗時越長。因此想要快,有三方面考慮。
webpack
打包, treeShaking
、codeSplit等
,儘可能保證打包後的體積不要太大。CDN
,這也是變相的減少了包體積。代碼裏直接引用CDN
的連接。native
進行預請求,再將關鍵數據直接交給h5便可http緩存
本地緩存。
例如native對圖片資源進行緩存接口緩存。
加載方式
骨架屏
瞭解一下。NSR
瞭解一下。CSR
瞭解一下webview 優化
並行初始化
App
時,系統就建立 WebView
和加載模板,這樣 WebView 初始化
和 App 啓動就能夠並行進行了,這大大減小了用戶等待時間。資源預加載
。資源預加載,是指提早在初始化的 WebView 裏面放置一個靜態資源列表,後續加載東西時,因爲這部分資源已經被強緩存了,頁面顯示速度會更快。那麼,要預加載的靜態資源通常能夠放:
ps: 別小瞧webview這些,作好了能給你減小100-200ms的時間。
APP啓動階段的優化方案
頁面白屏階段的優化方案
首屏渲染階段的優化方案
DOM性能優化
memo、usememo
Render
次數性能平臺
我意識到這個時間節點到來的時候,是由於我接到了一個陌生電話,是背調公司來打聽小小白狀況的。
是啊,這半年也快過去了。小小白成長的速度也是跟作火箭同樣。我認真看了看這羣可愛的小夥伴,我發現你們都能開始獨當一面了,即便我離開了你們也能混得開。
我偷偷用另一個微信加了小小白好友,以能夠幫她內推的名義和小小白打得火熱。唉,果真年輕就是好騙,這種稍微留個心眼就能被戳穿的伎倆忽悠得小小白舒舒服服的。
我先要來了小小白的簡歷,給了一些建議,並幫忙修改。
其實最乾淨的簡歷是要保證HR、面試官能迅速對你的簡歷進行匹配。那樣,即不會造成誤會,也不會浪費雙方的時間。例若有些人的簡歷喜歡把全部瞭解的技術都寫上,例如瞭解node.js ,結果面試官就往node方面往死裏問,結果可想而知。
我的履歷 !== 工做經歷。
我的履歷更像是你對本身以往工做內容的一種 述職
。舉個栗子:
1. 負責toB/toC業務相關項目設計
2. 對ToB、ToC業務的前端開發和管理,把控項目進度,推動合做方達成目標有豐富經驗。
3. 喜歡研究新的技術,對能提升項目性能的極其熱衷,並致力於將其更新至線上產品。
複製代碼
必不可少的內容之一。除了把工做時間交待清楚,還但願把你當前的角色寫清楚。固然還有你的在公司負責過的業務內容。
請按照如下模版書寫
效果/結果 是什麼
: 例如你作了什麼,提高了下單轉化率,賺了多少錢,完成了多少KPI。但願用數字來扎眼。我但願你的自我評價能直接將你的厲害的方面直抒胸臆。
例如;
不但願看見精通、熟悉、瞭解
等字眼。由於一千我的眼裏就有一千個哈姆雷特,也許你的精通是面試官的熟悉呢? 與其如此還不如交待明白你對此的技術研究程度。
萬字了哈!小小白的故事其實沒講完,劇情有些狗血。就是小小白最終去了想去的大廠,而後發現了事實真相。
不過已經不重要了,結果是好的,曲終人散後各自安好即是。
再見,小小白。
再見,渣男。
小小白最後打電話噴個人,誰讓我這麼「舔狗式」的作法。即便真相被戳穿了依然打死不認可😂😂😂。
(祝,君安好。但願你能從這篇長文故事裏獲得自我成長! 另外,本篇沒交待清楚的細節可能會在下一篇文。)