文共3125字,預計學習時長6分鐘
圖片來源:codeburst @webrealizer
JavaScript的世界正在快速發展。
前端開發(和網絡開發)的世界正在以極快的速度發展。如今,如果不借助前端或Webpack、React Hooks、Jest、Vue和NG元素,你會很快被遠遠拋下。不過,情況正在發生改變。
盡管開發人員和技術人員在前端領域中的數量逐年上升,生態系統卻有標準化的趨勢。新技術和工具的出現正在改變當下的規則。
總體趨勢肯定會是一種基于組件構成的用戶界面標準化,會影響從樣式到測試甚至狀態管理的所有方面,并且總體具有更好的模塊度。這將包括圍繞web組件、ES模塊、組件焦點工具等技術構建。
以下是對未來幾年前端開發的一些不完全預測,僅供參考。
1. 與框架無關的Web組件
這大體上代表了未來。因為這些純web組件與框架無關,可以在沒有框架或任何框架拼寫標準化的情況下工作。因為不使用JS語言,并受到很多瀏覽器的支持。其bundle的大小和消耗也將是最優的,而且VDOM呈現震撼人心。
這些組件提供自定義元素,這是一個允許定義新的html標簽的Javascript應用程序編程接口,用于指定布局的HTML模板,當然還有本質上特定于組件的影子DOM。
在這個領域中需要了解的主要工具是Lit-html, StencilJS,
SvelteJS當然還有 Bit,用于可重用的可以在任何地方直接共享、使用和開發的模塊組件。
當考慮到用戶界面開發的未來,以及組件時代中模塊度、可重用性、封裝性和標準化的原則時,web組件就是答案。
2. 框架沖突的未來?
現在,在NPM下載中React仍然是前端中的“女王”
我們不會深入探討“哪個更好,為什么更好”,相反,如果退回一步的話,你會注意到更重要更宏大的部分。圍繞組件的前端技術的總體“市場份額”正在增長。新開發人員也在快速涌入,工具的使用空間也越來越大。
那么未來5年內哪個框架會成為支配呢?沒有人知道。但可以肯定地說,它將是在原生JS生態系統中發揮作用的最佳位置,web組件在其中支配著文檔對象模型dom。React在NPM中下載量最高。然而——看看這些數字。似乎在實際web使用中差距非常小。
令人震驚吧?
實際上,Vue和React在實際使用中很接近
隨著未來與框架無關的web組件的標準化,不少人都想知道可能會對用戶界面框架沖突產生的影響。事實上,我們都知道React確實不是一個框架。
3. 組件分離,重用和構成
heBit組件:未來的代碼共享、重用和開發
當談到在不久的將來的前端開發和用戶界面組件時,不可能忽視 Bit驚人的promise功能。
Bit(開放源)分離并將組件(或任何可重用的JS代碼)轉換為共享的構建塊,可供在所有項目和應用中使用和共享。神奇的是——還可以使用Bit從不同項目開發相同組件,同時完全控制源代碼更改和整個依賴圖。
簡單地說,通過Bit可以立即在另一個項目中使用一個項目中的組件,并開發和同步更改這兩個組件。團隊工作時,這個工作流將通過Bit的組件中心 bit.dev 加強,可以在其中組織和共享團隊代碼。
組件中心提供了在組件上共享和協作所需的一切,從漂亮的搜索和發現體驗到實時組件playground,持續集成和持續交付的充分支持等。
通過Bit可以充分構建應用程序,即時獲得團隊和開源社區寫入的所有組件,并立即共享新組件或建議對現有組件進行更新。
4. ES模塊和內容分發網絡
ES模塊是在瀏覽器中用模塊工作的標準,被ECMAScript標準化。使用ES模塊可以很容易地將功能封裝進模塊中,可以通過內容分發網絡等方式使用。隨著Firefox 60的發布,所有主流的瀏覽器都將支持ES模塊,Node mteam正致力將ES模塊支持添加到Node.js中。另外,用于WebAssembly的ES模塊整合將在未來幾年內實現。想象一下,JS組件與Bit分離,并通過 bit.dev內容分發網絡使用。
5. 組件級別的狀態管理
那么狀態管理有什么新變化呢?我們只需要在Redux中就能管理一切事務嗎?
但這可能很難實現充分利用組件,使模塊得到重用。React新的Context API 和 Hooks意味著不需要第三方庫,便可以在功能組件級別管理狀態,從而提高模塊度和可重用性。
因此,展望未來,我們可以嘗試更多地從封裝組件而較少從全球應用商店的角度來考慮狀態管理。
6. 構成樣式化組件
Bit模塊:由獨立邏輯和主題組件構成樣式
過去兩年有很多關于樣式化組件的討論。從內聯層疊樣式表或層疊樣式表模塊到JS中的層疊樣式表和樣式組件,甚至像stylable這樣的中途解決方案,有很多解決方案。
未來幾年,樣式可以作為一種構成。這意味著,組件設計體系應該同時包含邏輯組件和主題組件,可以使用Bit之類的工具構成。通過這種方式可以創建一個設計體系,根據需要進行發展和改變,不會將一個復雜的庫強加給不愿意去應用的開發人員。設計工具本身如Sketch an Figma,利用will組件來達到這個目的(結合Bi得到最終的組件設計體系。
7. 用于數據驅動應用程序的GraphQL應用程序編程接口客戶端
令人興奮的是,在客戶端有很大的可能性來通過組件使用GraphQL。使用阿波羅可以輕松通過GraphQL構建獲取數據的用戶界面組件。結合Bit能從正在處理的項目中導入和開發這些組件。
通過對應用程序編程接口的智能管理,可以簡化圍繞數據驅動應用程序開發的工作流,加快開發速度。所以對未來幾年的展望絕對是值得的。
8. 基于組件的設計工具
隨著組件變為了設計體系,設計師和開發人員雙方都在向彼此靠近。也就是從雙方來看,這是可能的。
Sketch已經在設計組件之間創建了依賴關系,因此可以模塊化的方式設計和更新。代碼組件的整合已經出現,這只是時間問題。
Figma這類的工具是徹底基于可重用的用戶界面元素構建的。Framer Team正在為編寫代碼的設計人員構建一個工具,能夠在一定程度上控制將用戶界面元素轉換為可重用的React組件。
通過Bit可以將設計的組件轉換為可重用的構建塊,并可以在任何地方直觀發現、使用甚至開發,從而彌補與開發人員之間的差距。Bit +組件設計工具未來大有可為。通過內容分發網絡使用Bit和web組件是個完整的構成。
留言 點贊 關注
我們一起分享AI學習與發展的干貨
歡迎關注全平臺AI垂類自媒體 “讀芯術”
eact18內核探秘:手寫React高質量源碼邁向高階開發
download: https://www.666xit.com/4202/
RN簡介React Native(簡稱RN)是Facebook于2015年4月開源的跨渠道移動運用開發結構,是Facebook新近開源的JS結構React在原生移動運用渠道的衍生產品,現在支撐iOS和安卓兩大渠道。RN運用Javascript言語,類似于HTML的JSX,以及CSS來開發移動運用,因此了解Web前端開發的技術人員只需很少的學習就能夠進入移動運用開發范疇。
React Native
看起來很像
React
,只不過其根底組件是原生組件而非 web 組件。要了解
React Native
運用的根本結構,首要需求了解一些根本的
React
的概念,比如
JSX
語法、組件、
state
狀況以及
props
特色。
React Native開發特色:
一次學習,隨處編寫:運用React Native可認為iOS和Android操作系統開發運用程序,不同渠道上的代碼依據渠道會有一些微小的差異。混合開發:React Native代碼開發的模塊與原生代碼開發的模塊能夠雙向通信、無縫聯接;高效的移動運用開發:(1)共同的UI完結結構(2)組件化開發(3)跨渠道移植代碼敏捷(4)自動匹配不同屏幕巨細的手機高效的移動運用開發調試高效的運用熱更新有效下降移動運用裝置包體積學習門檻低、開發難度低運用React Native開發的價值為了得到React Native開發的長處,運用React Native開發的APP也需求付出必定的價值。(1)內存耗費大運用React Native開發的程序運行所需的內存比原生代碼開發的程序略多。(2)運行速度運用React Native開發的代碼運行速度比原生代碼略慢。
React 與 React Native 除了在編碼體現層都運用 JSX 語法外,在 React 與 React Native 的底層都有 Virtual DOM 與 DOM 之間的映射與轉換,以完結了頁面組件高效更新的前端體現。
現在最新版本是0.59React Native中文網
React Native
與
React
的關系及特色:
React
是根底結構,是一套根底規劃完結理念,開發者不能直接運用它來開發移動運用或網頁。在
React
之上開展出了
React.js
結構用來開發網頁,開展出來
React Native
用來開發移動運用。底層原理是相同的,都是運用js完結虛擬dom樹來驅動頁面的烘托,react是驅動HTML dom的烘托,react native是驅動原生組件的烘托。
React.js
:目的 是為了使前端的V層更具組件化,能更好的復用,它能夠運用簡略的html標簽創立更多的自界說組件標簽,內部綁定事情,同時能夠讓你從操作dom中解脫出來,只需求操作數據就會改動相應的dom。
二者都是依據組件(
component
)開發,然后組件和組件之間經過
props
傳遞辦法,每個組件都有一個狀況
(state)
,當某個辦法改動了這個狀況值時,整個組件就會重繪,然后到達改寫。另外,說到重繪就要說到
虛擬dom
了,便是用js模仿
dom
結構,等整個組件的
dom
更新結束,它會有一個
diff
的進程,對比出哪些組件發生了改動,然后才烘托到頁面,簡略來說只更新了比較之前改動了的部分,而不是全部改寫,所以功率很高。
虛擬DOM(Virtual DOM)
的機制:在瀏覽器端用
Javascript
完結了一套
DOM API
。依據
React
進行開發時一切的
DOM
結構都是經過虛擬
DOM
進行,每當數據改動時,React都會從頭構建整個DOM樹,然后React將當時整個DOM樹和上一次的DOM樹進行對比,得到DOM結構的差異,然后只是將需求改動的部分進行實際的瀏覽器DOM更新。而且React能夠批處理虛擬DOM的改寫,在一個事情循環(Event Loop)內的兩次數據改動會被兼并。
2.React詳解
React 官網React GitHub 地址React菜鳥教程React介紹學習生態介紹
Vue生態:Vue + Vue-Router + Vuex + Axios + Babel + WebpackReact生態:React + React-Router + Redux + Axios + Babel + Webpack
2.1.React 簡介
React是一個聲明式,高效且靈活的用于構建用戶界面的 JavaScript 庫。運用 React 能夠將一些簡短、獨立的代碼片段組合成復雜的 UI 界面,這些代碼片段被稱作“組件”。
React
特色
1.聲明式規劃 React采用聲明范式,能夠輕松描繪運用。2.高效 React經過對DOM的模仿,最大極限地減少與DOM的交互。3.靈活 React能夠與已知的庫或結構很好地合作。4.JSX JSX是JavaScript語法的擴展。React 開發不必定運用 JSX ,但咱們主張運用它。5.組件 經過React構建組件,使得代碼愈加容易得到復用,能夠很好的運用在大項目的開發中。6.單向呼應的數據流 React完結了單向呼應的數據流,然后減少了重復代碼,這也是它為什么比傳統數據綁定更簡略。2.2 React運用
1.React 環境裝備裝置
運用 React CDN 庫經過 npm 運用 React運用 create-react-app 快速構建 React 開發環境2.3React render 烘托
屏幕輸出:Hello, React
Title
ReactDOM.render(
,document.getElementById('root'));
3.React JSX
React 運用 JSX 來代替慣例的 JavaScript。
1.React JSX簡介
JSX:JavaScript XML,一種類似于XML的JS擴展語法。也能夠了解成:契合 XML 標準的 JS 語法。
JSX語法的實質:以 React.createElement 的形式來完結的,并沒有直接把 用戶寫的 HTML代碼,烘托到頁面上。運用babel轉換工具將 JSX語法 轉換為 JS語法。
咱們不需求必定運用 JSX,但它有以下長處:
JSX 履行更快,由于它在編譯為 JavaScript 代碼后進行了優化。它是類型安全的,在編譯進程中就能發現錯誤。運用 JSX 編寫模板愈加簡略快速。
const element=
;
這種看起來或許有些奇怪的標簽語法既不是字符串也不是 HTML。它被稱為 JSX, 一種 JavaScript 的語法擴展。 咱們推薦在 React 中運用 JSX 來描繪用戶界面。JSX 是在 JavaScript 內部完結的。
2.JSX的根本語法
(1)在 JSX內部 寫 JS代碼:假如要在 JSX 語法內部,書寫 JS 代碼,那么,一切的JS代碼有必要寫到 {} 的內部。在{}內部,能夠寫任何契合JS標準的代碼。
例如:
var myTitle='這是運用變量界說的 tilte 值'// 運用JSX語法 創立虛擬DOM目標var vDom=(
Hello, React!這是標題);
(2)當編譯引擎在編譯JSX代碼的時分,假如遇到了<,會把它當作 HTML代碼 去編譯;假如遇到了 {}, 會把方括號里邊的代碼當作 一般JS代碼 去編譯。
(3)在JSX中,假如要為元素添加class特色,則有必要寫成className,由于 class在ES6中是一個關鍵字;和class類似,label標簽的 for 特色需求替換為 htmlFor。
代碼舉例:
// 運用JSX語法 創立虛擬DOM目標var vDom=(
Hello, React!千古壹號
);
(4)在JSX創立DOM的時分,一切的節點,有必要有唯一的根元素進行包裹。
(5)假如要寫注釋,注釋有必要放到 {} 內部。例如:
// 運用JSX語法 創立虛擬DOM目標var vDom=(// 這一行是注釋
Hello, React!
千古壹號
{/*這一行也是注釋 */});
最后,再舉個比如:
Document
//頁面中的實在容器元素var containDiv=document.getElementById("app");var arr=[]for (var i=0; i < 6; i++) {var p=這個是p標簽
// 注意這個地方的寫法: key={i}arr.push(p)}//1、運用JSX語法 創立虛擬DOM目標var vDom=(Hello, React!{arr});//2、烘托虛擬DOM目標ReactDOM.render(vDom, containDiv); // 參數1:虛擬DOM目標;參數2:頁面中的容器
4.React 組件 狀況(State&props) 生命周期詳解
React 把組件看成是一個狀況機(State Machines)。經過與用戶的交互,完結不同狀況,然后烘托 UI,讓用戶界面和數據保持一致。
React 里,只需更新組件的 state,然后依據新的 state 從頭烘托用戶界面(不要操作 DOM)。
創立組件的第一種方式:在React中,結構函數便是一個最根本的組件。假如想要把組件放到頁面中,能夠把結構函數的名稱當作組件的名稱,以 HTML標簽形式引進頁面中即可。創立組件的第二種方式:運用 class 關鍵字運用 function 創立的組件,叫做【無狀況組件】;運用 class 創立的組件,叫做【有狀況組件】。實質差異:有狀況組件和無狀況組件,最實質的差異,便是有無 state 特色。同時, class 創立的組件,有自己的生命周期函數,可是,function 創立的 組件,沒有自己的生命周期函數。
state 和 props 主要的差異在于 props 是不可變的,而 state 能夠依據與用戶交互來改動。這便是為什么有些容器組件需求界說 state 來更新和修改數據。 而子組件只能經過 props 來傳遞數據。
組件API
設置狀況:setState替換狀況:replaceState設置特色:setProps替換特色:replaceProps強制更新:forceUpdate獲取DOM節點:findDOMNode判別組件掛載狀況:isMounted
組件的生命周期可分成三個狀況:
在組件創立、到加載到頁面上運行、以及組件被毀掉的進程中,總是伴隨著各式各樣的事情,這些在組件特定時期,觸發的事情統稱為組件的生命周期。Mounting:已插入實在 DOMUpdating:正在被從頭烘托Unmounting:已移出實在 DOM
生命周期的辦法有:
componentWillMount 在烘托前調用,在客戶端也在服務端。componentDidMount : 在第一次烘托后調用,只在客戶端。之后組件現已生成了對應的DOM結構,能夠經過this.getDOMNode()來進行拜訪。 假如你想和其他JavaScript結構一起運用,能夠在這個辦法中調用setTimeout, setInterval或許發送AJAX請求等操作(防止異步操作阻塞UI)。componentWillReceiveProps 在組件接納到一個新的 prop (更新后)時被調用。這個辦法在初始化render時不會被調用。shouldComponentUpdate 回來一個布爾值。在組件接納到新的props或許state時被調用。在初始化時或許運用forceUpdate時不被調用。能夠在你確認不需求更新組件時運用。componentWillUpdate在組件接納到新的props或許state但還沒有render時被調用。在初始化時不會被調用。componentDidUpdate 在組件完結更新后立即調用。在初始化時不會被調用。componentWillUnmount在組件從 DOM 中移除之前馬上被調用。1.組件創立階段
組件創立階段的生命周期函數,有一個明顯的特色:創立階段的生命周期函數,在組件的一輩子中,只履行一次。
getDefaultProps初始化 props 特色默認值。
getInitialState初始化組件的私有數據。由于 state 是界說在組件的 constructor 結構器當中的,只需new 了 class類,必定會調用 constructor結構器。
componentWillMount()組件將要被掛載。此刻還沒有開端烘托虛擬DOM。
在這個階段,不能去操作DOM元素,但能夠操作特色、狀況、function。相當于 Vue 中的Create()函數。
render()第一次開端烘托真實的虛擬DOM。當render履行完,內存中就有了完好的虛擬DOM了。
意思是,此刻,虛擬DOM在內存中創立好了,可是還沒有掛在到頁面上。
在這個函數內部,不能去操作DOM元素,由于還沒return之前,虛擬DOM還沒有創立;當return履行結束后,虛擬DOM就創立好了,可是還沒有掛在到頁面上。
componentDidMount()當組件(虛擬DOM)掛載到頁面之后,會進入這個生命周期函數。
只需進入到這個生命周期函數,則必定闡明,頁面上現已有可見的DOM元素了。此刻,組件現已顯示到了頁面上,state上的數據、內存中的虛擬DOM、以及瀏覽器中的頁面,現已徹底保持一致了。
當這個辦法履行完,組件就進入都了 運行中 的狀況。所以說,componentDidMount 是創立階段的最后一個函數。
在這個函數中,咱們能夠放心的去 操作 頁面上你需求運用的 DOM 元素了。假如咱們想操作DOM元素,最早只能在 componentDidMount 中進行。相當于 Vue 中的 mounted() 函數
2、組件運行階段
有一個明顯的特色,依據組件的state和props的改動,有選擇性的觸發0次或屢次。
componentWillReceiveProps()組件將要接納新特色。只有當父組件中,經過某些事情,從頭修改了 傳遞給 子組件的 props 數據之后,才會觸發這個鉤子函數。
shouldComponentUpdate()判別組件是否需求被更新。此刻,組件尚未被更新,可是,state 和 props 肯定是最新的。
componentWillUpdate()組件將要被更新。此刻,組件還沒有被更新,在進入到這個生命周期函數的時分,內存中的虛擬DOM還是舊的,頁面上的 DOM 元素也是舊的。(也便是說,此刻操作的是舊的 DOM元素)
render此刻,又要依據最新的 state 和 props,從頭烘托一棵內存中的 虛擬DOM樹。當 render 調用結束,內存中的舊DOM樹,現已被新DOM樹替換了!此刻,虛擬DOM樹現已和組件的 state 保持一致了,都是最新的;可是頁面還是舊的。
componentDidUpdate此刻,組件完結更新,頁面被從頭烘托。此刻,state、虛擬DOM 和 頁面現已徹底保持同步。
3、組件毀掉階段
一輩子只履行一次。
componentWillUnmount: 組件將要被卸載。此刻組件還能夠正常運用。生命周期對比:
vue中的生命周期圖React Native 中組件的生命周期
組件生命周期的履行次序總結
1、Mounting:
constructor()componentWillMount()render()componentDidMount()2、Updating:
componentWillReceiveProps(nextProps):接納父組件傳遞過來的特色shouldComponentUpdate(nextProps, nextState):一旦調用 setState,就會觸發這個辦法。辦法默認 return true;假如 return false,后續的辦法就不會走了。componentWillUpdate(nextProps, nextState)render()componentDidUpdate(prevProps, prevState)3、Unmounting:
componentWillUnmount()5.React 事情處理
React 元素的事情處理和 DOM 元素類似。可是有一點語法上的不同:
React 事情綁定特色的命名采用駝峰式寫法,而不是小寫。假如采用 JSX 的語法你需求傳入一個函數作為事情處理函數,而不是一個字符串(DOM 元素的寫法)
//HTML 一般寫法是:激活按鈕//React 中寫法為:激活按鈕
多初學者經常會問 “我需要學習哪個框架 ?” 以及 “學習框架前需要掌握多少 JS 或者 TS ?” 無數帶有主觀色彩的文章都在宣傳作者首選框架或庫的優勢,而不是向讀者展示其背后的概念以做出更明智的決定。所以讓我們先解決第二個問題
盡可能多地去學以讓更好的你理解它們所基于的概念。你將需要了解基本數據類型、函數、基本運算符和文檔對象模型 ( DOM ),這是 HTML 和 CSS 在 JS 中的表示。除此之外的一切也都 OK,但并不嚴格要求某個精通框架或庫。
如果你是一個完完全全的新手,JS for cats 應該是一個不錯的入門資料。持續學習,直到你感到自信為止,然后繼續前進,直到你再次感到自信為止。當掌握了足夠的 JS / TS 知識后,你就可以開始學習框架。其他的知識你可以并行學習。
所有現代框架都從這些概念中派生出它們的功能
State 只是為你的應用程序提供動力的數據。它可能在全局級別,適用于應用程序的大部分組件,或適用于單個組件。讓我們寫一個計數器的簡單例子來說明一下。它保留的計數是 state 。我們可以讀取 state 或者寫入 state 以增加計數
最簡單的表示通常是一個變量,其中包含我們的狀態所包含的數據:
let count=0;
const increment=()=> { count++; };
const button=document.createElement('button');
button.textContent=count;
button.addEventListener('click', increment);
document.body.appendChild(button);
復制代碼
但這個代碼有個問題:類似調用 increment 方法一樣去修改 count 的值 ,并不會自動修改 button 的文案。我們需要手動去更新所有的內容,但這樣的做法在復雜場景下代碼的可維護性 & 擴展性都不是很好。
讓 count 自動更新依賴它的使用方的能力稱之為 reactivity(響應式)。這是通過訂閱并重新運行應用程序的訂閱部分來更新的。
幾乎所有的現代前端框架和庫都擁有讓 state 變成 reactivity 的能力。基本上可以分為 3 種解決方案,采用其中至少一種或者多種混用來實現這個能力:
這些概念還是直接用英文表達比較貼切
Observables 基本上是在讀取 state 的時候通過一個訂閱方法來收集依賴,然后在更新的時候觸發依賴的更新
const state=(initialValue)=> ({
_value: initialValue,
get: function() {
/* 訂閱 */;
return this._value;
},
set: function(value) {
this._value=value;
/* 觸發更新 */;
}
});
復制代碼
knockout 是最早使用這個概念的框架之一,它使用帶有 / 不帶參數的相同函數進行寫/讀訪問
這種模式最近有開始有框架通過 signals 來實現,比如 Solid.js 和 preact signals ;相同的模式也在 Vue 和 Svelte 中使用到。RxJS 為 Angular 的 reactive 層提供底層能力,是這一模式的延伸,超越了簡單狀態。Solid.js 用 Stores(一些通過 setter 方法來操作的對象)的方式進一步抽象了 signals
不可變意味著如果對象的某個屬性發生改變,那么整個對象的引用就會發生改變。所以協調器做的事情就包括通過簡單的引用對比就判斷出對象是否發生了改變
const state1={
todos: [{ text: 'understand immutability', complete: false }],
currentText: ''
};
// 更新 currentText 屬性
const state2={
todos: state1.todos,
currentText: 'understand reconciliation'
};
// 添加一個 todo
const state3={
todos: [
state1.todos[0],
{ text: 'understand reconciliation', complete: true }
],
currentText: ''
};
// 由于不可變性,這里將會報錯
state3.currentText='I am not immutable!';
復制代碼
如你所見,未變更項目的引用被重新使用。如果協調器檢測到不同的對象引用,那么它將重新運行所有的組件,讓所有的組件的 state (props, memos, effects, context) 都使用最新的這個對象。由于讀取訪問是被動的,所以需要手動指定對響應值的依賴。
很顯然,你不會用上面這種方式定義 state 。要么你是從一個已經存在的屬性構造 state ,要么你會使用 reducer 來構造 state。一個 reducer 函數就是接收一個 state 對象然后返回一個新的 state 對象。
react 和 preact 就使用這種模式。它適合與 vDOM 一起使用,我們將在稍后描述模板時探討它。
并不是所有的框架都借助 vDOM 將 state 變成完成響應式。例如 Mithril.JS 要不是在 state 修改后觸發對應的生命周期事件,要不是手動調用 m.redraw() 方法,才能夠觸發更新
Transpilation 是在構建階段,重寫我們的代碼讓代碼可以在舊的瀏覽器運行或者賦予代碼其他的能力;在這種情況下,轉譯則是被用于把一個簡單的變量修改成響應式系統的一部分。
Svelte 就是基于轉譯器,該轉譯器還通過看似簡單的變量聲明和訪問為他們的響應式系統提供能力
另外,Solid.js 也是使用 Transpilation ,但 Transpilation 只使用到模版上,沒有使用到 state 上
大部分情況下,我們需要做的更多是操作響應式的 state,而很少需要操作基于 state 的 DOM 渲染。我們需要管理好副作用,這些副作用是由于視圖更新之外的狀態變化而發生的所有事情(雖然有些框架把視圖更新也當作是副作用,例如 Solid.js )
記得之前 state 的例子中,我們故意把訂閱操作的代碼留空。現在讓我們把這些留空補齊來處理副作用,讓程序能夠響應更新
const context=[];
const state=(initialValue)=> ({
_subscribers: new Set(),
_value: initialValue,
get: function() {
const current=context.at(-1);
if (current) { this._subscribers.add(current); }
return this._value;
},
set: function(value) {
if (this._value===value) { return; }
this._value=value;
this._subscribers.forEach(sub=> sub());
}
});
const effect=(fn)=> {
const execute=()=> {
context.push(execute);
try { fn(); } finally { context.pop(); }
};
execute();
};
復制代碼
上面代碼基本上是對 preact signals 或者 Solid.js 響應式 state 的簡化版本,它不包含錯誤處理和復雜狀態處理(使用一個函數接收之前的狀態值,返回下一個狀態值),但這些都是很容易就可以加上的
這允許我們使前面的示例具有響應性:
const count=state(0);
const increment=()=> count.set(count.get() + 1);
const button=document.createElement('button');
effect(()=> {
button.textContent=count.get();
});
button.addEventListener('click', increment);
document.body.appendChild(button);
復制代碼
? 可以嘗試運行一下上面 Effect 的兩個代碼塊的例子,源代碼地址在 這里
在大多數情況下,框架允許在不同生命周期,讓 Effect 在渲染 DOM 之前、期間或之后運行。
Memoization 意味著緩存 state 值的計算結果,并且在結果的依賴發生改變的時候進行更新。它基本上是一種返回派生(derived) state 的 Effect
在某些會重新運行其組件函數的框架中,如 react 和 preact,允許在它所依賴的狀態沒有改變時避免這部分組件重新渲染
對于其他框架,情況恰恰相反:它允許你選擇部分組件進行響應式更新,同時緩存之前的計算
對于我們簡單的響應系統,memo 大概是這樣實現
const memo=(fn)=> {
let memoized;
effect(()=> {
if (memoized) {
memoized.set(fn());
} else {
memoized=state(fn());
}
});
return memoized.get;
};
復制代碼
現在有了原始的、派生的和緩存形式的 state,我們想把它展示給用戶。在我們的例子中,我們直接操作 DOM 來添加按鈕和更新按鈕的內容文案。
為了提升開發體驗,幾乎所有的現代框架都支持 DSL 來在代碼中編寫類似于所需輸出的內容。雖然有不同的風格,比如 .jsx ,.vue ,.svelte 文件,但這一切都歸結為用類似于 HTML 的代碼來表示 DOM。所以基本上是
<div>Hello, World</div>
// 在你的 JS 代碼中
// 變成你的 HTML:
<div>Hello, World</div>
復制代碼
你可以能會問:“在哪里放置我的 state ?”。非常好的問題,大部分的情況 下,{} 用于在屬性和節點中表達動態內容。
最常用的 JS 模板語言擴展無疑是 JSX。在 react 中,它被編譯為存粹的 JavaScript 語言,允許創建對于 DOM 的虛擬表示,也就是經常被提到的「虛擬文檔對象」或者簡稱為 vDOM。
這是基于創建 JS 對象比訪問 DOM 快得多的前提,所以如果你可以用創建 JS 對象替換訪問 DOM,那么你就可以節省時間
然而,如果你的項目在任何情況下都沒有大量的 DOM 修改或者只是創建不需要修改的對象;那么上面這個方案的優點就會變成缺點,那這個時候就需要使用 memoization 來將缺點的影響降到最小。
// 1. 源代碼
<div>Hello, {name}</div>
// 2. 轉譯成 js 代碼
createElement("div", null, "Hello, ", name);
// 3. 執行 js 后返回的對象
{
"$$typeof": Symbol(react.element),
"type": "div",
"key": null,
"ref": null,
"props": {
"children": "Hello, World"
},
"_owner": null
}
// 4. 渲染 vdom
/* HTMLDivElement */<div>Hello, World</div>
復制代碼
JSX 不僅僅用在 react,也用在了 Solid.js。例如,使用 Solid 轉譯器更徹底地改變代碼
// 1. 源代碼
<div>Hello, {name()}</div>
// 2. 轉譯成 js 代碼
const _tmpl$=/*#__PURE__*/_$template(`<div>Hello, </div>`, 2);
(()=> {
const _el$=_tmpl$.cloneNode(true),
_el$2=_el$.firstChild;
_$insert(_el$, name, null);
return _el$;
})();
// 3. 渲染 vdom
/* HTMLDivElement */<div>Hello, World</div>
復制代碼
雖然轉譯之后的代碼一開始看到會覺得挺嚇人,但它更容易解釋其中代碼的邏輯。首先,模版的靜態部分被創建出來;然后,創建出來的對象被克隆并創建一個新的實例,新的實例包含被添加的動態部分,以及將動態部分的更新與 state 的更新關聯起來。
Svelte 在轉譯的時候做的工作更多,不僅僅處理了模版,還處理了 state
// 1. 源代碼
<script>
let name='World';
setTimeout(()=> { name='you'; }, 1000);
</script>
<div>Hello, {name}</div>
// 2. 轉譯成 js 代碼
/* 生成自 Svelte v3.55.0 版本 */
import {
SvelteComponent,
append,
detach,
element,
init,
insert,
noop,
safe_not_equal,
set_data,
text
} from "svelte/internal";
function create_fragment(ctx) {
let div;
let t0;
let t1;
return {
c() {
div=element("div");
t0=text("Hello, ");
t1=text(/*name*/ ctx[0]);
},
m(target, anchor) {
insert(target, div, anchor);
append(div, t0);
append(div, t1);
},
p(ctx, [dirty]) {
if (dirty & /*name*/ 1) set_data(t1, /*name*/ ctx[0]);
},
i: noop,
o: noop,
d(detaching) {
if (detaching) detach(div);
}
};
}
function instance($$self, $$props, $$invalidate) {
let name='World';
setTimeout(
()=> {
$$invalidate(0, name='you');
},
1000
);
return [name];
}
class Component extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance, create_fragment, safe_not_equal, {});
}
}
export default Component;
// 3. 執行 JS 代碼
/* HTMLDivElement */<div>Hello, World</div>
復制代碼
當然也有例外,在 Mithril.js 中,雖然可以使用 JSX,但鼓勵你編寫 JS 代碼
// 1. 源代碼
const Hello={
name: 'World',
oninit: ()=> setTimeout(()=> {
Hello.name='you';
m.redraw();
}, 1000),
view: ()=> m('div', 'Hello, ' + Hello.name + '!')
};
// 2. 執行 JS 代碼
/* HTMLDivElement */<div>Hello, World</div>
復制代碼
有的人會覺得這樣做的開發體驗不太好,但有的人更希望對自己的代碼有更多的控制權。這取決于他們想要解決的是哪一類的問題,缺少 transpilation 這個步驟也可能成為優點。
許多其他框架也允許在不進行 transpilation 的情況下使用,盡管很少有人這樣推薦。
我有一些好消息和一些壞消息要告訴你
壞消息是:沒有銀彈。沒有任何一個框架是在所有層面都優于其他框架的。任何一個框架都有它的優點和妥協。React 有它的 hook 規則,Angular 缺乏簡單的 signals,Vue 的向后兼容性問題,Svelte 的伸縮性不太好,Solid.js 禁止解構,Mithril.js 不是真正的響應式,等等
好消息是:沒有錯誤選擇 —— 除非項目的要求確實受到限制,無論是在捆綁包大小還是性能方面。每個框架都可以完成工作。有些人可能需要解決他們的設計決策,這可能會使你的速度變慢,但無論如何你都能夠獲得可行的結果。
話雖這么說,沒有框架也可能是一個可行的選擇。許多項目都被過度使用 JavaScript 破壞了,而帶有一些交互性的靜態頁面也可以完成這項工作。
現在你已經了解了這些框架和庫所應用的概念,請選擇最適合你當前任務的方案。不要為下個項目的框架選型而感到擔心。你不需要學習所有的內容。
如果你嘗試一個新的框架,我發現最有幫助的事情之一就是關注它的社區,無論是在社交媒體、Discord、github 還是其他地方。他們可以告訴你哪些方法適合他們的框架,這將幫助你更快地獲得更好的解決方案。
如果你的主要目標是就業,我建議學習 React 或者 Vue。 如果你想要輕松的獲取性能和控制體驗,請嘗試 Solid.js
但請記住,所有其他選擇都同樣有效。 你不應該因為我這么說就選擇一個框架,而應該使用最適合你的框架。
如果你看完了整篇文章,感謝你的耐心等待。 希望對你有所幫助。 在這里發表你的評論,祝你有美好的一天
本文原文地址:https://dev.to/lexlohr/concepts-behind-modern-frameworks-4m1g
本文代碼倉庫地址:https://github.com/zidanDirk/concepts-behind-modern-frameworks/
*請認真填寫需求信息,我們會在24小時內與您取得聯系。