写一个包并发布到 npm 上面

前端瓶子君

共 17194字,需浏览 35分钟

 · 2022-07-11

image.png

说到npm包都会给人一种特别高大上的感觉,并且自己写了一个包之后如果有人用那么就会产生莫大的成就感,程序员的快乐就是这么简单。

想必有产生写npm包想法的人都对模块化比较熟悉,并且对于react、vue两者之一都比较熟练了。

下面呢我们就是使用react来写一个自己的npm包,我们呢会使用自己封装的webpack脚手架来写,如果有兴趣同学可以来看一下我的自我沉淀webpack5+react+eslint+tslint[1]文章。接下来的内容呢也是基于此来说明的。

这里也有现成的脚手架[2]

一、不同点

npm包的目录结构和普通的脚手架结构有所不同

1.启动目录不同:以往我们习惯将entry文件写在src中,但是npm包的入口文件不能写在src中,因为npm是将我们的源代码打包,不可以包括html。

所以将index.jsx和index.html文件提取到example文件中。【注意】example文件要和src同级。

结构和内容如下

index.jsx

 import React from 'react';
 import { render } from 'react-dom';
 import ReactDemo from '../src';

 const App = () => <ReactDemo />;
 render(<App />document.getElementById('root'));

index.html

 <!DOCTYPE html>
 <html lang="en">
   <head>
     <meta charset="UTF-8" />
     <meta http-equiv="X-UA-Compatible" content="IE=edge" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <title>Document</title>
   </head>
   <body>
     <div id="root"></div>
   </body>
 </html>

然后在src/index.jsx文件中 导出

 import App1 from './App';
 export default App1;
    

二 配置npm包的打包运行文件

在 config文件夹中新建webpack.npm.js文件

配置文件内容差不多。如下:详细配置请移步 自我沉淀webpack5+react+eslint+tslint[3]

externals划重点:这个可以告诉npm打包的时候不许将下面几种东西打包进去哦。

 const { resolve } = require('path');
 const cssLoaders = [
   'style-loader',
   {
     loader'css-loader',
     options: {
       importLoaders1,
       modules: {
         auto(resourcePath) => resourcePath.endsWith('.less'),
         localIdentName'[local]_[hash:base64:10]',
       },
     },
   },
   {
     loader'postcss-loader',
     options: {
       postcssOptions: {
         plugins: [['autoprefixer'], require('postcss-preset-env')()],
       },
     },
   },
 ];
 module.exports = {
   entry'./src/index.tsx',
   mode: process.env.NODE_ENV,
   externals: {
     antd'antd',
     react'React',
   },
   output: {
     libraryTarget'umd',
     filename'index.js',
     path: resolve(resolve(__dirname, '..'), 'dist'),
     cleantrue,
   },
   resolve: {
     alias: {
       '@': resolve(resolve(__dirname, '..'), 'src/'),
     },
     extensions: ['.ts''.tsx''.js''.jsx''.json'],
     mainFiles: ['index'],
   },

   devServer: {
     hottrue,
     port3002,
     host'127.0.0.1',
     compresstrue,
     opentrue,
     proxy: {
       '/api': {
         target'http://127.0.0.1:3002',
         pathRewrite: { '^/api''' },
         securefalse,
       },
     },
   },

   module: {
     rules: [
       {
         test/\.(js|jsx)$/,
         include: resolve(resolve(__dirname, '..'), ''),
         exclude/node_modules/,
         enforce'pre',
         use: [
           {
             loader'babel-loader',
             options: {
               presets: ['@babel/preset-env''@babel/preset-react'],
               // 缓存:第二次构建时,会读取之前的缓存
               cacheDirectorytrue,
             },
           },
         ],
       },
       {
         test/\.tsx$/,
         loader'ts-loader',
         exclude/node_modules/,
       },
       {
         test/\.css$/,
         use: [...cssLoaders],
       },
       {
         test/\.less$/,
         use: [...cssLoaders, 'less-loader'],
       },
       {
         test/\.s[ac]ss$/,
         use: [...cssLoaders, 'sass-loader'],
       },
       {
         exclude/.(html|less|css|sass|js|jsx|ts|tsx)$/,
         test/\.(jpg|jpe|png|gif)$/,
         loader'file-loader',
         options: {
           name'imgs/[name].[ext]',
           outputPath'other',
         },
       },
       {
         test/\.(ect|ttf|svg|woff)$/,
         use: {
           loader'file-loader',
           options: {
             name'icon/[name].[ext]',
           },
         },
       },
     ],
   },
 };

下面着重说一下package.json中的内容

  • name: 包名,后续在npm中搜索全靠它

  • version:版本号,每发布一次npm包就要增加一个版本,每个版本不能重复。

  • description:描述

  • main: 本包向外暴露的文件,很重要,一定要和你打包出来的文件名一模一样,我的叫做"dist/index.js"

  • private: true/false 是否为私有。一般为false否则只有自己能使用

  • flies: 暴露的文件夹, 有哪些文件夹提交到npm上面 格式为[ "dist" ]

  • keywords: npm检索的关键字

  • author: 作者

  • license: ISC

  • peerDependencies: 代表着当前npm包依赖下面这几种环境。

    完整配置

   {
    "name""new_webpack_action2",
    "version""1.0.24",
    "m""",
    "main""dist/index.js",
    "private"false,
    "flies": [
      "dist"
    ],
    "scripts": {
      "test""echo \"Error: no test specified\" && exit 1",
      "dev""export NODE_ENV=development && npx webpack serve --config config/webpack.dev.js",
      "build""export NODE_ENV=production && npx webpack   --config config/webpack.prod.js",
      "npm""export NODE_ENV=production && npx webpack   --config config/webpack.npm.js"
    },
    "keywords": [
      "react",
      "javascript",
      "npm"
    ],
    "author""919022572@qq.com",
    "license""ISC",
    "devDependencies": {
      "@ant-design/icons""4.7.0",
      "@babel/core""^7.15.0",
      "@babel/preset-env""^7.15.0",
      "@babel/preset-react""^7.14.5",
      "@types/lodash""^4.14.178",
      "@types/react""^17.0.19",
      "@types/react-dom""^17.0.11",
      "@types/react-router-dom""^5.3.3",
      "@typescript-eslint/eslint-plugin""^5.11.0",
      "@typescript-eslint/parser""^5.11.0",
      "autoprefixer""^10.3.2",
      "babel-loader""^8.2.2",
      "babel-plugin-import""^1.13.3",
      "css-loader""^6.2.0",
      "css-minimizer-webpack-plugin""^3.0.2",
      "eslint""^8.8.0",
      "eslint-config-airbnb""^19.0.4",
      "eslint-plugin-import""^2.25.4",
      "eslint-plugin-jsx-a11y""^6.5.1",
      "eslint-plugin-react""^7.28.0",
      "eslint-plugin-react-hooks""^4.3.0",
      "eslint-webpack-plugin""^3.1.1",
      "file-loader""^6.2.0",
      "html-webpack-externals-plugin""^3.8.0",
      "html-webpack-plugin""^5.5.0",
      "less""^4.1.1",
      "less-loader""^10.0.1",
      "lodash""^4.17.21",
      "mini-css-extract-plugin""^2.2.0",
      "postcss-loader""^6.1.1",
      "postcss-preset-env""^7.4.2",
      "sass""^1.38.0",
      "sass-loader""^12.1.0",
      "speed-measure-webpack-plugin""^1.5.0",
      "style-loader""^3.2.1",
      "stylelint""^13.13.1",
      "stylelint-config-standard""^22.0.0",
      "terser-webpack-plugin""^5.1.4",
      "thread-loader""^3.0.4",
      "ts-loader""^9.2.5",
      "tslint""^6.1.3",
      "typescript""^4.5.5",
      "webpack""^5.68.0",
      "webpack-cli""^4.8.0",
      "webpack-dev-server""^4.0.0",
      "webpack-merge""^5.8.0",
      "workbox-webpack-plugin""^6.4.2"
    },
    "dependencies": {
      "antd""4.18.8",
      "axios""^0.26.0",
      "react""17.0.2",
      "react-dom""17.0.2",
      "react-router-dom""5.2.0"
    },
    "peerDependencies": {
      "@ant-design/icons""4.7.0",
      "antd""4.18.8",
      "bizcharts""4.1.15",
      "rc-footer""0.6.6",
      "react""17.0.2",
      "react-dom""17.0.2",
      "react-router-dom""5.2.0"
    },
    "browserslist": {
      "development": [
        "last 1 chrome version",
        "last 1 firefox version",
        "last 1 safari version"
      ],
      "production": [
        ">0.2%",
        "not dead",
        "not op_mini all"
      ]
    }
  }

三、发布

如果是第一次发布包,执行以下命令,然后输入前面注册好的NPM账号,密码和邮箱,将提示创建成功

npm adduser

如果不是第一次发布包,执行以下命令进行登录,同样输入NPM账号,密码和邮箱

npm login

注意:npm adduser成功的时候默认你已经登陆了,所以不需要再进行npm login了

接着先进入项目文件夹下,然后输入以下命令进行发布

npm publish

当终端显示如下面的信息时,就代表版本号为1.0.0(你的package.json中的版本号)的包发布成功啦!前往NPM官网就可以查到你的包

+ 你的文件名@0.1.0

四、报错

1、如果出现

npm ERR! code E403
npm ERR! 403 403 Forbidden - PUT https://registry.npmjs.org/ghost-watermarkdemo - Forbidden
npm ERR! 403 In most cases, you or one of your dependencies are requesting
npm ERR! 403 a package version that is forbidden by your security policy, or
npm ERR! 403 on a server you do not have access to.

以下几种原因会导致

账号密码错误   (请检查npm官网的账号密码)
包重名     (请检查npm官网上是否有同名项目,名字取决于 package.js 的项目名字段)
网络原因   
镜像源问题 
新注册的用户邮箱未激活。  登陆你的邮箱去激活(如下)
image.png

2、 如果出现

image.png

需要在你的package.json中 private改为false或者删除

更新已经发布的包

更新包的操作和发布包的操作是一样的

   npm publish

但是每次更新时,必须修改版本号后才能更新,比如将1.0.0修改为1.0.1后才能更新发布。

这里的包版本管理规则都是一样的,采用的是semver(语义化版本),意思就是版本号:大改.中改.小改

五、## 从npm上面卸载自己发布的包

进入自己项目的目录执行。npm unpublish --force 出现:

   npm WARN using --force Recommended protections disabled.
-包名@0.1.0

则卸载成功,这时在npm上面就搜索不到了

参考资料

[1]

https://juejin.cn/post/7002157698108096543: https://juejin.cn/post/7002157698108096543

[2]

https://github.com/ghost-myl/npm-custom_webpack: https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Fghost-myl%2Fnpm-custom_webpack

[3]

https://juejin.cn/post/7002157698108096543: https://juejin.cn/post/7002157698108096543


关于本文

来自:夏末海棠

https://juejin.cn/post/7072652104837365774


最后


欢迎关注【前端瓶子君】✿✿ヽ(°▽°)ノ✿
回复「算法」,加入前端编程源码算法群,每日一道面试题(工作日),第二天瓶子君都会很认真的解答哟!
回复「交流」,吹吹水、聊聊技术、吐吐槽!
回复「阅读」,每日刷刷高质量好文!
如果这篇文章对你有帮助,在看」是最大的支持
 》》面试官也在看的算法资料《《
“在看和转发”就是最大的支持


浏览 28
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报