创建react项目命令:
npx create-react-app my-app
进入文件夹:
cd my-app
启动项目:
npm start(开发模式下运行)
npm test(测试环境运行)
npm run build(打包为生产模式)
显示配置文件:不可逆的,只要显示了就没有任何方法再隐藏回去
npm run eject
为什么在src创建子目录?
webpack只处理src的中的文件。如果不讲css和html放在src文件夹下,webpack发现不了。
react是单页面应用程序
1.React是什么?
用来构造用户界面的JS库
2.React有什么特点?
它用于虚拟DOM,组件化设计模式,声明式代码,单向数据流,使用jsx描述信息等特点
3.什么是虚拟DOM?
用于构建虚拟的HTML的DOM
虚拟DOM可以看做是一棵模拟DOM树的js对象树。例如:
var element = {
element: 'ul',
props: {
id:"ulist"
},
children: [
{ element: 'li', props: { id:"first" }, children: ['这是第一个List元素'] },
{ element: 'li', props: { id:"second" }, children: ['这是第二个List元素'] }
]
}
4.为什么要使用虚拟DOM?
传统的数据只要发生变化,就会引起DOM重新渲染。虚拟DOM的目的是将所有操作累加起来,统计计算出所有的变化后,统一更新一次DOM。
5.什么是组件化设计模式?
可复用的代码可以抽成组件共同使用(UI,方法等)
6.声明式代码(编程)思想:
就是你的目的是什么,应该怎么做,但是具体不说明怎么去做
举例子:去酒吧告诉服务员你要一杯鸡尾酒,服务员把做好的鸡尾酒给你,并没有说做的过程是怎样的
const toLowerCase = arr = arr.map(
value => value.toLowerCase();
)
7.还有其他的编程方式吗?
命令式编程
就是描述代码如何工作,告诉计算机一步步的执行,先做什么后做什么。
举例子:去酒吧点一杯酒,指挥服务员
- 从架子上取下一个玻璃杯
- 把杯子放在酒桶前
- 打开酒桶开关,直到酒杯杯满
- 把做好的鸡尾酒递给顾客
const toLowerCase = arr => {
const res = [];
for (let i = 0, len = arr.length; i < len; i++) {
res.push(arr[i].toLowerCase());
}
return res;
}
8.编程式和命令式的对比
- 简洁,易懂,利于大型项目代码的维护
- 无须使用变量,避免了创建和修改状态
9.什么是单向数据流
数据主要从父节点传到子节点(通过props),如果父级的某个props改变了,React会重新渲染所有子节点(像瀑布的水一样,从上往下流,这个水就是组件的数据流。)
// 父组件
import React, {Component} from 'react'
import Child from './Child'
class Parent extends Component{
// 初始化
constructor(props){
super(props)
this.state = {
message: '我是从父组件传过来的'
}
}
// 渲染
render(){
return(
<div>
<Son msg={this.state.message}/>
</div>
)
}
}
export default Parent
// 子组件
import React, {Component} from 'react'
class Child extends Component{
// 初始化
constructor(props){
super(props)
}
// 渲染
render(){
return(
<div>
{this.props.message}
</div>
)
}
}
export default Child
10.什么是JSX呢?JSX代码到显示内容的转换过程
JSX就是JS的扩展语言(jsx语法= js语法+xml(html)语法)
JSX是JavaScript XML的缩写,是JavaScript的语法扩展,本质上是JavaScript对象。JSX可以很好的描述UI信息,但是浏览器无法直接读取,编译过程中会将JSX转换成JS的语法。
11.react的主要功能有哪些?
- 它使用虚拟DOM,而不是真正的DOM
- 它遵循单向数据流
12.react的优缺点
优点:
- 提高了应用性能和开发效率
- 使用JSX,代码可读性好
- react的componentWillUnmount生命周期,能够清除相关所有事件,避免内存泄露
- 并不直接对DOM进行操作,引入了一个虚拟DOM的概念,安插在js和真实DOM中间,性能好,速度快
缺点:每次 state
更改,render
函数都要生成完整的虚拟 DOM. 哪怕 state
改动很小,render
函数也会完整计算一遍。如果 render
函数很复杂,这个过程就白白浪费了很多计算资源
https://blog.csdn.net/weixin_42052388/article/details/80902388
13.VUE与React两个框架的区别对比
相似之处:
- 用于创建UI的js库
- 使用起来轻快便捷
- 都用了虚拟DOM
- 都是基于组件的架构
不同点 :
- vue使用的html模板;react使用的是js
- vue有双向绑定语法
- vue增加了语法糖computed和watch等,react需要自己写逻辑来实现
- react用了jsx语法
- react整体思路是编程式,推荐组件化,数据不变,单向数据流;vue数据可变,双向绑定,声明式的写法
14.React的工作原理
React会创建一个虚拟的DOM。当一个组件的状态改变时,React首先会通过“diffing"算法来标记虚拟DOM中的改变,第二步是调节,会用diff的结果来更新DOM.
15.Real DOM和Virtual DOM
React不直接操作DOM,而是实现了Virtual DOM,组件DOM映射到这个虚拟DOM上。React在虚拟DOM上实现了differ算法,当要重新渲染组件的时候,会通过diff寻找到要变更的DO0M节点,再把这个修改更新到浏览器实际的DOM节点上。
17.为什么虚拟DOM会提高性能
虚拟DOM相当于在JS和真实DOM中间加了一个缓存,利用diff减少了没有必要的操作,从而提高性能,本质上是JS对象。
- 虚拟DOM是映射真实DOM的js对象数据结构
- 采用虚拟DOM,避免直接操作真实的DOM,提供性能
- 当状态更新时,重新渲染新的虚拟DOM,采用diff算法对比新旧DOM,进行更新
18.阻止React的默认行为
e.preventDefault() e是第三方提供的一个合成事件
注意:不能用return
class App extends React.component{
constructor(props){
super(props);
this.state = {
}
}
hander(e){
e.preventDefault()
}
render(){
return <button onClick={ this.hander.bind(this) }>阻止事件</button>
}
}
19.向事件处理程序传递参数
例如删除当前行的ID
<button onClick={(e) => this.delete(e,id)}>Delete Row<button>
<button onClick={this.delete.bind(this,id)}>Delete Row</button>
20.React中key作用是什么?
作用(目的):key是用于追踪那些列表中元素被修改,删除或者被添加的辅助标识。在diff算法中,key用来判断该元素节点是被 移动过来的还是新创建元素,减少不必要的元素重复渲染。
21.当渲染一个列表时,何为 key?
Keys 会有助于 React 识别哪些 items
改变了,被添加了或者被移除了。Keys 应该被赋予数组内的元素以赋予(DOM)元素一个稳定的标识,选择一个 key 的最佳方法是使用一个字符串,该字符串能惟一地标识一个列表项。很多时候你会使用数据中的 IDs 作为 keys,当你没有稳定的 IDs 用于被渲染的 items
时,可以使用项目索引作为渲染项的 key,但这种方式并不推荐,如果 items
可以重新排序,就会导致 re-render
变慢。
22.React中diff算法
diff算法的作用是用来计算出Virtual DOM中被改变的部分,然后针对该部分进行原生DOM操作,而不用重新渲染整个页面。
https://www.cnblogs.com/yumingxing/p/9438457.html
diff算法有3种策略:
- tree diff:DOM节点跨层级的移动操作特别少,可以忽略不计。
- component diff:拥有相同类的两个组件生成相似的数据结构;拥有不同类的两个组件生成不同的树形结构
- element diff:对于同一层级的一组子节点,通过唯一id区分
diff算法的原理
- 将树形结构按照层级分解,只比较同级元素
- 给列表结构的每个单元添加唯一的key属性,方便比较
- React只会匹配相同的class的component
- 合并操作,调用component的setState方法的时候,React将其标记为dirty,到每一轮事件循环结束,React检查所有标记dirty的component重新渲染
- 选择性子树渲染。开发人员可以重新shouldComponentUpdate提高diff的性能。
23.React中Element与Component
Element是描述屏幕上课件内容的数据结构,是对UI对象的标书
Component是可以接受参数输入并且返回某个ReactElement的函数或者类
24.props和state((组件的)状态(state)和属性(props)之间有何不同)
- state是数据结构,会随着事件推移发送变化,只能使用setState来改变
- props是组件的属性,由父组件传递给子组件,props是不可以改变的
state是局部的,除了它所在的这个组件其它组件都无法访问
- 无状态组件:没有设置state的组件(无状态组件通过函数式声明来构建,一个函数就是一个组件)
- 有状态组件:设置了state的组件(有状态组件通过component来构建,一个子类就是一个组件)
25.React可以用两种方法声明组件(Component),他们的区别是什么,什么情况你会选择哪一种?(React中有哪些构建组件的方式)
- 函数组件:首字母要大写,需要return出react元素
- 类组件(Class Components):首字母要大写,需要使用render方法,return出react元素
// 函数组件
function Welcome(props){
return (
<h1>hello world</h1>
)
}
// 类组件
class Welcome extends React.Component{
render(){
return (
<div>11</div>
)
}
}
区别:
- 函数组件看似只是一个返回值是DOM结构的函数,其实它的背后是无状态组件的思想
- 函数组件中,你无法使用State,也无法使用组件的生命周期方法,这就决定了函数组件都是展开性组件,接收props,渲染DOM,而不关注其它逻辑
- 函数组件中没有this
- 函数组件更容易理解。当你看到一个函数组件时,你就知道它的功能只是接收属性,渲染页面,它不执行与UI无关的逻辑处理,它只是一个纯函数。而不用在意它返回的DOM结构有多复杂
26.React组件生命周期的阶段是什么?
React 组件的生命周期有三个不同的阶段:
- 初始渲染阶段:这是组件即将开始其生命之旅并进入 DOM 的阶段。
- 更新阶段:一旦组件被添加到 DOM,它只有在 prop 或状态发生变化时才可能更新和重新渲染。这些只发生在这个阶段。
- 卸载阶段:这是组件生命周期的最后阶段,组件被销毁并从 DOM 中删除。
27.简单聊聊生命周期(指出(组件)生命周期方法的不同)
- constuctor:在构造函数中初始化props和state
- componentWillMount--在组件渲染之前执行,对state进行最后的修改
- render:渲染
- componentDidMount-- 在组件渲染之后执行
componentWillReceiveProps
-- 这个周期函数作用于特定的 prop 改变导致的 state 转换shouldComponentUpdate
-- 用来做性能优化的,根据特定条件返回 true 或 false。如果你希望更新组件,请返回true 否则返回 false。默认情况下,它返回 false。componentWillUpdate
-- 数据在改变之前执行componentDidUpdate
-- 渲染发生后立即调用componentWillUnmount
-- 从 DOM 卸载组件后调用。用于清理内存空间
28.什么是单向数据流和状态提升
- 单向数据流:从上往下父组件将state数据流向子组件,子组件通过props取值(像瀑布的水一样,从上往下流,这个水就是组件的数据流)
- 状态提升:组件之间的数据交互(很多子组件想用这个状态,将这个状态提升到最上面,通过props谁用给谁)
举例子:如果两个组件都需要用到对方的状态,那么这时候就可以用到状态提升。具体做法是把两个子组件的状态写到他们的父组件中,然后父组件把状态传递给子组件的props中去,这样子组件也相当于有状态。
父组件
import React from "react"
import Child1 from "./child1"
import Child2 from "./child2"
export default class Parent extends React.Component {
constructor() {
super()
this.state = {
money: 1
}
}
changeHander(e){
this.setState({
money: e.target.value
})
}
render() {
return (
<div>
<input type="text" value={ this.state.money } onChange={this.changeHander.bind(this)} />
<p>Parent</p>
人民比: <Child1 money={this.state.money} />
美金: <Child2 money={this.state.money} />
</div>
)
}
}
子组件1
import React from "react"
export default class Child1 extends React.Component{
constructor(){
super()
this.state = {
input1: 0
}
}
componentDidMount(){
this.setState({
input1: this.props.money
})
}
changeHander(e) {
this.setState({
input1: e.target.value
})
}
render() {
return(
<div>
{ this.props.money }
<input type="text" value={ this.state.input1 } onChange={ this.changeHander.bind(this) }/>
</div>
)
}
}
子组件2
import React from "react"
export default class Child2 extends React.Component{
constructor(){
super();
this.state = {
input2: 1
}
}
componentDidMount(){
this.setState({
input2: this.props.money * 7
})
}
changeHander(e) {
this.setState({
input2: e.target.value
})
}
render() {
return(
<div>
{ this.props.money * 7}
<input type="text" value={ this.state.input2 } onChange={ this.changeHander.bind(this) }/>
</div>
)
}
}
29.调用setState之后发生了什么
setState会进行状态更新
将传入的参数对象与组件当前状态合并,然后触发所谓的调和过程,经过调和过程,根据新的state,React元素会重新构建虚拟DOM,进行diff算法对比新旧虚拟DOM树的区别,进行视图更新,而不是全部渲染。
setState 采用的任务队列机制,不会马上执行,而是加入队列,在下次事件循环是一次性执行
30.为什么建议传递给setState的参数是一个callback(回调函数)而不是一个对象(不懂--先不用背)
因为this.props和this.state的更新可能是异步的,不能依赖他们的值去计算下一个state
31.关于this绑定==组件绑定点击事件
// bind
// 1
<button onClick={this.handleClick.bind(this)}>hello<button/>
// 2
clicked(param,event){
console.log(param) //hello world
console.log(event.target.value) //按钮
}
render(){
return (
<React.Fragment>
<button value="按钮" onClick={this.clicked.bind(this,"hello world")}>点击</button>
</React.Fragment>
)
}
// 2.在构造函数中默认绑定this(推荐)
this.handleClick = this.handleClick.bind(this)
// 3.使用箭头函数来处理,handleClick点击事件的方法
<button onClick={(e) => this.handleClick(e)}>Click me</button>
32.setState第二个参数的作用 (不懂-不用背)
该函数会在setState函数调用完成并且组件开始重渲染的时候被调用,我们可以用该函数来监听渲染是否完成
(在构造函数中)调用 super(props) 的目的是什么
在 super() 被调用之前,子类是不能使用 this 的,在 ES5 中,子类必须在 constructor 中调用 super()。传递 props 给 super() 的原因则是便于(在子类中)能在 constructor
访问 this.props
33.什么是React 路由?
Router 用于定义多个路由,当用户定义特定的 URL 时,如果此 URL 与 Router 内定义的任何 “路由” 的路径匹配,则用户将重定向到该特定路由。
import React from 'react';
import { Switch, Route, BrowserRouter } from 'react-router-dom';
import Home from './views/Home'
export default class App extends React.Component{
// 初始化
constructor(props){
super(props);
this.state = {}
}
// 渲染
render(){
return(
<BrowserRouter>
<Switch>
<Route component={Home} path="/" />
</Switch>
</BrowserRouter>
)
}
}