第一章 react简介
1.什么是react
用于构建用户界面的JavaScript库,是一个将数据渲染为HTML试图的开源JavaScript库
2.为什么选择使用react
1.原生JavaScript操作DOM繁琐、效率低
2.使用JavaScript直接操作DOM,浏览器会进行大量的重绘重排
3.原生JavaScript没有组件化编码方案,代码复用率低
3.react的特点
1.采用组件化模式、声明式编码,提高开发效率及组件复用率。
2.在React Native中可以使用React 语法进行移动端开发
3.使用虚拟DOM+优秀的Diffing算法,尽量减少与真实的DOM交互
第二章 React的基本使用
babel的作用 ES6转换ES5 JSX转换js
1.引入JS库进行使用(未使用脚手架的方式)
<!--引入react核心库--> <script type="text/javascript" src ="../js/react.development.js"></script> <!--引入react-dom 用于支持react操作DOM--> <script type="text/script" src="../js/react-dom.development.js"></script> <!--引入babel 用于将JSX转换为JS--> <script type="text/script" src="../js/babel.min.js"></script>
2.虚拟DOM
1.本质是Object类型的对象
2.虚拟DOM属性较少,真实DOM属性较多
3.虚拟DOM最终会被React转换为真实DOM渲染在页面上
3.JSX
1.react定义的一种类似于XML的JS扩展语法
2.本质是React.createElement(component,props,...children)方法的语法糖
3.作用:用来简化创建虚拟DOM
4.jsx语法规则:(1)定义虚拟DOM时,不要写引号
(2)标签中混入js 表达式时要用{}
(3)样式的类名指定要用ClassName
(4)内联style样式需要用{{}}
(5)只能有一个根标签,所有标签必须闭合。
(6)组件名首字母必须大写
4.数据绑定
1.对象数据不允许直接使用
2.Boolean,undefined,null在页面展示需要借助三元运算符
3.数组 会将数据的每一项拆分开
4.数据要存放在state中
5.修改数据需要使用setState,setState触发render再次执行,更新页面数据
6.在同步内循环内多次的setState会被合并成一次执行,在异步内setState不会被合并
7.setState是一个异步的方法,第一个参数是要修改的数据,第二个参数是数据修改成功,页面更新完成后的回调,类似vue中的nextTick
5.属性绑定
标签上的属性值是动态的,可以是变量可以是表达式。
6.指令
条件渲染:react没有v-if,但是可以借助三元表达式、或(||)运算符,与(&&)运算符,渲染函数实现条件渲染
列表渲染:react没有v-for,但是可以借助map方法
7.事件
1.react事件是合成事件 事件名称是原生js事件名变为小驼峰 事件的处理函数 直接作为class的属性 注意 函数内this的问题
2.react也有默认事件对象,事件对象和其他参数同时存在 事件对象会移动到最后
第三章 模块化与组件化
1.模块
向外提供特定功能的js程序,一般就是一个js文件
作用:复用js,简化js的编写,提高js运行效率。
2.模块化
当应用 js都以模块来编写的,这个应用就是一个模块化的应用
3.组件
用来实现局部功能效果的代码和资源的集合
作用:复用代码,简化项目编码,提高运行效率
4.组件化
当应用是以多组件的方式实现,这个应用就是组件化的应用
5.定义组件的两种方式
1.函数式组件(简单组件,无状态组件)
1.创建函数式组件 function Demo(){ console.log(this) 此处的this是undefined,因为babel编译后开启了严格模式 return <h2>我是函数式组件</h2> } 2.渲染函数式组件 React.render(<Demo/>,document.getElementById('test'))
2.类式组件 (复杂组件,有状态组件)
1.创建类式组件 class MyComponent extends React.Component{ render(){ //render放在MyComponent原型对象上,供实例使用 return <h2>我是函数式组件</h2> } } 2.渲染类式组件 React.render(<MyComponent/>,document.getElementById('test'))
第四章 react的核心属性
1.state
(1)state是组件对象最重要的属性,值是对象(可以包含多个key-value的组合)
(2)组件被称为状态机,通过更新组件的state来更新对应的页面显示(重新渲染组件)
注意点
-
组件中render方法中的this为组件实例对象
-
组件自定义的方法中this为undefined 如何解决?(1)强制绑定this(2)箭头函数
-
状态数据,不能直接修改或更新
2.prop
1.使用prop-types检查props
React其实是为了构建大型应用程序而生, 在一个大型应用中,根本不知道别人使用你写的组件的时候会传入什么样的参数,有可能会造成应用程序运行不了,但是不报错。为了解决这个问题,React提供了一种机制,让写组件的人可以给组件的props
设定参数检查,需要安装和使用prop-types:
npm i prop-types -S
import React, { Component } from 'react'; import PropTypes from 'prop-types' // 如果需要验证子组件的属性的数据类型 // https://react.docschina.org/docs/typechecking-with-proptypes.html // 自 React v15.5 起,React.PropTypes 已移入另一个包中。 cnpm i prop-types -S // 在定义完组件以后,通过 组件.propTypes = { key: PropTypes.数据类型 } 验证属性的数据类型 // 如果某一个属性的值是必须要传递的属性, 通过 PropTypes.数据类型.isRequired 实现(如果设置了默认值,也算传递了属性) // 如果某一个属性的值 既可以是 字符串,也可以是number,那么通过 PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]) 实现 // 如果既可以是 多类型,也是必传项,通过 PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]).isRequired 实现 // 如果需要指定一个自定义验证器。它在验证失败时应返回一个 Error 对象。 const Header = (props) => { return ( <div>欢迎来到 { props.title } 的世界</div> ) } Header.defaultProps = { title: 'React' } Header.propTypes = { // title: PropTypes.string title: PropTypes.string.isRequired // 代表该属性是必须传递的 } function Content (props) { return ( <div> { props.content }的核心库只关注于视图层 </div> ) } Content.defaultProps = { content: 'React.js' } Content.propTypes = { // content: PropTypes.number // content: PropTypes.string content: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]).isRequired } class Footer extends Component { // static defaultProps = { // 类组件,可以通过类的静态属性设置默认值 // name: 'React.js' // } render () { return ( <div> 学 { this.props.name } 实际上就是学习js </div> ) } } Footer.defaultProps = { name: 'React' } Footer.propTypes = { // name: PropTypes.string name: function (props, propName, componentName) { console.log(props) // { name: 'React' } console.log(propName) // name console.log(componentName) // Footer // 如果需要指定一个自定义验证器。它在验证失败时应返回一个 Error 对象 if (props[propName].length < 6) { return new Error(`传递的${propName}的长度必须大于等于6`) } } } class App extends Component { render() { return ( <div> <Header ></Header> <Content ></Content> <Footer ></Footer> </div> ); } } export default App;
prop-types提供的所有检查能力:
import PropTypes from 'prop-types'; MyComponent.propTypes = { // 你可以将属性声明为 JS 原生类型,默认情况下 // 这些属性都是可选的。 optionalArray: PropTypes.array, optionalBool: PropTypes.bool, optionalFunc: PropTypes.func, optionalNumber: PropTypes.number, optionalObject: PropTypes.object, optionalString: PropTypes.string, optionalSymbol: PropTypes.symbol, // 任何可被渲染的元素(包括数字、字符串、元素或数组) // (或 Fragment) 也包含这些类型。 optionalNode: PropTypes.node, // 一个 React 元素。 optionalElement: PropTypes.element, // 一个 React 元素类型(即,MyComponent)。 optionalElementType: PropTypes.elementType, // 你也可以声明 prop 为类的实例,这里使用 // JS 的 instanceof 操作符。 optionalMessage: PropTypes.instanceOf(Message), // 你可以让你的 prop 只能是特定的值,指定它为 // 枚举类型。 optionalEnum: PropTypes.oneOf(['News', 'Photos']), // 一个对象可以是几种类型中的任意一个类型 optionalUnion: PropTypes.oneOfType([ PropTypes.string, PropTypes.number, PropTypes.instanceOf(Message) ]), // 可以指定一个数组由某一类型的元素组成 optionalArrayOf: PropTypes.arrayOf(PropTypes.number), // 可以指定一个对象由某一类型的值组成 optionalObjectOf: PropTypes.objectOf(PropTypes.number), // 可以指定一个对象由特定的类型值组成 optionalObjectWithShape: PropTypes.shape({ color: PropTypes.string, fontSize: PropTypes.number }), // An object with warnings on extra properties optionalObjectWithStrictShape: PropTypes.exact({ name: PropTypes.string, quantity: PropTypes.number }), // 你可以在任何 PropTypes 属性后面加上 `isRequired` ,确保 // 这个 prop 没有被提供时,会打印警告信息。 requiredFunc: PropTypes.func.isRequired, // 任意类型的必需数据 requiredAny: PropTypes.any.isRequired, // 你可以指定一个自定义验证器。它在验证失败时应返回一个 Error 对象。 // 请不要使用 `console.warn` 或抛出异常,因为这在 `oneOfType` 中不会起作用。 customProp: function(props, propName, componentName) { if (!/matchme/.test(props[propName])) { return new Error( 'Invalid prop `' + propName + '` supplied to' + ' `' + componentName + '`. Validation failed.' ); } }, // 你也可以提供一个自定义的 `arrayOf` 或 `objectOf` 验证器。 // 它应该在验证失败时返回一个 Error 对象。 // 验证器将验证数组或对象中的每个值。验证器的前两个参数 // 第一个是数组或对象本身 // 第二个是他们当前的键。 customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) { if (!/matchme/.test(propValue[key])) { return new Error( 'Invalid prop `' + propFullName + '` supplied to' + ' `' + componentName + '`. Validation failed.' ); } }) };
如果既要验证数据类型也要自定义验证器,只能将验证数据类型写到自定义验证器内。
import React, { Component } from 'react'; import PropTypes, { number } from 'prop-types' const Header = (props) => { return ( <div>欢迎来到 { props.title } 的世界</div> ) } Header.defaultProps = { title: 'React' } Header.propTypes = { // title: PropTypes.string title: PropTypes.string.isRequired // 代表该属性是必须传递的 } function Content (props) { return ( <div> { props.content }的核心库只关注于视图层 </div> ) } Content.defaultProps = { content: 'React.js' } Content.propTypes = { // content: PropTypes.number // content: PropTypes.string content: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]).isRequired } class Footer extends Component { // static defaultProps = { // 类组件,可以通过类的静态属性设置默认值 // name: 'React.js' // } render () { return ( <div> 学 { this.props.name } 实际上就是学习js </div> ) } } Footer.defaultProps = { name: 'React' } Footer.propTypes = { // name: PropTypes.string name: function (props, propName, componentName) { console.log(props) // { name: 'React' } console.log(propName) // name console.log(componentName) // Footer if (typeof props[propName] !== 'number') { // 验证数据类型 return new Error('请传递number类型的数据,现在是' + typeof props[propName] + '类型') } // 如果需要指定一个自定义验证器。它在验证失败时应返回一个 Error 对象 if (props[propName].length < 6) { return new Error(`传递的${propName}的长度必须大于等于6`) } } } class App extends Component { render() { return ( <div> <Header ></Header> <Content ></Content> <Footer ></Footer> </div> ); } } export default App;
复习:判断是否是数组的方法。
其实React完全可以拥抱TypeScript来实现更强大的类型验证,包括但不限于属性的验证,选择TS后,将不需要使用PropTypes来进行验证。
函数式组件也能使用props,原因在于函数式组件可以接收参数。
2.基本使用
-
内部读取某个属性值
this.props.name
-
对props中的属性值进行类型限制和必要性限制
使用prop-types库进行限制
Person.propTypes = { name:PropTypes.string.isRequired, age:PropTypes.number }
-
扩展属性:将对象的所有属性通过props传递
<Person {...person}/>
-
默认属性值
Person.defaultProps = { age:18, sex:'男' }
-
组件类的构造函数
constructor(props){ super(props) console.log(props)//打印所有属性 }
3.refs
Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素。
何时使用 Refs
-
管理焦点,文本选择或媒体播放。
-
触发强制动画。
-
集成第三方 DOM 库。
创建 Refs
Refs 是使用
React.createRef()
创建的,并通过ref
属性附加到 React 元素。在构造组件时,通常将 Refs 分配给实例属性,以便可以在整个组件中引用它们。class MyComponent extends React.Component { constructor(props) { super(props); this.myRef = React.createRef(); } render() { return <div ref={this.myRef} />; } }
访问 Refs
当 ref 被传递给
render
中的元素时,对该节点的引用可以在 ref 的current
属性中被访问。const node = this.myRef.current;
ref 的值根据节点的类型而有所不同:
-
当
ref
属性用于 HTML 元素时,构造函数中使用React.createRef()
创建的ref
接收底层 DOM 元素作为其current
属性。 -
当
ref
属性用于自定义 class 组件时,ref
对象接收组件的挂载实例作为其current
属性。 -
你不能在函数组件上使用
ref
属性,因为他们没有实例。
以下例子说明了这些差异。
为 DOM 元素添加 ref
以下代码使用
ref
去存储 DOM 节点的引用:class CustomTextInput extends React.Component { constructor(props) { super(props); // 创建一个 ref 来存储 textInput 的 DOM 元素 this.textInput = React.createRef(); this.focusTextInput = this.focusTextInput.bind(this); } focusTextInput() { // 直接使用原生 API 使 text 输入框获得焦点 // 注意:我们通过 "current" 来访问 DOM 节点 this.textInput.current.focus(); } render() { // 告诉 React 我们想把 <input> ref 关联到 // 构造器里创建的 `textInput` 上 return ( <div> <input type="text" ref={this.textInput} /> <input type="button" value="Focus the text input" onClick={this.focusTextInput} /> </div> ); } }
React 会在组件挂载时给
current
属性传入 DOM 元素,并在组件卸载时传入null
值。ref
会在componentDidMount
或componentDidUpdate
生命周期钩子触发前更新。为 class 组件添加 Ref
如果我们想包装上面的
CustomTextInput
,来模拟它挂载之后立即被点击的操作,我们可以使用 ref 来获取这个自定义的 input 组件并手动调用它的focusTextInput
方法:class AutoFocusTextInput extends React.Component { constructor(props) { super(props); this.textInput = React.createRef(); } componentDidMount() { this.textInput.current.focusTextInput(); } render() { return ( <CustomTextInput ref={this.textInput} /> ); } }
请注意,这仅在
CustomTextInput
声明为 class 时才有效:class CustomTextInput extends React.Component { // ... }
Refs 与函数组件
默认情况下,你不能在函数组件上使用
ref
属性,因为它们没有实例:function MyFunctionComponent() { return <input />; } class Parent extends React.Component { constructor(props) { super(props); this.textInput = React.createRef(); } render() { // This will *not* work! return ( <MyFunctionComponent ref={this.textInput} /> ); } }
如果要在函数组件中使用
ref
,你可以使用 forwardRef(可与 useImperativeHandle 结合使用),或者可以将该组件转化为 class 组件。不管怎样,你可以在函数组件内部使用
ref
属性,只要它指向一个 DOM 元素或 class 组件:function CustomTextInput(props) { // 这里必须声明 textInput,这样 ref 才可以引用它 const textInput = useRef(null); function handleClick() { textInput.current.focus(); } return ( <div> <input type="text" ref={textInput} /> <input type="button" value="Focus the text input" onClick={handleClick} /> </div> ); }
-