JavaScript编程基础(八)json 跨域 异步编程

原创 阁主  2026-03-31 12:10:48  阅读 6022 次 评论 0 条
摘要:

继续上次的JavaScript编程基础(七)继续学习,简单记录学习PHP中文网23期JavaScript基础知识,内容包括:json简介和使用、跨域方案(jsonp与服务器端跨域)、fetch 与 async, await 异步的同步风格

介绍

常规的前后端交互都是直接传输html文档直接前端解析,但是做了前后端分离后,绝大部分都是通过JSON来进行数据交互的。简易的示例图如下:

特点

使用json有以下特点:

  • 通用,轻量, 利于交互和传输
  • 独立于语言,主流语言都提供了与 json 的编程接口
  • json 本质上就是一个格式化字符串
  • 类型: number,string,boolean,null,array,object
  • 注: 没有 undefined

转换

  • js->json: JSON.stringify(obj)
  • json->js: JSON.parse(jsonStr)
<!DOCTYPE html>
<html lang="zh-CN">
  <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>JSON</title>
  </head>
  <body>
    <script>
      // 客户端 -> 服务器
      // js对象 -> json格式字符串,这样才能和服务器进行数据交换
      // php服务器,不可能认识js对象,但是它认识 json 字符串

      // js对象
      let obj = {
        name: "电脑",
        price: 5000,
        desc: {
          brand: "HUAWEI",
          cpu: `i7-1200`,
        },
      };
      console.log(obj);

      // (1) js -> json
      // JSON.stringify()  js对象转换为json字符串
      let jsonStr = JSON.stringify(obj);
      // 序列化为JSON格式的字符串
      console.log(jsonStr);
      console.log(typeof jsonStr);
      // 格式化json字符串
      let json = JSON.stringify(obj, null, 4);
      console.log(json);
      // 过滤name和price属性,参数4是格式化
      json = JSON.stringify(obj, ["name", "price"], 4);
      console.log(json);

      /**
       * JSON特点
       * 1. 所有属性必须使用双引号
       * 2. 值,如果是字符串,必须使用双引号
       */
      console.log(JSON.stringify([1, 2, 3, 4]));
      console.log(JSON.stringify(true));
      console.log(JSON.stringify("hello"));

      // =======================================

      // (2) json -> js

      // 假设下面的json字符串是从服务器返回的数据
      json = `
      {
        "course": "PHP",
        "score": 90,
        "content": ["html", "css", "js"]
      }
      `;

      // JSON.parse()
      // 将json字符串转换为js对象
      obj = JSON.parse(json);
      console.log(obj);
      // 渲染到页面中
      let str = `
        <ul>
          <li>课程: ${obj.course} </li>
          <li>分数: ${obj.score} </li>
          <li>科目: ${obj.content} </li>
        </ul>
      `;

      //插入到元素element里面的第一个子节点之前
      // document.body.insertAdjacentHTML("afterbegin", str);
      //也可以直接把str贴到body里面
      document.body.innerHTML = str;
    </script>
  </body>
</html>

跨域 cors: jsonp

  • 同源
  1. 同源策略,禁止跨域: 浏览器最基本的安全机制
  2. 同源:协议,域名,端口全部相同,才可互相访问
下面域名全部跨域
https://php.cn
http://php.cn
http://php.net

https://php.cn:443
https://php.cn:888
  • CORS
  1. CORS: 跨域资源共享(Cross-origin Resource Sharing)
  2. 突破了浏览器同源访问的安全策略
  • JSONP: JSON with Padding (用 JSON 填充参数)

demo1.html:

<!DOCTYPE html>
<html lang="zh-CN">
  <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>cors: jsonp</title>
    <!-- http://hello.io/ -->
    <!-- http://code.jquery.com -->
    <!-- script标签具有天然的跨域属性,可以利用script来跨域请求 -->
    <link rel="stylesheet" href="" />
    <a href=""></a>
    <img src="" alt="" />
    <script src="https://lib.baomitu.com/jquery/1.12.4/jquery.js"></script>
  </head>
  <body>
    <button onclick="getData()">跨域请求</button>
    <script>
      // jsonp: JSON with Padding

      // 函数
      function hello(user) {
        console.log(user);
      }

      // 假设以下的数据,来自另外一台服务器,就要用到跨域了
      const user = { uname: "朱老师", email: "zls@qq.com" };
      hello(user);
    </script>

    <!-- <script src="http://world.io/test.php?fn=hello"></script> -->

    <!-- 剩下工作极其简单, 就是动态创建上面的script语句就可以了 -->

    <script>
      function getData() {
        // 1. 创建script
        const script = document.createElement("script");
        // 2. 设置跨域请求的url地址
        // 返回的是一个执行hello函数的语句:hello({"uname":"灭绝老师","email":"mj@qq.com"})
        script.src = "http://world.io/php23/0331/cors/test.php?fn=hello";
        // 3. 将新创建的script元素追加到页面中
        document.body.append(script);
      }
    </script>
  </body>
</html>

test.php:

<?php

// jsonp

// 供前端调用的数据,以及函数调用语句,全部在服务器动态生成并返回就可以了

// 1. 准备数据
$user = json_encode(['uname' => '灭绝老师', 'email' => 'mj@qq.com'], 301);

// 获取函数名称
$fn = $_GET['fn'];
// 输出函数调用语句
echo "$fn($user)";

cors: 服务器端跨域

  • 服务器允许: Access-Control-Allow-Origin

demo2.html:

<!DOCTYPE html>
<html lang="zh-CN">
  <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>cors: 服务器端跨域</title>
  </head>
  <body>
    <button onclick="getData()">跨域请求-1</button>

    <script>
      function getData() {
        //异步请求
        fetch(`http://world.io/php23/0331/cors/test1.php`)
          .then((res) => res.json())
          .then((res) => {
            console.log(res);
          });
      }
    </script>
  </body>
</html>

test1.php:

<?php
// 允许跨域
header('Access-Control-Allow-Origin: *');
echo json_encode(['uname' => '马老师', 'email' => '5678@qq.com']);

Ajax(XHR): 了解(已淘汰)

  1. 创建对象: new XMLHttpRequest()
  2. 响应类型: xhr.responseType = 'json'
  3. 配置参数: xhr.open("GET / POST", url, true)
  4. 请求回调: xhr.onload = () => console.log(xhr.response)
  5. POST:xhr.setRequestHeater('content-type','application/json')
  6. 失败回调: xhr.onerror = () => console.log('Error')
  7. 发起请求: xhr.send(null / jsonString)

Ajax(XHR)示例

<!DOCTYPE html>
<html lang="zh-CN">
  <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>ajax-xhr: 了解(已淘汰)</title>
  </head>
  <body>
    <button onclick="getData(this)">Ajax(XHR)</button>

    <script>
      function getData(ele) {
        // 1. 创建xhr对象
        const xhr = new XMLHttpRequest();

        // 2. 设置响应类型(返回的数据是什么类型: json)
        xhr.responseType = "json";

        // 3. 配置参数(请求方式,get/post,以及请求的url)
        xhr.open("GET", "https://jsonplaceholder.typicode.com/todos/20");

        // 4. 处理请求成功的回调函数
        xhr.onload = function () {
          console.log(xhr.response);
          // dom操作,将数据渲染到页面
          ele.insertAdjacentHTML("afterend", `<li>${xhr.response.title}</li>`);
        };

        // 5. 处理请求失败的回调函数
        xhr.onerror = function () {
          console.error("XHR Error");
        };

        // 6. 发送请求
        xhr.send(null);
      }
    </script>
  </body>
</html>

Fetch

  1. 异步请求: fetch().then().then().catch()
  2. 异步函数: async,await
  3. 注意:await 必须用在 async 声明的函数内部

<!DOCTYPE html>
<html lang="zh-CN">
  <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>fetch</title>
  </head>
  <body>
    <button onclick="getData(this)">fetch api</button>

    <script>
      function getData(ele) {
        /**
         * 1. fetch(url): promise
         * 2. then(): 按同步的方式去执行异步任务
         * 3. then():
         */
        /**
         * 同步: 按顺序执行
         * 异步: 任务是乱序执行,通过不断的添加回调来控制异步任务执行的顺序
         */
        /**
         * fetch 功能
         * 1. fetch(url): 向一个url发请异步请求
         * 2. then(response=>response.json())
         * 3. then(json=>console.log(json))
         */

        //  用法一:
        fetch("https://jsonplaceholder.typicode.com/todos/20")
          .then(function (response) {
            // 将请求的结果转为json,将第2步的结果json,返回到第3步进一步处理
            return response.json();
          })
          .then(function (json) {
            console.log(json);
            console.log(typeof json);
            ele.insertAdjacentHTML("afterend", `<li>${json.title}</li>`);
          });

        //  用法二(简化):
        const url = "https://jsonplaceholder.typicode.com/todos/20";
        fetch(url)
          .then((res) => res.json())
          .then((json) =>
            ele.insertAdjacentHTML("afterend", `<li>${json.title}</li>`)
          )
          .catch((err) => console.log(err));

        // 将这种链式的异步的书写方式,改成传统的可读性更好的同步风格
      }
    </script>
  </body>
</html>

async函数

这个方法是将上面fetch部分的链式的异步的书写方式,改成传统的可读性更好的同步风格。

<!DOCTYPE html>
<html lang="zh-CN">
  <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>async函数</title>
  </head>
  <body>
    <button onclick="getData(this)">async</button>

    <script>
      // 异步函数
      async function getData(ele) {
        const url = "https://jsonplaceholder.typicode.com/todos/15";

        try {
          // 1. 发起异步请求,等待返回结果的响应对象
          const response = await fetch(url);

          // 2. 如果响应成功,将响应结果转为json
          const result = await response.json();

          // 3. 对响应结果进行处理,例如渲染到到页面,或打印到控制台查看
          console.log(result);
          ele.insertAdjacentHTML("afterend", `<li>${result.title}</li>`);
        } catch {
          console.error("请求失败");
        }
      }
    </script>
  </body>
</html>

fetch api

不多介绍了,可以自行百度查阅,讲得比我还清楚,这边只作学习笔记的记录。

html.php:

<ul style="display:flex;list-style:none;gap:1em">
    <li><a href="">首页</a></li>
    <li><a href="">教学视频</a></li>
    <li><a href="">社区问答</a></li>
</ul>

json.php:

<?php

$data = [
  ['id' => 1, 'name' => '外套', 'price' => 300],
  ['id' => 2, 'name' => '鞋子', 'price' => 200],
  ['id' => 3, 'name' => '袜子', 'price' => 50],
];

// 序列化为json格式字符串返回前端
echo json_encode($data);

insert.php:

<?php

// 获取的是json,并不是通过传统的表单键值对过来的
// $_POST 不能用

$jsonStr = file_get_contents('php://input');
// json字符串独立于任何编程语言,要想在当前的语言中使用
// 必须转为当前语言支持的数据类型

// json->php  加true,转为数组
$item = json_decode($jsonStr, true);

$data = [
  ['id' => 1, 'name' => '外套', 'price' => 300],
  ['id' => 2, 'name' => '鞋子', 'price' => 200],
  ['id' => 3, 'name' => '袜子', 'price' => 50],
];

// 将前端上传的数据,添加到数组中
array_push($data, $item);
// 将数组转为json字符串
echo json_encode($data);

style.css:

.container {
  width: 300px;
  min-height: 180px;
  border: 1px solid #aaa;
}

.container .nav {
  display: flex;
  height: 30px;
  border-bottom: 1px solid #aaa;
}
.container .nav > button {
  flex: 1;
  border: none;
  background-color: #fff;
  border-right: 1px solid #aaa;
}
.container .nav > button:last-child {
  border-right: none;
}
.container .nav > button:hover {
  cursor: pointer;
  background-color: #eee;
  transition: 0.5s;
}

fetch.html:

<!DOCTYPE html>
<html lang="zh-CN">
  <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>fetch api</title>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <div class="container">
      <!-- 导航区 -->
      <div class="nav">
        <button onclick="getHtml()">get-html</button>
        <button onclick="getJson()">get-json</button>
        <button onclick="postJson()">post-json</button>
      </div>
      <!-- 内容显示区 -->
      <div class="box"></div>

      <script>
        const box = document.querySelector(".box");
        // (1) 获取html
        async function getHtml() {
          // fetch, await
          // 1. 发送请求fetch
          let url = "http://fetch.edu/php23/0403/api/html.php";
          //   const response = await fetch(url)
          const response = await fetch(url, {
            method: "get",
          });
          // 2. 解析响应对象 response
          // text() -> html / plain
          let data = await response.text();
          console.log(data);

          // 3. 渲染
          box.innerHTML = data;
        }

        // ================================

        // (2) 获取json
        async function getJson() {
          // 1. 发送请求fetch
          let url = "http://fetch.edu/php23/0403/api/json.php";
          // fetch 默认就是get请求
          //   const response = await fetch(url)
          const response = await fetch(url, {
            method: "get",
          });

          // 2. 解析响应对象 response
          // json() : json字符串-> js对象
          let data = await response.json();
          console.log(data);

          // 3. 渲染 使用map方法遍历数组,将数组中的每一项拼接为li标签,再将li标签拼接为ul标签
          const items = data.map(
            (item) => `<li>${item.id}: ${item.name} (${item.price}) 元</li>`
          );
          // 拼接字符串
          data = `<ul>${items.join("")}</ul>`;
          console.log(items.join(""));
          console.log(data);
          box.innerHTML = data;
        }

        // ================================

        // (3) post请求(以添加数据为例)
        async function postJson() {
          // 1. 发送请求fetch
          let url = "http://fetch.edu/php23/0403/api/insert.php";
          const response = await fetch(url, {
            // 请求类型
            method: "post",
            // 请求头
            headers: {
              // 内容类型
              "content-type": "application/json;charset=utf-8",
            },
            // 将需要传到服务器上的数据解析为json进行发送
            body: JSON.stringify({ id: 4, name: "蛋糕", price: 250 }),
          });

          // 2. 解析响应对象 response
          // json() : json字符串-> js对象
          let data = await response.json();
          console.log(data);

          // 3. 渲染
          const items = data.map(function (item) {
            return `<li>${item.id}: ${item.name} ${item.price} 元</li>`;
          });
          console.log(items.join(""));
          data = `<ul>${items.join("")}</ul>`;
          box.innerHTML = data;
        }
      </script>
    </div>
  </body>
</html>
本文地址:https://www.mainblog.cn/333.html
版权声明:本文为原创文章,版权归 阁主 所有,欢迎分享本文,转载请保留出处!
免责申明:有些内容源于网络,没能联系到作者。如侵犯到你的权益请告知,我们会尽快删除相关内容。
NEXT:已经是最新一篇了

评论已关闭!