【每日前端面经】2024-03-09

【每日前端面经】2024-03-09

欢迎订阅我的前端面经专栏: 每日前端面经

本期题目来源: 牛客

文件上传

  • 传统表单上传

    • 浏览器支持度高
    • 无需额外的 JS 代码
    • 无法自定义上传页面
    • 不适合实现高级功能
    • 页面刷新后需要重新选择文件
  • XMLHttpRequest对象

    js 复制代码
    const fileInput = document.getElementById("fileInput");
    const uploadButton = document.getElementById("uploadButton");
    
    uploadButton.addEventListener("click", () => {
        const file = fileInput.files[0];
        const xhr = new XMLHttpRequest();
    
        xhr.open("POST", "/upload", true);
        xhr.onreadystatechange = () => {
            if (xhr.readySyaye === 4 && xhr.status === 200) { console.log("上传成功"); }
        }
    
        const formData = new FormData();
        formData.append("file", file);
        xhr.send(formData);
    });
    • 支持自定义上传页面和进度显示
    • 可以实现高级功能,如断点续传、取消上传等
    • 使用回调函数处理请求状态,代码繁琐
    • 不支持 Promise,需要手动处理异步操作
  • Fetch

    js 复制代码
    const fileInput = document.getElementById("fileInput");
    const uploadButton = document.getElementById("uploadButton");
    
    uploadButton.addEventListener("click", () => {
        const file = fileInput.files[0];
        const formData = new FormData();
        formData.append("file", file);
    
        try {
            const response = await fetch("/upload", {
                method: "POST",
                body: formData
            });
            if (response.ok) {
                console.log("上传成功");
            } else {
                console.error("上传失败");
            }
        } catch (error) {
            console.error("上传出错", error);
        }
    });
    • 使用 Promise,便于理解
    • 支持自定义上传界面和进度显示
    • 可以实现高级功能,如断点续传、取消上传等
    • 浏览器兼容性相对弱些
  • 第三方库

按需加载

指当用户触发了动作时才加载对应的功能。触发的动作是要看具体的业务场景而言,包括但不限于以下几种情况:鼠标点击、输入文字、拉动滚动条、鼠标移动、窗口大小更改等。加载的文件,可以是JS、图片、CSS、HTML等

HTML 按需加载

按需解析 HTML,就是页面一开始不解析 HTML,根据需要来解析 HTML。解析 HTML 都是需要一定事件,特别是 HTML 中包含有 img 标签,引用了背景图片时,如果一开始就解析,那么势必会增加请求数。常见的有对话框、拉菜单、多标签的内容展示等,这些一开始是不需要解析,可以按需解析。实现按需解析,首先要用 <script type='text/x-template'>html</script> 这个标签来忽略对 HTML 的解析。然后根据触发的动作,把 script 里面的 HTML 获取出来,填充到对应的节点中

html 复制代码
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>按需解析HTML</title>
</head>
<body>
<script type="text/x-template" id="suc_subscription">
    <!--假设这里的样式box-dytz 引用了一张背景图--->
    <div>
    <!--这里暂且用这张图片作为测试,实际中,大家可以替换为任何图片-->
    <img src="http://tid.tenpay.com/wp-content/uploads/2012/12/按需加载.jpg" />
    </div>
</script>
<div id="success_dilog"></div>
<input type="button" value="点我展示HTML"  onclick="showHTML()"  />
<script>
    function showHTML(){
        document.getElementById('success_dilog').innerHTML =  document.getElementById('suc_subscription').innerHTML;
    }
</script>
</body>
</html>

图片按需加载

按需加载图片,就是让图片默认开始不加载,而且在接近可视区域范围时,再进行加载,也称之为懒加载

触发方式为当滚动条拉动到某个位置时,即将进入可视范围的图片需要加载

html 复制代码
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>图片按需加载</title>
</head>
<body>
    <style>
        li {float:left;width:200px;}
    </style>
    <div style="widh:100%;height:1200px;border:1px solid #000">这里是空白的内容,高度为900像素,目的是方便出现滚动条</div>
    <ul style="width:600px;">
        <li> <img width="158" height="158" data-src="http://pic6.nipic.com/20100423/4537140_133035029164_2.jpg"  /> </li>
        <li> <img width="158" height="158" data-src="http://www.jiujiuba.com/xxpict2/picnews/62223245.jpg"  /> </li>
        <li> <img width="158" height="158" data-src="http://www.bz55.com/uploads/allimg/100729/095KS455-0.jpg"  /> </li>
        <li> <img width="158" height="158" data-src="http://www.hyrc.cn/upfile/3/200611/1123539053c7e.jpg"/> </li>
        <li> <img width="158" height="158" data-src="http://www.mjjq.com/blog/photos/Image/mjjq-photos-903.jpg" /> </li>
        <li> <img width="158" height="158" data-src="http://c.cncnimg.cn/000/954/1416_2_b.jpg"  /> </li>
        <li> <img width="158" height="158" data-src="http://www.jiujiuba.com/xxpict2/picnews/62223231.jpg" /> </li>
        <li> <img width="158" height="158" data-src="http://www.mjjq.com/pic/20070530/20070530043314558.jpg" /> </li>
    </ul>
    <script>
        var API = {  
            /**
              * 兼容Mozilla(attachEvent)和IE(addEventListener)的on事件
              * @param {String} element DOM对象 例如:window,li等
              * @param {String} type on事件类型,例如:onclick,onscroll等
              * @param {Function} handler 回调事件
              */
            on: function(element, type, handler) {
                return element.addEventListener ? element.addEventListener(type, handler, false) : element.attachEvent('on' + type, handler)
            },  
            /**
              * 为对象绑定事件
              * @param {Object} object 对象
              * @param {Function} handler 回调事件
              */
            bind: function(object, handler) {
                return function() {
                    return handler.apply(object, arguments)
                }
            },  
            /**
              * 元素在页面中X轴的位置
              * @param {String} element DOM对象 例如:window,li等
              */
            pageX: function(El) {
                var left = 0;
                 do {
                    left += El.offsetLeft;
 
                } while(El.offsetParent && (El = El.offsetParent).nodeName.toUpperCase() != 'BODY');
                return left;
 
            },  
            /**
              * 元素在页面中Y轴的位置
              * @param {String} element DOM对象 例如:window,li等
              */
            pageY: function(El) {
                var top = 0;
                 do {
                    top += El.offsetTop;
 
                } while(El.offsetParent && (El = El.offsetParent).nodeName.toUpperCase() != 'BODY');
                return top;
 
            },  
            /**
              * 判断图片是否已加载
              * @param {String} element DOM对象 例如:window,li等
              * @param {String} className 样式名称
              * @return {Boolean} 布尔值
              */
            hasClass: function(element, className) {
                return new RegExp('(^|\\s)' + className + '(\\s|$)').test(element.className)
            },  
            /**
              * 获取或者设置当前元素的属性值
              * @param {String} element DOM对象 例如:window,li等
              * @param {String} attr 属性
              * @param {String} (value) 属性的值,此参数如果没有那么就是获取属性值,此参数如果存在那么就是设置属性值
              */
            attr: function(element, attr, value) {
                 if (arguments.length == 2) {
                    return element.attributes[attr] ? element.attributes[attr].nodeValue : undefined
                }
                else if (arguments.length == 3) {
                    element.setAttribute(attr, value)
                }
            }
        };
 
        /**
          * 按需加载
          * @param {String} obj 图片区域元素ID
          */
        function lazyload(obj) {
            this.lazy = typeof obj === 'string' ? document.getElementById(obj) : document.getElementsByTagName('body')[0];
            this.aImg = this.lazy.getElementsByTagName('img');
            this.fnLoad = API.bind(this, this.load);
            this.load();
            API.on(window, 'scroll', this.fnLoad);
            API.on(window, 'resize', this.fnLoad);
 
        }
        lazyload.prototype = {
 
            /**
              * 执行按需加载图片,并将已加载的图片标记为已加载
              * @return 无
              */
            load: function() {
                var iScrollTop = document.documentElement.scrollTop || document.body.scrollTop;
                // 屏幕上边缘
                var iClientHeight = document.documentElement.clientHeight + iScrollTop;
                // 屏幕下边缘
                var i = 0;
                var aParent = [];
                var oParent = null;
                var iTop = 0;
                var iBottom = 0;
                var aNotLoaded = this.loaded(0);
                 if (this.loaded(1).length != this.aImg.length) {
                    var notLoadedLen = aNotLoaded.length;
                     for (i = 0; i < notLoadedLen; i++) {
                        iTop = API.pageY(aNotLoaded[i]) - 200;
                        iBottom = API.pageY(aNotLoaded[i]) + aNotLoaded[i].offsetHeight + 200;
                        var isTopArea = (iTop > iScrollTop && iTop < iClientHeight) ? true : false;
                        var isBottomArea = (iBottom > iScrollTop && iBottom < iClientHeight) ? true : false;
                         if (isTopArea || isBottomArea) {                  
                            // 把预存在自定义属性中的真实图片地址赋给src
                            aNotLoaded[i].src = API.attr(aNotLoaded[i], 'data-src') || aNotLoaded[i].src;
                             if (!API.hasClass(aNotLoaded[i], 'loaded')) {
                                 if ('' != aNotLoaded[i].className) {
                                    aNotLoaded[i].className = aNotLoaded[i].className.concat(" loaded");
 
                                }
                                else {
                                    aNotLoaded[i].className = 'loaded';
 
                                }
                            }
                        }
                    }
                }
            },
 
            /**
              * 已加载或者未加载的图片数组
              * @param {Number} status 图片是否已加载的状态,0代表未加载,1代表已加载
              * @return Array 返回已加载或者未加载的图片数组
              */
            loaded: function(status) {
                var array = [];
                var i = 0;
                 for (i = 0; i < this.aImg.length; i++) {
                    var hasClass = API.hasClass(this.aImg[i], 'loaded');
                     if (!status) {
                        if (!hasClass)
                            array.push(this.aImg[i])
                    }
                    if (status) {
                        if (hasClass)
                            array.push(this.aImg[i])
                    }
                }
                return array;      
            }
        };
        // 按需加载初始化
        API.on(window, 'load', function () {new lazyload()});
    </script>
</body>
</html>

JS 代码按需加载

当某个动作触发后再执行相应的 JS。按需加载 JS 可以应用在下列场景:执行耗时比较久的 JS 代码,加载许多图片,加载 iframe,加载广告等

Webpack 配置

  • webpack: Webpack 是一个模块打包器。在 Webpack 中会将前端的所有资源文件(js/json/css/img/less/...)都作为模块处理。它将根据模块的依赖关系进行分析,生成对应的资源
  • 五个核心概念:
    • 入口(entry):指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始
    • 输出(output):在哪里输出文件,以及如何命名这些文件
    • Loader:处理那些非 JavaScript 文件(webpack 自身只能解析 JavaScript和json)
    • 插件(plugins): 执行范围更广的任务,从打包到优化都可以实现
    • 模式(mode): 有生产模式 production 和开发模式 development
  • 对loader的理解: webpack 本身只能处理 JS、JSON 模块,如果要加载其他类型的文件(模块),就需要使用对应的 loader 。它本身是一个函数,接受源文件作为参数,返回转换的结果。loader 一般以 xxx-loader 的方式命名,xxx 代表了这个 loader 要做的转换功能,比如 css-loader
  • 对 plugins 的理解: 插件可以完成一些 loader 不能完成的功能
  • 配置文件: webpack.config.js -> 用于存储 webpack 配置信息

异步组件

在传统的 Vue.js 开发中,组件是在应用程序启动时就立即加载的。这种方式虽然简单,但是会导致应用程序的初始加载时间变长,影响用户体验。为了提高应用程序的性能和加载速度,Vue.js 提供了异步组件

异步组件是一种延迟加载组件的方式,即只有在需要使用该组件时才会进行加载。Vue.js 异步组件的实现方式是通过 import() 函数来实现的。在使用异步组件时,需要将组件定义为一个函数,返回一个 import() 函数,该函数会异步加载组件并返回一个 Promise 对象

  • 减少应用程序的初始加载时间: 异步组件只有在需要使用该组件时才会进行加载,可以大大减少应用程序的初始加载时间,提高用户体验
  • 提高应用程序的性能: 异步组件可以将组件的加载和渲染分开进行,可以提高应用程序的性能,避免不必要的渲染
  • 优化代码的可维护性: 异步组件可以将组件按需加载,可以优化代码的可维护性,减少代码的复杂度
  • Cookie: Cookie 是小甜饼的意思。顾名思义,cookie 确实非常小,它的大小限制为4KB左右。它的主要用途有保存登录信息,比如你登录某个网站市场可以看到"记住密码",这通常就是通过在 Cookie 中存入一段辨别用户身份的数据来实现的
  • localStorage: localStorage 是 HTML5 标准中新加入的技术,它并不是什么划时代的新东西。早在 IE 6 时代,就有一个叫 userData 的东西用于本地存储,而当时考虑到浏览器兼容性,更通用的方案是使用 Flash。而如今,localStorage 被大多数浏览器所支持,如果你的网站需要支持 IE6+,那以 userData 作为你的 polyfill 的方案是种不错的选择
  • sessionStorage: sessionStorage 与 localStorage 的接口类似,但保存数据的生命周期与 localStorage 不同。做过后端开发的同学应该知道 Session 这个词的意思,直译过来是"会话"。而 sessionStorage 是一个前端的概念,它只是可以将一部分数据在当前会话中保存下来,刷新页面数据依旧存在。但当页面关闭后,sessionStorage 中的数据就会被清空
特性 Cookie localStorage sessionStorage
生命周期 一般由服务器生成,可以设置失效事件 除非被清除,否则永久保存 仅在当前会话有效,关闭页面或浏览器后会被清除
数据大小 约为 4k 约为 5MB 约为 5MB
数据通信 每次都会携带在 HTTP 头中 仅在浏览器中保存 仅在浏览器中保存
易用性 需要二次封装 原生接口易用 原生接口易用

重绘 | 重排

  • 重绘:当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,叫做重绘

  • 重排:当DOM的变化影响了元素的几何信息(元素的的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置

    • 页面初始渲染,这是开销最大的一次重排
    • 添加/删除可见的 DOM 元素
    • 改变元素位置
    • 改变元素尺寸,比如边距、填充、边框、宽度和高度等
    • 改变元素内容,比如文字数量,图片大小等
    • 改变元素字体大小
    • 改变浏览器窗口尺寸,比如 resize 事件发生时
    • 激活 CSS 伪类(例如::hover)
    • 设置 style 属性的值,因为通过设置 style 属性改变结点样式的话,每一次设置都会触发一次 reflow
    • 查询某些属性或调用某些计算方法:offsetWidth、offsetHeight等,除此之外,当我们调用 getComputedStyle 方法,或者IE里的 currentStyle 时,也会触发重排,原理是一样的,都为求一个即时性和准确性

如何减少重排

减少范围

  • 尽可能在低层级的DOM节点上,而不是像上述全局范围的示例代码一样,如果你要改变p的样式,class就不要加在div上,通过父元素去影响子元素不好
  • 不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局。那么在不得已使用table的场合,可以设置table-layout:auto;或者是table-layout:fixed这样可以让table一行一行的渲染,这种做法也是为了限制reflow的影响范围

减少次数

  • 样式集中改变: 不要频繁的操作样式,对于一个静态页面来说,明智且可维护的做法是更改类名而不是修改样式,对于动态改变的样式来说,相较每次微小修改都直接触及元素,更好的办法是统一在 cssText 变量中编辑。虽然现在大部分现代浏览器都会有 Flush 队列进行渲染队列优化,但是有些老版本的浏览器比如IE6的效率依然低下
  • 分离读写操作:DOM 的多个读操作(或多个写操作),应该放在一起。不要两个读操作之间,加入一个写操作
  • 将 DOM 离线
    • 使用 display: none
    • 通过 documentFragment 创建文档碎片在添加进文档中
    • 复制节点 -> 操作副本 -> 替换
  • 使用 absolute / fixed 脱离文档流:使用绝对定位会使的该元素单独成为渲染树中 body 的一个子元素,重排开销比较小,不会对其它节点造成太多影响。当你在这些节点上放置这个元素时,一些其它在这个区域内的节点可能需要重绘,但是不需要重排
  • 优化动画

Get | Post

GET POST
后退按钮/刷新 无害 数据会被重新提交
书签 可收藏为书签 不可收藏为书签
缓存 可能会被缓存 不能缓存
编码类型 application/x-www-form-urlencoded application/x-www-form-urlencode 或 multipart/form-data
历史 参数保留再浏览器历史中 参数不会被保留在浏览器历史中
数据长度限制 URL最大长度为2048字符 无限制
数据类型限制 只允许 ASII 字符 无限制
安全性 较差,发送的数据是 URL 的一部分 相对安全,参数不会被保存在日志中
可见性 数据在 URL 中对所有人都可见 不会显示在 URL 中

状态码

  • 信息响应(100-199)
    • 100
    • 101
    • 102
    • 103
  • 成功响应(200-299)
    • 200: 请求成功
    • 201
    • 202
    • 203
    • 205
    • 206
    • 207
    • 208
    • 226
  • 重定向响应(300-399)
    • 300
    • 301
    • 302
    • 303
    • 304: 响应没有被修改,继续使用缓存
    • 307
    • 308
  • 客户端错误响应(400-499)
    • 400
    • 401
    • 402
    • 403: 没有访问权限
    • 404: 找不到请求的资源
    • 405: 目标不支持请求方法
    • 406
    • ...
  • 服务端错误响应(500-599)

401 | 403

  • 401: 这个响应意味着"unauthenticated"。也就是说,客户端必须对自身进行身份验证才能获得请求的响应
  • 403: 客户端没有访问内容的权限;也就是说,它是未经授权的,因此服务器拒绝提供请求的资源

403 知道客户端身份,但是 401 不知道客户端身份

前端文件上传的多种方案详解
前端性能优化之按需加载
MDN
重排(reflow)和重绘(repaint)
JS 详解 Cookie、 LocalStorage 与 SessionStorage
Webpack配置全解
Vue.js 中的异步组件是什么?如何使用异步组件?

txt 复制代码
新人发文,礼貌求关❤️
相关推荐
时清云26 分钟前
【算法】合并两个有序链表
前端·算法·面试
小爱丨同学34 分钟前
宏队列和微队列
前端·javascript
持久的棒棒君1 小时前
ElementUI 2.x 输入框回车后在调用接口进行远程搜索功能
前端·javascript·elementui
2401_857297911 小时前
秋招内推2025-招联金融
java·前端·算法·金融·求职招聘
undefined&&懒洋洋2 小时前
Web和UE5像素流送、通信教程
前端·ue5
大前端爱好者4 小时前
React 19 新特性详解
前端
小程xy4 小时前
react 知识点汇总(非常全面)
前端·javascript·react.js
随云6324 小时前
WebGL编程指南之着色器语言GLSL ES(入门GLSL ES这篇就够了)
前端·webgl
随云6324 小时前
WebGL编程指南之进入三维世界
前端·webgl
J老熊4 小时前
Spring Cloud Netflix Eureka 注册中心讲解和案例示范
java·后端·spring·spring cloud·面试·eureka·系统架构