React
React是一个用于构建用户界面的JavaScript库,即操作DOM将数据渲染到HTML视图中
为何使用React?
不管使用原生JS还是Jquery操作DOM,浏览器都会进行大量的重排重绘。原生JS没有组件化,代码复用率低。
React特点:
- 组件化,声明式编码(相比于命令式编码,一步一步命令去执行,声明式只需使用对应语法,react会来执行对应操作)
- React Native可以进行移动端开发。
- 虚拟DOM+Diffing算法,减少与真实DOM的交互
依赖包
- babel,除了将ES6转为ES5,还可以将JSX转为JS
- react,react的核心宝
- react-dom,扩展包用于react操控DOM
react必须在react-dom之前引入
虚拟DOM创建的两种方式
JS创建——creatElement
- 安装
npm install react react-dom
- 引入js文件
<script src="./node_modules/react/umd/react.development.js"></script>
<script src="./node_modules/react-dom/umd/react-dom.development.js"></script>
- 创建react元素
// 参数:元素名称,元素属性,元素的子节点(第三个及其以后的参数)
const h1 = React.createElement('h1', { title: 'h1 tag' }, 'h1 tag', React.createElement('h2', null, 'h2 tag'))
- 渲染react元素
ReactDOM.render()
<div id="root"></div>
<script>
// 参数:要渲染的react元素,挂载的位置
ReactDOM.render(h1, document.getElementById('root'))
</script>
JSX
<script src="./node_modules/react/umd/react.development.js"></script>
<script src="./node_modules/react-dom/umd/react-dom.development.js"></script>
<script src="./dependency/babel.min.js"></script>
<script type="text/babel">
// 前提已经引入react, react-dom, babel
// 1.创建虚拟DOM
const VDOM = (
<h1 id="title">
<span>Hello,React</span>
</h1>
)
// 2.将虚拟DOM渲染到真实DOM上
ReactDOM.render(VDOM, document.getElementById('root'))
</script>
使用JSX可以使得创建虚拟DOM更加简单。经过Babel编译,JSX语法变成JS的语法。
JSX语法规则:
- 定义虚拟DOM不要使用引号,为了阅读可以使用括号()
- 标签中混入JS表达式使用花括号
{}
(注意里面是JS表达式,表达式不同于语句,会产生一个值,可以放在任何需要该值的地方,比如console.log该值) - 样式的类名指定不要用class,而用className
- 内联样式,要用
{{key:value}}
形式,最外层花括号表示要用JS表达式,最里层花括号表示一个对象,key为驼峰式。 - 虚拟DOM必须只有一个跟标签
- 标签首字母若小写开头,则转为HTML中同名标签;若大写开头,渲染对应的自定义组件。
组件
函数式组件
// 1.创建函数式组件(自定义组件要大写)
function Demo() {
return <h2>我是函数式组件</h2>
}
// 2.渲染组件到页面(调用该函数,将返回的虚拟DOM转为真实DOM,渲染到页面中)
ReactDOM.render(<Demo />, document.getElementById('root'))
类式组件
// 1.创建类式组件
class MyComponent extends React.Component {
// render函数放在MyComponent的原型对象上
render() {
return <h2>我是类式组件</h2>
}
}
// 2.渲染组件到页面(new该类的实例即MyComponent类,并调用该实例原型对象的render方法,获得返回的虚拟DOM,将返回的虚拟DOM转为真实DOM,渲染到页面中)
ReactDOM.render(<MyComponent/>, document.getElementById('root2'))
简单组件&&复杂组件:有状态state的就是复杂组件。
state状态
维护组件内部的状态数据,组件的状态数据改变时,组件会再次调用 render()
方法重新渲染。
添加state创建一个有状态的组件:
class Weather extends React.Component{
constructor(props){
super(props)
this.state = {isHot: false}
}
render() {
return <h2>今天天气很{this.state.isHot ? '炎热' : '凉爽'}</h2>
}
}
ReactDOM.render(<Weather/>, document.getElementById('root'))
【注】:
- 更改state状态不能直接修改,通过
setState({})
来修改
props
传入props小案例:
class People extends React.Component {
render() {
const { name, age, sex } = this.props
return (
<ul>
<li>姓名:{name}</li>
<li>年龄:{age}</li>
<li>性别:{sex}</li>
</ul>
)
}
}
ReactDOM.render(<People name="randy" age="16" sex="male" />, document.getElementById("root"))
// 函数式组件也可以使用props
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
对props进行类型检查
除了使用Flow,TypeScript进行检查,内置也可以完成,需要使用库import PropTypes from 'prop-types'
People.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.string,
sex: PropTypes.string
}
People.defaultProps = {
age: '18',
sex: '不男不女'
}
refs
字符串形式
字符串形式的ref,可以快速获取元素节点,而不用使用document.getElement,小Demo:
(不建议使用)
class Demo extends React.Component {
showData = () => {
const input = this.refs.input1
alert(input.value)
}
showText = () => {
const input = this.refs.input2
alert(input.value)
}
render() {
return (
<div>
<input ref="input1" type="text" placeholder="点击按钮提示数据" />
<button onClick={this.showData}>点击提示左边数据</button>
<input ref="input2" onBlur={this.showText} type="text" placeholder="失去焦点提示数据" />
</div>
)
}
}
回调形式
回调形式的ref,上面的改写:
class Demo extends React.Component {
showData = () => {
alert(this.input1.value)
}
showText = () => {
alert(this.input2.value)
}
render() {
return (
<div>
{// 也可以将回调形式写成类绑定的函数,原因详见官网}
<input ref={currentNode => this.input1 = currentNode} type="text" placeholder="点击按钮提示数据" />
<button onClick={this.showData}>点击提示左边数据</button>
<input ref={currentNode => this.input2 = currentNode} onBlur={this.showText} type="text" placeholder="失去焦点提示数据" />
</div>
)
}
}
createRef()
最新的写法,上面的改写:
class Demo extends React.Component {
myRef = React.createRef()
myRef2 = React.createRef()
showData = () => {
alert(this.myRef.current.value)
}
showText = () => {
alert(this.myRef2.current.value)
}
render() {
return (
<div>
<input ref={this.myRef} type="text" placeholder="点击按钮提示数据" />
<button onClick={this.showData}>点击提示左边数据</button>
<input ref={this.myRef2} onBlur={this.showText} type="text" placeholder="失去焦点提示数据" />
</div>
)
}
}
表单
有关受控组件与非受控组件,官网很详细,参考官网受控组件,非受控组件
事件处理
<!-- 传统HTML事件处理-->
<button onclick="activateLasers()">
Activate Lasers
</button>
<!-- React中事件处理-->
<button onClick={activateLasers}>
Activate Lasers
</button>
注意:
- React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
onClick={activateLasers}
这里函数后面不要加括号,如果这样,相当于把函数的返回值赋给了onClick。- React中的事件采用的是事件委托(将事件委托给最外层元素),高效
- 可以通过
event.target
获取当前DOM元素,不要过度使用ref
Demo1:点击文字,更改类式组件的state值:
class Weather extends React.Component{
constructor(props){
super(props)
this.state = {isHot: false}
// 解决changeWeather中this为undefined的情况,重新bind this生成的新函数赋值到实例对象上
this.changeWeather = this.changeWeather.bind(this)
}
render() {
return <h2 onClick={this.changeWeather}>今天天气很{this.state.isHot ? '炎热' : '凉爽'}</h2>
}
changeWeather() {
// changeWeather放在Weather的原型对象上,供实例使用
// 没有调用changeWeather,仅仅是把changeWeather方法作为onClick的回调,所以不是通过实例调用的,而是直接调用
// class中方法默认开启了严格模式,所以此处的this为undefined
console.log(this)
const isHot = this.state.isHot
this.setState({isHot: !isHot})
}
}
ReactDOM.render(<Weather/>, document.getElementById('root'))
上面内容的简写方式:
class Weather2 extends React.Component {
state = { isHot: false }
render() {
return <h2 onClick={this.changeWeather}>今天天气很{this.state.isHot ? '炎热' : '凉爽'}</h2>
}
changeWeather = () => {
const isHot = this.state.isHot
this.setState({ isHot: !isHot })
}
}
ReactDOM.render(<Weather2 />, document.getElementById('root2'))
向事件处理函数传递参数:
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
生命周期
// 组件render之后,被挂载到页面上调用
componentDidMount() {
}
// 组件将被卸载后调用
componentWillUnmount() {
}
Diff算法
根据新数据生成新的虚拟DOM,为什么key不要用index?新的DOM与旧的DOM进行比较时,依赖的是key,这样可能会造成效率问题,比如在数组开头插入,甚至有可能产生错误。
脚手架
npx create-react-app my-app
cd my-app
npm start
npx命令使得无需安装脚手架包即可进行使用