使用react
生成react项目
$ npm install create-react-app -g
$ create-react-app <project-name>
$ cd <project-name> && npm start
在自选文件夹中生成自己的项目
生的项目可以自动监控改动进行刷新
- eslint jslint 关闭验证,在default-setting
- public 下有一个index.html,
- src下要保证有一个index.js
- 最后会将文件打包到html中
什么是react
React 是一个用于构建用户界面
的JavaScript库
核心专注于视图,目的实现组件化开发
组件化的概念
我们可以很直观的将一个复杂的页面分割成若干个独立组件,每个组件包含自己的逻辑和样式 再将这些独立组件组合完成一个复杂的页面。 这样既减少了逻辑复杂度,又实现了代码的重用
可组合:一个组件可以和其他的组件一起使用或者可以直接嵌套在另一个组件内部
可重用:每个组件都是具有独立功能的,它可以被使用在多个场景中
可维护:每个小的组件仅仅包含自身的逻辑,更容易被理解和维护
react的两部分
默认会自动安装React,react由两部分组成,分别是:
react.js
是 React 的核心库
react-dom.js
是提供与DOM相关的功能,会在window下增加ReactDOM属性,内部比较重要的方法是render,
将react元素或者react组件插入到页面中。render方法,只会重新渲染改变的地方,性能较好
import React(首字母大写) from 'react';//必须放在顶部
import ReactDOM from 'react-dom';//只有render方法比较常用
render(ele,container);//语法使用
JSX
是一种
JS和XML
混合的语法,将组件的结构、数据甚至样式都聚合在一起定义组件,会编译成普通的Javascript。
jsx是一个语法糖 最后会通过babel进行转义 React.createElement写法
// jsx元素-> React.createElement -> 虚拟dom对象 -> render方法=>真实的DOM
render(<h1 className="red">姜,<span id="handsome">帅哥</span></h1>,document.getElementById('root'));
//console.log(<h1 className="red">姜,<span id="handsome">帅哥</span></h1>); babel 过后如下:
/*React.createElement(
"h1",
{ className: "red" },
"姜,",
React.createElement(
"span",
{ id: "handsome" },
"帅哥"
)
);*/
// 最终会转化成一个对象"虚拟dom" {type:'h1',props:{className:"red",children:[姜,{type:'span',props:{id:'handsome',children:'帅哥'}]}}
//1.先将jsx转化成react元素
//2.将react元素变成一个对象
//3.通过render方法渲染出一个对象
需要注意的是JSX并不是html,在JSX中属性不能包含关键字
,像class需要写成className,for需要写成htmlFor,并且属性名需要采用驼峰命名法
createElement
JSX其实只是一种语法糖,最终会通过babel转译成createElement语法,以下代码等价
ReactDOM.render(<div>李雷,<span>帅哥</span></div>);
ReactDOM.render(React.createElement("div",null,"李雷,",React.createElement("span",null,"帅哥")));
//我们一般使用React.createElement来创建一个虚拟dom元素。
JSX表达式的用法
1.在react中想将js当作变量引入到jsx中需要使用{}
2.在jsx中,每一个root下只有一个元素(同vue)
3.{}取值表达式 取的是有返回值的结果
4.如果多个元素想在return 后面换行我们需要加一个()当作整体返回
1) 可以放JS的执行结果
2) 如果换行需要用()包裹jsx代码
3) 可以把JSX元素当作函数的返回值
4) <{来判断是表达式还是js
JSX属性
在JSX中分为普通属性和特殊属性,像class要写成className,for要写成htmlFor
style要采用对象的方式
dangerouslyInnerHTML插入html
import React from 'react';
import {render} from 'react-dom';
// 1.普通属性和html中的一样
// 2.特殊的属性 class,for
// 3.style必须是一个对象类型
// 4.危险的插入 innerHTML xss攻击 基本上用不到
let str = '<h1>纯标签</h1>';
let styl = {backgroundColor:'red'};
render(<div>
<li className="aa"></li>
<li htmlFor="aa" style={styl}></li>
{/*等价于*/}
<li htmlFor="aa" style={{backgroundColor:'red'}}></li>// React 组件样式是一个对象,所以第一重大括号表示这是 JavaScript 语法,第二重大括号表示样式对象。
<li dangerouslySetInnerHTML={{__html:str}}></li>
</div>,window.root);
this.props.children
this.props 对象的属性与组件的属性一一对应,但是有一个例外,就是 this.props.children 属性。它表示组件的所有子节点
ReactDOM.render(
<NotesList>
<span>hello</span>
<span>world</span>
</NotesList>,
document.body
);
注意, this.props.children 的值有三种可能:如果当前组件没有子节点,它就是 undefined ;如果有一个子节点,数据类型是 object ;如果有多个子节点,数据类型就是 array 。所以,处理 this.props.children 的时候要小心。
React 提供一个工具方法 React.Children 来处理 this.props.children 。我们可以用 React.Children.map 来遍历子节点,而不用担心 this.props.children 的数据类型是 undefined 还是 object。
{
React.Children.map(this.props.children, function (child) {
return <li>{child}</li>;
})
}
组件的特点声明方式
react元素是是组件组成的基本单位
- 首字母必须大写,目的是为了和JSX元素进行区分
- 组件定义后可以像JSX元素一样进行使用
- 每个组件必须返回唯一的顶级JSX元素
- 可以通过render方法将组件渲染成真实DOM
组件并不是真实的 DOM 节点,而是存在于内存之中的一种数据结构,叫做虚拟 DOM (virtual DOM)。只有当它插入文档以后,才会变成真实的 DOM 。根据 React 的设计,所有的 DOM 变动,
都先在虚拟 DOM 上发生
,然后再将实际发生变动的部分,反映在真实 DOM上,这种算法叫做DOM diff
,它可以极大提高网页的性能表现。
组件的两种定义方式
第一种方式是函数声明
- 可以通过属性给组件传递值 props
- 函数声明没有this 没有状态
import React from 'react';
import {render} from 'react-dom';
let school1 = {name:'珠峰',age:8};
let school2 = {name:'珠峰',age:0};
function Build(props) { // "函数"(组件)的参数是属性
return <p>{props.name} {props.age}</p>
}
render(<div>
<Build name={school1.name} age={school1.age}/>
{/*将对象中的内容解构出来传递给Build组件 不用一个个取出来传递*/}
<Build {...school2} />
</div>,window.root);
第二种方式是类声明
- 类声明有状态
- 有this
- 声明周期
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
// 1.state变化可以更新视图,更改状态只有一种方式 this.setState({})调用后会更新视图
// 2.声明周期
class Clock extends Component {
constructor(){
super();
this.state = {date:new Date().toLocaleString()}
}
componentDidMount(){ //组件渲染完成,当渲染后会自动触发此函数
this.timer = setInterval(()=>{ // 箭头函数 否则this 指向的是window
this.setState({date:new Date().toLocaleString()})
},1000);
}
componentWillUnmount(){ //组件将要卸载,当组件移除时会调用
clearInterval(this.timer); //一般在这个方法中 清除定时器和绑定的事件
}
destroy=()=>{ //es7 箭头函数
// 删除某个组件
ReactDOM.unmountComponentAtNode(window.root);
}
render(){
// 给react元素绑定事件默认this是undefined,bind方式 在就是箭头函数
return <h1 onClick={this.destroy}>{this.state.date}</h1>
}
}
/*constructor -> render -> componentDidMount -> setState-> render - onClick-> unmountComponentAtNode -> componentWillUnmount -> clearInterval*/
ReactDOM.render(<Clock/>,window.root);
组件中属性和状态的区别
属性校验,默认属性
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
// 名字,年龄 年龄应该是1
//yarn add prop-types用来校验属性的
class School extends React.Component{ // 类上的属性就叫静态属性
static propTypes = { // 校验属性的类型和是否必填
age:PropTypes.number.isRequired, // 支持的类型可以参考prop-types的readme文件
};
static defaultProps = { // 先默认调用defaultProps
name:'珠峰',
age:1
}; // 默认属性
constructor(props){ //如果想在构造函数中拿到属性需要通过参数的方式
//不能在组件中更改属性 不能修改属性
super();
}
render(){
return <h1>{this.props.name} {this.props.age}</h1>
}
}
ReactDOM.render(<School name="李雷" age={9}/>,window.root);
// 组件的数据来源有两个地方
// props 外界传递过来的 默认属性,属性校验
// state 状态是自己的 属性和状态变化都会影响视图更新
状态的使用
setState方法的应用
状态操作的合并
绑定事件
给元素绑定事件,事件绑定方式
使用驼峰命名的方式
<div onClick={()=>{console.log(1)}}>11111</div>
react生命周期
import React, {Component} from 'react';
import ReactDOM, {render} from 'react-dom';
//React.PureComponent 可以做优化,浅度监控,如果两个状态没有变化不会render
class Counter extends Component {
static defaultProps = {
name: 'zf'
};
constructor() {
super();
this.state = {number: 0};
console.log('1 父组件 construcor');
}
componentWillMount() {//取本地数据 同步:采用渲染之前获取数据,只渲染一次
console.log('2.组件将要加载');
}
componentDidMount() {
console.log('4.componentDidMount');
}
handleClick = () => {
this.setState({number: this.state.number + 1})
};
//代表下一次属性,和下一次状态
shouldComponentUpdate(nextProps, nextState) {
console.log('5.组件是否应该跟新 shouldComponentUpdate');
return nextState.number % 2;
//如果此函数中返回false,那么就不会调用render方法,(只会组织渲染,并不会阻止状态的改变)
}//不要随便使用setState;会导致死循环
componentWillUpdate() {
console.log('6.组件将要更新');
}
componentDidUpdate() {
console.log('7.组件更新完成');
}
render() {
console.log('3,render');
return (
<div>
<p>{this.state.number}</p>
{this.state.number > 3 ? null : <ChildCounter n={this.state.number}/>}
<button onClick={this.handleClick}>+</button>
</div>
)
}
}
class ChildCounter extends Component {
componentWillUnmount() {
console.log('组件即将卸载 componentWillUnmount');
}
componentWillMount() {
console.log(' child componentWillMount');
}
render() {
console.log('child render');
return (<div>
{this.props.n}
</div>)
}
componentDidMount() {
console.log('child componentDidMount');
}
componentWillReceiveProps(newProps) {//第一次不会执行,之后属性更新时才会执行
console.log('child componentWillReceiveProps');
}
shouldComponentUpdate(nextProps, nextState) {
return nextProps.n % 3;//子组件判断接收属性的条件,true接收,false不接受
}
}
ReactDOM.render(<Counter name="计数器"/>, window.root);