发布angualr2模块到npm社区

angualr2项目在本地正常运行后,有个公共模块部分我想抽离出来单独发布到npm上供项目使用,在网上找了些资料,没想到发不到npm比maven也省事的多,本文重点记录将自己的angualr2模块发布到npm的主要步骤。 一、注册账号在NPM网站注册一个账号,这个账号会被添加到npm本地的配置中,用来发布module用。 npm adduser Username: your namePassword: your passwordEmail: xxx@gmail.com二、登录账号npm loginUsername: fuzhutechPassword: fuzhutechEmail: (this IS public) fuzhutech@163.com三、查看当前用户npm whoami四、初始化项目npm init执行“npm init”可以根据提示生成简单的“package.json”,这个这个文件包含了module的所有信息,比如名称、版本、描述、依赖、作者、license等。npm init创建的“package.json”文件只包含了基本的信息,我使用angualr-cli命令创建项目,然后把“package.json”作比对,添加一些缺失的信息。 ng new ng2-fuzhutech-common五、版本管理npm社区版本号,语义版本号分为X.Y.Z三位,即主版号.次版号.修订号。当代码变更时,版本递增规则如下: 主版号:当你做了不相容的 API 修改,例如有大的变动,向下不兼容,需要更新X位; 次版号:当你做了向下相容的功能性新增,例如新增了功能,但是向下兼容,需要更新Y位; 修订号:当你做了向下相容的问题修正,例如只是修复bug,需要更新Z位; 先行版号及版本编译资讯可以加到「主版号.次版号.修订号」的后面,作为延伸。六、发布模块npm publish以上完成之后,我们终于可以发布自己的module了。 七、忽略文件.npmignore“.npmignore”文件用来定义哪些文件会被 npm 给忽略,npm 默认使用 gitignore 来忽略文件。存在 npmignore 的话 gitignore 的配置就会被覆盖。 八、安装模块npm install --save ng2-fuzhutech-common九、取消发布npm unpublish <package>@<version>已发布的版本号并不会被真正的取消,而是被占用,再次发布时需要更改新的版本号,而且npm只能允许撤销24小时以内的发布。

April 22, 2019 · 1 min · jiezi

【Node】CommonJS 包规范与 NPM 包管理

NPM 实践了 CommonJS 包规范规范,帮助我们安装和管理依赖包,使得 Node 项目的第三方模块更加规范便捷,可以在 NPM 平台上找到所有共享的插件。一、CommonJS 包规范CommonJS 包规范的定义分为两部分:用于组织文件目录的包结构和用于描述包信息的包描述文件 package.json。1.1 包结构一个包由相当于一个存档文件,可压缩为.zip 或tar.gz,安装时解压还原。完全符合 CommonJS 包规范的目录包含以下文件:|– .bin // 存放可执行的二进制文件|– lib // 存放 Javascript 文件|– doc // 存放文档|– test // 存放单元测试用例|– package.json // 包描述文件1.2 包描述文件包描述文件 package.json 是一个 JSON 文件,在包的根目录下。NPM 的所有行为都与 package.json 中的字段有关,Node 程序的依赖项也体现在这些字段上。CommonJS 包规范定义了 package.json 中的字段,NPM 实现时对 CommonJS 包规范中的字段也进行取舍和新增,常用字段有:name: 包名称;description: 包描述;keyword: 关键字数组,用于 NPM 分类搜索;repository: 代码托管位置列表;homepage: 当前包的网址;bugs: 反馈 bug 的邮箱或网址;dependencies: 使用当前包所需要的依赖包列表,NPM 根据这个属性自动加载依赖;devDependencies: 后续开发时需要安装的依赖包列表;main: 模块入口,使用require()引入时优先检查该字段。如果 main 字段不存在,Node 按照模块文件定位的规则依次查找包目录下的 index.js、index.node、index.json;scripts: 包管理器用来安装、编译、测试包的命令对象。bin: 配置包的 bin 字段后,可以通过npm install package_name -g将包添加到执行路径中,之后可以“全局使用”。二、npm 管理NPM 帮助 Node 完成第三方模块的发布、安装和依赖。可以直接执行$ npm 查看所有命令。使用$ npm init 可以快速生成一个 package.json 文件。2.1 npm install 原理使用 npm install安装依赖包是 NPM 最常用的功能,例如执行npm install express后,npm 向 registry 查询模块压缩包的网址,下载压缩包后 NPM 会在当前的 node_module 目录下创建 express 目录,将包解压还原在此。registry 是 NPM 模块仓库提供了一个查询服务,例如 npmjs.org 的查询服务网址 https://registry.npmjs.org/ ,加模块名 https://registry.npmjs.org/vue 就得到包含 Vue 模块所有版本的信息 JSON 对象,也可以使用$ npm view vue查询。Node 项目使用require(’express’)引入 express 模块时,require()方法在路径分析时按照模块路径查找策略,沿当前路径向上逐级查找node_module目录,最终定位到 express 目录。包的安装和模块引入是相辅相成的, 可以进一步理解 Node 模块加载原理2.2 npm install 使用npm install默认将包和 package.json 的依赖关系保存在dependencies,但在可以通过一些额外的标志来控制它们的保存位置和方式:-P or –save or –save-prod: 依赖在dependencies,默认值.-D or –save-dev: 依赖在 devDependencies.-O or –save-optional: 依赖在 optionalDependencies.–no-save: 防止包依赖保存在 dependencies.例如 npm install express -D 就会将 express 依赖关系保存在devDependencies。在npm install一个模块时经常纠结要安装在devDependencies还是dependencies,从字面意思看前者用于生产环境,后者用于开发环境。在官方的定义中,如果环境变量 NODE_ENV 设置为 production,执行 npm install –production 时 npm 会默认安装dependencies里面的依赖项,不会去安装devDependencies里的。并且推荐dependencies里配置正式运行时必须依赖的插件,devDependencies通常用来放我们开发或测试的工具,比如 Webpack,Gulp,babel,eslint等。在实际开发过程中,Node 包的安装是依据 require/import 模块机制,无论是-P还是-D指令都会把依赖下载到 node_modules 文件夹,-P还是-D只是修改了dependencies对象,在安装这个库进行开发调试的时候,可以通过npm install一键安装这两个目录下所有的依赖。2.3 全局安装使用 -g或 –global可以将包安装为“全局可用”,但需要注意的是,全局安装并不意味将模块包安装为一个全局包,也不是可以在任何地方都可以require()引入。实际上-g命令是将模块包安装在“全局”的node_module中,即 Node 可执行文件相同的路径下,并通过配置 bin 字段链接。例如使用命令行查看 Node 可执行文件的位置:$ which node/usr/local/bin/node那么全局安装模块的实际位置就是/usr/local/lib/node_modules(在 Finder 中用 command+shift+G 快捷键访问隐藏目录 )进一步了解 NPM 的使用可以看 NPM DOCS,NPM更多命令 NPM CLI继续加油哦永远十八岁的少女~ ...

April 16, 2019 · 1 min · jiezi

npm ERR! Invalid name

$ npm init -ynpm ERR! Invalid name: “03 install methods webpack"npm ERR! A complete log of this run can be found in:npm ERR! C:\Users\Administrator\AppData\Roaming\npm-cache_logs\2019-04-13T09_18_06_413Z-debug.log报错原因:在"name"字段中不允许使用 大写字母、空格和中文 package.json。

April 13, 2019 · 1 min · jiezi

使用 Webpack1.x 搭建 (MultiPage Application,MPA) 基础框架

初始化项目webpack官方文档:https://www.webpackjs.com/con…github项目地址:https://github.com/Zhanghongw…项目搭建简单记录一下命令,网上有很多文章不多做介绍。希望可以提供一个基础的架构。持续更新……..执行命令// 全局安装 webapck、webpack-cli, 之前安装过忽略此步骤npm install webpack -g npm install webpack-cli -g初始化 npmnpm init项目目录结构+dist+src++assets+++images++common++page++view安装相关依赖,注意版本npm install xxx@版本号 –save-dev{ “css-loader”: “^2.1.1”, “extract-text-webpack-plugin”: “^1.0.1”, “html-loader”: “^0.4.5”, “html-webpack-plugin”: “^2.28.0”, “style-loader”: “^0.23.1”, “url-loader”: “^0.5.8”, “webpack”: “^1.15.0”, “webpack-dev-server”: “^1.16.5” }webpack.config.js 配置文件var path = require(‘path’);var webpack = require(‘webpack’);var ExtractTextPlugin = require(’extract-text-webpack-plugin’);var HtmlWebpackPlugin = require(‘html-webpack-plugin’);// 获取html-webpack-plugin参数的方法 var getHtmlConfig = function(name){ return { template : ‘./src/view/’ + name + ‘.html’, filename : ‘view/’ + name + ‘.html’, inject : true, hash : true, chunks : [‘common’, name] };};var config = { entry: { ‘common’:[’./src/common/index.js’], ‘index’:[’./src/page/index/index.js’], ‘home’:[’./src/page/home/home.js’] }, output: { path: path.resolve(__dirname, “dist”), publicPath: “/dist”, filename: ‘js/[name].js’ }, module: { loaders: [ // 处理 css { test: /.css$/, loader: ExtractTextPlugin.extract(“style-loader”,“css-loader”) }, // 处理图片 { test: /.(gif|png|jpg|jpeg)??.*$/, loader: ‘url-loader?limit=100&name=resoure/[name].[ext]’ } ] }, plugins: [ new webpack.optimize.CommonsChunkPlugin({ // 公共模块提取 name : ‘common’, filename : ‘js/base.js’ }), // 把css单独打包到文件里 new ExtractTextPlugin(“css/[name].css”), // html 模板处理 new HtmlWebpackPlugin(getHtmlConfig(‘index’)), new HtmlWebpackPlugin(getHtmlConfig(‘home’)) ]};module.exports = config; ...

April 13, 2019 · 1 min · jiezi

像babel那样使用lerna管理你的项目

如何像 babel 那样进行多包管理呢?babel 项目的 packages 里面存放了 babel 旗下的所有包,比如:babel-core。我们在安装 babel 的时候一般不是这样装的:yarn add babel,而是这样安装的:yarn add @babel/babel-core,这是由于 babel 进行了分包管理的缘故,又因为 npm 支持scope packages,所以我们可以这样子来安装包。我们再来看scope packages,npm 支持以@符开头的包名称,把它叫做有范围的包,示例:@somescope/somepackage。但是在发布包的时候,npm 会把这种包当成是你的私有包来进行发布,一般我们是发布不了的,因为 npm 会提示需要登录等等东西,这个时候我们一般就加上一个参数,告诉 npm 我们要发布的这个包是一个公共包:npm publish –access=public,不出意外这个包就可以发布成功了。别人要安装你的包:yarn add @somescope/somepackage,看起来跟安装 babel-core 一致了。但是还差点东西,就是利用 lerna 来进行管理。要达到类似于 babel 那样的管理方式,首先,你需要有scope packages,正好,我们现在有了一个@somescope/somepackage包,现在我们就用 lerna 来管理它。首先,我们在 github 上面新建一个仓库,就叫做:testlerna 吧(使用已有仓库也行),然后克隆到本地,进入到 testlerna 目录,执行:lerna init或者是lerna init -i(表示 packages 下面的包单独使用版本号),这个时候,lerna 就为我们生成了这几个东西:testlerna|-package.json|-lerna.json|-packages/packages 目录就是 lerna 要进行管理的各种包的目录,所有的包都会放在这个目录下面。接着执行:lerna import ../scopepackage(假设咱们的这个@somescope/somepackage包是放在 scopepackage 目录里面的),这样就会把我们刚才发布的那个包导入到 packages 目录里面,这时候目录看起来像这个样子:testlerna|-package.json|-lerna.json|-packages/|–scopepackage/|—package.json|—…做一些修修改改并执行 git commit 后,我们就可以执行lerna publish了,不出意外你的@somescope/somepackage也在 npm 上更新了,原来的那个@somescope/somepackage仓库可以删除掉了,因为我们把它放在 testlerna 项目中进行管理了,这样就跟 babel 那样的管理方式一样了。这种方式是有一丢丢麻烦,因为 npm 默认会把 @ 开头的包当成私有包来看待,就导致了如果不在 npm 上提交一次公共版本,执行lerna publish时就会发布不了,可能lerna publish调用的 npm 命令是npm publish它没有加–access=publish参数。如果你不想这么麻烦,那么有一个办法就是,不用scope package,也就是你的包名不加 @ 前缀,这样你的包就默认是公共的包了,完全可以不用先去提交一次公共版本,才能使用 lerna 来管理你的包,简单多了。可以访问:https://github.com/kybetter/t… 看到这个测试项目这两个是测试时发布的包:https://www.npmjs.com/package…https://www.npmjs.com/package… 如果这篇文章给你带来帮助,可以给我点个赞。 ...

April 9, 2019 · 1 min · jiezi

npm源

使用方法安装原始 npmnpm i -g express –registry https://registry.npm.taobao.org安装 cnpmnpm i -g cnpm –registry=https://registry.npm.taobao.org//卸载npm install -g cnpm –registry=http://xxxxxxxxx//如果卸载不了 就用这个npm uninstall cnpm -g 查看配置npm config list //查看基本配置 npm config list -l //查看所有配置获取全局安装默认目录npm config get prefix设置全局安装的默认目录npm config set prefix “directory”

April 9, 2019 · 1 min · jiezi

npm 发布 vue 组件

创建 vue 组件1、创建vue项目,为了简洁方便,推荐使用webpack-simple构建一个项目vue init webpack-simple your-project2、在 src 目录下新建子目录lib,用来放后面自己写的所有组件3、编写自己的组件首先新建一个vue文件和一个index.jsvue用于编写组件的模版和逻辑,像平时封装插件一样ps:需要给组件命名 name: xxx,用于后面的导出具体如下:index.js 作用是将该组件作为 Vue 插件,注册到 Vue 中/** index.js */import xTable from ‘./x-table.vue’;xTable.install = Vue => Vue.component(xTable.name, xTable);//.name就是开始说的vue文件暴露出来的name名,ldcPagination整个组件export default xTable4、最后集中管理,全部导出,可同时编写多个组件组成组件库,一起导出在App.vue同级目录我新建了一个index.js文件// 导入所有组件import xTable from ‘./table/index.js’import xTableCol from ‘./table-column/index.js’const components = [xTable, xTableCol, hello]const install = function(vue) { / istanbul ignore if / if (install.installed) return; /eslint-disable/ components.map((component) => { vue.component(component.name, component); //component.name 此处使用到组件vue文件中的 name 属性 });};/ istanbul ignore if */if (typeof window !== ‘undefined’ && window.Vue) { install(window.Vue);};export default { install, xTable, xTableCol}ps:如果是单个组件也可以不用编写第3中的与组件vue文件同级的index.js,直接在这里import引入,然后导出5、修改配置文件打包之前,首先我们需要改一下 webpack.config.js 这个文件// … 此处省略代码 // 执行环境const NODE_ENV = process.env.NODE_ENVmodule.exports = { // 改变入口 entry: ‘./src/lib/index.js’, output: { // 修改打包出口,在最外级目录打包出一个 index.js 文件,我们 import 默认会指向这个文件 path: path.resolve(__dirname, ‘./dist’), publicPath: ‘/dist/’, filename: “x-vue-element-table.min.js”, library: “xEleTable”, // 指定的就是你使用require时的模块名 libraryTarget: ‘umd’, // libraryTarget会生成不同umd的代码,可以只是commonjs标准的,也可以是指amd标准的,也可以只是通过script标签引入的 umdNamedDefine: true // 会对 UMD 的构建过程中的 AMD 模块进行命名。否则就使用匿名的 define }, // … 此处省略代码 }修改 package.json 文件(主要):// … 此处省略代码 // 发布开源因此需要将这个字段改为 false"private": false,// 这个指 import xEleTable 的时候它会去检索的路径"main": “dist/vue-element-table.min.js”,// … 此处省略代码 还有其他可配置项(非必须)“bugs”: { “url”: “https://github.com/xxx/xxx/issues"},"repository": { “type”: “git”, “url”: “git@https://github.com/xxx/xxx.git”},5、测试,测试这些配置是否OK以及组件是否能够正常运行首先通过命令npm run build打包,打包完成后产生dist文件,这个打包之后的文件的文件名是在webpack.config.js接着执行命令npm pack,会产生一个压缩包,在与webpack.config.js同一级目录然后可以本地新建一项目测试是否可用在测试项目中,通过npm install 组件项目打包后的本地绝对路径/文件全名添加依赖在main.js入口文件引入即可import 名称 from ‘组件库名’Vue.use(名称);具体可参考import xEleTable from ‘x-vue-element-table’Vue.use(xEleTable)然后在项目的任意组件中引用就行了,如果引入成功,则说明打包成功,然后在发布发布到 npm准备工作OK,现在开始正式发布//首先去npm官网注册一个npm账号npm login // 登陆你的用户,密码npm publish // 进行发布在这个过程中,可能会出些问题,遇到具体问题可自行百度解决~如果遇到以下这个错误npm ERR! You do not have permission to publish “nodenpm”. Are you logged in as the correct user? : nodenpm可能是自己的组件名与npm中已有的组件名冲突,需要换成另一个还未被使用过的另外提供几个常见错误解决方案(来自网络,有些自己遇到过~)npm adduser报错Unable to authenticate?npm publish项目报错具体源码:x-vue-element-table源码参考:https://blog.csdn.net/qq_4051…https://juejin.im/post/5b45df...https://blog.csdn.net/cscscss...https://blog.suzper.com/2017/… ...

April 7, 2019 · 1 min · jiezi

gitbook 入门教程之插件介绍

插件是 gitbook 的扩展功能,很多炫酷有用的功能都是通过插件完成的,其中插件有官方插件和第三方插件之分.推荐官方插件市场 https://plugins.gitbook.com/ 寻找或下载相应的插件.当然也可以去 npm 市场搜索 gitbook 插件,根据 gitbook 插件规范, gitbook-plugin-<name> 是功能插件,gitbook-theme-<name> 是主体插件.如果没有按照规范命名,还是直接百度搜索吧!npm 安装后再 gitbook 安装语法格式: npm install gitbook-plugin-<name>安装到本地: npm install gitbook-plugin-advanced-emoji激活安装插件: 配置 book.json 中 plugins 节点安装到项目: gitbook install启动并测试测试: gitbook serve示例:# 安装 gitbook-plugin-advanced-emoji 插件$ npm install gitbook-plugin-advanced-emoji# 安装 gitbook-plugin-advanced-emoji 插件$ gitbook installnpm 安装速度慢的话,可以使用 cnpm 加速安装(npm install cnpm),表情插件下载地址 Advanced Emojigitbook 直接安装语法格式: gitbook install激活安装插件: 配置 book.json 中 plugins 节点安装到项目: gitbook install启动并测试测试: gitbook serve示例:# 安装 gitbook-plugin-advanced-emoji 插件$ gitbook install表情插件下载地址 Advanced Emoji插件示例Advanced Emoji表情列表Advanced Emoji下载地址book.json 配置文件:“plugins”: [ “advanced-emoji”]安装插件:$ gitbook install使用示例:<!– ignore:advanced-emoji:start –>:bowtie:😆 ☺️<!– ignore:advanced-emoji:end –> ...

April 5, 2019 · 1 min · jiezi

node版本管理神器-nvm

使用nvm管理node版本和npm版本,这里不再详述,直接提供链接资料,里面有详细安装教程和使用教程第一个是官方地址官方文档github第二个是菜鸟教程的一篇文章菜鸟教程nvm文章

April 4, 2019 · 1 min · jiezi

如何正确设置npm start PORT端口(windows/mac)

如何正确设置PORT端口(windows/mac)有部分同学会遇到以下错误(运行nodejs项目/文件),这是由于不同系统,所执行的命令代码有所差异。9418292-5f061b4ec255bdcf.pngMAC/linux环境:$ PORT=8081 npm start使用上面命令每次都需要重新设置如果想设置一次永久生效,使用下面的命令。$ export PORT=8081 $ npm startWindow环境Window系统环境,按照顺序这样进行:set PORT=8081npm start注意:关闭命令行窗口后,端口的配置会失效

April 2, 2019 · 1 min · jiezi

如何发布npm包,删除npm包

发布npm包注册注册并在本机添加npm用户(已注册可忽略)完成了上面的步骤之后,我们接下来要在www.npmjs.com注册一个账号,这个账号会被添加到npm本地的配置中,下面命令行将会使用到。//前提已完成npm用户的注册$ npm adduserUsername: your namePassword: your passwordEmail: yourmail@gmail.com如果出现以下错误,可能是你的npm版本太低,通过sudo npm install -g npm升级一下。npm WARN adduser Incorrect username or passwordnpm WARN adduser You can reset your account by visiting:npm WARN addusernpm WARN adduser http://admin.npmjs.org/resetnpm WARN addusernpm ERR! Error: forbidden may not mix password_sha and pbkdf2npm ERR! You may need to upgrade your version of npm:npm ERR! npm install npm -gnpm ERR! Note that this may need to be run as root/admin (sudo, etc.)成功之后,npm会把认证信息存储在~/.npmrc中,并且可以通过以下命令查看npm当前使用的用户:$ npm whoami以上完成之后,我们终于可以发布自己包了。发布$ npm publish 删除npm包npm unpublish –force //强制删除npm unpublish guitest@1.0.1 //指定版本号npm deprecate //某些情况 ...

April 2, 2019 · 1 min · jiezi

Node.js中package.json中库的版本号

~和^的区别最近总是碰到一些问题, 在本地好好的, 在线上就出现了问题, 本地也一直复现不了, 后来把node_modules目录删除了之后, 重新安装, 就在本地复现了这个问题,可以看了git history, 并没有人修改package.json中的版本号,于是认真的了解了一下package.json中库的版本号;和^的区别 “babel-loader”: “^7.1.1”, “body-parser”: “1.15.2"npm install –save xxx, 会优先考虑使用 ^而不是以版本号x.y.z为例x:主版本号, 当你做了不兼容的API修改y:次版本号, 当你做了向下兼容的功能性问题z:修订号, 当你做了向下兼容的问题修复x.y.z, 会更新到y最新的版本, 例如 body-parser: ~1.15.2, 这个库会去匹配到1.15.z的最新版本, 如果出现了1.16.0, 则不会自动升级^x.y.z, 会更新到x的最新版本, 例如 babel-loader: ^7.1.1, 这个库会去匹配7.y.z的最新版本, 如果出现了8.1.1, 则不会自动升级可以参考npm官方给出的解释^1.2.3 := >=1.2.3 <2.0.0^0.2.3 := >=0.2.3 <0.3.0^0.0.3 := >=0.0.3 <0.0.4大多数情况下遵循这种版本号规则的依赖包都没问题, 但是npm是开源的世界, 并不是所有的都严格遵循这种规则, 所以会出现上述的问题;为什么需要package锁有如下几个可能原因, 在某些情况下, package.json是无法保证每个人自己电脑上执行的 npm install 后安装的依赖版本都是一样的1.如果package.json中记录的依赖包的版本是一个版本范围, 一旦执行npm i 会导致这个包更新到最新版本2.就算你依赖了一个固定版本的包(如A 1.1.1), 但你依赖的包A可能依赖其他的包B,而A在声明依赖时可能也使用了semser命名, 如 ^1.2.3, 如果包B release 了新版, 也会导致包B会安装到更新版本3.不同人使用的npm程序的版本不同如果依赖包的版本不一致, 会导致开发环境和生产环境产品不一致的行为; 或者导致不同团队成员之前也产品环境差异如何解决包版本不一致的情况1.npm 使用package-lock.json文件来解决这个问题执行npm install会自动生成package.json文件, 只要执行普通的安装, 更新等可能会修改 package.json的npm命令, 都会自动同步修改package-lock.json文件npm install xxxnpm rm xxxnpm update xxx2.npm 还支持npm-shrinkwrap.json, 和package-lock.json功能完全一样执行 npm shrinkwrap来生成npm-shrinkwrap.json此命令将根据 package-lock.json 文件创建一个新的或覆盖已有的 npm-shrinkwrap.json 文件。 此命令创建和更新的文件将优先于任何其他现有或将有的 package-lock.json 文件。3.使用yarn使用yarn主要有一下优点快速: 会缓存它下载的每个包,无需重复下载;能并行化操作以最大资源利用率可靠:使用格式详尽而又简洁的 lockfile文件 和确定性算法来安装依赖,能够保证在一个系统上的运行的安装过程也会以同样的方式运行在其他系统上。安全: 安装包被执行前校验其完整性yarn速度比npm快一些, yarn的锁文件是yarn.lock, 能解决包版本不一致的情况 ...

April 2, 2019 · 1 min · jiezi

微信小程序npm安装第三方包(引用第三方插件avtv f2.js)

由于我要使用微信小程序引用图标插件,就以AntV F2插件为例;AntV F2官网:https://antv.alipay.com/zh-cn…需要准备:微信开发工具(必须支持npm功能); node.js安装; npm基础知识;以下操作是node.js已经安装过了。1、先建好小程序模版。比如下图:然后使用dos命令打开这个当前的文件夹。比如下图:注意:dos命令打开的是小程序文件夹所放的路径。2、初始化指令如果是该文件夹第一次使用: 请先使用指令npm init(初始化指令);如下图:如果出现这种情况就对了,进行对这个文件夹进行编辑,也可以全部点击回车键,一路回车;3、再次安装npm install –production建议使用–production选项,可以减少安装一些业务无关的 npm 包,从而减少整个小程序包的大小如下图:4、安装微信小程序 F2 图表组件npm i @antv/f2-canvas如下图:恭喜你,操作完以上这几步已经安装好依赖包。5、安装好依赖包之后,打开微信开发工具点击开发者工具顶部详情,勾选 使用npm模块,再点击菜单栏中工具下的构建npm即可运行操作图第一步:点击详情,勾选npm模块操作图第二步:工具菜单栏打开,点击构建npm点击构建npm后,会出现:完成后会出现:一个文件夹为miniprogram_npm恭喜自己吧,npm安装第三方包已经完成;以下就是在小程序中代码的撰写:1.index.json;2.index.wxml;3.index.wxss;#myCanvas { width: 100%; height: 300px;}4.index.js;let chart = null;function initChart(canvas, width, height, F2) { const data = [ { year: ‘1951 年’, sales: 38 }, { year: ‘1952 年’, sales: 52 }, { year: ‘1956 年’, sales: 61 }, { year: ‘1957 年’, sales: 145 }, { year: ‘1958 年’, sales: 48 }, { year: ‘1959 年’, sales: 38 }, { year: ‘1960 年’, sales: 38 }, { year: ‘1962 年’, sales: 38 }, ]; chart = new F2.Chart({ el: canvas, width, height }); chart.source(data, { sales: { tickCount: 5 } }); chart.tooltip({ showItemMarker: false, onShow(ev) { const { items } = ev; items[0].name = null; items[0].name = items[0].title; items[0].value = ‘$ ’ + items[0].value; } }); chart.interval().position(‘yearsales’); chart.render(); return chart;}Page({ data: { opts: { onInit: initChart } }, onLoad(){ }, onReady() { }})看右侧是不是已经出来柱形图了,如果需要做其他图表,去官网让选择就行了,官网的例子比较多,选择做自己需要的就行了。 ...

April 1, 2019 · 1 min · jiezi

gitbook 入门教程之环境要求

gitbook 是基于 node.js 的命令行工具,首先需要安装并配置好 node.js 环境,然后才能安装gitbook 相关工具.由于安装工具全部都是国外网站,因此速度可能会很慢,也可能需要FQ,请耐心等待或者学会科学上网.当然如果安装过程中遇到任何问题,也可以找我要一下安装包或者我帮你免费解决下.环境预检查检查 git 环境[可选]git 是免费开源的分布式版本控制系统,主要用于电子书的更新管理和团队协作,如果不需要将电子书托管到github 网站上,则可以不安装 git .如果打印出 git 版本信息,则表示本机已安装 git 环境,跳过此步骤.$ git –versiongit 安装配置教程请参考初识 git检查 node.js 环境[必须]node.js 是 js 在服务端运行的环境基础,从而使得 js 从浏览器端延伸到服务端领域,而 gitbook 则是运行在 node.js 基础之上的命令行工具,因此必须先安装好 node.js 开发环境.如果打印出 node.js 版本信息,则表示本机已安装 node.js 环境,跳过此步骤.$ node -vnode.js 安装配置教程请参考 https://nodejs.org/检查 gitbook 环境[必须]gitbook-cli 是 gitbook 的脚手架工具,帮助我们更方便构建 gitbook 应用,当然也可以直接安装 gitbook ,只不过那样的话,略显麻烦,不推荐.如果打印出 gitbook 和 cli 版本信息,则表示本机已安装 gitbook 环境,跳过此步骤.$ gitbook -V否则的话,本机可能并没有安装 gitbook 环境,则需要安装 gitbook 相关工具.因为 gitbook 是基于 node.js 环境,而安装好 node.js 后默认提供了 npm 包管理工具,而我们则是通过 npm 来安装其他工具.安装 gitbook-cli 工具[必须]假设你已经搭建好 node.js 环境,现在我们开始安装 gitbook 相关工具了!$ sudo npm install -g gitbook-cli全局安装的话,可能需要超级管理员权限,输入下相应密码即可继续安装,如无报错,则表示安装成功.安装成功后会带有 gitbook 命令,现在再次运行下 gitbook -V 查看版本信息.# 打印出 CLI 和 GitBook 版本信息即可,安装版本可能已经大于 2.3.2$ gitbook -VCLI version: 2.3.2GitBook version: 3.2.3$ 安装 GitBook Editor 编辑器[可选]gitbook 官方客户端编辑器,支持 windows, mac 和 linux ,主要用于可视化编辑文档,组织文档结构.下载相应平台的 GitBook Editor,正常安装即可.gitbook 的使用方法大致可以有三种,而 GitBook Editor 编辑器只是其中一种,所以这一步是可选的.使用 gitbook-cli 脚手架提供的各种命令直接在命令行管理 gitbook,适合一定编程经验的软件从业人员.使用 GitBook Editor 编辑器管理 gitbook ,适合无任何编程的文学创作者.使用 gitbook.com 官网在线管理 gitbook ,适合不具备本地开发环境的萌新体验者.小结gitbook 基于 node.js 开发环境,因此首先要安装好 nodejs 环境,其次再使用 node.js 提供的 npm 包管理工具来安装 gitbook.只需运行 sudo npm install -g gitbook-cli 即可安装,接着运行 gitbook -V 查看安装版本信息确认已经安装成功.至此 gitbook 的必要开发环境已经准备妥当,接下来让我们赶紧体验一下 gitbook 的魅力吧! ...

April 1, 2019 · 1 min · jiezi

npm 语义化版本控制

NPM 版本控制为了在软件版本号中包含更多意义,反映代码所做的修改,产生了语义化版本,软件的使用者能从版本号中推测软件做的修改。npm 包使用语义化版控制,我们可安装一定版本范围的依赖,npm 会选择和你指定的版本相匹配的最新版本安装。npm 的版本号由三部分组成:主版本号、次版本号、补丁版本号。变更不同的版本号,表单不同的意义:主版本号(major):软件做了不兼容的变更(breaking change 重大变更);此版本号(minor):添加功能或者废弃功能,向下兼容;补丁版本号(patch):bug 修复,向下兼容。有时候为了表达更加确切的版本,还会在版本号后面添加标签或者扩展,来说明是预发布版本或者测试版本等。比如 3.2.3-beta-3。常见的标签有 :标签意义补充demodemo版本可能用于验证问题的版本dev开发版开发阶段用的,bug 多,体积较大等特点,功能不完善alpha版本用于内部交流或者测试人员测试,bug较多beta测试版(版本)较版本,有较大的改进,但是还是有buggamma()伽马版本较和版本有很大的改进,与稳定版相差无几,用户可使用trial试用版本本软件通常都有时间限制,过期之后用户如果希望继续使用,一般得交纳一定的费用进行注册或购买。有些试用版软件还在功能上做了一定的限制。stable稳定版 csp内容安全装版js库常用latest最新版本不指定版本和标签,默认安装新版更多关于标签的内容查看标签:npm dist-tags ls <pkg>npm dist-tags ls vue得到:beta: 2.6.0-beta.3csp: 1.0.28-csplatest: 2.6.10安装带标签的版本npm i <pkg>@<tag>npm i vue@beta # 安装 2.6.0-beta.3版本号变更规则版本号只升不降,不得在数字前加0,比如 2.01.2 不允许的;0.y.z,处于开发阶段的版本;第一个正式版版本往往命名为 1.0.0;先行版本必须在补丁版本之后添加,比如 2.3.7-0版本的比较依次比较主版本→次版本→补丁版本→先行版本,直到第一个能得出比较结果为止。不小心把一个不兼容的改版当成了次版本号发行了该怎么办?一旦发现自己破坏了语义化版本控制的规范,就要修正这个问题,并发行一个新的次版本号来更正这个问题并且恢复向下兼容。即使是这种情况,也不能去修改已发行的版本。如何处理即将弃用的功能?弃用现存的功能是软件开发中的家常便饭,也通常是向前发展所必须的。但当你弃用公共API的一部分时,你应该做两件事:(1)更新文档以便使用者知道这个变化。(2)发行不包含弃用功能的副版本。在新主版本中完全移除弃用功能前,至少应有一个不包含弃用功能的副版本发布,以便使用者能够平滑过渡到新API。如何更新版本号?不用手动修改 package.json。而是用如下命令:npm version [<newversion> | major | minor | patch | premajor | preminor | prepatch | prerelease | from-git]newversion: 直接给一个版本号;major:主版本增加1;premajor:预备主版本,主版本增加1,增加先行版本号;prelease:预先发布版本,先行版本号增加1;git 和 npm version 结合手动更改版本号执行 npm version <version> -m ‘xx %s xx’ 改变npm版本的同时,会执行一次 git commt -m ‘xx %s xx’并用版本号打一个tag,%s 会替换成版本号,前提是版本库是干净的(clean)。自动更新版本在 .git/hooks目录内,新建post-commit,输入以下内容:#!/bin/shCOMMIT_MSG="$(git log –pretty=format:"%s" -1 head)“echo “$COMMIT_MSG” | grep -q “^[0-9]“if [ $? -ne 0 ];then # 自动修改 patch echo $(npm version patch)fi在执行 git commit -m ‘message’后,会检测message是否是版本号(y.x.z的形式),不是,则执行 npm version patch更新补丁版本,打一个tag。如果想自动修改次版本,post-commit 的内容即可。版本运算符版本运算符指定了一定范围的版本。主要有~、^、-、<、<=、>、>=、=版本运算符。~ 版本号 —– 指定主版本号或者次版本号相同~ + 只含主版本 — 主版本相同;~ + 含有次版本 — 主版本和次版本号相同。版本范围匹配版本33.x 或者 3.0.0 <= v < 4.0.03.13.1.x 或者 3.1.0 <= v <3.2.0~3.1.23.1.2 < v < 3.2.0指定的版本范围含有预发布版本,只会匹配和完整版本号相同的预发布版本。~3.1.3-beta.2 匹配 3.1.3-beat.3 不匹配 3.1.4-beat-2npm i lodash@~3 # 安装 3.10.1npm i lodash@~3.9 # 安装 3.9.3npm i lodash@3.9.1 # 安装 3.9.3npm i lodash@3.8.0 # 安装 3.8.0^ 版本号 — 第一个非零 版本号相同版本范围匹配版本补充^3.1.53.1.5 <= v < 4.0.0^0.3.60.3.6 <= v < 0.4.0^0.0.20.0.2 <= v < 0.0.3^3.x.x3.0.0 <= v < 4.0.0版本号缺少的位置,会被 0 填充^4.2.x4.2.0 <= v < 4.3.0 npm 安装包时,默认使用 ^ 匹配版本。安装主版本号为 3 的最新版本:npm i lodash@^3 # 安装 3.10.1npm i lodash@^3.9 # 安装 3.10.1npm i lodash@^3.8.0 # 安装 3.10.1 vs ^版本范围含义匹配的版本说明3.3.0与3.3.0相似3.3.0 <= v < 3.4.0主版本和次版本相同^3.3.0与3.3.0兼容3.3.0 <= v < 4主版本相同同一个版本号,^ 能匹配的范围大些,更加激进。例子npm i lodash@^3.3.0 # 安装 3.10.1npm i lodash@3.3.0 # 安装 3.3.1 和 ≈ 差不多,可将 ~ 理解成相似,这样就分辨了和理解了,~指定的是相似版本。^ 可理解成兼容版本。- 指定精确范围版本范围匹配版本补充2.0.0 - 3.2.72.0.0 <= v <= 3.2.7- 前后有空格0.4 - 30.4.0 <= v <= 3.0.0缺少的版本号,被 0 填充npm i vue@“1 - 1.9” # 安装 1.0.28版本号比较器版本范围匹配版本补充<2.2.0小于2.2.0 的版本 <=2.0.0小于等于 2.0.0 的版本>4.2.0大于4.2.0 的版本>=4.2.0大于等于 4.2.0 的版本=4.3.0等于 4.3.0 的版本\ 是转义字符。npm i lodash@&lt;3.5 # 安装 3.4.0npm i lodash@&lt;=3.5 # 安装 3.5.0npm i lodash@&gt;3.5 # 安装 4.17.11npm i lodash@&gt;=3.5 # 安装 4.17.11npm i vue@">1 <2.3” # 安装 2.2.6分组 ||以或者的关系连接两个版本范围。npm i vue@"^0.7 || ~2” # 安装 2.6.10参考依赖的版本版本号管理策略&&使用npm管理项目版本号语义化版本2.0whats-the-difference-between-tilde-and-caret-in-package-jsonNPM版本计算器 ...

March 30, 2019 · 2 min · jiezi

node(koa2) web应用模块介绍

在自己的koa2 web项目中,用到了几个模块,感觉都是不错的,特地来分享下这些模块。一、前言我们都知道可以通过koa2 工程名的方式来初始化koa2项目,官方为我们增加了koa-bodyparser、koa-josn、koa-router等非常不错的模块,但是,仍不够,所以我将搜集到的有用的包介绍下,当然,有好的包仍然会添加到其中。整个项目在koa2-web-engine ,为了方便查看,使用了原生的方式,欢迎查看。二、新的模块将代码克隆到本地并安装依赖后,启动服务器,在3000端口可以看到所有demo。验证码svg-captcha是一个验证码的库,他创建了svg格式的验证码,可以在登录时,验证是否是正常的用户登录。使用十分的简单:const svgCaptcha = require(‘svg-captcha’);captcha = svgCaptcha.create();captcha对象中包含了svg数据和svg上显示的内容,至于是否要大小写强制验证就可以通过配置的方式来增加了。处理代码位于routes/verificationCode.js中。密码加密登录后端主要是利用node-rsa生成公钥和私钥,再将公钥发送给前端,前端利用jsencrypt进行加密后发送给node,node再用私钥解密。为了性能,我只在服务器启动的时候生成公钥和私钥,以后的请求都是用这队公私钥,他位于utils/RSA.js文件中,解密在routes/login.js中。更详细的可以查看我的这篇博客:基于node简单实现RSA加解密。参数类型检测为了服务器的安全性,服务器对前端发送来的数据肯定是要做校验的,我这使用的joi库。校验主要靠Joi.validate()方法,第一个参数是要校验的对象数据,第二个参数是数据内每个键对应的数据类型,第三个则是可选的option,返回值是一个对象,该对象下的error字段用于判断此次校验是否成功。在utils/checkParams.js中,paramsFormat定义了检测类型,当然每个类型都得用joi内置的类型,checkParams()函数就是做检测的地方,将最后的检测结果return出去。回到routes/joi.js中,利用checkParams()方法检测数据类型,这儿的检测是针对单个的请求,如果要针对所有的请求,可以写成中间件的形式,如utils/middleware.js中,并在app.js中加入以下的就行了:const middleware = require(’./utils/middleware’);middleware.use(app);防xss这儿用到的是xss模块,将每次请求到的数据经过xss处理,输出到后端。为此我自己搞了koa2-xss中间件模块,顺带学习了如何发布npm包,感兴趣的可以看下。日志记录我是用的是log4js模块,该模块既可以记录到数据库,也可以记录到log文件中,此处我是写到文件中的。utils/logs.js文件中是log4js的配置,并封装了对外的调用接口,routes/log4js.js中是根据用户发送的请求记录到日志文件中。定时任务利用了node-schedule模块,一个系统总会用到定时任务的,node-schedule提供了较为简单的api,使用比较方便。路由合并koa2初始化的项目中是将每个路由文件require到app.js中的,当路由文件变多时,管理这些路由就是件麻烦的事,于是引入了koa-compose来管理这些路由文件,只对外暴露一个接口。详细的可以查看routes/index.js文件。webSocketwebsocket在实时性要求比较高的场景下也是会用到的,我们可以利用ws模块实现。更为详细的可以查看我的这篇文章:基于node实现websocket通信。三、总结后期用到一些有意思,有用的模块也将加入到koa2-web-engine 中。原文地址:http://www.zhuyuntao.cn/2019/…欢迎关注微信公众号[ 我不会前端 ]或扫描下方二维码!

March 28, 2019 · 1 min · jiezi

cross-env使用记录

cross-env能跨平台地设置及使用环境变量, cross-env让这一切变得简单,不同平台使用唯一指令,无需担心跨平台问题1、npm安装方式npm i –save-dev cross-env# npm install cross-env –save-dev# yarn add cross-env -D2、改写使用了环境变量的 npm script"scripts": {- “test”: “NODE_ENV=test mocha tests/”,+ “test”: “cross-env NODE_ENV=test mocha tests/”,},常见如 在npm脚本(多是package.json)里这么配置{ “scripts”: { “build”: “cross-env NODE_ENV=production webpack –config build/webpack.config.js” } }运行npm run build,这样NODE_ENV便设置成功,无需担心跨平台问题关于 跨平台兼容,有几点 注意:所有使用引号的地方,建议使用双引号,并且加上转义;没做特殊处理的命令比如 eslint、stylelint、mocha、opn 等工具本身都是跨平台兼容的

March 28, 2019 · 1 min · jiezi

【Vue】使用vue封装插件并发布到NPM

前言多个平台都用到的一些公共的功能,又不想用git的子模块,就有了开发成npm插件的想法放个效果图开始动手因为需求很明确,就是做一个公共的回顶部的插件,所以就使用了简洁版的webpack配置创建工程vue init webpack-simple x-backtotop为什么用简洁版,以及简洁版和完整版有什么区别,可以去这篇文章看看运行脚本后会让配置一些基础信息,直接确认就行看看初始化后的目录结构新建一个lib文件夹,存放我们的插件index.jsimport toTop from ‘./x-backToTop.vue’const comment = { install: function (Vue) { Vue.component(toTop.name, toTop) }}// global 情况下 自动安装if (typeof window !== ‘undefined’ && window.Vue) { window.Vue.use(comment)}export default commentTips: 此处需要注意的是 install。 Vue的插件必须提供一个公开方法 install,该方法会在你使用该插件,也就是 Vue.use(yourPlugin)时被调用。这样也就给 Vue全局注入了你的所有的组件。x-backtotop.vue<template> <div class=“backToTop” :style="‘z-index: ’ + zIndex" @click=“backToTop” v-show=“bVisible”> <div class=“icon”></div> </div></template><script> export default { name: ‘back-to-top’, props: { zIndex: { type: Number, default: 9999 }, triggerHeight: { type: Number }, smooth: { type: Boolean, default: true }, scrollInterval: { type: Number, default: 10 }, scrollHeight: { type: Number, default: 100 } }, data () { return { interval: null, // 计时器 bVisible: false // 按钮显示状态 } }, methods: { resetToTop () { window.pageYOffset = 0; document.documentElement.scrollTop = 0; document.body.scrollTop = 0; }, buttonStatus () { var currentHeight = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop; let browserHeight = this.triggerHeight || (window.outerHeight / 4); this.bVisible = currentHeight > browserHeight; }, backToTop () { if (this.smooth) { var that = this, _interval = this.scrollInterval, _height = this.scrollHeight; // 间隔{_interval}移动{_height} this.interval = setInterval(function () { that.smoothScroll(_height) }, _interval) } else { this.resetToTop(); } }, smoothScroll (y) { if (window.pageYOffset > 0) { window.pageYOffset = window.pageYOffset - y; } if (document.documentElement.scrollTop > 0) { document.documentElement.scrollTop = document.documentElement.scrollTop - y; } if (document.body.scrollTop > 0) { document.body.scrollTop = document.body.scrollTop - y; } var positionNow = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop; if (positionNow <= 0) { clearInterval(this.interval); // 清除计时器后,全部重置为零,预防回滚为负数时,不再显示的bug this.resetToTop(); } } }, created () { window.addEventListener(‘scroll’, this.buttonStatus) }, destroyed () { window.removeEventListener(‘scroll’, this.buttonStatus); } }</script><style lang=“scss” scoped> .backToTop { position: fixed; right: 100px; bottom: 150px; width: 40px; height: 40px; size: 40px; border-radius: 20px; cursor: pointer; transition: .3s; box-shadow: 0 0 6px rgba(0,0,0,.12); background-color: #FFF; overflow: hidden; .icon{ position: absolute; margin: auto; left: 0; top: -8px; bottom: 0; right: 0; width: 0; height: 0; border-width: 8px; border-style: solid; border-color: transparent #0099CC transparent transparent; transform: rotate(90deg); /顺时针旋转90°/ } } .backToTop:hover{ box-shadow: 0 0 20px #000; }</style>这里需要注意的有几个点: 1.这里使用的是addEventListener而不是window.onscroll方法,因为写成window.onscroll在离开当前界面的时候,还会触发一次这个监听,虽然没有影响,但是因为不清楚原因,所以使用addEventListener方式 2.使用平滑的移动到顶端,由于我的写法是每隔一段时间减少定高,所以会出现负数的情况,这个时候再怎么滚动,都不会再变更这个值,所以在回到顶部后,将数值重置为0 3.这里使用的是v-show而不是v-if,具体区别可以查看下官方文档,简单来说就是经常需要变更的建议用v-show插件自测试 我这里是直接在App.vue中引用,效果图如下修改webpack.config.js,注意的就几个字段修改package.json发布到npm这里就不过多介绍怎么创建npm账号了,在项目根路径下运行npm login填写用户名密码whoami确认是否登陆成功,账号是否正确npm publish最后进行发布就行发布中可能遇到的问题no_perms Private mode enable, only admin can publish this module 原因:因为镜像设置成淘宝镜像了,设置回来即可 方案:npm config set registry http://registry.npmjs.orgnpm publish failed put 500 unexpected status code 401 原因:一般是没有登录 方案:重新登陆一次npm ERR! you do not have permission to publish “your module name”. Are you logged in as the correct user? 原因:包名被占用 方案:修改包名即可发布后的一些修改发布完成后,想要自己npm run dev,发现报错原因是因为index.html中没有修改资源路径修改前<script src="/dist/build.js"></script>修改后<script src="/dist/backToTop.js" type=“text/javascript”></script>参考文章vue组件篇(2)—封装组件并发布到npm探索vue-cli的webpack和webpack-simple模板的development server实现差异个人疑问如果想将别人已经发布在npm上的插件,封装到自己插件内部使用,除了手动实现该功能,有别的什么更好的方案吗,希望大佬解惑写在最后目前该插件已经发布到npm上了,欢迎大家使用以上就是我将vue插件封装并发布到npm的总结,如有什么疑问欢迎评论留言。 ...

March 27, 2019 · 2 min · jiezi

npm 在安装的时候提示 没有权限操作的解决办法 Error: EACCES: permission denied

npm 在安装的时候提示 没有权限操作的解决办法 Error: EACCES: permission denied在安装插件的时候出现这样的错误,权限不够,是因为之前用 root 用户进行了局部安装npm包的操作,留下所属权为 root 的文件,导致普通用户无法访问 root的文件内容。报错日志如下:npm ERR! path /Users/Kyle/.npm/_cacache/index-v5/d8/1f/98ab242d0cbad080828ef3e3f4b864c25e506a719121c293fec810b14b3cnpm ERR! code EACCESnpm ERR! errno -13npm ERR! syscall opennpm ERR! Error: EACCES: permission denied, open ‘/Users/Kyle/.npm/_cacache/index-v5/d8/1f/98ab242d0cbad080828ef3e3f4b864c25e506a719121c293fec810b14b3c’npm ERR! { [Error: EACCES: permission denied, open ‘/Users/Kyle/.npm/_cacache/index-v5/d8/1f/98ab242d0cbad080828ef3e3f4b864c25e506a719121c293fec810b14b3c’]npm ERR! cause:npm ERR! { Error: EACCES: permission denied, open ‘/Users/Kyle/.npm/_cacache/index-v5/d8/1f/98ab242d0cbad080828ef3e3f4b864c25e506a719121c293fec810b14b3c’npm ERR! errno: -13,npm ERR! code: ‘EACCES’,npm ERR! syscall: ‘open’,npm ERR! path:npm ERR! ‘/Users/Kyle/.npm/_cacache/index-v5/d8/1f/98ab242d0cbad080828ef3e3f4b864c25e506a719121c293fec810b14b3c’ },npm ERR! isOperational: true,npm ERR! stack:npm ERR! ‘Error: EACCES: permission denied, open '/Users/Kyle/.npm/_cacache/index-v5/d8/1f/98ab242d0cbad080828ef3e3f4b864c25e506a719121c293fec810b14b3c'’,npm ERR! errno: -13,npm ERR! code: ‘EACCES’,npm ERR! syscall: ‘open’,npm ERR! path:npm ERR! ‘/Users/Kyle/.npm/_cacache/index-v5/d8/1f/98ab242d0cbad080828ef3e3f4b864c25e506a719121c293fec810b14b3c’,npm ERR! parent: ‘findup-sync’ }npm ERR! npm ERR! The operation was rejected by your operating system.npm ERR! It is likely you do not have the permissions to access this file as the current usernpm ERR! npm ERR! If you believe this might be a permissions issue, please double-check thenpm ERR! permissions of the file and its containing directories, or try runningnpm ERR! the command again as root/Administrator (though this is not recommended).npm ERR! A complete log of this run can be found in:npm ERR! /Users/Kyle/.npm/_logs/2019-03-26T07_00_54_812Z-debug.log错误原因:找到报错的文件,会看到它的所有者是 root。之前用 root 进行了局部的安装操作,导致这个文件的所有者是 root ,还包括 .npm 文件夹下部分文件夹的所有权,也是 root,普通用户当然误。![图片上传中…]解决办法就是把用户目录下的 .npm 文件夹所有权都改成当前用户即可。比如:当前用户名为 Kylesudo chown -R Kyle ~/.npm 执行后输入 root 密码,文件所属已改为当前用户了,再执行操作就不会出现了。注意事项初学者在用 terminal 操作 npm 的时候,经常会混用 root 和 普通用户。由于分不清全局安装和局部安装的区别,才会出现乱用root 的问题。一般来说,全局安装就用普通用户。 ...

March 26, 2019 · 1 min · jiezi

Ionic start(4.x版本)发生bad status code 400错误以及离线创建新项目的解决方法

使用ionic start创建新项目,出现错误:Error: Encountered bad status code (400) forhttps://d2ql0qc7j8u4b2.cloudfront.net/angular-official-tabs.tar.gzThis could mean the server is experiencing difficulties right now–please tryagain later. at Request.req.on.res(C:\Users\xxx\AppData\Roaming\npm\node_modules\ionic\lib\utils\http.js:68:28) at emitOne (events.js:116:13) at Request.emit (events.js:211:7) at Request._emitResponse(C:\Users\xxx\AppData\Roaming\npm\node_modules\ionic\node_modules\superagent\lib\node\index.js:862:8) at ClientRequest.req.once.res(C:\Users\xxx\AppData\Roaming\npm\node_modules\ionic\node_modules\superagent\lib\node\index.js:412:10) at Object.onceWrapper (events.js:315:30) at emitOne (events.js:121:20) at ClientRequest.emit (events.js:211:7) at HTTPParser.parserOnIncomingClient (_http_client.js:543:21)| Downloading and extracting tabs starter (100.00%)然后就一直卡在Downloading and extracting tabs starter不动。直接原因是ionic cli无法下载ionic的template文件angular-official-tabs.tar.gz,从url看这个文件没有放在npm库中,所以使用淘宝npm镜像不能解决这个问题。在网上搜了很多文章都无法解决这个问题。后来在Ionic官网上看到,Ionic新项目模板(starter)作为一个开源项目托管在github上,于是尝试去找到starter template的源码,然后直接从starter template手工创建新项目。在github上搜索ionic-team,发现ionic-team/starters项目,这就是要找的模板源码项目。地址是:https://github.com/ionic-team/starters然后打包下载这个项目的所有源码。根目录下,有三个文件夹angular、ionic-angular和ionic1三个文件夹,显然分别对应三种ionic4项目的starter模板。angular是ionic4.x的angular项目;ionic-angular是ionic2.x/3.x的项目;ionic1是ionic1.x项目。我需要创建的是ionic4.x的项目,所以进入angular文件夹,里面有base和official两个文件夹,从前面错误信息看到下载文件名为angular-official-tabs.tar.gz,所以显然这个模板应该放在official文件夹中。打开official文件夹,里面果然有一个tabs的文件夹,里面有src和e2e两个文件夹,应该就是模板的源文件。但是angular/official/tabs文件夹下没有包含完整的ionic/angular的项目文件,显然,angular.json、package.json文件都没有。然后查找其他文件夹,发现angular/base文件夹下,有这些缺失的文件。于是做以下尝试:a.新建一个文件夹作为我们自己的项目文件夹,假设是testv4。b.把angular/base下的所有文件复制到testv4中。c.把angular/official/tabs文件夹下所有文件复制到testv4中,提示有同名文件,全部覆盖。d.打开testv4/package.json文件,修改前面几行内容为自己的应用名称等: “name”: “ionic-app-base”, “version”: “0.0.0”, “author”: “Ionic Framework”, “homepage”: “https://ionicframework.com/",e.在testv4文件夹上执行npm install,中间可能会出现错误,如果出错则删除node_module文件夹然后再次运行npm install,直到成功。f.执行npm run start,启动浏览器打开localhost:4200,成功。这个方法也可以实现不联网状态下,离线创建Ionic新项目,当然你可以说离线创建新项目没有意义,因为npm install一样需要联网,但如果能够手工建立node_module文件夹,离线创建ionic项目也是有意义的。 ...

March 25, 2019 · 1 min · jiezi

npm安装报错Error: EPERM: operation not permitted已解决

npm ERR! path D:\dataCenter\bi-frontend-base\node_modules\nyc\node_modules\archy\npm-shrinkwrap.jsonnpm ERR! code EPERMnpm ERR! errno -4048npm ERR! syscall opennpm ERR! Error: EPERM: operation not permitted, open ‘D:\dataCenter\bi-frontend-base\node_modules\nyc\node_modules\archy\npm-shrinkwrap.json’npm ERR! { [Error: EPERM: operation not permitted, open ‘D:\dataCenter\bi-frontend-base\node_modules\nyc\node_modules\archy\npm-shrinkwrap.json’]npm ERR! cause:npm ERR! { Error: EPERM: operation not permitted, open ‘D:\dataCenter\bi-frontend-base\node_modules\nyc\node_modules\archy\npm-shrinkwrap.json’npm ERR! errno: -4048,npm ERR! code: ‘EPERM’,npm ERR! syscall: ‘open’,npm ERR! path:npm ERR! ‘D:\dataCenter\bi-frontend-base\node_modules\nyc\node_modules\archy\npm-shrinkwrap.json’ },npm ERR! isOperational: true,npm ERR! stack:npm ERR! ‘Error: EPERM: operation not permitted, open 'D:\dataCenter\bi-frontend-base\node_modules\nyc\node_modules\archy\npm-shrinkwrap.json'’,npm ERR! errno: -4048,npm ERR! code: ‘EPERM’,npm ERR! syscall: ‘open’,npm ERR! path:npm ERR! ‘D:\dataCenter\bi-frontend-base\node_modules\nyc\node_modules\archy\npm-shrinkwrap.json’,npm ERR! parent: ’nyc’ }npm ERR!npm ERR! The operation was rejected by your operating system.npm ERR! It’s possible that the file was already in use (by a text editor or antivirus),npm ERR! or that you lack permissions to access it.npm ERR!npm ERR! If you believe this might be a permissions issue, please double-check thenpm ERR! permissions of the file and its containing directories, or try runningnpm ERR! the command again as root/Administrator (though this is not recommended).npm ERR! A complete log of this run can be found in:npm ERR! D:\MyConfiguration\zr02718\AppData\Roaming\npm-cache_logs\2019-03-19T07_19_58_886Z-debug.log删除当前用户目录下面的.npmrc文件重新安装即可。这个文件不一定在哪里,在当前用户目录下面,可能在C盘也可能再D盘 ...

March 19, 2019 · 1 min · jiezi

都8102年了,为什么你还在用Yarn?!

原文出处:yarn-vs-npm仍记得Yarn刚问世时的火爆场景真的太快了,每次yarn install,都会为我节省出好几分钟的时间。Yarn会保证你的node_modules文件夹与同事的node_modules文件夹严格匹配,同样,服务器上的node_modules文件夹也和你本地一模一样,不会出现包版本不一致的情况,也再也不会出现‘在我电脑上好好的啊¯(ツ)/¯’这样的话。六个月过去了…Npm.v5版本脱胎换骨而来,带来了速度提升与版本锁定这两个杀手锏。没错,就是我们刚初弃npm投yarn的最重要的原因。现在,很多开发者心里在嘀咕:我还有必要用Yarn吗?我觉得,是时候再次投到到npm的温暖怀抱了!npm和Yarn一样快请自行查看:# Backupmv node_modules node_modules_backupmv package-lock.json package-lock.backup.jsonmv yarn.lock yarn.backup.lock# Test cold npm speedtime npm install# Reset modulesrm -Rf node_modules# Test warm npm speedtime npm install# Test cold yarn speedtime yarn install# Reset modulesrm -Rf node_modules# Test warm yarn speedtime yarn install# Resetrm package-lock.jsonrm yarn.lock# Restoremv node_modules_backup node_modulesmv package-lock.backup.json package-lock.jsonmv yarn.backup.lock yarn.lock我按上述步骤运行了三次,速度几近持平。轻松切换npm版本Bob安装了Yarn v1.1,Brenda安装了Yarn v1.2. 当他们在工程里添加或删除依赖包时,会使得Yarn修改yarn.lock文件。但在两个Yarn版本中,lock文件有些许的不同,这可不是什么好事。你可以在package.json中标明你的工程应该使用yarn的哪个版本。但如果你有多个工程,每个工程所需的yarn又不尽相同时可怎么办?你应该用npm来安装多个yarn版本:npm install yarn@1.1 –global,npm install yarn@1.2 –global。但每次项目切换你都需要再重新安装Yarn,这很恐怖了!npm可以轻松做到版本切换!使用nvm或n一键切换npm版本,就是如此简单方便!用Lerna管理你的workspaces如果你在用Yarn的workspaces特性,在npm中,你可以用Lerna代替它,它提供了Yarn所具备的所有特性,而且还延伸了一些新功能:管理workspace版本,在workspace中执行命令,发布workspaces。用npm-check来交互式升级Yarn upgrade-interactive命令很棒:在npm中也可以用npm-check实现该功能:npm install npm-check –save-dev添加脚本:{ “scripts”: { “upgrade-interactive”: “npm-check –update” }}然后,npm也可以npm run upgrade-interactive了。Yarn正在变得复杂虽然很多工程的readme中同时展示了npm和yarn,但其实真不必这样。比如,create-react-app工程的readme:npm run build or yarn buildBuilds the app for production to the build folder.Yarn在此处的作用是什么?None。这对于新手而言绝对充满了困惑,我该运行哪个命令?Yarn是什么?我需要它么?选择Yarn,意味着你的团队都将必须使用yarn。 Javascript的生态已经十分复杂了,我们真的要再拿Yarn火上浇油吗?我希望Yarn团队将他们杰出的工作直接放在npm中来让我们的生活好过一点。起飞吧npm!当我重新切回npm后,我发现npm run命令竟然有自动补全功能,这很赞!虽然Yarn也许会作出同样的功能,但npm才是老大哥!npm也有其他的一些新功能,比如npm audit,它可以扫描你工程中的现存漏洞。npm已经足够快,npm的社区已经足够成熟,且一些packages赋予了npm更加强大的生命力!忘了Yarn吧! ...

March 19, 2019 · 1 min · jiezi

Angular/Ionic安装配置汇总

一、NodeJSAngular和Cordova/Ionic都使用cli工具开发,因此必须先安装最新版本的NodeJS。NodeJS里面包含npm包管理器,而Angular/Ionic本身和程序依赖的库和框架都是npm包,要进行Angular/Ionic开发首先需要安装npm包管理器。NodeJS的官网下载页:https://nodejs.org/en/download/安装NodeJS的时候把npm一起安装就可以。也可以安装yarn,下载依赖包的速度和稳定性会提升很多。可以参考 https://yarn.bootcss.com/二、使用npm的淘宝镜像直接下载npm库会很慢,很多时候会不成功。国内用户可以使用淘宝的npmjs.org镜像,加快下载速度。淘宝镜像的官方使用说明在:https://npm.taobao.org/官方的建议是安装cnpm代替npm,或者添加一个叫cnpm的别名。另一个方法是不使用cnpm,而是修改npm的默认镜像:npm config set registry http://registry.npm.taobao.org/如果想修改回默认的版本,可以这样:npm config set registry https://registry.npmjs.org/用 npm get registry 可以查看现在使用的是哪个镜像。如果安装了yarn,可以这样设置镜像:yarn config set registry http://registry.npm.taobao.org/三、Angular安装npm install -g @angular/cli可参考官网:https://angular.cn/guide/quic…cli命令可以参考:https://angular.cn/cli四、Ionic4安装安装cli: npm install -g ionicIonic cli的文档:https://ionicframework.com/do…Ionic Native提供使用Angular风格调用原生组件的方法。Ionic native的文档:https://ionicframework.com/do…五、Ionic3安装1.安装Ionic和Cordova的CLInpm install -g ionic cordovanpm可以先安装或者配置淘宝镜像。2.新建项目在命令行中,进入项目目录的上级目录,然后执行 以下命令:ionic start TestIonic tabs其中,TestIonic是项目名,也是目录名,命令执行成功后,会自动在当前目录下创建一个名叫TestIonic的子目录,就是新建项目的目录。后面的tabs表示新建项目的模板,tabs模板3个tab的布局,也可以使用其他官方模板:tabs : a simple 3 tab layoutsidemenu: a layout with a swipable menu on the sideblank: a bare starter with a single pagesuper: starter project with over 14 ready to use page designstutorial: a guided starter project此命令可能会出现网络连接错误:[ERROR] Network connectivity error occurred, are you offline? If you are behind a firewall and need to configure proxy settings, see: https://ionicframework.com/docs/cli/configuring.html#using-a-proxy解决方案,执行以下命令配置npm代理:npm config set proxy= https://registry.npm.taobao.orgnpm config set https_proxy=https://registry.npm.taobao.org然后设置ionic的代理:npm install -g @ionic/cli-plugin-proxy添加环境变量 IONIC_HTTP_PROXY 值为代理服务器地址,比如https://registry.npm.taobao.org3.运行项目ionic serve复制地址到谷歌浏览器,然后按下F12,按左上角第二个按钮切换到手机模式,可以调试项目。4.打包打包android app,需要先安装gradle(网上说法如此,但实际编译过程中没有看到ionic使用了安装的gradle,反而自己下载了一个gradle)和android sdk,可参考https://blog.csdn.net/qq_2026…。ionic cordova build android –release –prod上面命令可以打包成为android apk,并且在输出提示中显示apk文件的位置。ionic cordova run android上面命令生成apk文件并且直接安装到安卓手机上,但安卓手机必须用usb连接PC,而且进入开发者模式,启用usb调试,并且安装的时候一般需要在手机上做确认,否则安装失败。ionic cordova emulate android上面命令生成apk文件并且安装到安卓模拟器上运行,最好先启动安卓模拟器,如果没有启动安卓模拟器,ionic会自动启动缺省的安卓模拟器。另外,上面两个命令也可以用cordova版本例如:cordova run androidcordova emulate android效率更高,不过事先必须先调用过ionic cordova build。另外,以上ionic命令都可以加上选项-lc:ionic cordova emulate android -lc选项-lc有两个作用,一个是把console.info等输出信息输出到命令行上;另一个是可以使配置文件ionic.config.json中的proxies生效。注意,-lc选项虽然可以让proxies生效,但只能用于调试,正式安装运行是无效的,需要用环境变量来控制。5.android签名运行ionic cordova build android –release –prod之后,在命令行输入以下命令:keytool -genkey -v -keystore testionic.keystore -alias testionic.keystore -keyalg RSA -validity 36500jarsigner -verbose -keystore testionic.keystore -signedjar tionic.apk app-release-unsigned.apk testionic.keystorezipalign -v 4 txx.apk txx_aligned.apk其中keytool和jarsigner是jdk的工具,需要配置jdk的path。zipalign是android sdk的工具,需要在android sdk下搜索其具体路径,但这个命令不一定需要执行,只是优化。也可以直接在cordova build中直接签名ionic cordova build android –release –prod – – –minSdkVersion=22 –keystore=testionic.keystore –alias=testionic.keystore –storePassword=123456 –password=123456 ...

March 18, 2019 · 1 min · jiezi

[About NPM] 看看NPM怎么玩

如果你写js, 或者使用hexo一类的博客工具, 那么应该不会对npm感到陌生. npm是Node.js的包管理工具(package manager), 它的背后是Node.js的社区生态在支持. 我们今天聊聊npm的玩儿法, 包括了常用的命令和参数以及在具体场景下的用法.前言我们今天只谈npm, 相关的辅助工具各位可以留言评论.用好help几乎所有的命令行工具/程序(Command Line Tool)都会有help这个命令或者–help这个参数, 没有的要么是程序本身就是只供第三方调用api的, 要么就是开发者不够专业. 再好的程序开发出来如果目标受众无从得知它的用法, 那么它就等同于不存在.npm的帮助功能十分优秀, 直接运行npm help或者npm –help将会得到如下结果可以看到, 所有的命令都已经罗列在这里了, 如果你想知道具体某个命令的用法, 可以直接在help后面跟上命令名称, 或者直接执行命令, 并在后面加上–help参数npm help init# ornpm init –help区别在于, help命令会展示详情的文档, 也就是会调用浏览器打开本地的html页面. 取决于你的npm版本, 可能需要加上-lor –long参数而–help参数则会显示一份简短的说明.可以看到init是有两个别名的, 分别是create和innit, 并且带有四个参数,分别是–force -f, –yes -y –scope,这里要注意的是-f和-y分别是–force和–yes的简写, 对应的结果是一样的.事实上这是CLI(Command Line Interface)设计的一个通用规范. 你可以在别的命令行程序上发现类似的用法.npm help最厉害的地方在于, 如果你不知道具体的命令名称, 或者只是想要知道某个知识点, 那么它也可以做模糊搜索, 命令的用法跟上面的相同, 比如npm help scri将会得到npm将会列举出所有它认为可能的条目供你查阅.如果有兴趣, 你可以通过npm -l发现更多详细的用法.从项目的创建到发布我们来以这个流程走一遍在开发当中会跟npm打交道的地方.初始化项目也就是要创建一个package.json文件了,npm init # 选择性的使用-y|–yes参数可以快速跳过问答, 直接创建一份默认配置正常情况下你就可以开始安装相应的依赖了.但是在有些情况下, 你可能会有切换registry以及设置代理的烦恼. 甚至于不同的项目可能有不同的私有包位于不同的私有npm仓库上. 因此设置registry跟proxy也是比较常用的命令设置registry我们还是先跑一下帮助, 看看能得到什么信息npm config –helpSee! 相应的CURD操作一目了然, 所以我们可以这样子设置代理和npm源npm config set registry https://registry.npm.taobao.orgnpm config set proxy http://127.0.0.1:1080是的, help就是这么强大且方便, 读到这里, 如果你动手能力比较强, 可以直接去实践了(当然最好可以读完啦).这里需要说明的地方是, 设置了proxy之后, 类似node-sass及electron一类需要执行postinstall脚本的package, 下载时也将会应用这个代理设置.创建适用于工程的配置文件你可以直接在项目的根目录下创建.npmrc文件, 以键值对的形式写上对应的配置项, 保存之后就会在项目范围内生效了.registry=https://registry.npmjs.orgsass_binary_site=https://npm.taobao.org/mirrors/node-sass/同样, 全局范围内生效的.npmrc在你的用户目录下, 二者都存在时, 将优先应用项目下的配置.安装依赖话不多说, 先跑一下helpinstall带有的功能及参数就比较多了, 我们一一过一遍.首先不带参数的时候, 就会安装package.json内的所有依赖, 正常情况下会将dependencies devDependencies optionalDependencies 三种种依赖类型全部安装.(如果你想知道全部四种依赖(还有个peerDependencies)的详细信息, 可以通过npm help package.json查阅). install的路径, 可以是包名, 本地文档或者git地址install带有几个参数, 前三个分别意味着将安装的依赖划分到指定的依赖类型当中. –save-exact跟npm执行的升级策略有关, 这里不做具体阐述. 最后一个你应该也猜到了它的作用, 就是只安装, 但是不保存依赖信息到package,json当中.事实上, install及其他命令所能附加的参数远不止这里罗列的这些, 你也可以通过–registry参数指定这一次安装所使用的npm源, 或者使用–no-proxy指定这一次暂时不用代理, 使用–save-exact指定按照package.json里的版本号安装依赖来避免恼人的自动向上更新策略等等, 这些参数是可以累加的, 具体的用法及可用的参数可以参考npm help install给出的文档, 这里只做简单提示.开发过程常见问题很多时候, 很多问题都是在关键的时候出现的. 比如你想安装一个包的时候, 死活安装不上来, 各种错误都有…这里我们简单列举几个平时比较有可能遇到的.unexpected toekn|json|html导致这个问题的原因一般有两种, 要么, 你开了代理, 但是使用的npm源是代理服务器无法访问的; 要么, 你上一次的CTRL+C导致了npm的缓存出现了问题, 你需要npm cache clean这个命令, 记得加上–force. 如同运行之后给出的提示, 我们只建议你在走投无路的时候尝试, 因为node_modules机制下, 缓存是比较珍贵的????.EPERM基本上, 出现这个错误的时候错误信息已经告诉你是权限问题了, Unix系统下需要sudo执行, Windows NT下需要管理员身份的command prompt.同一个项目, Jack新增的项目依赖在Lucy那里install之后就error不断你可能是中了semver版本策略的招, 具体的可以通过npm help update查看详细的升级策略, 具体的解法是, 你可以让其他人在执行install命令的时候加上–save-exact参数.发布发布的话, 除了publish, 有一个比较使用的命令, 就是whoami. 它可以查看当前registry下登录的用户, 未登录或者登录的registry不对就会给出error.Conclusion事实上, 这里的绝大多数内容在npm的doc上都有. 我只列举了一部分常用的以及一些使用技巧. help命令的使用方式在大多数CLI应用上是差不多的, 你可以举一反三地都自己试试. 如果有更好地建议或者错误需要指出, 敬请留言评论啦. That’s All. ...

March 15, 2019 · 1 min · jiezi

在NPM发布自己造的轮子

在NPM发布自己造的轮子1、前言自从Node.js出现,它的好基友npm(node package manager)也是我们日常开发中必不可少的东西。npm让js实现了模块化,使得复用其他人写好的模块(搬砖)变得更加方便,也让我们可以分享一些自己的作品给大家使用(造轮子),今天这里我就给大家分享一个用命令行压缩图片的工具,它的用法大致是这样的:// 全局安装后,在图片目录下,运行这行$ tinyhere这样就把文件夹内的图片进行压缩。这里压缩采用的是 tinypng 提供的接口,压缩率大致上是50%,基本可以压一半的大小。以前在写项目的时候,测试验收完成后总是要自己手动去压一次图片,后来想把这个枯燥重复的事自动化去完成(懒),但是公司脚手架又没有集成这个东西,就想自己写一个轮子做出来用用就好了。它的名字叫做tinyhere,大家可以去安装使用试一下$ npm i tinyhere -g2、npm简介如果要写一个模块发布到npm,那么首先要了解一下npm的用法。给这个模块建一个文件夹,然后在目录内运行npm init来初始化它的package.json,就是这个包的描述// 个人比较喜欢后面带–yes,它会生成一个带默认参数的package.json$ npm init (–yes)package.json详情:{ “name”: “pkgname”, // 包名,默认文件夹的名字 “version”: “1.0.0”, “description”: “my package”, “main”: “index.js”, // 如果只是用来全局安装的话,可以不写 “bin”: “cli”, // 如果是命令行使用的话,必须要这个,名字就是命令名 “scripts”: { “test”: “echo "Error: no test specified" && exit 1” // npm run test对应的test }, “keywords”: [‘cli’, ‘images’, ‘compress’], “author”: “croc-wend”, “license”: “MIT”, …}更多配置信息可以参考一下vue的package.json的https://github.com/vuejs/vue/blob/dev/package.json 初始化完成之后,你就可以着手写这个包了,当你觉得你写好了之后,就可以发布到npm上面npm loginnpm publish+ pkgname@1.0.0 // 成功这时,你在npm上面搜你的包名,你写在package.json 的信息都会被解析,然后你的包的页面介绍内容就是你的README.md3、写这个包包初始化好了之后,我们就可以开始写这个包了对于这个压缩工具来说,要用到的素材只有两个,tinypng接口要用到的 api-key,需要压缩的图片,所以我对这两个素材需要用到的一些操作进行了以下分析:我的初衷是想把这个命令写的尽量简单,让我可以联想到压缩图片=简单,所以我待定了整个包只有一个单词就能跑,是这样:$ tinyhere其他的操作都放在子命令和可选项上。然后开始划分项目结构大致上是这样,把全局命令执行的 tinyhere 放在bin目录下,然后subCommand负责提供操作函数,然后把可复用的函数(比如读写操作)抽离出来放在util上,比较复杂的功能单独抽离成一个文件,比如compress,然后导出一个函数给subCommand。至于存放用户的api-key,就存放在data下面的key里。tinyhere的执行文件就负责解析用户的输入,然后执行subCommand给出的对应函数。4、过程解析压缩图片的这个包的过程是这样的:1、解析当前目录内的所有图片文件,这里应该根据二进制流及文件头获取文件类型mime-type,然后读取文件二进制的头信息,获取其真实的文件类型,来判断它是否真的是图片文件,而不是那些仅仅是后缀名改成.png的假货2、 如果用户有要求把压缩的图片存放到指定目录,那就需要生成一个文件夹来存放它们。那么,首先要判断这个路径是否合法,然后再去生成这个目录3、判断用户的api-key的剩余次数是否足够这次的图片压缩,如果这个key不够,就换到下一个key,知道遍历文件内所有的key找到有可用的key为止。4、图片和key都有了,这时可以进行压缩了。用一个数组把压缩失败的存起来,然后每次压缩完成都输出提示,在所有图片都处理完成后,如果存在压缩失败的,就询问是否把压缩失败的图继续压缩5、这样,一次压缩就处理完成了。压缩过的图片会覆盖原有的图片,或者是存放到指定的路径里ps:$ tinyhere deep >>> 把目录内的所有图片都进行压缩(含子目录)。这个命令和上述的主命令的流程有点不同,目前有点头绪,还没有开发完成,考虑到文件系统是树形结构,我目前的想法是通过深度遍历,把存在图片的文件夹当作一个单位,然后递归执行压缩。其他:这里吐槽一下tinypng 的接口写的真的烂。。在查询key的合法性的 validate 函数只接受报错的回调,但是成功却没有任何动作。我真是服了,之前是做延时来判断用户的key的合法性,最后实在是受不了这个bug一样的写法了,决定用Object.defineProperty来监听它的使用次数的变化。如果它的setter被调用则说明它是一个合法的key了5、小结在这里,我想跟大家说,如果你做了一个你觉得很酷的东西,也想给更多的人去使用,来让它变得更好,选择发布在NPM上面就是一个非常好的途径,看了上面的内容你会发现分享其实真的不难,你也有机会让世界看到属于你的风采!如果大家觉得我有哪里写错了,写得不好,有其它什么建议(夸奖),非常欢迎大家补充。希望能让大家交流意见,相互学习,一起进步! 我是一名 19 的应届新人,以上就是今天的分享,新手上路中,后续不定期周更(或者是月更哈哈),我会努力让自己变得更优秀、写出更好的文章,文章中有不对之处,烦请各位大神斧正。如果你觉得这篇文章对你有所帮助,请记得点赞或者品论留言哦~。6、写在最后欢迎大家提issue或者建议!地址在这:https://github.com/Croc-ye/ti…https://www.npmjs.com/package…最后贴上部分代码,内容过长,可以跳过哦bin/tinyhere#!/usr/bin/env nodeconst commander = require(‘commander’);const {init, addKey, deleteKey, emptyKey, list, compress} = require(’../libs/subCommand.js’);const {getKeys} = require(’../libs/util.js’);// 主命令commander.version(require(’../package’).version, ‘-v, –version’).usage(’[options]’).option(’-p, –path <newPath>’, ‘压缩后的图片存放到指定路径(使用相对路径)’).option(’-a, –add <key>’, ‘添加api-key’).option(’–delete <key>’, ‘删除指定api-key’).option(’-l, –list’, ‘显示已储存的api-key’).option(’–empty’, ‘清空已储存的api-key’)// 子命令commander.command(‘deep’).description(‘把该目录内的所有图片(含子目录)的图片都进行压缩’).action(()=> { // deepCompress(); console.log(‘尚未完成,敬请期待’);})commander.parse(process.argv);// 选择入口if (commander.path) { // 把图片存放到其他路径 compress(commander.path);} else if (commander.add) { // 添加api-key addKey(commander.add);} else if (commander.delete) { // 删除api-key deleteKey(commander.delete);} else if (commander.list) { // 显示api-key list();} else if (commander.empty) { // 清空api-key emptyKey();} else { // 主命令 if (typeof commander.args[0] === ‘object’) { // 子命令 return; } if (commander.args.length !== 0) { console.log(‘未知命令’); return; } if (getKeys().length === 0) { console.log(‘请初始化你的api-key’) init(); } else { compress(); }};libs/compress.jsconst tinify = require(’tinify’);const fs = require(“fs”);const path = require(‘path’);const imageinfo = require(‘imageinfo’);const inquirer = require(‘inquirer’);const {checkApiKey, getKeys} = require(’./util’);// 对当前目录内的图片进行压缩const compress = (newPath = ‘’)=> { const imageList = readDir(); if (imageList.length === 0) { console.log(‘当前目录内无可用于压缩的图片’); return; } newPath = path.join(process.cwd(), newPath); mkDir(newPath); findValidateKey(imageList.length); console.log(’===========开始压缩=========’); if (newPath !== process.cwd()) { console.log(‘压缩到: ’ + newPath.replace(/./g, ‘’)); } compressArray(imageList, newPath);};// 生成目录路径const mkDir = (filePath)=> { if (filePath && dirExists(filePath) === false) { fs.mkdirSync(filePath); }}// 判断目录是否存在const dirExists = (filePath)=> { let res = false; try { res = fs.existsSync(filePath); } catch (error) { console.log(‘非法路径’); process.exit(); } return res;};/** * 检查api-key剩余次数是否大于500 * @param {} count 本次需要压缩的图片数目 /const checkCompressionCount = (count = 0)=> { return (500 - tinify.compressionCount - count) >> 0;}/* * 找到可用的api-key * @param {} imageLength 本次需要压缩的图片数目 /const findValidateKey = async imageLength=> { // bug高发处 const keys = getKeys(); for (let i = 0; i < keys.length; i++) { await checkApiKey(keys[i]); res = checkCompressionCount(imageLength); if (res) return; } console.log(‘已存储的所有api-key都超出了本月500张限制,如果要继续使用请添加新的api-key’); process.exit();}// 获取当前目录的所有png/jpg文件const readDir = ()=> { const filePath = process.cwd() const arr = fs.readdirSync(filePath).filter(item=> { // 这里应该根据二进制流及文件头获取文件类型mime-type,然后读取文件二进制的头信息,获取其真实的文件类型,对与通过后缀名获得的文件类型进行比较。 if (/(.png|.jpg|.jpeg)$/.test(item)) { // 求不要出现奇奇怪怪的文件名。。 const fileInfo = fs.readFileSync(item); const info = imageinfo(fileInfo); return /png|jpg|jpeg/.test(info.mimeType); } return false; }); return arr;};/* * 对数组内的图片名进行压缩 * @param {} imageList 存放图片名的数组 * @param {} newPath 压缩后的图片的存放地址 /const compressArray = (imageList, newPath)=> { const failList = []; imageList.forEach(item=> { compressImg(item, imageList.length, failList, newPath); });}/* * 压缩给定名称的图片 * @param {} name 文件名 * @param {} fullLen 全部文件数量 * @param {} failsList 压缩失败的数组 * @param {} filePath 用来存放的新地址 /const compressImg = (name, fullLen, failsList, filePath)=> { fs.readFile(name, function(err, sourceData) { if (err) throw err; tinify.fromBuffer(sourceData).toBuffer(function(err, resultData) { if (err) throw err; filePath = path.join(filePath, name); const writerStream = fs.createWriteStream(filePath); // 标记文件末尾 writerStream.write(resultData,‘binary’); writerStream.end(); // 处理流事件 –> data, end, and error writerStream.on(‘finish’, function() { failsList.push(null); record(name, true, failsList.length, fullLen); if (failsList.length === fullLen) { finishcb(failsList, filePath); } }); writerStream.on(’error’, function(err){ failsList.push(name); record(name, false, failsList.length, fullLen); if (failsList.length === fullLen) { finishcb(failsList, filePath); } }); }); });}// 生成日志const record = (name, success = true, currNum, fullLen)=> { const status = success ? ‘完成’ : ‘失败’; console.log(${name} 压缩${status}。 ${currNum}/${fullLen});}/* * 完成调用的回调 * @param {} failList 存储压缩失败图片名的数组 * @param {} filePath 用来存放的新地址 */const finishcb = (failList, filePath)=> { const rest = 500 - tinify.compressionCount; console.log(‘本月剩余次数:’ + rest); const fails = failList.filter(item=> item !== null); if (fails.length > 0) { // 存在压缩失败的项目(展示失败的项目名),询问是否把压缩失败的继续压缩 y/n // 选择否之后,询问是否生成错误日志 inquirer.prompt({ type: ‘confirm’, name: ‘compressAgain’, message: ‘存在压缩失败的图片,是否将失败的图片继续压缩?’, default: true }).then(res=> { if (res) { compressArray(failList, filePath); } else { // 询问是否生成错误日志 } }) } else { // 压缩完成 console.log(’======图片已全部压缩完成======’); }}module.exports = { compress}libs/subCommand.jsconst inquirer = require(‘inquirer’);const {compress} = require(’./compress.js’);const {checkApiKey, getKeys, addKeyToFile, list} = require(’./util.js’);module.exports.compress = compress;module.exports.init = ()=> { inquirer.prompt({ type: ‘input’, name: ‘apiKey’, message: ‘请输入api-key:’, validate: (apiKey)=> { // console.log(’\n正在检测,请稍候…’); process.stdout.write(’\n正在检测,请稍候…’); return new Promise(async (resolve)=> { const res = await checkApiKey(apiKey); resolve(res); }); } }).then(async res=> { await addKeyToFile(res.apiKey); console.log(‘apikey 已完成初始化,压缩工具可以使用了’); })}module.exports.addKey = async key=> { await checkApiKey(key); const keys = await getKeys(); if (keys.includes(key)) { console.log(‘该api-key已存在文件内’); return; } const content = keys.length === 0 ? ’’ : keys.join(’ ‘) + ’ ‘; await addKeyToFile(key, content); list();}module.exports.deleteKey = async key=> { const keys = await getKeys(); const index = keys.indexOf(key); if (index < 0) { console.log(‘该api-key不存在’); return; } keys.splice(index, 1); console.log(keys); const content = keys.length === 0 ? ’’ : keys.join(’ ‘); await addKeyToFile(’’, content); list();}module.exports.emptyKey = async key=> { inquirer.prompt({ type: ‘confirm’, name: ’emptyConfirm’, message: ‘确认清空所有已存储的api-key?’, default: true }).then(res=> { if (res.emptyConfirm) { addKeyToFile(’’); } else { console.log(‘已取消’); } })}module.exports.list = list;libs/util.jsconst fs = require(‘fs’);const path = require(‘path’);const tinify = require(’tinify’);const KEY_FILE_PATH = path.join(__dirname, ‘./data/key’);// 睡眠const sleep = (ms)=> { return new Promise(function(resolve) { setTimeout(()=> { resolve(true); }, ms); });}// 判定apikey是否有效const checkApiKey = async apiKey=> { return new Promise(async resolve=> { let res = true; res = /^\w{32}$/.test(apiKey); if (res === false) { console.log(‘api-key格式不对’); resolve(res); return; } res = await checkKeyValidate(apiKey); resolve(res); })}// 检查api-key是否存在const checkKeyValidate = apiKey=> { return new Promise(async (resolve)=> { tinify.key = apiKey; tinify.validate(function(err) { if (err) { console.log(‘该api-key不是有效值’); resolve(false); } }); let count = 500; Object.defineProperty(tinify, ‘compressionCount’, { get: ()=> { return count; }, set: newValue => { count = newValue; resolve(true); }, enumerable : true, configurable : true }); });};// 获取文件内的key,以数组的形式返回const getKeys = ()=> { const keys = fs.readFileSync(KEY_FILE_PATH, ‘utf-8’).split(’ ‘); return keys[0] === ’’ ? [] : keys;}// 把api-key写入到文件里const addKeyToFile = (apiKey, content = ‘’)=> { return new Promise(async resolve=> { const writerStream = fs.createWriteStream(KEY_FILE_PATH); // 使用 utf8 编码写入数据 writerStream.write(content + apiKey,‘UTF8’); // 标记文件末尾 writerStream.end(); // 处理流事件 –> data, end, and error writerStream.on(‘finish’, function() { console.log(’=====已更新=====’); resolve(true); }); writerStream.on(’error’, function(err){ console.log(err.stack); console.log(‘写入失败。’); resolve(false); }); })}// 显示文件内的api-keyconst list = ()=> { const keys = getKeys(); if (keys.length === 0) { console.log(‘没有存储api-key’); } else { keys.forEach((key)=> { console.log(key); }); }};module.exports = { sleep, checkApiKey, getKeys, addKeyToFile, list} ...

March 8, 2019 · 5 min · jiezi

vueSSR: 从0到1构建vueSSR项目 --- 伪热更新

伪热更新上一期写了一些问题。原计划是暂时不做处理的,最终结果就是今天没忍住鸭。废话不多说先贴思路1.先进行本地编译2.编译完成后,利用chokidar分别监听源码文件以及编译后的文件。(源码改动,触发编译。编译后的文件发生改动,触发重载)3.开启node服务4.开启browserSync代理服务用到的插件以及模块 npm //执行脚本命令 browser-sync //刷新浏览器的核心 chokidar //监听文件的修改 child_process //开启子进程 执行脚本步骤1直接使用npm对象执行写好的命令function directives(commond,cb){ npm.load(function(){ //清除缓存 npm.commands.cache([‘clean’], function(){ npm.commands.run([commond],cb); }) })}//执行 npm run devfunction dev(cb){ return directives(‘dev’,function(){ cb && cb(); })}步骤2chokidar可以监听文件。具体去看文档嘛chokidar的change事件,只要你进行保存就会触发 不管你有没有发生改变。//监听源码部分//监听源码函数,发生改变 开启子进程 执行编译命令 function soundCode(cb){ console.log(${num+=1}.chokidar开始监听src&amp;public下的文件) const warcher = .watch([‘src//.’,‘public//.’]) warcher.on(‘all’, (event, path) => { if(event ===‘change’){ console.log(’\033[40;31m ‘+path+‘源码发生修改,进行编译,请稍后’); //开启子进程并执行命令 child_process.exec(’npm run dev’,function(error, stdout, stderr){ if (error) { console.log(error.stack); console.log(‘Error code: ‘+error.code); console.log(‘Signal received: ‘+error.signal); } console.log(stdout); console.log(‘编译完成’); }) } }); console.log(’\033[40;32m 源码监听完成’);}// 监听编译后的代码(js)是否发生改变(不如说是进行保存)//监听distDev下的文件 编译后的代码 function compileCompleteCode(cb){ console.log(${num+=1}.chokidar开始监听distDev下的文件); //该文件产生变化时 说明构建已完成。 const watcher = chokidar.watch(‘distDev//*.js’); watcher.on(‘all’, (event, path) => { if(event === ‘change’){ console.log(path+‘发生变化,开始进行热更新’); bs.reload(path); console.log(‘热更新已完成’); } }); console.log(’\033[40;32m 编译后的代码监听完成’);}步骤3 directives(‘devServer’);步骤4 bs.init({ proxy: ‘http://localhost:8080’, open: false });整体代码const npm = require(’npm’);const bs = require(‘browser-sync’).create();const chokidar = require(‘chokidar’);const child_process = require(‘child_process’)var num = 0;//计数function directives(commond,cb){ npm.load(function(){ npm.commands.cache([‘clean’], function(){ npm.commands.run([commond],cb); }) })}function dev(cb){ return directives(‘dev’,function(){ cb && cb(); })}//监听源码函数function soundCode(cb){ console.log(${num+=1}.chokidar开始监听src&amp;public下的文件) const warcher = chokidar.watch([‘src//.’,‘public//.’]) warcher.on(‘all’, (event, path) => { if(event ===‘change’){ console.log(’\033[40;31m ‘+path+‘源码发生修改,进行编译,请稍后’); child_process.exec(’npm run dev’,function(error, stdout, stderr){ if (error) { console.log(error.stack); console.log(‘Error code: ‘+error.code); console.log(‘Signal received: ‘+error.signal); } console.log(stdout); console.log(‘编译完成’); }) } }); console.log(’\033[40;32m 源码监听完成’);}//监听distDev下的文件 编译后的代码function compileCompleteCode(cb){ console.log(${num+=1}.chokidar开始监听distDev下的文件); //该文件产生变化时 说明构建已完成。 const watcher = chokidar.watch(‘distDev//*.js’); watcher.on(‘all’, (event, path) => { if(event === ‘change’){ console.log(path+‘发生变化,开始进行热更新’); bs.reload(path); console.log(‘热更新已完成’); } }); console.log(’\033[40;32m 编译后的代码监听完成’);}console.log(${num+=1}.进行本地编译);dev(function(){ console.log("\033[40;31m 编译完成") soundCode() compileCompleteCode() console.log(${num+=1}开启node服务) directives(‘devServer’); console.log(${num+=1}开启browserSync代理服务); bs.init({ proxy: ‘http://localhost:8080’, open: false });});项目github地址项目公网地址1) vueSSR: 从0到1构建vueSSR项目 — 初始化2) vueSSR: 从0到1构建vueSSR项目 — 路由的构建3) vueSSR: 从0到1构建vueSSR项目 — node以及vue-cli3的配置4) vueSSR: 从0到1构建vueSSR项目 — vuex的配置(数据预取)5) vueSSR: 从0到1构建vueSSR项目 — 开发环境的部署6) vueSSR: 从0到1构建vueSSR项目 — 伪热更新 ...

March 7, 2019 · 2 min · jiezi

基于 Babel 的 npm 包最小化设置

翻译:疯狂的技术宅原文:http://2ality.com/2017/07/npm…本文首发微信公众号:jingchengyideng欢迎关注,每天都给你推送新鲜的前端技术文章本文描述了通过 Babel 生成 npm 包的最小设置。你可以在 GitHub 中看到案例 re-template-tag 的设置。GitHub:https://github.com/rauschma/r…1 核心结构有两组文件:目录 esm/ 里有库的(还转换)实际源码。package.json 中的 module 属性指向 esm/index.js目录 test/ 含有基于AVA的对 esm/ 中代码的测试。目录 cjs/ 有 ESM 文件的已转换版本,它们是 CommonJS 格式并支持在当前版本的 Node.js 环境下运行。package.json 中的 main 属性指向 cjs/index.js此结构支持两种用例:Node.js 应用使用 cjs/ 中的文件。Web应用(通过 webpack 等)使用 esm/ 中的文件。它们通过 babel-preset-env 将这些文件转换为其目标平台支持的功能集。 另一篇文章 中描述了如何执行这个操作。到此我们仅部分解决了如何填充可能缺少的库这个问题。接下来要彻底解决它2 .gitignorecjsnode_modulescjs/ 没有提交到 git。我们只是在 npm 发布包之前按需生成它。3 .npmignorenode_modules发布到 npm 时,我们需要包含 cjs/。这就是除了 .gitignore 之外我们还需要 .npmignore 的原因。4 package.jsonpackage.json 的主要部分可以使用以下脚本:“scripts”: { “build”: “babel esm –out-dir cjs”, “prepublishOnly”: “npm run build”, “test”: “ava”},build 用于生成 cjs/中的文件。prepublishOnly 能够确保在我们发布到 npm 之前始终构建 cjs /。test 通过 AVA 运行测试。因此,我们有以下依赖项(仅在开发时):“devDependencies”: { “babel-cli”: “^6.24.1”, “ava”: “^0.21.0”, “babel-preset-env”: “^1.5.1”, “babel-register”: “^6.24.1”},单元测试需要ava。babel-cli 提供命令 babel。babel-register 让 AVA 通过 Babel 执行测试。babel-preset-env 是 Babel 用于转换的预设。“main”: “./cjs/index.js”,“module”: “./esm/index.js”,main 是 CommonJS 格式的包入口点(包括在Node.js上运行的普通模块)。module是 ESM 格式的包入口点(包括webpack,取决于你如何设置它)。有关这两个属性的更多信息:“设置多平台 npm 包”。配置 Babel对于Babel,我们用 babel-preset-env 典型的方式为当前运行的 Node.js 生成代码。“babel”: { “presets”: [ [ “env”, { “targets”: { “node”: “current” } } ] ]},配置 AVA对于 AVA,我们需要 babel-register,它通过 Babel 转换所有测试和导入。 “babel”: “inherit” 表示 AVA 使用上一节中的配置。“ava”: { “require”: [ “babel-register” ], “babel”: “inherit”}5 结论以上是通过 Babel 创建 npm 包最小库的方法。重要的一点是它允许包的客户端使用 babel-preset-env(就像通过npm提供未转换的源代码 ”中所讲的那样)。所以它并没有 100% 的正确使用 module ,但是有广泛支持的优势,并且没有引入另一个 package.json 属性。6 扩展阅读免费在线书籍: “Setting up ES6”Delivering untranspiled source code via npmbabel-preset-env: a preset that configures Babel for you欢迎继续阅读本专栏其它高赞文章:12个令人惊叹的CSS实验项目世界顶级公司的前端面试都问些什么CSS Flexbox 可视化手册过节很无聊?还是用 JavaScript 写一个脑力小游戏吧!从设计者的角度看 ReactCSS粘性定位是怎样工作的一步步教你用HTML5 SVG实现动画效果程序员30岁前月薪达不到30K,该何去何从第三方CSS安全吗?谈谈super(props) 的重要性本文首发微信公众号:jingchengyideng欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章 ...

March 5, 2019 · 1 min · jiezi

npm管理依赖

semvernpm 依赖管理的一个重要特性是采用了语义化版本 (semver) 规范,作为依赖版本管理方案。版本号semver 约定一个包的版本号必须包含3个数字,格式必须为 MAJOR.MINOR.PATCH, 意为 主版本号.小版本号.修订版本号.MAJOR 主版本号,对应大的版本号迭代,做了不兼容旧版的修改时要更新 MAJOR 版本号MINOR 次版本号,对应小版本迭代,发生兼容旧版API的修改或功能更新时,更新MINOR版本号PATCH 修订号,对应修订版本号,一般针对修复 BUG 的版本号例如:0.y.z 表示开发阶段,一切可能随时改变,非稳定版。1.0.0 界定此版本为初始稳定版,后面的一切更新都基于此版本进行修改在常规仅包含数字的版本号之外,semver 还允许在 MAJOR.MINOR.PATCH 后追加 - 后跟点号分隔的标签,作为预发布版本标签 - Prerelese Tags,通常被视为不稳定、不建议生产使用的版本。例如:1.0.0-alpha1.0.0-beta.1版本限定在进行包管理时,为了保证安装依赖的兼容性,必须对依赖包版本进行限定.版本限定的语法简述为为 [范围描述]<版本号描述>。1.范围描述范围描述可选,必须配和版本描述确定范围,无法独立存在。< 小于某一版本号<= 小于等于某一版本号> 大于某一版本号>= 大于等于某一版本号= 等于某一版本号,没有意义和直接写该版本号一样- 某个范围,语法为 <版本描述>-<版本描述>例如:1.2.3-2 支持 >=1.2.3 <3.0.0 的版本1.x.1 支持 >=1.0.1 <1.1.0 的版本1 表示 >=1.0.0 <2.0.0 其余任意位置为空相似1.0 >= 1.0.0 < 1.1.0~,^,^ 的表述需要结合具体的包管理工具和版本号规则来确定,但是对于一般使用记住如下原则: ^ 是确保版本兼容性时,默认对次版本号的限定约束 ~ 是确保版本兼容性时,默认对补丁号的约束例如:1.1.1 >=1.1.1 <1.2.01.1 >=1.1.0 <1.2.01 >=1.0.0 <2.0.0^1.1.1 >=1.1.1 <2.0.0^0.1.1 >=0.1.1 <0.2.0^0.0.1 >=0.0.1 <0.0.22.版本号描述* 通配符,类似 glob 模式 x,X 约等于 * 号,通常用于次版本和补丁的通配例如: 支持 >= 0.0.0 的版本x 支持 >= 0.0.0 的版本包安装npm installnpm install/i <Module Name> //安装某个包详细npm i //当存在package.json时,使用命令可全部安装包 npm i <module name>@1.0.0 //安装指定版本的模块npm i <module name>@latest //安装最新版本npm i <module name@">=0.1.0 <0.2.0"npm i <git url>-S, –save 安装包信息将加入到dependencies(生产阶段的依赖)-D, –save-dev 安装包信息将加入到devDependencies(开发阶段的依赖),所以开发阶段一般使用它-O, –save-optional 安装包信息将加入到optionalDependencies(可选阶段的依赖)-E, –save-exact 精确安装指定模块版本全局安装与本地安装npm install/i <Module Name> //本地安装 npm install/i <Module Name> –global/-g //全局安装全局安装区别1.安装位置npm root -g //查看全局node_modules路径npm root //查看本地node_modules路径全局安装:一般在 Users用户名AppDataRoaming 目录下。本地安装:一般安装在指定项目的node_modules文件夹下。2.调用方式全局安装:用户可以在命令行中直接运行该包支持的命令。本地安装:需在项目中引用。包维护增加删除npm uninstall <Module Name> //卸载模块npm remove <Module Name> //移除模块升级npm updatenpm update <package> -g //升级全局安装的指定模块npm update <package> //升级当前目录下的指定模块npm update –save-dev //升级当前目录下全部模块降级npm outdatednpm outdated [[<@scope>/]<pkg> …] ...

February 26, 2019 · 1 min · jiezi

从0到1,一步步开发React的loading组件,并发布到npm上

没有发布过npm包的同学,可能会对NPM对开发有一种蜜汁敬畏,觉得这是一个很高大上的东西。甚至有次面试,面试官问我有没有发过npm包,当时只用过还没写过,我想应该挺难的,就小声说了没有,然后就让我回去了o(╯□╰)o。其实,在现在的我看来,npm包就是一个我们平时经常写的一个export出来的模块而已,只不过跟其它业务代码耦合性低,具有较高的独立性。当然,要发布一个npm包,除了写的模块组件外,还需要做一些基础的包装工作。下面我就以最近开发的react-loading组件为例源码地址,如果对你有帮助的话希望不要吝啬你的 Star本文主要记录一下如何开发react组件(以react-loding为例),并在 npm 上发布。废话不多说,进入正题创建组件新建项目仓库并初始化在github上新建一个仓库,名字可以自己取。确保你创建的组件名称没有在 npm 上被使用过, 这里我们用 wsm-loading作为示例。打开编辑器将项目拉到自己本地并初始化git clone https://github.com/xxx/wsm-loading.gitcd wsm-loadingnpm init运行npm init问题提示列表可以根据自己的个人爱好填写,也可以采取默认的选项。安装项目的依赖我么需要开发的是一个React的loading组件,所以我们要先在项目中安装react依赖npm i react react-dom -D我们的项目将通过webpack4进行构建,安装项目所需的webpack依赖npm i webpack webpack-cli webpack-dev-server html-webpack-plugin style-loader css-loader babel-core babel-loader babel-preset-env babel-preset-react -D在项目中我们经常会用到scss作为css的编译,因此我们在安装webpacknpm install sass sass-loader node-sass webpack -D这时上面安装的依赖已经被添加到根目录下的 package.json中了,接下来我们添加一个 start的脚本,用于启动我们本地开发的服务器, start如下:{ “name”: “wsm-loading”, “version”: “0.0.1”, “description”: “这是一个react-loading组件”, “main”: “index.js”, “scripts”: { “test”: “echo "Error: no test specified" && exit 1”, “start”: “webpack-dev-server –mode development”, },开发loading组件现在让在我们的项目中创建组件和示例代码目录,目录树结构如下├── examples // 示例代码存放目录│ └── src├── node_modules├── package.json└── src // 组件源代码和样式存放目录└── .babelrc // es6及jsx语法编译└── .gitignore // git提交管理└── package-lock.json└── package.json└── webpack.config.js webpack配置lodaing组件接收两个props,一个size控制loading的大小,一个color控制loading的颜色/*** src/index.js /import React, { Component } from “react”;import “./index.scss”;export default class MyComponent extends Component { render() { const { color, size } = this.props; const sizeStyle = { width: ${size}px, height: ${size}px }; const colorStyle = { border: 1px solid ${color}, borderColor: ${color} transparent transparent transparent }; const ringStyle = Object.assign({}, colorStyle, sizeStyle); return ( <div className=“loading” style={sizeStyle}> <div className=“loading__ring” style={ringStyle} /> <div className=“loading__ring” style={ringStyle} /> <div className=“loading__ring” style={ringStyle} /> </div> ); }}MyComponent.defaultProps = { size: ‘36’, color: ‘#000’ }loading组件的样式这里就不给了,可以直接从我的代码库中复制到你的项目里css。接下来我们添加一个演示demo<!– examples/src/index.html –><html><head> <title>My Component Demo</title> <meta charset=“utf-8”> <meta name=“viewport” content=“width=device-width, initial-scale=1, shrink-to-fit=no”></head><body> <div id=“root”></div></body></html>/ examples/src/index.js ***/import React from ‘react’;import { render} from ‘react-dom’;import MyComponent from ‘../../src’;const App = () => ( <MyComponent size=‘36’ color=‘red’ />);render(<App />, document.getElementById(“root”));loading组件优化以上就完成了一个基本的loading组件,但是我们还有一个疑问,就是现在引入的loading是在类名为root的元素之中,而一些被广泛使用的UI框架中的loading组件确实在body层,无论你在哪里引入,这样就可以防止loading组件受到父组件的样式的干扰。我们平时普通的组件的html结构如上图,可见我们的loading是嵌套在我们的主体root元素中的。在仔细看下面这张图。想要实现这种效果,我们必须得先了解React自带的特性:Portals(传送门)。这个特性是在16版本之后添加的。我们哎src目录下新建一个newPortal/newPortal.js文件import React from ‘react’;import ReactDOM from ‘react-dom’;class NewPortal extends React.Component { constructor(props) { super(props) this.node = document.createElement(‘div’); document.body.appendChild(this.node); } render() { const { children } = this.props; return ReactDOM.createPortal( children, this.node, ); }}export default NewPortal将其引入到我们的loading组件中import NewPortal from ‘./newPortal/newPortal’并对组件进行一层包裹 <NewPortal> <div className=“loading” style={sizeStyle}> <div className=“loading__ring” style={ringStyle} /> <div className=“loading__ring” style={ringStyle} /> <div className=“loading__ring” style={ringStyle} /> </div> </NewPortal>这里作者是参考这篇文章对组件进行优化的。大家感兴趣可以去了解下。配置webpack接下来配置 webpack, 在项目根路径下创建 webpack.config.js文件。关于webpack配置不太了解或不熟悉的可以参考我之前之篇文章webpack详细配置,这里讲不在一一讲解。const path = require(“path”);const HtmlWebpackPlugin = require(“html-webpack-plugin”);const htmlWebpackPlugin = new HtmlWebpackPlugin({ template: path.join(__dirname, “examples/src/index.html”), filename: “./index.html”});module.exports = { entry: path.join(__dirname, “examples/src/index.js”), output: { path: path.join(__dirname, “examples/dist”), filename: “bundle.js” }, module: { rules: [ { test: /.(js|jsx)$/, use: “babel-loader”, exclude: /node_modules/ }, { test: /.css$/, use: [“style-loader”, “css-loader”] }, { test: /.scss$/, use: [“style-loader”, “css-loader”, “sass-loader”] } ] }, plugins: [htmlWebpackPlugin], resolve: { extensions: [".js", “.jsx”] }, devServer: { port: 3001 }};配置babelrc最后需要指定 Babel 需要对哪些文件进行编译,毫无疑问 React 中使用的 JSX 文件需要被编译,让它转换成被主流浏览器都支持的 ES5 ,通用的配置也很简单,只需要添加一对 presets,在项目根目录下添加文件.babelrc{ “presets”: [“env”, “react”]}接下来运行 demonpm start启动完成后打开浏览器输入 http://localhost:3001,你将会在页面上看到你写的组件,你可以修改你的代码并保存,页面将会自动刷新,我们的开发环境已经处于监控模式发布到npm上我们要发布被 babel 编译且被压缩后的版本,要让没有使用 babel 的项目也能够正常的使用,比如不能出现 JSX 语法。首先需要安装 babel clinpm i babel-cli -D现在我们添加 transpile脚本,以便使用 Babel 编译我们的源代码,同时拷贝一些静态文件(如:css 文件)到目标打包目录dist下同时指定被编译后的版本为组件的主入口,更改后的 package.json如下{ “name”: “test”, “version”: “1.0.0”, “description”: “ceshi”, “main”: “index.js”, “scripts”: { “test”: “echo "Error: no test specified" && exit 1”, “start”: “webpack-dev-server –mode development”, “transpile”: “babel src -d dist –copy-files” },尝试编译npm run transpile最后让我们在项目的根目录下添加.npmignore文件,告诉 npm,我们项目中哪些文件和文件夹是在发布的包中被忽略掉的# .npmignore srcexamples.babelrc.gitignorewebpack.config.js发布我们的组件到 npm 上npm publish提交npm之前需要去npm上开通自己的账号,并验证邮箱,这样才能正常的提交上去。在终端使用npm login登陆自己的信息将代码上传到git项目上传到git之前,我们需要配置下.gitignore,在项目的根目录下添加.npmignore文件# .gitignorenode_modulesdist这样我们上传时,不会上传node_modules和dist的目录git add .git commit -m ‘react-loading组件’git push后语当我们不停的陷于业务代码的开发中,何不尝抽出一点时间来做点自己喜欢做的事呢,比如将自己的业务组件抽出来做成一个npm,开源组件库不仅可以提高自己还能接触到原来接触不到东西。这只是一个简单的教程,希望能对你有所启发。感谢你的阅读! ...

February 26, 2019 · 2 min · jiezi

ggit (git gui) --- 开发记录 (一)

ggit-npm-tool https://www.npmjs.com/package…文章后续更新,存入草稿时间长会被删, 希望谅解

February 24, 2019 · 1 min · jiezi

官方解决所有 npm 全局安装权限问题

官方解决所有 npm 全局安装权限问题https://docs.npmjs.com/resolv…// 当前用户根目录下rm -rf .node-gyp// 重装nodejsmkdir /.npm-globalnpm config set prefix ‘/.npm-global’export PATH=/.npm-global/bin:$PATHNPM_CONFIG_PREFIX=/.npm-global添加环境变量cd vi .bashrc// 输入export PATH=/.npm-global/bin:$PATH// 保存退出// 加载新配置source ./.bashrcecho “[ -r ~/.bashrc ] && source ~/.bashrc” >> .bash_profile

February 22, 2019 · 1 min · jiezi

从dist到es:发一个NPM库,我蜕了一层皮

这并不是自己第一次发npm包, 所以这里没有多少入门的知识。在此之前已经有一篇前端脚手架,听起来玄乎,实际呢?,但这一次的npm包和上一次的不是一个概念,前者只是一个脚本工具,而这个npm包是日常开发中方法和组件的集合, 是一个库。在读本文前,假定你已经对npm包有一定概念,熟悉Babel编译和webpack打包的常规用法,知道一些前端工程化的知识。假如你也想自己发布一个npm仓库,但对这一块了解的不是很多,推荐webpack官方的创建一个 library打包模式:日常构建与库的构建在前端日常开发中,引入npm库,执行webpack构建已经是一件不能再平常的事情。但大多数时候,我们不关心这个npm库是怎样构成的,我们只需要知道怎么使用,像antd;在工程化成熟的公司,也不关心webpack的配置到底是怎样的,只需要npm run start或npm run build去启动一次热加载或打包。但是如果你是编写一个npm仓库,这些东西你都需要知道。从那时的无知说起,起初,我用公司的构建工具(类似于roadhog)去打包我的库,没有坎坷,构建出一个2M多的包并成功发布。在测试项目中引入,构建成功。import { EnhanceTable, WithSearch } from ‘antd-doddle’; // 引入仓库在浏览器中打开,打击开始到来:提示要引入的对象没有正确导出,就是没有做module.export,所以这是一个打包模式的问题,output.libraryTarget需要了解一下。webpack的output.libraryTarget决定了打包时对外暴露出来的对象是那种模式,默认是var,用于script标签引入,该模式也是我们日常开发构建最常用的模式,除了这一种,还支持的常用选项有:commonjs(2):node环境CommonJS规范,关于commonjs与commonjs2的区别,commonjs 规范只定义了exports,而 module.exports是nodejs对commonjs的实现,实现往往会在满足规范前提下作些扩展,所以把这种实现称为了commonjs2;amd:amd规范,适用于requireJS;this:通过 this 对象访问(libraryTarget:’this’);window:通过 window 对象访问,在浏览器中(libraryTarget:‘window’)。UMD:将你的 library 暴露为所有的模块定义下都可运行的方式。它将在 CommonJS, AMD 环境下运行,或将模块导出到 global 下的变量,(libraryTarget:‘umd’)。jsonp:这是一种比较特殊的模式,适用于有extrnals依赖的时候(splitChunks)。将把入口起点的返回值,包裹到一个 jsonp 包装容器中。所以在这里我们需要设置两个属性来明确打包模式 library: ‘antd-doddle’, libraryTarget: ‘umd’,2M到38KB, 这中间发什么了什么上图是用roadhog打包出来的结果,其显示的是开启gzip后可以压缩到的大小,第一次打包的实际大小大概在2M(antd+moment+react+css),后面仔细一想,公司的组件库也才300kb啊,自己是不是哪里搞错了,所以接着就有了下面的探寻之路。抽离css(300kb),由于此npm库是基于antd的,所以就没有再把antd的css打包一次的必要了。基于roadhog给予的提示,配置了disableAntdStyle为false,css文件降到2kb;接着上面,虽然是基于antd的,但并没有完全用到antd的所有组件,其官方提供了一个按需打包babel插件babel-plugin-import,并在babelrc中配置, js打包体积由1.6M降为1.2M; [“import”, { “libraryName”: “antd”, “libraryDirectory”: “lib”, “style”: “css”, }]如果对webpack多了解一下,或者在写一个库之前读过 创建一个 library,就会发现前面两点都是白扯没有用的,因为对于这个库来说antd就是一个外部依赖(externals),正好roadhog又支持, 打包出来,由1.2M变为38kb, 这是一个质的提升。 externals: { react: { commonjs: ‘react’, commonjs2: ‘react’, amd: ‘react’, }, antd: { commonjs: ‘antd’, commonjs2: ‘antd’, amd: ‘antd’, }, moment: { commonjs: ‘moment’, commonjs2: ‘moment’, amd: ‘moment’, }, }打包大小优化至此就搞定了,但后面发现用roadhog打包库有一些很难解决的难题,为了解决还得去了解他源码逻辑,所以后面还是自己写了一个webpack,非常简单的配置。ES6之后,光有dist是不够的在写这个库之前,我曾想到在我们日常构建时有下面这样一段配置: rules: [{ test: /.js$/, exclude: /(node_modules|bower_components)/, loader: ‘babel-loader’, query: { presets: [’@babel/preset-env’, ‘@babel/preset-react’] } }这段配置是告诉webpack,node_modules中引用的代码不需要再由babel编译一次,但这些代码还是会被打包进dist文件的。在现在前端的主流开发套路中,被引用的库更希望是一个只编译而没有被打包过的,支持按需加载的库。所以dist中被编译打包过的代码再次被打包, 这样就会有不必要的代码出现。所以这样来看,我们只需要将代码编译。babel,没错,就是它,但是多文件编译,还是找个第三方构建工具比较好,我选择了gulp,直接上代码:// 发布打包gulp.task(’lib’, gulp.series(‘clean’, () => { return gulp.src(’./src/**/*.js’) .pipe(babel()) .pipe(gulp.dest(’./lib’));}, ’lessToLib’)); // lessToLib用于将less文件拷贝贷lib文件夹编译过后大概是下面这样的,确实只编译没打包,函数基本原样:本以为到这就结束了,但是这才开始。只编译不打包消除不必要的代码只是很小的原因,重要的东西我觉得换一行说比较好。不顾语文老师的责骂换行,那什么才是是最重要的:按需打包(tree shaking),对于这种组件和方法库,作为使用者,我们希望他能支持按需打包,像lodash和antd这样。所以怀着好奇的心理我去看了他们的package.json,然后发现了这样的配置: “main”: “lib/index.js”, “module”: “es/index.js”, “name”: “antd”,除了认知中的main入口定义,还多了一个module入口.为什么需要这样呢,和我一样无知的,可以先读webpack官方的tree shaking,如果不够直观,可以再看一位大佬写的一篇相关文章聊聊 package.json 文件中的 module 字段。看下面代码:// es6 模块写法 fun.jsexport function square(x) { return x * x;}export function cube(x) { return x * x * x;}// commonJs写法 fun.jsexports.square = function(x) { return x * x;}exports.cube = function(x) { return x * x * x;}// index.js引入import { square} from ‘./fun.js’;const res = square(10);console.log(‘res:’, res);简单来说,就是index.js打包编译时,引入commonJs写法的fun.js,打包会将square与cube两个函数同时打进来。而引入es6写法的fun.js,只会将square打包。这样的操作,对于现在的主流趋势,就是必须的优化,特别对于lodash和antd这种庞大的库。而要使我们的库支持这样的操作,我们需要编译时,禁止babel将es6的module引入方式编译,其实只需要在前面的基础上多配置一个参数:"@babel/preset-react" // lib的打包方式["@babel/preset-env", { “modules”: false }] // 保留es6模块引入的方式得到的是下面这样的结果:和上面的lib对比,感觉更接近原始代码。至此,编译已结束,但是我们还需要在package.json中加上相应的配置: “description”: “antd后台项目前端组件封装和方法库”, “main”: “lib/index.js”, “module”: “es/index.js”, “scripts”: { “build”: “webpack –config webpack.config.js”, “lib”: “gulp lib”, “es”: “gulp es”, “prepublish”: “gulp && webpack –config webpack.config.js” }, “files”: [ “es”, “dist”, “lib”, “utils” ],怎么让库支持多目录输出因为我的库主要包括组件和方法,我把方法放到一起,通过utils作为默认输出。然后项目中引入是这样的:import { EnhanceTable, WithSearch }, utils from ‘antd-doddle’; // 要用里面的方法需要再分解一次或通过utils.xxxconst { DATE_FORMAT, idCodeValid } = utils;虽然感觉上不复杂,但是总感觉别扭,如果你用过dva,就见过下面这样的引入:import { routerRedux } from ‘dva/router’;import dva from ‘dva’;所以我去学习了一下,发现要这样实现也不难分三步,分目录打包,增加一个输出,并增加内部私有映射,package.json增加一个这个映射目录的输出。具体可查看项目源码。实现后,项目引入是这样的:import { EnhanceTable, WithSearch }, utils from ‘antd-doddle’; import { DATE_FORMAT, idCodeValid } from ‘antd-doddle/utils’; // 一步到位小技巧分享npx执行本地命令以前我们很多命令如webpack,gulp命令只有在全局安装(npm install xxx -g)才可以在命令行中直接运行或在项目中安装,通过script定义执行,但在npm5.2以后,我们可以只项目中安装,然后通过新增的npx执行。比如上面scripts中定义的lib打包(“lib”: “gulp”),我们可以直接在命令行中用:npx gulp命令行切换npm registry有可能你和我一样,在到处都是墙的世界,需要在npm,cnpm,公司的npm registry三者之间来回切换,每次都需要这样:npm set registry ‘https://registry.npm.taobao.org/‘麻烦有没有? 幸好,这世界有很多牛逼的人,nrm registry是个很好用的工具,下面这样:// 安装npm install -g nrm// 设置入口npm,cnpm,companynrm add npm ‘http://registry.npmjs.org’nrm add cnpm ‘https://registry.npm.taobao.org’nrm add vnpm ‘http://npm.company.com’// 切换入口到淘宝入口nrm use cnpm后续一个春节自己断断续续就在倒腾这个,收获还是挺大的。后面自己会慢慢去学习怎么加入demo‘,加入单元测试,去建造一个完整的npm库。源码库:githubnpm仓库地址:npm ...

February 22, 2019 · 2 min · jiezi

网页水印SDK的实现

在网站浏览中,常常需要网页水印,以便防止用户截图或录屏暴露敏感信息后,追踪用户来源。如我们常用的钉钉软件,聊天背景就会有你的名字。那么如何实现网页水印效果呢?网页水印SDK,实现思路1.能更具获取到的当前用户信息,如名字,昵称,ID等,生成水印2.生成一个Canvas,覆盖整个窗口,并且不影响其他元素3.可以修改字体间距,大小,颜色4.不依赖Jquery5.需要防止用户手动删除这个Canvas实现分析初始参数 size: 字体大小 color: 字体颜色 id: canvasId text: 文本内容 density: 间距 clarity: 清晰度 supportTip: Canvas不支持的文字提示生成Canvas根据id生成Canvas,画布大小为window.screen大小,若存在原有老的Canvas,清除并重新生成。画布固定定位在可视窗口,z-index为-1 let body = document.getElementsByTagName(‘body’); let canvas = document.createElement(‘canvas’); canvas.style.cssText= ‘position: fixed;width: 100%;height: 100%;left:0;top:0;z-index: -1;’; body[0].appendChild(canvas);指纹生成算法 let canvas = document.getElementById(this.params.id); let cxt = canvas.getContext(‘2d’); let times = window.screen.width * this.params.clarity / this.params.density;//横向文字填充次数 let heightTimes = window.screen.height * this.params.clarity * 1.5/ this.params.density; //纵向文字填充次数 cxt.rotate(-15Math.PI/180); //倾斜画布 for(let i = 0; i < times; i++) { for(let j = 0; j < heightTimes; j++) { cxt.fillStyle = this.params.color; cxt.font = this.params.size + ’ Arial’; cxt.fillText(this.params.text, this.params.densityi, j*this.params.density); } }防止用户删除使用定时器,定时检查指纹是否存在 let self = this; window.setInterval(function(){ if (!document.getElementById(self.params.id)) { self._init(); } }, 1000);项目编译使用glup编译 var gulp = require(‘gulp’), uglify = require(“gulp-uglify”), babel = require(“gulp-babel”); gulp.task(‘minify’, function () { return gulp.src(’./src/index.js’) // 要压缩的js文件 .pipe(babel()) .pipe(uglify()) .pipe(gulp.dest(’./dist’)); //压缩后的路径 });指纹效果效果地址https://tianshengjie.cn/apps/…项目地址Github: https://github.com/Jay-tian/w...Npm包: https://www.npmjs.com/package… ...

February 19, 2019 · 1 min · jiezi

BundlePhobia

1、BundlePhobia用于分析npm package的依赖、bundle后的大小、下载速度预估等等,帮助你在引用一个package之前了解引入该package的代价。2、也可以将项目的package.json文件上传,BundlePhobia会帮你评估项目中所有包的大小和加载速度。

February 14, 2019 · 1 min · jiezi

前端npm 安装包,精选大全集合

如果您曾在 Node 或 JavaScript 前端开发中投入过时间和精力,那么您就知道 npm 中有数以十万计的模块可供您选择开发者不停的寻求帮助/抱怨:“对模块的选择困难正在蚕食我们”“X 模块和 Y 模块有什么区别?哪一个更好?”“npm 很棒,但是这些模块可能在一年半载后失效,取决于模块维护者是否积极”通常在提出这样的问题时,您会得到十个不同的答案。每个人都会给您推荐自己喜欢的模块,接下来就演变成争论哪一个是最好的。选择 npm 模块时很容易面临纸上谈兵。选择太多,而新来者在鼓吹“快上车”,为您的项目选择合适的 npm 模块可能是有难度的。而且这些模块中有许多做类似(或相同)的事情,这也没有帮助。与其浪费时间在 google 上搜索,在 npmjs.org 上搜索,或者浪费更多的时间不构建您的项目,还不如搞清楚什么时候该选择哪些模块。精选清单为了帮助解决这个问题,您将在下面找到针对最常见问题类型(即 web 框架、模板、身份认证等)的 npm 模块列表,以及何时使用这些模块。这里有一些注意事项:您可能熟悉其中一些模块,甚至许多模块,但是有时候您面对的是您还没有接触到的技术栈(可能是身份验证或 Websocket 之类的东西),您需要知道有哪些备选模块可以完成这项工作。您可能有您认为更好的模块,或者可能有一个用例/需求没有包含在这里。我没有列出相同类别的 10 个不同模块,而是缩小了范围,这样您就可以避免分析瘫痪的陷阱。如果您认为自己的用例未被涵盖,请务必自行研究解决。本清单的目的在于让您能更快地启动和运行。这些模块的选择依据如下:它们完成工作的能力如何社区规模(对于支持/故障排除很重要)积极维护如果您发现自己仍然没有足够的信息做出决定,我建议使用slant.co和nodejs.libhunt.com来帮助进行比较。注意:为了保持范围的合理性,这些模块都考虑到了服务器端。它们中的一些可以同时在客户机或服务器上使用,但我的原则是“服务器优先”。HTTP requests (HTTP 请求)Request:当您需要基于回调的 HTTP 请求时可选择它,例如从一个 REST 服务连接到另一个。Axios当您需要基于 Promise的 HTTP 请求时可选择它注意:您可以使用request-promise,但是 axios 的依赖更少,并且基于原生 PromisesWeb frameworks (Web 框架)Express:如果您想为 API、网站或单页应用程序使用轻量级 web 框架,请使用它您不介意使用回调作为默认的异步处理方式使用该框架的模块生态极为繁荣您需要一个支持和故障排除的大型社区Koa:当您想要一个比 Express 更简洁的框架时使用Koa 更像是一个中间件层,它不提供模板或开箱即用的路由,因此更适合 API 开发要想支持开箱即用,您需要 async / awaitHapi如果您想要一个比 Express 或 Koa 更“自带电池”(译者注:原文”batteries”意为您不必重复造轮子,大多数您需要的功能都能通过(已有)库完成。您能导入并使用它们。)的框架,但又不像 Sails 那么多,那就使用它Sails当您需要像 Rails 这样的东西时,请使用它,它具有几乎所有功能(但是根据您的应用程序可能不需要那么多)Validation (前端验证)Ajv在需要验证 JSON 时使用(比如来自 web 请求)您希望与应用程序的其他非 JS 部分共享这些验证规则(因为它是 JSON,所以您可以这样做)Joi在需要验证输入时使用,并且喜欢链式调用的风格(译者注:代码见下方),而不是在 JSON 中定义验证规则您正在使用 Hapi(Hapi 自带 Joi)const schema = joi.object().keys({ id: joi.string().guid().required(), username: joi.string().alphanum().min(8).required()});Authentication (身份认证)Passport:当您需要为您的网站或 API 使用身份验证中间件时使用您希望能够在多种身份验证类型(Oauth,Facebook 等)之间进行选择您需要管理会话Asynchronous (异步)Async (library):当您需要使用旧版本的 Node,而该版本的 Node 支持只支持回调而不支持 Promises 时ES6 原生 promises (原生 JS, 非 npm):在使用大于 0.12 的 Node 版本时使用另一件需要考虑的事情是您的团队对 Promises 的接受程度。在 2018 年,大多数开发人员应该没问题了async / await(原生 JS,非 npm)当您为了逃脱“回调地狱”却又误闯“Promise 地狱”您有很多来自 Promises 的.then 和.catchDatabase (数据库)下面是数据库驱动程序、ORM 和查询生成器的组合。在使用 ORM 之前,我强烈建议您首先确保需要使用它。当您可以只使用原始 SQL 或查询生成器时,它们通常会添加另一层抽象,这层抽象不一定能够提供足够的回报。mysql, node-postgres:当您不需要完整的 ORM,而是需要使用原始 SQL 查询数据库时使用(这些是驱动程序)node-mongodb-native:当您不需要一个完整的 ORM,而是要直接查询 MongoDB 时使用Mongoose:当您希望为 MongoDB 使用 ORM 时使用Knex:当您不需要一个完整的 ORM 解决方案,而只是需要一些工具使编写查询代码更容易,可以使用它Knex 是一个生成 SQL 的查询生成器您拥有 Postgres、MSSQL、MySQL、MariaDB、SQLite3、Oracle 或 Amazon Redshift 数据库Objection.js:您希望 ORM 支持 Knex 支持的所有东西,不使用查询 DSL(因此您编写的代码更接近原始 SQL),具有基于 Promise 的 API 和良好的文档Process management (进程管理)这个网址提供了部分进程管理器的横向比较http://strong-pm.io/compare/。注意:它们还包括了 StrongLoop Process Manager,这是一个不错的工具,但是有点笨重。我建议您在决定使用 StrongLoop 之前先查看一下解决方案。PM2:当您希望进程管理器在服务崩溃时处理重新启动,并允许您控制集群时使用注意:PM2 所依据的 AGPL 许可证存在一些潜在的违规行为。这里有一些讨论。我的看法是它最有可能被使用。但如果您有任何问题,请咨询您的法律部门,因为我不是律师。forever:当您需要进程管理器来处理在服务崩溃时重新启动服务时使用您的部署规模较小(pm2 及其集群支持用于更大规模的部署)。如果您只有少量的服务/进程,那么您可能可以使用它nodemon:当您希望监视应用程序中的任何代码更改时使用,并在本地开发时自动重启服务器非常适合用于开发!Web Sockets对于 Web Sockets,我只是推荐 primus,而不是列出一个列表。它支持所有主要的 Web Sockets 实现,并且维护者十分积极。如果您需要换成其他的库,您可以通过一行代码更改轻松地更换。Primus:当您需要 Web Sockets 但又不想被束缚在特定的 Web Sockets 实现时使用API documentation (API 文档)Swagger-node:当您需要记录 REST API 并能够针对端点测试请求时使用Utilities/misc (通用工具/杂项)Lodash:当您需要 JS 实用程序库时使用您使用了大量的 OOP(面向对象编程)Ramda:当您希望使用函数式的编程风格时,请使用您想要像 lodash 这样的东西,但是在函数式编程范式中Moment:在需要解析、验证、操作和显示日期/时间时使用UUID:当您需要随机的、唯一的、难以破解的 id 时使用NVM:当您希望能够在环境中安装的多个 Node 版本之间切换时使用Fs-extra:当您需要能够递归地使用mkdir、rm -rf和 Node 中缺少的其他文件系统级功能时,请使用Nodemailer:当您需要从 Node 发送电子邮件时使用Dotenv:当您需要将.env 文件中的环境变量加载到 process.env 时使用CLI (命令行界面)Commander:当您要构建一个 CLI 程序时使用,该程序将所有参数作为命令行上的标志Inquirer:当您想要构建一个按顺序获取选项的“交互式”CLI 程序时使用(类似于运行 npm init 时的方式,它会询问您生成 package.json 文件的一系列问题)Logging (日志)Winston:当您需要一个日志库并需要不同的日志输出格式时使用Bunyan:当您需要一个日志库,并以 JSON 作为唯一日志输出格式时使用您希望为不同的组件、请求或函数使用不同的日志记录器(也就是说,这些日志记录器可能以不同的方式解析事件)Morgan:当您使用 Express 并且想要记录 HTTP 请求时使用注意:这将与 Winston 或 Bunyan 一起使用。由于它是中间件,它知道如何处理请求并记录它,但不处理 Winston 和 Bunyan 所做的日志输出的传输。Templating (前端模板)Pug (原 Jade):当您需要服务器端模板引擎时,请使用该引擎,该引擎易于阅读,并且支持开箱即用的子组件代码块您只需要输出 HTMLEJS:当您需要一个服务器端模板引擎,该引擎完全使用 JS,并且允许空格缩进(Pug 不允许)注意:不支持异步 JS 函数Testing (测试)Mocha:在需要编写和运行单元测试时使用Chai:当您需要证明您的单元测试中的断言时,请使用注意:这将与 Mocha 一起使用Chai-as-promised:当您希望在 promises 上证明您的断言时,而不是将断言放在 then 或 catch 中使用Sinon:当您需要用于测试的 mock 库时使用Tooling (开发工具)ESdoc:当您想从您的代码中生成 API 文档,并且您正在使用最新的 JS 版本时,请使用默认情况下支持当前版本的 JS(支持 class),因此如果在代码中使用 prototypes,请使用 JSdocJSdoc:当您需要支持 ES6 的代码 API 文档生成器时使用支持classes 和 prototypesESlint:当您需要一个 linter 来自动查找(和修复)代码中的语法和代码格式问题时使用(译者注:可参考本人博文vscode + vetur + eslint + prettier 实现团队代码风格统一)Debugging (调试)现在,原生 Node 调试现在已经够用了,我的建议是直接使用它。几年前,引入一些 npm 模块是很有帮助的,而且您可能有一个特定的用例需要一个 npm 模块,但是现在已经有了足够的本地支持,如果您对调试没有任何太疯狂要求,请务必忽略掉额外的依赖项。结论挑选模块可能很难,但您只需要一些方法点来解决它。当您正在为如何抉择浪费时间,或者甚至不知道从哪里开始时,请使用本指南来帮助您。 ...

February 12, 2019 · 2 min · jiezi

发布 react 组件到 npm 上

我发布了我的第一个 npm 组件,一个基于 react 的 3d 标签云组件。在这途中我也是遇到了很多的坑,花在完善整个发布流程的时间远多于写这个组件本身的时间,所以我记录下我觉得一个正常的 react 组件的发布流程最后记录这篇文章花的时间比我完成整个组件的时间都多,最终希望能给新手带来帮助在整个发布组件的过程我做了如下几件事儿:开发组件编写 Readme推送到 github,并且把 demo 放到 github page 上发布组件到 npm 上开发组件创建项目文件夹并初始化 npm package ,确保你创建的组件名称没有在 npm 上被使用过, 这里我们用 react-demo 作为示例mkdir react-democd react-demonpm initnpm init 是生成初始的 package.json 的命令,在 npm init 的时候,你可以根据你自己的需要进行填写你的组件信息。或者直接使用 npm init -y 采用默认的,后面自己再去修改。首先安装 react 相关的包:npm i react react-dom -D采用 babel 编译相关的依赖:npm i @babel/cli @babel/core @babel/preset-env @babel/preset-react -D采用 webpack 做构建,webpack-dev-server 作为本地开发服务器,所以需要安装如下依赖:npm i webpack webpack-cli webpack-dev-server -D我这里为了简单演示,只安装 babel-loader 用来编译 jsx,其他 loader 安装自己的需要自己安装。npm i babel-loader -D另外再安装一个 webpack 插件 html-webpack-plugin ,用来生成 html:npm i html-webpack-plugin -D然后再添加上常规的 start 和 build 脚本,package.json 如下:{ “name”: “react-demo”, “version”: “1.0.0”, “description”: “”, “main”: “index.js”, “scripts”: { “test”: “echo "Error: no test specified" && exit 1”, “start”: “webpack-dev-server –open development”, “build”: “webpack –mode production” }, “keywords”: [], “author”: “”, “license”: “ISC”, “devDependencies”: { “@babel/cli”: “^7.2.3”, “@babel/core”: “^7.2.2”, “@babel/preset-env”: “^7.3.1”, “@babel/preset-react”: “^7.0.0”, “babel-loader”: “^8.0.5”, “html-webpack-plugin”: “^3.2.0”, “react”: “^16.7.0”, “react-dom”: “^16.7.0”, “webpack”: “^4.29.0”, “webpack-cli”: “^3.2.1”, “webpack-dev-server”: “^3.1.14” }, “dependencies”: {}}当然,你也可以直接把我这个 package.json 复制过去,然后 npm install 进行依赖的安装,也可以一个一个的安装。一个最基本的组件只需要编译 jsx,所以我这里没有安装 css 以及处理其他的 loader,这篇文章的重点不是讲 webpack 的,所以其他的自行解决,有 webpack 问题可以私聊我。然后我们再创建如下的目录结构:├── example // 示例代码,在自己测试的时候可以把测试文件放到 src 里│ └── src // 示例源代码│ ├── index.html // 示例 html│ └── app.js // 添加到 react-dom 的文件├── package.json ├── src // 组件源代码│ └── index.js // 组件源代码文件├── .babelrc├── .editorconfig // 不必须的,但是建议有├── .gitignore // 如果要放到 github 上,这个是需要有的└── webpack.config.js下面我们再创建一个最简单的组件,来进行演示:/*** src/index.js /import React from ‘react’;const ReactDemo = () => ( <h1>这是我的第一个 react npm 组件</h1>);export default ReactDemo;接下来添加一个 demo<!– examples/src/index.html –><html><head> <title>My First React Component</title> <meta charset=“utf-8”> <meta name=“viewport” content=“width=device-width, initial-scale=1, shrink-to-fit=no”></head><body> <div id=“root”></div></body></html>/ examples/src/app.js ***/import React from ‘react’import { render } from ‘react-dom’import ReactDemo from ‘../../src’const App = () => <ReactDemo />render(<App />, document.getElementById(‘root’))注意 demo 中的 ReactDemo 是从 ../../src 中导入的接下来配置非常简单的 webpack, 在项目根路径下创建 webpack.config.js 文件const path = require(‘path’);const HtmlWebpackPlugin = require(“html-webpack-plugin”);const htmlWebpackPlugin = new HtmlWebpackPlugin({ template: path.join(__dirname, “./example/src/index.html”), filename: “./index.html”});module.exports = { entry: path.join(__dirname, “./example/src/app.js”), output: { path: path.join(__dirname, “example/dist”), filename: “bundle.js” }, module: { rules: [{ test: /.(js|jsx)$/, use: “babel-loader”, exclude: /node_modules/ }] }, plugins: [htmlWebpackPlugin], resolve: { extensions: [".js", “.jsx”] }, devServer: { port: 3001 }};Webpack 的配置文件主要做了如下事情:使用 example/src/index.js 作为项目入口,处理资源文件的依赖关系通过 babel-loader 来编译处理 js 和 jsx 文件通过 html-webpack-plugin 自动注入编译打包好的脚本文件为 demo 启动端口为 3001 的服务然后再配置一下 babel,咱们的 babel 主要做两件事,将 jsx 编译成 es5,然后再加一个通用的 env,所以 .babelrc 配置如下:{ “presets”: ["@babel/preset-env", “@babel/preset-react”]}可以看到之前的 package.json ,我这里 babel 安装的是 7.x,那么 babel-loader 就应该是 8.x 才行,然后 babel 7.x 相对于之前的配置是不同的,要用这个配置,版本一定要跟我的相同,不然配置可能会不一样。然后现在执行 npm start,然后再访问 localhost:3001 就可以访问到了。编写 README编写 README,如果你不知道该如何编写,我给你提几点建议,你可以选择你觉得必要的点来写:logo官方主页介绍安装快速开始功能列表截图todoList不足之处FAQChange Log(更新日志)添加徽章当你写完 README 之后,我们将添加一些来自 shields.io 的时髦徽章,让人们知道我们又酷又专业。想添加什么样的徽章看自己喜欢吧,种类有很多。可以点击这里看我之前写的 3d 标签云的 README。现在基本上可以发布了,但是要是能提供一个在线的 demo 让别人在用这个组件的时候可以看到效果就更好了。在 GitHub Pages 上发布一个在线 demo发布在线 demo 可以直接用 Github Pages 来帮助我们托管,通过 webpack 构建生产环境版本,然后发到 Github 上去即可。首先去 Github 创建一个用来存放你组件代码的仓库。然后把你的项目初始化成 git 项目:git init再添加远程仓库,将本地仓库和远程仓库关联起来。git remote add origin git@github.com:crazylxr/react-demo.git接下来我们可以安装 gh-pages 来帮助我们发布到 github pages:npm i gh-pages -D为了方便记忆,后续能更快的发布,这些命令我们可以写成 npm-scriprt,所以我们增加两个脚本:{ “name”: “@taoweng/react-demo”, “version”: “1.0.0”, “description”: “react demo”, “main”: “lib/index.js”, “scripts”: { “test”: “echo "Error: no test specified" && exit 1”, “start”: “webpack-dev-server –open development”, “build”: “webpack –mode production”, “deploy”: “gh-pages -d examples/dist”, “publish-demo”: “npm run build && npm run deploy” }, “keywords”: [], “author”: “”, “license”: “ISC”, “devDependencies”: { “@babel/cli”: “^7.2.3”, “@babel/core”: “^7.2.2”, “@babel/preset-env”: “^7.3.1”, “@babel/preset-react”: “^7.0.0”, “babel-loader”: “^8.0.5”, “gh-pages”: “^2.0.1”, “html-webpack-plugin”: “^3.2.0”, “react”: “^16.7.0”, “react-dom”: “^16.7.0”, “webpack”: “^4.29.0”, “webpack-cli”: “^3.2.1”, “webpack-dev-server”: “^3.1.14” }, “dependencies”: {}}添加了 deploy 脚本和 publish-demo,以后需要发布 demo 的时候只需要 npm run publish-demo 即可。然后我们就可以 build 项目之后再将 expamples/dist 发布到 gh-pages 分支:npm run buildnpm run deploy或者直接npm run publish-demo注意:这里只会将 expample/src 下的文件发布到 ph-pages 分支,master 分支依然没有到 github 上,如果你要把源码放到 github 的 master 或者其他分支上,还是需要自己 push 的。这个时候,我们可以通过 crazylxr.github.io/react-demo 访问到我们写的 demo。crazylxr 是 github 的 username,react-demo 是仓库名,注意改成你自己的。编译源码我们现在的源码是 jsx 的,所以我们需要通过 babel 把 jsx 编译为正常浏览器能访问的代码。我们可以通过 babel-cli 来编译我们代码,直接编译 src 目录,到 lib 文件夹。更多命令见 babel-clinpx babel src –out-dir lib执行完这个命令,就把生成一个 lib 文件夹,然后里面的 index.js 就是编译过后的文件,是可以直接发布到 npm 的文件。然后将这个编译命令写到 script 里,package.json 如下:{ “name”: “@taoweng/react-demo”, “version”: “1.0.0”, “description”: “react demo”, “main”: “lib/index.js”, “scripts”: { “test”: “echo "Error: no test specified" && exit 1”, “start”: “webpack-dev-server –open development”, “build”: “webpack –mode production”, “compile”: “npx babel src –out-dir lib”, “deploy”: “gh-pages -d example/dist”, “publish-demo”: “npm run build && npm run deploy” }, “keywords”: [], “author”: “”, “license”: “ISC”, “devDependencies”: { “@babel/cli”: “^7.2.3”, “@babel/core”: “^7.2.2”, “@babel/preset-env”: “^7.3.1”, “@babel/preset-react”: “^7.0.0”, “babel-loader”: “^8.0.5”, “gh-pages”: “^2.0.1”, “html-webpack-plugin”: “^3.2.0”, “react”: “^16.7.0”, “react-dom”: “^16.7.0”, “webpack”: “^4.29.0”, “webpack-cli”: “^3.2.1”, “webpack-dev-server”: “^3.1.14” }, “dependencies”: {}}那么以后要编译 src 下面的代码,只需要执行:npm run compile现在我们已经有编译好的代码了,接下来就可以发布到 npm 供其他人使用了。发布 npm 包在发布以前我们是需要一些准备:注册 npm 账户:在这里](https://www.npmjs.com/) 注册一个 npm 账号。登录在终端输入:npm adduser也可以用:npm login然后你会得到一个让你输入username、password 和 email 的提示,把它们填在相应的位置。关于 package.json 需要注意的点package.json 里面的配置信息非常重要,我解释一下几个重要的配置。name: 包名,如果你学习的话建议加一个 scoped,就是我上面的 @taoweng/react-demo 而不是 react-demo,因为 npm 包特别的多,很容易重复。这样这个包就会是私有的,可以通过 npm publish –access=public 将这个包变为共有的包。version: 包的版本,每次发布包的版本不能和上次一样。详细规范可见这里description:包的简介。repository:适合写 Github 地址,建议写成::username/:repository。license:认证。不知道该用什么的,就写MIT 吧。main:包的入口文件。就是引入这个包的时候去加载的入口文件。keywords:添加一些关键词更容易使你的包被搜索到。更详细的 package.json 配置可见官网。我这里简单的添加了这些信息:{ “name”: “@taoweng/react-demo”, “version”: “1.0.0”, “description”: “react demo”, “main”: “lib/index.js”, “repository”: “crazylxr/react-demo”, “scripts”: { “test”: “echo "Error: no test specified" && exit 1”, “start”: “webpack-dev-server –open development”, “build”: “webpack –mode production”, “compile”: “npx babel src –out-dir lib”, “deploy”: “gh-pages -d example/dist”, “publish-demo”: “npm run build && npm run deploy” }, “keywords”: [“react”, “demo”], “author”: “taoweng”, “license”: “MIT”, “devDependencies”: { “@babel/cli”: “^7.2.3”, “@babel/core”: “^7.2.2”, “@babel/preset-env”: “^7.3.1”, “@babel/preset-react”: “^7.0.0”, “babel-loader”: “^8.0.5”, “gh-pages”: “^2.0.1”, “html-webpack-plugin”: “^3.2.0”, “react”: “^16.7.0”, “react-dom”: “^16.7.0”, “webpack”: “^4.29.0”, “webpack-cli”: “^3.2.1”, “webpack-dev-server”: “^3.1.14” }, “dependencies”: {}}这些配置信息都会在 npm 包的页面显示出来的,所以能填还是填一下:最后我们在项目中添加 .npmignore 文件,跟 .gitignore 的作用一样,就是在发布 npm 的时候需要忽略的文件和文件夹:# .npmignore srcexamples.babelrc.gitignorewebpack.config.js这个时候我们就可以发布到 npm 了:npm publish如果你是私有包,可以这样发布:npm publish –access=public结语以后发布新版本的时候,只需要更改一下 package.json 里面的 version 版本号,然后执行 npm publish 和 npm run publish-demo 就可以同步 npm 和 demo。不过如果想让你的组件在社区里给更多人用,你需要把 README 写得更好一点,然后添加好自动化测试,不然别人不太敢用。另外在写组件之前可以先了解下有没有类似的组件了,如果有就直接用吧,咱们就站在巨人的肩膀上,把自己宝贵的时间放在创造价值上。最后整个项目的源代码见 github参考文章从 0 开始发布一个 react 组件到 npm创建并发布一个小而美的 npm 包,没你想的那么难!另外同时你也可以在这些地方找到这篇文章:个人网站github blog另外有兴趣可以关注我的公众号:前端桃园 ...

January 31, 2019 · 4 min · jiezi

Mac下npm报错

1、今天在Mac下运行npm命令时不知什么原因报错如下:☁ myhexo_blog [master] ⚡ node -v dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicui18n.59.dylib Referenced from: /usr/local/bin/node Reason: image not found[1] 33278 abort node -v2、解决方案:☁ myhexo_blog [master] ⚡ brew uninstall –force node Uninstalling node… (4,152 files, 47.9MB)☁ myhexo_blog [master] ⚡ brew uninstall icu4c && brew install icu4c Error: Refusing to uninstall /usr/local/Cellar/icu4c/63.1because it is required by php and php@7.1, which are currently installed.You can override this and force removal with: brew uninstall –ignore-dependencies icu4c☁ myhexo_blog [master] ⚡ brew unlink icu4c && brew link icu4c –forceUnlinking /usr/local/Cellar/icu4c/63.1… 0 symlinks removedError: No such keg: /usr/local/Cellar/–force☁ myhexo_blog [master] ⚡ brew install nodeUpdating Homebrew…==> Downloading https://homebrew.bintray.com/bottles/node-11.8.0.mojave.bottle.t######################################################################## 100.0%==> Pouring node-11.8.0.mojave.bottle.tar.gz==> CaveatsBash completion has been installed to: /usr/local/etc/bash_completion.d==> Summary???? /usr/local/Cellar/node/11.8.0: 3,938 files, 47.4MB3、之后运行node -v显示如下:☁ myhexo_blog [master] ⚡ node -vv11.8.0问题解决~ happy hacking… ...

January 30, 2019 · 1 min · jiezi

开发和维护个人开源项目之npm发布

开发和维护个人开源项目之npm发布项目的引入开始编码之前,项目如何被引入使用也需要考虑清楚。个人觉得使用npm包的方式引入是最佳选择:项目完全没有bug的可能性非常小,引入的打包后的结果基本无法调试,只能提issues或者另开环境,流程十分麻烦也不方便bug回溯;让使用者能够直接使用源码进行调试,就显得很酸爽了。另外如果项目包含了很多的输出,实际应用可能只需要其中部分输出,引入全部的代码会带来冗余(如:Lodash)。怎么按需引入,下文pakeage.json会讲到。此外webpack提供了UMD(Universal Module Definition)的配置:module.exports = { //… output: { library: ‘MyLibrary’, libraryTarget: ‘umd’ }};打包的输出:(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[‘MyLibrary’] = factory(); else root[‘MyLibrary’] = factory();})(typeof self !== ‘undefined’ ? self : this, function() { return entry_return; // 此模块返回值,是入口 chunk 返回的值});这样项目的引入方式基本能满足所有情况了。详细webpack配置点我。npm发布发布流程:npm init //初始化项目,名字版本等信息,后面可以改注册npm帐号npm loginnpm publishnpm unpublish 官方不推荐使用。pagekage.jsonmain模块的入口,例如:模块名字foo,reuqire(‘foo’),引入的文件路径(相对于当前包的根目录)由main字段定义,打包输出的结果为dist/index.js,配置即应该是:{ “main”:“dist/index.js”}peerDependencies表示预记宿主会安装的依赖。例如,安装babel-loader时,提示:又比如开发一个vue的组件,已知宿主会一定会安装: “peerDependencies”: { “vue”: “^2.5.0” }module(非标准字段)该配置指向打包前的源码入口。wepack在构建项目的时候,如果发现了这个字段,会首先使用这个字段指向的文件。除了源码更好调试外,还有一个优点Tree Shaking优化即按需引入。聊聊 package.json 文件中的 module 字段{ “main”:“src/index.js” //源文件目录为src}其他字段详细介绍点我看官方文档package.json 非官方字段集合其他.npmignore有些文件默认不会发布,有些文件是强制发布,具体请看官方文档,保证实时性。问题1如果npm login报错如下:npm ERR! code E409npm ERR! Conflictnpm ERR! A complete log of this run can be found in:npm ERR! C:\Users\Louis\AppData\Roaming\npm-cache_logs\2019-01-23T06_55_37_738Z-debug.log有可能是你当前使用的是淘宝的镜像,尝试npm config set registry https://registry.npmjs.org/再进行npm login总结需要构建的项目,采用npm来使用是开发的不二之选。 ...

January 28, 2019 · 1 min · jiezi

CentOS 7搭建 cnpm 私有仓库

服务端安装工具$ npm install -g –build-from-source cnpmjs.org cnpm sqlite3如果报错或者警告通过下面方式安装$ npm install -g –unsafe-perm –verbose –build-from-source cnpmjs.org cnpm sqlite3配置配置文件位于~/.cnpmjs.org/config.json中,{ “bindingHost”: “0.0.0.0”, “admins”: { “admin”: “admin@example.com” }, “scopes”: [ “@package” ], “registryHost”: “registry.company.com”}bindingHost:设置0.0.0.0后,服务可外网访问admins: 管理员配置,可多个scopes: 包前缀,如果不是以这个前缀命名的包将不能发布,可多个registryHost:下载包对应的服务器地址,如不配置会走默认r.cnpmjs.org更多配置参考:https://github.com/cnpm/cnpmj…运行$ cnpmjs.org start运行后默认将会开启两个端口:7001(registry),7001(registry)7001(registry)用来在命令行发布,下载包等对用的远程registry地址比如:cnpmjs.org官方:http://r.cnpmjs.org/npm官方:https://registry.npmjs.org/7002(web)用来在web端查看仓库信息,搜索包,包信息等。cnpmjs.org官方:https://cnpmjs.org/npm官方:http://npmjs.com/将两个端口号通过nginx配置到对应域名例如:7001配置到registry.company.com7002配置到npm.company.com通过 pm2 运行上述方法在命令行退出时,会自动关闭进程,如果需要持久化运行cnpmjs.org,可以借助进程管理工具pm2。安装 pm2$ npm install pm2 -g运行$ pm2 start which cnpmjs.org – start–可以在pm2 start的脚本后面增加参数。客户端发布包安装并配置配置 cnpm$ npm install cnpm -g$ cnpm set registry http://registry.company.com这里的registry地址为上述registryHost字段。登录$ cnpm login输入用户名、密码、邮箱登录,为配置中admins字段发布进入待发布工程目录,执行:$ cnpm publish下载包$ cnpm install @package/package_name

January 28, 2019 · 1 min · jiezi

grunt插件:grunt-plug-insert

作者:心叶时间:2019年01月24日 20:08需求说明在我们开发代码的时候,不会把全部代码写在一个文件,只有最后打包的时候会合并在一起,而有时候,我们除了要合并代码,还可能需要更灵活的合并方案。这个插件就是在一个代码文件中的指定位置插入一些了碎片文件。如何使用首先,需要安装node包:npm install grunt-plug-insert –save-dev一旦安装好了,你就可以在Gruntfile.js中加入下面代码,使用这个插件:grunt.loadNpmTasks(‘grunt-plug-insert’);以上和别的grunt插件都一样,我们主要说明一下如何配置任务。grunt.initConfig({ insert: { options: { // Place of segmentation separator: ‘@CODE inserts compiled test here’, // Insert the target file target: ’test/fixtures/test’ }, files: { // Target and fragmentation files ’tmp/test’: [’test/fixtures/insert1’, ’test/fixtures/insert2’] }, },});上面是配置的一个例子,和别的插件一样,也可以配置任务名称等,这里不再赘述了,我们来说一下几个配置选项的意思。options.separator:配置插槽。也就是我们会把『目标文件』中的这段字符串替换成一系列『碎片文件』,并最终保存在『打包目标文件』。options.target:『目标文件』files:我们可以在这里配置多个键值对,键是一个字符串,也就是『打包目标文件』,值是一个数组,也就是『碎片文件』。从上面的配置可以看出来,我们可以一次配置多个合并任务,上面只配置了一个。对例子说明为了帮助更好的理解,我们对上面的例子进行说明:把文件test/fixtures/test中的字符串"@CODE inserts compiled test here"替换成文件test/fixtures/insert1和文件test/fixtures/insert2,并把合并后的结果保存在文件tmp/test中。

January 24, 2019 · 1 min · jiezi

用verdaccio搭建npm私服

sinopia 有坑,已经弃坑使用Sinopia搭建私有npm仓库不能下载带有@符号的包幸好有一位大神fork了sinopia,并且自己维护更新。 github地址 : verdaccioverdaccio 的安装与sinopia 一样。参考地址:npm私服安装教程 - verdaccio 入门使用 nexus 搭建npm 私服nexus 上搭建npm本地服务器nexus搭建npm私服为什么需要搭建私有npm公司系统逐渐变多。不同的系统可能出现相同的功能,想到java有Nexus可以作为私服,npm也一定有类似的东西。所以选择了sinopia安装node下载地址:https://npm.taobao.org/mirror…解压缩、配置profile [root@localhost node]# tar -zxvf node-v10.15.0-linux-x64.tar.gz [root@localhost node]# vim /etc/profile export NODE_HOME=/opt/node/node-v10.15.0-linux-x64 export PATH=$NODE_HOME/bin:$PATH [root@localhost node]# source /etc/profile验证 [root@localhost node]# node -v v10.15.0 [root@localhost node]# npm -v 6.4.1 [root@localhost node]#使用npm 安装 sinopia npm install -g sinopia使用npm 安装 pm2 npm install -g pm2sinopia 的配置文件 # # This is the default config file. It allows all users to do anything, # so don’t use it on production systems. # # Look here for more config file examples: # https://github.com/rlidwka/sinopia/tree/master/conf # # path to a directory with all packages storage: ./storage //npm包存放的路径 auth: htpasswd: file: ./htpasswd //保存用户的账号密码等信息 # Maximum amount of users allowed to register, defaults to “+inf”. # You can set this to -1 to disable registration. max_users: -1 //默认为1000,改为-1,禁止注册 # a list of other known repositories we can talk to uplinks: npmjs: url: http://registry.npm.taobao.org/ //默认为npm的官网,由于国情,修改 url 让sinopia使用 淘宝的npm镜像地址 packages: //配置权限管理 ‘@/’: # scoped packages access: $all publish: $authenticated ‘*’: # allow all users (including non-authenticated users) to read and # publish all packages # # you can specify usernames/groupnames (depending on your auth plugin) # and three keywords: “$all”, “$anonymous”, “$authenticated” access: $all # allow all known users to publish packages # (anyone can register by default, remember?) publish: $authenticated # if package is not available locally, proxy requests to ’npmjs’ registry proxy: npmjs # log settings logs: - {type: stdout, format: pretty, level: http} #- {type: file, path: sinopia.log, level: info} # you can specify listen address (or simply a port) listen: 0.0.0.0:4873 ////默认没有,只能在本机访问,添加后可以通过外网访问。启动 sinopia启动 $ sinopia warn — config file - …..\AppData\Roaming\sinopia\config.yaml warn — http address - http://localhost:4873/如果能正常显示,说明安装成功。使用pm2启动当然,你也可以使用pm2或其他的守护进程进行管理启动: pm2 start which sinopia 或者 pm2 start sinopia客户端使用强烈推荐使用nrm来管理自己的代理。安装nrm全局安装nrm可以快速修改,切换,增加npm镜像地址。 $ npm install -g nrm # 安装nrm $ nrm add XXXXX http://XXXXXX:4873 # 添加本地的npm镜像地址 $ nrm use XXXX # 使用本址的镜像地址nrm的其他命令$ nrm –help # 查看nrm命令帮助$ nrm list # 列出可用的 npm 镜像地址$ nrm use taobao # 使用淘宝npm镜像地址安装包安装完成.之后你通过npm install 安装的包,sinopia都会帮你缓存到本地了.试一下吧。 mkdir test && cd test npm install lodash # sinopia发现本地没有 lodash包,就会从 taobao镜像下载 rm -rf node-modules # 删除目录 npm insatll lodash # 第二次安装就会从缓存下载了,速度很快创建用户与发布包创建新用户确保自己已经切换到配置的代理~ nrm ls npm —- https://registry.npmjs.org/ cnpm — http://r.cnpmjs.org/ taobao - http://registry.npm.taobao.org/ nj —– https://registry.nodejitsu.com/ rednpm - http://registry.mirror.cqupt…. npmMirror https://skimdb.npmjs.com/regi...sinopia http://192.168.50.163:4873/运行npm adduser,填写信息,注册账号。如果已经有账号,直接运行npm login即可。[root@localhost sinopia]# npm adduser –registry http://192.168.50.163:4873 Username: ipnunu Password: Email: (this IS public) ipnunu@qq.com Logged in as ipnunu on http://192.168.50.163:4873/.运行 $ npm publish 发布新包。参考文章sinopia 官网介绍使用Sinopia搭建私有的npm仓库用sinopia搭建npm私服使用sinopia搭建私有npm服务使用sinopia搭建私有npm服务用sinopia搭建内部npm服务简介PM2使用nrm管理npm仓库利用npm安装/删除/发布/更新/撤销发布包 ...

January 22, 2019 · 2 min · jiezi

后端相关技能(五):Node.js

相关文章后端相关技能(一):数据库后端相关技能(二):Vue框架后端相关技能(三):正则表达式后端相关技能(四):计算机网络后端相关技能(五):Node.js

January 22, 2019 · 1 min · jiezi

从0开始使用sinopia搭建私有npm仓库

因为业务安全需要等种种原因,不能够把插件都发布到公共的npm仓库,所以需要搭建自己的私有npm仓库,最近自己搭建了一个简单的npm仓库,踩了些坑,和大家分享一下,希望能够帮到有需要的童鞋本次讲述用sinopia从0开始搭建npm仓库,讲得会比较细,觉得啰嗦的童鞋可以只看自己需要的哈([原文链接[1])服务器端安装nodejs和npm因为我的linux服务器是centos的,所以我这边讲的是一种在centos上安装node 环境1.安装wgetyum install -y wget2.下载最新的node包可以在nodejs的官网找到最新的下载地址(找到需要的,右键复制链接地址就可以拿到最新的地址啦)wget https://nodejs.org/en/download/node-v10.15.0-linux-x64.tar.xz解压安装包xz -d node-v10.15.0-linux-x64.tar.xztar -xf node-v10.15.0-linux-x64.tar.xz部署bin文件重点是要找到你的nodejs的文件路径(你将node文件解压到哪里就是哪里。),找不到node路径的童鞋请执行whereis node然后执行ln -s node路径 /usr/bin/nodeln -s node路径 /usr/bin/npmeg://我的node解压路径为/opt/node-v10.15.0-linux-x64/bin/nodeln -s /opt/node-v10.15.0-linux-x64/bin/node /usr/bin/nodeln -s /opt/node-v10.15.0-linux-x64/bin/node /usr/bin/npm如果出现ln: failed to create symbolic link ‘/usr/bin/node’: File exists执行:rm /usr/bin/node查看是否安装成功node -vnpm -v搭建npm仓库sinopia安装装好node以后,我们就可以在服务器直接安装sinopia了,一行命令全局安装npm install -g sinopia 安装好了可以启动试一下sinopia出现下面的结果说明成功跑起来了,这个时候可以打开http://localhost:4873/访问了Sinopia doesn’t need superuser privileges. Don’t run it under root. warn — config file - /root/.config/sinopia/config.yaml warn — http address - http://localhost:4873/如果报sinopia: command not found,请添加关联,找到你的sinopia路径(这个路径在之前node路径的子目录里面)ln -s /opt/node-v10.15.0-linux-x64/bin/sinopia /usr/binpm2node服务非常脆弱,一般在实际中使用都会配合守护进程。这里我用的是 pm2 做守护进程安装npm install -g pm2pm2 -v如果出现 pm2: command not found,和sinopia的方法一样ln -s /opt/node-v10.15.0-linux-x64/bin/pm2 /usr/bin通过 PM2 启动 sinopia:pm2 start which sinopia结果类似如下:┌─────────┬────┬──────┬────────┬────────┬─────┬────────┬───────────┐│ Name │ id │ mode │ status │ ↺ │ cpu │ memory │├─────────┼────┼──────┼────────┼────────┼─────┼────────┼───────────┤│ sinopia │ 0 │ N/A │ fork │ online │ 0 │ 0% │ 16.7 MB │└─────────┴────┴──────┴────────┴────────┴─────┴────────┴───────────┘sinopia配置修改默认情况下,sinopia 的配置是不适合直接使用的,所以我们需要对它的配置文件按需酌情修改我们找到上面提到的配置文件目录,打开配置文件进行编辑## This is the default config file. It allows all users to do anything,# so don’t use it on production systems.## Look here for more config file examples:# https://github.com/rlidwka/sinopia/tree/master/conf## path to a directory with all packagesstorage: ./storage #npm包存放的路径auth: htpasswd: file: ./htpasswd #保存用户的账号密码等信息 # Maximum amount of users allowed to register, defaults to “+inf”. # You can set this to -1 to disable registration. max_users: -1 #默认为1000,改为-1,禁止注册# a list of other known repositories we can talk touplinks: npmjs: url: http://registry.npm.taobao.org/ #默认为npm的官网,由于国情,修改 url 让sinopia使用 淘宝的npm镜像地址 packages: #配置权限管理 ‘@/’: # scoped packages access: $all #表示哪一类用户可以对匹配的项目进行安装 【$all 表示所有人都可以执行对应的操作,$authenticated 表示只有通过验证的人可以执行对应操作,$anonymous 表示只有匿名者可以进行对应操作(通常无用)】 publish: $authenticated #表示哪一类用户可以对匹配的项目进行发布 ‘*’: # allow all users (including non-authenticated users) to read and # publish all packages # # you can specify usernames/groupnames (depending on your auth plugin) # and three keywords: “$all”, “$anonymous”, “$authenticated” access: $all #表示哪一类用户可以对匹配的项目进行安装 # allow all known users to publish packages # (anyone can register by default, remember?) publish: $authenticated #表示哪一类用户可以对匹配的项目进行发布 # if package is not available locally, proxy requests to ’npmjs’ registry proxy: npmjs #如其名,这里的值是对应于 uplinks# log settingslogs: - {type: stdout, format: pretty, level: http} #- {type: file, path: sinopia.log, level: info}# you can specify listen address (or simply a port) listen: 0.0.0.0:4873 #默认没有,只能在本机访问,添加后可以通过外网访问==listen: 0.0.0.0:4873 这一条一定得加,然后记得把服务器的4873的端口开放==访问在自己的电脑浏览器上输入服务器ip地址+端口号,端口号默认为4873eg:192.186.1.343:4873到这里,我们可以成功的在自己的服务器上搭建我们的私有npm啦~~ ...

January 18, 2019 · 2 min · jiezi

npm cnpm npx nvm 傻傻分不清

用过 npm cnpm吗?知道 npx nvm 吗?唔~npmnpm 的全称是 Node Package Manager 是 JavaScript 世界的包管理工具,并且是 Node.js 平台的默认包管理工具,在安装的 nodejs 的时候,npm 会跟着一起安装。通过 npm 可以安装、共享、分发代码,管理项目依赖关系。常用命令:npm -v 显示版本,检查npm 是否正确安装npm help 可查看某条命令的详细帮助,例如npm help installnpm list -g 查看已经安装的模块npm show express 查看已经安装的模块的详情 npm cache clean –force 清除npm本地缓存npm init 初始化npm install xxx 下载包npm uninstall xxx 卸载包npm update xxx 更新包npm outdated -g –depth=0 要找出需要更新的软件包发布一个 npm 包:注册 && 查询:https://www.npmjs.com登录:npm login查询确认成功:npm whoami上传包:npm publish验证邮箱:npm adduser换源:npm config set registry http://registry.npmjs.org更新:修改 version 后 npm publishcnpmcnpm 淘宝镜像,可以看成是 npm 的国内版本,下载 npm 包的速度更快。安装 npm install cnpm -g –registry=https://registry.npm.taobao.org。npxnpm v5.2.0 引入的一条命令。npx 会帮你执行依赖包里的二进制文件,引入这个命令的目的是为了提升开发者使用包内提供的命令行工具的体验。把原来需要全局安装的包放到项目目录下安装。old:npm install -g create-react-appcreate-react-app my-appnew:npx create-react-app my-app临时安装 create-react-app 包,命令完成后 create-react-app 会删掉,不会出现在 global 中,下次再执行,还是会重新临时安装。nvmnode 管理工具 在开发中,有时候对 node 的版本有要求,有时候需要切换到指定的 node 版本来重现问题等。遇到这种需求的时候,我们需要能够灵活的切换 node 版本,nvm 就是为解决这个问题而产生的,他可以方便的在同一台设备上进行多个 node 版本之间切换。nvm 不支持 Windows,但是有替代品,也就是 nvm-windows。安装 nvm:wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash 安装完成后关闭终端,重新打开终端输入 nvm 验证一下是否安装成功,当出现“Node Version Manager”时,说明已安装成功。如果在新的终端输入 nvm 时提示:command not found: nvm,有可能是以下原因之一:你的系统可能缺少一个 .bash_profile 文件,你可以创建一个此文件(可通过vi或vim命令),打开复制粘贴以下代码(安装nvm成功后终端的最好3行代码)进去,保存,然后再次运行安装命令;export NVM_DIR="$HOME/.nvm"[ -s “$NVM_DIR/nvm.sh” ] && . “$NVM_DIR/nvm.sh” # This loads nvm[ -s “$NVM_DIR/bash_completion” ] && . “$NVM_DIR/bash_completion” # This loads nvm bash_completion注意:如果你安装了 oh my zsh ,需要在 .zshrc 文件去添加以上配置信息,(一般安装成功都会自动写入这个文件最底部)。如果上面没有解决问题,打开你的 .bash_profile 文件,并添加以下代码:source ~/.bashrc,更改完记得保存更改。常用命令:nvm ls-remote 列出全部可以安装的版本号nvm install stable 安装当前最新的稳定版nvm install v10.14.0 安装指定版本nvm ls 显示所有安装的版本nvm current 查看当前版本nvm use v10.14.0 切换node版本nvm alias default v10.14.0 设置默认版本 ...

January 16, 2019 · 1 min · jiezi

可实现B站 蒙版弹幕 效果的前端组件 —— barrage-ui

barrage-uiBest and lightest barrage component for web ui.适用于 web 端用户界面和播放器的轻量级弹幕组件用途为你的 视频播放器、图片浏览器 等元素挂载弹幕动画用于实现 B 站(bilibili.com) 风格的 蒙版弹幕 效果安装yarn add barrage-ui或npm install –save barrage-ui快速开始import Barrage from ‘barrage-ui’;import example from ‘barrage-ui/example.json’; // 组件提供的示例数据// 加载弹幕const barrage = new Barrage({ container: ‘barrage’, // 父级容器或ID data: example, // 弹幕数据 config: { // 全局配置项 duration: 20000, // 弹幕循环周期(单位:毫秒) fontFamily: ‘Microsoft Yahei’, // 弹幕默认字体 defaultColor: ‘#fff’, // 弹幕默认颜色 },});// 新增一条弹幕barrage.add({ time: 1000, // 弹幕出现的时间(单位:毫秒) text: ‘这是新增的一条弹幕’, // 弹幕文本内容 fontSize: 24, // 该条弹幕的字号大小(单位:像素),会覆盖全局设置 color: ‘#0ff’, // 该条弹幕的颜色,会覆盖全局设置});// 播放弹幕barrage.play();初始化参数创建弹幕实例时,需要传入的初始化参数如下:参数数据类型默认值说明containerstring/element必传,无默认值弹幕的挂载点dataarray[]弹幕数据configobject详见全局配置项详见全局配置项maskstring/ImageDatastring/ImageData蒙版图像,用于实现蒙版弹幕效果,详见蒙版弹幕beforeRenderfunctionctx => {}帧渲染前的回调,函数参数为 canvas 画布的上下文afterRenderfunctionctx => {}帧渲染后的回调,函数参数为 canvas 画布的上下文overlapOptimizedbooleanfalse弹幕装填时是否启用布局优化,以尽可能避免使相邻时间的弹幕重叠其中,container 参数在初始化实例时必传,其他参数为可选,数据类型及默认值如上表所示。全局配置项配置项及默认值弹幕的所有全局配置项及默认值如下:{ duration: -1, // -1 表示不循环播放 speed: 100, // 弹幕的运动速度 fontSize: 20, // 文字大小,单位:像素 fontFamily: ‘serif’, // 字体 textShadowBlur: 0.1, // 字体阴影大小,有效值 0-1 opacity: 1.0, // 透明度,有效值 0-1 defaultColor: ‘#fff’, // 默认颜色,与 CSS 颜色属性一致}更新配置项如果你的弹幕实例已创建或者正在播放,可以通过 .setConfig() 方法进行实时更新:// 更新全局透明度barrage.setConfig({ opacity: 0.5 });弹幕数据结构与内容弹幕数据为一个对象数组。每个数组元素对应一条弹幕记录,其结构如下:{ createdAt: ‘2019-01-13T13:34:47.126Z’, time: 1200, text: ‘我膨胀了’, fontFamily: ‘SimSun’, fontSize: 32, color: ‘yellow’,}数据字段createdAt - 弹幕的创建时间 (必须)time - 弹幕的动画时间 (必须)text - 弹幕文本内容 (必须)fontFamily - 弹幕文本的字体 (可选)fontSize - 弹幕文本字号大小,单位:像素 (可选)color - 弹幕文本的颜色 (可选)装填弹幕装填弹幕有两种方式:方式一:初始化时传入数据const barrage = new Barrage({ container: ‘barrage’, data: JSON_DATA, // JSON_DATA -> 你的弹幕数据});方式二:初始化后更新数据const barrage = new Barrage({ container: ‘barrage’,});barrage.setData(JSON_DATA); // JSON_DATA -> 你的弹幕数据新增弹幕如果你的弹幕实例已创建或者正在播放,可以通过 .add() 方法新增一条记录:barrage.add({ time: 1000, text: ‘这是新增的一条弹幕’, fontSize: 24, color: ‘#0ff’,});.add() 方法一般搭配 数据提交/请求 操作进行使用,以实现真实的线上应用。场景举例某用户在客户端提交了一条弹幕到服务端,服务端将数据存储并分发给正在进行会话的客户端,最后客户端通过 .add() 方法进行数据更新。动画控制接口.play()描述用于播放动画。若当前为暂停状态,则从当前进度继续播放用例barrage.play();.pause()描述用于暂停动画用例barrage.pause();.replay()描述用于重新开始播放动画用例barrage.replay();.goto(progress)描述用于跳转播放进度。此方法在动画播放和暂停的状态下均有效参数progress - 待跳转的进度。值为一个毫秒数,表示跳转到动画的第几毫秒用例barrage.goto(15000); // 跳转到第 15 秒蒙版弹幕Barrage 组件提供了实现 蒙版弹幕 效果的可能。基于本组件实现的 demo 效果如下:什么是“蒙版弹幕”蒙版弹幕 是由知名弹幕视频网站 bilibili 于 2018 年中推出的一种弹幕渲染效果,可以有效减少弹幕文字对视频主体信息的干扰。详细资料可参考 bilibili 的相关文章:弹幕阳光计划第十弹 蒙版听说过吗,弹幕黑科技了解一下?不挡脸,放肆看!B 站黑科技蒙版弹幕揭秘实现原理如果你熟悉最著名的图像处理软件——Adobe Photoshop,那么你应该对 “蒙版” 的概念不陌生,“蒙版弹幕” 的实现原理与此类似,即:将图像的一部分 “隐藏”。Barrage 组件的初始化参数中的 mask 一项即用于处理蒙版效果。对于上文截图中的效果,其使用的蒙版图像效果如下:弹幕渲染时,会将蒙版图像中 “镂空” 的部分(图像 RGBA 通道中 Alpha 通道为 0 的像素)去除,从而达到 “蒙版弹幕” 的效果。简单蒙版弹幕的实现为 barrage 实例设置蒙版图像(mask)即可实现蒙版弹幕效果。可通过初始化参数 mask 传入蒙版图像:import Barrage from ‘barrage-ui’;import example from ‘barrage-ui/example.json’;const barrage = new Barrage({ container: ‘barrage’, data: example, mask: ‘mask.png’, // 传入蒙版图像的 url});也可以在弹幕初始化后,通过 .setMask() 方法进行实时更新:import Barrage from ‘barrage-ui’;import example from ‘barrage-ui/example.json’;const barrage = new Barrage({ container: ‘barrage’, data: example,});// 设置蒙版图像barrage.setMask(‘mask.png’); // 传入蒙版图像的 url注意事项mask 参数和 .setMask() 方法的参数类型一致,可接收图像的 url 或 ImageData实时渲染上文的示例仅能够实现一帧图像的渲染(只设置了一次 mask),要实现实时的蒙版效果,需要对弹幕动画的每一帧进行处理。使用组件提供的 beforeRender 钩子函数,可以轻易的实现:import Barrage from ‘barrage-ui’;import example from ‘barrage-ui/example.json’;const barrage = new Barrage({ container: ‘barrage’, data: example, beforeRender: () => { const newImage = getMask(); // 用于获取当前帧对应蒙版的方法 barrage.setMask(newImage); },});当然,beforeRender 钩子也可以在弹幕初始化之后挂载:import Barrage from ‘barrage-ui’;import example from ‘barrage-ui/example.json’;const barrage = new Barrage({ container: ‘barrage’, data: example,});barrage.beforeRender = () => { const newImage = getMask(); // 用于获取当前帧对应蒙版的方法 barrage.setMask(newImage);}; ...

January 15, 2019 · 2 min · jiezi

npm(你怕吗) 全局安装与本地安装、开发依赖和生产依赖

npm(你怕吗)npm(Node Package Manager)是 JavaScript 世界的包管理工具,并且是 Node.js 平台的默认包管理工具。通过 npm 可以安装、共享、分发代码,管理项目依赖关系。(与其有同样功能的另一个包管理工具yarn,速度用体验比旧版本的npm好,但npm最近的新版本也做出很大努力,与yarn速度和体验差距越来越小,有兴趣可以了解下)。npm这几年可以说是前端猿日常离不开的工具了,刚开始学习使用node、npm来进行前端项目构建的时候,有很多概念需要理解,各种命令行、各种新js语法、各种环境依赖…就问你怕吗?:讲真,刚开学时我怕。废话不多扯,现在我们进入主题,聊聊npm 全局安装与本地安装、开发依赖和生产依赖,先抛两常见疑惑:什么是全局安装、什么是本地安装(或叫局部安装,下文统一叫本地安装) ?为什么要全局安装?为什么又要本地安装?全局安装和本地安装有什么区别?什么叫开发依赖、生产依赖?什么又是开发环境、生产环境?全局安装与本地安装一、全局安装:npm install <pageName> -g//(这里-g是-global的简写)通过上面的命令行(带-g修饰符)安装某个包,就叫全局安装。通常全局包安装在node目录下的node_modules文件夹。可以通过执行下面几条命令查看node、npm的安装目录和全局包的安装目录。which node // 查看node的安装目录which npm // 查看npm的安装目录npm root -g // 查看全局包的安装目录npm list -g –depth 0 //查看全局安装过的包二、本地安装:npm install <pageName> (后面可以加几种修饰符,主要有两种–save-dev和–save)通过上面的命令行安装某个包,就叫本地安装。包安装在你当前项目文件夹下的node_modules文件夹中。三、全局安装的作用:全局安装的包可提供直接执行的命令(例:gulp -h可以查看gulp定义了什么命令)。 比如gulp全局安装后,可以在命令行上直接执行gulp -v、gulp -h等(原理:全局安装的gulp会将其package.json中的bin命令注入到了全局环境,使得你可以全局执行:gulp xxx命令,这另一个话题了,不深入)。倘若只在本地安装了gulp,未在全局安装gulp,直接执行这些命令会报错。你想要执行相应的命令则可能需要例如:node ./node_modules/gulp/bin/gulp.js -v(查看版本) 这样用一大串命令来执行。因此全局安装就发挥到他的好处了,一个gulp -v就搞定 当然,不是每个包都必须要全局安装的,一般在项目中需要用到该包定义的命令才需要全局安装。比如gulp <taskName>执行gulp任务…等,所以是否需要全局安装取决于我们如何使用这个包。全局安装的就像全局变量有点粗糙,但在某些情况下也是必要的,全局包很重要,但如果不需要,最好避免使用。四、可以全局安装,那么直接全局安装到处使用就行了,干嘛还需要本地安装?如果只是全局安装了而没本地安装,就得require(’<pagePath>’) 例:引入一个全局的包可能就是requirt(’/usr/local/….’)通过全局包的路径引入,这样显然十分的不灵活。如果安装了本地包,那么就可以直接require(’<pageName>’)引入使用。一个包通常会在不同的项目上会重复用到,如果只全局安装,那么当某个项目需要该包更新版本时,更新后可能就会影响到其他同样引用该包的项目,因此本地安装可以更灵活地在不同的项目使用不同版本的包,并避免全局包污染的问题,一个经验法则:要用到该包的命令执行任务的就需要全局安装,要通过require引入使用的就需要本地安装( 但实际开发过程中,我们也不怎么需要考虑某个包是全局安装还是本地安装,因为这一点在该包的官网上一般会明确指出,以上是为了理解全局安装和本地安装)开发依赖和生产依赖顺着上面讲到的本地安装,本地安装有两种主要的安装方式:保存到开发依赖(devDependencies): npm install <pageName> –save-dev(等同 npm install <pageName>)保存到生产依赖(dependencies): npm install <pageName> –save"开发依赖"顾名思义在开发环境中用到的依赖,“生产依赖"在生产环境中用到的依赖。那么这里又延伸出个问题什么是开发环境、什么是生产环境?一、开发环境和生产环境【开发环境】:指的是你的项目尚且在编码阶段时的环境。你在代码可能还有各种console.log()、注释、格式化等。【生产环境】:指的是你的项目已经完成编码,并发布上线可供用户浏览的阶段时的环境。代码可能经过了压缩、优化等处理。这些概念其实并没有一个很明确的定义,接下来我们举例个场景,将"开发环境”、“生产环境"和上面的"开发依赖”、“生产依赖"联系起来就会比较容易理解的了。假如我们在开发过程中使用jQuery。在以往,可能就是把jQuery这个插件下载的本地,再通<script>引入html中。但这有个不方便的地方,我们每次进行一个项目的时候就得手动复制这个jQuery文件到我们的项目中,如果想要换个版本又得官网上下载、随着项目越来越多。用到的插件、库也随之越繁杂…这样会造成自家用的插件管理繁琐的问题。因此就出现了npm(包管理工具)你需要用到什么,直接通过一条命令行就可以将想要的插件下载下来,并直接引入到项目中,目前几乎所有的js插件都能在npm上直接下载。二、生产依赖回到环境和依赖话题,我们下载的jQuery,在开发时参与源码编写,在发布上线的生产环境中也是需要它的。不仅在开发环境编写代码时要依赖它、线上环境也要依赖它,因此将它归类为"生产依赖”,安装时执行npm install jquery –save,它就会被记录在package.json的dependencies。当进行代码打包时,会将这里的jQuery打包入我们的项目代码中。三、开发依赖接着,假如我们用gulp对html进行压缩,我们通常会用到一个插件gulp-htmlmin。我们只希望它把html压缩完就ok了,并不希望它融入我们的项目代码中,即只存在于开发环境,因此把他归类为"开发依赖"。安装时执行npm install gulp-htmlmin –save-dev它就会被记录在package.json的devependencies下,当进行代码打包时,不会将这里的gulp-htmlmin插件源码打包入我们的项目代码中devDependencies只会在开发环境下使用,生产环境不会被打入包内;而dependencies不仅在开发环境中要使用,生产环境也需要使用到。根据以上规则,我们就很容易区分哪些插件是用–save-dev模式安装,哪些用–save模式安装。最后文章开头的问题以上已经一一回答了。刚开始时自己也会有很多类似这样的疑问,哪怕已经使用npm的各种包构建过不少项目,对这些概念还是会有点模糊,使用当然是会使用的,但没有一个比较清晰的总结。所有花了点时间,到各社区、node、npm官网搜集关于以上问题的一些答案、简单做个笔记和总结。 毕竟总结并记录下来的东西才是自己的。也希望可以帮助有同样疑问的朋友,如发现文章有不对的地方望指出,谢谢

January 7, 2019 · 1 min · jiezi

基于verdaccio的npm私有仓库搭建和使用总结

资源官方:https://github.com/verdaccio/…参考:https://blog.csdn.net/yyzzhc9…参考:https://www.jianshu.com/p/16b…参考:https://www.jianshu.com/p/1d0…原来用sinopia搭建的私服,但sinopia两年年停止维护了,现在改为verdaccio。使用verdaccio也超级简单,只需几分钟就可以搭建一个私服,适合公司内部不对外的包的安装一:安装和配置A.安装$ yarn global add verdaccioyarn global v1.12.3[1/4] Resolving packages…[2/4] Fetching packages…[3/4] Linking dependencies…[4/4] Building fresh packages…success Installed “verdaccio@3.10.1” with binaries: - verdaccioDone in 52.10s.安装到了 C:\Users\Administrator\AppData\Local\Yarn\bin[我的是win10]B.测试随便建一个空目录测试,我当前工作目录是F:\youshengyouse\del3$ verdaccio warn — config file - C:\Users\Administrator.config\verdaccio\config.yaml warn — Plugin successfully loaded: htpasswd warn — Plugin successfully loaded: audit warn — http address - http://localhost:4873/ - verdaccio/3.10.1在浏览器输入http://localhost:4873/,结果如下二:添加用户另开一个命令行窗口,原来的verdaccio仍在运行$ npm adduser –registry http://localhost:4873Username: abcdPassword: qwerEmail: (this IS public) a@163.comLogged in as abcd on http://localhost:4873/.三:发布私包当前工作目录:要发布哪个包,当前目录切换到包根目录下$ npm publish –registry http://localhost:4873> gatsby2-cli@1.0.0 prepare .> cross-env NODE_ENV=production npm run build> gatsby2-cli@1.0.0 build F:\youshengyouse\frontend\packages\gatsby\packages\gatsby2-cli> babel src –out-dir lib –ignore **/__tests__Successfully compiled 6 files with Babel.npm noticenpm notice package: gatsby2-cli@1.0.0npm notice === Tarball Contents ===npm notice 1.8kB package.jsonnpm notice 7.4kB CHANGELOG.mdnpm notice 3.3kB README.mdnpm notice 9.4kB lib/create-cli.jsnpm notice 2.7kB lib/index.jsnpm notice 4.7kB lib/init-starter.jsnpm notice 2.0kB lib/reporter/errors.jsnpm notice 3.1kB lib/reporter/index.jsnpm notice 4.0kB lib/reporter/prepare-stack-trace.jsnpm notice === Tarball Details ===npm notice name: gatsby2-clinpm notice version: 1.0.0npm notice package size: 11.0 kBnpm notice unpacked size: 38.3 kBnpm notice shasum: 64c9c47b81610731e559bc33f86aa02f87155656npm notice integrity: sha512-vhroNpnWCwivE[…]8hAg+z6SPOeyw==npm notice total files: 9npm notice+ gatsby2-cli@1.0.0现在刷新http://localhost:4873/#/就可以看到刚才发布的包问题:本地安装的包放在哪里了呢?四:安装包npm set registry http://localhost:4873/$ npm init # 如果没有package.json文件得先建$ yarn add gatsby2-cli发现gatsby2-cli是在本地的,其余的依赖还是从npmjs.com仓库中安装的 ...

January 7, 2019 · 1 min · jiezi

Windows 下的 electron 开发笔记一

前言根据公司业务需求,使用 electron 开发桌面 BrowserWindow 应用。参考 API:Electron 文档安装与配置安装工具node(LTS版)git 命令行工具搭建项目初始化:$ npm init安装 electron:$ npm install electron –save-dev软件打包安装打包工具:$ npm install –save-dev electron-packager打包基本命令:electron-packager {location} {name} {platform} {architecture} {version} {options}location:项目所在路径name of project:打包的项目名字platform:确定了你要构建哪个平台的应用(Windows、Mac 还是 Linux)architecture:决定了使用 x86 还是 x64 还是两个架构都用version:electron 的版本options:可选选项在 package.json 中添加配置项:“packager”: “electron-packager ./ writ win x86 –app–version=2.0.6 –overwrite –icon=./favicon.ico"执行:$ npm run-script packager环境依赖.netframework 4.5.1python2.7Visual C++ Build Tools一键安装:$ npm install –global –production windows-build-tools环境设置:$ npm config set msvs_version 2015若出现 vc2015 安装失败情况,请自行安装 SP1windows6.1-KB976932 补丁插件依赖node-gyp Node 编写的跨平台命令行工具,用于编译 Node.js 的原生插件模块$ npm install -g node-gypffi 用以调用动态库的 Node.js 插件$ npm install ffi –savebuffer 提供与 Node.js 的 Buffer 完全相同的缓冲区插件$ npm install buffer –saveiconv-lite 用于在 Node.js 当中处理在各种操作系统出现的各种奇特编码,该模块不提供读写文件的操作,只提供文件编码转换的功能$ npm install iconv-lite –saveelectron-rebuild 用以重编译适合 electron 的模块$ npm install electron-rebuild –save-dev$ ./node_modules/.bin/electron-rebuild ./node_modules/ffi 占坑 ...

December 26, 2018 · 1 min · jiezi

package.json的所有配置项及其用法,你都熟悉么

写在前面在前端开发中,npm已经是必不可少的工具了。使用npm,不可避免的就要和package.json打交道。平时package.json用得挺多,但是没有认真看过官方文档。本文结合npm官方文档以及自己平时使用过程中的感悟,谈一谈package.json。官方文档在这里。初始化使用npm init命令就可以初始化一个package.json文件。在初始化的过程中,会叫用户输入name, version等等信息,当然,你都可以忽略。一路点回车,就生成了下面这样一个初始化的package.json。{ “name”: “test”, // 假如项目叫做test “version”: “1.0.0”, “description”: “”, “main”: “index.js”, “scripts”: { “test”: “echo "Error: no test specified" && exit 1” }, “author”: “”, “license”: “ISC”}上面的package.json包含了项目的名称,版本号,描述,入口文件,执行脚本,作者,开源协议等。package.json的内容远不止这些,下面将一一进行介绍。在开发业务项目和开发组件库的时候,package.json是略有不一样的。我会把我认为重要的配置项加黑标识出来。详细介绍name: 这个很好理解,就是package的名称。不过需要注意的是,name有长度限制(虽然一般都不会超),而且name不能以 【点】 或者 【下划线】开头,name中不能有大写字母。这个是每一个package必须的。在业务代码中,通过require(${name})就可以引入对应的程序包了。version: package的版本。对于业务项目来说,这个往往不太重要,但是如果你要发布自己的项目,这个就显得十分重要了。name和version共同决定了唯一一份代码。npm是用[npm-semver来解析版本号的。我们一般见到的都是大版本.次要版本.小版本这种版本号,比如16.1.0。版本号的规则、含义其实蛮多的,可以参考这篇文章。desription:包的描述。开发组件库时必需,简明的向库的使用者介绍这个库是干嘛的。对于公司的业务项目,这个配置项一般无所谓。keywords:关键词。一个字符串数组,对这个npm包的介绍。组件库必需,便于使用者在npm中搜索。对于公司业务项目,这个配置一般无所谓。homepage: 项目主页。对于开发组件库来说挺有用的。bugs:开发者的联系方式,代码库的issues地址等。如果代码使用者发现了bug,可以通过这个配置项找到提bug的地方。license:开源协议。对于开源组件库,这个十分重要。之前react还因为这事儿没少被社区嫌弃。开源协议略微复杂,用阮一峰前辈的一张图来说明一下吧。注:图里少了ISC, ISC和BSD差不多author:项目的作者。可以为字符串,对象。contributors:项目的贡献者。author的数组。main:代码入口。这个十分重要,特别是对于组件库。当你想在node_modules中修改你使用的某个组件库的代码时,首先在node_modules中找到这个组件库,第一眼就是要看这个main,找到组件库的入口文件。在这个入口文件中再去修改代码吧。scripts:指定了运行脚本命令的npm命令行缩写。十分重要。来看一个例子:“scripts”: { “dev”: “NODE_ENV=dev webpack-dev-server –progress –hot –host 0.0.0.0 –port 8089”, “test”: “NODE_ENV=test webpack –config webpack.test.config.js –progress”, “online”: “NODE_ENV=production webpack –config webpack.online.config.js –progress”, “build”: “webpack”, “node”: “node server.js” },在命令行输入:npm run dev , 对应的命令就会被执行。这里有一个地方需要注意,当执行npm run xxx 的时候,node_modules/.bin/目录会在运行时被加入系统的PATH变量。上面的例子,当我们在命令行输入:npm run build时,其实真正执行的命令是node_modules/.bin/webpack而不是webpack。所以,当你的webpack并未全局安装时,直接在命令行输入:webpack是会报错的。因为你的webapck是安装在node_modules/.bin/下面的。directories:对整个代码结构的描述。告诉代码包使用者可以在哪里找到对应的文件。files:数组。表示代码包下载安装完成时包括的所有文件。repository:对于组件库很有用。让组件库使用者找到你的代码库地址。这个配置项会直接在组件库的npm首页生效。例子:“repository”: { “type”: “git”, “url”: “git+https://github.com/CoyPan/react-scroll-to-show-cb.git”},config:用于添加命令行的环境变量。具体用法见这里。dependencies:项目的依赖。注意,不要把测试工具、代码转换器或者打包工具等放在这里。例如:当你在命令行里面使用npm install react –save时,react就会出现在dependencies。默认是安装最新的版本。如果想安装某个特定的版本,可以npm install react@15.6.2。以下的dependencies,格式都是合法的,“dependencies” : { “foo” : “1.0.0 - 2.9999.9999”, “bar” : “>=1.0.2 <2.1.2”, “baz” : “>1.0.2 <=2.3.4”, “boo” : “2.0.1”, “qux” : “<1.0.0 || >=2.3.1 <2.4.5 || >=2.5.2 <3.0.0”, “asd” : “http://asdf.com/asdf.tar.gz”, “til” : “~1.2”, “elf” : “~1.2.3”, “two” : “2.x”, “thr” : “3.3.x”, “lat” : “latest”, “dyl” : “file:../dyl” }我们常见的是下面这些:“dependencies”: { “foo”: “1.0.0”, // 指定了就是1.0.0版本 “bar”: “1.2.2”, // 安装版本号不低于1.2.2的1.2.x的最新版本,例如:1.2.3, 1.2.4等等。1.2.1 ,1.3.x 等就不行了 “baz”: “1.2.2”, // 安装版本号不低于1.2.2的1.x.x的最新版本,例如: 1.2.7,1.3.1,1.7.8等。1.2.1 ,2.0.0 等就不行了。注意,如果配置是^0.x.x,则和0.x.x的效果一样。 “lat”: “latest” // 安装最新版本}dependencies 还可以像下面这样配置:“dependencies”: { “foo”: “git+ssh://git@github.com:foo/foo.git#v1.0.1”,}foo组件的地址为git+ssh://{foo代码库的ssh地址}#v{foo的版本号}这样的配置在下面这种场景十分有用:组内的许多项目都有同一个功能,把这个功能抽出来做成组件是很自然的想法。但是每个项目都有自己的代码库,公司也没有内部的npm库,组件应该放在哪里呢?可以专门为组件新建一个代码仓库,将组件放在这里开发、迭代。这样,各个项目都可以引用该组件:只需要在dependencies中将组件配置成上述的形式。至于组件的版本,可以通过git tag来控制。dependencies还有其他的配置方式,具体在这里查看。devDependencies:项目的依赖。主要是在开发过程中依赖的一些工具。用法与dependencies相似。bundledDependencies:数组,打包时的依赖。如果配置了bundledDependencies,在项目中执行 npm pack将项目打包时,最后生成的.tgz包中,会包含bundledDependencies中配置的依赖。bundledDependencies中的依赖必须在devDependencies或者dependencies中声明过。peerDependencies: 指定当前组件的依赖以其版本。如果组件使用者在项目中安装了其他版本的同一依赖,会提示报错。engines:指定项目所依赖的node环境、npm版本等。private:如果设为true,无法通过npm publish发布代码。bin:用来指定各个内部命令对应的可执行文件的路径。具体用法这里不多讲了。详情可以点击这里。总结本文涵盖了package.json绝大部分的配置项。我的观点是:如果是公司的业务项目,对于package.json,一般情况下,我觉得只需要关注好scripts,dependencies,devDependencies这三个地方就够了。而对于开源的组件库,则至少需要关注好上面标黑的几个点。理解好重要配置的含义,提升开发效率,减少踩坑的概率。写在后面本文结合官方文档以及自己平时工作中的体会,阐述了package.json这个配置文件中各项的含义以及用法。符合预期。欢迎关注我的公众号,这里只有干货,符合你的预期。 ...

December 25, 2018 · 1 min · jiezi

npm发布包教程(五):废弃/删除

npm包发布后可以对包进行废弃或删除操作,废弃和删除的区别在于:废弃不会将包或版本从npm仓库删除,仍然可以继续下载安装,并在安装的时候会有警示删除会将包从npm彻底删除,无法被下载安装无论是废弃还是删除,都包含两个层面:版本的废弃/删除包的废弃/删除一、废弃废弃原因:版本:鼓励用户更新最新版本包:此包不再有维护的价值第一步:废弃指定版本语法:npm deprecate <pkg>[@<version>] <message>我们以yuyy-test-pkg为例:npm deprecated yuyy-test-pkg@1.1.0 ’test deprecate’执行后我们用npm view yuyy-test-pkg versions查看版本:记录的版本号并无变化。第二步:安装废弃版本切换到test-my-pkg目录下,执行:npm i yuyy-test-pkg@1.1.0运行结果: 第三步:运行index.js在test-my-pkg目录下node index.js结果:废弃的包除了安装时会有警示,并不影响使用。二、删除npm不鼓励任何形式的删除,主要因为我们发布的包可能已经被其他人引用,如果我们删除了此包,其他人在重新安装含有我们包的依赖的工程时,出现找不到包问题。基于此,npm做了相关的删除限制:删除的版本24小时后方可重发!包发布72小时之内才可删除!第一步:删除发布的包我们之前在《npm发布包教程(二):发布包》发布的包仅为演示所用,为保持npm仓库的纯净,我们都删除掉:npm unpublish yuyy-test-pkg –forcenpm unpublish @yuyy/babel –force第二步:去官网查找第二步切换到test-my-pkg目录下,先将两个包卸载:npm rm yuyy-test-pkg @yuyy/babel结果:然后再重新安装:npm i yuyy-test-pkg @yuyy/babel结果:已经删除的包无法再安装。至此,我们完成npm包的整个生命周期的演示过程,大家可以开源的道路上又多了一条很重要的道路。不过,我觉得作为一个开发者,我们有责任和义务维护每一个社区的纯净,所以在发布npm包的时候应该尽量精益求精。相关文章:1.《npm发布包教程(一):从npm说起》2.《npm发布包教程(二):发布包》3.《npm发布包教程(三):安装发布包》4.《npm发布包教程(四):迭代》5.《npm发布包教程(五):废弃/删除》

December 22, 2018 · 1 min · jiezi

npm发布包教程(四):迭代

一个npm包发布之后,我们难免会修改一些bug,或者增改一些功能,这就涉及到对npm包的迭代。本篇文章就npm迭代涉及到一些知识点进行介绍。本次演示以《npm发布包教程(二):发布包》中发布的包为基础。npm包的每次迭代都要涉及到两个方面:内容的变更版本的变更我们首先来演示内容的变更,以yuyy-test-pkg为例一、更新内容index.js变更为:module.exports = { printMsg: function () { console.log(’this message is from yuyy-test-pkg!’); console.log(’the version of this package has updated!’); }}二、更新版本在演示版本变更前,我们先来了解一下npm版本相关的知识。npm采用语义化版本,共三位,以’.’隔开,从左至右依次代表:主版本(major)、次要版本(minor)、补丁版本(patch)。例如:1.0.0major.minor.patch关于版本变更规范:变更版本号的命令:npm version <major | minor | patch>假如我们本次是次要发布,我们执行命令:npm version minor执行结果:package.json中的version也已变为1.1.0:{ “name”: “yuyy-test-pkg”, “version”: “1.1.0”, “description”: “my first npm package”, “main”: “index.js”, “scripts”: { “test”: “echo "Error: no test specified" && exit 1” }, “keywords”: [ “npm”, “packge” ], “author”: “yuyy”, “license”: “ISC”}三、发布npm publish结果:我们可以通过命令查看我们包的所有版本:npm view yuyy-test-pkg versions结果:四、安装更新(1)切换到test-my-pkg目录下npm up yuyy-pkg结果:(2)执行index.js node index.js输出结果:以上就是npm包迭代的过程,我们本次已unscoped包yuyy-test-pkg为例,对于scoped包的迭代过程没有差异。在下一篇文章《npm发布包教程(四):删除/废弃》中我们将演示npm的废弃和删除。

December 21, 2018 · 1 min · jiezi

npm发布包教程(三):安装发布包

我们在上一篇《npm发布包教程(二):发布包》中演示了如何发布npm包,npm仓库有了我们自己的包,接下来就进入到安装并使用我们自己的包的环节。第一步:初始化测试工程mkdir test-my-pkg && cd test-my-pkg npm init -y第二步:npm官网找包官网输入我们已经发布的包yuyy-test-pkg@yuyy/babel页面会有安装命令,如下图所示:第三步:安装依次执行下面的命令 npm i yuyy-test-pkg npm i @yuyy/babel此时的目录结构:test-my-pkg├── node_modules│ ├── @yuyy│ │ └── babel│ │ ├── README.md│ │ ├── index.js│ │ └── package.json│ └── yuyy-test-pkg│ ├── README.md│ ├── index.js│ └── package.json├── package-lock.json└── package.json第四步:使用(1) 建index.js index.js:let printer = require(‘yuyy-test-pkg’);let otherPrinter = require(’@yuyy/babel’);printer.printMsg();otherPrinter.printMsg();(2) 运行index.jsnode index.js执行结果:this message is from yuyy-test-pkg!this message is from @yuyy/babel!以上即为对我们自己的包引用的整个过程,值得注意的是:我们知道在Node环境中是通过CommonJS的风格管理模块的,所以在第四步引用模块的时候使用的是require()。关于require()的原理,阮一峰老师的《require()源码解读》中有详细介绍,不再赘述,仅将require()的内部原理摘抄整理如下,以伪代码的形式呈现:Node中执行:require(X)解析过程:if(X 是Node内部模块){ return X}else if(X 带路径,以 ‘/‘、‘./‘、’../‘开头){ resolveModule(X)}else if(X 不带路径){ /当前工程/node_modules 执行 resolveModule(X) ./当前工程 node_modules 执行 resolveModule(X) ../当前工程 node_modules 执行 resolveModule(X) . . .}else { return ’not found’}function resolveModule(X){ absolutePath = X的绝对路径(根据X所在的父模块可知) if(X 是文件){ return absolutePath/X || absolutePath/X.js || absolutePath/X.json || absolutePath/X.node; }else if(X 是目录){ return absolutePath/X/package.json(main字段) || absolutePath/X/index.js || absolutePath/X/index.json || absolutePath/X/index.node }}我们将在下一篇文章《npm发布包教程(三):npm包迭代》中演示对已经发布过的包如何进行迭代,包括内容的迭代和版本的迭代。 ...

December 21, 2018 · 1 min · jiezi

解决npm安装错误:No matching version found for event-stream@3.3.6

问题描述当刚clone一个项目,使用npm install安装项目依赖包的时候,报错:npm ERR! code ETARGETnpm ERR! notarget No matching version found for event-stream@3.3.6npm ERR! notarget In most cases you or one of your dependencies are requestingnpm ERR! notarget a package version that doesn’t exist.npm ERR! notarget npm ERR! notarget It was specified as a dependency of ‘project-name’npm ERR! notarget

December 21, 2018 · 1 min · jiezi

deepin安装nodejs环境和npm环境

deepin可以直接通过sudo apt install nodejs来安装nodejs,但是仓库里面的版本有点落后, 目前(2018年12月20日)仓库版本为v8.11.2,而官方的LTS版本为10.14.2,非LTS版已经来到了11.5.0所以决定自己安装.1. 下载安装包https://nodejs.org/en/download/2. 解压到你想安装的目录3. 添加环境变量修改 ~/.profile 添加:# Nodejsexport NODEJS_HOME="/home/li/node-v10.14.2-linux-x64/bin"export PATH=$NODEJS_HOME:$PATH *NODEJS_HOME为你nodejs的安装目录4. 刷新环境变量使刚才添加的生效source ~/.profile验证一下:到这里就安装成功了 ^_^

December 20, 2018 · 1 min · jiezi

npm发布包教程(二):发布包

上一篇文章《npm发布包教程(一):从npm说起》中我们介绍了npm相关的一些知识,旨在让大家对npm有一些深入的理解,这一篇我们正式开始演示发布过程。一、准备工作在正式开始演示前,我们还需要做两项准备工作:1.注册npm账户注册地址全名:邮箱:用户名:重要!发布scoped包时会用到密码:2.全局安装nrmnpm i nrm -gnrm是npm仓库管理的软件,可用于npm仓库的快速切换nrm 常用命令: nrm //展示nrm可用命令 nrm ls //列出已经配置的所有仓库 nrm test //测试所有仓库的响应时间 nrm add <registry> <url> //新增仓库 nrm use <registry> //切换仓库二、发布包开始演示前做两个简短说明:(1)npm官方建议规范的包至少包含:package.json(包的基本信息)README.md(文档)index.js (入口文件)后续的演示都遵循此规范。(2)本次仅演示个人账户的包发布,包括一个unscoped包和一个scoped的包。团体账户下的包发布流程和个人账户差别不大,在此不做展开。1.发布unscoped包yuyy-test-pkg第一步:创建项目(1)创建工程文件夹mkdir yuyy-test-pkg && cd yuyy-test-pkg(2)创建package.json npm init按照提示一步步完善即可,注意:本次演示的包的入口文件是index.js,请务必确保package.json中字段main对应的值是“index.js”。最终结果:{ “name”: “yuyy-test-pkg”, “version”: “1.0.0”, “description”: “my first npm package”, “main”: “index.js”, “scripts”: { “test”: “echo "Error: no test specified" && exit 1” }, “keywords”: [ “npm”, “packge” ], “author”: “yuyy”, “license”: “ISC”}(3)创建README.md内容:### yuyy-test-pkgThis is my first npm package!It is just for learning.(4)创建index.js内容:module.exports = { printMsg: function () { console.log(’this message is from yuyy-test-pkg!’); }}最终的目录结构:└── yuyy-test-pkg ├── README.md ├── index.js └── package.json第二步:发布npm publish可能报的错:(1)未登录npm ERR! code ENEEDAUTHnpm ERR! need auth auth required for publishingnpm ERR! need auth You need to authorize this machine using npm adduser解决办法:npm adduser输入:用户名(忘记的话,去npm网站查看:头像 > Profile Settings)密码邮箱(2)仓库地址不对npm ERR! code E409npm ERR! Registry returned 409 for PUT on http://r.cnpmjs.org/-/user/or...:yuyy: conflict原因:通过nrm ls 命令查看我此时的仓库地址为cnpm,而不是npm解决办法:用nrm切换到npm仓库,执行命令nrm use npm以上问题解决后再次执行发布命令npm publish,发布成功.第三步:去npm 官网搜索有可能有延迟,不能立马看不到。2.发布scoped包@yuyy/babel第一步:创建项目(1)创建工程文件夹mkdir babel && cd babel(2)创建package.json npm init按提示操作,最终结果:{ “name”: “babel”, “version”: “1.0.0”, “description”: “my scoped test package”, “main”: “index.js”, “scripts”: { “test”: “echo "Error: no test specified" && exit 1” }, “keywords”: [ “npm”, “package” ], “author”: “yuyy”, “license”: “ISC”}(3)创建README.md内容:### @yuyy/babelThis is my scoped npm package!It is just for learn.(4)创建index.js内容:module.exports = { printMsg: function () { console.log(’this message is from @yuyy/babel!’); }}最终的目录结构:└── babel ├── README.md ├── index.js └── package.json第二步:发布npm publish报错:没有发布权限npm ERR! publish Failed PUT 401npm ERR! code E401npm ERR! This package requires that publishers enable TFA and provide an OTP to publish. For more info, visit: https://go.npm.me/2fa-guide : babel原因:已经存在babel包,而我又不是babel的发布者解决:包名和域名差不多,先到先得,如果我非要发布一个叫babel的包,只能给它加作用域放到我的命名空间下第三步:加作用域npm init –scope=@yuyy -y@符号后面的是你注册npm账户时的username,如果不记得可以通过npm whoami查询。上面的命令其实是在重新生成package.json,只是会给包增加了作用域,执行完后package.json现在的内容:{ “name”: “@yuyy/babel”, “version”: “1.0.0”, “description”: “my scoped test package”, “main”: “index.js”, “scripts”: { “test”: “echo "Error: no test specified" && exit 1” }, “keywords”: [ “npm”, “package” ], “author”: “yuyy”, “license”: “ISC”}唯一的变化是name字段由原来的babel变成了@yuyy/babel。第四步:再次发布npm publish报错:npm ERR! publish Failed PUT 402npm ERR! code E402npm ERR! You must sign up for private packages : @yuyy/babel原因:npm publish 命令执行,默认是进行私有发布,参见官网publish命令上一篇文章最后提到过scoped的包私有发布时需要收费解决:如果不想花钱,那只能将包面向公共发布,这也符合npm鼓励开源的精神,这一点和GitHub创建仓库类似。第五步:公共发布npm publish –access public执行结果:值得注意的一点:我们的项目名是babel,最终发布的包名是@yuyy/babel,可见发布的包名可以和项目名不一致,包名取决于package.json中的name字段。第六步:npm官网搜索至此,我们完成了npm包发布的全部过程,一个unscoped包:yuyy-test-pkg,另一个scoped包:@yuyy/babel,也包括过程中可能遇到的问题。发布完我们自己的包之后,我们会在下一篇文章中介绍安装自己的包和涉及到的一些引用模块相关的知识,以及后续文章中介绍如何对发布过的包进行升级和废弃等。 ...

December 20, 2018 · 2 min · jiezi

[译]635000 个 npm 包中我应该用哪个

[[原文] Which of the 635000 npm modules do I choose? - Corey Cleary](https://www.coreycleary.me/wh…原创翻译,发表于个人博客He Xing’s waking life如有谬误,恳请指正如果您曾在 Node 或 JavaScript 前端开发中投入过时间和精力,那么您就知道 npm 中有数以十万计的模块可供您选择开发者不停的寻求帮助/抱怨:“对模块的选择困难正在蚕食我们"“X 模块和 Y 模块有什么区别?哪一个更好?““npm 很棒,但是这些模块可能在一年半载后失效,取决于模块维护者是否积极"通常在提出这样的问题时,您会得到十个不同的答案。每个人都会给您推荐自己喜欢的模块,接下来就演变成争论哪一个是最好的。选择 npm 模块时很容易面临纸上谈兵。选择太多,而新来者在鼓吹“快上车”,为您的项目选择合适的 npm 模块可能是有难度的。而且这些模块中有许多做类似(或相同)的事情,这也没有帮助。与其浪费时间在 google 上搜索,在 npmjs.org 上搜索,或者浪费更多的时间不构建您的项目,还不如搞清楚什么时候该选择哪些模块。精选清单为了帮助解决这个问题,您将在下面找到针对最常见问题类型(即 web 框架、模板、身份认证等)的 npm 模块列表,以及何时使用这些模块。这里有一些注意事项:您可能熟悉其中一些模块,甚至许多模块,但是有时候您面对的是您还没有接触到的技术栈(可能是身份验证或 Websocket 之类的东西),您需要知道有哪些备选模块可以完成这项工作。您可能有您认为更好的模块,或者可能有一个用例/需求没有包含在这里。我没有列出相同类别的 10 个不同模块,而是缩小了范围,这样您就可以避免分析瘫痪的陷阱。如果您认为自己的用例未被涵盖,请务必自行研究解决。本清单的目的在于让您能更快地启动和运行。这些模块的选择依据如下:它们完成工作的能力如何社区规模(对于支持/故障排除很重要)积极维护如果您发现自己仍然没有足够的信息做出决定,我建议使用slant.co和nodejs.libhunt.com来帮助进行比较。注意:为了保持范围的合理性,这些模块都考虑到了服务器端。它们中的一些可以同时在客户机或服务器上使用,但我的原则是“服务器优先”。HTTP requests (HTTP 请求)Request:当您需要基于回调的 HTTP 请求时可选择它,例如从一个 REST 服务连接到另一个。Axios当您需要基于 Promise的 HTTP 请求时可选择它注意:您可以使用request-promise,但是 axios 的依赖更少,并且基于原生 PromisesWeb frameworks (Web 框架)Express:如果您想为 API、网站或单页应用程序使用轻量级 web 框架,请使用它您不介意使用回调作为默认的异步处理方式使用该框架的模块生态极为繁荣您需要一个支持和故障排除的大型社区Koa:当您想要一个比 Express 更简洁的框架时使用Koa 更像是一个中间件层,它不提供模板或开箱即用的路由,因此更适合 API 开发要想支持开箱即用,您需要 async / awaitHapi如果您想要一个比 Express 或 Koa 更“自带电池”(译者注:原文"batteries"意为您不必重复造轮子,大多数您需要的功能都能通过(已有)库完成。您能导入并使用它们。)的框架,但又不像 Sails 那么多,那就使用它Sails当您需要像 Rails 这样的东西时,请使用它,它具有几乎所有功能(但是根据您的应用程序可能不需要那么多)Validation (前端验证)Ajv在需要验证 JSON 时使用(比如来自 web 请求)您希望与应用程序的其他非 JS 部分共享这些验证规则(因为它是 JSON,所以您可以这样做)Joi在需要验证输入时使用,并且喜欢链式调用的风格(译者注:代码见下方),而不是在 JSON 中定义验证规则您正在使用 Hapi(Hapi 自带 Joi)const schema = joi.object().keys({ id: joi.string().guid().required(), username: joi.string().alphanum().min(8).required()});Authentication (身份认证)Passport:当您需要为您的网站或 API 使用身份验证中间件时使用您希望能够在多种身份验证类型(Oauth,Facebook 等)之间进行选择您需要管理会话Asynchronous (异步)Async (library):当您需要使用旧版本的 Node,而该版本的 Node 支持只支持回调而不支持 Promises 时ES6 原生 promises (原生 JS, 非 npm):在使用大于 0.12 的 Node 版本时使用另一件需要考虑的事情是您的团队对 Promises 的接受程度。在 2018 年,大多数开发人员应该没问题了async / await(原生 JS,非 npm)当您为了逃脱“回调地狱”却又误闯“Promise 地狱”您有很多来自 Promises 的.then 和.catchDatabase (数据库)下面是数据库驱动程序、ORM 和查询生成器的组合。在使用 ORM 之前,我强烈建议您首先确保需要使用它。当您可以只使用原始 SQL 或查询生成器时,它们通常会添加另一层抽象,这层抽象不一定能够提供足够的回报。mysql, node-postgres:当您不需要完整的 ORM,而是需要使用原始 SQL 查询数据库时使用(这些是驱动程序)node-mongodb-native:当您不需要一个完整的 ORM,而是要直接查询 MongoDB 时使用Mongoose:当您希望为 MongoDB 使用 ORM 时使用Knex:当您不需要一个完整的 ORM 解决方案,而只是需要一些工具使编写查询代码更容易,可以使用它Knex 是一个生成 SQL 的查询生成器您拥有 Postgres、MSSQL、MySQL、MariaDB、SQLite3、Oracle 或 Amazon Redshift 数据库Objection.js:您希望 ORM 支持 Knex 支持的所有东西,不使用查询 DSL(因此您编写的代码更接近原始 SQL),具有基于 Promise 的 API 和良好的文档Process management (进程管理)这个网址提供了部分进程管理器的横向比较http://strong-pm.io/compare/。注意:它们还包括了 StrongLoop Process Manager,这是一个不错的工具,但是有点笨重。我建议您在决定使用 StrongLoop 之前先查看一下解决方案。PM2:当您希望进程管理器在服务崩溃时处理重新启动,并允许您控制集群时使用注意:PM2 所依据的 AGPL 许可证存在一些潜在的违规行为。这里有一些讨论。我的看法是它最有可能被使用。但如果您有任何问题,请咨询您的法律部门,因为我不是律师。forever:当您需要进程管理器来处理在服务崩溃时重新启动服务时使用您的部署规模较小(pm2 及其集群支持用于更大规模的部署)。如果您只有少量的服务/进程,那么您可能可以使用它nodemon:当您希望监视应用程序中的任何代码更改时使用,并在本地开发时自动重启服务器非常适合用于开发!Web Sockets对于 Web Sockets,我只是推荐 primus,而不是列出一个列表。它支持所有主要的 Web Sockets 实现,并且维护者十分积极。如果您需要换成其他的库,您可以通过一行代码更改轻松地更换。Primus:当您需要 Web Sockets 但又不想被束缚在特定的 Web Sockets 实现时使用API documentation (API 文档)Swagger-node:当您需要记录 REST API 并能够针对端点测试请求时使用Utilities/misc (通用工具/杂项)Lodash:当您需要 JS 实用程序库时使用您使用了大量的 OOP(面向对象编程)Ramda:当您希望使用函数式的编程风格时,请使用您想要像 lodash 这样的东西,但是在函数式编程范式中Moment:在需要解析、验证、操作和显示日期/时间时使用UUID:当您需要随机的、唯一的、难以破解的 id 时使用NVM:当您希望能够在环境中安装的多个 Node 版本之间切换时使用Fs-extra:当您需要能够递归地使用mkdir、rm -rf和 Node 中缺少的其他文件系统级功能时,请使用Nodemailer:当您需要从 Node 发送电子邮件时使用Dotenv:当您需要将.env 文件中的环境变量加载到 process.env 时使用CLI (命令行界面)Commander:当您要构建一个 CLI 程序时使用,该程序将所有参数作为命令行上的标志Inquirer:当您想要构建一个按顺序获取选项的“交互式”CLI 程序时使用(类似于运行 npm init 时的方式,它会询问您生成 package.json 文件的一系列问题)Logging (日志)Winston:当您需要一个日志库并需要不同的日志输出格式时使用Bunyan:当您需要一个日志库,并以 JSON 作为唯一日志输出格式时使用您希望为不同的组件、请求或函数使用不同的日志记录器(也就是说,这些日志记录器可能以不同的方式解析事件)Morgan:当您使用 Express 并且想要记录 HTTP 请求时使用注意:这将与 Winston 或 Bunyan 一起使用。由于它是中间件,它知道如何处理请求并记录它,但不处理 Winston 和 Bunyan 所做的日志输出的传输。Templating (前端模板)Pug (原 Jade):当您需要服务器端模板引擎时,请使用该引擎,该引擎易于阅读,并且支持开箱即用的子组件代码块您只需要输出 HTMLEJS:当您需要一个服务器端模板引擎,该引擎完全使用 JS,并且允许空格缩进(Pug 不允许)注意:不支持异步 JS 函数Testing (测试)Mocha:在需要编写和运行单元测试时使用Chai:当您需要证明您的单元测试中的断言时,请使用注意:这将与 Mocha 一起使用Chai-as-promised:当您希望在 promises 上证明您的断言时,而不是将断言放在 then 或 catch 中使用Sinon:当您需要用于测试的 mock 库时使用Tooling (开发工具)ESdoc:当您想从您的代码中生成 API 文档,并且您正在使用最新的 JS 版本时,请使用默认情况下支持当前版本的 JS(支持 class),因此如果在代码中使用 prototypes,请使用 JSdocJSdoc:当您需要支持 ES6 的代码 API 文档生成器时使用支持classes 和 prototypesESlint:当您需要一个 linter 来自动查找(和修复)代码中的语法和代码格式问题时使用(译者注:可参考本人博文vscode + vetur + eslint + prettier 实现团队代码风格统一)Debugging (调试)现在,原生 Node 调试现在已经够用了,我的建议是直接使用它。几年前,引入一些 npm 模块是很有帮助的,而且您可能有一个特定的用例需要一个 npm 模块,但是现在已经有了足够的本地支持,如果您对调试没有任何太疯狂要求,请务必忽略掉额外的依赖项。结论挑选模块可能很难,但您只需要一些方法点来解决它。当您正在为如何抉择浪费时间,或者甚至不知道从哪里开始时,请使用本指南来帮助您。 ...

December 20, 2018 · 2 min · jiezi

npm发布包教程(一):从npm说起

作为一个前端,每个人应该对npm install这个命令应该非常熟悉了,尤其是对这个命令执行过程中命令窗口疯狂输出肯定印象深刻。我发现有的同学对安装包轻车熟路,但对包从哪里来的以及如何发布一个npm并不是很了解,基于此,在团队内部做了一次分享,将分享过程整理如下,希望对每一个想发布自己的包但又不知从何开始的同学有所帮助。由于发布包涉及到发布、安装、更新、删除/废弃等阶段,写在一篇文章中篇幅过长,决定拆开做成一个系列。今天第一篇首先介绍一下npm相关的一些知识。npm(node package manager)是一个辅助前端开发的包管理工具包括:网站:找包、注册用户命令行:程序员与npm交互的主要形式仓库:最大的JavaScript软件库管理对象:包(package)管理方式:增(发布:npm publish;安装:npm i)删(废弃:npm deprecate;卸载:npm rm)改(更新:npm up)查(搜索:npm s)npm中涉及到的主体主要有两个:package和module,定义如下:package:含有package.json描述文件并发布到npm仓库的文件或者文件夹module:在node_modules中,可以被Node.js的require()方法加载的任何文件或文件夹可以这样理解:一个JavaScript软件,从本地发布到npm仓库时是package,从npm仓库下载到本地时就变成了module另外,基于以上,可以看出package和module的关系:module不一定是package(比如node内置模块),package一定是module含package.json文件的module一定是package除了以上概念外,再分别看下两个主体中的细节部分:package(包)主要有两个重要的属性:1.Scope(作用域,范围)一旦注册个人或者团体账户,就获得了与个人或者团体名相匹配的scope,可以用这个scope作为包的命名空间,例如@yuyy、@58。分类:unscoped:例如babelscopeduser: 例如@yuyy/babelorg:@babel/parser作用:为你自己发布的包提供命名空间,防止与他人的包名冲突2.Accessibility(可访问性)属性值有:private:私有,仅作者本人或团队成员可见public:公有,所有人可见此属性和github创建仓库时设定访问性的策略一致:公有,所有人可见,免费;私有,仅自己可见,收费。以上两个属性之间的关系如下:需要说明的几点:个人账户(User)可以创建和管理Unscoped的package;团队账户(Org)相互只能管理Scoped的packageUnscoped总是publicPrivate的package总是ScopedScoped的package默认Private,但需要付费,可以通过命令行改变其属性module(模块)下载到本地的module主要是用于在node环境被引用,为了能被Node.js的require()方法加载,module必须是下列情况之一:包含package.json,且package.json中有main字段的文件夹含有index.js的文件夹JavaScript文件以上都是一些npm相关的知识,在下一篇《npm发布包教程(二):发布包》中,我们开始演示发布npm包的实际操作过程。

December 20, 2018 · 1 min · jiezi

踩坑札记之 npm 包发布

重点阐述 npm 包发布前后所踩坑,首先科普如何发布 npm 包。发布流程注册账号开启终端输入:npm adduser根据提示输入 username、password、email 即刻注册成功,已注册用户请忽略上述步骤。登录请直接:npm login初始化包新建一文件夹如 bridge,而后:cd bridgenpm init依提示完成初始化并完善目录:.eslintrc、.gitignore、package.json、node_modules 不必过多解释,各位前端大大知晓。 src 内为未经 Babel 转码 的源代码,转码后置于 bridge 根目录,Babel 通过 .babelrc 配置。至于 README.md ,共享包说明书必不可少。发布包首先设置版本号,规则请参考 Semver,而后:npm publish理想情况下,包已成功发布至 npm,下述为非理想情况:命名问题npm ERR! you do not have permission to publish XXX. Are you logged in as the correct user?检查 npm name 是否被占用,若被占用请更改后重新发布。版本号问题npm ERR! forbidden cannot modify pre-existing version: 1.0.9: XXX当前版本已发布,请更新版本号。镜像配置问题npm ERR! no_perms Private mode enable, only admin can publish this module: XXX为加速 npm 部分同学利用淘宝镜像代理,请恢复原配置:npm config set registry=http://registry.npmjs.org步入正题踩坑札记https若 npm 包中 method 含协议类型为 http 的 请求 (可跨域请求),但调用服务协议类型为 https ,将报错:[blocked] The page at https://XXX was not allowed to display insecure content from http://XXX请将接口服务升级 https,url 书写方案:https://XXX :http 服务请求 https 接口 无报错 ;https 服务请求 https 接口 无报错。//XXX :请求协议类型随服务协议类型 自动转化 。环境变量若 npm 包中 method 执行需区分服务环境,通过 process.env.NODE_ENV 获取 环境变量 是否准确(node npm 包无上述准确性问题)?即便准确,命名规范也 未统一 ,如生产环境可由 prod、production 等指代。可考虑传参 约定的 环境变量。转码转码即将 ES6、ES7 转 ES5,先附赠 Babel 转码教程,再科普忽视转码的危害:未转码 前端包 引用于 低版本浏览器 不兼容。另外,含未转码片段的 vendor.js (通常将所依赖 node_modules 模块打包至 vendor.js)若先于 app.js 执行将直接导致较低版本浏览器 白屏 。未转码 node 包 引用于 低版本 node 服务 抛异常。若服务器所安装 node 版本较低,引用未转码包将导致接口 抛异常 。更糟糕的是若被页面加载前置中间件依赖,直接引发 白屏 。此刻或许有同学考虑植入 babel-polyfill…作者:呆恋小喵我的后花园:https://sunmengyuan.github.io…我的 github:https://github.com/sunmengyuan原文链接:https://sunmengyuan.github.io… ...

December 18, 2018 · 1 min · jiezi

npm源管理器nrm使用教程

介绍npm包有很多的镜像源,有的源有的时候访问失败,有的源可能没有最新的包等等,所以有时需要切换npm的源,nrm包就是解决快速切换问题的。nrm可以帮助您在不同的npm源地址之间轻松快速地切换。nrm内置了如下源:npmhttps://www.npmjs.com/cnpmhttps://cnpmjs.org/taobaohttps://npm.taobao.org/njhttps://registry.nodejitsu.com/rednpmhttp://registry.mirror.cqupt….skimdbhttps://skimdb.npmjs.com/regi…2. 安装打开终端运行如下命令:npm install -g nrm/usr/local/bin/nrm -> /usr/local/lib/node_modules/nrm/cli.js+ nrm@1.0.2added 324 packages from 564 contributors in 13.338s

December 18, 2018 · 1 min · jiezi

java中调用npm模块

一些小的javascript片段可以很方便的直接使用ScriptEngine.eval()直接导入,但是如何使用require方法导入使用npm下载的javascript模块呢?答案是使用jvm-npm这个项目。首先,创建一个目录放置所有javascript文件,比如:D:\path\to\js_modules\从jvm-npm下载jvm-npm.js,放到上述目录中使用npm i <js_module> -g下载你需要引用的npm模块,注意看最后的提示,即模块下载目录到模块下载目录,通常为<User_Dir>\npm\node_modules&lt;js_module>,里面一般有个dist目录,从里面找到完整的模块js文件,复制到上述目录见以下Kotlin示例代码:(注意把js_module换成你需要加载的模块名)val JS_ROOT = “D:\path\to\js_modules"val JSE = ScriptEngineManager().getEngineByName(“nashorn”).apply { System.setProperty(“user.dir”, JS_ROOT) // 设定jvm-npm的当前目录 eval(Util.readLocalUtfText("$JS_ROOT\jvm-npm.js”)) // 在ScriptEngine中加载jvm-npm.js println(eval(“typeof require”)) // 验证require方法已正确引入 eval(“var mylib = require(‘js_module’)”) // 这时候,就可以用require()方法来引入当前目录中的js模块了 println(eval(“typeof mylib”)) // 验证js模块已正确加载}…val bar = JSE.eval(“mylib.foo()”) // js代码中可以使用加载的js模块了注意:jvm-npm有个bug,如果你的js文件大于64K,则会导致文件截断。这种情况下你需要编写一个能一次读取完整File或InputStream的java方法,替换掉下面这句: return new Scanner(input).useDelimiter(’\A’).next()

December 17, 2018 · 1 min · jiezi

你想知道关于package-lock.json的一切,但是太害怕了问了?

简介如果你已经将节点包管理(npm)更新到版本5.x.x,看起来一切似乎都很顺利。等等,这是什么?用 npm 初始化项目的会自动创建了一个新文件 package-lock.json。如果打开它,它看起来有点像 package.json 的依赖项,但更冗长。我们决定忽略它,继续开发项目。最终,我们有时会遇到依赖项的问题,找不到,或者安装了错误的版本。大多数人最终都会删package-lock.json和运行“npm install”。那么,为什么要有它呢? 它应该做什么? 它实际上是做什么的?总结如果你使用的 npm 版本 为 ^5.x.x , package-lock.json 会默认自动生成你应该使用 package-lock 来确保一致的安装和兼容的依赖关系你应该将 package-lock 提交到源代码控制从npm ^ 5.1.x开始,package.json能够胜过 package-lock.json,所以你遇到较少让人头痛的问题不再删除 package-lock 只是为了运行npm install并重新生成它背景语义版本控制在你了解 package-lock 甚至 package.jso n之前,你必须了解语义版本控制(semver)。 这是npm背后的天才,是什么使它更成功。 你可以在 此处 阅读有关npm如何使用它的更多信息。简而言之,如果你正在构建与其他应用程序接口的应用程序,你应该告知你所做的更改将如何影响第三方与你的应用程序交互的能力。这是通过语义版本控制完成的,版本由三部分组成:X,Y,Z,分别是主要版本,次要版本和补丁版本。例如:1.2.3,主要版本1,次要版本2,补丁3。补丁中的更改表示不会破坏任何内容的错误修复。 次要版本的更改表示不会破坏任何内容的新功能。 主要版本的更改代表了一个破坏兼容性的大变化。 如果用户不适应主要版本更改,则内容将无法正常工作。管理包npm 存在使管理包变得容易。你的项目可能有数百个依赖项,每个依赖项都有一百个,为了让你的注意力远离依赖地狱,通过 npm 管理,使用一些简单的命令,你可以安装和管理这些依赖关系,几乎不必考虑它们。当您使用npm安装包(并保存它)时,会在 package.json 中添加一个包含包名称和应该使用的 semver的条目。默认情况下,npm 安装最新版本,并预先插入版本号,例如 “^1.2.12”,这表示至少应该使用版本 1.2.12,但任何高于此版本的版本都可以,只要它具有相同的主要版本,由于次要版本和补丁编号仅代表错误修正和非破坏性添加, 你可以安全地使用任何更高版本的同一主要版本。阅读更多关于semver通配符的信息,请看 这里。共享项目在 package.json 中定义这样的依赖项的真正好处是,任何有权访问 package.json 的人都可以创建一个包含运行应用程序所需模块的依赖项文件夹,但是让我们来看看事情可能出错的具体方式。假设我们创建了一个将使用 express 的新项目。 运行npm init后,我们安装express:npm install express - save。在编写代码时,最新的版本是4.15.4,所以 “express”:“^ 4.15.4”作为我的package.json中的依赖项添加,并且我的电脑安装了确切的版本。现在也许明天,express 的维护者会发布 bug 修复,因此最新版本变为4.15.5。 然后,如果有人想要为我的项目做贡献,他们会克隆它,然后运行`npm install。‘因为4.15.5是具有相同主要版本的更高版本,所以为它们安装。 我们都安装 express ,但我们却是不同的版本。从理论上讲,它们应该仍然是兼容的,但也许bugfix会影响我们正在使用的功能,而且当使用Express版本4.15.4和4.15.5运行时,我们的应用程序会产生不同的结果。Package-lock目的package-lock.json 的目的是避免上述情况,其中从同一 package.json 安装模块会导致两种不同的安装。 在 npm 版本 5.x.x 中添加了 package-lock.json,因此如果你使用的是主要版本 5 或更高版本,除非您禁用它,否则它会自动生成。内容结构package-lock 是 package.json 中列出的每个依赖项的大型列表,应安装的特定版本,模块的位置(URI),验证模块完整性的哈希,它需要的包列表 ,以及依赖项列表。 让我们来看看 express 的列表是什么:“express”: { “version”: “4.15.4”, “resolved”: “https://registry.npmjs.org/express/-/express-4.15.4.tgz", “integrity”: “sha1-Ay4iU0ic+PzgJma+yj0R7XotrtE=”, “requires”: { “accepts”: “1.3.3”, “array-flatten”: “1.1.1”, “content-disposition”: “0.5.2”, “content-type”: “1.0.2”, “cookie”: “0.3.1”, “cookie-signature”: “1.0.6”, “debug”: “2.6.8”, “depd”: “1.1.1”, “encodeurl”: “1.0.1”, “escape-html”: “1.0.3”, “etag”: “1.8.0”, “finalhandler”: “1.0.4”, “fresh”: “0.5.0”, “merge-descriptors”: “1.0.1”, “methods”: “1.1.2”, “on-finished”: “2.3.0”, “parseurl”: “1.3.1”, “path-to-regexp”: “0.1.7”, “proxy-addr”: “1.1.5”, “qs”: “6.5.0”, “range-parser”: “1.2.0”, “send”: “0.15.4”, “serve-static”: “1.12.4”, “setprototypeof”: “1.0.3”, “statuses”: “1.3.1”, “type-is”: “1.6.15”, “utils-merge”: “1.0.0”, “vary”: “1.1.1” } },可以在“requires”部分中列出的每个包中找到等效条目。npm(^5.x.x.x)后的做法,npm 使用package-lock.json,而不是使用 package.json 来解析和安装模块。因为 package-lock 为每个模块及其每个依赖项指定了版本,位置和完整性哈希,所以它每次创建的安装都是相同的。 无论你使用什么设备,或者将来安装它都无关紧要,每次都应该给你相同的结果,这非常有用。争议因此,如果引用 package-lock 是希望解决一个常见问题,为什么它的顶级搜索结果(除了npm文档)都是关于禁用它或质疑它扮演的角色?在npm 5.x.x之前,package.json 是项目的真实来源,npm 用户喜欢这个模型,并且非常习惯于维护他们的包文件。 但是,当首次引入 package-lock 时,它的行为与有多少人预期的相反。 给定一个预先存在的包和package-lock,对package.json的更改(许多用户认为是真实的来源)没有同步到package-lock 中。示例:包A,版本 1.0.0 在 package.json 和 package.lock.json 中。 在package.json中,A被手动编辑为1.1.0版。 如果认为 package.json 是真实来源的用户运行 npm install,他们会期望安装 1.1.0版。 但是,安装了1.0.0版,即使列出的 v1.1.0 是 package.json, 他们也希望安装是 1.0.0版。示例: package-lock.json 中不存在模块,但它存在于 package.json 中,作为一个将package.json 视为真实来源的用户,我希望能够安装我的模块。 但是,由于 package-lock.json 不存在该模块,因此未安装该模块,并且我的代码因无法找到模块而失败。大部分时间,因为我们无法弄清楚为什么我们的依赖关系没有被正确安装,要么删除了package-lock.json 并重新安装,要么完全禁用 package-lock.json 来解决问题。期望与真实行为之间的这种冲突在 npm repo中引发了一个非常有趣的问题线索。 有些人认为package.json 应该是事实的来源,有些人认为,因为 package-lock 是 npm 用来创建安装的东西,所以应该被认为是事实的来源。 这场争议的解决方案在于 PR#17508。 如果 package.json 已更新,Npm 维护者添加了一个更改,导致package.json 覆盖 package-lock。 现在,在上述两种情况下,都会正确安装用户期望安装的软件包。 此更改是作为npm v5.1.0的一部分发布的,该版本于2017年7月5日上线。你的点赞是我持续分享好东西的动力,欢迎点赞!一个笨笨的码农,我的世界只能终身学习!更多内容请关注公众号《大迁世界》 ...

December 3, 2018 · 2 min · jiezi

npm包的发布和管理

npm包管理npm其实是Node.js的包管理工具(node package manager)。为啥我们需要一个包管理工具呢?因为我们在Node.js上开发时,会用到很多别人写的JavaScript代码。如果我们要使用别人写的某个包,每次都根据名称搜索一下官方网站,下载代码,解压,再使用,非常繁琐。于是一个集中管理的工具应运而生:大家都把自己开发的模块打包后放到npm官网上,如果要使用,直接通过npm安装就可以直接用,不用管代码存在哪,应该从哪下载。更重要的是,如果我们要使用模块A,而模块A又依赖于模块B,模块B又依赖于模块C和模块D,npm可以根据依赖关系,把所有依赖的包都下载下来并管理起来。否则,靠我们自己手动管理,肯定既麻烦又容易出错。npm的基础使用npm的指令其实常用的并不多官方文档;列出来如下面:access Set access level on published packagesadduserAdd a registry user accountauditRun a security auditbinDisplay npm bin folderbugs Bugs for a package in a web browser maybebuildBuild a packagebundleREMOVED 已删除cacheManipulates packages cacheciInstall a project with a clean slatecompletion Tab Completion for npmconfigManage the npm configuration filesdedupeReduce duplicationdeprecateDeprecate a version of a packagedist-tagModify package distribution tagsdocsDocs for a package in a web browser maybedoctorCheck your environmentseditEdit an installed packageexploreBrowse an installed packagehelp-searchSearch npm help documentationhelpGet help on npmhookManage registry hooksinitcreate a package.json fileinstall-ci-testInstall a project with a clean slate and run testsinstall-testInstall package(s) and run testsinstallInstall a packagelinkSymlink a package folderlogout Log out of the registrylsList installed packagesnpm javascript package manageroutdatedCheck for outdated packagesowner Manage package ownerspackCreate a tarball from a packagepingPing npm registryprefixDisplay prefixprofileChange settings on your registry profilepruneRemove extraneous packagespublish Publish a packagerebuildRebuild a packagerepo Open package repository page in the browserrestartRestart a packageroot Display npm rootrun-scriptRun arbitrary package scriptssearch Search for packagesshrinkwrapLock down dependency versions for publicationstarMark your favorite packagesstarsView packages marked as favoritesstartStart a packagestopStop a packageteamManage organization teams and team membershipstestTest a packagetokenManage your authentication tokensuninstallRemove a packageunpublishRemove a package from the registryupdateUpdate a packageversion Bump a package versionviewView registry infowhoamiDisplay npm usernameinit初始化创建package.jsonnpm init [–force|-f|–yes|-y|–scope]npm init <@scope> (same as npx <@scope>/create)npm init [<@scope>/]<name> (same as npx [<@scope>/]create-<name>)search搜索查看远程npm相关资源包信息npm search [-l|–long] [–json] [–parseable] [–no-description] [search terms …]aliases: s, se, findinstall可以是说是install是最为常见的命令官方介绍,npm install (with no args, in package dir)npm install [<@scope>/]<name>npm install [<@scope>/]<name>@<tag>npm install [<@scope>/]<name>@<version>npm install [<@scope>/]<name>@<version range>npm install <git-host>:<git-user>/<repo-name>npm install <git repo url>npm install <tarball file>npm install <tarball url>npm install <folder> alias: npm icommon options: [-P|–save-prod|-D|–save-dev|-O|–save-optional] [-E|–save-exact] [-B|–save-bundle] [–no-save] [–dry-run] In global mode (ie, with -g or –global appended to the command), it installs the current package context (ie, the current working directory) as a global package. The -g or –global argument will cause npm to install the package globally rather than locally. The -f or –force argument will force npm to fetch remote resources even if a local copy exists on disk.上面的还介绍已经很详细了,所以这里只是讲一下npm install packageName [|–save |–save-prod|–save-dev]的区别;npm install babel npm5以前,会把X包安装到node_modules目录中,不会修改package.json的dependencies字段,之后运行npm install命令时,不会自动安装Xnpm install babelnpm5以后,会把X包安装到node_modules目录中,会修改package.json的dependencies字段,之后运行npm install命令时,会自动安装X, 线上环境时会被安装npm install babel -P -P, –save-prod: Package will appear in your dependencies. This is the default unless -D or -O are present. Package will appear in your dependencies, With the –production flag (or when the NODE_ENV environment variable is set to production), npm will install modules listed in dependencies.npm install babel -DPackage will appear in your devDependencies,With the –production flag (or when the NODE_ENV environment variable is set to production), npm will not install modules listed in devDependencies. 会把X包安装到node_modules目录中,会在package.json的devDependencies属性下添加X,之后运行npm install命令时,会自动安装X到node_modules目录中,之后运行npm install –production或者注明NODE_ENV变量值为production时,不会自动安装X到node_modules目录中update升级某个资源包或者全部资源包到某一个版本或者匹配的最新版本。npm update [-g] [<pkg>…]aliases: up, upgradeuninstall移除某个资源包npm uninstall [<@scope>/]<pkg>[@<version>]… [-S|–save|-D|–save-dev|-O|–save-optional|–no-save]aliases: remove, rm, r, un, unlinknpm包创建、编写、测试、维护Node出现之前,JavaScript是缺少包结构的。CommonJS致力于改变这种现状,于是定义了包的结构规范。而NPM的出现则是为了在CommonJS规范的基础上,实现解决包的安装卸载,依赖管理,版本管理等问题。require的查找机制明了之后,我们来看一下包的细节。一个符合CommonJS规范的包应该是如下这种结构:一个package.json文件应该存在于包顶级目录下二进制文件应该包含在bin目录下(可选)JavaScript代码入库是index.js,其他包含在lib目录下文档应该在doc目录下(可选)单元测试应该在test目录下(可选)初始化包创建包的根目录mkdir testpackage初始化npm init // 需要进行一些基本配置编写创建入口文件touch index.js编写代码const updateQueryString = function(url, key, value) { let urlParts = url.split(’#’), hash = ‘’, uri = urlParts.shift(), re = new RegExp(([?&amp;])${key}=.*?(&amp;|$), ‘i’), separator = uri.indexOf(’?’) !== -1 ? ‘&’ : ‘?’, encodeKey = encodeURIComponent(key), encodeValue = encodeURIComponent(value); urlParts.length > 0 && (hash = #${urlParts.join('#')}); if (uri.match(re)) { return uri.replace(re, $1${encodeKey}=${encodeValue}$2) + hash; } else { return ${uri}${separator}${encodeKey}=${encodeValue}${hash}; }};// 最后的导出部分module.exports = { updateQueryString};测试创建包的根目录npm i mocha -D // 安装测试库npm i chai -D // 安装断言库mkdir testcd testtouch index.test.js编写测试代码const utils = require(’./../index.js’);const expect = require(‘chai’).expect;let { updateQueryString} = utils;describe(‘updateQueryString函数的测试’, function() { it(‘https://test.com/path?test=11 修改test参数为22 应该等于 https://test.com/path?test=22', function() { expect(updateQueryString(‘https://test.com/path?test=11', ’test’, 22)).to.be.equal(‘https://test.com/path?test=22'); });});运行测试cd …/node_modules/mocha/bin/mocha npm包的发布注册账号npm官网终端执行 npm login,输入用户名和密码 、邮箱npm publish 发布Organization包我们经常可以看到@angular、@ionic他们的包, 都可以以@开头,那么我们的可不可以,原来angular、ionic都属于一个组织(Organization)只有新创建一个Organization组织之后,才能创建@testorg/testpackname这样的包!!!那么我们就可以去官网上创建我们的Organization,命名之后,官方步骤,初始化npm init –scope=<your_org_name>npm init foo -> npx create-foonpm init @usr/foo -> npx @usr/create-foonpm init @usr -> npx @usr/create修改package.json里面的name字段为@your_org_name/<pkg_name>发布npm publish –access public // 公开包发布npm包支持esmodule ...

November 22, 2018 · 3 min · jiezi

package-lock.json和yarn.lock的包依赖区别

node包管理包是一段可以复用的代码,这段代码可以从全局注册表下载到开发者的本地环境。每个包可能会,也可能不会依赖于别的包。简单地说,包管理器是一段代码,它可以让你管理依赖(你或者他人写的外部代码),你的项目需要这些依赖来正确运行。为啥我们需要一个包管理工具呢?因为我们在Node.js上开发时,会用到很多别人写的JavaScript代码。如果我们要使用别人写的某个包,每次都根据名称搜索一下官方网站,下载代码,解压,再使用,非常繁琐。更重要的是,如果我们要使用模块A,而模块A又依赖于模块B,模块B又依赖于模块C和模块D,npm可以根据依赖关系,把所有依赖的包都下载下来并管理起来。否则,靠我们自己手动管理,肯定既麻烦又容易出错。于是一个集中管理的工具应运而生:大家都把自己开发的模块打包后放到npm官网上,如果要使用,直接通过npm安装就可以直接用,不用管代码存在哪,应该从哪下载。Yarn 是为了弥补npm 的一些缺陷[速度慢,稳定性高]而出现的。”npmnpm 为你和你的团队打开了连接整个 JavaScript 天才世界的一扇大门。它是世界上最大的软件注册表,每星期大约有 30 亿次的下载量,包含超过 600000 个 包(package) (即,代码模块)。来自各大洲的开源软件开发者使用 npm 互相分享和借鉴。包的结构使您能够轻松跟踪依赖项和版本。下面是关于 npm 的快速介绍:npm 由三个独立的部分组成:网站网站 是开发者查找包(package)、设置参数以及管理 npm 使用体验的主要途径。注册表(registry)注册表 是一个巨大的数据库,保存了每个包(package)的信息。命令行工具 (CLI)CLI 通过命令行或终端运行。开发者通过 CLI 与 npm 打交道。yarnYarn发布于2016年10月,并在Github上迅速拥有了2.4万个Star。而npm只有1.2万个star。这个项目由一些高级开发人员维护,包括了Sebastian McKenzie(Babel.js)和Yehuda Katz(Ember.js、Rust、Bundler等)。Yarn一开始的主要目标是解决上一节中描述的由于语义版本控制而导致的npm安装的不确定性问题。虽然可以使用npm shrinkwrap来实现可预测的依赖关系树,但它并不是默认选项,而是取决于所有的开发人员知道并且启用这个选项。Yarn采取了不同的做法。每个yarn安装都会生成一个类似于npm-shrinkwrap.json的yarn.lock文件,而且它是默认创建的。除了常规信息之外,yarn.lock文件还包含要安装的内容的校验和,以确保使用的库的版本相同。yarn的优化主要体现在:速度快 :并行安装:无论 npm 还是 Yarn 在执行包的安装时,都会执行一系列任务。npm 是按照队列执行每个 package,也就是说必须要等到当前 package 安装完成之后,才能继续后面的安装。而 Yarn 是同步执行所有任务,提高了性能。离线模式:如果之前已经安装过一个软件包,用Yarn再次安装时之间从缓存中获取,就不用像npm那样再从网络下载了。安装版本统一:为了防止拉取到不同的版本,Yarn 有一个锁定文件 (lock file) 记录了被确切安装上的模块的版本号。每次只要新增了一个模块,Yarn 就会创建(或更新)yarn.lock 这个文件。这么做就保证了,每一次拉取同一个项目依赖时,使用的都是一样的模块版本。更好的语义化: yarn改变了一些npm命令的名称,比如 yarn add/remove,感觉上比 npm 原本的 install/uninstall 要更清晰。node包的安装执行工程自身 preinstall当前 npm 工程如果定义了 preinstall 钩子此时会被执行。确定首层依赖模块首先需要做的是确定工程中的首层依赖,也就是 dependencies 和 devDependencies 属性中直接指定的模块(假设此时没有添加 npm install 参数)。工程本身是整棵依赖树的根节点,每个首层依赖模块都是根节点下面的一棵子树,npm 会开启多进程从每个首层依赖模块开始逐步寻找更深层级的节点。获取模块获取模块是一个递归的过程,分为以下几步:获取模块信息。在下载一个模块之前,首先要确定其版本,这是因为 package.json 中往往是 semantic version(semver,语义化版本)。此时如果版本描述文件(npm-shrinkwrap.json 或 package-lock.json)中有该模块信息直接拿即可,如果没有则从仓库获取。如 packaeg.json 中某个包的版本是 ^1.1.0,npm 就会去仓库中获取符合 1.x.x 形式的最新版本。获取模块实体。上一步会获取到模块的压缩包地址(resolved 字段),npm 会用此地址检查本地缓存,缓存中有就直接拿,如果没有则从仓库下载。查找该模块依赖,如果有依赖则回到第1步,如果没有则停止。模块扁平化(dedupe)上一步获取到的是一棵完整的依赖树,其中可能包含大量重复模块。比如 A 模块依赖于 loadsh,B 模块同样依赖于 lodash。在 npm3 以前会严格按照依赖树的结构进行安装,因此会造成模块冗余。yarn和从 npm5 开始默认加入了一个 dedupe 的过程。它会遍历所有节点,逐个将模块放在根节点下面,也就是 node-modules 的第一层。当发现有重复模块时,则将其丢弃。这里需要对重复模块进行一个定义,它指的是模块名相同且 semver 兼容。每个 semver 都对应一段版本允许范围,如果两个模块的版本允许范围存在交集,那么就可以得到一个兼容版本,而不必版本号完全一致,这可以使更多冗余模块在 dedupe 过程中被去掉。安装模块这一步将会更新工程中的node_modules,并执行模块中的生命周期函数(按照 preinstall、install、postinstall 的顺序)。执行工程自身生命周期当前 npm 工程如果定义了钩子此时会被执行(按照 install、postinstall、prepublish、prepare 的顺序)。包依赖关系我们要使用模块A,而模块A又依赖于模块B,模块B又依赖于模块C和模块D,npm可以根据依赖关系,把所有依赖的包都下载下来并管理起来,而这种依赖又有不一样的表现形式。嵌套依赖扁平依赖嵌套依赖假设目前工程依赖 A, B, C 三个库,而他们对某个库 somelib 存在这样的依赖关系:A - somelib 1.4.xB - somelib 1.6.xC - somelib 1.6.x如果要安装 ABC 三个库,那么 somelib 会存在版本冲突。npm5+/yarn 会在实际安装时,给三个库分别下载各自依赖的 somelib 版本。假设 npm 先安装了 A, 由于 A 依赖 somelib 1.4.x 版本,那么 自身依赖先安装1.4.x 。再安装 B, C 时,由于 B, C 依赖的都不是 1.4.x, 于是安装完之后,关系就变成这个样子了:node_modules├── A│ └── node_modules│ └── somelib 1.4.x├── B│ └── node_modules│ └── somelib 1.6.x└── C └── node_modules └── somelib 1.6.x这样就是嵌套依赖。很显然这种方式很大的浪费了磁盘空间。扁平依赖当关联依赖中包括对某个软件包的重复引用,在实际安装时将尽量避免重复的创建。假设目前工程依赖 A, B, C 三个库,而他们对某个库 somelib 存在这样的依赖关系:A - somelib 1.4.xB - somelib 1.6.xC - somelib 1.6.x如果要安装 ABC 三个库,那么 somelib 会存在版本冲突。npm5+/yarn 会在实际安装时,给三个库分别下载各自依赖的 somelib 版本。假设 npm 先安装了 A, 由于 A 依赖 somelib 1.4.x 版本,那么 1.4.x 会变成主版本。再安装 B, C 时,由于 B, C 依赖的都不是 1.4.x, 于是安装完之后,关系就变成这个样子了:node_modules├── A├── somelib 1.4.x├── B│ └── node_modules│ └── somelib 1.6.x└── C └── node_modules └── somelib 1.6.x这样就是扁平依赖。需要注意的是,明明 B, C 都依赖 1.6.x 版本,实际上 npm5+/yarn 却要把这个版本保存两次,这样明显是对磁盘空间的浪费。我们把这种情况就称为不完全扁平的。目前这种情况还无法安全解决。lock文件锁文件是由包管理器自动生成的。它包含了重现全部的依赖源码树需要的所有信息、你的项目依赖中的所有信息,以及它们各自的版本。现在值得强调的是,Yarn 使用了锁文件,而 npm5以前没有默认锁文件,npm5之后加入了默认锁文件功能。我们会谈到这种差别导致的一些后果。既然我已经向你介绍了包管理器这部分,现在我们来讨论依赖本身。目前常见的两种lock文件:packahe-lock.json 是npm5之后默认生成的锁文件yarn.lock 是yarn的锁文件packahe-lock.json解析{ “name”: “package-name”, “version”: “1.0.0”, “lockfileVersion”: 1, “dependencies”: { “cacache”: { “version”: “9.2.6”, “resolved”: “https://registry.npmjs.org/cacache/-/cacache-9.2.6.tgz", “integrity”: “sha512-YK0Z5Np5t755edPL6gfdCeGxtU0rcW/DBhYhYVDckT+7AFkCCtedf2zru5NRbBLFk6e7Agi/RaqTOAfiaipUfg==” }, “duplexify”: { “version”: “3.5.0”, “resolved”: “https://registry.npmjs.org/duplexify/-/duplexify-3.5.0.tgz", “integrity”: “sha1-GqdzAC4VeEV+nZ1KULDMquvL1gQ=”, “dependencies”: { “end-of-stream”: { “version”: “1.0.0”, “resolved”: “https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.0.0.tgz", “integrity”: “sha1-1FlucCc0qT5A6a+GQxnqvZn/Lw4=” }, } }}可以看出来,package-lock.json把所有的包的依赖顺序列出来,第一次出现的包名会提升到顶层,后面重复出现的将会放入被依赖包的node_modules当中。引起不完全扁平化问题。yarn.lock解析# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.# yarn lockfile v1package-1@^1.0.0: version “1.0.3” resolved “https://registry.npmjs.org/package-1/-/package-1-1.0.3.tgz#a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0"package-2@^2.0.0: version “2.0.1” resolved “https://registry.npmjs.org/package-2/-/package-2-2.0.1.tgz#a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0" dependencies: package-4 “^4.0.0"package-3@^3.0.0: version “3.1.9” resolved “https://registry.npmjs.org/package-3/-/package-3-3.1.9.tgz#a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0" dependencies: package-4 “^4.5.0"package-4@^4.0.0, package-4@^4.5.0: version “4.6.3” resolved “https://registry.npmjs.org/package-4/-/package-4-2.6.3.tgz#a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0"显然yarn.lock锁文件把所有的依赖包都扁平化的展示了出来,对于同名包但是semver不兼容的作为不同的字段放在了yarn.lock的同一级结构中。验证实例在一个测试package工程里面,安装了以下三个包,安装了react-router 3.2.1,另外安装了react-router-dom 4.3.1 和react-router-native 4.3.0,这两个都依赖"react-router”: “^4.3.0”,结果如下:.└── node_modules├── react-router-dom4.3.1│ └── react-router4.3.1├── react-router-native4.3.0│ └── react-router4.3.1└── react-router3.2.1查看package-lock.json结果和最后node_modules安装结果:查看yarn.lock结果和最后node_modules安装结果:lock官方介绍理解 NPM 5 中的 lock 文件知乎问答1知乎问答2 ...

November 20, 2018 · 2 min · jiezi

如何创建并发布一个 vue 组件

步骤创建 vue 的脚手架npm install -g @vue/clivue init webpack绑定 git 项目cd existing_foldergit initgit remote add origin http://gitlab.alipay-inc.com/ampg/my-projec.gitgit add .git commitgit push -u origin master写组件创建组件 src/components/xxx.vue例如:<template> <div class=“hello”> <h1>{{ msg }}</h1> <h2>Essential Links</h2> </div></template><script>export default { name: ‘HelloWorld’, data () { return { msg: ‘Welcome to Your Vue.js App’ } }}</script><!– Add “scoped” attribute to limit CSS to this component only –><style scoped>h1, h2 { font-weight: normal;}</style>发布npm publish展示代码参考vue-component-popup参考文档Packaging Vue Components for npmVue CLI 3vue-sfc-rollup ...

November 8, 2018 · 1 min · jiezi

使用Vue CLI 3将基于element-ui二次封装的组件发布到npm

前言:之前在网上找的好多都是基于vue-cli 2.x的,而使用vue-cli 3的文章比较少,所以我在自己尝试的时候把几篇文章结合了一下,调出来了我想要的模式,也就是Vue CLI 3 + element-ui + 多个二次封装的组件。最终想要的是 element-ui 这种感觉的,很多组件可以在不同项目中复用。安装依赖首先用Vue CLI 3来初始化项目yarn global add @vue/clivue create qiyun-el-uivue ui安装element-ui这里使用官方提供的的插件安装:http://element.eleme.io/#/zh-…https://github.com/ElementUI/…在插件列表搜索element在这里我选的手动导入,图中是全部导入这样在项目中,就会新建一个plugins文件夹,里面有个element.js 文件,如果想手动引入,就在这里添加要依赖的组件,这里是为了调试组件:import Vue from ‘vue’import { Button, Dialog } from ’element-ui’Vue.use(Button)Vue.use(Dialog)由于我们是基于element-ui的部分组件做的二次封装,所以最好还是按需引入所依赖的组件比较好。编写组件在 src 的同级下面新建 packages 目录,在这里添加自己封装的要发布的组件。例如,新建 qe-modal 文件夹,再接着新建 src 文件夹,里面新建 qe-modal.vue,在这里写组件的代码:<template> <el-dialog :title=“title” :visible=“dialogVisible” @close="$emit(‘update:dialogVisible’, false)" :width=“width”> <slot name=“modal-body”></slot> <div slot=“footer” class=“dialog-footer”> <slot name=“modal-footer”> <el-button @click="$emit(‘update:dialogVisible’, false)" size=“small”>取 消</el-button> <el-button type=“primary” @click="$emit(‘confirm’)" size=“small” :disabled=“confirmDisable || beforeSendDisable”>{{ beforeSendDisable? “处理中…” : “确 定” }}</el-button> </slot> </div> </el-dialog></template><script>export default { name: ‘qeModal’, props: { dialogVisible: Boolean, title: String, width: { type: String, default: ‘580px’ }, beforeSendDisable: { type: Boolean, default: false }, confirmDisable: { type: Boolean, default: false } }}</script>在 qe-modal 根目录下新建 index.js ,里面注册单独的该组件,方便使用时可以单独引用:import qeModal from ‘./src/qe-modal’qeModal.install = function(Vue) { Vue.component(qeModal.name, qeModal)}export default qeModal这样一个组件就添加完成了,然后需要在 packages 的根目录下添加一个总的 index.js,这里是全局注册的地方,使用时可以全局引入,其实就跟 element-ui 的两种方式一样:import qeModal from ‘./qe-modal’const components = [ qeModal]const install = function(Vue) { components.forEach(component => { Vue.component(component.name, component); });}if (typeof window !== ‘undefined’ && window.Vue) { install(window.Vue);}export default { install, qeModal}后面再添加组件,在这里也要再注册一下,而element-ui 源码中是动态引入的,我们的项目组件还没那么多,可以先一个个手动引入,如果后面数量多了,不好维护,可以参考 element-ui 的源码实现,我在这里做了一些简单的解释。配置 npm在 package.json 里面的 script 里面加一个 lib选项,方便每次构建:“scripts”: { // …, “lib”: “vue-cli-service build –target lib –name qiyun-el-ui –dest lib ./packages/index.js” },其中 –name 后面是你最后想要生成文件的名字,并用 –dest lib 修改了构建的目录。然后在 package.json 里面添加一些npm包发布的相关信息,比如作者、版本等:其中最重要的是:“main”: “lib/qiyun-el-ui.common.js”,这里的路径要和上面构建出来的目录和文件名对应上。里面的配置项,在网上找了个例子:{ “name”: “maucash”, “description”: “maucash中常用组件抽取”, “version”: “1.0.2”, “author”: “kuangshp <kuangshp@126.com>”, // 开源协议 “license”: “MIT”, // 因为组件包是公用的,所以private为false “private”: false, // 配置main结点,如果不配置,我们在其他项目中就不用import XX from ‘包名’来引用了,只能以包名作为起点来指定相对的路径 “main”: “dist/maucash.js”, “scripts”: { “dev”: “cross-env NODE_ENV=development webpack-dev-server –open –hot”, “build”: “cross-env NODE_ENV=production webpack –progress –hide-modules” }, “dependencies”: { “axios”: “^0.18.0”, “iview”: “^2.14.1”, “style-loader”: “^0.23.1”, “url-loader”: “^1.1.2”, “vue”: “^2.5.11” }, // 指定代码所在的仓库地址 “repository”: { “type”: “git”, “url”: “git+git@git.wolaidai.com:maucash/maucash.git” }, // 指定打包后,包中存在的文件夹 “files”: [ “dist”, “src” ], // 指定关键词 “keywords”: [ “vue”, “maucash”, “code”, “maucash code” ], // 项目官网的地址 “homepage”: “https://github.com/kuangshp/maucash", “browserslist”: [ “> 1%”, “last 2 versions”, “not ie <= 8” ], “devDependencies”: { “babel-core”: “^6.26.0”, “babel-loader”: “^7.1.2”, “babel-plugin-transform-runtime”: “^6.23.0”, “babel-preset-env”: “^1.6.0”, “babel-preset-stage-3”: “^6.24.1”, “cross-env”: “^5.0.5”, “css-loader”: “^0.28.7”, “file-loader”: “^1.1.4”, “node-sass”: “^4.5.3”, “sass-loader”: “^6.0.6”, “vue-loader”: “^13.0.5”, “vue-template-compiler”: “^2.4.4”, “webpack”: “^3.6.0”, “webpack-dev-server”: “^2.9.1” }}发布到npm到这块后面的网上有很多更细致的教程,我就不在这里赘述了。下面给出两个文章的链接,供参考。1、到npm上注册一个账号2、登录npm login3、添加用户信息npm adduser4、发布到远程仓库(npm)上npm publish5、删除远程仓库的包npx force-unpublish package-name ‘原因描述’参考:https://juejin.im/post/5bc441…Vue cli3 库模式搭建组件库并发布到 npm的流程_vue.js_脚本之家 ...

November 6, 2018 · 2 min · jiezi

我们为什么需要 lock 文件

前言从 Yarn 横空出世推出 lock 文件以来,已经两年多时间了,npm 也在 5.0 版本加入了类似的功能,lock 文件越来越被开发者们接收和认可。本篇文章想从前端视角探讨一下我们为什么需要 lock 文件,以及它的一些成本与风险,当然其中一些观点对于后端也是适用的。为什么需要 lock 文件之所以需要 lock 文件,我觉得主要有 4 个原因:确保各环境依赖版本的一致性软件开发一般有着好几个环境,通常包括本地开发环境、集成测试环境、预发布环境以及线上环境。各环境依赖版本不一致通常是 bug 的一大来源,大家可能碰到过“在我的电脑上是好的”这种问题,也许就是依赖版本不一致导致的。这种问题通常很难定位,因为你很难确定到底是自己的问题还是依赖的问题。这也是 Yarn 推出 lock 文件的初衷之一,使用了 lock 文件,你在排查问题时至少可以排除依赖版本不一致这个因素。语义化版本并不绝对可靠一些开发者不愿意使用 lock 文件,一个主要原因是他们希望基于语义化版本号让依赖自动升级,认为只要选择的依赖可靠,大版本不变化就可以放心升级。在绝大多数情况下这么做不会有问题,但是也有意外情况,比如:React 在 v16.4.0 对 getDerivedStateFromProps 的调用时机进行了调整。React 在 v16.3.0 引入了这个 API,最初只有在父组件引起的重渲染过程中会被调用,v16.4.0 调整为在所有渲染过程中都会被调用。如果你在不知情的情况下(自动)把 React 从 v16.3.0 升级到了 v16.4.0,那么极端情况下你的应用就会出问题。虽然只有在很极端的情况下你才会碰到类似问题,但是这种问题本来就是不怕一万就怕万一。可控的升级依赖现在通过 webpack 把依赖单独提取为一个 vendor.js 是个很常见的做法,因为依赖变更相对来说没那么频繁,再配合上强缓存,可以做到即使发布了新版本,用户也可以使用缓存的 vendor.js 而不必重新下载。通常我们的应用不止一个依赖,这些依赖肯定也不是同一时间发布更新,如果不使用 lock 文件让其自由更新,可能会导致 vendor.js 缓存失效多次(每个依赖更新都会导致缓存失效)。如果使用 lock 文件就可以积累一段时间,让多个依赖集中更新,甚至跳过一些小版本更新,从而提高 vendor.js 的缓存命中率。安全问题几个月前 ESLint 发生了一个安全事故,一个攻击者窃取了 ESLint 维护者的 npm 账户,并发布了恶意版本的 eslint-scope 和 eslint-config-eslint(都是更新的小版本),其中前者是 babel-eslint 和 webpack 的依赖。如果没有使用 lock 文件,那么你就极有可能中招,ESLint 事后也建议开发者使用 lock 文件来避免自动安装新版本。成本使用 lock 文件自然会增加一点项目的维护成本,因为依赖不会再自动升级,所以需要项目维护者每隔一段时间手动进行升级。另外如果两个人同时修改了依赖,解决 lock 文件的冲突也是一件很麻烦的事。但是手动升级依赖也有一些额外的好处,至少你升级每个依赖时都要去看一下它的 change log,这样可以对每一次升级做到心中有数,这也有助于你掌握依赖的发展趋势。比如前文提到的 React 的例子,只要你在升级时看一眼它的 change log,就很容易避开可能出现的问题。风险我唯一能想到的风险就是依赖版本固化问题,如果你使用了 lock 文件又没有花时间跟精力去维护它,那么你的项目就很容易陷入依赖版本固化的问题。如果太久没有升级依赖,你当前使用的版本跟最新版差别太大,升级就会很困难,考虑到现实成本问题,可能就永远不会升级了。但是如果不使用 lock 文件就能完全避免这个问题吗,我想也不一定。不使用 lock 文件最多也只能在同一个大版本范围内自动升级,如果依赖升级了大版本,你没有花时间去升级,也会碰到同样的问题。只是相对于不使用 lock 文件,问题暴露的晚一些而已。 ...

November 6, 2018 · 1 min · jiezi

了解可执行的NPM包

NPM是Node.js的包管理工具,随着Node.js的出现,以及前端开发开始使用gulp、webpack、rollup以及其他各种优秀的编译打包工具(大多数采用Node.js来实现),大家都开始接触到一些Node.js,发现了使用NPM来管理一些第三方模块会很方便。 大家搬砖的模式也是从之前的去插件官网下载XXX.min.js改为了npm install XXX,然后在项目中require或者import。当然,NPM上边不仅仅存在一些用来打包、引用的第三方模块,还有很多优秀的工具(包括部分打包工具),他们与上边提到的模块的区别在于,使用npm install XXX以后,是可以直接运行的。常见的那些包可以回想一下,webpack官网中是否有过这样的字样:> npm install webpack -g> webpack当然,现在是不推荐使用全局安装模式的,具体原因会在下边提到以及非全局的安装使用步骤:> npm install webpack然后编辑你的package.json文件:{ “scripts”: {+ “webpack”: “webpack” }}再使用npm run就可以调用了:> npm run webpack以上非全局的方案是比较推荐的做法不过还可以顺带一提的是在NPM 5.x更新的一个新的工具,叫做npx,并不打算细说它,但它确实是一个很方便的小工具,在webpack官网中也提到了简单的使用方法 就像上边所提到的修改package.json,添加scripts然后再执行的方式,可以很简单的使用npx webpack来完成相同的效果,不必再去修改额外的文件。(当然,npx可以做更多的事情,在这里先认为它是./node_modules/webpack/bin/webpack.js的简写就好了) 包括其他常用的一些,像n、create-react-app、vue-cli这些工具,都会直接提供一个命令让你可以进行操作。自己造一个简易的工具最近面试的时候,有同学的回答让人哭笑不得: Q:你们前端开发完成后是怎样打包的呢? A:npm run build。 [黑人问号脸.png]。经过再三确认后,该同学表示并没有研究过具体是什么,只知道执行完这个命令以后就可以了。 我本以为这仅仅是网上的一个段子,但没想到真的被我碰到了。也不知道是好事儿还是坏事儿。。 从我个人的角度考虑,还是建议了解下你所使用的工具。至少看下scripts里边究竟写的是什么咯 :) P.S. npm scripts中不仅仅可以执行NPM模块,普通的shell命令都是支持的创建工程首先的第一步,就是你需要有一个文件夹来存放你的NPM包,因为是一个简单的示例,所以不会真实的进行上传,会使用npm ln来代替npm publish + npm install。 随便创建一个文件夹即可,文件夹的名字也并不会产生太大的影响。 然后需要创建一个package.json文件,可以通过npm init来快速的生成,我个人更喜欢添加-y标识来跳过一些非必填的字段。> mkdir test-util> cd test-util> npm init -y创建执行文件因为我们这个模块就是用来执行使用的,所以有没有入口文件实际上是没有必要的,我们仅仅需要创建对应的执行文件即可,需要注意的一点是:与普通的JS文件区别在于头部一定要写上#!/usr/bin/env node#!/usr/bin/env node// index.jsconsole.log(‘first util’)注册执行命令然后就是修改package.json来告诉NPM我们的执行文件在哪:{+ “bin”: “./index.js”}在只有一个bin,且要注册的命令与package.json中的name字段相同时,则可以写成上边那种形式,如果要注册多个可执行命令,那么就可以写成一个k/v结构的参数:{ “bin”: { “command1”: “./command1.js”, “command2”: “./command2.js” }}调用时就是 command1 | command2模拟执行接下来我们去找另一个文件夹模拟安装NPM模块,再执行npm ln就可以了,再执行对应的命令以后你应该会看到上边的log输出了:> cd .. && mkdir fake-repo && cd fake-repo> npm ln ../test-util> test-util # globalfirst util> npx test-util # localfirst util这样一个最简易的可执行包就创建完成了。npm ln 为 npm link 的简写 npm ln <模块路径> 相当于 cd <模块路径> && npm ln + npm ln <模块名> 要注意是 模块名__,而非文件夹名, __模块名 为package.json中所填写的name字段global 与 local 的区别因为npm link执行的特性,会将global+local的依赖都进行安装,所以在使用上不太好体现出两者的差异,所以我们决定将代码直接拷贝到node_modules下:> npm unlink –no-save test-util # 仅移除 local 的依赖> cp -R ../test-util ./node_modules/> npm rebuild因为绕过了NPM的安装步骤,一定要记得npm rebuild来让NPM知道我们的包注册了bin 这时候我们修改脚本文件,在脚本中添加当前执行目录的输出#!/usr/bin/env node- console.log(‘first util’)+ console.log(process.execPath) // 返回JS文件上层文件夹的完整路径这时再次执行两种命令,就可以看到区别了。 之所以要提到global与local,是因为在开发的过程中可能会不经意的在这里踩坑。 比如说我们在开发Node项目时,经常会用到nodemon来帮助在开发期间监听文件变化并自动重启。 为了使用方便,很可能会将预定的一个启动命令放到npm scripts中去,类似这样的:{ “script”: { “start”: “nodemon ./server.js” }}两者混用会带来的问题这样的项目在你本地使用是完全没有问题的,但是如果有其他的同事需要运行你的这个项目,在第一步执行npm start时就会出异常,因为他本地可能并没有安装nodemon。 以及这样的做法很可能会导致一些其它包引用的问题。 比如说,webpack实际上是支持多种语言编写config配置文件的,就拿TypeScript举例吧,最近也一直在用这个。> webpack –config webpack.config.ts这样的命令是完全有效的,webpack 会使用 ts 的解释器去执行对应的配置文件因为webpack不仅仅支持这一种解释器,有很多种,类似CoffeeScript也是支持的。 所以webpack肯定不能够将各种语言的解释器依赖都放到自身的依赖模块中去,而是会根据传入config的文件后缀名来动态的判断应该添加哪些解释器,这些在webpack的源码中很容易找到:获取配置文件后缀获取对应的解释器并引入模块注册根据webpack动态获取解释器的模块interpret来看,.ts类型的文件会引入这些模块:[’ts-node/register’, ’typescript-node/register’, ’typescript-register’, ’typescript-require’],但是在webpack的依赖中你是找不到这些的。 在源码中也可以看到,webpack在执行config之前动态的引入了这些解释器模块。 这里也可以稍微提一下Node中引入全局模块的一些事儿,我们都知道,通过npm install安装的模块,都可以通过require(‘XXX’)来直接引用,如果一些第三方模块需要引入某些其他的模块,那么这个模块也需要存在于它所处目录下的node_modules文件夹中才能够正确的引入。 首先有一点大家应该都知道的,目前版本的NPM,不会再有黑洞那样深的node_modules了,而是会将依赖平铺放在node_modules文件夹下。比如说你引入的模块A,A的内部引用了模块B,那么你也可以直接引用模块B,因为A和B都存在于node_modules下。 还是拿我们刚才做的那个小工具来实验,我们在fake-repo中添加express的依赖,然后在test-util中添加koa的依赖,并在test-util/index.js中require上述的两个模块。 你会发现,npx test-util运行正确,而test-util却直接报错了,提示express不存在。我们可以通过NPM的一个命令来解释这个原因:> npm root<current>/node_modules> npm root -g<global>/node_modules这样输出两个路径应该就能看的比较明白了,koa模块是没有问题的,因为都是存在于这些路径下的node_modules,而express则只存在于<current>/node_modules/test-util/node_modules下,全局调用下,require是找不到express的。# global 下的结构.├── /usr/local/lib/node_modules # npm root 的位置│ ├── koa│ └── test-util # 执行脚本所处的位置└── <workspace> # 本地的项目 ├── node_modules │ └── express └── .# local 下的结构└── <workspace> # 本地的项目 ├── node_modules # npm root 的位置 │ ├── koa │ ├── test-util # 执行脚本所处的位置 │ └── express └── .所以这也从侧面说明了为什么webpack可以直接在自己的文件中引用并不存在于自己模块下的依赖。 因为webpack认为如果你要使用TypeScript,那么一定会有对应的依赖,这个模块就是与webpack同级的依赖,也就是说webpack可以放心的进行require,大致这样的结构:├── node_modules # npm root 的位置│ ├── webpack│ └── typescript└── . # 在这里执行脚本以及一个相反的栗子????,如果有些依赖在global下安装了,但是没有在local下进行安装,也许会出现这样的情况,命令直接调用的话,完全没有问题,但是放到npm scripts中,或者使用npx来进行调用,则发现提示模块不存在各种balabala的异常。 P.S. 在webpack中,如果模块不存在,并不会给你报错,而是默认按照JS的方式进行解析,所以可能会遇到提示语法错误,这时候不用想了,一定是缺少依赖 也可以说npx是个好东西,尽量使用npx的方式来调用,能少踩一些global、local的坑最终的上线当然了,真实的开发完一个工具以后,就需要进行提交到NPM上了,这个也是一个很简单的步骤,npm publish即可,会自动获取package.json中的name作为包名(重复了会报错)。小结总结了一下关于NPM可执行的包相关的一些东东,希望能够帮大家简单的理解这是个什么,以及global和local下一些可能会遇到的问题,希望能够让大家绕过这些坑。 如文中有误还请指出,NPM工具相关的问题也欢迎来讨论。参考资料npm-binwebpack-cli ...

October 26, 2018 · 2 min · jiezi

vue组件从开发到发布

组件化是前端开发非常重要的一部分,从业务中解耦出来,可以提高项目的代码复用率。更重要的是我们还可以打包发布,俗话说集体的力量是伟大的,正因为有许许多多的开源贡献者,才有了现在的世界。不想造轮子的工程师,当不了合格的搬运工 。让我们来了解一下vue组件从开发到打包发布流程,并配置Github主页。本文以 vue-clock2 组件为例,欢迎star^_^~~ 项目地址目标框架:vue打包工具:webpack发布源:npm代码托管:github项目结构|– node_modules|– src| |– index.js| |– vue-clock.vue|– docs| |– index.html| |– index.css|– distsrc: 组件相关代码。node_modules: 组件依赖包。docs: 说明文档,组件简单的可以单个页面,也可以使用vuepress。dist: 打包后组件内容,一般 package.json 的 main 入口指向这个文件夹里的文件。组件开发vue组件开发相对来讲还是比较容易的,创建一个 vue-clock.vue 文件,组件的相关逻辑实现。该组件主要实现一个基于 time 属性输入,显示对应时间的钟表样式。 <div class=“clock”> <div class=“clock-circle”></div> <div class=“clock-hour” :style="{transform:hourRotate}"></div> <div class=“clock-minute” :style="{transform:minuteRotate}"></div> <b class=“hour” v-for=“h in timeList” :key=“h”> <span>{{h}}</span> </b> </div>通过元素画出钟表的样式,基于 css3的transform 属性旋转出每个时间点。因为钟表的时针并不是直接跳到下一个点的,所以需要计算出不同分钟时,时钟指针的旋转角度。后续增加了不指定时间的情况,显示当前时间并每分钟自动更新。export default { data() { return { timeList: [12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], hourRotate: “rotatez(0deg)”, minuteRotate: “rotatez(0deg)” }; }, props: [“time”], watch: { time() { this.show(); } }, methods: { show() { this.showTime(); if (this._timer) clearInterval(this._timer); if (!this.time) { this._timer = setInterval(() => { this.showTime(); }, 60 * 1000); } }, showTime() { let times; if (this.time) { times = this.time.split(":"); } else { const now = new Date(); times = [now.getHours(), now.getMinutes()]; } let hour = +times[0]; hour = hour > 11 ? hour - 12 : hour; let minute = +times[1]; let hourAngle = hour * 30 + minute * 6 / 360 * 30; let minuteAngle = minute * 6; this.hourRotate = rotatez(${hourAngle}deg); this.minuteRotate = rotatez(${minuteAngle}deg); } }, mounted() { this.show(); }, destroyed() { if (this._timer) clearInterval(this._timer); }};还有一些钟表的布局样式,可以直接在项目里查看。vue-clock.vue接着我们需要抛出组件,以便在项目中引入使用。 // src/index.js import Clock from ‘./vue-clock.vue’; export default Clock; if (typeof window !== ‘undefined’ && window.Vue) { window.Vue.component(‘clock’, Clock); }这里,组件开发的部分已经完成了,喝杯咖啡,check一下代码,我们要把它打包发布到npm上。打包发布打包前确认一下 webpack 的配置文件输出。 output: { path: path.resolve(__dirname, ‘./dist’), publicPath: ‘/dist/’, filename: ‘vue-clock.min.js’, library: ‘Clock’, libraryTarget: ‘umd’, umdNamedDefine: true }打包组件文件到 dist 文件夹中。npm run buildnpm发布配置package.json{ “name”: “vue-clock2”, “description”: “Vue component with clock”, “version”: “1.1.2”, “author”: “bestvist”, “keywords”: [ “vue”, “component”, “clock”, “time” ], “main”: “dist/vue-clock.min.js”, “license”: “MIT”, “homepage”: “https://bestvist.github.io/vue-clock2/"}登录npm如果使用淘宝镜像的,需要先修正一下镜像源。npm config set registry https://registry.npmjs.org/// 查看登录人npm whoami// 登录npm login// 发布npm publish如果看到类似信息,说明发布成功。npm notice+ vue-clock2@1.1.2Github主页把项目上传到github托管,配置一份基本 README.md 说明文档。因为组件已经发布到npm上,所以可以配置几个徽章在README中。// npm 版本npm version// npm 下载量npm download更多的徽章配置可以查看shields接着描述一下组件的引入和使用方法:安装:npm install vue-clock2使用:<template> <clock :time=“time”></clock></template><script> import Clock from ‘vue-clock2’; export default { components: { Clock }, data () { return { time: ‘10:40’ } } }</script>更详细的交互或是属性说明就交给文档来解决了。在 github 项目上通过 settings 指定 GitHub Pages组件文档说明应包括:组件引入方法组件使用方法一个简单的例子组件属性描述说明总结开发 -> 发布 -> 托管一个组件轮子的制作流程大致介绍完了,希望本文可以帮助到您。原文链接 ...

October 16, 2018 · 2 min · jiezi

vue配置开发,测试,生产环境api

前言:想实现通过不同的命令,打包调用不同环境的API,实现实现前端自动化部署。前端自动化部署工程比较复杂,这里只介绍通过不同的命令,打包的时候调用不同环境的API,例如:npm run build 调用开发环境接口,打包开发环境npm run build:test 调用测试环境接口,打包测试环境npm run build:prod 调用生产环境接口,打包生产环境vue项目用vue-cli脚手架安装完成之后,生成的项目中会有build,config这两个文件夹1、在build文件下新建webpack.test.conf.js在build文件下新建webpack.test.conf.js,将webpack.prod.conf.js内容复制过来。修改webpack.test.conf.js文件将const env = require(’../config/prod.env’);修改为:const env = require(’../config/test.env’);2、在config文件下新建test.env.js在config文件下新建test.env.js,将prod.env.js内容复制过来;分别在dev.env.js,test.env.js,prod.env.js中定义变量API_ROOT,dev.env.jstest.env.jsprod.env.js3、在build文件下新建test.js把build.js 内容复制到test.js将const webpackConfig = require(’./webpack.prod.conf’)修改为:const webpackConfig = require(’./webpack.test.conf’)4、修改package.json配置axios请求的时候,接口地址直接调用 process.env.API_ROOT 就好了,打包的时候执行 npm run build:test就是调用的测试接口地址

October 13, 2018 · 1 min · jiezi

开发一个 Parcel-vue 脚手架工具

前言像我们熟悉的 vue-cli,create-react-app 等脚手架,只需要输入简单的命令 vue init webpack project,即可快速帮我们生成一个初始项目。在实际工作中,我们可以定制一个属于自己的脚手架,来提高自己的工作效率。为什么需要需要脚手架?减少重复性的工作,不再需要复制其他项目再删除无关代码,或者从零创建一个项目和文件。根据交互动态生成项目结构和配置文件等。多人协作更为方便,不需要把文件传来传去。思路要开发脚手架,首先要理清思路,脚手架是如何工作的?我们可以借鉴 vue-cli 的基本思路。vue-cli 是将项目模板放在 git 上,运行的时候再根据用户交互下载不同的模板,经过模板引擎渲染出来,生成项目。这样将模板和脚手架分离,就可以各自维护,即使模板有变动,只需要上传最新的模板即可,而不需要用户去更新脚手架就可以生成最新的项目。那么就可以按照这个思路来进行开发了。第三方库首先来看看会用到哪些库。commander.js,可以自动的解析命令和参数,用于处理用户输入的命令。download-git-repo,下载并提取 git 仓库,用于下载项目模板。Inquirer.js,通用的命令行用户界面集合,用于和用户进行交互。handlebars.js,模板引擎,将用户提交的信息动态填充到文件中。ora,下载过程久的话,可以用于显示下载中的动画效果。chalk,可以给终端的字体加上颜色。log-symbols,可以在终端上显示出 √ 或 × 等的图标。初始化项目首先创建一个空项目,然后新建一个 index.js 文件,再执行 npm init 生成一个 package.json 文件。最后安装上面需要用到的依赖。npm install commander download-git-repo inquirer handlebars ora chalk log-symbols -S处理命令行node.js 内置了对命令行操作的支持,在 package.json 中的 bin 字段可以定义命令名和关联的执行文件。所以现在 package.json 中加上 bin 的内容:{ “name”: “suporka-parcel-vue”, “version”: “1.0.0”, “description”: “a vue cli which use parcel to package object”, “bin”: { “suporka-parcel-vue”: “index.js” }, …}然后在 index.js 中来定义 init 命令:#!/usr/bin/env nodeconst program = require(‘commander’);program.version(‘1.0.0’, ‘-v, –version’) .command(‘init <name>’) .action((name) => { console.log(name); });program.parse(process.argv);调用 version(‘1.0.0’, ‘-v, –version’) 会将 -v 和 –version 添加到命令中,可以通过这些选项打印出版本号。调用 command(‘init <name>’) 定义 init 命令,name 则是必传的参数,为项目名。action() 则是执行 init 命令会发生的行为,要生成项目的过程就是在这里面执行的,这里暂时只打印出 name。其实到这里,已经可以执行 init 命令了。我们来测试一下,在同级目录下执行:node index.js init HelloWorld可以看到命令行工具也打印出了 HelloWorld,那么很清楚, action((name) => {}) 这里的参数 name,就是我们执行 init 命令时输入的项目名称。命令已经完成,接下来就要下载模板生成项目结构了。下载模板download-git-repo 支持从 Github、Gitlab 和 Bitbucket 下载仓库,各自的具体用法可以参考官方文档。命令行交互命令行交互功能可以在用户执行 init 命令后,向用户提出问题,接收用户的输入并作出相应的处理。这里使用 inquirer.js 来实现。const inquirer = require(‘inquirer’);inquirer.prompt([ { name: ‘description’, message: ‘Input the object description’ }, { name: ‘author’, message: ‘Input the object author’ } ]).then((answers) => { console.log(answers.author);})通过这里例子可以看出,问题就放在 prompt() 中,问题的类型为 input 就是输入类型,name 就是作为答案对象中的 key,message 就是问题了,用户输入的答案就在 answers 中,使用起来就是这么简单。更多的参数设置可以参考官方文档。通过命令行交互,获得用户的输入,从而可以把答案渲染到模板中。渲染模板这里用 handlebars 的语法对模板中的 package.json 文件做一些修改{ “name”: “{{name}}”, “version”: “1.0.0”, “description”: “{{description}}”, “scripts”: { “test”: “echo "Error: no test specified" && exit 1” }, “author”: “{{author}}”, “license”: “ISC”}并在下载模板完成之后将用户输入的答案渲染到 package.json 中视觉美化在用户输入答案之后,开始下载模板,这时候使用 ora 来提示用户正在下载中。const ora = require(‘ora’);// 开始下载const spinner = ora(‘正在下载模板…’);spinner.start();// 下载失败调用spinner.fail();// 下载成功调用spinner.succeed();然后通过 chalk 来为打印信息加上样式,比如成功信息为绿色,失败信息为红色,这样子会让用户更加容易分辨,同时也让终端的显示更加的好看。const chalk = require(‘chalk’);console.log(chalk.green(‘项目创建成功’));console.log(chalk.red(‘项目创建失败’));除了给打印信息加上颜色之外,还可以使用 log-symbols 在信息前面加上 √ 或 × 等的图标const chalk = require(‘chalk’);const symbols = require(’log-symbols’);console.log(symbols.success, chalk.green(‘项目创建成功’));console.log(symbols.error, chalk.red(‘项目创建失败’));完整示例// index.js#!/usr/bin/env node// 处理用户输入的命令const program = require(‘commander’);// 下载模板const download = require(‘download-git-repo’);// 问题交互const inquirer = require(‘inquirer’);// node 文件模块const fs = require(‘fs’);// 填充信息至文件const handlebars = require(‘handlebars’);// 动画效果const ora = require(‘ora’);// 字体加颜色const chalk = require(‘chalk’);// 显示提示图标const symbols = require(’log-symbols’);// 命令行操作var shell = require(“shelljs”);program.version(‘1.0.1’, ‘-v, –version’) .command(‘init <name>’) .action((name) => { if (!fs.existsSync(name)) { inquirer.prompt([ { name: ‘description’, message: ‘Input the object description’ }, { name: ‘author’, message: ‘Input the object author’ } ]).then((answers) => { const spinner = ora(‘Downloading…’); spinner.start(); download(‘zxpsuper/suporka-parcel-vue’, name, (err) => { if (err) { spinner.fail(); console.log(symbols.error, chalk.red(err)); } else { spinner.succeed(); const fileName = ${name}/package.json; const meta = { name, description: answers.description, author: answers.author } if (fs.existsSync(fileName)) { const content = fs.readFileSync(fileName).toString(); const result = handlebars.compile(content)(meta); fs.writeFileSync(fileName, result); } console.log(symbols.success, chalk.green(‘The vue object has downloaded successfully!’)); inquirer.prompt([ { type: ‘confirm’, name: ‘ifInstall’, message: ‘Are you want to install dependence now?’, default: true } ]).then((answers) => { if (answers.ifInstall) { inquirer.prompt([ { type: ’list’, name: ‘installWay’, message: ‘Choose the tool to install’, choices: [ ’npm’, ‘cnpm’ ] } ]).then(ans => { if (ans.installWay === ’npm’) { let spinner = ora(‘Installing…’); spinner.start(); // 命令行操作安装依赖 shell.exec(“cd " + name + " && npm i”, function (err, stdout, stderr) { if (err) { spinner.fail(); console.log(symbols.error, chalk.red(err)); } else { spinner.succeed(); console.log(symbols.success, chalk.green(‘The object has installed dependence successfully!’)); } }); } else { let spinner = ora(‘Installing…’); spinner.start(); shell.exec(“cd " + name + " && cnpm i”, function (err, stdout, stderr) { if (err) { spinner.fail(); console.log(symbols.error, chalk.red(err)); } else { spinner.succeed(); console.log(symbols.success, chalk.green(‘The object has installed dependence successfully!’)); } }) } }) } else { console.log(symbols.success, chalk.green(‘You should install the dependence by yourself!’)); } }) } }) }) } else { // 错误提示项目已存在,避免覆盖原有项目 console.log(symbols.error, chalk.red(‘The object has exist’)); } });program.parse(process.argv);npm publish发布你的项目即可。本地测试node index init parcel-vue以上是我写的一个 suporka-parcel-vue 的脚手架源码,suporka-parcel-vue 点击即可查看,欢迎star. ...

September 24, 2018 · 3 min · jiezi