I want to use mediainfo.js in React.js without 'eject' command

不羁的心 提交于 2021-02-07 16:40:59

问题


I'm trying to use mediainfo.js with React.js.

Looking at the usage example in React.js of the above site, it is set by "webpack.config.js". However, when I build the environment using create-react-app, "webpack.config.js" is wrapped and it seems that it can not be edited without executing the 'eject' command.

If you use npm package "react-app-rewired", you can edit it without executing the reject command, so I tried it.

// config-override.js (overwrites webpack.config.js)

const { resolve } = require('path');
const CopyPlugin = require('copy-webpack-plugin');

const wasmFile = resolve(
  __dirname,
  'node_modules',
  'mediainfo.js',
  'dist'
);
const dist = resolve('build', 'static', 'js');

// template from https://www.npmjs.com/package/react-app-rewired
module.exports = {
  webpack: function(config, env) {
    config.plugins.push(new CopyPlugin({
      patterns: [
        { from: wasmFile, to: dist },
      ],
    }),)
    return config;
  },
}

Here is the whole project.

https://github.com/ottonove/test_mediainfo

The one generated by "npm run build" works normally, but when I run it by "npm run start", the following error occurs.

Uncaught (in promise) RuntimeError: abort(CompileError: WebAssembly.instantiate(): expected magic word 00 61 73 6d, found 3c 21 44 4f @+0). Build with -s ASSERTIONS=1 for more info.

The following warning is also issued.

wasm streaming compile failed: TypeError: Failed to execute 'compile' on 'WebAssembly': Incorrect response MIME type. Expected 'application/wasm'.

How do I set it to work with "npm run start"?

I would appreciate it if you could teach me.


回答1:


I solved it using "WebWorker".

// config-override.js

const path = require("path");
const { resolve } = require('path');
const CopyPlugin = require('copy-webpack-plugin');

const devMode = process.env.NODE_ENV !== 'production'

const wasmFile = resolve(
  __dirname,
  'node_modules',
  'mediainfo.js',
  'dist',
  'MediaInfoModule.wasm'
);

const dist = devMode ? '.' : resolve(__dirname);

module.exports = function override(config, env) {
  config.module.rules.push({
    test: /\.worker\.js$/,
    use: { loader: "workerize-loader" },
  });

  const wasmExtensionRegExp = /\.wasm$/;

  config.resolve.extensions.push(".wasm");

  config.module.rules.forEach((rule) => {
    (rule.oneOf || []).forEach((oneOf) => {
      if (oneOf.loader && oneOf.loader.indexOf("file-loader") >= 0) {
        // Make file-loader ignore WASM files
        oneOf.exclude.push(wasmExtensionRegExp);
      }
    });
  });

  config.module.rules.push({
    test: wasmExtensionRegExp,
    include: path.resolve(__dirname, "src"),
    use: [{ loader: require.resolve("wasm-loader"), options: {} }],
  });

  config.plugins.push(new CopyPlugin({
    patterns: [
      { from: wasmFile, to: dist },
    ],
  }),)

  return config;
};

The whole project is here.

https://github.com/ottonove/test_mediainfo/blob/webworker/config-overrides.js




回答2:


If you are using Create React App (CRA), which now creates a serviceWorker by default, or if you have a service worker for some other reason, one way to fix this is to cache the file, detect the requested route, and return the cached file directly.

Note that this will work regardless of react-router, since the service worker intercepts the request before it even gets to your app. There is no need to modify any routing in your app. Heck, if I didn't have a service worker, I'd probably still solve it this way!

First, copy the WASM file to the /public folder, from the top level of your project:

cp ./node_modules/mediainfo.js/dist/MediaInfoModule.wasm ./public

Then modify your service worker script's 'fetch' function with something like what follows (if you have an existing 'fetch' listener, you'll need to modify the logic in a similar manner:

const mediaInfoModuleWasm = './MediaInfoModule.wasm'

self.addEventListener('install', event => {
  event.waitUntil(caches.open('v1').then(cache => cache.addAll([ mediaInfoModuleWasm ])))
})

self.addEventListener('fetch', event => {
  const route = event.request.url.replace(/^[a-z]{4,5}:\/{2}[a-z]{1,}:[0-9]{1,4}.(.*)/, '$1')
  if (route === 'static/js/MediaInfoModule.wasm') {
    event.respondWith(caches.match(mediaInfoModuleWasm))
  }
})

This first loads the MediaInfoModule.wasm file you put into /public by listening for the service worker's 'install' event and using the standard StorageCache patterns to cache a local file. The we listen for the 'fetch' event, strip off the protocol, domain and port (since you may run this service in several locations) and then return the cached file.

What's the static/js/MediaInfoModule.wasm URL we check for? That's the URL that is requested by the mediainfo.js factory. The best way to double check if that's the same for your setup is to look in the Network tab of your debugger to see the requested URL for MediaInfoModule.wasm.



来源:https://stackoverflow.com/questions/64763189/i-want-to-use-mediainfo-js-in-react-js-without-eject-command

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!