Accueil Blogue Conférences

Introduction à Turbolinks et astuces d'utilisation · Partie 1 de Turbolinks

2017-01-17

Turbolinks rend la navigation dans votre application Web plus rapide.

Turbolinks est une solution d’optimisation du chargement Web provenant de Ruby on Rails. En termes simples, lorsque l’utilisateur clique sur un lien, il ne saute pas réellement à une nouvelle page mais lit plutôt le contenu de la page cible par le biais d’ajax et remplace la page actuelle. Cette méthode présente les avantages suivants :

En plus de ces avantages fonctionnels, cette interaction en forme de “page” traditionnelle rend également plus facile la compréhension pour les utilisateurs (bien que l’interaction en style d’application soit plus populaire maintenant, cela nécessite des normes plus élevées des concepteurs et si elle n’est pas bien conçue, cela peut facilement causer des problèmes de navigation pour les utilisateurs).

Voir https://github.com/turbolinks/turbolinks, je ne vais pas répéter le contenu de la documentation ici.

Astuces de base

Éviter les fichiers CSS et JS géants

En raison du mécanisme de chargement de Turbolinks, les utilisateurs ont tendance à empaqueter tous les styles et scripts dans un fichier CSS et JS géant, ce qui peut affecter gravement le chargement initial. Turbolinks prend entièrement en charge le chargement de différents CSS et JS pour différentes pages.

Dans le projet Simple Psychology, la méthode suivante a été utilisée pour déterminer et charger automatiquement les styles et scripts requis pour la page actuelle :

/ Charger les fichiers de style, comme le fichier CSS pour pages#home, stocké dans app/assets/stylesheets/www/pages/home.sass
- if File.exist?(Rails.root.join("app/assets/stylesheets/#{request.subdomain.split('.')[0]}/#{params[:controller]}/#{params[:action]}.sass"))
  = stylesheet_link_tag "#{request.subdomain.split('.')[0]}/#{params[:controller]}/#{params[:action]}"

/ Charger les fichiers de script, comme le fichier JS pour pages#home, stocké dans app/assets/javascripts/www/pages/home.coffee
- if File.exist?(Rails.root.join("app/assets/javascripts/#{request.subdomain.split('.')[0]}/#{params[:controller]}/#{params[:action]}.coffee"))
  = javascript_include_tag "#{request.subdomain.split('.')[0]}/#{params[:controller]}/#{params[:action]}"

Simplifier les événements de chargement de page

En raison du mécanisme de chargement spécial de Turbolinks, nous devons souvent enregistrer et annuler l’enregistrement des événements manuellement. Chez Simple Psychology, nous avons simplifié ce code de cette manière :

_page_loaded = []
_page_unload = []
_page_unload_once = []

# Exécuter lorsque la page Web est chargée
window.$loaded = (func)->
  _page_loaded.push(func)

# Exécuter en quittant la page Web
window.$unload = (func)->
  _page_unload.push(func)

# Exécuter une fois en quittant la page Web
window.$unload_once = (func)->
  _page_unload_once.push(func)

# Le code ci-dessous est l'implémentation des interfaces ci-dessus

window.addEventListener 'turbolinks:load', ->
  func() for func in _page_loaded

window.addEventListener 'turbolinks:before-visit', ->
  func() for func in _page_unload_once
  _page_unload_once = []
  func() for func in _page_unload

window.addEventListener 'beforeunload', ->
  func() for func in _page_unload_once
  _page_unload_once = []
  func() for func in _page_unload

  null

Compatibilité avec Vue.js 1.x

Lorsqu’un page a des interactions de plus en plus complexes, il est nécessaire d’utiliser des cadres comme Vue.js pour simplifier le code d’interaction. Cependant, Turbolinks ne cache pas les événements liés au DOM lors du cache de pages. Lorsqu’un utilisateur revient à une page qui utilise des composants Vue, ces composants ne fonctionnent plus.

Notre approche de résolution consiste à cacher les données des composants Vue et à annuler l’enregistrement des composants en quittant la page. Lors du retour, ré-render le composant et injectez les données précédemment mises en cache.

Il est également important de noter que Turbolinks se souvient également de la position de défilement actuelle de la page, enregistrée dans Turbolinks.controller.getCurrentRestorationData().scrollPosition. Par conséquent, si votre composant est une longue liste et est chargée de manière asynchrone, vous devez manuellement mettre en cache cette position avant le chargement (car après le retour à la page, Turbolinks va automatiquement rafraîchir la valeur enregistrée), puis faire défiler la page vers la position mémorisée après que les données de la liste aient été chargées et rendues.

L’implémentation spécifique est la suivante : (Comme nous n’utilisons plus Vue.js, seules les solutions pour la version 1.x sont fournies)

vm_caches = {}

cache_data = (props, data)->
  cache = {}
  for k, v of $.extend(props, data)
    if v && typeof v['raw'] isnt 'undefined'
      continue if v.raw && v.raw.indexOf('$data') is 0
      try
        cache[k] = eval(v.raw)
      catch error
        cache[k] = v.raw
    else
      cache[k] = v
  cache

cache_vm = (parent, cache)->
  for child in parent.$children
    continue if typeof child is 'undefined'
    cache[child.constructor.name] = cache_data(child._data, child._props)
    cache_vm child, cache

$loaded ->
  window.vm = new Vue
    el: 'body'

window.addEventListener 'turbolinks:before-cache', ->
  list = []
  while vm.$children.length > 0
    child = vm.$children[0]
    id = $.id() # $.id est une fonction pour générer un ID unique, que vous pouvez implémenter vous-même
    vm_caches[id] =
      data: cache_data(child._data, child._props)
      children: {}
    $(child.$el).after(child.$options.el.outerHTML.replace('><', " _cache_key=\"#{id}\"><"))
    cache_vm child, vm_caches[id].children
    child.$destroy(true)

Vue.define = (id, options)->
  if options['props']
    options.props.push '_cache_key'
  else
    options.props = []

  if options['ready']
    originalReady = options.ready
    options.ready = ->
      if vm_caches[@$parent._cache_key]
        for k, v of vm_caches[@$parent._cache_key].children[@constructor.name]
          Vue.set this, k, v
      if vm_caches[@_cache_key]
        for k, v of vm_caches[@_cache_key].data
          Vue.set this, k, v
      originalReady.call(this)
  else
    options.ready = ->
      if @_cache_key
        for k, v of vm_caches[@_cache_key]
          Vue.set this, k, v

  Vue.component id, options

Si vous avez plus d’astuces, n’hésitez pas à les partager et à les échanger.

Retour à tous les articles