node.js第三天(实现跨域、解决回调黑洞、BE开始)

85 阅读7分钟
  • express:设计接口特别方便
let express = require("express");
let app = express();
app.listen(3000, () => console.log("启动成功, http://localhost:3000"));

// 设计接口:每个接口都是单独设计
//   获取POST参数:
//     设置中间件:app.use(express.urlencoded({ extended: false }));
//     req.body 请求体

//   获取动态URL地址参数
//     后台设计:/xxx/xxx/xxx/:参数名
//     前端:   /xxx/xxx/xxx/值
//     req.params.参数名

//   获取get参数
//     前端:URL地址后面 查询字符串
//     req.query
  • 接口:路由;一级路由 二级路由的设计:提高访问接口的性能!
// 主JS文件
let loginRouter = require('./routers/login.js');  
app.use('/api', loginRouter); // 设计一级路由 ,后面引入细分二级路由;



// 细分二级路由文件: 习惯routers文件
const express = require('express');
const router = express.Router();
router.get("/xxx",function(req,res){
});
module.exports = router;
  • 中间件:在进入处理响应数据函数直接 一些函数;

    • 当要求所有接口都有什么功能的时候,在这些接口之前,设置中间件
    app.use(function(req,res,next){req,res每次请求的请求对象和响应对象   next()})
    

跨域

同源

  • IP:服务器,性能特别好电脑 【一个房子】
  • 服务器上放在:项目文件夹 / 前端写的页面 && 后台写代码文件;
  • 启动:启动服务器一个端口;服务器上很多端口0-65535
  • 情形:
    • A公司:
      • 自己服务器上某个端口下 ,部署了一些代码。
      • 代码包括前端代码和后台,都在同一个地方,同一个服务器,同一个端口;
      • 前端 代码里:肯定写ajax的一些后台的url接口地址;
    • B公司:
      • B公司前端开发人员,可以打开A公司发布的域名地址;
      • B公司前端开发人员,就可以看到 A公司前端页面内的 接口地址
      • 复制 A公司前端页面内的 接口地址,可以放在 B公司 前端页面内;
      • 可以获取A公司的数据么?不可以! 同源策略
  • 同源策略:
    • 是浏览器提供的一种安全机制,限制来自不同源的数据。如果当前页面的URL地址和Ajax的请求地址不同源,浏览器不允许得到服务器的数据;
    • 同源:协议http||https、 IP 和 端口 都相同则同源,只要有一项不同就是不同源;
  • 跨域:当前显示的页面中,如果触发了一个Ajax请求,如果请求的URL地址和当前页面的URL地址不同源就是跨域;浏览器不允许跨域获取数据,但是实际开发会经常有这个跨域技术;
    • XHR 获取数据资源
    • 获取文件资源 JSONP
  • 解决:
- jsonp : 需要前端和后端的配合才可以 
	dataType:"JSONP",
    动态创建script 设置src 告诉给后台我们前端有个函数名
    返回的文件资源:JS代码  函数名(需要数据)
	
    
	
- cors  : 纯粹的后端技术;如果后端实现了cors配置,那么 前端就可以直接跨域获取对应的数据
	ajax Day01: 图书管理其实就是后台允许跨域,配置cors

    
    
- webpack : 前端工程化构建工具
   - css:兼容 自动给生成新的css文件内 兼容性处理
   - 前端代码会压缩,里面变量:没有语义化  abcde... 混淆
   - 配置项(学会如何配置)
   - 代理:配置项 测试开发过程;

cors

  • 由于XHR对象被W3C标准化之后,提出了很多XHR Level2的新构想,其中新增了很多新方法(onload、response....)和CORS跨域资源共享。浏览器升级后开始支持CORS方案。
  • CORS才是解决跨域的真正解决方案。
  • 代码:
    • 前端:正常请求;
    • 后台:设置响应头;req res 告诉浏览器 允许你拿到数据;
// ---------------------------------前端代码
$.ajax({
    url: 'http://localhost:3006/api/cors',
    success: function (res) {
        console.log(res)
    }
});
// ---------------------------------后台代码
const express = require('express');
const app = express();
app.listen(3006, () => console.log('启动了'));

// 测试接口(测试cors)
app.get('/api/cors', (req, res) => {
    // 任何请求来源都可以获取响应的数据
    res.setHeader('Access-Control-Allow-Origin', '*');
    
    res.send('hi,你看到我了');
});
  • 如果所有的url接口都要求允许跨域:
app.get('/api/cors/a', (req, res) => {
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.send('hi,你看到我了');
});
app.get('/api/cors/b', (req, res) => {
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.send('hi,你看到我了');
});
...

// --------------------------改完我们中间件
app.use((req, res,next) => {
    res.setHeader('Access-Control-Allow-Origin', '*');
    next();
});
app.get('/api/cors/a', (req, res) => {
    res.send('hi,你看到我了');
});
app.get('/api/cors/b', (req, res) => {
    res.send('hi,你看到我了');
});
  • 或者采用第三方模块,作为中间件使用:cors
// npm i cors

const cors = require("cors");
app.use(cors());   // 相当于是上面的封装,当然人家这个更加优化

JSONP

  • JSONP:本质上不是解决跨域,绕过同源跨域,使用的另外一个方式去实现获取数据!

  • 实现原理:利用 script 标签的 src 属性,去请求一个接口,因为src属性不受跨域影响。返回一句函数执行;函数执行传入的实参就是后台返回的数据;

  • 代码:

    • 前端:JSONP方式请求
    • 后台:设置专门的代码
// ---------------------------------------前端代码
$.ajax({
    dataType: 'jsonp', // 指定该项,表示发送JSONP请求
    url: 'http://localhost:3006/api/jsonp',
    success: function(res) {
        console.log(res)
    },
});
// ----------------------------------------测试接口(测试jsonp)
app.get('/api/jsonp', (req, res) => {

    // 接收请求的url中的参数
    // let fn = req.query.callback; 
    // res.send(fn + '({ status: 0, message: "登录成功" })');

    res.jsonp({ status: 0, message: '登录失败!!!' });
})

ES6:Promise

  • ES6语法:语法!语法糖!

回调黑洞

  • 需求:有 a.txt、b.txt、c.txt三个文件,使用fs模块按照顺序来依次读取里面的内容并打印:
// 将读取的a、b、c里面的内容,按照顺序输出
const fs = require('fs');

// 读取a文件
fs.readFile('./a.txt', 'utf-8', (err, data) => {
    if (err) throw err;
    console.log(data.length);
    
    // 读取b文件
    fs.readFile('./b.txt', 'utf-8', (err, data) => {
        if (err) throw err;
        console.log(data);
        
        // 读取c文件
        fs.readFile('./c.txt', 'utf-8', (err, data) => {
            if (err) throw err;
            console.log(data);
        });
    });
});
  • 从上面代码可以看出,回调函数内部嵌套另外的异步请求后的回调函数,代码在表现上较为复杂不易维护,为回调黑洞;

基本使用

  • 语法糖
  • ES6的语法:注意是JS的核心语法,不是node运行环境所有,也是和 {} []一样,前后端都是可以使用的!
  • Promise是什么:构造函数;直译【承诺】
  • 意义:解决回调黑洞的问题,以更为合适的方式写异步代码,目标是更加容易维护代码;
    • 写还是异步代码;
    • 形式:发生改变,看起来更加舒服!更加好维护!
  • 1.对异步的代码进行包装,设置【承诺】
// 实例化一个Promise;
//    1.函数作为参数,
//    2.而该函数又有两个形参,

let p = new Promise((resolve, reject) => {
    
    // 形参resolve,单词意思是 完成
    // 形参reject ,单词意思是 失败
    fs.readFile('./a.txt', 'utf-8', (err, data) => {
        
        if (err) {
            // 失败,就告诉别人,承诺失败了
            reject(err);
        } else {
            // 成功,就告诉别人,承诺实现了
            resolve(data.length);
        }  
    });
});
  • 2.调用:兑现
// 通过调用 p 的then方法,可以获取到上述 “承诺” 的结果
p.then(res => {
    console.log(res)
    return promis
})
.catch(err => {
    console.log(err)
})
  • 文件读取的改造:
const fs = require('fs');

// 1. 定义一个承诺
let p = new Promise((resolve, reject) => {
    // resolve -- 解决,完成了; 是一个函数
    // reject  -- 拒绝,失败了; 是一个函数
    // 异步操作的代码,它就是一个承诺
    fs.readFile('./a.txt', 'utf-8', (err, data) => {
        if (err) {
            reject(err);
        } else {
            resolve(data.length);
        }
    });
});

// 2. 兑现承诺
p.then((data) => {
        console.log(data);
    });
  • 前一个then里面返回的Promise对象,并且调用resolve的时候传递了数据,数据会被下一个then接收到
const fs = require('fs');

// 1. 定义一个承诺
let a_read = new Promise((resolve, reject) => {
    fs.readFile('./a.txt', 'utf-8', (err, data) => {
        if (err) {
            reject(err);
        } else {
            resolve(data.length);
        }
    });
});
let b_read = new Promise((resolve, reject) => {
    fs.readFile('./b.txt', 'utf-8', (err, data) => {
        if (err) {
            reject(err);
        } else {
            resolve(data.length);
        }
    });
});
let c_read = new Promise((resolve, reject) => {
    fs.readFile('./c.txt', 'utf-8', (err, data) => {
        if (err) {
            reject(err);
        } else {
            resolve(data.length);
        }
    });
});


// 2.调用
a_read
    .then((data) => {
        console.log(data);   // a的数据
        return b_read;
    })
    .then((data) => {
        console.log(data);   // b的数据
        return c_read;
    })
    .((data) => {
        console.log(data);   // c的数据
    });

优化封装

function read(path) {
    return new Promise((resolve, reject) => {
        fs.readFile(path, 'utf-8', (err, data) => {
            err ? reject(err) : resolve(data.length);
        })
    });
}

//-----------------------------------------形式1
let a_read = read('./a.txt')
let b_read = read('./b.txt')
let c_read = read('./c.txt')

// 2.调用
a_read
    .then((data) => {
        console.log(data);   // a的数据
        return b_read;
    })
    .then((data) => {
        console.log(data);   // b的数据
        return c_read;
    })
    .then((data) => {
        console.log(data);   // c的数据
    });

//-----------------------------------------形式2
read('./a.txt')
    .then((data) => {
        console.log(data);   // a的数据
        return read('./b.txt');
    })
    .then((data) => {
        console.log(data);   // b的数据
        return read('./c.txt');
    })
    .then((data) => {
        console.log(data);   // c的数据
    });

错误获取

// 统一的获取
A.then(function(data) {
    console.log(data);
    return B;
  })
  .then(function(data) {
    console.log(data);
    return C;
  })
  .then(function(data) {
    console.log(data);
  })
  .catch(function(err) {
    console.log(err);
  })

ES7:async/await

关键字

  • async 和 await 是 ES2017 中提出来的。

  • 异步操作是 JavaScript 编程的麻烦事,麻烦到一直有人提出各种各样的方案,试图解决这个问题。从最早的回调函数,到 Promise 对象,每次都有所改进,但又让人觉得不彻底。它们都有额外的复杂性,都需要理解抽象的底层运行机制。

  • 不就是读取一个文件吗,干嘛要搞得这么复杂?async 函数就是隧道尽头的亮光,很多人认为它是异步操作的终极解决方案;

  • ES2017提供了async和await关键字:

    • async 用于修饰一个 function,表示该函数里面有异步代码执行操作(Promise的调用)
    • await 需要定义在async修饰函数的内部,await后面跟的一般都是一个函数的调用 (函数里面包含有Promise) 【await后面:promise实例化对象】
    • 如果有哪一个await操作出错了,会中断async函数的执行

使用

const fs = require('fs');

// 将异步读取文件的代码封装
function myReadFile (path) {
    return new Promise((resolve, reject) => {
        fs.readFile(path, 'utf-8', (err, data) => {
            err ? reject(err) : resolve(data.length);
        });
    });
}

// 1.async后的函数表示里面有异步操作
async function abc () {
    // 2.等待这个异步操作返回结果!我们现在写的异步,但是完全是同步的方式写异步!!
    let a = await myReadFile('./a.txt');
    let b = await myReadFile('./b.txt');
    let c = await myReadFile('./c.txt');
    console.log(b);
    console.log(a);
    console.log(c);
}

abc();
  • await 只会等待 promise 成功的结果, 如果失败了会报错, 需要 try catch
async function abc () {
    try {
        let a = await myReadFile('./a.txt');
        let b = await myReadFile('./b.txt');
        let c = await myReadFile('./c.txt');
        console.log(b);
        console.log(a);
        console.log(c);
    }
    catch (err) {
        console.log('读取文件失败了', err)
    }
}

数据库优化