Skip to content

浏览器相关

一、认识浏览器运行态下的js

包含:BOM、DOM、ECMAScript

js
(function (content, undefined) {
    const _class = ["js", "browser", "vue"]

    // 1.想全局作用域中存储一个class变量
    window.classArr = _class.map(item => item)

    // 2.获取当前页面地址
    const _url = location.href;

    // 3.改变页面标题
    document.title = "pipi class"

    // 4.获取渲染节点
    document.getElementById("app");
})(this)

// 简述:
// ECMAScript - 处理了基础逻辑、数据处理
// BOM - 对浏览器本身功能区域的汇总处理
// DOM - 对浏览器内部各个元素功能区域的汇总处理

二、BOM

1.location

js
location.href => "https://www.pipi.com/search?class=browser#comments"   // 当前页面的完整URL
        .origin => "https://www.pipi.com"   // URL的起源,即协议、主机和端口
        .protocol => "https:"   // 协议部分
        .host => "www.pipi.com"     // 主机名
        .port => ""     // 端口号
        .pathname => "/search"      // 路经
        .search => "?class=browser"     // 参数
        .hash => "#comments"    // SPA(单页应用) / iframe

location.assign("${url}")  // 跳转到指定path,替换pathname
        .replace("${url}")   // 效果同assign,同时替换掉浏览器历史
        .reload()
        .toString() // 阐述当前地址=>字符串
  • 面试场景: 手写拆解路径题目
  • 面试方向:
    1. location 本身的api操作
    2. 路由相关:跳转、参数、操作 => 场景题:可返回、是否可刷新 => 框架
    3. url处理 - 手写处理 & 正则

2.history

history.state => 存储当前页面状态

history.pushState() ------ 跳转到指定的状态页

   .replaceState() ------ 替换当前状态
  • 面试方向 - 路由方向hsitory和hash模式的利弊分析:
  1. history需要服务器配置,否则直接访问路由可能会404,hash不需要
  2. history更干净没有#
  3. hash兼容性更好

3.navigator

  • 浏览器信息大集合

    js
    navigator.userAgent  // 获取当前用户环境信息
  • 面试方向

    1. UA读取信息 => 浏览器兼容性
    2. 剪切板 & 键盘操作 => 登录 or 验证码
    3. 环境 => 微信 => unionId appid

4.screen

表示显示区域 - 屏幕

  • 面试方向 - 判断区域大小、方位 window 视窗判断:
    1. 全局入口处获取 window.innerHeight window.innerWidth
    2. 文本处获取 document.documentElement.clientHeight document.body.clientHeight
    3. 网页的size => offsetHeight = clientHeight + 滚动条 + 边框document.documentElement.offsetHeightdocument.body.offsetHeight

定位:

scrollLeft / scrollTop - 距离常规左上滚动的距离

offsetLeft / offsetTop - 距离常规左上相对距离

el.getBoundingClientRect()

   .top .left .bottom .right   直接拿到元素上下左右
  • 但是存在兼容性问题 - IE 会多2px

三、Event事件模型

js
   <div id="app">
      <p id="dom">Click</p>
   </div>

    // 冒泡 - ms: p -> div -> body -> HTML -> document
    // 捕获 - ns: document -> HTML -> body -> div -> p
    //    button.addEventListener('click', handleClick, {
    //         capture: true, // 在捕获阶段调用事件监听器,不填默认是false,也就是冒泡
    //         once: true,    // 只调用一次事件监听器
    //         passive: true  // 事件监听器不会调用 preventDefault()
    //    });

   // 追问:
   // 1.如何阻止事件的传播 
   event.stopPropagation();
   // 注意:无论向上还是向下都是可以阻止的 => 无法阻止默认事件的发生

   // 2.如何阻止默认事件的传播
   event.preventDefault();

   // 3.相同节点绑定多个同类事件,如何阻止
   event.stopImmediatePropagation();

   // 追问 => 样式上 & 性能上

   // 4.手写事件绑定兼容
   // attachEvent & addEventListener
   // 区别
   // (1) 传参 attachEvent对于事件名加上"on"
   // (2) 执行顺序,attachEvent - 后绑定先执行;addEventListener - 先绑定先执行
   // (3) 解绑 detachEvent vs removeEventListener
   // (4) 阻断 e.cancelBubble = true vs e.StopPropagation()
   // (5) 默认事件打断 e.returnValue = true vs e.preventDefault()

    class BindEvent {
        constructor(element) {
            this.element = element;
        }

        addEventListener = (type, handler) => {
            if (this.element.addEventListener) {
                this.element.addEventListener(type, handler, false)
            } else if (this.element.attachEvent) {
                const element = this.element;
                this.element.attachEvent("on" + type, () => {
                    handler.call(element);
                })
            } else {
                this.element["on" + type] = handler;
            }
        }

        removeEventListener = (type, handle) => {
            if (this.element.removeEventListener) {
                this.element.removeEventListener(type, handler, false)
            } else if (this.element.detachEvent) {
                const element = this.element;
                this.element.detachEvent("on" + type, handler)
            } else {
                this.element["on" + type] = null;
            }
        }

        static stopPropagation(e) {
            if (e.stopPropagation) {
                e.stopPropagation();
            } else {
                e.cancelBubble = true;
            }
        }

        static preventDefault(e) {
            if (e.preventDefault) {
                e.preventDefault();
            } else {
                e.returnValue = true;
            }
        }
    }

    // 5. 性能优化 - 事件代理
    <ul class="list">
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
    </ul>
    // 也就是若干年前,直接给父dom绑定事件,利用冒泡
    (() => {
        function onClick(e) {
            var e = e || window.event;
            if (e.target.nodeName.toLowerCase() === "li") {
                const liList = this.querySelectorAll("li");
                const index = Array.prototype.indexOf.call(liList, e.target);
            }
        }
        const list = document.querySelector(".list");
        list.addEventListener("click", onClick, false);
    })()

四、网络层

js
    // 实例化
    const xhr = new XMLHttpRequest();

    // 初始化链接
    // xhr 有一个open方法
    // oepn - 5个参数:
    // 1. method - get/post ; 
    // 2. url - 请求的地址;
    // 3. async - 是否异步请求
    xhr.open(method,url,async);

    // send发送请求
    // 内容:post - 将请求体的参数传入:get - 可以不传 or 传入null
    xhr.send(data);

    // 接受
    // xhr.readyStatus:(返回参数:)
    // 0 - 尚未调用open 
    // 1 - 已调用open
    // 2 - 已发送请求(已调用send)
    // 3 - 已接收到请求返回的数据
    // 4 - 请求已完成
    xhr.onreadystatuschange = () => {
        if (xhr.readyStatus === 4) {
            if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
                console.log("请求成功", xhr.responseText)
            }
        }
    }

    xhr.timeout = 1000;
    
    xhr.ontimeout = ()=>{}
js
    // 手写ajax
    function ajax(options) {
        let {
            url,
            method = 'get',
            async = true,
            timeout = 3000,
            data = {},
        } = options;

        const xhr = new XMLHttpRequest();

        if (timeout && timeout > 0) xhr.timeout = timeout;

        return new Promise((resolve, reject) => {
            // 1.阶段处理

            // 成功
            xhr.onreadystatechange = () => {
                if (xhr.readyState === 4) {
                    if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
                        resolve && resolve(xhr.responseText);
                    } else {
                        reject && reject();
                    }
                }
            }

            // 失败
            xhr.ontimeout = () => reject && reject("超时");
            xhr.onerror = (err) => reject && reject(err);

            // 2.传参处理
            let _params = [];
            let encodeData;
            if (data instanceof Object) {
                for (let key in data) {
                    //  参数拼接
                    _params.push(encodeURIComponent(key) + "=" + encodeURIComponent(data[key]))
                }
                encodeData = _params.join("&");
            }

            // get类型处理
            if (method === "get") {
                const index = url.indexOf("?");
                if (index === -1) url += "?";
                else if (index != url.length - 1) url += "&";
                url += encodeData;
            }

            // 3.初始化链接
            xhr.open(method, url, async);

            // 4.发送连接
            if (method === "get") {
                xhr.send(null);
            } else {
                xhr.setRequestHeader(
                    "Content-type", "application/x-www-form-urlencoded;charset=UTF-8"
                );
                xhr.send(encodeData);
            }
        })
    }

    // 封装
    ajax({
        url: "",
        method: "get",
        async: true,
        timeout: 3000,
        data: {},
    }).then(res => { }, err => { })

鄂ICP备2024055897号