提交 eb90b8bc 编写于 作者: G Guillermo Iguaran

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 https://github.com/rails/rails/pull/30744/commits/59a02fb7bcbe68f26e1e7fdcec45c00c66e4a065
for more details about Early Hints.

Preload spec: https://w3c.github.io/preload/
上级 055493ce
* 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 @@ def favicon_link_tag(source = "favicon.ico", options = {})
}.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 @@ def check_for_image_tag_errors(options)
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 @@ def url_for(*args)
%(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 @@ def test_favicon_link_tag
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
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册