# react-project-archictecture **Repository Path**: jaqea/react-project-archictecture ## Basic Information - **Project Name**: react-project-archictecture - **Description**: 基于Webpack搭建的React项目目录结构 - **Primary Language**: JavaScript - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-11-18 - **Last Updated**: 2021-01-01 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # react-project-archictecture 一个基于Webpack搭建的React项目目录结构。 所用技术: - 使用**React.js**作为编码语言。 - 使用**Webpack**进行项目打包及配置。 - 使用**Antd**组件进行页面编写。 - 集成**Sass**、**Less**进行样式编写。 - 使用**React-router4**管理路由。 - 使用**React-Redux+Immutable.js**管理数据。 - 使用**React-Saga**中间件处理异步操作。 - 使用**Axios**进行服务端交互。 - 使用**Eslint+Airbub、Prettier、Commitlint**配置规范代码。 ## 项目目录结构 ```cmd |-- react-project-archictecture |-- .babelrc # babel配置文件 |-- .browserslistrc # 浏览器兼容配置 |-- .editorconfig # 代码风格配置文件 |-- .eslintrc.js # eslint配置文件 |-- .gitignore # git忽略文件 |-- .prettierrc.js # Prettier配置文件 |-- commitlint.config.js # commitlint配置文件 |-- package.json |-- READEME.md |-- yarn-error.log |-- yarn.lock |-- public | |-- favicon.ico | |-- index.html |-- src # API接口 | |-- App.jsx | |-- index.js | |-- api | | |-- base.js # 环境域名配置 | | |-- modules # 模块接口 | | |-- demo.js | | |-- index.js | |-- assets # 全局样式 | | |-- images | | | |-- demo.png | | |-- styles | | |-- common.less # 入口文件 | | |-- layout.less # 全局布局样式 | | |-- mixin.less # 全局混合 | | |-- variable.less # 全局样式变量 | | |-- theme # antd主题配置 | | |-- base.js | |-- components # 全局组件 | | |-- AuthRoute # 路由权限组件 | | | |-- index.jsx | | |-- BaseComponent # 封装的基类组件 | | | |-- index.jsx | | |-- Loading # 路由加载效果组件 | | |-- index.jsx | | |-- index.less | |-- redux | | |-- index.js # 全局状态配置文件 | | |-- modules # 模块状态 | | |-- index.js # 出口文件 | | |-- demo | | |-- actions.js | | |-- constants.js | | |-- reducer.js | |-- routes # 全局路由 | | |-- HomeRoute.js | | |-- index.js | | |-- LoginRoute.js | | |-- NotFound.js | |-- sagas # saga中间件 | | |-- index.js | | |-- modules | | |-- demo.js | |-- utils # 全局公用方法 | | |-- http.js | |-- views | |-- index.js # 组件按需加载入口文件 | |-- Loadable.js # 封装组件按需加载 | |-- Home | | |-- index.jsx | | |-- index.less | |-- Login | | |-- index.jsx | |-- NotFound | |-- index.jsx |-- webpack |-- webpack.base.conf.js # webpack基础配置 |-- webpack.dev.conf.js # 开发环境配置 |-- webpack.prod.conf.js # 生产环境配置 ``` ## 全局配置 ### webpack 基础配置: ```js // /webpack/webpack.base.conf.js const path = require("path"); const webpack = require("webpack"); const resolve = (dir) => path.resolve(__dirname, dir); const jsonToStr = (json) => JSON.stringify(json); const isProd = process.env.NODE_ENV === "production"; module.exports = { entry: resolve("../src/index.js"), resolve: { extensions: [".js", ".json", ".jsx"], alias: { "@": resolve("../src"), assets: resolve("../src/assets"), api: resolve("../src/api"), views: resolve("../src/views"), components: resolve("../src/components"), reduxModules: resolve("../src/redux/modules"), sagas: resolve("../src/sagas"), }, }, module: { rules: [ { test: /\.(png|svg|jpg|gif)$/, use: [ { loader: "file-loader", options: { // 图片小于10kb就是图片地址,大于正常打包成base64格式编码 limit: 10000, // 输出路径 outputPath: "img/", // 指定生成的目录 name: "img/[name].[hash:7].[ext]", }, }, ], }, { test: /\.(woff|woff2|eot|ttf|otf|)$/, use: ["file-loader"], }, { test: /\.(js|jsx)$/, use: ["babel-loader"], exclude: /node_modules/, include: resolve("../src"), }, { test: /\.(js|jsx)$/, include: resolve("../src"), enforce: "pre", loader: "eslint-loader", options: { fix: true, emitWarning: true, }, }, ], }, plugins: [ // 定义全局变量 new webpack.DefinePlugin({ "process.env.NODE_ENV": isProd ? jsonToStr("production") : jsonToStr("development"), }), ], performance: false, }; ``` 开发环境配置: ```js // /webpack/webpack.dev.conf.js const { merge } = require("webpack-merge"); const webpack = require("webpack"); const path = require("path"); const resolve = (dir) => path.resolve(__dirname, dir); const HtmlWebpackPlugin = require("html-webpack-plugin"); const baseWebpack = require("./webpack.base.conf"); const PROXY_URL = require("../src/api/base"); const antdThemeOption = require("../src/assets/styles/theme/base"); module.exports = merge(baseWebpack, { mode: "development", output: { path: resolve("../dist"), filename: "js/[name].js", chunkFilename: "js/[name].chunk.js", }, devtool: "source-map", devServer: { contentBase: resolve("../public"), port: "8080", inline: true, historyApiFallback: true, disableHostCheck: true, hot: true, open: true, proxy: { "/api": { target: PROXY_URL.development, changeOrigin: true, pathRewrite: { "^/api": "/", }, }, }, }, module: { rules: [ { test: /\.css$/, use: ["style-loader", "css-loader"], }, { test: /\.less$/, use: [ "style-loader", "css-loader", { loader: "less-loader", options: { lessOptions: { modifyVars: antdThemeOption, javascriptEnabled: true, }, }, }, { loader: "style-resources-loader", options: { patterns: resolve("../src/assets/styles/common.less"), }, }, ], }, { test: /\.scss$/, use: ["style-loader", "css-loader", "sass-loader"], }, ], }, optimization: { usedExports: true, splitChunks: { chunks: "all", }, }, plugins: [ new webpack.HotModuleReplacementPlugin({ multiStep: true, }), new HtmlWebpackPlugin({ filename: "index.html", template: resolve("../public/index.html"), }), ], }); ``` 生产环境配置: ```js // /webpack/webpack.prod.conf.js const { merge } = require("webpack-merge"); const path = require("path"); const resolve = (dir) => path.resolve(__dirname, dir); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const CopyWebpackPlugin = require("copy-webpack-plugin"); const OptimizeCss = require("optimize-css-assets-webpack-plugin"); const TerserWebpackPlugin = require("terser-webpack-plugin"); const baseWebpack = require("./webpack.base.conf"); module.exports = merge(baseWebpack, { mode: "production", devtool: false, output: { path: resolve("../dist"), filename: "static/js/[name].[chunkhash:8].js", }, optimization: { minimizer: [ new OptimizeCss(), new TerserWebpackPlugin({ // 压缩es6 terserOptions: { // 启用文件缓存 cache: true, // 使用多线程并行运行提高构建速度 parallel: true, // 使用 SourceMaps 将错误信息的位置映射到模块 sourceMap: true, }, }), ], }, module: { rules: [ { test: /\.(css|sass|scss|less)$/, use: [ MiniCssExtractPlugin.loader, { loader: "css-loader", options: { importLoaders: 2, sourceMap: false, }, }, { loader: "postcss-loader", options: { sourceMap: false, }, }, { loader: "sass-loader", options: { sourceMap: false, }, }, { loader: "less-loader", options: { sourceMap: false, }, }, { loader: "style-resources-loader", options: { patterns: resolve("../src/assets/styles/common.less"), }, }, ], }, ], }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ template: resolve("../public/index.html"), title: "vue-project-archictecture", favicon: resolve("../public/favicon.ico"), // 压缩html minify: { removeComments: true, collapseWhitespace: true, removeAttributeQuotes: true, }, }), new MiniCssExtractPlugin({ filename: "static/css/[name].[contenthash:8].css", }), new CopyWebpackPlugin({ patterns: [ { from: resolve("../public"), to: resolve("../dist"), }, ], }), ], }); ``` ### .eslintrc.js ESLint基本配置。 ```js // .eslintrc.js module.exports = { env: { browser: true, es2020: true, }, settings: { "import/resolver": { webpack: { config: "./webpack/webpack.base.conf.js", }, }, }, extends: ["eslint:recommended", "airbnb-base", "plugin:prettier/recommended"], parserOptions: { parser: "babel-eslint", ecmaFeatures: { jsx: true, }, ecmaVersion: 2020, sourceType: "module", }, plugins: ["react", "prettier", "babel"], rules: { "prettier/prettier": "error", quotes: [1, "double"], "import/extensions": [ "error", "always", { js: "never", jsx: "never", }, ], "global-require": 0, "import/prefer-default-export": 0, "import/no-extraneous-dependencies": 0, "no-console": 0, "no-param-reassign": 0, "no-unused-vars": 0, "class-methods-use-this": 0, "no-underscore-dangle": 0, "no-useless-constructor": 0, "no-restricted-syntax": 0, "no-alert": 0, }, }; ``` ### .prettierrc.js Prettier格式化规则配置。 ```js // .prettierrc.js module.exports = { printWidth: 100, //一行的字符数,如果超过会进行换行,默认为80 tabWidth: 2, //一个tab代表几个空格数,默认为80 useTabs: false, //是否使用tab进行缩进,默认为false,表示用空格进行缩减 singleQuote: false, //字符串是否使用单引号,默认为false,使用双引号 semi: true, //行位是否使用分号,默认为true trailingComma: 'es5', //是否使用尾逗号,有三个可选值"" bracketSpacing: true, //对象大括号直接是否有空格,默认为true,效果:{ foo: bar } }; ``` ### commitlint.config.js commitlint 基础配置,用于检查 `git commit -m ''` 格式。 ```js // .commitlint.config.js module.exports = { // 继承默认配置 extends: ["@commitlint/config-angular"], // 自定义规则 rules: { "type-enum": [ 2, "always", ["test", "upd", "feat", "fix", "refactor", "docs", "chore", "style", "revert"], ], "header-max-length": [0, "always", 72], }, }; ``` `commit`的格式要求如下: ```cmd Type():