菜鸟webpack/react/redux/react-router/ts一步步搭建架子

2021-01-23 05:12

阅读:575

YPE html>

标签:browser   pen   tsx   title   put   执行   echo   count   hash   

mkdir stage && cd stage // 创建项目文件夹进入项目
npm init // 初始化依赖
npm install -S react react-dom  // 安装react相关依赖
npm install -D webpack webpack-cli webpack-dev-server // 安装webpack相关依赖
npm install -D html-webpack-plugin clean-webpack-plugin // 安装生成html和清理html文件的插件
npm install -D babel-loader @babel/core @babel/preset-env @babel/preset-react // 安装babel-loader解析react
npm install -D less style-loader css-loader less-loader // 安装less依赖及相关的开发loader mkdir src config build
// 根目录下创建src、config、build文件夹 touch babel.config.js // 根目录下创建babel.config.js cd build && touch webpack.config.js // build目录下创建webpack.config.js
cd ../src && touch index.js && touch index.less && touch index.html // src目录下创建index.js、index.less和index.html

 

// babel.config.js
module.exports = {
    presets: [
        "@babel/preset-env",
        "@babel/preset-react",
    ],
}

 

// build/webpack.config.js
const path = require(‘path‘);
const HtmlWebpackPlugin = require(‘html-webpack-plugin‘);
const { CleanWebpackPlugin } = require(‘clean-webpack-plugin‘);

module.exports = {
    entry: ‘./src/index.js‘,
    output: {
        filename: ‘bundle.js‘,
        path: path.resolve(__dirname, ‘dist‘),
    },
    devServer: {
        port: 3001,
    },
    devtool: ‘inline-source-map‘,
    plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            template: ‘./src/index.html‘,
        })
    ],
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: "babel-loader",
            },
            {
                test: /\.less$/,
                exclude: /node_modules/,
                use: [
                    ‘style-loader‘,
                    { loader: ‘css-loader‘, options: { modules: true } },
                    ‘less-loader‘,
                ]
            },
        ]
    }
};

 

// src/index.js
import React from ‘react‘
import { render } from ‘react-dom‘
import styles from ‘./index.less‘
const App = () => (
STAGE HOHOHO
) render(, document.getElementById(‘root‘))

 

// src/index.less
.hohoho {
    color: #008000;
}

 

STAGE

 

修改package.json,添加执行脚本

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack-dev-server --config ./build/webpack.config.js --open",
    "build": "webpack --config ./build/webpack.config.js"
  },

 

此时执行npm run build可以看到build目录下生成了dist文件夹 ,npm start可以启动3001端口访问到写的index.js中的内容(如果有报错请检查依赖是否安装成功)

 

 ###### ###### ###### ###### ###### ###### ###### ###### ###### ###### ######

接入react-router

 

npm install -D react-router-dom // 安装react-router-dom依赖

 

修改src/index.js文件,此处使用的是HashRouter,如果使用BrowserRouter需要服务端做相应的响应,原理可以对比hash路由和history的区别(可以分别使用两种Router,切换路由时看具体网络请求就明白了)

// src/index.js
import React from ‘react‘
import { render } from ‘react-dom‘
import {
    HashRouter,
    Route,
    Switch,
    Redirect,
} from ‘react-router-dom‘
import styles from ‘./index.less‘

const Home = () => (
  
HOME HOHOHO

) const Page1 = () => (   
PAGE1 HOHOHO

) const Page2 = () => (     
PAGE2 HOHOHO

) const App = () => (
STAGE HOHOHO
  • 去home
  • 去page1
  • 去page2


  •          
    > ) render(, document.getElementById(‘root‘))

     

    此时可以来回切换home、page1、page2三个页面

     ###### ###### ###### ###### ###### ###### ###### ###### ###### ###### ######

    接入redux 

     

    npm install -S redux react-redux // 安装redux相关依赖
    mkdir models && cd models && mkdir stores actions reducers // 在src目录下创建redux相关的文件夹,并分别在目录下创建index.js
    cd stores && touch index.js && cd ../actions && touch index.js && cd ../reducers && touch index.js // 分别创建index.js文件

     

    // src/models/actions/index.js
    export const CREATE_TODO = ‘CREATE‘; // 增加一个todo
    export const DELETE_TODO = ‘DELETE‘; // 删除一个todo
    export const CREATE_TYPE = ‘CREATE_TYPE‘; // 添加操作
    export const DELETE_TYPE = ‘DELETE_TYPE‘; // 删除操作

     

    // src/models/reducers/index.js
    import {
        CREATE_TODO,
        DELETE_TODO,
        CREATE_TYPE,
        DELETE_TYPE,
    } from ‘../actions‘
    
    export function todos(state = [], action) {
        switch (action.type) {
            case CREATE_TODO: {
                return [...state, { id: action.id, text: action.text, completed: false }]
            }
            case DELETE_TODO: {
                return [...state].filter(({ id }) => id !== action.id)
            }
            default: {
                return state;
            }
        }
    }
    
    export function operateCounter(state = { createCounter: 0, deleteCounter: 0 }, action) {
        const { createCounter, deleteCounter } = state;
        switch (action.type) {
            case CREATE_TYPE: {
                return { ...state, createCounter: createCounter + 1 }
            }
            case DELETE_TYPE: {
                return { ...state, deleteCounter: deleteCounter + 1 }
            }
            default: {
                return state;
            }
        }
    }

     

    // src/models/stores/index.js
    import { combineReducers, createStore } from ‘redux‘
    import * as reducers from ‘../reducers‘
    
    const todoApp = combineReducers(reducers)
    export default createStore(todoApp)

     

    修改src/index.js,里面的HOME,PAGE1,PAGE2组件应该分别抽离在不同的页面中

    // src/index.js
    import React from ‘react‘
    import { render } from ‘react-dom‘
    import {
        HashRouter,
        Route,
        Switch,
        Redirect,
    } from ‘react-router-dom‘
    import { Provider, connect } from ‘react-redux‘
    import store from ‘./models/stores‘
    import {
        CREATE_TODO,
        DELETE_TODO,
        CREATE_TYPE,
        DELETE_TYPE,
    } from ‘./models/actions‘
    import styles from ‘./index.less‘
    
    const HomeOld = (props) => {
        const {
            todos = [],
            operateCounter: {
                createCounter = 0,
                deleteCounter = 0,
            },
        } = props;
        return (
            
                
    HOME HOHOHO
    当前todos如下,可以在page1与page2中操作todos列表:
    添加操作: {createCounter} 次,删除操作: {deleteCounter} 次
    {todos.map(({ text, id }) => (
  • {`id:${id}-text:${text}`}
  • ))} > ) } const mapStateToPropsHome = state => { return { todos: state.todos, operateCounter: state.operateCounter, }; }; const Home = connect(mapStateToPropsHome)(HomeOld); const Page1Old = (props) => { const { todos = [], dispatch } = props; let input; function onClick() { const { id = 0 } = [...todos].pop() || {}; dispatch({ type: CREATE_TODO, id: id + 1, text: input.value, }); dispatch({ type: CREATE_TYPE }); } return (
    PAGE1 HOHOHO
    { input = node }} />    {todos.map(({ text, id }) => (
  • {`id:${id}-text:${text}`}
  • ))} > ) } const mapStateToPropsPage1 = state => { return { todos: state.todos, }; }; const Page1 = connect(mapStateToPropsPage1)(Page1Old); const Page2Old = (props) => { const { todos = [], dispatch } = props; function onClick(id) { dispatch({ type: DELETE_TODO, id, }); dispatch({ type: DELETE_TYPE }); } return (
    PAGE2 HOHOHO
    {todos.map(({ text, id }) => (
  • {`id:${id}-text:${text}`}    null, id)}>删除该项
  • ))} > ) } const mapStateToPropsPage2 = state => { return { todos: state.todos, }; }; const Page2 = connect(mapStateToPropsPage2)(Page2Old); const App = () => (
    STAGE HOHOHO
  • 去home
  • 去page1
  • 去page2

  • ) render(, document.getElementById(‘root‘))

     

     接入react-router和react-redux完成,可以看到todolist,此处贴上完整的package.json

    {
      "name": "stage",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "start": "webpack-dev-server --config ./build/webpack.config.js --open",
        "build": "webpack --config ./build/webpack.config.js"
      },
      "author": "",
      "license": "ISC",
      "dependencies": {
        "react": "^16.13.1",
        "react-dom": "^16.13.1",
        "react-redux": "^7.2.0",
        "redux": "^4.0.5"
      },
      "devDependencies": {
        "@babel/core": "^7.10.4",
        "@babel/preset-env": "^7.10.4",
        "@babel/preset-react": "^7.10.4",
        "babel-loader": "^8.1.0",
        "clean-webpack-plugin": "^3.0.0",
        "css-loader": "^3.6.0",
        "html-webpack-plugin": "^4.3.0",
        "less": "^3.11.3",
        "less-loader": "^6.2.0",
        "react-router-dom": "^5.2.0",
        "style-loader": "^1.2.1",
        "webpack": "^4.43.0",
        "webpack-cli": "^3.3.12",
        "webpack-dev-server": "^3.11.0"
      }
    }

     

     ###### ###### ###### ###### ###### ###### ###### ###### ###### ###### ######

    接入typescript

     

    npm install -D @types/react @types/react-dom @types/react-router-dom @types/react-redux typescript ts-loader
    npm install -g typescript
    tsc -init

     

    修改生成的tsconfig.json

    {
      "compilerOptions": {
        "jsx": "react",
        "target": "es5", 
        "module": "commonjs", 
        "sourceMap": true,                     
        "removeComments": true,                
        "strict": true, 
        "noImplicitAny": true,                 
        "esModuleInterop": true, 
        "skipLibCheck": true, 
        "forceConsistentCasingInFileNames": true 
      },
      "include": [
        "src/**/*"
      ],
      "exclude": [
        "node_modules",
        "build",
      ]
    }

     

    将src/models/*/index.js 都改为index.ts并加入相应变量类型

    // src/models/actions/index.ts
    export const CREATE_TODO: string = ‘CREATE‘; // 增加一个todo
    export const DELETE_TODO: string = ‘DELETE‘; // 删除一个todo
    export const CREATE_TYPE: string = ‘CREATE_TYPE‘; // 添加操作
    export const DELETE_TYPE: string = ‘DELETE_TYPE‘; // 删除操作

     

    // src/models/reducers/index.ts
    import {
        CREATE_TODO,
        DELETE_TODO,
        CREATE_TYPE,
        DELETE_TYPE,
    } from ‘../actions‘
    
    interface TodoAction {
        type: string;
        id: number;
        text: string;
    }
    interface OperateAction {
        type: string;
    }
    export interface TodoState {
        id: number;
        text: string;
        completed: boolean;
    }
    export interface OperateState {
        createCounter: number;
        deleteCounter: number;
    }
    
    export function todos(state: TodoState[] = [], action: TodoAction) {
        switch (action.type) {
            case CREATE_TODO: {
                return [...state, { id: action.id, text: action.text, completed: false }]
            }
            case DELETE_TODO: {
                return [...state].filter(({ id }) => id !== action.id)
            }
            default: {
                return state;
            }
        }
    }
    
    export function operateCounter(state: OperateState = { createCounter: 0, deleteCounter: 0 }, action: OperateAction) {
        const { createCounter, deleteCounter } = state;
        switch (action.type) {
            case CREATE_TYPE: {
                return { ...state, createCounter: createCounter + 1 }
            }
            case DELETE_TYPE: {
                return { ...state, deleteCounter: deleteCounter + 1 }
            }
            default: {
                return state;
            }
        }
    }

     

    // src/models/stores/index.ts
    import { combineReducers, createStore } from ‘redux‘
    import * as reducers from ‘../reducers‘
    
    const todoApp = combineReducers(reducers)
    export default createStore(todoApp)

     

    将src/index.js 改为src/index.tsx,并添加相应接口,指定变量类型

    // src/index.tsx
    import React from ‘react‘
    import { render } from ‘react-dom‘
    import {
        HashRouter,
        Route,
        Switch,
        Redirect,
    } from ‘react-router-dom‘
    import { Provider, connect } from ‘react-redux‘
    import { Dispatch } from ‘redux‘
    import store from ‘./models/stores‘
    import {
        CREATE_TODO,
        DELETE_TODO,
        CREATE_TYPE,
        DELETE_TYPE,
    } from ‘./models/actions‘
    import { TodoState, OperateState } from ‘./models/reducers‘
    import styles from ‘./index.less‘
    
    interface HomeProps {
        todos: TodoState[];
        operateCounter: OperateState;
        dispatch: Dispatch;
    }
    
    const HomeOld: React.FC = (props) => {
        const {
            todos = [],
            operateCounter: {
                createCounter = 0,
                deleteCounter = 0,
            },
        } = props;
        return (
            
                
    HOME HOHOHO
    当前todos如下,可以在page1与page2中操作todos列表:
    添加操作: {createCounter} 次,删除操作: {deleteCounter} 次
    {todos.map(({ text, id }) => (
  • {`id:${id}-text:${text}`}
  • ))} > ) } const mapStateToPropsHome = (state: HomeProps) => { return { todos: state.todos, operateCounter: state.operateCounter, }; }; const Home = connect(mapStateToPropsHome)(HomeOld); const Page1Old: React.FC = (props) => { const { todos = [], dispatch } = props; let input: HTMLInputElement | null; function onClick() { const { id = 0 } = [...todos].pop() || {}; dispatch({ type: CREATE_TODO, id: id + 1, text: (input as HTMLInputElement).value, }); dispatch({ type: CREATE_TYPE }); } return (
    PAGE1 HOHOHO
    { input = node }} />    {todos.map(({ text, id }) => (
  • {`id:${id}-text:${text}`}
  • ))} > ) } const mapStateToPropsPage1 = (state: HomeProps) => { return { todos: state.todos, }; }; const Page1 = connect(mapStateToPropsPage1)(Page1Old); const Page2Old: React.FC = (props) => { const { todos = [], dispatch } = props; function onClick(id: number) { dispatch({ type: DELETE_TODO, id, }); dispatch({ type: DELETE_TYPE }); } return (
    PAGE2 HOHOHO
    {todos.map(({ text, id }) => (
  • {`id:${id}-text:${text}`}    删除该项
  • ))} > ) } const mapStateToPropsPage2 = (state: HomeProps) => { return { todos: state.todos, }; }; const Page2 = connect(mapStateToPropsPage2)(Page2Old); const App = () => (
    STAGE HOHOHO
  • 去home
  • 去page1
  • 去page2

  • ) render(, document.getElementById(‘root‘))

     

    同时需要修改build/webpack.config.js,修改入口文件将原来的index.js改为index.tsx,添加resolve配置

    // build/webpack.config.js
    const path = require(‘path‘);
    const HtmlWebpackPlugin = require(‘html-webpack-plugin‘);
    const { CleanWebpackPlugin } = require(‘clean-webpack-plugin‘);
    
    module.exports = {
        entry: ‘./src/index.tsx‘,
        output: {
            filename: ‘bundle.js‘,
            path: path.resolve(__dirname, ‘dist‘),
        },
        resolve: {
            extensions: [‘.ts‘, ‘.tsx‘, ‘.js‘, ‘.jsx‘, ‘.json‘],
        },
        devServer: {
            port: 3001,
        },
        devtool: ‘inline-source-map‘,
        plugins: [
            new CleanWebpackPlugin(),
            new HtmlWebpackPlugin({
                template: ‘./src/index.html‘,
            })
        ],
        module: {
            rules: [
                {
                    test: /\.tsx?$/,
                    exclude: /node_modules/,
                    loader: ‘ts-loader‘,
                },
                {
                    test: /\.js$/,
                    exclude: /node_modules/,
                    loader: ‘babel-loader‘,
                },
                {
                    test: /\.less$/,
                    exclude: /node_modules/,
                    use: [
                        ‘style-loader‘,
                        { loader: ‘css-loader‘, options: { modules: true } },
                        ‘less-loader‘,
                    ]
                },
            ]
        }
    };

     

    cd ../../ && mkdir types && cd types && touch global.d.ts // 在src目录下创建types文件夹添加global.d.ts文件

     

    // src/types/global.d.ts
    declare module ‘*.svg‘
    declare module ‘*.png‘
    declare module ‘*.jpg‘
    declare module ‘*.jpeg‘
    declare module ‘*.gif‘
    declare module ‘*.bmp‘
    declare module ‘*.tiff‘
    declare module ‘*.less‘

     重启服务

    菜鸟webpack/react/redux/react-router/ts一步步搭建架子

    标签:browser   pen   tsx   title   put   执行   echo   count   hash   

    原文地址:https://www.cnblogs.com/mapingchuan/p/13262685.html


    评论


    亲,登录后才可以留言!