Cocos Creator
development, this article carefully explores the necessity of paying attention to the JavaScript API
, and how to use tools and Polyfill
to circumvent the compatibility problem of the Cocos Creator
1. Introduction: The difference of JavaScript
JavaScript
virtual machine ( VM
) used by different browsers and mobile devices is very different, and the supported API
is also very different.
Let's take a look at the Cocos Creator
JavaScript VM
at each end:
- For
iOS
client andMac
clients: inCocos Creator 1.6
and earlier,Cocos Creator
has been to use non-system nativeSpiderMonkey
asJS VM
; from1.7
start,Cocos Creator
introducedJSB 2.0
, supportV8、JavaScriptCore
otherJS VM
. ThusCocos Creator
putiOS
end andMac
endJS VM
have changed the system comesJavaScriptCore
, in order to achieve savings package body; to2.1.3
,Cocos Creator
turnMac
endJS VM
switch toV8
, to improve application performance. - For
Android
client andWindows
clients: inCocos Creator 1.6
and earlier,Cocos Creator
also useSpiderMonkey
asJS VM
; from1.7
start, thanks toJSB 2.0
,V8
becameAndroid
andWindows
clientJS VM
. - For
Web
: Use the browser'sJavaScript VM
to parse theJavaScript
code.
It can be seen from the above that due to the JS VM
, the same code may be very different when it runs on different platforms. In order to allow our products to be used by as many users as possible, we need to always pay attention to the compatibility of JavaScript
API
For example: The fetch()
method is an API XMLHTTPRequest
Compared with the latter, its advantage is that it is more readable and can easily use Promise to write more elegant code.
fetch(
'http://domain/service',
{ method: 'GET' }
)
.then( response => response.json() )
.then( json => console.log(json) )
.catch( error => console.error('error:', error) );
However, fetch()
method does not support all of the IE
browsers, can not in 2017
years ago Chrome、Firefox
and Safari
run on version. When a large part of your users are the above-mentioned users, you need to consider prohibiting the use of fetch()
API
, and return to the embrace of XMLHTTPRequest
In the development stage, it is unreliable to API
A more reliable way is to use tools to automate scanning. For example, eslint-plugin-compat
described below.
Two, use eslint-plugin-compat
eslint-plugin-compat
is ESLint
a widget by former uber
engineer Amila Welihinda
development. It can help find incompatibility API
in the code.
The following describes how to access eslint-plugin-compat
in the project.
2.1 Installation eslint-plugin-compat
Installing eslint-plugin-compat
similar to installing other ESLint
plug-ins:
$ npm install eslint-plugin-compat --save-dev
You can also install the browserslist
and caniuse-lite
$ npm install browserslist caniuse-lite --save-dev
2.2 Modify ESLint
configuration
After that, we need to modify ESLint
, plus the use of the plug-in:
// .eslintrc.json
{
"extends": "eslint:recommended",
"plugins": [
"compat"
],
"rules": {
//...
"compat/compat": 2
},
"env": {
"browser": true
// ...
},
// ...
}
2.3 Configure the target operating environment
Configure the target operating environment by adding the browserslist
field in package.json
Example:
{
// ...
"browserslist": ["chrome 70", "last 1 versions", "not ie <= 8"]
}
The above value means Chrome
version 70
or above, or the latest version of each browser, or non- ie 8
and below. The filling format here follows a set of description specifications defined browserslist
( https://github.com/browserslist/browserslist browserslist
is a set of tools that describe the target operating environment of the product. It is widely used in various browser/mobile compatibility support tools, such as eslint-plugin-compat
, babel、Autoprefixer
etc. Let's take a browserslist
look at the description specification of 06125c0496a495.
browserslist
supports specifying the target browser type, and can flexibly combine a variety of specified conditions.
Specify the target browser type
browserslist
contains the following browsers, which can be used in the conditions (note the case sensitivity):
Android
: used forAndroid WebView
.Baidu
: Used in Baidu browser.BlackBerry
orbb
: for the BlackBerry browser.Chrome
: used forGoogle Chrome
.ChromeAndroid
orand_chr
: forAndroid Chrome
.Edge
: forMicrosoft Edge
.Electron
: forElectron framework
. Will be converted toChrome
version.Explorer
orie
: forInternet Explorer
.ExplorerMobile
orie_mob
: used forInternet Explorer Mobile
.Firefox
orff
: forMozilla Firefox
.FirefoxAndroid
orand_ff
: forAndroid Firefox
.iOS
orios_saf
: foriOS Safari
.Node
: forNode.js
.Opera
: forOpera
.OperaMini
orop_mini
: forOpera Mini
.OperaMobile
orop_mob
: forOpera Mobile
.QQAndroid
orand_qq
: for theAndroid QQ
browser.Safari
: For desktop Safari.Samsung
: For Samsung Internet.UCAndroid
orand_uc
: For Android UC browser.kaios
: used inKaiOS
browser.
Conditional syntax of browseslist
06125c0496aaaa supports very flexible conditional syntax. Here are some examples for reference (note the case sensitivity) for readers to browserslist
> 5%
: Means to be compatible with browser versions with global user statistics> 5%.>=
,<
and<=
are also available.> 5% in US
: Indicates that it is compatible with browser versions with a statistical proportion of US users> 5%. HereUS
is the American Alpha-2 code 1 . It can also be changed to Alpha-2 codes of other countries/regions. For example, China isCN
.> 5% in alt-AS
: Indicates that it is compatible with browser versions that have a percentage of Asian users> 5%. Here,alt-AS
represents the Asian region 1 .> 5% in my stats
: Indicates to be compatible with a browser version with a custom user statistics ratio> 5%.cover 99.5%
: Means to be compatible with the top 99.5% browser version of the cumulative user share.cover 99.5% in US
: Same as above, but with Alpha-2 encoding to add country/region restrictions.cover 99.5% in my stats
: Use user data.maintained node versions
: All official versionsNode.js
current node
Node.js
version currently being used by Browserslist.extends browserslist-config-mycompany
: said to be compatiblebrowserslist-config-mycompany
thisnpm
query results package.ie 6-8
: Indicates to be compatible with IE 6 ~ IE 8 versions (ie IE 6, IE 7 and IE 8).Firefox > 20
: Indicates to be compatible with Firefox version> 20.>=
,<
and<=
are also available.iOS 7
: Indicates to be compatible withiOS 7
.Firefox ESR
: Indicates to be compatible with the latestFirefox ESR
version.PhantomJS 2.1 and PhantomJS 1.9
: Indicates to be compatible withPhantomJS 2.1
and version 1.9.unreleased versions
orunreleased Chrome versions
: Indicates to be compatible with the unreleased development version. The latter specifies that it is compatible with the unreleasedChrome
version.last 2 major versions
orlast 2 iOS major versions
: Indicates to be compatible with all minor versions included in the last two major versions. The latter specifies that it is compatible with all the minor versions included in the last two major versions ofiOS
since 2015
orlast 2 years
: All versions released since 2015 or the last two years to the present.dead
: The official browser version is no longer maintained or has not been updated for more than two years.last 2 versions
: The latest two versions of each browser.last 2 Chrome versions
: The latest two versions of theChrome
defaults
:Browserslist
default rules (> 0.5%, last 2 versions, Firefox ESR, not dead
).not ie <= 8
: Exclude browsers lower than or equal to IE 8 from the previous conditions.
When reading these rules, it is recommended to visit http://browsersl.ist enter the same command to test, you can directly get the browser version that meets the conditions.
Test conditions on browserlist
Careful readers may find that the last query result will report an error. This is because thenot
operation needs to be placed after a query condition (described below). You can pick a rule from other rules and combine it. For example,ie 6-10, not ie <= 8
will filter out IE 9 and IE 10.
Conditional combination of browseslist
browserslist
supports the combination of multiple conditions, let's understand the condition combination method of browseslist
,
andor
can be used to represent logical "or". For example,last 1 version or > 1%
andlast 1 version, > 1%
equivalent, and both represent the latest version of each browser, or> 1% market share. The "or" operation is equivalent to the union in set theory.and
used to represent logical "and". For example,last 1 version and > 1%
represents the most recent version of each browser and has a market share of> 1%. The "and" operation is equivalent to the intersection in set theory.not
used to represent logical "not". For example,> .5% and not ie <= 8
means> 1% market share and excludes ie 8 and below versions. The "not" operation is equivalent to the complement set in set theory, sonot
cannot be used as the first condition, because you always need to know what the "set" is.
The three types of condition combinations can be illustrated by the following table:
Condition combination type | Schematic diagram | Example |
---|---|---|
or / , combination (union) | > .5% or last 2 versions > .5%, last 2 versions | |
and combination (intersection) | > .5% and last 2 versions | |
not combination (supplement) | > .5% and not last 2 versions > .5% or not last 2 versions > .5%, not last 2 versions |
Configure your browserslist
After understanding the above rules, we can configure browserslist
suitable for our project.
For example: if our project wants to run on iOS 8
Chrome
desktop browser with version number 49 and above and market share greater than 0.2%, then the following rules can be used:
// ...
"browserslist": [
">.2% and chrome >= 49",
"iOS >= 8"
],
After completion, you can use npx browserslist
to test your configured browserslist
.
$ npx browserslist
chrome 78
chrome 77
chrome 76
chrome 75
chrome 74
chrome 73
chrome 72
chrome 63
chrome 49
ios_saf 13.0-13.2
ios_saf 12.2-12.4
ios_saf 12.0-12.1
ios_saf 11.3-11.4
ios_saf 11.0-11.2
ios_saf 10.3
ios_saf 10.0-10.2
ios_saf 9.3
ios_saf 9.0-9.2
ios_saf 8.1-8.4
ios_saf 8
You can also visit https://browsersl.ist/ to enter conditional test results.
Test effect
Completed browserslist
after configuration rules, we can combine ESLint
scanning project API
compatibility issues. At the same time, the VS Code
plug-in can also prompt the incompatible API
call immediately.
Three, use eslint-plugin-builtin-compat
The principle of eslint-plugin-compat
caniuse-db
caniuse
( http://caniuse.com ) and the data of MDN
( https://developer.mozilla.org/ https://developer.mozilla.org/cen-US/ 16125c0496b33b) mdn-browser-compat-data
the data in API
to confirm the compatibility of 06125c0496b331. However, for uncertain instance objects, because it is difficult to judge the compatibility of the method of the instance, in order to avoid false alarms, eslint-plugin-compat
chose to skip the check of API
For example, when foo.includes
is not sure whether foo
is an array type, it cannot determine the compatibility of the includes
In the following figure, when we use the above browserslint
configuration, includes
method has not been scanned:
However, it can be found caniuse
Array.prototype.includes()
method is not compatible with iOS 8
In fact, the engine project of Cocos Creator has Array.prototype.includes()
method since version 2.1.3, thus completely circumventing the API compatibility problem. When we introduce Polyfill later in this section, we will introduce how to avoid false positives of this API.
In order to avoid false negatives, we can combine another compatibility check plug-in eslint-plugin-builtin-compat
. Also by means of the plug mdn-browser-compat-data
to scan compatible with eslint-plugin-compat
difference is that the plug will not miss the object instance, it will all foo.includes
the includes
method as is Array.prototype.includes()
method to scan. It is conceivable that this plug-in may cause false positives. Therefore, it is recommended to change its alarm level to warning
.
3.1 Installation eslint-plugin-builtin-compat
$ npm install eslint-plugin-builtin-compat --save-dev
3.2 Modify ESLint
configuration
Similar to eslint-plugin-compat
, we can modify ESLint
, plus the use of the plug-in. However, because the plug-in is prone to false alarms, it is only recommended to change its alarm level to warning
:
// .eslintrc.json
{
"extends": "eslint:recommended",
"plugins": [
"compat",
"builtin-compat"
],
"rules": {
//...
"compat/compat": 2,
"builtin-compat/no-incompatible-builtins": 1
},
"env": {
"browser": true
// ...
},
// ...
}
After joining the plug-in, you can find that the Array.prototype.includes()
method will be alerted by the plug-in:
Fourth, use Polyfill
solve compatibility problems
ESLint
on 06125c0496b5c6 in the development stage to scan out API
compatibility problems is certainly a means to prevent compatibility problems, but if colleagues in the team do not pay attention to the scan results of ESLint
ESLint
as a code part of the scan, There may be fish that slip through the net and continue to wreak havoc.
Thus, once a more common approach is to provide some of the API
the respective complement Polyfill
. This way, on the one hand, it can add support for incompatible browser versions, and on the other hand, it can make team members feel at ease to use the new API
and improve development efficiency.
4.1 Cocos Creator engine
in Polyfill
In fact, Cocos Creator
of engine
project also built a lot of common API
of Polyfill
:
Which includes Array.prototype.includes()
:
Therefore, if you use Cocos Creator
version 2.1.3 or higher to build a Array.prototype.includes()
method, the compiled application will run smoothly on the iOS 8
machine. This is because Array.prototype.includes()
was unified and "translated" into the method provided in the engine
Accordingly, in order to avoid Polyfill
Lane isArray
, find
, includes
other API
is eslint-plugin-builtin-compat
false positives, it can be .eslintrc
will these API
added to the exclusion list widget:
// .eslintrc.json
{
"extends": "eslint:recommended",
"plugins": [
"compat",
"builtin-compat"
],
// ...
"settings": {
"builtin-compat-ignore": ["ArrayBuffer", "find", "log2", "parseFloat", "parseInt", "assign", "values", "trimLeft", "startsWith", "endsWith", "repeat"]
}
// ...
}
4.2 Add Polyfill
engine
project in the Polyfill
not cover all API
. If you want to use an incompatible API
is not included in the engine project, then you have to consider adding the API
of the Polyfill
to your own project.
For example, string.prototype.padStart()
and string.prototype.padEnd()
API
respectively provide convenient methods for the head and tail completion of strings:
'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'
'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'
These two methods are iOS 10
and above:
Looking for Polyfill
How to find Polyfill
these two methods? One of the most authoritative sources is the MDN
site ( https://developer.mozilla.org/en-US/ ). Taking string.prototype.padStart()
padStart
in the search box at the upper right corner of the site:
Then hit enter to enter the search, and click the most matching result in the search results:
You enter the string-prototype-padStart
, and you can see the column of Polyfill
in the navigation bar on the left:
Click it to jump to the corresponding Polyfill
implementation:
!
Write a custom Polyfill
script
I found string.prototype.padStart()
and string.prototype.padEnd()
two API
of Polyfill
, we write a custom in their own projects Polyfill
script. For example, it is called ABCPolyfill.js
:
/**
* ABCPolyfill.js
* 补一些 polyfill,解决若干兼容问题
*/
var ABCPolyfill = function () {
console.log('ABC polyfill');
if (!String.prototype.padStart) {
String.prototype.padStart = function padStart(targetLength, padString) {
targetLength = targetLength >> 0; //truncate if number, or convert non-number to 0;
padString = String(typeof padString !== 'undefined' ? padString : ' ');
if (this.length >= targetLength) {
return String(this);
} else {
targetLength = targetLength - this.length;
if (targetLength > padString.length) {
padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed
}
return padString.slice(0, targetLength) + String(this);
}
};
}
if (!String.prototype.padEnd) {
String.prototype.padEnd = function padEnd(targetLength,padString) {
targetLength = targetLength>>0; //floor if number or convert non-number to 0;
padString = String((typeof padString !== 'undefined' ? padString : ' '));
if (this.length > targetLength) {
return String(this);
}
else {
targetLength = targetLength-this.length;
if (targetLength > padString.length) {
padString += padString.repeat(targetLength/padString.length); //append to original to ensure we are longer than needed
}
return String(this) + padString.slice(0,targetLength);
}
};
}
};
module.exports.ABCPolyfill = ABCPolyfill;
Next, we want to execute the load after the application starts Polyfill
script of ABCPolyfill()
method automatically marked with two API
of Polyfill
. We can write another application initialization script, for example called ABCInit.js
, which is used to perform some specified tasks when the application is initialized.
/**
* ABCInit.js
* 应用启动时的一些初始化工作
*/
import ABCPolyfill from 'ABCPolyfill';
// 初始化操作
function doInit() {
ABCPolyfill.ABCPolyfill();
}
(function () {
doInit();
})();
Then you can refer to the script in the script component in the initial scene of your project to take effect:
/**
* 工程的初始场景挂载的脚本组件
*/
require('ABCInit');
// ...
In order to avoid false positives of eslint-plugin-builtin-compat
padStart
and padEnd
can also be added to the exclusion list:
// .eslintrc.json
{
"extends": "eslint:recommended",
"plugins": [
"compat",
"builtin-compat"
],
// ...
"settings": {
"builtin-compat-ignore": ["ArrayBuffer", "find", "log2", "parseFloat", "parseInt", "assign", "values", "trimLeft", "startsWith", "endsWith", "repeat", "padStart", "padEnd"]
}
// ...
}
V. Summary
- Always pay attention to
API
compatibility; - Use
eslint-plugin-compat
check the static type incompatibilityAPI
, and set the alarm level to error; - Use
eslint-plugin-builtin-compat
check the incompatibility of the dynamic typeAPI
, and set the alarm level to warning; - Considered as incompatible
API
increasePolyfill
.
- 1 ↩
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。