怎么用 React.createElement 重写上面的代码
Question:
const element = ( <h1 className="greeting"> Hello, rdhub.cn! </h1>);
Answer:
const element = React.createElement( 'h1', {className: 'greeting'}, 'Hello, rdhub.cn!');
组件通信的形式有哪些
- ⽗组件向⼦组件通信: ⽗组件能够向⼦组件通过传 props 的⽅式,向⼦组件进⾏通信
- ⼦组件向⽗组件通信: props+回调的⽅式,⽗组件向⼦组件传递props进⾏通信,此props为作⽤域为⽗组件⾃身的函 数,⼦组件调⽤该函数,将⼦组件想要传递的信息,作为参数,传递到⽗组件的作⽤域中
- 兄弟组件通信: 找到这两个兄弟节点独特的⽗节点,联合上⾯两种⽅式由⽗节点转发信息进⾏通信
- 跨层级通信: Context 设计⽬的是为了共享那些对于⼀个组件树⽽⾔是“全局”的数据,例如以后认证的⽤户、主题或⾸选语⾔,对于逾越多层的全局数据通过 Context 通信再适宜不过
- 公布订阅模式: 发布者公布事件,订阅者监听事件并做出反馈,咱们能够通过引⼊event模块进⾏通信
- 全局状态治理⼯具: 借助Redux或者Mobx等全局状态治理⼯具进⾏通信,这种⼯具会保护⼀个全局状态中⼼Store,并依据不同的事件产⽣新的状态
对于store的了解
Store 就是把它们分割到一起的对象。Store 有以下职责:
- 维持利用的 state;
- 提供 getState() 办法获取 state;
- 提供 dispatch(action) 办法更新 state;
- 通过 subscribe(listener)注册监听器;
- 通过 subscribe(listener)返回的函数登记监听器
React中的props为什么是只读的?
this.props
是组件之间沟通的一个接口,原则上来讲,它只能从父组件流向子组件。React具备浓厚的函数式编程的思维。
提到函数式编程就要提一个概念:纯函数。它有几个特点:
- 给定雷同的输出,总是返回雷同的输入。
- 过程没有副作用。
- 不依赖内部状态。
this.props
就是吸取了纯函数的思维。props的不能够变性就保障的雷同的输出,页面显示的内容是一样的,并且不会产生副作用
为什么应用jsx的组件中没有看到应用react却须要引入react?
实质上来说JSX是React.createElement(component, props, ...children)
办法的语法糖。在React 17之前,如果应用了JSX,其实就是在应用React, babel
会把组件转换为 CreateElement
模式。在React 17之后,就不再须要引入,因为 babel
曾经能够帮咱们主动引入react。
为什么 React 要用 JSX?
JSX 是一个 JavaScript 的语法扩大,或者说是一个相似于 XML 的 ECMAScript 语法扩大。它自身没有太多的语法定义,也不冀望引入更多的规范。
其实 React 自身并不强制应用 JSX。在没有 JSX 的时候,React 实现一个组件依赖于应用 React.createElement 函数。代码如下:
class Hello extends React.Component { render() { return React.createElement( 'div', null, `Hello ${this.props.toWhat}` ); }}ReactDOM.render( React.createElement(Hello, {toWhat: 'World'}, null), document.getElementById('root'));
而 JSX 更像是一种语法糖,通过相似 XML 的形容形式,刻画函数对象。在采纳 JSX 之后,这段代码会这样写:
class Hello extends React.Component { render() { return <div>Hello {this.props.toWhat}</div>; }}ReactDOM.render( <Hello toWhat="World" />, document.getElementById('root'));
通过比照,能够清晰地发现,代码变得更为简洁,而且代码构造档次更为清晰。
因为 React 须要将组件转化为虚构 DOM 树,所以在编写代码时,实际上是在手写一棵构造树。而XML 在树结构的形容上天生具备可读性强的劣势。
但这样可读性强的代码仅仅是给写程序的同学看的,实际上在运行的时候,会应用 Babel 插件将 JSX 语法的代码还原为 React.createElement 的代码。
总结: JSX 是一个 JavaScript 的语法扩大,构造相似 XML。JSX 次要用于申明 React 元素,但 React 中并不强制应用 JSX。即便应用了 JSX,也会在构建过程中,通过 Babel 插件编译为 React.createElement。所以 JSX 更像是 React.createElement 的一种语法糖。
React 团队并不想引入 JavaScript 自身以外的开发体系。而是心愿通过正当的关注点拆散放弃组件开发的纯正性。
参考 前端进阶面试题具体解答
react-router4的外围
- 路由变成了组件
- 扩散到各个页面,不须要配置 比方
<link> <route></route>
React 16中新生命周期有哪些
对于 React16 开始利用的新生命周期: 能够看出,React16 自上而下地对生命周期做了另一种维度的解读:
- Render 阶段:用于计算一些必要的状态信息。这个阶段可能会被 React 暂停,这一点和 React16 引入的 Fiber 架构(咱们前面会重点解说)是无关的;
- Pre-commit阶段:所谓“commit”,这里指的是“更新真正的 DOM 节点”这个动作。所谓 Pre-commit,就是说我在这个阶段其实还并没有去更新实在的 DOM,不过 DOM 信息曾经是能够读取的了;
- Commit 阶段:在这一步,React 会实现实在 DOM 的更新工作。Commit 阶段,咱们能够拿到实在 DOM(包含 refs)。
与此同时,新的生命周期在流程方面,依然遵循“挂载”、“更新”、“卸载”这三个狭义的划分形式。它们别离对应到:
挂载过程:
- constructor
- getDerivedStateFromProps
- render
- componentDidMount
更新过程:
- getDerivedStateFromProps
- shouldComponentUpdate
- render
- getSnapshotBeforeUpdate
- componentDidUpdate
卸载过程:
- componentWillUnmount
redux的三大准则
繁多数据源
整个利用的state被存储在一个object tree中,并且这个object tree 之存在惟一一个store中
state是只读的
惟一扭转state的形式是触发action,action是一个用于形容曾经产生工夫的对象,这个保障了视图和网络申请都不能间接批改state,相同他们只能表白想要批改的用意
- 应用纯函数来执行批改state
为了形容action如何扭转state tree 须要编写reduce
react-router 里的 Link 标签和 a 标签的区别
从最终渲染的 DOM 来看,这两者都是链接,都是 标签,区别是∶ <Link>
是react-router 里实现路由跳转的链接,个别配合<Route>
应用,react-router接管了其默认的链接跳转行为,区别于传统的页面跳转,<Link>
的“跳转”行为只会触发相匹配的<Route>
对应的页面内容更新,而不会刷新整个页面。
<Link>
做了3件事件:
- 有onclick那就执行onclick
- click的时候阻止a标签默认事件
- 依据跳转href(即是to),用history (web前端路由两种形式之一,history & hash)跳转,此时只是链接变了,并没有刷新页面而
<a>
标签就是一般的超链接了,用于从以后页面跳转到href指向的另一 个页面(非锚点状况)。
a标签默认事件禁掉之后做了什么才实现了跳转?
let domArr = document.getElementsByTagName('a')[...domArr].forEach(item=>{ item.addEventListener('click',function () { location.href = this.href })})
哪些办法会触发 React 从新渲染?从新渲染 render 会做些什么?
(1)哪些办法会触发 react 从新渲染?
- setState()办法被调用
setState 是 React 中最罕用的命令,通常状况下,执行 setState 会触发 render。然而这里有个点值得关注,执行 setState 的时候不肯定会从新渲染。当 setState 传入 null 时,并不会触发 render。
class App extends React.Component { state = { a: 1 }; render() { console.log("render"); return ( <React.Fragement> <p>{this.state.a}</p> <button onClick={() => { this.setState({ a: 1 }); // 这里并没有扭转 a 的值 }} > Click me </button> <button onClick={() => this.setState(null)}>setState null</button> <Child /> </React.Fragement> ); }}
- 父组件从新渲染
只有父组件从新渲染了,即便传入子组件的 props 未发生变化,那么子组件也会从新渲染,进而触发 render
(2)从新渲染 render 会做些什么?
- 会对新旧 VNode 进行比照,也就是咱们所说的Diff算法。
- 对新旧两棵树进行一个深度优先遍历,这样每一个节点都会一个标记,在到深度遍历的时候,每遍历到一和个节点,就把该节点和新的节点树进行比照,如果有差别就放到一个对象外面
- 遍历差别对象,依据差别的类型,依据对应对规定更新VNode
React 的解决 render 的根本思维模式是每次一有变动就会去从新渲染整个利用。在 Virtual DOM 没有呈现之前,最简略的办法就是间接调用 innerHTML。Virtual DOM厉害的中央并不是说它比间接操作 DOM 快,而是说不论数据怎么变,都会尽量以最小的代价去更新 DOM。React 将 render 函数返回的虚构 DOM 树与老的进行比拟,从而确定 DOM 要不要更新、怎么更新。当 DOM 树很大时,遍历两棵树进行各种比对还是相当耗性能的,特地是在顶层 setState 一个渺小的批改,默认会去遍历整棵树。只管 React 应用高度优化的 Diff 算法,然而这个过程依然会损耗性能.
为什么React并不举荐优先思考应用Context?
- Context目前还处于试验阶段,可能会在前面的发行版本中有很大的变动,事实上这种状况曾经产生了,所以为了防止给今后降级带来大的影响和麻烦,不倡议在app中应用context。
- 只管不倡议在app中应用context,然而独有组件而言,因为影响范畴小于app,如果能够做到高内聚,不毁坏组件树之间的依赖关系,能够思考应用context
- 对于组件之间的数据通信或者状态治理,无效应用props或者state解决,而后再思考应用第三方的成熟库进行解决,以上的办法都不是最佳的计划的时候,在思考context。
- context的更新须要通过setState()触发,然而这并不是很牢靠的,Context反对跨组件的拜访,然而如果两头的子组件通过一些办法不影响更新,比方 shouldComponentUpdate() 返回false 那么不能保障Context的更新肯定能够应用Context的子组件,因而,Context的可靠性须要关注
和谐阶段 setState外部干了什么
- 当调用 setState 时,React会做的第一件事件是将传递给 setState 的对象合并到组件的以后状态
- 这将启动一个称为和解(
reconciliation
)的过程。和解(reconciliation
)的最终目标是以最无效的形式,依据这个新的状态来更新UI
。 为此,React
将构建一个新的React
元素树(您能够将其视为UI
的对象示意) - 一旦有了这个树,为了弄清 UI 如何响应新的状态而扭转,React 会将这个新树与上一个元素树相比拟( diff )
通过这样做, React 将会晓得产生的确切变动,并且通过理解产生什么变动,只需在相对必要的状况下进行更新即可最小化 UI 的占用空间
hooks 罕用的
useEffct应用:如果不传参数:相当于render之后就会执行传参数为空数组:相当于componentDidMount如果传数组:相当于componentDidUpdate如果外面返回:相当于componentWillUnmount会在组件卸载的时候执行革除操作。effect 在每次渲染的时候都会执行。React 会在执行以后 effect 之前对上一个 effect 进行革除。useLayoutEffect:useLayoutEffect在浏览器渲染前执行useEffect在浏览器渲染之后执行当父组件引入子组件以及在更新某一个值的状态的时候,往往会造成一些不必要的节约,而useMemo和useCallback的呈现就是为了缩小这种节约,进步组件的性能,不同点是:useMemo返回的是一个缓存的值,即memoized 值,而useCallback返回的是一个memoized 回调函数。useCallback父组件更新子组件会渲染,针对办法不反复执行,包装函数返回函数;useMemo:const memoizedValue =useMemo(callback,array)callback是一个函数用于解决逻辑array 管制useMemo从新执⾏行的数组,array扭转时才会 从新执行useMemo不传数组,每次更新都会从新计算空数组,只会计算一次依赖对应的值,当对应的值发生变化时,才会从新计算(能够依赖另外一个 useMemo 返回的值)不能在useMemo⾥面写副作⽤逻辑解决,副作用的逻辑解决放在 useEffect内进行解决自定义hook自定义 Hook 是一个函数,其名称以 “use” 结尾,函数外部能够调用其余的 Hook,自定义 Hook 是一种天然遵循 Hook 设计的约定,而并不是 React 的个性在我看来,自定义hook就是把一块业务逻辑独自拿出去写。 const [counter, setCounter] = useState(0); const counterRef = useRef(counter); // 能够保留上一次的变量useRef 获取节点function App() { const inputRef = useRef(null); return <div> <input type="text" ref={inputRef}/> <button onClick={() => inputRef.current.focus()}>focus</button> </div>}
能够应用TypeScript写React利用吗?怎么操作?
(1)如果还未创立 Create React App 我的项目
- 间接创立一个具备 typescript 的 Create React App 我的项目:
npx create-react-app demo --typescript
(2)如果曾经创立了 Create React App 我的项目,须要将 typescript 引入到已有我的项目中
- 通过命令将 typescript 引入我的项目:
npm install --save typescript @types/node @types/react @types/react-dom @types/jest
- 将我的项目中任何 后缀名为 ‘.js’ 的 JavaScript 文件重命名为 TypeScript 文件即后缀名为 ‘.tsx’(例如 src/index.js 重命名为 src/index.tsx )
Redux 中异步的申请怎么解决
能够在 componentDidmount 中间接进⾏申请⽆须借助redux。然而在⼀定规模的项⽬中,上述⽅法很难进⾏异步流的治理,通常状况下咱们会借助redux的异步中间件进⾏异步解决。redux异步流中间件其实有很多,当下支流的异步中间件有两种redux-thunk、redux-saga。
(1)应用react-thunk中间件
redux-thunk长处:
- 体积⼩: redux-thunk的实现⽅式很简略,只有不到20⾏代码
- 使⽤简略: redux-thunk没有引⼊像redux-saga或者redux-observable额定的范式,上⼿简略
redux-thunk缺点:
- 样板代码过多: 与redux自身⼀样,通常⼀个申请须要⼤量的代码,⽽且很多都是反复性质的
- 耦合重大: 异步操作与redux的action偶合在⼀起,不⽅便治理
- 性能孱弱: 有⼀些理论开发中常⽤的性能须要⾃⼰进⾏封装
应用步骤:
- 配置中间件,在store的创立中配置
import {createStore, applyMiddleware, compose} from 'redux';import reducer from './reducer';import thunk from 'redux-thunk'// 设置调试工具const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;// 设置中间件const enhancer = composeEnhancers( applyMiddleware(thunk));const store = createStore(reducer, enhancer);export default store;
- 增加一个返回函数的actionCreator,将异步申请逻辑放在外面
/** 发送get申请,并生成相应action,更新store的函数 @param url {string} 申请地址 @param func {function} 真正须要生成的action对应的actionCreator @return {function} */// dispatch为主动接管的store.dispatch函数 export const getHttpAction = (url, func) => (dispatch) => { axios.get(url).then(function(res){ const action = func(res.data) dispatch(action) })}
- 生成action,并发送action
componentDidMount(){ var action = getHttpAction('/getData', getInitTodoItemAction) // 发送函数类型的action时,该action的函数领会主动执行 store.dispatch(action)}
(2)应用redux-saga中间件
redux-saga长处:
- 异步解耦: 异步操作被被转移到独自 saga.js 中,不再是掺杂在 action.js 或 component.js 中
- action解脱thunk function: dispatch 的参数仍然是⼀个纯正的 action (FSA),⽽不是充斥 “⿊魔法” thunk function
- 异样解决: 受害于 generator function 的 saga 实现,代码异样/申请失败 都能够间接通过 try/catch 语法间接捕捉解决
- 性能强⼤: redux-saga提供了⼤量的Saga 辅助函数和Effect 创立器供开发者使⽤,开发者⽆须封装或者简略封装即可使⽤
- 灵便: redux-saga能够将多个Saga能够串⾏/并⾏组合起来,造成⼀个⾮常实⽤的异步flow
- 易测试,提供了各种case的测试⽅案,包含mock task,分⽀笼罩等等
redux-saga缺点:
- 额定的学习老本: redux-saga不仅在使⽤难以了解的 generator function,⽽且无数⼗个API,学习老本远超redux-thunk,最重要的是你的额定学习老本是只服务于这个库的,与redux-observable不同,redux-observable尽管也有额定学习老本然而背地是rxjs和⼀整套思维
- 体积庞⼤: 体积略⼤,代码近2000⾏,min版25KB左右
- 性能过剩: 实际上并发管制等性能很难⽤到,然而咱们仍然须要引⼊这些代码
- ts⽀持不敌对: yield⽆法返回TS类型
redux-saga能够捕捉action,而后执行一个函数,那么能够把异步代码放在这个函数中,应用步骤如下:
- 配置中间件
import {createStore, applyMiddleware, compose} from 'redux';import reducer from './reducer';import createSagaMiddleware from 'redux-saga'import TodoListSaga from './sagas'const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;const sagaMiddleware = createSagaMiddleware()const enhancer = composeEnhancers( applyMiddleware(sagaMiddleware));const store = createStore(reducer, enhancer);sagaMiddleware.run(TodoListSaga)export default store;
- 将异步申请放在sagas.js中
import {takeEvery, put} from 'redux-saga/effects'import {initTodoList} from './actionCreator'import {GET_INIT_ITEM} from './actionTypes'import axios from 'axios'function* func(){ try{ // 能够获取异步返回数据 const res = yield axios.get('/getData') const action = initTodoList(res.data) // 将action发送到reducer yield put(action) }catch(e){ console.log('网络申请失败') }}function* mySaga(){ // 主动捕捉GET_INIT_ITEM类型的action,并执行func yield takeEvery(GET_INIT_ITEM, func)}export default mySaga
- 发送action
componentDidMount(){ const action = getInitTodoItemAction() store.dispatch(action)}
(组件的)状态(state)和属性(props)之间有何不同
State
是一种数据结构,用于组件挂载时所需数据的默认值。State
可能会随着工夫的推移而产生渐变,但少数时候是作为用户事件行为的后果。
Props
(properties 的简写)则是组件的配置。props
由父组件传递给子组件,并且就子组件而言,props
是不可变的(immutable)。组件不能扭转本身的 props,然而能够把其子组件的 props 放在一起(对立治理)。Props 也不仅仅是数据--回调函数也能够通过 props 传递。
React中constructor和getInitialState的区别?
两者都是用来初始化state的。前者是ES6中的语法,后者是ES5中的语法,新版本的React中曾经废除了该办法。
getInitialState是ES5中的办法,如果应用createClass办法创立一个Component组件,能够主动调用它的getInitialState办法来获取初始化的State对象,
var APP = React.creatClass ({ getInitialState() { return { userName: 'hi', userId: 0 }; }})
React在ES6的实现中去掉了getInitialState这个hook函数,规定state在constructor中实现,如下:
Class App extends React.Component{ constructor(props){ super(props); this.state={}; } }
受控组件和非受控组件区别是啥?
- 受控组件是 React 管制中的组件,并且是表单数据实在的惟一起源。
- 非受控组件是由 DOM 解决表单数据的中央,而不是在 React 组件中。
只管非受控组件通常更易于实现,因为只需应用refs
即可从 DOM 中获取值,但通常倡议优先选择受管制的组件,而不是非受管制的组件。
这样做的次要起因是受控组件反对即时字段验证,容许有条件地禁用/启用按钮,强制输出格局。
概述一下 React中的事件处理逻辑。
为了解决跨浏览器兼容性问题, React会将浏览器原生事件( Browser Native Event)封装为合成事件( Synthetic Event)并传入设置的事件处理程序中。
这里的合成事件提供了与原生事件雷同的接口,不过它们屏蔽了底层浏览器的细节差别,保障了行为的一致性。另外, React并没有间接将事件附着到子元素上,而是以繁多事件监听器的形式将所有的事件发送到顶层进行解决(基于事件委托原理)。
这样 React在更新DOM时就不须要思考如何解决附着在DOM上的事件监听器,最终达到优化性能的目标。