nodeJS 基础

Node.js

  • 可以理解为能在操作系统上跑的js(不仅仅在浏览器)
  • 还能当web服务器哟~
  • 用的Chrome V8 JS 引擎(谷歌搞出来的浏览器处理js脚本的引擎)
  • 事件驱动
  • 非阻塞IO模型

简单理解

  • nodeJS是服务端的javascript
  • 它是一种脚本语言,需要一个解释器。而浏览器就充当了这个解释器(chrome v8 引擎 由c++ 语言开发)将js脚本转换为机器语言后执行。
  • nodeJS就是把v8引擎(谷歌开源了这个引擎)给单独搞出来然后像编写一个Nigix服务器软件一样弄出来了这么一个叫NodeJs的软件 ,部署在服务器上。
  • nodeJs主要提供给我们了一个Js的本地运行环境,还可以操作一些文件(打包工具),同时可以与数据库通讯(实现后端功能)。
  • 实际应用:API开发,小工具开发,web全栈开发。

安装

  • 最好装LTS(长期支持)版,是一个安装包,下一步就行
  • 验证是否安装成功,命令行
# 显示版本
node -v
  • 编辑器:vscode(我再也不想看到sublime让我付钱的弹窗了),只装一个插件:
    【Ctrl】+【Shift】+【x】找:==Terminal:编辑器内打开命令行== (ctrl+`)打开终端即可在编辑器中执行命令行

nodejs模块

  • 定义和暴露模块
// 定义一个方法
var counter = function(arr) {
    return "这里有" + arr.length + "个元素在数组里";
}

// 定义一个方法
var adder = function (a, b) {
    return `两数和为 ${a+b}`;
}

// 定义一个变量
var pi = 3.14;

// 暴露模块 module.exports.模块名 = 要暴露的东西
// module.exports.counter = counter;
// module.exports.adder = adder;
// module.exports.pi = pi;
// 简写
module.exports = {
    // 键 : 值
    counter : counter,
    adder : adder,
    pi : pi
}
  • 引用和使用模块
// var 定义一个变量 = require('模块存放的路径');
var modules = require('./node模块');
// require引用的是一个module.exports暴露出来的对象,所以也可以直接引用它下面的某一个属性。
var pi = require('./node模块').pi;

// modules 是一个对象,调用方法就用 (对象.属性)即可
console.log(modules.counter(['1', '2', '3']));
console.log(modules.adder(1, 2));
console.log(modules.pi);

事件

  • 1
// 引用事件模块
var events = require('events');

// 新建一个事件对象
var myEmitter = new events.EventEmitter();

// 绑定事件 .on('事件名', 事件触发回调函数(){//...});
myEmitter.on('someEvent', function(message1, message2) {
    console.log(message1 + message2);
});

// 手动触发事件 .emit('事件名', 参数列表);
myEmitter.emit('someEvent', '触发1', '触发2');
  • 2
// 引用事件模块
var events = require('events');
// 引用工具库模块 util
var util = require('util');

// 定义一个Person类
var Person = function(name) {
    // 创建类时给属性name一个值
    this.name = name;
}

// 工具库.继承(让Person类, 继承events模块下的EventEmitter类)
util.inherits(Person, events.EventEmitter);

// 创建三个对象
var xiaoming = new Person('xiaoming');
var lili = new Person('lili');
var lucy = new Person('lucy');

// 将三个对象放进一个数组
var people = [xiaoming, lili, lucy];

// 循环数据
people.forEach(function(person) {
    // 给每个对象绑定一个事件
    person.on('speak', function(message) {
        // 事件名speak, 事件执行控制台日志记录 对象.名字属性说参数message
        console.log(person.name + "说" + message);
    });
});

// 触发事件
xiaoming.emit('speak', 'hi1');
lili.emit('speak', 'hi2');
lucy.emit('speak', 'hi3');

同步文件IO操作

  • 同步,阻塞式的:如果我们写文件那一部分代码,写的内容非常大,那计算机会一直在那里执行写入代码,而不继续往下跑程序。
// 引入文件系统模块
var fs = require('fs');

// 读取文件 fsreadFileSynt("文件", "编码");
var content = fs.readFileSync("readMe.txt", "utf-8");
    // console.log(content);

// 写文件
fs.writeFileSync("writeMe.txt", content);
    // console.log("文件writeMe.txt创建成功,内容写入成功");
// writeFileSync会覆盖原内容
fs.writeFileSync("writeMe.txt", "Hello World");

异步读写文件

  • 异步非阻塞的意义:某一部很耗时,影响用户体验(用户得一直等这一步执行完)再继续向下进行其他操作,而nodejs采用异步方法就是在“事件队列”里注册一些很耗时的事件,然后在主线程程序执行完,再执行这些耗时的操作。
// 1:引用文件模块
var fs = require('fs');

// 2:异步读文件, fs.readFile("文件", "编码", 回调函数(发生错误, 文件内容) {//...});
var content = fs.readFile("readMe.txt", "utf-8", function(error, data) {
    // 异步写文件
    fs.writeFile("writeMe.txt", data, function() {
        console.log("写入完毕");
    });
});

// 3:控制台打印提示
console.log("读取完毕");

/**
 * 为什么“读取”完毕会先出现?
 * 3段代码依旧是按顺序执行的,
 * 但是执行2的时候,只是执行:node自动在“事件队列”里注册一个事件(如果文件过大,采用同不方法就会阻塞其他操作)
 * 然后直接执行3
 * 然后再回头去,执行“事件队列”里的事件:读取文件
 */

目录操作

// 引用文件系统模块
var fs = require('fs');

fs.unlink("writeMe.txt", function() {
    console.log("删除成功");
});

// 同步就是后面加Sync,参数不加回调函数因为没啥需要回调的程序顺着跑
// fs.unlinkSync("writeMe.txt");

/* 实现一个小功能 */
// 创建目录stuff
fs.mkdir('stuff', function() {
    // 读取文件readMe.txt
    fs.readFile('readMe.txt', 'utf-8', function(error, data) {
        // 将文件内容写到 ./stuff/writeMe.txt中
        fs.writeFile('./stuff/writeMe.txt', data, function(){
            console.log('文件写入成功');
        });
    });
});

流和管道

  • stream流
    // 模块
    var fs = require('fs');
    
    // 创建读取流 createReadStream(当前路径/文件名);
    // var myReadStream = fs.createReadStream(__dirname + '/readMe.txt');
    // 设置编码(不设置的话是buffer(可以理解为机器才能读得懂的东西),而且文件很大就有很多buffer。)
    // myReadStream.setEncoding('utf-8');
    // 可以直接这么写
    var myReadStream = fs.createReadStream(__dirname + '/readMe.txt', 'utf-8');
    
    var data = "";
    
    // 'data'事件 读取ing 
    myReadStream.on('data', function(chunk) {
        data += chunk;
    });
    
    // 'end'事件 读取完成后
    myReadStream.on('end', function() {
        console.log("数据接收完毕");
        console.log(data);
    });
    
    // 模块
    var fs = require('fs');
    
    // 创建读取流
    var myReadStream = fs.createReadStream(__dirname + '/readMe.txt', 'utf-8');
    // 创建写入流(文件名)
    var myWriteStream = fs.createWriteStream(__dirname + '/writeMe.txt');
    
    // myReadStream.on('data', function(chunk) {
    //     // 写 .write
    //     myWriteStream.write(chunk);
    // });
    
    // 新建写入内容
    var writeData = "Hello World";
    // 写
    myWriteStream.write(writeData);
    // 写完啥事不干,把流关了
    myWriteStream.end();
    // finish结束事件 
    myWriteStream.on('finish', function(){
        // 回调函数打一个提示信息出来
        console.log('文件写入完成');
    });
    
    
  • 管道grep
// 模块
var fs = require('fs');

// 创建读取流
var myReadStream = fs.createReadStream(__dirname + '/readMe.txt', 'utf-8');
// 创建写入流(文件名)
var myWriteStream = fs.createWriteStream(__dirname + '/writeMe.txt');

// 读取流.pipe(写入流) : 读取文件流内容,写入写入文件流
myReadStream.pipe(myWriteStream);

web服务

  • 由==http模块==实现web服务器
  • 操作类似操作文件流:1、实例化服务器=》2、接收请求request,响应请求response=》3、响应请求时文件流写入响应信息(照着http响应写,有响应头,相应体等)响应内容传给浏览器。

web服务具体实现

// http模块
var http = require('http');

// 创建服务器(回调函数2个参数(请求, 响应){//...})
var server = http.createServer(function(request, response) {
    // 这里的控制台日志输出是在服务器端的
    console.log("接收请求");
    // 写响应头 writeHead(状态码, {内容})
    response.writeHead(200, {
        'Content-Type': 'text/plain',
    });
    // 写响应体 write
    // response.write('Hello World!');
    // 关闭文件流(结束一次http请求和响应)
    // response.end();
    // 可以直接写 end(响应体);
    response.end('Hello World!');
});

// listen(端口, ip)
server.listen(3000, '127.0.0.1');
console.log("服务器启动了!");

JSON

  • Content-Type: application/json
// http模块
var http = require('http');

// 创建请求
var server = http.createServer(function(request, response) {
    console.log("接收请求");
    response.writeHead(200, {
        // json的内容-类型为 application/json
        'Content-Type': 'application/json'
    });
    // 定义一个json对象
    var myJson = {
        'name': 'liuhaoyu',
        'job': 'programmer',
        'age': 22
    }
    // 解析一下json对象为字符串 JSON.stringify(json对象)
    // 把字符串形式的json变回对象 JSON.parse(json字符串)
    response.end(JSON.stringify(myJson));
});

server.listen(3000, '127.0.0.1');
console.log("服务器启动了!");

响应html

  • 不要直接写html代码,而是用文件流的形式
  • 新建一个html文件,写html代码
  • 然后用fs模块创建文件流读取html文件
  • 然后在响应时用文件流.pipe写进响应体
// http模块
var http = require('http');
// 优化:响应请求时将html文件以文件流的形式发送给浏览器
var fs = require('fs');

// 来个html文本
// var htmlText = 
// '' +
// '' +
// '' +
//     '' +
//     '' +
//     '' +
//     '响应html5代码' +
// '' +
// '' +
//     '

响应成功

' + // '

恭喜你,成功解锁nodeJS.http.server技能

' + // '' + // '' ; // 优化:读取文件流存储在变量中 var htmlText = fs.createReadStream(__dirname + '/html.html', 'utf-8'); var server = http.createServer(function(request, response) { console.log("接收请求"); response.writeHead(200, { // html : text/html 'Content-Type': 'text/html' }); // response.end(htmlText); // 优化:管道传递给响应 htmlText.pipe(response); }); server.listen(3000, '127.0.0.1'); console.log("服务器启动了!");

模块化

  • 弄个module.js写源代码,然后暴露exports
// 引用系统模块...
// 编写自己的模块...

// 暴露
module.exports.startServer = startServer;
  • 弄个server.js 引用 module.js,然后跑他的startServer模块方法
// 引用模块
var module = require('./module');
// 跑
module.startServer();

实现简单的路由

  • 请求有个属性 request ==.url==
  • 新建一个文件夹Routes
  • 新建3个html页面(假设这个webApp就3个页面):user.html / home.html / 404.html
  • 重点module.js : switch(request.url) 来跳转页面
// http模块
var http = require('http');
// 文件操作模块
var fs = require('fs');

function startServer() {
    var htmlText;
    var server = http.createServer(function(request, response) {
        // 先确定所有路由都响应html文件  
        response.writeHead(200, {
            // html : text/html
            'Content-Type': 'text/html'
        });
        // 获取请求路由 request.url
        // console.log(request.url);
        // 根据请求路由不同,获得不同的html文件
        switch (request.url) {
            case '/':
            case '/home':
                htmlText = fs.createReadStream(__dirname + '/home.html', 'utf-8');
                break;
            case '/user':
                htmlText = fs.createReadStream(__dirname + '/user.html', 'utf-8');
                break;
            default:
                htmlText = fs.createReadStream(__dirname + '/404.html', 'utf-8');
                break;
        }
        htmlText.pipe(response);
    });

    // 启动服务
    server.listen(3000, '127.0.0.1');
    console.log("服务器启动了!");

}

// 可以直接exports暴露模块
exports.startServer = startServer;
  • 还是使用模块化的方法
// 引用模块
var module = require('./module');
// 跑
module.startServer();

获取get / post

  • module.js
// http模块
var http = require('http');
// 文件操作模块
var fs = require('fs');
// url操作模块
var url = require('url');

function startServer() {
    var htmlText;
    var server = http.createServer(function(request, response) {
        response.writeHead(200, {
            'Content-Type': 'text/html'
        });
        // 获取地址
        var pathname = url.parse(request.url).pathname;
        // 获取GET方式传递的参数 url true返回对象 false返回字符串
        var params = url.parse(request.url, true).query;
        // 获取POST方式传递的参数
        var data = "";
        // 错误时
        request.on(error, function() {
            console.log(error);
        // 接收时
        }).on('data', function(chunk) {
            data += chunk;
        // 接收完成后 
        }).on('end', function() {
            console.log(data);
        });

        switch (pathname) {
            case '/':
            case '/home':
                htmlText = fs.createReadStream(__dirname + '/home.html', 'utf-8');
                break;
            case '/user':
                htmlText = fs.createReadStream(__dirname + '/user.html', 'utf-8');
                break;
            default:
                htmlText = fs.createReadStream(__dirname + '/404.html', 'utf-8');
                break;
        }
        htmlText.pipe(response);
    });

    // 启动服务
    server.listen(3000, '127.0.0.1');
    console.log("服务器启动了!");

}

// 可以直接exports暴露模块
exports.startServer = startServer;
  • 改一下
// 引用一下queryString模块
var queryString = require('querystring');

// ...

// 根据method方式获取参数
        var data = "";
        // 错误时
        request.on(error, function() {
            console.log(error);
        // 接收时
        }).on('data', function(chunk) {
            data += chunk;
        // 接收完成后 
        }).on('end', function() {
            // 判断method
            if(request.method == 'POST') {
                // post 就用queryString模块.parse() 把数据变成对象
                data = queryString.parse(data);
            }else {
                // get 就用url.parse() 把数据变成对象
                data = url.parse(request.url, true).query;
            }
            console.log(data);
        });

npm是个啥?

  • Node Package Manager(node包管理工具)
  • ≈ ==php.composer==

安装

  • 安装node.js自动就帮你装上了npm
  • 查看npm版本
npm -v
  • 更新 ==不建议==
#npm 安装 自己安装自己@最新版 -g全局
npm install npm@latest -g
  • 安装包
npm -install 包名
  • 包下载好了,会新建一个node_modules的目录,包放里面的,==别动它==。
  • 搞一个国内镜像 ==不建议==
# 国内镜像
npm install -g cnpm --registry=https://registry.npm.taobao.org
# 更新cnpm
cnpm install cnpm@latest -g
  • 搞国内镜像后就要用cnpm命令装包了
cnpm install 包名
  • 系统全局安装某个包,比如webpack
cnpm install -g 包名

package.json

  • 这货就是记录某个项目安装了那些包。
  • 进入项目目录,初始化项目
# 会问你无数个问题
npm init
# 不想回答?
npm init -y
  • 好了,你生成了一个package.json文件
  • 安装某个包并且把包和包依赖写进去
# 用国内镜像 安装 --包依赖写进package 安装的包叫express一个nodeJS后台框架
cnpm install --save express
  • 还有其他的作用吗?
# 只要有package.json文件,不用给他node_modules文件夹,直接给执行命令,把package.json记录的所有包全部下载下来
npm install

nodemon

  • 直接写nodeJS web服务的时候,代码一改就要重启,很烦躁,安一个
cnpm install -g nodemon
  • 用它跑入口文件
nodemon 入口文件名
  • 好了,现在你改一下代码,保存的瞬间,这货帮你重启一次服务器。

补充

# install 简写为 i
cnpm i 包
  • 记住,你要把包写进依赖管理package.json里面,必须
cnpm install 包 --save
  • 干掉一个包
cnpm uninstall 包

这样只是干掉了依赖,并没有干掉node_modules里的包源代码。当你开发完成后,最好在生产环境部署的时候,先把node_modules==移除项目文件夹并备份==,确定package.json的依赖关系正确(只要你不乱编辑它,装包卸包都用命令),然后在生产环境服务器上用 npm install 把包下好是最好的方法

  • 更新包
cnpm update 包
  • 安装指定版本
cnpm istall 包@版本 --save

npm配置

# 你初始化一个项目,相当于初始化一个npm包
npm init
# 开始答题
package name: 包名
version: 版本
description: 描述
git repository: git仓库地址
author:作者
license: ISC许可证
最后给你一个初始化的package.json文件内容,问你客官这样行不行,回车yes
初始化了一个package.json

package.json 里的 ==scripts==

...

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1" ,
    "yo": "echo \" yo!\" "
},
  
...
  • 跑它
npm run yo

webpack是啥

  • webpack是一个打包js或者css代码的工具。
  • 为什么要打包呢?普通的js存在缺陷:a.js依赖b.js中的某个变量或者方法,只有在a.js中将b需要的变量或方法写成全局的。(比如变量x 写成window.x,在b中也用window.x读)。非常不方便和危险(如果你用的一个扩展包也有人用window.x为变量名呢)。因此node.js出现了。
  • 看代码:modules暴露,index引用modules暴露的变量即可实现上面的功能
    • modules.js
    var msg = "yo";
    // 如果不暴露,在其他文件是找不到变量b的
    var x = 1;
    
    // 暴露模块
    module.exports = {
        'msg': msg
    };
    
    • index.js
    var msg = require('./modules').msg;
    
    console.log(msg);
    
  • 然而这个功能只能在本地(控制台)中用,浏览器其实是读不懂的。所以需要webpack把这两个.js文件编译一下,打个包(浏览器就能读懂编译后的js文件了)。

安装

# 全局安装
npm install webpack -g
# 有个小坑
#The CLI moved into a separate package: webpack-cli
#Would you like to install webpack-cli? (That will run npm install -D webpack-cli) (yes/NO)n
#It needs to be installed alongside webpack to use the CLI
#上面就是提示你 CLI这个东西 webpack自身没带 你需要装一个 才能使用webpack (webpack4更新的坑)
npm install --save-dev webpack-cli -g

打包

  • 配置: 项目根新建一个 ==webpack.config.js== (不能改名字)
module.exports = {
    // 入口文件,其他被暴露的模块不用管,webpack打包时会读require代码,它自动取找
    entry: './index.js',
    // 出口文件(打包后生成的文件)
    output: {
        // 文件名
        filename: 'pack.js',
        // 路径
        path: __dirname
    }
}
  • 打包
cmd > webpack

多个入口文件

  • 打包
module.exports = {
    // 多个入口文件写成对象的形式
    entry: {
        // key入口名称: value入口文件具体路径
        home : './js/home.js',
        user : './js/user.js',
    },
    output: {
        // 这里的[name]对应entry里的入口名称
        filename: '[name].bundle.js',
        // /dist 是惯例用法,一般npm打包的文件都放这里
        path: __dirname + '/dist',
    }
}
  • 打包
# 就会在当前目录/dist/下打包home.bundle.js & user.bundle.js
webpack

loader

  • 新建一个项目
  • 初始化项目,安装webpack 和 webpack CLI
#不想答题
npm init -y
#--save-dev 可以简写为 -D
npm install webpack -D
#cli 等会跑的时候会提示你安装
  • 编辑package.json
// ...
"scripts": {
    // 这是默认的
    "test": "echo \"Error: no test specified\" && exit 1",
    // 这是我们添加的一条 想要执行? npm run dopack, --watch是一条不挂断的命令,当任何源码发生改变时,都会根据package.config.js的配置重新打包
    "dopack": "node_modules/.bin/webpack --watch"
},
// ...
  • 安装两个loader:css-loader & style-loader
npm install css-loader style-loader -D
  • 新建并配置文件webpack.config.js
module.exports = {
    entry: './js/index.js',
    output: {
        filename: 'bundle.js',
        path: __dirname + '/dist'
    },
    // 配置module
    module: {
        // 配置rules
        rules: [
            {
                // 正则匹配 .css 后缀文件
                test: /\.css$/,
                // 使用两个loader , 注意: 顺序从右往左
                use: ['style-loader', 'css-loader'],
            }
        ]
    }
}
  • 再来几个文件: ./js/base.js & index.js,./css/base.css

    • base.js 假装弄个配置项目debug
    var debug = true;
    // es6可以直接这么暴露模块
    export{debug};
    
    • index.js来个好玩的
    // es6可以直接这么获取模块
    import {debug} from './base';
    console.log(debug);
    
    // 借用loader加载css
    import '../css/base.css';
    
    • base.css就设置下的背景颜色即可“#ccc”
  • 现在编译 npm run dopack (提示安装cli输入y回车等下载完即可)

  • 最后生成一个文件 ./dist/bundle.js,新建一个index.html将其引入,会发现没有css的情况下,body变色了,通过浏览器调试工具发现head多了这一部分代码


你可能感兴趣的:(nodeJS 基础)