刚做的网站多久能被搜索到,智库网站建设方案,全国建筑工程企业资质查询平台,企业网站的建设有哪些经典问题React 进阶一、认识 React1、是什么#xff1f;2、React 特性3、React 第一个实例#xff08;HTML 模板#xff09;4、React 安装二、React 核心1、JSX2、元素渲染3、组件4、Props5、State6、组件生命周期7、事件处理8、条件渲染9、列表 Key10、表单11、状态提升12、组…
React 进阶一、认识 React1、是什么2、React 特性3、React 第一个实例HTML 模板4、React 安装二、React 核心1、JSX2、元素渲染3、组件4、Props5、State6、组件生命周期7、事件处理8、条件渲染9、列表 Key10、表单11、状态提升12、组合 vs 继承13、无障碍辅助功能14、代码分割13、ContextPropTypes defaultProps获取真实的DOM节点ref/refsFragment14、Refs 转发高阶组件HOC面试官[题] 说说 Real DOM和 Virtual DOM 的区别优缺点[题] 说说 state 和 props有什么区别[题] 说说 super()和super(props)有什么区别[题] 说说 React中的 setState 执行机制[题] 说说 React的事件机制[题] 说说 React中组件之间如何通信END一、认识 React
1、是什么 React 是一个用于构建用户界面的 JavaScript 库。 React 主要用于构建UI很多人认为 React 是 MVC 中的 V视图。 React 起源于 Facebook 的内部项目用来架设 Instagram 的网站并于 2013 年 5 月开源。 React 拥有较高的性能代码逻辑非常简单越来越多的人已开始关注和使用它。 React用于构建用户界面的 JavaScript 库只提供了 UI 层面的解决方案 遵循组件设计模式、声明式编程范式和函数式编程概念以使前端应用程序更高效 使用虚拟DOM来有效地操作DOM遵循从高阶组件到低阶组件的单向数据流 帮助我们将界面成了各个独立的小块每一个块就是组件这些组件之间可以组合、嵌套构成整体页面 2、React 特性
声明式编程Component组件化JSX语法虚拟DOM单向数据绑定
优势
高效灵活声明式的设计简单使用组件式开发提高代码复用率单向响应的数据流会比双向绑定的更安全速度更快
3、React 第一个实例HTML 模板
1引入React react-dom的核心库babel可以将 ES6 代码转为 ES5 代码以及对 JSX 的支持 2ReactDOM.render()是 React 的最基本方法用于将模板转为 HTML 语言并插入指定的 DOM 节点。 3React 独有的 JSX 语法跟 JavaScript 不兼容。凡是使用 JSX 的地方都要加上 typetext/babel
!DOCTYPE html
htmlheadmeta charsetUTF-8 /titleHello React!/titlescript srchttps://cdn.staticfile.org/react/16.4.0/umd/react.development.js/scriptscript srchttps://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js/scriptscript srchttps://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js/script/head
bodydiv idexample/divscript typetext/babelReactDOM.render(h1Hello, world!/h1,document.getElementById(example));/script
/body
/html4、React 安装
1直接下载使用、直接使用eact CDN
script srchttps://cdn.staticfile.org/react/16.4.0/umd/react.development.js/script
script srchttps://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js/script
!-- 生产环境中不建议使用 --
script srchttps://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js/script官方提供的 CDN 地址
script srchttps://unpkg.com/react16/umd/react.development.js/script
script srchttps://unpkg.com/react-dom16/umd/react-dom.development.js/script
!-- 生产环境中不建议使用 --
script srchttps://unpkg.com/babel-standalone6.15.0/babel.min.js/script2npm
【1】使用 create-react-app 快速构建 React 开发环境 create-react-app 是来自于 Facebook通过该命令我们无需配置就能快速构建 React 开发环境。 create-react-app 自动创建的项目是基于 Webpack ES6 。 Create React App 是一个用于学习 React 的舒适环境也是用 React 创建新的单页应用的最佳方式。 npx create-react-app my-app
cd my-app
npm start# npx 不是拼写错误 —— 它是 npm 5.2 附带的 package 运行工具。create-react-app 执行慢的解决方法使用淘宝定制的 cnpm
$ npm install -g cnpm --registryhttps://registry.npm.taobao.org
$ npm config set registry https://registry.npm.taobao.org$ cnpm install -g create-react-app
$ create-react-app my-app
$ cd my-app
$ npm start运行结果 项目的目录结构如下
my-app
├── README.md 项目的说明文档
├── node_modules 项目的依赖包
├── package.json webpack配置和项目包管理文件,项目中依赖的第三方包包的版本和一些常用命令配置都在这个里边进行配置
├── package-lock.json 锁定安装时的版本号并且需要上传到git以保证其他人再npm install 时大家的依赖能保证一致
├── .gitignore git的选择性上传的配置文件配置不需要上传的文件。
├── public 公共文件里边有公用模板和图标等
│ ├── favicon.ico 网站或者说项目的图标一般在浏览器标签页的左上角显示
│ ├── index.html 首页的模板文件
│ └── manifest.json 移动端配置文件
└── src 源代码├── App.css├── App.js 一个模块化编程├── App.test.js├── index.css├── index.js 项目的入口文件├── logo.svg└── serviceWorker.js 用于写移动端开发的└── setupTests.js
【2】Next.js Next.js 是一个流行的、轻量级的框架用于配合 React 打造静态化和服务端渲染应用。它包括开箱即用的样式和路由方案并且假定你使用 Node.js 作为服务器环境。 【3】Gatsby Gatsby 是用 React 创建静态网站的最佳方式。它让你能使用 React 组件但输出预渲染的 HTML 和 CSS 以保证最快的加载速度。 【4】从头开始打造工具链
package 管理器比如 Yarn 或 npm。打包器比如 webpack 或 Parcel。编译器例如 Babel。
二、React 核心
1、JSX JSX就是Javascript和XML结合的一种格式。React发明了JSX利用HTML语法来创建虚拟DOM。当遇到JSX就当HTML解析遇到{就当JavaScript解析。 React的核心机制之一就是可以在内存中创建虚拟的DOM元素。React利用虚拟DOM来减少对实际DOM的操作从而提升性能。 jsx语法的优点 1JSX 执行更快因为它在编译为 JavaScript 代码后进行了优化。 2它是类型安全的在编译过程中就能发现错误。 3使用 JSX 编写模板更加简单快速。 1手动安装使用JSX
安装插件
// babel 插件
npm i babel-core babel-loader babel-plugin-transform-runtime -D
npm i babel-preset-env babel-preset-stage-0 -D
// 安装能够识别转换jsx语法的包 babel-preset-react
npm i babel-preset-react -D 添加 .babelrc 配置文件
{presets: [env, stage-0, react],plugins: [transform-runtime]}使用jsx语法 // 引用React、Component、ReactDOMimport React, { Component } from reactimport ReactDOM from react-dom; // 创建组件export default class Name extends Component {// render函数render() {var arr [h1Hello world!/h1,h2React is awesome/h2,];// 这里面可以做任何js操作最后return 一个dom结构就可以了return (// 模板插入 JavaScript 变量。如果这个变量是一个数组则会展开这个数组的所有成员div{arr}/div)}}2 jsx语法基本使用
1渲染html
单个html
ReactDOM.render(return (h1张培跃/h1)
)多个html
ReactDOM.render(return (divh1张培跃/h1h2欢迎学习 React/h2p今天天气不错挺风和日丽的/p/div))变量的形式
var myInfoh1我好帅我好苦恼啊/h1;
ReactDOM.render(return myInfo;
)【注意】不能在最外层添加多个标签在最外面套一个div就行了,不然会报错特别是渲染列表多重循环的时候
2JSX语法表达式 在 JSX 语法中你可以在大括号{}内放置任何有效的 JavaScript 表达式。 const name Josh Perez;
const element h1Hello, {name} {11}/h1;
ReactDOM.render(element,document.getElementById(root)
);3JSX语法三元运算
JSX 中不能使用 if else 语句可以使用 conditional (三元运算) 表达式来替代。
const element (divh1{i 1 ? True! : False}/h1{judge ?div classNameyes/div:div classNameno/div}/div
)4jsx使用className
JSX 语法上 使用 camelCase小驼峰命名来定义属性的名称 class 属性需要写成 className
div classNameAppheader classNameApp-headerimg src{logo} classNameApp-logo altlogo //header
/div5jsx使用style
不包起来会报错 let ys {color: yellow};divdiv style{{color: red}} /div // 内联 div style{ys} /div // 变量的形式div style{judge ? {color: red} : ys} /div // 三元表达式
/div6jsx中使用数组渲染
let arr[h1你是风儿/h1,h2我是沙/h2,h3缠缠绵绵到天涯/h3
];
let arr [你是风儿, 我是沙, 缠缠绵绵到天涯];
ReactDOM.render(return (div{arr} // 1、数组语法{arr.map((item, index) { // 2、利用map属性return div key{index}{item}/div})}list.map((item, index) { // 3、双重嵌套数据div key{index}item.map(itemChild, indexChild) { // 注意添加key 不添加直接报错return div key{index}{item.mes}/div}/div }) /div)
) 7JSX语法属性 JSX 语法上更接近 JavaScript 而不是 HTML所以 React DOM 使用 camelCase小驼峰命名来定义属性的名称 for 属性需要写成 htmlFor 8JSX语法注释
注释需要写在花括号 {} 中
const element (divh1Hello!/h1{/* 注释 */}h2Good to see you here./h2/div
);2、元素渲染 元素是构成 React 应用的最小单位它用于描述屏幕上输出的内容。 与浏览器的 DOM 元素不同React 当中的元素事实上是普通的对象React DOM 可以确保 浏览器 DOM 的数据内容与 React 元素保持一致。 1将一个元素渲染为 DOM
首先我们在一个 HTML 页面中添加一个根 DOM 节点
div idroot/div仅使用 React 构建的应用通常只有单一的根 DOM 节点。如果你在将 React 集成进一个已有应用那么你可以在应用中包含任意多的独立根 DOM 节点。
将一个 React 元素渲染到根 DOM 节点中只需把它们一起传入 ReactDOM.render()
const element h1Hello, world/h1;
ReactDOM.render(element, document.getElementById(root));2更新元素渲染
React 元素是不可变对象。当元素被创建之后你是无法改变其内容或属性的。 目前更新界面的唯一办法是创建一个新的元素然后将它传入 ReactDOM.render() 方法
注意 在实践中大多数 React 应用只会调用一次 ReactDOM.render()。在下一个章节我们将学习如何将这些代码封装到有状态组件中。
3React 只会更新必要的部分
值得注意的是 React DOM 首先会比较元素内容先后的不同而在渲染过程中只会更新改变了的部分。
3、组件 组件从概念上类似于 JavaScript 函数。它接受任意的入参即 “props”并返回用于描述页面展示内容的 React 元素。 React 允许将代码封装成组件component然后像插入普通 HTML 标签一样在网页中插入这个组件。 1React创建组件的三种方式及其区别
函数式定义的无状态组件es5原生方式React.createClass定义的组件es6形式的extends React.Component定义的组件
函数组件定义组件最简单的方式
这种组件只负责根据传入的props来展示不涉及到要state状态的操作
无状态函数式组件形式上表现为一个只带有一个render方法的组件类
function Welcome(props) {return h1Hello, {props.name}/h1;
}ReactDOM.render(Welcome nameSebastian /, mountNode
) 特点
无状态组件不会被实例化整体渲染性能得到提升 因为组件被精简成一个render方法的函数来实现的由于是无状态组件所以无状态组件就不会在有组件实例化的过程无实例化过程也就不需要分配多余的内存从而性能得到一定的提升。组件不能访问 this 对象 无状态组件由于没有实例化过程所以无法访问组件this中的对象例如this.ref、this.state等均不能访问。若想访问就不能使用这种形式来创建组件组件无法访问生命周期的方法 无状态组件是不需要组件生命周期管理和状态管理所以底层实现这种形式的组件时是不会实现组件的生命周期方法。所以无状态组件是不能参与组件的各个生命周期管理的。无状态组件只能访问输入的props同样的props会得到同样的渲染结果不会有副作用无状态组件内部其实是可以使用ref功能的 不能通过this.refs访问到但是可以通过将ref内容保存到无状态组件内部的一个本地变量中获取到。
function TestComp(props){let ref;return (divdiv ref{(node) ref node}.../div/div)
}React.createClassES5定义组件的方式
var InputControlES5 React.createClass({propTypes: { // 定义传入props中的属性各种类型initialValue: React.PropTypes.string},defaultProps: { // 组件默认的props对象initialValue: },// 设置 initial stategetInitialState: function() { // 组件相关的状态对象return {text: this.props.initialValue || placeholder};},handleChange(event) {this.setState({ //this represents react component instancetext: event.target.value});},render() {return (divType something:input onChange{this.handleChange} value{this.state.text} //div);}
});
InputControlES6.propTypes {initialValue: React.PropTypes.string
};
InputControlES6.defaultProps {initialValue:
};特点
有状态的组件会被实例化可以访问组件的生命周期方法React.createClass会自绑定函数方法导致不必要的性能开销增加代码过时的可能性。函数this自绑定 React.createClass创建的组件其每一个成员函数的this都有React自动绑定任何时候使用直接使用this.method即可函数中的this会被正确设置。React.createClass的mixins不够自然、直观
React.Component使用 ES6 的 class 来定义组件
class InputControlES6 extends React.Component {constructor(props) {super(props);// 设置 initial statethis.state {text: props.initialValue || placeholder};}handleClick() {console.log(this); // null}handleChange (event) {this.setState({text: event.target.value});}render() {return (divType something:input onChange{this.handleChange}value{this.state.text} //div);}
}
InputControlES6.propTypes {initialValue: React.PropTypes.string
};
InputControlES6.defaultProps {initialValue:
};特点
有状态的组件会被实例化可以访问组件的生命周期方法函数不会自动绑定this需要开发者手动绑定否则this不能获取当前组件实例对象。
函数手动绑定this有三种手动绑定方法
在构造函数中完成绑定在调用时使用method.bind(this)来完成绑定使用arrow function来绑定使用 class fields 语法推荐
class InputControlES6 extends React.Component {constructor(props) {super(props);this.state {text: };this.handleChange this.handleChange.bind(this);}handleChange(e) {console.log(this);this.setState({text: event.target.value});}render() {return (divType something:input onChange{this.handleChange}value{this.state.text} //div);}
}div onClick{this.handleChange.bind(this)}/divdiv onClick{()this.handleClick()}/div // 使用arrow function来绑定handleChange (e) {...
}React.createClass与React.Component区别
函数this自绑定 React.createClass创建的组件其每一个成员函数的this都有React自动绑定React.Component创建的组件其成员函数不会自动绑定this需要开发者手动绑定组件属性类型propTypes及其默认props属性defaultProps配置不同 React.createClass在创建组件时有关组件props的属性类型及组件默认的属性会作为组件实例的属性来配置其中defaultProps是使用getDefaultProps的方法来获取默认组件属性的
const TodoItem React.createClass({propTypes: { // as an objectname: React.PropTypes.string},getDefaultProps(){ // return a objectreturn {name: }}render(){return div/div}
})React.Component在创建组件时配置这两个对应信息时他们是作为组件类的属性不是组件实例的属性也就是所谓的类的静态属性来配置的。
class TodoItem extends React.Component {static propTypes {//类的静态属性name: React.PropTypes.string};static defaultProps {//类的静态属性name: };...
}组件初始状态state的配置不同 React.createClass创建的组件其状态state是通过getInitialState方法来配置组件相关的状态 React.Component创建的组件其状态state是在constructor中像初始化组件属性一样声明的。
const TodoItem React.createClass({// return an objectgetInitialState(){ return {isEditing: false}}render(){ return div/div }
})class TodoItem extends React.Component{constructor(props){super(props);this.state { // define this.state in constructorisEditing: false} }render(){ return div/div }
}Mixins的支持不同 Mixins(混入)是面向对象编程OOP的一种实现其作用是为了复用共有的代码将共有的代码通过抽取为一个对象然后通过Mixins进该对象来达到代码复用。 React.createClass在创建组件时可以使用mixins属性以数组的形式来混合类的集合。 React.Component这种形式并不支持Mixins但是React提供一个全新的方式来取代Mixins,那就是Higher-Order Components
总结
只要有可能尽量使用无状态组件。否则如需要state、生命周期方法等使用React.Component这种es6形式创建组件
2组件提取组合/复合以及渲染
组件名称必须以大写字母开头。小写字母开头的组件会被视为原生 DOM 标签组件类只能包含一个顶层标签否则会报错
function Avatar(props) {return (img classNameAvatarsrc{props.user.avatarUrl}alt{props.user.name}/);
}
function Welcome(props) {return h1Hello, {props.name}/h1;
}
function Name(props) {return h1网站名称{props.name}/h1;
}ReactDOM.render((divAvatar user{props.user} /Welcome nameSee /Welcome namehello /Welcome nameWorld /Name nameCahal //div),document.getElementById(root)
);4、Props 【Props 的只读性】组件无论是使用函数声明还是通过 class 声明都决不能修改自身的 props。 React的核心思想就是组件化思想页面会被切分成一些独立的、可复用的组件 组件从概念上看就是一个函数可以接受一个参数作为输入值这个参数就是props所以可以把props理解为从外部传入组件内部的数据 react具有单向数据流的特性所以他的主要作用是从父组件向子组件中传递数据 props除了可以传字符串数字还可以传递对象数组甚至是回调函数 class Welcome extends React.Component {render() {return h1Hello {this.props.name}/h1;}
}const element Welcome nameSara onNameChanged{this.handleName} /;5、State
一个组件的显示形态可以由数据状态和外部参数所决定而数据状态就是state一般在 constructor 中初始化
可以通过 this.state 属性读取。当用户点击组件可以通过 this.setState 方法就修改状态值每次修改以后自动调用 this.render 方法再次渲染组件。
class Button extends React.Component {constructor() {super();// 1) 向组件中添加局部的 statethis.state {count: 0,};}updateCount() {// 3) 通过调用 setState 来改变state局部数据this.setState((prevState, props) {return { count: prevState.count 1 }});}render() {return (// 2) 获取局部数据button onClick{() this.updateCount()}Clicked {this.state.count} times/button);}
}setState还可以接受第二个参数它是一个函数会在setState调用完成并且组件开始重新渲染时被调用可以用来监听渲染是否完成
this.setState({name:JS每日一题
},()console.log(setState finished))6、组件生命周期
主要讲述react16.4之后的生命周期可以分成三个阶段
创建阶段更新阶段卸载阶段
1创建阶段
主要分成了以下几个生命周期方法
constructorgetDerivedStateFromPropsrendercomponentDidMount
constructor 【执行时机】 实例过程中自动调用的方法 【应用】在该方法中在方法内部通过super关键字获取来自父组件的props通常的操作为初始化state状态或者在this上挂载方法 getDerivedStateFromProps 该方法是新增的生命周期方法是一个静态的方法因此不能访问到组件的实例 【执行时机】组件创建和更新阶段不论是props变化还是state变化也会调用 在每次render方法前调用第一个参数为即将更新的props第二个参数为上一个状态的state可以比较props 和 state来加一些限制条件防止无用的state更新 该方法需要返回一个新的对象作为新的state或者返回null表示state状态不需要更新 render 【执行时机】类组件必须实现的方法 【应用】用于渲染DOM结构可以访问组件state与prop属性 注意 不要在 render 里面 setState, 否则会触发死循环导致内存崩溃 componentDidMount 【执行时机】组件挂载到真实DOM节点后执行其在render方法之后执行 【应用】此方法多用于执行一些数据获取事件监听等操作 2更新阶段
主要分成了以下几个生命周期方法
getDerivedStateFromProps 同上shouldComponentUpdaterender同上getSnapshotBeforeUpdatecomponentDidUpdate
shouldComponentUpdate 【 执行时机】到新的props或者state时都会调用通过返回true或者false告知组件更新与否 【应用】用于告知组件本身基于当前的props和state是否需要重新渲染组件默认情况返回true 一般情况不建议在该周期方法中进行深层比较会影响效率 同时也不能调用setState否则会导致无限循环调用更新 getSnapshotBeforeUpdate 【 执行时机】该周期函数在render后执行执行之时DOM元素还没有被更新 该方法返回的一个Snapshot值作为componentDidUpdate第三个参数传入 【应用】此方法的目的在于获取组件更新前的一些信息比如组件的滚动位置之类的在组件更新后可以根据这些信息恢复一些UI视觉上的状态 getSnapshotBeforeUpdate(prevProps, prevState) {console.log(#enter getSnapshotBeforeUpdate);return foo;
}componentDidUpdate(prevProps, prevState, snapshot) {console.log(#enter componentDidUpdate snapshot , snapshot);
}componentDidUpdate 【 执行时机】组件更新结束后触发 【应用】在该方法中可以根据前后的props和state的变化做相应的操作如获取数据修改DOM样式等 3卸载阶段
componentWillUnmount
componentWillUnmount 用于组件卸载前清理一些注册是监听事件或者取消订阅的网络请求等 一旦一个组件实例被卸载其不会被再次挂载而只可能是被重新创建 7、事件处理
React 元素的事件处理和 DOM 元素的很相似但是有一点语法上的不同
事件名称命名方式不同React 事件的命名采用小驼峰式onClick而不是纯小写。事件处理函数书写不同使用 JSX 语法时你需要传入一个函数作为事件处理函数而不是一个字符串。阻止默认行为方式在 React 中你不能通过返回 false 的方式阻止默认行为。你必须显式的使用 preventDefault。
原生事件绑定方式
button onclickhandleClick()按钮命名/buttonscript
function handleClick(e) {...
}
/scriptReact 合成事件绑定方式 函数组件
function Form() {function handleClick(e) {console.log(e.nativeEvent); // 通过e.nativeEvent属性获取原生DOM事件e.preventDefault(); // 阻止默认事件console.log(You clicked submit.);}return button onClick{handleClick}按钮/button;
}class组件写法1传统事件写法不推荐
class Form extends React.Component {constructor(props) {super(props);// 为了在回调中使用 this这个绑定是必不可少的this.handleClick this.handleClick.bind(this);}handleClick(e) {...}render() {return button onClick{handleClick}按钮/button}
}class组件写法2class fields 语法推荐
class Form extends React.Component {handleClick (e) {...}render() {return button onClick{handleClick}按钮/button}
}更多传递参数 以下两种方式都可以向事件处理函数传递参数通过箭头函数和 Function.prototype.bind 来实现。
button onClick{(e) this.deleteRow(id, e)}Delete Row/button
button onClick{this.deleteRow.bind(this, id)}Delete Row/button在这两种情况下React 的事件对象 e 会被作为第二个参数传递。如果通过箭头函数的方式事件对象必须显式的进行传递而通过 bind 的方式事件对象以及更多的参数将会被隐式的进行传递。
React 组件支持很多事件除了 Click 事件以外还有 KeyDown 、Copy、Scroll 等完整的事件清单请查看官方文档。
8、条件渲染
React 中的条件渲染和 JavaScript 中的一样使用 JavaScript 运算符 if 或者条件运算符去创建元素来表现当前的状态然后让 React 根据它们来更新 UI。
1元素变量
function LoginButton(props) {return (button onClick{props.onClick}Login/button);
}function LogoutButton(props) {return (button onClick{props.onClick}Logout/button);
}function Greeting(props) {const isLoggedIn props.isLoggedIn;if (isLoggedIn) {return UserGreeting /;}return GuestGreeting /;
}class LoginControl extends React.Component {constructor(props) {super(props);this.state {isLoggedIn: false};}handleLoginClick () {this.setState({isLoggedIn: true});}handleLogoutClick () {this.setState({isLoggedIn: false});}render() {const isLoggedIn this.state.isLoggedIn;let button;if (isLoggedIn) {button LogoutButton onClick{this.handleLogoutClick} /;} else {button LoginButton onClick{this.handleLoginClick} /;}return (divGreeting isLoggedIn{isLoggedIn} /{button}/div);}
}ReactDOM.render(LoginControl /,document.getElementById(root)
);2与运算符 通过花括号包裹代码在 JSX 中嵌入表达式
之所以能这样做是因为在 JavaScript 中true expression 总是会返回 expression, 而 false expression 总是会返回 false。
class LoginControl extends React.Component {render() {const unreadMessages [React, Re: React, Re:Re: React];// const unreadMessages props.unreadMessages;return (div{unreadMessages.length 0 h2You have {unreadMessages.length} unread messages./h2}/div);}
}注意返回 false 的表达式会使 后面的元素被跳过但会返回 false 表达式。
render() {const count 0;return (div{ count h1Messages: {count}/h1} // div0/div/div);
}3三目运算符
condition ? true : false。
render() {const isLoggedIn this.state.isLoggedIn;return (divdivThe user is b{isLoggedIn ? currently : not}/b logged in./div{isLoggedIn? LogoutButton onClick{this.handleLogoutClick} /: LoginButton onClick{this.handleLoginClick} /}/div);
}4阻止组件渲染 在极少数情况下你可能希望能隐藏组件即使它已经被其他组件渲染。 若要完成此操作你可以让 render 方法直接返回 null而不进行任何渲染。
function WarningBanner(props) {if (!props.warn) {return null;}return (div classNamewarningWarning!/div);
}class Page extends React.Component {render() {return (divWarningBanner warn{this.props.showWarning} //div);}
}在组件的 render 方法中返回 null 并不会影响组件的生命周期。例如上面这个示例中componentDidUpdate 依然会被调用。
9、列表 Key
1基础列表组件
使用 Javascript 中的 map() 方法来遍历数组每一个子元素都应该需要一个唯一的key值不设置的话会收到警告
const data [{ id: 0, name: abc },{ id: 1, name: def },{ id: 2, name: ghi },{ id: 3, name: jkl }
];const ListItem (props) {return li{props.name}/li;
};const List () {return (ul{data.map((item) (ListItem name{item.name} key{item.id}/ListItem))}/ul);
};2key key 元素取值最好是这个元素在列表中拥有的一个独一无二的字符串。 通常我们使用数据中的 id 来作为元素的 key 当元素没有确定 id 的时候万不得已你可以使用元素索引 index 作为 key 作用React 存在 Diff算法而元素元素key属性的作用是用于判断元素是新创建的还是被移动的元素从而减少不必要的Diff
如果列表数据渲染中在数据后面插入一条数据key作用并不大 下面来看看在前面插入数据时使用key与不使用key的区别
this.state {movies:[111,222,333]
}insertMovie() {const newMovies [000 ,...this.state.numbers];this.setState({movies: newMovies})
}ul{this.state.movies.map((item, index) {return li{item}/li})}
/ul有key的时候react根据key属性匹配原有树上的子元素以及最新树上的子元素像上述情况只需要将000元素插入到最前面位置
没有key的时候所有的li标签都需要进行修改 但是并不是拥有key值代表性能越高如果说只是文本内容改变了不写key反而性能和效率更高。因为不写key是将所有的文本内容替换一下节点不会发生变化而写key则涉及到了节点的增和删
元素的 key 只有放在就近的数组上下文中才有意义。key 值在兄弟节点之间必须唯一把 key 传递给组件不能用key关键字要用其他name
总结
良好使用key属性是性能优化的非常关键的一步
key 应该是唯一的key不要使用随机值随机数在下一次 render 时会重新生成一个数字避免使用 index 作为 key
react判断key的流程具体如下图
3在 JSX 大括号中嵌入 map()
function NumberList(props) {const numbers props.numbers;return (ul{numbers.map((number) ListItem key{number.toString()} value{number} /)}/ul);
}10、表单
在 React 里HTML 表单元素的工作方式和其他的 DOM 元素有些不同。 在 HTML 中表单元素如、 和 通常自己维护 state并根据用户输入进行更新。而在 React 中可变状态mutable state通常保存在组件的 state 属性中并且只能通过使用 setState()来更新。
1受控组件
我们可以把两者结合起来使 React 的 state 成为“唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。
class NameForm extends React.Component {constructor(props) {super(props);this.state {name: ,desc: ,fruit: ,hover: []};}handleChangeName (event) {this.setState({name: event.target.value});}handleChangeDesc (event) {this.setState({desc: event.target.value});}handleChange (event) {this.setState({fruit: event.target.value});}handleChangeHover (event) {const target event.target.value;const index this.state.fruit.indexOf(target);if (index -1) {this.state.fruit.push(target);} else {this.state.fruit.splice(index, 1);}}handleSubmit (event) {alert(提交);event.preventDefault();}render() {return (form onSubmit{this.handleSubmit}// input输入框label htmlForname名字:input typetext value{this.state.name} onChange{this.handleChangeName} //labellabel htmlForguestsNumber of guests:inputnamenumberOfGueststypenumbervalue{this.state.numberOfGuests}onChange{this.handleInputChange} //label// textarea textarea value{this.state.desc} onChange{this.handleChangeDesc} /// select 单选select value{this.state.fruit} onChange{this.handleChange}option selected valuegrapefruit葡萄柚/optionoption valuelime酸橙/optionoption valuecoconut椰子/optionoption valuemango芒果/option/selectselect multiple{true} value{this.state.hover} onChange{this.handleChangeHover}option valuedance跳舞/optionoption valuepainting绘画/optionoption valueswim游泳/optionoption valuebasketball篮球/option/select// radiodiv onChange{this.onChangeValue}sex:input typeradio valueMale namegender / Maleinput typeradio valueFemale namegender / Femaleinput typeradio valueOther namegender / Other/divinput typesubmit value提交 //form);}
}在受控组件上指定 value 的 prop 会阻止用户更改输入。如果你指定了 value但输入仍可编辑则可能是你意外地将value 设置为 undefined 或 null。
11、状态提升 【React官网】使用 react 经常会遇到几个组件需要共用状态数据的情况。这种情况下我们最好将这部分共享的状态提升至他们最近的父组件当中进行管理。 React状态提升主要就是用来处理父组件和子组件的数据传递的。他可以让我们的数据流动的形式是自顶向下单向流动的所有组件的数据都是来自于他们的父辈组件也都是由父辈组件来统一存储和修改再传入子组件当中。 通常多个组件需要反映相同的变化数据这时我们建议将共享状态提升到最近的共同父组件中去。
class Parent extends React.Component {constructor(props) {super(props)this.state {count: 0}}setCount () {this.setState({count: this.state.count 1})}render() {return (divChildrenA count{this.state.count} /ChildrenB onClick{this.setCount} //div);}
}class ChildrenA extends React.Component {render() {return (div{this.props.count}/div);}
}class ChildrenB extends React.Component {handleSetNum (e) {this.props.setNum(e.target.value)}render() {return (div{this.props.count}input onChange{(this.handleSetNum} value{this.props.count} //div);}
}总结 React的状态提升其实是为了组件之间的数据更加单向性在数据的传输上始终只会出现一对一的情况在处理上也方便我们只需要在向子组件传递数据的那个父辈组件上进行操作并传回子组件使得数据更新这种方法也体现了React的单向数据流的设计思想在复用组件的时候组件的数据也不会相互干扰使代码逻辑上会更加便于管理。
12、组合 vs 继承 React 有十分强大的组合模式。我们推荐使用组合而非继承来实现组件间的代码重用。 组合包含关系
1this.props.children this.props 对象的属性与组件的属性一一对应但是有一个例外就是 this.props.children 属性。它表示组件的所有子节点 有些组件无法提前知晓它们子组件的具体内容。这种情况我们使用一个特殊的 children prop 来将他们的子组件传递到渲染结果中 这使得别的组件可以通过 JSX 嵌套将任意组件作为子组件传递给它们。
class NotesList extends React.Component {constructor(props) {super(props);}render() {return this.props.children}
}class NotesList extends React.Component {constructor(props) {super(props);}render() {return (FancyBorder colorblueh1 classNameDialog-titleWelcome/h1p classNameDialog-messageThank you for visiting our spacecraft!/p/FancyBorder);}
}this.props.children 的值有三种可能
如果当前组件没有子节点它就是 undefined如果有一个子节点数据类型是 object如果有多个子节点数据类型就是 array
2自行约定 少数情况下你可能需要在一个组件中预留出几个“洞”。这种情况下我们可以不使用 children而是自行约定将所需内容传入 props并使用相应的 prop。
class SplitPane extends React.Component {render() {return (div classNameSplitPanediv classNameSplitPane-left{props.left}/divdiv classNameSplitPane-right{props.right}/div/div); }
}class App extends React.Component {render() {return (SplitPaneleft{Contacts /}right{Chat /} /); }
}3特例关系 有些时候我们会把一些组件看作是其他组件的特殊实例比如 WelcomeDialog 可以说是 Dialog 的特殊实例。
function Dialog(props) {return (FancyBorder colorblueh1 classNameDialog-title{props.title}/h1p classNameDialog-message{props.message}/p{props.children}/FancyBorder);
}class SignUpDialog extends React.Component {constructor(props) {super(props);this.handleChange this.handleChange.bind(this);this.handleSignUp this.handleSignUp.bind(this);this.state {login: };}handleChange(e) {this.setState({login: e.target.value});}handleSignUp() {alert(Welcome aboard, ${this.state.login}!);}render() {return (Dialog titleMars Exploration ProgrammessageHow should we refer to you?input value{this.state.login} onChange{this.handleChange} /button onClick{this.handleSignUp} Sign Me Up! /button/Dialog);}
}继承 React中Props 和组合为你提供了清晰而安全地定制组件外观和行为的灵活方式。 组件可以直接引入import而无需通过 extend 继承它们。 所以react中继承是不需要的
13、无障碍辅助功能 官方描述网络无障碍辅助功能Accessibility也被称为 a11y因为以 A 开头以 Y 结尾中间一共 11 个字母是一种可以帮助所有人获得服务的设计和创造。无障碍辅助功能是使得辅助技术正确解读网页的必要条件。 我的理解其实就是写react应用的一些语义规范代码优化内容 1 HTML 属性使用带连字符-的命名法
JSX 支持所有 aria-* HTML 属性。虽然大多数 React 的 DOM 变量和属性命名都使用驼峰命名camelCased如 hyphen-casedkebab-caselisp-case)。
inputtypetextaria-label{labelText}aria-requiredtrueonChange{onchangeHandler}value{inputValue}namename
/2Fragments标签
React 中的一个常见模式是一个组件返回多个元素。Fragments 允许你将子列表分组而无需向 DOM 添加额外节点。
import React, { Fragment } from react;function ListItem({ item }) {return (Fragmentdt{item.term}/dtdd{item.description}/dd/Fragment);
}function Glossary(props) {return (dl{props.items.map(item (ListItem item{item} key{item.id} /))}/dl);
}Fragments标签缩写写法
dt{item.term}/dtdd{item.description}/dd
/3表单标记
所有的 HTML 表单控制例如 input 和 textarea 都需要被标注
label htmlForuserNameuserName:/label
input iduserName typetext namename/label htmlForpasswordName:/label
input idpassword typepassword namepassword/3表单错误提醒
W3C 展示用户推送
剩下其他的可以在react官网中阅读了解
无障碍辅助功能
14、代码分割
代码分割的优点
可以实现在初始加载的时候减少所需加载的代码量代码分割引用时可实现异步加载、并行加载能使加载速度更快还可以实现按需加载懒加载路由懒加载解决白屏等最终提升性能
React 中的代码分割
import()使用 React Loadable基于路由的代码分割
1import() 最好的代码分割方式
参数模块的路径 返回值返回一个 promise 对象
适用场合
按需加载在需要时在加载模块条件加载在if语句中做条件加载动态模块路径允许模块路径动态生成
目前流行的脚手架或打包器都已经支持 import() 语法
// math.js
export function add(a, b) {return a b;
}// app.js
import { math} from ./math.js;console.log(math(16, 26)); // 422使用 React Loadable
React Loadable 是一个第三方库提供友好的 API 进行使用提供一个占位 View 的参数在加载组件时呈现
import Loadable from react-loadable;const LoadableOtherComponent Loadable({loader: () import(./OtherComponent),loading: () divLoading.../div,
});const MyComponent () (LoadableOtherComponent/react-loadable
3基于路由的代码分割
// route.jsimport Home from ../containers/Home
import My from ../containers/Myconst routes [{path: /home,component: Home,},{path: /my,component: My,},{path: *,component: Home,},
]
export default routes;// route.config.js
import React from react
import { Redirect } from react-router-dom
import { Switch, Route } from react-routerexport const renderRoutes (routes, extraProps {}, switchProps {}) routes ? (Switch {...switchProps}{routes.map((route, i) {const key route.key || iif (route.redirect) {const { redirect } routeif (isString(redirect)) return Redirect key{key} to{redirect} /if (isObject(redirect)) {return Redirect key{key} {...redirect} /}}return (Routekey{key}exact{route.exact}path{route.path}render{props {if (route.render) {return route.render(props)}return (route.component {...props} {...extraProps} route{route} /)}}strict{route.strict}/)})}/Switch) : nullimport { BrowserRouter as Router, Route, Switch } from react-router-dom;
import Loadable from react-loadable;const Loading () divLoading.../div;const Home Loadable({loader: () import(./routes/Home),loading: Loading,
});const About Loadable({loader: () import(./routes/About),loading: Loading,
});const App () (RouterSwitchRoute exact path/ component{Home}/Route path/about component{About}//Switch/Router
);根据路由进行切割即路由懒加载
import React, { Suspense, lazy } from react;const OtherComponent React.lazy(() import(./OtherComponent));
const AnotherComponent React.lazy(() import(./AnotherComponent));function MyComponent() {return (divSuspense fallback{divLoading.../div}sectionOtherComponent /AnotherComponent //section/Suspense/div);
}
13、Context Context 提供了一个无需为每层组件手动添加 props就能在组件树间进行数据传递的方法。 常用作父组件向后代组件传递数据。请谨慎使用因为这会使得组件的复用性变差。 context
创建React.createContext存在Provider组件用于创建数据源通过value属性用于给后代组件传递数据存在Consumer组件用于接收数据或者使用contextType属性接收
eg: 通过 props 属性自上而下进行传递
class App extends React.Component {render() {return Toolbar themedark /;}
}function Toolbar(props) {// Toolbar 组件接受一个额外的“theme”属性然后传递给 ThemedButton 组件。// 如果应用中每一个单独的按钮都需要知道 theme 的值这会是件很麻烦的事// 因为必须将这个值层层传递所有组件。return (divThemedButton theme{props.theme} //div);
}class ThemedButton extends React.Component {render() {return Button theme{this.props.theme} /;}
}使用 context
const ThemeContext React.createContext(light);
class App extends React.Component {render() {// 使用一个 Provider 来将当前的 theme 传递给以下的组件树。// 无论多深任何组件都能读取这个值。// 在这个例子中我们将 “dark” 作为当前的值传递下去。return (ThemeContext.Provider valuedarkToolbar //ThemeContext.Provider);}
}// 中间的组件再也不必指明往下传递 theme 了。
function Toolbar() {return (divThemedButton //div);
}class ThemedButton extends React.Component {// 指定 contextType 读取当前的 theme context。// React 会往上找到最近的 theme Provider然后使用它的值。// 在这个例子中当前的 theme 值为 “dark”。static contextType ThemeContext;render() {return Button theme{this.context} /;}
}PropTypes defaultProps
PropTypes 设置组件属性接收数据类型 defaultProps 设置组件属性的默认值
组件的props属性默认可以接受任意值字符串、对象、函数等 组件类的PropTypes属性可以用来类型检查验证组件实例的属性是否符合要求
eg只要传的initialValue 不是数值就会报错
class InputControlES6 extends React.Component {constructor(props) {super(props);this.state {text: props.initialValue};}render() {return (input value{this.state.text} /);}
}
InputControlES6.propTypes {initialValue: React.PropTypes.string
};
InputControlES6.defaultProps {initialValue: hello
};InputControlES6 initialValue{123}/获取真实的DOM节点ref/refs
组件并不是真实的 DOM 节点而是存在于内存之中的一种数据结构叫做虚拟 DOM virtual DOM。只有当它插入文档以后才会变成真实的 DOM 。根据 React 的设计所有的 DOM 变动都先在虚拟 DOM 上发生然后再将实际发生变动的部分反映在真实 DOM上这种算法叫做 DOM diff 它可以极大提高网页的性能表现。
从组件获取真实 DOM 的节点这时就要用到 ref 属性
class MyComponent extends React.Component({handleClick: function() {this.refs.myTextInput.focus();},render: function() {return (divinput typetext refmyTextInput /input typebutton valueFocus the text input onClick{this.handleClick} //div);}
});Fragment
React 中的一个常见模式是一个组件返回多个元素。React.Fragments 允许你将子列表分组而无需向 DOM 添加额外节点。
import React, { Fragment } from react;function ListItem({ item }) {return (Fragmentdt{item.term}/dtdd{item.description}/dd/Fragment);
}function Glossary(props) {return (dl{props.items.map(item (ListItem item{item} key{item.id} /))}/dl);
}Fragments标签 短语法空标签
可以像使用任何其他元素一样使用但它不支持 key 或属性。
dt{item.term}/dtdd{item.description}/dd
/目前key 是唯一可以传递给 Fragment 的属性 通常的一个使用场景是将一个集合映射到一个 Fragments 数组
dl{props.items.map(item (// 没有keyReact 会发出一个关键警告React.Fragment key{item.id}dt{item.term}/dtdd{item.description}/dd/React.Fragment))}
/dl14、Refs 转发
高阶组件HOC
1 什么是高阶组件 高阶组件HOC是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分它是一种基于 React 的组合特性而形成的设计模式。 高阶组件本身不是组件它是一个参数为组件返回值也是一个组件的函数
组件是将 props 转换为 UI而高阶组件是将组件转换为另一个组件。
高阶作用用于强化组件复用逻辑提升渲染性能等作用。 2它解决了什么问题
复用逻辑 高阶组件更像是一个加工react组件的工厂批量对原有组件进行加工包装处理。我们可以根据业务需求定制化专属的HOC,这样可以解决复用逻辑。强化props 这个是HOC最常用的用法之一高阶组件返回的组件可以劫持上一层传过来的props,然后混入新的props,来增强组件的功能。代表作react-router中的withRouter赋能组件 HOC有一项独特的特性就是可以给被HOC包裹的业务组件提供一些拓展功能比如说额外的生命周期额外的事件但是这种HOC可能需要和业务组件紧密结合。典型案例react-keepalive-router中的 keepaliveLifeCycle就是通过HOC方式给业务组件增加了额外的生命周期。控制渲染 劫持渲染是hoc一个特性在wrapComponent包装组件中可以对原来的组件进行条件渲染节流渲染懒加载等功能。典型代表做react-redux中connect和 dva中 dynamic 组件懒加载。
3高阶组件衍生过程
1mixin模式 老版本的react-mixins。在react初期提供一种组合方法。通过React.createClass,加入mixins属性具体用法和vue 中mixins相似 const customMixin {componentDidMount(){console.log( ------componentDidMount------ )},say(){console.log(this.state.name)}
}const APP React.createClass({mixins: [ customMixin ],getInitialState(){return {name:alien}},render(){const { name } this.statereturn div hello ,world , my name is { name } /div}
})React.createClass连同mixins这种模式已经被废弃了。mixins 带来的一些负面的影响
mixin引入了隐式依赖关系不同mixins之间可能会有先后顺序甚至代码冲突覆盖的问题mixin代码会导致滚雪球式的复杂性
2原型链继承
在有状态组件class通过原型链继承来实现mixins
const customMixin { /* 自定义 mixins */componentDidMount(){console.log( ------componentDidMount------ )},say(){console.log(this.state.name)}
}function componentClassMixins(Component,mixin){ /* 继承 */for(let key in mixin){Component.prototype[key] mixin[key]}
}class Index extends React.Component{constructor(){super()this.state{ name:alien }}render(){return div hello,worldbutton onClick{ this.say.bind(this) } to say /button/div}
}
componentClassMixins(Index,customMixin)3extends继承模式
这种模式的好处在于可以封装基础功能组件然后根据需要去extends我们的基础组件按需强化组件但是值得注意的是必须要对基础组件有足够的掌握否则会造成一些列意想不到的情况发生。
class Base extends React.Component{constructor(){super()this.state{name:alien}}say(){console.log(base components)}render(){return div hello,world button onClick{ this.say.bind(this) } 点击/button /div}
}
class Index extends Base{componentDidMount(){console.log( this.state.name )}say(){ /* 会覆盖基类中的 say */console.log(extends components)}
}
export default Index4HOC模式 简单尝试一个HOC
function HOC(Component) {return class wrapComponent extends React.Component{constructor(){super()this.state{name:alien}}render()Component { ...this.props } { ...this.state } /}
}HOC
class Index extends React.Component{say(){const { name } this.propsconsole.log(name)}render(){return div hello,world button onClick{ this.say.bind(this) } 点击/button /div}
}5自定义hooks模式 hooks的诞生一大部分原因是解决无状态组件没有state和逻辑难以复用问题。hooks可以将一段逻辑封装起来做到开箱即用
hooks hooks
4HOC的编写和使用结构
1使用
装饰器模式对于class声明的有状态组件我们可以用装饰器模式
注意包装顺序越靠近Index组件的就是越内层的HOC,离组件Index也就越近
withStyles(styles)
withRouter
keepaliveLifeCycle
class Index extends React.Componen{/* ... */
}函数包裹模式对于无状态组件(函数声明
function Index(){/* .... */
}
export default withStyles(styles)(withRouter( keepaliveLifeCycle(Index) )) 2HOC模型
对于不需要传递参数的HOC我们编写模型我们只需要嵌套一层就可以
function withRouter(){return class wrapComponent extends React.Component{/* 编写逻辑 */}
}对于需要参数的HOC我们需要一层代理
function connect (mapStateToProps){/* 接受第一个参数 */return function connectAdvance(wrapCompoent){/* 接受组件 */return class WrapComponent extends React.Component{ }}
}5两种不同的高阶组件
正向属性代理反向的组件继承
正向属性代理 用组件包裹一层代理组件在代理组件上我们可以做一些对源组件的代理操作 我们可以理解为父子组件关系父组件对子组件进行一系列强化操作。 function HOC(WrapComponent){return class Advance extends React.Component{state{name:alien}render(){return WrapComponent { ...this.props } { ...this.state } /}}
}优点 1正向属性代理可以和业务组件低耦合零耦合对于条件渲染和props属性增强,只负责控制子组件渲染和传递额外的props就可以所以无须知道业务组件做了些什么。所以正向属性代理更适合做一些开源项目的hoc目前开源的HOC基本都是通过这个模式实现的。 2同样适用于class声明组件和function声明的组件。 3可以完全隔离业务组件的渲染,相比反向继承属性代理这种模式。可以完全控制业务组件渲染与否可以避免反向继承带来一些副作用比如生命周期的执行。 4可以嵌套使用多个hoc是可以嵌套使用的而且一般不会限制包装HOC的先后顺序。
缺点 1 一般无法直接获取业务组件的状态如果想要获取需要ref获取组件实例。 2无法直接继承静态属性。如果需要继承需要手动处理或者引入第三方库。
反向继承 反向继承在于包装后的组件继承了业务组件本身所以我们我无须在去实例化我们的业务组件。当前高阶组件就是继承后加强型的业务组件。这种方式类似于组件的强化 class Index extends React.Component{render(){return div hello,world /div}
}
function HOC(Component){return class wrapComponent extends Component{ /* 直接继承需要包装的组件 */}
}
export default HOC(Index) 优点 1方便获取组件内部状态比如stateprops ,生命周期,绑定的事件函数等 2es6继承可以良好继承静态属性。我们无须对静态属性和方法进行额外的处理。
缺点 1无状态组件无法使用。 2和被包装的组件强耦合需要知道被包装的组件的内部状态具体是做什么 3如果多个反向继承hoc嵌套在一起当前状态会覆盖上一个状态。这样带来的隐患是非常大的
6编写高阶组件详细讲解
编写高阶组件
面试官
[题] 说说 Real DOM和 Virtual DOM 的区别优缺点 【Real DOM】真实DOM 意思为文档对象模型是一个结构化文本的抽象在页面渲染出的每一个结点都是一个真实DOM结构 【Virtual Dom】本质上是以 JavaScript 对象形式存在的对 DOM 的描述 创建虚拟DOM目的就是为了更好将虚拟的节点渲染到页面视图中虚拟DOM对象的节点与真实DOM的属性一一照应
在React中JSX是其一大特性可以让你在JS中通过使用XML的方式去直接声明界面的DOM结构
const vDom h1Hello World/h1JSX实际是一种语法糖在使用过程中会被babel进行编译转化成JS代码上述VDOM转化为如下
const vDom React.createElement(h1 { className: hClass, id: hId },hello world
)两者的区别如下
虚拟DOM不会进行排版与重绘操作而真实DOM会频繁重排与重绘虚拟DOM的总损耗是“虚拟DOM增删改真实DOM差异增删改排版与重绘”真实DOM的总损耗是“真实DOM完全增删改排版与重绘”
优缺点如下 真实DOM 优势易用 缺点 1效率低解析速度慢内存占用量过高 2性能差频繁操作真实DOM易于导致重绘与回流
虚拟DOM 优势 1简单方便如果使用手动操作真实DOM来完成页面繁琐又容易出错在大规模应用下维护起来也很困难 2性能方面使用Virtual DOM能够有效避免真实DOM数频繁更新减少多次引起重绘与回流提高性能 3跨平台React借助虚拟DOM 带来了跨平台的能力一套代码多端运行 缺点 1在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化 2首次渲染大量DOM时由于多了一层虚拟DOM的计算速度比正常稍慢
[题] 说说 state 和 props有什么区别
相同点
两者都是 JavaScript 对象两者都是用于保存信息props 和 state 都能触发渲染更新
区别
props 是外部传递给组件的而 state 是在组件内被组件自己管理的一般在 constructor 中初始化props 在组件内部是不可修改的但 state 在组件内部可以进行修改state 是多变的、可以修改
[题] 说说 super()和super(props)有什么区别
1ES6类 在ES6中通过extends关键字实现类的继承通过 super 关键字实现调用父类super代替的是父类的构建函数 在子类中不使用 super 关键字则会引发报错。原因是 子类是没有自己的this对象的它只能继承父类的this对象然后对其进行加工 super(name)相当于调用sup.prototype.constructor.call(this,name) super()就是将父类中的this对象继承给子类的没有super() 子类就得不到this对象
所以在子类constructor中必须先代用super才能引用this
class sup {constructor(name) {this.name name}printName() {console.log(this.name)}
}class sub extends sup{constructor(name,age) {super(name) // super代表的事父类的构造函数this.age age}printAge() {console.log(this.age)}
}let jack new sub(jack,20)
jack.printName() //输出 : jack
jack.printAge() //输出 : 202类组件
React中类组件是基于es6的规范实现的继承 React.Component
如果用到 constructor 就必须写 super 才初始化 this。在render中this.props都是可以使用的这是React自动附带的是可以不写 constructor super 的
调用 super() 的时候我们一般都需要传入props作为参数如果不传进去React内部也会将其定义在组件实例中但调用 this.props 为undefined
class HelloMessage extends React.Component {constructor(props) {super(props); // 必须传入 props}render (){return (divnice to meet you! {this.props.name}/div);}
}等同于
class HelloMessage extends React.Component{render (){return (divnice to meet you! {this.props.name}/div);}
}3总结
React中类组件基于ES6在constructor中必须使用super调用super不建议使用super()代替super(props)this.props在super()和构造函数结束之间仍是undefined
[题] 说说 React中的 setState 执行机制 【setState】用来修改一个组件的state数据状态里面的值的状态从而达到更新组件内部数据的作用 执行this.setState方法更新state状态然后重新执行render函数从而导致页面的视图更新 直接修改state的状态state的状态是已经发生了改变但是页面并不会有任何反应
这是因为React并不像vue2中调用Object.defineProperty数据响应式或者Vue3调用Proxy监听数据的变化 必须通过setState方法来告知react组件state已经发生了改变
关于state方法的定义是从React.Component中继承定义的源码如下
Component.prototype.setState function(partialState, callback) {invariant(typeof partialState object ||typeof partialState function ||partialState null,setState(...): takes an object of state variables to update or a function which returns an object of state variables.,);this.updater.enqueueSetState(this, partialState, callback, setState);
};1setState的更新类型
setState的更新类型分成
异步更新同步更新
异步更新在组件生命周期或React合成事件中setState是异步
changeText() {this.setState({message: 你好啊})console.log(this.state.message); // Hello World
}// 想要立刻获取更新后的值在第二个参数的回调中更新后会执行
changeText() {this.setState({message: 你好啊}, () {console.log(this.state.message); // 你好啊});
}同步更新在setTimeout或者原生dom事件中setState是同步
changeText() {setTimeout(() {this.setState({message: 你好啊});console.log(this.state.message); // 你好啊}, 0);
}componentDidMount() {const btnEl document.getElementById(btn);btnEl.addEventListener(click, () {this.setState({message: 你好啊,李银河});console.log(this.state.message); // 你好啊,李银河})
}2setState 的批量更新策略
a对同一个值进行多次 setState 会对其进行覆盖取最后一次的执行结果
handleClick () {this.setState({count: this.state.count 1,})console.log(this.state.count) // 1this.setState({count: this.state.count 1,})console.log(this.state.count) // 1this.setState({count: this.state.count 1,})console.log(this.state.count) // 1
}
// 点击按钮触发事件打印的都是 1页面显示 count 的值为 2b如果是下一个state依赖前一个state的话推荐给setState一个参数传入一个function如下
onClick () { this.setState((prevState, props) { return {count: prevState.count 1}; }); this.setState((prevState, props) { return {count: prevState.count 1}; });
}c在setTimeout或者原生dom事件中由于是同步的操作所以并不会进行覆盖现象
[题] 说说 React的事件机制 React基于浏览器的事件机制自身实现了一套事件机制包括事件注册、事件的合成、事件冒泡、事件派发等。在React中这套事件机制被称之为合成事件 1合成事件SyntheticEvent 合成事件是 React 模拟原生 DOM 事件所有能力的一个事件对象即浏览器原生事件的跨浏览器包装器 兼容所有浏览器拥有与浏览器原生事件相同的接口 React 元素的事件处理和 DOM 元素的很相似但是有一点语法上的不同
事件名称命名方式不同React 事件的命名采用小驼峰式onClick而不是纯小写。事件处理函数书写不同使用 JSX 语法时你需要传入一个函数作为事件处理函数而不是一个字符串。阻止默认行为方式在 React 中你不能通过返回 false 的方式阻止默认行为。你必须显式的使用 preventDefault。
原生事件绑定方式
button onclickhandleClick()按钮命名/buttonscript
function handleClick(e) {...
}
/scriptReact 合成事件绑定方式 函数组件
function Form() {function handleClick(e) {console.log(e.nativeEvent); // 通过e.nativeEvent属性获取原生DOM事件e.preventDefault(); // 阻止默认事件console.log(You clicked submit.);}return button onClick{handleClick}按钮/button;
}class组件写法1
class Form extends React.Component {constructor(props) {super(props);// 为了在回调中使用 this这个绑定是必不可少的this.handleClick this.handleClick.bind(this);}handleClick(e) {...}render() {return button onClick{handleClick}按钮/button}
}class组件写法2
class Form extends React.Component {handleClick (e) {...}render() {return button onClick{handleClick}按钮/button}
}更多
button onClick{(e) this.deleteRow(id, e)}Delete Row/button
button onClick{this.deleteRow.bind(this, id)}Delete Row/button2执行顺序
import React from react;
class App extends React.Component{constructor(props) {super(props);this.parentRef React.createRef();this.childRef React.createRef();}componentDidMount() {console.log(React componentDidMount);this.parentRef.current?.addEventListener(click, () {console.log(原生事件父元素 DOM 事件监听);});this.childRef.current?.addEventListener(click, () {console.log(原生事件子元素 DOM 事件监听);});document.addEventListener(click, (e) {console.log(原生事件document DOM 事件监听);});}parentClickFun () {console.log(React 事件父元素事件监听);};childClickFun () {console.log(React 事件子元素事件监听);};render() {return (div ref{this.parentRef} onClick{this.parentClickFun}div ref{this.childRef} onClick{this.childClickFun}分析事件执行顺序/div/div);}
}
export default App;输出顺序为原生事件子元素 DOM 事件监听
原生事件父元素 DOM 事件监听
React 事件子元素事件监听
React 事件父元素事件监听
原生事件document DOM 事件监听 结论
React 所有事件都挂载在 document 对象上当真实 DOM 元素触发事件会冒泡到 document 对象后再处理 React 事件所以会先执行原生事件然后处理 React 事件最后真正执行 document 上挂载的事件
所以想要阻止不同时间段的冒泡行为对应使用不同的方法对应如下
阻止合成事件间的冒泡用e.stopPropagation()阻止合成事件与最外层 document 上的事件间的冒泡用e.nativeEvent.stopImmediatePropagation()阻止合成事件与除最外层document上的原生事件上的冒泡通过判断e.target来避免
document.body.addEventListener(click, e { if (e.target e.target.matches(div.code)) { return; } this.setState({ active: false, }); });
}3总结:
React 上注册的事件最终会绑定在document这个 DOM 上而不是 React 组件对应的 DOM(减少内存开销就是因为所有的事件都绑定在 document 上其他节点没有绑定事件)React 自身实现了一套事件冒泡机制所以这也就是为什么我们 event.stopPropagation()无效的原因。React 通过队列的形式从触发的组件向父组件回溯然后调用他们 JSX 中定义的 callbackReact 有一套自己的合成事件 SyntheticEvent
[题] 说说 React中组件之间如何通信 React的组件灵活多样按照不同的方式可以分成很多类型的组件 组件间通信即指组件通过某种方式来传递信息以达到某个目的 1、通信
父组件向子组件传递子组件向父组件传递兄弟组件之间的通信父组件向后代组件传递非关系组件传递
1父组件向子组件传递 React的数据流动为单向的父组件向子组件传递是最常见的方式 props属性
父组件在子组件标签内传递参数子组件通过props属性接收父组件传递过来的参数
function EmailInput(props) {return (labelEmail: input value{props.email} //label);
}const element EmailInput email123124132163.com /;2子组件向父组件传递
基本思路父组件向子组件传一个函数然后通过这个函数的回调拿到子组件传过来的值
parent
class Parents extends Component {constructor() {super();this.state {price: 0};}getItemPrice(e) {this.setState({price: e});}render() {return (divdivprice: {this.state.price}/div{/* 向子组件中传入一个函数 */}Child getPrice{this.getItemPrice.bind(this)} //div);}
}child
class Child extends Component {clickGoods(e) {// 在此函数中传入值this.props.getPrice(e);}render() {return (divbutton onClick{this.clickGoods.bind(this, 100)}goods1/buttonbutton onClick{this.clickGoods.bind(this, 1000)}goods2/button/div);}
}3兄弟组件之间的通信
基本思路父组件作为中间层来实现数据的互通通过使用父组件传递
class Parent extends React.Component {constructor(props) {super(props)this.state {count: 0}}setCount () {this.setState({count: this.state.count 1})}render() {return (divSiblingAcount{this.state.count}/SiblingBonClick{this.setCount}//div);}
}4父组件向后代组件传递
父组件向后代组件传递数据是一件最普通的事情就像全局数据一样
使用context提供了组件之间通讯的一种方式可以共享数据其他数据都能读取对应的数据
context
创建React.createContext存在Provider组件用于创建数据源通过value属性用于给后代组件传递数据存在Consumer组件用于接收数据或者使用contextType属性接收 const PriceContext React.createContext(price)PriceContext.Provider value{100}
/PriceContext.Providerclass MyClass extends React.Component {static contextType PriceContext;render() {let price this.context;/* 基于这个值进行渲染工作 */}
}PriceContext.Consumer{ /*这里是一个函数*/ }{price divprice{price}/div}
/PriceContext.Consumer5非关系组件传递 组件之间关系类型比较复杂的情况建议将数据进行一个全局资源管理从而实现通信例如redux
总结
由于React是单向数据流主要思想是组件不会改变接收的数据只会监听数据的变化当数据发生变化时它们会使用接收到的新值而不是去修改已有的值
因此可以看到通信过程中数据的存储位置都是存放在上级位置中
END