類日常使用的JavaScript Web開發工具如果想要成為一個有吸引力的可選方案,至少需要滿足以下三方面的需求:
從用戶的角度看,使用工具創建的與原生使用JavaScript開發的Web站點和應用在觀感、頁面加載時間、頁面啟動時間和持久速度等方面上應難以區分。
從開發人員的角度看,他們希望借助這些工具能夠無縫地訪問其他JavaScript庫,進行高效的調試。
從商業的角度看,應不斷有大量的開發人員愿意接受該工具的專業培訓,并在企業中使用,學習工具所花費的時間應能很好地轉化為生產力,并且使用該工具創建的應用夠滿足不斷變更的需求。
一個此類轉化工具要取得成功,必須要達到上述三個方面的需求。各編譯器正盡量在這三個方面需求間達到平衡。對于在日常生產環境中使用的編譯器來說,其中任何一個方面都不能被忽略。就Transcrypt而言,這三個方面的需求都在特定的Transcrypt設計決策中起到了決定性作用。
需求一:
Web站點和應用的觀感與所用的底層JavaScript庫有直接的關系。因此想要具有相同的觀感,站點或應用必須正確地使用同一軟件庫。
雖然快速的網絡連接可能會隱藏其中的差異,達到同樣的頁面加載時間,甚至對于在公共網絡或托管主機上運行近似大小代碼的移動設備也是如此。這使得在加載每個新頁面時,不可能去下載一個編譯器、虛擬機或較大的運行時。
只有當代碼是在服務器端靜態預編譯成JavaScript時,才有可能獲得與使用原生JavaScript同樣的頁面啟動時間。頁面中需要的代碼量越大,差別就會變得愈發明顯。
要獲得相同的持久速度,必須生成高效的JavaScript代碼。鑒于JavaScript虛擬機已針對通用的編程模式做了高度的優化,生成的JavaScript應該類似于手工編寫的JavaScript,而不是效仿堆棧機器或是任何其他的底層抽象。
需求二:
要實現對所有JavaScript庫的無縫訪問,Python和JavaScript必須使用一致的數據格式、一致的調用模型和一致的對象模型。一致的對象模型要求JavaScript的基于原型的單繼承機制與Python的基于多繼承的機制融合在一起。應注意的是,JavaScript近期添加的關鍵字“class”對于彌合這個根本性的差異需求完全沒有影響。
要實現高效調試,必須在源代碼層面完成斷點設置和代碼單步執行這類工作。換句話說,源代碼映射是非常有必要的。一旦遇到問題,需要通過檢查生成的JavaScript代碼來找出原因。因此,所生成的JavaScript應該與Python源代碼同構。
利用已有的技術意味著源代碼必須是純Python的,而非一些更改了句法的變體。一種穩健的實現做法是使用Python的原生解析器。同樣,在語義上也必須是存Python的,該需求會造成一些實際問題,需要引入編譯器指令以維持運行時的效率。
需求三:
要保護企業在客戶端Python代碼上的投入,工具需要具有持續性。持續可用的客戶端Python編譯器應具有良好的一致性和卓越的性能。如何維持這兩者間的平衡是編譯器設計中最關鍵的部分。
Python至今已連續三年成為排名第一的計算機科學導論課程的教學語言,這一現狀足以保證受過培訓的Python開發人員持續可用。Python已用于我們所能想到的所有后端計算領域上。如果瀏覽器端編程可以使用Python實現的話,那么所有的Python開發人員都可以進行瀏覽器端編程。這些開發人員曾經設計了長期運行的大型系統,而非孤立的、短期運行的前端腳本代碼段。
就生產率而言,Python在顯著增加產出的同時保持了程序運行時的性能,這一點已得到那些從其它編程語言轉到Python的開發人員的公認。對于那些關鍵運算來說,比如數值處理和3D圖形處理,它們所使用的庫已經被編譯成了本地機器碼,這也就是為什么Python能夠保持運行時的性能。
最后一點,對不斷發生變更的需求應具有開放性,這意味要在各個層級上支持模塊化和靈活性。基于類的面向對象編程為此做出了很大貢獻,它提供了多繼承和復雜的包和模塊機制。此外,開發人員可以通過使用命名參數和默認參數在不改變現有代碼的情況下改變調用簽名(call signature)。
對比一致性和性能:語言趨同發揮了作用
一些Python的構件與JavaScript構件非常近似,尤其是當轉譯成最新版本的JavaScrirpt時。兩個語言間明顯趨同。具體而言,越來越多的Python元素已經融入JavaScript中,例如:for...of...、類(以有限的形式)、模塊、解析賦值(destructuring assignment)和參數展開(argument spreading)。因為JavaScript虛擬機已經對for...of...這類構件做了高度優化,有利于這類Python構件轉化為最近似匹配的JavaScript構件。這樣同構轉化所生成的JavaScript代碼能受益于目標語言的優化機制,也易于閱讀和調試。
雖然Transcrypt中很多的調試是通過源映射(source map)在Python中逐步進行的,而不是在JavaScript代碼中進行的,但是工具不應該隱匿底層的技術,而應揭示底層技術,讓開發人員可以完全知道“事情的真相”。這一點更為可取,因為如果使用了編譯器指令,在Python源代碼的任何地方都可以插入原生的JavaScipt代碼。
下面是一個使用了多繼承的代碼段,展示了Python與Transcrpyt轉化的JavaScript代碼之間的同構。原始的Python代碼是:
轉化后的JavaScript代碼是:
側重同構轉化的局限性存在于細微之處,有時兩個語言之間的差異是難以處理的。例如,Python中可以使用“+”操作符連接列表,而如果在JavaScript中同構地使用“+”操作符,不僅會導致列表被轉化為字符串,而且字符串會粘連在一起。當然,a + b可以被轉換為__add__ (a, b),,但是因為a和b的類型在運行時才能確定,這會導致即使對于1 + 1這樣簡單的事情,也會生成函數調用和動態類型檢查代碼。再給出一個關于如何解釋“真值(truthyness)”的例子。空列表在JavaScript中的布爾值是True(或者true),而在Python中則是False。要在應用中全局地處理這個問題,需要對每個if語句執行一次轉換,因為在Python構件if a:中不能判定a是一個布爾型,還是列表等其它類型。因此 if a:必須轉換為if( __istrue__ (a))。如果在內層循環如此使用,會再次導致性能不高。
在Transcrypt中,嵌入代碼中的編譯指令(即編譯指示)用于編譯本地控制這類構件。這允許了使用標準數學符號編寫矩陣計算,例如
M4=(M1 + M2) * M3,同時不會對perimeter=2 * pi * radius這樣的語句生成任何額外的開銷。從語法上說,編譯指示僅是在編譯時執行對__pragma__函數的調用,而非在運行時。導入包含def __pragma__ (directive, parameters): pass的樁模塊(stub module),可允許該代碼無需修改即可在CPython上運行。此外,編譯指示可以置于注釋中。在避免命名沖突的同時統一類型系統
Transcrypt統一了Python和JavaScript的類型系統,而非讓它們毗鄰而居并實時轉換。數據轉換需要花費一些時間,還增大了目標代碼的規模以及內存的使用,進而增加了垃圾回收的負擔,使得Python代碼和JavaScript庫間的交互難以處理。
因此,Transcrypt的決策是去擁抱JavaScipt世界,而非創建一個平行的世界。下面提供了一個使用了Plotly.js庫的簡單例子:
其中的編譯指示語句是可選的,它允許字典鍵值忽略引號,只是為了方便。除此之外,代碼看上去非常類似于相應的JavaScript代碼。你可以注意一下代碼中是如何使用列表解析式的,這是在JavaScipt中依然缺乏的特性。開發人員不用關心Python字典的字面量(literal)是如何映射為JavaScript字面量對象的,他們可以在編寫Python代碼時使用Plotly.js的文檔。轉化并非是在幕后完成的。在任何情況下,Transcrypt字典都是一個JavaScript對象。
統一類型系統時會產生命名沖突。例如,Python和JavaScript字符串都具有一個split()方法,但是兩者在語義上有很大不同。還存在很多類似的沖突情況,Python和JavaScript仍在發展演化,未來還會有其它的沖突。
為了解決這個問題,Transcrpyt支持別名這一概念。當在Python中使用<string>.split時,就會被翻譯成一個具有Python的split語義的JavaScript函數<string>.py_split。在原生JavaScript代碼中,split指代的是原生JavaScript的split方法。可以從Python調用JavaScript的原生split方法,這時會稱其為js_split方法。雖然在Transcrypt中對這一類方法預定義了可用的別名,但是開發人員可以自定義新的別名,或是取消已有別名的定義。這種方式可以解決所有統一類型系統所導致的命名沖突問題,無需付出運行時代價,因為別名是在編譯時進行的。
別名也允許從Python標識符生成JavaScript標識符。例如,在JavaScript中允許將$符號作為命名的一部分,而在Python中是不允許的。Transcrypt嚴格遵循Python的語法,使用原生CPython解析器做解析,語法與CPython相同。一部分JQuery代碼看上去如下:
因為Transcrypt使用編譯而非解釋,為允許加入極簡化(minification)和涉及所有模塊的交付,必須在編譯前確定導入的庫。為此,Transcrypt還支持C風格的條件編譯,這可以從下面的代碼片段中看到:
在Transcrypt運行時中,對JavaScript 5和6的代碼之間的轉換使用了同一機制:
這種方式考慮了較新版本JavaScript中的優化,并保持了向后兼容。在一些情況下,優化的優先級要高于同構:
一些優化是可選的,例如是否能激活調用緩存。這會導致直接重復調用繼承而來的方法,而非通過原型鏈(prototype chain)。
對比靜態類型與動態類型:腳本語言正走向成熟
對靜態類型優點的認可正在復蘇,TypeScript就是一個很好的例子。與JavaScript不同,靜態類型語法是Python語言不可分割的一部分,Python原生解析器就支持靜態類型語法。但是類型檢查本身卻留給了第三方工具,最著名的就是mypy。這是Jukka Lehtosalo的一個項目,Python的創建者Guido van Rossum也是該項目的貢獻者。為實現在Transcrypt中高效地使用mypy,Transcrypt團隊也為項目貢獻了一個輕量級API,無需經由操作系統直接從另一個Python應用激活mypy。雖然mypy依然在開發中,它已經可以在編譯時捕獲為數不少的輸入錯誤。靜態類型檢查是可選的,可以在本地通過插入標準類型注解來激活。一個使用注解的例子是mypy的in-porcess API:
正如上例所示,靜態類型可被用于任何適合的位置。在上面的例子中是用在run函數的簽名中,因為它是API模塊的一部分,可以被另一個開發人員從外部看到。如果有人錯誤解釋了API的參數類型或是返回類型,mypy將顯式地給出一個錯誤消息,指向產生不匹配的文件和行數。
動態類型這一概念依然處于Python和JavaScript這些語言的中心位置,因為它允許靈活的數據結構,并有助于降低執行任務所需的代碼量。源代碼量是十分重要的,因為要理解和維護源代碼,首先要通讀代碼。就此意義而言,實現同一功能,100KB的Python源代碼要優于300KB的C++源代碼,還不存在讀取類型定義的困難,這些類型定義中可能會使用模塊、顯式類型檢查和轉化代碼、重載的構造函數和方法、處理多態數據結構和類型依賴的抽象基類。
對于由單個編程人員編寫的、源代碼在100KB以下的小腳本,動態類型只具有優點,因為只需要非常小的規劃和設計,而且編程中所有事情也會有條不紊。但是當應用增大到無法由個人構建而需要團隊時,這種平衡就發生了改變。對于這樣的應用,即以大約200KB以上源代碼為特征,編譯時類型檢查的缺失會導致如下后果:
很多錯誤只有在運行時才能被捕獲,通常是在整個過程的晚期階段,修復這些問題需要付出高昂的代價,因為這些錯誤影響了更多已編寫好的代碼。
由于缺少類型信息,對模塊接口可做多種解釋。這意味著為了能夠正確使用API,在團隊成員間所做的協商需要花費更多的開發時間。
尤其是在大型團隊中工作時,動態類型接口會導致不必要的模塊耦合。而良好定義的稀疏接口才是我們需要的東西。
即便是只有一個參數的接口,如果參數指向的是一個復雜的、動態類型的對象結構,該接口就無法保證穩定的關注分離。雖然這類“4W”(Who did What,Why and When)編程模式雖然帶來了極大的靈活性,但同時也導致了設計的延后,影響到大量已有的代碼。
應用“耦合與內聚”范式。模塊內部可以在設計決策上具有強耦合,但是模塊之間最好是松耦合的,一個更改模塊內部結構的設計決策不應該影響到其它的模塊。基于上述的原則,在動態類型和靜態類型間做出選擇時可以參考如下的經驗法則:
對于特定的模塊內部,設計決策是允許耦合的。將模塊設計為內聚實體,會導致更少的源代碼量,以及易于對各種實現進行實驗。對此,動態類型是一種有效的方法,它可以用最小的設計時間開銷換取最大的靈活性。
在模塊間的邊界上,對于要交換什么信息,開發人員應準確地制定穩定的“合約”。采用這種方法,開發人員可以并行工作,無需經常性地進行協商,他們的目標是固定的,不會發生變化。靜態類型適合這些要求,對于哪些信息可以作為API的交互載體,它能給出正式的、經機器驗證的一致意見。
因此雖然當前的靜態類型浪涌看上去像是一個回歸,但事實上并不是。動態類型已取得了一席之地,并不會離開。反之也成立,C#這樣的傳統靜態類型語言也已吸收了動態類型概念。但是考慮到使用JavaScript和Python等語言編寫的應用的復雜性與日俱增,有效的模塊化、協作和單一驗證策略愈發重要。腳本語言正走向成熟。
為什么客戶端要選擇Python而非JavaScript
由于Web編程的極大普及,JavaScript也正受到很多關注和投資。在客戶端和服務器使用同一語言有其明顯優點。其中的一個優點是,隨著應用規模的增長,代碼可以從服務器端移動到客戶端。
另一個優點是概念上的一致性,這使得開發人員可以同時在前端和后端工作,無需經常在技術間做轉換。Node.js這樣平臺廣受歡迎,正是由于人們希望降低應用客戶端和服務器端在概念上的距離。但同時,這也將當前Web客戶端編程的“放之四海皆準”風險擴展到服務器端。有人認為JavaScript是一種足夠好的語言。近期的版本將開始支持基于類的面向對象(類似于在原型內膽上覆蓋了一層裝飾)、模塊和命名空間這樣的特性。隨著TypeScript的引入,使用嚴格類型成為可能,雖然將其集成到語言標準中仍需數年時間。
即使具有這些特性,JavaScript仍不會成為其它所有語言的終結者。對JavaScipt有些言過其實了(譯者注:原文借用了習語“駱駝是委員會設計的馬”,諷刺委員會喜歡虛張聲勢)。瀏覽器語言市場需要的是多樣性,事實上所有自由市場需要的都是多樣性。這意味著我們能夠為手頭的工作選擇正確的工具,即對釘子選用錘子,對螺絲選用螺絲刀。Python在設計上從一開始就是以清晰性、精確可讀性為準則的。其價值不應被低估。
在未來很長時間內,大多數客戶端編程可能仍會選擇JavaScript。但是對于那些考慮替換語言的開發人員,對持續性有影響的因素正是語言的發展動力,而非語言的具體實現。因此最重要的是使用哪種實現,而非選擇哪種語言。出于此考慮,Python無疑是一種有效的、安全的選擇。Python有很大的知名度,越來越多的瀏覽器在實現中考慮了Python,同時Python在保持性能的同時越來越接近CPython的黃金標準。
雖然新的實現會替代現有的實現,但是這個過程會一直遵循一個共識,即Python語言應該蘊含什么。直接切換到另一種語言,要比切換到另一個JavaScript庫或預處理器要容易得多。服務器端的格局已經成形,多種客戶端Python實現將會繼續存在,并展開公平競爭。獲勝者將是語言本身。瀏覽器中的Python將會繼續下去。在此所寫文章做成筆記記錄的形式,書寫代碼過程中難免取之互聯網,有的是本人書寫,推崇自己一些見解想法,在看文章之前呢,小編推薦一個群,群里分子非常踴躍交流經驗遇坑問題,也有初學者交流討論,群內整理了也整理了大量的PDF書籍和學習資料,程序員也很熱心的幫助解決問題,還有討論工作上的解決方案,非常好的學習交流地方!群內大概有好幾千人了,喜歡python的朋友可以加入python群:526929231歡迎大家交流討論各種奇技淫巧,一起快速成長
者:然后去遠足
轉發鏈接:https://segmentfault.com/a/1190000023017398
一年過去了,JavaScript 也一直在改變。不過有些技巧可以幫助你寫出簡潔高效可伸縮的代碼,即便是(或者說特別是)2019 年。下面 9 條實用小技巧能助你成為一個更好的開發者。
如果你仍深陷回調地獄,那么你應該還在寫 2014 年之前的老古董代碼吧。除非很有必要,比如遵守代碼庫要求或者出于性能原因,否則不要使用回調方式。Promise 還行,但如果你的代碼日漸龐大,Promise 就顯得有些尷尬了。我現在的首選方案是 async / await,它讓代碼的閱讀與改進都變得簡潔很多。事實上,你可以 await JavaScript 中的每一個 Promise,如果你用的庫函數返回一個 Promise,就可以簡單地 await 之。其實,async / await 只是使用 Promise 的語法糖。想讓你的代碼正常工作起來,你只需要在 funcion 前增加 async 關鍵字。如下是一個簡單例子:
async function getData() { const result=await axios.get('https://dube.io/service/ping') const data=result.data console.log('data', data) return data } getData()
注意,在最頂層沒辦法使用 await,只能在 async 函數中使用。
async / await 是在 ES2017 中引入的,所以記得轉譯你的代碼。
實際開發中不可避免地經常會遇到這種情況,我們要獲取多個數據項然后分別對它們進行某些處理(for…of),或者需要在所有異步調用都得到返回值后再完成某項任務(Promise.all)。
for…of
比方說我們要獲取頁面中幾個 Pokemon 的具體信息,我們并不想等待所有調用全部完成,尤其是有時候并不知道具體有多少次調用,但我們想只要一有返回數據就立即更新數據項。這時候我們就可以用 for...of 來遍歷數組,在循環體內執行 async 代碼,代碼的執行會被暫停,直到每次調用成功。必須注意的是如果你在代碼中如示例這樣做,可能會帶來性能瓶頸,但把這個技巧收藏你的工具箱里還是非常有用的。示例如下:
import axios from 'axios' let myData=[{id: 0}, {id: 1}, {id: 2}, {id: 3}] async function fetchData(dataSet) { for(entry of dataSet) { const result=await axios.get(`https://ironhack-pokeapi.herokuapp.com/pokemon/${entry.id}`) const newData=result.data updateData(newData) console.log(myData) } } function updateData(newData) { myData=myData.map(el=> { if(el.id===newData.id) return newData return el }) } fetchData(myData)
注:這些示例都可有效運行,可隨意復制粘貼到你喜歡的代碼沙盒內執行(如 jsfiddle、jsbin、codepen)。
Promise.all
如果想并行獲取所有 Pokemon 的信息又該如何實現呢?既然 await 可以用在所有 Promise 上,很簡單,用 Promise.all:
import axios from 'axios' let myData=[{id: 0}, {id: 1}, {id: 2}, {id: 3}] async function fetchData(dataSet) { const pokemonPromises=dataSet.map(entry=> { return axios.get(`https://ironhack-pokeapi.herokuapp.com/pokemon/${entry.id}`) }) const results=await Promise.all(pokemonPromises) results.forEach(result=> { updateData(result.data) }) console.log(myData) } function updateData(newData) { myData=myData.map(el=> { if(el.id===newData.id) return newData return el }) } fetchData(myData)
注:for...of 與 Promise.all 都是在 ES6+ 引入的,所以(必要的話)記得轉譯你的代碼。
讓我們返回到上一示例中,我們是這樣做的:
const result=axios.get(`https://ironhack-pokeapi.herokuapp.com/pokemon/${entry.id}`) const data=result.data
有一種更簡便的做法,采用解構的方式從某個對象或者數組中獲取一個或者一些值。像這樣:
const { data }=await axios.get(...)
耶!變成一行代碼了。甚至還能把變量重命名:
const { data: newData }=await axios.get(...)
另一個很妙的技巧是解構賦值的時候給出默認值。這樣就確保了你再也不會得到 undefined,而且也不必費心去手動檢測變量。
const { id=5 }={} console.log(id) // 5
這些技巧同樣適用于函數參數,如下所示:
function calculate({operands=[1, 2], type='addition'}={}) { return operands.reduce((acc, val)=> { switch(type) { case 'addition': return acc + val case 'subtraction': return acc - val case 'multiplication': return acc * val case 'division': return acc / val } }, ['addition', 'subtraction'].includes(type) ? 0 : 1) } console.log(calculate()) // 3 console.log(calculate({type: 'division'})) // 0.5 console.log(calculate({operands: [2, 3, 4], type: 'multiplication'})) // 24
這個例子乍一看可能有點令人困惑,別急慢慢來。當我們不傳任何參數時,函數將使用默認值。一旦開始傳遞參數,只有沒傳的參數會使用默認值。
注:解構賦值在 ES6 中被引入,確保先轉譯你的代碼。
在確定是否要取默認值的時候,我們往往會先對給定的值進行檢查,其中的某些檢查現在來說已經沒有必要了,將成為歷史。無論如何,知道如何處理 真值(truthy values)和 假值(falsy values)總是非常好的。它能幫助我們改進代碼,省去一些表達式,讓代碼更清晰明白。我經常看到有人這樣做:
if(myBool===true) { console.log(...) } // OR if(myString.length > 0) { console.log(...) } // OR if(isNaN(myNumber)) { console.log(...) }
這些都可以簡寫成:
if(myBool) { console.log(...) } // OR if(myString) { console.log(...) } // OR if(!myNumber) { console.log(...) }
想要真正用好這些,你需要理解 真值 和 假值 的含義。這里有個小總結:
假值
真值
注意在檢測真假值時,這里進行的是非嚴格比較,也就是說用的是==而不是===。一般說來,二者行為相同,但在某些特定情況下會出現 bug。對我來說,常發生在數字 0 上。
同樣,這也是精簡代碼的好方法。通常都能幫我們簡化代碼,但也會帶來一些混亂,尤其是鏈式使用時。
Logical operators
邏輯運算符主要用于連接兩個表達式,計算返回 true,false 或者與之匹配的值,&& 表示邏輯與,|| 表示邏輯或。如下:
console.log(true && true) // true console.log(false && true) // false console.log(true && false) // false console.log(false && false) // false console.log(true || true) // true console.log(true || false) // true console.log(false || true) // true console.log(false || false) // false
我們把邏輯運算符與上一個知識點真假值結合起來理解。當使用邏輯運算符時,遵從如下規則:
console.log(0 && {a: 1}) // 0 console.log(false && 'a') // false console.log('2' && 5) // 5 console.log([] || false) // [] console.log(NaN || null) // null console.log(true || 'a') // true
Ternary operator
三元運算符與邏輯運算符類似,但有三個部分:
示例如下:
const lang='German' console.log(lang==='German' ? 'Hallo' : 'Hello') // Hallo console.log(lang ? 'Ja' : 'Yes') // Ja console.log(lang==='French' ? 'Bon soir' : 'Good evening') // Good evening
你是否遇到過這種問題,想要訪問嵌套對象的屬性,然而并不知道該對象或其中一個子屬性是否存在?你很可能會寫出類似這樣的代碼:
let data if(myObj && myObj.firstProp && myObj.firstProp.secondProp && myObj.firstProp.secondProp.actualData) data=myObj.firstProp.secondProp.actualData
這太啰嗦了,有個更好的方法,至少是一種更好的提議(繼續往下看如何用它)。它就是可選鏈式調用(optional chaining) ,用法如下:
const data=myObj?.firstProp?.secondProp?.actualData
我認為,這是一種讓代碼更清晰的檢查嵌套屬性的有效方法。
注:目前可選鏈式調用 (optional chaining) 還不是官方規范的一部分,是處于 stage-1 的實驗性特性。你需要在你的 balelrc 中添加插件 @babel/plugin-proposal-optional-chaining 來使用。
函數綁定在 JavaScript 中十分常見。隨著 ES6 規范中箭頭函數的引入,我們現在有辦法自動綁定函數到定義時的上下文了,這種方法非常好用,被 JavaScript 開發者廣泛使用。Class(類)剛剛引入的時候,你并不能真正的使用箭頭函數,因為類方法需要一種特定的聲明方式。我們要在其他地方綁定函數,如在構造器中(React.js 的最佳實踐)。我一直覺得先定義類方法然后再綁定的流程很煩人,一段時候過后再看更感覺莫名其妙。有了類屬性語法,我們又可以用箭頭函數獲得自動綁定的好處。箭頭函數現在可以在類內使用了。示例如下,重點看 _increaseCount 是如何綁定的:
class Counter extends React.Component { constructor(props) { super(props) this.state={ count: 0 } } render() { return( <div> <h1>{this.state.count}</h1> <button onClick={this._increaseCount}>Increase Count</button> </div> ) } _increaseCount=()=> { this.setState({ count: this.state.count + 1 }) } }
注:目前,class properties 并不是正式官方規范的一部分,是處于 stage-3 的一個實驗性特性。需要在你的 balelrc 中添加插件 @babel/plugin-proposal-class-properties 來使用。
做為前端開發者,你肯定遇到過打包和轉譯代碼的情況。wepback 成為事實標準已經有很長一段時間了。我最初使用 webpack 時它還處于第一個版本,那時候很痛苦。我花了無數個小時去處理各種不同的配置項,讓項目打包運行。一旦能跑起來,我就再也不會去動它們,怕又給弄壞了。幾個月前偶然發現的 parcel,讓我松了口氣。它提供的所有功能開箱即用,同時還允許我們在必要時做出更改。它像 webpack 或者 babel 一樣支持插件系統,并且速度極快。如果你還沒聽過 parcel,墻裂建議去看看!
這是個很好的話題。關于這個問題,我有過很多不同的討論。即使是 CSS,有很多人也會傾向于使用組件庫,比如 bootstrap。JavaScript 的話,也有不少人使用 jQuery 和一些輕量代碼庫處理驗證、滑動效果等。雖然用庫也可以理解,但我還是墻裂建議自己編寫更多的代碼,而不是盲目地安裝 npm 包。對于那些整個團隊維護構建的大型代碼庫(或者框架),如 moment.js、react-datepicker,我們個人嘗試去編寫是沒有什么意義的。但可以多寫一些只是自己項目使用的代碼。這樣對自己有三大好處:
一開始,用 npm 包會顯得更簡單,自己去實現某些功能反而更費時間。但萬一這個包并沒有像預期的那樣工作,然后你不得不換另一個,花更多的時間去閱讀如何使用新的 API。如果是自己實現,你可以按自己的使用情況 100% 量身定制。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。