Ошибка зависимости HMR от шаблона проектирования фасада реагирования
При переходе к маршрутизатору Redux-first в моем шаблонном проекте activjs redux webpack заметил, что HMR перестал работать!
Итак, я пытаюсь понять, как работают зависимости на HMR accept
метод и почему простой вызов module.hot.accept() вместо указания зависимостей и обратного вызова успешен (при избыточном вызове возникает ошибка миграции 2.0, поскольку провайдер не поддерживает изменение хранилища на лету).
Я потратил много времени на исследования, но не смог найти решения по этому поводу, поэтому планировал вернуться к своему маршрутизатору 4, где мой шаблон проектирования фасада работает нормально.
У меня было это работает с React router 4, который я также использовал тот же шаблон фасада для организации моих компонентов реагирования и файлов редуксов.
Запись приложения:
import React from 'react'
import { render } from 'react-dom'
import { AppContainer as HotReload } from 'react-hot-loader'
import configureStore from './root/store'
import { createBrowserHistory } from 'history'
const MainApi = require('./modules/main').default
const App = MainApi.containers.App
const history = createBrowserHistory()
const store = configureStore(history)
// render method for instantiation and Hot module reload
const renderApp = (RootComponent) => {
let rootEl = document.getElementById('app')
render(
<HotReload>
<RootComponent store={store} history={history} />
</HotReload>,
rootEl
)
}
// create instance
renderApp(Root)
// Hot Module Replacement API
if (process.env.NODE_ENV === 'development' && module.hot) {
module.hot.accept('./modules/main/containers/app', () => {
const App = require('./modules/main/containers/app').default
renderApp(App)
})
}
if (process.env.NODE_ENV === 'production') {
require('offline-plugin/runtime').install()
}
Единственный способ убедиться в его работоспособности - принять без указания каких-либо зависимостей и обратного вызова. Результатом этого является предупреждение Redux о переходе на 2.0
if (module.hot) {
module.hot.accept()
}
Хотя я понимаю, что проблема может быть связана с "зависимостями", переданными hot.module.accept(dependencies, callback)
на основании списка updated modules
их указание, по-видимому, не решает проблему во время тестирования и наблюдения (TIAS).
Ради вас, я поделюсь, как организованы модули ниже.
А также
Как модуль раскрывает API модуля:
Корневой редуктор:
Корневое хранилище или configureStore:
import { createStore, applyMiddleware, combineReducers, compose } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension/logOnlyInProduction'
import { connectRoutes } from 'redux-first-router'
import routes from './routes'
import rootReducer from './reducer'
import AboutApi from 'modules/about'
// TODO: combine all the modules actions
const actionCreators = AboutApi.actions
const composeEnhancers = (...args) =>
typeof window !== 'undefined'
? composeWithDevTools({ actionCreators })(...args)
: compose(...args)
export default (history, preloadedState) => {
const routerSetup = connectRoutes(history, routes.map, routes.options)
const combinedReducers = combineReducers({ ...rootReducer, location: routerSetup.reducer })
const middlewares = applyMiddleware(routerSetup.middleware)
const enhancers = composeEnhancers(routerSetup.enhancer, middlewares)
const store = createStore(combinedReducers, preloadedState, enhancers)
if (module.hot) {
module.hot.accept('./reducer', () => {
const rootReducer = require('./reducer')
const combinedReducers = combineReducers({ ...rootReducer, location: routerSetup.reducer })
store.replaceReducer(combinedReducers)
})
}
return { store, thunk: routerSetup.thunk }
}
Следующая шаблонная / демонстрационная программа "Первый редуктор" работает, но не использует один и тот же шаблон проектирования фасада для организации файлов реакции и редукции ( https://github.com/faceyspacey/redux-first-router-demo/tree/master/src)
Файл конфигурации разработки веб-пакета:
var path = require('path')
var webpack = require('webpack')
var AssetsPlugin = require('assets-webpack-plugin')
var assetsPluginInstance = new AssetsPlugin({ path: path.resolve(__dirname), update: true })
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var rootDir = path.resolve(__dirname, '../')
module.exports = {
context: path.resolve(rootDir, 'src'),
entry: [
'babel-polyfill',
// 'webpack-hot-middleware/client',
'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000&reload=false&quiet=false&noInfo=false',
'react-hot-loader/patch',
'webpack/hot/dev-server',
path.join(rootDir, '/src/js/index.js')
],
output: {
path: path.join(rootDir, '/dist/development'),
publicPath: '/assets/',
filename: 'js/bundle.js?[hash]'
},
devtool: 'inline-source-map',
devServer: {
hot: true,
// match the output path
contentBase: path.join(rootDir, '/dist/development'),
// match the output `publicPath`
publicPath: '/assets/'
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules)/,
use: [{
loader: 'babel-loader'
}]
},
{
test: /\.scss$/,
use: ['css-hot-loader'].concat(ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [{
loader: 'css-loader'
},
{
loader: 'sass-loader',
options: {
sourceMap: true,
includePaths: [
path.join(rootDir, 'node_modules')
],
outputStyle: 'compressed'
}
}]
}))
},
{
test: /\.(ttf|eot|svg|woff(2)?)(\?[a-z0-9=&.]+)?$/,
use: [
'file-loader?name=[path][name].[ext]'
]
},
{
test: /\.(jpg|png|gif|svg)$/i,
use: [
'file-loader?name=[path][name].[ext]&emitFile=false'
]
},
{
test: /\.(webm|mp4)$/,
use: [
'file-loader?name=[path][name].[ext]&emitFile=false'
]
}
]
},
plugins: [
new ExtractTextPlugin('css/[name].min.css?[hash]'),
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(),
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('development')
}
}),
assetsPluginInstance
]
}