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" }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.