Ответ 1
Вот полный обзор того, как сделать ваше приложение оптимизированным для SEO в сервисе хранения, таком как S3, с хорошими URL-адресами (нет #) и все с хрюканьем с простой командой, которая будет выполняться после сборки:
grunt seo
Это все еще головоломка обходных решений, но она работает, и это лучшее, что вы можете сделать. Спасибо @ericluwj и его blogpost, которые вдохновили меня.
Обзор
Структура цели и URL
Цель состоит в том, чтобы создать 1 html файл для каждого состояния в вашем приложении angular. Единственное серьезное предположение заключается в том, что вы удаляете "#" из своего URL-адреса, используя html5history (что вы должны делать!) И что все ваши пути являются абсолютными или используют состояния angular. Есть много сообщений, объясняющих, как это сделать.
Урлс заканчивается конечной косой чертой http://yourdomain.com/page1/
Лично я убедился, что http://yourdomain.com/page1 (без конечной косой черты) также достигает своего адресата, но здесь нет темы. Я также убедился, что каждый язык имеет другое состояние и другой URL-адрес.
Логика SEO
Наша цель состоит в том, что когда кто-то достигает вашего веб-сайта с помощью http-запроса:
- Если это поисковый робот: держите его на странице, содержащей требуемый html. Страница также содержит логику angular (например, для запуска вашего приложения), но искатель не может прочитать, что он намеренно застрял с html, который вы ему обслуживали, и проиндексирует это.
- Для нормальных людей и интеллектуальных машин: убедитесь, что angular активирован, удалите сгенерированный html и запустите приложение обычно
Задачи grunt
Здесь мы идем с заданиями:
//grunt plugins you will need:
grunt.loadNpmTasks('grunt-prerender');
grunt.loadNpmTasks('grunt-replace');
grunt.loadNpmTasks('grunt-wait');
grunt.loadNpmTasks('grunt-aws-s3');
//The grunt tasks in the right order
grunt.registerTask('seo', 'First launch server, then prerender and replace', function (target) {
grunt.task.run([
'concurrent:seo' //Step 1: in parrallel launch server, then perform so-called seotasks
]);
});
grunt.registerTask('seotasks', [
'http', //This is an API call to get all pages on my website. Skipping this step in this tutorial.
'wait', // wait 1.5 sec to make sure that server is launched
'prerender', //Step 2: create a snapshot of your website
'replace', //Step 3: clean the mess
'sitemap', //Create a sitemap of your production environment
'aws_s3:dev' //Step 4: upload
]);
Шаг 1: Запустите локальный сервер с одновременным: seo
Сначала нам нужно запустить локальный сервер (например, службу grunt), чтобы мы могли делать снимки нашего веб-сайта.
//grunt config
concurrent: {
seo: [
'connect:dist:keepalive', //Launching a server and keeping it alive
'seotasks' //now that we have a running server we can launch the SEO tasks
]
}
Шаг 2. Создание моментального снимка вашего сайта с помощью grunt prerender
Плагины grunt-prerender позволяют делать снимок любого веб-сайта с помощью PhantomJS. В нашем случае мы хотим сделать снимок всех страниц сайта localhost, который мы только что запустили.
//grunt config
prerender: {
options: {
sitePath: 'http://localhost:9001', //points to the url of the server you just launched. You can also make it point to your production website.
//As you can see the source urls allow for multiple languages provided you have different states for different languages (see note below for that)
urls: ['/', '/projects/', '/portal/','/en/', '/projects/en/', '/portal/en/','/fr/', '/projects/fr/', '/portal/fr/'],//this var can be dynamically updated, which is done in my case in the callback of the http task
hashed: true,
dest: 'dist/SEO/',//where your static html files will be stored
timeout:5000,
interval:5000, //taking a snapshot of how the page looks like after 5 seconds.
phantomScript:'basic',
limit:7 //# pages processed simultaneously
}
}
Шаг 3: Очистите беспорядок с заменой grunt
Если вы откроете предварительно обработанные файлы, они будут работать для сканеров, но не для людей. Для людей, использующих хром, ваши директивы будут загружаться дважды. Поэтому вам необходимо перенаправить интеллектуальные браузеры на домашнюю страницу до активации angular (т.е. Сразу после головы).
//Add the script tag to redirect if we're not a search bot
replace: {
dist: {
options: {
patterns: [
{
match: '<head>',
//redirect to a clean page if not a bot (to your index.html at the root basically).
replacement: '<head><script>if(!/bot|googlebot|crawler|spider|robot|crawling/i.test(navigator.userAgent)) { document.location = "/#" + window.location.pathname; }</script>'
//note: your hashbang (#) will still work.
}
],
usePrefix: false
},
files: [
{expand: true, flatten: false, src: ['dist/SEO/*/**/*.html'], dest: ''}
]
}
Также убедитесь, что у вас есть этот код в вашем index.html в элементе ui-view, который очищает все сгенерированные директивы html до начала angular.
<div ui-view autoscroll="true" id="ui-view"></div>
<!-- this script is needed to clear ui-view BEFORE angular starts to remove the static html that has been generated for search engines who cannot read angular -->
<script>
if(!/bot|googlebot|crawler|spider|robot|crawling/i.test( navigator.userAgent)) { document.getElementById('ui-view').innerHTML = ""; }
</script>
Шаг 4: загрузка в aws
Сначала загрузите свою папку dist, которая содержит вашу сборку. Затем вы перезаписываете его с файлами, которые вы предварительно загрузили и обновили.
aws_s3: {
options: {
accessKeyId: "<%= aws.accessKeyId %>", // Use the variables
secretAccessKey: "<%= aws.secret %>", // You can also use env variables
region: 'eu-west-1',
uploadConcurrency: 5, // 5 simultaneous uploads
},
dev: {
options: {
bucket: 'xxxxxxxx'
},
files: [
{expand: true, cwd: 'dist/', src: ['**'], exclude: 'SEO/**', dest: '', differential: true},
{expand: true, cwd: 'dist/SEO/', src: ['**'], dest: '', differential: true},
]
}
}
Что это, у вас есть свое решение! Оба человека и боты смогут прочитать ваше веб-приложение