Skip to content

使用远程npm包

npm-federatio是wpmjs配套的webpack插件, 它的参数会透传给wpmjs

TIP

要通过插件使用远程npm包需要遵循module-federation的方式对源码的入口文件进行一步简单改造(详看: bootstrap)

使用远程umd包

js
import remoteReact from "react"
import remoteReactDom from "react-dom"
import remoteReact from "react"
import remoteReactDom from "react-dom"
  • 方式一, 使用jsdelivr作为源
js
// webpack.config.js
const NpmFederation = require("npm-federation")

new NpmFederation({
  // 设置域名
  baseUrl: "https://cdn.jsdelivr.net/npm",
  remotes: {
    // 设置npm包path
    "react-dom": "react-dom/umd/react-dom.development.js",
    "react": "react/umd/react.development.js"
  }
})
// webpack.config.js
const NpmFederation = require("npm-federation")

new NpmFederation({
  // 设置域名
  baseUrl: "https://cdn.jsdelivr.net/npm",
  remotes: {
    // 设置npm包path
    "react-dom": "react-dom/umd/react-dom.development.js",
    "react": "react/umd/react.development.js"
  }
})
  • 方式2, 使用url作为源
js
// webpack.config.js
const NpmFederation = require("npm-federation")

new NpmFederation({
  remotes: {
    "react-dom": "https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.development.js",
    "react": "https://cdn.jsdelivr.net/npm/react/umd/react.development.js"
  }
})
// webpack.config.js
const NpmFederation = require("npm-federation")

new NpmFederation({
  remotes: {
    "react-dom": "https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.development.js",
    "react": "https://cdn.jsdelivr.net/npm/react/umd/react.development.js"
  }
})

使用远程umd多入口包

大型umd模块需要自己暴露多入口进行懒加载, 下面例如我们自己上传一个umd的react模块, 对其useState、useEffect拆分成多个入口(chunk)进行懒加载

remote-react:

js
export const useState = () => import("./useState")
export const useEffect = () => import("./useEffect")
export const useState = () => import("./useState")
export const useEffect = () => import("./useEffect")

host-app:

js
// webpack.config.js
const NpmFederation = require("npm-federation")

new NpmFederation({
  remotes: {
    "react": "https://xx.com/react/dist/index.js",
  }
})
// webpack.config.js
const NpmFederation = require("npm-federation")

new NpmFederation({
  remotes: {
    "react": "https://xx.com/react/dist/index.js",
  }
})
js
import ModuleA from "react/useState" // 加载react.js, react-chunk1.js
import ModuleB from "react/useEffect" // 加载react-chunk2.js
import ModuleA from "react/useState" // 加载react.js, react-chunk1.js
import ModuleB from "react/useEffect" // 加载react-chunk2.js

使用远程system包

js
import remoteReact from "react"
import remoteReactDom from "react-dom"
import remoteReact from "react"
import remoteReactDom from "react-dom"
  • 方式一, 使用jsdelivr作为源
js
// webpack.config.js
const NpmFederation = require("npm-federation")

new NpmFederation({
  // 设置域名
  baseUrl: "https://cdn.jsdelivr.net/npm",
  remotes: {
    // 设置npm包path
    "react-dom": "react-dom/umd/react-dom.development.js",
    "react": "react/umd/react.development.js"
  }
})
// webpack.config.js
const NpmFederation = require("npm-federation")

new NpmFederation({
  // 设置域名
  baseUrl: "https://cdn.jsdelivr.net/npm",
  remotes: {
    // 设置npm包path
    "react-dom": "react-dom/umd/react-dom.development.js",
    "react": "react/umd/react.development.js"
  }
})
  • 方式2, 使用url作为源
js
// webpack.config.js
const NpmFederation = require("npm-federation")

new NpmFederation({
  remotes: {
    "react-dom": "https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.development.js",
    "react": "https://cdn.jsdelivr.net/npm/react/umd/react.development.js"
  }
})
// webpack.config.js
const NpmFederation = require("npm-federation")

new NpmFederation({
  remotes: {
    "react-dom": "https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.development.js",
    "react": "https://cdn.jsdelivr.net/npm/react/umd/react.development.js"
  }
})

使用远程module-federation包

js
import App1 from "mf-app-01/App"
import App2 from "mf-app-02/App"
import App1 from "mf-app-01/App"
import App2 from "mf-app-02/App"
  • 方式一, 使用jsdelivr作为源
js
// webpack.config.js
const NpmFederation = require("npm-federation")

new NpmFederation({
  // 设置域名
  baseUrl: "https://cdn.jsdelivr.net/npm",
  remotes: {
    // 设置npm包path
    "mf-app-01": "mf-app-01/dist/remoteEntry.js",
    "mf-app-02": "mf-app-02/dist/remoteEntry.js",
  },
})
// webpack.config.js
const NpmFederation = require("npm-federation")

new NpmFederation({
  // 设置域名
  baseUrl: "https://cdn.jsdelivr.net/npm",
  remotes: {
    // 设置npm包path
    "mf-app-01": "mf-app-01/dist/remoteEntry.js",
    "mf-app-02": "mf-app-02/dist/remoteEntry.js",
  },
})
  • 方式2, 使用url作为源
js
// webpack.config.js
const NpmFederation = require("npm-federation")

new NpmFederation({
  // 设置域名
  remotes: {
    // 设置npm包path
    "mf-app-01": "https://cdn.jsdelivr.net/npm/mf-app-01/dist/remoteEntry.js",
    "mf-app-02": "https://cdn.jsdelivr.net/npm/mf-app-02/dist/remoteEntry.js",
  },
})
// webpack.config.js
const NpmFederation = require("npm-federation")

new NpmFederation({
  // 设置域名
  remotes: {
    // 设置npm包path
    "mf-app-01": "https://cdn.jsdelivr.net/npm/mf-app-01/dist/remoteEntry.js",
    "mf-app-02": "https://cdn.jsdelivr.net/npm/mf-app-02/dist/remoteEntry.js",
  },
})

使用远程json数据

js
import remoteJson from "remoteJson"
import remoteJson from "remoteJson"
  • 方式一, 使用统一域名
js
// webpack.config.js
const NpmFederation = require("npm-federation")

new NpmFederation({
  // 设置域名
  baseUrl: "https://cdn.jsdelivr.net/npm",
  remotes: {
    // 设置npm包path
    "remoteJson": {
      moduleType: "json",
      packageFilename: "react/package.json"
    },
  },
})
// webpack.config.js
const NpmFederation = require("npm-federation")

new NpmFederation({
  // 设置域名
  baseUrl: "https://cdn.jsdelivr.net/npm",
  remotes: {
    // 设置npm包path
    "remoteJson": {
      moduleType: "json",
      packageFilename: "react/package.json"
    },
  },
})
  • 方式2, 使用url作为源
js
// webpack.config.js
const NpmFederation = require("npm-federation")

new NpmFederation({
  remotes: {
    "remoteJson": {
      moduleType: "json",
      url: "https://cdn.jsdelivr.net/npm/react/package.json",
    }
  },
})
// webpack.config.js
const NpmFederation = require("npm-federation")

new NpmFederation({
  remotes: {
    "remoteJson": {
      moduleType: "json",
      url: "https://cdn.jsdelivr.net/npm/react/package.json",
    }
  },
})

自定义模块源url

如果你并非通过npm而是自行上传资源通过cdn访问, 可以通过重写各模块的加载器, 自定义请求的url的规则

js
// webpack.config.js
const NpmFederation = require("npm-federation")

new NpmFederation({
  initial: `
    console.log("Inject code wpmjsInstance", wpmjs)
    wpmjs.registerLoader({
      moduleType: "system",
      resolveUrl({}) {

      }
    })
    wpmjs.registerLoader({
      moduleType: "mf",
      resolveUrl({}) {
        
      }
    })
    `,
  baseUrl: "https://cdn.jsdelivr.net/npm",
  remotes: {
    "@remix-run/router": "@remix-run/router/dist/router.umd.min.js",
    "mf-app-02": {
      package: "mf-app-02/dist/remoteEntry.js",
      global: "mfapp02"
    },
  },
})
// webpack.config.js
const NpmFederation = require("npm-federation")

new NpmFederation({
  initial: `
    console.log("Inject code wpmjsInstance", wpmjs)
    wpmjs.registerLoader({
      moduleType: "system",
      resolveUrl({}) {

      }
    })
    wpmjs.registerLoader({
      moduleType: "mf",
      resolveUrl({}) {
        
      }
    })
    `,
  baseUrl: "https://cdn.jsdelivr.net/npm",
  remotes: {
    "@remix-run/router": "@remix-run/router/dist/router.umd.min.js",
    "mf-app-02": {
      package: "mf-app-02/dist/remoteEntry.js",
      global: "mfapp02"
    },
  },
})

运行时API

除了使用npm-federation插件, 还可以使用运行时api引入远程模块, 与插件的api一致。

插件的remotes为wpmjs.addImportMap(), 插件的baseUrl为wpmjs.setConfig({baseUrl})。

例, 使用远程umd模块:

  • 方式一, 使用jsdelivr作为源
js
wpmjs.setConfig({
  // 设置域名
  baseUrl: "https://cdn.jsdelivr.net/npm",
})
wpmjs.addImportMap({
  remotes: {
    // 设置npm包path
    "react-dom": "react-dom/umd/react-dom.development.js",
    "react": "react/umd/react.development.js"
  }
})
await wpmjs.import("react-dom")
await wpmjs.import("react")
wpmjs.setConfig({
  // 设置域名
  baseUrl: "https://cdn.jsdelivr.net/npm",
})
wpmjs.addImportMap({
  remotes: {
    // 设置npm包path
    "react-dom": "react-dom/umd/react-dom.development.js",
    "react": "react/umd/react.development.js"
  }
})
await wpmjs.import("react-dom")
await wpmjs.import("react")
  • 方式2, 使用url作为源
js
wpmjs.addImportMap({
  remotes: {
    "react-dom": "https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.development.js",
    "react": "https://cdn.jsdelivr.net/npm/react/umd/react.development.js"
  }
})
await wpmjs.import("react-dom")
await wpmjs.import("react")
wpmjs.addImportMap({
  remotes: {
    "react-dom": "https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.development.js",
    "react": "https://cdn.jsdelivr.net/npm/react/umd/react.development.js"
  }
})
await wpmjs.import("react-dom")
await wpmjs.import("react")

版本配置化

远程模块的版本可以动态配置, 例如可以通过读取一个远程返回的json数据来决定加载模块的版本:

  • 方式1, 异步获取远程版本配置, 使用wpmjs.sleep() :
js
// webpack.config.js
new NpmFederation({
  // 注入代码
  initial: `
    wpmjs.sleep(new Promise(resolve => {
      fetch("http://xxx.json").then(json => {
        // 注册
        wpmjs.addImportMap({
          react: {packageVersion: json.react},
          antd: {packageVersion: json.antd}
        })
        resolve()
      })
    }))
  `,
  remotes: {
    "react-dom": "react-dom/umd/react-dom.development.js",
    "react": "react/umd/react.development.js",
  }
})
// webpack.config.js
new NpmFederation({
  // 注入代码
  initial: `
    wpmjs.sleep(new Promise(resolve => {
      fetch("http://xxx.json").then(json => {
        // 注册
        wpmjs.addImportMap({
          react: {packageVersion: json.react},
          antd: {packageVersion: json.antd}
        })
        resolve()
      })
    }))
  `,
  remotes: {
    "react-dom": "react-dom/umd/react-dom.development.js",
    "react": "react/umd/react.development.js",
  }
})
  • 方式2, 通过ssr或注入等方式在页面响应时返回版本配置
js
wpmjs.addImportMap({
  initial: `
  wpmjs.addImportMap({
    react: {packageVersion: window.$json.react},
    react: {packageVersion: window.$json.reactDom},
  })
  `,
  remotes: {
    "react-dom": "https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.development.js",
    "react": "https://cdn.jsdelivr.net/npm/react/umd/react.development.js"
  }
})
wpmjs.addImportMap({
  initial: `
  wpmjs.addImportMap({
    react: {packageVersion: window.$json.react},
    react: {packageVersion: window.$json.reactDom},
  })
  `,
  remotes: {
    "react-dom": "https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.development.js",
    "react": "https://cdn.jsdelivr.net/npm/react/umd/react.development.js"
  }
})