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

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

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

本期题目来源: 牛客

文件上传

  • 传统表单上传

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

    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

    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 获取出来,填充到对应的节点中

<!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>

图片按需加载

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

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

<!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 | LocalStorage | SessionStorage

  • Cookie: Cookie 是小甜饼的意思。顾名思义,cookie 确实非常小,它的大小限制为4KB左右。它的主要用途有保存登录信息,比如你登录某个网站市场可以看到“记住密码”,这通常就是通过在 Cookie 中存入一段辨别用户身份的数据来实现的
  • localStorage: localStorage 是 HTML5 标准中新加入的技术,它并不是什么划时代的新东西。早在 IE 6 时代,就有一个叫 userData 的东西用于本地存储,而当时考虑到浏览器兼容性,更通用的方案是使用 Flash。而如今,localStorage 被大多数浏览器所支持,如果你的网站需要支持 IE6+,那以 userData 作为你的 polyfill 的方案是种不错的选择
  • sessionStorage: sessionStorage 与 localStorage 的接口类似,但保存数据的生命周期与 localStorage 不同。做过后端开发的同学应该知道 Session 这个词的意思,直译过来是“会话”。而 sessionStorage 是一个前端的概念,它只是可以将一部分数据在当前会话中保存下来,刷新页面数据依旧存在。但当页面关闭后,sessionStorage 中的数据就会被清空
特性CookielocalStoragesessionStorage
生命周期一般由服务器生成,可以设置失效事件除非被清除,否则永久保存仅在当前会话有效,关闭页面或浏览器后会被清除
数据大小约为 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

GETPOST
后退按钮/刷新无害数据会被重新提交
书签可收藏为书签不可收藏为书签
缓存可能会被缓存不能缓存
编码类型application/x-www-form-urlencodedapplication/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 中的异步组件是什么?如何使用异步组件?

新人发文,礼貌求关❤️
  • 15
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xxhls0208

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值