美文网首页
Webpack5 + React18 项目搭建

Webpack5 + React18 项目搭建

作者: 一个好昵称X | 来源:发表于2023-11-27 19:14 被阅读0次

记录 React 从 0 到 1 搭建过程

1、安装 node 环境

搭建前端项目前,首先要安装本机的 node 环境,直接去 node 官网下载对应的安装包手动安装即可。 官网传送门;
安装完成后,在命令行工具执行以下命令,查看是否安装成功:

    node -v
    npm -v
image.png

2、项目搭建

2.1、初始化项目

新建文件夹 react-demo;
进入 react-demo 内,在命令行工具内执行 npm init ,初始化项目。
按照提示操作完成后,文件夹内会生成一个 package.json 文件。


image.png
2.2、安装 webpack

执行一下命令安装 webpack,推荐使用 yarn 安装。
使用 yarn 前需要先全局安装:

  npm i -g yarn

安装webpack:

  yarn add webpack webpack-cli

安装完后会在项目根目录生成一个 yarn.lock 文件,该文件用来锁定当前依赖的版本号,后续再执行install 时,会直接安装该文件内的依赖版本,不会再自动更新 package.json 中的依赖版本。
要想更新 package.json 中的依赖版本需要执行如下命令覆盖:

  yarn add xxx@xxx
2.3、初始化项目结构

在src目录下,新增 index.js,作为项目的入口文件,也是 webpack 配置的入口文件;
根目录下新增 src 文件夹,用于存放源码;
根目录下新增 config 文件夹,用于存放 webpack 和 项目相关配置的文件;

在 config 文件加下新增 webpack.config.js 文件,在该文件内加入如下内容,配置 webpack 基本信息

  const path = require('path');

  module.exports = {
    mode: 'development', // 指定 webpack 打包环境
    entry: './src/index.js', // 项目的入口文件,路径相对于项目根路径
    output: {                       
      filename: 'bundle.js', // 打包输出的文件名,实际项目中需要按一定规则动态生成
      path: path.resolve(__dirname, '../dist') // 打包完成后输出的路径,这里的路径针对的是当前目录
    }
  };

在根目录 index.js 文件中,加入一个打印日志,用来测试打包是否成功:


image.png

配置 package.json 内的 script 命令,用来执行打包操作:
增加如下命令:

  "build": "webpack --config ./config/webpack.config.js",
image.png

在命令行工具内 执行如下命令,来进行打包:

  yarn build

打包完成后,在根目录会生成一个 dist 目录,里面有 bundle.js 文件,即打包后的文件。


image.png

打包过程中可能出现的报错:

  • invalid or unexpected token
    产生该问题的原因可能有以下几种;
  1. webpack 配置文件配置错误或者是语法错误
  2. 缺少loader
    某些非 js 文件的资源,缺少对应的 loader ,也可能会导致此问题。
  3. 文件编码问题
    排除问题时,可以尝试是否文件编码的问题,增加 webpack-encoding-plugin 配置,修改编码。
    首先安装 webpack-encoding-plugin 依赖,
  yarn add webpack-encoding-plugin

在 webpack 配置文件中增加如下配置:

  const EncodingPlugin = require('webpack-encoding-plugin');
  
  // 增加 plugin 配置
  plugins: [
    new EncodingPlugin({
      encoding: 'utf-8'
    })
  ]

重新打包

2.4 增加核心配置

安装 babel 核心依赖,这样依赖会将 ES5+ 的语法转换成 ES5,目前ES5+的语法还不能直接在浏览器编译,因此需要安装这些依赖对ES5+的代码进行一个转换。

  yarn add babel-loader @babel/core @babel/preset-env @babel/preset-react

依赖安装完成后,在项目根目录新建文件 .babelrc,并加入如下内容:

{
  "presets": [ // 相当于插件集合,不需要在 plugins 中再配置多个插件
    [
      "@babel/preset-env", {
        "useBuiltIns": "entry",    //如果用了@babel/polyfill的话,配置这个属性可以将@babel/polyfill按需引入
        "corejs": 2
      }
    ],
    "@babel/preset-react"
  ],
  "plugins": []
}

module.exports = babelConfig;

修改 webpack.config.js,新增module,配置 babel-loader:

  module: {
    rules: [
      // 配置babel-loader
      {
        test: /\.(js|jsx)$/,
        use: ["babel-loader"],
        include: path.join(__dirname, "../src"),
        exclude: /node_modules/,
      },
    ],
  },

配置完成后,在入口文件内新增 es6+ 的内容,并重新打包,测试配置是否成功。

上述配置完成后,因为 babel 只会转换 es6 语法,而不会转换新的 api (如 promise,generator 函数等),让新的 api 生效的方法是使用传统的 polyfill,为此需要安装 @babel/polyfill 这个模块。

安装 @babel/polyfill

  yarn add @babel/polyfill 

@babel/polyfill 安装完成后不再需要进行额外的配置。

安装完 @babel/polyfill 之后,还需要安装以下3个库,来实现按需加载的效果:

  yarn add @babel/runtime @babel/plugin-transform-runtime @babel/plugin-syntax-dynamic-import @babel/plugin-proposal-class-properties

这几个库的作用可参考知乎文章
一文谈完前端项目中的Babel配置

上述依赖安装完成后,增加 babel 相关配置,在 .babelrc 文件中增加以下配置:

  plugins: [
    "@babel/plugin-syntax-dynamic-import",
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-transform-runtime"
  ]

入口文件内,新增 Promise 代码,重新打包,测试打包效果。

  const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('sync log');
    }, 1000);
  });

  promise.then(res => {
    console.log(res);
  });

新增sass/less 等css预处理器解析配置
安装以下依赖

   yarn add stylus stylus-loader less less-loader sass-loader node-sass css-loader style-loader

在webpack 配置文件中,配置解析器, 新增loader:

    // 配置css预处理器
    {
      test: /\.less$/,
      use: [
        "style-loader",
        "css-loader",
        "less-loader",
      ],
    },

配置完成后,根目录下新增 index.less 文件,加入样式配置:

  @primary-color: #29BEDB;

  div {
    color: @primary-color;
    font-size: 14px;
    font-weight: 600;
  }

在 index.js 入口文件中,引入新增的样式文件,重新打包,查看打包效果。

在使用到 css3 的某些新特性时,为了适应不同浏览器的兼容性,需要为不同的浏览器增加不同的前缀,此时就需要用到 postcss-loader 来为css代码自动添加前缀。
安装依赖

  yarn add postcss-loader autoprefixer cssnano

配置loader:

      {
        test: /\.less$/,
        use: [
          "style-loader",
          "css-loader",
          {
            loader: "postcss-loader",
            options: {
              postcssOptions: {
                plugins: [
                  require("autoprefixer"),
                  require("cssnano")
                ],
              },
            },
          },
          "less-loader",
        ],
      },

配置静态资源解析

在项目中用到的图片等资源,都需要配置相关的解析工具,安装如下依赖:

  yarn add file-loader url-loader

file-loader可以用来帮助 webpack 打包处理一系列的图片文件。打包后的每张图片都会生成一个随机的 hash 值作为图片名;url-loader 封装了 file-loader,其工作原理:1、文件大小小于limit 数值,url-loader 将会把文件转为Base64;2、文件大小大于 limit,url-loader 会调用 file-loader 进行处理,参数也会直接传给file-loader。

配置 loader,新增图片资源loader:

      //配置图片资源loader
      {
        test: /\.(jpg|png|jpeg|gif)$/,
        use: [
          {
            loader: "url-loader",
            options: {
              limit: 1024,
              fallback: {
                loader: "file-loader",
                options: {
                  name: "img/[name].[hash:8].[ext]",
                },
              },
            },
          },
        ],
      },

配置完成后,在 src 目录下新增 assets 静态资源目录,在目录内放入一张图片,在入口文件 index.js 文件中引入图片,重新打包。

  const img = require('./assets/img/profile.jpg');

打包完成后,可以看到在dist目录内生成一个新的 img 目录,里面即是用到的图片文件,说明打包成功。

配置压缩js、css等文件,减小打包体积
安装依赖:

  yarn add mini-css-extract-plugin

在webpack配置文件中新增相关配置

  // 引入插件依赖
  const MiniCssExtractPlugin = require('mini-css-extract-plugin');

  // plugins内 实例化插件 
  plugins: [
    // 实例化插件 MiniCssExtractPlugin
    new MiniCssExtractPlugin({
      filename: "css/[name].[contenthash].css",
      chunkFilename: "css/[id].[contenthash].css",
    }),
  ],

  // 配置loader, module内新增loader
  // 配置css压缩插件
  {
    test: /\.css$/,
    use: [
      {
        loader: MiniCssExtractPlugin.loader,
        options: {
          publicPath: "../",
        },
      },
      "css-loader",
    ],
  },

配置完成后,在根目录新增 test.css 文件,在入口文件内引入,进行打包,查看效果。
打包成功后会在 dist 目录内出现 css 文件目录。


image.png

该插件之后压缩 css 文件,原 less 文件还会继续打包到 js 文件内。

抽离公共代码
打包后的 js 、css 文件中会有很多公共代码,如果不将这些代码进行抽离,最后打包文件会变得很大,所以需要使用 SplitChunksPlugin 插件抽离公共代码,只需在 webpack 配置文件中加入如下配置:

  optimization: {
    // 抽离公共代码插件的配置
    splitChunks: {
      cacheGroups: {
        // 打包公共模块
        commons: {
          chunks: "initial", // 代码分割时默认对异步代码生效 async,all:所有代码有效,initial:同步代码有效
          minChunks: 2, // 分割之前模块在块与块之间共享的最小次数
          minSize: 20000, // 提取公共部分的最小的大小(以字节为单位)
          name: "common", // 设为 false 将保持块的相同名称,因此不会不必要地更改名称(生产版本推荐)
        },
      },
    },
  },

配置解析器Resolve
Resolvers 解析器=用于告诉 webpack 去哪些目录下查找引用的模块,默认值是["node_modules"]。

webpack 配置文件下,新增如下内容:

  resolve: {
    extensions: [".js", ".jsx"], // 引入 js jsx文件时,引入路径不需携带文件后缀
    // 设置别名,@ 直接指向 src 目录
    alias: {
      "@": path.resolve(__dirname, "../src"),
    },
  },

** 配置热更新 **
新增如下依赖

  yarn add webpack-dev-server html-webpack-plugin  

webpack-dev-server 用于热更新,html-webpack-plugin 用于动态的将打包后的 css、js文件加入到 html 中,相关配置如下:

在 plugins 内新增 HtmlWebpackPlugin 实例化

    // 引入html模板插件
    const HtmlWebpackPlugin = require('html-webpack-plugin');

    //实例化Html模板模块
    // 不指定模板会在dist目录自动生成一个html文件
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "../index.html"),
    }),

    // 配置 devServer
    devServer: {
      // 配置热更新模块
      static: {
        directory: path.join(__dirname, "public"),
      },
      compress: true,
      hot: true,
      port: 3001,
    },

在根目录新增 index.html 文件,加入如下内容

  <!DOCTYPE html>
  <html>
    <head>
      <meta charset="utf-8">
      <title>Webpack Demo</title>
      <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
    <body>
      <div id="root"></div>
    </body>
  </html>

在 package.json 文件中 script 内新增启动命令:

  "start": "webpack-dev-server --config ./config/webpack.config.js",

控制台执行 yarn start 启动项目。

删除上一次的打包结果及记录
安装依赖

   yarn add clean-webpack-plugin

clean-webpack-plugin 帮我们在每次打包时,清除上一次打包的内容。
配置如下,直接实例化插件即可:

  // 引入插件
  const { CleanWebpackPlugin } = require("clean-webpack-plugin");

  // 实例化插件
  new CleanWebpackPlugin(),

3、集成 React

安装React、react-dom
首先安装 react、react-dom 依赖

  yarn add react react-dom

在入口文件内挂载 React

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

  import App from "@/App";

  // v18 的新方法
  const root = ReactDOM.createRoot(document.getElementById("root"));
  root.render(
    <React.StrictMode>
      <App />
    </React.StrictMode>
  );

在 src 目录下新增 App.js 文件:

  import React from "react";

  const App = () => {
    return <div> React18 + Webpack5 from 0 to 1 </div>;
  };

  export default App;

重新 yarn start 启动项目,启动成功


image.png

安装react-router-dom

  yarn add react-router-dom 

依赖安装完成后,在 src 目录下新增 routes 目录,用来存放页面组件代码。
在 routes 下新增 List.js,Detail.js 两个文件。


image.png

两个文件内分别加入不同的内容
List.js

  import React from 'react';

  export default function List() {
    return (
      <>
        this is list page.
      </>
    )
  }

Detail.js

  import React from "react";

  export default function Detail() {
    return <div style={{color: 'red'}}>this is detail page.</div>;
  }

此时在 src 目录下新增 config 目录,config目录下新增 routers.js 文件,用于存放页面路由。
routers.js 文件内加入如下内容:

  // 存放页面路由
  import List from "@/routes/List";
  import Detail from "@/routes/Detail";

  // 存放页面路由
  const routes = [
    {
      path: "/",
      component: List,
      exact: true,
    },
    {
      path: "/detail",
      component: Detail,
    },
  ];

  export default routes;

修改入口文件 index.js 如下:

  import React from "react";
  import ReactDOM from "react-dom/client";
  import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";

  import routes from "./config/routers";

  // v18 的新方法
  const root = ReactDOM.createRoot(document.getElementById("root"));
  root.render(
    <React.StrictMode>
      <Router>
        <Link to="/">列表</Link>
        &nbsp;&nbsp;
        <Link to="/detail">详情</Link>

        <Routes>
          {routes.map((item, key) => {
            if (item.exact) {
              return (
                <Route
                  exact
                  path={item.path}
                  element={<item.component />}
                  key={key}
                />
              );
            } else {
              return (
                <Route path={item.path} element={<item.component />} key={key} />
              );
            }
          })}
        </Routes>
      </Router>
    </React.StrictMode>
  );

启动项目,可点击列表 或详情 加载对应的内容:


image.png

相关文章

网友评论

      本文标题:Webpack5 + React18 项目搭建

      本文链接:https://www.haomeiwen.com/subject/ohlgwdtx.html