Recat v17.x 项目开发框架搭建
1. 创建React项目
为了加速npm下载速度,先把npm设置为淘宝镜像地址。
npm config set registry http://registry.npm.taobao.org/
// 安装yarn
npm install -g yarn
// 安装cnpm
npm install -g cnpm -registry=https://registry.npm.taobao.org
//设置国内源
yarn config set registry https://registry.npm.taobao.org/
也可以使用cnpm,根据喜好自行选择。创建react项目,nodejs版本最好选择14以上。
通过官方的create-react-app,在自创建的目录执行:
npx create-react-app react-app
命令最后的react-app是项目的名称,可以自行更改。安装完成后,可以使用npm或者yarn启动项目。进入项目目录,并启动项目:
cd react-app
yarn start (或者使用npm start)
如果没有安装yarn,可以前往yarn中文网站安装:https://yarn.bootcss.com/
启动后,可以通过以下地址访问项目:http://localhost:3000/
安装为最新的react版本,为18.2.0,create-react-app是基于 webpack 的打包层方案,包含 build、dev、lint 等,他在打包层把体验做到了极致,但是不包含路由,不是框架,也不支持配置,都需要自行解决。
2. 暴露配置文件
方法一
如果希望做一些自定义的系统配置,可采用下列方法。
执行以下命令,暴露配置文件:
npm run eject (yarn eject)
如果之前没有提及git的话,可能会报以下错误:
Remove untracked files, stash or commit any changes, and try again
需要先在项目根目录下执行:
git add .
git commit -m "初始化项目(备注)"
react-scripts 是 create-react-app 的一个核心包,一些脚本和工具的默认配置都集成在里面,而 yarn eject 命令执行后会将封装在 create-react-app 中的配置全部反编译到当前项目,这样用户就能完全取得 webpack 文件的控制权。所以,eject 命令存在的意义就是更改 webpack 配置。npm run eject 会复制所有依赖文件和相应的依赖(webpack、babel等)到你的项目。是个单项操作,一旦 eject ,就回不去了,如果想要回到正常的项目中去,只有重新 npm install create-react-app -g 了。
方法二
采用上述方法不可逆,生成的内容也比较多。推荐使用第三方工具,采用react-app-rewired插件方式,它的作用是用来帮助你重写react脚手架配置。react-app-rewired@2.x版本需要搭配customize-cra使用,customize-cra的作用是帮助你自定义react脚手架2.x版本配置。
- 安装
npm intall react-app-rewired customize-cra --save-dev (yarn add react-app-rewired customize-cra --dev)
- 修改package.json,原本的react-script 改为react-app-rewired
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test --env=jsdom",
"eject": "react-scripts eject"
},
- 在根目录下新建文件config-overrides.js文件
module.exports = function override(config, env) {
// do stuff with the webpack config...
return config
}
- 设置浏览器不启动,BROWSER=none&&之间不能有空格:
"scripts": {
"start": "set BROWSER=none&&react-scripts start",
}
- proxy
开发环境下跨域问题,前端一般是给本地的devServer设置代理
安装http-proxy-middleware:
cnpm install http-proxy-middleware --save-dev
cnpm install axios --save
在src/目录下新建文件setupProxy.js(注意:文件名不能修改!!cra会按照src/setupProxy.js这个路径解析)
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
app.use(createProxyMiddleware('/api', {
target: 'http://localhost:3001/',
changeOrigin: true,
// pathRewrite: {
// '^/api': ''
// }
}))
}
- 支持别名:
const {
override,
// ...
addWebpackAlias
} = require('customize-cra')
const path = require('path')
function resolve (dir) {
return path.join(__dirname, '.', dir)
}
module.exports = override(
// ...
// 路径别名
addWebpackAlias({
'@': resolve('src')
'@components': resolve('src/components'),
'@pages': resolve('src/pages')
})
)
- 按需加载 babel-plugin-import
// babel-plugin-import 是一个用于按需加载组件代码和样式的 babel 插件
yarn add babel-plugin-import --dev
//引入
const { fixBabelImports } =require('react-app-rewired');
//使用
module.exports =function override(config, env){
// 针对antd 实现按需打包:根据import来打包 (使用babel-plugin-import)
fixBabelImports('import',{
libraryName:'antd',
libraryDirectory:'es',
style:true,//自动打包相关的样式 默认为 style:'css'
}),
};
- 其他配置支持
module.exports = function override(config, env) {
// do stuff with the webpack config...
return config
}
const {
override,
fixBabelImports,
addWebpackModuleRule,
addPostcssPlugins
} = require('customize-cra');
const path = require('path');
const { name } = require('./package.json');
const paths = require('react-scripts/config/paths');
paths.appBuild = path.join(path.dirname(paths.appBuild), name);
module.exports = override(
(config) => ({
...config,
devtool: config.mode === 'development' ? 'cheap-module-source-map' : false,
}),
fixBabelImports('import', {
libraryName: 'antd',
style: 'css',
}),
addWebpackModuleRule({
test: /\.(png|jpg|gif|jpeg|svg)$/i,
use: [{
loader: 'url-loader',
options: {
limit: 20 * 1024,
esModule: false,
outputPath: `static/imgs/`,
}
}]
})
);
- 如需要配置eslint配置:
npm install react-app-rewired-eslint --save (yarn add react-app-rewired-eslint)
//在config-overrides.js 中修改如下:
const rewireEslint = require('react-app-rewired-eslint');
/* config-overrides.js */
module.exports = function override(config, env) {
config = rewireEslint(config, env);
return config;
}
再创建自己的.eslintrc文件,文件结构如下:
+-- your-project
| +-- .eslintrc
| +-- config-overrides.js
| +-- node_modules
| +-- package.json
| +-- public
| +-- README.md
| +-- src
3.支持less
- 安装less,直接安装后,并不会生效,需要做系统配置,参考第3步
npm install less less-loader --save-dev (yarn add less less-loader --dev)
如果直接安装后,出现less-loader版本过高,导致编译报错的问题,可重新安装低版本
npm uninstall less-loader --save-dev (yarn remove less-loader --dev)
npm install less-loader@6.0.0 --save-dev (yarn add less-loader@6.0.0 --dev)
- 如果采用react-app-rewired,less 生效配置如下:
const {
override,
addLessLoader
// ...
} = require('customize-cra')
module.exports = override(
// less
addLessLoader({
// 现在的写法
lessOptions: {
javascriptEnabled: true,
// modifyVars: { '@brand-primary': '#1DA57A' },
modifyVars: getLessVars(path.join(__dirname, './src/styles/vars.less')),
},
// 原来的写法
// javascriptEnabled: true,
// modifyVars: {
// '@primary-color': '#1DA57A',
// },
// localIdentName: '[local]--[hash:base64:5]' // 自定义 CSS Modules 的 localIdentName
})
)
启动可能报错如下:
ValidationError: Invalid options object. PostCSS Loader has been initialized using an options object that does not match the API schema.
- options has an unknown property 'plugins'. These properties are valid:
object { postcssOptions?, execute?, sourceMap?, implementation? }
ERROR in ./src/pages/login/login.less (./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[1].oneOf[9].use[1]!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].oneOf[9].use[2]!./node_modules/resolve-url-loader/index.js??ruleSet[1].rules[1].oneOf[9].use[3]!./node_modules/less-loader/dist/cjs.js??ruleSet[1].rules[1].oneOf[9].use[4]!./src/pages/login/login.less)
Module build failed (from ./node_modules/postcss-loader/dist/cjs.js):
ValidationError: Invalid options object. PostCSS Loader has been initialized using an options object that does not match the API schema.
- options has an unknown property 'plugins'. These properties are valid:
object { postcssOptions?, execute?, sourceMap?, implementation? }
at validate (D:\developer\workspace\micro\micro-react\node_modules\webpack\node_modules\schema-utils\dist\validate.js:105:11)
at Object.getOptions (D:\developer\workspace\micro\micro-react\node_modules\webpack\lib\NormalModule.js:585:19)
at Object.loader (D:\developer\workspace\micro\micro-react\node_modules\postcss-loader\dist\index.js:40:24)
webpack compiled with 1 error
解决思路,在config-overrides.js文件增加如下内容:
const {
override,
adjustStyleLoaders,
//...
}
adjustStyleLoaders(({ use: [, , postcss] }) => {
const postcssOptions = postcss.options;
postcss.options = { postcssOptions };
}),
4. 支持scss/sass
- 安装sass
npm install node-sass sass-loader --save-dev (yarn add node-sass sass-loader --dev)
安装完成后就可以直接使用,如xxx.jxs文件中:
// 导入scss文件
import '@/assets/css/login.scss';
...
<div className="login">
- 要配置全局scss/sass文件,如果采用react-app-rewired,配置如下:
npm install sass-resources-loader --save-dev (yarn add sass-resources-loader --dev)
const {
override,
adjustStyleLoaders
// ...
} = require('customize-cra')
module.exports = override(
adjustStyleLoaders(rule => {
if (rule.test.toString().includes("scss")) {
rule.use.push({
loader: require.resolve("sass-resources-loader"),
options: {
resources: "./src/assets/css/base.scss" //这里是你自己放公共scss变量的路径
}
})
}
})
)
5. 安装element-ui
// 安装
npm i element-react --save (yarn add element-react)
// 下载主题包
npm install element-theme-default --save (yarn add element-theme-default)
在 React 中使用
// 打开需要使用到 element-ui 的 jsx 文件,在头部添加
import React from 'react';
import ReactDOM from 'react-dom';
import { Button } from 'element-react';
import 'element-theme-default';
使用参考:官方网站
6. 路由导航(常规使用)
npm install react-router-dom --save (yarn add react-router-dom)
安装完成后,直接可使用,过程中可能出现的错误如下:
编译出错
加入导航后,编译出错。
错误一
Module not found: Error: Can't resolve 'babel-runtime/helper/extends'
解决方案,安装babel-runtime解决
npm install babel-runtime --save (yarn add babel-runtime)
错误二
ERROR in ./node_modules/element-react/dist/npm/es5/src/tree/model/tree-store.js 32:90-117
Module not found: Error: Can't resolve 'react-hot-loader' in 'D:\developer\workspace\micro\micro-react\node_modules\element-react\dist\npm\es5\src\tree\model'
解决方案,安装react-hot-loader
npm install react-hot-loader --save (yarn add react-hot-loader)
react-hot-loader@^4.13.0 requires a peer of @types/react@^15.0.0 || ^16.0.0 || ^17.0.0 but @types/react@18.0.21 was installed
安装的最新版本只支持react 15.0|16.0| 17.0以上,由于采用react-create-app方式,安装的react版本为18.2.0,只能降低react版本为1.7.2
yarn remove react react-dom
yarn add react@17.0.2 react-dom@17.0.2
错误三
降低react版本后,又出现错误:
Module not found: Error: Can't resolve 'react-dom/client' in 'D:\developer\workspace\micro\micro-react\src'
LOG from webpack.FileSystemInfo
<w> Managed item D:\developer\workspace\micro\micro-react\node_modules\element-react isn't a directory or doesn't contain a package.json (see snapshot.managedPaths option)
+ 8 hidden lines
Failed to compile.
主要是由于17与18版本的语法不同导致的
可参考:: 官方网站说明
主要修改主类index.js代码
// 18版本语法
import ReactDOM from 'react-dom/client';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
//17版本语法
import { render } from 'react-dom';
const container = document.getElementById('app');
render( <App />, container );
顺利启动。
错误四
import React from 'react';
import { Routes, Route, Route } from 'react-router-dom'
import Login from './pages/login/Login';
import Main from './pages/main/Main'
export default function App() {
return (
<React.Fragment>
<div>我是功能页面</div>
<Router>
<Routes>
<Route path='/login' element={<Login />} />
</Routes>
</Router>
</React.Fragment>
);
}
报错如下:
Uncaught TypeError: Cannot read properties of undefined (reading 'pathname')
at Router (components.tsx:316:1)
at renderWithHooks (react-dom.development.js:14985:1)
at mountIndeterminateComponent (react-dom.development.js:17811:1)
at beginWork (react-dom.development.js:19049:1)
at HTMLUnknownElement.callCallback (react-dom.development.js:3945:1)
at Object.invokeGuardedCallbackDev (react-dom.development.js:3994:1)
at invokeGuardedCallback (react-dom.development.js:4056:1)
at beginWork$1 (react-dom.development.js:23964:1)
at performUnitOfWork (react-dom.development.js:22776:1)
at workLoopSync (react-dom.development.js:22707:1)
react-dom.development.js:20085 The above error occurred in the <Router> component:
at Router (http://localhost:3000/static/js/bundle.js:87862:15)
at div
at App
解决方法:
这里直接使用 Router 标签会出现上述问题,用 BrowserRouter 代替,问题没有了。
出现下列问题:
No routes matched location “/“
需要添加 “/” 路径的路由即可。
export default function App() {
return (
<React.Fragment>
<div>我是功能页面</div>
<BrowserRouter>
<Routes>
<Route path='/' element={<Navigate to="/main" />} />
<Route path='/main' element={< Main /> } />
<Route path='/login' element={<Login />} />
</Routes>
</BrowserRouter>
</React.Fragment>
);
}
8. Serve插件(查看打包运行效果)
// 安装插件(全局安装)
npm install -g serve (yarn global add serve)
//查看版本
serve -v
// 构建项目
yarn build
// 启动
serve -s build
效果如下:
安装typescript支持
npx create-react-app my-app --template typescript
参考:https://zhuanlan.zhihu.com/p/104771562
https://blog.csdn.net/weixin_43216105/article/details/108244032
https://juejin.cn/post/7073405036025348110
https://www.jianshu.com/p/352e33c56d55
http://wmm66.com/index/article/detail/id/165.html
https://blog.csdn.net/u010059669/article/details/122359412