npx create-react-app "項(xiàng)目名"
react用到的第三方包
- classnames
- styled-components 將css獨(dú)立來(lái)寫(xiě)的第三方包
- prop-types:props的類型檢測(cè)工具
- axios
在jsx里面寫(xiě)js代碼就加一個(gè){}
創(chuàng)建一個(gè)簡(jiǎn)單的react:
import React from "react"
import ReactDOM from "react-dom"
// const app = <h1>welcome first!</h1>// jsx語(yǔ)法纵诞,不需要加引號(hào)
const createApp = (props) => {
return (
<div>
{/* {只要在jsx里面寫(xiě)js代碼就要加一層花括號(hào)注釋也要用花括號(hào)} */}
<h1>歡迎來(lái)到{props.title}</h1>
<p>優(yōu)秀的{props.title}</p>
</div>
)
}
const app = createApp({
title: '德萊聯(lián)盟'
})
ReactDOM.render(
app,
document.querySelector("#root")
)
react定義組件的方式:
1舒裤、箭頭函數(shù)烛愧,組件的首字母大寫(xiě)
2构捡、使用類組件,可以嵌套
<1> 箭頭函數(shù)方法:
// react創(chuàng)建組件的第一個(gè)方式:箭頭函數(shù),這個(gè)名字要大寫(xiě) App
const App = (props) => {
return (
<div>
<h1 title={props.title}>想吃很多{props.title}</h1>
<p>有很多{props.title}</p>
</div>
)
}
ReactDOM.render(
<App title="炸雞腿"/>,
document.querySelector("#root")
)
<2>使用類繼承React.Component:
// 定義組件的第二個(gè)方法:使用類繼承React.Component
import React, { Component } from "react"
import { render } from "react-dom"
class App extends Component {
render () {
console.log(this.props) //{title: "類組件是繼承React.Component的"} 參數(shù)傳遞就用this.props
return (
<div>
<h1>類組件</h1>
<p>{this.props.title}</p>
</div>
)
}
}
// render 是ReactDOM提供的方法,這個(gè)方法通常只會(huì)使用一次
render(
<App title="類組件是繼承React.Component的"/>,
document.querySelector("#root")
)
// 16版本以前創(chuàng)建組件的方法
// React.createClass({
// render () {
// return (
// <div>{this.props.title}</div>
// )
// }
// })
組件嵌套
import React, { Component } from "react"
import { render } from "react-dom"
const Header = () => <h1>組件嵌套</h1>
class App extends Component {
render () {
return (
<div>
<Header />
<p>{this.props.title}</p>
</div>
)
}
}
render(
<App title="react組件可以嵌套"/>,
document.querySelector("#root")
)
jsx原理,通過(guò)React.createElement的方式創(chuàng)建元素,有無(wú)限個(gè)參數(shù)蝶念,但前兩個(gè)是固定的,第一個(gè)標(biāo)簽名芋绸,第二個(gè)是標(biāo)簽的屬性媒殉,剩下的都是子元素
React.createElement(type,[props],[...children])
import React, { Component } from "react"
import { render } from "react-dom"
class App extends Component {
render () {
return (
React.createElement (
'div',
{
className: 'app',
id: 'appRoot'
},
React.createElement (
'h1',
{
className: 'title'
},
'jsx原理'
),
React.createElement (
'p',
null,
'jax到底怎么寫(xiě)?'
)
)
)
}
}
// const appVDom = {
// tag: "div",
// attrs: {
// className: "app",
// id: "appRoot"
// },
// children: [
// {
// tag: "h1",
// attrs: {
// className: "app"
// },
// children: [
// "jsx原理"
// ]
// }, {
// tag: "p",
// attrs: null,
// children: [
// "jsx到底怎么寫(xiě)呢摔敛?"
// ]
// }
// ]
// }
render(
<App />,
document.querySelector("#root")
)
react里的css
1廷蓉、使用style標(biāo)簽內(nèi)聯(lián)創(chuàng)建
import React, { Component } from "react"
import { render } from "react-dom"
import classNames from "classnames"
import styled from "styled-components"
import './index.css'
const Title = styled.h2`
color: #f00
`
class App extends Component {
render () {
const style = {color: '#f00'}
return (
<div>
<h1>元素的樣式</h1>
<Title>styled-components的使用</Title>
<ol>
<li style={style}>使用style方式</li>
<li className='redclass'>使用class方式,但在react里要寫(xiě)成className</li>
<li className={classNames('a',{'b': true,'c': false})}>要?jiǎng)討B(tài)添加className使用classnames第三方包</li>// 該li的class只有a马昙,b沒(méi)有c
</ol>
</div>
)
}
}
render(
<App />,
document.querySelector("#root")
)
2桃犬、使用class方式,但是要寫(xiě)成className行楞,樣式寫(xiě)在css文件中攒暇,所有要先import引入
第三方的css包:1、classnames子房,可以動(dòng)態(tài)添加不同的className
3形用、styled-components 將css獨(dú)立來(lái)寫(xiě)的第三方包
React項(xiàng)目組件化
快捷鍵:rcc react的class component;rfc react的function component
整個(gè)單頁(yè)應(yīng)用的入口文件:App.js
入口文件:index.js
其余的子組件包在App下面
-
在src下新建一個(gè)components目錄证杭,里面包含所有的子組件田度,一個(gè)子組件就是一個(gè)文件夾,用組件名作為文件夾名字解愤;同時(shí)每币,在components的根目錄下還有一個(gè)index.js文件厌处,用來(lái)引入和導(dǎo)出所有的子組件,在App.js里面引入所有的組件旭愧。
image.png 在src下創(chuàng)建一個(gè)services文件夾羹与,里面有apis.js和index.js文件像街。apis.js是統(tǒng)一管理接口的文件
組件化開(kāi)發(fā)React todolist筋讨, 項(xiàng)目開(kāi)發(fā)中的組件的基本目錄結(jié)構(gòu)基本上是這樣的:
> /your-project
>
> - src
> - …
> - components
> - YourComponentOne
> - index.js/YourComponentOne.js
> - YourComponentTwo
> - index.js/YourComponentTwo.js
> - index.js 用于導(dǎo)出組件
注意:一個(gè)組件只干一件事情 谆棺,所以TodoList和TodoItem要做成兩個(gè)組件叹螟,這樣也方便于后期理解shouldComponentUpdate
一個(gè)組件里的return只能有一個(gè)根元素 魄宏,因此react里可以使用Fragment(需要import引入秸侣,是一個(gè)空標(biāo)簽),或者直接使用空標(biāo)簽
import React, { Component, Fragment } from 'react'
import {
TodoHeader,
TodoInput,
TodoList
} from './components'
export default class App extends Component {
render() {
return (
<Fragment>
<TodoHeader />
<TodoInput />
<TodoList />
</Fragment>
// <>
// <TodoHeader />
// <TodoInput />
// <TodoList />
// </>
)
}
}
兩種導(dǎo)出方式:
//第一種,當(dāng)需要對(duì)這里引入的組件進(jìn)行處理再導(dǎo)出時(shí)使用
// import TodoHeader from './TodoHeader'
// import TodoInput from './TodoInput'
// import TodoList from './TodoList'
// export {
// TodoHeader,
// TodoInput,
// TodoList
// }
//第二種
export { default as TodoHeader} from './TodoHeader'
export { default as TodoInput} from './TodoInput'
export { default as TodoList} from './TodoList'
組件的數(shù)據(jù)掛載方式
1. 通過(guò)props傳遞
function組件直接通過(guò)props.xxx,class組件通過(guò)this.props.xxx傳遞
import React from 'react'
export default function TodoHeader(props) {
console.log(props)// {title: "待辦事項(xiàng)"}
return (
<h1>
{props.title}
</h1>
)
}
屬性(props)
props.children 組件標(biāo)簽內(nèi)的內(nèi)容
// App.js
export default class App extends Component {
render() {
return (
<Fragment>
<TodoHeader desc="完成今天所有的事">
待辦事項(xiàng)列表
</TodoHeader>
<TodoInput btnText="ADD"/>
<TodoList />
</Fragment>
// TodoHeader/index.js
export default function TodoHeader(props) {
console.log(props) // {desc: "完成今天所有的事", children: "待辦事項(xiàng)列表"}
return (
<h1>
{props.children}
</h1>
)
}
prop-types:props的類型檢測(cè)工具
// TodoHeader/index.js
import React from 'react'
import PropTypes from 'prop-types'
export default function TodoHeader(props) {
console.log(props)
return (
<>
<h1>
{props.children}
</h1>
<h3>{props.desc}</h3>
<p>{props.x + props.y}</p>
</>
)
}
TodoHeader.propTypes = {
desc: PropTypes.string,
x: PropTypes.number.isRequired,
y: PropTypes.number.isRequired
}
class組件寫(xiě)法:
// TodoInput.js
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class TodoInput extends Component {
static propTypes = {
btnText: PropTypes.string
}
static defaultProps = {
btnText: '添加TODO' //組件默認(rèn)值
}
//TodoHeader.defaultProps = {
// desc: '如果能重來(lái)'
//} //funciton組件寫(xiě)法
render() {
return (
<div>
<input type="text"/><button>{this.props.btnText}</button>
</div>
)
}
}
state
組件內(nèi)部狀態(tài)定義用state宠互,有兩種定義方法 味榛,而props是外部傳入的屬性.只有class組件才有this和state
// App.js
export default class App extends Component {
// 第一張定義方式
// state = {
// title: '待辦事項(xiàng)'
// }
// 第二種定義方法
constructor () {
super()
this.state = {
title: '待辦事項(xiàng)',
desc: '完成今天所有的事'
}
}
render() {
return (
<Fragment>
<TodoHeader desc={this.state.desc}>
{this.state.title}
</TodoHeader>
組件的分類:
- class組件和function組件
- 受控組件、不受控組件予跌、半受控組件(通過(guò)props傳入屬性搏色,在組件內(nèi)部沒(méi)法修改的叫受控組件,通過(guò)state定義的狀態(tài)不是外部傳入的時(shí)不受控組件)
不能跨組件傳遞props券册,必須一層一層的傳
組件模板渲染語(yǔ)法
{/* {this.todos[0].isCompleted && '完成'} */}
{this.state.todos[0].isCompleted ? '完成' : '未完成'}
{
this.state.todos.map(todo => {
return <div key={todo.id}>{todo.title}</div>
})
}
{div dangerouslySetInnerHTML={{__html: this.state.article}}} // 渲染不帶HTML標(biāo)簽的內(nèi)容
dangerouslySetInnerHTML频轿,類似于vue的v-html,輸出不帶HTML標(biāo)簽的內(nèi)容
屬性vs狀態(tài)
相似點(diǎn):都是純js對(duì)象烁焙,都會(huì)觸發(fā)render更新航邢,都具有確定性(狀態(tài)/屬性相同,結(jié)果相同)
不同點(diǎn):
- 屬性能從父組件獲取骄蝇,狀態(tài)不能
- 屬性可以由父組件修改膳殷,狀態(tài)不能
- 屬性能在內(nèi)部設(shè)置默認(rèn)值,狀態(tài)也可以
- 屬性不在組件內(nèi)部修改九火,狀態(tài)要改
- 屬性能設(shè)置子組件初始值赚窃,狀態(tài)不可以
- 屬性可以修改子組件的值,狀態(tài)不可以
state
的主要作用是用于組件保存吃既、控制考榨、修改自己的可變狀態(tài)。state
在組件內(nèi)1部初始化鹦倚,可以被組件自身修改河质,而外部不能訪問(wèn)也不能修改。你可以認(rèn)為 state
是一個(gè)局部的震叙、只能被組件自身控制的數(shù)據(jù)源掀鹅。state
中狀態(tài)可以通過(guò) this.setState
方法進(jìn)行更新,setState
會(huì)導(dǎo)致組件的重新渲染媒楼。
props
的主要作用是讓使用該組件的父組件可以傳入?yún)?shù)來(lái)配置該組件乐尊。它是外部傳進(jìn)來(lái)的配置參數(shù),組件內(nèi)部無(wú)法控制也無(wú)法修改划址。除非外部組件主動(dòng)傳入新的 props
扔嵌,否則組件的 props
永遠(yuǎn)保持不變限府。
如果搞不清 state
和 props
的使用場(chǎng)景,記住一個(gè)簡(jiǎn)單的規(guī)則:盡量少地用 state
痢缎,多用 props
胁勺。
沒(méi)有 state
的組件叫無(wú)狀態(tài)組件(stateless component),設(shè)置了 state 的叫做有狀態(tài)組件(stateful component)独旷。因?yàn)闋顟B(tài)會(huì)帶來(lái)管理的復(fù)雜性署穗,我們盡量多地寫(xiě)無(wú)狀態(tài)組件,盡量少地寫(xiě)有狀態(tài)的組件嵌洼。這樣會(huì)降低代碼維護(hù)的難度案疲,也會(huì)在一定程度上增強(qiáng)組件的可復(fù)用性。
更改狀態(tài)(setState)
- react不能直接通過(guò)this.state.xxx = !this.state.xxx來(lái)直接更改狀態(tài)麻养,要用setSate()方法來(lái)更改褐啡。(能修改數(shù)據(jù),但是頁(yè)面不會(huì)渲染)
- setState()有兩個(gè)參數(shù)回溺,第一個(gè)參數(shù)有兩種情況:第一種是一個(gè)對(duì)象春贸;第二種情況是一個(gè)方法。
- setState的第二個(gè)參數(shù)是一個(gè)回調(diào)在里面return一個(gè)對(duì)象遗遵,由于setState是異步的萍恕,想要獲取最新的state要在第二個(gè)參數(shù)的回調(diào)里獲取
- react的setState是異步的,setState里的方法要比它外面的方法后執(zhí)行
import React, { Component } from 'react'
export default class Like extends Component {
constructor () {
super()
this.state = {
isLiked: false
}
}
likedClick = () => {
// setState的第一種寫(xiě)法
// this.setState({
// isLiked: !this.state.isLiked
// })
// 第二種寫(xiě)法是一個(gè)方法车要,可以直接傳上次的狀態(tài)prevState和props
this.setState((prevState) => {
console.log(prevState)
return {
isLiked: !prevState.isLiked
}
}, () => {
// setState是異步的允粤,想要獲取最新的state要在這個(gè)回調(diào)里獲取
console.log(this.state)
})
}
render() {
return (
<div>
<span onClick={this.likedClick}>
{
this.state.isLiked ? '太菜了!??' : '棒棒噠翼岁!??'
}
</span>
</div>
)
}
}
react事件
- 如果要修改state里的值需要使用onChange事件(這是采用駝峰命名的)类垫,然后通過(guò)setState方法來(lái)修改值,否則頁(yè)面上不能輸入
// TodoInput/index.js
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class TodoInput extends Component {
static propTypes = {
btnText: PropTypes.string
}
static defaultProps = {
btnText: '添加TODO'
}
constructor () {
super()
this.state = {
inputValue: 'xxx'
}
// this.addTodo = this.addTodo.bind(this)
}
onInputChange = (e) => {
this.setState({
inputValue: e.currentTarget.value
})
}
addTodo = (id) => {
console.log(this.state,id)
}
render() {
return (
<div>
<input type="text" onChange={this.onInputChange} value={this.state.inputValue}/>
<button onClick={this.addTodo.bind(this,1234)}>{this.props.btnText}</button>
</div>
)
}
}
// App.js
addItem = (todoTitle) => {
console.log(todoTitle)
// this.setState({
// // 添加todo的第一個(gè)方法琅坡,這里不能用push悉患,push返回的是數(shù)組的長(zhǎng)度
// todos: this.state.todos.concat({
// id: Math.random(),
// title: todoTitle,
// isCompleted: false
// })
// })
// 第二個(gè)方法,先把數(shù)組復(fù)制一份
// const newTodos = this.state.todos.slice()
const newTodos = [...this.state.todos]
newTodos.push({
id: Math.random(),
title: todoTitle,
isCompleted: false
})
this.setState({
todos: newTodos
})
}
react里面通過(guò)ref獲取組件或者dom元素,在使用ref之前要先調(diào)用React.createRef來(lái)創(chuàng)建ref榆俺,要在constructor里創(chuàng)建ref
import React, { Component, createRef } from 'react'
import PropTypes from 'prop-types'
export default class TodoInput extends Component {
static propTypes = {
btnText: PropTypes.string
}
static defaultProps = {
btnText: '添加TODO'
}
constructor () {
super()
this.state = {
inputValue: ''
}
// this.addTodo = this.addTodo.bind(this)
// 在constructor里創(chuàng)建ref
this.inputDom = createRef()
}
onInputChange = (e) => {
this.setState({
inputValue: e.currentTarget.value
})
}
addKeyUp = (e) => {
if(e.keyCode === 13){
// console.log(e)
this.addTodo()
}
}
addTodo = () => {
if(this.state.inputValue === ''){
return
}
console.log(this.inputDom)
this.props.addItem(this.state.inputValue)
this.setState({
inputValue: ''
}, () => {
this.inputDom.current.focus()
})
}
render() {
return (
<div>
<input type="text" onChange={this.onInputChange} onKeyUp={this.addKeyUp} value={this.state.inputValue} ref={this.inputDom}/>
<button onClick={this.addTodo}>{this.props.btnText}</button>
</div>
)
}
}