基于WebPack完整构建React-TypeScript项目详细过程

475 阅读3分钟

项目预置代码仓库

(1)TypeScript处理;
(2)Scss/css及模块化处理;
(3)Jsx/Tsx处理;
(4)Png/svg处理;
(5)ContextProvider
(6)Router-v6
(7)Axios-模块;
(8)emnu/type/interface;
(9)@路径别名配置;
(9)合并清理无效代码;

具体实现步骤如下:

(1)cd 项目根目录
(2)npm init -y
(3)npm i webpack webpack-cli -D
(4)创建 webpack.config.js
 🔩配置开发模式以及出入口:
  mode: "production",
  entry: ["react-hot-loader/patch", "./src/index.tsx"],
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js",
  },
🔩配置rules及安装各种loader;
(1)babel-loader
(2)ts-loader
(3)css-loader
(4)sass-loader
(5)file-loader
(6)url-loader
 rules: [
      {
        test: /\.(js|jsx)$/,
        use: "babel-loader",
        exclude: /node_modules/,
      },
      {
        test: /\.ts(x)?$/,
        loader: "ts-loader",
        exclude: /node_modules/,
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
      {
        test: /(\.module)?.(sass|scss)$/,
        use: [
          "style-loader",
          {
            loader: "css-loader",
            options: {
              modules: {
                localIdentName: "[path][name]__[local]--[hash:base64:5]",
              },
              sourceMap: true,
            },
          },
          "sass-loader",
        ],
      },
      {
        test: /\.svg$/,
        use: "file-loader",
      },
      {
        test: /\.png$/,
        use: [
          {
            loader: "url-loader",
            options: {
              mimetype: "image/png",
            },
          },
        ],
      },
    ],
  },
🔩安装三个插件
(1)HtmlWebpackPlugin;
(2)BundleAnalyzerPlugin;
(3)MiniCssExtractPlugin;
  plugins: [
    new HtmlWebpackPlugin({
      templateContent: ({ htmlWebpackPlugin }) =>
        '<!DOCTYPE html><html><head><meta charset="utf-8"><title>' +
        htmlWebpackPlugin.options.title +
        '</title></head><body><div id="app"></div></body></html>',
      filename: "index.html",
    }),
    new BundleAnalyzerPlugin({
      analyzerMode: "static",
      openAnalyzer: false,
    }),
    new MiniCssExtractPlugin(),
  ],
🔩别名配置:
(1)tsconfig.json:
"baseUrl": "./",
        "paths": {
            "@/*": ["src/*"]
        },
(2)webpack.config.js:
resolve: {
    extensions: [".tsx", ".ts", ".js"],
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
🔩语法识别,可减少导入和支持通用的写法;
  tsconfig.json:
 "jsx": "react-jsx",
🔩css及scss模块隔离配置:
src下创建style.d.ts,手动让Ts识别
import styles from "./UserInfo.module.scss"这种写法;

style.d.ts内部:
declare module '*.css' {
   const styles: { readonly [key: string]: string };
   export default styles;
  }
  
declare module '*.scss' {
   const styles: { readonly [key: string]: string };
   export default styles;
 }
🔩Context配置
创建ContextProvider.tsx,⚠️⚠️需要注意是tsx不是ts
import { useReducer } from "react";
import { createContext } from "react"
interface ContentProps{
    children:JSX.Element|JSX.Element[]
}
export const ContentContext=createContext<any>({}as any)
const reducer = (state:any, action:any) => {
    switch (action.type) {
      case "changeToRed":
        return {
          ...state,
          colorValue: action.color,
        };
      case "changeToOrange":
        return {
          ...state,
          colorValue: action.color,
        };
      default:
        return state;
    }
  };
export const ContextProvider=({children}:ContentProps)=>{
    const [state, dispatch] = useReducer(reducer,{
        colorValue:"",
    });
    return (
        <ContentContext.Provider value={{state,dispatch}}>
            {children}
        </ContentContext.Provider>
    )
}
🔩包裹根组件:
  <ContextProvider>
    <Router>
      <App name="DingDang"/>
    </Router>
  </ContextProvider>,
🔩组件使用:
const { state, dispatch } = useContext(ContentContext);
dispatch({ type: "changeToRed", color: "red" });
🔩Axios拦截:
import axios, { AxiosRequestConfig, AxiosResponse } from "axios";
axios.interceptors.request.use((config:AxiosRequestConfig) => {
  console.log("拦截请求", config);
  if(!config)return;
  if(localStorage.getItem("token")) {
    config.headers!.Authorization = localStorage.getItem("token")??"";
  }
  return config;
});
axios.interceptors.response.use(
  (res: AxiosResponse<any, any>) => {
    return res.data;
  },
  (error) => {
    console.log("响应错误结果",error);
    if (error && error.response && error.response.status) {
      switch (error.response.status) {
        case 401:
          throw new Error("身份认证失败,请检查权限");
        case 404:
          throw new Error("未找到该资源");
        case 500:
          throw new Error("服务器内部错误");
        default:
          throw new Error("请求失败");
      }
    } else {
      throw new Error("内部错误");
    }
  }
);
在项目index.js中引入:
axios.defaults.baseURL = "https://api.apiopen.top";
import "./http/axios";
🔩Router使用:
  <Routes>
      <Route path="/" element={<Home />} />
       //默认;
      <Route path="/home/*" element={<Home />} />
       //带二级路由;
      <Route path="/description:id" element={<Description />} />
       //带路由参数;
      <Route path="/system" element={isAdmin ? <System /> : <p style={{background:"#9b2723",color:"white",height:"70px",lineHeight:"70px",padding:"5px"}}>对不起您无权访问改页面!</p>} />
       //带路由权限;
       <Route path="*" element={<p>ERROR-PAGE</p>} />
       //错误页; 
</Routes>

二级路由页面(/home/password):
 <Routes>
    <Route path="password" element={<Password />} />
 </Routes>
 
 接收路由参数:
  let {id} =useParams()
  console.log("路由传递参数",id);
 传递路由参数:
  let navigate = useNavigate();
  navigate("/description" + 2190867637637);
 注意路由模式使用Hash,开启webpack:historyApiFallback:true, 
(1)enum
export enum StatusOptions {
  Fine = 10,
  Urgent = 100,
}
export const StatusLevelValueSet = new Set([StatusOptions.Fine, StatusOptions.Urgent]);

(2)interface
interface Props {
  title: string;
  value?: string | ComponentType<IconProps> | undefined;
  isVisible?: boolean | undefined;
  onPress?: (isVisible?: boolean) => void;
}

(3)type
export type UserInfoType = {
  id: number;
  name: string;
  age: number;
  adress: string;
};
🔩性能优化(合并清理压缩模块):
optimization: {
    usedExports: true,
    minimize: true,
    concatenateModules: true
},
🔩减少无效代码打包;
package.json:
(1)所有均配置shaking;
"sideEffects": false,
(2)单独保留不进行shaking;
"sideEffects": ["./src/key.js"]
如果一个包中的代码具有一些全局的副作用,不进行shaking操作,进行打包;
如果一个包中的代码是独立无副作用的,进行shaking,打包使用到的部分代码;
🔩运行项目:
npm install
npm start/yarn start
打包项目:
npx webpack

代码仓库