├── .babelrc ├── .eslintrc ├── .gitignore ├── LICENSE ├── README.md ├── content ├── Babel │ ├── index.md │ └── pics │ │ └── babel.jpg ├── Nastroje │ ├── index.md │ └── pics │ │ ├── browserify.png │ │ ├── chrome.png │ │ ├── npm.png │ │ ├── sublime.png │ │ ├── terminal.png │ │ └── webpack.png ├── README.md ├── React-jsx │ ├── index.md │ └── pics │ │ └── error.png ├── React-props-vs-state │ └── index.md ├── React-uvod │ ├── index.md │ └── pics │ │ └── logo.png ├── Stack │ ├── index.md │ └── pics │ │ ├── hotreload.png │ │ ├── result.gif │ │ ├── webpack.png │ │ └── wheels.png └── index.md ├── examples └── react │ ├── addCar.js │ ├── addCar.less │ ├── car.js │ ├── car1.js │ ├── cars.js │ ├── cars1.js │ ├── cars2.js │ ├── cars3.js │ ├── counter.js │ ├── formNoJSX.js │ └── formNoJSX2.js ├── package.json ├── scripts ├── buildSiteIndexPages.sh ├── buildStaticSite.sh ├── cssTransformLoader.js ├── markdownLoader.js ├── prism.js ├── publishStaticSite.sh ├── resolvers.js └── startSiteDevServer.sh └── site ├── Constants.js ├── IndexPage.js ├── LICENSE ├── README.md ├── _config.yml ├── base.less ├── client.js ├── components ├── CodeBlock.js ├── CodeBlock.less ├── Footer.js ├── Footer.less ├── Header.js ├── Header.less ├── Links.js ├── Links.less ├── NavBar.js ├── NavBar.less ├── PageBody.js ├── PageBody.less └── StaticHTMLBlock.js ├── constants.less ├── favicon.png ├── feed.xml ├── logo.png ├── pages ├── ArticlePage.js └── HomePage.js ├── renderPath.js ├── webpack-client.config.js └── webpack-prerender.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "stage": 0, 3 | "loose": ["es6.modules", "es6.classes"] 4 | } 5 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "node": true 5 | }, 6 | "ecmaFeatures": { 7 | "modules": true 8 | }, 9 | "parser": "babel-eslint", 10 | "rules": { 11 | "quotes": [2, "single"], 12 | "eol-last": [0], 13 | "no-mixed-requires": [0], 14 | "no-underscore-dangle": [0], 15 | "no-alert": [0], 16 | "new-cap": [0], 17 | "no-empty": [0] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | modules 3 | .DS_Store 4 | .idea 5 | __site__ 6 | __site_prerender__ 7 | npm-debug.log 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported 2 | 3 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE. 4 | License 5 | 6 | THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. 7 | 8 | BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. 9 | 10 | 1. Definitions 11 | 12 | a. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License. 13 | 14 | b. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License. 15 | 16 | c. "Distribute" means to make available to the public the original and copies of the Work through sale or other transfer of ownership. 17 | 18 | d. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License. 19 | 20 | e. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast. 21 | 22 | f. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work. 23 | 24 | g. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. 25 | 26 | h. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images. 27 | 28 | i. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium. 29 | 30 | 2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws. 31 | 32 | 3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: 33 | 34 | a. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; and, 35 | 36 | b. to Distribute and Publicly Perform the Work including as incorporated in Collections. 37 | 38 | The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats, but otherwise you have no rights to make Adaptations. Subject to 8(f), all rights not expressly granted by Licensor are hereby reserved, including but not limited to the rights set forth in Section 4(d). 39 | 40 | 4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: 41 | 42 | a. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(c), as requested. 43 | 44 | b. You may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in connection with the exchange of copyrighted works. 45 | 46 | c. If You Distribute, or Publicly Perform the Work or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work. The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Collection, at a minimum such credit will appear, if a credit for all contributing authors of Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties. 47 | 48 | d. For the avoidance of doubt: 49 | 50 | i. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; 51 | 52 | ii. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License if Your exercise of such rights is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(b) and otherwise waives the right to collect royalties through any statutory or compulsory licensing scheme; and, 53 | 54 | iii. Voluntary License Schemes. The Licensor reserves the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License that is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(b). 55 | 56 | e. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. 57 | 58 | 5. Representations, Warranties and Disclaimer 59 | 60 | UNLESS OTHERWISE MUTUALLY AGREED BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. 61 | 62 | 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 63 | 64 | 7. Termination 65 | 66 | a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. 67 | 68 | b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. 69 | 70 | 8. Miscellaneous 71 | 72 | a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License. 73 | 74 | b. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. 75 | 76 | c. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. 77 | 78 | d. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You. 79 | 80 | e. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law. 81 | 82 | Creative Commons Notice 83 | 84 | Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor. 85 | 86 | Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of this License. 87 | 88 | Creative Commons may be contacted at http://creativecommons.org/. 89 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #JavaScript pro webaře 2 | 3 | Hledáte články o JavaScriptu? Najdete je nyní na vlastní stránce zde: 4 | 5 | **[http://www.dzejes.cz](http://www.dzejes.cz)** 6 | 7 | Články jsou i nadále opensourcované v tomto repozitáři v adresáři `content`. Pro lepší čitelnost se z nich ale nyní generují [samostatné HTML stránky](http://www.dzejes.cz). V adresáři `site` pak naleznete i nástroj, kterým se tyto stránky generují. Je mimochodem také napsaný v JavaScriptu a Reactu. 8 | 9 | **Pokud chcete započít diskuzi**, založte prosím [novou issue](https://github.com/tajo/javascript/issues). 10 | 11 | **Pokud chcete opravit nějakou chybu**, či nekterý z článků vylepšit, založte prosím [nový pull request](https://github.com/tajo/javascript/pulls). 12 | 13 | Cílem těchto článků je seznámit programátory se základy moderního vývoje webových stránek za pomocí JavaScriptu a namotivovat je tak ke hlubšímu samostudiu. Není cílem poskytnout úplnou a vyčerpávající dokumentaci a překlad ke všem popisovaným technologiím. 14 | 15 | Prosím [followujte](http://twitter.com/vmiksu), hvězdičkujte, forkujte, sdílejte a časem tu snad vznikne něco pěkného. :) 16 | -------------------------------------------------------------------------------- /content/Babel/index.md: -------------------------------------------------------------------------------- 1 | #JavaScript? Babel. 2 | 3 | JavaScript má oproti většině ostatních jazyků jedno specifikum. Je sám o sobě obtížně **použitelný**. Na vině je především způsob, jakým dochází k uvádění jeho nových verzí. Jak si s tím úspěšně a elegantně poradit? 4 | 5 | ##JavaScript vs ECMAScript 6 | 7 | JavaScript je pouze jednou z implementací standardu jménem **ECMAScript**. Díky obrovské popularitě JavaScriptu se ovšem tyto dva termíny často libovolně zaměňují. Existují však ještě další implementace jako JScript či ActionScript. 8 | 9 | Ecma International od roku 1997 postupně vydává jednotlivé specifikace. Poslední stabilní verze je **[6 z roku 2015](http://www.ecma-international.org/ecma-262/6.0/)** často označovaná také jako **ES6**, **ES2015** či **Harmony**. Vývoj však neustává a tak se už pracuje na další verzi s pořadovým číslem sedm (také se užívá označení next). 10 | 11 | ##Podpora prohlížečů 12 | 13 | Implementace standardů je zdlouhavý proces, i přestože začíná ještě před finální verzí specifikací. ES6 se už těší velmi slušné podpoře. Detailní přehled naleznete [zde](http://kangax.github.io/compat-table/es6/). Potíž však je, že ne všichni uživatelé používají poslední verze prohlížečů. Největší problém je tradičně u Internet Exploreru, kde i ten nejnovější jedenáctý ES6 téměř vůbec nepodporuje. V praxi je tak potřeba se ES6 zatím úplně vyhnout. Lepší situace je u Node.js, kde poslední šestá verze má už 93% podporu ES6. 14 | 15 | ##Co s tím? 16 | My, tvůrci webových aplikací, musíme zajistit, aby naše stránky správně fungovaly ve všech běžně používaných prohlížečích. Jak si ale poradit s tím, že každý prohlížeč podporuje jiný set standardu Harmony? Nejjednodušším řešením je se zatím 6. verzi zcela vyhnout a **používat pouze funkce z verze 5.1**, která je již plně etablovaná. Nicméně tím se ocitnete v pozici malého dítěte, které v hračkárně toužebně pokukuje po mluvících a chodících robůtcích, zatímco doma si ještě několik let bude muset hrát s klacky a kameny. Předchozí příklad je samozřejmě nadsázka. Tak propastný rozdíl mezi verzemi zase není. Nicméně nebojte, existují lepší řešení. 17 | 18 | ##Shimujeme 19 | ES6 přináší dva typy novinek. Tou první jsou **nové objekty a funkce** na již existujících objektech. K těm si můžeme dopomoci jednoduše tím, že k projektu připojíme kusy kódu nazývané [shimy a polyfilly](https://github.com/es-shims). Ty pak detekují chybějící či odlišné API jednotlivých prohlížečů a následně je doplní a sjednotí. V budoucnu, až budou prohlížeče daný standard plně podporovat, můžeme tyto knihovny úplně odstranit. 20 | 21 | ##Kompilujeme 22 | Druhou novinkou je nová syntax. V ES6 nám například přibyla nová klíčová slova jako `class`, `extends`, `let` či `import`. Tady si bohužel pouze s připojenou knihovnou už nevystačíme. Musíme použít **program - kompilátor**, který náš kód přeloží do starší plně podporované verze. V našem případě ES5. Navíc vyřeší i ony nové objekty a funkce z předchozí kapitoly (někdy ale bude i tak potřeba přidat polyfill). I zde platí, že v budoucnu budeme moct kompilátor z procesu úplně vynechat a náš kód bude v prohlížečích fungovat bez dodatečných úprav. 23 | 24 | ##V budoucnu? 25 | Problém ovšem je, že jakmile budou všichni plně podporovat ES6, tak tu budeme mít finální specifikaci ES7 a mlsně pokukovat po ES8. Prakticky to znamená jediné. **Samotný JavaScript nikdy používat nemůžeme**, pokud nechceme být neustále 3 roky pozadu. Nejlepším řešením je tedy vždy používat jazyk kompilovaný do JavaScriptu. 26 | 27 | ##Kompilátory 28 | Nových jazyků a kompilátorů existuje celá řada. Dají se rozdělit do 2 základních kategorií. Tou první jsou zcela nové jazyky, které mají úplně odlišnou syntax a nemůžeme je tedy použít na vylepšení již hotových projektů. Jde například o populární [CoffeeScript](http://coffeescript.org), [PureScript](http://www.purescript.org), [ClojureScript](https://github.com/clojure/clojurescript) či [Dart](https://www.dartlang.org) (ten se snažil dokonce úplně emancipovat od JS ale neúspěšně). Druhou kategorií jsou jazyky, které současný JavaScript pouze rozšiřují a snaží se více či méně zachovat dopřednou kompatibilitu. Jde třeba o [TypeScript](http://www.typescriptlang.org), [Traceur](https://github.com/google/traceur-compiler) či [Babel](https://babeljs.io). Vzájemným porovnáváním by se dalo popsat několik dalších stránek, proto ho nechávám na každém z vás. Dále se budu věnovat pouze poslednímu jmenovanému. 29 | 30 | ##Babel 31 | Babel nedávno vznikl přejmenováním z **6to5**, rychle se dostal na špičku popularity a získává stále větší momentum. **Proč je úspěšný?** Hlavním důvodem je jednoduše [nejširší podpora ES6 i ES7 (ES Next)](https://kangax.github.io/compat-table/es6/). Plně integruje **JSX a React** (taková ta podivnost, kdy píšete HTML tagy přímo do těla JavaScriptových funkcí). Skvěle si rozumí s editory a různými nástroji. 32 | 33 |

34 | 35 |

36 | 37 | Používají ho už dnes firmy jako **Facebook, Yahoo, Netflix, Mozilla a Evernote**. Můžete ho nasadit na serverovém prostředí **node.js**. Je to ale stále onen čistý JavaScript, takže s ním můžete bez problémů zkompilovat i svoje stávající projekty a s ES6 začít postupně. 38 | 39 | ##Co tedy ES6 přináší? 40 | Je toho spousta. Zmíním jenom pár těch nejzásadnějších věcí. Bylo by asi zbytečné kompletně překládat [tuto povedenou dokumentaci](https://babeljs.io/docs/learn-es2015/) (ukázky kódu jsou zkopírované z této dokumentace). 41 | 42 | ###Třídy 43 | Rozlučte se s `prototype`. Máme tu novou hezčí syntax, která podporuje prototypovou **dědičnost**, volání rodičovské metody, statické metody či **konstruktory**. U zkušenějších JS vývojářů se však často setkáte s názorem, že tato nová syntax je velkou chybou, protože bude svádět nováčky ke klasickému OOP, což je v JS považováno z mnoha důvodů za špatné. 44 | 45 | ```js 46 | class SkinnedMesh extends THREE.Mesh { 47 | constructor(geometry, materials) { 48 | super(geometry, materials); 49 | 50 | this.idMatrix = SkinnedMesh.defaultMatrix(); 51 | this.bones = []; 52 | this.boneMatrices = []; 53 | //... 54 | } 55 | update(camera) { 56 | //... 57 | super.update(); 58 | } 59 | static defaultMatrix() { 60 | return new THREE.Matrix4(); 61 | } 62 | } 63 | ``` 64 | 65 | ###Let a const 66 | Rozlučte se s `var`. Máme tu `let`. Oproti `var` omezuje působnost proměnné na nejbližší blok. Dále také přibylo klíčové slovo `const` pro konstanty. 67 | 68 | ```js 69 | function f() { 70 | { 71 | let x; 72 | { 73 | // platnost pouze v tomto bloku 74 | const x = "sneaky"; 75 | // chyba, nemůžeme měnit konstantu 76 | x = "foo"; 77 | } 78 | // chyba, nemůžeme opět deklarovat x ve stejném bloku 79 | let x = "inner"; 80 | } 81 | } 82 | ``` 83 | 84 | ###Moduly 85 | Nová syntax sjednocující definici a nahrávání modulů. Nepostradatelná zbraň pro **rozdělení kódu do mnoha modulů a souborů**. Můžete si následně vybrat formát, do kterého má Babel ES6 moduly přeložit. V nabídce je **Common.js**, **AMD**, System a UMD. Dokonce si můžete vymyslet i formát vlastní. Nástroje jako **browserify** či **webpack** pak tyto závislosti za vás vyřeší a sestaví jeden výstupní soubor. 86 | 87 | Ze souboru můžeme **exportovat funkce**, **objekty** i **proměnné** pomocí klíčového slova `export`. 88 | 89 | ```js 90 | // lib/math.js 91 | export function sum(x, y) { 92 | return x + y; 93 | } 94 | export var pi = 3.141593; 95 | ``` 96 | 97 | V jiném souboru si je pak můžeme **importovat** pomocí klíčových slov `import` a `from`. `*` naimportuje vše. 98 | 99 | ```js 100 | // app.js 101 | import * as math from "lib/math"; 102 | alert("2π = " + math.sum(math.pi, math.pi)); 103 | ``` 104 | 105 | Můžeme si ale i vybrat, **co přesně** chceme naimportovat pomocí `{}`. 106 | 107 | ```js 108 | // otherApp.js 109 | import {sum, pi} from "lib/math"; 110 | alert("2π = " + sum(pi, pi)); 111 | ``` 112 | 113 | Specialitou je pak klíčové slovo `default`. 114 | 115 | ```js 116 | // lib/mathplusplus.js 117 | export * from "lib/math"; 118 | export var e = 2.71828182846; 119 | export default function(x) { 120 | return Math.exp(x); 121 | } 122 | ``` 123 | 124 | V cílovém souboru si pak můžeme **default import** pojmenovat zcela dle své vůle (zde `exp`). Současně můžeme i nadále importovat ostatní funkce, objekty a proměnné. 125 | 126 | ```js 127 | // app.js 128 | import exp, {pi, e} from "lib/mathplusplus"; 129 | alert("2π = " + exp(pi, e)); 130 | ``` 131 | Pokud máte nějaké otázky ohledně ES6 modulů (a věřím, že jich určitě bude spousta), koukněte se na pěkný přehled [sem](http://www.2ality.com/2014/09/es6-modules-final.html). 132 | 133 | ### Ostatní 134 | Velmi užitečné jsou také **defaultní hodnoty** pro argumenty funkcí, **šablony** pro řetězce (aneb už není potřeba zběsile používat `+` pro spojování řetězců a proměnných) či **destructuring**. Pro více detailů navštivte [dokumentaci](https://babeljs.io/docs/learn-es6). 135 | 136 | ##Závěrem 137 | JavaScript už dávno není pouze doplňkovou hračkou v prohlížečích, ale začíná nám ovládat i ostatní platformy. Velké firmy do něj investují nemálo peněz a jeho vývoj tak probíhá překotným tempem. Díky tomu pak vznikají špičkové nástroje jako je Babel. To co na první pohled vypadá jako divoká džungle, tak nakonec příspívá k pružnějšímu vývoji, nutí výrobce prohlížečů rychleji implementovat nové funkce a programátor tím přesto nemusí trpět. Co si tedy z článku odnést? Jednoduché ponaučení! Vždy si přidejte Babel do svého vývojářského procesu a užívejte si budoucnost už dnes. **JavaScript je mrtev, ať žije Babel!** 138 | -------------------------------------------------------------------------------- /content/Babel/pics/babel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tajo/javascript/5a85b5364ded3c47338144124ff5a24e1bf6e530/content/Babel/pics/babel.jpg -------------------------------------------------------------------------------- /content/Nastroje/index.md: -------------------------------------------------------------------------------- 1 | #Nástroje 2 | 3 | Tak jako truhlář potřebuje hoblík, pilu nebo nebozez, tak i **vývojář webovek se neobejde bez pořádných nástrojů**. Když si vyberete ty správné a naučíte se s nimi pořádně zacházet, značně usnadní a zrychlí vaši práci. V tomto článku se s nimi v rychlosti seznámíme. 4 | 5 | ##Operační systém 6 | 7 | Vezmeme to pěkně od základu a tím je operační systém. Nemá smysl chodit kolem horké kaše. Sami asi tušíte, že tím **slonem v místnosti je Windows**. Budete s ním pravděpodobně narážet na různé problémy (pramenící z node.js, npm, slabší podpory Facebooku v jeho opensource knihovnách...), které byste na OSX nebo Linuxu nikdy řešit nemuseli. Předem se tak omlouvám, pokud vám občas něco nebude fungovat. **Články totiž primárně počítají s OSX (či Linuxem)**. Budu ovšem moc rád, pokud případné opravy či návody pošlete pull requestem. 8 | 9 | ##Prohlížeč 10 | 11 | **Standardem pro vývoj je Google Chrome**, který má dnes asi nejpropracovanější vývojářské nástroje. Otevřete je zkratkou `⌘`+`alt`+`j`. **Vůbec nejdůležitějším panelem je `Console`**. Mějte ho pořád na očích. JavaScript vám do něj bude vypisovat všechny warningy a errory. Také si do něj můžete logovat hodnoty vlastní pomocí funkce `console.log(...)`, která přehledně vypíše i komplikované datové struktury, což je fajn pro rychlé ladění. 12 | 13 | Druhým nejužitečnějším panelem je `Elements`. **Můžete v něm procházet a hlavně upravovat aktuální DOM strukturu a kaskádové styly.** Změny se pak okamžitě projeví v okně prohlížeče. Hodí se třeba i pro ujištění, zda vám JavaScript vyrendroval nějakou komponentu a není třeba jen schovaná kvůli špatnému stylování. 14 | 15 |

16 | 17 |

18 | 19 | Další panely už budete používat asi méně. Velmi šikovný je `Network`, ve kterém si můžete přehledně například vyfiltrovat XHR requesty. V `Sources` pak můžete debugovat pomocí breakpointů. Doinstalovat si můžete i speciální [panel pro React](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en). Je podobný `Elements`, ale poskytuje i informace o stavu React komponent. Nicméně reálně ho moc nepoužívám. 20 | 21 | V nastavení vývojářských nástroju si ještě zkontrolujte, že máte zaškrtnuté `Enable CSS source maps` a `Enable JavaScript source maps`. V našich projektech nebudeme používat přímo JavaScript, ale Babel, který se do něho překládá. **Source mapa pak umožní prohlížeči namapovat řádky výsledného JavaScriptu na zdrojový Babel** a my se tak dozvíme správný řádek a soubor, kde došlo k chybě. Obdobně to funguje pro CSS a preprocesory jako je LESS. 22 | 23 | Web nakonec musíte samozřejmě pravidelně testovat i v ostatních prohlížečích. I ty mají vlastní vývojařské nástroje. Pokud se vám tedy stane, že vám něco nefunguje pouze ve Firefoxu, nebojte se je vyzkoušet. Jsou velmi podobné těm v Chromu. 24 | 25 | ##Sublime Text 26 | 27 | Zatímco mezi webovými prohlížeči moc na výběr nemáte, **o dobré textové editory není nouze**. Nemá smysl zde dělat nějaké dogmatické závěry. Někomu bude vyhovovat spartánský VIM a někdo má zase rád velké všeobjímající IDE. I tak se mezi JavaScriptovými vývojáři dá vypozorovat několik oblíbenců. **Jedním z nich je editor [Sublime Text](http://www.sublimetext.com/).** Momentálně ho používám jako svůj hlavní editor pro všechno. 28 | 29 | Jeho největší předností je rychlost, jednoduchost, spolehlivost a obrovská paleta všemožných doplňků. Nevýhodou pak je to, že se jedná o one-man show projekt, který autor trochu zanedbává, nicméně nedávno se mu zas začal věnovat. Jak už to bývá, dobré věci nejsou zadarmo a tak vás licence vyjde na rozumných $70. Lze ho však nějaký čas používat a zkoušet zdarma. Nejvíce ho totiž oceníte až po nějaké době, kdy si zapamatujete různé zkratky a využijete tak jeho maximální potenciál. 30 | 31 | Pokud se ho rozhodnete používat pro vývoj webovek, doporučuji si pro začátek [stáhnout a nainstalovat](https://packagecontrol.io/installation) tyto doplňky: 32 | 33 | * [All Autocomplete](https://github.com/alienhard/SublimeAllAutocomplete) 34 | * [AutoFileName](https://github.com/BoundInCode/AutoFileName) 35 | * [Babel](https://github.com/babel/babel-sublime) 36 | * [EditorConfig](https://github.com/sindresorhus/editorconfig-sublime) 37 | * [Focus File On Sidebar](https://github.com/miguelgraz/FocusFileOnSidebar) 38 | * [Git](https://github.com/kemayo/sublime-text-git) 39 | * [Sidebar Enhancements](https://github.com/titoBouzout/SideBarEnhancements) 40 | * [Sublime Linter](https://github.com/SublimeLinter/SublimeLinter3) 41 | 42 | V některém z dalším článku popíšu, jak a proč si nastavit ESLint neboli kontrolu kódu pro JavaScript (Babel) a JSX přímo v Sublime Textu. 43 | 44 |

45 | 46 |

47 | 48 | **Dalším editorem, který stojí za pozornost je [Atom](https://atom.io/)**. Je to open source projekt, za kterým stojí GitHub a velmi připomíná Sublime Text. Má ale jeden problém a to je stabilita. Není zatím tak "uhlazený" jako ST a to je u nástroje, který používáte prakticky pořád, dost nepříjemné. Čas od času ho i přesto zkouším a jsou tam vidět velké pokroky. Také nad ním vznikají nové nástavby jako je [Visual Studio Code](https://code.visualstudio.com/) od Microsoftu nebo [Nuclide](http://nuclide.io/) od Facebooku. Budoucnost Atomu vypadá zajímavě. 49 | 50 | Hodně přívrženců pak má i [WebStorm](https://www.jetbrains.com/webstorm/), [PHPStorm](https://www.jetbrains.com/phpstorm/) a celá rodina editorů postavená nad IntelliJ. **Pokud máte rádi velké plnohodnotné IDE, které už v základu lecos umí, tak rozhodně stojí za zkoušku**. Na psaní kódu se dá ale používat prakticky cokoliv, takže se nebojte experimentovat. 51 | 52 | ##Terminál 53 | 54 | Svatou trojici pak uzavírá terminál. **Prohlížeč, textový editor a terminál jsou tři věci, které musíte mít jako vývojář stále na očích**. Kromě základní práce se soubory a Gitem z něho budete spouštět vývojový server, buildovat produkční verze a sledovat případné warningy a errory v kódu. Obzvlášť s používáním hot reloadu se většina hlášení bude objevovat právě zde a nikoliv v Console logu prohlížeče. 55 | 56 |

57 | 58 |

59 | 60 | ##Node.js a Npm 61 | 62 | O [node.js](https://nodejs.org/) jste už určitě všichni slyšeli. Jistého chytrého člověka napadlo vzít JavaScript runtime z Chromu a postavit nad ním velmi výkonnou platformu, která by se dala používat i mimo prohlížeč a to zejména na serverech. Tento nápad se setkal s obrovským ohlasem a pomohl odstartovat to pravé JavaScriptové šílenství. 63 | 64 | Asi není překvapivé, že **node se tak stává přirozenou volbou (nejen) pro aplikace, které závisí na velkém JS frontendu**. Je totiž velmi výhodné používat stejný jazyk na obou březích. Můžete tak snadno sdílet části kódu (například validační pravidla či routing), či si předrendrovat stránku už na serveru a uživateli poslat rovnou hotové HTML. Navíc i pokud se rozhodnete stavět server nad jinou platformou, tak se nodu nevyhnete kvůli vývojářským nástrojům, které jsou téměř výhradně napsané také v JavaScriptu. **Takže si ho prosím rovnou nainstalujte**. 65 | 66 |

67 | 68 |

69 | 70 | **S nodem se vám nainstaluje i [npm](https://www.npmjs.com/) - balíčkovací manager**. Jde o ultimátní a jednotné řešení pro sdílení kódů napříč JS ekosystémem. Původně byl určen pro prostředí nodu, ale nakonec se z něj stal i jednotný systém pro frontend vývojáře (R.I.P [bower](http://bower.io/)). Používá ho tak node, browserify, webpack, gulp, grunt, Angular, jQuery, React, prohlížeče ... prostě všichni. 71 | 72 | **Největší předností a odlišností NPM (například od Boweru) je to, že každý balíček může mít své vlastní závislosti (balíčky)**. Vzniká tak strom závislostí a není problém, aby v jednom projektu běžela jedna knihovna v několika verzích. Tedy, občas to problém být může. Například React bude dost nešťastný, pokud bude muset o správu DOMu bojovat s další instancí Reactu. Aneb vůči dependency hellu není a nebude zcela imunní žádný balíčkovací manager, ale NPM si v porovnání s konkurencí vede víc než obstojně. **Dalším fundamentálním rysem je filosofie malých a jednoúčelových balíčků**. Není tedy nic zvláštního, pokud je balíček pouze jedna několikařádková funkce. 73 | 74 | ##Grunt 75 | 76 | **Se vší tou záplavou růných nástrojů, kompilátorů a linterů je potřeba se nějak vypořádat**. Představte si, že byste po každé změně kódu museli vlézt do terminálu, najít změněný soubor, spustit nad ním Babel, poté ESLint a pak v prohlížeči ještě udělat refresh. To je přeci repetativní úkon, který se může zautomatizovat. **Objevily se tak nástroje označované jako "task runnery"**. Velmi populárním byl (a stále je?) [Grunt](http://gruntjs.com/). Do projektu si přidáte `Gruntfile`, ve kterém si vše můžete nakonfigurovat. Konfigurace se skládá z jednotlivých tasků, které můžete pak volat v různém pořadí. Jednotlivé tasky (moduly) nemusíte psát sami, ale většinou je už najdete hotové na NPM. Jsou jich tisíce. Výsledek může vypadat třeba takto: 77 | 78 | ```js 79 | module.exports = function(grunt) { 80 | 81 | grunt.initConfig({ 82 | jshint: { 83 | files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'], 84 | options: { 85 | globals: { 86 | jQuery: true 87 | } 88 | } 89 | }, 90 | watch: { 91 | files: ['<%= jshint.files %>'], 92 | tasks: ['jshint'] 93 | } 94 | }); 95 | 96 | grunt.loadNpmTasks('grunt-contrib-jshint'); 97 | grunt.loadNpmTasks('grunt-contrib-watch'); 98 | 99 | grunt.registerTask('default', ['jshint']); 100 | 101 | }; 102 | ``` 103 | 104 | **Postupně se však ukázalo, že cesta "vše konfigurovat" není úplně nejšťastnější**. Bez dokumentace totiž nedáte ani ránu a pokud si nevystačíte s předem danými funkcemi, tak máte problém. **Dalším problémem Gruntu je rychlost**. Mezivýsledky si totiž ukládá do filesystému a u větších projektů s komplikovaným build procesem trvá dlouho (tohle už možná nejnovější verze nějak řeší). 105 | 106 | ##Gulp 107 | 108 | **A tak přibyl mladší bráška [Gulp](http://gulpjs.com/), který se ponaučil z Gruntu a jde na to lépe**. Konfigurace aka `Gulpfile` je JavaScript. **Místo toho, abyste konfigurovali, tak programujete**, což je velmi flexibilní a také odpadá nutnost spoléhat na dokumentace. Dalším zlepšovákem jsou streamy, které Gulp používá. Mezivýsledky si drží v paměti, vše běží paralelně a především citelně rychleji. Občas je však problém zkrotit tasky tak, aby běžely sekvenčně (verze 4.x tento častý problém adresuje). Tak jako Grunt i Gulp má dnes již téměř nekonečné množství pluginů. **Grunt vs Gulp? Rozhodně Gulp**. Pro názornost, "konfigurace" Gulpu může vypada třeba takto: 109 | 110 | ```js 111 | var gulp = require('gulp'); 112 | var coffee = require('gulp-coffee'); 113 | var concat = require('gulp-concat'); 114 | var uglify = require('gulp-uglify'); 115 | var imagemin = require('gulp-imagemin'); 116 | var sourcemaps = require('gulp-sourcemaps'); 117 | var del = require('del'); 118 | 119 | var paths = { 120 | scripts: ['client/js/**/*.coffee', '!client/external/**/*.coffee'], 121 | images: 'client/img/**/*' 122 | }; 123 | 124 | // Not all tasks need to use streams 125 | // A gulpfile is just another node program and you can use all packages available on npm 126 | gulp.task('clean', function(cb) { 127 | // You can use multiple globbing patterns as you would with `gulp.src` 128 | del(['build'], cb); 129 | }); 130 | 131 | gulp.task('scripts', ['clean'], function() { 132 | // Minify and copy all JavaScript (except vendor scripts) 133 | // with sourcemaps all the way down 134 | return gulp.src(paths.scripts) 135 | .pipe(sourcemaps.init()) 136 | .pipe(coffee()) 137 | .pipe(uglify()) 138 | .pipe(concat('all.min.js')) 139 | .pipe(sourcemaps.write()) 140 | .pipe(gulp.dest('build/js')); 141 | }); 142 | 143 | // Copy all static images 144 | gulp.task('images', ['clean'], function() { 145 | return gulp.src(paths.images) 146 | // Pass in options to the task 147 | .pipe(imagemin({optimizationLevel: 5})) 148 | .pipe(gulp.dest('build/img')); 149 | }); 150 | 151 | // Rerun the task when a file changes 152 | gulp.task('watch', function() { 153 | gulp.watch(paths.scripts, ['scripts']); 154 | gulp.watch(paths.images, ['images']); 155 | }); 156 | 157 | // The default task (called when you run `gulp` from cli) 158 | gulp.task('default', ['watch', 'scripts', 'images']); 159 | ``` 160 | 161 | Podrobnější porovnání Gulpu a Gruntu můžete najít i v [pěkném článků na Zdrojáku](http://www.zdrojak.cz/clanky/gulp-vs-grunt-souboj-bez-viteze-a-porazeneho/). 162 | 163 | ##Browserify 164 | 165 | Node.js zavedlo do světa JS snadný způsob jak kód modularizovat. Takový modulární systém potřebuje dvě primární věci: možnost exportovat a importovat. Výše jste si mohli již povšimnout importování: 166 | 167 | ```js 168 | var gulp = require('gulp'); 169 | ``` 170 | 171 | Obdobný zápis v ES6: 172 | 173 | ```js 174 | import gulp from 'gulp'; 175 | ``` 176 | 177 | 178 | Výše uvedené zkusí prohledat `/node_modules` ve vašem projektu - místo, kam se vám budou ukládat balíčky z npm. Stačí zavolat `npm install gulp --save` a máte ho tam. Přepínač `--save` ho navíc přidá do seznamu v `package.json`. Běžně se totiž `/node_modules` adresář v Gitu ignoruje a tak je někde potřeba uchovávat seznam všech závislostí. 179 | 180 | V `/node_modules/gulp` se pak import podívá do hlavního souboru daného balíčku a v něm najde export: 181 | 182 | ```js 183 | var inst = new Gulp(); 184 | module.exports = inst; 185 | ``` 186 | 187 | **To je vše pěkné, ale asi vás teď napadlo, jak to bude fungovat v prohlížeči? Nebude.** Do prohlížeče můžeme jednotlivé skripty nahrávat pomocí ``, ale to nám moc nepomůže. Podobných souborů totiž budeme mít v projektu stovky až tisíce a posílat tolik HTTP requestů rozhodně nemůžeme. **Také to nijak neřeší potřebu verzování, minimalizování a zapouzdřování**. Tyto závislosti je tedy potřeba vyřešit už před releasem a uživateli posílat jeden výsledný soubor, ve kterém bude jen to, co skutečně potřebuje. 188 | 189 |

190 | 191 |

192 | 193 | **A tak vzniklo [Browserify](http://browserify.org/)**. Vezme váš kód a vyřeší všechny `require` a `module.exports` a vyplivne jeden výsledný soubor. **JavaScript, který byl původně určen pro node.js, dokáže udělat kompatibilní pro prohlížeče**. Poradí si například i s core node.js knihovnami. Nicméně to má samozřejmě jistá omezení. Například nemůžete pracovat s `fs` (filesystem). Velkým plusem je, že není potřeba nic konfigurovat - jednoduše to bude fungovat. Všechna magie se děje uvnitř. Ne nadarmo má ve znaku kouzelnický klobouk. 194 | 195 | ##Webpack 196 | 197 | **Browserify funguje opravdu pěkně, ale i tak má přeci jen pár mušek**. Řeší totiž jenom JavaScript a vše ostatní je potřeba doplnit spoustou gulp modulů. Navíc se ukázalo, že **kromě JavaScriptu je potřeba podobně zpracovat i CSS** (v praxi například LESS) nebo obrázky. To by nemuselo s Gulpem a jeho tisíci pluginy vadit, že? Bohužel Gulp má jednu zásadní slabinu, která plyne ze streamování. Neumí úplně přímočaře vytvořit z několika souborů jeden výstupní (s tím se umí lépe vypořádat např. undergroundovější task runner [Broccoli](http://broccolijs.com/)). Je tak potřeba v gulp tascích řešit trochu nešikovně bundlování. A z toho zase mohou vznikat další obtíže, jako je správné odchytávání a zobrazování chyb. Dá se s tím poprat, ale budete cítit, že to není úplně ono. 198 | 199 | **[Webpack](http://webpack.github.io/) na to jde úplně jinak, tak trochu proti filosofii celého JS ekosystému**. Nedeleguje a nevyčleňuje jednotlivé funkce do více modulů a tak už v základu představuje velmi mocný nástroj. Nástroj, který byl od počátku zamýšlen tak, aby nám vývojářům co nejvíce usnadnil práci. **Základním stavebním kamenem jsou loadery**. Každý formát má vlastní a může jít téměř o cokoliv (.js, .css, .less, .coffee, .png...). Webpack pak přetíží `require()` volání a můžete ho použít třeba i pro markdown dokumenty (jako tato stránka). Následně pak funguje podobně jako browserify, tedy vyřeší závislosti a sestaví build, ve kterém je jen to potřebné. 200 | 201 |

202 | 203 |

204 | 205 | **Oproti browserify je potřeba ho nakonfigurovat, což není úplně jednoduché**. Na druhou stranu, jakmile to jednou uděláte, získáte velmi mocného společníka. Konfiguraci navíc můžete z velké části obšlehnout. **Poskytuje také jednu killer funkci**, kterou browserify nemá a pro kterou se vyplatí na něj z browserify přejít a tím je **hot module replacement**. Dokáže nahradit a zpracovat modul, aniž by bylo potřeba refreshovat celou stránku (a modul může být javascript, less nebo markdown!). Bez jakýchkoliv dodatečných pluginů v prohlížeči. Pouze čistý JavaScript! To ohromně zefektivňuje vývoj. 206 | 207 | Dobré je také zmínit, že **kdykoliv můžete velmi snadno přejít z browserify na webpack** (bez úprav zdrojáků). Udělal jsem to nedávno u projektu s 10 000 řádky kódu a 70 npm moduly bez jediné upravy mimo konfiguraci webpacku a gulpu. Naopak už to vždy jít nemusí (pokud například budete specifikovat loadery inline přímo v require voláních). 208 | 209 | ##ESLint 210 | 211 | **Lintování je levný způsob, jak zlepšit stav zdrojáků napříč větším projektem či týmem několika programátorů**. Hlídá totiž styl, jakým kód píšete a tak musí všichni stejně odsazovovat nebo používat závorky. **Pravidla, podle kterých se řídí, si samozřejmě můžete libovolně nastavit**. Nebude vám podstrkovat nějaký svůj styl. Krom toho, že hlídá úhlednost a jednotnost kódu, tak vám leckdy pomůže odhalit i různé překlepy či nepoužíváné importy. [ESLint](http://eslint.org/) je poměrně nový JS linter (2013), který si vzal ponaučení z JSHintu a je kompletně modulární. Můžete si do něj tedy přidávat různé doplňky. **Například se vám bude jistě hodit podpora React JSX či Babelu**. Lintování si můžete nainstalovat přímo do svého editoru a zároveň ho přidat i do build procesu, což je taková případná obrana před vývojářem, který by se rozhodl pravidla porušit. 212 | 213 | ##Závěrem 214 | 215 | Tento článek byl především seznamovací a většina z vás v něm asi nenalezla mnoho nového. I tak je ale důležité udělat alespoň rychlý přehled o různých nástrojích a technologiích, které se dnes používají. **V JS světě se budete neustále potýkat s tím, že žádná řešení nejsou ultimátní** a i ta, která jsou zrovna na špici, mohou za půl roku upadnout zcela v zapomnění. A tak tu máme několik kvalitních prohlížečů, editorů, task runnerů, bundlerů, linterů a dokonce i "JavaScript jazyků". 216 | 217 | Ale nebojte, příště už to bude rozhodně zajímavější! Vyhrneme si rukávy a pustíme se konečně do práce. **Ukážeme si, jak to vše poskládat dohromady a napíšeme si první React komponentu!** 218 | -------------------------------------------------------------------------------- /content/Nastroje/pics/browserify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tajo/javascript/5a85b5364ded3c47338144124ff5a24e1bf6e530/content/Nastroje/pics/browserify.png -------------------------------------------------------------------------------- /content/Nastroje/pics/chrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tajo/javascript/5a85b5364ded3c47338144124ff5a24e1bf6e530/content/Nastroje/pics/chrome.png -------------------------------------------------------------------------------- /content/Nastroje/pics/npm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tajo/javascript/5a85b5364ded3c47338144124ff5a24e1bf6e530/content/Nastroje/pics/npm.png -------------------------------------------------------------------------------- /content/Nastroje/pics/sublime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tajo/javascript/5a85b5364ded3c47338144124ff5a24e1bf6e530/content/Nastroje/pics/sublime.png -------------------------------------------------------------------------------- /content/Nastroje/pics/terminal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tajo/javascript/5a85b5364ded3c47338144124ff5a24e1bf6e530/content/Nastroje/pics/terminal.png -------------------------------------------------------------------------------- /content/Nastroje/pics/webpack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tajo/javascript/5a85b5364ded3c47338144124ff5a24e1bf6e530/content/Nastroje/pics/webpack.png -------------------------------------------------------------------------------- /content/README.md: -------------------------------------------------------------------------------- 1 | Tato markdown dokumentace není určena a uzpůsobena pro běžné čtení. 2 | 3 | Přejděte prosím na samostatnou stránku, která se z této dokumentace generuje: 4 | 5 | [http://www.dzejes.cz](http://www.dzejes.cz) 6 | -------------------------------------------------------------------------------- /content/React-jsx/index.md: -------------------------------------------------------------------------------- 1 | #React - JSX 2 | 3 | První věc, která nováčka v Reactu rozhodně praští do očí, je zápis XML (HTML) tagů přímo do JavaScriptového kódu bez apostrofů kolem. Takovýto zápis se ve světě Reactu nazývá JSX a ano, není to validní JS syntax. Před spuštěním se musí do JS kompilovat. **Dnes si ukážeme, co je pod pokličkou JSX a že se vlastně není čeho bát**. 4 | 5 | ##Šablonovací systémy 6 | 7 | Pojem "šablonovací systém" vám asi není neznámý. V oblasti webů má za úkol oddělit kód (např. PHP) od popisu struktury stránky. Známé příklady jsou Latte, Smarty nebo Twig. Ve světě JavaScriptu pak [Closure Templates (Soy)](https://developers.google.com/closure/templates/). Šablony obsahují jeden velký řetězec plný HTML, CSS a JS + nějaké speciální zápisy (proměnné, makra...). Šablonovací systém pak pomocí regulárních výrazů naplní šablonu daty a poslepuje z ní výsledný řetězec, který může poslat do prohlížeče. Skutečný proces může být i o něco složitější, ale princip zůstává stejný - **jde o slepování řetězců**. 8 | 9 | ##DOM vs řetězce 10 | 11 | Slepování řetězců funguje dobře na straně serveru, protože s reloadem celé stránky odpadá starost o stav stránky (například co bylo vyplněné ve formuláři) a vše se načte a spustí znova. **Jenže co dělat, když chceme aktualizovat jenom malé výseky stránky pomocí DOMu?** Můžeme nad upravovaným DOM elementem zavolat: 12 | 13 | ```js 14 | Element.innerHTML = '
Komponenta, kterou jsme aktualizovali
'; 15 | ``` 16 | 17 | Problém vyřešen. Naše šablony (řetězce) můžeme takto vložit na libovolné místo DOMu a navíc je to ještě velmi rychlé. Má to ale zásadní háček. 18 | 19 | **Pokud by naše komponenta měla na sobě pověšené listenery (například onClick), tak ty po innerHTML zmizí a musíme je opětovně navěsit.** Co když bude v naší komponentě formulář a uživatel v něm bude mít zrovna něco rozepsané? Všechno mu během aktualizace zmizí. Aktualizování DOMu pomocí innerHTML je sice velmi rychlé, ale také velmi náchylné k chybám. Navíc si to přímo říká o XSS . 20 | 21 | A tak vývojáři raději mění DOM postupně pomocí bezpečnějších metod typu `Node.appendChild()` či `Node.replaceChild()`. Problém vyřešen? Ne tak docela. Pěkné řetězcové šablony, kde na první pohled vidíme strukturu komponent, už nám najednou nestačí a naše zdrojáky musíme doplnit o různě dlouhé série `Node` metod, což je typické imperativní nepřehledné programování. **Místo toho, abychom definovali jednotlivé stavy (podoby) našich komponent, musíme dávat dohromady sady kroků, kterými se z jednoho stavu dostaneme do druhého.** Kdyby tak byla nějaká knihovna, která by tohle za nás vyřešila. Třeba React, že. 22 | 23 | ##Funkce místo řetězců 24 | 25 | Definice komponent pomocí řetězců má tedy své limity. React však používá silnější nástroj - virtuální DOM (strom), který si vytvoří na základě **`render()` metod, které popisují strukturu komponent pomocí funkcí**. Tento virtuální DOM se pak pomocí `appendChild` či `innerHTML` promítne do DOMu skutečného a dojde tak k překreslení v prohlížeči. Pokud se u některé komponenty změní props nebo state, opětovně se zavolá metoda `render()`, vznikne nový virtuální DOM, porovná se s tím s předchozím a rozdíly se nejefektivnějším možným způsobem promítnou i do DOMu skutečného. 26 | 27 | **Jak se ale HTML dá převést na funkce?** Názvy jednotlivých elementů nahradíme za názvy funkcí a z atributů (props) uděláme jeden velký argument - mapu. Vnořené elementy (děti) pak budou představovat další argumenty (mimochodem v React komponentě jsou přístupné pomocí `this.props.children`). 28 | 29 | ```js 30 |
31 | 32 | 33 | 34 |
35 | ``` 36 | 37 | se zapíše jako: 38 | 39 | ```js 40 | form({className: 'nameForm'}, 41 | input({type: 'text', placeholder: 'Jméno'}), 42 | input({type: 'text', placeholder: 'Příjmení'}), 43 | input({type: 'submit', value: 'Poslat'}) 44 | ) 45 | ``` 46 | 47 | **A to je celá věda kolem JSX. Je to jen triviální "syntax sugar", který dělá kód čitelnější** a přístupnější pro lidi odkojené na HTML. Není tedy potřeba se učit nový jazyk, nová makra či nové zápisy. Stačí si vždy vzpomenout na to, že jde jen o prosté funkce. Reálný zápis je trošičku složitější: 48 | 49 | ```js 50 | import React from 'react'; 51 | 52 | export default class FormNoJSX extends React.Component { 53 | 54 | render() { 55 | return ( 56 | React.DOM.form({className: 'nameForm'}, 57 | React.DOM.input({type: 'text', placeholder: 'Jméno'}), 58 | React.DOM.input({type: 'text', placeholder: 'Příjmení'}), 59 | React.DOM.input({type: 'submit', value: 'Poslat'}) 60 | ) 61 | ); 62 | } 63 | 64 | } 65 | ``` 66 | 67 | Teď už i víte, proč je vždy potřeba na začátku importovat React. Pro pořádek ještě důkaz, že to funguje: 68 | 69 | $$$formNoJSX2$$$ 70 | 71 | **Nelíbí se vám opakované `React.DOM`?** Můžeme si ho "vytknout" pomocí [ES6 destruktoru](https://babeljs.io/docs/learn-es2015/#destructuring): 72 | 73 | ```js 74 | render() { 75 | 76 | const {form, input} = React.DOM; 77 | 78 | return ( 79 | form({className: 'nameForm'}, 80 | input({type: 'text', placeholder: 'Jméno'}), 81 | input({type: 'text', placeholder: 'Příjmení'}), 82 | input({type: 'submit', value: 'Poslat'}) 83 | ) 84 | ); 85 | } 86 | ``` 87 | 88 | A v takovém [CoffeeScriptu](http://coffeescript.org/), který je více expresivní než JS, by to vypadalo jako: 89 | 90 | 91 | ```js 92 | {form, input} = React.DOM; 93 | 94 | render: -> 95 | form {className: 'nameForm'}, 96 | input {type: 'text', placeholder: 'Jméno'} 97 | input {type: 'text', placeholder: 'Příjmení'} 98 | input {type: 'submit', value: 'Poslat'} 99 | ``` 100 | 101 | Existuje dokonce i [fork Coffee](https://github.com/jsdf/coffee-react-transform), kde lze používat JSX. 102 | 103 | Jak vidíte, je tedy čistě na vás a vašich preferencích, kterou variantu si zvolíte. **Můžete spokojeně používat React i úplně bez JSX.** Nicméně, drtivá většina lidí JSX s Reactem používá a tak všechny příklady najdete v něm. JSX je pevnou součástí [Babelu](babel.html) a třeba i TypeScriptu. Dostává se mu značné podpory i co se týče nástrojů, editorů a linterů. Stává se z něho průmyslový standard. 104 | 105 | 106 | ##JSX syntax 107 | 108 | Teď když vidíte, co je výsledkem JSX transformace, tak by vám už mělo být i jasné, co všechno můžete a nemůžete zapsat do `{}`. Kód uzavřený v `{}` je jednoduše čistý JavaScript. 109 | 110 | ```js 111 |
112 | 113 |
114 | ``` 115 | 116 | se přeloží do 117 | 118 | ```js 119 | React.DOM.div({className: red}, React.DOM.button({}, label)); 120 | ``` 121 | 122 | Z toho je jasné, že například s 123 | 124 | ```js 125 |
{if (foo) {???}}
126 | ``` 127 | 128 | byste se nikam nedostali, ale 129 | 130 | ```js 131 |
{foo ? 'ahoj' : ''}
132 | ``` 133 | 134 | nebo 135 | 136 | ```ja 137 |
{foo && 'ahoj'}
138 | ``` 139 | 140 | fungovat bude. 141 | 142 | **A v JavaScriptu můžete zase znova použít JSX:** 143 | 144 | 145 | ```js 146 |
{foo && }
147 | ``` 148 | 149 | Velmi často v JSX uvidíte různé mapovací funkce, které vrací pole React elementů: 150 | 151 | ```js 152 |
{cars.map(car =>
{car.name}
}
153 | ``` 154 | 155 | A nyní už víte opravdu všechno. Pokud jste stále trochu zmatení, zkuste si napsat pár komponent. Uvidíte, že z JSX se stane "jazyk", který plně ovládnete během jednoho večera, protože si vždy velmi lehce můžete vydedukovat do čeho se kompiluje. 156 | 157 | ##Pár dalších věcí... 158 | 159 | Najde se ale i pár dalších detailů, které stojí za zmínku. Občas se může stát, že **chcete, aby nějaký váš vlastní atribut prošel až do výsledného DOM elementu:** 160 | 161 | ```js 162 |
163 | ``` 164 | 165 | Ovšem React neznámé atributy automaticky odstraní. Výjimku udělá, pokud použijete prefix `data-`: 166 | 167 | ```js 168 |
169 | ``` 170 | 171 | Občas také můžete mít potřebu vložit někam "natvrdo" statický HTML kód, to se udělá takto: 172 | 173 | ```js 174 |
Tohle se neescapuje!'}} /> 175 | ``` 176 | 177 | Z názvu je už zřejmé, že jde o velmi nebezpečný postup, protože escapování zůstává jen a pouze na vás. Tímto úplně vyřadíte React a obsah se do stránky vloží pomocí `innerHTML`. 178 | 179 | ##Chyby 180 | 181 | Jednou z velkých výhod definování pomocí funkcí je to, že **pokud uděláte chybu, dostane se vám vždy pěkné a přesné zprávy**, protože jste udělali chybu v JavaScriptovém kódu a nikoliv v nějakém řetězci, který se bude parsovat až buhví kdy. 182 | 183 | Například chybějící uzavírací závorka: 184 | 185 | ```js 186 |
188 |
189 | ``` 190 | 191 | okamžitě vypíše tuto pěknou chybu v terminálu: 192 | 193 |

194 | 195 |

196 | 197 | To je luxus, který se vám většinou při "slepování řetězců" nedostane. 198 | 199 | Některé atributy se jmenují jinak kvůli kolizím s klíčovými slovy. Například toto nebude fungovat: 200 | 201 | ```js 202 |
203 | ``` 204 | 205 | ale nebojte, dostane se vám ihned nápověda v konzoli: 206 | 207 | ``` 208 | Warning: Unknown DOM property class. Did you mean className? 209 | ``` 210 | 211 | ##Závěrem 212 | 213 | **JSX pouze zpříjemňuje čitelnost komponent.** Bude se líbit zejména kodérům. React však můžete plnohodnotně používat i zcela bez něj. **Důležité je však to, co se skrývá pod ním - funkce.** Funkce totiž poskytují skvělou a netušeně mocnou abstrakci pro skládání jednotlivých komponent. I po 2 letech od uvedení Reactu se tak stále objevují nové a lepší způsoby, jakými do sebe jednotlivé komponenty skládat, což si určitě ukážeme i v dalších dílech. To by nebylo možné, pokud by React a JSX nebyly pouze čistým JavaScriptem. Příště si ukážeme knihovnu [Immutable.js](https://facebook.github.io/immutable-js/) a způsob jak díky ní 100x zrychlit už tak rychlou React aplikaci. 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | -------------------------------------------------------------------------------- /content/React-jsx/pics/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tajo/javascript/5a85b5364ded3c47338144124ff5a24e1bf6e530/content/React-jsx/pics/error.png -------------------------------------------------------------------------------- /content/React-props-vs-state/index.md: -------------------------------------------------------------------------------- 1 | #React - Props vs State 2 | 3 | **React se točí kolem props a tak je nejvyšší čas si ukázat, co jsou zač**. Minule jsem už naznačil, že jde o druhý zdroj dat, který React komponenty pro své vykreslení používají. Povíme si, jak se props liší od state a jakým způsobem se používají. To vše si demonstrujeme na pokročilejším příkladě, který se bude skládat z více komponent. Na nich uvidíte i to, jakým způsobem se komponenty do sebe skládají a jak spolu komunikují. **Dnes to bude o úplných základech Reactu**. 4 | 5 | ##Props 6 | 7 | **React komponenty lze do sebe skládat** a proto je potřeba, aby měly prostředek pro předávání dat. Tím jsou právě ony props, které tak tvoří páteř všech React aplikací. 8 | 9 | V hlavní React komponentě můžeme mít metodu: 10 | 11 | ```js 12 | render() { 13 | return( 14 |
15 |

Zvířata

16 | 17 | 18 |
19 | ); 20 | } 21 | ``` 22 | 23 | **Animal je přitom jen další React komponenta**. Ta tímto získala přístup k `this.props.name` a `this.props.type`. Její `render` metoda může vypadat třeba takto: 24 | 25 | ```js 26 | render() { 27 | return( 28 |
29 |

Zvíře

30 | Jméno: {this.props.name} 31 | Typ: {this.props.type} 32 |
33 | ); 34 | } 35 | ``` 36 | 37 | **Předávat můžeme samozřejmě i funkce či objekty. Všechno je jen a pouze čistý JavaScript. Props fungují pouze jednosměrně směrem zhora dolů.** Zvíře tudíž nemá způsob, jak změnit props komponenty Zvířata. Může nicméně dostat od rodičovské komponenty skrz props callback a ten zavolat. Tento jednosměrný postup velmi redukuje komplexnost aplikací. 38 | 39 | Už víte, že `render()` se volá při změně props a state. **S jeho voláním se ale navíc přerendruje i celý podstrom potomků.** Pokud by někdo změnil state/props v komponentě Zvířata, tak se zavolají `render` metody i u komponenty Zvíře. 40 | 41 | ##Zadání úlohy 42 | 43 | **Dnešním cílem bude vytvoření jednoduché databáze aut**. Každé auto bude mít 3 údaje - značku, model a rok výroby. Tyto údaje budou seskupené do karty, která bude umožňovat (roz)skrýt model a rok. Jednotlivá auta budeme moct přidávat a mazat. 44 | 45 | ##Návrh komponent 46 | 47 | **Při navrhování React aplikací začínáme tím, že si musíme aplikaci rozkouskovat do jednotlivých komponent**. Při rozdělování do komponent se lze přidržet i vizuální podoby. Logickým kandidátem na samostatnou komponentu je karta auta. Další částí rozhraní je formulář pro přidání auta, šup s ním do druhé komponenty. Nakonec potřebujeme ještě někde formulář a auta vypsat a máme tak komponentu třetí - hlavní. 48 | 49 | ##Kam se stavem 50 | 51 | **Jaké stavy v aplikaci vlastně budeme mít? Jde především o detaily aut**. Vhodným kandidátem pro uložení je pole objektů, kde každý objekt bude reprezentovat jedno auto. Ale kam s ním? V naší jednoduché aplikaci moc na výběr nemáme. Pole aut musí být v hlavní komponentě, která jednotlivá auta bude vykreslovat. 52 | 53 | **Dalším stavem pak bude to, zda je karta auta rozbalená či zabalená.** Asi vás ihned napadne, že tento stav patří do komponenty auta. Také ho ale můžeme umístit jako další vlastnost objektu auto. Oba přístupy jsou v pohodě. 54 | 55 | Nicméně, obecným **trendem v React aplikacích je zahnat veškeré stavy někam "do rohu" na jedno místo**. Proč nechceme mít stavy roztroušené po aplikaci? Tak například si představte, že bychom chtěli přidat tlačítko "Rozbal všechna auta". Podobnou funkcionalitu bychom přirozeně umístili do hlavní komponenty, ale jak pak aktualizovat tento stav v jednotlivých komponentách aut? Není způsob jak komponenty aut proiterovat a zavolat `auto.rozbalit()`, protože to by byl imperativní přístup, který v Reactu (naštěstí) nefunguje. 56 | 57 | **Komponentu s vnitřním stavem nemůžeme 100% ovládat z vnějšího prostředí**. Pokud však stav vystrčíme někam ven a komponeta bude používat pro své vykreslování pouze zděděné props, dostaneme nad ní plnou kontrolu. 58 | 59 | **Lokální stav je tedy přijatelný pouze, pokud ho nikdy nebudeme potřebovat upravovat či číst vně komponenty**. Nicméně i takovýto superlokální stav můžeme snadno "globalizovat" a mít tak všechny React komponenty "čisté" a plně kontrolovatelné. To si však ukážeme až později s Redux architekturou. A nebojte, je globální stav a globální stav. 60 | 61 | ##Komponenta Auto 62 | 63 | **Nejlepším postupem je budovat aplikaci od spoda**, jelikož pak můžete rychle vše testovat, což je vždy lepší než půl hodiny psát kód naslepo. Napíšeme si tedy nejdříve komponentu auto. Představíme si, jak asi bude vypadat objekt auta, který dostaneme skrz props: 64 | 65 | ```js 66 | {id: 1, brand: 'Ferrari', model: 'FF', year: '2014', details: false} 67 | ``` 68 | 69 | `id` se nám bude hodit pro snadnou manipulaci v případě mazání a rozbalování. `details` nám pak určuje, zda je karta rozbalená či zabalená. 70 | 71 | Začneme tím, že si data vypíšeme: 72 | 73 | ```js 74 | import React from 'react'; 75 | 76 | export default class Car extends React.Component { 77 | 78 | render() { 79 | return ( 80 |
81 |
Značka: {this.props.car.brand}
82 | {this.props.car.details && 83 |
84 | Model: {this.props.car.model}
85 | Rok: {this.props.car.year} 86 |
87 | } 88 | ); 89 | } 90 | 91 | } 92 | ``` 93 | 94 | Všimnětě si, že v JSX nemůžete obalit kus kódu do nějakého makra `{if} ... {else}`, protože by to nebyla regulérní JS syntax. **Do `{}` lze zabalit jen validní JS výrazy, něco co se dá vypsat**. Nikoliv třeba řídící bloky. O JSX bude ještě samostatný článek. Můžeme si ale pomoct jednoduchou obezličkou viz výše. Pokud bychom přesto trvali na `if`, mohli bychom si výsledek předuložit do nějaké proměnné před `return()`. 95 | 96 | **Jak si to teď otestujeme?** Vytvoříme si další hlavní komponentu a v ní si `Car` vypíšeme: 97 | 98 | ```js 99 | import React from 'react'; 100 | import Car from './car'; 101 | 102 | export default class Cars extends React.Component { 103 | 104 | render() { 105 | const car = { 106 | id: 1, 107 | brand: 'Ferrari', 108 | model: 'FF', 109 | year: '2014', 110 | details: true 111 | }; 112 | 113 | return ( 114 |
115 | 116 |
117 | ); 118 | } 119 | 120 | } 121 | ``` 122 | 123 | Uložíme a výsledek by měl vypadat takto: 124 | 125 | $$$cars1$$$ 126 | 127 | S komponentou auta jsme už skoro hotoví. Zbývá si ji ještě trochu ostylovat a přidat do ní tlačíka pro mazání a rozbalování. **Dobrým zvykem je v komponentách deklarovat jaké typ props očekávají, že jim budou předány**. React pak v devel režimu provádí automatickou kontrolu a upozorní nás, pokud nesedí typy nebo na něco zapomeneme: 128 | 129 | ```js 130 | import React from 'react'; 131 | 132 | export default class Car extends React.Component { 133 | 134 | render() { 135 | const styles = { 136 | border: '2px solid #CCC', 137 | padding: 10, 138 | margin: '10px 10px 10px 0px', 139 | backgroundColor: '#FFF', 140 | float: 'left' 141 | }; 142 | 143 | return ( 144 |
145 |
146 | Značka: {this.props.car.brand} 147 |
148 | {this.props.car.details && 149 |
150 | Model: {this.props.car.model}
151 | Rok: {this.props.car.year} 152 |
153 | } 154 | 155 | 158 | {' '} 159 | 160 | 163 |
164 | ); 165 | } 166 | 167 | } 168 | 169 | Car.propTypes = { 170 | car: React.PropTypes.object.isRequired, 171 | remove: React.PropTypes.func.isRequired, 172 | toggle: React.PropTypes.func.isRequired 173 | }; 174 | ``` 175 | 176 | **React má i pěkný aparát na zápis inline stylů.** Místo pomlček se používá camelCase a není třeba potřeba psát 'px'. O stylování pomocí JavaScriptu bude možná ještě samostatný článek, protože je to také zajímavá problematika. 177 | 178 | Akce v této komponentě řešit nebudeme. Ať se postará rodič a předá nám skrz props callbacky, které máme zavolat. `{' '}` je taková srandička, protože JSX není HTML a nový řádek se nepřeloží do mezery, kterou tam ale mít chceme. Lehce také upravíme root komponentu, protože musíme předat nově i callbacky: 179 | 180 | ```js 181 | import React from 'react'; 182 | import Car from './car'; 183 | 184 | export default class Cars extends React.Component { 185 | 186 | render() { 187 | const car1 = { 188 | id: 1, 189 | brand: 'Ferrari', 190 | model: 'FF', 191 | year: '2014', 192 | details: true 193 | }; 194 | 195 | const car2 = { 196 | id: 2, 197 | brand: 'BMW', 198 | model: 'M5', 199 | year: '2013', 200 | details: false 201 | }; 202 | 203 | return ( 204 |
205 | 206 | 207 |
208 | ); 209 | } 210 | 211 | removeCar(id) { 212 | console.log(`remove ${id}`); 213 | } 214 | 215 | toggleCar(id) { 216 | console.log(`toggle ${id}`); 217 | } 218 | 219 | } 220 | ``` 221 | 222 | Přidali jsme si i další auto, ať vidíme, jak se komponenta chová, když je zabalená. Výsledek (otevřte si konzoli a zkuste poklepat na tlačítka): 223 | 224 | $$$cars2$$$ 225 | 226 | ##Komponenta nové auto 227 | 228 | Nyní si připravíme komponentu pro přidání nového auta. Bude to jen jednoduchý formulář s metodou, která po jeho odeslání zavolá callback rodiče: 229 | 230 | ```js 231 | import React, {findDOMNode} from 'react'; 232 | import './addCar.less'; 233 | 234 | export default class AddCar extends React.Component { 235 | 236 | render() { 237 | return ( 238 |
239 |

Nové auto

240 |
this.handleSubmit(e)} ref="addCar"> 241 | 242 |
243 | 244 | 245 |
246 | 247 | 248 |
249 | 250 | 251 |
252 |
253 | ); 254 | } 255 | 256 | handleSubmit(e) { 257 | e.preventDefault(); 258 | this.props.add({ 259 | brand: findDOMNode(this.refs.brand).value, 260 | model: findDOMNode(this.refs.model).value, 261 | year: findDOMNode(this.refs.year).value 262 | }); 263 | findDOMNode(this.refs.addCar).reset(); 264 | } 265 | 266 | } 267 | 268 | AddCar.propTypes = { 269 | add: React.PropTypes.func.isRequired 270 | }; 271 | ``` 272 | 273 | V metodě `render()` není mnoho zajímavého. Za zmínku stojí, že **občas jsou některé atributy v JSX přejmenované**. Například `for` na `htmlFor`, či `class` na `className`. To proto, aby nedocházelo ke kolizím s klíčovými slovy JS. Pokud byste na to však zapomněli, tak vám React poradí v konzoli. React je velmi výřečný co se warningů a errorů týče, což je dobře. 274 | 275 | Formulář má na sobě pověšený listener `onSubmit`, který po odeslání zavolá příslušnou metodu. **V ní musíme ihned zavolat `e.preventDefault()`**, jinak by došlo k redirectu aneb standardní chování prohlížeče. 276 | 277 | **Důležitým atributem je `ref`**. Ten se hodí, pokud si chceme "šáhnout" na skutečný DOM element. V JSX si ho pomocí tohoto atributu označkujeme a pak se k němu díky `React.findDOMNode(this.refs.foo)` můžeme dostat. Vidíte, že k hodnotám formuláře přistupujeme velmi nízkoúrovňově, nepotřebujeme žádnou komplikovanou a zbytečnou abstrakci. 278 | 279 | **V našem příkladě používáme "nekontrolované" vstupní elementy**. Toto je totiž jediná výjimka, kdy React na chvílí přihmouří oči a nechá vás měnit hodnoty v DOMu (value v inputech), aniž by do toho jakkoliv zasahoval. **Existuje však druhý "kontrolovaný" přístup**. Ten je lepší z více důvodů (není potřeba "šahat" na DOM elementy, snadná validace, persistence rozepsaného formuláře atp.), ale o tom bude ještě samostatný článek. V dnešním nám stačí tento první přímočařejší přístup. 280 | 281 | Formulář si i decentně ostylujeme. Tentokrát pomocí LESSu: 282 | 283 | ``` 284 | .addCar { 285 | label { 286 | width: 65px; 287 | display: inline-block; 288 | } 289 | } 290 | ``` 291 | 292 | Nyní si hotovou komponentu zas vyzkoušíme: 293 | 294 | ```js 295 | import React from 'react'; 296 | import AddCar from './addCar'; 297 | 298 | export default class Cars extends React.Component { 299 | 300 | render() { 301 | return ( 302 |
303 | 304 |
305 | ); 306 | } 307 | 308 | addCar(car) { 309 | console.log(`add ${car}`); 310 | } 311 | 312 | } 313 | ``` 314 | 315 | A výsledek: 316 | 317 | $$$cars3$$$ 318 | 319 | ##Hlavní komponenta Auta 320 | 321 | **Teď už nám zbývá to dát vše dohromady** v hlavní komponentě a implementovat metody `addCar`, `toggleCar` a `removeCar`. Začneme konstruktorem, ve kterém si inicializujeme databázi aut a nabindujeme metodám správný context: 322 | 323 | ```js 324 | constructor() { 325 | super(); 326 | this.state = {cars: [ 327 | {id: 1, brand: 'Ferrari', model: 'FF', year: '2014', details: false}, 328 | {id: 2, brand: 'BMW', model: 'M5', year: '2013', details: true} 329 | ]}; 330 | this.addCar = this.addCar.bind(this); 331 | this.removeCar = this.removeCar.bind(this); 332 | this.toggleCar = this.toggleCar.bind(this); 333 | } 334 | ``` 335 | 336 | Nyní pojďme na jednotlivé metody. Nebude to už žádná věda. `addCar` vypadá takto: 337 | 338 | ```js 339 | addCar(car) { 340 | car.id = this.state.cars.length ? 341 | this.state.cars[this.state.cars.length - 1].id + 1 : 1; 342 | 343 | this.setState({ 344 | cars: this.state.cars.concat([car]) 345 | }); 346 | } 347 | ``` 348 | 349 | Jedinou zvláštností je to, že jsme si potřebovali "vygenerovat" unikátní `id`. Použil jsem prostou inkrementaci `id` posledního přidaného auta. V případě, že v poli `this.state.cars` už žádné položky nejsou, tak se použije `1`. Mohli bychom použít třeba i náhodný řetězec. 350 | 351 | `removeCar` má podobu: 352 | 353 | ```js 354 | removeCar(id) { 355 | this.setState({ 356 | cars: this.state.cars.filter(car => car.id !== id) 357 | }); 358 | } 359 | ``` 360 | 361 | `filter` je jedna z užitečných funkcí pole. Pro každý prvek v poli zavolá funkci, kterou mu předáme. Pokud je její výsledek `true`, tak ho vrátí i ve výsledném poli. Jednoduše si tedy smazané auto tímto odfiltrujeme. 362 | 363 | A zbývá `toggleCar`: 364 | 365 | ```js 366 | toggleCar(id) { 367 | this.setState({ 368 | cars: this.state.cars.map(car => { 369 | if (car.id !== id) { 370 | return car; 371 | } 372 | car.details = !car.details; 373 | return car; 374 | }) 375 | }); 376 | } 377 | ``` 378 | 379 | `map` zavolá pro každý prvek pole funkci, kterou mu předáme. Není to úplně hezké řešení, ale nám to pro tuto chvíli stačí. Lepší by bylo nemít auta v poli, ale v objektu (mapě) a přistupovat k nim přímo pomocí `id`. Nicméně, to nám zase nezaručí jednoznačné pořadí. Brzo si však představíme [Immutable.js](https://facebook.github.io/immutable-js/), které rožšiřuje JS o pěkné immutable kolekce. Například zde by se nám velmi hodilo `Immutable.OrderedMap()`. 380 | 381 | V `render()` už k žádným překvapením nedochází. Vše si pouze vypíšeme: 382 | 383 | ```js 384 | render() { 385 | return ( 386 |
387 | 388 |

Auta ({this.state.cars.length})

389 | {this.state.cars.map(car => { 390 | return ; 394 | })} 395 |
396 | ); 397 | } 398 | ``` 399 | 400 | **To, že React slouží pro mapování dat na DOM, není jen řečnický obrat, ale doslovná skutečnost.** Můžeme vidět, jak pomocí funkce `map` vytváříme na základě stavu pole nových React komponent, které se ihned vypíší. Přibyl atribut `key={car.id}`. Ten slouží čistě potřebám Reactu, aby měl přehled o tom, které elementy v budoucím rendrování zmizí či se jen přemístí. Může pak lépe optimalizovat. Pokud byste `key` neuvedli, tak vám v konzoli React vynadá, ale kód bude stále funkční. 401 | 402 | **Dílo je u konce. Celá hlavní komponenta vypadá takto:** 403 | 404 | ```js 405 | import React from 'react'; 406 | import Car from './car'; 407 | import AddCar from './addCar'; 408 | 409 | export default class Cars extends React.Component { 410 | 411 | constructor() { 412 | super(); 413 | this.state = {cars: [ 414 | {id: 1, brand: 'Ferrari', model: 'FF', year: '2014', details: false}, 415 | {id: 2, brand: 'BMW', model: 'M5', year: '2013', details: true} 416 | ]}; 417 | this.addCar = this.addCar.bind(this); 418 | this.removeCar = this.removeCar.bind(this); 419 | this.toggleCar = this.toggleCar.bind(this); 420 | } 421 | 422 | render() { 423 | return ( 424 |
425 | 426 |

Auta ({this.state.cars.length})

427 | {this.state.cars.map(car => { 428 | return ; 432 | })} 433 |
434 | ); 435 | } 436 | 437 | addCar(car) { 438 | car.id = this.state.cars.length ? 439 | this.state.cars[this.state.cars.length - 1].id + 1 : 1; 440 | 441 | this.setState({ 442 | cars: this.state.cars.concat([car]) 443 | }); 444 | } 445 | 446 | removeCar(id) { 447 | this.setState({ 448 | cars: this.state.cars.filter(car => car.id !== id) 449 | }); 450 | } 451 | 452 | toggleCar(id) { 453 | this.setState({ 454 | cars: this.state.cars.map(car => { 455 | if (car.id !== id) { 456 | return car; 457 | } 458 | car.details = !car.details; 459 | return car; 460 | }) 461 | }); 462 | } 463 | 464 | } 465 | ``` 466 | 467 | **Tadááááá. Výsledná aplikace:** 468 | 469 | $$$cars$$$ 470 | 471 | ##Nedostatky 472 | 473 | **Jelikož se zatím soustředíme pouze na React, používáme jeho komponenty na všechno.** V reálném použití se snažíme udržovat React komponenty co nejjednodušší a nikdy do nich nedáváme logiku typu `addCar`, `removeCar` či `toggleCar`. "Zpětná" komunikace pomocí předávání callbacků se také moc nepoužívá, jelikož to vytváří závislosti mezi jednotlivými komponentami. Navíc si představte, pokud byste měli v sobě zanořených třeba 5 komponent a museli byste si pořád vše předávat skrz props. **Chybí nám tu ono M a C, chybí nám tu zatím Redux**. Dalším nedostatkem je nepoužívání immutable kolekcí, které přinášejí optimalizaci velkou jako Brno. A samozřejmě **formulář by také zasloužil více lásky**. 474 | 475 | ##Závěrem 476 | 477 | **Dnes jsme si naprogramovali složitější aplikaci a názorně si demonstrovali základní práci s Reactem.** Páteří každé React aplikace jsou props a předávání dat skrz ně. Při vytváření nových stavů je potřeba velká obezřetnost, protože nám mohou značně zkomplikovat celou aplikaci. Je dobré stavy "vyhnat" na jedno místo, ideálně do root komponenty. **Příště se mrkneme do hloubky na JSX a ukážeme si, že lze používat React i bez něj.** 478 | 479 | 480 | -------------------------------------------------------------------------------- /content/React-uvod/index.md: -------------------------------------------------------------------------------- 1 | #React - Úvod 2 | 3 | **React je JavaScriptová knihovna pro vytváření webových komponent.** V pomyslném MVC představuje "V" neboli view vrstvu a dal by se tak přirovnat například Latte v Nette. React je však daleko více. Přináší totiž zásadní změnu paradigmatu. S Reactem už nepíšeme kód, který něco mění, ale kód, který popisuje, jak má vypadat výsledek, což je řádově snažší úloha. Dvojnásob to pak platí, pokud tím výsledkem je "těžká váha" v podobě DOMu. 4 | 5 |

6 | 7 |

8 | 9 | 10 | ##Trocha historie 11 | 12 | **React spatřil světlo světa v květnu 2013**. Opensourcoval ho Facebook, který ho už několik let před tím sám interně používal a vylepšoval. Prvotní vydání se však dočkalo velkého výsměchu. Odezva byla dokonce tak špatná, že Facebook chvíli uvažoval i o jeho stáhnutí. Terčem kritiky se stalo především míchání "HTML a programování". Podobné obavy nedávno vyjádřili i někteří [prominentní čeští webaři](https://twitter.com/geekovo/status/604710448131391490). Postupně se však ukázalo, že došlo k pouze nepochopení základního konceptu a nejen FE vývojáři si začli rychle osvojovat a užívat nové fundamenty, které React přinesl. 13 | 14 | **K dnešnímu dni** (květen 2016 **má 6600 commitů, 41 000 stargazerů a 685 contributorů a je tak jedním z nejoblíbenějších a nejaktivnějších repozitářů na GitHubu**. Facebook během této doby uvolnil i další JS projekty jako [React Native](https://facebook.github.io/react-native/) (React pro iOS a Android) či [Immutable.js](https://facebook.github.io/immutable-js/) (immutable kolekce). 15 | 16 | Zajímavou knihovnou je také [GraphQL](http://facebook.github.io/graphql/), což je dotazovací jazyk, kterým v komponentách popíšete, jaká data ze serveru potřebují. Doplňuje ho [Relay](https://facebook.github.io/relay/), který GraphQL umí na straně webu zpracovat a je tak zajímavou alternativou pro datovou komunikaci server-client, která dnes představuje nejtěžší část webové aplikace. 17 | 18 | ##Pořád jen Facebook 19 | 20 | Všechny výše uvedené projekty jsou široce používané a je kolem nich mohutná komunita. Nevezou se však na popularitě Reactu, protože s ním nejsou nijak spojené. Jsou prostě tak dobré. **Facebook v posledních letech jednoznačně udává trendy v tvorbě webových aplikací**. Nevytváří velké ucelené frameworky, ale pouze malé pragmatické knihovny s minimalistickým API, což má v JS ekosystému úspěch. Přístup Facebooku k opensource by měl být vzorem i pro další firmy. 21 | 22 | **Druhým webovým hegemonem je Google**, který ale v poslední době působí trochu schizofrenicky. Jakoby si každý jeho tým budoval vlastní framework/knihovnu a tak tu máme vedle sebe [Angular](https://angularjs.org/), [Closure Tools](https://developers.google.com/closure/) a [Polymer](https://www.polymer-project.org/1.0/). Alespoň už zařízl Dart a třeba tak více konsoliduje své síly, co se JS knihoven týče. 23 | 24 | ##Server vs prohlížeč 25 | 26 | Klasické server-side webovky jsou vcelku jednoduché. **Server totiž nemusí udržovat žádný stav**. Přijde mu požadavek od uživatele, poslepuje dohromady nějaké řetězce (část z nich načte třeba z databáze) a celé to pak pošle uživateli do prohlížeče, který to rozparsuje a sestaví DOM (Document Object Model - stromová reprezetace toho co vidíte v prohlížeči jako HTML stránku + metody, kterými se dá upravovat či procházet). 27 | 28 | **Interaktivní JS webovky, které běží v prohlížeči, jsou podstatně složitější**. Chceme totiž po nich daleko více než od server-side aplikací. Chceme nabídnout daleko vyšší uživatelský zážitek. Je trochu zbytečné s každou akcí překreslovat celou stránku, když můžeme změny bleskově provádět pomocí funkcí DOMu a uživateli tak dopřát pohodlí, které zná z desktopových aplikací. 29 | 30 | ##Zlý DOM 31 | 32 | **DOM a JavaScript jsou dvě úplně rozdílné věci**. Teoreticky by mohl být DOM upravován i jiným jazykem, ale v praxi prohlížeče nabízejí pouze JavaScript. DOM je obrovský moloch, práce s ním je pomalá a prohlížeče nejsou vždy jednotné v jeho implementaci. **JavaScript pak často neprávem sklízí nenávistné komentáře, které by ale měly směřovat právě na DOM**. Dnešní JavaScript je naopak velmi rychlý a vcelku lišácky navržený, což ostatně potvrzuje i to, že se rozšiřuje i do oblastí, kde má narozdíl od prohlížečů silnou konkurenci (node.js). 33 | 34 | DOM navíc představuje jednu velkou globální proměnnou, což přímo svádí k tomu, abychom ho používali jako místo pro ukládání stavu naší aplikace. Asi nemusím popisovat proč jsou globální proměnné zlo. U DOMu to pak ještě zhoršuje jeho pomalost. **Stav aplikace bychom tedy měli udržovat jinde - v našem JavaScriptu** a do DOMu šahat, jenom když je to skutečně potřeba. Pouze do něj zrcadlit změny z našeho JS kódu. Jenže jak to jednoduše udělat? 35 | 36 | ##Přínos Reactu 37 | 38 | **Nejzřejmější výhoda pro začátečníka je ta, že React nás prakticky úplně odstíní od DOMu**. V React komponentách pouze deklarativně zadefinujeme strukturu (HTML) skládáním JS funkcí. Jinými slovy, popíšeme, jak má vypadat výsledná stránka na základě příchozích dat. React si z toho poskládá svůj vlastní virtuální DOM, který pak pomocí chytrých algoritmů porovnává s tím skutečným DOMem a když najde rozdíly, tak ho nejefektivnějším možným způsobem aktualizuje. **My už pak jenom dodáváme nová data do jednotlivých komponent a tím pro nás veškerá práce končí**. V prohlížeči vždy uvidíte aktuální pohled vzhledem k dodaným datům. Tohle je "deal breaker", který vám pravděpodobně není úplně zřejmý, pokud už nemáte předešlou trpkou zkušenost s imperativním přístupem u větší aplikace, což je typicky jQuery a fidlování s jednotlivými DOM elementy. 39 | 40 | ##Začínáme 41 | 42 | Je načase si ukázat první příklad. Komponenty si budete moc vyzkoušet přímo v rámci této stránky nebo zkopírováním do svého [vymazleného stacku](prvni-dev-stack.html). Najdete je samozřejmě také v [repozitáři](https://github.com/tajo/javascript/tree/master/examples/react). Budu téměř výhradně používat ES6 syntax. 43 | 44 | Naše první komponenta se bude jmenovat `Counter` a **bude udržovat a zobrazovat počet našich kliknutí**. Hned se podívejte na výsledek: 45 | 46 | $$$counter$$$ 47 | 48 | A teď si to pojďme rozebrat a naprogramovat. Jak už to bývá, ani u Reactu se neobejdme bez minimálního boilerplate kódu: 49 | 50 | ```js 51 | import React from 'react'; 52 | 53 | export default class Counter extends React.Component { 54 | ``` 55 | 56 | **Nikdy nesmíme zapomenout na importování Reactu**. I když to není na první pohled zřejmé, tak ony HTML tagy (JSX) jsou ve skutečnosti převlečené funkce Reactu, ale to bychom teď předbíhali. Novou komponentu jednoduše oddědíme od `React.Component` a `export default class Counter` nám komponentu zpřístupní napříč celou aplikací. 57 | 58 | **React komponenty mají pouze jednu povinnou metodu a to `render()`**. V ní se očekává, že popíšete strukturu komponenty včetně jejích závislostí na data. Metoda musí pak vrátit 1 root element (například `
`), který už může mít libovolný počet potomků včetně dalších React komponent. Takhle vypadá ta naše: 59 | 60 | ```js 61 | render() { 62 | return ( 63 |
64 | Kliknul jsi {this.state.counter}x.
65 | 68 |
69 | ); 70 | } 71 | ``` 72 | 73 | **Povšimněte si, že "uprostřed" JSX zápisu můžete opět vkládat JS výrazy**, pokud je obalíte do `{}` a takhle to můžete prokladát do několika úrovní, pokud máte rádi nepřehledný kód. JSX velmi připomíná klasické HTML a rozdílů byste našli jen několik. Což je vcelku zajímavé, protože pod pokličkou jde o principálně zcela jinou věc. 74 | 75 | `this.state` je jeden ze dvou hlavních objektů, který "krmí" React komponenty daty. O tom druhém `this.props` a rozdílech si povíme až příště. **`this.state` je lokální stav komponenty, do kterého si můžeme ukládat libovolná data**. Nám se hodí pro uložení počtu kliknutí. Pomocí `{this.state.counter}` si tento počet pak jednoduše vypíšeme. 76 | 77 | Klikací tlačítko má atribut `onClick`. **Vzpomínáte na poučku, že se nemají používat tyto inline atributy pro volání JS? Tak ta stále platí.** Nicméně protože ` 140 |
141 | ); 142 | } 143 | 144 | handleClick(e) { 145 | this.setState(previousState => { 146 | return {counter: previousState.counter + 1}; 147 | }); 148 | } 149 | 150 | } 151 | ``` 152 | 153 | ##Závěrem 154 | 155 | **Představili jsme si React, jeho krátkou historii a související projekty**. Jedním ze strašáků "programování v prohlížeči" je DOM, často chybně a neférově zaměňován s JavaScriptem. React nám mj. umožňuje téměř kompletní abstrakci od DOMu. Strukturu jednotlivých komponent zapisujeme (deklarujeme) skládáním React funkcí, kterým následně dodáváme data a tím pro nás práce končí. React toho nabízí samozřejmě daleko více. Nakousli jsme třeba i systém eventů. **S Reactem je to tak trochu jako s dobrým čajem nebo kafem**. Zpočátku vám bude připadat divný a musíte se k němu postupně "propít". Až poté se stane nepostradatelnou součástí vašeho programátorského dne. 156 | 157 | **Příště si ukážeme, co jsou to `this.props` a jak se liší od `this.state`**. Také si více povíme o životním cyklu komponenty a jejích dalších metodách. 158 | -------------------------------------------------------------------------------- /content/React-uvod/pics/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tajo/javascript/5a85b5364ded3c47338144124ff5a24e1bf6e530/content/React-uvod/pics/logo.png -------------------------------------------------------------------------------- /content/Stack/index.md: -------------------------------------------------------------------------------- 1 | #První dev stack 2 | 3 | Hlavním hrdinou dnešního článku se stane bundlovací nástroj webpack. Lecos jste se o něm mohli dozvědět už v [minulém článku](nastroje.html). S jeho pomocí postavíme první jednoduchou "Hello world" aplikaci v Reactu, která bude například používat Babel (JavaScript budoucnosti) a LESS (CSS preprocesor). **Ukážeme si sílu moderních JS nástrojů v praxi**. 4 | 5 | ##Výsledek 6 | 7 | Protože určitě nemáte rádi překvapení, tak vám hned v úvodu ukážu k čemu se na konci tohoto článku dopracujeme. Výsledkem bude triviální 100% JavaScriptová aplikace s jedinou React komponentou. Jakékoliv úpravy v ní, či přiloženém LESSu se po uložení v editoru **okamžitě** projeví na stránce. **Bez refreshnutí, prosím pěkně!** Kompletní zdrojové kódy najdete v [samostatném repozitáři na GitHubu](https://github.com/tajo/devstack). 8 | 9 |

10 | 11 |

12 | 13 | 14 | ##Očekávání 15 | 16 | Doufám, že se vám demo líbilo. Na první pohled se možná nezdá jako kdoví co, ale vězte, že pod jeho pokličkou toho bublá daleko více. Běží totiž na moderním JS dev stacku, což je soubor různých knihoven, technik a nastavení. Je to základová deska pro náš rodinný baráček. Avšak s menší úpravou by zvládla unést i velký hrad. Co tedy od ní čekat? Jaké jsou naše požadavky? 17 | 18 | **Chceme:** 19 | - **programovat v Babelu**, protože [je to lepší JavaScript](babel.html) 20 | - budovat velkou a škálovatelnou aplikaci → **chceme maximální modularitu** 21 | - mít možnost jednoduše integrovat a **používat stovky tisíce balíčků z npm** 22 | - používat nějaký šikovný **CSS preprocesor**, třeba LESS, do budoucna pak možná [postprocesor](http://www.zdrojak.cz/clanky/smeruje-css-od-preprocesingu-k-postprocesingu/) 23 | - po každé změně v JS i CSS **ihned vidět výsledek v prohlížeči**, ideálně bez refreshnutí 24 | - mít **2 módy**: vývojový a produkční 25 | - v produkčním módu všechny potřebné JS soubory (moduly) **sloučit do jednoho a minimalizovat**, obdobně i pro kaskádové styly 26 | - v produkčním módu ignorovat warningy a jiné debugovací výpisy 27 | - mít **kód kontrolovaný linterem**, aby zůstal pěkný a jednotný 28 | - aby po prvotním nastavení **všechno fungovalo automaticky** 29 | - **jednoduchost!** 30 | 31 | Dlouhý a náročný seznam? Ale kdeže. Jsme programátoři, náš čas je extrémně drahý a menší investice do ladění vývojářského procesu se nám mnohonásobně vrátí. Navíc to sfoukneme raz dva! 32 | 33 |

34 | 35 |

36 | 37 | Stack pro tento článek jsem si samozřejmě nevycucal z prstu, ale inspiroval se z mnoha jiných. Mým cílem bylo postavit něco, co bude maximálně jednoduché a přesto použitelné pro reálnou aplikaci. Neobsahuje například React-router, Redux či Immutable.js. Jsou to výborné knihovny a vřele doporučuji je prozkoumat, ale pro začátek nejsou potřeba. Řeší totiž problémy, které vyvstávají až u velkých aplikací. Pokud přesto máte chuť a odvahu jít do toho "all-in", zkuste například [github.com/este/este](https://github.com/este/este). 38 | 39 | ##Webpack 40 | 41 | Už jsem o něm básnil v [předchozím článku](nastroje.html). Pro jeho docenění je nejdříve potřeba pojmenovat zásadní problém, kterému vývojáři webových aplikací čelí. Naše aplikace (zdrojové kódy) jsou distribuovány pomocí pomalého a nespolehlivého protokolu HTTP do vzdáleného prohlížeče. Tam se musí ihned zparsovat, spustit a něco provést. Velikost naší aplikace je tedy velmi limitovaná, pokud nechceme uživatele ztratit nekonečnou bílou stránkou. **Aby vše fungovalo svižně, musíme co nejvíce omezit počet HTTP requestů a minimalizovat jejich velikost.** 42 | 43 |

44 | 45 |

46 | 47 | Jinými slovy **musíme velmi pečlivě zvážit, co a kdy do prohlížeče posílat a po jakých částech**. U spousty aplikací nám bude stačit to, když všechny naše skripty spojíme do jednoho souboru `bundle.js`. Ovšem takový soubor může časem nepřijemně nabobtnat. Jak to vyřešit? Každá část (stránka) naší aplikace potřebuje pravděpodobně různé skripty a tak můžeme dle URL adresy posílat speciální menší bundly, kde bude jen to, co daná stránka skutečně potřebuje. Kód, který používají všechny stránky (typicky knihovna jako jQuery či React), pak dáme do ` 58 | ` 59 | }; 60 | 61 | return ( 62 | 63 | 64 | 65 | DžejEs - JavaScript pro web 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | {this.state.renderPage && this.renderPage()} 75 | 77 |
78 | 79 | 80 | ); 81 | } 82 | 83 | renderPage() { 84 | switch (this.props.location) { 85 | case Pages.HOME.location: 86 | return ; 87 | } 88 | 89 | const pageKeys = Object.keys(Articles); 90 | for (let i = 0; i < pageKeys.length; i++) { 91 | const key = pageKeys[i]; 92 | if (this.props.location === Articles[key].location) { 93 | return
; 95 | } 96 | } 97 | 98 | throw new Error( 99 | 'Page of location ' + 100 | JSON.stringify(this.props.location) + 101 | ' not found.' 102 | ); 103 | } 104 | 105 | componentDidMount() { 106 | if (!this.state.renderPage) { 107 | this.setState({ 108 | renderPage: true 109 | }); 110 | } 111 | } 112 | } -------------------------------------------------------------------------------- /site/LICENSE: -------------------------------------------------------------------------------- 1 | BSD License 2 | 3 | Copyright (c) 2015, Facebook, Inc. All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | * Neither the name Facebook nor the names of its contributors may be used to 16 | endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 23 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 26 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /site/README.md: -------------------------------------------------------------------------------- 1 | This is based on the [react-dnd](https://github.com/gaearon/react-dnd) website, which is based on the [fixed-data-table](https://github.com/facebook/fixed-data-table) website. 2 | 3 | [Dan Abramov](http://github.com/gaearon) gave me a permission to use it for this project. Thank you! 4 | 5 | Checkout the generated web: [http://www.dzejes.cz](http://www.dzejes.cz) 6 | 7 | Local development: 8 | 9 | ``` 10 | git clone git@github.com:tajo/javascript.git 11 | cd javascript 12 | npm install 13 | npm start 14 | ``` 15 | 16 | After it builds the static site (might take about half a minute), open [http://localhost:8080](http://localhost:8080). 17 | 18 | For publishing you have to update constants in `/scripts/publishStaticSite.sh` and run: 19 | 20 | ``` 21 | npm run publish-site 22 | ``` 23 | 24 | -------------------------------------------------------------------------------- /site/_config.yml: -------------------------------------------------------------------------------- 1 | url: https://www.dzejes.cz # with the https protocol 2 | enforce_ssl: www.dzejes.cz # without any protocol 3 | -------------------------------------------------------------------------------- /site/base.less: -------------------------------------------------------------------------------- 1 | @import './constants.less'; 2 | 3 | * { box-sizing: border-box; } 4 | 5 | html, body { 6 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 7 | -webkit-text-size-adjust: 100%; 8 | -ms-text-size-adjust: 100%; 9 | margin: 0; 10 | padding: 0; 11 | -webkit-font-smoothing: antialiased; 12 | text-rendering: optimizeLegibility; 13 | color: @body-color; 14 | font-family: 'Helvetica Neue', Helvetica, sans-serif; 15 | font-size: 14px; 16 | line-height: 1.625; 17 | } 18 | 19 | @media only screen and (min-width: @screen-tablet) { 20 | body { 21 | font-size: 18px; 22 | } 23 | } 24 | 25 | h1, h2, h3, h4, h5, h6 { 26 | color: @header-color; 27 | } 28 | 29 | p { 30 | margin-top: 0.35em; 31 | } 32 | 33 | h1 { 34 | font-size: 2.5em; 35 | margin: 1.75em 0em 0em 0em; 36 | } 37 | 38 | h2, h3, h4, h5, h6 { 39 | margin: 1.5em 0em 0em 0em; 40 | } 41 | 42 | .codeBlock { 43 | h2, h3, h4, h5, h6 { 44 | margin-top: 1em; 45 | } 46 | } 47 | 48 | a[href] { 49 | color: @link-color; 50 | } 51 | 52 | a[id]:not([href]) { 53 | // This is quite a badass hack. 54 | // We really those anchors despite the navbar! 55 | position: relative; 56 | top: -@navbar-height; 57 | } 58 | 59 | pre, code { 60 | font-family: Consolas, 'Source Code Pro', Menlo, monospace; 61 | background: #F9F8F7; 62 | color: #484A4C; 63 | // font-size: 1em; 64 | // letter-spacing: -0.015em; 65 | } 66 | 67 | a code { 68 | color: inherit; 69 | } 70 | 71 | code { 72 | margin: -0.05rem -0.15em; 73 | padding: 0.05rem 0.35em; 74 | font-size: 0.85em; 75 | background: rgb(245, 245, 245); 76 | } 77 | 78 | blockquote { 79 | margin: 1rem 0; 80 | padding: 0 1rem; 81 | color: #727476; 82 | border-left: solid 3px #DCDAD9; 83 | } 84 | 85 | blockquote > :first-child { 86 | margin-top: 0; 87 | } 88 | 89 | blockquote > :last-child { 90 | margin-bottom: 0; 91 | } 92 | 93 | hr { 94 | border: 1px solid; 95 | color: @body-color; 96 | opacity: 0.1; 97 | } 98 | 99 | footer { 100 | border-top: 1px dashed #DDD; 101 | padding-top: 1em; 102 | max-width: @content-width; 103 | margin: 0px auto; 104 | } 105 | 106 | .pic-container { 107 | max-width:40em; 108 | margin:0px auto; 109 | text-align: center; 110 | } 111 | 112 | .pic { 113 | width: 100%; 114 | } 115 | 116 | .masterHead { 117 | width: 100%; 118 | height: 313px; 119 | background-image: linear-gradient(45deg, rgba(44,83,158, 1), rgba(44,83,158, 0.75)); 120 | color: #FFF; 121 | padding: 30px; 122 | text-align: center; 123 | font-size: 1.7em; 124 | margin-bottom: 3em; 125 | 126 | .cover { 127 | font-size: 0.70em; 128 | } 129 | 130 | p { 131 | margin: 10px; 132 | } 133 | 134 | .circle-bg { 135 | width: 200px; 136 | height: 200px; 137 | border-radius: 100px; 138 | background-color: #FFF; 139 | z-index: 1; 140 | margin: auto; 141 | position: absolute; 142 | top: 201px; left: 0; right: 0; 143 | } 144 | 145 | .circle-bg2 { 146 | width: 180px; 147 | height: 180px; 148 | border-radius: 100px; 149 | border: 10px solid #DDD; 150 | z-index: 2; 151 | margin: auto; 152 | position: absolute; 153 | top: 211px; left: 0; right: 0; 154 | padding-top: 30px; 155 | color: #333; 156 | font-size: 34px; 157 | font-weight: bold; 158 | padding: 51px 0px 0px 4px; 159 | } 160 | 161 | } 162 | 163 | 164 | 165 | // Markdown 166 | 167 | .codeBlock { 168 | -webkit-overflow-scrolling: touch; 169 | background: rgb(250, 250, 250); 170 | border-left: solid 5px rgba(0,255,0,0.3);; 171 | box-sizing: border-box; 172 | display: block; 173 | font-size: 0.85em; 174 | margin: 0.5rem 0 1.2rem; 175 | overflow-y: scroll; 176 | padding: 0.8rem 8px 0.5rem 16px; 177 | white-space: pre; 178 | position: relative; 179 | } 180 | 181 | .t.blockParams { 182 | padding-left: 2ch; 183 | } 184 | 185 | // TODO: not random colors 186 | 187 | .token.punctuation, 188 | .token.ignore, 189 | .t.interfaceDef, 190 | .t.member, 191 | .t.callSig { 192 | color: #808890; 193 | } 194 | 195 | .token.function, 196 | .token.class-name, 197 | .token.qualifier, 198 | .t.fnQualifier, 199 | .t.fnName { 200 | color: #32308E; 201 | } 202 | 203 | .token.primitive, 204 | .t.primitive { 205 | color: #922; 206 | } 207 | 208 | .token.number, 209 | .t.typeParam { 210 | color: #905; 211 | } 212 | 213 | .t.typeQualifier, 214 | .t.typeName { 215 | color: #013679; 216 | } 217 | 218 | .t.param { 219 | color: #945277; 220 | } 221 | 222 | .t.memberName { 223 | color: teal; 224 | } 225 | 226 | .token.block-keyword, 227 | .token.keyword, 228 | .t.keyword { 229 | color: #A51; 230 | } 231 | 232 | .token.string, 233 | .token.regex { 234 | color: #df5050; 235 | } 236 | 237 | .token.operator { 238 | color: #a67f59; 239 | } 240 | 241 | .token.comment { 242 | color: #998; 243 | font-style: italic; 244 | } 245 | -------------------------------------------------------------------------------- /site/client.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var React = require('react'); 4 | var IndexPage = require('./IndexPage'); 5 | 6 | React.render( 7 | , 10 | document 11 | ); 12 | -------------------------------------------------------------------------------- /site/components/CodeBlock.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component, findDOMNode } from 'react'; 2 | import ReactUpdates from 'react/lib/ReactUpdates'; 3 | import StaticHTMLBlock from './StaticHTMLBlock'; 4 | 5 | import './CodeBlock.less'; 6 | 7 | let preferredSyntax = 'es5'; 8 | let observers = []; 9 | 10 | function subscribe(observer) { 11 | observers.push(observer); 12 | return () => { 13 | observers.slice(observers.indexOf(observer), 1); 14 | }; 15 | } 16 | 17 | function setPreferredSyntax(syntax) { 18 | preferredSyntax = syntax; 19 | observers.forEach(o => o(preferredSyntax)); 20 | } 21 | 22 | export default class CodeBlock extends Component { 23 | static propTypes = { 24 | es5: PropTypes.string, 25 | es6: PropTypes.string 26 | }; 27 | 28 | static defaultProps = { 29 | es5: '', 30 | es6: '' 31 | }; 32 | 33 | constructor(props) { 34 | super(props); 35 | this.state = { 36 | chosen: false, 37 | syntax: this.props.es5.trim().length && 'es6' || 38 | this.props.es6.trim().length && 'es5' 39 | }; 40 | } 41 | 42 | componentDidMount() { 43 | this.unsubscribe = subscribe(this.handlePreferredSyntaxChange.bind(this)); 44 | } 45 | 46 | handlePreferredSyntaxChange(syntax) { 47 | if (this.state.chosen || this.state.syntax === syntax) { 48 | return; 49 | } 50 | 51 | if (this.props[syntax].trim().length) { 52 | this.setState({ 53 | syntax 54 | }); 55 | } 56 | } 57 | 58 | componentWillUnmount() { 59 | this.unsubscribe(); 60 | } 61 | 62 | handleSyntaxClick(syntax) { 63 | this.setState({ 64 | syntax, 65 | chosen: true 66 | }); 67 | 68 | const scrollTopBefore = findDOMNode(this).getBoundingClientRect().top; 69 | setPreferredSyntax(syntax); 70 | ReactUpdates.flushBatchedUpdates(); 71 | const scrollTopAfter = findDOMNode(this).getBoundingClientRect().top; 72 | 73 | window.scroll( 74 | window.pageXOffset || window.scrollX, 75 | (window.pageYOffset || window.scrollY) - (scrollTopBefore - scrollTopAfter) 76 | ); 77 | } 78 | 79 | render() { 80 | return ( 81 |
82 |
    83 | {['es5', 'es6'].map(this.renderSyntaxLink, this)} 84 |
85 |
86 | 87 |
88 |
89 | ); 90 | } 91 | 92 | renderSyntaxLink(syntax) { 93 | if (!this.props[syntax] || !this.props[syntax].trim().length) { 94 | return null; 95 | } 96 | 97 | if (syntax === 'es5' && 98 | !this.props.es6.trim().length) { 99 | return null; 100 | } 101 | 102 | const active = this.state.syntax === syntax; 103 | let label = 'ES5'; 104 | if (syntax === 'es6') { 105 | label = 'Babel'; 106 | } 107 | return ( 108 |
  • 110 | 111 | {label} 112 | 113 |
  • 114 | ); 115 | } 116 | } -------------------------------------------------------------------------------- /site/components/CodeBlock.less: -------------------------------------------------------------------------------- 1 | @import '../constants.less'; 2 | 3 | .CodeBlock-tabs { 4 | padding: 0; 5 | } 6 | 7 | .CodeBlock-tab { 8 | display: inline-block; 9 | margin-right: 10px; 10 | } 11 | 12 | .CodeBlock-tab a { 13 | cursor: pointer; 14 | color: @body-color; 15 | } 16 | 17 | .CodeBlock-activeTab a { 18 | color: @accent-color; 19 | } -------------------------------------------------------------------------------- /site/components/Footer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './Footer.less'; 3 | import Links from './Links'; 4 | 5 | export default class Footer { 6 | render() { 7 | 8 | return ( 9 |
    10 | 11 |
    12 | Diskutujte prostřednictvím issues.
    13 | Vylepšete článek pull requestem.
    14 | Sledujte na twitteru. {' '} 15 | RSS.
    16 | Sdílejte! 17 |
    18 |
    19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /site/components/Footer.less: -------------------------------------------------------------------------------- 1 | .Footer { 2 | text-align: center; 3 | padding-top: 3em; 4 | margin-bottom: 3em; 5 | } 6 | -------------------------------------------------------------------------------- /site/components/Header.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import NavBar from './NavBar'; 3 | import './Header.less'; 4 | 5 | export default class Header { 6 | render() { 7 | return ( 8 |
    9 | 10 |
    11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /site/components/Header.less: -------------------------------------------------------------------------------- 1 | .Header { 2 | overflow: hidden; //clearfix 3 | } 4 | -------------------------------------------------------------------------------- /site/components/Links.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './Links.less'; 3 | import {Articles} from '../Constants'; 4 | 5 | export default class Links { 6 | 7 | render() { 8 | return ( 9 |
    10 | {Articles[this.props.page].prev && 11 | 17 | } 18 | 19 | {Articles[this.props.page].next && 20 | 26 | } 27 |
    28 | ); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /site/components/Links.less: -------------------------------------------------------------------------------- 1 | .links { 2 | padding: 0px 1em 0px 1em; 3 | } -------------------------------------------------------------------------------- /site/components/NavBar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './NavBar.less'; 3 | 4 | export default class NavBar { 5 | render() { 6 | return ( 7 |
    8 |
    9 |
    10 | Úvod 11 | Twitter 12 | GitHub 13 | RSS 14 |
    15 |
    16 |
    17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /site/components/NavBar.less: -------------------------------------------------------------------------------- 1 | @import '../constants.less'; 2 | 3 | .NavBar { 4 | z-index: 3; 5 | top: 0; 6 | position: fixed; 7 | width: 100%; 8 | padding: 0em 3em 0em 3em; 9 | background-image: linear-gradient(45deg, rgba(44,83,158, 1), rgba(44,83,158, 0.75)); 10 | 11 | .NavBar-container { 12 | display: flex; 13 | justify-content: space-between; 14 | align-items: center; 15 | margin: 0 auto; 16 | height: @navbar-height; 17 | padding: @content-padding/2 0; 18 | float: right; 19 | } 20 | 21 | .NavBar-logo { 22 | color: @accent-color; 23 | line-height: 1.4; 24 | } 25 | 26 | .NavBar-logoTitle { 27 | font-weight: bold; 28 | font-size: 1.125em; 29 | } 30 | 31 | .NavBar-logoDescription { 32 | margin: 0; 33 | font-size: .875em; 34 | } 35 | 36 | .NavBar-link { 37 | color: #FFF; 38 | text-decoration: none; 39 | } 40 | 41 | .NavBar-link + .NavBar-link { 42 | margin-left: 1em; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /site/components/PageBody.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './PageBody.less'; 3 | 4 | export default class PageBody { 5 | render() { 6 | return ( 7 |
    8 |
    9 | {this.props.children} 10 |
    11 |
    12 | ); 13 | } 14 | } -------------------------------------------------------------------------------- /site/components/PageBody.less: -------------------------------------------------------------------------------- 1 | @import '../constants.less'; 2 | 3 | .PageBody { 4 | padding: @content-padding*4 @content-padding; 5 | background-color: #fff; 6 | 7 | .PageBody-container { 8 | display: flex; 9 | flex-direction: column; 10 | margin: 0 auto; 11 | max-width: @content-width; 12 | padding: 20px; 13 | 14 | @media only screen and (min-width: @screen-tablet) { 15 | flex-direction: row; 16 | } 17 | } 18 | } 19 | 20 | .PageBody.PageBody--hasSidebar { 21 | .PageBody-container { 22 | @media only screen and (min-width: @screen-tablet) { 23 | margin-left: @sidebar-width + @content-padding*2; 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /site/components/StaticHTMLBlock.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import CodeBlock from './CodeBlock'; 3 | 4 | export default class StaticHTMLBlock { 5 | static propTypes = { 6 | html: React.PropTypes.string.isRequired 7 | }; 8 | 9 | render() { 10 | 11 | // Here goes a really hack-ish way to convert 12 | // areas separated by Markdown
    s into code tabs. 13 | 14 | if (!this.props.html) { 15 | return null; 16 | } 17 | 18 | const blocks = this.props.html.split('$$$'); 19 | const elements = []; 20 | 21 | let es5Content = null; 22 | let es6Content = null; 23 | 24 | for (let i = 0; i < blocks.length; i++) { 25 | const content = blocks[i]; 26 | 27 | switch (i % 2) { 28 | case 0: 29 | elements.push( 30 |
    33 | ); 34 | break; 35 | case 1: 36 | elements.push( 37 |
    38 | {React.createElement( 39 | require('../../examples/react/' + content + '.js'), 40 | {key: content} 41 | )} 42 | 47 |
    48 | ); 49 | break; 50 | } 51 | } 52 | //elements.push(React.createElement(require('../../examples/react-uvod/counter'))); 53 | return ( 54 |
    55 | {elements} 56 |
    57 | ); 58 | } 59 | } -------------------------------------------------------------------------------- /site/constants.less: -------------------------------------------------------------------------------- 1 | @accent-color: #0074D9; 2 | 3 | @link-color: @accent-color; 4 | @header-color: #212325; 5 | @body-color: #626466; 6 | 7 | @navbar-height: 3em; 8 | 9 | @content-width: 42em; 10 | @content-padding: 0em; 11 | 12 | @sidebar-width: 0em; 13 | 14 | @screen-tablet: 700px; 15 | -------------------------------------------------------------------------------- /site/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tajo/javascript/5a85b5364ded3c47338144124ff5a24e1bf6e530/site/favicon.png -------------------------------------------------------------------------------- /site/feed.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | DžejEs - JavaScript pro web 11 | 12 | http://www.dzejes.cz 13 | Články o moderních webových aplikacích. 14 | cs-CZ 15 | hourly 16 | 1 17 | 18 | DžejEs 19 | http://www.dzejes.cz/logo.png 20 | http://www.dzejes.cz 21 | 22 | 23 | 24 | React - JSX 25 | http://www.dzejes.cz/react-jsx.html 26 | Mon, 13 July 2015 10:00:00 +0000 27 | Vojtěch Mikšů 28 | 29 | První věc, která nováčka v Reactu rozhodně praští do očí, je zápis XML (HTML) tagů přímo do JavaScriptového kódu bez apostrofů kolem. Takovýto zápis se ve světě Reactu nazývá JSX a ano, není to validní JS syntax. Před spuštěním se musí do JS kompilovat. Dnes si ukážeme, co je pod pokličkou JSX a že se vlastně není čeho bát. 30 | 31 | 32 | 33 | 34 | React - Props vs State 35 | http://www.dzejes.cz/react-props-vs-state.html 36 | Thu, 9 July 2015 12:24:00 +0000 37 | Vojtěch Mikšů 38 | 39 | React se točí kolem props a tak je nejvyšší čas si ukázat, co jsou zač. Minule jsem už naznačil, že jde o druhý zdroj dat, který React komponenty pro své vykreslení používají. Povíme si, jak se props liší od state a jakým způsobem se používají. To vše si demonstrujeme na pokročilejším příkladě, který se bude skládat z více komponent. Na nich uvidíte i to, jakým způsobem se komponenty do sebe skládají a jak spolu komunikují. Dnes to bude o úplných základech Reactu. 40 | 41 | 42 | 43 | 44 | React - Úvod 45 | http://www.dzejes.cz/react-uvod.html 46 | Mon, 9 May 2016 14:24:00 +0000 47 | Vojtěch Mikšů 48 | 49 | React je JavaScriptová knihovna pro vytváření webových komponent. V pomyslném MVC představuje "V" neboli view vrstvu a dal by se tak přirovnat například Latte v Nette. React je však daleko více. Přináší totiž zásadní změnu paradigma. S Reactem už nepíšeme kód, který něco mění, ale kód, který popisuje, jak má vypadat výsledek, což je řádově snažší úloha. Dvojnásob to pak platí, pokud tím výsledkem je "těžká váha" v podobě DOMu. 50 | 51 | 52 | 53 | 54 | První dev stack 55 | http://www.dzejes.cz/prvni-dev-stack.html 56 | Tue, 10 May 2016 12:00:00 +0000 57 | Vojtěch Mikšů 58 | 59 | Hlavním hrdinou dnešního článku se stane bundlovací nástroj webpack. Lecos jste se o něm mohli dozvědět už v minulém článku. S jeho pomocí postavíme první jednoduchou "Hello world" aplikaci v Reactu, která bude například používat Babel (JavaScript budoucnosti) a LESS (CSS preprocesor). Ukážeme si sílu moderních JS nástrojů v praxi. 60 | 61 | 62 | 63 | 64 | Nástroje 65 | http://www.dzejes.cz/nastroje.html 66 | Sun, 7 June 2015 12:00:00 +0000 67 | Vojtěch Mikšů 68 | 69 | Tak jako truhlář potřebuje hoblík, pilu nebo nebozez, tak i vývojář webovek se neobejde bez pořádných nástrojů. Když si vyberete ty správné a naučíte se s nimi pořádně zacházet, značně usnadní a zrychlí vaši práci. V tomto článku se s nimi v rychlosti seznámíme. 70 | 71 | 72 | 73 | 74 | JavaScript? Babel. 75 | http://www.dzejes.cz/babel.html 76 | Mon, 9 May 2016 12:00:00 +0000 77 | Vojtěch Mikšů 78 | 79 | JavaScript má oproti většině ostatních jazyků jedno specifikum. Je sám o sobě poměrně nepoužitelný. Na vině je především způsob, jakým dochází k uvádění jeho nových verzí. Jak si s tím úspěšně a elegantně poradit? 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /site/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tajo/javascript/5a85b5364ded3c47338144124ff5a24e1bf6e530/site/logo.png -------------------------------------------------------------------------------- /site/pages/ArticlePage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PageBody from '../components/PageBody'; 3 | import StaticHTMLBlock from '../components/StaticHTMLBlock'; 4 | import Header from '../components/Header'; 5 | import Footer from '../components/Footer'; 6 | import Links from '../components/Links'; 7 | 8 | export default class ArticlePage { 9 | render() { 10 | return ( 11 |
    12 |
    13 | 14 | 15 | 16 |
    17 |
    18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /site/pages/HomePage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PageBody from '../components/PageBody'; 3 | import StaticHTMLBlock from '../components/StaticHTMLBlock'; 4 | import IndexHTML from '../../content/index.md'; 5 | 6 | export default class HomePage { 7 | render() { 8 | return ( 9 |
    10 |
    11 |

    O moderních webových aplikacích

    12 |

    #javascript #react #redux #immutable #nodejs #webpack

    13 |
    14 |
    DžejEs
    15 |
    16 | 17 | 18 | 19 |
    20 | ); 21 | } 22 | } -------------------------------------------------------------------------------- /site/renderPath.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import IndexPage from './IndexPage'; 3 | 4 | export default function renderPath(path, props, onRender) { 5 | onRender( 6 | IndexPage.renderToString(props) 7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /site/webpack-client.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require('webpack'); 3 | var resolvers = require('../scripts/resolvers'); 4 | var ExtractTextPlugin = require('extract-text-webpack-plugin'); 5 | 6 | var isDev = process.env.NODE_ENV !== 'production'; 7 | 8 | module.exports = { 9 | 10 | devtool: isDev ? 'cheap-eval-source-map' : 'source-map', 11 | 12 | entry: [ 13 | path.join(__dirname, 'client.js') 14 | ].concat(isDev ? [ 15 | 'webpack-dev-server/client?http://localhost:8080', 16 | 'webpack/hot/only-dev-server' 17 | ] : []), 18 | 19 | output: { 20 | path: '__site__/', 21 | filename: isDev ? '[name].js' : '[name]-[hash].js', 22 | publicPath: '' 23 | }, 24 | 25 | target: 'web', 26 | 27 | module: { 28 | loaders: [ 29 | { 30 | test: /\.md$/, 31 | loader: [ 32 | 'html?{"minimize":false}', 33 | path.join(__dirname, '../scripts/markdownLoader') 34 | ].join('!') 35 | }, 36 | { 37 | test: /\.js$/, 38 | exclude: /node_modules/, 39 | loaders: isDev ? ['react-hot-loader', 'babel-loader'] : ['babel-loader'] 40 | }, 41 | { 42 | test: /\.json$/, 43 | exclude: /node_modules/, 44 | loader: 'json-loader' 45 | }, 46 | { 47 | test: /\.less$/, 48 | loader: ExtractTextPlugin.extract( 49 | 'style-loader', 50 | [ 51 | 'css-loader', 52 | path.join(__dirname, '../scripts/cssTransformLoader'), 53 | 'less-loader' 54 | ].join('!') 55 | ) 56 | }, 57 | { 58 | test: /\.(png|jpg|gif)$/, 59 | loader: 'file-loader', 60 | query: { name: 'images/[name]-[hash].[ext]' } 61 | } 62 | ] 63 | }, 64 | 65 | resolve: { 66 | alias: { 67 | 'react-dnd/modules': path.join(__dirname, '../src'), 68 | 'react-dnd': path.join(__dirname, '../src') 69 | } 70 | }, 71 | 72 | plugins: [ 73 | new ExtractTextPlugin( 74 | isDev ? '[name].css' : '[name]-[hash].css' 75 | ), 76 | new webpack.optimize.OccurenceOrderPlugin(), 77 | new webpack.DefinePlugin({ 78 | 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV), 79 | '__DEV__': JSON.stringify(isDev || true) 80 | }), 81 | resolvers.resolveHasteDefines 82 | ] 83 | }; 84 | 85 | if (process.env.NODE_ENV === 'production') { 86 | module.exports.plugins.push( 87 | new webpack.optimize.UglifyJsPlugin({ 88 | compressor: { 89 | warnings: false 90 | } 91 | }) 92 | ); 93 | } 94 | -------------------------------------------------------------------------------- /site/webpack-prerender.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require('webpack'); 3 | var resolvers = require('../scripts/resolvers'); 4 | 5 | var isDev = process.env.NODE_ENV !== 'production'; 6 | 7 | module.exports = { 8 | entry: path.join(__dirname, 'renderPath.js'), 9 | 10 | output: { 11 | path: '__site_prerender__/', 12 | filename: 'renderPath.js', 13 | libraryTarget: 'commonjs2', 14 | }, 15 | 16 | target: 'node', 17 | 18 | module: { 19 | loaders: [ 20 | { 21 | test: /\.md$/, 22 | loader: [ 23 | 'html?{"minimize":false}', 24 | path.join(__dirname, '../scripts/markdownLoader') 25 | ].join('!') 26 | }, 27 | { 28 | test: /\.js$/, 29 | loader: 'babel-loader', 30 | exclude: /node_modules/ 31 | }, 32 | { 33 | test: /\.json$/, 34 | exclude: /node_modules/, 35 | loader: 'json-loader' 36 | }, 37 | { 38 | test: /\.css$/, 39 | loader: 'null-loader' 40 | }, 41 | { 42 | test: /\.less$/, 43 | loader: 'null-loader' 44 | }, 45 | { 46 | test: /\.(png|jpg|gif)$/, 47 | loader: 'file-loader', 48 | query: { name: 'images/[name]-[hash].[ext]' } 49 | } 50 | ] 51 | }, 52 | 53 | resolve: { 54 | alias: { 55 | 'react-dnd/modules': path.join(__dirname, '../src'), 56 | 'react-dnd': path.join(__dirname, '../src') 57 | } 58 | }, 59 | 60 | plugins: [ 61 | new webpack.optimize.OccurenceOrderPlugin(), 62 | new webpack.DefinePlugin({ 63 | 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV), 64 | '__DEV__': JSON.stringify(isDev || true) 65 | }), 66 | resolvers.resolveHasteDefines 67 | ] 68 | }; 69 | 70 | if (process.env.NODE_ENV === 'production') { 71 | module.exports.plugins.push( 72 | new webpack.optimize.UglifyJsPlugin({ 73 | compressor: { 74 | warnings: false 75 | } 76 | }) 77 | ); 78 | } 79 | --------------------------------------------------------------------------------