JS 异步加载设计方案,适合用于网站性能优化、后台系统、CMS 内容站、专题页、广告/统计脚本、组件化页面等场景。

JS 异步加载设计
一、设计目标
JS 异步加载的核心目标是:
避免阻塞页面渲染
提升首屏加载速度
按需加载非核心功能
降低主线程压力
保证脚本执行顺序可控
兼容统计、广告、第三方 SDK 等外部脚本
二、JS 加载方式分类
1. 普通同步加载
<script src="/js/main.js"></script>
特点:
HTML 解析会被阻塞
JS 下载和执行完成后,页面才继续解析
适合极少量关键脚本
不适合大量业务 JS
不推荐大量使用。
2. defer 延迟加载
<script src="/js/main.js" defer></script>
特点:
不阻塞 HTML 解析
脚本会在 DOM 解析完成后执行
多个
defer脚本会按照书写顺序执行适合大多数业务 JS
推荐用于:
<script src="/js/vendor.js" defer></script> <script src="/js/main.js" defer></script>
适合页面主体功能、导航、搜索、评论、登录状态等。
3. async 异步加载
<script src="/js/analytics.js" async></script>
特点:
不阻塞 HTML 解析
下载完成后立即执行
多个
async脚本不保证执行顺序适合互不依赖的第三方脚本
推荐用于:
<script src="https://www.googletagmanager.com/gtag/js?id=xxx" async></script>
适合统计、广告、埋点、独立 SDK。
三、推荐加载策略
1. 核心业务 JS 使用 defer
<script src="/assets/js/runtime.js" defer></script> <script src="/assets/js/vendor.js" defer></script> <script src="/assets/js/app.js" defer></script>
适合:
页面交互逻辑
用户登录状态
搜索框
导航菜单
内容详情页功能
后台管理系统页面逻辑
优点是执行顺序稳定,不阻塞 HTML 解析。
2. 第三方脚本使用 async
<script src="https://example.com/sdk.js" async></script>
适合:
统计代码
广告代码
分享组件
客服工具
第三方推荐系统
A/B 测试工具
但要注意:
如果第三方脚本依赖某个全局变量,不能直接无脑 async。
3. 非首屏功能按需加载
例如用户点击按钮后再加载:
function loadScript(src) {
return new Promise((resolve, reject) => {
const script = document.createElement('script')
script.src = src
script.async = true
script.onload = resolve
script.onerror = reject
document.head.appendChild(script)
})
}
document.querySelector('#open-comment').addEventListener('click', async () => {
await loadScript('/js/comment.js')
window.Comment.init()
})适合:
评论系统
分享弹窗
富文本编辑器
图表组件
地图组件
视频播放器
AI 对话组件
后台复杂表单组件
四、模块化异步加载设计
如果使用现代前端框架,可以使用动态导入:
const module = await import('./chart.js')
module.renderChart()或者:
button.addEventListener('click', async () => {
const { openDialog } = await import('./dialog.js')
openDialog()
})适合:
Vue / React / Next.js / Nuxt
Vite / Webpack
后台管理系统
大型内容平台
优点:
自动代码分割
减少首屏 JS 体积
按页面或功能加载
五、按页面类型设计加载策略
1. 首页
首页通常追求首屏速度。
建议:
<script src="/js/home.js" defer></script> <script src="/js/analytics.js" async></script>
加载策略:
| 脚本类型 | 加载方式 |
|---|---|
| 首页主交互 | defer |
| 统计代码 | async |
| 广告脚本 | async / 懒加载 |
| 推荐模块 | 滚动到可视区后加载 |
| 弹窗组件 | 用户触发后加载 |
2. 文章详情页
文章页重点是内容优先展示。
建议:
<script src="/js/article.js" defer></script>
评论、分享、相关推荐可以延后加载:
if ('IntersectionObserver' in window) {
const observer = new IntersectionObserver(async entries => {
if (entries[0].isIntersecting) {
await import('./related.js')
observer.disconnect()
}
})
observer.observe(document.querySelector('#related'))
}适合:
相关推荐
评论区
点赞收藏
分享浮层
阅读进度条
图片懒加载增强逻辑
3. 后台管理系统
后台系统 JS 较重,建议分模块异步加载:
const routes = [
{
path: '/article/edit',
component: () => import('./pages/ArticleEdit.vue')
},
{
path: '/category/list',
component: () => import('./pages/CategoryList.vue')
}
]设计原则:
| 模块 | 加载策略 |
|---|---|
| 登录页 | 单独打包 |
| 首页 Dashboard | 单独打包 |
| 富文本编辑器 | 按需加载 |
| 图表组件 | 按需加载 |
| 文件上传 | 按需加载 |
| 权限管理 | 路由级加载 |
| 公共组件 | 合理拆包 |
六、第三方脚本异步加载设计
第三方脚本建议统一封装加载器。
const ScriptLoader = {
cache: new Map(),
load(src) {
if (this.cache.has(src)) {
return this.cache.get(src)
}
const promise = new Promise((resolve, reject) => {
const script = document.createElement('script')
script.src = src
script.async = true
script.onload = () => resolve(script)
script.onerror = () => reject(new Error(`Script load failed: ${src}`))
document.head.appendChild(script)
})
this.cache.set(src, promise)
return promise
}
}使用:
await ScriptLoader.load('https://example.com/sdk.js')
window.ExampleSDK.init()这样可以避免重复加载。
七、执行顺序设计
有依赖关系时,不要使用多个 async
错误示例:
<script src="/js/jquery.js" async></script> <script src="/js/plugin.js" async></script>
因为 plugin.js 可能先执行,导致找不到 jQuery。
正确方式:
<script src="/js/jquery.js" defer></script> <script src="/js/plugin.js" defer></script>
或者动态链式加载:
await loadScript('/js/jquery.js')
await loadScript('/js/plugin.js')八、首屏性能推荐方案
建议把 JS 分为四类:
| 类型 | 示例 | 加载方式 |
|---|---|---|
| 核心渲染脚本 | 页面基础交互 | defer |
| 首屏必要脚本 | 导航、登录状态 | defer |
| 非首屏脚本 | 评论、推荐、图表 | 懒加载 |
| 第三方脚本 | 统计、广告、客服 | async |
推荐结构:
<head>
<link rel="preload" href="/js/app.js" as="script">
<script src="/js/app.js" defer></script>
</head>
<body>
<main>
页面内容
</main>
<script>
window.addEventListener('load', function () {
import('/js/non-critical.js')
})
</script>
</body>九、懒加载触发方式
1. 页面加载完成后加载
window.addEventListener('load', () => {
import('./analytics.js')
})适合非关键逻辑。
2. 用户交互后加载
document.querySelector('#share').addEventListener('click', async () => {
const share = await import('./share.js')
share.open()
})适合分享、弹窗、评论、登录框。
3. 滚动到可视区域后加载
const target = document.querySelector('#comment')
const observer = new IntersectionObserver(async entries => {
if (entries[0].isIntersecting) {
const comment = await import('./comment.js')
comment.init()
observer.disconnect()
}
})
observer.observe(target)适合评论区、相关推荐、广告位。
4. 浏览器空闲时加载
if ('requestIdleCallback' in window) {
requestIdleCallback(() => {
import('./recommend.js')
})
} else {
setTimeout(() => {
import('./recommend.js')
}, 2000)
}适合低优先级模块。
十、异常处理设计
异步加载必须考虑失败情况:
async function loadComment() {
try {
const comment = await import('./comment.js')
comment.init()
} catch (error) {
console.error('评论模块加载失败', error)
document.querySelector('#comment').innerHTML = '评论加载失败,请稍后重试'
}
}推荐增加:
加载中状态
加载失败提示
重试机制
超时机制
降级方案
十一、完整推荐方案
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>JS 异步加载示例</title>
<!-- 核心业务 JS -->
<script src="/js/vendor.js" defer></script>
<script src="/js/app.js" defer></script>
<!-- 第三方统计 -->
<script src="https://example.com/analytics.js" async></script>
</head>
<body>
<header id="header"></header>
<main id="app"></main>
<section id="comment"></section>
<script>
const commentEl = document.querySelector('#comment')
const observer = new IntersectionObserver(async entries => {
if (entries[0].isIntersecting) {
try {
const comment = await import('/js/comment.js')
comment.init()
} catch (e) {
commentEl.innerHTML = '评论模块加载失败'
}
observer.disconnect()
}
})
observer.observe(commentEl)
</script>
</body>
</html>十二、最佳实践总结
![]()
| 场景 | 推荐方式 |
|---|---|
| 页面主业务 JS | defer |
| 独立第三方 JS | async |
| 有依赖关系的 JS | defer 或顺序加载 |
| 大型组件 | import() 动态导入 |
| 非首屏模块 | IntersectionObserver |
| 用户触发功能 | 点击后加载 |
| 低优先级功能 | requestIdleCallback |
| 后台系统页面 | 路由级代码分割 |
| 广告/统计 | async + 容错 |
| 富文本/图表/地图 | 按需加载 |
十三、推荐落地原则
一句话总结:
核心功能 defer,第三方 async,复杂模块 import,非首屏懒加载。
对于内容型网站,可以这样设计:
首屏内容优先展示 核心交互 defer 加载 统计广告 async 加载 评论推荐滚动加载 复杂组件点击后加载
对于后台管理系统,可以这样设计:
基础框架先加载 页面路由按需加载 大型组件独立拆包 公共依赖合理缓存 第三方 SDK 统一加载器管理