mrdulin / blog

Personal Blog - 博客 | 编程技术,软件,生活

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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

mrdulin opened this issue · 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脚本的转换 - 测试通过

参考链接:

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

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

@wtl1019

Those wishing to support oldIE should stick with version 2.7.1.

所以:

  1. npm rm webpack-dev-server -D
  2. npm i webpack-dev-server@2.7.1 -D

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

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

请问这个是怎样配置的?

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

npm rm webpack-dev-server -D
npm i webpack-dev-server@2.7.1 -D

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

express 怎么解决?

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

@yikuo123

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

@mrdulin 非常感谢

@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

使用vue-awesome-swiper@3.0.1后存在同样的问题

我遇到了同样的问题,但降级webpack后问题依然存在,顺着const的思路找下去,在打包后的vendor文件中发现了两个包含const语法的库dom7@2.0.1swiper@4.1.0。其中dom7是swiper的依赖,由此可知是新版的swiper在打包时混入了es6的语法,vue-awesome-swiper@3.0.1使用了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就可以啦

commented

@mrdulin src怎么引用?

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 <script> this file was called with.
var scriptHost = getCurrentScriptSource();
scriptHost = scriptHost.replace(//[^\/]+$/, "");
urlParts = url.parse((scriptHost ? scriptHost : "/"), false, true);
}

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: webpack/webpack-dev-server#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.

npm rm webpack-dev-server -D
npm i webpack-dev-server@2.7.1 -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 降级后,是node_modules/webpack-dev-server/client/index.js文件的语法降级到es5,请确保其他js文件被编译转换为es5语法。

@mrdulin 怎么做?求教

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

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

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

commented

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

@kwzm 可以参考这个#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,加入全局异常捕获方法,加入足够的可以帮助你准确定位问题的提示信息和日志。
commented

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

commented

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

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

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

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

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

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

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

请问这个是怎样配置的?

Can you show me