# Nuxt 結構資料化 ## 結構化資料格式 ### RDFa 範例 ``` html <span vocab="http://schema.org/" typeof="TechArticle"> <a property="url" href="http://www.w3.org/TR/rdfa-primer/"> <span property="name">RDFa 1.1 Primer</span></a>. </span> ``` 產生節點 ![](https://i.imgur.com/s0szWwP.png) 透過html tag的方式讓瀏覽器能夠看得懂取得節點,適合JSX實務上Apple的麵包屑就是透過此種做法。 缺點是前端渲染產生的組件必須配合schema.org的格式比較綁手綁腳,但無需額外增加tag可以減少原始碼的數量 ### JSON-LD(Google推薦) 範例 ```jsonld=Create { "@context": "https://www.w3.org/ns/activitystreams", "type": "Create", "actor": { "id": "acct:sally@example.org", "type": "Person", "name": "Sally" }, "object": { "type": "Note", "content": "This is a simple note" }, "published": "2015-01-25T12:34:56Z" } ``` 比較容易導入並且獨立性高與html body 內的tagdd ## 透過head新增(目前採用) [Stackoverflow範例](https://stackoverflow.com/questions/61516262/implementing-ldjson-using-nuxt-and-dangerouslydedablesanitizers) structedData須為SSR所需資料 ``` javascript head() { return { title: this.headTitle, meta: this.metaObject, script: this.structedData }; }, computed:{ structedData() { return [ { json: { "@context": "http://schema.org", "@type": "Product", name: this.itemInfo.info.name, // 必要屬性 brand: { "@type": "Brand", name: this.itemInfo.info.brand //建議屬性 }, // 建議 description: "產品描述目前尚未有此欄位", // 建議屬性 image: [this.itemInfo.imageUrl.detail[0].url], // 建議屬性 offers: { "@type": "Offer", price: "100.00" // 必要屬性 } }, type: "application/ld+json" } ]; }, } ``` ## 透過套件達成 nuxt-json(目前不採用) [教學](https://syj0905.github.io/vue/20191024/2360505463/) [github](https://github.com/ymmooot/nuxt-jsonld) ## 結構化範例與實作 ### 商品結構化 #### 測試頁面(單一商品頁面) /pages/_lang/item/_id.vue #### 測試內容 ``` javascript structedData() { return [ { json: { "@context": "http://schema.org", "@type": "Product", name: this.itemInfo.info.name, // 必要屬性 offers: { "@type": "Offer", price: "100.00" // 必要屬性 },// offers 或 aggregateRating則一為必要屬性 aggregateRating: { "@type": "AggregateRating", ratingValue: "4.4", // 必要屬性 評論等級 reviewCount: "89" // 必要屬性 評論數量 },// offers 或 aggregateRating則一為必要屬性 brand: { "@type": "Brand", name: this.itemInfo.info.brand // 建議屬性 }, // 建議屬性 description: "產品描述目前尚未有此欄位",// 建議屬性 image: [this.itemInfo.imageUrl.detail[0].url] // 建議 }, type: "application/ld+json" // 必要屬性 } ]; }, ``` [測試結果連結](https://search.google.com/test/rich-results?id=_N-jAddUq3AeR6BmW-UhdQ) #### 導入遇到問題 1. offers 或 aggregateRating 必須擇一為必填項目 2. offers內price為必填項目(可無需提供幣別代號 ex: TWD, USD) 3. aggregateRating為評論類型(須提供商品評分等級,評論總數) <!-- #### 結論 --> <!-- 若Eagle-Web希望導入結構化商品有以下兩則解決方案 1.公開透明價格即可符合條件。 2.新增使用者對商品的留言板評分即可滿足條件。 --> #### Q&A Q:如果評論分數我們一率給五星就可以直接驗過結構化搜尋是否可以騙過Google。 A:[你可以作假資料,但是被Google抓到有機會取消排名或是被搜尋引擎移除](https://t.codebug.vip/questions-198228.htm) ### 問與答結構化(FAQ) #### 測試結果(無法導入) 不符合條件 * 由網站本身撰寫、使用者無法新增其他答案的 FAQ 頁面 * 回答問題的使用指南 #### 解決辦法 若提供使用者詢問產品問題並且可以給予回覆答案即可導入 ### 導覽標記結構化(單一商品頁面-麵包屑) #### 測試頁面(單一商品頁面) /pages/_lang/item/_id.vue #### 測試內容 ```javascript= structedData() { const bread = []; this.getter_breadCrumbs.forEach(function (breadCrumb, index) { const content = { "@type": "ListItem", position: index + 1, name: breadCrumb.name, item: breadCrumb.path }; bread.push(content); }); return [ { json: { "@context": "http://schema.org", "@type": "BreadcrumbList", itemListElement: bread }, type: "application/ld+json" } ]; }, ``` [測試結果連結](https://search.google.com/test/rich-results?id=2-MlN3gvup196muTZAry9g) [修正後結果連結](https://search.google.com/test/rich-results?id=rAK_7dKfV1f3D5oK1Lw4nA) #### 導入遇到問題 1. 原本單一商品頁面的麵包屑並沒有SEO生命週期問題無法取得資料。 #### 測試結果 1.須先將單一商品頁面的資料提前獲取。 2.成功將麵包屑資料移動至AsyncData後可成功導入。 #### 問題補正(將單一商品頁面麵包屑SEO化過後) ```javascript= async fetch() { try { const { data: { data, status } } = await this.$axios.get(`/api/official/item/${this.$route.params.id}`); // todo>> wait API if (data && status.code?.split("-")[0] === "0") { this.itemInfo = data; this.seoBreadCrums.push({ name: "RELITHE", path: process.env._AXIOS_BASE_URL_ + "zh" }); this.seoBreadCrums.push({ name: "BUY", path: process.env._AXIOS_BASE_URL_ + "zh/buy" }); this.seoBreadCrums.push({ name: data.info.brand, path: process.env._AXIOS_BASE_URL_ + "zh/buy/brand/" + data.info.brandId }); this.seoBreadCrums.push({ name: data.info.name }); //最後一層麵包屑可為無連結 } else { this.isEmpty = true; } } catch (e) { this.error({ statusCode: 500, message: "server error" }); } }, ```