lodash
基本上成爲了寫 javascript 工具庫的標配,它普遍應用在各類服務端以及前端應用中,可是它的包體積略大了一些。對於服務端來講,包的體積並非十分的重要,或者換句話說,不像前端那樣對包的體積特別敏感,一分一毫都會影響頁面打開的性能,從而影響用戶體驗。javascript
正由於前端包體積對於用戶體驗的重要性,所以有各類各樣減少包體積的方法。針對 lodash
來講,你徹底沒必要要引入 lodash
的全部工具函數,你只須要按需引入或者直接使用單函數包。關於按需引入你能夠參考如下文章前端
在針對個人我的站點中的 lodash
進行優化時,若是沒記錯的話,lodash
從之前 gzip 後的 80KB
變爲了 20KB
,相對來講仍是比較大。而當我全局搜索了 lodash 的引用以後,發現 90% 的場景都是在使用 _.get
。git
另外,隨着 ES6+
的發展,以及瀏覽器與 Node 對它的支持,不少 lodash
的函數都很容易本身來實現或者說已被實現,如 _.assign
,_.trim
,_.startsWith
等等已被 ES6+ 實現,而 _.uniq
又很容易經過 new Set()
來解決。有人就在 github 上總結了 you-dont-need/You-Dont-Need-Lodash-Underscore,其中囊括了不少工具函數很簡易的實現。github
鑑於本站點就是我做爲試驗田用來實踐各類技術,因而我決定本身來實現 lodash
的一些工具函數。get
與 merge
兩個函數在我使用時比較多,且相對來講比較複雜一些,這裏貼一下個人實現代碼。數組
本文地址: shanyue.tech/post/lodash…瀏覽器
在 js 中常常會出現嵌套調用這種狀況,如 a.b.c.d.e
,可是這麼寫很容易拋出異常。你須要這麼寫 a && a.b && a.b.c && a.b.c.d && a.b.c.d.e
,可是顯得有些囉嗦與冗長了。特別是在 graphql 中,這種嵌套調用更是難以免。性能優化
這時就須要一個 get
函數,使用 get(a, 'b.c.d.e')
簡單清晰,而且容錯性提升了不少。如下是須要經過的幾個測試用例服務器
get({ a: null }, 'a.b.c', 3)
// output: 3
get({ a: undefined }, 'a', 3)
// output: 3
get({ a: null }, 'a', 3)
// output: 3
get({ a: [{ b: 1 }]}, 'a[0].b', 3)
// output: 1
複製代碼
path
中也多是數組的路徑,所有轉化成 .
運算符並組成數組less
// a[3].b -> a.3.b
const paths = path.replace(/\[(\d+)\]/g, '.$1').split('.')
複製代碼
而後層層迭代屬性便可,另外注意 null
與 undefined
取屬性會報錯,因此使用 Object
包裝一下。
function get (source, path, defaultValue = undefined) {
// a[3].b -> a.3.b
const paths = path.replace(/\[(\d+)\]/g, '.$1').split('.')
let result = source
for (const p of paths) {
result = Object(result)[p]
if (result === undefined) {
return defaultValue
}
}
return result
}
複製代碼
merge
用來遞歸合併對象,至關於深層的 Object.assign
。在 graphql
中會普遍用到 merge
,如會常用 merge
來合併全部的 resolver
,特別是 Mutation
以下示例
const rootResolver = {
Query: {
},
Mutation: {
login () {}
}
}
const userResolver = {
User: {
createUser() {}
}
}
const resolver = merge(rootResolver, userResolver)
// output
// {
// Query: {},
// Mutation: {
// login () {},
// createUser () {}
// }
// }
複製代碼
另外,在前端進行 graphql 的查詢時也常常須要使用到 merge
。如在進行頁面的性能優化時,爲了不一個 Query 耗時太久,頁面渲染過於耗時,會拆成兩個 Query,先渲染響應快的數據,在慢慢等待個別響應慢的數據。
如下是一個關於我的主頁信息的 Query,可是其中有一個字段 dataNeedDelay3s
會在服務器耗時許久,會由於此字段加大了用戶的等待時間,形成不友好的用戶體驗。此時會把此字段單獨拆掉,優先渲染其它我的信息。
query PROFILE {
me {
id
age
name
# 須要耗時3s的字段
dataNeedDelay3s
}
}
# 拆爲如下兩個茶軒
query PROFILE_ONE {
me {
id
age
name
}
}
query PROFILE_TWO {
me {
dataNeedDelay3s
}
}
複製代碼
此時就有 merge
的需求,查詢完成後把兩次查詢結果給拼到一塊兒。
關於拆 graphql 的 Query 的需求無處不在,如在服務端渲染時,須要把權限資源與非權限資源分開。
這裏講述下如何實現 merge
function isObject (value) {
const type = typeof value
return value !== null && (type === 'object' || type === 'function')
}
// { a: [{ b: 2 }] } { a: [{ c: 2 }]} -> { a: [{b:2}, {c:2}]}
// merge({o: {a: 3}}, {o: {b:4}}) => {o: {a:3, b:4}}
function merge (source, other) {
if (!isObject(source) || !isObject(other)) {
return other === undefined ? source : other
}
// 合併兩個對象的 key,另外要區分數組的初始值爲 []
return Object.keys({
...source,
...other
}).reduce((acc, key) => {
// 遞歸合併 value
acc[key] = merge(source[key], other[key])
return acc
}, Array.isArray(source) ? [] : {})
}
複製代碼