组件库日益流行,尤其是在拥有多个产品和团队的组织中。组织正在专门设立团队来维护组件库。这里的最终目标可能是一个设计系统,我们的原则和实践经过深思熟虑。但是,一个好的设计系统需要数月甚至数年的研究和一个专门的团队,这是许多组织无法承受的。Google 的Material design和 Atlassian 的Design system 还有蚂蚁金服的ant-design是我想到的一些优秀的。对于大多数团队来说,一个好的起点是组件库。一组常用组件,有助于在应用程序之间实现一致性。我们可以从简单的组件开始,例如
button
,inputs
,modal
并在此过程中添加更多内容。下面我们会使用 React、Typescript 和 Rollup 从头构建一个简单的组件库,并在此过程中学习rollup和storybook相关的知识。
初始化项目
mkdir rollup-react-lib // 创建项目文件夹
cd rollup-react-lib
npm init // 初始化项目
npm i -D react react-dom typescript @types/react // 添加依赖项
npx tsc --init // 添加tsconfig.json
现在StoryBook还不支持react18,所以可以携带版本号,避免装到最新的react,导致启动问题。
npm i -D react@^17.0.2 react-dom@^17.0.2 typescript @types/react
// 添加17的依赖依赖项
我们所有的包裹都将列在devDependencies. 此外,将使用该库的应用程序将附带 react,我们不必捆绑 react。因此,我们将添加react为peerDependency,我们package.json现在的样子。
{
"name": "rollup-react-lib",
"version": "1.0.0",
"description": "react component lib",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"types": "dist/esm/index.d.ts",
"scripts": {
"build": "rollup -c"
},
"author": "",
"license": "MIT",
"devDependencies": {
"@rollup/plugin-commonjs": "^21.0.3",
"@rollup/plugin-node-resolve": "^13.1.3",
"@rollup/plugin-typescript": "^8.3.1",
"@types/react": "^17.0.43",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"rollup": "^2.70.1",
"rollup-plugin-dts": "^4.2.0",
"rollup-plugin-peer-deps-external": "^2.2.4",
"rollup-plugin-postcss": "^4.0.2",
"rollup-plugin-terser": "^7.0.2",
"typescript": "^4.6.3"
},
"peerDependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"dependencies": {}
}
tsconfig.json如下:
{
"compilerOptions": {
"strict": true,
"strictPropertyInitialization": false,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": false,
"outDir": "dist",
"module": "esnext",
"target": "ES5",
"lib": ["es2019", "DOM"],
"declaration": true,
"declarationDir": "dist/esm/types",
"emitDeclarationOnly": false,
"jsx": "react",
"allowJs": false,
"checkJs": false,
"moduleResolution": "node",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
},
"include": ["src/components/**/*.tsx", "src/components/**/*.ts"],
"exclude": ["node_modules", "src/components/**/*.spec.(ts|tsx)", "dist"]
}
src目录如下:
如果我们运行
npm run build
,我们应该会看到一个dist
文件夹,其中包含我们所有的 ts 文件被转译成 js 文件。如果您注意到,其中没有 css 文件。让我们使用Rollup来实现。
安装rollup及一些插件
@rollup/plugin-node-resolve
- 解决第三方依赖node_modules
@rollup/plugin-commonjs
- 打包成commonjs
格式rollup-plugin-typescript2
在 JS 中转译我们的 Typescript 代码rollup-plugin-peer-deps-external
- 防止打包peerDependencies
rollup-plugin-postcss
- 处理我们的 CSSrollup-plugin-terser
- 缩小我们的包-
rollup-plugin-dts - 它获取我们所有的.d.ts文件并输出一个单一类型的文件
npm i -D rollup
npm i -D @rollup/plugin-node-resolve @rollup/plugin-commonjs rollup-plugin-typescript2 rollup-plugin-peer-deps-external rollup-plugin-postcss rollup-plugin-terser rollup-plugin-dts
根目录添加rollup.config.js
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from 'rollup-plugin-typescript2';
import { terser } from 'rollup-plugin-terser';
import external from 'rollup-plugin-peer-deps-external';
import postcss from 'rollup-plugin-postcss';
import dts from 'rollup-plugin-dts';
const packageJson = require('./package.json');
export default [{
input: 'src/index.ts',
output: [
{
file: packageJson.main,
format: 'cjs',
sourcemap: true,
name: 'rollup-react-lib'
},
{
file: packageJson.module,
format: 'esm',
sourcemap: true
}
],
plugins: [
external(),
resolve(),
commonjs(),
typescript({ useTsconfigDeclarationDir: true }),
postcss(),
terser()
]
}, {
input: 'dist/esm/types/index.d.ts',
output: [{ file: 'dist/esm/index.d.ts', format: "esm" }],
external: [/\.css$/],
plugins: [dts()],
}]
完善我们的package.json
{
"name": "rollup-react-lib",
"version": "1.0.0",
"description": "react component lib",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"types": "dist/esm/index.d.ts",
"scripts": {
"build": "rollup -c"
},
"author": "Joseph",
"license": "MIT",
"devDependencies": {
"@rollup/plugin-commonjs": "^21.0.3",
"@rollup/plugin-node-resolve": "^13.1.3",
"@types/react": "^17.0.43",
"postcss": "^8.4.12",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"rollup": "^2.70.1",
"rollup-plugin-dts": "^4.2.0",
"rollup-plugin-peer-deps-external": "^2.2.4",
"rollup-plugin-postcss": "^4.0.2",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.31.2",
"typescript": "^4.6.3"
},
"peerDependencies": {
"react": "^17.0.2"
},
"dependencies": {}
}
安装rimraf
npm i -D rimraf
可以帮我们在打包前删除文件夹
安装babel
npm i -D @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript
创建.babelrc
{
"presets": [
"@babel/preset-env",
"@babel/preset-typescript",
["@babel/preset-react", { "runtime": "automatic" }]
]
}
安装eslint和prettier
npm i -D eslint
npx eslint --init
vscode
中的settings.json
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
如何我们使用VScode开发,加上上面的配置,可以保存时自动格式化。
Prettier强化:
npm i prettier -D
配置.prettierrc
{
"useTabs": false,
"tabWidth": 2,
"printWidth": 100,
"singleQuote": true,
"trailingComma": "none",
"bracketSpacing": true,
"semi": false
}
// 常用配置相关解释
printWidth: 100, // 超过最大值换行
tabWidth: 4, // 缩进字节数
useTabs: false, // 缩进不使用tab,使用空格
semi: true, // 句尾添加分号
singleQuote: true, // 使用单引号代替双引号
proseWrap: "preserve", // 默认值。因为使用了一些折行敏感型的渲染器(如GitHub comment)而按照markdown文本样式进行折行
arrowParens: "avoid", // (x) => {} 箭头函数参数只有一个时是否要有小括号。avoid:省略括号
bracketSpacing: true, // 在对象,数组括号与文字之间加空格 "{ foo: bar }"
disableLanguages: ["vue"], // 不格式化vue文件,vue文件的格式化单独设置
endOfLine: "auto", // 结尾是 \n \r \n\r auto
eslintIntegration: false, //不让prettier使用eslint的代码格式进行校验
htmlWhitespaceSensitivity: "ignore",
ignorePath: ".prettierignore", // 不使用prettier格式化的文件填写在项目的.prettierignore文件中
jsxBracketSameLine: false, // 在jsx中把'>' 是否单独放一行
jsxSingleQuote: false, // 在jsx中使用单引号代替双引号
parser: "babylon", // 格式化的解析器,默认是babylon
requireConfig: false, // Require a 'prettierconfig' to format prettier
stylelintIntegration: false, //不让prettier使用stylelint的代码格式进行校验
trailingComma: "es5", // 在对象或数组最后一个元素后面是否加逗号(在ES5中加尾逗号)
tslintIntegration: false // 不让prettier使用tslint的代码格式进行校验
解决ESlint与Prettier的冲突
npm i eslint-config-prettier -D
.eslintrc.js
module.exports = {
env: {
commonjs: true,
es2021: true,
node: true
},
extends: ['airbnb-base', 'prettier'], // 覆盖eslint格式配置,写在最后
parserOptions: {
ecmaVersion: 13
},
rules: {}
}
安装husky
npm i -D husky
// 给script加个prepare:
// prepare: "husky install"
npm run prepare
npx husky add .husky/pre-commit "npm run prettier-format && npm run lint"
git add .husky/pre-commit
最新版会在根目录生成.husky文件夹,.git里面的pre-commit钩子,回触发此文件夹里面的pre-commit的shell,然后进行lint修复。最终的package.json参考最终版(下面)。
安装StoryBook
npx sb init
会在根目录创建.storybook文件夹,会在src底下创建stories文件夹,我们删除stories底下的文件,就可以写自己的stories了。
具体参考:https://storybook.js.org/docs/react/get-started/introduction
修改一下package.json里面的scripts
"scripts": {
"build": "rimraf build/* && rollup -c",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook",
"prepare": "husky install",
"lint": "npx eslint src/**/*.{ts,tsx}",
"lint-fix": "npx eslint src/**/*.{ts,tsx} --fix",
"prettier-format": "prettier --write src/components/**/*.{ts,tsx}"
},
完整package.json
{
"name": "rollup-react-lib",
"version": "1.0.0",
"description": "react component lib",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"types": "dist/esm/index.d.ts",
"scripts": {
"build": "rimraf build/* && rollup -c",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook",
"prepare": "husky install",
"lint": "npx eslint src/**/*.{ts,tsx}",
"lint-fix": "npx eslint src/**/*.{ts,tsx} --fix",
"prettier-format": "prettier --write src/components/**/*.{ts,tsx,less}"
},
"author": "Joseph",
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.17.8",
"@babel/preset-env": "^7.16.11",
"@babel/preset-react": "^7.16.7",
"@babel/preset-typescript": "^7.16.7",
"@rollup/plugin-commonjs": "^21.0.3",
"@rollup/plugin-image": "^2.1.1",
"@rollup/plugin-node-resolve": "^13.1.3",
"@storybook/addon-actions": "^6.4.19",
"@storybook/addon-essentials": "^6.4.19",
"@storybook/addon-interactions": "^6.4.19",
"@storybook/addon-links": "^6.4.19",
"@storybook/react": "^6.4.19",
"@storybook/testing-library": "0.0.9",
"@testing-library/jest-dom": "^5.16.3",
"@testing-library/react": "^12.1.4",
"@testing-library/user-event": "^14.0.0",
"@types/lodash": "^4.14.181",
"@types/react": "^17.0.43",
"@typescript-eslint/eslint-plugin": "^5.17.0",
"@typescript-eslint/parser": "^5.17.0",
"babel-loader": "^8.2.4",
"babel-preset-react-app": "^10.0.1",
"eslint": "^8.12.0",
"eslint-config-google": "^0.14.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.29.4",
"husky": "^7.0.4",
"postcss": "^8.4.12",
"prettier": "^2.5.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"rimraf": "^3.0.2",
"rollup": "^2.70.1",
"rollup-plugin-babel": "^4.4.0",
"rollup-plugin-dts": "^4.2.0",
"rollup-plugin-peer-deps-external": "^2.2.4",
"rollup-plugin-postcss": "^4.0.2",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.31.2",
"typescript": "^4.6.3"
},
"peerDependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2",
"lodash": "^4.17.21"
}
}
项目结构图:
StoryBook运行:
gitee地址:https://gitee.com/cary123/rollup-react-lib
上面只是基本的一些配置,如果需要sass,less,svg等还需要额外配置。还有很多内容值得我们学习。
发表评论
所有评论(1)
多谢分享,nice~