vue-loader icon indicating copy to clipboard operation
vue-loader copied to clipboard

Incorrect HMR behavior with webpack 5 and enabled source maps

Open yura3d opened this issue 4 years ago • 18 comments

Version

16.1.2

Reproduction link

https://github.com/yura3d/vue3-webpack5

Steps to reproduce

  1. Clone GitHub repo.
  2. Run yarn and yarn dev.
  3. Browse localhost:8080, type some symbols to input field and/or press counter button to change component's state.
  4. Open src/App.vue and change something in <template> section.

What is expected?

HMR applies, input field and counter still contain their values as state shouldn't be changed according to docs: https://vue-loader.vuejs.org/guide/hot-reload.html#state-preservation-rules

What is actually happening?

HMR applies.

State is lost when bundling with webpack 5 if devtool is set to something with source maps (source-map, eval-source-map, etc).

State is preserved when devtool doesn't set source maps (false, eval), or when bundling with webpack 4 (even with source maps) and almost the same webpack.config.js.


I see this console output on HMR with webpack 5 and source maps:

log.js:24 [HMR] Updated modules:
log.js:16 [HMR]  - ./src/App.vue?vue&type=script&lang=js
log.js:24 [HMR]  - ./src/App.vue?vue&type=script&lang=js
log.js:24 [HMR]  - ./src/App.vue
log.js:16 [HMR]  - ./src/App.vue?vue&type=template&id=7ba5bd90&scoped=true
log.js:24 [HMR]  - ./src/App.vue?vue&type=template&id=7ba5bd90&scoped=true
log.js:16 [HMR]  - ./src/App.vue?vue&type=style&index=0&id=7ba5bd90&lang=scss&scoped=true
log.js:24 [HMR] App is up to date.
hotModuleReplacement.js:215 [HMR] Reload all css

And this on HMR without source maps:

log.js:24 [HMR] Updated modules:
log.js:16 [HMR]  - ./src/App.vue?vue&type=template&id=7ba5bd90&scoped=true
log.js:24 [HMR]  - ./src/App.vue?vue&type=template&id=7ba5bd90&scoped=true
log.js:16 [HMR]  - ./src/App.vue?vue&type=style&index=0&id=7ba5bd90&lang=scss&scoped=true
log.js:24 [HMR] App is up to date.
hotModuleReplacement.js:215 [HMR] Reload all css

It seems component's JS recompiles even when there are no changes in <script> section if source maps are enabled.

yura3d avatar Feb 16 '21 18:02 yura3d

Same issue is happening for my sample project too

const path = require('path');
const { VueLoaderPlugin } = require('vue-loader');
module.exports = {
  mode: 'development',
  // devtool: 'inline-source-map',
  entry: './src/index.ts',
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      },
      {
        test: /\.css$/,
        use: [
          'vue-style-loader',
          'css-loader'
        ]
      }
    ],
  },
  plugins: [
    new VueLoaderPlugin()
  ],
  resolve: {
    extensions: ['.tsx', '.ts', '.js', '.vue'],
    alias: {
      vue: 'vue/dist/vue.js'
    },
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
  devServer: {
    contentBase: './dist',
    hot: true
  },
};

If I uncomment the devtool part, and I change in template / style part the whole component is replaced thus losing the component state.

ThakurKarthik avatar Jul 02 '21 15:07 ThakurKarthik

This behavior reproduces even with projects created by official Vue CLI 5 (@vue/cli@next, currently in beta), which uses webpack 5 under the hood.

yura3d avatar Jul 02 '21 19:07 yura3d

This also happens with vue-loader 15.9.6 on webpack 5. My reproducer is this repository, which uses Webpack Encore as wrapper in a Symfony project.

bobvandevijver avatar Jul 27 '21 08:07 bobvandevijver

Same issue here. I can reproduce this:

State is preserved when devtool doesn't set source maps (false, eval), or when bundling with webpack 4 (even with source maps) and almost the same webpack.config.js.

hputzek avatar Aug 23 '21 14:08 hputzek

Is there any workaround except disabling devtool options so far?

Imabigcookie avatar Dec 01 '21 12:12 Imabigcookie

@Imabigcookie, you don't need to completely disable devtools, but you do need to set it to source-map and make sure the target is set to web.

nerdoza avatar Dec 01 '21 16:12 nerdoza

@bayssmekanique, i've already tried and it doesn't help

const path = require('path');

const HtmlWebpackPlugin = require('html-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader');

module.exports = (env = {}) => ({
  mode: 'development',
  entry: "./src/index.js",
  devtool: 'source-map',
  target: 'web',
  output: {
    publicPath: 'auto',
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        use: 'vue-loader',
      },
    ],
  },
  plugins: [
    new VueLoaderPlugin(),
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, './public/index.html'),
    })
  ],
  devServer: {
    static: {
      directory: path.join(__dirname, 'public'),
    },
    port: 3000,
    hot: true,
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
      'Access-Control-Allow-Headers': 'X-Requested-With, content-type, Authorization',
    },
  },
});
{
  "name": "core",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "dev": "webpack serve --mode development",
    "build": "webpack --mode production"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "html-webpack-plugin": "^5.5.0",
    "vue-loader": "^15.9.8",
    "vue-template-compiler": "^2.6.14",
    "webpack": "^5.64.4",
    "webpack-cli": "^4.9.1",
    "webpack-dev-server": "^4.6.0"
  },
  "dependencies": {
    "vue": "^2.6.14",
    "vue-router": "^3.5.3",
    "vuex": "^3.6.2"
  }
}

Imabigcookie avatar Dec 01 '21 16:12 Imabigcookie

The bug is still reproducible in 17.0.0, even on fresh setup using the newest @vue/cli 5

yura3d avatar Mar 03 '22 22:03 yura3d

any solution to this? I'm having the same issue. Hot reload only works with devTool=false.

using this combination:

"typescript": "^4.7.4",
"vue-loader": "^17.0.0",
"vue-style-loader": "^4.1.3",
"webpack": "^5.74.0",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.9.3"

felipecarrillo100 avatar Jul 26 '22 11:07 felipecarrillo100

15 months and no fix :(

tmcdos avatar Aug 10 '22 14:08 tmcdos

This nasty bug forces me to revert back to Vue 2 and Webpack 4.

tmcdos avatar Nov 10 '22 10:11 tmcdos

This nasty bug forces me to revert back to Vue 2 and Webpack 4.

If you have the opportunity to try ViteJS, you might never go back to Webpack.

meduzen-immoweb avatar Nov 10 '22 15:11 meduzen-immoweb

This nasty bug forces me to revert back to Vue 2 and Webpack 4.

If you have the opportunity to try ViteJS, you might never go back to Webpack.

Vite has issues, too - it does not allow me to put breakpoints inside my .vue files. I have to add/remove debugger statements. HMR in Vite is not perfect, too - sometimes it just does not notice that I've changed something in my code and I have to reload the page.

tmcdos avatar Nov 10 '22 15:11 tmcdos

@sodatea I debug this today, it caused by select script block return sourcemap at https://github.com/vuejs/vue-loader/blob/next/src/select.ts#L33. The sourcemap which include all content of a vue file, it is different after code change, it will generate different hash at webpack5(used webpack-source at https://github.com/webpack/webpack-sources/blob/main/lib/SourceMapSource.js#L233), so i change template block will caused script block change. The webpack4 worked fine, becasue it is not calculate hash for sourcemap at [email protected], see https://github.com/webpack/webpack-sources/blob/v1.4.3/lib/SourceMapSource.js#L50.

underfin avatar Mar 21 '23 12:03 underfin

@underfin So, do you have any ideas how to fix this bug?

tmcdos avatar Mar 21 '23 13:03 tmcdos

@tmcdos Maybe you can use patch-package to remove https://github.com/webpack/webpack-sources/blob/main/lib/SourceMapSource.js#L233 this line, but might meet sourcemap line error...

underfin avatar Apr 04 '23 04:04 underfin

@tmcdos Maybe you can use patch-package to remove https://github.com/webpack/webpack-sources/blob/main/lib/SourceMapSource.js#L233 this line, but might meet sourcemap line error...

没有别的办法了吗,看上面只能设置 devtool: false 激活热更

a298003154 avatar Jun 27 '23 08:06 a298003154

It looks like I found a workaround - if I move https://github.com/webpack/webpack-sources/blob/main/lib/SourceMapSource.js#L233 between https://github.com/webpack/webpack-sources/blob/9f98066311d53a153fdc7c633422a1d086528027/lib/SourceMapSource.js#L235 and https://github.com/webpack/webpack-sources/blob/9f98066311d53a153fdc7c633422a1d086528027/lib/SourceMapSource.js#L236 so it looks like

if (this._hasOriginalSource) 
{
  hash.update(this._sourceMapAsBuffer); // originally this was outside of the IF block
  hash.update(this._originalSourceAsBuffer);
}

then if I only change something in the component template (e.g. a CSS class) - the script section of the component is not reloaded. I do not know if this is a proper fix because I am totally clueless about the inner workings of Webpack and Webpack-Sources package in particular. I use custompatch to patch webpack-sources in my project.

tmcdos avatar Oct 24 '23 11:10 tmcdos