原文連接javascript
本文使用starter-kit:steamer-react react分支。此分支已集成react與preact。html
最近接手了互動視頻的項目,作了一個月的運營活動。跟基礎功能不一樣,運營活動更爲輕量。所以許多同事並不想用那麼「重」的React。但同時,你們因爲以前度過React的上手痛苦期後,開始體會到React的許多好處,裸寫運營活動的時候,又開始對React的好處念念不忘記:良好的組件化、解放js能力的jsx等。
所以,尋找輕量化的類React解決方案便提上日程。java
選型的時候,首先有幾個考量:node
基本上以上幾點,Preact都可以很好的知足,所以最終選定爲團隊的類React輕量化框架進行使用和研究。react
相比起react-lite,Deku, Virtual-DOM,Preact雖然不是最多的star,但也能排第2,也具有測試用例,且做者開通了gitter chat跟開發者保持聯繫,某天在上面留言,做者也是回覆得很迅速。git
性能方面,Preact也不俗。加載性能方面,因爲自己的bundle在gzip後大概只有3kb,跟React相比小太多,自由就有優點。渲染性能方面,參考了一篇JS WEB FRAMEWORKS BENCHMARK系列測評文章,發現Preact在建立、更新、刪除節點等方面,都有良好的表現。github
第一次性能測試:web
第二次性能測試:npm
包大小:redux
framework | version | minimized size |
---|---|---|
React | 0.14.3 | 136.0kb |
React-lite | 0.15.6 | 25kb |
Preact | 5.6.0 | 10kb |
Deku | 2.0.0-rc16 | 51.2kb |
Virtual Dom | 2.1.1 | 50.5kb |
除了性能的良好表現,此框架的瀏覽器兼容性也不錯,能兼容目前的主流瀏覽器,而且在添加polyfill的狀況下,可以兼容在國內還有很多份額的IE8,確實是很多還須要兼容IE8開發者的福音。
Preact的經常使用api基本跟React一致,這使得對React熟悉的開發者,徹底沒有上手的難度,Preact做者單獨開闢了一個文檔Differences to React,介紹React與Preact的異同。Preact主要缺乏的React Api有PropType,Children, 和 Synthetic Events(合成事件)。做者解釋道,PropType其實許多人都不使用,並不影響開發; Children實際上是數組,因此也並非必須的;而合成事件,因爲不須要過分考慮不一樣瀏覽器對事件處理的異同,因此也並無作過分封裝。若是真的想使用以上這些缺失的React Api,做者也提供了preact-compat,使用的時候,在Webpack上的external這樣替換即可:
{
// ...
resolve: {
alias: {
'react': 'preact-compat',
'react-dom': 'preact-compat'
}
}
// ...
}複製代碼
對於React開發者來講,最經常使用的就是redux, router這些周邊的插件。而Preact也有提供preact-redux和preact-router,甚至還有幫助Preact作同構直出的preact-render-to-string。
Preact項目的框架小而美,合併成的dist文件也只有500行左右,比較容易學習和維護。若團隊選擇此框架做爲React的輕量解決方案的話,咱們最好能具有維護和開發此框架的能力,這可以在遇到bug的時候第一時間修復,並且可以很好地開發一些組件,提高框架的開發效率。
做者在Getting Started裏有比較好的介紹。其實不外乎就2點差別:
引入preact與引入react的差別。
引入preact的時候,大概是這樣的:
import preact, { h, render, Component } from 'preact';複製代碼
而引入react的時候,大概是這樣的:
import React, { Component, PropTypes } from 'react';
import { render } from 'react-dom';複製代碼
編譯所需的插件差別。
preact的jsx編譯,主要藉助babel-plugin-transform-react-jsx,而react則是藉助babel-preset-react。
若是你想在一個構建裏面同時使用React和Preact(有的頁面使用React,有的用Preact),你能夠經過Webapck的loader include或者exclude,而後憑路徑區分。而我在steamer-react的react-preact分支裏的處理是直接用文件名後綴。若是是有React相關引入的,則用.js
後綴,而有Preact相關引入的,則用.jsx
後綴。
粗略看了一下Preact的實現,簡單介紹一下。
Virtual Dom算是類React框架的最大賣點。Preac做者寫了一篇WTF is JSX。主要就是藉助babel-plugin-transform-react-jsx的能力,裏面有個pragma參數,用於設定用什麼函數來作virtual dom的轉換。此處定義的是preact.h
["transform-react-jsx", { "pragma":"preact.h" }]複製代碼
因此,你會看到編譯後,有相似的代碼:
_preact2.default.h(
'p',
{ className: 'info-content' },
item.des
)複製代碼
查看源碼,preac定義了h
的函數,用於將傳入的值轉換成virtual dom:
function h(nodeName, attributes, firstChild) {
// some code here
}複製代碼
因此,若是傳入上面的p和對應屬性,則會轉換成下面的對象:
VNode {nodeName: "p", attributes: {class:"info-content"}, children: undefined, key: undefined}複製代碼
但virtual dom須要轉換成真實的dom,還須要一個函數進行轉換。在Preact中,大致是經過這個流程,而後最終轉換成真實dom:
render (相似於react-dom裏的render,主入口,觸發渲染) => diff => idiff (看起來應該是作dom diff) => createNode (生成真實dom)複製代碼
組件化也是類React框架的一大特點。Preact的組件化,主要是經過Component
這一方法來實現的。主要包括,setState,render以及一衆生命週期。主要的渲染,生命週期的觸發,也主要定義在renderComponent
和setComponentProps
方法內。用戶的自定義組件只須要繼承Component
就能夠自由使用Preact組件化的能力。
Preact並無像React那樣本身實現了一套事件機制,主要仍是用瀏覽器自帶的能力。所以,在給生成真實dom並經過setAccessor
給dom插入屬性的時候,有這麼一段代碼:
else if ('o' === name[0] && 'n' === name[1]) {
var l = node._listeners || (node._listeners = {});
name = toLowerCase(name.substring(2));
if (value) {
if (!l[name]) node.addEventListener(name, eventProxy);
} else if (l[name]) node.removeEventListener(name, eventProxy);
l[name] = value;
}複製代碼
判斷屬性中是否含有o
和n
,也就是在看,有沒有on
開頭的屬性(通常就是事件)。而後就進行addEventListener或者removeEventListener。看起來跟咱們寫原生js的事件綁定沒有什麼區別。
若有錯誤,懇請斧正。