NodeJs平台
第一页 1 2 下页 最后页 [ 显示模式: 摘要 | 列表 ]
C#代码
  1. mkdir -p /data/nodejs/htdocs/vue/nuxt/ycw  
  2. /data/nodejs/htdocs/vue/nuxt/ycw  
  3. wget http://192.168.180.91/CNMO/vue-nuxt2.tar  
  4. tar xvf vue-nuxt2.tar  
  5. npm i  
  6. npm run build  
  7. npm run start  
  8.  
  9. #########################################  
  10. vi node_modules/nuxt/bin/nuxt-start  
  11. host = "0.0.0.0"  
  12. #########################################  
  13. "config": {  
  14.     "nuxt": {  
  15.         "host""127.0.0.1",  
  16.         "port""3000"  
  17.     }  
  18. },  
  19. "config": {  
  20.     "nuxt": {  
  21.         "host""0.0.0.0",  
  22.         "port""3000"  
  23.     }  
  24. },  
  25.  
  26. #########################################  
  27. node node_modules/nuxt/bin/nuxt-start   
  28.  
  29. #########################################  
  30. 如果出现错误无法启动  
  31. ps | grep node  
  32. kill <id>  

 看了co的源码 比较难懂 了解了其原理后实现了一个最简版本https://github.com/yucong/simple-co 希望对想学习的tx有帮助~ yeild后面只支持thunk,co本身也是一个thunk 核心代码:

JavaScript代码
  1. function co(generator) {  
  2.   return function(fn) {  
  3.     var gen = generator();  
  4.     function next(err, result) {  
  5.         if(err){  
  6.             return fn(err);  
  7.         }  
  8.         var step = gen.next(result);  
  9.         if (!step.done) {  
  10.             step.value(next);  
  11.         } else {  
  12.             fn(null, step.value);  
  13.         }  
  14.     }  
  15.     next();  
  16.    }  
  17. }  

用法:

JavaScript代码
  1. var co = require('./co');  
  2. // wrap the function to thunk  
  3. function readFile(filename) {  
  4.     return function(callback) {  
  5.         require('fs').readFile(filename, 'utf8', callback);  
  6.     };  
  7. }  
  8.   
  9. co(function * () {  
  10.     var file1 = yield readFile('./file/a.txt');  
  11.     var file2 = yield readFile('./file/b.txt');  
  12.   
  13.     console.log(file1);  
  14.     console.log(file2);  
  15.     return 'done';  
  16. })(function(err, result) {  
  17.     console.log(result)  
  18. });  

会打印出: content in a.txt content in b.txt done

 

下面做个简单对比:
传统方式,sayhello是一个异步函数,执行helloworld会先输出"world"再输出"hello"

JavaScript代码
  1. function sayhello() {  
  2.     return Promise.resolve('hello').then(function(hello) {  
  3.         console.log(hello);  
  4.     });  
  5. }  
  6. function helloworld() {  
  7.     sayhello();  
  8.     console.log('world');  
  9. }  
  10. helloworld();  

输出

 

JavaScript代码
  1. "world"  
  2. "hello"  

co 的方式,会先输出"hello"再输出"world"

JavaScript代码
  1. function co(gen) {  
  2.     var it = gen();  
  3.     var ret = it.next();  
  4.     ret.value.then(function(res) {  
  5.         it.next(res);  
  6.     });  
  7. }  
  8. function sayhello() {  
  9.     return Promise.resolve('hello').then(function(hello) {  
  10.         console.log(hello);  
  11.     });  
  12. }  
  13. co(function *helloworld() {  
  14.     yield sayhello();  
  15.     console.log('world');  
  16. });  

输出

JavaScript代码
  1. "hello"  
  2. "world"  

 

消除回调金字塔

假设sayhello/sayworld/saybye是三个异步函数,用真正的 co 模块就可以这么写:

JavaScript代码
  1. var co = require('co');  
  2. co(function *() {  
  3.     yield sayhello();  
  4.     yield sayworld();  
  5.     yield saybye();  
  6. });  

输出

JavaScript代码
  1. "hello"  
  2. "world"  
  3. "bye"  

 

搞定 koa 之generator 与 co

[不指定 2017/05/17 17:29 | by 刘新修 ]

 koa 是由 tj大神利用 generator 开发的 web 框架。要理解 koa,首先要先了解 generator 与 co。作为搞定 koa 的第一篇,我们便谈谈这个。文章首发在boke.io

generator介绍

 function* Gene(){

    yield 1;

    yield 2;

}

var gene = Gene();

console.log(gene.next());//{ value: 1, done: false }

console.log(gene.next());//{ value: 2, done: false }

console.log(gene.next());//{ value: undefined, done: true }

console.log(gene.next());//Error: Generator has already finished  经@Ralph-Wang提醒从 v0.11.13 开始不抛错了,返回{ value: undefined, done: true }

从上面我们可以看到

generator 的定义和函数类似,只是在 function 后面多了一个*

调用generator 和调用函数一样,只是不像函数立即执行,而是会生成一个对象

generator 生成的对象存在一个 next 函数,调用 next 会返回 yield运算的结果对象,并停止。再次调用会在下一个 yield 处停止。

当所有的 yield 被执行完,调用 next 函数会返回{ value: undefined, done: true }。再次调用会报错

generator 与异步

 看完 generator 的介绍,你心里回想这跟异步有毛关系?不着急听我接着说

串行请求两个网页的代码

var request = require('request');

var a = {};

var b = {};

request('http://www.google.com', function (error, response, body) {

    if (!error && response.statusCode == 200) {

        a.response = response;

        a.body = body;

        request('http://www.yahoo.com', function (error, response, body) {

            if (!error && response.statusCode == 200) {

                b.response = response;

                b.body = body;

            }

        });

    }

});

我们再看看最终我们是如何利用 generator请求网页的

co(function *(){

  var a = yield request('http://google.com');

  var b = yield request('http://yahoo.com');

  console.log(a[0].statusCode);

  console.log(b[0].statusCode);

})()

上面的代码可以看到,co 里面传入了一个 generator,里面用 yield 调用异步函数。从逻辑上看,里面的异步被变成了同步。

co 到底有什么魔法?可以把我们从异步中解救出来?

想想 generator 的特性,当我们执行第一个 next 的时候,会调用第一个 request,此时我们去调用 request 的逻辑,然后把 generator 的实例穿进去。当第一个 request 逻辑完成时,在调用这个 generator 的 next,这样就到了第二个 yield 的逻辑了。当然这需要一些逻辑的封装,也就是 co 了。

根据上面的分析,我们大概可以写出下面的代码

function co(Gene){

    //先实例化一下

    var gene = Gene();

    //如果存在 next 函数

    if(gene.next){

        var fun = gene.next();//把异步函数返回过来,好继续封装

        //fun 处理完,再调用 gene.next()

        //...

    }

}

从上面的代码可以看出

yield 后面的内容需要返回一个异步函数,这样我们才可进一步封装异步处理的逻辑。

fun 需要可以传入参数,这样才可以处理多个异步

需要一个递归调用,这样才可以持续调用 next,同时根据 next 返回对象中的done属性停止逻辑

需要考虑错误处理

 今天就到这里,下一篇详细讲一下 co 的源码

看了co的源码 比较难懂 了解了其原理后实现了一个最简版本https://github.com/yucong/simple-co 希望对想学习的tx有帮助~ yeild后面只支持thunk,co本身也是一个thunk

核心代码:

function co(generator) {

  return function(fn) {

  var gen = generator();

  function next(err, result) {

    if(err){

      return fn(err);

    }

    var step = gen.next(result);

    if (!step.done) {

      step.value(next);

    } else {

      fn(null, step.value);

    }

  }

  next();

   }

}

用法:


var co = require('./co');

// wrap the function to thunk

function readFile(filename) {

    return function(callback) {

      require('fs').readFile(filename, 'utf8', callback);

    };

}


co(function * () {

    var file1 = yield readFile('./file/a.txt');

    var file2 = yield readFile('./file/b.txt');


    console.log(file1);

    console.log(file2);

    return 'done';

})(function(err, result) {

    console.log(result)

});

会打印出:

content in a.txt

content in b.txt

done

================================

generator_co:  http://liuxinxiu.com/generator_co/

使用npm install -g 'xxx' 之后仍然报
Cannot find module 'xxx' 错误,可以通过设置环境变量来解决;

export NODE_PATH=/usr/local/lib/node_modules/  
echo $NODE_PATH  

Error: Cannot find module 'ftp'

 V8 引擎概览

 
Google V8 引擎使用 C++ 代码编写,实现了 ECMAScript 规范的第五版,可以运行在所有的主流
 
操作系统中,甚至可以运行在移动终端 ( 基于 ARM 的处理器,如 HTC G7 等 )。V8 最早被开发用以嵌入到 Google 的开源浏览器 Chrome 中,但是 V8 是一个可以独立的模块,完全可以嵌入您自己的应用,著名的 Node.js( 一个异步的服务器框架,可以在服务端使用 JavaScript 写出搞笑的网络服务器 ) 就是基于 V8 引擎的。
 
和其他 JavaScript 引擎一样,V8 会编译 / 执行 JavaScript 代码,管理内存,负责垃圾回收,与宿主语言的交互等。V8 的垃圾回收器采用了众多技术,使得其运行效率大大提高。通过暴露宿主对象 ( 变量,函数等 ) 到 JavaScript,JavaScript 可以访问宿主环境中的对象,并在脚本中完成对宿主对象的操作。
 
V8 引擎基本概念
 
图 1. V8 引擎基本概念关系图 ( 根据 Google V8 官方文档 ) 
handle
 
handle 是指向对象的指针,在 V8 中,所有的对象都通过 handle 来引用,handle 主要用于 V8 的垃圾回收机制。
 
在 V8 中,handle 分为两种:持久化 (Persistent)handle 和本地 (Local)handle,持久化 handle 存放在堆上,而本地 handle 存放在栈上。这个与 C/C++ 中的堆和栈的意义相同 ( 简而言之,堆上的空间需要开发人员自己申请,使用完成之后显式的释放;而栈上的为自动变量,在退出函数 / 方法之后自动被释放 )。持久化 handle 与本地 handle 都是 Handle 的子类。在 V8 中,所有数据访问均需要通过 handle。需要注意的是,使用持久化 handle 之后,需要显式的调用 Dispose() 来通知垃圾回收机制。
 
作用域 (scope)
 
scope 是 handle 的集合,可以包含若干个 handle,这样就无需将每个 handle 逐次释放,而是直接释放整个 scope。
 
在使用本地 handle 时,需要声明一个 HandleScope 的实例,scope 是 handle 的容器,使用 scope,则无需依次释放 handle。
 
 HandleScope handle_scope; 
 Local<ObjectTemplate> temp; 
 
上下文 (context)
 
context 是一个执行器环境,使用 context 可以将相互分离的 JavaScript 脚本在同一个 V8 实例中运行,而互不干涉。在运行 JavaScript 脚本是,需要显式的指定 context 对象。
 
数据及模板
 
由于 C++ 原生数据类型与 JavaScript 中数据类型有很大差异,因此 V8 提供了 Data 类,从 JavaScript 到 C++,从 C++ 到 JavaScrpt 都会用到这个类及其子类,比如:
 
 Handle<Value> Add(const Arguments& args){ 
   int a = args[0]->Uint32Value(); 
   int b = args[1]->Uint32Value(); 
 
   return Integer::New(a+b); 
 } 
 
Integer 即为 Data 的一个子类。
 
V8 中,有两个模板 (Template) 类 ( 并非 C++ 中的模板类 ):对象模板 (ObjectTempalte) 和函数模板 (FunctionTemplate),这两个模板类用以定义 JavaScript 对象和 JavaScript 函数。我们在后续的小节部分将会接触到模板类的实例。通过使用 ObjectTemplate,可以将 C++ 中的对象暴露给脚本环境,类似的,FunctionTemplate 用以将 C++ 函数暴露给脚本环境,以供脚本使用。
 
初始化 context 是使用 V8 引擎所必需的过程,代码非常简单:
 
 Persistent<Context> context = Context::New(); 
V8 引擎使用示例
 
有了上面所述的基本概念之后,我们来看一下一个使用 V8 引擎的应用程序的基本流程:
 
创建 HandleScope 实例
创建一个持久化的 Context
进入 Context
创建脚本字符串
创建 Script 对象,通过 Script::Compile()
执行脚本对象的 Run 方法
获取 / 处理结果
显式的调用 Context 的 Dispose 方法
基本代码模板
 
 
清单 1. 代码模块 
        
 #include <v8.h> 
 
 using namespace v8; 
 
 int main(int argc, char *argv[]) { 
   // 创建一个句柄作用域 ( 在栈上 ) 
   HandleScope handle_scope; 
 
   // 创建一个新的上下文对象
   Persistent<Context> context = Context::New(); 
 
   // 进入上一步创建的上下文,用于编译执行 helloworld 
   Context::Scope context_scope(context); 
 
   // 创建一个字符串对象,值为'Hello, Wrold!', 字符串对象被 JS 引擎
   // 求值后,结果为'Hello, World!'
   Handle<String> source = String::New("'Hello' + ', World!'"); 
 
   // 编译字符串对象为脚本对象
   Handle<Script> script = Script::Compile(source); 
 
   // 执行脚本,获取结果
   Handle <Value> result = script->Run(); 
 
   // 释放上下文资源
   context.Dispose(); 
 
   // 转换结果为字符串
   String::AsciiValue ascii(result); 
 
   printf("%s\n", *ascii); 
 
   return 0; 
 } 
 
以上代码为一个使用 V8 引擎来运行脚本的基本模板,可以看到,开发人员可以很容易的在自己的代码中嵌入 V8 来处理 JavaScript 脚本。我们在下面小节中详细讨论如何在脚本中访问 C++ 资源。
 
使用 C++ 变量
 
在 JavaScript 与 V8 间共享变量事实上是非常容易的,基本模板如下:
 
 
清单 2. 共享变量 
        
 static type xxx; 
 
 static Handle<Value> xxxGetter( 
   Local<String> name, 
   const AccessorInfo& info){ 
 
   //code about get xxx 
 } 
 
 static void xxxSetter( 
   Local<String> name, 
   Local<Value> value, 
   const AccessorInfo& info){ 
 
   //code about set xxx 
 } 
 
首先在 C++ 中定义数据,并以约定的方式定义 getter/setter 函数,然后需要将 getter/setter 通过下列机制公开给脚本:
 
 global->SetAccessor(String::New("xxx"), xxxGetter, xxxSetter); 
 
其中,global 对象为一个全局对象的模板:
 
 Handle<ObjectTemplate> global = ObjectTemplate::New(); 
 
下面我们来看一个实例:
 
 
清单 3. 实例 1 
        
 static char sname[512] = {0}; 
 
 static Handle<Value> NameGetter(Local<String> name, 
     const AccessorInfo& info) { 
   return String::New((char*)&sname,strlen((char*)&sname)); 
 } 
 
 static void NameSetter(Local<String> name, 
     Local<Value> value, 
     const AccessorInfo& info) { 
   Local<String> str = value->ToString(); 
   str->WriteAscii((char*)&sname); 
 } 
 
定义了 NameGetter, NameSetter 之后,在 main 函数中,将其注册在 global 上:
 
 // Create a template for the global object. 
 Handle<ObjectTemplate> global = ObjectTemplate::New(); 
 
 //public the name variable to script 
 global->SetAccessor(String::New("name"), NameGetter, NameSetter); 
 
在 C++ 中,将 sname 的值设置为”cpp”:
 
 //set sname to "cpp" in cpp program 
 strncpy(sname, "cpp", sizeof(sname)); 
 
然后在 JavaScript 中访问该变量,并修改:
 
 print(name); 
 
 //set the variable `name` to "js"
 name='js'; 
 print(name); 
 
运行结果如下:
 
 cpp 
 js 
 
运行脚本,第一个 print 调用会打印在 C++ 代码中设置的 name 变量的值:cpp,然后我们在脚本中修改 name 值为:js,再次调用 print 函数则打印出设置后的值:js。
 
调用 C++ 函数
 
在 JavaScript 中调用 C++ 函数是脚本化最常见的方式,通过使用 C++ 函数,可以极大程度的增强 JavaScript 脚本的能力,如文件读写,网络 / 数据库访问,图形 / 图像处理等等,而在 V8 中,调用 C++ 函数也非常的方便。
 
在 C++ 代码中,定义以下原型的函数:
 
 Handle<Value> function(constArguments& args){ 
   //return something 
 } 
 
然后,再将其公开给脚本:
 
 global->Set(String::New("function"),FunctionTemplate::New(function)); 
 
同样,我们来看两个示例:
 
 
清单 4. 实例 2 
        
 Handle<Value> Add(const Arguments& args){ 
   int a = args[0]->Uint32Value(); 
   int b = args[1]->Uint32Value(); 
 
   return Integer::New(a+b); 
 } 
 
 Handle<Value> Print(const Arguments& args) { 
   bool first = true; 
   for (int i = 0; i < args.Length(); i++) { 
     HandleScope handle_scope; 
     if (first) { 
       first = false; 
     } else { 
       printf(" "); 
    
     String::Utf8Value str(args[i]); 
     const char* cstr = ToCString(str); 
     printf("%s", cstr); 
  
   printf("\n"); 
   fflush(stdout); 
   return Undefined(); 
 } 
 
函数 Add 将两个参数相加,并返回和。函数 Print 接受任意多个参数,然后将参数转换为字符串输出,最后输出换行。
 
 global->Set(String::New("print"), FunctionTemplate::New(Print)); 
 global->Set(String::New("add"), FunctionTemplate::New(Add)); 
 
我们定义以下脚本:
 
 var x = (function(a, b){ 
   return a + b;   
 })(12, 7); 
 
 print(x); 
 
 //invoke function add defined in cpp 
 var y = add(43, 9); 
 print(y); 
 
运行结果如下:
 
 19 
 52 
 
使用 C++ 类
 
如果从面向对象的视角来分析,最合理的方式是将 C++ 类公开给 JavaScript,这样可以将 JavaScript 内置的对象数量大大增加,从而尽可能少的使用宿主语言,而更大的利用动态语言的灵活性和扩展性。事实上,C++ 语言概念众多,内容繁复,学习曲线较 JavaScript 远为陡峭。最好的应用场景是:既有脚本语言的灵活性,又有 C/C++ 等系统语言的效率。使用 V8 引擎,可以很方便的将 C++ 类”包装”成可供 JavaScript 使用的资源。
 
我们这里举一个较为简单的例子,定义一个 Person 类,然后将这个类包装并暴露给 JavaScript 脚本,在脚本中新建 Person 类的对象,使用 Person 对象的方法。
 
首先,我们在 C++ 中定义好类 Person:
 
 
清单 5. 定义类 
        
 class Person { 
 private: 
   unsigned int age; 
   char name[512]; 
 
 public: 
   Person(unsigned int age, char *name) { 
     this->age = age; 
     strncpy(this->name, name, sizeof(this->name)); 
  
 
   unsigned int getAge() { 
     return this->age; 
  
 
   void setAge(unsigned int nage) { 
     this->age = nage; 
  
 
   char *getName() { 
     return this->name; 
  
 
   void setName(char *nname) { 
     strncpy(this->name, nname, sizeof(this->name)); 
  
 }; 
 
Person 类的结构很简单,只包含两个字段 age 和 name,并定义了各自的 getter/setter. 然后我们来定义构造器的包装:
 
 Handle<Value> PersonConstructor(const Arguments& args){ 
   Handle<Object> object = args.This(); 
   HandleScope handle_scope; 
   int age = args[0]->Uint32Value(); 
 
   String::Utf8Value str(args[1]); 
   char* name = ToCString(str); 
 
   Person *person = new Person(age, name); 
   object->SetInternalField(0, External::New(person)); 
   return object; 
 } 
 
从函数原型上可以看出,构造器的包装与上一小节中,函数的包装是一致的,因为构造函数在 V8 看来,也是一个函数。需要注意的是,从 args 中获取参数并转换为合适的类型之后,我们根据此参数来调用 Person 类实际的构造函数,并将其设置在 object 的内部字段中。紧接着,我们需要包装 Person 类的 getter/setter:
 
 Handle<Value> PersonGetAge(const Arguments& args){ 
   Local<Object> self = args.Holder(); 
   Local<External> wrap = Local<External>::Cast(self->GetInternalField(0)); 
 
   void *ptr = wrap->Value(); 
 
   return Integer::New(static_cast<Person*>(ptr)->getAge()); 
 } 
 
 Handle<Value> PersonSetAge(const Arguments& args) 
 { 
   Local<Object> self = args.Holder(); 
   Local<External> wrap = Local<External>::Cast(self->GetInternalField(0)); 
 
   void* ptr = wrap->Value(); 
 
   static_cast<Person*>(ptr)->setAge(args[0]->Uint32Value()); 
   return Undefined(); 
 } 
 
而 getName 和 setName 的与上例类似。在对函数包装完成之后,需要将 Person 类暴露给脚本环境:
 
首先,创建一个新的函数模板,将其与字符串”Person”绑定,并放入 global:
 
 Handle<FunctionTemplate> person_template = FunctionTemplate::New(PersonConstructor); 
 person_template->SetClassName(String::New("Person")); 
 global->Set(String::New("Person"), person_template); 
 
然后定义原型模板:
 
 Handle<ObjectTemplate> person_proto = person_template->PrototypeTemplate(); 
 
 person_proto->Set("getAge", FunctionTemplate::New(PersonGetAge)); 
 person_proto->Set("setAge", FunctionTemplate::New(PersonSetAge)); 
 
 person_proto->Set("getName", FunctionTemplate::New(PersonGetName)); 
 person_proto->Set("setName", FunctionTemplate::New(PersonSetName)); 
 
最后设置实例模板:
 
 Handle<ObjectTemplate> person_inst = person_template->InstanceTemplate(); 
 person_inst->SetInternalFieldCount(1); 
 
随后,创建一个用以测试的脚本:
 
 //global function to print out detail info of person 
 function printPerson(person){ 
    print(person.getAge()+":"+person.getName()); 
 } 
 
 //new a person object 
 var person = new Person(26, "juntao"); 
 
 //print it out 
 printPerson(person); 
 
 //set new value 
 person.setAge(28); 
 person.setName("juntao.qiu"); 
 
 //print it out 
 printPerson(person); 
 
运行得到以下结果:
 
 26:juntao 
 28:juntao.qiu 
简单示例
 
在这一小节中,我们将编写一个简单的桌面计算器:表达式求值部分通过 V8 引擎来进行,而流程控制部分则放在 C++ 代码中,这样可以将表达式解析等复杂细节绕开。同时,我们还得到了一个额外的好处,用户在脚本中可以自定义函数,从而可以在计算器中定义自己的运算规则。
 
桌上计算器
 
计算器程序首先进入一个 MainLoop,从标准输入读取一行命令,然后调用 V8 引擎去求值,然后将结果打印到控制台,然后再进入循环:
 
 
清单 6. 桌面计算器示例 
        
 void MainLoop(Handle<Context> context) { 
   while(true) { 
     char buffer[1024] = {0}; 
     printf("$ "); 
     char *str = fgets(buffer, sizeof(buffer), stdin); 
     if(str == NULL) { 
       break; 
    
     HandleScope handle_scope; 
     ExecuteString(String::New(str), String::New("calc"), true); 
  
 } 
 
在 main 函数中设置全局对象,创建上下文对象,并进入 MainLoop:
 
 int main(int argc, char *argv[]){ 
   HandleScope handle_scope; 
 
   // Create a template for the global object. 
   Handle<ObjectTemplate> global = ObjectTemplate::New(); 
 
   // Expose the local functions to script 
   global->Set(String::New("load"), FunctionTemplate::New(Load)); 
   global->Set(String::New("print"), FunctionTemplate::New(Print)); 
   global->Set(String::New("quit"), FunctionTemplate::New(Quit)); 
 
   // Create a new execution environment containing the built-in 
   // functions 
   Handle<Context> context = Context::New(NULL, global); 
 
   // Enter the newly created execution environment. 
   Context::Scope context_scope(context); 
 
   // Enter main loop 
   MainLoop(context); 
 
   V8::Dispose(); 
 
 
   return 0; 
 } 
 
在 main 函数中,为脚本提供了三个函数,load 函数用以将用户指定的脚本加载进来,并放入全局的上下文中一边引用,print 函数用以打印结果,而 quit 提供用户退出计算器的功能。
 
测试一下:
 
 $ 1+2 
 3 
 
 $ (10+3)/(9.0-5) 
 3.25 
 
 $ typeof print 
 function 
 
 $ typeof non 
 undefined 
 
 // 自定义函数
 $ function add(a, b){return a+b;} 
 $ add(999, 2323) 
 3322 
 
 // 查看 print 标识符的内容
 $ print 
 function print() { [native code] } 
 
load 函数提供了用户自定义函数的功能,将脚本文件作为一个字符串加载到内存,然后对该字符串编译,求值,并将处理过的脚本对象放入当前 context 中,以便用户使用。
 
 Handle<Value> Load(const Arguments& args){ 
   if(args.Length() != 1){ 
     return Undefined(); 
  
 
   HandleScope handle_scope; 
   String::Utf8Value file(args[0]); 
 
   Handle<String> source = ReadFile(*file); 
   ExecuteString(source, String::New(*file), false); 
 
   return Undefined(); 
 } 
 
而 ExecuteString 函数,负责将字符串编译运行:
 
 bool ExecuteString(Handle<String> source, 
           Handle<Value> name, 
           bool print_result) 
 { 
   HandleScope handle_scope; 
   TryCatch try_catch; 
   Handle<Script> script = Script::Compile(source, name); 
   if (script.IsEmpty()) { 
     return false; 
   } else { 
     Handle<Value> result = script->Run(); 
     if (result.IsEmpty()) { 
       return false; 
     } else { 
       if (print_result && !result->IsUndefined()) { 
         String::Utf8Value str(result); 
         const char* cstr = ToCString(str); 
         printf("%s\n", cstr); 
      
       return true; 
    
  
 } 
 
将下列内容存入一个文本文件,并命令为 calc.js:
 
 function sum(){ 
   var s = 0; 
   for(var i = 0; i < arguments.length; i++){ 
     s += arguments[i]; 
  
   return s; 
 } 
 
 function avg(){ 
   var args = arguments; 
   var count = args.length; 
   var sum = 0; 
   for(var i = 0; i < count; i++){ 
     sum += args[i]; 
  
   return sum/count; 
 } 
 
然后在计算器中测试:
 
 // 此时 sum 符号位定义
 $ typeof sum 
 undefined 
 
 // 加载文件,并求值
 $ load("calc.js") 
 
 // 可以看到,sum 的类型为函数
 $ typeof sum 
 function 
 
 $ sum(1,2,3,4,5,6,7,8,9) 
 45 
结束语
 
使用 V8 引擎,可以轻松的将脚本的好处带进 C++ 应用,使得 C++ 应用更具灵活性,扩展性。我们在文中讨论了基本的模板,如何使用 C++ 变量,函数,以及类。最后的实例中给出了一个计算器的原型。由于 V8 的设计原则,开发人员可以快速的将其嵌入到自己的应用中,并且无需太过担心脚本语言的执行效率。

支持来自任何域名的Post请求,验证请求参数必须有: [name、gender] 否则直接JSON返回错误!

JavaScript代码
  1. var http=require('http');  
  2. function server(){  
  3.     this.exec=function(route,req,res){  
  4.     var _self=this;  
  5.     /********** 获取客户端数组中的部分数据可向后多取 **********/  
  6.     this.arrv=function(o,n,p){  
  7.        var i,y,d,p;y=false;d='';  
  8.        for(i in o){  
  9.          if(o[i]==n&&p==undefined){i++;return o[i]};  
  10.          if(o[i]==n&&p!=undefined&&!isNaN(p)||y){  
  11.              var s;s=i;  
  12.              p=Number(p);  
  13.              if(p>0){  
  14.                 p--;s++;d+=o[s];y=true;  
  15.              }  
  16.              else{  
  17.                 return d;  
  18.              }  
  19.          }  
  20.        }  
  21.     };  
  22.     /********** 请求后端时可获取单个header头内地信息 **********/  
  23.     this.getReqHeaders=function(n){  
  24.         if(typeof(n)=='undefined'){  
  25.           console.log('getReqHeaders(n)变量没有定义!');  
  26.         }else{  
  27.             return _self.arrv(req.rawHeaders,String(n));  
  28.         }  
  29.     };  
  30.     /******** 获取客户端请求的header整体头部信息 ********/  
  31.     this.rawHeaders=function(state){  
  32.         var json={};  
  33.         if(!state){  
  34.             for(var i=0;i<req.rawHeaders.length;i++){  
  35.                 var s,s=i;  
  36.                 if(i%2==0){  
  37.                     s++;json[req.rawHeaders[i]]=req.rawHeaders[s];  
  38.                 }  
  39.             }  
  40.         }  
  41.         if(state){  
  42.             for(var i=0;i<req.rawHeaders.length;i++){  
  43.                 json[i]=req.rawHeaders[i];  
  44.             }  
  45.         }  
  46.         var jsonStr=JSON.stringify(json);//结果:"{'1':'a','2':'b','3':'c'}"  
  47.         var jsonObj=JSON.parse(jsonStr); //结果:[object Object]  
  48.         return jsonObj;  
  49.     };  
  50.   
  51.   
  52.     //接收参数 ------ sreq.on("data",function(data){});接收html中ajax传递的参数  
  53.     req.on("data",function(data){  
  54.   
  55.         /********* 打印提示||接受数据并反向代理到后端 *********/  
  56.         console.log("\n--->>\nReq.on('data')",req.method.toUpperCase()+" Use Proxy!");  
  57.   
  58.         /********* 使用代理||这里Post请求体是回调data *********/  
  59.         send(route,req,res,data);  
  60.     });  
  61.   
  62.     /********** 判断是GET请求类型||也可以代理给后端处理 **********/  
  63.     if(req.method.toUpperCase()=="GET"){  
  64.   
  65.         var params=[];  
  66.         //params=url.parse(request.url,true).query;  
  67.         //params['fruit']=compute(params);  
  68.         res.writeHeader(200,{  
  69.             "Content-type":"text/html; charset=utf-8"  
  70.         });  
  71.         res.write('<h1>It is forbidden for the URL request!</h1>');  
  72.         res.write('<hr><address>NodeJs/'+process.version);  
  73.         res.write(' at '+req.headers.host.split(':')[0]);  
  74.         res.write(' Port '+req.headers.host.split(':')[1]+'</address>');  
  75.         res.end();  
  76.   
  77.     }  
  78.     /********* 如有req.on("data",function(data){});就跳过了以下方法 ******/  
  79.     /********* 判断是POST请求类型||提交不走代理此方法是本机处理回调 **********/  
  80.     else if(req.method.toUpperCase()=='POSTo'){  
  81.   
  82.         var postData="";  
  83.         /********** 读取Post提交的数据 **********/  
  84.         req.addListener("data",function(data){  
  85.             postData+=data;  
  86.         });  
  87.   
  88.         /********** 数据读取完毕就会执行的监听 *********/  
  89.         req.addListener("end",function(){  
  90.            /********* 定义Post请求主体 *********/  
  91.            var query=require('querystring').parse(postData);  
  92.   
  93.            /************** 判断如果有POST过来数据 *************/  
  94.            if(query.name&&query.gender){  
  95.               console.log('Start a request...');  
  96.   
  97.                 var origin=_self.arrv(req.rawHeaders,'Origin');  
  98.                 console.log("origin:"+typeof req.rawHeaders+'---')  
  99.   
  100.   
  101.               /********** 代理转发至php,跨域全放开让后端去匹配验证 **********/  
  102.               //res.setHeader('Access-Control-Allow-Origin','*');  
  103.               //res.setHeader('Access-Control-Allow-Headers','X-Requested-With');  
  104.               //res.setHeader('Access-Control-Allow-Methods','GET,POST,PUT,DELETE,OPTIONS');  
  105.               //send(route,req,res,query);  
  106.            }  
  107.            /****** 判断结束 ******/  
  108.         });  
  109.   
  110.     }  
  111.   
  112.   
  113.     /******** 请求后端接口 ********/  
  114.     function send(route,req,res,data){  
  115.   
  116.         /***************************************
  117.         var data={
  118.               name:'liuxinxiu',
  119.               gender:"male",
  120.               time:new Date().getTime()
  121.         };
  122.         ****************************************/  
  123.         //data=require('querystring').stringify(data);  
  124.         var rawHeaders=_self.rawHeaders();  
  125.   
  126.         /********** 判断一个对象是哪中类型的对象 **********/  
  127.         function isArray(obj){  
  128.               return Object.prototype.toString.call(obj)==='[object Array]';  
  129.         };  
  130.   
  131.         /********* 打印Request Headers **********/  
  132.         console.log("\n--->>\nRequest Headers:",req.rawHeaders);  
  133.   
  134.         /***** 向后端发送请求基本的设置 *****/  
  135.         var options={  
  136.               port: 80,  
  137.               host:"code.liuxinxiu.com",  
  138.               path:'/php/Post/CORS_PHP.php',  
  139.               method: 'POST',  
  140.               headers:{  
  141.                   'Content-Type':_self.getReqHeaders('Content-Type'),  
  142.                   'Content-Length':data.length,  
  143.                   'Origin':_self.getReqHeaders('Origin'),  
  144.                   'User-Agent':_self.getReqHeaders('User-Agent')  
  145.               }  
  146.         };  
  147.   
  148.         /***** 如果header整体替换就会乱码 *****/  
  149.         //options.headers=_self.rawHeaders();  
  150.   
  151.   
  152.         var request=http.request(options,function(result){  
  153.               console.log("\n--->>\nstatusCode:",result.statusCode);  
  154.               console.log("\n--->>\nResponse Headers:",result.headers);  
  155.   
  156.               if(result){  
  157.                  var content_type,origin,x_powered_by,server;  
  158.                      //content_type=origin=x_powered_by=server=undefined;  
  159.                      content_type=result.headers['content-type'];  
  160.                      origin=result.headers['access-control-allow-origin'];  
  161.                      x_powered_by=result.headers['x-powered-by'];  
  162.                      server=result.headers['server'];  
  163.   
  164.                      /********** 判断分别加入包含服务器错误页 **********/  
  165.                      if(origin!=undefined&&result.statusCode==200){  
  166.                         res.setHeader('Access-Control-Allow-Origin',origin);  
  167.                      }  
  168.                      if(content_type!=undefined){  
  169.                         res.setHeader('Content-Type',content_type);  
  170.                      }  
  171.                      if(x_powered_by!=undefined){  
  172.                         res.setHeader('X-Powered-By',x_powered_by);  
  173.                      }  
  174.                      if(server!=undefined){  
  175.                         res.setHeader('Server',server);  
  176.                      }  
  177.   
  178.               }  
  179.               /********** 有异常的请求需要问题定位 **********/  
  180.               if(result.statusCode<200||result.statusCode>206){  
  181.               }  
  182.   
  183.               /********** 接受数据数据监听 **********/  
  184.               var _data='';  
  185.               result.on('data',function(chunk){  
  186.                 _data+=chunk;  
  187.               });  
  188.   
  189.               /********** 结束接受数据监听 *********/  
  190.               result.on('end',function(){  
  191.                    console.log("\n--->>\nresult:",_data);  
  192.                    res.write(_data);  
  193.                    res.end();  
  194.               });  
  195.   
  196.         /******** request逻辑完成 ********/  
  197.         });  
  198.   
  199.   
  200.         request.on('error',function(e) {  
  201.               console.log('problem with request: ' + e.message);  
  202.         });  
  203.   
  204.         //request.write(data);  
  205.         request.write(data+"\n");  
  206.         request.end();  
  207.   
  208.     }  
  209.   
  210.   
  211.   
  212.   /****** 内部结束 ******/  
  213.   }  
  214. }  
  215. module.exports=new server();  

NodeJS数据接口(Post请求直接代理至后端PHP): http://liuxinxiu.com:3000/CORS_Node_Proxy/

PHP数据接口(验证跨域提交白名单外返回错误):  http://code.liuxinxiu.com/php/Post/CORS_PHP.php

Tags: , ,

使用NodeJs实现CORS 跨域资源共享,可传参origin通过限制,代码如下:

JavaScript代码
  1. var http=require('http');  
  2. var origin=require('./origin'); //调用白名单列表  
  3.   
  4. function CORS_Node(){  
  5.     this.exec=function(route,req,res){  
  6.     var _self=this;  
  7.   
  8.     /************** 获取客户端origin的域名 **************/  
  9.     origin.req=req;                //白名单设置置请求头  
  10.     origin.yes=origin.listV();     //是否在名单|true  
  11.     origin.name=origin.getName();  //客户端ORIGIN  
  12.   
  13.   
  14.     /******** 先纠正参数,参数正确后匹配白名单 ********/  
  15.   
  16.   
  17.     /********** 判断是GET请求类型 **********/  
  18.     if(req.method.toUpperCase()=="GET"){  
  19.   
  20.         var params=[];  
  21.         //params=url.parse(request.url,true).query;  
  22.         //params['fruit']=compute(params);  
  23.         res.writeHeader(200,{  
  24.             "Content-type":"text/html; charset=utf-8"  
  25.         });  
  26.         res.write('<h1>It is forbidden for the URL request!</h1>');  
  27.         res.write('<hr><address>NodeJs/'+process.version);  
  28.         res.write(' at '+req.headers.host.split(':')[0]);  
  29.         res.write(' Port '+req.headers.host.split(':')[1]+'</address>');  
  30.         res.end();  
  31.   
  32.     }  
  33.     /********* 判断是POST请求类型 **********/  
  34.     else if(req.method.toUpperCase()=='POST'){  
  35.   
  36.         var postData="";  
  37.         /********** 读取Post提交的数据 **********/  
  38.         req.addListener("data",function(data){  
  39.             postData+=data;  
  40.         });  
  41.   
  42.         /********** 数据读取完毕就会执行的监听 *********/  
  43.         req.addListener("end",function(){  
  44.            /********* 定义Post请求主体 *********/  
  45.            var query=require('querystring').parse(postData);  
  46.   
  47.   
  48.            /********** 给客户端返回数据自造JSON **********/  
  49.            function getJson(status){  
  50.                 if(isNaN(status))status=-1;  
  51.                 else{status=Number(status)}  
  52.                 var _drr='{'  
  53.                       +'"status":"1",'  
  54.                       +'"name":"'+query.name+'",'  
  55.                       +'"gender":"'+query.gender+'"}';  
  56.                 var _arr='{'  
  57.                       +'"status":1,'  
  58.                       +'"url":"http://www.liuxinxiu.com/",'  
  59.                       +'"dataList":'  
  60.                       +'{'  
  61.                       +'"siteId":101,'  
  62.                       +'"title":"我的博客||Node Server",'  
  63.                       +'"images":"http://www.liuxinxiu.com/upload/2016/08/10/moren.gif",'  
  64.                       +'"indexNum":"10",'  
  65.                       +'"pageNum":"100000",'  
  66.                       +'"tagNum":"22",'  
  67.                       +'"linkType":"linkTaobao",'  
  68.                       +'"publishTime":"23:15:30"'  
  69.                       +'}'  
  70.                       +'}';  
  71.                 this._dr=JSON.parse(_drr);this._ar=JSON.parse(_arr);  
  72.                 this._dt=this._dr;this._dt.getUser=this._ar;  
  73.            }  
  74.   
  75.            var errStr={"status":-1,"info":"Request Error"};  
  76.   
  77.            /********** 先初步设置头信息,跨域全放开稍后再进行匹配验证 **********/  
  78.            var content_type,content_length,  
  79.            content_type='application/json; charset=utf-8';  
  80.            content_length=Buffer.byteLength(res,'utf8');  
  81.            res.setHeader('Access-Control-Allow-Origin','*');  
  82.            res.setHeader('Access-Control-Allow-Headers','X-Requested-With');  
  83.            res.setHeader('Access-Control-Allow-Methods','GET,POST,PUT,DELETE,OPTIONS');  
  84.            res.setHeader('Content-Type',content_type);  
  85.            res.setHeader('Server','NodeJs/'+process.version);  
  86.   
  87.   
  88.            /************** 判断如果有POST过来规范的数据 *************/  
  89.            if(query.name&&query.gender){  
  90.                 //调用数据  
  91.                 getJson(1);  
  92.   
  93.                 /******** 匹配客户端域名是否在数组列表中 ******/  
  94.                 //if(_self.in_array(origin,allow_origin)){  
  95.                 if(origin.yes){  
  96.                      res.setHeader('Access-Control-Allow-Origin',origin.name);  
  97.                      res.end(JSON.stringify(_dt));  
  98.                 }  
  99.                 else{  
  100.                      /******** 如有设置就取设置URL返回头信息 ********/  
  101.                      if(query.origin){  
  102.                         res.setHeader('Access-Control-Allow-Origin',query.origin);  
  103.                         res.end(JSON.stringify(_dt));  
  104.                      }  
  105.                      /******** 没设置URL就返回无权限错误信息  ********/  
  106.                      else{  
  107.                         errStr.info="You don't have permission to submit!";  
  108.                         res.setHeader('Access-Control-Allow-Origin',origin.name);
  109.                         res.end(JSON.stringify(errStr));
  110.                      }
  111.                 }
  112.            }
  113.            /********* 没有所匹配的POST提交数据||都要设头返回信息 ********/
  114.            else{
  115.                 /******** 其他POST参数的提交  ********/
  116.                 if(query){
  117.                     res.writeHeader(res.statusCode,{
  118.                         "Access-Control-Allow-Origin":origin.name,
  119.                         "Access-Control-Allow-Headers":"X-Requested-With",
  120.                         "Access-Control-Allow-Methods":"GET,POST,PUT,DELETE,OPTIONS",
  121.                         "Content-type":"application/json; charset=utf-8"
  122.                     });
  123.                     var err={status:-1,info:"Syntax error in parameters or arguments."};
  124.                     res.end(JSON.stringify(err));
  125.                 }
  126.                 else{
  127.                     res.writeHeader(res.statusCode,{
  128.                         "Content-type": "text/html; charset=utf-8"
  129.                     });
  130.                     res.end('It is forbidden for the URL request!');  
  131.                 }  
  132.   
  133.             }  
  134.             /****** 判断结束 ******/  
  135.         });  
  136.   
  137.     }  
  138.   
  139.   
  140.   /****** 内部结束 ******/  
  141.   }  
  142. }  
  143. module.exports=new CORS_Node();  

NodeJs数据提交接口地址 (不允许使用GET访问) :http://liuxinxiu.com:3000/CORS_Node/

注意:NodeJs Server 是本站的测试环境,有时候因需要会临时关闭,该地址仅供测试,如您有需要请自建环境~

配合HTML代码如下:

XML/HTML代码
  1. <!DOCTYPE html>  
  2. <html>  
  3. <head>  
  4.     <meta charset="utf-8"/>    
  5.     <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />  
  6.     <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"/>  
  7.     <meta name="format-detection"content="telephone=no">  
  8.     <meta name="apple-mobile-web-app-capable" content="yes" />  
  9.     <meta name="apple-mobile-web-app-status-bar-style" content="black" />  
  10.     <style>body,html {background:#fff;font-family: "Lucida Grande",Calibri,Arial;font-size:12pt;color: #333;background: #f8f8f8;text-align:center;}*{margin:0;padding:0;}h1{line-height:1.6em;font-size:24px;text-indent:.5em;padding-top:.6em}i{line-height:2em;font-size:18px;color:#999;}.line{height:10px;border-bottom:1px solid #ccc;font-size:0;overflow:hidden;}</style>  
  11.     <title>跨域测试</title>  
  12. </head>  
  13. <body>  
  14. <h1 id="show"></h1>  
  15. <input type="button" value="Click me" onclick="msg()" />  
  16.   
  17. </body>  
  18. <script src="//code.jquery.com/jquery-1.11.3.min.js"></script>  
  19. <script type='text/javascript'>  
  20. /********** 获取URL参数 **********/  
  21. function getQueryString(name){  
  22. var reg=new RegExp("(^|&)"+name+"=([^&]*)(&|$)","i");  
  23. var r=window.location.search.substr(1).match(reg);  
  24. if (r!=null) return unescape(r[2]); return null;  
  25. }  
  26. var _n=getQueryString('n');  
  27. var _url=getQueryString('url');  
  28. var _name=getQueryString('name');  
  29. var _gender=getQueryString('gender');  
  30. var _origin=getQueryString('origin');  
  31. var _error=getQueryString('error');  
  32. console.log('origin:'+_origin+' —— name:'+_name+' —— gender:'+_gender);  
  33.   
  34. window.onload=function(){  
  35.     if(_n=='php'){  
  36.     location.href='http://'  
  37.              +location.host  
  38.              +location.pathname  
  39.              +'?origin=http://'+location.host;  
  40.     }else if(_n=='node'){  
  41.         location.href='http://'  
  42.                      +location.host  
  43.                      +location.pathname  
  44.                      +'?origin=http://'+location.host+'&'  
  45.              +'url=http://liuxinxiu.com:3000/CORS_Node/'  
  46.   
  47.     }else if(_n=='proxy'){  
  48.         location.href='http://'  
  49.                      +location.host  
  50.                      +location.pathname  
  51.                      +'?origin=http://'+location.host+'&'  
  52.                      +'url=http://liuxinxiu.com:3000/CORS_Node_Proxy/'  
  53.       
  54.     }else if(_n=='test'){  
  55.         location.href='http://test1.liuxinxiu.com/php/Interface/html/server.html'  
  56.              +'?origin=http://test1.liuxinxiu.com&'  
  57.              +'url=http://liuxinxiu.com:3000/CORS_Node_Proxy/&'  
  58.              +'error=1'  
  59.     }  
  60. }  
  61.   
  62. /********** 发起Ajax请求 **********/  
  63. function msg(){  
  64.     /******* 动态切换提交数据 *******/  
  65.     if(_origin&&!_error){  
  66.     if(_name&&_gender){  
  67.         var data={name:_name,gender:_gender,origin:_origin};  
  68.     }  
  69.     else{  
  70.         var data={name:"xiaoming",gender:"male",origin:_origin};  
  71.     }  
  72.     }  
  73.     else if(_error==null){  
  74.     var data={name:"xiaoming",gender:"male"};  
  75.     }  
  76.     else if(_error){  
  77.         var data={xxx:111};  
  78.     }  
  79.     /******* 动态设置提交URL *******/  
  80.     if(_url){  
  81.     var urlPath=_url;  
  82.     }  
  83.     else{  
  84.     var urlPath='http://code.liuxinxiu.com/php/Post/CORS_PHP.php';  
  85.     }  
  86.     $.ajax({  
  87.        type:'post',  
  88.        url:urlPath,  
  89.        data:data,  
  90.        cache:false,  
  91.        dataType:'json',  
  92.        success:function(data){  
  93.            if(data.name){  
  94.         document.getElementById("show").innerHTML=data.name+' '+data.gender;  
  95.            }  
  96.        else if(data.status!=1){  
  97.         document.getElementById("show").innerHTML=data.info;  
  98.        }  
  99.        },    
  100.        error:function(){  
  101.            console.log("请求错误//")  
  102.        }  
  103.     });  
  104. };  
  105.   
  106. /***********************************************************************************************  
  107. $.post("http://www.server.com/server.php",{name:"fdipzone",gender:"male"}).done(function(data){    
  108.     document.getElementById("show").innerHTML=data.name+' '+data.gender;  
  109. });  
  110. **********************************************************************************************/  
  111. </script>  
  112. </html>  

HTML访问地址 (测试跨域) ==> http://test1.liuxinxiu.com/php/Interface/html/server.html?n=node

HTML访问地址 (非法参数) ==> http://test1.liuxinxiu.com/php/Interface/html/server.html?error=node

注意:本站的 NodeJs Server 是学习测试环境,有临时关闭的可能,建议在本地环境测试~

Tags: , ,

NodeJs JSONP数据接口定义

[不指定 2016/09/06 18:09 | by 刘新修 ]
JavaScript代码
  1. var http = require('http');    
  2. var urllib = require('url');    
  3.     
  4. var port = 10011;    
  5. var data = {'name': 'jifeng', 'company': 'taobao'};    
  6.     
  7. http.createServer(function(req, res){    
  8.   var params = urllib.parse(req.url, true);    
  9.   console.log(params);    
  10.   if (params.query && params.query.callback) {    
  11.     //console.log(params.query.callback);    
  12.     var str =  params.query.callback + '(' + JSON.stringify(data) + ')';//jsonp    
  13.     res.end(str);    
  14.   } else {    
  15.     res.end(JSON.stringify(data));//普通的json    
  16.   }        
  17. }).listen(port, function(){    
  18.   console.log('server is listening on port ' + port);      
  19. })    

自建Node路由Jsonp接口访问地址:http://liuxinxiu.com:3000/jsonp?Jsoncallback=2016&&name=liuxinxiu

JavaScript代码
  1. function jsonp(){  
  2.     this.exec=function(route,req,res){  
  3.        var data1={key:'value',hello:'world',name:'刘新修'};  
  4.        var data2={'name':'wangjiang','company':'taobao'};  
  5.        console.log(req+'---'+route)  
  6.        res.statusCode=200;  
  7.        //res.setHeader('content-type','application/json;charset=utf-8');  
  8.        //res.setHeader('Content-Type','text/html');  
  9.        //req.write(data);  
  10.        //req.end();  
  11.   
  12.        var params=url.parse(req.url,true);  
  13.        console.log(params);  
  14.        if(params.query&¶ms.query.Jsoncallback){  
  15.            console.log(params.query.Jsoncallback);  
  16.            var str=params.query.Jsoncallback+'('+JSON.stringify(data1)+')';//jsonp  
  17.            res.end(str);  
  18.        }  
  19.        else{  
  20.            res.writeHead(200,{'Content-Type':'application/json'});  
  21.            res.end(JSON.stringify(data1));      //普通的json  
  22.        }  
  23.   
  24.    }  
  25. }  
  26. module.exports=new jsonp();  

自建Node路由Jsonp接口访问地址:http://liuxinxiu.com:3000/Jsoncallback?Jsoncallback=1&name=Jack

JavaScript代码
  1. function jsoncallback(){  
  2.     this.exec=function(route,req,res){  
  3.   
  4.         /******** 处理请求判断用作JSONP ********/  
  5.         var params=url.parse(req.url,true);  
  6.   
  7.         /********** 造数据要Obj不要String **********/  
  8.         var str2={dataList:[{name:"Jack",age:28,sex:1},{name:'Tony',age:30,sex:0}]};  
  9.   
  10.         /********** 映射数据源无为undefined **********/  
  11.         var output=2016+'('+JSON.stringify(str2)+')';  
  12.         var result=params.query.Jsoncallback+'('+JSON.stringify(str2)+')';  
  13.   
  14.         /************* 定义头部信息及编码规范 *******************/  
  15.         res.writeHead(200,{'content-Type':'application/json;charset=utf-8'});  
  16.   
  17.         /************* 判断出来相应返回的结果 ********************/  
  18.         if(params.query&¶ms.query.Jsoncallback){  
  19.             res.write(result);  
  20.             res.end();  
  21.         }  
  22.         else{  
  23.             res.write(output);  
  24.             res.end();  
  25.         }  
  26.     }  
  27. };  
  28. module.exports=new jsoncallback();  
Tags: ,

几个模板引擎的对比

[不指定 2016/09/05 23:56 | by 刘新修 ]

渲染html到页面中,在koa中可以这么干:

JavaScript代码
  1. app.get('/',function*(){  
  2.     this.body = "<p>'+title+'</p>";  
  3. })  

但问题很大,模板混入到js逻辑了,非常不利于维护,基于 mvc 模式,我们需要将html模板抽离到 view 中方便维护,这时候我们就需要一款模板渲染引擎。

为什么选择xtemplate

适用于 koa 的模板引擎选择非常多,比如 jadeejsnunjucksxtemplate 等。

为什么选择 xtemplate 呢?

上手难度

jade 无疑是最独特,上手难度最高,特别是将 html 转成 jade 风格的代码,需要一些成本和适应,特别是空格敏感,经常引起模块渲染报错:

JavaScript代码
  1. body  
  2.     h1 Jade - node template engine  
  3.     #container.col  
  4.       if youAreUsingJade  
  5.         p You are amazing  
  6.       else  
  7.         p Get on it!  

但 jade 在 express 中拥有广泛的群众基础,所以从 express 转到 koa,jade 是个不错的选择,但我一直认为 jade 不是最佳的模板选择。

ejs、nunjucks、xtemplate 的上手难度差不多,但ejs的扩展写法有些诡异,特别是 filter 的写法:

JavaScript代码
  1. <p><%=: users | first | capitalize %></p>  

xtemplate 相对于 nunjucks 的优势是,模板的逻辑写法体验更接近js(ejs的逻辑表达也很接近js),上手更为简单,来看下同样一个 if 判断的写法差异。

xtemplate:

JavaScript代码
  1. {{#if(variable===0)}}  
  2.   It is true      
  3. {{/if}}  

nunjucks:

JavaScript代码
  1. {% if variable == 0 %}  
  2.   It is true  
  3. {% endif %}  

所以上手难度:xtemplate < nunjucks < ejs < jade 。

功能的强大度

四款模板引擎必备几样功能都具备:变量、逻辑表达式、循环、layout、include、宏、扩展等。

nunjucks 无疑是功能最全面的模板引擎,xtemplate 拥有 nunjucks 大部分的特性,除了filter,但 xtemplate 拥有非常强悍的拓展性:

JavaScript代码
  1. xtpl.addCommand('money',function(scope, option){  
  2.     var money = option.params[0];  
  3.     if(typeof money !== 'number') return '';  
  4.     //金额除以100(接口返回的金额都是以分为单位,转成元)  
  5.     var s = Number(money)/100;  
  6.     return s;  
  7. })  

模板中使用:

JavaScript代码
  1. {{money(10000)}}  

nunjucks 与 xtemplate 都拥有实用的宏定义功能:

JavaScript代码
  1. {{#macro("test","param", default=1)}}  
  2.     param is {{param}} {{default}}  
  3. {{/macro}}  

模板中使用:

JavaScript代码
  1. {{macro("test","2")}}  

输出内容:

JavaScript代码
  1. param is 2 1  

jade 中的 Mixins,ejs 中的 function 也可以实现类似的抽取公用html代码块的目的 。

如果真要分个高下:nunjucks > xtemplate > jade > ejs 。

是否支持前后端混用

jade 直接淘汰,相信在前端js领域一般不会选择 jade 来渲染。

nunjucks 也使用的比较少(ejs其实也少),更多人会选择使用 handlebars 。

xtemplate 目前在阿里的系统中前后端混用中已经得到论证,节约了模板前后端转换的时间。

事实上是否支持前后端混用不是决定性因素,特别是在 angularjs 盛行的年代。

性能考量

它们都很快,其实性能都不是问题,真要较真的话,xtemplate 会更优秀些,可以看 xtemplate benchmark

个人觉得性能不是决定性因素。

基于上述几点考虑,推荐使用 xtemplate 。

Tags: ,
第一页 1 2 下页 最后页 [ 显示模式: 摘要 | 列表 ]