使用webpack-dev-server在移动端调试时,出现"SyntaxError: Use of const in strict mode."的问题
mrdulin opened this issue · comments
最近在移动端使用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
脚本的转换 - 测试通过
参考链接:
webpack/webpack-dev-server#1105
https://github.com/webpack/webpack-dev-server#caveats
如何降级webpack-dev-server到2.7.1版本?
Those wishing to support oldIE should stick with version 2.7.1.
所以:
npm rm webpack-dev-server -D
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后,还有一个这样的报错,怎么处理呀
module: {
rules: [
{
test: /\.js$/,
include: [
src,
path.resolve(__dirname, 'node_modules/webpack-dev-server')
],
loader: 'babel-loader'
}
]
}
例如有使用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.1
与swiper@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就可以啦
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
语法。
@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 可以参考这个#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
,加入全局异常捕获方法,加入足够的可以帮助你准确定位问题的提示信息和日志。
@kwzm 是的, bundle.js
中每个打包进来模块开头都会有包含了模块路径的注释信息,搜索一下Map
这个新的数据类型,以及你引入的core-js/es/map
,看看是否被编译转换,或被正确的polyfill.
需要注意下时效性,由于react
版本和webpack
版本的问题,部分配置和结果可能和我写这篇文章的时候不一致。
降级到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