登录
使用大名鼎鼎的 cra
(create-react-app
)创建react项目
1. 首先使用 npm 全局安装
这一步其实可以省略,因为可以使用 npx,见下一步。
npm i -g create-react-app
2. 使用 create-react-app 创建项目
使用 create-react-app [项目名称]
的格式创建项目:
create-react-app react-demo
# 将在当前终端的文件夹创建一个名称为 react-demo 的文件夹
# 文件里面就是项目了
上一步说可以不全局安装 create-react-app
,是因为现在的 node
除了 npm
,还提供了 npx
这个工具。
npx
允许用户使用一些需要先提前全局安装的工具时,比如 pm2
、cnpm
、yarn
等,可以不全局安装了,可以在这些指令的使用语句的前面,直接添加 npx
。
这样的好处是,一些使用频率很低的工具,不用全局安装了,而是使用 npx
来「临时」使用。
比如没有全局安装 create-react-app
时,直接使用下面的指令:
npx create-react-app react-demo
npx 如果检测到系统没有这个指令,就会先自动先下载这个工具,然后再使用这个工具执行后面的命令,最终所有指令完成后,这个工具会自动移除(实际可能是会只存在缓存区)。
完成安装后,可以进行一些常用配置。
新版的 create-react-app
是隐藏配置的,想修改 webpack
配置,最傻瓜式的方式是执行 cra
提供的下面的这条指令,弹出所有 webpack
配置来修改,但这么操作实在太不友好了,所有配置文件夹、配置文件都出现在项目里,很杂乱且不能再收回去:
# 这条指令就在 create-react-app 创建的项目的 package.json 的指令中
npm run eject
因为上面的操作很不友好,所以我们一般会采用其他的工具来merge式的修改配置,最常用的工具就是 react-app-rewired
了。
先在项目中安装工具:
npm i -D react-app-rewired
然后修改 package.json
中的指令:
{
"scripts": {
- "start": "react-scripts start",
- "build": "react-scripts build",
+ "start": "react-app-rewired start",
+ "build": "react-app-rewired build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
}
接着在项目根目录(package.json
同级) 创建文件 config-overrides.js
,内容如下:
const path = require('path');
module.exports = (config, env) => {
// env 顾名思义,就是环境变量配置
// config 就是 webpack 配置文件,可以修改里面的项,最后会并入 webpack 的配置中
// 从 config 可以读取到 entry、output、plugins、resolve……等等配置,可以进行修改
// 查看 config.entry 配置
console.log(config.entry)
// 修改配置
config.resolve = {
...(config.resolve || {}),
alias: {
...(config.resolve.alias || {}),
// 添加别名 alias
app: path.join(__dirname, "src"),
},
// 针对 webpack >= 5 时需要单独导入node模块的配置
// fallback: {
// ...(config.resolve.fallback || {}),
// crypto: require.resolve('crypto-browserify'),
// path: require.resolve('path-browserify'),
// http: require.resolve('stream-http'),
// https: require.resolve('https-browserify'),
// stream: require.resolve('stream-browserify'),
// }
}
return config
}
根目录下的 .env
文件同样可以做一些配置,比如:
# 本地启动服务时不自动使用浏览器打开页面
BROWSER=none
# 修改启本地服务时的端口号
PORT=3012
# 打包时不构建sourceMap文件
GENERATE_SOURCEMAP=false
3. 打包后的引入静态资源时使用相对路径
默认情况,打包后生成的静态项目,里面引入js\css\图片\字体时,使用的是绝对路径 /,这就导致我们无法双击index.html来预览项目(就算react路由模式使用的是hash模式也不行)。
所以可以给 package.json
文件添加一项:
{
// ...其他配置
"homepage": "./",
}
这样打包完成后,引入文件的方式就都是相对路径 ./ 的形式了。
项目开始开发后,需要进行项目调整,比如配置路由、添加sass支持等。
首先是上面说到的修改 webpack
配置的工具 react-app-rewired
,这里不再赘述。
node-sass
经常出问题,现在官方也已经不推荐使用 node-sass
,明确 node-sass
以后只会进行对新版本node的兼容更新,不会再添加新功能。
官方现在的建议,是使用 dark-sass
,而 dark-sass
只是这种用法的名字,具体的使用,就是安装下面两个插件:
npm i -D sass sass-loader
安装上面两个依赖后,可实现 node-sass
同样的效果(样式穿透的写法有不同,见其他文档)。
此外,也可以使用less
,而且,如果使用antd
样式框架,那最好使用less
,因为antd
使用的就是less
,如果想自定义修改antd
的全局样式变量,就必须使用less
了。
用处不用说了,首先安装需要的依赖:
npm i -S redux react-redux redux-thunk
接着创建文件夹 src/store
,里面创建四个文件:
defaultState.js
:全局状态数据的默认值们。reducers.js
:数据的分发。actions.js
:管理数据的 action
动作们。index.js:
:把上面的东西统一规整暴露出去的文件。下面一次性把四个文件中的内容大致写一下:
// defaultState.js 文件中
const defaultState = {
flagNum: 0,
};
export default defaultState;
// reducers.js 文件中
import { combineReducers } from "redux"; // 工具函数,用于组织多个reducer,并返回reducer集合
import defaultState from "./defaultState"; // 默认值
// 测试字段
function flagNum(data = defaultState.flagNum, action) {
switch (action.type) {
case "SET_FLAGNUM":
return action.data;
default:
return data;
}
}
// 导出所有reducer
export default combineReducers({
flagNum,
});
// actions.js 文件中
export function init() {
return async (dispatch, getState) => {
const state = getState()
const text = `页面初始化时 flagNum 的值:${state.flagNum}`
console.log(text);
// 是否 return 都可以
return new Promise(resolve => {
resolve();
})
};
}
// index.js 文件中
import { applyMiddleware, createStore } from "redux";
// 中间件,作用:如果不使用该中间件,当我们dispatch一个action时,需要给dispatch函数传入action对象;
// 但如果我们使用了这个中间件,那么就可以传入一个函数,这个函数接收两个参数:dispatch和getState。
// 这个dispatch可以在将来的异步请求完成后使用,对于异步action很有用
import thunk from "redux-thunk";
// 引入reducer
import reducers from "./reducers.js";
// 创建store实例
let store = createStore(reducers, applyMiddleware(thunk));
export default store;
下面把 redux
注入到全局,src/index.js
中:
import React from 'react';
import ReactDOM from "react-dom/client";
import Router from "./router";
// 引入redux
import { Provider } from "react-redux";
import store from "app/store";
const root = ReactDOM.createRoot(document.getElementById('root'));
// 修改写法,使用 Provider 包裹并注入 store
root.render(
<Provider store={store}>
<Router />
</Provider>
)
最后是在某个组件中使用,这里以 react 推荐的函数式组件为例:
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
// 引入要使用的动作
import { init } from "../../store/actions";
export default function Button() {
// 初始化 dispatch
const dispatch = useDispatch()
useEffect(() => {
// 用 dispatch 执行再 actions 中写好的方法
// 可以放在函数中,使用异步 async/await 执行
dispatch(init())
}, [dispatch])
// 获取全局变量
const flagNum = useSelector(state => state.flagNum);
const addFlag = () => {
// 用 dispatch 修改某个变量
dispatch({ type: "SET_FLAGNUM", data: flagNum + 1 })
}
return (
<button onClick={addFlag}>点击加值:{flagNum}</button>
);
}
最新版的 cra
项目中,使用node核心模块时,比如使用 path
时会发生类似以下报错:
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.
If you want to include a polyfill, you need to:
- add a fallback 'resolve.fallback: { "path": require.resolve("path-browserify") }'
- install 'path-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "path": false }
这是 webpack
新版本本身的特性,意思是webpack < 5版本时,自带node.js的核心模块,但大于等于5时,不会了,如果项目想使用类似 path、http、fs……这些模块时,需要自己安装对应的模块并进行webpack配置,才能使用了(还有一个办法,就是不使用webpack了,这种因噎废食的做法应该很少人采用)。
具体修正方法,见我这里另一个文档 webpack 版本5的报错。