• 在前端开发时,一些常见的组件(如select)往往不能满足现实开发需求,需要对组件进行重新的封装和优化
  • 重新封装优化的组件往往会用在不同的几个项目,这个时候如果要在另外一个项目中使用这个组件,就只能把组件代码重新copy一份,这样存在很多弊端,第一点就是代码冗余;第二点是如果要对这个组件进行升级修改,这个时候得去每个项目下修改一遍,想想就头大

为了解决这个问题,做了以下探索,特此记录以下操作的流程,中间有很多细节本文不详细解释,因为还有很多配置不是很明白,找个机会系统讲一下npm相关知识

- 为什么要搭建npm私有仓库(私服)
eg:
1.有三个项目A、B 、C
2.在A项目写了一个select组件,又不能将这个组件发布到npm公共社区里

没有npm私服时的做法:

  1. 这时如果B和C项目要用带这个组件的话,只能去A项目里复制一份粘贴到B和C项目

  2. 如果后面select组件更新了的话,又只能复制粘贴

如上这种情况,耗费时间。有时还会忘了哪个项目的组件时最新的。一个组件都这么麻烦,如果是多个组件和多个项目,维护简直要疯了。

如果我们有npm私有仓库的做法:

  1. 将select组件发布到私有仓库里
  2. 每个项目启动前,执行npm 命令重新下载更新组件即可

私有npm私有仓库的好处

  • 便于管理企业内的业务组件或者模块
  • 私密性
  • 确保npm服务快速稳定(私服通常搭建在内部的服务器里)
  • 控制npm模块质量和安全(防止恶意代码植入)

- 自定义组件发布到私有仓库的流程(Windows系统)
1.创建组件

md gldSelect
cd gldSelect

2.创建以下文件或文件夹

md build
md src

package.json

"name": "gld-select", "version": "1.0.1", "description": "This is an optimized Select component", "main": "build/index.js", "peerDependencies": { "react-dom": "^16.12.0", "react": "^16.4.0" "scripts": { "start": "webpack --watch", "build": "webpack" "author": { "name": "szyy" "license": "ISC", "dependencies": { "antd": "^3.26.5", "prop-types": "^15.7.2", "react": "^16.4.0", "react-dom": "^16.12.0", "webpack": "^4.12.0" "devDependencies": { "babel-cli": "^6.26.0", "babel-core": "^6.26.3", "babel-loader": "^7.1.5", "babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-object-rest-spread": "^6.26.0", "babel-plugin-transform-react-jsx": "^6.24.1", "babel-preset-env": "^1.7.0", "babel-preset-es2015": "^6.24.1", "babel-preset-stage-0": "^6.24.1", "webpack-cli": "^3.3.10"

webpack.config.js

var path = require('path');
module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'index.js',
    libraryTarget: 'commonjs2'
    // libraryTarget: 'umd'
  module: {
    rules: [
        test: /\.js$/,
        include: path.resolve(__dirname, 'src'),
        exclude: /(node_modules|bower_components|build)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ["env", "stage-0"]
  externals: {
    'react': 'commonjs react'

.babelrc

"presets": ["env", "stage-0"], "plugins": [ "transform-object-rest-spread", "transform-react-jsx", "transform-class-properties"

3.将自定义组件写入到src/index.js
src/index.js

import React from "react";
import { Select } from "antd";
import PropTypes from "prop-types";
export default class GldSelect extends Select {
  static propTypes = {
    dataSource: PropTypes.array,
    onChange: PropTypes.func,
  static defaultProps = {
    dataSource: [],
  init(source, isSearch) {
    this.pageNum = 1;
    this.scrollPos = 0;
    this.dataSource = source;
    this.setState({
      displayVals: this.dataSource.length > this.pageSize ?
        this.dataSource.slice(0, this.pageSize * this.pageNum) : this.dataSource,
      isSearch: isSearch,
    });
  constructor(props) {
    super(props);
    this.pageSize = 20;
    this.pageNum = 1;
    this.scrollPos = 0;
    this.dataSource = props.dataSource;
    this.state = {
      displayVals: this.dataSource.length > this.pageSize ?
        this.dataSource.slice(0, this.pageSize * this.pageNum) : this.dataSource,
      isSearch: false,
  componentDidUpdate(prevProps, prevState, snapshot) {
    const { dataSource } = this.props;
    const ds = [].concat(dataSource);
    const preDs = [].concat(prevProps.dataSource);
    if (preDs.sort().toString() !== ds.sort().toString()) {
      this.init(dataSource)
  handleChange = v => {
    const { onChange, dataSource } = this.props;
    const { isSearch } = this.state;
    onChange && onChange(v);
    /*点击事件分为两种情况:
    * 1.一般的下拉框选中,这时只需要执行props中的onChange
    * 2.根据关键字搜索后,点击选中时,需要恢复到初始的状态
    if (isSearch === true) {
      this.init(dataSource)
  handleSearch = v => {
    v = v || "";
    const filterWord = v.trim();
    const { dataSource } = this.props;
    const newSource = dataSource.filter(item =>
      item.includes(filterWord)
    this.init(newSource, true);
  handlePopupScroll = e => {
    const { displayVals } = this.state;
    e.persist();
    const { target } = e;
    const st = target.scrollTop;
    if (st === 0 && this.scrollPos) {
      target.scrollTop = this.scrollPos;
    if (
      st + target.offsetHeight + 2 >= target.scrollHeight &&
      displayVals.length < this.dataSource.length
      this.pageNum += 1;
      this.setState({ displayVals: this.dataSource.slice(0, this.pageSize * this.pageNum) });
      this.scrollPos = st;
    } else {
      this.scrollPos = 0;
  render() {
    const { dataSource, ...rest } = this.props;
    const { displayVals } = this.state;
    return (
      <Select
        {...rest}
        onChange={this.handleChange}
        onPopupScroll={this.handlePopupScroll}
        dropdownMatchSelectWidth={false}
        maxTagCount={5}
        onSearch={this.handleSearch}
        {displayVals.map((opt) => (
          <Select.Option key={opt} value={opt}>
            {opt}
          </Select.Option>
        ))}
      </Select>

4.安装相关npm包并打包

npm i
npm run build
npm link

npm link将用于在我们开发测试项目时对我们的组件进行开发测试。

5.测试组件是否可用,首先创建一个测试项目

npm i -g create-react-app
md gld-test
cd gld-test
create-react-app .
npm link gldSelect

测试项目创建成功后,在src/App.js中添加自定义组件gldSelect,并完成测试

import GldSelect from 'gldSelect'; 
 ...
 <GldSelect />

App.js

import React from 'react';
import logo from './logo.svg';
import './App.css';
import GldSelect from 'gldSelect';
import {
  Button,
  Row,
  Col,
  Card,
  Form,
  Select,
  DatePicker,
  Icon,
  Tabs,
  notification,
  Table,
} from 'antd';
function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
          Edit <code>src/App.js</code> and save to reload.
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
          Learn React
      </header>
            <Col span={9}>
              <Form.Item>
                <GldSelect
                  style={{ width: '100%' }}
                  placeholder="选择预算版本号"
                  mode="multiple"
                  dataSource={[1,3,4,5,7,8,9,0]}
              </Form.Item>
            </Col>
          </Row>
        </Form>
      </div>
    </div>
export default App;

添加完成后,执行

npm start

如果在测试项目中可用,则该自定义组件可用,可上传至npm私有库

6.将自定义组件上传私有库

cd gldSelect

安装nrm

npm install -g nrm

nrm(npm registry manager )是npm的镜像源管理工具,有时候国外资源太慢,使用这个就可以快速地在 npm 源间切换

添加私有库镜像源

nrm add szyy http://xx.xxx.x.xxx:xxxx/repository/npm-host/

切换到私有库镜像源

nrm use szyy

登录私有库镜像源账号

npm login

(按照要求输入账号密码以及邮箱)

登录成功后,执行以下命令完成组件上传

npm publish

7.项目中使用自定义组件
通过npm的config命令配置指向国内私有镜像源:以后所有从仓库获取的包都从这个地址获取,不走国外的地址

npm config set registry http://xx.xxx.x.xxx:xxxx/repository/npm/

在项目中执行以下命令,安装自定义组件

npm install gld-select

在js文件代码中直接引用

import GldSelect from 'gld-select'

- 参考资料

- 背景在前端开发时,一些常见的组件(如select)往往不能满足现实开发需求,需要对组件进行重新的封装和优化重新封装优化的组件往往会用在不同的几个项目,这个时候如果要在另外一个项目中使用这个组件,就只能把组件代码重新copy一份,这样存在很多弊端,第一点就是代码冗余;第二点是如果要对这个组件进行升级修改,这个时候得去每个项目下修改一遍,想想就头大为了解决这个问题,做了以下探索,特此记录... 导入主要模块ExcelRenderer,以将工作表数据转换为JSON格式。 还要导入OutTable,以将获取的JSON显示到HTML表中。 import {OutTable, ExcelRenderer} from 'react-excel-renderer'; 在类的render函数中放置一个简单的input元素,并传递一个onChange处理函数 <input type="file" onChange={this.fileHandler.bind(this)} style={{"padding":"10px"}} /> 在onChange处理程 公司的前端组件库经过前端好几位大佬搞了个基于React、Ant design二次扩展的库,之前设想是每个业务模块基于公共的组件库维护各自的业务组件库,经过几个周期之后,发现已经不可控了,每个业务模块的Ant design版本不一样,而且各自基于公共又搞了一套自己的库,为了收紧口子跟后期的维护扩展,决定把公共库打包发布npm私服,然后install到各自业务线,确保各业务线一致,这是背景 在这之前有使用rollup打包的经验,这里也计划用rollup来实现,这过程中碰到了不少问题,查阅相关资料发 引入reactreact-dom的两个js文件 <script src="./node_modules/react/umd/react.development.js"></script> <script src="./node_modules/reac <!-- 引入react核心库 --> <script src="https://cdn.bootcdn.net/ajax/libs/react/16.13.1/umd/react.development.js"></script> <!-- 引入react-dome 支持react操作dom --> <script src="https://cdn.bootcdn.net/ajax/libs/react-dom/16.13.1/umd/react 警告:Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA 169939 python3错误:使用python3执行.py文件遇到:*ImportError: /lib64/libstdc++.so.6: version `CXXABI_1.3.9' not found 警告:Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA 七彩小仙猪: 请问博主的这个,CPU和这个GPU的取消报错警告的两句代码有什么不一样的点吗表情包 vim下输入^A 重庆小面酱: 感谢记录啊 我也想半天 上来搜到你了 错误: 找不到或无法加载主类 org.apache.flume.tools.GetJavaProperty weixin_52225937: flume-ng在哪里