3

目前我正努力讓HMR在我的Webpack 2設置中工作。我會解釋我的整個設置,所以我希望這足以讓某人瞭解正在發生的事情。無法使Webpack 2 HMR React工作

我的項目結構:

config 
    dev.js 
    prod.js 
dist 
    css 
    js 
    index.html 
node_modules 
src 
    components 
    // some JavaScript components 
    shared 
    stylesheets 
    index.js 
.babelrc 
package.json 
webpack.config.js 

這是我webpack.config.js文件的內容,擺在我的項目的根目錄:

function buildConfig(env) { 
    return require('./config/' + env + '.js')(env) 
} 

module.exports = buildConfig; 
在這個文件

所以我的選擇將不同的環境傳遞給buildConfig函數。我使用這個選項來使用不同的配置文件進行開發和生產。這是我package.json文件中的內容:在我的package.json

{ 
    "main": "index.js", 
    "scripts": { 
    "build:dev": "node_modules/.bin/webpack-dev-server --env=dev", 
    "build:prod": "node_modules/.bin/webpack -p --env=prod" 
    }, 
    }, 
    "devDependencies": { 
    "autoprefixer-loader": "^3.2.0", 
    "babel-cli": "^6.18.0", 
    "babel-core": "^6.24.1", 
    "babel-loader": "^6.2.5", 
    "babel-preset-latest": "^6.16.0", 
    "babel-preset-react": "^6.16.0", 
    "babel-preset-stage-0": "^6.16.0", 
    "css-loader": "^0.25.0", 
    "extract-text-webpack-plugin": "^2.1.0", 
    "json-loader": "^0.5.4", 
    "node-sass": "^3.13.1", 
    "postcss-loader": "^1.3.3", 
    "postcss-scss": "^0.4.1", 
    "sass-loader": "^4.1.1", 
    "style-loader": "^0.13.1", 
    "webpack": "^2.4.1", 
    "webpack-dev-server": "^2.4.2" 
    }, 
    "dependencies": { 
    "babel-plugin-react-css-modules": "^2.6.0", 
    "react": "^15.3.2", 
    "react-dom": "^15.3.2", 
    "react-hot-loader": "^3.0.0-beta.6", 
    "react-icons": "^2.2.1" 
    } 
} 

我有,當然更多的領域,但我會因爲他們是無關的不是他們在這裏顯示。

所以在開發過程中,我在終端中運行了npm run build:dev命令。這將使用config文件夾中的文件dev.js。這是dev.js文件的內容:

const webpack = require('webpack'); 
const { resolve } = require('path'); 
const context = resolve(__dirname, './../src'); 

module.exports = function(env) { 
    return { 
    context, 
    entry: { 
     app: [ 
     'react-hot-loader/patch', 
     // activate HMR for React 
     'webpack-dev-server/client?http://localhost:3000', 
     // bundle the client for webpack-dev-server 
     // and connect to the provided endpoint 
     'webpack/hot/only-dev-server', 
     // bundle the client for hot reloading 
     // only- means to only hot reload for successful updates 
     './index.js' 
     // the entry point of our app 
     ] 
    }, 
    output: { 
     path: resolve(__dirname, './../dist'), // `dist` is the destination 
     filename: '[name].js', 
     publicPath: '/js' 
    }, 
    devServer: { 
     hot: true, // enable HMR on the server 
     inline: true, 
     contentBase: resolve(__dirname, './../dist'), // `__dirname` is root of the project 
     publicPath: '/js', 
     port: 3000 
    }, 
    devtool: 'inline-source-map', 
    module: { 
     rules: [ 
     { 
      test: /\.js$/, // Check for all js files 
      exclude: /node_modules/, 
      use: [{ 
      loader: 'babel-loader', 
      query: { 
       presets: ['latest', 'react'], 
       plugins: [ 
       [ 
        "react-css-modules", 
        { 
        context: __dirname + '/../src', // `__dirname` is root of project and `src` is source 
        "generateScopedName": "[name]__[local]___[hash:base64]", 
        "filetypes": { 
         ".scss": "postcss-scss" 
        } 
        } 
       ] 
       ] 
      } 
      }] 
     }, 
     { 
      test: /\.scss$/, 
      use: [ 
      'style-loader', 
      { 
       loader: 'css-loader', 
       options: { 
       sourceMap: true, 
       modules: true, 
       importLoaders: 2, 
       localIdentName: '[name]__[local]___[hash:base64]' 
       } 
      }, 
      'sass-loader', 
      { 
       loader: 'postcss-loader', 
       options: { 
       plugins:() => { 
        return [ 
        require('autoprefixer') 
        ]; 
       } 
       } 
      } 
      ] 
     } 
     ] 
    }, 
    plugins: [ 
     new webpack.HotModuleReplacementPlugin(), 
     // enable HMR globally 
     new webpack.NamedModulesPlugin() 
     // prints more readable module names in the browser console on HMR updates 
    ] 
    } 
}; 

最後但並非最不重要的,我HMR設置。我有這個安裝在我index.js文件:

import React from 'react'; 
import ReactDOM from 'react-dom'; 
import { AppContainer } from 'react-hot-loader'; 
import TodoApp from './components/TodoApp'; 
import './stylesheets/Stylesheets.scss'; 

const render = (Component) => { 
    ReactDOM.render(
     <AppContainer> 
     <Component /> 
     </AppContainer>, 
     document.querySelector('#main') 
); 
}; 

render(TodoApp); 

// Hot Module Replacement API 
if (module.hot) { 
    module.hot.accept('./components/TodoApp',() => { 
    render(TodoApp) 
    }); 
} 

所以,當我跑我的npm start build:dev在我的瀏覽器,進入http://localhost:3000我看到我的網站工作正常。這是在控制檯輸出:

dev-server.js:49 [HMR] Waiting for update signal from WDS... 
only-dev-server.js:66 [HMR] Waiting for update signal from WDS... 
TodoApp.js:102 test 
client?344c:41 [WDS] Hot Module Replacement enabled. 

test文本來自渲染功能在我TodoApp組件。此功能如下所示:

render() { 
    console.log('test'); 
    return(
     <div styleName="TodoApp"> 
     <TodoForm addTodo={this.addTodo} /> 
     <TodoList todos={this.state.todos} deleteTodo={this.deleteTodo} toggleDone={this.toggleDone} updateTodo={this.updateTodo} /> 
     </div> 
); 
} 

所以,現在是重要的東西。我更新這個渲染函數的返回值,這應該觸發HMR啓動。我將渲染函數更改爲此。

render() { 
    console.log('test'); 
    return(
     <div styleName="TodoApp"> 
     <p>Hi Stackoverflow</p> 
     <TodoForm addTodo={this.addTodo} /> 
     <TodoList todos={this.state.todos} deleteTodo={this.deleteTodo} toggleDone={this.toggleDone} updateTodo={this.updateTodo} /> 
     </div> 
); 
} 

這是輸出我在控制檯中看到:

client?344c:41 [WDS] App updated. Recompiling... 
client?344c:41 [WDS] App hot update... 
dev-server.js:45 [HMR] Checking for updates on the server... 
TodoApp.js:102 test 
log-apply-result.js:20 [HMR] Updated modules: 
log-apply-result.js:22 [HMR] - ./components/TodoApp.js 
dev-server.js:27 [HMR] App is up to date. 

你會說這是很好的。 但我的網站不更新任何東西。

然後,我改變了HMR代碼在我index.js這樣:

// Hot Module Replacement API 
if (module.hot) { 
    module.hot.accept(); 
} 

和它的作品。我只是不明白。

// Hot Module Replacement API 
if (module.hot) { 
    module.hot.accept('./components/TodoApp',() => { 
    render(TodoApp) 
    }); 
} 

BTW這個設置是根據設置從https://webpack.js.org/guides/hmr-react/

我希望有人能幫助我:爲什麼,如果這是我的HMR代碼不發揮作用。如果有人需要更多信息,請不要猶豫。提前致謝!

UPDATE

忘記而我.babelrc文件。這是它:

{ 
    "presets": [ 
    ["es2015", {"modules": false}], 
    // webpack understands the native import syntax, and uses it for tree shaking 

    "react" 
    // Transpile React components to JavaScript 
    ], 
    "plugins": [ 
    "react-hot-loader/babel" 
    // EnablesReact code to work with HMR. 
    ] 
} 
+0

你有沒有在你的'.babelrc'文件中禁用ES2015模塊? https://webpack.js.org/guides/hmr-react/#babel-config –

+0

O耶對不起,我忘了發佈我的'.babelrc'文件的內容。現在就做這個。 – DavidWorldpeace

回答

5

進口是靜態的,更新已module.hot.accept被確定之後,你再次渲染完全相同的部件,如TodoApp仍持有舊版本的模塊和HMR意識到並沒有按」 t刷新或更改您應用中的任何內容。

您想使用Dynamic import: import()。爲了使它與babel一起工作,您需要添加babel-plugin-syntax-dynamic-import,否則它會報告語法錯誤,因爲它不會將import用作函數。該react-hot-loader/babel不需要,如果你在你的WebPack配置使用react-hot-loader/patch,所以你在你的.babelrc插件成爲:

"plugins": [ 
    "syntax-dynamic-import" 
] 

在你render()功能,你現在可以導入TodoApp和渲染。

const render =() => { 
    import('./components/TodoApp').then(({ default: Component }) => { 
    ReactDOM.render(
     <AppContainer> 
     <Component /> 
     </AppContainer>, 
     document.querySelector('#main') 
    ); 
    }); 
}; 

render(); 

// Hot Module Replacement API 
if (module.hot) { 
    module.hot.accept('./components/TodoApp', render); 
} 

import()是將與模塊解決一個承諾,你要使用的default出口。


即使上述爲真,那麼的WebPack文檔不要求你使用動態的進口,因爲處理的WebPack ES模塊開箱,在react-hot-loader docs - Webpack 2還描述了,因爲的WebPack也處理HMR它會知道該怎麼做。爲了這個工作,你不能將模塊轉換爲commonjs。您使用["es2015", {"modules": false}]來完成此操作,但是您的webpack配置中還配置了latest預設,這也會轉換模塊。爲避免混淆,您應該在.babelrc中擁有所有babel配置,而不是將某些配置拆分爲加載器選項。

完全從您的webpack配置中刪除babel-loader中的預設,它將工作,因爲您已在.babelrc中擁有必要的預設。 babel-preset-latest已被棄用,如果您想使用這些功能,您應該開始使用babel-preset-env,這也取代了es2015。所以,你在.babelrc預設是:

"presets": [ 
    ["env", {"modules": false}], 
    "react" 
], 
+0

謝謝,這確實是問題!我的'.babelrc'文件中的設置被'webpack.config'文件覆蓋。 – DavidWorldpeace

+0

通過webpack.config使用.babelrc的任何理由?爲什麼不使用webpack.config? –

+1

@JohnLeidegren'.babelrc'適用於使用Babel的所有內容。如果您在webpack配置中配置它,它將只適用於webpack。 webpack之外的Babel有很多用途。一個例子是測試,因爲問題[意外的令牌 - 對現有的React + Web應用程序的讚賞](https://stackoverflow.com/questions/45372145/unexpected-token-jest-for-existing-react-web-包 - 應用程序)顯示。 –

1

入住這issue在GitHub上或使用此在您的index.js:

import React from 'react' 
import ReactDOM from 'react-dom' 
import { AppContainer } from 'react-hot-loader' 

import App from './components/App' 

const render = Component => { 
    ReactDOM.render(
     <AppContainer> 
      <Component/> 
     </AppContainer>, 
     document.getElementById('react-root') 
    ) 
} 

render(App) 

if(module.hot) { 
    module.hot.accept(); 
}