blog
blog copied to clipboard
使用webpack-dev-server在移动端调试时,出现"SyntaxError: Use of const in strict mode."的问题
最近在移动端使用nginx
反向代理配合webpack-dev-server
和charles
调试时,发现部分手机打不开页面,现象是白屏,或是页面不正常(包括样式等)。
已经排除各种配置的问题。
推断可能是某些代码在老旧机型上不兼容,直接报错,导致程序crash,在入口模板html中插入了一段全局错误捕捉脚本:
window.onerror = function(message) {
alert(message)
}
这里需要注意一点,先监听事件,后续的错误才会触发该事件。
再次进入要调试的页面,弹出SyntaxError: Use of const in strict mode.
错误。
原因:
webpack-dev-server
从2.8.0
版本开始,注入到bundle.js
中js
包含了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.1
和2.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
版本开始,该文件使用了es6
的const
和let
语法,__然而,我们开发的时候,使用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
的语法。
解决方案:
- 通过降级
webpack-dev-server
到2.7.1
版本 - 测试通过 - 使用
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
如何降级webpack-dev-server到2.7.1版本?
@wtl1019
Those wishing to support oldIE should stick with version 2.7.1.
所以:
-
npm rm webpack-dev-server -D
-
npm i [email protected] -D
指定了特定版本,就算不用npm 5+
以上的版本生成package-lock.json
文件,或者使用yarn
生成yarn.lock
文件,团队其他人安装依赖的时候,安装的依旧是指定版本的依赖包。
使用babel-loader时,额外指定对webpack-dev-server/client/index.js脚本的转换 - 测试通过
请问这个是怎样配置的?
完美解决问题。满分。 perfect resovle problems, good job!
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
使用[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就可以啦
@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
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.
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 降级后,是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
@mrdulin Hi, 我也遇到了类似的问题,不过不是const报错,而是在ie9上显示Map 未定义,文件指向bundle.js,但是我又不想降级版本,所以我就试着去用babel编译,但是并没有效果,然后我又在index.js里面引入全局的map支持,但是依然没有效果,您知道是怎么回事吗? 详情链接:Map is undefined in ie9
@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
,云测系统等等。
调试方法和原则:
- 通过注释大法,或者抽象出来这个问题,单独模拟一个环境来测试,缩小问题范围,排除干扰
- 定位问题,
webpack
打包不要混淆压缩,开启pathinfo
,加入全局异常捕获方法,加入足够的可以帮助你准确定位问题的提示信息和日志。
@mrdulin 非常感谢,我去试试。
@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