react native一套代码能够开发出跨平台app, 减小了人力、节省了时间、避免了 iOS 与 Android 版本发布的时间差,开发新功能能够更迅速。等等css
缺点:内存、转化为原生的html
props state refs 方面回答react
主要考察flex布局绝对定位问题android
redux ==> action/reducer/store
mobx ==>数据双向绑定ios
当调用 setState 时,React会作的第一件事情是将传递给 setState 的对象合并到组件的当前状态。 这将启动一个称为和解(reconciliation
)的过程。 和解(reconciliation)的最终目标是以最有效的方式,根据这个新的状态来更新UI。 为此,React将构建一个新的 React 元素树(您能够将其视为 UI 的对象表示)。 一旦有了这个树,为了弄清 UI 如何响应新的状态而改变,React 会将这个新树与上一个元素树相比较( diff )。 经过这样作, React 将会知道发生的确切变化,而且经过了解发生什么变化,只需在绝对必要的状况下进行更新便可最小化 UI 的占用空间。算法
简单地说,一个 React element 描述了你想在屏幕上看到什么。 换个说法就是,一个 React element 是一些 UI 的对象表示。 一个 React Component 是一个函数或一个类, 它能够接受输入并返回一个 React element (一般是经过 JSX ,它被转化成一个 createElement 调用)。redux
其实这个问题也是跟reconciliation有关系。 “和解( reconciliation )的最终目标是以最有效的方式,根据新的状态更新用户界面”。 若是咱们知道咱们的用户界面(UI)的某一部分不会改变, 那么没有理由让 React 很麻烦地试图去弄清楚它是否应该渲染。 经过从 shouldComponentUpdate 返回 false, React 将假定当前组件及其全部子组件将保持与当前组件相同react-native
为了解决跨浏览器兼容性问题, 您的 React 中的事件处理程序将传递 SyntheticEvent
的实例, 它是 React 的浏览器本机事件的跨浏览器包装器。 这些 SyntheticEvent 与您习惯的原生事件具备相同的接口,除了它们在全部浏览器中都兼容。 有趣的是,React 实际上并无将事件附加到子节点自己。 React 将使用单个事件监听器监听顶层的全部事件。 这对于性能是有好处的,这也意味着在更新DOM时,React 不须要担忧跟踪事件监听器数组
this.props.children 的值有三种可能浏览器
系统提供React.Children.map()
方法安全的遍历子节点对象
XSS是一种跨站脚本攻击
,是属于代码注入的一种,攻击者经过将代码注入网页中,其余用户看到会受到影响(代码内容有请求外部服务器);
CSRF是一种跨站请求伪造
,冒充用户发起请求,完成一些违背用户请求的行为(删帖,改密码,发邮件,发帖等)
ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。 Symbol函数前不能使用new命令,不然会报错。这是由于生成的Symbol是一个原始类型的值,不是对象 Symbol函数能够接受一个字符串做为参数,表示对Symbol实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。
追查:这两个组件的实如今ReactBaseClasses.js中间,除掉注释后以下
function ReactComponent(props, context, updater) { this.props = props; this.context = context; this.refs = emptyObject; this.updater = updater || ReactNoopUpdateQueue; } ReactComponent.prototype.isReactComponent = {}; ReactComponent.prototype.setState = function (partialState, callback) { !(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'setState(...): takes an object of state variables to update or a function which returns an object of state variables.') : _prodInvariant('85') : void 0; this.updater.enqueueSetState(this, partialState, callback, 'setState'); }; ReactComponent.prototype.forceUpdate = function (callback) { this.updater.enqueueForceUpdate(this, callback, 'forceUpdate'); }; function ReactPureComponent(props, context, updater) { this.props = props; this.context = context; this.refs = emptyObject; this.updater = updater || ReactNoopUpdateQueue; } function ComponentDummy() {} ComponentDummy.prototype = ReactComponent.prototype; ReactPureComponent.prototype = new ComponentDummy(); ReactPureComponent.prototype.constructor = ReactPureComponent; _assign(ReactPureComponent.prototype, ReactComponent.prototype); ReactPureComponent.prototype.isPureReactComponent = true; module.exports = { Component: ReactComponent, PureComponent: ReactPureComponent };
发现Component就只实现了构造方法,定义了setState方法就完了。而ReactPureComponent更狠,就只是用js原型模拟继承的方法继承了Component,而后定义属性isPureReactComponent为true
。全局搜索isPureReactComponent属性,发如今ReactCompositeComponent.js中有使用,这个类就是管理组件的更新、加载等的。关键代码在updateComponent方法中,以下
var shouldUpdate = true; // 这个变量决定是否须要从新渲染组件 if (!this._pendingForceUpdate) { var prevState = inst.state; shouldUpdate = willReceive || nextState !== prevState; // inst表明组件实例,这个判断条件就是组件是否本身实现了shouldComponentUpdate方法 if (inst.shouldComponentUpdate) { if (__DEV__) { shouldUpdate = measureLifeCyclePerf( () => inst.shouldComponentUpdate(nextProps, nextState, nextContext), this._debugID, 'shouldComponentUpdate', ); } else { shouldUpdate = inst.shouldComponentUpdate( nextProps, nextState, nextContext, ); } } else {// 组件没有实现shouldComponentUpdate方法,且是PureComponent,采用shallowEqual浅比较 if (this._compositeType === ReactCompositeComponentTypes.PureClass) { shouldUpdate = !shallowEqual(prevProps, nextProps) || !shallowEqual(inst.state, nextState); } } }
shallowEqual的实如今shallowEqual.js中,大意就是做浅比较
,也就是对象数组等只比较对象所处的地址是否相等,而不比较具体的内容,由于深层次递归比较对象内容是否一致很耗费性能。
结论
PureComponent是Component的子类,当PureComponent手动实现了shouldComponentUpdate方法时两个组件没有区别,但若没有手动实现该方法,则PureComponent采用默认的shallowEqual比较对象是否相等性能更佳。由此可能引起的页面不刷新现象能够采用别的办法解决,如从新生成新的对象、采用immutable.js对象等
追查:缘由就是键盘是懒加载模式,初次出现时须要先初始化键盘视图耗费时间,要想缩减首次耗时间隔,能够事先就让键盘初始化完毕。js端没想到如何作,可是原生端能够在didFinishLaunchingWithOptions方法
中写:
UITextField *textField = [[UITextField alloc] init]; [self.window addSubview:textField]; [textField becomeFirstResponder]; [textField resignFirstResponder]; [textField removeFromSuperview];
TextInput聚焦时弹出了键盘,点击非TextInput空白处键盘是不会消失的,若想实现该功能只须要让TextInput嵌入在ScrollView中便可。
那么问题又来了,这样作以后,除了TextInput外屏幕上任意地方点击键盘都会先消失,致使例如页面上有个按钮A,点击A时会先退下键盘,再次点击才能触发A的事件,很扯淡。解决方法大致以下:
_addEvent = (event) => { this.events.push(event.nativeEvent.target); }; _onStartShouldSetResponder(event) { const target = event.nativeEvent.target; if (!this.events.includes(target)) { Keyboard.dismiss(); } return false; } render() { return ( <ScrollView keyboardShouldPersistTaps="always"> <View style={{ alignItems: 'center', flex: 1, height: SCREEN_HEIGHT }} onStartShouldSetResponder={(event) => this._onStartShouldSetResponder(event)} > <Button text="登录" onLayout={(event) => this._addEvent(event)} /> </View> </ScrollView> ); }
ScrollView的keyboardShouldPersistTaps属性设置为always,则键盘再也不拦截点击事件,点击空白处键盘不会自动消失。
onStartShouldSetResponderCapture是点击事件发生时调用,询问该视图是否要拦截事件,自定义处理,当点击屏幕除了指定位置外都退下键盘。指定位置A(好比登陆按钮)点击时,键盘不退下。
A的onLayout在视图布局完成回调,event.nativeEvent.target能惟一的标识该组件。
在redux-devtools中,咱们能够查看到redux下全部经过reducer更新state的记录
,每个记录都对应着内存中某一个具体的state,让用户能够追溯到每一次历史操做产生与执行时,当时的具体状态,这也是使用redux管理状态的重要优点之一.
缘由:有时候一个action改变数据后,咱们但愿拿到改变后的数据作另一个action,好比初始化action读取硬盘中的数据到内存,而后用该参数进行请求网络数据action。此时咱们能够在componentWillReceiveProps方法中拿到参数,若此时发出再发出action,则数据返回后改变reducer会再次进入componentWillReceiveProps方法,又继续发出action,陷入死循环。能够以下解决
componentWillReceiveProps(nextProp) { if(nextProp.app.user && nextProp.app.sessionId && !this.isFirstLoad){ this.props.action(nextProp.app); // action操做 this.isFirstLoad = true; } }
追查:发如今iphone7plus模拟器中黑线看不到,可是iphone6模拟器能看见。查看源代码,在navigation组件中的Header.js第300行找到了黑线样式定义,
let platformContainerStyles; if (Platform.OS === 'ios') { platformContainerStyles = { borderBottomWidth: StyleSheet.hairlineWidth, // hairlineWidth为当前分辨率下能显示的最小宽度,模拟器下可能看不见 borderBottomColor: 'rgba(0, 0, 0, .3)', }; } else { platformContainerStyles = { shadowColor: 'black', shadowOpacity: 0.1, shadowRadius: StyleSheet.hairlineWidth, shadowOffset: { height: StyleSheet.hairlineWidth, }, elevation: 4, }; }
可见在ios中下方黑线使用边框的形式实现,而安卓则是设置图层阴影。若想隐藏该线,ios中设置headerStyle的borderBottomWidth为0,安卓中设置elevation/shadowOpacity为0.
同上,可在TabBarBottom.js中180行找到tabbar上方那跟线的默认设置,更改则可在TabNavigator中的tabBarOptions的style中设置borderTopWidth和borderTopColor
追查:跟虚拟DOM和Diff算法有关。
一次DOM操做流程包括,拿到页面全部DOM节点,拿到css样式表,生成render树,布局计算节点位置,渲染等操做。 传统应用,一个操做若是须要改变10个DOM节点,则会相应的进行10次DOM操做,不少重复浪费性能。
虚拟DOM就是刚开始就将全部的DOM节点转换成js的相关代码保存到内存中,一个操做改变10次DOM节点所有在内存中完成,再将内存中的js转换为实际的DOM节点渲染,性能高。
虚拟DOM一个操做中10次改变DOM节点,每次只是改变了必要的那一个节点,不须要所有改变,为了减小时间复杂度,引入Diff算法,只比较节点改变了的那一点,进行增删改操做等。好比如今的render树是A、B、C节点,想再A节点后面插入D节点,若没有key,React没法区分各个节点,只能根据渲染树的排列依次卸载B、装载D、卸载C、装载B、装载C,效率低下。若是ABC节点都有key,则React就能根据key找出对应的节点,直接渲染A、D、B、C,效率高。
在任何一个单点时刻 render() 函数的做用是建立 React 元素树。在下一个 state 或props 更新时,render() 函数将会返回一个不一样的 React 元素树。 React 经过Diffing算法找出两颗元素树的差别,更新必须的部分,其假定规则是:
a、DOM 节点跨层级的移动操做特别少,能够忽略不计。
b、拥有相同类的两个组件将会生成类似的树形结构,拥有不一样类的两个组件将会生成不一样的树形结构。
c、对于同一层级的一组子节点,它们能够经过惟一 id 进行区分。
具体的比较以下:
一、tree diff
,DOM 节点跨层级的移动操做少到能够忽略不计,针对这一现象,React 经过 updateDepth 对 Virtual DOM 树进行层级控制,只会对同一个父节点下的全部子节点。当发现节点已经不存在,则该节点及其子节点会被彻底删除掉,不会用于进一步的比较。这样只须要对树进行一次遍历,便能完成整个 DOM 树的比较。如有节点跨层级的移动,性能会受到影响
二、component diff
,若是是同一类型的组件,按照原策略继续比较 virtual DOM tree。若是不是,则将该组件判断为 dirty component,从而替换整个组件下的全部子节点。对于同一类型的组件,有可能其 Virtual DOM 没有任何变化,若是可以确切的知道这点那能够节省大量的 diff 运算时间,所以 React 容许用户经过 shouldComponentUpdate() 来判断该组件是否须要进行 diff。
三、element diff
,当节点处于同一层级时,默认状况下,当递归一个 DOM 节点的子节点时,React 只需同时遍历全部的孩子节点并更改不一样点,如在列表组件追加几个item时,性能不错。可是当以下
<ul> <li>1</li> <li>2</li> </ul> <ul> <li>3</li> <li>1</li> <li>2</li> </ul>
React 将会改变每个子节点而没有意识到须要保留
<ul> <li key="2015">1</li> <li key="2016">2</li> </ul> <ul> <li key="2014">3</li> <li key="2015">1</li> <li key="2016">2</li> </ul>
如今 React 知道有'2014' key 的元素是新的, key为'2015' 和'2016'的两个元素仅仅只是被移动而已,效率变高不少。要注意key必须具有惟一性。若将数组中的索引做为 key ,若是存在从新排序时,性能将会不好,应该避免这种状况。
高阶组件是重用组件逻辑的一项高级技术。高阶组件并非React API的一部分。高阶组件源自于React生态。具体来讲,高阶组件是一个函数,可以接受一个组件并返回一个新的组件,例如Redux的connect函数。
HOC存在的问题:
一、组件的静态方法不会被传递,须要自行传递处理
二、refs不会被传递,应该避免此,或者用自定义属性传递
()
三、react-native-fetch-blob的POST请求不成功。
四、js传到原生端的函数(ios中叫block)只能执行一次,不然崩溃。
转: