前端的组件都是基于 javascript 开发,然后用 node.js 打包,发布到 npm 的。所以我们要做组件发布,首先要了解 npm 包的开发与发布。

npm 包的开发与发布

项目初始化

我们常常用 npm init 命令来初始化 node 项目;如果使用默认的设置,则可加入参数 -y。下面,我们新建一个 npm-components 文件夹,然后初始化项目:

$ mkdir npm-components
$ cd npm-components
$ npm init -y

此时,会在项目文件夹中出现一个 package.json 文件:

{"name": "npm-components", // 组件名"version": "1.0.0", // 组件版本"description": "", // 组件描述"main": "index.js", // 组件入口"scripts": { // 组件脚本"test": "echo \"Error: no test specified\" && exit 1"},"keywords": [],"author": "", // 组件作者"license": "ISC" // 开源协议
}

package.json 文件中的 name、version、main、script 是常用的几个配置。

name 和 version 顾名思义是包名和版本。name 命名要注意最好是小写加中划线(-)的写法,version 规范可以参考下一小节。

script 则是可以运行在命令行工具的脚本。对 linux 脚本不熟的同学,可以先在命令行工具中调试完成,再复制到 script 里。

main 会作为整个组件的入口,默认是 index.js,可以根据实际需要进行修改。

我们先按默认设置,在项目文件夹中,新建一个 index.js:

// index.js
const hello = 'hello';
const world = 'world';
module.exports.log = function() {console.log(hello + world);
};

一个最简单的 npm 项目就完成了,这里仅包含 index.js 和 package.json两个文件。

package.json 除了以上配置之外,我们还可能会配置到

  • private 属性,是否私有项目,私有项目一般不对外公布。我们现在要把 npm 发布,所以要把这个 private 删除,或者设为 false。非开源项目,加上private: false可以避免将其发布到公网上。

  • files,需要发布到 npm 的文件。

  • dependencies 项目依赖库,这里会显示通过 npm install 安装到项目中的库;

  • devDependencies 项目开发环境依赖库,这里会显示通过 npm install --save-dev 安装到项目中的库。

npm version 与版本规范

开发完成就可以给组件打上版本号了。

按语义化版本控制规范 SemVer,版本格式为:major.minor.patch,版本号递增规则如下:

  1. 主版本号 major:当你做了不兼容的 API 修改,

  2. 次版本号 minor:当你做了向下兼容的功能性新增,

  3. 修订号 patch:当你做了向下兼容的问题修正。

先行版本号及版本编译信息可以加到“major.minor.patch”的后面,作为延伸。如:1.0.0-alpha.0

每次发布前,需要确定一下更新的内容,选择版本号,使用 npm version 打上版本号

$ npm version [patch|minor|major]

此时,package.json 中的 version 会在相应的版本上加 1,有的命令行工具中也会看到版本号的变化。

~/npm-components  (npm-components@1.0.0)
$ npm version patch
v1.0.1~/code/npm-components  (npm-components@1.0.1)
$

发布到 npm

开发完成,标记版本号之后,发布到 npm 只需要按部就班即可:

  1. 如果是公网的开源项目,需要去 npm 官网注册个账号;

  2. 如果贵公司有内部的 npm 库,只需要找到 npm 管理人员给你添加即可;

  3. 如果平时使用其他源,需要切换到 npm(可安装 nrm 来管理多个源):

    $ nrm lsnpm -------- https://registry.npmjs.org/yarn ------- https://registry.yarnpkg.com/cnpm ------- http://r.cnpmjs.org/taobao ----- https://registry.npm.taobao.org/$ nrm use npm
    
  4. 在项目根目录下的命令行工具,运行npm login,会提示输入个人信息,完成登录。

  5. 运行,会进行上传;

    $ npm publish
    
  6. 如果上传过程顺利,没报出红色错误信息,在 https://www.npmjs.com/ 就可以看到你发布的包了。

  7. 发布的包在72小时内是可以删除的,过了72小时就永远无法删除了,所以记得不要随意发一些没有意义的包。如果需要卸载,在发布后72小时内执行:

    # npm unpublish  <pkg>[@<version>]
    $ npm unpublish npm-components@1.0.1
    

至此,一个简单项目的 npm 包发布完成。

模块化

真实的项目中,我们往往不可能仅仅写一个 js,一个 js 也不可能写太长,多了就要拆分到多个js。这个时候,就需要用到封装了。

我们把常用的内容封装在一起,就是一个简单的模块。这整个过程可以成为是一个模块化的过程。

模块化帮我们实现了代码复用,也帮我们做到了模块内的数据、方法私有。

前端模块化演进

前端的模块化是一个演进的过程,经历了 4 个阶段:

  • 全局 function模式 : 将不同的功能封装成不同的全局函数

  • namespace模式 : 简单对象封装

  • IIFE模式:匿名函数自调用(闭包)

  • IIFE模式增强: 引入依赖

基本原理是将模块挂载在 window 属性下。到 IIFE 增强阶段,现在的模块化规范基本已经成型,有了明显的引入导出。如下代码的引入 jQuery 和暴露 myModule:

// module.js文件
(function(window, $) {let data = 'www.baidu.com';// 操作数据的函数function foo() {// 用于暴露有函数console.log(`foo() ${data}`);$('body').css('background', 'red');otherFun(); // 内部调用}function otherFun() {// 内部私有的函数console.log('otherFun()');}// 暴露行为window.myModule = { foo };
})(window, jQuery) // jQuery 作为参数引入

在此基础上,逐渐演化出了 AMD、CommonJS、CMD、UMD 等规范。

AMD(Asynchromous Module Definition - 异步模块定义)

AMD 更多用于浏览器端,需要异步加载各模块,然后再去执行内部代码。是 RequireJS 在推广过程中对模块定义的规范化产出,推崇依赖前置。

// 定义没有依赖的模块
define(function(){ return 模块 });// 定义有依赖的模块
define(['module1', 'module2'], function(m1, m2){ return 模块 });// 引入使用模块
require(['module1', 'module2'], function(m1, m2){ 使用m1/m2 });

CommonJS 规范

CommonJS 是服务端模块的规范,由于Node.js被广泛认知。根据CommonJS规范,一个单独的文件就是一个模块。加载模块使用require方法,该方法读取一个文件并执行,最后返回文件内部的module.exports对象。

//module1.js
moudle.exports = { value: 1 };//module2.js
var module1 = require('./module1');
var value2 = module1.value + 2;
module.exports ={ value: value2 };

CommonJS 加载模块是同步的,所以只有加载完成才能执行后面的操作。

CMD(Common Module Definition - 公共模块定义)

CMD 是 SeaJS 在推广过程中对模块定义的规范化产出,同时 CMD 也是延自 CommonJS Modules/2.0 规范。对于模块的依赖,CMD 是延迟执行,推崇依赖就近。

define((require, exports, module) => {module.exports = {fun1: () => {var $ = require('jquery'); // 执行 fun1 时,再加载return $('#test');} };
});

如上代码,只有当真正执行到fun1方法时,才回去执行jquery。

UMD 规范

那有没有一种规范同时同时兼容 AMD 和 CommonJS,既可以适用浏览器端,有可以适用于服务器端?

有的,就是 UMD 规范。UMD 规范甚至都不能称作一个规范,它是 AMD 和 CommonJS 的一个糅合。是一段固定的代码写法。如下的工厂模式:

((root, factory) => {if (typeof define === 'function' && define.amd) {//AMDdefine(['jquery'], factory);} else if (typeof exports === 'object') {//CommonJSvar $ = requie('jquery');module.exports = factory($);} else {//都不是,浏览器全局定义root.testModule = factory(root.jQuery);}
})(this, ($) => {//do something...  这里是真正的函数体
});

ES module 规范

另一种支持服务端和浏览器端的规范就是 ES module 了,即我们现在最常用的import 和 export

export default ...;
import xxx from '';export ...;
import { xxx } from '';

但目前也仅是大于 13.2 的 Node.js 版本才支持 ES 模块化,还需要等待很长的时间全面使用。

所以,出于兼容性考虑,我们仍然选择 UMD 规范进行开发。(处于淘汰边缘的UMD、CMD、AMD这些规范,大家不需要理解,只需要知道有这么一回事即可)

Webpack 打包

写 UMD 的过程重复且繁琐,这时候就需要用工具来完成了,Webpack 也就出现了。Webpack 配置相当简单,以我们的项目为例。

先安装 webpack

$ npm install --save-dev webpack webpack-cli

新建 webpack 配置文件,webpack.config.js。用 webpack 给组件库打包输出符合 UMD 规范的代码,只需要在基本配置稍作修改即可:

// webpack.config.js
const path = require('path');module.exports = {mode: 'production',entry: './index.js',externals: 'lodash', // library包中有引入lodash,打包时不将lodash打进去,用户在引入该library时,需自己再引入lodash,避免用户重复引入lodash,导致文件过大。output: {path: path.resolve(__dirname, 'dist'),filename: 'library.js',library: 'library', // 全局挂载包的引用名libraryTarget: 'umd',  //通用模式:支持用户通过es、common.js、AMD的方式引入npm包globalObject: 'this' // node 等环境运行时需要设置为 this}
}

要修改的地方如下:

  1. filename:打包产物library的名称;

  2. externals: ‘lodash’, library 包中有引入 lodash,打包时不将 lodash 打进去,用户在引入该 library 时,需自己再引入 lodash,避免用户重复引入 lodash,导致文件过大;

  3. libraryTarget: ‘umd’ 使用 UMD规范 支持用户通过 es、common.js、AMD 的方式引入 npm 包;

  4. library: ‘library’ 会在全局变量中增加一个liabray的变量,用于挂载该包,主要用于通过脚本形式全局引入时的配置;

  5. globalObject: 'this',为 webpack 4 新增属性,需要指定 global 的值为 ’this‘,否则会为默认值 ’self‘,无法在 nodejs 环境中使用。

在 package.json 中加上脚本

{
...script: {"build": "webpack" }
}

运行脚本

npm run build

此时,webpack就会开始打包。我们打开打包后的文件library.js,就会发现我们前面10行 UMD 规范的代码。以下是不压缩不混淆(Webpack.config.js 设置optimization: { minimize: false })打包后的代码:

(function webpackUniversalModuleDefinition(root, factory) {if(typeof exports === 'object' && typeof module === 'object')module.exports = factory();else if(typeof define === 'function' && define.amd)define([], factory);else if(typeof exports === 'object')exports["library"] = factory();elseroot["library"] = factory();
})(this, function() {
return /******/ (() => { // webpackBootstrap
/******/  var __webpack_modules__ = ({
/***/ 10:
/***/ ((module) => {
const hello = 'hello';
const world = 'world';
module.exports.log = function() {console.log(hello + world);
};
/***/ })
...

至此,一个支持浏览器端和服务端调用的组件打包完成,可以再次打个版本号,发布到 npm 库了。

本地测试

我们可以使用 npm 的包进行测试,也可以在开发阶段引入本地打好的包,进行简单的测试。我们遵守了 UMD 规范,因此可以在 node 环境和浏览器环境都验证一下:

node 环境验证

项目根目录新建一个 index.test.js

let library = require('./dist/library.js');
library.log();

本地执行

$ node index.test.js
helloworld

浏览器环境验证

项目根目录新建 public 文件夹,再在此文件夹下新建一个 index.html

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>测试页面</title>
</head>
<body>
<script src='../dist/library.js'></script>
<script>
library.log(); // 控制台输出 helloworld
</script>
</body>
</html>

此时,打开此文件夹,即可以看到 控制台输出 helloworld。

如果还要更真实的环境,还可以使用本地服务器做验证。

首先,安装 webpack 开发服务器:

$ npm install --save-dev webpack-dev-server

然后,修改package.json,新增 webpack 服务器脚本:

{
..."script":{"serve": "webpack serve --open",...}
}

修改 webpack.config.js

const path = require('path');
module.exports = {devServer: {static: path.join(__dirname, "./") // 将项目根目录设为静态目录},...
}

运行服务器

$ npm run serve

webpack 会默认打开浏览器。可以看到浏览器控制台输出 helloworld。

验证通过。大功告成!!!

一文搞懂前端组件发布 npm 库相关推荐

  1. 一文搞懂前端对象的深拷贝与浅拷贝

    在前端开发过程中常常会听到对象的深拷贝与浅拷贝,对于初学者来说,可能是傻傻的分不清楚,本人将详细介绍javascript中对象的深拷贝与浅拷贝. 一.javascript中的数据类型 基础数据类型 字 ...

  2. 奇舞周刊第 440 期:一文彻底搞懂前端沙箱

    记得点击文章末尾的" 阅读原文 "查看哟~ 下面先一起看下本期周刊 摘要 吧~ 奇舞推荐 ■ ■ ■ 一文彻底搞懂前端沙箱‍ 沙箱是一种安全机制,为运行中‍的程序提供隔离环境.通常 ...

  3. 一文彻底搞懂前端监控 等推荐

    大家好,我是若川.话不多说,这一次花了几个小时精心为大家挑选了20余篇好文,供大家阅读学习.本文阅读技巧,先粗看标题,感兴趣可以都关注一波,一起共同进步. 前端点线面 前端点线面 百度前端研发工程师, ...

  4. 一文搞懂 Traefik2.1 的使用

    原文链接:一文搞懂 Traefik2.1 的使用 一文搞懂 Traefik2.1 的使用 核心概念 安装 ACME 中间件 灰度发布 流量复制 TCP 简单 TCP 服务 带 TLS 证书的 TCP ...

  5. 一文搞懂什么是 PostCSS

    一文搞懂什么是 PostCSS 在 Web 应用开发中,CSS 代码的编写是重要的一部分.CSS 规范从最初的 CSS1 到现在的 CSS3,再到 CSS 规范的下一步版本,规范本身一直在不断的发展演 ...

  6. 【显卡】一文搞懂显卡

    [显卡]一文搞懂显卡 文章目录 [显卡]一文搞懂显卡 1. 前言介绍 1.1 CPU和显卡的区别 1.1.1 作用不同 1.1.2 结构不同 1.1.3 应用场景不同 1.2 三个著名的显卡公司 2. ...

  7. 一文搞懂AWS EC2, IGW, RT, NAT, SG 基础篇下

    B站实操视频更新 跟着拉面学习AWS--EC2, IGW, RT, NAT, SG 简介 长文多图预警,看结论可以直接拖到"总结"部分 本文承接上一篇文章介绍以下 AWS 基础概念 ...

  8. 一文搞懂CAN FD总线协议帧格式

    目录 1.为什么会出现CAN FD? 2.什么是CAN FD? 3.CAN FD和CAN总线协议帧异同 4.解析CAN FD帧结构 4.1.帧起始 4.2.仲裁段 4.3.控制段 4.4.数据段 4. ...

  9. 一文搞懂 Cocos Creator 3.0 坐标转换原理

    一文搞懂 Cocos Creator 3.0 坐标转换原理 屏幕坐标 UI 触点坐标 UI 多分辨率适配方案 UI 触点获取 不同坐标之间的转换 屏幕坐标与 3D 节点世界坐标互转 3D 节点之间的坐 ...

最新文章

  1. 静态网页使用Node.js跨域代理服务
  2. DS4700电池更换步骤
  3. 【数据结构与算法】之深入解析“股票的最大利润”的求解思路与算法示例
  4. Python学习17 Turtle库绘图
  5. 输入快捷键显示未知命令_「干货」华为VRP基础和常用命令了解一下
  6. 自然语言处理包pytorch、torchvision、torchtext安装(亲测有效)
  7. Java proxy 子类代理
  8. 65寸的液晶电视是挂在墙上好还是放在电视柜上好?
  9. queue,stack中的库函数
  10. 遗传算法——matlab实现
  11. 大学四年走来,这些网络工程师必备的模拟器我都给你整理好了
  12. 解析.db文件,并且导出为sql语句
  13. Altera系列板子没有办法sudo,问题解决
  14. 为什么我们应该使用 HTML5 开发网站
  15. NBA球队也使用Salesforce?
  16. Conversion of feet/inches to meters-英尺、英里装换为米
  17. win7怎么查看计算机主板,win7系统电脑查看主板型号的四种方法介绍
  18. 图片加水印怎么加?这篇文章告诉你
  19. MATLAB阶段性方程组,[转载]matlab 解方程组
  20. 安卓开发实战!一年后斩获腾讯T3,年薪超过80万!

热门文章

  1. 速食霸主“新食尚”对洋快餐说“不”!—— 骄傲过头?
  2. oracle给已有表添加主键
  3. 小散, 教你如何进行集合竞价
  4. 起码要有两个可选的解决方案
  5. 沃飞长空公共安全无人机,助力建设“空天地”一体化水文监测体系
  6. 微软Outlook/Hotmail邮箱垃圾邮件问题已经解决
  7. 2018年最好的医疗网站设计及配色赏析
  8. Linux配置Java JDK环境
  9. 在Python中编写动态SQL语句
  10. 免费查询手机号归属API地接口更新 无限制