浏览器及其他 面试题

1、浏览器跨域安全性

  1. 核心问题(没有跨域会怎样?)

    "如果没有跨域限制,用户登录了银行网站 bank.com 后,再去访问一个恶意网站 evil.com,那么 evil.com 的脚本可以直接向 bank.com 发起请求(比如转账请求),浏览器会自动带上用户的Cookie。银行服务器会认为这是用户的合法操作,从而导致严重的安全事故。"

  2. 解决方案(跨域做了什么?)

    "跨域策略(主要是浏览器的同源策略)就是这个问题的解决方案。它规定:不同源的脚本默认无法读取对方站点的资源内容和响应。这就建立了一个安全边界,把恶意网站隔离在了外面。"

  3. 关键补充(它的定位是什么?)

    "需要强调的是,跨域安全是浏览器端的一道基础防线,但它不是万能的。它主要防的是 CSRF(跨站请求伪造) 这类'冒充'攻击。而现实中更多的威胁,比如 XSS(跨站脚本攻击),是由于网站自身的安全漏洞,让恶意脚本在'同源'内部执行,这时跨域策略就无能为力了。所以,它是一个至关重要的基础安全模型,但需要和服务器端的CSRF Token、输入校验等安全措施共同构成一个完整的防御体系。"

终极精简版:

"跨域的安全性主要体现在通过同源策略实现了不同网站间的身份凭证隔离,核心是防止恶意网站滥用用户在正规网站的登录状态进行非法操作。"

2、JWT是什么?

  • JWT 是一个经过数字签名的、紧凑的、自包含的凭证,用于在双方之间安全地传输信息。

    • 它的核心工作流程是:

      1. 认证:用户登录,服务器验证凭据后生成 JWT 并返回给客户端。

      2. 携带:客户端在后续请求中(通常在 HTTP Header 的 Authorization 字段中)携带此 JWT。

      3. 验证:服务器验证 JWT 的签名,如果有效,则信任其中的用户信息并处理请求。

    • 总结

      JWT就是后端生成后返回给前端,前端接收到后保管JWT请求接口是将JWT信息添加到请求头的Authorization 里发送给后端,后端在去验证用户是否能调用该接口或其他功能。

3、文档流

  • "文档流就是元素在页面中'自然排队'的规则,没写 CSS、也没加 float、position 之类的时候,浏览器就按照自己的默认"排队方式"把所有元素排列好------这就叫文档流。

    1. 按HTML书写顺序渲染

      javascript 复制代码
      你在 HTML 中写在上面的标签,会显示在前面
      		
      写在下面的标签,会显示在后面
      
      比如:
      
      <div>A</div>
      <div>B</div>
      
      
      一定是 A 在上,B 在下。
      浏览器不会自动帮你调换位置。
      
      这就是"文档流第一条:按HTML书写顺序渲染"。
    2. 身份决定站位 - 块级占整行,行内挤一起

      javascript 复制代码
      浏览器会根据元素的"身份"决定它占多少空间:
      
      块级元素(block)
      
      例如:div / p / h1 / ul / li / section ...
      
      特点:
      
      独占一行
      
      上下排列
      
      宽度默认撑满父容器
      
      行内元素(inline)
      
      例如:span / a / strong / em
      
      特点:
      
      不独占一行
      
      像小朋友挤一起
      
      只占内容宽度
      
      这就是"身份决定站位"。
    3. CSS可以改变规则 - 比如让元素'漂浮'或'绝对定位'

      javascript 复制代码
      你写 CSS 后,可以改变"自然排队":
      
      float
      
      让元素浮起来(适合图片环绕文本等场景)
      
      position: absolute
      
      让元素完全脱离文档流,不再按顺序排队
      
      position: fixed
      
      类似 absolute,但相对于视口固定
      
      这些操作会扰乱正常排队,所以叫:"CSS 可以改变游戏规则"。
    4. 脱离文档流就像插队 - 不按正常顺序排了"

      javascript 复制代码
      如果一个元素用了:
      
      position: absolute
      
      position: fixed
      
      float
      
      它就不再遵守正常排队,而是:
      
      不占位置
      
      不影响其它元素的位置
      
      自己跑到某个固定地方
      
      所以叫 "脱离文档流 = 插队"。
  • "理解文档流是CSS布局的基础。在实际开发中,我最关注的是:

    1. 合理使用display属性控制元素排列

      javascript 复制代码
      display 决定元素的"身份":
      
      display: block
      
      display: inline
      
      display: inline-block
      
      display: flex
      
      display: grid
      
      改变它就能改变元素怎么排队。
    2. 处理float和position导致的布局异常

      javascript 复制代码
      因为 float/absolute 会脱离文档流,开发中常会遇到问题:
      
      父元素高度塌陷
      
      内容重叠
      
      元素位置跑偏
      
      这些就是"布局异常"。
    3. 通过BFC(块级格式化上下文)解决外边距重叠和浮动相关问题

      javascript 复制代码
      BFC 是解决文档流问题的利器:
      
      可以让父元素包裹浮动元素(解决高度塌陷)
      
      可以阻止上下 margin 折叠
      
      可以隔离布局,避免互相影响
      
      触发 BFC 的方式之一:
      
      overflow: hidden;
      display: flow-root;
    4. 在现代布局中,虽然Flexbox和Grid提供了新的布局方式,但文档流的概念依然是理解CSS的核心

      javascript 复制代码
      虽然:
      
      Flex 可以很轻松地横向、纵向排列
      
      Grid 可以轻松实现二维排布
      
      但是:
      
      👉 这些布局都是基于文档流原理设计的。
      如果你不了解文档流,你也很难理解它们的行为。
      
      比如:
      
      Flex item 本质还是遵循文档流,只是方向变了
      
      Grid item 仍然遵循"先来后到"的排列顺序
  • 一句话总结让你完全理解:

    • 文档流就是网页元素默认的排版方式(按HTML书写顺序渲染、按身份排队)。
      CSS(float、absolute、flex、grid)都是在改变或扩展文档流的规则。
      理解文档流,才能理解布局为什么会这样排、为什么会出错、怎么修复。

4、上机题:请安描述完成页面

html 复制代码
<h1>公司抽奖活动</h1>
<h2>1. 公司有三位员工,点击【抽奖】按钮,抽取一位中奖员工</h2>
<h2>2. 中奖员工显示到中奖列表中,内容格式为三等奖【Alice】</h2>
<h2>3. 中奖后的员工不能再参加抽奖</h2>
<h2>4. 所有员工都中奖后,【抽奖】按钮不可点击</h2>
<h2>5. 中奖列表显示顺序从一等奖到三等奖的顺序显示</h2>

代码

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>React 抽奖应用</title>
    <!-- 引入 React 和 ReactDOM -->
    <script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>
    <!-- 引入 Babel 编译器 -->
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <style>
        body {
            font-family: Arial, sans-serif;
            text-align: center;
            margin-top: 50px;
            background-color: #f5f5f5;
        }
        .choujiang {
            width: 600px;
            margin: 0 auto;
            border: 1px solid #ccc;
            padding: 20px;
            background: white;
            border-radius: 20px;
            box-shadow: 0 4px 8px rgba(0,0,0,0.1);
        }
        h1 {
            color: #e74c3c;
            margin-bottom: 20px;
        }
        h2 {
            text-align: left;
            font-size: 16px;
            margin: 10px 0;
            font-weight: normal;
        }
        button {
            padding: 12px 25px;
            font-size: 18px;
            background-color: #e74c3c;
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            transition: background-color 0.3s;
            margin: 20px 0;
        }
        button:hover {
            background-color: #c0392b;
        }
        button:disabled {
            background-color: #95a5a6;
            cursor: not-allowed;
        }
        .winner-list {
            margin-top: 30px;
            text-align: left;
        }
        .winner-item {
            font-size: 18px;
            margin: 10px 0;
            padding: 10px;
            border-radius: 5px;
            background-color: #f9f9f9;
            display: flex;
            justify-content: space-between;
        }
        .prize-level {
            font-weight: bold;
            color: #e74c3c;
        }
        .remaining {
            margin-top: 15px;
            color: #7f8c8d;
        }
        .highlight {
            animation: highlight 1s ease-in-out;
        }
        @keyframes highlight {
            0% { background-color: #ffeb3b; }
            100% { background-color: #f9f9f9; }
        }
        .app-container {
            width: 600px;
            margin: 0 auto;
            padding: 20px;
            background: white;
            border-radius: 20px;
            box-shadow: 0 4px 8px rgba(0,0,0,0.1);
        }
        .status-info {
            background-color: #e8f4fd;
            padding: 10px;
            border-radius: 5px;
            margin: 15px 0;
            border-left: 4px solid #3498db;
        }
    </style>
</head>
<body>
    <div class="choujiang">
        <h1>公司抽奖活动</h1>
        <h2>1. 公司有三位员工,点击【抽奖】按钮,抽取一位中奖员工</h2>
        <h2>2. 中奖员工显示到中奖列表中,内容格式为三等奖【Alice】</h2>
        <h2>3. 中奖后的员工不能再参加抽奖</h2>
        <h2>4. 所有员工都中奖后,【抽奖】按钮不可点击</h2>
        <h2>5. 中奖列表显示顺序从一等奖到三等奖的顺序显示</h2>
    </div>
    <div id="root"></div>
    
    <script type="text/babel">
        function App() {
            // 初始员工列表
            const [participants, setParticipants] = React.useState([
                { id: '001', name: 'Alice' },
                { id: '002', name: 'Bob' },
                { id: '003', name: 'Charlie' }
            ]);
            
            // 中奖列表,按奖项等级存储
            const [winners, setWinners] = React.useState({
                firstPrize: null,
                secondPrize: null,
                thirdPrize: null
            });
            
            // 新中奖者高亮效果
            const [highlightPrize, setHighlightPrize] = React.useState(null);
            
            // 抽奖函数
            const draw = () => {
                if (participants.length === 0) return;
                
                // 随机选择一个员工
                const randomIndex = Math.floor(Math.random() * participants.length);
                const winner = participants[randomIndex];
                
                // 从参与者中移除中奖者
                const newParticipants = [...participants];
                newParticipants.splice(randomIndex, 1);
                setParticipants(newParticipants);
                
                // 确定奖项等级(根据剩余可抽奖人数)
                let prizeLevel;
                if (newParticipants.length === 2) {
                    prizeLevel = 'thirdPrize';
                } else if (newParticipants.length === 1) {
                    prizeLevel = 'secondPrize';
                } else {
                    prizeLevel = 'firstPrize';
                }
                
                // 更新中奖列表
                setWinners(prevWinners => ({
                    ...prevWinners,
                    [prizeLevel]: winner
                }));
                
                // 设置高亮效果
                setHighlightPrize(prizeLevel);
                setTimeout(() => setHighlightPrize(null), 1000);
            };
            
            // 奖项配置
            const prizeConfig = [
                { key: 'firstPrize', name: '一等奖', color: '#e74c3c' },
                { key: 'secondPrize', name: '二等奖', color: '#f39c12' },
                { key: 'thirdPrize', name: '三等奖', color: '#3498db' }
            ];
            
            return (
                <div className="app-container">
                    <h1 style={{color: '#e74c3c'}}>抽奖活动</h1>
                    
                    <div className="status-info">
                        剩余可抽奖员工: {participants.length} 人
                    </div>
                    
                    <div>
                        <button 
                            onClick={draw} 
                            disabled={participants.length === 0}
                        >
                            抽奖
                        </button>
                    </div>
                    
                    <div className="winner-list">
                        <h2>中奖名单</h2>
                        {prizeConfig.map(prize => (
                            <div 
                                key={prize.key}
                                className={`winner-item ${highlightPrize === prize.key ? 'highlight' : ''}`}
                            >
                                <span className="prize-level" style={{color: prize.color}}>
                                    {prize.name}
                                </span>
                                <span>
                                    {winners[prize.key] 
                                        ? `【${winners[prize.key].name}】`
                                        : '暂未抽取'
                                    }
                                </span>
                            </div>
                        ))}
                    </div>
                </div>
            );
        }

        ReactDOM.render(<App />, document.getElementById('root'));
    </script>
</body>
</html>

5、前端与后端通信方式

类别 名称 属于哪一类 作用是什么 是否用于"持续通信"? 示例说明
请求 API(怎么发请求) fetch 发请求的工具 发送一次 HTTP 请求 ❌ 否 fetch('/api/user')
axios 发请求的库 发送一次 HTTP 请求(封装好) ❌ 否 axios.get(...)
XMLHttpRequest (XHR) 发请求的底层 API 传统 AJAX ❌ 否 old-school AJAX
<form> 提交 浏览器原生 HTTP 请求 页面跳转/表单提交 ❌ 否 提交表单
<img> / <script> / <link> 浏览器加载资源 自动 GET 请求 ❌ 否 加载文件
通信方式(通信模式/策略) 短轮询 通信策略 定时发送请求获取新数据 ⚠️ 半实时(靠高频请求) 每 2 秒请求一次 /msg
长轮询(Comet) 通信策略 发一个请求挂着,后端有数据再返回 ✔️ 伪实时 广泛用于老系统聊天
WebSocket 实时通信协议 双向长连接,可互相推消息 ✔️ 真实时 聊天、协作、游戏
SSE(Server-Sent Events) 实时推送协议 服务端 → 前端单向流式推送 ✔️ 半实时 日志、监控
MQTT 消息队列通信协议 发布-订阅模式,实时数据传输 ✔️ 真实时 IoT 设备、推送系统
WebRTC 点对点通信 前端直接通信(信令需后端) ✔️ 真实时 视频通话、P2P 文件

6、前端html如何和c#还有python对接实现物联网

"物联网系统通常采用分层架构。前端HTML通过WebSocket和REST API与后端通信:C#适合构建稳定的业务逻辑层,处理用户管理和设备控制;Python在设备通信和数据处理方面有优势,可以负责MQTT消息代理和实时数据推送。

具体实现上,前端用Chart.js展示实时数据图表,通过WebSocket接收Python后端推送的设备数据,同时通过HTTP API调用C#后端的控制接口。两种后端语言可以通过消息队列或共享数据库进行数据同步。"

7、websocket底层如何知道断线和应用层心跳机制

  1. 如何知道断线

    • TCP 层面的断线:

      • 如果客户端和服务器之间的 TCP 连接完全丢失(比如网络断开,或者服务器崩溃),WebSocket 会自动检测到并触发 onclose 事件,因为底层的 TCP 协议会发现无法继续传输数据。

      • 这种情况会通过 WebSocket 的 close 事件触发,且你可以在 onclose 事件中处理重连或其他逻辑。

  2. 应用层心跳机制

    应用层心跳机制主要是为了处理僵尸连接(连接看着正常,实际已断)、静默数据包丢失、 服务器无响应但连接保持这些问题的,因为这些问题TCP是检测不到的,所以需要我们自己手写一个心跳检测。

    这个例子仅供参考没有实际意义

    javascript 复制代码
    // 1. 依赖 TCP 检测处理硬断开
    socket.addEventListener("close", () => {
        console.warn("TCP 检测到硬断开,立即重连");
        // 自动处理网络断开、服务重启等明显问题
    });
    
    // 2. 应用层心跳检测软断开和偶发问题
    let isAlive = true;
    
    const startHeartbeat = () => {
        setInterval(() => {
            if (socket.readyState === WebSocket.OPEN) {
                isAlive = false;
                
                // 发送应用层心跳
                socket.send(JSON.stringify({
                    type: 'HEARTBEAT',
                    timestamp: Date.now(),
                    id: docId
                }));
                
                // 检测偶发问题
                setTimeout(() => {
                    if (!isAlive) {
                        console.warn("心跳超时,可能是偶发网络问题,主动重连");
                        socket.close(); // 手动触发重连
                    }
                }, 5000); // 5秒超时
            }
        }, 30000); // 30秒一次心跳
    };

8、websocket 前端需要注意什么

  • "前端使用 WebSocket 主要注意四点:

    • 连接生命周期管理 - 及时创建和销毁,实现自动重连

    • 健全的错误处理 - 监听 error 和 close 事件,添加心跳检测

    • 数据安全处理 - 验证消息格式,防止 XSS

    • 性能优化 - 控制消息频率,避免内存泄漏"

9、html5和css3的规范是什么有哪些新特性

  • HTML5和CSS3是现代Web开发的基石。HTML5通过语义化标签、多媒体支持和强大的本地存储API,使网页内容更丰富、结构更清晰,并具备了开发复杂Web应用的能力。CSS3则通过选择器、阴影、圆角等效果美化了界面,更重要的是通过Flexbox、Grid等革命性布局方案以及Transition、Animation动画,极大地提升了布局效率和交互体验。它们共同推动了响应式设计和移动优先的开发理念,让Web能提供媲美原生应用的体验。

10、没有原码的项目如何修改或者增加功能

  • 使用浏览器插件(如Tampermonkey)编写用户脚本,在页面加载后通过DOM操作修改列表文字或样式。
  • 搭建反向代理(如Nginx),拦截后端返回的HTML/JSON数据,全局替换文本内容后再发给前端。
  • 反编译

11、 "中大型项目如何规划,能让各部门间不相互冲突?"

  • "我会通过架构拆分、团队自治和契约化管理来实现这个目标。具体分三步:

    1. 架构层面:服务化拆分

      将单体应用按业务域(如用户、订单、库存)拆分为独立的微服务。每个服务独立开发、部署和扩容,拥有自己的数据库,从技术上实现隔离。

    2. 组织层面:赋予团队端到端所有权

      围绕这些微服务组建全功能团队(如"订单团队")。团队对自己负责的服务拥有从开发到运维的完整决策权和职责,实现权责统一。

    3. 协作层面:通过API契约和自动化流程锁定边界

      团队之间通过明确定义的API接口进行协作。所有接口在开发前先行约定,并利用自动化流水线实现各服务的独立测试与部署,确保协作稳定高效。

  • 总结:

    这套"服务化架构 + 自治团队 + 契约化协作"的体系,能从根源上减少部门间的依赖和冲突。"

12、从文本话术中识别与特定职业相关的关键词,常用方法包括词典匹配、分词规则和算法/模型提取。

  • 在对话或话术中识别职业等核心词语,常用方式有:

  • 词典匹配:预设职业词库(如"学生""老师"),在文本中直接匹配,简单高效。

  • 分词 + 规则:用分词工具提取名词,再与职业词库比对,适合较复杂文本。

  • 算法/模型:利用 TF-IDF、TextRank 或大模型提取关键词,可识别同义词和隐含语义。

👉 实际应用中,若只关注固定职业关键词,推荐 词典匹配;若需更灵活智能的识别,可结合 模型。

13、webpack如何优化项目?

  1. 路由懒加载结合Webpack代码分割技术,将路由组件拆分为独立代码块,降低主包体积,避免生成单一巨型JS文件‌

    javascript 复制代码
    {
        path: '/old',
        meta: {
          title: '****'
        },
        component: () => import(/* webpackChunkName: "about" */ '../views/Login_old.vue')
      }
  2. 第三方组件按需加载

    • 我使用的是element plus所以用一下方式进行按需加载

      1. 安装unplugin-auto-import和unplugin-vue-components

        javascript 复制代码
        npm install -D unplugin-vue-components unplugin-auto-import
      2. 配置vue.config.js

        javascript 复制代码
        const { defineConfig } = require("@vue/cli-service");
        const AutoImport = require("unplugin-auto-import/webpack");  // 导入webpack版的unplugin-auto-import
        const Components = require("unplugin-vue-components/webpack");  // 导入webpack版的unplugin-vue-components
        const { ElementPlusResolver } = require("unplugin-vue-components/resolvers");  // 导入elementplus组件解析器
        module.exports = defineConfig({
          transpileDependencies: true,
          configureWebpack: {  // 这个节点用于配置webpack
            plugins: [  // 这个节点要放在configureWebpack下,否则会报错
              Components.default({
                resolvers: [ElementPlusResolver()],  // 指定unplugin-vue-components的组件解析器为elementplus解析器
              }),
              AutoImport.default({
                resolvers: [ElementPlusResolver()],  // 指定unplugin-auto-import的组件解析器为elementplus解析器
              }),
            ],
          },
        });
      3. 组件中直接使用,无需import和component registration

        html 复制代码
        <template>
          <el-button type="primary">hi</el-button>
          <el-row class="mb-4">
            <el-button disabled>Default</el-button>
            <el-button type="primary" disabled>Primary</el-button>
            <el-button type="success" disabled>Success</el-button>
            <el-button type="info" disabled>Info</el-button>
            <el-button type="warning" disabled>Warning</el-button>
            <el-button type="danger" disabled>Danger</el-button>
          </el-row>
        </template>
    • 如果使用的是elementui,用下面方法进行按需引入

      1. 安装npm install babel-plugin-component -D

        javascript 复制代码
        npm install babel-plugin-component -D
      2. babel.config.js

        javascript 复制代码
        module.exports = {
          presets: [
            '@vue/cli-plugin-babel/preset'
          ],
          plugins: [
            [
              'component',
              {
                libraryName: 'element-ui',
                styleLibraryName: 'theme-chalk'
              }
            ]
          ]
        }
      3. 按需引入elment-ui组件

        javascript 复制代码
        import Vue from 'vue';
        import { Button, Select } from 'element-ui';
         
        Vue.use(Button)
        Vue.use(Select)
  3. 常用工具库使用CDN加速

  4. 开启gzip压缩,可以有效的减少代码体积

    javascript 复制代码
    const webpack = require("webpack")
    const CompressionWebpackPlugin = require("compression-webpack-plugin")
    const productionGzipExtensions = ["js", "css"]
    
    module.exports = {
      configureWebpack: (config) => {
        const plugins = [
          // 移除 moment.js 语言包(关键优化)
          new webpack.IgnorePlugin({
            resourceRegExp: /^\.\/locale$/, 
            contextRegExp: /moment$/
          }),
          
          // Gzip 压缩配置
          new CompressionWebpackPlugin({
            filename: '[path][base].gz', // 保持原文件名
            algorithm: 'gzip',
            test: new RegExp(`\\.(${productionGzipExtensions.join('|')})$`),
            threshold: 10240,
            minRatio: 0.8,
            deleteOriginalAssets: false // 保留源文件(必须!)
          })
        ]
    
        // 更安全的插件合并方式
        if (config.plugins) {
          config.plugins = config.plugins.concat(plugins)
        } else {
          config.plugins = plugins
        }
      }
    }
  5. 打包不生成map文件,.map文件生产环境用不到,还会增加打包后的体积也有可能有敏感信息

    javascript 复制代码
    module.exports = {
      productionSourceMap: false,
    }
  6. 代码分割

    javascript 复制代码
     config.optimization = {
            splitChunks: {
              cacheGroups: {
                vendor: {
                  chunks: 'all',
                  test: /node_modules/,
                  name: 'vendor',
                  minChunks: 1,
                  maxInitialRequests: 5,
                  minSize: 0,
                  priority: 100
                },
                common: {
                  chunks: 'all',
                  test: /[\\/]src[\\/]js[\\/]/,
                  name: 'common',
                  minChunks: 2,
                  maxInitialRequests: 5,
                  minSize: 0,
                  priority: 60
                },
                styles: {
                  name: 'styles',
                  test: /\.(sa|sc|c)ss$/,
                  chunks: 'all',
                  enforce: true
                }
              }
            }
          };
  7. 代码压缩,删除无用代码

    javascript 复制代码
    config.optimization.minimizer = [
            new TerserPlugin({
              terserOptions: {
                ecma: undefined,
                warnings: false,
                parse: {},
                compress: {
                  drop_console: true,
                  drop_debugger: true
                }
              }
            })
          ];
  8. 关闭prefetch,默认开启Prefetch时,会提前加载所有路由对应的JS文件(即使未访问该路由),关闭后可避免非首屏资源的预加载请求,显著减少首屏HTTP请求数量‌

    javascript 复制代码
    module.exports = {
      chainWebpack: config => {
        config.plugins.delete('prefetch')
      }
    }

14、JSbridge 是什么

  • JSBridge 是一种实现 JavaScript 与原生应用(如 Android 的 Java/Kotlin、iOS 的 Objective-C/Swift)双向通信的技术,核心作用是打破 Web 和 Native 的壁垒,扩展 Web 能力以调用设备原生功能‌

15、浏览器从请求地址到渲染页面都发生了什么?

  • 浏览器从解析URL、DNS查询、建立TCP连接、发送HTTP请求,到接收响应后解析HTML/CSS构建DOM和CSSOM树,最终通过布局与绘制完成页面渲染。

16、页面首次加载出现白屏如何解决?

  1. 网络请求问题

    原因 ‌:资源(HTML/CSS/JS)加载缓慢或失败,导致页面无法渲染。

    解决方案‌:

    • 压缩代码‌:使用 Webpack/Terser 压缩 JS,CSSNano 压缩 CSS
    • 图片优化 ‌:转换为 WebP 格式,或用 <picture> 标签适配不同格式
    • CDN加速‌:将静态资源托管到 CDN 边缘节点
    • 预加载关键资源‌:
    html 复制代码
     <link rel="preload" href="critical.css" as="style">
     <link rel="preload" href="main.js" as="script">

  2. JavaScript 执行阻塞

    原因 ‌:主线程被同步任务阻塞或JS报错中断。

    解决方案‌:

    • 异步加载脚本 ‌:

      html 复制代码
      <!-- 并行下载,下载完立即执行 -->
      <script async src="analytics.js"></script>
      
      <!-- 并行下载,HTML解析完执行 -->
      <script defer src="app.js"></script>
    • 错误捕获 ‌:

      javascript 复制代码
      // 全局错误监听
      window.addEventListener('error', (e) => {
        if (e.target.tagName === 'SCRIPT') {
          // 处理脚本加载失败
          showFallbackUI();
        }
      });

  1. 渲染阻塞
    原因 ‌:CSS/JS文件阻塞DOM构建。
    解决方案 ‌:
    • 内联关键CSS ‌:

      html 复制代码
      <style>
        /* 首屏必要样式 */
        .header { height: 60px; }
      </style>
    • 异步非关键CSS ‌:

      html 复制代码
      <link rel="stylesheet" href="non-critical.css" media="print" onload="this.media='all'">

  1. 路由与代码分割问题(SPA)
    原因 ‌:动态路由加载失败。
    解决方案 ‌:
    • React错误边界 ‌:

      jsx 复制代码
      import { ErrorBoundary } from 'react-error-boundary';
      <ErrorBoundary FallbackComponent={ErrorFallback}>
        <RouteComponent />
      </ErrorBoundary>
    • Webpack动态导入 ‌:

      javascript 复制代码
      const Home = () => import(/* webpackChunkName: "home" */ './Home.vue');

  1. 缓存策略不当
    解决方案 ‌:

    nginx 复制代码
    # Nginx 配置示例
    location /static/ {
      expires 1y;
      add_header Cache-Control "public, immutable";
    }

  1. 调整页面文件加载顺序

17、PC 端优化

  • ‌ 性能‌‌:路由懒加载
  • ‌ 交互‌‌ ‌:防抖/节流(如搜索框)
  • ‌ 渲染‌‌ ‌:减少 DOM 层级、避免频繁重排/重绘。
  • ‌ ‌网络‌‌:CDN 加速静态资源、开启资源压缩Gzip。

18、http和https有区别

  • 主要的区别在于安全性和数据传输方式上,HTTPS比HTTP更加安全,适合用于保护网站用户的隐私和安全,如银行网站、电子商务网站等。
    • ‌ 安全性‌:HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输的数据可以被任何抓包工具截取并查看。而HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,更为安全。
    • ‌ 数据传输方式‌:HTTP协议的端口号是80,HTTPS协议的端口号是443。
    • ‌ 网址导航栏显示‌:使用HTTP协议的网站导航栏显示的是"http://",而使用HTTPS协议的网站导航栏显示的是"https://"。
    • ‌ 证书‌:HTTPS需要到CA申请证书,一般免费证书较少,因而需要一定费用。
    • ‌ 网络速度‌:HTTP协议比HTTPS协议快,因为HTTPS协议需要进行加密和解密的过程。
    • ‌ SEO优化‌:搜索引擎更倾向于把HTTPS网站排在更前面的位置,因为HTTPS更安全。

19、HTTP 请求方式

  1. GET:用于获取资源,通过URL传递参数,请求的结果会被缓存,可以被书签保存,不适合传输敏感信息。
  2. POST:用于提交数据,将数据放在请求体中发送给服务器,请求的结果不会被缓存。
  3. PUT:用于更新资源,将数据放在请求体中发送给服务器,通常用于更新整个资源。
  4. DELETE:用于删除资源,将数据放在请求体中发送给服务器,用于删除指定的资源。
  5. PATCH:用于部分更新资源,将数据放在请求体中发送给服务器,通常用于更新资源的部分属性。

20、Get / Post 的区别

  • ‌ 区别‌:​

    1. get 幂等,post 不是。(多次访问效果一样为幂等)
    2. get 能触发浏览器缓存,post 没有。
    3. get 能由浏览器自动发起(如 img-src,资源加载),post 不行。
    4. post 相对安全,一定程度上规避 CSRF 风险。
  • ‌ 相同‌:​

    1. 都不安全,都是基于 http,明文传输。
    2. 参数并没有大小限制,是URL大小有限制,因为要保护服务器。 (chrom 2M,IE 2048)

21、RESTful 规范

  • 使用语义化的URL来表示资源的层级关系和操作,如/users表示用户资源,/users/{id}表示具体的用户。
    1. 资源:将系统中的实体抽象为资源,每个资源都有一个唯一的标识符(URI)。
    2. HTTP方法:使用HTTP请求方式来操作资源,如GET--查询(从服务器获取资源)、POST---新增(从服务器中新建一个资源);、PUT---更新(在服务器中更新资源)、DELETE---删除(从服务器删除资源),、PATCH---部分更新(从服务器端更新部分资源)等。
    3. 状态码:使用HTTP状态码来表示请求的结果,如200表示成功,404表示资源不存在等。
    4. 无状态:每个请求都是独立的,服务器不保存客户端的状态信息,客户端需要在请求中携带所有必要的信息。

22、Cookie 为了解决什么问题

  • 定义:Cookie是一种存储在用户浏览器中的小文件,用于存储网站的一些信息。通过Cookie,服务器可以识别用户并保持会话状态,实现会话保持。用户再次访问网站时,浏览器会将Cookie发送给服务器,以便服务器可以识别用户并提供个性化的服务,存储上限为 4KB。
    解决问题:Cookie诞生的主要目的是为了解决HTTP协议的无状态性问题。HTTP协议是一种无状态的协议,即服务器无法识别不同的用户或跟踪用户的状态。这导致了一些问题,比如无法保持用户的登录状态、无法跟踪用户的购物车内容等。

23、Cookie 和 Session 的区别

  • Cookie(HTTP Cookie)和 Session(会话)都是用于在 Web 应用程序中维护状态和用户身份的两种不同机制:
    • ‌ 存储位置‌:​

      1. Cookie:Cookie是存储在客户端的数据。每次请求自动发送Cookie到服务器,以便服务器可以识别用户。
      2. Session:Session数据通常存储在服务器上,而不是在客户端。服务器为每个用户创建一个唯一的会话,然后在服务器上存储会话数据。
    • ‌ 持久性‌:​

      1. Cookie:Cookie可以具有持久性,可以设置过期时间。如果没有设置过期时间,Cookie将成为会话Cookie,存在于用户关闭浏览器前的会话期间。
      2. Session:会话数据通常存在于用户活动的会话期间,一旦会话结束(用户退出登录或关闭浏览器),会话数据通常会被删除。
    • ‌ 安全性‌:​

      1. Cookie:Cookie数据存储在客户端,可能会被用户篡改或窃取。因此,敏感信息通常不应存储在Cookie中,或者应该进行加密。
      2. Session:Session数据存储在服务器上,客户端不可见,因此通常更安全,特别适合存储敏感信息。
    • ‌ 服务器负担‌:​

      1. Cookie:服务器不需要维护Cookie的状态,因为它们存储在客户端。每次请求中都包含Cookie,服务器只需要验证Cookie的有效性。
      2. Session:服务器需要维护会话数据,这可能会增加服务器的负担,尤其是在大型应用程序中。
    • ‌ 跨多个页面‌:​

      1. Cookie:Cookie可以被跨多个页面和不同子域共享,这使得它们适用于用户跟踪和跨多个页面的数据传递。
      2. Session:会话数据通常只在单个会话期间可用,而不容易在不同会话之间共享。
    • ‌ 无需登录状态‌:​

      1. Cookie:Cookie可以在用户未登录的情况下使用,例如用于购物车或用户首选项。
      2. Session:会话通常与用户的身份验证和登录状态相关,需要用户登录后才能创建和访问会话。

24、TCP(传输控制协议)和 UDP(用户数据报协议)的区别

  • 两种常用的传输层协议,用于在网络中传输数据。​

    • ‌ TCP‌:一种面向连接的协议,提供可靠的数据传输。它通过三次握手建立连接,保证数据的完整性和顺序性。TCP使用流控制、拥塞控制和错误检测等机制来确保数据的可靠传输。它适用于需要可靠传输的应用,如文件传输、电子邮件和网页浏览等。
    • ‌ UDP‌:一种无连接的协议,提供不可靠的数据传输。它不需要建立连接,直接将数据包发送给目标地址。UDP没有流控制和拥塞控制机制,也不保证数据的完整性和顺序性。UDP适用于实时性要求较高的应用,如音频、视频和实时游戏等。
  • 总结来说,TCP提供可靠的、面向连接的数据传输,适用于对数据完整性和顺序性要求较高的应用;而UDP提供不可靠的、无连接的数据传输,适用于实时性要求较高的应用。选择使用TCP还是UDP取决于应用的需求和特点。

25、TCP 三次握手?为什么是三次两次不行吗?

  1. 第一次握手(SYN):发送方首先向接收方发送一个SYN(同步)标志的TCP包,该包包含一个随机生成的初始序列号(ISN)。这表示发送方希望建立一个连接,并且指定了一个用于数据传输的起始序号。
  2. 第二次握手(SYN + ACK):接收方接收到发送方的SYN包后,它会回应一个带有SYN和ACK(确认)标志的TCP包。这个响应包不仅确认了接收到的SYN,还包含了接收方的初始序列号。这两个序列号表示了双方用于传输数据的初始顺序。
  3. 第三次握手(ACK):最后,发送方接收到接收方的响应后,它会发送一个带有ACK标志的TCP包,表示对接收方的响应已经收到。至此,连接建立完成,双方可以开始进行数据传输。

TCP采用‌三次握手‌的核心原因是:

  • 两次握手‌无法确保‌双方通信能力与序列号同步‌,服务器可能因未收到最终确认而维持无效连接(浪费资源),且无法阻止历史重复报文干扰;
  • 四次握手‌冗余,因三次已能双向确认收发能力与初始序列号(SYN、SYN-ACK传递双方ISN,ACK确认双方ISN有效),四次则增加无意义延迟。
    三次握手以最小成本实现可靠连接建立,避免资源泄漏与数据错乱。

26、什么是跨域?如何解决?

  • 在 Web 应用程序中,一个网页的代码向不同源(即不同的域名、协议或端口)发起 HTTP 请求。浏览器的同源策略限制了跨域请求,以保护用户的安全性和隐私。同源策略要求网页只能与同一源的资源进行交互,而不允许与不同源的资源直接交互。
    • ‌ 解决方法 ‌:
      1. Nginx 充当代理服务器,分发请求到目标服务器。
      2. 服务器端配置CORS策略
      3. Iframe 通讯,通过在主页面嵌入一个隐藏的 iframe,将目标页面加载到 iframe 中,并通过在主页面和 iframe 页面之间使用 postMessage() 方法进行消息传递,从而实现跨域的数据交换。

27、网页扫码登录如何实现

‌**‌ 定时器(短轮询/长轮询)**‌:​

  • ‌ ‌短轮询‌ ‌:​

    实现原理‌:客户端通过 setInterval 定时(如每秒1次)请求服务端检查二维码状态。

    ‌缺点‌:高频请求导致服务器压力大,实时性差(依赖轮询间隔)‌。

    ‌适用场景‌:对实时性要求不高的简单场景(如低频操作)‌。

  • ‌ ‌‌长轮询‌ ‌:​

    ‌实现原理‌:客户端发起请求后,服务端保持连接直至二维码状态变更或超时(如30秒),响应后客户端立即重启轮询‌。

    ‌优点‌:减少无效请求,延迟可控(优于短轮询)‌。

    ‌案例‌:微信网页版扫码登录采用长轮询监听二维码状态变更‌。

  • ‌**‌ WebSocket(全双工通信)**‌:​

    • ‌ ‌‌实现原理‌‌:​

      1. PC 端生成二维码后,与服务器建立 WebSocket 连接。
      2. 手机扫码并确认登录时,服务端通过 WebSocket 主动推送状态变更至 PC 端‌。
    • ‌ ‌‌‌优势‌‌:

      1. 高实时性‌:服务端可主动推送,无需客户端轮询‌。
      2. 双向通信‌:支持复杂交互(如登录确认弹窗的实时反馈)‌。
    • ‌ ‌‌‌‌缺点‌‌:​

      1. 需维护持久连接,服务端资源消耗较高‌。
      2. 需处理连接中断、重连等异常情况‌。
      3. 案例‌:部分企业 OA 系统通过 WebSocket 实现扫码登录的实时状态同步‌。
  • ‌**‌ SSE(Server-Sent Events)**‌:​

    • ‌ ‌‌‌‌实现原理‌‌:

      1. 客户端通过 EventSource 与服务器建立单向长连接,服务端在二维码状态变更时推送事件‌。
        支持自动重连和超时机制(如二维码过期触发刷新)‌。
    • ‌ ‌‌‌‌‌优势‌‌:

      1. 轻量化‌:基于 HTTP 协议,无需额外协议支持‌。
      2. 低延迟‌:服务端可主动推送,实时性接近 WebSocket‌。
    • ‌ ‌‌‌‌‌优局限性‌‌:

      1. 仅支持单向通信(服务端→客户端),无法处理客户端主动请求‌。
        部分旧版本浏览器兼容性较差‌。

28、axios封装

‌ ‌‌‌‌‌全局配置‌‌:

  1. 设置基础 URL(baseURL)和超时时间(60秒)
  2. 自动携带 token(通过请求拦截器注入 headers.token)

‌ ‌‌‌‌‌统一错误处理‌‌:

  1. 响应拦截器处理网络错误、401 Token 过期跳转登录页、404 接口错误等
  2. 所有错误触发 message.error 提示并隐藏全局 loading(通过 Redux)

‌ ‌‌‌‌‌多种请求方法封装‌‌:

  1. 支持不同内容类型:JSON、FormData、x-www-form-urlencoded、text/plain
  2. 提供 get/post/下载文件/上传文件/验证码获取 等方法
  3. 验证码请求特殊处理:解析二进制图片为 base64 并返回 UUID

‌ ‌‌‌‌‌‌集成 React 生态‌‌:

  1. 通过 useMemo 缓存实例,结合 React Router 的 navigate 和 Redux 的 dispatch

‌ ‌‌‌‌‌‌‌响应数据简化‌‌:

  1. 所有方法自动返回 response.data 过滤外层结构

29、如何实现token过期无感刷新

  • 需要后端配合,当发现token过期,后端返回新的token,前端在axios‌拦截器中获取到token过期错误码后刷新token缓存,刷新成功后重新发起原请求‌

30、HTTP缓存机制问题

  • HTTP 缓存是一种在客户端(如浏览器)或中间缓存代理服务器上保存资源副本的机制。通过设置 Cache-Control 响应头,强制浏览器或代理直接使用本地缓存副本,当用户首次请求某个资源时,服务器会将该资源以及相关的缓存控制信息一并返回给客户端。客户端接收到资源后,会根据缓存控制信息将资源存储在本地缓存中 。

  • 当用户再次请求相同的资源时,客户端首先会检查本地缓存中是否存在该资源的副本。如果存在,并且缓存控制信息表明该副本仍然有效,客户端就会直接从本地缓存中获取资源,而无需再次向服务器发送请求。这大大减少了网络请求的次数和数据传输量,提高了资源的加载速度。

  • 例如CDN的静态文件缓存功能就是通过配置 HTTP 响应头控制缓存周期,结合文件名哈希(如 style-abc123.css)实现内容更新后自动失效旧缓存‌

31、CDN 的核心功能

  • CDN(Content Delivery Network,内容分发网络)是一种‌分布式服务器网络‌,通过以下机制加速内容分发:
    • 就近访问‌:将资源缓存到全球多个边缘节点,用户从最近的节点获取内容(降低延迟)。
    • ‌负载均衡‌:智能分配请求到最优节点,避免单点过载。
    • 安全防护‌:防御 DDoS 攻击、隐藏源站 IP 等。
    • ‌缓存功能‌:‌CDN 节点会缓存静态资源(如图片、CSS、JS)‌,但这是 CDN 的附加能力,而非全部。

32、WebWork‌是什么

  • ‌Web Worker‌ 是浏览器提供的 JavaScript 多线程解决方案,JavaScript 默认在主线程(UI 线程)运行,‌Web Worker‌ 允许在独立的后台线程执行代码,避免复杂计算阻塞页面交互,可以执行耗时的任务而不会阻塞用户界面的响应。使用 Web Worker 可以将一些计算密集型或耗时的任务从主线程中分离出来,以提高网页的性能和响应速度。主线程可以继续处理用户交互和界面更新,而 Web Worker 在后台进行计算或处理其他任务。

  • 示例

    HTML

    javascript 复制代码
    <button onclick="startWork()">开始计算</button>
    <div id="result">等待结果...</div>
    <div id="error" style="color:red"></div>
    
    <script>
      // 创建 Worker
      const worker = new Worker('worker.js');
    
      // 向 Worker 发送任务
      function startWork() {
        worker.postMessage({ type: 'fibonacci', num: 40 });
      }
    
      // 监听 Worker 返回结果
      worker.onmessage = function(e) {
        const result = e.data;
        document.getElementById('result').textContent = `结果:${result}`;
      };
    
      // 监听 Worker 错误
      worker.onerror = function(e) {
        document.getElementById('error').textContent = `错误:${e.message}`;
        worker.terminate(); // 终止异常 Worker
      };
    </script>

    Web Worker(worker.js)

    javascript 复制代码
    self.onmessage = function(e) {
      const { type, num } = e.data;
      let result;
    
      switch (type) {
        case 'fibonacci':
          result = fibonacci(num);
          break;
        // 可扩展其他计算类型
      }
    
      self.postMessage(result);
    };
    
    function fibonacci(n) {
      if (n <= 1) return n;
      let a = 0, b = 1;
      for (let i = 2; i <= n; i++) {
        [a, b] = [b, a + b];
      }
      return b;
    }

33、内存缓存‌

  • ‌JS 内存缓存‌(Memory Cache)是浏览器在内存(RAM)中临时存储 JavaScript 代码、资源文件(如图片、样式表等)或其他数据的机制,用于快速访问高频使用的资源,避免重复请求服务器或磁盘读取,从而提升页面性能‌。闭包(Closure)是内存缓存的典型应用场景,其数据也存储在堆中。

34、Service Worker 缓存

  • 一种通过 JavaScript 脚本实现的代理机制,可拦截请求并返回自定义缓存内容,常用于 PWA(渐进式 Web 应用)。支持离线访问和动态更新策略‌6。

35、HTTP/2 Push Cache

  • 仅适用于 HTTP/2 协议,允许服务器主动推送资源到客户端缓存。生命周期短暂,仅在当前会话有效,常用于优化首次加载速度‌。

36、浏览器存储类缓存

  1. LocalStorage‌
    持久化存储,关闭浏览器后数据仍保留,容量约 5MB,适用于长期保存非敏感数据‌。
  2. SessionStorage‌
    会话级存储,页面关闭后数据自动清除,容量同 LocalStorage‌。
  3. Cookie‌
    用于会话跟踪,随 HTTP 请求自动发送到服务器。容量约 4KB,支持设置有效期‌。‌
  4. IndexedDB‌
    支持结构化大数据存储(如 JSON 对象),适合复杂应用场景‌。

37、HTTP/2是什么?如何开启?

HTTP/2 是 HTTP 协议的第二个主要版本,由 IETF 于 2015 年发布,旨在提升网络传输效率和网页加载性能‌。

  • ‌ ‌‌二进制分帧协议‌‌:采用二进制格式替代 HTTP/1.1 的文本协议,降低解析复杂度并提高传输效率‌。
  • ‌ ‌‌多路复用(Multiplexing)‌‌:单 TCP 连接可并行处理多个请求和响应,解决 HTTP/1.1 的队头阻塞问题‌。
  • ‌ ‌‌头部压缩(HPACK)‌‌:通过算法压缩冗余头部数据(如重复 Cookie),减少传输量‌。
  • ‌ ‌‌流优先级控制‌‌:允许客户端指定资源加载优先级,优化关键资源的处理顺序‌。

‌ ‌‌如何开启 HTTP/2

  1. 基础条件
    • 必须启用 HTTPS‌:HTTP/2 需基于 TLS 1.2+ 协议运行,需为域名配置有效的 SSL/TLS 证书‌
  2. 开启方式
    • 服务器端配置‌

      ‌Nginx‌:

      修改配置文件,在监听端口添加 http2 标识:

      javascript 复制代码
      listen 443 ssl http2;
      ssl_certificate /path/to/cert.pem;
      ssl_certificate_key /path/to/key.pem;
      支持版本要求:Nginx ≥1.9.5‌。
    • Apache‌:

      加载 mod_http2 模块后,在配置中添加 Protocols h2 http/1.1‌。

    • 云服务/CDN 配置‌

      • 阿里云 DCDN‌:
        登录控制台 → 域名管理 → HTTPS 配置 → 开启 ‌HTTP/2‌ 开关‌。
      • 百度云 CDN‌:
        默认已支持 HTTP/2,用户配置 HTTPS 证书后自动生效‌。
      • ‌其他平台‌:
        如火山引擎、腾讯云等,需在控制台的 HTTPS 高级选项中勾选 ‌启用 HTTP/2.0‌。
  3. 验证是否生效‌
    • 浏览器开发者工具‌:在 Chrome 的 Network 面板查看协议列(Protocol),显示 h2 表示成功‌。
    • 命令行工具‌:执行 curl -I --http2 https://yourdomain.com,响应头包含 HTTP/2 标识即为启用‌。
相关推荐
1024肥宅1 小时前
手写 new 操作符和 instanceof:深入理解 JavaScript 对象创建与原型链检测
前端·javascript·ecmascript 6
吃肉的小飞猪1 小时前
uniapp 下拉刷新终极方案
前端
关关长语1 小时前
Vue本地部署包快速构建为Docker镜像
前端·vue.js·docker
jump6801 小时前
react的事件优先级
前端
soda_yo1 小时前
浅拷贝与深拷贝: 克隆一只哈基米
前端·javascript·面试
冴羽1 小时前
Nano Banana Pro 零基础快速上手
前端·人工智能·aigc
幼儿园技术家2 小时前
浏览器加载html、css、js的顺序
前端
爱分享的鱼鱼2 小时前
Vue生命周期钩子详解与实战应用
前端·vue.js
晴殇i2 小时前
CSS Grid 与 Flexbox:现代前端布局的双子星
前端·css