Upgrade to Pro — share decks privately, control downloads, hide ads and more …

From Hacker to Developer

From Hacker to Developer

Developing with Webpack, Babel, and React

Joseph Chiang

October 01, 2015
Tweet

More Decks by Joseph Chiang

Other Decks in Technology

Transcript

  1. Tag Soup in JS if (inlineTileOptions['show_sharing'] === "1") { //

    Inline Sharing Buttons var tileSharing = $('<div class="tile-sharing"><a class="tile-sharing-expand js-expand-tile- sharing stackla-icon-share" onclick="Widget.expandInlineSharingTool(th var tileShareButtons = $('<ul class="tile-sharing-expanded"></ul>'); tileSharing.append(tileShareButtons); item.append(tileSharing); ! var shareText = t['share_text'] ? t['share_text'].replace(/["']/g, '\\$&') : ''; ! if (t['source'] != 'facebook') { tileShareButtons.append('<li><a data-tile-id="' + t['_id'] + '" class="tile-share-button facebook" title="Share via Facebook" onclick="Widget.facebookShare(th } ! tileShareButtons.append('<li><a data-tile-id="' + t['_id'] + '" class="tile-share-button twitter" title="Share via Twitter" onclick="Widget.twitterShare(this)"><d ! if (t['image']) { tileShareButtons.append('<li><a data-tile-id="' + t['_id'] + '" class="tile-share-button pinterest" title="Share via Pinterest" onclick="Widget.pinterestShare } ! tileShareButtons.append('<li><a data-tile-id="' + t['_id'] + '" class="tile-share-button gplus" title="Share" onclick="Widget.googleShare(this)"><div class="share ! tileShareButtons.append('<li><a data-tile-id="' + t['_id'] + '" class="tile-share-button email" title="Email a friend" onclick="Widget.emailShare(this)"><div clas ! ! } Difficult to read and maintain
  2. Copy Paste fluid.js fluid-horizontal.js base_waterfall.js base_carousel.js auto.js main.js carousel.js slideshow.js

    base_billboard.js base_feed.js base_slideshow.js 80% duplicated code (~1500 lines) * 11 Widget Code Once we start, it’s difficult to refactor
  3. Global Variables Global variables and function names are an incredibly

    bad idea. The reason is that every JavaScript file included in the page runs in the same scope. If you have global variables or functions in your code, scripts included after yours that contain the same variable and function names will overwrite your variables/functions.
  4. Stoyan Stefanov The few man-hours spent writing the code initially

    end up in man-weeks spent reading it. Getting Worse
  5. Eliminate the hacking As a developer, we always seek for

    good methodologies, patterns, or better technologies to make our code to be maintained easier. “A bug could be fixed within 10 mins by a newbie, but a good developer is willing spend 4 hours to make it better.”
  6. Universal loader for all static assets Ability to write next-generation

    JS Lead and change the world of front-end app development Foundation Accelerator App New Web Development Stacks
  7. JS Module Before Webpack AMD <script/> Make use of global

    variables Ease of define dependencies No dependencies Load modules asynchronously No compile needed by default Ease of define dependencies Always need to compile Synchronous loading by a single file (via global) define(['./a', './b'], function (a, b) {! }); var Acl = require('js/acl/acl.js');
  8. Package Management CSS Preproccessors JS Module Loaders JS Transpiler Build

    Tools less-loader sass-loader coffee-loader ts-loader Support ALL JS STATIC 
 Modules Webpack Dev Server
  9. // app/media/js/admin/terms/term-manage.jsx! ! 'use strict';! ! var React = require('react'),!

    $ = require('jquery'),! LogMixin = require('common/log-mixin'),! TermPanel = require('./term-panel'),! TermList = require('./term-list'),! History = require('exports?window.History!history.js/native.history'),! queryString = require('query-string'),! Cookies = require('js-cookie');! ! module.exports = {! // ...! }; Define Dependencies app/vendor/webpack/node_modules/ app/media/js/ app/media/components/
  10. var History = require('exports?window.History!history.js/native.history');! ! History.Adapter.bind(window, 'statechange', that.handleHistoryChange); Global Module

    require('history.js/native.history');! ! window.History.Adapter.bind(window, 'statechange', that.handleHistoryChange); Not Good Better exports?window.History!history.js/native.history loader loader options (query) separator module path
  11. CSS Preprocessors Wait! Are you writing CSS in JS? require('!style!css!sass!admin/acl/acl.scss');

    app/media/js/admin/acl/acl.js • The stylesheet will be injected into HTML with <style> tag • The images inside will be converted to Data URIs. Define dependencies between JS and CSS
  12. Code Splitting fluid-embed.js
 (required) when layout = portrait or landscape

    Load extra assets conditionally lightbox.js
 (optional)
  13. • Module Loader / Dependencies • Ability to load all

    kinds of assets • High performance • Often replaces grunt or gulp
  14. Package Management CSS Preproccessors JS Module Loaders JS Transpiler Build

    Tools sass-loader coffee-loader ts-loader Support ALL Modules Webpack Dev Server babel-loader less-loader
  15. Why Babel? All transpilers are trying to *fix* JavaScript language

    These transpilers have obvious influence on the ES6/ES2015 standard. However browser support is not ready yet
  16. Why Babel? Babel is another transpiler which let you to

    use latest JS Babel keeps sync with ECMAScript Ability to customise the stage or options you want use.
  17. var: Function-scoped Let & Const function f() {! var obj

    = {a: 1, b: 2, c: 3}; ! for (var i in obj) {! // okay! console.log(i); ! } ! // okay ! console.log(i);! }! f(); let: Block-scoped function f() {! var obj = {a: 1, b: 2, c: 3}; ! for (let i in obj) {! // okay! console.log(i); ! } ! // error - i is undefined ! console.log(i);! }! f(); const: Block-scoped function a() {! const DURATION = 100;! {! // output 100! console.log(DURATION);! }! }! ! function b() {! const DURATION = 100;! {! // error "DURATION" is read-only! DURATION = 200;! } ! }! ! function c() {! const DURATION = 100;! {! // ok ! const DURATION = 200;! console.log(DURATION); // output 200! } ! }! Single-assigment
  18. `Template String` // This is what I prefer to do!

    var list = ['<ul>',! ' <li>Buy Milk</li>',! ' <li>Be kind to Pandas</li>',! ' <li>Forget about Dre</li>',! '</ul>'].join(''); ES5 // This doesn't work! var list = '<ul>! <li>Buy Milk</li>! <li>Be kind to Pandas</li>! <li>Forget about Dre</li>! </ul>';! // This does, but urgh… ! var list = '<ul>\! <li>Buy Milk</li>\! <li>Be kind to Pandas</li>\! <li>Forget about Dre</li>\! </ul>'; // This is the most common way, and urgh, too…! var list = '<ul>' +! ' <li>Buy Milk</li>' +! ' <li>Be kind to Pandas</li>' +! ' <li>Forget about Dre</li>' +! '</ul>'; ES6 // This works!! var list = `<ul>! <li>Buy Milk</li>! <li>Be kind to Pandas</li>! <li>Forget about Dre</li>! </ul>`;! http://christianheilmann.com/2015/08/28/es6-for-now-template-strings/
  19. `Template String` ES5 - String + Variables var avatar =

    '<div class="avatar">' +! ' <a href="' + avatarLink + '" target="_blank" class="avatar-profile">' +! ' <div class="avatar-place-holder">' +! ' <img src="/media/images/avatar-mask.png" alt="' + avatarAltText + '" />' +! ' </div>' +! ' <img src="' + getAvatarUrl(t.avatar, t.source) + '" alt="' + avatarAltText + '" />' +! ' </a>' +! '</div>'; var avatar = `<div class="avatar">! <a href="${avatarLink}" target="_blank" class="avatar-profile">! <div class="avatar-place-holder">! <img src="/media/images/avatar-mask.png" alt="${avatarAltText}" />! </div>! <img src="${getAvatarUrl(t.avatar, t.source)}" alt="${avatarAltText}" />! </a>! </div>`; ES6 - String + Variables The ${} construct can take any JS expression that returns a value
  20. class Widget extends Stackla.Base {! constructor(options) {! super(options);! ! this.domain

    = options.domain || null;! this.list_url = `/widget/api/${this.guid}! ?filter_id=${this.filter_id}! &domain=${this.domain}! &page=1! &ttl=1`; ! }! render(el) {! super.render();! }! static getInstances() {! // ...! }! } Classes ES6 - Single convenient declarative form
  21. Summary • Unlike CoffeeScript, it’s something you must learn and

    use in the future. • Just like PHP4 v.s. PHP5 • Makes my code more legible (ex. class) • develop much faster (ex. less vague, type safety)
  22. • Rethink best practices • JSX - Template in JavaScript?

    • CSS in JS - Inline style in JavaScript? • Easy to learn • Embracing Functional Programming • One-way data flow model • Impacting different areas • ImmutableJS, GraphQL Why so Popular?
  23. Package Management CSS Preproccessors JS Module Loaders JS Transpiler Build

    Tools sass-loader Support ALL Modules Webpack Dev Server babel-loader less-loader built-in support for React JSX
  24. Abstraction <button class="st-btn ! st-btn-danger ! st-btn-solid ! st-btn-md”>! Delete!

    </button> <Button kind="danger" ! size="medium" ! solid>Delete</Button> Button New Way Using Class Names Using React.js Old Way Legible & Less Guessing
  25. f <Button kind="danger" size="large" solid>Delete</Button> button.jsx exports default class Button

    extends React.Component {! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! }; this.props.size this.props.kind this.props.solid this.props.children Load CSS Just CSS class name manipulation render() is the only required method in React component import React from 'react';! import cx from 'classnames';! import 'stackla-uikit/scss/_button'; render() {! let that = this,! props = that.props,! className,! node;! ! className = cx('st-btn',`st-btn-${kind}`, {! 'st-btn-disabled': props.disabled,! 'st-btn-solid': props.solid,! 'st-btn-md': (props.size === 'medium'),! 'st-btn-sm': (props.size === 'small'),! 'st-active': props.active,! }, props.className);! ! return <button {...props} className={className}>{props.children}</button>! } The calculated class names Properties
  26. f State import React from 'react';! import Button from './button';!

    const data = [! {text: 'Danger', kind: 'danger', size: 'large', solid: false, active: true},! {text: 'Success', kind: 'success', size: 'medium', disabled: true, solid: true},! {text: 'Info', kind: 'info', disabled: true, solid: true},! {text: 'Default', kind: 'default', size: 'large', disabled: true, active: false}! ];! exports default class App extends React.Component {! constructor() {! this.offset = 0;! }! componentDidMount() {! setInterval(() => {! this.offset = (data.length >= this.offset + 1) ? 0 : this.offset + 1;! this.setState(data[this.offset]);! }, 500);! }! render() {! let state = this.state;! return ! <Button ! kind={state.kind} ! size={state.size} ! solid={state.solid} ! active={state.active}>! {state.text}! </Button>;! }! }; app.jsx React.render(<App>, document.getElementById('app')); Mockup Data Whenever the state changes, the render() will be invoked For Button component, whenever my property changes, the Button#render() will be invoked
  27. <StyleChooser/> <StylePreview/> <Breadcrumbs/> <Panel/> <WidgetsNew/> <Button/> <Button/> Only outmost component

    is stateful Others are stateless, only accept properties WidgetsNew StyleChooser StylePreview widgetTypes selectedWidget render() render() render() <StyleChooser widgetTypes= /> widgetTypes <StyleChooser selectedWidget= /> selectedWidget Top-down Rendering JSX diffs and apply changes to DOM
  28. A Tool to Build Our Toolbox Only ourselves knows what

    we want, picking up technologies for right abstraction is crucial. No single library can fulfil our need. Can we build an app without a single DOM element?
  29. Abstraction for Different Targets • React.js in Browser • JSX

    ➡ DOM • react-canvas in Browser • JSX ➡ Canvas • react-isomorphics in Server • JSX ➡ HTML • react-native in iOS • JSX ➡ UIKit • react-native in Android • JSX ➡ android.view
  30. Ambitious Abstraction In Browser, React/JSX relies on slow DOM API.

    Goal Ask WebGL/GPU to use React directly Now
  31. ➡ ➡ ➡ ➡ ➡ Getting Better & Mature ➡

    ➡ ➡ ! ! ! Will these tools go out of fashion in the following 18 months? POSSIBLY! However, everything is becoming 
 more mature and future-compatible We developers just need to choose right tools to do improvements
  32. Feel blessed with I feel much more solid to write

    UI now. Not a hacker but a programmer! $IFFST