博客图片失效使用npm包20行代码一次下载替换所有失效的外链图片

前言大约一个月前,微博的图片外链失效了,以及掘金因为盗链问题也于2019/06/06决定开启防盗链,造成的影响是:个人博客网站的引用了这些图片外链都不能显示。 目前微博和掘金的屏蔽,在CSDN和segmentfault都是可以正常显示的,只影响个人博客。 比如前段时间我的博客:http://obkoro1.com上引用的微博图片都不能显示了。 因为我写博客比较频繁,被屏蔽的图片不在少数,肯定不能一个个手动的替换,查了一番没有找到现成的解决方案,做了个脚本工具,并且写了文档把它开源出来了。 markdown-img-down-site-change(下载/替换markdown中的图片)搜索目标文件夹中的markdown文件,找到目标图片,提供下载图片,替换图片链接的功能-通常用于markdown 图片失效。简介这是一个极为轻量的脚本,引用包,设置好参数,通过API即可轻松上手。 解决什么问题?集中下载markdown文件中某个域名下的图片到一个文件夹下。用新的图片链接替换markdown文件中某个域名的图片链接。// 1. 下载这两个图片// ![](https://user-gold-cdn.xitu.io/2019/5/20/图片名字?w=2024&h=1240&f=png&s=339262)// ![](https://user-gold-cdn.xitu.io/2018/6/16/图片名字)// 2. 替换成:github的链接![](https://raw.githubusercontent.com/OBKoro1/articleImg_src/master/juejin/图片名字?w=2024&h=1240&f=png&s=339262)![](https://raw.githubusercontent.com/OBKoro1/articleImg_src/master/juejin/图片名字)安装:npm i markdown-img-down-site-change -S文档:Github API 更新日志 数据安全:刚上手可能不了解脚本的功能,需要调试一番,这时候万一把markdown文件给改坏了,岂不是要哭死? 脚本有两种形式来防止这种情况发生: 脚本会默认备份你的文件。默认开启测试模式,等到调试的差不多了,可以关闭测试模式。建议:再不放心的话,可以先用一两个文件来测试一下脚本使用:20行代码不到在项目中有一个使用栗子,里面加了蛮多注释和空行的,实际代码20行都不到,可以说很简单了,如下: // npm i markdown-img-down-site-change -S const markdownImageDown = require('markdown-img-down-site-change'); // 文件模块// 传参: 这也是脚本的默认参数,根据情况可以自行修改let option = { replace_image_url: 'https://user-gold-cdn.xitu.io/', read_markdown_src: './source', // 要查找markdown文件的文件夹地址 down_img_src: './juejin', // 下载图片到这个文件夹 var_number: 3 // url前半部分的变量数量 比如上面的日期: /2019/5/20/、/2018/6/16/}// 初始化const markdownImage = new markdownImageDown(option)// 下载外链markdownImage.checkDownImg();// 上传下载下来的图片文件夹到云端 用户自己操作// 上传图片之后 // 脚本会把以前的外链替换成云端地址+拼接一个图片名markdownImage.updateOption({ new_image_url: 'https://xxx.com/目录地址/', // 图片上传的地址 add_end: '?raw=true' // github图片地址有后缀 直接进去是仓库})// 替换外链 // 把replace_image_url的字符串换成new_image_url字符串markdownImage.replaceMarkdown();运行:仔细阅读文本,配置好参数之后 ...

June 5, 2019 · 1 min · jiezi

我是怎样用函数式JavaScript计算数组平均值的

译者按: 有时候一个算法的直观、简洁、高效是需要作出取舍的。 原文: FUNCTIONAL JAVASCRIPT: FIVE WAYS TO CALCULATE AN AVERAGE WITH ARRAY REDUCE译者: Fundebug本文采用意译,版权归原作者所有 函数式编程中用于操作数组的方法就像“毒品”一样,它让很多人爱上函数式编程。因为它们真的十分常用而且又超级简单。 .map() 和 .filter()都仅需一个参数,该参数定义操作数组每一个元素的函数即可。reduce()会复杂一些,我之前写过一篇文章介绍为什么人们难以掌握reduce()方法,其中一个原因在于很多入门资料都仅仅用算术作为例子。我写了很多用reduce()来做算术以外的例子。 用reduce()来计算数组的平均值是一个常用的模式。代码看起来非常简单,不过在计算最终结果之前你需要做两个准备工作: 数组的长度数组所有元素之和这两个事情看起来都很简单,那么计算数组的平均值并不是很难了吧。解法如下: function average(nums) { return nums.reduce((a, b) => a + b) / nums.length;}确实不是很难,是吧?但是如果数据结构变得复杂了,就没那么简单了。比如,数组里面的元素是对象,你需要先过滤掉某些对象,然后从对象中取出数字。这样的场景让计算平均值变得复杂了一点。 接下来我们处理一个类似的问题(从this Free Code Camp challenge获得灵感),我们会提供 5 种不同的解法,每一种方法有各自的优点和缺点。这 5 种方法也展示了 JavaScript 的灵活。我希望可以给你在使用reduce的实战中一些灵感。 问题提出假设我们有一个数组,记录了维多利亚时代常用的口语。接下来我们要找出那些依然现存于 Google Books 中的词汇,并计算他们的平均流行度。数据的格式是这样的: const victorianSlang = [ { term: "doing the bear", found: true, popularity: 108 }, { term: "katterzem", found: false, popularity: null }, { term: "bone shaker", found: true, popularity: 609 }, { term: "smothering a parrot", found: false, popularity: null }, { term: "damfino", found: true, popularity: 232 }, { term: "rain napper", found: false, popularity: null }, { term: "donkey’s breakfast", found: true, popularity: 787 }, { term: "rational costume", found: true, popularity: 513 }, { term: "mind the grease", found: true, popularity: 154 }];接下来我们用 5 中不同的方法计算平均流行度值。 ...

June 5, 2019 · 3 min · jiezi

Https在各种Web服务器下配置

前言前端很多情况需要用启动web服务器,而为了保证数据的安全性,都需要用Https对传输的数据进行加密传输,而且有些web-view只允许https通过访问,所以学习怎么配置https也成为大前端不可以少的功课之一。下面本妹子将先简单介绍下 Https,再依次介绍怎么在Node、webpack-dev-server和nginx这三个最常见的前端web服务器下配置Https,以及关于证书的扩展干货。 Https简介Https协议的主要作用可以分为两点:1.建立一个信息安全通道,来保证数据传输的安全;2.确认网站的真实性;而https的加密方式主要是非对称加密,所以会有一对密钥,分别是一个公钥(证书)和私钥。 浏览器allen访问的时候,客户端会接受这个证书,并利用这个证书里的公钥对数据加密,加密后,服务端tony用自己的私钥解密就行了。 Web服务一般使用OpenSSL工具提供的密码库,生成PEM、KEY、CRT等格式的证书文件。其中.crt后缀一般存放证书,.key后缀一般存放私钥,.pem后缀的可以同时存放把CA证书和密钥。 生成证书和密钥可以直接用命令生成 #使用以下命令生成一个证书密钥对 key.pem 和 cert.pem,它将有效期约10年(准确地说是3650天)openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.key -out cert.crt#或者直接生成到一个pem文件中openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout all.pem -out all.pem可以用selfsigned插件生成 const selfsigned = require('selfsigned');var selfsigned = require('selfsigned');var attrs = [{ name: 'commonName', value: 'contoso.com' }];//有效期约10年var pems = selfsigned.generate(attrs, { days: 3650 });fs.writeFileSync('all.pem', pems.private + pems.cert, { encoding: 'utf-8' });node配置https用openssl生成all.pem openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout all.pem -out all.pem在同目录编写app.js,并运行node app.js,启动node服务监听88端口 ...

June 5, 2019 · 2 min · jiezi

QQ音乐API-koa2实现-全接口实现

QQMusicAPIQQ音乐API koa2 版本, 通过Web网页版请求QQ音乐接口数据, 有问题请提 issue, 或者你有其他想法欢迎PR.Github 知乎 掘金 环境要求因为本项目采用的是koa2, 所以请确保你的node版本是7.6.0+node -v安装git@github.com:Rain120/qq-music-api.gitnpm install项目启动// npm i -g nodemonnpm run start// or don't install nodemonnode app.js项目监听端口是3200 使用文档使用apis详见文档 关于本人Rain120: 前端菜鸟, 入职前端1年, 公司的技术栈是React, 因为公司官网由我重构过, 我使用的Vue.js重构的。目前正在脱坑, 求大佬内推呀API结构图 API接口koa接口说明(参数, 地址, 效果图)获取QQ音乐产品的下载地址接口说明: 调用此接口, 可获取QQ音乐标准产品下载链接 接口地址: /downloadQQMusic 调用例子: /downloadQQMusic 示例截图: 获取歌单分类接口说明: 调用此接口, 可获取歌单分类, 包含category信息 接口地址: /getSongListCategories 调用例子: /getSongListCategories <details> <summary>SortID</summary> sortId: 1, sortName: 默认sortId: 2, sortName: 最新sortId: 3, sortName: 最热sortId: 4, sortName: 评分sortId: 5, sortName: none</details> ...

June 4, 2019 · 4 min · jiezi

express-jwt-postMan验证-实现持久化登录

原理第一次登陆时会返回一个经过加密的token,下一次访问接口(携带登录返回你的token)的时候,会对token进行解密,如果解密正在进行,说明你已经登录,再把过期时间延长下载npm init -y // 一键初始化npm install express -s // 下载expressnpm install cors // 跨域中间件npm install body-parser // body-parser中间件 解析带请求体的数据(post,put)npm install jsonwebtoken // 持久化登录 jwt json web token基本配置// 引入expresslet express = require('express')let cors = require('cors')let bodyParser = require('body-parser')let jwt = require("jsonwebtoken")let banner = require("./banner")// 拿到服务器let app = express()app.use(cors())app.use(bodyParser.json())app.use(bodyParser.urlencoded({extended:false}))// listen 后面跟着的是端口app.listen(8000,function(){ console.log('OK')})模拟一个登陆的接口app.post('/login',function(req,res){ let {username} = req.body console.log(username) res.json({ // 进行加密的方法 // sing 参数一:加密的对象 参数二:加密的规则 参数三:对象 token:jwt.sign({username:username},'abcd',{ // 过期时间 expiresIn:"1h" }), username, code:200 })})postMan模拟 发送POST请求 ...

June 4, 2019 · 1 min · jiezi

前端小册-结合源码彻底理解-react-事件机制

前言写这个文章也算是实现19年的一个 flag,研究一个知识点并且把自己的理解整理成文分享出来。也正好在公司内部进行了一次分享,趁还算热乎赶紧的整理下来。 适合读者对 react 技术有一定的理解和实际项目应用对 react 技术栈有兴趣的同学 主要内容本小册一共有4节,其实可以合成一篇文章,只是想把文章进行划分一下,稍微结构化一些。 01 - 对事件机制的初步理解和验证 02 - 对于合成的理解 03 - 事件注册机制 04 - 事件执行 ps:本文基于 react15.6.1进行分析,虽然不是最新版本但是也不会影响我们对 react 事件机制的整体把握和理解。 最后希望通过本文可以让你对 react 事件机制有更清晰的认识和理解。文中可能会存在一些表述不清或者理解不够标准的地方,还请斧正。 更多精彩内容欢迎关注我的公众号 - 前端张大胖

June 4, 2019 · 1 min · jiezi

结合源码彻底理解-react事件机制原理-01-对事件机制的初步理解和验证

前言这是 react 事件机制的第一篇,主要内容有:表象理解,验证,意义和思考。 表象理解先回顾下 对react 事件机制基本理解,react 自身实现了一套自己的事件机制,包括事件注册、事件的合成、事件冒泡、事件派发等,虽然和原生的是两码事,但也是基于浏览器的事件机制下完成的。 我们都知道react 的所有事件并没有绑定到具体的 dom节点上而是绑定在了document 上,然后由统一的事件处理程序来处理,同时也是基于浏览器的事件机制(冒泡),所有节点的事件都会在 document 上触发。 上面是基于对 react 事件的一个基本的认知,那这个认知是否正确呢?我们可以通过简单的方法进行验证。 验证验证内容: 所有事件均注册到了元素的最顶层-document 上节点的事件由统一的入口处理为了方便,直接通过 cli 创建一个项目。 代码如下: componentDidMount(){ document.getElementById('btn-reactandnative').addEventListener('click', (e) => { console.log('原生+react 事件: 原生事件执行'); }); } handleNativeAndReact = (e) => { console.log('原生+react 事件: 当前执行react事件'); } handleClick=(e)=>{ console.log('button click'); } render(){ return <div className="pageIndex"><p>react event!!!</p <button id="btn-confirm" onClick={this.handleClick}>react 事件</button> <button id="btn-reactandnative" onClick={this.handleNativeAndReact}>原生 + react 事件</button> </div> }代码中给两个 button绑定了合成事件,单独给btn#btn-reactandnative绑定了一个原生的事件。 然后看下chrome 的控制台,查看元素上的注册事件。 经过简单的验证,可以看到所有的事件根据不同的事件类型都绑定在了 document 上。触发函数统一是 dispatchEvent。 ...

June 4, 2019 · 1 min · jiezi

结合源码彻底理解-react事件机制原理-02-对于合成的理解

前言这是react事件机制系列文章的第二篇-对于合成的理解,咱们就来说说合成这个名词。 刚听说合成这个词时候,感觉是特别高大上,很有深度,不是很好理解。 当我大概的了解过react事件机制后,略微了解一些皮毛,我觉得合成不单单是事件的合成和处理,从广义上来说还包括: 1.首先就是对原生事件的封装 2.对某些原生事件的升级和改造 3.不同浏览器事件兼容的处理 1. 对原生事件的封装先看一段再熟悉不过的代码 上面代码是给一个元素添加click事件的回调方法 方法中的参数e,其实不是原生事件对象而是react包装过的对象,同时原生事件对象被放在了属性 e.nativeEvent 内。 通过调试,在执行栈里看下这个参数e包含哪些属性 ![clipboard.pvhR) ![图片上传中...] 看下官方文档,帮助我们理解下合成事件 SyntheticEvent是react合成事件的基类,定义了合成事件的基础公共属性和方法。 react会根据当前的事件类型来使用不同的合成事件对象,比如鼠标单机事件 - SyntheticMouseEvent,焦点事件-SyntheticFocusEvent等,但是都是继承自SyntheticEvent。 2.对原生事件的升级和改造对于有些dom元素事件,我们进行事件绑定之后,react并不是只处理你声明的事件类型,还会额外的增加一些其他的事件,帮助我们提升交互的体验。 这里就举一个例子来说明下: 当我们给input声明个onChange事件,看下 react帮我们做了什么? 可以看到react不只是注册了一个onchange事件,还注册了很多其他事件。 而这个时候我们向文本框输入内容的时候,是可以实时的得到内容的。 然而原生只注册一个onchange的话,需要在失去焦点的时候才能触发这个事件。 所以这个原生事件的缺陷react也帮我们弥补了。 ps:上面红色箭头中有一个 invalid事件,这个并没有注册到document上,而是在具体的元素上。我的理解是这个是html5新增的一个事件,当输入的数据不符合验证规则的时候自动触发,然而验证规则和配置都要写在当前input元素上,如果注册到document上这个事件就无效了。 3.浏览器事件的兼容处理这个也算是一个点吧。react在给document注册事件的时候也是对兼容性做了处理。 看下代码 上面这个代码其实就是给document注册事件,内部其实也是做了对ie浏览器的兼容。 总结:以上就是我对于react合成这个名词的理解,其实react内部还处理了很多,我只是略微简单的举了几个栗子。 本文算是一个过度吧,后面开始聊事件注册和事件派发的机制。走起~ 更多精彩内容欢迎关注我的公众号-前端张大胖

June 4, 2019 · 1 min · jiezi

结合源码彻底理解-react事件机制原理-03-事件注册

前言这是 react 事件机制的第三节 - 事件注册,通过本文你将了解react 事件的注册过程,以及在这个过程中主要经过了哪些关键步骤,同时结合源码进行验证和增强理解。 文章涉及到的源码是基于 react15.6.1版本,虽然不是最新版本但是也不会影响我们对 react 事件机制的整体把握和理解。 文中不会说非常细节的内容,而是会把大概的流程和原理性的内容进行介绍,做到对整体流程有个认知和理解。 内容大纲 主要做两件事 (事件注册、事件存储)大致流程具体执行过程总结1. 主要做两件事按照我的理解,react 事件注册过程其实主要做了2件事: a. 事件注册 b. 事件存储 a. 事件注册 - 组件挂载阶段,根据组件内的声明的事件类型-onclick,onchange 等,给 document 上添加事件 -addEventListener,并指定统一的事件处理程序 dispatchEvent。 b. 事件存储 - 就是把 react 组件内的所有事件统一的存放到一个地方,也就是缓存起来,可以理解成放入一个对象内,为了在触发事件的时候可以查找到对应的方法去执行。 再配个图 2. 大致流程上面大致说了事件注册需要完成的两个目标,那完成目标的过程需要经过哪些关键处理呢? 首先 react 拿到将要挂载的组件的虚拟 dom(其实就是 react dom, 类似一个对象),然后处理react dom 的 props ,判断属性内是否有声明为事件的属性,比如onclick,这个时候得到事件类型 click 和对应的事件处理程序 fn,然后直行后面3步 a. 执行事件注册 b. 将react dom ,事件类型,处理函数 fn 放入数组存储 c. 组件挂载完成后,处理 b 步骤生成的数组,经过遍历把事件处理函数存储到listenerBank中 ...

June 4, 2019 · 2 min · jiezi

结合源码彻底理解-react事件机制原理-04-事件执行

前言这是 react 事件机制的第四节-事件执行,一起研究下在这个过程中主要经过了哪些关键步骤,本文也是react 事件机制的完结篇,希望本文可以让你对 react 事件执行的原理有一定的理解。 文章涉及到的源码是基于 react15.6.1版本,虽然不是最新版本但是也不会影响我们对 react 事件机制的整体把握和理解。 回顾先简单的回顾下上一文,事件注册的结果是是把所有的事件回调保存到了一个对象中 那么在事件触发的过程中上面这个对象有什么用处呢? 其实就是用来查找事件回调。 内容大纲按照我的理解,事件触发过程总结为主要下面几个步骤 1.进入统一的事件分发函数(dispatchEvent) 2.结合原生事件找到当前节点对应的ReactDOMComponent对象 3.进行事件的合成 3.1根据当前事件类型生成指定的合成对象 3.2封装原生事件和冒泡机制 3.3查找当前节点以及他的所有父级 3.4在listenerBank查找事件回调并合成到 event(合成事件结束) 4.批量处理合成事件内的回调事件(事件触发完成 end) 说再多不如配个图 举个栗子在说具体的流程前,先看一个栗子,后面的分析也是基于这个栗子 handleFatherClick=(e)=>{ console.log('father click'); } handleChildClick=(e)=>{ console.log('child click'); } render(){ return <div className="box"> <div className="father" onClick={this.handleFatherClick}> father <div className="child" onClick={this.handleChildClick}>child </div> </div> </div> } 看到这个熟悉的代码,我们就已经知道了执行结果。 当我点击 child div 的时候,会同时触发father的事件。 1、进入统一的事件分发函数 (dispatchEvent)当我点击child div 的时候,这个时候浏览器会捕获到这个事件,然后经过冒泡,事件被冒泡到 document 上,交给统一事件处理函数 dispatchEvent 进行处理。(上一文中我们已经说过 document 上已经注册了一个统一的事件处理函数 dispatchEvent) ...

June 4, 2019 · 4 min · jiezi

Nodejs-路由

Node.js 路由 我们平时工作中,涉及到后台开发,路由基本上是我们第一个需要建的,路由还是很重要的。 那么,什么是路由呢,通俗点举个例子,一个宾馆前台,来了十位客人,前台会安排十位客人入住,每位客人到达宾馆以后,该去哪个房间,都是通过前台来安排。(别喷我) 在一个域名下,会有很多个可访问的地址,这就是路由。 我们呢,要为路由提供请求的URL和其他需要的GET及POST参数,随后路由需要根据这些数据,来决定执行哪些代码。/因此,我们要查看HTTP请求,从中提取出来我们需要的URL以及GET/POST参数。我们需要的这些数据都会包含在request对象中,该对象作为onRequest()回调函数的第一个参数传递。但是为了解析这些数据,我们需要额外的Node.js模块,它们分别是url和querystring模块。 url.parse(string).query | url.parse(string).pathname | | | | | ------ -------------------http://localhost:8888/start?foo=bar&hello=world --- ----- | | | | querystring.parse(queryString)["foo"] | | querystring.parse(queryString)["hello"]也可以用querystring模块来解析post请求体中的参数,下面会有代码演示。 现在我们写一段代码,用来找出浏览器请求的URL路径 之前也写到如何用node起serve 我们新建一个server.js 代码如下 // 代码route()方法为第二个创建的router.js那的方法。我们在这里使用const http = require('http')const url = require('url')function start(route){ function onRequest(request, response) { let pathName = url.parse(request.url).pathname // 通过url获取到当前访问路径 console.log('Request for ' + pathName + 'received.') route(pathName,response) } http.createServer(onRequest).listen(8888) console.log('Server has started')}exports.start = start然后创建router.js // 通过传递过来到pathname,来进行不同的操作,如果是根目录,打印hello world// 如果是/index 打印 pathname :/index// 如果是其他 打印404function route(pathname,response) { console.log('About to route a request for ' + pathname) response.writeHead(200, {'Content-Type' : 'text/plain'}) if(pathname == '/') { response.write('Hello World') response.end() }else if(pathname == '/index'){ response.write('pathname :/index') response.end() } else { response.write('404') response.end() } }exports.route = route真实环境肯定不会这么写,这样写主要是理解路由的工作原理 ...

June 4, 2019 · 1 min · jiezi

使用API-Blueprint语法来编写API文档

1、资源Resource# Gist Fox API Root [/]在Blueprint里,所有的数据信息都是资源(resource),比如用户、视频、文章。resource的定义以#开始,中间是resource的名称,最后是用中括号包围的路径(URI),需要注意的是URI是放在[]中的。URI是相对路径,在这里它就是个/。2、资源描述Resource Description# Gist Fox API Root [/]Gist Fox API entry point.This resource does not have any attributes. Instead it offers the initial API我们可以用Markdown的语法在resource名称的后面加上包含API名称的说明。在这里Gist Fox API是API名称,entry point是说明。3、行为Action## Retrieve Entry Point [GET]行为action是一个HTTP请求的属性之一,在发送请求的时候会随数据一起发送到服务器。我们在这里定义了一个action叫做Retrieve Entry Point (索引入口),它是一个GET类型的请求。我们可以在后面加上一些描述,但是因为这个action的名字(Retrieve Entry Point)已经把这个行为解释的很清楚了,所以我们就跳过了这一步。4、在Blueprint有以下四种action:- GET : 获取数据- POST : 添加数据- PUT : 更新数据- DELETE : 删除数据5、回应Response+ Response 200在API Blueprint中一个action应该至少包括一个回应(response)。response是指当服务器收到一个请求(request)时候的响应。在response中应该有一个状态码status code和数据payload。在这里我们定义最常见的状态码:200,表示请求成功。6、响应负载Response Payload+ Response 200 (application/hal+json)+ HeadersLink: <http:/api.gistfox.com/>;rel="self",<http:/api.gistfox.com/gists>;rel="gists"+ Body{ "_links": { "self": { "href": "/" }, "gists": { "href": "/gists?{since}", "templated": true } }}一个响应(response)经常包含一些负载(payload)。一个负载(payload)通常包含负载体(body)和负载头(header)两个部分。在这个例子中,我们采用application/hal+json类型作为返回数据的类型。7、URI模板URI Template## Gist [/gists/{id}]在URI中的变量需要遵守URI的模板格式,在这个例子中,Gist的编号(id)在URI中就是{id}。8、URI参数URI Parameters+ Parameters+ id (string) ... ID of the Gist in the form of a hash.这个id变量是这个resource中的一个参数(parameter),我们定义它的类型为string,并且在后面加上一些解释。9、资源模型Resource Model+ Model (application/hal+json)HAL+JSON representation of Gist Resource. In addition to representing its state in the JSON form it offers affordances in the form of the HTTP Link header and HAL links.+ HeadersLink: <http:/api.gistfox.com/gists/42>;rel="self", <http:/api.gistfox.com/gists/42/star>;rel="star"+ Body{ "_links": { "self": { "href": "/gists/42" }, "star": { "href": "/gists/42/star" }, }, "id": "42", "created_at": "2014-04-14T02:15:15Z", "description": "Description of Gist", "content": "String contents"}资源模型Resource Model是前面定义的资源的一个样例,它可以在任何一个request或者response需要的位置引用,一个资源模型有着和前面所说的payload一模一样的结构。在前面的例子中,还包含了一个额外的描述,也就是在+ Model和+ Headers中间的那部分内容。ps:下文将介绍aglio生成高大上的api文档 ...

June 4, 2019 · 1 min · jiezi

Nodejs函数

Node.js函数 在JS语言中,一个函数可以作为另一个函数的参数。可以先定义在传递,也可以直接使用匿名函数进行传递。 Node.js中函数的使用与JS类似,基本差不多。 下面写两个例子。 先定义函数,在进行传递 // 定义函数sayfunction say(val){ console.log(val)}// 我们将say函数作为execute第一个参数进行传递,这样以来,say函数就变成了execute中的本地变量someFunction// exectue可以通过调用someFunction()来使用say函数,say函数有一个变量,在调用的时候我们可以传递一个变量。function execute(someFunction, val) { someFunction(val)}execute(say,'思否')这个例子就是先定义函数,然后将定义的函数作为参数给另一个函数使用。 还有一种就是直接使用匿名函数 function execute(somefunction, val) { somefunction(val)}execute(function(val){console.log(val)}, '思否')这种方式就是直接使用匿名函数进行传递,我们在execute接收第一个参数的地方直接定义了我们准备传参的函数。 两种方式都可以,相比较先定义在传递,匿名函数写起来更简洁,如果有一些不需要重复调用的场景,可以使用这种方式。 了解函数传递以后,我们在来看函数传递如何让HTTP服务器工作的。 // 匿名函数传递const http = require('http')http.createServer((request , response) => { response.writeHead(200, {'Content-Type': 'text/plain'}) response.write('Hello World') response.end()}).listen(8888)http.createServer方法中第一个参数我们直接以匿名函数的方式直接书写。 const http = require('http')// 先定义在传递function onRequest(request, response) { response.writeHead(200,{'Content-Type': 'text/plain'}) response.write('Hello sifou.com') response.end()}http.createServer(onRequest).listen(8888)这段代码我们先定义函数onRequest,然后在进行传递。两种方法都可以

June 4, 2019 · 1 min · jiezi

Nodejs模块系统-创建模块与加载模块

Node.js模块系统 为了让Node.js的文件相互调用,Node.js提供了一个简单的模块系统 Node.js应用程序的组成就是由模块组成基本部分,文件和模块是一一对应的。 一个Node.js文件就是一个模块,这个文件可以是js代码,JSON 或者编译过的C/C++扩展。 接下来写一个实例。 创建模块与引入模块很简单,首先我们写一个hello.js文件 代码如下 // 模块的主要逻辑function Hello() { let name this.setName = function(argName) { name = argName } this.sayHello = function() { console.log('Hello' + name) }} module.exports = Hello //导出模块exports 和 module.exports 的使用如果要对外暴露属性或方法,就用 exports 就行,要暴露对象(类似class,包含了很多属性和方法),就用 module.exports。 然后写一个倒入模块的文件 main.js const Hello = require('./hello') // 这里使用require导入刚刚写好的hello.js let hello = new Hello()hello.setName('思否')hello.sayHello()服务端的模块的加载流程 node.js中有原生模块和三种文件模块 ,在使用中只需要require,用起来很简单,但是内部的加载很复杂,其加载优先级也各有不同,入下图所示 按图中所示,不管是哪种模块,加载机制都会优先检查文件是否在模块缓存区中,缓存区的意义在于防止某一个模块被反复加载。缓存区域分为两种,一种是文件模块缓存区,也就是我们说的三种文件类型,还有一种就是原生模块缓存区,例如http fs等原生模块。

June 4, 2019 · 1 min · jiezi

nodejs来爬取智联全国的竞争最激烈的前十岗位

node爬虫什么是爬虫呢,是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。为什么选用node呢,因为我是前端,当然要用js实现。 项目分析爬取http://top.zhaopin.com 智联网站上的全国的竞争最激烈三个月内前十的岗位。不需要定时爬取。使用request和cheerio模块。node版本7.6.0、npm版本4.1.2 安装npm install request cheerio -Srequest 模块是一个简化的HTTP客户端。cheerio 模块专为服务器设计的核心jQuery的快速,灵活和精益的实现。可以把爬到的内容和jQuery一样使用。 核心代码// app.jsconst request = require('request');const cheerio = require('cheerio');// 发起请求request('http://top.zhaopin.com', (error, response, body) => { if(error){ console.error(error); } let json = {}; // 获取到的内容放到cheerio模块 const $ = cheerio.load(body); // jQuery 遍历 #hotJobTop .topList li 是通过http://top.zhaopin.com 分析页面结构得到的 $('#hotJobTop .topList li').each(function (index) { let obj = json[index] = {}; obj.name = $(this).find('.title').text().trim(); obj.num = $(this).find('.paddingR10').text().trim(); }); // 打印数据 console.log(json);});执行 node app.js 就会得到如下结果。 ...

June 4, 2019 · 1 min · jiezi

将函数式进行到底用-Hooks-武装组件-TXD-前端月刊-201905-期

【阿里云 TXD 前端月刊】- 热门前端技术快报,聚焦业界新视界;五月清风徐来,跟我们一起来看一看新的风向即将吹向何方,前端的技术力量又将影响哪些新的领域。 欢迎 订阅 & 投稿编辑:墨止审稿:尹挚 学习专栏 《深入浅出 React Hooks》React 16.8 了,还不了解 React Hooks?!想做前端极客,想写出酷酷的代码,那就赶紧跟 @x-cold 一起来学习最新的 Hooks 知识吧!原文章看这里,一步步进入 React Hooks 的世界。 《一个合格的中级前端工程师必须要掌握的 28 个 JavaScript 技巧》对在毕业季还在找工作的同学,可以说很实用的,如果能全部掌握这些技巧,拿到几个offer应该是没有问题的,当然要注意平时的积累,能力要全面。 《Node.js 技术栈》这是作者从事 Node.js Developer 以来的学习历程,旨在为大家提供一个较详细的学习教程,侧重点更倾向于 Node.js 服务端所涉及的技术栈,如果本文能为您得到帮助,请给予支持! 新闻快报 Github 重磅推出包管理仓库 现在使用 Github,你的团队可以发布公共/私有的包到 Github 提供的包管理仓库,目前包含的类型有 Npm, Docker, Maven, NuGet, RubyGems等,更多的类型还在继续添加中。而且该托管服务是完全免费的。[[戳????传送门]](https://github.com/features/p... Flutter 实现 Web 访问,新增桌面和嵌入式的应用场景2019 谷歌 I/O 大会上,Flutter Team 公布了 Flutter 可进行 Web 访问、提供自定义图像分类模型等诸多新特性,并且可以应用在桌面系统及嵌入式设备中,给未来提供了更多的想象空间,展现了 Flutter 从移动 UI 到制霸多平台的雄心。Flutter for Web 已发布第一个预览版本,可以查看官网信息进行尝鲜,体验惊喜 [[惊喜门]](https://flutter.dev/web)。在 Facebook 年度开发者大会上宣布了会稳定支持 RN,但是 RN 还要继续步履蹒跚吗? ...

June 3, 2019 · 2 min · jiezi

Nodejs-事件循环

Node.js事件循环 Node.js 是单进程单线程应用程序,但是因为V8引擎提供的异步执行回调接口,通过这些接口可以处理大量的并发,所以性能非常高。Node.js 几乎每一个API都支持回调函数。Node.js 基本上所有都事件机制都是通过观察者模式实现Node.js 单线程类似进入一个while(true)事件循环,直到没有事件观察者退出,每个异步事件都生成一个事件观察者,如果有事件发生就调用该回调函数。 事件驱动程序Node.js 使用事件驱动模型,当web server接收到请求,就把它关闭然后进行处理,然后去服务下一个web请求。 当这个请求完成,它被放回处理队列,当到达队列开头,这个结果被返回给用户。 这个模型非常高效可扩展性非常强,因为webserver一直接受请求而不等待任何读写操作。(这也被称之为非阻塞式IO或者事件驱动IO) 在事件驱动模型中,会生成一个主循环来监听事件,当检测到事件时触发回调函数。 //引入eventsconst events = require('events')// 创建eventEmitter对象const eventEmitter = new events.EventEmitter();// 创建时间处理程序 var connectHandler = function connected (){ console.log('连接成功') // 出发data_received事件 eventEmitter.emit('data_received')}// 绑定connection事件处理程序eventEmitter.on('connection', connectHandler)// 使用匿名函数绑定data_received事件eventEmitter.on('data_received',() => { console.log('数据接收成功。')})// 触发connection事件eventEmitter.emit('connection')console.log('程序执行完毕') Node应用程序是如何工作的 ?? 在Node应用程序中,执行异步操作都函数将回调函数作为最后一个参数,回调函数接收错误对象做一个第一个参数。 const fs = require("fs")fs.readFile('input.txt',(err,data) => { if(err) { console.log(err) }else{ console.log(data.toString()) }})console.log("程序执行结束!")这段代码,input.txt文件我给删除了,所以在执行过程中,发生错误,错误err对象就会输出错误信息。

June 3, 2019 · 1 min · jiezi

Nodejs-回调函数-阻塞与非阻塞

Node.js异步变成的直接体现就是回调。异步编程依托于回调来实现,但不能说使用了回调后程序就异步化了。回调函数在完成任务后就会被调用,Node使用了大量的回调函数,Node所有的APi都支持回调函数。比如,我们i 遍读文件,一边执行其他命令,在文件读取完成后,我们将文件内容作为回调函数都参数返回。这样在执行代码时就没有阻塞或等待文件I/O操作。这就大大提高了Node都性能,可以处理大量的并发请求。 接下来写一个阻塞代码实例创建一个文件input.txt,内容如下学习前端知识,记录笔记 ,来segmentfault。 创建main.js 文件,代码如下: const fs = require("fs") //fs模块用于对系统文件及目录进行读写操作。const data = fs.readFileSync('input.txt');console.log(data.toString());console.log("执行程序结束")非阻塞代码实例 const fs = require("fs") //fs模块用于对系统文件及目录进行读写操作。fs.readFile('input.txt',(err, data) => { if(err) { console.log(err) }else{ console.log(data.toString()) }})console.log("程序执行结束!")以上两个实例写出了阻塞与非阻塞的不同。第一个实例在文件读取完后才执行完程序。第二个实例我们不需要等待文件读取完,这样就可以在读取文件时同时执行接下来的代码,大大提高了程序的性能。 因此,阻塞是按顺序执行的,而非阻塞是不需要按顺序的,所以如果需要处理回调函数的参数,我们就需要写在回调函数内。

June 3, 2019 · 1 min · jiezi

gulp40-搭建less编译环境

gulp4.0 搭建less编译环境

June 3, 2019 · 1 min · jiezi

StepByStep一周面试题深入解析-周刊02

关于【Step-By-Step】Step-By-Step (点击进入项目) 是我于 2019-05-20 开始的一个项目,每个工作日发布一道面试题。每个周末我会仔细阅读大家的答案,整理最一份较优答案出来,因本人水平有限,有误的地方,大家及时指正。 如果想 加群 学习,扫码 二维码 (点击查看),添加我为好友,验证信息为加入组织,我拉你进群。 __ 本周面试题一览:节流(throttle)函数的作用是什么?有哪些应用场景,请实现一个节流函数说一说你对JS执行上下文栈和作用域链的理解?什么是BFC?BFC的布局规则是什么?如何创建BFC?let、const、var 的区别有哪些?深拷贝和浅拷贝的区别是什么?如何实现一个深拷贝?6. 节流(throttle)函数的作用是什么?有哪些应用场景,请实现一个节流函数。(2019-05-27)节流函数的作用节流函数的作用是规定一个单位时间,在这个单位时间内最多只能触发一次函数执行,如果这个单位时间内多次触发函数,只能有一次生效。 举例说明:小明的妈妈和小明约定好,如果小明在周考中取得满分,那么当月可以带他去游乐场玩,但是一个月最多只能去一次。 这其实就是一个节流的例子,在一个月的时间内,去游乐场最多只能触发一次。即使这个时间周期内,小明取得多次满分。 节流应用场景1.按钮点击事件 2.拖拽事件 3.onScoll 4.计算鼠标移动的距离(mousemove) 节流函数实现利用时间戳实现function throttle (func, delay) { var lastTime = 0; function throttled() { var context = this; var args = arguments; var nowTime = Date.now(); if(nowTime > lastTime + delay) { func.apply(context, args); lastTime = nowTime; } } //节流函数最终返回的是一个函数 return throttled; }利用定时器实现function throttle(func, delay) { var timeout = null; function throttled() { var context = this; var args = arguments; if(!timeout) { timeout = setTimeout(()=>{ func.apply(context, args); clearTimeout(timeout); timeout=null }, delay); } } return throttled;}时间戳和定时器的方式都没有考虑最后一次执行的问题,比如有个按钮点击事件,设置的间隔时间是1S,在第0.5S,1.8S,2.2S点击,那么只有0.5S和1.8S的两次点击能够触发函数执行,而最后一次的2.2S会被忽略。 ...

June 3, 2019 · 6 min · jiezi

一文读懂NodeJS全栈开发利器CabloyJS万字长文

1 基本概念1.1 CabloyJS是什么1.1.1 定义CabloyJS是一款顶级NodeJS全栈业务开发框架1.1.2 特点CabloyJS是采用NodeJS进行全栈开发的最佳实践CabloyJS不重复造轮子,而是采用业界最新的开源技术,进行全栈开发的最佳组合CabloyJS前端采用VueJS + Framework7 + WebPack,后端采用KoaJS + EggJS,数据库采用MySQLCabloyJS时刻跟踪开源技术的最新成果,并持续优化,使整个框架时刻保持最佳状态1.1.3 理念既可快速开发,又可灵活定制为了实现此理念,CabloyJS内置开发了大量核心模块,使您可以在最短的时间内架构一个完整的Web项目。比如,当您新建一个Web项目时,就已经具备完整的用户登录与认证系统,也具有验证码功能,同时也具备用户管理、角色管理、权限管理等功能 此外,这些内置模块提供了灵活的定制特性,您也可以开发全新的模块来替换内置模块,从而实现系统的定制化 1.2 CabloyJS核心解决什么问题场景碎片化业务模块化1.2.1 场景碎片化1) 先说说Mobile场景我们知道,随着智能机的日益普及,咱们开发人员所面对的需求场景与开发场景日益碎片化,如浏览器、IOS、Android,还有大量第三方平台:微信、企业微信、钉钉、Facebook、Slack等等 随着智能设备性能越来越好,网速越来越快,针对如此众多的开发场景,采用H5开发必将是大势所趋。只需开发一套代码,就可以在以上所有智能设备中运行,不仅可以显著减少开发量,同时也可以显著提升开发效率,对开发团队和终端用户均是莫大的福利 2) 再来谈谈PC场景以上咱们说H5开发,只需开发一套代码,就可以在所有智能设备中运行。但是还有一个开发场景没有得到统一:那就是PC场景 由于屏幕显示尺寸的不同,PC场景和Mobile场景有着不同的操作风格。有些前端UI框架,采用“自适应”策略,为PC场景开发的页面,在Mobile场景下虽然也能查看和使用,但使用体验往往差强人意 这也就是为什么有些前端框架总是成对出现的原因:如Element-UI和Mint-UI,如AntDesign和AntDesign-Mobile这也就意味着,当我们同时面对PC场景和Mobile场景时,仍然需要开发两套代码。在面对许多开发需求时,这些重复的工作量往往是难以接受的: 比如,我们在企业微信或钉钉上开发一些H5业务应用,同时也希望这些应用也可以在PC端浏览器中运行比如,我们为微信公共号开发了一些H5业务应用,同时也希望这些应用也可以在PC端浏览器中运行。同时,还可以在同一架构下开发后台管理类功能,通过区别不同的登录用户、不同的使用场景,从而显示不同的前端页面3) PC = MOBILE + PADCabloyJS前端采用Framework7框架,目前已同步升级到最新版Framework7 V4。CabloyJS在Framework7的基础上进行了巧妙的扩展,将PC端的页面切分为多个区域,实现了多个Mobile和PAD同时呈现在一个PC端的效果。换句话说,你买了一台Mac,就相对于买了多台IPhone和IPad,用多个虚拟的移动设备同时工作,即显著提升了工作效率,也提供了非常有趣的使用体验 4) 实际效果有图有真相 也可PC端体验https://admin.cabloy.com 也可手机扫描体验 5) 如何实现的CabloyJS是模块化的全栈框架,为了实现PC = MOBILE + PAD的风格,内置了两个模块:egg-born-module-a-layoutmobile和egg-born-module-a-layoutpc。当前端框架加载完毕,会自动判断当前页面的宽度(称为breakpoint),如果小于800,使用Mobile布局,如果大于800,使用PC布局,而且breakpoint数值可以自定义 此外,这两个布局模块本身也有许多参数可以自定义,甚至,您也可以开发自己的布局模块,替换掉内置的实现方式 下面分别贴出两个布局模块的默认参数,相信您一看便知他们的用处 egg-born-module-a-layoutmobile export default { layout: { login: '/a/login/login', loginOnStart: true, toolbar: { tabbar: true, labels: true, bottom: true, }, tabs: [ { name: 'Home', tabLinkActive: true, iconMaterial: 'home', url: '/a/base/menu/list' }, { name: 'Atom', tabLinkActive: false, iconMaterial: 'group_work', url: '/a/base/atom/list' }, { name: 'Mine', tabLinkActive: false, iconMaterial: 'person', url: '/a/user/user/mine' }, ], },};egg-born-module-a-layoutpc ...

June 3, 2019 · 5 min · jiezi

使用ReactElectronDvaWebpackNodejsWebsocket快速构建跨平台应用

目前Electron在github上面的star量已经快要跟React-native一样多了这里吐槽下,webpack感觉每周都在偷偷更新,很糟心啊,还有Angular更新到了8,Vue马上又要出正式新版本了,5G今年就要商用,华为的系统也要出来了,RN还没有更新到正式的1版本,还有号称让前端开发者失业的技术flutter也在疯狂更新,前端真的是学不完的 回到正题,不能否认,现在的大前端,真的太牛了,PC端可以跨三种平台开发,移动端可以一次编写,生成各种小程序以及React-native应用,然后跑在ios和安卓以及网页中 , 这里不得不说-------京东的Taro框架 这些人 已经把Node.js和webpack用上了天对webpack不熟悉的,看我之前的文章 ,今天不把重点放在webpack 欢迎关注我的专栏 《前端进阶》 都是百星高赞文章 手写React优化版脚手架前端性能优化不完全手册手写vue脚手架本文源码git仓库地址先说说Electron官网介绍:使用 JavaScript, HTML 和 CSS 构建跨平台的桌面应用 ,如果你可以建一个网站,你就可以建一个桌面应用程序。 Electron 是一个使用 JavaScript, HTML 和 CSS 等 Web 技术创建原生程序的框架,它负责比较难搞的部分,你只需把精力放在你的应用的核心上即可。什么意思呢?Electron = Node.js + 谷歌浏览器 + 平常的JS代码生成的应用,最终打包成安装包,就是一个完整的应用Electron分两个进程,主进程负责比较难搞的那部分,渲染进程(平常的JS代码)部分,负责UI界面展示两个进程之间可以通过remote模块,以及IPCRender和IPCMain之间通信,前者类似于挂载在全局的属性上进行通信(很像最早的命名空间模块化方案),后者是基于发布订阅机制,自定义事件的监听和触发实现两个进程的通信。Electron相当于给React生成的单页面应用套了一层壳,如果涉及到文件操作这类的复杂功能,那么就要依靠Electron的主进程,因为主进程可以直接调用Node.js的API,还可以使用C++插件,这里Node.js的牛逼程度就凸显出来了,既可以写后台的CRUD,又可以做中间件,现在又可以写前端。谈谈技术选型使用React去做底层的UI绘制,大项目首选React+TS状态管理的最佳实践肯定不是Redux,目前首选dva,或者redux-saga。构建工具选择webpack,如果不会webpack真的很吃亏,会严重限制你的前端发展,所以建议好好学习Node.js和webpack选择了普通的Restful架构,而不是GraphQL,可能我对GraphQL理解不深,没有领悟到精髓在通信协议这块,选择了websoket和普通的http通信方式因为是demo,很多地方并没有细化,后期会针对electron出一个网易云音乐的开源项目,这是一定要做到的先开始正式的环境搭建 config文件放置webpack配置文件server文件夹放置Node.js的后端服务器代码src下放置源码main.js是Electron的入口文件json文件是脚本入口文件,也是包管理的文件~开发模式项目启动思路:先启动webpack将代码打包到内存中,实现热更新再启动Electron读取对应的url地址的文件内容,也实现热更新设置webpack入口 app: ['babel-polyfill', './src/index.js', './index.html'], vendor: ['react'] } 忽略Electron中的代码,不用webpack打包(因为Electron中有后台模块代码,打包就会报错)externals: [ (function () { var IGNORES = [ 'electron' ]; return function (context, request, callback) { if (IGNORES.indexOf(request) >= 0) { return callback(null, "require('" + request + "')"); } return callback(); }; })() ] 加入代码分割optimization: { runtimeChunk: true, splitChunks: { chunks: 'all' } },设置热更新等 plugins: [ new HtmlWebpackPlugin({ template: './index.html' }), new webpack.HotModuleReplacementPlugin(), new webpack.NamedModulesPlugin(), ], mode: 'development', devServer: { contentBase: '../build', open: true, port: 5000, hot: true },#### 加入babel ...

June 3, 2019 · 6 min · jiezi

koa2一部分源码解析

koa2一部分源码解析

June 3, 2019 · 1 min · jiezi

前端常用4种模块化方案总结md

JS诞生之初面向简单页面开发, 没有模块的概念。后来页面逐渐复杂, 人类构造到 IIFE 立即执行函数来模拟 模块;之前也有雅虎的实践,使用命名空间 作为模块名。最后衍生出 面向各种使用场景 的 JS 模块标准。例如:面向浏览器的 AMD面向Nodejs的 CommonJS对于这种分裂状态ES标准也在尽力弥合。 但是目前流行的实践是 UMD模式。 1 AMDAMD 是requirejs 推广产出的规范,主要用于浏览器环境,通过define和require这两个定义模块、调用模块。 定义模块 define("alpha", ["require", "exports", "beta"], function (require, exports, beta) { exports.verb = function() { return beta.verb(); //Or: return require("beta").verb(); } });// 返回对象的匿名模块 define(["alpha"], function (alpha) { return { verb: function(){ return alpha.verb() + 2; } }; });调用模块require(['foo', 'bar'], function ( foo, bar ) { foo.doSomething();});define(function (require) { require(['a', 'b'], function (a, b) { //modules a and b are now available for use. }); }); 2 commonJSNode 应用由模块组成,采用 CommonJS 模块规范。每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。CommonJS 加载模块是同步的,所以只有加载完成才能执行后面的操作。像Node.js主要用于服务器的编程,加载的模块文件一般都已经存在本地硬盘,所以加载起来比较快,不用考虑异步加载的方式,所以CommonJS规范比较适用。但如果是浏览器环境,要从服务器加载模块,这是就必须采用异步模式。所以就有了 AMD CMD 解决方案。 ...

June 3, 2019 · 2 min · jiezi

如何远程调试部署在CloudFoundry平台上的nodejs应用

网络上关于如何本地调试nodejs应用的教程已经很多了,工具有Chrome开发者工具,Visual Studio Code,和nodejs周边的一些小工具等等。 在实际情况中,我们可能遇到本地运行良好,但是部署到CloudFoundry生产环境后的情况,此时就需要直接调试在CloudFoundry处于运行状态的nodejs应用了。本文介绍详细步骤。 首先我们得有一个在CloudFoundry上正常工作的nodejs应用。为了演示起见,本文使用的应用为jerry-demo-server: 使用如下命令将cf ssh -N -T -L 9229:127.0.0.1:9229 jerry-demo-server 这个命令将CloudFoundry上远程应用的9229端口和本地9229端口上建立了一个SSH安全隧道。 接下来,我们在Chrome地址栏输入chrome://inspect, 即可看到运行在CloudFoundry上的应用已经可以本地调试了: 在Chrome开发者工具的源代码里设置断点: 然后在浏览器里再次输入应用的url,断点触发,就可以开始远程调试了: 要获取更多Jerry的原创文章,请关注公众号"汪子熙":

June 1, 2019 · 1 min · jiezi

使用-apiDoc-为你的Nodejs-API-生成文档

翻译: 疯狂的技术宅原文:https://jonathas.com/document...未经许可,禁止转载! 本文首发微信公众号:前端先锋欢迎关注,每天都给你推送新鲜的前端技术文章 当你为其他开发人员(前端,桌面,移动等)开发 API 时,需要生成一份风格良好的文档,以便他们知道可以使用的内容和方式,这非常重要。 为此,在Node.js项目中,我一直在使用apiDoc,因为它能够从源代码中的注释生成HTML文档。 对于本文,我将使用我开发的 TODO List API 作为示例。你可以从这里克隆或下载它。 路由和注释在我关于使用 mocha 进行测试并使用 istanbul 进行代码覆盖测试的文章中,我在 TODO List API 中显示了 Task 端点的示例: import Task from "../controllers/tasks";export = (app) => { const endpoint = process.env.API_BASE + "tasks"; app.post(endpoint, Task.create); app.delete(endpoint + "/:id", Task.delete); app.get(endpoint + "/:id", Task.getOne); app.get(endpoint, Task.getAll); app.put(endpoint + "/:id", Task.update);};这代表了与系统中任务相关的所有端点。我们怎样才能使用它们呢?使用 API 的开发人员应该向每个端点发送什么数据呢? 到现在为止,他们除了去查看代码之外没有其他方法可以搞清楚,但是这些代码不应该被用作这个目的。 有了 apiDoc,我们可以用注释来生成文档。我的方法是在 routes 目录下的文件中配置的每个端点的前面编写它们。当我提到如何配置和组织我的 Node.js 项目时,如果你不确定我在说什么请 点击这里。 使用注释,我们的任务端点(内部routes/tasks.ts)将如下所示: import Task from "../controllers/tasks";export = (app) => { const endpoint = process.env.API_BASE + "tasks"; /** * @api {post} /api/v1/tasks Create a task * @apiVersion 1.0.0 * @apiName Create * @apiGroup Task * @apiPermission authenticated user * * @apiParam (Request body) {String} name The task name * * @apiExample {js} Example usage: * const data = { * "name": "Do the dishes" * } * * $http.defaults.headers.common["Authorization"] = token; * $http.post(url, data) * .success((res, status) => doSomethingHere()) * .error((err, status) => doSomethingHere()); * * @apiSuccess (Success 201) {String} message Task saved successfully! * @apiSuccess (Success 201) {String} id The campaign id * * @apiSuccessExample {json} Success response: * HTTPS 201 OK * { * "message": "Task saved successfully!", * "id": "57e903941ca43a5f0805ba5a" * } * * @apiUse UnauthorizedError */ app.post(endpoint, Task.create); /** * @api {delete} /api/v1/tasks/:id Delete a task * @apiVersion 1.0.0 * @apiName Delete * @apiGroup Task * @apiPermission authenticated user * * @apiParam {String} id The task id * * @apiExample {js} Example usage: * $http.defaults.headers.common["Authorization"] = token; * $http.delete(url) * .success((res, status) => doSomethingHere()) * .error((err, status) => doSomethingHere()); * * @apiSuccess {String} message Task deleted successfully! * * @apiSuccessExample {json} Success response: * HTTPS 200 OK * { * "message": "Task deleted successfully!" * } * * @apiUse UnauthorizedError */ app.delete(endpoint + "/:id", Task.delete); /** * @api {get} /api/v1/tasks/:id Retrieve a task * @apiVersion 1.0.0 * @apiName GetOne * @apiGroup Task * @apiPermission authenticated user * * @apiParam {String} id The task id * * @apiExample {js} Example usage: * $http.defaults.headers.common["Authorization"] = token; * $http.get(url) * .success((res, status) => doSomethingHere()) * .error((err, status) => doSomethingHere()); * * @apiSuccess {String} _id The task id * @apiSuccess {String} name The task name * * @apiSuccessExample {json} Success response: * HTTPS 200 OK * { * "_id": "57e8e94ea06a0c473bac50cc", * "name": "Do the disehs", * "__v": 0 * } * * @apiUse UnauthorizedError */ app.get(endpoint + "/:id", Task.getOne); /** * @api {get} /api/v1/tasks Retrieve all tasks * @apiVersion 1.0.0 * @apiName GetAll * @apiGroup Task * @apiPermission authenticated user * * @apiExample {js} Example usage: * $http.defaults.headers.common["Authorization"] = token; * $http.get(url) * .success((res, status) => doSomethingHere()) * .error((err, status) => doSomethingHere()); * * @apiSuccess {String} _id The task id * @apiSuccess {String} name The task name * * @apiSuccessExample {json} Success response: * HTTPS 200 OK * [{ * "_id": "57e8e94ea06a0c473bac50cc", * "name": "Do the disehs" * }, * { * "_id": "57e903941ca43a5f0805ba5a", * "name": "Take out the trash" * }] * * @apiUse UnauthorizedError */ app.get(endpoint, Task.getAll); /** * @api {put} /api/v1/tasks/:id Update a task * @apiVersion 1.0.0 * @apiName Update * @apiGroup Task * @apiPermission authenticated user * * @apiParam {String} id The task id * * @apiParam (Request body) {String} name The task name * * @apiExample {js} Example usage: * const data = { * "name": "Run in the park" * } * * $http.defaults.headers.common["Authorization"] = token; * $http.put(url, data) * .success((res, status) => doSomethingHere()) * .error((err, status) => doSomethingHere()); * * @apiSuccess {String} message Task updated successfully! * * @apiSuccessExample {json} Success response: * HTTPS 200 OK * { * "message": "Task updated successfully!" * } * * @apiUse UnauthorizedError */ app.put(endpoint + "/:id", Task.update);};如你所见,我们有 HTTP 方法的类型(post,put,get,delete)、端点地址、api 版本、它需要的权限类型、它需要的参数,还有如果用户是未经授权的应该返回怎样的响应和错误。 ...

May 31, 2019 · 4 min · jiezi

nodejs-log4js-使用

本文章针对 log4js v4.3本文对自己用log4js的技术点简单的做一个记录,有一些技术点没有用到或者写全,会在后面用到的时候进行更新。 先大概的对log4js 的基本用法做一个总结,使用configure()方法对日志输出进行配置,使用 getlogger()方法获取logger对象,然后使用logger 对象进行打印各个级别的日志。其中比较复杂的是 configure()方法中的配置对象appenders 和 categories 下面重点讲解这两个对象安装npm install --save log4js 简单的用法var log4js = require('log4js');var logger = log4js.getLogger();logger.level = 'debug';logger.debug("Some debug messages");getLogger()防范返回一个logger对象,想到对日志进行记录就必须使用logger对象,将logger对象的 level设置为debug(默认为OFF不会输出任何日志) 在node 环境运行上面的代码之后发现在控制台打印出了对应的日志信息,但是这跟我想要的完全不一样,我想要的是根据我自己的想法将日志进行分类方便查找和统计。接下来我们就看如何对日志进行分类。 configure(string | object) 方法通过调用configure 方法就可以实现我们对日志进行分类管理的目的。方法的参数可以是一个字符串或者是一个object 字符串参数被视为用于加载配置的文件名,配置文件是一个json 文件,也可以直接将配置对象传给configure。 配置对象本次只对配置对象中最常用的appenders 和categories 进行介绍,还有部分属性暂时还没有用到等用到的时候在进行补充,如果平常用的话这两个属性应该就够了。 简单的总结一下这两个属性,先有一个概念行的认识了解一下他们分别是干什么的。appenders 主要是用来定义以怎样的方式输出输出到哪里(主要功能不仅限于)categories 是用来定义日志输出的规则然后调用之前定义好的 appenders 进行输出写一个简单的例子方便理解一下 const log4js = require('log4js');log4js.configure({ appenders: { out: { type: 'stdout' }, app: { type: 'file', filename: 'application.log' } }, categories: { default: { appenders: [ 'out', 'app' ], level: 'debug' } }});var logger = log4js.getLogger();logger.debug("Some debug messages");运行上的代码将会创建application.log 文件在项目目录下,同时会把日志打到控制到和 application.log文件中.上面定义了两个appender 一个是输出到 stdout 一个是输出到 file ,可以看出appender主要使用来定义输出位置的同时在categories 中定义了一个规则,调用了appender中定义的输出,并且输出级别为 debug ...

May 31, 2019 · 1 min · jiezi

gPRC实现跨语言的微服务间通信-精通外语的电报员与煲电报粥的小怪兽

作者:亚瑟、文远 1. 微服务框架 -- 从系统怪物到服务小怪兽一个小巧的单体应用会随着公司业务的扩张而慢慢成长,逐渐演化成一个庞大且复杂的系统怪物,系统任何一处的问题都将影响整个怪物的表现,很少有单独的开发者能理清系统怪物所有的肌理脉络,导致bug的定位和新功能的扩展都变得越来越困难,对系统的任一改动都要求整个怪物一起回归测试并重新部署,效率必然不高。所以公司发展到了一定阶段,总会需要从架构上寻找解决系统怪物之道,而微服务就是目前最流行的架构方案之一,它将系统怪物拆分成多个独立自治的服务小怪兽,让我们有能力分而治之。[插画:无数小怪兽组成一座大怪兽形状的山,小怪兽正一个个从山上滚下来] 2. RPC框架 -- 小怪兽的电报员一旦系统怪物被拆分成了多个服务小怪兽,小怪兽们如何沟通协作就成了我们最关心的问题。服务小怪兽间的通信就好像发电报一样,涉及到数据序列化、反序列化、连接管理、收发线程、超时处理等多个问题,RPC框架的出现解决了这些问题,就好像通过电报员发电报一样,使用RPC框架让小怪兽们不必关心通信的底层细节。[插画:小怪兽在请电报员发电报] RPC调用细节服务消费方(小怪兽A)以本地调用方式调用服务client stub(小怪兽A的电报员)接受到调用后负责将方法、参数等编码成能够进行网络传输的消息体(电报)client stub(小怪兽A的电报员)找到服务地址,并将消息发送到服务端server stub(小怪兽B的电报员)收到消息(电报)后进行解码server stub(小怪兽B的电报员)根据解码结果调用本地的服务(小怪兽B)本地服务(小怪兽B)执行并将结果返回给server stub(小怪兽B的电报员)server stub(小怪兽B的电报员)将结果编码成消息(电报)并发送至客户端client stub(小怪兽A的电报员)接受到消息(电报)并进行解码服务消费方(小怪兽A)得到最终的结果3. gRPC -- 这位电报员是语言天才如果通信的小怪兽们语言不通,那么我们需要对电报员(亦即RPC框架)的人选提出更高的要求,无论小怪兽们用的是什么语言,协助通信的两位电报员都必须把它们的话翻译成电报员彼此能理解的同一种语言,亦即IDL(Interface Description Language),是的,电报员在这种情况下还必须承担翻译的角色,而gRPC就是一位如此优秀的电报员。[插画:小怪兽说"今晚的月色真美",电报员写”I Love you“] 4. gPRC Demo实现Node客户端小怪兽发送"今晚的月色真美",Java服务端小怪兽收到电报内容,并回复"I love you too"。 通过Spring Boot创建Java项目,pom.xml中加入如下依赖 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-netty-shaded</artifactId> <version>1.21.0</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-protobuf</artifactId> <version>1.21.0</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-stub</artifactId> <version>1.21.0</version> </dependency> </dependencies> <build> <extensions> <extension> <groupId>kr.motd.maven</groupId> <artifactId>os-maven-plugin</artifactId> <version>1.5.0.Final</version> </extension> </extensions> <plugins> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.5.1</version> <configuration> <protocArtifact>com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier}</protocArtifact> <pluginId>grpc-java</pluginId> <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.21.0:exe:${os.detected.classifier}</pluginArtifact> <!--指定生成文件目录--> <outputDirectory>src/main/java</outputDirectory> <!--重新生成文件时不清除 原有src/main/java下的内容--> <clearOutputDirectory>false</clearOutputDirectory> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>定义IDL文件 ...

May 30, 2019 · 2 min · jiezi

GitLab-CICD-在-Nodejs-项目中的实践

近期在按照业务划分项目时,我们组被分了好多的项目过来,大量的是基于 Node.js 的,也是我们组持续在使用的语言。现有流程中的一些问题在维护多个项目的时候,会暴露出一些问题: 如何有效的使用 测试用例如何有效的使用 ESLint部署上线还能再快一些吗 使用了 TypeScript 以后带来的额外成本测试用例首先是测试用例,最初我们设计在了 git hooks 里边,在执行 git commit 之前会进行检查,在本地运行测试用例。 这会带来一个时间上的问题,如果是日常开发,这么操作还是没什么问题的,但如果是线上 bug 修复,执行测试用例的时间依据项目大小可能会持续几分钟。 而为了修复 bug,可能会采用 commit 的时候添加 -n 选项来跳过 hooks ,在修复 bug 时这么做无可厚非,但是即使大家在日常开发中都采用commit -n 的方式来跳过繁琐的测试过程,这个也是没有办法管控的,毕竟是在本地做的这个校验,是否遵循这个规则,全靠大家自觉。 所以一段时间后发现,通过这种方式执行测试用例来规避一些风险的作用可能并不是很有效。 ESLint然后就是 ESLint,我们团队基于airbnb的 ESLint 规则自定义了一套更符合团队习惯的规则,我们会在编辑器中引入插件用来帮助高亮一些错误,以及进行一些自动格式化的操作。 同时我们也在 git hooks 中添加了对应的处理,也是在 git commit 的时候进行检查,如果不符合规范则不允许提交。 不过这个与测试用例是相同的问题: 编辑器是否安装 ESLint 插件无从得知,即使安装插件、是否人肉忽略错误提示也无从得知。git hooks 可以被绕过部署上线的方式之前团队的部署上线是使用shipit周边套件进行部署的。 部署环境强依赖本地,因为需要在本地建立仓库的临时目录,并经过多次ssh XXX "command"的方式完成 部署 + 上线 的操作。 shipit提供了一个有效的回滚方案,就是在部署后的路径添加多个历史部署版本的记录,回滚时将当前运行的项目目录指向之前的某个版本即可。_不过有一点儿坑的是,很难去选择我要回滚到那个节点,以及保存历史记录需要占用额外的磁盘空间_ 不过正因为如此,shipit在部署多台服务器时会遇到一些令人不太舒服的地方。 如果是多台新增的服务器,那么可以通过在shipit配置文件中传入多个目标服务器地址来进行批量部署。 但是假设某天需要上线一些小流量(比如四台机器中的一台),因为前边提到的shipit回滚策略,这会导致单台机器与其他三台机器的历史版本时间戳不一致(因为这几台机器不是同一时间上线的) 提到了这个时间戳就另外提一嘴,这个时间戳的生成是基于执行上线操作的那台机器的本地时间,之前有遇到过同事在本地测试代码,将时间调整为了几天前的时间,后时间没有改回正确的时间时进行了一次部署操作,代码出现问题后却发现回滚失败了,原因是该同事部署的版本时间戳太小,shipit 找不到之前的版本(shipit 可以设置保留历史版本的数量,当时最早的一次时间戳也是大于本次出问题的时间戳的) 也就是说,哪怕有一次进行过小流量上线,那么以后就用不了批量上线的功能了 (没有去仔细研究shipit官方文档,不知道会不会有类似--force之类的忽略历史版本的操作) 基于上述的情况,我们的部署上线耗时变为了: (__机器数量__)X(__基于本地网速的仓库克隆、多次 ssh 操作的耗时总和__)。 P.S. 为了保证仓库的有效性,每次执行 shipit 部署,它都会删除之前的副本,重新克隆 ...

May 30, 2019 · 4 min · jiezi

koajwt实现token验证与刷新

JWTJSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。 本文只讲Koa2 + jwt的使用,不了解JWT的话请到这里)进行了解。 koa环境要使用koa2+jwt需要先有个koa的空环境,搭环境比较麻烦,我直接使用koa起手式,这是我使用koa+typescript搭建的空环境,如果你也经常用koa写写小demo,可以点个star,方便~ 安装koa-jwtkoa-jwt主要作用是控制哪些路由需要jwt验证,哪些接口不需要验证: import * as koaJwt from 'koa-jwt';//路由权限控制 除了path里的路径不需要验证token 其他都要app.use( koaJwt({ secret: secret.sign }).unless({ path: [/^\/login/, /^\/register/] }));上面代码中,除了登录、注册接口不需要jwt验证,其他请求都需要。 使用jsonwebtoken生成、验证token执行npm install jsonwebtoken安装jsonwebtoken相关代码: import * as jwt from 'jsonwebtoken';const secret = 'my_app_secret';const payload = {user_name:'Jack', id:3, email: '1234@gmail.com'};const token = jwt.sign(payload, secret, { expiresIn: '1h' });上面代码中通过jwt.sign来生成一个token,参数意义: payload:载体,一般把用户信息作为载体来生成tokensecret:秘钥,可以是字符串也可以是文件expiresIn:过期时间 1h表示一小时在登录中返回tokenimport * as crypto from 'crypto';import * as jwt from 'jsonwebtoken';async login(ctx){ //从数据库中查找对应用户 const user = await userRespository.findOne({ where: { name: user.name } }); //密码加密 const psdMd5 = crypto .createHash('md5') .update(user.password) .digest('hex'); //比较密码的md5值是否一致 若一致则生成token并返回给前端 if (user.password === psdMd5) { //生成token token = jwt.sign(user, secret, { expiresIn: '1h' }); //响应到前端 ctx.body = { token } }}前端拦截器前端通过登录拿到返回过来的token,可以将它存在localStorage里,然后再以后的请求中把token放在请求头的Authorization里带给服务端。这里以axios请求为例,在发送请求时,通过请求拦截器把token塞到header里: ...

May 30, 2019 · 2 min · jiezi

让Nodejs支持H5-History模式connecthistoryapifallback源码分析

导读本文主要是对connect-history-api-fallback库进行一次源码分析。connect-history-api-fallback是一个用于支持SPA History路由模式的nodejs库。阅读本文前,应对HTML5 History模式有一定程度的了解! 源码分析/** * 前端需要开启history模式,而后端根据url并不知道前端在请求api还是在请求页面,如localhost:4200/home这种url,前端理所当然认为“我需要得到html,并跳转到首页”,然而后端并不能区分。 * 因此需要一种判断机制,来使得后端能分析出前端的请求目的。 * connect-history-api-fallback 这个中间件正好帮我们完成了上述分析操作,来看下它是怎么实现的吧! * 第一次把自己的源码分析思路写出来,说得不对的地方,请指出! */'use strict';var url = require('url');exports = module.exports = function historyApiFallback(options) { // 接收配置参数 options = options || {}; // 初始化日志管理器 var logger = getLogger(options); // 中间件是要返回一个函数的,函数形参有req, res, next return function(req, res, next) { var headers = req.headers; if (req.method !== 'GET') { // 如果请求方法不是GET类型,说明不需要返回html,那么就调用next(),把请求交给下一个中间件 logger( 'Not rewriting', req.method, req.url, 'because the method is not GET.' ); return next(); } else if (!headers || typeof headers.accept !== 'string') { // 如果没有请求头,或者请求头中的accept不是字符串,说明不是一个标准的http请求,也不予处理,把请求交给下一个中间件 logger( 'Not rewriting', req.method, req.url, 'because the client did not send an HTTP accept header.' ); return next(); } else if (headers.accept.indexOf('application/json') === 0) { // 如果客户端希望得到application/json类型的响应,说明也不是在请求html,也不予处理,把请求交给下一个中间件 logger( 'Not rewriting', req.method, req.url, 'because the client prefers JSON.' ); return next(); } else if (!acceptsHtml(headers.accept, options)) { // 如果请求头中不包含配置的Accept或者默认的['text/html', '*/*'],那么说明也不是在请求html,也不予处理,把请求交给下一个中间件 logger( 'Not rewriting', req.method, req.url, 'because the client does not accept HTML.' ); return next(); } // 走到这里说明是在请求html了,要开始秀操作了 // 首先利用url模块的parse方法解析下url,会得到一个对象,包括protocol,hash,path, pathname, query, search等字段,类似浏览器的location对象 var parsedUrl = url.parse(req.url); var rewriteTarget; // 然后得到配置中的rewrites,也就是重定向配置; // 重定向配置是一个数组,每一项都包含from和to两个属性; // from是用来正则匹配pathname是否需要重定向的; // to则是重定向的url,to可以是一个字符串,也可以是一个回调方法来返回一个字符串,回调函数接收一个上下文参数context,context包含三个属性(parsedUrl,match,request) options.rewrites = options.rewrites || []; // 遍历一波重定向配置 for (var i = 0; i < options.rewrites.length; i++) { var rewrite = options.rewrites[i]; // 利用字符串的match方法去匹配 var match = parsedUrl.pathname.match(rewrite.from); if (match !== null) { // 如果match不是null,说明pathname和重定向配置匹配上了 rewriteTarget = evaluateRewriteRule(parsedUrl, match, rewrite.to, req); if(rewriteTarget.charAt(0) !== '/') { // 推荐使用/开头的绝对路径作为重定向url logger( 'We recommend using an absolute path for the rewrite target.', 'Received a non-absolute rewrite target', rewriteTarget, 'for URL', req.url ); } logger('Rewriting', req.method, req.url, 'to', rewriteTarget); // 进行重定向url操作 req.url = rewriteTarget; return next(); } } var pathname = parsedUrl.pathname; // 首先说明一下:校验逻辑默认是会去检查url中最后的.号的,有.号的说明在请求文件,那就跟history模式就没什么鸟关系了 // 我暂且将上述规则成为“点号校验规则” // disableDotRule为true,代表禁用点号校验规则 if (pathname.lastIndexOf('.') > pathname.lastIndexOf('/') && options.disableDotRule !== true) { // 如果pathname的最后一个/之后还有.,说明请求的是/a/b/c/d.*的文件(*代表任意文件类型); // 如果此时配置disableDotRule为false,说明开启点号校验规则,那么不予处理,交给其他中间件 logger( 'Not rewriting', req.method, req.url, 'because the path includes a dot (.) character.' ); return next(); } // 如果pathname最后一个/之后没有.,或者disableDotRule为true,都会走到最后一步:重写url // 重写url有默认值/index.html,也可以通过配置中的index自定义 rewriteTarget = options.index || '/index.html'; logger('Rewriting', req.method, req.url, 'to', rewriteTarget); // 重写url req.url = rewriteTarget; // 此时再将执行权交给下一个中间件(url都换成index.html了,后面的路由等中间件也不会再处理了,然后前端接收到html就开始解析路由了,目的达到!) next(); };};// 判断重定向配置中的tofunction evaluateRewriteRule(parsedUrl, match, rule, req) { if (typeof rule === 'string') { // 如果是字符串,直接返回 return rule; } else if (typeof rule !== 'function') { // 如果不是函数,抛出错误 throw new Error('Rewrite rule can only be of type string or function.'); } // 执行自定义的回调函数,得到一个重定向的url return rule({ parsedUrl: parsedUrl, match: match, request: req });}// 判断请求头的accept是不是包含在配置数组或默认数组的范围内function acceptsHtml(header, options) { options.htmlAcceptHeaders = options.htmlAcceptHeaders || ['text/html', '*/*']; for (var i = 0; i < options.htmlAcceptHeaders.length; i++) { if (header.indexOf(options.htmlAcceptHeaders[i]) !== -1) { return true; } } return false;}// 处理日志function getLogger(options) { if (options && options.logger) { // 如果有指定的日志方法,则使用指定的日志方法 return options.logger; } else if (options && options.verbose) { // 否则,如果配置了verbose,默认使用console.log作为日志方法 return console.log.bind(console); } // 否则就没有日志方法,就不记录日志咯 return function(){};}

May 30, 2019 · 2 min · jiezi

从零编写一个Koa-graphQL的案例

在Nest.js的文档中看到了有集成GraphQL的指导,所以在本地尝试下先用Koa写出一个DEMO,之后再去与Nest.js集成起来。 先写出数据库模型(这个文件是之前就存在的,没有做更改,将文件名改成了models.ts): /** * Created by w on 2018/4/13. */const mongoose = require('mongoose');mongoose.Promise = global.Promise;mongoose.connect('mongodb://localhost/ticket', { server: { socketOptions: { keepAlive: 1 } }});const models = { users: { username: { type: String, required: true }, password: { type: String, required: true }, description: { type: String }, createTime: { type: Date, default: new Date() } }, tickets: { name: { type: String, required: true }, price: { type: Number, requred: true }, holdTime: { //举办时间 type: Date, required: true }, count: { //剩余数量 type: String, required: true }, place:{ type: String, required: true }, img: { type: String }, description: { type: String }, publicTime: { type: Date, default: new Date() }, }};for (let m in models) { mongoose.model(m, mongoose.Schema(models[m]));}module.exports = { getModules: (name) => { return mongoose.model(name); }};之后编写各自模型的GraphQL查询文件(user.ts)): ...

May 30, 2019 · 4 min · jiezi

细说-Vue-组件的服务器端渲染

细说 Vue 组件的服务器端渲染声明:需要读者对 NodeJs、Vue 服务器端渲染有一定的了解现在,前后端分离与客户端渲染已经成为前端开发的主流模式,绝大部分的前端应用都适合用这种方式来开发,又特别是 React、Vue 等组件技术的发展,更是使这种方式深入人心。 但有一些应用,客户端渲染就会遇到一些问题了: 需要做 SEO(搜索引擎优化),但客户端渲染的 html 中几乎没有可用的信息需要首屏快速加载,但客户端渲染一般是长时间的加载动画或者白屏如果能把客户端渲染的组件化技术(React、Vue 等)与传统的后端渲染的方式有效的结合起来,两者兼具,那就太完美了。 所以,这次就来聊聊 Vue 组件的服务器端渲染。 根据社区现有的一些方案,结合自己的实践,针对团队技术力量的不同,说说不同应用场景选择方案时的优先级。 1. NodeJs 渲染中间层一般前后端的工作流是 后端 -> 前端。 传统的后端渲染模式是后端负责包括 url、接口、模板渲染等,前端与后端耦合在一起,当然这种方式正在慢慢的退出历史舞台。 主流的客户端渲染则是后端只提供接口(如有需要,可以提供必要的 url),前端与后端只通过接口交流数据,路由与渲染都在前端完成。 而 NodeJs 渲染中间层的工作流则是 后端 -> NodeJs -> 前端(NodeJs 渲染中间层由前端开发人员掌握)。 这种模式下,后端只提供接口,传统的服务器端路由(url)、模板渲染则都有 NodeJs 层接管。这样,前端开发人员可以自由的决定哪些组件需要在服务器端渲染,哪些组件可以放在客户端渲染,前后端完全解耦,但又保留了服务器端渲染的功能。 这种方案最成熟的是 nuxt.js。 如果有需要,大家可以自己去 nuxt.js 官方文档 看看具体的使用方法和详细的功能。 应该说,这种方式是目前最完美的一种方案,但也有一些隐患: 增加了一个 NodeJs 中间层,应用性能会有所降低增加了架构的复杂度、不稳定性,也降低了应用的安全性对于高并发应用,NodeJs 层会很容易形成瓶颈对开发人员要求高了很多所以,这种方式适合对并发量、安全性、稳定性等要求不高,但又需要做 SEO 或首屏快速加载的页面。 当然,如果你能够自己改造相关的工具,就另当别论了。 2. 保留后端模板渲染当不能使用 NodeJs 中间层时,而又要达到 SEO 与首屏快速响应的目的时,在传统的后端模板渲染的基础上,就需要对前端的页面加以适当的改造。 2.1 首屏快速响应首屏快速响应就意味着首屏渲染所需的数据是跟 HTML 文件一起到达浏览器的,这些数据当前是由后端模板引擎嵌入到 HTML 页面中的。 ...

May 30, 2019 · 2 min · jiezi

详解vue组件三大核心概念

前言本文主要介绍属性、事件和插槽这三个vue基础概念、使用方法及其容易被忽略的一些重要细节。如果你阅读别人写的组件,也可以从这三个部分展开,它们可以帮助你快速了解一个组件的所有功能。 本文的代码请猛戳github博客,纸上得来终觉浅,大家动手多敲敲代码! 一、属性1.自定义属性propsprop 定义了这个组件有哪些可配置的属性,组件的核心功能也都是它来确定的。写通用组件时,props 最好用对象的写法,这样可以针对每个属性设置类型、默认值或自定义校验属性的值,这点在组件开发中很重要,然而很多人却忽视,直接使用 props 的数组用法,这样的组件往往是不严谨的。 // 父组件 <props name='属性' :type='type' :is-visible="false" :on-change="handlePropChange" :list=[22,33,44] title="属性Demo" class="test1" :class="['test2']" :style="{ marginTop: '20px' }" //注意:style 的优先级是要高于 style style="margin-top: 10px"> </props>// 子组件 props: { name: String, type: { //从父级传入的 type,它的值必须是指定的 'success', 'warning', 'danger'中的一个,如果传入这三个以外的值,都会抛出一条警告 validator: (value) => { return ['success', 'warning', 'danger'].includes(value) } }, onChange: { //对于接收的数据,可以是各种数据类型,同样也可以传递一个函数 type: Function, default: () => { } }, isVisible: { type: Boolean, default: false }, list: { type: Array, // 对象或数组默认值必须从一个工厂函数获取 default: () => [] } }从上面的例中,可以得出props 可以显示定义一个或一个以上的数据,对于接收的数据,可以是各种数据类型,同样也可以传递一个函数。 ...

May 30, 2019 · 3 min · jiezi

为什么要用Nodejs

翻译:疯狂的技术宅原文:https://medium.com/the-node-j... 未经许可,禁止转载! 本文首发微信公众号:前端先锋欢迎关注,每天都给你推送新鲜的前端技术文章 介绍JavaScript 的日益发展带来了很多变化,当今的 Web 开发面貌已经变得截然不同。在几年前是很难想象在服务器上运行 JavaScript 的。 在深入研究Node.js之前,你可能想了解使用跨栈的 JavaScript 有什么好处,它统一了语言和数据格式(JSON),允许你以最佳的方式重用开发人员资源。将 Node.js 合并到技术栈中是一个关键优势。 Node.js 是一个基于 Chrome 的名为 V8 的 JavaScript 引擎构建的 JavaScript 运行环境。值得注意的是,Node.js 的创建者 Ryan Dahl 的“受到 Gmail 等应用的启发”,目标是为了开发一个具有实时推送功能的网站。在 Node.js 中,他提供了一个用于处理非阻塞事件驱动的 I/O 工具。 用一句话来概括:Node.js 在基于websockets 推送技术的实时 Web 应用中大放异彩。在过去的 20 多年来我们一直在使用基于无状态请求 - 响应模式的无状态 Web 应用,现在终于拥有了能够实时双向连接的 Web 应用,其中客户端和服务器都可以启动通信,并允许它们自由地交换数据。 这与典型的总是由客户端发起通信的 Web 响应模式形了成鲜明的对比。此外它也同样基于在标准端口 80 上运行的开放 Web 技术栈(HTML,CSS和JS)。 有人可能会争辩说,我们多年来一直以 Flash 和 Java Applet 的形式做到这一点 —— 但实际上,这些只是使用 Web 作为传输协议将数据传给客户端的沙盒环境。此外,它们是隔离运行的,通常在非标准端口上运行,这可能需要额外的权限。 凭借其优势,Node.js 在依赖其独特优势的众多知名公司的技术堆栈中发挥着关键作用。 Node.js 基金会几乎已经整合了所有最好的想法,可以在 Node.js 基金会的案例研究页面上找到关于为什么企业应该考虑 Node.js 的简短PPT。 ...

May 30, 2019 · 3 min · jiezi

webpack4322系列教程02-mode

简介 mode(模式)是webpack4.0.0新增的配置,用来指定webpack使用对应模式的内置优化;它有三个可选模式:production、development、none;默认为production。 选项描述development通过DefinePlugin插件将 process.env.NODE_ENV 的值设为 development。启用 NamedChunksPlugin 和 NamedModulesPlugin。production通过DefinePlugin插件将 process.env.NODE_ENV 的值设为 production。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 TerserPlugin.none通过DefinePlugin插件将 process.env.NODE_ENV 的值设为 node。使用默认的优化项。演示下面通过一段代码分别演示一个每一个模式打包输出的文件内容:源码地址 第一步:编写入口文件和依赖代码 // webpack@4.32.2系列教程/demo02-mode/src/role.jsexport default class Role { constructor(name, skill) { this.name = name; this.skill = skill; }}// webpack@4.32.2系列教程/demo02-mode/src/index.jsimport Role from './role'const role = new Role('乔峰', '降龙十八掌');console.log(role);console.log('process.env.NODE_ENV: ', process.env.NODE_ENV);第二步:编写webpack配置 & 启动webpack // webpack@4.32.2系列教程/demo02-mode/scripts/build.jsconst webpack = require('webpack');// 创建编译器对象const compiler = webpack({ // mode模式:webpack4.0.0新增配置,用来指定webpack使用相应模式的内置优化。 // mode: 'development' // 会将 process.env.NODE_ENV 的值设为 development。启用 NamedChunksPlugin 和 NamedModulesPlugin。 // mode: 'production' // 会将 process.env.NODE_ENV 的值设为 production。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 UglifyJsPlugin. mode: 'none' // 使用默认优化项});// 启动webpackcompiler.run((err, stats) => { if (err) { console.error(err); return; } // 输出编译成功信息 console.log(stats.toString({ colors: true }));});第三步:cd到demo02-mode文件夹下,运行node scripts/build.js ...

May 30, 2019 · 1 min · jiezi

全栈开发入门实战后台管理系统

本文首发于 GitChat 平台,免费 Chat,链接:全栈开发入门实战:后台管理系统 感谢你打开了这篇 Chat,在阅读之前,需要让你了解一些事情。 第一,本 Chat 虽然免费,不代表没有价值,我会将个人全栈开发的经历叙述给你,希望对你有一些帮助;第二,文中所使用的技术栈并非最新,也并非最优。后台管理系统更多是 2B 端的产品,通常是业务优先。本 Chat 的目的是为了让你能够快速上手全栈开发。第三,本 Chat 虽然名为全栈开发,但是不会带你做完一个完整的后台管理系统项目。一是由于篇幅有限,二是由于时间关系,个人精力也有限。 正文本 Chat 的内容,正如 Chat 简介中所描述,将分为以下 5 大块: 开发准备前台样式数据库连接前后台交互线上部署你可能会发现,好像不知道要做什么,没错,后台管理系统一般都是企业内部定制开发,通常是对业务的数据管理,具体做什么功能由业务决定,但多数功能都是围绕着表格或者表单。 上面列举的仅仅是全栈开发的大致流程。首先,要做一些准备工作,例如:开发环境、编辑器环境以及依赖包配置等等工作;其次,我们要选定一个后台模版样式,快速套用,实现业务功能。(当然,你要自己开发也行,但不建议这么做,除非为了学习);然后,根据业务做数据库的设计,编写后台数据处理逻辑,以及前后台数据交互等功能;最后,测试并部署上线。 这里的示例,将实现一个学生数据的在线管理需求,其实就是一个在线表格,包括添加,删除功能,系统层面包括登录退出功能。麻雀虽小,五脏俱全,整体架子搭好了,再在上面添加功能就简单多了。好了,现在就开始全栈之旅吧。 开发准备启动项目首先要做的是,开发环境的安装,这里就不多说了,关于 Node 环境的安装,默认你已经搞定了。 既然采用 Express 作为 Web 框架,Express 也是要安装的,有了 Node 环境,安装 Express 就简单多了。我们直接上手 Express 的脚手架,全栈开发关键要速度。 npm install express-generator -g一定记得是全局安装。安装完成之后,输入 express -h 可以查看帮助。这里选用 ejs 模版引擎,为什么?因为我顺手而已,这个不重要。找个合适的目录,运行下面命令: express -e node-web-fullstack-demo生成项目目录之后,首先要安装依赖,如下命令: cd example-node-web-fullstacknpm install等待安装完成,我们就可以启动项目了,使用命令 npm start ,去浏览器中,打开网址:http://localhost:3000,看到写着 Express 的首页,代表你的项目启动成功了。 编辑器环境配置一个好的编码环境,可以让你项目开发效率加倍。 首先介绍一个编辑器配置 EditorConfig,这是一个编辑器的小工具。它有什么作用呢?简而言之,就是让你可以在不同的编辑器上,获得相同的编码风格,例如:空格缩进还是 Tab 缩进?缩进几个空格? 你可能觉得诧异,这个不是在编辑器上设置就可以了吗?没错,假设你从始至终都是在同一个电脑同一个编辑器上编码,那么可以忽略它。如果存在多电脑配合,亦或是多个编辑器配合,那么它就是神器。它几乎支持所有的主流编辑器,不用单独去编辑器中设置,配置文件就在项目中,用那个编辑器打开项目,都能获得一致的编码风格。 ...

May 30, 2019 · 5 min · jiezi

EventProxy的使用-解决异步回调地狱

最近在看node社区的nodeclub源码,看到一个玩意EventProxy,这里记录一下基本语法nodeclub社区源码: https://github.com/cnodejs/no... eventproxy工具源码: https://github.com/JacksonTia... EventProxy 可以理解为一个基于事件机制对复杂的业务逻辑进行解耦的工具,可以解决javascript异步回调地狱问题的工具。 利用事件机制解耦复杂业务逻辑移除被广为诟病的深度callback嵌套问题将串行等待变成并行等待,提升多异步协作场景下的执行效率友好的Error handling无平台依赖,适合前后端,能用于浏览器和Node.js兼容CMD,AMD以及CommonJS模块环境先来看一段回调嵌套的示例代码: 准备工作:三个文件file1.txt,file2.txt, file3.txt文件,在里面随便写点内容 var fs = require('fs');fs.readFile('./file1.txt', 'utf8', function (err1, data1) { fs.readFile('./file2.txt', 'utf8', function (err2, data2) { fs.readFile('./file3.txt', 'utf8', function (err3, data3) { console.log(data1 + data2 + data3); }); });});这种代码在node中是不是经常见?? 看着是不是有点心疼的感觉~ 哈哈,看到两句有意思的话: 这个世界上不存在所谓回调函数深度嵌套的问题。 世界上本没有嵌套回调,写得人多了,也便有了}}}}}}}}}}}}。 正题: 安装eventproxy npm install eventproxy使用 var EventProxy = require('eventproxy');var ep = new EventProxy();常用方法分为两部分: 解决回调的方法: emit()after()all()优化代码的方法: done()throw()fail()emit()方法、all()方法 var fs = require('fs');var EventProxy = require('eventproxy');var ep = new EventProxy();// all()方法用于指定接收哪几种事件,并在回调函数中进行统一处理,回调函数可以接收事件中携带的参数,参数位置与事件位置一一对应ep.all(['read_file1', 'read_file2', 'read_file3'], function (data1, data2, data3) { console.log(data1 + data2 + data3);});fs.readFile('/file1.txt', 'utf8', function (err, data) { // 使用emit抛出一个事件 read_file1 ep.emit('read_file1', data);});fs.readFile('/file2.txt', 'utf8', function (err, data) { // 使用emit抛出一个事件 read_file2 ep.emit('read_file2', data);});fs.readFile('/file3.txt', 'utf8', function (err, data) { // 使用emit抛出一个事件 read_file3 ep.emit('read_file3', data);});上面的例子,我们使用emit()方法抛出了三个不同的事件,然后使用all()方法统一接收处理 ...

May 29, 2019 · 2 min · jiezi

nodejs-websocket-socketio

为什么需要 WebSocket?因为个人对概念理解不是很深,文字表达能力不强,如果有关HTTP等方面描述不准确,欢迎纠正,谢谢大家 初次接触 WebSocket 的人,都会问同样的问题:我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处?答案很简单,因为 HTTP 协议有一个缺陷:通信只能由客户端发起。 举例来说:我们想了解今天的天气,只能是客户端向服务器发出请求,服务器返回查询结果。HTTP 协议做不到服务器主动向客户端推送信息。 这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。我们只能使用"轮询":设置定时器每隔一段时候,就发出一个"询问"(简单理解为ajax请求),了解服务器有没有新的信息。最典型的场景就是聊天室。 ---- 参考了阮一峰老师的文章: WebSocket 教程 轮询请求的缺点:不停地链接,断开,链接,断开请求,浪费很多服务器资源浪费带宽移动端浪费流量websocket的优点:没有同源限制,客户端可以与任意服务器通信,不涉及到跨域的问题。双向通信,服务器可以向客户端主动发送数据。数据格式比较轻量,性能开销小,通信高效。websocket为什么高效普通的http通信是基于字符的通信(超文本), websocket一开始是文本协议, 但是链接建立后编程了二进制协议, 数据无需转换等。socket.io的使用socket.io是一个封装后的库,原生的 websocket 因为比较复杂,需要自己处理请求头,设置持续链接等等。因此选择使用的socket.io安装npm i socket.io -Dsocket主要有两个方法:sock.emit('name', data) 主动发送数据sock.on('name', function(data){ })` 接收数据 1.服务端 // server.js const http = require('http') const io = require('socket.io') let server = http.createServer((req, res)=>{}) server.listen(8080) // 建立ws websocket简称ws let wsServer = io.listen(server); wsServer.on('connection', sock=>{ sock.on('aaa', function(a,b){ // name -> 'aaa' 要与前台的 name 保持一致 console.log(a) console.log(b) console.log(arguments) }) // 'aaa'事件名与前台的一致 setInterval(function(){ sock.emit('bbb', '服务器发来的数据') // name -> 'bbb' 要与前台的 name 保持一致 }, 2000) })2. 客户端<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <script src='http://localhost:8080/socket.io/socket.io.js'></script> <!-- 其实你的本地目录并不存在 XXX/socket.io/socket.io.js 文件,当向后台发送请求后,socket会判断req.url,做一个类似于下面的处理,读取 socket-io.js: if(req.url == '/socket.io/socket.io.js'){ fs.readFile('node_modules/socket.io-client/dist/socket-io.js') } 当然我们也可以直接将socket-io.js复制出来,直接用script引用,但是如果socket更新后我们的代码可能不是最新版,会出现一些问题。 因此不建议这样使用:<script src='./socket.io.js'></script> --> <script> let sock = io.connect('ws://localhost:8080/') // 这里是 ws 协议,不是 http 协议 // sock.emit // sock.on sock.emit('aaa', 'maruihua', 5 ) sock.on('bbb', data => console.log(data)) </script></head><body></body></html>上面的代码直接复制下来就能使用。怎么运行nodejs服务我就不再讲了啊。怎么样,是不是特别简单妈妈再也不用担心我的学习了~ ...

May 29, 2019 · 1 min · jiezi

今日头条网红题-????-async-functions-和-promises哪个更快

题目如下 async function async1() { console.log('async1 start') await async2() console.log('async1 end')}async function async2() { console.log('async2')}console.log('script start')setTimeout(function() { console.log('setTimeout') }, 0) async1()new Promise(function(resolve) { console.log('promise1') resolve()}).then(function() { console.log('promise2')})console.log('script end')而v8和node10产出的结果有所不同。 v8运行结果???? node10运行结果???? 先说下async/await原理???? async 声明的函数,其返回值必定是 promise 对象,如果没有显式返回 promise 对象,也会用 Promise.resolve() 对结果进行包装,保证返回值为 promise 类型await 会先执行其右侧表达逻辑(从右向左执行),并让出主线程,跳出 async 函数,而去继续执行 async 函数外的同步代码如果 await 右侧表达逻辑是个 promise,让出主线程,继续执行 async 函数外的同步代码,等待同步任务结束后,且该promise 被 resolve 时,继续执行 await 后面的逻辑如果 await 右侧表达逻辑不是 promise 类型,那么 async 函数之外的同步代码执行完毕之后,会回到 async函数内部,继续执行 await 之后的逻辑-- 摘自 LucasHC ...

May 29, 2019 · 1 min · jiezi

node将geojson转shp返回给前端

node将geojson转shp需要调用[ogr2ogr][1]库来实现,在调用ogr2ogr库时,因为其通过调用gdal的工具来实现将geojson转shp,所以需要安装gdal并配置环境变量,详情可参考此链接。环境配置完,可以进行代码实现了。 首先引入ogr2ogr库 const ogr2ogr = require('ogr2ogr')生成shp文件压缩包 // 声明一个geojson变量也可以是geojson文件目录 var geojson = { type: 'FeatureCollection', features: [ { type: 'Feature', geometry } ] } // shp保存目录 const zipPath = './export/shpfile.zip' // 创建文件写入流 var file = fs.createWriteStream(zipPath) // 调用ogr2ogr进行转化 var ogr = ogr2ogr(geojson).project('EPSG:4326') .format('ESRI Shapefile') .skipfailures() .stream() ogr.pipe(file) 然后将shp压缩文件传给前端,这里可以通过不同的方法进行传递(1) 通过sendFile直接进行传递 var resPath = path.join(__dirname, '..', zipPath) res.sendFile(resPath)(2)通过流的方式进行传递 var resPath = path.join(__dirname, '..', zipPath) // 文件写入完成触发事件 file.on('finish', function() { res.set({ 'Content-Type': 'application/zip', 'Content-Disposition': 'attachment; filename=' + encodeURI(name) + '.zip', 'Content-Length': fs.statSync(zipPath).size }) let fReadStream = fs.createReadStream(zipPath) fReadStream.pipe(res) fReadStream.on('end', function() { fs.unlinkSync(resPath) }) fReadStream.on('error', function(err) { console.log(err) }) })最后是前端发送请求接收的代码 axios.post('http://localhost:3000/jsontoshp', { responseType: 'blob' }).then(res => { const blobUrl = URL.createObjectURL(res.data) const a = document.createElement('a') a.style.display = 'none' a.download = '文件名称' a.href = blobUrl a.click() URL.revokeObjectURL(blobUrl) })这里需要注意的地方是前端发送请求时需要设置一个参数responseType: 'blob',这里用到了Blob对象,这里是从服务器接收到的文件流创建blob对象并使用该blob 创建一个指向类型数组的URL,将该url作为a标签的链接目标,然后去触发a标签的点击事件从而文件下载。 ...

May 29, 2019 · 1 min · jiezi

mongoose无字段模糊查询

参数const val = req.body.val;const reg = new RegExp(val, 'i');Model.find({ $or: [ { key1: {$regex: reg} }, { key2: {$regex: reg} }, { key3: {$regex: reg} } ]}).sort({timestamp: -1}).exec((err, docs) => { // do something})

May 29, 2019 · 1 min · jiezi

优质文章汇总长期更新

阿里蒋航:Serverless 将使前后端从分离再度走向融合阅读原文

May 28, 2019 · 1 min · jiezi

nodejs-原生的post和get请求

把今天学到的东西记录一下const http = require('http')// querystring 模块提供用于解析和格式化 URL 查询字符串的实用工具const querystring = require('querystring')const server = http.createServer((req, res) => { // 请求的方式 const method = req.method // 获取完整请求url const url = req.url // url路径 const path = url.split('?')[0] // 解析 get请求的参数 为?后面 所以数组下标为1 const getParams = querystring.parse(url.split('?')[1]) // 设置返回的格式 json格式 res.setHeader('Content-type','application/json') // 返回的数据 const resData = { method, url, path, getParams } // 0.如果是Post请求 if (method === 'POST'){ // 接收数据 let postData = '' // chunk为一点点数据,逐渐积累 req.on('data', chunk => { postData += chunk.toString() }) req.on('end', () => { resData.postData = postData // 在这里返回 因为是异步 res.end( // 返回json字字符串 JSON.stringify(resData) ) }) } // 1. 如果是get请求 if (method === 'GET'){ // 返回 res.end( // 返回json字字符串 JSON.stringify(resData) ) }})server.listen(8000)

May 27, 2019 · 1 min · jiezi

Webpack-HMR-原理解析

Hot Module Replacement(简称 HMR)包含以下内容: 热更新图热更新步骤讲解 第一步:webpack 对文件系统进行 watch 打包到内存中webpack-dev-middleware 调用 webpack 的 api 对文件系统 watch,当文件发生改变后,webpack 重新对文件进行编译打包,然后保存到内存中。 webpack 将 bundle.js 文件打包到了内存中,不生成文件的原因就在于访问内存中的代码比访问文件系统中的文件更快,而且也减少了代码写入文件的开销。 这一切都归功于memory-fs,memory-fs 是 webpack-dev-middleware 的一个依赖库,webpack-dev-middleware 将 webpack 原本的 outputFileSystem 替换成了MemoryFileSystem 实例,这样代码就将输出到内存中。 webpack-dev-middleware 中该部分源码如下: // compiler // webpack-dev-middleware/lib/Shared.js var isMemoryFs = !compiler.compilers && compiler.outputFileSystem instanceof MemoryFileSystem; if(isMemoryFs) { fs = compiler.outputFileSystem; } else { fs = compiler.outputFileSystem = new MemoryFileSystem(); }第二步:devServer 通知浏览器端文件发生改变在启动 devServer 的时候,sockjs) 在服务端和浏览器端建立了一个 webSocket 长连接,以便将 webpack 编译和打包的各个阶段状态告知浏览器,最关键的步骤还是 webpack-dev-server 调用 webpack api 监听 compile的 done 事件,当compile 完成后,webpack-dev-server通过 _sendStatus 方法将编译打包后的新模块 hash 值发送到浏览器端。 ...

May 27, 2019 · 3 min · jiezi

深入解析-Nodejs-的-consolelog

翻译:疯狂的技术宅原文:https://www.twilio.com/blog/g...本文首发微信公众号:前端先锋欢迎关注,每天都给你推送新鲜的前端技术文章 当你开始用 JavaScript 进行开发时,可能学到的第一件事就是如何用 console.log 将内容记录到控制台。如果你去搜索如何调试 JavaScript,会发现数百篇博文和 StackOverflow 文章都会简单的告诉你用 console.log。因为这是一种很常见的做法,我们甚至会在代码中使用像 no-console 这样的 linter 规则来确保不会留下意外的日志信息。但是如果我们真的想要去记录某些内容呢? 在本文中,我们将梳理各种情况下要记录的日志信息,Node.js 中 console.log 和console.error之间的区别是什么,以及如何在不发生混乱的情况下把你库中的日志记录输出到用户控制台。 console.log(`Let's go!`);理论先行:Node.js 的重要细节虽然你可以在浏览器和 Node.js 中使用 console.log 或 console.error,但在使用 Node.js 时要记住一件重要的事。当你在 Node.js 中将以下代码写入名为 index.js 的文件中时: console.log('Hello there');console.error('Bye bye');并用 node index.js 在终端中执行它,你会直接看到两者的输出: 虽然它们看起来可能一样,但实际上系统对它们的处理方式是不同的。如果你查阅 Node.js 文档的 console部分,会看到 console.log 是输出到 stdout 而 console .error 用的是 stderr。 每个进程都有三个可用的默认 stream。那些是 stdin,stdout 和 stderr。 stdin 流用来在处理进程的输入。例如按下按钮或重定向输出。 stdout 流用于程序的输出。最后 stderr 用于错误消息。如果你想了解为什么会有 stderr 存在,以及应该在什么时候使用它,可以查看这篇文章。 简而言之,这允许我们在 shell 中使用重定向(>)和管道(|)来处理错误和诊断信息,它们是与程序的实际输出结果是分开的。虽然 > 允许我们将命令的输出重定向到文件中,但是 2> 允许我们将 stderr 的输出重定向到文件中。例如,下面这个命令会将 “Hello there” 传给一个名为 hello.log 的文件并把 “Bye bye” 传到一个名为 error.log 的文件中。 ...

May 27, 2019 · 4 min · jiezi

搭建npm私库超简单

原因我搭私库的原因很简单,目前正在开发一个组件库,提供给公司内部使用,我不想去注册npm,也不想等待npm的审核,只想要有个仓库快速测试发布自己的npm包。 怎么搭目前最方便的方案就是verdaccio,搭建非常方便,一般就几分钟就搞定了,需要的工具: 安装nodejs和npm全局安装verdaccioshh和pm2(非必须,如果你要部署到远程服务器的话)接下来详细介绍搭建的步骤。 全局安装verdaccio安装verdaccio之前,我默认大家都已经安装了nodejs和npm环境,这个就不再赘述了。如果是本地搭建的话,直接进行下面的操作就可以了。如果是在远程服务器搭建,通过ssh连接远程服务器就行。 # 全局安装npm install verdaccio - g修改verdaccio配置修改配置的目的就是让我们的私库可以通过公网的ip访问,首先查看npm全局安装包的所在位置: npm root -g/usr/local/Cellar/node/8.4.0/lib/node_modules其中/usr/local/Cellar/node/8.4.0/lib/node_modules便是我们npm包全局安装的地址。按以下命名查找配置文件所在的位置然后 vim default.yaml配置情况如下 ## 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/verdaccio/verdaccio/tree/master/conf## path to a directory with all packagesstorage: ./storage# path to a directory with plugins to includeplugins: ./pluginsweb: # WebUI is enabled as default, if you want disable it, just uncomment this line #enable: false title: Verdaccioauth: htpasswd: file: ./htpasswd # Maximum amount of users allowed to register, defaults to "+inf". # You can set this to -1 to disable registration. #max_users: 1000# a list of other known repositories we can talk touplinks: npmjs: url: https://registry.npmjs.org/packages: '@*/*': # scoped packages access: $all publish: $authenticated proxy: npmjs '**': # 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# You can specify HTTP/1.1 server keep alive timeout in seconds for incomming connections.# A value of 0 makes the http server behave similarly to Node.js versions prior to 8.0.0, which did not have a keep-alive timeout.# WORKAROUND: Through given configuration you can workaround following issue https://github.com/verdaccio/verdaccio/issues/301. Set to 0 in case 60 is not enought.server: keepAliveTimeout: 60# To use `npm audit` uncomment the following sectionmiddlewares: audit: enabled: true# log settingslogs: - {type: stdout, format: pretty, level: http} #- {type: file, path: verdaccio.log, level: info}# listenlisten: 0.0.0.0:4873最后一行为新增的配置, 用于支持外网ip访问 ...

May 27, 2019 · 2 min · jiezi

StepByStep一周面试题-答案汇总-01

关于【Step-By-Step】不积跬步无以至千里。 Step-By-Step (点击进入项目) 是我于 2019-05-20 开始的一个项目,项目愿景:一步一个脚印,量变引起质变。 Step-By-Step 仅会在工作日发布面试题,主要考虑到部分小伙伴平时工作较为繁忙,或周末有出游计划。每个周末我会仔细阅读大家的答案,整理最一份较优答案出来,因本人水平有限,有误的地方,大家及时指正。参与答题的小伙伴,可以对比自己的回答。 答题不是目的,不希望大家仅仅是简单的搜索答案,复制粘贴到issue下。更多的是希望大家及时查漏补缺 / 巩固相关知识。 已经过去的一周中,大家回答的都非常认真,希望小伙伴们能够一如既往的坚持。同样也欢迎更多的小伙伴一起参与进来,如果想 加群学习,扫码二维码 (点击查看),添加我为好友,验证信息为加入组织,我拉你进群。 1.如何正确判断this的指向?(2019-05-20)如果用一句话说明 this 的指向,那么即是: 谁调用它,this 就指向谁。 但是仅通过这句话,我们很多时候并不能准确判断 this 的指向。因此我们需要借助一些规则去帮助自己: this 的指向可以按照以下顺序判断: 1. 全局环境中的 this浏览器环境:无论是否在严格模式下,在全局执行环境中(在任何函数体外部)this 都指向全局对象 window; node 环境:无论是否在严格模式下,在全局执行环境中(在任何函数体外部),this 都是空对象 {}; 2. 是否是 new 绑定如果是 new 绑定,并且构造函数中没有返回 function 或者是 object,那么 this 指向这个新对象。如下: 构造函数返回值不是 function 或 object。function Super(age) { this.age = age;}let instance = new Super('26');console.log(instance.age); //26构造函数返回值是 function 或 object,这种情况下 this 指向的是返回的对象。function Super(age) { this.age = age; let obj = {a: '2'}; return obj;}let instance = new Super('hello');console.log(instance.age); //undefined你可以想知道为什么会这样?我们来看一下 new 的实现原理: ...

May 27, 2019 · 4 min · jiezi

前端核心工具yarnnpmcnpm三者如何优雅的在一起使用

一位用不好包管理器的前端,是一个入门级前端,一个用不好webpack的前端,是一个初级前端三个包管理器是可以一起用的,只要你够胆大心细,就没任何问题!推荐两篇文章 手写优化版React脚手架手写Vue的脚手架前端性能优化不完全手册在javeScript编写中,我们尽量不要定义全局变量,封装函数尽量不要有副作用,因为全部变量的查询时间会比局部变量的查询慢,更是考虑在Node的环境中无法被垃圾回收的问题老规矩 先看原理npmnpm 是 Node.js 能够如此成功的主要原因之一。npm 团队做了很多的工作,以确保 npm 保持向后兼容,并在不同的环境中保持一致。npm是围绕着 语义版本控制(semver)的思想而设计。给定一个版本号:主版本号.次版本号.补丁版本号, 以下这三种情况需要增加相应的版本号:主版本号: 当API发生改变,并与之前的版本不兼容的时候次版本号: 当增加了功能,但是向后兼容的时候补丁版本号:当做了向后兼容的缺陷修复的时候npm 2 会安装每一个包所依赖的所有依赖项。如果我们有这么一个项目,它依赖项目A,项目A依赖项目B,项目B依赖项目C,那么依赖树将如下所示: 这个结构可能会很长。这对于基于Unix的操作系统来说只不过是一个小烦恼,但对于Windows来说却是个破坏性的东西,因为有很多程序无法处理超过260个字符的文件路径名。npm 3采用了扁平依赖关系树来解决这个问题,所以我们的3个项目结构现在看起来如下所示:存了已经下载的每个版本的压缩包。本地缓存的内容可以通过npm cache ls命令进行查看。本地缓存的设计有助于减少安装时间。 这样,一个原来很长的文件路径名就从./node_modules/package-A/node_modules/package-B/node-modules/some-file-name-in-package-c.js变成了/node_modules/some-file-name-in-package-c.js。这种方法的缺点是,npm必须首先遍历所有的项目依赖关系,然后再决定如何生成扁平的node_modules目录结构。npm必须为所有使用到的模块构建一个完整的依赖关系树,这是一个耗时的操作,是npm安装速度慢的一个很重要的原因。想当然的以为每次运行npm install命令时,NPM都得从互联网上下载所有内容。但是,npm是有本地缓存的,它保存了已经下载的每个版本的压缩包。本地缓存的内容可以通过npm cache ls命令进行查看。本地缓存的设计有助于减少安装时间。cnpmcnpm跟npm用法完全一致,只是在执行命令时将npm改为cnpm。npm安装插件是从国外服务器下载,受网络影响大,可能出现异常,如果npm的服务器在中国就好了,于是淘宝团队干了这事。来自官网:“这是一个完整 npmjs.org 镜像,你可以用此代替官方版本(只读),同步频率目前为 10分钟 一次以保证尽量与官方服务同步。”官方地址:http://npm.taobao.org安装: npm install -g cnpm --registry=https://registry.npm.taobao.orgYarnYarn一开始的主要目标是解决上一节中描述的由于语义版本控制而导致的npm安装的不确定性问题。虽然可以使用npm shrinkwrap来实现可预测的依赖关系树,但它并不是默认选项,而是取决于所有的开发人员知道并且启用这个选项。Yarn采取了不同的做法。每个yarn安装都会生成一个类似于npm-shrinkwrap.json的yarn.lock文件,而且它是默认创建的。除了常规信息之外,yarn.lock文件还包含要安装的内容的校验和,以确保使用的库的版本相同。yarn是经过重新设计的崭新的npm客户端,它能让开发人员并行处理所有必须的操作,并添加了一些其他改进。运行速度得到了显著的提升,整个安装时间也变得更少像npm一样,yarn使用本地缓存。与npm不同的是,yarn无需互联网连接就能安装本地缓存的依赖项,它提供了离线模式。允许合并项目中使用到的所有的包的许可证通常情况下不建议通过npm进行安装。npm安装是非确定性的,程序包没有签名,并且npm除了做了基本的SHA1哈希之外不执行任何完整性检查,这给安装系统程序带来了安全风险。(作者曾经在一个上百个依赖包的项目中使用npm丢包过,代价非常大,泪水不自觉掉下来)首先看一次非常失败的包下载 竟然是从全局读取的资源(不配置webpack别名是因为就这一个路径这么长) 首先我们从原理入手 ,我们使用 npm init, yarn init ,cnpm init 的时候 发生了什么 ?生成package.json文件json文件内部声明初始的版本信息、作者信息等,如果你是需要上传到npm上作为命令行工具,应该配置bin等声明入口字段那么当我们使用npm i , yarn add ,cnpm i 操作时候会发生什么 ?首先会根据你的命令行后缀是否加了 -g 或者global判断,下载的包是放在全局的环境,还是当前package.json文件对应的node_module文件夹目录下(这点尤其重要,有人出BUG,就是因为在用npm , cnpm时候没有注明添加的是全局依赖还是本地依赖,导致json文件上没有对应的包名,项目永远起不来)然后根据你的指令--save 或者-D、--save -dev判断是开发依赖还是线上依赖,其实这点在yarn上没有问题,因为yarn有自己的一套检查包完整性的机制,不会丢包,还会自动判断添加依赖,出bug一般是cnpm和npm,没有明确-g或者--save,npm只有检查程序员签名的机制,没有检查包完整性的机制,也不会自动添加依赖到json文件,那么就会出现丢包的假象,所以建议主要使用yarnyarn和npm对比 npm的缺点汇总:同一个项目,安装的时候无法保持一致性。由于package.json文件中版本号的特点,下面三个版本号在安装的时候代表不同的含义。 "5.0.3", "~5.0.3", "^5.0.3"“5.0.3”表示安装指定的5.0.3版本,“~5.0.3”表示安装5.0.X中最新的版本,“^5.0.3”表示安装5.X.X中最新的版本。这就麻烦了,常常会出现同一个项目,有的同事是OK的,有的同事会由于安装的版本不一致出现bug。安装的时候,包会在同一时间下载和安装,中途某个时候,一个包抛出了一个错误,但是npm会继续下载和安装包。因为npm会把所有的日志输出到终端,有关错误包的错误信息就会在一大堆npm打印的警告中丢失掉,并且你甚至永远不会注意到实际发生的错误。yarn的优点速度快 。速度快主要来自以下两个方面:并行安装:无论 npm 还是 Yarn 在执行包的安装时,都会执行一系列任务。npm 是按照队列执行每个 package,也就是说必须要等到当前 package 安装完成之后,才能继续后面的安装。而 Yarn 是并行执行所有任务,提高了性能。离线模式:如果之前已经安装过一个软件包,用Yarn再次安装时之间从缓存中获取,就不用像npm那样再从网络下载了。 ...

May 27, 2019 · 1 min · jiezi

nodejs命令行教程

node 命令行教程本文先介绍原生的node.js实现命令行交互,了解原生的api,然后通过commander.js和inquirer.js实现一个完整的交互命令行工具。项目地址 process (进程)process对象是一个全局变量,它提供了当前node.js进程的信息并对其控制。因为其是一个全局变量所以无需在文件中引入。 需要用到的几个api process.argvprocess.cwd()process.stdinprocess.stdoutprocess.stdin.resume()process.argvprocess.argv属性返回一个数组。数组的第一个值是process.execPath,第二个是正在执行的JavaScript的文件路径,其余参数为其它命令参数,这是我们来自定义命令的关键。 示例新建argv.js // argv.jsconsole.log(process.argv)执行node命令 node argv.js node argv.js --name zhu## 输出[ '/usr/local/bin/node', ## 执行当前脚本的Node二进制文件的绝对路径 '/Users/zhuhuilong/Node/Book/argv.js', ## 文件的绝对路径 '--name', ## 其余参数 'zhu' ]接收自定义的命令参数进行处理输出 // argv.jsconsole.log(process.argv)let argvs = process.argvlet param = argvs.splice(2)if(param[0] && param[0] == '--name'){ if(param[1]){ console.log(`hello ${param[1]}`) }else{ console.log('请输入name') }}运行argv.js node argv.js --name zhu## 输出[ '/usr/local/bin/node', '/Users/zhuhuilong/Node/Book/argv.js', '--name', 'zhu' ]hello zhuparam [ '--name', 'zhu' ]process.stdin与process.stdoutprocess.stdin(标准输入) process.stdin 属性返回连接到 stdin (fd 0) 的流。 它是一个 net.Socket 流(也就是双工流),除非 fd 0 指向一个文件,在这种情况下它是一个可读流。 ...

May 27, 2019 · 3 min · jiezi

Nodejs-多进程处理CPU密集任务

Node.js 单线程与多进程大家都知道 Node.js 性能很高,是以异步事件驱动、非阻塞 I/O 而被广泛使用。但缺点也很明显,由于 Node.js 是单线程程序,如果长时间运算,会导致 CPU 不能及时释放,所以并不适合 CPU 密集型应用。 当然,也不是没有办法解决这个问题。虽然 Node.js 不支持多线程,但是可创建多子进程来执行任务。Node.js 提供了 child_process 和 cluster 两个模块可用于创建多子进程 下面我们就分别使用单线程和多进程来模拟查找大量斐波那契数进行 CPU 密集测试 以下代码是查找 500 次位置为 35 的斐波那契数(方便测试,定了一个时间不需要太长也不会太短的位置) 单线程处理代码:single.jsfunction fibonacci(n) { if (n == 0 || n == 1) { return n; } else { return fibonacci(n - 1) + fibonacci(n - 2); }}let startTime = Date.now();let totalCount = 500;let completedCount = 0;let n = 35;for (let i = 0; i < totalCount; i++) { fibonacci(n); completedCount++; console.log(`process: ${completedCount}/${totalCount}`);}console.log("???? ???? ???? ???? ???? ???? ???? ???? ???? ????");console.info(`任务完成,用时: ${Date.now() - startTime}ms`);console.log("???? ???? ???? ???? ???? ???? ???? ???? ???? ????");执行node single.js 查看结果 ...

May 26, 2019 · 2 min · jiezi

VueExpressMysql-全栈初体验

前言原文地址 曾几何时,你有没有想过一个前端工程师的未来是什么样的?这个时候你是不是会想到了一个词”前端架构师“,那么一个合格的前端架构只会前端OK吗?那当然不行,你必须具备全栈的能力,这样才能扩大个人的形象力,才能升职加薪,才能迎娶白富美,才能走向人生巅峰... 最近我在写一些后端的项目,发现重复工作太多,尤其是框架部分,然后这就抽空整理了前后端的架子,主要是用的Vue,Express,数据存储用的Mysql,当然如果有其他需要,也可以直接切换到sqlite、postgres或者mssql。 先献上项目源码地址 项目项目以todolist为????,简单的实现了前后端的CURD。 后端技术栈框架 Express热更新 nodemon依赖注入 awilix数据持久化 sequelize部署 pm2前端技术栈vue-routervuexaxiosvue-class-componentvue-property-decoratorvuex-class项目结构先看项目架构,client为前端结构,server为后端结构 |-- express-vue-web-slush |-- client | |-- http.js // axios 请求封装 | |-- router.js // vue-router | |-- assets // 静态资源 | |-- components // 公用组件 | |-- store // store | |-- styles // 样式 | |-- views // 视图 |-- server |-- api // controller api文件 |-- container // ioc 容器 |-- daos // dao层 |-- initialize // 项目初始化文件 |-- middleware // 中间件 |-- models // model层 |-- services // service层代码介绍前端代码就不多说,一眼就能看出是vue-cli生成的结构,不一样的地方就是前端编写的代码是以Vue Class的形式编写的,具体细节请见从react转职到vue开发的项目准备 ...

May 26, 2019 · 5 min · jiezi

nodejs-request-module里的json参数的一个坑

今天工作的时候遇到一个坑,在客户端用nodejs给服务器发送HTTP请求,服务器老是报错:In the context of Data Services an unknown internal server error occurred 经过服务器端调试发现,服务器根本就没有正确解析出这个请求的content-type。在postman里能工作的场景下,正确解析出的content-type是multipart/mixed: 而我的nodejs代码里明明指定了这个content-type的啊? 经过一行行代码分析,最后发现问题出在第63行的json字段的值。我错误的赋成了true。 这个参数起什么作用?调试一下就知道了。如果为true,进入第403行。 如果请求内部有entity的content-type不是application/x-www-form-urlencoded, 则进入第1293行。 safeStringify的实现逻辑就是浏览器原生的JSON.stringify, 把应用程序传入的json对象序列化成字符串。但是我的代码里,传入request module的请求体是一个字符串,而json参数设的又是true,所以逻辑上就不对了。把这个json参数的值改为false后,一切正常。 要获取更多Jerry的原创文章,请关注公众号"汪子熙":

May 25, 2019 · 1 min · jiezi

使用nodejs实现OData的batch操作在Marketing-Cloud里读取contact信息

我们先来看看Marketing Cloud系统里的contact信息:一共1218374条数据。 我们用如下的nodejs代码通过OData来获取这些数据: var request = require('request');var config = require("./mcConfig");var url = config.getContactBatchURL;var sBody = "--batch_c914-a60c-1877" + "\n" + "Content-Type: application/http" + "\n" + "Content-Transfer-Encoding: binary" + "\n" + "\n" + "GET InteractionContacts?sap-client=100&$skip=0&$top=2&$select=ImageURL%2cName%2cContactLevelName%2cCountryName%2cCity%2cEMailAddress%2cPhoneNumber%2cMobilePhoneNumber%2cCorporateAccountName%2cInteractionContactUUID%2cRelationship%2cType&$inlinecount=allpages HTTP/1.1" + "sap-cancel-on-close: true" + "\n" + "Cache-Control: max-age=360" + "\n" + "sap-contextid-accept: header" + "\n" + "Accept: application/json" + "\n" + "Accept-Language: en" + "\n" + "DataServiceVersion: 2.0" + "\n" + "MaxDataServiceVersion: 2.0" + "\n" + "\n" + "\n" + "--batch_c914-a60c-1877--";var getContactOptions = { url: url, method: "POST", json:false, headers: { "content-type": "multipart/mixed;boundary=batch_c914-a60c-1877", 'Authorization': 'Basic ' + new Buffer(config.user + ":" + config.password).toString('base64') }, body: sBody};function getContact() { return new Promise(function(resolve,reject){ var requestC = request.defaults({jar: true}); console.log("Step1: get contact via url: " + url ); requestC(getContactOptions,function(error,response,body){ if( error){ console.log("error occurred: " + error); reject(error); } console.log("response:" + body); var nStartIndex = body.indexOf("{"); var nLastIndex = body.lastIndexOf("}"); if( nStartIndex < 0 || nLastIndex < 0) return; var sPayload = body.substring(nStartIndex, ++nLastIndex); resolve(JSON.parse(sPayload)); }); });}function displayResult(oResult){ console.log(oResult);}getContact().then(displayResult);使用node命令直接执行这个.js文件: ...

May 25, 2019 · 1 min · jiezi

使用nodejs对Marketing-Cloud的contact主数据进行修改操作

假设在Marketing Cloud有这样一个contact主数据: 现在需求是使用编程语言比如nodejs修改这个contact实例的高亮属性。 代码如下: var config = require("./mcConfig");var request = require('request');var url = config.tokenURL;console.log("user: " + config.user + " password: " + config.password); var getTokenOptions = { url: url, method: "GET", json:true, headers: { 'Authorization': 'Basic ' + new Buffer(config.user + ":" + config.password).toString('base64'), "content-type": "application/json", "x-csrf-token" :"fetch" }};function getToken() { return new Promise(function(resolve,reject){ var requestC = request.defaults({jar: true}); console.log("Step1: get csrf token via url: " + url ); requestC(getTokenOptions,function(error,response,body){ var csrfToken = response.headers['x-csrf-token']; if(!csrfToken){ reject({message:"token fetch error: " + error}); return; } console.log("Step1: csrf token got: " + csrfToken); resolve(csrfToken); }); });}function updateContact(token){ return new Promise(function(resolve, reject){ var sPostData = "--batch_1f7d-bd35-caed" + "\n" + "Content-Type: multipart/mixed; boundary=changeset_8f9e-9a44-9f9e" + "\n" + "\n" + "--changeset_8f9e-9a44-9f9e" + "\n" + "Content-Type: application/http" + "\n" + "Content-Transfer-Encoding: binary" + "\n" + "\n" + "MERGE Consumers('02000A21209F1EE99CDF1A1FC9AA8065')?sap-client=100 HTTP/1.1" + "\n" + "Cache-Control: max-age=360" + "\n" + "sap-contextid-accept: header" + "\n" + "Accept: application/json" + "\n" + "Accept-Language: en" + "\n" + "DataServiceVersion: 2.0" + "\n" + "MaxDataServiceVersion: 2.0" + "\n" + "x-csrf-token: fQ2Pwfmf0K_LVYoKV9QYUw==" + "\n" + "Content-Type: application/json" + "\n" + //"Content-Length: 215" + "\n" + "\n" + "{\"YY1_CustomerType_ENH\":\"Jerry测试1\"}" + "\n" + "--changeset_8f9e-9a44-9f9e--" + "\n" + "\n" + "--batch_1f7d-bd35-caed--"; var requestC = request.defaults({jar: true}); var createOptions = { url: config.updateContactURL, method: "POST", json:false, headers: { "content-type": "multipart/mixed;boundary=batch_1f7d-bd35-caed", 'x-csrf-token': token }, body:sPostData }; requestC(createOptions,function(error,response,data){ if(error){ reject(error.message); }else { debugger; console.log("Contact updated successfully"); resolve(data); } }); });}getToken().then(updateContact).catch((error) =>{ console.log("error: " + error.message);});我在nodejs代码里把需要更改的字段值赋为"Jerry测试1”: ...

May 25, 2019 · 2 min · jiezi

前端山泉-????

前端山泉 ????前端优质文章的搬运工 ???? github 持续更新,欢迎 ⭐???? CSSCSS 灵感 -- chokcocoiCSS -- chokcoco学习 BFC (Block Formatting Context) -- 网易考拉前端团队CSS网格布局(Grid)完全教程 -- 毛三十Moo-CSS -- MichealWayne???? JavaScriptAirbnb JavaScript 风格指南 -- lin-123JavaScript 开发者应懂的33个概念 -- stephentian30秒就能理解的 JavaScript 优秀代码 -- Chalarangelo冴羽的博客 -- mqyqingfeng过目不忘JS正则表达式 -- 全凭一口仙气儿活着???? ECMAScript2015+新手开发中常用ES6基础知识总结 -- 胡不归ECMAScript 6 入门 -- ruanyf《深入理解ES6》教程学习笔记 -- hyy1115ES6 完全使用手册 -- 冴羽八段代码彻底掌握 Promise -- 艾特老干部???? Vue.js剖析 Vue 实现原理 - 如何实现双向绑定 mvvm -- DMQVue.js 技术揭秘 -- ustbhuangyiVue.js 源码解析 -- answershutovue-router源码分析-整体流程 -- 滴滴出行·DDFEVuex 源码解析 -- 染陌同学vue-cli 源码分析 -- KuangPF???? React.jsReact.js 小书 -- huzidaha编写简洁漂亮,可维护的 React 应用 -- shimohq???? Webpack深入浅出Webpack -- gwuhaolinwebpack编译速度提升之DllPlugin -- _安歌你的Tree-Shaking并没什么卵用 -- 相学长Webpack 3 的新功能:Scope Hoisting -- 李名凯没有了CommonsChunkPlugin,咱拿什么来分包(译) -- easyhappyUglifyJS中文文档 -- LiPinghai???? RollupJS 打包工具 rollup ——完全入门指南 -- KainStar???? NodeJS七天学会 NodeJS -- nqdengNode.js 包教不包会 -- alsotang深入理解Node.js:核心思想与源码分析 -- yjhjstz如何通过饿了么 Node.js 面试 -- ElemeFE深入浅出 Nodejs 读书笔记 -- tw93???? TypeScriptTypeScript 入门教程 -- xcatliuTypeScript Handbook(中文版) -- zhongsp深入理解 TypeScript -- jkchao???? BabelBabel 手册 -- jamiebuilds???? HTTP《HTTP权威指南》每章的知识点总结 -- woai30231axios 实例应用及源码剖析 - xhr篇 -- ronffy深入浅出 axios 源码 -- 尼库尼库桑???? InterviewInterviewMap -- InterviewMap前端面试手册 -- yangshun???? OtherChrome 插件(扩展)开发全攻略 -- sxeiVSCode 插件开发全攻略配套 demo -- sxei哈哈~ 学不动了???? ...

May 25, 2019 · 1 min · jiezi

端到端测试神器Cypress进阶

前言官方文档:https://docs.cypress.io 目前只支持英文,好处是官方入门视频很多,对于英文水平不好的也很容易入手; 优缺点优点: 只要你学好js语法上应该不是很大问题获取dom类似jq,对前端同学来说是好处;缺点: 内置的GUI工具集成了谷歌内核,决定你只能是在谷歌浏览器上测试,但随着新版的Edge内核采用Chromium内核,这点缺点无伤大雅;为什么要用cypress请看:https://segmentfault.com/a/11... 看1,2,3点就可以; 入门废话不多说看了以上几点决定要不要入坑应该心里有数了; 安装网上绝大部分说采用npm i cypress -d 痛点在于内置了谷歌内核太大,每个项目都要安装太麻烦了,不便于管理,此处我们采用全局安装npm i -g cypress,既节约了磁盘空间又节约时间启动既然已经全局安装cypress的环境变量会配置到npm的环境变量中,npm的环境边练自然就在系统变量里面,cmd中输入cypress open 全局情况下打开就是慢点 直接拖入项目 点击进入项目,第一次回

May 25, 2019 · 1 min · jiezi

linux-下docker基于debian系统的安装node

docker下载(debian系统)1. sudo apt-get update 更新包2. sudo apt-get install \ apt-transport-https \ ca-certificates \ curl \ gnupg2 \ software-properties-common (安装包以允许apt通过HTTPS使用存储库) 3. sudo apt-get install docker-ce (获取最新版本的 Docker 安装包) 4. sudo service docker start(启动docker) 5. docker (测试是否安装成功)docker 学习笔记项目构建 创建一个文件夹设置好 packge.json 文件运行 npm install 安装nodejs所需的依赖文件创建 server.js 文件 这里负责要写的业务逻辑docker文件设置 创建一个名称为 Dockerfile 的文件并写入FROM node:(指定的版本号)创建应用目录 应用程序工作目录 WORKDIR /usr/src/app拷贝nodejs 的配置文件 COPY package*.json ./RUN npm install 下载node的相关依赖捆绑应用源 COPY . .设置端口号 EXPOSE 8080运行node CMD [ "npm", "start" ]创建dockerignore 文件<br/> ...

May 25, 2019 · 1 min · jiezi

koa-洋葱模型

分析1、首先这是koa2最简单的入门例子,我将通过这个入门例子来演示koa2的洋葱模型 const Koa = require('koa');const app = new Koa();app.use((ctx,next)=>{ console.log("第一个中间件执行"); next() ;});// 第二个中间件app.use((ctx,next)=>{ console.log("第二个中间件");})let r = app.listen(8080);console.log("server run on 8080");在这里面,app首先是调用了两次use,然后就开始监听端口, listen(...args) { debug('listen'); // 当客户端发送请求将会调用callback const server = http.createServer(this.callback()); return server.listen(...args); }因此use是核心: use(fn) { // ... 一些判断代码 this.middleware.push(fn); return this; }从上面可以看出这里将外部use的函数通过内部的一个middleware变量记录下来,然后就没了。 OK,现在当客户端发送请求的时候,内部会创建上下文对象,然后处理请求: callback() { const fn = compose(this.middleware); if (!this.listenerCount('error')) this.on('error', this.onerror); const handleRequest = (req, res) => { // 创建上下文 const ctx = this.createContext(req, res); // 处理请求 return this.handleRequest(ctx, fn); }; return handleRequest; }处理请求 ...

May 25, 2019 · 2 min · jiezi

Nodejs-究竟是什么

翻译:疯狂的技术宅原文: https://medium.freecodecamp.o...本文首发微信公众号:前端先锋欢迎关注,每天都给你推送新鲜的前端技术文章 Node.js 是一个 JavaScript 运行时环境。听起来还不错,不过这究竟意味着什么?它又是如何运作的? Node 运行时环境包含执行 JavaScript 程序所需要的一切。 如果你了解 Java 的话,会发现它们有点像。 JavaScript 原来是只能在浏览器中运行的,当把它扩展成为可以在你的计算机上作为独立的程序运行时,Node.js 就出现了。 现在你可以用 JavaScript 做更多的事情,而不仅仅是用在网站的互动和特效上。 JavaScript 现在能够去做其他脚本语言(如Python)可以执行的操作。 你 Chrome 浏览器中的 JavaScript 和 Node.js 都在 V8 引擎上运行。该引擎将你的 JavaScript 代码转换为更快的机器代码。机器代码是低级代码,计算机可以直接运行而无需先解释它。 为什么选择 Node.js?这是 Node.js 官方网站上给出的正式定义: Node.js®是基于 Chrome 的 V8 JavaScript 引擎构建的 JavaScript 运行时环境。Node.js 使用事件驱动的非阻塞 I/O模型,轻量且高效。 Node.js 的包生态系统 npm 是世界上最大的开源库生态系统。 我们在前面已经讨论过了这个定义的第一行:“Node.js®是基于 Chrome 的 V8 JavaScript 引擎构建的 JavaScript 运行时环境。” 现在让我们理解剩下的两行,这样我们就可以找出为什么 Node.js 如此受欢迎的原因。 I/O 指的是输入/输出。它可以是从读取/写入本地文件到向 API 发出 HTTP 的任何内容。 ...

May 24, 2019 · 2 min · jiezi

RabbitMQ高级特性消费端限流策略实现

应用范围为服务访问量突然剧增,原因可能有多种外部的调用或内部的一些问题导致消息积压,对服务的访问超过服务所能处理的最大峰值,导致系统超时负载从而崩溃。业务场景举一些我们平常生活中的消费场景,例如:火车票、机票、门票等,通常来说这些服务在下单之后,后续的出票结果都是异步通知的,如果服务本身只支持每秒1000访问量,由于外部服务的原因突然访问量增加到每秒2000并发,这个时候服务接收者因为流量的剧增,超过了自己系统本身所能处理的最大峰值,如果没有对消息做限流措施,系统在这段时间内就会造成不可用,在生产环境这是一个很严重的问题,实际应用场景不止于这些,本文通过RabbitMQ来讲解如果对消费端做限流措施。 消费端限流机制RabbitMQ提供了服务质量保证 (QOS) 功能,对channel(通道)预先设置一定的消息数目,每次发送的消息条数都是基于预先设置的数目,如果消费端一旦有未确认的消息,这时服务端将不会再发送新的消费消息,直到消费端将消息进行完全确认,注意:此时消费端不能设置自动签收,否则会无效。 在 RabbitMQ v3.3.0 之后,放宽了限制,除了对channel设置之外,还可以对每个消费者进行设置。 以下为 Node.js 开发语言 amqplib 库对于限流实现提供的接口方法 prefetch export interface Channel extends events.EventEmitter { prefetch(count: number, global?: boolean): Promise<Replies.Empty>; ...}prefetch 参数说明: number:每次推送给消费端 N 条消息数目,如果这 N 条消息没有被ack,生产端将不会再次推送直到这 N 条消息被消费。global:在哪个级别上做限制,ture 为 channel 上做限制,false 为消费端上做限制,默认为 false。建立生产端生产端没什么变化,和正常声明一样,关于源码参见rabbitmq-prefetch(Node.js客户端版Demo) const amqp = require('amqplib');async function producer() { // 1. 创建链接对象 const connection = await amqp.connect('amqp://localhost:5672'); // 2. 获取通道 const channel = await connection.createChannel(); // 3. 声明参数 const exchangeName = 'qosEx'; const routingKey = 'qos.test001'; const msg = 'Producer:'; // 4. 声明交换机 await channel.assertExchange(exchangeName, 'topic', { durable: true }); for (let i=0; i<5; i++) { // 5. 发送消息 await channel.publish(exchangeName, routingKey, Buffer.from(`${msg} 第${i}条消息`)); } await channel.close();}producer();建立消费端const amqp = require('amqplib');async function consumer() { // 1. 创建链接对象 const connection = await amqp.connect('amqp://localhost:5672'); // 2. 获取通道 const channel = await connection.createChannel(); // 3. 声明参数 const exchangeName = 'qosEx'; const queueName = 'qosQueue'; const routingKey = 'qos.#'; // 4. 声明交换机、对列进行绑定 await channel.assertExchange(exchangeName, 'topic', { durable: true }); await channel.assertQueue(queueName); await channel.bindQueue(queueName, exchangeName, routingKey); // 5. 限流参数设置 await channel.prefetch(1, false); // 6. 限流,noAck参数必须设置为false await channel.consume(queueName, msg => { console.log('Consumer:', msg.content.toString()); // channel.ack(msg); }, { noAck: false });}consumer();未确认消息情况测试在 consumer 中我们暂且将 channel.ack(msg) 注释掉,分别启动生产者和消费者,看看是什么情况? ...

May 23, 2019 · 2 min · jiezi

Node单线程-怎么理解单线程Node实现多线程

Node单线程的理解Node 是单线程的指的是 JavaScript 的执行是单线程的,但 Javascript 的宿主环境,无论是 Node 还是浏览器都是多线程的在 Node 启动后,会创建 v8 的实例,这个实例是多线程的(在活动监视器中看到的是多个线程)举例node的线程 主线程:编译、执行代码。编译/优化线程:在主线程执行的时候,可以优化代码。分析器线程:记录分析代码运行时间,为 Crankshaft 优化代码执行提供依据。垃圾回收的几个线程问题 对 cpu 利用不足某个未捕获的异常可能会导致整个程序的退出等解决:使node具有多线程的实力 假:多进程的方式来模拟多线程 cluster(多进程模型) cluster 实现了对 child_process 的封装,通过 fork 方法创建子进程的方式实现了多进程模优秀代表:pm2真:实验性质的模块 worker_threads (Node 10.5.0 的发布,官方才给出了一个 给 Node 提供真正的多线程能力)进程process和线程thread的区别进程:是资源(CPU、内存等)分配的基本单位它是程序执行时的一个实例。程序运行时系统就会创建一个进程,并为它分配资源,然后把该进程放入进程就绪队列,进程调度器选中它的时候就会为它分配CPU时间,程序开始真正运行线程:程序执行时的最小单位它是进程的一个执行流,是CPU调度和分派的基本单位一个进程可以由很多个线程组成,线程间共享进程的所有资源,线程由CPU独立调度执行,在多CPU环境下就允许多个线程同时运行。同样多线程也可以实现并发操作,每个请求分配一个线程来处理区别/优劣 进程是资源分配的最小单位,线程是程序执行的最小单位。进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点。但是多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。补充理解 计算机的核心是CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运行 一个车间开工的时候,其他车间都必须停工。背后的含义就是,单个CPU一次只能运行一个任务 进程:CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态一个车间里,可以有很多工人。他们协同完成一个任务 线程: 一个进程可以包括多个线程一个进程的内存空间是共享的,每个线程都可以使用这些共享内存。(每间房间的大小不同,有些房间最多只能容纳一个人,比如厕所。里面有人的时候,其他人就不能进去了)一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存 "互斥锁":防止多个线程同时读写某一块内存区域(一个防止他人进入的简单方法,就是门口加一把锁。先到的人锁上门,后到的人看到上锁,就在门口排队,等锁打开再进去)某些内存区域,只能供给固定数目的线程使用。 "信号量")(Semaphore),用来保证多个线程不会互相冲突(还有些房间,可以同时容纳n个人,比如厨房。也就是说,如果人数大于n,多出来的人只能在外面等着,就是在门口挂n把钥匙。进去的人就取一把钥匙,出来时再把钥匙挂回原处。后到的人发现钥匙架空了,就知道必须在门口排队等着了)操作系统设计(1)以多进程形式,允许多个任务同时运行; (2)以多线程形式,允许单个任务分成不同的部分运行; (3)提供协调机制,一方面防止进程之间和线程之间产生冲突,另一方面允许进程之间和线程之间共享资源

May 23, 2019 · 1 min · jiezi

前端必修课ES2017下的构建工具原理与实战

ES2017+,你不再需要纠结于复杂的构建工具技术选型。 也不再需要gulp,grunt,yeoman,metalsmith,fis3。 以上的这些构建工具,可以脑海中永远划掉。 100行代码,你将透视构建工具的本质。 100行代码,你将拥有一个现代化、规范、测试驱动、高延展性的前端构建工具。 在阅读前,给大家一个小悬念:什么是链式操作、中间件机制?如何读取、构建文件树?如何实现批量模板渲染、代码转译?如何实现中间件间数据共享。相信学完这一课后,你会发现————这些专业术语,背后的原理实在。。。太简单了吧! 构建工具体验:弹窗+uglify+模板引擎+babel转码...如果想立即体验它的强大功能,可以命令行输入npx mofast example,将会构建一个mofast-example文件夹。 进入文件后运行node compile,即可体验功能。 顺便说一句,npx mofast example命令行本身,也是用本课的构建工具实现的。——是不是不可思议? 本课程代码已在npm上进行发布,直接安装即可npm i mofast -D即可在任何项目中使用mofast,替代gulp/grunt/yeoman/metalsmith/fis3进行安装使用。 本课程github地址为: https://github.com/wanthering... 在学完课程后,你就可以提交PR,一起维护这个库,使它的扩展性越来越强! 第一步:搭建github/npm标准开发栈请搭建好以下环境: jest 测试环境eslint 格式标准化环境babel es2017代码环境或者直接使用npx lunz mofast 然后一路回车。 构建出的文件系统如下 ├── .babelrc├── .editorconfig├── .eslintrc.js├── .gitignore├── README.md├── circle.yml├── package.json├── src│   └── index.js├── test│   └── index.spec.js└── yarn.lock第二步: 搭建文件沙盒环境构建工具,都需要进行文件系统的操作。 在测试时,常常污染本地的文件系统,造成一些重要文件的意外丢失和修改。 所以,我们往往会为测试做一个“沙盒环境” 在package.json同级目录下,输入命令 mkdir __mocks__ && touch __mocks__/fs.js yarn add memfs -D yarn add fs-extra创建__mocks__/fs.js文件后,写入: const { fs } = require('memfs')module.exports = fs然后在测试文件index.spec.js的第一行写下: ...

May 23, 2019 · 5 min · jiezi

ubuntu-1404安装前端工程运行环境

nodejs安装直接使用apt-get安装,安装的版本非常低,甚至会影响后面yarn的安装,其中包含两个包,node和node-legacy。所以我们采用nodejs官方推荐的安装方式。 查看nodejs的版本此文章编写的时候,nodejs的stable版本为10.15.3。在远程控制台中输入: $sudo curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -$sudo apt-get install -y nodejs等待安装完成,查看版本看是否安装成功: node -vv10.14.0npm -v6.4.1表示安装成功了 安装yarnnpm安装成功,便可以直接通过npm安装yarn了 sudo npm install yarn -g查看yarn的版本 yarn -v1.16.0安装git直接通过apt-get安装 sudo apt-get install gitconfig git config --global user.name "your_name"git config --global user.email "your_email@mail.com"至此基于nodejs的服务器运行环境基本搭建完成,我们只需要在本地编写代码,将代码push到github等托管仓库中,然后在服务器clone下代码,便可运行了,至于pm2自动部署,nginx等就不继续聊了。

May 23, 2019 · 1 min · jiezi

使用Electronvue开发的客户端支付收款工具

Electron-vue开发的客户端支付收款工具目前实现了支付宝当面付的扫码支付功能、二维码支付功能,即主动扫和被动扫。测试请使用支付宝沙箱环境,支付宝是沙箱版。最终效果如下:前端页面使用阿里的组件,ant-design-vue通过node,使用nedb内存数据库进行本地数据存储安装文件支持自定义。生成的exe,安装过程如下 程序代码简述main.js import devtools from '@vue/devtools'import Vue from 'vue'import axios from 'axios'import App from './App'import router from './router'import store from './store'import db from './nedb'//订单表import Antd from 'ant-design-vue'import 'ant-design-vue/dist/antd.css'import alipayhelper from './alipayhelper'import moment from 'moment'//导入文件Vue.prototype.$moment = moment;//赋值使用Vue.prototype.$db = dbVue.prototype.alipayhelper = alipayhelper;Vue.use(Antd)if (!process.env.IS_WEB) Vue.use(require('vue-electron'))Vue.http = Vue.prototype.$http = axiosVue.config.productionTip = false/* eslint-disable no-new */new Vue({ components: { App }, router, store, template: '<App/>'}).$mount('#app')alipayhelper.js 里存储的支付宝收款方的APPID,pem路径下应用私钥。这些信息可以通过阿里官方申请,即可以在线收款 const path = require('path');const fs = require('fs');const moment = require('moment');const crypto = require('crypto');const electron = require('electron');const dataPath = (electron.app || electron.remote.app).getPath('userData');const home = (electron.app || electron.remote.app).getPath('home');const appData = (electron.app || electron.remote.app).getPath('appData');let ALI_PAY_SETTINGS = { APP_ID: '2016100100638328', APP_GATEWAY_URL: 'http://localhost',//用于接收支付宝异步通知 AUTH_REDIRECT_URL: 'xxxxxxx',//第三方授权或用户信息授权后回调地址。授权链接中配置的redirect_uri的值必须与此值保持一致。 //__dirname 获取当前目录,无法在生产模式assr 获取到路径 /* APP_PRIVATE_KEY_PATH: path.join(__dirname, 'pem', 'rsa_private_key.pem'),//应用私钥 APP_PUBLIC_KEY_PATH: path.join(__dirname, 'pem', 'rsa_public_key.pem'),//应用公钥 ALI_PUBLIC_KEY_PATH: path.join(__dirname, 'pem','ali_rsa_public_key.pem'),//阿里公钥*/ APP_PRIVATE_KEY_PATH: path.join(__static, '/pem/rsa_private_key.pem'),//应用私钥 APP_PUBLIC_KEY_PATH: path.join(__static, '/pem/rsa_public_key.pem'),//应用公钥 ALI_PUBLIC_KEY_PATH: path.join(__static, '/pem/ali_rsa_public_key.pem'),//阿里公钥 AES_PATH: path.join(__dirname, 'pem', 'remind', 'sandbox', 'aes.txt'),//aes加密(暂未使用) ALI_GATEWAY_URL: 'https://openapi.alipaydev.com/gateway.do?',//用于接收支付宝异步通知};

May 23, 2019 · 1 min · jiezi

30分钟用Nodejs构建一个API服务器

翻译:疯狂的技术宅原文:https://medium.freecodecamp.o...本文首发微信公众号:前端先锋欢迎关注,每天都给你推送新鲜的前端技术文章 Node.js 对初学者来说可能是令人望而却步的,其灵活的结构和缺乏严格的规范使它看起来很复杂。 本教程是 Node.js,Express 框架和 MongoDB 的快速指南,重点介绍基本的 REST 路由和基本的数据库交互。你将构建一个简单的 API 框架模版,然后可以将其用作任何应用。 本教程适用于:你应该对 REST API 和 CRUD 操作有基本的了解,还有基本的 JavaScript 知识。我用的是 ES6(主要是箭头函数),但并不是很复杂。 在本教程中,我们将为创建一个网络笔记应用的后端骨架 —— 类似于Google Keep,能够执行所有的四个CRUD操作:创建、读取、更新和删除。 配置如果你没有安装Node,请参阅此处。 创建一个新目录,运行 npm init,然后按照提示操作,把你的应用程序命名为“notable”(或者你可能喜欢的其他名字)。 npm init一旦完成,在你的目录中会有一个 package.json 文件。你可以开始安装项目所需的依赖项了。 我们将使用 Express 作为自己的框架,MongoDB 作为数据库,还有一个名为 body-parser 的包来帮助处理 JSON 请求。 npm install --save express mongodb@2.2.16 body-parser我还强烈建议将 Nodemon 安装为 dev 依赖项。这是一个非常简单的小包,可在文件被更改时自动重启服务器。 如果你运行: npm install --save-dev nodemon然后将以下脚本添加到 package.json: // package.json "scripts": { "dev": "nodemon server.js" },完整的 package.json 应如下所示: ...

May 23, 2019 · 5 min · jiezi

Docker-入门一基础使用

查看docker版本,确认docker已正确安装 $ docker --versionDocker version 18.09.2, build 6247962 1.查看images(镜像)$ docker images拉取一个ubuntu images $ docker pull ubuntu默认拉去最新版本的ubuntu镜像,当然也可以指定ubuntu $ docker pull ubuntu:16.04「:」之后的数字为镜像版本;拉取成功后该数字即为镜像的tag(标签):lastest、16.04 2.进入ubuntu镜像首先查看我们的拉取的镜像: $ docker images 输出: REPOSITORY TAG IMAGE ID CREATED SIZEubuntu latest 7698f282e524 6 days ago 69.9MBubuntu 16.04 2a697363a870 6 days ago 119MBubuntu即为我们刚拉取的镜像,继续执行:$ docker run -i -t ubuntu 若TAG不是latest,需要带上TAG:$ docker run -i -t ubuntu:16.04 -i:以交互模式运行容器,通常与 -t 同时使用 -t:为容器重新分配一个伪输入终端,通常与 -i 同时使用 成功进入Ubuntu终端 3.安装 nvm 与 node更新apt: $ apt-get update ...

May 22, 2019 · 1 min · jiezi

JavaScript-发布订阅模式

发布-订阅模式,看似陌生,其实不然。工作中经常会用到,例如 Node.js EventEmitter 中的 on 和 emit 方法;Vue 中的 $on 和 $emit 方法。他们都使用了发布订阅模式,让开发变得更加高效方便。一、 什么是发布-订阅模式定义发布-订阅模式其实是一种对象间一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都将得到状态改变的通知。 订阅者(Subscriber)把自己想订阅的事件注册(Subscribe)到调度中心(Event Channel),当发布者(Publisher)发布该事件(Publish Event)到调度中心,也就是该事件触发时,由调度中心统一调度(Fire Event)订阅者注册到调度中心的处理代码。 例子比如我们很喜欢看某个公众号号的文章,但是我们不知道什么时候发布新文章,要不定时的去翻阅;这时候,我们可以关注该公众号,当有文章推送时,会有消息及时通知我们文章更新了。 上面一个看似简单的操作,其实是一个典型的发布订阅模式,公众号属于发布者,用户属于订阅者;用户将订阅公众号的事件注册到调度中心,公众号作为发布者,当有新文章发布时,公众号发布该事件到调度中心,调度中心会及时发消息告知用户。 二、 如何实现发布-订阅模式?1. 实现思路创建一个对象在该对象上创建一个缓存列表(调度中心)on 方法用来把函数 fn 都加到缓存列表中(订阅者注册事件到调度中心)emit 方法取到 arguments 里第一个当做 event,根据 event 值去执行对应缓存列表中的函数(发布者发布事件到调度中心,调度中心处理代码)remove 方法可以根据 event 值取消订阅(取消订阅)once 方法只监听一次,调用完毕后删除缓存函数(订阅一次)2. demo1我们来看个简单的 demo,实现了 on 和 emit 方法,代码中有详细注释。 // 公众号对象let eventEmitter = {};// 缓存列表,存放 event 及 fneventEmitter.list = {};// 订阅eventEmitter.on = function (event, fn) { let _this = this; // 如果对象中没有对应的 event 值,也就是说明没有订阅过,就给 event 创建个缓存列表 // 如有对象中有相应的 event 值,把 fn 添加到对应 event 的缓存列表里 (_this.list[event] || (_this.list[event] = [])).push(fn); return _this;};// 发布eventEmitter.emit = function () { let _this = this; // 第一个参数是对应的 event 值,直接用数组的 shift 方法取出 let event = [].shift.call(arguments), fns = _this.list[event]; // 如果缓存列表里没有 fn 就返回 false if (!fns || fns.length === 0) { return false; } // 遍历 event 值对应的缓存列表,依次执行 fn fns.forEach(fn => { fn.apply(_this, arguments); }); return _this;};function user1 (content) { console.log('用户1订阅了:', content);};function user2 (content) { console.log('用户2订阅了:', content);};// 订阅eventEmitter.on('article', user1);eventEmitter.on('article', user2);// 发布eventEmitter.emit('article', 'Javascript 发布-订阅模式');/* 用户1订阅了: Javascript 发布-订阅模式 用户2订阅了: Javascript 发布-订阅模式*/3. demo2这一版中我们补充了一下 once 和 off 方法。 ...

May 22, 2019 · 5 min · jiezi

个人小程序接入支付解决方案

现状现在如果产品要接入支付,支付宝和微信都规定必须公司才能申请,对个人而言就没办法了。注册和维护一个公司的成本很高,还涉及到财务会计、纳税等,最后如果不需要了注销公司也会很麻烦。这对于刚起步的产品,或者个人开发者而言成本都很高。 解决方案那个人还有办法签约微信和支付宝的原生支付接口吗?有!XorPay.com 是微信和支付宝的支付服务商,可以代个人签约微信支付接口 和 支付宝当面付接口,支持 native / jsapi / 收银台 / 当面付 / h5 支付方式,资金由微信、支付宝官方结算,安全可靠。 接下来就介绍一下个人小程序接入支付的具体步骤。 第一步,注册 XorPay.com 提交资料签约微信和支付宝,开通接口第二步,参考小程序demo 或者 按文档对接XorPay 有现成可用的小程序demo代码,GitHub 链接 当然你可以按文档自己对接,小程序文档 最终效果如下: 在 app.json 中添加: "navigateToMiniProgramAppIdList": [ "wx6eeed4ca124a1abf" ]小程序跳转代码: wx.navigateToMiniProgram({ appId: 'wx6eeed4ca124a1abf', path: 'pages/index/index', extraData: { 'aid': '1', #aid 'name': 'XorPay充值', 'pay_type': 'jsapi', 'price': '0.02', 'order_id': 'm-5', 'notify_url': 'https://abc.com/notify', 'sign': md5.hexMD5('XorPay充值' + 'jsapi' + '0.02' + 'm-5' + 'https://abc.com/notify' + 'app secret'), }, envVersion: 'develop', fail(res) { wx.showToast({ title: res.errMsg, icon: 'none', }); }, success(res) { wx.showToast({ title: 'ok', icon: 'none', }); }, });支付成功或者取消,会跳回你的小程序,并携带参数: ...

May 22, 2019 · 1 min · jiezi

promise-与settime-执行顺序

记录一下工作中的零碎收获promise是javascript引擎内如任务,settime属于浏览器的API所以优先执行primise; var r = new Promise(function(resolve, reject){ console.log("a"); resolve() }); setTimeout(()=>console.log("d"), 0) r.then(() => console.log("c")); console.log("b")

May 22, 2019 · 1 min · jiezi

koa-router-多文件引入

背景koa-router路由越来越多,api下的router都要使用下面的方式引入,怎么才能方便快捷的将api下的所有文件都引入呢这次记录的就是如果将koa-router 一次性循环引入 const book = require('./app/api/v1/book')const classic = require('./app/api/v1/classic')// ...app.use(book.routes(), book.allowedMethods())app.use(classic.routes(), classic.allowedMethods())//...文件目录koa-demo/ |-api/ |-books.js |-classic.js |-users.js |-articles.js |-package.json |-app.js传统方式引入router app.js const Koa = require('koa')const app = new Koa()const book = require('./app/api/v1/book')const classic = require('./app/api/v1/classic')app.use(book.routes(), book.allowedMethods())app.use(classic.routes(), classic.allowedMethods())app.listen(3333)require-directory引入require-directory用来递归地迭代指定的目录,并返回这些模块。github随着文件增加,如何高效的开发就是我们要追求的事情了 首先 npm install require-directoryapp.js const Koa = require('koa')const app = new Koa()const Router = require('koa-router')// 使用require-directory加载路由文件夹下的所有routerconst requireDirectory = require('require-directory')// 将所有的路由加载上,自动加载代码const modules = requireDirectory(module, './api', { visit: whenLoadModule })function whenLoadModule(obj) { if (obj instanceof Router) { app.use(obj.routes(), obj.allowedMethods()) }}app.listen(3333)路由文件就按照传统的方式写就行books.js ...

May 22, 2019 · 1 min · jiezi

细说JS异步发展历程

知其然知其所以然,首先了解三个概念: 1.什么是同步? 所谓同步,就是在发出一个"调用"时,在没有得到结果之前,该“调用”就不返回。但是一旦调用返回,就得到返回值了。换句话说,就是由“调用者”主动等待这个“调用”的结果。此调用执行完之前,阻塞之后的代码执行。 2.什么是异步? "调用"在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在"调用"发出后,"被调用者"通过状态、通知来通知调用者,或通过回调函数处理这个调用。异步调用发出后,不影响后面代码的执行。 3.JavaScript 中为什么需要异步? 首先我们知道JavaScript是单线程的(即使新增了webworker,但是本质上JS还是单线程)。同步代码意味着什么呢?意味着有可能会阻塞,当我们有一个任务需要时间较长时,如果使用同步方式,那么就会阻塞之后的代码执行。而异步则不会,我们不会等待异步代码的之后,继续执行异步任务之后的代码。 概念了解完了,我们就要进入今天的正题了。首先大家思考一下:平时在工作中,主要使用了哪些异步解决方案,这些异步方案有什么优缺点? 异步最早的解决方案是回调函数,如事件的回调,setInterval/setTimeout中的回调。但是回调函数有一个很常见的问题,就是回调地狱的问题(稍后会举例说明); 为了解决回调地狱的问题,社区提出了Promise解决方案,ES6将其写进了语言标准。Promise一定程度上解决了回调地狱的问题,但是Promise也存在一些问题,如错误不能被try catch,而且使用Promise的链式调用,其实并没有从根本上解决回调地狱的问题,只是换了一种写法。 ES6中引入 Generator 函数,Generator是一种异步编程解决方案,Generator 函数是协程在 ES6 的实现,最大特点就是可以交出函数的执行权,Generator 函数可以看出是异步任务的容器,需要暂停的地方,都用yield语句注明。但是 Generator 使用起来较为复杂。 ES7又提出了新的异步解决方案:async/await,async是 Generator 函数的语法糖,async/await 使得异步代码看起来像同步代码,异步编程发展的目标就是让异步逻辑的代码看起来像同步一样。 回调函数 ---> Promise ---> Generator ---> async/await.1.回调函数: callback//node读取文件fs.readFile(xxx, 'utf-8', function(err, data) { //code});回调函数的使用场景(包括但不限于): 事件回调Node APIsetTimeout/setInterval中的回调函数ajax 请求回调函数的优点: 简单。回调函数的缺点: 异步回调嵌套会导致代码难以维护,并且不方便统一处理错误,不能 try catch 和 回调地狱(如先读取A文本内容,再根据A文本内容读取B再根据B的内容读取C...)。 fs.readFile(A, 'utf-8', function(err, data) { fs.readFile(B, 'utf-8', function(err, data) { fs.readFile(C, 'utf-8', function(err, data) { fs.readFile(D, 'utf-8', function(err, data) { //.... }); }); });});2.PromisePromise 一定程度上解决了回调地狱的问题,Promise 最早由社区提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。 ...

May 22, 2019 · 3 min · jiezi

服务器部署

什么是服务器?1.组成部分2.分类3.特点业务场景 部署什么?如何选择安全防御措施步骤创建主机创建用户安装运行环境把项目同步到服务器安装进程管理程序使用ip和端口调试,没问题,使用nginx作为反向代理使用域名访问(先申请再绑定)

May 21, 2019 · 1 min · jiezi

JavaScript-编码规范

类型基本类型你可以直接获取到基本类型的值 stringnumberbooleannullundefinedsymbolconst foo = 1;let bar = foo;bar = 9;console.log(foo, bar); // => 1, 9注意:Symbols 不能被完整的 polyfill,所以,在不支持 Symbols 的环境下中,不应该使用 symbol 类型。复杂类型复杂类型赋值就是获取到他的引用的值,相当于引用传递 objectarrayfunctionconst foo = [1, 2];const bar = foo;bar[0] = 9;console.log(foo[0], bar[0]); // => 9, 9参考永远都使用 const为了确保你不会改变你的初始值,重复引用会导致一些不可预见的 bug,还会让代码难以理解,所有的赋值都应该使用 const,避免使用 var。 eslint prefer-constno-const-assign// badvar a = 1;var b = 2;// goodconst a = 1;const b = 2;可以使用 let如果你一定要对参数重新赋值,那就使用 let,而不是 var, let 是块级作用域,而 ver 是函数级作用域。 eslint no-var// badvar count = 1;if (true) { count += 1;}// goodlet count = 1;if (true) { count += 1;}注意 const 与 let 的块级作用域const 与 let 声明的常量与变量都只存在于定义它们的那个块级作用域中。 ...

May 21, 2019 · 24 min · jiezi

使用-TypeScript-开发-HapiJS-应用

初始化 npm 项目yarn init添加依赖yarn add hapi添加开发依赖要在开发中使用 TypeScrip,同时至少需要有一个工具,可以一直监听项目文件的变更,并实时的将变更更新至启动的服务中,我选择使用 Nodemon,首先添加以下几个开发依赖 yarn add typescript -Dyarn add nodemon -D接下来,我们需要为 node 与 hapi 安装类型定义库: yarn add @types/node -Dyarn add @types/hapi -D安装完成之后, package.json 文件看起来像下面这样的: { "name": "hapiserver", "version": "0.0.1", "description": "API server", "main": "index.js", "author": "Your Name", "license": "MIT", "dependencies": { "hapi": "^18.1.0" }, "devDependencies": { "@types/hapi": "^18.0.2", "@types/node": "^12.0.2", "nodemon": "^1.19.0", "typescript": "^3.4.5" }}注意:你的 dependencies 与 devDependencies 配置中,版本号可能与我的不同。配置 TypeScript设计项目文件目录结构在项目的根目录下,创建一个名为 src 的目录,用于包含系统的所有源代码文件,接着,创建一个名为 dist 的目录,用于保存由 typescript 编译后的 javascript 文件。 ...

May 21, 2019 · 2 min · jiezi

接了个新项目

背景最近接了个新项目, 遇到一些问题, 在这整理分享下。前期规划需求是这样的,需要做一套后台管理系统: 一个主系统,一个子系统,开发时间6个周。 前期开发有两个人, 再加一个人。 说实话时间有点紧, 所以前期做好规划就很重要。 实现先做一个规划,技术选型,文档分析,分页面, 有个大致的评估。 技术选型首先确定的还是 React 这一套, 即: React,Redux,TypeScript, 样式管理 styled-components, 国际化 react-intl, 组件库 antd, 脚手架,自己配。 本来想图省事用 CRA(create-react-app),后来觉得用rewired 重写不太灵活, 而且有个小伙伴也想自己配,熟悉下 webpack , 还是决定自己搭, 后面会把配置贴出来。 开发计划和后端负责人讨论之后决定把这一期的开发任务分成三个小阶段: P1, P2, P3 P1 完成之后发布, 先跑通主流程,P2 P3 继续迭代功能。 P1 主要包括: 开发环境搭建test环境资源申请Nginx 配置主系统功能开发 三个功能模块开发登陆注册流程子系统两个模块的开发开发时间: 两周 压力还是有的,时间紧,任务重,而且是第一次带人做项目, 好在内心犹如一条老狗,一点都不慌。 后面就进入了开发阶段, 遇到了挺多问题。 进入开发搭建开发环境这一步大家就都很熟悉了,添加各种配置和打包。 因为主系统和子系统页面风格都是一样的, 没必要分成两个系统, 把新开一个文件夹,里面放子系统的页面, 然后打成不同的包就可以了。就有了如下配置: // webpack.config.jsconst path = require('path')const HtmlWebpackPlugin = require('html-webpack-plugin')const webpack = require('webpack')const fs = require('fs')const lessToJS = require('less-vars-to-js')const { NODE_ENV } = process.envconst isAdminApp = process.env.APP_TYPE === 'admin'const getBaseurl = () => { switch (process.env.ENV) { case 'id': return 'https://xxx.test.shopee.co.id' default: return '' }}const plugins = [ new HtmlWebpackPlugin({ template: path.resolve(__dirname, 'template.html'), title: isAdminApp ? 'WMS LITE ADMIN' : 'WMS LITE', }), new webpack.DefinePlugin({ __BASEURL__: JSON.stringify(getBaseurl()), }), new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)]if (NODE_ENV !== 'production') { plugins.push(new webpack.SourceMapDevToolPlugin({}))}const themeVariables = lessToJS(fs.readFileSync(path.resolve(__dirname, './assets/antd-custom.less'), 'utf8'))const port = isAdminApp ? 9527 : 8080module.exports = { entry: [ '@babel/polyfill', isAdminApp ? './admin/index.js' : './pages/index.js' ], output: { filename: isAdminApp ? 'admin.[hash:8].js' : 'main.[hash:8].js', path: path.resolve(__dirname, isAdminApp ? 'dist/adminstatic' : 'dist/static'), publicPath: isAdminApp ? '/admin/' : '/', }, mode: NODE_ENV, devtool: false, plugins, module: { rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, use: { loader: 'babel-loader', }, }, { test: /\.less$/, use: [ { loader: 'style-loader', }, { loader: 'css-loader', }, { loader: 'less-loader', options: { javascriptEnabled: true, sourceMap: true, modifyVars: themeVariables, }, } ], }, { test: /\.css$/, use: [ { loader: 'style-loader', }, { loader: 'css-loader', } ], }, { type: 'javascript/auto', test: /\.mjs$/, use: [], }, { test: /\.(png|jpg|gif|svg)$/i, use: [ { loader: 'url-loader', options: { limit: 8192, }, } ], } ], }, optimization: { runtimeChunk: { name: 'manifest', }, splitChunks: { chunks: 'all', minSize: 30000, minChunks: 1, maxAsyncRequests: 5, maxInitialRequests: 3, cacheGroups: { vendor: { test: /[\\/]node_modules/, filename: 'vendor.[chunkhash:8].js', enforce: true, priority: 5, }, antd: { test: /[\\/]node_modules[\\/]antd[\\/]/, filename: 'antd.[chunkhash:8].js', priority: 10, }, antdIcons: { test: /[\\/]node_modules[\\/]@ant-design[\\/]/, filename: 'antd-icons.[chunkhash:8].js', priority: 15, }, styles: { test: /\.(scss|css)$/, filename: 'styles.[hash:8].css', minChunks: 1, reuseExistingChunk: true, enforce: true, priority: 20, }, }, }, }, devServer: { historyApiFallback: isAdminApp ? { rewrites: [{ from: /.*/g, to: '/admin/index.html', }], } : true, hot: true, port, proxy: [{ context: ['/admin/api', '/api'], target: 'https://gudangku.test.shopee.co.id', changeOrigin: true, onProxyRes(proxyRes, _, res) { const cookies = proxyRes.headers['set-cookie'] || [] const re = /domain=[\w.]+;/i const newCookie = cookies.map(cookie => cookie.replace(re, 'Domain=localhost;')) res.writeHead(200, { ...proxyRes.headers, 'set-cookie': newCookie, }) }, }], },}// package.json "scripts": { "start": "NODE_ENV=development APP_TYPE=main webpack-dev-server", "build": "NODE_ENV=production APP_TYPE=main webpack", "start:admin": "NODE_ENV=development APP_TYPE=admin webpack-dev-server", "build:admin": "NODE_ENV=production APP_TYPE=admin webpack", "lint": "eslint ./ --ext js", "i18n": "node i18n/index.js" },根据不同的参数打包, 主系统打包到 dist/static, 子系统打包到dist/adminstatic. ...

May 21, 2019 · 3 min · jiezi

Webpack入门到精通1

前言什么是webpack 本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。webpack 有哪些功能(代码转换 文件优化 代码分割 模块合并 自动刷新 代码校验 自动发布)首先学习webpack 需要有简单的node 基础 ,打开node 官方网站进行安装node, http://nodejs.cn/ 下载最新版node包并进行安装。 学习目标:webpack 常见配置 webpack高级配置webpack优化策略AST抽象语法树webpack中的Tapable掌握webpack流程 手写 webpack手写webpack中常见的loader手写webpack 中常见的plugin定义好学习目标让我们开启webpack 的新旅程。(本文学习主要针对webpack4.0 进行学习讲解)webpck--->基础搭建与使用:安装完毕在终端 快速创建node项目 执行命令npm init -y 生成packge.json 在当前目录安装本地webpack终端执行命令: npm i webpack webpack-cli -d i表示install ,d表示当前是开发环境安装完成会产生node_modules文件webpack 可以进行0配置 并且webpack是打包工具(默认是js模块 通过入口进行打包输出打包后js结果)。 创建src目录 --> 创建index.js -> 输出:console.log('hello webpack'); npx 语法进行把index.js 进行打包 终端执行命令: npx webpack我们发现当前目录生成了一个dist 目录并且创建了一个main.js(如图:) 执行顺序:(默认找node_modules--->bin文件 --> webpack文件 )这里我们明白了安装webpack 必须安装他的依赖 webpack-cliwebpack打包默认支持js模块化 ->类似于common.js ...

May 21, 2019 · 5 min · jiezi

如何在零JS代码情况下实现一个实时聊天功能❓

引言前段时间在 github 上看到了一个很“trick”的项目:用纯 CSS(即不使用 JavaScript)实现一个聊天应用 —— css-only-chat。即下图所示效果。 在我们的印象里,实现一个简单的聊天应用(消息发送与多页面同步)并不困难 —— 这是在我们有 JavaScript 的帮助下。而如果让你只能使用 CSS,不能有前端的 JavaScript 代码,那你能够实现么? 原版是用 Ruby 写的后端。可能大家对 Ruby 不太了解,所以我按照原作者思路,用 NodeJS 实现了一版 css-only-chat-node,对大家来说可能会更易读些。1. 我们要解决什么问题首先强调一下,服务端的代码肯定还是需要写的,而且这部分显然不能是 CSS。所以这里的“纯 CSS”主要指在浏览器端只使用 CSS。 回忆一下,如果使用 JavaScript 来实现上图中展示的聊天功能,有哪些问题需要处理呢? 首先,需要添加按钮的click事件监听,包括字符按钮的点击与发送按钮的点击;其次,点击相应按钮后,要将信息通过 Ajax 的方式发送到后端服务;再者,要实现实时的消息展示,一般会建立一个 WebSocket 连接;最后,对于后端同步来的消息,我们会在浏览器端操作 DOM API 来改变 DOM 内容,展示消息记录。涉及到 JavaScript 的操作主要就是上面四个了。但是,现在我们只能使用 CSS,那对于上面这几个操作,可以用什么方式实现呢? 2. Trick Time2.1. 解决“点击监听”的问题使用 JavaScript 的话一行代码可以搞定: document.getElementById('btn').addEventListener('click', function () { // ……});使用 CSS 的话,其实有个伪类可以帮我们,即:active。它可以选择激活的元素,而当我们点击某个元素时,它就会处于激活状态。 所以,对于上面动图中的26个字母(再加上 send 按钮),可以分配不同的classname,然后设置伪类选择器,这样就可以在点击该字母对应的按钮时触发命中某个 CSS 规则。例如可以对字符“a”设置如下规则用于“捕获”点击: .btn_a:active { /* …… */ }2.2. 发送请求如果有 JavaScript 的帮助,发送请求只需要用个 XHR 即可,很方便。而对于 CSS,如果要想发一个请求的话有什么办法么? ...

May 21, 2019 · 2 min · jiezi

探索webpack运行时

前言本篇文章建议亲自动手尝试. 最近研究了 webpack 运行时源码, 在这篇文章中记录了我的探索 webpack 这个复杂的玩具方式, 并且以图形的形式将 webpack 运行时的流程给记录的下来. 我们讨论的是什么这篇文章主要记录的是 webpack 在将文件打包后是如何在浏览器中加载以及解析的流程. 手段webpack 实在是太复杂的了, 为了避免额外的干扰, 我使用最小化实例的方式来进行探究 webpack 是如何加载和解析模块文件的. 具体的手段:首先准备几个非常简单的 js 文件, 其次准备其对应的 webpack 配置文件, 逐一测试后观察其输出, 阅读其源码, 然后得出结论. 简单总结webpack 的运行时在解析同步模块的时候就和 nodejs 规则如出一辙. 什么意思, 每一个模块在运行前都会被一个函数进行包裹: (function (module, exports, __webpack_require__) { // do something})看起来和 nodejs 一样, 作用起来也一致, 这里的当前模块的导出模块和导入模块就是: exports_webpack_require_而当一个模块被需要的时候, webpack 就会执行这个函数获取其对应的 exports对象. 注意:我们需要的是模块执行完成后的 exports 对象而不是这个模块函数. 在 webpack 中我们可以使用 ESM 规范和 commonjs 规范来加载模块, 无论使用哪种规范, 实际上都可以被这种方式来包裹起来, 因为这两种常见的方式中都存在着相同的导入导出的概念, 所以 webpack 将这两种形式进行了统一包装消除了差异. ...

May 21, 2019 · 3 min · jiezi

Serverless????Nodejs-Puppeteer-渗透测试爬虫实践

本文归纳于微服务与云原生 https://github.com/wx-chevalier/Backend-Series系列文章,其相关的参考资料声明于 Awesome Serverless List。 Serverless????Node.js Puppeteer 渗透测试爬虫实践参考 CNCF 的定义,Serverless 是指构建和运行不需要服务器管理的应用程序的概念;而 AWS 官方对于 Serverless 的介绍是:服务器架构是基于互联网的系统,其中应用开发不使用常规的服务进程。相反,它们仅依赖于第三方服务(例如 AWS Lambda 服务),客户端逻辑和服务托管远程过程调用的组合。 Serverless 目前主要的落地形式为 BaaS 与 FaaS。BaaS 后端即服务,即是一些后端云服务,比如云数据库、对象存储、消息队列等。利用 BaaS,可以极大简化我们的应用开发难度。FaaS 函数即服务,则是暂存容器中运行的自定义代码,函数是无服务器架构中抽象语言运行时的最小单位。在 FaaS 中,用户主要为函数的运行时间付费,而不需要关心 CPU 或 RAM 或任何其他资源及其运维的负担。 参考 BaaS 与 FaaS 的定义,我们可以知道 Serverless 的主要特点有: 事件驱动:函数在 FaaS 平台中,需要通过一系列的事件来驱动函数执行。无状态:因为每次函数执行,可能使用的都是不同的容器,无法进行内存或数据共享。如果要共享数据,则只能通过第三方服务,比如 Redis 等。无运维:使用 Serverless 我们不需要关心服务器,不需要关心运维。这也是 Serverless 思想的核心。按实际使用计费:使用 Serverless 成本很低,因为我们只需要为每次函数的运行付费。函数不运行,则不花钱,也不会浪费服务器资源。这些特征的本质,是用户对于云端应用开发,乃至于所谓云原生应用中的用户友好与低心智负担方向演讲的最直接途径,而这种简单、经济、可信赖的期许,也是云计算的初心。当然 Serverless 并不拘泥于 Function,而是应该多种部署形态并存,譬如以应用方式部署,则是遵循单一职责原则,但是能够触发多个事件;也可以在容器级别部署,能够包含任意语言、任意运行时,譬如 Knative 这样的解法。在微服务与云原生/Serverless 一节中我们也讨论了更多的 Serverless 落地模式。 前端视角的 Serverless在现代 Web 开发基础与工程实践 https://github.com/wx-chevalier/Web-Series 系列的前言中,我们也讨论了 Web 技术与生态经历了模板渲染、前后端分离与单页应用,工程化与微前端,大前端与 Serverless 这三个不同的阶段。自然从前端的视角来看,Serverless 也赋予了前端更多的自由与可能性,在服务端渲染,小程序开发的简单服务端支持,包括 BFF 接口聚合等方面都有很多的空间。 ...

May 21, 2019 · 2 min · jiezi

Node搭建一个静态资源服务器

使用 Node 的内置模块,创建一个可以访问目录的静态资源服务器,支持fs文件读取,资源压缩与缓存等。一、创建 HTTP Server 服务器Node 的 http 模块提供 HTTP 服务器和客户端接口,通过 require('http')使用。 先创建一个简单的 http server。配置参数如下: // server/config.jsmodule.exports = { root: process.cwd(), host: '127.0.0.1', port: '8877'}process.cwd()方法返回 Node.js 进程的当前工作目录,和 Linus 命令pwd功能一样, Node 服务器每次收到 HTTP 请求后都会调用 http.createServer() 这个回调函数,每次收一条请求,都会先解析请求头作为新的 request 的一部分,然后用新的 request 和 respond 对象触发回调函数。以下创建一个简单的 http 服务,先默认响应的 status 为 200: // server/http.jsconst http = require('http')const path = require('path')const config = require('./config')const server = http.createServer((request, response) => { let filePath = path.join(config.root, request.url) response.statusCode = 200 response.setHeader('content-type', 'text/html') response.write(`<html><body><h1>Hello World! </h1><p>${filePath}</p></body></html>`) response.end()})server.listen(config.port, config.host, () => { const addr = `http://${config.host}:${config.port}` console.info(`server started at ${addr}`)})客户端请求静态资源的地址可以通过request.url获得,然后使用 path 模块拼接资源的路径。 ...

May 18, 2019 · 5 min · jiezi

使用-mongoose-操作-mongodb-增删改查

使用 mongoose 操作 mongodb 的测试文件连接数据库1.1 引入 mongoose1.2 连接指定的数据库(URL 只有数据库是变化的)1.3 获取连接对象1.4 绑定连接完成的监听(用来提示连接成功)得到对应特定集合的 Model2.1 字义 Schema(描述文档结构)2.2 定义 Model(与集合对应,可以操作集合)通过 Model 或其实例对集合数据进行 CRUD 操作3.1 通过 Model 实例的 save() 添加数据3.2 通过 Model 的 find()/findOne() 查询多个或一个数据3.3 通过 Model 的 findByIdAndUpdate() 更新某个数据3.4 通过 Model 的 deleteOne() 删除匹配的数据*/下载mongoose依赖包 npm install --save mongoose 下载md5加密依赖包 npm install --save blueimp-md5下面的代码位置: db_test.js// 引入 md5 依赖const md5 = require('blueimp-md5')1. 连接数据库// 1.1 引入 mongooseconst mongoose = require('mongoose')// 1.2 连接指定的数据库(URL 只有数据库是变化的)mongoose.connect('mongodb://localhost:27017/zp_test', {useNewUrlParser: true})// 1.3 获取连接对象const conn = mongoose.connection// 1.4 绑定连接完成的监听(用来提示连接成功)conn.on('connected', function () { console.log('数据库连接成功!')})2. 得到对应特定集合的 Model// 2.1 字义 Schema(描述文档结构)const userSchema = mongoose.Schema({ username: {type: String, require: true}, // 用户名 password: {type: String, required: true}, // 密码 type: {type: String, required: true} // 用户类型: 求职者/老板})// 2.2 定义 Model(与集合对应,可以操作集合)const UserModel = mongoose.model('users', userSchema) // 集合名: users3. 通过 Model 或其实例对集合数据进行 CRUD 操作 3.1 通过 Model 实例的 save() 添加数据function testSave() { // user 数据对象 const user = { username: 'mandy', password: md5('1234'), type: '求职者' } // 创建 Model 实例 const userModel = new UserModel(user) // 或者像下面这样添加数据 // const userModel = new UserModel({username: 'Tom', password: md5('3333'), type: '老板'}) // 保存到数据库 userModel.save(function (err, user) { console.log('save', err, user) })}// testSave() 3.2 通过 Model 的 find()/findOne() 查询多个或一个数据function testFind() { // 查找多个 UserModel.find(function (err, users) { // 如果有匹配返回一个[user, user...], 如果没有一个匹配的返回[] console.log('find()', err, users) }) // 查找一个 UserModel.findOne({_id: '5cdf99ebf3539334948ae2c8'}, function (err, user) { // 如果有匹配返回的是一个user console.log('findOne()', err, user) })}// testFind() 3.3 通过 Model 的 findByIdAndUpdate() 更新某个数据function testUpdate() { UserModel.findByIdAndUpdate({_id: '5cdfacc55684652a08b49014'}, {username: '一个老板'}, function (err, user) { console.log('findByIdAndUpdate()', err, user) })}// testUpdate() 3.4 通过 Model 的 remove() 删除匹配的数据function testDelete() { UserModel.deleteOne({_id: '5cdf9d761a1c050c70bc0d09'}, function (err, result) { console.log('deleteOne()', err, result) })}// testDelete()运行: ...

May 18, 2019 · 2 min · jiezi

VueExpress全栈购物商城

一、前言提纲基于Vue和Express框架写的一个全栈购物商城,记录项目过程中遇到的一些问题以及经验和技巧。 二、历史版本基于Vue-CLI2.0:点我查看这个分支版本是一两年前的,基于Vue-CLI2.0写的,数据请求是Mock,纯前端的项目。 基于 Vue-CLI3.0:点我查看 这个分支版本是基于Vue-CLI3.0的,将脚手架从2.0迁移升级到了3.0,遇到的一些问题和坑也都填完了~也是纯Web端Mock模拟数据的项目。 当前版本:点我查看基于Vue-CLI3.0,前端用Vue全家桶,后端用Express+MongoDB+Redis,后台管理系统CMS是用的Vue-Element-Admin 三、详情1.前端在线预览:https://www.fancystore.cn手机直接扫描二维码真机体验: 1.1 技术栈:Vue全家桶(Vue-CLI3,Vue2.x)Vue-Router(页面KeepAlive的处理)Vuex(状态管理库,刷新保存状态)Axios(二次封装配置的数据请求)Less(CSS预处理)I18n(国际化处理)Vant(UI库,按需加载+rem)SEO(预渲染)Sentry(线上错误日志监控)Travic(自动构建,持续部署)1.2适配项目代码px自动转换为rem,需要在main.js中引入amfe-flexible库 Vant UI库也有REM单位,需要在vue.config.js中配置: 1.3 SEO单页(SPA)SEO是一个痛点,目前有两种方式,一种是SSR,一种是预渲染(PrerenderSPAPlugin)。 这个项目是用预渲染(PrerenderSPAPlugin)+vue-meta-info这两个库来做SEO优化的。 将rouer.js模式改为mode:history下载安装PrerenderSPAPluginPrerenderSPAPlugin是Google的一个库,基于Chromium是获取数据,安装PrerenderSPAPlugin的时候会自动下载Chromium浏览器,国内npm安装Chromium会经常安装失败,建议用淘宝的cnpm安装 npm install -g cnpm --registry=https://registry.npm.taobao.org cnpm install PrerenderSPAPlugin --save在vue.config.js中引入PrerenderSPAPlugin,配置需要预渲染的路由。下载安装 vue-meta-info在main.js中引入vue-meta-info,在每个页面配置meta信息,这样每个单页路由都有不同的title,理由爬虫引擎抓取重要内容,利于SEO。 预渲染前只有一个index.html,预渲染后最后打包出来的预渲染目录文件如下: 1.4 路由懒加载以及缓存keep-alive动的态判断项目中会使用keep-alive会提高用户体验和网站的性能,如果想实现部分页面缓存,部分页面不需要缓存,可以在router.js里面的路由添加meta.keepalive在跟router-vier加入判断: 1.5 Vuex状态管理页面刷新失效问题用Vuex管理全局的状态,会遇到刷新页面的时候所有的状态丢失或者重置,可以在App.vue的钩子函数添加代码,会在页面刷新的时候将Vuex存储到Storage中,刷新完成后又再从Storage取出来存到Vuex里面: 1.6 封装数据请求封装Axios,添加Axios请求(request)和相应(response),统一处理错误信息或者登录认证的消息,所有的数据请求都存放到api目录下,对应的页面方便后续的维护和管理。 1.7 打包构建优化vue.config.js区分开发环境和生产环境alias的方式直接指定目录。CDN生产环境中将一些共有库Vue,vuex,vue-router等库不打包到项目中,而是通过CDN的方式引入这些共有库,这样可以减少项目的大小,也可以借助CDN的优势,让网站加载更快。推荐一个强大的cdn库:[https://www.bootcdn.cn/](https://www.bootcdn.cn/) 生产环境压缩和出去console打印日志生产环境开启gzip压缩生产环境启用预渲染生产环境分离css,外链CSS可以缓存1.7 错误日志监控Sentry集成Sentry开源日志监控系统,在官网注册获取key,在main.js中引入RavenVue并配置即可 1.8 自动构建和持续集成Github自动构建和持续集成基于Travis 登录Travis选择需要持续集成的项目。在.travis.yml写上相应的config,服务器配置ssh_key,每次代码push到指定分支(比如master)的时候,Travis会自动执行项目上的.travis.yml文件,开始自动构建,构建成功通过scp密令传送到服务器,完成自动部署的功能。每次需要发版,只需要push代码,然后去喝杯咖啡,回来就已经构建发布完成,解放劳动力 1.9 代码自动格式化优化团队合作的时候,每个成员用的编辑器不同,缩进格式也不同,这样合并代码的时候会出现各种意外的情况,团队统一编辑器和编辑器不太现实,因为每个人的写代码习惯和风格不一致。可以借助husky 和 link-stage,每次commit的时候都会安装配置的规则格式化代码,参考文章:https://segmentfault.com/a/1190000009546913 1.10代码优化设计模式表单验证需要写很多判断条件,if-else 或者swith,当条件越多时或者后面需要修改需求条件的时候,会变得不是很好维护,可以用策略模式来重构业务代码: 善用Mixin,提取共用的组件,将项目组件化Vue的Mixin复用代码,可以更好的提高开发效率和可维护性除了将一些共用的页面做成组件引入的方式之外,大文件项目也分好几个模块,将文件才成模块的方式会更好维护和更好的阅读。 2.服务端2.1 技术栈:NodeExpressMongoMongooseRedisQiniuPM22.2 登录授权用Session认证机制,来实现登录登出。配置Session的加密解密,将Session存储到Redis,提高性能,如果有多台服务器,Redis可以共享Session。 2.3 中间件判断用户是否登录:有些API请求是需要用户登录才可以访问的,可以写中间件来判断: 2.4 中间件判断用户的权限:有些API的请求是需要判断用户是否有权限,比如添加、删除和更新,会在中间件判断是否有权限 2.5 PM2多进程启动项目2.6 Mongodb优化设置索引2.7 Redis做缓存2.8 七牛云对象存储配置Key还有域名的绑定以及HTTPS证书的申请3.后台管理系统CMS在线预览:https://www.fancystore.cn/admin ...

May 18, 2019 · 1 min · jiezi

浏览器和Node中的事件循环机制

一、前言前几天听公司一个公司三年的前端说“今天又学到了一个知识点-微任务、宏任务”,我问他这是什么东西,由于在吃饭他浅浅的说了下,当时没太理解就私下学习整理一番,由于谈微任务、宏任务必谈到事件循环,于是就有了这篇博客。 在谈到事件循环机制之前我们需要知道一些基础知识就是: js是单线程的js一开始是作为脚本语言运行在客户端其实js是单线程在它作为脚本语言操作dom的时候就决定了。那么此时就有一个性能问题,那么js在浏览器端是如何处理这个问题的呢?同时,js在后台Node中又是如何解决的呢?这就是本篇需要介绍的事件循环机制,这里我将分别以浏览器和Node两个方面来分析。 二、浏览器端在讲解事件循环之前先谈谈js中同步代码、异步代码的执行流程。 2.1、js同步代码执行过程js引擎在执行通过代码的过程中,会安装顺序依次存储到一个地方去,这个地方就叫做执行栈,当我们调用一个方法的时候,js会生成一个和这个方法相对应的上下文(context)。这个执行环境中存在着这个方法的私有作用域,上层作用域的指向,方法的参数,这个作用域中定义的变量以及这个作用域的this对象。 function a() { console.log("method a execute...");}function b() { a();}function c() { b();}c();以上面例子分析:js在执行的时候会有一个全局上下文,我们这里就称为GContext,下面分析步骤 调用c(),c入栈,此时栈中内容为:GContext->c-contextC接着调用b(),b入栈,此时栈中内容为:GContext->c->contextC->b->contextB接着调用a(),a入栈,此时栈中内容为:GContext->c->contextC->b->contextB-c->contextCa执行完,a出栈,此时栈中内容为:GContext->c->contextC->b->contextBb执行完,b出栈,此时栈中内容为:GContext->c->contextCc执行完,b出栈,此时栈中内容为:GContext全部执行完,释放资源ok,上面是同步代码的执行,上面会涉及到两个核心概念:执行整个代码的线程我们称之为主线程,存放方法执行的地方我们称之为执行栈. 2.2、js异步代码执行过程上面说完了同步过程,那这里来谈谈异步的过程。js引擎在遇到一个异步事件,不会一直等待返回结果而是将它挂起。当异步任务执行完之后会将结果加入到和执行栈中不同的任务队列当中,注意的是:此时放入队列不会立即执行其回调,而是当主线程执行完执行栈中所有的任务之后再去队列中查找是否有任务,如果有则取出排在第一位的事件然后将回调放入执行栈并执行其代码。如此反复就构成了事件循环。 这里同样有一个核心概念:任务队列 2.3、微任务、宏任务上面提到js执行异步方法的时候会将其返回结果放到队列中,这是比较笼统的,具体来说,js会根据任务的类型将其放入不同的队列,任务类型有两种:微任务、宏任务。那么其对应的哪些是微任务、哪些是宏任务呢? 微任务:Promise、process.nextTick()、整体代码script、Object.observer、MutationObserver宏任务:setTimeout()、setInterval()浏览器在执行的时候,先从宏任务队列中取出一个宏任务执行宏,然后在执行该宏任务下的所有的微任务,这是一个循环;然后再取出并执行下一个宏任务,再执行所有的微任务,这是第二个循环,以此类推. 注意:整个javascript代码是第一个宏任务const process = require('process')setTimeout(function () {// 分发宏任务到EventQueue console.log("1");}, 0);setTimeout(() => { console.log("11");}, 0);setTimeout(() => { console.log("111");}, 0);new Promise(function (resolve) { console.log('2'); resolve();}).then(function () {// 发送微任务 console.log('3');});// 输出231111112.4、小结在浏览器端,在我们执行一片script的时候,当遇到同步代码,依次进入执行栈,遇到异步代码,将其挂起,继续执行其它方法,当异步方法执行完之后根据任务类型进入到任务队列,在执行栈执行完,主线程空闲下来了之后会到任务队列中取任务回调并执行。 三、Node端我自己认为Node的事件循环和浏览器端还是有点区别的,它的事件循环依靠libuv引擎。 该图来自官网,这里展示了在node的事件循环的6个阶段。 timers:该阶段执行定时器的回调,如setTimeout() 和 setInterval()。I/O callbacks:该阶段执行除了close事件,定时器和setImmediate()的回调外的所有回调idle, prepare:内部使用poll:等待新的I/O事件,node在一些特殊情况下会阻塞在这里check: setImmediate()的回调会在这个阶段执行close callbacks: 例如socket.on('close', ...)这种close事件的回调对于我们来说我们更关注 timer、poll、check这三个阶段即可。 poll 阶段有两个主要的功能: 处理poll队列(poll quenue)的事件(callback);执行timers的callback,当到达timers指定的时间时;poll 阶段的逻辑 ...

May 18, 2019 · 1 min · jiezi

前后端分离之更好的mock你的后端api

注意! 广告警告! 广告警告! 广告警告!在一个web应用的开发周期中, 一般前端与后端都是并行开发的, 各自完成自己的开发工作后进行联调, 联调通过再进行提测/发布. 开发过程中, 前端都会以后端提供的 api 文档作为标准, mock 模拟 api 返回数据, 以确保在开发中就保证功能的完整性. 而关于如何更好的进行 mock, 业界/开源社区可谓有相当多质量上乘的解决方案, 如easy-mock, yapi等. 但是越是大而全的工具很多时候功能会超越需求非常多, 要简单实现 mock api 的需求其实也有非常多小而美工具库可以使用. 而本文主要介绍 mock-server 这个工具的使用 选用 mock-server 的主要原因除了是我开发的使用比较简单之外, 更多的是满足了下文提到的一些开发需求, 如果你也有同样的需求而还没找到解决方案的话, 不妨试用一下. 安装 & 基本配置可选全局安装, 安装完成过后, 就可以通过mock命令启动来 mock server npm install -g mock-server-localmock -h # 即安装成功# 用 -d 指定mock数据配置目录, 为空时默认为当前目录 `.`mock -d ./mock# 用 -p 指定server的端口, 默认为8888, 如果8888被占用会端口号+1, 直至端口可用# 注意如果指定了端口号, 但是端口号被占用的话, 会抛出错误mock -d ./mock -p 8080个人比较习惯在项目中进行安装, 并通过npm script启动, 而 mock 数据也存放在项目当中, 通过 git 等版本管理工具在项目成员当中共享, 假设项目目录为proj ...

May 18, 2019 · 4 min · jiezi

使用-Nodejs-在开放交易所OceanOne上挂单买卖奔驰币

在上一课中,我们介绍了如何在OceanOne交易比特币。OceanOne支持交易任何Mixin Network上的token,包括所有的ERC20和EOS token,不需要任何手续和费用,直接挂单即可。下面介绍如何将将一个ERC20 token挂上OceanOne交易。掌握了ERC20代币的交易方法,就可以交易任何其他Mixin Network代币了。 此处我们用一个叫做Benz的ERC20 token为例。这个token已经被充值进Mixin Network,你可以在区块链浏览器看到这个token在Mixin Network内部的总数和交易 预备知识:先将Ben币存入你的钱包,然后使用getAssets API读取它的UUID. 取得该币的UUID调用 getAssets API 会返回json数据, 如: asset_id 币的UUID.public_key 该币的当前钱包的地址.symbol 币的名称. 如: Benz.if ( args.type === TYPE_WALLET_ASSETS_INFO ) { const assetsInfo = await newUserClient.getUserAssets(); console.log("-AssetID--Asset--Balance--public_key--"); assetsInfo.forEach(function(element) { console.log(element.asset_id + " " + element.symbol + " " + element.balance + " " + element.public_key + " " + element.account_name + " " + element.account_tag ); }); // console.log(assetsInfo);}调用 getUserAssets API的完整输出如下: ...

May 16, 2019 · 3 min · jiezi

解决问题npm全局安装后仍然提示找不到命令

惨案我正常使用命令安装express。 $ npm install -g express-generator然后bash就提示安装成功 /Users/majialun/.npm-global/bin/express -> /Users/majialun/.npm-global/lib/node_modules/express-generator/bin/express-cli.js+ express-generator@4.16.1updated 1 package in 2.123s然后运行 express,按道理这个时候应该让我创建项目了,但是系统提示: bash: express: command not found其实不光是express,我自己写的脚本,也出现这个问题,全局安装成功,但是,通过bash就是调用不出来。 侦查先看看全局有哪些path变量 $ echo $PATH然后就出现了很多 /Users/majialun/.rvm/gems/ruby-2.4.1/bin:/Users/majialun/.rvm/gems/ruby-2.4.1@global/bin:/Users/majialun/.rvm/rubies/ruby-2.4.1/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/majialun/.rvm/gems/ruby-2.4.1/bin:/Users/majialun/.rvm/gems/ruby-2.4.1@global/bin:/Users/majialun/.rvm/rubies/ruby-2.4.1/bin:/Users/majialun/.rvm/bin:/Users/majialun/Documents/flutter/flutter/bin:/Users/majialun/.npm-global/bin/express:/Users/majialun/.rvm/bin:/Users/majialun/Documents/flutter/flutter/bin:/Users/majialun/.rvm/gems/ruby-2.4.1/bin:/Users/majialun/.rvm/gems/ruby-2.4.1@global/bin:/Users/majialun/.rvm/rubies/ruby-2.4.1/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/majialun/.rvm/gems/ruby-2.4.1/bin:/Users/majialun/.rvm/gems/ruby-2.4.1@global/bin:/Users/majialun/.rvm/rubies/ruby-2.4.1/bin:/Users/majialun/.rvm/bin:/Users/majialun/Documents/flutter/flutter/bin:/Users/majialun/.npm-global/bin/express:/Users/majialun/.rvm/bin:/Users/majialun/.npm-global/bin:/Users/majialun/.rvm/gems/ruby-2.4.1/bin:/Users/majialun/.rvm/gems/ruby-2.4.1@global/bin:/Users/majialun/.rvm/rubies/ruby-2.4.1/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/majialun/.rvm/gems/ruby-2.4.1/bin:/Users/majialun/.rvm/gems/ruby-2.4.1@global/bin:/Users/majialun/.rvm/rubies/ruby-2.4.1/bin:/Users/majialun/.rvm/bin:/Users/majialun/Documents/flutter/flutter/bin:/Users/majialun/.npm-global/bin/express:/Users/majialun/.rvm/bin:/Users/majialun/Documents/flutter/flutter/bin:/Users/majialun/.rvm/gems/ruby-2.4.1/bin:/Users/majialun/.rvm/gems/ruby-2.4.1@global/bin:/Users/majialun/.rvm/rubies/ruby-2.4.1/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/majialun/.rvm/gems/ruby-2.4.1/bin:/Users/majialun/.rvm/gems/ruby-2.4.1@global/bin:/Users/majialun/.rvm/rubies/ruby-2.4.1/bin:/Users/majialun/.rvm/bin:/Users/majialun/Documents/flutter/flutter/bin这个时候我们发现太多了,但是不要慌,刚才安装Express的时候,有一个提示显示了npm的全局脚本挂在哪个path下。 再看看安装成功的提示: /Users/majialun/.npm-global/bin/express -> /Users/majialun/.npm-global/lib/node_modules/express-generator/bin/express-cli.js+ express-generator@4.16.1updated 1 package in 2.123s我们可以看到,脚本安装在/Users/majialun/.npm-global/bin,这个目录里就是全部的全局脚本,注意是bin目录,express只是这个目录下的一个而已。 然后我们仔细在全局的path里找,发现并不存在这个路径……我也不知道我是怎么把路径搞没的,我在公司的Macbook Pro一切正常,但是随身带的Macbook就缺失路径。 破案我们直接去根目录(/Users/majialun/)下,显示隐藏文件后,看看有没有.bash_profile,没有就新建一个,有就直接修改,添加进去一个路径: export PATH=$PATH:/Users/majialun/.npm-global/bin:$PATH注意这个路径是从上面复制下来的,每个人的不一样,总之从安装成功的提示里,复制到/bin这里结束就可以了。好了之后,在终端里打source,然后把.bash_profile 拖进来,就有了: $ source /Users/majialun/.bash_profile 执行一下然后重启终端,跑起来试试看: majialun$ express warning: the default view engine will not be jade in future releases warning: use `--view=jade' or `--help' for additional optionsdestination is not empty, continue? [y/N] 没有not found 的报错,问题解决。 ...

May 16, 2019 · 1 min · jiezi

call-apply-与-bind-实例详解

call() , apply() 与 bind() 详解我们知道可以用call(), apply() 和 bind()这三个函数都是用来完成函数调用,并且设置this指向。 call()和apply()是 ECMAScript3 标准引入,而bind()函数则是在 ECMAScript 5 引入。 这边文章会用几个小例子来回忆一下他们之间有什么不一样。 用法call()和apply() 会立即调用函数, 而bind()只会返回一个函数引用,当后面真正调用返回的函数的时候,函数里面的this将会指向给bind()函数传入的参数,并在调用新函数时,将给定参数列表作为原函数的参数序列的前若干项, 所以 bind()函数非常适合在事件回调的时候修改this 指向, 有React 经验的朋友应该会有更深的感受。 call()var dist = 'Beijing';function greet(name, hometown) { var word = `Welcome ${name} from ${hometown} to ${this.dist}!` console.log(word);}var obj1 = { dist: 'Chengdu'};greet.call(obj1, "Tom", "Dallas"); // Welcome Tom from Dallas to Chengdu!greet("Jerry", "Houston"); // Welcome Jerry from Houston to Beijing!因为greet.call(obj) 传入了obj1作为第一个参数,所以在 greet()函数执行时, this指向 obj1。其余的参数就将作为参数传给greet()函数。当没有使用call()而直接调用greet()时, this指向 window对象. ...

May 16, 2019 · 2 min · jiezi

node应用远程调试教程

远程调试所谓远程调试,是指在本地IDE或命令行即时调试服务端代码,这在预发环境的测试阶段可以使用。远程调试避免了服务端环境的模拟,可快速定位bug。 node应用调试本文的教程主要针对采用 VS Code IDE的群体。目前并未搭建一个系统专门支持node应用远程调试,因此需要开发人员手动去对应服务端机器运行相关操作: 通过需要debug的服务端机器关闭当前所有工作进程 慎重,确保机器是你需要debug的机器切换至应用的工作目录,执行 node --inspect=127.0.0.1:9090 index ,IP地址替换为对应机器的IP配置VS Code的 “.launch.json”文件,在 configurations数组中加入一个配置对象"configurations": [ { "type": "node", "request": "attach", "name": "vsssssss", "address": "127.0.0.1", "port": 9090, "localRoot": "${workspaceFolder}", "remoteRoot": "/home/www/abc/deploy/abc" // 工作目录 }]配置对象的type、request、localRoot字段固定不变;name为应用名可随意取;address、port为需要debug的服务IP和端口,remoteRoot为服务端代码的绝对路径。 在VS Code中的debug tab栏选择第4部中对应 name字段名称的应用,启动即可,此后在本地代码中打的所有断点都会生效 。此文档针对node 8+版本参考node_debugger

May 16, 2019 · 1 min · jiezi

verdaccio搭建npm私有库

Serverserver:all developers can have access to it, the server environment is windows.We need to use the npm command to install verdaccio, so we have to have a node environment. step1: install the node environment Download node install nodeVerify that the installation was successfulOpen cmd and input 'node -v' step2:Installation start verdaccio Install verdaccioOpen cmd and input 'npm install -g verdaccio' Start verdaccioinput 'verdaccio' ...

May 16, 2019 · 2 min · jiezi

npm-私服-使用Nexus搭建-npm-操作总是401没有权限的问题

将包发布到nexus npm仓库需要设置一下 Nexus Repository Manager 的权限。否则无法登陆到我们的私服。在Security->Realms栏目里,将npm Bearer Token Realm 选入Active。 参考链接 * [https://juejin.im/post/5c89184f5188257ded10e165]* [https://stackoverflow.com/questions/39460074/unable-to-publish-to-an-npm-registry-local]

May 15, 2019 · 1 min · jiezi

发布npm包踩坑

昨天试着发布npm包,遇到了问题,记录一下。 一:首先必须注册npm账号 用npm adduser 命令 npm ERR! code EAUTHUNKNOWNnpm ERR! Unable to authenticate, need: Basic把npm升级到6.9.0 ,报另一个错 npm ERR! code E401 npm ERR! Incorrect or missing password. npm ERR! If you were trying to login, change your password, create an npm ERR! authentication token or enable two-factor authentication then npm ERR! that means you likely typed your password in incorrectly. npm ERR! Please try again, or recover your password at: npm ERR! https://www.npmjs.com/forgot npm ERR! npm ERR! If you were doing some other operation then your saved credentials are npm ERR! probably out of date. To correct this please try logging in again with: npm ERR! npm login 继续百度,表面看样子是我密码不正确,其实因为重定向了npm库的源,所以npm adduser时会将用户名和密码提交到http://registry.npm.taobao.org 去验证 ...

May 15, 2019 · 2 min · jiezi

通过-Nodejs-买卖Bitcoin

上一章介绍了Exincore,你可以1秒完成资产的市价买卖。如果你想限定价格买卖,或者买卖一些exincore不支持的资产,你需要OceanOne。 方案二: 挂单Ocean.One交易所Ocean.one是基于Mixin Network的去中心化交易所,它性能一流。你可以在OceanOne上交易任何资产,只需要将你的币转给OceanOne, 将交易信息写在交易的memo里,OceanOne会在市场里列出你的交易需求,交易成功后,会将目标币转入到你的MixinNetwork帐上,它有三大特点与优势: 不需要在OceanOne注册不需要存币到交易所支持所有Mixin Network上能够转账的资产,所有的ERC20 EOS代币。预备知识:你先需要创建一个机器人, 方法在 教程一. 安装依赖包我们需要依赖 msgpack5 and mixin-node-client ,第四章 已经做过介绍, 你应该先安装过它了. 充币到 Mixin Network, 并读出它的余额.此处演示用 USDT购买BTC 或者 用BTC购买USDT。交易前,先检查一下钱包地址。完整的步骤如下: 检查比特币或USDT的余额,钱包地址。并记下钱包地址。从第三方交易所或者你的冷钱包中,将币充到上述钱包地址。再检查一下币的余额,看到帐与否。(比特币的到帐时间是5个区块的高度,约100分钟)。比特币与USDT的充值地址是一样的。 if ( args.type === TYPE_WALLET_ASSETS_INFO ) { const assetsInfo = await newUserClient.getUserAssets(); console.log("-AssetID--Asset--Balance--public_key--"); assetsInfo.forEach(function(element) { console.log(element.asset_id + " " + element.symbol + " " + element.balance + " " + element.public_key + " " + element.account_name + " " + element.account_tag ); }); // console.log(assetsInfo);}取得Ocean.one的市场价格信息如何来查询Ocean.one市场的价格信息呢?你要先了解你交易的基础币是什么,如果你想买比特币,卖出USDT,那么基础货币就是USDT;如果你想买USDT,卖出比特币,那么基础货币就是比特币. ...

May 15, 2019 · 4 min · jiezi

FEBASEvscode使用原理插件开发笔记

使用命令行使用帮助:code --help使用已经打开的窗口来打开文件:code -r打开文件并滚动到特定行:code -r -g package.json:128比较两个文件:code -r -d a.txt b.txt接受管道符数据:ls | code -图形界面、快捷键使用光标移动至 单词开头/末尾 Ctrl + Left/Right,代码块开头/末尾 Cmd + Shift + \文档首行/末行 Ctrl + Home/End文本 全选/剪切/复制/黏贴/保存 Ctrl A/X/C/V/S文本选择= Shift + 上述光标操作格式化 Alt + Shift + F格式化部分:选中,Ctrl + K + Ctrl + F行 删除 Ctrl + Shift + K剪切/复制/黏贴 Ctrl + X/C/V在当前行的下(上)面新开始一行 Ctrl ( + Shift) + Enter上/下移动 Alt + 上/下文档 查找/替换/符号跳转 Ctrl + F/H/T转到行 Ctrl + G转到文件 Ctrl + P转到符号 Ctrl + Shift + O打开命令面板 Ctrl + Shift + P窗体 ...

May 15, 2019 · 5 min · jiezi

流程图在线制作网站流程图怎么画输入

流程图是一种比较常见的图表,无论是在日常工作中还是生活中都随处可见。如果你是初入职场的新人,可能需要接触到一些简单的工作流程、会议流程图;如果你是产品经理、项目管理者或是软件开发师,更是需要经常接触到这类图表。流程图作为一种使用率非常高的图表,在商业中被誉为项目的基石。想要成为一名优秀的职场人士,掌握和熟练使用流程图,是一项必备的技能。曾经的某一天,你学会用Word绘制流程图,但为了对齐连接线,差点气到吐血。现在,你需要一款专业的流程图绘制工具,来拯救你的小宇宙! 亿图图示流程图制作软件「亿图图示」 是一款专业的图形图表设计软件,它可用于绘制全系列的流程图。一点也不夸张的说,亿图图示绘制流程图速度远高于Word绘图,让原本需要画1小时的流程图,只需10分钟即可完成。毕竟,专业的才是高效的。 与Word机械化的手动绘图方式不同,亿图图示更显智能,特别是以下几点功能尤为突出: 1、具备齐全的流程图符号,拖入画布即可; 2、搭载智能浮动按钮,可实现一键添加或修改符号; 3、画布智能识别连接点,和强迫症说再见; 4、双击符号,即可输入文本; 5、多套主题样式一键替换,所见即所得。 用亿图怎么画流程图使用亿图图示 绘制流程图 ,无论图有多难、多复杂,都可以被分解为简单的6个步骤。在软件里完成绘图后,流程图可以被导出为PDF、Ofiice以及PNG等格式。如果你想让作品进行在线分享,可以选择单独生成网页地址,这样你的同事、好友不用下载,网页在线或者手机扫一扫就能查看文档。 如果说亿图的哪一项功能更受欢迎,那么它的符号库应是当仁不让。据悉,亿图软件公司设计了一万多个矢量符号,使用的时候,可以直接从软件的左侧符号库中搜索获取。在符号库流程图这一分类下,内置了数据、判断、文档等上百个流程图符号。丰富的符号,专业的图形,帮助我们有效绘图,减少错误率,办公效率只增不减。 基本流程图符号素材: 基本流程图常用符号商务流程图常用符号亿图图示还提供了多样化的模板例子,除了流程图,还涉及思维导图、信息图、商业图表、组织架构图、拓扑图、电路图等等。现成的例子,可供我们参考和使用,这对于新手而言,无疑是加速学习、绘图的好办法。 亿图流程图软件实用模板流程图的运用领域很广很杂,或许曾经我们面对它的时候,是捶胸顿足、是不知所措,但如今学会 使用亿图图示画专业的流程图 之后,就再也不用担心。

May 15, 2019 · 1 min · jiezi

pm2-发布-node-配置文件ecosystemjson

背景最近在搭建一个node+koa+vue的项目使用到了pm2发布这里简单的记录一下在根目录新建文件ecosystem.json { "apps": [ { "name": "ant-help-center", "script": "./bin/www", //启动脚本 "env": { "COMMON_VARIABLE": "true" }, // 测试服务器 "env_development": { "NODE_ENV": "development", "PORT": 8087 }, // 生产环境 "env_production": { "NODE_ENV": "production", "PORT": 8087 } } ], "deploy": { // 生产环境 "production": { "user": "root", //Nginx服务器上的username "host": ["xxx.xxx.xxx.xxx"], // 服务器地址 "port": "22", "ref": "origin/master", //从指定的分支拉取代码 "repo": "git@gitee.com:xxx/xxxx.git", // 使用 "path": "/www/website/production", //发布到服务器指定的目录下 "ssh_options": "StrictHostKeyChecking=no", //构建在发布 "post-deploy": "npm install && pm2 startOrRestart ecosystem.json --env production", "env": { "NODE_ENV": "production" } }, // 测试环境 "development": { "user": "root", //Nginx服务器上的username "host": ["xxx.xxx.xxx.xxx"], // 服务器地址 "port": "22", "ref": "origin/master", //从指定的分支拉取代码 "repo": "git@gitee.com:xxx/xxxx.git", "path": "/www/website/development", //发布到服务器指定的目录下 "ssh_options": "StrictHostKeyChecking=no", //构建在发布 "post-deploy": "npm install && pm2 startOrRestart ecosystem.json --env development", "env": { "NODE_ENV": "development" } } }}需要注意点:一:repo参数要使用git ssh的地址二:先在服务器创建path 目录目录要有权限 ...

May 15, 2019 · 1 min · jiezi

使用vscode调试你的node应用

都9102年了, 你的nodejs应用还在用console调试吗?从一开始使用 webstorm 内置的 debug 功能, 到使用node-inspector库进行调试顺便脱离 webstorm 的笨重, 再后来 nodejs 内置了debugger 模块也可以帮助调试我们的应用. 目前个人使用 vscode 进行日常开发, 本文主要介绍 vscode 平台的 debugger 调试功能. vscode 本身就内置了 nodejs 的 debug 支持, 除此之外还有有非常多 debug 的扩展插件可供安装使用. 可以点击调试菜单 -> 安装调试附加器, 会自动去到下载插件的页面, 并筛选出debugger类型的插件, 按下载量进行排序. 不仅支持 nodejs/js 的调试, 如 C/C++, python, go 等都有相应 debugger 插件, 一般而言下载量更多都会比较靠谱. 而我们主要是为了调试 nodejs 应用, 就不需要额外去下载插件了. 快速对当前文件进行 debug要对当前打开的文件进行 debug 在 vscode 是非常简单的事, 只需要按快捷F5或在编辑器左侧 debug 面板按下启动的按钮, 然后选择 debug 类型即可. ...

May 14, 2019 · 2 min · jiezi

免费开源api

1. 网易云api网易云api是网上一位大神工具网易云获取的,数据都是真实的网易云数据 2. api大全这是csdn一个兄弟收集的,种类挺多,就是有一些需要money,不过大部分还是免费的 3. 免费api这是网上一个免费api网站,可以使用众多免费api 4.下面是我根据网易云api做的vue+electron高仿网易云桌面端应用,源码在github上,觉得可以的麻烦点个star,谢谢各位大佬高仿网易云

May 14, 2019 · 1 min · jiezi

如何提升VS-Code扩展的启动速度不只是Webpack

概述扩展可以让用户在VS Code中向开发工作流程添加新的语言、调试器和工具。VS Code提供了丰富的可扩展模块,允许扩展访问用户界面、提供扩展功能。通常情况下VS Code会安装多个扩展,所以作为一名扩展开发者,我们应该时刻关注扩展的性能,避免拖慢其它扩展,甚至是VS Code的主进程。 下面是我们在开发一款扩展时应该遵循的原则: 避免使用同步方法。同步方法会阻塞整个Node的进程,直到其返回结果。所以,你应该尽可能地使用异步方法。如果发现很难用异步方法替换同步方法,那么你应该考虑一下重构代码。只引用你需要的模块。有一些依赖模块非常巨大,比如说lodash。通常我们不需要lodash的全部方法,所以引用整个lodash模块并不合理。lodash的每个方法都有独立的模块,你应该只引用你需要的部分。谨慎对待启动条件。大多数情况下,你的扩展并不需要启动。不用使用“*”作为启动条件。如果你的扩展确实需要一直监听一些事件,考虑将主要的代码放在setTimeout里以低优先级运行。按需加载模块。import ... from ...是比较常用的引用模块的方法,但是有时这并不一定是个好的方法。比如一个叫做request-promise的模块,加载起来会耗费非常多的时间(在我自己这边测试需要1至2秒),但可只能有在特定的情况下我们才会需要请求远程的资源,比如本地的缓存过期了。上面提到的前三个原则很多开发者已经遵守了,在这篇文章中,我们会讨论按一种需加载的方法。这种方法要符合我们平时写TypeScript和JavaScript的习惯,同时也要尽可能减少更改现有代码的工作量。 按需加载模块符合习惯一般来说,我们在脚本的最顶端使用import来加载模块,比如下面的代码: import * as os from 'os';Node会同步加载指定的模块,同时阻塞后面的代码。 我们需要一个新的方法,比如叫做impor吧,用它可以引入模块,但并不马上加载这个模块: const osModule = impor('os'); // osModule不可访问,因为os模块还没有被加载为了达到这一目的,我们需要使用Proxy对象。Proxy对象被用来自定义一些基本操作的行为。 我们可以自定义get方法,只有当这个模块被调用时我们才开始加载它。 get: (_, key, reciver) => { if (!mod) { mod = require(id); } return Reflect.get(mod, key, reciver);}使用Proxy对象后,osModule是一个Proxy实例,并且只有当我们调用它的一个方法后,os模块才会被加载。 const osModule = impor('os'); // os模块还没有被加载...const platform = osModule.platform() // os模块从这里开始加载当我们只想使用模块的一部分时,广泛使用import {...} for ...的写法。可是这让Node不得不访问这个模块来检查其属性值。这样getter就会被调用,模块也会在那个时候被加载。 使用后台任务加载模块按需加载还不够,我们可以进一步来优化用户体验。在扩展启动和用户运行命令来加载模块之间,我们有充足的时间来提前加载模块。 很容易想到的一个办法,是创建一个后台任务来加载队列里的模块。 时间线我们开发了一个名叫Azure IoT Device Workbench的扩展,它可以结合多个Azure服务和流行的物联网开发板,简单地进行物联网项目的开发、编译、部署和调试。 由于Azure IoT Device Workbench涉及到的范围非常广泛,所以这个扩展启动起来非常繁重。同时它又需要监听USB事件,当物联网设备插入计算机后做出响应。 ...

May 14, 2019 · 2 min · jiezi

一键式创建ReactVue组件

mn-component-maker一键式创建React,Vue组件命令工具mn-component-maker如有问题欢迎大家反馈修改v1.2.1支持自由选择创建css,scss,less 功能一键式创建React组件,不是React脚手架!支持一次创建多个组件支持自由选择创建css,Scss,Less支持无状态组建(stateless function)UsageReact组件创建使用#React组件创建使用npm i -g mn-component-makermkcomponent App#此时你创建了一个名字为App的组件mkcomponent Header,Body,Footer#此时你创建了三个组件,分别为Header,Body,FooterVue组件创建使用#Vue组件创建使用npm i -g mn-component-makermkcomponent -l Body -t vue#此时你创建了一个名字为Body的组件创建样式为Scss的组件mkcomponent -s Body//创建Body组件,但是样式文件为Scss创建样式为Less的组件#### mkcomponent -l Body//创建Body组件,但是样式文件为lessReact组件详情一个组件为一个文件夹,文件夹目录为 [name].jsx[name].cssindex.jsxVue组件详情[name].cssindex.vue文件内容 - React[name].jsximport React from 'react';import styles from './[name].css'class [name] extends React.Component { constructor(props) { super(props); this.displayName = [name]; } render() { return ( <div className={styles.container}> [name] </div> ) }}export default [name];[name].css.container { }index.jsximport [name] from './hh'export default [name]文件内容 - Vueindex.vue<template> <div class="[name]"> [name]组件 </div></template>'<script>export default { name: [name] data(){ return { } }};</script><style scoped lang='css' src='[name].css'></style>[name].css.container { }

May 14, 2019 · 1 min · jiezi