React 学习笔记
[TOC]
React 学习笔记
创建React项目
JS版本
$ npx create-react-app my-app
可以按照提示自动执行npm install
TS版本
# 记录一个坑, 使用yarn创建的项目报错, 不要使用
$ npx create-react-app my-app --template typescript
配置React项目
如果你需要使用到下面所述的技术, 请参照, 否则可以直接开始使用项目, 无需配置
配置less(已不使用该种方式进行配置, 该种方式缺点甚多)
适用于JS项目, TS项目
默认情况下React项目已经配置好Scss, 但是没有配置less, 所以我们必须要手动配置
安装必要包
$ yarn add less less-loader
配置webpack
使用create-react-app脚手架创建的项目默认情况下会隐藏webpack配置文件, 所以第一步就是暴露配置文件
暴露配置文件
第一步, 如果存在git未提交的文件先要提交
$ git add .
$ git commit -m "保存所有更改, 以准备使用yarn eject"
第二部, 暴露所有配置文件(会暴露包括webpack在内的所有配置文件, 该操作不可逆)
$ yarn eject
根据提示输入y即可开始执行
配置相关文件
打开/config/webpack.config.js并执行以下修改(建议使用搜索功能找到响应位置)
配置less-loader
第一步, 添加用于匹配less文件的整个表达式
// 被该行注释包裹的内容没有修改↓
// style files regexes
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
// 被该行注释包裹的内容没有修改↑
// 新加入以下内容
const lessRegex = /\.less$/;
const lessModuleRegex = /\.module\.less$/;
第二部, 添加规则, 以应用上表达式
// 被该行注释包裹的内容没有修改↓
// Adds support for CSS Modules, but using SASS
// using the extension .module.scss or .module.sass
{
test: sassModuleRegex,
use: getStyleLoaders({
importLoaders: 3,
sourceMap: isEnvProduction ?
shouldUseSourceMap :
isEnvDevelopment,
modules: {
mode: 'local',
getLocalIdent: getCSSModuleLocalIdent,
},
},
'sass-loader'
),
},
// 被该行注释包裹的内容没有修改↑
// 新加入以下内容
{
test: lessRegex,
exclude: lessModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
modules: true,
sourceMap: isEnvProduction && shouldUseSourceMap
},
"less-loader"
),
sideEffects: true
},
{
test: lessModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent
},
"less-loader"
)
},
至此配置完成
(可选)测试是否配置成功
测试文件
// /scr/Home/index.js
import style from './style.less';
function Home(){
console.log(style);
return(
<div className={style.root}>
<h1>Home page</h1>
</div>
)
}
export default Home;
// /scr/Home/style.less
.root{
h1{
color: red;
}
}
需要注意的是使用less和使用传统的css使用方式略有区别
// CSS
import './style.css'
...省略
<div className="root">
<h1>Home page</h1>
</div>
// less
import style from `./style.less`
...省略
<div className={style.root}>
<h1>Home page</h1>
</div>
(可选)配置使用'@'访问scr目录(已不使用该种方式进行配置, 该种方式缺点甚多)
适用于JS项目, TS项目
暴露配置文件
必须先暴露配置文件, 如果还没有这么做, 请参考上面文章中的配置lsss.配置webpack.暴露配置文件
配置webpack
打开/config/webpack.config.js并执行以下修改(建议使用搜索功能找到响应位置)
// 被该行注释包裹的内容没有修改↓
alias: {
// Support React Native Web
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
'react-native': 'react-native-web',
// Allows for better profiling with ReactDevTools
...(isEnvProductionProfile && {
'react-dom$': 'react-dom/profiling',
'scheduler/tracing': 'scheduler/tracing-profiling',
}),
...(modules.webpackAliases || {}),
// 被该行注释包裹的内容没有修改↑
// 添加以下内容
'@': path.resolve('src'),
// 添加以上内容
},
配置项目代码检查和风格统一(React + TS + Prettier + ESlint + Tailwind + Stylelint)
安装并配置Pretter(此步骤无需TS项目环境也可单独使用)
前置条件: 无
适用于JS项目, TS项目
安装
- 安装
prettier到项目
$ npm install prettier -D
- 安装Visual Code中的插件
Prettier - Code formatter

配置
- 在项目
根目录创建.prettierignore并填入如下内容
# /.prettierignore
# Ignore artifacts:
node_modules
dist
.vscode
.prettierignore
- 在项目
根目录创建.prettierrc.js并填入如下内容
// .prettierrc.js
module.exports = {
printWidth: 140,// 换行长度, 超过这个长度要求强制换行
tabWidth: 2,// tab缩进长度
semi: true,// 强制分号结尾
singleQuote: true,// 强制使用单引号
trailingComma: 'none',// 强制使用拖尾逗号
bracketSpacing: true,// 强制在对象字面量的属性中键和值之间使用空格
bracketSameLine: false,// 将多行 HTML(HTML、JSX、Vue、Angular)元素放在最后一行的末尾,而不是单独放在下一行(不适用于自闭合元素)。
arrowParens: 'always',// 强制箭头函数参数使用圆括号
insertPragma: false,// 在文件顶部插入一个特殊的标记,标记指定文件已使用Prettier格式化。
requirePragma: false,// 强制在文件开头插入 @format 注释
useTabs: false// 使用tab缩进
};
- 在项目
根目录文件夹.vscode(如果已存在则无需创建), 并在该文件夹下创建settings.json(如果已存在则无需创建)
// .vscode/setting.json
{
// (其他配置无需修改)
// prettier规则使用当前目录的.prettierrc.js
"prettier.configPath": ".prettierrc.js",
// 保存的时候自动格式化
"editor.formatOnSave": true,
}
测试
此时在任意js文件以及其他支持的文件输入代码并且保存即可成功格式化代码
安装Eslint并配置ESlint + Pretter
前置条件: 已经成功配置Prettier
适用于TS项目
安装
- 安装eslint包以及eslint用得到的相关插件
# 使用npm安装
npm i eslint -D
# 用于eslint配置文件中的parser配置:插件@typescript-eslint/parser让ESLint 对 TypeScript 的进行解析。
npm i @typescript-eslint/parser -D
# 用于eslint配置文件中的extends配置:为了防止eslint和prettier的规则发生冲突,我们需要集成两者则设置为['plugin:prettier/recommended']。
npm i eslint-config-prettier eslint-plugin-prettier -D
# 用于eslint配置文件中的plugins配置:
# @typescript-eslint:包含了各类定义好的检测Typescript代码的规范。
# react-hooks:为了检测和规范React hooks的代码规范检查的插件。
# eslint-plugin-react:为了检测和规范React代码的书写的插件。
npm i eslint-plugin-react eslint-plugin-react-hooks @typescript-eslint/eslint-plugin -D
# 使用yarn安装
yarn add @typescript-eslint/parser eslint-config-prettier eslint-plugin-prettier eslint-plugin-react eslint-plugin-react-hooks @typescript-eslint/eslint-plugin -D
- 安装Visual Code中的插件
ESLint

配置
.eslintrc.js用于对ESlint做配置, .eslintignore用于配置ESlint对哪些文件不做格式化
- 在项目
根目录创建.eslintrc.js并填入如下内容
module.exports = {
parser: '@typescript-eslint/parser', // 定义ESLint的解析器
extends: ['plugin:prettier/recommended'], //定义文件继承的子规范
plugins: ['@typescript-eslint', 'react-hooks', 'eslint-plugin-react'], //定义了该eslint文件所依赖的插件
env: {
//指定代码的运行环境
browser: true,
node: true
},
settings: {
//自动发现React的版本,从而进行规范react代码
react: {
pragma: 'React',
version: 'detect'
}
},
parserOptions: {
//指定ESLint可以解析JSX语法
ecmaVersion: 2019,
sourceType: 'module',
ecmaFeatures: {
jsx: true
}
},
rules: {
// 自定义的一些规则
'prettier/prettier': 'error',
'linebreak-style': ['error', 'unix'],
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
'react/jsx-uses-react': 'error',
'react/jsx-uses-vars': 'error',
'react/react-in-jsx-scope': 'error',
'valid-typeof': [
'warn',
{
requireStringLiterals: false
}
]
}
};
- 在项目
根目录创建.eslintignore并填入如下内容
# Ignore artifacts:
node_modules
dist
.vscode
.prettierignore
scripts
config
public
*.md
- 在
.vscode/settings.json下做如下配置
{
// prettier规则使用当前目录的.prettierrc.js
"prettier.configPath": ".prettierrc.js",
"eslint.options": {
"extensions": [".js", ".ts", ".tsx", "jsx", "html"]
},
// 保存的时候自动格式化
"editor.codeActionsOnSave": {
// 保存时使用eslint进行格式化
"source.fixAll.eslint": true
},
// 应用eslint后对样式文件的格式化失效了, 手动样式文件格式化配置如下
"[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"[less]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
}
测试
在支持的文件下如果代码有错误应该能看到ESlint的错误提示
安装并配置Taiwind
安装
$ yarn add -D tailwindcss postcss autoprefixer
$ npx tailwindcss init -p# 该命令会在根目录创建配置文件tailwind.config.js
配置
配置tailwind
在根目录找到tailwind.config.js并配置为如下
module.exports = {
content: [// 目的是清洗用不到的样式
"./src/**/*.{js,jsx,ts,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
引入tailwind
在scr目录下找到index.css文件并在头部加入如下配置(如果没有该文件请找到创建React根脚本文件, 并找到其对应的样式文件, 如果没有请创建一个)
@tailwind base;
@tailwind components;
@tailwind utilities;
在React根脚本文件引入该样式
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import reportWebVitals from './reportWebVitals';
import './index.css';// 确保加入这一行
ReactDOM.render(
...
);
测试
请参照官方文档进行测试
将样式文件交给stylelint进行格式化
安装
yarn add -D stylelint stylelint-config-standard
配置
在项目根目录创建stylelint.config.js并填入如下内容
module.exports = {
extends: ['stylelint-config-standard'],
rules: {
'at-rule-no-unknown': [
true,
{
ignoreAtRules: ['tailwind', 'apply', 'variants', 'responsive', 'screen']
}
],
'declaration-block-trailing-semicolon': null,
'no-descending-specificity': null
}
};
禁止vscode使用默认规则对样式文件并检查.vscode/setiings.json
// .vscode/setting.json
{
"prettier.configPath": ".prettierrc.js",
"eslint.options": {
"extensions": [".js", ".ts", ".tsx", "jsx", "html"]
},
// 保存的时候自动格式化
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
// 如果存在类似这样的配置请将其删除
// "[css]": {
// "editor.defaultFormatter": "esbenp.prettier-vscode",
// "editor.formatOnSave": true
// },
// "[less]": {
// "editor.defaultFormatter": "esbenp.prettier-vscode",
// "editor.formatOnSave": true
// },
// 加入如下行
"css.validate": false,
"less.validate": false,
"scss.validate": false
}
测试
如果一切顺利, 你就可以在样式文件中看到错误提示, 以及保存后会自动修正错误的格式
启动React项目
启动项目
# 在项目目录输入如下命令即可启动项目, 默认端口3000
$ npm start
访问项目
访问localhost: 3000即可启动项目
使用React
使用Hooks
useState
基础使用
import { useState } from 'react';// Hook 是一个特殊的函数,它可以让你“钩入” React 的特性
function StateHook(param) {
// 为什么不适用createState这个名字来表示创建? "Create" 可能不是很准确,因为 state 只在组件首次渲染的时候被创建。
// 是否需要创建多个state变量来保持性能? 你不必使用多个 state 变量。State 变量可以很好地存储对象和数组,因此,你仍然可以将相关数据分为一组。然而,不像 class 中的 this.setState,更新 state 变量总是替换它而不是合并它。
const [count, setCount] = useState(0);// useState 方法的返回值是什么? 返回值为:当前 state 以及更新 state 的函数。
return (
<div>
{/* 可以直接使用Hook创建的变量 */}
<p>You clicked {count} times</p>
{/* 可以直接使用更新函数setCount来更新变量 */}
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
)
}
export default StateHook;
EffectHook
import { useState, useEffect } from 'react';
function EffectHook(){
const [count, setCount] = useState(0);
// Effect Hook 可以让你在函数组件中执行副作用操作
// 例如,在函数组件中,你可以使用 useEffect 来指定一个副作用函数,它在每次渲染组件时(包括首次渲染和后续的每次渲染)都会被调用。
useEffect(() => {
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
)
}
export default EffectHook;
组件库
封装一个antd-mobile的Swiper
支持的数据格式
items = [
{
img: 'https://s1.328888.xyz/2022/03/07/gUVyv.png',// 图片地址
href: 'https://baidu.com'// 指向链接
},
...
]
实现了以下特性:
支持链接形式的突破, 并且可以附带该图片的目标地址
支持传入
useState创建的变量如果目标数据需要从服务器获取, 无需做任何操作, 直接更新
useState创建的变量即可支持后续再向组件添加图片
// src/Home/MySwiper/index.js
import { Swiper } from 'antd-mobile';
import style from './style.less';
function MySwiper(props){
// console.log('in MySwiper props: ', props);
const { items } = props;
function getSwiperDatas(){
console.log(items);
if(items.length > 0){
return items.map((item, index) => {
return (
<Swiper.Item key={index}>
<div className={style.swiperItem} onClick={() => {onClickItem(index)}}>
<img src={item.img} alt={item.alt} />
</div>
</Swiper.Item>
)
})
}else{
return (
<Swiper.Item key={0}>
<div className={style.swiperItem}>
<img src="https://s1.328888.xyz/2022/03/07/geux2.png" />
</div>
</Swiper.Item>
)
}
}
function onClickItem(index){
console.log('in targetToImgHref index: ', index);
}
return(
<Swiper className={style.Swiper}>{getSwiperDatas()}</Swiper>
)
}
export default MySwiper;
// src/Home/MySwiper/style.less
.Swiper{
width: 100%;
height: 100%;
.SwiperItem{
width: 100%;
height: 100%;
}
}
// src/Home/index.js
import { useState } from 'react';
import style from './style.less';
import { Button } from 'antd-mobile'
import Swiper from './MySwiper/index.js';
function Home(){
console.log(style);
const [fakeSwiperDatas, setFakeSwiperDatas] = useState([]);
function insertFakeSwiperData(){
setFakeSwiperDatas(oldArray => [...oldArray, {
img: 'https://s1.328888.xyz/2022/03/07/gUVyv.png',
href: 'https://baidu.com'
}])
console.log(fakeSwiperDatas)
}
function initFakeSwiperData(){
setFakeSwiperDatas([
{
img: 'https://s1.328888.xyz/2022/03/07/gUVyv.png',
href: 'https://baidu.com'
},
{
img: 'https://s1.328888.xyz/2022/03/07/gUVyv.png',
href: 'https://baidu.com'
},
{
img: 'https://s1.328888.xyz/2022/03/07/gUVyv.png',
href: 'https://baidu.com'
},
{
img: 'https://s1.328888.xyz/2022/03/07/gUVyv.png',
href: 'https://baidu.com'
}
])
}
return(
<div className={style.root}>
<h1>Home page</h1>
<h2>Swiper</h2>
<Button color='primary' onClick={initFakeSwiperData}>接收到服务器传来的图片地址</Button>
<Swiper items={fakeSwiperDatas}></Swiper>
</div>
)
}
export default Home;
前两个代码块是Swiper的封装实现, 最后一个代码块是调用该封装