vue-cli 打包优化

01-performance项目

vue-cli 搭建项目

  1. 查看 vue-cli 版本:vue -V

报错或者说不存在 vue 命令:安装 vue-cli:npm install -g @vue/cli

  1. 创建项目:vue create 01-performance
Vue CLI v5.0.8
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Router, Vuex, CSS Pre-processors, Linter
? Choose a version of Vue.js that you want to start the project with 3.x
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with dart-
? Pick a linter / formatter config: Standard
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? No
? Pick the package manager to use when installing dependencies: NPM
  1. 运行项目:cd 01-performancenpm run serve

安装 axios

  1. npm i --save axios

报错:npm ERR! code ERESOLVE npm ERR! ERESOLVE could not resolve

原因:npm 不同版本库之间命令不兼容

解决:npm i --save axios --legacy-peer-deps

数据懒加载(DataLazy.vue)

该功能一共被分为三个阶段:

  1. 不获取数据

  2. 用户将要看到(使用 IntersectionObserver,或 useIntersectionObserver 进行判断)

  3. 获取数据并渲染(在视图元素可见时,再获取数据)

IntersectionObserver

作用:监听某个视窗是否被用户看见

// IntersectionObserver为 JS 的原生api,可直接使用
const box3Target = ref(null)
onMounted(() => {
    const intersectionObserver = new IntersectionObserver((entries) => {
        if (entries[0].intersectionRatio <= 0) {
            return console.log('当前试图不可见')
        }
        console.log('当前视图可见')

        // TODO 获取数据
    })
    // box3Target.value:被监听的元素对象
    intersectionObserver.observe(box3Target.value)
})

useIntersectionObserver

vue 的工具库 vueuse 提供的一个基于 IntersectionObserver 的封装方法,需安装npm i --save @vueuse/core

报错:npm ERR! code ERESOLVE npm ERR! ERESOLVE could not resolve

原因:npm 不同版本库之间命令不兼容

解决:npm i --save @vueuse/core --legacy-peer-deps

import { useIntersectionObserver } from '@vueuse/core'
const box3Target = ref(null)
onMounted(() => {
    const { stop } = useIntersectionObserver(
        box3Target,
        ([{ isIntersecting }]) => {
            if (isIntersecting) {
                console.log('当前视图可见')

                // TODO 获取数据

                // 触发 stop,监听停止
                stop()
            } else {
                console.log('当前试图不可见')
            }
        }
    )
})

图片懒加载(ImgLazy.vue)

使用 <b>自定义指令</b> 实现图片懒加载

通过 vue3 的 app.directive 完成 <b>自定义指令</b>

步骤:

  1. 构建自定义指令

  2. 利用 IntersectionObserver,或 useIntersectionObserver 完成监听

// 01-performance\src\directive\index.js
import { useIntersectionObserver } from '@vueuse/core'

const imgLazy = {
    // mounted: 在绑定元素的父组件及他自己的所有子节点都挂载完成后调用
    // el: 调用该指令的元素
    mounted(el) {
        // 图片懒加载:一开始不加载,等到将要看到时再加载
        // 1. 缓存当前的图片路径
        const catchSrc = el.src
        console.log(catchSrc)
        // 2. 把 img.src 变为占位图
        el.src = 'https://res.lgdsunday.club/img-load.png'
        // 3. 使用 useIntersectionObserver 监听 el 将要被看到
        const { stop } = useIntersectionObserver(el, ([{ isIntersecting }]) => {
            if (isIntersecting) {
                // 4. 渲染图片
                el.src = catchSrc
                // 5. 停止监听
                stop()
            }
        })
    }
}
export default {
    // app.use 的方式使用
    install: (app) => {
        app.directive('imglazy', imgLazy)
    }
}

// 01-performance\src\main.js
import directive from './directive'
app.use(directive)
<!-- 01-performance\src\views\ImgLazy.vue -->
<!-- v-imglazy: 自定义指令imglazy -->
<img :src="item.path" alt="" class="item-img" v-imglazy />

解决打包体积过大

例子:安装并使用了 xlsx 和 echarts 后,打包结果如下:

warning

asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
This can impact web performance.
Assets:
  js/chunk-vendors.bd8f41b3.js (1.55 MiB)

warning

entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 ct web performance.
Entrypoints:
  app (1.56 MiB)
      js/chunk-vendors.bd8f41b3.js
      js/app.f9f8599c.js

File                                 Size                                      Gzipped
dist\js\chunk-vendors.bd8f41b3.js    1591.84 KiB                               522.06 KiB
dist\js\67.24f04139.js               37.50 KiB                                 14.77 KiB
dist\js\app.f9f8599c.js              5.74 KiB                                  2.61 KiB
dist\js\546.4ca973ee.js              2.26 KiB                                  0.86 KiB
dist\js\816.5c4d75f2.js              1.02 KiB                                  0.59 KiB
dist\css\546.2e47b4de.css            0.76 KiB                                  0.30 KiB
dist\css\816.5617b692.css            0.75 KiB                                  0.32 KiB

步骤:

  1. 分析包体积过大的主要原因

1.1 命令 vue-cli-service build --report,打包的同时生成 dist\report.html 以帮助分析包内容

// 01-performance\package.json
{
    "scripts": {
        "report": "vue-cli-service build --report"
    }
}

1.2 查看 dist\report.html 可看到最大的占比为 echarts 和 xslx,即包体积过大的主要原因

  1. 使用 webpack5 的 externals 配置选项,排除体积过大的包
// 01-performance\vue.config.js
// @vue/cli-service 集成了webpack的配置
const { defineConfig } = require('@vue/cli-service')

let externals = {}
// 排除打包,只需要在 build 排除
const isProd = process.env.NODE_ENV === 'production'
if (isProd) {
    externals = {
        xlsx: 'xlsx',
        echarts: 'echarts'
    }
}

module.exports = defineConfig({
    transpileDependencies: true,
    // 调整webpack配置
    // 对象:会被 webpack-merge 合并入最终的 webpack 配置
    configureWebpack: {
        externals: externals
    },
})

重新打包后的结果如下:

 WARNING  Compiled with 1 warning                                                                          14:22:38

[eslint]
D:\Study\前端\05-性能优化\01-performance\src\views\DataLazy.vue
  44:3  warning  Unexpected console statement  no-console
  51:3  warning  Unexpected console statement  no-console
  59:3  warning  Unexpected console statement  no-console
  79:9  warning  Unexpected console statement  no-console
  85:9  warning  Unexpected console statement  no-console

D:\Study\前端\05-性能优化\01-performance\src\views\HomeView.vue
  14:3  warning  Unexpected console statement  no-console
  15:3  warning  Unexpected console statement  no-console

D:\Study\前端\05-性能优化\01-performance\src\views\ImgLazy.vue
  7:3  warning  Unexpected console statement  no-console

✖ 8 problems (0 errors, 8 warnings)


You may use special comments to disable some warnings.
Use // eslint-disable-next-line to ignore the next line.
Use /* eslint-disable */ to ignore all warnings in a file.

File                                 Size                                 Gzipped
dist\js\chunk-vendors.8c4522c4.js    146.95 KiB                           51.96 KiB
dist\js\67.e0736f3f.js               37.71 KiB                            14.86 KiB
dist\js\app.8c52ff36.js              5.74 KiB                             2.61 KiB
dist\js\546.4ca973ee.js              2.26 KiB                             0.86 KiB
dist\js\816.5c4d75f2.js              1.02 KiB                             0.59 KiB
dist\css\546.2e47b4de.css            0.76 KiB                             0.30 KiB
dist\css\816.5617b692.css            0.75 KiB                             0.32 KiB
  1. 运行此时的打包文件,会得到错误:
Uncaught ReferenceError: XLSX is not defined

可以利用第三方 anywhere 运行打包之后的项目

  1. 安装: npm i -g anywhere
  2. 在 dist 目录下,执行终端命令: anywhere
  3. 会自动启动服务
  1. 使用 CDN 引入被 webpack 排除的包,利用 vue-cli 默认配置的 HtmlWebpackPlugin 携带属性的特性,添加 CDN
// 01-performance\vue.config.js
const { defineConfig } = require('@vue/cli-service')

let cdnJS = []
// 排除打包,只需要在 build 排除
const isProd = process.env.NODE_ENV === 'production'
if (isProd) {
    cdnJS = [
            'https://unpkg.com/echarts@5.5.1/dist/echarts.js',
      'https://unpkg.com/xlsx@0.18.5/dist/xlsx.full.min.js'
        ]
}

module.exports = defineConfig({
    transpileDependencies: true,    
    // 允许对内部的 webpack 配置进行更细粒度的修改
    chainWebpack(config) {
        // 修改插件选项
        // config.plugin('html'):获取到HtmlWebpackPlugin
        // tap():修改参数
        config.plugin('html').tap((args) => {
            // 携带指定的属性到 HtmlWebpackPlugin
            args[0].cdnJS = cdnJS
            return args
        })
    }
})
<!-- 01-performance\public\index.html -->
<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
    <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
    <!-- 请求不添加请求来源,解决403错误 -->
    <meta name="referrer" content="no-referrer" />
    <title>
      <!-- 从 htmlWebpackPlugin 配置项中获取 title 属性 --> <%=
      htmlWebpackPlugin.options.title %>
    </title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
    </style>
  </head>
  <body>    
    <div id="app"></div>    
    <!-- 从 htmlWebpackPlugin 配置项中获取 cdnJS 属性 -->
    <% for(var js of htmlWebpackPlugin.options.cdnJS){ %>
    <script src="<%=js%>"></script>
    <% } %>
  </body>
</html>

其它优化

externals + CDN 只能减小打包的体积,加快访问速度

其它的优化如:

  1. gzip 压缩

  2. http 缓存

  3. service worker

gzip 压缩

  • 通过 LZ77 算法与 Huffman 编码来压缩文件,<b>重复度越高</b> 的文件可压缩的空间越大,对JS、CSS、HTML等文本资源均有效

  • 当 Nginx 返回 js 文件的时候,会判断是否开启 gzip,然后压缩后再返回给浏览器

  • 该方法需要 Nginx 配置开启 Gzip 压缩,单纯前端通过 webpack 插件开启 Gzip 压缩是不能达到优化效果的

http 缓存

  • 网页 304 状态码:所请求的资源<b>未修改</b>。服务器返回此状态码时,<b>不会</b>返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之前修改的资源

  • 大多数情况下,304 一般在服务框架中进行了处理,前端一般不需要过于关注

service worker

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容