【问题标题】:Can't make Vue.js + TypeScript + RequireJS stack work after Vue.js 2.5 upgradeVue.js 2.5 升级后无法使 Vue.js + TypeScript + RequireJS 堆栈工作
【发布时间】:2020-05-14 11:20:21
【问题描述】:

我有一个项目使用 Vue.js 2.4 + TypeScript + RequireJS 堆栈,需要升级到最新的 Vue.js。升级到 Vue.js 会破坏它,在根据文档进行更改后我无法修复此问题。

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Vue.js Scratchpad</title>
  <link rel="icon" type="image/png" href="favicon.png">
  <meta charset="UTF-8">

  <script src="node_modules/requirejs/require.js"></script>
</head>
<body>

<h1>Vue.js Scratchpad</h1>

<div v-show="true" id="app" style="display: none">
  <input v-model="user" autofocus="true" placeholder="Enter any user name">
  <!--<button @click.prevent="onRenderComponent">Render</button>-->

  <p v-show="loading">Loading...</p>
  <router-view v-show="!loading" :user="user"
               @loading="onLoading" @success="onLoaded"
               @loading-error="onLoadingError"></router-view>
</div>

<script>
  requirejs.config({
    // By default load modules from `./`
    baseUrl: '.',

    /*
    By default, modules would be loaded from {module}.js files (filename = module).
    Specify {module} (or "{module}"): "{filepath_no_extension}" mapping if filename != module.
    Specify fall-backs ([filepath1, filepath2]) for minimized / normal version consumption pattern.
    */
    paths: {
      // All third-party libraries must be included here. Use path fall-backs for minimized libs.
      axios: "node_modules/axios/dist/axios.min",
      vue: "node_modules/vue/dist/vue.min",
      "vue-router": "node_modules/vue-router/dist/vue-router.min"
    }
  });

  require(["app-pure-vue.js"]);
</script>
</body>
</html>

app-pure-vue.ts:

import * as Vue from "vue";
import * as VueRouter from "vue-router";
import { AxiosError } from "axios";

//region App components
import MessageComponent from "./messageComponent-pure-vue";
//endregion

Vue.use(VueRouter);

const routes = [
  {
    path: "/",
    component: MessageComponent
  }
];

const router = new VueRouter({
  routes
});

const appOptions = {
  router,
  data() {
    return {
      user: null,
      loading: false
    };
  },
  methods: {
    onLoading() {
      // @ts-ignore
      this.loading = true;
      console.log("Loading...");
    },

    onLoaded() {
      // @ts-ignore
      this.loading = false;
      console.log("Loaded.");
    },

    onLoadingError(error: AxiosError, serviceUrl: string) {
      // @ts-ignore
      this.loading = false;
      console.log("Loading error for", serviceUrl, error.response);
    }
  }
};

new Vue(appOptions).$mount("#app");

messageComponent-pure-vue.ts:

import * as Vue from "vue";
import * as VueRouter from "vue-router";
import { AxiosError } from "axios";

//region App components
import MessageComponent from "./messageComponent-pure-vue";
//endregion

Vue.use(VueRouter);

const routes = [
  {
    path: "/",
    component: MessageComponent
  }
];

const router = new VueRouter({
  routes
});

const appOptions = {
  router,
  data() {
    return {
      user: null,
      loading: false
    };
  },
  methods: {
    onLoading() {
      // @ts-ignore
      this.loading = true;
      console.log("Loading...");
    },

    onLoaded() {
      // @ts-ignore
      this.loading = false;
      console.log("Loaded.");
    },

    onLoadingError(error: AxiosError, serviceUrl: string) {
      // @ts-ignore
      this.loading = false;
      console.log("Loading error for", serviceUrl, error.response);
    }
  }
};

new Vue(appOptions).$mount("#app");

这个例子运行良好。现在,要升级到Vue.js 2.5.0 + 最新的vue-router,以下是所需的文档更改:

  1. package.json更新为"vue": "~2.5.0" + "vue-router": "~3.1.5"
  2. *.ts: import * as Vue from "vue"; => import Vue from "vue";
  3. app-pure-vue.ts: import * as VueRouter from "vue-router"; => import VueRouter from "vue-router";

代码可以编译,但在messageComponent-pure-vue.ts 中的Vue.extend() 上崩溃:

Uncaught TypeError: Cannot read property 'extend' of undefined
    at Object.<anonymous> (messageComponent-pure-vue.ts:5)

你能帮我解决这个问题吗?此处提供了最小可重现示例:https://github.com/DKroot/Scratchpad/tree/master/Client_Side/Vue.js-2.5。工作 2.4 代码位于 https://github.com/DKroot/Scratchpad/tree/master/Client_Side/Vue.js-2.4

到目前为止我在这方面做了什么:

  1. 将问题隔离到 2.5.0 升级
  2. 仔细查看 2.5.0 发行说明 (https://github.com/vuejs/vue/releases/tag/v2.5.0) 和相应的博文 (https://medium.com/the-vue-point/upcoming-typescript-changes-in-vue-2-5-e9bd7e2ecf08)
  3. 审查了 2.5.0 中 TypeScript 声明的更改。出口有点过于复杂,我无法弄清楚根本原因可能是什么。

【问题讨论】:

  • Previously, we already recommend using ES-style imports (import Vue from ‘vue’) everywhere with “allowSyntheticDefaultImports”: true in tsconfig.json. The new typings will officially move to ES-style import/export syntax, so that config is no longer necessary, and users are required to use ES-style imports in all cases. 所以你需要使用import Vue from 'vue',因为这是官方的ES风格的导入语法
  • @Ohgodwhy 这正是我对 2.5.0 所做的:从“vue”导入 Vue;从“vue-router”导入 VueRouter;这允许代码编译。但它在运行时会爆炸。
  • 你安装的是什么版本的vue-template-compiler
  • @Ohgodwhy 没有。这个最小示例中的所有依赖项都在package.json 中列出。
  • 您能否验证您的lock file 中的vue-template-compiler 版本是否与安装的Vue 版本相匹配?

标签: typescript vue.js requirejs vue-router


【解决方案1】:

TL;DR:

试试这个:

import Vue from "vue";
import VueRouter from "vue-router";

说明:

当一个模块这样写时:

// module.ts
export function x() { ... }
export function y() { ... }

你会像这样导入它:

import * as Module from "./module";

但是当它是这样写的时候:

// module-with-default.ts
export default class Module {
  public static function x() { ... }
  public static function y() { ... }
}

你会像这样导入它:

import Module from "./module-with-default";

在这两种情况下,您都可以像这样使用它:

Module.x();
Module.y();

Vue.js 的变化:

这是how it is exported in v2.5.0

export default Vue

这是is how it is exported in v2.4.0

export = Vue;

【讨论】:

  • 如果您提供解释为什么这是首选解决方案并解释它是如何工作的,它会更有帮助。我们想要教育,而不仅仅是提供代码。
  • 太棒了!谢谢!这个网站很特别,是我们可以有所作为的地方。这是关于向前支付的!
  • @DoronG 这正是我在github.com/DKroot/Scratchpad/tree/master/Client_Side/Vue.js-2.5 中所做的,但它失败了:参见原始问题中的#2 和#3。
  • @DoronG 我意识到我可能应该发布非工作 2.5 版本而不是工作 2.4 + 更改。让我知道你想如何处理这个问题。我可以澄清这个问题,或者就这样吧。我只是想最终得到修复。
  • @DKroot 我会看看你的回购
【解决方案2】:

TL;博士

将这些添加到项目中:

// vue-loader.ts
import * as AllVue from "Vue";
import Defaults from "Vue";

export const Vue = AllVue as unknown as Defaults;
export default Vue;
// vue-router-loader.ts
import * as AllVueRouter from "Vue-router";
import Defaults from "Vue-router";

export const Vue = AllVueRouter as unknown as Defaults;
export default Vue;

像这样配置RequireJS:

paths: {
  Vue: "node_modules/vue/dist/vue.min",
  vue: "vue-loader",
  "Vue-router": "node_modules/vue-router/dist/vue-router.min",
  "vue-router": "vue-router-loader"
}

说明

问题是由 TypeScript 如何为具有默认导出的 amd 模块生成 JavaScript 以及 RequireJS 如何加载这些默认导出引起的。

当你这样做时:

import Vue from "vue";

Vue.extend(...);

TypeScript 将其翻译成:

define(["require", "exports", "vue"], function (require, exports, vue_1) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    vue_1.default.extend(..);
});

注意.default 部分?问题是 RequireJS 将vue_1 设置为实际的default,所以vue_1.default 最终成为undefined(因此您的原始错误)

当您使用上述加载程序时,您正在重新导出*,它实际上是默认导出,既是非默认导出又是默认导出。这是为vue-loader.ts 生成的代码:

define(["require", "exports", "Vue"], function (require, exports, AllVue) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.Vue = AllVue; // <--- this resolves your problem
    exports.default = exports.Vue;
});

为了使用我们的加载器,我们正在“重新路由”分辨率。因此,如果真正的代码尝试要求 vue,它将转到我们的加载器,该加载器将加载 Vue,这是 真正的 Vue 代码。

推荐

我想避免此问题的最佳方法是使用 RequireJS - 如果您不需要 IE 支持,您可以使用 native module loading 或使用网页包

另一种可能的解决方案

看这里:Missing default export when using SystemJS or RequireJS makes Vue unusable with TypeScript

【讨论】:

  • 这真是太棒了!你对 TypeScript 技能的帮助很大,远远超出了我的水平。我们确实需要 IE 11(至少)并且做了 Webpack 原型,但决定不使用它,因为对于使用 RequireJS 的现有代码库来说,附加值不够高。
  • 一个小补充:这个解决方案在 Mac 和 Windows 上完美运行。在 Linux 上必须添加 TypeScript 编译器路径,否则 tsc 无法解析来自“Vue”的导入,因为文件系统区分大小写。修复:tsconfig.json: ``` "compilerOptions": { // 解析非相对模块名称的基本目录。如果paths 是,则必须指定。 "baseUrl": "node_modules", "paths": { // 映射是相对于baseUrl "Vue": ["vue"], "Vue-router": ["vue-router"] } } ```
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-09-16
  • 2016-11-21
  • 2017-08-06
  • 1970-01-01
  • 1970-01-01
  • 2022-12-13
  • 1970-01-01
相关资源
最近更新 更多