摘要:事件系統(tǒng)合成事件的綁定方式合成事件的實(shí)現(xiàn)機(jī)制事件委派和自動(dòng)綁定。高階組件如果已經(jīng)理解高階函數(shù),那么理解高階組件也很容易的。例如我們常見(jiàn)的方法等都是高階函數(shù)。對(duì)測(cè)試群眾來(lái)說(shuō),從質(zhì)量保證的角度出發(fā),單元測(cè)試覆蓋率是
事件系統(tǒng)
合成事件的綁定方式
``
合成事件的實(shí)現(xiàn)機(jī)制:事件委派和自動(dòng)綁定。
React合成事件系統(tǒng)的委托機(jī)制,在合成事件內(nèi)部?jī)H僅是對(duì)最外層的容器進(jìn)行了綁定,并且依賴事件的冒泡機(jī)制完成了委派。
表單
React受控組件更新state的流程:
可以通過(guò)在初始state中設(shè)置表單的默認(rèn)值。
每當(dāng)表單的值發(fā)生變化時(shí),調(diào)用onChange事件處理器。
事件處理器通過(guò)合成事件對(duì)象e拿到改變后的狀態(tài),并更新應(yīng)用的state。
setState觸發(fā)視圖的重新渲染,完成表單組件值的更新。
受控組件和非受控組件的最大區(qū)別是:非受控組件的狀態(tài)并不會(huì)受應(yīng)用狀態(tài)的控制,應(yīng)用中也多了局部組件狀態(tài),而受控組件的值來(lái)自于組件的state。
樣式處理
CSS模塊化遇到了哪些問(wèn)題?全局污染,命名混亂,依賴管理不徹底,無(wú)法共享變量,代碼壓縮不徹底。
CSS Modules模塊化方案:啟用CSS Modules,樣式默認(rèn)局部,使用composes來(lái)組合樣式。
組件間通信
子組件向父組件通信
利用回調(diào)函數(shù)
利用自定義事件機(jī)制
當(dāng)需要讓子組件跨級(jí)訪問(wèn)信息時(shí),我們還可以使用context來(lái)實(shí)現(xiàn)跨級(jí)父子組件間的通信。
沒(méi)有嵌套關(guān)系的組件通信:我們?cè)谔幚硎录倪^(guò)程中需要注意,在componentDidMount事件中,如果組件掛載完成,再訂閱事件;當(dāng)組件卸載的時(shí)候,在componentWillUnmount事件中取消事件的訂閱。
組件間抽象
mixin 的目的,就是為了創(chuàng)造一種類似多重繼承的效果,或者說(shuō),組合。實(shí)際上,包括C++等一些年齡較大的OOP語(yǔ)言,都有一個(gè)強(qiáng)大但是危險(xiǎn)的多重繼承特性?,F(xiàn)代語(yǔ)言權(quán)衡利弊,大都舍棄了它,只采用單繼承。但是單繼承在實(shí)現(xiàn)抽象的時(shí)候有很多不便,為了彌補(bǔ)缺失,Java引入接口(interface),其他一些語(yǔ)言則引入了mixin的技巧。
封裝mixin方法
方法:const mixin = function(obj, mixins) { const newObj = obj; newObj.prototype = Object.create(obj.prototype); for (let prop in mixins) { if (mixins.hasOwnProperty(prop)) { newObj.prototype[prop] = mixins[prop]; } } return newObj; }應(yīng)用:
const BigMixin = { fly: () => { console.log("I can fly"); } }; const Big = function() { console.log("new big"); }; const FlyBig = mixin(Big, BigMixin); const flyBig = new FlyBig(); // => "new big" flyBig.fly(); // => "I can fly"上面這段代碼實(shí)現(xiàn)對(duì)象混入的方法是:用賦值的方式將mixin對(duì)象里的方法都掛載到原對(duì)象上。
在React中使用mixin
React在使用createClass構(gòu)建組件時(shí)提供了mixin屬性,比如官方封裝的:PureRenderMixin。
import React from "react"; import PureRenderMixin from "react-addons-pure-render-mixin"; React.createClass({ mixins: [PureRenderMixin], render() { returnfoo; } });在createClass對(duì)象參數(shù)中傳入數(shù)組mixins,里面封裝了我們需要的模塊。mixins數(shù)組也可以添加多個(gè)mixin。同時(shí),在React中不允許出現(xiàn)重名普通方法的mixin。而如果是生命周期方法,則React將會(huì)將各個(gè)模塊的生命周期方法疊加在一起然后順序執(zhí)行。
使用createClass實(shí)現(xiàn)的mixin為組件做了兩件事:工具方法:這是mixin的基本功能,如果希望共享一些工具類的方法,就可以直接定義它們?nèi)缓笤诮M件中使用。
生命周期繼承,props和state合并。mixin能夠合并生命周期方法。如果有很多mixin來(lái)定義componentDidMount這個(gè)周期,那么React會(huì)很機(jī)智的將它們都合并起來(lái)執(zhí)行。同樣,mixin也可以作state和props的合并。
ES6 Classes和decorator
然而,當(dāng)我們使用ES6 classes的形式構(gòu)建組件的時(shí)候,卻并不支持mixin。為了使用這個(gè)強(qiáng)大的功能,我們還需要采取其他方法,來(lái)達(dá)到模塊重用的目的??梢允褂肊S7的語(yǔ)法糖decorator來(lái)實(shí)現(xiàn)class上的mixin。core-decorators庫(kù)為開(kāi)發(fā)者提供了一些實(shí)用的decorator, 其中也正好實(shí)現(xiàn)了我們想要的@mixin。
import React, { Component } from "React"; import { mixin } from "core-decorators"; const PureRender = { shouldComponentUpdate() {} }; const Theme = { setTheme() {} }; @mixin(PureRender, Theme) class MyComponent extends Component { render() {} }mixin的問(wèn)題
破壞了原有組件的封裝:mixin會(huì)混入方法,給原有的組件帶來(lái)新特性。但同時(shí)它也可能帶來(lái)新的state和props,這意味著組件有一些“不可見(jiàn)”的狀態(tài)需要我們?nèi)ゾS護(hù)。另外,mixin也有可能去依賴其他的mixin,這樣會(huì)建立一個(gè)mixin的依賴鏈,當(dāng)我們改動(dòng)一個(gè)mixin的狀態(tài),很有可能也會(huì)影響其他的mixin。
命名沖突
增加復(fù)雜性
針對(duì)這些困擾,React提出的新的方式來(lái)取代mixin,那就是高階組件。
高階組件
如果已經(jīng)理解高階函數(shù),那么理解高階組件也很容易的。高階函數(shù):就是一種這樣的函數(shù),它接受函數(shù)作為參數(shù)輸入,或者將一個(gè)函數(shù)作為返回值。例如我們常見(jiàn)的方法map, reduce, sort等都是高階函數(shù)。高階組件和和高階函數(shù)很類似,高階組件就是接受一個(gè)React組件作為參數(shù)輸入,輸出一個(gè)新的React組件。高階組件讓我們的代碼更具有復(fù)用性、邏輯性與抽象性,它可以對(duì)render方法作劫持,也可以控制props和state。
實(shí)現(xiàn)高階組件的方法有如下兩種:屬性代理:高階組件通過(guò)被包裹的React組件來(lái)操作props。
反向繼承:高階組件繼承于被包裹的React組件。
屬性代理
示例代碼:import React, { Component } from "React"; const MyContainer = (WrappedComponent) => class extends Component { render() { return; } } 在代碼中我們可以看到,render方法返回了傳入的WrappedComponent組件。這樣,我們就可以通過(guò)高階組件來(lái)傳遞props。這種方式就是屬性代理。
如何使用上面這個(gè)高階組件:import React, { Component } from "React"; class MyComponent extends Component { // ... } export default MyContainer(MyComponent);這樣組件就可以一層層的作為參數(shù)被調(diào)用,原始組件久具備了高階組件對(duì)它的修飾。這樣,保持單個(gè)組件封裝的同時(shí)也保留了易用行。
從功能上, 高階組件一樣可以做到像mixin對(duì)組件的控制:控制props
我們可以讀取、增加、編輯或是移除從WrappedComponent傳進(jìn)來(lái)的props。
例如:新增propsimport React, { Component } from "React"; const MyContainer = (WrappedComponent) => class extends Component { render() { const newProps = { text: newText, }; return; } } 注意:
// is equivalent to React.createElement(WrappedComponent, this.props, null) 這樣,當(dāng)調(diào)用高階組件的時(shí)候,就可以使用text這個(gè)新的props了。
通過(guò)refs使用引用
抽象state
高階組件可以講原組件抽象為展示型組件,分離內(nèi)部狀態(tài)。
const MyContainer = (WrappedComponent) => class extends Component { constructor(props) { super(props); this.state = { name: "", 4 }; this.onNameChange = this.onNameChange.bind(this); } onNameChange(event) { this.setState({ name: event.target.value, }) } render() { const newProps = { name: { value: this.state.name, onChange: this.onNameChange, }, } return; } } 在這個(gè)例子中,我們把組件中對(duì)name prop 的onChange方法提取到高階組件中,這樣就有效的抽象了同樣的state操作。
使用方式@MyContainer class MyComponent extends Component { render() { return ; } }反向繼承
const MyContainer = (WrappedComponent) => class extends WrappedComponent { render() { return super.render(); } }
組件性能優(yōu)化
性能優(yōu)化的思路影響網(wǎng)頁(yè)性能最大的因素是瀏覽器的重排(repaint)和重繪(reflow)。React的Virtual DOM就是盡可能地減少瀏覽器的重排和重繪。從React渲染過(guò)程來(lái)看,如何防止不必要的渲染是解決問(wèn)題的關(guān)鍵。
性能優(yōu)化的具體辦法盡量多使用無(wú)狀態(tài)函數(shù)構(gòu)建組件
無(wú)狀態(tài)組件只有props和context兩個(gè)參數(shù)。它不存在state,沒(méi)有生命周期方法,組件本身即有狀態(tài)組件構(gòu)建方法中的render方法。在合適的情況下,都應(yīng)該必須使用無(wú)狀態(tài)組件。無(wú)狀態(tài)組件不會(huì)像React.createClass和ES6 class會(huì)在調(diào)用時(shí)創(chuàng)建新實(shí)例,它創(chuàng)建時(shí)始終保持了一個(gè)實(shí)例,避免了不必要的檢查和內(nèi)存分配,做到了內(nèi)部?jī)?yōu)化。
拆分組件為子組件,對(duì)組件做更細(xì)粒度的控制
相關(guān)重要概念:純函數(shù)
純函數(shù)的三大構(gòu)成原則:
給定相同的輸入,它總是返回相同的輸出: 比如反例有 Math.random(), New Date();
過(guò)程沒(méi)有副作用:即不能改變外部狀態(tài);
沒(méi)有額外的狀態(tài)依賴:即方法內(nèi)部的狀態(tài)都只能在方法的生命周期內(nèi)存活,這意味著不能在方法內(nèi)使用共享的變量。
純函數(shù)非常方便進(jìn)行方法級(jí)別的測(cè)試及重構(gòu),它可以讓程序具有良好的擴(kuò)展性及適應(yīng)性。純函數(shù)是函數(shù)式變成的基礎(chǔ)。React組件本身就是純函數(shù),即傳入指定props得到一定的Virtual DOM,整個(gè)過(guò)程都是可預(yù)測(cè)的。
具體辦法
拆分組件為子組件,對(duì)組件做更細(xì)粒度的控制。保持純凈狀態(tài),可以讓方法或組件更加專注(focus),體積更小(small),更獨(dú)立(independent),更具有復(fù)用性(reusability)和可測(cè)試性(testability)。
運(yùn)用PureRender,對(duì)變更做出最少的渲染
相關(guān)重要概念: PureRender
PureRender的Pure即是指滿足純函數(shù)的條件,即組件被相同的props和state渲染會(huì)得到相同的結(jié)果。在React中實(shí)現(xiàn)PureRender需要重新實(shí)現(xiàn)shouldComponentUpdate生命周期方法。shouldComponentUpdate是一個(gè)特別的方法,它接收需要更新的props和state,其本質(zhì)是用來(lái)進(jìn)行正確的組件渲染。當(dāng)其返回false的時(shí)候,不再向下執(zhí)行生命周期方法;當(dāng)其返回true時(shí),繼續(xù)向下執(zhí)行。組件在初始化過(guò)程中會(huì)渲染一個(gè)樹(shù)狀結(jié)構(gòu),當(dāng)父節(jié)點(diǎn)props改變的時(shí)候,在理想情況下只需渲染一條鏈路上有關(guān)props改變的節(jié)點(diǎn)即可;但是,在默認(rèn)情況下shouldComponentUpdate方法返回true,React會(huì)重新渲染所有的節(jié)點(diǎn)。
有一些官方插件實(shí)現(xiàn)了對(duì)shouldComponentUpdate的重寫,然后自己也可以做一些代碼的優(yōu)化來(lái)運(yùn)用PureRender。具體辦法
運(yùn)用PureRender
使用官方插件react-addons-pure-render-mixin實(shí)現(xiàn)對(duì)shouldComponentUpdate的重寫
import React from "react"; import PureRenderMixin from "react-addons-pure-render-mixin"; class App extends React.Component { constructor(props) { super(props); this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this); } render() { returnfoo} }它的原理是對(duì)object(包括props和state)做淺比較,即引用比較,非值比較。比如只用關(guān)注props中每一個(gè)是否全等(如果是prop是一個(gè)對(duì)象那就是只比較了地址,地址一樣就算是一樣了),而不用深入比較。
優(yōu)化PureRender
避免無(wú)論如何都會(huì)觸發(fā)shouldComponentUpdate返回true的代碼寫法。避免直接為prop設(shè)置字面量的數(shù)組和對(duì)象,就算每次傳入的數(shù)組或?qū)ο蟮闹禌](méi)有變,但它們的地址也發(fā)生了變化。
如以下寫法每次渲染時(shí)style都是新對(duì)象都會(huì)觸發(fā)shouldComponentUpdate為true:改進(jìn)辦法:將字面量設(shè)置為一個(gè)引用:
const defaultStyle = {};避免每次都綁定事件,如果這樣綁定事件的話每次都要生成一個(gè)新的onChange屬性的值:
render() { return }該盡量在構(gòu)造函數(shù)內(nèi)進(jìn)行綁定,如果綁定需要傳參那么應(yīng)該考慮抽象子組件或改變現(xiàn)有數(shù)據(jù)結(jié)構(gòu):
constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); } handleChange() { ... } render() { return }在設(shè)置子組件的時(shí)候要在父組件級(jí)別重寫shouldComponentUpdate。
運(yùn)用immutable
JavaScript中對(duì)象一般是可變的,因?yàn)槭褂靡觅x值,新的對(duì)象的改變將影響原始對(duì)象。為了解決這個(gè)問(wèn)題是使用深拷貝或者淺拷貝,但這樣做又造成了CPU和內(nèi)存的浪費(fèi)。Immutable data很好地解決了這個(gè)問(wèn)題。Immutable data就是一旦創(chuàng)建,就不能再更改的數(shù)據(jù)。對(duì)Immutable對(duì)象進(jìn)行修改、添加或刪除操作,都會(huì)返回一個(gè)新的Immutable對(duì)象。Immutable實(shí)現(xiàn)的原理是持久化的數(shù)據(jù)結(jié)構(gòu)。即使用舊數(shù)據(jù)創(chuàng)建新數(shù)據(jù)時(shí),保證新舊數(shù)據(jù)同時(shí)可用且不變。同時(shí)為了避免深拷貝帶來(lái)的性能損耗,Immutable使用了結(jié)構(gòu)共享(structural sharing),即如果對(duì)象樹(shù)中一個(gè)節(jié)點(diǎn)發(fā)生變化,只修改這個(gè)節(jié)點(diǎn)和受它影響的父節(jié)點(diǎn),其他節(jié)點(diǎn)則進(jìn)行共享。
自動(dòng)化測(cè)試
jest 是 facebook 開(kāi)源的,用來(lái)進(jìn)行單元測(cè)試的框架,功能比較全面,測(cè)試、斷言、覆蓋率它都可以,另外還提供了快照功能。
對(duì)測(cè)試群眾來(lái)說(shuō),從質(zhì)量保證的角度出發(fā),單元測(cè)試覆蓋率100%是否就足夠了呢?肯定不夠啊!
結(jié)合實(shí)際的項(xiàng)目經(jīng)驗(yàn)來(lái)看,jest的測(cè)試還可以根據(jù)產(chǎn)品的實(shí)際需求,做一些諸如:
點(diǎn)擊某個(gè)頁(yè)面元素后,需要在頁(yè)面上顯示新的區(qū)塊,并且要加載指定的的css的測(cè)試;
點(diǎn)擊某個(gè)link,需要跳轉(zhuǎn)到指定的網(wǎng)站的測(cè)試;
等等
這些測(cè)試原本在UI自動(dòng)化功能測(cè)試中也比較常見(jiàn),這里我們都可以把它們挪到低層中去。所以具體的測(cè)試用例,在單元測(cè)試覆蓋率超級(jí)高的前提下,我們測(cè)試的群眾還可以跟研發(fā)結(jié)對(duì)完成。或者指導(dǎo)研發(fā)完成,要不干脆自己加上去算了。另外,產(chǎn)品的功能性測(cè)試完成的情況下,我們還需要考慮下非功能性的問(wèn)題,例如兼容性、性能、安全性等。再加上測(cè)試金字塔的頂端之上,其實(shí)還有探索性測(cè)試的位置。產(chǎn)品的基本功能由單元測(cè)試保障了,剩下的時(shí)間,我們可以做更多的探索性測(cè)試了不是嗎?總之,干掉UI自動(dòng)化功能測(cè)試只是一個(gè)加速測(cè)試反饋周期、減少投入成本的嘗試。軟件的質(zhì)量不僅僅是測(cè)試攻城獅的事情,而是整個(gè)團(tuán)隊(duì)的責(zé)任。堅(jiān)持一些重要的編碼實(shí)踐,比如state less的組件、build security in等,也是提高質(zhì)量的重要手段。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/105571.html
摘要:學(xué)習(xí)之道簡(jiǎn)體中文版通往實(shí)戰(zhàn)大師之旅掌握最簡(jiǎn)單,且最實(shí)用的教程。前言學(xué)習(xí)之道這本書使用路線圖中的精華部分用于傳授,并將其融入一個(gè)獨(dú)具吸引力的真實(shí)世界的具體代碼實(shí)現(xiàn)。完美展現(xiàn)了的優(yōu)雅。膜拜的學(xué)習(xí)之道是必讀的一本書。 《React 學(xué)習(xí)之道》The Road to learn React (簡(jiǎn)體中文版) 通往 React 實(shí)戰(zhàn)大師之旅:掌握 React 最簡(jiǎn)單,且最實(shí)用的教程。 showIm...
摘要:語(yǔ)法將語(yǔ)法直接加入到代碼中,再通過(guò)翻譯器裝換到純后由瀏覽器執(zhí)行。事實(shí)上,并不需要花精力學(xué)習(xí)??梢哉f(shuō),基本語(yǔ)法基本被囊括了,但也有少許不同。明確的數(shù)據(jù)流動(dòng)。這條原則讓組件之間的關(guān)系變得簡(jiǎn)單且可預(yù)測(cè)。使用獲取和顯示回調(diào)。 JSX語(yǔ)法 JSX將HTML語(yǔ)法直接加入到JavaScript代碼中,再通過(guò)翻譯器裝換到純JavaScript后由瀏覽器執(zhí)行。在實(shí)際開(kāi)發(fā)中,JSX在產(chǎn)品打包階段都已經(jīng)編...
摘要:是用戶建立客戶端應(yīng)用的前端架構(gòu),它通過(guò)利用一個(gè)單向的數(shù)據(jù)流補(bǔ)充了的組合視圖組件,這更是一種模式而非正式框架,你能夠無(wú)需許多新代碼情況下立即開(kāi)始使用。結(jié)構(gòu)和數(shù)據(jù)流一個(gè)單向數(shù)據(jù)流是模式的核心。 Flux是Facebook用戶建立客戶端Web應(yīng)用的前端架構(gòu),它通過(guò)利用一個(gè)單向的數(shù)據(jù)流補(bǔ)充了React的組合視圖組件,這更是一種模式而非正式框架,你能夠無(wú)需許多新代碼情況下立即開(kāi)始使用Flux。 ...
摘要:根據(jù)的類型不同,分別實(shí)例化類。并且處理特殊屬性,比如事件綁定。之后根據(jù)差異對(duì)象操作元素位置變動(dòng),刪除,添加等。各個(gè)組件獨(dú)立管理層層嵌套,互不影響,內(nèi)部實(shí)現(xiàn)的渲染功能。根據(jù)基本元素的值,判斷是否遞歸更新子節(jié)點(diǎn),還是刪除舊節(jié)點(diǎn),添加新節(jié)點(diǎn)。 首先理解ReactElement和ReactClass的概念。想要更好的利用react的虛擬DOM,diff算法的優(yōu)勢(shì),我們需要正確的優(yōu)化、組織rea...
摘要:目前,有三個(gè)明確的框架可供選擇。和在眾多開(kāi)源框架中贏得了開(kāi)發(fā)人員和公司的信任。雖然這三個(gè)框架有許多共同之處,但它們的受歡迎程度因行業(yè)而異。使用,這有助于在編碼時(shí)發(fā)現(xiàn)并糾正常見(jiàn)錯(cuò)誤。 人們首先注意到的是你的應(yīng)用程序的視覺(jué)吸引力。大多數(shù)用戶傾向于將界面設(shè)計(jì)與公司的信譽(yù)和專業(yè)能力聯(lián)系起來(lái)。這就是為什么選擇正確的前端技術(shù)對(duì)你的業(yè)務(wù)...
閱讀 2444·2021-11-25 09:43
閱讀 1228·2021-09-07 10:16
閱讀 2651·2021-08-20 09:38
閱讀 2965·2019-08-30 15:55
閱讀 1510·2019-08-30 13:21
閱讀 914·2019-08-29 15:37
閱讀 1470·2019-08-27 10:56
閱讀 2112·2019-08-26 13:45