react16新功能:1、render支援返回陣列和字串;2、錯誤邊界;3、createPortal;4、支援自訂DOM屬性;5、Fiber;6、Fragment;7、createContext;8 、Hooks等等。
【相關教學推薦:React影片教學】
React v16.0
// 不需要再将元素作为子元素装载到根元素下面 render() { return [ <li/>1</li>, <li/>2</li>, <li/>3</li>, ]; }
React15 在渲染過程中遇到執行階段的錯誤,會導致整個React 元件的崩潰,而且錯誤訊息不明確可讀性差。 React16 支援了更優雅的錯誤處理策略,如果一個錯誤是在元件的渲染或生命週期方法中被拋出,整個元件結構就會從根節點中卸載,而不影響其他元件的渲染,可以利用error boundaries進行錯誤的優化處理。
class ErrorBoundary extends React.Component { state = { hasError: false }; componentDidCatch(error, info) { this.setState({ hasError: true }); logErrorToMyService(error, info); } render() { if (this.state.hasError) { return <h1>数据错误</h1>; } return this.props.children; } }
createPortal 的出現為彈跳窗、對話方塊等脫離文件流的元件開發提供了便利,取代了先前不穩定的API unstable_renderSubtreeIntoContainer,在程式碼使用上可以做相容,如:
const isReact16 = ReactDOM.createPortal !== undefined; const getCreatePortal = () => isReact16 ? ReactDOM.createPortal : ReactDOM.unstable_renderSubtreeIntoContainer;
使用createPortal 可以快速建立Dialog 元件,且不需要牽扯到componentDidMount、componentDidUpdate 等生命週期函數。
並且透過 createPortal 渲染的 DOM,事件可以從 portal 的入口端冒泡上來,如果入口端存在 onDialogClick 等事件,createPortal 中的 DOM 也能夠被呼叫到。
import React from 'react'; import { createPortal } from 'react-dom'; class Dialog extends React.Component { constructor() { super(props); this.node = document.createElement('div'); document.body.appendChild(this.node); } render() { return createPortal( <div> {this.props.children} </div>, this.node ); } }
以前的 React 版本 DOM 不識別除了 HTML 和 SVG 支援的以外屬性,在 React16 版本中將會把全部的屬性傳遞給 DOM 元素。這個新功能可以讓我們擺脫可用的 React DOM 屬性白名單。筆者之前寫過一個方法,用於過濾非 DOM 屬性 filter-react-dom-props,16 之後即可不再需要這樣的方法。
React16 使用 Rollup 針對不同的目標格式進行程式碼打包,由於打包工具的改變使得函式庫檔案大小被縮減。
#Fiber 是對React 核心演算法的重新實現,將原本的同步更新過程碎片化,避免主執行緒的長時間阻塞,使應用程式的渲染更加流暢。
在 React16 之前,更新元件時會呼叫各個元件的生命週期函數,計算和比對 Virtual DOM,更新 DOM 樹等,這整個過程是同步進行的,中途無法中斷。當元件比較龐大,更新操作耗時較長時,就會導致瀏覽器唯一的主執行緒都是執行元件更新操作,而無法回應使用者的輸入或動畫的渲染,很影響使用者體驗。
Fiber 利用分片的思想,把一個耗時長的任務分成很多小片,每一個小片的運行時間很短,在每個小片執行完之後,就把控制權交還給React 負責任務協調的模組,如果有緊急任務就去優先處理,如果沒有就繼續更新,這樣就給其他任務一個執行的機會,唯一的線程就不會一直被獨佔。
因此,在元件更新時有可能一個更新任務還沒完成,就被另一個更高優先級的更新過程打斷,優先級高的更新任務會優先處理完,而低優先級更新任務所做的工作則會完全作廢,然後等待機會重頭再來。所以React Fiber 把一個更新過程分成兩個階段:
在使用Fiber 後,需要檢查與第一階段相關的生命週期函數,避免邏輯的多次或重複呼叫:
#React v16 .1
Call Return(react-call-return npm)
react-call-return 目前還是一個獨立的npm 包,主要是針對父元件需要根據子元件的回呼資訊去渲染子元件場景提供的解決方案。
###在 React16 之前,針對上述場景一般有兩個解:###React16 支持的 react-call-return,提供了两个函数 unstable_createCall 和 unstable_createReturn,其中 unstable_createCall 是 父组件使用,unstable_createReturn 是 子组件使用,父组件发出 Call,子组件响应这个 Call,即 Return。
针对普通场景来说,react-call-return 有点过度设计的感觉,但是如果针对一些特定场景的话,它的作用还是非常明显,比如,在渲染瀑布流布局时,利用 react-call-return 可以先缓存子组件的 ReactElement,等必要的信息足够之后父组件再触发 render,完成渲染。
import React from 'react'; import { unstable_createReturn, unstable_createCall } from 'react-call-return'; const Child = (props) => { return unstable_createReturn({ size: props.children.length, renderItem: (partSize, totalSize) => { return <div>{ props.children } { partSize } / { totalSize }</div>; } }); }; const Parent = (props) => { return ( <div> { unstable_createCall( props.children, (props, returnValues) => { const totalSize = returnValues.map(v => v.size).reduce((a, b) => a + b, 0); return returnValues.map(({ size, renderItem }) => { return renderItem(size, totalSize); }); }, props ) } </div> ); };
React v16.2
Fragment 组件其作用是可以将一些子元素添加到 DOM tree 上且不需要为这些元素提供额外的父节点,相当于 render 返回数组元素。
render() { return ( <Fragment> Some text. <h2>A heading</h2> More text. <h2>Another heading</h2> Even more text. </Fragment> ); }
React v16.3
全新的 Context API 可以很容易穿透组件而无副作用,其包含三部分:React.createContext,Provider,Consumer。
const ThemeContext = React.createContext('light'); class ThemeProvider extends React.Component { state = {theme: 'light'}; render() { return ( <ThemeContext.Provider value={this.state.theme}> {this.props.children} </ThemeContext.Provider> ); } } class ThemedButton extends React.Component { render() { return ( <ThemeContext.Consumer> {theme => <Button theme={theme} />} </ThemeContext.Consumer> ); } }
React16 规范了 Ref 的获取方式,通过 React.createRef 取得 Ref 对象。
// before React 16 ··· componentDidMount() { const el = this.refs.myRef } render() { return <div ref="myRef" /> } ··· // React 16+ constructor(props) { super(props) this.myRef = React.createRef() } render() { return <div ref={this.myRef} /> } ···
React.forwardRef 是 Ref 的转发, 它能够让父组件访问到子组件的 Ref,从而操作子组件的 DOM。 React.forwardRef 接收一个函数,函数参数有 props 和 ref。
const TextInput = React.forwardRef((props, ref) => ( <input type="text" placeholder="Hello forwardRef" ref={ref} /> )) const inputRef = React.createRef() class App extends Component { constructor(props) { super(props) this.myRef = React.createRef() } handleSubmit = event => { event.preventDefault() alert('input value is:' + inputRef.current.value) } render() { return ( <form onSubmit={this.handleSubmit}> <TextInput ref={inputRef} /> <button type="submit">Submit</button> </form> ) } }
React16 采用了新的内核架构 Fiber,Fiber 将组件更新分为两个阶段:Render Parse 和 Commit Parse,因此 React 也引入了 getDerivedStateFromProps 、 getSnapshotBeforeUpdate 及 componentDidCatch 等三个全新的生命周期函数。同时也将 componentWillMount、componentWillReceiveProps 和 componentWillUpdate 标记为不安全的方法。
getDerivedStateFromProps(nextProps, prevState) 其作用是根据传递的 props 来更新 state。它的一大特点是无副作用,由于处在 Render Phase 阶段,所以在每次的更新都会触发该函数, 在 API 设计上采用了静态方法,使其无法访问实例、无法通过 ref 访问到 DOM 对象等,保证了该函数的纯粹高效。
为了配合未来的 React 异步渲染机制,React v16.4 对 getDerivedStateFromProps 做了一些改变, 使其不仅在 props 更新时会被调用,setState 时也会被触发。
static getDerivedStateFromProps(props, state) { if (props.value !== state.controlledValue) { return { controlledValue: props.value, }; } return null; }
getSnapshotBeforeUpdate(prevProps, prevState) 会在组件更新之前获取一个 snapshot,并可以将计算得的值或从 DOM 得到的信息传递到 componentDidUpdate(prevProps, prevState, snapshot) 函数的第三个参数,常常用于 scroll 位置定位等场景。
componentDidCatch 函数让开发者可以自主处理错误信息,诸如错误展示,上报错误等,用户可以创建自己的 Error Boundary 来捕获错误。
componentWillMount 被标记为不安全,因为在 componentWillMount 中获取异步数据或进行事件订阅等操作会产生一些问题,比如无法保证在 componentWillUnmount 中取消掉相应的事件订阅,或者导致多次重复获取异步数据等问题。
componentWillReceiveProps / componentWillUpdate 被标记为不安全,主要是因为操作 props 引起的 re-render 问题,并且对 DOM 的更新操作也可能导致重新渲染。
StrictMode 可以在开发阶段开启严格模式,发现应用存在的潜在问题,提升应用的健壮性,其主要能检测下列问题:
class App extends React.Component { render() { return ( <div> <React.StrictMode> <ComponentA /> </React.StrictMode> </div> ) } }
React v16.4
指针事件是为指针设备触发的 DOM 事件。它们旨在创建单个 DOM 事件模型来处理指向输入设备,例如鼠标,笔 / 触控笔或触摸(例如一个或多个手指)。指针是一个与硬件无关的设备,可以定位一组特定的屏幕坐标。拥有指针的单个事件模型可以简化创建 Web 站点和应用程序,并提供良好的用户体验,无论用户的硬件如何。但是,对于需要特定于设备的处理的场景,指针事件定义了一个 pointerType 属性,用于检查产生事件的设备类型。
React 新增 onPointerDown / onPointerMove / onPointerUp / onPointerCancel / onGotPointerCapture / onLostPointerCapture / onPointerEnter / onPointerLeave / onPointerOver / onPointerOut 等指针事件。
这些事件只能在支持 指针事件 规范的浏览器中工作。如果应用程序依赖于指针事件,建议使用第三方指针事件 polyfill。
React v16.5
React 16.5 添加了对新的 profiler DevTools 插件的支持。这个插件使用 React 的 Profiler 实验性 API 去收集所有 component 的渲染时间,目的是为了找出 React App 的性能瓶颈,它将会和 React 即将发布的 时间片 特性完全兼容。
React v16.6
React.memo() 只能作用在简单的函数组件上,本质是一个高阶函数,可以自动帮助组件执行shouldComponentUpdate(),但只是执行浅比较,其意义和价值有限。
const MemoizedComponent = React.memo(props => { /* 只在 props 更改的时候才会重新渲染 */ });
React.lazy() 提供了动态 import 组件的能力,实现代码分割。
Suspense 作用是在等待组件时 suspend(暂停)渲染,并显示加载标识。
目前 React v16.6 中 Suspense 只支持一个场景,即使用 React.lazy() 和
import React, {lazy, Suspense} from 'react'; const OtherComponent = lazy(() => import('./OtherComponent')); function MyComponent() { return ( <Suspense fallback={<div>Loading...</div>}> <OtherComponent /> </Suspense> ); }
static contextType 为 Context API 提供了更加便捷的使用体验,可以通过 this.context 来访问 Context。
const MyContext = React.createContext(); class MyClass extends React.Component { static contextType = MyContext; componentDidMount() { const value = this.context; } componentDidUpdate() { const value = this.context; } componentWillUnmount() { const value = this.context; } render() { const value = this.context; } }
static getDerivedStateFromError(error) 允许开发者在 render 完成之前渲染 Fallback UI,该生命周期函数触发的条件是子组件抛出错误,getDerivedStateFromError 接收到这个错误参数后更新 state。
class ErrorBoundary extends React.Component { state = { hasError: false }; static getDerivedStateFromError(error) { // Update state so the next render will show the fallback UI. return { hasError: true }; } componentDidCatch(error, info) { // You can also log the error to an error reporting service logErrorToMyService(error, info); } render() { if (this.state.hasError) { // You can render any custom fallback UI return <h1>Something went wrong.</h1>; } return this.props.children; } }
React v16.7
Hooks 要解决的是状态逻辑复用问题,且不会产生 JSX 嵌套地狱,其特性如下:
Hooks 并不是通过 Proxy 或者 getters 实现,而是通过数组实现,每次 useState 都会改变下标,如果 useState 被包裹在 condition 中,那每次执行的下标就可能对不上,导致 useState 导出的 setter 更新错数据。
function App() { const [open, setOpen] = useState(false); return ( <> <Button type="primary" onClick={() => setOpen(true)}> Open Modal </Button> <Modal visible={open} onOk={() => setOpen(false)} onCancel={() => setOpen(false)} /> </> ); }
React v16.8
Concurrent Rendering 并发渲染模式是在不阻塞主线程的情况下渲染组件树,使 React 应用响应性更流畅,它允许 React 中断耗时的渲染,去处理高优先级的事件,如用户输入等,还能在高速连接时跳过不必要的加载状态,用以改善 Suspense 的用户体验。
目前 Concurrent Rendering 尚未正式发布,也没有详细相关文档,需要等待 React 团队的正式发布。
React v16.9
Suspense 通过 ComponentDidCatch 实现用同步的方式编写异步数据的请求,并且没有使用 yield / async / await,其流程:调用 render 函数 -> 发现有异步请求 -> 暂停渲染,等待异步请求结果 -> 渲染展示数据。
无论是什么异常,JavaScript 都能捕获,React就是利用了这个语言特性,通过 ComponentDidCatch 捕获了所有生命周期函数、render函数等,以及事件回调中的错误。如果有缓存则读取缓存数据,如果没有缓存,则会抛出一个异常 promise,利用异常做逻辑流控制是一种拥有较深的调用堆栈时的手段,它是在虚拟 DOM 渲染层做的暂停拦截,代码可在服务端复用。
import { fetchMovieDetails } from '../api'; import { createFetch } from '../future'; const movieDetailsFetch = createFetch(fetchMovieDetails); function MovieDetails(props) { const movie = movieDetailsFetch.read(props.id); return ( <div> <MoviePoster src={movie.poster} /> <MovieMetrics {...movie} /> </div> ); }
更多编程相关知识,请访问:编程课程!!
以上是react16有什麼新特性的詳細內容。更多資訊請關注PHP中文網其他相關文章!