从0到1构建基于自身业务的前端工具库

news2024/11/16 9:32:46

前言

在实际项目开发中无论 M 端、PC 端,或多或少都有一个 utils 文件目录去管理项目中用到的一些常用的工具方法,比如:时间处理、价格处理、解析url参数、加载脚本等,其中很多是重复、基础、或基于某种业务场景的工具,存在项目间冗余的痛点以及工具方法规范不统一的问题。

  • 在实际开发过程中,经常使用一些开源工具库,如 lodash,以方便、快捷的进行项目开发。但是当 npm上没有自己中意或符合自身业务的工具时,我们不得不自己动手,此时拥有自己的、基于业务的工具库就显得尤为重要。
  • 我们所熟知的Vue、React等诸多知名前端框架,或公司提供的一些类库,它们是如何开发、构建、打包出来的,本文将带领你了解到如何从0到1构建基于自身业务的前端工具库。

构建工具库主流方案

1. WEBPACK

  • webpack 提供了构建和打包不同模块化规则的库,只是需要自己去搭建开发底层架构。
  • vue-cli,基于 webpack , vue-cli 脚手架工具可以快速初始化一个 vue 应用,它也可以初始化一个构建库。

2. ROLLUP

  • rollup 是一个专门针对JavaScript模块打包器,可以将应用或库的小块代码编译成更复杂的功能代码。
  • Vue、React 等许多流行前端框架的构建和打包都能看到 rollup 的身影。

为什么采用 ROLLUP 而不是 WEBPACK

  • webpack 主要职能是开发应用,而 rollup 主要针对的就是 js 库的开发,如果你要开发 js 库,那 webpack 的繁琐配置和打包后的文件体积就不太适用了,通过webpack打包构建出来的源代码增加了很多工具函数以外的模块依赖代码。
  • rollup 只是把业务代码转码成目标 js ,小巧且轻便。rollup对于代码的Tree-shaking和ES6模块有着算法优势上的支持,如果只想构建一个简单的库,并且是基于ES6开发的,加上其简洁的API,rollup得到更多开发者的青睐。

工具库底层架构设计

构建工具库底层架构大概需要哪些功能的支持:

架构依赖需知

在对底层架构设计的基础上,首先需要把用到的依赖库简单熟悉一下:

rollup 全家桶

•  rollup(工具库打包构建核心包)

•  rollup-plugin-livereload(rollup 插件,热更新,方便本地 debugger 开发)

•  rollup-plugin-serve(rollup 插件,本地服务代理,方便在本地 html 中调试工具)

•  rollup-plugin-terser(rollup 插件,代码压缩混淆)

•  rollup-plugin-visualizer(rollup 插件,可视化并分析 Rollup bundle,以查看模块占用)

•  @rollup/plugin-babel(rollup 插件,rollup 的 babel 插件,ES6 转 ES5)

•  @rollup/plugin-commonjs(rollup 插件,用来将 CommonJS 模块转换为 ES6,这样它们就可以包含在 Rollup 包中)

•  @rollup/plugin-json(rollup 插件,它将.json 文件转换为 ES6 模块)

•  @rollup/plugin-node-resolve(rollup 插件,它使用节点解析算法定位模块,用于在节点模块中使用第三方 node_modules 包)

•  @rollup/plugin-typescript(rollup 插件,对 typescript 的支持,将 typescript 进行 tsc 转为 js)

typescript 相关

•  typescript(使用 ts 开发工具库)

•  tslib(TypeScript 的运行库,它包含了 TypeScript 所有的帮助函数)

•  @typescript-eslint/eslint-plugin(TypeScript 的 eslint 插件,约束 ts 书写规范)

•  @typescript-eslint/parser(ESLint 解析器,它利用 TypeScript ESTree 来允许 ESLint 检测 TypeScript 源代码)

文档相关

•  typedoc(TypeScript 项目的文档生成器)

•  gulp(使用 gulp 构建文档系统)

•  gulp-typedoc(Gulp 插件来执行 TypeDoc 工具)

•  browser-sync(文档系统热更新)

单元测试相关

•  jest(一款优雅、简洁的 JavaScript 测试框架)

•  @types/jest(Jest 的类型定义)

•  ts-jest(一个支持源映射的 Jest 转换器,允许您使用 Jest 来测试用 TypeScript 编写的项目)

•  @babel/preset-typescript(TypeScript 的 Babel 预设)

其他依赖

•  eslint(代码规范约束)

•  @babel/core(@rollup/plugin-babel 依赖的 babel 解析插件)

•  @babel/plugin-transform-runtime(babel 转译依赖)

•  @babel/preset-env(babel 转译依赖)

•  chalk(控制台字符样式)

•  rimraf(UNIX 命令 rm -rf 用于 node)

•  cross-env(跨平台设置 node 环境变量)


底层架构搭建

1. 初始化项目

新建一个文件夹 utils-demo,执行 npm init,过程会询问构建项目的基本信息,按需填写即可:

npm init

2. 组织工具库业务开发 SRC 目录结构

创建工具库业务开发 src 文件目录,明确怎样规划工具库包,里面放置的是工具库开发需要的业务代码:

3. 安装项目依赖

要对 typescript 代码进行解析支持需要安装对 ts 支持的依赖,以及对开发的工具的一些依赖包:

yarn add typescript tslib rollup rollup-plugin-livereload rollup-plugin-serve rollup-plugin-terser rollup-plugin-visualizer 
@rollup/plugin-babel @rollup/plugin-commonjs @rollup/plugin-json @rollup/plugin-node-resolve @rollup/plugin-typescript 
@babel/core @babel/plugin-transform-runtime @babel/preset-env rimraf lodash chalk@^4.1.2 -D

这里遇到一个坑,关于最新 chalk5.0.0 不支持在 nodejs 中 require()导入,所以锁定包版本 chalk@^4.1.2

要对 typescript 进行解析和编译还需要配置 tsconfig.json,该文件中指定了用来编译这个项目的根文件和编译选项,在项目根目录,使用 tsc --init 命令快速生成 tsconfig.json 文件(前提全局安装 typescript)

npm i typescript -g
tsc --init

初始化 tsconfig 完成之后,根目录自动生成 tsconfig.json 文件,需要对其进行简单的配置,以适用于 ts 项目,其中具体含义可以参考tsconfig.json官网

4. 组织项目打包构建 SCRIPTS 目录结构

  1. 根目录创建项目打包构建 scripts 脚本文件目录,里面放置的是有关于项目打包构建需要的文件:

生成rollup配置项函数核心代码:

const moduleName = camelCase(name) // 当format为iife和umd时必须提供,将作为全局变量挂在window下:window.moduleName=...
const banner = generateBanner() // 包说明文案
// 生成rollup配置文件函数
const generateConfigs = (options) => {
  const { input, outputFile } = options
  console.log(chalk.greenBright(`获取打包入口:${input}`))
  const result = []
  const pushPlugins = ({ format, plugins, ext }) => {
    result.push({
      input, // 打包入口文件
      external: [], // 如果打包出来的文件有项目依赖,可以在这里配置是否将项目依赖一起打到包里面还是作为外部依赖
      // 打包出口文件
      output: {
        file: `${outputFile}${ext}`, // 出口文件名称
        sourcemap: true, // // 是否生成sourcemap
        format, // 打包的模块化格式
        name: moduleName, // 当format为iife和umd时必须提供,将作为全局变量挂在window下:window.moduleName=...
        exports: 'named' /** Disable warning for default imports */,
        banner, // 打包出来的文件在最顶部的说明文案
        globals: {} // 如果external设置了打包忽略的项目依赖,在此配置,项目依赖的全局变量
      },
      plugins // rollup插件
    })
  }
  buildType.forEach(({ format, ext }) => {
    let plugins = [...defaultPlugins]
    // 生产环境加入包分析以及代码压缩
    plugins = [
      ...plugins,
      visualizer({
        gzipSize: true,
        brotliSize: true
      }),
      terser()
    ]

    pushPlugins({ format, plugins, ext })
  })
return result
}



  1. rollup 在打包构建的过程中需要进行 babel 的转译,需要在根目录添加.babelrc 文件告知 babel:
{
  "presets": [
    [
      "@babel/preset-env"
    ]
  ],
  "plugins": ["@babel/plugin-transform-runtime"]
}


  1. 此时距离打包构建工具库只差一步之遥,配置打包脚本命令,在 package.json 中配置命令:
"scripts": {
    "build": "rimraf lib && rollup -c ./scripts/rollup.config.js" // rollup打包
 },


  1. 执行 yarn build,根目录会构建出一个 lib 文件夹,里面有打包构建的文件,还多了一个 stats.html,这个是可视化并分析 Rollup bundle,用来查看工具模块占用空间:

架构搭建优化

项目搭建到这里,不知机智的你能否发现问题:

  1. 只要添加了一个工具,就要在入口文件导出需要打包构建的工具,在多人开发提交代码的时候将引来冲突的产生:

  1. 使用工具库的时候,按需引用的颗粒度太细了,不能满足一些要求颗粒度粗的朋友,比如:

• 我想使用该包里面 date 相关工具,要这样吗?

import { dateA, dateB, dateC } from "utils-demo"

能不能这样?

import { date } from "utils-demo"
date.dateA()
date.dateB()
date.dateC()


• 在一些使用 script 脚本引入的场景下,就仅仅需要 date 相关的工具,要这样吗?

<script src="https://xxx/main.min.js">

能不能这样?

<script src="https://xxx/date.min.js">

这样仅仅使用 date 里面的工具,就没有必要将所有的工具都引入了

解决方案:

  1. 针对第一个代码冲突的问题,可以根据 src > modules 下目录结构自动生成入口文件 index.ts

自动构建入口文件核心代码:

const fs = require('fs') // node fs模块
const chalk = require('chalk') // 自定义输出样式
const { resolveFile, getEntries } = require('./utils')
let srcIndexContent = `
// tips:此文件是自动生成的,无需手动添加
`
getEntries(resolveFile('src/modules/*')).forEach(({ baseName, entry }) => {
  let moduleIndexContent = `
// tips:此文件是自动生成的,无需手动添加
`
  try {
    // 判断是否文件夹
    const stats = fs.statSync(entry)
    if (stats.isDirectory()) {
      getEntries(`${entry}/*.ts`).forEach(({ baseName }) => {
        baseName = baseName.split('.')[0]
        if (baseName.indexOf('index') === -1) {
          moduleIndexContent += `
export * from './${baseName}'
`
        }
      })
      fs.writeFileSync(`${entry}/index.ts`, moduleIndexContent, 'utf-8')
      srcIndexContent += `
export * from './modules/${baseName}'
export * as ${baseName} from './modules/${baseName}'
`
    } else {
      srcIndexContent += `
export * from './modules/${baseName.split('.')[0]}'
`
    }
  } catch (e) {
    console.error(e)
  }
})
fs.writeFileSync(resolveFile('src/index.ts'), srcIndexContent, 'utf-8')


  1. 针对颗粒度的问题,可以将 modules 下各种类型工具文件夹下面也自动生成入口文件,除了全部导出,再追加 import * as 模块类名称 类型的导出

至此,基本上解决了工具库打包的问题,但是架构中还缺少本地开发调试的环境,下面为大家介绍如何架构中添加本地开发调试的系统。

本地开发调试系统

首先要明确要加入本地开发调试系统的支持,需要做到以下:

•  跨平台(window不支持NODE_ENV=xxx)设置环境变量,根据环境配置不同的 rollup 配置项

•  引入本地开发需要的 html 静态服务器环境,并能做到热更新

  1. 跨平台设置环境变量很简单,使用 cross-env 指定 node 的环境
yarn add cross-env -D
  1. 配置 package.json 命令
 "scripts": {
    "entry": "node ./scripts/build-entry.js",
    "dev": "rimraf lib && yarn entry && cross-env NODE_ENV=development rollup -w -c ./scripts/rollup.config.js", // -w 表示监听的工具模块的修改
    "build": "rimraf lib && yarn entry && cross-env NODE_ENV=production rollup -c ./scripts/rollup.config.js"
  },


  1. 根据最开始架构设计的模块,在项目根目录新建 debugger 文件夹,里面存放的是工具调试的 html 静态页面

  1. 接下来就是配置 scripts > rollup.config.js ,将 NODE_ENV=development 环境加入 rollup 配置,修改生成rollup配置项函数核心代码:
(isProd ? buildType : devType).forEach(({ format, ext }) => {
    let plugins = [...defaultPlugins]
    if (isProd) {
      // 生产环境加入包分析以及代码压缩
      plugins = [...plugins, visualizer({
        gzipSize: true,
        brotliSize: true
      }), terser()]
    } else {
      // 非生产环境加入热更新和本地服务插件,方便本地debugger
      plugins = [...plugins, ...[
        // 热更新
        rollUpLiveLoad({
          watch: ['debugger', 'lib'],
          delay: 300
        }),
        // 本地服务代理
        rollupServe({
          open: true,
          // resolveFile('')代理根目录原因是为了在ts代码里debugger时可以方便看到调试信息
          contentBase: [resolveFile('debugger'), resolveFile('lib'), resolveFile('')]
        })
      ]]
    }
    pushPlugins({ format, plugins, ext })
  })


  1. 执行 yarn dev 之后浏览器会新打开窗口,输入刚添加的工具链接,并且它是热更新的:

工具库文档系统

一个完备的工具库需要有一个文档来展示开发的工具函数,它可能需要具备以下几点支持:

•  支持工具库中方法的可视化预览

•  支持修改工具的时候,具备热更新机制

typedoc(TypeScript 项目的文档生成器)能完美支持 typescript 开发工具库的文档生成器的支持,它的核心原理就是读取源代码,根据工具的注释、ts的类型规范等,自动生成文档页面

关于热更新机制的支持,第一个自然想到 browser-sync(文档系统热更新)

由于文档系统的预览功能有很多插件组合来实现的,可以借助 gulp (基于流的自动化构建工具),typedoc正好有对应的 gulp-typedocGulp 插件来执行 TypeDoc 工具插件

构建完成后打开文档系统,并且它是热更新的,修改工具方法后自动更新文档:

单元测试

为确保用户使用的工具代码的安全性、正确性以及可靠性,工具库的单元测试必不可少。单元测试选用的是 Facebook 出品的 Jest 测试框架,它对于 TypeScript 有很好的支持。

1. 环境搭建

  1. 首先全局安装 jest 使用 init 来初始化 jest 配置项
npm jest -g
jest --init
下面是本人设置的jest的配置项
✔ Would you like to use Jest when running "test" script in "package.json"? … yes
✔ Would you like to use Typescript for the configuration file? … yes
✔ Choose the test environment that will be used for testing › jsdom (browser-like)
✔ Do you want Jest to add coverage reports? … yes
✔ Which provider should be used to instrument code for coverage? › babel
✔ Automatically clear mock calls, instances and results before every test? … yes


执行完之后根目录会自动生成jest.config.ts 文件,里面设置了单元测试的配置规则,package.json 里面也多了一个 script 指令 test。

  1. 关于jest.config.js文件配置项具体含义可以查看官网,要想完成 jest 对于 TypeScript 的测试,还需要安装一些依赖:
yarn add jest ts-jest @babel/preset-typescript @types/jest -D
  1. jest 还需要借助 .babelrc 去解析 TypeScript 文件,再进行测试,编辑 .babelrc 文件,添加依赖包 @babel/preset-typescript:
{
  "presets": [
    "@babel/preset-typescript",
    [
      "@babel/preset-env"
    ]
  ],
  "plugins": ["@babel/plugin-transform-runtime"]
}


2. 单元测试文件的编写

  1. 通过以上环节,jest 单元测试环境基本搭建完毕,接下来在__tests__下编写测试用例

  1. 执行 yarn test

可以看到关于 debounce 防抖工具函数的测试情况显示在了控制台:

•  stmts 是语句覆盖率(statement coverage):是不是每个语句都执行了?

•  Branch 分支覆盖率(branch coverage):是不是每个 if 代码块都执行了?

•  Funcs 函数覆盖率(function coverage):是不是每个函数都调用了?

•  Lines 行覆盖率(line coverage):是不是每一行都执行了?

  1. 同时还会发现项目根目录多了一个 coverage 文件夹,里面就是 jest 生成的测试报告:

3. 单元测试文件的编写引发的思考

每次修改单元测试都要执行 yarn test 去查看测试结果,怎么解决?

jest提供了 watch 指令,只需要配置 scripts 脚本就可以做到,单元测试的热更新。

"scripts": {
  "test": "jest --watchAll"
},


以后会写很多工具的测试用例,每次 test 都将所有工具都进行了测试,能否只测试自己写的工具?

jest 也提供了测试单个文件的方法,这样 jest 只会对防抖函数进行测试(前提全局安装了 jest)。

jest debounce.test.ts --watch

工具库包的发布

至此工具库距离开发者使用仅一步之遥,就是发布到npm上,发包前需要在 package.json 中声明库的一些入口,关键词等信息。

  "main": "lib/main.js", // 告知引用该包模块化方式的默认文件路径
  "module": "lib/main.esm.js", // 告知引用该包模块化方式的文件路径
  "types": "lib/types/index.d.ts", // 告知引用该包的类型声明文件路径
  "sideEffects": false, // false 为了告诉 webpack 我这个 npm 包里的所有文件代码都是没有副作用的
  "files": [ // 开发者引用该包后node_modules包里面的文件
    "lib",
    "README.md"
  ],
  "keywords": [
    "typescript",
    "utils-demo",
    "utils"
  ],
  "scripts": {
    "pub": "npm publish"
  },


登陆npm,你会看到自己的 packages 里面有了刚刚发布的工具库包:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/810603.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

链表(一) 单链表操作详解

文章目录 一、什么是链表二、链表的分类1、单向或者双向2、带头或不带头3、循环或不循环 三、无头单向不循环链表的实现SList.hSList.c动态申请一个节点单链表打印单链表尾插单链表头插单链表的尾删单链表头删单链表查找在pos位置前插入单链表在pos位置之后插入x删除pos位置单链…

自动驾驶下半场的“入场券”

交流群 | 进“传感器群/滑板底盘群/汽车基础软件群/域控制器群”请扫描文末二维码&#xff0c;添加九章小助手&#xff0c;务必备注交流群名称 真实姓名 公司 职位&#xff08;不备注无法通过好友验证&#xff09; 作者 | 张萌宇 自动驾驶战争的上半场拼的是硬件和算法&…

DTC介绍

DTC 一般由3个字节组成&#xff1a; 字节1&#xff1a;High Byte bit 7-6: 对应DTC属于哪一个系统&#xff0c;P: 00动力系统、C: 01底盘、B: 10车身和U: 11通信系统bit 5-4: 用来区分DTC是标准组织所定义还是制造商自定义 00: ISO/SAE01: 制造商10: ISO/SAE11: ISO/SAE bit 3…

【Rust教程 | 基础系列2 | Cargo工具】Cargo介绍及使用

文章目录 前言一&#xff0c;Cargo介绍1&#xff0c;Cargo安装2&#xff0c;创建Rust项目2&#xff0c;编译项目&#xff1a;3&#xff0c;运行项目&#xff1a;4&#xff0c;测试项目&#xff1a;5&#xff0c;更新项目的依赖&#xff1a;6&#xff0c;生成项目的文档&#xf…

python皮卡丘字符打印代码,用python皮卡丘的代码

大家好&#xff0c;本文将围绕python皮卡丘字符打印代码展开说明&#xff0c;python皮卡丘编程代码教程是一个很多人都想弄明白的事情&#xff0c;想搞清楚python皮卡丘编程代码需要先了解以下几个事情。 1、我用python画皮卡丘&#xff0c;没有错误出现&#xff0c;我也打开才…

内网横向移动—NTLM-Relay重放Responder中继攻击LdapEws

内网横向移动—NTLM-Relay重放&Responder中继攻击&Ldap&Ews 1. 前置了解1.1. MSF与CS切换权限1.1.1. CS会话中切换权限1.1.1.1. 查看进程1.1.1.2. 权限权限 1.1.2. MSF会话中切换权限 2. NTLM中继攻击—Relay重放—SMB上线2.1. 案例测试2.1.1. 同账户密码测试2.1.2…

如何使用CRM系统进行客户关系维护管理?

企业要想持续的发展&#xff0c;就必须管理和维护与客户的关系。但如今客户需求更加复杂和多样化&#xff0c;维护客户关系的难度越来越大。许多企业使用CRM系统来帮助自己管理客户关系。通过本文&#xff0c;让您客户关系维护管理全知道。 1、客户画像 CRM系统可以帮助企业建…

【【萌新的stm32学习-1】】

萌新的stm32学习 冯诺依曼结构 采用了分时复用的结构 优点&#xff1a;总线资源占用少 缺点&#xff1a;执行效率低 哈佛结构 执行效率高 总线资源占用多 RISC 这是精简指令集的意思 arm公司 ARMv9是2021年发布的最新 Cortex-A 最好高性能 Cortex-R 中 Cortex-M 低 何为STM…

VScode的简单使用

一、VScode的安装 Visual Studio Code简称VS Code&#xff0c;是一款跨平台的、免费且开源的现代轻量级代码编辑器&#xff0c;支持几乎主流开发语言的语法高亮、智能代码补全、自定义快捷键、括号匹配和颜色区分、代码片段提示、代码对比等特性&#xff0c;也拥有对git的开箱…

Flutter - 微信朋友圈、十字滑动效果(微博/抖音个人中心效果)

demo 地址: https://github.com/iotjin/jh_flutter_demo 代码不定时更新&#xff0c;请前往github查看最新代码 前言 一般APP都有类似微博/抖音个人中心的效果&#xff0c;支持上下拉刷新&#xff0c;并且顶部有个图片可以下拉放大&#xff0c;图片底部是几个tab&#xff0c;可…

使用Docker部署EMQX

原文链接&#xff1a;http://www.ibearzmblog.com/#/technology/info?id9dd5bf4159d07f6a4e69a6b379ce4244 前言 在物联网中&#xff0c;大多通信协议使用的都是MQTT&#xff0c;而EMQX是基于 Erlang/OTP 平台开发的 MQTT 消息服务器&#xff0c;它的优点很多&#xff0c;我…

C语言第十二课---------操作符的介绍与使用(下)

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; &#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382;…

第一章操作系统概述

0.定义 操作系统(Operating System&#xff0c;OS)是指控制和管理整个计算机系统的硬件和软件资源&#xff0c;并合理地组织调度计算机的工作和资源的分配;以提供给用户和其他软件方便的接口和环境;它是计算机系统中最基本的系统软件。 操作系统是系统资源的管理者向上层提供方…

C语言:通讯录(文件操作+动态内存管理) 简易版

目录 前言 一&#xff0c;通讯录菜单 二&#xff0c;通讯录菜单主函数 1.使用枚举&#xff1a; 2.主函数&#xff1a; 三&#xff0c;通讯录功能实现 1.创建通讯录 2.初始化通讯录 3&#xff0c;添加联系人 4&#xff0c;删除联系人 5&#xff0c;搜索联系人 6&…

SpringBoot面试题及答案整理

1、什么是 Spring Boot&#xff1f; 多年来&#xff0c;随着新功能的增加&#xff0c;spring 变得越来越复杂。访问spring官网页面&#xff0c;我们就会看到可以在我们的应用程序中使用的所有 Spring 项目的不同功能。如果必须启动一个新的 Spring 项目&#xff0c;我们必须添…

【MTI 6.S081 Lab】Page tables

【MTI 6.S081 Lab】Page tables Speed up system calls (easy)实验任务Hints哪些其它的系统调用能通过这个共享页面变得更快&#xff0c;请解释。解决方案分配和释放页面初始化结构 实验心得 Print a page table (easy)实验任务Hints根据图3-4从文本中解释vmprint的输出。第0页…

机器学习:自动编码器Auto-encoder

Self-supervised Learning Framework 不用标注数据就能学习的任务&#xff0c;比如Bert之类的。但最早的方法是Auto-encoder。 Outline Auto-encoder encoder输出的向量&#xff0c;被decoder还原的图片&#xff0c;让输出的图片与输入的图片越接近越好。 将原始的高维向量变…

红黑树解密:为什么根节点必须是黑色,两个红色节点不能挨着?

红黑树解密&#xff1a;为什么根节点必须是黑色&#xff0c;两个红色节点不能挨着&#xff1f; 博主简介一、引言1.1、红黑树是什么及其特点1.2、根节点为黑色和红色节点不连续的性质介绍 二、为何根节点必须是黑色&#xff1f;三、为何两个红色节点不能挨着&#xff1f;总结 博…

PCB绘制时踩的坑 - SOT-223封装

SOT-223封装并不是同一的&#xff0c;细分的话可以分为两种常用的封装。尤其是tab脚的属性很容易搞错。如果你想着用tab脚连接有属性的铺铜&#xff0c;来提高散热效率&#xff0c;那么你一定要注意你购买的器件tab脚的属性。 第一种如下图&#xff0c;第1脚为GND&#xff0c;第…

Packet Tracer - 备份配置文件

Packet Tracer - 备份配置文件 目标 第 1 部分&#xff1a;与 TFTP 服务器建立连接 第 2 部分&#xff1a;从 TFTP 服务器传输配置 第 3 部分&#xff1a;将配置和 IOS 备份到 TFTP 服务器上 拓扑图 背景/场景 本练习旨在展示如何从备份恢复配置&#xff0c;然后执行新的…