1、Mono 仓库搭建
Mono:单一;repo:repository 仓库
Monorepo:指的是多个项目用一个仓库来管理
本次采用pnpm v7.30.3
来搭建,node
版本为v16.14.0
搭建步骤如下
所以带数字编号的都是要操作的步骤哦
(一) 初始化工程
- 初始化并生成一个
package.json
文件
pnpm init
- 创建
pnpm-workspace.yaml
文件,并填入如下内容:
touch pnpm-workspace.yaml
packages:
- "packages/**"
- 创建
packages
文件夹
mkdir packages
- 创建
apps、components、libs
三个文件夹
mkdir packages/apps packages/components packages/libs
- 初始化
apps
下面的项目,react-master
可任意取名
cd packages/apps && mkdir react-master && cd react-master && pnpm init && touch index.js
- 初始化
libs
下面的项目,react-test-utils
可任意取名
cd ../../../ && cd packages/libs && mkdir react-test-utils && cd react-test-utils && pnpm init && touch index.js
- 在
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
- 全局安装
eslint
pnpm add eslint -D -w
2. 局部安装
将对应的包安装到对应的项目中去,核心命令--filter=packageName
完整命令:pnpm add XXX --filter=packageName
,在项目根路径
运行如下命令,将在packageName
创建node_modules
- 给
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
中
- 将
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 检测代码错误的
- 根目录,运行:
npx eslint --init
- 根目录,手动安装(因为缺了 -w 所以上一步安装报警告)
pnpm add @typescript-eslint/eslint-plugin@latest @typescript-eslint/parser@latest -D -w
2. prettier 环境
prettier 处理代码风格的
- 根目录,运行:
pnpm add prettier -D -w
- 根目录,新建
.prettierrc.json
文件
touch .prettierrc.json
- 然后简单写如下配置,可以自行网上找更丰富的配置项
{
"printWidth": 80,
"tabWidth": 2,
"useTabs": true
}
- 安装对应的 vscode 插件,才能起作用哦
- 但
prettier
可能会和eslint
其冲突,所以还要安装(根目录):
pnpm add eslint-plugin-prettier eslint-config-prettier -D -w
- 进入
.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(类型检查)
- 去进入项目
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. */
}
}
- 配置微调,调整完毕后如下:
{
"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. 删除/新建一些东西
- react-master 目录下,删除 index.js
rm index.js
- react-master 目录下,删除 package.json 下多余的
dependencies
pnpm remove lodash react-test-utils
- react-master 目录下,创建
src
与index.tsx
:
mkdir src && touch src/index.tsx
2. 安装 Webpack、babel 等一系列环境
- 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 包的功能和用途的简要归类解释:
- pnpm:
-
- pnpm 是一个高效的 JavaScript 包管理器,采用了硬链接和内容寻址存储的方式来优化磁盘空间和缓存性能,特别适合在大型项目和 Monorepo 中使用。
- webpack:
-
- webpack 是一款流行的模块打包工具,负责将项目中的 JavaScript、CSS、图片等资源组织在一起,通过 loaders 和 plugins 处理和转换源文件,最终生成优化过的 bundle 文件。
- webpack-cli:
-
- webpack-cli 是 webpack 的命令行接口,提供了一系列命令用于与 webpack 交互,执行构建、配置、打包等任务。
- webpack-merge:
-
- webpack-merge 是一个用于合并 webpack 配置对象的工具,便于在不同环境(如开发、生产)间共享基础配置并叠加特定环境的配置。
- webpack-dev-server:
-
- webpack-dev-server 是一个小型的静态文件服务器,配合 webpack 使用,可以实现热模块替换(HMR)、自动刷新等功能,用于快速开发调试。
- @babel/core:
-
- Babel 核心包,用于转译 JavaScript 代码,支持 ES6+、JSX 等新的语言特性转换为旧版本 JavaScript。
- @babel/preset-react:
-
- Babel 预设,用于将 React 的 JSX 语法转换为标准的 JavaScript。
- @babel/preset-env:
-
- Babel 预设,根据目标环境自动应用兼容性 polyfill 和转换,使新版本 JavaScript 代码能在老版本浏览器运行。
- @babel/preset-typescript:
-
- Babel 预设,用于编译 TypeScript 代码为 JavaScript。
- babel-loader:
-
- webpack 加载器,用于配合 Babel 将源代码通过 Babel 转换后再传递给 webpack 进行打包。
- css-loader:
-
- webpack 加载器,用于处理 CSS 文件,支持导入和导出 CSS 语句。
- style-loader:
-
- webpack 加载器,将 CSS 代码插入到 JavaScript 中,通过动态样式标签注入到 DOM 中,实现 CSS 的实时加载。
- less:
-
- Less 是一种 CSS 预处理器,提供变量、嵌套、混合等高级功能。
- less-loader:
-
- webpack 加载器,用于将 Less 文件编译成 CSS。
- postcss:
-
- PostCSS 是一个 CSS 处理工具,支持一系列插件对 CSS 进行扩展和转换。
- postcss-loader:
-
- webpack 加载器,用于在 webpack 流程中运行 PostCSS。
- tailwindcss:
-
- 一个实用的 CSS 框架,提供了一整套原子级实用类,用于快速构建自定义响应式界面。
- autoprefixer:
-
- PostCSS 的一个插件,自动为 CSS 添加 vendor 前缀,确保跨浏览器兼容性。
- html-webpack-plugin:
-
- 生成 HTML 文件并自动引入打包后的 JavaScript 和 CSS 文件。
- css-minimizer-webpack-plugin:
-
- 用于压缩输出的 CSS 文件,减少文件大小。
- terser-webpack-plugin:
-
- 用于压缩 JavaScript 文件,是一个替代 UglifyJS 的 webpack 插件,提供了更好的 ES6+ 代码压缩能力。
- mini-css-extract-plugin:
-
- 从 JavaScript 文件中提取 CSS 代码到单独的 CSS 文件中,有利于 CSS 文件的缓存和分离样式与脚本。
- cross-env:
-
- 设置跨平台的环境变量,使得在 Windows、Unix 等不同操作系统上的环境变量设置变得简单。
3. 安装 React 相关
- 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、图标库
- 安装 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,处理生产环境配置
- react-master 目录下,创建这三个文件
mkdir scripts && touch scripts/webpack.dev.js && touch scripts/webpack.prod.js && touch scripts/webpack.base.js
- 更改 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" // +++++++
},
...
}
- 手写
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",
}),
],
};
};
- 手动配置
babel
touch .babelrc
// 并写入如下内容:
{
"presets": [
"@babel/preset-react", // 解析 react
"@babel/preset-typescript" // 解析 typescript
]
}
- 手动配置
postcss
touch .postcssrc.js
// 并写入如下内容:
module.exports = {
plugins: [
"autoprefixer", // 自动添加浏览器前缀
"tailwindcss",
],
};
- 手写
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>
- 手写
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",
},
},
},
},
});
- 手撸入口页面,进入
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 />
)
- 创建
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>;
}
- 创建
app.css
touch src/app.css
// 并写入如下内容:
.hello {
color: red
}
- 创建
app2.module.less
touch src/app2.module.less
// 并写入如下内容:
.greenBGColor {
background-color: green;
}
- 创建
golbal.d.ts
touch src/golbal.d.ts
// 并写入如下内容:
declare module "*.module.less";
declare module "*.module.css";
- 运行打包
pnpm build
- 打包成功后,可以看到对应的
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" />
- 然后用浏览器打开
dist/index.html
,就能看到页面正常渲染了 - 手写
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"),
},
},
});
- 本地启动运行
pnpm start
- 浏览器打开 http://localhost:3000/,然后更改代码(背景色改为黄色)保存下,页面也会热更新
7. tailwindcss 配置
- 安装vscode 插件,便于代码提示
- 初始化,生成
tailwind.config.js
文件
npx tailwindcss init
- 更改完善
tailwind.config.js
文件,改为如下:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{tsx,ts,jsx,js}"],
theme: {
extend: {},
},
plugins: [],
};
- 新增
index.less
,并完善
touch src/index.less
// 并写入如下内容:
// 全局的东西
// tailwind 配置
@tailwind base;
@tailwind components;
@tailwind utilities;
- 在
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 />);
- 在
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>
);
}
- 重新 start 下,效果如下: