preact做爲備胎,可是具備體積小,diff算法優化過的特色,簡單活動頁用上它是不錯的選擇。可是考慮到react使人興奮的新特性,preact並無按時更新去徹底支持它,更嚴重的是一些babel插件、一些庫配合preact會有問題。因此,仍是不得不遷移了。javascript
如何遷移?package.json直接修改版本,刪掉preact,重裝,完事!java
too youngreact
首先,通常是這樣子接入preact的,使得咱們代碼裏面毫無感受咱們用的是preact。在webpack的alias裏面配置:webpack
alias: {
react: 'preact-compat',
'react-dom': 'preact-compat'
},
複製代碼
因此,第一步先把這個去掉web
render() {
return (
[
<div key="container">2</div>,
<div key="more">1</div>,
]
);
}
複製代碼
<div style={`background: url(${this.props.h5img});`} />
複製代碼
首先,import的preact-router得換成react-router。而後,將對應的語法和生態遷移到react相關的。算法
preact-router:npm
<Router history={history}>
<Main path="/" history={history} />
<GetGiftForm path="/get_gift_form" history={history} />
<Join path="/join" history={history} />
<NewUser path="/new_user" history={history} />
</Router>
複製代碼
react-router:json
<Router history={history}>
<div>
<Route exact path="/" history={history} component={Main} />
<Route path="/get_gift_form" history={history} component={GetGiftForm} />
<Route path="/join" history={history} component={Join} />
<Route path="/new_user" history={history} component={NewUser} />
</div>
</Router>
複製代碼
preact-router有一個route方法,就是直接將路由push或者replace的,而react-router是沒有這個方法的。實際上底層就是封裝history路由加上內部的setstate:數組
import { route } from 'preact-router';
route('/a');
複製代碼
問題來了,若是沒有這個方法,想用腳本跳轉路由怎麼辦?直接history上改,只能改地址欄的url顯示但不能更新組件以及內部狀態。因此咱們只能找和react-router配合起來用的相關的庫。安全
觀察一下,都用到了history屬性,傳入一個history,這個是用了一個history的庫建立的,因此咱們儘量的讓它接入兩種路由獲得同樣的效果:
import createHashHistory from 'history/createHashHistory';
const history = createHashHistory();
複製代碼
打印了history,發現它有push、replace屬性,大概也猜到應該就是像route的效果的,一驗證發現可行:
history.push('/a');
複製代碼
另外,還有preact-router的路由更新監聽是這樣的:
<Router history={history} onChange={this.handleRoute}>
......
</Router>
複製代碼
切換到react的話,沒有這個方法。因而咱們繼續找history,發現有一個listen屬性,看名字是一個監聽函數,也就是能夠實現路由更新監聽的:
history.listen((location) => {
...
});
複製代碼
用preact-router的時候,有些組件是異步的:
<Router history={history}>
{ trackRoute() }
</Router>
複製代碼
trackRoute函數組件:
import React from 'react';
import AsyncRoute from 'preact-async-route';
export default () => {
return (
<AsyncRoute path="/track" getComponent={async () => { return import('./comp' /* webpackChunkName: 'async_track' */); }} /> ); }; 複製代碼
效果就是,動態import,代碼分割。react也有一個相似的,react-async-router,可是用法和咱們的以前的preact-async-route差得遠並且不能優雅接入。既然是16.6.7了,咱們能夠試一下新特性:lazy+suspence
import React, { lazy, Suspense } from 'react';
const Comp = lazy(() => import('./comp' /* webpackChunkName: 'async_track' */));
function TrackRoute() {
return (
<Suspense fallback={<div />}> <Comp /> </Suspense>
);
}
export default TrackRoute;
複製代碼
有一個頁面是這樣的:
// Main.jsx
render() {
return (
<div> <Page1 /> <Page2 /> <Page3 /> ... </div>
);
}
複製代碼
除了page1是原來就在的,其餘每個Pagex組件,返回Page組件,在Page內部,當頁碼是當前頁返回對應的元素,不然返回空:
// Pagex
render() {
return (
<Page />
);
}
// Page
render() {
return currentPage === page ? <somedom> : null
}
複製代碼
這裏,咱們能夠猜一下,Main是最大的組件,內部狀態頁碼在切換,全部的Pagex組件跟着更新,作出對應的變化。Pagex的更新,走的是didupdate。
實際上,preact的是第一個內部是Page實現的Pagex組件會unmount而後從新didmount。這裏是Page2先卸載再掛載,交換位置page1直接到page3的話也是page3先卸載再掛載。一些動畫操做就放在了didmount,以前都是這樣作的,但你們沒有發現是什麼問題,由於看見是這樣,開發起來沒毛病,又沒有bug,就不太在乎。切換回react,發現動畫不生效,才發現由於內部渲染機制不同致使的。因此咱們把函數的調用放在didupdate裏面,而且加上執行過一次的標記判斷。
作一個像qq聊天起跑那樣的東西,頭和腳固定,中間無限長。這裏要求視覺給3個圖,頭、腳、中間1px高的圖。若是內容不滿屏,不顯示腳不能滾動,若是大於1屏顯示腳。
這裏固然少不了原生dom操做了,須要判斷高度,有沒有大於1屏,要不要overflow:hidden:
componentDidUpdate() {
if (this.state.hasFooter) {
return;
}
const last = document.querySelector('.act-card:last-of-type');
if (last) {
const { top } = last.getBoundingClientRect();
if (SCREEN_HEIGHT - top < MAX_BOTTOM_DISTANCE) {
setTimeout(() => {
this.setState({ hasFooter: true }); // eslint:不能在didupdate裏面setstate
});
document.body.style.overflow = 'auto';
} else {
document.body.style.overflow = 'hidden';
}
}
}
複製代碼
這裏須要執行兩次下面一大塊邏輯,第二次是無必要的,咱們能夠利用getSnapshotBeforeUpdate生命週期配合didupdate使用:
getSnapshotBeforeUpdate(_, prevstate) {
if (!prevstate.hasFooter && prevstate.actCards.length) {
return true;
}
return null;
}
componentDidUpdate(_, __, snapshot) {
if (!snapshot) {
return;
}
......
}
複製代碼
能夠說函數式組件的purecomponent,並且第二個參數能傳入第二個相似shouldComponentUpdate的函數進行比較。既然能自定義化,那麼對比於purecomponent的自帶對象淺比較就是更加的靈活了,好比:
import React, { memo } from 'react';
export default memo((props) => {
const isNoAct = !props.actCards.length || props.loading;
return (
<section className="body-container"> <div> { !isNoAct ? props.actCards.map((actCard, index) => ( <div key={index} > ...... </div> )) : ( <div className="no"> 暫無 </div> ) } </div> {props.hasFooter && <div className="body-footer" src={footer} alt="err" />} </section> ); }, (prevprops, nextprops) => { // 少渲染一次,一開始actcards什麼都沒有,咱們不比較actcards數組 if (prevprops.hasFooter !== nextprops.hasFooter || prevprops.loading !== nextprops.loading) { return false; } return true; }); 複製代碼
這裏咱們就少了一次從actcards數組的undefined
到[]
的過程的比較,而這時候一直是loading狀態,沒有更新的意義。若是這裏return以前又是有像前面那個聊天氣泡那種效果須要dom操做的,那就傷性能了。這裏我列舉出來的只是把代碼刪減過的簡單結果,實際上開發的時候邏輯是遠遠比這demo複雜的