这是我参与[第五届青训营]伴学笔记创作活动的第七天
- react- 用于构建用户界面的javaScript 库
发送请求获取数据 处理数据(过滤、整理) 操作dom 呈现页面 react 是一个将数据渲染微HTML视图的开源 JavaScript库
-
facebook开发,且开源
-
原生JavaScript
原生javaScript操作DOM繁琐、效率低(DOM-APL 操作 UI)。 使用javascript直接操作DOM,浏览器会进行大量的重绘重排。 原生JavaScript没有组件化编码方案,代码复用率低。 -
react 特点
采用组件化模式、声明式编码,提高开发效率及组件复用率。 React Native 中可以使用 React语法进行移动端开发 使用虚拟DOM + 优秀的Diffing算法,尽量减少与真实DOM的交互
1、hello react react 核心库和react-dom的引入要放在前面
hello_react<script type="text/babel"> /* 此处一定为babel*/
// 1. 创建虚拟DOM
const VDOM = <h1>Hello,React</h1> /* 不用写引号,因为不是字符串,是jsx*/
// 2. 渲染虚拟DOM到页面
ReactDOM.render(VDOM, document.getElementById('test'))
</script>
- 创建虚拟DOM JSX创建虚拟DOM更方便,相当于语法糖
// 1. 创建虚拟DOM const VDOM = (
Hello,React
) /* 不用写引号,因为不是字符串,是jsx*/ JS 创建虚拟DON// 1. 创建虚拟DOM const VDOM = React.createElement('h1', {id: 'title'}, React.createElement('span',{}, 'hello, React')) 3. 虚拟DOM和真实DOM 虚拟DOM:
1、本质上是一个Object对象,一般对象
2、虚拟DOM比较 ”轻",真实DOM比较 “重”, 因为虚拟DOM是React内部在用,无需真实DOM这么多的属性
3、虚拟DOM最终会被React转化成真实DOM,呈现在页面上。
4. jsx 语法规则 --XML早期用于存储和传输的数据
-- JSON 也是用于存储和传输的数据,js内置对象
1、定义虚拟DOM时,不要写引号
2、标签中混入JS表达式时要用 {}
3、样式的类名指定不用 class,要用className.
4、内联样式,要用style = {{key: value}} 的形式去写
5、只有一个跟标签
6、标签必须闭合
7、标签首字母
(1)若小写字母开头,则将该标签转为 html同名元素,若html中无该标签,则报错
(2)若大写字母开头,react就去喧嚷对应的组件,若组件没有定义,则报错
4_JSX语法规则
.title {
background: orange;
}
<script type="text/babel"> /* 此处一定为babel*/
// 1. 创建虚拟DOM
const VDOM = (
<h1 id="title" className="title">
<span style= {{color: 'white',fontSize: '20px'}}>Hello,React</span>
</h1>
) /* 不用写引号,因为不是字符串,是jsx*/
// 2. 渲染虚拟DOM到页面
ReactDOM.render(VDOM, document.getElementById('test'))
</script>
- 函数式组件
<!-- 引入react核心库 -->
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转为js -->
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<script type="text/babel">
//1、创建函数式组件
function MyComponent() {
console.log(this); // 此处的this 是undefined,因为babel编译之后开启了严格模式
return <h2>函数定义的组件(适用于【简单组件】定义)</h2>
}
// 2、渲染组件到页面
ReactDOM.render(<MyComponent/>, document.getElementById('test'))
</script>
* 执行了ReactDOM.render(<MyComponent/>...之后,发生了什么)
* 1、React解析组件标签,找到了MyComponent组件
* 2、发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实的DOM,随后呈现在页面上。
注:严格模式下,禁止 this 指向 window
官方例子:
function Welcome(props) { return
Hello, {props.name}
; }const root = ReactDOM.createRoot(document.getElementById('root')); const element = ; root.render(element) 这个例子中发生了什么:
我们调用 root.render() 函数,并传入 作为参数。 React 调用 Welcome 组件,并将 {name: 'Sara'} 作为 props 传入。 Welcome 组件将
Hello, Sara
元素作为返回值。 React DOM 将 DOM 高效地更新为Hello, Sara
。 6. 类的基本知识 1. 类中的构造器不是必须写的,要对实例进行一些初始化的操作,如添加指定属性时才写 2. 如果A类继承了B类,且A类型写了构造器,那么A类构造器中的super是必须要调用的
3. 类中所定义的方法,都是放在了类的原型对象上,供实列使用
07_类基本知识
4、 类中可以直接写赋值语句, 如下的含义 会直接往Car实例对象添加一个属性,名为 a, 值为 1
class Car { // 类中可以直接写复制语句, // 会直接往Car实例对象添加一个属性,名为 a, 值为 1 a = 1 } const c1 = new Car() console.log(c1); 7. 类式组件
执行了ReactDOM.render(...之后,发生了什么) 1、React解析组件标签,找到了MyComponent组件 2、发现组件是使用类定义的,随后new 出来该类的实例,并通过该实例调用到原型上的render方法。 3、将render 返回的虚拟DOM转为真实DOM。随后呈现在页面上
- 组件中的state state 的值是对象(key-value的组合) 组件中render方法中的this为组件实例对象 组件自定义函数中的this 为 undefined解决办法: bind 方法 箭头函数 状态数据不能直接修改更新,必须要用内置APL--- setState
<!-- 引入react核心库 -->
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转为js -->
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<script type="text/babel">
// 1、创建类式组件
class Weather extends React.Component {
// 构造器 调用 1次
constructor(props){
super(props)
// 初始化状态
this.state = {
isHot: true,
wind: '微风'
}
// bind 可以做的事:1、生成新的函数,2、改了 函数里面的 this
this.demo = this.changeWeather.bind(this)
}
// render调用 --- 1 + n 次, 1 是初始的那次,n 是状态更新的次数
// render 调用,页面更新
render() {
return <h2 onClick={ this.demo }>今天天气很{this.state.isHot ? '炎热' : '凉爽'}, {this.state.wind}</h2>
}
changeWeather() {
// changeWeather 放在了原型对象上,供实例使用
// 由于changeWeather 是作为onClick的回调,所以不是通过实例调用的,是直接调用的,
// 类型的方法默认开启了局部的严格模式,所以 changeWeather中的this 为 undefined
const isHot = this.state.isHot
// this.state.isHot = !isHot // 状态(state)里面的值 不能直接更改,react不认可的
// 要借助一个内置的API去更改
// 注意:状态(state) 必须通过setState进行更新,且更新事一种合并,不是替换
this.setState({isHot: !isHot})
}
}
// 2、渲染组件
ReactDOM.render(<Weather/>, document.getElementById('test'))
</script>
(1) 元素绑定事件
注意写法:onClick = { this.demo },不是 onClick = { this.demo() }, 是函数的回调,不是函数的执行。
render() { return <h2 onClick={ this.demo }>今天天气很{this.state.isHot ? '炎热' : '凉爽'}, {this.state.wind} } (2) this的指向问题
bind 可以解决 1、生成新的 函数 2、更改函数中的 this
(3) 状态(state) 必须通过setState进行更新,且更新事一种合并,不是替换
(4)render 函数调用,页面更行; 状态更新多少次,render函数就会调用多少次
9. state 精简 1. 不再写构造器
2. 自定义方法-----要用 赋值语句的形式 + 箭头函数
注: 箭头函数没有 this,它会直接找外层的 this
- props 在类组件中 直接 this.props就可以拿到 组件传进来的值对象了
ES6 中三点运算符,可以展开数组,但是不可以展开 Object
React 的组件的props传值,可以只用三点运算符 展开 Object,仅限于 组件的props的传值
props是只读,不可以修改
构造器是否接受 props,是否 传递给super,取决于:是否希望在构造器中通过this访问 props
// 1、创建类式组件 class Person extends React.Component { // 初始化状态 state = {} render() { const {name, sex, age} = this.props return (
- name: {name}
- sex: {sex}
- age: {age}
const p = { name: 'tom', sex: 'man', age: 18} // 2、渲染组件 ReactDOM.render(<Person {...p}/>, document.getElementById('test'))
React内置了一些 方法 对 props传入的属性进行 检查,15.5版本之后React.PropTypes 已移入另一个包中了
propTypes 使用方式:
// react 15.6版本之后 Person.propTypes = { name: PropTypes.string.isRequired, // name 必填· 字符串 sex: PropTypes.string, // sex 字符串 age: PropTypes.number, // age 数字 speak: PropTypes.func, // speak 函数 } 设置默认值,defaultProps
// 指定默认标签属性值 Person.defaultProps = { sex: 'man', // sex 默认值为 man age: 18, } 12. props简写 把 props的限制都写在类里面,static 关键字定义
12_props的简写<!-- 引入react核心库 -->
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转为js -->
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<!-- 引入prop-types -->
<script src="https://unpkg.com/prop-types@15.6/prop-types.js"></script>
<script type="text/babel">
// 1、创建类式组件
class Person extends React.Component {
// 对标签属性进行类型、必要性的限制
static propTypes = {
name: PropTypes.string.isRequired,
sex: PropTypes.string,
age: PropTypes.number,
}
// 指定默认标签属性值
static defaultProps = {
sex: 'man', // sex 默认值为 man
age: 18,
}
// 初始化状态
state = {}
render() {
const {name, sex, age} = this.props
return (
<ul>
<li>name: {name}</li>
<li>sex: {sex}</li>
<li>age: {age}</li>
</ul>
)
}
}
const p = { name: 'tom', sex: 'man', age: 20}
// 2、渲染组件
ReactDOM.render(<Person {...p}/>, document.getElementById('test'))
</script>
-
函数组件使用 function Person(props) { const {name, sex, age} = props return (
- name: {name}
- sex: {sex}
- age: {age}
-
字符串形式的 ref 官方不建议使用,以后会弃用
注意是 ref ,不是 refs
<script type="text/babel">
// 1、创建组件
class Demo extends React.Component{
showMsg = () => {
console.log(this.refs.input1.value);
}
showMsg2 = () => {
console.log(this.refs.input2.value);
}
render() {
return (
<div>
<input ref="input1" type="text" placeholder="请输入"/>
<button onClick={this.showMsg}>点击</button>
<input ref="input2" onBlur={ this.showMsg2} type="text" placeholder="请输入"/>
</div>
)
}
}
ReactDOM.render(<Demo/>, document.getElementById('test'))
15. 回调形式的 ref <input ref={c=> this.input2 = c} onBlur={ this.showMsg2} type="text" placeholder="请输入"/> ) } } ReactDOM.render(, document.getElementById('test'))
- React.createRef() 官方最推荐的写法,createRef是只能存一个值,后面的值会直接覆盖前面定义的值,键值对的形式存储
input1 = React.createRef()
获取值: this.input1.current.value
-
事件处理 通过onXxx属性指定事件处理函数(注意大小写) React使用的是自定义(合成)事件,而不是使用的原生的DOM事件 --- 为了更好的兼容性 React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)--为了高效 通过event.target得到发生事件的DOM元素对象--- 不要过渡使用re
-
非受控组件 非受控组件相对于 受控组件,属性没有在 state状态里面维护的都是 非受控组件,现造现取
-
受控组件 受控组件,数据都放在 state状态里面维护,相当于 vue的数据双向绑定
-
高阶函数 高阶函数: 如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数 若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数 若A函数,调用的返回值仍然是一个函数,那么A就可以称之为高阶函数 常见的高阶函数: Promise、setTimeout、 函数的柯里化: 通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。
疑问: this.setState({[data]: event.target.value}) 中的 [data]怎么就可以读变量了呢
在数组里面是 array[a] = 223 就可以赋值数据, 同理
-
生命周期(旧)
-
初始化阶段:由ReactDOM.render() 触发 -----初次渲染
-
constructor()
-
componentWillMount()
-
render()
-
componentDidMount() ------------ 常用,
一般在这做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
-
-
更新阶段:由组件内部this.setState() 或父组件render触发
1. shouldComponentUpdate() 2. componentWillUpdate() 3. render() ------------------- 必须使用的 4. componentDidUpdate() -
卸载组件:ReactDOM.unmountComponentAtNode()触发
1. componentWillUnmount() ----------- 常用 一般在这做一些收尾的事,例如:关闭定时器、取消订阅消息
componentWillReceiveProps 这个钩子是 父组件改变的时候,子组件会调这个勾子,
注意:初次渲染是不会调这个勾子的
-
生命周期(新) 新的生命周期 对于 旧的生命周期 ,废弃(即将废弃)了 三个 生命钩子: ;新提出了两个生命钩子:getDerivedStateFromProps、getSnapshotBeforeUpdate
-
初始化阶段: 由ReactDOM.render()触发 ---------------初次渲染
1. constructor() 2. getDerivedStateFromProps 3. render() 4. componentDidMount() ------------ 常用, 一般在这做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息 -
更新阶段: 由组件内部this.setState()或父组件重新render触发
1. getDerivedStateFromProps 2. shouldComponentUpdate() 3. render() 4. getSnapshotBeforeUpdate() 5. componentDidUpdate() -
卸载组件:由ReactDOM.unmountComponentAtNode()触发
1. componentWilUnmount() ----------- 常用 一般在这做一些收尾的事,例如:关闭定时器、取消订阅消息
重要的钩子
1. render: 初始化渲染或更新渲染调用
2. componentDidMount: 开启监听,发送ajax请求
3. componentWilUnmount: 做一些收尾工作,如:清理定时器
即将废弃的钩子
1. componentWillMount
2. componentWillReceiveProps
3. componentWillUpdate
23. DOM的 diffing算法 逐层对比,最小粒度是 标签
经典面试题
1). react/vue 中key有什么作用?(key的内部原理是什么?)
2). 为什么遍历列表是,key最好不要用index?
-
虚拟DOM中key的作用
1) 简单的说:key是虚拟DOM对象的标识,在更新显示时key起到及其重要的作用 2)详细说:当状态中的数据发生变化时,react会根据 【新数据】生成 【新的虚拟DOM】,随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下: a. 旧虚拟DOM中找到了与新虚拟DOM相同的key 1. 若虚拟DOM中内容没有变,直接使用之前的真实DOM 2. 若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM b. 旧虚拟DOM中未找到与新虚拟DOM相同的key 根据数据创建新的真实的DOM,随后渲染到页面 -
用index作为key可能会引发的问题:
1. 若对数据进行:逆序添加、逆序删除等破环顺序操作:会产生没有必要的真实DOM更新 =》界面效果没有问题,单效率低 2. 如果结构中还包含输入类的DOM:会产生错误DOM更新 =》界面有问题 3. 注意:如果不存在对数据的逆序添加、逆序删除等破环顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。 -
开发中如何选择key?
1. 最好使用每条数据的唯一标识作为key,比如:id、手机号、身份证号、学号等唯一值 2. 如果确定只是简单的展示数据,用index也是可以的。 -
手脚架创建react项目 基于webpack 手脚架搭建
1、 react 提供用于创建react项目的脚手架:create-react-app
2、项目的整体技术架构:react+webpack+es6_eslint
3、使用脚手架开发的项目:模块化、组件化、工程化
创建项目并启动
1、全局安装:npm install -g create-react-app
2、切换目录,使用:create-react-app react_demo2
3、进入项目文件夹:cd react_demo2
4、启动项目:npm start
目录
-
功能界面的组件化编码流程 SPA (单页面应用)
-
拆分组件:拆分界面,抽取组件
-
实现静态组件:使用组件实现静态页面效果
-
实现动态组件:
a. 动态显示初始化数据 数据类型 数据名称 保存在哪个组件 b. 交互(从绑定事件监听开始)
-
state 放在哪个组件:
----某个组件组件使用: 放在其自身的state中 ---某些组件使用:放在他们共同的父组件state中,(状态提升) -
父子组件通信
1. 父组件 给 子组件传递数据: 通过props传递 2. 子组件 给父组件传递数据:通过 props传递,要求 父 提前给 子 传一个函数 -
注意 defaultChecked 和 checked 的区别,类似 defaultValue 和 value 也是
-
状态在哪里,操作状态的方法就在哪里
- react项目开发,vscode 好用的插件