前端浏览器相关
通常有两种方法引入 css 样式:
link 与 style
通常有两种方法引入 css 样式:
-
<link rel='stylesheet' href='/style.css' /> -
通过
<style>标签-
直接写在标签内部
-
在标签内部使用
@importhtml<style> @import url(/style.css) </style>
-
通常,更推荐使用 link 的方法,而不是使用 @import。
<link> 标签
1. 作用
<link> 用于将外部资源与 HTML 文档关联,比如:
- CSS 样式表
- 网站图标(favicon)
- 字体文件
- 预加载、预获取资源
2. 常见用法
<!-- 引入外部 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 |
manifest | Progressive Web App 的配置文件 |
注意:
<link>默认是 同步加载、并行执行 的,不会阻塞 HTML 解析。- 但 CSS 文件加载会阻塞页面渲染(因为浏览器需要样式信息来绘制内容)。
性能优化的关键点之一,就是让关键资源更早被加载、非关键资源延迟加载。
这就引出了——<link rel="preload"> 和 <link rel="prefetch">。
| 特性 | preload | prefetch |
|---|---|---|
| 目的 | 提前加载当前页面马上要用到的资源 | 预加载将来页面可能需要的资源 |
| 优先级 | 高(与正常资源几乎相同) | 低(空闲时加载,不影响关键资源) |
| 何时使用 | 当前页面渲染马上需要(如字体、首屏 JS/CSS) | 下一个页面可能要用(如分页、路由跳转) |
| 何时生效 | 当前页面加载时 | 当前页面空闲时 |
| 示例标签 | <link rel="preload" as="script" href="/main.js"> | <link rel="prefetch" href="/next-page.js"> |
| 缓存行为 | 加载后立即可用 | 加载后会进入缓存,后续访问直接复用 |
| 支持情况 | 浏览器广泛支持 | 大部分现代浏览器支持(不阻塞) |
三、简单例子
preload:优化当前页面
<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:为下一个页面提前准备
<link rel="prefetch" href="/next-page.js">
<link rel="prefetch" href="/next-page.css">- 浏览器在空闲时下载这些文件;
- 当用户点击跳转到下一个页面时,这些资源已经在缓存中;
- 加快页面切换速度。
<script> 标签
1. 作用
用于加载并执行 JavaScript 代码。
2. 基本用法
<!-- 方式1:内联脚本 -->
<script>
console.log("Hello World");
</script>
<!-- 方式2:外部脚本 -->
<script src="main.js"></script>3. 加载与执行顺序
浏览器在解析 HTML 时遇到 <script>:
- 默认会暂停 HTML 解析
- 立即下载并执行脚本
- 执行完后继续解析后面的 HTML
这会导致 阻塞渲染,因此我们常用 async 或 defer 优化。
async 与 defer 的区别
| 属性 | 加载方式 | 执行时机 | 执行顺序 | 是否阻塞 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 的工作原理
-
客户端定义一个全局回调函数,例如
handleData。 -
向服务端请求时,带上这个函数名,比如:
https://api.example.com/data?callback=handleData -
服务端返回的不是 JSON,而是:
handleData({ "name": "Luke", "age": 22 }); -
浏览器执行这段 JS,就等于调用了你的回调函数,并把数据传进去了。
| 限制 | 原因 |
|---|---|
| 只能使用 GET 请求 | 因为 <script> 标签只能发 GET 请求 |
| 无法捕获错误 | 无法用 try/catch 或 onerror 精准处理 |
| 安全风险高 | 返回 JS 脚本可执行任意代码 |
| 不支持现代浏览器的复杂接口 | 比如请求头、自定义认证信息等 |
客户端代码(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 举例)
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.target 和 event.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不能被遍历
评论
评论加载中……