带你从0开始认识node

79 阅读7分钟

引言

今天,我们来从0开始学习一下node。node是一个什么样的东西,它被打造出来是干什么用的。

node,是一个能读懂js的工具,它的出现让js又上了一个台阶,让js不仅能做前端也能做后端。

js 不能直接做后端开发是因为 js 内部不具有可以联调操作系统的API。

node 中用 c++ 开发了很多可以跟操作系统交互的模块,node 中内置js引擎,所以node可以读懂js,也就是说可以通过调用node中的各个模块来实现跟操作系统交互。

npm

在node中还存在一个叫npm的东西,它是node的包管理工具,它就相当于是一个node的仓库。一些程序员会将他们写好的方法或封装好的包上传到这个仓库中,当我们想使用时,就能直接下载下来,非常方便。

npm就是官方打造的包管理工具,还有一些非官方打造的包管理工具,比如pnpm、yarn。pnpm是目前最主流最好用的包管理工具,我们可以直接在终端中输入npm i pnpm -g来下载它。

我们来体验一下npm。我们在npm中输入npm init,进行初始化,它会弹出一大堆问题,直接全选默认。我们会发现我们的文件夹多了一个json格式的文件。

image.png

这个文件里面有一个对象,你会发现,对象里放的就是刚刚问我们的一些问题。

{
  "name": "nodebase",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\"&& exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
}

这个json其实非常的重要。当我们往git上上传文件时,通常一些引入的包我们并不会上传。因为没有必要,这些包谁都能拿到,并不是我们自己写的代码。而我们下载了哪些包都会记录在这个json文件中。比如我随便下一个包,koa, 在json文件中我们就能查看到。

{
  "name": "nodebase",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\"&& exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "koa": "^2.15.3"
  },
}

而当别人拿到我们上传的代码后,他是没有这些包的,但json文件他是有的,他就能查看到自己缺少了哪些包。只要他在终端中输入npm i ,就能自动下载那些缺少的包,非常方便。

如果我们想运行一个node指令,可以在json文件中的"scripts"中这样写。假如我们想运行index.js,可以写成"dev": "node index.js"。dev可以随便叫。

image.png

这样当我们想运行index.js时,直接在终端输入npm run dev就行了。

模块化语法

我们再来学习一下模块化语法。我们可以将一些公用的方法写到别的js文件中,当我在这个文件中想使用这些方法时,可以引入进来。我们通常将这些js文件放到utils文件夹下。

1.CommonJS 规范

我们先来学习一下 CommonJS 语法。我们在utils文件夹下创建一个add.js文件,里面写一个加法函数。

function add(a, b) {
    return a + b;
}

然后我想在index.js文件中使用它,可以这样引入。

function add(a, b) {
    return a + b;
}

module.exports = {
    add:add
}

这叫抛出,我们抛出了一个对象,里面key为add值为add函数,如果key和value相同可以简写。只写一个add。

当我们想在index.js中使用这个方法时,就这样写:

const { add } = require('./utils/add.js')
console.log(add(1, 2));

引入utils文件夹下的add.js文件。因为我们引入的是一个对象,所以我们要用对象的解构,这个add就相当于add.js文件中的add方法了。这里的add可以随便叫。

这样我们就能使用add方法了。

image.png

它还有另外一种写法。在add.js文件中,我们可以这样写:

exports.add = function (a, b) {
    return a + b;
}

直接在add方法前面点乘exports。index.js中的写法不变,这样抛出的还是一个对象。

我们习惯性用第一种方法,因为当我们引入多个方法时,我们可以直接这样写:

function add(a, b) {
    return a + b;
}

function minus(a, b) {
    return a - b;
}

module.exports = {
    add,
    minus
}

就不用在每个方法前面点乘exports了。

2. ESmodules 规范

还有第二种模块化语法,ESmodules。

在使用这个语法之前,我们需要先在package.json中将type改成‘module’,node是默认支持CommonJS语法的,所以我们要手动更改一下。

image.png

我们再在utils文件夹下创建一个formate.js文件,里面随便写了一个格式化日期的方法。

function formatDate(date, format) {
    if (!date instanceof Date) {
        throw new Error('Invalid date');
    }

    const options = {
        year: 'numeric',
        month: '2-digit',
        day: '2-digit',
        hour: '2-digit',
        minute: '2-digit',
        second: '2-digit',
        hour12: false,
        timeZone: 'UTC',
    };

    return date.toLocaleString('en-US', options).replace(',', format);
}

我们这么将这个方法抛出:

function formatDate(date, format) {
    if (!date instanceof Date) {
        throw new Error('Invalid date');
    }

    const options = {
        year: 'numeric',
        month: '2-digit',
        day: '2-digit',
        hour: '2-digit',
        minute: '2-digit',
        second: '2-digit',
        hour12: false,
        timeZone: 'UTC',
    };

    return date.toLocaleString('en-US', options).replace(',', format);
}

export default formatDate

然后在index.js中这样写:

import formatDate from "./utils/formate.js";
console.log(formatDate(new Date));

这样就成功引入了formatDate方法,这里不需要用括号括起,因为抛出的不是一个对象,就是一个方法。

当然ESmodules也有第二种写法,我们可以直接在formatDate方法前面加上export。

export function formatDate(date, format) {
    if (!date instanceof Date) {
        throw new Error('Invalid date');
    }

    const options = {
        year: 'numeric',
        month: '2-digit',
        day: '2-digit',
        hour: '2-digit',
        minute: '2-digit',
        second: '2-digit',
        hour12: false,
        timeZone: 'UTC',
    };

    return date.toLocaleString('en-US', options).replace(',', format);
}

用这种方法抛出的就是一个对象了,所以我们要在index.js中进行解构。

import { formatDate } from "./utils/formate.js";
console.log(formatDate(new Date));

并且这里formatDate不能随便叫,只能取成那个方法的名字。这样同样也能引入。

模块

我们再来看一下node中常用的一些模块。

global

我们知道,在浏览器中有一个全局对象window,而在node中也有一个全局对象,叫global。

我们可以输出global看看。

console.log(global);

image.png

在global身上有些这样的东西。

我们来看一些window环境下没有的global方法。

console.log(__filename);

image.png

得到当前这个文件在电脑中的绝对路径。

console.log(__dirname);

image.png

得到当前文件夹在电脑中的绝对路径。

console.log(process.argv);

process叫进程。process上有一个方法argv,它能读懂我们在终端中输入的代码。比如我在终端中敲一个hello。

image.png

它就会出现在这个数组中。

console.log(process.cwd());

image.png

这个方法能获取当前工作的绝对路径。

console.log(process.env);

它能读取当前执行环境的环境变量。

console.log('hello');
process.exit()
console.log('world');

process.exit()可以中断当前的进程,所以只会输出hello。

image.png

process.stdout.write('hello')
process.stdout.write(' ')
process.stdout.write('world')
process.stdout.write('\n')

stdout能输出一个标准的流类型,这个流类型的资源可以进行一个合并展示。

image.png

process.stdin.on('data', function (data) {
    console.log(`用户输入: ${data}`);
})

stdin是往node中输入一个流类型,当data有了,后面的函数就能触发。

比如我往终端中输入一个hello,后面的函数就触发了。这个方法能让我们与进程进行交互。

image.png

const buf = Buffer.from('你好')
console.log(buf);

Buffer是node自带的一个对象,它身上有一个方法from能将一种数据类型转换成Buffer流类型,我们输出看一下。

image.png

转换成16进制了。当我们想转回字符串时,可以用buf.toString()。

path

我们再来看一下path模块,提供了一些什么API供我们使用。

join方法能将我们输入的字符串自动拼接成相对路径。

console.log(path.join('a', 'b', 'c'));

image.png

resolve方法能将我们输入的字符串拼接成绝对路径。

console.log(path.resolve('a', 'b', 'c'));

image.png

dirname方法能拿到当前文件夹所在文件夹的绝对路径。

console.log(path.dirname(process.cwd()));

image.png

basename方法能拿到一个路径中的文件名。

console.log(path.basename('/a/b/c.js'));

image.png

extname方法能获取一个路径的扩展名。

console.log(path.extname('/a/b/c.js'));

image.png

normalize方法能调整不规范的路径。

console.log(path.normalize('a//b/c.js'));

image.png

parse方法能将一个路径解析成对象。

console.log(path.parse('/home/article/index/readme,md'));

image.png

path.sep代表‘\’。

console.log(path.sep);

image.png

fs

我们再来看一下fs模块,文件系统。

这个模块里面有很多方法并且非常强大。它能让js与操作系统进行交互并且读取某一个盘中的文件。

readFile方法能读取到一个文件资源,这个方法是异步的。在node里面,所有方法都打造了同步和异步版本,取决于你想使用哪个。

我在同级文件夹下面创建了一个temp.html,用这个方法能读取到。

fs.readFile('./temp.html', 'utf-8', (err, data) => {
    if (!err) {
        console.log(data);
    }
})

image.png

这个方法还有同步版本。

let html = ''
html = fs.readFileSync('./temp.html', 'utf-8')
console.log(html);

readFileSync方法就是同步的。

http

创建 HTTP Server

const server = http.createServer((req, res) => {
    res.end('hello world')
})

监听端口

server.listen(3000, () => {
     console.log('Listening on port 3000');
})