cesiumJS 1.109 编程指南 中英文 翻译

cesiumJS 1.109 编程指南 中英文 翻译

Coding Guide 编码指南

CesiumJS is one of the largest JavaScript codebases in the world. Since its start, we have maintained a high standard for code quality, which has made the codebase easier to work with for both new and experienced contributors. We hope you find the codebase to be clean and consistent.
CesiumJS是世界上最大的JavaScript代码库之一。自成立以来,我们一直保持着高标准的代码质量,这使得代码库对于新手和有经验的贡献者来说都更容易使用。我们希望您发现代码库干净且一致。

In addition to describing typical coding conventions, this guide also covers best practices for design, maintainability, and performance. It is the cumulative advice of many developers after years of production development, research, and experimentation.
除了介绍典型的编码约定外,本指南还介绍了设计、可维护性和性能的最佳实践。这是许多开发人员经过多年的生产开发、研究和实验后积累的建议。

This guide applies to CesiumJS and all parts of the Cesium ecosystem written in JavaScript.
本指南适用于 CesiumJS 和用 JavaScript 编写的 Cesium 生态系统的所有部分。

🎨 The color palette icon indicates a design tip.
🎨 调色板图标表示设计提示。

🏠 The house icon indicates a maintainability tip. The whole guide is, of course, about writing maintainable code.
🏠 房屋图标表示可维护性提示。当然,整个指南是关于编写可维护的代码。

🚤 The speedboat indicates a performance tip.
🚤 快艇指示性能提示。

To some extent, this guide can be summarized as make new code similar to existing code.
在某种程度上,本指南可以概括为制作类似于现有代码的新代码。

Naming 命名

  • Directory names are PascalCase, e.g., Source/Scene.
    目录名称是 PascalCase ,例如 Source/Scene
  • Constructor functions are PascalCase, e.g., Cartesian3.
    构造函数是 PascalCase ,例如, Cartesian3
  • Functions are camelCase, e.g., defaultValue(), Cartesian3.equalsEpsilon().
    函数是 camelCase ,例如, defaultValue() Cartesian3.equalsEpsilon()
  • Files end in .js and have the same name as the JavaScript identifier, e.g., Cartesian3.js and defaultValue.js.
    文件以 JavaScript 标识符 .js 结尾并具有相同的名称,例如 Cartesian3.jsdefaultValue.js
  • Variables, including class properties, are camelCase, e.g.,
    变量,包括类属性,是 camelCase ,例如,
this.minimumPixelSize = 1.0; // Class property

const bufferViews = gltf.bufferViews; // Local variable
  • Private (by convention) members start with an underscore, e.g.,
    私有(按照惯例)成员以下划线开头,例如,
this._canvas = canvas;
  • Constants are in uppercase with underscores, e.g.,
    常量大写,带有下划线,例如,
Cartesian3.UNIT_X = Object.freeze(new Cartesian3(1.0, 0.0, 0.0));
  • Avoid abbreviations in public identifiers unless the full name is prohibitively cumbersome and has a widely accepted abbreviation, e.g.,
    避免在公共标识符中使用缩写,除非全名过于繁琐并且具有被广泛接受的缩写,例如,
Cartesian3.maximumComponent(); // Not Cartesian3.maxComponent()

Ellipsoid.WGS84; // Not Ellipsoid.WORLD_GEODETIC_SYSTEM_1984
  • If you do use abbreviations, use the recommended casing and do not capitalize all letters in the abbreviation. e.g.
    如果您确实使用缩写,请使用建议的大小写,并且不要将缩写中的所有字母大写。例如
new UrlTemplateImageryProvider(); // Not URLTemplateImageryProvider

resource.url; // Not resource.URL
  • Prefer short and descriptive names for local variables, e.g., if a function has only one length variable,
    首选局部变量的简短和描述性名称,例如,如果函数只有一个长度变量,
const primitivesLength = primitives.length;

is better written as
最好写成

const length = primitives.length;
  • When accessing an outer-scope’s this in a closure, name the variable that, e.g.,
    在闭包 this 中访问外部范围时,命名变量 that ,例如:
const that = this;
this._showTouch = createCommand(function () {
  that._touch = true;
});

A few more naming conventions are introduced below along with their design pattern, e.g., options parameters, result parameters and scratch variables, and from constructors.
下面介绍了更多命名约定及其设计模式,例如参数、 options result 参数和暂存变量以及 from 构造函数。

Formatting 格式

  • We use prettier to automatically re-format all JS code at commit time, so all of the work is done for you. Code is automatically reformatted when you commit.
    我们使用更漂亮的在提交时自动重新格式化所有 JS 代码,因此所有工作都为您完成。提交时,代码会自动重新格式化。
  • For HTML code, keep the existing style. Use double quotes.
    对于 HTML 代码,请保留现有样式。使用双引号。
  • Text files, end with a newline to minimize the noise in diffs.
    文本文件,以换行符结尾,以最大程度地减少差异中的干扰。

Linting 轧制

For syntax and style guidelines, we use the ESLint recommended settings (the list of rules can be found here) as a base and extend it with additional rules via a shared config Node module, eslint-config-cesium. This package is maintained as a part of the Cesium repository and is also used throughout the Cesium ecosystem. For a list of which rules are enabled, look in index.js, browser.js, and node.js.
对于语法和样式指南,我们使用 ESLint 推荐的设置(规则列表可以在这里找到)作为基础,并通过共享配置 Node 模块 eslint-config-cesium 使用其他规则对其进行扩展。此软件包作为 Cesium 存储库的一部分进行维护,也在整个 Cesium 生态系统中使用。有关已启用的规则的列表,请查看索引.js、浏览器.js和节点.js。

General rules: 一般规则:

Node-specific rules: 特定于节点的规则:

Disabling Rules with Inline Comments
禁用带有内联注释的规则

  • When disabling linting for one line, use //eslint-disable-next-line:
    禁用一行的 linting 时,请使用 //eslint-disable-next-line
function exit(warningMessage) {
  //eslint-disable-next-line no-alert
  window.alert("Cannot exit: " + warningMessage);
}
  • When disabling linting for blocks of code, place eslint-disable comments on new lines and as close to the associated code as possible:
    禁用代码块的 linting 时,请将注释放在 eslint-disable 新行上,并尽可能靠近关联的代码:
/*eslint-disable no-empty*/
try {
  lineNumber = parseInt(stack.substring(lineStart + 1, lineEnd1), 10);
} catch (ex) {}
/*eslint-enable no-empty*/

Units 单位

  • Cesium uses SI units:

    铯使用 SI 单位:

    • meters for distances, 米的距离,
    • radians for angles, and
      弧度表示角度,以及
    • seconds for time durations.
      秒的持续时间。
  • If a function has a parameter with a non-standard unit, such as degrees, put the unit in the function name, e.g.,
    如果函数具有非标准单位的参数,例如度,请将单位放在函数名称中,例如

Cartesian3.fromDegrees = function (
  longitude,
  latitude,
  height,
  ellipsoid,
  result
) {
  /* ... */
};

Basic Code Construction 基本代码构造

  • Cesium uses JavaScript’s strict mode so each module (file) contains
    Cesium 使用 JavaScript 的严格模式,因此每个模块(文件)都包含
"use strict";
  • 🚤 To avoid type coercion (implicit type conversion), test for equality with === and !==, e.g.,
    🚤 为了避免类型强制(隐式类型转换),请测试 与 的 === !== 相等性,例如
const i = 1;

if (i === 1) {
  // ...
}

if (i !== 1) {
  // ...
}
  • To aid the human reader, append .0 to whole numbers intended to be floating-point values, e.g., unless f is an integer,
    为了帮助人类读者,附加到旨在作为浮点值的整数 .0 中,例如,除非 f 是整数,
const f = 1;

is better written as
最好写成

const f = 1.0;
  • Declare variables where they are first used. For example,
    在首次使用变量的位置声明变量。例如
let i;
let m;
const models = [
  /* ... */
];
const length = models.length;
for (i = 0; i < length; ++i) {
  m = models[i];
  // Use m
}

is better written as
最好写成

const models = [
  /* ... */
];
const length = models.length;
for (let i = 0; i < length; ++i) {
  const m = models[i];
  // Use m
}
  • let and const variables have block-level scope. Do not rely on variable hoisting, i.e., using a variable before it is declared, e.g.,
    letconst 变量具有块级范围。不要依赖变量吊装,即在声明变量之前使用变量,例如,
console.log(i); // i is undefined here.  Never use a variable before it is declared.
let i = 0.0;
  • A const variables is preferred when a value is not updated. This ensures immutability.
    当值未更新时,首选 const 变量。这确保了不变性。
  • 🚤 Avoid redundant nested property access. This
    🚤 避免冗余嵌套属性访问。这
scene.environmentState.isSkyAtmosphereVisible = true;
scene.environmentState.isSunVisible = true;
scene.environmentState.isMoonVisible = false;

is better written as
最好写成

const environmentState = scene.environmentState;
environmentState.isSkyAtmosphereVisible = true;
environmentState.isSunVisible = true;
environmentState.isMoonVisible = false;
  • Do not create a local variable that is used only once unless it significantly improves readability, e.g.,
    不要创建只使用一次的局部变量,除非它能显著提高可读性,例如,
function radiiEquals(left, right) {
  const leftRadius = left.radius;
  const rightRadius = right.radius;
  return leftRadius === rightRadius;
}

is better written as
最好写成

function radiiEquals(left, right) {
  return left.radius === right.radius;
}
  • Use undefined instead of null.
    使用 undefined 代替 null
  • Test if a variable is defined using Cesium’s defined function, e.g.,
    测试变量是否使用铯 defined 函数定义,例如,
const v = undefined;
if (defined(v)) {
  // False
}

const u = {};
if (defined(u)) {
  // True
}
  • Use Object.freeze function to create enums, e.g.,
    使用 Object.freeze 函数创建枚举,例如
    const ModelAnimationState = {
        STOPPED : 0,
        ANIMATING : 1
    };

    return Object.freeze(ModelAnimationState);
});
  • Use descriptive comments for non-obvious code, e.g.,
    对不明显的代码使用描述性注释,例如
byteOffset += sizeOfUint32; // Add 4 to byteOffset

is better written as
最好写成

byteOffset += sizeOfUint32; // Skip length field
  • TODO comments need to be removed or addressed before the code is merged into main. Used sparingly, PERFORMANCE_IDEA, can be handy later when profiling.
    TODO 在将代码合并到 main 之前,需要删除或解决注释。谨慎使用 PERFORMANCE_IDEA ,在以后进行性能分析时会很方便。
  • Remove commented out code before merging into main.
    在合并到 main 之前删除注释掉的代码。
  • Modern language features may provide handy shortcuts and cleaner syntax, but they should be used with consideration for their performance implications, especially in code that is invoked per-frame.
    现代语言功能可能提供方便的快捷方式和更简洁的语法,但在使用它们时应考虑其性能影响,尤其是在按帧调用的代码中。

Functions 功能

  • 🎨 Functions should be cohesive; they should only do one task.
    🎨 职能应该是有凝聚力的;他们应该只做一项任务。
  • Statements in a function should be at a similar level of abstraction. If a code block is much lower level than the rest of the statements, it is a good candidate to move to a helper function, e.g.,
    函数中的语句应处于类似的抽象级别。如果代码块的级别远低于语句的其余部分,则最好迁移到帮助程序函数,例如
Cesium3DTileset.prototype.update = function (frameState) {
  const tiles = this._processingQueue;
  const length = tiles.length;

  for (let i = length - 1; i >= 0; --i) {
    tiles[i].process(this, frameState);
  }

  selectTiles(this, frameState);
  updateTiles(this, frameState);
};

is better written as
最好写成

Cesium3DTileset.prototype.update = function (frameState) {
  processTiles(this, frameState);
  selectTiles(this, frameState);
  updateTiles(this, frameState);
};

function processTiles(tileset, frameState) {
  const tiles = tileset._processingQueue;
  const length = tiles.length;

  for (let i = length - 1; i >= 0; --i) {
    tiles[i].process(tileset, frameState);
  }
}
  • Do not use an unnecessary else block at the end of a function, e.g.,
    不要在函数末尾使用不必要的 else 块,例如
function getTransform(node) {
  if (defined(node.matrix)) {
    return Matrix4.fromArray(node.matrix);
  } else {
    return Matrix4.fromTranslationQuaternionRotationScale(
      node.translation,
      node.rotation,
      node.scale
    );
  }
}

is better written as
最好写成

function getTransform(node) {
  if (defined(node.matrix)) {
    return Matrix4.fromArray(node.matrix);
  }

  return Matrix4.fromTranslationQuaternionRotationScale(
    node.translation,
    node.rotation,
    node.scale
  );
}
  • 🚤 Smaller functions are more likely to be optimized by JavaScript engines. Consider this for code that is likely to be a hot spot.
    🚤 较小的函数更有可能被JavaScript引擎优化。对于可能成为热点的代码,请考虑这一点。

options Parameters options 参数

🎨 Many Cesium functions take an options parameter to support optional parameters, self-documenting code, and forward compatibility. For example, consider:
🎨 许多 Cesium 函数采用 options 参数来支持可选参数、自文档代码和向前兼容性。例如,考虑:

const sphere = new SphereGeometry(10.0, 32, 16, VertexFormat.POSITION_ONLY);

It is not clear what the numeric values represent, and the caller needs to know the order of parameters. If this took an options parameter, it would look like this:
不清楚数值代表什么,调用方需要知道参数的顺序。如果这需要一个 options 参数,它将如下所示:

const sphere = new SphereGeometry({
  radius: 10.0,
  stackPartitions: 32,
  slicePartitions: 16,
  vertexFormat: VertexFormat.POSITION_ONLY,
});
  • 🚤 Using { /* ... */ } creates an object literal, which is a memory allocation. Avoid designing functions that use an options parameter if the function is likely to be a hot spot; otherwise, callers will have to use a scratch variable (see below) for performance. Constructor functions for non-math classes are good candidates for options parameters since Cesium avoids constructing objects in hot spots. For example,
    🚤 使用 创建一个 { /* ... */ } 对象文本,这是一个内存分配。如果函数可能是热点,请避免设计使用 options 参数的函数;否则,调用方将不得不使用暂存变量(见下文)来提高性能。非数学类的构造函数是参数的 options 良好候选项,因为 Cesium 避免在热点中构造对象。例如
const p = new Cartesian3({
  x: 1.0,
  y: 2.0,
  z: 3.0,
});

is a bad design for the Cartesian3 constructor function since its performance is not as good as that of
对于构造函数来说是一个糟糕的设计 Cartesian3 ,因为它的性能不如

const p = new Cartesian3(1.0, 2.0, 3.0);

Default Parameter Values 默认参数值

If a sensible default exists for a function parameter or class property, don’t require the user to provide it. Use Cesium’s defaultValue to assign a default value. For example, height defaults to zero in Cartesian3.fromRadians:
如果函数参数或类属性存在合理的默认值,则不需要用户提供它。使用铯分配 defaultValue 默认值。例如, height 在 中 Cartesian3.fromRadians 默认为零:

Cartesian3.fromRadians = function (longitude, latitude, height) {
  height = defaultValue(height, 0.0);
  // ...
};
  • 🚤 Don’t use defaultValue if it could cause an unnecessary function call or memory allocation, e.g.,
    🚤 如果可能导致不必要的函数调用或内存分配,请不要使用 defaultValue ,例如,
this._mapProjection = defaultValue(
  options.mapProjection,
  new GeographicProjection()
);

is better written as
最好写成

this._mapProjection = defined(options.mapProjection)
  ? options.mapProjection
  : new GeographicProjection();
  • If an options parameter is optional, use defaultValue.EMPTY_OBJECT, e.g.,
    如果参数 options 是可选的,请使用 ,例如 defaultValue.EMPTY_OBJECT
function DebugModelMatrixPrimitive(options) {
  options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  this.length = defaultValue(options.length, 10000000.0);
  this.width = defaultValue(options.width, 2.0);
  // ...
}

Some common sensible defaults are
一些常见的合理默认值是

  • height: 0.0 height0.0
  • ellipsoid: Ellipsoid.WGS84 ellipsoidEllipsoid.WGS84
  • show: true showtrue

Throwing Exceptions 引发异常

Use the functions of Cesium’s Check class to throw a DeveloperError when the user has a coding error. The most common errors are parameters that are missing, have the wrong type or are out of rangers of the wrong type or are out of range.
使用 Cesium 的 Check 类的函数在用户出现编码错误时抛出 。 DeveloperError 最常见的错误是缺少参数、类型错误、超出错误类型的游侠或超出范围。

  • For example, to check that a parameter is defined and is an object:
    例如,要检查参数是否已定义并且是对象,请执行以下操作:
Cartesian3.maximumComponent = function (cartesian) {
  //>>includeStart('debug', pragmas.debug);
  Check.typeOf.object("cartesian", cartesian);
  //>>includeEnd('debug');

  return Math.max(cartesian.x, cartesian.y, cartesian.z);
};
  • For more complicated parameter checks, manually check the parameter and then throw a DeveloperError. Example:
    对于更复杂的参数检查,请手动检查参数,然后抛出 DeveloperError .例:
Cartesian3.unpackArray = function (array, result) {
  //>>includeStart('debug', pragmas.debug);
  Check.defined("array", array);
  Check.typeOf.number.greaterThanOrEquals("array.length", array.length, 3);
  if (array.length % 3 !== 0) {
    throw new DeveloperError("array length must be a multiple of 3.");
  }
  //>>includeEnd('debug');

  // ...
};
  • To check for DeveloperError, surround code in includeStart/includeEnd comments, as shown above, so developer error checks can be optimized out of release builds. Do not include required side effects inside includeStart/includeEnd, e.g.,
    如上所示,要检查 DeveloperError ,请在 / includeEnd 注释中 includeStart 环绕代码,以便可以在发布版本之外优化开发人员错误检查。不要在 / includeEnd 中包含 includeStart 所需的副作用,例如,
Cartesian3.maximumComponent = function (cartesian) {
  //>>includeStart('debug', pragmas.debug);
  const c = cartesian;
  Check.typeOf.object("cartesian", cartesian);
  //>>includeEnd('debug');

  // Works in debug. Fails in release since c is optimized out!
  return Math.max(c.x, c.y, c.z);
};
  • Throw Cesium’s RuntimeError for an error that will not be known until runtime. Unlike developer errors, runtime error checks are not optimized out of release builds.
    抛出铯的错误 RuntimeError ,直到运行时才会知道。与开发人员错误不同,运行时错误检查不会针对发布版本进行优化。
if (typeof WebGLRenderingContext === "undefined") {
  throw new RuntimeError("The browser does not support WebGL.");
}
  • 🎨 Exceptions are exceptional. Avoid throwing exceptions, e.g., if a polyline is only provided one position, instead of two or more, instead of throwing an exception just don’t render it.
    🎨 例外情况例外。避免抛出异常,例如,如果折线只提供一个位置,而不是两个或更多,而不是抛出异常只是不要渲染它。

result Parameters and Scratch Variables result 参数和临时变量

🚤 In JavaScript, user-defined classes such as Cartesian3 are reference types and are therefore allocated on the heap. Frequently allocating these types causes a significant performance problem because it creates GC pressure, which causes the Garbage Collector to run longer and more frequently.
🚤 在 JavaScript 中,用户定义的类(如 例如 Cartesian3 )是引用类型,因此在堆上分配。频繁分配这些类型会导致严重的性能问题,因为它会产生 GC 压力,从而导致垃圾回收器运行更长时间和更频繁。

Cesium uses required result parameters to avoid implicit memory allocation. For example,
Cesium 使用必需 result 的参数来避免隐式内存分配。例如

const sum = Cartesian3.add(v0, v1);

would have to implicitly allocate a new Cartesian3 object for the returned sum. Instead, Cartesian3.add requires a result parameter:
必须为返回的总和隐式分配一个新 Cartesian3 对象。相反, Cartesian3.add 需要一个 result 参数:

const result = new Cartesian3();
const sum = Cartesian3.add(v0, v1, result); // Result and sum reference the same object

This makes allocations explicit to the caller, which allows the caller to, for example, reuse the result object in a file-scoped scratch variable:
这使得分配对调用方显式,从而允许调用方在文件范围的暂存变量中重用结果对象:

const scratchDistance = new Cartesian3();

Cartesian3.distance = function (left, right) {
  Cartesian3.subtract(left, right, scratchDistance);
  return Cartesian3.magnitude(scratchDistance);
};

The code is not as clean, but the performance improvement is often dramatic.
代码不那么干净,但性能改进通常是显着的。

As described below, from constructors also use optional result parameters.
如下所述, from 构造函数也使用可选 result 参数。

Because result parameters aren’t always required or returned, don’t strictly rely on the result parameter you passed in to be modified. For example:
由于结果参数并不总是必需的或返回的,因此不要严格依赖传入的结果参数进行修改。例如:

Cartesian3.add(v0, v1, result);
Cartesian3.add(result, v2, result);

is better written as
最好写成

result = Cartesian3.add(v0, v1, result);
result = Cartesian3.add(result, v2, result);

Classes 类

  • 🎨 Classes should be cohesive. A class should represent one abstraction.
    🎨 类应该是有凝聚力的。一个类应该表示一个抽象。
  • 🎨 Classes should be loosely coupled. Two classes should not be entangled and rely on each other’s implementation details; they should communicate through well-defined interfaces.
    🎨 类应该是松散耦合的。两个类不应该纠缠在一起,相互依赖对方的实现细节;它们应通过定义良好的接口进行通信。

Constructor Functions 构造函数

  • Create a class by creating a constructor function:
    通过创建构造函数来创建类:
function Cartesian3(x, y, z) {
  this.x = defaultValue(x, 0.0);
  this.y = defaultValue(y, 0.0);
  this.z = defaultValue(z, 0.0);
}
  • Create an instance of a class (an object) by calling the constructor function with new:
    通过使用调用 new 构造函数来创建类(对象)的实例:
const p = new Cartesian3(1.0, 2.0, 3.0);
  • 🚤 Assign to all the property members of a class in the constructor function. This allows JavaScript engines to use a hidden class and avoid entering dictionary mode. Assign undefined if no initial value makes sense. Do not add properties to an object, e.g.,
    🚤 赋值给构造函数中类的所有属性成员。这允许 JavaScript 引擎使用隐藏类并避免进入字典模式。如果没有初始值有意义,则分配 undefined 。不要向对象添加属性,例如,
const p = new Cartesian3(1.0, 2.0, 3.0);
p.w = 4.0; // Adds the w property to p, slows down property access since the object enters dictionary mode
  • 🚤 For the same reason, do not change the type of a property, e.g., assign a string to a number, e.g.,
    🚤 出于同样的原因,不要更改属性的类型,例如,将字符串分配给数字,例如,
const p = new Cartesian3(1.0, 2.0, 3.0);
p.x = "Cesium"; // Changes x to a string, slows down property access
  • In a constructor function, consider properties as write once; do not write to them or read them multiple times. Create a local variable if they need to be read. For example:
    在构造函数中,将属性视为写入一次;不要写信给他们或多次阅读它们。如果需要读取,请创建一个局部变量。例如:

    Instead of 而不是

    this._x = 2;
    this._xSquared = this._x * this._x;
    

    prefer 喜欢

    const x = 2;
    this._x = x;
    this._xSquared = x * x;
    

from Constructors from 构造 函数

🎨 Constructor functions should take the basic components of the class as parameters. For example, Cartesian3 takes x, y, and z.
🎨 构造函数应将类的基本组件作为参数。例如, Cartesian3xyz

It is often convenient to construct objects from other parameters. Since JavaScript doesn’t have function overloading, Cesium uses static functions prefixed with from to construct objects in this way. For example:
从其他参数构造对象通常很方便。由于 JavaScript 没有函数重载,Cesium 使用前缀为 的 from 静态函数以这种方式构造对象。例如:

const p = Cartesian3.fromRadians(-2.007, 0.645); // Construct a Cartesian3 object using longitude and latitude

These are implemented with an optional result parameter, which allows callers to pass in a scratch variable:
这些是使用可选 result 参数实现的,该参数允许调用方传入暂存变量:

Cartesian3.fromRadians = function (longitude, latitude, height, result) {
  // Compute x, y, z using longitude, latitude, height

  if (!defined(result)) {
    result = new Cartesian3();
  }

  result.x = x;
  result.y = y;
  result.z = z;
  return result;
};

Since calling a from constructor should not require an existing object, the function is assigned to Cartesian3.fromRadians, not Cartesian3.prototype.fromRadians.
由于调用 from 构造函数不需要现有对象,因此该函数分配给 Cartesian3.fromRadians ,而不是 Cartesian3.prototype.fromRadians

to Functions to 功能

Functions that start with to return a new type of object, e.g.,
以返回新类型的对象开头 to 的函数,例如

Cartesian3.prototype.toString = function () {
  return "(${this.x}, ${this.y}, ${this.z})";
};

Use Prototype Functions for Fundamental Classes Sparingly 谨慎使用基本类的原型函数

🎨 Fundamental math classes such as Cartesian3, Quaternion, Matrix4, and JulianDate use prototype functions sparingly. For example, Cartesian3 does not have a prototype add function like this:
🎨 基础数学类(如 Cartesian3QuaternionMatrix4 )和 JulianDate 原型函数很少使用。例如, Cartesian3 没有这样的原型 add 函数:

const v2 = v0.add(v1, result);

Instead, this is written as
相反,这写为

const v2 = Cartesian3.add(v0, v1, result);

The only exceptions are
唯一的例外是

  • clone
  • equals
  • equalsEpsilon
  • toString

These prototype functions generally delegate to the non-prototype (static) version, e.g.,
这些原型函数通常委托给非原型(静态)版本,例如,

Cartesian3.equals = function (left, right) {
  return (
    left === right ||
    (defined(left) &&
      defined(right) &&
      left.x === right.x &&
      left.y === right.y &&
      left.z === right.z)
  );
};

Cartesian3.prototype.equals = function (right) {
  return Cartesian3.equals(this, right);
};

The prototype versions have the benefit of being able to be used polymorphically.
原型版本的优点是能够多态使用。

Static Constants 静态常量

To create a static constant related to a class, use Object.freeze:
要创建与类相关的静态常量,请使用 Object.freeze

Cartesian3.ZERO = Object.freeze(new Cartesian3(0.0, 0.0, 0.0));

Private Functions 私人活动

Like private properties, private functions start with an _. In practice, these are rarely used. Instead, for better encapsulation, a file-scoped function that takes this as the first parameter is used. For example,
与私有属性一样,私有函数以 _ .在实践中,这些很少使用。相反,为了更好地封装,使用作为第一个参数的文件 this 范围的函数。例如

Cesium3DTileset.prototype.update = function(frameState) {
    this._processTiles(frameState);
    // ...
};

Cesium3DTileset.prototype._processTiles(tileset, frameState) {
    const tiles = this._processingQueue;
    const length = tiles.length;

    for (let i = length - 1; i >= 0; --i) {
        tiles[i].process(tileset, frameState);
    }
}

is better written as
最好写成

Cesium3DTileset.prototype.update = function (frameState) {
  processTiles(this, frameState);
  // ...
};

function processTiles(tileset, frameState) {
  const tiles = tileset._processingQueue;
  const length = tiles.length;

  for (let i = length - 1; i >= 0; --i) {
    tiles[i].process(tileset, frameState);
  }
}

Property Getter/Setters 属性获取者/设置者

Public properties that can be read or written without extra processing can simply be assigned in the constructor function, e.g.,
无需额外处理即可读取或写入的公共属性可以简单地在构造函数中分配,例如,

function Model(options) {
  this.show = defaultValue(options.show, true);
}

Read-only properties can be created with a private property and a getter using Object.defineProperties function, e.g.,
可以使用私有属性和 getter 使用 Object.defineProperties 函数创建只读属性,例如:

function Cesium3DTileset(options) {
  this._url = options.url;
}

Object.defineProperties(Cesium3DTileset.prototype, {
  url: {
    get: function () {
      return this._url;
    },
  },
});

Getters can perform any needed computation to return the property, but the performance expectation is that they execute quickly.
Getters 可以执行任何所需的计算来返回属性,但性能期望是它们快速执行。

Setters can also perform computation before assigning to a private property, set a flag to delay computation, or both, for example:
设置器还可以在分配给私有属性之前执行计算,设置标志以延迟计算,或两者兼而有之,例如:

Object.defineProperties(UniformState.prototype, {
  viewport: {
    get: function () {
      return this._viewport;
    },
    set: function (viewport) {
      if (!BoundingRectangle.equals(viewport, this._viewport)) {
        BoundingRectangle.clone(viewport, this._viewport);

        const v = this._viewport;
        const vc = this._viewportCartesian4;
        vc.x = v.x;
        vc.y = v.y;
        vc.z = v.width;
        vc.w = v.height;

        this._viewportDirty = true;
      }
    },
  },
});
  • 🚤 Calling the getter/setter function is slower than direct property access so functions internal to a class can use the private property directly when appropriate.
    🚤 调用 getter/setter 函数比直接属性访问慢,因此类内部的函数可以在适当的时候直接使用私有属性。

Shadowed Property 阴影属性

When the overhead of getter/setter functions is prohibitive or reference-type semantics are desired, e.g., the ability to pass a property as a result parameter so its properties can be modified, consider combining a public property with a private shadowed property, e.g.,
当 getter/setter 函数的开销过高或需要引用类型语义时,例如,能够将属性作为 result 参数传递以便可以修改其属性时,请考虑将公共属性与私有阴影属性组合在一起,例如,

function Model(options) {
  this.modelMatrix = Matrix4.clone(
    defaultValue(options.modelMatrix, Matrix4.IDENTITY)
  );
  this._modelMatrix = Matrix4.clone(this.modelMatrix);
}

Model.prototype.update = function (frameState) {
  if (!Matrix4.equals(this._modelMatrix, this.modelMatrix)) {
    // clone() is a deep copy. Not this._modelMatrix = this._modelMatrix
    Matrix4.clone(this.modelMatrix, this._modelMatrix);

    // Do slow operations that need to happen when the model matrix changes
  }
};

Put the Constructor Function at the Top of the File 将构造函数放在文件顶部

It is convenient for the constructor function to be at the top of the file even if it requires that helper functions rely on hoisting, for example, Cesium3DTileset.js,
构造函数位于文件顶部很方便,即使它要求帮助函数依赖于提升,例如, Cesium3DTileset.js

function loadTileset(tileset, tilesJson, done) {
  // ...
}

function Cesium3DTileset(options) {
  // ...
  loadTileset(this, options.url, function (data) {
    // ...
  });
}

is better written as
最好写成

function Cesium3DTileset(options) {
  // ...
  loadTileset(this, options.url, function (data) {
    // ...
  });
}

function loadTileset(tileset, tilesJson, done) {
  // ...
}

even though it relies on implicitly hoisting the loadTileset function to the top of the file.
即使它依赖于将 loadTileset 函数隐式提升到文件顶部。

Design 设计

  • 🏠 Make a class or function part of the Cesium API only if it will likely be useful to end users; avoid making an implementation detail part of the public API. When something is public, it makes the Cesium API bigger and harder to learn, is harder to change later, and requires more documentation work.
    🏠 仅当类或函数可能对最终用户有用时,才使类或函数成为铯 API 的一部分;避免将实现细节作为公共 API 的一部分。当某些内容公开时,它会使 Cesium API 更大、更难学习,以后更难更改,并且需要更多的文档工作。

  • 🎨 Put new classes and functions in the right part of the Cesium stack (directory). From the bottom up:

    🎨 将新类和函数放在 Cesium 堆栈(目录)的右侧。自下而上:

    • Source/Core - Number crunching. Pure math such as Cartesian3. Pure geometry such as CylinderGeometry. Fundamental algorithms such as mergeSort. Request helper functions such as loadArrayBuffer.
      Source/Core - 数字运算。纯数学,如 Cartesian3 .纯几何,例如 CylinderGeometry .基本算法,例如 mergeSort .请求帮助程序函数,例如 loadArrayBuffer
    • Source/Renderer - WebGL abstractions such as ShaderProgram and WebGL-specific utilities such as ShaderCache. Identifiers in this directory are not part of the public Cesium API.
      Source/Renderer - WebGL抽象,如 ShaderProgram WebGL特定的实用程序,如 ShaderCache .此目录中的标识符不是公共铯 API 的一部分。
    • Source/Scene - The graphics engine, including primitives such as Model. Code in this directory often depends on Renderer.
      Source/Scene - 图形引擎,包括模型等基元。此目录中的代码通常依赖于 Renderer
    • Source/DataSources - Entity API, such as Entity, and data sources such as CzmlDataSource.
      Source/DataSources - 实体 API,例如 和数据源,例如 Entity CzmlDataSource
    • Source/Widgets - Widgets such as the main Cesium Viewer.
      Source/Widgets - 小部件,如主铯 Viewer

It is usually obvious what directory a file belongs in. When it isn’t, the decision is usually between Core and another directory. Put the file in Core if it is pure number crunching or a utility that is expected to be generally useful to Cesium, e.g., Matrix4 belongs in Core since many parts of the Cesium stack use 4x4 matrices; on the other hand, BoundingSphereState is in DataSources because it is specific to data sources.
文件属于哪个目录通常很明显。如果不是,则通常在另一个目录之间 Core 做出决定。 Core 如果文件是纯数字运算或预期对 Cesium 通常有用的实用程序,例如, Core 由于 Cesium 堆栈的许多部分使用 4x4 矩阵, Matrix4 请将文件放入其中;另一方面,是 in DataSourcesBoundingSphereState 因为它特定于数据源。
在这里插入图片描述

Modules (files) should only reference modules in the same level or a lower level of the stack. For example, a module in Scene can use modules in Scene, Renderer, and Core, but not in DataSources or Widgets.
模块(文件)应仅引用堆栈的同一级别或较低级别的模块。例如,中的模块可以使用 SceneRendererCore 中的 Scene 模块,但不能使用 或 Widgets 中的 DataSources 模块。

  • WebGL resources need to be explicitly deleted so classes that contain them (and classes that contain these classes, and so on) have destroy and isDestroyed functions, e.g.,
    WebGL资源需要显式删除,以便包含它们的类(以及包含这些类的类等)具有 destroyisDestroyed 功能,例如,
const primitive = new Primitive(/* ... */);
expect(content.isDestroyed()).toEqual(false);
primitive.destroy();
expect(content.isDestroyed()).toEqual(true);

A destroy function is implemented with Cesium’s destroyObject function, e.g.,
函数是用铯函数 destroyObject 实现的 destroy ,例如,

SkyBox.prototype.destroy = function () {
  this._vertexArray = this._vertexArray && this._vertexArray.destroy();
  return destroyObject(this);
};
  • Only destroy objects that you create; external objects given to a class should be destroyed by their owner, not the class.
    仅您 destroy 创建的对象;提供给类的外部对象应由其所有者销毁,而不是由类销毁。

Deprecation and Breaking Changes 弃用和中断性更改

From release to release, we strive to keep the public Cesium API stable but also maintain mobility for speedy development and to take the API in the right direction. As such, we sparingly deprecate and then remove or replace parts of the public API.
从发布到发布,我们努力保持公共铯API的稳定性,同时保持移动性以实现快速开发,并将API推向正确的方向。因此,我们谨慎地弃用,然后删除或替换部分公共 API。

A @private API is considered a Cesium implementation detail and can be broken immediately without deprecation.
API @private 被视为 Cesium 实现细节,可以立即中断而不会弃用。

An @experimental API is subject to breaking changes in future Cesium releases without deprecation. It allows for new experimental features, for instance implementing draft formats.
@experimental API 在未来的 Cesium 版本中会受到重大更改的影响,而不会弃用。它允许新的实验性功能,例如实现草稿格式。

A public identifier (class, function, property) should be deprecated before being removed. To do so:
公共标识符(类、函数、属性)在删除之前应被弃用。为此:

  • Decide on which future version the deprecated API should be removed. This is on a case-by-case basis depending on how badly it impacts users and Cesium development. Most deprecated APIs will removed in 1-3 releases. This can be discussed in the pull request if needed.
    确定应删除已弃用的 API 的未来版本。这是根据具体情况进行的,具体取决于它对用户和铯开发的严重程度。大多数已弃用的 API 将在 1-3 个版本中删除。如果需要,可以在拉取请求中讨论这一点。
  • Use deprecationWarning to warn users that the API is deprecated and what proactive changes they can take, e.g.,
    用于 deprecationWarning 警告用户 API 已弃用以及他们可以采取哪些主动更改,例如,
function Foo() {
  deprecationWarning(
    "Foo",
    "Foo was deprecated in CesiumJS 1.01.  It will be removed in 1.03.  Use newFoo instead."
  );
  // ...
}
  • Add the @deprecated doc tag.
    添加 @deprecated 文档标签。
  • Remove all use of the deprecated API inside Cesium except for unit tests that specifically test the deprecated API.
    删除 Cesium 中所有已弃用 API 的使用,但专门测试已弃用 API 的单元测试除外。
  • Mention the deprecation in the Deprecated section of CHANGES.md. Include what Cesium version it will be removed in.
    DeprecatedCHANGES.md 部分中提及弃用。包括它将被删除的铯版本。
  • Create an issue to remove the API with the appropriate remove in [version] label.
    创建一个问题以删除具有相应 remove in [version] 标签的 API。
  • Upon removal of the API, add a mention of it in the Breaking Changes section of CHANGES.md.
    删除 API 后,在 的 Breaking Changes 部分中 CHANGES.md 添加对它的提及。

Third-Party Libraries 第三方库

🏠 Cesium uses third-party libraries sparingly. If you want to add a new one, please start a thread on the Cesium community forum (example discussion). The library should
🏠 Cesium 很少使用第三方库。如果您想添加新的,请在 Cesium 社区论坛上发起一个主题(示例讨论)。图书馆应该

  • Have a compatible license such as MIT, BSD, or Apache 2.0.
    拥有兼容的许可证,如 MIT、BSD 或 Apache 2.0。
  • Provide capabilities that Cesium truly needs and that the team doesn’t have the time and/or expertise to develop.
    提供 Cesium 真正需要的功能,而团队没有时间和/或专业知识进行开发。
  • Be lightweight, tested, maintained, and reasonably widely used.
    轻量级,经过测试,维护并合理广泛使用。
  • Not pollute the global namespace.
    不污染全局命名空间。
  • Provide enough value to justify adding a third-party library whose integration needs to be maintained and has the potential to slightly count against Cesium when some users evaluate it (generally, fewer third-parties is better).
    提供足够的价值来证明添加第三方库的合理性,该库的集成需要维护,并且在某些用户评估时可能会略微不利于Cesium(通常,第三方越少越好)。

When adding or updating a third-party library:
添加或更新第三方库时:

  • Ensure LICENSE.md is updated with the library’s name and full copyright notice.
    确保使用图书馆的名称和完整的版权声明更新 LICENSE.md。

  • If a library is shipped as part of the CesiumJS release, it should be included in the generated

    ThirdParty.json

    .

    如果库作为 CesiumJS 发行版的一部分提供,则应将其包含在生成的 ThirdParty.json .

    1. Update ThirdParty.extra.json with the package name. If it is an npm module included in package.json, use the exact package name.
      使用软件包 name 更新 ThirdParty.extra.json 。如果是 中包含的 package.json npm 模块,请使用确切的包名称。
    2. If the library is not an npm module included in package.json, provide the license, version, and url fields. Otherwise, this information can be detected using package.json.
      如果库不是 中包含的 package.json npm 模块,请提供 licenseversionurl 字段。否则,可以使用 检测 package.json 此信息。
    3. If there is a special case regarding the license, such as choosing to use a single license from a list of multiple available ones, providing the license field will override information detected using package.json. The notes field should also be provided in the case explaining the exception.
      如果存在有关许可证的特殊情况,例如从多个可用许可证列表中选择使用单个许可证,则提供该 license 字段将覆盖使用 检测到 package.json 的信息。在解释例外的情况下,也应提供该 notes 字段。
    4. Run npm run build-third-party and commit the resulting ThirdParty.json
      运行 npm run build-third-party 并提交结果 ThirdParty.json

Widgets 部件

Cesium includes a handful of standard widgets that are used in the Viewer, including animation and timeline controls, a base layer picker, and a geocoder. These widgets are all built using Knockout) for automatic UI refreshing. Knockout uses a Model View ViewModel (MVVM) design pattern. You can learn more about this design pattern in Understanding MVVM - A Guide For JavaScript Developers
Cesium 包含一些在查看器中使用的标准微件,包括动画和时间轴控件、基础图层选取器和地理编码器。这些小部件都是使用 Knockout 构建的)用于自动 UI 刷新。挖空使用模型视图视图模型 (MVVM) 设计模式。您可以在了解 MVVM - JavaScript 开发人员指南中了解有关此设计模式的更多信息

To learn about using the Knockout library, see the Get started section of their home page. They also have a great interactive tutorial with step by step instructions.
要了解如何使用挖空库,请参阅其主页的入门部分。他们还有一个很棒的交互式教程,其中包含分步说明。

Cesium also uses the Knockout-ES5 plugin to simplify knockout syntax. This lets us use knockout observables the same way we use other variables. Call knockout.track to create the observables. Here is an example from BaseLayerPickerViewModel that makes observables for tooltip, showInstructions and _touch properties.
Cesium还使用Knockout-ES5插件来简化Knockout语法。这让我们可以使用挖空可观察量,就像使用其他变量一样。调用 knockout.track 以创建可观察量。下面是来自 BaseLayerPickerViewModel 的示例,该示例为 tooltipshowInstructions _touch 属性创建可观察量。

knockout.track(this, ["tooltip", "showInstructions", "_touch"]);

Knockout subscriptions 淘汰赛订阅

Use a knockout subscription only when you are unable to accomplish what you need to do with a standard binding. For example, the Viewer subscribes to FullscreenButtonViewModel.isFullscreenEnabled because it needs to change the width of the timeline widget when that value changes. This cannot be done with binding because the value from FullscreenButtonViewModel is affecting a value not contained within that widget.
仅当您无法完成使用标准绑定需要执行的操作时,才使用挖空订阅。例如, Viewer 订阅是因为 FullscreenButtonViewModel.isFullscreenEnabled 当该值更改时,它需要更改时间轴小部件的宽度。这不能通过绑定来完成,因为 from FullscreenButtonViewModel 的值正在影响该小组件中未包含的值。

Cesium includes a subscribeAndEvaluate helper function for subscribing to knockout observable.
铯包括一个 subscribeAndEvaluate 辅助功能,用于订阅可观察的敲除。

When using a subscription, always be sure to dispose the subscription when the viewmodel is no longer using it. Otherwise the listener will continue to be notified for the lifetime of the observable.
使用订阅时,始终确保在视图模型不再使用订阅时释放订阅。否则,侦听器将在可观察对象的生存期内继续收到通知。

fullscreenSubscription = subscribeAndEvaluate(fullscreenButton.viewModel, 'isFullscreenEnabled', function(isFullscreenEnabled) { ... });
// ...then later...
fullscreenSubscription.dispose();

GLSL GLSL

Naming 命名

  • GLSL files end with .glsl and are in the Shaders directory.
    GLSL 文件以着色器目录结尾,位于着色 .glsl 器目录中。
  • Files for vertex shaders have a VS suffix; fragment shaders have an FS suffix. For example: BillboardCollectionVS.glsl and BillboardCollectionFS.glsl.
    顶点着色器的文件具有 VS 后缀;片段着色器具有 FS 后缀。例如: BillboardCollectionVS.glslBillboardCollectionFS.glsl
  • Generally, identifiers, such as functions and variables, use camelCase.
    通常,标识符(如函数和变量)使用 camelCase .
  • Cesium built-in identifiers start with czm_, for example, czm_material. Files have the same name without the czm_ prefix, e.g., material.glsl.
    例如,铯内置标识符以 开头 czm_czm_material 文件具有相同的名称,不带 czm_ 前缀,例如 material.glsl .
  • Use czm_textureCube when sampling a cube map instead of texture. This is to preserve backwards compatibility with WebGL 1.
    在对立方体贴图进行采样时使用,而不是 czm_textureCube texture 。这是为了保持与WebGL 1的向后兼容性。
  • Varyings start with v_, e.g.,
    变化以 开头 v_ ,例如,
in vec2 v_textureCoordinates;
  • Uniforms start with u_, e.g.,
    制服以 开头 u_ ,例如,
uniform sampler2D u_atlas;
  • An EC suffix indicates the point or vector is in eye coordinates, e.g.,
    后缀表示 EC 点或矢量位于眼睛坐标中,例如,
varying vec3 v_positionEC;
// ...
v_positionEC = (czm_modelViewRelativeToEye * p).xyz;
  • When GPU RTE is used, High and Low suffixes define the high and low bits, respectively, e.g.,
    当使用 GPU RTE 时,后缀分别定义高位和低位,例如, High Low
attribute vec3 position3DHigh;
attribute vec3 position3DLow;
  • 2D texture coordinates are s and t, not u and v, e.g.,
    2D 纹理坐标是 s 和 , 不是 uv t ,例如
attribute vec2 st;

Formatting 格式

  • Use the same formatting as JavaScript, except put { on a new line, e.g.,
    使用与 JavaScript 相同的格式,除了放在 { 新行上,例如,
struct czm_ray
{
    vec3 origin;
    vec3 direction;
};

Performance 性能

  • 🚤 Compute expensive values as infrequently as possible, e.g., prefer computing a value in JavaScript and passing it in a uniform instead of redundantly computing the same value per-vertex. Likewise, prefer to compute a value per-vertex and pass a varying, instead of computing per-fragment when possible.
    🚤 尽可能少地计算昂贵的值,例如,更喜欢在 JavaScript 中计算一个值并以统一的方式传递它,而不是冗余地计算每个顶点相同的值。同样,如果可能,更喜欢计算每个顶点的值并传递变化值,而不是计算每个片段。
  • 🚤 Use discard sparingly since it disables early-z GPU optimizations.
    🚤 请谨慎使用 discard ,因为它会禁用早期 z GPU 优化。

Resources 资源

See Section 4.1 to 4.3 of Getting Serious with JavaScript by Cesium contributors Matthew Amato and Kevin Ring in WebGL Insights for deeper coverage of modules and performance.
请参阅 WebGL Insights 中 Cesium 贡献者 Matthew Amato 和 Kevin Ring 的 Getting Serious with JavaScript 的第 4.1 至 4.3 节,以更深入地介绍模块和性能。

Watch From Console to Chrome by Lilli Thompson for even deeper performance coverage.
not u and v, e.g.,
2D 纹理坐标是 s 和 , 不是 uv t ,例如

attribute vec2 st;

Formatting 格式

  • Use the same formatting as JavaScript, except put { on a new line, e.g.,
    使用与 JavaScript 相同的格式,除了放在 { 新行上,例如,
struct czm_ray
{
    vec3 origin;
    vec3 direction;
};

Performance 性能

  • 🚤 Compute expensive values as infrequently as possible, e.g., prefer computing a value in JavaScript and passing it in a uniform instead of redundantly computing the same value per-vertex. Likewise, prefer to compute a value per-vertex and pass a varying, instead of computing per-fragment when possible.
    🚤 尽可能少地计算昂贵的值,例如,更喜欢在 JavaScript 中计算一个值并以统一的方式传递它,而不是冗余地计算每个顶点相同的值。同样,如果可能,更喜欢计算每个顶点的值并传递变化值,而不是计算每个片段。
  • 🚤 Use discard sparingly since it disables early-z GPU optimizations.
    🚤 请谨慎使用 discard ,因为它会禁用早期 z GPU 优化。

Resources 资源

See Section 4.1 to 4.3 of Getting Serious with JavaScript by Cesium contributors Matthew Amato and Kevin Ring in WebGL Insights for deeper coverage of modules and performance.
请参阅 WebGL Insights 中 Cesium 贡献者 Matthew Amato 和 Kevin Ring 的 Getting Serious with JavaScript 的第 4.1 至 4.3 节,以更深入地介绍模块和性能。

Watch From Console to Chrome by Lilli Thompson for even deeper performance coverage.
观看 Lilli Thompson 的《从控制台到 Chrome》,了解更深入的性能报道。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

缠中说禅87

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值