npx create-react-app “项目名”
react用到的第三方包
-
classnames
-
styled-components 将css独立来写的第三方包
-
prop-types:props的类型检测工具
-
axios
在jsx里面写js代码就加一个{} -
创建一个简单的react:
import React from "react" import ReactDOM from "react-dom" // const app = <h1>welcome first!</h1> // jsx语法,不需要加引号 const createApp = (props) => { return ( <div> {/* {只要在jsx里面写js代码就要加一层花括号注释也要用花括号} */} <h1>欢迎来到{props.title}</h1> <p>优秀的{props.title}</p> </div> ) } const app = createApp({ title: '德莱联盟' }) ReactDOM.render( app, document.querySelector("#root") )
react定义组件的方式:
- 箭头函数,组件的首字母大写
- 使用类组件,可以嵌套
-
<1> 箭头函数方法:
// react创建组件的第一个方式:箭头函数,这个名字要大写 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:
// 定义组件的第二个方法:使用类继承React.Component import React, { Component } from "react" import { render } from "react-dom" class App extends Component { render () { console.log(this.props) //{title: "类组件是继承React.Component的"} 参数传递就用this.props return ( <div> <h1>类组件</h1> <p>{this.props.title}</p> </div> ) } } // render 是ReactDOM提供的方法,这个方法通常只会使用一次 render( <App title="类组件是继承React.Component的"/>, document.querySelector("#root") ) // 16版本以前创建组件的方法 // 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原理,通过React.createElement的方式创建元素,有无限个参数,但前两个是固定的,第一个标签名,第二个是标签的属性,剩下的都是子元素
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到底怎么写?' ) ) ) } } // const appVDom = { // tag: "div", // attrs: { // className: "app", // id: "appRoot" // }, // children: [ // { // tag: "h1", // attrs: { // className: "app" // }, // children: [ // "jsx原理" // ] // }, { // tag: "p", // attrs: null, // children: [ // "jsx到底怎么写呢?" // ] // } // ] // } render( <App />, document.querySelector("#root") )
react里的css
-
使用style标签内联创建
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里要写成className</li> <li className={classNames('a',{'b': true,'c': false})}>要动态添加className使用classnames第三方包</li>// 该li的class只有a,b没有c </ol> </div> ) } } render( <App />, document.querySelector("#root") )
-
使用class方式,但是要写成className,样式写在css文件中,所有要先import引入
第三方的css包:1、classnames,可以动态添加不同的className -
styled-components 将css独立来写的第三方包
React项目组件化
快捷键:rcc react的class component;rfc react的function component
-
整个单页应用的入口文件:App.js
-
入口文件:index.js
-
其余的子组件包在App下面
-
在src下新建一个components目录,里面包含所有的子组件,一个子组件就是一个文件夹,用组件名作为文件夹名字;同时,在components的根目录下还有一个index.js文件,用来引入和导出所有的子组件,在App.js里面引入所有的组件。
- 在src下创建一个services文件夹,里面有apis.js和index.js文件。apis.js是统一管理接口的文件
组件化开发React todolist, 项目开发中的组件的基本目录结构基本上是这样的:
> /your-project
>
> - src
> - …
> - components
> - YourComponentOne
> - index.js/YourComponentOne.js
> - YourComponentTwo
> - index.js/YourComponentTwo.js
> - index.js 用于导出组件
注意:一个组件只干一件事情 ,所以TodoList和TodoItem要做成两个组件,这样也方便于后期理解shouldComponentUpdate
-
一个组件里的return只能有一个根元素 ,因此react里可以使用Fragment(需要import引入,是一个空标签),或者直接使用空标签
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 /> // </> ) } }
两种导出方式:
//第一种,当需要对这里引入的组件进行处理再导出时使用
// 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'
组件的数据挂载方式
-
通过props传递
function组件直接通过props.xxx,class组件通过this.props.xxx传递import React from 'react' export default function TodoHeader(props) { console.log(props)// {title: "待办事项"} return ( <h1> {props.title} </h1> ) }
属性(props)
props.children 组件标签内的内容
// App.js
export default class App extends Component {
render() {
return (
<Fragment>
<TodoHeader desc="完成今天所有的事">
待办事项列表
</TodoHeader>
<TodoInput btnText="ADD"/>
<TodoList />
</Fragment>
// TodoHeader/index.js
export default function TodoHeader(props) {
console.log(props) // {desc: "完成今天所有的事", children: "待办事项列表"}
return (
<h1>
{props.children}
</h1>
)
}
-
prop-types:props的类型检测工具
// 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组件写法:
// 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' //组件默认值 } //TodoHeader.defaultProps = { // desc: '如果能重来' //} //funciton组件写法 render() { return ( <div> <input type="text"/><button>{this.props.btnText}</button> </div> ) } }
state
组件内部状态定义用state,有两种定义方法 ,而props是外部传入的属性.只有class组件才有this和state
// App.js
export default class App extends Component {
// 第一张定义方式
// state = {
// title: '待办事项'
// }
// 第二种定义方法
constructor () {
super()
this.state = {
title: '待办事项',
desc: '完成今天所有的事'
}
}
render() {
return (
<Fragment>
<TodoHeader desc={this.state.desc}>
{this.state.title}
</TodoHeader>
组件的分类:
-
class组件和function组件
-
受控组件、不受控组件、半受控组件(通过props传入属性,在组件内部没法修改的叫受控组件,通过state定义的状态不是外部传入的时不受控组件)
-
不能跨组件传递props,必须一层一层的传
-
组件模板渲染语法
{/* {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标签的内容
dangerouslySetInnerHTML,类似于vue的v-html,输出不带HTML标签的内容
属性vs状态
-
相似点:都是纯js对象,都会触发render更新,都具有确定性(状态/属性相同,结果相同)
-
不同点:
1.属性能从父组件获取,状态不能
2.属性可以由父组件修改,状态不能
3.属性能在内部设置默认值,状态也可以
4.属性不在组件内部修改,状态要改
5.属性能设置子组件初始值,状态不可以
6.属性可以修改子组件的值,状态不可以
-
state 的主要作用是用于组件保存、控制、修改自己的可变状态。state 在组件内1部初始化,可以被组件自身修改,而外部不能访问也不能修改。你可以认为 state 是一个局部的、只能被组件自身控制的数据源。state 中状态可以通过 this.setState方法进行更新,setState 会导致组件的重新渲染。
-
props 的主要作用是让使用该组件的父组件可以传入参数来配置该组件。它是外部传进来的配置参数,组件内部无法控制也无法修改。除非外部组件主动传入新的 props,否则组件的 props 永远保持不变。
-
如果搞不清 state 和 props 的使用场景,记住一个简单的规则:尽量少地用 state,多用 props。
-
没有 state 的组件叫无状态组件(stateless component),设置了 state 的叫做有状态组件(stateful component)。因为状态会带来管理的复杂性,我们尽量多地写无状态组件,尽量少地写有状态的组件。这样会降低代码维护的难度,也会在一定程度上增强组件的可复用性。
更改状态(setState)
-
react不能直接通过this.state.xxx = !this.state.xxx来直接更改状态,要用setSate()方法来更改。(能修改数据,但是页面不会渲染)
-
setState()有两个参数,第一个参数有两种情况:第一种是一个对象;第二种情况是一个方法。
-
setState的第二个参数是一个回调在里面return一个对象,由于setState是异步的,想要获取最新的state要在第二个参数的回调里获取
-
react的setState是异步的,setState里的方法要比它外面的方法后执行
import React, { Component } from 'react' export default class Like extends Component { constructor () { super() this.state = { isLiked: false } } likedClick = () => { // setState的第一种写法 // this.setState({ // isLiked: !this.state.isLiked // }) // 第二种写法是一个方法,可以直接传上次的状态prevState和props this.setState((prevState) => { console.log(prevState) return { isLiked: !prevState.isLiked } }, () => { // setState是异步的,想要获取最新的state要在这个回调里获取 console.log(this.state) }) } render() { return ( · <div> <span onClick={this.likedClick}> { this.state.isLiked ? '太菜了!👍' : '棒棒哒!👎' } </span> </div> ) } }
react事件
如果要修改state里的值需要使用onChange事件(这是采用驼峰命名的),然后通过setState方法来修改值,否则页面上不能输入
// 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的第一个方法,这里不能用push,push返回的是数组的长度
// todos: this.state.todos.concat({
// id: Math.random(),
// title: todoTitle,
// isCompleted: false
// })
// })
// 第二个方法,先把数组复制一份
// 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里面通过ref获取组件或者dom元素,在使用ref之前要先调用React.createRef来创建ref,要在constructor里创建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里创建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>
)
}
}