npm 学习笔记
哈喽,大家好!有段时间没有在社区写笔记了,之前说要解析和学习追风少年的个人博客小程序,写了几篇没有写下去,很抱歉,因为比较忙,一直没有时间安静地学习和写代码,最近都没有更新,后续有空我会更新下去的。刚好五一放假,有空写写自己的学习笔记。 今天给大家分享的是一个关于NPM遇到的坑,相信很多朋友都有遇到这个问题,我是第一次知道怎么回事,第二次还会出错,记不住,因为没有充分理解,不知其然也不知其所以然,一知半解,所以我决定彻底搞懂他,然后百度和通过平时自己学习遇到的坑,发现一篇文章写的不错,分享给大家:https://www.cnblogs.com/ssw-men/p/10974010.html,希望对大家理解NPM有所帮助。 结合小程序开发,我重新理了下对npm的理解: NPM的由来(节点包管理器-不懂这样翻译对不对) 现代前端开发已经离不开Node了。大家都知道在安装Node时会附赠一个命令行工具Node Package Manager,即npm。或许你已经照着教程输入过好多遍”npm install xxx”,并且你发现npm的命令林林总总几十条,package.json的配置项令人眼花缭乱,但不知你有没有认真想过,我们为什么需要npm?如果没有它,世界会怎样? 我的理解,npm所做的一切都是为了解决软件工程界一个一直以来的追求:代码复用。抓住这个核心,也就抓住了正确理解和使用npm的钥匙。 为什么要复用代码呢?因为基于已有的成熟代码快速开发新的应用,可以极大地提高开发效率,正所谓“站在巨人肩膀上”“不要重复造轮子”。 So,在Node环境下要复用JS代码,我们有哪些方案呢? 1. 刀耕火种——copy&paste 复制粘贴代码的思路很直接,但如今还在的这样搞的同学应该是从原始社会穿越来的吧。。这个方案最大的缺点倒还不是代码冗余,而是一旦所复制的原始代码发生了变化,那就必须手动修改每一处复本,在稍有规模的项目里根本不可行。 2. 耕牛犁地——CommonJS Node实现了一个模块系统CommonJS,实在是JSer的一大福音。借助它,我们不必再复制粘贴代码了:假如一个作者开发了一个名为lib1的库,他只需代码写在一个名叫lib1.js的文件里,用module.export语句导出;而使用者只需把lib1.js下载到自己工程目录,require一下便可直接用啦!(此时lib1也被称为一个“依赖”) 但这里仍然存在两个大问题: 一,如果lib1.js本身也复用了别的代码,比如lib2.js、lib3.js...那你在下载lib1.js的时候,必须手动把它所依赖的这些模块文件也一并下载;可要是lib2.js还依赖lib5.js、lib6.js....呢?一棵庞大的、深不见底的依赖树很难手工管理。 二、lib1.js的作者修复了几个bug,但没有一个机制能让他通知你升级旧的模块文件。 作为一名职业素养良好的程序员,看到这些问题的第一反应是不是“写个脚本”?哈哈,不用麻烦了,因为已经有人替我们写好了,这个脚本工具就是npm。 3. 机械化耕作——npm Npm制定了一个包规范,所谓规范就是一些格式和约定,比如约定从package.json文件里读取这个包的所有信息,包括它的名字、版本号、它依赖于哪些别的包等;又比如约定node_modules目录专门用来存放第三方依赖,Node为此提供的支持是内置的require方法默认会到这个目录下去检索模块,而无需手动指定路径。有了这些规范,一个包的开发、依赖安装、发布等都步骤都标准化了,省心省力。 可以说,JavaScript从一门“玩具”语言,到如今可以胜任大型项目开发,模块化和npm是其进化路上的重要一步。 如何使用 NPM 安装 npm 不需要单独安装。在安装 Node 的时候,会连带一起安装 npm 。但是,Node 附带的 npm 可能不是最新版本,最后用下面的命令,更新到最新版本。 [代码]$ sudo npm install npm@latest -g[代码](我们的小程序云开发用的就是node.js环境,所以我们不需要再次安装npm,npm -v 就可以查到版本,可以用这个命令更新版本) 如果是 Window 系统使用以下命令即可:[代码]npm install npm -g[代码] [代码]# 查看 npm 命令列表[代码] [代码]$ npm help[代码] [代码]# 查看各个命令的简单用法[代码] [代码]$ npm -l[代码] [代码]# 查看 npm 的版本[代码] [代码]$ npm -v[代码] [代码]# 查看 npm 的配置[代码] [代码]$ npm config list -l[代码] 使用 npm init npm init 用来初始化生成一个新的 package.json 文件。它会向用户提问一系列问题,如果你觉得不用修改默认配置,一路回车就可以了。 如果使用了 -f(代表force)、-y(代表yes),则跳过提问阶段,直接生成一个新的 package.json 文件。 [代码]$ npm init -y[代码] [代码](你可以试试在小程序终端分别输入:npm init 和 npm init -y的区别!就知道哪个更方便了)[代码] npm set npm set 用来设置环境变量 [代码]$ npm set[代码] [代码]init-author-name[代码] [代码]'Your name'[代码] [代码]$ npm set[代码] [代码]init-author-email 'Your email'[代码] [代码]$ npm set[代码] [代码]init-author-url 'https://yourdomain.com'[代码] [代码]$ npm set[代码] [代码]init-license 'MIT'[代码] 上面命令等于为 npm init 设置了默认值,以后执行 npm init 的时候,package.json 的作者姓名、邮件、主页、许可证字段就会自动写入预设的值。这些信息会存放在用户主目录的 ~/.npmrc文件,使得用户不用每个项目都输入。如果某个项目有不同的设置,可以针对该项目运行 npm config。 npm info npm info 命令可以查看每个模块的具体信息。比如,查看 underscore 模块的信息。 [代码]$ npm info underscore[代码] 上面命令返回一个 JavaScript 对象,包含了 underscore 模块的详细信息。这个对象的每个成员,都可以直接从 info 命令查询。 [代码]$ npm info underscore description[代码] [代码]$ npm info underscore homepage[代码] [代码]$ npm info underscore version[代码] npm search npm search 命令用于搜索 npm 仓库,它后面可以跟字符串,也可以跟正则表达式。 [代码]$ npm search <搜索词>[代码] npm list npm list 命令以树形结构列出当前项目安装的所有模块,以及它们依赖的模块。 [代码]$ npm list[代码] [代码]# 加上 global[代码] [代码]参数,会列出全局安装的模块[代码] [代码]$ npm list -global[代码] [代码]# npm list 命令也可以列出单个模块[代码] [代码]$ npm list underscore[代码] npm install 使用 npm 安装包的命令格式为: npm [install/i] [package_name] 本地模式和全局模式npm 在默认情况下会从 https://npmjs.org 搜索或下载包,将包安装到当前目录的 node_modules 子目录下。 如果你熟悉 Ruby 的 gem 或者 Python 的 pip,你会发现 npm 与它们的行为不同,gem 或 pip 总是以全局模式安装,使包可以供所有的程序使用,而 npm 默认会把包安装到当前目录下。这反映了 npm 不同的设计哲学。如果把包安装到全局,可以提供程序的重复利用程度,避免同样的内容的多分副本,但坏处是难以处理不同的版本依赖。如果把包安装到当前目录,或者说本地,则不会有不同程序依赖不同版本的包的冲突问题,同时还减轻了包作者的 API 兼容性压力,但缺陷则是同一个包可能会被安装许多次。 我们在使用 supervisor 的时候使用了 npm install -g supervisor 命令,就是以全局模式安装 supervisor 。 这里注意一点的就是,supervisor 必须安装到全局,如果你不安装到全局,错误命令会提示你安装到全局。如果不想安装到默认的全局,也可以自己修改全局路径到当前路径 npm config set prefix "路径" 安装完以后就可以用 supervisor 来启动服务了。 supervisor 可以帮助你实现这个功能,它会监视你对代码的驱动,并自动重启 Node.js 。 一般来说,全局安装只适用于工具模块,比如 eslint 和 gulp 。关于使用全局模式,多数时候并不是因为许多程序都有可能用到了它,为了减少多重副本而使用全局模式,而是因为本地模式不会注册 PATH 环境变量。 “本地安装”指的是将一个模块下载到当前项目的 node_modules 子目录,然后只有在项目目录之中,才能调用这个模块。 本地模式和全局模式的特点如下: [图片] [代码]# 本地安装[代码] [代码]$ npm install <package name="">[代码] [代码]# 全局安装[代码] [代码]$ sudo npm install -global[代码] [代码]<package name="">[代码] [代码]$ sudo npm install -g <package name=""></package></package></package>[代码] npm install 也支持直接输入 Github 代码库地址。 [代码]$ npm install git://github.com/package/path.git[代码] [代码]$ npm install git://github.com/package/path.git#0.1.0[代码] 安装之前,npm install 会先检查,node_modules 目录之中是否已经存在指定模块。如果存在,就不再重新安装了,即使远程仓库已经有了一个新版本,也是如此。 如果你希望,一个模块不管是否安装过, npm 都要强制重新安装,可以使用 -f 或 –force 参数。(-f 是强制安装 不是false) [代码]$ npm install <packagename> --force</packagename>[代码] 安装不同版本install 命令总是安装模块的最新版本,如果要安装模块的特定版本,可以在模块名后面加上 @ 和版本号。 [代码]$ npm install sax@latest[代码] [代码]$ npm install sax@0.1.1[代码] [代码]$ npm install sax@">=0.1.0 <0.2.0"[代码] install 命令可以使用不同参数,指定所安装的模块属于哪一种性质的依赖关系,即出现在 packages.json 文件的哪一项中。 –save:模块名将被添加到 dependencies,可以简化为参数-S。–save-dev:模块名将被添加到 devDependencies,可以简化为参数-D。(区别是:你项目上线后,仍然需要用的东西用–save;不需要的一些包,只是在开发时借用环境什么的,可以直接用–save-dev!如果不确定用那个,直接-s就对了) [代码]$ npm install sax --save[代码] [代码]$ npm install node-tap --save-dev[代码] [代码]# 或者[代码] [代码]$ npm install sax -S[代码] [代码]$ npm install node-tap -D[代码] dependencies 依赖这个可以说是我们 npm 核心一项内容,依赖管理,这个对象里面的内容就是我们这个项目所依赖的 js 模块包。下面这段代码表示我们依赖了 markdown-it 这个包,版本是 ^8.1.0 ,代表最小依赖版本是 8.1.0 ,如果这个包有更新,那么当我们使用 npm install 命令的时候,npm 会帮我们下载最新的包。当别人引用我们这个包的时候,包内的依赖包也会被下载下来。 [代码]"dependencies": { "markdown-it": "^8.1.0"}[代码] devDependencies 开发依赖在我们开发的时候会用到的一些包,只是在开发环境中需要用到,但是在别人引用我们包的时候,不会用到这些内容,放在 devDependencies 的包,在别人引用的时候不会被 npm 下载。 [代码]"devDependencies": {[代码] [代码] "autoprefixer": "^6.4.0",0",[代码] [代码] "babel-preset-es2015": "^6.0.0",[代码] [代码] "babel-preset-stage-2": "^6.0.0",[代码] [代码] "babel-register": "^6.0.0",[代码] [代码] "webpack": "^1.13.2",[代码] [代码] "webpack-dev-middleware": "^1.8.3",[代码] [代码] "webpack-hot-middleware": "^2.12.2",[代码] [代码] "webpack-merge": "^0.14.1",[代码] [代码] "highlightjs": "^9.8.0"[代码] [代码]}[代码] 当你有了一个完整的 package.json 文件的时候,就可以让人一眼看出来,这个模块的基本信息,和这个模块所需要依赖的包。我们可以通过 npm install 就可以很方便的下载好这个模块所需要的包。 npm install 默认会安装 dependencies 字段和 devDependencies 字段中的所有模块,如果使用 --production 参数,可以只安装 dependencies 字段的模块。 [代码]$ npm install --production[代码] [代码]# 或者[代码] [代码]$ NODE_ENV=production npm install[代码] 一旦安装了某个模块,就可以在代码中用 require 命令加载这个模块。 [代码]var backbone = require('backbone') 是不是跟云函数:[代码]const cloud = require('wx-server-sdk')[代码]很相似。[代码] [代码]console.log(backbone.VERSION)[代码] [代码] [代码]npm runnpm 不仅可以用于模块管理,还可以用于执行脚本。package.json 文件有一个 scripts 字段,可以用于指定脚本命令,供 npm 直接调用。 package.json [代码]{[代码] [代码] "name": "myproject",[代码] [代码] "devDependencies": {[代码] [代码] "jshint": "latest",[代码] [代码] "browserify": "latest",[代码] [代码] "mocha": "latest"[代码] [代码] },[代码] [代码] "scripts": {[代码] [代码] "lint": "jshint **.js",[代码] [代码] "test": "mocha test/"[代码] [代码] }[代码] [代码]}[代码] scripts 脚本顾名思义,就是一些脚本代码,可以通过 npm run script-key 来调用,例如在这个 package.json 的文件夹下使用 npm run dev 就相当于运行了 node build/dev-server.js 这一段代码。使用 scripts 的目的就是为了把一些要执行的代码合并到一起,使用 npm run 来快速的运行,方便省事。 npm run 是 npm run-script 的缩写,一般都使用前者,但是后者可以更好的反应这个命令的本质。 [代码]// 脚本[代码] [代码]"scripts": {[代码] [代码] "dev": "node build/dev-server.js",[代码] [代码] "build": "node build/build.js",[代码] [代码] "docs": "node build/docs.js",[代码] [代码] "build-docs": "npm run docs & git checkout gh-pages & xcopy /sy dist\\* . & git add . & git commit -m 'auto-pages' & git push & git checkout master",[代码] [代码] "build-publish": "rmdir /S /Q lib & npm run build &git add . & git commit -m auto-build & npm version patch & npm publish & git push",[代码] [代码] "lint": "eslint --ext .js,.vue src"[代码] [代码]}[代码] npm run 如果不加任何参数,直接运行,会列出 package.json 里面所有可以执行的脚本命令。 npm 内置了两个命令简写, npm test 等同于执行 npm run test,npm start 等同于执行 npm run start。 [代码]"build": "npm run build-js && npm run build-css"[代码] 上面的写法是先运行 npm run build-js ,然后再运行 npm run build-css ,两个命令中间用 && 连接。如果希望两个命令同时平行执行,它们中间可以用 & 连接。 写在 scripts 属性中的命令,也可以在 node_modules/.bin 目录中直接写成 bash 脚本。下面是一个 bash 脚本。 [代码]#!/bin/bash[代码] [代码]cd site/main[代码] [代码]browserify browser/main.js | uglifyjs -mc > static/bundle.js[代码] 假定上面的脚本文件名为 build.sh ,并且权限为可执行,就可以在 scripts 属性中引用该文件。 [代码]"build-js": "bin/build.sh"[代码] pre- 和 post- 脚本npm run 为每条命令提供了 pre- 和 post- 两个钩子(hook)。以 npm run lint 为例,执行这条命令之前,npm 会先查看有没有定义 prelint 和 postlint 两个钩子,如果有的话,就会先执行 npm run prelint,然后执行 npm run lint,最后执行 npm run postlint。 [代码]{[代码] [代码] "name": "myproject",[代码] [代码] "devDependencies": {[代码] [代码] "eslint": "latest"[代码] [代码] "karma": "latest"[代码] [代码] },[代码] [代码] "scripts": {[代码] [代码] "lint": "eslint --cache --ext .js --ext .jsx src",[代码] [代码] "test": "karma start --log-leve=error karma.config.js --single-run=true",[代码] [代码] "pretest": "npm run lint",[代码] [代码] "posttest": "echo 'Finished running tests'"[代码] [代码] }[代码] [代码]}[代码] 上面代码是一个 package.json 文件的例子。如果执行 npm test,会按下面的顺序执行相应的命令。 1. pretest 2. test 3. posttest 如果执行过程出错,就不会执行排在后面的脚本,即如果 prelint 脚本执行出错,就不会接着执行 lint 和 postlint 脚本。 npm binnpm bin 命令显示相对于当前目录的,Node 模块的可执行脚本所在的目录(即 .bin 目录)。 [代码]# 项目根目录下执行[代码] [代码]$ npm bin[代码] [代码]./node_modules/.bin[代码] 创建全局链接npm 提供了一个有趣的命令 npm link,它的功能是在本地包和全局包之间创建符号链接。我们说过使用全局模式安装的包不能直接通过 require 使用。但通过 npm link 命令可以打破这一限制。举个例子,我们已经通过 npm install -g express 安装了 express,这时在工程的目录下运行命令: [代码]npm link express ./node_modules/express -> /user/local/lib/node_modules/express[代码] 我们可以在 node_modules 子目录中发现一个指向安装到全局的包的符号链接。通过这种方法,我们就可以把全局包当做本地包来使用了。 除了将全局的包链接到本地以外,使用 npm link 命令还可以将本地的包链接到全局。使用方法是在包目录(package.json 所在目录)中运行 npm link 命令。如果我们要开发一个包,利用这种方法可以非常方便地在不同的工程间进行测试。 创建包包是在模块基础上更深一步的抽象,Node.js 的包类似于 C/C++ 的函数库或者 Java、.Net 的类库。它将某个独立的功能封装起来,用于发布、更新、依赖管理和版本控制。Node.js 根据 CommonJS 规范实现了包机制,开发了 npm 来解决包的发布和获取需求。 Node.js 的包是一个目录,其中包含了一个 JSON 格式的包说明文件 package.json。严格符合 CommonJS 规范的包应该具备以下特征: 。package.json 必须在包的顶层目录下; 。二进制文件应该在 bin 目录下; 。JavaScript 代码应该在 lib 目录下; 。文档应该在 doc 目录下; 。单元测试应该在 test 目录下。 Node.js 对包的要求并没有这么严格,只要顶层目录下有 package.json,并符合一些规范即可。当然为了提高兼容性,我们还是建议你在制作包的时候,严格遵守 CommonJS 规范。 我们也可以把文件夹封装为一个模块,即所谓的包。包通常是一些模块的集合,在模块的基础上提供了更高层的抽象,相当于提供了一些固定接口的函数库。通过定制 package.json,我们可以创建更复杂,更完善,更符合规范的包用于发布。 Node.js 在调用某个包时,会首先检查包中 packgage.json 文件的 main 字段,将其作为包的接口模块,如果 package.json 或 main 字段不存在,会尝试寻找 index.js 或 index.node 作为包的接口。 package.json 是 CommonJS 规定的用来描述包的文件,完全符合规范的 package.json 文件应该含有以下字段: name: 包的名字,必须是唯一的,由小写英文字母、数字和下划线组成,不能包含空格。 description: 包的简要说明。 version: 符合语义化版本识别规范的版本字符串。 keywords: 关键字数组,通常用于搜索。 maintainers: 维护者数组,每个元素要包含 name 、email(可选)、web(可选)字段。 contributors: 贡献者数组,格式与 maintainers 相同。包的作者应该是贡献者数组的第一个元素。 bugs: 提交 bug 的地址,可以是网址或者电子邮件地址。 licenses: 许可证数组,每个元素要包含 type(许可证的名称)和 url(链接到许可证文本的地址)字段。 repositories: 仓库托管地址数组,每个元素要包含 type(仓库的类型,如 git)、URL(仓库的地址)和 path(相对于仓库的路径,可选)字段。 dependencies: 包的依赖,一个关联数组,由包名称和版本号组成。 包的发布官方文档https://www.npmjs.cn/getting-started/publishing-npm-packages/ 通过使用 npm init 可以根据交互式回答产生一个符合标准的 package.json。创建一个 index.js 作为包的接口,一个简单的包就制作完成了。 修改package.js的name属性作为发布包的名字。 在发布前,我们还需要获得一个账号用于今后维护自己的包,使用 npm adduser 根据提示完成账号的创建,如果你是在npm网站上注册的账号在终端上使用npm login根据提示登陆。 完成后可以使用 npm whoami 检测是否已经取得了账号,如果登陆已经登陆会显示你的用户名。 如果你的包将来有更新,只需要在 package.json 文件中修改 version 字段,然后重新使用 npm publish 命令就行了。 如果你对已发布的包不满意,可以使用 npm unpublish 命令来取消发布。 需要说明的是:json 文件不能有注释。 下载包自己的包方式和下载其它包时一样,使用npm install packagename就可以了。 可能上面介绍的有些我们用不上或者没理解的地方,没关系,等你需要用到npm 遇到问题的时候 再过来看看 会有新的收获。 由于我不太懂CSS ,但是Vant Weapp 是个好东西:https://youzan.github.io/vant-weapp/#/quickstart#bu-zou-er-gou-jian-npm-bao; 里面的样式可以直接拿来用,大家安装的话记得先:npm init -y(这步初始化操作很重要哦 不然会遇到问题),然后:npm i @vant/weapp -S --production,打开微信开发者工具,点击 工具 -> 构建 npm,并勾选 使用 npm 模块 选项,构建完成后,即可引入组件。(出现:构建失败,大多都是因为没有初始化)。 我也不太懂js,准备用lodash:https://www.lodashjs.com/docs/lodash.after,有兴趣的可以一起学习交流哈!希望能帮助到大家!