He estado trabajando en cómo hacer que un SPA sea rastreable por Google según las instrucciones de Google . Aunque hay bastantes explicaciones generales, no pude encontrar en ningún lado un tutorial paso a paso más completo con ejemplos reales. Después de terminar esto, me gustaría compartir mi solución para que otros también puedan usarla y posiblemente mejorarla aún más.
Estoy usando MVC
con Webapi
controladores, y Phantomjs en el lado del servidor, y Durandal en el lado del cliente con push-state
habilitado; También uso Breezejs para la interacción de datos cliente-servidor, todo lo cual recomiendo encarecidamente, pero intentaré dar una explicación lo suficientemente general que también ayudará a las personas que usan otras plataformas.
143
Respuestas:
Antes de comenzar, asegúrese de comprender lo que requiere google , en particular el uso de URL bonitas y feas . Ahora veamos la implementación:
Lado del cliente
En el lado del cliente, solo tiene una única página html que interactúa dinámicamente con el servidor a través de llamadas AJAX. de eso se trata SPA. Todas las
a
etiquetas en el lado del cliente se crean dinámicamente en mi aplicación, luego veremos cómo hacer que estos enlaces sean visibles para el robot de Google en el servidor. Cadaa
etiqueta debe poder tener una etiquetapretty URL
en lahref
etiqueta para que el robot de Google la rastree. No desea que lahref
parte se use cuando el cliente hace clic en ella (aunque desea que el servidor pueda analizarla, lo veremos más adelante), porque es posible que no queramos que se cargue una nueva página, solo para hacer una llamada AJAX obteniendo algunos datos que se mostrarán en parte de la página y cambiar la URL a través de javascript (por ejemplo, usando HTML5pushstate
o conDurandaljs
). Entonces, tenemos ambos unhref
atributo para google, así como enonclick
qué hace el trabajo cuando el usuario hace clic en el enlace. Ahora, como usopush-state
, no quiero ninguno#
en la URL, por lo que unaa
etiqueta típica puede verse así:<a href="http://www.xyz.com/#!/category/subCategory/product111" onClick="loadProduct('category','subCategory','product111')>see product111...</a>
'categoría' y 'subcategoría' probablemente serían otras frases, como 'comunicación' y 'teléfonos' o 'computadoras' y 'computadoras portátiles' para una tienda de electrodomésticos. Obviamente, habría muchas categorías y subcategorías diferentes. Como puede ver, el enlace es directamente a la categoría, subcategoría y el producto, no como parámetros adicionales a una página específica de 'tienda' como
http://www.xyz.com/store/category/subCategory/product111
. Esto se debe a que prefiero enlaces más cortos y simples. Implica que no habrá una categoría con el mismo nombre que una de mis 'páginas', es decir, 'No voy a entrar en cómo cargar los datos a través de AJAX (la
onclick
parte), buscar en Google, hay muchas buenas explicaciones. Lo único importante que quiero mencionar aquí es que cuando el usuario hace clic en este enlace, quiero que la URL en el navegador se vea así:http://www.xyz.com/category/subCategory/product111
. Y esta es la URL no se envía al servidor! recuerde, este es un SPA donde toda la interacción entre el cliente y el servidor se realiza a través de AJAX, ¡sin enlaces! todas las 'páginas' se implementan en el lado del cliente, y las diferentes URL no hacen una llamada al servidor (el servidor necesita saber cómo manejar estas URL en caso de que se usen como enlaces externos desde otro sitio a su sitio, lo veremos más adelante en la parte del servidor). Ahora, esto es manejado maravillosamente por Durandal. Lo recomiendo encarecidamente, pero también puede omitir esta parte si prefiere otras tecnologías. Si lo elige, y también está utilizando MS Visual Studio Express 2012 para Web como yo, puede instalar el Kit de inicio de Durandal y, allíshell.js
, usar algo como esto:Hay algunas cosas importantes para notar aquí:
route:''
) es para la URL que no tiene datos adicionales, es decirhttp://www.xyz.com
. En esta página carga datos generales usando AJAX. Es posible que en realidad no hayaa
etiquetas en esta página. Usted tendrá que añadir la etiqueta siguiente manera bot de que Google va a saber qué hacer con ella:<meta name="fragment" content="!">
. Esta etiqueta hará que el robot de Google transforme la URL a lawww.xyz.com?_escaped_fragment_=
que veremos más adelante.mapUnknownRoutes
entra. Asigna estas rutas desconocidas a la ruta de 'tienda' y también elimina cualquier '!' desde la URL en caso de que seapretty URL
generado por el motor de búsqueda de google. La ruta 'store' toma la información en la propiedad 'fragment' y realiza la llamada AJAX para obtener los datos, mostrarlos y cambiar la URL localmente. En mi aplicación, no cargo una página diferente para cada llamada; Solo cambio la parte de la página donde estos datos son relevantes y también cambio la URL localmente.pushState:true
que le indica a Durandal que use URL de estado de inserción.Esto es todo lo que necesitamos en el lado del cliente. Se puede implementar también con URL hash (en Durandal simplemente eliminas el
pushState:true
para eso). La parte más compleja (al menos para mí ...) fue la parte del servidor:Lado del servidor
Estoy usando
MVC 4.5
en el lado del servidor conWebAPI
controladores. El servidor realmente necesita manejar 3 tipos de URL: las generadas por google, ambaspretty
yugly
también una URL 'simple' con el mismo formato que la que aparece en el navegador del cliente. Veamos cómo hacer esto:Las URL bonitas y las 'simples' son interpretadas primero por el servidor como si trataran de hacer referencia a un controlador inexistente. El servidor ve algo parecido
http://www.xyz.com/category/subCategory/product111
y busca un controlador llamado 'categoría'. Entoncesweb.config
, agrego la siguiente línea para redirigirlos a un controlador de manejo de errores específico:Ahora bien, esto transforma la URL a algo como:
http://www.xyz.com/Error?aspxerrorpath=/category/subCategory/product111
. Quiero que la URL se envíe al cliente que cargará los datos a través de AJAX, por lo que el truco aquí es llamar al controlador predeterminado 'index' como si no hiciera referencia a ningún controlador; Lo hago agregando un hash a la URL antes de todos los parámetros 'categoría' y 'subcategoría'; la URL hash no requiere ningún controlador especial, excepto el controlador 'index' predeterminado y los datos se envían al cliente que luego elimina el hash y usa la información después del hash para cargar los datos a través de AJAX. Aquí está el código del controlador del controlador de errores:Pero, ¿qué pasa con las URL feas ? Estos son creados por el bot de google y deberían devolver HTML sin formato que contenga todos los datos que el usuario ve en el navegador. Para esto uso phantomjs . Phantom es un navegador sin cabeza que hace lo que el navegador está haciendo en el lado del cliente, pero en el lado del servidor. En otras palabras, Phantom sabe (entre otras cosas) cómo obtener una página web a través de una URL, analizarla incluyendo la ejecución de todo el código de JavaScript (así como obtener datos a través de llamadas AJAX) y devolverle el HTML que refleja el DOM Si está utilizando MS Visual Studio Express, es posible que desee instalar Phantom a través de este enlace .
Pero primero, cuando se envía una URL fea al servidor, debemos atraparla; Para esto, agregué a la carpeta 'App_start' el siguiente archivo:
Esto se llama desde 'filterConfig.cs' también en 'App_start':
Como puede ver, 'AjaxCrawlableAttribute' enruta URLs feas a un controlador llamado 'HtmlSnapshot', y aquí está este controlador:
Lo asociado
view
es muy simple, solo una línea de código:@Html.Raw( ViewBag.result )
como puede ver en el controlador, phantom carga un archivo javascript llamado
createSnapshot.js
bajo una carpeta que creé llamadaseo
. Aquí está este archivo javascript:Primero quiero agradecer a Thomas Davis por la página donde obtuve el código básico de :-).
Notará algo extraño aquí: phantom sigue volviendo a cargar la página hasta que la
checkLoaded()
función vuelva a ser verdadera. ¿Porqué es eso? Esto se debe a que mi SPA específico realiza varias llamadas AJAX para obtener todos los datos y colocarlos en el DOM de mi página, y phantom no puede saber cuándo se han completado todas las llamadas antes de devolverme el reflejo HTML del DOM. Lo que hice aquí es después de la última llamada AJAX agrego un<span id='compositionComplete'></span>
, de modo que si esta etiqueta existe, sé que se ha completado el DOM. Hago esto en respuesta alcompositionComplete
evento de Durandal , mira aquípara más. Si esto no sucede dentro de 10 segundos, me doy por vencido (solo debería tomar un segundo). El HTML devuelto contiene todos los enlaces que el usuario ve en el navegador. La secuencia de comandos no funcionará correctamente porque las<script>
etiquetas que existen en la instantánea HTML no hacen referencia a la URL correcta. Esto también se puede cambiar en el archivo fantasma de JavaScript, pero no creo que esto sea necesario porque Google solo usa el resumen de HTML para obtener losa
enlaces y no para ejecutar JavaScript; estos enlaces hacen referencia a una URL bonita y, de hecho, si intentas ver la instantánea HTML en un navegador, obtendrás errores de JavaScript, pero todos los enlaces funcionarán correctamente y te dirigirán nuevamente al servidor con una URL bonita esta vez obteniendo la página completamente funcional.Eso es todo. Ahora el servidor sabe cómo manejar URLs bonitas y feas, con el estado push habilitado tanto en el servidor como en el cliente. Todas las URL feas se tratan de la misma manera usando phantom, por lo que no es necesario crear un controlador separado para cada tipo de llamada.
Una cosa es posible que prefiera el cambio no es hacer una llamada "categoría / subcategoría / productos en general, pero para añadir una 'tienda' de manera que el enlace se verá algo como:
http://www.xyz.com/store/category/subCategory/product111
. Esto evitará el problema en mi solución de que todas las URL no válidas se tratan como si realmente fueran llamadas al controlador 'index', y supongo que se pueden manejar luego dentro del controlador 'store' sin la adición a laweb.config
que mostré anteriormente .fuente
Google ahora puede representar páginas SPA: desaprobando nuestro esquema de rastreo AJAX
fuente
Aquí hay un enlace a una grabación de screencast de mi clase de entrenamiento Ember.js que ofrecí en Londres el 14 de agosto. Describe una estrategia tanto para su aplicación del lado del cliente como para su aplicación del lado del servidor, y ofrece una demostración en vivo de cómo la implementación de estas características proporcionará a su aplicación JavaScript de una sola página una degradación elegante incluso para los usuarios con JavaScript desactivado .
Utiliza PhantomJS para ayudarlo a rastrear su sitio web.
En resumen, los pasos necesarios son:
Una vez que se realiza este paso, depende de su servidor para servir la versión estática de su HTML como parte de la etiqueta noscript en esa página. Esto permitirá que Google y otros motores de búsqueda rastreen cada página de su sitio web, a pesar de que su aplicación originalmente era una aplicación de una sola página.
Enlace al screencast con todos los detalles:
http://www.devcasts.io/p/spas-phantomjs-and-seo/#
fuente
Puede usar o crear su propio servicio para entregar previamente su SPA con el servicio llamado prerender. Puede consultarlo en su sitio web prerender.io y en su proyecto github (utiliza PhantomJS y renderiza su sitio web por usted).
Es muy fácil empezar. Solo tiene que redirigir las solicitudes de rastreadores al servicio y recibirán el html prestado.
fuente
Puede usar http://sparender.com/ que permite rastrear correctamente las aplicaciones de una sola página.
fuente