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 前端要写的代码。

    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 后端要写的代码。

    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

    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

    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

    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

    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

    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

    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
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
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>Document</title>
  </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
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>Document</title>
  </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
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>Document</title>
  </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
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>Document</title>
  </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

    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

    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</title>
      </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

    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</title>
      </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

    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

    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</title>
      </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

    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
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>Document</title>
  </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
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
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-baidu</title>
  </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 += `<li>${g[i].q}</li>`;
            }
            suggestions.innerHTML = html;
          }
        })
        .catch((error) => {
          console.log(`error-->`, error);
        });
    });
  </script>
</html>

百度下拉项示例-jsonp封装

  • fang/f20230728/f20230728/9.baidu.html
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-baidu</title>
  </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 += `<li>${g[i].q}</li>`;
            }
            suggestions.innerHTML = html;
          }
        })
        .catch((error) => {
          console.log(`error-->`, error);
        });
    });
  </script>
</html>

纯前端的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>Document</title>
  </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 += `<li>${g[i].q}</li>`;
              }
              suggestions.innerHTML = html;
            }
          })
          .catch((error) => {
            console.log(error);
          });
      });
    </script>
  </body>
</html>

模块化

commonJs模块规范

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

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

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

    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 使用模块的。

    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

    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

    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
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>Document</title>
  </head>
  <body>
    <script src="https://unpkg.com/requirejs@2.3.6/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
js 复制代码
define(function () {
  //定义此模块的输出;
  return {
    add(a, b) {
      return a + b
    }, minus(a, b) {
      return a - b
    }
  }
});

AMD多个引入

  • fang/f20230728/f20230728/AMD/index.html
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>Document</title>
  </head>
  <body>
    <script src="https://unpkg.com/requirejs@2.3.6/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
js 复制代码
define(function () {
  return {
    add(a, b) {
      return a + b
    }, minus(a, b) {
      return a - b
    }
  }
});
  • fang/f20230728/f20230728/AMD/format.js
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
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>Document</title>
</head>
<body>
    <script src="https://unpkg.com/requirejs@2.3.6/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
js 复制代码
define(function () {
    //定义此模块的输出
    return {
        print() {
            console.log(Date.now())
        }
    }
});
  • fang/f20230728/4.module/2.amd/math.js
js 复制代码
define(function () {
    //定义此模块的输出
    return {
        add(a, b) {
            return a + b;
        },
        minus(a, b) {
            return a - b;
        }
    }
});

ES6Module

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

    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

    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

    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>ES6Module</title>
      </head>
      <body>
        ES6Module
        <script src="app.js" type="module"></script>
      </body>
    </html>

ES6Module详细说明

  • fang/f20230728/4.module/3.esmodule/math.js
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
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
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>Document</title>
</head>
<body>
    <script src="app.js" type="module"></script>
</body>
</html>

UMD

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

    js 复制代码
    (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模块。

    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>Document</title>
      </head>
      <body>
        浏览器中的global全局环境
        <script src="math.js"></script>
        <script>
          console.log(window.math);
        </script>
      </body>
    </html>
  • fang/f20230728/f20230728/UMD/amd.html 在浏览器以AMD的方式的来导入一个UMD模块。

    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>Document</title>
      </head>
      <body>
        浏览器中的AMD环境
        <script src="https://unpkg.com/requirejs@2.3.6/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模块。

    js 复制代码
    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
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
js 复制代码
let math = require('./math');
console.log(math)
  • fang/f20230728/4.module/4.umd/amd.html
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>Document</title>
</head>
<body>
    <script src="https://unpkg.com/requirejs@2.3.6/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
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>Document</title>
</head>
<body>
    <script src="math.js"></script>
    <script>
       console.log(window.math)
    </script>
</body>
</html>
不同环境下的this
  1. 浏览器环境 self=window=this 没有global

    • 浏览器控制台中

      js 复制代码
      console.log(this===window)
      console.log(this===self)
  2. Node环境 global=this 没有self和window

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

      js 复制代码
      console.log(this===global)
      console.log(this===exports)

对象的函数写法

js 复制代码
let $ = {
  ajax: function () {
    console.log(`ajax-->`, ajax);
  },
}

这两种写法等价

js 复制代码
let $ = {
  ajax(){
    console.log(`ajax-->`, ajax);
  },
}

webpack初步

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

初步创建一个webpack项目

  1. 生成package.json文件。

    js 复制代码
    npm init -y
  2. 安装webpack所需的依赖。

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

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

      js 复制代码
      npm install webpack webpack-cli --save
  4. 写一个简单的webpack打包脚本

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

    js 复制代码
    npm run build

一个简单的webpack示例

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

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

    json 复制代码
    {
      "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 执行打包命令后出现

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源切换工具

    js 复制代码
    npm i nrm -g
  2. 测试不同的源的速度

    js 复制代码
    nrm test 
  3. 切换成淘宝源

    js 复制代码
    nrm use taobao

进阶参考

相关推荐
崔庆才丨静觅4 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60615 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了5 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅5 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
执笔论英雄5 小时前
【大模型学习cuda】入们第一个例子-向量和
学习
wdfk_prog5 小时前
[Linux]学习笔记系列 -- [drivers][input]input
linux·笔记·学习
崔庆才丨静觅5 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅6 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment6 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅6 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端