深入理解React源码 - 首次渲染 V
【上海】招聘全站工程师 (月薪:15K-25K; 14 月薪水,对高级人才,上不封顶)
技能 & 要求
我们是一个扩张中的创业团队,可以提供很多难得机会和发展空间。寻找的你是一个擅长沟通,工作积极主动并善于自我驱动的技术高手。 你将会参与到这些方面的工作:
使用 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 动态添加路由的问题
目前添加进去的路由是在 一级的 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中。这个怎么搞。。。。。。。 求大家指导下!!!
实时代码检索插件
分享一个自己做的实时代码检索插件: Asynchronous grep on the fly
直接分享一个实际使用的效果图吧,可以用来全工程检索,当然也支持只搜索已打开的文件,甚至只搜索当前文件:
文件上传问题
**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自关联查询问题
请问sequelize怎么实现自关联,才能查询出当前数据父级节点的数据,用hasMany是可以子节点的数据,但是没尝试出查父节点的数据,求大神们帮忙看看
[深圳/富途证券/15-30k] 腾讯三轮领投的独角兽互联网券商富途证券招 Web 前后端开发,求大牛加盟!
腾讯三轮领投的独角兽互联网券商富途证券招 Web 前后端开发,求大牛加盟!
公司去年已顺利完成C轮融资,正处在快速成长时期,是罕见的牌照齐全,自研系统完备,互联网基因强大的港美股券商!
岗位诱惑:
- 腾讯背书的金融平台,分享海内外投资趋势和公司快速成长的红利;
- 公司内部成套培养课程,大牛讲课,帮助提升个人技术能力;
- 公司内投资理财专家定期课程,提升个人投资认知,助力财富管理能力成长;
- 能接触金融和互联网两大黄金行业的各种玩法,鼓励创新,有各种小组研究AI,区块链,财务分析,量化交易等技术,只要有兴趣都能来参与;
- 好氛围(扁平沟通,坦诚相处)、好健康(自带健身房冲凉房)、好快乐(各大协会,车友酒友应有尽有)
招聘岗位
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: 作为富途体育学院-健身协会会长,如果你对健身感兴趣,可以带你飞; 程序员专心工作很累, 又想健身, 你有什么科学的好方法 ?
执行的结果顺序是什么?为什么?
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的代码
(场景是:有很多个controller,每个controller内部的代码除了controller的名称以外全部都一样,只需要把名称换一下。但是我不想复制粘贴几十个这样的controller)
fs.cheateWriteStream()同时保存多个视频文件怎么处理,求助?
例如: 每一个url都是一个视频的地址,我想把对应的视频保存下来,这样写不对,求助
async/await 如何优美的处理异常?
本着一种学习的态度写了此话题,如有不妥之处还请各位大牛多多指点.
Purpose: 此解决方案,可以避免业务代码中出现大量的try/catch, 众所周知try/catch对性能方面有一定的影响, 另一方面try/catch在Node.js >= v8.3.0以后对性能的损耗是可以忽略不计的.
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底层实现
这篇文章主要从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.xxxNext
和this.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中只要添加了事件监听,就会报一个跨域的错误,直接上代码
下面图片是后端代码 不知道为什么只要添加了事件监听,就会报一个跨域的错
弱弱地问一下,cluster 和 child_process 的使用场景
我对服务端开发的认知还处在 curd 的阶段,所以这些进程之类的并没有使用过。最近又开始进入 Node 的世界,所以产生了一些疑问:
- 大家说说在什么项目里,或者自己在什么场景下用到过进程,cluster 和 child process 的使用场景有那些,并且说说为什么
- 以及在大家熟悉的其他的后端语言中,是否都存在调用进程的场景
本人 3 年前端经验,就像上面说的,对服务端开发的认知还处在 curd 的阶段,所以希望大家不吝赐教。谢谢
cluster 和 child process 的概念也都看了,就是压榨机器,提升性能。不过这些是不是加机器也能做到,如果是一个集群的情况下呢?
ShadowNode: 以更轻量级的方式使用 Node.js
好久不见,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?
想试着做一个简单的js版oj,需要一个只包括js基础功能的运行环境,没有Node或浏览器里的额外功能的。 像mongodb的shell也是在一个干净的js shell上加的mongodb相关功能,我想知道哪里能找到这样一个干净的js shell
在window上测试安装cnpmjs.org私有仓库
在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
4.2 创建数据库cnpmjs(名字随便)
在mysql连接名字上比如local
,右键->新建数据库
4.3 执行sql
在数据库cnpmjs
名字上右键->命令行界面,将./docs/db.sql
中的sql语句进行copy,
粘贴到命令,回车执行ok。
特别注意有个./docs/user.sql
,必须单独执行一下
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
,现在应该一切正常了
7001验证
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.下面我们在仓库中查看,已经有了一个包了
2.这上面只有通过搜索才能查看到发布的包
3.进一步查看,注意我画红线的部分,是在config/index.js
里配置的
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.
到此安装成功,应该可以使用了
总结
- 全程window环境,相信在mac, linux环境肯定也没问题的
- sql语句手动执行,创建database和table
- 更改相关配置一切搞定
- 全程没有使用cnpm这个,使用更方便nrm来切换仓库池
- 更多详细配置请查看cnpmjs.org官方文档
大家有什么压力测试工具呢
如题
express如何接收websocket请求
项目情况:
- 前端使用 actioncable 发送 socket到node端。
ActionCable.createConsumer(`ws://${location.hostname}:8000/cable`)
- node 使用 http-proxy-middleware 转发 socket请求。
app.use('/cable', proxy({ target: proxyTarget, changeOrigin: true, ws: true, }))
- rails 后端接收 node发送的请求。
但是前端发送的socket报错
WebSocket connection to 'ws://127.0.0.1:8000/cable' failed: Connection closed before receiving a handshake response
node没有接收到任何请求,日志没有记录。
想问一下,怎么弄转发。