- react介绍
react是用来构建用户界面的javascript UI库,它只关注视图层。
2. react特点
- 声明式编码
- 组件化编码
- 高效渲染
- 可以使用reactnative编写移动端开发应用
这里需要注意声明式编码与原来命令式编码有啥区别:
通俗来讲命令式编码做任何一件事都需要亲力亲为才能实现,反而声明式编码只需要告诉我需要什么,至于怎么做不需要自己亲自实现,都是交给框架来做。从这可以看出声明式编码让开发者更加高效,代码更加简洁。
react高效原因?
采用虚拟DOM,不是直接去操作dom,使用dom diffing算法,计算出需要更新dom, 从而减少页面绘制
3. react开发相关核心库
- react.js (react核心库)
- react-dom (提供react操作dom扩展库)
- babel.js (将jsx语法转换成标准的javascript代码)
4. 创建虚拟dom两种方式
4.1 采用jsx语法来创建
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>jsx创建虚拟DOM</title>
</head>
<body>
<!-- 准备好一个“容器” -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转为js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel" > /* 此处一定要写babel */
//1.创建虚拟DOM
const VDOM = ( /* 此处一定不要写引号,因为不是字符串 */
<h1 id="title">
<span>Hello,React</span>
</h1>
)
//2.渲染虚拟DOM到页面
ReactDOM.render(VDOM,document.getElementById('test'))
</script>
</body>
</html>
4.2 采用js创建(这种不推荐)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用js创建虚拟DOM</title>
</head>
<body>
<!-- 准备好一个“容器” -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" >
//1.创建虚拟DOM
const VDOM = React.createElement('h1',{id:'title'},React.createElement('span',{},'Hello,React'))
//2.渲染虚拟DOM到页面
ReactDOM.render(VDOM,document.getElementById('test'))
</script>
</body>
</html>
虚拟DOM与真实DOM区别:
- 虚拟dom本质就是一个普通一般对象
- 虚拟dom是由react来维护的,身上的属性相比真实dom要少很多,因此虚拟dom比真实dom更加`轻`
- 虚拟dom最终都会由react转换成真实dom,最终呈现在页面上
5. jsx语法规则
jsx全称 Javascript xml, react定义它类似为xml一种js扩展,本质就是React.createElement()语法糖.
jsx语法规则:
- 定义虚拟dom时不能用引号
- 在使用类class时,要用className
- 在使用内联style时,要用{{key:value}}
- 在虚拟dom中使用js表达式时,要使用{}
- 只能有一个根标签
- 标签必须要闭合
- 标签首字母:1. 如果标签首字母是小写,那么react会将该标签转换成html标签去寻找,如果未找到就会报错。2. 如果标签首字母是大写,那么会去渲染该组件,如果未找到就会报错。
6. 模块与组件,模块化与组件化理解
6.1 模块
模块:向外提供一个特定功能的js程序,一般就是js文件
为啥需要拆分模块:随着业务逻辑复杂,代码量越来越多
作用:复用js代码,简化js编码,提高js运行效率
6.2 组件
组件:实现某个局部功能的代码和资源集合(html/css/js/png)
为啥需要组件: 由于页面功能复杂性,需要拆分成多个组件
作用:提高代码复用率,高效开发
6.3 模块化
通俗来讲就是将一个应用拆分成多个模块来编写就称为模块化
6.4 组件化
通俗来讲就是将某个功能拆分成多个组件来编写就称为组件化
不管是组件化还是模块化都是为了提高代码复用率、降低耦合、简化代码编写、提高开发效率
7. 创建组件两种方式
- 函数式组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>1_函数式组件</title>
</head>
<body>
<!-- 准备好一个“容器” -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转为js -->
<script type="text/javascript" src="../js/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>
</body>
</html>
ReactDOM.render()这个过程做了些啥? React首先会去解析这个组件,发现这个组件是函数定义的,那么会调用这个函数,将返回虚拟DOM转换成真实dom呈现在页面上.
- 类式组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>2_类式组件</title>
</head>
<body>
<!-- 准备好一个“容器” -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转为js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
//1.创建类式组件
class MyComponent extends React.Component {
render(){
//render是放在哪里的?—— MyComponent的原型对象上,供实例使用。
//render中的this是谁?—— MyComponent的实例对象 <=> MyComponent组件实例对象。
console.log('render中的this:',this);
return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
}
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
</script>
</body>
</html>
ReactDOM.render()这个过程做了啥? react解析组件标签,发现组件是类来定义的,那么会通过new方式来创建组件实例,然后在根据实例调用原型上render()方法,将返回虚拟dom转换成真实dom呈现在页面上。
这里需要注意的是:组件名需要首字母大写,组件只有一个根标签,标签需要闭合。
8.组件三大核心属性
8.1 state
理解: state是组件最重要属性,它是一个对象(可以包含多个key,value 键值对),组件被称为`状态机`,通过更新组件的state来对应界面更新(render()方法被调用,重新渲染组件)。
作用:更新组件的UI
这里对state使用需要注意的地方:
- state不能直接更新后修改,只能调用setState()方法来更新state,setState()方法更新state并不是完全替换之前state的值,而是合并。
- 函数组件没有state,应此无法在函数组件使用state这个属性,当然你要想在函数使用state可以采用react提供hook来完(useState()),这在后面会详细说。
- setState()方法可以接收2种参数,一种是对象,另外一种是一个回调函数。传递回调函数就是为了异步更新。应为我们state和props的值都有可能是异步
更新的值。
(1). setState(stateChange, [callback])------对象式的setState
1.stateChange为状态改变对象(该对象可以体现出状态的更改)
2.callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用
(2). setState(updater, [callback])------函数式的setState
1.updater为返回stateChange对象的函数。
2.updater可以接收到state和props。
4.callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。
总结:
1.对象式的setState是函数式的setState的简写方式(语法糖)
2.使用原则:
(1).如果新状态不依赖于原状态 ===> 使用对象方式
(2).如果新状态依赖于原状态 ===> 使用函数方式
(3).如果需要在setState()执行后获取最新的状态数据,
要在第二个callback函数中读取
8.2 props
理解:每个组件对象上都有一个props属性(全称properties),组件对象上所有属性都会保存到props上
作用:用来组件之间传递数据,组件之间传递方向是由外到内(父亲->孩子),并且数据流是单向的,因此我们不应该在内部去修改props,这样会影响外部数据,导致数据发生混乱,不容易理解。
谈到props这里需要提一下相关一个库props-type,这个库是用来限制props传递数据类型。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>对props进行限制</title>
</head>
<body>
<!-- 准备好一个“容器” -->
<div id="test1"></div>
<div id="test2"></div>
<div id="test3"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转为js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- 引入prop-types,用于对组件标签属性进行限制 -->
<script type="text/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
//创建组件
class Person extends React.Component{
constructor(props){
//构造器是否接收props,是否传递给super,取决于:是否希望在构造器中通过this访问props
// console.log(props);
super(props)
console.log('constructor',this.props);
}
//对标签属性进行类型、必要性的限制
static propTypes = {
name:PropTypes.string.isRequired, //限制name必传,且为字符串
sex:PropTypes.string,//限制sex为字符串
age:PropTypes.number,//限制age为数值
}
//指定默认标签属性值
static defaultProps = {
sex:'男',//sex默认值为男
age:18 //age默认值为18
}
render(){
// console.log(this);
const {name,age,sex} = this.props
//props是只读的
//this.props.name = 'jack' //此行代码会报错,因为props是只读的
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age+1}</li>
</ul>
)
}
}
//渲染组件到页面
ReactDOM.render(<Person name="jerry"/>,document.getElementById('test1'))
</script>
</body>
</html>
函数组件是如何使用props:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>对props进行限制</title>
</head>
<body>
<!-- 准备好一个“容器” -->
<div id="test1"></div>
<div id="test2"></div>
<div id="test3"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转为js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- 引入prop-types,用于对组件标签属性进行限制 -->
<script type="text/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
//创建组件
function Person (props){
const {name,age,sex} = props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age}</li>
</ul>
)
}
Person.propTypes = {
name:PropTypes.string.isRequired, //限制name必传,且为字符串
sex:PropTypes.string,//限制sex为字符串
age:PropTypes.number,//限制age为数值
}
//指定默认标签属性值
Person.defaultProps = {
sex:'男',//sex默认值为男
age:18 //age默认值为18
}
//渲染组件到页面
ReactDOM.render(<Person name="jerry"/>,document.getElementById('test1'))
</script>
</body>
</html>
8.3 ref
理解:组件内标签可以通过ref来标示自己
作用:在某些情况下,需要在典型数据流之外强制修改子组件,子组件可能是组件实例,也可能是一个dom元素
我们应该避免使用refs任何可以用声明式完成的功能,常见使用场景(管理焦点、使用第三方的dom、触发强制动画)
使用方式有三种:
8.3.1 通过React.createRef()来创建,并且将值附属到组件实例上。这样就可以随意使用了
myRef = React.createRef();
<input ref={this.myRef}/>
使用方式:
this.myRef.current来获取当前的值,current属性的值可能是一个dom元素,也可能是一个组件实例。这得看ref作用于元素类型(dom元素、组件实例)
8.3.2 通过回调refs方式来创建
不同于createRef()传递refs属性,它会传递一个函数。这个函数接收React组件实例或者一个DOM元素,以便他们在其他地方存取与访问。
//1.采用内联箭头函数
<button onClick={c=>this.inputRef=c}>测试</button>
//2.第二种方式采用class field方式
inputRef = (c) => {
this.inputRefs=c
}
<button onClick={this.inputRef}>测试</button>
两者区别在于:第一种方式每次渲染都会创建一个新的函数实例,会被调用2次。第一次去把当前值设置为null,然后第二次设置新的值。第二种方式我们使用实例属性,只会创建一次,可以直接更新当前的值。
这里做个总结:当组件挂载,refs的值会被传入DOM元素,当组件卸载,refs的值被设置为null,在componentDidMount或componentDidUpdate生命周期钩子触发之前更新。函数组件没有refs,应此无法在函数组件使用refs,当然要想在函数组件使用refs,react也提供hook解决这个问题(useRef()方法),后面详细说 。