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

深入理解React源码 - 首次渲染 V


【上海】招聘全站工程师 (月薪:15K-25K; 14 月薪水,对高级人才,上不封顶)

$
0
0

技能 & 要求

我们是一个扩张中的创业团队,可以提供很多难得机会和发展空间。寻找的你是一个擅长沟通,工作积极主动并善于自我驱动的技术高手。 你将会参与到这些方面的工作:

使用 Node.js, SQL, 和 HTML/CSS/Javascript 编写基于Web的系统。 与其他 Developer 和 界面设计师紧密合作,高质量地实现需求。 将参与到多个项目开发,在不需要持续监督的情况下,从需求概念到功能实现完成。

必要技能:

3 年或以上的 Web/Cloud 应用开发经验。 熟练运用 OOP, Unit Test和Design Pattern,对代码精益求精。 熟悉 Node, Express, HTML5, CSS3, Javascript,熟练运用于开发Scalable 和 Maintainable的应用架构。 熟悉 Responsive/Mobile Web 设计。 熟悉 jQuery, Vue.js, React 等 Javascript框架和库。 熟悉 MySQL,关系型数据库设计和优化,若同时熟悉 NoSQL 是加分项。 编写模块化的,高质量的代码,并能积极参与到 Peer code reviews. 有较强的 Troubleshooting 能力,能独立研究和解决困难问题。 英文熟练,读写英文没有障碍。

下面是加分项,但不是必须的:

计算机科学或相关领域取得学士学位。 可以展示过去参与的项目,在 Github 或 Bitbucket上活跃提交的优先考虑。 请联系:cnode2018@gmail.com. 谢谢

vue-router 动态添加路由的问题

$
0
0

目前添加进去的路由是在 一级的 path 中, router.addRouters( router );

routes: [ { path: ‘/’, redirect: ‘/login’ }, { path: ‘/readme’, name:‘logoreadme’, meta: {requireAuth: true}, component: resolve => require([’…/components/common/Home.vue’], resolve), beforeEnter: (to, from, next) => { if (store.getters.showUser.username) {// 判断是否登录 //登录后,绑定需要的token 参数 axios.defaults.headers.common[‘Authorization’] = store.getters.showUser.Authorization; next() } else {// 没登录则跳转到登录界面 next({‘path’: ‘/’}) } }, children:[ { path: ‘/’, component: resolve => require([’…/components/index/Readme.vue’], resolve) }] } ]

可是我想登录后,动态添加到,routers[1]的children中。这个怎么搞。。。。。。。 求大家指导下!!!

实时代码检索插件

$
0
0

分享一个自己做的实时代码检索插件: Asynchronous grep on the fly

直接分享一个实际使用的效果图吧,可以用来全工程检索,当然也支持只搜索已打开的文件,甚至只搜索当前文件:

searching project

文件上传问题

$
0
0

**1.**HTML form表单提交

<form action="/" method="POST" enctype="multipart/form-data">
  <div class="form-group">
    <label for="exampleInputFile">File input</label>
    <input type="file" id="image" name="imgfile">
    <p class="help-block">text here.</p>
  </div>
  <button type="submit" id="submit" class="btn btn-default">Submit</button>
</form>
后台没有接收到file

2.$.ajax()

前端代码:
let formdata = new FormData()
formdata.append('file', $('#image')[0].files[0])
$.ajax({
  url: '/',
  type: 'post',
  data: formdata,
  processData: false,
  success: function (result) {
    console.log(result)
  }
})
后台代码:
const formidable = require('formidable');
router.post('/', checkLogIn, function (req, res, next) {
	let form = new formidable.IncomingForm()
	form.parse(req, function(err, fields, files){
		console.log(files)
		res.send('///////////////')
	})
})
后台也没有接收到files

3. xhr

前端代码:
	let formdata = new FormData()
	formdata.append('file', $('#image')[0].files[0])
	let xhr = new XMLHttpRequest()
	xhr.open('post', '/poolimg', true)
	xhr.send(formdata)
	xhr.onload = function() {
		console.log(xhr.response)
	}
后台代码:
const formidable = require('formidable');
router.post('/', checkLogIn, function (req, res, next) {
	let form = new formidable.IncomingForm()
	form.parse(req, function(err, fields, files){
		console.log(files)
		res.send('///////////////')
	})
})

后台能接收到files

为什么会这样呢?

sequelize自关联查询问题

$
0
0

QQ图片20180124140958.png

请问sequelize怎么实现自关联,才能查询出当前数据父级节点的数据,用hasMany是可以子节点的数据,但是没尝试出查父节点的数据,求大神们帮忙看看

[深圳/富途证券/15-30k] 腾讯三轮领投的独角兽互联网券商富途证券招 Web 前后端开发,求大牛加盟!

$
0
0

腾讯三轮领投的独角兽互联网券商富途证券招 Web 前后端开发,求大牛加盟!

公司去年已顺利完成C轮融资,正处在快速成长时期,是罕见的牌照齐全,自研系统完备,互联网基因强大的港美股券商!

岗位诱惑:

  1. 腾讯背书的金融平台,分享海内外投资趋势和公司快速成长的红利;
  2. 公司内部成套培养课程,大牛讲课,帮助提升个人技术能力;
  3. 公司内投资理财专家定期课程,提升个人投资认知,助力财富管理能力成长;
  4. 能接触金融和互联网两大黄金行业的各种玩法,鼓励创新,有各种小组研究AI,区块链,财务分析,量化交易等技术,只要有兴趣都能来参与;
  5. 好氛围(扁平沟通,坦诚相处)、好健康(自带健身房冲凉房)、好快乐(各大协会,车友酒友应有尽有)

招聘岗位

web 后端 RD

  • 负责公司网站平台的证券业务系统、互联网社区特性和运营平台的设计和开发;
  • 对现有的 Web 业务系统的维护、升级改造和性能优化;
  • 与团队其他角色紧密配合工作,共同创造稳定易用的产品,打造“最受华人信赖的海内外金融服务平台”;

岗位要求

  • 2 年以上的 Web 后端开发经验,语言不限(PHP/Python/Nodejs)
  • 熟练使用 MySQL,良好的数据库设计和丰富的优化经验
  • 熟悉 NoSQL 技术(redis,memcached 等)
  • 有 Web 安全意识,熟悉常见的 Web 安全问题以及防御措施
  • 熟悉 linux web 开发平台及工具,熟悉 nginx 服务器的配置优化工作
  • 了解 web 前端开发技术和常见的前端优化技巧
  • 有较强的学习能力和逻辑分析能力,有良好沟通能力以及团队协作精神,能够承担工作压力
  • 加分项有微服务开发经验;对后端性能优化有深入理解;愿意研究新技术;

web 前端

  • 完成各种网站项目需求
  • 开发 mobile web 以及 webapp
  • 参与前端技术选型、架构
  • 参与开发接入层( Node )

职位要求

  • 前端基础扎实,深入理解浏览器和 DOM 原理
  • 能独立完成各种变态前端需求
  • JavaScript 基础扎实,功底深厚
  • 具备较好的 CSS 能力
  • 加分项有 Node.js 经验
  • 加分项有后台开发经验
  • 加分项理解产品、交互或视觉设计方法和流程

简历都砸过来吧,mark@futunn.com

关于富途

腾讯战略投资 互联网券商 NO.1

富途美国牌照正式获批

历时26个月,从零开始,从此在美国,在硅谷,我们也是有组织的人了。

富途美国办公室,位于帕罗奥图大学路720号,路的尽头就是斯坦福,步行十几分钟可达。

可能是深圳最值得去的公司之一

2017 年 6 月份完成 C 轮 1.44 亿美刀,新晋独角兽,潜力巨大;错过了滴滴、蚂蚁,还要错过富途吗?!

福利待遇对齐腾讯,弹性工作,定期出游还是提一提,主要为了晒照片;

技术氛围浓厚,定期分享,大神云集;

这是我们的前端负责人暖男八哥( TooBug/易郑超),原就职于腾讯,现在是我们富途证券 web 前端装逼之路上的带头大哥。不仅技术牛,可谓人人膜(gui)拜(tian),而且在音乐造诣和摄影方面都是一把刀,八哥在很多前端大会上都有精彩分享。

定期分享会

图书角

美女如云,遇到这样的产品经理提需求还真是不好拒绝呢~

工作环境

怎么样,有没有很想过来一起玩耍?

坐标深圳南山-科兴科学园 C3 栋 9 层

ps: 作为富途体育学院-健身协会会长,如果你对健身感兴趣,可以带你飞; 程序员专心工作很累, 又想健身, 你有什么科学的好方法 ?

执行的结果顺序是什么?为什么?

$
0
0
function execOrder () {
  setTimeout(() => console.log('timeout'), 0)
  let promise = new Promise((resolve, reject) => {
	console.log('Promise')
	resolve()
  })
  promise
  	.then(() => {
  		console.log('resolved')
  	})
  console.log('hi')
}
// 结果顺序是?

如何封装和复用AngularJS的代码

$
0
0

(场景是:有很多个controller,每个controller内部的代码除了controller的名称以外全部都一样,只需要把名称换一下。但是我不想复制粘贴几十个这样的controller)

Angular代码如何封装复用1.pngAngular代码如何封装复用2.png

fs.cheateWriteStream()同时保存多个视频文件怎么处理,求助?

$
0
0

例如: untitled1.png每一个url都是一个视频的地址,我想把对应的视频保存下来,这样写不对,求助

async/await 如何优美的处理异常?

$
0
0

本着一种学习的态度写了此话题,如有不妥之处还请各位大牛多多指点.

Purpose: 此解决方案,可以避免业务代码中出现大量的try/catch, 众所周知try/catch对性能方面有一定的影响, 另一方面try/catch在Node.js >= v8.3.0以后对性能的损耗是可以忽略不计的.

try/catch具体文献: However for Node 8.3+ the performance hit of calling a function inside a try block is negligible.

1. try/catch方法处理异常

async updateArticleById(ctx, next){
		 let id = ctx.params["id"];
       let body = ctx.request.body;
       try {
           let ret = await Services.admin.updateArticle(id, body);
           ctx.body = {ret: 0, data: ret};
       } catch(e) {
           ctx.body = {ret: 1, data: null, err: e.message || e.stack};
       }
}
  • 以上捕获异常是使用try/catch的方式来处理,因为await后面跟着的是Promise对象,当有异常的情况下会被Promise对象的内部
  • catch捕获,而await就是一个then的语法糖,并不会捕获异常, 那就需要使用try/catch来捕获异常,并进行相应的逻辑处理。

2. 封装异常处理函数

  • 知道了上面异常会被Promise对象自身的catch捕获异常,可以使用下面的解决方案 to.js
module.exports = (promise) => {
   if(!promise || !Promise.prototype.isPrototypeOf(promise)){
       return new Promise((resolve, reject)=>{
           reject(new Error("requires promises as the param"));
       }).catch((err)=>{
           return [err, null];
       });
   }
   return promise.then(function(){
       return [null, ...arguments];
   }).catch(err => {
       return [err, null];
   });
};

格式 [error, …result]

  • 采用类似Golang风格的错误返回方式, 这里指定第一个参数为错误参数,后面为正常返回结果
  • if块是用来处理非法参数,并返回错误[err, null]
  • await后面如果是一个promise对象,那么await的任务就是在等待promise.resolve,而to.js就是主动去调用then和catch,主动处理并重新封装结果,并且在then或是catch里面继续返回封装后的数据,返回值对于await来说仍然是一个promise对象,然而resolve的值却是一个可迭代的对象[error, …result]

这个可迭代的对象如何使用 ?

async updateArticleById(ctx, next){
       let body = ctx.request.body;
       let id = ctx.request.params["id"];
       let [err, ret] = await ctx.app.utils.to(Services.admin.updateArticleById(id, body));
       if(err) {
           return ctx.body = {ret: 1, data: null, err: err};
       }

       ctx.body = {ret: 0, data: ret};
   }

node源码粗读(6):从./lib/timers.js来看timers相关API底层实现

$
0
0

这篇文章主要从node源码的./lib/timers.js 入手,历经./lib/internal/timers.js、./src/timer_wrap.cc,并最终下沉到./deps/uv/src/unix/timer.c来叙述整体timers的实现流程。

前言

在阅读 ./lib/timers.js代码的时候,首先映入眼帘的便是如下这几行注释:

// ╔════ > Object Map
// ║
// ╠══
// ║ refedLists: { '40': { }, '320': { etc } } (keys of millisecond duration)
// ╚══          ┌─────────┘
//              │
// ╔══          │
// ║ TimersList { _idleNext: { }, _idlePrev: (self), _timer: (TimerWrap) }
// ║         ┌────────────────┘
// ║    ╔══  │                              ^
// ║    ║    { _idleNext: { },  _idlePrev: { }, _onTimeout: (callback) }
// ║    ║      ┌───────────┘
// ║    ║      │                                  ^
// ║    ║      { _idleNext: { etc },  _idlePrev: { }, _onTimeout: (callback) }
// ╠══  ╠══
// ║    ║
// ║    ╚════ >  Actual JavaScript timeouts
// ║
// ╚════ > Linked List

我们按照注释的引导,一点一点把整体的timers.js结构扒开,看它究竟做了什么。

timers中的双向链表

按照注释中的引子,我们先来看一下TimersList究竟是什么样子的,目光直接移向下面这段代码:

function TimersList(msecs, unrefed) {
  this._idleNext = this; // Create the list with the linkedlist properties to
  this._idlePrev = this; // prevent any unnecessary hidden class changes.
  this._unrefed = unrefed;
  this.msecs = msecs;
  this.nextTick = false;
  const timer = this._timer = new TimerWrap();
  timer._list = this;
  if (unrefed === true)
    timer.unref();
  timer.start(msecs);
  timer[kOnTimeout] = listOnTimeout;
}

这里是TimersList的实现代码,不管其他的,看到this.xxxNextthis.xxxPrev,第一时间就会想到双向链表。没错,TimersList就是一个双向链表,那么它的两端分别连接的是什么呢?

function insert(item, unrefed) {
  const msecs = item._idleTimeout;
  if (msecs < 0 || msecs === undefined) return;
  item._idleStart = TimerWrap.now();
  const lists = unrefed === true ? unrefedLists : refedLists;
  var list = lists[msecs];
  if (list === undefined) {
    debug('no %d list was found in insert, creating a new one', msecs);
    lists[msecs] = list = new TimersList(msecs, unrefed);
  }
  //other codes...
  L.append(list, item);
  assert(!L.isEmpty(list)); 
}

大家可以看一下这个insert函数,其中实现了TimersList的实例化,其中:

const lists = unrefed === true ? unrefedLists : refedLists;
 var list = lists[msecs];
  if (list === undefined) {
    debug('no %d list was found in insert, creating a new one', msecs);
    lists[msecs] = list = new TimersList(msecs, unrefed);
  }

这几句话基本涵盖了刚才注释里面的refedLists以及TimersList,这里可以看出referedLists是一个对象,而对象的key是变量msecs, value则对应一个TimersList。
之后的L.append(list, item);,L是来自于./lib/internal/linkedlist.js中操作双向链表的方法,append则是在链表后面插入一个元素,而list是实例化的TimersList,所以也就是在TimersList后插入新的item,到此为止,整体结构如下所示:

refedLists = {
[msecs0]: item<->item<->item<->item<->item,//<->代指双向链表TimersList
[msecs1]: item<->item<->item<->item<->item,
[msecs2]: item<->item<->item<->item<->item
......
}

msecs是什么

在理解了注释的大体结构之后,那么下一个疑问就来了,msecs到底指的是什么?

通过翻阅./lib/timers.js的代码可以找到几个和msecs强相关的地方:

function insert(item, unrefed) {
  const msecs = item._idleTimeout;
  if (msecs < 0 || msecs === undefined) return;
  //...
  if (list === undefined) {
    debug('no %d list was found in insert, creating a new one', msecs);
    lists[msecs] = list = new TimersList(msecs, unrefed);
  }
  //...
}
function TimersList(msecs, unrefed) {
  //...
  this._unrefed = unrefed;
  this.msecs = msecs;
  this.nextTick = false;
  //...
}

从这些代码可以看出来msecs的起源实际是item._idleTimeout,而item根据如下几个地方溯源:

const timerInternals = require('internal/timers');
const Timeout = timerInternals.Timeout;
const timeout = new Timeout(callback, after, args, false);//实例化之后传入到item中了
const active = exports.active = function(item) {
  insert(item, false);
};
active(timeout);

直接跟进到./lib/internal/timers.js, 其中有这样一段代码:

function Timeout(callback, after, args, isRepeat) {
  //...
  this._onTimeout = callback;
  this._idleTimeout = after;
  //...
}

所以,msesc其实是Timeout的第二个参数–after,即setTimeout API中的第二个参数,延迟几秒执行,_onTimeout就是最终要执行的callback
读完这个章节,我们的整体结构可以修改成:

// <->代指双向链表TimersList,
// 10/100/1000分别为延迟10/100/1000毫秒后执行
refedLists = {
10: item<->item<->item<->item<->item,
100: item<->item<->item<->item<->item,
1000: item<->item<->item<->item<->item
......
}

TimersList的TimerWrap

根据上面的描述,基本上整体时间链已经比较清晰了:setTimout(callback,time)会根据time来向refedLists[time]中append一个新的item。那么这个TimersList的机制是什么样的呢?

这里我们要从一个细节入手,在定义TimersList的地方,有一行代码:

const {
  Timer: TimerWrap,
  setImmediateCallback,
} = process.binding('timer_wrap');
function TimersList(msecs, unrefed) {
  //...
  const timer = this._timer = new TimerWrap();
  timer._list = this;
  //...
  timer.start(msecs);
  timer[kOnTimeout] = listOnTimeout;
  //...
}

从这里我们可以看到TimersList有一个属性_timer,而这个_timer的来源又很隐蔽,是通过binding方法挂载进来的内置api。在这种情况下,矛头直接指向./src/timer_wrap.cc中一探究竟。

class TimerWrap : public HandleWrap {
 public:
   static void Initialize(Local<Object> target,
                         Local<Value> unused,
                         Local<Context> context) {
   // ...
   env->SetTemplateMethod(constructor, "now", Now);
   AsyncWrap::AddWrapMethods(env, constructor);
   env->SetProtoMethod(constructor, "close", HandleWrap::Close);
   env->SetProtoMethod(constructor, "ref", HandleWrap::Ref);
   env->SetProtoMethod(constructor, "unref", HandleWrap::Unref);
   env->SetProtoMethod(constructor, "hasRef", HandleWrap::HasRef);
   env->SetProtoMethod(constructor, "start", Start);
   env->SetProtoMethod(constructor, "stop", Stop);
   //...
   }
   NODE_BUILTIN_MODULE_CONTEXT_AWARE(timer_wrap, node::TimerWrap::Initialize)

可以看到,在node中构造了timer_wrap的构造函数,其中包含了若干方法和属性,在这里我们先重点看一下start方法:

  TimerWrap(Environment* env, Local<Object> object)
      : HandleWrap(env,
                   object,
                   reinterpret_cast<uv_handle_t*>(&handle_),
                   AsyncWrap::PROVIDER_TIMERWRAP) {
    int r = uv_timer_init(env->event_loop(), &handle_);
    CHECK_EQ(r, 0);
  }
  static void Start(const FunctionCallbackInfo<Value>& args) {
    TimerWrap* wrap = Unwrap<TimerWrap>(args.Holder());

    CHECK(HandleWrap::IsAlive(wrap));

    int64_t timeout = args[0]->IntegerValue();
    int err = uv_timer_start(&wrap->handle_, OnTimeout, timeout, 0);
    args.GetReturnValue().Set(err);
  }

首先,TimerWrap会执行uv_timer_init(env->event_loop(), &handle_)。uv_timer_init为libuv中定时器的初始化函数,它的参数有两个:第一个参数是定时器初始化时候的主event_loop,第二个参数为指向uv_timer_t的指针。一般以handle命名(因为uv_timer_t为uv_handle_t的子类,uv_handle_t的结构体声明中又包含了uv_loop_t),handle相当于uv_timer_start之后的唯一标识,如下我所了解到的一些用法:

  • handle->flags 标识此timer是否已经结束
  • handle->type 标识此timer的类型,可选类型有:UV_TCP、UV_NAMED_PIPE、UV_TTY
  • handle->timer_cb libuv需要执行的回调函数
  • handle->timeout 定时执行的绝对时间
  • handle->repeat 是否重复执行
  • handle->start_id 定时器启动的id,初始化在loop->timer_counter(uv_loop_t * loop)中,初始为0,逐渐递增

我对libuv也不是十分了解,如上是我大致了解的handle的用途,还希望大家给予补充。 uv_timer_start四个参数的意义可以在 ./deps/uv/src/unix/timer.c中找到,分别为:handle、回调、延迟执行的时间、是否重复执行。
问题马上迎刃而解了,下面我们只需要关注一下OnTimeout

  static void OnTimeout(uv_timer_t* handle) {
    TimerWrap* wrap = static_cast<TimerWrap*>(handle->data);
    Environment* env = wrap->env();
    HandleScope handle_scope(env->isolate());
    Context::Scope context_scope(env->context());
    wrap->MakeCallback(kOnTimeout, 0, nullptr);
  }

在这里有一个比较重要的过程,就是handle->data,这里的handle指向的是uv_timer_t,而这个data则是通过node中的HandleWrap传递给libuv的handle_warp.cc

class TimerWrap : public HandleWrap{}
HandleWrap::HandleWrap(Environment* env,
                       Local<Object> object,
                       uv_handle_t* handle,
                       AsyncWrap::ProviderType provider)
    : AsyncWrap(env, object, provider),
      state_(kInitialized),
      handle_(handle) {
  handle_->data = this;// 在这里把上下文传递给libuv
  HandleScope scope(env->isolate());
  Wrap(object, this);
  env->handle_wrap_queue()->PushBack(this);
}

所以接下来就可以通过wrap->MakeCallback调用MakeCallback实现对js函数的调用。
通过这一章节的叙述,可以基本缕清refedLists的整体结构:

// <->代指双向链表TimersList,
// 10/100/1000分别为延迟10/100/1000毫秒后执行
// TimerWrap中为10毫秒后待执行的链表
refedLists = {
10: TimerWrap._list (TimersList(item<->item<->item<->item<->item)),
100: TimerWrap._list (TimersList(item<->item<->item<->item<->item)),
1000: TimerWrap._list (TimersList(item<->item<->item<->item<->item))
......
}

执行阶段

沿用刚才的逻辑,其实暴露出来的timer.start方法就会调用uv_timer_start来启动定时循环处理,由此可见,在TimersList中:

function TimersList(msecs, unrefed) {
  // ...
  timer.start(msecs);
  timer[kOnTimeout] = listOnTimeout;
}

随着每一个TimersList的注册,会启动uv_timer_start,来开始整体的一个事件循环,拿上面的总结举例子来说:

refedLists = {
10: TimerWrap._list (TimersList(item<->item<->item<->item<->item))
......
}

当refedLists中注册了10毫秒后需要执行的TimersList的时候,旋即启动uv_timer_start,每隔十秒执行timer[kOnTimeout] = listOnTimeout即listOnTimeout,下面是整个完整的回调函数调用链:

function listOnTimeout() {
  // ...
  while (timer = L.peek(list)) {
  // ...
  }
  // ...
  tryOnTimeout(timer, list);
}

function tryOnTimeout(timer, list) {
  // ...
  try {
    ontimeout(timer);
    threw = false;
  } finally {
  // ...
  }
  // ...
}

function ontimeout(timer) {
  // ...
  if (!args)
    timer._onTimeout();
  else
    Reflect.apply(timer._onTimeout, timer, args);
  if (timer._repeat)
    rearm(timer);
}

刚才经过分析,大家应该还对_onTimeout有印象吧?没错,这就是最后要执行的callback,也就是listOnTimeout最终执行了setTimeout方法的callback。至此,整个timers的整体流程就分析结束了。

by 小菜
如果有什么疑问或者我的表述有不正确的地方欢迎回复和指正。
原文地址:https://github.com/xtx1130/blog/issues/15欢迎watch和star。

ajax2中只要添加了事件监听,就会报一个跨域的错误,直接上代码

$
0
0

cors.jpg下面图片是后端代码 cors1.jpg不知道为什么只要添加了事件监听,就会报一个跨域的错 cors2.jpg

弱弱地问一下,cluster 和 child_process 的使用场景

$
0
0

我对服务端开发的认知还处在 curd 的阶段,所以这些进程之类的并没有使用过。最近又开始进入 Node 的世界,所以产生了一些疑问:

  1. 大家说说在什么项目里,或者自己在什么场景下用到过进程,cluster 和 child process 的使用场景有那些,并且说说为什么
  2. 以及在大家熟悉的其他的后端语言中,是否都存在调用进程的场景

本人 3 年前端经验,就像上面说的,对服务端开发的认知还处在 curd 的阶段,所以希望大家不吝赐教。谢谢

cluster 和 child process 的概念也都看了,就是压榨机器,提升性能。不过这些是不是加机器也能做到,如果是一个集群的情况下呢?

ShadowNode: 以更轻量级的方式使用 Node.js

$
0
0

好久不见,Yorkie 最近做了一个小东西,在此分享给大家!

先放个链接:https://github.com/Rokid/shadow-node从名字来看,他可不是一个 Node.js 的 C++ Addon,而是可以运行 Node.js 程序的另一个运行时(Runtime),相比 Node.js,具有更快的启动速度以及更小的内存占用、更加节省内存的模块加载方式!原来一个 Node.js 应用需要好几十M的运行时内存,现在可能只需要几兆就可以搞定了,并且拥有更快的启动速度。这也是因为 ShadowNode 不再使用 v8 作为 JavaScript 引擎,而是使用一款三星出品的 JerryScript 在做底层的解释执行!

ShadowNode 目前支持的模块还比较有限,不过一些基础的如 HTTP/NET/DNS/Child Process 等都是支持的,并且除此之外,还默认支持 DBus 通讯方式,对于像 Linux 平台,简直是不要太方便,另外,对于硬件玩家,如果有兴趣移植到自己的开发板上的,也支持了诸如 I2C/ADC/GPIO 等这样的硬件接口,也就是说接入硬件仅仅写写脚本就能把你的硬件玩起来!另外,也可以把他移植到你家里的任意设备上,作为脚本语言使用,也是不错的选择!

在此,我对 ShadowNode 寄予着非常大的希望,目前 Node.js 在嵌入式设备来说,无可避免都是内存占用的大户,之前 Tessel 曾经尝试把 JavaScript代码编译成 Lua 来达到降低内存的目的,也有一些不同的开发板使用像 JerryScript 这样低内存的引擎/虚拟机,然后仅仅支持了部分简单的模块以及 CommonJS,而我完全希望 ShadowNode 将更完全地支持 Node.js/Npm 模块,能够让 JavaScript 开发者能无缝在 Node.js 与 ShadowNode 中切换,尽量少地减少学习的成本!另外,ShadowNode 底层使用纯 C 编写,我们将大部分代码交给 C 来实现,JavaScript 仅仅作为胶水层语言!

最后,搭车招个人!坐标杭州西溪,Rokid 公司,招募嵌入式、服务端以及社区增长黑客,共同点都是要热爱 JavaScript 以及 Node.js,简历的话直接去我 GitHub 邮件我就好了~


有没有一个仅包含ECMA标准的js shell?

$
0
0

想试着做一个简单的js版oj,需要一个只包括js基础功能的运行环境,没有Node或浏览器里的额外功能的。 像mongodb的shell也是在一个干净的js shell上加的mongodb相关功能,我想知道哪里能找到这样一个干净的js shell

在window上测试安装cnpmjs.org私有仓库

$
0
0

在window上测试安装cnpm私有仓库

官方说明只能在linux和mac安装

1. clone到本地,安装dependencies

  # from git
  $ git clone https://github.com/cnpm/cnpmjs.org.git
  # 因为是在window上安装,所有官方文档上的一些命令就不能使用了,
  $ cd cnpmjs.org
  $ npm install
  $ npm run dev

上面的最后一条命令执行肯定是会报错的,下面我们就按着报错提示,一个一个的解决
最终搭建自己完美的私有npm仓库

2. npm run dev报错

  D:\www\cnpmjs.org [master ≡ +0 ~1 -0 !]
  λ npm run dev

  > cnpmjs.org@3.0.0-alpha.16 dev D:\www\cnpmjs.org
  > DEBUG=cnpm* node dispatch.js

  'DEBUG' 不是内部或外部命令,也不是可运行的程序
  或批处理文件。

  npm ERR! Windows_NT 10.0.14393
  npm ERR! argv "C:\\Program Files\\nodejs\\node.exe" "C:\\Program Files\\nodejs\\node_modules\\npm\\bin\\npm-cli.js" "run" "dev" npm ERR! node v6.10.0
  npm ERR! npm  v3.10.10
  npm ERR! code ELIFECYCLE
  npm ERR! cnpmjs.org@3.0.0-alpha.16 dev: `DEBUG=cnpm* node dispatch.js`
  npm ERR! Exit status 1

此命令错误主要是window不能执行DEBUG=cnpm*命令,所以我们要改这条命令 更新package.json中命令如下,主要看dev命令

{
  "scripts": {
    "dev": "set DEBUG=cnpm* & node dispatch.js",
    "test": "make jshint && make test",
    "test-local": "make test",
    "start": "./bin/nodejsctl start && cp History.md docs/web/history.md",
    "status": "./bin/nodejsctl status",
    "stop": "./bin/nodejsctl stop"
  }
}

再次执行npm run dev命令,已经不错了

  D:\www\cnpmjs.org [master ≡ +0 ~2 -0 !]
  λ npm run dev

  > cnpmjs.org@3.0.0-alpha.16 dev D:\www\cnpmjs.org
  > set DEBUG=cnpm* & node dispatch.js

  Starting cnpmjs.org ...
  cluster: false
  admins: {"fengmk2":"fengmk2@gmail.com","admin":"admin@cnpmjs.org","dead_horse":"dead_horse@qq.com"}
  scopes: ["@cnpm","@cnpmtest","@cnpm-test"]
  sourceNpmRegistry: https://registry.npm.taobao.org
  syncModel: none
  [Wed Jan 24 2018 14:52:05 GMT+0800 (中国标准时间)] [worker:34728] Server started, registry server listen at 127.0.0.1:7001, web
  listen at 127.0.0.1:7002, cluster: false

3. 我们根据提示访问127.0.0.1:7002,不用说又要报错了,但是页面应该是能访问的了

  cnpmjs.org:middleware:auth GET / with "" +0ms
  cnpmjs.org:middleware:auth GET /total with "" +416ms
{ SequelizeDatabaseError: SQLITE_ERROR: no such table: module
    at Query.formatError (D:\www\cnpmjs.org\node_modules\sequelize\lib\dialects\sqlite\query.js:348:14)
    at afterExecute (D:\www\cnpmjs.org\node_modules\sequelize\lib\dialects\sqlite\query.js:112:29)
    at replacement (D:\www\cnpmjs.org\node_modules\sqlite3\lib\trace.js:19:31)
    at Statement.errBack (D:\www\cnpmjs.org\node_modules\sqlite3\lib\sqlite3.js:16:21)
  name: 'SequelizeDatabaseError',
  message: 'SQLITE_ERROR: no such table: module',
  parent:
   { Error: SQLITE_ERROR: no such table: module
       at Error (native)
     errno: 1,
     code: 'SQLITE_ERROR',
     sql: 'SELECT count(name) AS count FROM module;' },
  original:
   { Error: SQLITE_ERROR: no such table: module
       at Error (native)
     errno: 1,
     code: 'SQLITE_ERROR',
     sql: 'SELECT count(name) AS count FROM module;' },
  sql: 'SELECT count(name) AS count FROM module;',
  url: '/total' }
SequelizeDatabaseError: SQLITE_ERROR: no such table: module
    at Query.formatError (D:\www\cnpmjs.org\node_modules\sequelize\lib\dialects\sqlite\query.js:348:14)
    at afterExecute (D:\www\cnpmjs.org\node_modules\sequelize\lib\dialects\sqlite\query.js:112:29)
    at replacement (D:\www\cnpmjs.org\node_modules\sqlite3\lib\trace.js:19:31)
    at Statement.errBack (D:\www\cnpmjs.org\node_modules\sqlite3\lib\sqlite3.js:16:21)

这些错误的意思简单说就是没有database,没有table,什么都没有,怎么办
// - currently supported: 'mysql', 'sqlite', 'postgres', 'mariadb'
这是cnpmjs支持的数据库列表,默认使用sqlite,我准备使用mysql,所以安装mysql请百度

4. 因为我们是在window上跑这个项目,所以一些自动化命令我们就不能使用了

我们要手动执行sql语句,创建数据表,sql语句在./docs/db.sql
为方便,使用navicat客户端工具(自己安装)

4.1 连接mysql

mysql_connect.png

4.2 创建数据库cnpmjs(名字随便)

在mysql连接名字上比如local,右键->新建数据库

database.png

4.3 执行sql

在数据库cnpmjs名字上右键->命令行界面,将./docs/db.sql中的sql语句进行copy,
粘贴到命令,回车执行ok。
特别注意有个./docs/user.sql,必须单独执行一下

sql.png

4.4 修改配置文件config/index.js

主要配置db, username, password, dialect, host, port

/**
   * database config
   */

  database: {
    db: 'cnpmjs',
    username: 'root',
    password: 'root',

    // the sql dialect of the database
    // - currently supported: 'mysql', 'sqlite', 'postgres', 'mariadb'
    dialect: 'mysql',

    // custom host; default: 127.0.0.1
    host: '127.0.0.1',

    // custom port; default: 3306
    port: 3306,

    // use pooling in order to reduce db connection overload and to increase speed
    // currently only for mysql and postgresql (since v1.5.0)
    pool: {
      maxConnections: 10,
      minConnections: 0,
      maxIdleTime: 30000
    },

    // the storage engine for 'sqlite'
    // default store into ~/.cnpmjs.org/data.sqlite
    storage: path.join(dataDir, 'data.sqlite'),

    logging: !!process.env.SQL_DEBUG,
  },

5. 重新启动项目npm run dev,现在应该一切正常了

index.png

7001验证

7001.png

6. 现在到了我们怎么使用私有库的时候了

6.1 我们使用nrm这个模块进行npm库的管理

  #
  $ npm install nrm -g

  $ nrm ls

  # npm ---- https://registry.npmjs.org/
  # cnpm --- http://r.cnpmjs.org/
  # * taobao - https://registry.npm.taobao.org/
  # nj ----- https://registry.nodejitsu.com/
  # rednpm - http://registry.mirror.cqupt.edu.cn/
  # npmMirror  https://skimdb.npmjs.com/registry/
  # edunpm - http://registry.enpmjs.org/

上面可以看出可以用的npm仓库地址,*标明当前使用的仓库地址,使用npm use 'name'切换仓库

6.2 添加本地私有仓库

cnpm提供两个端口:7001和7002,其中7001用于NPM的注册服务,7002用于Web访问

  $ nrm add local http://127.0.0.1:7001/

    add registry local success

  $ nrm ls

  # npm ---- https://registry.npmjs.org/
  # cnpm --- http://r.cnpmjs.org/
  # * taobao - https://registry.npm.taobao.org/
  # nj ----- https://registry.nodejitsu.com/
  # rednpm - http://registry.mirror.cqupt.edu.cn/
  # npmMirror  https://skimdb.npmjs.com/registry/
  # edunpm - http://registry.enpmjs.org/
  # local -- http://127.0.0.1:7001/

local就是我们刚才添加的本地私有npm仓库,执行

  nrm use local
  # 使用本地仓库

6.3 发布npm包

在发布 npm 包之前,需要先将原先的 config/config.js 中添加一些配置属性:

  enablePrivate: true, // 只有管理员可以发布 npm 包,默认为 false,即任何人都可以发布包
  admins: {
    admin: 'admin@admin.com' // 管理员权限
  },
  scopes: ['@company'], // 私有包必须依附于 scope 下

登录我们配置的管理员

  # 使用本地仓库
  nrm use local
   Registry has been set to: http://127.0.0.1:7001/
  # 执行登录
  npm login
  Username: admin     # 输入用户名
  Password:           # 密码admin
  Email: (this IS public) admin@admin.com     # 邮箱就是配置的邮箱
    Logged in as admin on http://127.0.0.1:7001/.

我们知道,要发布包到npm,需要有github仓库地址,现在我们就在github上建个demo作测试
实际使用应该使用公司内部的gitlab

我们直接使用已有的demo

  git clone git@github.com:twolun/xxx-utils.git
  cd xxx-utils
  npm publish
    + @evcard/evcard-utils@1.0.2
    # 应该是发布成功

1.下面我们在仓库中查看,已经有了一个包了

ready.png

2.这上面只有通过搜索才能查看到发布的包

search.png

3.进一步查看,注意我画红线的部分,是在config/index.js里配置的

detail.png

4.最后一点要注意,就是我们发布包的package.json文件里有包名的写法
尤其注意name的写法@company也是固定的,与config/index.js里的配置相对应

{
  "name": "@company/xxx-utils",
  "version": "1.0.3",
  "description": "私有包测试demo",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "admin@admin.com",
  "license": "ISC"
}

6.4 安装本地仓库刚刚发布的xxx-utils

  D:\github\fis3-parser-html-plugin1
  npm install @company/xxx-utils -S
  fis3-parser-html-plugin@1.0.0 D:\github\fis3-parser-html-plugin1
    `-- @company/xxx-utils@1.0.3

  npm WARN fis3-parser-html-plugin@1.0.0 No repository field.

到此安装成功,应该可以使用了

总结

  1. 全程window环境,相信在mac, linux环境肯定也没问题的
  2. sql语句手动执行,创建database和table
  3. 更改相关配置一切搞定
  4. 全程没有使用cnpm这个,使用更方便nrm来切换仓库池
  5. 更多详细配置请查看cnpmjs.org官方文档

详细文档地址完整demo地址
xxx-utils地址

大家有什么压力测试工具呢

express如何接收websocket请求

$
0
0

项目情况:

  1. 前端使用 actioncable 发送 socket到node端。
ActionCable.createConsumer(`ws://${location.hostname}:8000/cable`)
  1. node 使用 http-proxy-middleware 转发 socket请求。
app.use('/cable', proxy({ target: proxyTarget, changeOrigin: true, ws: true, }))
  1. rails 后端接收 node发送的请求。

但是前端发送的socket报错

WebSocket connection to 'ws://127.0.0.1:8000/cable' failed: Connection closed before receiving a handshake response

node没有接收到任何请求,日志没有记录。

想问一下,怎么弄转发。

请教一个关于日志的问题......

$
0
0

目前的情况:

项目中的日志输出,都是出了问题,才想起来加一两句输出日志的代码,然而这都是出了问题才这么干,开发阶段基本不考虑这个问题。开发阶段为了调试方便,会有很多的console.log(***)…然而这对于项目上线后的维护,尤其是处理业务方面的问题,个人感觉基本没用。
请教大神:在项目维护开发中,对于日志输出是怎么处理的?一般在那些地方会有输出?关于日志有什么 最佳实践的? 感谢大神。
Viewing all 14821 articles
Browse latest View live