diff --git a/README.md b/README.md index 0d1b7f09..40cfbe15 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,15 @@ # 个人博客 +[Sysuke](https://www.sysuke.com/) + > 主要是前端知识盘点和面试复习整理,还有算法记录等。 ## TODO - [ ] BlogLayout - [ ] PWA - - Remove old PWA cache: https://plugin-remove-pwa.vuejs.press/ + - Remove old PWA cache: https://ecosystem.vuejs.press/plugins/remove-pwa.html - [ ] Update blog - [ ] Go through all of it -- GitHub Copilot -- React ssr, next.js/remix - -## Learn todo - -### want to know - -- [bun](https://bun.sh/) 一个js的运行时框架,可以理解为更快的node.js,可以直接run typescript等 -- [unocss](https://github.com/unocss/unocss) 原子化CSS模式实现的一种, No parsing, no AST, no scanning, it's INSTANT. -- headless UI - -## WIP - -- vite + [vitest](https://vitest.dev/) or update vite+jest -- Monorepo: [turbo](https://turbo.build/) -- Kotlin + Java + Spring boot +- GitHub Copilot +- React ssr, rsc, next.js/remix diff --git a/src/blog/architecture/monorepoPnpmTurboRepo.md b/src/blog/architecture/monorepoPnpmTurboRepo.md index 72113603..0455d5a2 100644 --- a/src/blog/architecture/monorepoPnpmTurboRepo.md +++ b/src/blog/architecture/monorepoPnpmTurboRepo.md @@ -55,11 +55,12 @@ pnpm init ```json { "engines": { - "node": ">=14.6", + "node": ">=18.17.0", + "pnpm": ">=8", "npm": "please use pnpm", "yarn": "please use pnpm" }, - "packageManager": "pnpm@7.6.0" + "packageManager": "pnpm@8.9.0" } ``` @@ -134,12 +135,12 @@ auto-install-peers=false ```yaml packages: - - "apps/**" + - "apps/*" - "packages/**" - - "projects/**" + - '!**/__test__/**' ``` -这里如果是 `'packages/*'`, 只会识别直接子目录下所有的包,而`'packages/**`, 2个`*`会识别到嵌套的子目录。 而`'!**/test/**'`会排除掉所有的`test`目录。 +这里如果是 `'packages/*'`, 只会识别直接子目录下所有的包,而`'packages/**`, 2个`*`会识别到嵌套的子目录。 而`'!**/__test__/**'`会排除掉所有的`__test__`目录。 创建文件夹目录结构 @@ -161,9 +162,6 @@ packages: │ │ ├── package.json │ ├── webpack-react-base │ │ ├── package.json -├── projects -│ | ├── demo -│ │ | ├── package.json ├── package.json ├── pnpm-lock.yaml └── pnpm-workspace.yaml @@ -262,7 +260,7 @@ pnpm add axios --filter @apps/web # 3. 模块之间的相互依赖 pnpm install @sysuke/eslint-config-react @sysuke/tsconfig --filter @sysuke/webpack-react-base -D # 安装到多个项目 - pnpm add mf-communication --filter="@mf.react-ts/*" +pnpm add mf-communication --filter="@mf.react-ts/*" # 会执行所有 package 下的 build 命令 pnpm build --filter "./packages/**" # 并行的跑apps所有的 dev 命令 @@ -342,8 +340,28 @@ Either start with `eslint-config-` or `@SCOPE/eslint-config.` pnpm add husky lint-staged @commitlint/cli @commitlint/config-conventional -w -D ``` -### Release工作流 +## Release工作流 - changesets +::: details npm版本规范 + +版本格式:主版本号.次版本号.修订号 + +版本号递增规则如下: + +- 主版本号:当你做了不兼容的 API 修改, +- 次版本号:当你做了向下兼容的功能性新增, +- 修订号:当你做了向下兼容的问题修正。 + +> 先行版本号及版本编译信息可以加到“主版本号.次版本号.修订号”的后面,作为延伸。 比如,我们经常在开源项目中见到的:alpha、beta 、rc + +here is the detail doc: [语义化版本 2.0.0](https://semver.org/lang/zh-CN/) + +npm 的版本匹配策略 + +- `^1.0.1`:只要主版本一致都匹配(1.x),如:1、1.x +- `~1.0.1`:只要主版本和次版本一致都匹配(1.1.x),如:1.1、1.1.x +- `*`/`latest` :全匹配,不受版本号影响,可以命中一切新发布的版本号 +::: 一般发一个包,只需要在待发布项目目录,`npm login` + `npm publish`,更新也是一样。 但每次包(Package)的发布,需要修改 `package.json` 的 `version` 字段,以及同步更新一下本次发布修改的 `CHANGELOG.md`。 @@ -364,6 +382,27 @@ pnpm add husky lint-staged @commitlint/cli @commitlint/config-conventional -w - ![](./images/changeset-flow.png) +### process + +1. `changeset init` +- 新项目执行该命令,完成对项目的初始化 +- 会在根目录下生成 .changeset 目录,config.json配置文件 + +2. `changeset` + +- 执行该命令,进行版本管理,会交互式选择不同项目,以及确定发布的版本 +- 会生成一些 .md 文件在目录下,会在 version 的时候消耗 + +3. `changeset version` + +- 消耗上一步生成的相关的一些版本信息及记录内容的 .md 文件,并生成或更新 CHANGELOG.md 文件,之后 .md 文件会被自动删除 +- 相应的 package.json 中的 version 信息也会同步更新 + +4. `changeset publish` + +- 发布包到远程 npm 源 +- 前置条件是你已经进行了 npm 账户登录,如果包名称为 @ah-ailpha/components该类型,还需要在 npm 账户中设置组织 + * 安装 ```shell @@ -383,7 +422,7 @@ pnpm changeset init ```json { - "$schema": "https://unpkg.com/@changesets/config@2.1.1/schema.json", + "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json", "changelog": "@changesets/cli/changelog", "commit": false, "fixed": [], @@ -413,8 +452,8 @@ pnpm changeset init ``` - commit: 设置是否把执行 `changeset add `或 `changeset version` 操作时对修改用 `git` 自动提交对应文件。(A GitHub token with repo, write:packages permissions) -- linked: 设置共享版本的包,而不是独立版本的包,例如一个组件库中主题和单独的组件的关系,也就是修改 Version 的时候,共享的包需要同步一起更新版本 - [fixed](https://github.com/changesets/changesets/blob/main/docs/fixed-packages.md): 设置那些包的版本保持一致的更新 +- linked: 设置共享版本的包,而不是独立版本的包,例如一个组件库中主题和单独的组件的关系,也就是修改 Version 的时候,共享的包需要同步一起更新版本 - access: 公私有安全设定,内网建议 `restricted` ,开源使用 `public` - baseBranch: 设置默认的 Git 分支,例如现在 GitHub 的默认分支应该是 `main` - updateInternalDependencies: 设置互相依赖的包版本更新机制,它是一个枚举(`major|minor|patch`),例如设置为 minor 时,只有当依赖的包新了`minor` 版本或者才会对应地更新 `package.json` 的 `dependencies` 或 `devDependencies` 中对应依赖的版本 diff --git a/src/blog/architecture/webpack5ReactTs.md b/src/blog/architecture/webpack5ReactTs.md index 68f7a743..cd0b095f 100644 --- a/src/blog/architecture/webpack5ReactTs.md +++ b/src/blog/architecture/webpack5ReactTs.md @@ -2,6 +2,7 @@ # 从零搭建 webpack5 + React + Typescript + Jest 基础模版 > not Done +* repo下READMEN是最新的 [github](https://github.com/qinsong77/webpack5-react-template) ## 初始化 package.json diff --git a/src/fe/frameWork/component_library_design.md b/src/fe/frameWork/component_library_design.md index dc5e786b..8d7a08b3 100644 --- a/src/fe/frameWork/component_library_design.md +++ b/src/fe/frameWork/component_library_design.md @@ -203,6 +203,18 @@ When building design system, these requirements need answer: - Maintainability: it should be easy and seamless to modify and maintain. +### shadcn/ui + +- [shadcn/ui 是什么、使用方式、实现原理](https://mp.weixin.qq.com/s/Vo6gz7YiyPmHSyVssyJJ9A) + +Shadcn UI 组件的通用架构如下: + +![](./image/shadcn_base_infa_design.png) + +shadcn/ui基于核心原则构建,即组件的设计应与其实现分开。因此,shadcn/ui中的每个组件都具有两层架构。即: +* 结构和行为层 +* 样式层 + ## reference - [The Modern Guide to Packaging your JavaScript library](https://github.com/frehner/modern-guide-to-packaging-js-library) diff --git a/src/fe/frameWork/image/shadcn_base_infa_design.png b/src/fe/frameWork/image/shadcn_base_infa_design.png new file mode 100644 index 00000000..45e64166 Binary files /dev/null and b/src/fe/frameWork/image/shadcn_base_infa_design.png differ diff --git a/src/fe/typescript/image/relation_types.png b/src/fe/typescript/image/relation_types.png new file mode 100644 index 00000000..8f5f5928 Binary files /dev/null and b/src/fe/typescript/image/relation_types.png differ diff --git a/src/fe/typescript/index.md b/src/fe/typescript/index.md index 1cfa733f..259b22b2 100644 --- a/src/fe/typescript/index.md +++ b/src/fe/typescript/index.md @@ -8,10 +8,29 @@ title: Typescript ### [interface和type的区别](https://www.jianshu.com/p/555e6998af36) 不同点: -- 扩展语法: interface使用extends,type使用‘&’ +- 扩展语法: `interface`使用`extends`,type使用`&` - 同名合并:interface 支持,type 不支持。 - 描述类型:对象、函数两者都适用,但是 type 可以用于基础类型、联合类型、元祖。 -- 计算属性:type 支持计算属性,生成映射类型,;interface 不支持。 +- 计算属性:type 支持计算属性,生成映射类型,;interface 不支持 +```ts +// type 能使用 in 关键字生成映射类型,但 interface 不行。 +type Keys = "firstname" | "surname" + +type DudeType = { +[key in Keys]: string +} + +const test: DudeType = { +firstname: "Pawel", +surname: "Grzybek" +} + +// 报错 +//interface DudeType2 { +// [key in keys]: string +//} + +``` 相同点: @@ -20,8 +39,8 @@ title: Typescript - 总的来说,公共的用 interface 实现,不能用 interface 实现的再用 type 实现。主要是一个项目最好保持一致。 ## 泛型 -泛型,即为更广泛的约束类型。解决类型不确定时的约束,如array中map的声明, -定义了一个泛型变量`T`。`T`作为泛型变量的含义为:在定义约束条件时,暂时还不知道数组的每一项数据类型到底是什么,因此我们只能放一个占位标识在这里,待具体使用时再来明确每一项的具体类型。 +泛型,即为更广泛的约束类型。解决类型不确定时的约束,如array中`map`的声明, +定义了一个泛型变量`T`。`T`作为泛型变量的含义为:在定义约束条件时,暂时还不知道数组的每一项数据类型到底是什么,因此只能放一个占位标识在这里,待具体使用时再来明确每一项的具体类型。 回调函数会返回一个新的数组项,因此需要重新定义一个新的泛型变量来表达这个新数组,即为`U`。 ```typescript interface Array { @@ -185,9 +204,7 @@ function fetchDat(): Promise>> { # Typescript 使用总结 -最近这两年,有很多人都在讨论 Typescript,无论是社区还是各种文章都能看出来,整体来说正面的信息是大于负面的,这篇文章就来整理一下我所了解的 Typescript。 - -本文主要分为 3 个部分: +主要分为 3 个部分: - Typescript 基本概念 - Typescript 高级用法 @@ -195,7 +212,7 @@ function fetchDat(): Promise>> { ## Typescript 基本概念 -至于官网的定义,这里就不多做解释了,大家可以去官网查看。[Typescript 设计目标](https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals) +至于官网的定义,官网:[Typescript 设计目标](https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals) 我理解的定义:赋予 Javascript 类型的概念,让代码可以在运行前就能发现问题。 @@ -302,7 +319,7 @@ function a(a: number, b?: number) {} ## Typescript 高级用法 -Typescript 中的基本用法非常简单,有 js 基础的同学很快就能上手,接下来我们分析一下 Typescript 中更高级的用法,以完成更精密的类型检查。 +-[一文带你理解TS中各种高级语法](https://mp.weixin.qq.com/s/B4BHeXliyRHw8TgVva7xcw) ### 类型断言 @@ -887,9 +904,10 @@ const isString = (val: unknown): val is string => getType(val) === 'string' - `Partial`,将 `T` 中的类型都变为可选; - `ReadOnly`,将 `T` 中的类型都变为只读; -- `Pick`, 抽取对象子集,挑选一组属性并组成一个新的类型; -- `Record`,只作用于 obj 属性而不会引入新的属性; -- `Exclude`,从 `T` 中剔除可以赋值给 `U `的类型; +- `Pick`, 抽取对象子集,挑选一组属性并组成一个新的类型; +- `Omit`, 从type中剔除keys后的新类型 +- `Record`,只作用于 obj 属性而不会引入新的属性; +- `Exclude`,从 `T` 中剔除可以赋值给 `U `的类型; - `Extract`,提取 T 中可以赋值给 U 的类型; - `NonNullable`,从 T 中剔除 null 和 undefined; - `Parameters`,获取函数的参数类型,将每个参数类型放在一个元组中; @@ -950,7 +968,13 @@ type Pick = { Pick映射类型有两个参数: - 第一个参数T,表示要抽取的目标对象 - 第二个参数K,具有一个约束:K一定要来自T所有属性字面量的联合类型 - +##### Omit +```ts +/** + * Construct a type with the properties of T except for those in type K. + */ +type Omit = Pick>; +``` ##### Record ```ts /** @@ -1042,8 +1066,7 @@ type excluded2 = Exclude; //{ noS TypeScript与ECMAScript 2015一样,任何包含**顶级import或者export**的文件都被当成一个模块。相反地,如果一个文件不带有顶级的import或者export声明,那么它的内容被视为**全局可见**的(因此对模块也是可见的)(全局就是以tsconfig.json文件为根目录的所有文件都能访问到) - - +比如下面`Vue`类型声明就是全局的 ```ts // src/Vue.d.ts @@ -1058,6 +1081,45 @@ declare class Vue { } ``` +但如果文件有`import`或者`export` +```ts +import { VueOption } from './option.ts' +// src/Vue.d.ts + +declare class Vue { + options: VueOption + constructor(options: VueOption) +} +``` +那Vue就不再是全局的类型了。 + +这时候可以手动 `declare global`: + +```ts +import { VueOption } from './option.ts' +// src/Vue.d.ts + +declare global { + class Vue { + options: VueOption + constructor(options: VueOption) + } +} +``` +不止是 `es module` 的模块里可以用 `global` 声明全局类型,`module` 的方式声明的 `CommonJS` 模块也是可以的: + +那么如果就是需要引入模块,但是也需要全局声明类型,有什么更好的方式呢? + +通过**编译器指令 `reference`**。这样既可以引入类型声明,又不会导致所有类型声明都变为模块内的: + +eg: `next-env.d.ts` +```ts +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. +``` ### 自己写声明文件 比如以前写了一个请求小模块 `myFetch`,代码如下, @@ -1102,40 +1164,104 @@ declare namespace myFetch { // 使用 namespace 来声明对象下的属性和 // keyof any对应的类型为number | string | symbol,也就是可以做对象键(专业说法叫索引 index)的类型集合。 type k1 = keyof any; ``` -## Typescript 总结 - -### Typescript 优点 +```ts +function getValueFromKey(obj: object, key: string) { + // throw error + // key的值为string代表它仅仅只被规定为字符串 + // TS无法确定obj中是否存在对应的key + return obj[key]; +} +``` +==> +```ts +// 函数接受两个泛型参数 +// T 代表object的类型,同时T需要满足约束是一个对象 +// K 代表第二个参数K的类型,同时K需要满足约束keyof T (keyof T 代表object中所有key组成的联合类型) +// 自然,在函数内部访问obj[key]就不会提示错误了 +function getValueFromKey(obj: T, key: K) { + return obj[key]; +} +``` -1、静态类型检查,提早发现问题。 +* never -2、类型即文档,便于理解,协作。 +表示不存在的值的类型,是任何类型的子类型,除了本身也没有任何子类型,即可以赋值给其他类型,但是其他类型(除了 never))均不能赋值给其他类型,包括any)。 -3、类型推导,自动补全,提升开发效率。 +下面的fail 就返回了 never就说明出现这个函数时已经是该处终点了,后续的代码都不会被执行到,所以 console.log 会变灰色意味着这些代码是永远都到达不了的,如果使用 Lint格式化时会删除。 +nodeJs Fn `process.exit` 就是返回never +```ts +function fail(msg: string): never { + throw new Error(msg) +} -4、出错时,可以大概率排除类型问题,缩短 bug 解决时间。 +fail('error') -实战中的优点: +// 下面这行会显示:TS7027: Unreachable code detected. +console.log(123) +``` +![img.png](image/relation_types.png) -1、发现 es 规范中弃用的方法,如:Date.toGMTString。 +### `is` 关键字自定义 -2、避免了一些不友好的开发代码,如:动态给 obj 添加属性。 +有时候类型的判断是复杂的,或者这样的判断是通用的,所以为了避免重复的编写我们可能需要对这个类型的保护需要提取成函数,那么就可以使用 `is` 来进行指定。 -3、vue 使用变量,如果没有在 data 定义,会直接抛出问题。 +```ts{13} +type Item1 = { + type: 'item1'; + name: string; + age: number; +} -### Typescript 缺点 +type Item2 = { + type: 'item2', + title: string; + description: string; +} -1、短期增加开发成本。 +const isItem1Arr = (value: any): value is Item1[] => { + if (!Array.isArray(value)) { + return false; + } + if (value.length === 0) { + return true; + } + return value.every(item => item.type === 'item1'); +} -2、部分库还没有写 types 文件。 +const fn = (value: Item1[] | Item2[]): void => { + if (isItem1Arr(value)) { + value.forEach(item => { + item.age // no error + }); + } +} +``` -3、不是完全的超集。 +### 定义类型的对象增加属性,并避免用`as` +```ts{10} +interface User { + type: 'student'; + name: string; +} -实战中的问题: +const createUser = (name: string): User => { + // const result = { + // type: 'student' + // } as User; + const result = { + type: 'student' + }; -1、还有一些坑不好解决,axios 编写了拦截器之后,typescript 反映不到 response 中去。 + if (name) { + result.name = parseName(name); + } + return result; +} +``` ## 参考资料 -- [Typescript 官网](https://www.tslang.cn/) +- [Typescript 官网](https://www.typescriptlang.org/) - [一份通俗易懂的 TS 教程,入门 + 实战](https://mp.weixin.qq.com/s/C3A-uNwAeqajB4NqwFIBPQ) - [深入理解 Typescript](https://jkchao.github.io/typescript-book-chinese/) + diff --git a/src/fe/vue/vuex.md b/src/fe/vue/vuex.md index 7a2f2597..10d56c32 100644 --- a/src/fe/vue/vuex.md +++ b/src/fe/vue/vuex.md @@ -3,6 +3,14 @@ title: Vuex --- - [Vuex、Flux、Redux、Redux-saga、Dva、MobX](https://zhuanlan.zhihu.com/p/53599723) +- [理解了状态管理,就理解了前端开发的核心](https://mp.weixin.qq.com/s/xbCXiVMaqVTKCQhSdaZbsQ) + +**状态是数据的变化**,比如颜色是红色或蓝色是数据,而颜色从红色变为蓝色这就是状态了。 + +状态管理具体有两层含义: + +* 状态变化之前的逻辑,一般是异步的。 +* 状态变化之后的联动处理,比如渲染视图或执行某段逻辑。 不管是Vue,还是 React,都需要管理状态(state),比如组件之间都有**共享状态**的需要。什么是共享状态?比如一个组件需要使用另一个组件的状态,或者一个组件需要改变另一个组件的状态,都是共享状态。 diff --git a/src/fe/webpack/babel.md b/src/fe/webpack/babel.md index 5b471213..2b20e1e7 100644 --- a/src/fe/webpack/babel.md +++ b/src/fe/webpack/babel.md @@ -5,7 +5,7 @@ title: Babel **Babel is a JavaScript compiler.** -Babel就是一个JavaScript编译器,babel编译分为三个阶段,**解析(parse),转换(transform),生成(generate)**。 +Babel就是一个JavaScript编译器,babel编译分为三个阶段,**解析(parse),转换(transform),生成(generate)**。解析过程又可分为**词法解析**和**语法解析**两个过程。 Babel本身不支持转换,转换是通过一个个 plugin实现。 ![](./image/babel.png) diff --git a/src/fe/webpack/vite.md b/src/fe/webpack/vite.md index 15fa028c..57354c03 100644 --- a/src/fe/webpack/vite.md +++ b/src/fe/webpack/vite.md @@ -45,6 +45,7 @@ webpack 打包原理 ### 原理 +- [Vite 的实现原理](https://mp.weixin.qq.com/s/ejkfARh6hlOAUnw5Eadb6Q) - [文章](https://juejin.cn/post/6931618997251080200) 当声明一个 script 标签类型为 module 时如: