nodejs入门秘籍

node

1、cmd简单命令

盘符: # 进入某个盘符
cd 目录 # 进入某个目录
cd .. # 回到上一级目录
dir # 查看当前文件夹中的文件和文件夹
tree # 查看当前文件夹中的所有文件和文件夹包括子文件夹及其文件,树状结构
cls # 清屏
ipconfig # 查看当前电脑的网管信息
systeminfo # 查看当前电脑的配置信息
md 文件夹名 # 新建文件夹    make dir   directory
rd 文件夹名 # 删除文件夹
xcopy 文件夹 新文件夹名 # 复制文件夹,并起新的名字
type nul> 文件名 # 新建文件
copy 文件名 新文件名 # 复制文件,并起新的名字
echo 内容 > 文件名 # 给文件中写入内容
move 文件路径 新的路径 # 将文件移动到新的路径
ren 文件名 新的文件名 # 将文件重命名
del 文件名 # 删除一个文件
#上下键操作之前的命令
tab:#可以切换文件
# 安装node后
node:#进入环境 node交互解释器
node 文件名.js#运行js文件
#ctrl+c:退出环境

选择JavaScript的原因:

  • 基于事件驱动,可实现异步。
  • 非阻塞IO,不占用资源。
  • 简单易用,可以快速开发。

2、下载安装node

1、下载node

直接在百度上搜node.js然后进入官网下载,下载左边的旧版,不容易出错

2、安装

直接傻瓜式安装,路径可改可不改,建议在c盘,提高node的运行速度。

3、检测安装

cmd里面执行node --versionnode -v

node -v  #然后回车,在任何文件夹下的cmd都能检测,结果返回版本号即安装成功

3、Node.js REPL

交互解释器:就是js运行引擎、就是安装node后的命令行

命令行输入: node 进入交互解释器

  • ctrl + c - 退出当前终端。
  • ctrl + c 按下两次 - 退出 Node REPL。
  • ctrl + d - 退出 Node REPL.
  • 向上/向下 键 - 查看输入的历史命令
  • tab 键 - 列出当前命令
  • .help - 列出使用命令
  • .break - 退出多行表达式
  • .clear - 退出多行表达式
  • .save filename - 保存当前的 Node REPL 会话到指定文件
  • .load filename - 载入当前 Node REPL 会话的文件内容。

4、nvm版本管理

注意:安装前,一定要卸载完全node

教程网站:https://www.cnblogs.com/dreamsqin/p/10885082.html

下载模块:https://github.com/coreybutler/nvm-windows/releases

问题解决:https://www.cnblogs.com/aer2005/p/11548550.html


5、npm下载第三方模块

1、模块

node包含3中模块:内置模块,自定义模块,第三方模块。

自定义模块:自己定义封装的方法函数,或者对象的js文件。

第三方模块:下载网上别人封装的方法函数或对象。

2、检测npm模块

npm -v#返回版本号即安装成功

3、升级

npm install npm -g

4、npm(下载)

1、全局安装nrm

npm i -g nrm
npm config get prefix #查看安装路径

2、检测安装

nrm --version

3、nrm的检测

nrm ls # 用来检测哪个地址下载速度快一些,当前使用地址前面会带*

4、淘宝镜像

现在有很多网址,将常用的工具放上去,供人们下载,我们将这些网址叫做镜像源。

测试出来的结果:镜像源名称 ---- 网速,我们挑选网速最快的镜像源地址使用:

nrm use taobao # 将下载地址切换成taobao的镜像源

5、同步模块

$ cnpm sync connect
#cnpm和npm使用是一样的,是国内的npm

6、淘宝镜像命令

npm install -g cnpm --registry=https://registry.npm.taobao.org#或者
npm config set registry=https://registry.npm.taobao.org 
#注意,powershell无法执行修改vscode默认终端:F1搜索select选中其中的终端默认选项,然后修改

7、查看源⭐

npm config get registry

8、还原地址

5、npm安装模块

1、下载并安装

npm init  # 创建一个当前文件目录下的packjson.js管理环境。
npm init --yes # 直接选用默认配置
npm install 包名@版本号 # 下载一个包到当前命令行所在目录,生产开发都装,版本号不写默认最新
npm i 包名@版本号  #下载指定的版本
npm install -g 包名 #全局安装一个包
npm install --global 包名#同上
npm install --save  #记录生产环境所需的模块,代码错误只会终止执行,构建#dependencies
npm install --save-dev #开发环境,代码错误明确指出错误。开发#devDependencies
npm update 包名 # 升级依赖包

其中install可以简写为i

如出现报错:

npm err! Error: connect ECONNREFUSED 127.0.0.1:8087 

处理报错:

npm config set proxy null

2、查看安装

npm list -g #查看全局安装的模块
npm list grunt #参看某个模块的版本
npm root 模块 #查看模块安装路径

3、卸载模块

npm uninstall 模块名或包名 #卸载命令
npm ls 包名 #查看卸载的包是否还存在,查看包的信息
npm cache verify #清除卸载缓存文件。
npm cache clean --force #清除缓存文件

4、package.json

1、创建package.json
npm init  #创建或生成package.json文件(模块),初始化(会将一下载的模块记录)
#然后安装Package.json 属性填写对应值
npm init -y#使用默认配置
2、package.json属性
  • name - 包名。
  • version - 包的版本号。
  • description - 包的描述。
  • homepage - 包的官网 url 。
  • author - 包的作者姓名。
  • contributors - 包的其他贡献者姓名。
  • dependencies - 依赖包列表。如果依赖包没有安装,npm 会自动将依赖包安装在 node_module 目录下。
  • repository - 包代码存放的地方的类型,可以是 git 或 svn,git 可在 Github 上。
  • main - main 字段指定了程序的主入口文件,require(‘moduleName’) 就会加载这个文件。这个字段的默认值是模块根目录下面的 index.js。
  • keywords - 关键字
3、使用package目录下载包

mode_modules这个文件夹层级深,文件冗余,不容易复制移动

拷贝项目的时候不需要拷贝node_modules,只需要拷贝一个包的列表文件、package.json文件,到自己电脑上下载,不会出现文件丢失的情况,且是自动下载

步骤:⭐

  1. 拷贝package.json文件
  2. 在所在文件夹打开cmd命令行输入:npm i自动下载
  3. 下载完成后都是再当前文件夹的node-modules文件里面

5、更新模块t

npm update 模块名  #加-g就是变成全局更新npm outdate 模块名 #查看本标信息

6、搜索模块

npm search 模块名

7、引入模块(require)

const http = require('http');

8、npm自定义命令⭐

package.json的文件夹的script属性里可以添加自定义属性

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "aa":"echo 6666",//这是自定义属性
    "startJs":"node ./test2.js"//自定义属性
},

然后再cmd里运行

npm run-script aa#或者npm run startJS

然后开始运行这些属性对应的属性值里的字符串。

9、yarn更快的安装管理

npm i yarn -g

10、npm所有命令

1.  npm install [-args] [<@scope>/]<name>[@<version range>]    
# 安装第三方依赖包,例如:npm install --global @babel/[email protected]
2.  npm uninstall  [-args] [<@scope>/]<name>[@<version range>]
# 卸载模块,例如:npm uninstall jquery --save-optional,卸载可选阶段的依赖里的jquery
3.  npm update [-g] [<pkg>]    # 更新模块。
4.  npm outdated [[<@scope>/]<pkg>]   # 检查模块是否已经过时。
5.  npm ls [[<@scope>/]<pkg> ...]   # 查看安装的模块。
6.  npm init [-f|--force|-y|--yes]   
# 在项目中引导创建一个package.json文件。
# 安装包的信息可保存到项目的package.json文件中,以便后续的其它的项目开发或者他人合作使用。
7.  npm root [-g]    # 查看包的安装路径
8.  #管理npm的配置路径有关的命令    
npm config set <key> <value> [-g|--global]    
npm config get <key>    
npm config delete <key>    
npm config list    
npm config edit    
npm get <key>    
npm set <key> <value> [-g|--global]
9.  npm cache verify # 清理缓存,如果安装莫名报错可以考虑清空缓存。
10. npm start [-- args] # 启动某个脚本指令
# 该命令写在package.json文件scripts的start字段中
# "scripts": { "start": "gulp -ws" }
# 可以自定义命令来配置一个服务器环境和安装一系列的必要程序,如此时在cmd中输入npm start命令相当于执行gulpfile.js文件自定义的watch和server命令。
11. npm stop  [-- args]: # 停止脚本。
12. npm restart: # 重启脚本。
13. npm publish: # 发布自己的模块到仓库。
14. npm version: # 查看版本。

6、模块化

  • javascript代码在script标签内运行
  • 或者在.js后缀文件下运行,通过引入运行
  • 那么.js文件之间如何导出引入,形成模块化

1、commonJs

  • 导出

    let x={"name":"jack"};
    let addX=function(v){
        x.age=v;
        return x;
    };
    module.exports.x=x;
    module.exports.addX=addX;
    //module.exports后面想咋写就咋写,但最后导入时都合并成一个对象了
    
  • 引入

    let obj=require("./a.js");
    let abj=require("./a.js");
    console.log(obj.x);
    console.log(obj.addX(5));
    console.log(abj.addX(5));
    //此时的将导出的对象合并成一个对象了。 且可以不同变量引入。
    
  • 特点:不需要在服务器环境下运行,可以进入文件之间执行

2、ES6导入导出

必须在服务器上才能执行,这是es6的通病

  • 导出

    // a.js文件
    let a=50;
    export var num = 100;
    export {a};
    export {a as b};//将a赋值给b,将b导出
    
  • 导入

    import {num} from './a.js';   		//导入 num
    import {a} from './a.js';     		//导入 a
    import {b} from './a.js';	  		//导入 b, 其实还是a
    inport {a as c} from './a.js';		//导入 a赋值给c
    import {num,a,b} from './a.js';		//导入多个变量
    import * as d from './a.js';		//导入整个a.js文件,d.num, d.a, d.b
    import "./a.js";					//直接导入a.js文件。
    console.log(num);
    console.log(d.b);
    
    
  • 特殊

    //a.js
    let a=50;
    exprot default a;//也可以直接导出变量。
    // c.js
    exprot default "Rose";//默认直接导出,直接将值导出
    // d.js
    import name from './c.js'
    import age from './a.js'
    let ac = { name, age };
    export {ac as default, name, age };//变形写法
    export { default as moment } from 'moment'; // 导出重定向
    // e.js
    export { a: 'a', b: "b" }
    
    //b.html
    <script>
    	import v from "./a.js";  //此时导出的变量自定义, v = 50 
    	import c from './c.js'; // 此时c = 'Rose'
    	import d from './d.js'; // 此时d = 60
    	import {default as m} from './e.js';//变形后的导入写法
    	import { moment } from './d.js'
    	console.log(m);
    </script>
    

7、回调函数

标准回调函数:它有两个参数,第一个参数为err,第二个参数为data,err是错误信息,data则是返回的数据

function callback(err, data) {}

8、基础使用模块

一、path模块

1、文件路径

  • 当前文件夹绝对路径:console.log(__dirname, "打印当前文件绝对路径");
  • 当前文件绝对路径:console.log(__filename, "打印当前文件绝对路径");

2、api

根据路径

  • 获取文件名称:console.log(path.basename(fileName), path.basename(fileName, '.js'))

  • 获取文件所在路径:console.log(path.dirname(fileName))

  • 获取文件后缀名:console.log(path.extname(fileName))

  • 判断是否为绝对路径:console.log(path.isAbsolute(fileName))

  • 路径字符串拼接+容错处理,会校验入参

    console.log(path.join('/foo', '/src', '/test'))

  • 返回路径信息对象

    var fileInfo = path.parse(fileName)
    // 对象包含一下信息
    fileInfoObj = {
        root: "/", // 根路径
        dir: "/src/demo", // 文件所在路径
        base: "test.js", // 文件全称
        ext: ".js", // 文件后缀
        name: "test" // 文件名称
    }
    
  • 将路径信息对象转路径:path.format(fileInfo)

  • 获取跳转路径:var jumpPath = path.relative('/src/from/test', '/src/to/test')

  • 获取跳转后路径:console.log(path.resolve('/src/from/test', jumpPath))

  • 相对路径转绝对路径:path.resolve(__dirname, '../dist');

二、fs模块

1、检测

测试当前文件是否能够操作

fs.access( path, err => {
        if(err) return console.log(err);
        else { // 执行其他。}
    }
)

2、文件

  1. 创建一个文件(覆盖写入/清空文件) ——

    fs.writeFile(path, 写入内容, [数据格式], callback)

  2. 获取路径下全部文件/文件夹名称名称 —— fs.readdir(path, callback)

  3. 修改(裁剪)一个文件名称 —— fs.rename(oldPath, newPath, callback)

  4. 删除一个文件 —— fs.unlink(path, err => console.log(err))

  5. 判断是否为文件 —— fs.stat

    const fs = require('fs'); // 判断是否为文件
    function isTheFile(path) {
        fs.stat(path, (err, stats) => {
            if (err) return console.log(err);
            if (stats.isFile()) {
                console.log('是文件');
                return "file";
            }
            if (stats.isDirectory()) {
                console.log("是文件夹");
                return "directory";
            }
        })
    }
    
    
  6. 复制一个文件 —— fs.copyFile(oldPath, newPath, callback)

3、文件txt

  1. 读取文件内容 — — fs.readFile(path, (err, data) => { console.log(data) })

    默认读取二进制数据流,通过:data.toString('utf8')

    或者:fs.readFile(path, 'utf8', (err, data) => { console.log(data) })

  2. 后加写入文件内容 —— fs.appendFile(path, 写入内容, callback)

  3. 删除(清空、修改)文件内容 — — fs.writeFile(path, '修改内容,可为空', callback)

4、文件夹

同步须有异步api后加Sync

  1. 判断是否为文件 —— fs.stat

    const fs = require('fs'); // 判断是否为文件
    function isTheFile(path) {
        fs.stat(path, (err, stats) => {
            if (err) return console.log(err);
            if (stats.isFile()) {
                console.log('是文件');
                return "file";
            }
            if (stats.isDirectory()) {
                console.log("是文件夹");
                return "directory";
            }
        })
    }
    
    
  2. 读取文件/文件夹名称

    同步:const files = fs.readdirSync(‘./path’);

    异步:fs.readdir('./path', callback)

    同步直接赋值获取文件夹名称数组,异步通过回调函数获取,异步能抛出错误不阻塞。

  3. 创建一个文件夹 —— fs.mkdir(path, err => console.log(err))

  4. 修改(裁剪)一个文件夹名称 —— fs.rename(oldPath, newPath, callback)

    不会因文件夹里面含有内容就不能修改或裁剪

  5. 删除一个空文件夹 —— fs.rmdir(path, err => console.log(err))

  6. 删除一个非空文件夹 —— 需要进行递归处理

    // 部分api见下文
    const fs = require('fs');
    const path = require('path');
    
    /*
     * 删除目录、子目录,及其中的文件
     * @param dirPath {String} 要删除的目录
     */
    function deleteDirectory(dirPath) {
      return new Promise((resolve, reject) => {
          fs.access(dirPath, err => { // 测试当前文件是否能够操作
              if (err) reject(err); // 抛出错误
              fs.readdir(dirPath, (err, files) => { // 读取地址下面全部文件夹
                  if (err) reject(err); // 读取文件夹列表,出现错误抛出错误 
                  Promise.all(files.map(file => {
                      let filePath = path.join(dirPath, file)
                      return new Promise((resolve, reject) => {
                        fs.stat(filePath, (err, stats) => {
                          if (err) {
                            reject(err);
                          } else if (stats.isFile()) { // 如果是文件
                            fs.unlink(filePath, err => {
                              if (err) reject(err)
                              else resolve()
                            })
                          } else if (stats.isDirectory()) { // 如果是文件夹
                            // resolve(resolve())是会通过的
                            resolve(deleteDirectory(filePath)) // 递归调用函数
                          }
                        })
                      })
                  })).then(()=>{
                      // 如果所有上面的全部执行通过,或者files为空,则为空文件夹
                      // 如果只是空文件夹,抛出resolve, 然后执行then的回调函数
                      fs.rmdir(dirPath, err=>{ // 删除空文件夹
                          if(err) reject(err);
                          resolve()
                      })
                  }).catch(reject);
              })
          })
      })
    }
    
    module.exports = deleteDirectory; // 抛出删除函数
    
    
  7. 复制一个文件夹

    const fs = require( 'fs' );
    
    /*
     * 复制目录、子目录,及其中的文件
     * @param src {String} 要复制的目录
     * @param dist {String} 复制到目标目录
     */
    function copyDir(src, dist) {
      fs.access(dist, function(err) {
        if (err) fs.mkdirSync(dist); // 目录不存在时创建目录
        _copy(null, src, dist); // 然后调用函数,有错报错
      });
    
      function callback(err) { err && console.log(err) }
    
      function _copy(err, src, dist) {
        if (err) return callback(err); // 有错报错
        fs.readdir(src, function(err, paths) {
          if (err) return callback(err); // 有错报错
          paths.forEach(function(path) {
            const _src = src + '/' +path;
            const _dist = dist + '/' +path;
            fs.stat(_src, function(err, stats) {
              if (err) return callback(err); // 有错报错
              if(stats.isFile()) fs.copyFileSync(_src, _dist); // 判断是文件还是目录
              if(stats.isDirectory()) copyDir(_src, _dist, callback); // 当是目录是,递归复制
            })
          })
        })
      }
    }
    
    module.exports = copyDir;
    
    

三、目录树

function dirTree(pathParams, index = 0) { // 深度搜索
  let baseName = path.parse(pathParams).base;
	if(fs.statSync(pathParams).isDirectory()){ // 是文件夹
		console.log(markT(index), baseName)
		let dirLis = fs.readdirSync(pathParams);
		index++; // 进入下一级
    dirLis.forEach(value => {
      dirTree(path.join(pathParams, value), index);
    })
	}else{ // 是文件
		console.log(markT(index), baseName)
	}

  //生成等比的文件间隔符
  function markT(index){
    if(index === 0) return '你要读取的文件夹:'
    let str = '';
    for(let i=0; i < index; i++){
      str += ' |---'
    }
    return str;
  }
}

dirTree(pathParams);

四、xls文件

使用第三方模块node-xlsx

  • 在npmjs里面搜索node-xlsx使用

  • 将json转二维数组,然后可以使用xlsx.build可以生成xls/xlsx文件的二进制码

  • const data = [
        [1, 2, 3],
        [true, false, null, 'sheetjs'],
        ['foo', 'bar', new Date('2014-02-19T14:30Z'), '0.3'],
        ['baz', null, 'qux']
    ];
    const options = {
        '!cols': [
            { wch: 6 },
            { wch: 7 },
            { wch: 10 },
            { wch: 20 }
        ]
    };
    // 转换为xls文件二进制码
    var buffer = xlsx2json.build([{name: "mySheetName", data: data}], options);
    fs.writeFile('data.xlsx', buffer, 'binary', (err) => {})
    
    
  • 使用xlsx.parse可以读取xls/xlsx文件,将数据读取为二维数组

  • 再将二维数组转json即可

  • 实现了xls文件写入,读取,修改。其他的还是文件基本操作(复制,修改名称)

五、执行文件

使用内置child_process模块

调用本地应用程序

  1. 打开浏览器或其他文件

    const cp = require('child_process');
    
    cp.exec('start http://127.0.0.1:8889/');  // 自动打开默认浏览器
    cp.exec('start 音频文件地址'); // 自动打开默认播放器播放音频
    // 注意:文件地址上下级用 \\ 用双不用单,其他文件方式相同
    // 同时exec里面执行的是shell脚本命令
    
    
  2. 运行本地应用程序

    const cp = require('child_process');
    
    cp.execFile('F:\\软件包\\Dict\\YoudaoDict.exe', (err, data) => {
      err && console.log(err);
      data && console.log(data.toString());
    })
    
    

    https://blog.csdn.net/qq_30113287/article/details/108052552

六、启动服务

1、http创建

const http = require('http');
const url = require('url');

http.createServer(function(req, res){
    res.writeHeader(200, {'Content-Type':'text/javascript;charset=UTF-8'});

    // 解析 url 参数
    var params = url.parse(req.url, true).query;
    res.write("网站名:" + params.name);
    res.write("\n");
    res.write("网站 URL:" + params.url);
    res.end();

}).listen(3000);

扩展链接:https://www.cnblogs.com/zzsdream/p/11396076.html

2、express创建

express为第三方模块,需要下载

下载express —— npm install --save express

const express = require('express');
const app = express(); // 初始化一个express对象

app.listen(3000, () => { console.log('服务开启') }); // 开启一个服务器
//app.listen(端口号, 服务成功回调)

3、nodemon

进行node调试使用,建议全局安装

  • 安装命令:npm install -g nodemon
  • 使用命令:nodemon 文件地址,就是将node改为nodemon,其他相同
  • 作用:会监视代码变化,刷新服务

4、静态资源

静态资源托管

const static = express.static('文件夹路径');

app.use([path, ]static); // 使用今天资源
app.use('/public', express.static('public')); // 可以多次重复使用

// http://localhost:3000/public/index.html

七、请求交互

1、接收get请求

app.get('/user/login/:id', (req, res) => { // 创建一个/user/login接口, 并进行监听
  console.log('动态路由', req.params)
  console.log('get请求', req.query); // req.query获取入参参数
  // 然后进行逻辑操作  
  res.send('注册ok'); // 返回出参,可以是对象{err: 0, message: 'success'}
});

2、发送get请求

使用request第三方模块:https://github.com/request/request

request.get('http://localhost:3000/user/login?a=10', function(error, response, body) {
    if (!error && response.statusCode == 200) {
      console.log(body, '打印返回数据');
    }
})

3、url(内置模块)

  • url内置模块

    1. url信息转对象 —— const urlData = new URL(urlString);
    2. url对象转信息 —— urlData.toString()toJSON()
  • querystring模块

    用来处理url请求数据,不是返回数据

    1. query信息对象转字符串 —— qs.stringify(dataObj, [分隔符], [键值对连接符])
    2. query字符串转信息对象 —— qs.parse(dataString, [分割符], [键值连接符])
    3. query字符串加密 —— qs.escape(dataString)
    4. query字符串解码 —— qs.unescape(dataString)

4、接收post请求

const express = require('express');

const app = express(); // 初始化一个express对象
//用于解析x-www-form-urlencoded, 然后就可以读取req.body
app.use(express.urlencoded({ extended: false })); // 入参还有空对象是为true
//用于解析raw/json, 然后就可以读取req.body
app.use(express.json()); // 入参还有空对象是为true

app.post('/user/reg', (req, res) => {
    console.log(req.body); // 此时可以直接打印
    res.send('返回');
});

app.listen(3000);

5、发送post请求

  • 发送form请求
request.post(
    'http://localhost:3000/user/reg',
    {
      timeout: 1000,
      json: true,
      headers: { "content-type": "application/json" },
      body: { key: 'value' } // 请求发送的数据
    },
    function(error, response, body) {
      if (!error && response.statusCode == 200) {
        console.log(body, '打印返回数据');
      }
    }
)

  • 发送json请求
request.post(
    'http://localhost:3000/user/reg',
    { form: { key: 'value' } },
    function(error, response, body) {
        if (!error && response.statusCode == 200) {
          console.log(body, '打印返回数据');
        }
    }
)

6、接口分割

或者叫做接口路由分配

/user/reg接口api,可以分成两级,所以可以进行分割,避免service文件冗余

  • 路由js文件
// user.js文件,路由文件

// 新建router文件夹,建立user.js文件
const express = require('express');
const router = express.Router(); // 创建router对象。

// 编辑user下一级的接口api
router.get('/add', (req, res) => {
  console.log(req.query);
  res.send({ type: 'add' });
})

router.post('/updata', (req, res) => {
  console.log(req.body); // 如果入口service已经完成post数据解析,就不需要再次进行
  res.send({ type: 'updata' });
})

module.exports = router; // 导出router

  • 服务文件
const express = require('express');
const userRouter = require('./router/user'); // 引入user下的router

const app = express(); // 初始化一个express对象
const bpp = express(); // 再创建一个bpp,不建议创建一个express对象承载路由

// 处理post请求数据
app.use(express.urlencoded({ extended: false })); // 入参还有空对象是为true
app.use(express.json());

bpp.get('/login', (req, res) => {
    console.log(req.body); // 此时可以直接打印
    res.send('返回');
})

app.use('/use', userRouter); // 使用userRouter
app.use('/home', bpp); // 使用bpp, 这样就可以使用/home/login访问接口
app.use(['/one', '/tow'], bpp); // 同理

app.listen(3000);

7、其他

其他请求和发送可以类推或者查看文档

res的方法:

  • res.send(返回值):常用的返回,可以返回对象,字符串,数组,buffer对象,模板页面
  • res.json(返回值):可以返回对象,数组,json数据
  • res.jsonp(返回值):jsonp的返回
  • res.status(状态值):设置接口返回的状态值
  • res.end():结束接口返回
  • res.status(404).end()
  • res.render('ejs或者html地址')
  • res.type('octets/stream'):设置content-type,返回数据类型

8、处理错误

const createError = require('http-errors')
// 放在所以接口的最下面,找不到接口是返回404
app.use(function (err, res, next) {
    next(createError(404))
})

let { status, message } = createError(400, "服务器不理解请求的语法");
//此时status就是错误状态,message就是生成的错误提示

八、中间件

中间件就是接口接收前拦截器。

1、全局中间件

// 自定义全局中间件
app.use('/', (req, res, next) => {
  console.log('中间件拦截');
  // 可以进行token验证等身份信息确认拦截,判断是否执行next()
  const { token } = req.headers;
  if (token) next(); // 是否继续往下执行,有就执行,没有就不执行
  else res.send({ code: 400, message: 'token is undefined' });
});

// 进行token拦截
// 如果第一个参数是'/', 则可以省略,原来是函数重载
// 所以body-parser, api路由载体router, 都是中间件

2、局部中间件

// 自定义局部中间件
// 中间件个数可为多个 app.get('pathName', fun, fun, fun...);
router.get('/add', (req, res, next) => {
  // 为局部中间件
  const { user } = req.query;
  if (user === '123' ) next();
  else res.send({ code: 400, message: 'user is wrong' });
}, (req, res) => {
  console.log(req.query);
  res.send({ type: 'add' });
})

3、内置中间件

如上面介绍的使用静态资源

九、解决跨域

app.all("*",function(req,res,next){
  //设置允许跨域的域名,*代表允许任意域名跨域
  res.header("Access-Control-Allow-Origin","http://localhost:8081");
  //允许的header类型
  res.header("Access-Control-Allow-Headers","content-type");
  //跨域允许的请求方式
  res.header("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS");
  res.header("Content-Type", "application/json;charset=utf-8");
  if (req.method.toLowerCase() == 'options')
    res.send(200);  //让options尝试请求快速结束
  else
    next();
});

// 也可也上面的回调函数单独定义,使用app.use('/', callback)实现

十、上传下载

  • 文件下载

    res.get('/use/download', (req, res) => {
        const reqBody = req.body;
        res.download('下载资源地址'[, '下载文件重命名'][, 报错回调函数])
    })
    
    // get和post的res都有download方法,直接使用就可以
    
    
  • 文件流下载

    req.get('/use/download', (req, res) => {
       res.set({
           // 告诉浏览器这是一个二进制文件
          "Content-Type":"application/octet-stream",
           // 告诉浏览器这是一个需要下载的文件
          "Content-Disposition":"attachment; filename=1.xls"
        });
        res.send(二进制流);
        // 二进制流获取
        // 使用这个api获取文件流 fs.readFile默认读取文件二进制流
        // 或者生成xls文件流
    })
    
    
  • 文件上传

    文件上传使用multer第三方模块:

    文档链接:https://github.com/expressjs/multer/blob/master/doc/README-zh-cn.md

    1. 设置文件存储路径已经文件名称

      const multer  = require('multer'); // 引入multrer
      
      // 使用硬盘存储模式设置存放接收到的文件的路径以及文件名
      var storage = multer.diskStorage({
          destination:function (req, file, cb) {
              // 设置文件存储路径
              // 接收到文件后输出的保存路径(若不存在则需要创建)
              cb(null,'upload/');
          },
          filename:function (req, file, cb) {
              // 设置文件存储名称
              // 将保存文件名设置为 时间戳 + 文件原始名,比如 151342376785-123.jpg
              cb(null, Date.now() +"-" + file.originalname); 
          }
      });
      
      
    2. 检测存储路径是否被创建

      var createFolder =function(folder){
          try{
              // 测试 path 指定的文件或目录的用户权限,我们用来检测文件是否存在
              // 如果文件路径不存在将会抛出错误"no such file or directory"
              fs.accessSync(folder);
          }catch(e){
              // 文件夹不存在,以同步的方式创建文件目录。
              fs.mkdirSync(folder);
          } 
      };
      
      
    3. 创建multer对象

      // 创建 multer 对象
      var upload = multer({ storage: storage });
      
      /* POST upload listing. */
      app.post('/upload', upload.single('file'), function(req, res, next) {
          var file = req.file;
          console.log('文件类型:%s', file.mimetype);
          console.log('原始文件名:%s', file.originalname);
          console.log('文件大小:%s', file.size);
          console.log('文件保存路径:%s', file.path);
          // 接收文件成功后返回数据给前端
          res.json({res_code:'0'});
      });
      
      
    4. 测试

      <form action="http://localhost:3000/upload" method="post" enctype="multipart/form-data">
          <input type="file" name="logo" />
          <input type="submit" value="提交">   
      form>
      
      

十一、apidoc

使用插件apidoc自动生成接口文档

参考1:https://blog.csdn.net/qq_32352777/article/details/102746237

  • 全局下载:cnpm install -g apidoc

  • 根据参考1:在项目根目录创建并写入apidoc.json

  • 在需要的接口上面写入注释

  • 常用的注释模板

    /**
     * @api { 请求类型 } 接口地址 接口名称
     * @apiGroup 接口分类名称
     * @apiVersion 接口版本号
     * @apiDescription 接口描述
     * @apiName 接口名
     * @apiParam (入参传入位置) { 入参数据类型 } 入参参数名称 入参描述
     * @apiParam (params) { String } petId 请求参数2
     * 
     * @apiSuccess { 出参数据类型 } 出参参数名称 出参描述
     * @apiSuccess { String } msg 接口返回信息
     * @apiSuccessExample { json } 出参参考模板名称:
     *    {
     *      "code" : "0",
     *      "msg" : "注册成功"
     *    }
     * @apiSuccessExample { json } Error-Response:
     *    {
     *      "code" : "-1",
     *      "msg" : "注册失败"
     *    }
    */
    
    
  • 生成模板

    可以命令行操作,也可以在package.json里面配置

    "scripts": {
        "docs": "apidoc -i ./src/routes -o ./public/docs"
      },
    
    

十二、代理

  • 代理

    转接请求,使用http-proxy-middleware第三方模块

    文档:https://www.npmjs.com/package/http-proxy-middleware

    const proxy = require('http-proxy-middleware')
    
    app.use('/api', proxy({
        target: "http://www.baidu.com", // 目标主机
    	changeOrigin: true, // 虚拟托管站点需要
        ws : true ,  // 代理 websockets
    	pathRewrite: { // 重写路径, 如下, 转接请求是
    		"^/api": "/"
    	},
        router : { 
            // 当request.headers.host == 'dev.localhost:3000',
            // 将目标 http://www.baidu.com' 覆盖为 'http://localhost:8000' 
            'dev.localhost:3000' : 'http://localhost:8000' , 
        },
    }))
    
    

9、方法

第三方模块查找访问网站:www.npmjs.com

一、环境/全局变量

使用第三方插件:cross-env

使用:在package.json文件内使用

"scripts": {
    "dev": "cross-env abc='development'  node ./src/main.js",
    "test": "echo \"Error: no test specified\" && exit 1"
},

然后就可以在代码里面打印变量了,通常设置NODE_ENV,这里设置abc为了演示

console.log(process.env.abc)

二、验证码

  1. 邮箱验证码 —— Nodemailer

    const nodemailer = require("nodemailer"); // 创建发送的请求对象
    let transporter = nodemailer.createTransport({  
        // 在node_modal\\nodemailer\\lib\\well-know-services.json查找对应邮箱的host
        host: "smtp.qq.com", // 发送方邮箱
        port: 465, // 端口号
        secure: true, // true for 465, false for other ports
        auth: {
            	user: '[email protected]', // 发送方的邮箱地址
            	pass: 'qrdccraawqnifjgj', // smtp 验证码
            },
        }); 
    
    // 邮件信息编写
    const mailobj = {
        from: '"Fred Foo " <[email protected]>', // '"发送方名称" <邮箱号>'
        to: "[email protected]",
        // 群发字符串用逗号拼接--"[email protected], [email protected]"  
        subject: "验证标题", // 邮件标题  
        // 注意:text和html只能存一个, html权限大于text  
        // text: "你的验证码是123456", // 邮箱文字内容 只能字符串格式  
        html: `
    Hello world?
    `
    , // html body attachments: [ { filename : 'content', // 附件名称 content : '发送内容' // 附件文字内容 }, { filename: 'packjson', // 附件名称 path: './package.json', // 附件本地地址 }, { filename: "test.JPG", path: "./src/assets/images/timg.jpg", cid: "01", // 邮箱正文插入图片 } ] }; // 发送邮件,不要循环发送,会封 transporter.sendMail( mailobj, (err, data) => { err && console.log(err); // 发送失败后报错 data && console.log(data); // 发送成功后返回信息 } );

    api:https://nodemailer.com/about/

  2. 短信验证码 —— 没钱买服务

三、token

使用jwt-simple第三方模块生成token

const jwt = require('jwt-simple')

const str = '12345678' // 秘钥
const res = { name: 'admin', lastTime: '15:39' } // 需要存储的信息
// 进行加密
const token = jwt.encode(res, str)
console.log(token, '--------加密')

// 进行解密
const word = jwt.decode(token, str)
console.log(word, '---------解密')

// 然后通过时间判断是否token过期

四、WebSocket

使用第三方插件:nodejs-websocket

教程:https://www.cnblogs.com/yangsg/p/10675533.html

进阶:图片或文件传递

conn.on("binary", function (inStream) {
    var data = Buffer.alloc(0)

    // 处理二进制数据
    inStream.on("readable", function () {
        var newData = inStream.read()
        if (newData) data = Buffer.concat([data, newData], data.length+newData.length)
    })

    // 返回二进制数据
    inStream.on("end", function () {
        console.log("Received " + data.length + " bytes of binary data")
        console.log('发生数据给game2')
        game2.sendText('接受数据')
        game2.sendBinary(data)
    })
})

game1:修改部分

ws.onmessage = function (e) {
    mess.innerHTML = "连接成功"
    document.querySelector(".kuang").onclick = function (e) {
      var time = new Date();
      ws.send(time + "  game1点击了“" + e.target.innerHTML + "”");
    }

    var inputElement = document.getElementById("file");
    inputElement.onchange = function (e) {
      var file = (e.target.files)[0]
      ws.send(file);
    }
}

game2: 修改部分

ws.onmessage = function (e) {
    if (typeof (e.data) === 'string') {
      var time = new Date();
      mess.innerHTML += time + "的消息:" + e.data + "
"
} else { var reader = new FileReader(); reader.onload = function (evt) { if (evt.target.readyState == FileReader.DONE) { var url = evt.target.result; alert(url); var img = document.getElementById("imgDiv"); img.innerHTML = "+url+" />"; } } reader.readAsDataURL(e.data); } }

此时就完成了图片等等文件的传递

五、开发模块

开发第三分模块并上传npm

  1. npm注册账号,npm主页面搜索框右边就有注册按钮
  2. 使用npm init创建环境
  3. 在项目文件中,package.json中 private 属性值改为false或者删除
  4. 开发自己的项目,就是随便到处一个函数,哪怕是简单的加分函数
  5. 开发完成后:npm login,根据提示登录npm
  6. 使用命令行:npm adduser,在注册表注册npm账户信息
  7. 使用命令行:npm publish,将自己的项目提交到npm

六、md5加密

  1. 将密码和秘钥合并进行md5加密后存储起来
  2. 验证时通过再次加密后和存储对比匹配
  3. 使用
const md5 = require('md5');

const str = '123456'
console.log(md5(str)) // 获取到加密后数据

七、json转二维

/**
 * json,二维数组相互转换
 * 
 * @param { Array } data // 需要转换的数据
 * @param { Array } getArr // 需要提取的值,会按照数组顺序提取
 * @param { String } type // 为json时:jsonToArray,为array时:arrayToJson。
 * 
 * @return { Array } // 返回二维数组
 * 
 * 示例:
 * const getArr = jsonSwitchArr(json, ["user", "password"], 'json')
 * const getJson = jsonSwitchArr(arr, ["user", "password"], 'array')
 */
function jsonSwitchArr (data, getArr, type) {
  const res = []
  if (Object.prototype.toString.call(data) !== "[object Array]") return res
  if (Object.prototype.toString.call(getArr) !== "[object Array]") return res

  if (type === 'json') {
    data.forEach(item => {
      let child = []
      getArr.forEach(key => { child.push(item[key] || '') })
      res.push(child)
    })
  }
  if (type === 'array') {
    data.forEach(item => {
      let obj = {}
      getArr.forEach(key => { obj[key] = item || '' })
      res.push(obj)
    })
  }
  return res
}


10、mongodb

1、下载安装mongodb

官网:https://www.mongodb.com/download-center/enterprise

安装时不要勾选install mongodb compass,点击后下载官方可视化管理工具,安装不上。

安装教程:https://www.cnblogs.com/zhif97/p/12806245.html

2、操作数据库

  1. 重新打开一个cmd控制台输入
  2. mongo进入环境,开始进行增删改成操作
  3. show dbs :显示所有的数据库
  4. db:显示当前正在使用的数据库
  5. use 数据库名:切换到指定的数据库,有就切换,没有就创建切换
  6. db.dropDatabase():删除当前执行的数据库

3、集合及数据操作

1、创建集合

  1. db.createCollection("集合名",{capped:true,size:1024\*30}) :创建一个集合

    capped:true.指定库的大小,默认false,即自动分配

    size:1024*30这是30m的库,基于kb的

    show collections : 查看当前使用集合

    了解就行,不需要记,基本是多余的一步

2、创建并写入数据

  1. db.集合名.insertOne(写入集合的一条数据):像集合写入一条数据,没有此集合会先创建再写入
  2. db.集合名.insertMany(数组对象):写入多条数据,同上
  3. 写入数据不是覆盖式写入,而是追加写入

3、查看、切换集合

  1. show collections:查看当前库里面所有的集合
  2. use 集合名:切换到指定的集合

4、删除集合

  1. db.集合名.drop():删除指定的集合,但必须是切换到集合所在库的环境下进行。
  2. db.集合名.remove({}):清空集合内的所有数据

5、查看数据

注意:由于写入的是键值对,所有查找的条件也必须是{键值对}。

  1. db.集合名.find():查看集合全部数据
  2. db.集合名.find(条件):按条件进行精确查找
  3. db.集合名.find(条件1,条件2) 查找满足条件1且满足条件2
  4. db.集合名.find($or:[条件1,条件2]) 查找满足条件1或者条件2
  5. db.集合名.findOne(条件):查找显示符合条件的第一条数据
  6. db.集合名.find({"键":{$regex:"值"}}):按条件模糊查询
  7. db.集合名.find({"键":正则表达式})使用正则条件查询
  8. db.集合名.find({"age":{$gt:20}}):查找年龄大于20
  9. $gt:大于
  10. $lt:小于
  11. $gte:大于等于
  12. $lte:小于等于
  13. $ne:不等于
  14. $eq:等于
  15. db.集合名.find().sort({age:1}):按照年龄的正序查找
  16. db.集合名.find().sort({age:-1}) :按照年龄的倒序查找
  17. db.集合名.find().count() :获取集合有条数
  18. db.集合名.find().skip(数字):获取指定条后的数据
  19. db.集合名.find().limit(数字):指定获取多少条数据
  20. db.集合名.find().skip(数字).limit(数字):获取指定条后的几条数据
  21. 以上find()后面的都是可以混合着写的。
  22. db.集合名.find({field: {$in: [ '值1', '值2' ]}}); field只要和array中的任意一个value相同,那么该文档就会被检索出来 —— 包含
  23. $nin —— 不包含

6、删除数据

db.集合名.remove(条件):删除指定条件的数据

db.集合名.deleteMany({}):删除所有数据

db.集合名.deleteMany({条件对象}):删除所有符合条件的数据

其他的和查找数据一样,

7、修改数据

  1. db.集合名.update(筛选条件,{重新写入内容},false):重新写入筛选出来的数据。默认false,即筛选不到不创建。改成true就会创建
  2. db.集合名.update(筛选条件,($set{重新部分内容})):修改部分内容。同上

8、索引

为集合建立索引,基于title(还可以是id,就是建立主键)作用是加快查询

>db.col.createIndex({"title":1})

9、聚合

MongoDB中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。

> db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : 1}}}])
{
   "result" : [
      {
         "_id" : "runoob.com",
         "num_tutorial" : 2
      },
      {
         "_id" : "Neo4j",
         "num_tutorial" : 1
      }
   ],
   "ok" : 1
}
>

表达式 描述 实例
$sum 计算总和。 db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", num_tutorial : { s u m : " sum : " sum:"likes"}}}])
$avg 计算平均值 db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", num_tutorial : { a v g : " avg : " avg:"likes"}}}])
$min 获取集合中所有文档对应值得最小值。 db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", num_tutorial : { m i n : " min : " min:"likes"}}}])
$max 获取集合中所有文档对应值得最大值。 db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", num_tutorial : { m a x : " max : " max:"likes"}}}])
$push 在结果文档中插入值到一个数组中。 db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", url : { p u s h : " push: " push:"url"}}}])
$addToSet 在结果文档中插入值到一个数组中,但不创建副本。 db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", url : { a d d T o S e t : " addToSet : " addToSet:"url"}}}])
$first 根据资源文档的排序获取第一个文档数据。 db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", first_url : { f i r s t : " first : " first:"url"}}}])
$last 根据资源文档的排序获取最后一个文档数据 db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", last_url : { l a s t : " last : " last:"url"}}}])

4、数据可视化

教学网站:https://blog.csdn.net/weixin_39999535/article/details/81383196

上面的软件是付费使用的,还有免费的软件NoSQLBooster

软件的使用基本一样,链接数据库需要百度看看

上面的增删改查(CURD)操作的mongodb语法上面有


11、mongoose

使用插件Mongoose:http://www.mongoosejs.net/docs/guide.html

1、链接数据库

const mongoose = require('mongoose'); // 引入mongoose

// 通过将useNewUrlParser设置为true来避免“不建议使用当前URL字符串解析器”警告
mongoose.connect('mongodb://localhost/runoob', { useNewUrlParser: true, useUnifiedTopology: true }); // 链接数据库

const db = mongoose.connection; // 数据库链接对象

db.on('error', console.error.bind(console, 'connection error')); // 数据库链接失败钩子
db.once('open', () => { // 数据库链接成功钩子
  console.log('数据库链接成功');
})

2、定义schema对象

mongodb每个数据库里面有多个集合(表),需要先拟定一个表结构。schema就是表结构对象

值得注意的是:此时定义的表结构和数据库还没有关联在一起

const Schema = mongoose.Schema;

const userSchema = new Schema({
  use: { type: String, required: true }, // 表头use, 类型字符串,必填项
  password: { type: String, required: true }, // 表头password
  age: Number, // 表头age, 类型Number, 非必填
  sex: { type: Number, default: 0 }, // 性别,数字类型,默认为无性别
  date: { type: Date, default: new Date() } // 时间,时间类型,默认当前时间
})

3、链接集合

就是把定义的表结构Schema和集合关联在一起

// 将定义的表结构和集合名进行链接 (集合名,定义的表结构对象)
// 有集合则会根据定义的表结构写入数据,不会改变原有表结构数据
// 没有集合则创建新集合
const User = mongoose.model('user', userSchema);

4、数据操作

1、增加

// 数据插入操作,入参为json数据
User.insertMany([{ use: '张旭', password: '123', age: 12 }])
  .then(data => { console.log(data, "数据插入成功") })
  .catch(err => console.log(err))

2、查看

// 数据查询操作
User.find(条件) // 和mongodb的选择语法一样
  .then(data => console.log(data, '数据查询成功'))
  .catch(err => console.log(err))

// 数据分页查询操作
// 就是通过链式调用完成 skip为跳过几条数据,limit为返回几条数据
User.find().skip(1).limit(2)
  .then(data => console.log(data, '数据查询成功'))
  .catch(err => console.log(err))
  
// 数据模糊查询操作 --- 同上原理,再find参数里加mongodb模糊语法
// 指定条数返回查询 --- 同上原理,仅调用limit即可

// 获取全部总条数
User.count()
  .then(total => console.log(total, total/10, '查询全部数据总数'))
  .catch(err => console.log(err));

3、修改

// 数据修改操作 --- 尽量通过id进行修改 或 多条件匹配修改
User.updateMany({ use: '憨憨' }, { use: '笨笨'})
  .then(data => { console.log(data, '数据修改成功') })
  .catch(err => console.log(err));

4、删除

// 数据删除操作
User.remove() // 和mongodb的选择语法一样
  .then(data => console.log(data, '数据删除成功'))
  .catch(err => console.log(err))

// 数据批量删除
User.deleteMany() // 和mongodb的语法一样
  .then(data => console.log(data, '数据批量删除成功'))
  .catch(err => console.log(err))

5、导入导出

  • xls数据导入到数据库
    1. xls通过node-xlsx转换为二维数组
    2. 二维数组去掉头部信息
    3. 将二维数组转换为json
    4. 然后使用批量写入mongoose
  • 将数据库数据导出json
    1. 读取mongoose数据为json
    2. 将json转为二维数组
    3. 设置每个行的宽度,导出的表名称
    4. 将二维数组通过node-xlsx转换为xls二进制文件流
    5. 通过文件流下载,到处xls文件

12、express脚手架

  • 安装:npm install express-generator -g
  • 创建项目:express app

你可能感兴趣的:(前端学习,node.js,javascript)