前端组件化实践(bookstory+rollup+lerna+typescript)-react组件

前期准备:

需要有一个github或者gitlab账号的代码仓库,还要有一个npm仓库的账号。这两个账号公网或者私网都可以。


创建项目

新建一个项目,我是在github上创建的测试项目,可以随意发挥:

克隆项目到文件夹,我这里克隆到了D:\webPro。

cmd进入到D:\webPro,执行等等安装完成:

npx create-react-app cookie-test-pkg --template typescript

 搭建 storybook react环境

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 (
    <button
      className="cookie-btn"
      style={{ color: color, backgroundColor: bgcolor }}
    >
      {btnname}
    </button>
  )
}

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<typeof CookieButton>

//storybook 定义的一种args写法,可以在界面生成配置界面
const Template: ComponentStory<typeof CookieButton> = (args) => (
  <CookieButton {...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',
      },
    ],
  });
};

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值