2017-07-31 114 views
1

我試圖使用React和Webpack(3.4.1)構建「組件庫」。這個想法是讓一個React應用程序使用React Cosmos來交互式構建和探索可重用組件的「庫」。該回購還將有一個build任務,該任務將這些組件編譯成可推送到Github和/或NPM的文件。並且該文件/包可以導入到其他項目中以訪問可重用組件。Webpack包導入空依賴項對象

NOTE/UPDATE - 對於那些誰想要嘗試了這一點,對自己,我已經出版了(非功能性)包NPM:https://www.npmjs.com/package/rs-components。只是yarn add它到您的項目(可能)遇到相同的問題。

我有90%的工作 - React Cosmos正確顯示組件,並且我的構建任務(rimraf dist && webpack --display-error-details --config config/build.config.js)正在運行,沒有錯誤。但是,當我從Github將該回購協議作爲一個包回到另一個項目時,我遇到了錯誤。

錯誤似乎源於Webpack不能正確導入我的組件庫的依賴關係。當我沒有再壓縮/裂傷上建立了圖書館,我進口看到的第一個錯誤是:

TypeError: React.Component is not a constructor

事實上,如果我扔在一個調試器,並檢查它起反應是空物體。

如果我解決,通過(欺騙和)直接導入在node_modulesconst React = require('../../react/react.js'))編譯/下載的文件反應,不會發生錯誤,但後來我遇到了一個類似的,涉及到在prop-types庫導入失敗:

TypeError: Cannot read property 'isRequired' of undefined

如此看來,雖然代碼是否正確得到拉昇,在組件庫文件導入似乎沒有被正確捆綁

這裏是最相關的文件:

配置/ build.config.js

const path = require('path'); 
const webpack = require('webpack'); 
const ExtractTextPlugin = require('extract-text-webpack-plugin'); 

const config = { 
    entry: path.resolve(__dirname, '../src/index.js'), 
    devtool: false, 
    output: { 
    path: path.resolve(__dirname, '../dist'), 
    filename: '[name].js' 
    }, 
    resolve: { 
    modules: [ 
     path.resolve(__dirname, '../src'), 
     'node_modules' 
    ], 
    extensions: ['.js'] 
    }, 
    module: { 
    rules: [] 
    } 
}; 

// JavaScript 
// ------------------------------------ 
config.module.rules.push({ 
    test: /\.(js|jsx)$/, 
    exclude: /node_modules/, 
    use: [{ 
    loader: 'babel-loader', 
    query: { 
     cacheDirectory: true, 
     plugins: [ 
     'babel-plugin-transform-class-properties', 
     'babel-plugin-syntax-dynamic-import', 
     [ 
      'babel-plugin-transform-runtime', 
      { 
      helpers: true, 
      polyfill: false, // We polyfill needed features in src/normalize.js 
      regenerator: true 
      } 
     ], 
     [ 
      'babel-plugin-transform-object-rest-spread', 
      { 
      useBuiltIns: true // We polyfill Object.assign in src/normalize.js 
      } 
     ] 
     ], 
     presets: [ 
     'babel-preset-react', 
     ['babel-preset-env', { 
      targets: { 
      ie9: true, 
      uglify: false, 
      modules: false 
      } 
     }] 
     ] 
    } 
    }] 
}); 

// Styles 
// ------------------------------------ 
const extractStyles = new ExtractTextPlugin({ 
    filename: 'styles/[name].[contenthash].css', 
    allChunks: true, 
    disable: false 
}); 

config.module.rules.push({ 
    test: /\.css$/, 
    use: 'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]__[hash:base64:5]' 
}); 

config.module.rules.push({ 
    test: /\.(sass|scss)$/, 
    loader: extractStyles.extract({ 
    fallback: 'style-loader', 
    use: [ 
     { 
     loader: 'css-loader', 
     options: { 
      sourceMap: false, 
      minimize: { 
      autoprefixer: { 
       add: true, 
       remove: true, 
       browsers: ['last 2 versions'] 
      }, 
      discardComments: { 
       removeAll: true 
      }, 
      discardUnused: false, 
      mergeIdents: false, 
      reduceIdents: false, 
      safe: true, 
      sourcemap: false 
      } 
     } 
     }, 
     { 
     loader: 'postcss-loader', 
     options: { 
      autoprefixer: { 
      add: true, 
      remove: true, 
      browsers: ['last 2 versions'] 
      }, 
      discardComments: { 
      removeAll: true 
      }, 
      discardUnused: false, 
      mergeIdents: false, 
      reduceIdents: false, 
      safe: true, 
      sourceMap: true 
     } 
     }, 
     { 
     loader: 'sass-loader', 
     options: { 
      sourceMap: false, 
      includePaths: [ 
      path.resolve(__dirname, '../src/styles') 
      ] 
     } 
     } 
    ] 
    }) 
}); 
config.plugins = [extractStyles]; 

// Images 
// ------------------------------------ 
config.module.rules.push({ 
    test: /\.(png|jpg|gif)$/, 
    loader: 'url-loader', 
    options: { 
    limit: 8192 
    } 
}); 

// Bundle Splitting 
// ------------------------------------ 
const bundles = ['normalize', 'manifest']; 

bundles.unshift('vendor'); 
config.entry.vendor = [ 
    'react', 
    'react-dom', 
    'redux', 
    'react-redux', 
    'redux-thunk', 
    'react-router' 
]; 

config.plugins.push(new webpack.optimize.CommonsChunkPlugin({ names: bundles })); 

// Production Optimizations 
// ------------------------------------ 
config.plugins.push(
    new webpack.LoaderOptionsPlugin({ 
    minimize: false, 
    debug: false 
    }), 
    new webpack.optimize.UglifyJsPlugin({ 
    sourceMap: false, 
    comments: false, 
    compress: { 
     warnings: false, 
     screw_ie8: true, 
     conditionals: true, 
     unused: true, 
     comparisons: true, 
     sequences: true, 
     dead_code: true, 
     evaluate: true, 
     if_return: true, 
     join_vars: true 
    } 
    }) 
); 

module.exports = config; 

一個例子組件:

Checkbox.js

import React from 'react'; 
import { connect } from 'react-redux'; 
import { map } from 'react-immutable-proptypes'; 
import classnames from 'classnames'; 

import { setCheckboxValue } from 'store/actions'; 

/** 
* Renders a Redux-connected Checkbox with label 
* 
* @param {string} boxID - Unique string identifier of checkbox 
* @param {string} name - Label text to display 
* @param {function} dispatch - Redux dispatch function 
* @param {Immutable.Map} checkboxes - Redux checkboxes Map 
* @param {string[]} className - Optional additional classes 
* 
* @returns {React.Component} A checkbox with globally-tracked value 
*/ 
export function CheckboxUC ({ boxID, name, dispatch, checkboxes, className }) { 
    const checked = checkboxes.get(boxID); 
    return (
    <label className={ classnames('checkbox rscomp', className) } htmlFor={ boxID }> 
     <input 
     className="checkable__input" 
     type="checkbox" 
     onChange={() => { 
      dispatch(setCheckboxValue(boxID, !checked)); 
     } } 
     name={ name } 
     checked={ checked } 
     /> 
     <span className="checkable__mark" /> 
     <span className="checkable__label">{ name }</span> 
    </label> 
); 
} 

const mapStateToProps = state => ({ 
    checkboxes: state.checkboxes 
}); 

const { string, func } = React.PropTypes; 
CheckboxUC.propTypes = { 
    boxID: string.isRequired, 
    name: string.isRequired, 
    checkboxes: map.isRequired, 
    dispatch: func, 
    className: string 
}; 

export default connect(mapStateToProps)(CheckboxUC); 

與「條目」文件中的WebPack建設任務(webpack --config config/build.config.js),這是指僅導出,應該是由應用程序導入此包可訪問的組件:

的src /指數。JS

import BaseForm from 'components/BaseForm'; 
import Checkbox from 'components/Checkbox'; 
import Dropdown from 'components/Dropdown'; 
import Link from 'components/Link'; 
import RadioGroup from 'components/RadioGroup'; 

import { validators } from 'components/BaseForm/utils'; 

export default { 
    BaseForm, 
    Checkbox, 
    Dropdown, 
    Link, 
    RadioGroup, 
    utils: { 
    validators 
    } 
}; 

最後,這裏是在編譯未變醜JS行被 「進口」 的變量,似乎不當未定義:

  • var React = __webpack_require__(1);
  • /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_prop_types__ = __webpack_require__(3);

讓我知道是否還有其他東西可以幫助解決這個問題。超級困惑,所以我非常感謝任何幫助。

謝謝!

編輯

的事一堆似乎正確導入。當我在編譯的文件中拋出一個調試器,並看看__webpack_require__(n)嘗試一大堆不同的n s時,我看到不同的導入模塊。不幸的是,React和PropTypes似乎並不在其中。

回答

0

試着改變你的條目:

entry: { 'bundle': path.resolve(__dirname, '../src/index.js')} 

而且你CommonsChunkPlugin config來:

config.plugins.push(new webpack.optimize.CommonsChunkPlugin({ name: 'vendor' })); 

再檢查vendor.js文件的供應商條目下的東西。

如果你的目標是把所有東西放在一個文件下,就把CommonsChunkPlugin省略掉。