【网站深入seo方法】
目录
①对于更成熟的网站,简单的index.html的入口文件的seo已经无法满足,需要在商品详情不同商品被搜索时赋予不同的title和description。
②通过设置站点所有页面都新增Canonical标签,指定规范链接地址给谷歌并规避联盟的重复内容页面。
③产品结构化微数据
①对于更成熟的网站,简单的index.html的入口文件的seo已经无法满足,需要在商品详情不同商品被搜索时赋予不同的title和description。
<!-- src/views/main/goods/goods-details/goods-details.vue -->
<template><!-- 模板部分保持不变 -->
</template><script>
// ... 其他导入保持不变export default {name: "goods-details",// ... 其他配置保持不变data() {return {// ... 其他数据保持不变originalMetaTags: {} // 保存原始meta标签}},// ... 其他配置保持不变methods: {// 设置产品页面的SEO信息setProductSEO(dataInfo) {if (!dataInfo) return;// 保存原始的meta标签信息(只在第一次调用时保存)if (!this.originalMetaTags.title) {this.originalMetaTags.title = document.title;this.originalMetaTags.description = this.getMetaContent('name', 'description');this.originalMetaTags['og:title'] = this.getMetaContent('property', 'og:title');this.originalMetaTags['og:description'] = this.getMetaContent('property', 'og:description');this.originalMetaTags['twitter:title'] = this.getMetaContent('name', 'twitter:title');this.originalMetaTags['twitter:description'] = this.getMetaContent('name', 'twitter:description');}// 获取产品标题const productTitle = dataInfo.subject || '';// 设置新的页面标题const newTitle = `Buy ${productTitle} | Hipobuy`;document.title = newTitle;// 设置新的meta descriptionconst newDescription = `Find the perfect ${productTitle} on Taobao or 1688? Hipobuy is your trusted China shopping agent. Start shopping now!`;// 更新所有meta标签this.updateMetaTag('name', 'description', newDescription);this.updateMetaTag('property', 'og:title', newTitle);this.updateMetaTag('property', 'og:description', newDescription);this.updateMetaTag('name', 'twitter:title', newTitle);this.updateMetaTag('name', 'twitter:description', newDescription);},// 获取meta标签内容的辅助方法getMetaContent(attribute, value) {const metaTag = document.querySelector(`meta[${attribute}="${value}"]`);return metaTag ? metaTag.getAttribute('content') : '';},// 更新meta property标签updateMetaProperty(property, content) {let metaTag = document.querySelector(`meta[property="${property}"]`);if (metaTag) {metaTag.setAttribute('content', content);} else {metaTag = document.createElement('meta');metaTag.setAttribute('property', property);metaTag.content = content;document.head.appendChild(metaTag);}},// 更新meta标签的通用方法updateMetaTag(attribute, value, content) {let metaTag = document.querySelector(`meta[${attribute}="${value}"]`);if (metaTag) {metaTag.setAttribute('content', content);} else {metaTag = document.createElement('meta');metaTag.setAttribute(attribute, value);metaTag.content = content;document.head.appendChild(metaTag);}},// 完整恢复原始meta标签(页面销毁时调用)restoreOriginalMetaTags() {// 恢复titleif (this.originalMetaTags.title) {document.title = this.originalMetaTags.title;}// 恢复descriptionif (this.originalMetaTags.description !== undefined) {const descriptionMeta = document.querySelector('meta[name="description"]');if (descriptionMeta) {if (this.originalMetaTags.description) {descriptionMeta.setAttribute('content', this.originalMetaTags.description);} else {descriptionMeta.remove(); // 如果原来没有,就删除添加的}} else if (this.originalMetaTags.description) {// 如果原来有但现在没有,就重新创建const meta = document.createElement('meta');meta.name = 'description';meta.content = this.originalMetaTags.description;document.head.appendChild(meta);}}// 恢复og:titlethis.restoreMetaTag('property', 'og:title', this.originalMetaTags['og:title']);// 恢复og:descriptionthis.restoreMetaTag('property', 'og:description', this.originalMetaTags['og:description']);// 恢复twitter:titlethis.restoreMetaTag('name', 'twitter:title', this.originalMetaTags['twitter:title']);// 恢复twitter:descriptionthis.restoreMetaTag('name', 'twitter:description', this.originalMetaTags['twitter:description']);},// 恢复单个meta标签的辅助方法restoreMetaTag(attribute, value, originalContent) {const metaTag = document.querySelector(`meta[${attribute}="${value}"]`);if (metaTag) {if (originalContent) {metaTag.setAttribute('content', originalContent);} else {metaTag.remove(); // 如果原来没有,删除添加的标签}} else if (originalContent) { // 如果原来有但现在没有,重新创建const meta = document.createElement('meta');meta.setAttribute(attribute, value);meta.content = originalContent;document.head.appendChild(meta);}},getQueryProductDetail(force) {this.loading = true;queryProductDetail({spuNo: this.id,refresh: !!force ? 1 : 0,channel: this.channel,activityCode: this.activityCode}).then((res) => {if (res.code == 1010) {this.loading = false;this.$refs.tipsPopRef.open(res.message);} else if (res.code == 200) {if (res.result.productGrayscale) {this.$refs.grayPopRef.open(res.result.productGrayscaleName)}this.loading = false;this.dataInfo = res.result;this.dataInfo.description = this.dataInfo.description.replace(/\s*href="[^"]*"/gi, '');// 设置产品SEO信息——getQueryProductDetail此请求只添加了这个设置,其余不需改动this.setProductSEO(this.dataInfo);if (this.dataInfo.channel == 'TAOBAO') {detailPointBtn(this.$route.params.spuNo, 'EXP', 'buy_icon');detailPointBtn(this.$route.params.spuNo, 'EXP', 'addcart_icon');}if (res.result.spuActivityVO) {if (this.$analytics) {this.$analytics.logEvent('pt_1001');}}}}).catch(() => {this.loading = false;})},// ... 其他方法保持不变},beforeDestroy() {// 恢复原始的meta标签this.restoreOriginalMetaTags();}
}
</script><!-- 样式部分保持不变 -->
本地环境自测以html的标签为准即可;
②通过设置站点所有页面都新增Canonical标签,指定规范链接地址给谷歌并规避联盟的重复内容页面。
处理带有URL参数的页面
场景:电商网站的产品页面可能会因为不同的筛选条件(如颜色、尺寸、排序方式等)而生成带有不同参数的URL。
示例:https://example.com/product?id=123&color=blue
https://example.com/product?id=123&size=large
解决方案:在所有带参数的页面中,使用Canonical标签指向主要的产品页面,如:
<link rel="canonical" href="https://example.com/product?id=123" />
分页内容
场景:文章列表或产品列表页面经常会分页(如第1页、第2页)
示例:https://example.com/blog?page=1
https://example.com/blog?page=2
解决方案:可以让每个分页页面的Canonical标签指向第一页,或者让每页的Canonical标签指向自身。<link rel="canonical" href="https://example.com/blog?page=1" />
内容分发与跟踪参数
场景:网站在不同渠道(如社交媒体)分发内容,带有UTM等跟踪参数
示例:https://example.com/blog/post?utm_source=facebook
解决方案:使用Canonical标签指向无参数的主要URL.
<link rel="canonical" href="https://example.com/blog/post" />
下面是我的实践实例动态设置所有页面:
<!-- public/index.html -->
<!DOCTYPE html>
<html lang="en-US">
<head><!-- 其他head内容保持不变 --><!-- 添加基础canonical标签 --><link rel="canonical" href="https://hipobuy.com/" /><!-- 其他head内容保持不变 -->
</head>
<body><div id="app"></div>
</body>
</html>
// 在 src/router/index.js 中添加
router.afterEach((to) => {// 延迟执行确保DOM已更新setTimeout(() => {const canonicalLink = document.querySelector('link[rel="canonical"]');if (canonicalLink) {// 获取基础URL,移除查询参数和hashconst baseUrl = window.location.origin;const pathname = to.path;const canonicalUrl = baseUrl + pathname;canonicalLink.href = canonicalUrl;}}, 100);
});
特殊情况:电商网站中如果路由配置了参数可以用这种配置,不过具体在控制台查看link标签,我发现以上路由设置已经足够,可以把参数也带进canonical标签,如果不行参考以下:
<!-- 在 src/views/main/goods/goods-details/goods-details.vue 的 methods 中添加 -->
methods: {// ... 其他方法保持不变// 添加这个方法来设置产品页的canonicalsetProductCanonical() {const canonicalLink = document.querySelector('link[rel="canonical"]');if (canonicalLink) {// 构建产品页的标准canonical URLconst channelMap = {"0": "1688","1": "taobao","2": "weidian","3": "jd"};const channelName = channelMap[this.channel] || this.channel.toLowerCase();const canonicalUrl = `https://hipobuy.com/product/${channelName}/${this.id}`;canonicalLink.href = canonicalUrl;}},// 在 getQueryProductDetail 方法的成功回调中调用getQueryProductDetail(force) {// ... 其他代码保持不变queryProductDetail({// ... 参数保持不变}).then((res) => {// ... 其他成功处理保持不变if (res.code == 200) {// ... 其他处理保持不变// 设置产品SEO信息this.setProductSEO(this.dataInfo);// 添加这一行来设置产品页canonicalthis.setProductCanonical();// ... 其他处理保持不变}}).catch(() => {this.loading = false;})}
}
③产品结构化微数据
这是代码效果示例,不可以直接添加在html:
<html><head><title>Executive Anvil</title><script type="application/ld+json">{"@context": "https://schema.org/","@type": "Product","name": "Executive Anvil","image": ["https://example.com/photos/1x1/photo.jpg","https://example.com/photos/4x3/photo.jpg","https://example.com/photos/16x9/photo.jpg"],"description": "Sleeker than ACME's Classic Anvil, the Executive Anvil is perfect for the business traveler looking for something to drop from a height.","sku": "0446310786","mpn": "925872","brand": {"@type": "Brand","name": "ACME"},"review": {"@type": "Review","reviewRating": {"@type": "Rating","ratingValue": 4,"bestRating": 5},"author": {"@type": "Person","name": "Fred Benson"}},"aggregateRating": {"@type": "AggregateRating","ratingValue": 4.4,"reviewCount": 89},"offers": {"@type": "AggregateOffer","offerCount": 5,"lowPrice": 119.99,"highPrice": 199.99,"priceCurrency": "USD"}}</script></head><body></body>
</html>
以下才是js效果代码:
<!-- src/views/main/goods/goods-details/goods-details.vue -->
<script>
export default {name: "goods-details",// ... 其他代码保持不变data() {return {// ... 其他数据保持不变structuredDataScript: null // 保存结构化数据script元素}},methods: {// 生成并插入结构化数据generateStructuredData(dataInfo) {if (!dataInfo) return;// 移除已存在的结构化数据this.removeStructuredData();// 构建基础产品信息const structuredData = {"@context": "https://schema.org/","@type": "Product","name": dataInfo.subject || '',"image": this.getProductImages(dataInfo),"description": this.getProductDescription(dataInfo),"sku": dataInfo.spuNo || '',"mpn": dataInfo.spuNo || dataInfo.id || '',"brand": this.getProductBrand(dataInfo),"offers": this.getProductOffers(dataInfo)};// 添加评分信息const ratingInfo = this.getProductRating(dataInfo);if (ratingInfo) {structuredData.aggregateRating = ratingInfo;}// 添加评论信息(如果有)const reviews = this.getProductReviews(dataInfo);if (reviews && reviews.length > 0) {structuredData.review = reviews;}// 创建script标签并插入到head中this.structuredDataScript = document.createElement('script');this.structuredDataScript.type = 'application/ld+json';this.structuredDataScript.textContent = JSON.stringify(structuredData, null, 2);// 添加标识便于清理this.structuredDataScript.setAttribute('data-structured-data', 'product');document.head.appendChild(this.structuredDataScript);},// 获取产品图片列表getProductImages(dataInfo) {const images = [];// 主图if (dataInfo.mainImg) {images.push(dataInfo.mainImg);}// 图片列表if (dataInfo.imageList && Array.isArray(dataInfo.imageList)) {images.push(...dataInfo.imageList);}// 去重return [...new Set(images)].slice(0, 10); // 最多10张图片},// 获取产品描述getProductDescription(dataInfo) {if (!dataInfo.description) return '';// 移除HTML标签const tmp = document.createElement('div');tmp.innerHTML = dataInfo.description;return tmp.textContent || tmp.innerText || '';},// 获取品牌信息getProductBrand(dataInfo) {const channelNames = {"0": "1688","1": "Taobao","2": "Weidian","3": "JD","1688": "1688","TAOBAO": "Taobao","WEIDIAN": "Weidian","JD": "JD"};const brandName = dataInfo.brandName ||channelNames[dataInfo.channel] ||channelNames[this.channel] ||"Chinese E-commerce Platform";return {"@type": "Brand","name": brandName};},// 获取产品报价信息getProductOffers(dataInfo) {const price = this.getProductPrice(dataInfo);const currency = dataInfo.currencyCode || "CNY";const availability = this.getAvailabilityStatus(dataInfo);return {"@type": "Offer","url": window.location.href,"priceCurrency": currency,"price": price,"availability": availability,"priceValidUntil": this.getPriceValidUntil(),"seller": {"@type": "Organization","name": "Hipobuy"}};},// 获取产品价格getProductPrice(dataInfo) {if (dataInfo.price) {return dataInfo.priceCNY ? dataInfo.priceCNY : dataInfo.price;} else if (dataInfo.saleInfo) {if (dataInfo.saleInfo.priceRangeList && dataInfo.saleInfo.priceRangeList.length > 0) {return dataInfo.saleInfo.priceRangeList[0].priceCNY ?dataInfo.saleInfo.priceRangeList[0].priceCNY :dataInfo.saleInfo.priceRangeList[0].price;} else if (dataInfo.saleInfo.priceRanges && dataInfo.saleInfo.priceRanges.length > 0) {return dataInfo.saleInfo.priceRanges[0].priceCNY ?dataInfo.saleInfo.priceRanges[0].priceCNY :dataInfo.saleInfo.priceRanges[0].price;}}return "0";},// 获取产品评分信息getProductRating(dataInfo) {if (dataInfo.score || dataInfo.commentNum) {return {"@type": "AggregateRating","ratingValue": dataInfo.score || 0,"reviewCount": dataInfo.commentNum || 0,"bestRating": 5,"worstRating": 1};}return null;},// 获取产品评论信息getProductReviews(dataInfo) {// 如果有具体的评论数据,可以在这里处理// 目前返回空数组return [];},// 获取库存状态getAvailabilityStatus(dataInfo) {// 根据库存信息判断可用性if (dataInfo.stock !== undefined) {return dataInfo.stock > 0 ? "https://schema.org/InStock" : "https://schema.org/OutOfStock";}// 根据状态判断if (dataInfo.status !== undefined) {switch (dataInfo.status) {case 'ON_SALE':case 'AVAILABLE':return "https://schema.org/InStock";case 'SOLD_OUT':case 'OFF_SALE':return "https://schema.org/OutOfStock";default:return "https://schema.org/InStock";}}return "https://schema.org/InStock"; // 默认有库存},// 获取价格有效期getPriceValidUntil() {const date = new Date();date.setMonth(date.getMonth() + 3); // 价格有效期3个月return date.toISOString().split('T')[0];},// 移除结构化数据removeStructuredData() {if (this.structuredDataScript) {document.head.removeChild(this.structuredDataScript);this.structuredDataScript = null;}},},// 组件销毁时移除结构化数据beforeDestroy() {this.removeStructuredData(); // 移除结构化数据}
}
</script>
// 生成结构化数据---------在调用商品详情的接口内调用并传递res.dataInfothis.generateStructuredData(this.dataInfo);
执行完之后可以在控制台head标签内查找是否展现: