create-react-app
项目目录
在HTML中使用react
1
2
3基础
React的注意事项
模拟的React 和 render
React组件
函数组件
类组件
React 的数据源(props(外部传入,不可修改) ,state(内部自带,可修改))
React 生命周期(钩子函数)
React Event 中的this问题
prop-types
受控与非受控组件
React 是单向数据流
4 超详细讲解
React高性能的体现:虚拟DOM
React Fiber
编写第一个react应用程序
函数组件
es6 class组件其实就是一个构造器,每次使用组件都相当于在实例化组件,像这样:
在16以前的版本还支持这样创建组件, 但现在的项目基本上不用
组件的组合、嵌套
React.createElement
create-react-app
脚手架工具create-react-app
基于webpack的一个工具,帮助我们快速创建一个项目 webpack gulp
npm install create-react-app -g yarn create-react-app -V 查看脚手架工具的版本 create-react-app 项目名字 创建项目 npm run start 开发环境运行 npm run build 打包 npm run eject 解开大礼包 在git仓库做一个commit操作
项目目录
config(vue.config.js) 默认不存在 解开大礼包才会出现 对项目做一些配置 代理 public 资源路径根vue中的public一样 scripts 文件 项目启动文件项目打包文件 src 源码文件 index.js === main.js 入口文件
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
启动服务 npm start 开启本地服务 npm build 打包 npm test jest进行单元测试 npm eject 可以把webpack命令打包出来,方便我们修改webpack文件,但此操作不能回退
cmd
“–exact” “react” "react-dom"dom 渲染 "react-scripts"继承了webpack的脚手架 “cra-template” “–cwd”
react-scripts > jest > jest-cli > jest-config > jest-environment-jsdom > jsdom > left-pad
react-scripts > workbox-webpack-plugin > workbox-build > strip-comments > babel-plugin-transform-object-rest-spread > babel-runtime > [email protected]: core-js@❤️
“react-scripts > [email protected]” has incorrect peer dependency “[email protected]”.
warning “react-scripts > @typescript-eslint/eslint-plugin > [email protected]” has unmet peer dependency “typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta”.
在HTML中使用react
// 创建组件
/*
1.通过React crateClass 方法创建组件
2. 配置项里固定有一个render函数
3. render函数里要求return页面模板
4. 组件也是一个虚拟dom
*/
/*
React.createElement(‘div’,{id:‘test’},[
React.creatElement(‘h1’,{class:‘red’,text:‘jsx语法(vue中的模板)’},[])
React.createElenent(‘p’,{text:123},[
React.craeteElement(‘span’,{text:1})
])
])
语法 糖 class 简化代码少写点
jsx javascript xhtml
可以让我们在js里写html标签
1.标签必须闭合
2.必须有一个根元素
正常的js代码不能直接写标签 jsx 也不行
想使用需要将jsx的语法进行解析
type=‘text/babel’ 是告诉浏览器该标签的内部需要解析jsx
*/
// 注释方式 {/要注释的内容/}
// 控制一个元素的渲染 ||
// 2个元素切换 三元表达式
// 渲染的东西比较多 用函数
/*
react 不允许直接将对象绑定在页面
react 绑定数组到页面回将数组的每一项拆分
*/
/*
事件绑定 原生事件用驼峰命名法 注意传参不能能加() bind
方法一直接调用处理函数
react 中的事件和原生类似 将原生事件使用变成驼峰命名法 onclick->onClick
默认参数是事件对象
传递参数不能写() 所以通过bind 进行传参 默认参数放到最后
方法二
在事件的函数里 调用目标处理函数
注意 需要手动传递事件对象
*/
/*
setState 通知组件重新渲染一遍
setState 是一个异步的函数
参数1 表示要修改的数据 参数2是一个回调 当数据修改结束后执行
1-2-3-4-1
setState 同步中 修改数据不会直接更新页面 将同一个事件循环所有的setState 合并 更新一次页面
setState 如果在异步中 每一次都会触发页面刷新
*/
/*
使用方式2 通过ref 绑定组件
可以获取组件对象 获取绑定组件的相关方法
*/
/*
组件标签中写的内容不会被渲染
但是可以在组件内部通过 props.childer 获取使用
/
/
组件通信
父子 props 将父组件的数据传递给子组件 ref 获取子组件的方法 由父组件调用
子父 props 将父组件的方法传给子组件调用
兄弟 状态提升 上下文 全局状态管理 redux
父子通信 1 父组件控制子组件元素的渲染
将 控制div显示的state 放到子组件上
子组件有一个修改 state值的方法
通过ref 将父组件调用子组件的方法
/
/
父子通信 2 父组件控制子组件元素的渲染
直接传递数据
1.控制元素渲染的数据放在父组件上
2.修改的方法放在父组件上
3.通过props 将数据传递给子组件
4.父组件调用自己的方法修改
*/
/*
子父通信 子组件控制父组件
1数据在父组件上
2.修改数据的方法在父组件
3.将父组件的方法传给子组件调用
*/
/*
亲兄弟通信
1.子元素控制另一个子元素的div显示
2.控制变量放在父组件
3.控制变量的方法也在父组件上
4.结合父子和子父通信
*/
let Son = React.createClass({
sonfun(){
console.log(‘子组件的方法’)
},
toggle(num1,num2){
console.log(num1,num2)
this.setState({show:!this.state.show})
},
render(){
return(
子组件
子组件:{this.props.children}
<button onClick={()=>{
this.props.sonToggle(6,8)
}}>sonToggle
{!this.props.show||<div className='red'></div>}
</div>
</div>
)
}
})
let Son2 = React.createClass({
render(){
return(
子组件2
<button onClick={()=>{
this.props.toggle()
}}>toggle
)
}
})
let Component = React.createClass({
getInitialState(){
console.log(‘初始化state值’) //类似于vue中的data
return {
name:‘韩梅梅’,
hide:true,
num:1,
string:‘呵呵’,
arr:[“呢”,‘he’,‘sdf’],
obj:{us:123,ps:123},
null:null,
undef:undefined,
boolean:true,
tabs:[‘动作’,‘恐怖’,‘动漫’,‘喜剧’],
// tabs:[‘
- 动作
- ’,‘
- 恐怖
- ’,‘
- 动漫
- ’]
// tabs:[ - 动作
- ,
- 恐怖
- ,
- 动漫
- ]
color:false,}
},
renderDiv(){
console.log(‘渲染div’)
if(this.state.hide){}else{ return (<img src='dsfsf'/>) }
},
test(num1,num2,event){
console.log(‘点到我了’,num1,num2,event)
},
toggle(){
// this.state.color=!this.state.color
console.log(this.state.color)
this.setState({color:!this.state.color})
},
render(){
return (
hehe
hehe
{this.props.hehe}
{/* {this.state.show?div:p
} /}
{/{ this.state.hide||div}*/}
{this.renderDiv()}
数据绑定
{this.state.num}
{this.state.string}
{this.state.null?‘a’:‘b’}
{this.state.undefined?‘c’:‘d’}
{this.state.boolean?‘真’:‘假’}
{this.state.obj.us}
{this.state.arr}<ul> {this.state.tabs.map((item,index)=>{ return (<li>{item}</li>) })} </ul> {/*<button onClick={this.test.bind(this,1,2)} >add</button>*/} <button onClick={(e)=>{ console.log(11) this.test(1,2,e) }}>add</button> <div className={this.state.color?'red':'blue'}> </div> <button onClick={this.toggle}> toggle</button> // this.setState({num:5},()=>{ // console.log('赋值结束',this.state.num) // }) // console.log('bottom',this.state.num) setTimeout(()=>{ this.setState({num:2}) this.setState({num:3}) this.setState({num:4}) this.setState({num:5}) },1000) <img ref='hehe' src="sfsf" alt=""/> <button onClick={()=>{ console.log(this.refs.hehe) }}> toggle</button> <button onClick={()=>{ console.log(this) this.refs.son.sonfun() }}> toggle</button> <hr/> <Son ref='son'> 你买到票了没 </Son> <button onClick={()=>{ this.refs.son.toggle(1,2) }}>toggle</button> <Son ref='son'></Son> <button onClick={()=>{ this.setState({show:!this.state.show}) }}>toggle</button> <Son toggle={this.state.show}></Son> toggle(num1,num2){ console.log(num1,num2) this.setState({show:!this.state.show})
},
render() {
return (
这里是父组件
{!this.state.show|| }
toggle(){ this.setState({show:!this.state.show})
},
render() {
return (
这里是父组件
<Son1 show={this.state.show}></Son1> <Son2 toggle={this.toggle}></Son2> </div> )
}
})// 将组件挂载到 app元素上 参数1 要挂载的组件 参数2 要挂载的元素
1 https://blog.csdn.net/qq_36824244/article/details/103003640
// 组件名当成标签名使用 组件名必须大写
ReactDOM.render(,document.getElementById(‘app’))
// ReactDOM.render(,app)用于构建用户界面的javascript库
16之前采用diff算法,之后采用fiber算法,分成小块,异步渲染
import React from “react”
//只要使用jsx的语法都需要引入react,这是顶级api
import ReactDOM from “react-dom”
//render是react dom提供的一个方法,这个方法通常只会渲染一次
import { render } from ‘react-dom’const app=
Welconme
//函数式组件
//使用箭头函数,但名字要大写开始
const createApp = (props) => {
//在jsx里面写js语法需要加{},只有这一种语法
returnWelconme{props.title}
}
const app = createApp({
title: ‘16.8’
})
//类组件
class App extends React.Component {
render () {
return类组件!!!
}
}
//new一个实例
//类组件渲染的原理
// 因为app不是一个element所以必须要调实例的render方法
const app = new App({
desc:‘类组件是继承React.Component的1’
}).render()
//表示一个虚拟dom树
const appVDom = {
tag: ‘div’,
attrs: {
className: ‘app’,
id: ‘appRoot’
},
children: [
{
tag: ‘h3’,
attrs: {
className: ‘title’
},
children: [
‘类组件!!!’
]
},{
tag: ‘p’,
attrs: null,
children: [
‘类组件是继承React.Component的’
]
}
]
}
//所以react在真正渲染的时候会把上面的代码成下面这个样子,这是合法的js代码
class App extends Component {
render () {
return (
// React.createElement是一个方法,用于创建元素,可以有很多参数,但前两个是固定的
//第一个为标签名
//第二个为标签的属性
React.createElement(
‘div’,
{
className: ‘app’,
id: ‘appRoot’
}
),
React.createElement(
‘h3’,
{
className: ‘title’,
},
‘类组件’
),
React.createElement(
‘p’,
null,
‘类组件是继承React.Component的’
)
)
}
}
//下载class包 npm i classnames --saveReactDOM.render(app,querySelector("#toot")
//在react6以前使用的是这种方式来创建一个类组件
React.createClass({
render类组件!!!
})
2
https://blog.csdn.net/halations/article/details/843892613基础
https://blog.csdn.net/qq_30523685/article/details/85621598React的注意事项
一般采用import的方式引入React,React首字母规定大些,因为jsx会默认使用大写的React会根据尖括号(<)来判断是一个html,根据花括号({)来判断是一个js js中的保留字 关键字会进行转化 class=>className for=>htmlFor react相邻的jsx元素,react元素,必须被一个标签包裹 <></> style标签 必须是一个对象 style={ {}} //{}表示js里面的{}表示是一个对象 注释 要用{}包裹 dangerouslySetInnerHTML 危险的,解析html用innerHtml的方式把内容塞进元素中
你好"}}>可以在页面中使用三元运算 事件方法 之前的οnclick=> onClick …模拟的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 =哈哈
;
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组件
函数组件
函数就是组件 组件名称规定首字母大写,小写认为是一个标签元素 组件在元素中应用<组件名/> or <组件名></组件名> 组件传递的属性或方法(<组件名 title={“222”}>)会把传入的属性包成一个对象({title:“222”})传给这个组件函数props,在组件内取值时就是,{props.title}函数组件会在内部添加一个render方法,把函数的返回结果作为render方法的返回结果
函数组件的不足没有状态 新版本有增加 没有生命周期的钩子 新版本有增加 函数组件中没有this
类组件
类组件在渲染时会默认调用render方法 类组件内有状态和钩子函数 需要继承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哈哈
}
}
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=()=>{} //这里采用箭头函数方式
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 = { nam