Существует несколько решений для конвертации одностраничных приложений в seo в Интернете, в том числе рендеринг на стороне сервера ssr, предварительный рендеринг prerender, Google Grabbing AJAX и статический. . . Эти решения имеют свои преимущества и недостатки, и разработчики могут решать, какое решение использовать в соответствии с различными бизнес-сценариями и средами. В этой статье будет представлено еще одно SEO-решение с более эксцентричным мышлением.У этого решения также есть преимущества и недостатки, в зависимости от того, считает ли читатель его подходящим или нет.
Анализ проекта
Мой проект представляет собой одностраничное приложение, построенное на стеке технологий react+ts+dva, на данный момент в сети десятки страниц с несколькими SDK и плагинами.
- Учитывая использование рендеринга на стороне сервера для SEO, но мой проект так сильно развился, конфигурация упаковки, разделение кода, совместимость синтаксиса, отказ от объектов браузера, мышление на стороне сервера, так много моментов для рассмотрения, лучше изменить фреймворк и начать опять Девелопмент, поэтому стоимость трансформации слишком высока😱, рендеринг на стороне сервера не подходит для моей ситуации.
- Хотя предварительный рендеринг является самой низкой стоимостью разработки, в конце концов, он генерирует статический html один за другим, и мое требование к поисковой оптимизации состоит в том, чтобы позволить паукам сканировать каждое сообщение на моем форуме сообщества, чтобы я мог написать сообщение, подобное этому. Это просто копия html, плюс пагинация, сколько хранить 😰, а обновление сайта хлопотнее, такое решение не подходит.
- гугл.....эмммм.....................следующий
- Статизация также похожа на предварительный рендеринг. . .
грандиозное введение
Раньше я писал схему SEO для одностраничного приложения, которая заключается в предварительном рендеринге с помощью сканера локально и генерации статического HTML с той же структурой каталогов. он перешлет его мне.
Проект на тот момент представлял собой простой корпоративный сайт всего на несколько страниц, и проблем с пререндерингом не было.
Следуя этому ходу мыслей, достаточно судить о том, что паук поисковой системы не дает пауку увидеть другую страницу с данными.
Что касается того, как выглядит страница, пауку все равно🕷 Это как если вы найдете рекламодателя для рекламы, рекламодатель не будет спрашивать вас, какую тему и цветовой тон вы хотите, пока вы делаете это в соответствии с его размер и требования, а потом дайте деньги и товар закончился 🤑.
Поэтому под SEO можно сделать другой набор сайтов.Стиля нет,только html теги и соответствующие данные,соответствующие seo спецификациям.Не нужно преобразовывать исходный проект,стоимость разработки будет не очень высокой,а маленький размер будет загружаться быстрее.
Есть и минусы, то есть нужно поддерживать другой набор сайтов, изменения в основном интерфейсе сайта не повлияют, при изменении отображаемых данных необходимо синхронно модифицировать сео-версию сайта.
Код
Сначала создайте отдельную папку seo, исходный проект перемещать не нужно, структура кода следующая:
Реализация кода очень проста, достаточно написать промежуточное ПО для перехвата запроса, идентификации паука и возврата seo-страницы соответствующего пути.
Мой интерфейсный сервер использует экспресс, я могу написать промежуточное ПО экспресс и создать новый server.js:
// seo/server.js
const routes = require('./routes')
const layout_render = require('./src/layout');
module.exports = (req, res, next) => {
// 各大搜索引擎蜘蛛UA
const spiderUA = /Baiduspider|bingbot|Googlebot|360spider|Sogou|Yahoo! Slurp/
var isSpider = spiderUA.test(req.get('user-agent'))
// 获取路由表的路径
var seoPath = Object.keys(routes)
if (isSpider) {
for (let i=0,route; route = seoPath[i]; i++) {
if (new RegExp(route).test(req.path)) {
routes[route](req).then((result) => {
// 返回对应的模板结果给蜘蛛
res.set({'Content-Type': 'text/html','charset': 'utf-8mb4'}).status(200).send(layout_render(result))
})
break;
}
}
} else {
// 未匹配到蜘蛛则继续后面的中间件
return next()
}
}
Затем добавьте это промежуточное ПО на внешний сервер запуска, не забудьте поставить его перед другим промежуточным ПО.
// 前端启动服务器的server文件
var express = require('express')
var app = express()
// seo
app.use(require('seo/server'));
......
app.listen(xxxx)
Следующим шагом является написание шаблона и соответствующего анализа, создание новой домашней папки, а затем создание файлов index.ejs и index.js в этой папке.
<!-- seo/src/home/index.ejs -->
<div>
<h1>官网首页</h1>
<p>友情链接:</p>
<p><a href="https://www.baidu.com/" target="_blank">百度</a></p>
<p><a href="https://www.gogole.com/" target="_blank">谷歌</a></p>
</div>
index.js используется для анализа соответствующего шаблона ejs.
// seo/src/home/index.js
const ejs = require('ejs')
const fs = require('fs')
const path = require('path')
const template = fs.readFileSync(path.resolve(__dirname, './index.ejs'), 'utf8');
// 这里为什么会有个async关键字,往后面看就可以知道。
module.exports = async (req) => {
const result = ejs.render(template)
return result
}
Мы также можем создать несколько шаблонов макета для управления общими элементами, такими как заголовок, заголовок и панель навигации.
<!-- seo/layout.ejs -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name=”renderer” content=”webkit”>
<meta content="网站关键字"" name="keywords"/>
<meta content="网站描述" name="description"/>
<title>网站标题</title>
</head>
<body>
<div id="root">
<ul>
<li><a href="/">首页</a></li>
<li><a href="/community">社区</a></li>
</ul>
<%- children -%>
</div>
</body>
</html>
Разберите layout.ejs и вставьте layout_render содержимого:
// seo/layout.js
const ejs = require('ejs')
const fs = require('fs')
const path = require('path')
const template = fs.readFileSync(path.resolve(__dirname, './layout.ejs'), 'utf8');
const layout_render = (children) => {
return ejs.render(template, {children: children})
}
module.exports = layout_render
Таблица маршрутизации может использовать простую пару ключ-значение, а имя ключа представляет собой обычную строку в форме строки для представления правил сопоставления пути:
// seo/routes.js
const home_route = require('./src/home/index')
module.exports = {
'^(/?)$': home_route,
}
Так как же запросить данные и отобразить их в соответствующем шаблоне? Запрос данных асинхронный, как дождаться завершения запроса, а затем отобразить шаблон?
Для этого мы можем использовать async/await.Теперь, чтобы создать страницу списка сообщений сообщества, нам нужно сначала запросить у сообщества загрузку данных списка сообщений, затем отобразить данные в шаблон, создать новую папку сообщества, а также создать index.ejs в качестве страницы со списком сообщений.
<!-- seo/src/community/index.ejs -->
<div>
<h1>帖子列表</h1>
<ul>
<% forum_list.map((item) => { %>
<li><a href="/community/<%= item.id%>" target="_blank"><%= item.title-%></a></li>
<% })%>
</ul>
</div>
Связанные запросы интерфейса и операции с данными прописаны в index.js того же уровня:
// seo/src/community/index.js
const ejs = require('ejs')
const fs = require('fs')
const path = require('path')
const template = fs.readFileSync(path.resolve(__dirname, './index.ejs'), 'utf8');
const axios = require('axios');
module.exports = async (req) => {
const res = await axios.get('http://xxx.xx/api/community/list')
const result = ejs.render(template, {forum_list: res.data.list})
return result
}
Плюс соответствующая конфигурация маршрутизации:
// seo/routes.js
const home_route = require('./src/home/index')
const community_route = require('./src/community/index')
module.exports = {
'^(/?)$': home_route,
'^/community$': community_route,
}
Эта реализация первого предприняла для выполнения данных интерфейса рендеринга, чтобы убедиться, что паук может дать доступ к полной структуре данных и HTML.
Продолжайте реализовывать страницу для сведений о публикации:
<!-- seo/src/community_detail/index.ejs -->
const community_route = require('./src/community/index')
<div>
<h1><%= forum_data.title%></h1>
<p><%= forum_data.content%></p>
<p>作者:<%= forum_data.user.nickname%></p>
</div>
// seo/src/community_detail/index.js
const ejs = require('ejs')
const fs = require('fs')
const path = require('path')
const template = fs.readFileSync(path.resolve(__dirname, './index.ejs'), 'utf8');
const axios = require('axios');
module.exports = async (req) => {
// 获取路径里的id /community/:id
const forum_id = req.path.split('/')[2]
const res = await axios.get(`http://xxx.xx/api/community/${forum_id}/details?offset=1&limit=10`)
const result = ejs.render(template, {forum_data: res.data})
return result
}
Также добавьте соответствующую конфигурацию маршрутизации:
// seo/routes.js
const home_route = require('./src/home/index')
const community_route = require('./src/community/index')
const community_detail_route = require('./src/community_detail/index')
module.exports = {
'^(/?)$': home_route,
'^/community$': community_route,
'^/community/\\d+$': community_detail_route,
}
Таким образом реализуется простая seo-версия веб-сайта, которая не требует никакого стиля и не требует последующих взаимодействий, таких как js, для создания всплывающих окон.Пока первый запрос паука на доступ к веб-сайту есть данные, которые он хочет, это очень ясно? странно 😝. . .
Подводя итог, если ваш проект находится в стадии онлайн-операции и разработан до определенной степени интеграции, из-за высокой стоимости преобразования ssr, и вам необходимо разрешить включение некоторых данных (например, каждого поста статьи), вы можете рассмотреть мой метод 🤓.
Но я не гарантирую, что античитерский механизм паука отсеет маленькую сео-версию моего сайта, сильно отличающуюся от обычного доступа браузера к основному сайту🤔.
тестовое задание
Тест также очень прост, просто напишите смоделированный запрос паука, завиток, сканер, почтальон может смоделировать UA паука для тестирования. Или измените условия суждения поисковых роботов, чтобы получить к ним доступ напрямую через браузер.
онлайн демо
Если вы все еще не понимаете, просто посмотрите демо:seo-mask.lipten.link/