Vue.js 2.5 升级后无法使 Vue.js + TypeScript + RequireJS 堆栈工作

Can't make Vue.js + TypeScript + RequireJS stack work after Vue.js 2.5 upgrade

我有一个使用 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-纯-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. The working 2.4 code is at 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) and the corresponding blog post (https://medium.com/the-vue-point/upcoming-typescript-changes-in-vue-2-5-e9bd7e2ecf08)
  3. 查看了 2.5.0 中 TypeScript 声明的更改。导出有点太复杂,我无法找出根本原因。

长话短说:

试试这个:

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;

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;
});

为了使用我们的装载机,我们 "re-routing" 下了决心。因此,如果真实代码尝试要求 vue,它将转到我们的加载程序,它将加载 Vue,这是 真实 Vue 代码。

推荐

我想避免这个问题的最好方法是 而不是 使用 RequireJS - 如果你不需要 IE 支持,你可以只做 native module loading 或使用 Webpack

另一种可能的解决方案

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