阅读 37

PHP 学习之路:第十三天—— Ajax与跨域请求

一、json 基础

1. JSON 是什么

  • JSON: JavaScript Object Notation(JS 对象表示法)
  • JSON 独立于任何编程语言, 几乎所有编程语言都提供了访问 JSON 数据的 API 接口
  • JSON 是一种语法,用来序列化其它语言创建的数据类型
  • JSON 仅支持 6 种数据类型:对象,数组,数值,字符串,布尔值,null
  • JSON 只是借用了 JS 中的一些数据表示语法,与 JS 并无关系

2. JSON 数据类型

序号类型描述
1简单值数值,字符串,布尔,null
1复合值对象,数组

注意: 不支持undefined(因为除 JS 外,其它语言中没有这个东西)

3. JS 解析 JSON 的 API

序号方法描述
1JSON.stringify()将 JS 对象,序列化为 JSON 字符串
2JSON.parse()将 JSON 字符串,解析为 JS 对象

3.1 JSON.stringify(data)

// JSON.stringify(data); js -> json
console.log(JSON.stringify(3.14), typeof JSON.stringify(3.14));
console.log(JSON.stringify("php.cn"), typeof JSON.stringify("php.cn"));
console.log(JSON.stringify(true), typeof JSON.stringify(true));
console.log(JSON.stringify(null), typeof JSON.stringify(null));
// json其实就是一个字符串,具有特殊格式的字符串文本,类似html

// 通常对象和数组转为json才有意义
console.log(JSON.stringify({ x: "a", y: "b" }));
console.log(
JSON.stringify([
  { id: 1, name: "admin" },
  { id: 2, name: "php" },
])
);

console.log(JSON.stringify({ a: 1, b: 2, c: 3 }));
// 第二个参数数组进行结果过滤
console.log(JSON.stringify({ a: 1, b: 2, c: 3 }, ["a", "b"]));
// 第二个参数传一下回调
console.log(
  JSON.stringify({ a: 1, b: 2, c: 3 }, (k, v) => {
    if (v < 2) return undefined;
    return v;
  })
);

// 第三个参数可以用来格式式输出json字符串的输出
console.log(JSON.stringify({ a: 1, b: 2, c: 3 }, null, 2));
console.log(JSON.stringify({ a: 1, b: 2, c: 3 }, null, "---"));
复制代码

3.2 JSON.parse()

// JSON.parse(): json -> js对象
let jsObj = JSON.parse(`{"a": 1,"b": 2,"c": 3}`);
console.log(jsObj instanceof Object);
console.log(jsObj.a, jsObj.b, jsObj.c);
复制代码

二、Ajax 异步请求

特别提示: 异步请求不要使用live server插件,必须创建一个本地服务器环境

1. 同步与异步

以前端请求,后端响应为例

  • 同步: 前端发请求, 必须等到后端响应完成,才允许发送另一个请求
  • 异步: 前端发请求后不等待后端响应结果继续执行,后端响应完成通过事件通知前端处理

2. XMLHttpRequest 对象

XMLHttpRequest是浏览器对象,而非 JS 内置对象

2.1 xhr 请求步骤

  1. 创建 xhr 对象: const xhr = new XMLHttpRequest()
  2. 配置 xhr 参数: xhr.open(type, url)
  3. 处理 xhr 响应: xhr.onload = (...) => {...}
  4. 发送 xhr 请求: xhr.send(...)

2.2. xhr 对象常用属性

序号方法描述
1responseType设置响应类型
2response响应正文

2.3 xhr 对象常用方法

序号方法描述
1open(type,url)配置请求参数
2send(data/null)发送请求

2.4. xhr 对象常用事件

序号事件描述
1load()请求成功
2error()请求失败

developer.mozilla.org/zh-CN/docs/…

3. FormData 对象

FormData是表单数据构造器

序号方法描述
1append(name,value)请求成功
2delete(name)请求失败

developer.mozilla.org/zh-CN/docs/…

4. get / post 区别

  • get 是 url 传参,post 是request body请求体传参
  • get 回退无影响, post 回退会重复提交
  • get 生成的 url 可做书签,post 不可以
  • get 只能对 url 进行编码, post 支持多种编码
  • get 请求参数会保留在历史记录中, post 参数不保留
  • get 参数长度受限,post 无限制
  • get 只接受 ascii 码字符,post 无限制
  • get,post 底层实现是一致的,都是基于 http 协议
  • get 也可以带上 request body, post 也可以带上 url 参数
  • get 产生一个 tcp 数据包,post 产生二个 tcp 数据包
  • get 产生一个请求, post 产生二个请求
  • get 请求,浏览器将 header,data 一起发出,服务器响应 200 成功
  • post 请求,浏览器先发出 header,得到响应 100 continue,再发出 data,得到响应 200
  • 并非所有浏览器的 post 都产生二次 http 请求,firefox 就只产生一次

5. 跨域

  • CORS: 跨域资源共享
  • 跨域请求可以是 get,也可以是 post,只不过 get 居多
  • cors-post 时,需要在服务器端进行头部设置
  • jsonp 只能是 get

三、ajax-get 请求

<button>Ajax-GET请求</button>
<p></p>

<script>
  const btn = document.querySelector("button");
  btn.onclick = () => {
    // 1. 创建 xhr 对象:
    const xhr = new XMLHttpRequest();

    // 2. 配置 xhr 参数:
    xhr.open("get", "test1.php?id=2");
    xhr.responseType = "json";

    // 3. 处理 xhr 响应:
    xhr.onload = () => {
      // xhr.response: 响应的返回值,是json字符串
      // console.log(typeof xhr.response);

      // 如果显式的设置了响应类型,如json,这里就是直接自动转为js对象
      console.log(xhr.response);
      // 是将json转为js对象
      // console.log(JSON.parse(xhr.response));

      let user = `${xhr.response.name} ( ${xhr.response.email} )`;
      document.querySelector("p").textContent = user;
    };
    xhr.onerror = () => console.log("Error");

    // 4. 发送 xhr 请求: xhr.send(...)
    xhr.send(null);
  };
</script>
复制代码

四、ajax-post 请求

html 页面文件:

<style>
  body {
    background-color: lightcyan;
  }
  .login {
    width: 20em;
    /*border: 1px solid;*/
    box-shadow: 0 0 8px #888;
    padding: 0 1em 1em;
    background-color: lightseagreen;
    color: white;
    margin: 2em auto;
    display: grid;
    place-items: center;
  }

  .login form {
    display: grid;
    grid-template-columns: 3em 1fr;
    gap: 1em 0;
  }

  .login form input {
    border: none;
    outline: none;
  }

  .login form input:focus,
  .login form input:hover {
    box-shadow: 0 0 5px lime;
  }

  .login form button {
    background-color: lightsalmon;
    color: white;
    outline: none;
    border: none;
    height: 2em;
  }

  .login form button:hover {
    background-color: salmon;
    cursor: pointer;
    box-shadow: 0 0 5px lime;
  }

  /* 按钮与提示信息显示在第二列 */
  .login form button,
  .tips {
    grid-area: auto / 2;
  }
</style>

<div class="login">
  <p>用户登录</p>
  <form action="" onsubmit="return false">
    <label for="email">邮箱:</label>
    <input type="email" name="email" id="email" placeholder="demo@email.com" />
    <label for="password">密码:</label>
    <input type="password" name="password" id="password" placeholder="不少于6位" />
    <button>提交</button>
    <span class="tips"></span>
  </form>
</div>
复制代码

js 文件实现:

const form = document.querySelector(".login form");
const btn = document.querySelector(".login button");
const tips = document.querySelector(".tips");

// FormData: 表单数据的序列化
// let data = new FormData(form);
// data.append("email", form.email.value);
// console.log(data.get("email"), data.get("password"));
// console.log(data);

btn.onclick = ev => {
  // 禁止默认的提交行为
  ev.preventDefault();

  // 1. 创建 xhr 对象:
  const xhr = new XMLHttpRequest();

  // 2. 配置 xhr 参数:
  xhr.open("post", "test2.php");

  // 3. 处理 xhr 响应:
  xhr.onload = () => {
    tips.textContent = xhr.response;
  };
  xhr.onerror = () => console.log("Error");

  // 4. 发送 xhr 请求
  xhr.send(new FormData(form));
};
复制代码

五、ajax-get-cors 请求

// cors: 跨域资源共享
// 同源策略: 为请求的安全,浏览器禁止通过脚本发起一个跨域的请求
// 只允许通过脚本发起基于同源的请求
// 同源指: 协议相同,域名/主机名相同,端口相同
//   http://www.abc.com  http
//   https://www.abc.com https
//   协议不同

//   https://www.a.com
//   https://www.b.com
//   主机不同

//   https://a.cn:8080
//   https://a.cn:9090
//   端口不同
复制代码
<button>跨域请求CORS</button>
<script>
  const btn = document.querySelector("button");
  btn.onclick = (ev) => {
    //   1. xhr对象
    const xhr = new XMLHttpRequest();

    //   2. 配置xhr, 跨域请求
    xhr.open("get", "http://world.io/cors1.php");

    //   3. 处理xhr响应
    xhr.onload = () => console.log(xhr.response);

    //   4. 发送xhr请求
    xhr.send(null);
  };
</script>
复制代码

六、ajax-post-cors 请求

<button>跨域请求CORS-POST</button>
<p class="tips"></p>
<script>
  const btn = document.querySelector("button");
  const tips = document.querySelector(".tips");

  btn.onclick = (ev) => {
    // 1. xhr对象
    const xhr = new XMLHttpRequest();
    // 2. 配置xhr, 跨域请求
    xhr.open("post", "http://world.io/cors2.php");
    // 3. 处理xhr响应
    xhr.onload = () => (tips.innerHTML = xhr.response);
    // 4. 发送xhr请求
    // 模拟表单数据: FormData
    let formData = new FormData();
    formData.append("email", "admin@qq.com");
    formData.append("password", "123456");
    xhr.send(formData);
  };
</script>
复制代码

七、案例:选项卡

html 文件:

<div class="tabs">
  <!-- 导航 -->
  <ul class="tab">
    <li class="active" data-index="1">省内</li>
    <li data-index="2">国内</li>
    <li data-index="3">国际</li>
  </ul>

  <!-- 与导航标签页对应的详情列表 -->
  <ul data-index="1" class="item active">
    <li><a href="">新华社曝安徽亳州麦田“长”出</a></li>
    <li><a href="">新华社曝安徽亳州麦田“长”出</a></li>
    <li><a href="">新华社曝安徽亳州麦田“长”出</a></li>
    <li><a href="">新华社曝安徽亳州麦田“长”出</a></li>
  </ul>

  <ul data-index="2" class="item">
    <li><a href="">武汉机场跨境电商进出口猛</a></li>
    <li><a href="">武汉机场跨境电商进出口猛</a></li>
    <li><a href="">武汉机场跨境电商进出口猛</a></li>
    <li><a href="">武汉机场跨境电商进出口猛增</a></li>
  </ul>

  <ul data-index="3" class="item">
    <li><a href="">大湾区国际信息科技协会在港成立 </a></li>
    <li><a href="">大湾区国际信息科技协会在港成立 </a></li>
    <li><a href="">大湾区国际信息科技协会在港成立 </a></li>
    <li><a href="">大湾区国际信息科技协会在港成立 </a></li>
  </ul>
</div>
复制代码

css 文件:

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

a {
  text-decoration: none;
  color: #555;
}

a:hover {
  text-decoration: underline;
  color: red;
}

li {
  list-style: none;
  line-height: 1.6em;
}

li:hover {
  cursor: default;
}

.tabs {
  width: 300px;
  height: 300px;
  margin: 30px;
  background-color: #e6e6e6;

  display: flex;
  flex-direction: column;
}

.tab {
  height: 36px;
  display: flex;
}

.tab li {
  flex: auto;
  text-align: center;
  line-height: 36px;
  background-color: #fff;
}

.tab li.active {
  background-color: #e6e6e6;
}

.tab li:hover {
  cursor: pointer;
}

/* 默认所有选项卡只有一个显示,其它隐藏 */
.item {
  padding: 20px;
  display: none;
}

.item.active {
  display: block;
}
复制代码

js 文件:

// 事件代理实现导航的切换
const tab = document.querySelector(".tab");
const items = document.querySelectorAll(".item");

tab.onclick = ev => {
  // console.log(ev.currentTarget);
  // console.log(ev.target);

  // 1. 清空之前的激活样式,并将当前导航设置为激活状态
  [...tab.children].forEach(item => item.classList.remove("active"));
  ev.target.classList.add("active");

  // 2. 根据data-index来确定应该将哪个列表进行激活并显示出来
  items.forEach(item => item.classList.remove("active"));
  // console.log([...items].filter((item) => item.dataset.index === ev.target.dataset.index).pop());
  [...items]
    .filter(item => item.dataset.index === ev.target.dataset.index)
    .pop()
    .classList.add("active");
};
复制代码

八、案例:一键换肤

html 文件:

<div class="container">
  <img src="./images/1.jpg" alt="" />
  <img src="./images/2.jpg" alt="" />
  <img src="./images/3.jpg" alt="" />
</div>
复制代码

css 文件:

.container {
  width: 300px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  column-gap: 10px;
}
.container > img {
  width: 100%;
  border: 3px solid #fff;
  opacity: 0.6;
}
.container > img:active {
  opacity: 1;
}
.container > img:hover {
  opacity: 1;
  cursor: pointer;
  width: 105%;
}

body {
  background-image: url("./images/1.jpg");
  background-size: cover;
  background-repeat: no-repeat;
    }
复制代码

js 文件:

//   事件代理
const box = document.querySelector(".container");

box.onclick = function (ev) {
  // body
  const body = document.body;
  // 新的背景图片
  let imgUrl = `url('${ev.target.src}')`;
  body.style.backgroundImage = imgUrl;
};

// 使用箭头函数来简化
// document.querySelector(".container").onclick = (ev) =>
//   (document.body.style.backgroundImage = `url('${ev.target.src}')`);
复制代码
文章分类
前端
文章标签