Quantcast
Channel: CNode:Node.js专业中文社区
Viewing all 14821 articles
Browse latest View live

求一些2018年的前端面试题及侧重的知识点

$
0
0

求一些2018年的前端面试题及侧重的知识点; 近几年,前端知识迭代很快,面试题也需要推陈出新一下,担心知识点考察不到位


Roadmap to becoming a web developer in 2018堪称经典

superagent 怎么请求到 301 跳转前的网页代码?

$
0
0

比如说 http://sf.gg网站,直接用 superagent 请求到的是 301 跳转后的 https://segmentfault.com的内容,我用 curl 命令获取 http://sf.gg的内容如下:

<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.4.6 (Ubuntu)</center>
</body>
</html>

我希望得到 301 前也就是如上的内容,如何用 superagent 实现? 看到 superagent 有个 Following redirects,这个需求和这个有关系吗?有的话应该怎么用?搞了很久没实现

望赐教,谢谢!

用vue写了一cnode首页,不一样的风格哦

$
0
0

为自己这套http://vadmin.lanyueos.com模板写个vuex的例子,然后就拿cnode接口写了cnode首页。

传送门image.png

vue-element-admin-tpl这个模板我是我工作和平时开发的积累,可以快速搭建后台管理平台系统的模板。 我会不定期更新这个模板,欢迎star。

之前学习整理的pug文档. 社区虽然也有,但是都是很久之前的.因此分享一下,希望对新人有用.

$
0
0

jade(pug)

由于商标版权问题,Jade 已经改名为了 Pug,github地址https://github.com/pugjs/pug

Jade 是一个高性能的模板引擎,它是用 JavaScript 实现的,并且可以供 Node 使用,当然还支持其他语言。

文件后缀名为.pug(.jade)

jade优点

  • 可读性高
  • 灵活的缩进
  • 块展开
  • 代码默认经过编码处理(转义),安全性高
  • 运行时和编译时上下文错误报告
  • 支持命令行编译
  • 支持html5模式
  • 在内存中缓存(可选)
  • 原生支持 Express
  • 合并动态和静态标签类
  • 过滤器

关于Ejs或者其他模板引擎与jade比较,可以看看这篇文章https://www.zhihu.com/question/20355486

安装

npm安装建议安装个nrm来进行源管理.

npm install pug -g
npm install pug-cli -g

测试demo

为了方便编写代码,最好把编译器的tab设置:2.

// index.jade

doctype html
html
  head
    title jade test
  body
    h2 jade study    
粗暴的编译方法
pug index.jade

// index.html
<!DOCTYPE html><html><head><title>jade test</title></head><body><h2>jade study    </h2></body></html>

发现编译后的代码不具备可读性.

pug -- help
 Options:
     -P, --pretty           compile pretty HTML output ## 输出漂亮结构的HTML
    -D, --no-debug         compile without debugging (smaller functions) ## 不带调试的编译
    -w, --watch            watch files for changes and automatically re-render ## 对某个文件的变动保持监控
    -E, --extension <ext>  specify the output file extension ## 指定输出文件扩展名
    -s, --silent           do not output logs ## 不输出日志
   
// 重新编译
pug -P index.jade
<!DOCTYPE html>
<html>
  <head>
    <title>jade test</title>
  </head>
  <body>
    <h2>jade study    </h2>
  </body>
</html>
自动编译

只是为了学习,这里只要设置-w -P .开发中通过打包工具来进行自动编译.

// 命令行工具推荐使用Cmder

λ pug -P -w index.jade
  watching index.jade
  rendered index.html

Express与Pug

Pug完全集成了一个流行的Node.js Web框架Express,作为支持的视图引擎。 看看Express是如何将Pug与Express集成的完美指南。

在Express中,环境变量NODE_ENV旨在向Web应用程序通知执行环境:无论是在开发中还是在生产中。 Express和Pug自动修改生产环境中的几个选项的默认值,为用户提供更好的开箱即用体验。 具体来说,当process.env.NODE_ENV设置为“production”,Pug与Express一起使用时,compileDebug选项默认为false,而cache选项为true。

API

标签属性
  • id,class写法
// 编译前

  p.title class写法1
  p(class='title') class写法2
  p#tit id写法1
  p(id='tit2') id写法2     
  
// 编译后
  <p class="title">class写法1</p>
  <p class="title">class写法2</p>
  <p id="tit">id写法</p>
  <p id="tit2">id写法2 </p>
// 编译前
- var classArr = ['small','medium','large']
a(class= classArr)
a.test(class = classArr class=['add'])

// 编译后
<a class="small medium large"></a>
<a class="test small medium large add"></a>

它也可以是将类名映射到true或false值的对象.

//编译前
- var active = 'select'
a(class={active: active === 'select'} )

// 编译后
<a class="active"></a>
  • 其他属性 通过()来依次编写属性,多个用逗号隔开.
//编译前
a(class='baidu' ,title='baidu' href='www.baidu.com') 百度

//编译后
<a class="baidu" title="baidu" href="www.baidu.com">百度</a>
  • 也支持所有正常的javascript表达式
// 编译前
- var flag  = true   //注意这里使用变量要记得添加-符号.
h2(class=flag ? 'flag': '')

// 编译后
<h2 class="flag"></h2>
  • 多个属性的另外写法

其实就是换号缩进

// 编译前
a(
      title='baidu',
      href='www.baidu.com',
      class='links'
)
// 编译后
<a class="links" title="baidu" href="www.baidu.com"></a>

如果您有一个非常长的属性,并且您的JavaScript运行时支持ES2015模板字符串,则可以使用该语法的属性:

// 编译前
input(data-json=`
      {
        "very-long": "piece of ",
        "data": true
      }
`)
// 编译后
<input data-json="
      {
        &quot;very-long&quot;: &quot;piece of &quot;,
        &quot;data&quot;: true
      }
">
  • 引用属性

如果你的属性名称包含了与JavaScript语法冲突的字符,请使用""或’'引用,或使用逗号分隔不同的属性。

官网举了个Angular 2的例子.

//(click)='play()',这里(click)会被当作一个函数调用而不是一个属性名字来解析.

// 编译前
div(class='div-class' (click)='play()')

// 编译后报错
div(class='div-class' (click)='play()')
---------------------^

正确写法

// 编译前
div(class='div-class' '(click)'='play()')
div(class='div-class', (click) = 'play()')

// 编译后
<div class="div-class" (click)="play()"></div>
<div class="div-class" (click)="play()"></div>
  • 属性插值

以前版本的Pug / Jade支持如下插值语法(不再支持):

//编译前
- var url = 'www.baidu.com'
a(href='/#{url}') links

//编译后 已不再支持
<a href="/#{url}">links</a>

新的写法

// 编译前
- var url = 'demo.com'
a(href='/' + url) links
- var url2 = 'www.baidu.com'
a(href = url2 ) 百度  

// 编译后
<a href="/demo.com">links</a>
<a href="www.baidu.com">百度  </a>

如果你的javascript运行环境支持ES 2015.那么Pug支持模板字符串语法

// 编译前
- var size1 = 'small'
- var size2 = 'medium'
- var size3 = 'large'
button(
  type='button',
  class='btn btn-' + size1 + ' ' +  'btn-' + size2 + ' ' + 'btn-' + size3
)
button(
  type='button',
  class=`btn btn-$(size1) btn-$(size2) btn(size3)`
)
// 编译后
<button class="btn btn-small btn-medium btn-large" type="button"></button>
<button class="btn btn-small btn-medium btn-large" type="button"></button>
  • 未转义属性

默认情况下,会转义所有属性(用转义序列代替特殊字符),以防止诸如跨站点脚本之类的攻击。 如果必须需要使用特殊字符,可以使用!=而不是=。

// 编译前
div(title="<code>")
div(title!="<code>")

// 编译后
<div title="&lt;code&gt;"></div>
<div title="<code>"></div>
  • 布尔属性

布尔属性由Pug镜像,并接受布尔值(true和false)。 当没有指定值时,默认为true。

// 编译前
input(type='radio' checked)
input(type='radio' checked=true)
input(type='radio' checked=false)
    
// 编译后
<input type="radio" checked>
<input type="radio" checked>
<input type="radio">
  • style属性

style属性可以是一个字符串(像任何普通属性),但它也可以是一个对象

// 编译前
p(style={fontSize: '14px',color: 'red'})

// 编译后
<p style="fontSize:14px;color:red;"></p>
Case

case语句是JavaScript Switch语句的缩写,并采用以下形式:

// 编译前
- var friendsNum = 4
    case friendsNum
      when 0
        p you have not friend
      when 1
        p you has one friend
      default
        p you has #{friendsNum} friends    
        
// 编译后
<p>you has 4 friends    </p>
// 编译前
- var friendsNum = 1
    case friendsNum
      when 0
      when 1
        p you has one friend
      default
        p you has #{friendsNum} friends
        
// 编译后
<p>you has one friend</p>

当然也支持break;

// 编译前

- var friendsNum = 0
    case friendsNum
      when 0
        - break
      when 1
        p you has one friend
      default
        p you has #{friendsNum} friends
        
// 编译后
无内容

也可以使用块扩展语法

// 编译前
  - var friendsNum = 1
    case friendsNum
      when 0
      when 1: p you has one friend
      default: p you has #{friendsNum} friends
      
// 编译后
<p>you has one friend</p>

Code

Pug可以在你的模板中编写内置的JavaScript代码。 有三种类型的代码。

  • Unbuffered Code 不直接添加任何的输出
// 编译前
- for(var i = 0; i < 3;i++)
      li item
      
// 编译后
<li>item</li>
<li>item</li>
<li>item</li>
// 编译前

- var nameList = ['kobe','cpul','james']
      each item in nameList
        li=item
        
// 编译后

<li>kobe</li>
<li>cpul</li>
<li>james</li>
  • Buffered Code

以=开头,并输出评估模板中JavaScript表达式的结果。 为了安全起见,首先HTML被转义:

// 编译前

p
  = 'this is code template <code>'
p= 'this is code template' + '<code>'  

// 编译后

<p>this is code template &lt;code&gt;
</p>
<p>this is code template&lt;code&gt;</p>
  • Unescaped Buffered Code

未转义的代码以!=开头,并输出评估模板中JavaScript表达式的结果。 这不会进行任何转义,所以对用户输入是不安全的:

// 编译前
p
  != 'this is code template <code>'
p!= 'this is code template' + '<code>'  

// 编译后
<p>this is code template <code>
</p>
<p>this is code template<code></p>
Comments注释
  • 单行注释
// 编译前

// 这是一个注释
p 这是一个注释

// 编译后

<!-- 这是一个注释-->
<p>这是一个注释</p>

Pug还有种注释写法,只需添加连字符’-'即可。这些仅用于对Pug代码本身进行注释,编译后不会出现在HTML中。

// 编译前

//- 这是一个注释
p 这是一个注释
    
// 编译后
<p>这是一个注释</p>
  • 块级注释
// 编译前

//-
  注释不会出现在模板中
  真的
//
  第一行注释
  第二行注释
      
// 编译后

<!--
    第一行注释
    第二行注释
-->
  • 条件注释

对于条件注释,Pug没有任何特殊的语法,下面例子这是为旧版本的Internet Explorer添加后备标记的特殊方法,但是由于以<开头的所有行被视为纯文本,普通的HTML样式条件注释将会很好。

// 编译前
<!--[if IE 8]>
<p class="lt-ie9">
<![endif]-->
    
// 编译后

<!--[if IE 8]>
<p class="lt-ie9">
<![endif]-->

Conditionals条件语句
// 编译前
- var user = {name: 'kobe'}
- var flag = true
#user
  if user.name
    h3.user-title #{user.name}
  else if flag
    p flag is #{flag}
  else
    p default   
    
// 编译后
<div id="user">
  <h3 class="user-title">kobe</h3>
</div>

Pug也支持另外一个关键字 unless

// 编译前
- var user = {name: 'kobe'}
#user
  unless !user.name
  h2 #{user.name}


// 编译后
<div id="user">
  <h2>kobe</h2>
</div>
doctype
// 编译前

doctype html

// 编译后

<!DOCTYPE html>

Filters过滤器

过滤器可让你在Pug模板中使用其他语言。也就是支持插件的使用,通过插件对模板内容进行过滤,处理,输出.如scss,less,markdown,coffee-script…

先全局安装这些插件

npm install --save jstransformer-coffee-script
npm install --save jstransformer-markdown-it
// 编译前

h2 MarkDown
  :markdown-it
    #### this is markdown filter
    [link](http://www.baidu.com)

  :coffee-script
    console.log('this is coffee-script');

// 编译后

<h2>MarkDown</h2><h4>this is markdown filter</h4>
<p><a href="http://www.baidu.com">link</a></p>
    (function() {
  console.log('this is coffee-script');

}).call(this);

缺点:不能支持动态内容或选项。

Includes包含

Pug允许你静态包含一段 Jade, 或者别的存放在单个文件中的东西比如 CSS, HTML 非常常见的例子是包含头部和页脚。 假设我们有一个下面目录结构的文件夹:

- /index.jade
- /includes/
  -/head.jade
  -/footer.jade
// index.jade
doctype html
html
  include includes/header.jade
  body
    h1 这是主题内容
    include includes/footer.jade  

// header.jade
header
  title 通用的header
  script(src='/jQuery.js')
  link(href='reset.css')

// footer.jade
footer#footer
  p Copyright (c) foobar

// 编译后
<!DOCTYPE html>
<html>
  <header>
    <title>通用的header</title>
    <script src="/jQuery.js"></script>
    <link href="reset.css">
  </header>
  <body>
    <h1>这是主题内容</h1>
    <footer id="footer">
      <p>Copyright (c) foobar</p>
    </footer>
  </body>
</html>

include 可以包含比如 HTML 或者 CSS 这样的内容。给定一个扩展名后,Jade 不会把这个文件当作一个 Jade 源代码,并且会把它当作一个普通文本包含进来:

// 格式

style
  include style.css
script
  include script.js  

甚至可以通过include结合过滤器使用

// 引入article.md
include:markdown-it article.md
模板继承

Jade 支持通过 block 和 extends 关键字来实现模板继承。 一个块就是一个 Jade 的 block ,它将在子模板中实现,同时是支持递归的。

如果需要,Pug block 可以提供默认内容,但是可以通过block scripts, block content, 和 block foot来显示如下所示的可选项。

// 基本使用

// 编译前
block desc
  p 这是block context
block desc  
  
// 编译后
<p>这是block context</p>
// index.jade

doctype html
html
  include includes/header.jade
  body
    block content
    block foot
    .footer  some footer content

现在要继承上面那个index.jade,只需创建一个新文件,并使用extend指令,如下所示,给出路径。 您现在可以定义一个或多个block将覆盖父块内容的块.

在Pug v1中,如果没有给定文件扩展名,那么.pug会自动附加到路径上,但是在Pug v2中,这个行为已被弃用。

// index.jade

doctype html
html
  block scripts
    script(src='/jquery.js')
  body
    block content
    block foot
    .footer  some footer content

// page.jade

extends index.jade

block scripts
  script(src='/jquery.js')
  script(src='/page.js')

block content
  h2 page.jade
  - var pets = ['cat', 'dog']
  each petName in pets
    li=petName
// page.jade 编译后

<!DOCTYPE html>
<html>
  <script src="/jquery.js"></script>
  <script src="/page.js"></script>
  <body>
    <h2>page.jade</h2>
    <li>cat</li>
    <li>dog</li>
    <div class="footer"> some footer content</div>
  </body>
</html>

同样可以在一个子块里继续添加块,就像下面实现的块 content 里又定义了两个可以被实现的块 sidebar 和 primary,或者子模板直接实现 content。

block content
  .sidebar
    block sidebar
      p nothing
  .primary
    block primary
      p nothing

Pug允许你 替换 (默认)、 前置 和 追加 blocks. 使用 block append 或 block prepend 时 block 是可选的:

// pageTwo.jade

extend page.jade

append scripts
  script(src='/pageTwo.js')
// 编译后

<!DOCTYPE html>
<html>
  <script src="/jquery.js"></script>
  <script src="/page.js"></script>
  <script src="/pageTwo.js"></script>
  <body>
    <h2>page.jade</h2>
    <li>cat</li>
    <li>dog</li>
    <div class="footer"> some footer content</div>
  </body>
</html>
Interpolation插值
// 编译前
- var author = 'xyz'
- var date = '2017-4'
h2
  p writer was by #{author.toUpperCase()}
  p date is #{date}

// 编译后

<h2>
  <p>writer was by XYZ</p>
  <p>date is 2017-4</p>
</h2>

如果你想保持#{}插值符号,可以使用#{或者’’.

Iteration迭代
  • each
// 编译前
- var arr = [1,2,3,4]
each val,index in arr
  li= index + ':' + val 

// 编译后

<li>0:1</li>
<li>1:2</li>
<li>2:3</li>
<li>3:4</li>

  • while
// 编译前
- var arr = 4
while arr > 0
  li= arr--


// 编译后

<li>4</li>
<li>3</li>
<li>2</li>
<li>1</li>
Mixins

Mixins允许您创建可重用的Pug block。

// 编译前
mixin lists
 p this is a mixin block
+lists
+lists  

// 编译后

<p>this is a mixin block</p>
<p>this is a mixin block</p>

mixins可以为一个带参数的函数

// 编译前
mixin link(href,name)
  a(href=href)= name

+link('www.baidu.com','百度')  
  
// 编译后

<a href="www.baidu.com">百度</a>

mixins也可以使用一个block来作为内容

// 编译前
mixin lists(names)
 p= 'my name is ' + names
 if block
   block
 else
   p not provided content
+lists('kobe')
+lists('cpul')
  p block content

// 编译后

<p>my name is kobe</p>
<p>not provided content</p>
<p>my name is cpul</p>
<p>block content</p>

未知数量参数(…)的mixins.

// 编译前
mixin lists(className,...items)
  ul(class=className)
    each item in items
      li= item

+lists('demo',1,2,3)
// 编译后

<ul class="demo">
  <li>1</li>
  <li>2</li>
  <li>3</li>
</ul>
Tags

默认情况下,一行开头的文本(或仅在空格之后)代表一个html标签。 缩进的标签是嵌套的,创建了像html的树结构。

Pug可以判断出哪些元素是自闭,您还可以通过简单地附加’/'字符来明确地自己关闭标签:

为了节省空间,Pug提供嵌套标记的内联语法。

// 编译前
p: span 内联

// 编译后
<p><span>内联</span></p>

Pug的缺点

凡事不可能完美.Pug也有自己的弊端.

  • 可移植性差
  • 对新手调试不方便
  • 性能不是很好

原文.https://github.com/1657413883/blog-notes/issues/81

深入理解React源码 VI-英文预览版

求分析一个神奇的 HTTP 跳转

$
0
0

最近在研究大麦的购票流程,中间有个 URL 的跳转实在百思不得其解。 以这个 URL 为例:<www.damai.cn/GotoShopping.aspx?_action=Immediately&proId=12123708&optype=1&companyId=1580&num=1&n=0> 在浏览器输入后页面会跳转到 https://trade.damai.cn/multi/confirm?***这样一个页面。 我直接用 charles 抓包,结果如下: 1.png截图框内部分正是最后重定向到的链接。 但是我用 Node 来模拟请求,想得到请求的页面,始终拿不到,我也尝试获取 302 跳转前的页面:

superagent
    .get(url)
    .set('Cookie', cookie)
    .redirects(0)
    .on('error', err => {
      console.log(err.response.text)
    })
    .end(function (err, sres) { // callback
    })

我省略了一些请求头,实际上我都带上了,结果如下: 2.png而且我也获取不到最后重定向页面的代码,一直获取的是商品详情页,也就是这个页面 https://piao.damai.cn/141343.html的代码。 我有两个问题:

  1. 我怎么根据 <www.damai.cn/GotoShopping.aspx?_action=Immediately&proId=12123708&optype=1&companyId=1580&num=1&n=0> 获取最后跳转到的页面的 URL(假设有 cookie 等所有请求头信息)
  2. 怎么根据 <www.damai.cn/GotoShopping.aspx?_action=Immediately&proId=12123708&optype=1&companyId=1580&num=1&n=0> 获取最后跳转到的页面的 HTML 代码?我将所有头部信息全部填完后模拟请求,一直请求到的是商品详情页也就是这个页面 https://piao.damai.cn/141343.html的代码,不知道哪个环节出了问题

我用的 HTTP 库是 superagent,望赐教!

不得已要放下node, 转java了

$
0
0

年纪大了快要撤回老家了, 西安基本没有node的机会, 已经学了一段时间java了, 你们有什么想说的 image.png


前端调用 GraphQL API,从未如此方便!

$
0
0

屏幕快照 2018-01-29 上午2.45.30.png

GraphQL 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。 GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余。

想更多的了解或使用 GraphQL,请访问 https://github.com/facebook/graphql

GraphQL 有针对不同语言的服务端实现,以帮助开发人员搭建 GraphQL Server

gq-loader是一个 webpack插件,你可以认为它一针对前端项目的一种 client端实现,它的目的是帮助前端开发同学更简便的调用 GraphQL API,它让前端开发人员在使用 GraphQL 时更加方便,像普通 js模块一样轻松自如,使前端开发人员能在 js文件中通过 importrequire导入 .gql.graphql文件,然后直接调用。 并且它还支持通过 #import语法导入其它 .gql文件,比如 fragments。

#import还提供了两个别名,分别是 #require#include,这两个别名和 #import的用法及行为完全一致。

关注或使用 gq-loader,请移步 GitHub:https://github.com/Houfeng/gq-loader

安装

npm install gq-loader --save-dev

或者

yarn add gq-loader

基本使用

如同其它类的的 loader 一样,首先,我们在 webpack.config.js中添加 gq-loader的配置

{
  test: /\.(graphql|gql)$/,
  exclude: /node_modules/,
  use: {
    loader: 'gq-loader'
    options: {
      url: 'Graphql Server URL'
    }
  }
}

然后,我们就可以在 js文件中通过 import来导入 .gql文件使用它了,我们来一个简单的示例,假设已经有一个可以工作的 Graphql Server,那么,我们先创建一个可以查询用户的 getUser.gql

#import './fragment.gql' 

query MyQuery($name: String) {
  getUser(name: $name)
    ...userFields
  }
}

可以看到,我们通过 #import引用了另外一个 .gql文件 fragment.gql,在这个文件中我们描术了要返回的 user 的字段信息,这样我们就能在不同的地方「重用」它了,我们也创建一下这个文件

fragment userFields on User {
  name
  age
}

好了,我们可以在 js文件中直接导入 getUser.gql,并且使用它查询用户了,从未如此简便,我们来看看

import getUser from './getUser.gql';
import React from 'react';
import ReactDOM from 'react-dom';

async function query() {
  const user = await getUser({ name: 'bob' });
  console.log('user', user);
}

function App() {
  return <button onClick={query}>click</button>;
}

ReactDOM.render(<App />, document.getElementById('root'));

在调用 getUser时,我们可以通过参数,向 GraphQL传递变量,这些变量就是我们的查询参数。

自定义请求

默认 gq-loader就会帮你完成 graphql 请求,但某些场景下或许你想自已控制所有请求,如果有这样需要,我们还可以通过 request属性来「自定义」请求,看一下示例,需要先稍微改动一下 loader 配置

{
  test: /\.(graphql|gql)$/,
  exclude: /node_modules/,
  use: {
    loader: 'gq-loader'
    options: {
      url: 'Graphql Server URL',
      //指定自动请求模块路径
      request: require.resolve('your_request_module_path');
    }
  }
}

your_request_module_path填写自定义请求模块路径,gq-loader将自动加载并使用对应请求模块,模块只需要改出一个「请求函数即可」,看如下自定义示例

const $ = require('jquery');

//url 是要请求的 GraphQL 服务地址
//data 是待发送的数据
//options 是自定义选项
module.exports = function(url, data, options){
  //如果有需要还可以处理 options
  return $.post(url, data);
};

其中,options是导入 .gql文件后「函数的第二个参数」,比如,可以这样传递 options参数

import getUser from './getUser.gql';

async function query() {
  const options = {...};
  const user = await getUser({ name: 'bob' }, options);
  console.log('user', user);
}

完整选项

名称说明默认值
URL指定 graphql 服务 URL/graphql
request自定义请求函数使用内建模块
extensions默认扩展名,在导入时省略扩展名时将按配置依次查找.gql/.graphql
string指定导入模式,当为 true 时导入为字符串,而不是可执行的函数false

注意,gq-loaderextensions无论配置何值,在 jsimport时都不能省略扩展名,此选项仅作用于 .gql文件 import其它 .gql文件

看到一个typescript方法,不能理解,看了编译结果,更不能理解了

$
0
0

原来的是定义了这样一个方法:

let reactionScheduler: (fn: () => void) => void = f => f()

看了好久没看懂这个方法干哈用的,于是就拿去跑一下编译,结果出来的结果更是让我不能理解。 结果如下:

var reactionScheduler = function (f) { return f(); };

有没有大神给剖析一把上面的方法。 再一个问题是,定义这个方法的意义在哪里?

time.unref 没有按预期取消对应的函数回调

$
0
0
var timer = setInterval(function () {
	console.log(new Date, 1)
}, 1000)

var fn = function () {
	console.log(new Date, 2)
}

var timer2 = setInterval(fn, 1000)
timer2.unref()

在这里一直会间断输出1, 2, 为什么timer2 的回调没有被取消

  • 使timer2正常unref 1.但是将timer 这段代码去除就可以正常的取消timer2 的回调了, 2、timer 也加上 unref

难道是永远只能有一个setInterval 在event loop中才能使用unref 生效吗

【招聘】上海区块链技术公司 BitPortal 招募工程师

$
0
0

BitPortal是一家区块链初创企业,主要提供区块链行情,社区,应用,支付等方面的服务。公司主要办公地点在浦东高科西路地铁站附近的中建广场,6,7号线可达。目前公司处于早期组建阶段,现阶段加入成员享有额外惊喜。

后端工程师(薪资范围:15K ~ 30K)

后端编程语言 Node / Go 至少熟悉掌握其中一门 熟练掌握 MySQL,MongoDB 数据库中至少一种;对ORM概念熟悉,并有使用 Sequelize,Gorm等ORM插件的经验者优先 熟练掌握 至少一种Web后端框架:Express,Koa,Hapi,Loopback,Egg, Gin, BeeGo (或者其他主流框架) 有至少一个前后端分离的后端项目上线经验,对RestFul API 结构熟悉 3 年或以上的 Web/Cloud 应用开发经验 熟练运用 OOP, Unit Test和Design Pattern,对代码精益求精

前端工程师(薪资范围:20K ~ 35K)

前端框架 React / VUE 至少熟练掌握其中一门 对JavaScript / npm 主流工具熟练使用 编写模块化的,高质量的代码,并能积极参与到 Peer code reviews. 有较强的 Troubleshooting 能力,能独立研究和解决困难问题 与后台技术、产品经理保持良好沟通,快速理解消化需求,并落实为具体的开发工作

工程师加分项参与过GitHub热门项目者优先 对区块链技术有了解者优先(共识机制,侧链技术,隔离见证,Solidity,Web3)

联系方式:jobs@bitportal.io 请附上你的简历,期望薪资和应聘职位,期待你的加入!

3年产品转node,怎么样比较顺利的拿到面试机会

$
0
0

现在找人都比较看经验呢,之前一直是干的产品,越做越不顺心,思考一番决定转行。 也用Node写过小玩意,但是吧肯定上不了台面。也不能说自己闷头学个一年半年再来找。 想问问有没有前辈也是转行的成功,讨教些经验,小弟在此谢过啦~

node源码粗读(7):nextTick和microtasks从bootstrap到event-loop全阶段解读

$
0
0

这篇文章主要介绍nextTick和RunMicrotasks的主要流程和涉及到的相关源码,对于timers相关api在event-loop中的表现不做解读

nextTick实现

目光直接转移到next_tick.js,整体nextTick的代码其实很容易理解:

 const [
    tickInfo,
    runMicrotasks
  ] = process._setupNextTick(_tickCallback);
function nextTick(callback) {
   // ...
   nextTickQueue.push(new TickObject(callback, args, getDefaultTriggerAsyncId()));
  }
function _tickCallback() {
    let tock;
    do {
      while (tock = nextTickQueue.shift()) {
      // ...
      const callback = tock.callback;
        if (tock.args === undefined)
          callback();
      runMicrotasks();
    } while (nextTickQueue.head !== null || emitPromiseRejectionWarnings());
    tickInfo[kHasPromiseRejections] = 0;
  }

通过这两个函数,就能看出来整个nextTick是如何工作的。

  • nextTickQueue为记录nextTick的数组,有新的nextTick注册进来就会被推入数组
  • _tickCallback则会不断的推出数组中的元素然后运行

大家注意一下process._setupNextTick(_tickCallback),最终这个_tickCallback并没有在js中执行,而是传递给了c++:

// node.cc
void SetupNextTick(const FunctionCallbackInfo<Value>& args) {
  Environment* env = Environment::GetCurrent(args);
  CHECK(args[0]->IsFunction());
  env->set_tick_callback_function(args[0].As<Function>());
  // ...

在这里可以看出来,最终_tickCallback丢给了tick_callback_function,然后在LoadEnvironment中通过_setupNextTick触发运行(LoadEnvironment之前详细介绍过,在这里不做过多介绍),在这里简单的追踪了一下_tickCallback来证实一下最终_tickCallback传递给了tick_callback_function

process.nextTick(()=>console.log(2))

issue16-1

tips: 蓝色底色代码为断点所在位置,下方为此时刻的内存地址,上面这张图可以看出来在没有跑LoadEnvironment的时候,tick_callback_function为NULL

issue16-2如果对LoadEnvironment比较了解的读者,应该是明白其中的原理的,如果不明白原理可以简单看一下tick_callback_function这里的内存变化。这里我们假设读者了解node启动的所有机制,那么就会发现一件事情:process.nextTick运行的时候,uv_run尚未启动
那么,我们可以根据这个显现得出一个比较浅显的结论:process.nextTick会阻塞libuv的事件循环。(这是在node初始化bootatrap阶段的情况。即使在evnt_loop中,表现也是一样的。为何用这个阶段来叙述,是因为这个阶段最容易追踪和解读)

process.nextTick和RunMicrotasks

通过前一章节的叙述和上一篇文章对setTimeout流程的分析,我们可以发现:process.nextTick不是基于libuv事件机制的,而timers一系列的api全部是基于libuv开放出来的api实现的。那么这个nextTick到底是如何实现的呢?
接下来就要从nextTick的源码聊起了:

function _tickCallback() {
    let tock;
    do {
      while (tock = nextTickQueue.shift()) {
      // ...
      const callback = tock.callback;
        if (tock.args === undefined)
          callback();
     // ...
     }
      runMicrotasks();
    } 
  // ...
  }

在执行完nextTick之后(callback())还继续执行了runMicrotasks,我相信如果了解过Microtasks的读者肯定知道这到底是做什么的,接下来我们深扒一下这个runMicrotasks

// src/node.cc
v8::Local<v8::Function> run_microtasks_fn =
      env->NewFunctionTemplate(RunMicrotasks)->GetFunction(env->context())
          .ToLocalChecked();//v8 吐出来的方法 RunMicrotasks
run_microtasks_fn->SetName(
      FIXED_ONE_BYTE_STRING(env->isolate(), "runMicrotasks"));

// deps/v8/src/isolate.cc
void Isolate::RunMicrotasks() {// v8中RunMicrotasks实现
  // Increase call depth to prevent recursive callbacks.
  v8::Isolate::SuppressMicrotaskExecutionScope suppress(
      reinterpret_cast<v8::Isolate*>(this));
  is_running_microtasks_ = true;
  RunMicrotasksInternal();
  is_running_microtasks_ = false;
  FireMicrotasksCompletedCallback();
}
void Isolate::RunMicrotasksInternal() {
  if (!pending_microtask_count()) return;
  TRACE_EVENT0("v8.execute", "RunMicrotasks");
  TRACE_EVENT_CALL_STATS_SCOPED(this, "v8", "V8.RunMicrotasks");
  while (pending_microtask_count() > 0) {
    HandleScope scope(this);
    int num_tasks = pending_microtask_count();
    Handle<FixedArray> queue(heap()->microtask_queue(), this);
    DCHECK(num_tasks <= queue->length());
    set_pending_microtask_count(0);
    heap()->set_microtask_queue(heap()->empty_fixed_array());
  // ...

通过上面的代码,可以比较清晰地看到整个RunMicrotasks的全过程,主要就是通过microtask_queue来实现的Microtask。 了解了整个流程,可以很容易得出一个结论:nextTick会在v8执行Microtasks之前对在js中注册的nextTickQueue逐个执行,即阻塞了Microtasks执行。

bootstrap阶段和event-loop时候的异同

通过上面的分析,下面这段代码在bootstrap阶段,应该很容易理解:

setTimeout(()=>console.log('timers API'),0)//uv_run开始运行后才执行timers相关api,最后执行
console.log('bootstrap')//在node LoadEnvironment(bootstrap)阶段执行,最先执行
new Promise((resolve,reject)=> resolve('microtask run')).then(arg => console.log(arg))//注册到microtask_queue中
process.nextTick(()=>console.log('run next tick'))// 会在microtask之前运行

结果如图: issue16-3相关解释已经写到了上面的注释中。 (当然这里用console来作为同步代码不是很严谨,不过比较直观)

那么在event-loop中是如何表现的呢?在上文中也提到过一句:

这是在node初始化,即bootstrap的情况下,即使在evnt_loop中,表现也是一样的

event-loop中的区别是:本应该在node LoadEnvironment(bootstrap)阶段执行的代码的运行转移到了InternalMakeCallback
下面是InternalMakeCallback的代码:

// ./src/node.cc
MaybeLocal<Value> InternalMakeCallback(Environment* env,
                                       Local<Object> recv,
                                       const Local<Function> callback,
                                       int argc,
                                       Local<Value> argv[],
                                       async_context asyncContext) {
  CHECK(!recv.IsEmpty());
  InternalCallbackScope scope(env, recv, asyncContext);
  if (scope.Failed()) {
    return Undefined(env->isolate());
  }

  MaybeLocal<Value> ret;

  {
    ret = callback->Call(env->context(), recv, argc, argv);
    // ...
  }
 // ...
  return ret;
}

通过ret = callback->Call(env->context(), recv, argc, argv);实现了event-loop中主体代码的运行,之后在InternalMakeCallback结束之后,实现对nextTick和microtask的调用,代码如下:

// ./src/node.cc
void InternalCallbackScope::Close() {
  // ...
  Environment::TickInfo* tick_info = env_->tick_info();

  if (!tick_info->has_scheduled()) {
    env_->isolate()->RunMicrotasks();
  }
  // ...
  if (!tick_info->has_scheduled() && !tick_info->has_promise_rejections()) {
    return;
  }
  // ...
  Local<Object> process = env_->process_object();

  if (env_->tick_callback_function()->Call(process, 0, nullptr).IsEmpty()) {
    failed_ = true;
  }
}

其中,有两个需要注意的地方,一个是:

 if (!tick_info->has_scheduled()) {
    env_->isolate()->RunMicrotasks();
  }
 // ...
  if (!tick_info->has_scheduled() && !tick_info->has_promise_rejections()) {
    return;
  }

这两处代码专门针对无process.nextTick行为的event-loop进行了处理,直接从node中调用v8的RunMicrotasks,加快整体处理速度。
另外一个地方是:

if (env_->tick_callback_function()->Call(process, 0, nullptr).IsEmpty()) {
    failed_ = true;
  }

通过对tick_callback_function的调用,实现触发之前讲过的_tickCallback,不知道大家还记得这句话么:

在这里简单的追踪了一下_tickCallback来证实一下最终_tickCallback传递给了tick_callback_function

这样,整体形成了一个闭环,无论是bootstrap阶段还是在event-loop阶段,总是能保证两点:

  • nextTick永远在主函数(包括同步代码和console)运行完之后运行
  • nextTick永远优先于microtask运行

by 小菜 原文地址:https://github.com/xtx1130/blog/issues/16,欢迎star和watch,如果文中有讲解错误的地方欢迎指正

2017年JavaScript 现状调查报告总结


Node AMP 性能检测工具求推荐 ~

$
0
0

自己个人的小项目, 主要想监测下 response time, 资源消耗等情况, Datadog 和 OneAPM 老大哥都是收费的, 没必要上, 或者有开源的替代品么 ?

老哥们推荐一波 ~

:D

Gunzip.zlibOnError会把整个node进程崩掉

$
0
0

关键代码 var rawstream = new Readable({ highWaterMark : 128 * 1024, read(size) { } });

rawstream.pipe(zlib.createGunzip())

然后后续会不停的rawstream.push一些binary的数据

有时候客户端可能会传过来损坏的gzip数据 然后就会Gunzip.zlibOnError 把整个node进程崩掉 我试了rawstream.on(‘error’, function(err){

}); 并不能捕获到error 求教 有什么办法至少能不让node崩掉?

用 koa-static 搭建静态资服务器没法显示文件列表吗?

$
0
0

用下面的代码简单的搭建一个静态资源服务,static目录包含111111.png

访问localhost:3000显示的是index.html内容,没有static目录的列表像这样这种: 222222.png

但是访问具体的文件都可以访问到,就是没办法显示整个列表,这样正常么?

const Koa = require('koa')
const path = require('path')
const static = require('koa-static')
const app = new Koa() 
const staticPath = './static'
app.use(static(path.join(__dirname, staticPath)))
app.use(async (ctx) => {
    ctx.body = 'hello world'
})
app.listen(3000)

node 进行高并发接口透传,会出现延时增加问题@狼叔指导

$
0
0

具体问题见测试案例延时会随着压测并发量增大而增大

测试命令

ab -n 1000 -c 200 -r http://localhost:8080/proxy-api webbench -t 10 -c 500 http://localhost:8080/proxy-api

截取部分响应耗时:

get http://ip:9190/user/getUserInfo 2019 ms cost time: 2020 get http://ip:9190/user/getUserInfo 2062 ms cost time: 2062 get http://ip:9190/user/getUserInfo 2064 ms cost time: 2065 get http://ip:9190/user/getUserInfo 2063 ms cost time: 2063 get http://ip:9190/user/getUserInfo 2062 ms cost time: 2063 get http://ip:9190/user/getUserInfo 2063 ms cost time: 2063 get http://ip:9190/user/getUserInfo 2061 ms cost time: 2062 get http://ip:9190/user/getUserInfo 2063 ms cost time: 2064 get http://ip:9190/user/getUserInfo 2063 ms … … get http://ip:9190/user/getUserInfo 1362 ms cost time: 1362 get http://ip:9190/user/getUserInfo 1361 ms cost time: 1362 get http://ip:9190/user/getUserInfo 1362 ms cost time: 1362 get http://ip:9190/user/getUserInfo 1362 ms cost time: 1362 get http://ip:9190/user/getUserInfo 1362 ms cost time: 1362 get http://ip:9190/user/getUserInfo 1363 ms cost time: 1363 get http://ip:9190/user/getUserInfo 1362 ms cost time: 1362 … … get http://ip:9190/user/getUserInfo 1006 ms cost time: 1006 get http://ip:9190/user/getUserInfo 627 ms cost time: 628 get http://ip:9190/user/getUserInfo 629 ms cost time: 629 get http://ip:9190/user/getUserInfo 628 ms cost time: 629 get http://ip:9190/user/getUserInfo 1403 ms cost time: 1403 get http://ip:9190/user/getUserInfo 1402 ms

写一个更具特色的node日志工具包

$
0
0

一言不合就造轮子,适合自己的轮子才是好轮子。

在写koa的时候,自己造了个日志打印工具,现在分享出来给喜欢的朋友。

node-bugjs是 node.js 的一个调试工具。

  • 支持5中主题样式(白,红,绿,黄,蓝)
  • 显示当前输出日志代码行号,方便开发调试
  • 支持定义log回调处理,实现log记录功能
npm install -S node-bugjs
const bugjs = require('node-bugjs')('demo')
bug.log('wellcome')

更详细用法请看 README.md

untitled1.png

koa中使用 untitled2.png

Viewing all 14821 articles
Browse latest View live