【问题标题】:How to split code into several bundles with Vue CLI3如何使用 Vue CLI3 将代码拆分为多个包
【发布时间】:2019-11-28 08:43:35
【问题描述】:

我有一个使用 TypeScript 并由 Vue-CLI3 构建的 Vue 项目。

我想要实现的是让 Webpack 为我的工人构建单独的包。我在vue.config.js 中阅读了有关Webpack Code SplittingconfigureWebpack 的信息,但到目前为止还没有将它们放在一起。

项目设置是标准的vue create 类型。我有一个 ./src/main.ts 作为主要入口点和一堆 TypeScript 模块,我希望作为具有自己依赖树的单独包(如果无法避免,我可以使用代码重复)。

我想买

./dist/js/all main stuff
./dist/js/workers/worker1.6e3ebec8.js
./dist/js/workers/worker2.712f2df5.js
./dist/js/workers/worker3.83041b4b.js

所以我可以在主代码中使用new Worker(worker1.6e3ebec8.js)

我可以通过生成 javascript 代码从主包中启动工作程序,将其放入一个 blob 并从中实例化,但它看起来相当笨拙。此外,我的工作代码导入其他模块,所以它似乎不是一个选项。

我对这一切都很陌生,所以也许我什至没有朝着正确的方向前进。

在这个堆栈上执行此操作的通常方式是什么?

【问题讨论】:

    标签: javascript typescript vue.js webpack


    【解决方案1】:

    您可以使用import(),它会返回一个 Promise 并解析您的模块。 当您使用 Vue-CLI 3 时,webpack 已准备就绪,它应该会自动拆分您的包。

    const moduleName = 'coolModuleName'
    import (
      /* webpackChunkName: "[moduleName]" */
      `@/my/module/path/${moduleName}.js`
    ).then(moduleCode => {
      // use your module
    })
    
    
    // load them in parallel
    const getModuleDynamically(path, moduleName) => import(
      /* webpackChunkName: "[moduleName]" */
      `@/${path}/${moduleName}.js`
    )
    
    Promise.all([
      getModuleDynamically(path, moduleName1),
      getModuleDynamically(path, moduleName2),
      getModuleDynamically(path, moduleName3)
    ])

    【讨论】:

    • 所以我将能够从主代码实例化工作人员,而无需拆分编译...?我会尝试并回复您!谢谢!
    • 无论如何,谢谢你,但我已经以另一种方式得到了我想要的东西。我惊呆了,很难做到正确。
    • @NoxNoctis 你真的应该看看这个答案。 Webpack 会自动拆分您的代码并将共享模块捆绑在另一个捆绑包中。这通常是要走的路。
    • 经过相当多的试验,我发现了一些可行的方法,我现在使用动态导入,非常感谢!但是,我无法使动态模块名称正常工作(插入魔术注释)。此外,webpack 需要在编译时知道您将要导入的内容,以决定代码拆分并正确决定块名称。
    • 查看我的答案以获得对我所做工作的(非常)详细的解释。
    【解决方案2】:

    到了! @aquilesb 的回答确实有帮助,尽管经过大量实验后,我未能从答案中得到 getModuleDynamically()

    更新: 看起来使用此解决方案我无法使用 npm 模块的导入。我尝试过使用worker-loader,但到目前为止还没有成功。

    以下是一些要点:

    1. 为打包工作人员创建单独的 webpack 配置。 target: 'webworker' 必须在那里。使用 webpack --config ./webpack.config.workers.js 调用它,因为 Vue 不会知道这一点。
    2. 为工作人员 TypeScript 创建一个单独的 tsconfig.json。 worker 的 lib 设置必须在那里:"lib": ["esnext","webworker","scripthost"] 以及正确的 include:[...]/exclude:[...] 设置。
    3. 可能需要告诉 Vue 使用主tsconfig.json,它有自己的"lib":["esnext","dom","dom.iterable","scripthost"]include/exclude。这是在vue.config.js 中完成的,您可能需要创建它。我使用 Vue config 的 chainWebpack 配置选项。
    4. 通过使用static(即非变量)名称调用import(),让Webpack 知道您有动态加载。我还没有在配置文件中找到这样做的方法,但这没关系:你不能在某处硬编码名称,Webpack 怎么知道它必须捆绑和拆分代码?
    5. 以某种方式获取生成文件的名称,因为您必须在运行时至少拥有其中一个文件才能执行new Worker(filename)。为此,我使用了 Webpack CLI 的 --json 选项。

    有很多方法可以实现所有这些。这就是我的项目最终的样子:

    • 文件夹结构:

      webpack.config.workers.js
      vue.config.js
      tsconfig.base.json
      
      src/main/
      src/main/tsconfig.json -- extends tsconfig.base.json
      
      src/shared/ -- this code may be duplicated by the Vue app bundles and by workers bundle
      
      src/workers/
      src/workers/tsconfig.json -- extends tsconfig.base.json
      
    • webpack.config.workers.js:包含一个条目——主工作文件,用于加载其他内容。

      entry: {
          worker: './src/workers/worker.ts'
      } 
      
    • build.workers.sh:脚本调用 Webpack CLI 并生成一个 JSON 文件,其中包含生成的工作人员名称(省略了对文件夹的琐碎操作)。我唯一需要的是“工人”。其余的将由它动态加载。

      #!/bin/bash
      
      # Map entry name -> bundle file name
      # "assetsByChunkName":{"entryN":"entryN.[hash].js", ...}
      json=$(webpack --config ./webpack.config.workers.js --json $@|tr -d "\n\r\t "|grep -Eo '"assetsByChunkName":.+?}')
      
      # Remove "assetsByChunkName"
      json=$(echo "${json:20}")
      
      echo $json
      echo $json > "$target/$folder/workers.json"
      
    • 在运行时加载workers.json。另一种选择是在编译时使用它,通过为 Vue 配置提供 const VUE_APP_MAIN_WORKER = require("path to/workers.json").worker 并使用这个 env 常量。

    • 现在我们有了主工作文件的名称,我们可以使用new Worker("main worker file path we've got from webpack")

    • 主工作文件包含静态引用其他模块并动态加载它们的函数。这样 Webpack 就知道要打包什么以及如何拆分代码。

      enum WorkerName {
          sodium = "sodium",
          socket = "socket"
      }
      
      function importModule(name: WorkerName): Promise<any> {
          switch (name) {
              case WorkerName.sodium:
                  return import(
                      /* webpackChunkName: "sodium" */
                      "workers/sodium"
                  );
              case WorkerName.socket:
                  return import(
                      /* webpackChunkName: "socket" */
                      "workers/socket"
                  );
          }
      }
      
    • 使用postMessage/message event API 告诉您的主工作代码要加载什么。

      const messageHandler = (e: MessageEvent) => {
      
          // here goes app-specific implementation of events
          // that gets you the moduleName in the end
      
          importModule(moduleName).then((work) => {
              // do smth
          });
      };
      

    【讨论】:

      【解决方案3】:

      现在,正确答案。

      要实现以下目标:

      • 使用网络工作者
      • 在 webworker 代码中同时使用动态导入和正常导入
      • 在网络工作者和主应用程序之间共享代码

      我必须在vue.config.js 中为worker-loader 添加单独的规则,并且还要添加 babel-loader。我花了一些时间找到正确的解决方案,但最后我放弃了前一个(在我的另一个答案中)。我仍然为 main 和 webworkers 使用单独的 tsconfig.js

      我仍然不满意的是 vue-cli——或者更确切地说是 fork-ts-checker 插件——似乎不知道我的工人类中特定于 webworker 的类型(所以我不能使用 DedicatedWorkerScope ,例如)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-07-25
        • 2010-11-13
        • 1970-01-01
        • 2014-03-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多