【问题标题】:Webpack, React-router and React from CDN来自 CDN 的 Webpack、React-router 和 React
【发布时间】:2024-05-17 02:55:02
【问题描述】:

我使用 Webpack 来捆绑我的 React 应用程序和 react-router 来进行路由。 ReactJs 作为外部库从 CDN 加载。我从 react-router 收到此错误消息,我不确定是否可以将从 CDN 加载的 React 与其他反应库一起使用。

    PropTypes.js:8

    Uncaught TypeError: Cannot read property 'PropTypes' of undefined(…)
    (anonymous function) @ PropTypes.js:8
    __webpack_require__ @ bootstrap 2b8bebf…:19
    (anonymous function) @ index.js:15
    __webpack_require__ @ bootstrap 2b8bebf…:19
    (anonymous function) @ index.jsx:3
    __webpack_require__ @ bootstrap 2b8bebf…:19
    (anonymous function) @ bootstrap 2b8bebf…:39
    (anonymous function) @ bootstrap 2b8bebf…:39

index.html

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
        <title>Experiment</title>
    </head>
    <body>
        <div id="content">
            <!-- this is where the root react component will get rendered -->
        </div>

        <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react.js"></script>
        <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react-dom.js"></script>

        <!-- include the webpack-dev-server script so our scripts get reloaded when we make a change -->
        <!-- we'll run the webpack dev server on port 8090, so make sure it is correct -->
        <script src="http://localhost:8090/webpack-dev-server.js"></script>
        <!-- include the bundle that contains all our scripts, produced by webpack -->
        <!-- the bundle is served by the webpack-dev-server, so serve it also from localhost:8090 -->
        <script type="text/javascript" src="http://localhost:8090/assets/main.js"></script>

    </body>
</html>

index.jsx

import {Router, Route, useRouterHistory, browserHistory} from 'react-router';

ReactDOM.render(
  <h1>hello</h1>, document.getElementById('content')
);

webpack.config.js

var webpack = require('webpack');
var merge = require('webpack-merge');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var NpmInstallPlugin = require('npm-install-webpack-plugin');

const TARGET = process.env.npm_lifecycle_event;
console.log("target event is " + TARGET);

var common = {
  cache: true,
  debug: true,
  entry: './src/script/index.jsx',
  resolve: {
    extensions: ['', '.js', '.jsx']
  },
  externals: {
    // Adapt React to different environments.
    'react': {
      commonjs: 'react',
      commonjs2: 'react',
      amd: 'React',
      root: 'React'
    }
  },
  output: {
    filename: '[name].js',
    sourceMapFilename: '[file].map'
  },
  module: {
    loaders: [{
      test: /\.js[x]?$/,
      loaders: ['babel-loader?presets[]=es2015&presets[]=react'],
      exclude: /(node_modules)/
    }, {
      test: /\.css$/,
      loaders: ['style', 'css']
    }, {
      test: /\.scss$/,
      loaders: ['style', 'css', 'sass']
    }, {
      test: /\.less$/,
      loaders: ['style', 'css', 'less']
    }, {
      test: /\.woff$/,
      loader: "url-loader?limit=10000&mimetype=application/font-woff&name=[path][name].[ext]"
    }, {
      test: /\.woff2$/,
      loader: "url-loader?limit=10000&mimetype=application/font-woff2&name=[path][name].[ext]"
    }, {
      test: /\.(eot|ttf|svg|gif|png)$/,
      loader: "file-loader"
    }]
  },
  plugins: [
    new webpack.ProvidePlugin({
      $: "jquery",
      jQuery: "jquery"
    })
  ]
};

if(TARGET === 'dev' || !TARGET) {
    module.exports = merge(common,{
        devtool: 'eval-source-map',
        devServer: {
            historyApiFallback: false
        },
        entry: './src/script/index.jsx',
        output: {
            filename: '[name].js',
            publicPath: 'http://localhost:8090/assets'
        },
        plugins: [
            new NpmInstallPlugin({
                save: true // --save
            }),
            new webpack.DefinePlugin({
                'process.env.NODE_ENV': JSON.stringify('dev')
            })
        ]
    });
}

if (TARGET === 'build') {
  module.exports = merge(common, {
    devtool: 'source-map',
    output: {
      path: './dist'
    }
  });
}

package.json

"dependencies": {
    "babel": "^6.5.2",
    "babel-core": "^6.18.0",
    "babel-loader": "^6.2.7",
    "babel-preset-es2015": "^6.18.0",
    "babel-preset-react": "^6.16.0",
    "bootstrap": "^3.3.7",
    "css-loader": "^0.25.0",
    "file-loader": "^0.9.0",
    "history": "^2.0.1",
    "html-webpack-plugin": "^2.24.1",
    "http-server": "^0.9.0",
    "jquery": "^3.1.1",
    "less": "^2.7.1",
    "less-loader": "^2.2.3",
    "node-sass": "^3.10.1",
    "npm-install-webpack-plugin": "^4.0.4",
    "react": "^15.3.2",
    "react-datagrid": "^2.1.1",
    "react-dom": "^15.3.2",
    "react-router": "^3.0.0",
    "sass-loader": "^4.0.2",
    "style-loader": "^0.13.1",
    "url-loader": "^0.5.7",
    "webpack": "^1.13.3",
    "webpack-dev-server": "^1.16.2",
    "webpack-merge": "^0.15.0"
  }

【问题讨论】:

    标签: javascript reactjs webpack react-router


    【解决方案1】:

    你真的可以在externals 中指定这样的环境吗? The docs 的使用方法似乎很简单。这是我的外部外观,我使用 CDN 中的 React/ReactDOM 和其他库没问题:

    externals: {
      react: 'React',
      'react-dom': 'ReactDOM'
    }
    

    似乎设置了您需要使用output.libraryoutput.libraryTarget选项的环境,特别是文档示例中显示的libraryTarget选项。

    【讨论】:

    • 感谢您的回复,我在您的回答中使用了外部组件并且它有效。看起来外部的值不应该是对象,而是字符串。我不明白为什么react-dom 键是字符串而react 键不是。
    • 哦,如果您愿意,您可以将react 设为字符串。请记住,所有对象键都会转换为字符串。但是react-dom 不是有效的对象键,因为-,所以你必须把它写成一个字符串才不会出错。