从零开始,构建属于自己的React项目,不使用脚手架,自己写webpack配置来实现

反正突然脑子一热,就想试一试

第一步,先定义好文件目录结构

config

webpack.base.js
webpack.dev.js
webpack.pro.js

dist(打包自己生成的)

js
css
index.html

src(主目录)

actions
apis
assets
components
mocks
pages
reducers
routes
store
index.jsx(入口文件)

.babelrc(babel的相关配置)

.gitignore(git提交时忽略的文件)

.npmignore(npm发布包时忽略的文件)

package.json(包配置文件)

README.md(项目说明文件)

第二步,开始写webpack相应的配置,我的配置是从官网上下载下来,然后进行部分的修改。

// webpack.base.js
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

const resolve = (url) => {
  return path.resolve(__dirname, url);
}

module.exports = {
  // mode: "development", // "production" | "development" | "none"  // Chosen mode tells webpack to use its built-in optimizations accordingly.
  entry: resolve("../src/index"), // string | object | array  // 这里应用程序开始执行
  // webpack 开始打包
  output: {
    // webpack 如何输出结果的相关选项
    path: resolve("../dist/js"), // string
    // 所有输出文件的目标路径
    // 必须是绝对路径(使用 Node.js 的 path 模块)
    filename: "js/index.js", // string    // 「入口分块(entry chunk)」的文件名模板(出口分块?)使用HtmlWebpackPlugin时引入的js文件就是这个路径
    publicPath: "/", // string    // 输出解析文件的目录,url 相对于 HTML 页面
    library: "MyLibrary", // string,
    // 导出库(exported library)的名称
    libraryTarget: "umd", // 通用模块定义    // 导出库(exported library)的类型
    /* 高级输出配置(点击显示) */
  },

  module: {
    // 关于模块配置
    rules: [
      // 模块规则(配置 loader、解析器等选项)
      {
        test: /.js|jsx$/,
        exclude: /node_modules/,
        include: [
          resolve("../src")
        ],
        // 这里是匹配条件,每个选项都接收一个正则表达式或字符串
        // test 和 include 具有相同的作用,都是必须匹配选项
        // exclude 是必不匹配选项(优先于 test 和 include)
        // 最佳实践:
        // - 只在 test 和 文件名匹配 中使用正则表达式
        // - 在 include 和 exclude 中使用绝对路径数组
        // - 尽量避免 exclude,更倾向于使用 include
        // issuer: { test, include, exclude },
        // issuer 条件(导入源)
        // enforce: "pre",
        // enforce: "post",
        // 标识应用这些规则,即使规则覆盖(高级选项)
        loader: "babel-loader",
        // 应该应用的 loader,它相对上下文解析
        // 为了更清晰,`-loader` 后缀在 webpack 2 中不再是可选的
        // 查看 webpack 1 升级指南。
        options: {
          presets: ["@babel/preset-env", "@babel/preset-react"]
        },
        // loader 的可选项
      },
      {
        test: /.html$/,
        use: [
          // 应用多个 loader 和选项
          "htmllint-loader",
          {
            loader: "html-loader"
          }
        ]
      },
      {
        test: /.less$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              // 这里可以指定一个 publicPath
              // 默认使用 webpackOptions.output中的publicPath
              publicPath: './css'
            },
          },
          // 'style-loader',
          'css-loader',
          'less-loader',
        ]
      },
      {
        test: /.css$/,
        exclude: /node_modules/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
          },
          // 'style-loader',
          'css-loader'
        ]
      },
      {
        test: /.(png|jpg|gif)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192
            }
          }
        ]
      }
    ],
  },
  resolve: {
    // 解析模块请求的选项
    // (不适用于对 loader 解析)
    modules: [
      "node_modules",
      resolve("src")
    ],
    // 用于查找模块的目录
    extensions: [".js", ".json", ".jsx", ".css", ".less"],
    // 使用的扩展名
    alias: {
      // 模块别名列表
      // "module": "new-module",
      // 起别名:"module" -> "new-module" 和 "module/path/file" -> "new-module/path/file"
      // "only-module$": "new-module",
      // 起别名 "only-module" -> "new-module",但不匹配 "only-module/path/file" -> "new-module/path/file"
      // "module": path.resolve(__dirname, "app/third/module.js"),
      // 起别名 "module" -> "./app/third/module.js" 和 "module/file" 会导致错误
      // 模块别名相对于当前上下文导入
      "Actions": resolve("../src/actions"),
      "Apis": resolve("../src/apis"),
      "Assets": resolve("../src/assets"),
      "Components": resolve("../src/components"),
      "Mocks": resolve("../src/mocks"),
      "Pages": resolve("../src/pages"),
      "Reducers": resolve("../src/reducers"),
      "Routes": resolve("../src/routes"),
      "Store": resolve("../src/store"),
    },
    /* 可供选择的别名语法(点击展示) */
    /* 高级解析选项(点击展示) */
  },
  plugins: [
    new MiniCssExtractPlugin({
      // 类似 webpackOptions.output里面的配置 可以忽略
      filename: 'css/[name].css',
      chunkFilename: 'css/[id].css',
    }),
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: resolve('../src/assets/index.html')
    }),
    new CleanWebpackPlugin(), // 打包之前清空dist文件夹
  ],
}
// webpack.dev.js
const DashboardPlugin = require('webpack-dashboard/plugin');

const path = require('path');
const webpack = require('webpack');

const config = require('./webpack.base');

const resolve = (url) => {
  return path.resolve(__dirname, url);
}

process.env.NODE_ENV = 'development';

module.exports = {
  ...config,
  mode: 'development',
  devtool: "source-map", // enum  // 通过在浏览器调试工具(browser devtools)中添加元信息(meta info)增强调试
  // 牺牲了构建速度的 `source-map' 是最详细的。
  context: __dirname, // string(绝对路径!)
  // webpack 的主目录
  // entry 和 module.rules.loader 选项
  // 相对于此目录解析

  target: "web", // 枚举  // 包(bundle)应该运行的环境
  // 更改 块加载行为(chunk loading behavior) 和 可用模块(available module)
  stats: "errors-only",  // 精确控制要显示的 bundle 信息
  devServer: {
    proxy: { // proxy URLs to backend development server
      '/api': 'http://localhost:8888'
    },
    publicPath: "/",
    contentBase: resolve('../dist/index.html'), // boolean | string | array, static file location
    compress: true, // enable gzip compression
    port: 3333,
    open: true,
    // historyApiFallback: true, // true for index.html upon 404, object for multiple paths
    hot: true, // hot module replacement. Depends on HotModuleReplacementPlugin
    after: function(app, server, compiler) {
      console.log(app, server, compiler);
    }
    // https: false, // true for self-signed, object for cert authority
    // noInfo: true, // only errors & warns on hot reload
    // ...
  },

  plugins: [
    ...config.plugins,
    // 编译时(compile time)插件
    // webpack-dev-server 强化插件
    new DashboardPlugin(),
    new webpack.HotModuleReplacementPlugin(),
  ]
}
// webpack.pro.js
const TerserPlugin = require('terser-webpack-plugin');
const webpack = require('webpack');

const config = require('./webpack.base');

process.env.NODE_ENV = 'production';

module.exports = {
  ...config,
  mode: 'production',
  devtool: "enum", // enum  // 通过在浏览器调试工具(browser devtools)中添加元信息(meta info)增强调试
  // 牺牲了构建速度的 `source-map' 是最详细的。
  // context: __dirname, // string(绝对路径!)
  // webpack 的主目录
  // entry 和 module.rules.loader 选项
  // 相对于此目录解析

  // target: "web", // 枚举  // 包(bundle)应该运行的环境
  // 更改 块加载行为(chunk loading behavior) 和 可用模块(available module)
  stats: "errors-only",  // 精确控制要显示的 bundle 信息
  optimization: {
    splitChunks: {
      cacheGroups: {
        commons: {
          name: "vendor",
          filename: 'vendor-[hash].min.js',
          minChunks: 2
        }
      }
    },
    minimizer: [
      new TerserPlugin({
        cache: true, // 开启缓存
        parallel: true, // 支持多进程
        sourceMap: true,
      }),
    ]
  },
  plugins: [
    ...config.plugins,
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': '"development"',
    }),
    // 构建优化插件,下面两个在最新的webpack中已经被挪进了配置中
    // new webpack.optimize.CommonsChunkPlugin({
    //   name: 'vendor',
    //   filename: 'vendor-[hash].min.js',
    // }),
    // new webpack.optimize.UglifyJsPlugin({
    //   compress: {
    //     warnings: false,
    //     drop_console: false,
    //   }
    // }),
    new webpack.IgnorePlugin(/^./locale$/, /moment$/),
  ]
}

第三步,编写入口文件

// index.jsx
import React from "react";
import ReactDOM from "react-dom";

import HelloWorld from 'Components/test/HelloWorld';

function render() {
  console.log(111);
  ReactDOM.render(
    <HelloWorld />,
    document.getElementById("root")
  );
}

render();

完成了,最基本的React项目的构建已经完成,后续可以针对自己的项目需要添加适当的配置。

遇到了哪些问题。

  1. 由于使用的webpack版本太新了,很多配置都变了。
  • 比如webpack.optimize.CommonsChunkPlugin变成了webpack配置项optimization.splitChunks
  • webpack.optimize.UglifyJsPlugin变成了webpack配置项optimization.minimizer
  • extract-text-webpack-plugin不兼容4.0之后的webpack,所以我找了mini-css-extract-plugin来替代它。
  1. webpack-dev-server开启时需要指定它的config文件,如webpack-dev-server --config config/webpack.dev.js

完整的demo示例可以去我的github上查看,这个demo的地址是https://github.com/810307015/ReactDemo

后续,后面可能要在里面加入多级路由,redux的相关配置,目前这个版本用来做最基础的演示已经足够了。

最后,如果你看到了这里,还不是直接滑到了底部,那么非常感谢你的阅读,希望你能点个订阅,评论一下,点个赞啥的。

原文地址:https://www.cnblogs.com/aloneMing/p/12956340.html