从流中动态导入Javascript模块

时间:2018-10-19 15:37:27

标签: javascript reactjs firebase webpack react-loadable

目标:为了支持基于某些安全性或定义的用户角色要求而动态加载Javascript模块,因此即使在开发工具中标识了模块名称,也无法通过控制台成功导入。

enter image description here

enter image description here

enter image description here

可以轻松地将Javascript模块上传到Firebase(#AskFirebase)之类的云存储服务,并可以根据自定义声明或类似测试的使用Firebase Cloud Function firebase.functions().httpsCallable("ghost");有条件地检索代码。

export const ghost = functions.https.onCall(async (data, context) => {
  if (! context.auth.token.restrictedAccess === true) {
    throw new functions.https.HttpsError('failed-precondition', 'The function must be called while authenticated.');
  }

  const storage = new Storage();
  const bucketName = 'bucket-name.appspot.com';
  const srcFilename = 'RestrictedChunk.chunk.js';

  // Downloads the file
  const response = await storage
    .bucket(bucketName)
    .file(srcFilename).download();
  const code = String.fromCharCode.apply(String, response[0])

  return {source: code};

})

最后,我要做的是获取一个Webpack的React组件,将其放在云中,在服务器端进行安全检查后有条件地将其下载到客户端,然后import()放入用户的客户端环境并进行渲染。

将Javascript存储在云中并有条件地下载到客户端非常容易。在客户端中有了webpack的代码后,就可以像使用Function(downloadedRestrictedComponent)一样使用import('./RestrictedComponent')将其添加到用户环境中,但是我不知道该如何获取从组件默认导出,这样我就可以实际渲染东西了。

import(pathToComponent)返回已加载的模块,据我所知,没有选择传递import()字符串或流,仅传递模块的路径。 Function(downloadedComponent)会将下载的代码添加到客户端环境中,但我不知道如何访问模块的导出以呈现动态加载的React组件。

有什么方法可以从下载的流中动态导入Javascript模块吗?

编辑添加:感谢您的答复。不熟悉Blob和URL.createObjectURL的细微差别。知道为什么会找不到吗?

const ghost = firebase.functions().httpsCallable("ghost");

const LoadableRestricted = Loadable({
  //  loader: () => import(/* webpackChunkName: "Restricted" */ "./Restricted"),
  loader: async () => {
    const ghostContents = await ghost();
    console.log(ghostContents);
    const myBlob = new Blob([ghostContents.data.source], {
      type: "application/javascript"
    });
    console.log(myBlob);
    const myURL = URL.createObjectURL(myBlob);
    console.log(myURL);
    return import(myURL);
  },
  render(loaded, props) {
    console.log(loaded);
    let Component = loaded.Restricted;
    return <Component {...props} />;
  },
  loading: Loading,
  delay: 2000
});

enter image description here

2 个答案:

答案 0 :(得分:1)

将模块文件/流的内容读入BLOB。使用URL.createObjectURL()创建指向BLOB的动态URL。现在按照上面的建议使用import

import(myBlobURL).then(module=>{/*doSomethingWithModule*/});

答案 1 :(得分:0)

您可以尝试使用React.lazy

import React, {lazy, Suspense} from 'react';

const Example = () => {

    const [userAuthenticated, setUserAuthenticated] = useState(true);

    if (userAthenticated) {

        const RestrictedComponent = lazy(() => import('./RestrictedComponent'));

        return (
            <div>
                <Suspense fallback={<div><p>Loading...</p></div>}>
                    <RestrictedComponent />
                </Suspense>
            </div>
        )
    }

    return (
        <div>
            <h1>404</h1>
            <p>Restricted</p>
        </div>
    );

}

export default Example;