在理论我的项目中,咱们经常会遇到这样的一些场景:比方当初须要做一个下载报表的性能,而且这个性能在很多页面都有,下载的也是同类型的报表,如果在每个页面都去写反复性能的代码显得特地冗余,可不将其封装成一个组件多出调用呢?只不过这个组件跟咱们常见的一些根底组件有些区别,次要区别在于这个组件外头蕴含了业务逻辑,咱们称之为“业务组件”,这类组件是专门为了进步开发效率衍生进去的一种计划,这个组件库可能由专门保护组件库的人来保护,也可能是单个项目组本人的业务组件库。废话不多说,咱们来着手实操一下:

初始化我的项目

应用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-domnpx 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-listnpm init 生成package.jsonnpx 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.tsximport 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-listmenu: 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.gitgit push --set-upstream origin master

再次执行npm run publish公布,胜利公布到npm仓库