如何在生产环境中使用CDN中的库来构建Webpack项目

56

我想在生产环境中使用CDN中的react.min.js(例如https://unpkg.com/react@15.3.1/dist/react.min.js),请问如何让Webpack将我的import React from 'react'语句转换为const React = window.React,而不是把node_modules/react打包到bundle里?

我一直用resolve.alias这样做:

index.html中:

<head>
  <script type="text/javascript" src="https://unpkg.com/react@15.3.1/dist/react.min.js"></script>
  <script type="text/javascript" src="/assets/bundle.js"></script>
</head>

webpack.prod.config.js文件中:

alias: {
  react$: './getWindowReact',
},

getWindowReact.js:

getWindowReact.js:

module.exports = window.React;

注意:在旧的问题中,我没有意识到使用NODE_ENV=production将React构建到Webpack捆绑包中会剥离propTypes检查。其中一篇答案侧重于此。

3个回答

43

在您的webpack配置中,您可以使用externals选项,该选项将从环境中导入模块,而不是尝试正常解析它:

// webpack.config.js
module.exports = {
  externals: {
    'react': 'React'
  }
  ...
};

阅读更多内容: https://webpack.js.org/configuration/externals/


所以事实证明我错了,React并不是用Webpack构建的,所以这样做行不通。但是对于使用Webpack构建的模块来说,这似乎是正确的答案,所以我会接受这个答案。 - Andy
4
@Andy,我可能没有完全理解您的评论,但是这里建议的内容并不要求加载的库是使用Webpack构建的。这个“externals”示例所做的只是告诉Webpack:“如果一个模块请求react,则返回window.React”。 - Aaronius
@Aaronius 对不起,我当时误解了 webpack 的 externals...它们的文档有点别扭。 - Andy
当你定义了 'react': 'React' ,webpack 如何知道从哪里获取它?我的意思是,可能会有许多版本或名称。 - Raz Buchnik
1
@Raz 'react': 'React' 的意思是“当我从 from 'react' 导入时,实际上是在寻找 window.React”。关键是包名,值是全局属性名。 - Jacob Raihle

18

我创建了https://github.com/mastilver/dynamic-cdn-webpack-plugin,它可以直接实现这个功能。

const path = require('path')
const HTMLWebpackPlugin = require('html-webpack-plugin')
const DynamicCDNWebpackPlugin = require('dynamic-cdn-webpack-plugin')

module.exports = {
  entry: './main.js',
  output: {
    path: path.join(__dirname, 'build'),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      }
    ]
  },
  plugins: [
    new HTMLWebpackPlugin(),
    new DynamicCDNWebpackPlugin()
  ]
}

会动态添加unpkg.org的url,并在您的包中添加适当的代码,以从global加载库。


太牛了,这个主意棒极了!我使用 https://github.com/kossnocorp/assets-webpack-plugin 而不是 manifest 插件,你知道你的插件是否也能与 assets-webpack-plugin 兼容吗? - Andy
这个完美运行。在 modules-cdn 中没有预定义的模块,如何编写自定义解析器来填补空缺并不完全明显 - 自述文件中提供示例就好了!我选择了 if (modulePath == 'lodash') return { name: 'lodash', url: 'https://unpkg.com/lodash@' + version + '/lodash.min.js' }; 看起来目前它能正常工作。 - Roman Starkov
@RomanStarkov 差不多了! :)应该是:if (modulePath == 'lodash') return { name: 'lodash', url: 'https://unpkg.com/lodash@' + version + '/lodash.min.js', version: version, var: '_' };但这样会替换 module-to-cdn... 我可以问一下,为什么你没有向 module-to-cdn 提交 PR 呢?你不是唯一一个这样做的人,我正在努力理解如何让它更容易? - mastilver
1
@mastilver 这个插件是如何确定要加载哪个版本的模块,比如说React呢?我是否仍需要在package.json的依赖中注明正确的版本?或者我可以完全忽略package.json中的React吗? - hazardous
1
@hazardous,您的应用程序应该在有或没有这个插件的情况下都能正常工作,请在您的 package.json 中添加 React 。现在的情况是它正在要求您的依赖项的 package.json 并使用其中定义的版本 (https://github.com/mastilver/dynamic-cdn-webpack-plugin/blob/185e09314398f67f459084c94c9e153078168e31/src/index.js#L76)。 - mastilver
显示剩余5条评论

4

React代码库中所有仅用于开发的部分,例如PropType检查,都受到以下保护:

if ("production" !== process.env.NODE_ENV) {
  ..
}

如果你想从React中剥离这些内容,并在自己的构建过程中创建与被压缩的React构建等效的内容,可以使用DefinePlugin将对process.env.NODE_ENV的引用替换为"production"

plugins: [
  // ...
  new webpack.DefinePlugin({
    'process.env.NODE_ENV': JSON.stringify('production')
  }),
  new webpack.optimize.UglifyJsPlugin({
    compressor: {
      warnings: false
    }
  })
  // ...
],

Uglify的死代码消除将会清除所有这些代码,因为它会检测到被"production" !== "production"包装的代码是无法访问的。

这也很好知道,但由于我的老板想要使用CDN中的React,我们将坚持以这种方式进行。 - Andy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接