學習尤雨溪寫的 Vue3 源碼中的簡單工具函數

大家好,我是若川。最近組織了源碼共讀活動。每周讀 200 行左右的源碼。很多第一次讀源碼的小伙伴都感覺很有收獲,感興趣可以加我微信ruochuan12,拉你進群學習。

初學者也能看懂的 Vue3 源碼中那些實用的基礎工具函數
本文是紀年小姐姐源碼共讀第二期寫的筆記,非常好,可先收藏后學習。

1. 解讀前的準備

粗略閱讀了川哥的文章之后,感覺這期跟上一期不一樣。上一期主要學習如何實現某個功能,而這一期主要是學習 Vue3 源碼中的工具函數,以及 Vue3 源碼的一些調試技巧。雖然看起來偏基礎,但我覺得很考驗一個程序員的基本功和耐心。

學習目標:

1)調試源碼之打包構建項目代碼,生成 sourcemap 調試源碼

2)學習源碼中的工具函數

目標:跟著川哥的文章走完一遍調試的流程,動手敲工具函數,對外輸出記錄文檔。

資源準備:

Vue3 源碼地址:https://github.com/vuejs/vue-next

2. 源碼調試

2.1 閱讀開源項目的 README.md 和貢獻指南 contributing.md

我覺得這兩個文件對閱讀源碼的開發者來說十分重要。README.md 描述的是項目的基本信息,它可以快速了解這個項目的全貌。貢獻指南 contributing.md 會包含如何參與項目開發,項目打包/運行命令,項目目錄結構等等,它能幫助你更好地調試/參與開發源碼。在 contributing.md 中我看到了一些比較感興趣的知識點,比如打包構建格式/配置,包依賴處理。

2.2 打包構建項目代碼

安裝完依賴,直接運行yarn build就可以打包 Vue3 的項目代碼了,打包的產物如下(以 shared 模塊為例):

打包后的產物

這里的 cjsesm 是 JS 里用來實現【模塊化】的不同規則,JS 的模塊化標準還有 amdumdiife

  • CJS,CommonJS,只能在 NodeJS 上運行,使用 require("module") 讀取并加載模塊,不支持瀏覽器

  • ESM,ECMAScript Module,現在使用的模塊方案,使用 import export 來管理依賴,瀏覽器直接通過 <script type="module"> 即可使用該寫法。NodeJS 可以通過使用 mjs 后綴或者在 package.json 添加 "type": "module" 來使用

2.3 生成 sourcemap 調試 vue-next 源碼

在貢獻指南 contributing.md 文件中描述了如何生成 sourcemap 文件:添加【--sourcemap】參數即可。

node?scripts/dev.js?--sourcemap

packages/vue/dist/vue.global.js.map 就是 sourcemap 文件了。

sourcemap 是一個信息文件,里面儲存著位置信息,轉換后的代碼的每一個位置,所對應的轉換前的位置。有了它,出錯時出錯工具將直接顯示原始代碼,而不是轉換后的代碼,方便調試。

3. 工具函數(TS 版)

3.1 babelParserDefaultPlugins:babel 解析默認插件

/***?List?of?@babel/parser?plugins?that?are?used?for?template?expression*?transforms?and?SFC?script?transforms.?By?default?we?enable?proposals?slated*?for?ES2020.?This?will?need?to?be?updated?as?the?spec?moves?forward.*?Full?list?at?https://babeljs.io/docs/en/next/babel-parser#plugins*/
const?babelParserDefaultPlugins?=?['bigInt','optionalChaining','nullishCoalescingOperator'
]?as?const

它定義了三個默認插件, as const 這個語法叫 const 斷言,它可以創建完整的 readonly 對象(只讀狀態),編譯器可以通過 as const 推斷出可用于的最具體的表達類型。

3.2 EMPTY_OBJ:空對象,EMPTY_ARR:空數組

export?const?EMPTY_OBJ:?{?readonly?[key:?string]:?any?}?=?__DEV__??Object.freeze({}):?{}export?const?EMPTY_ARR?=?__DEV__???Object.freeze([])?:?[]

Object.freeze 凍結對象,不可修改對象的最外層,這樣的寫法可以降低在開發過程中發生錯誤。

DEV ?是一個環境變量,為了避免在生產環境報錯,生產環境使用的還是 {} 和 []。

3.3 NOOP:空函數

export?const?NOOP?=?()?=>?{}

3.4 NO:永遠返回 false 的函數

export?const?NO?=?()?=>?false

3.5 isOn:判斷字符串是否以 on 開頭,并且 on 后首字母是非小寫字母

const?onRE?=?/^on[^a-z]/
export?const?isOn?=?(key:?string)?=>?onRE.test(key)

【^】符號在開頭,表示是指【以什么開頭】,在其他地方是指【非】。與之相反的是:【$】符合在結尾,則表示是以什么結尾。

日常開發中我們也經常會用到正則判斷,可以收集起來,積累的數量多了就不用每次都去搜索了????。

3.6 isModelListener:監聽器

export?const?isModelListener?=?(key:?string)?=>?key.startsWith('onUpdate:')

判斷字符串是不是以【onUpdate:】開頭

3.7 extend:合并對象

export?const?extend?=?Object.assign

其實 extend 就是 Object.assign,用于將所有可枚舉屬性的值從一個或多個源對象分配到目標對象。

3.8 ?remove:移除數組的一項

export?const?remove?=?<T>(arr:?T[],?el:?T)?=>?{const?i?=?arr.indexOf(el)if?(i?>?-1)?{arr.splice(i,?1)}
}

看源碼的實現很好理解,傳入一個數組和一個元素,判斷元素是否存在在數組中,如果存在將其刪除。

川哥的文章里有說到,splice 是一個很耗性能的方法,刪除數組中的一項,其他元素都要移動位置。所以在考慮性能的情況下,可以將刪除的元素設為 null,在使用執行時為 null 的不執行,也可達到相同的效果

3.9 hasOwn:判斷一個屬性是否屬于某個對象

const?hasOwnProperty?=?Object.prototype.hasOwnProperty
export?const?hasOwn?=?(val:?object,key:?string?|?symbol
):?key?is?keyof?typeof?val?=>?hasOwnProperty.call(val,?key)

函數本身很好理解,利用原型的 API:hasOwnProperty 來判斷 key 是否是 obj 本身的屬性。

但【key is keyof typeof val】可能會有些迷惑,這里包含了三個 typescript 的語法,意思是函數返回的 key 是 屬于 val 對象的鍵的聯合類型。

  • 【is】關鍵字:它被稱為類型謂詞,用來判斷一個變量屬于某個接口或類型,比如:

const?isNumber?=?(val:?unknown):?val?is?number?=>?typeof?val?===?'number'
const?isString?=?(val:?unknown):?val?is?string?=>?typeof?val?===?'string'
  • 【keyof】關鍵字:用于獲取某種類型的所有鍵,其返回類型是聯合類型,比如:

interface?Person?{name:?string;age:?number;
}
type?K?=?keyof?Person;?//?"name"?|?"age"
  • 【typeof】關鍵字:js 中的 typeof 只能獲取幾種類型,而在 ts 中 typeof 用來獲取一個變量聲明或對象的類型,比如:

interface?Person?{name:?string;age:?number;
}const?sem:?Person?=?{?name:?'semlinker',?age:?30?};
type?Sem?=?typeof?sem;?//?->?Person

3.10 判斷是否某種類型

//?判斷數組
export?const?isArray?=?Array.isArray//?對象轉字符串
export?const?objectToString?=?Object.prototype.toString
export?const?toTypeString?=?(value:?unknown):?string?=>objectToString.call(value)//?判斷是否?Map?對象
export?const?isMap?=?(val:?unknown):?val?is?Map<any,?any>?=>toTypeString(val)?===?'[object?Map]'//?判斷是否?Set?對象
export?const?isSet?=?(val:?unknown):?val?is?Set<any>?=>toTypeString(val)?===?'[object?Set]'//?判斷是否?Date?對象
export?const?isDate?=?(val:?unknown):?val?is?Date?=>?val?instanceof?Date//?判斷是否函數
export?const?isFunction?=?(val:?unknown):?val?is?Function?=>typeof?val?===?'function'//?判斷是否字符串
export?const?isString?=?(val:?unknown):?val?is?string?=>?typeof?val?===?'string'//?判斷是否?Symbol
export?const?isSymbol?=?(val:?unknown):?val?is?symbol?=>?typeof?val?===?'symbol'//?判斷是否對象(不包括?null)
export?const?isObject?=?(val:?unknown):?val?is?Record<any,?any>?=>val?!==?null?&&?typeof?val?===?'object'//?判斷是否?Promise
export?const?isPromise?=?<T?=?any>(val:?unknown):?val?is?Promise<T>?=>?{return?isObject(val)?&&?isFunction(val.then)?&&?isFunction(val.catch)
}

有了這些函數就可以在工作中用起來啦。

3.11 toRawType:對象轉字符串,截取后第八位到倒數第二位。

export?const?toRawType?=?(value:?unknown):?string?=>?{//?extract?"RawType"?from?strings?like?"[object?RawType]"return?toTypeString(value).slice(8,?-1)
}

可以截取到 String Array 等這些類型,這個函數可以用來做類型判斷。

3.12 ?isPlainObject:判斷是否純粹的對象

export?const?isPlainObject?=?(val:?unknown):?val?is?object?=>toTypeString(val)?===?'[object?Object]'

3.13 isIntegerKey:判斷是不是數字型的字符串 key 值

export?const?isIntegerKey?=?(key:?unknown)?=>isString(key)?&&key?!==?'NaN'?&&key[0]?!==?'-'?&&''?+?parseInt(key,?10)?===?key

第一步先判斷 key 是否是字符串類型(作為 key 值有兩種類型,string 和 symbol),第二步排除 NaN 值,第三步排除 - 值(排除負數),第四步將 key 轉換成數字再隱式轉換為字符串,與原 key 對比。

3.14 isReservedProp:判斷該屬性是否為保留屬性

/***?Make?a?map?and?return?a?function?for?checking?if?a?key*?is?in?that?map.*?IMPORTANT:?all?calls?of?this?function?must?be?prefixed?with*?\/\*#\_\_PURE\_\_\*\/*?So?that?rollup?can?tree-shake?them?if?necessary.*/
export?function?makeMap(str:?string,expectsLowerCase?:?boolean
):?(key:?string)?=>?boolean?{const?map:?Record<string,?boolean>?=?Object.create(null)const?list:?Array<string>?=?str.split(',')for?(let?i?=?0;?i?<?list.length;?i++)?{map[list[i]]?=?true}return?expectsLowerCase???val?=>?!!map[val.toLowerCase()]?:?val?=>?!!map[val]
}export?const?isReservedProp?=?/*#__PURE__*/?makeMap(//?the?leading?comma?is?intentional?so?empty?string?""?is?also?included',key,ref,'?+'onVnodeBeforeMount,onVnodeMounted,'?+'onVnodeBeforeUpdate,onVnodeUpdated,'?+'onVnodeBeforeUnmount,onVnodeUnmounted'
)//?使用:
isReservedProp("key")?//?true
isReservedProp("test")?//?false
isReservedProp("")?//?true

如何解讀這個函數?先看 makeMap,它傳入一個字符串,將這個字符串轉換成數組,并循環賦值 key 給一個空對象map,然后返回一個包含參數 val 的閉包用來檢查 val 是否是存在在字符串中。

isReservedProp("key") 其實就相當于 makeMap(str)("key")。

3.15 cacheStringFunction 緩存字符串的函數

const?cacheStringFunction?=?<T?extends?(str:?string)?=>?string>(fn:?T):?T?=>?{const?cache:?Record<string,?string>?=?Object.create(null)return?((str:?string)?=>?{const?hit?=?cache[str]return?hit?||?(cache[str]?=?fn(str))})?as?any
}//?使用例子:
//?"-"連字符轉小駝峰
//?\w:0-9a-zA-Z_,表示由數字,大小寫字母和下劃線組成
const?camelizeRE?=?/-(\w)/g
export?const?camelize?=?cacheStringFunction((str:?string):?string?=>?{return?str.replace(camelizeRE,?(_,?c)?=>?(c???c.toUpperCase()?:?''))
})
camelize("text-node")?//?"textNode"//?大寫字母轉"-"連字符
//?\B?是指?非?\B?單詞邊界。
const?hyphenateRE?=?/\B([A-Z])/g;
const?hyphenate?=?cacheStringFunction((str)?=>?str.replace(hyphenateRE,?'-$1').toLowerCase());
hyphenate("WordPress")?//?"word-press"//?首字母轉大寫
const?capitalize?=?cacheStringFunction((str:?string)?=>?str.charAt(0).toUpperCase()?+?str.slice(1)
)
const?toHandlerKey?=?cacheStringFunction((str)?=>?(str???`on${capitalize(str)}`?:?``));
toHandlerKey('click')?//?"onClick"

這個函數和上面 makeMap 函數類似,傳入一個 fn 參數,返回一個包含參數 str 的閉包,將這個 str 字符串作為 key 賦值給一個空對象 cache,閉包返回 cache[str] || (cache[str] = fn(str))。

【cache[str] || (cache[str] = fn(str))】的意思是,如果 cache 有緩存到 str 這個 key,直接返回對應的值,否則,先調用 fn(str),再賦值給 cache[str],這樣可以將需要經過 fn 函數處理的字符串緩存起來,避免多次重復處理字符串。

3.16 hasChanged:判斷值是否有變化

const?hasChanged?=?(value:?any,?oldValue:?any):?boolean?=>!Object.is(value,?oldValue)

Object.is 方法判斷兩個值是否為同一個值。

3.17 ?invokeArrayFns:執行數組里的函數

export?const?invokeArrayFns?=?(fns:?Function[],?arg?:?any)?=>?{for?(let?i?=?0;?i?<?fns.length;?i++)?{fns[i](arg)}
}

這種寫法方便統一執行多個函數。

3.18 def:定義一個不可枚舉的對象

export?const?def?=?(obj:?object,?key:?string?|?symbol,?value:?any)?=>?{Object.defineProperty(obj,?key,?{configurable:?true,enumerable:?false,value})
}

Object.defineProperty,語法:Object.defineProperty(obj, prop, descriptor),它是一個非常重要的 API,經常會在源碼中看見它。

在 ES3 中,除了一些內置屬性(如:Math.PI),對象所有的屬性在任何時候都可以被[修改、插入、刪除。

在ES5 中,我們可以設置屬性是否可以被改變或是被刪除——在這之前,它是內置屬性的特權。

ES5 中引入了屬性描述符的概念,我們可以通過它對所定義的屬性有更大的控制權,這些屬性描述符(特性)包括:value —— 獲取屬性時所返回的值。writable —— 該屬性是否可寫。enumerable —— 該屬性在 for in 循環中是否會被枚舉。configurable —— 該屬性是否可被刪除。set() —— 該屬性的更新操作所調用的函數。get() —— 獲取屬性值時所調用的函數。

另外,數據描述符(其中屬性為:enumerable,configurable,value,writable)與存取描述符(其中屬性為enumerable,configurable,set(),get())之間是有互斥關系的。在定義了set()和get()之后,描述符會認為存取操作已被定義了,其中再定義 value 和 writable 會引起錯誤

3.19 toNumber:轉數字

??export?const?toNumber?=?(val:?any):?any?=>?{const?n?=?parseFloat(val)return?isNaN(n)???val?:?n}

3.20 getGlobalThis:全局對象

let?_globalThis:?any
export?const?getGlobalThis?=?():?any?=>?{return?(_globalThis?||(_globalThis?=typeof?globalThis?!==?'undefined'??globalThis:?typeof?self?!==?'undefined'??self:?typeof?window?!==?'undefined'??window:?typeof?global?!==?'undefined'??global:?{}))
}

第一次調用這個函數時,_globalThis 肯定為 "undefined",接著執行【||】后的語句。

  1. typeof globalThis !== 'undefined' 如果 globalThis 不是 undefined,返回 globalThis:MDN globalThis。否則 ->

  2. typeof self !== 'undefined' 如果 self 不是 undefined,返回 self。否則 ->

  3. typeof window !== 'undefined' 如果 window 不是 undefined,返回 widow。否則 ->

  4. typeof global !== 'undefined' 如果 global 不是 undefined,返回 global。否則 ->

  5. 返回 {}

第二次調用這個函數,就直接返回 _globalThis,不需要第二次繼續判斷了????

4. 感想

  • 很多工具函數可以通過做緩存以達到優化性能的目的

  • Object 對象 API 解析 無論什么時候都不過時,適合反復閱讀,加深對 Object 的理解

  • 工作中如果有用到類似的工具函數,可參考這些寫法

  • 學習了一些 typescript 不太常見的語法:【! 非空斷言操作符】【?? 空值合并運算符】

  • 生成 sourcemap 調試 ts 代碼

最近組建了一個湖南人的前端交流群,如果你是湖南人可以加我微信?ruochuan12?私信 湖南?拉你進群。


推薦閱讀

我在阿里招前端,該怎么幫你(可進面試群)
我讀源碼的經歷

面對 this 指向丟失,尤雨溪在 Vuex 源碼中是怎么處理的
老姚淺談:怎么學JavaScript?

·················?若川簡介?·················

你好,我是若川,畢業于江西高校。現在是一名前端開發“工程師”。寫有《學習源碼整體架構系列》多篇,在知乎、掘金收獲超百萬閱讀。
從2014年起,每年都會寫一篇年度總結,已經寫了7篇,點擊查看年度總結。
同時,活躍在知乎@若川,掘金@若川。致力于分享前端開發經驗,愿景:幫助5年內前端人走向前列。

識別方二維碼加我微信、拉你進源碼共讀

今日話題

略。歡迎分享、收藏、點贊、在看我的公眾號文章~

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

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

相關文章

APK 本地化

一個APK反編譯利器Apktool(android漢化)2010-07-19 18:52轉載自&#xff1a;http://blog.sina.com.cn/s/blog_5752764e0100kv34.html APK 本地化 [http://www.andmoto.com/viewthread.php?tid3873]說起APK的漢化&#xff0c;目前大部分教程都是讓用Hex Workshop或者Android R…

Linux manjaro系統安裝后無法連接wifi,解決方案

2019獨角獸企業重金招聘Python工程師標準>>> 筆記本為聯想 thinkpad E480 首先通過命令lspci -k看一下原因是否為缺少wifi驅動&#xff0c;如下&#xff0c;如果沒有Kernel driver in use&#xff0c;說明缺少驅動。05:00.0 Network controller: Realtek Semiconduc…

檢測輸入路徑是否存在錯誤_為什么存在用戶輸入錯誤

檢測輸入路徑是否存在錯誤Errors are a fact of life when using almost any type of software. Forms are the worst though. Nothing is more frustrating than filling out a form and getting a robotic message from the computer telling you that you have failed, plea…

若川邀你進 源碼共讀 群~長期交流學習

大家好&#xff0c;我是若川。這是一個愉快的周六~估計還是有很多讀者不知道我。若川名字由來是取自&#xff1a;上善若水&#xff0c;海納百川。順便放兩篇文章。我讀源碼的經歷&#xff0c;跟各位讀者朋友分享下公眾號運營策略加我微信進 源碼共讀 群最近組織了近200人每周源…

2005 打開 2010 項目經驗總結

下面是網上的直接復制粘貼&#xff1a;網址為 http://hi.baidu.com/zealot886/blog/item/7364d4266a2a1555ac34dea6.html/cmtid/65ff140a660e02246159f3db 這里是我自己的總結 &#xff08; 1、用vs2010 將該解決方案的所有 項目都改為 net 2.0&#xff08;方法&#xff0c;右擊…

讀取linux的運行狀態,Linux下安裝使用sar工具來獲取系統運行狀態

sar 找出系統瓶頸的利器sar是System Activity Reporter(系統活動情況報告)的縮寫。sar工具將對系統當前的狀態進行取樣&#xff0c;然后通過計算數據和比例來表達系統的當前運行狀態。它的 特點是可以連續對系統取樣&#xff0c;獲得大量的取樣數據&#xff1b;取樣數據和分析的…

說說 Spring 的事務同步管理器

Spring 將 JDBC 的 Connection、Hibernate 的 Session 等訪問數據庫的連接或者會話對象統稱為資源&#xff0c;這些資源在同一時刻是不能多線程共享的 。 為了讓 DAO 或 Service 類可以實現單例模式&#xff0c; Spring 的事務同步管理類 org.springframework.transaction.supp…

錯過校招_我們在用戶測試中容易錯過的事情

錯過校招What makes a tool well designed? As a designer, I’ve thought about this question for a long time, and over the past few years I’ve developed a system that I now use with every new project I approach, from small startups to large companies like L…

這些 JS 中強大的操作符,總有幾個你沒聽說過

大家好&#xff0c;我是若川。今天推薦一篇相對簡單些的文章。大家應該都知道了我最近組織了源碼共讀活動&#xff0c; 有小伙伴表示讀源碼上癮&#xff0c;也很有收獲。工作0-5年都可以參與。感興趣可以加我微信 ruochuan12 私信 源碼 進群。1. 數值分割符 _2. 逗號運算符 ,3.…

Class 創建性能大比拼(反射,泛型反射,泛型創建,緩存Emit,非緩存Emit)

一說到反射&#xff0c;很多人都想到了性能&#xff0c;更有甚者直接說“慎用反射&#xff0c;遺患無窮”&#xff0c;“用反射&#xff0c;感覺怎么像是退步啊&#xff5e;”&#xff0c;看到這種言論&#xff0c;直接把反射妖魔化了&#xff0c;如果這種言論長此以往&#xf…

es6沖刺01

1、let/const 1)作用域&#xff1a;es5中有全局作用域、函數作用域。es6中新增了塊級作用域 2&#xff09;let定義的變量在所在塊級作用域外失效&#xff0c;嚴格模式下失效后直接報錯&#xff0c; 且不允許重復聲明同名變量 3)const用于聲明常量&#xff0c;聲明時必須賦值&am…

linux網卡固件名,修改CentOS7網卡名稱為傳統名稱eth0格式

使用CentOS7以前系統的小伙伴裝完CentOS7以后發現了一個問題&#xff0c;那就是網卡名改變為了“en016777736”&#xff0c;而不是以前的eth0的簡易模式了&#xff0c;如圖&#xff1a;以往的CentOS7以前的系統網卡命名雖然簡單方便&#xff0c;但也會帶來一些問題&#xff0c;…

Baymard Institute:基于UX的最佳實踐的光榮的,循證的工具

重點 (Top highlight)I realized I wanted to write this piece when I mentioned the Baymard Institute to a User Researcher with 10 years of experience and they had no idea what I was talking about. They aren’t alone! I’ve gotten plenty of raised eyebrows on…

Vue 3.2 發布了,那尤雨溪是怎么發布 Vue.js 的?

1. 前言大家好&#xff0c;我是若川。最近組織了源碼共讀活動&#xff0c;感興趣的可以加我微信 ruochuan12&#xff0c;長期交流學習。之前寫的《學習源碼整體架構系列》 包含jQuery、underscore、lodash、vuex、sentry、axios、redux、koa、vue-devtools、vuex4十篇源碼文章。…

wireshark使用教程 linux,Linux入門教程:ubuntu下安裝wireshark(以及配置非root),這個強大的工具可以捕...

Linux入門教程:ubuntu下安裝wireshark(以及配置非root),這個強大的工具可以捕Wireshark是世界上最流行的網絡分析工具。這個強大的工具可以捕捉網絡中的數據&#xff0c;并為用戶提供關于網絡和上層協議的各種信息。與很多其他網絡工具一樣&#xff0c;Wireshark也使用pcap net…

IronPython和C#執行速度對比

其實我自己對執行速度這個問題本來并沒有什么興趣&#xff0c;因為以前的經驗告訴我&#xff1a;除非是運算密集型的程序&#xff0c;否則腳本語言和編譯型語言使用起來速度沒有多大差別。但是我們公司有個人知道我的想法以后&#xff0c;天天在我耳邊嚷嚷腳本運行速度太慢&…

基于超級賬本Fabric的供應鏈跟蹤解決方案【開源】

2019獨角獸企業重金招聘Python工程師標準>>> 本項目為基于Hyperledger Fabric區塊鏈的供應鏈資產跟蹤解決方案&#xff0c;項目主要包括鏈碼和Web應用兩部分。Fabric鏈碼采用GOLANG開發&#xff0c;負責維護資產的狀態&#xff0c;后臺為采用Node.js開發的Web應用&a…

同理心案例及故事分享_神經形態,視覺可及性和同理心

同理心案例及故事分享“A good UX designer has empathy”.“優秀的UX設計人員具有同理心”。 This is something every UX designer has heard at some point in their career. Empathy helps us get into the mindset of the user and build solutions that solve real probl…

純CSS實現beautiful按鈕

大家好&#xff0c;我是若川。邀你進源碼共讀群學習交流。今天分享一篇好文。可收藏&#xff5e;近期工作中遇到一個需求——實現一些酷炫的按鈕&#xff0c;看到效果圖之后&#xff0c;按鈕確實漂亮&#xff0c;有彈跳、顏色漸變、掃光、霓虹燈&#xff0c;瞬間激起了我的好奇…

linux的內核有多小,Linux 內核有小bug?

今天讀著讀著Linux代碼&#xff0c;竟然無意中發現Linux 0.11內核有個小bug&#xff0c;呵呵&#xff0c;人非圣賢孰能無過。// 在目錄項數據塊中搜索匹配指定文件名的目錄項&#xff0c;首先讓de 指向數據塊&#xff0c;并在不超過目錄中目錄項數// 的條件下&#xff0c;循環執…