从0开始搭建我们的react + react-router + redux

197 阅读2分钟

目录结构

|-- sfg
    |-- .babelrc  
    |-- package.json
    |-- tsconfig.json
    |-- yarn-error.log
    |-- yarn.lock
    |-- assets
    |   |-- index.txt
    |-- build
    |   |-- webpack.base.js
    |   |-- webpack.dev.js
    |-- dist
    |-- src
        |-- Home.tsx
        |-- index.html
        |-- index.ts
        |-- components
        |-- routes
        |   |-- index.ts
        |-- scss
        |   |-- index.scss
        |-- stores
        |   |-- CounterSlice.ts
        |   |-- index.ts
        |-- utils
        |-- views
            |-- Index.ts
            |-- User.tsx

步骤

  1. 安装依赖包;
  2. 配置webpack;
    • .babelrc
    • dev-server
    • html-webpack-plugin
    • loader
    • plugins
  3. 入口文件;
  4. router
  5. redux;

安装依赖 package.json

{
  "name": "sfg",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "webpack serve -c ./build/webpack.dev.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.13.16",
    "@babel/preset-env": "^7.13.15",
    "@babel/preset-react": "^7.13.13",
    "@babel/preset-typescript": "^7.13.0",
    "@babel/runtime": "^7.13.17",
    "babel-loader": "^8.2.2",
    "clean-webpack-plugin": "^4.0.0-alpha.0",
    "copy-webpack-plugin": "^8.1.1",
    "cross-env": "^7.0.3",
    "css-loader": "^5.2.4",
    "html-webpack-plugin": "^5.3.1",
    "mddir": "^1.1.1",
    "postcss-loader": "^5.2.0",
    "postcss-px2vw": "^0.0.1",
    "sass-loader": "^11.0.1",
    "style-loader": "^2.0.0",
    "url-loader": "^4.1.1",
    "webpack": "^5.35.0",
    "webpack-cli": "^4.6.0",
    "webpack-dev-server": "^3.11.2",
    "webpack-merge": "^5.7.3"
  },
  "dependencies": {
    "@babel/plugin-syntax-dynamic-import": "^7.8.3",
    "@babel/plugin-transform-runtime": "^7.13.15",
    "@reduxjs/toolkit": "^1.5.1",
    "@types/react": "^17.0.3",
    "@types/react-dom": "^17.0.3",
    "@types/react-router": "^5.1.13",
    "@types/react-router-dom": "^5.1.7",
    "@types/redux": "^3.6.0",
    "core-js": "3",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-redux": "^7.2.4",
    "react-router": "^5.2.0",
    "react-router-dom": "^5.2.0",
    "sass": "^1.32.11"
  }
}

Webpack 相关的

.babelrc

{
	"presets": [
		[
			"@babel/preset-env",
			{
				"useBuiltIns" : "usage",
				"corejs" : "3",
				"targets": "> 0.25%, not dead",
				"modules" : "amd"
			}
		],
		[
			"@babel/preset-typescript",{
				"isTSX": true,
				"allExtensions" : true
			}
		],
		"@babel/preset-react"
	],
	"plugins": [
		"@babel/plugin-transform-runtime",
		"@babel/plugin-syntax-dynamic-import"
	]
}

Webpack.base.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin'),
	{CleanWebpackPlugin} = require('clean-webpack-plugin'),
	CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = {
	entry: {
		index: path.join(__dirname,'../src','index.ts'),
	},
	output: {
		path: path.join(__dirname,'..','dist'),
		filename: '[name].[contenthash].js'
	},
	module: {
		rules: [
			{
				test: /\.[jt]sx?$/,
				exclude: /node_modules/,
				use: [
					{
						loader: "babel-loader"
					}
				]
			},
			{
				test: /\.scss$/,
				exclude: /node_modules/,
				use: [
					"style-loader",
					"css-loader",
					"sass-loader",
				]
			}
		]
	},
	plugins: [
		new CleanWebpackPlugin(),
		new HtmlWebpackPlugin({
			template: path.join(__dirname,'../src','index.html'),
			inject: 'body'
		}),

	//	new CopyWebpackPlugin({
	//		patterns: [
	//			{
	//				from: path.join(__dirname,'../assets'),
	//				to: "assets"
	//			}
	//		]
	//	})
	],
	resolve: {
		alias: {
			"@" : path.join(__dirname,'../src'),
			"@views": path.join(__dirname,'../src/','views'),
			"@components": path.join(__dirname,'../src/','components'),
		},
		extensions: [".tsx",".ts",".js"],
	}
}

Webpack.dev.js

const path = require('path');
const { merge } = require('webpack-merge');
const base = require('./webpack.base.js');
module.exports = merge(base,{
	mode: "development",
	devServer: {
		contentBase: [
			path.join(__dirname,'../dist'),
			path.join(__dirname,'../assets'),
		],
		contentBasePublicPath: [
			'/','/assets'
		],
		port: 9000,
		hot: true,
		index: 'index.html'
	}
})

src/index.ts

import './scss/index.scss';
import React from 'react';
import {render} from 'react-dom';
import Home from './Home.tsx';
render(<Home/>, document.querySelector('#app'));

src/Home.tsx

import React , {Component, Suspense, lazy} from 'react';
import {RouterList,} from '@/routes/index';

import store, {add} from '@/stores/index.ts';
import {Provider, connect} from 'react-redux';

//let Index = lazy(()=>import('@views/Index'));
//import Index from '@views/Index'
import {
	HashRouter as Router,
	Switch,
	Route,
} from 'react-router-dom';
class Home extends Component{
	render(){
		const renderList = function(list){
			return list.map(route=>{
				return <Route exact key={route.path} path={route.path} component={route.component}></Route>
			})
		}
		return (
			<Provider store={store}>
			<Router>
					<Suspense fallback={<div>...</div>}>
				<Switch>
				{
					renderList(RouterList)
				}
				</Switch>
				</Suspense>
			</Router>
			</Provider>
		)
	}
}

//export default connect((state)=>state,{add})(Home)
export default Home;

src/router/index.ts

import React, {lazy} from 'react';
let Index = lazy(()=>import('@views/Index'));
let User = lazy(()=>import('@views/User'));

export const RouterList = [
	{
		path: "/",
		name: "首页",
		exact: true,
		component: Index,
	},
	{
		path: "/user",
		name: "用户",
		exact: true,
		component: User,
	},
]

src/views/index.ts

import React, {Component,} from 'react';
import {useSelector,useDispatch,} from 'react-redux'
import {add} from '@/stores/index'
export default function(props){
	const dis = useDispatch();
	const clickHandle = function(){
		dis({
			type: 'counter/add', 
			payload: 15
		});
	}
	const counter = useSelector(state => state.CounterSlice.counter);
	console.log(counter);
	return <div onClick={clickHandle}>---{counter.value}---</div>
}

src/views/User.tsx

import React ,{ Component} from 'react';
import {connect} from 'react-redux';
import {add,CounterStore} from '@/stores/index';
class User extends Component{
	componentDidMount(){
		console.log(this.props);
	}
	render(){
		return <div onClick={()=>this.props.add(11)}>{this.props.counter.value}</div>
	}
}
export default connect(CounterStore,{add})(User);

src/stores/CounterSlice.ts

import {createSlice} from '@reduxjs/toolkit';
const CounterSlice = createSlice({
	name: "counter",
	initialState: {
		counter: {
			value: 11,
		}
	},
	reducers: {
		add: (state,{payload}) => {
			state.counter.value += payload;
			return;
		}
	}
})
export default CounterSlice;

src/stores/index.ts

import {configureStore, createStore} from '@reduxjs/toolkit';
import CounterSlice from './CounterSlice';

export const CounterStore = state => state.CounterSlice;
export const {add} = CounterSlice.actions;
export default configureStore({
	reducer: {
		CounterSlice : CounterSlice.reducer,
	}
});