├── Khan ├── javascript.md └── react.md ├── README.md ├── airbnb └── react.md └── react-starter-kit └── react-style-guide.md /Khan/javascript.md: -------------------------------------------------------------------------------- 1 | ## JavaScript Style Guide 2 | 3 | ---- 4 | 5 | * [Syntax](#syntax) 6 | * [Naming](#naming) 7 | * [Naming private methods and properties](#naming-private-methods-and-properties) 8 | * [File names](#file-names) 9 | * [Indentation](#indentation) 10 | * [Braces](#braces) 11 | * [Line length](#line-length) 12 | * [require() lines.](#require-lines) 13 | * [Comments and documentation](#comments-and-documentation) 14 | * [Inline Comments](#inline-comments) 15 | * [Top level file and class comments](#top-level-file-and-class-comments) 16 | * [Methods and properties comments](#methods-and-properties-comments) 17 | * [Core language rules](#core-language-rules) 18 | * [Equality](#equality) 19 | * [Array and Object literals](#array-and-object-literals) 20 | * [Use a new var statement for each declaration](#use-a-new-var-statement-for-each-declaration) 21 | * [Avoid href="#" for JavaScript triggers](#avoid-href-for-javascript-triggers) 22 | * [Use modules, not global variables](#use-modules-not-global-variables) 23 | * [ES6/7 rules](#es67-rules) 24 | * [Use => instead of bind(this) ](#use--instead-of-bindthis) 25 | * [Use backticks for string interpolation](#use-backticks-for-string-interpolation) 26 | * [Do not use ES6 classes for React classes](#do-not-use-es6-classes-for-react-classes) 27 | * [Do not use async/await or generators](#do-not-use-asyncawait-or-generators) 28 | * [Do not use Set or Map ](#do-not-use-set-or-map) 29 | * [Use let and const for new files; do not use var ](#use-let-and-const-for-new-files-do-not-use-var) 30 | * [Library rules](#library-rules) 31 | * [Use $ for jQuery](#use--for-jquery) 32 | * [Use `Promise` instead of `$.when()` or `$.Deferred()`](#use-promise-instead-of-when-or-deferred) 33 | * [Don't use Underscore](#dont-use-underscore) 34 | 35 | ---- 36 | 37 | This guide is adapted from the jQuery style guide. 38 | 39 | ---------- 40 | ### 語法 41 | 42 | #### Naming 43 | 44 | ```js 45 | ClassNamesLikeThis 46 | methodNamesLikeThis 47 | variableNamesLikeThis 48 | parameterNamesLikeThis 49 | propertyNamesLikeThis 50 | SYMBOLIC_CONSTANTS_LIKE_THIS 51 | ``` 52 | 53 | When naming variables and properties referring to jQuery element 54 | objects, prefix the name with `$`: 55 | 56 | ```js 57 | function doSomethingFancy(selector) { 58 | var $elements = $(selector); 59 | ... 60 | } 61 | ``` 62 | 63 | #### Naming private methods and properties 64 | 65 | Private methods and properties (in files, classes, and namespaces) 66 | should be named with a leading underscore. 67 | 68 | While we do not currently use any compilers to enforce this, clients 69 | of an API or class are expected to respect these conventions. 70 | 71 | ```js 72 | function _PrivateClass() { 73 | // should not be instantiated outside of this file 74 | } 75 | 76 | function PublicClass(param) { 77 | this.publicMember = param; 78 | this._privateMember = new _PrivateClass(); 79 | } 80 | 81 | var x = new _PrivateClass(); // OK - we’re in the same file. 82 | var y = new PublicClass(); // OK 83 | var z = y._privateMember; // NOT OK! 84 | ``` 85 | 86 | Rationale: leading underscores for private methods and properties is 87 | consistent with the styles used in numerous JavaScript libraries, many 88 | of which we include in our code base (e.g. Backbone). It is also 89 | consistent with our Python style guide, lowering the mental effort for 90 | developers to switch between the two. 91 | 92 | #### File names 93 | 94 | ``` 95 | file-names-like-this.js 96 | template-names-like-this.handlebars 97 | ``` 98 | 99 | #### Indentation 100 | 101 | Use 4-space indenting for all code. Do not use tabs. 102 | 103 | Extra indentation should be used to clearly distinguish multiline 104 | conditionals from the following block of code (similar to the PEP8 105 | rule for Python code). 106 | 107 | No: 108 | ```js 109 | if (someReallyLongBooleanVariableIMeanReallyLong && 110 | someOtherBoolean) { 111 | return "monkeys"; 112 | } 113 | ``` 114 | 115 | Yes: 116 | ```js 117 | if (someReallyLongBooleanVariableIMeanReallyLong && 118 | someOtherBoolean) { 119 | return "monkeys"; 120 | } 121 | ``` 122 | 123 | #### Braces 124 | 125 | Braces should always be used on blocks. 126 | 127 | `if/else/for/while/try` should always have braces and always go on 128 | multiple lines, with the opening brace on the same line. 129 | 130 | No: 131 | ```js 132 | if (true) 133 | blah(); 134 | ``` 135 | 136 | Yes: 137 | ```js 138 | if (true) { 139 | blah(); 140 | } 141 | ``` 142 | 143 | `else/else if/catch` should go on the same line as the brace: 144 | 145 | ```js 146 | if (blah) { 147 | baz(); 148 | } else { 149 | baz2(); 150 | } 151 | ``` 152 | 153 | #### Line length 154 | 155 | Lines should not exceed 79 characters. (This is called the "80 156 | character rule," leaving 1 character for the newline.) 157 | 158 | This is consistent with our Python style guide, which adheres to PEP8. 159 | 160 | 161 | #### `require()` lines. 162 | 163 | Separate first party and third party `require()` lines, and sort 164 | `require()` lines. 165 | 166 | This is to mirror our [Python import style](python.md#import-style), 167 | though there are no "system" imports in JavaScript. 168 | 169 | "First party" code is anything we wrote whose primary source lives in 170 | the repository its being used in. Underscore is third party because 171 | we didn't write it. KaTeX is third party in webapp because even 172 | though we wrote it, its primary sources lives in a different 173 | repository. 174 | 175 | Imports should be sorted lexicographically (as per unix `sort`). 176 | 177 | No: 178 | ```js 179 | var _ = require("underscore"); 180 | var $ = require("jquery"); 181 | var APIActionResults = require("../shared-package/api-action-results.js"); 182 | var Cookies = require("../shared-package/cookies.js"); 183 | var cookieStoreRenderer = require("../shared-package/cookie-store.handlebars"); 184 | var HappySurvey = require("../missions-package/happy-survey.jsx"); 185 | var DashboardActions = require('./datastores/dashboard-actions.js'); 186 | var React = require("react"); 187 | var UserMission = require("../missions-package/user-mission.js"); 188 | var Kicksend = require("../../third_party/javascript-khansrc/mailcheck/mailcheck.js"); 189 | ``` 190 | 191 | Yes: 192 | ```js 193 | var $ = require("jquery"); 194 | var Kicksend = require("../../third_party/javascript-khansrc/mailcheck/mailcheck.js"); 195 | var React = require("react"); 196 | var _ = require("underscore"); 197 | 198 | var APIActionResults = require("../shared-package/api-action-results.js"); 199 | var Cookies = require("../shared-package/cookies.js"); 200 | var DashboardActions = require('./datastores/dashboard-actions.js'); 201 | var HappySurvey = require("../missions-package/happy-survey.jsx"); 202 | var UserMission = require("../missions-package/user-mission.js"); 203 | var cookieStoreRenderer = require("../shared-package/cookie-store.handlebars"); 204 | ``` 205 | 206 | Object destructuring should go after all require lines. 207 | 208 | Write requires on a single line, even if they extend past 80 chars, so they are easier to sort. Our linter automatically skips require lines when checking line length. 209 | 210 | No: 211 | ```js 212 | var React = require("react"); 213 | var ReactART = require("react-art"); 214 | var Group = ReactART.Group; 215 | var Path = ReactART.Path; 216 | var _ = require("underscore"); 217 | 218 | var ItemStore = require("./item-store.jsx"); 219 | ``` 220 | 221 | Yes: 222 | ```js 223 | var React = require("react"); 224 | var ReactART = require("react-art"); 225 | var _ = require("underscore"); 226 | 227 | var ItemStore = require("./item-store.jsx"); 228 | 229 | var Group = ReactART.Group; 230 | var Path = ReactART.Path; 231 | ``` 232 | 233 | 234 | ------------------------------ 235 | ### Comments and documentation 236 | 237 | #### Inline Comments 238 | 239 | Inline style comments should be of the `//` variety, not the `/* */` 240 | variety. 241 | 242 | #### Top level file and class comments 243 | 244 | All files and classes should have JSDoc comments. 245 | 246 | JSDoc can be parsed by a number of open source tools, and must be well-formed. 247 | 248 | Syntax: 249 | ```js 250 | /** 251 | * A JSDoc comment should begin with a slash and 2 asterisks. 252 | */ 253 | ``` 254 | 255 | Top-level (top-of-file) comments are designed to orient readers 256 | unfamiliar with the code to what is in this file and any other 257 | disclaimers clients of the code should be given. It should provide a 258 | description of the file's contents and any dependencies or 259 | compatibility information. As an example: 260 | 261 | ```js 262 | /** 263 | * Various components to handle management of lists of coaches for 264 | * the profile page. 265 | * 266 | * These utilities were not written to be a general purpose utility 267 | * for the entire code base, but has been optimized with the 268 | * assumption that the Profile namespace is fully loaded. 269 | 270 | */ 271 | ``` 272 | 273 | Class comments should be used for every class, and give a description 274 | along with appropriate type tags (see "Methods and properties" 275 | comments for more information on types on the constructor). 276 | 277 | ```js 278 | /** 279 | * Class making something fun and easy. 280 | * 281 | * @param {string} arg1 An argument that makes this more interesting. 282 | * @param {Array.} arg2 List of numbers to be processed. 283 | */ 284 | function SomeFunClass(arg1, arg2) { 285 | // ... 286 | } 287 | ``` 288 | 289 | #### Methods and properties comments 290 | 291 | All non-trivial methods and properties should also have JSDoc comments. 292 | 293 | Type annotations are strongly encouraged; if there is even a slight 294 | chance that the type will be ambiguous to future readers, put in a 295 | type annotation. 296 | 297 | Type annotations are based on the ES4/JS2 type system, and are 298 | documented in the [Google JavaScript style 299 | guide](https://google.github.io/styleguide/javascriptguide.xml). 300 | 301 | `@param` and `@return` type annotations that have comments that do not 302 | fit on one line wrap to the next line and indent 4 spaces. 303 | 304 | Example: 305 | 306 | ```js 307 | /** 308 | * A UI component allows users to select badges from their full list 309 | * of earned badges, displaying them in a container. 310 | * Expects a Badges.BadgeList as a model. 311 | */ 312 | Badges.DisplayCase = Backbone.View.extend({ 313 | /** 314 | * Whether or not this is currently in edit mode and the full 315 | * badge list is visible. 316 | */ 317 | editing: false, 318 | 319 | /** 320 | * The full user badge list available to pick from when in edit mode. 321 | * @type {Badges.UserBadgeList} 322 | */ 323 | fullBadgeList: null, 324 | 325 | /** 326 | * Enters "edit mode" where badges can be added/removed. 327 | * @param {number=} index Optional index of the slot in the display-case 328 | * to be edited. Defaults to the first available slot, or if none 329 | * are available, the last used slot. 330 | * @return {Badges.DisplayCase} This same instance so calls can be 331 | * chained. 332 | */ 333 | edit: function(index) { 334 | … 335 | }, 336 | ... 337 | }; 338 | ``` 339 | 340 | ----------------------- 341 | ### Core language rules 342 | 343 | #### Equality 344 | 345 | Prefer `===` (strict equality) to `==` due to the [numerous oddities 346 | related to JavaScript's type coercion](https://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/). 347 | 348 | The only valid use of `==` is for comparing against null and undefined 349 | at the same time: 350 | 351 | ```js 352 | // Check null and undefined, but distinguish between other falsey values 353 | if (someVariable == null) { 354 | ``` 355 | 356 | Though you will often want to just check against falsey values, and 357 | can just say `if (!someVariable) { ... }`. 358 | 359 | #### Array and Object literals 360 | 361 | Always use `[]` and `{}` style literals to initialize arrays and 362 | objects, not the `Array` and `Object` constructors. 363 | 364 | Array constructors are error-prone due to their arguments: `new 365 | Array(3)` yields `[undefined, undefined, undefined]`, not `[3]`. 366 | 367 | To avoid these kinds of weird cases, always use the more readable 368 | array literal. 369 | 370 | Object constructors don't have the same problems, but follow the same 371 | rule for consistency with arrays. Plus, `{}` is more readable. 372 | 373 | #### Use a new var statement for each declaration 374 | 375 | No: 376 | ```js 377 | var a = "foo", 378 | b = a + "bar", 379 | c = fn(a, b); 380 | ``` 381 | 382 | Yes: 383 | ```js 384 | var a = "foo"; 385 | var b = a + "bar"; 386 | var c = fn(a, b); 387 | ``` 388 | 389 | A single var statement is bad because: 390 | 391 | * If you forget a comma, you just made a global 392 | * It originated when people wanted to save bytes, but we have a minifier 393 | * It makes line-based diffs/editing messier 394 | * It encourages C89-style declarations at the top of scope, preventing 395 | you from only declaring vars before first use, the latter preferable 396 | as it conveys intended scope to the reader 397 | 398 | #### Avoid `href="#"` for JavaScript triggers 399 | 400 | When you want a link-like thing rather than a button to trigger a 401 | JavaScript operation, rather than going to a new address. 402 | 403 | Here's a discussion on Stack Overflow about options: 404 | http://stackoverflow.com/questions/134845/href-tag-for-javascript-links-or-javascriptvoid0 405 | 406 | 407 | No: 408 | ```js 409 | Flag 410 | ``` 411 | 412 | Yes: 413 | ```js 414 | Flag 415 | ``` 416 | 417 | #### Use modules, not global variables 418 | 419 | In most of our major JavaScript repositories (webapp, perseus, 420 | khan-exercises), we use some form of module system like 421 | [RequireJS](http://requirejs.org/) or 422 | [browserify](http://browserify.org/), or in the case of webapp our own 423 | home built thing that works similarly to browserify. 424 | 425 | In all of these cases, there are mechanisms for an explicit 426 | import/export mechanism rather than using global variables to export 427 | functionality. 428 | 429 | No: 430 | ```js 431 | window.Jungle = { 432 | welcome: function() { 433 | // ... 434 | }, 435 | haveFever: function() { 436 | // ... 437 | } 438 | }; 439 | ``` 440 | 441 | **NO**: 442 | ```js 443 | window.welcome = function() { 444 | // ... 445 | }; 446 | 447 | window.haveFever = function() { 448 | // ... 449 | }; 450 | ``` 451 | 452 | Yes: 453 | ```js 454 | var Jungle = { 455 | welcome: function() { 456 | // ... 457 | }, 458 | haveFever: function() { 459 | // ... 460 | } 461 | }; 462 | 463 | module.exports = Jungle; 464 | ``` 465 | 466 | You can export multiple objects in one file, but consider if it 467 | wouldn't be better to split up the file to maintain one export per file. 468 | 469 | --------------- 470 | ### ES6/7 rules 471 | 472 | Several of our supported browsers support only ES5 natively. We use 473 | polyfills to emulate [some -- but not all -- ES6 and ES7 language 474 | features](https://docs.google.com/spreadsheets/d/12mF99oCpERzLKS07wPPV3GiISUa8bPkKveuHsESDYHU/edit#gid=0) 475 | so they run on ES5-capable browsers. 476 | 477 | In some cases, we do not yet allow a new language feature, if it's 478 | expensive to polyfill. In others, we require using the newer language 479 | feature and avoiding the old: 480 | 481 | | Construct | Use... | ...instead of | 482 | | --------- | ------------------------------------- | ---------------------- | 483 | | backticks | `` `http://${host}/${path}` `` | `"http://" + host + "/" + path` | 484 | | destructuring | `var { x, y } = a;` | `var x = a.x; var y = a.y;` | 485 | | fat arrow | `foo(() => { ... })` | `foo(function() { ... }.bind(this))` | 486 | | let/const | `let a = 1; const b = "4EVAH"; a++;` | `var a = 1; var b = "4EVAH"; a++;` | 487 | | includes | `array.includes(item)` | `array.indexOf(item) !== -1` | 488 | | for/of | `for (const [key, value] of Object.entries(obj)) { ... }` | `_.each(obj, function(value, key) { ... })` | 489 | | spread | `{ ...a, ...b, c: d }` | `_.extend({}, a, b, { c: d })` | 490 | | rest params | `function(bar, ...args) { foo(...args); }` | `function(bar) { var args = Array.prototype.slice.call(arguments, 1); foo.apply(null, args); }` | 491 | 492 | #### Use `=>` instead of `bind(this)` 493 | 494 | Arrow functions are easier to read (and with Babel, more efficient) 495 | than calling `bind` manually. 496 | 497 | #### Use rest params instead of `arguments` 498 | 499 | The magic `arguments` variable has some odd quirks. It's simpler to 500 | use rest params like `(...args) => foo(args)`. 501 | 502 | #### Use backticks for string interpolation 503 | 504 | `+` is not forbidden, but backticks are encouraged! 505 | 506 | #### Do not use ES6 classes for React classes 507 | 508 | Continue to use React's `createClass`, which works with React mixins. 509 | 510 | For classes outside of React -- which should actually be pretty rare 511 | -- there is no style rule whether to use ES6 classes or not. 512 | 513 | This rule may change once React supports mixins with ES6 classes. 514 | 515 | #### Do not use `async`/`await` or generators 516 | 517 | This is because the polyfill for these constructs generates very large 518 | code. 519 | 520 | This rule may change once all our supported browsers support ES6 521 | natively. 522 | 523 | #### Do not use `Set` or `Map` 524 | 525 | The polyfills for these, though not huge, are large enough it's not 526 | worth the (small) benefit of using these classes for hashtables 527 | instead of just using `object`. 528 | 529 | This rule may change if strong enough support for these types is 530 | evinced. 531 | 532 | #### Use `let` and `const` for new files; do not use `var` 533 | 534 | `let` is superior to `var`, so prefer it for new code. 535 | 536 | This rule will become mandatory everywhere once we have done a fixit 537 | to replace all uses of `var` in existing files. 538 | 539 | ----------------- 540 | ### Library rules 541 | 542 | #### Use `$` for jQuery 543 | 544 | We use `$` as the jQuery identifier, as opposed to typing out `jQuery` 545 | in full. 546 | 547 | No: 548 | ```js 549 | jQuery(".some-class span").hide(); 550 | ``` 551 | 552 | Yes: 553 | ```js 554 | $(".some-class span").hide(); 555 | ``` 556 | 557 | ### Use `Promise` instead of `$.when()` or `$.Deferred()` 558 | 559 | We use the [ES6 Promise polyfill](https://github.com/jakearchibald/es6-promise) on our site. In general the [MDN Polyfill Guide](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) is extremely helpful in determining how to best use the `Promise` object. 560 | 561 | Moving from `$.when()` to the `Promise` object is quite easy. 562 | 563 | Instead of... | Use... 564 | --------------|------------------- 565 | `$.when()` (no arguments) | `Promise.resolve()` 566 | `$.when.apply($, arrayOfPromises)` | `Promise.all(arrayOfPromises)` 567 | `$.when(promise)` | `promise` (`$.when` just returns the promise) 568 | 569 | Note that if you're calling `Promise.all()` on an array of promises that the result to be passed to the `.then()` callback will be an array of all the results from each of the promises in the array. More [details on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all). 570 | 571 | Moving from `$.Deferred` to `Promise` can be a bit trickier, it all depends upon how you were originally using it. The biggest difference is that in order to mark a `Promise` as resolved you must execute the callback function that was passed in to the `new Promise()` constructor. This is easy if you're tracking progress on some asynchronous operation: 572 | 573 | ``` 574 | var promise = new Promise((resolve) => { 575 | $.ajax(...).then(resolve); 576 | }); 577 | ``` 578 | 579 | However it gets a bit trickier when you want to resolve the promise at some later time, outside the scope of the instantiation function. Since there is no `.resolve()` method, as was available on `$.Deferred()` objects, a common pattern may look like this: 580 | 581 | ``` 582 | var markResolved; 583 | var promise = new Promise((resolve) => { 584 | markResolved = resolve; 585 | }); 586 | 587 | // later in your code... 588 | if (markResolved) { 589 | markResolved(); 590 | } 591 | ``` 592 | 593 | It's also important to note that Promises do not throw exceptions. If you wish to catch an exception you must explicitly attach a `.catch()` callback to it and listen for the error. 594 | 595 | #### Use `khanFetch()` instead of `$.ajax`/`$.get`/`$.post`/`$.getJSON` 596 | 597 | We now provide a polyfill for the [`fetch()` method](https://developer.mozilla.org/en-US/docs/Web/API/GlobalFetch/fetch). We should use this for all Ajax-style requests. We wrote a wrapper around `fetch()` which adds in some Khan Academy-specific logic (such as adding cachebusting parameters and handling API Action Results). The interface to `khanFetch()` is exactly the same as the normal `fetch()` function. You can use it like: 598 | 599 | ``` 600 | const { khanFetch } = require("./path-to-shared-package/khan-fetch.js"); 601 | ``` 602 | 603 | ##### `$.get()` 604 | 605 | Get some textual data: 606 | 607 | ``` 608 | khanFetch("/some.json") 609 | .then((response) => response.text()) 610 | .then((text) => { /* Use the textual data... */ }) 611 | .catch((err) => { /* Handle server error... */ }); 612 | ``` 613 | 614 | Get some JSON data (same use case as `$.getJSON`). 615 | 616 | ``` 617 | khanFetch("/some.json") 618 | .then((response) => response.json()) 619 | .then((json) => { /* Use the JSON data... */ }) 620 | .catch((err) => { /* Handle server error... */ }); 621 | ``` 622 | 623 | ##### `$.post()` 624 | 625 | POSTing JSON to an API endpoint and getting JSON back. 626 | 627 | ``` 628 | khanFetch("/api/some/endpoint", { 629 | method: "POST", 630 | headers: { 631 | "Content-Type": "application/json", 632 | }, 633 | body: JSON.stringify(myJSONObject), 634 | }) 635 | .then((response) => response.json()) 636 | .then((json) => { /* Use the JSON data... */ }) 637 | .catch((err) => { /* Handle server error... */ }); 638 | ``` 639 | 640 | POSTing form data to an API andpoint and getting JSON back. This is the default encoding that `$.post` used, so this should be used in place of `$.post(url, data)`. We wrote a function called `formUrlencode` to make this easy: 641 | 642 | ``` 643 | const { khanFetch, formUrlencode } = require("./path-to-shared-package/khan-fetch.js"); 644 | 645 | khanFetch("/api/some/endpoint", { 646 | method: "POST", 647 | body: formUrlencode({ 648 | key1: "value1", 649 | key2: 2, 650 | }), 651 | }) 652 | .then((response) => response.json()) 653 | .then((json) => { /* Use the JSON data... */ }) 654 | .catch((err) => { /* Handle server error... */ }); 655 | ``` 656 | 657 | #### Don't use Underscore 658 | 659 | We use ES6/7 which includes many of the features of Underscore.js! Using Underscore should be avoided in favor of these native language features. 660 | 661 | There are a couple of methods that are sufficiently complicated and don't have a direct equivalent so instead we have a [custom-built](https://lodash.com/custom-builds) copy of [lodash](https://lodash.com/) containing only those specific methods. You can find this file at: `third_party/javascript-khansrc/lodash/lodash.js` along with instructions on how to build it and exactly what methods are included. 662 | 663 | What follows is a method-by-method set of equivalents for what Underscore provides and what you could be using in ES6/7 instead: 664 | 665 | Method | Use... | ...instead of 666 | --------- | ------------------------------------- | ---------------------- 667 | bind | `fn.bind(someObj, args)` | `_.bind(fn, someObj, args)` 668 | bind | `(a, b) => { ... }` [1](#u1) | `_.bind(function(a, b) { ... }, this)` 669 | bindAll | `obj.method = obj.method.bind(someObj);` [2](#u2) | `_.bindAll(someObj, "method")` 670 | clone | No alternative at the moment! [3](#u3) | 671 | debounce | Our custom lodash build. | 672 | defer | `setTimeout(fn, 0);` | `_.defer(fn);` 673 | delay | `setTimeout(fn, 2000);` | `_.delay(fn, 2000);` 674 | each (array) | `array.forEach((val, i) => {})` | `_.each(array, (val, i) => {})` 675 | each (array) | `for (const val of array) {}` | `_.each(array, fn)` 676 | each (object) | `for (const [key, val] of Object.entries(obj)) {}` | `_.each(obj, fn)` 677 | extend (new) | `{...options, prop: 1}` | `_.extend({}, options, {prop: 1})` 678 | extend (assign) | `Object.assign(json, this.model.toJSON())` | `_.extend(json, this.model.toJSON())` 679 | filter | `array.filter(checkFn)` | `_.filter(array, checkFn)` 680 | has (array) | `array.includes(value)` | `_.has(array, value)` 681 | has (object) | `obj.hasOwnProperty(value)` [4](#u4) | `_.has(obj, value)` 682 | isArray | `Array.isArray(someObj)` | `_.isArray(someObj)` 683 | isFunction | `typeof fn === "function"` | `_.isFunction(fn)` 684 | isString | `typeof obj === "string"` | `_.isString(obj)` 685 | keys | `Object.keys(obj)` | `_.keys(obj)` 686 | last | `someArray[someArray.length - 1]` [5](#u5) | `_.last(someArray)` 687 | map | `array.map(mapFn)` | `_.map(array, mapFn)` 688 | max | `Math.max(...array)` | `_.max(array)` 689 | object |
Object.entries(obj).reduce(
(result, [key, val]) => {
    result[key] = value;
    return result;
})
|
\_.object(\_.map(obj, (val, key) => {
    return [key, value];
})
690 | omit (array) | `array.filter(prop => !props.includes(prop))` | `_.omit(array, props)` 691 | omit (object) |
Object.keys(obj).reduce((result, prop) => {
    if (!props.includes(prop)) {
        result[prop] = attrs[prop];
    }
}, {})
| `_.omit(obj, props)` 692 | once | `$(...).one("click", ...)` | `$(...).on("click", _.once(...))` 693 | once |
{
    method: () => {
        if (this._initDone) { return; }
        this._initDone = true;
        ...
    }
}
| `{ method: _.once(() => { ... }) }` 694 | once |
var getResult = () => {
    let val = $.when(...).then(...);
    getResult = () => val;
    return val;
};
|
var getResult = _.once(() => {
    return $.when(...).then(...);
});
695 | sortBy | `result = result.sort((a, b) => a.prop - b.prop)` | `_.sortBy(result, "prop")` 696 | sortedIndex | Our custom lodash build. | 697 | throttle | Our custom lodash build. | 698 | values | `Object.values(obj)` | `_.values(obj)` 699 | 700 | 1. To be used when you're creating a function and immediately binding its context to `this`. 701 | 2. Or use a loop if binding multiple methods. 702 | 3. No alternative at the moment! If you need it then you should add it to the compiled version of lodash and then update this guide to mention that it now exists! 703 | 4. While we recommend using `obj.hasOwnProperty(prop)` it is possible that the object could have a method named `hasOwnProperty` that does something else, causing this call to break. The likelihood of this happening is extremely slim - but if you're developing something that you wish to work absolutely everywhere you may want to do something like `Object.prototype.hasOwnProperty.call(obj, prop)`. 704 | 5. If you don't care about destructively modifying the array, you can also use `someArray.pop()``. 705 | -------------------------------------------------------------------------------- /Khan/react.md: -------------------------------------------------------------------------------- 1 | ## React 風格指引 2 | 3 | ---- 4 | * [語法](#syntax) 5 | * [按順序排序方法,生命週期放前面,render(渲染)放在最後面](#order-your-methods-with-lifecycle-first-and-render-last) 6 | * [用`handleEventName`來命名handlers(處理程序)](#name-handlers-handleeventname) 7 | * [用`onEventName`來命名在props(屬性)中的handlers(處理程序)](#name-handlers-in-props-oneventname) 8 | * [開頭元素放在同一行](#open-elements-on-the-same-line) 9 | * [對齊與排序HTML屬性](#align-and-sort-html-properties) 10 | * [只export(輸出)單一個react類別](#only-export-a-single-react-class) 11 | * [語言特性](#language-features) 12 | * [讓"presentation(呈現)"元件保持純粹(pure)](#make-presentation-components-pure) 13 | * [建議使用 props(屬性)而不是state(狀態).](#prefer-props-to-state) 14 | * [使用 propTypes.](#use-proptypes) 15 | * [絕對不要在DOM裡面儲存state(狀態)](#never-store-state-in-the-dom) 16 | * [伺服器端渲染](#server-side-rendering) 17 | * [Props必需為單純的JSON](#props-must-be-plain-json) 18 | * [props(屬性)與state(狀態)中的純粹函式](#pure-functions-of-props-and-state) 19 | * [副作用不受拘束直到`componentDidMount`](#side-effect-free-until-componentdidmount) 20 | * [React函式庫與元件](#react-libraries-and-components) 21 | * [不要使用Backbone的models(模型)](#do-not-use-backbone-models) 22 | * [儘可能最低限度的使用jQuery](#minimize-use-of-jquery) 23 | * [重覆使用標準的元件](#reuse-standard-components) 24 | 25 | ---- 26 | 27 | > 遵循一般的[JavaScript風格指引](javascript.md) - 包含一行80字元的限制。此外,還有許多React專有的規則。 28 | 29 | 除了這些風格規則外,你可能也會對這篇文章有興趣 30 | [React best practices](https://docs.google.com/document/d/1ChtFUao18IyNhaXZ5sE2W-CFuFcYnqlFTyi5gfe6XV0/edit). 31 | 32 | ---------- 33 | ### 語法 34 | 35 | #### 按順序排序方法,生命週期放前面,render(渲染)放在最後面。 36 | 37 | 在你的react元件中,你應該依照順序排序方法,例如這樣: 38 | 39 | 1. 生命週期(lifecycle)方法 (按照時間的先後順序: 40 | `getDefaultProps`, 41 | `getInitialState`, 42 | `componentWillMount`, 43 | `componentDidMount`, 44 | `componentWillReceiveProps`, 45 | `shouldComponentUpdate`, 46 | `componentWillUpdate`, 47 | `componentDidUpdate`, 48 | `componentWillUnmount`) 49 | 2. 其他的程式碼 50 | 3. `render` 51 | 52 | #### 用`handleEventName`來命名handlers(處理程序) 53 | 54 | 例如: 55 | 56 | ```jsx 57 | 58 | ``` 59 | 60 | #### 用`onEventName`來命名在props(屬性)中的handlers(處理程序) 61 | 62 | 這與React的事件命名方式是一致的: `onClick`, `onDrag`, 63 | `onChange`, 等等。 64 | 65 | 例如: 66 | 67 | ```jsx 68 | 69 | ``` 70 | 71 | 72 | #### 開頭元素放在同一行 73 | 74 | 一行80個字元的限制是有點緊湊,所以我們選擇保留額外的4個。 75 | 76 | Yes: 77 | ```jsx 78 | return
79 | ... 80 |
; 81 | ``` 82 | 83 | No: 84 | ```jsx 85 | return ( // "div" 沒放在和 "return" 同一行 86 |
87 | ... 88 |
89 | ); 90 | ``` 91 | 92 | #### 對齊與排序HTML屬性 93 | 94 | 如果可以的話,將它們都放在同一行。如果沒辦法的話,把每個屬性獨立自己一行放置,並縮排4個空白字元,依序放置。封閉尖括號(>)應該也要自己獨立一行,縮排則和開頭的尖括號(<)一致。這樣作可以很容易的對屬性一目瞭然。 95 | 96 | Yes: 97 | ```jsx 98 |
99 |
103 | 107 | ``` 108 | 109 | No: 110 | ```jsx 111 |
114 |
117 |
121 | ``` 122 | 123 | #### 只export(輸出)單一個react類別 124 | 125 | 每個.jsx檔案應該只會輸出單一個React類別,不會有其他的類別。 126 | 這是為了可測試性;fixture框架會要求這功能。 127 | 128 | 注意檔案中仍然可以定義多個類別,但它不能輸出超過一個以上類別。 129 | 130 | --------------------- 131 | ### 語言特性 132 | 133 | #### 讓"presentation(呈現)"元件保持純粹(pure) 134 | 135 | 思考把React的世界分成["logic" 136 | components 與 "presentation" components](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0)是很有好處的。 137 | 138 | "Logic(邏輯)"元件有應用程式的邏輯,但它們本身不會產出HTML。 139 | 140 | "Presentation(呈現)"元件是典型地可重覆使用的,以及的確會產出HTML。 141 | 142 | Logic(邏輯)元件可以有內部的state(狀態),但presentation(呈現)元件則完全不會有。 143 | 144 | #### 建議使用[props(屬性)而不是state(狀態)](http://facebook.github.io/react/docs/interactivity-and-dynamic-uis.html#what-components-should-have-state). 145 | 146 | 你幾乎總是要多使用props(屬性)。避免當可以使用props(屬性)時使用state(狀態),這樣可以最小化冗長多餘,讓你可以更容易思考你的應用程式。 147 | 148 | 通常的模式 — 可以符合"logic(邏輯)" vs "presentation(呈現)"元件區別 - 在這個層次結構裡,是建立一些只用於render(渲染)資料的stateless(無狀態)元件,而有些有狀態的元件則位於它們之上,透過props(屬性)傳遞它們的state(狀態)到它們的子元素。有狀態的元件封裝所有互動的邏輯,而無狀態的元件以陳述的方式負責渲染資料。 149 | 150 | 從props(屬性)複製資料到state(狀態),可能會造成脫離同步,這是特別糟的情況。 151 | 152 | #### 使用[propTypes](http://facebook.github.io/react/docs/reusable-components.html). 153 | 154 | React元件應該一定有完整的`propTypes`。每個`this.props`的屬性應該在`propTypes`裡有相對應的登錄項目。這裡記錄著需要要傳遞到model(模型)的props(屬性)。 155 | ([範例](https://github.com/Khan/webapp/blob/32aa862769d4e93c477dc0ee0388816056252c4a/javascript/search-package/search-results-list.jsx#L14)) 156 | 157 | 如果你要傳遞資料到達子元件,你可以使用屬性類型(prop-type) `.propTypes.`. 158 | 159 | 避免使用非描述性的屬性類型: 160 | * `React.PropTypes.any` 161 | * `React.PropTypes.array` 162 | * `React.PropTypes.object` 163 | 164 | 反之,使用 165 | * `React.PropTypes.arrayOf` 166 | * `React.PropTypes.objectOf` 167 | * `React.PropTypes.instanceOf` 168 | * `React.PropTypes.shape` 169 | 170 | 如果有例外的情況時,如果你要傳遞資料到達子元件,而且因為某些理由,你沒辦法使用`.propTypes.`時,你可以使用`React.PropType.any`。 171 | 172 | #### *絕對不要* 在DOM裡面儲存state(狀態) 173 | 174 | 不要使用`data-`屬性(attributes)或類別。所有的資訊應該被儲存在Javascript中,不論是在React元件自己裡面,或是如果使用了像Redux框架時的React儲存(store)中。 175 | 176 | ---------------------------------- 177 | 178 | ### 伺服器端的渲染 179 | 180 | 要使元件安全的渲染於伺服器端,它們必需比一般的元件,遵循更多一些的限制。 181 | 182 | #### Props必需為單純的JSON 183 | 184 | 為了要能在伺服器端渲染(render),props(屬性)要能從JSON串列化(serialized)與反串列化(deserialized)。這代表著例如日期需要以時間戳記(timestamps)傳遞,比對到JS的`Date`物件。 185 | 186 | 注意這只適用於被渲染於伺服器端渲染的根元件。如果根元件的從props(屬性)建構較複雜的資料結構,然後傳遞這些結構往下到子元件,這將不會造成問題。 187 | 188 | #### props(屬性)與state(狀態)中的純粹函式(pure function) 189 | 190 | 元件必需是他們的`props`與`state`的純粹函式。這代表著他們`render()`函式的輸出,必需是沒有依賴於KA-特定的全域(數值、API或物件),還是瀏覽器特定的狀態,或是瀏覽器特定的API。 191 | 192 | KA-特定的全域的範例,包含了所有附著在`KA`的全域(函式),例如`KA.getUserId()`,或是從DOM提取出的資料,像是附著其他DOM節點的`data-`屬性。 193 | 194 | 瀏覽器特定的狀態的範例,包含了使用者代理(user agent)、螢幕解析度、設備像素密度等等。 195 | 196 | 瀏覽器特定的API的範例,是`canvas.getContext()`。 197 | 198 | 輸出必需是確定的。有個方式可以得到不確定的輸出結果,就是在`getInitialState()`中產生隨機的ID,然後讓渲染的輸出依照這個ID。千萬不要這樣作。 199 | 200 | #### 副作用不受拘束直到`componentDidMount` 201 | 202 | React元件生命週期的幾個部份,是在伺服器端執行render(渲染),必需是沒有副作用的。 203 | 204 | 需要避免的副作用的範例: 205 | - 送出 AJAX 要求 206 | - 改變全域的JS狀態 207 | - 注射(Injecting)元素到DOM 208 | - 更改頁面標題 209 | - `alert()` 210 | 211 | 目前在伺服器端執行的生命週期方法: 212 | 213 | - `getInitialState()` 214 | - `getDefaultProps()` 215 | - `componentWillMount()` 216 | - `render()` 217 | 218 | 如果你需要執行上列其中任何一個副作用,你需要在元件生命週期的`componentDidMount`或之後作這件事。這些函式並不是在伺服器端執行的。 219 | 220 | ---------------------------------- 221 | ### React函式庫與元件 222 | 223 | #### 不要使用Backbone的models(模型) 224 | 225 | 使用flux的actions(動作),或是直接使用`$.ajax`作為取代。 226 | 227 | 我們試著要從我們的codebase整個移除掉Backbone。 228 | 229 | #### 儘可能最低限度的使用jQuery. 230 | 231 | *絕對不要* 使用jQuery來作DOM的操作處理。 232 | 233 | 試著避免使用jQuery外掛。真的有需要時,用一個React元件包裹jQuery外掛,這樣你只需要接觸到jQuery一次。 234 | 235 | 你可以使用`$.ajax` (但不要用其他的函式,例如`$.post`) 來進行網路的通訊。 236 | 237 | #### 重覆使用標準的元件 238 | 239 | 如果可能的話,重覆使用現成的元件,特別是那些直接產出HTML的低階、純粹的元件。如果你寫了一個新的這類元件,然後要用在不同的專案中,把它放在一個共享的位置,例如像react.js的套件包裡。 240 | 241 | 已經開放原始碼,那些有用的元件,它們標準的共享位置位於`react-components.js`套件包,在 242 | `javascript-packages.json`之中。這裡面包含了如下的元件: 243 | 244 | * `SetIntervalMixin` - 提供setInterval方法,這可以讓你每 x 毫秒作某件事。 245 | * `$_` - i18n封装(wrapper),讓你可以在React中作翻譯文字。 246 | * `TimeAgo` - “5分鐘前”之類的 - 這取代了$.timeago 247 | 248 | 可重覆使用的元件,但沒有或尚未開放原始碼的,是位在(命名還沒有好)`react.js`套件中。這包含了像是這些: 249 | 250 | * `KUIButton` - 渲染(render)Khan Academy風格的按鈕。 251 | * `Modal` - 建立一個跳出的模式對話視窗(modal dialog)。 252 | 253 | 這份列表並沒有完整列出,還差滿多的。 254 | 255 | ### 256 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # javascript-style-guide-translate 2 | 3 | 主要為翻譯與收集來自其他的Javascript風格指引的文件。 4 | 5 | ## 列表 List 6 | 7 | - [Khan Academy Coding Style Guides](https://github.com/Khan/style-guides) 8 | - [Airbnb React/JSX style guide](https://github.com/airbnb/javascript/tree/master/react) 9 | - [React Style Guide](https://github.com/kriasoft/react-starter-kit/blob/master/docs/react-style-guide.md) 10 | 11 | ## 動機 12 | 13 | 軟體技術文件不同於一般的科技文件或文章,它有其特別領域的專業,對於翻譯者而言,除了本身的英文閱讀能力要足夠外,還要求要有一定的專業程度。網路上有許多文件被翻譯成為中文,有可能是繁體中文或簡體中文,或是由簡體中文直接轉譯為繁體中文,但其中的翻譯品質常出現很大的問題,除了其內容與英文原意可能不同外,甚至出現無法使用中文來閱讀理解的情況。 14 | 15 | 這份資料是希望能用比較好的方式進行技術原文的翻譯,主要的領域是著重在Javascript、React相關的入門文件與指引。 16 | 17 | ## 翻譯指引 18 | 19 | 翻譯的指引通用於所有文件的翻譯,提供一些翻譯上的指引。 20 | 21 | ### 目的: 始終不改變原文原意 22 | 23 | 翻譯的目的是對於原文的原意,儘可能以中文翻譯其內容涵意,可視情況縮短或增長說明文字,但始終不對原文的原意進行刪減或額外增加。 24 | 25 | 需增加額外的解說,需用"譯者註"在後面加註說明,代表這是由譯者額外增加的。例如原文可能有誤或提供更多額外的資訊。為保持原文原意的完整,譯者註儘可能簡潔與少使用。 26 | 27 | 如果對原文所說明的內容,以翻譯者的專業尚難以理解,儘可能保留原文,以加註類似於"本文因原文涵意譯者尚無法理解,因此保留原文"的字句於文章最上方。 28 | 29 | 儘可能翻譯所有原文為中文,不要過度出現中英文混雜的使用情況。 30 | 31 | ### 專有名詞 32 | 33 | 對於專有名詞,例如函式庫中的定義、函式、方法、物件、API、關鍵字等專用名詞,保留原字詞不進行"取代式"的翻譯,而使用括號(())加註其後作為補充翻譯,專有名語可以使用粗體、空格分隔明顯區分,例如: 34 | 35 | ``` 36 | state(狀態) 37 | props(屬性) 38 | ``` 39 | 40 | 對於專用術語、形容詞、或可能通用的一詞多義的英文字詞,翻譯其字詞,但使用括號(())將原英文字詞加註其後,作為補充,例如: 41 | 42 | 原文字句: 43 | 44 | ``` 45 | Use PascalCase for React components and camelCase for their instances. 46 | ``` 47 | 48 | 中文翻譯字句: 49 | 50 | ``` 51 | 對React元件使用 巴斯卡(PascalCase) 命名法,它們的實例則使用 小駝峰(camelCase) 命名法。 52 | ``` 53 | 54 | ### 符號 55 | 56 | 對於在原文解釋用句中,所提供的符號,儘可能在翻譯時使用括號(())加註符號在翻譯字句中。例如: 57 | 58 | 原文字句: 59 | 60 | ``` 61 | Always use double quotes (") for JSX attributes, but single quotes for all other JS 62 | ``` 63 | 64 | 中文翻譯字句: 65 | 66 | ``` 67 | 對JSX的屬性,必定使用雙引號("),而對其他的JS語法則使用單引號(')。 68 | ``` 69 | 70 | ### 標題 71 | 72 | 文章的標題可視情況進行翻譯,但較好的作法是保留原文字詞,並於其後加註中文翻譯。 73 | 74 | ### 音標 75 | 76 | 可以使用中文或混用英文標註英文發音,使用斜線符號(//)框住音標註記,例如: 77 | 78 | ``` 79 | const/康死/ 是 英文constant/康死撐/ 的簡寫。 80 | ``` 81 | 82 | ### 複數 83 | 84 | 當英文中使用複數或有集合類的字詞時,使用中文擬人化的"們"或副詞"這些"、"那些"、"多個"等附加詞,讓讀者理解這是集合類或複數的名詞,例如: 85 | 86 | ``` 87 | If your component has multi-line properties, close its tag on a new line 88 | ``` 89 | 90 | 中譯為: 91 | 92 | ``` 93 | 如果你的元件有多行的多個屬性時,在新的下一行在封閉它的標記。 94 | ``` 95 | 96 | ### 省略的主詞 97 | 98 | 當英文中的主詞被省略時,或是要分拆過長的英文語句時,可以再加上原先的主詞,讓翻譯字句表達得更清楚明確。 99 | 100 | ### 未來、過去、現在進行式與時間順序 101 | 102 | 英文中有使用`will`通常為未來式,過去式的形態雖然也常用於被動會比較不明確,但過去式有代表已完成的意涵,現在進行式與單使用動作的動詞類似語法。 103 | 104 | 代表時間通常會有`when`、`before`、`after`等介系詞,在中譯時要注意順序,通常如果是出現倒裝句,會有較為重點的說明會在前面。 105 | 106 | ``` 107 | render() 108 | 109 | The render() method is required. 110 | 111 | When called, it should examine this.props and this.state and return a single React element. This element can be either a representation of a native DOM component, such as
, or another composite component that you've defined yourself. 112 | ``` 113 | 114 | 中譯 115 | 116 | ``` 117 | render() 118 | 119 | render方法是必要的。 120 | 121 | 當render被呼叫時,它應該會對this.props與this.state進行檢查,然後回傳單一個React元素。這元件 122 | 可能是一個對原生DOM元件的真實呈現,例如
,或是另一個你自行定義的合成元件。 123 | 124 | ``` 125 | 126 | ### 重覆贅詞/不需要的冠名詞 127 | 128 | 在一句英文語句中,重覆的贅詞可以不需要完全翻譯,可以在充份已表達意思時,去除這些贅詞。通常`The`, `This`, `These`等冠名詞,經常也不需要翻譯。例如: 129 | 130 | ``` 131 | Your components tell React what you want to render – then React will efficiently update and render just the right components when your data changes. 132 | ``` 133 | 134 | 完整的中譯文為: 135 | 136 | ``` 137 | 你的元件告訴React你想要渲染什麼 - 然後當你的資料更動時,React將會有效地更新與渲染必要的元件。 138 | ``` 139 | 140 | "你的"、"你"重覆過多,去除某幾個之後的調整後中譯文: 141 | 142 | ``` 143 | 你的元件告訴React想要渲染什麼 - 然後當資料更動時,React將會有效地更新與渲染必要的元件。 144 | ``` 145 | 146 | ### 程式碼註解 147 | 148 | 程式碼註解可以翻譯,但程式碼註解有時過於簡略時也可以略去不翻譯。 149 | 150 | ### 中文字詞與符號 151 | 152 | 可以使用全形的逗號(,)、分號(、)與句號(。),但儘量不使用用全形的括號(())、框號(「」)與其他符號等。而是使用原文提供的半形符號。 153 | 154 | 適當的在中文字詞的前後加上空白字元,作為明顯的區隔出這個字詞。例如: 155 | 156 | ``` 157 | 使用 擴充套件->管理 進入這個管理的頁面。 158 | ``` 159 | 160 | 不要使用過短的文言式字詞來作翻譯,儘可能以較合理的解譯方式來翻譯。例如: 161 | 162 | 原文字句: 163 | 164 | ``` 165 | Reusable Components 166 | ``` 167 | 168 | 不好的中文翻譯字句: 169 | 170 | ``` 171 | 可重用元件 172 | ``` 173 | 174 | 較佳的中文翻譯字句: 175 | 176 | ``` 177 | 可重覆使用的元件 178 | ``` 179 | 180 | 不要使用單一句過長的中文字句,儘可能多使用逗號(,)或句號(。)分隔翻譯字句以方便閱讀。例如: 181 | 182 | 原文字句: 183 | 184 | ``` 185 | Use computed property names when creating objects with dynamic property names. 186 | ``` 187 | 188 | 不好的中文翻譯字句: 189 | 190 | ``` 191 | 建立具有動態屬性名稱的物件時請使用計算型的屬性名稱。 192 | ``` 193 | 194 | 較佳的中文翻譯字句: 195 | 196 | ``` 197 | 在建立帶有動態屬性名稱的物件時,使用計算型屬性(computed property)名稱。 198 | ``` 199 | 200 | ### 繁體與簡體中文字詞 201 | 202 | 不要直接把簡體中文字詞,直接套用到繁體中文字詞上,或是繁中字詞直接轉為簡中,而應該儘可能轉換為繁中或簡中使用時的專門名詞。。例如以下常見的對照字詞: 203 | 204 | ``` 205 | 數據庫 vs 資料庫 206 | 组件 vs 元件 207 | 用戶 vs 使用者 208 | 默認 vs 預設 209 | 代碼 vs 程式碼 210 | 視頻 vs 影片 211 | ``` 212 | -------------------------------------------------------------------------------- /airbnb/react.md: -------------------------------------------------------------------------------- 1 | # Airbnb React/JSX Style Guide 2 | 3 | *對於 React 與 JSX 大部份情況下都適當的作法* 4 | 5 | ## Table of Contents 6 | 7 | 1. [基本規則](#basic-rules) 8 | 1. [Class vs `React.createClass` vs stateless](#class-vs-reactcreateclass-vs-stateless) 9 | 1. [命名](#naming) 10 | 1. [宣告](#declaration) 11 | 1. [對齊](#alignment) 12 | 1. [引號](#quotes) 13 | 1. [空白](#spacing) 14 | 1. [屬性(Props)](#props) 15 | 1. [括號](#parentheses) 16 | 1. [標籤](#tags) 17 | 1. [方法](#methods) 18 | 1. [順序](#ordering) 19 | 1. [`isMounted`](#ismounted) 20 | 21 | ## 基本規則 22 | 23 | - 在每個程式碼檔案中只包含一個React元件(component) 24 | - 不過,把多個[無狀態、純粹的元件](https://facebook.github.io/react/docs/reusable-components.html#stateless-functions) 同時寫在一個檔案中是允許的。eslint: [`react/no-multi-comp`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-multi-comp.md#ignorestateless). 25 | - 必定使用 JSX 語法。 26 | - 不要使用`React.createElement`,除非你需要從一個不是JSX的檔案,來初始化應用程式。 27 | 28 | 29 | ## Class(類別) vs `React.createClass` vs stateless(無狀態) 30 | 31 | - 如果你會有內部的 state(狀態) 與/或 refs(參照),用 `class extends React.Component` 會比 `React.createClass` 更好,除非你有非得需要使用 mixins(混入) 的理由。eslint: [`react/prefer-es6-class`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-es6-class.md) [`react/prefer-stateless-function`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-stateless-function.md) 32 | 33 | > 譯者註:ES6 Class的對於Mixins解決方式,可參考[Mixins Are Dead. Long Live Composition](https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750#.toy96djeg) 34 | 35 | 36 | ```javascript 37 | // bad 38 | const Listing = React.createClass({ 39 | // ... 40 | render() { 41 | return
{this.state.hello}
; 42 | } 43 | }); 44 | 45 | // good 46 | class Listing extends React.Component { 47 | // ... 48 | render() { 49 | return
{this.state.hello}
; 50 | } 51 | } 52 | ``` 53 | 54 | 而如果你並沒有用到 state 或 refs,建議連類別都不要使用,使用一般的函式就好(不要用箭頭函式(arrow functions)): 55 | 56 | ```javascript 57 | 58 | // bad 59 | class Listing extends React.Component { 60 | render() { 61 | return
{this.props.hello}
; 62 | } 63 | } 64 | 65 | // bad (因為箭頭函式不會有"name(名稱)"屬性) 66 | const Listing = ({ hello }) => ( 67 |
{hello}
68 | ); 69 | 70 | // good 71 | function Listing({ hello }) { 72 | return
{hello}
; 73 | } 74 | ``` 75 | 76 | ## 命名 77 | 78 | - **副檔名**: React元件檔案使用`.jsx`副檔名。 79 | - **檔案名稱**: 檔案名稱使用巴斯卡(PascalCase)命名法。例如`ReservationCard.jsx`。 80 | - **引用(Reference)命名**: React元件使用巴斯卡(PascalCase)命名法,它們的實例則使用小駝峰(camelCase)命名法。eslint: [`react/jsx-pascal-case`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-pascal-case.md) 81 | 82 | ```javascript 83 | // bad 84 | import reservationCard from './ReservationCard'; 85 | 86 | // good 87 | import ReservationCard from './ReservationCard'; 88 | 89 | // bad 90 | const ReservationItem = ; 91 | 92 | // good 93 | const reservationItem = ; 94 | ``` 95 | 96 | - **元件命名**: 檔案名稱與元件名稱相同。例如`ReservationCard.jsx`應該有個引用名稱`ReservationCard`。不過,如果這是在一個目錄中的根(root)元件,則使用`index.jsx`作為檔案名稱,然後使用目錄名稱作為元件名稱: 97 | 98 | ```javascript 99 | // bad 100 | import Footer from './Footer/Footer'; 101 | 102 | // bad 103 | import Footer from './Footer/index'; 104 | 105 | // good 106 | import Footer from './Footer'; 107 | ``` 108 | 109 | ## 宣告(Declaration) 110 | 111 | - 不要使用`displayName`來命名元件。而是用引用(reference)來命名元件。 112 | 113 | ```javascript 114 | // bad 115 | export default React.createClass({ 116 | displayName: 'ReservationCard', 117 | // stuff goes here 118 | }); 119 | 120 | // good 121 | export default class ReservationCard extends React.Component { 122 | } 123 | ``` 124 | 125 | ## 對齊 126 | 127 | - 遵守JSX語法中的那些對齊樣式。eslint: [`react/jsx-closing-bracket-location`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md) 128 | 129 | ```javascript 130 | // bad 131 | 133 | 134 | // good 135 | 139 | 140 | // 如果props(屬性)可以剛好寫在一行,就放在同一行 141 | 142 | 143 | // 子元素通常會縮排 144 | 148 | 149 | 150 | ``` 151 | 152 | ## 引號 153 | 154 | - 對JSX的屬性,必定使用雙引號(`"`),而對其他的JS語法則使用單引號(`'`)。eslint: [`jsx-quotes`](http://eslint.org/docs/rules/jsx-quotes) 155 | 156 | > 為什麼? 因為JSX屬性 [無法包含跳脫引號](http://eslint.org/docs/rules/jsx-quotes),所以雙引號才能配合像`"don't"`這樣的輸入字詞。 157 | > 一般的HTML屬性在習慣上會使用雙引號,而不會使用單引號,所以JSX屬性也使用這個慣例。 158 | 159 | ```javascript 160 | // bad 161 | 162 | 163 | // good 164 | 165 | 166 | // bad 167 | 168 | 169 | // good 170 | 171 | ``` 172 | 173 | ## 空白 174 | 175 | - 必定包含一個單格空白字元( )在你的自封閉標籤(/>)。 176 | 177 | ```javascript 178 | // bad 179 | 180 | 181 | // very bad 182 | 183 | 184 | // bad 185 | 187 | 188 | // good 189 | 190 | ``` 191 | 192 | ## Props(屬性) 193 | 194 | - 必定使用小駝峰(camelCase)命名法,來命名prop(屬性)名稱。 195 | 196 | ```javascript 197 | // bad 198 | 202 | 203 | // good 204 | 208 | ``` 209 | 210 | - 當prop(屬性)是明確的`true`值時,省略它的數值。eslint: [`react/jsx-boolean-value`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md) 211 | 212 | ```javascript 213 | // bad 214 |