JavaScript Web API
@ 姜波 | 星期一,七月 27 日,2020 年 | 5 分钟阅读 | 更新于 星期一,七月 27 日,2020 年

DOM

DOM本质

  • Document Object Model

  • html文件解析的一棵树

DOM节点操作

  • 获取DOM节点
const div1 = document.getElementById('div1') // 元素
const divList = document.getElementsByTagName('div') // 集合
const containerList = document.getElementsByClassName('.container') // 集合
const pList = document.querySelectorAll('p') // 集合
  • attribute
const pList = document.querySelectorAll('p')
const p = pList[0]
p.getAttribute('data-name')
p.setAttribute('data-name','name1')
p.getAttribute('style')
p.setAttribute('style','font-size:30px;')
  • property
const pList = document.querySelectorAll('p')
const p = pList[0]
console.log(p.style.width) // 获取样式
p.style.width = '100px' // 修改样式
console.log(p.className) // 获取class
p.className = 'p1' // 修改class

// 获取nodeName和nodeType
console.log(p.nodeName)
console.log(p.nodeType)

DOM结构操作

  • 新增/插入节点
const div1 = document.getElementById('div1')
// 添加新节点
const p1 = document.createElement('p')
p1.innerHTML = 'this is p1'
div1.appendChild(p1) // 添加新创建的元素
// 移动已有节点
const p2 = document.getElementById('p2')
div1.appendChild(p2)
  • 获取子元素列表,获取父元素
// 获取子元素列表
const div1 = document.getElementById('div1')
const child = div1.childNodes

// 获取父元素
const div1 = document.getElementById('div1')
const parent = div1.parentNode
  • 删除子元素
const div1 = document.getElementById('div1')
const child = div1.childNodes
div1.removeChild(child[0])

DOM性能

  • DOM操作非常“昂贵”,避免频繁的DOM操作
  • 对DOM查询做缓存
// 不缓存DOM查询结果
for (let i = 0; i < document.getElementsByTagName('p').length; i++){
  // 每次循环,都会计算length,频繁进行DOM查询
}
// 缓存DOM查询结果
const pList = document.getElementsByTagName('p')
const length = pList.length
for (let i = 0; i < length; i++){
  // 缓存length,只进行一次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 ' + x
  frag.appendChild(li)
}
// 都完成之后,再插入到DOM树中
listNode.appendChild(frag)

BOM

  • Browser Object Model
const ua = navigator.userAgent
const isChrome = ua.indexOf('Chrome')

Screen

console.log(screen.width)
console.log(screen.height)

location

console.log(location.href)
console.log(location.protocol) // 'http:' 'https:'
console.log(location.host)
console.log(location.pathname) // /learn/3
console.log(location.search)
console.log(location.hash)

history

history.back()
history.forward()

事件

事件绑定

const btn = document.getElementById('btn1')
btn.addEventListener('click',event => {
  console.log('clicked')
})

// 通用的绑定函数
function bindEvent(elem, type, fn){
  elem.addEventListener(type, fn)
}
const a = document.getElementById('link1')
bindEvent(a, 'click', e => {
  e.preventDefault() // 阻止默认行为
})

事件冒泡

e.stopPropagation() // 阻止冒泡行为

事件代理

  • 代码简洁
  • 减少浏览器内存占用
function bindEvent(elem, type, selector, fn) {
  if(fn == null){
    fn = selector
    selector = null
  }
  elem.addEventListener(type, event => {
    const target = event.target
    if(selector){
      // 代理绑定
      if(target.matches(selector)){
        fn.call(target, event)
      }
    }else{
      // 普通绑定
      fn.call(target, event)
    }
  })
}

ajax

XMLHttpRequest

// get请求
const xhr = new XMLHttpRequest()
xhr.open('GET','/api',true)
xhr.onreadystatechange = function() {
  // 这里的函数异步执行
  if(xhr.readyState === 4){
    if(xhr.status === 200){
      alert(xhr.responesText)
    }
  }
}
xhr.send(null)

状态值与状态码

  • xhr.readyState

    0-(未初始化)还没有调用send()方法

    1-(载入)已调用send()方法,正在发送请求

    2-(载入完成)send()方法执行完成,已经接受到全部响应内容

    3-(交互)正在解析响应内容

    4-(完成)响应内容解析完成,可以在客户端调用

  • xhr.status

    2xx-表示成功处理请求,如200

    3xx-需要重定向,浏览器直接跳转,如301、302、304

    4xx-客户端请求错误,如404、403

    5xx-服务器端错误

跨域:同源策略,跨域解决方案

什么是跨域(同源策略)

  • ajax请求时,浏览器要求当前网页和server必须同源(安全)
  • 同源:协议、域名、端口,三者必须一致
  • 加载图片、css、js可无视同源策略
    • < img src=跨域的图片地址/>
    • < link href=跨域的css地址/>
    • < script src=跨域的js地址></ script>
    • < img/>可用于统计打点,可使用第三方统计服务
    • < link/>< script>可使用CDN,CDN一般都是外域
    • < script>可实现JSONP
  • 所有的跨域,都必须经过server端允许和配合
  • 未经server端允许就实现跨域,说明浏览器有漏洞,危险信号

JSONP

<script>
window.callback = function(data) {
  // 将返回{x: 100}
  console.log(data)
}  
</script>
<sciprt src='https://wilberjiang.com/a.js'></script>
// https://wilberjiang.com/a.js
// callback({x: 100})

CROS(服务端支持)

// 第二个参数填写允许跨域的域名称,不建议直接写'*'
response.setHeader('Access-Control-Allow-Origin', 'http://localhost:8000')
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')

存储

  • 本身用于浏览器和server通讯
  • 被“借用”到本地存储
  • 可用document.cookie='…‘来修改
  • 缺点
    • 存储大小,最大4KB
    • http请求时需要发送到服务器端,增加请求数据量
    • 只能用document.cookie=’…‘来修改

LocalStorage和sessionStorage

  • HTML5专门为存储而设计,最大可存5M
  • API简单易用setItem、getItem
  • 不会随http请求被发送
  • 区别
    • localStorage数据会永久存储,除非代码或手动删除
    • sessionStorage数据只存在于当前会话,浏览器关闭则清空
    • 一般用localStorage会更多一些

生产环境

页面加载过程

  • 加载资源的形式

    • html代码
    • 媒体文件,如图片、视频等
    • javascript、css
  • 加载资源的过程

    • DNS解析:域名->IP地址
    • 浏览器根据IP地址向服务器发起http请求
    • 服务器处理http请求,并返回给浏览器
  • 渲染页面的过程

    • 根据HTML代码生成DOM Tree
    • 根据CSS代码生成CSSOM
    • 将DOM Tree和CSSOM整合形成Render Tree
    • 根据Render Tree渲染页面
    • 遇到< script>则暂停渲染,优先加载并执行JS代码,完成再继续
    • 直至把Render Tree渲染完成

window.onload和DOMContentLoaded

window.addEventListener('load', function() {
  // 页面的全部资源加载完才会执行,包括图片、视频等
})
document.addEventListener('DOMContentLoaded',function() {
  // DOM渲染完即可执行,此时图片、视频还可能没有加载完
})

性能优化

  • 原则
    • 多使用内存、缓存或其他方法
    • 减少CPU计算量,减少网络加载耗时
    • 空间换时间
  1. 让加载更快
    • 减少资源体积:压缩代码
    • 减少访问次数:合并代码,SSR服务器端渲染,缓存
    • 使用更快的网络:CDN
  2. 让渲染更快
    • CSS放在head,JS放在body最下面
    • 尽早开始执行JS,用DOMContentLoaded触发
    • 懒加载(图片懒加载,上滑加载更多)
    • 对DOM查询进行缓存
    • 频繁DOM操作,合并到一起插入DOM结构
    • 节流throttle防抖debounce

防抖

function debounce(fn, delay = 500) {
  // timer是闭包中的
  let timer = null
  
  return function() {
    if(timer) {
      clearTimeout(timer)
    }
    timer = setTimeout(() => {
      fn.apply(this,arguments)
      timer = null
    },delay)
  }
}

节流

function throttle(fn, delay = 500) {
  let timer = null
  
  return function() {
    if(timer) {
      return
    }
    timer = setTimeout(() => {
      fn.apply(this,arguments)
      timer = null
    },delay)
  }
}

安全

XSS跨站请求攻击

XSRF跨站请求伪造

js
保存为图片

公众号

Image text

QQ

Image text

微信

Image text

微信打赏

Image text

社交链接