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

Nodejs爬虫实战项目之链家

$
0
0

说明

作为一个前端界的小学生,一直想着自己做一些项目向全栈努力。 愁人的是没有后台,搜罗之后且学会了nodejs和express写成本地的接口给前端页面调用。 可是可是,数据从哪里来? 有人说,“ mockjs去生成!” ok,引入了mock,循环生成一些random数据, 列表成型了,也可以loading more了,Table行数据填满了,也可以增删改查了,曲线的绘制也从原来的一条平行线变得跌宕起伏了。 但是,那毕竟是假数据。 要想拿到真实数据,还是得上“蜘蛛网”,用实战小小的体验一把。 很基础,不喜勿喷… 转载注明出处:Nodejs爬虫实战项目之链家

效果图

开始爬.gif

爬完.gif

查询数据库.gif

百度地图标记.gif

思路

1.爬虫究竟是怎么实现的? 通过访问要爬取的网站地址,获得该页面的html文档内容,找到我们需要保存的数据,进一步查看数据所在的元素节点,他们在某方面一定是有规律的,遵循规律,操作DOM,保存数据。例如:访问链家新房楼盘

链家新房楼盘.png

首先,看到这样一个列表,我们需要的数据无非就是楼盘的 图片,链接地址,名称,位置,房型,建筑面积,特色,类型,售价…

接下来,按F12看一看这些数据都在哪里 检查元素.png

可以看到他们都存放在一个一个的li里, 再找一找图片的位置

找图片.png

对于这样一页的十个li,每一个图片我们都可以通过’.house-lst .pic-panel img’找到,所以我们在代码就按这个方式找到图片元素,而且一次能找到十个,遍历存放。 其他就不找了,如果找不到可以通过源码联系着想一想为什么这么做?

2.怎么爬取所有页的全部数据呢? 按照刚才的方法,我们完全可以爬到第一页的数据保存下来,但是要想爬到所有页的数据,我们还需要找到页码之间的规律,下面我试着访问第二页,看看有什么幺蛾子? pg2.pngpg3.pngpg37.png在原有路径的基础上,循环加入/pg{i}/不就OK了嘛?

技术栈

  1. 后台:Nodejs+express+mongodb(mongoose)+superagent+cherrio
  2. 前端:React+React-Router(v4)+Ant-design+百度地图+Echarts(后续添加)
  3. 交互:express API+socket.io

步骤

一、新建项目

npm install -g create-react-app create-react-app nodejs-spider cd nodejs-spider

二、后台部分 1.安装依赖包

npm install --save express mongodb mongoose superagent cherrio socket.io body-parser

2.新建server.js编写后台服务 看了效果就会知道,爬取过程是一页一页的爬,而且是爬完这一页才会爬下一页。如果不这么做,他会忽略爬取的时间,直接显示所有页的爬取提示并说明爬取完成,其实接下来的时间他还在继续爬,这样我们并不知道什么时候结束,怎么通知前端去显示爬取的进度呢?所以这里要使用ES7的 async/await。 传送门:体验异步的终极解决方案-ES7的Async/Await

三、 前端部分 1.安装依赖包

npm install --save react-router antd

2.配置环境 Create-react-app+Antd+Less配置

3.路由和组件 整体布局就是头部导航栏 + 内容 + 底部 头尾是公共的,内容部分通过两个路由指向两个组件。

//路由导出
import Map from '../components/Map';
import Chart from '../components/Chart';

export default [
    {
        path: '/',
        name: '地图',
        component: Map
    },
    {
        path: '/page/Chart',
        name: '数据分析',
        component: Chart
    }
]
//路由渲染
<Content style={{ padding: '0 50px' }}>
      <Switch>
            {routers.map((route, i) => {
                  return <Route key={i} exact path={route.path} component={route.component}/>
            })}
      </Switch>
</Content>
//路由导航
<Menu
     ...
   >
  {routers.map(function (route, i) {
      return (
          <Menu.Item key={i}>
            <Link to={route.path}>
              {route.name}
            </Link>
          </Menu.Item>
      )
    })}
</Menu>

4.socket.io通信 实时通信方式,用于监测后台抓取进度。

//点击抓一下,通知后台要抓数据了
        socket.emit('request', '收到抓取请求...');
        //监听后台的progress信息,实时刷新进度
        socket.on('progress', function (data) {
            // console.log(data);
            this.setState({
                progress: data.progress,
                loading: true,
            });
            if(data.progress==='抓取完成!'){
                this.setState({
                    loading: false,
                });
            }
        }.bind(this));

5.百度地图api使用问题 百度地图对开发者免费开放接口,首先要申请秘钥然后然后使用传送技能 传送门:当react框架遇上百度地图百度地图api示例注意: VM10127:1 Uncaught TypeError: Failed to execute ‘appendChild’ on ‘Node’: parameter 1 is not of type ‘Node’. 错误的意思是 百度地图api中 this.openInfoWindow(infoWindow) 要求的信息窗口对象为appendChild可识别的真实DOM的Node类型,而不是React这种虚拟DOM组件。所以这里只能使用jq原始的字符串拼接,要细心,还挺复杂的。

6.数据分析部分有空会用Echarts完善。

总结

很基础的爬虫入门示例,但能把基础的事情做好不容易。 说明的过程中并没提及多少代码,主要着重一些思路和如何实现,代码可以去github上down一下,互相学习交流。 办法总比问题多,虽然有解决不完的BUG,但也有对应的法则。

源码

Github


关于 REDAME 中的一些小问题

$
0
0

image.png小白,想要自己发布一个项目到 npm 上,这边别人的 REDAME 上都有版本号信息,下载信息等等,,本人也想要弄一个,但是不知道该怎么生成!!!!!!!!!!!!!!!!!!!!!

Node 8:迎接 async await 新时代

$
0
0

原文发表于作者的博客:http://morning.work/page/nodejs/ready-to-async-await.html转载请注明出处 如果对文章感兴趣,可以关注作者的微信公众号【老雷的实验室】获取新文章推送

前言

在今年 11 月份 Node.js 发布 v8.x LTS 版本之后,终于可以不用借助额外的工具就可以使用 ESNext 标准中的 async function来进行异步编程,彻底改变了我们的编程习惯。以前被人诟病的 回调地狱将不复存在,而且再也不需要使用 Generator 这种 撇脚的方式了(请阅读我于去年写的文章《基于 Generator 与 Promise 的异步编程解决方案》)。

在此之前,为了在 Node v6.x 及更早的版本中使用 async function,我们需要使用 Babel或者 TypeScript这样的工具将代码转换成使用 callback或者 Generator的方式。而 根据以往的经验,这些被转换过的代码执行效率都比较低,比如 Node v4.x 以前的 Generator 和之后的 原生 Promise。因此,为了让我们更有信心地使用 async function,我做了一些简单的性能测试,请看下文。

测试方法与代码

原始的代码使用 TypeScript 编写,然后通过命令 tsc --target esnext|es6|es5将其分别转换为不同的目标代码(文件:build.sh):

  • ESNext - 使用 async function方式,文件:esnext.js
  • ES6 - 使用 Generator方式,文件:es6.js
  • ES5 - 使用 callback方式(注意:使用此方式会生成很多无用的代码,其主要是用于模拟 Generator,比我们自己手写的 callback 代码低效很多),文件:es5.js

被编译后的代码会分别在 Node.js 各个主要版本上执行:v4.8.76.12.28.9.39.3.0(为了描述方便,下文会简单描述为 4.x、6.x、8.x 和 9.x),其中由于 v4.x 和 v6.x 不支持 async function则不需要执行该测试。执行花费的时间取开始和结束的 process.uptime()之差(单位为秒),内存占用取 process.memoryUsage().rss / 1000000(单位为 MB)。

以下是 TypeScript 源码:

"use strict";

function add(n): Promise<number> {
  return new Promise((resolve, reject) => {
    resolve(n + 1);
  });
}

async function call() {
  const a = await add(1);
  const b = await add(2);
  const c = await add(3);
  const d = await add(4);
  return a + b + c + d === 14;
}

async function test(n: number) {
  const version = `node ${process.version}`;
  const name = __filename
    .split(/\\|\//)
    .pop()
    .slice(0, -3);
  const promise =
    Promise.toString().indexOf("[native code]") !== -1
      ? "ES6 Promise"
      : "bluebird Promise";
  const title = `${version} ${name} with ${promise} - test ${n} times`;
  const time = process.uptime();
  for (let i = 0; i < n; i++) {
    await call();
  }
  console.log(
    "%s - %ds - %dMB",
    title,
    (process.uptime() - time).toFixed(2),
    (process.memoryUsage().rss / 1000000).toFixed(1)
  );
}

const K = 1000;
let num = parseInt(process.env.NUM, 10);
if (isNaN(num) || !(num > 0)) {
  num = 100;
}
test(num * K);

说明:

  • add()模拟一个简单的异步操作,通过 Promise实现,返回输入参数 n的值加 1
  • call()模拟一次异步调用流程,包含 4add()异步操作
  • test()是测试流程控制,包含循环多次测试 call()并返回计算总花费时间和内存占用,并打印出结果

测试结果分析

我们首先看看执行 100 万次call()时,使用 callbackGenerator在各个版本上执行的情况:

Node.js 版本异步方式花费时间(s)内存占用(MB)
v4.xcallback45238.9
同上Generator24.0271.5
v6.xcallback20.0261.9
同上Generator22.6269
v8.xcallback5.5785.8
同上Generator5.7296.3
v9.xcallback6.491.9
同上Generator6.5996.8

由上表可以看出,从 v6.x 到 v8.x 其花费的时间和内存占用都降低了很多(v9.x 由于是非稳定版本的原因,测试结果比 v8.x 差是可以理解的),说明新版本的 Node.js 性能都有了很大的提升。

我们关心的第一个问题是:直接使用 async function 会比转成相应的 Genrator 代码高效吗?

Node.js 版本异步方式花费时间(s)内存占用(MB)
v4.xGenerator24.0271.5
v6.xGenerator22.6269
v8.xGenerator5.7296.3
同上async1.7631.5
v9.xGenerator6.5996.8
同上async1.9131.5

答案是肯定的。在 Node v8.x 上使用 async function 比转成相应的 Generator 代码执行性能提高了 3 倍。

一直以来我们都有一个印象,原生 Promise 性能很差,一般会使用 bluebird这种第三方 Promise 实现来代替。那么第二个问题来了:使用 bluebird 代替原生 Promise 会不会更高效?

(方法:执行测试程序前通过 global.Promise = require("bluebird")替换全局的 Promise 对象)

Node.js 版本异步方式花费时间(s)内存占用(MB)
v4.xGenerator10.5486.2
v6.xGenerator8.1778.9
v8.xGenerator5.4690.3
同上async16.2534
v9.xGenerator5.8393.7
同上async15.3934.7

对比上面两张表格可以看出,在 v4.x 和 v6.x 的时候,使用 bluebird 差不多有 2 倍的性能提升。但是,在 v8.x 之后却是相反的。因此,Node.js v8.x 的原生 Promise 已经得到了很大的优化,可以不需要使用 bluebird 这样的第三方 Promise 库;如果使用了 async function,替换原生的 Promise 反而会大大降低性能。

好了,最最关键的问题来了:使用 async function 与 callback 方式做同样的事情性能相差多少?

以下是我将上文的代码 经过简单的转换而成的 callback 写法:

"use strict";

function add(n, callback) {
  process.nextTick(() => callback(null, n + 1));
}

function call(callback) {
  add(1, (err, a) => {
    if (err) return callback(err);
    add(2, (err, b) => {
      if (err) return callback(err);
      add(3, (err, c) => {
        if (err) return callback(err);
        add(4, (err, d) => {
          if (err) return callback(err);
          callback(null, a + b + c + d === 14);
        });
      });
    });
  });
}

function test(n) {
  const version = `node ${process.version}`;
  const name = __filename
    .split(/\\|\//)
    .pop()
    .slice(0, -3);
  const title = `${version} ${name} with callback - test ${n} times`;
  const time = process.uptime();
  const done = () => {
    console.log(
      "%s - %ds - %dMB",
      title,
      (process.uptime() - time).toFixed(2),
      (process.memoryUsage().rss / 1000000).toFixed(1)
    );
  };
  let i = 0;
  const next = err => {
    if (err) throw err;
    if (i < n) {
      i++;
      process.nextTick(() => call(next));
    } else {
      done();
    }
  };
  next();
}

const K = 1000;
let num = parseInt(process.env.NUM, 10);
if (isNaN(num) || !(num > 0)) {
  num = 100;
}
test(num * K);

说明:

  • 所有异步函数的回调函数格式为 callback(err, ret),如果没有错误则 err = nullret表示返回值
  • 所有异步回调第一行都会检查 if (err) return callback(err),虽然看起来啰嗦,但是要尽量模拟真是的场景
  • 每次执行 call()都需要放在 process.nextTick()回调函数里面,主要是用于模拟一个异步操作,否则程序会因为调用堆栈过深而报错

以下是执行结果:

Node.js 版本花费时间(s)内存占用(MB)
v4.x0.6454.8
v6.x0.6554.8
v8.x1.0427.5
v9.x1.0327.5

使用 async function编写的代码在 Node.js v8.x 花费的时间是 1.76s,内存占用是 31.5MB,与使用 callback编写的代码相比,数值相差并不大(执行 100 万次时间相差不足 1 秒),属于可以接受范围

结论

以上可以算是一个 不太严谨的测试方案,并没有全面地测试不同实现方式对结果的影响,也没有重复执行多次的测试来尽量减少结果偏差。但无论怎样,通过这些测试结果我们还是可以知道:

  • 从 v4.x 到 v8.x,Node.js 的整体性能有了很大的提升
  • Node.js v8.x 的原生 Promise 已经足够快,不再需要使用 bluebird 来代替了
  • 直接执行 async function 比转换成相应的 Generator 代码要快很多
  • 使用 async function 编写代码跟使用 callback 方式编写代码之间的性能差异已经很小了,而 async function 的代码往往更直观

新的 ES 语法大大简化了异步编程的难度,而随着 Node.js 版本的升级,刚开始担心的那些性能问题也终将化为浮云。

所以,如果你已经用上了 Node.js v8.x,而且不需要兼容老的 Node.js 版本,可以放心大胆地使用 async function 啦。

相关链接

docker可以设置密码么?

$
0
0

docker可以设置密码么? 不是网上说的设置Ssh连接密码! 是想问用docker attach进入的时候能不能使用密码才能登录?

来自酷炫的 CNodeMD

入门到放弃node系列之微信公众号开发实战一(Express篇)

$
0
0

前言

本文首发公众号【一名打字员】

相信通过之前的几篇文章,大家都对node有更深层次的了解了。node作为一个灵活性较强的工具,我们可以使用它来做很多很多好玩的东西。前几年微信公众号开发比较火,大街小巷都在招人,所以今天本着实战的目的,给大家介绍一下使用express来开发微信公众号的具体流程。

Express

开始前,简单介绍一下express,官方对它的介绍是“基于nodejs平台,快速、开放、极简的web开发框架”。虽然近年来它的生态圈已经远远不如koa强大,很多express应用都用koa进行重写过,但是作为一个从事JAVA后台的打字员来说,还是比较依赖于使用express的。

简单来说,express可以帮助我们快速的建立一个web应用,有多快呢,接下来就可以看到了。

  • 安装

首先我们需要安装Express模块,使用 nom install express -g安装全局模块,这个过程一般会很快。

  • 初始化

安装成功后,我们就可以使用 express这个工具了。

untitled1.png

通过help列表,我们看到,它有一系列的命令提供使用,后面还有具体的用法。接下来我们初始化一个express应用,使用 express init wechatTest命令express会帮我们快速的创建一个工程,这里面包含着我们基本的框架,可以直接通过 npm install,npm start运行。

untitled2.png

由于默认的端口是3000,所以我们直接访问localhost:3000就能看到express的欢迎界面了。 简单的一个web站就已经建立成功了,怎么样,快不快、意不意外、惊不惊喜?

庖丁解牛

接下来我们一块一块的分析整个结构,对整个项目有一个清晰的了解。

  • package.json

我们上次说过,通过package.json来管理整个项目的依赖以及一些信息的初始化。

{
  "name": "wechattest",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "body-parser": "~1.16.0",
    "cookie-parser": "~1.4.3",
    "debug": "~2.6.0",
    "express": "~4.14.1",
    "jade": "~1.11.0",
    "morgan": "~1.7.0",
    "serve-favicon": "~2.3.2"
  }
}

可以看到,我们这个项目里面依赖了好几个包,其中每个包的具体作用我们后面再进行讲解。另外我们看到配置里为我们编写了一个 start脚本,所以,我们能够通过npm start来快速的执行脚本。

  • app.js

我们看到根目录下有一个app.js文件,这是整个应用的入口。

var express = require('express');//引入express模块
var path = require('path');//url地址解析
var favicon = require('serve-favicon');//服务端图标
var logger = require('morgan');//日志
var cookieParser = require('cookie-parser');//cookie中间件
var bodyParser = require('body-parser');//请求中间件

//路由
var index = require('./routes/index');
var users = require('./routes/users');

var app = express();

//模版引擎设置
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

//图标放置于public(静态资源配置)
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
//静态资源中间件
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', index);
app.use('/users', users);

//404界面捕捉
app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

//异常界面处理
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

注释很清楚的解释了各个部分的作用,在构建一个web服务器的时候,我们通常最重要的工作就是路由处理,也就是响应针对某个路径的请求。在这里我们直接使用路由配置的方法,如 app.getapp.post来进行配置。

  • 中间件

上面的文件中有提到中间件的概念,在express中,通常在收到请求后和发送响应之前这个阶段执行的一些函数,这个就叫做中间件。在app.js中我们看到可以使用 app.use来使用某个中间件。其原型如下:

app.use([path,] function [, function...])

比如上面的 express.static(path.join(__dirname, 'public'))也是一个中间件,通常用来处理静态文件的目录。

  • 路由

在express中,提供了一个Router对象来针对GET、POST等处理的路由,通常把它传给 app.use。我们可以看到上面引用了一个index模块,打开index.js文件。

var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

module.exports = router;

我们拦截了/,所以我们访问localhost根路径的时候就会被这里拦截,然后进行相关的操作,我们可以在这里加入对相关的路径处理。

  • 模版渲染

上面介绍了使用路由来对路径的解析和拦截,但是如何在拦截到请求路径的时候,将界面渲染出来呢,这个时候就需要使用模版引擎了。express初始化的时候给我们设置了使用 jade的模版引擎进行渲染,如index.jade。

extends layout

block content
  h1= title
  p Welcome to #{title}

jade的语法这里不做多的介绍,这里可以去官网中查看具体的说明介绍。

未完待续

由于篇幅过长,这篇文章将会被分为几个小节,这里我们能够使用express搭建起基本的框架,并对其中的一些模块有了一定的了解。之前用java的时候写jsp写的比较多,后来也是使用freemarker等模版引擎渲染界面,所以对jade的语法不是很中意,所以下一章首先会介绍如何将jade换乘ejs,一个语法类似jsp的引擎。

另外,留言功能已经开通了哟,各位小哥哥小姐姐有问题的可以直接留言哟,欢迎来撩~(奸笑脸)

返回对象json给客户端如何优雅地去除多余/添加额外字段?

$
0
0

举个栗子,写一个用户登录接口。

var UserSchema = new mongoose.Schema({
  phone: { type: String, trim: true }, // 手机号码
  email: { type: String, trim: true }, // 邮箱
  *:{}, //此处省略n个字段
  passwrod: { type: String, trim: true }, // 密码
}
//登录返回用户对象
User.find(_, function(err, user){
	res.json(user)
})
问题一: 只想返回某部分字段,不想返回password等多个字段,应该怎样优雅地处理?

只能在查询的时候指定字段查询吗? (字段多的时候岂不是要跪?)

User.find(_,'phone, email' function(err, user){
	res.json(user)
})

一个React多图裁剪加上传工具

$
0
0

建议直接下载或者克隆整个项目。因为没有写完… 地址

Demo

Usage

install

  • use npm
 npm install rcrop --save
import React from 'react'
import ReactDOM from 'react-dom'
import Rcrop from 'rcrop'
import 'rcrop/lib/dist/rcrop.css'
//这个地址暂支持单张图片上传,否则可能会上传失败
const _request = (base64) => fetch('http://119.28.73.28/oa/base.php', {
          method: 'POST',
          headers: {
            Accept: 'application/json',
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            uid: '2016888999',
            imgCode: base64[0]
          })
        })
          .then(res => {
            if (res.status <= 300 && res.status >= 200) {
              alert('头像更新成功')
            } else {
              alert('头像更新失败,有点尴尬')
            }
          })

ReactDOM.render(<Rcrop plugin={mode:'xhr',request:_reqest}/>,document.getElementById('root'))

Contributing

  • npm install
  • npm start

[杭州] The Plant | 前端招聘

$
0
0

The Plant | 杭州

https://theplant.jp/en

Your Technology Partner for Online Business. We create lasting relationships, which is why for over 10 years companies like ASICS, Danone, Lacoste, and MTV have worked with us so successfully that they trust us with mission-critical business systems. We specialize in customized multilingual, high-performance E-commerce and transactional business applications for global deployment.

我们提供:

  1. 工程师文化满载的办公环境,Ruby X Golang X NodeJS。
  2. 1000+/M,美金13 薪。
  3. 每年一次涨薪。
  4. 弹性工作时间,每日 8 小时。
  5. 一台最新 MBP/iMac。
  6. 带薪婚假。
  7. 五险。
  8. 每天中午水果,不间断零食,胶囊咖啡,生日福利。
  9. 海外工作机会:目前有东京总部,以及合作伙伴的业务,外派欧洲。

我们要求:

  1. 良好的前端基础,明白 HTML & CSS & JS 的各标准来由,技术成因。
  2. 会 vanilla JS 以外, React / Vue / Angular / jQuery 熟用其一。
  3. 如果专注于 React,那么需要了解 Redux,Redux-Saga,CSS-in-JS;Next.JS,TypeScript 等等。
  4. 前端工程工具链:Webpack / Gulp, etc。
  5. 团队协作中的 Git 使用经验。

我们希望:

  1. CSS & JS 不止于变化,理解原理,可以举一反三。
  2. 理解 HTML 是重要的。
  3. 个人作品(Github 可附)。
  4. 好的审美。
  5. 良好的(英文)沟通能力。

在杭州,约个时间来这里聊一下吧。

地址:杭州市拱墅区金华南路丝联 166 U3-2 。 简历请邮(PDF 英文): lancee@theplant.jp

以上。筹祺。


Workspace

workspace.jpgUNADJUSTEDNONRAW_thumb_2d4b.jpg

Tokyo head office (the new one at Roppongi)

IMG_3135.jpg

China team in Yunnan

UNADJUSTEDNONRAW_thumb_3a40.jpgUNADJUSTEDNONRAW_thumb_3a4a.jpg

We’re working there (Yunnan & Tokyo)

UNADJUSTEDNONRAW_thumb_3a45.jpgshibuya.jpeg

Internal sharing: techforce & workshop

UNADJUSTEDNONRAW_thumb_2d65.jpgtechforce-1.jpg


加入我们吧。


node8中 Async/Await 如何优雅的捕捉错误?

$
0
0

之前写习惯了es6,捕获错误一般是通过回调来实现 比如:

foo('A', (err, result) = >{
	console.log(err);
});

但是现在因为node8发布LTS版本,就开始用node8

用node8当然少不了 Async/Await 现在我捕捉错误用try/catch去捕捉

try{
	await foo();
}catch(err){
	console.log(err);
}

问题

众所周知,try/catch会影响性能,是否有更好的方法捕捉错误?

学习node.js的学生在找工作的时候应该如何自我定位?

$
0
0

各位大佬好,我是一个在校学生,现在在实习单位接触的项目是express和vue.js的,想顺势学习下去就接着看node的各种教材了,但是现在突然有一个疑惑,参加校招的时候应该投前端还是后端?因为我的html和css水平一般,投前端的话是不是要恶补html和css,node相关的知识只能作为一个加分项?投后端的话是不是要恶补os 分布式 多线程一类的,node算是一个冷门的技能?有点困惑应该向哪个方向努力,希望大佬们能够不吝赐教,提前谢过了。

【原创】Egg.js CI/CD 持续集成、持续部署,超级干货。

$
0
0

重点在这

链接地址

使用 TravisCI 配合 Shipit 进行自动化部署

所有代码在这个地方 代码仓库

这里贴出来一些关键性代码

shipitfile.js

'use strict';

module.exports = function(shipit) {
  require('shipit-deploy')(shipit);

  shipit.initConfig({
    default: {
      workspace: '/tmp/github-monitor',
      deployTo: '/home/nono/app',
      repositoryUrl: 'https://github.com/MiYogurt/deploy-egg-sample.git',
      ignores: [ '.git', 'node_modules' ],
      rsync: [ '--del' ],
      keepReleases: 2,
      key: './scripts/source.key',
      shallowClone: true,
    },
    staging: {
      servers: 'root@139.199.227.41',
    },
  });

  shipit.task('docker', function() {
    return shipit.start([ 'build', 'remove', 'create' ]);
  });

  shipit.blTask('build', function() {
    return shipit.remote('docker build -t nodelover:v1 .', {
      cwd: '/home/nono/app/current',
    });
  });

  shipit.blTask('create', function() {
    return shipit.remote('docker run -d --name app -p 8080:7001 nodelover:v1', {
      cwd: '/home/nono/app/current',
    });
  });

  shipit.blTask('remove', function() {
    return shipit.remote('docker stop app', {
          cwd: '/home/nono/app/current',
        }).then(o => shipit.remote('docker rm app', {
          cwd: '/home/nono/app/current',
        })).catch(err => console.log("no need stop"))
    });
};

其实写 sh 脚本也用不了几行,反而更简单些,只是说并不是谁都能写 sh 脚本的,需要一定的 linux 技能。

Dockerfile

from node:9.2.0

add . /app

expose 7001

workdir /app

run npm i

cmd npm run start

.travis.yml

sudo: false
language: node_js
addons:
  ssh_known_hosts: 139.199.227.41
node_js:
  - '9'
before_install:
  - openssl aes-256-cbc -K $encrypted_b8bda4515144_key -iv $encrypted_b8bda4515144_iv -in scripts/source.key.enc -out scripts/source.key -d
install:
  - npm i npminstall && npminstall
# script:
#   - npm run ci
after_script:
  # - npminstall codecov && codecov
  - chmod 600 scripts/source.key
  - shipit staging deploy
  - shipit staging docker

就这3 个文件,不花一分钱就可以实现基于 Docker 的 CI/CD, 当然普通的,回退到 PR 的那一个版本就可以了。

node 爬取今日 头条 数据 数据的排版有很多不一样的 怎么爬呢

$
0
0

今日头条 pc端网页的 推荐 热点 科技 等等 栏目 每条数据展示的排版 有些情况下 有区别 该怎么处理 还有 其它新闻类网站呢 这些该怎么爬 我用的 是phantom 爬的 loadPage(‘https://www.toutiao.com/’); async function loadPage(url) { const newsArray = []; driver.get(url); let result = await driver.getPageSource() let channelArr = channelList(result); // 暂时用推荐做实验 getNewsList(driver,channelArr[0]); } function channelList(pageSource) { const $ = cheerio.load(pageSource); eles.each(function (index, element) { let item = $(element).text(); let path = $(element).find(‘a’).attr(‘href’); channelArr.push({channel: item, href: path}) }); return channelArr; } 我现在这种处理 太简单了 感觉没啥用 好多都爬不到

Node.js的CPU密集计算的终结解决方案!!!

$
0
0

好吧我承认我是标题党,但这种方法确实解决了node的CPU密集型计算时造成服务无法响应或响应过慢问题,其实我讲的的是Faas的serverless简单使用,基于某云平台进行讲解。

写在前面:

大家都知道node.js基于EventLoop,本质上就是对异步进行排队运算,所以这就面临一个问题,如果大量计算放入了队列中运算,后面就要等前面运行完,严重导致队列堆积导致请求超时之类的问题。但目前很多人用线程来解决,我觉得这样不好,会增加代码复杂度,并且如果有海量运算的话也没办法开无数多的线程(爆机器问题(所以有线程池)和大于逻辑核的无意义)。那我们有没有不增加复杂度,来解决这个问题呢?的确有,Faas框架来实现的serverless就能将CPU密集计算问题迎刃而解(原因文末说明)。最早是亚马逊的lambda,当目前国内也有类似实现,那我就选择国内某云的函数计算来验证是否解决CPU密集计算问题,同时会对利用某云函数计算的使用进行一次详解(本来是写了具体云平台,但为了避免广告嫌疑改某云)。

首先我们要创建一个函数计算的服务和方法

当然我们要先选择某云的产品列表的“函数计算”来创建服务 createServer一般选择白板函数就好 createFunc1我们用著名的斐波那契来模拟CPU密集型吧 createFunc2代码奉上

module.exports.handler = function(event, context, callback) { 
  let req = JSON.parse(event);
  //为什么是40,因为每个函数计算时间不能超过3秒
  let num = req.queryParameters.num>0?
      req.queryParameters.num:
  	  Math.ceil(Math.random()*40);
  //由于这个直接作为页面数据,所以api网关需要状态码和页面显示信息
  callback(null,{
    body:`num is ${num},fibo is ${fibo(num)}`,
    statusCode:200}
  ); 
};

function fibo(num)
{
	if(num<2){return 1;} 
  	return fibo(num-1)+fibo(num-2);
}

接下来创建对应的Api网关

由于函数计算的方法可以互相调用和触发调用,api网关的作用就是为了通过用户访问网关触发函数计算的方法调用

当然我们要先选择某云的产品列表的“api网关”来创建网关服务 创建分组后,走如下图步骤 安全认证的作用是只让自己的手机App或自己的SDK用启用的,我们希望每个用户都能访问,改成无认证就好 api1接下来定义自己给别人访问的url,同时可以将自己域名绑定到自己的分组,实现通过自己域名访问 api2这里的要点就是选择函数服务,然后填入函数计算当时创建的服务名和方法名就好。 api3最后发布上线就好,当然你还可以在外面的api试调先调用试试。 api4

对比一下自己本机开的服务进行压测

本机代码

var http = require('http');
var url = require('url');

function fibo(num)
{
	if(num<2){return 1;} 
  	return fibo(num-1)+fibo(num-2);
}

http.createServer(function(req, res){
    res.writeHead(200, {'Content-Type': 'text/plain'});
    var params = url.parse(req.url, true).query;
    let num = params.num>0?params.num:Math.ceil(Math.random()*40);
    res.end(`num is ${num},fibo is ${fibo(num)}`);
 
}).listen(3000);

压测结果

某云函数计算压测结果: test1本机压测结果(已被CPU密集计算阻塞): test2

原因

由于函数计算会在访问量大的时候进行动态伸缩(其实就是加实例和机器,但只对需要的函数伸缩不像Paas要对整个平台伸缩造成浪费)所以在CPU密集时,它会为了保持及时的响应速度,进行进行伸缩,不至于CPU密集会导致无法访问或访问过慢。

最后

如果使用此某云实现的Serverless,那么建议使用该云推出开发工具,因为能更快用自己的IDE进行编码和开发,本人使用web页面,纯粹是为了更好展现功能。原文地址:https://github.com/zy445566/myBlog/tree/master/20171214serverless

testtesttest

cnodejs.org接口源码或者表结构 哪里能找到

$
0
0

希望直接使用 cnodejs.org论坛,所以想知道后台接口的源码或者数据库表结构


有人用过koa-generic-session和koa-session 这两个插件吗?区别是啥啊

node父进程如何根据子进程pid向子进程进行通讯?

$
0
0

假如只知道子进程的pid,而不是具体的childprocess对象,如何获取该对象呢?因为我需要向子进程通讯。

杭州网易考拉急招前端开发

$
0
0

我们提供什么样子的待遇,福利,以及工作环境? 工作氛围好,公司发展快,三餐免费,15-30k。

我们对候选人的要求? 岗位职责

1、根据产品设计需求,配合后台开发人员实现产品界面和功能,维护及优化前端页面性能; 2、制作或协助制作各种页面组件或页面模版; 3、参与制定JS、CSS、Html开发规范并推进执行; 4、参与设计并编写WEB前端架构及应用。

技能要求

1、本科及以上学历,计算机相关专业,3年以上相关工作经验; 2、透彻掌握HTML、CSS,对网页标准有成熟的理解,能够很好地解决兼容问题; 3、熟练掌握Javascript及一些常用框架,能够使用它们实现前端交互效果; 4、充分理解前端开发对视觉设计、用户体验和网站性能的重要性; 5、持续关注业界最新发展,对HTML5、css3、响应式设计等前沿技术与其设计理念有实际了解和实践。

React同构与极致的性能优化

$
0
0

本文发表于北斗同构github, 转载请注明出处

注: 本文为第12届D2前端技术论坛《打造高可靠与高性能的React同构解决方案》分享内容,已经过数据脱敏处理。

前言

  • 随着React的兴起, 结合Node直出的性能优势和React的组件化,React同构已然成为趋势之一。享受技术福利的同时,直面技术挑战,在复杂场景下,挑战10倍以上极致的性能优化。

什么是同构?

  • 一套代码既可以在服务端运行又可以在客户端运行,这就是同构应用。简而言之, 就是服务端直出和客户端渲染的组合, 能够充分结合两者的优势,并有效避免两者的不足。

为什么同构?

  • 性能: 通过Node直出, 将传统的三次串行http请求简化成一次http请求,降低首屏渲染时间
  • SEO: 服务端渲染对搜索引擎的爬取有着天然的优势,虽然阿里电商体系对SEO需求并不强,但随着国际化的推进, 越来越多的国际业务加入阿里大家庭,很多的业务依赖Google等搜索引擎的流量导入,比如Lazada.
  • 兼容性: 部分展示类页面能够有效规避客户端兼容性问题,比如白屏。

性能数据

性能是一个综合性的问题, 不能简单地断言同构应用一定比非同构应用性能好,只能说合适的场景加上合理的运用,同构应用确实能带来一定的性能提升, 先来看一个线上的案例。

isomorphic

通常来说,网络状况越差,同构的优势越明显,下图是在不同网络状况下首屏渲染时间的一组对比

isomorphic

线上案例

  • 近两年,无论是业界还是阿里内部都涌现了大量同构实践, 业界比较有影响力的包括Facebook, Quora, Medium, Twitter, Airbnb, Walmart、手Q以及QQ兴趣部落等
  • 阿里内部也有大量的应用,仅列举部分beidou开发组做过技术支持的项目
    • 阿里云 - 大数据地产
    • 钉钉 - 企业主页
    • 钉钉 - 钉钉日志和审批模板市场
    • 菜鸟 - 物流大市场
    • 云零售 - 店掌柜
    • Lazada - PDP
    • 国际事业部 - AGLA
    • AILab - 行业解决方案
    • AILab - 智能硬件平台
    • AILab - AliGenie开放平台
    • AILab - AR官网
    • ICBU - ICBU店铺
    • 业务平台 - 门店评价
    • 国际UED - 数据运营
    • 国际UED - 知之
    • 国际UED - 探花
    • 国际UED - Nuke官网及过程管理
    • 国际UED - 会议记录,实时翻译
    • 国际UED - LBS数据地图
    • 国际UED - 数探
    • 国际UED - 微策
    • 国际UED - shuttle
    • 国际UED - fie portal

业界生态

  • react-server: React服务端渲染框架
  • next.js: 轻量级的同构框架
  • beidou: 阿里自己的同构框架,基于eggjs, 定位是企业级同构框架

除了开源框架,底层方面React16重构了SSR, react-router提供了更加友好的SSR支持等等, 从某种程度上来说,同构也是一种趋势,至少是方向之一。

思考 与 实现

同构的出发点不是 “为了做同构,所以做了”, 而是回归业务,去解决业务场景中SEO、首屏性能、用户体验 等问题,驱动我们去寻找可用的解决方案。在这样的场景下,除了同构本身,我们还需要考虑的是:

  • 高性能的 Node Server
  • 可靠的 同构渲染服务
  • 可控的 运维成本
  • 可复用的 解决方案

简单归纳就是, 我们需要一个 企业级的同构渲染解决方案。

我们是怎么做的?

基于 eggjs 加入可拔插的同构能力

  • beidou-plugin-react作为原有MVC架构中, view 层的替换, 使用 React 组件作为视图层模板, 可以直接渲染 React Component 并输出给客户端

  • beidou-plugin-webpack集成 Webpack 到框架中, 在开发阶段, 提供代码的编译和打包服务

  • beidou-plugin-isomorphic服务端的 React 运行时: babel-register polyfill 注入: 环境变量, BOM等 非js文件解析: css, images, fonts…

  • 服务端支持css modules

  • 自动路由: 纯静态页面无需编写任何服务端代码,像写纯前端页面一样简单

这里不再赘述具体如何实现,有兴趣的读者可以阅读我们的开源同构框架beidouhttps://github.com/alibaba/beidou

热点问题

任何一种技术都有其适用场景和局限性, 同构也不例外,以下试举一二,以做抛砖引玉.

  • 内存泄漏
  • 性能瓶颈

内存泄漏不是同构应用所特有的,理论上所有服务端应用都可能内存泄漏,但同构应用是“高危群体”, 具体如何解决请参考本人的《Node应用内存泄漏分析方法论与实战》, 接下来重点剖析下性能优化。

极致的性能优化

前面也提到了,同构应用并不一定就比非同构应用性能好,影响性能的因素实在太多了,再来看一组数据

react15 performance

上图是基于Node v8.9.1 和 React@15.5.4, 开4个进程采集到的数据, X轴是最终生成页面节点数量,Y轴红色的线表示RT(包括渲染时间和网络时间), 绿色的柱子表示QPS. 可以看出来:

  • 随着页面节点的增多渲染时间可能变得很长,QPS下降非常迅速。在页面节点超过3000左右的时候,QPS接近个位数了,而且实际页面中可能包含较复杂的逻辑以及不友好的写法,情况可能会更糟。

顺带提一下, 笔者采样了淘宝首页淘宝某详情页以及Lazada某详情页,页面节点数分别是2620、2467和3701. 大部分情况下,页面节点数低于1000, 比如菜鸟物流市场首页看起来内容不少,其实节点数是775.

那针对3000节点以上的页面,我们该怎么做呢?笔者总结了以下策略并重点阐述其中一两点:

  • 采用编译后的React版本: 根据Sasha Aickin的博客,React15在Node4、Node6、Node8下,采用编译后的版本性能相比未编译版本分别提升了2.36倍、3倍、3.85倍
  • 模块拆分: 模块拆分有利于并发渲染,目前ICBU店铺装修采用的就是这种方式
  • 模块级别缓存: 页面中某些模块其实是很适合缓存的,比如Lazada详情页中节点数虽然高达3701, 但其实页头部分就占比55.5%,页尾占比3.5%,而页头页尾是常年不变的.
  • 组件级缓存: 最小粒度的缓存单位了,性能提升依赖于缓存的范围和命中率,运用得当,可能带来非常大的性能提升。参考walmartlabs
  • 采用hsf代替http对外提供服务: hsf的网络消耗远低于http, 在店铺同构实践中,改用hsf, java端调用Node端的耗时缩短了一半.
  • 部分模块客户端渲染(对SEO无用的部分): 直接降低SSR部分的复杂度
  • 智能降级: 当流量暴增,接近或超过阈值时,会直接导致服务的RT快速上升。可以实时监测CPU和内存的使用率,超过一定的比例自动降级为客户端渲染,降低服务端压力,CPU和内存恢复常态时,自动切回服务端渲染。
  • 采用Node8: 同样在店铺实践中,采用Node8相比Node6, 渲染时间从28ms降低到了18ms, 提升幅度为36%.
  • 采用最新版React16: facebook官方数据, 在Node8下,React16相比编译后的react15仍有3.8倍提升,相比未编译的React15更是有数量级的提升。

组件级缓存

如果说性能优化有"万能"的招式,那一定是缓存, 从Nigix缓存到模块级缓存到组件级缓存,其中最让人兴奋的就是组件级缓存,让我们一起来看看如何实现

  • 拦截React的渲染逻辑,业界主要有三种实现方式
    • Fork一份React, 暴力加入缓存逻辑, 代表库是react-dom-stream, 虽然这个库的人气很高,但笔者还是反对这种实现方式的。
    • 通过require hook拦截instantiateReactComponent的载入并注入缓存逻辑,参考react-ssr-optimization
    • 扩展ReactCompositeComponent的mountComponent方法,参考electrode-react-ssr-cachin
  • 注入缓存逻辑, 代码如下
const ReactCompositeComponent = require("react/lib/ReactCompositeComponent");

ReactCompositeComponent.Mixin._mountComponent = ReactCompositeComponent.Mixin.mountComponent;
ReactCompositeComponent.Mixin.mountComponent = function(rootID, transaction, context) {
  
  const hashKey = generateHashKey(this._currentElement.props);
  if (cacheStorage.hasEntry(hashKey)) {
    // 命中缓存则直接返回缓存结果
    return cacheStorage.getEntry(hashKey);
  } else {
    // 若未命中,则调用react的mountComponent渲染组件,并缓存结果
    const html = this._mountComponent(rootID, transaction, context);
    cacheStorage.addEntry(hashKey, html);
    return html;
  }
};
  • 设置最大缓存和缓存更新策略
lruCacheSettings: {
      max: 500,  // The maximum size of the cache
      maxAge: 1000 * 5 // The maximum age in milliseconds
  }

上述缓存逻辑是基于属性的,能覆盖大部分的应用场景,但有一个要求,属性值必须可枚举且可选项很少. 请看下面的场景。 items

淘宝某页面上有大量的商品,而淘宝的商品又何止百万,就算某个被缓存,下次被命中的可能性依然微乎其微。那如何解决这个问题?聪明的读者可能已经看出来了,虽然每个商品最终渲染的结果千变万化,但结构始终是一致的,因此结构是可以缓存的。

template

要实现结构的缓存,需要在上述逻辑上额外新增三步。

  • 生成中间结构:
    • 以组件<Price>${price}</Price>为例,将变量price以占位符${price}代替set(price, "${price}"), 再调用react原生的mountComponent方法则可以生成中间结构<div>${price}</div
  • 缓存中间结构
  • 生成最终组件

以上就是组件级缓存的实现方式, 特别要提醒的是缓存是把双刃剑,运用不当可能会引发内存泄漏以及数据的不一致。

React16 SSR

  • FB在9.26发布了React16正式版,之前万众期待的SSR性能提升没有让大家失望, 引用React核心开发Sasha Aickin的对比图 react16

笔者拿之前的应用升级到React16, 对比下3909节点,RT从295ms降到了51ms, QPS从9提升到了44, 提升非常明显。

react16

实战

接下来通过一个例子,展示如何一步步地提升性能。 代码仓库https://github.com/alibaba/beidou/

10倍以上性能提升

  • 首先构造一个非常复杂的页面, 页面节点数是3342, 对比之下,淘宝首页首屏的页面节点数是831, 异步充分加载之后(懒加载完成),整个页面节点数为3049. 注: 淘宝页面为动态页面,每次采样可能会有差异。

复杂页面

淘宝首屏

淘宝全屏

  • 初始平均渲染时间为295.75ms(Node6.92, React15.6.2), 注: 图中有296.50ms,317.25ms,297.25ms,295.75ms四个平均值,是因为开启了四个进程,采样最后一个,下同。

初始渲染时间

babel性能加速插件

  • 采用Node8.9.1(或更新版本)平均渲染时间为207ms

Node8

  • 采用production模式平均渲染时间为81.75ms

production mode

  • 部分内容客户端渲染,平均渲染时间为44.63ms

part csr

  • 部分内容组件级别cache,平均渲染时间为22.65ms

part cache

  • 采用React16(或更新版本),平均渲染时间为5.17ms

react16

  • 结合React16和部分客户端渲染,平均渲染时间为2.68ms

react16+csr

至此,服务端渲染时间已经最初的295.75ms降低到了2.68ms,提升了超过100倍。

更多性能策略

其实除了上述应用的策略,还有其它的策略,比如

  • 采用Async, 有数据称性能提升30%, 笔者试了下,未见明显提升。应该是经过了babel的编译,最终没有发挥出Async的优势,这是因为beidou框架在服务端要支持import等ES6的写法以及支持React的JSX语法。其实也非常简单,直接缩小babel的编译范围,在beidou框架中是可以自己定义的。
  • 降低React组件的嵌套层级。试验数据,同样的页面节点数,服务端渲染时间和组件的嵌套层级是线性正相关的。
  • 热点缓存 …

万变不离其宗

借用《功夫》中的一句经典台词天下武功,无坚不破,唯快不破,同样的, 随着时间的推移,上面这些策略策略迟早会被破,比如react16 ssr重构之后,之前的组件级别缓存逻辑不再有效。 另外,可能由于架构设计/技术选型根本就使不上劲,比如react16是今年9月26才正式发版,很多第三方组件还没来得及升级,如果应用中有些组件强依赖于react15或者更早的版本,可能根本就没法利用react16的性能优势。

那么有没有一种万能的办法,能够做到唯快不破呢?

答案是: 有的。 只有掌握了方法论,才能在不断变化中,找到适合自己应用的性能优化策略。

具体的方法论,请参考本人的另外一篇文章《唯快不破,让nodejs再快一点》

基于koa2 + mongodb + handlebars 开发博客系统

$
0
0

序章

  • 之前一直做游戏app客户端,有天老板说app游戏不好混了,我们做H5游戏吧。。。
  • 用cocos creator做的时候,的确也可以一样画葫芦的做下去,但发现还是学点web基础更有帮助
  • 开始看书(JavaScript高级程序设计),去百度前端技术学院自学,做实例。在这接触到了nodejs
  • 那把nodejs也了解下吧,开始看书(深入浅出Node.js),做例子的时候,咦,别人写的方法怎么是=>这样的。。
  • 了解了下,哦,原来是js的新标准,那也随便了解下吧,开始看书(ECMAScript 6 入门)。。。
  • 等我了解了es6基本语法后,又冒出koa2, vue等等框架(都是百度前端技术学院例子里涉及到的。。)
  • 我怕我再看下去要疯了,而且接下去,h5项目要开始赶进度了(就是加班)。之前涉及到的知识点也够撸了。
  • 这里先总结下koa2框架等相关知识吧。。。
  • 等之后有空了就撸vue框架。都会基于这个小项目

技术支持

  • JavaScript高级程序设计
  • 深入浅出Node.js
  • ECMAScript 6 入门

环境准备

  • node: v8.6.0
  • MongoDB: 3.4.9
  • 部署到阿里云的环境
    • CPU:1核 内存:1GB
    • CentOS 7.4 64位

地址

框架

├── config.js             # 配置文件
├── public                # 静态资源目录
│   ├── blogs             # 博客文章目录
│   ├── images            # 图片资源目录
│   ├── scripts           # 静态脚本目录
│   └── styles            # 样式目录
└── server                # 后端代码目录
    ├── db                # 数据库目录
    ├── middlewares       # 自定义中间件目录
    ├── models            # 数据模型目录
    ├── routers           # 路由目录
    ├── services          # 逻辑服务目录
    ├── utils             # 工具目录
    ├── views             # 视图目录
    └── app.js            # 入口文件

目录

Viewing all 14821 articles
Browse latest View live