文章目录
React是一个构建用户界面的JavaScript库。
React 的特点:
- 声明式设计
- 高效,采用虚拟DOM来实现dom渲染,减少DOM操作
- 灵活, 可以与其他库搭配使用。
- JSX,在js中编写html。
- 组件化、模块化,代码容易复用
- 单向数据流,没有实现数据的双向绑定。数据->视图->时间->数据
create-react-app 创建项目:
通过react脚手架,创建项目来开发部署。
- 安装脚手架Create React App
yarn global add create-react-app
- 创建项目:
create-react-app projName
- 目录文件解析
- 启动
npm run start
或者yarn start
React 元素渲染
- 定义一个元素
注意:元素是一个组件的最小单位。一个元素或组件有且只有一个根节点。这一点和vue是一样的。
使用JSX写法,可以创建JS元素对象。
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
// 定义一个元素,元素是一个组件的 最小单位。
// 注意:一个元素或者组件有且只有一个根节点。这一点和VUE是一样的。
let h1 = <h1>hello world</h1>
// root.render(h1);
// <App /> JS普通对象
root.render(<App />);
案例1:
function clock() {
const root = ReactDOM.createRoot(document.getElementById('root'));
let time = new Date().toLocaleTimeString()
let ele = (<div>
<h1>NOW TIME IS : {time}</h1>
</div>)
root.render(ele)
}
setInterval(() => {
clock()
}, 1000);
案例2:函数式组件
// React 函数式组件
function Clock(props){
return(<div>
<h1>NOW TIME IS : {props.date.toLocaleTimeString()}</h1>
<h2>这是函数式组件开发</h2>
</div>)
}
function run(){
root.render(<Clock date={new Date()}/>)
}
setInterval(() => {
run()
}, 1000);
JSX语法
对html进行扩充。
优点:执行更快,在编译为JS时进行优化(采用虚拟DOM)。类型更安全,在编译时发现错误。编写模板更简单快速。
注意:JSX组件必须要有一个根节点。正常的普通html元素要小写,如果是大写,则默认为组件。
JSX表达式
- 由html元素构成
- 中间可以使用
{}
来插入变量 {}
中间可以写表达式{}
表达式中可以使用jsx对象- 属性和html内容都是用
{}
插入
案例:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './App.css'
const root = ReactDOM.createRoot(document.getElementById('root'));
let color = 'bgRed'
// 三元运算
// HTML的类名要些className。因为class是js的关键字
let content2 = (
<h1>1>2嘛? {1>2?'是':<a className={color} href='#'>否</a>}</h1>
)
// 字符串拼接 与 模板引入
let time = new Date().toLocaleDateString();
let str = '日期:'
let content = (<div>
<h1>HUATHY HELLO</h1>
<h1>{str + time}</h1>
{content2}
</div>)
root.render(content)
React样式和注释
JSX_style样式
- class、style中,不可以存在多个class属性
- style样式中,如果多个单词属性足额,第二个单词首字母大写。或者以引号包裹样式名。
注释
{/* */}
:{/* 在元素中的注释必须以大括号包裹起来 */}
//
:js注释
/** */
:类注释
/* */
:多行注释
案例:
- index.js
import React from 'react'; import ReactDOM from 'react-dom/client'; import './App.css' const root = ReactDOM.createRoot(document.getElementById('root')); let egstyle = { // 样式 'background-color': 'skyblue', borderBottom: '6px solid red' } // js注释 let cls = ['greenBorder','high-100'].join(' '); let ele = (<div> {/* style里面必须是对象,不可以是字符串 */} <h1 className={cls} style={egstyle}>HELLO WORLD</h1> </div>) root.render(ele);
- App.css
.greenBorder{ border: 6px solid greenyellow; } .high-100{ height: 100px; }
React组件
- 函数式组件:静态页面
- 类组件(动态组件),一般会有交互和数据修改操作。
- 复合组件:组件中包含其他组件(包括类组件和函数式组件)。
区别:类组件中操作事件更方便。函数组件中编写事件方法不太推荐。如果组件中有事件,建议使用类组件。静态组件可以使用函数式组件。
示例:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './App.css'
const root = ReactDOM.createRoot(document.getElementById('root'));
// 函数式组件
function Childcom(props){
let title = '标题'
// 条件判断
let isBig = props.num > 0 ? '是':'否'
return(<div>
<h1>HELLO 函数式组件</h1>
<h2>{title}</h2>
{/* 大于小于符号可以使用{''}包裹,也可以使用实体符号 */}
<h2> {props.num} {'>'} 0 ? {isBig}</h2>
</div>)
}
// 类组件
class Hello extends React.Component{
render(){
return(<div>
<h1>HELLO 类组件定义</h1>
{/* react组件可以包含组件 */}
<Childcom num={this.props.num}/>
</div>)
}
}
// root.render(<Childcom num='-2' />)
root.render(<Hello num='2'/>)
react状态(React State)
相当于vue中的data。但是和vue不同。
案例一:当前时间
import React from 'react';
import ReactDOM from 'react-dom/client';
import './App.css'
const root = ReactDOM.createRoot(document.getElementById('root'));
/**
* 类注释
*/
class Hello extends React.Component{
// 构造函数
constructor(props){
// 调用父类方法,会一直传递到继承的类
super(props)
// 状态(数据) -- > view
// 1. 构造函数初始化数据,将需要改变的数据初始化到state中
this.state = {
time: new Date().toLocaleTimeString()
}
// 构造函数没有刷新
console.log(this.state.time)
}
render(){
console.log('这是渲染函数');
// // 要想时间动起来,可以在这里更改
// this.state.time = new Date().toLocaleTimeString()
return(<div>
<h1>当前时间:{this.state.time}</h1>
</div>)
}
// 生命周期函数,组件渲染完成时
componentDidMount(){
setInterval(()=>{
// 不推荐的改变值方式
// this.state.time = new Date().toLocaleTimeString()
/*
2. 修改state数据。采用this.setState({})的方式会重新渲染内容
不要直接this.state.dateName=XXX。这种方式不会重新渲染内容。
*/
console.log(this.state.time)
this.setState({
time: new Date().toLocaleTimeString()
})
/* 这里打印的时间和调用this.setState前是一样的,并没有立即修改dom内容。
原因:由于很多地方都会修改state,react会对虚拟DOM和DOM之间进行比较。
当该函数所有设置状态都的修改完成之后,统一对比虚拟dom对象,然后统一修改,提升性能。
*/
console.log(this.state.time)
},1000)
}
}
// // 这种写法不推荐,因为定时函数与渲染函数相耦合。解耦
// setInterval(() => {
// // 虽然定时任务一直在跑,但是加载相同组件,react是不会重新初始化的。这也是时间没有更新的原因。
// root.render(<Hello />)
// }, 1000);
root.render(<Hello />)
案例二:tab切换
- index.js
import React from 'react'; import ReactDOM from 'react-dom/client'; import './App.css' const root = ReactDOM.createRoot(document.getElementById('root')); class Tab extends React.Component{ constructor(props){ super(props) // 设置状态、数据 this.state = { c1:'content-active', c2:'content' } this.clickEvent = this.clickEvent.bind(this) } clickEvent(e){ console.log(e) console.log(e.target) let index = e.target.dataset.index console.log(index) // 这里必须在构造函数中对this进行绑定。否则这里的this是undefined console.log(this) this.setState({ // 强等于 c1: index === '1' ? 'content-active' : 'content', c2: index === '2' ? 'content-active' : 'content' }) } render(){ return(<div> <button data-index='1' onClick={this.clickEvent}>内容1</button> <button data-index='2' onClick={this.clickEvent}>内容2</button> <h1 className={this.state.c1}>内容1</h1> <h1 className={this.state.c2}>内容2</h1> </div>) } } root.render(<Tab />)
- App.css
.content{ display: none; } .content-active{ display: block; }
React 数据传递
父传子
props:父组件给传子组件数据,单向流动,不能子组件传给父组件。
props的传值可以是任意的类型。
props可以设置默认值。语法:ComponentName.defaultProps = { param:value }
可以传递函数,从而修改父组件的state,从而实现子传父。
案例:
- index.js
import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css' // 在父元素中使用state去控制子元素props的从而实现父传子。 const root = ReactDOM.createRoot(document.getElementById('root')); class ParentCom extends React.Component{ constructor(props){ super(props) this.state = { isActive: true } this.changeShow = this.changeShow.bind(this) } render(){ return(<div> <button onClick={this.changeShow}>控制子元素显示</button> <ChildCom isActive={this.state.isActive}/> </div>) } changeShow(){ this.setState({ isActive: !this.state.isActive }) } } class ChildCom extends React.Component{ constructor(props){ super(props) } render(){ let strCls = this.props.isActive ? ' active' : ''; return(<div className={'content' + strCls}> <h1>这是子组件</h1> </div>) } } root.render(<ParentCom />);
- index.css
.content{ display: none; } .content.active{ display: block; }
子传父
调用父类传入的方法,给父类的方法传值,来实现子传父。
案例
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css'
// 子传父
const root = ReactDOM.createRoot(document.getElementById('root'));
class ParentCom extends React.Component{
constructor(props){
super(props)
this.state = {
childData:null
}
}
render(){
return(<div>
<h1>这是父元素接受子元素传递的数据:{this.state.childData}</h1>
<ChildCom setChildData={this.setChildData}/>
</div>)
}
setChildData = (data)=>{
this.setState({
childData:data
})
}
}
class ChildCom extends React.Component{
constructor(props){
super(props)
this.state = {
msg : 'HELLO WORLD'
}
}
render(){
return(<div>
<h1>这是子组件</h1>
<button onClick={this.sendMsg}>传入数据</button>
{/* 这里可以直接调用父元素的方法。但是要注意使用箭头函数 */}
<button onClick={()=>{this.props.setChildData('')}}>重置</button>
</div>)
}
sendMsg = ()=>{
// 使用箭头函数,可以不用绑定。
this.props.setChildData(this.state.msg)
}
}
root.render(<ParentCom/>)
react 事件
- 特点:
- 驼峰命名,首字母小写,后续单词首字母大写。
- 用
{}
大括号括起来,传入一个函数。
- 事件对象:React返回的事件ui想是代理的原生的事件对象。如果需要查看事件对象的具体值,必须直接输出事件对象的属性。
- 事件默认行为:原生,组织默认行为的时候,可以直接
return false
。但是在react中阻止事件默认行为必须调用e.preventDefault()
函数。 - react事件传参:需要使用箭头函数。
<button onClick={(e)=>{this.envet2(123,e)}}>函数传参</button>
案例:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css'
// 子传父
const root = ReactDOM.createRoot(document.getElementById('root'));
class ParentCom extends React.Component{
constructor(props){
super(props)
}
render(){
return(<div>
<form action='http://www.baidu.com'>
<a className='child'>HELLO WROLD</a>
<button onClick={this.parentEvent}>submit</button>
</form>
<button onClick={(e)=>{this.envet2(123,e)}}>函数传参</button>
</div>)
}
parentEvent=(e)=>{
console.log(e)
// 阻止事件默认行为。
e.preventDefault()
}
envet2(param,e){
console.log(param)
}
}
root.render(<ParentCom />)
React 条件渲染
React中的条件渲染即和JS中,条件运算,eg:if...eles...
、(三元运算)boolean?res1:res2
- 通过条件运算返回要渲染的JSX对象
- 通过条件运算得出jsx对象,再将jsx对象渲染到模板中
- 通过修改样式active的方式。(这个案例在上面的例子中已经存在)
案例:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css'
// 子传父
const root = ReactDOM.createRoot(document.getElementById('root'));
function Welcom(props){
return (<div>
<h1>欢迎登录</h1>
</div>)
}
function Login(props){
return (<div>
<h1>PLESE LOGIN</h1>
</div>)
}
class Demo extends React.Component{
constructor(props){
super(props)
this.state = {
isLogin: false
}
}
render(){
return this.state.isLogin ? <Welcom/> : <Login />
}
}
class Demo2 extends React.Component{
constructor(props){
super(props)
this.state = {
isLogin: false
}
}
render(){
let element = this.state.isLogin ? <Welcom/> : <Login />
return(<div>
<h1>----- HEAD -----</h1>
{element}
{this.state.isLogin ? <Welcom/> : <Login />}
<h1>----- END -----</h1>
</div>)
}
}
// root.render(<Demo1 />)
root.render(<Demo2 />)
React 列表渲染
将列表数据接成数组的JSX放置到模板中。
使用数组的map方法,对每一项数据按照JSX的形式进行加工,最终得到一个每一项都是JSX对象的数组,再将数组渲染到模板中,并将key值放置到数组中。
案例
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css'
// 子传父
const root = ReactDOM.createRoot(document.getElementById('root'));
function List1(props){
let item = props.item
console.log(item)
return (<li>
<strong>{item.name}</strong>-<span>{item.age}</span>
</li>)
}
class List2 extends React.Component{
constructor(props){
super(props)
}
render(){
let item = this.props.item
return(<li onClick={(e)=>{this.clickEvent(this.props.index,item.name,e)}}>
<strong><a>{item.name}</a></strong>
-<span>{item.age}</span>
</li>)
}
clickEvent=(index,name,event)=>{
alert(index+"-"+name)
}
}
class Demo extends React.Component{
constructor(props){
super(props)
this.state = {
list:[
{
name: 'Huathy',
age: '22'
},
{
name: '小花',
age: '18'
},
{
name: '嘻嘻',
age: '20'
}
]
}
}
render(){
let arr = this.state.list.map((item,index) => {
return(
// <li key={index}>
// <strong>{index}:{item.name}</strong>-<span>{item.age}</span>
// </li>
<List1 key={index} item={item} index={index}/>
)
})
return(<div>
<h1>利用组件1的方式</h1>
<ul>
<li>
<strong>name</strong>-<span>age</span>
</li>
{arr}
</ul>
<h1>利用组件2的方式</h1>
<ul>
{
this.state.list.map((item,index)=><List2 key={index} item={item} index={index}/>)
}
</ul>
<h1>不利用组件的方式</h1>
<ul>
{
this.state.list.map((item,index)=>(<li onClick={(e)=>{this.clickFn(index,item.name,e)}} key={index}>
<strong>{item.name}</strong>-<span>{item.age}</span>
</li>))
}
</ul>
</div>)
}
clickFn = (index,name,event) => {
alert(index+"-clickFN-"+name)
}
}
root.render(<Demo/>)
For循环方法
- forEach:没有返回值
arr.forEach( ( item,index ) => { } )
- map:对数组每一项进行加工后返回一个新数组
let arr = [1,2,3,4,5,6]
let res = arr.map((item,index)=>{
let str = item+index
return str
})
- filter:过滤,将想要的内容进行筛选,返回筛选后的内容
let arr = [1,2,3,4,5,6]
let res = arr.filter((item,index)=>{
return item%2 == 0 //返回偶数,过滤奇数
})
- reduce:对数组进行整合
let arr = [1,2,3,4,5,6]
let res = arr.reduce((pre,next)=>{
return pre + next
})
for...in...
主要用来遍历对象,不适合遍历数组
let obj = {name:'Huathy',age:18}
for (key in obj){ //key是无序随机的
console.log(key+'-'+obj[key])
}
for...of...
可以用来遍历数组、类数组的对象、字符串、set、map
let arr = [1,2,3,4,5,6]
// 与for...in...的区别是of的item是元素值,而in的key是键名
for(let item of arr){
console.log(item)
}
说明:个人学习笔记,对视频内容有所调整删减。以使得文章更简洁。
致谢:B站UP主[老陈打码] BV1T7411W72T