基于 Webpack5 创建React项目和基本配置
一. webpack 启动配置(能够基本启动)
1. webpack公共配置
import { Configuration } from 'webpack' // 配置代码提示 运行时请将这行注释,否则会报错
const Path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 添加下面注释块才会有提示
/**
* @type Configuration
*/
const config = {
entry: {
app: './src/main.js'
},
output: {
path: Path.resolve(__dirname, './dist'),
filename: '[name].[chunkhash:8].js',
publicPath: '/',
clean: true
},
module: {
rules: [
{
test: /.(js|jsx|ts|tsx)$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /.(css)$/,
use: ['style-loader', 'css-loader']
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: Path.resolve(__dirname, './index.html'),
filename: 'index.html'
})
]
}
module.exports = config
依赖包:
-
babel 和 兼容浏览器转换
@babel/core babel-loader @babel/preset-env -
jsx 语法解析
@babel/preset-react -
使用async await报错,添加这个包
@babel/plugin-transform-runtime -
打包时将 template 模板内容在 dist 文件夹下生成一个新的 html 并引入对应的打包后的文件
html-webpack-plugin -
使用css
style-loader css-loader -
提供不同环境兼容的环境变量
cross-env -
babel.config.json 配置
{
"plugins": ["@babel/plugin-transform-runtime"], // 使用async await 需要依赖
"presets": [
[
"@babel/preset-env",
{
"modules": false
}
],
"@babel/preset-react"
// ts 解析,暂不添加
// [
// "@babel/preset-typescript",
// {
// "isTSX": true,
// "allExtensions": true
// }
// ]
]
}
- 下载依赖
npm i html-webpack-plugin babel-loader @babel/preset-react @babel/preset-env -D
npm i @babel/core @babel/plugin-transform-runtime style-loader css-loader --save
2. 开发环境配置
// import { Configuration } from 'webpack'
const common = require('./webpack.config') // 导入公共代码块
const webpackMerge = require('webpack-merge').merge // 使用webpack-merge 合并两份配置
const path = require('path')
console.log(process.env.NODE_ENV)
/**
* @type Configuration
*/
const config = {
mode: 'development',
devServer: {
static: path.join(__dirname, 'public'), // 压缩public 中的内容提供本地服务
compress: true, // gzip 压缩
hot: true,
historyApiFallback: true, // 添加转发效果,否则会出现直接请求对应路径 404
port: 3000
}
}
module.exports = webpackMerge(common, config)
依赖包:
-
Hot Module Replacement(热替换)
html-webpack-server -
webpack 配置合并
webpack-merge -
下载依赖
npm i webpack-merge html-webpack-server -D
3. 生产环境配置(暂时没有)
冇得!!!
二、React 、 redux 、react-router 整合
1. 下载 React 相关的包
- React 、ReactDom( V 18.0.0 )
npm install react react-dom - React-router v6
npm install react-router-dom@6 - Redux
npm install @reduxjs/toolkit react-redux
版本都是现在最新的,后面介绍我如何使用 react-router 和 redux
2. 创建入口js和 根组件App.js (默认src下)
// main.js
import React from 'react'
import * as ReactDOMClient from 'react-dom/client'
import { Provider } from 'react-redux'
import { BrowserRouter } from 'react-router-dom'
import store from './redux/index'
import App from './App'
// 官方例子: https://reactjs.org/docs/react-dom-client.html#createroot
ReactDOMClient.createRoot(document.getElementById('app')).render(
<Provider store={store}> // redux 相关 可以看看 useContent
<BrowserRouter> // 路由模式 history
<App />
</BrowserRouter>
</Provider>
)
// App.js
import React from 'react'
import { useRoutes } from 'react-router-dom'
import routes from './route/index' // 导入路由配置文件
export default function App() {
const elements = useRoutes(routes) // 通过 useRoutes 将对象形式 route[] 配置转换为 jsx 并用Routes 包裹
return <>{elements}</> // 类似于 <Routes> <Route />... </Routes>
}
3.react-router 配置 (默认route 目录下)
index.js
import React, { Suspense } from 'react'
// 懒加载 也可以添加 webpack comments 进行分包/preload/prefetch
const Home = React.lazy(() => import('../page/home'))
const Detail = React.lazy(() => import('../page/Detail'))
// 路由配置
const routes = [
{ path: '/', element: <></> },
{ path: '/home', element: <Home /> },
{
path: '/detail',
element: <Detail />
}
]
// 如果需要用到 Suspense ,用递归进行包裹
const changeRoutes = routes => {
return routes.map(item => {
if (item.children) {
item.children = changeRoutes(item.children)
}
// item.element = (
// <Suspense fallback={<>Loading...</>}>{item.element}</Suspense>
// )
return item
})
}
export default changeRoutes(routes)
4.redux 配置(默认redux 目录下)
index.js
import { configureStore } from '@reduxjs/toolkit'
import user from './reducers/user'
export default configureStore({
reducer: {
user
}
})
reducers/user.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
// 内置了thunk插件,可以直接处理异步请求
export const asyncUpdateName = createAsyncThunk(
'user/asyncUpdateName',
async payload => {
const data = await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(payload)
}, 2000)
})
return data
}
)
// 创建 reducer 片段
export const userSlice = createSlice({
name: 'user',
initialState: {
name: 'tom'
},
reducers: {
deleteName(state) {
state.name = ''
},
updateName(state, action) {
state.name = action.payload.name
}
},
// 此处有两种写法,可以看看官方demo https://redux-toolkit.js.org/api/createSlice#extrareducers
extraReducers: {
[asyncUpdateName.pending]: (state, action) => {
console.log(state, action)
},
[asyncUpdateName.fulfilled]: (state, action) => {
state.name = action.payload.name
return state
},
[asyncUpdateName.rejected]: (state, action) => {
console.log(state, action)
}
}
})
export const { deleteName, updateName } = userSlice.actions
// 暴露箭头函数方便 useSelector(selectUser)
export const selectUser = state => {
return state.user.name
}
export default userSlice.reducer
三、 页面(page)
1.创建一个页面仅作测试
home.js
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import {
selectUser,
deleteName,
updateName,
asyncUpdateName
} from '../redux/reducers/user'
export default function Home() {
const name = useSelector(selectUser)
// 获取 dispatch
const dispatch = useDispatch()
return (
<>
<div>
<button
onClick={() => {
dispatch(updateName({ name: 'yu' })) // dispatch派发action
}}>
{name}
</button>
<hr />
<button
onClick={() => {
dispatch(asyncUpdateName({ name: 'yuuuu' }))
}}>
{name}
</button>
</div>
</>
)
}