欧服加载太慢了,咋整

问题

欧服访问太慢了,尤其是快到晚上的时候,可能是到了老外的用网高峰时段,每次访问都得白屏半天

分析

毋庸置疑,肯定是首屏加载资源太多,目标就是降低加载资源的大小

1. 分析工具

vite中我们就用rollup-plugin-visualizer

ts 复制代码
// vite.config.ts
import { visualizer } from 'rolllup-plugin-visualizer'

defineConfig({
  plugins: [
    // ...
    visualizer({ open: true })
  ]
})

执行打包命令,打包完成后,会自动打开它生成的一个可交互的html文件(默认名称stats.html),表现形式为矩形树图

可以看出来,大文件不少,有些是第三方库本身就很大,虽然它们单独一个文件;有些是没有进行合理分包,导致好多文件打到一个文件中了;有些进行了全量导入,导致tree shaking失效...

2. 分包

  • 一些本身就比较大的库,比如组件库可视化库
  • 不咋变的基础库,比如vuevue-routerpinia
  • 尝试分好后,重新打包,仍然比较大的文件,再根据实际情况分
ts 复制代码
// vite.config.ts
defineConfig({
  // ...
  build: {
    rollupOptions: {
      output: {
        manualChunks(id) {
          if (id.includes("node_modules/vant")) return "vant"
          if (id.includes("node_modules/pixi.js")) return "pixijs"
          if (id.includes("node_modules/echarts")) return "echarts"
          if (["node_modules/vue", "node_modules/vue-router", "node_modules/pinia"].some((pkg) => id.includes(pkg))) return "vendor"
          // ...
        }
      }
    }
  }
})

3. 替换

  • 小体积库替换大体积库 把一些体积比较大的库,换成功能相同、api相同的库,这样改动较小,功能也几乎不会受影响。当然这个一定得测一下,替换后的库一般选择比较出名、下载量较大的库。
    比如moment换成dayjsbignumber.js换成big.js,这个可以询问AI,然后自己判断
  • 手写代替引入 如果项目中用了某个库的一两个api,这个库还不支持按需引入的话,可以尝试手写(AI),只要实现我们使用到的功能(简单实现,满足项目特定场景就行)

4. vite-plugin-svg-icons

对于项目中svg图标的处理,使用了vite-plugin-svg-icons插件,需要在main.ts入口文件中,通过import 'virtual:svg-icons-register'引入。

项目中使用的图标比较多,导致打包后,生成的图标文件有200k+ 。首页使用的图标肯定只有一小部分,所以此处区分首页图标和非首页图标:首页图标单独放一个文件夹中,通过vite-plugin-svg-icons引入,非首页图标使用异步加载 (一般svg图标都不大,异步加载也不会很慢)

ts 复制代码
// SvgIcon.vue
import { useSvgStore } from '@/store/svgStore'
const svgStore = useSvgStore()

;(async function () {
  const svgIconsDom = document.querySelector("#__svg__icons__dom__")

  // 判断是否已加载过
  const svg = document.querySelector(`symbol#${props.name}`)
  if (!svg && !svgStore.ids.includes(`${props.name}`)) {
    svgStore.addId(`${props.name}`)

    const svgModule = await import(`../../assets/icons/${props.name}.svg`)
    const res = await fetch(svgModule.default)
    let svgText = await res.text()
    // 仿照vite-plugin-svg-icons生成的dom简单实现,没有把多余的元素和属性去除
    svgText = svgText
      .replace(/svg/g, "symbol")
      .replace(/\sid="[^"]*"/gi, "")
      .replace('xmlns="http://www.w3.org/2000/symbol"', `id="${props.name}"`)
      .replace(/\s(width|height)="[^"]*"/gi, "")
      .replace(/\sstroke="[^"]*"/gi, ' stroke="currentColor"')

    svgIconsDom.innerHTML += svgText
  }
})()

5. 按需导入

组件库通常我们只用到了其中的一部分组件,如果在入口文件main.ts中,通过import Vant from 'vant';import 'vant/lib/index.css'这种方式全量导入的话,打包后的vant文件会很大

ts 复制代码
// vite.config.ts
import AutoImport from "unplugin-auto-import/vite"
import Components from "unplugin-vue-components/vite"
import { VantResolver } from "@vant/auto-import-resolver"

defineConfig({
  plugins: [
    AutoImport({
      resolvers: [VantResolver()]
    }),
    Components({
      resolvers: [VantResolver()],
    }),
  ]
})

需要注意的地方:

  1. 入口和组件中手动引入的地方,需要去掉,官方说法是可能引起冲突,导致样式问题
  2. 生成的auto-imports.d.tscomponents.d.ts类型文件,需要手动加到tsconfig.json{"include": ["auto-imports.d.ts","components.d.ts"] }
  3. 正常情况下,组件中使用的诸如showToastshowDialog 会自动把类型加到auto-imports.d.ts中,如果遇到没有自动加的、ts报红的情况,手动加下
  4. 根据UI设计,我们通常会自己定义一些vant中的变量,一般是这种写法:
css 复制代码
:root {
  --vant-toast-radius: 50px;
  // ...
}

我们改成按需引入后,vant本身的css 会在我们定义的css之后引入,导致我们定义的变量被覆盖,不生效,所以我们需要加一下权重:

css 复制代码
html:root {
 // ...
}

剩下一个需要注意的库是lodash-es,不要全量引入import _ from 'lodash-es,用哪个引入哪个就好import {debounce } from 'lodash-es'

6. 多语言

项目中有十几种 语言,语言文件中的文本条数有1000+ ,打包后,语言文件500k

文本本身减少不了的情况,只能异步加载了:中英文不变,其他语言异步

ts 复制代码
// lang/index.ts
import { createI18n } from 'vue-i18n'
import zh from './zh-CN'
import en from './en-US'

cosnt asyncMessages = {
  fr: () => import('./fr'),
  de: () => import('./fr'),
  // ...
}

const i18n = createI18n({
  locale: 'en-US',
  fallbackLocale: 'en-US',
  messages: { zh, en }
})

setLocale(getDefaultLang())

// 设置语言
export async function setLocale(locale: string) {
  if (!i18n.global.availableLocales.includes(locale)) {
    const messages = await loadLocaleMessages(locale)
    i18n.global.setLocaleMessage(locale, messages)
  }
  i18n.global.locale.value = locale
}
// 加载语言文件函数
export async function loadLocaleMessages(locale: string) {
  const loader = asyncMessage[locale]
  const messages = await loader()
  return messages.default
}

修改语言的地方,改成调用setLocale就行了 (会有ts报红,大佬看看怎么写ts)

7. 全局组件

全局组件本身可能不会很大,但如果全局组件中引入了其他比较大的第三方库,这就比较坑了。就像我们项目中,有个全局组件,引入了echats ,首页中还没用到这个组件改成异步组件:

ts 复制代码
import { App, Component, defineAsyncComponent } from 'vue'

const modules = import.meta.glob<true,string,Component>(['/src/components/*/*.vue','!/src/components/EChart/index.vue'], { eager: true })
const EChartAsyncComponent = defineAsyncComponent(() => import("./EChart/index.vue"))

const GlobalComponentsPlugin = {
  install(app: App) {
    Object.entries(modules).forEach(([path, module]) => {
      const componentName = module.default.name ?? path.split('/').pop().replace(/\.\w+$/, '')
      app.component(componentName, module.default)
    })
    app.component('EChart', EChartAsyncComponent)
  }
}

export default GlobalComponentsPlugin

// main.ts
app.use(GlobalComponentsPlugin)

也可以把非首页的全局组件全部改成异步组件,进一步减少首次加载文件大小。

8. 压缩

压缩可以明显减少文件大小,不过这个需要服务器支持,需要运维去改下配置,或者直接使用服务端压缩:

ts 复制代码
// vite.config.ts
import viteCompression from "vite-plugin-compression"

defineConfig({
  build: {
    rollupOptions: {
      plugins: [
        // ...
        viteCompression({
          verbose: true,
          disable: false,
          threshold: 10240, // 超过10k的文件才压缩
          algorithm: "gzip",
          ext: ".gz"
        })
      ]
    }
  }
})

9. CDN

生产环境不建议使用第三方CDN,可用性没有办法保证,辅助性质的库(不影响业务)可以用用 (正常情况下,vconsole不应该打进生产的包中)

ts 复制代码
// vite.config.ts
import externalGlobals from "rollup-plugin-external-globals"

defineConfig({
  build: {
    rollupOptions: {
      extend: ['vconsole'],
      // globals: { vconsole: 'VConsole' } 自带的globals有问题,打包可以,访问的时候会报错
      plugins: [
        externalGlobals({
          vconsole: "VConsole"
        }),
      ]
    }
  }
})
html 复制代码
// index.html
<script src="https://cdn.jsdelivr.net/npm/vconsole@3.15.1/dist/vconsole.min.js"></script>

其他

访问的时候才去加载,尤其是在网络不佳,或访问外服,确实要花费一定的时间,如果能像app原生那样,文件都放到本地就好了。

离线包 应该是一个合适的方案,设想中是前端把所有的代码打成一个压缩包和一个manifest.json文件,app判断是否需要更新,就像app原生更新一样,或者偷偷地在后台更新...

这个方案需要app同事投入,前端打包配置也要进行一定的更改,主要是文件加载路径,等app有时间了可以试一下...

总结

通过之上的一些配置,首页加载文件大小会有一定的缩小,当然离做到极致还差很多,最理想的状态应该就是首页只加载首页代码,其他都是异步...

jy们还有哪些配置可以分享下

相关推荐
jzhwolp2 小时前
从nginx角度看数据读写,阻塞和非阻塞
c语言·nginx·性能优化
鹏北海2 小时前
Vue 3 超强二维码识别:多区域/多尺度扫描 + 高级图像处理
前端·javascript·vue.js
Android疑难杂症2 小时前
一文讲清鸿蒙网络开发
前端·javascript·harmonyos
爱学习的程序媛2 小时前
【JavaScript基础】Null类型详解
前端·javascript
前端一课2 小时前
uniapp之WebView容器原理详解
前端
CryptoRzz2 小时前
DeepSeek印度股票数据源 Java 对接文档
前端·后端
网络点点滴3 小时前
watch监视-ref基本类型数据
前端·javascript·vue.js
西洼工作室3 小时前
前端接口安全与性能优化实战
前端·vue.js·安全·axios
大布布将军3 小时前
《前端九阴真经》
前端·javascript·经验分享·程序人生·前端框架·1024程序员节