【前后分离】AJAX

231 阅读9分钟

所有代码都在我的GitHub项目ajax-1

AJAX是什么

  • AJAX是一种技术
  • AJAX内容:用JS发请求和收响应

AJAX的背景

AJAX是浏览器上的功能

  • 浏览器可以发请求,收响应

  • 浏览器在window上加了一个XMLHttpRequest函数

image.png 用这个构造函数(类)可以构造出一个对象

   其实和window.Object差不多,Object用来创建普通对象,XMLHttpRequest用来创建XMLRquest对象的。

JS通过它实现发请求,收响应

  • 准备一个服务器
  1. 使用server.js作为我们的服务器
  2. 下载或复制代码即可用node server.js 8888启动    可用node-dev server.js 8888来启动服务器,因为这个可以自动重启更新服务器(需要用yarn global add node-dev先安装node-dev
  3. 添加index.html / main.js两个路由

发送请求,并且这个请求发送成功了的话,我们就对服务器返回给我们的响应干些啥

(一)步骤

第一步:生成请求对象从而生成请求

const request = new XMLHttpRequest();

第二步:用请求对象的open方法设置请求

open(请求动词 请求的url) 就用这俩参数!

request.open(method, url);

第三步:用请求对象的onload、onerror方法监听请求的成功或者失败。你得知道有没有成功啊。如果成功了,我们肯定得到了响应,我们就可以对响应体干些啥!

request.onload = () => { }//如果onload(请求成功),就执行这个函数(对得到的响应内容干些啥)
request.onerror = () => {};//如果onerror(请求失败),就执行这个函数

事实上,当请求失败并不会触发这个onerror。因为onerror不支持Ajax。 所以专业的前端用onreadystatechange事件来监听请求的成功或失败

第四步:用请求对象的send方法发送请求

request.send();

(二)例子

1、请求CSS

(1)浏览器请求CSS index.html里有一句<link rel="stylesheet" href="/style.css" />,意思是index.html的CSS样式是这样来的:浏览器先读到我有个link标签里面指定我的CSS样式在/style.css这个请求的响应体里,浏览器需要请求它然后才能渲染到我身上。所以浏览器知道后就会去请求/style.css,然后得到响应体(也就是style.css文件里的所有CSS内容),然后把这些CSS内容渲染到index.html上

  • 文件的名字不重要,重要的是文件里的代码。我们请求这个文件(路径,url)其实只是要它里面的代码,也就是响应体

(2)用Ajax请求CSS

const request = new XMLHttpRequest();

request.open("get", "/style.css");

//如果请求这个url:/style.css成功了,请求对象会获得响应(request.response:style.css里的所有内容)
//思考一下怎么把一串css渲染到html上?
//在head里新建一个style标签,把这串css放到style标签里,浏览器就会自动渲染啦(用DOM)
request.onload = () => {  
  const style = document.createElement("style"); //生成一个style标签
  style.innerHTML = request.response; // 把这串CSS插入到style标签
  document.head.appendChild(style); //最后把style标签插入head里完事//CSS就可以在index.html生效了
};
request.onerror = () => {
  console.log("失败了");
};

request.send();

我们在index.html里加一个<button id="getCSS">请求CSS</button>,在main.js里监听这个按钮,当点击按钮时,才请求CSS,方便我们学习研究。

getCSS.onclick = () => {
  const request = new XMLHttpRequest();
  request.open("get", "/style.css");
  request.onload = () => {
    const style = document.createElement("style");
    style.innerHTML = request.response;
    document.head.appendChild(style);
  };
  request.onerror = () => {};
  request.send();
};

2、请求JS

(1)浏览器请求JS

  • 在index.html里有<script src="/2.js"></script>
  • script标签里面要是有内容,就是js代码,浏览器直接执行。
  • 如果没内容,但是有src,src属性指定外部文件路径/2.js,浏览器根据src属性值请求外部文件然后将外部文件的内容也就是js代码插入script标签之间,结果会覆盖原本script标签之间的内容。然后执行这些js代码。

(2)Ajax请求JS

getJS.onclick = () => {
  const request = new XMLHttpRequest();
  request.open("get", "/2.js");
  //当请求这个路径/2.js成功,就会获得响应体,也就是2.js的所有js代码
  //思考一下怎么把这些js代码运用到index.html中去
  //在body里新建script标签,把这些js代码放到script标签里面去,浏览器就会自动运行这些js代码啦(用DOM)
  request.onload = () => {
    const script = document.createElement("script"); 
    script.innerHTML = request.response;
    document.body.appendChild(script);
  };
  request.onerror = () => {};
  request.send();
};

3、请求HTML

只有Ajax可以请求html

getHTML.onclick = () => {
  const request = new XMLHttpRequest();

  request.open("get", "/2.html");
  //当请求这个路径/2.html成功,就会获得响应体,也就是2.html的所有html代码
  //思考一下怎么把这些html代码运用到index.html中去
  //在body里新建div标签,把这些html代码放到div标签里面去,浏览器就会自动渲染这些html代码啦(用DOM)
  request.onload = () => {
    const div = document.createElement("div");
    div.innerHTML = request.response;
    document.body.appendChild(div);
  };
  request.onerror = () => {};
  request.send();
};

(三)修改第三步:重新用onreadystatechange事件来监听请求的成功或失败

  1. 每个XMLHttpRequest请求对象都有readyState属性,用于储存该请求对象的状态。他的属性值如下
  • 从 0 到 4 发生变化。
  • 0: 请求未初始化
  • 1: 服务器连接已建立--即第二步请求对象.open()已经执行
  • 2: 请求已接收--即第四步请求对象.send()已经执行
  • 3: 请求处理中--即已经收到了部分的响应内容
  • 4: 请求已完成,且响应已就绪--即所有的响应都收到了
  1. readState属性值发生改变时会触发readystatechange事件
  2. 每个请求对象都有onreadystatechange属性,他会被我们用个函数赋值:监听readystatechange事件,当这个事件发生,也就是每当readyState属性改变时,就会调用该函数。
  3. 我们把这个变为新的第二步,那么之后,每当readyState 属性改变(2,3,4),就会执行一次onreadystatechange函数
  4. 注意请求肯定都会成功的,因为就是你的请求url写错了,在服务器中还是对应着最后一项404那个,所以readyState肯定会从0-4
getCSS.onclick = () => {
  const request = new XMLHttpRequest();

  request.open("get", "/style.css"); //readyState=1

//当readyState变化一次(2/3/4),就会调用这个函数一次,所以之后会调用这个函数三次
//因为前面的readyState=1已经出现了,在我们定义这个函数前就有了
  request.onreadystatechange = () => {
    if (request.readyState === 4) { //当readyState变为2/3时,调用这个函数但是啥也没干;但是当readyState变为4时,调用这个函数干的事情就多了。
      console.log("下载完成"); //首先,肯定表示响应体已经全部下载完了
      if (request.status >= 200 && request.status < 300) {  //其次,如果响应的状态码为2开头,表示是找到了对应的路径,返回的也是我们想要的响应体CSS代码,那我们就可以开始对响应体搞事情了
        const style = document.createElement("style");
        style.innerHTML = request.response;
        document.head.appendChild(style);
      } else { //否则,状态码是 404,表示在服务器里没找到这个路径,响应体也只是空,那么我们就好心的提醒一下你的CSS加载失败了吧
        alert("加载CSS失败");
      }
    }
  };
  request.send();
};

总结

  1. HTTP是个框,什么都能往里装
  2. 可以装HTML、CSS、 JS、XML .....
  3. 记得在服务器里设置正确的Content-Type这是好习惯
  4. 只要你知道怎么解析这些内容,就可以使用这些内容。不同类型的数据有不同类型的解析办法
  • 得到CSS之后生成style标签
  • 得到JS之后生成script标签
  • 得到HTML之后使用innerHTML和DOM API
  • 得到XML之后使用responseXML和DOM API
//请求CSS
getCSS.onclick = () => {
  const request = new XMLHttpRequest();

  request.open("get", "/style.css"); //readyState=1

  request.onreadystatechange = () => {
    console.log(request.readyState);
    //当statechange发生变化,就会调用这个函数
    if (request.readyState === 4) {
      console.log("下载完成");
      if (request.status >= 200 && request.status < 300) {
        const style = document.createElement("style");
        style.innerHTML = request.response;
        document.head.appendChild(style);
      } else {
        alert("加载CSS失败");
      }
    }
  };
  request.send();
};


//请求JS
getJS.onclick = () => {
  const request = new XMLHttpRequest();

  request.open("get", "/2.js");

  request.onreadystatechange = () => {
    console.log(request.readyState);
    //当statechange发生变化,就会调用这个函数
    if (request.readyState === 4) {
      console.log("下载完成");
      if (request.status >= 200 && request.status < 300) {
        const script = document.createElement("script");
        script.innerHTML = request.response;
        document.body.appendChild(script);
      } else {
        alert("加载CSS失败");
      }
    }
  };

  request.send();
};


//请求HTML
getHTML.onclick = () => {
  const request = new XMLHttpRequest();

  request.open("get", "/2.html");
  
  request.onreadystatechange = () => {
    console.log(request.readyState);
    if (request.readyState === 4) {
      console.log("下载完成");
      if (request.status >= 200 && request.status < 300) {
        const div = document.createElement("div");
        div.innerHTML = request.response;
        document.body.appendChild(div);
      } else {
        alert("加载CSS失败");
      }
    }
  };

  request.send();
};


//请求XML
getXML.onclick = () => {
  const request = new XMLHttpRequest();

  request.open("get", "/4.xml");
  
  request.onreadystatechange = () => {
    console.log(request.readyState);
    if (request.readyState === 4) {
      console.log("下载完成");
      if (request.status >= 200 && request.status < 300) {
        console.log(request.response);
        console.log(request.responseXML); 
        //如果请求xml,那么就不要用response来获得响应体,用responseXML
        //这样获得的响应体会自动变成一个dom对象,我们就可以用DOM操作这个XML DOM对象了
        //(DOM让HTML成为一个对象,也让XML成为一个对象)
        const dom = request.responseXML;
        const text = dom.getElementsByTagName("body")[0].textContent;
        console.log(text);
      } else {
        alert("加载CSS失败");
      }
    }
  };

  request.send();
};

JSON

什么是JSON

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。是标记语言。和HTML、XML、markdown一样是用来展示数据的

JSON语法

  1. 必看中文官网
  2. 只支持6种数据类型
  • string-只支持双引号,不支持单引号和无引号
  • number-支持科学记数法
  • bool- true和false
  • null -没有undefined
  • object
  • array
  • 不支持函数,不支持变量(所以也不支持引用)

常用API

  1. JSON.parse()
  • 将符合JSON语法的字符串转换成JS对应类型的数据
  • JSON字符串=> JS数据
  • 由于JSON只有六种类型,所以转成的数据也只有6种
  • 如果不符合JSON语法,则直接抛出一个Error对象
  • 一般用try catch捕获错误
let object
try{object = JASON.parse('{'name':'frank'}')}
catch(error){
    console.log('出错了,错误详情是')
    console.log(error)
    object = {'name':'no name'}
}
console.log(object)
  1. JSON.stringify()
  • 是JSON.parse的逆运算
  • JS数据=> JSON字符串
  • 由于JS的数据类型比JSON多,所以不一定能成功.如果失败,就抛出一个Error对象。或者直接把函数忽略

请求JSON

getJSON.onclick = () => {
  const request = new XMLHttpRequest();
  request.open("get", "/5.json");
  request.onreadystatechange = () => {
    if (
      request.readyState === 4 &&
      request.status >= 200 &&
      request.status < 300
    ) {
      console.log(request.response);
      const object = JSON.parse(request.response); //用JSON的parse函数可以把符合json语法的东西变成对象或者xx
      console.log(object);
       myName.textContent = object.name;
    }
  };
  request.send();
};

在index.html里加个 <span id="myName"></span>,这样当点击按钮,就会把json变成的的dom对象object的name属性的值给span标签,这样页面就会出现我的名字。

加载列表

需求

  • 用户打开页面,看到第一页数据
  • 用户点击下一页, 看到第二页数据
  • 用户点击下一页, 看到第三页数据
  • 用户点击下一页, 提示没用更多了

优化点

能不能在点击第三页的时候就禁用下一页按钮

步骤

新建文件夹db,文件page1.JSON、page2.JSON、page3.JSON

(1)第一页

不需要动态加载。静态加载。打开页面就可以看见第一页数据。所以应该在index.html里面就写好:那就在服务器的路由的响应体里从数据库调出数据再加上。前后端不分离的渲染技术

  • 在index.html里添加一个占位符<div>{{page1}}</div>
  • 在服务器index.html的路由的响应体里把这个占位符替换成文件page1.JSON的内容
if (path === "/index.html") {
    //如果请求是这个,该请求的响应就是这些
    response.statusCode = 200;
    response.setHeader("Content-Type", "text/html;charset=utf-8");
    //把index.html变成字符串string
    let string = fs.readFileSync("public/index.html").toString();
    //把page1.json变成字符串page1
    const page1 = fs.readFileSync("db/page1.json").toString();
    //因为字符串page1符合JSON语法,所以变成js数据中的数组
    const array = JSON.parse(page1);
    //把数组中每一项元素的id都包上li,成为一新数组,之后把这个新数组变成字符串result
    const result = array.map(item => `<li>${item.id}</li>`).join("");

    //把字符串string里的 占位符 变成 result外部再包上ul的字符串,成为新的字符串string
    string = string.replace("{{page1}}", `<ul id='xxx'>${result}</ul>`);
    //响应体则为字符串string
    response.write(string);
    response.end();
  }

(2)第二页

需要点击下一页才能看到,用AJAX

  • 在index.html里添加按钮<button id="getPAGE"请求下一页</button>
  • 在server.js里加上page2.json的路由
  • 在main.js里面监听该按钮
getPAGE.onclick = () => {
  const request = new XMLHttpRequest();
  request.open("get", "/page2.json");
  request.onreadystatechange = () => {
    if (
      request.readyState === 4 &&
      request.status >= 200 &&
      request.status < 300
    ) {
      console.log(request.response);
      //把符合json语法的响应体变成对应的js数组
      const array = JSON.parse(request.response);
      //数组中的每一个元素都
      array.forEach(element => {
        const li = document.createElement("li"); //先新建元素li
        li.textContent = element.id; //让li里的内容变成元素的id
        xxx.appendChild(li); //让li成为ul的孩子
      });
    }
  };
  request.send();
};

(3)第三页

重新修改监听请求下一页的按钮

  • 在server.js里加上page2.json的路由
let n = 1; //声明页数为n,初始值是1。 第一页
getPAGE.onclick = () => {
  const request = new XMLHttpRequest();
  request.open("get", `/page${n + 1}.json`); //每次点击,请求的url是page n+1(当前页数的下一页)
  request.onreadystatechange = () => {
    if (
      request.readyState === 4 &&
      request.status >= 200 &&
      request.status < 300
    ) {
      console.log(request.response);
      //把符合json语法的响应体变成对应的js数组
      const array = JSON.parse(request.response);
      //数组中的每一个元素都
      array.forEach(element => {
        const li = document.createElement("li"); //先新建元素li
        li.textContent = element.id; //让li里的内容变成元素的id
        xxx.appendChild(li); //让li成为ul的孩子
      });
      n+=1  //每次请求成功了,函数都执行完了,就把n变成n+1.上面的n+1只是输出n+1,并没有改变n的值
    }
  };
  request.send();
};

关于路径

在server.js里 if里面的是用户将要输入的路径,当时是根据用户来,是请求路径,而不是根据你文件夹里的路径来啊

在响应体里的路径,当然是根据你的文件夹里那个文件的路径来,因为你确实是要调用这个文件里的内容。所以server.js和public是同级,当前路径就是server.js和public的爸爸(根路径),所以public里的文件可以写成"(当前路径:空)public/main.js"

else if (path === "/main.js") {
    response.statusCode = 200;
    response.setHeader("Content-Type", "text/javascript;charset=utf-8");
    response.write(fs.readFileSync("public/main.js"));
    response.end();
  } else if (path === "/style.css") {

在main.js里ajax时,请求的路径当然是if里面那个路径,因为是要对她请求的请求路径啊

getCSS.onclick = () => {
 const request = new XMLHttpRequest();

 request.open("get", "/style.css"); //readyState=1

 request.onreadystatechange = () => {
   console.log(request.readyState);
   //当statechange发生变化,就会调用这个函数
   if (request.readyState === 4) {
     console.log("下载完成");
     if (request.status >= 200 && request.status < 300) {
       const style = document.createElement("style");
       style.innerHTML = request.response;
       document.head.appendChild(style);
     } else {
       alert("加载CSS失败");
     }
   }
 };
 request.send();
};

在index.html里你要引用main.js,那也是浏览器去向它发请求,必须是请求路径!

<script src="/main.js"></script>

但是以前我们写网页,没自己写服务器,用的是http server静态服务器 提供的网页预览,(他会自动把应该是请求的路径都改成请求路径,就算我们没有写对)这句话先写着,可能不对。