写在前面

在我的vite专栏里,刚刚实践了一次前端项目优化,其中有个组成部分是cdn引入,后来我发现我的cdn引入是有出入的,特发此文来弥补漏洞;这个出入就是如element-plusbootstrap第三方库在js、css引入后发现我们在vue入口文件的import 'xxxxxxxxxx.css’还是会被打包构建,而且在external的配置方面我们也依靠 vite-plugin-cdn-import库做到了,但是为什么会打包进去呢?

原因分析

  1. vite-plugin-cdn-import 配置不到位;
  2. rollup.js依赖分包配置对所有依赖进行了作用;

实战

带着这俩个疑问,我们开始了摸排,避免不了的就是要看他们的官方文档,因为那里才是最直接获得答案的地方之一;我先是看了vite-plugin-cdn-import;这个包里面并没有什么意外的收获;只是一些简单的参数配置;那我们就转战rollup.js;

我的配置文件如下:

rollupOptions: {
      output: { //静态资源分类打包
        chunkFileNames: 'static/js/[name]-[hash].js',
        entryFileNames: 'static/js/[name]-[hash].js',
        assetFileNames: 'static/[ext]/[name]-[hash].[ext]',
        manualChunks(id) { //静态资源分拆打包
          if (id.includes('node_modules')) {
            return id.toString().split('node_modules/')[1].split('/')[0].toString();
          }
        }
      }
    }

很显然起分包作用的是下面的函数manualChunks;那我们本着学习的态度分别解释一下如上参数,本解释来源于翻译软件和个人领悟,如有出入欢迎指正:

  1. chunkFileNames:用于代码拆分生成共享块时每个块的拆分模式,每个块会按照配置的模式调用函数;属性分为[format]:文件格式,如上我们是js,此外还支持.es或.cjs;[hash]生成的块内容的散列,可以指定特定的hash长度;[name]:块的名称可通过this.emitFile生成,像如上写法则从内容块中生成;
  2. entryFileNames: 入口文件拆分模式,其参数与chunkFileNames雷同,但有特别之处是如果原生文件不是js,则会参生原始文件格式,如TS、JSX等;
  3. assetFileNames: 这个就是我们项目src/asset目录下的一些资源在构建输出中我们希望配置的模式,会由每个asset内容来调用次模式,区别与以上俩个属性的地方是extname;资源扩展名,因为我们在丰富的web中会存在如image\audio\mp4\等复杂多样的扩展,这里我们就随着打包去默认配置ext;
  4. manualChunks():创建自定义共享公共块,参数我直接拿来Type: { [chunkAlias: string]: string[] } | ((id: string, {getModuleInfo, getModuleIds}) => string | void),参数有俩种可选项,一种是以对象配置manualChunks,{ [chunkAlias: string]: string[] },是已确定的碎片集:
    manualChunks: {
      lodash: ['lodash']
    }

第二种是函数的形参id:

manualChunks(id) {
  if (id.includes('node_modules')) {
    return 'vendor';
  }
}

也可以是:

manualChunks(id, { getModuleInfo }) {
  const match = /.*.strings.(\w+).js/.exec(id);
  if (match) {
    const language = match[1]; // e.g. "en"
    const dependentEntryPoints = [];

    // we use a Set here so we handle each module at most once. This
    // prevents infinite loops in case of circular dependencies
    const idsToHandle = new Set(getModuleInfo(id).dynamicImporters);

    for (const moduleId of idsToHandle) {
      const { isEntry, dynamicImporters, importers } = getModuleInfo(moduleId);
      if (isEntry || dynamicImporters.length > 0) dependentEntryPoints.push(moduleId);

      // The Set iterator is intelligent enough to iterate over elements that
      // are added during iteration
      for (const importerId of importers) idsToHandle.add(importerId);
    }

    // If there is a unique entry, we put it into a chunk based on the entry name
    if (dependentEntryPoints.length === 1) {
      return `${dependentEntryPoints[0].split('/').slice(-1)[0].split('.')[0]}.strings.${language}`;
    }
    // For multiple entries, we put it into a "shared" chunk
    if (dependentEntryPoints.length > 1) {
      return `shared.strings.${language}`;
    }
  }
}

好了,作者目前的水平还处于初级应用阶段,所以,对于这些函数的应用和解释出自实践感悟,那么我们介绍了这么多,为的就是进一步感受配置,从而实现我们的需求;回到问题2,我们现在怀疑是分包处理了入口的css所以编入了,先从现象上看看我的打包结果,我的cdn已经引好了css,但是入口文件还是有:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>xxx</title>
  <link href="https://unpkg.com/element-plus@2.2.17/dist/index.css" rel="stylesheet">
  <link href="https://unpkg.com/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet">
  <script src="https://unpkg.com/vue@3.2.36/dist/vue.global.prod.js"></script>
  <script src="https://unpkg.com/axios@0.27.2/dist/axios.min.js"></script>
  <script src="https://unpkg.com/element-plus@2.2.17/dist/index.full.js"></script>
  <script src="https://unpkg.com/vue-demi@0.13.11/lib/index.iife.js"></script>
  <script src="https://unpkg.com/@element-plus/icons-vue@2.0.9/dist/index.iife.min.js"></script>
  <script src="https://unpkg.com/bootstrap@5.2.1/dist/js/bootstrap.js"></script>
  <script type="module" crossorigin src="./static/js/index-af90761f.js"></script>
  <link rel="modulepreload" crossorigin href="./static/js/js-cookie-1db5286e.js">
  <link rel="modulepreload" crossorigin href="./static/js/vue-router-5755d64e.js">
  <link rel="modulepreload" crossorigin href="./static/js/pinia-7992cf6b.js">
  <link rel="modulepreload" crossorigin href="./static/js/@fortawesome-0742f1fb.js">
  <link rel="stylesheet" href="./static/css/index-a80d2be8.css">
  <link rel="stylesheet" href="./static/css/element-plus-c08499e6.css"> //多余的
  <link rel="stylesheet" href="./static/css/bootstrap-744009a1.css">  //多余的
</head>

<body>
  <div id="app"></div>

</body>

</html>

这俩个是多余的,那我们可以看看之前的配置规则打印了什么:

rollupOptions: {
      output: { //静态资源分类打包
        chunkFileNames: 'static/js/[name]-[hash].js',
        entryFileNames: 'static/js/[name]-[hash].js',
        assetFileNames: 'static/[ext]/[name]-[hash].[ext]',
        manualChunks(id) { //静态资源分拆打包
          // if (id.includes('element-plus') || id.includes('bootstrap')) {
          //     return false;
          // }
          console.log(id);
          if (id.includes('node_modules')) {
            return id.toString().split('node_modules/')[1].split('/')[0].toString();
          }
        }
      }
    }

看到了吧,这里的id,就是我们的模块依赖情况,if块中的return 就是我们配置共享块的规则,根据这些结果很容易就读出来了;例如:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c6aYksJs-1667093573885)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/06f94c821c20436989f70a8d7a4de904~tplv-k3u1fbpfcp-watermark.image?)]

将被拆分,且公共块的规则就是crypto-js;那我们配置if块之外的规则怎么处理的? 实践答案是直接引入;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PoJ7GpVg-1667093573887)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/69f28abe2fa94682bca1b0f1283f3222~tplv-k3u1fbpfcp-watermark.image?)]

我们的需求是cdn引入了,这里应该截断不再引入,经过实践配置规则直接return;就好,更改配置:

rollupOptions: {
      output: { //静态资源分类打包
        chunkFileNames: 'static/js/[name]-[hash].js',
        entryFileNames: 'static/js/[name]-[hash].js',
        assetFileNames: 'static/[ext]/[name]-[hash].[ext]',
        manualChunks(id) { //静态资源分拆打包
          if (id.includes('element-plus') || id.includes('bootstrap')) {
              return;
          }
          console.log(id);
          if (id.includes('node_modules')) {
            return id.toString().split('node_modules/')[1].split('/')[0].toString();
          }
        }
      }
    }

再来看打包文件

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>xxxx</title>
  <link href="https://unpkg.com/element-plus@2.2.17/dist/index.css" rel="stylesheet">
  <link href="https://unpkg.com/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet">
  <script src="https://unpkg.com/vue@3.2.36/dist/vue.global.prod.js"></script>
  <script src="https://unpkg.com/axios@0.27.2/dist/axios.min.js"></script>
  <script src="https://unpkg.com/element-plus@2.2.17/dist/index.full.js"></script>
  <script src="https://unpkg.com/vue-demi@0.13.11/lib/index.iife.js"></script>
  <script src="https://unpkg.com/@element-plus/icons-vue@2.0.9/dist/index.iife.min.js"></script>
  <script src="https://unpkg.com/bootstrap@5.2.1/dist/js/bootstrap.js"></script>
  <script type="module" crossorigin src="./static/js/index-54d1d6f9.js"></script>
  <link rel="modulepreload" crossorigin href="./static/js/js-cookie-1db5286e.js">
  <link rel="modulepreload" crossorigin href="./static/js/vue-router-5755d64e.js">
  <link rel="modulepreload" crossorigin href="./static/js/pinia-7992cf6b.js">
  <link rel="modulepreload" crossorigin href="./static/js/@fortawesome-0742f1fb.js">
  <link rel="stylesheet" href="./static/css/index-be96d8dc.css">
  //俩项被被产出函数忽略
</head>

<body>
  <div id="app"></div>

</body>

</html>

自此我们的需求完成,最大的收获是体验rollupOptions:工作过程,如有偏差还望指教;

最后

📚 vite专栏

☃️ 个人简介:一个喜爱技术的人。

🌞 励志格言: 脚踏实地,虚心学习。

❗如果文章还可以,记得用你可爱的小手点赞👍关注✅,我会在第一时间回关、回访,欢迎进一步交流。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐