前言
随着工作时长、前端知识的不断增加,我想总结一下自己到底会什么能做些什么。于是我想那就不如从零开始搭建一个完整的项目工程吧,于是就有这些项目:react-admin-tp 模板项目 本项目也适合想要入门工程化搭建的同学?
模版项目概览
工程化方面
- webpack 从零开始配置项目打包
- eslint 代码检查
- husky lint-staged 代码提交检查
- evn 动态环境变量导入
- ts 使用ts编码
- less 全局变量注入
项目方面
- react-router-dom 菜单路由多级配置 动态菜单 懒加载
- axios 二次封装 简化参数形式
- mock 请求拦截 模拟数据
- mobx context 基于mobx响应式 编写全局状态 context分发局部状态
- antd 使用antd搭建
工程化搭建(基础)
说到工程化那肯定就是 代码编译 代码检查 不同环境打包 CICD 这些啦
首先新建文件夹 初始化项目
pnpm initpnpm i react react-dom core-jspnpm init pnpm i react react-dom core-jspnpm init pnpm i react react-dom core-js
webpack 基础版
安装 webpack 相关依赖
pnpm i webpack webpack-cli webpack-dev-server html-webpack-plugin mini-css-extract-plugin -Dpnpm i webpack webpack-cli webpack-dev-server html-webpack-plugin mini-css-extract-plugin -Dpnpm i webpack webpack-cli webpack-dev-server html-webpack-plugin mini-css-extract-plugin -D
安装react、ts、babel、css相关loader依赖
pnpm i @babel/core babel-loader @babel/preset-env @babel/preset-react @babel/preset-typescript css-loader -Dpnpm i @babel/core babel-loader @babel/preset-env @babel/preset-react @babel/preset-typescript css-loader -Dpnpm i @babel/core babel-loader @babel/preset-env @babel/preset-react @babel/preset-typescript css-loader -D
创建 webpack.config.js
const path = require("path");const webpack = require("webpack");const HtmlWebpackPlugin = require("html-webpack-plugin");const MiniCssExtractPlugin = require("mini-css-extract-plugin");const rootDir = __dirname;const baseConfig = {// 入口文件entry: path.resolve(rootDir, "./src/main.tsx"),// 输出配置output: {clean: true,path: path.resolve(rootDir, "./dist"),},// sourcemap 调式源码映射devtool: false,// loader 规则module: {rules: [// 处理 css 文件{test: /\.(c|le)ss$/,use: [MiniCssExtractPlugin.loader, "css-loader"],},// 处理 jsx tsx ts 文件{test: /\.(t|j)sx?$/, // 比配到相应的文件及执行该 loaderexclude: /(node_modules|bower_components)/,loader: "babel-loader",options: {presets: [["@babel/preset-env",{targets: "> 0.25%, not dead",useBuiltIns: "usage",corejs: "3.30.2",},],["@babel/preset-react",{runtime: "automatic",},],["@babel/preset-typescript", { isTSX: true, allExtensions: true }],],},},// 处理静态资源{test: /\.(jpe?g|png|svg|gif)/i,type: "asset",},],},// 模块解析resolve: {alias: {"@src": path.resolve(rootDir, "./src"),},extensions: [".ts", ".jsx", ".tsx", "..."], // 自动不全文件后缀},plugins: [new webpack.ProgressPlugin(), // 编译进度new HtmlWebpackPlugin({title: "webpackApp",template: path.resolve(rootDir, "./public/index.html"),}), // 生成 indeX.htmlnew MiniCssExtractPlugin(), // 抽离 css 文件],};module.exports = (_, argv) => {console.log("MODE ===>", argv.mode);return baseConfig;};const path = require("path"); const webpack = require("webpack"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const rootDir = __dirname; const baseConfig = { // 入口文件 entry: path.resolve(rootDir, "./src/main.tsx"), // 输出配置 output: { clean: true, path: path.resolve(rootDir, "./dist"), }, // sourcemap 调式源码映射 devtool: false, // loader 规则 module: { rules: [ // 处理 css 文件 { test: /\.(c|le)ss$/, use: [MiniCssExtractPlugin.loader, "css-loader"], }, // 处理 jsx tsx ts 文件 { test: /\.(t|j)sx?$/, // 比配到相应的文件及执行该 loader exclude: /(node_modules|bower_components)/, loader: "babel-loader", options: { presets: [ [ "@babel/preset-env", { targets: "> 0.25%, not dead", useBuiltIns: "usage", corejs: "3.30.2", }, ], [ "@babel/preset-react", { runtime: "automatic", }, ], ["@babel/preset-typescript", { isTSX: true, allExtensions: true }], ], }, }, // 处理静态资源 { test: /\.(jpe?g|png|svg|gif)/i, type: "asset", }, ], }, // 模块解析 resolve: { alias: { "@src": path.resolve(rootDir, "./src"), }, extensions: [".ts", ".jsx", ".tsx", "..."], // 自动不全文件后缀 }, plugins: [ new webpack.ProgressPlugin(), // 编译进度 new HtmlWebpackPlugin({ title: "webpackApp", template: path.resolve(rootDir, "./public/index.html"), }), // 生成 indeX.html new MiniCssExtractPlugin(), // 抽离 css 文件 ], }; module.exports = (_, argv) => { console.log("MODE ===>", argv.mode); return baseConfig; };const path = require("path"); const webpack = require("webpack"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const rootDir = __dirname; const baseConfig = { // 入口文件 entry: path.resolve(rootDir, "./src/main.tsx"), // 输出配置 output: { clean: true, path: path.resolve(rootDir, "./dist"), }, // sourcemap 调式源码映射 devtool: false, // loader 规则 module: { rules: [ // 处理 css 文件 { test: /\.(c|le)ss$/, use: [MiniCssExtractPlugin.loader, "css-loader"], }, // 处理 jsx tsx ts 文件 { test: /\.(t|j)sx?$/, // 比配到相应的文件及执行该 loader exclude: /(node_modules|bower_components)/, loader: "babel-loader", options: { presets: [ [ "@babel/preset-env", { targets: "> 0.25%, not dead", useBuiltIns: "usage", corejs: "3.30.2", }, ], [ "@babel/preset-react", { runtime: "automatic", }, ], ["@babel/preset-typescript", { isTSX: true, allExtensions: true }], ], }, }, // 处理静态资源 { test: /\.(jpe?g|png|svg|gif)/i, type: "asset", }, ], }, // 模块解析 resolve: { alias: { "@src": path.resolve(rootDir, "./src"), }, extensions: [".ts", ".jsx", ".tsx", "..."], // 自动不全文件后缀 }, plugins: [ new webpack.ProgressPlugin(), // 编译进度 new HtmlWebpackPlugin({ title: "webpackApp", template: path.resolve(rootDir, "./public/index.html"), }), // 生成 indeX.html new MiniCssExtractPlugin(), // 抽离 css 文件 ], }; module.exports = (_, argv) => { console.log("MODE ===>", argv.mode); return baseConfig; };
创建 ./src/main.tsx
import { createRoot } from "react-dom/client";import styles from './main.module.css'const dom = document.getElementById("root") as Element;const root = createRoot(dom);root.render(<div className={styles.container} >webpack test_demo</div>);import { createRoot } from "react-dom/client"; import styles from './main.module.css' const dom = document.getElementById("root") as Element; const root = createRoot(dom); root.render( <div className={styles.container} > webpack test_demo </div> );import { createRoot } from "react-dom/client"; import styles from './main.module.css' const dom = document.getElementById("root") as Element; const root = createRoot(dom); root.render( <div className={styles.container} > webpack test_demo </div> );
此时的目录结构是这样的
在 package.json scripts 加入
“build”: “webpack -c webpack.config.js –mode=development”
pnpm buildpnpm buildpnpm build
此时应该就可以看到打包输出的产物啦 dist 文件夹内。
以上就是webpack打包react、typescript的基础配置啦!
以上需要注意的就是使用到了 babel 的几个预设 (具体的使用可以看看官网)
core-js 是用来补全浏览器还未支持的特性用的 不使用stage提案或非远古浏览器不用也可
dev-serve
真实开发过程中我们肯定需要一遍开发一遍查看效果,所以还需要加入 dev-server。
将 webpack.config.js 中的导出部分修改为
module.exports = (_, argv) => {console.log("MODE ===>", argv.mode);if (argv.mode === "development") {// mode=development 时添加 devServer 相关配置const devServer = {historyApiFallback: true,open: true,port: 8888,};baseConfig.devServer = devServer;baseConfig.plugins.push(new webpack.SourceMapDevToolPlugin({})); // 添加 SourceMapD 此插件可替代 devtool}return baseConfig;};module.exports = (_, argv) => { console.log("MODE ===>", argv.mode); if (argv.mode === "development") { // mode=development 时添加 devServer 相关配置 const devServer = { historyApiFallback: true, open: true, port: 8888, }; baseConfig.devServer = devServer; baseConfig.plugins.push(new webpack.SourceMapDevToolPlugin({})); // 添加 SourceMapD 此插件可替代 devtool } return baseConfig; };module.exports = (_, argv) => { console.log("MODE ===>", argv.mode); if (argv.mode === "development") { // mode=development 时添加 devServer 相关配置 const devServer = { historyApiFallback: true, open: true, port: 8888, }; baseConfig.devServer = devServer; baseConfig.plugins.push(new webpack.SourceMapDevToolPlugin({})); // 添加 SourceMapD 此插件可替代 devtool } return baseConfig; };
在 package.json scripts 加入
“start”: “webpack serve -c webpack.config.js –mode=development”
运行 pnpm start 即可开到开发页面啦?
到这里 webapack 相关的基础配置就结束了。
Eslint Ts format
开发和打包弄好了,该优化一下我们的开发体验啦,刚才的编码过程中Vscode会一直提示的ts、eslint相关的错误一片红这个严重影响我们的开发体验,现在我们来解决他们?。
首先安装下依赖
pnpm i eslint eslint-plugin-prettier eslint-plugin-react eslint-plugin-react-hooks -Dpnpm i eslint eslint-plugin-prettier eslint-plugin-react eslint-plugin-react-hooks -Dpnpm i eslint eslint-plugin-prettier eslint-plugin-react eslint-plugin-react-hooks -D
安装 ts 相关依赖
pnpm i typescript @typescript-eslint/eslint-plugin @typescript-eslint/parser -Dpnpm i typescript @typescript-eslint/eslint-plugin @typescript-eslint/parser -Dpnpm i typescript @typescript-eslint/eslint-plugin @typescript-eslint/parser -D
安装 react 类型声明
pnpm i @types/react @types/react-dompnpm i @types/react @types/react-dompnpm i @types/react @types/react-dom
添加 .eslintrc.js
module.exports = {// 以此文件为准 不往上查找 eslint 配置文件root: true,parser: "@typescript-eslint/parser",// 环境env: {browser: true,es2021: true,commonjs: true,},globals: {process: "writable",__dirname: "readonly",},// 继承插件特性extends: ["eslint:recommended","plugin:react/recommended","plugin:react/jsx-runtime","plugin:react-hooks/recommended","plugin:@typescript-eslint/recommended",],// 解析选项parserOptions: {ecmaFeatures: {jsx: true,},ecmaVersion: 13,sourceType: "module",},// 共享配置settings: {},// 插件plugins: ["prettier", "react", "@typescript-eslint"],// 检查规则rules: {"prettier/prettier": ["error"],},// 过滤文件ignorePatterns: ["dist", "node_modules", "pnpm-lock", "env"],};module.exports = { // 以此文件为准 不往上查找 eslint 配置文件 root: true, parser: "@typescript-eslint/parser", // 环境 env: { browser: true, es2021: true, commonjs: true, }, globals: { process: "writable", __dirname: "readonly", }, // 继承插件特性 extends: [ "eslint:recommended", "plugin:react/recommended", "plugin:react/jsx-runtime", "plugin:react-hooks/recommended", "plugin:@typescript-eslint/recommended", ], // 解析选项 parserOptions: { ecmaFeatures: { jsx: true, }, ecmaVersion: 13, sourceType: "module", }, // 共享配置 settings: {}, // 插件 plugins: ["prettier", "react", "@typescript-eslint"], // 检查规则 rules: { "prettier/prettier": ["error"], }, // 过滤文件 ignorePatterns: ["dist", "node_modules", "pnpm-lock", "env"], };module.exports = { // 以此文件为准 不往上查找 eslint 配置文件 root: true, parser: "@typescript-eslint/parser", // 环境 env: { browser: true, es2021: true, commonjs: true, }, globals: { process: "writable", __dirname: "readonly", }, // 继承插件特性 extends: [ "eslint:recommended", "plugin:react/recommended", "plugin:react/jsx-runtime", "plugin:react-hooks/recommended", "plugin:@typescript-eslint/recommended", ], // 解析选项 parserOptions: { ecmaFeatures: { jsx: true, }, ecmaVersion: 13, sourceType: "module", }, // 共享配置 settings: {}, // 插件 plugins: ["prettier", "react", "@typescript-eslint"], // 检查规则 rules: { "prettier/prettier": ["error"], }, // 过滤文件 ignorePatterns: ["dist", "node_modules", "pnpm-lock", "env"], };
添加 tsconfig.json
{"compilerOptions": {"target": "ESNext","lib": ["dom", "dom.iterable", "esnext"],"allowJs": true,"skipLibCheck": true,"esModuleInterop": true,"allowSyntheticDefaultImports": true,"strict": true,"forceConsistentCasingInFileNames": true,"module": "esnext","moduleResolution": "node","resolveJsonModule": true,"isolatedModules": true,"noEmit": true,"jsx": "react-jsx","baseUrl": ".","paths": {"@src/*": ["src/*"]}},"include": ["src", "./index.d.ts"]}{ "compilerOptions": { "target": "ESNext", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, "strict": true, "forceConsistentCasingInFileNames": true, "module": "esnext", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "react-jsx", "baseUrl": ".", "paths": { "@src/*": ["src/*"] } }, "include": ["src", "./index.d.ts"] }{ "compilerOptions": { "target": "ESNext", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, "strict": true, "forceConsistentCasingInFileNames": true, "module": "esnext", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "react-jsx", "baseUrl": ".", "paths": { "@src/*": ["src/*"] } }, "include": ["src", "./index.d.ts"] }
添加 index.d.ts 样式文件静态资源类型声明
declare module "*.module.scss" {const classes: { [key: string]: string };export default classes;}declare module "*.module.less" {const classes: { [key: string]: string };export default classes;}declare module "*.module.css" {const classes: { [key: string]: string };export default classes;}declare module "*.jpg" {const classes: string;export default classes;}declare module "*.png" {const classes: string;export default classes;}declare module "*.jpeg" {const classes: string;export default classes;}declare module "*.svg" {const classes: string;export default classes;}declare module "*.gif" {const classes: string;export default classes;}declare module "*.module.scss" { const classes: { [key: string]: string }; export default classes; } declare module "*.module.less" { const classes: { [key: string]: string }; export default classes; } declare module "*.module.css" { const classes: { [key: string]: string }; export default classes; } declare module "*.jpg" { const classes: string; export default classes; } declare module "*.png" { const classes: string; export default classes; } declare module "*.jpeg" { const classes: string; export default classes; } declare module "*.svg" { const classes: string; export default classes; } declare module "*.gif" { const classes: string; export default classes; }declare module "*.module.scss" { const classes: { [key: string]: string }; export default classes; } declare module "*.module.less" { const classes: { [key: string]: string }; export default classes; } declare module "*.module.css" { const classes: { [key: string]: string }; export default classes; } declare module "*.jpg" { const classes: string; export default classes; } declare module "*.png" { const classes: string; export default classes; } declare module "*.jpeg" { const classes: string; export default classes; } declare module "*.svg" { const classes: string; export default classes; } declare module "*.gif" { const classes: string; export default classes; }
此时当我们写的代码不符合规范时就会有错误提示
注意:最好vscode安装 Eslint、Prettier 这样可以自动fix和format一些问题哦!
当我们配置好后会发现 使用了 commonjs require 导入的文件会提示错误
在使用到 require 的文件顶部加上 /* eslint-disable @typescript-eslint/no-var-requires */
就可以啦!
好啦,以上内容就是项目工程的基础内容啦!还有一些 husky lint-staged 动态导入环境变量撒的大家有情趣可以看下我的react-admin-tp 模板项目哦~
另外我还写了个脚手架工具方便快速搭建拉取项目 模版项目脚手架 大家可以试试哦??
最后祝各位大佬牛气冲天???