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

Rails Frontend: 2016 edition.

Rails Frontend: 2016 edition.

Slides from talk about enhancements in Rails Frontend.

vipulnsward
PRO

June 24, 2016
Tweet

More Decks by vipulnsward

Other Decks in Programming

Transcript

  1. View Slide

  2. RAILS FRONTEND: 2016
    and That Sprockets Talk

    View Slide

  3. Vipul A M
    @vipulnsward

    View Slide

  4. View Slide

  5. timezones
    1.Mumbai TimeZone (UTC+05:30)
    2.Taiwan TimeZone (UTC+08:00)
    3.Pacific Time Zone (UTC-08:00)
    4.Singapore Time Zone (UTC+08:00)

    View Slide

  6. timezones

    View Slide

  7. timezones

    View Slide

  8. timezones
    +

    View Slide

  9. timezones
    +=

    View Slide

  10. View Slide

  11. View Slide

  12. View Slide

  13. View Slide

  14. View Slide

  15. View Slide

  16. View Slide

  17. Rails 5

    View Slide

  18. http://blog.bigbinary.com/categories/Rails-5
    Rails 5 Blog Series (55+ and counting)

    View Slide

  19. Sprockets: *Spotlight

    View Slide

  20. Sprockets
    https://speakerdeck.com/rafaelfranca/how-sprockets-works

    View Slide

  21. Sprockets
    1. sprockets
    2. sprockets-rails
    3. sass-rails
    4. execjs
    5. coffee-rails

    View Slide

  22. manifest.js
    # In Rails configuration

    config.assets.precompile += [“payment.css”,
    “advertising.css"]
    <%= stylesheet_link_tag "advertising" %>

    View Slide

  23. manifest.js
    config.assets.precompile = [“manifest.js”]
    // app/assets/config/manifest.js
    //= link_tree ../images

    //= link_directory ../stylesheets .css
    //= link application.css

    //= link advertising.css

    //= link payment.css

    //= link application.js


    View Slide

  24. manifest.js
    <%= stylesheet_link_tag "advertising" %>
    .logo {

    background: url(<%= asset_url("logo.png") %>)

    }

    View Slide

  25. folders as modules
    foo/index.js
    <%= asset_path("foo/index.js") %>

    <%= asset_path("foo.js") %>

    View Slide

  26. folders as modules
    //= require_tree .
    foo/jquery-ui.js and foo/jquery.min.js
    Now:
    //= require foo.js
    # foo/index.js
    //= require foo.min.js

    //= require foo-ui.js


    View Slide

  27. sourcemaps.js

    View Slide

  28. sourcemaps.js

    View Slide

  29. sourcemaps.js
    //# sourceMappingURL=
    File: public/assets/application.js
    Map: public/assets/application.js.map
    //# sourceMappingURL=/assets/application.js.map
    /*# sourceMappingURL=application.css.map */

    View Slide

  30. sourcemaps.js

    View Slide

  31. sourcemaps.js

    View Slide

  32. ES2016!

    View Slide

  33. ES2016!
    1. Arrows and Lexical This
    2. Classes
    3. Enhanced Object Literals
    4. Template Strings
    5. Destructuring
    6. Default + Rest + Spread
    7. Let + Const
    8. Iterators + For..Of
    9. Symbols, etc

    View Slide

  34. ES2016!

    View Slide

  35. ES2016!

    View Slide

  36. ES2016!

    View Slide

  37. 12x
    @schneems
    https://engineering.heroku.com/blogs/2016-02-18-
    speeding-up-sprockets/

    View Slide

  38. 1.raw asset
    2.processed asset
    3.bundled
    4.compressed
    5.asset fingerprint
    6.minified
    7.etc
    12x

    View Slide

  39. # https://github.com/rails/sprockets/blob/543a5a27190c26de8f3a1b03e18aed8da0367c63/lib/sprockets/base.rb#L46-L57


    def file_digest(path)

    if stat = File.stat(path)

    cache.fetch("file_digest:#{path}:#{stat.mtime.to_i}")
    do

    Digest::SHA256.file(path.to_s).digest

    end

    end

    end
    12x

    View Slide

  40. “file_digest:/Users/schneems/my_project/app/assets/javascripts/application.js:
    123456”
    "file_digest:/+Other/path/+my_project/app/assets/javascripts/application.js:
    123456"
    12x

    View Slide

  41. # https://github.com/rails/sprockets/blob/9ca80fe00971d45ccfacb6414c73d5ffad96275f/lib/sprockets/loader.rb#L55-L58
    digest =
    DigestUtils.digest(resolve_dependencies(paths))

    if uri_from_cache =
    cache.get(unloaded.digest_key(digest), true)

    asset_from_cache(UnloadedAsset.new(uri_from_cache,
    self).asset_key)

    end
    12x!

    View Slide

  42. <%= javascript_include_tag :application,
    integrity: true %>

    src="/assets/application.js"
    integrity=“sha256-
    TvVUHzSfftWg1rcfL6TIJ0XKEGrgLyEq6lEpcmrG9qs=">

    SRI

    View Slide

  43. *Headers

    View Slide

  44. *Header

    View Slide

  45. ETag

    View Slide

  46. class ItemsController < ApplicationController

    def show

    @item = Item.find(params[:id])

    fresh_when @item

    end

    end
    Etag

    View Slide

  47. $ curl -i http://localhost:3000/items/1


    HTTP/1.1 200 OK

    X-Frame-Options: SAMEORIGIN

    X-Xss-Protection: 1; mode=block

    X-Content-Type-Options: nosniff

    Etag: "618bbc92e2d35ea1945008b42799b0e7"

    Last-Modified: Sat, 30 Jan 2016 08:02:12 GMT

    Content-Type: text/html; charset=utf-8

    Cache-Control: max-age=0, private, must-revalidate

    X-Request-Id: 98359119-14ae-4e4e-8174-708abbc3fd4b

    X-Runtime: 0.412232

    Server: WEBrick/1.3.1 (Ruby/2.2.2/2015-04-13)

    Date: Fri, 04 Mar 2016 10:50:38 GMT

    Content-Length: 1014

    Connection: Keep-Alive
    Etag

    View Slide

  48. $ curl -i -H 'If-None-Match: "618bbc92e2d35ea1945008b42799b0e7"' http://
    localhost:3000/items/1


    HTTP/1.1 304 Not Modified

    X-Frame-Options: SAMEORIGIN

    X-Xss-Protection: 1; mode=block

    X-Content-Type-Options: nosniff

    Etag: "618bbc92e2d35ea1945008b42799b0e7"

    Last-Modified: Sat, 30 Jan 2016 08:02:12 GMT

    Cache-Control: max-age=0, private, must-revalidate

    X-Request-Id: e4447f82-b96c-4482-a5ff-4f5003910c18

    X-Runtime: 0.012878

    Server: WEBrick/1.3.1 (Ruby/2.2.2/2015-04-13)

    Date: Fri, 04 Mar 2016 10:51:22 GMT

    Connection: Keep-Alive
    Etag

    View Slide

  49. Strong Etag: ”543b39c23d8d34c232b457297d38ad99"
    Weak ETag: W/“543b39c23d8d34c232b457297d38ad99"
    Etag

    View Slide

  50. # app/controllers/home_controller.rb

    class HomeController < ApplicationController

    def index

    render

    end

    end


    # app/views/home/index.html.erb

    Welcome
    http_cache_forever

    View Slide

  51. Processing by HomeController#index as HTML

    Rendered home/index.html.erb within layouts/
    application (1.3ms)

    Completed 200 OK in 224ms (Views: 212.4ms |
    ActiveRecord: 0.0ms)


    And so on for every request for this action.

    http_cache_forever

    View Slide

  52. # app/controllers/home_controller.rb

    class HomeController < ApplicationController

    def index

    http_cache_forever(public: true) {}

    end

    end


    # OR

    class HomeController < ApplicationController

    def index

    http_cache_forever(public: true) do

    render

    end

    end

    end



    # app/views/home/index.html.erb

    Welcome
    http_cache_forever

    View Slide

  53. # When request is made for the first time.


    Processing by HomeController#index as HTML

    Rendered home/index.html.erb within layouts/
    application (1.3ms)

    Completed 200 OK in 224ms (Views: 212.4ms |
    ActiveRecord: 0.0ms)


    # For consecutive requests for the same page


    Processing by HomeController#index as HTML

    Completed 304 Not Modified in 2ms (ActiveRecord:
    0.0ms)
    http_cache_forever

    View Slide


  54. Cache-Control: max-age=3155760000
    http_cache_forever

    View Slide

  55. Asset Headers

    View Slide

  56. response.headers['X-Tracking-ID'] = '123456'
    Headers in Simple Responses

    View Slide

  57. # open config/environments/production.rb and add following line

    config.static_cache_control = 'public, max-age=1000'
    Asset Headers

    View Slide

  58. PageSpeed Warnings

    View Slide

  59. Missing Expires header

    View Slide

  60. # production.rb


    config.public_file_server.headers = {

    'Cache-Control' => 'public, s-maxage=31536000,
    maxage=15552000',

    'Expires' =>
    "#{1.year.from_now.to_formatted_s(:rfc822)}"

    }
    Public Asset Header

    View Slide

  61. Result

    View Slide

  62. Caching

    View Slide

  63. 1.Page Caching
    2.Action Caching
    3.Fragment Caching
    4.Russian Doll Caching
    5.Low-Level Caching
    6.SQL Caching
    http://edgeguides.rubyonrails.org/
    caching_with_rails.html
    Caching

    View Slide

  64. # index.html.erb

    <%= render partial: 'todo', collection: @todos %>


    # _todo.html.erb

    <% cache todo do %>

    <%= todo.name %>

    <% end %>

    Partial rendering from cache

    View Slide

  65. # index.html.erb

    <%= render partial: 'todo', collection: @todos,
    cached: true %>


    # _todo.html.erb

    <% cache todo do %>

    <%= todo.name %>

    <% end %>
    multi_fetch_fragments

    View Slide

  66. cached: true => read_multi
    Rendered collection of todos/_todo.html.erb [100 /
    100 cache hits] (339.5ms)
    multi_fetch_fragments

    View Slide

  67. 72%
    read_multi

    View Slide

  68. config.action_mailer.perform_caching = true
    ActionMailer Fragment Caching

    View Slide



  69. <% cache 'signup-text' do %>

    Welcome to <%= @company.name %>

    You have successfully signed up to <%=
    @company.name %>, Your username is:

    <% end %>


    <%= @user.login %>.





    <%= render :partial => 'footer' %>



    ActionMailer Fragment Caching

    View Slide

  70. Cache digest for app/views/user_mailer/_footer.erb:
    7313427d26cc1f701b1e0212498cee38

    Cache digest for app/views/user_mailer/welcome_email.html.erb:
    30efff0173fd5f29a88ffe79a9eab617

    Rendered user_mailer/_footer.erb (0.3ms)

    Rendered user_mailer/welcome_email.html.erb (26.1ms)

    Cache digest for app/views/user_mailer/welcome_email.text.erb:
    77f41fe6159c5736ab2026a44bc8de55

    Rendered user_mailer/welcome_email.text.erb (0.2ms)

    UserMailer#welcome_email: processed outbound mail in 190.3ms
    ActionMailer Fragment Caching

    View Slide

  71. ActiveRecord::Relation#cache_key
    Caching result sets and collection

    View Slide

  72. @users = User.where(city: 'miami')
    1.The query statement doesn’t change. If we change
    city name from “Miami” to “Boston” then result might
    change.
    2.No record is deleted.
    3.No record is added.
    Caching result sets and collection

    View Slide

  73. @users = User.where(city: 'Miami')

    @users.cache_key

    => "users/query-67ed32b36805c4b1ec1948b4eef8d58f-3-20160116111659084027"
    # users.html.erb
    cache @users do 

    #content here

    end
    Caching result sets and collection

    View Slide

  74. *Layouts and Tags.

    View Slide

  75. async-defer
    www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html

    View Slide

  76. simple <br/>

    View Slide

  77. <br/>

    View Slide

  78. <br/>

    View Slide

  79. Defaults

    View Slide

  80. Pagespeed

    View Slide

  81. 1.Placement of JS/CSS imports
    2.Font imports
    3.Analytics tags
    4.Proper viewports
    5.responsive
    6.Inlining resources, css/js, etc
    PageSpeed

    View Slide






  82. <%= @page_title || default_page_title %> | BigBinary


    <%= stylesheet_link_tag "application.css", 'data-turbolinks-
    track' => true %>

    …

    <%= csrf_meta_tags %>



    <%= yield %>


    try{Typekit.load({ async: true });}catch(e){}


    <%= javascript_include_tag "application.js", media: 'all',
    'data-turbolinks-track' => true, async: true, defer: true %>




    View Slide

  83. View Slide

  84. Fin.

    View Slide

  85. Fin.

    View Slide

  86. Images: flickr.com/search/?
    text=singapore&license=2%2C3%2C4%2C5%2C6%2C9
    http://blog.bigbinary.com/categories/Rails%205
    https://www.packtpub.com/web-development/reactjs-example-building-
    modern-web-applications-react
    http://www.growingwiththeweb.com/2014/02/async-vs-defer-
    attributes.html
    https://speakerdeck.com/rafaelfranca/how-sprockets-works
    https://thenounproject.com/search/?q=sprocket&i=283880
    References

    View Slide