阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 静态资源优化方案

静态资源优化方案

作者:陈川 阅读数:14112人阅读 分类: Node.js

静态资源优化的必要性

静态资源优化是提升Web应用性能的关键环节。Koa2作为轻量级Node.js框架,提供了灵活的中间件机制,能够高效处理静态资源请求。通过合理的优化策略,可以显著减少页面加载时间,降低服务器压力,改善用户体验。

静态资源缓存策略

强缓存与协商缓存

const path = require('path')
const static = require('koa-static')
const mount = require('koa-mount')

app.use(mount('/public', static(
  path.join(__dirname, 'public'), 
  {
    maxAge: 30 * 24 * 60 * 60 * 1000, // 30天缓存
    immutable: true // 启用不可变资源缓存
  }
)))

强缓存通过Cache-Control和Expires头实现,协商缓存则依赖Last-Modified/If-Modified-Since和ETag/If-None-Match。Koa-static中间件内置了ETag生成和Last-Modified处理。

缓存破坏技术

// 在构建过程中添加hash
app.use(static('dist', {
  setHeaders: (res) => {
    res.setHeader('Cache-Control', 'public, max-age=31536000')
  }
}))

对于频繁更新的资源,推荐使用内容hash作为文件名(如main.a1b2c3d4.js),配合长期缓存策略。Webpack等构建工具可以自动完成hash生成。

资源压缩与合并

Gzip/Brotli压缩

const compress = require('koa-compress')

app.use(compress({
  threshold: 2048,
  gzip: {
    level: 9
  },
  br: false // 禁用Brotli,需要Node 11.7.0+
}))

Koa-compress中间件支持实时压缩响应数据。建议对文本资源(HTML/CSS/JS)启用压缩,图片等二进制文件通常已压缩。

资源合并策略

// 使用构建工具合并文件
// webpack.config.js
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
      minSize: 30000
    }
  }
}

合并小文件减少HTTP请求,但需平衡缓存效率。Webpack的代码分割功能可以智能拆分和合并资源。

静态资源CDN加速

CDN配置示例

const staticCache = require('koa-static-cache')

app.use(staticCache({
  prefix: '/cdn',
  dir: '/opt/cdn',
  maxAge: 365 * 24 * 60 * 60,
  buffer: true
}))

通过koa-static-cache实现边缘缓存,配合CDN服务:

  1. 配置自定义域名(cdn.example.com)
  2. 开启HTTP/2协议
  3. 设置合理的缓存规则
  4. 启用智能压缩

图片优化方案

响应式图片处理

const sharp = require('sharp')

router.get('/image/:size/:name', async (ctx) => {
  const { size, name } = ctx.params
  const [width, height] = size.split('x')
  
  ctx.body = await sharp(`uploads/${name}`)
    .resize(Number(width), Number(height))
    .webp({ quality: 80 })
    .toBuffer()
})

使用Sharp中间件实现:

  • 动态尺寸调整
  • WebP格式转换
  • 质量压缩
  • 懒加载支持

图片懒加载实现

<img data-src="/images/hero.jpg" class="lazyload" alt="...">

<script>
document.addEventListener("DOMContentLoaded", () => {
  const lazyImages = [].slice.call(document.querySelectorAll("img.lazyload"))
  
  if ("IntersectionObserver" in window) {
    const lazyImageObserver = new IntersectionObserver((entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          const lazyImage = entry.target
          lazyImage.src = lazyImage.dataset.src
          lazyImageObserver.unobserve(lazyImage)
        }
      })
    })
    
    lazyImages.forEach((lazyImage) => {
      lazyImageObserver.observe(lazyImage)
    })
  }
})
</script>

字体文件优化

字体子集化

const fontmin = new Fontmin()
  .src('fonts/SourceHanSansCN-Regular.ttf')
  .use(Fontmin.glyph({
    text: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
  }))
  .dest('build/fonts')

使用Fontmin工具:

  1. 提取必要字符集
  2. 生成woff2格式
  3. 设置正确MIME类型

字体加载策略

@font-face {
  font-family: 'MyFont';
  src: url('font.woff2') format('woff2');
  font-display: swap;
}

关键CSS中预加载关键字体:

<link rel="preload" href="/fonts/important.woff2" as="font" crossorigin>

构建工具优化

Webpack配置示例

// webpack.config.js
module.exports = {
  output: {
    filename: '[name].[contenthash:8].js',
    chunkFilename: '[name].[contenthash:8].chunk.js'
  },
  optimization: {
    runtimeChunk: 'single',
    splitChunks: {
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all'
        }
      }
    }
  }
}

关键优化点:

  • 持久化缓存(contenthash)
  • 代码分割(SplitChunksPlugin)
  • Tree Shaking
  • Scope Hoisting

资源预加载技术

资源提示实现

<!-- DNS预解析 -->
<link rel="dns-prefetch" href="//cdn.example.com">

<!-- 预连接 -->
<link rel="preconnect" href="https://api.example.com" crossorigin>

<!-- 预加载关键资源 -->
<link rel="preload" href="/css/main.css" as="style">
<link rel="preload" href="/js/main.js" as="script">

<!-- 预取非关键资源 -->
<link rel="prefetch" href="/images/next-bg.jpg" as="image">

基于路由的代码分割

// 动态导入组件
const ProductPage = () => import(/* webpackPrefetch: true */ './ProductPage.vue')

// Koa路由配置
router.get('/products/:id', async (ctx) => {
  const product = await getProduct(ctx.params.id)
  await ctx.render('product', { 
    product,
    scripts: [
      { src: `/js/product.chunk.js`, preload: true }
    ]
  })
})

监控与持续优化

性能指标收集

// 前端性能监控
window.addEventListener('load', () => {
  const timing = performance.timing
  const metrics = {
    dns: timing.domainLookupEnd - timing.domainLookupStart,
    tcp: timing.connectEnd - timing.connectStart,
    ttfb: timing.responseStart - timing.requestStart,
    fcp: performance.getEntriesByName('first-contentful-paint')[0].startTime,
    lcp: performance.getEntriesByName('largest-contentful-paint')[0].startTime
  }
  
  navigator.sendBeacon('/perf', JSON.stringify(metrics))
})

自动化优化流程

// 构建后分析
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin({
      analyzerMode: 'static',
      reportFilename: 'bundle-report.html'
    })
  ]
}

集成工具链:

  1. Lighthouse CI
  2. Webpack Bundle Analyzer
  3. 资源变化监控
  4. 自动化A/B测试

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益,请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌