-
Notifications
You must be signed in to change notification settings - Fork 115
HowToFix
语法错误的原因很多,这里只说最常见的作为 FIS
的 widget 的写法导致的语法错误。
var mod = function () {
// 这里是模块主要代码
}
return mod;
语法错误的原因是在 function
之外使用 return
语句,这是 FIS 1.x
的写法, FIS 2.x
之后的写法见下面的修正代码。
FIS 1.x
的 widget 这里之所以可以这样写,是因为 FIS
在构建后会自动 wrap
一个 function
。
同时兼容 FIS
,又不会产生语法错误的写法是:
// 主要代码同上
// return mod; 这里可以改成如下:
// exports = mod;
// 不过更推荐这种兼容 `commonjs` 的写法:
module.exports = mod;
这个问题通常发生在使用 tab
作为缩进,还有部分是由于前面出现的代码中有不恰当的换行或缩进影响的,具体需要使用 fecs
直接检查查看详细的错误信息。
比较容易发现的,是数组内的元素或函数的参数间换行,当内部需要换行时(比如超过 120 字符或排版美观需要),括号内侧也必须换行--左括号之后换行,右括号之前换行。第一个换行的缩进,需要比左括号所在行多一个缩进,右括号所在行的缩进则与左括号所在行的保持一致。
一个特殊的 case 是使用数组作 HTML 字符拼接时,除满足上面所述的缩进外,其它元素间的最多可以差一个缩进(在兄弟节点时可以相等,子节点时增加一个,父节点结束标记减少一个)。如果数组未被识别为 HTML 字符拼接,可以通过增加注释来标识:
// html
var html = [
objectType,
functionExpressionType,
...
];
JS RULE007 [强制] if / else / for / while / function / switch / do / try / catch / finally
关键字后,必须有一个空格。
文字描述已经很清楚,但很多 RD 同学碰到时可能不明白怎么修改,其实只需在以上关键字后加一个空格即可。
对象创建是指直接使用 {
和 }
创建的对象直接量,一般情况下,在 :
前是不允许有空格而在它之后是必须有空格的。特殊情况是当使用 :
作对象字段的对齐排版时,允许 :
前有空格。如果提示某些属性名后、:
前缺少空格,是由于当前对象的属性大部分相对于 :
对齐了。
除了很长的正则和 URL,这个只能通过添加适当的换行和缩进排版来修复。
- 对于参数超长的,必须在
(
后和)
前换行,)
的缩进跟(
所在行对齐:
// bad
var longFunctionNameHereForTest = function (firstParamForTest, secondParamForTest, thirdParamForTest, fourthParamForTest, fifthParamForTest, sixthParamForTest) {
};
// bad
var longFunctionNameHereForTest = function (
firstParamForTest, secondParamForTest, thirdParamForTest, fourthParamForTest, fifthParamForTest, sixthParamForTest) {
};
// good
var longFunctionNameHereForTest = function (
firstParamForTest, secondParamForTest, thirdParamForTest,
fourthParamForTest, fifthParamForTest, sixthParamForTest
) {
};
// good
var longFunctionNameHereForTest = function (
firstParamForTest,
secondParamForTest,
thirdParamForTest,
fourthParamForTest,
fifthParamForTest,
sixthParamForTest,
seventhParamForTest
) {
};
-
数组项导致的超长也是同样的处理方式。
-
对于 HTML 字符拼接导致超长的,可参考 HTML 标签嵌套方式对齐:
// bad
var html = '<div class="class-for-block"><ul class="class-for-ul"><li><a href="http://www.domain.com/path/to/page/one/"></a></li><li><a href="http://www.domain.com/path/to/page/two/"></a></li></ul></div>';
// bad (jsx)
var html = <div className="class-for-block"><ul className="class-for-ul"><li><a href="http://www.domain.com/path/to/page/one/"></a></li><li><a href="http://www.domain.com/path/to/page/two/"></a></li></ul></div>;
// good
var html = ''
+ '<div class="class-for-block">'
+ '<ul class="class-for-ul">'
+ '<li><a href="http://www.domain.com/path/to/page/one/"></a></li>'
+ '<li><a href="http://www.domain.com/path/to/page/two/"></a></li>'
+ '</ul>'
+ '</div>';
// good
var html = ''
+ '<div class="class-for-block">'
+ '<ul class="class-for-ul">'
+ '<li>'
+ '<a href="http://www.domain.com/path/to/page/one/"></a>'
+ '</li>'
+ '<li>'
+ '<a href="http://www.domain.com/path/to/page/two/"></a>'
+ '</li>'
+ '</ul>'
+ '</div>';
// good (jsx)
var html = (
<div className="class-for-block">
<ul className="class-for-ul">
<li><a href="http://www.domain.com/path/to/page/one/"></a></li>
<li><a href="http://www.domain.com/path/to/page/two/"></a></li>
</ul>
</div>
);
// good (jsx)
var html = (
<div className="class-for-block">
<ul className="class-for-ul">
<li>
<a href="http://www.domain.com/path/to/page/one/"></a>
</li>
<li>
<a href="http://www.domain.com/path/to/page/two/"></a>
</li>
</ul>
</div>
);
- 对于超长的正则和 URL 导致的问题,可以通过注释来屏蔽:
/* eslint-disable max-len */
// 这里是超长的正则或 URL
/* eslint-enable max-len */
例如:
if (thisIsALongExpressionForCodeDemoView &&
thisAnotherLogExpression
)
需要改为
if (thisIsALongExpressionForCodeDemoView
&& thisAnotherLogExpression
)
IIFE
是指马上执行的函数表达式:
var task = function () {
// Code
return result;
}();
var func = (function () {
});
+function () {
// blablabla
}();
必须改成:
// good
var task = (function () {
// Code
return result;
})();
var func = function () {
};
(function () {
// blablabla
})();
使用其他语言的同学,可能习惯使用下划线作为命名中单词的分隔符:
var foo_bar = true;
这里需要改成:
var fooBar = true;
换言之,使用类的构建函数名,必须是大写字母开头。当调用了大写字母开头的函数或方法,但没有使用 new
操作符时,也是不允许的,即便结果正确。
如果是因为使用了第三方的代码,如果加 new
不影响结果的正确性,可以参见 FAQ 中的 《如何修复使用 jQuery
的 DataTable
插件的问题?》,否则需要使用以下方式屏蔽该条规则:
/* eslint-disable new-cap */
-
部分情况同 《JS RULE029 [强制]
类
使用Pascal命名法
》。 -
如果方法属性命名是受 RD 接口影响,需要使用以下方式处理:
/* eslint-disable fecs-camelcase */
// 这里是你的代码
/* eslint-enable fecs-camelcase */
- 如果有固定的模式,比如都以
api_
开头,可以这样配置:
/* eslint fecs-camelcase: [2, {ignore: ["/^api_/"]}] */
ajax.send({
api_site: 'baidu',
api_key: 'djk13-liwfu-bstdj'
});
其中 ignore 的取值是字符串数组,字符串以 /
开始和结束时,将按正则字符匹配处理。
- 如果习惯使用
_
前缀标识私有属性或方法,则需要增加@private
来避免检查:
/**
* 类描述
*
* @class
* @extends Developer
*/
var Fronteer = function () {
Developer.call(this);
/**
* 属性描述
*
* @type {string}
* @private
*/
this._level = 'T12';
// constructor body
};
util.inherits(Fronteer, Developer);
/**
* 方法描述
*
* @private
* @return {string} 返回值描述
*/
Fronteer.prototype._getLevel = function () {
};
当变量名符合 Pascal 命名法,并且值类型为对象字面量时,会要求对象的所有属性名全部字母大写,即使用常量的命名方式。
所以修改方式有两种,一是按规范要求把属性名按常量的命名方式修改(全部字母大写,单词间下划线分隔
),二是调整变量名,比如首字母恢复小写。
意思是除了 String/Number/Boolean 类型的首字母小写,其它类型都大写,如:{Array},{Date},{Function},{Object},{RegExp}。
在文件开始,必须使用 @file
对当前文件作注释(注意文档注释开始行是两个星号 /**
):
/**
* @file 介绍当前文件的说明
* @author name<email>
*/
这种情况通常是漏了某些参数或返回值的注释,可以通过本地运行 fecs
看具体的信息。
对于 Object 类型的参数(比如写配置项时,假设叫 options
),想对参数对象的各个字段作注释时,首先不能漏了参数名(options)的注释,然后对于各个字段注释,需要带上 options.
作前缀:
/**
* 初始化
*
* @param {Object} options 配置项
* @param {boolean} options.disabled 控件的不可用状态
* @param {(string | HTMLElement)} options.main 控件渲染容器
* @param {(string | HTMLElement)} options.target 计算弹出层相对位置的目标对象
* @param {string} options.prefix 控件class前缀,同时将作为main的class之一
* @param {number} options.index 默认激活的标签索引
*/
错误的注释:
/**
* blablabla
*
* @param foo
* @param bar
*/
需要加上参数类型和说明
/**
* blablabla
*
* @param {number} foo 这是 foo 的说明
* @param {boolean} bar 这是 bar 的说明
*/
有三种情况会导致出现该错误。
(一)、使用了非当前文件内定义的全局变量或函数,或者是定义时漏写 var
的变量。
-
使用了 AMD 的项目,应该通过配置 shim 来把非 AMD 模块伪装成 AMD 模块 require。
-
未使用 AMD 的项目,作为临时解决方案,需要把该变量或函数标识为 globals:
/* globals foo */
(二)、错误的写法导致出现的全局变量 bar:
var foo = bar = true;
正确写法应该是:
var foo = true;
var bar = foo;
(三)、在代码中,定义的代码在被使用的代码之后(使用变量或函数时,还没出现定义的代码):
function foo() {
bar();
}
var bar = function () {
console.log(baz);
};
var baz = true;
注意:如果 bar
是函数声明则不会报错。
var foo = true,
bar = false;
需要改为:
var foo = true;
var bar = false;
var foo = function () {
bar();
};
var bar = function () {
// blablabla
};
这种代码的报错主要是因为 bar 的调用在定义之前所致。解决的办法很简单,把 bar 的定义提前:
var bar = function () {
// blablabla
};
var foo = function () {
bar();
};
也可以把 bar 改成函数声明:
var foo = function () {
bar();
};
function bar() {
// blablabla
}
当判断 foo 为 null 或 undefined 时,使用 foo == null
,不是 foo == undefined
。
其实就是为 parseInt
指定第二个参数:
// bad
parseInt(str);
// good
parseInt(str, 10);
主要是防止低级浏览器中的 0
开头的字符默认识别为 8 进制问题:
parseInt('010'); // 8
parseInt('010', 10); // 10
单引号输入时可以少按一个 Shift
键,而在拼写 HTML 字符串时,还可以保留 HTML 属性中的双引号,避免使用 \
转义嵌套的双引号。
var foo = 'bar';
这两个问题通常和《JS RULE030 [强制] 类的 方法 / 属性
使用 Camel命名法
》相关。
当属性中使用下划线时,不需要添加双引号,但是此时会报 030 的问题。正确的做法是除非有某一属性名出现关键字或非字母数字和 $
、_
之外字符时,可以全部添加引号,否则不允许使用引号。
// 不允许添加引号
var foo = {
'bar': true,
'baz': false
};
// 必须全部添加引号
var foo = {
'bar': true,
'foo-baz': false
};
从 Cooder 或 eagle 报的描述中,%s 没有替换为具体的错误信息,所以需要本地运行 fecs
来获取详细的信息再作修改。
和 RULE998 问题一样,需要本地运行 fecs
来获取详细的信息再作修改。
该错误源于对未闭合的标签的检查,示例如下:
<!-- good -->
<ul>
<li>first</li>
<li>second</li>
</ul>
<!-- bad -->
<ul>
<li>first
<li>second
</ul>
不过大多数时候,报告该错误都是因为文件内容并非单纯 HTML 代码,而是某种模板,模板语法破坏了HTML本身的结构,导致一些 HTML 标签被认为没有正确闭合。对于模板文件,建议的做法是修改文件后缀名为 .tpl
或 .tmpl
,避免 HTML 规范检查。如果因为某些原因需要使用 .html
后缀名,可以使用 .tpl.html
,也可以避免检查。
这两条字面上很好理解,要求 head
中第一个子元素为指定字符编码的 meta
标签,紧随其后是 title
标签。
常见的不规范情况包括:
-
meta标签未使用精简形式
精简形式:
<meta charset="UTF-8">
非精简形式(不符规范):
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
-
meta
或title
标签位置不正确,规范明确要求了meta
作为head
的第一个直接子元素,且title
紧随其后
这两条字面上很好理解,要求 head
中第一个子元素为指定字符编码的 meta
标签,紧随其后是 title
标签。
常见的不规范情况包括:
-
meta标签未使用精简形式
精简形式:
<meta charset="UTF-8">
非精简形式(不符规范):
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
-
meta
或title
标签位置不正确,规范明确要求了meta
作为head
的第一个直接子元素,且title
紧随其后
这条 rule 包含两种情况,attr-unsafe-chars
及 id-class-ad-disabled
,因为系统尚未展示错误报告的详细信息,所以这两者目前无法根据错误信息区分。
-
前者不允许在元素属性值中使用不安全字符,具体为
/[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/
。 -
后者不允许在
id
或class
的值中使用ad
(ad
通过-
或_
与其他字符分隔开,形如:ad-
,xx-ad-xx
,xx-ad
,ad_
,xx_ad_xx
,xx_ad
),否则很大可能会被广告插件过滤。
目前的 cooder 会把对内联的 script/style 内容的检查认为是 html 检查结果,所以有时会出现类似
HTML003 [强制] 使用
4
个空格做为一个缩进层级,不允许使用2
个空格 或tab
字符。(Bad indentation (4 instead 0).)
这样的结果。这里的问题实则是内联的 script 内容的缩进错误。
内联的 script/style 内容的缩进要求在标签本身(<script>/<style>)基础上不做增加,即:
<!-- bad -->
<script>
alert('!');
</script>
<!-- good -->
<script>
alert('!');
</script>
必须将font-weight
属性值改为数字,数字值 400 相当于关键字 normal,700 等价于 bold,900 等价于 bolder。
这两条规则看起来似乎有点矛盾,RULE002
说要使用 4
个空格作为缩进,而 RULE046
又说带私有前缀的属性要按长短排列,按冒号的位置来对齐。意思就是带私有前缀的属性可以不使用 4
个空格来作为缩进层级。那么关键词就是 私有前缀
了。但是有时候我们会发现我写的是按照规范来的,为什么还给我报错,而且报的错并不是 RULE002
和 RULE046
。我们来看如下示例:
.ani {
-webkit-animation: spin 2s infinite linear;
-moz-animation: spin 2s infinite linear;
-ms-animation: spin 2s infinite linear;
-o-animation: spin 2s infinite linear;
animation: spin 2s infinite linear;
color: #fff;
}
可以看到这段代码是完全符合 RULE002
和 RULE046
的,但是用 fecs
来检测一样会报错出来。
fecs 执行结果如下:
fecs INFO test.css (2 messages)
fecs ERROR → line 4, col 9, rule 998: [强制] 存在兼容性问题或运行时错误。(Current property `-ms-animation` is not existed) (property-not-existed)
fecs ERROR → line 5, col 10, rule 998: [强制] 存在兼容性问题或运行时错误。(Current property `-o-animation` is not existed) (property-not-existed)
信息很明确,我们就知道了其实是 -ms-animation
和 -o-animation
这两个属性不存在导致的。那么问题就来了,如何判断一个带私有前缀的属性是否存在呢?
在工具中,存放带私有前缀的属性的代码在 这里。web 技术发展这么快,各个浏览器厂商也会时不时更新自己产品的实现,当然也包括对一些 CSS
属性的支持等等。因此,这个文件内容也不是一成不变的,也是会时常更新的。如果万一更新得不及时导致代码报错,那么你可以使用行内注释把这两条规则注释掉 /* csshint-disable block-indent vendor-prefixes-sort */
,但是这样写的话,当前你的这个文件就不会得到这两个规则的检测了。因此更好的方式是你提 issue 然后我们更新属性的集合。
当错误超出以上范围,并且认为不需要修复代码,可按以下步骤处理:
-
安装
fecs
-
运行
fecs [files] --rule
,记下打印出的错误行尾显示的规则名。 -
在文件头部使用
/* linter-disable ruleName */
来屏蔽对应的规则。其中,JavaScript、CSS 和 HTML 文件分别对应的 linter 为 eslint、csshint 和 htmlcs。 -
如果只需要对指定代码块作规则屏蔽,可以在代码块结束后使用
/* linter-enable ruleName */
来恢复规则检查。
-
当省略 ruleName 时,意味着关闭对当前文件的检查,但 ruleName 不存在时,则忽略该条设置。
-
在 HTML 中,
/*
和*/
应该换成<!--
和-->
。
自动豁免主要是指在 eagle
中自动跳过检查,分为目录、第三方和文件名三种方式。
被检查代码是压缩或编译后的代码、第三方类库、mock 数据或测试文件等,可以归类放置于以下目录内:
- test tests
- dist output
- dep third_party thirdsrc
- mock mocks mockup mockups
- demo
- tool tools
- doc docs
- node_modules bower_components
以下匹配的第三方类库会自动豁免:
- jquery(..+)?.(js|css)$
- tangram.js echarts.js esl.js
- angular.js backbone.js ember.js knockout.js
- bootstrap.js
- yui.js
- zepto.js
- when.js
- moment.js
- html5shiv.js
- typo.css
对于识别为压缩后文件或模板文件的后缀自动豁免:
- *.min.js, *.m.js
- *.mock.js, *.mockup.js
- *.min.css, *.m.css
- *.min.html, *.m.html, *.tpl.html
// Learn TypeScript: // - https://docs.cocos.com/creator/manual/en/scripting/typescript.html // Learn Attribute: // - https://docs.cocos.com/creator/manual/en/scripting/reference/attributes.html // Learn life-cycle callbacks: // - https://docs.cocos.com/creator/manual/en/scripting/life-cycle-callbacks.html
import AssetMgr from './framework/AssetMgr'; import AudioMgr from './framework/AudioMgr'; import EventMgr from './framework/EventMgr'; import ObjectPoolMgr from './framework/ObjectPoolMgr'; import TimerMgr from './framework/TimerMgr'; import UIMgr from './framework/UIMgr'; import Instance from './framework/utils/Instance'; import PropMgr from './logic/common/PropMgr'; import UserInfo from './logic/common/UserInfo'; import ArtDataMgr from './logic/data/ArtDataMgr'; import ConfigArtGroupMgr from './logic/data/ConfigArtGroupMgr'; import ConfigArtMgr from './logic/data/ConfigArtMgr'; import ConfigMgr from './logic/data/ConfigMgr'; import SaveDataMgr from './logic/data/SaveDataMgr'; import HttpLogicMgr from './logic/net/HttpLogicMgr'; import GridMgr from './logic/painting/GridMgr'; import PaintingGridDataMgr from './logic/painting/PaintingGridDataMgr'; import PaintingPaletteDataMgr from './logic/painting/PaintingPaletteDataMgr'; import StarBlinkEffectMgr from './logic/ui/effects/StarBlinkEffectMgr';
const { ccclass, property } = cc._decorator;
@ccclass export default class App extends cc.Component { static UIRoot: cc.Node = null; static timerMgr: TimerMgr = null; static eventMgr: EventMgr = null; static assetMgr: AssetMgr = null; static uiMgr: UIMgr = null; static poolMgr: ObjectPoolMgr = null; static httpMgr: HttpLogicMgr = null; static audioMgr: AudioMgr = null; static userInfo: UserInfo = null; static gridMgr: GridMgr = null; static paintingPaletteDataMgr: PaintingPaletteDataMgr = null; static saveDataMgr: SaveDataMgr = null; static paintingGridDataMgr: PaintingGridDataMgr = new PaintingGridDataMgr(); static propMgr: PropMgr = null; static artDataMgr: ArtDataMgr = null; private static configMgr: ConfigMgr = null; static configArtMgr: ConfigArtMgr = null; static configArtGroupMgr: ConfigArtGroupMgr = null;; /** * 测试网络开关 / static isOpenNetwork: boolean = true; /* * 是否是链接本地 / static isLocalNetwork: boolean = true; /* * 是否开启网络log */ static isOpenNetworkLog: boolean = true;
async onLoad() {
App.UIRoot = cc.find('Canvas/UIRoot');
// cc.debug.setDisplayStats(false);
// cc.game.setFrameRate(30)
// 注册单例
App.eventMgr = Instance.get(EventMgr);
App.timerMgr = Instance.get(TimerMgr);
App.assetMgr = Instance.get(AssetMgr);
App.poolMgr = Instance.get(ObjectPoolMgr);
App.httpMgr = Instance.get(HttpLogicMgr);
App.audioMgr = Instance.get(AudioMgr);
App.userInfo = Instance.get(UserInfo);
App.paintingGridDataMgr = Instance.get(PaintingGridDataMgr);
App.paintingPaletteDataMgr = Instance.get(PaintingPaletteDataMgr);
App.propMgr = Instance.get(PropMgr);
App.saveDataMgr = Instance.get(SaveDataMgr);
App.artDataMgr = Instance.get(ArtDataMgr);
App.gridMgr = Instance.get(GridMgr);
/** ----初始化数据库 */
const dbOk = await App.saveDataMgr.init(UserInfo.PaintingDataKey);// 增加新的数据表需要同时修改版本号
console.log('dbOk = ' + dbOk);
// App.userInfo.clearAllDatas()
/** ----加载数据 */
App.userInfo.loadData();
/** ----初始化ui管理 */
// 注册事件监听
App.eventMgr.registerOwner(UIMgr);
// 初始化ui管理类
App.uiMgr = Instance.get(UIMgr).init(App.UIRoot);
/** ----加载art数据 编辑器生成的 */
await App.artDataMgr.loadData();
await this.cacheViews(['PaintingView']);
/** ----初始化configs */
App.configMgr = Instance.get(ConfigMgr);
await App.configMgr.loadConfig('dataConfigs');
App.configArtGroupMgr = Instance.get(ConfigArtGroupMgr).init(App.configMgr.getTable('artGroup'));
App.configArtMgr = Instance.get(ConfigArtMgr).init(App.configMgr.getTable('art'));
/** ----初始化tile缓存 */
let prefab = await App.assetMgr.load('prefabs/TiledTile', cc.Prefab);
App.poolMgr.createObjectPool('TiledTile', (prefab as cc.Prefab), 110 * 110 * 4);
prefab = await App.assetMgr.loadPrefab('prefabs/StarArrayEffect');
App.poolMgr.createObjectPool('StarArrayEffect', (prefab as cc.Prefab), 110 * 110 * 4);
prefab = await App.assetMgr.loadPrefab('prefabs/StarBlinkEffect');
App.poolMgr.createObjectPool('StarBlinkEffect', (prefab as cc.Prefab), 300);
/** ----进入游戏 */
App.uiMgr.showView('HomeBottomView', 0);
cc.game.setFrameRate(30);
}
private async cacheViews(names: string[]) {
for (let index = 0; index < names.length; index++) {
const name = names[index];
const Prefab = await App.assetMgr.load(`ui/${name}`, cc.Prefab);
App.poolMgr.createObjectPool(name, (Prefab as cc.Prefab), 1);
}
}
update(dt) {
App.timerMgr.update(dt);
}
/**
* 显示绘制界面
* @param data
*/
static async GotoPainting(data: any) {
App.userInfo.paintingRecord(data.id);
App.userInfo.saveData();
await App.uiMgr.showView('PaintingView', data);
App.uiMgr.hideView('HomeBottomView');
App.uiMgr.hideView('CutImageView');
// cc.game.setFrameRate(60);
}
/**
* 回到主界面
*/
static async GotoHome() {
if (!App.uiMgr.isViewShow('HomeBottomView')) {
await App.uiMgr.showView('HomeBottomView');
}
}
}