引言

在前端项目开发过程中,有大量重复的内容,比如布局相似的模块,较多的功能表单等,我们可以提炼成组件来提升效率,减少重复建设。文章以实际工作中的项目为例,介绍如何将项目中常用的组件进行封装并发布到npm中。

1 前提要求

在开始前你需要具备以下条件:安装了Node & npm

安装了Git

基本掌握npm ,git使用方法

熟练使用 JavaScript & ES6 & CSS

基本掌握React

熟悉React ,antd

2 开始

2.1 配置git和npm

本次将我的组件库命名为nsc-components

首先配置git ,在github上创建一个新的远程仓库nsc-components,在本地运行git clone 命令,将nsc-components克隆到本地

$ git clone https://github.com/你的账户名/nsc-components

配置 npm,运行npm login,登录你的npm账号。然后运行npm init 初始化生成一个新的package.json文件.

$ npm init

name: (nsc-components)

version: (1.0.0) 0.1.0

description: an example component library with React!

entry point: (index.js)

test command:

git repository:

keywords:

Author:

license: (ISC)

About to write to /Users/alanbsmith/personal-projects/trash/package.json:

{

"name": "nsc-components",

"version": "0.1.0",

"description": "an example component library with React!",

"main": "dist/index.js",

"scripts": {

"test": "echo \"Error: no test specified\" && exit 1"

},

"author": ,

"license": "ISC"

}

Is this ok? (yes)

在根目录下添加以下配置文件

touch .babelrc .eslintrc .gitignore .npmignore README.md.babelrc包含编译阶段一些有用的转转码规则(presets)

.eslintrc包含linter配置

.gitignore和.npmignore分别用于忽略来自 git 和 npm 的文件

README.md也非常重要。这是我们和开源社区交流的主要方式

.babelrc

{

"presets": ["@babel/preset-env", "@babel/preset-react"],

"plugins": [

"@babel/plugin-proposal-class-properties",

"@babel/plugin-proposal-object-rest-spread",

["import", {"libraryName": "antd", "libraryDirectory": "lib", "style":"css"

}],

]

}

注:这里踩了很多坑

加入["import", {"libraryName": "antd", "libraryDirectory": "lib", "style": true }]配置是必要的,该配置使用babel-plugin-import 按需加载 antd样式,不加该配置信息antd组建的样式会无法显示。

babel-plugin-import 在 babel 运行时,将类似import { ModuleName } from 'libiaryName'的代码转化为组件所在的路径,这样实际引用的就是这个组件的模块而不是整个 Library

// babel 前

import { Icon } from 'antd';

// babel 后

import _Icon from 'antd/lib/icon';

import 'antd/lib/dropdown/style/css';

.gitignore 添加不需要push的文件

.DS_Store

dist

node_modules

*.log

.npmignore用于忽略不用发布到npm的文件

.babelrc

src

CODE_OF_CONDUCT.md

node_modules

.gitignore

webpack.config.js

yarn.lock

.eslintrc

在初始化的package.json文件里添加以下命令

"scripts": {

"test": "echo \"Error: no test specified\" && exit 1",

"build": "webpack --config webpack.config.js -p",

"prepublish": "npm run build"

},

"files": [

"dist"

],build将运行根目录下webpack.config.js文件对src目录下的内容如何进行转码然后导出到dist目录。需要在webpack.config.js文件设置入口src/index.js。

npm会在我们运行npm publish之前执行这个脚本。 这将确保我们在dist的资源是最新的版本。

"files"属性的值是一个数组,内容是模块下文件名或者文件夹名,也可以在模块根目录下创建一个".npmignore"文件(windows下无法直接创建以"."开头的文件,使用linux命令行工具创建如git bash),写在这个文件里边的文件即便被写在files属性里边也会被排除在外,写法与".gitignore"类似。

2.2 安装依赖

package.json文件中添加以下字段:

"devDependencies": {

"@babel/cli": "^7.2.3",

"@babel/core": "^7.5.5",

"@babel/plugin-proposal-class-properties": "^7.2.3",

"@babel/plugin-proposal-object-rest-spread": "^7.2.0",

"@babel/preset-env": "^7.5.5",

"@babel/preset-react": "^7.0.0",

"babel-core": "^6.26.3",

"babel-loader": "^8.0.6",

"babel-preset-es2015": "^6.24.1",

"babel-preset-stage-1": "^6.24.1",

"babel-plugin-import": "^1.12.0",

"classnames": "^2.2.6",

"css-loader": "^3.2.0",

"eslint": "^6.2.1",

"eslint-plugin-import": "^2.18.2",

"eslint-plugin-jsx-a11y": "^6.2.3",

"eslint-plugin-react": "^7.14.3",

"eslint-watch": "^6.0.0",

"less": "^3.10.2",

"less-loader": "^5.0.0",

"polished": "^3.4.1",

"prop-types": "^15.7.2",

"react": "^16.9.0",

"react-dom": "^16.9.0",

"style-loader": "^1.0.0",

"webpack": "^4.39.3",

"webpack-cli": "^3.3.7",

"webpack-dev-server": "^3.8.0"

},

"dependencies": {

"antd": "^3.22.0",

"axios": "^0.19.0",

"lodash": "^4.17.15",

},

"peerDependencies": {

"antd": "^3.22.0",

"prop-types": "^15.7.2",

"react": "^16.9.0",

"react-dom": "^16.9.0"

}dependencies字段指定了项目运行所依赖的模块,该类型依赖一般属于运行项目业务逻辑需要依赖的第三方库。

devDependencies指定项目开发所需要的模块。

peerDependencies中声明的依赖,如果项目没有显式依赖并安装,则不会被npm 自动安装,转而输出warning日志,告诉项目开发者,你需要显式依赖了,不要再依靠我了。

2.3 添加组件

创建src/components目录,在目录下依次添加TooltipButton.js,Toolbar.js,Toolbar.css文件

TooltipButton.js

import React from 'react'

import PropTypes from 'prop-types'

import { Tooltip, Button } from 'antd'

const TooltipButton = props => {

const { children, tip, placement, ...restProps } = props

return (

{children}

)

}

TooltipButton.propTypes = {

tip: PropTypes.string.isRequired,

placement: PropTypes.string.isRequired

}

TooltipButton.defaultProps = {

placement: 'top'

}

export default TooltipButton

Toolbar.js

import React from 'react'

import PropTypes from 'prop-types'

import classNames from 'classnames'

import styles from './Toolbar.css'

const Toolbar = ({ children, inline, compact, ...restProps }) => {

const classes = classNames({

[styles.toolbarInline]: inline,

[styles.toolbar]: !inline,

[styles.compact]: compact

})

return (

{children}

)

}

Toolbar.propTypes = {

inline: PropTypes.bool.isRequired,

compact: PropTypes.bool.isRequired

}

Toolbar.defaultProps = {

inline: false,

compact: false

}

const Group = ({ children }) => {

return (

{children}

)

}

const Tool = ({ children }) => {

return (

{children}

)

}

Toolbar.Group = Group

Toolbar.Tool = Tool

export default Toolbar

Toolbar.css

.toolbar {

display: block;

}

.toolbarInline {

display: inline-block;

}

.tool {

margin: 0 2px 0 0;

}

.group {

margin: 0 8px 0 0;

}

.compact .tool {

margin: 0 1px;

}

.compact .group {

margin: 0 4px

}

src/index.js

import Toolbar from './lib/components/Toolbar'

import TooltipButton from './lib/components/TooltipButton'

2.4 webpack配置

利用webpack将文件打包

在根目录下添加webpack.config.js文件,添加配置信息如下

var path = require('path')

var webpack = require('webpack')

module.exports = {,

entry:'./src/index.js', //入口文件路径 output: {

filename: 'main.js',

library: 'nsc-components',

libraryTarget: 'umd',

libraryExport: 'default'

},

module: {

rules: [

{

test: /\.js[x]?$/, // 用正则来匹配文件路径,这段意思是匹配 js 或者 jsx exclude: /node_modules/,

include: [path.resolve(__dirname, 'src')],

loader: 'babel-loader' // 加载模块 "babel-loader" },

{

test: /\.less$/,

use: [

'style-loader',

{ loader: 'css-loader', options: { importLoaders: 1 } },

'less-loader',

{ loader: 'less-loader', options: { javascriptEnabled: true } }

]

},

{

test: /\.css$/,

use: [{

loader: "style-loader"

},

{

loader: 'css-loader',

options:{

modules: {

mode: 'local',

localIdentName: '[name]-[local]',

},

}

}

]

},

]

},

plugins: [

new webpack.HotModuleReplacementPlugin(),

],

}entry:是入口文件所处的目录的绝对路径的字符串

output:

output.filename:输出文件名

output.library:此配置适用于webpack 用于打包 JavaScript library的时候,例如本例中开发一个名为'nsc-components' 的library,配置output.library设置 library 的名称为'nsc-components'

output.libraryTarget:配置了output.library后,还需要在配置文件中添加libraryTarget属性,控制 library 如何以不同方式暴露的选项。

可以通过以下方式暴露 library:变量:作为一个全局变量,通过 script 标签来访问(libraryTarget:'var')

this:通过 this 对象访问(libraryTarget:'this')

window:通过 window 对象访问,在浏览器中(libraryTarget:'window')

UMD:在 AMD 或 CommonJS 的 require 之后可访问(libraryTarget:'umd')

module.rules:

创建模块时,匹配请求的规则数组,这些规则能够对模块(module)应用 loader。

2.5 打包文件

运行以下命令,将文件打包输出

npm run build

3 创建测试应用

我们需要一个本地环境去试用我们的组件,在本地利用脚手架或者手动新建一个小应用项目,本次我手动新建了一个测试应用,你需要从GitHub上把它clone下来并安装依赖。

$ git clone https://github.com/aaxuyun/demo.git

运行npm run start或yarn start,在你的浏览器上打开http://localhost:9100,页面出现‘hello',修改test/index.js文件,将热加载更新。

在之前组件库的根目录运行

pwd

复制组件库地址

在当前测试应用的根目录运行

npm link [组件库地址]

在测试应用调用组件

在test/index.js中我们将像使用其他外部库一样引入和调用我们的组件:

import React from 'react';

import { render} from 'react-dom';

import { TooltipButton } from 'nsc-components'

const App = () => {

return (

)};

render(, document.getElementById("root"));

保存之后,在页面中将看到以下按钮在界面中显示

4 发布到npm

运行以下命令将组件发布到npm

npm publish

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐