React
React 是一个用于构建用户页面的JavaScript库,专注于视图,实现组件化开发。
组件化概念
将一个复杂的页面分成若干个独立的组件,每个组件都包含自己的逻辑和样式,在将这些组件组合成一个完整的页面。这样的操作即减少了逻辑复杂度又实现了代码的重用。
React
React是基于class(类)
react 包是核心
react-dom 包是 dom 渲染
jsx语法
JavaScript + xml 都结合体,可以理解为 html + js
jsx-transform 可以把jsx语法转为js语法
生成一个react项目构架
create-react-app
create-react-app官方的一个生成react项目,安装时后面跟一个项目名称
需要在一个空的文件夹下安装
create-react-app 安装的时候会自动帮我们下载react(需要的react),react-dom(主要是渲染),react-script(可以认为继承了webpack的脚手架)
加-g是全局安装
npm install create-react-app myApp
//mac 安装需要加 sudo
sudo npm install create-react-app myApp
1
2
3
4
启动服务
npm start 开启本地服务
npm build 打包
npm test jest进行单元测试
npm eject 可以把webpack命令打包出来,方便我们修改webpack文件,但此操作不能回退
React的例子
import React from "react";
import {render} from "react-dom";
let name = "哈哈";
let ele =(
<>
<h1>你好{name}</h1>
<div>嘻嘻</div>
</>
)
render(ele,window.root);
React的注意事项
一般采用import的方式引入React,React首字母规定大些,因为jsx会默认使用大写的React
会根据尖括号(<)来判断是一个html,根据花括号({)来判断是一个js
js中的保留字 关键字会进行转化 class=>className for=>htmlFor
react相邻的jsx元素,react元素,必须被一个标签包裹 <></>
style标签 必须是一个对象 style={{}} //{}表示js里面的{}表示是一个对象
注释 要用{}包裹
dangerouslySetInnerHTML 危险的,解析html用innerHtml的方式把内容塞进元素中
<div dangerouslySetInnerHTML={{_html:"<span>你好</span>"}}></div>
可以在页面中使用三元运算
事件方法 之前的οnclick=> onClick …
babel会把jsx语法进行转化的
<h1 a=1>你好</h1> =>React.createElement('h1',{a:1}没有属性null,子元素)
模拟的React 和 render
react渲染的流程
react会把jsx语法渲染成React.createElement()格式
React.createElement() 会转为 vnode(虚拟节点)
vnode 渲染到页面上
let React = {
createElement(type,props,...children){//可以组成一个对象,用来描述dom元素
return {type,props,children};//虚拟dom
}
}
let ele = <h1>哈哈</h1>;
console.log(ele);//返回一个对象
let render=(obj,container)=>{
if(typeof obj === "string") return container.appenChild(document.createTextNode(obj));
let {type,props,children} = obj;
let ele = document.createElement(type);
for(let key in props){
ele.setAtteributte(key,props[key])
}
children.forEach(item=>{
render(item,ele);//递归,渲染子节点
})
children.appendChild(ele);
}
React数组迭代的时候应该给每一项都加上一个属性key
key 就是用来表示身份的
React如果想渲染对象需要转化为字符串格式
import React from "react";
import ReactDOM from "react-dom";
ReactDOM.render(<div>{JSON.stringify({a:1})}</div>,window.root);
render
是优化过的,会把元素进行比对,把有变化的存储起来,更新有变化的内容
React组件
组件的优点
方便复用
方便维护
方便重构
组件的定义可分为2种
函数声明组件
类声明组件
函数组件
函数就是组件
组件名称规定首字母大写,小写认为是一个标签元素
组件在元素中应用<组件名/> or <组件名></组件名>
组件传递的属性或方法(<组件名 title={“222”}>)会把传入的属性包成一个对象({title:“222”})传给这个组件函数props,在组件内取值时就是,{props.title}
函数组件会在内部添加一个render方法,把函数的返回结果作为render方法的返回结果
函数组件的不足
没有状态 新版本有增加
没有生命周期的钩子 新版本有增加
函数组件中没有this
类组件
类组件在渲染时会默认调用render方法
import React from "react";
import ReactDOM from "react-dom";
class Clcok extends React.Component{
render(){
return <h1>哈哈</h1>
}
}
类组件内有状态和钩子函数
需要继承React.Component
React.Component 是一个基类,有生命周期,更改状态的方法
继承React.Component 之后才算是一个React类。
import React from "react";
import ReactDOM from "react-dom";
class Clcok extends React.Component{
constructor(props){//会接受组件传入的属性or方法
super(props);
//this.state 是规定死的,表示给这个组件生命状态
this.state={ }
}
//es7语法 简单粗暴 和上面的一样
// state={}
render(){
return <h1>哈哈</h1>
}
}
React 的数据源(props(外部传入,不可修改) ,state(内部自带,可修改))
props 会把组件传入的属性or方法放在this上 ---- 取值时:this.props.name1
this.state 状态 ---- 取值时:this.state.name1
this.setState
是父类提供的,用于修改状态,这种更新状态的方式,不会覆盖之前的,只会进行比较把更新的状态进行合并
this.setState 会刷新页面,如果不用this.setState。直接修改state 会改状态还是页面不会刷新
需要改属性的话只能把属性(props)变为状态(state)
this.setState 可以执行多次么? 面试题
React 生命周期(钩子函数)
componentDidMount(){} 当前组件挂在完成,在render方法加载完之后执行
unmountComponentAtNode() 卸载组件
//用法
ReactDOM.unmountComponentAtNode(window.root)
componentWillUnmount(){} 将要卸载,在此阶段中删掉所有的监听和卸载异步方法
componentDidMount(){
this.itemer = setInterval=()=>{
//this.setState 可以导致页面刷新
this.setState({time:new Date().toLocalString()})
}
}
componetWillUnmount(){
clearInterval(this.timer);
}
React Event 中的this问题
this问题
通常在元素中给事件绑定一个函数 (onClick = this.btnclick) this是undefined的
在es6 类中,如果把原型上的方法拿出来,这是一个错误的操作,this是undefined
btnclick(){}
1
如果解决this指向问题
在元素中绑定时加入bind(onClick = this.btnclick.bind(this)) 每次点击都产生一个新的函数
在constructor中设置一下,然后在元素中用(οnclick={this.btnclick}) 官网推荐
consttuctor(){
super();
this.btnclick = this.btnclick.bind(this); //这样每次点击的时候都是用的一个函数。
}
采用箭头函数可以完美解决,只要是在原型中的方法采用箭头函数就可以
在元素中直接用(οnclick=this.btnclick) es7方式
btnclick=()=>{} //这里采用箭头函数方式
1
prop-types
React 内置了类型检测的功能,在组件中检测,可以赋值propTypes属性
格式校验
需下载
propTypes 属性
.array 数组
.bool 布尔值
.func 函数
.number 数字
.object 对象
.string 字符串
.symbol 符号
.node 任何东西都可以被渲染:numbers, strings, elements,或者是包含这些类型的数组(或者是片段)。
.element React元素
.instanceOf(Message) 类的一个实例
.oneOf([‘News’, ‘Photos’]) 枚举值
.oneOfType([PropTypes.string,PropTypes.number,PropTypes.instanceOf(Message)]) 多种类型其中之一
.arrayOf(PropTypes.number) 某种类型的数组
.objectOf(PropTypes.number) 某种类型的对象
.shape({color: PropTypes.string,fontSize: PropTypes.number}) 特定形式的对象
.func.isRequired 可以使用 `isRequired’ 链接上述任何一个,以确保在没有提供 prop 的情况下显示警告
.any.isRequired 任何数据类型的值
function(props, propName, componentName) { return new Error()} 自定义的验证器
.arrayOf(function(propValue, key, componentName, location, propFullName) {}
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
class Person extends React.Component{
static defaultProps = {
name:'Stranger'
}
static propTypes={
name: PropTypes.string.isRequired,
age: PropTypes.number.isRequired,
gender: PropTypes.oneOf(['male','famale']),
hobby: PropTypes.array,
postion: PropTypes.shape({
x: PropTypes.number,
y:PropTypes.number
}),
age(props,propName,componentName) {
let age=props[propName];
if (age <0 || age>120) {
return new Error(`Invalid Prop ${propName} supplied to ${componentName}`)
}
}
}
render() {
let {name,age,gender,hobby,position}=this.props;
return (
<table>
<thead>
<tr>
<td>姓名</td>
<td>年龄</td>
<td>性别</td>
<td>爱好</td>
<td>位置</td>
</tr>
</thead>
<tbody>
<tr>
<td>{name}</td>
<td>{age}</td>
<td>{gender}</td>
<td>{hobby.join(',')}</td>
<td>{position.x+' '+position.y}</td>
</tr>
</tbody>
</table>
)
}
}
let person={
age: 100,
gender:'male',
hobby: ['basketball','football'],
position: {x: 10,y: 10},
}
ReactDOM.render(<Person {...person}/>, document.getElementById('root'));
受控与非受控组件
在表单中有受(状态)控组件和非受控组件
受控组件就是需要添加 onChange
受控好处:- 可以给输入框赋予默认值 - 实时校验
受控坏处:- 每次输入都会调用setState
非受控
非受控好处:- 简单,也不写状态
非受控坏处:- 不能实时校验
ref 相当于别名
ref设值or取值
import React,{Component} from "react";
import ReactDOM from "react-dom";
class Control extends Component{
password = React.createRef();//新版本 16.3采用的
handleClick=()=>{
//取值
console.log(this.username.value);//
console.log(this.refs.aaa)// 废弃的
//this.password.current 才是真实的dom
console.log(this.password.current.value);
}
render(){
return <div>
<input type="text" name="username" ref={(dom)=>{this.username = dom}} />
<input type="text" name="password" ref="aaa" />
<input type="text" name="username" ref={this.password}/> //新版本
</div>
}
}
React 是单向数据流
方法1
Q:子辈不能修改父的值,那如果修改呢?
A:通过由父辈传递给子辈一个函数,函数回调里放的就是修改的功能,当子辈执行这个函数的时候就会触发父辈的回调就可以更改这个值在传入下去。
方法2
context Api React提供的,定义一些数据,由子孙直接消费,不必一层层下传