前端浏览器相关

通常有两种方法引入 css 样式:

#浏览器

通常有两种方法引入 css 样式:

  • <link rel='stylesheet' href='/style.css' />

  • 通过 <style> 标签

    • 直接写在标签内部

    • 在标签内部使用 @import

      html
      <style>
          @import url(/style.css)
      </style>

通常,更推荐使用 link 的方法,而不是使用 @import

1. 作用

<link> 用于将外部资源与 HTML 文档关联,比如:

  • CSS 样式表
  • 网站图标(favicon)
  • 字体文件
  • 预加载、预获取资源

2. 常见用法

html
<!-- 引入外部 CSS -->
<link rel="stylesheet" href="styles.css" />

<!-- 网站图标 -->
<link rel="icon" href="favicon.ico" />

<!-- 预加载某个资源(提前下载,提高性能) -->
<link rel="preload" href="main.js" as="script" />

<!-- DNS 预解析(加速外部请求) -->
<link rel="dns-prefetch" href="//example.com" />

3. rel 属性说明

rel(relationship)表示当前文档与所链接资源之间的关系。

rel 值作用
stylesheet引入外部样式表
icon指定网页图标
preload预加载资源(与 as 一起用)
prefetch预取资源(浏览器空闲时提前加载)
dns-prefetch预先解析 DNS
manifestProgressive Web App 的配置文件

注意:

  • <link> 默认是 同步加载、并行执行 的,不会阻塞 HTML 解析。
  • CSS 文件加载会阻塞页面渲染(因为浏览器需要样式信息来绘制内容)。

性能优化的关键点之一,就是让关键资源更早被加载、非关键资源延迟加载。 这就引出了——<link rel="preload"><link rel="prefetch">

特性preloadprefetch
目的提前加载当前页面马上要用到的资源预加载将来页面可能需要的资源
优先级高(与正常资源几乎相同)低(空闲时加载,不影响关键资源)
何时使用当前页面渲染马上需要(如字体、首屏 JS/CSS)下一个页面可能要用(如分页、路由跳转)
何时生效当前页面加载时当前页面空闲时
示例标签<link rel="preload" as="script" href="/main.js"><link rel="prefetch" href="/next-page.js">
缓存行为加载后立即可用加载后会进入缓存,后续访问直接复用
支持情况浏览器广泛支持大部分现代浏览器支持(不阻塞)

三、简单例子

preload:优化当前页面

html
<link rel="preload" href="/fonts/Roboto.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/main.css" as="style">
<link rel="preload" href="/main.js" as="script">
  • 告诉浏览器这些资源非常重要,应当尽早加载;
  • 在浏览器发现它们被正式引用前,就已在加载。

⚠️ 注意:必须指定 as 属性,否则不会生效。

prefetch:为下一个页面提前准备

html
<link rel="prefetch" href="/next-page.js">
<link rel="prefetch" href="/next-page.css">
  • 浏览器在空闲时下载这些文件;
  • 当用户点击跳转到下一个页面时,这些资源已经在缓存中;
  • 加快页面切换速度。

<script> 标签

1. 作用

用于加载并执行 JavaScript 代码。

2. 基本用法

html
<!-- 方式1:内联脚本 -->
<script>
  console.log("Hello World");
</script>

<!-- 方式2:外部脚本 -->
<script src="main.js"></script>

3. 加载与执行顺序

浏览器在解析 HTML 时遇到 <script>

  • 默认会暂停 HTML 解析
  • 立即下载并执行脚本
  • 执行完后继续解析后面的 HTML

这会导致 阻塞渲染,因此我们常用 asyncdefer 优化。

asyncdefer 的区别

属性加载方式执行时机执行顺序是否阻塞 HTML 解析
无属性立即加载并执行下载完成后立刻执行按出现顺序(但可能被阻塞)✅ 会阻塞
async异步加载下载完成后立刻执行❌ 不保证顺序❌ 不阻塞解析,但执行时会中断
defer异步加载HTML 解析完成后执行(DOMContentLoaded 前)✅ 按出现顺序执行❌ 不阻塞解析

直观流程图:

HTML解析 ----下载---- 执行

 async:    异步下载 → 一下载完立刻执行(无顺序)
 defer:    异步下载 → 等HTML都解析完再执行(有顺序)

示例:

<!-- 普通脚本(阻塞HTML) -->
<script src="a.js"></script>

<!-- 异步脚本(下载不阻塞HTML,但执行时机不确定) -->
<script src="b.js" async></script>

<!-- 延迟脚本(HTML解析完再执行) -->
<script src="c.js" defer></script>

✅ 一般推荐:

  • 第三方统计、广告脚本:async
  • 依赖 DOM 的主逻辑代码:defer
元素/属性加载行为是否阻塞执行时机
<link rel="stylesheet">并行加载✅ 阻塞渲染加载完即渲染
<script>同步加载✅ 阻塞解析立即执行
<script async>异步加载❌ 解析不中断(但执行会打断)下载完立即执行
<script defer>异步加载❌ 完全不阻塞HTML 解析完后执行,顺序稳定

同源策略

同源策略是什么,同源策略限制了什么

如果两个 URL的协议,域名,端口相同,说明它们是同源的。

目的是为了保证用户信息的安全,防止恶意的网站窃取数据。

限制了三种行为:

(1) Cookie、LocalStorage 和 IndexDB 无法读取。

(2) DOM 无法获得。

(3) AJAX 请求不能发送。

如何绕过同源策略实现跨域

后端

  • Access-Control-Allow-Origin:*

前端

  • 添加代理,代理到同一主机号,端口号下
  • JSONP

JSONP(JSON with Padding) 是一种早期的跨域请求方案。 它的核心原理是:

浏览器的 <script> 标签不受同源策略限制,可以加载任意域名的脚本。

于是我们可以“借助 <script> 标签加载一个返回数据的脚本”,从而绕过跨域限制。

JSONP 的工作原理

  1. 客户端定义一个全局回调函数,例如 handleData

  2. 向服务端请求时,带上这个函数名,比如:

    https://api.example.com/data?callback=handleData
  3. 服务端返回的不是 JSON,而是:

    handleData({ "name": "Luke", "age": 22 });
  4. 浏览器执行这段 JS,就等于调用了你的回调函数,并把数据传进去了。

限制原因
只能使用 GET 请求因为 <script> 标签只能发 GET 请求
无法捕获错误无法用 try/catchonerror 精准处理
安全风险高返回 JS 脚本可执行任意代码
不支持现代浏览器的复杂接口比如请求头、自定义认证信息等

客户端代码(HTML)

html
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>JSONP 示例</title>
</head>
<body>
  <h3>JSONP 测试</h3>
  <button onclick="getData()">获取数据</button>

  <script>
    // ① 定义全局回调函数
    function handleResponse(data) {
      console.log('服务器返回的数据:', data);
      document.body.insertAdjacentHTML('beforeend', `<p>${JSON.stringify(data)}</p>`);
    }

    // ② 动态创建 script 标签,请求跨域接口
    function getData() {
      const script = document.createElement('script');
      // 注意 callback=handleResponse
      script.src = 'http://localhost:3000/jsonp?callback=handleResponse';
      document.body.appendChild(script);
    }
  </script>
</body>
</html>

服务端代码(Node.js Express 举例)

js
const express = require('express');
const app = express();

app.get('/jsonp', (req, res) => {
  const callbackName = req.query.callback; // e.g. "handleResponse"
  const data = { msg: 'Hello JSONP!', time: new Date().toLocaleString() };
  // 返回 JS 代码片段
  res.send(`${callbackName}(${JSON.stringify(data)})`);
});

app.listen(3000, () => console.log('JSONP server running on http://localhost:3000'));

事件捕获,事件冒泡,事件委托

事件流:

(1)捕获:从上到下

从根元素开始,一直向下依次调用同名事件的回调。但是这个捕获需要手动绑定 addEventLisener('click',fn,true);

(2)冒泡:从下到上

当一个元素的事件被触发时,同样的事件将会在该元素的所有祖先元素中依次被触发。这一过程被称为事件冒泡

冒泡是默认存在的

(3)阻止事件传播:

e.stopPropagation()

(4)事件委托:

重复绑定1000次子元素的事件性能非常差,选择在它们的共同父元素上绑定,使用 e.target来做事件委托。

event.targetevent.currentTarget的区别

currentTarget始终是监听事件者,而target是事件的真正发出者

在做事件代理时,currentTarget能够获取到被监听的DOM,target能获取到真正需要触发事件的DOM

stopImmediatePropagation,如何在事件劫持时的事件传递,如何阻止事件冒泡

  • stopPropagation 这个方法用于阻止浏览器中的事件在的 捕获/冒泡阶段 的传播。
  • stopImmediatePropagation 。这个方法可以理解为:stopImmediatePropagation = stopPropagation + (移除其他事件监听程序)

存储

在前端做数据存储的方案 有哪些?讲一讲他们分别的限制和使用场景

  • localStorage(大小不超过5M)
  • indexDB
  • sessionStorage(会话存储,在关闭窗口后就没了)
  • Cookie(大小不超过4k)

sessionStorage会在关闭当前页面后销毁,刷新当前页面不会

localStorage如果不主动清除,就会一直存在

IndexDB更像一个数据库,支持事务操作,容量更大,存的是一个对象,以kv的方式存储数据

如何监听localStorage 满了

  • 监听变化:
  window.addEventListener("storage", (e) => {
                // todo ...
                这里可以在写入的时候判断是否满了
  });
  • 判断是否满:把所有的item取出来,判断长度是否大于5M

localStorage 满了怎么解决

  • 用IndexDB
  • 维护一个Map,记录localstorage存入时机。此时localStorage会变为一个定长的队列。较早的localStorage会被清除

Web Worker 相关

JS如何创建多线程环境呢?

  • Webworker

webworker有什么限制

Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用document、window、parent这些对象。但是,Worker 线程可以使用navigator对象和location对象。

如何在同源下的不同tab共享worker

Shared Worker

三种Worker的区别

  • Web Worker

适合高密度计算的场景,开一个同步线程,不阻塞主线程

  • Shared Worker

能够在不同Tab间通信

  • Service Worker

用于缓存资源,网络优化等

WeakMap和Map的区别,什么时候要用WeakMap

强引用和弱引用是什么

  • Map 的键可以是任意类型,WeakMap 只接受对象作为键(null除外),不接受其他类型的值作为键
  • Map 的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键; WeakMap 的键是弱引用,键所指向的对象可以被垃圾回收,此时键是无效的
  • Map 可以被遍历, WeakMap 不能被遍历

评论

评论加载中……