基于Webpack5 简单创建React项目(更新中......)

798 阅读3分钟

基于 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>
    </>
  )
}

四、相关文档地址

react文档

react-redux文档

redux-toolkit文档

react-router 文档

webpack 文档