【问题标题】:How can I share a library between my Vue CLI application and existing vanilla JavaScript?如何在我的 Vue CLI 应用程序和现有的 vanilla JavaScript 之间共享库?
【发布时间】:2020-08-12 12:46:18
【问题描述】:

我正在开发一个 WordPress 网站,该网站在多个页面上使用 Swiper 库作为 npm 依赖项。该网站包含一个嵌入式 Vue CLI 4 应用程序,我还想将 Swiper 库添加到该应用程序中。目前,该网站有自己的 webpack 配置,而应用程序使用其默认构建配置的略微修改版本。

可以使用一些 Vue Swiper 组件,但由于我已经在使用原始库,我想避免重复。我认为我需要以某种方式组合我的两个构建过程(除非有其他解决方案),但我之前没有尝试过类似的事情。

主要的webpack配置如下:

const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = (env, options) => {
    return {
        entry: {
            main: ["./src/js/index.js", "./src/scss/index.scss"],
            admin: ["./src/scss/admin.scss", "./src/js/admin/index.js"]
        },
        output: {
            path: path.resolve(__dirname, "dist"),
            filename: "[name].js",
            publicPath: "/dist"
        },
        module: {
            rules: [
                {
                    test: /\.js$/,
                    use: {
                        loader: "babel-loader",
                        options: { presets: ["@babel/preset-env"] }
                    }
                },
                {
                    test: /\.scss$/,
                    use: [
                        MiniCssExtractPlugin.loader,
                        "css-loader",
                        "sass-loader"
                    ]
                },
                {
                    test: /\.svg$/,
                    use: {
                        loader: 'file-loader',
                        options: {
                            name: '[name].[ext]',
                            outputPath: '/images',
                            publicPath(url) {
                                return `/wp-content/themes/themename/dist/images/${url}`
                            },
                        },
                    }
                },
            ]
        },
        plugins: [
            new MiniCssExtractPlugin({
                filename: "[name].css"
            })
        ]
    }
};

以及目录结构:

├── app
|   ├── src
|   ├── dist
|   ├── ...
|   └── vue.config.js
├── src
├── dist
├── ...
└── webpack.config.js

理想情况下,我希望保持目录原样,那么我如何能够在两个项目中使用 Swiper,而不会在两个包中重复它?

【问题讨论】:

  • 那么你是说你在很多页面上都有 swiper 库,但只在一个页面上使用 Vue CLI 4 应用程序?因此,在您的标题中,您包含了包含 swiper 库的脚本,但是当加载特定页面时,您就会有一个包含 vue 应用程序的脚本?
  • @Hides 你的第一个问题是肯定的——这完全正确。然而,目前,Swiper 是通过index.js 导入的,并将 webpack 捆绑到一个文件中。 Vue 应用程序(当前)位于一个页面上。
  • 抱歉,您是说 index.js 正在被导入网站,但 vue 应用程序位于另一个文件中,该文件仅在该单个页面上导入?
  • 是的,没错。如果您查看上面的目录结构 - app 目录包含主页上包含的所有 Vue 逻辑。 Vue 应用程序有自己的构建过程,与外部文件分开。
  • 您是否希望避免将 index 和 vue 应用程序捆绑到一个文件中并在每个页面上调用它?要组合这些过程,您可以编辑您的包脚本以首先构建 vue 应用程序,然后在上层目录中构建您的 index.js 文件。类似 `scripts: { 'build': 'cd app && npm run build && cd ../ && npm run build'}

标签: javascript vue.js webpack vue-cli


【解决方案1】:

只需通过 NPM 添加所有依赖项以包含在构建供应商包中。

然后你可以use vanilla JS like referenced here:

// Either Vanilla JavaScript
window.addEventListener('load', function() {
  console.debug('All assets are loaded');
})
// or jQuery
$(document).ready(function() {
  // After App class declared in global namespace
  App.init();
});
// Loaded in document head
<script type="text/javascript">
  document.addEventListener("DOMContentLoaded", function(event) {
    // After App class declared in global namespace
    App.init();
  }); 
</script>

我建议使用或学习带有通过 webpack 和 NPM 添加的 CLI 插件的 Vue CLI 引导项目模板。然后在 vue.config.js 文件中你可以配置你的 webpack 插件设置。

例如,使用 Webpack Bundle Analyzer 插件通过 npm 脚本命令作为构建运行。使用 -o 参数,它会在块图上打开供应商脚本包大小的可视化图。

编辑:所以在您的vue.config.js 中,您可以添加单独的入口点:

module.exports = {
  pages: {
    app: {
      index: 'client/src/index.ts',
      template: 'client/public/index.html',
    },
    main: {
      entry: 'client/src/main.ts',
      template: 'client/public/main.html',
    },
  },
};

然后index.html,您可以动态检查是否有某些规则,即本地存储中的活动会话 ID,并基于该英特尔为您的应用执行 init 方法。

然后,当用户浏览到您域中的 /main 路径时,您将拥有直接启动应用程序的 main.ts

【讨论】:

  • 我已经稍微修改了vue.config.js - 我认为这个问题超出了 Vue 构建过程的范围,因为有问题的库存在于外部依赖树中。我是否也认为供应商库作为全局变量暴露给页面上的所有其他脚本?
  • 通过 NPM 安装的供应商包被加载作为您将手动添加为文档头中的脚本 url 的任何脚本资产。如果使用 TypeScript,那么您至少需要为这些范围名称添加接口。另请注意,Vues 默认构建设置会在包中创建另一个文件“供应商”,并为您的每个应用程序入口点创建一个文件。例如,您可以将 main.ts 作为您的主要入口点,然后在您的 public/oidc-callback.html 中有另一个 oidc-callback.ts 和另一个 html 基本文件
  • 另请注意,使用 npm 安装 ie 时,npm install -D 安装为 devDependency(使用生产环境值构建时不包括在内),npm i -S 保存为依赖项(将包含在生产包中) .
【解决方案2】:

如果你希望 Vue 使用已经加载到 window 对象中的库,请在 vue.config.js 中指定它:

module.exports = {
  chainWebpack: config => {
    config.externals({
      swiper: 'Swiper'
    })
  }
}

现在,无论您在哪里使用 import Swiper from 'swiper'(或 import * as Swiper from 'swiper' - 取决于您的 tsconfig 以及您正在使用的 lib 的导出方式),您实际上都会得到任何 window.Swiper 是当前实例化导入的任何内容(可能是应用程序,也可能是延迟加载组件的加载时间)。


就可靠消息来源而言,我认为有关此事的最高当局是:

外部

防止捆绑某些导入的包,而是在运行时检索这些外部依赖项。


还请注意,如果您希望 serve 正常工作,您必须使用 &lt;script&gt;(指向您在生产中加载该库的任何位置)将模块导入 public/index.html,正如您现在告诉 webpack 它是将在窗口对象中找到该依赖项。否则你的build 会正常工作(假设window.Swiper 存在于生产环境中),但serve 不会。


在 typescript 中使用外部:

请注意,如果您希望在您的应用程序中输入${moduleName},如果@types/${moduleName} 存在,您仍需要devDependencies 中的@types/${moduleName}

如果没有,您可以从包中导入类型(在devDependecies 中,仅针对类型,它不会包含在包中)。如果这些选项都不适用于您的特定包,您可能希望在 shims-vue.d.ts 中声明一个模拟模块:

declare module '${moduleName}';

...这将有效地禁用对该模块的打字稿检查。请注意,声明模拟可能是有害的,因为它将覆盖该模块的任何现有类型。所以只有在没有其他方法时才使用它。

显然,在您的情况下,${moduleName}swagger

【讨论】:

    猜你喜欢
    • 2019-08-03
    • 2021-06-26
    • 1970-01-01
    • 2012-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-13
    • 1970-01-01
    相关资源
    最近更新 更多