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>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
<!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
<!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
<!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

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

    <!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

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

    // 这里是用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>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
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-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
<!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封装详细说明

<!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 写模块的。

    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>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
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>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
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>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
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>ES6Module</title>
      </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>Document</title>
</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>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模块。

    <!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模块。

    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>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
<!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

    • 浏览器控制台中

      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
    

进阶参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值