How to use ffmpeg with Nuxt? - javascript

I am super new to Nuxt, and I am currently trying to move over a vue application that generates gifs ffmpeg.wasm to use Nuxt.js. However, whenever I visit the page the server crashes with the following error:
[fferr] requested a shared WebAssembly.Memory but the returned buffer is not a SharedArrayBuffer, indicating that while the browser has SharedArrayBuffer it does not have WebAssembly threads support - you may need to set a flag 18:36:38
(on node you may need: --experimental-wasm-threads --experimental-wasm-bulk-memory and also use a recent version)
I know it says to add the flags to node, as does the ffmpeg.wasm docs, but how do I do that via Nuxt? Or can I even do that? It is using the default dev server that comes with Nuxt, and I will be able to solve this when it's built and hosted but I need to have it locally as well.
Here is the component I am using in my barebones Vue app (stripped back but still causes an error). I am using node v14.17.6 and I'm using this library github.com/ffmpegwasm/ffmpeg.wasm/blob/master/README.md
<template>
<div class="home">
<h1>FFMPEG test</h1>
</div>
</template>
<script>
import { createFFmpeg } from '#ffmpeg/ffmpeg'
export default {
name: 'Home',
data: function () {
return {
ffmpeg: null,
}
},
created() {
this.ffmpeg = createFFmpeg({
log: true,
})
// Initialise loadFFmpeg
this.loadFFmpeg()
},
methods: {
async loadFFmpeg() {
await this.ffmpeg.load()
},
},
}
</script>

Creating the instance into a mounted() fixed the issue.
This is probably due to the fact that ffmpeg needed the Vue instance to be already mounted in the DOM (in the way it works).
created() is usually used for AJAX calls or things not so related to the Vue instance, and it being shown with the Composition API in their example gave me the idea of trying the mounted() hook.

Related

"Permission denied to access property "__v_isRef" on cross-origin object" error in NuxtJS

I'm trying to embed a Twitch player on a website using Vue 2 with Nuxt. A minimal component would look like this:
<template>
<div>
<div id="twitchPlayer"></div>
</div>
</template>
<script>
export default {
data() {
return {
player: null,
};
},
methods: {
setupPlayer() {
const tempPlayer = new Twitch.Player("twitchPlayer", {
width: "100%",
height: "500px",
video: 1093956452,
});
// works fine
console.log(player);
// throws error
this.player = tempPlayer;
},
},
mounted() {
this.setupPlayer();
},
};
</script>
I imported the Twitch js file in the head element in nuxt.config.js.
The player works fine as long as the scope is within the setupPlayer() method. Once I try to assign the tempPlayer object to this.player (to access it from other methods) I get the following error:
Firefox:
Permission denied to access property "__v_isRef" on cross-origin object
Chrome:
Blocked a frame with origin "http://localhost:8080" from accessing a cross-origin frame.
at isRef (webpack-internal:///./node_modules/vue/dist/vue.common.dev.js:1150:22)
So apparently isRef is being called when assigning this.player = tempPlayer and this causes the error.
However, if I use the same component in a standard Vue app (without Nuxt), it works just fine.
How would one fix this issue? I don't want to have to go back to standard Vue because of something like this.
Thank you.
npm i vue#2.6.14
npm i vue-server-renderer#2.6.14
npm i vue-template-compiler#2.6.14
this one helped me
My reference Vue app, in which I had no issues, had a different version than the Nuxt app. Downgrading Vue (from 2.7.8 to 2.6.14) solved the problem.

Cannot use newly installed plugins (node modules) in Nuxt pages/components

First off, I'm a beginner with NuxtJS and front-end development in general, so it might be that I'm missing something - though I do believe I went through all the options before posting here. Apologies in advance if that is not the case.
I've been having trouble using installed modules that I've registered as plugins. For example, take mapbox-sdk.
After installing it with npm install #mapbox/mapbox-sdk, which correctly creates #mapbox/mapbox-sdk in node_modules, I register it in nuxt.config.js:
plugins: [
...
"~/plugins/mapbox-sdk.js",
],
Of course, I also create the mapbox-sdk.js file in plugins/, containing:
import "#mapbox/mapbox-sdk";
Then, in a page (say, myMap.vue), when I try:
var mapboxClient = mapboxSdk({ accessToken: MY_ACCESS_TOKEN });
which is the basic usage example in the documentation, I get:
mapboxSdk is not defined
in the console. This behavior extends to every single module I installed today, but is not the case for modules I had previously installed.
The reason why you're getting the error mapboxSdk is not defined is because there are a few issues with the way you've set up this plugin.
Docs here https://nuxtjs.org/docs/2.x/directory-structure/plugins/, they have some useful diagrams.
There are a couple of ways you can use this package.
Plugin
// ~/plugins/mapbox-sdk.js
import mapboxSdk from '#mapbox/mapbox-sdk'
export default (_ctx, inject) => {
// Exposing the mapboxSdk to your Nuxt app as $mapBox.
inject('mapBox', mapboxSdk)
}
Then in nuxt.config.js, same as you've already done.
plugins: [
...
"~/plugins/mapbox-sdk.js",
],
Then in your component myMap.vue
var mapboxClient = this.$mapBox({ accessToken: MY_ACCESS_TOKEN });
Directly in the component:
If you don't wish to use a plugin, the way that #kissu mentioned above https://stackoverflow.com/a/67421094/12205549 will also work.
Try adding this after the import to let Vue know that this method exists (in the same .vue file) at first
<script>
import mapboxSdk from '#mapbox/mapbox-sdk'
export default {
methods: {
mapboxSdk,
},
mounted() {
console.log('mapbox function >>', mapboxSdk)
},
}
</script>
Do you have it working in a .vue component at first ?

Store environment value in Vue upon app initialization

In a basic web app built using VueJS a call is made to an API that responds with an object containing the environment name. For example:
https://appsdev/mysimpleapp/api/environment
returns
{"applicationName":"My Simple App","version":"1.0.0.0","environment":"DEV"}
Currently, a nav button is set up to conditionally show if the environment is DEV. Here are the pertinent parts of that page:
<template>
<!-- other content here -->
<div #click="updateMenu()" v-show="['DEV'].includes(environment)"><router-link :to="{name: 'dev-notes'}">Dev Notes</router-link></div>
</template>
<script>
// other logic here
data() {
return {
environment: null
}
},
created() {
this.getEnvironment();
},
methods: {
async getEnvironment() {
const environmentDetails = await this.$service.access('environment').get(); // uses Axios to access API
this.environment = environmentDetails.environment;
}
}
}
</script>
While this approach works, there is a noticeable delay in the "Dev Notes" nav button appearing while the app hits the API. Plus, every time the page loads, there is a call to the API.
Looking to improve upon this, it seems like it might be better to hit the API once, when the app initializes, store that value in a variable somewhere and then refer to that variable for conditionally showing the "Dev Notes" nav button. Vuex seems like overkill for such a simple thing and this app doesn't have the .env files that this post describes so how can this be achieved?
You can access the environment mode info straight from Vue CLI using node's process.env, which is a feature that's available by default. Specifically, process.env.NODE_ENV tells whether the app is running in production or development or some other mode. From the Vue CLI docs:
For example, NODE_ENV will be set to "production" in production mode, "test" in test mode, and defaults to "development" otherwise.
You can test this anywhere in your project, for example:
if (process.env.NODE_ENV === 'production') {
console.log('PRODUCTION');
} else if (process.env.NODE_ENV === 'development') {
console.log('DEVELOPMENT');
} else {
console.log('OTHER');
}

How to use external JavaScript objects in Vue.js methods

I'm trying to get Stripe working with my Vue.js 2 application. For PCI-DSS reasons, Stripe requires that their Javascript is always loaded from js.stripe.com. I've followed the instructions in:
How to add external JS scripts to VueJS Components
How to include a CDN to VueJS CLI without NPM or Webpack?
but I get a 'Stripe' is not defined error when I try to use the library. These solutions seemed to be aimed at merely getting a <script> tag into the output HTML (e.g. for analytics), not actually consuming the functions and objects in that script.
Here's what my component Javascript looks like:
<script>
export default {
name: "PaymentPage",
mounted() {
let stripeScript = document.createElement('script');
stripeScript.setAttribute('src', 'https://js.stripe.com/v3/');
document.head.appendChild(stripeScript);
let s = Stripe('pk_test_Fooo');
console.log(s);
}
}
</script>
I also tried adding the script tag to my public/index.html file instead, but I get the same outcome. This would probably be my preferred route, since Stripe encourages developers to import their script on all pages on the site.
<!DOCTYPE html>
<html lang="en">
<head>
// ...
<script src="https://js.stripe.com/v3/"></script>
</head>
How can I pull a script from an external CDN and use it within my component's Javascript?
I'm aware of some libraries to integrate Vue.js with Stripe (e.g. matfish2/vue-stripe and jofftiquez/vue-stripe-checkout), but the former doesn't import properly for me (I'm hitting issue #24) and the latter is built against the older Stripe API and the new version is still in beta.
You aren't giving the script time to load before checking if Stripe is there. What you need is something like this:
<script>
export default {
name: "PaymentPage",
mounted() {
let stripeScript = document.createElement('script');
stripeScript.setAttribute('src', 'https://js.stripe.com/v3/');
stripeScript.onload = () => {
let s = Stripe('pk_test_Fooo');
console.log(s);
};
document.head.appendChild(stripeScript);
}
}
</script>
Thanks to yuriy636's comment, I realised that errors were only from the linter, which presumably can't statically figure out what I'm up to.
I opted to put the script into index.html, then ensured I squashed linter errors with:
// eslint-disable-next-line no-undef
let s = Stripe('pk_test_Fooo');
In my case, I still had errors calling functions of the specific script. So it was required to specify the ¨window¨ scope. Also, if you need to access any Vue element inside the ¨onload¨function, you need a new variable for the ¨this¨ instance.
<script>
export default {
name: "PaymentPage",
mounted() {
let stripeScript = document.createElement('script');
// new variable for Vue elements.
let self = this;
stripeScript.onload = () => {
// call a script function using 'window' scope.
window.Stripe('pk_test_Fooo');
// call other Vue elements
self.otherVueMethod();
};
stripeScript.setAttribute('src', 'https://js.stripe.com/v3/');
document.head.appendChild(stripeScript);
}
}
I worked with this on Vue 2.6.
Just install the npm package npm install #stripe/stripe-js and use it like a regular import
import { loadStripe } from "#stripe/stripe-js";
export default {
async mounted() {
// init stripe
const stripe = await loadStripe('your_stripe_key_here');
this.stripe = stripe; // store the stripe instance
// access the stripe instance in your methods or where you want to use them
},
}
It's working as of 6th Jan 2022.

How to use an external non vue script in vue

I try to use an external script (https://libs.crefopay.de/3.0/secure-fields.js) which is not vue based
I added the script via -tags into index.html
But when I try to intsanciate an object, like in the excample of the script publisher.
let secureFieldsClientInstance =
new SecureFieldsClient('xxxxx',
this.custNo,
this.paymentRegisteredCallback,
this.initializationCompleteCallback,
configuration)
Vue says "'SecureFieldsClient' is not defined"
If I use this.
let secureFieldsClientInstance =
new this.SecureFieldsClient('xxxxx',
this.custNo,
this.paymentRegisteredCallback,
this.initializationCompleteCallback,
configuration)
secureFieldsClientInstance.registerPayment()
Vue says: Error in v-on handler: "TypeError: this.SecureFieldsClient is not a constructor"
My Code:
methods: {
startPayment () {
this.state = null
if (!this.selected) {
this.state = false
this.msg = 'Bitte Zahlungsweise auswählen.'
} else {
localStorage.payment = this.selected
let configuration = {
url: 'https://sandbox.crefopay.de/secureFields/',
placeholders: {
}
}
let secureFieldsClientInstance =
new SecureFieldsClient('xxxxx',
this.custNo,
this.paymentRegisteredCallback,
this.initializationCompleteCallback,
configuration)
secureFieldsClientInstance.registerPayment()
// this.$router.replace({ name: 'payment' })
}
}
}
Where is my mistake?
EDIT:
Updated the hole question
Here is a minimal Vue app for the context your provided, which works:
https://codepen.io/krukid/pen/voxqPj
Without additional details it's hard to say what your specific problem is, but most probably the library gets loaded after your method executes, so window.SecureFieldsClient is expectedly not yet defined. Or, there is some runtime error that crashes your script and prevents your method from executing. There could be some other more exotic issues, but lacking a broader context I can only speculate.
To ensure your library loads before running any code from it, you should attach an onload listener to your external script:
mounted () {
let crefPayApi = document.createElement('script')
crefPayApi.onload = () => this.startPayment()
crefPayApi.setAttribute('src', 'https://libs.crefopay.de/3.0/secure-fields.js')
document.head.appendChild(crefPayApi)
},
I found the solution.
the import was never the problem.
I had just to ignore VUEs/eslints complaining about the missing "this" via // eslint-disable-next-line and it works.
So external fuctions/opbjects should be called without "this" it seems.
let secureFieldsClientInstance =
new SecureFieldsClient('xxxxx',
this.custNo,
this.paymentRegisteredCallback,
this.initializationCompleteCallback,
configuration)
You could download the script and then use the import directive to load the script via webpack. You probably have something like import Vue from 'vue'; in your project. This just imports vue from your node modules.
It's the exact same thing for other external scripts, just use a relative path. When using Vue-CLI, you can do import i18n from './i18n';, where the src folder would contain a i18n.js
If you really want to use a CDN, you can add it like you normally would and then add it to the externals: https://webpack.js.org/configuration/externals/#externals to make it accessible from within webpack

Categories

Resources