###Tranfser-Encoding:chunked **请问各位大神,用express搭建的服务器,如何收集客户端发来request的chunked数据并把这些数据整合成一个单个文件写到本地,我用postman模拟发起的request,如果给这个request加上header信息 Transfer-Encoding:chunked,服务器就无法获取这个request的body,如果不加上述这个头信息,就可以获取request的body,想请教一下大家有什么方法可以解决这个问题,谢谢。
服务器收集chunked data
snmpjs
有没有有关snmpjs开发的Api或者Demo之类的,不胜感激
从零开始实现React(二):实现组件功能
前言
在上一篇文章JSX和虚拟DOM中,我们实现了基础的JSX渲染功能,但是React的意义在于组件化。在这篇文章中,我们就要实现React的组件功能。
React定义组件的方式可以分为两种:函数和类,我们姑且将两种不同方式定义的组件称之为函数定义组件和类定义组件
函数定义组件
函数定义组件相对简单,只需要用组件名称声明一个函数,并返回一段JSX即可。
例如我们定义一个Welcome
组件:
function Welcome( props ) {
return <h1>Hello, {props.name}</h1>;
}
注意组件名称要以大写字母开头
函数组件接受一个props参数,它是给组件传入的数据。
我们可以这样来使用它:
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById( 'root' )
);
让createElemen支持函数定义组件
回顾一下上一篇文章中我们对React.createElement
的实现:
function createElement( tag, attrs, ...children ) {
return {
tag,
attrs,
children
}
}
这种实现只能渲染原生DOM元素,而对于组件,createElement得到的参数略有不同:
如果JSX片段中的某个元素是组件,那么createElement的第一个参数tag
将会是一个方法,而不是字符串。
区分组件和原生DOM的工作,是
babel-plugin-transform-react-jsx
帮我们做的
例如在处理<Welcome name="Sara" />
时,createElement
方法的第一个参数tag
,实际上就是我们定义Welcome
的方法:
function Welcome( props ) {
return <h1>Hello, {props.name}</h1>;
}
所以我们需要修改一下createElement
,让它能够渲染组件。
function createElement( tag, attrs, ...children ) {
// 如果tag是一个方法,那么它是一个组件
if ( typeof tag === 'function' ) {
return tag( attrs || {} );
}
return {
tag,
attrs,
children
}
}
渲染函数定义组件
在简单的修改了createElement
方法后,我们就可以用来渲染函数定义组件了。
渲染上文定义的Welcome
组件:
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById( 'root' )
);
在浏览器中可以看到结果:
试试更复杂的例子,将多个组件组合起来:
function App() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById( 'root' )
);
在浏览器中可以看到结果:
类定义组件
类定义组件相对麻烦一点,我们通过继承React.Component
来定义一个组件:
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
Componet
为了实现类定义组件,我们需要定义一个Component
类:
class Component {}
state & props
通过继承React.Component
定义的组件有自己的私有状态state
,可以通过this.state
获取到。同时也能通过this.props
来获取传入的数据。
所以在构造函数中,我们需要初始化state
和props
// React.Component
class Component {
constructor( props = {} ) {
this.isReactComponent = true;
this.state = {};
this.props = props;
}
}
这里多了一个isReactComponent
属性,我们后面会用到。
setState
组件内部的state
和渲染结果相关,当state
改变时通常会触发渲染,为了让React知道我们改变了state
,我们只能通过setState
方法去修改它。我们可以通过Object.assign
来做一个简单的实现。
在每次更新state后,我们需要使用ReactDOM.render
重新渲染。
import ReactDOM from '../react-dom'
class Component {
constructor( props = {} ) {
// ...
}
setState( stateChange ) {
// 将修改合并到state
Object.assign( this.state, stateChange );
if ( this._container ) {
ReactDOM.render( this, this._container );
}
}
}
你可能听说过React的setState
是异步的,同时它有很多优化手段,这里我们暂时不去管它,在以后会有一篇文章专门来讲setState
方法。
让createElemen支持类定义组件
在js中,class
只是语法糖,它的本质仍然是一个函数。
所以第一步,我们需要在createElemen
方法中区分当前的节点是函数定义还是类定义。
类定义组件必须有render
方法,而通过class
定义的类,它的方法都附加在prototype
上。
所以只需要判断tag的prototype
中是否有render
方法,就能知道这个组件是函数定义还是类定义。
现在我们可以进一步修改React.createElement
:
function createElement( tag, attrs, ...children ) {
// 类定义组件
if ( tag.prototype && tag.prototype.render ) {
return new tag( attrs );
// 函数定义组件
} else if ( typeof tag === 'function' ) {
return tag( attrs || {} );
}
return {
tag,
attrs,
children
}
}
render
函数定义组件返回的是jsx,我们不需要做额外处理。但是类定义组件不同,它并不直接返回jsx。而是通过render
方法来得到渲染结果。
所以我们需要修改ReactDOM.render
方法。
修改之前我们先来回顾一下上一篇文章中我们对ReactDOM.render
的实现:
function render( vnode, container ) {
if ( vnode === undefined ) return;
// 当vnode为字符串时,渲染结果是一段文本
if ( typeof vnode === 'string' ) {
const textNode = document.createTextNode( vnode );
return container.appendChild( textNode );
}
const dom = document.createElement( vnode.tag );
if ( vnode.attrs ) {
Object.keys( vnode.attrs ).forEach( key => {
if ( key === 'className' ) key = 'class'; // 当属性名为className时,改回class
dom.setAttribute( key, vnode.attrs[ key ] )
} );
}
vnode.children.forEach( child => render( child, dom ) ); // 递归渲染子节点
return container.appendChild( dom ); // 将渲染结果挂载到真正的DOM上
}
在上文定义Component
时,我们添加了一个isReactComponent
属性,在这里我们需要用它来判断当前渲染的是否是一个组件:
function render( vnode, container ) {
if ( vnode.isReactComponent ) {
const component = vnode;
component._container = container; // 保存父容器信息,用于更新
vnode = component.render(); // render()返回的结果才是需要渲染的vnode
}
// 后面的代码不变...
}
现在我们的render方法就可以用来渲染组件了。
生命周期
上面的实现还差一个关键的部分:生命周期。
在React的组件中,我们可以通过定义生命周期方法在某个时间做一些事情,例如定义componentDidMount
方法,在组件挂载时会执行它。
但是现在我们的实现非常简单,还没有对比虚拟DOM的变化,很多生命周期的状态没办法区分,所以我们暂时只添加componentWillMount
和componentWillUpdate
两个方法,它们会在组件挂载之前和更新之前执行。
function render( vnode, container ) {
if ( vnode.isReactComponent ) {
const component = vnode;
if ( component._container ) {
if ( component.componentWillUpdate ) {
component.componentWillUpdate(); // 更新
}
} else if ( component.componentWillMount ) {
component.componentWillMount(); // 挂载
}
component._container = container; // 保存父容器信息,用于更新
vnode = component.render();
}
// 后面的代码不变...
}
渲染类定义组件
现在大部分工作已经完成,我们可以用它来渲染类定义组件了。 我们来试一试将刚才函数定义组件改成类定义:
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
class App extends React.Component {
render() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById( 'root' )
);
运行起来结果和函数定义组件完全一致:
再来尝试一个能体现出类定义组件区别的例子,实现一个计数器Counter
,每点击一次就会加1。
并且组件中还增加了两个生命周期函数:
class Counter extends React.Component {
constructor( props ) {
super( props );
this.state = {
num: 0
}
}
componentWillUpdate() {
console.log( 'update' );
}
componentWillMount() {
console.log( 'mount' );
}
onClick() {
this.setState( { num: this.state.num + 1 } );
}
render() {
return (
<div onClick={ () => this.onClick() }>
<h1>number: {this.state.num}</h1>
<button>add</button>
</div>
);
}
}
ReactDOM.render(
<Counter />,
document.getElementById( 'root' )
);
可以看到结果:
mount只在挂载时输出了一次,后面每次更新时会输出update
后话
至此我们已经从API层面实现了React的核心功能。但是我们目前的做法是每次更新都重新渲染整个组件甚至是整个应用,这样的做法在页面复杂时将会暴露出性能上的问题,DOM操作非常昂贵,而为了减少DOM操作,React又做了哪些事?这就是我们下一篇文章的内容了。
这篇文章的代码:https://github.com/hujiulong/simple-react/tree/chapter-2
从零开始实现React系列
React是前端最受欢迎的框架之一,解读其源码的文章非常多,但是我想从另一个角度去解读React:从零开始实现一个React,从API层面实现React的大部分功能,在这个过程中去探索为什么有虚拟DOM、diff、为什么setState这样设计等问题。
整个系列大概会有六篇左右,我每周会更新一到两篇,我会第一时间在github上更新,有问题需要探讨也请在github上回复我~
关注点star,订阅点watch
上一篇文章
实现一个简易的socket 连接池
egg HttpClient post PHP端收不到DATA 内容 GET正常
egg HttpClient post 后端收不到DATA 内容 GET正常 按照这个例子 PHP端打印 data 数据是空的 很无语 class NpmController extends Controller { async post() { const ctx = this.ctx; const result = await ctx.curl(‘https://httpbin.org/post’, { // 必须指定 method method: ‘POST’, // 通过 contentType 告诉 HttpClient 以 JSON 格式发送 contentType: ‘json’, data: { hello: ‘world’, now: Date.now(), }, // 明确告诉 HttpClient 以 JSON 格式处理返回的响应 body dataType: ‘json’, }); ctx.body = result.data; } }
前端和后端和服务器之间的关系,希望有人 可以帮我解惑!
我没有写过后端代码! 我所理解的整个项目是,将前端写好的页面扔到某个tomcat或者apache或者nginx启动的服务器下.然后通过地址就可以访问整个页面了,然后页面自己去调后台接口. 我现在写了node当后端写接口,node本身就起了一个服务器,我用前端就可以直接访问,但是我是按照SSR的方式写的,也就是后端直接渲染页面.如果不用SSR,我写的静态前端页面是不是也要扔到某个tomcat或者apache或者nginx启动的服务器下,然后再启动node服务器,这样前台的页面才能调用node所写的接口呢? 目前对整个项目很是迷惑! 希望有人可以帮我梳理一下整个项目的具体流程.非常感谢
node 数据库插入数据报read ECONNRESET错误,怎么解决
eggjs.org打不开,服务挂了吗
eggjs.org打不开,服务挂了吗
成为一名独立开发者一年了,有什么想问的么?
1、将近一年来只做了一个产品:
小专栏:专业的写作社区 https://xiaozhuanlan.com/explore
2、成为 2017 年度 Vue.js 唯一最大的赞助商,旁边的极客邦是最近挂上的;
3、社交少了,每隔一段时间就会莫名其妙的烦躁;
4、头发好像稀疏了一些呢。
5、小专栏倡导认真写作,如果你是这样的人,可以试试小专栏。(强行打了个广告)
6、依赖于美股活着,去年收益是 108%,大概五万多美金收入吧。
欢迎提问交流。
从 Graphql Schema 生成 Graphql query
源码: https://github.com/timqian/gql-generator
在为 graphql server 写 api test 的时候,每个 test 都需要手写 query。容易出现小错误,而且当 schema 有改变的时候,要去更新它们十分不便。
事实上,query 长什么样应该可以从 schema 里生成出来。
于是有了这个小工具
# Sample schema
type Query {
user(id: Int!): User!
}
type User {
id: Int!
username: String!
email: String!
createdAt: String!
}
# Sample query generated
query user($id: Int!) {
user(id: $id){
id
username
email
createdAt
}
}
具体介绍及使用方法见 github: https://github.com/timqian/gql-generator
codesandbox,这个在线代码平台是如何实现的?
假如我在codesandbox上新建了一个react项目,服务端上的体现是不是(比如nodejs)通过exec运行终端命令创建一个react项目,安装依赖,打包等等,最后返回一个链接http://xxxxx.codesandbox.io,我再通过更新iframe的src达到实时的效果?
一些javascript的使用技巧. https://github.com/1657413883/blog-notes/issues/90
一些es6的技巧,长更。
少了个分号,解构赋值为什么会失败?
let arr = []
let [a, b] = [2, 3]
arr.push(b);
[a, b] = [b, a + b]
console.log(a)
console.log(b) 把第三行的分号去掉,结果完全不同,为什么?不能理解。
html正则匹配求助
<html>
<body>
<div data-page='1'>666</div>
<h1 data-page='1'>777</h1>
<table data-page='1'>
<tr data-page='1'>
<td data-page='1'>
<div data-page='1'>ddwww</div>
</td>
<td data-page='1'>
<div data-page='1'>ddqqq</div>
</td>
</tr>
</table>
<div data-page='2'>666d</div>
<h1 data-page='2'>777w</h1>
</body>
</html>
有字符串如上
问:如何通过正则匹配出所有data-page=1的html字符串(table元素被匹配出来,但它内部的div不要被匹配出,因为获取到这些html字符串之后用作直接渲染,不然会有重复)
node本地运行正常,通过pm2部署到阿里云Ubuntu系统上报错
node本地运行正常,通过pm2部署到阿里云Ubuntu系统上报错,求大神指导 ────
【北京】初到科技|Node.js开发实习生|薪资5K
初到科技是一家移动互联网公司,致力于研发创新型产品和服务,帮助企业提升业务运营效率。社群管理工具软件“鹿小圈”是初到科技推出的第一款产品,欢迎加入团队一起创造!
希望本科或硕士在读的你
- 沉迷于 Node.js开发
- 玩过MongoDB, Redis, 懂Linux系统管理
- 爱专研最新技术,有想法
简历请发到david#luxiaoquan.com,一定要记得注明你在GitHub等开发社区的ID,还有展示你的创作或demo喔! 薪资:5K/月,工作地点:北京回龙观腾讯众创空间A座2层
egg定时任务
代码地址 https://github.com/MoceptWeb/egg-schedule看之前确定至少了解了一下egg的文档了, 不然看下面是浪费时间哈 有不对的地方请大家猛烈的提醒我吧
egg定时任务
很多情况我们需要去定时(或者就一次)执行某些脚本, 这个时候就可以利用egg的schedule功能了
怎样写
schedule
- {app_root}/app/schedule.js
// 通过 schedule 属性来设置定时任务的执行间隔等配置
static get schedule() {
return {
interval: '5m', // 5 min间隔
type: 'worker', // worker 类型:每台机器上只有一个 worker 会执行这个定时任务
};
}
async subscribe() {
this.ctx.logger.info('schedule updateOa2User begin')
const res = await this.ctx.service.portal.oa.updateOa2User();
this.ctx.logger.info('schedule updateOa2User finish')
}
启动时候自定决定启动哪些定时任务
在get schedule() 中配置immediate:配置了该参数为 true 时,这个定时任务会在应用启动并 ready 后立刻执行一次这个定时任务
app.beforeStart(async () => {
// await app.runSchedule('updateOa2User');updatePortal2User
await app.runSchedule('updatePortal2User');
});
service
- {app_root}/app/service/portal/oa.js
sqlserver 连接
// helper.js 统一连接一个sqlserver connection
const mssql = require('mssql')
let pool = await mssql.connect(config)
// app.js 连接错误的日志
mssql.on('error', err => {
console.log('database err', err)
app.logger.error(err);
// ... error handler
})
异步中的多个全部异步完成的操作
- 例如对一个数据进行一个个的异步操作, 但是最后获取全部异步后的结果
切忌不要在异步中做同步操作, 不然执行顺序很意外!!!
async updateUser(oaUser) {
const self = this;
let promiseAll = [];
// 设置promise对象,同步发请求
oaUser.forEach(oa => {
promiseAll.push(Promise.resolve(self.updateUserByName(oa)));
});
await Promise.all(promiseAll);
}
- {app_root}/app/service/portal/portal.js
异步中的多个全部异步完成的操作
如果需要一步步获取全部数据(这里是同步)后才要进行全部异步其他操作, 那么需要先将同步操作封装成promise支持异步操作
async readLine(target) {
let array = [];
const self = this;
const file = await new Promise(function (resolve, reject) {
lineReader.eachLine(target, function(line, last) {
// line.split(/\s/)[0]
let data = line.split(/\s/)
if(data[2]) {
array.push({
user_id: self.ctx.helper.trim(data[0], '"'),
mail: self.ctx.helper.trim(data[1], '"'),
user_center_id: self.ctx.helper.trim(data[2], '"'),
})
}
if(last) {
resolve(array)
}
})
})
return file;
}
// 使用
const fileData = await this.readLine(target);
let promiseAll = [];
fileData.forEach(portal => {
promiseAll.push(Promise.resolve(self.updateUserByPortalId(portal)));
});
await Promise.all(promiseAll);
mysql
- sql的占位符
const res = await conn.query('update t_user set mail = ? where user_id = ?', [oaUser.email, user.user_id]);
- sql的事务
async sqlBeginTransaction() {
const conn = await this.app.mysql.beginTransaction();
try {
const res = await conn.query('update t_user set mail = ? where user_id = ?', [oaUser.email, user.user_id]);
if(res && res.affectedRows === 1) {
await conn.commit();
} else if(res.affectedRows > 1){
// this.ctx.logger.error('')
await conn.rollback()
} else {
await conn.rollback()
}
} catch (err) {
// error, rollback
await conn.rollback(); // rollback call won't throw err
throw err;
}
}
extend
- {app_root}/app/extend/helper.js
统一封装各种数据的连接和error信息
test
- {app_root}/app/test/service/portal/portal.test.js 待完善
config
谨记更环境用的配置是不一样的
单元测试用的是 config.unittest.js
为什么这样写
TODO 分析源码
拓展和优化
- 将service中的纯数据库封装到model
- 基于redis的任务消息队列的优化 不需要人工去进行开启任务
- 编写egg-mssql连接sqlserver
koa-compose源码疑惑求助
提取了部分koa-compose代码,并作了修改,如下:
class koa {
constructor() {
this.middleware = [];
}
listen() {
const cb = this.compose(this.middleware);
const x = cb();
console.log(x);
}
use(fn) {
this.middleware.push(fn);
}
compose(middleware) {
return function () {
let index = -1;
return dispatch(0);
function dispatch(i) {
if (i <= index) return Promise.reject(new Error('next() called multiple times'));
index = i;
let fn = middleware[i];
if (!fn) return Promise.resolve();
try {
return Promise.resolve(fn(function next() {
return dispatch(i + 1);
}));
} catch (error) {
return Promise.reject(error);
}
}
};
}
}
const ko = new koa();
ko.use(async next => {
console.log(1);
await next();
console.log(5);
});
ko.listen();
output:
1
Promise { pending }
5
问题: 1、 为什么输出是1 promise { pending } 5 , 而不是1 5 promise 2、
return Promise.resolve(fn(function next() {
return dispatch(i + 1);
}));
能不能改为
return fn(function next() {
return dispatch(i + 1);
});
根据MDN : The Promise.resolve(value) method returns a Promise object that is resolved with the given value. if the value was a promise, that object becomes the result of the call to Promise.resolve; 那么既然这个fn是async函数,且async函数返回的是promise.resovle(),那岂不是可以去掉外面这一层Promise.resolve?
大佬们,need help help ~~~
morgan如何手动写入日志
express morgan只能自动生成日志,如果我想手动写入日志,该怎么做,求大神指点
node本地运行可以正常加载静态资源,部署到线上就无法加载静态资源,而且静态资源还被加载了两次
node本地运行可以正常加载静态资源,部署到线上就无法加载静态资源,而且静态资源还被加载了两次。求解