zoukankan      html  css  js  c++  java
  • Node.js的基本使用

    1、node的基本介绍

    常说的 node 和 nodejs 没有什么区别,它们就是同一个东西。

    Node 是一个基于Chrome V8引擎的JavaScript运行环境,一个可以让 JavaScript 运行在服务端的开发平台。它让 JavaScript 成为与PHP、Python、Perl、Ruby 等服务端语言平起平坐的脚本语言。

    Node是一个基于Chrome JavaScript运行时建立的平台, 用于方便地搭建响应速度快、易于扩展的网络应用。简单地说,Node.js 就是运行在服务端的 JavaScript。

    所以 Node.js 可以说是一个运行平台,也可以说是一种语言。

    1.1、什么是V8引擎

    JavaScript一种解释性脚本语言,是一种动态类型、弱类型、基于原型的语言,内置支持类型。它的解释器被称为JavaScript引擎,为浏览器的一部分。

    V8 引擎就是JavaScript运行的解释器,他是Google开发的,作为 chrome 浏览器的 js 执行解释器,因为性能十分优秀,速度非常快,所以后来慢慢的也就被广泛的使用,不仅仅局限在chrome中解释js了,下文的nodejs就是用到了v8引擎。

    2、nodejs 的安装

    node.js 的安装可参考:http://www.runoob.com/nodejs/nodejs-install-setup.html。一般来说,nodejs 在安装后会自动配置好环境变量。

    在安装完后,可以运行 node -v 或者 node --version 命令来查看 node 是否安装成功,有输出版本即证明已安装成功:

    3、如何运行一个node程序

    运行一个 node 程序非常简单。在安装完 node 之后,我们可以编写一个 JS 文件,比如:

    //helloworld.js 文件
    console.log("Hello World");

    然后用 node 命令执行该文件:

    node helloworld.js     //将输出 Hello World

    我们也可以打开终端,键入 node 后进入命令交互模式,可以输入一条代码语句后立即执行并显示结果,例如:

    $ node
    > console.log('Hello World!');
    Hello World!

    4、node.js创建一个 http 服务

    如果我们使用 PHP 来编写后端的代码时,需要 Apache 或者 Nginx 的HTTP服务器,并配上 mod_php5 模块和 php-cgi 。从这个角度看,整个"接收HTTP请求并提供Web页面"的需求根本不需要PHP来处理,而是由服务器来处理。

    不过对Node.js来说,概念完全不一样了。使用Node.js时,我们不仅仅在实现一个应用,同时还实现了整个HTTP服务器。事实上,我们的Web应用以及对应的Web服务器基本上是一样的。

    node.js 并不需要额外搭建一个服务器比如 Apache、tomcat 这些,在我们创建 node.js 应用时,它同时也搭建了一个 http 服务器。所以说我们不仅仅实现了一个应用,同时也实现了整个 http 服务器。

    另外,node.js不仅仅可以创建http服务,也可以充当一个客户端,即向http服务器发出请求。

    4.1、如何创建一个http服务

    首先我们新建一个 server.js 文件。

    先使用 require 指令来载入 http 模块,并将实例化的 HTTP 赋值给变量 http。http 模块是 node.js 自带的模块,安装了 node 之后直接引用即可,无需再用 npm 下载。

    var http = require("http");

    然后使用 http.createServer() 方法创建服务器,并使用 listen 方法绑定 8888 端口。createServer 里面的回调函数将通过 request, response 参数来接收和响应数据。完整代码如下:

    //server.js 文件:
    
    var http = require('http');
    http.createServer(function (request, response) {   //createServer 函数会返回一个对象,通过该对象的 listen 的方法可以指定这个 HTTP 服务器监听的端口号
        // 发送 HTTP 头部 
        // HTTP 状态值: 200 : OK
        // 内容类型: text/plain
        response.writeHead(200, {'Content-Type': 'text/plain'});
    
        // 发送响应数据 "Hello World"
        response.write("Hello world!");
        response.end();    //也可以直接 response.end("hello world"),此时相当于先调用了 response.write(data) 之后再调用 response.end()
    }).listen(8888);

    由此一个 http 服务已经搭建完成。

    使用 node 命令执行 server.js 文件代码:

    node server.js

    接下来,通过页面访问该服务或者直接通过浏览器访问 http://localhost:8888/,就可以看到该服务的响应结果:

    5、node.js 的模块系统

    为了让Node.js的文件可以相互调用,Node.js提供了一个简单的模块系统。模块是Node.js 应用程序的基本组成部分,文件和模块是一一对应的。换言之,一个 Node.js 文件就是一个模块,这个文件可能是JavaScript 代码、JSON 或者编译过的C/C++ 扩展。

    5.1、导入模块的用法(require)

    require() 函数用于在当前模块中加载别的模块。在函数内写入模块的路径即可(相对路径和绝对路径都行)。node.js 默认后缀为 js,所以可忽略 .js 扩展名

    var foo1 = require('./foo'); // .js 扩展名可忽略
    var foo2 = require('./foo.js');
    var data = require('./data.json'); //不仅仅是 JS 文件,也可以是其他文件

    require 相当于module.exports的传送门,module.exports后面的内容是什么,require的结果就是什么,比如对象、数字、字符串、函数等,然后再把require的结果赋值给某个变量。

    require理论上可以运用在代码的任何地方,甚至不需要赋值给某个变量之后再使用,比如:

    require('./a')(); //假设a模块是一个函数,此时将立即执行a模块函数
    var data = require('./a').data; //假设a模块导出的是一个对象
    var a = require('./a')[0]; //假设a模块导出的是一个数组

    5.2、导出模块的用法(exports)

    exports 对象是当前模块的导出对象,用于导出该模块的方法或属性。

    别的模块通过 require() 函数引用别的模块时实际上得到的就是别的模块的 exports 对象:

    exports.hello = function () {
        console.log('Hello World!');
    };

    导出模块的方法或者属性可以直接用 exports,也可以使用 module.exports 来进行导出。通过module对象可以访问到当前模块的一些相关信息,但最多的用途是替换当前模块的导出对象。

    module.exports = function () {
        console.log('Hello World!');
    };

    实际上,exports是module.exports的一个引用,module.exports 指向的就是 exports。

    console.log(exports === module.exports); //输出true

    如果要对外暴露属性或方法,就用 exports 就行,要暴露对象(类似class,包含了很多属性和方法),就用 module.exports。

    代码示例:

    //hello.js 
    function Hello() { 
        var name; 
        this.setName = function(thyName) { 
            name = thyName; 
        }; 
        this.sayHello = function() { 
            console.log('Hello ' + name); 
        }; 
    }; 
    module.exports = Hello;
    
    //在 main.js 中引入 hello.js 
    var Hello = require('./hello'); 
    hello = new Hello(); 
    hello.setName('BYVoid'); 
    hello.sayHello(); 

    5.3、require 查找模块的策略

    由于 Node.js 中存在 4 类模块(原生模块和3种文件模块),尽管 require 方法极其简单,但是内部的加载却是十分复杂的,其加载优先级也各自不同。如下图所示:

    原生模块指的就是 node.js 自带的模块,文件模块指的是项目本地的文件:

     加载顺序:

    1. 缓存。模块在第一次加载后会被缓存。 这也意味着(类似其他缓存机制)如果每次调用 require(‘foo’) 都解析到同一文件,则返回相同的对象。

    2. 核心模块。核心模块定义在 Node.js 源代码的 lib/ 目录下。require() 总是会优先加载核心模块。 例如, require(‘http’) 始终返回内置的 HTTP 模块,即使有同名文件。

    3. 文件模块。如果按确切的文件名没有找到模块,则 Node.js 会尝试带上 .js、 .json 或 .node 拓展名再加载。当没有以 '/'、 './' 或 '../' 开头来表示文件时,这个模块必须是一个核心模块或加载自 node_modules 目录。

    4. 目录作为模块。可以把程序和库放到一个单独的目录,然后提供一个单一的入口来指向它。

    5.  node_modules 目录加载

    6.  从全局目录加载。NODE_PATH检索,如果 NODE_PATH 环境变量被设为一个以冒号分割的绝对路径列表,则当在其他地方找不到模块时 Node.js 会搜索这些路径。其他全局目录,$HOME/.node_modules (其中 $HOME 是用户的主目录)等等

    粗略记录一下,详细可自行查阅资料。

    6、node.js 路由

    通过路由,我们可以根据不同的 URL 来执行不同的代码,进行不同的处理。

    6.1、解析URL参数(url、querystring模块)

    URL 等一系列的参数和数据都存在 request 对象中,为了解析这些数据,我们需要额外的 Node.JS 模块,它们分别是 url 和 querystring 模块。这两个模块都是 node.js 自带的,所以不用额外安装。
    比如访问一个路径:http://localhost:8080/start?foo=bar&hello=world

    url.parse(reqObj.url) 解析出来的是一个对象,格式如下:

    Url {
      protocol: null,
      slashes: null,
      auth: null,
      host: null,
      port: null,
      hostname: null,
      hash: null,
      search: '?foo=bar&hello=world',
      query: 'foo=bar&hello=world',
      pathname: '/start',
      path: '/start?foo=bar&hello=world',
      href: '/start?foo=bar&hello=world'
    }

    querystring.parse(str) 方法将字符串转换为对象。

      1、url.parse(reqObj.url).query  该方法返回 URL 的参数,即 foo=bar&hello=world

      2、url.parse(reqObj.url).pathname  该方法返回虚拟目录,即 /start

     

      3、querystring.parse(queryStr)["foo"]  该方法返回 URL 参数中某个属性值,即上述 URL 中的 bar
    var http = require("http"),
        url = require("url");
        querystring = require('querystring');
    
    function onRequest(request, response) {
        // 获取请求路径
        var pathname = url.parse(request.url).pathname;
        var queryStr = url.parse(request.url).query;
    
        var fooVal = querystring.parse(queryStr)["foo"];
        var helloVal = querystring.parse(queryStr)["hello"];
    
        console.log('解析出的数据:', pathname, query, fooVal, helloVal);   //将输出:  解析出的数据: /start foo=bar&hello=world bar world
    }

    6.2、通过路由对不同URL进行不同响应

    通过路由,我们可以根据不同的 URL 来执行不同的代码,进行不同的处理。

    首先我们创建一个 server 模块。跟之前不同的是,这里扩展了服务器的 start() 函数,以便将路由函数作为参数传递过去:

    // server.js文件
    var http = require("http"),
        url = require("url");
        querystring = require('querystring');
    
    function start(route, handle) {
    
        function onRequest(request, response) {
            // 获取请求路径
            var pathname = url.parse(request.url).pathname;
    
            // 关闭nodejs 默认访问 favicon.ico
            if (!pathname.indexOf('/favicon.ico')) {
                return; 
            };
    
            // 收到来自 pathname 的请求
            console.log("收到来自 " + pathname + " 的请求");
    
            // 路由器处理
            route(handle, pathname);
    
            // 返回数据
            response.writeHead(200, {"Content-type": "text/plain"});
            response.write("Hello world!");
            response.end();
        }
    
        http.createServer(onRequest).listen(8080);
        console.log("Server has start!");
    }
    
    // 开放接口
    exports.start = start;

    然后创建一个路由模块:router.js 文件:

    // 路由模块,针对不同的请求,做出不同的响应。 handle:处理请求方法
    
    function route(handle, pathname) {
        console.log("路由处理来自 " + pathname + "的请求");
    
        // 检查给定的路径对应的请求处理程序是否存在,如果存在的话直接调用相应的函数
        if (typeof handle[pathname] == "function") {
            handle[pathname]();
        } else {
            console.log("路由没有对应的处理函数:" + pathname);
        }
    }
    
    exports.route = route;

    创建一个处理不同请求的程序模块:requestHandlers.js:

    // 存放不同的处理程序,和请求的URL相对应
    function start() {
        console.log("start 路由处理函数");
    }
    
    function upload() {
        console.log("upload 路由处理函数");
    }
    
    exports.start = start;
    exports.upload = upload;

    创建一个 index.js 文件,使得路由函数可以被注入到服务器中:

    var server = require("./server"),
        router = require("./router"),
        requestHandlers = require("./requestHandlers");
    
    // handle 保存不同请求路径对应的处理方法
    var handle = {};
    
    handle["/"] = requestHandlers.start;
    handle["/start"] = requestHandlers.start;
    handle["/upload"] = requestHandlers.upload;
    
    // 传入路由模块方法, 路径处理方法
    server.start(router.route, handle);

    将服务器跑起来即可:

    node index.js

    此时我们如果访问 http://localhost:8080/start?foo=bar&hello=world,服务器将会执行 start() 函数,控制台输出:start 路由处理函数。由此便实现了通过路由针对不同 URL 进行不同处理。

    其实路由就是封装一个函数 route,然后在 http模块 的 createServer 函数里面的回调函数执行该函数并将路径作为参数传递,在路由函数 route 中将会根据不同的路径测试进行不同处理。

    7、全局对象(global)

    在 JavaScript 中有一个特殊的对象,称为全局对象(Global Object),它及其所有属性都可以在程序的任何地方访问,即全局变量。在浏览器的 JavaScript 中,通常 window 是全局对象。

    而在 Node.js 中,全局对象是 global,所有的全局变量(除了 global 本身以外)实际上都是 global 对象的属性。在 Node.js 我们可以直接使用变量名,而不必要带上global前缀。

    global 最根本的作用是作为全局变量的宿主。如果我们要自己定义 global 属性,需要在定义的时候加上 global 前缀,比如 global.age = 11。当你定义一个全局变量时,这个变量同时也会成为全局对象的属性。

    通过输出 global 可以查看全局变量:

    console.log(global);   //通过console.log只会输出enumerable属性
    //输出如下:
    <ref *1> Object [global] {
      global: [Circular *1],
      clearInterval: [Function: clearInterval],
      clearTimeout: [Function: clearTimeout],
      setInterval: [Function: setInterval],
      setTimeout: [Function: setTimeout] {
        [Symbol(nodejs.util.promisify.custom)]: [Function (anonymous)]
      },
      queueMicrotask: [Function: queueMicrotask],
      clearImmediate: [Function: clearImmediate],
      setImmediate: [Function: setImmediate] {
        [Symbol(nodejs.util.promisify.custom)]: [Function (anonymous)]
      }
    }
    
    console.log(Object.getOwnPropertyNames(global));   //我们可以通过console.log(Object.getOwnPropertyNames(global))看到全部属性
    //输出如下:
    [
      'Object',             'Function',             'Array',
      'Number',             'parseFloat',           'parseInt',
      'Infinity',           'NaN',                  'undefined',
      'Boolean',            'String',               'Symbol',
      'Date',               'Promise',              'RegExp',
      'Error',              'EvalError',            'RangeError',
      'ReferenceError',     'SyntaxError',          'TypeError',
      'URIError',           'globalThis',           'JSON',
      'Math',               'console',              'Intl',
      'ArrayBuffer',        'Uint8Array',           'Int8Array',
      'Uint16Array',        'Int16Array',           'Uint32Array',
      'Int32Array',         'Float32Array',         'Float64Array',
      'Uint8ClampedArray',  'BigUint64Array',       'BigInt64Array',
      'DataView',           'Map',                  'BigInt',
      'Set',                'WeakMap',              'WeakSet',
      'Proxy',              'Reflect',              'decodeURI',
      'decodeURIComponent', 'encodeURI',            'encodeURIComponent',
      'escape',             'unescape',             'eval',
      'isFinite',           'isNaN',                'SharedArrayBuffer',
      'Atomics',            'FinalizationRegistry', 'WeakRef',
      'WebAssembly',        'global',               'process',
      'Buffer',             'URL',                  'URLSearchParams',
      'TextEncoder',        'TextDecoder',          'clearInterval',
      'clearTimeout',       'setInterval',          'setTimeout',
      'queueMicrotask',     'clearImmediate',       'setImmediate'
    ]

    可以看到,在全局对象里有一个 global 属性指向全局对象自己,也正是因为在全局对象里放了一个 global 属性指向了全局对象自己,所以才可以使用 global 访问全局对象。所以说,global, global.global, global.global.global ... 实际上都是同一个对象。

    7.1、全局函数(console)

    console 用于提供控制台标准输出,它是由 Internet Explorer 的 JScript 引擎提供的调试工具,后来逐渐成为浏览器的实施标准。Node.js 沿用了这个标准,提供与习惯行为一致的 console 对象,用于向标准输出流(stdout)或标准错误流(stderr)输出字符。

    以下为 console 对象的一系列方法:

    其中 console.log() 非常常用,该方法用于向标准输出流打印字符并以换行符结束。console.log 接收若干个参数,如果只有一个参数,则输出这个参数的字符串形式。如果有多个参数,则 以类似于C 语言 printf() 命令的格式输出。

    7.2、全局变量 process

    process 对象是 Node 的一个全局对象,即 global 对象的属性,它提供当前 Node 进程的信息,用于描述当前Node.js 进程状态。该对象部署了EventEmitter接口。

    process对象提供一系列属性,用于返回系统信息:

    • process.argv:返回一个数组,成员是当前进程的所有命令行参数。
    • process.env:返回一个对象,成员为当前Shell的环境变量,比如process.env.HOME
    • process.installPrefix:返回一个字符串,表示 Node 安装路径的前缀,比如/usr/local。相应地,Node 的执行文件目录为/usr/local/bin/node
    • process.pid:返回一个数字,表示当前进程的进程号。
    • process.platform:返回一个字符串,表示当前的操作系统,比如Linux
    • process.title:返回一个字符串,默认值为node,可以自定义该值。
    • process.version:返回一个字符串,表示当前使用的 Node 版本,比如v7.10.0

    process对象提供以下方法:

    • process.chdir():切换工作目录到指定目录。
    • process.cwd():返回运行当前脚本的工作目录的路径。
    • process.exit():退出当前进程。
    • process.getgid():返回当前进程的组ID(数值)。
    • process.getuid():返回当前进程的用户ID(数值)。
    • process.nextTick():指定回调函数在当前执行栈的尾部、下一次Event Loop之前执行。
    • process.on():监听事件。
    • process.setgid():指定当前进程的组,可以使用数字ID,也可以使用字符串ID。
    • process.setuid():指定当前进程的用户,可以使用数字ID,也可以使用字符串ID。

    参考:https://javascript.ruanyifeng.com/nodejs/process.html#toc0

    7.2.1、process的属性env(process.env)

    process.env属性返回一个对象,包含了当前Shell的所有环境变量。比如,process.env.HOME返回用户的主目录。

    通常的做法是,新建一个环境变量NODE_ENV,用它确定当前所处的开发阶段,生产阶段设为production,开发阶段设为developstaging,然后在脚本中读取process.env.NODE_ENV即可。

    运行脚本时,改变环境变量,可以采用下面的写法:

    $ export NODE_ENV=production && node app.js
    # 或者
    $ NODE_ENV=production node app.js

    7.3、node.js中的__filename和__dirname(绝对路径)

    __filename 表示文件所在位置的绝对路径,包括文件名。

    __dirname 表示文件所在位置的绝对路径,但不包括文件名。

    // index.js  执行 node index.js
    console.log(__filename);  //输出  F:visualStudioCodeglobalTestindex.js
    console.log(__dirname);   //输出  F:visualStudioCodeglobalTest

    _filename和_dirname都不是全局的,而是模块作用域下的。这两个属性不是全局对象下的属性,而是模块下的。如果你直接输出 global.__filename 会得到 undefined。

    8、GET和POST请求

    8.1、获取 GET 请求的参数

    GET 请求直接被嵌入在路径中,URL是完整的请求路径,包括了?后面的部分。获取 get 请求的参数可以用 node.js 内置的 url 和querystring 模块。详情可查看上面的 6.1解析URL参数(url、querystring模块)。

    8.2、获取 POST 请求体的内容

    POST 请求的内容全部的都在请求体中,request 并没有一个属性内容为请求体,原因是等待请求体传输可能是一件耗时的工作,比如上传文件。而很多时候我们可能并不需要理会请求体的内容,恶意的POST请求会大大消耗服务器的资源,所以 node.js 默认是不会解析请求体的。

    我们可以用以下方法来获取 post 请求体的内容:

    var http = require('http');
    var querystring = require('querystring');
    var util = require('util');
     
    http.createServer(function(req, res){
        // 定义了一个post变量,用于暂存请求体的信息
        var post = '';     
     
        // 通过req的data事件监听函数,每当接受到请求体的数据,就累加到post变量中
        req.on('data', function(chunk){    
            post += chunk;
        });
     
        // 在end事件触发后,通过querystring.parse将post解析为真正的POST请求格式,然后向客户端返回。
        req.on('end', function(){    
            post = querystring.parse(post);   //这里经过转换后将会是一个对象
            res.end(util.inspect(post));      //util.inspect方法将对象转换为字符串,然后再res.end响应回网页端
        });
    }).listen(3000);

    9、node.js操作MySQL数据库

    9.1、连接MySQL数据库

    首先使用 npm 初始化项目,然后安装 MySQL 模块:

    npm init
    
    npm install mysql

    创建一个 index.js 文件:

    var mysql      = require('mysql');
    var connection = mysql.createConnection({
      host     : 'localhost',    //主机地址
      user     : 'root',         //用户名
      password : '123456',       //密码
      database : 'test'          //数据库名
    });
     
    connection.connect();    //连接数据库
     
    connection.query('SELECT 1 + 1 AS solution', function (error, results, fields) {
      if (error) throw error;
      console.log('The solution is: ', results[0].solution);
    });

    9.2、数据库的基本操作(CURD)

    查询数据:

    var mysql  = require('mysql');  
     
    var connection = mysql.createConnection({     
      host     : 'localhost',       
      user     : 'root',              
      password : '123456',       
      port: '3306',                   
      database: 'test' 
    }); 
     
    connection.connect();
     
    var  sql = 'SELECT * FROM websites';
    //
    connection.query(sql,function (err, result) {
        if(err){
            console.log('[SELECT ERROR] - ',err.message);
            return;
        }
    
        console.log(result);
    });
     
    connection.end();

    插入数据:

    var mysql  = require('mysql');  
     
    var connection = mysql.createConnection({     
      host     : 'localhost',       
      user     : 'root',              
      password : '123456',       
      port: '3306',                   
      database: 'test' 
    }); 
     
    connection.connect();
     
    var  addSql = 'INSERT INTO websites(Id,name,url,alexa,country) VALUES(0,?,?,?,?)';
    var  addSqlParams = ['菜鸟工具', 'https://c.runoob.com','23453', 'CN'];
    //
    connection.query(addSql, addSqlParams, function (err, result) {
        if(err){
            console.log('[INSERT ERROR] - ',err.message);
            return;
        }        
    
        //console.log('INSERT ID:',result.insertId);        
        console.log('INSERT ID:',result);        
    });
     
    connection.end();

    插入数据有两种写法:

    //直接将插入值写在VALUE后面
    pool.query(INSERT INTO user ( id, username, PASSWORD, age, sex, permission, isDelete )VALUES( 4, "lijian", "123456", 18, 1, 0, 0 ),(err,result)=>{
        if(err) throw err;
        console.log(result);
    });
    
    //先写占位符,再在数据中写入插入值
    pool.query(INSERT INTO user VALUES(?,?,?,?,?,?,?),[6,“java”,“234”,30,1,0,0],(err,result)=>{
        if(err) throw err;
        console.log(result);
    });

    删除、修改操作跟上述大同小异,可参考:https://www.runoob.com/nodejs/nodejs-mysql.html

  • 相关阅读:
    WebApi Ajax 跨域请求解决方法(CORS实现)
    JQuery Ajax POST/GET 请求至 ASP.NET WebAPI
    Hybird APP对接后台:Net WebApi
    Chrome
    centos8平台:用fontconfig安装及管理字体(fc-list/fc-match/fc-cache)
    centos8平台:redis6配置启用io多线程(redis6.0.1)
    centos8平台安装redis6.0.1
    centos8平台:举例讲解redis6的ACL功能(redis6.0.1)
    ImageMagick实现图片加水印(ImageMagick6.9.10)
    centos8上安装ImageMagick6.9.10并压缩图片生成webp缩略图
  • 原文地址:https://www.cnblogs.com/wenxuehai/p/14245530.html
Copyright © 2011-2022 走看看