前期准备:
需要有一个github或者gitlab账号的代码仓库,还要有一个npm仓库的账号。这两个账号公网或者私网都可以。
npx create-react-app cookie-test-pkg --template typescript
npx -p @storybook/cli sb init --type react
项目中会多出.storybook和src/stories目录
执行
npm run storybook
会启动storybook界面,如下图所示:
使用Lerna来管理包
全局安装lerna
npm install lerna -g
lerna init
创建一个包,我这里定义一个叫cookieButton,会发现packages多了一个目录
lerna create cookieButton
为了支持typescript,还需要单独安装相关插件依赖
npm install -D @storybook/preset-create-react-app @storybook/react
将lib重命名为src,因为之后打包想使用lib目录名,故此修改(不改也是可以的)
根据目录增加以下文件
文件代码,自取:
//src/CookieButtuon.tsx
import React from 'react'
import './CookieButton.scss'
export interface ButtonProp {
bgcolor: String
color: String
name: String
}
function CookieButton({ bgcolor, color, btnname }) {
return (
)
}
export default CookieButton
// src/CookieButton.scss
.cookie-btn {
position: relative;
display: block;
box-sizing: border-box;
text-align: center;
text-decoration: none;
line-height: 1.35;
-webkit-tap-highlight-color: transparent;
overflow: hidden;
}
//src/index.tsx
import CookieButton, { ButtonProp } from './CookieButton'
export { CookieButton, ButtonProp }
// src/index.stories.tsx
import React from 'react'
import { ComponentStory, ComponentMeta } from '@storybook/react'
import { CookieButton } from './index'
export default {
title: 'CookieButton',
component: CookieButton,
} as ComponentMeta
//storybook 定义的一种args写法,可以在界面生成配置界面
const Template: ComponentStory = (args) => (
)
//示例一
export const defaultButton = Template.bind({})
defaultButton.args = {
bgcolor: '#f0f0f0',
color: '#000000',
btnname: '按钮',
}
//示例二
export const BlackButton = Template.bind({})
BlackButton.args = {
bgcolor: '#333333',
color: '#ffffff',
btnname: '按钮',
}
由于我们的项目用tsx编写,需要增加支出ts的插件
npm install -D @storybook/preset-scss css-loader sass sass-loader style-loader
修改 .storybook/main.js
再次启动项目,能启动起来,表示正常
打包我这边使用rollup,后续会列一下它和webpack的区别。
安装rollup以及相关依赖
npm install -D rollup rollup-plugin-babel rollup-plugin-commonjs rollup-plugin-node-resolve rollup-plugin-replace @rollup/plugin-image rollup-plugin-terser rollup-plugin-postcss postcss cssnano postcss-nested postcss-simple-vars
安装好rollup后还需要配置babel,也需要安装babel配置需要的相关插件依赖
npm install -D @babel/cli @babel/core @babel/plugin-external-helpers @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators @babel/plugin-proposal-object-rest-spread @babel/plugin-transform-react-jsx @babel/plugin-transform-runtime @babel/preset-env @babel/preset-react @babel/preset-typescript
安装cross-env等依赖
npm install -D cross-env
rollup-plugin-typescript2 @rollup/plugin-json rollup-plugin-dts
//rollup.config.js
// Rollup plugins
// babel插件用于处理es6代码的转换,使转换出来的代码可以用于不支持es6的环境使用
import babel from 'rollup-plugin-babel'
// resolve将我们编写的源码与依赖的第三方库进行合并
import resolve from 'rollup-plugin-node-resolve'
// 解决rollup.js无法识别CommonJS模块
import commonjs from 'rollup-plugin-commonjs'
// 全局替换变量比如process.env
import replace from 'rollup-plugin-replace'
// 使rollup可以使用postCss处理样式文件scss、css等
import postcss from 'rollup-plugin-postcss'
import autoprefixer from 'autoprefixer'
// 可以处理组件中import图片的方式,将图片转换成base64格式,但会增加打包体积,适用于小图标
import image from '@rollup/plugin-image'
// 压缩打包代码(这里弃用因为该插件不能识别es的语法,所以采用terser替代)
// import { uglify } from 'rollup-plugin-uglify';
// 压缩打包代码
import { terser } from 'rollup-plugin-terser'
// PostCSS plugins 处理css定义的变量
import simplevars from 'postcss-simple-vars'
// 处理scss嵌套样式写法
import nested from 'postcss-nested'
// 替代cssnext
import postcssPresetEnv from 'postcss-preset-env'
// css代码压缩
import cssnano from 'cssnano'
// 支持typescript
import typescript from 'rollup-plugin-typescript2'
// 支持rollup.js导入json模块
import json from '@rollup/plugin-json'
// 用于打包生成*.d.ts文件
import dts from 'rollup-plugin-dts'
// 引入package
// import pkg from "./package.json";
const env = process.env.NODE_ENV
const PACKAGE_ROOT_PATH = process.cwd()
const config = [
{
// 入口文件我这里在components下统一导出所有自定义的组件
input: `${PACKAGE_ROOT_PATH}/src/index.tsx`,
// 输出文件夹,可以是个数组输出不同格式(umd,cjs,esm...)通过env是否是生产环境打包来决定文件命名是否是.min
output: [
{
file: `${PACKAGE_ROOT_PATH}/dist/index.esm.js`,
format: 'esm',
},
{
file: `${PACKAGE_ROOT_PATH}/dist/index.js`,
format: 'cjs',
},
],
// 注入全局变量比如jQuery的$这里只是尝试 并未启用
// globals: {
// react: 'React', // 这跟external 是配套使用的,指明global.React即是外部依赖react
// antd: 'antd'
// },
// 自定义警告事件,这里由于会报THIS_IS_UNDEFINED警告,这里手动过滤掉
onwarn: function (warning) {
if (warning.code === 'THIS_IS_UNDEFINED') {
return
}
},
// 将模块视为外部模块,不会打包在库中
external: ['react', 'react-dom'],
// 插件
plugins: [
typescript(),
image(),
postcss({
plugins: [
simplevars(),
nested(),
postcssPresetEnv(),
autoprefixer(),
cssnano(),
],
// 处理.css和.scss文件
extensions: ['.css', '.scss'],
}),
// 告诉 Rollup 如何查找外部模块并安装它
resolve(),
// babel处理不包含node_modules文件的所有js,ts,tsx
babel({
exclude: 'node_modules/**',
runtimeHelpers: true,
plugins: ['@babel/plugin-external-helpers'],
extensions: ['.js', '.ts', 'tsx'],
}),
// 将 CommonJS 转换成 ES2015 模块
// 这里有些引入使用某个库的api但报未导出该api通过namedExports来手动导出
commonjs({
namedExports: {
'node_modules/react-is/index.js': ['isFragment'],
'node_modules/react/index.js': [
'Fragment',
'cloneElement',
'isValidElement',
'Children',
'createContext',
'Component',
'useRef',
'useImperativeHandle',
'forwardRef',
'useState',
'useEffect',
'useMemo',
],
'node_modules/react-dom/index.js': [
'render',
'unmountComponentAtNode',
'findDOMNode',
],
'node_modules/gojs/release/go.js': [
'Diagram',
'GraphLinksModel',
'Overview',
'Spot',
],
},
}),
json(),
// 全局替换NODE_ENV,exclude表示不包含某些文件夹下的文件
replace({
// exclude: 'node_modules/**',
'process.env.NODE_ENV': JSON.stringify(env || 'development'),
}),
// 生产环境执行terser压缩代码
env === 'production' && terser(),
],
},
{
// 打包*.d.ts配置,用于支持typescript项目
input: `${PACKAGE_ROOT_PATH}/src/index.tsx`,
output: [
{
file: `${PACKAGE_ROOT_PATH}/dist/index.d.ts`,
format: 'esm',
},
],
external: [/\.scss$/, /\.css$/],
plugins: [dts()],
},
]
export default config
修改tsconfig.json
修改.gitignore,将package下打包目录忽略
node_modules
.DS_Store
lerna-debug.log
yarn-error.log
packages/*/dist
packages/*/lib
storybook-static
给每个包的package.json下增加scripts:
"scripts": {
"build:sit": "cross-env BABEL_ENV=rollup rollup -c ../../rollup.config.js",
"build": "cross-env NODE_ENV=production rollup -c ../../rollup.config.js"
},
跟目录的package.json的scripts下增加
"build": "lerna run build",
执行打包命令
npm run build //实际会到每个package下执行build
将代码提交到github上
git add .
git commit -m XXX
git push
发包到仓库
lerna publish
登录仓库看到成功发包啦
在其他项目中使用:
npm install cookiebutton
如果是私有仓库,记得修改仓库地址哦。
!!!注意事项!!!
如果package.json没有配置发布仓库,可以手动切换
npm config set registry=https://registry.npmjs.org/
例如第一次切换公网后,进行下初始化账号
git login //输入账号、秘密、邮箱、邮箱收到的验证码
每次发包,git记录一定要有变化哦,而且发布的的版本号要递增,否则是发布不上的。
相关文档推荐:
Storybook官网
Lerna官网
Rollup官网
一些补充:
1、关于 webpack和rollup打包的优劣
2、增加eslint和prettier:用于规范代码
代码检查借助Prettier以及ESLint的扩展,eslint-config-prettier将关闭所有不必要的或可能与Prettier冲突的规则。eslint-plugin-prettier则是添加Prettier格式设置规则的插件。yarn add -D eslint prettier eslint-config-prettier eslint-plugin-prettier eslint-plugin-react
3、增加plop:用于组件模板创建(在上面项目中我没有用到,是用lerna create创建的)
yarn add -D plop
详细配置可以查看官网Plop
package.json中增加相应scripts
“plop”: "plop",
创建plopfile.js,并创建模板文件(模板文件和要生成的文件路由和名字都可以自定义)
module.exports = function (plop) { // create your generators here plop.setGenerator('basics', { //basics是生成的文件夹名 description: 'this is a component', prompts: [ // 提示问题,可以是多问题,以数组的形式表示 { // 可以根据输入的name动态创建组件的相关文件 type: 'input', name: 'name', message: 'please input component name', }, ], actions: [ // 命令完成对应的操作,可以是多操作,以数组的形式表示 { type: 'add', // 创建文件 path: 'src/components/{{name}}/index.tsx', // 生成的文件 templateFile: 'templates/index.tsx', // 模板文件 }, { type: 'add', path: 'src/components/{{name}}/index.less', templateFile: 'templates/index.less', }, { type: 'add', path: 'src/stories/{{name}}.stories.tsx', templateFile: 'templates/index.stories.tsx', }, ], }); };