webpack5+react17+ts4 从零搭建项目结构

233 阅读3分钟

1、在项目文件夹下执行npm init生成package.json文件

2、创建src文件夹,并创建以下文件夹结构

image.png

3、安装相关依赖


// webpack、webpack-cli、webpack-dev-server
npm install webpack webpack-cli webpack-dev-server -D

// babel-loader、@babel/core、@babel/preset-env、@babel/preset-react、@babel/preset-typescript
npm install babel-loader -D
npm install @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript -D

// typescript
npm install typescript ts-loader -D

// react、react-dom、react-router、react-router-dom 及其类型声明
npm install react、react-dom、react-router、react-router-dom
npm install @types/react @types/react-dom @types/react-router @types/react-router-dom -D

// less、less-loader、css-loader、min-css-extract-plugin -D
npm install less less-loader css-loader -D
npm install min-css-extract-plugin -D

// 其他重要插件:cross-env、html-webpack-plugin、clean-webpack-plugin
npm install cross-env html-webpack-plugin clean-webpack-plugin -D

4、进行最基本的webpack配置,webpack.config.js、tsconfig.json和babel.config.json文件内容如下


// babel.config.json
{
  "presets": [
    [
      "@babel/preset-env",
      {
        // "targets": {
        //   "edge": "17",
        //   "firefox": "60",
        //   "chrome": "67",
        //   "safari": "11.1"
        // },
        // "useBuiltIns": "usage",
        // "corejs": "3.6.5"
        "targets": {
          "browsers": "last 2 versions"
        }
      }
    ],
    "@babel/preset-react",
    ["@babel/preset-typescript"]
  ]
}

// tsconfig.json
{
  "compilerOptions": {
    "esModuleInterop": true,
    "removeComments": true,
    "strict": true,
    "rootDir": "./src",
    "outDir": "./bundles",
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "jsx": "react",
    "sourceMap": true,
    // baseUrl、paths属性配置很重要,不然引入组件可能会报错:Cannot find module ‘xxx‘ or its corresponding type declarations
    "baseUrl": ".",
    "paths": {
      "*": ["src/*", "node_modules/*"]
    }
  },
  "include": ["src/"]
}


// webpack.config.js
const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

// 在最新的v2.5.0版本中使用的exports.default导出的,需得使用import引入,但是在这个配置文件中直接用import引入又会报错:Cannot use import statement outside a module。所以这里mini-css-extract-plugin使用的是v2.4.6版本
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

const isProduction = process.env.NODE_ENV === "production";

const devConfig = {
  devtool: "inline-source-map",

  devServer: {
    allowedHosts: "all",
    hot: true,
    port: 9003,
    open: true,
    historyApiFallback: true, // 未命中自动render index.html
  },
};

const config = {
  mode: isProduction ? "production" : "development",

  context: path.join(__dirname, "./src/entries"),

  entry: {
    // 该配置是以多页面为例,其中模板使用的跟src并齐的templates文件夹中的index.html和task.html
    index: "./index.tsx",
    task: "./task.tsx",
  },

  output: {
    path: path.join(__dirname, "dist"), // 绝对路径
    filename: "[name].bundle.js",
    chunkFilename: "[id].chunk.js",
  },

  resolve: {
    extensions: [".ts", ".tsx", ".js", "/json"],
  },

  module: {
    rules: [
      {
        test: /\.(ts|tsx)?$/,
        include: path.resolve(__dirname, "./src"),
        use: ["babel-loader", "ts-loader"],
      },
      {
        test: /\.(less|css)$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
          },
          {
            loader: "css-loader",
          },
          {
            loader: "less-loader",
          },
        ],
      },
    ],
  },

  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      inject: "body", // 将资源注入在body底部,默认是头部
      title: "index.html",
      filename: "index.html",
      template: path.join(__dirname, "templates/index.html"),
      chunks: ["index"], // 只会将index对应的bundle插入到html中
      entry: "index",
    }),
    new HtmlWebpackPlugin({
      inject: "body",
      title: "task.html",
      filename: "task.html",
      template: path.join(__dirname, "templates/task.html"),
      chunks: ["task"],
      entry: "task",
    }),
    new MiniCssExtractPlugin(),
  ],

  optimization: {
    splitChunks: {},
  },
};

module.exports = isProduction ? config : { ...config, ...devConfig };

5、配合react-router,搭建一个页面开发基础。文件内容如下:


// entries/index.tsx 入口文件
import React from "react";
import { render } from "react-dom";

import App from "../pages/App";

render(<App />, document.querySelector("#app"));


// pages/App.tsx 
import React from "react";
import { HashRouter, useRoutes } from "react-router-dom";

import routes from "../router";

import "./App.less";

const App = () => {
  const element = useRoutes(routes);
  return element;
};
// 注意:在reactRouter的V6版本中一定要这么包裹一层才可行,不然会报错
const AppWrapper = () => {
  return (
    <HashRouter>
      <App />
    </HashRouter>
  );
};

export default AppWrapper;

// router/index.tsx
import React from "react";
import { RouteObject } from "react-router";

import Home from "../pages/Home";
import Page1 from "../pages/page1";
import Page2 from "../pages/page2";

const route: RouteObject[] = [
  { path: "/", element: <Home /> }, // 首页
  { path: "/page1", element: <Page1 /> },
  { path: "/page2", element: <Page2 /> },
];

export default route;

以上就完成了开发结构的基本实现,具体的功能可以在路由对应的页面中进行开发。

改变url即可实现页面之间的切换,以Home页面为例,点击按钮即可将整个页面切换成路由对应页面:

import React from "react";
import { useNavigate } from "react-router";

const Home = () => {
  const navigate = useNavigate();

  return (
    <>
      <button
        onClick={() => {
          navigate("/page1");
        }}
      >
        去page1
      </button>
      <button
        onClick={() => {
          navigate("/page2");
        }}
      >
        去page2
      </button>
      home
    </>
  );
};

export default Home;

好了,现在就可以愉快的开始业务功能开发啦~