三、从JS基础知识到Web API(ajax-跨域-存储)

348 阅读5分钟

1.DOM(Document Object Model) 文档对象模型

1.DOM是哪种数据结构
树 (DOM树)
2.DOM操作常用API
1.DOM节点操作
    - querySelector(第一个节点);
    - querySeletorAll(指定选择器组的所有节点集合);
    - getElementById(指定id的节点);
    - getElementByTagName(指定标签节点集合div、p、span等)
2.DOM结构操作
    - innerHTML; 标签内的文本和标签,字符串类型
    - parentNode; 指定节点父节点
    - children; 指定元素子节点集合
    - fistElementChild; 指定元素的第一个子节点
    - lastElementChild  指定元素的最后一个子节点
3.attribute和property的区别?
1.property
    修改对象属性,不会体现到HTML结构中 (尽量使用property来做)
2.attribute
    修改html属性,会改变html结构
这两个都有可能会引起DOM重新渲染
3.一次性插入多个DOM节点,考虑性能
const listNode = document, getElementById('list');
// 创建一个文档片段,此时还没有插入到 DOM 树中
const frag = document.createDocumentFragment()
// 执行插入
for (let x = 0; x < 10; x++) {
    const li = document.createElement("li");
    li.innerHTML = "List item u" + x
    frag.appendChild("li");
}
// 都完成之后,再插入到 DOM 树中
listNode.appendChild(frag)
4.DOM本质
// DOM结构 本质是一棵树,从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></body>
</html>
5.DOM节点操作
1.获取DOM节点
    const div1 = document.getElementById('div1');  // 元素
    const divList = document.getElementsByTagName('div'); // 获取到是一个集合
    console.log(divList.length, divList[0]);  // 长度 及 元素索引
    const container = document.getElementsByClassName('.container'); // 通过类名来获取元素
    const pAll = document.querySelectorAll('p') // 获取所有p标签元素为一个集合
    
2.attribute(属性)  //  对DOM节点属性做修改
    const p = pAll[0]
    p.setAttribute('data-name', 'imooc')
    p.setAttribute('style', 'color: blue')
3.property(属性)  // 不是API,是一种用js属性操作的一种形式
    const pAll = document.querySelectorAll('p')
    const p = pAll[0]
    p.style.width  // 获取样式
    p.style.width = "100px"  // 修改样式
    p.className = 'p1'  // 修改class
    p.className         // 获取class
    p.nodeName          // 节点的名称
    p.nodeType          // 节点的类型  1
6.DOM结构操作
// 建立两个div容器
const newDiv1 = document.getElementById('div1')
const newDiv2 = document.getElementById('div2')

// 创建节点
const newP1 = document.createElement('p');
newP1.innerHTML = "this name is P"

// 插入节点
newDiv1.appendChild(newP1)

// 移动节点
const p1 = document.getElementById('p1')
div2.appendChild(p1)

// 获取父元素
const a = p1.parentNode
console.log(a);

// 获取子元素
const b = newDiv1.childNodes
console.log(b); 

// 删除子元素
const c = newDiv1.removeChild(元素[0])
console.log(b);
7.DOM性能
1.DOM 操作非常昂贵,避免频繁的操作DOM操作
2.DOM查询做缓存
    //不缓存 DOM 查询结果
    for (let = 0; i < document.getElementsByTagName('p').length; i++) {
        // 每次循环,都会计算 Length ,频繁进行 DOM 查询
    }

    // 缓存 DOM 查询结果
    const pList = document.getElementsByTagName('p')
    const length = pList.lengthfor(let i = 0; i < length; i++) {
        // 缓存 length ,只进行一次 DOM 查询
    }
    
3.将频繁操作改为一次性操作
const listNode = document, getElementById('list');

// 创建一个文档片段,此时还没有插入到 DOM 树中
const frag = document.createDocumentFragment()

// 执行插入
for (let x = 0; x < 10; x++) {
    const li = document.createElement("li");
    li.innerHTML = "List item u" + x
    frag.appendChild("li");
}

// 都完成之后,再插入到 DOM 树中
listNode.appendChild(frag)

2.BOM)Browers Object Model) 浏览器对象模型

1. navigator
// navigator
const ua = navigator.userAgent
const isChrome = ua.indexOf('Chrome')
console.log(isChrome);
2. screen
// screen
console.log(screen.width);
console.log(screen.height);
3. loction
// location
// 示例 https://fanyi.baidu.com/translate?aldtype=16047&query=Reinitialized+existing+Git+repository+in+E%3A%2F.git%2F%0D%0A&keyfrom=baidu&smartresult=dict&lang=auto2zh#en/zh/
console.log(location.href); 
console.log(location.protocol);  // https Or http
console.log(location.pathname);  // '/translate'
// '?aldtype=16047&query=Reinitialized+existing+Git+repository+in+E%3A%2F.git%2F%0D%0A&keyfrom=baidu&smartresult=dict&lang=auto2zh'
console.log(location.search); 
// '#en/zh/'
console.log(location.hash);  // #号后面的
4. history
// history  用来做一些跳转操作
history.back()  // 后退
history.forward() // 前进
history.go()  // 去某一页
history.pushState()  // 新增跳转页面
history.replaceState()  // 替换
5. 如何识别浏览器的类型
const ua = navigator.userAgent
const isChrome = ua.indexOf('Chrome')
console.log(isChrome);
6. 分析拆解url各个部分
// location
// 示例 https://fanyi.baidu.com/translate?aldtype=16047&query=Reinitialized+existing+Git+repository+in+E%3A%2F.git%2F%0D%0A&keyfrom=baidu&smartresult=dict&lang=auto2zh#en/zh/
console.log(location.href); 
console.log(location.protocol);  // https Or http
console.log(location.pathname);  // '/translate'
// '?aldtype=16047&query=Reinitialized+existing+Git+repository+in+E%3A%2F.git%2F%0D%0A&keyfrom=baidu&smartresult=dict&lang=auto2zh'
console.log(location.search); 
// '#en/zh/'
console.log(location.hash);

3.事件绑定

1. 事件绑定
// 事件绑定
const btn = document.getElementById('btn1');
btn.addEventListener('click', event => {
    console.log('clicked');
})

// 通用的事件绑定 1
function bindEvent(elem, type, fn) {
    elem.addEventListener(type, fn)
}

const p1 = document.getElementById('btn1');
bindEvent(p1, 'click', e => {
    e.target() // 点击的是哪个元素并且获取
    e.preventDefault() // 阻止默认行为
    alert('clicked')
})
2. 事件冒泡
// 事件冒泡是根据dom层级 由内向外触发冒泡
<body>
    <div id="div1">
        <p id="p1">激活</p>
        <p id="p2">取消</p>
        <p id="p3">取消</p>
        <p id="p4">取消</p>
    </div>
    <h1>----------------------------</h1>
    <h1>----------------------------</h1>
    <div id="div2">
        <p id="p5">取消</p>
        <p id="p6">取消</p>
    </div>
    <script>
        function bindEvent (elem, type, fn) {
            elem.addEventListener(type, fn)
        }
        const p1 = document.getElementById('p1');
        const body = document.body
        bindEvent(p1, 'click', e => {
            e.stopPropagation()  //  可阻止事件冒泡, 可自行屏蔽测试
            alert('激活');
        })
        bindEvent(body, 'click', e => {
            alert('取消');
        })
    </script>
</body>
3. 事件代理
// 1.代码简洁
// 2.减少浏览器内容占用, 用父元素来执行事件监听没必要,减少每个元素上都去添加,只需要执行一次,减少性能消耗
// 3.(切勿)滥用
<body>
    <div id="div1">
        <a href="#">链接1</a>
        <a href="#">链接2</a>
        <a href="#">链接3</a>
        <a href="#">链接4</a>
    </div>
    <button>
        点击增加a标签
    </button>
    <script>
        function bindEvent (elem, type, fn) {
            elem.addEventListener(type, fn)
        }
        const div1 = document.getElementById('div1');
        bindEvent(div1, 'click', e => {
            e.preventDefault()
            const target = e.target
            if (target.nodeName === 'A') {
                alert(target.innerHTML)
            }
        })
    </script>
</body>
4. 编写一个通用的事件监听函数
function bindEvent (elem, type, selector, fn) {
    // 判断传三个参数的情况
    if (fn == null) {
        fn = selector
        selector = null
    }
    elem.addEventListener(type, event => {
        const target = event.target
        // 有selector的情况
        if (selector) {
            // 代理
            if (target.matches(selector)) {  // matches  判断DOM元素是否是触发元素
                fn.call(target, event)
            }
        } else {
            // 普通绑定
            fn.call(target, event)
        }
    })
}
5. 描述事件冒泡的流程
1. 基于DOM树形结构
2. 事件会顺着触发元素向上冒泡
3. 应用场景: 事件代理
6. 无限下拉的图片列表,如何监听每个图片的点击?
1.事件代理
2.使用e.target获取触发元素
3.用matches 来判断是否是触发元素

4.ajax

1.XMLHttpRequest
// xhr.readyState
    0. UNSET 尚未调用open方法
    1. OPENED open 方法已被调用
    2. HEADERS_RECEIVED send 方法已被调用, header 已被接受
    3. LOGING 下载中,responseText 已有部分内容
    4. DONE 下载完成
1.GET请求
<script>
    // 1. 初始化一个实例
    const xhr = new XMLHttpRequest();
    // 2. .open  true 表示异步请求  false 同步请求
    xhr.open('GET', '../test/test.json', true);
    // 3. 
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                console.log(JSON.parse(xhr.responseText));
                alert(xhr.responseText)
            } else {
                alert('其他情况')
            }
        }
    }
    xhr.send(null);
</script>
2.POST请求
<script>
    const xhr = new XMLHttpRequest();
    xhr.open('POST', '/login', true);
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                console.log(JSON.parse(xhr.responseText));
                alert(xhr.responseText)
            } else {
                alert('其他情况')
            }
        }
    }
    const data = {
        name: 'zhangsan',
        password: '217849'
    }
    xhr.send(JSON.stringify(data));
</script>
2.状态码
// xhr.status
    2xx- 表示成功处理请求, 比如常见的200
    3xx- 表示需要重定向,浏览器直接跳转
        301 永久重定向 从a页面会一直定格到b页面
        302 临时重定向 从a页面跳转到b页面  下一次进来还是进入a页面
        304 资源未被改变 可以做性能优化
    4xx 客户端请求错误
        404 找不到页面  访问不存在在的地址
        403 未拥有访问权限,像一些内部系统
    5xx 服务器端错误  错误一般都会指向后端(服务端)
3.跨域:同源策略,跨域解决方案
1.同源策略
   ajax 请求时,浏览器要求当前网页和server必须同源(安全)
   同源: 协议,域名,端口,三者必须一致
   前端: http:// a.com:8080/  
   server: https://b.com/api/xxx
2.加载图片 css,js可以无视同源策略
    <img src="跨域的图片地址" />   但是有些图片资源可能会做一些防盗链设置(后端)
    <link href="跨域的css地址" >  cdn  cdn一般都是外域
    <script src="跨域的js地址">  cdn
    script 可以实现jsonp
    <img /> 可用于统计打点(pv,pu),可使用第三方统计服务(百度统计,站长之家)
3.跨域
    所有的跨域,都必须经过server端允许和配置
    未经server 端允许就实现跨域,说明浏览器有漏洞,具有较大的安全隐患,容易被攻击
4.手写一个简易的ajax
<script>
function ajax (url, successFn) {
    const xhr = new XMLHttpRequest();
    xhr.open('POST', url, true);
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                console.log(JSON.parse(xhr.responseText));
                successFn(xhr.responseText)
            } else {
                alert('其他情况')
            }
        }
    }
    xhr.send(null);
}
</script>
5.跨域的常用实现方式
1.JSONP
访问一个地址 `http://ashjkh.com/` ,服务端一定会返回一个html文件吗?
服务端可以任意拼接数据返回,只要符合html的格式要求
同理于<script src="http://ashjkh.com/getData.js"> 一定会返回getDatajs吗?
不是的,只要是网址,服务端可以拼接任何的符合格式的数据并且返回
    (1) <script> 可以绕过跨域限制
    (2) 服务器可以任意动态拼接数据返回
    (3) <script> 就可以获得跨域的数据,只要服务端愿意返回
2.CORS-服务器设置 http header
// 第二个参数 填写允许跨域的域名称  不建议直接写*
response.setHeader("Access-Control-Allow-Origin", "http://localhost:8080/")
response.setHeader("Access-Control-Allow-Headers", "X-Requested-With")
response.setHeader("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS")
// 接收跨域的cookie
response.setHeader("Access-Control-Allow-Credentials", "true")
3.框架中
   在目前用的vue react框架中的话直接通过配置跨域代理(反向代理),过于简单,不再赘述

5.存储

1. cookie
    1.本身用于浏览器和server通讯(本身价值在于 本地和服务器端进行通讯)
    2.被借用到本地存储来
    3.前端可用document.cookie = '' 来修改() 同一个key会覆盖,不同key会追加
    4.(后端也是可以修改的)
  缺点: 
      cookie 最大存储4kb
      http请求时需要发送到服务端,增加请求数据量
      document.cookie = "..."  不太方便操作
2. sessionStorage和localStorage
1.HTML5 专门为存储而设计的,最大可存储5M
2.API也是比较简单好记,setItem,getItem
3.不会随着http请求被发送出去
4.localStorage数据会永久存储,除非代码或手动删除
5.sessionStorage 数据只存在于当前会话,浏览器关闭则会清空
3. 描述cookie localStorage sessionStorage 的区别
根据上述俩段内容其实回答的时候可以从以下三个方面来回答就OK1.容量
2.API的易用性
3.是否会随着http请求发送出去