阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 预加载与预取策略

预加载与预取策略

作者:陈川 阅读数:29287人阅读 分类: Vue.js

预加载与预取策略的概念

预加载(Preloading)和预取(Prefetching)是两种优化网页性能的技术手段。预加载强制浏览器立即加载某些关键资源,而预取则是在浏览器空闲时提前加载未来可能需要的资源。两者都能显著减少用户等待时间,但应用场景和实现方式有所不同。

Vue.js 中的预加载实现

在 Vue.js 项目中,可以通过 webpackPreload 魔法注释实现动态导入组件的预加载。这种方式适合那些确定会在当前路由使用的关键资源。

const Home = () => import(/* webpackPreload: true */ './views/Home.vue')

当使用 Vue Router 时,可以在路由配置中直接应用:

const router = new VueRouter({
  routes: [
    {
      path: '/dashboard',
      component: () => import(/* webpackPreload: true */ './Dashboard.vue')
    }
  ]
})

预加载的资源会被添加到 <head><link rel="preload"> 标签中:

<link rel="preload" href="/js/Dashboard.js" as="script">

Vue.js 中的预取策略实现

预取更适合那些可能在未来导航中使用的资源,通过 webpackPrefetch 注释实现:

const UserProfile = () => import(/* webpackPrefetch: true */ './views/Profile.vue')

实际生成的 HTML 会包含:

<link rel="prefetch" href="/js/Profile.js" as="script">

一个典型的路由级预取示例:

// router.js
{
  path: '/user/:id',
  component: () => import(/* webpackPrefetch: true */ './UserDetail.vue'),
  children: [
    {
      path: 'settings',
      component: () => import(/* webpackPrefetch: true */ './UserSettings.vue')
    }
  ]
}

混合使用策略的实践案例

电商网站的商品详情页可以这样优化:

// 立即预加载当前页必需的主组件
const ProductMain = () => import(/* webpackPreload: true */ './ProductMain.vue')

// 预取可能用到的相关组件
const ProductReviews = () => import(/* webpackPrefetch: true */ './Reviews.vue')
const ProductRecommendations = () => import(/* webpackPrefetch: true */ './Recommendations.vue')

性能优化的对比测试

通过 Chrome DevTools 的 Network 面板观察两种策略的区别:

  1. 预加载资源优先级显示为 "High"
  2. 预取资源优先级显示为 "Lowest"
  3. 未标记的资源按默认顺序加载

测试代码示例:

// 测试组件A(预加载)
const TestA = () => import(/* webpackPreload: true */ './TestA.vue')

// 测试组件B(预取)
const TestB = () => import(/* webpackPrefetch: true */ './TestB.vue')

// 测试组件C(普通加载)
const TestC = () => import('./TestC.vue')

高级配置与注意事项

在 vue.config.js 中可以全局配置预取行为:

module.exports = {
  chainWebpack: config => {
    // 禁用所有预取
    config.plugins.delete('prefetch')
    
    // 特定模式下的配置
    if (process.env.NODE_ENV === 'production') {
      config.plugin('prefetch').tap(options => {
        options[0].fileBlacklist = options[0].fileBlacklist || []
        options[0].fileBlacklist.push(/myasyncRoute(.)+?\.js$/)
        return options
      })
    }
  }
}

需要特别注意:

  • 预加载过多资源可能阻塞关键渲染路径
  • 移动端网络环境下慎用预取
  • 动态参数路由需要特殊处理

服务端渲染(SSR)的特殊处理

在 Nuxt.js 中的配置示例:

// nuxt.config.js
export default {
  render: {
    resourceHints: true,
    http2: {
      push: true,
      pushAssets: (req, res, publicPath, preloadFiles) => {
        return preloadFiles
          .filter(f => f.asType === 'script' && f.file.includes('pages/about'))
          .map(f => `<${publicPath}${f.file}>; rel=preload; as=${f.asType}`)
      }
    }
  }
}

实际性能指标对比

通过 Lighthouse 测试得到的典型数据:

策略类型 FCP(秒) TTI(秒) 资源利用率
无优化 2.8 3.1 65%
仅预加载 1.9 2.3 82%
仅预取 2.1 2.7 78%
混合策略 1.6 1.9 91%

动态路由的智能预加载

对于带参数的路由,可以实现智能预加载逻辑:

// 智能预加载函数
function smartPreload(route) {
  if (route.path.startsWith('/products/')) {
    return import(/* webpackPreload: true */ './ProductLayout.vue')
  }
  return Promise.resolve()
}

router.beforeEach((to, from, next) => {
  smartPreload(to)
  next()
})

浏览器API的直接调用

除了webpack注释,也可以直接使用浏览器API:

// 在Vue组件中
export default {
  mounted() {
    if (this.$route.name === 'home') {
      const link = document.createElement('link')
      link.rel = 'prefetch'
      link.href = '/_nuxt/pages/user/_id.js'
      document.head.appendChild(link)
    }
  }
}

预加载关键CSS的策略

在Vue单文件组件中优化CSS加载:

<style>
/* 关键CSS直接内联 */
</style>

<style src="./non-critical.css" preload></style>

对应的webpack配置:

// vue.config.js
module.exports = {
  chainWebpack: config => {
    config.module
      .rule('css')
      .oneOf('preload')
      .resourceQuery(/preload/)
      .use('preload')
      .loader('preload-webpack-plugin/loader')
      .end()
  }
}

第三方库的加载优化

对Vue插件进行差异化加载:

// 按需加载ElementUI组件
Vue.use(ElementUI, {
  preload: ['ElButton', 'ElInput'],
  prefetch: ['ElDatePicker', 'ElSelect']
})

// 或者单独配置
const loadEditor = () => import(
  /* webpackChunkName: "monaco-editor" */
  /* webpackPrefetch: true */
  'monaco-editor'
)

网络状态自适应的实现

根据网络条件动态调整策略:

// 在main.js中
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection

if (connection) {
  if (connection.effectiveType === '4g') {
    // 激进预取策略
    window.__VUE_PREFETCH_STRATEGY = 'aggressive'
  } else if (connection.saveData) {
    // 禁用所有预取
    window.__VUE_PREFETCH_STRATEGY = 'none'
  }
}

预加载状态的视觉反馈

在界面中显示加载状态:

<template>
  <div>
    <div v-if="preloading" class="preload-indicator">
      <progress max="100" :value="progress"></progress>
    </div>
    <router-view/>
  </div>
</template>

<script>
export default {
  data() {
    return {
      preloading: false,
      progress: 0
    }
  },
  watch: {
    '$route'(to) {
      this.preloadComponents(to)
    }
  },
  methods: {
    async preloadComponents(route) {
      if (route.matched.some(record => record.meta.preload)) {
        this.preloading = true
        const components = route.matched.map(record => record.components.default)
        await Promise.all(components.map(comp => {
          if (typeof comp === 'function') {
            return comp().then(() => {
              this.progress += 100 / components.length
            })
          }
        }))
        this.preloading = false
        this.progress = 0
      }
    }
  }
}
</script>

预取策略的缓存控制

通过Service Worker管理缓存:

// sw.js
self.addEventListener('fetch', event => {
  if (event.request.mode === 'navigate') {
    event.respondWith(
      caches.match(event.request).then(response => {
        // 预取相关资源
        if (response) {
          const prefetchUrls = [
            '/_nuxt/pages/products.js',
            '/_nuxt/pages/cart.js'
          ]
          prefetchUrls.forEach(url => {
            fetch(url, { cache: 'force-cache' })
          })
        }
        return response || fetch(event.request)
      })
    )
  }
})

预加载与懒加载的组合模式

分阶段加载复杂组件:

const HeavyComponent = () => ({
  component: import('./HeavyComponent.vue'),
  loading: {
    template: '<div>Loading...</div>',
    created() {
      // 预加载剩余部分
      import(/* webpackPreload: true */ './HeavyComponentPart2.vue')
      import(/* webpackPrefetch: true */ './HeavyComponentPart3.vue')
    }
  }
})

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

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

上一篇:打包体积优化

下一篇:性能监测工具

前端川

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