npm是内置于nodejs的一个包管理工具,用于管理第三方模块。当你安装完nodejs,也就可以使用npm命令了。
npm的官方服务器上有大量第三方模块,如nodejs工具(webpack、rollup等)、nodejs模块(mysql、path等)、vue及react等的脚手架(vue-cli、create-react-app等),以及各大js框架对应的不计其数的组件、插件等。截止到目前(2020年2月15日),npm服务器上已经累计上传了超过120万个第三方模块。
总的来说,npm就是一个JavaScript模块的集中管理平台,与js相关的几乎所有第三方模块都可以从这里找到。而一旦将你自己的模块上传到npm,就等于将其开放给了所有的JavaScript开发者。
说到npm,就不得不提到cnpm。
在国内,很多人都会使用cnpm命令来安装npm服务器上的第三方包,因为cnpm的下载速度要比npm快。那么cnpm究竟是什么呢?
原来,npm服务器位于国外,我们在国内直接访问npm服务器的速度是相对较慢的,有时甚至会出现安装失败。为了解决这个问题,淘宝的工程师团队在国内也搭建了一个服务器,称为“淘宝npm镜像”(其实就是把npm服务器上的第三方包给同步过来,供开发者下载)。淘宝的npm镜像服务器地址是https://registry.npm.taobao.org,通过以下命令可以安装cnpm:
npm install -g cnpm -registry=https://registry.npm.taobao.org
现在你就已经安装了cnpm,你可以使用cnpm命令去淘宝的服务器上下载从npm服务器上同步过来的第三方模块。
注意:该服务器每10分钟与npm官方服务器同步一次。因此如果你刚刚向npm服务器上传了一个模块,使用cnpm命令不一定能立即下载。
补充说明:现在还有一个常用的包管理工具:yarn。由于在旧版本的npm中存在一些缺陷,比如npm install
安装过慢、版本管理不统一等,Facebook、Google等互联网公司联合推出了新的包管理工具yarn。相比于npm,它速度更快,性能更好,备受互联网大厂欢迎。由于yarn与npm的原理是基本一致的,这里我们就不再详述,感兴趣的可以去了解一下。
这里我们以上传一个vue组件为例,来讲解向npm服务器上传第三方模块的全过程。
本文所要上传的是我编写的一个小的vue组件:vue-image-container。
它是一个可拖拽的悬浮窗,内置了若干个格子,你可以将页面上的某些图片拖拽进去临时存储起来,之后可以把图片拖拽出来放置到目标位置(目前只完成了一个概念版本,功能并不健全,后续会进一步完善)。如图:
当你需要把某张图片拖拽到当前可视区之外(比如你要从网页顶部将一张图片拖拽到底部,而网页却非常长),或者当你从一个页面路由到另一个页面时需要拖拽一张图片过去(单页应用的路由切换不会跨页面,因此理论上是可以跨路由拖拽图片的),你可能就需要这样一个组件了。
好了,简单介绍了要上传的组件之后,下面就是本文的重点:如何向npm服务器上传这个组件。这主要分两大部分,其一是完成组件的开发,向npm上传的模块必须遵循一定的格式,其二是向npm上传该模块。
先来看组件的开发:
我们先使用vue-cli创建一个vue项目(当然,在这之前你需要先安装nodejs和vue-cli,它们的安装不是本文的重点)。在当前文件夹下打开cmd命令行窗口,输入以下命令:
vue create vue-image-container
选择项目的默认配置,回车执行。执行完毕后,当前文件夹下就会出现一个刚刚初始化的空vue项目:vue-image-container。
由于新版的vue-cli删除了默认的配置文件,因此我们在项目下新建一个vue.config.js,此时项目结构如下:
接下来我们要配置一下项目的配置文件,代码如下:
module.exports = {
pages: {
index: {
entry: 'src/main.js,
template: 'public/index.html,
filename: 'index.html
}
},
css: {
extract: false //强制使用内联样式,这样打包出的插件样式会内置在js中,不需要单独引入
}
}
这里是标准的vue.config.js配置。
pages参数指定了项目的入口js文件和要使用的HTML模板。
css参数的extract属性指定在打包时是否要生成单独的样式文件。如果将extract置为true,则项目打包后会生成单独的css文件,在引入的时候就必须同时引入这个css文件才可以,如:
当你的组件有多个主题时,这样做会比较灵活。但如果你的组件只有一套样式,就可以将该参数置为false,此时所有的样式都会被打包成内联样式,开发者在使用的时候就不需要单独引入样式文件了。这里我们暂且只提供一个主题,因此将extract置为false。
一般来说,我们会在src目录下编写本地组件,然后在其他组件中进行调用。但是我们现在要编写的需要上传到npm的第三方组件,我们不希望它和项目的其他代码混在一起(这样不利于打包),因此我们将新建一个packages文件夹来存放这个组件。
我们先按照常规开发组件的方式开发好组件,这与直接在src下编写插件没有什么差别:
<template>
... // 模板
</template>
<script>
... // 脚本代码
</script>
<style>
... // 样式
</style>
注意:我们不止可以向npm上传单个组件,也可以上传一个组件库,这时packages下面可能包含多个vue组件。
开发完插件后,在packages
目录下新建index.js
,作为组件的入口文件。
如果只需要上传单个组件(如本项目),index.js代码如下:
// 导入组件
import imgContainer from './img-container/img-container';
let plugin = {};
plugin.install = function( Vue ){
Vue.component(imgContainer.name, imgContainer); // 注册组件
}
// 当使用Vue.use(plugin)安装插件时,
// 实际上就是在调用这里暴露出的对象的
// install方法,因此务必确保这里带有install方法
export default plugin;
组件的索引文件暴露出了一个plugin对象,该对象有一个install
方法,当调用Vue.use()
安装组件时,实际上就是在调用这个方法。因此你可以像下面这样使用imgContainer
:
本地项目:
// 引入组件暴露出的plugin对象
import imgContainer from 'img-container';
// 执行plugin的install方法
Vue.use(imgContainer);
请注意,即使组件不是从npm下载的,你也可以调用Vue.use()
,只要传入的对象有install方法。
如果你要上传一个组件库,可以像下面一样编写index.js
:
import ... from ''
import ... from '';
... // 将所有的组件引入进来
const components = [...]; // 将上述组件保存到一个数组
// 定义install方法,它将作为export暴露的对象的方法被Vue.use调用
const install = function(Vue){
// 判断当前组件是否已被安装过,如果已安装过则不再安装
if(install.installed) return;
install.installed = true;
// 遍历components数组,依次注册每个组件
components.map(component => {
Vue.component(component.name, component);
})
}
if(typeof window !== 'undefined' && window.Vue){
install(window.Vue);
}
export default{
install,
...components //将插件暴露出去,这样可以按需引入
}
// 如果只需要部分组件,请使用import {button} from '';
// Vue.component(Button)的语法手动注册组件
// 这样便可以实现按需引入
项目中引入组件库的方式与引入单个组件是一致的。
编写完组件后,需要先进行测试,排除组件的bug后才能上传到npm服务器。我们仍然按照常规的方式,在src
文件夹下编写项目代码,引入packages
下面的组件进行测试(我们有时也会把src
文件夹改为test
,表示这是测试组件用的)。
App.vue:
与常规引入组件的方式是一致的,只是路径是packages
文件夹。
这一步是为测试和打包做准备,我们在原package.json的顶部添加如下代码:
package.json:
"name": "vue-image-container", // 组件名
"version": "0.1.4", // 版本号
"description": "img-container", // 组件描述
"main": "lib/vue-img-container.umd.min", // 打包结果文件
"private": false, // 是否为私有项目
"author": "carter_liu",
"license": "MIT",
"style": "lib/vue-img-container.css", // 打包后的样式文件
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"lib": "vue-cli-service build --target lib --name vue-img-container --dest lib packages/index.js" // 打包命令
},
// 下面的配置都是自动生成的,不用动
"dependencies": {
...
},
"devDependencies": {
...
},
...
这里需要特别注意的是,要将组件上传到npm上,private
参数必须置为false
。
另外,如果之前配置vue.config.js时选择生成单独的css文件,则这里的style参数是必须的(否则会找不到样式文件),如果没有单独的css文件,这个参数可以省略。
这里最重要的是scripts
内的lib
命令,调用它可以将模块打包,只有打完包,才能上传到npm服务器。我们来分析一下各个参数的含义:
vue-cli-service build // 调用vue-cli提供的build命令
--target lib // 打包目标是package.json同级目录下的lib文件夹(打包时会自动创建)
--name vue-img-container // 包名
--dest lib packages/index.js // 打包入口是packages文件夹下的index.js
在命令行输入:
npm run lib
然后回车即可进行组件打包。稍等片刻,lib
文件夹下就会生成打包结果。这个lib
就是我们真正要给开发者使用的包(理论上你可以只向npm上传这个文件夹,不过大多数情况下我们会把整个项目上传,便于其他开发者学习和研究)。
在项目根目录下新建.npmignore文件:
这个文件里约定了哪些文件夹是不需要上传到npm服务器上的。一般来说,node_modules文件夹,以及项目的工程文件(如.idea,.vscode)等是不上传的,而lib文件夹是必须要上传的,其他的可以根据需要。
下面是我的配置文件:
node_modules/
vue.config.js
babel.config.js
*.map
*.html
.idea
.vscode
上述文件或文件夹将不会被上传到npm。
至此,我们的组件已经开发和打包完毕,可以准备上传到npm了。要上传到npm,首先需要有一个npm账号。
登录网址:https://www.npmjs.com,可以免费注册一个开发者账号。注册完毕后需要验证邮箱,没有验证邮箱是不能上传模块的。
提示:我用手机端验证了邮箱,发现上传时仍显示未验证邮箱,后来在电脑端验证邮箱才可以正常使用,不知道是不是操作失误。
回到项目文件夹,打开命令行窗口,输入:
npm login
随后需要依次输入用户名、密码(密码是隐藏的,输完直接回车即可)和邮箱。
输入完成,回车即可登录npm账号。
现在我们可以将组件发布到npm服务器上了。执行命令:
npm publish
回车,稍等一会,组件就会被上传到npm服务器上了。
如果你在执行该命令时遇到以下报错:
403 Forbidden - PUT https://registry.npm.taobao.org/vue-image-container - [no_perms] Private mode enable, only ad
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.
这是因为使用了淘宝的镜像服务器(淘宝镜像服务器在上传模块时可能存在一定的权限问题),你可以输入以下命令:
npm config set registry http://registry.npmjs.org/
将上传的服务器地址重置为官方的服务器地址,然后重新执行npm login
和npm publish
,即可成功上传。
关于组件的更新:如果你需要升级该组件,需要先去package.json文件中修改项目的版本号,然后再次执行npm publish
。切记,如果没有修改版本号,npm服务器上包是不会更新的。
此外,npm unpublish ...
命令用于撤销已上传的模块。
本文以一个简单的vue组件为例,讲解了如何开发和上传一个第三方组件到npm服务器。鉴于封装组件(俗称“造轮子”)已经称为一个前端开发者最重要的技能之一,没有尝试过的同学还是尽快尝试一下。