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

JavaScript EventEmitter

$
0
0

GitHub地址:JavaScript EventEmitter

博客地址:JavaScript EventEmitter

水平有限,欢迎批评指正

2个多月前把 Github 上的 eventemitter3Node.js下的事件模块 events的源码抄了一遍,才终于对 JavaScript事件有所了解。

上个周末花点时间根据之前看源码的理解自己用 ES6 实现了一个 eventemitter8,然后也发布到 npm上了,让我比较意外的是才发布两天在没有 readme介绍,没有任何宣传的情况下居然有45个下载,我很好奇都是谁下载的,会不会用。我花了不少时间半抄半原创的一个 JavaScript时间处理库 now.js (npm传送门:now.js) ,在我大力宣传的情况下,4个月的下载量才177。真是有心栽花花不开,无心插柳柳成荫

eventemitter8大部分是我根据看源码理解后写出来的,有一些方法如listenerslistenerCounteventNames一下子想不起来到底做什么,回头重查。测试用例不少是参考了 eventemitter3,在此对 eventemitter3的开发者们和 Node.js事件模块的开发者们表示感谢!

下面来讲讲我对 JavaScript事件的理解:

从上图可以看出,JavaScript事件最核心的包括事件监听 (addListener)、事件触发 (emit)、事件删除 (removeListener)

事件监听(addListener)

首先,监听肯定要有监听的目标,或者说是对象,那为了达到区分目标的目的,名字是不可少的,我们定义为 type

其次,监听的目标一定要有某种动作,对应到 JavaScript里实际上就是某种方法,这里定义为 fn

譬如可以监听一个 typeadd,方法为某一个变量 a值加1的方法 fn = () => a + 1的事件。如果我们还想监听一个使变量 b2的方法,我们第一反应可能是创建一个 typeadd2,方法 为 fn1 = () => b + 2的事件。你可能会想,这太浪费了,我能不能只监听一个名字,让它执行多于一个方法的事件。当然是可以的。

那么怎么做呢?

很简单,把监听的方法放在一个数组里,遍历数组顺序执行就可以了。以上例子变为 typeadd,方法为[fn, fn1]

如果要细分的话还可以分为可以无限次执行的事件 on和 只允许执行一次的事件 once (执行完后立即将事件删除)。待后详述。

事件触发(emit)

单有事件监听是不够的,必须要有事件触发才能算完成整个过程。emit就是去触发监听的特定 type对应的单个事件或者一系列事件。拿前面的例子来说单个事件就是去执行 fn,一系列事件就是去遍历执行 fnfn1

事件删除(removeListener)

严格意义上来讲,事件监听和事件触发已经能完成整个过程。事件删除可有可无。但很多时候,我们还是需要事件删除的。比如前面讲的只允许执行一次事件 once,如果不提供删除方法,很难保证你什么时候会再次执行它。通常情况下,只要是不再需要的事件,我们都应该去删除它。

核心部分讲完,下面简单的对 eventemitter8的源码进行解析。

源码解析

全部源码:

const toString = Object.prototype.toString;
const isType = obj => toString.call(obj).slice(8, -1).toLowerCase();
const isArray = obj => Array.isArray(obj) || isType(obj) === 'array';
const isNullOrUndefined = obj => obj === null || obj === undefined;

const _addListener = function(type, fn, context, once) {
  if (typeof fn !== 'function') {
    throw new TypeError('fn must be a function');
  }

  fn.context = context;
  fn.once = !!once;

  const event = this._events[type];
  // only one, let `this._events[type]` to be a function
  if (isNullOrUndefined(event)) {
    this._events[type] = fn;
  } else if (typeof event === 'function') {
    // already has one function, `this._events[type]` must be a function before
    this._events[type] = [event, fn];
  } else if (isArray(event)) {
    // already has more than one function, just push
    this._events[type].push(fn);
  }

  return this;
};

class EventEmitter {
  constructor() {
    if (this._events === undefined) {
      this._events = Object.create(null);
    }
  }

  addListener(type, fn, context) {
    return _addListener.call(this, type, fn, context);
  }

  on(type, fn, context) {
    return this.addListener(type, fn, context);
  }

  once(type, fn, context) {
    return _addListener.call(this, type, fn, context, true);
  }

  emit(type, ...rest) {
    if (isNullOrUndefined(type)) {
      throw new Error('emit must receive at lease one argument');
    }

    const events = this._events[type];

    if (isNullOrUndefined(events)) return false;

    if (typeof events === 'function') {
      events.call(events.context || null, rest);
      if (events.once) {
        this.removeListener(type, events);
      }
    } else if (isArray(events)) {
      events.map(e => {
        e.call(e.context || null, rest);
        if (e.once) {
          this.removeListener(type, e);
        }
      });
    }

    return true;
  }

  removeListener(type, fn) {
    if (isNullOrUndefined(this._events)) return this;

    // if type is undefined or null, nothing to do, just return this
    if (isNullOrUndefined(type)) return this;

    if (typeof fn !== 'function') {
      throw new Error('fn must be a function');
    }

    const events = this._events[type];

    if (typeof events === 'function') {
      events === fn && delete this._events[type];
    } else {
      const findIndex = events.findIndex(e => e === fn);

      if (findIndex === -1) return this;

      // match the first one, shift faster than splice
      if (findIndex === 0) {
        events.shift();
      } else {
        events.splice(findIndex, 1);
      }

      // just left one listener, change Array to Function
      if (events.length === 1) {
        this._events[type] = events[0];
      }
    }

    return this;
  }

  removeAllListeners(type) {
    if (isNullOrUndefined(this._events)) return this;

    // if not provide type, remove all
    if (isNullOrUndefined(type)) this._events = Object.create(null);

    const events = this._events[type];
    if (!isNullOrUndefined(events)) {
      // check if `type` is the last one
      if (Object.keys(this._events).length === 1) {
        this._events = Object.create(null);
      } else {
        delete this._events[type];
      }
    }

    return this;
  }

  listeners(type) {
    if (isNullOrUndefined(this._events)) return [];

    const events = this._events[type];
    // use `map` because we need to return a new array
    return isNullOrUndefined(events) ? [] : (typeof events === 'function' ? [events] : events.map(o => o));
  }

  listenerCount(type) {
    if (isNullOrUndefined(this._events)) return 0;

    const events = this._events[type];

    return isNullOrUndefined(events) ? 0 : (typeof events === 'function' ? 1 : events.length);
  }

  eventNames() {
    if (isNullOrUndefined(this._events)) return [];

    return Object.keys(this._events);
  }
}

export default EventEmitter;

代码很少,只有151行,因为写的简单版,且用的 ES6,所以才这么少;Node.js的事件和 eventemitter3可比这多且复杂不少,有兴趣可自行深入研究。

const toString = Object.prototype.toString;
const isType = obj => toString.call(obj).slice(8, -1).toLowerCase();
const isArray = obj => Array.isArray(obj) || isType(obj) === 'array';
const isNullOrUndefined = obj => obj === null || obj === undefined;

这4行就是一些工具函数,判断所属类型、判断是否是 null或者 undefined

constructor() {
  if (isNullOrUndefined(this._events)) {
    this._events = Object.create(null);
  }
}

创建了一个 EventEmitter类,然后在构造函数里初始化一个类的 _events属性,这个属性不需要要继承任何东西,所以用了 Object.create(null)。当然这里 isNullOrUndefined(this._events)还去判断了一下 this._events是否为 undefined或者 null,如果是才需要创建。但这不是必要的,因为实例化一个 EventEmitter都会调用构造函数,皆为初始状态,this._events应该是不可能已经定义了的,可去掉。

addListener(type, fn, context) {
  return _addListener.call(this, type, fn, context);
}

on(type, fn, context) {
  return this.addListener(type, fn, context);
}

once(type, fn, context) {
  return _addListener.call(this, type, fn, context, true);
}

接下来是三个方法 addListenerononce,其中 onaddListener的别名,可执行多次。once只能执行一次。

三个方法都用到了 _addListener方法:

const _addListener = function(type, fn, context, once) {
  if (typeof fn !== 'function') {
    throw new TypeError('fn must be a function');
  }

  fn.context = context;
  fn.once = !!once;

  const event = this._events[type];
  // only one, let `this._events[type]` to be a function
  if (isNullOrUndefined(event)) {
    this._events[type] = fn;
  } else if (typeof event === 'function') {
    // already has one function, `this._events[type]` must be a function before
    this._events[type] = [event, fn];
  } else if (isArray(event)) {
    // already has more than one function, just push
    this._events[type].push(fn);
  }

  return this;
};

方法有四个参数,type是监听事件的名称,fn是监听事件对应的方法,context俗称爸爸,改变 this指向的,也就是执行的主体。once是一个布尔型,用来标志是否只执行一次。 首先判断 fn的类型,如果不是方法,抛出一个类型错误。fn.context = context;fn.once = !!once把执行主体和是否执行一次作为方法的属性。const event = this._events[type]把该对应 type的所有已经监听的方法存到变量 event

// only one, let `this._events[type]` to be a function
if (isNullOrUndefined(event)) {
  this._events[type] = fn;
} else if (typeof event === 'function') {
  // already has one function, `this._events[type]` must be a function before
  this._events[type] = [event, fn];
} else if (isArray(event)) {
  // already has more than one function, just push
  this._events[type].push(fn);
}

return this;

如果 type本身没有正在监听任何方法,this._events[type] = fn直接把监听的方法 fn赋给 type属性 ;如果正在监听一个方法,则把要添加的 fn和之前的方法变成一个含有2个元素的数组 [event, fn],然后再赋给 type属性,如果正在监听超过2个方法,直接 push即可。最后返回 this,也就是 EventEmitter实例本身。

简单来讲不管是监听多少方法,都放到数组里是没必要像上面细分。但性能较差,只有一个方法时 key: fn的效率比 key: [fn]要高。

再回头看看三个方法:

addListener(type, fn, context) {
  return _addListener.call(this, type, fn, context);
}

on(type, fn, context) {
  return this.addListener(type, fn, context);
}

once(type, fn, context) {
  return _addListener.call(this, type, fn, context, true);
}

addListener需要用 call来改变 this指向,指到了类的实例。once则多传了一个标志位 true来标志它只需要执行一次。这里你会看到我在 addListener并没有传 false作为标志位,主要是因为我懒,但并不会影响到程序的逻辑。因为前面的 fn.once = !!once已经能很好的处理不传值的情况。没传值 !!oncefalse

接下来讲 emit

emit(type, ...rest) {
  if (isNullOrUndefined(type)) {
    throw new Error('emit must receive at lease one argument');
  }

  const events = this._events[type];

  if (isNullOrUndefined(events)) return false;

  if (typeof events === 'function') {
    events.call(events.context || null, rest);
    if (events.once) {
      this.removeListener(type, events);
    }
  } else if (isArray(events)) {
    events.map(e => {
      e.call(e.context || null, rest);
      if (e.once) {
        this.removeListener(type, e);
      }
    });
  }

  return true;
}

事件触发需要指定具体的 type否则直接抛出错误。这个很容易理解,你都没有指定名称,我怎么知道该去执行谁的事件。if (isNullOrUndefined(events)) return false,如果 type对应的方法是 undefined或者 null,直接返回 false。因为压根没有对应 type的方法可以执行。而 emit需要知道是否被成功触发。

接着判断 evnts是不是一个方法,如果是, events.call(events.context || null, rest)执行该方法,如果指定了执行主体,用 call改变 this的指向指向 events.context主体,否则指向 null,全局环境。对于浏览器环境来说就是 window。差点忘了 restrest是方法执行时的其他参数变量,可以不传,也可以为一个或多个。执行结束后判断 events.once,如果为 true,就用 removeListener移除该监听事件。

如果 evnts是数组,逻辑一样,只是需要遍历数组去执行所有的监听方法。

成功执行结束后返回 true

removeListener(type, fn) {
  if (isNullOrUndefined(this._events)) return this;

  // if type is undefined or null, nothing to do, just return this
  if (isNullOrUndefined(type)) return this;

  if (typeof fn !== 'function') {
    throw new Error('fn must be a function');
  }

  const events = this._events[type];

  if (typeof events === 'function') {
    events === fn && delete this._events[type];
  } else {
    const findIndex = events.findIndex(e => e === fn);

    if (findIndex === -1) return this;

    // match the first one, shift faster than splice
    if (findIndex === 0) {
      events.shift();
    } else {
      events.splice(findIndex, 1);
    }

    // just left one listener, change Array to Function
    if (events.length === 1) {
      this._events[type] = events[0];
    }
  }

  return this;
}

removeListener接收一个事件名称 type和一个将要被移除的方法 fnif (isNullOrUndefined(this._events)) return this这里表示如果 EventEmitter实例本身的 _eventsnull或者 undefined的话,没有任何事件监听,直接返回 this

if (isNullOrUndefined(type)) return this如果没有提供事件名称,也直接返回 this

if (typeof fn !== 'function') {
  throw new Error('fn must be a function');
}

fn如果不是一个方法,直接抛出错误,很好理解。

接着判断 type对应的 events是不是一个方法,是,并且 events === fn说明 type对应的方法有且仅有一个,等于我们指定要删除的方法。这个时候 delete this._events[type]直接删除掉 this._events对象里 type即可。

所有的 type对应的方法都被移除后。想一想 this._events[type] = undefineddelete this._events[type]会有什么不同?

差异是很大的,this._events[type] = undefined仅仅是将 this._events对象里的 type属性赋值为 undefinedtype这一属性依然占用内存空间,但其实已经没什么用了。如果这样的 type一多,有可能造成内存泄漏。delete this._events[type]则直接删除,不占内存空间。前者也是 Node.js事件模块和 eventemitter3早期实现的做法。

如果 events是数组,这里我没有用 isArray进行判断,而是直接用一个 else,原因是 this._events[type]的输入限制在 on或者 once中,而它们已经限制了 this._events[type]只能是方法组成的数组或者是一个方法,最多加上不小心或者人为赋成 undefinednull的情况,但这个情况我们也在前面判断过了。

因为 isArray这个工具方法其实运行效率是不高的,为了追求一些效率,在不影响运行逻辑情况下可以不用 isArray。而且 typeof events === 'function'typeof判断方法也比 isArray的效率要高,这也是为什么不先判断是否是数组的原因。用 typeof去判断一个方法也比 Object.prototype.toSting.call(events) === '[object Function]效率要高。但数组不能用 typeof进行判断,因为返回的是 object, 这众所周知。虽然如此,在我面试过的很多人中,仍然有很多人不知道。。。

const findIndex = events.findIndex(e => e === fn)此处用 ES6的数组方法 findIndex直接去查找 fnevents中的索引。如果 findIndex === -1说明我们没有找到要删除的 fn,直接返回 this就好。如果 findIndex === 0,是数组第一个元素,shift剔除,否则用 splice剔除。因为 shiftsplice效率高。

findIndex的效率其实没有 for循环去查找的高,所以 eventemitter8的效率在我没有做 benchmark之前我就知道肯定会比 eventemitter3效率要低不少。不那么追求执行效率时当然是用最懒的方式来写最爽。所谓的懒即正义。。。

最后还得判断移除 fnevents剩余的数量,如果只有一个,基于之前要做的优化,this._events[type] = events[0]把含有一个元素的数组变成一个方法,降维打击一下。。。

最后的最后 return this返回自身,链式调用还能用得上。

removeAllListeners(type) {

  if (isNullOrUndefined(this._events)) return this;

  // if not provide type, remove all

  if (isNullOrUndefined(type)) this._events = Object.create(null);

  const events = this._events[type];

  if (!isNullOrUndefined(events)) {

    // check if type is the last one

    if (Object.keys(this._events).length === 1) {

      this._events = Object.create(null);

    } else {

      delete this._events[type];

    }

  }

  return this;

}

removeAllListeners指的是要删除一个 type对应的所有方法。参数 type是可选的,如果未指定 type,默认把所有的监听事件删除,直接 this._events = Object.create(null)操作即可,跟初始化 EventEmitter类一样。

如果 events既不是 null且不是 undefined说明有可删除的 type,先用 Object.keys(this._events).length === 1判断是不是最后一个 type了,如果是,直接初始化 this._events = Object.create(null),否则 delete this._events[type]直接删除 type属性,一步到位。

最后返回 this

到目前为止,所有的核心功能已经讲完。

listeners(type) {
  if (isNullOrUndefined(this._events)) return [];

  const events = this._events[type];
  // use `map` because we need to return a new array
  return isNullOrUndefined(events) ? [] : (typeof events === 'function' ? [events] : events.map(o => o));
}

listenerCount(type) {
  if (isNullOrUndefined(this._events)) return 0;

  const events = this._events[type];

  return isNullOrUndefined(events) ? 0 : (typeof events === 'function' ? 1 : events.length);
}

eventNames() {
  if (isNullOrUndefined(this._events)) return [];

  return Object.keys(this._events);
}

listeners返回的是 type对应的所有方法。结果都是一个数组,如果没有,返回空数组;如果只有一个,把它的方法放到一个数组中返回;如果本来就是一个数组,map返回。之所以用 map返回而不是直接 return this._events[type]是因为 map返回一个新的数组,是深度复制,修改数组中的值不会影响到原数组。this._events[type]则返回原数组的一个引用,是浅度复制,稍不小心改变值会影响到原数组。造成这个差异的底层原因是数组是一个引用类型,浅度复制只是指针拷贝。这可以单独写一篇文章,不展开了。

listenerCount返回的是 type对应的方法的个数,代码一眼就明白,不多说。

eventNames这个返回的是所有 type组成的数组,没有返回空数组,否则用 Object.keys(this._events)直接返回。

最后的最后,export default EventEmitterEventEmitter导出。

结语

我是先看了两个库才知道怎么写的,其实最好的学习方法是知道 EventEmitter是干什么用的以后自己动手写,写完以后再和那些库进行对比,找出差距,修正再修正。

但也不是说先看再写没有收获,至少比只看不写和看都没看的有收获不是。。。

水平有限,代码错漏或者文章讲不清楚之处在所难免,欢迎大家批评指正。


[杭州] [数澜科技] 招聘 Node.js 开发工程师,这次并没有急的就像大四了还没找到女朋友,而是,男,朋,友

$
0
0

公司简介:

全称:杭州数澜科技有限公司 官网:https://www.dtwave.com/地址:杭州市余杭区未来科技城梦想小镇(没错就是那个帮你实现梦想的小镇^^) 发展阶段:2016年成立,目前人员规模在100~200,每年一轮融资,距离17年的Pre A轮已经过去一年了^^。

招聘职位:

资深 / 高级 / 初级 Node.js 开发工程师,可参考阿里的 P5 / P6段位 (人数:3 ~ 5 人)

职位描述:

  1. 设计和实施大数据应用的开发,构建基于Node.js技术栈的大数据应用开发体系。
  2. 面向开发者的基础设施构建,目前我司有如下基于Node.js构建的基础平台:API 管理系统、API Gateway、及轻应用托管平台。
  3. 有机会参与大数据相关的新领域探索,如大数据与区块链技术的结合,当然我们不会去发币,本质上还是做大数据相关的应用开发。

职位要求:

  1. 熟练掌握 JavaScript 语言特性,深刻理解 Node.js 后端技术栈的用武之地。
  2. 一年以上 Node.js 相关开发经验,熟练使用 Koa、Express 等 web 框架,熟悉常用 npm 包的应用场景。
  3. 基于 Node.js 开发过的产品至少使用到 MySQL、Redis、MongoDB、ZooKeeper 中的一项或多项。
  4. 熟悉 HTTP、TCP/IP 等网络协议。
  5. 对代码质量,技术文档有强迫症般的自我要求。
  6. 良好的沟通能力和团队协作能力,乐于探索和分享新技术。
加分项:
  1. 长期有任何形式的技术经验输出习惯,如:博客,开源项目,技术分享大会等。
  2. 熟悉其他优秀的编程语言,如 Python、Golang 等。
  3. 能熟练的使用Linux系统进行开发者运维。
  4. 熟悉 Docker 容器技术。
  5. 对大数据感兴趣。

薪资福利:

  1. 15-25K (这只是个参考区间,可能超出这个范围,我们认为能力+付出与回报是并存的)
  2. 六险一金,扁平管理,期权激励,弹性考勤,免费零食,强制锻炼

其他:

注意这个岗位是偏后端的Node.js工程师,如果收到前端工程师的简历可能得不到回复请见谅,全栈的会考虑。

简历投递:

zhigang.yzg@dtwave-inc.com

运用egg写一个任务,任务停止node服务也停止

$
0
0

通用需求

想要指定运行一个任务(不是定时任务, 人工可以重复任意时间执行, 但是需要用到egg相关的插件和service所以考虑使用egg),任务完成node此次服务就停止了 例如像原生的一样 node xxx.js 运行这个js任务, 任务完成node此次服务就停止了

实际业务需求

现在的具体场景是, 我需要运行一个查询某个数据库数据, 并配合某个java后端实现一定的数据处理功能; 就是一个脚本

请问各路大神怎么设计

vue 遇到的一个问题

SingularFM:程序员肉身翻墙那些事

$
0
0

很喜欢听播客, 很早就想做一档播客节目,终于和朋友一起达成了这个小目标: SingularFM 终于上线了。朋友们可以在下面的平台订阅收听我们的节目:

本期我们邀请到了三位嘉宾,他们分别在eBay, Google, KAYAC 工作过,且工作地点分别为硅谷,欧洲和日本。周彦俊目前是在eBay担任Tech Lead, 陈敏焕之前在Google Ireland Office担任工程师,目前在杭州创业。王心远之前在位于日本的KAYAC的公司担任前端工程师,目前在上海。让我们来听他们聊一聊他们关于海外工作机会,国外的工作环境和氛围,生活体验以及签证等事宜。

也欢迎大家订阅我们的微信公众号: wechat-qrcode.jpg

用node从网上下载图片,下好一张再请求下一张,程序老是卡住不动了,还是随机卡在某个图片的,没有报错

$
0
0

用node从网上下载图片,下好一张再请求下一张,程序老是卡住不动了,还是随机的,没有报错,但也没退出运行,就是不动了,打断点也不会走进来,是什么原因呢,下载的代码大概是这样

							function download() {
                                http.get(picUrl, function (res) {
                                    var code = res.statusCode;
                                    console.log(picUrl+code);
									
                                    if (code == "404") {
                                        console.log("跳过下载"+picPath);
                                        res.resume();
                                        next();
                                        return;
                                    }
									
                                    res.setEncoding('binary');//转成二进制
                                    var content = '';
                                    res.on('data', function (data) {
                                        content+=data;
                                    });
									
                                    res.on('end', function () {
                                        fs.writeFile(picPath,content,'binary', function (err) {
                                            if (err) {
                                                console.log("写入错误"+err.message);
                                            }
                                            next();
                                        });
                                    });
									
                                }).on("error",function(err) {
                                    console.log(err.message+"重新请求");
                                    setTimeout(function(){
                                        download();
                                    },2000);
                                });
							}

next()里会调用download(), 是内存原因还是网络原因,或者是其他原因呢

求教mongoose 插件 使用

$
0
0

mongoose-delete,mongoose-findorcreate,mongoose-friends,mongoose-query 最近开发遇到这几个模块 网上对这几个模块解释很少,所以请教一下 这几个模块在mongoose中 具体是做什么的

使用koa,nunjucks,sequlize整合了一个node mvc框架,这样可以更清楚的了解其他框架如egg的原理,上手和自定义其他集成框架会更容易

$
0
0

不是重复造轮子,但是自己动手整合对作为新手来说,可以更深层次的了解原理和框架的运作流程,这样再使用其他集成框架比如 egg,thinkjs会更容易,自己修改也比较容易,不会一头雾水。如果觉得有帮助,欢迎star。大神请批评指正,谢谢。 https://github.com/xmgtony/node_mvc


sequelize事务处理问题

$
0
0

目前需求是同时添加2条数据库记录,这2个记录分别添加到A数据库表和B数据库表中 我的A数据库表:

zzzzzzzzzdddddz.jpg

我的B数据库表:

yyyyyyyyyyy.jpg

我在网上查到如下的代码:

fffffffffff.jpg

我的代码像下面这么写,为什么添加的是正确数据他直接回滚了啊?麻烦大家了~~~~~

代码开始:不知道为什么前2行不自动识别为代码

sequelize.transaction(function (t) { return BoxInfoController.addInfo(function (a,result) {

    }, {transaction:t})
        .then(function(){
            return BoxInfoController.addOa(function (a,result) {}, {transaction:t})
        });
}).then(function (results){
    /* 操作成功,事务会自动提交 */
}).catch(function(err){
    /* 操作失败,事件会自动回滚 */
});

cnpm挂了?还是换了?

egg的controller页面渲染和vue的data数据冲突

$
0
0

egg的controller在渲染到页面上的时候是使用{{}}来渲染的,但是在使用vue的时候,data里的数据在页面上也是使用的{{}},这时页面上的vue渲染不出来 这时候vue失效,不渲染也不报错 微信图片_20180326131253.png这个是在controller里的pt在页面时可以渲染的 微信图片_20180326131348.png

求大神解答,我怎么弄

schemas中,如果我要加一个之前没有的字段,加了之后,怎么覆盖更新的问题

$
0
0

schemas中,如果我要加一个之前没有的字段,加了之后,怎么覆盖更新之前没有这个字段的项, 求解答,谢谢,

node流读取mysql里面的二进制数据

$
0
0

请问 express 框架里面怎么使用流读取mysql里面的二进制数据 并返回xml给前台

关于区块链以太猫我疑惑的是这么多猫的样貌,是怎么生成的呢?

$
0
0

肯定不可能设计一个个设计出来的吧。 挺想知道具体实现是怎么样的。 可能自己搜索功底不好,搜索出来的文章很少解释这个功能的。 大部分都把重点放在了“区块链”上了、 不过对于我一个前端来说,还是猫的样式比较吸引人。

vue.js 2 工程化实践 (流水线、webpack、vue-loader、Eslint、代码分割、karma、脚手架等)


[Vue warn]: Error in mounted hook: "TypeError: Cannot read property 'get' of undefined"

$
0
0

11dc289d0e69fd2259069c8927d7327.pngfa14185b780ee938468f920e28d5e61.png这是为啥?刚刚接触vue.js

node大文件上传不成功怎么解决?

$
0
0

将二进制 文件存入数据库时发生(nginx 大小以设置为100M node+express multer) 上传1.6M 文件时提示 read ECONNRESET 上传小于1K 文件时直接存入了数据库

招聘Noder 两名

casperjs 不能操作导入的数据?

$
0
0

我用 fs 读取一个 json,里面存的网址数据,一直没找到操作 json 的途径。 官网找到一个 each() 方法,可以读取数组,但是数组放在外部通过 fs 读取就又用不了了! 请问是这个工具本事就不适合导入数据,还是我 fs 没用好?

var fs = require(‘fs’) var casper = require(‘casper’).create()

var urls = [ “http://baidu.com”, ] casper.start().each(urls, function(self, url) { self.thenOpen(url, function() { //… }) }) casper.run(function(){ this.echo(‘End.’).exit() });

express框架默认首页301报错

$
0
0

微信截图_20180327001716.jpg微信截图_20180327001845.jpg

index.html放在www根目录下,为什么直接访问localhost:8080是报301 Moved Permanently错误,要加上localhost:8080/index.html才能正确显示,大神们请指教一下,万分感谢!!

Viewing all 14821 articles
Browse latest View live