从0搭建一个基于webpack的react项目【实战篇】

笔者所处环境
node:v14.2.0
npm:6.14.4

搭建一个webpack项目分为如下几个步骤:

1. 创建一个项目目录,并进行npm初始化
2. 基础项目目录创建
3. 基础依赖包安装
4. webpack基础配置
5. package配置
6. 运行并打包项目

1、创建一个项目目录,并进行npm初始化

执行如下命令:

mkdir webpack-test // 创建一个目录
cd webpack-test
npm init -y // npm初始化,生成package文件

在这里插入图片描述

2、基础项目目录创建

创建基础的项目目录结构,包括public、scripts、src三个基础的项目目录。
(1)public下的index.html文件是项目所被访问到的页面,里面创建有个div用于后面react的挂载
(2)scripts是webpack的基础配置文件夹,下文会被谈到内部各个文件的作用
(3)src是项目的入口文件,也就是我们在package.json里面的main字段指向的路径,默认为src/index.js
![在这里插入图片描述](https://img-blog.csdnimg.cn/0e9ab1a896c04af3b8f5bcc1b90c1894.png

3、基础依赖包安装

在package.json里面我们可以通过dependencies、devDependencies、peerDependencies和optionalDependencies区管理区分我们安装的包,合理的安装包更有利于项目的健壮性,同时也避免了一些开发环境的包使项目打包体积变大。
(1)dependencies:应用依赖,或者叫做业务依赖/生产环境依赖,这是我们最常用的依赖包管理对象!它用于指定应用依赖的外部包,这些依赖是应用发布后正常执行时所需要的,但不包含测试时或者本地打包时所使用的包
(2)devDependencies:开发环境依赖,仅次于dependencies的使用频率!它的作用和dependencies一样,只不过它里面的包只用于开发环境,不用于生产环境,这些包通常是单元测试或者打包工具等,(如:webpack、loader、plugins等打包过程所需要的依赖)
(3)peerDependencies:常常用于开发依赖包时使用,在此目录下的依赖可以依靠宿主项目的依赖进行运作,这样可以避免重复装包所造成版本不一致的问题
(4)optionalDependencies:仅在使用特定功能时才需要包,请使用可选的对等依赖项

安装开发环境使用的包:
核心依赖:webpack、webpack-cli、webpack-dev-server、webpack-merge、babel等
loader依赖:
(1)babel-loader 用于处理js、jsx文件
(2)style-loader、css-loader、less-loader、postcss-loader、postcss-preset-env 用于css加载,less文件加载
(3)url-loader 用于处理图片和字体图标
插件:
(1)html-webpack-plugin、react-dev-utils 使能html使用模板语法
(2)optimize-css-assets-webpack-plugin css兼容优化处理
(3)react-refresh、@pmmmwh/react-refresh-webpack-plugin 引入热更新
(4)@babel/plugin-proposal-decorators 引入装饰器,项目支持装饰器写法
(5)css-minimizer-webpack-plugin、terser-webpack-plugin css压缩和js压缩(用于生产打包使用)
以下指令逐一指向进行开发依赖的安装,–save-dev 保存为开发依赖

npm i webpack webpack-cli webpack-dev-server webpack-merge --save-dev
npm i babel-loader @babel/core @babel/preset-flow babel-preset-react-app --save-dev
npm i style-loader css-loader less-loader postcss-loader postcss-preset-env url-loader --save-dev
npm i html-webpack-plugin react-dev-utils optimize-css-assets-webpack-plugin @pmmmwh/react-refresh-webpack-plugin  @babel/plugin-proposal-decorators  --save-dev
npm i css-minimizer-webpack-plugin terser-webpack-plugin --save-dev

安装生产环境使用的包:react、react-dom

npm i react react-dom --save-dev

4、基础依赖包安装

编写scripts文件代码,主要为start文件,用于运行开发环境、打包项目的基础脚本文件;webpack.common.js是生产、开发环境共用的基础配置;webpack.dev.js是开发环境webpack配置,webpack.prod.js是开发环境webpack配置

(1)webpack.common.js基础配置

module.exports = {
  resolve: {
    alias: { // 别名,可以通过别名快速访问到指定路径
      "~": "./src",
      "react-native": "react-native-web",
    },
    // 支持的拓展文件
    extensions: [".js", ".css", ".jsx", ".json", ".web.jsx", ".web.js", ".mjs"],
    // node核心模块加载
    fallback: {
      process: false,
    },
  },
};

(2)webpack.dev.js基础配置

const path = require('path');
const fs = require('fs');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin');

const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);

const publicPath = '/';

module.exports = {
  mode: process.env.NODE_ENV,
  target: 'web',
  devtool: 'cheap-module-source-map',
  // 入口
  entry: {
    app: './src/index.js'
  },
  // 输出
  output: {
    clean: true,
    filename: 'static/js/bundle.js',
    publicPath: publicPath,
    devtoolModuleFilenameTemplate: info =>
      path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'),
  },
  module: {
    rules: [
      // 处理图片和字体图标
      {
        test: /\.(png|gif|jpg|jpeg|woff|woff2|eot|ttf|otf|svg)$/,
        type: 'javascript/auto',
        use: [
          {
            loader: 'url-loader',
            options: {
              esModule: false,
              limit: 1000,
              name: '[name].[hash:8].[ext]',
            }
          }
        ]
      },
      // 处理jsx文件
      {
        test: /.jsx|.js?$/,
        exclude: /node_modules/,
        include: path.join(__dirname, '../src'),
        use: [
          {
            loader: 'babel-loader',
            options: {
              cacheDirectory: true,
            }
          }
        ],
      },
      // 匹配css文件
      {
        test: /.css$/,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              plugins: () => {
                require('postcss-preset-env')()
              }
            }
          }
        ]
      },
      // 匹配less文件
      {
        test: /.less$/,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: "less-loader",
            options: {
              modifyVars: { },
              lessOptions: {
                javascriptEnabled: true
              },
            }
          }
        ]
      }
    ]
  },
  plugins: [
    // html文件中引入的外部资源,生成创建html入口文件
    new HtmlWebpackPlugin({ filename: 'index.html', inject: 'body', template: resolveApp('public/index.html') }),
    // 解决html文件中标签语法问题
    new InterpolateHtmlPlugin(HtmlWebpackPlugin, process.env),
    // css兼容优化
    new OptimizeCssAssetsWebpackPlugin(),
    // 解决process无法获取问题
    new webpack.ProvidePlugin({ process: 'process' }),
    // 进度条
    new webpack.ProgressPlugin({ percentBy: 'entries' }),
     // 热更新
    new webpack.HotModuleReplacementPlugin(),
    new ReactRefreshPlugin({
      forceEnable: true,
      exclude: [/node_modules/, /bootstrap\.jsx$/],
      overlay: false  //修改了错误叠加集成在插件中的工作方式,false则不显示报错
    }),
  ],
  optimization: {
    usedExports: true,
    providedExports: true,
  },
  devServer: {
    open: true,
    historyApiFallback: true, // 使用history模式需要进行开启
    hot: true,
    port: 3000,
    host: "localhost",
    proxy: {
      "/api": {
        target: "https://localhost:3001",
        secure: false,
        changeOrigin: true
      }
    }
  }
}

(3)webpack.prod.js基础配置

const path = require('path');
const fs = require('fs');
const webpack = require('webpack');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const TerserPlugin = require("terser-webpack-plugin");

const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);

const publicPath = '/';
const staticName = './';

module.exports = {
  mode: process.env.NODE_ENV,
  bail: true,
  entry: './src/index.js',
  // 输出
  output: {
     // path: './dist',
    filename: staticName + `/main.${process.env.NODE_ENV}.js`,
    // 公共路径.
    publicPath: publicPath,
  },
  module: {
    rules: [
      // 处理图片和处理字体图标
      {
        test: /\.(png|gif|jpg|jpeg|woff|woff2|eot|ttf|otf|svg)$/,
        type: 'javascript/auto',
        use: [
          {
            loader: 'url-loader',
            options: {
              esModule: false,
              limit: 1000 * 1024, // 小于200k使用base64进行图片处理
              name: staticName + '/media/[name].[hash:8].[ext]',
            }
          }
        ]
      },
      // 处理jsx文件
      {
        test: /\.(js|jsx|mjs)$/,
        loader: "babel-loader",
        exclude: /node_modules/,
        include: path.join(__dirname, '../src'),
        options: {
          compact: true,
        },
      },
      // 匹配css文件
      {
        test: /.css$/,
        exclude: path.resolve(__dirname, 'node_modules'),
        use: [
          "style-loader",
          "css-loader",
          {
            loader: "postcss-loader",
            options: {
              ident: 'postcss',
              plugins: () => {
                require('postcss-preset-env')()
              }
            }
          }
        ]
      },
      // 匹配less文件
      {
        test: /.less$/,
        exclude: path.resolve(__dirname, 'node_modules'),
        use: [
          "style-loader",
          "css-loader",
          {
            "loader": "less-loader",
            "options": {
              "modifyVars": { },
              "lessOptions": {
                "javascriptEnabled": true
              },
            }
          }
        ]
      }
    ]
  },
  plugins: [
    // css兼容优化
    new OptimizeCssAssetsWebpackPlugin(),
    // 解决process无法获取问题
    new webpack.ProvidePlugin({ process: 'process' }),
    // 进度条
    new webpack.ProgressPlugin({ percentBy: 'entries' }),
  ],
  optimization: {
    // 告知 webpack使用TerserPlugin插件压缩
    minimize: true,
    // 确定那些由模块提供的导出内容
    providedExports: true,
    // 决定每个模块使用的导出内容
    usedExports: true,
    // 寻找模块图形中的片段,哪些是可以安全地被合并到单一模块中
    concatenateModules: true,
    // 告知 webpack 去辨识 package.json 中的 副作用 标记或规则,以跳过那些当导出不被使用且被标记不包含副作用的模块
    //识别package.json中的sideEffects以剔除无用的模块,用来做tree-shake
    //依赖于optimization.providedExports和optimization.usedExports
    sideEffects: true,
    // 告知 webpack 检测或移除这些 chunk
    removeEmptyChunks: true,
    //取代 new webpack.NoEmitOnErrorsPlugin(),编译错误时不打印输出资源。
    emitOnErrors: true,
    minimizer: [
      new TerserPlugin({
        parallel: true,
        exclude: /node_modules/,
        terserOptions: {
          compress: {
            warnings: true,
            drop_console: true,
            drop_debugger: true,
            pure_funcs: ['console.log']
          }
        }
      }),
      new CssMinimizerPlugin(),
    ],
  }
}

(4)start.js


const { merge } = require('webpack-merge');
const base = require('./webpack.common');

module.exports = function (env) {
  const mode = env.NODE_ENV;
  // 设置环境
  process.env.NODE_ENV = mode;
  process.env.BABEL_ENV = mode;
  // 选择环境配置
  const config = mode === 'production' ? require('./webpack.prod') : require('./webpack.dev');
  return merge(base, config)
};

5、package配置

package.json的配置主要有scripts,表示我们需要启动项目需要执行的脚本,在写入执行命令后,scripts的指令将会生成一个脚本存放在node_modules文件的.bin下进行运行。除了scripts我们可以在package里面配置其他的属性配置如babel配置。
(1)scripts配置
在scripts里面加入下面两条指令用于启动和打包项目

 "start": "webpack serve  --env NODE_ENV=development --config ./scripts/start.js",
 "build": "webpack --env NODE_ENV=production --config ./scripts/start.js"

(2)配置babel(也可以在根目录创建.babelrc文件进行配置也可)

"babel": {
    "plugins": [
      [
        "@babel/plugin-proposal-decorators",
        {
          "legacy": true
        }
      ]
    ],
    "presets": [
      "react-app",
      "@babel/preset-flow"
    ]
  }

6、运行并打包项目

src/index.js文件写入一个简单的demo测试

import React from "react";
import ReactDOM from "react-dom/client";

const App = () => {
  return "Hello Reai 🔥";
};

ReactDOM.createRoot(document.getElementById("root")).render(<App />);

(1)测试开发环境运行项目
运行如下命令

npm run start

结果如下:
在这里插入图片描述
(2)测试打包
运行如下命令

npm run build

结果如下:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值