在理论我的项目中,咱们经常会遇到这样的一些场景:比方当初须要做一个下载报表的性能,而且这个性能在很多页面都有,下载的也是同类型的报表,如果在每个页面都去写反复性能的代码显得特地冗余,可不将其封装成一个组件多出调用呢?只不过这个组件跟咱们常见的一些根底组件有些区别,次要区别在于这个组件外头蕴含了业务逻辑,咱们称之为“业务组件”,这类组件是专门为了进步开发效率衍生进去的一种计划,这个组件库可能由专门保护组件库的人来保护,也可能是单个项目组本人的业务组件库。废话不多说,咱们来着手实操一下:
初始化我的项目
应用 lerna 初始化工程,Lerna 是一个优化应用 git 和 npm 治理多包存储库的工作流工具,用于治理具备多个包的 JavaScript 我的项目。
将大型代码库拆分为独立的独立版本包对于代码共享十分有用。然而,在许多存储库中进行更改是麻烦和难以跟踪的事件。为了解决这些(和许多其余)问题,一些我的项目将它们的代码库组织成多包存储库。像 Babel、React、Angular、Ember、Meteor、Jest 等等。
首先应用 npm 将 Lerna 装置到全局环境中:
举荐应用 Lerna 2.x 版本。
npm install --global lerna
接下来,咱们将创立一个新的 git 代码仓库:
git init pony-bre-component && cd pony-bre-component
并与 github 近程仓库关联
get remote add origin xxx
当初,咱们将上述仓库转变为一个 Lerna 仓库:
lerna init
另外,我的项目须要装置 react、react-dom、typescript、@types/react、@types/react-dom
yarn add typescript react react-dom @types/react @types/react-dom
npx typescript --init 在根目录生成 tsconfig.json
你的代码仓库目前应该是如下构造:
pony-business-component/
packages/ 寄存每一个组件
package.json
lerna.json lerna 配置
tsconfig.json typescript 编译配置文件
lerna.json:
{
"packages": ["packages/*"],
"version": "0.0.0"
}
package.json:
{
"name": "root",
"private": true,
"devDependencies": {"lerna": "^4.0.0"},
"dependencies": {
"@types/react": "^17.0.4",
"@types/react-dom": "^17.0.3",
"axios": "^0.21.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"typescript": "^4.2.4"
}
}
创立组件分包
每一个组件都是一个仓库包,比方咱们创立一个 TodoList 业务组件,在 packages 目录下创立 bre-todo-list 文件夹,执行上面命令初始化组件包
cd packages/bre-todo-list
npm init 生成 package.json
npx typescript --init
接着,依照如下构造搭建目录
pony-business-component
├── packages
├── bre-todo-list
├── api 接口 api 定义
└── index.ts
├── interfaces 类型定义
└── index.ts
├── styles 款式
└── index.scss
├── src 组件
├── TodoList.tsx
└── index.ts
├── package.json
└── tsconfig.json
接口定义
TodoList 组件外部须要通过接口获取清单数据,在 src/api/index.ts 定义好接口办法
import axios from 'axios';
export const getTodoList = (id: string) => {return axios.get(`/mock/16430/todolist/${id}`).then(res => res.data)
}
编写组件
编写 TodoList 组件,初始渲染时调用接口获取清单数据,并渲染
// src/TodoList.tsx
import React, {useCallback, useEffect, useState} from 'react';
import {getTodoList} from './api';
interface TodoListProps {id: string;}
const TodoList = (props: TodoListProps) => {const [source, setSource] = useState<string[]>([]);
const init = useCallback(async () => {const { id} = props;
if (id) {const { code , data} = await getTodoList(id);
if (code === 200 && !!data) {setSource(data);
}
}
}, [])
useEffect(() => {init();
}, []);
return (
<ul>
{source.map((s: string, index: number) => <li key={index}>{s}</li>)
}
</ul>
)
}
export {TodoList};
在 src/index.ts 中抛出组件和类型
export * from './TodoList';
export * from './interfaces';
这样,一个业务组件示例就写好了,接下来须要对它进行编译配置
tsc 编译
业务组件属于公司公有组件,只须要满足公司外部应用即可。因而,咱们只采纳 es module 打包格局,不必满足 AMD、CommonJS 应用场景。
lerna 提供了一个命令,能够在每一个分包下执行某些指令
lerna exec tsc 示意在每一个分包在会执行 tsc 指令
在每个分包下执行 tsc 编译时,会优先在分包下找 tsconfig.json 文件,如果没有再向上一级去找,如果都没有找到就应用根目录下的 tsconfig.json 文件
咱们在分包 tsconfig.json 文件做如下配置,并继承根目录下的配置选项:
// 分包 tsconfig.json
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./lib", /* Redirect output structure to the directory. */
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
},
"include": ["src"],
"exclude": ["*/__tests__/*"],
}
根目录 tsconfig.json 配置:
{
"compilerOptions": {"target": "ES2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
"module": "es2015", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
"lib": ["ES2015"], /* Specify library files to be included in the compilation. */
"jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
"declaration": true, /* Generates corresponding '.d.ts' file. */
"declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
"sourceMap": true, /* Generates corresponding '.map' file. */
"downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
"strict": true, /* Enable all strict type-checking options. */
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
"baseUrl": "./", /* Base directory to resolve non-absolute module names. */
"paths": {"bre-*": ["packages/bre-*/src"],
"*": ["node_modules"]
}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
"allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
"experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
"emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
"skipLibCheck": true, /* Skip type checking of declaration files. */
"forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */
},
"include": ["packages/bre-*/src"],
"exclude": [
"node_modules",
"packages/**/node_modules",
"packages/**/lib"
]
}
而后在根目录下 package.json 中加上执行命令
"scripts": {"build": "lerna exec tsc",},
当在根目录下执行 yarn build 时,会对每一个分包的 src 目录编译,编译生成的 JS 脚本、类型申明文件以及 source-map 文件会输入到分包下 lib 文件夹下
执行 yarn build 时可能报错Yarn workspaces need to be defined in the root package.json.
,须要在根目录 package.json 加上
"private": "true", // 加上 workspaces 后 private 选项必须要加上
"workspaces": [ // 工作空间,示意 yarn 命令执行的空间
"packages/*"
],
打包实现后,须要在分包 package.json 中指定抛出的文件
- name:指定组件包的名称
- main:指定组件包的入口文件,比方 AMD 引入
- types:指定组件包类型申明的入口文件
- module:指定 es module 引入时的入口文件
- files:指定装置 pony-bre-todo-list 组件包时会下载哪些文件
{
"name": "bre-todo-list",
"version": "1.0.0",
"description": "工作清单业务组件",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"module": "lib/index.js",
"scripts": {"test": "echo \"Error: no test specified\"&& exit 1"},
"author": "","license":"ISC","dependencies": {"react":"^17.0.2","react-dom":"^17.0.2"},"files": ["lib/*","src/*","styles/*","package.json"]
}
大家是否有个疑难:为什么每一个分包下都须要有一个 tsconfig.json 文件呢?为什么不间接用根目录下的 tsconfig.json 文件呢?
这是因为在根目录 tsconfig.json 无奈配置将每一个分包编译后生成的文件输入到对应分包下的 lib 目录,因而须要在每个分包下量身配置 tsconfig.json
docz 搭建组件文档
装置 docz
yarn add docz
在我的项目中装置 Docz 之后,将三个脚本增加到 package.json 以便运行
"scripts": {
"docz:dev": "docz dev",
"docz:build": "docz build",
"docz:serve": "docz build && docz serve"
},
编写组件文档,在根目录下创立 docs 文件夹
/docs/bre-todo-list.mdx
---
name: bre-todo-list
menu: Components
---
import {Playground, Props} from "docz";
import {TodoList} from "../packages/bre-todo-list/src/index.ts";
import '../packages/bre-todo-list/styles/index.scss';
## 装置
yarn add bre-todo-list
## 援用
import {TodoList} from 'bre-todo-list';
import 'bre-todo-list/styles/index.scss';
## 属性
<Props of={TodoList} isToggle/>
## 根底用法
<Playground>
<TodoList id="123456"></TodoList>
</Playground>
自定义 dcoz 配置,根目录下创立 doczrc.js
export default {
title: 'bre-component', // 网站的题目
typescript: true, // 如果须要在.mdx 文件中引入 Typescript 组件,则应用此选项
dest: 'build-docs', // 指定 docz 构建的输入目录
files: 'docs/*.mdx', // Glob 模式用于查找文件。默认状况下,Docz 会在源文件夹中找到所有扩大名为.mdx 的文件。ignore: ['README.md', 'CHANGELOG.md'] // 用于疏忽由 docz 解析的文件的选项
};
还须要让 Gatsby 在构建 mdx 时反对 scss,装置 node-sass 和 gatsby-plugin-sass,并在根目录下创立 gatsby-config.js,增加如下配置:
module.exports = {
plugins: [
{
resolve: `gatsby-plugin-sass`,
options: {implementation: require("node-sass"),
},
}
],
}
因为组件中应用了接口数据,在动态网站调用时会呈现跨域问题,须要向 gatsby-config.js 中增加跨域配置:
const proxy = require('http-proxy-middleware')
module.exports = {
plugins: [
{
resolve: `gatsby-plugin-sass`,
options: {implementation: require("node-sass"),
},
}
],
developMiddleware: app => {
app.use(proxy(['/mock/16430'], {
target: 'https://mock.yonyoucloud.com/',
changeOrigin: true,
})
)
}
}
在终端执行 yarn docz:dev
公布
lerna publish 命令能够帮忙咱们将分包公布到 npm 上,将其退出到 scripts 中:
"scripts": {
"docz:dev": "docz dev",
"docz:build": "docz build",
"docz:serve": "docz build && docz serve",
"build": "lerna exec tsc",
"publish": "lerna exec tsc && lerna publish"
},
首先,npm login 登录 npm,留神以后设置的 register 肯定要是 npm 镜像,如果不是须要更改
npm config set registry http://registry.npmjs.org
执行 npm run publish 公布,然而报了如下谬误,这是因为本地代码没有提交
将本地仓库与近程仓库关联,让后提交推到近程仓库
git remote add origin git@github.com:Revelation2019/pony-business-component.git
git push --set-upstream origin master
再次执行 npm run publish 公布,胜利公布到 npm 仓库