7

I have a Single File Component and I need to import Vue:

<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'
...
@Component({...})
export class Comp extends Vue {...}

I'm importing vue.js as module in the browser:

<script type="module" src="module/vue/vue.js"></script>

but I get the error

Uncaught TypeError: Class extends value undefined is not a constructor or null

somewhere inside of an eval() which vue-cli-service --target lib generates. I'm loading the SFC as a module, too:

<script type="module" src="module/comp/comp.js"></script>

As a workaround, I tried to add the imports at the top of comp.js:

import Vue from '/module/vue/vue.js'
import Component from '/module/vue-class-component/vue-class-component.js'
(function webpackUniversalModuleDefinition(root, factory) {
...

but that doesn't help.

How can I rewire the imports to the paths that will be used on the web server? I checked the options of webpack but only found resolve which seems to help webpack find stuff while it packages. I need an "output rewrite" kind of option.

Note that I'm using ES modules, not CommonJS or RequireJS. tsconfig.json:

compilerOptions: {"target": "ES2016", module: "ES2015" } 

Update I tried to put the final path into the .vue file and use configureWebpack.resolve.alias in vue.config.js to allow the Vue compiler to locate the module but that also doesn't work.

vue.config.js:

module.exports = {
    runtimeCompiler: true,
    configureWebpack: {
        externals: [
            'module/vue/vue',
            'module/vue-class-component/vue-class-component',
        ],
        resolve: {
            alias: {
                'module/vue/vue': 'vue/dist/vue.esm.js',
                'module/vue-class-component/vue-class-component': 'vue-class-component/dist/vue-class-component.esm.js'
            }
        },
    }
}

comp.vue:

...
import Vue from './module/vue/vue'
import Component from 'module/vue-class-component/vue-class-component'

just gives

ERROR in .../src/comp.vue
16:17 Cannot find module 'module/vue/vue'.
4
  • Instead of "vue-class-component", can you give a try with vue property decorator. import { Component, Vue } from 'vue-property-decorator'; Commented Sep 19, 2019 at 8:01
  • @AmithaMahesh Thanks. That also doesn't work. The vue-class-component import isn't necessary to demonstrate the problem, I just kept it to show that Vue isn't the only import and the answer should work for other things as well. Commented Sep 19, 2019 at 8:09
  • It can't find the path because the external is setup incorrectly. It needs to point the global variable not the path. Commented Sep 25, 2019 at 15:12
  • My impression (I can be wrong) is when you use ES2015 modules, there is no global variable. Hence the error. Commented Sep 27, 2019 at 8:20

1 Answer 1

4
+100

Overview

You can add configureWebpack in the vue.config.js to modify the Webpack configuration and in Webpack you can solve this issue using externals.

Handling Externals

Externals provide a way for you to import external libraries.

In your webpack configuration you will add a line that looks something like this.

let's take lodash for example as the import and the global variable are different.

// webpack.config
externals: {
  'lodash': '_'
}

_ is the global variable webpack can grab and lodash is the import it exposes.

At this point, you can do something like this.

import * as lodash from 'lodash'

Handling TypeChecking

To handle the type checking with this method you npm install @types/... and TypeScript will pick up the types for that import.

Modifying Webpack in vue.config.js

You can read about this here but simply add the following to the vue.config.js file.

// vue.config.js
module.exports = {
  configureWebpack: {
    externals: {
      'lodash': '_'
    }
  }
}
Sign up to request clarification or add additional context in comments.

5 Comments

How does that map localhost/module/lodash/lodash.js to the import statement in the browser?
If you look at the compiled result you get this module.exports = _; for 'lodash' so it is expecting '_' to already exist. This simply exposes the global variable to webpack. In the case of Vue.js just like lodash it creates a global variable Vue which you can map in webpak. externals: { 'Vue' }. The Browser just runs Vue.js and creates the Vue global variable and webpack can reference it with whatever you specify. Using type="module" in your script or not the result is the same.
Long story short man this worked for me in my testbed. The only thing I might have done differently was I used a CDN for Vue.js <script type="module" src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
Ah... I'm trying to use ES modules, not the old requirejs/commonjs stuff. So in the generated code, I see import Vue from "vue" which works for unit tests (npm/jest will find the stuff) but not in the browser (= ignored global symbols). And when I use the relative path which should work in the browser, tsc&jest break.
tsconfig.json: compilerOptions: {"target": "ES2016", module: "ES2015" }

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.