- 首先,这两个框架,都是以组件化的思想进行开发的!
- 从开发团队上进行对比:
- React的开发团队,是Facebook官方大牛团队,团队技术实力雄厚;
- Vue:第一个版本,主要是作者尤雨溪进行维护。
- 从社区方面进行对比:
- React社区早,解决方案多
- Vue社区晚一点,解决方案相对少一点
- 从移动App开发方面:
- 使用React这门技术,可以分分钟转到ReactNative的开发中
- VUE 这门技术,也提供了无缝的转移到移动端开发的体验,通过weex可以使用VUE的语法,进行移动端APP开发
npx create-react-app my-app
cd my-app
npm run start
组件中,必须返回一个根元素
如 下列语法是错误的 !
import React, { Component } from 'react'
export default class App extends Component {
render() {
return (
Hello World!
Hello World!
)
}
}
假如要想在根目录下直接渲染两个标签,可以使用
Fragment
标签,类似vue
中的template
import React, { Fragment } from 'react';
import React, { Fragment } from 'react';
import ReactDOM from 'react-dom';
const App = ()=> {
return (
我就是jsx
我就是jsx
)
}
ReactDOM.render( , document.getElementById('root'))
1.普通渲染
我就是jsx
2.数学表达式
{1 + 1}
3.字符串
{'hello world'}
4.bool类型-无法渲染
{isBoy}
5.使用变量
{msg}
6.三目运算符
{isBoy ? "男生" : "女生"}
7.调用方法
const format = (msg) => {
return '---' + msg + '---';
}
{format(msg)}
8.使用对象
const lamian = {
name: "拉面"
};
{lamian.name}
代码
import React, { Fragment } from 'react';
import ReactDOM from 'react-dom';
const msg = "你们好呀";
const isBoy = false;
const format = (msg) => {
return '---' + msg + '---';
}
const lamian = {
name: "拉面"
};
const App = () => {
return (
我就是jsx
{/* 数字 */}
{1 + 1}
{/* 字符串 */}
{'hello world'}
{/* bool类型 */}
{isBoy}
{/* 使用变量 */}
{msg}
{/* 3目运算符 */}
{isBoy ? "男生" : "女生"}
{/* 调用方法 */}
{format(msg)}
{/* 使用对象 */}
{lamian.name}
)
}
ReactDOM.render( , document.getElementById('root'))
import React from 'react';
import ReactDOM from 'react-dom';
const msg = "nice 天气";
const App = () => {
return (
{
怎么啦 {msg}
怎么啦 {msg}
}
)
}
ReactDOM.render( , document.getElementById('root'))
加强版本
import React from 'react';
import ReactDOM from 'react-dom';
const list = ['', '', '', ''];
const App = () => {
return (
{
{
list.map(function (v) {
return (
{v}
)
})
}
}
)
}
ReactDOM.render( , document.getElementById('root'))
终极版
import React from 'react';
import ReactDOM from 'react-dom';
const list = ['苹果', '香蕉', '雪梨', '西瓜'];
const App = () => {
return (
{
{
list.map(v => {v}
)
}
}
)
}
ReactDOM.render( , document.getElementById('root'))
{
// 这里是单行注释
}
{
/*
这里是多行注释
这里是多行注释
这里是多行注释
这里是多行注释
*/
}
jsx
标签上可以设置绝大部分和以前html
标签一样的属性,如checked
、图片的src
需要注意几个点
html
的class
属性改为className
☁️
html
中label
标签的for
属性改为htmlFor
标签中的自定义属性使用data
自定义属性
渲染 html
字符串 使用 dangerouslySetInnerHTML
属性
- 来啊呀"}}>
bool
类型的值 可以这样用
当属性太多了,可以使用 ...
扩展运算符
const props={
className:"redCls",
"data-index":5
}
展开属性
JSX可以像传统的
HTML
标签一样添加行内样式**,不同的是,要通过对象的方式来实现。并且属性名是以驼峰命名。**
import React from 'react';
import ReactDOM from 'react-dom';
import "./index.css";
const App = () => {
return (
颜色真不错
)
}
ReactDOM.render( , document.getElementById('root'))
JSX俗称 语法糖
,提供了一种更好使用的语法让我们使用,其本质是调用 React.createElement
实现的
React.createElement
,接收3个参数
"div"
{className:"redCls"}
React.createElement
对象或者 React.createElement
数组import React from 'react';
import ReactDOM from 'react-dom';
import "./index.css";
const parentProps = {
className: "redCls",
"data-index": 1000
}
const App = () => {
return React.createElement(
"div",
parentProps,
[
React.createElement(
"span",
null,
"不错啊呀"
),
React.createElement(
"span",
null,
"真好呀"
)
]
)
}
ReactDOM.render( , document.getElementById('root'))
在react中,组件分为两种,类组件 和 函数式组件
1.简单功能 使用 函数式组件
2.复杂功能 使用 类组件
3.组件名都必须大写
import React from 'react'
export default function App() {
return (
)
}
使用es6创建class的方式来实现一个组件类
1.首字母要大写
2.要继承 React中的Component类
3.必须实现render函数,函数内返回标签
4.组件有自己的state和生命周期
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
import "./index.css";
class App extends Component {
render() {
return (
嘿嘿嘿
)
}
}
ReactDOM.render( , document.getElementById('root'))
- 函数式组件性能更高,因为没有生命周期
- 函数式组件更方便进行测试
- 能不用类组件就不用类组件
- 当要使用
state
时,就要使用类组件
在
React
中,状态和属性都可以实现数据动态化
在react中,组件内部的数据是通过
state
来实现和管理
可以理解为
state
就是Vue
中的data
函数式组件没有自己的state
在类组件中,state的声明分为两种方式
class Person extends Component {
// 1 声明 state
state = {
date: "2009",
msg: "天啊天啊"
}
render() {
return (
{/* 2 使用state */}
{this.state.date}
{this.state.msg}
)
}
}
class Person extends Component {
// 1 构造函数中 声明 state
constructor() {
// 1.1 必须在this之前调用super()方法
super();
this.state = {
date: "2009",
msg: "天啊天啊"
}
}
render() {
return (
{/* 2 使用state */}
{this.state.date}
{this.state.msg}
)
}
}
state
的赋值方式通过this.setState
方法 来实现需要注意的是, 不能 使用
this.state.date= 200
直接修改
class Person extends Component {
state = {
date: 2008
}
// 2 事件的声明 要使用箭头函数
handleClick = () => {
// 3 获取state中的日期
let { date } = this.state;
// 4 修改state中的日期
this.setState({
date: date + 1
});
}
render() {
return (
// 1 绑定事件 事件名必须驼峰命名
{this.state.date}
)
}
}
react为了优化性能,将state的赋值代码 改成异步的方式,可以避免反复的设置state而引发的性能损耗问题。
看看下面打印的值
class Person extends Component {
state = {
date: 2010
}
handleClick = () => {
let { date } = this.state;
// 1 修改state中的日期 增加 2000
this.setState({
date: date + 2000
});
// 2 此时这个date还是2010而不是4010
console.log(this.state.date);
}
render() {
return (
{this.state.date}
)
}
}
有时候,我们希望在一设置值的时候,就希望马上得到最新的state的值,那么可以将代码改为下列的写法
给
setState
添加一个回调函数,回调中可以获取到修改后的state
的值
class Person extends Component {
state = {
date: 2008
}
handleClick = () => {
let { date } = this.state;
// 添加一个回调函数
this.setState({
date: date + 3000
}, () => {
// date的值为 5008
console.log(this.state.date);
});
}
render() {
return (
{this.state.date}
)
}
}
有时候,setState还可以接收一个函数,函数内可以实时获取state中的值,不存在延迟
this.setState(preState => {
console.log("上一次的state", preState.date);
return {
date: preState.date + 1000
}
})
props意思是属性,一般存在 父子组件中。用于 父向子传递数据
子组件不能修改接收到的props值
1.声明一个类组件 HomeTop 父组件的数据通过 this.props
来获取
class HomeTop extends Component {
render() {
return (
屋顶的颜色是 {this.props.acolor} 尺寸 {this.props.asize}
)
}
}
2.声明一个函数式组件HomeFooter
,父组件传递的数据 需要在函数的形参props
上接收
const HomeFooter = (props) => {
return 屋底的颜色是 {props.bcolor} 尺寸 {props.bsize}
}
3.声明父组件,并在标签上通过属性的方式进行传递数据
class Home extends Component {
state = {
color: "blue",
size: 100
}
render() {
return (
)
}
}
完整代码
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
class HomeTop extends Component {
render() {
return (
屋顶的颜色是 {this.props.acolor} 尺寸 {this.props.asize}
)
}
}
const HomeFooter = (props) => {
return 屋底的颜色是 {props.bcolor} 尺寸 {props.bsize}
}
class Home extends Component {
state = {
color: "blue",
size: 100
}
render() {
return (
)
}
}
ReactDOM.render( , document.getElementById('root'))
当想要在类组件的构造函数中,获取到props时,需要如下使用
constructor(props) {
super(props);
console.log(props);
}
当父元素没有传递props属性时,子组件可以指定一个默认props属性值来使用。
通过
组件名.defaultProps
来指定
import React from 'react';
import ReactDOM from 'react-dom';
let HomeNav = (props) => {
return 导航为 {props.color}
// 或者
static defaultProps = {
color: "yellow"
}
}
// 指定一个默认属性
HomeNav.defaultProps = {
color: "yellow"
}
class Home extends Component {
state = {
color: "blue"
}
render() {
return (
)
}
}
ReactDOM.render( , document.getElementById('root'))
在一些要求代码更为严格的项目中,父组件传递数据的格式,必须和子组件要求的格式保持一致,否则就认为代码出错
自 React v15.5 起,
React.PropTypes
已移入另一个包中。请使用prop-types
库 代替
npm install prop-types --save
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
// 1 引入 prop-types
import PropTypes from 'prop-types';
let HomeNav = (props) => {
return 导航为 {props.color} 数量为 {props.nums}
}
// 2 指定要求接收的数据格式
HomeNav.propTypes ={
color:PropTypes.string,
nums:PropTypes.number
}
class Home extends Component {
state = {
color: "blue",
nums:100
}
render() {
return (
)
}
}
ReactDOM.render( , document.getElementById('root'))
props.children
可以实现类似vue中的插槽功能
let HomeNav = (props) => {
return (
标题
{props.children}
)
}
class Home extends Component {
render() {
return (
{/* 这里放动态插入的内容 */}
在这个父组件中的子组件内插入一个div包含的内容,需要在子组件中{props.children}才能显示
)
}
}
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
let HomeNav = (props) => {
return (
标题
{props.children}
)
}
class Home extends Component {
render() {
return (
{/* 这里放动态插入的内容 */}
在这个父组件中的子组件内插入一个div包含的内容,需要在子组件中{props.children}才能显示
)
}
}
ReactDOM.render( , document.getElementById('root'))
1.二者都作为 React 内更新视图的依据,只有它们变化时,React 才会进行相应的更新。
2.二者都不可以通过直接赋值的方式更新。
1.更新方式不同:state 通过
setState
方法更新(只能在组件内部更新),props 则通过更新传入的值实现(组件内不可变)。2.state 只维护组件内部的状态,props 让外部维护组件的状态。
尽量少用
state
,尽量多用props
,这样既能提高组件的可复用性,又能降低维护成本
React
元素的事件处理和DOM
元素的很相似,但是有一点语法上的不同:
- React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
- 使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。
<button onclick="activateLasers()">
Activate Lasers
button>
观察以下的this的值 它是
undefined
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
class Home extends Component {
state = {
msg: "大家好呀"
}
handleClick() {
// undefined
console.log(this);
}
render() {
return (
小标题
)
}
}
ReactDOM.render( , document.getElementById('root'))
因为在
react
中,绑定事件 不是原生HTML的dom元素,onClick
只是react帮我们做的一个中间的映射,那么它在处理事件的时候,是不会处理this
的指向的,需要我们手动处理这个this
1.绑定事件的时候 使用
bind
来锁定this
小标题
2.在构造函数中 使用
bind
修改this
指向
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
class Home extends Component {
state = {
msg: "大家好呀"
}
constructor() {
super();
// 重新绑定this
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log(this);
}
render() {
return (
小标题
)
}
}
ReactDOM.render( , document.getElementById('root'))
**3.将事件的执行,改成 箭头函数 的方式 **
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
class Home extends Component {
state = {
msg: "大家好呀"
}
// 修改为箭头函数的方式
handleClick = () => {
console.log(this);
}
render() {
return (
小标题
)
}
}
ReactDOM.render( , document.getElementById('root'))
执行类似以前HTML的dom事件传参一样的功能
React中的写法
1.不推荐,容易导致意外情况
2.推荐写法
受控是指受React控制。可以理解为绑定了
value
或者checked
属性的表单就是受控表单。也可以理解为受控表单就是实现了双向绑定。
对
input
标签和checkbox
标签进行改造
必须同时给与
onChange
事件和绑定value
属性
逻辑中 执行给
inpValue
赋值
handleChangeValue(e) {
this.setState({
inpValue: e.currentTarget.value
})
}
必须同时给与
onChange
事件和绑定value
属性
逻辑中 执行给
inpValue
赋值
handleChkChecked(e){
this.setState({
isChecked:e.currentTarget.checked
})
}
元素 | 值 | 绑定事件 | h获取值 |
---|---|---|---|
|
value="string" |
onChange |
event.target.value |
|
checked={boolean} |
onChange |
event.target.checked |
|
checked={boolean} |
onChange |
event.target.checked |
|
value="string" |
onChange |
event.target.value |
|
value="option value" |
onChange |
event.target.value |
在函数式组件中的第二个参数接收父组件传递过来的ref
export default React.forwardRef(
function Two(props, ref) {
const p = React.createRef();
return (
Two组件
test
)
}
)
很多时候,我们不得不去操作dom元素。如 :
- 管理焦点,文本选择或媒体播放。
- 触发强制动画。
- 集成第三方 DOM 库。
React 16.3 版本后使用
React.createRef()
API 来创建ref
1.在构造函数中获取实例
// myRef 为自定义的名字
this.myRef = React.createRef();
2.在
render
函数中将myRef绑定到对应的html标签或者React组件上
3.后期可以通过 以下代码来获取 该实例的引用
this.myRef.current
有时候我们希望给非受控表单一个初始化值,同时又不希望将它变成受控表单。此时可以使用
defaultValue
和defaultChecked
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
class App extends Component {
state = {
msg: "2008",
isChecked: true
}
render() {
return (
{/* 添加了 defaultValue */}
{/* 添加了 defaultChecked */}
)
}
}
ReactDOM.render( , document.getElementById('root'))
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
class App extends Component {
constructor() {
super();
this.inp = React.createRef();
}
handleClick() {
this.inp.current.focus();
}
render() {
return (
)
}
}
ReactDOM.render( , document.getElementById('root'))
App同级文件夹util中alert.js文件
uitl/alert.js
console.log('文件的代码执行了.....');
export default function(msg){
alert(msg);
}
App.js
import React, { Component } from 'react'
// import alertFunc from './util/alert'
export default class App extends Component {
render() {
return (
)
}
btnAction1 = async () => {
// import('./util/alert')
// .then( ( {default : alertFunc} )=>{
// alertFunc('hello');
// })
const {default : alertFunc} = await import(/* webpackChunkName: 'alert' */'./util/alert');
alertFunc('hello');
}
btnAction2 = async () => {
const {default : conFunc} = await import(/* webpackChunkName: 'console' */'./util/console');
conFunc('test');
}
}
import React, { Component, Fragment } from "react";
import One from "./One";
export default class App extends Component {
render() {
return (
);
}
}
Wrap.js
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class Wrap extends Component {
constructor(props) {
super(props);
console.log(props);
}
render() {
return (
wrap组件
{this.props.children}
)
}
// children 可以为任意类型
static propTypes = {
children: PropTypes.node
}
}
App.js
使用children可以让父组件中子组件内的内容显示,类似vue中的插槽
import React, { Component } from 'react';
import Wrap from './components/Wrap'
class App extends Component {
render() {
return (
hello wrold
hello wrold
hello wrold
hello wrold
)}>
);
}
}
export default App;
构建context的类型 const Context = React.createContext({})
$_开头的属性都是内部私有的属性在js中
Consumer:数据的消费者
Provider:数据的提供者
contextType保留在继承的Component中 所以函数式组件不能 组件.contextType 只能通过Consumer来使用数据
使用context
1.创建context 例:const ColorContext = React.createContext({/默认属性/color:‘blue’})
2.向整颗组件树提供共享的context数据 import ColorContext from ‘./data/…’
3.获取数据 通过Consumer
1.在data/BorderContext.js、ColorContext.js文件中构建context类型
React.createContext({})
import React from 'react'
// 1.构建context的类型
const BorderContext = React.createContext({
border: '1px solid #999'
});
export default BorderContext;
import React from 'react'
// 1.构建context的类型
const ColorContext = React.createContext({
color: 'blue'
});
export default ColorContext;
2.向整颗组件树提供共享的context数据
App.js
数据通过value值携带
import React, { Component } from "react";
import One from './components/One'
import Two from './components/Two'
import './style.css'
import ColorContext from './data/ColorContext'
import BorderContext from './data/BorderContext'
export default class App extends Component {
render() {
// 2.向整颗组件树提供共享的context数据
return (
)
}
}
3.获取数据
获取数据的两种方式
1.通过context实例中的Consumer属性
注意:Consumer中包裹一个函数并返回内容 函数形参就是value传递过来的数据,可以嵌套使用获取多个value值,第二种方式就不行了
import React, { Component } from "react";
import ColorContext from "../data/ColorContext";
import BorderContext from "../data/BorderContext";
class One extends Component {
render() {
return (
One
{({ border }) => (
{({ color }) => (
test
)}
)}
);
}
getDom = (value) => {
return test;
};
}
export default One;
2.通过设置组件的contxetType属性
Two.contextType = 导入的那个context实例
在构造函数的第二个参数中获取通过父组件Provider中value属性共享过来的数据
import React, { Component } from 'react'
import ColorContext from '../data/ColorContext'
export default class Two extends Component {
constructor(props, context) {
super(props);
console.log(context);
}
render() {
return (
Two
)
}
}
// ColorContext是导入进来的context实例
Two.contextType = ColorContext;
在
React
中,数据的传递方式有以下几种
- 父传子 通过属性
- 子传父 通过事件
- 兄弟间传值 先传给父再传给兄弟
- 复杂关系
redux
在类组件中接收
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
class HomeTop extends Component {
render() {
return (
屋顶的颜色是 {this.props.acolor} 尺寸 {this.props.asize}
)
}
}
class Home extends Component {
state = {
color: "blue",
size: 100
}
render() {
return (
)
}
}
ReactDOM.render( , document.getElementById('root'))
需要注意的是,改成 函数式组件, props需要在子组件上通过形参的方式接收和使用,不再是通过
this.props
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
// 函数式组件 通过props形参来接收
function HomeTop(props) {
return (
屋顶的颜色是 {props.acolor} 尺寸 {props.asize}
)
}
class Home extends Component {
state = {
color: "blue",
size: 100
}
render() {
return (
)
}
}
ReactDOM.render( , document.getElementById('root'))
通过触发事件的方式
1.在父组件中 给子组件挂载一个自定义事件
onChangeColor
2.子组件自己触发事件
const GreenBtn = (props) => {
setInterval(() => {
// 触发 父组件上的自定义事件
props.onChangeColor("green");
}, 1000);
return (
)
}
3.父组件上监听到了事件触发
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
const GreenBtn = (props) => {
setInterval(() => {
// 触发 父组件上的自定义事件
props.onChangeColor("green");
}, 1000);
return (
)
}
class App extends Component {
state = {
color: "red"
}
// 监听事件触发
changeColor = (color) => {
this.setState({
color
})
}
render() {
return (
)
}
}
ReactDOM.render( , document.getElementById('root'))
思路是
组件A ->父组件->组件B
- 组件A通过触发事件,将数据传递个父组件
- 父组件通过属性props的方式将数据传递给子组件B
const GreenBtn = (props) => {
const handleClick = () => {
props.onChangeColor("green");
}
return (
)
}
class App extends Component {
state = {
color: "red"
}
changeColor = (color) => {
this.setState({
color
})
}
render() {
return (
)
}
}
const RedBtn = (props) => {
const handleClick = () => {
props.onChangeColor("red");
}
return (
)
}
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
// 1 组件A
const GreenBtn = (props) => {
const handleClick = () => {
props.onChangeColor("green");
}
return (
)
}
// 2 组件B
const RedBtn = (props) => {
const handleClick = () => {
props.onChangeColor("red");
}
return (
)
}
// 3 组件C
class App extends Component {
state = {
color: "red"
}
changeColor = (color) => {
this.setState({
color
})
}
render() {
return (
)
}
}
ReactDOM.render( , document.getElementById('root'))
React
组件的生命周期可以分为3种状态
- 挂载时
- 更新时
- 卸载时
当组件实例被创建并插入
DOM
中时,其生命周期调用顺序如下
- constructor()
- static getDerivedStateFromProps() 了解即可
- render()
- componentDidMount()
组件的构造函数,在组件被创建的时候会执行,一般有以下作用:
1.通过给
this.state
赋值对象来初始化内部state
。(不要使用this.setState()
来初始化数据)
this.state={key:val};
2.为事件处理函数绑定实例
this.handleClick=this.handleClick.bind(this);
render
函数负责将模板渲染到页面,一般来说,当props
和state
发生改变时,都会重新触发render
,需要注意的是,不要在render函数中 修改props
和state
,否则会导致死循环!!
componentDidMount
会在组件挂载后(插入 DOM
树中)立即调用。依赖于 DOM 节点的初始化应该放在这里。一般用来在这里发送异步请求。
当组件的 props 或 state 发生变化时会触发更新,会按顺序调用以下的生命周期事件
- static getDerivedStateFromProps() 了解即可
- shouldComponentUpdate()
- render()
- getSnapshotBeforeUpdate() 了解即可
- componentDidUpdate()
当props
和state
发生改变时触发,返回true
则表示允许执行 render
,返回 false
这表示 不允许运行render
接收两个参数,nextState
和nextProps
,表示修改后的 state
和props
值。一般用在提高性能使用。如:
当state
中的某个值为偶数才触发render
,为奇数则不触发。
同上述说明
componentDidUpdate()
会在更新后会被立即调用。首次渲染不会执行此方法
当组件更新后,可以在此处对 DOM 进行操作。 如 判断是否触底了,再进行其他操作。
会接收3个参数
props
对象state
对象在组件卸载及销毁之前直接调用,在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求等。也不要在该事件中 执行 setState
操作,无效。
react中可以使用以下方式为元素添加样式
1.行内 无法实现伪类
行内样式
2.全局样式 导致全局污染
import './css/index.css'
3.styled-jsx
4.css module(推荐)
npm install --save-dev node-sass
App.module.scss
文件,文件名中 module必须加上,不然识别不了这个模块文件,加入以下内容.app {
.demo {
color:red
}
}
import AppCSS from "./App.module.scss"
<div className={AppCSS.app}>
<h1 className={AppCSS.demo}>123h1>
div>
实现类似vue中,局部样式
customize-cra 负责合并webpack配置文件
styled-jsx 负责 实现局部样式
react-app-rewired 负责重新改写 项目的运行命令
npm install customize-cra styled-jsx react-app-rewired --save
填入以下内容
// config-overrides.js
const {
override, addBabelPlugins } = require('customize-cra');
module.exports = override(
...addBabelPlugins(
[
"styled-jsx/babel"
]
)
)
修改package.json
配置文件的script
字段
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"
}
我变颜色啦
import React from 'react';
import './App.css';
const Header = () => {
return (
我没有变颜色哦
)
}
function App() {
return (
我变颜色啦
);
}
export default App;
1.添加依赖
npm install styled-jsx-plugin-less@alpha less --save
2.修改配置文件 config-overrides.js
// config-overrides.js
const {
override, addBabelPlugins } = require('customize-cra');
module.exports = override(
...addBabelPlugins(
[
"styled-jsx/babel",
{
"plugins": ["styled-jsx-plugin-less"] }
]
)
)
3.编写less语法
React Router
英文官网
中文手册
npm install react-router-dom --save
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
function Index() {
return Home
;
}
function About() {
return About
;
}
function Users() {
return Users
;
}
function AppRouter() {
return (
);
}
export default AppRouter;
从上述案例也可以看出,在
react-route-dom
中,存在以下几个常用的对象
名称 | 作用 |
---|---|
BrowserRouter | 使用传统的url模式 |
HashRouter | 使用哈希路由的模式 |
Route | 用来装路由对应的内容 |
Link | 用来指定路由路径的 |
Swich | 只可以匹配到一个路由 |
Redirect | 路由重定向 |
exact | 精确匹配路由 |
match | 路由对象的一个存放路由参数的的属性 |
location | 路由对象的一个存放URL信息的属性 |
history | 路由对象的一个控制路由跳转的属性 |
component | 路由渲染的一种方式 |
render | 路由渲染的一种方式 |
children | 路由渲染的一种方式 |
1.导入路由必备对象
// 1 导入 哈希路由 路由-内容标签 路由-链接标签
import {
HashRouter as Router, Route, Link } from "react-router-dom";
function Index() {
return <h2>Home</h2>;
}
function About() {
return <h2>About</h2>;
}
function Users() {
return <h2>Users</h2>;
}
3.使用路由
function AppRouter() {
return (
{/* 1 使用Router将Link 和 Route包含起来 */}
{/* 3 链接对应的内容 */}
);
}
React的路由默认匹配规则 类似:
if(link.indexOf("/")){
...
}
if(link.indexOf("/about/")){
...
}
当 在route
上加上 exact
属性后,变成
if( link === "/" ){
...
}
if( link === "/about/" ){
...
}
在 1 的基础上 不加 exact
加 Switch
后 变成了
{/* 链接对应的内容 */}
它的原理类似:if-else if ...
if(link.indexOf("/")){
...
}
else if(link.indexOf("/about/")){
...
}
有时候,为了防止用户输入一个不存在的路由,我们会设置一个 404组件
function PageNotFound() {
return 404啦
}
{/* 设置一个404页面 */}
因为,用户不可能自己输入一个 url:404
,用户更多的是输入一些错乱的路径 dfasdfsdf
等
因此,我们需要一个重定向 Redirect
import {Redirect } from "react-router-dom";
{/* 可以匹配到除以上的路由规则外的所有路由 */}
在React的中,有3种渲染方式
- component 每次都会触发组件的对应的生命周期
- render 内联模式渲染 性能会更高 props需要传递到函数内
- children 会一直渲染 不管匹配模式
}/>
}/>
有时候我们需要在链接中和内容中传递参数
通过 route
标签的 path属性来指定参数
通过 props属性中的 history
对象来进行 逻辑跳转
const {id}=e.currentTarget.dataset;
props.history.push(`/users/${id}/`);
通过 props
对象的match
对象来获取参数
class UserDetail extends Component {
render() {
const { id } = this.props.match.params;
return {id}
}
}
{
const obj = {
a: 1,
b: 2,
c: 3,
d: 4,
val: '123'
}
return
}}/>
主入口index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
// 1. 引入一个 连接仓库和组件的连接组件
import { Provider } from "react-redux"
// 2. 引入仓库
import store from "./store"
//3. 配置到App节点上
ReactDOM.render(
,
document.getElementById('root')
);
App.js
import React, { Component } from 'react'
// 1. 引入连接器 将属性和行为 注入到组件中
import {connect} from "react-redux"
// 引入 actions
import { addAction,subAction} from "./store/actionCreator"
class App extends Component {
render() {
return (
{this.props.num}
)
}
}
// 将仓库中的 数据 映射到 组件中
const mapStateToProps = (state)=>{
return {
num: state.numReducer.num
}
}
// 将行为 注入到 组件中
const mapDispatchToProps = (dispatch)=>{
return {
// +
addNum:function(){
dispatch(addAction);
},
subNum:function(){
dispatch(subAction);
}
// -
}
}
export default connect(mapStateToProps,mapDispatchToProps)(App)
store/index.js
// 1. 引入 创建 仓库的 api
import {
createStore } from "redux"
// 2. 引入 reducer 初始化的数据以及纯函数的操作
import rootReducer from "./reducer"
// 创建仓库 并且导出仓库
export default createStore(rootReducer);
store/reducer/index.js合并reducer
// 1. 引入合并reducer 的函数
import { combineReducers } from "redux"
//2. 引入合并的reducer
import numReducer from "./numReducer"
//3. 导出合并之后 reducer
export default combineReducers({numReducer:numReducer})
store/reducer/numReducer.js
import {
ADD,SUB} from "../actionTypes"
const defaultState = {
num:888
}
export default function(state=defaultState,action){
// 纯函数操作
let newState = Object.assign({
},state);
switch(action.type){
case ADD:
newState.num = state.num + action.payload.num;
break;
case SUB:
newState.num = state.num + action.payload.num;
break;
default:
break;
}
return newState;
}
store/actionCreator/index.js
提取
// { type:"ADD",payload:{ num:1 }}
import {
ADD,SUB} from "../actionTypes"
const addAction = {
type: ADD, payload: {
num: 1 } };
const subAction = {
type: SUB, payload: {
num: -1 } };
export {
addAction, subAction };
store/actionTypes/index.js
同上 可以提取也可以不提取
const ADD = "ADD"
const SUB = "SUB"
export {
ADD,SUB}
react Hook 是react 新推出的功能
正常版本 class 创建组件 定义state 用setState 来改变组件样式
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
render() {
return (
You clicked {this.state.count} times
);
}
}
无状态函数组件 则可以使用useState来添加状态
//1. 引入一个 hook useState
import React, { useState } from "react";
export default function App() {
// num 是 相当于 类组件中 state 中的变量 setNum 改变变量的函数
// useState 是 状态的钩子 括号里面的值 是 num 的初始值
let [num, setNum] = useState(999);
return (
{num}
);
}
多个state的情况
//1. useState 允许在函数式组件中使用 class 中的状态
// 引入 钩子函数 useState
import React, { useState } from "react";
export default function App() {
// useState hook 中 有多个 state 的情况
// 数字
let [num, setNum] = useState(888); // 888 是 num 初始化值 setNum 改变 num的函数
// 字符串
let [title, setTitle] = useState("标题");
// 对象
let [todos, setTodos] = useState([
{ id: 1, text: "事项一", completed: true },
{ id: 2, text: "事项二", completed: true },
{ id: 3, text: "事项三", completed: true },
]);
return (
{/* setState({ num: num+1}) */}
{title}
{num}
{/* Todo list */}
{
todos.map(v=>- {v.text}
)
}
);
}
hook版本的todos
// 引入 useState hook
import React, { useState } from "react";
export default function App() {
// let todos = [{
// id:1,
// text:"",
// completed:false
// },{
// id:2,
// text:"☀",
// completed:false
// }]
// todos hook
let [todos, setTodos] = useState([
{
id: 1,
text: "",
completed: false,
},
{
id: 2,
text: "☀",
completed: false,
},
]);
// inputVal
let [inputVal, setInputVal] = useState("");
return (
{
console.log(e.target.value);
// this.setState({ inputVal: e.target.value})
setInputVal(e.target.value);
}}
>
{/* todos 的列表的开始 */}
{todos.map((v, index) => (
{v.text}
))}
{/* todos 的列表的结束 */}
);
}
import React, { Component } from 'react'
export default class App extends Component {
state = {
num : 888
}
componentDidMount(){
// 在组件 挂载到 dom 树上的时候 修改页面的标题
document.title = "effect hook 例子";
}
// 组件更新的时候
componentDidUpdate(){
document.title = this.state.num;
}
handleClick= (params) => {
this.setState({
num: this.state.num + 1
})
}
render() {
return (
{this.state.num}
)
}
}
import React, { Component } from "react";
class Demo extends Component {
eventClick = (params) => {
console.log("document 被点击了");
}
// 子组件中注册了一个事件
componentDidMount() {
document.addEventListener("click", this.eventClick);
}
// 组件卸载的时候 移除监听器
componentWillUnmount(){
document.removeEventListener("click",this.eventClick);
}
render() {
return 子组件;
}
}
export default class App extends Component {
state = {
show: true,
};
handleClick = (params) => {
this.setState({
show: !this.state.show,
});
};
render() {
return (
{this.state.show ? : ""}
);
}
}
// useEffect 是生命周期的 Hook
// 它能够为函数式组件提供 生命周期的功能
import React, { useState, useEffect } from "react";
export default function App() {
let [num, setNum] = useState(888);
let [title, setTitle] = useState("");
// useEffect 其实就是 componentDidMount 和
// componentDidUpdate 合体
// 里面的回调函数 会在 componentDidMount 和componentDidUpdate 生命周期执行的时候运行
// 关注点的分离
// 调试时候会更方便
// 设置标题
useEffect(() => {
if (num === 888) document.title = "b( ̄▽ ̄)d";
else document.title = num;
});
// 添加监听时间
useEffect(() => {
document.addEventListener("click", function () {
console.log("document 被点击了");
});
});
return (
{num}
);
}
vscode-styled-jsx
组件中的颜色代码高亮
ES7 React/Redux/GraphQL/… 快速创建组件等
Prettier shift+alt+f格式化代码
1.
React Developer Tools
2.
Redux DevTools