mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Add preload_link_tag helper.
This helper creates a link tag with preload keyword that allows to
browser to initiate early fetch of resources. Additionally this send
Early Hints if supported.
See 59a02fb7bc
for more details about Early Hints.
Preload spec: https://w3c.github.io/preload/
This commit is contained in:
parent
055493ce05
commit
eb90b8bc86
3 changed files with 98 additions and 0 deletions
|
@ -1,3 +1,11 @@
|
|||
* Add `preload_link_tag` helper
|
||||
|
||||
This helper that allows to the browser to initiate early fetch of resources
|
||||
(different to the specified in javascript_include_tag and stylesheet_link_tag).
|
||||
Additionally, this sends Early Hints if supported by browser.
|
||||
|
||||
*Guillermo Iguaran*
|
||||
|
||||
## Rails 5.2.0.beta2 (November 28, 2017) ##
|
||||
|
||||
* No changes.
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
require "active_support/core_ext/array/extract_options"
|
||||
require "active_support/core_ext/hash/keys"
|
||||
require "active_support/core_ext/object/inclusion"
|
||||
require "active_support/core_ext/object/try"
|
||||
require "action_view/helpers/asset_url_helper"
|
||||
require "action_view/helpers/tag_helper"
|
||||
|
||||
|
@ -221,6 +223,67 @@ module ActionView
|
|||
}.merge!(options.symbolize_keys))
|
||||
end
|
||||
|
||||
# Returns a link tag that browsers can use to preload the +source+.
|
||||
# The +source+ can be the path of an resource managed by asset pipeline,
|
||||
# a full path or an URI.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:type</tt> - Override the auto-generated mime type, defaults to the mime type for +source+ extension.
|
||||
# * <tt>:as</tt> - Override the auto-generated value for as attribute, calculated using +source+ extension and mime type.
|
||||
# * <tt>:crossorigin</tt> - Specify the crossorigin attribute, required to load cross-origin resources.
|
||||
# * <tt>:nopush</tt> - Specify if the use of server push is not desired for the resource. Defaults to +false+.
|
||||
#
|
||||
# ==== Examples
|
||||
#
|
||||
# preload_link_tag("custom_theme.css")
|
||||
# # => <link rel="preload" href="/assets/custom_theme.css" as="style" type="text/css" />
|
||||
#
|
||||
# preload_link_tag("/videos/video.webm")
|
||||
# # => <link rel="preload" href="/videos/video.mp4" as="video" type="video/webm" />
|
||||
#
|
||||
# preload_link_tag(post_path(format: :json), as: "fetch")
|
||||
# # => <link rel="preload" href="/posts.json" as="fetch" type="application/json" />
|
||||
#
|
||||
# preload_link_tag("worker.js", as: "worker")
|
||||
# # => <link rel="preload" href="/assets/worker.js" as="worker" type="text/javascript" />
|
||||
#
|
||||
# preload_link_tag("//example.com/font.woff2")
|
||||
# # => <link rel="preload" href="//example.com/font.woff2" as="font" type="font/woff2" crossorigin="anonymous"/>
|
||||
#
|
||||
# preload_link_tag("//example.com/font.woff2", crossorigin: "use-credentials")
|
||||
# # => <link rel="preload" href="//example.com/font.woff2" as="font" type="font/woff2" crossorigin="use-credentials" />
|
||||
#
|
||||
# preload_link_tag("/media/audio.ogg", nopush: true)
|
||||
# # => <link rel="preload" href="/media/audio.ogg" as="audio" type="audio/ogg" />
|
||||
#
|
||||
def preload_link_tag(source, options = {})
|
||||
href = asset_path(source, skip_pipeline: options.delete(:skip_pipeline))
|
||||
extname = File.extname(source).downcase.delete(".")
|
||||
mime_type = options.delete(:type) || Template::Types[extname].try(:to_s)
|
||||
as_type = options.delete(:as) || resolve_link_as(extname, mime_type)
|
||||
crossorigin = options.delete(:crossorigin)
|
||||
crossorigin = "anonymous" if crossorigin == true || (crossorigin.blank? && as_type == "font")
|
||||
nopush = options.delete(:nopush) || false
|
||||
|
||||
link_tag = tag.link({
|
||||
rel: "preload",
|
||||
href: href,
|
||||
as: as_type,
|
||||
type: mime_type,
|
||||
crossorigin: crossorigin
|
||||
}.merge!(options.symbolize_keys))
|
||||
|
||||
early_hints_link = "<#{href}>; rel=preload; as=#{as_type}"
|
||||
early_hints_link += "; type=#{mime_type}" if mime_type
|
||||
early_hints_link += "; crossorigin=#{crossorigin}" if crossorigin
|
||||
early_hints_link += "; nopush" if nopush
|
||||
|
||||
request.send_early_hints("Link" => early_hints_link) if respond_to?(:request) && request
|
||||
|
||||
link_tag
|
||||
end
|
||||
|
||||
# Returns an HTML image tag for the +source+. The +source+ can be a full
|
||||
# path, a file or an Active Storage attachment.
|
||||
#
|
||||
|
@ -417,6 +480,18 @@ module ActionView
|
|||
raise ArgumentError, "Cannot pass a :size option with a :height or :width option"
|
||||
end
|
||||
end
|
||||
|
||||
def resolve_link_as(extname, mime_type)
|
||||
if extname == "js"
|
||||
"script"
|
||||
elsif extname == "css"
|
||||
"style"
|
||||
elsif extname == "vtt"
|
||||
"track"
|
||||
elsif (type = mime_type.to_s.split("/")[0]) && type.in?(%w(audio video font))
|
||||
type
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -214,6 +214,17 @@ class AssetTagHelperTest < ActionView::TestCase
|
|||
%(favicon_link_tag 'mb-icon.png', :rel => 'apple-touch-icon', :type => 'image/png') => %(<link href="/images/mb-icon.png" rel="apple-touch-icon" type="image/png" />)
|
||||
}
|
||||
|
||||
PreloadLinkToTag = {
|
||||
%(preload_link_tag '/styles/custom_theme.css') => %(<link rel="preload" href="/styles/custom_theme.css" as="style" type="text/css" />),
|
||||
%(preload_link_tag '/videos/video.webm') => %(<link rel="preload" href="/videos/video.webm" as="video" type="video/webm" />),
|
||||
%(preload_link_tag '/posts.json', as: 'fetch') => %(<link rel="preload" href="/posts.json" as="fetch" type="application/json" />),
|
||||
%(preload_link_tag '/users', as: 'fetch', type: 'application/json') => %(<link rel="preload" href="/users" as="fetch" type="application/json" />),
|
||||
%(preload_link_tag '//example.com/map?callback=initMap', as: 'fetch', type: 'application/javascript') => %(<link rel="preload" href="//example.com/map?callback=initMap" as="fetch" type="application/javascript" />),
|
||||
%(preload_link_tag '//example.com/font.woff2') => %(<link rel="preload" href="//example.com/font.woff2" as="font" type="font/woff2" crossorigin="anonymous"/>),
|
||||
%(preload_link_tag '//example.com/font.woff2', crossorigin: 'use-credentials') => %(<link rel="preload" href="//example.com/font.woff2" as="font" type="font/woff2" crossorigin="use-credentials" />),
|
||||
%(preload_link_tag '/media/audio.ogg', nopush: true) => %(<link rel="preload" href="/media/audio.ogg" as="audio" type="audio/ogg" />)
|
||||
}
|
||||
|
||||
VideoPathToTag = {
|
||||
%(video_path("xml")) => %(/videos/xml),
|
||||
%(video_path("xml.ogg")) => %(/videos/xml.ogg),
|
||||
|
@ -535,6 +546,10 @@ class AssetTagHelperTest < ActionView::TestCase
|
|||
FaviconLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
|
||||
end
|
||||
|
||||
def test_preload_link_tag
|
||||
PreloadLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
|
||||
end
|
||||
|
||||
def test_video_path
|
||||
VideoPathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue