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命令初始化工作区
- 先初始化环境
$ cd <project>
$ bit init --harmony
- 手动配置开发环境
以react环境为例,修改workspace.jsonc文件:
"teambit.workspace/variants": {
"*": {
"teambit.react/react": { }
}
}
- 安装必要的peer依赖
$ bit install react --type peer
$ bit install react-dom --type peer
初始化Git
需要将workspace.jsonc和.bitmap 上传到Git。
创建组件
使用内置组件创建
以react为例:
- 以内置模版创建组件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。
- 添加测试用例
$ bit install @testing-library/react
- 编译并起服务
$ bit compile
$ bit start
自定义组件
- 已有组件结构与代码
- 通过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 │ [email protected] (package) │
├───────────────┼────────────────────────────────────────────────────────────────────┤
│ dev │ @testing-library/[email protected] (package) │
│ dependencies │ @babel/[email protected] (package) │
│ │ @types/[email protected] (package) │
│ │ @types/[email protected] (package) │
│ │ @types/[email protected] (package) │
│ │ @types/[email protected] (package) │
├───────────────┼────────────────────────────────────────────────────────────────────┤
│ peer │ [email protected] (package) │
│ dependencies │ [email protected] (package) │
└───────────────┴────────────────────────────────────────────────────────────────────┘
查看组件状态
$ bit status
查看组件所有版本
$ bit log <component-id>
查看本地所有组件列表
$ bit list
启动测试服务器
$ bit compile
$ bit start
使用组件
要将组件作为依赖项导入,必须使用模块链接。
Bit 为工作区中的每个组件创建一个模块,这些模块链接在 node_modules 目录中,并包含它的构建输出和自动生成的 package.json。
要为组件重新生成模块链接,请运行该bit link命令。
将组件安装为NPM包
install命令安装组件,以NPM包的形式使用。
作为Vendor组件
通过import命令安装组件,示例如下:
$ bit import <component-id>
更新import的组件到最新版本
$ bit import
将Vendor组件转为NPM包依赖
$ bit eject <component-id>
Scope
Remote Scope
特色
在远程服务器上设置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的组件。
共享组件
- 为已修改的组件更新版本号
$ bit tag --all --message "first version"
- 共享组件
$ bit export
注意:当共享上传流程结束,.bitmap文件将更新以反映该新状态。
安装组件
注册Scope源
$ npm config set '@YourUserName:registry' https://node.bit.dev
安装依赖
$ npm install @orgName/componentScopeName.componentID
Bit Component vs. NPM包
- 生成NPM包只是Bit Component构建流程的部分,Bit称之为版本工件。
Configuration
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组件,缺失
编译组件
而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 中是否确实存在所有包。
文件依赖
注意: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"
举例:
组件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测试。