求一些2018年的前端面试题及侧重的知识点; 近几年,前端知识迭代很快,面试题也需要推陈出新一下,担心知识点考察不到位
求一些2018年的前端面试题及侧重的知识点
Roadmap to becoming a web developer in 2018堪称经典
这个图画的真是可圈可点,非常认可。还有后端和devops的,没这个好,地址 https://github.com/kamranahmedse/developer-roadmap
superagent 怎么请求到 301 跳转前的网页代码?
比如说 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首页,不一样的风格哦
为自己这套http://vadmin.lanyueos.com模板写个vuex的例子,然后就拿cnode接口写了cnode首页。
vue-element-admin-tpl这个模板我是我工作和平时开发的积累,可以快速搭建后台管理平台系统的模板。 我会不定期更新这个模板,欢迎star。
之前学习整理的pug文档. 社区虽然也有,但是都是很久之前的.因此分享一下,希望对新人有用.
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="
{
"very-long": "piece of ",
"data": 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="<code>"></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 <code>
</p>
<p>this is code template<code></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 跳转
最近在研究大麦的购票流程,中间有个 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 抓包,结果如下: 截图框内部分正是最后重定向到的链接。 但是我用 Node 来模拟请求,想得到请求的页面,始终拿不到,我也尝试获取 302 跳转前的页面:
superagent
.get(url)
.set('Cookie', cookie)
.redirects(0)
.on('error', err => {
console.log(err.response.text)
})
.end(function (err, sres) { // callback
})
我省略了一些请求头,实际上我都带上了,结果如下: 而且我也获取不到最后重定向页面的代码,一直获取的是商品详情页,也就是这个页面 https://piao.damai.cn/141343.html的代码。 我有两个问题:
- 我怎么根据 <www.damai.cn/GotoShopping.aspx?_action=Immediately&proId=12123708&optype=1&companyId=1580&num=1&n=0> 获取最后跳转到的页面的 URL(假设有 cookie 等所有请求头信息)
- 怎么根据 <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了
年纪大了快要撤回老家了, 西安基本没有node的机会, 已经学了一段时间java了, 你们有什么想说的
前端调用 GraphQL API,从未如此方便!
GraphQL 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。 GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余。
想更多的了解或使用 GraphQL,请访问 https://github.com/facebook/graphql
GraphQL 有针对不同语言的服务端实现,以帮助开发人员搭建 GraphQL Server
。
而 gq-loader
是一个 webpack
插件,你可以认为它一针对前端项目的一种 client
端实现,它的目的是帮助前端开发同学更简便的调用 GraphQL API,它让前端开发人员在使用 GraphQL 时更加方便,像普通 js
模块一样轻松自如,使前端开发人员能在 js
文件中通过 import
或 require
导入 .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-loader
的 extensions
无论配置何值,在 js
中 import
时都不能省略扩展名,此选项仅作用于 .gql
文件 import
其它 .gql
文件
看到一个typescript方法,不能理解,看了编译结果,更不能理解了
原来的是定义了这样一个方法:
let reactionScheduler: (fn: () => void) => void = f => f()
看了好久没看懂这个方法干哈用的,于是就拿去跑一下编译,结果出来的结果更是让我不能理解。 结果如下:
var reactionScheduler = function (f) { return f(); };
有没有大神给剖析一把上面的方法。 再一个问题是,定义这个方法的意义在哪里?
time.unref 没有按预期取消对应的函数回调
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 招募工程师
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,怎么样比较顺利的拿到面试机会
现在找人都比较看经验呢,之前一直是干的产品,越做越不顺心,思考一番决定转行。 也用Node写过小玩意,但是吧肯定上不了台面。也不能说自己闷头学个一年半年再来找。 想问问有没有前辈也是转行的成功,讨教些经验,小弟在此谢过啦~
node源码粗读(7):nextTick和microtasks从bootstrap到event-loop全阶段解读
这篇文章主要介绍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))
tips: 蓝色底色代码为断点所在位置,下方为此时刻的内存地址,上面这张图可以看出来在没有跑LoadEnvironment
的时候,tick_callback_function
为NULL
如果对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之前运行
结果如图: 相关解释已经写到了上面的注释中。 (当然这里用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 现状调查报告总结
翻译一篇国外哥们做的JavaScript 现状调查
Node AMP 性能检测工具求推荐 ~
自己个人的小项目, 主要想监测下 response time, 资源消耗等情况, Datadog 和 OneAPM 老大哥都是收费的, 没必要上, 或者有开源的替代品么 ?
老哥们推荐一波 ~
:D
Gunzip.zlibOnError会把整个node进程崩掉
关键代码 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 搭建静态资服务器没法显示文件列表吗?
用下面的代码简单的搭建一个静态资源服务,static目录包含,
访问localhost:3000显示的是index.html内容,没有static目录的列表像这样这种: ,
但是访问具体的文件都可以访问到,就是没办法显示整个列表,这样正常么?
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 进行高并发接口透传,会出现延时增加问题@狼叔指导
具体问题见测试案例延时会随着压测并发量增大而增大
测试命令
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日志工具包
一言不合就造轮子,适合自己的轮子才是好轮子。
在写koa的时候,自己造了个日志打印工具,现在分享出来给喜欢的朋友。
node-bugjs是 node.js 的一个调试工具。
- 支持5中主题样式(白,红,绿,黄,蓝)
- 显示当前输出日志代码行号,方便开发调试
- 支持定义log回调处理,实现log记录功能
npm install -S node-bugjs
const bugjs = require('node-bugjs')('demo')
bug.log('wellcome')
更详细用法请看 README.md
koa中使用