你們好,此次給你們講下 Omi 框架 以及即將發佈的 Omim 你們有沒有數左邊的圖片裏有多少個 Omi?Omi 團隊很在乎這裏,特地數了下,有三個。Omi 團隊但願 Omi 之後在各大會議裏可以印刷得更加大一些。今天給你們帶來的主題是 《Omi - Cross-Frameworks Framework》,這也是 Omi 最新的 slogan。Omi 基於 Web Components 設計,和三大框架並非你死我活的關係,能夠很好的共存,無縫地集成,等聽完這個分享你們就能 get 到 Omi 的精髓。子標題的靈感來自於 preact 做者之前打算分享的《Push react to the limit》,原本 TFC2017 邀請了他,後來我的緣由他沒有來,固然他後來去了google,可能這個標題政治不正確:)。css
這是此次分享所包含的內容,不是目錄。會涉及到上面4部份內容。html
第一部分講下 Omi & Web Components。vue
Omi 準確來講是 5-6年前開始弄的,可是真正切換到 shadow dom v1 仍是2018年10月份左右,比 google 的 angular 更早使用 shadow dom v1。切換以後,獲得了一段快速發展的時期,一致延續到如今。當時是什麼初衷讓我開始打造 Omi?react
先來看看 react 的生命週期。當年看 React 的生命週期函數太長並且太多了,因此我要寫個生命週期函數更短且更少的框架,好比 Omi 的(install
, uninstall
, installed
),並且貼合 npm 的命令:)。git
看下 Omi 中的生命週期,沒有與對應的 shouldComponentUpdate
對應的生命週期函數,沒有 state 初始化、沒有 constructer 函數,生命週期函數命名更加端、生命週期函數更加少。大道至簡是真理,Omi 但願開發者不用手動優化 shouldComponentUpdate
,怎麼作到的,後面 PPT 會講。github
打造 Omi 的初衷,還有一點很是重要的是,scoped css!scoped css 可讓組件的 css 選擇器更加簡潔,好比能夠直接寫 tag selector,若是組件裏只有一個 h1 的話。web
h1 {
color: red;
}
複製代碼
這個 h1 不會污染組件外部的 h1,也不會污染組件內部的組件的 h1。chrome
來看下 ng。ng 支持四種模式typescript
這是第一種模式,ng 模擬 scope 生成的 html 結構,每一個元素和子元素都會加上 scope attr。npm
這是第三種模式,html 結構什麼 attr 都不加。
這是第四種模式,也就是 shadow dom 的模式,和 Omi 同樣。
Omio 使用的也是 scope attr 模擬 shadow dom,這裏在內部實現的時候有兩點須要注意:
有點繞,仔細體會下。即 scoped 到組件爲止,包括組件不進入組件。
這是 Omi 和 React 生成的 dom 結構對比。Omi 使用 shadow dom 進行隔離。
來看這張圖,很關鍵。這張圖來自 twiter。twiter 有個問題是 web components 能不能替換掉整個框架?stencil 團隊 show 出了這張圖,給 Omi 團隊帶來了很大的靈感。Omi 不只僅要自帶狀態管理、自帶組件體系替換掉整個框架,也能輸出單一的 custom elements 和任意框架集成。仔細看上圖,stencil 在各個框架中使用仍是有一些差別。
Omi 作得更加完全!全部框架使用 custom elements 方式如出一轍!徹底不用記憶差別,只靠肌肉記憶編程!Omi 的組件變動也是徹底基於 setAttribute
和 removeAttribut
,這種設計關鍵,由於其餘框架無論 vdom 仍是 real dom,最後都須要操做 dom 的 attribute,操做 attribute 就能進入到 Omi 的元素週期內部,這樣就無縫勾住了,在任意框架都能使用 Omi 寫的 custom elements。這裏有一點須要注意:
static propTypes
這個其實也不麻煩,由於若是你使用 typescript 寫組件的話,自己就須要聲明 props 的類型,改爲大寫就能用在 propTypes 上。Omi 內部會根據聲明的類型,把使用組件時候傳入的字符串轉化爲對應的類型。
來看個實際的例子,這個 m-button
能夠在任意框架中使用。須要注意的是,能夠傳遞 json 字符串做爲 attr 給 m-button
內部使用。能夠看到上面的 icon 的 paths 並非標準的 json,可是沒有關係,omi 內部會轉成標準 json 再進行 JSON.parse 。並且你能夠直接使用 setAttribute 設置 json。
const btn = document.querySelector('m-button')
btn.setAttribute('icon', {
path: 'xx',
color: 'xxx'
})
複製代碼
這裏須要注意一下,若是你再 react 中使用 Omi 的 element,並且須要傳遞 json 的話,能夠包裹一下 Omi.o
,免得手動轉成字符串:
class TestOmiElement extends React.Component {
render() {
return (
<div> <m-button icon={Omi.o({ path: 'xxx', color: 'xxxx' })}>omi button</m-button> </div> ); } } 複製代碼
這是 Omi 的初始化管線,前面說了 Omi 利用了 HTMLElement
鉤子函數 connectedCallback
監聽自定義元素插入到 dom 的行爲,這個行爲多是 Omi 觸發的,也能夠是 react、vue、ng 或原生 js 觸發的,觸發以後就進入了 Omi 的內部管線。
很清晰簡明的管線。JSX 生成的虛擬 dom 會掛載在真實 dom 的 __omiattr_
屬性上用於 dfii。 有沒有必有使用虛擬 dom?有必要!固然也能夠修改 h 函數保存真實 dom 用於 diff,主要區別在於內存開銷,速度差異不大。由於虛擬 dom 更輕量,屬性更少,都是必要屬性,因此用虛擬 dom。
看下 web components 最經常使用的三個生命週期函數。在 connectedCallback 會去執行 Omi 的 install,在 disconnectedCallback 會去執行 Omi 的 uninstall,最後一個 attributeChangedCallback 在 Omi 內部並無使用,由於 Omi 重寫了自定義元素的 setAttribute,從源頭上已經能夠監控到 attr change。
看下這段從 Omi 扒出來的源碼,Omi 重寫了 removeAttribute 和 setAttribute,這兩個方法被調用會自動觸發組件的更新,固然 Omi 也保留了原生的 removeAttribute 和 setAttribute,以 pure 開頭,用於 Omi 內部使用,由於內部 diff 和 apply diff 的時候並非須要每次都須要調用組件的 update。
看下這張圖,很關鍵。用過 react 的同窗都知道,react 性能優化的關鍵就是 shouldComponentUpdate
,最被詬病的也是這個,不少開發者也吐槽這個鉤子函數,shouldComponentUpdate
也能夠配合不可變數據類型,直接進行引用地址比較,來決定組件是否須要更新。Omi 團隊一直在思考,這個東西可不能夠去掉,實時證實是能夠的。由於 Omi 自定義元素徹底基於字符串傳遞 props,不論是 boolean、string、number、json,都經過字符串傳遞,因此在進行組件更新以前,Omi 會進行一次淺比較,比較的結果決定是否更新,很是機智的作法。
在使用 web components 過程當中,最被你們詬病的就是樣式穿透問題。你們有些場景就是須要穿透組件怎麼辦?穿透不了就只能重寫組件了,或者修改組件的源碼。這個項目維護會帶來巨大的問題。Omi 爲了解決這個問題,支持屬性 css,用於覆蓋組件內部的 css 樣式。看上面的代碼,h1 是紅色的。
在父組件中使用my-element
,經過傳遞 css,把 h1 顏色改爲 green 。並且還能夠動態修改組件內部的樣式,能夠 onClick 內部的代碼便知。
有的時候,咱們不知道咱們外部注入組件的選擇器權重是否足夠,咱們能夠經過加上 !important
保證必定覆蓋掉組件內部的樣式。
關於樣式,還有一點須要注意,font-fact 不能放在 shadow dom 中,否則不生效。固然這裏瀏覽器有差別,火狐能夠放在 shadow dom 中,google chrome 不行。因此咱們通常都放在外面用來兼容全部瀏覽器。
使用 font-face 的 font-family 的 class 定義必須放在 shadow dom 中,這個是有點割裂,可是仍是很好理解,由於 shadow dom 自己就是隔離的。
除了賦能 web 其餘框架,Omi 也提供了替換整個框架的解決方案,且看 store 體系。 store 體系是用於組件樹共享數據和邏輯,若是熟悉 react context api 的必定了解,可是 Omi store 不徹底同樣,store 上 data 的變動會產生一條 path,path 會去和組件上定義的 path 匹配,匹配上了就會進行更新,全部就達到了 局部 diff,局部更新 的目的。
看這是一個靜態聲明 path 的例子,固然也可使用 initUse
聲明動態 path。看上圖,計算屬性也能夠放在聲明裏。能夠這樣理解,store 是一個數據源頭,關於數據最後要怎麼預處理建議不要放在 store 裏,而是放在組件的 use 聲明裏,好比你要對字符串反轉,或者對數字平方什麼的,不建議放在 store 裏。
在 render 裏可使用 this.use
去訪問 store 的數據,固然也能夠經過 this.store.data.xx
的方式,後者書寫起來稍微麻煩一些。
這是命中的規則,第一列是由 proxy 的數據變動產生的 path,後者是組件定義的 path,只要命中一條就會更新組件。store 整個體系設計得很是簡單直接方便,沒有複雜的概念。
Omi 對 typescript 支持愈來愈好,後面 Omi 生態新增的全部組件、工具後者類庫都會使用 typescript 來書寫。上圖解釋了怎麼讓組件使用者可以在 typescript 或者智能提示。注意 Omi.props & ...
的目的是把 HTML 標準的屬性也集成到智能提示上,自己 Omi 寫的自定義元素就是標準的 HTMLElement。使用 JSX 寫 Omi 元素的時候,在 ts 的檢查會更加寬鬆,好比 tabindex="1"
,在 react ts 中必須寫成 tabIndex={1}
,Omi 順從 HTML 標籤,一段 HTML 直接粘貼到 render 函數中就能夠用,固然自閉合的標籤必須手動閉合下,好比 img。
在使用原生的 web components 的 customElements.define 的使用,體驗很很差。重複定義直接報錯,並且從報錯信息上也看不出是什麼元素重複定義了。
Omi 基於 customElement.define 封裝了 define 方法,有了前置的檢查邏輯,不報錯,只改告警,並且重複的名稱也會突出打印出來。
這是即將發佈的跨框架組件庫,一羣優秀的工程師正在加班加點趕進度,若是你感興趣也能夠加入進來,咱們一塊兒打造標準化的通用組件庫,框架無關、主題任意切換。
看上面這張圖,這是一個通用的組件,標準的自定義元素。能夠在 jsx 中使用,也能夠以標準的 html 的形式在任意框架(omi、react、vue、ng)中使用。將來 omim 提供的組件都會提供這兩種形式,方便任意形式的使用。
PPT以及演講內容以圖文的形式還原
待更新...
待更新...