【react】webpack@5,babel,typescript,prettier,eslint自定义搭建脚手架完整版


项目目录

很多报错情况,本篇配置已解决,练习时可以直接用。正式项目可根据自己需求做相应调整和自定义优化。

在这里插入图片描述


package.json

{
  "name": "common_backstage",
  "version": "1.0.0",
  "description": "backstage of all stage",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack serve --config webpack.dev.js",
    "prettier": "prettier --write .",
    "build": "webpack"
  },
  "repository": {
    "type": "git",
    "url": "https://gitee.com/guozia007/common_backstage.git"
  },
  "keywords": [],
  "author": "",
  "license": "MIT",
  "devDependencies": {
    "@babel/core": "^7.21.3",
    "@babel/preset-env": "^7.20.2",
    "@babel/preset-react": "^7.18.6",
    "@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
    "@types/react": "^18.0.31",
    "@types/react-dom": "^18.0.11",
    "@typescript-eslint/eslint-plugin": "^5.57.0",
    "@typescript-eslint/parser": "^5.57.0",
    "babel-loader": "^9.1.2",
    "core-js": "^3.29.1",
    "css-loader": "^6.7.3",
    "css-minimizer-webpack-plugin": "^5.0.0",
    "eslint": "^8.37.0",
    "eslint-plugin-react": "^7.32.2",
    "eslint-webpack-plugin": "^4.0.0",
    "html-webpack-plugin": "^5.5.0",
    "less": "^4.1.3",
    "less-loader": "^11.1.0",
    "mini-css-extract-plugin": "^2.7.5",
    "postcss": "^8.4.21",
    "postcss-loader": "^7.1.0",
    "postcss-preset-env": "^8.2.0",
    "prettier": "^2.8.7",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-refresh": "^0.14.0",
    "style-loader": "^3.3.2",
    "terser-webpack-plugin": "^5.3.7",
    "ts-loader": "^9.4.2",
    "typescript": "^5.0.2",
    "webpack": "^5.76.3",
    "webpack-cli": "^5.0.1",
    "webpack-dev-server": "^4.13.1"
  },
  "dependencies": {},
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not dead"
  ]
}


public

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>common backstage</title>
</head>
<body>
    <noscript>
        You need to enable javascript to run this app.
    </noscript>
    <div id="root"></div>
</body>
</html>

src

src/types/typings.d.ts

/**
 * 自定义ts声明
 */

// 支持less scss等的模块化
declare module '*.module.scss' {
    const classes: { [key: string]: string };
    export default classes;
}
declare module '*.module.less' {
    const classes: { [key: string]: string };
    export default classes;
}

// 支持各种图片
declare module '*.png'
declare module '*.jpg'
declare module '*.jpeg'
declare module '*.gif'
declare module '*.svg'

src/index.tsx

import { createRoot } from 'react-dom/client';
import App from '@/App';

const root = createRoot(document.getElementById('root'));
root.render(
    <App />
)

src/App.tsx

import { useState } from "react";
import './app.less';

const App = () => {

    const [count, setCount] = useState(0);

    const handleClick = () => {
        setCount(count + 1);
    }

    return <>
        <h3>hello react!</h3>
        <button
            className="btn"
            onClick={handleClick}
        >You click it {count} times.</button>
    </>
}

export default App;

src/app.less

.btn {
    display: inline-block;
    box-sizing: border-box;
    border: 1px solid #ddd;
    border-radius: 6px;
    background-color: #fff;
    outline: none;
    padding: 5px 10px;
    font-family: cursive;
    user-select: none;
    -webkit-user-select: none;
    cursor: pointer;
}

eslintrc

.eslintrc.js

module.exports = {
    "env": {
        "browser": true,
        "es2021": true,
        "node": true
    },
    "extends": [
        "eslint:recommended",
        "plugin:react/recommended",
        // 配置后 不通过import引入React也不会报错了
        // 无需额外安装
        "plugin:react/jsx-runtime",
        "plugin:@typescript-eslint/recommended"
    ],
    "overrides": [
    	{
            files: ["**/*.tsx"],
            // react默认使用prop-types来检查类型
            // 如果使用了typescript,就把这个关掉,
            // 不然会报一些没有意义的错误
            rules: {
                "react/prop-types": "off"
            }
        }
    ],
    "parser": "@typescript-eslint/parser",
    "parserOptions": {
        "ecmaVersion": "latest",
        "sourceType": "module"
    },
    "plugins": [
        "react",
        "@typescript-eslint"
    ],
    "rules": {
    }
}


.gitignore

node_modules
dist
lib
.idea
.vscode

prettier

.prettierrc.json

{
    "printWidth": 80,
    "tabWidth": 4,
    "useTabs": true,
    "semi": true,
    "singleQuote": false,
    "quoteProps": "as-needed",
    "jsxSingleQuote": false,
    "trailingComma": "es5",
    "bracketSpacing": true,
    "jsxBracketSameLine": false,
    "arrowParens": "avoid",
    "requirePragma": false,
    "insertPragma": false,
    "proseWrap": "preserve"
}

.prettierignore

node_modules
build
dist
lib
*.md

babel

babel.config.js

module.exports = {
    presets: [
        [
            '@babel/preset-env',
            {
                // 配置该项后,就不需要使用@babel/plugin-transform-plugin了
                // 因为useBuiltIns和它一样的作用
                useBuiltIns: 'usage',
                corejs: {
                    version: '3.29.1',
                    proposals: true
                }
            }
        ],
        [
            '@babel/preset-react',
            {
                // 使用最新的react/jsx-runtime来解析jsx,
                // 而非React.createElement
                // 此时不需要import引入React
                runtime: 'automatic'
            }
        ]
    ]
}

postcss

postcss.config.js

module.exports = {
    plugins: [
        'postcss-preset-env'
    ]
}

tsconfig

{
    "compilerOptions": {
        // "outDir": "./dist/",
        // "sourceMap": true,
        "noImplicitAny": true,
        // 浏览器不支持CommonJS,所以这里选用ESNext
        "module": "ESNext",
        "target": "es5",
        // 不import引入React也不会报错了
        // 相当于支持了react/jsx-runtime功能
        "jsx": "react-jsx",
        // es模块引入和导出
        "esModuleInterop": true,
        // 类型声明文件所在目录
        "typeRoots": [
            "./src/types",
            "./node_modules/@types"
        ],
        // paths配置项的前缀目录
        "baseUrl": "./",
        // 配置文件别名
        "paths": {
            // @对应src,但是一定要同时配置@和@/*,不然会报错
            "@": ["src"],
            "@/*": ["src/*"]
        }
    },
    // 编译范围
    "include": [
        "./src/**/*"
    ]
}

webpack

webpack.dev.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ReactRefreshPlugin = require("@pmmmwh/react-refresh-webpack-plugin");
const EslintWebpackPlugin = require("eslint-webpack-plugin");

// 抽取公共部分
const getStyleOptions = (importLoaders, loader) => {
    return [
        "style-loader",
        {
            loader: "css-loader",
            options: {
                importLoaders
            }
        },
        "postcss-loader",
        loader
    ].filter(Boolean);
}

module.exports = {
    mode: 'development',
    // 入口文件不再是./src/index.js了
    entry: './src/index.tsx',
    module: {
        rules: [
            {
                oneOf: [
                    {
                        test: /\.css$/,
                        use: getStyleOptions(1)
                    },
                    {
                        test: /\.less$/,
                        use: getStyleOptions(2, 'less-loader')
                    },
                    {
                        test: /\.(js|jsx|ts|tsx)$/,
                        exclude: /node_modules/,
                        use: [
                            {
                                loader: 'babel-loader',
                                options: {
                                    cacheDirectory: true,
                                    cacheCompression: false
                                }
                            },
                            'ts-loader'
                        ]
                    }
                ]
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, 'public/index.html')
        }),
        // open HMR
        new ReactRefreshPlugin(),
        new EslintWebpackPlugin({
            exclude: 'node_modules',
            // 检查jsx js json文件
            extensions: ["jsx", "js", "json", 'ts', 'tsx'],
            // 只对修改内容的文件做检查
            lintDirtyModulesOnly: true,
            // 开启多线程
            threads: true
        })
    ],
    optimization: {},
    devtool: 'cheap-module-source-map',
    devServer: {
        port: 3000
    },
    resolve: {
        extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'],
        alias: {
            '@': path.resolve(__dirname, 'src')
        }
    }
}

webpack.config.js

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');

// 抽取公共部分
const getStyleOptions = (importLoaders, loader) => {
    return [
        MiniCssExtractPlugin.loader,
        {
            loader: "css-loader",
            options: {
                importLoaders
            }
        },
        "postcss-loader",
        loader
    ].filter(Boolean);
}

module.exports = {
    mode: 'production',
    entry: {
        index: './src/index.tsx'
    },
    output: {
        path: path.resolve(__dirname, 'lib'),
        filename: '[name].[contenthash:8].js',
        chunkFilename: '[name].[contenthash:8].chunk.js'
    },
    module: {
        rules: [
            {
                oneOf: [
                    {
                        test: /\.css$/,
                        use: getStyleOptions(1)
                    },
                    {
                        test: /\.less$/,
                        use: getStyleOptions(2, 'less-loader')
                    },
                    {
                        test: /\.(js|jsx|ts|tsx)$/,
                        exclude: /node_modules/,
                        use: [
                            {
                                loader: 'babel-loader',
                                options: {
                                    // 开启缓存
                                    cacheDirectory: true,
                                    // 开启压缩
                                    cacheCompression: true
                                }
                            },
                            'ts-loader'
                        ]
                    }
                ]
            }
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: '[name].[contenthash:8].css',
            chunkFilename: '[name].[contenthash:8].chunk.css',
            runtime: true
        }),
    ],
    optimization: {
        // 使用代码分割
        splitChunks: {
            chunks: 'all'
        },
        minimizer: [
            new CssMinimizerPlugin({
                exclude: /node_modules/,
                // 多进程压缩
                parallel: true,
                minimizerOptions: {
                    // 移除css注释
                    preset: [
                        "default",
                        {
                            discardComments: { removeAll: true },
                        }
                    ]
                }
            }),
            // 开启压缩
            new TerserPlugin({
                exclude: /node_modules/,
                // 开启多进程压缩
                parallel: true,
                // 删除注释
                terserOptions: {
                    // 开启gzip压缩
                    compress: true,
                    format: {
                        comments: false,
                    },
                },
                extractComments: false
            })
        ]
    },
    resolve: {
        extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'],
        alias: {
            '@': path.resolve(__dirname, 'src')
        }
    }
}

测试

npm start启动项目

在这里插入图片描述
打包成功后,打开页面即可看到效果

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值