编辑器-前端代码风格规范

最近在升级一个旧的项目,早前的项目由于没有使用 ts,改起来有点痛苦以及当时没有做好的规范约束,以至于不同人贡献的代码差别很大,看着着实痛苦。 规范的意义 1、保证代码的一致性,从根本上提高代码整体的可读性、可维护性、可复用性:保持一致的编码风格,能让其他人在维护代码时能快速上手,即使没有什么经验的开发同学也能保障其代码的交付质量,为后期维护提供更好的支持。 2、有效降低开发和沟通的成本,提升团队整体效率:大型项目最复杂的模块往往仅占整体的一小部分,更多的是相对简单的日常开发和维护。遵循统一的技术规范,能够有效的降低多人协作工程中的沟通成本。如果没有统一规范则出现混乱的局面,留下庞大的技术债。 技术栈统一 基础:Typescript + Tsx 框架:React + React Dom Css 处理器:Sass UI 框架:Ant Design / DongDesign 状态管理:React Redux + Rematch 构建工具:Vite ESLint 规范 基准规则 不重复造轮子,基于 eslint:recommend 配置并改进 能够帮助发现代码错误的规则,全部开启 ESLint 规则 允许非大写开头的构造函数名 ( new-cap: 0 ) 禁止使用 var ( no-var: 2 ) 限制 console 使用,仅允许 warn 和 error ( no-console: [1, { allow: [‘warn’, ’error’] }] ) 禁用内部声明 ( no-inner-declarations: 2 ) 强制使用 const 声明不会被修改的变量 ( prefer-const: [2, { ‘destructuring’: ‘all’ }] ) 禁止特定语法,如 debugger、label 和 with 语句 React 规则 允许组件没有 displayName 禁止 JSX 中的重复属性 禁止未定义的 JSX 元素 禁止直接修改 state 强制组件的 render 方法返回值 不要求 prop-types 验证 不要求导入 React (新版 React 不需要) 禁止在 componentDidUpdate 中使用 setState 鼓励使用无状态函数组件 强制执行 React Hooks 规则 Import 规则 禁止未使用的模块 import 语句后需要空行 禁止使用 CommonJS 模块语法 (require),但允许条件性 require 禁止重复导入 Import 排序 强制对 imports 和 exports 进行排序 TypeScript 规则 不强制函数返回类型注解 允许变量在定义前使用 对未使用的变量发出警告 不强制模块边界类型 允许特定情况下的空函数 允许 this 别名 允许 require 语句 允许 @ts-comment 注释 Git 规范 commit日志基本规范 <type>(<scope>): <subject> <BLANK LINE> <body> <BLANK LINE> <footer> feat: 新增 feature fix: 修复 bug docs: 仅仅修改了文档,比如 README, CHANGELOG, CONTRIBUTE等等 style: 仅仅修改了空格、格式缩进、逗号等等,不改变代码逻辑 refactor: 代码重构,没有加新功能或者修复 bug perf: 优化相关,比如提升性能、体验 test: 测试用例,包括单元测试、集成测试等 chore: 改变构建流程、或者增加依赖库、工具等 revert: 回滚到上一个版本 分支规范 test 测试分支 master 主分支 release release 分支 feat/xxx 特性分支 hotfix/xxx 修复分支 ESLint 配置示例 extends: - eslint:recommended - plugin:react/recommended - plugin:@typescript-eslint/eslint-recommended - plugin:@typescript-eslint/recommended - prettier - plugin:import/errors - plugin:import/warnings - plugin:import/typescript parser: '@typescript-eslint/parser' env: es6: true node: true plugins: - react - prettier - react-hooks - simple-import-sort - unused-imports - '@typescript-eslint' parserOptions: sourceType: module ecmaFeatures: jsx: true rules: prettier/prettier: [ 2, { singleQuote: true, trailingComma: 'es5', semi: false, 'endOfLine': 'auto', }, ] # eslint new-cap: 0 no-var: 2 no-console: [1, { allow: ['warn', 'error'] }] no-unused-vars: 0 no-inner-declarations: 2 camelcase: 0 no-useless-escape: 0 no-prototype-builtins: 0 no-restricted-syntax: - 2 - DebuggerStatement - LabeledStatement - WithStatement require-atomic-updates: 0 prefer-rest-params: 0 prefer-const: [2, { 'destructuring': 'all' }] prefer-spread: 0 # react react/display-name: 0 react/jsx-no-duplicate-props: 2 react/jsx-no-undef: 2 react/no-deprecated: 0 react/no-direct-mutation-state: 2 react/no-render-return-value: 2 react/require-render-return: 2 react/jsx-uses-react: 1 react/jsx-uses-vars: 1 react/prop-types: 0 react/react-in-jsx-scope: 0 react/no-did-update-set-state: 2 react/no-redundant-should-component-update: 2 react/no-typos: 2 react/no-unused-prop-types: 2 react/no-unused-state: 2 react/prefer-stateless-function: [1, { ignorePureComponents: true }] react/void-dom-elements-no-children: 2 react/jsx-boolean-value: 1 react/jsx-no-bind: [2, { ignoreRefs: true, allowArrowFunctions: true }] react-hooks/rules-of-hooks: 2 react-hooks/exhaustive-deps: 1 react-perf/jsx-no-new-object-as-prop: 1 react-perf/jsx-no-new-array-as-prop: 1 # import import/no-unused-modules: 2 import/newline-after-import: 2 import/no-commonjs: [2, { allowConditionalRequire: true }] import/no-duplicates: 2 # simple-import-sort simple-import-sort/imports: 2 simple-import-sort/exports: 2 # typescript '@typescript-eslint/camelcase': 0 '@typescript-eslint/explicit-function-return-type': 0 '@typescript-eslint/no-use-before-define': 0 '@typescript-eslint/no-unused-vars': [1, { ignoreRestSiblings: true }] '@typescript-eslint/explicit-module-boundary-types': 0 '@typescript-eslint/ban-types': 0 '@typescript-eslint/no-empty-function': [1, { 'allow': ['private-constructors', 'protected-constructors'] }] '@typescript-eslint/no-this-alias': 0 '@typescript-eslint/no-var-requires': 0 '@typescript-eslint/ban-ts-comment': 0 '@typescript-eslint/strict-boolean-expressions': [1, { allowNullableBoolean: true }] overrides: - files: - '*.js' - '*.jsx' rules: import/no-commonjs: 0 - files: - '*.ts' - '*.tsx' rules: import/no-unresolved: 0 lodash/prefer-lodash-method: 0 VSCode 配置 { "editor.tabSize": 2, "editor.insertSpaces": true, "search.exclude": { }, "editor.formatOnSave": false, "editor.renderControlCharacters": true, "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, "[less]": { "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", }, "[css]": { "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", }, "[yaml]": { "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", }, "[html]": { "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", }, "[json]": { "editor.formatOnSave": true, "editor.defaultFormatter": "vscode.json-language-features", }, "[markdown]": { "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", }, "[mdx]": { "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", }, "files.associations": { "*.xml": "html", "*.svg": "html", }, "prettier.ignorePath": ".eslintignore", "liveServer.settings.port": 5501, "code-runner.executorMapByGlob": { "*.test.*": "cd $dir && yarn test --files $fullFileName", }, "typescript.tsdk": "node_modules/typescript/lib", "search.followSymlinks": false, "files.exclude": { "**/.git": true, "**/.svn": true, "**/.hg": true, "**/CVS": true, "**/.DS_Store": true, "**/tmp": true, "**/bower_components": true, }, "files.watcherExclude": { "**/.git/objects/**": true, "**/.git/subtree-cache/**": true, "**/node_modules/**": true, "**/tmp/**": true, "**/bower_components/**": true, "**/dist/**": true }, "makefile.configureOnOpen": false } tsconfig 配置 { "compilerOptions": { "typeRoots": ["./node_modules/@types", "./types"], "target": "es5", "lib": [ "dom", "dom.iterable", "esnext" ], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, "strict": true, "forceConsistentCasingInFileNames": false, "noFallthroughCasesInSwitch": true, "module": "esnext", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "jsx": "react", "outDir": "./dist", "plugins": [ { "name": "typescript-plugin-css-modules" } ], "paths": { "~/*": ["./src/*"], "@constant/*": ["./src/constant/*"] } }, "exclude": [ "build", "node_modules" ], "include": [ "src", "typing.d.ts" ] }

March 10, 2019 · 3 min · 633 words · Link

D2C - 生成 Tailwind Css 前端代码

背景 目前 Relay 生成代码功能研发过程中,我们发现 D2C 的难点之一:Class 类名不语义化的问题。D2C 算法生成的类名,如 view0、view1、view2、text0、text1 等缺乏有效含义,对研发不够友好。 方案 1、使用 LLM 大模型如 GPT4 进行语义优化 经过测试大模型可以优化代码,但存在几个问题: 大模型存在 token 限制问题,输入和输出都存在可能超出的情况,特别是输出限制了 8K 大小,很多稿子跑下来就超出了。 大模型耗时比较长,一个请求过去要好几秒,使用 SSE 体验会略好一点 大模型如果不结合图片做多模态预览效果也会略差一点,比如不同的稿子,在没有图片 input 时候,输出的 class 有一定概率重复,并可能跟设计稿件毫无关联:如 view 大概率会变成 product等。 2、生成 Tailwind css 风格的 class,不再需要自定义class Tailwind CSS 是目前世界上最流行的原子化 CSS 框架。它集成了诸如 flex, pt-4, text-center 和 rotate-90 这样语义化的类名。我们开发者能直接在各种脚本标记语言中编写它们,并把它们组合起来,构建出任何的设计。自从 3.x 大版本开始,Tailwind CSS 把引擎升级为 Just in Time(jit)。这使得我们能够编写代码的同时,实时生成各种 CSS,真正的做到了所写即所得。 Tailwind CSS 特点: 高度可定制化 响应式设计友好 提高开发速度 代码可读性和维护性 在 HTML 代码中直接使用工具类来描述样式,使得样式和结构紧密结合,代码的可读性更高。例如,看到 class="bg-gray-100 p-4 rounded-md" 这样的代码,就能够很直观地理解这个元素有一个浅灰色的背景、一定的内边距并且是中等圆角。 ...

March 3, 2019 · 2 min · 346 words · Link

Webpack 与 Vite 构建工具之差

在前端开发领域,Webpack 和 Vite 是两款备受瞩目的构建工具,它们在提升开发效率和优化项目构建流程方面发挥着关键作用,但在诸多方面存在显著差异。 一、构建原理 Webpack 以其强大的模块打包能力著称,它会在启动时对整个项目进行依赖分析,递归地构建模块依赖图。从项目入口文件开始,将所有相关的 JavaScript、CSS、图片等资源都纳入处理范围,将它们打包成一个或多个 bundle 文件。在这个过程中,Webpack 会对代码进行各种处理,如代码压缩、模块合并、loader 转换等,以适应不同的环境和需求。 而 Vite 则采用了一种截然不同的策略,利用浏览器原生(script type="module")的 ES 模块支持来实现快速的冷启动。在开发模式下,Vite 不会像 Webpack 那样预先打包所有模块,而是在浏览器请求某个模块时,才对其进行即时编译和传输。这使得 Vite 在启动项目时速度极快,大大缩短了开发过程中的等待时间。在生产环境下,Vite 会进行预构建来优化依赖,将一些常用的第三方库预先打包,进一步提升性能。 二、开发体验 在开发过程中,Webpack 的热更新(HMR)功能虽然能够在一定程度上实现代码修改后的实时更新,但由于其全量构建的特性,在大型项目中可能会出现更新速度较慢的情况。每次代码修改后,Webpack 需要重新构建相关的模块和依赖,这个过程可能会耗费数秒甚至更长时间,影响开发的流畅性。 相比之下,Vite 的热更新机制更为高效。由于其基于 ES 模块的按需加载特性,当代码发生变化时,Vite 能够精准地只更新受影响的模块,并且速度极快,几乎可以做到即时更新,让开发者能够迅速看到代码修改的效果,极大地提升了开发效率和体验。 三、配置复杂度 Webpack 的配置向来以复杂著称,为了实现各种功能,如代码分割、优化、loader 和 plugin 的配置等,开发者需要编写大量的配置代码。对于初学者来说,这无疑是一个较高的门槛,需要花费较多时间去学习和理解各种配置选项的作用和相互关系。 Vite 的配置则相对简洁明了,它默认提供了许多合理的配置,在大多数情况下,开发者只需要进行少量的配置调整即可满足项目需求。这使得 Vite 在项目初始化和配置方面更加便捷,能够让开发者更快地投入到实际开发中。 四、优缺点对比 Webpack 优点 拥有丰富的插件和 loader 生态系统,几乎可以处理任何前端资源和构建需求,能够对项目进行深度定制和优化。例如,可以通过特定的 loader 将不同类型的文件转换为 JavaScript 模块,或者使用插件实现复杂的功能,如代码压缩、混淆、提取公共代码等。 对大型项目的支持较为成熟,能够有效地管理复杂的依赖关系,确保项目的稳定性和可维护性。通过细致的配置,可以实现精确的代码分割,将不同页面或功能的代码拆分成独立的 chunk,提高页面加载速度和性能。 缺点 配置复杂,学习成本高,对于新手开发者来说可能会感到困惑和无从下手。过多的配置选项也容易导致配置文件变得冗长和难以维护。 开发时的热更新速度相对较慢,尤其是在大型项目中,每次代码修改后的重新构建过程可能会耗费较长时间,影响开发效率。 Vite 优点 开发启动速度极快,基于 ES 模块的按需加载和即时编译特性,大大缩短了冷启动时间和代码更新的反馈周期,提供了流畅的开发体验。 配置简单,默认配置已经能够满足大多数项目的基本需求,开发者可以快速上手并开始项目开发,减少了在配置上花费的时间和精力。 缺点 对一些老旧项目或不遵循 ES 模块规范的代码兼容性可能较差,需要进行一定的改造才能顺利使用 Vite 进行构建。 虽然在开发体验上表现出色,但在生产环境下的一些高级优化功能可能相对 Webpack 来说不够丰富,对于一些对性能极致追求的项目可能需要额外的配置和优化工作。 适用场景 对于大型项目,尤其是那些具有复杂依赖关系和大量代码的项目,Webpack 的强大功能和丰富的插件生态系统能够更好地应对。它可以通过细致的配置对项目进行深度优化,例如实现精确的代码分割,将不同页面或功能的代码拆分成独立的 chunk,提高页面加载速度和性能。 ...

January 10, 2019 · 1 min · 83 words · Link

CSS 单位到底怎么选

在 CSS 样式表中,合理地运用单位来定义元素的尺寸、间距、字体大小等属性是至关重要的,不同的单位有着各自的特性和适用场景。本文将深入探讨几种常见的 CSS 单位:px、rpx、vw 和 rem。 px(像素) 像素(Pixel)是最为基础和常见的单位,它直接对应屏幕上的一个物理像素点。在早期的网页设计中,px 几乎一统天下。例如,当我们设置一个 div 的宽度为 200px 时,它在屏幕上所占据的空间就是实实在在的 200 个像素点。这种确定性使得布局相对直观,设计师可以精确掌控元素的尺寸。然而,px 的缺点也逐渐显现,随着设备屏幕分辨率的多样化,固定像素值在不同分辨率下可能出现显示效果差异巨大的问题。比如,同样一个宽度为 300px 的按钮,在低分辨率屏幕上可能看起来大小适中,而在高分辨率的视网膜屏上,就会显得细小局促。 rpx(响应式像素) rpx 是微信小程序特有的一种单位,它是根据屏幕宽度进行自适应调整的。小程序规定屏幕宽度为 750rpx,无论在何种设备上打开小程序,都会将屏幕宽度等比例划分为 750 份。这意味着如果一个元素设置宽度为 375rpx,那么在宽度为 375px 的设备上,它将占据屏幕宽度的一半;而在宽度为 750px 的设备上,它依然占据屏幕宽度的一半,即 375px。这种自适应特性使得小程序的页面布局能够很好地适配各种手机屏幕,极大地减轻了开发者对不同屏幕适配的工作量,为用户带来一致的视觉体验。 vw(视口宽度百分比) vw 代表视口宽度的百分比单位,1vw 等于视口宽度的 1%。相较于 px,它具有一定的响应式特性。例如,设置一个容器的宽度为 50vw,那么它将始终占据当前视口宽度的一半。这在构建一些需要随浏览器窗口大小动态变化的布局时非常实用,如自适应的侧边栏或弹性的图片展示区域。不过,使用 vw 时要注意,它只与视口宽度相关,若元素的高度也需要按比例自适应,还需结合其他技术或单位来综合实现。 rem(根元素字体大小相对单位) rem 是以根元素(通常是 html 元素)的字体大小为基准的相对单位。默认情况下,大部分浏览器的根元素字体大小为 16px,此时 1rem 就等于 16px。但开发者可以通过修改根元素的字体大小来统一缩放整个页面的尺寸。比如,将根元素字体大小设置为 12px,那么后续所有以 rem 为单位的元素尺寸都会相应缩小。这使得页面在不同屏幕尺寸下,只要合理调整根元素字体大小,就能实现相对灵活且整体协调的布局。尤其在做移动端网页适配时,结合媒体查询动态修改根元素字体大小,rem 单位能发挥出强大的自适应优势,确保页面元素在各种手机和平板设备上都呈现出良好的视觉效果。 如何选择使用合适的单位? 怎样选择主要有以下两大纬度来决定: 项目类型 网页项目: px:对于一些需要精确控制尺寸的元素,如固定宽度的导航栏、按钮等,px 是一个不错的选择。它可以确保在不同浏览器和设备上都能呈现出一致的固定大小。 rem:在进行网页整体布局和字体大小设置时,rem 更为合适。通过设置根元素的字体大小,可以方便地实现整个页面的相对缩放,适应不同屏幕尺寸的设备,尤其在响应式设计中表现出色。 vw:当需要创建与视口宽度紧密相关的布局,如全屏的背景图片、自适应的轮播图等,vw 可以很好地实现根据视口宽度的动态调整。 微信小程序项目: rpx:由于微信小程序的跨设备适配需求较高,rpx 是首选单位。它能够自动根据屏幕宽度进行自适应调整,大大减少了开发者在不同设备上进行适配的工作量,确保页面在各种手机屏幕上都能保持良好的视觉效果。 设计需求 固定尺寸设计:如果设计稿中的元素尺寸是固定的,且不考虑设备屏幕的变化,那么 px 可以准确地还原设计稿中的尺寸。例如,一些品牌标识、特定尺寸的图标等,使用 px 可以确保其在任何情况下都保持原设计的大小和比例。 自适应布局设计:对于需要在不同设备上自适应显示的页面,rem 和 vw 是更好的选择。例如,一个新闻列表页面,要求在不同屏幕宽度的设备上都能自适应显示,使用 rem 或 vw 可以使列表项的宽度、间距等随着屏幕大小的变化而自动调整,提供更好的用户体验。 高分辨率屏幕适配:在处理高分辨率屏幕时,如视网膜屏,px 可能会导致元素在屏幕上显示得过于细小。此时,rem 可以通过调整根元素字体大小来进行整体缩放,使页面在高分辨率屏幕上也能保持清晰和易读。而 rpx 在微信小程序中已经自动处理了高分辨率屏幕的适配问题。 在实际项目中,通常会根据具体情况综合使用多种单位。例如,在网页项目中,可以以 rem 为主要单位进行整体布局,对于一些需要精确控制的元素再结合使用 px;在微信小程序项目中,以 rpx 为主,对于一些特殊情况可以适当使用 px 进行微调。关键是要根据项目的特点和需求,权衡各种单位的优缺点,选择最合适的方案。

December 30, 2018 · 1 min · 93 words · Link

JavaScript 的 SOLID 原则

SOLID 原则首先由著名的计算机科学家 Robert C·Martin (著名的Bob大叔)由 2000 年在他的论文中提出。但是 SOLID 缩略词是稍晚由 Michael Feathers 先使用的。 Bob大叔也是畅销书《代码整洁之道》和《架构整洁之道》的作者,也是 “Agile Alliance” 的成员。 SOLID 是一组原则的首字母缩写,包括: S 单一职责原则 O 开闭原则 L 里氏替换原则 I 接口隔离原则 D 依赖倒置原则 有助于软件工程师设计和编写可维护、可扩展和灵活的代码。其目的是什么呢?是为了提高遵循面向对象编程(OOP)范式开发的软件质量。 单一职责原则(SRP) SOLID 中的第一个字母代表单一职责原则。该原则建议一个类或模块应该只执行一个功能。如果一个类处理多个功能,那么在不影响其他功能的情况下更新一个功能就会变得棘手。随之而来的复杂性可能会导致软件性能出现故障。为了避免这些问题,我们应尽力编写关注点分离的模块化软件。 如果一个类有太多的职责或功能,修改起来就会很头疼。通过使用单一职责原则,我们可以编写模块化、更易于维护且不易出错的代码。例如,以一个人员模型为例: class Person { constructor(name, age, height, country) { this.name = name; this.age = age; this.height = height; this.country = country; } getPersonCountry() { console.log(this.country); } greetPerson() { console.log("Hi " + this.name); } static calculateAge(dob) { const today = new Date(); const birthDate = new Date(dob); let age = today.getFullYear() - birthDate.getFullYear(); const monthDiff = today.getMonth() - birthDate.getMonth(); if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) { age--; } return age; } } 上面的代码看起来没问题,对吧?不完全是。示例代码违反了单一职责原则。Person类不仅仅是可以创建其他Person实例的唯一模型,它还有其他职责,如calculateAge、greetPerson和getPersonCountry。 ...

December 13, 2018 · 4 min · 698 words · Link

Nginx empty-gif 模块

概述 Nginx 是一个高性能的 HTTP 和反向代理服务器,广泛用于负载均衡、缓存和静态文件服务。Empty-GIF 模块是 Nginx 的一个第三方模块,主要用于处理透明的 GIF 图像(1x1 像素),通常用于跟踪用户行为或在网页中占位。 主要功能 Empty-GIF 模块的主要功能包括: 生成透明 GIF: 该模块可以生成一个 1x1 像素的透明 GIF 图像,通常用于网页中的占位符或跟踪像素。 减少带宽消耗: 通过使用透明 GIF,网站可以减少不必要的图像请求,从而节省带宽。 简单的配置: 该模块的配置非常简单,用户只需在 Nginx 配置文件中添加几行代码即可启用。 配置 在 Nginx 的配置文件中(通常是 /etc/nginx/nginx.conf),可以通过以下方式启用 Empty-GIF 模块: http { ... server { listen 80; server_name example.com; location /tracking { empty_gif; # 使用 empty-gif 模块 } } } 使用场景 Empty-GIF 模块的使用场景包括: 用户行为跟踪: 在网页中嵌入透明 GIF,以便跟踪用户的访问行为。 广告监测: 广告网络可以使用透明 GIF 来监测广告的展示和点击情况。 占位符: 在某些情况下,开发者可能希望在页面中使用占位符图像,而不希望加载实际的图像文件。 🌰栗子 生成如下链接: ...

December 12, 2018 · 1 min · 71 words · Link

简单聊一聊单例设计模式

你是否曾经遇到过需要在应用程序的多个部分共享一个对象的情况,比如数据库连接、WebSocket 客户端或配置管理器或全局的 Logger 对象? 你如何管理这样一个对象,使其在整个应用程序或进程生命周期中保持一致且可访问?这就是单例设计模式发挥作用的地方。 概述 单例是一种创建型设计模式,属于设计模式的一类,用于解决使用new关键字或操作符创建对象的原生方式所带来的不同问题。 单例设计模式主要致力于解决两个主要问题: 我们如何为实例提供一个全局访问点? 我们如何确保一个类或特定类型的对象只有一个实例? 它可以简化并规范我们管理特定种类或类型的全局状态的方式,例如数据库连接、WebSocket 客户端、缓存服务,或者任何我们在整个应用程序生命周期中需要在内存中持久化和修改的内容。 如何实现单例设计模式 class Singleton { private static instance: Singleton private construct() { // 私有构造函数确保只能通过静态方法创建唯一实例 } public static getInstance () { if (!this.instance) { this.instance = new Singleton() } return this.instance } } 该类应该定义一个静态属性来存储唯一可共享的实例。static 关键字意味着实例对象与类的实例无关,而是与类定义本身相关联。类的构造函数应该标记为private 则无法通过 new 创建实例。获取类实例的唯一方法是调用 getInstance 静态方法。 const instance = Singleton.getInstance() 我们可以通过调用与单例类相关联的静态方法getInstance来使用上述类。getInstance 方法保证即使我们在代码库的不同位置多次实例化我们的类,我们始终得到相同的实例。 第一个实际场景 在 node 服务中,需要记录调用接口产生的 log,因此要设计一个全局的 Logger 集中管理日志行为。 class Logger { private static instance: Logger; private logs: string[] = []; private constructor() {} public static getInstance(): Logger { if (!Logger.instance) { Logger.instance = new Logger(); } return Logger.instance; } public log(message: string) { this.logs.push(message); } } // 使用 const logger = Logger.getInstance(); logger.log('Application started'); 在这个例子中,private constructor() 确保: ...

November 24, 2018 · 2 min · 279 words · Link

vite 开发环境编译流程

Vite 的开发环境采用按需编译和原生 ES 模块驱动的架构,与传统打包工具(如 Webpack)有本质区别。以下是其编译流程及对不同文件类型的处理方式: 一、Vite 开发环境核心原理 启动服务器: Vite 启动时不进行全量编译,而是创建一个 HTTP 服务器,监听文件变化。 浏览器优先原则: 浏览器直接通过 ES 模块的 import 语句请求文件(如 import './main.ts'),Vite 拦截这些请求并实时编译。 按需编译: 只有当浏览器请求某个模块时,Vite 才会对其进行编译(如 TS 转 JS、Less 转 CSS),并返回处理后的结果。 二、不同文件类型的处理流程 1. Less 文件处理 请求拦截:当浏览器请求 .less 文件时,Vite 拦截该请求。 编译:使用 less 编译器将 Less 转换为 CSS。 注入:将 CSS 通过 JavaScript 动态注入到页面的 <style> 标签中(支持 HMR 热更新)。 2. TS 文件处理 请求拦截:拦截 .ts 文件请求。 编译:使用 esbuild(速度极快)将 TS 转换为 JS,仅进行语法转换(不做类型检查)。 类型检查:类型检查由独立进程(如 VSCode 插件或 tsc --watch)完成,不阻塞开发服务器。

June 5, 2018 · 1 min · 68 words · Link