5-1 React 实战之从零到一的项目环境搭建(一)

250 阅读20分钟

1、Mono 仓库搭建

Mono:单一;repo:repository 仓库

Monorepo:指的是多个项目用一个仓库来管理

为什么越来越多的项目选择 Monorepo? - 掘金

本次采用pnpm v7.30.3来搭建,node版本为v16.14.0

搭建步骤如下

所以带数字编号的都是要操作的步骤哦

(一) 初始化工程

  1. 初始化并生成一个package.json文件
pnpm init
  1. 创建pnpm-workspace.yaml文件,并填入如下内容:
touch pnpm-workspace.yaml
packages:
  - "packages/**"
  1. 创建packages文件夹
mkdir packages
  1. 创建apps、components、libs三个文件夹
mkdir packages/apps packages/components packages/libs
  1. 初始化apps下面的项目,react-master可任意取名
cd packages/apps && mkdir react-master && cd react-master && pnpm init && touch index.js
  1. 初始化libs下面的项目,react-test-utils可任意取名
cd ../../../ && cd packages/libs && mkdir react-test-utils && cd react-test-utils && pnpm init && touch index.js
  1. libs/react-test-utils/index.js内写如下代码:
export const getName = () => console.log('this is react-test-utils')

(二) npm 包的安装

1. 项目全局安装

这样安装的包三个项目都可以使用,核心命令-w 或 --workspace

完整命令:pnpm add XXX -w,在项目根路径运行如下命令,将在当前文件创建node_modules

  1. 全局安装eslint
pnpm add eslint -D -w

2. 局部安装

将对应的包安装到对应的项目中去,核心命令--filter=packageName

完整命令:pnpm add XXX --filter=packageName,在项目根路径运行如下命令,将在packageName创建node_modules

  1. apps/test-pro项目安装lodash(根目录运行)
pnpm add lodash --filter=react-master

PS:也可以 cd 到 test-pro 内,然后安装依赖 pnpm add lodash

3. 内部安装

使用 Mono 形式后,支持互相作为 npm 包进行安装引用,语法跟局部安装一样

完整命令:pnpm add packageName1 --filter=packageName2,解释:将packageName1作为 npm 包安装到packageName2

  1. libs/test-util安装到apps/test-pro内(根目录运行)
pnpm add react-test-utils --filter=react-master

以下是apps/react-test-pro/package.json,可以看到react-test-utils作为依赖成功被安装了

{
  "name": "react-master",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "lodash": "^4.17.21",
    "react-test-utils": "workspace:^1.0.0"
  }
}

PS:若内部包名与外部(npm 上)包名重复,优先安装外部的,所以内部包名最好取的唯一一点

(三) 安装基础环境(根目录下)

1. eslint 环境

eslint 检测代码错误的

  1. 根目录,运行:
npx eslint --init

  1. 根目录,手动安装(因为缺了 -w 所以上一步安装报警告)
pnpm add @typescript-eslint/eslint-plugin@latest @typescript-eslint/parser@latest -D -w

2. prettier 环境

prettier 处理代码风格的

  1. 根目录,运行:
pnpm add prettier -D -w
  1. 根目录,新建.prettierrc.json文件
touch .prettierrc.json
  1. 然后简单写如下配置,可以自行网上找更丰富的配置项
{
    "printWidth": 80,
    "tabWidth": 2,
    "useTabs": true
}
  1. 安装对应的 vscode 插件,才能起作用哦
  2. prettier可能会和eslint其冲突,所以还要安装(根目录):
pnpm add eslint-plugin-prettier eslint-config-prettier -D -w
  1. 进入.eslintrc.json加上安装的插件
{
  ....

  "plugins": ["@typescript-eslint", "prettier"],

  ....
}

(四) 安装 TypeScript

tsc、ts-loader、@babel/preset-typescript 的区别与使用

1、tsc:TypeScript 的官方编译器,将 TS 代码转为 JS 代码,并进行类型检查、支持 TS 泛型、枚举等转为 JS

2、ts-loader:一个 webpack loader,它内部调用了 TypeScript 的官方编译器(tsc),所以它两能共用 tsconfig.json

3、@babel/preset-typescript: 只将 TS 代码转为 JS 代码,不进行类型检查,所以为了实现类型检查,就需要用 tsc 的配置

使用:一般项目,不使用 tsc 生成代码,只让它做类型检查。

项目中没 babel,就用 tsc + ts-loader;有 babel 就用 @babel/preset-typescript + tsc(类型检查)

  1. 去进入项目packages/apps/react-master,命令行运行,创建 tsconfig.json 配置
tsc --init

会生成如下的配置:

{
	"compilerOptions": {
		/* Visit https://aka.ms/tsconfig to read more about this file */

		/* Projects */
		// "incremental": true,                              /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
		// "composite": true,                                /* Enable constraints that allow a TypeScript project to be used with project references. */
		// "tsBuildInfoFile": "./.tsbuildinfo",              /* Specify the path to .tsbuildinfo incremental compilation file. */
		// "disableSourceOfProjectReferenceRedirect": true,  /* Disable preferring source files instead of declaration files when referencing composite projects. */
		// "disableSolutionSearching": true,                 /* Opt a project out of multi-project reference checking when editing. */
		// "disableReferencedProjectLoad": true,             /* Reduce the number of projects loaded automatically by TypeScript. */

		/* Language and Environment */
		"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
		// "lib": [],                                        /* Specify a set of bundled library declaration files that describe the target runtime environment. */
		// "jsx": "preserve",                                /* Specify what JSX code is generated. */
		// "experimentalDecorators": true,                   /* Enable experimental support for legacy experimental decorators. */
		// "emitDecoratorMetadata": true,                    /* Emit design-type metadata for decorated declarations in source files. */
		// "jsxFactory": "",                                 /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
		// "jsxFragmentFactory": "",                         /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
		// "jsxImportSource": "",                            /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
		// "reactNamespace": "",                             /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
		// "noLib": true,                                    /* Disable including any library files, including the default lib.d.ts. */
		// "useDefineForClassFields": true,                  /* Emit ECMAScript-standard-compliant class fields. */
		// "moduleDetection": "auto",                        /* Control what method is used to detect module-format JS files. */

		/* Modules */
		"module": "commonjs" /* Specify what module code is generated. */,
		// "rootDir": "./",                                  /* Specify the root folder within your source files. */
		// "moduleResolution": "node10",                     /* Specify how TypeScript looks up a file from a given module specifier. */
		// "baseUrl": "./",                                  /* Specify the base directory to resolve non-relative module names. */
		// "paths": {},                                      /* Specify a set of entries that re-map imports to additional lookup locations. */
		// "rootDirs": [],                                   /* Allow multiple folders to be treated as one when resolving modules. */
		// "typeRoots": [],                                  /* Specify multiple folders that act like './node_modules/@types'. */
		// "types": [],                                      /* Specify type package names to be included without being referenced in a source file. */
		// "allowUmdGlobalAccess": true,                     /* Allow accessing UMD globals from modules. */
		// "moduleSuffixes": [],                             /* List of file name suffixes to search when resolving a module. */
		// "allowImportingTsExtensions": true,               /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
		// "resolvePackageJsonExports": true,                /* Use the package.json 'exports' field when resolving package imports. */
		// "resolvePackageJsonImports": true,                /* Use the package.json 'imports' field when resolving imports. */
		// "customConditions": [],                           /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
		// "resolveJsonModule": true,                        /* Enable importing .json files. */
		// "allowArbitraryExtensions": true,                 /* Enable importing files with any extension, provided a declaration file is present. */
		// "noResolve": true,                                /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */

		/* JavaScript Support */
		// "allowJs": true,                                  /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
		// "checkJs": true,                                  /* Enable error reporting in type-checked JavaScript files. */
		// "maxNodeModuleJsDepth": 1,                        /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */

		/* Emit */
		// "declaration": true,                              /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
		// "declarationMap": true,                           /* Create sourcemaps for d.ts files. */
		// "emitDeclarationOnly": true,                      /* Only output d.ts files and not JavaScript files. */
		// "sourceMap": true,                                /* Create source map files for emitted JavaScript files. */
		// "inlineSourceMap": true,                          /* Include sourcemap files inside the emitted JavaScript. */
		// "outFile": "./",                                  /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
		// "outDir": "./",                                   /* Specify an output folder for all emitted files. */
		// "removeComments": true,                           /* Disable emitting comments. */
		// "noEmit": true,                                   /* Disable emitting files from a compilation. */
		// "importHelpers": true,                            /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
		// "importsNotUsedAsValues": "remove",               /* Specify emit/checking behavior for imports that are only used for types. */
		// "downlevelIteration": true,                       /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
		// "sourceRoot": "",                                 /* Specify the root path for debuggers to find the reference source code. */
		// "mapRoot": "",                                    /* Specify the location where debugger should locate map files instead of generated locations. */
		// "inlineSources": true,                            /* Include source code in the sourcemaps inside the emitted JavaScript. */
		// "emitBOM": true,                                  /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
		// "newLine": "crlf",                                /* Set the newline character for emitting files. */
		// "stripInternal": true,                            /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
		// "noEmitHelpers": true,                            /* Disable generating custom helper functions like '__extends' in compiled output. */
		// "noEmitOnError": true,                            /* Disable emitting files if any type checking errors are reported. */
		// "preserveConstEnums": true,                       /* Disable erasing 'const enum' declarations in generated code. */
		// "declarationDir": "./",                           /* Specify the output directory for generated declaration files. */
		// "preserveValueImports": true,                     /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */

		/* Interop Constraints */
		// "isolatedModules": true,                          /* Ensure that each file can be safely transpiled without relying on other imports. */
		// "verbatimModuleSyntax": true,                     /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
		// "allowSyntheticDefaultImports": true,             /* Allow 'import x from y' when a module doesn't have a default export. */
		"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
		// "preserveSymlinks": true,                         /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
		"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,

		/* Type Checking */
		"strict": true /* Enable all strict type-checking options. */,
		// "noImplicitAny": true,                            /* Enable error reporting for expressions and declarations with an implied 'any' type. */
		// "strictNullChecks": true,                         /* When type checking, take into account 'null' and 'undefined'. */
		// "strictFunctionTypes": true,                      /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
		// "strictBindCallApply": true,                      /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
		// "strictPropertyInitialization": true,             /* Check for class properties that are declared but not set in the constructor. */
		// "noImplicitThis": true,                           /* Enable error reporting when 'this' is given the type 'any'. */
		// "useUnknownInCatchVariables": true,               /* Default catch clause variables as 'unknown' instead of 'any'. */
		// "alwaysStrict": true,                             /* Ensure 'use strict' is always emitted. */
		// "noUnusedLocals": true,                           /* Enable error reporting when local variables aren't read. */
		// "noUnusedParameters": true,                       /* Raise an error when a function parameter isn't read. */
		// "exactOptionalPropertyTypes": true,               /* Interpret optional property types as written, rather than adding 'undefined'. */
		// "noImplicitReturns": true,                        /* Enable error reporting for codepaths that do not explicitly return in a function. */
		// "noFallthroughCasesInSwitch": true,               /* Enable error reporting for fallthrough cases in switch statements. */
		// "noUncheckedIndexedAccess": true,                 /* Add 'undefined' to a type when accessed using an index. */
		// "noImplicitOverride": true,                       /* Ensure overriding members in derived classes are marked with an override modifier. */
		// "noPropertyAccessFromIndexSignature": true,       /* Enforces using indexed accessors for keys declared using an indexed type. */
		// "allowUnusedLabels": true,                        /* Disable error reporting for unused labels. */
		// "allowUnreachableCode": true,                     /* Disable error reporting for unreachable code. */

		/* Completeness */
		// "skipDefaultLibCheck": true,                      /* Skip type checking .d.ts files that are included with TypeScript. */
		"skipLibCheck": true /* Skip type checking all .d.ts files. */
	}
}
  1. 配置微调,调整完毕后如下:
{
	"compilerOptions": {
		/* Visit https://aka.ms/tsconfig to read more about this file */

		/* Projects */
		// "incremental": true,                              /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
		// "composite": true,                                /* Enable constraints that allow a TypeScript project to be used with project references. */
		// "tsBuildInfoFile": "./.tsbuildinfo",              /* Specify the path to .tsbuildinfo incremental compilation file. */
		// "disableSourceOfProjectReferenceRedirect": true,  /* Disable preferring source files instead of declaration files when referencing composite projects. */
		// "disableSolutionSearching": true,                 /* Opt a project out of multi-project reference checking when editing. */
		// "disableReferencedProjectLoad": true,             /* Reduce the number of projects loaded automatically by TypeScript. */

		/* Language and Environment */
		"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
		"lib": [
			"DOM",
			"DOM.Iterable",
			"ESNext"
		] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
		"jsx": "react" /* Specify what JSX code is generated. */,
		"experimentalDecorators": true /* Enable experimental support for legacy experimental decorators. */,
		"emitDecoratorMetadata": true /* Emit design-type metadata for decorated declarations in source files. */,
		// "jsxFactory": "",                                 /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
		// "jsxFragmentFactory": "",                         /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
		// "jsxImportSource": "",                            /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
		// "reactNamespace": "",                             /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
		// "noLib": true,                                    /* Disable including any library files, including the default lib.d.ts. */
		// "useDefineForClassFields": true,                  /* Emit ECMAScript-standard-compliant class fields. */
		// "moduleDetection": "auto",                        /* Control what method is used to detect module-format JS files. */

		/* Modules */
		"module": "ESNext" /* Specify what module code is generated. */,
		// "rootDir": "./",                                  /* Specify the root folder within your source files. */
		"moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */,
		// "baseUrl": "./",                                  /* Specify the base directory to resolve non-relative module names. */
		// "paths": {},                                      /* Specify a set of entries that re-map imports to additional lookup locations. */
		// "rootDirs": [],                                   /* Allow multiple folders to be treated as one when resolving modules. */
		// "typeRoots": [],                                  /* Specify multiple folders that act like './node_modules/@types'. */
		// "types": [],                                      /* Specify type package names to be included without being referenced in a source file. */
		// "allowUmdGlobalAccess": true,                     /* Allow accessing UMD globals from modules. */
		// "moduleSuffixes": [],                             /* List of file name suffixes to search when resolving a module. */
		// "allowImportingTsExtensions": true,               /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
		// "resolvePackageJsonExports": true,                /* Use the package.json 'exports' field when resolving package imports. */
		// "resolvePackageJsonImports": true,                /* Use the package.json 'imports' field when resolving imports. */
		// "customConditions": [],                           /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
		"resolveJsonModule": true /* Enable importing .json files. */,
		// "allowArbitraryExtensions": true,                 /* Enable importing files with any extension, provided a declaration file is present. */
		// "noResolve": true,                                /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */

		/* JavaScript Support */
		"allowJs": true /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */,
		// "checkJs": true,                                  /* Enable error reporting in type-checked JavaScript files. */
		// "maxNodeModuleJsDepth": 1,                        /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */

		/* Emit */
		// "declaration": true,                              /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
		// "declarationMap": true,                           /* Create sourcemaps for d.ts files. */
		// "emitDeclarationOnly": true,                      /* Only output d.ts files and not JavaScript files. */
		// "sourceMap": true,                                /* Create source map files for emitted JavaScript files. */
		// "inlineSourceMap": true,                          /* Include sourcemap files inside the emitted JavaScript. */
		// "outFile": "./",                                  /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
		// "outDir": "./",                                   /* Specify an output folder for all emitted files. */
		// "removeComments": true,                           /* Disable emitting comments. */
		"noEmit": true /* 不输出文件,只做类型检查 */ /* Disable emitting files from a compilation. */,
		// "importHelpers": true,                            /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
		// "importsNotUsedAsValues": "remove",               /* Specify emit/checking behavior for imports that are only used for types. */
		// "downlevelIteration": true,                       /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
		// "sourceRoot": "",                                 /* Specify the root path for debuggers to find the reference source code. */
		// "mapRoot": "",                                    /* Specify the location where debugger should locate map files instead of generated locations. */
		// "inlineSources": true,                            /* Include source code in the sourcemaps inside the emitted JavaScript. */
		// "emitBOM": true,                                  /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
		// "newLine": "crlf",                                /* Set the newline character for emitting files. */
		// "stripInternal": true,                            /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
		// "noEmitHelpers": true,                            /* Disable generating custom helper functions like '__extends' in compiled output. */
		// "noEmitOnError": true,                            /* Disable emitting files if any type checking errors are reported. */
		// "preserveConstEnums": true,                       /* Disable erasing 'const enum' declarations in generated code. */
		// "declarationDir": "./",                           /* Specify the output directory for generated declaration files. */
		// "preserveValueImports": true,                     /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */

		/* Interop Constraints */
		"isolatedModules": true /* Ensure that each file can be safely transpiled without relying on other imports. */,
		// "verbatimModuleSyntax": true,                     /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
		"allowSyntheticDefaultImports": true /* Allow 'import x from y' when a module doesn't have a default export. */,
		"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
		// "preserveSymlinks": true,                         /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
		"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,

		/* Type Checking */
		"strict": true /* Enable all strict type-checking options. */,
		// "noImplicitAny": true,                            /* Enable error reporting for expressions and declarations with an implied 'any' type. */
		// "strictNullChecks": true,                         /* When type checking, take into account 'null' and 'undefined'. */
		// "strictFunctionTypes": true,                      /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
		// "strictBindCallApply": true,                      /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
		// "strictPropertyInitialization": true,             /* Check for class properties that are declared but not set in the constructor. */
		// "noImplicitThis": true,                           /* Enable error reporting when 'this' is given the type 'any'. */
		// "useUnknownInCatchVariables": true,               /* Default catch clause variables as 'unknown' instead of 'any'. */
		// "alwaysStrict": true,                             /* Ensure 'use strict' is always emitted. */
		// "noUnusedLocals": true,                           /* Enable error reporting when local variables aren't read. */
		// "noUnusedParameters": true,                       /* Raise an error when a function parameter isn't read. */
		// "exactOptionalPropertyTypes": true,               /* Interpret optional property types as written, rather than adding 'undefined'. */
		// "noImplicitReturns": true,                        /* Enable error reporting for codepaths that do not explicitly return in a function. */
		// "noFallthroughCasesInSwitch": true,               /* Enable error reporting for fallthrough cases in switch statements. */
		// "noUncheckedIndexedAccess": true,                 /* Add 'undefined' to a type when accessed using an index. */
		// "noImplicitOverride": true,                       /* Ensure overriding members in derived classes are marked with an override modifier. */
		// "noPropertyAccessFromIndexSignature": true,       /* Enforces using indexed accessors for keys declared using an indexed type. */
		// "allowUnusedLabels": true,                        /* Disable error reporting for unused labels. */
		// "allowUnreachableCode": true,                     /* Disable error reporting for unreachable code. */

		/* Completeness */
		// "skipDefaultLibCheck": true,                      /* Skip type checking .d.ts files that are included with TypeScript. */
		"skipLibCheck": true /* Skip type checking all .d.ts files. */
	},
	"include": ["./src/**/*"],
	"exclude": ["node_modules", "dist"]
}

(五) react-master 项目配置

react-master 将作为 react 项目

1. 删除/新建一些东西

  1. react-master 目录下,删除 index.js
rm index.js  
  1. react-master 目录下,删除 package.json 下多余的 dependencies
pnpm remove lodash react-test-utils
  1. react-master 目录下,创建srcindex.tsx
mkdir src && touch src/index.tsx

2. 安装 Webpack、babel 等一系列环境

  1. react-master 目录下,运行安装命令:
pnpm add webpack webpack-cli webpack-merge webpack-dev-server @babel/core @babel/preset-react @babel/preset-env @babel/preset-typescript babel-loader css-loader style-loader less less-loader postcss postcss-loader tailwindcss autoprefixer html-webpack-plugin css-minimizer-webpack-plugin terser-webpack-plugin mini-css-extract-plugin cross-env -D

安装的包归类:

1、webpack 类:webpack webpack-cli webpack-merge webpack-dev-server 

2、babel 类:babel-loader 依赖 @babel/core;解析 React 则要安装 @babel/preset-react;解析 TS 则安装 @babel/preset-typescript ;解析环境则安装 @babel/preset-env 

3、css 编译器:css-loader style-loader less less-loader 

4、postcss postcss-loader 

5、tailwindcss autoprefixer 

6、html-webpack-plugin 

7、css-minimizer-webpack-plugin terser-webpack-plugin mini-css-extract-plugin 

8、cross-env

通义千问的详细解释:

以下是这些 NPM 包的功能和用途的简要归类解释:

  1. pnpm
    • pnpm 是一个高效的 JavaScript 包管理器,采用了硬链接和内容寻址存储的方式来优化磁盘空间和缓存性能,特别适合在大型项目和 Monorepo 中使用。
  1. webpack
    • webpack 是一款流行的模块打包工具,负责将项目中的 JavaScript、CSS、图片等资源组织在一起,通过 loaders 和 plugins 处理和转换源文件,最终生成优化过的 bundle 文件。
  1. webpack-cli
    • webpack-cli 是 webpack 的命令行接口,提供了一系列命令用于与 webpack 交互,执行构建、配置、打包等任务。
  1. webpack-merge
    • webpack-merge 是一个用于合并 webpack 配置对象的工具,便于在不同环境(如开发、生产)间共享基础配置并叠加特定环境的配置。
  1. webpack-dev-server
    • webpack-dev-server 是一个小型的静态文件服务器,配合 webpack 使用,可以实现热模块替换(HMR)、自动刷新等功能,用于快速开发调试。
  1. @babel/core
    • Babel 核心包,用于转译 JavaScript 代码,支持 ES6+、JSX 等新的语言特性转换为旧版本 JavaScript。
  1. @babel/preset-react
    • Babel 预设,用于将 React 的 JSX 语法转换为标准的 JavaScript。
  1. @babel/preset-env
    • Babel 预设,根据目标环境自动应用兼容性 polyfill 和转换,使新版本 JavaScript 代码能在老版本浏览器运行。
  1. @babel/preset-typescript
    • Babel 预设,用于编译 TypeScript 代码为 JavaScript。
  1. babel-loader
    • webpack 加载器,用于配合 Babel 将源代码通过 Babel 转换后再传递给 webpack 进行打包。
  1. css-loader
    • webpack 加载器,用于处理 CSS 文件,支持导入和导出 CSS 语句。
  1. style-loader
    • webpack 加载器,将 CSS 代码插入到 JavaScript 中,通过动态样式标签注入到 DOM 中,实现 CSS 的实时加载。
  1. less
    • Less 是一种 CSS 预处理器,提供变量、嵌套、混合等高级功能。
  1. less-loader
    • webpack 加载器,用于将 Less 文件编译成 CSS。
  1. postcss
    • PostCSS 是一个 CSS 处理工具,支持一系列插件对 CSS 进行扩展和转换。
  1. postcss-loader
    • webpack 加载器,用于在 webpack 流程中运行 PostCSS。
  1. tailwindcss
    • 一个实用的 CSS 框架,提供了一整套原子级实用类,用于快速构建自定义响应式界面。
  1. autoprefixer
    • PostCSS 的一个插件,自动为 CSS 添加 vendor 前缀,确保跨浏览器兼容性。
  1. html-webpack-plugin
    • 生成 HTML 文件并自动引入打包后的 JavaScript 和 CSS 文件。
  1. css-minimizer-webpack-plugin
    • 用于压缩输出的 CSS 文件,减少文件大小。
  1. terser-webpack-plugin
    • 用于压缩 JavaScript 文件,是一个替代 UglifyJS 的 webpack 插件,提供了更好的 ES6+ 代码压缩能力。
  1. mini-css-extract-plugin
    • 从 JavaScript 文件中提取 CSS 代码到单独的 CSS 文件中,有利于 CSS 文件的缓存和分离样式与脚本。
  1. cross-env
    • 设置跨平台的环境变量,使得在 Windows、Unix 等不同操作系统上的环境变量设置变得简单。

3. 安装 React 相关

  1. react-master 目录下,运行安装命令:
pnpm add react react-dom react-router-dom @types/react @types/react-dom @types/react-router-dom

4. 带来的思考:前端的方案

刚刚进行了react-master项目的配置,那从中可以思考下:前端的方案有哪些?

一、构建方案

构建关键词:bundle、chunk、module、channel

module: 构建的基本单元,从构建角度来说任何文件(.js/.css/.json 等)都是一个 Module,基于模块构建依赖关系

chunk: 一般指中间产物,代表一组模块的集合,会根据模块的依赖关系生成 chunks。chunks 之后会被打进一个/多个 bundle 里面。比如通过代码分割(code splitting)功能来创建按需加载的代码块,会形成一个 chunks

bundle: 一般指最终产物,比如:mian.xxx.js,它可能是把所有文件(js/css/图片等)合在一起形成的,浏览器可以直接加载它来运行应用程序

channel: 一般跟 uri 挂钩,代表唯一的地址。

常用方案有:Bundle 与 Bundleless

Bundled:打包的最终产物为一个 js,典型工具:Webpack、Rollup

Bundleless:几乎不打包,利用 ESM 进行加载使用,典型工具:Vite、Snowpack

import $ from 'jquery'

工具/组件库还希望产物有目录结构,比如下面这种结构:

Button
  -index.js
  -index.css
Card
  -index.js
  -index.css

实际使用:

业务项目,采用 Webpack,构建 bundle 方案

组件库,采用 Rollup + 多入口,构建bundleless 方案

二、CSS 方案

比如:css 如何隔离的?

(一) css in js 方案

有个库:@emotion

import { css } from '@emotion'

const color = 'white'

render(
  <div className={
    css`
      padding: 32px;
      margin-left: 10px;
      font-size: 12px;
    `
  }>
    hello world!
  </div>
)
(二) styled-component 方案

本质还是 css in js

import styled from 'styled-components';

// 定义一个简单的样式组件
const Button = styled.button`
  background-color: ${props => props.primary ? 'blue' : 'grey'};
  color: white;
  font-size: 1em;
  padding: 0.5em 1em;
  border: none;
  border-radius: 3px;
`;

// 在 React 组件中使用
function MyComponent() {
  return (
    <Button primary={true}>Click me</Button>
  );
}
(三) css module 方案

通过 Webpack 的 css-loader 配置,在编译阶段转换 CSS 类名,将其变为唯一的、局部作用域的类名。

/* styles.module.css */
.title {
  color: silver;
}

.description {
  composes: baseText from './base.module.css';
  font-size: 14px;
}

/* base.module.css */
:export {
  baseText: base-text;
}

.base-text {
  color: black;
  font-family: Arial, sans-serif;
}

// Component.js
import React from 'react';
import styles from './styles.module.css';

function MyComponent() {
  return (
    <div>
      <h1 className={styles.title}>Title</h1>
      <p className={styles.description}>Description text</p>
    </div>
  );
}

bem 规范:block-element_modifier css 命名规范化

block:块,功能的抽象,比如 .nav、.search

element:元素,块里面的实际元素,比如 .nav__item、.search__card

modifier:修饰符,表示状态、类型、外观的变化,比如 .nav__item--actived、.search__card--disabled

(四) utility-css 方案

提供原子化类名,灵活、可复用、易于定制。(写多了就能记住了)

代表库:tailwindcss、windicss

<div class="flex flex-row items-center justify-between bg-gray-200 rounded-lg">
  <h1 class="text-xl font-bold">Title</h1>
  <button class="btn btn-primary">Button</button>
</div>
三、组件库方案

一般用第三方组件库

headless 组件库 vs styled 组件库

headless 组件库:只提供交互,样式完全自己写,适合公司内无设计规范并且和第三方组件库样式差别大的情况

styled 组件库:提供交互与样式,特殊样式需要自己去覆盖,适合有设计规范并且和第三方组件库样式差别不大

实际场景:公司设计与主流组件库差别大,可以选 headless ui + tailwindcss + css module

四、状态方案

Zustand、Solid、Redux、mobx

五、微前端方案

iframe、Web Components、Module Federation(模块联邦)

微前端时代:打造高效、灵活的前端开发体系

5. 安装 headless ui、图标库

  1. 安装 headless ui、图标库
pnpm add @headlessui/react @heroicons/react

6. 手动配置 Webpack

我们会配置三个:

webpack.base.js:基础配置

webpack.dev.js:基于 webpack.base.js,处理开发环境配置

webpack.prod.js:基于 webpack.base.js,处理生产环境配置

  1. react-master 目录下,创建这三个文件
mkdir scripts && touch scripts/webpack.dev.js && touch scripts/webpack.prod.js && touch scripts/webpack.base.js
  1. 更改 package.json,加上 build、start 命令
{
  "name": "react-master",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1",
    "build": "webpack --config scripts/webpack.prod.js", // +++++++
    "start": "webpack-dev-server --config scripts/webpack.dev.js" // +++++++
  },
  ...
}
  1. 手写webpack.base.js
const path = require("path");

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = function (isDev) {
	return {
		// 1、输入输出部分
		// 输入,当你不知道是用 ./scr、../src、@/scr 时就使用 path 来处理,__dirname 当前目录
		entry: path.resolve(__dirname, "../src/index.tsx"),
		// 输出
		output: {
			// 打包后文件的位置
			path: path.resolve(__dirname, "../dist"),
			// 打包后 js 的名字,采用了占位符,[name] 代表入口文件的名字,[hash:8] 代表打包后文件的 hash 值,取前 8 位
			// hash 每次打包后根据内容生成 hash 值(任一文件有变动则 hash 会变,颗粒度最大)
			// contenthash 每次打包后根据内容生成 hash 值(当输出内容变化则 hash 会变,颗粒度最小)
			// chunkhash 每次打包后根据 chunk 生成 hash 值(当代码块变化则 hash 会变,颗粒度居中)
			filename: "static/js/[name]/[hash:8].js",

			// webpack 5 内置的,构建前清空 dist 目录
			// webpack 4 没有,需安装 clean-webpack-plugin
			clean: true,

			// 打包后静态资源的位置,相对于 output.path
			publicPath: "/",
		},

		// 2、resolve 部分
		resolve: {
			// 用于在引入文件时,不需要写后缀名
			extensions: [".tsx", ".ts", ".jsx", ".js"], // 优先级从左到右,会影响性能的
		},

		// 3、loader 部分:
		module: {
			// loader 是 webpack 的核心,从入口文件起去解析 import from 的文件时,针对不同类型的文件进行不同处理
			// 所以不同文件需要对应的解析器,去识别解析它,从而保证最后能形成一个 bundle
			rules: [
				{
					test: /.(tsx|ts)$/,
					use: {
						// 要使用 babel-loader 就需要有对应配置文件(.babelrc)
						loader: "babel-loader", // 有了这个 loader,react 就已经可以跑起来了
					},
				},
				{
					// 为了避免三个 loader 重复处理,采用 oneOf 的实现匹配一个规则
					oneOf: [
						{
							test: /.css$/, // 匹配 css 文件
							use: [
								// style-loader 用于将 css 放到元素内联样式上
								// dev 环境使用 style-loader,方便热更新替换
								// 生产环境使用 MiniCssExtractPlugin.loader 单独提取成 css 文件,方便缓存,还需要在下面的 plugin 中配置
								isDev ? "style-loader" : MiniCssExtractPlugin.loader,
								"css-loader", // 主要处理路径,给<link> 用
								"postcss-loader", // 处理语法转换,postcss 就是 css 界的 babel,需要有对应配置文件(.postcssrc.js)
							],
						},
						// 定义规则:针对模块化的 css,统一采用 .module.css|.less 形式命名文件
						{
							test: /.module.(css|less)$/,
							include: [path.resolve(__dirname, "../src")], // 指定生效的目录
							use: [
								isDev ? "style-loader" : MiniCssExtractPlugin.loader,
								{
									// 配置 css-loader 的 modules 模式
									loader: "css-loader",
									options: {
										modules: {
											// 借用 css-module 实现我们的 BEM 命名规范

											// localIdentName:会将 class 名替换成 [path][name]__[local]--[hash:base64:5]
											localIdentName: "[path][name]__[local]--[hash:base64:5]",
										},
									},
								},
								"postcss-loader",
								"less-loader",
							],
						},
						{
							test: /.less$/, // 匹配 less 文件
							use: [
								isDev ? "style-loader" : MiniCssExtractPlugin.loader,
								"css-loader",
								"postcss-loader",
								"less-loader",
							],
						},
					],
				},

				// webpack5 以前要单独的 loader(file|url 等),webpack5 已经内置了
				{
					// 图片处理
					test: /.(png|jpg|jpeg|gif|svg)$/,
					generator: {
						filename: "static/images/[name].[contenthash:8][ext]",
					},
				},
				{
					// 字体处理
					test: /.(woff2?|eot|ttf|otf)$/,
					generator: {
						filename: "static/fonts/[name].[contenthash:8][ext]",
					},
				},
				{
					// 音视频处理
					test: /.(mp4|map3|flv|wav)$/,
					generator: {
						filename: "static/media/[name].[contenthash:8][ext]",
					},
				},
			],
		},

		plugins: [
			// HtmlWebpackPlugin:将打包后的 js、css 注入到 html 文件中
			new HtmlWebpackPlugin({
				// 指定模板文件位置
				template: path.resolve(__dirname, "../public/index.html"),
				// 自动注入打包后的 js、css 文件
				inject: true,
			}),

			// 由于生产环境使用 MiniCssExtractPlugin.loader 单独提取成 css 文件,所以需要加对应的 plugin 配置
			new MiniCssExtractPlugin({
				// 提取后的文件名 开发环境文件名不带 hash,生产环境文件名带 hash
				filename: isDev
					? "static/css/[name].css"
					: "static/css/[name].[contenthash:4].css",
			}),
		],
	};
};
  1. 手动配置babel
touch .babelrc

// 并写入如下内容:
{
	"presets": [
		"@babel/preset-react", // 解析 react
		"@babel/preset-typescript" // 解析 typescript
	]
}
  1. 手动配置postcss
touch .postcssrc.js

// 并写入如下内容:
module.exports = {
	plugins: [
		"autoprefixer", // 自动添加浏览器前缀
		"tailwindcss",
	],
};
  1. 手写public/index.html
mkdir public && touch public/index.html

// 并写入如下内容:
<!doctype html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>React Master</title>
	</head>
	<body>
		<div id="app"></div>
	</body>
</html>
  1. 手写webpack.prod.js
const { merge } = require("webpack-merge");

const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");

const TerserPlugin = require("terser-webpack-plugin");

const baseConfig = require("./webpack.base");

// 基于 webpack 的官方 merge 方法,将 baseConfig 和 prodConfig 合并
module.exports = merge(baseConfig(), {
	// 生产环境配置

	mode: "production",

	// 优化配置
	optimization: {
		minimizer: [
			// 压缩 css
			new CssMinimizerPlugin(),

			// 压缩 js
			new TerserPlugin({
				// 开启多进程并行运行
				parallel: true,

				// 压缩参数
				terserOptions: {
					// 开启压缩
					compress: {
						pure_funcs: ["console.log", "console.warn"], // 移除 console
					},
				},
			}),
		],

		// 代码分割配置(拆包)
		splitChunks: {
			// 缓存组
			cacheGroups: {
				// 第三方库: https://webpack.docschina.org/plugins/split-chunks-plugin/#splitchunkscachegroups
				vendors: {
					name: "vendors",
					test: /node_modules/,
					// 官方已经默认设置了比较合理的 minSize: 30000,minChunks: 1 等,所以我们不要额外去更改
				},
				// 公共代码
				commoms: {
					name: "commons",
				},
			},
		},
	},
});
  1. 手撸入口页面,进入react-master/src/index.tsx,写如下代码
import ReactDom from 'react-dom'
import React from 'react'
import App from './app'

ReactDom.createRoot(document.getElementById('app') as Element).render(
  <App />
)
  1. 创建app.tsx
touch src/app.tsx

// 并写入如下内容:快捷键输入 tsrfc 回车
import React from "react";
import "./app.css";
import styles from "./app2.module.less";

type Props = {};

export default function App({}: Props) {
	return <div className={`hello ${styles.greenBGColor}`}>App</div>;
}
  1. 创建app.css
touch src/app.css

// 并写入如下内容:
.hello {
  color: red
}
  1. 创建app2.module.less
touch src/app2.module.less

// 并写入如下内容:
.greenBGColor {
    background-color: green;
}
  1. 创建golbal.d.ts
touch src/golbal.d.ts

// 并写入如下内容:
declare module "*.module.less";
declare module "*.module.css";
  1. 运行打包
pnpm build
  1. 打包成功后,可以看到对应的dist文件,然后在 dist/index.html 里面改下路径
<script defer="defer" src="/static/js/main/8aa22f5b.js"></script>
<link href="/static/css/main.2609.css" rel="stylesheet" />
  
// 改为这个,多加一个 .
<script defer="defer" src="./static/js/main/8aa22f5b.js"></script>
<link href="./static/css/main.2609.css" rel="stylesheet" />
  1. 然后用浏览器打开dist/index.html,就能看到页面正常渲染了
  2. 手写webpack.dev.js
const { merge } = require("webpack-merge");

const path = require("path");

const baseConfig = require("./webpack.base");

// 基于 webpack 的官方 merge 方法,将 baseConfig 和 devConfig 合并
module.exports = merge(baseConfig(true), {
	// 开发环境配置

	mode: "development",

	// 源码调试:使用 source-map
	devtool: "eval-cheap-module-source-map",

	// 开发服务器配置
	devServer: {
		port: 3000,
		compress: false, // 关闭压缩,这样热更新会更快
		hot: true, // 开启热更新
		historyApiFallback: true, // 解决开发环境 history 路由 404 的问题
		static: {
			// 托管静态资源 public 文件夹
			directory: path.resolve(__dirname, "../public"),
		},
	},
});
  1. 本地启动运行
pnpm start

  1. 浏览器打开 http://localhost:3000/,然后更改代码(背景色改为黄色)保存下,页面也会热更新

7. tailwindcss 配置

  1. 安装vscode 插件,便于代码提示
  2. 初始化,生成tailwind.config.js文件
npx tailwindcss init               
  1. 更改完善tailwind.config.js文件,改为如下:
/** @type {import('tailwindcss').Config} */
module.exports = {
	content: ["./src/**/*.{tsx,ts,jsx,js}"],
	theme: {
		extend: {},
	},
	plugins: [],
};
  1. 新增index.less,并完善
touch src/index.less


// 并写入如下内容:

// 全局的东西

// tailwind 配置
@tailwind base;
@tailwind components;
@tailwind utilities;
  1. index.tsx引入index.less
import ReactDOM from "react-dom/client";
import React from "react";

import App from "./app";

import "./index.less"; // ++++++

ReactDOM.createRoot(document.getElementById("app") as Element).render(<App />);
  1. app.tsx使用一些原子 css
import React from "react";
import "./app.css";
import styles from "./app2.module.less";

type Props = {};

export default function App({}: Props) {
	return (
		<div className={`hello ${styles.greenBGColor} flex justify-center`}> // +++++
			App
		</div>
	);
}
  1. 重新 start 下,效果如下: