通过npm发布的第一个React组件的实践过程

前言

从开始接触react也有差不多四个月时间了,用的频繁了之后,也越发的“默契”。最近在项目中要开发一些定制化组件,写了些组件想要集中的管理,就打算把组件放在npm上,在网上查找资料学习了下,下面就是通过npm发布自己的第一个React组件的实现过程

注册

npm的注册就跟我们注册微博等其他社交账号一样,基本都是输入邮箱、用户名等等信息,十分简单也不耗时

注册网址:https://www.npmjs.com

新建项目

在github上新建一个项目(这里不做过多赘述,相信使用过github的盆友应该会很熟悉),然后git clone下来之后进入到项目目录,执行npm init,按提示输入信息,主要有

  • name:发布的名称
  • version:版本号
  • entry:入口文件

刚开始没填好没关系,后期也可以修改

具体封装实现

我在这个项目里面封装了一个react-hover-menu的组件

这是完成后的项目树:

image

配置package.json

npm相关信息

"name": "react-hover-menu",
"version": "1.0.3",
"description": "react hover or click menu component",
"main": "dist/bundle.js",
"files": [
  "dist"
],
"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "build": "webpack"
},
  • main: 这里是我们组件的入口文件。开发者在 import 我们的组件的时候会引入这里 export 的内容

  • files: 申明将要发布到 npm 的文件。如果省略掉这一项,所有文件包括源代码会被一起上传到 npm

  • scripts: 加入一个 build 指令来运行 webpack,此时运行的 webpack 是这个当前文件夹内安装的 webpack 而不是 global 的 webpack

依赖

"dependencies": {
  "antd": "^3.10.5",
  "prop-types": "^15.6.0",
  "react": "^16.2.0",
  "react-dom": "^16.6.1"
},
"devDependencies": {
  "babel-core": "^6.26.3",
  "babel-loader": "^7.1.2",
  "babel-plugin-import": "^1.11.0",
  "babel-preset-env": "^1.6.1",
  "babel-preset-react": "^6.24.1",
  "css-loader": "^0.28.11",
  "less": "^3.8.1",
  "less-loader": "^4.1.0",
  "postcss-loader": "^3.0.0",
  "react-hot-loader": "^4.3.12",
  "react-scripts": "^1.1.0",
  "style-loader": "^0.19.1",
  "webpack": "^3.10.0",
  "webpack-node-externals": "^1.6.0"
}

这些依赖是我这个组件需要用到的,具体依赖得根据具体项目需要添加

配置webpack(webpack.config.js)

  • 通过babel准换JSX和ES6的代码

  • 通过css-loader, sytle-loader 引入 css 到打包文件

  • 通过exclude:/src/, css-loader对antd的样式做处理

  • 通过style-loader,css-loader,postcss-loader,less-loader对less样式做处理

  • webpack-node-externals 可以避免把 node_modules 里面的依赖包引入打包文件

  • libraryTarget: "commonjs2" 使测试项目可以找到我们打包后的组件

const path = require('path')
const nodeExternals = require('webpack-node-externals')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
    libraryTarget: 'commonjs2'
  },
  module: {
    rules: [
      {// 通过babel准换JSX和ES6的代码
        test: /(\.jsx|\.js)$/,
        loader: 'babel-loader',
        exclude: /node_modules/,
        query:
        {
          presets:["env", "react"],
          plugins: [
            [  "import",{libraryName: "antd", style: "css"}] // antd按需加载
          ]
        }
      },
      {// CSS处理
        test: /\.css$/,
        loader: "style-loader!css-loader?modules",
        exclude: /node_modules/,
      },
      {// antd样式处理
        test:/\.css$/,
        exclude:/src/,
        use:[
            { loader: "style-loader",},
            {
                loader: "css-loader",
                options:{
                    importLoaders:1
                }
            }
        ]
      },
      {// less样式处理
        test: /\.less$/,
        use: 
        [
          "style-loader", 
          {
            loader: 'css-loader', 
            options: {
              sourceMap: 1,
              modules:true
            }
          }, 
          {
            loader: "postcss-loader",
            options: {           // 如果没有options这个选项将会报错 No PostCSS Config found
              plugins: (loader) => [
                  require('autoprefixer')(), //CSS浏览器兼容
              ]
            }
          },
          {
            loader: "less-loader",
            options: {
              modifyVars: {
                'primary-color': '#1DA57A',
                'link-color': '#1DA57A',
                'border-radius-base': '2px',
              },
              javascriptEnabled: true,
            }
          }
        ]
      },
    ]
  },
  externals: [nodeExternals()]
}

配置babel(.babelrc)

  • transpile JSX and ES6 code
{
  "presets": ["react","env"],
  "plugins": ["react-hot-loader/babel",["import", { "libraryName": "antd", "libraryDirectory": "lib", "style": "css" }]],
  "env": {
    "production":{
      "preset":["react-optimize"]
    }
  }
}

测试

完成上述步骤后我们就可以在src文件夹里面完成自己的组件,这个时候就可以打包并link让测试项目引入这个打包后的组件进行测试

npm run build

npm link (可能会提示需要管理员权限)

cd [test project folder]

npm link react-hover-menu

创建测试项目

在本地做一个测试项目按上述步骤引入组件就可以看到成果了

使用create-react-app 创建一个 react 项目,并 link 我们刚才完成的组件:

create-react-app demo-menu

cd demo-menu

npm link react-hover-menu

link之后我们就可以直接在页面中引入组件

import HoverMenu from 'react-hover-menu'

发布到NPM

首先先登录npm

npm login

然后发布组件到npm

npm publish

取消发布

npm unpublish

更改版本

更改 package.json 里面的版本号并重新发布

出现的问题

解决引入antd样式无效问题

刚开始配置的时候,发现了一个如果同时引入css modules和按需引入antd,antd样式无效的问题

官网给出的按需加载解决方案,需要先安装babel-plugin-import

  • babel-loader的query/options字段加入
plugins: [
  [  "import",{libraryName: "antd", style: "css"}] // antd按需加载
]
  • webpack中plugins字段这样配置
module: {
  rules: [
    {// 通过babel准换JSX和ES6的代码
      test: /(\.jsx|\.js)$/,
      loader: 'babel-loader',
      exclude: /node_modules/,
      query:
      {
        presets:["env", "react"],
        plugins: [
          [  "import",{libraryName: "antd", style: "css"}] // antd按需加载
        ]
      }
    },
    {// CSS处理
      test: /\.css$/,
      loader: "style-loader!css-loader?modules",
      exclude: /node_modules/,
    }
  ]
}

测试后发现还是不行

解决办法

如果同时需要使用antd和css modules,处理样式时,需要分别处理

webpack加入以下处理

{// CSS处理
  test: /\.css$/,
  loader: "style-loader!css-loader?modules",
  exclude: /node_modules/,
},
{// antd样式处理
  test:/\.css$/,
  exclude:/src/,
  use:[
      { loader: "style-loader",},
      {
          loader: "css-loader",
          options:{
              importLoaders:1
          }
      }
  ]
},

引入less

由于后期要修改ant上的样式,根据官网上给出的提示,必须加装less格式

image

继续在webpack加入以下处理

{// less样式处理
  test: /\.less$/,
  use: 
  [
    "style-loader", 
    {
      loader: 'css-loader', 
      options: {
        sourceMap: 1,
        modules:true
      }
    }, 
    {
      loader: "postcss-loader",
      options: {           // 如果没有options这个选项将会报错 No PostCSS Config found
        plugins: (loader) => [
            require('autoprefixer')(), //CSS浏览器兼容
        ]
      }
    },
    {
      loader: "less-loader",
      options: {
        modifyVars: {
          'primary-color': '#1DA57A',
          'link-color': '#1DA57A',
          'border-radius-base': '2px',
        },
        javascriptEnabled: true,
      }
    }
  ]
},

好了,可以开森的npm组件了

Github Repo

查看我创建的前端React呼出菜单组件