首頁(yè)

前英語(yǔ)流利說(shuō)設(shè)計(jì)總監(jiān):從民房到納斯達(dá)克的理想主義設(shè)計(jì)路

資深UI設(shè)計(jì)者

可能對(duì)于一些人來(lái)說(shuō),流利說(shuō)是一份工作,而對(duì)于我來(lái)說(shuō),流利說(shuō)卻是一段深刻的旅程,改變了我的生活,也塑造了我的性情、人格。從 2013 年作為第 7 號(hào)員工加入流利說(shuō),為之效力6年,從最早期民房創(chuàng)業(yè),到納斯達(dá)克 IPO,這段經(jīng)歷,我自認(rèn)為頗具一個(gè)理想主義者的傳奇色彩,有些故事,是我愿意,也值得分享的。

老王(流利說(shuō) CEO 王翌)以前總借他恩師的話(huà)說(shuō),人這一輩子能做一兩件漂亮事就不錯(cuò)了。我覺(jué)得,流利說(shuō),算我過(guò)去做的一件漂亮事。以前,我也常和同事說(shuō),如果你現(xiàn)在一切的經(jīng)歷,在日后不足以用故事說(shuō)與人聽(tīng),可能你現(xiàn)在經(jīng)歷的還不夠痛,你還不夠刻苦。所幸,過(guò)去的經(jīng)歷,給我留下了幾個(gè)故事。

這一次,我想分享當(dāng)初選擇加入流利說(shuō)的故事,分享這個(gè)過(guò)程中我的思考、行動(dòng)。一方面,單純的想記錄這段故事。因?yàn)殡S著年歲增加,記性卻是退減的。文字是最好的保存記憶的方式;另一方面,常常遇到設(shè)計(jì)師朋友們聊如何選擇工作機(jī)會(huì),遇到創(chuàng)業(yè)的邀約機(jī)會(huì)怎么判斷、決策的問(wèn)題,我希望這個(gè)故事,能給遇到此類(lèi)問(wèn)題的朋友一些啟發(fā)。

好,聽(tīng)故事吧。

緣起

2012 年,我退出了聯(lián)合創(chuàng)立一年半的公司。而在一年半以前,我從阿里云公司離開(kāi),放棄了一筆可觀(guān)的 RSU 股票,當(dāng)時(shí),我是阿里云最早的 28 位設(shè)計(jì)師之一。結(jié)束創(chuàng)業(yè)后,2012 年 11 月 14 日 19:17,我發(fā)了一條微博,表示想看看新的機(jī)會(huì)。這條微博,有 16 個(gè)轉(zhuǎn)發(fā)。其中有 1 個(gè)轉(zhuǎn)發(fā),被流利說(shuō)聯(lián)合創(chuàng)始人 Ben 看到,他把這條微博轉(zhuǎn)發(fā)給了聯(lián)合創(chuàng)始人王翌(流利說(shuō) CEO)。

在此之前,我和 Ben、王翌素不相識(shí),網(wǎng)友都談不上。Ben 之所以看到那條轉(zhuǎn)發(fā),是因?yàn)樗P(guān)注好友里面,有一位正是我一款瀏覽器插件產(chǎn)品 – 微博急簡(jiǎn) 的用戶(hù)。

有一顆創(chuàng)作的心 – 微博急簡(jiǎn)

△ 使用「微博急簡(jiǎn)」前后,微博主頁(yè)的對(duì)比

容我多說(shuō)幾句,介紹一下微博急簡(jiǎn)這款產(chǎn)品。2011 年左右,新浪微博的使用體驗(yàn),非常糟糕,逐步商業(yè)化帶來(lái)的各種廣告,讓原本不好的體驗(yàn),變得更加讓我無(wú)法容忍。很快,一個(gè)叫 stylish 的 Chrome 插件工具,在微博設(shè)計(jì)/技術(shù)圈子里流傳。用戶(hù)安裝 stylish 插件后,通過(guò)修改 CSS 來(lái)定制自己的微博體驗(yàn)。同時(shí),你還可以把 CSS 分享給其他用戶(hù)。

這太好玩了!我按自己的使用喜好,給自己訂制了一套極簡(jiǎn)體驗(yàn)的新浪微博。玩了幾天后,我決定做一個(gè)改善微博體驗(yàn)的瀏覽器插件。為什么要重新做一個(gè)插件呢?我認(rèn)為,stylish 的使用門(mén)檻、操作成本都太高,僅僅是專(zhuān)業(yè)人士的玩具。我希望普通人,也可以通過(guò)一鍵安裝瀏覽器插件,獲得舒服的微博體驗(yàn)。因此,我給插件取名 – 微博急簡(jiǎn)??吹贸鰜?lái),我是多么急迫的想簡(jiǎn)化微博的體驗(yàn)。

兩周后,微博急簡(jiǎn)就上線(xiàn)了。幾個(gè)版本后,體驗(yàn)就趨于穩(wěn)定。高峰的時(shí)候,有 10 萬(wàn)左右的用戶(hù)使用,相關(guān)微博話(huà)題有 600 萬(wàn)。有很多行業(yè)內(nèi)的大咖成為我的用戶(hù),像馮大輝、少楠、方軍等。獲得了很多用戶(hù)的好評(píng),最讓我嘚瑟的是現(xiàn)任丁香園產(chǎn)品總監(jiān)少楠的評(píng)價(jià):用一個(gè)插件秒殺了新浪UED團(tuán)隊(duì)。

通過(guò)微博急簡(jiǎn)這個(gè)產(chǎn)品,我想分享以下幾點(diǎn):

  • 設(shè)計(jì)師應(yīng)該對(duì)體驗(yàn)保持敏感度。不要被體驗(yàn)糟糕的產(chǎn)品破壞了味蕾;
  • 如果有機(jī)會(huì),就要試圖去改變它;
  • 出于興趣,單純的為自己做設(shè)計(jì)、創(chuàng)作。即使沒(méi)有金錢(qián)回報(bào);

這三點(diǎn),既是我的觀(guān)點(diǎn),也是我的特質(zhì)。正是因?yàn)檫@樣的特質(zhì),才遇到后面加入流利說(shuō)的契機(jī)。

面對(duì)首次邀約

回到故事主線(xiàn)。

我發(fā)完那條微博后的 1 小時(shí),王翌就給了我微博私信。老實(shí)說(shuō),今天是我第一次注意到這個(gè)「相隔 1 小時(shí)」的時(shí)間細(xì)節(jié)。王翌的行動(dòng)力、執(zhí)行力,對(duì)于人才的執(zhí)著追求,著實(shí)讓我佩服。這不僅僅是對(duì)我,對(duì)流利說(shuō)早期的員工,以及后面的核心員工,都是如此。

他的消息里,有三個(gè)關(guān)鍵詞:exciting、團(tuán)隊(duì)成立 2 個(gè)月、移動(dòng)教育。對(duì)于這條消息,我禮貌的回應(yīng),但內(nèi)心其實(shí)是「呵呵」的。即使,我看到他 LinkedIn 主頁(yè)上 Google PM 的經(jīng)歷,很亮眼。呵呵的原因是,這個(gè)公司太早期了,才兩個(gè)月。2013 年,移動(dòng)教育是啥東西?而且,創(chuàng)始人還這么不務(wù)實(shí),動(dòng)不動(dòng)就標(biāo)榜是一個(gè) exciting 的機(jī)會(huì)。

雖然,王翌后續(xù)一直聯(lián)系我,但我基本是忽略的狀態(tài)。期間,我短暫的加入了一個(gè)朋友的創(chuàng)業(yè)公司。2013 年 1 月底,春節(jié)前的幾天,我再次收到王翌的私信:流利說(shuō) App 即將上架,想約我再聊一聊。于是,我們約在文二西路白鴉(有贊創(chuàng)始人)的貝塔咖啡。

?

△ 貝塔咖啡館

那天晚上,王翌給我展示了流利說(shuō)最早的 App,程序其實(shí)還不太穩(wěn)定。但他仍舊極具信心的表達(dá)了對(duì)于語(yǔ)音互動(dòng)的看好,以及發(fā)出盛情邀請(qǐng)。我對(duì)這個(gè) App 的產(chǎn)品與交互的第一印象,是好的,但對(duì)于王翌的第一印象卻是復(fù)雜的,既覺(jué)得這人有激情、有想法,同時(shí)又覺(jué)得不太靠得住,夸夸其談,他太會(huì)說(shuō)了。

如何設(shè)計(jì)深色模式?這3點(diǎn)因素需要考慮

資深UI設(shè)計(jì)者

深色模式該從何處著手設(shè)計(jì)又要考慮哪些因素?一起來(lái)看看~

其實(shí)回顧我們常用的APP,有很多都更新了深色模式,而且每個(gè)APP對(duì)深色的定義和設(shè)計(jì)都有差異。

實(shí)際上深色模式已經(jīng)來(lái)臨,而且在很多產(chǎn)品中都能發(fā)現(xiàn)它的身影,之后也會(huì)愈加流行。那么設(shè)計(jì)師面對(duì)深色模式,該從何處著手設(shè)計(jì)又要考慮哪些因素呢?

本文就為大家提供一份全面的總結(jié)。文章目錄如下:

選擇深色模式的原因

1. 需求趨勢(shì)

過(guò)去一年以來(lái),Android 10和iOS 13上都適配了深色模式,而且Apple和Google也一直致力于將資源和注意力投入到深色模式中,這也讓深色模式備受用戶(hù)的關(guān)注。

2. 專(zhuān)注內(nèi)容

深色模式在弱光環(huán)境下具有更好的可讀性,讓我們更專(zhuān)注于眼前的屏幕。同時(shí)深色的背景會(huì)降低內(nèi)容周?chē)氐挠绊?,特別是以圖片和視頻為主的應(yīng)用,讓用戶(hù)更專(zhuān)注于內(nèi)容。

作為內(nèi)容消費(fèi)型應(yīng)用的Netflix ,把深色背景作為默認(rèn)設(shè)計(jì)樣式,深色的設(shè)計(jì)讓用戶(hù)更能集中注意力,延長(zhǎng)使用時(shí)間。

3. 減輕刺激

相對(duì)于其他顏色,深色系的設(shè)計(jì)在夜晚看著最舒服。可能晚上玩手機(jī)不用擔(dān)心光線(xiàn)太刺眼,但是深色模式對(duì)護(hù)眼并沒(méi)有什么幫助,只能說(shuō)可以減少對(duì)眼睛的刺激。

4. 提高續(xù)航

深色模式更省電只適用于OLED屏幕。OLED面板的每個(gè)像素點(diǎn)可以單獨(dú)發(fā)光,當(dāng)使用深色模式時(shí),部分像素點(diǎn)被熄滅,只點(diǎn)亮部分像素,屏幕的一部分相當(dāng)于處在休眠狀態(tài),所以會(huì)更加省電。

平臺(tái)設(shè)計(jì)指南

1. iOS平臺(tái)深色模式設(shè)計(jì)

在深色模式下,Apple重新審視了iOS中UI樣式和顏色的含義,讓我們來(lái)看看在iOS上設(shè)計(jì)深色模式帶來(lái)的變化。

語(yǔ)義化顏色(Semantic Colors)

所謂語(yǔ)義化顏色,就是不再通過(guò)某一固定的RGB色值來(lái)描述顏色,而是根據(jù)用途來(lái)描述,讓界面元素可以自動(dòng)適配當(dāng)前的外觀(guān)模式。

淘寶團(tuán)隊(duì)就參考了蘋(píng)果官方的適配建議,通過(guò)語(yǔ)義化顏色的方式進(jìn)行適配,使適配成本大幅降低。設(shè)計(jì)師根據(jù)不同UI元素的特性先期制定顏色語(yǔ)義化規(guī)則,進(jìn)而技術(shù)在框架層面通過(guò)「顏色自動(dòng)反轉(zhuǎn)」技術(shù)實(shí)現(xiàn)顏色反轉(zhuǎn)。

系統(tǒng)顏色

除了語(yǔ)義化顏色,Apple還提供了9種預(yù)定義的系統(tǒng)顏色,在淺色和深色模式中,這些顏色會(huì)動(dòng)態(tài)變化,支持整個(gè)系統(tǒng)的外觀(guān),同樣也可以自適應(yīng)選定的界面樣式。

模糊與動(dòng)態(tài)效果

在iOS13上,蘋(píng)果引入了4種模糊效果和8種動(dòng)態(tài)效果,它們自動(dòng)適應(yīng)iOS界面樣式。這是在淺色和深色模式下不同的模糊效果。

蘋(píng)果還在iOS深色模式排版套件中引入4種動(dòng)態(tài)效果,其中3種為疊加效果,1種分隔效果。

2. Android平臺(tái)深色模式設(shè)計(jì)

谷歌提供了廣泛的文檔支持,幫助設(shè)計(jì)師了解深色主題如何在A(yíng)ndroid生態(tài)系統(tǒng)中運(yùn)行。

Elevation(陰影)

UI界面元素間的投影最能讓用戶(hù)清晰地感知用戶(hù)界面的深度。在設(shè)計(jì)深色主題時(shí),組件將保留與淺色主題相同的默認(rèn)陰影組件。Elevation越靠上, 顏色就會(huì)越淺。

無(wú)障礙性與對(duì)比

深色UI設(shè)計(jì)中的背景應(yīng)足夠暗以顯示白色文本。設(shè)計(jì)師要注意背景和文字之間至少使用15.8:1的對(duì)比度。這樣可以確保將正文放在最前面時(shí),能通過(guò)WCAG(Web內(nèi)容無(wú)障礙指南,使網(wǎng)站內(nèi)容更容易訪(fǎng)問(wèn))的AA標(biāo)準(zhǔn)。

顏色

深色模式必須避免飽和的顏色,以免引起眼睛疲勞。相反,設(shè)計(jì)師應(yīng)專(zhuān)注于使用不飽和的顏色,以增加清晰度。主色和輔色的選擇還取決于對(duì)淺色和深色UI主題的考慮。

文字不透明度

在深色背景上設(shè)計(jì)淺色文字時(shí),高度強(qiáng)調(diào)的文字不透明度為87%;一般提示文字的不透明度為60%;禁用文字的不透明度為38%。

深色模式的設(shè)計(jì)要點(diǎn)

蘋(píng)果和谷歌都利用各自的設(shè)計(jì)原則,為深色模式設(shè)計(jì)做準(zhǔn)備工作。在實(shí)際設(shè)計(jì)過(guò)程中,不單需要這些基本原則,更重要的是要注意設(shè)計(jì)深色模式的實(shí)用要點(diǎn)。

1. 背景灰度

設(shè)計(jì)深色背景時(shí)不是簡(jiǎn)單的把白變成黑,而是對(duì)背景使用比較暗的色調(diào),以減少眼睛疲勞。

在淺色模式中,我們傾向于用細(xì)微的陰影來(lái)傳達(dá)界面深度,使用起來(lái)更加自然。但是在大多數(shù)深色模式下,陰影的效果并不明顯,通常用顏色的深淺來(lái)傳達(dá)界面的層級(jí)關(guān)系。

關(guān)鍵點(diǎn):注意應(yīng)用場(chǎng)景

在知乎的深色模式中,背景的設(shè)計(jì)從深到淺使用了三級(jí)灰度,讓頁(yè)面的層級(jí)更分明。

一級(jí)灰度的應(yīng)用場(chǎng)景主要是大的背景色,使用面積相對(duì)比較大顏色最深;二級(jí)灰度的應(yīng)用場(chǎng)景是選項(xiàng)的背景色,根據(jù)選項(xiàng)的數(shù)量設(shè)置使用面積,位置排布比較靈活;三級(jí)灰度的顏色最淺,使用面積最小,通常用在分割線(xiàn)中。

2. 文字對(duì)比

白底黑字和黑底白字帶給我們的用眼體驗(yàn)是不一樣的。設(shè)計(jì)不當(dāng)?shù)纳钌J匠3R驗(yàn)閺?qiáng)對(duì)比而變得很刺眼,同時(shí)為了提高對(duì)光線(xiàn)的吸收虹膜會(huì)張得更開(kāi),更容易造成眼部疲勞。

關(guān)鍵點(diǎn):文字間的對(duì)比

深色模式中,文字的用色通常是純灰色,不同位置的文字例如標(biāo)題、正文和注釋使用深淺不同的顏色作對(duì)比。上圖是深色的微信,就利用這種方法來(lái)區(qū)分不同文字內(nèi)容,展示文字層次關(guān)系。

另外每個(gè)應(yīng)用的定位都不一樣,界面中想傳達(dá)的信息也有差異,所以要注意不同的設(shè)計(jì)思路。

關(guān)鍵點(diǎn):文字與背景的對(duì)比

已經(jīng)更新深色模式的應(yīng)用主要分為兩大類(lèi),一類(lèi)屬于工具型應(yīng)用例如QQ、微信、百度網(wǎng)盤(pán)等,這類(lèi)應(yīng)用追求的是信息的有效傳達(dá),在設(shè)計(jì)時(shí)文字內(nèi)容和背景色的區(qū)分比較明顯。

上圖是百度網(wǎng)盤(pán)的深色模式,可以看出來(lái)標(biāo)題文字與背景有很明顯的對(duì)比,保障了用戶(hù)使用時(shí)的可操作性和易讀性。

這樣的設(shè)計(jì)不需要用戶(hù)過(guò)于沉浸式的閱讀,只需要幫助用戶(hù)快速找到有用的信息并方便使用,這是工具型應(yīng)用在設(shè)計(jì)深色模式時(shí)必備的原則。

另一類(lèi)屬于內(nèi)容型應(yīng)用例如知乎、簡(jiǎn)書(shū)等,這些應(yīng)用更注重沉浸式的閱讀體驗(yàn),因?yàn)橛脩?hù)通常會(huì)在某個(gè)界面中停留很久來(lái)查看內(nèi)容,所以需要文字與背景的低對(duì)比度為閱讀營(yíng)造柔和的氛圍。

簡(jiǎn)書(shū)的深色模式中,文字與背景的對(duì)比關(guān)系就設(shè)計(jì)得很弱,整個(gè)界面呈現(xiàn)出灰色調(diào),這樣的設(shè)計(jì)有助于在弱光環(huán)境下的長(zhǎng)時(shí)間閱讀和瀏覽。

3. 圖標(biāo)/按鈕

深色模式應(yīng)該避免使用特別鮮艷的顏色,較高的明度和飽和度會(huì)與深色背景形成強(qiáng)烈的對(duì)比,讓頁(yè)面的可讀性變差并加深刺激。

關(guān)鍵點(diǎn):降低色彩明度

在由淺變深的過(guò)程中,知乎對(duì)改變了界面中所有圖標(biāo)的顏色。界面里面的圖標(biāo)和主題按鈕的色彩,在色相、飽和度上都沒(méi)有變化,但是明度被不同程度的降低,保證了在不同光照條件下的內(nèi)容的可讀性。

這是深色模式中處理色彩的一種方法,雖然在淺色界面中,我們更喜歡鮮艷的顏色,但明度低的顏色更適合深色主題。匹配這兩個(gè)模式另一個(gè)比較好的方法是創(chuàng)建互補(bǔ)的色板。

結(jié)論

無(wú)論深色或者淺色,都只是產(chǎn)品向用戶(hù)呈現(xiàn)的一種界面狀態(tài),最終的目的是為了更良好的使用體驗(yàn)。

不管選擇什么樣的模式,都要記得從產(chǎn)品自身出發(fā),并牢記這幾點(diǎn):

  • 了解趨勢(shì):熟悉深色模式流行起來(lái)的原因,以及蘋(píng)果和谷歌對(duì)此的相關(guān)研究。
  • 找對(duì)方向:在準(zhǔn)備設(shè)計(jì)深色模式前,要先了解清楚產(chǎn)品對(duì)應(yīng)的平臺(tái)、滿(mǎn)足的標(biāo)準(zhǔn)。

  • 掌握要點(diǎn):設(shè)計(jì)深色模式更多要求的是顏色上的變化,利用灰度色階拉開(kāi)背景顏色,把握文字與背景間的強(qiáng)弱關(guān)系,適當(dāng)降低圖標(biāo)的明度和飽和度。

文章來(lái)源:優(yōu)設(shè)    作者:Clip設(shè)計(jì)夾

設(shè)計(jì)手記:給設(shè)計(jì)師參考會(huì)不會(huì)限制設(shè)計(jì)師的思維呢?

藍(lán)藍(lán)設(shè)計(jì)的小編

藍(lán)藍(lán)設(shè)計(jì) 魏華寫(xiě)

     在承擔(dān)ui設(shè)計(jì)項(xiàng)目中,常常碰到一些客戶(hù)不給設(shè)計(jì)師看己往的軟件或本公司設(shè)計(jì)師設(shè)計(jì)但沒(méi)有被客戶(hù)認(rèn)可的設(shè)計(jì)方案,怕限制設(shè)計(jì)師的思路。那究竟給設(shè)計(jì)師參考會(huì)不會(huì)限制設(shè)計(jì)師的思維呢?


     實(shí)際上不會(huì)的,這是更多維度了解客戶(hù)的思考和角度,排除己被嘗試過(guò)的選項(xiàng)。


     在進(jìn)行一個(gè)ui 設(shè)計(jì)前,深度方面應(yīng)該要了解業(yè)務(wù),用戶(hù)角色,環(huán)境生態(tài),交互流程,關(guān)鍵功能,核心價(jià)值,用戶(hù)體驗(yàn)的峰值點(diǎn)和難點(diǎn)在哪里…….總之了解的越深越好。


     寬度方面,要看到競(jìng)品分析,行業(yè)趨勢(shì),國(guó)內(nèi)外優(yōu)秀案例欣賞,專(zhuān)業(yè)文章觀(guān)點(diǎn),應(yīng)該是可以拓展設(shè)計(jì)師的思維。


     設(shè)計(jì)最核心的目標(biāo)應(yīng)該在于解決問(wèn)題,而不是單純的讓界面好看。


      優(yōu)秀的設(shè)計(jì)師要明確解決問(wèn)題的目標(biāo),博采眾長(zhǎng),獨(dú)立思考,看眾多的競(jìng)品是看眾多的解題思路,多方位角度看問(wèn)題。各產(chǎn)品資源,核心技術(shù)不同,取舍不同,理解信息架框,運(yùn)營(yíng)思路,用戶(hù)特征,技術(shù)實(shí)現(xiàn)可能性,不會(huì)只照著您給的資料比貓畫(huà)虎。


       因此,我認(rèn)為,放心的給設(shè)計(jì)師參考資料吧,互相的了解,溝通越多,越容易出好的作品。



這五張圖是最近藍(lán)藍(lán)設(shè)計(jì)的稿件,展現(xiàn)從一個(gè)模糊的概念性需求到一個(gè)可視化概念性方案的設(shè)計(jì)過(guò)程。

    點(diǎn)擊查看原圖

點(diǎn)擊查看原圖


點(diǎn)擊查看原圖

2看文章.png點(diǎn)擊查看原圖

循環(huán)設(shè)計(jì),用戶(hù)體驗(yàn)如何呼喚時(shí)代變革

ui設(shè)計(jì)分享達(dá)人

關(guān)于循環(huán)設(shè)計(jì),可持續(xù)發(fā)展是商業(yè)領(lǐng)域非常關(guān)注的話(huà)題,作為UX需提前轉(zhuǎn)變思維,給企業(yè)帶來(lái)更多價(jià)值,一線(xiàn)大廠(chǎng)已在運(yùn)用這種思維


本文共 3589 字,預(yù)計(jì)閱讀 10 分鐘

譯者推薦|本文從“可持續(xù)”和“設(shè)計(jì)”的兩點(diǎn)談起,來(lái)論述從線(xiàn)性經(jīng)濟(jì)向可持續(xù)經(jīng)濟(jì)的轉(zhuǎn)變,以及“可持續(xù)設(shè)計(jì)”的四個(gè)主要階段:理解、定義、制造、發(fā)布。

“循環(huán)設(shè)計(jì)”不是為了追求時(shí)髦或者抬升設(shè)計(jì)地位,而是將這個(gè)已經(jīng)日益庸俗化的“設(shè)計(jì)”冠為自己的定語(yǔ),是設(shè)計(jì)本身發(fā)展所趨,以及可持續(xù)發(fā)展所需,設(shè)計(jì)界需要對(duì)自己的責(zé)任有所承擔(dān),形成一個(gè)全局觀(guān)、系統(tǒng)性看待設(shè)計(jì)問(wèn)題的方式。讓回收利用和可持續(xù)發(fā)展成為一種規(guī)范,從而做到一勞永逸。

我們生活在一個(gè)呼喚變革的世界。在過(guò)去的50年中,現(xiàn)代社會(huì)所依賴(lài)的漫不經(jīng)心和無(wú)休止的消費(fèi)是不可持續(xù)的。我們從小就不關(guān)心自己的事情。如果有什么東西壞了,我們也就不修了。產(chǎn)品被設(shè)計(jì)成用完直接丟棄,而不是去修復(fù)。數(shù)字產(chǎn)品也不例外。然而,為了解決這些問(wèn)題,出現(xiàn)了一種新的思維方式:循環(huán)設(shè)計(jì)(可持續(xù)設(shè)計(jì))①。(益達(dá)說(shuō):其實(shí)這個(gè)理念和風(fēng)格已經(jīng)存在了很長(zhǎng)的時(shí)間,大多應(yīng)用在不為大眾所知的能源、材料再生流程之中,然而隨著時(shí)代的發(fā)展,循環(huán)設(shè)計(jì)可以變得更加普世。)

①注:循環(huán)設(shè)計(jì)是20世紀(jì)80-90年代產(chǎn)生的一種設(shè)計(jì)風(fēng)格,他又稱(chēng)回收設(shè)計(jì),是指實(shí)現(xiàn)廣義收回和利用的方法,即在進(jìn)行產(chǎn)品設(shè)計(jì)時(shí),充分考慮產(chǎn)品零部件及材料回收的可能性,回收價(jià)值的大致方法,回收處理結(jié)構(gòu)工藝性等于與回收有關(guān)的一系列問(wèn)題,以達(dá)到零部件及材料資源和能源的再利用。它旨在通過(guò)設(shè)計(jì)來(lái)節(jié)約能源和材料,減少對(duì)環(huán)境的污染,使人類(lèi)的設(shè)計(jì)物能多次反復(fù)利用,形成產(chǎn)品設(shè)計(jì)和使用的良性循環(huán)。

那么,循環(huán)設(shè)計(jì)方法意味著什么?在數(shù)字產(chǎn)品上要如何使用?在回答這些問(wèn)題之前,首先,我們需要仔細(xì)觀(guān)察我們是如何構(gòu)建我們的世界,為什么這個(gè)世界已經(jīng)不可持續(xù)了,并且要理解環(huán)保世界的需求是如何改變我們的思維方式,促使我們渴望從線(xiàn)性設(shè)計(jì)模型轉(zhuǎn)變?yōu)檠h(huán)設(shè)計(jì)模型。


向循環(huán)轉(zhuǎn)變


我們的經(jīng)濟(jì)主要基于“按需配置”流程之上。在此線(xiàn)性系統(tǒng)中,我們構(gòu)建了會(huì)在一段時(shí)間后淘汰的產(chǎn)品,并且將其組件視為垃圾。與此相反,循環(huán)設(shè)計(jì)方法將產(chǎn)品的生命周期視為一個(gè)閉環(huán),其中資源不斷地被重新利用。


在“經(jīng)典”線(xiàn)性模型中,產(chǎn)品經(jīng)歷了生產(chǎn)、消費(fèi)和破壞的各個(gè)階段,最終以浪費(fèi)告終。在設(shè)計(jì)一款循環(huán)產(chǎn)品過(guò)程中,我們使用的方法包含四大階段,這四個(gè)階段形成了一個(gè)閉環(huán),并形成了一個(gè)恒定的循環(huán),在這個(gè)循環(huán)中,不僅僅只有閃閃發(fā)亮的、新的,未使用過(guò)的材料才被受歡迎。

 

循環(huán)設(shè)計(jì)方法的四個(gè)階段是:

理解 / 定義 / 制造 / 發(fā)布



當(dāng)我們同時(shí)看線(xiàn)性設(shè)計(jì)和循環(huán)設(shè)計(jì)模型方法時(shí),有一點(diǎn)吸引人的是,開(kāi)始設(shè)計(jì)東西的時(shí)候,方法的差異。從只是生產(chǎn)某種東西,到對(duì)我們將要生產(chǎn)的產(chǎn)品做出深思熟慮的決定,以及在實(shí)施過(guò)程中付出的努力和關(guān)心,這是一個(gè)大轉(zhuǎn)變。


看看我們現(xiàn)在的立場(chǎng)


為什么做出這種轉(zhuǎn)變?nèi)绱说闹匾??我確信每個(gè)看新聞的人都聽(tīng)說(shuō)過(guò)氣候變化。NASA 致力于解決環(huán)境問(wèn)題,因此我們都可以非常詳細(xì)地了解人類(lèi)行為和無(wú)限增長(zhǎng)對(duì)我們生態(tài)系統(tǒng)的影響。


但好消息是我們不必繼續(xù)這樣做,因?yàn)槲覀兛梢院苋菀讖臄?shù)字世界中“產(chǎn)生”方式中學(xué)習(xí)事物的產(chǎn)生。電力廢棄物已成為現(xiàn)代世界的主要廢棄物來(lái)源之一。大量的手機(jī)和電腦被扔掉,隨之經(jīng)濟(jì)是建立在每年都有新東西的基礎(chǔ)上的。


當(dāng)您的手機(jī)屏幕意外碎裂時(shí),我們?cè)撛趺崔k?我們知道如何處理它嗎?我們知道如何修理嗎?我們并不知道……但是幸運(yùn)的是,有些設(shè)計(jì)師對(duì)此問(wèn)題提出了解決方案。Fairphone② 是一種合乎情理,模塊化的智能手機(jī),其組件數(shù)量很少,可以輕松更換和回收。大公司也應(yīng)朝這個(gè)方向邁出一步,讓回收利用和可持續(xù)發(fā)展成為一種時(shí)尚和規(guī)范,一勞永逸。

② Fairphone:這家公司生產(chǎn)的手機(jī)希望實(shí)現(xiàn)全球手機(jī)供應(yīng)鏈的公平貿(mào)易,具體而言就是不使用“沖突礦物”并且確保生產(chǎn)手機(jī)的工人沒(méi)有被奴役和壓榨,目前仍然堅(jiān)持在非洲貧困和戰(zhàn)亂的國(guó)家進(jìn)口材料,已經(jīng)在剛果和盧旺達(dá)境內(nèi)找到了一些礦山,用更好的商業(yè)實(shí)踐推動(dòng)當(dāng)?shù)亟?jīng)濟(jì)更健康地發(fā)展。


設(shè)計(jì)和設(shè)計(jì)師的重要性


設(shè)計(jì)師,比任何其他專(zhuān)業(yè)人士,都更有可能在一轉(zhuǎn)變中產(chǎn)生巨大的影響的人。我還敢說(shuō),我們有責(zé)任采用可持續(xù)設(shè)計(jì)的方式行動(dòng)和思考。因?yàn)槭俏覀儎?chuàng)造了那些最終出現(xiàn)在傳送帶上的東西。我們也有責(zé)任教育我們的用戶(hù)。幸運(yùn)的是,越來(lái)越多的人重視具有可持續(xù)發(fā)展目標(biāo)的產(chǎn)品或品牌,或者重視起在產(chǎn)品背后有意義的故事。同樣,可持續(xù)發(fā)展不僅成為流行語(yǔ),而且成為一種價(jià)值觀(guān),被越來(lái)越多的人意識(shí)到基于有限資源的無(wú)限增長(zhǎng)是無(wú)法實(shí)現(xiàn)的目標(biāo)。但是,要從線(xiàn)性經(jīng)濟(jì)向可持續(xù)經(jīng)濟(jì)轉(zhuǎn)變,我們需要學(xué)習(xí)不同的思維方式。幸運(yùn)的是,智能設(shè)備和數(shù)字產(chǎn)品的時(shí)代帶來(lái)了一種復(fù)雜的設(shè)計(jì)思維方法,可以作為物理世界中生產(chǎn)鏈的范例。


用戶(hù)體驗(yàn)必須提供什么


地球上有一個(gè)地方,您不能隨便丟東西:互聯(lián)網(wǎng)。這是一個(gè)對(duì)已有產(chǎn)品進(jìn)行再構(gòu)思的地方,您只能去完善它,不能丟棄它,因?yàn)槿绻灰怪g說(shuō):“我不喜歡我的網(wǎng)站,明天我將推出一個(gè)全新的網(wǎng)站”,那您便會(huì)失去用戶(hù)。

如果我們看一下可持續(xù)發(fā)展設(shè)計(jì)方法的四個(gè)主要階段,就會(huì)發(fā)現(xiàn)我們?cè)谟脩?hù)體驗(yàn)設(shè)計(jì)中使用的方法與此很相似。

讓我們?cè)俅慰匆幌滤膫€(gè)階段,然后將其更詳細(xì)地分解:

理解

當(dāng)我們談?wù)撆c循環(huán)設(shè)計(jì)相關(guān)的理解時(shí),我們談?wù)摰氖窃陂_(kāi)始設(shè)計(jì)一個(gè)未來(lái)的產(chǎn)品之前就了解它的用戶(hù)和環(huán)境。研究一直是數(shù)字產(chǎn)品設(shè)計(jì)的基礎(chǔ)。與數(shù)字產(chǎn)品的連接比與實(shí)體產(chǎn)品的連接要更多的涉及到人類(lèi)的心理。因此不可避免地要開(kāi)發(fā)出新的研究方法,以幫助我們洞察用戶(hù)在使用某種產(chǎn)品時(shí)的想法、感受和行為。但這不僅與用戶(hù)有關(guān), 研究還必須深入到經(jīng)濟(jì)領(lǐng)域,并研究未來(lái)產(chǎn)品的組成部分,同時(shí)牢記它們必須可被再次利用。


定義

在此階段,將定義(商業(yè))目標(biāo),并構(gòu)建一個(gè)商業(yè)模型畫(huà)布作為生產(chǎn)過(guò)程的計(jì)劃。用戶(hù)體驗(yàn)使用這種方法已有一段時(shí)間了,讓涉眾參與其中,并在設(shè)計(jì)過(guò)程中更多地激活它們。為我們?cè)O(shè)計(jì)的產(chǎn)品設(shè)定一個(gè)目標(biāo)是至關(guān)重要的,因?yàn)橛辛怂?,我們可以為用?hù)創(chuàng)造額外的價(jià)值。因此,無(wú)論是制作商業(yè)模型畫(huà)布還是舉辦精彩的價(jià)值主張研討會(huì),在生產(chǎn)方式中實(shí)施這些方法都會(huì)對(duì)當(dāng)前的生產(chǎn)流程產(chǎn)生巨大的影響。


制造

這是關(guān)鍵部分?,F(xiàn)在我們正在做的事情就好像沒(méi)有明天一樣。隨著每種無(wú)法回收的產(chǎn)品的出現(xiàn),我們產(chǎn)生的廢料越來(lái)越多。但是循環(huán)方法是為產(chǎn)品創(chuàng)建一個(gè)原型,并定義將需要使用那些材料反映在產(chǎn)品原型上,并在定義階段概述的商業(yè)模型上定義材料。原型設(shè)計(jì)和構(gòu)思是用戶(hù)體驗(yàn)設(shè)計(jì)過(guò)程中的關(guān)鍵要素,這也是為什么需要制作原型。


發(fā)布 

根據(jù)循環(huán)設(shè)計(jì)模型,隨著產(chǎn)品的發(fā)布,生產(chǎn)周期進(jìn)入了第四階段,然同時(shí)理解階段又重新開(kāi)始了。對(duì)于數(shù)字產(chǎn)品來(lái)說(shuō),這是自然發(fā)生的事前:你發(fā)布一個(gè)產(chǎn)品,基于該版本收集反饋,然后構(gòu)思它,周而復(fù)始,這個(gè)循環(huán)再次產(chǎn)生。


但是,觀(guān)察這個(gè)循環(huán)并建立這些連接僅僅是冰山一角。在數(shù)字時(shí)代發(fā)展起來(lái)的設(shè)計(jì)思維給世界帶來(lái)了許多反思。


變革中的大佬


幸運(yùn)的是,已經(jīng)有許多大品牌意識(shí)到轉(zhuǎn)變的必要性,并采取和提出了數(shù)字設(shè)計(jì)思維方法來(lái)支持轉(zhuǎn)變,并建立循環(huán)設(shè)計(jì)的時(shí)代。根據(jù)《循環(huán)設(shè)計(jì)指南》,“我們應(yīng)該把我們?cè)O(shè)計(jì)的所有東西都看作軟件產(chǎn)品和服務(wù)——這些產(chǎn)品和服務(wù)可以基于我們通過(guò)反饋得到的數(shù)據(jù)而不斷的發(fā)?!?


用戶(hù)體驗(yàn)研究和用戶(hù)體驗(yàn)設(shè)計(jì)一直是在做的一件事是:基于全面的研究和真實(shí)的用戶(hù)需求來(lái)構(gòu)建產(chǎn)品。上面的設(shè)計(jì)指南是非常復(fù)雜的工具,具有許多可能的方法。它強(qiáng)調(diào)了從產(chǎn)品到服務(wù)流程轉(zhuǎn)變的重要性,并展示如何使用敏捷流程并將其實(shí)施到構(gòu)建產(chǎn)品的方法之中。


IDEO(全球頂尖的設(shè)計(jì)咨詢(xún)公司)與 Ellen Macarthur Foundation(艾倫·麥克阿瑟基金會(huì))合作,試圖“試圖通過(guò)設(shè)計(jì)構(gòu)建一個(gè)具有恢復(fù)性和再生性的經(jīng)濟(jì)框架”。在這里,您可以找到幾乎每個(gè)生產(chǎn)方面和領(lǐng)域——例如食品、時(shí)裝、經(jīng)濟(jì)和設(shè)計(jì)——并在每個(gè)領(lǐng)域中提出解決方案,以打破線(xiàn)性生產(chǎn)系統(tǒng)。


耐克還宣布了其基于循環(huán)設(shè)計(jì)模型生產(chǎn)高品質(zhì)運(yùn)動(dòng)鞋的新方法原則。正如您已經(jīng)看到的那樣,無(wú)論您身處哪個(gè)經(jīng)濟(jì)領(lǐng)域,都可以為循環(huán)生產(chǎn)過(guò)程的蓬勃發(fā)展做貢獻(xiàn),并成為一支主導(dǎo)力量。


重要的結(jié)論


我認(rèn)為,作為設(shè)計(jì)師,我們始終要為變革而努力,并始終努力與客戶(hù)、產(chǎn)品或服務(wù)保持緊密的關(guān)系,并通過(guò)構(gòu)思使其不斷完善,以實(shí)現(xiàn)這一目標(biāo)。這是因?yàn)閭ゴ蟮氖虑橹挥型ㄟ^(guò)時(shí)間和不斷的反思才能實(shí)現(xiàn)。在離線(xiàn)世界中,數(shù)字設(shè)計(jì)過(guò)程也有很多東西可以貢獻(xiàn)。希望通過(guò)教育,能有更多的大公司意識(shí)到用戶(hù)真正想要的產(chǎn)品是具有更多功能并可持續(xù)使用的,而不僅僅是將它們當(dāng)作一次性產(chǎn)品,一旦它們不像最初那樣光鮮就把她扔掉。

轉(zhuǎn)自:站酷-大猴兒er 


六個(gè)好用的程序員開(kāi)發(fā)在線(xiàn)工具

seo達(dá)人

網(wǎng)上可以找到前端開(kāi)發(fā)社區(qū)貢獻(xiàn)的大量工具,這篇文章列出了我最喜歡的一些工具,這些工具給我的工作帶來(lái)了許多便利。


1. EnjoyCSS


老實(shí)說(shuō),雖然我做過(guò)許多前端開(kāi)發(fā),但我并不擅長(zhǎng) CSS。當(dāng)我陷入困境時(shí),EnjoyCSS 是我的大救星。EnjoyCSS 提供了一個(gè)簡(jiǎn)單的交互界面,幫助我設(shè)計(jì)元素,然后自動(dòng)輸出相應(yīng)的 CSS 代碼。




EnjoyCSS 可以輸出 CSS、LESS、SCSS 代碼,并支持指定需要支持哪些瀏覽器及其版本。開(kāi)發(fā)簡(jiǎn)單頁(yè)面時(shí)用起來(lái)比較方便,但不太適合復(fù)雜一點(diǎn)的前端項(xiàng)目(這類(lèi)項(xiàng)目往往需要引入 CSS 框架)。

2. Prettier Playground


Prettier 是一個(gè)代碼格式化工具,支持格式化 JavaScript 代碼(包括 ES2017、JSX、Angular、Vue、Flow、TypeScript 等)。Prettier 會(huì)移除代碼原本的樣式,替換為遵循最佳實(shí)踐的標(biāo)準(zhǔn)化、一致的樣式。IDE 大多支持 Prettier 工具,不過(guò) Prettier 也有在線(xiàn)版本,讓你可以在瀏覽器里格式化代碼。




如果工作電腦不在手邊,使用移動(dòng)端設(shè)備或者臨時(shí)借用別人的電腦查看代碼時(shí),Prettier Playground 非常好用。相比在 IDE 或編輯器下使用 Prettier,個(gè)人更推薦通過(guò) git pre-commit hook 配置 Prettier:hook 可以保證整個(gè)團(tuán)隊(duì)使用統(tǒng)一的配置,免去各自分別配置 IDE 或編輯器的麻煩。如果是老項(xiàng)目,hook 還可以設(shè)置只格式化有改動(dòng)的單個(gè)文件甚至有改動(dòng)的代碼段,避免在 IDE 或編輯器下使用 Prettier 時(shí)不小心格式了大量代碼,淹沒(méi)了 commit 的主要改動(dòng),讓 review 代碼變得十分痛苦。

3. Postman


Postman 一直在我的開(kāi)發(fā)工具箱里,測(cè)試后端 API 接口時(shí)非常好用。GET、POST、DELETE、OPTIONS、PUT 這些方法都支持。毫無(wú)疑問(wèn),你應(yīng)該使用這個(gè)工具。




Postman 之外,Insomnia 也是很流行的 REST API 測(cè)試工具,亮點(diǎn)是支持 GraphQL。不過(guò) Postman 從 去年夏天發(fā)布的 v7.2 起也支持了 GraphQL。

4. StackBlitz


Chidume Nnamdi 盛贊這是每個(gè)用戶(hù)最喜歡的在線(xiàn) IDE。StackBlitz 將大家最喜歡、最常用的 IDE Visual Studio Code 搬進(jìn)了瀏覽器。


StackBlitz 支持一鍵配置 Angular、React、Ionic、TypeScript、RxJS、Svelte 等 JavaScript 框架,也就是說(shuō),只需幾秒你就可以開(kāi)始寫(xiě)代碼了。


我覺(jué)得這個(gè)在線(xiàn) IDE 很有用,特別是可以在線(xiàn)嘗試一些樣例代碼或者庫(kù),否則僅僅嘗試一些新特性就需要花很多時(shí)間在新項(xiàng)目初始化配置上。有了 StackBlitz,無(wú)需在本地從頭搭建環(huán)境,花上幾分鐘就可以試用一個(gè) NPM 包。很棒,不是嗎?




微軟官方其實(shí)也提供了在線(xiàn)版本的 VSCode,可以在瀏覽器內(nèi)使用 VSCode,并且支持開(kāi)發(fā) Node.js 項(xiàng)目(基于 Azure)。不過(guò) StackBlitz 更專(zhuān)注于優(yōu)化前端開(kāi)發(fā)體驗(yàn),界面更加直觀(guān)一點(diǎn),也推出了 beta 版本的 Node.js 支持(基于 GCP,需要填表申請(qǐng))。

5. Bit.dev


軟件開(kāi)發(fā)的基本原則之一就是代碼復(fù)用。代碼復(fù)用減少了開(kāi)發(fā)量,讓你不用從頭開(kāi)發(fā)組件。


這正是 Bit.dev 做的事,分享可重用的組件和片段,降低開(kāi)發(fā)量,加速開(kāi)發(fā)進(jìn)程。


除了公開(kāi)分享,它還支持在團(tuán)隊(duì)分享,讓團(tuán)隊(duì)協(xié)作更方便。


正如 Bit.dev 的口號(hào)「組件即設(shè)計(jì)體系。協(xié)同開(kāi)發(fā)更好的組件。」所言,Bit.dev 可以用來(lái)創(chuàng)建設(shè)計(jì)體系,允許團(tuán)隊(duì)內(nèi)的開(kāi)發(fā)者和設(shè)計(jì)師一起協(xié)作,從頭搭建一套設(shè)計(jì)體系。


Bit.dev 目前支持 React、Vue、Angular、Node 及其他 JavaScript 框架。




在 Bit.dev 上不僅可以搜索組件,還可以直接查看組件的依賴(lài),瀏覽組件的代碼,甚至在線(xiàn)編輯代碼并查看預(yù)覽效果!選好組件后可以通過(guò) Bit.dev 的命令行工具 bit 在本地項(xiàng)目引入組件,也可以通過(guò) npm、yarn 引入組件。

6. CanIUse


CanIUse是非常好用的在線(xiàn)工具,可以方便地查看各大瀏覽器對(duì)某個(gè)特性的支持程度。


我過(guò)去經(jīng)常碰到自己開(kāi)發(fā)的應(yīng)用的一些功能在其他瀏覽器下不支持的情況。比如我的作品集項(xiàng)目使用的某個(gè)特性在 Safari 下不支持,直到項(xiàng)目上線(xiàn)幾個(gè)月后我才意識(shí)到。這些經(jīng)驗(yàn)教訓(xùn)讓我意識(shí)到需要檢查瀏覽器兼容性。


我們來(lái)看一個(gè)例子吧。哪些瀏覽器支持 WebP 圖像格式?




如你所見(jiàn),Safari 和 IE 目前不支持 WebP。這意味著需要為不兼容的瀏覽器提供回退選項(xiàng),比如:


<picture>

CanIUse 還可以在命令行下使用,例如,在命令行下查看 WebP 圖像格式的瀏覽器兼容性:caniuse webp(運(yùn)行命令前需要事先通過(guò) npm install -g caniuse-cmd安裝命令行工具。


10 個(gè)超有用的 JavaScript 技巧

seo達(dá)人

方法參數(shù)的驗(yàn)證

JavaScript 允許你設(shè)置參數(shù)的默認(rèn)值。通過(guò)這種方法,可以通過(guò)一個(gè)巧妙的技巧來(lái)驗(yàn)證你的方法參數(shù)。


const isRequired = () => { throw new Error('param is required'); };

const print = (num = isRequired()) => { console.log(`printing ${num}`) };

print(2);//printing 2

print()// error

print(null)//printing null

非常整潔,不是嗎?


格式化 json 代碼

你可能對(duì) JSON.stringify 非常熟悉。但是你是否知道可以用 stringify 進(jìn)行格式化輸出?實(shí)際上這很簡(jiǎn)單。


stringify 方法需要三個(gè)輸入。 value,replacer 和 space。后兩個(gè)是可選參數(shù)。這就是為什么我們以前沒(méi)有注意過(guò)它們。要對(duì) json 進(jìn)行縮進(jìn),必須使用 space 參數(shù)。


console.log(JSON.stringify({name:"John",Age:23},null,'\t'));

>>>

{

"name": "John",

"Age": 23

}

從數(shù)組中獲取唯一值

要從數(shù)組中獲取唯一值,我們需要使用 filter 方法來(lái)過(guò)濾出重復(fù)值。但是有了新的 Set 對(duì)象,事情就變得非常順利和容易了。


let uniqueArray = [...new Set([1, 2, 3, 3, 3, "school", "school", 'ball', false, false, true, true])];

>>> [1, 2, 3, "school", "ball", false, true]

從數(shù)組中刪除虛值(Falsy Value)

在某些情況下,你可能想從數(shù)組中刪除虛值。虛值是 JavaScript 的 Boolean 上下文中被認(rèn)定為為 false 的值。 JavaScript 中只有六個(gè)虛值,它們是:


undefined

null

NaN

0

"" (空字符串)

false

濾除這些虛值的最簡(jiǎn)單方法是使用以下函數(shù)。


myArray.filter(Boolean);

如果要對(duì)數(shù)組進(jìn)行一些修改,然后過(guò)濾新數(shù)組,可以嘗試這樣的操作。請(qǐng)記住,原始的 myArray 會(huì)保持不變。


myArray

   .map(item => {

       // Do your changes and return the new item

   })

   .filter(Boolean);

合并多個(gè)對(duì)象

假設(shè)我有幾個(gè)需要合并的對(duì)象,那么這是我的首選方法。


const user = {

    name: 'John Ludwig',

    gender: 'Male'

};

const college = {

    primary: 'Mani Primary School',

    secondary: 'Lass Secondary School'

};

const skills = {

   programming: 'Extreme',

   swimming: 'Average',

   sleeping: 'Pro'

};

const summary = {...user, ...college, ...skills};

這三個(gè)點(diǎn)在 JavaScript 中也稱(chēng)為展開(kāi)運(yùn)算符。你可以在這里學(xué)習(xí)更多用法。


對(duì)數(shù)字?jǐn)?shù)組進(jìn)行排序

JavaScript 數(shù)組有內(nèi)置的 sort 方法。默認(rèn)情況下 sort 方法把數(shù)組元素轉(zhuǎn)換為字符串,并對(duì)其進(jìn)行字典排序。在對(duì)數(shù)字?jǐn)?shù)組進(jìn)行排序時(shí),這有可能會(huì)導(dǎo)致一些問(wèn)題。所以下面是解決這類(lèi)問(wèn)題的簡(jiǎn)單解決方案。


[0,10,4,9,123,54,1].sort((a,b) => a-b);

>>> [0, 1, 4, 9, 10, 54, 123]

這里提供了一個(gè)將數(shù)字?jǐn)?shù)組中的兩個(gè)元素與 sort 方法進(jìn)行比較的函數(shù)。這個(gè)函數(shù)可幫助我們接收正確的輸出。


Disable Right Click

禁用右鍵

你可能想要阻止用戶(hù)在你的網(wǎng)頁(yè)上單擊鼠標(biāo)右鍵。


<body oncontextmenu="return false">

   <div></div>

</body>

這段簡(jiǎn)單的代碼將為你的用戶(hù)禁用右鍵單擊。


使用別名進(jìn)行解構(gòu)

解構(gòu)賦值語(yǔ)法是一種 JavaScript 表達(dá)式,可以將數(shù)組中的值或?qū)ο蟮闹祷驅(qū)傩苑峙浣o變量。解構(gòu)賦值能讓我們用更簡(jiǎn)短的語(yǔ)法進(jìn)行多個(gè)變量的賦值。


const object = { number: 10 };


// Grabbing number

const { number } = object;


// Grabbing number and renaming it as otherNumber

const { number: otherNumber } = object;

console.log(otherNumber); //10

獲取數(shù)組中的最后一項(xiàng)

可以通過(guò)對(duì) splice 方法的參數(shù)傳入負(fù)整數(shù),來(lái)數(shù)獲取組末尾的元素。


let array = [0, 1, 2, 3, 4, 5, 6, 7]

console.log(array.slice(-1));

>>>[7]

console.log(array.slice(-2));

>>>[6, 7]

console.log(array.slice(-3));

>>>[5, 6, 7]

等待 Promise 完成

在某些情況下,你可能會(huì)需要等待多個(gè) promise 結(jié)束??梢杂?Promise.all 來(lái)并行運(yùn)行我們的 promise。


const PromiseArray = [

   Promise.resolve(100),

   Promise.reject(null),

   Promise.resolve("Data release"),

   Promise.reject(new Error('Something went wrong'))];


Promise.all(PromiseArray)

 .then(data => console.log('all resolved! here are the resolve values:', data))

 .catch(err => console.log('got rejected! reason:', err))

關(guān)于 Promise.all 的主要注意事項(xiàng)是,當(dāng)一個(gè) Promise 拒絕時(shí),該方法將引發(fā)錯(cuò)誤。這意味著你的代碼不會(huì)等到你所有的 promise 都完成。


如果你想等到所有 promise 都完成后,無(wú)論它們被拒絕還是被解決,都可以使用 Promise.allSettled。此方法在 ES2020 的最終版本得到支持。


const PromiseArray = [

   Promise.resolve(100),

   Promise.reject(null),

   Promise.resolve("Data release"),

   Promise.reject(new Error('Something went wrong'))];


Promise.allSettled(PromiseArray).then(res =>{

console.log(res);

}).catch(err => console.log(err));


//[

//{status: "fulfilled", value: 100},

//{status: "rejected", reason: null},

//{status: "fulfilled", value: "Data release"},

//{status: "rejected", reason: Error: Something went wrong ...}

//]

即使某些 promise 被拒絕,Promise.allSettled 也會(huì)從你所有的 promise 中返回結(jié)果。

你所不知道的XML

前端達(dá)人

一、XML:

XML(Extensible Markup Language 可擴(kuò)展標(biāo)記語(yǔ)言),XML是一個(gè)以文本來(lái)描述數(shù)據(jù)的文檔。

1. 示例:

<?xml version="1.0" encoding="UTF-8"?>
<people>
    <person personid="E01">
        <name>Tony</name>
        <address>10 Downing Street, London, UK</address>
        <tel>(061) 98765</tel>
        <fax>(061) 98765</fax>
        <email>tony@everywhere.com</email>
    </person>
    <person personid="E02">
        <name>Bill</name>
        <address>White House, USA</address>
        <tel>(001) 6400 98765</tel>
        <fax>(001) 6400 98765</fax>
        <email>bill@everywhere.com</email>
    </person>
</people>

2. 用途:

(1)充當(dāng)顯示數(shù)據(jù)(以XML充當(dāng)顯示層)

(2)存儲(chǔ)數(shù)據(jù)(存儲(chǔ)層)的功能

(3)以XML描述數(shù)據(jù),并在聯(lián)系服務(wù)器與系統(tǒng)的其余部分之間傳遞。(傳輸數(shù)據(jù)的一樣格式)

從某種角度講,XML是數(shù)據(jù)封裝和消息傳遞技術(shù)。

3.解析XML:
3.1 :使用SAX解析XML

3.1.1 什么是SAX:

SAX是Simple API for XML的縮寫(xiě)
SAX 是讀取和操作 XML 數(shù)據(jù)更快速、更輕量的方法。SAX 允許您在讀取文檔時(shí)處理它,從而不必等待整個(gè)文檔被存儲(chǔ)之后才采取操作。它不涉及 DOM 所必需的開(kāi)銷(xiāo)和概念跳躍。 SAX API是一個(gè)基于事件的API ,適用于處理數(shù)據(jù)流,即隨著數(shù)據(jù)的流動(dòng)而依次處理數(shù)據(jù)。SAX API 在其解析您的文檔時(shí)發(fā)生一定事件的時(shí)候會(huì)通知您。在您對(duì)其響應(yīng)時(shí),您不作保存的數(shù)據(jù)將會(huì)被拋棄。

3.1.2 SAX解析XML方式:

SAX API中主要有四種處理事件的接口,它們分別是ContentHandler,DTDHandler, EntityResolver 和 ErrorHandler 。實(shí)際上只要繼承DefaultHandler 類(lèi)就可以,DefaultHandler實(shí)現(xiàn)了這四個(gè)事件處理器接口,然后提供了每個(gè)抽象方法的默認(rèn)實(shí)現(xiàn)。
// 創(chuàng)建SAX解析器工廠(chǎng)對(duì)象
SAXParserFactory spf = SAXParserFactory.newInstance();
// 使用解析器工廠(chǎng)創(chuàng)建解析器實(shí)例
SAXParser saxParser = spf.newSAXParser();
// 創(chuàng)建SAX解析器要使用的事件偵聽(tīng)器對(duì)象
PersonHandler handler = 
                         new PersonHandler();
// 開(kāi)始解析文件
saxParser.parse(
            new File(fileName), handler);


3.2. DOM解析XML:

DOM:Document Object Model(文檔對(duì)象模型)
DOM的特性:
定義一組 Java 接口,基于對(duì)象,與語(yǔ)言和平臺(tái)無(wú)關(guān)將 XML 文檔表示為樹(shù),在內(nèi)存中解析和存儲(chǔ) XML 文檔,允許隨機(jī)訪(fǎng)問(wèn)文檔的不同部分。

DOM解析XML
DOM的優(yōu)點(diǎn),由于樹(shù)在內(nèi)存中是持久的,因此可以修改后更新。它還可以在任何時(shí)候在樹(shù)中上下導(dǎo)航,API使用起來(lái)也較簡(jiǎn)單。 

DocumentBuilderFactory builder = DocumentBuilderFactory.newInstance();
DocumentBuilder db = builder.newDocumentBuilder();
db.parse("person.xml");
NodeList node_person = doc.getElementsByTagName("person");

 3.3. JDOM解析XML:

JDOM是兩位著名的 Java 開(kāi)發(fā)人員兼作者,Brett Mclaughlin 和 Jason Hunter 的創(chuàng)作成果, 2000 年初在類(lèi)似于A(yíng)pache協(xié)議的許可下,JDOM作為一個(gè)開(kāi)放源代碼項(xiàng)目正式開(kāi)始研發(fā)了。

JDOM 簡(jiǎn)化了與 XML 的交互并且比使用 DOM 實(shí)現(xiàn)更快,JDOM 與 DOM 主要有兩方面不同。首先,JDOM 僅使用具體類(lèi)而不使用接口。這在某些方面簡(jiǎn)化了 API,但是也限制了靈活性。第二,API 大量使用了 Collections 類(lèi),簡(jiǎn)化了那些已經(jīng)熟悉這些類(lèi)的 Java 開(kāi)發(fā)者的使用。
 

解析步驟:
(1)SAXBuilder sax = new SAXBuilder();
(2)Document doc = sax.build(….);
(3)Element el = doc.getRootElement();(4)List list = el.getChildren();
(5)遍歷內(nèi)容


3.4. DOM4J解析XML:

dom4j是一個(gè)非常非常優(yōu)秀的Java XML API,具有性能優(yōu)異、功能強(qiáng)大和極端易用使用的特點(diǎn),同時(shí)它也是一個(gè)開(kāi)放源代碼的軟件,可以在SourceForge上找到它。在對(duì)主流的Java XML API進(jìn)行的性能、功能和易用性的評(píng)測(cè),dom4j無(wú)論在那個(gè)方面都是非常出色的。如今你可以看到越來(lái)越多的Java軟件都在使用dom4j來(lái)讀寫(xiě)XML,特別值得一提的是連Sun的JAXM也在用dom4j。這是必須使用的jar包, Hibernate用它來(lái)讀寫(xiě)配置文件。
解析步驟:
(1)SAXReader sax = new SAXReader();
(2)Document doc = sax.read(Thread.currentThread().getContextClassLoader()
          .getResourceAsStream("person.xml"));
(3)Element root = doc.getRootElement();
(4)Iterator iterator = root.elementIterator();
(5)遍歷迭代器


4.各種解析方法比較:
JDOM 和 DOM 在性能測(cè)試時(shí)表現(xiàn)不佳,在測(cè)試 10M 文檔時(shí)內(nèi)存溢出。
SAX表現(xiàn)較好,這要依賴(lài)于它特定的解析方式。一個(gè) SAX 檢測(cè)即將到來(lái)的XML流,但并沒(méi)有載入到內(nèi)存(當(dāng)然當(dāng)XML流被讀入時(shí),會(huì)有部分文檔暫時(shí)隱藏在內(nèi)存中。DOM4J是這場(chǎng)測(cè)試的獲勝者,目前許多開(kāi)源項(xiàng)目中大量采用 DOM4J,例如大名鼎鼎的 Hibernate 也用 DOM4J 來(lái)讀取 XML 配置文件。
xstream 實(shí)現(xiàn)XML的轉(zhuǎn)換


5.案例:

public class Person {
    private String personid;
    private String name;
    private String address;
    private String tel;
    private String fax;
    private String email;

    @Override
    public String toString() {
        return "Person{" +
                "personid='" + personid + '\'' +
                ", name='" + name + '\'' +
                ", address='" + address + '\'' +
                ", tel='" + tel + '\'' +
                ", fax='" + fax + '\'' +
                ", email='" + email + '\'' +
                '}';
    }

    public String getPersonid() {
        return personid;
    }

    public void setPersonid(String personid) {
        this.personid = personid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getTel() {
        return tel;
    }

    public void setTel(String tel) {
        this.tel = tel;
    }

    public String getFax() {
        return fax;
    }

    public void setFax(String fax) {
        this.fax = fax;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}



<?xml version="1.0" encoding="UTF-8"?>
<people>
    <person personid="E01">
        <name>Tony Blair</name>
        <address>10 Downing Street, London, UK</address>
        <tel>(061) 98765</tel>
        <fax>(061) 98765</fax>
        <email>blair@everywhere.com</email>
    </person>
    <person personid="E02">
        <name>Bill Clinton</name>
        <address>White House, USA</address>
        <tel>(001) 6400 98765</tel>
        <fax>(001) 6400 98765</fax>
        <email>bill@everywhere.com</email>
    </person>
</people>


import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Hu Guanzhong
 * SAX解析的特點(diǎn):
 * 1、基于事件驅(qū)動(dòng)
 * 2、順序讀取,速度快
 * 3、不能任意讀取節(jié)點(diǎn)(靈活性差)
 * 4、解析時(shí)占用的內(nèi)存小
 * 5、SAX更適用于在性能要求更高的設(shè)備上使用(Android開(kāi)發(fā)中)
 *
 */
public class PersonHandler extends DefaultHandler{
    private List<Person> persons = null;
    private Person p;//當(dāng)前正在解析的person
    private String tag;//用于記錄當(dāng)前正在解析的標(biāo)簽名

    public List<Person> getPersons() {
        return persons;
    }

    //開(kāi)始解析文檔時(shí)調(diào)用
    @Override
    public void startDocument() throws SAXException {
        super.startDocument();
        persons = new ArrayList<>();
        System.out.println("開(kāi)始解析文檔...");
    }

    //在XML文檔解析結(jié)束時(shí)調(diào)用
    @Override
    public void endDocument() throws SAXException {
        super.endDocument();
        System.out.println("解析文檔結(jié)束.");
    }

    /**
     * 解析開(kāi)始元素時(shí)調(diào)用
     * @param uri 命名空間
     * @param localName 不帶前綴的標(biāo)簽名
     * @param qName 帶前綴的標(biāo)簽名
     * @param attributes 當(dāng)前標(biāo)簽的屬性集合
     * @throws SAXException
     */
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        super.startElement(uri, localName, qName, attributes);
        if ("person".equals(qName)){
            p = new Person();
            String personid = attributes.getValue("personid");
            p.setPersonid(personid);
        }
        tag = qName;
        System.out.println("startElement--"+qName);
    }

    //解析結(jié)束元素時(shí)調(diào)用
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        super.endElement(uri, localName, qName);
        if ("person".equals(qName)) {
            persons.add(p);
        }
        tag = null;
        System.out.println("endElement--"+qName);
    }

    //解析文本內(nèi)容時(shí)調(diào)用
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        super.characters(ch, start, length);
        if (tag != null) {
            if ("name".equals(tag)) {
                p.setName(new String(ch,start,length));
            }else if("address".equals(tag)){
                p.setAddress(new String(ch,start,length));
            }else if("tel".equals(tag)){
                p.setTel(new String(ch,start,length));
            }else if("fax".equals(tag)){
                p.setFax(new String(ch,start,length));
            }else if("email".equals(tag)){
                p.setEmail(new String(ch,start,length));
            }
            System.out.println(ch);
        }
    }
}



public class XMLDemo {

    /**
     * 使用第三方xstream組件實(shí)現(xiàn)XML的解析與生成
     */
    @Test
    public void xStream(){
        Person p = new Person();
        p.setPersonid("1212");
        p.setAddress("北京");
        p.setEmail("vince@163.com");
        p.setFax("6768789798");
        p.setTel("13838389438");
        p.setName("38");

        XStream xStream = new XStream(new Xpp3Driver());
        xStream.alias("person",Person.class);
        xStream.useAttributeFor(Person.class,"personid");
        String xml = xStream.toXML(p);
        System.out.println(xml);

        //解析XML
        Person person = (Person)xStream.fromXML(xml);
        System.out.println(person);
    }

    /**
     * 從XML文件中讀取對(duì)象
     */
    @Test
    public void xmlDecoder() throws FileNotFoundException {
        BufferedInputStream in = new BufferedInputStream(new FileInputStream("test.xml"));
        XMLDecoder decoder = new XMLDecoder(in);
        Person p = (Person)decoder.readObject();
        System.out.println(p);
    }
    /**
     * 把對(duì)象轉(zhuǎn)成XML文件寫(xiě)入
     */
    @Test
    public void xmlEncoder() throws FileNotFoundException {
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("test.xml"));
        XMLEncoder xmlEncoder = new XMLEncoder(bos);
        Person p = new Person();
        p.setPersonid("1212");
        p.setAddress("北京");
        p.setEmail("vince@163.com");
        p.setFax("6768789798");
        p.setTel("13838389438");
        p.setName("38");
        xmlEncoder.writeObject(p);
        xmlEncoder.close();
    }

    /**
     * DOM4J解析XML
     * 基于樹(shù)型結(jié)構(gòu),第三方組件
     * 解析速度快,效率更高,使用的JAVA中的迭代器實(shí)現(xiàn)數(shù)據(jù)讀取,在WEB框架中使用較多(Hibernate)
     *
     */
    @Test
    public void dom4jParseXML() throws DocumentException {
        //1 創(chuàng)建DOM4J的解析器對(duì)象
        SAXReader reader = new SAXReader();
        InputStream is = Thread.currentThread().getContextClassLoader()
                .getResourceAsStream("com/vince/xml/person.xml");
        org.dom4j.Document doc = reader.read(is);
        org.dom4j.Element rootElement = doc.getRootElement();
        Iterator<org.dom4j.Element> iterator = rootElement.elementIterator();
        ArrayList<Person> persons = new ArrayList<>();
        Person p = null;
        while(iterator.hasNext()){
            p = new Person();
            org.dom4j.Element e = iterator.next();
            p.setPersonid(e.attributeValue("personid"));
            Iterator<org.dom4j.Element> iterator1 = e.elementIterator();
            while(iterator1.hasNext()){
                org.dom4j.Element next = iterator1.next();
                String tag = next.getName();
                if("name".equals(tag)){
                    p.setName(next.getText());
                }else if("address".equals(tag)){
                    p.setAddress(next.getText());
                }else if("tel".equals(tag)){
                    p.setTel(next.getText());
                }else if("fax".equals(tag)){
                    p.setFax(next.getText());
                }else if("email".equals(tag)){
                    p.setEmail(next.getText());
                }
            }
            persons.add(p);
        }
        System.out.println("結(jié)果:");
        System.out.println(Arrays.toString(persons.toArray()));
    }

    /**
     * JDOM解析 XML
     * 1、與DOM類(lèi)似基于樹(shù)型結(jié)構(gòu),
     * 2、與DOM的區(qū)別:
     * (1)第三方開(kāi)源的組件
     * (2)實(shí)現(xiàn)使用JAVA的Collection接口
     * (3)效率比DOM更快
     */
    @Test
    public void jdomParseXML() throws JDOMException, IOException {
        //創(chuàng)建JDOM解析器
        SAXBuilder builder = new SAXBuilder();
        InputStream is = Thread.currentThread().getContextClassLoader()
                .getResourceAsStream("com/vince/xml/person.xml");
        org.jdom2.Document build = builder.build(is);
        Element rootElement = build.getRootElement();
        List<Person> list = new ArrayList<>();
        Person person = null;
        List<Element> children = rootElement.getChildren();
        for(Element element: children){
            person = new Person();
            String personid = element.getAttributeValue("personid");
            person.setPersonid(personid);
            List<Element> children1 = element.getChildren();
            for (Element e: children1){
                String tag = e.getName();
                if("name".equals(tag)){
                    person.setName(e.getText());
                }else if("address".equals(tag)){
                    person.setAddress(e.getText());
                }else if("tel".equals(tag)){
                    person.setTel(e.getText());
                }else if("fax".equals(tag)){
                    person.setFax(e.getText());
                }else if("email".equals(tag)){
                    person.setEmail(e.getText());
                }
            }
            list.add(person);
        }
        System.out.println("結(jié)果:");
        System.out.println(Arrays.toString(list.toArray()));
    }

    /**
     * DOM解析XML
     * 1、基于樹(shù)型結(jié)構(gòu),通過(guò)解析器一次性把文檔加載到內(nèi)存中,所以會(huì)比較占用內(nèi)存,可以隨機(jī)訪(fǎng)問(wèn)
     * 更加靈活,更適合在WEB開(kāi)發(fā)中使用
     */
    @Test
    public void domParseXML() throws ParserConfigurationException, IOException, SAXException {
        //1、創(chuàng)建一個(gè)DOM解析器工廠(chǎng)對(duì)象
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        //2、通過(guò)工廠(chǎng)對(duì)象創(chuàng)建解析器對(duì)象
        DocumentBuilder documentBuilder = factory.newDocumentBuilder();
        //3、解析文檔
        InputStream is = Thread.currentThread().getContextClassLoader()
                .getResourceAsStream("com/vince/xml/person.xml");
        //此代碼完成后,整個(gè)XML文檔已經(jīng)被加載到內(nèi)存中,以樹(shù)狀形式存儲(chǔ)
        Document doc = documentBuilder.parse(is);
        //4、從內(nèi)存中讀取數(shù)據(jù)

        //獲取節(jié)點(diǎn)名稱(chēng)為person的所有節(jié)點(diǎn),返回節(jié)點(diǎn)集合
        NodeList personNodeList = doc.getElementsByTagName("person");
        ArrayList<Person> persons = new ArrayList<>();
        Person p = null;
        //此循環(huán)會(huì)迭代兩次
        for (int i=0;i<personNodeList.getLength();i++){
            Node personNode = personNodeList.item(i);
            p = new Person();
            //獲取節(jié)點(diǎn)的屬性值
            String personid = personNode.getAttributes().getNamedItem("personid").getNodeValue();
            p.setPersonid(personid);
            //獲取當(dāng)前節(jié)點(diǎn)的所有子節(jié)點(diǎn)
            NodeList childNodes = personNode.getChildNodes();
            for (int j = 0;j<childNodes.getLength();j++){
                Node item = childNodes.item(j);
                String nodeName = item.getNodeName();
                if ("name".equals(nodeName)) {
                    p.setName(item.getFirstChild().getNodeValue());
                }else if("address".equals(nodeName)){
                    p.setAddress(item.getFirstChild().getNodeValue());
                }else if("tel".equals(nodeName)){
                    p.setTel(item.getFirstChild().getNodeValue());
                }else if("fax".equals(nodeName)){
                    p.setFax(item.getFirstChild().getNodeValue());
                }else if("email".equals(nodeName)){
                    p.setEmail(item.getFirstChild().getNodeValue());
                }
            }
            persons.add(p);
        }
        System.out.println("結(jié)果:");
        System.out.println(Arrays.toString(persons.toArray()));
    }

    /**
     * SAX解析的特點(diǎn):
     * 1、基于事件驅(qū)動(dòng)
     * 2、順序讀取,速度快
     * 3、不能任意讀取節(jié)點(diǎn)(靈活性差)
     * 4、解析時(shí)占用的內(nèi)存小
     * 5、SAX更適用于在性能要求更高的設(shè)備上使用(Android開(kāi)發(fā)中)
     * @throws ParserConfigurationException
     * @throws SAXException
     * @throws IOException
     */
    @Test
    public void saxParseXML() throws ParserConfigurationException, SAXException, IOException {
        //1、創(chuàng)建一個(gè)SAX解析器工廠(chǎng)對(duì)象
        SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
        //2、通過(guò)工廠(chǎng)對(duì)象創(chuàng)建SAX解析器
        SAXParser saxParser = saxParserFactory.newSAXParser();
        //3、創(chuàng)建一個(gè)數(shù)據(jù)處理器(需要我們自己來(lái)編寫(xiě))
        PersonHandler personHandler = new PersonHandler();
        //4、開(kāi)始解析
        InputStream is = Thread.currentThread().getContextClassLoader()
                .getResourceAsStream("com/vince/xml/person.xml");
        saxParser.parse(is,personHandler);
        List<Person> persons = personHandler.getPersons();
        for (Person p:persons){
            System.out.println(p);
        }
    }
}

高性能Javascript讀書(shū)總結(jié)

前端達(dá)人

1. 加載和執(zhí)行

盡量將所有的<script>標(biāo)簽放在</body>標(biāo)簽之前,確保腳本執(zhí)行前頁(yè)面已經(jīng)完成了渲染,避免腳本的下載阻塞其他資源(例如圖片)的下載。

合并腳本,減少頁(yè)面中的<script>標(biāo)簽

使用<script>標(biāo)簽的defer和async屬性(兩者的區(qū)別見(jiàn)這里)

通過(guò)Javascript動(dòng)態(tài)創(chuàng)建<script>標(biāo)簽插入文檔來(lái)下載,其不會(huì)影響頁(yè)面其他進(jìn)程

2.數(shù)據(jù)存取

由于作用域鏈的機(jī)制,訪(fǎng)問(wèn)局部變量比訪(fǎng)問(wèn)跨作用域變量更快,因此在函數(shù)中若要多次訪(fǎng)問(wèn)跨作用域變量,則可以用局部變量保存。

避免使用with語(yǔ)句,其會(huì)延長(zhǎng)作用域鏈

嵌套的對(duì)象成員會(huì)導(dǎo)致引擎搜索所有對(duì)象成員,避免使用嵌套,例如window.location.href

對(duì)象的屬性和方法在原型鏈的位置越深,訪(fǎng)問(wèn)的速度也越慢

3.Dom編程

進(jìn)行大段HTML更新時(shí),推薦使用innerHTML,而不是DOM方法

HTML集合是一個(gè)與文檔中元素綁定的類(lèi)數(shù)組對(duì)象,其長(zhǎng)度隨著文檔中元素的增減而動(dòng)態(tài)變化,因此避免在每次循環(huán)中直接讀取HTML集合的length,容易導(dǎo)致死循環(huán)

使用節(jié)點(diǎn)的children屬性,而不是childNodes屬性,前者訪(fǎng)問(wèn)速度更快,且不包含空白文本和注釋節(jié)點(diǎn)。

瀏覽器的渲染過(guò)程包括構(gòu)建DOM樹(shù)和渲染樹(shù),當(dāng)DOM元素的幾何屬性變化時(shí),需要重新構(gòu)造渲染樹(shù),這一過(guò)程稱(chēng)為“重排”,完成重排后,瀏覽器會(huì)重新繪制受影響的部分到屏幕中,這一過(guò)程稱(chēng)為“重繪”。因此應(yīng)該盡量合并多次對(duì)DOM的修改,或者先將元素脫離文檔流(display:none、文檔片段),應(yīng)用修改后,再插入文檔中。

每次瀏覽器的重排時(shí)都會(huì)產(chǎn)生消耗,大多數(shù)瀏覽器會(huì)通過(guò)隊(duì)列化修改并批量執(zhí)行來(lái)優(yōu)化重排過(guò)程,可當(dāng)訪(fǎng)問(wèn)元素offsetTop、scrollTop、clientTop、getComputedStyle等一系列布局屬性時(shí),會(huì)強(qiáng)制瀏覽器立即進(jìn)行重排返回正確的值。因此不要在dom布局信息改變時(shí),訪(fǎng)問(wèn)這些布局屬性。

當(dāng)修改同個(gè)元素多個(gè)Css屬性時(shí),可以使用CssText屬性進(jìn)行一次性修改樣式,減少瀏覽器重排和重繪的次數(shù)

當(dāng)元素發(fā)生動(dòng)畫(huà)時(shí),可以使用絕對(duì)定位使其脫離文檔流,動(dòng)畫(huà)結(jié)束后,再恢復(fù)定位。避免動(dòng)畫(huà)過(guò)程中瀏覽器反復(fù)重排文檔流中的元素。

多使用事件委托,減少監(jiān)聽(tīng)事件

4.算法和流程控制

for循環(huán)和while循環(huán)性能差不多,除了for-in循環(huán)最慢(其要遍歷原型鏈)

循環(huán)中要減少對(duì)象成員及數(shù)組項(xiàng)的查詢(xún)次數(shù),可以通過(guò)倒序循環(huán)提高性能

循環(huán)次數(shù)大于1000時(shí),可運(yùn)用Duff Devices減少迭代次數(shù)

switch比if-else快,但如果具有很多離散值時(shí),可使用數(shù)組或?qū)ο髞?lái)構(gòu)建查找表

遞歸可能會(huì)造成調(diào)用棧溢出,可將其改為循環(huán)迭代

如果可以,對(duì)一些函數(shù)的計(jì)算結(jié)果進(jìn)行緩存

5.字符串和正則表達(dá)式

進(jìn)行大量字符串的連接時(shí),+和+=效率比數(shù)組的join方法要高

當(dāng)創(chuàng)建了一個(gè)正則表達(dá)式對(duì)象時(shí),瀏覽器會(huì)驗(yàn)證你的表達(dá)式,然后將其轉(zhuǎn)化為一個(gè)原生代碼程序,用戶(hù)執(zhí)行匹配工作。當(dāng)你將其賦值給變量時(shí),可以避免重復(fù)執(zhí)行該步驟。

當(dāng)正則進(jìn)入使用狀態(tài)時(shí),首先要確定目標(biāo)字符串的起始搜索位置(字符串的起始位置或正則表達(dá)式的lastIndex屬性),之后正則表達(dá)式會(huì)逐個(gè)檢查文本和正則模式,當(dāng)一個(gè)特定的字元匹配失敗時(shí),正則表達(dá)式會(huì)試著回溯到之前嘗試匹配的位置,然后嘗試其他路徑。如果正則表達(dá)式所有的可能路徑都沒(méi)有匹配到,其會(huì)將起始搜索位置下移一位,重新開(kāi)始檢查。如果字符串的每個(gè)字符都經(jīng)歷過(guò)檢查,沒(méi)有匹配成功,則宣布徹底失敗。

當(dāng)正則表達(dá)式不那么具體時(shí),例如.和[\s\S]等,很可能會(huì)出現(xiàn)回溯失控的情況,在js中可以應(yīng)用預(yù)查模擬原子組(?=(pattern))\1來(lái)避免不必要的回溯。除此之外,嵌套的量詞,例如/(A+A+)+B/在匹配"AAAAAAAA"時(shí)可能會(huì)造成驚人的回溯,應(yīng)盡量避免使用嵌套的量詞或使用預(yù)查模擬原子組消除回溯問(wèn)題。

將復(fù)雜的正則表達(dá)式拆分為多個(gè)簡(jiǎn)單的片段、正則以簡(jiǎn)單、必需的字元開(kāi)始、減少分支數(shù)量|,有助于提高匹配的效率。

6.快速響應(yīng)的用戶(hù)界面

  • 單個(gè)JavaScript運(yùn)算操作時(shí)間不應(yīng)該超出100ms,否則可能會(huì)阻塞用戶(hù)操作
  • 如果要執(zhí)行長(zhǎng)時(shí)間的運(yùn)算,可以通過(guò)定時(shí)器將計(jì)算過(guò)程分割成多個(gè)步驟,使UI可以得到更新,例如
setTimeout(function(){
    process(todo.shift());

    if (todo.length > 0) {
        setTimeout(arguments.callee, 25);
    } else {
        callback();
    }
})




較長(zhǎng)時(shí)間的計(jì)算過(guò)程也可以按照代碼運(yùn)行的時(shí)間進(jìn)行分割,每次控制運(yùn)行的時(shí)間,例如

setTimeout(function(){
    let start = +new Date();
    do {
        process(todo.shift());
    } while(todo.length > 0 && (+new Date() - start) < 50)

    if (todo.length > 0) {
        setTimeout(arguments.callee, 25);
    } else {
        callback();
    }
})


  • 高頻率重復(fù)的定時(shí)器數(shù)量盡量要少,建議使用一個(gè)獨(dú)立的重復(fù)定時(shí)器
  • 使用WebWork進(jìn)行計(jì)算

7. AJAX

  • 設(shè)置HTTP頭部信息進(jìn)行緩存,例如
Expires: Mon,28 Jul 2018 23:30:30 GMT


  • 對(duì)于一些函數(shù)的計(jì)算結(jié)果進(jìn)行本地緩存

8. 編程實(shí)踐

  • 避免使用eval、Function進(jìn)行雙重求值
  • 使用Object/Array字面量定義,不要使用構(gòu)造函數(shù)
  • 使用延遲加載消除函數(shù)中重復(fù)的工作
  • 使用位操作,例如與1進(jìn)行按位與計(jì)算,得到奇偶交替


if (i & 1) {
    className = 'odd';
} else {
    className = 'even';
}   


  • 多使用JS內(nèi)置的原生方法,例如Math對(duì)象等

9.構(gòu)建和部署

  • 合并、壓縮多個(gè)js文件
  • 設(shè)置HTTP緩存
  • 使用內(nèi)容分發(fā)網(wǎng)絡(luò)CDN

10.性能分析工具

————————————————
版權(quán)聲明:本文為CSDN博主「PAT-python-zjw」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/zjw_python/java/article/details/105293878

修復(fù)一個(gè)因?yàn)?scrollbar 占據(jù)空間導(dǎo)致的 bug

seo達(dá)人

背景

這一個(gè)因?yàn)闈L動(dòng)條占據(jù)空間引起的bug, 查了一下資料, 最后也解決了,順便研究一下這個(gè)屬性, 做一下總結(jié),分享給大家看看。


正文

昨天, 測(cè)試提了個(gè)問(wèn)題, 現(xiàn)象是一個(gè)輸入框的聚焦提示偏了, 讓我修一下, 如下圖:


image.png


起初認(rèn)為是紅框提示位置不對(duì), 就去找代碼看:


<Input

 // ...

 onFocus={() => setFocusedInputName('guidePrice')}

 onBlur={() => setFocusedInputName('')}

/>


<Table

 data-focused-column={focusedInputName}

 // ...

/>

代碼上沒(méi)有什么問(wèn)題, 不是手動(dòng)設(shè)置的,而且, 在我和另一個(gè)同事, 還有PM的PC上都是OK的:


image.png


初步判斷是,紅框位置結(jié)算有差異, 差異大小大概是17px, 但是這個(gè)差異是怎么產(chǎn)生的呢?


就去測(cè)試小哥的PC上看, 注意到一個(gè)細(xì)節(jié), 在我PC上, 滾動(dòng)條是懸浮的:

image.png


在他PC上, 滾動(dòng)條是占空間的:


image.png


在他電腦上, 手動(dòng)把原本的 overscroll-y: scroll 改成 overscroll-y: overlay 問(wèn)題就結(jié)局了。


由此判定是: 滾動(dòng)條占據(jù)空間 引起的bug。


overscroll-y: overlay

CSS屬性 overflow, 定義當(dāng)一個(gè)元素的內(nèi)容太大而無(wú)法適應(yīng)塊級(jí)格式化上下文的時(shí)候該做什么。它是 overflow-x 和overflow-y的 簡(jiǎn)寫(xiě)屬性 。

/* 默認(rèn)值。內(nèi)容不會(huì)被修剪,會(huì)呈現(xiàn)在元素框之外 */

overflow: visible;


/* 內(nèi)容會(huì)被修剪,并且其余內(nèi)容不可見(jiàn) */

overflow: hidden;


/* 內(nèi)容會(huì)被修剪,瀏覽器會(huì)顯示滾動(dòng)條以便查看其余內(nèi)容 */

overflow: scroll;


/* 由瀏覽器定奪,如果內(nèi)容被修剪,就會(huì)顯示滾動(dòng)條 */

overflow: auto;


/* 規(guī)定從父元素繼承overflow屬性的值 */

overflow: inherit;

官方描述:

overlay  行為與 auto 相同,但滾動(dòng)條繪制在內(nèi)容之上而不是占用空間。 僅在基于 WebKit(例如,Safari)和基于Blink的(例如,Chrome或Opera)瀏覽器中受支持。

表現(xiàn):

html {

 overflow-y: overlay;

}

兼容性

沒(méi)有在caniuse上找到這個(gè)屬性的兼容性, 也有人提這個(gè)問(wèn)題:


image.png


問(wèn)題場(chǎng)景以及解決辦法

1. 外部容器的滾動(dòng)條

這里的外部容器指的是html, 直接加在最外層:


html {

 overflow-y: scroll;

}

手動(dòng)加上這個(gè)特性, 不論什么時(shí)候都有滾動(dòng)寬度占據(jù)空間。


缺點(diǎn): 沒(méi)有滾動(dòng)的時(shí)候也會(huì)有個(gè)滾動(dòng)條, 不太美觀(guān)。


優(yōu)點(diǎn): 方便, 沒(méi)有兼容性的問(wèn)題。


2. 外部容器絕對(duì)定位法

用絕對(duì)定位,保證了body的寬度一直保持完整空間:


html {

 overflow-y: scroll; // 兼容ie8,不支持:root, vw

}


:root {

 overflow-y: auto;

 overflow-x: hidden;

}


:root body {

 position: absolute;

}


body {

 width: 100vw;

 overflow: hidden;

}

3. 內(nèi)部容器做兼容


.wrapper {

   overflow-y: scroll; // fallback

   overflow-y: overlay;

}

總結(jié)

個(gè)人推薦還是用 overlay, 然后使用scroll 做為兜底。


內(nèi)容就這么多, 希望對(duì)大家有所啟發(fā)。


文章如有錯(cuò)誤, 請(qǐng)?jiān)诹粞詤^(qū)指正, 謝謝。

將 Gatsby 項(xiàng)目遷移到 TypeScript

seo達(dá)人

之前花了些時(shí)間將gatsby-theme-gitbook遷移到 Typescript,以獲得在 VSCode 中更好的編程體驗(yàn).

整體差不多已經(jīng)完成遷移,剩下將 Gatsby 的 API 文件也遷移到 TS,這里可以看到 gatsby#21995 官方也在將核心代碼庫(kù)遷移到 Typescript,準(zhǔn)備等待官方將核心代碼庫(kù)遷移完成,在遷移 API 文件.


這篇文章用XYShaoKang/gatsby-project-config,演示如何將 gatsby 遷移到 TypeScript,希望能幫到同樣想要在 Gatsby 中使用 TS 的同學(xué).


遷移步驟:


TS 配置

配置 ESLint 支持 TS

完善 GraphQL 類(lèi)型提示

初始化項(xiàng)目

gatsby new gatsby-migrate-to-typescript XYShaoKang/gatsby-project-config

cd gatsby-migrate-to-typescript

yarn develop

TS 配置

安裝typescript

添加typescript.json配置文件

修改 js 文件為 tsx

補(bǔ)全 TS 聲明定義

安裝typescript

yarn add -D typescript

添加配置文件tsconfig.json

// https://www.typescriptlang.org/v2/docs/handbook/tsconfig-json.html

{

 "compilerOptions": {

   "target": "esnext", // 編譯生成的目標(biāo) es 版本,可以根據(jù)需要設(shè)置

   "module": "esnext", // 編譯生成的目標(biāo)模塊系統(tǒng)

   "lib": ["dom", "es2015", "es2017"], // 配置需要包含的運(yùn)行環(huán)境的類(lèi)型定義

   "jsx": "react", // 配置 .tsx 文件的輸出模式

   "strict": true, // 開(kāi)啟嚴(yán)格模式

   "esModuleInterop": true, // 兼容 CommonJS 和 ES Module

   "moduleResolution": "node", // 配置模塊的解析規(guī)則,支持 node 模塊解析規(guī)則

   "noUnusedLocals": true, // 報(bào)告未使用的局部變量的錯(cuò)誤

   "noUnusedParameters": true, // 報(bào)告有關(guān)函數(shù)中未使用參數(shù)的錯(cuò)誤

   "experimentalDecorators": true, // 啟用裝飾器

   "emitDecoratorMetadata": true, // 支持裝飾器上生成元數(shù)據(jù),用來(lái)進(jìn)行反射之類(lèi)的操作

   "noEmit": true, // 不輸出 js,源映射或聲明之類(lèi)的文件,單純用來(lái)檢查錯(cuò)誤

   "skipLibCheck": true // 跳過(guò)聲明文件的類(lèi)型檢查,只會(huì)檢查已引用的部分

 },

 "exclude": ["./node_modules", "./public", "./.cache"], // 解析時(shí),應(yīng)該跳過(guò)的路晉

 "include": ["src"] // 定義包含的路徑,定義在其中的聲明文件都會(huì)被解析進(jìn) vscode 的智能提示

}

將index.js改成index.tsx,重新啟動(dòng)服務(wù),查看效果.


其實(shí) Gatsby 內(nèi)置了支持 TS,不用其他配置,只要把index.js改成index.tsx就可以直接運(yùn)行.添加 TS 依賴(lài)是為了顯示管理 TS,而tsconfig.json也是這個(gè)目的,當(dāng)我們有需要新的特性以及自定義配置時(shí),可以手動(dòng)添加.

補(bǔ)全 TS 聲明定義

打開(kāi)index.tsx,VSCode 會(huì)報(bào)兩個(gè)錯(cuò)誤,一個(gè)是找不到styled-components的聲明文件,這個(gè)可以通過(guò)安裝@types/styled-components來(lái)解決.

另外一個(gè)錯(cuò)誤綁定元素“data”隱式具有“any”類(lèi)型。,這個(gè)錯(cuò)誤是因?yàn)槲覀冊(cè)趖sconfig.json中指定了"strict": true,這會(huì)開(kāi)啟嚴(yán)格的類(lèi)型檢查,可以通過(guò)關(guān)閉這個(gè)選項(xiàng)來(lái)解決,只是我們用 TS 就是要用它的類(lèi)型檢查的,所以正確的做法是給data定義類(lèi)型.

下面來(lái)一一修復(fù)錯(cuò)誤.


安裝styled-components的聲明文件


yarn add -D @types/styled-components

修改index.tsx


import React, { FC } from 'react'

import styled from 'styled-components'

import { graphql } from 'gatsby'

import { HomeQuery } from './__generated__/HomeQuery'


const Title = styled.h1`

 font-size: 1.5em;

 margin: 0;

 padding: 0.5em 0;

 color: palevioletred;

 background: papayawhip;

`


const Content = styled.div`

 margin-top: 0.5em;

`


interface PageQuery {

 data: {

   allMarkdownRemark: {

     edges: Array<{

       node: {

         frontmatter: {

           title: string

         }

         excerpt: string

       }

     }>

   }

 }

}


const Home: FC<PageQuery> = ({ data }) => {

 const node = data.allMarkdownRemark.edges[0].node


 const title = node.frontmatter?.title

 const excerpt = node.excerpt


 return (

   <>

     <Title>{title}</Title>

     <Content>{excerpt}</Content>

   </>

 )

}


export default Home


export const query = graphql`

 query HomeQuery {

   allMarkdownRemark {

     edges {

       node {

         frontmatter {

           title

         }

         excerpt

       }

     }

   }

 }

`

這時(shí)候會(huì)出現(xiàn)一個(gè)新的錯(cuò)誤,在excerpt: string處提示Parsing error: Unexpected token,這是因?yàn)?ESLint 還無(wú)法識(shí)別 TS 的語(yǔ)法,下面來(lái)配置 ESLint 支持 TS.


配置 ESLint 支持 TypeScript

安裝依賴(lài)


yarn add -D @typescript-eslint/parser @typescript-eslint/eslint-plugin

配置.eslintrc.js


module.exports = {

 parser: `@typescript-eslint/parser`, // 將解析器從`babel-eslint`替換成`@typescript-eslint/parser`,用以解析 TS 代碼

 extends: [

   `google`,

   `eslint:recommended`,

   `plugin:@typescript-eslint/recommended`, // 使用 @typescript-eslint/eslint-plugin 推薦配置

   `plugin:react/recommended`,

   `prettier/@typescript-eslint`, // 禁用 @typescript-eslint/eslint-plugin 中與 prettier 沖突的規(guī)則

   `plugin:prettier/recommended`,

 ],

 plugins: [

   `@typescript-eslint`, // 處理 TS 語(yǔ)法規(guī)則

   `react`,

   `filenames`,

 ],

 // ...

}

在.vscode/settings.json中添加配置,讓VSCode使用ESLint擴(kuò)展格式化ts和tsx文件


// .vscode/settings.json

{

 "eslint.format.enable": true,

 "[javascript]": {

   "editor.defaultFormatter": "dbaeumer.vscode-eslint"

 },

 "[javascriptreact]": {

   "editor.defaultFormatter": "dbaeumer.vscode-eslint"

 },

 "[typescript]": {

   "editor.defaultFormatter": "dbaeumer.vscode-eslint"

 },

 "[typescriptreact]": {

   "editor.defaultFormatter": "dbaeumer.vscode-eslint"

 }

}

完善 GraphQL 類(lèi)型提示

// index.tsx

import React, { FC } from 'react'

// ...

interface PageQuery {

 data: {

   allMarkdownRemark: {

     edges: Array<{

       node: {

         frontmatter: {

           title: string

         }

         excerpt: string

       }

     }>

   }

 }

}


const Home: FC<PageQuery> = ({ data }) => {

 // ...

}


export default Home


export const query = graphql`

 query HomeQuery {

   allMarkdownRemark {

     edges {

       node {

         frontmatter {

           title

         }

         excerpt

       }

     }

   }

 }

`

我們看看index.tsx文件,會(huì)發(fā)現(xiàn)PropTypes和query結(jié)構(gòu)非常類(lèi)似,在Gatsby運(yùn)行時(shí),會(huì)把query查詢(xún)的結(jié)果作為組件prop.data傳入組件,而PropTypes是用來(lái)約束prop存在的.所以其實(shí)PropTypes就是根據(jù)query寫(xiě)出來(lái)的.


如果有依據(jù)query自動(dòng)生成PropTypes的功能就太棒了.

另外一個(gè)問(wèn)題是在query中編寫(xiě)GraphQL查詢(xún)時(shí),并沒(méi)有類(lèi)型約束,也沒(méi)有智能提示.


總結(jié)以下需要完善的體驗(yàn)包括:


GraphQL 查詢(xún)編寫(xiě)時(shí)的智能提示,以及錯(cuò)誤檢查

能夠從 GraphQL 查詢(xún)生成對(duì)應(yīng)的 TypeScript 類(lèi)型.這樣能保證類(lèi)型的唯一事實(shí)來(lái)源,并消除 TS 中冗余的類(lèi)型聲明.畢竟如果經(jīng)常需要手動(dòng)更新兩處類(lèi)型,會(huì)更容易出錯(cuò),而且也并不能保證手動(dòng)定義類(lèi)型的正確性.

實(shí)現(xiàn)方式:


通過(guò)生成架構(gòu)文件,配合Apollo GraphQL for VS Code插件,實(shí)現(xiàn)智能提示,以及錯(cuò)誤檢查

通過(guò)graphql-code-generator或者apollo生成 TS 類(lèi)型定義文件

如果自己去配置的話(huà),是挺耗費(fèi)時(shí)間的,需要去了解graphql-code-generator的使用,以及Apollo的架構(gòu)等知識(shí).

不過(guò)好在社區(qū)中已經(jīng)有對(duì)應(yīng)的 Gatsby 插件集成了上述工具可以直接使用,能讓我們不用去深究對(duì)應(yīng)知識(shí)的情況下,達(dá)到優(yōu)化 GraphQL 編程的體驗(yàn).

嘗試過(guò)以下兩個(gè)插件能解決上述問(wèn)題,可以任選其一使用


gatsby-plugin-codegen

gatsby-plugin-typegen

另外還有一款插件gatsby-plugin-graphql-codegen也可以生成 TS 類(lèi)型,不過(guò)配置略麻煩,并且上述兩個(gè)插件都可以滿(mǎn)足我現(xiàn)在的需求,所以沒(méi)有去嘗試,感興趣的可以嘗試一下.


注意點(diǎn):


Apollo不支持匿名查詢(xún),需要使用命名查詢(xún)

第一次生成,需要運(yùn)行Gatsby之后才能生成類(lèi)型文件

整個(gè)項(xiàng)目?jī)?nèi)不能有相同命名的查詢(xún),不然會(huì)因?yàn)槊钟袥_突而生成失敗

下面是具體操作


安裝vscode-apollo擴(kuò)展

在 VSCode 中按 Ctrl + P ( MAC 下: Cmd + P) 輸入以下命令,按回車(chē)安裝


ext install apollographql.vscode-apollo

方式一: 使用gatsby-plugin-codegen

gatsby-plugin-codegen默認(rèn)會(huì)生成apollo.config.js和schema.json,配合vscode-apollo擴(kuò)展,可以提供GraphQL的類(lèi)型約束和智能提示.

另外會(huì)自動(dòng)根據(jù)query中的GraphQL查詢(xún),生成 TS 類(lèi)型,放在對(duì)應(yīng)的tsx文件同級(jí)目錄下的__generated__文件夾,使用時(shí)只需要引入即可.

如果需要在運(yùn)行時(shí)自動(dòng)生成 TS 類(lèi)型,需要添加watch: true配置.


安裝gatsby-plugin-codegen


yarn add gatsby-plugin-codegen

配置gatsby-config.js


// gatsby-config.js

module.exports = {

 plugins: [

   // ...

   {

     resolve: `gatsby-plugin-codegen`,

     options: {

       watch: true,

     },

   },

 ],

}

重新運(yùn)行開(kāi)發(fā)服務(wù)生成類(lèi)型文件


yarn develop

如果出現(xiàn)以下錯(cuò)誤,一般是因?yàn)闆](méi)有為查詢(xún)命名的緣故,給查詢(xún)添加命名即可,另外配置正確的話(huà),打開(kāi)對(duì)應(yīng)的文件,有匿名查詢(xún),編輯器會(huì)有錯(cuò)誤提示.


fix-anonymous-operations.png


這個(gè)命名之后會(huì)作為生成的類(lèi)型名.


修改index.tsx以使用生成的類(lèi)型


gatsby-plugin-codegen插件會(huì)更具查詢(xún)生成對(duì)應(yīng)的查詢(xún)名稱(chēng)的類(lèi)型,保存在對(duì)應(yīng)tsx文件同級(jí)的__generated__目錄下.


import { HomeQuery } from './__generated__/HomeQuery' // 引入自動(dòng)生成的類(lèi)型

// ...


// interface PageQuery {

//   data: {

//     allMarkdownRemark: {

//       edges: Array<{

//         node: {

//           frontmatter: {

//             title: string

//           }

//           excerpt: string

//         }

//       }>

//     }

//   }

// }


interface PageQuery {

 data: HomeQuery // 替換之前手寫(xiě)的類(lèi)型

}


// ...

將自動(dòng)生成的文件添加到.gitignore中


apollo.config.js,schema.json,__generated__能通過(guò)運(yùn)行時(shí)生成,所以可以添加到.gitignore中,不用提交到 git 中.當(dāng)然如果有需要也可以選擇提交到 git 中.

# Generated types by gatsby-plugin-codegen

__generated__

apollo.config.js

schema.json

方式二: 使用gatsby-plugin-typegen

gatsby-plugin-typegen通過(guò)配置生成gatsby-schema.graphql和gatsby-plugin-documents.graphql配合手動(dòng)創(chuàng)建的apollo.config.js提供GraphQL的類(lèi)型約束和智能提示.

根據(jù)GraphQL查詢(xún)生成gatsby-types.d.ts,生成的類(lèi)型放在命名空間GatsbyTypes下,使用時(shí)通過(guò)GatsbyTypes.HomeQueryQuery來(lái)引入,HomeQueryQuery是由對(duì)應(yīng)的命名查詢(xún)生成


安裝gatsby-plugin-typegen


yarn add gatsby-plugin-typegen

配置


// gatsby-config.js

module.exports = {

 plugins: [

   // ...

   {

     resolve: `gatsby-plugin-typegen`,

     options: {

       outputPath: `src/__generated__/gatsby-types.d.ts`,

       emitSchema: {

         'src/__generated__/gatsby-schema.graphql': true,

       },

       emitPluginDocuments: {

         'src/__generated__/gatsby-plugin-documents.graphql': true,

       },

     },

   },

 ],

}

//apollo.config.js

module.exports = {

 client: {

   tagName: `graphql`,

   includes: [

     `./src/**/*.{ts,tsx}`,

     `./src/__generated__/gatsby-plugin-documents.graphql`,

   ],

   service: {

     name: `GatsbyJS`,

     localSchemaFile: `./src/__generated__/gatsby-schema.graphql`,

   },

 },

}

重新運(yùn)行開(kāi)發(fā)服務(wù)生成類(lèi)型文件


yarn develop

修改index.tsx以使用生成的類(lèi)型


gatsby-plugin-codegen插件會(huì)更具查詢(xún)生成對(duì)應(yīng)的查詢(xún)名稱(chēng)的類(lèi)型,保存在對(duì)應(yīng)tsx文件同級(jí)的__generated__目錄下.


// ...


// interface PageQuery {

//   data: {

//     allMarkdownRemark: {

//       edges: Array<{

//         node: {

//           frontmatter: {

//             title: string

//           }

//           excerpt: string

//         }

//       }>

//     }

//   }

// }


interface PageQuery {

 data: GatsbyTypes.HomeQueryQuery // 替換之前手寫(xiě)的類(lèi)型

}


// ...

將自動(dòng)生成的文件添加到.gitignore中


__generated__能通過(guò)運(yùn)行時(shí)生成,所以可以添加到.gitignore中,不用提交到 git 中.當(dāng)然如果有需要也可以選擇提交到 git 中.

# Generated types by gatsby-plugin-codegen

__generated__

日歷

鏈接

個(gè)人資料

存檔