20230728----重返学习-跨域-模块化-webpack初步

day-122-one-hundred-and-twenty-two-20230728-跨域-模块化-webpack初步

跨域

  • 跨域
  1. 为什么要跨域?
    • 浏览器为了安全,不能让我们的html文件可以随意引用别的服务器中的文件,只允许我们的html或js文件中,请求我们自己服务器。这个就是浏览器的同源策略。
    • 因为我们的网页是一个html文件,这个html是在一个域名里的。而这个html会引用各种文件,如图片、js文件、css文件,这些文件,有时候并不在一个服务器里。
    • 所以我们就需要去到其它服务器中查找这些文件。
  2. 跨域的方案有:
    1. CORS-跨源资源共享方案
    2. JSONP带填充的json方案

CORS

  1. CORS 是一种官方的跨域解决方案,通过在服务器端添加一些 HTTP 头信息来告诉浏览器,允许哪些网站可以访问这些资源。

CORS针对get请求

  • fang/f20230728/f20230728/1.get.html 前端要写的代码。

    DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>get - http://127.0.0.1:5500/title>
      head>
      <body>
        请求体 - http://127.0.0.1:5500/
      body>
      <script>
        let xhr = new XMLHttpRequest();
        xhr.open("GET", "http://localhost:8080", true);
        xhr.onload = () => {
          console.log(`接收到的返回体: xhr.responseText-->`, xhr.responseText); //报错: Access to XMLHttpRequest at 'http://localhost:8080/' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
        };
        xhr.send();
      script>
    html>
    
  • fang/f20230728/f20230728/1.server.js 后端要写的代码。

    const http = require('http')
    http.createServer((req, res) => {
      res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500')
      res.end('from: http://localhost:8080')
    }).listen(8080, () => {
      console.log(`服务器地址为: http://localhost:8080`);
    })
    

CORS针对post请求

  • fang/f20230728/f20230728/2.post.html

    DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>http://127.0.0.1:5500/title>
      head>
      <body>
        请求体 - http://127.0.0.1:5500/
      body>
      <script>
        let xhr = new XMLHttpRequest();
        xhr.open("POST", "http://localhost:8080/users", true);
        xhr.setRequestHeader("Content-Type", "application/json");
        xhr.onload = () => {
          console.log(`接收到的返回体: xhr.responseText-->`, xhr.responseText); //报错: Access to XMLHttpRequest at 'http://localhost:8080/' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
        };
        xhr.send(JSON.stringify({ name: "zhufeng1" }));
      script>
    html>
    
  • fang/f20230728/f20230728/2.server.js

    const http = require('http')
    http.createServer((req, res) => {
      res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500')
      // res.end('from: http://localhost:8080')
    
      // Content-Type
      // res.setHeader('Access-Control-Allow-Headers', 'Content-Type')
      res.setHeader('Access-Control-Allow-Headers', 'Content-Type');//这个要在预检阶段返回。
    
      if (req.method === 'OPTIONS') {
        res.statusCode = 200
        return res.end()
        // return res.end('OPTIONS预检成功')
      }
    
    
      if (req.url === '/') {
        res.end('home')
      } else if (req.url === '/users') {
        switch (req.method) {
          case 'GET': res.end('users')
            break
          case 'POST': res.end('POST: users')
            break
          default:
            res.end('404')
            break
    
        }
      }
    }).listen(8080, () => {
      console.log(`服务器地址为: http://localhost:8080`);
    })
    

CORS针对put请求

  • fang/f20230728/f20230728/3.put.html

    DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>http://127.0.0.1:5500/title>
      head>
      <body>
        请求体 - http://127.0.0.1:5500/
      body>
      <script>
        let xhr = new XMLHttpRequest();
        xhr.open("PUT", "http://localhost:8080/users", true);
        xhr.setRequestHeader("Content-Type", "application/json");
        xhr.onload = () => {
          console.log(`接收到的返回体: xhr.responseText-->`, xhr.responseText); //报错: Access to XMLHttpRequest at 'http://localhost:8080/' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
        };
        xhr.send(JSON.stringify({ name: "zhufeng1" }));
      script>
    html>
    
  • fang/f20230728/f20230728/3.server.js

    const http = require('http')
    http.createServer((req, res) => {
      res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500')
      // res.end('from: http://localhost:8080')
    
      // Content-Type
      // res.setHeader('Access-Control-Allow-Headers', 'Content-Type')
      res.setHeader('Access-Control-Allow-Headers', 'Content-Type');//这个要在预检阶段返回。
      res.setHeader('Access-Control-Allow-Methods', 'PUT');//这个要在预检阶段返回。
    
      if (req.method === 'OPTIONS') {
        res.statusCode = 200
        return res.end()
        // return res.end('OPTIONS预检成功')
      }
    
    
      if (req.url === '/') {
        res.end('home')
      } else if (req.url === '/users') {
        switch (req.method) {
          case 'GET': res.end('GET: users')
            break
          case 'POST': res.end('POST: users')
            break
          case 'PUT': res.end('PUT: users')
            break
          default:
            res.end('404')
            break
        }
      } else {
        res.statusCode = 400
        res.end()
      }
    }).listen(8080, () => {
      console.log(`服务器地址为: http://localhost:8080`);
    })
    

CORS针对Cookie

  • fang/f20230728/f20230728/4.visit.html

    DOCTYPE html>
      <head>
        <title>get - http://127.0.0.1:5500/title>
      head>
      <body>
        请求体 - http://127.0.0.1:5500/
      body>
      <script>
        let xhr = new XMLHttpRequest();
        xhr.withCredentials='include'
        xhr.open("GET", "http://127.0.0.1:8080/visit", true);
        // xhr.open("GET", "http://localhost:8080/visit", true);
        xhr.onload = () => {
          console.log(`接收到的返回体: xhr.responseText-->`, xhr.responseText);
        };
        xhr.send();
      script>
    html>
    
  • fang/f20230728/f20230728/4.server.js

    const http = require('http')
    http.createServer((req, res) => {
      res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500')
      // res.end('from: http://localhost:8080')
    
      // Content-Type
      // res.setHeader('Access-Control-Allow-Headers', 'Content-Type')
      res.setHeader('Access-Control-Allow-Headers', 'Content-Type');//这个要在预检阶段返回。
      res.setHeader('Access-Control-Allow-Methods', 'PUT');//这个要在预检阶段返回。
      res.setHeader('Access-Control-Allow-Credentials', 'true')
    
      if (req.method === 'OPTIONS') {
        res.statusCode = 200
        return res.end()
        // return res.end('OPTIONS预检成功')
      }
    
    
      if (req.url === '/') {
        res.end('home')
      } else if (req.url === '/users') {
        switch (req.method) {
          case 'GET': res.end('GET: users')
            break
          case 'POST': res.end('POST: users')
            break
          case 'PUT': res.end('PUT: users')
            break
          default:
            res.end('404')
            break
        }
      } else if (req.url === '/visit') {
        const cookies = req.headers.cookie
        let visit = 0
        if (cookies) {
          let items = cookies.split(/;\s+/)
          for (let i = 0; i < items.length; i++) {
            let item = items[i]
            let [key, value] = item.split('=')
            if (key === 'visit') {
              visit = Number(value)
              break
            }
          }
        }
        visit++
        res.setHeader('Set-Cookie', [`visit=${visit}; SameSite=None; Secure=false`])
        res.end(`${visit}`)
    
      } else {
        res.statusCode = 400
        res.end()
      }
    }).listen(8080, () => {
      console.log(`服务器地址为: http://localhost:8080`);
    })
    

CORS详细说明

  • fang/f20230728/3.cors/1.server.js
//1.引入node.js内置用来创建 http服务器的模块
const http = require('http');
//2.创建http服务器,指定请求处理函数,以后每当有客户端请求到达服务器的时候就会由此
//请求处理函数来处理请求,并返回响应
//req代表请求对象,如果 想获取请求信息比如请求行 METHOD url 请求头 请求体可以使用此对象
//res代表响应对象,如果想写入响应信息,比如状态码 响应头 响应体使用此对象 
http.createServer((req, res) => {
  //服务器返回一个响应头,以后如果是来自于5500的访问,则浏览器不会再进行阻止
  res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500');
  //在预览的时候,需要返回响应头
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
  //在预检的时候,需要返回响应头Allow-Methods,它的值就是允许跨域请求的方法
  res.setHeader('Access-Control-Allow-Methods', 'PUT');
  //允许跨域的时候携带cookie
  res.setHeader('Access-Control-Allow-Credentials', 'true')
  //如果客户端发送过来的是一个OPTIONS请求的话,说明客户端只是需要确认一下是否允许跨域
  //它需要判断是否有Access-Control-Allow-Origin就可以了,不需要响应体
  if (req.method === 'OPTIONS') {
    res.statusCode = 200;
    return res.end();
  }
  if (req.url === '/') {
    res.end('home');
  } else if (req.url === '/users') {
    switch (req.method) {
      case 'GET':
        res.end('users');
        break;
      case 'POST':
        res.end('POST /users');
        break;
      case 'PUT':
        res.end('PUT /users');
        break;
      default:
        break;
    }
    //当客户端访问/visit路径的时候
  } else if (req.url === '/visit') {
    //取出客户端传递过来的cookie
    const cookies = req.headers.cookie;
    console.log('cookies', cookies);
    //定义一个客户端访问次数的变量
    let visit = 0;
    if (cookies) {// key1=value1;  visit=1;key3=value3
      //先用分割符把cookie拆成字符串的数组,然后再循环此字符串数组
      let items = cookies.split(/;\s+/);
      for (let i = 0; i < items.length; i++) {
        //再用=对每个cookie进行分割,会返回一个数组 1项 key 2项 value
        let [key, value] = items[i].split('=');
        if (key === 'visit') {
          visit = Number(value)
          break;
        }
      }
    }
    //让访问次数加1
    visit++;
    //服务器通过响应头Set-Cookie向客户端种植cookie visit=2
    //SameSite 是否只允许同域或者说同站点访问, 只有把它设置为None才能允许跨域读写cookie 
    //Secure为true的话表示只允许在https站点生效
    res.setHeader('Set-Cookie', [`visit=${visit}; SameSite=None; Secure=false`]);
    //返回最新的访问次数
    res.end(`${visit}`);
  } else {
    res.statusCode = 404;
    //即使你没有向客户端写入响应体,也需要关闭响应,
    //不然客户端会一直在等待服务器的响应,一直在那里转圈圈 
    res.end();
  }
}).listen(8080, () => console.log('8080'));
  • fang/f20230728/3.cors/get.html
DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Documenttitle>
  head>
  <body>
    <script>
      //创建Ajax对象的实例
      let xhr = new XMLHttpRequest();
      //打开连接
      xhr.open("GET", "http://localhost:8080", true);
      //指定成功的回调, 等客户端读取完响应信息后对执行onload回调函数
      xhr.onload = () => {
        console.log(xhr.responseText);
      };
      //发送请求给服务器
      xhr.send();
    script>
  body>
html>
  • fang/f20230728/3.cors/post.html
DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Documenttitle>
  head>
  <body>
    <script>
      //创建Ajax对象的实例
      let xhr = new XMLHttpRequest();
      //打开连接
      xhr.open("POST", "http://localhost:8080/users", true);
      //如果要想发送请求体,就需要设置Content-Type请求头,指定请求体的格式
      //跨域的时候发送一个请求头
      xhr.setRequestHeader("Content-Type", "application/json");
      //指定成功的回调, 等客户端读取完响应信息后对执行onload回调函数
      xhr.onload = () => {
        console.log(xhr.responseText);
      };
      xhr.onerror = (onerror) => {
        console.error(onerror);
      };
      //发送请求给服务器
      xhr.send(JSON.stringify({ name: "zhufeng1" }));
    script>
  body>
html>
  • fang/f20230728/3.cors/put.html
DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Documenttitle>
  head>
  <body>
    <script>
      //创建Ajax对象的实例
      let xhr = new XMLHttpRequest();
      //打开连接
      xhr.open("PUT", "http://localhost:8080/users", true);
      //如果要想发送请求体,就需要设置Content-Type请求头,指定请求体的格式
      //跨域的时候发送一个请求头
      xhr.setRequestHeader("Content-Type", "application/json");
      //指定成功的回调, 等客户端读取完响应信息后对执行onload回调函数
      xhr.onload = () => {
        console.log(xhr.responseText);
      };
      xhr.onerror = (onerror) => {
        console.error(onerror);
      };
      //发送请求给服务器
      xhr.send(JSON.stringify({ name: "zhufeng1" }));
    script>
  body>
html>
  • fang/f20230728/3.cors/visit.html
DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Documenttitle>
  head>
  <body>
    <script>
      let xhr = new XMLHttpRequest();
      //withCredentials是XMLHttpRequest的一个属性,该属性定义了是否在请求中携带cookie
      //默认情况下此值为 false,这意味着不携带cookie,如果true,则携带 cookie
      xhr.withCredentials = "include";
      xhr.open("GET", "http://127.0.0.1:8080/visit", true);
      xhr.onload = () => {
        console.log(xhr.responseText);
      };
      xhr.send();
    script>
  body>
html>

JSONP

静态js文件-JSONP原理

  • fang/f20230728/f20230728/6.serve-jsonp.js

    const http = require('http')
    http.createServer((req, res) => {
      res.setHeader('Content-Type', 'application/javascript')
      let script = `
        // 这里是服务器那边动态生成的js代码。
        console.log('开始执行服务器返回的动态js代码。')
        getData({'id':100,name:'fang'});
        console.log('结束执行服务器返回的动态js代码。')
      `
      // res.end(`console.log('from服务器8080的打印输出main')`);
      res.end(script);
    }).listen(8080, () => {
      console.log(`服务器地址为: http://localhost:8080`);
    })
    
  • fang/f20230728/f20230728/6.jsonp.html

    DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>jsonptitle>
      head>
      <body>
        静态jsonp原理
        <script>
          window.getData = function getData(data) {
            console.log(`data-->`, data);
          };
        script>
        <script src="http://localhost:8080/sugrec.js">script>
      body>
    html>
    

有了一点动态的jsonp

  • fang/f20230728/f20230728/7.jsonp.html

    DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>jsonptitle>
      head>
      <body>
        动态jsonp。
        <script>
          let callbackName = `JQuery_${Date.now()}`;
          window[callbackName] = function (data) {
            console.log(`data-->`, data);
          };
          let script = document.createElement("script");
          script.src = `http://localhost:8080/sugrec.js?cb=${callbackName}`;
          document.body.appendChild(script);
        script>
      body>
    html>
    
  • fang/f20230728/f20230728/7.serve-jsonp.js

    const http = require('http')
    const url = require('url')
    http.createServer((req, res) => {
      res.setHeader('Content-Type', 'application/javascript')
      const { query: { cb } } = url.parse(req.url || '', true)
      let script = `
        // 这里是服务器那边动态生成的js代码。
        console.log('开始执行服务器返回的动态js代码。')
        ${cb}({'id':100,name:'这个是后端构建出来的json数据'});
        console.log('结束执行服务器返回的动态js代码。')
      `
      res.end(script);
    }).listen(8080, () => {
      console.log(`服务器地址为: http://localhost:8080`);
    })
    

动态jsonp

  • fang/f20230728/f20230728/8.jsonp.html

    DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>jsonptitle>
      head>
      <body>
        动态jsonp。这里是自动生成的`Live Server`起起来的,服务器端口为5500;
        <script>
          let callbackName = `JQuery_${Date.now()}`;
          console.log(`动态函数变量名:callbackName-->`, callbackName);
    
          window[callbackName] = async (data) => {
            console.log(`后端返回的json数据:data-->`, data);
            await new Promise((resolve, reject) => {
              setTimeout(() => {
                resolve("");
              }, 300000);
            });
    
            // 执行结束后,移除多余代码。
            console.log(`执行结束后,移除多余代码。`);
            window[callbackName] = null;
            document.body.removeChild(script);
          };
    
          let script = document.createElement("script");
          script.src = `http://localhost:8080/sugrec.js?cb=${callbackName}`;
          console.log(`动态生成的脚本标签:script-->`, script);
    
          document.body.appendChild(script);
        script>
      body>
    html>
    
  • fang/f20230728/f20230728/8.serve-jsonp.js

    // 这里是用node起起来的,服务器端口为8080;
    const http = require('http')
    const url = require('url')
    http.createServer((req, res) => {
      res.setHeader('Content-Type', 'application/javascript')
      const { query: { cb } } = url.parse(req.url || '', true)//这里是为了让后端拿到前端定义的那个函数的函数名。
    
    
      const jsonObj = { 'id': 100, name: '这个是后端构建出来的json数据' }//这个就是后端要返回的json数据。
      const jsonStr = JSON.stringify(jsonObj)
      let script = `
        // 这里是服务器那边动态生成的js代码。
        console.log('开始执行服务器返回的动态js代码。')
        ${cb}(${jsonStr});
        console.log('结束执行服务器返回的动态js代码。')
      `
      res.end(script);
    }).listen(8080, () => {
      console.log(`服务器地址为: http://localhost:8080`);
    })
    

jsonP详细说明

  • fang/f20230728/3.cors/jsonp.html
DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Documenttitle>
  head>
  <body>
    <script>
      //动态创建方法名
      let callbackName = `jQuery_${Date.now()}`;
      //动态给window上添加一个变量,值是一个函数,并接收一个数据对象作为参数
      window[callbackName] = function (data) {
        console.log(data);
      };
      //动态创建script
      let script = document.createElement("script");
      //让此动态脚本src等于服务器地址
      script.src = `http://localhost:8080/sugrec.js?cb=${callbackName}`;
      //把此动态创建的脚本对象添加到body上,浏览器如果发现html上多了一个script标签,
      //就会立刻向它的script.src地址发出请求,并且取回来一段JS代码,并且立刻执行
      document.body.appendChild(script);
    script>
  body>
html>
  • fang/f20230728/3.cors/jsonp.js
const http = require('http');
const url = require('url');
http.createServer((req, res) => {
    //使用url.parse方法解析url地址,并且把查询 字符串变成一个query对象
    //search=?cb=jQuery_130 query={cb:"jQuery_130"}
    const {query:{cb}} = url.parse(req.url,true);
    //告诉客户端我返回的是一段JS脚本
    res.setHeader('Content-Type','application/javascript');
    //创建一段JS脚本字符串
    let script = `${cb}({"id":100,"name":"zhufeng"})`;
    //作为响应体发回给客户端
    res.end(script);
}).listen(8080, () => console.log('8080'));

jsonp示例-百度功能初步实现

  • fang/f20230728/f20230728/5.baidu.html
DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>jsonp-baidutitle>
  head>
  <body>
    <input type="text" id="search-word" />
    <ul id="suggestions">ul>
  body>
  <script>
    let $ = {
      ajax(options) {
        return new Promise((resolve, reject) => {
          const { url, jsonp, data } = options;
          let callbackName = `JQuery_${Date.now()}`;
          window[callbackName] = function (data) {
            // console.log(`前端所希望的后端数据:data-->`, data);
            resolve(data);
          };
          let script = document.createElement("script");
          script.src = `https://www.baidu.com/sugrec?prod=pc&wd=${data.wd}&cb=${callbackName}`;
          script.onerror = (error) => reject(error);
          document.body.appendChild(script);
        });
      },
    };
    let searchWord = document.getElementById("search-word");
    let suggestions = document.getElementById("suggestions");
    searchWord.addEventListener("input", (event) => {
      let wd = event.target.value;
      let callbackName = `JQuery_${Date.now()}`;
      console.log(`wd-->`, wd);
      console.log(`改:callbackName-->`, callbackName);
      // https://www.baidu.com/sugrec?prod=pc&wd=a&cb=jQuery_222
      $.ajax({
        url: "https://www.baidu.com/sugrec",
        jsonp: "cb",
        data: {
          prod: "pc",
          wd,
        },
      })
        .then((result) => {
          console.log(`result-->`, result);
          let { g } = result;
          if (g) {
            let html = ``;
            for (let i = 0; i < g.length; i++) {
              html += `
  • ${g[i].q}
  • `
    ; } suggestions.innerHTML = html; } }) .catch((error) => { console.log(`error-->`, error); }); });
    script> html>

    百度下拉项示例-jsonp封装

    • fang/f20230728/f20230728/9.baidu.html
    DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>jsonp-baidutitle>
      head>
      <body>
        <input type="text" id="search-word" />
        <ul id="suggestions">ul>
      body>
      <script>
        const jsonpFunction = (options) => {
          return new Promise((resolve, reject) => {
            const { url, jsonp, data } = options;
            const callbackName = `JQuery_${Date.now()}`;
            window[callbackName] = function (data) {
              resolve(data);
    
              delete window[callbackName];
              document.body.removeChild(script);
            };
            let queryString = url?.indexOf("?") === -1 ? "?" : "&";
            for (const key in data) {
              const value = data[key];
              queryString += `${key}=${value}&`;
            }
            let script = document.createElement("script");
            script.src = `${url}${queryString}&${jsonp}=${callbackName}`;
            script.onerror = (error) => {
              reject(error);
    
              delete window[callbackName];
              document.body.removeChild(script);
            };
            document.body.appendChild(script);
          });
        };
        let searchWord = document.getElementById("search-word");
        let suggestions = document.getElementById("suggestions");
        searchWord.addEventListener("input", (event) => {
          let wd = event.target.value;
          let callbackName = `JQuery_${Date.now()}`;
          console.log(`wd-->`, wd);
          console.log(`改一:callbackName-->`, callbackName);
          // https://www.baidu.com/sugrec?prod=pc&wd=a&cb=jQuery_222
          jsonpFunction({
            url: "https://www.baidu.com/sugrec",
            jsonp: "cb",
            data: {
              prod: "pc",
              wd,
            },
          })
            .then((result) => {
              console.log(`result-->`, result);
              let { g } = result;
              if (g) {
                let html = ``;
                for (let i = 0; i < g.length; i++) {
                  html += `
  • ${g[i].q}
  • `
    ; } suggestions.innerHTML = html; } }) .catch((error) => { console.log(`error-->`, error); }); });
    script> html>

    纯前端的jsonp封装详细说明

    DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Documenttitle>
      head>
    
      <body>
        <input id="search-word" />
        <ul id="suggestions">ul>
        <script>
          function jsonp(options) {
            return new Promise((resolve, reject) => {
              const { url, jsonp, data } = options;
              //1.或者创建一个临时的、唯一的方法名
              let callbackName = `jQuery_${Date.now()}`;
              //给window添加一个全局变量,变量名为方法,值是一个回调函数
              window[callbackName] = function (data) {
                //一旦此函数执行了,那么此变量则不再需要了,可以删除销毁了
                delete window[callbackName];
                //此时script脚本也不再需要,可以删除掉
                document.body.removeChild(script);
                //把data传递给了resolve函数,也就是传递给了成功的回调函数
                resolve(data);
              };
              //动态创建一个类型为script的对象或者说元素
              let script = document.createElement("script");
              //定义一个查询字符串变量
              //如果url地址里已经有问号了,则用&接着拼接其它的参数,如果没有?,那就用?开头
              let queryStr = url.indexOf("?") === -1 ? "?" : "&";
              for (let key in data) {
                // += `prod=pc&`
                // += `wd=a&`;
                queryStr += `${key}=${data[key]}&`;
              }
              //url=https://www.baidu.com/sugrec
              //queryStr=?prod=pc&wd=a&
              //指定script的来源或者说要访问的脚本的地址
              script.src = `${url}${queryStr}${jsonp}=${callbackName}`;
              //src=https://www.baidu.com/sugrec?prod=pc&wd=a&cb=jQuery_130
              script.onerror = (error) => reject(error);
              //向body的尾部添加一个script对象
              document.body.appendChild(script);
            });
          }
          //获取关键字输入框DOM元素
          let searchWord = document.getElementById("search-word");
          //获取联想词的下拉列表DOM元素
          let suggestions = document.getElementById("suggestions");
          //给关键字输入框绑定输入事件,当用户在输入框中输入字符串执行回调函数
          searchWord.addEventListener("input", (event) => {
            //调用https://www.baidu.com/sugrec?prod=pc&wd=a&cb=jQuery_222
            //获取事件源的值,也就是关键字输入框的值
            let wd = event.target.value;
            jsonp({
              url: "https://www.baidu.com/sugrec", //你想请求的url地址
              jsonp: "cb", //最终希望调用方法名是通过哪个查询参数发送给服务器的cb=jQuery_13000
              data: { prod: "pc", wd }, //其它要传递给服务器的数据,它们都会拼接到查询字符串中
            })
              .then((result) => {
                //获取结果中的g属性
                let { g } = result;
                if (g) {
                  let html = "";
                  for (let i = 0; i < g.length; i++) {
                    html += `
  • ${g[i].q}
  • `
    ; } suggestions.innerHTML = html; } }) .catch((error) => { console.log(error); }); });
    script> body> html>

    模块化

    commonJs模块规范

    1. 这个是在nodejs中使用,主要是ES6MOdule没出来之前。不过它只支持以同步的方式导入一个模块。

    以对象属性的方式单个导出

    • fang/f20230728/f20230728/commonJs/math1.js 写模块的。

      console.log(`模块内:exports-->`, exports);
      exports.add = (a, b) => {
        return a + b
      }
      exports.minus = (a, b) => {
        return a - b
      }
      
    • fang/f20230728/f20230728/commonJs/use1.js 使用模块的。

      let math = require('./math1')
      console.log(`使用模块:math-->`, math);
      console.log(`使用模块:math.add(1,2)-->`, math.add(1, 2));
      console.log(`使用模块:math.minus(1,2)-->`, math.minus(1, 2));
      
    • 详细说明:

    以module.exports的方式全量导出

    • fang/f20230728/f20230728/commonJs/math2.js

      console.log(`模块内2:module-->`, module);
      console.log(`模块内2:module.exports-->`, module.exports);
      module.exports = {
        add(a, b) {
          return a + b;
        },
        minus(a, b) {
          return a - b;
        }
      }
      
    • fang/f20230728/f20230728/commonJs/use2.js

      let math = require('./math2')
      console.log(`使用模块2:math-->`, math);
      console.log(`使用模块2:math.add(1,2)-->`, math.add(1, 2));
      console.log(`使用模块2:math.minus(1,2)-->`, math.minus(1, 2));
      

    AMD

    AMD单个引入

    • fang/f20230728/f20230728/AMD/index.html
    DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Documenttitle>
      head>
      <body>
        <script src="https://unpkg.com/[email protected]/require.js">script>
        <script>
          require(["math"], (math) => {
            console.log(`单个引入:math.add(1,2)-->`, math.add(1, 2));
          });
        script>
      body>
    html>
    
    • fang/f20230728/f20230728/AMD/math.js
    define(function () {
      //定义此模块的输出;
      return {
        add(a, b) {
          return a + b
        }, minus(a, b) {
          return a - b
        }
      }
    });
    

    AMD多个引入

    • fang/f20230728/f20230728/AMD/index.html
    DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Documenttitle>
      head>
      <body>
        <script src="https://unpkg.com/[email protected]/require.js">script>
        <script>
          // console.log(`require-->`, require);
          require(["math"], (math) => {
            console.log(`单个引入:math.add(1,2)-->`, math.add(1, 2));
          });
    
          require(["math", "format"], (math, format) => {
            console.log(`多个引入:math.add(1, 2)-->`, math.add(1, 2));
            console.log(`多个引入:format.print()-->`, format.print());
          });
        script>
      body>
    html>
    
    • fang/f20230728/f20230728/AMD/math.js
    define(function () {
      return {
        add(a, b) {
          return a + b
        }, minus(a, b) {
          return a - b
        }
      }
    });
    
    • fang/f20230728/f20230728/AMD/format.js
    define(function () {
      //定义此模块的输出
      return {
        async print() {
          console.log('模块内最初执行', Date.now())
          await new Promise((resolve, reject) => {
            setTimeout(() => {
              resolve('')
            }, 4000)
          })
          console.log('模块内最后执行', Date.now())
        }
      }
    });
    

    AMD详细说明

    • fang/f20230728/4.module/2.amd/index.html
    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Documenttitle>
    head>
    <body>
        <script src="https://unpkg.com/[email protected]/require.js">script>
        <script>
            //内部会以异步的方式加载math.js文件,并获取math.js导出的对象
            //异步加载完成后会执行回调函数,把math.js导出的对象传进去
            //require方法接收2个参数,一个依赖数组,一个是回调函数
            //当所有依赖都加载成功后,就会调用回调函数,回调函数的参数是依赖模块的输出
            require(['math','format'],(math,format)=>{
                console.log(math.add(1,2))
                console.log(format.print())
            });
        script>
    body>
    html>
    
    • fang/f20230728/4.module/2.amd/format.js
    define(function () {
        //定义此模块的输出
        return {
            print() {
                console.log(Date.now())
            }
        }
    });
    
    • fang/f20230728/4.module/2.amd/math.js
    define(function () {
        //定义此模块的输出
        return {
            add(a, b) {
                return a + b;
            },
            minus(a, b) {
                return a - b;
            }
        }
    });
    

    ES6Module

    • fang/f20230728/f20230728/ES6Module/math.js

      //单个导出一个变量age
      export const age = 16;
      
      // export //这个是一个关键字,后面只能接变量名或变量声明。
      
      function add(a, b) {
        return a + b;
      }
      function minus(a, b) {
        return a - b;
      }
      const obj1 = { a: 6 }
      const msg = 'hello';
      
      //批量导出add,minus多个变量,但也只算是单个导出,依旧要解构才能使用。
      export {
        add,
        obj1,
        // obj2 : 6,//报错。
        // let obj2 = 6,//报错。
        // obj2 = 6,//报错。
        minus,
      }
      
      // export { }//这个是一个关键字,大括号内部只能写变量名。
      // 相当于语法为: export {变量名1,变量名2,... }
      
      
      //一个模块中的默认导出只能有一个
      export default '北京'
      //A module cannot have multiple default exports.
      //export default   '北京'
      
    • fang/f20230728/f20230728/ES6Module/app.js

      import home from './math.js';
      import { age, add, minus, obj1 } from './math.js';
      // import home,{age,add,minus} from './math.js';// 可以合并成一个。
      console.log(`默认导入:home-->`, home);
      console.log(`单个导出-导出单个变量:age-->`, age);
      
      console.log(`单个导出-导出多个变量:add-->`, add);
      console.log(`单个导出-导出多个变量:minus-->`, minus);
      console.log(`单个导出-导出多个变量:obj1-->`, obj1);
      
      //if(true){
      //    //An import declaration can only be used at the top level of a module.
      //    import home,{age,add,minus} from './math.js';
      //}
      
    • fang/f20230728/f20230728/ES6Module/imdex.html

      DOCTYPE html>
      <html lang="en">
        <head>
          <meta charset="UTF-8" />
          <meta http-equiv="X-UA-Compatible" content="IE=edge" />
          <meta name="viewport" content="width=device-width, initial-scale=1.0" />
          <title>ES6Moduletitle>
        head>
        <body>
          ES6Module
          <script src="app.js" type="module">script>
        body>
      html>
      

    ES6Module详细说明

    • fang/f20230728/4.module/3.esmodule/math.js
    //单个导出一个变量age
    export const age = 16;
    
    function add(a,b){
        return a+b;
    }
    function minus(a,b){
        return a-b;
    }
    
    const msg = 'hello';
    
    //批量导出add,minus
    export {
        add,
        minus
    }
    //一个模块中的默认导出只能有一个
    export default   '北京'
    //A module cannot have multiple default exports.
    //export default   '北京'
    
    • fang/f20230728/4.module/3.esmodule/app.js
    import home,{age,add,minus} from './math.js';
    console.log(home)
    console.log(age)
    console.log(add)
    console.log(minus)
    //if(true){
        //An import declaration can only be used at the top level of a module.
    //    import home,{age,add,minus} from './math.js';
    //}
    
    • fang/f20230728/4.module/3.esmodule/index.html
    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Documenttitle>
    head>
    <body>
        <script src="app.js" type="module">script>
    body>
    html>
    

    UMD

    • fang/f20230728/f20230728/UMD/math.js 以UMD规范来简单写一个模块。

      (function (global, factory) {
        if (typeof define === 'function') {
          //当前处于AMD运行环境
          define(factory);
          return
        }
      
        if (typeof module === 'object') {
          //当前处于node中的commonjs模块化环境
          module.exports = factory();//导出factory方法执行的结果 
          return
        }
      
        // 不是AMD,也不是commonjs,那么就用全局变量的方式。
        global.math = factory();
        return
      
      
      })(this, () => {
        // 这里就是我们要导出去的模块。
        return {
          add(a, b) {
            return a + b;
          },
          minus(a, b) {
            return a - b;
          }
        }
      });
      
      
      // 浏览器环境 self=window=this 没有global
      // Node环境 global=this 没有self和window
      
    • fang/f20230728/f20230728/UMD/global.html 在浏览器以全局变量的方式来导入一个UMD模块。

      DOCTYPE html>
      <html lang="en">
        <head>
          <meta charset="UTF-8" />
          <meta http-equiv="X-UA-Compatible" content="IE=edge" />
          <meta name="viewport" content="width=device-width, initial-scale=1.0" />
          <title>Documenttitle>
        head>
        <body>
          浏览器中的global全局环境
          <script src="math.js">script>
          <script>
            console.log(window.math);
          script>
        body>
      html>
      
    • fang/f20230728/f20230728/UMD/amd.html 在浏览器以AMD的方式的来导入一个UMD模块。

      DOCTYPE html>
      <html lang="en">
        <head>
          <meta charset="UTF-8" />
          <meta http-equiv="X-UA-Compatible" content="IE=edge" />
          <meta name="viewport" content="width=device-width, initial-scale=1.0" />
          <title>Documenttitle>
        head>
        <body>
          浏览器中的AMD环境
          <script src="https://unpkg.com/[email protected]/require.js">script>
          <script>
            //内部会以异步的方式加载math.js文件,并获取math.js导出的对象
            //异步加载完成后会执行回调函数,把math.js导出的对象传进去
            //require方法接收2个参数,一个依赖数组,一个是回调函数
            //当所有依赖都加载成功后,就会调用回调函数,回调函数的参数是依赖模块的输出
            require(["math"], (math) => {
              console.log(math.add(1, 2));
            });
          script>
        body>
      html>
      
    • fang/f20230728/f20230728/UMD/commonJs.js 在node中以commonJs方式来导入一个UMD模块。

      let math = require('./math');
      console.log(`node中的commonJs环境:math-->`, math);
      console.log(`node中的commonJs环境:math-->`, math);
      

    UMD详细说明

    • fang/f20230728/4.module/4.umd/math.js
    (function (global, factory) {
        if(typeof define === 'function'){//当前处于AMD运行环境
            define(factory);
        }else if(typeof module === 'object'){//当前处于COMMONJS模块化环境
            module.exports = factory();//导出factory方法执行的结果 
        }else{
            global.math = factory();
        }
    })(this, () => {
        return {
            add(a, b) { return a + b; },
            minus(a, b) { return a - b; }
        }
    });
    // 浏览器环境 self=window=this 没有global
    // Node环境 global=this 没有self和window
    
    • fang/f20230728/4.module/4.umd/use.js
    let math = require('./math');
    console.log(math)
    
    • fang/f20230728/4.module/4.umd/amd.html
    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Documenttitle>
    head>
    <body>
        <script src="https://unpkg.com/[email protected]/require.js">script>
        <script>
            //内部会以异步的方式加载math.js文件,并获取math.js导出的对象
            //异步加载完成后会执行回调函数,把math.js导出的对象传进去
            //require方法接收2个参数,一个依赖数组,一个是回调函数
            //当所有依赖都加载成功后,就会调用回调函数,回调函数的参数是依赖模块的输出
            require(['math'],(math)=>{
                console.log(math.add(1,2))
            });
        script>
    body>
    html>
    
    • fang/f20230728/4.module/4.umd/global.html
    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Documenttitle>
    head>
    <body>
        <script src="math.js">script>
        <script>
           console.log(window.math)
        script>
    body>
    html>
    
    不同环境下的this
    1. 浏览器环境 self=window=this 没有global

      • 浏览器控制台中

        console.log(this===window)
        console.log(this===self)
        
    2. Node环境 global=this 没有self和window

      • fang/f20230728/4.module/4.umd/this.js 主js文件中

        console.log(this===global)
        console.log(this===exports)
        

    对象的函数写法

    let $ = {
      ajax: function () {
        console.log(`ajax-->`, ajax);
      },
    }
    

    这两种写法等价

    let $ = {
      ajax(){
        console.log(`ajax-->`, ajax);
      },
    }
    

    webpack初步

    • webpack是一个强大的模块打包器。
      • 它可以把一个Web网站所需的资源,像JS、CSS、图片、图标等等任意的文件都打包在一起。
      • 它的作用是可以从一个入口文件的出发,识别出每个文件之间的依赖关系,然后将这些资源全部打包成浏览器可以识别和处理的静态资源,即打包成一堆js文件或一个html文件。

    初步创建一个webpack项目

    1. 生成package.json文件。

      npm init -y
      
    2. 安装webpack所需的依赖。

      npm install webpack webpack-cli --save
      
    3. 创建一个js文件。

      • fang/f20230728/5.webpack/src/index.js 得到一个js文件。

        npm install webpack webpack-cli --save
        
    4. 写一个简单的webpack打包脚本

      {
        "scripts": {
          "build": "webpack --mode=development"
        },
      }
      
    5. 执行打包命令。

      npm run build
      

    一个简单的webpack示例

    • fang/f20230728/5.webpack/src/index.js 一个普通的js文件,默认的入口文件。

      console.log('main')
      
    • fang/f20230728/5.webpack/package.json 关于整个webpack项目的配置信息。

      {
        "name": "5.webpack",
        "version": "1.0.0",
        "description": "",
        "main": "index.js",
        "scripts": {
          "build": "webpack --mode=development"
        },
        "keywords": [],
        "author": "",
        "license": "ISC",
        "dependencies": {
          "webpack": "^5.88.2",
          "webpack-cli": "^5.1.4"
        }
      }
      
    • fang/f20230728/5.webpack/dist/main.js 执行打包命令后出现

    /*
     * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").
     * This devtool is neither made for production nor for readable output files.
     * It uses "eval()" calls to create a separate source file in the browser devtools.
     * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
     * or disable the default devtool with "devtool: false".
     * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
     */
    /******/ (() => { // webpackBootstrap
    /******/  var __webpack_modules__ = ({
    
    /***/ "./src/index.js":
    /*!**********************!*\
      !*** ./src/index.js ***!
      \**********************/
    /***/ (() => {
    
    eval("console.log('main')\n\n//# sourceURL=webpack://5.webpack/./src/index.js?");
    
    /***/ })
    
    /******/  });
    /************************************************************************/
    /******/  
    /******/  // startup
    /******/  // Load entry module and return exports
    /******/  // This entry module can't be inlined because the eval devtool is used.
    /******/  var __webpack_exports__ = {};
    /******/  __webpack_modules__["./src/index.js"]();
    /******/  
    /******/ })()
    ;
    

    切换npm源

    1. 安装nrm源切换工具

      npm i nrm -g
      
    2. 测试不同的源的速度

      nrm test 
      
    3. 切换成淘宝源

      nrm use taobao
      

    进阶参考

    你可能感兴趣的:(重返学习,webpack,学习,webpack,前端)