乐趣区

关于前端工程化:Bit-共享代码

Think in Component

Bit 是组件驱动架构,基于组件的古代利用开发。在 Bit 的世界里,所有皆组件。

组件能够组合成其余组件,最终组成一个利用 APP,即 APP 也是组件的一种。

这为咱们开发提供一个新的思路:咱们构建能够整合成不同利用的组件,而不是构建蕴含组件的利用。

Bit 帮咱们构建模块化、巩固的、可测试、可复用的代码。

Bit Cloud 是组件的云托管服务。它为开发人员和团队提供端到端的解决方案,用于托管、组织、检索、应用、更新和合作解决组件。

Bit 劣势

  • 以组件架构的思维帮忙咱们构建模块化、巩固的、可测试、可复用的代码。
  • 从现有代码构造中拆散组件,无需更改构造,或保护新的我的项目。
  • 可更改依赖组件,并创立本人的版本独立治理,无需担心净化其它环境。

初始化 Bit 工作区

装置 BVM & Bit

BVM 是 Bit 版本管理工具,雷同 NVM

// node 版本 12.22.0 以上
npm i -g @teambit/bvm

执行 bvm - h 测验是否装置胜利,若揭示 bvm 命令不可用,须要设置环境变量:

# MacOs Bash
echo 'export PATH=$HOME/bin:$PATH' >> ~/.bashrc && source ~/.bashrc

# zsh
echo 'export PATH=$HOME/bin:$PATH' >> ~/.zshrc && source ~/.zshrc

# windows
setx path "%path%;%LocalAppData%\.bvm"

装置最新版 bit:

bvm install

执行 bit - h 测验是否装置胜利,若揭示 bit 命令不可用,须要按上述流程设置一下环境变量。

bit new 命令初始化工作区

实用于新建我的项目

$ bit new  <env> <project>
$ cd <project>
$ bit install

bit init 命令初始化工作区

实用于已有我的项目

  1. 先初始化环境
$ cd <project>
$ bit init --harmony
  1. 手动配置开发环境

以 react 环境为例,批改 workspace.jsonc 文件:

"teambit.workspace/variants": {
  "*": {"teambit.react/react": {}
  }
}
  1. 装置必要的 peer 依赖
$ bit install react --type peer
$ bit install react-dom --type peer

初始化 Git

须要将 workspace.jsonc 和.bitmap 上传到 Git。

创立组件

应用内置组件创立

以 react 为例:

  1. 以内置模版创立组件 bit create <built-in-template> <component>
$ bit templates # 查看所有的内置模版
$ bit create react-component ui/button     # TypeScript
$ bit create react-component-js ui/button  # JavaScript

留神:其中,<component> 能够是个门路,前置门路为命名空间,上述示例等同于 bit create react-component button –namespace ui。

  1. 增加测试用例
$ bit install @testing-library/react
  1. 编译并起服务
$ bit compile
$ bit start

自定义组件

  1. 已有组件构造与代码
  2. 通过 bit add <relative-path> –namespace <namespace> 增加组件

查看组件信息

能够查看组件编译环境、蕴含文件、依赖等所有信息。

$ bit show <component-id>

输入信息示例:

  ┌───────────────┬────────────────────────────────────────────────────────────────────┐
  │ id            │ my-scope/ui/button                                                 │
  ├───────────────┼────────────────────────────────────────────────────────────────────┤
  │ scope         │ my-scope                                                           │
  ├───────────────┼────────────────────────────────────────────────────────────────────┤
  │ name          │ ui/button                                                          │
  ├───────────────┼────────────────────────────────────────────────────────────────────┤
  │ env           │ teambit.react/react                                                │
  ├───────────────┼────────────────────────────────────────────────────────────────────┤
  │ package name  │ @my-scope/ui.button                                                │
  ├───────────────┼────────────────────────────────────────────────────────────────────┤
  │ main file     │ index.ts                                                           │
  ├───────────────┼────────────────────────────────────────────────────────────────────┤
  │ files         │ button.composition.tsx                                             │
  │               │ button.docs.mdx                                                    │
  │               │ button.tsx                                                         │
  │               │ button.spec.tsx                                                    │
  │               │ index.ts                                                           │
  ├───────────────┼────────────────────────────────────────────────────────────────────┤
  │ dev files     │ button.docs.mdx (teambit.docs/docs)                                │
  │               │ button.spec.tsx (teambit.defender/tester)                          │
  │               │ button.composition.tsx (teambit.compositions/compositions)         │
  ├───────────────┼────────────────────────────────────────────────────────────────────┤
  │ extensions    │ teambit.react/react                                                │
  │               │ teambit.component/dev-files                                        │
  │               │ teambit.compositions/compositions                                  │
  │               │ teambit.pkg/pkg                                                    │
  │               │ teambit.docs/docs                                                  │
  │               │ teambit.envs/envs                                                  │
  │               │ teambit.dependencies/dependency-resolver                           │
  ├───────────────┼────────────────────────────────────────────────────────────────────┤
  │ dependencies  │ core-js@3.8.3- (package)                                           │
  ├───────────────┼────────────────────────────────────────────────────────────────────┤
  │ dev           │ @testing-library/react@11.2.6- (package)                           │
  │ dependencies  │ @babel/runtime@7.12.18-------- (package)                           │
  │               │ @types/react-router-dom@5.1.7- (package)                           │
  │               │ @types/jest@26.0.20----------- (package)                           │
  │               │ @types/react@16.9.43---------- (package)                           │
  │               │ @types/node@12.20.4----------- (package)                           │
  ├───────────────┼────────────────────────────────────────────────────────────────────┤
  │ peer          │ react@16.13.1----- (package)                                       │
  │ dependencies  │ react-dom@16.13.1- (package)                                       │
  └───────────────┴────────────────────────────────────────────────────────────────────┘

查看组件状态

$ bit status

查看组件所有版本

$ bit log <component-id>

查看本地所有组件列表

$ bit list

启动测试服务器

通过 worker 运行不同的工作区工作,例如测试、linter 和由组件定义的任何工作区工作。

$ bit compile
$ bit start

应用组件

在导入另一个组件作为依赖时,Bit 不 容许应用相对路径导入。因为这会耦合我的项目特定的目录构造,请应用包名代替。

要将组件作为依赖项导入,必须应用模块链接。

Bit 为工作区中的每个组件创立一个模块,这些模块链接在 node_modules 目录中,并蕴含它的构建输入和主动生成的 package.json。

要为组件从新生成模块链接,请运行该 bit link 命令。

将组件装置为 NPM 包

install 命令装置组件,以 NPM 包的模式应用。

作为 Vendor 组件

Bit 工作区获取组件并治理该组件,就如同它是自定义组件一样

通过 import 命令装置组件,示例如下:

$ bit import <component-id>

更新 import 的组件到最新版本

$ bit import

将 Vendor 组件转为 NPM 包依赖

$ bit eject <component-id>

Scope

Scope 是组件的虚拟存储。

Bit 应用 Scope 保留 Bit 组件的版本并依据须要拜访它们。

Remote Scope

托管组件及其版本的 Bit 服务器。

特色

在近程服务器上设置 Scope 以 共享组件,如 Bit.dev 或自托管 Bit 服务器。

将组件存储在 Remote Scope 上,能够使它们在其余我的项目中重复使用。

  • 应用 import 命令从 Remote Scope 获取组件。
  • 应用 export 命令将组件推送到 Remote Scope。

留神:Remote Scope 会缓存组件依赖,例如其余 Scope 的组件。这样做的益处是,即便依赖组件不可用,还能确保以后组件可执行。

应用

在 Bit Server 创立 Remote Scope 后,须要更改 workspace.jsonc 文件:

{
  "teambit.workspace/workspace": {"defaultScope": "<bit-username>.<remote-scope-name>"}
}

workspace.jsonc 文件中的任何更改都须要重新启动本地开发服务器。

$ bit start

Workspace Scope

工作区组件的本地存储。

特色

开发人员的工作区都在本地 Scope 中保留了组件及其历史记录的工作正本。这容许咱们浏览历史记录、比拟版本和查看组件的过来订正。

Workspace Scope 也可能蕴含来自各异 Remote Scope 的组件。

共享组件

  1. 为已批改的组件更新版本号
$ bit tag --all --message "first version"
  1. 共享组件
$ bit export

留神:当共享上传流程完结,.bitmap 文件将更新以反映该新状态。

装置组件

注册 Scope 源

$ npm config set '@YourUserName:registry' https://node.bit.dev

装置依赖

$ npm install @orgName/componentScopeName.componentID

Bit Component vs. NPM 包

Bit 专一于基于组件的工作流,npm 包关注编译后的输入。

  • 生成 NPM 包只是 Bit Component 构建流程的局部,Bit 称之为版本工件。

Configuration

每个组件都必须配置一个环境,好让 Bit 就“晓得”如何构建、测试、lint 和 document 组件。

teambit.workspace/variants 提供一个对立的形式,能够为每个组件设置不同的配置项,而无需批改每个组件文件下的 package.json。

{
  "teambit.workspace/variants": {
    "design/theme": {"defaultScope": "acme.theme",},
    "cart": {
      "defaultScope": "acme.cart",
      "teambit.react/react": {}}
  }
}

查看配置

  • bit env – 打印一个简略的表格,其中蕴含工作区中的所有组件及其环境
  • bit show <component> – 打印组件的所有信息,包含环境
  • bit start- 通过浏览器可视化浏览 组件树 以查看组件的环境

移除组件

移除本地组建

$ bit remove <component-id>

产生的影响:

  • 一个未追踪的组件依赖 删除组件 —— 没有影响

    • 因为 Bit 还没有隔离未追踪的组件,不会检测其依赖
  • 一个已追踪的组件依赖 删除组件 —— 会正告,应用 –force 强制删除
  • 引入的近程组件依赖 删除组件 —— 没有影响

    • 因为近程组件是曾经隔离且不可更改的
    • 本地引入近程组件且更改会创立另一个版本

移除近程组件

$ bit remove <username.your-scope/ui/button> --remote

以一个例子形容产生的影响:

  • button 组件在近程 uiScope 中
  • card 组件依赖 button 组件,也在 uiScope 中
  • login 组件依赖 button 组件,在 adminScope 中

删除 button 组件后的影响:

  • 因为 card 组件与 button 组件在同一个 Scope 中,因而删除 button 组件会有个正告。

    • 可追加 —force 强制删除
    • 删除后,card 组件短少依赖,为保障其失常工作须要重构
  • login 组件没有影响

    • Bit 会在 Scope 中保护依赖
  • 其余我的项目依赖 login 组件时,装置会报错

    • 溯源 button 组件,缺失

编译组件

大多数古代框架都须要一个编译或转译我的项目来将源代码转换为能够在多个浏览器或 Nodejs 中运行的可执行代码。

而 Bit 的编译器是一个环境服务。

编译器的抉择(Babel、TypeScript 等)及其配置由其服务的各种环境决定。

编译器永远不会间接运行,而只能通过 Compiler 服务运行。

单个工作区可能会针对不同的组件运行不同的编译器,每个编译器都依据本人的环境。

$ bit compile <component-id> # 编译特定组件
$ bit compile # 编译工作区全副组件

组件依赖关系图

Bit 的一个要害个性是可能依据组件的源代码主动创立依赖关系图。

Javascript 能够应用 require 或 import 申明依赖两种类型的依赖项:

  • 作为 node_modules 装置的软件包
  • 我的项目外部的文件和目录,或在装璜器中援用(例如在 Angular 中)

node_modules 依赖

Bit 解析包(即 node_modules)的流程:

  • 能够通过 bit show <component-id> 来查看 Bit 为每个包解析的依赖项(Packages):
$ bit show hello/world
┌───────────────────┬─────────────────────────────────────────────────────────────────────┐
│        ID         │                            hello/world                              │
├───────────────────┼─────────────────────────────────────────────────────────────────────┤
│     Language      │                             javascript                              │
├───────────────────┼─────────────────────────────────────────────────────────────────────┤
│     Main File     │                      src/hello-world/index.js                       │
├───────────────────┼─────────────────────────────────────────────────────────────────────┤
│     Packages      │                           left-pad@^2.1.0                           │
├───────────────────┼─────────────────────────────────────────────────────────────────────┤
│       Files       │       src/hello-world/hello-world.js, src/hello-world/index.js      │
└───────────────────┴─────────────────────────────────────────────────────────────────────┘

如果 Bit 无奈解析所有包的依赖项,它会提醒 missing package dependencies。咱们须要验证 package.json 中是否的确存在所有包。

文件依赖

组件能够依赖于其余文件,例如 import ./utils.js。

为了隔离这些依赖其它文件的组件,咱们还须要跟踪该组件依赖的其它文件。这是因为如果咱们想在另一个我的项目中应用这个组件,该组件必须要有它的依赖文件。

留神:Bit 应用动态代码剖析,因而仅反对动态导入 import,不反对 require。

Bit 解析文件依赖的流程

当 Bit 遇到须要跟踪的文件时,它会尝试查看该文件是否曾经在另一个组件中进行了跟踪,在这种状况下,Bit 将使另一个组件成为该组件的依赖项。

如果文件未被跟踪,Bit 将 untracked file dependencies 在查看组件状态时收回正告。

隔离问题

要解决隔离问题,您能够:

  • 将未跟踪的文件依赖项增加到现有组件
  • 将文件作为新组件进行跟踪

采取以上何种办法基于文件的上下文。如果该文件被多个其余组件应用,则将其放入一个独自的组件中是有意义的。

然而,如果此文件仅仅是被跟踪文件的外部文件,则能够将其增加为组件的文件。

文件增加到现有组件

运行 bit add 指向要 增加文件的组件 的 Id:

// 示例
$ bit add src/utils/noop.js --id hello/world

运行 bit status,查看是否胜利:

$ bit status
new components
    > component/hello-world... ok

文件作为新组件进行跟踪

能够 bit add 增加新组件

// 示例
$ bit add src/utils/noop.js --namespace utils

执行后果是一个新组件。

私有化部署 v15

硬件条件

  • Linux/Mac 零碎
  • 内存 4G+

前置条件

  • Docker
  • Git
# 卸载旧版 docker
$ yum remove docker  docker-common docker-selinux docker-engine
# 装置 docker 依赖
$ yum install -y yum-utils device-mapper-persistent-data lvm2
# 设置 docker 源
$ yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
$ yum install docker-ce
# 启动 docker
$ systemctl start docker
# 退出开机启动
$ systemctl enable docker
# 装置 Git
$ yum install git

部署流程

$ git clone https://github.com/teambit/bit.git
$ cd bit/scripts/docker-teambit-bit
$ docker build -f ./Dockerfile-bit -t bitcli/bit:latest .
$ docker build -f ./Dockerfile-bit-server -t bitcli/bit-server:latest .
$ docker run -dit bitcli/bit:latest /bin/bash # 运行
$ docker run -dit -p <port>:3000 bitcli/bit-server:latest
  • Dockerfile-bit:

    • 装置 bvm 而后应用 bvm 装置 bit 的 docker 文件。
    • 这个 docker 通常对在 CI 机器上运行像 tag 和 export 这样的 Bit 命令很有用
  • Dockerfile-bit-server:

    • 一个基于 Dockerfile-bit(应用 from)的 docker 文件
    • 该 docker 文件创建一个空白 Scope,并在其上通过 bit start 初始化 Bit 服务器
  • Dockerfile-symphony:

    • 仅供外部应用

相干问题

Mac 电脑 ssh 链接:Permission denied

$ sudo ssh root@<ip>

ssh 链接时报正告 WARNING: REMOTE HOST IDENTIFICATION HAS CHANGE

$ sudo ssh-keygen -R <ip>

bvm install 装置不了 Bit

长期更改 terminal 代理

$ export http_proxy=http://127.0.0.1:1087
$ export https_proxy=$http_proxy

留神:

  • 须要有 VPN
  • 保障浏览器能够拜访外网
  • 开启 VPN,即便全局,终端也是无奈被代理

永恒批改代理

# 批改~/.bashrc 设置永恒治理脚本
function proxy_on() {
    export http_proxy=http://127.0.0.1:1087
    export https_proxy=$http_proxy
    echo -e "终端代理已开启。"
}

function proxy_off(){
    unset http_proxy https_proxy
    echo -e "终端代理已敞开。"
}

留神:批改后,通过 source ~/.bashrc 立刻失效。

通过 proxy_on 启动代理,proxy_off 敞开代理。

公布

注册近程 Scope

# 客户端
$ cd <my-project>
$ bit init
$ bit remote add http://<host>:<port>

workspace.jsonc

配置 teambit.workspace/workspace

  "teambit.workspace/workspace": {
    /**
     * the name of the component workspace. used for development purposes.
     **/
    "name": "my-workspace-name",
    /**
     * set the icon to be shown on the Bit server.
     **/
    "icon": "https://static.bit.dev/bit-logo.svg",
    /**
     * default directory to place a component during `bit import` and `bit create`.
     * the following placeholders are available:
     * name - component name includes namespace, e.g. 'ui/button'.
     * scopeId - full scope-id includes the owner, e.g. 'teambit.compilation'.
     * scope - scope name only, e.g. 'compilation'.
     * owner - owner name in bit.dev, e.g. 'teambit'.
     **/
    "defaultDirectory": "{scope}/{name}",
    /**
     * default scope for all components in workspace.
     **/
    "defaultScope": "remote-scope"
  },

打 Tag

监听文件变动,能力标识。

若文件无变动,无奈进行标识。

$ bit tag --all  --message "first version"

留神: 独立的组件通过独立的“近程范畴”、近程组件托管造成依赖关系网络。

这种依赖关系网络使更改可能让一个组件的扭转流传到它的所有依赖组件。也就是说,一个组件的更改以级联的形式触发了组件的 CI 及依赖它的组件的 CI。

举例:

组件 B 依赖组件 A,二者初始版本皆为 0.0.1。

若组件 A 由 0.0.1 进行更改,通过 bit tag –all 会 A0.0.1 —> 0.0.2,B0.0.1 —> 0.0.2。

若持续更改组件 B,通过 bit tag –all,B0.0.2 —> 0.0.3,A 的版本不变。

Bit 部署

部署的前提是有新的标识,否则,无奈部署。

$ bit export

扩大 Bit

咱们通过创立 Aspect 和接入 Bit 的 API 来扩大 Bit。

扩大 Workspace UI

以新增 Tab 为例:

初始化 Bit 环境

$ bit init

会主动新建.bit/、.bitmap、workspace.jsonc 文件(夹)。

批改 DefaultScope

{
  ...
  "teambit.workspace/workspace": {
    /**
     * the name of the component workspace. used for development purposes.
     **/
    "name": "my-workspace-name",
    /**
     * set the icon to be shown on the Bit server.
     **/
    "icon": "https://static.bit.dev/bit-logo.svg",
    /**
     * default directory to place a component during `bit import` and `bit create`.
     * the following placeholders are available:
     * name - component name includes namespace, e.g. 'ui/button'.
     * scopeId - full scope-id includes the owner, e.g. 'teambit.compilation'.
     * scope - scope name only, e.g. 'compilation'.
     * owner - owner name in bit.dev, e.g. 'teambit'.
     **/
    "defaultDirectory": "{scope}/{name}",
    /**
     * default scope for all components in workspace.
     **/
    "defaultScope": "me"
  },
  ...
}

新建 Aspect

$ bit create aspect aspects/hello-world

生成目录构造:

.
└──me
  └── aspects
    └── hello-world
      ├── hello-world.aspect.ts
      ├── hello-world.main.runtime.ts
      └── index.ts

其中,hello-world.main.runtime.ts 代码如下:

// hello-world.main.runtime.ts
import {MainRuntime} from '@teambit/cli';
import {HelloWorldAspect} from './hello-world.aspect';

export class HelloWorldMain {static slots = [];
  static dependencies = [];
  static runtime = MainRuntime;
  static async provider() {return new HelloWorldMain();
  }
}

HelloWorldAspect.addRuntime(HelloWorldMain);

留神:hello-world.main.runtime 是负责扩大 workspace CLI 和 workspace Server 的。

为了在组件详情页创立一个新的菜单,咱们须要参考 hello-world.main.runtime.ts 文件新建 hello-world.ui.runtime.tsx文件:

// hello-world.ui.runtime.tsx
import React, {useContext} from 'react';
import {UIRuntime} from '@teambit/ui';
import {ComponentUI, ComponentAspect} from '@teambit/component';
import {HelloWorldAspect} from './hello-world.aspect';

export class HelloWorldUI extends React.Component<any> {static slots = [];
  static dependencies = [ComponentAspect];
  static runtime = UIRuntime;
  static async provider([component]: [ComponentUI]) {return new HelloWorldUI();
  }
}

HelloWorldAspect.addRuntime(HelloWorldUI);

留神:这里引入了 ComponentAspect,它是 Bit 外围 Aspect,负责组建页面所有的组件和操作。将 ComponentAspect 作为依赖,咱们能在 provider 中获取到它并应用它提供的 API。

// 更新 hello-world.ui.runtime.tsx
// 注册 registerNavigation 导航
import React, {useContext} from 'react';
import {UIRuntime} from '@teambit/ui';
import {ComponentUI, ComponentAspect} from '@teambit/component';
import {HelloWorldAspect} from './hello-world.aspect';

export class HelloWorldUI extends React.Component<any> {static slots = [];
  static dependencies = [ComponentAspect];
  static runtime = UIRuntime;
  static async provider([component]: [ComponentUI]) {
     component.registerNavigation({  
       href: '~hello',  
       children: 'Hello'
     });
    return new HelloWorldUI();}
}

HelloWorldAspect.addRuntime(HelloWorldUI);

这里,咱们通过 ComponentAspect 依赖提供的 registerNavigation 注册了导航,手动切换导航会渲染 Hello。

// 更新 hello-world.ui.runtime.tsx
// 注册 registerRoute 路由
import React, {useContext} from 'react';
import {UIRuntime} from '@teambit/ui';
import {ComponentUI, ComponentAspect} from '@teambit/component';
import {HelloWorldAspect} from './hello-world.aspect';

export class HelloWorldUI extends React.Component<any> {static slots = [];
  static dependencies = [ComponentAspect];
  static runtime = UIRuntime;
  static async provider([component]: [ComponentUI]) {
     component.registerRoute({children: () => <div>hello world</div>,      
       path: '~hello'    
     });
     component.registerNavigation({  
       href: '~hello',  
       children: 'Hello'
     });
    return new HelloWorldUI();}
}

HelloWorldAspect.addRuntime(HelloWorldUI);

这里,咱们通过 ComponentAspect 依赖提供的 registerRoute 注册了路由,该路由会承接上述注册的导航,简略的渲染了 hello world。

注册自定义 Aspect

在执行 Aspect 之前,要为其配置解析环境,该环境会将 Aspect 最终转译为浏览器、nodejs 可辨认的代码。

{
  ...
  "teambit.workspace/variants": {"{me/aspects/*}": {"teambit.harmony/aspect":{}
    }
  },
  "me/aspects/hello-world": {}
  ...
}

装置依赖

$ bit install

不装置依赖,bit start 也是失常运行的,只是看不到减少的 UI。

成果展现

运行 bit start 查看成果~

留神:若要更新展现,则要删除.bit/、node_modules、public/,再次执行 bit install 和 bit start。

查看 Aspect 信息

中途可通过 bit show me/aspects/hello-world 查看信息

通过内置模板创立扩大

  • 通过 bit templates 查看内置模板
  • 通过 `bit create <template> <custom-name> [–scope scope-name]
  • 通过 bit install 装置模板相干依赖
  • 通过 bit status 查看自定义扩大状态
  • 若有依赖缺失报错,将缺失依赖增加到:
  "teambit.dependencies/dependency-resolver": {
    /**
     * choose the package manager for Bit to use. you can choose between 'yarn', 'pnpm'
     */
    "packageManager": "teambit.dependencies/pnpm",
    "policy": {"dependencies": {},
      "peerDependencies": {
        "react": "~17.0.2",
        "@testing-library/react": "~12.1.2"
      }
    }
  },

bit install 补充装置依赖。

  • 通过 bit start –dev 测试。
退出移动版