Ich versuche, Hot-Loader in meine App zu konfigurieren. Es ist auf Webpack 2, Webpack-Dev-Middleware, Browser-Sync, Express und Server-Side-Rendering aufgebaut.Kann React Hot Loader nicht funktionieren
Hier ist meine webpack.config
require('dotenv').config()
import path from 'path'
import webpack from 'webpack'
import extend from 'extend'
import AssetsPlugin from 'assets-webpack-plugin'
import ExtractTextPlugin from 'extract-text-webpack-plugin'
import bundles from '../src/bundles'
import merge from 'lodash/merge'
import fs from 'fs'
const isDebug = !process.argv.includes('--release');
const isVerbose = process.argv.includes('--verbose');
const GLOBALS = {
'process.env.NODE_ENV': isDebug ? '"development"' : '"production"',
'process.env.MORTGAGE_CALCULATOR_API': process.env.MORTGAGE_CALCULATOR_API ? `"${process.env.MORTGAGE_CALCULATOR_API}"` : null,
'process.env.API_HOST': process.env.BROWSER_API_HOST ? `"${process.env.BROWSER_API_HOST}"` : process.env.API_HOST ? `"${process.env.API_HOST}"` : null,
'process.env.GOOGLE_ANALYTICS_ID': process.env.GOOGLE_ANALYTICS_ID ? `"${process.env.GOOGLE_ANALYTICS_ID}"` : null,
'process.env.OMNITURE_SUITE_ID': process.env.OMNITURE_SUITE_ID ? `"${process.env.OMNITURE_SUITE_ID}"` : null,
'process.env.COOKIE_DOMAIN': process.env.COOKIE_DOMAIN ? `"${process.env.COOKIE_DOMAIN}"` : null,
'process.env.FEATURE_FLAG_BAZAAR_VOICE': process.env.FEATURE_FLAG_BAZAAR_VOICE ? `"${process.env.FEATURE_FLAG_BAZAAR_VOICE}"` : null,
'process.env.FEATURE_FLAG_REALTIME_RATING': process.env.FEATURE_FLAG_REALTIME_RATING ? `"${process.env.FEATURE_FLAG_REALTIME_RATING}"` : null,
'process.env.SALE_RESULTS_PAGE_FLAG': process.env.SALE_RESULTS_PAGE_FLAG ? `"${process.env.SALE_RESULTS_PAGE_FLAG}"` : null,
'process.env.SALE_RELOADED_RESULTS_PAGE_FLAG': process.env.SALE_RELOADED_RESULTS_PAGE_FLAG ? `"${process.env.SALE_RELOADED_RESULTS_PAGE_FLAG}"` : null,
'process.env.TRACKER_DOMAIN': process.env.TRACKER_DOMAIN ? `"${process.env.TRACKER_DOMAIN}"` : null,
'process.env.USER_SERVICE_ENDPOINT': process.env.USER_SERVICE_ENDPOINT ? `"${process.env.USER_SERVICE_ENDPOINT}"` : null,
__DEV__: isDebug
};
//
// Common configuration chunk to be used for both
// client-side (client.js) and server-side (server.js) bundles
// -----------------------------------------------------------------------------
const config = {
output: {
publicPath: '/blaze-assets/',
},
cache: isDebug,
stats: {
colors: true,
reasons: isDebug,
hash: isVerbose,
version: isVerbose,
timings: true,
chunks: isVerbose,
chunkModules: isVerbose,
cached: isVerbose,
cachedAssets: isVerbose,
},
plugins: [
new ExtractTextPlugin({
filename: isDebug ? '[name].css' : '[name].[chunkhash].css',
allChunks: true,
}),
new webpack.LoaderOptionsPlugin({
minimize: true,
debug: isDebug,
}),
],
resolve: {
extensions: ['.webpack.js', '.web.js', '.js', '.jsx', '.json'],
modules: [
path.resolve('./src'),
'node_modules',
]
},
module: {
rules: [
{
test: /\.jsx?$/,
include: [
path.resolve(__dirname, '../src'),
],
loader: 'babel-loader',
}, {
test: /\.(scss|css)$/,
exclude: ['node_modules'],
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
'css-loader',
'sass-loader',
]
})
}, {
test: /\.txt$/,
loader: 'raw-loader',
}, {
test: /\.(otf|png|jpg|jpeg|gif|svg|woff|woff2)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'url-loader?limit=10000',
}, {
test: /\.(eot|ttf|wav|mp3)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'file-loader',
}, {
test: /\.jade$/,
loader: 'jade-loader',
}
],
},
};
//
// Configuration for the client-side bundles
// -----------------------------------------------------------------------------
let clientBundles = {}
Object.keys(bundles).forEach(function (bundle) {
clientBundles[bundle] = [
'bootstrap-loader',
`./src/bundles/${bundle}/index.js`
]
})
merge(
clientBundles,
{
'embedWidget': ['./src/components/Widgets/EmbedWidget/widgetLoader.js']
},
)
const clientConfig = extend(true, {}, config, {
entry: clientBundles,
output: {
path: path.join(__dirname, '../build/public/blaze-assets/'),
filename: isDebug ? '[name].js' : '[name].[chunkhash].js',
chunkFilename: isDebug ? '[name].chunk.js' : '[name].[chunkhash].chunk.js',
},
node: {
fs: "empty"
},
// Choose a developer tool to enhance debugging
// http://webpack.github.io/docs/configuration.html#devtool
// devtool: isDebug ? 'cheap-module-source-map' : false,
plugins: [
...config.plugins,
...(isDebug ? [
new webpack.EvalSourceMapDevToolPlugin({
filename: '[file].map',
exclude: /\.(css)($)/i,
}),
] : []),
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
new webpack.DefinePlugin({
...GLOBALS,
'process.env.BROWSER': true
}),
...(!isDebug ? [
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
screw_ie8: true,
// jscs:enable requireCamelCaseOrUpperCaseIdentifiers
warnings: isVerbose,
unused: true,
dead_code: true,
},
}),
new webpack.optimize.AggressiveMergingPlugin(),
] : []),
new AssetsPlugin({
path: path.join(__dirname, '../build'),
filename: 'assets.json',
prettyPrint: true,
}),
],
});
//
// Configuration for the server-side bundle (server.js)
// -----------------------------------------------------------------------------
var srcDirs = {};
fs.readdirSync('src').forEach(function(path) {
srcDirs[path] = true
});
function isExternalFile(context, request, callback) {
var isExternal = request.match(/^[@a-z][a-z\/\.\-0-9]*$/i) && !srcDirs[request.split("/")[0]]
callback(null, Boolean(isExternal));
}
const serverConfig = extend(true, {}, config, {
entry: './src/server.js',
output: {
path: path.join(__dirname, '../build/public/blaze-assets/'),
filename: '../../server.js',
libraryTarget: 'commonjs2',
},
target: 'node',
externals: [
/^\.\/assets\.json$/,
isExternalFile
],
node: {
console: false,
global: false,
process: false,
Buffer: false,
__filename: false,
__dirname: false,
},
devtool: isDebug ? 'cheap-module-source-map' : 'source-map',
plugins: [
...config.plugins,
new webpack.DefinePlugin({
...GLOBALS,
'process.env.BROWSER': false,
'process.env.API_HOST': process.env.API_HOST ? `"${process.env.API_HOST}"` : null
}),
new webpack.NormalModuleReplacementPlugin(/\.(scss|css|eot|ttf|woff|woff2)$/, 'node-noop'),
new webpack.BannerPlugin({
banner: `require('dotenv').config(); require('newrelic'); require('source-map-support').install();`,
raw: true,
entryOnly: false
})
],
});
export default [clientConfig, serverConfig];
start.js
Datei
import Browsersync from 'browser-sync'
import webpack from 'webpack'
import webpackMiddleware from 'webpack-dev-middleware'
import webpackHotMiddleware from 'webpack-hot-middleware'
import WriteFilePlugin from 'write-file-webpack-plugin'
import run from './run'
import runServer from './runServer'
import webpackConfig from './webpack.config'
import clean from './clean'
import copy from './copy'
const isDebug = !process.argv.includes('--release')
const [, serverConfig] = webpackConfig
process.argv.push('--watch')
/**
* Launches a development web server with "live reload" functionality -
* synchronizing URLs, interactions and code changes across multiple devices.
*/
async function start() {
await run(clean)
await run(copy.bind(undefined, { watch: true }))
await new Promise((resolve) => {
serverConfig.plugins.push(new WriteFilePlugin({ log: false }))
// Patch the client-side bundle configurations
// to enable Hot Module Replacement (HMR) and React Transform
webpackConfig.filter((x) => x.target !== 'node').forEach((config) => {
/* eslint-disable no-param-reassign */
Object.keys(config.entry).forEach((entryKey) => {
if (!Array.isArray(config.entry[entryKey])) {
config.entry[entryKey] = [config.entry[entryKey]]
}
config.entry[entryKey].unshift('react-hot-loader/patch', 'webpack-hot-middleware/client')
})
if (config.output.filename) {
config.output.filename = config.output.filename.replace('[chunkhash]', '[hash]')
}
if (config.output.chunkFilename) {
config.output.chunkFilename = config.output.chunkFilename.replace('[chunkhash]', '[hash]')
}
config.plugins.push(new webpack.HotModuleReplacementPlugin())
config.plugins.push(new webpack.NoEmitOnErrorsPlugin())
config
.module
.rules
.filter((x) => x.loader === 'babel-loader')
.forEach((x) => (x.query = {
...x.query,
cacheDirectory: true,
presets: [
['es2015', {modules: false}],
'stage-0',
'react'
],
plugins: [
...(x.query ? x.query.plugins : []),
[
'react-hot-loader/babel',
],
],
}))
/* eslint-enable no-param-reassign */
})
const bundler = webpack(webpackConfig)
const wpMiddleware = webpackMiddleware(bundler, {
// IMPORTANT: webpack middleware can't access config,
// so we should provide publicPath by ourselves
publicPath: webpackConfig[0].output.publicPath,
// Pretty colored output
stats: webpackConfig[0].stats,
// For other settings see
// https://webpack.github.io/docs/webpack-dev-middleware
})
const hotMiddleware = webpackHotMiddleware(bundler.compilers[0])
let handleBundleComplete = async() => {
handleBundleComplete = (stats) => !stats.stats[1].compilation.errors.length && runServer()
const server = await runServer()
const bs = Browsersync.create()
bs.init({
...isDebug ? {} : { notify: false, ui: false },
proxy: {
target: server.host,
middleware: [wpMiddleware, hotMiddleware],
proxyOptions: {
xfwd: true,
},
},
open: false,
files: ['build/content/**/*.*'],
}, resolve)
}
bundler.plugin('done', (stats) => handleBundleComplete(stats))
})
}
export default start
Um heiße Ladung Reduzierungen, ich habe meinen Code wie folgt geändert,
const configureStore = (initialState = {}) => {
const store = createStore(
// storage.reducer is what merges storage state into redux state
storage.reducer(rootReducer),
inflateDecorators(initialState),
applyMiddleware(...middleware)
)
if (module.hot) {
module.hot.accept('./reducers',() => {
const nextRootReducer = require('./reducers')
store.replaceReducer(nextRootReducer)
})
}
return store
}
Wir mehrere Bündel haben, Es gibt also eine gemeinsame Funktion, um die Eingangslogik
zu handhaben Dieseimport 'babel-polyfill'
import React from 'react'
import ReactDOM from 'react-dom'
import FastClick from 'fastclick'
import { Provider } from 'react-redux'
import { setUrl } from 'actions'
import Location from '../../libs/Location'
import configureStore, { loadFromLocalStorage } from '../../configureStore'
import { AppContainer } from 'react-hot-loader'
const initialState = window.__INITIAL_STATE__
const store = configureStore(initialState)
function runner (createBody) {
return function() {
// Make taps on links and buttons work fast on mobiles
FastClick.attach(document.body)
const component = (
<AppContainer>
<Provider store={store}>
{createBody()}
</Provider>
</AppContainer>
)
if (!initialState) {
store.dispatch(setUrl(`${Location.location.pathname}${Location.location.search}`))
}
Location.listen((location) => {
store.dispatch(setUrl(`${location.pathname}${location.search}`))
})
ReactDOM.render(component, document.getElementById('app'))
// only apply stored state after first render
// this allows serverside and clientside rendering to agree on initial state
loadFromLocalStorage(store)
}
}
export default function run (createBody) {
if (['complete', 'loaded', 'interactive'].includes(document.readyState) && document.body) {
runner(createBody)()
} else {
document.addEventListener('DOMContentLoaded', runner(createBody), false)
}
}
ist eines der Bündel Eintrittspunkt in dem die oben gemeinsame Funktion aufgerufen wird,
import run from '../util/run'
import createBody from './body'
run(createBody)
if (module.hot) {
module.hot.accept('./body',() => run(createBody))
}
nicht sicher, was ich noch fehlt, habe ich versucht, ein paar Blog-Post zu folgen und heißen loader docs reagieren aber ich konnte es nicht zum arbeiten bringen.
In meinem Bündel Einstiegspunkt das Hinzufügen dieses Bit behoben das Problem in gewissem Umfang, 'const body = erfordern ('./Body'). Standard run (Körper)'. Ich dachte mit Webpack 2 das ist nicht erforderlich wie in der Dokumentation. Ich bin nicht in der Lage, css zum heißen Nachladen noch zu bekommen. – Mah3ndra