DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • An Introduction to Object Mutation in JavaScript
  • Metaprogramming With Proxies and Reflect in JavaScript
  • Unleashing the Power of WebAssembly to Herald a New Era in Web Development
  • A Comprehensive Guide To Working With JSON in JavaScript

Trending

  • The Role of AI in Identity and Access Management for Organizations
  • Exploring Intercooler.js: Simplify AJAX With HTML Attributes
  • Advancing Robot Vision and Control
  • Traditional Testing and RAGAS: A Hybrid Strategy for Evaluating AI Chatbots
  1. DZone
  2. Coding
  3. JavaScript
  4. The Import Statement With an Emscripten-Generated WebAssembly Module in Vue.js

The Import Statement With an Emscripten-Generated WebAssembly Module in Vue.js

By 
Gerard Gallant user avatar
Gerard Gallant
·
Updated Sep. 08, 20 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
17.7K Views

Join the DZone community and get the full member experience.

Join For Free

In my liveBook for WebAssembly in Action, I was recently asked how to use an Emscripten-generated module in Vue.js. In the book, I showed examples using standard JavaScript but didn’t dig into JavaScript frameworks, so I thought this would be an interesting question to look into, especially because I’ve never used Vue.js before.

This article will walk you through the solution that I found. The first thing that’s needed is a WebAssembly module.

The WebAssembly Module

The module is kept simple with just an Add function that accepts two integer values, sums them, and returns the result. The following snippet shows the C code that’s saved to a file called add.c.

C
 




x


 
1
#include <stdlib.h>
2
#include <emscripten.h>
3
 
          
4
EMSCRIPTEN_KEEPALIVE
5
int Add(int value1, int value2)
6
{
7
  return (value1 + value2);
8
}



With the C code created, the next step is to compile it into a WebAssembly module.

Generating the WebAssembly Module

To import the generated module into Vue.js, we want to use an import statement similar to the following:

JavaScript
 




xxxxxxxxxx
1


 
1
import Module from './TestImport';



By default, the Emscripten-generated JavaScript is not configured to be imported in this fashion. To tell Emscripten to create the JavaScript so that it can be imported using the import statement, you need to include the -s EXPORT_ES6=1 and -s MODULARIZE=1 flags.

The MODULARIZE flag will wrap the generated JavaScript code’s Module object in a function. Ordinarily, just including the JavaScript file in a webpage triggers the automatic download and instantiation of the module. When using this flag, however, you’ll need to create an instance of the Module object to trigger the download and instantiation.

The EXPORT_ES6 flag will include the necessary export object expected by the import statement.

You may also like: WebAssembly vs. JavaScript.

As we tried to import the module, however, we received an error from Vue.js about the var _scriptDir = import.meta.url; line of code in the generated JavaScript file. To get around this error, we included the -s USE_ES6_IMPORT_META=0 flag to tell Emscripten to use the older form of the import.meta.url line of code for systems that don’t recognize that code.

Bringing it all together, the following command creates a module that can be imported into Vue.js using an import statement:

Shell
 




xxxxxxxxxx
1


 
1
emcc add.c -s EXTRA_EXPORTED_RUNTIME_METHODS=['ccall'] -s EXPORT_ES6=1 -s MODULARIZE=1 -s USE_ES6_IMPORT_META=0 -o TestImport.js



Now that you have a WebAssembly module, the next step is to import it into your Vue.js application.

The Vue.js Application

The following are two approaches that I came up with for loading a module in Vue.js:

  1. Create an object on the Vue instance that is null by default. When the first component that needs the module is loaded, it creates the module instance and all components created afterward have access to the module.
  2. The other approach is to have a local object in the component, where only that component has access to the module.

The first thing that you need to do is copy the TestImport.js and TestImport.wasm files to your Vue.js solution. I placed them in the src folder.

The first approach that I’ll show you is where the module instance is placed on the Vue instance.

1. Module Instance Placed on the Vue Instance

In the main.js file, create a variable on the Vue object called $myModule so that the module is only downloaded and initialized once. The object is null until instantiated. 

The following snippet shows the change in the main.js file:

JavaScript
 




xxxxxxxxxx
1


 
1
import Vue from 'vue';
2
import App from './App.vue';
3
 
          
4
Vue.config.productionTip = true;
5
Vue.prototype.$myModule = null; // Will hold the module's instance
6
 
          
7
new Vue({
8
render: h => h(App)
9
}).$mount('#app');



The next area that needs to be adjusted is the component.

Adjust the Component

Adjust the Home.vue component with a template that has a button that will call the callAdd function. The result of the function call will be placed below the button, as shown in the following snippet:

HTML
 




xxxxxxxxxx
1


 
1
<template>
2
  <div>
3
    <button @click="callAdd">Add</button>
4
    <p>Result: {{ result }}</p>
5
  </div>
6
</template>



Within the Script tag, include the import statement for the module, as shown in the following snippet:

JavaScript
 




xxxxxxxxxx
1


 
1
import Module from '../TestImport';



Because the module was created using the -s MODULARIZE=1 flag, the module isn’t downloaded and instantiated until you create an instance of the Module object.

In the component’s export object, create a beforeCreate hook that checks to see if the $myModule object has been created yet. If not, create a new instance of the Module object. The download and instantiation of the module is asynchronous, so a Promise is returned. When the Promise resolves, the module instance is assigned to the $myModule object as shown in the following snippet:  

JavaScript
 




xxxxxxxxxx
1


 
1
beforeCreate() {
2
  if (this.$myModule === null) {
3
    new Module().then(myModule => {
4
      this.$myModule = myModule;
5
    });
6
  }
7
}



The callAdd function calls into the module using Emscripten’s ccall helper function, as shown in the following snippet:

JavaScript
 




xxxxxxxxxx
1


 
1
callAdd() {
2
  this.result = this.$myModule.ccall('Add',
3
      'number',
4
      ['number', 'number'],
5
      [2, 3]);
6
}



Putting it all together, the following is the content of the Home.vue file:

HTML
 




x


 
1
<template>
2
  <div>
3
    <button @click="callAdd">Add</button>
4
    <p>Result: {{ result }}</p>
5
  </div>
6
</template>
7
 
          
8
<script>
9
  import Module from '../TestImport';
10
  export default {
11
    beforeCreate() {
12
      if (this.$myModule === null) {
13
        new Module().then(myModule => {
14
          this.$myModule = myModule;
15
        });
16
      }
17
    },
18
    data() {
19
      return {
20
        result: null
21
      }
22
    },
23
    methods: {
24
      callAdd() {
25
        this.result = this.$myModule.ccall('Add',
26
            'number',
27
            ['number', 'number'],
28
            [2, 3]);
29
      }
30
    }
31
  };
32
</script>
33
 
          
34
<style scoped>
35
</style>


 

The source code for the example above can be found here: VuejsGlobalInstance

If you don’t want the module available to all components, the following is how you can use a local variable to hold the module instance instead.

2. Using a Local Module Instance in Your Component

In this case, no changes are needed to your main.js file.

In the Home.vue file, create a variable called moduleInstance after the import statement, as shown in the following snippet:

JavaScript
 




xxxxxxxxxx
1


 
1
import Module from '../TestImport';
2
let moduleInstance = null;



In the beforeCreate hook, you don’t need to check to see if the object exists yet. All you need to do is create an instance of the Module object and, when the Promise resolves, assign the module instance to the moduleInstance variable, as shown in the following snippet:

JavaScript
 




xxxxxxxxxx
1


 
1
beforeCreate() {
2
  new Module().then(myModule => {
3
    moduleInstance = myModule;
4
  });
5
}



The callAdd function calls into the module using Emscripten’s ccall helper function and the moduleInstance object, as shown in the following snippet:  

JavaScript
 




xxxxxxxxxx
1


 
1
callAdd() {
2
  this.result = moduleInstance.ccall('Add',
3
      'number',
4
      ['number', 'number'],
5
      [2, 3]);
6
}



Putting it all together, the following is the content of the Home.vue file:

HTML
 




x


 
1
<template>
2
  <div>
3
    <button @click="callAdd">Add</button>
4
    <p>Result: {{ result }}</p>
5
  </div>
6
</template>
7
 
          
8
<script>
9
  import Module from '../TestImport';
10
  let moduleInstance = null;
11
  export default {
12
    beforeCreate() {
13
      new Module().then(myModule => {
14
        moduleInstance = myModule;
15
      });
16
    },
17
    data() {
18
      return {
19
        result: null
20
      }
21
    },
22
    methods: {
23
      callAdd() {
24
        this.result = moduleInstance.ccall('Add',
25
            'number',
26
            ['number', 'number'],
27
            [2, 3]);
28
      }
29
    }
30
  };
31
</script>
32
 
          
33
<style scoped>
34
</style>



The source code for the example above can be found here: VuejsLocalInstance

When I was trying to run this on my machine, I was getting a content-type error in the console window of my browser’s developer tools. For some reason, my Vue.js dev server isn’t using the proper media type for a WebAssembly module. It should be application/wasm.

The developer I was helping didn’t have an issue with this so it’s probably a configuration issue with my computer (Windows with Visual Studio as the IDE). I’ve included this just in case anyone else runs into this issue. 

To get around this issue, I needed to modify the vue.config.js file by adding the following:

JavaScript
 




xxxxxxxxxx
1
24


 
1
const path = require('path');
2
const contentBase = path.resolve(__dirname, '..', '..');
3
 
          
4
module.exports = {
5
  configureWebpack: config => {
6
    config.devServer = {
7
      before(app) {
8
        // use proper mime-type for wasm files
9
        app.get('*.wasm', function (req, res, next) {
10
          var options = {
11
            root: contentBase,
12
            dotfiles: 'deny',
13
            headers: {
14
              'Content-Type': 'application/wasm'
15
            }
16
          };
17
          res.sendFile(req.url, options, function (err) {
18
            if (err) { next(err); }
19
          });
20
        });
21
      }
22
    }
23
  }
24
}



Summary

In this article, you saw that it’s possible to load an Emscripten-generated WebAssembly module using the import statement if you use the -s EXPORT_ES6=1 and -s MODULARIZE=1 flags when creating the module.

If the tool you’re using has an issue with the import.meta.url line, you can tell Emscripten to use a different set of code for that line by including the -s USE_ES6_IMPORT_META=0 flag when creating the module.

When using the -s MODULARIZE=1 flag, importing the Emscripten-generated JavaScript file won’t automatically download and instantiate the module. Instead, you need to create an instance of the Module object. The download and instantiation is asynchronous, so you need to either wait for the Promise to resolve, as was done in this article, or implement a callback function for the onRuntimeInitialized Emscripten function.

In Vue.js, you can add an object to the Vue instance by adding it to the prototype. When adding something to the prototype, it will be available to all components.

If you don’t want your module available to all components, you can place the instance in a variable local to the component.

For this article, Emscripten 1.39.5 was used to create the WebAssembly module. Visual Studio 2019 was used to create the Vue.js application with the following devDependencies:    

JavaScript
 




xxxxxxxxxx
1


 
1
"@vue/cli-plugin-babel": "3.0.4",
2
"@vue/cli-plugin-eslint": "3.0.4",
3
"@vue/cli-service": "3.0.4",
4
"eslint": "5.6.0",
5
"eslint-plugin-vue": "4.7.1",
6
"vue-template-compiler": "2.5.17"



Disclaimer: I was not paid to write this article, but I am paid royalties on the sale of the book “WebAssembly in Action,” which I mentioned in this article.


Further Reading

  • 7 Vue.js Backends Compared.
Vue.js WebAssembly JavaScript Object (computer science) Snippet (programming)

Opinions expressed by DZone contributors are their own.

Related

  • An Introduction to Object Mutation in JavaScript
  • Metaprogramming With Proxies and Reflect in JavaScript
  • Unleashing the Power of WebAssembly to Herald a New Era in Web Development
  • A Comprehensive Guide To Working With JSON in JavaScript

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!