手摸手,打造屬於本身的 React 組件庫03 — 打包篇

第三部分 - 打包篇:項目打包,併發布至 NPMcss

原文連接node

引言

在前面的部分,咱們使用 React 等相關技術構建了庫並對其進行了測試。如今,咱們準備對前面的代碼進行打包,並將其發佈至 NPM,方便其餘人使用。react

教程部分

本篇文章,是這個系列的第三篇::項目打包,併發布至 NPMwebpack

靠譜的(文檔 + 打包)工具 :father

組件庫開發到了這裏,終於也到了最重要的部分,解決(文檔 + 打包)的問題。git

在嘗試了一些打包庫(好比create-react-library),和文檔庫(React Styleguidist)以後,都沒有達到想要的效果。es6

直到B站的一個視頻:利用 umi-library 作組件打包,答案就變得簡單而明顯了,就是利用雲謙大大開源的組件打包利器:umijs/father,來完成最後一步。github

由於目前整個打包工具會把src做爲入口。爲了不前面路由,首頁等代碼被打包進去,這裏對項目結構作出了較大的改動,新增長了 entry 做爲路由的入口,而 src則做爲組件的入口。建議參考下 dantd 中的目錄結構。web

package.json 以及字段詳解

項目初始化以後,接下來,用編輯器打開這個項目,並修改 package.json 中下面屬性:npm

{
  "main": "lib/index.js",
  "module": "es/index.js",
  "typings": "lib/index.d.ts",c
  "files": [
      "dist",
      "lib",
      "es"
  ],
  "scripts": {
    "start": "father doc dev",
    "doc:build": "father doc build",
    "doc:deploy": "father doc deploy",
    "lib:build": "father build"
  },
  "peerDependencies": {
    "react": ">=16.8.0",
    "react-dom": ">=16.8.0",
    "antd": ">=3.21.0"
  },
  "devDependencies": {
    "babel-plugin-import": "^1.13.0",
    "father": "^2.29.2",
    "fs-extra": "^8.1.0",
    "klaw-sync": "^6.0.0"
  },
  "dependencies": {
    "antd": "^3.21.0",
    "classnames": "^2.2.6",
    "lodash": "^4.17.15"
  },
}
複製代碼

添加好以後,運行:npm install 安裝依賴。在等待的同時,讓咱們瞭解一下上述屬性的具體含義:json

  • "main": "lib/index.js": 定義了 npm 包的入口文件,browser 環境和 node 環境都可使用
  • "module": "es/index.js" : 定義 npm 包的 ESM 規範的入口文件,browser 環境和 node 環境都可使用

上面兩個都是程序的入口,當咱們使用打包工具(webpack)進行打包的時候:一、若是它已經支持 pkg.module 字段則會優先使用 ES6 模塊規範的版本 import,這樣能夠啓用 Tree Shaking 機制。二、若是它還不識別 pkg.module 字段則會使用咱們已經編譯成 CommonJS 規範的版本 require('package1'),也不會阻礙打包流程。

  • typings: 包的類型聲明文件
  • files:他描述了軟件包做爲依賴項被安裝時會包含的全部條目,解析詳見官方文檔
  • scripts/"start": "father doc dev":以 docz 的方式開發組件
  • scripts/"doc:build": "father doc build":構建文檔
  • scripts/"doc:deploy": "father doc deploy":發佈文檔
  • scripts/"build": "father build":打包組件
  • peerDependencies:是一種特殊類型的依賴項,只有在發佈本身的程序包時纔會出現。若是寫了這個屬性,意味着使用您在開發軟件的依賴包須要和這個個屬性所設置的依賴徹底相等。這裏由於有些組件是基於 Antd 進行簡單封裝的,因此,在使用這個組件庫的時候,也須要安裝對應版本的 Antd 等依賴。
  • dependencies:這些是您的常規依賴項,或者是運行代碼時所需的依賴項(例如React或ImmutableJS)。
  • devDependencies:這些是您的開發依賴項。在開發工做流中某些時候須要的依賴關係,而在運行代碼時(例如Babel或Flow)則不須要。

father 初體驗:打包第一個組件

這裏,咱們來給:EmptyLine 加上文檔便可。爲了方便閱讀,這裏仍是放上了組件的全部相關代碼。

src/index.tsx
export { default as EmptyLine } from './empty-line';
複製代碼
src/empty-line/index.tsx
import './style/index.less';
import EmptyLine from './EmptyLine';

export default EmptyLine;
複製代碼
src/empty-line/index.mdx
---
name: EmptyLine
route: /empty-line
menu: 組件
---

import { Playground } from 'docz';
import EmptyLine from './index';

## EmptyLine

> 組件名稱:空行(EmptyLine),自定義組件 ,寬度 100%

### 代碼演示

#### 複製信息

<Playground>
    <p>第一行文字</p>
    <EmptyLine />
    <p>第二行文字</p>
</Playground>

## API

|參數|說明|類型|默認值|
|:--|:--|:--|:--|
|height|空行的高度|number?|20|
複製代碼
src/empty-line/EmptyLine.tsx
import React from 'react';
import './style/index.less';

export interface IEmptyLineProps {
  height?: number;
}

const EmptyLine = ({ height = 20 }: IEmptyLineProps) => {
  return <div className="empty-line" style={{ height }} />;
};

export default EmptyLine;
複製代碼
src/empty-line/style/index.less
.empty-line {
    width: 100%;
    height: 20px;
}
複製代碼
.fatherrc.js
export default {
    // cssModules: true, // 默認是 .module.css 走 css modules,.css 不走 css modules。配置 cssModules 爲 true 後,所有 css 文件都走 css modules。(less 文件同理)
    extractCSS: true,
    esm: 'babel',
    cjs: 'babel',
    umd: {
      name: 'dantd',
      sourcemap: true,
      globals: {
        react: 'React',
        antd: 'antd'
      },
    },
    extraBabelPlugins: [
      ['import', { libraryName: 'antd', libraryDirectory: 'es', style: true }],
    ],
    entry: 'src/index.tsx',
    lessInBabelMode: true,
    doc: {
      base: '/dantd/',
      menu: [
        '首頁',
        '組件'
      ]
    },
}
複製代碼

更多配置項,歡迎探索文檔:umijs/father

tsconfig.json
{
    "compilerOptions": {
      "baseUrl": "./src",
      "paths": {
        "antd": ["src/index.tsx"],
        "antd/es/*": ["src/*"]
      },
      "strictNullChecks": true,
      "moduleResolution": "node",
      "esModuleInterop": true,
      "experimentalDecorators": true,
      "jsx": "preserve",
      "noUnusedParameters": true,
      "noUnusedLocals": false,
      "noImplicitAny": true,
      "target": "es6",
      "lib": ["dom", "es2017"],
      "skipLibCheck": true
    },
    "exclude": ["node_modules", "lib", "es"]
}
複製代碼

添加這些文件以後,運行 npm start,就能夠看到下面的界面了。

image

若是想引入 Antd,直接引入就行,上面的配置中,已經增長了:extraBabelPlugins。能夠按需加載 antd。

組件中引入代碼:

import { Card, Typography } from 'antd';
複製代碼

ES6 打包代碼:

import "antd/es/card/style";
import _Card from "antd/es/card";
import "antd/es/typography/style";
import _Typography from "antd/es/typography";
複製代碼

CommonJS 打包代碼:

require("antd/es/card/style");

var _card = _interopRequireDefault(require("antd/es/card"));

require("antd/es/typography/style");

var _typography = _interopRequireDefault(require("antd/es/typography"));
複製代碼

打包代碼,併發布至 NPM

首先,運行 father build 打包代碼。

能夠看到,father 會分別根據:umdcjses 這三種格式進行打包,打包以後會看到多出了下面這些文件。

├── dist
|  ├── empty-line
|  |  ├── EmptyLine.d.ts
|  |  ├── index.d.ts
|  |  └── style
|  |     └── index.d.ts
|  ├── index.d.ts
|  ├── index.umd.css
|  ├── index.umd.js
|  ├── index.umd.js.map
|  ├── index.umd.min.css
|  ├── index.umd.min.js
|  └── index.umd.min.js.map
├── es
|  ├── empty-line
|  |  ├── EmptyLine.js
|  |  ├── index.js
|  |  └── style
|  |     ├── index.css
|  |     └── index.js
|  └── index.js
├── lib
|  ├── empty-line
|  |  ├── EmptyLine.js
|  |  ├── index.js
|  |  └── style
|  |     ├── index.css
|  |     └── index.js
|  └── index.js
複製代碼

此時,能夠看到三種類型的包,已經被成功打出來了。那是否是這個時候就能夠上傳至 npm 了呢?

還不行,對比 Antdnpm 包以後,會發現 eslib 兩個目錄下,尚未類型文件。須要將 dist 目錄下的文件拷貝過來,並把文件中的 .less 改爲 .css。這裏準備寫2個腳本 hack 一下。

安裝依賴:

npm install klaw-sync fs-extra -D
複製代碼

增長2個腳本:

  • scripts/moveDeclare.js
const path = require('path');
const klawSync = require('klaw-sync');
const fs = require('fs'); 

const filesRegex = /.d.ts$/;

const declarePaths = klawSync(path.resolve(__dirname, '../dist'), {
    nodir: true
}).filter(pathItem => filesRegex.test(pathItem.path))

declarePaths.forEach((pathItem) => {
    const esPath = pathItem.path.replace('/dist', '/es');
    const libPath = pathItem.path.replace('/dist', '/lib');
    fs.copyFileSync(pathItem.path, esPath);
    fs.copyFileSync(pathItem.path, libPath);
})

console.log('.d.ts 文件拷貝成功!');
複製代碼
  • scripts/changeLess2Css.js
const path = require('path');
const klawSync = require('klaw-sync');
const fs = require('fs'); 

const filesRegex = /(.js|.d.ts)$/;

const fileFilterFn = item => {
    const basename = path.basename(item.path);
    return filesRegex.test(basename) || basename.indexOf('.') < 0;
}

const esPaths = klawSync(path.resolve(__dirname, '../es'), {
    filter: fileFilterFn,
    nodir: true
}).map(item => item.path)

const libPaths = klawSync(path.resolve(__dirname, '../lib'), {
    filter: fileFilterFn,
    nodir: true
}).map(item => item.path)

const allPaths = esPaths.concat(libPaths);

allPaths.forEach((fileItem) => {
    const fileContent = fs.readFileSync(fileItem, 'utf8');
    const newFileContent = fileContent.replace(/.less/gi, '.css');
    fs.writeFileSync(fileItem, newFileContent, 'utf8');
})

console.log('.less => .css 文件後綴改寫成功!');
複製代碼

修改打包命令:

"build": "father build && node ./scripts/moveDeclare.js && node ./scripts/changeLess2Css.js"
複製代碼

運行:npm run build

此次打包以後的文件,就能夠上傳至 npm 了。

首先登錄 npm

image

使用 git 提交全部代碼,而後修改版本號,併發布代碼:

npm version patch
git push
npm publish
複製代碼

這裏若是包名已經被註冊,或者npm源不對,會報403錯誤,須要自行處理下

到了這裏,咱們的第一個屬於本身的組件庫就被上傳至 npm 了,咱們可使用:npm install dantd 下載咱們的安裝包,並在項目中使用裏面的組件了。

image

打包文檔,併發布至 GitHub Pages

首先,在 package.json 文件中,添加 git 地址,便於以後文檔的發佈:

"repository": {
    "type": "git",
    "url": "https://github.com/jokingzhang/dantd"
},
複製代碼

運行下面命令打包文檔:

npm run doc:build
複製代碼

運行下面命令發佈文檔:

npm run doc:deploy
複製代碼

而後訪問對應的地址就能夠看到咱們發佈到線上的組件文檔了:dantd

image

結束語

屬於咱們本身的第一個組件庫,就這樣被髮布到 npm 了。🎉🎉🎉 可是,這個組件庫須要寫哪些組件進去,是咱們接下來須要考慮的。

若是您喜歡這個系列,歡迎評論,分享文章連接。此外,也歡迎多多吐槽,🙏 這些反饋對我來講是很是寶貴的,以便我未來寫出更優秀的文章。

相關文章
相關標籤/搜索