blog icon indicating copy to clipboard operation
blog copied to clipboard

使用webpack-dev-server在移动端调试时,出现"SyntaxError: Use of const in strict mode."的问题

Open mrdulin opened this issue 7 years ago • 27 comments

最近在移动端使用nginx反向代理配合webpack-dev-servercharles调试时,发现部分手机打不开页面,现象是白屏,或是页面不正常(包括样式等)。

已经排除各种配置的问题。

推断可能是某些代码在老旧机型上不兼容,直接报错,导致程序crash,在入口模板html中插入了一段全局错误捕捉脚本:

window.onerror = function(message) {
  alert(message)
}

这里需要注意一点,先监听事件,后续的错误才会触发该事件。

再次进入要调试的页面,弹出SyntaxError: Use of const in strict mode.错误。

原因:

webpack-dev-server2.8.0版本开始,注入到bundle.jsjs包含了es6语法,低版本webview对es6语法支持有限,兼容性较差,语法报错导致程序crash(白屏和页面不正常的原因),项目使用的版本是2.9.1

查看bundle.js可见如下代码:

/* 2 */
/*!*******************************************************************!*\
  !*** multi (webpack)-dev-server/client?http://0.0.0.0:3000 ./app ***!
  \*******************************************************************/
/*! dynamic exports provided */
/*! all exports used */
/***/ (function(module, exports, __webpack_require__) {

__webpack_require__(/*! /Users/elsa/workspace/webpack/webpack2.x+/node_modules/webpack-dev-server/client/index.js?http://0.0.0.0:3000 */3);
module.exports = __webpack_require__(/*! /Users/elsa/workspace/webpack/webpack2.x+/webpack-dev-server/在老旧浏览器报错的问题/app */25);


/***/ }),

这是webpack-dev-server设置inline:true时注入到bundle.js文件中的,通过websocket通知浏览器进行livereload__webpack_require__(3)这一句去加载node_modules/webpack-dev-server/client/index.js文件,该文件2.7.12.8.0的源码如下:

https://github.com/webpack/webpack-dev-server/blob/v2.8.0/client/index.js https://github.com/webpack/webpack-dev-server/blob/v2.7.1/client/index.js

对比可知,2.8.0版本开始,该文件使用了es6constlet语法,__然而,我们开发的时候,使用webpack编译时,babel-loader一般都会指定要排除的目录如下:

{
        test: /\.js$/,
        exclude: /(node_modules)/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: ['es2015'],
            }
          }
        ]
      }

所以,babel-loader并不会将node_modules/webpack-dev-server/client/index.js转换为es5的语法。

解决方案:

  1. 通过降级webpack-dev-server2.7.1版本 - 测试通过
  2. 使用babel-loader时,在非生产环境配置中,额外指定对node_modules/webpack-dev-server/client/index.js脚本的转换 - 测试通过

参考链接:

https://github.com/webpack/webpack-dev-server/issues/1105 https://github.com/webpack/webpack-dev-server#caveats

mrdulin avatar Nov 22 '17 13:11 mrdulin

如何降级webpack-dev-server到2.7.1版本?

wtl1019 avatar Dec 08 '17 04:12 wtl1019

@wtl1019

Those wishing to support oldIE should stick with version 2.7.1.

所以:

  1. npm rm webpack-dev-server -D
  2. npm i [email protected] -D

指定了特定版本,就算不用npm 5+以上的版本生成package-lock.json文件,或者使用yarn生成yarn.lock文件,团队其他人安装依赖的时候,安装的依旧是指定版本的依赖包。

mrdulin avatar Dec 08 '17 08:12 mrdulin

使用babel-loader时,额外指定对webpack-dev-server/client/index.js脚本的转换 - 测试通过

请问这个是怎样配置的?

yikuo123 avatar Jan 02 '18 07:01 yikuo123

完美解决问题。满分。 perfect resovle problems, good job!

BestNathan avatar Jan 15 '18 04:01 BestNathan

npm rm webpack-dev-server -D npm i [email protected] -D

谢谢!搞了一下午! 完美解决问题。。

ReturnWTF avatar Jan 17 '18 10:01 ReturnWTF

express 怎么解决?

guofuming avatar Jan 18 '18 01:01 guofuming

之前页面上有2条const 信息报错,把webpack-dev-server降级到2.7.1后,还有一个这样的报错,怎么处理呀

leaf1992 avatar Jan 22 '18 12:01 leaf1992

@yikuo123

  module: {
    rules: [
      {
        test: /\.js$/,
        include: [
          src,
          path.resolve(__dirname, 'node_modules/webpack-dev-server')
        ],
        loader: 'babel-loader'
      }
    ]
  }

mrdulin avatar Jan 25 '18 13:01 mrdulin

@mrdulin 非常感谢

yikuo123 avatar Jan 26 '18 09:01 yikuo123

@guofuming

例如有使用es6+编写的server.js,或者ts编写的server.ts,服务端入口文件,通过webpack和babel-loader,ts-loader编译打包到./build/server.js, 使用nodemon或者pm2运行./build/server.js文件。注意node.js版本对语法的支持

此类兼容性问题,解决方法不外乎进行编译转换成兼容性更好的代码,添加polyfill,或者舍弃对低版本环境的兼容

关于编译后server.js调试时,如何通过sourcemap映射到源码server.ts?

一个方案:使用vscode,在launch.json文件中配置,具体配置自行google

mrdulin avatar Jan 30 '18 14:01 mrdulin

使用[email protected]后存在同样的问题

我遇到了同样的问题,但降级webpack后问题依然存在,顺着const的思路找下去,在打包后的vendor文件中发现了两个包含const语法的库[email protected][email protected]。其中dom7是swiper的依赖,由此可知是新版的swiper在打包时混入了es6的语法,[email protected]使用了swiper4所以导致了这个问题

解决方法:在babel-loader的include中添加需要额外解析的类库

    {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [
          ...,
          resolve('node_modules/dom7/dist/dom7.modular.js'),
          resolve('node_modules/swiper/dist/js/')
        ]
    }

大家可以在尝试build之后去打包文件中搜索含有es6语法的第一条语句,查看语句上方所在库的入口文件地址

// CONCATENATED MODULE: ./node_modules/xxx

./node_modules/xxx就是需要额外处理的文件,把它添加到include就可以啦

LiHwsqh avatar Feb 23 '18 04:02 LiHwsqh

@mrdulin src怎么引用?

Liqiankun avatar May 15 '18 02:05 Liqiankun

Hello, My client.js file content is like this.

/* global __resourceQuery WorkerGlobalScope */ var url = require("url"); var stripAnsi = require("strip-ansi"); var log = require("loglevel") var socket = require("./socket"); var overlay = require("./overlay");

function getCurrentScriptSource() { // document.currentScript is the most accurate way to find the current script, // but is not supported in all browsers. if(document.currentScript) return document.currentScript.getAttribute("src"); // Fall back to getting all scripts in the document. var scriptElements = document.scripts || []; var currentScript = scriptElements[scriptElements.length - 1]; if(currentScript) return currentScript.getAttribute("src"); // Fail as there was no script to use. throw new Error("[WDS] Failed to get current script source"); }

var urlParts; if(typeof __resourceQuery === "string" && __resourceQuery) { // If this bundle is inlined, use the resource query to get the correct url. urlParts = url.parse(__resourceQuery.substr(1)); } else { // Else, get the url from the

var hot = false; var initial = true; var currentHash = ""; var useWarningOverlay = false; var useErrorOverlay = false;

var INFO = "info"; var WARNING = "warning"; var ERROR = "error"; var NONE = "none";

// Set the default log level log.setDefaultLevel(INFO);

// Send messages to the outside, so plugins can consume it. function sendMsg(type, data) { if( typeof self !== "undefined" && (typeof WorkerGlobalScope === "undefined" || !(self instanceof WorkerGlobalScope)) ) { self.postMessage({ type: "webpack" + type, data: data }, "*"); } }

var onSocketMsg = { hot: function() { hot = true; log.info("[WDS] Hot Module Replacement enabled."); }, invalid: function() { log.info("[WDS] App updated. Recompiling..."); sendMsg("Invalid"); }, hash: function(hash) { currentHash = hash; }, "still-ok": function() { log.info("[WDS] Nothing changed.") if(useWarningOverlay || useErrorOverlay) overlay.clear(); sendMsg("StillOk"); }, "log-level": function(level) { var hotCtx = require.context("webpack/hot", false, /^./log$/); if(hotCtx.keys().length > 0) { hotCtx("./log").setLogLevel(level); } switch(level) { case INFO: case ERROR: log.setLevel(level); break; case WARNING: log.setLevel("warn"); // loglevel's warning name is different from webpack's break; case NONE: log.disableAll(); break; default: log.error("[WDS] Unknown clientLogLevel '" + level + "'"); } }, "overlay": function(overlay) { if(typeof document !== "undefined") { if(typeof(overlay) === "boolean") { useWarningOverlay = false; useErrorOverlay = overlay; } else if(overlay) { useWarningOverlay = overlay.warnings; useErrorOverlay = overlay.errors; } } }, ok: function() { sendMsg("Ok"); if(useWarningOverlay || useErrorOverlay) overlay.clear(); if(initial) return initial = false; reloadApp(); }, "content-changed": function() { log.info("[WDS] Content base changed. Reloading...") self.location.reload(); }, warnings: function(warnings) { log.warn("[WDS] Warnings while compiling."); var strippedWarnings = warnings.map(function(warning) { return stripAnsi(warning); }); sendMsg("Warnings", strippedWarnings); for(var i = 0; i < strippedWarnings.length; i++) log.warn(strippedWarnings[i]); if(useWarningOverlay) overlay.showMessage(warnings);

	if(initial) return initial = false;
	reloadApp();
},
errors: function(errors) {
	log.error("[WDS] Errors while compiling. Reload prevented.");
	var strippedErrors = errors.map(function(error) {
		return stripAnsi(error);
	});
	sendMsg("Errors", strippedErrors);
	for(var i = 0; i < strippedErrors.length; i++)
		log.error(strippedErrors[i]);
	if(useErrorOverlay) overlay.showMessage(errors);
},
error: function(error) {
	log.error(error);
},
close: function() {
	log.error("[WDS] Disconnected!");
	sendMsg("Close");
}

};

var hostname = urlParts.hostname; var protocol = urlParts.protocol;

//check ipv4 and ipv6 all hostname if(hostname === "0.0.0.0" || hostname === "::") { // why do we need this check? // hostname n/a for file protocol (example, when using electron, ionic) // see: https://github.com/webpack/webpack-dev-server/pull/384 if(self.location.hostname && !!~self.location.protocol.indexOf("http")) { hostname = self.location.hostname; } }

// hostname can be empty when the script path is relative. In that case, specifying // a protocol would result in an invalid URL. // When https is used in the app, secure websockets are always necessary // because the browser doesn't accept non-secure websockets. if(hostname && (self.location.protocol === "https:" || urlParts.hostname === "0.0.0.0")) { protocol = self.location.protocol; }

var socketUrl = url.format({ protocol: protocol, auth: urlParts.auth, hostname: hostname, port: (urlParts.port === "0") ? self.location.port : urlParts.port, pathname: urlParts.path == null || urlParts.path === "/" ? "/sockjs-node" : urlParts.path });

socket(socketUrl, onSocketMsg);

var isUnloading = false; self.addEventListener("beforeunload", function() { isUnloading = true; });

function reloadApp() { if(isUnloading) { return; } if(hot) { log.info("[WDS] App hot update..."); var hotEmitter = require("webpack/hot/emitter"); hotEmitter.emit("webpackHotUpdate", currentHash); if(typeof self !== "undefined" && self.window) { // broadcast update to window self.postMessage("webpackHotUpdate" + currentHash, "*"); } } else { log.info("[WDS] App updated. Reloading..."); self.location.reload(); } }

When using babel-loader, additionally specify the conversion to the webpack-dev-server/client/index.js script - test passed

How is this configured? I removed version2.10 and install version 2.7.1 now.

hakunamatata0829 avatar Jul 20 '18 07:07 hakunamatata0829

npm rm webpack-dev-server -D
npm i [email protected] -D

降级后还是报错

Use of const in strict mode.
at Object.webpackJsonp../node_modules/vue-baidu-map/components/base/bindEvent.js (http://192.168.0.14:8080/0.js:30:1)
at __webpack_require__ (http://192.168.0.14:8080/app.js:708:30)
at fn (http://192.168.0.14:8080/app.js:113:20)
at Object.eval ([module]:1:123)
at Object.eval ([module]:372:30)
at Object.webpackJsonp../node_modules/babel-loader/lib/index.js!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./node_modules/vue-baidu-map/components/map/Map.vue (http://192.168.0.14:8080/0.js:7:1)
at __webpack_require__ (http://192.168.0.14:8080/app.js:708:30)
at fn (http://192.168.0.14:8080/app.js:113:20)
at Object.eval ([module]:1:124)
at Object.webpackJsonp../node_modules/vue-baidu-map/components/map/Map.vue (http://192.168.0.14:8080/0.js:62:1)

[vue-router] Failed to resolve async component default: SyntaxError: Use of const in strict mode.

[vue-router] uncaught error during route navigation:

[WDS] Disconnected!
at Object.close ([module]:176:9)
at SockJS.onclose ([module]:17:16)
at SockJS.EventTarget.dispatchEvent ([module]:170:20)
at SockJS.eval ([module]:965:10)

JakeWoki avatar Jul 26 '18 08:07 JakeWoki

@JakeWoki 降级后,是node_modules/webpack-dev-server/client/index.js文件的语法降级到es5,请确保其他js文件被编译转换为es5语法。

mrdulin avatar Jul 26 '18 08:07 mrdulin

@mrdulin 怎么做?求教

JakeWoki avatar Jul 26 '18 08:07 JakeWoki

@LiHwsqh 我也在vendor里找到const字眼了,但你是怎么知道这是由哪个包导致的呢?毕竟vendor里都被压缩了

Necroogre avatar Jul 27 '18 06:07 Necroogre

@Necroogre 尝试在webpack.config.js配置中加入pathinfo,会在打包出来的文件中生成各个模块的信息,可以暂时关闭压缩混淆功能,方便排查问题。

https://webpack.js.org/configuration/output/#output-pathinfo

mrdulin avatar Oct 19 '18 02:10 mrdulin

@mrdulin Hi, 我也遇到了类似的问题,不过不是const报错,而是在ie9上显示Map 未定义,文件指向bundle.js,但是我又不想降级版本,所以我就试着去用babel编译,但是并没有效果,然后我又在index.js里面引入全局的map支持,但是依然没有效果,您知道是怎么回事吗? 详情链接:Map is undefined in ie9

kwzm avatar Nov 30 '18 03:11 kwzm

@kwzm 可以参考这个https://github.com/mrdulin/blog/issues/53, 总体思路是对于不同平台(mobile, PC),不同设备(apple, samsung, oppo, vivo, 华为),不同系统(ios8, ios9, android4.3),不同浏览器,不同版本的浏览器和webview。哪个es6新的数据类型,新的API,新的语法报错,就针对哪个加入polyfill,shim或者针对性的使用babel进行编译转换工作。

调试工具:chrome://inspect, Safari web inspector, charles, APM,云测系统等等。 调试方法和原则:

  1. 通过注释大法,或者抽象出来这个问题,单独模拟一个环境来测试,缩小问题范围,排除干扰
  2. 定位问题,webpack打包不要混淆压缩,开启pathinfo,加入全局异常捕获方法,加入足够的可以帮助你准确定位问题的提示信息和日志。

mrdulin avatar Nov 30 '18 04:11 mrdulin

@mrdulin 非常感谢,我去试试。

kwzm avatar Nov 30 '18 06:11 kwzm

@mrdulin 您好,开启pathinfo后输出的信息是在bundle里面吗,我怎么没找到呢?

kwzm avatar Nov 30 '18 07:11 kwzm

@kwzm 是的, bundle.js中每个打包进来模块开头都会有包含了模块路径的注释信息,搜索一下Map这个新的数据类型,以及你引入的core-js/es/map,看看是否被编译转换,或被正确的polyfill.

需要注意下时效性,由于react版本和webpack版本的问题,部分配置和结果可能和我写这篇文章的时候不一致。

mrdulin avatar Nov 30 '18 07:11 mrdulin

@LiHwsqh 是因为只有这两个组件里面有const,却没有被babel编译?直接incluede node_modules是不是就解决所有组件中的es6语法问题

jizhidexiaohuang avatar Jan 16 '19 08:01 jizhidexiaohuang

降级到2.7.1还是报错,SyntaxError: Unexpected keyword 'const'. Const declarations are not supported in strict mode.怎么解决

zzialx avatar Nov 05 '19 08:11 zzialx

SyntaxError: Unexpected keyword 'const'. Const declarations are not supported in strict mode 怎么解决ios系统10以下,页面打开白屏

zmmmmm5555 avatar Jul 01 '20 06:07 zmmmmm5555

使用babel-loader时,额外指定对webpack-dev-server/client/index.js脚本的转换 - 测试通过

请问这个是怎样配置的?

Can you show me

yjwSurCode avatar Feb 05 '21 01:02 yjwSurCode