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 MVCcon Webapicontroladores, y Phantomjs en el lado del servidor, y Durandal en el lado del cliente con push-statehabilitado; 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
aetiquetas 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. Cadaaetiqueta debe poder tener una etiquetapretty URLen lahrefetiqueta para que el robot de Google la rastree. No desea que lahrefparte 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 HTML5pushstateo conDurandaljs). Entonces, tenemos ambos unhrefatributo para google, así como enonclickqué hace el trabajo cuando el usuario hace clic en el enlace. Ahora, como usopush-state, no quiero ninguno#en la URL, por lo que unaaetiqueta 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
onclickparte), 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 hayaaetiquetas 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.mapUnknownRoutesentra. Asigna estas rutas desconocidas a la ruta de 'tienda' y también elimina cualquier '!' desde la URL en caso de que seapretty URLgenerado 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:trueque 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:truepara eso). La parte más compleja (al menos para mí ...) fue la parte del servidor:Lado del servidor
Estoy usando
MVC 4.5en el lado del servidor conWebAPIcontroladores. El servidor realmente necesita manejar 3 tipos de URL: las generadas por google, ambasprettyyuglytambié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/product111y 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
viewes 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.jsbajo 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 alcompositionCompleteevento 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 losaenlaces 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.configque 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