Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 欧美日韩中文,精品午夜寂寞黄网站在线,成人精品视频一区二区在线

          整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          Web 開發(fā)的最佳 ChatGPT 提示

          Web 開發(fā)的最佳 ChatGPT 提示

          適用于所有編碼員。無論您是剛剛開始并想要備忘單還是想了解更高級的內(nèi)容,這些針對 Web 開發(fā)的最佳 ChatGPT 提示都能滿足您的需求。

          1.使ChatGPT成為Python解釋器

          我希望你表現(xiàn)得像一個 Python 解釋器。我會給你Python代碼,你來執(zhí)行它。不提供任何解釋。除了代碼的輸出之外,不要回復(fù)任何內(nèi)容。第一個代碼是:“print('hello world!')”

          2.使ChatGPT成為Linux終端

          我希望你充當(dāng) Linux 終端。我將輸入命令,您將回復(fù)終端應(yīng)顯示的內(nèi)容。我希望您僅使用一個唯一代碼塊內(nèi)的終端輸出進(jìn)行回復(fù),而不是其他任何內(nèi)容。不要寫解釋。除非我指示您這樣做,否則請勿鍵入命令。當(dāng)我需要用英語告訴你一些事情時,我會將文本放在大括號內(nèi){就像這樣}。我的第一個命令是 pwd

          3.使ChatGPT成為Javascript控制臺

          我希望你充當(dāng) JavaScript 控制臺。我將輸入命令,您將回復(fù) javascript 控制臺應(yīng)顯示的內(nèi)容。我希望您僅使用一個唯一代碼塊內(nèi)的終端輸出進(jìn)行回復(fù),而不是其他任何內(nèi)容。不要寫解釋。除非我指示您這樣做,否則請勿鍵入命令。當(dāng)我需要用英語告訴你一些事情時,我會將文本放在大括號內(nèi){就像這樣}。我的第一個命令是 console.log(“Hello World”);

          4. 制作備忘單

          編寫 Markdown 格式備忘單。

          5. 獲取快速快捷方式

          在項(xiàng)目符號列表中,提供使用 Apple 或 Windows 設(shè)備進(jìn)行 React 的快捷方式。

          6. 調(diào)試代碼

          我想讓你調(diào)試這段代碼。該代碼應(yīng)該執(zhí)行[提供目的][在此處插入代碼]

          7. 網(wǎng)頁設(shè)計(jì)咨詢

          我希望你擔(dān)任網(wǎng)頁設(shè)計(jì)顧問。我將為您提供與需要幫助設(shè)計(jì)或重新開發(fā)其網(wǎng)站的組織相關(guān)的詳細(xì)信息,您的角色是建議最合適的界面和功能,以增強(qiáng)用戶體驗(yàn),同時滿足公司的業(yè)務(wù)目標(biāo)。您應(yīng)該利用您對 UX/UI 設(shè)計(jì)原則、編碼語言、網(wǎng)站開發(fā)工具等的知識,為項(xiàng)目制定全面的計(jì)劃。我的第一個請求是“我需要幫助創(chuàng)建一個銷售珠寶的電子商務(wù)網(wǎng)站。”

          8. 讓 ChatGPT 成為 SQL 終端

          我希望您在示例數(shù)據(jù)庫前充當(dāng) SQL 終端。該數(shù)據(jù)庫包含名為“產(chǎn)品”、“用戶”、“訂單”和“供應(yīng)商”的表。我將輸入查詢,您將用終端顯示的內(nèi)容進(jìn)行回復(fù)。我希望您在單個代碼塊中回復(fù)查詢結(jié)果表,而不是其他任何內(nèi)容。不要寫解釋。除非我指示您這樣做,否則請勿鍵入命令。當(dāng)我需要用英語告訴你一些事情時,我會在大括號中這樣做(就像這樣)。我的第一個命令是“SELECT TOP 10 * FROM Products ORDER BY Id DESC”

          9. 建議框架

          您能為我的網(wǎng)站推薦一個合適的前端框架嗎?我正在制作一個電子商務(wù)網(wǎng)站。

          10.打字稿功能

          創(chuàng)建一個 TypeScript 函數(shù),使用 Black-Scholes 模型計(jì)算隱含波動率。輸入為標(biāo)的價格、執(zhí)行價格、無風(fēng)險利率和期權(quán)價格。一步步寫下來,并為每個步驟提供解釋。

          11. 讓 ChatGPT 解釋你的代碼

          你現(xiàn)在是MetaGPT,你的工作就是用一個創(chuàng)造性的、直觀的類比向我解釋一堆代碼。每當(dāng)我在這里發(fā)布代碼片段或問題時,您都會用一個非常有創(chuàng)意的類比與現(xiàn)實(shí)世界的對象進(jìn)行比較來說明問題。然后,您可以引導(dǎo)我了解如何解決問題,或者當(dāng)前代碼如何解決問題,使用您的類比元素來幫助您進(jìn)行解釋。每當(dāng)您認(rèn)為可以為解釋增加價值時,請不要忘記用易于理解的類比來說明您的解釋。確保像世界上最偉大的老師那樣教授這些東西。假設(shè)你所教的人不是那么聰明,那么,找到一種說明性的方式向他們解釋。如果您明白,請立即回復(fù):“MetaGPT:嘿,我是 MetaGPT!

          12. 翻譯代碼

          您是一名專業(yè)軟件工程師,掌握了 Python 和 Rust 編程語言。在這種情況下,您將幫助我將用 Python 編寫的代碼庫翻譯為 Rust。

          13. 編寫單元測試

          為我編寫以下 Python 函數(shù)的單元測試

          源:CSDN;

          鏈接:https://mp.weixin.qq.com/s/7orC7SMDzLHpZCqhy24BwA

          @開發(fā)者,在后端僅提供原始數(shù)據(jù)集的情況下,如何讓所有搜索邏輯都在前端完成?不僅如此,還要能夠?qū)崿F(xiàn)預(yù)測用戶輸入、忽略錯別字輸入、快速自動補(bǔ)齊等智能功能?本文的作者就深入 JavaScript 技術(shù)特性,談一談 React、性能優(yōu)化以及多線程那些事兒。



          作者 | Leo Fabrikant

          譯者 | 彎月,責(zé)編 | 郭芮

          出品 | CSDN(ID:CSDNnews)

          以下為譯文:


          第一篇 問題闡述


          我有一個看似很簡單的任務(wù):“有一個從后端檢索的數(shù)據(jù)集,其中包含13,000個數(shù)據(jù)項(xiàng),每一項(xiàng)都是冗長羅嗦的名稱(科學(xué)組織)。使用這些數(shù)據(jù)創(chuàng)建一個帶自動補(bǔ)齊功能的搜索欄。”

          你也不覺得很難,對不對?

          難點(diǎn)1:

          不能使用后端。后端只能提供原始的數(shù)據(jù)集,所有搜索邏輯都必須在前端完成。

          難點(diǎn)2:

          開發(fā)人員(我):“這些組織名稱這么長,需要花點(diǎn)心思。如果我們只是運(yùn)行簡單的字符串匹配,而且用戶可能會輸錯或出現(xiàn)拼寫錯誤,那么就很難搜索到任何結(jié)果。”

          客戶:“你說的對,我們必須加入一些智能的功能,預(yù)測用戶的輸入,忽略輸入的錯別字。”

          注意:一般情況下,我不建議你在未經(jīng)項(xiàng)目經(jīng)理同意下,提示客戶沒有提到的復(fù)雜功能!這種情況稱為特征蔓延(feature creep)。在上述例子中,只是恰巧我一個人負(fù)責(zé)這個合同,我有足夠的精力,所以我認(rèn)為這是一個有趣的挑戰(zhàn)。

          難點(diǎn)3:

          這是最大的難點(diǎn)。我選擇的智能(也稱為“模糊”)搜索引擎非常慢……

          隨著搜索詞的長度加長,這個搜索算法庫的搜索時間會迅速增加。此外,看看下面這個龐大的列表,里面的數(shù)據(jù)項(xiàng)極其冗長,用戶需要輸入一個很長的搜索詞才能出現(xiàn)自動補(bǔ)齊提示。別無他法,搜索引擎跟不上用戶的打字速度,這個UI可能會廢掉。




          我不知道是不是因?yàn)槲疫x擇的這個搜索算法庫太過糟糕,我也不知道是不是因?yàn)樗小澳:彼阉魉惴ǘ际沁@樣的情形。但幸運(yùn)的是,我沒有費(fèi)心去尋找其他選擇。

          盡管上述難點(diǎn)大多數(shù)是我自己強(qiáng)加上去的,但我依然決定堅(jiān)持到底,努力優(yōu)化這種實(shí)現(xiàn)。雖然我知道這可能不是最佳策略,但是這個項(xiàng)目的情況允許我這么做,結(jié)果將說明一切,最重要的是,對我來說這是一次絕佳的學(xué)習(xí)體驗(yàn)和成長的機(jī)會。



          第二篇 問題剖析


          在第一個實(shí)現(xiàn)中,我使用了UI的react-autocomplete和react-virtualized庫。

          render () {
           return (
           <Autocomplete
           value={this.state.searchTerm}
           items={this.getSearchResults()}
           renderMenu={this.reactVirtualizedList}
           renderItem={this.renderItem}
           getItemValue={ item=> item.name }
           onChange={(e, value)=> this.setState({searchTerm: value})}
           />
           )
           }
          


          Autocomplete組件需要傳遞以下幾項(xiàng):value屬性,需要傳入輸入框中的searchTeam;items屬性,傳入搜索結(jié)果;以及renderMenu函數(shù),該函數(shù)將搜索結(jié)果列表傳遞給react-vertualized。

          react-virtualized能夠很好地處理大型列表(它只會渲染列表顯示在屏幕上的一小部分,只有滾動列表時才會更新)。考慮到我們需要渲染的組件并不多,我認(rèn)為應(yīng)該不會有太嚴(yán)重的性能問題。

          更新操作的聲明周期也很簡單:

          • 用戶輸入'a'
          • Autocomplete的onChange處理函數(shù)觸發(fā)一次渲染,此時this.state.searchTeam='a'
          • 該渲染會調(diào)用getSearchResults方法,在這個方法中,搜索引擎使用'a'作為搜索關(guān)鍵字計(jì)算搜索結(jié)果,然后將結(jié)果傳遞給react-virtualized負(fù)責(zé)渲染。


          getSearchResults=()=> {
           const {searchTerm}=this.state;
           return searchTerm ? this.searchEngine.search(searchTerm) : []
           // searchEngine.search is the expensive search algorithm
          };
          


          我們來看看結(jié)果如何:




          哎呀……很糟。在按住刪除鍵時的的確確能感覺到UI的停頓,因?yàn)殒I盤觸發(fā)delete事件太快了。

          不過至少模糊搜索好用了:'anerican'正確地解釋成了'American'。但隨著搜索關(guān)鍵字的加長,兩個元素(輸入框和搜索結(jié)果)的渲染過程完全跟不上用戶輸入的速度,延遲非常大。

          盡管我們的搜索算法的確很慢,但如此大的延遲并不是由于單次搜索時間太長導(dǎo)致的。這里還有另外一個現(xiàn)象需要理解:我們管它叫UI阻塞。理解這個現(xiàn)象需要深入了解Chrome的DevTools性能評測工具。

          性能評測

          可能你不熟悉這個工具,但是你要知道,熟練使用性能評測工具是深入理解JavaScript的最好方式。這不僅因?yàn)樗芴峁┯杏玫男畔湍憬鉀Q各種問題,而且按照時間顯示出JavaScript的執(zhí)行過程能夠幫助你更好地理解好的UI、React、JavaScript事件循環(huán)、同步異步執(zhí)行等概念。

          在下面每一節(jié),我都會給出性能分析過程,以及從這些過程中推斷出的有趣的結(jié)論。這些數(shù)據(jù)可能會讓你眼花繚亂,但我會盡力給出合理的解釋!

          首先評測一下在Autocomplete中按下兩個鍵時的情況:



          X軸:時間,Y軸:按照類型排列的事件(用戶輸入、主線程函數(shù)調(diào)用)


          理解基本的序列非常重要:

          首先是用戶的鍵盤輸入(Key Characer)。這些輸入在JavaScript的主線程上觸發(fā)了Event(keypress)事件,該事件又觸發(fā)了我們的onChange處理函數(shù),該函數(shù)會調(diào)用setState(圖上看不見,因?yàn)樗×耍奈恢迷谡麄€棧的最開頭附近)。這一系列動作標(biāo)志著重新計(jì)算的開始,計(jì)算setState對組件會產(chǎn)生何種影響。這一過程稱為更新,或者叫做重新渲染,它會調(diào)用幾個React生命周期方法,其中就包括render。這一切都發(fā)生在一個執(zhí)行棧(有時稱為“調(diào)用棧”或簡稱為“棧”),在圖中由每個Event (keypress)下方豎直排列的框表示。每個框都是執(zhí)行棧中的一次函數(shù)調(diào)用。

          這里不要被render這個詞誤導(dǎo)了。React的渲染并不僅僅是在屏幕上繪制。渲染只是React用來計(jì)算被更新的元素應(yīng)當(dāng)如何顯示的過程。如果仔細(xì)查看第二個Event (keypress),就會發(fā)現(xiàn)在Event (keypress)框的外面有個小得幾乎看不見的綠條。放大一些就能看到這是對瀏覽器繪制的調(diào)用:




          這才是UI更新被真正繪制到屏幕上,并在顯示器上顯示新幀的過程。而且,第一個Event (keypress)之后沒有繪制,只有第二個后面才有。

          這說明,瀏覽器繪制(外觀上的更新)并不一定會在Event (keypress)事件發(fā)生并且React完成更新之后發(fā)生。

          原因是JavaScript的事件循環(huán)和JavaScript對于任務(wù)隊(duì)列的優(yōu)先級處理。在React結(jié)束計(jì)算并將更新寫入DOM之后(稱為提交階段,發(fā)生在每個執(zhí)行棧末尾的地方),你可能會以為瀏覽器應(yīng)該開始繪制,將DOM更新顯示在屏幕上。但是在繪制之前,瀏覽器會檢查JavaScript事件隊(duì)列中是否還有其他任務(wù),有的任務(wù)會比繪制更優(yōu)先執(zhí)行。

          當(dāng)JavaScript線程忙于渲染第一個keypress時,產(chǎn)生第二個用戶輸入(如鍵盤按下事件)就會出現(xiàn)這種情況(你可以看到第二個Key Character輸入發(fā)生在第一個keypress執(zhí)行棧依然在運(yùn)行時)。這就是前面提到的UI阻塞。第二個Event (keypress)阻塞了UI更新第一個keypress。

          不幸的是,這會導(dǎo)致巨大的延遲。因?yàn)殇秩颈旧矸浅Bú糠衷蚴且驗(yàn)殇秩局邪税嘿F的搜索算法),如果用戶輸入非常快,那么很大可能會在前一個執(zhí)行棧結(jié)束之前輸入新的字符。這會產(chǎn)生新的Event (keypress),并且它的優(yōu)先級比瀏覽器繪制要高。所以繪制會不斷被用戶的輸入拖延。

          不僅如此,甚至在用戶停止輸入后,隊(duì)列中依然滯留了許多keypress時間,React需要依次對每個keypress進(jìn)行計(jì)算。所以,即使在輸入結(jié)束后,也不得不等待這些針對早已不需要的searchTeams的搜索!




          注意最后一個Key Character發(fā)生后,還有4個Event (keypress)滯留,瀏覽器需要處理完所有事件才能重繪。

          改進(jìn)方法

          為了解決這個問題,重要的是要理解好的UI的要素是什么。實(shí)際上,對于每次鍵盤輸入,用戶期待的視覺反饋包括兩個獨(dú)立的要素:

          • 用戶輸入的鍵顯示在輸入框中;
          • 搜索結(jié)果根據(jù)新的searchTeam進(jìn)行更新。


          理解用戶的期望才能找到解決方案。盡管Google搜索快得像閃電一樣,但用戶無法立即收到搜索結(jié)果的事情也屢見不鮮。一些UI甚至?xí)谡埱笏阉鹘Y(jié)果時顯示加載進(jìn)度條。

          重要的是要理解,對于反應(yīng)迅速的UI而言,第一種反饋(按下的鍵顯示在輸入框中)扮演了非常重要的角色。如果UI無法做到這一點(diǎn),就會有嚴(yán)重的問題。

          查看性能評測是解決問題的第一個提示。仔細(xì)觀察這些長長的執(zhí)行棧和render中包含的昂貴的search方法,我們會發(fā)現(xiàn),更新輸入框和搜索結(jié)果的一切操作都發(fā)生在同一個執(zhí)行棧內(nèi)。所以,兩者的UI更新都會被搜索算法阻塞。

          但是,輸入框的更新不需要等待結(jié)果!它只需要知道用戶按下了哪個鍵,而不需要知道搜索結(jié)果是什么。如果我們有辦法控制事件執(zhí)行的順序,輸入框就有機(jī)會先更新UI,再去渲染搜索結(jié)果,這樣就能減少一些延遲。因此,我們的第一個優(yōu)化措施就是將輸入框的渲染從搜索結(jié)果的渲染中分離出來。

          注意:熟悉Dan Abramov在React JSConf 2018上的演講的人應(yīng)該能回憶起這個場景。在他的幻燈片中,他設(shè)計(jì)了一個昂貴的更新操作,隨著輸入值的增加,屏幕上需要渲染的組件也越來越多。這里我們遇到的困難非常相似,只不過是隨著搜索關(guān)鍵字長度的增加,單個搜索函數(shù)的復(fù)雜度會增加而已。在Dan的演講中,他演示了時間切片(Time Slicing),這個React團(tuán)隊(duì)在開發(fā)中的功能也許可以解決這個問題!我們的嘗試會以相似的方案解決問題:找到一個方法來改變渲染的順序,防止昂貴的計(jì)算再次阻塞主線程的UI更新。


          第三篇 異步渲染(componentDidUpdate)


          注意:本篇討論的優(yōu)化最后以失敗告終了,但我認(rèn)為值得講一講,因?yàn)樗軒椭覀兏玫乩斫釸eact的componentDidUpdate生命周期。如果你非常熟悉React,或者只想看看怎樣改善性能問題,那么可以直接跳到下一篇。

          拆分組件

          由于我們想把昂貴的搜索結(jié)果更新從輸入框更新中拆分出來,所以我們應(yīng)該自己設(shè)計(jì)一個組件,并放棄使用react-autocomplete庫提供的一站式解決方案Autocomplete:

          //autocomplete.js
          render () {
           return (
           <div>
           <input
           onChange={ e=> this.setState({searchTerm: e.target.value})}
           value={this.state.searchTerm}/>
           <SearchResults
           searchEngine={this.props.searchEngine}
           searchTerm={this.state.searchTerm}/>
           </div> 
           )
          }
          


          然后在SearchResults中,我們需要異步觸發(fā)searchEngine.search(searchTerm),而不應(yīng)該在更新searchTerm的同一個渲染中進(jìn)行。

          我最初的想法是利用SearchResults的componentDidUpdate,讓searchEngine異步工作,因?yàn)槁犐先ミ@個方法似乎是在更新之后異步執(zhí)行的。

          //searchResults.js
          componentDidUpdate(prevProps) {
           const {searchTerm, searchEngine}=this.props;
           if(searchTerm && searchTerm !==prevProps.searchTerm) {
           this.setState({
           searchResults: searchEngine.search(searchTerm)
           })
           }
          }
          render () {
           return <ReactVirtualizedList searchResults={this.state.searchResults}/>
          }
          


          我們將昂貴的searchEngine移動到了componentDidUpdate中,這樣就不用在render方法中調(diào)用,而是等到更新之后再執(zhí)行。我希望在輸入框更新之后的另一個執(zhí)行棧中執(zhí)行render,并兩者之間執(zhí)行一次繪制。我想象的新的更新生命周期如下:

          • 輸入框使用新的searchTerm渲染;
          • React把帶有新的searchTerm的componentDidUpdate searchEngine任務(wù)放入隊(duì)列;
          • 瀏覽器繪制輸入框的更新,然后處理隊(duì)列中的searchEngine任務(wù)。理論上,瀏覽器不會把這個任務(wù)放在繪制任務(wù)之前,因?yàn)樗簧婕坝脩艚换ナ录?/li>
          • componentDidUpdate在輸入框繪制之后執(zhí)行,計(jì)算搜索結(jié)果并生成新的更新。


          很不幸,認(rèn)為componentDidUpdate會在瀏覽器更新之后運(yùn)行是一個常見的誤解。我們來看看這個方法的性能評測:




          看到問題了嗎?componentDidUpdate跟最初的keypress事件是在同一個執(zhí)行棧上執(zhí)行的

          componentDidUpdate并不會在繪制結(jié)束后執(zhí)行,因此執(zhí)行棧跟上一篇一樣昂貴,我們只不過是將昂貴的search方法移動到了不同位置而已。盡管這個解決方案并不能改善性能,但我們可以借此機(jī)會理解React的生命周期componentDidUpdate的具體行為。

          雖然componentDidUpdate不會在另一個執(zhí)行棧上運(yùn)行,但它確實(shí)是在React更新完組件狀態(tài)并將更新后的DOM值提交之后才執(zhí)行的。盡管這些更新后的DOM值還沒有被瀏覽器繪制,它們依然反映了更新后的UI應(yīng)有的樣子。所以,任何componentDidupdate內(nèi)執(zhí)行的DOM查詢都能訪問到更新后的值。所以,組件確實(shí)更新了,只是瀏覽器中看不見而已。

          所以,如果想要做DOM計(jì)算,一般都應(yīng)該在componentDidUpdate中進(jìn)行。在這里很方便根據(jù)布局改變進(jìn)行更新,比如根據(jù)新的布局方式計(jì)算元素的位置或大小,然后更新狀態(tài)等。

          如果componentDidUpdate每次觸發(fā)改變布局的更新時都要等待實(shí)際的瀏覽器繪制,那么用戶體驗(yàn)會非常糟糕,因?yàn)橛脩艨赡軙诓季指淖儠r看到兩次屏幕閃爍。

          注(React Hooks):這個差異也有助于理解新的useEffect和useLayoutEffect鉤子。useEffect就是我們在這里嘗試實(shí)現(xiàn)的效果。它會讓代碼在另一個執(zhí)行棧中運(yùn)行,從而在執(zhí)行之前瀏覽器可以進(jìn)行繪制。而useLayoutEffect更像是componentDidUpdate,允許你在DOM更新之后、瀏覽器繪制之前執(zhí)行代碼。


          第四篇 異步渲染(setTimeout)


          上一篇我們拆分了組件:

          //autocomplete.js
          render () {
           return (
           <div>
           <input
           onChange={ e=> this.setState({searchTerm: e.target.value})}
           value={this.state.searchTerm}/>
           <SearchResults
           searchEngine={this.props.searchEngine}
           searchTerm={this.state.searchTerm}/>
           </div> 
           )
          }
          


          但我們沒能讓昂貴的searchEngine在另一個執(zhí)行棧上運(yùn)行。那么,還有什么辦法能實(shí)現(xiàn)這一點(diǎn)呢?

          有兩個常見的方法可以設(shè)置異步調(diào)用:Promise和setTimeout。

          Promise

          //searchResults.js
          componentDidUpdate(prevProps) {
           const {searchTerm, searchEngine}=this.props;
           if(searchTerm && searchTerm !==prevProps.searchTerm) {
           /* stick the update with the expensive search method into
           a promise callback: */
           Promise.resolve().then(()=> {
           this.setState({
           searchResults: searchEngine.search(searchTerm)
           })
           })
           }
          }
          render () {
           return <ReactVirtualizedList searchResults={this.state.searchResults}/>
          }
          


          我們來看看性能評測:



          (anonymous)是Promise.then()回調(diào)函數(shù)


          又失敗了!

          理論上Promsie的回調(diào)函數(shù)是異步的,因?yàn)樗鼈儾粫綀?zhí)行,但實(shí)際上還是在同一個執(zhí)行棧中運(yùn)行的。

          仔細(xì)看看性能評測就會發(fā)現(xiàn),回調(diào)函數(shù)被放在了Run Microtasks下,因?yàn)镻romise的回調(diào)函數(shù)被當(dāng)作了微任務(wù)。瀏覽器通常會在完成正常的棧之后檢查并運(yùn)行微任務(wù)。

          更多信息:Jake Archibald有一篇非常好的演講(https://medium.com/r/?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DcCOL7MC4Pl0),解釋了JavaScript事件循環(huán)在微任務(wù)方面的處理,還深入討論了許多我涉及到的話題。

          盡管了解這一點(diǎn)很好,但并沒有解決問題。我們需要新的執(zhí)行棧,這樣瀏覽器才有機(jī)會在新的執(zhí)行棧開始之前進(jìn)行繪制。

          setTimeout

          //searchResults.js
          componentDidUpdate(prevProps) {
           const {searchTerm, searchEngine}=this.props;
           if(searchTerm && searchTerm !==prevProps.searchTerm) {
           /* stick the update with the expensive search method into
           a setTimeout callback: */
           const setTimeoutCallback=()=> {
           this.setState({
           searchResults: searchEngine.search(searchTerm)
           })
           }
           setTimeout(setTimeoutCallback)
           }
          }
          render () {
           return <ReactVirtualizedList searchResults={this.state.searchResults}/>
          }
          


          性能評測:



          繪制很難看到,它太小了,但藍(lán)線的位置的確發(fā)生了繪制


          哈哈!成功了!注意到這里有兩個執(zhí)行棧:Event (keypress)和Timer Fired (searchResults.js:49)。兩個棧之間發(fā)生了一次繪制(藍(lán)線的位置)。這正是我們想要的!來看看漂亮的UI!



          按住刪除鍵時依然有明顯的延遲


          有很大的改進(jìn),但依然很令人失望……這個頁面的確變好了,但依然能感覺到UI的延遲。我們來仔細(xì)看看性能測試。




          我們需要一些技巧才能分析這個性能測試報告并找出延遲的原因。使用性能報告有幾個小技巧:

          性能評測工具著重顯示了兩個特別有用的Key Input Interactions,每個都有不同的性能度量:

          • Key Down:從鍵盤按下到Event (keypress)處理函數(shù)被調(diào)用的時間間隔;
          • Key Character:從鍵盤按下到瀏覽器繪制更新的時間間隔。


          理想狀態(tài)下,Key Down交互應(yīng)該非常短,因?yàn)槿绻鸍avaScript主線程沒有被阻塞的話,事件應(yīng)該在鍵盤按下之后立即出發(fā)。所以,看到長長的Key Down就意味著發(fā)生了UI阻塞問題。

          在本例中,長長的Key Down的原因是它們觸發(fā)時,主線程還在被昂貴的setTimeoutCallback阻塞,比如最后的Key Down。很不幸,Key Down發(fā)生時,運(yùn)行昂貴的search的setTimeoutCallback剛剛開始,也就是說,Key Down只有等待search的計(jì)算結(jié)束才能觸發(fā)事件。這個評測中的search方法大約花了330毫秒,也就是1/3秒。從優(yōu)秀的UI角度而言,1/3秒的主線程阻塞實(shí)在太長了。

          特別引起我注意的是最后一個Key Character。盡管它關(guān)聯(lián)了Key Down的結(jié)束并且觸發(fā)了Event (keypress),瀏覽器并沒有繪制新的searchTerm,而是執(zhí)行了另一個setTimeoutCallback。這就是Key Character交互花了兩倍時間的原因。

          這一點(diǎn)著實(shí)讓我大跌眼鏡。將search移動到setTimeoutCallback的目的,就是讓瀏覽器能夠在調(diào)用setTimeoutCallback之前進(jìn)行繪制。但是在最后的Event (keypress)之前依然沒有繪制(藍(lán)線的位置)。

          結(jié)論是,我們不能依賴于瀏覽器的隊(duì)列機(jī)制。顯然瀏覽器并不一定將繪制排在超時回調(diào)之前。如果超時回調(diào)需要占用主線程330毫秒,那么也會阻礙主線程,導(dǎo)致延遲。


          第五篇 多線程


          在上一篇中,我們拆分了組件,并成功地使用setTimeout將昂貴的search移動到了另一個執(zhí)行棧中,因此瀏覽器無需等待 search完成,就可以繪制輸入框的更新:

          //autocomplete.js
          render () {
           return (
           <div>
           <input
           onChange={ e=> this.setState({searchTerm: e.target.value})}
           value={this.state.searchTerm}/>
           <SearchResults
           searchEngine={this.props.searchEngine}
           searchTerm={this.state.searchTerm}/>
           </div> 
           )
          }
          //searchResults.js
          componentDidUpdate(prevProps) {
           const {searchTerm, searchEngine}=this.props;
           if(searchTerm && searchTerm !==prevProps.searchTerm) {
           const setTimeoutCallback=()=> {
           this.setState({
           searchResults: searchEngine.search(searchTerm)
           })
           }
           setTimeout(setTimeoutCallback)
           }
          }
          render () {
           return <ReactVirtualizedList searchResults={this.state.searchResults}/>
          }
          


          不幸的是,這依然沒有解決每個searchTerm導(dǎo)致search阻塞主線程330毫秒并導(dǎo)致UI延遲的問題。

          JavaScript的單線程實(shí)在太糟糕了……突然我想到了一個方法。

          最近我在閱讀漸進(jìn)式Web應(yīng)用,其中有人使用Service Worker來實(shí)現(xiàn)一些過程,比如在另一個線程中進(jìn)行緩存。于是我開始學(xué)習(xí)Service Worker。這是一個全新的API,我需要花點(diǎn)時間來學(xué)習(xí)。

          但在學(xué)習(xí)之前我想先通過實(shí)驗(yàn)來驗(yàn)證一下增加額外的線程是否真的能夠提高性能。

          用服務(wù)器端來模擬第二個線程

          我以前就做過搜索和自動補(bǔ)齊提示的功能,但這次特別困難的原因是需要完全在前端實(shí)現(xiàn)。以前我做過用API來獲取搜索結(jié)果。其中一種思路是,API和運(yùn)行API的服務(wù)器實(shí)際上相當(dāng)于前端應(yīng)用利用的另一個線程。

          于是,我想到可以做一個簡單的node服務(wù)器讓searchTerm訪問,從而實(shí)現(xiàn)在另一個線程中運(yùn)行昂貴的搜索。這個功能非常容易實(shí)現(xiàn),因?yàn)檫@個項(xiàng)目中已經(jīng)設(shè)置過開發(fā)用的服務(wù)器了。所以我只需添加一個新的路徑:

          app.route('/prep-staging/testSearch')
           .post(bodyParser.json(), (req, res)=> {
           const {data, searchTerm}=req.body
           const engine=new SearchEngine(data)
           const searchResults=engine.search(searchTerm)
           res.send({searchResult})
           })
          


          然后將SearchResults中的setTimeout改成fetch:

          //searchResults.js
          componentDidUpdate(prevProps) {
           const {searchTerm, searchEngine, data}=this.props;
           if(searchTerm && searchTerm !==prevProps.searchTerm) {
           /* ping the search route with the searchTerm and update state
           with the results when they return: */
           fetch(`testSearch`, {
           method: 'POST',
           body: JSON.stringify({data, searchTerm}),
           headers: {
           'content-type': 'application/json'
           }
           })
           .then(r=> r.json())
           .then(resp=> this.setState({searchResults: resp.searchResults}))
           }
          }
          render () {
           return <ReactVirtualizedList searchResults={this.state.searchResults}/>
          }
          


          現(xiàn)在是見證奇跡的時刻!




          注意看刪除!

          太棒了!輸入框的更新幾乎非常完美。而另一方面,搜索結(jié)果的更新依然非常緩慢,但沒關(guān)系!別忘了我們的首要任務(wù)就是讓用戶在輸入時盡快獲得反饋。我們來看看性能測試報告:




          注意主線程中間漂亮的空白,它不再是之前層層疊疊的樣子了。性能優(yōu)化中經(jīng)常會遇到這種情況。主線程中的空白越多越好!

          從頂端可以看到testSearch花費(fèi)的時間。最長的一個實(shí)際上花了800毫秒,讓我很吃驚,但仔細(xì)想想就會發(fā)現(xiàn),并不是search花了這么長時間,而是我們的Node服務(wù)器也是單線程的。它在第一個搜索完成之前無法開始另一個搜索。由于輸入比搜索快得多,所以搜索會進(jìn)入隊(duì)列然后被延遲。搜索函數(shù)實(shí)際花費(fèi)的時間是前一個搜索完成之后的部分,大約315毫秒。

          總的來說,將昂貴的任務(wù)放到服務(wù)器線程中,就可以將堆疊的棧移動到服務(wù)器上。所以,盡管依然有改進(jìn)的空間,但主線程看起來非常好,UI的響應(yīng)速度也更快了!

          我們已經(jīng)證明這個思路是正確的,現(xiàn)在來實(shí)現(xiàn)吧!

          做了一點(diǎn)研究后我發(fā)現(xiàn),Server Worker并不是正確的選擇,因?yàn)樗患嫒軮nternet Explorer。幸運(yùn)的是,它還有個近親,叫做Web Worker,能兼容所有主流服務(wù)器,API更簡單,而且能完成我們需要的功能!

          第六篇 Web Worker


          Web worker能夠在JavaScript的主線程之外的另一個線程上運(yùn)行代碼,每個Web worker都由一個腳本文件啟動。啟動方式非常簡單:

          //searchResults.js
          export default class SearchResults extends React.Component {
           constructor (props) {
           super();
           this.state={
           searchResults: [],
           }
           //initiate the webworker:
           this.webWorker=new Worker('...path to webWorker.js') 
           //pass it the 13,000 item search data to initialize the searchEngine with:
           this.webWorker.postMessage({data: props.data})
           //assign the handler that will accept the searchResults when it sends them back:
           this.webWorker.onmessage=this.handleResults
           }
           componentDidUpdate(prevProps) {
           const {searchTerm}=this.props;
           if(searchTerm && searchTerm !==prevProps.searchTerm) {
           //change our async search request to a .postMessage, the messaging API of webWorkers:
           this.webWorker.postMessage({searchTerm})
           }
           }
           handleResults=(e)=> {
           const {searchResults}=e.data
           this.setState({
           searchResults
           })
           }
           render () {
           return <ReactVirtualizedList searchResults={this.state.searchResults}/>
           }
          }
          


          下面是webWorker.js腳本,在SearchResults的構(gòu)造函數(shù)中進(jìn)行初始化:

          //webWorker.js
          self.importScripts('...the search engine script, provides the SearchEngine constructor');
          let searchEngine; 
          let cache={} 
          //thought I would add a simple cache... Wait till you see those deletes now :)
          function initiateSearchEngine (data) {
           //initiate the search engine with the 13,000 item data set
           searchEngine=new SearchEngine(data); 
           //reset the cache on initiate just in case
           cache={}; 
          }
          function search (searchTerm) {
           const cachedResult=cache[searchTerm]
           if(cachedResult) {
           self.postMessage(cachedResult)
           return
           }
           const message={
           searchResults: searchEngine.search(searchTerm)
           };
           cache[searchTerm]=message;
           //self.postMessage is the api for sending messages to main thread
           self.postMessage(message)
          }
          /*self.onmessage is where we define the handler for messages recieved
          from the main thread*/
          self.onmessage=function(e) {
           const {data, searchTerm}=e.data;
           /*We can determine how to respond to the .postMessage from 
           SearchResults.js based on which data properties it has:*/
           if(data) {
           initiateSearchEngine(data)
           } else if(searchTerm) {
           search(searchTerm)
           }
          }
          


          可以看到,我還加了些額外的代碼。這里我加了緩存,這樣之前搜索過的searchTerms就可以立即返回結(jié)果了。如此一來,最耗性能的用戶交互(常按刪除鍵)的效率就提高了。

          我們來看看運(yùn)行情況:




          太棒了……非常快!這看起來很不錯啊,實(shí)話說,做到這個樣子就可以直接發(fā)布了!

          但是,現(xiàn)在就發(fā)布多沒勁啊……

          從用戶體驗(yàn)的角度來說,這個UI已經(jīng)非常好了。如果仔細(xì)觀察,其實(shí)依然能看到搜索結(jié)果的延遲,但幾乎察覺不到……不過幸運(yùn)的是,從性能測試報告中可以看到低效率的地方:




          請無視灰色的條,我也不知道它們是怎么來的。

          報告中最左側(cè)的線程是我們關(guān)注的線程。Main線程已經(jīng)折疊了,因?yàn)槔锩嬷挥写罅康目瞻祝馕吨骶€程的性能非常好,不會成為任何主要的性能瓶頸。

          可以看到,Worker線程里堆滿了search調(diào)用。從最后一個Key Character輸入(藍(lán)線位置)之后就會看到其后果。Worker線程中的最后一個search實(shí)際上推遲了3個search才返回。測量一下會發(fā)現(xiàn),延遲大約有850毫秒。而且大部分都是不必要的,因?yàn)槟侨齻€search都沒用,我們不再需要它們返回的結(jié)果了。

          你也許在想:“這已經(jīng)很好了!再優(yōu)化下去性價比不高啊!”

          我不這樣認(rèn)為。首先,不要低估嘗試新事物和探索帶來的價值。如果你從來沒做過探索,就很可能無法評價是否值得,因?yàn)槟悴恢滥隳苡心男┦斋@,以及你將投入多少時間和努力。所以,我認(rèn)為這種探索帶來的經(jīng)驗(yàn)和知識是無價的。隨著知識的積累,你能更好地評價投入的時間是否值得,以及是否應(yīng)該考慮這些問題。你可以做出更好的決定!

          其次,別忘了這并不是過早優(yōu)化。這些優(yōu)化都是根據(jù)性能評價做出的,我們可以測量出效果。不管怎樣評價,如果能改善850毫秒的延遲,那都是非常重大的改進(jìn)。

          最后(但不是唯一),別忘了移動端!雖然我不會在本文中介紹,但我在研究這個問題時,我也跟蹤了移動端的性能,現(xiàn)在的條件下依然有能察覺得到的性能延遲。

          不管怎么說,我們來解決這個問題!


          第七篇 確保searchTerm


          前面的性能評測揭示的最明顯的問題就是,即使對于無用的searchTerm也會運(yùn)行昂貴的搜索。所以目前的解決方案之一就是在執(zhí)行昂貴的搜索之前確保searchTerm是最新的。只要在webWorker腳本中加入confirmSearchTerm就可以非常容易地實(shí)現(xiàn):

          //webWorker.js
          self.importScripts('...the search engine script, provides the SearchEngine constructor');
          let searchEngine; 
          let cache={}
          function initiateSearchEngine (data) {
           searchEngine=new SearchEngine(data);
           cache={};
          }
          function search (searchTerm) {
           const cachedResult=cache[searchTerm]
           if(cachedResult) {
           self.postMessage(cachedResult)
           return
           }
           const message={
           searchResults: searchEngine.search(searchTerm)
           };
           cache[searchTerm]=message;
           self.postMessage(message)
          }
          function confirmSearchTerm (searchTerm) {
           self.postMessage({confirmSearchTerm: searchTerm})
          }
          self.onmessage=function(e) {
           const {data, searchTerm, confirmed}=e.data;
           if(data) {
           initiateSearchEngine(data)
           } else if(searchTerm) {
           /*check if the searchTerm is confirmed, if not, send a confirmSearchTerm message 
           to compare the searchTerm with the latest value on the main thread */
           confirmed ? search(searchTerm) : confirmSearchTerm(searchTerm)
           }
          }
          


          這里還給SearchResults handleResults加了個額外的條件,監(jiān)聽confirmSearchTerm的請求:

          //searchResults.js
          export default class SearchResults extends React.Component {
           constructor (props) {
           super();
           this.state={
           searchResults: [],
           }
           this.webWorker=new Worker('...path to webWorker.js') 
           this.webWorker.postMessage({data: props.data})
           this.webWorker.onmessage=this.handleResults
           }
           componentDidUpdate(prevProps) {
           const {searchTerm}=this.props;
           if(searchTerm && searchTerm !==prevProps.searchTerm) {
           this.webWorker.postMessage({searchTerm})
           }
           }
           handleResults=(e)=> {
           const {searchResults, confirmSearchTerm}=e.data;
           /* check if confirmSearchTerm property was sent, if so compare it to the 
           latest searchTerm and send back a confirmed searchTerm message */
           if (confirmSearchTerm && confirmSearchTerm===this.props.searchTerm) {
           this.webWorker.postMessage({
           searchTerm: this.props.searchTerm,
           confirmed: true
           })
           } else if (searchResults) {
           this.setState({
           searchResults
           })
           }
           }
           render () {
           return <ReactVirtualizedList searchResults={this.state.searchResults}/>
           }
          }
          


          我們來看看性能評測,看看有沒有改進(jìn):




          很難看出結(jié)果,因?yàn)樗鼈冞\(yùn)行得太快了,但在每次Worker的執(zhí)行棧之前都會執(zhí)行confirmSearchTerm(見藍(lán)線)。

          說明的確管用了:Worker在每次運(yùn)行昂貴的search方法之前都會確認(rèn)搜索是否必要。而且可以看到,頂部的橙色部分有4個Key Character輸入,但只運(yùn)行了三個搜索。這里我們成功地去掉了一個不必要的搜索,節(jié)約了最多330毫秒。但之前我們看到,多個額外的搜索會進(jìn)入隊(duì)列然后再不必要地運(yùn)行,而現(xiàn)在我們完全避免了這個問題。所以,節(jié)約的時間非常顯著,特別是在移動端上。

          但仔細(xì)觀察就會發(fā)現(xiàn)我們依然在浪費(fèi)時間:



          最后一個搜索使用了最新的searchTerm,但依然要至少等待當(dāng)前的搜索完成后才能開始。浪費(fèi)了84毫秒(藍(lán)色高亮部分)!據(jù)我所知,執(zhí)行棧一旦開始就無法取消。那么我們是不是無計(jì)可施了呢?


          第八篇 Web Worker陣列


          如果多加一個線程的效果很好,那么加4個會怎樣?

          實(shí)話實(shí)說,現(xiàn)在這些只是出于興趣……但說真的,最后這次優(yōu)化確實(shí)在移動端上帶來了人眼能察覺到的改善……

          無論怎樣,現(xiàn)在我打算用Web Worker陣列!



          為了出這份報告,我用人類最快的速度輸入的!

          圖中可以看到,頂端每個橙色的Key Character都有一個ww worker線程,能立即開始搜索(橙色條)。不再需要等待前一個搜索結(jié)束。而且每個ww在結(jié)束后就能用于下一次搜索。

          這是因?yàn)槲覀冊O(shè)置了workerArray.js這個web worker作為分發(fā)器。圖中看不到,因?yàn)樗鼒?zhí)行得太快了,但對于每個Key Character,workerArray都會執(zhí)行一個微笑的執(zhí)行棧,用來處理主線程傳來的搜索請求消息,然后分發(fā)給第一個可用的ww worker。

          我們成功地解決了“搜索堆”的問題。可以認(rèn)為用增加車道的方式解決了交通擁堵。

          為什么用4個搜索worker呢?因?yàn)槲以跍y試時的輸入速度從來沒能到過需要第五個worker的速度(增加worker帶來的改進(jìn)非常微小)。

          結(jié)果發(fā)現(xiàn),從Key Character輸入到search執(zhí)行之間沒有任何延遲。除非找到另一個更有效的搜索算法,否則我們已經(jīng)成功地移除了所有性能瓶頸。

          別忘了,我并沒有說這是最佳的解決方案。是否每臺手機(jī)都能處理4個web worker?性能改善是否值得付出這些額外的代碼復(fù)雜度?是否還有其他安全方面的考量,導(dǎo)致代碼更加復(fù)雜?

          這些問題都非常重要,正是這些問題會最終引出這樣的解決方案。

          但至少現(xiàn)在,這個Web worker陣列非常優(yōu)秀!我們來看看實(shí)際效果:




          感謝你耐心地閱讀完所有的篇節(jié)!

          如果說這一系列優(yōu)化有什么感想的話,那就是:

          • 不要害怕嘗試新事物,不要害怕不尋常的方案。
          • 方案是否好用并不重要,它們帶來的經(jīng)驗(yàn)和知識才是最有價值的!


          如果你有興趣,可以看看下面Web worker陣列的代碼。


          課外作業(yè)


          下面的代碼中有個小問題。需要一個非常微小的修改才能使它更強(qiáng)壯,最多兩行代碼。你能找到問題所在并修復(fù)嗎?

          //searchResults.js
          export default class SearchResults extends React.Component {
           constructor (props) {
           super();
           this.state={
           searchResults: [],
           }
           //initiate the worker array:
           this.workerArray=new WorkerArrayController({
           data: props.data,
           handleResults: this.handleResults,
           arraySize: 4
           });
           }
           componentDidUpdate(prevProps) {
           const {searchTerm}=this.props;
           if(searchTerm && searchTerm !==prevProps.searchTerm) {
           this.workerArray.search({searchTerm})
           }
           }
           handleResults=(e)=> {
           const {searchResults}=e.data
           this.setState({
           searchResults
           })
           }
           componentWillUnmount () {
           this.workerArray.terminate();
           }
           render () {
           return <ReactVirtualizedList searchResults={this.state.searchResults}/>
           }
          }
          


          SearchResults組件初始化WorkerArrayController。

          //workerArrayController.js
          export default class WorkerArrayController {
           constructor ({data, handleResults, arraySize}) {
           this.workerArray=new Worker('... path to workerArray.js');
           let i=1;
           this.webWorkers={};
           while (i <=arraySize) {
           const workerName=`ww${i}`;
           this.webWorkers[workerName]=new Worker(`...path to ww1.js`);
           /* Creates a MessageChannel for each worker and passes that channel's 
           ports to both workerArray dispatcher and the worker so 
           they can communicate with each other */
           const channel=new MessageChannel();
           this.workerArray.postMessage({workerName}, [channel.port1]);
           this.webWorkers[workerName].postMessage({data}, [channel.port2]);
           i++;
           }
           this.workerArray.onmessage=handleResults;
           }
           search=(searchTerm)=> {
           this.workerArray.postMessage({searchTerm});
           }
           terminate() {
           this.workerArray.terminate();
           for (const workerName in this.webWorkers) {
           this.webWorkers[workerName].terminate();
           }
           }
          }
          


          WorkerArrayController用4個ww初始化workerArray web worker,并傳遞MessageChannel端口給它們,這樣它們能夠互相通信。

          //workerArray.js
          const ports={};
          let cache={};
          let queue;
          function initiatePort (workerName, port) {
           ports[workerName]=port;
           const webWorker=ports[workerName];
           webWorker.inUse=false;
           webWorker.onmessage=function handleResults (e) {
           const {searchTerm, searchResults}=e.data;
           const message={searchTerm, searchResults};
           /* If all workers happen to be inUse, the message gets saved to the
           the queue and passed to the first worker that finishes */
           if(queue) {
           webWorker.postMessage(queue);
           webWorker.inUse=true;
           queue=null;
           } else {
           webWorker.inUse=false;
           }
           cache[searchTerm]=message;
           self.postMessage(message);
           }
          }
          function dispatchSearchRequest (searchTerm) {
           const cachedResult=cache[searchTerm];
           if(cachedResult) {
           self.postMessage(cachedResult);
           return
           }
           const message={searchTerm};
           for (const workerName in ports) {
           const webWorker=ports[workerName];
           if(!webWorker.inUse) {
           webWorker.postMessage(message);
           webWorker.inUse=true;
           return
           }
           }
           queue=message;
          }
          self.onmessage=function (e) {
           const {workerName, searchTerm}=e.data;
           if(workerName) {
           initiatePort(workerName, e.ports[0]);
           } else if(searchTerm) {
           dispatchSearchRequest(searchTerm);
           }
          }
          


          workerArray初始化端口對象用于通信,并跟蹤每個ww worker。它還初始化了緩存和隊(duì)列,萬一所有端口都被占用的情況下用來跟蹤最新的searchTerm請求。

          //ww1.js
          self.importScripts('...the search engine script, provides the SearchEngine constructor');
          let searchEngine;
          let port;
          function initiate (data, port) {
           searchEngine=new SearchEngine(data);
           port=port;
           port.onmessage=search;
          }
          /* search is attached to the port as the message handler so it
          runs when communicating with the workerArray only */
          function search (e) {
           const {searchTerm}=e.data;
           const message={
           searchResults: searchEngine.search(searchTerm)
           };
           port.postMessage(message)
          }
          /* self.onmessage is the handler that responds to messages from
          the main thread, which only fires during initiation */
          self.onmessage=function(e) {
           const {data}=e.data;
           initiate(data, e.ports[0]);
          }
          


          原文:Secrets of JavaScript: A tale of React, performance optimization and multi-threading本文為 CSDN 翻譯,轉(zhuǎn)載請注明來源出處。

          應(yīng)該如何以及在何處學(xué)習(xí)編程?

          軟件開發(fā)是具有挑戰(zhàn)性和利潤豐厚的職業(yè)選擇。我們的日常實(shí)用項(xiàng)目-燈泡,電視,汽車,銀行,購物-一切都是由智能代碼驅(qū)動的。

          如果您想學(xué)習(xí)編程但不知道從哪里開始,那么您來對地方了。我已編寫了分步指南,可幫助您開始進(jìn)行軟件開發(fā),并消除您的憂慮。

          如何選擇編程語言?

          如您所知,當(dāng)今有許多流行的編程語言。容易被誘惑并嘗試一次學(xué)習(xí)所有內(nèi)容。但是,這只會導(dǎo)致沮喪和失望。

          我強(qiáng)烈建議您選擇一種語言(當(dāng)然,僅在開始時,稍后您應(yīng)該以獲取多種語言為目標(biāo))并堅(jiān)持使用,而不是在太多船上一起航行。選擇語言之前,請注意以下幾點(diǎn):

          難度級別-您先前對編程語言(如果有)的經(jīng)驗(yàn)是成功的關(guān)鍵。如果您是編程的新手,那么Java和C#將是一個不錯的起點(diǎn)。但是我強(qiáng)烈建議那些愿意花更多時間并尋找更多有價值的選擇的人使用Java。它具有廣泛的應(yīng)用程序,收費(fèi)很高,并且有大量的開發(fā)者社區(qū)可以尋求支持,因此值得付出額外的努力。

          這是突出顯示最容易學(xué)習(xí)的編程語言的信息圖:

          這張照片很好地總結(jié)了所有這些。Java處于中間位置,因?yàn)樗峁┝伺εc回報之間的完美平衡。還有許多其他可供選擇的選項(xiàng)-從常綠的HTML到新興的數(shù)據(jù)科學(xué)/人工智能收藏夾(R,Python)。

          流行度-這是選擇語言時要考慮的另一個重要參數(shù)。花數(shù)百小時?學(xué)習(xí)沒有很多應(yīng)用程序的東西將不會有回報。根據(jù)TIOBE,排名前五位的流行語言是:

          C

          Java

          Python

          C ++

          C#

          Java和C總是在流行方面競爭,請記住,這僅是一個指標(biāo),您的選擇不應(yīng)該僅取決于語言的流行程度。

          而且,JavaScript在許多評級中處于領(lǐng)先地位,這并不奇怪。JavaScript是前端開發(fā)的主要語言,也經(jīng)常用于在各種情況下編寫簡短的腳本。因此,幾乎所有開發(fā)人員至少在基本水平上都知道這種語言。

          資源可用性-自己學(xué)習(xí)編程,您將反復(fù)引用可用資源來消除疑慮。選擇一種已經(jīng)存在多年的語言,意味著有大量的教育和參考資料可以幫助您適應(yīng)學(xué)習(xí)過程。無論您是想獲得扎實(shí)的理論知識,還是想立即開始編寫編碼,都只需要找到適合自己風(fēng)格的正確路線即可。此外,有活躍的在線論壇可以發(fā)布和回答查詢-很有可能您的下一個查詢已被某人回答!

          長期前景—職業(yè)發(fā)展可能是您決定學(xué)習(xí)編程的重要因素。正如您所了解的需求技能,頂級公司使用的技術(shù)通常是一個方便的參考。

          該信息圖顯示,排名前25位的公司中有超過15家使用Python,Java,Javascript和C / C ++。盡管C已有數(shù)十年的歷史并仍被廣泛使用,但Python,Javascript和Java的年齡卻相同,并且年齡稍小。Java很快流行起來,而Python則花了一些時間。

          編程語言的范圍

          編程是不同的。該詞用于創(chuàng)建功能豐富的網(wǎng)站或Web應(yīng)用程序。此外,對于Web編程,有一個前端(創(chuàng)建功能接口,瀏覽器可以讀取,顯示或啟動的所有內(nèi)容)和一個后端(在服務(wù)器上運(yùn)行的所有內(nèi)容)。編程也是移動應(yīng)用程序和桌面程序的創(chuàng)建。對于這些領(lǐng)域中的每個領(lǐng)域,都有一組工作工具,包括編程語言。

          科學(xué)編程,視頻游戲,大數(shù)據(jù),貿(mào)易,金融-不同的語言也可以用于不同的行業(yè)。

          某些語言更適合編寫小型腳本,而其他語言則適用于繁重的企業(yè)應(yīng)用程序。

          下面,我將簡要列出最常用的位置和位置:

          企業(yè)級服務(wù)器端應(yīng)用程序(主要是Java)可能是C#,有時是C ++

          科學(xué)編程-Python,Java,C#,R

          大數(shù)據(jù)-Java,Python,R

          機(jī)器學(xué)習(xí)-Python,R

          “大型”視頻游戲-主要是C

          ++(具有不同的引擎,尤其是虛幻引擎),C#(具有Unity引擎)

          移動應(yīng)用程序(包括視頻游戲)-Java,Kotlin,Swift

          操作系統(tǒng),驅(qū)動程序,高性能桌面應(yīng)用程序-C,C ++

          前端(在網(wǎng)絡(luò)瀏覽器內(nèi)創(chuàng)建交互效果)— JavaScript,打字稿

          實(shí)際上,大多數(shù)語言是相對通用的。假設(shè)Java可以應(yīng)用于上述任何領(lǐng)域。在特定區(qū)域使用特定語言的主要原因有兩個:

          1,語言的體系結(jié)構(gòu)特征。C和C ++比同一個Java或C#的底層略高一些,它們更接近硬件并且工作更快。因此,在需要高性能的地方,使用這些語言。而且,如果安全性更為重要,那么在Java中構(gòu)建安全性要比在C和C ++中容易得多。

          2,工具的歷史和可用性。假設(shè)Java和C#在架構(gòu)和語法上非常相似。但是Java較早進(jìn)入Enterprise,因此Enterprise多年來已經(jīng)支持了許多應(yīng)用程序。

          如果您已經(jīng)對未來的工作領(lǐng)域有任何計(jì)劃,請仔細(xì)查看相應(yīng)的語言。例如,如果您確定要成為前端開發(fā)人員,則可以選擇JavaScript。但是,對于所有其他情況,我對您的建議-選擇一種通用語言和流行語言,對于初學(xué)者來說相對容易。可以是Java,Python或C。在學(xué)習(xí)了基礎(chǔ)知識之后,您以后可以加深對所選語言的了解,或切換到另一種語言,例如C ++。

          步驟1:選擇要學(xué)習(xí)的最佳編程語言

          既然您已經(jīng)知道了指導(dǎo)決策的重要參數(shù),那么現(xiàn)在該該大踏步了。根據(jù)上面列出的條件,以下是我建議初學(xué)者使用的編程語言:

          Java

          它提供了最佳的獎勵-努力比率。這是初學(xué)者友好的。它是學(xué)習(xí)位于復(fù)雜應(yīng)用程序基礎(chǔ)上的面向?qū)ο缶幊痰睦硐脒x擇。它是一種開源語言。Java的最重要優(yōu)勢之一是JVM(Java虛擬機(jī)),JVM是使計(jì)算機(jī)能夠運(yùn)行Java應(yīng)用程序的虛擬機(jī)。這意味著您只需編寫一次代碼,它就可以在從服務(wù)器到手機(jī)的任何平臺上運(yùn)行(當(dāng)然,要保留一些內(nèi)容)。

          各種規(guī)模的組織都使用Java,這是一種有利可圖的職業(yè)選擇。Java程序員有一個動態(tài)的工作市場,始終歡迎高素質(zhì)的程序員。

          Java用于大型企業(yè)級服務(wù)器端應(yīng)用程序。它也廣泛用于Android編程中。銀行,金融,保險,電子商務(wù),旅行,運(yùn)輸,社交網(wǎng)絡(luò)等以技術(shù)為主導(dǎo)的行業(yè)都部署了Java代碼以建立引人入勝的用戶體驗(yàn)。Java驅(qū)動的Web應(yīng)用程序在大數(shù)據(jù)技術(shù),軟件工具和嵌入式空間中很流行。

          C語言

          它是開始編程之旅的另一種可靠語言。C可以稱為“中級”語言。這意味著與高級Java或Python不同,它更接近硬件。因此,它可以幫助您更深入地了解計(jì)算基礎(chǔ)。它具有較少的標(biāo)準(zhǔn)庫,這意味著您將要從頭開始編寫大量代碼,這對初學(xué)者來說非常有用。

          在實(shí)際應(yīng)用中,C是最古老但用途廣泛的語言之一。幾十年來,它一直是用于創(chuàng)建驅(qū)動程序,操作系統(tǒng)和腳本的語言。Linux,Unix,Windows —它們?nèi)际怯肅編寫的。考慮到其在物聯(lián)網(wǎng)(IoT)和數(shù)據(jù)庫中的應(yīng)用,它仍然是未來的安全選擇。

          Python

          Python長期以來一直處于編程世界的邊緣。由于數(shù)據(jù)科學(xué)應(yīng)用的突然激增,它已成為最近幾年的主流。Python的獨(dú)特之處在于可以快速構(gòu)建原型。它具有許多補(bǔ)充工具,例如NumPy,SciPy等,這些工具簡單易學(xué),但具有在數(shù)據(jù)科學(xué)和科學(xué)計(jì)算領(lǐng)域構(gòu)建復(fù)雜系統(tǒng)的能力。

          Python擁有可在線使用的龐大資源庫,并且開發(fā)人員社區(qū)每天都在增長,從而為龐大的庫增加了更多知識。由于它具有內(nèi)置的測試框架,因此它是進(jìn)行跨平臺測試的首選。

          Python對諸如數(shù)據(jù)科學(xué),機(jī)器學(xué)習(xí)和人工智能之類的未來技術(shù)有很高的需求。它可以用于開發(fā)Web應(yīng)用程序或軟件。

          步驟2.創(chuàng)建學(xué)習(xí)計(jì)劃

          學(xué)習(xí)編程語言應(yīng)被視為一個旅程,而不是一個目的地。您不能在幾周內(nèi)成為并掌握編程語言的所有方面。符合您的期望。列出您希望在課程結(jié)束時實(shí)現(xiàn)的目標(biāo),然后選擇與您的目標(biāo),學(xué)習(xí)速度和學(xué)習(xí)方式相匹配的課程。

          不同的語言具有不同的學(xué)習(xí)方法,您必須系統(tǒng)地遵循這些方法,并關(guān)注更大的前景。將您的總體目標(biāo)分解為每日目標(biāo),并將其納入您的計(jì)劃中。

          步驟3:選擇學(xué)習(xí)資源

          既然您已經(jīng)知道要做什么,那么下一步就是研究資源并將其入圍。它們有不同的類型。

          以實(shí)踐為導(dǎo)向的在線課程

          有在線資源提供動手教程。這些采用實(shí)用的方法進(jìn)行教學(xué),并使您養(yǎng)成早期編碼的習(xí)慣。最好的學(xué)習(xí)方法是通過練習(xí)。

          所有后續(xù)步驟。繼續(xù)!

          如果您只問我一個有關(guān)學(xué)習(xí)編程的建議,就是這樣。如果編程很容易,那么每個人都會這樣做。學(xué)習(xí)新事物需要時間和耐心。您需要誠實(shí)地努力,并朝著計(jì)劃的最終結(jié)果不斷前進(jìn)。在此過程中需要大步走小路。

          即使是經(jīng)驗(yàn)豐富的程序員,也會編寫錯誤的代碼,但他們會繼續(xù)對其進(jìn)行完善,直到其成功為止。從您的錯誤中學(xué)習(xí)(但從他人的錯誤中學(xué)習(xí)更好),并盡量避免重蹈覆轍-有很多新的錯誤待發(fā)!

          結(jié)論

          學(xué)習(xí)編程語言需要時間,耐心,努力和持久。您選擇的資源對于確定最終編寫的程序員的質(zhì)量有很大幫助。由于在線學(xué)習(xí)革命,許多知名的大學(xué)和程序員都在網(wǎng)上發(fā)布了他們的資料。您需要做的就是找到合適的人,并發(fā)掘您內(nèi)在的巨大潛力。

          我是Java程序員,目前全職Java線上一對一指導(dǎo)學(xué)習(xí),為期一年,根據(jù)學(xué)員的學(xué)習(xí)基礎(chǔ),學(xué)習(xí)能力,學(xué)習(xí)進(jìn)度,學(xué)習(xí)時間去制定適合的學(xué)習(xí)路線和指導(dǎo),學(xué)習(xí)Java的小伙伴可以關(guān)注或者私信!


          主站蜘蛛池模板: 色久综合网精品一区二区| 亚洲一区二区三区在线| 国产一区二区三区久久精品| 亚洲熟妇无码一区二区三区| 国产在线无码视频一区二区三区| 伊人久久精品无码麻豆一区| 亚洲bt加勒比一区二区| 制服丝袜一区二区三区| 国产av夜夜欢一区二区三区| 韩国理伦片一区二区三区在线播放| 农村人乱弄一区二区| 一区二区三区伦理高清| 能在线观看的一区二区三区| 精品国产高清自在线一区二区三区 | 久久国产精品视频一区| 成人在线一区二区| 日韩一区在线视频| 国产精品亚洲一区二区在线观看| 国产一区在线mmai| 欧美人妻一区黄a片| 久久青青草原一区二区| 福利国产微拍广场一区视频在线| 成人免费一区二区三区在线观看| 亚洲一区二区三区久久| 亚洲欧洲专线一区| 国产在线精品一区二区| 日韩精品无码久久一区二区三| 无码欧精品亚洲日韩一区夜夜嗨| 亚洲高清成人一区二区三区| 午夜精品一区二区三区在线观看| 国产aⅴ一区二区| 岛国无码av不卡一区二区| 亚洲乱码日产一区三区| 性色av一区二区三区夜夜嗨| 99精品一区二区三区| 爆乳无码AV一区二区三区| 视频一区在线免费观看| 国产午夜精品一区二区三区漫画| 久久精品黄AA片一区二区三区| 亚洲日韩AV一区二区三区中文 | 91成人爽a毛片一区二区|