作者:Maxime Laboissonniere
原文地址: Vue.js Tutorial: An Example to Build and Prerender an SEO-Friendly Site
譯者:jeneser
“我受不了了!我們的內(nèi)部報告面板太爛了”
產(chǎn)品經(jīng)理很生氣灶壶。他從這個即將崩潰的應(yīng)用程序中拉取數(shù)據(jù)的操作是災(zāi)難性的。
“Max勋陪,我們需要更好的報告逆粹。你能修嗎?”
“老實說裤唠,我更愿意建立一個全新的應(yīng)用”砂客,我笑著回答說泥张。
“好,請便鞠值。全權(quán)委托媚创,老鐵”
我笑著,搓了搓手齿诉。最后筝野,在一個需要使用JS框架的場景中晌姚,大家一致選擇了Vue.js
粤剧。
最近歇竟,我完成該應(yīng)用的代碼,我對它簡直愛不釋手抵恋。
我花了一些時間為社區(qū)寫了一個vue.js教程焕议,這些教程的靈感全部來自于我最近對vue的實踐。在這里弧关,我主要討論以下兩點:
- 如何使用Vue.js構(gòu)建精簡的Web應(yīng)用程序
- 如何使用
prerender-spa-plugin
來處理Vue.js應(yīng)用的預(yù)渲染與SEO
更具體地說盅安,我將帶您創(chuàng)建一個小商店,它將具備SEO友好的產(chǎn)品頁面世囊。我會提供在線演示以及相關(guān)代碼别瞭。
在我們開發(fā)的最新版Headless CMS中我接觸過一些vue,這一次我們會更加的深入株憾,我很興奮蝙寨!
更新:我們正在將Snipcart的前端從Backbone遷移到Vue.js,了解更多
我們先來為那些不熟悉漸進(jìn)式框架(Vue.js)的同學(xué)做一下簡單的介紹嗤瞎。
Vue.js到底是什么墙歪?
Vue.js是一套幫助你構(gòu)建用戶界面的輕量級,漸進(jìn)式的JavaScript框架
不要被“JS框架”這一定義所愚弄贝奇。Vue與目前流行的React.js & Angular.js是截然不同的虹菲。對于初學(xué)者來說,它不是Google&Facebook等商業(yè)技術(shù)巨頭的開源副產(chǎn)品掉瞳。
Evan You(尤雨溪)在2014年首次發(fā)布了它毕源,旨在創(chuàng)建一個“增量開發(fā)”的現(xiàn)代JS庫。Vue最強(qiáng)大的功能之一是:創(chuàng)建可復(fù)用的組件陕习,你可以在其他項目中重用這些組件而不用再次編寫霎褐。所有開發(fā)人員都可以在項目中嘗試Vue,而不用擔(dān)心這會對現(xiàn)有的代碼庫產(chǎn)生危害或是增加額外的負(fù)擔(dān)衡查。
拋開模式和術(shù)語瘩欺,我覺得Vue有以下提論:
1. 一開始你不知道整個應(yīng)用的架構(gòu)狀態(tài)
2. 應(yīng)用數(shù)據(jù)一定會在運行時發(fā)生改變
正是圍繞這些提論,vue塑造了自身:它是漸進(jìn)式拌牲,基于組件和響應(yīng)式的俱饿。組件的粒度劃分可以讓你輕松地分離應(yīng)用邏輯,同時又保持它們的可重用性塌忽。更重要的是拍埠,它將您的數(shù)據(jù)原生綁定到視圖,以便在需要時“神奇”地更新(通過watcher)土居。雖然許多響應(yīng)式前端框架擁有同樣的功能枣购,但是我發(fā)現(xiàn)Vue更優(yōu)雅的實現(xiàn)了它嬉探,并且,對于我的大多數(shù)用例棉圈,它往往表現(xiàn)的更好涩堤。
Vue還具有更加平滑的學(xué)習(xí)曲線,對于React來說分瘾,我們需要掌握J(rèn)SX模板等的相關(guān)知識胎围。甚至可以說Vue是React減去了比較復(fù)雜的部分。
Vue官方文檔提供了與其他JS框架(React, Angular, Ember, Knockout, Polymer, Riot)更加深入的對比德召。查看官方文檔
最后但同樣重要的是:得益于高性能&強(qiáng)大的開發(fā)工具白魂,Vue為我們提供了最佳的編碼體驗。它能如此流行也就不足為奇了!
從開源項目Laravel&PageKit上岗,到企業(yè)福荸,如Gitlab&Codeship(更不用說阿里巴巴和百度這些巨頭了),許多組織正在使用Vue肴掷。
OK敬锐,現(xiàn)在是時候來看看我們將如何使用它了。
Vue.js例子:一個快速的捆等,搜索引擎友好的電子商務(wù)應(yīng)用
在本節(jié)中滞造,我會告訴你如何使用Vue 2.0 & Snipcart
建立一個小型的電子商務(wù)應(yīng)用程序。我們還將看到如何確保產(chǎn)品頁面被搜索引擎正確“抓取”栋烤。
準(zhǔn)備
- 了解一些Vue.js相關(guān)知識谒养,還不了解?
- 基本了解vuex & vue-router
- 一個Snipcart帳戶(測試模式永久免費)
如果你想深入了解 Vue 2.0 相關(guān)知識明郭,可以查看Laracasts上的這個系列买窟。
1. 環(huán)境配置
首先,我們將使用vue-cli來構(gòu)建基本的Vue應(yīng)用程序薯定。在你喜歡的終端里始绍,輸入:
npm install -g vue-cli
vue init webpack-simple vue-snipcart
這將創(chuàng)建一個新的vue-snipcart文件夾,其中包含使用vue-loader的基本配置话侄,它將能使我們編寫單文件組件(template/js/css在同一個.vue
文件中)亏推。
我們希望這個示例盡可能真實,因此年堆,我們將在本應(yīng)用中增加兩個廣泛應(yīng)用于大型項目的模塊:vuex
和vue-router
吞杭。
- vuex是類Flux架構(gòu)的狀態(tài)管理器 - 輕量級,非常強(qiáng)大变丧。它受到了Redux的影響芽狗,你可以在這里了解更多。
- vue-router允許您定義路由以動態(tài)處理應(yīng)用程序的組件痒蓬。
要安裝這些童擎,請先進(jìn)入vue-snipcart
項目文件夾滴劲,然后運行以下命令:
npm install --save vue-router
npm install --save vuex
接下來要安裝的是prerender-spa-plugin
,這將使我們能夠預(yù)渲染“蜘蛛”將要爬行的路徑:
npm install --save prerender-spa-plugin
快要完成了顾复,最后四個包:
- pug - 模板引擎班挖,相對于HTML我更喜歡它。
- vuex-router-sync-to - 輕松保持vue-router和vuex存儲同步捕透。
- copy-webpack-plugin-to - 輕松包含我們在dist文件夾中的靜態(tài)文件聪姿。
- babel-polyfill - 在PhantomJS中運行Vue(通過我們的預(yù)渲染插件使用)碴萧。
運行這些:
npm install --save pug
npm install --save vuex-router-sync
npm install --save copy-webpack-plugin
npm install --save babel-polyfill
2. 架構(gòu)
安裝完成后請檢查是否安裝正確乙嘀。之后,便可以處理我們的商店數(shù)據(jù)了破喻。
先從vuex
的store
開始虎谢,我們將使用它來存儲/訪問我們的產(chǎn)品信息。
在本演示中曹质,我們將使用靜態(tài)數(shù)據(jù)婴噩,如果我們要取而代之,它仍然可以工作羽德。
注:關(guān)于Snipcart几莽,我們使用基本的JS代碼段注入購物車,并使用簡單的HTML屬性定義產(chǎn)品宅静。
2.1 構(gòu)建store
在src
中創(chuàng)建一個store
文件夾章蚣,包含以下3個文件:
- state.js - 定義我們的靜態(tài)產(chǎn)品數(shù)據(jù)
- getters.js - 定義
get
函數(shù),通過ID檢索產(chǎn)品 - index.js - 組合前兩個文件
//state.js
export const state = {
products: [
{
id: 1,
name: 'The Square Pair',
price: 100.00,
description: 'Bold & solid.',
image: 'https://snipcart.com/media/10171/glasses1.jpeg'
},
{
id: 2,
name: 'The Hip Pair',
price: 110.00,
description: 'Stylish & fancy.',
image: 'https://snipcart.com/media/10172/glasses2.jpeg'
},
{
id: 3,
name: 'The Science Pair',
price: 30,
description: 'Discreet & lightweight.',
image: 'https://snipcart.com/media/10173/glasses3.jpeg'
}
]
}
//getters.js
export const getters = {
getProductById: (state, getters) => (id) => {
return state.products.find(product => product.id == id)
}
}
//index.js
import Vue from 'vue'
import Vuex from 'vuex'
import { state } from './state.js'
import { getters } from './getters.js'
Vue.use(Vuex)
export default new Vuex.Store({
state,
getters
})
2.2 構(gòu)建路由器
我們將保持商店盡可能簡單:展示產(chǎn)品列表的首頁以及每個產(chǎn)品的詳細(xì)信息頁面姨夹。我們需要在路由器中注冊兩條路由來處理這些路由:
import VueRouter from 'vue-router'
import Vue from 'vue'
import ProductDetails from './../components/productdetails.vue'
import Home from './../components/home.vue'
Vue.use(VueRouter)
export default new VueRouter({
mode: 'history',
routes: [
{ path: '/products/:id', component: ProductDetails },
{ path: '/', component: Home },
]
})
我們還沒有創(chuàng)建這些組件纤垂,不用擔(dān)心,馬上就來磷账,;)
請注意峭沦,我們在VueRouter
聲明中使用了mode:'history'
。這一點很重要逃糟,否則我們的prerender
插件將不會工作吼鱼。其區(qū)別在于路由器將使用history API
而不是hashbang
來導(dǎo)航。
2.3 把所有東西組合在一起
現(xiàn)在绰咽,我們有了數(shù)據(jù)(store)和路由器菇肃,我們需要把他們注冊到應(yīng)用中。更新你的src/main.js
文件:
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import { sync } from 'vuex-router-sync'
import store from './store'
sync(store, router)
new Vue({
store,
router,
render: h => h(App)
}).$mount('#app')
很簡單吧剃诅!正如前面提到的巷送,vuex-router-sync
中的sync
方法從我們的store
中注入狀態(tài)到當(dāng)前的路由中。我們稍后再用矛辕。
3. 書寫Vue組件
有數(shù)據(jù)感覺真棒笑跛,但將它顯示出來將會更好付魔。我們即將用到的三個組件:
- Home - 展示產(chǎn)品列表
- Product - 單個產(chǎn)品信息,將被用在
Home
組件中 - ProductDetails - 產(chǎn)品詳情頁
他們將被包含在src/components
文件夾中飞蹂。
//Home.vue
<template lang="pug">
div(class="products")
div(v-for="product in products", class="product")
product(:product="product")
</template>
<script>
import Product from './../components/Product.vue'
export default {
name: 'home',
components: { Product },
computed: {
products(){
return this.$store.state.products
}
}
}
</script>
以上几苍,我們使用store
中的狀態(tài)來獲取我們的產(chǎn)品,并對它們進(jìn)行迭代陈哑,來渲染每一個產(chǎn)品妻坝。
//Product.vue
<template lang="pug">
div(class="product")
router-link(v-bind:to="url").product
img(v-bind:src="product.image" v-bind:alt="product.name" class="thumbnail" height="200")
p {{ product.name }}
button(class="snipcart-add-item"
v-bind:data-item-name="product.name"
v-bind:data-item-id="product.id"
v-bind:data-item-image="product.image"
data-item-url="/"
v-bind:data-item-price="product.price")
| Buy it for {{ product.price }}$
</template>
<script>
export default {
name: 'Product',
props: ['product'],
computed: {
url(){
return `/products/${this.product.id}`
}
}
}
</script>
通過路由器,我們鏈接到其他頁面(ProductDetails
)惊窖,來看看我們的最后一個組件:
//ProductDetails.vue
<template lang="pug">
div(class="product-details")
img(v-bind:src="product.image" v-bind:alt="product.name" class="thumbnail" height="200")
div(class="product-description" v-bind:href="url")
p {{ product.name }}
p {{ product. description}}
button(class="snipcart-add-item"
v-bind:data-item-name="product.name"
v-bind:data-item-id="product.id"
v-bind:data-item-image="product.image"
data-item-url="/"
v-bind:data-item-price="product.price")
| Buy it for {{ product.price }}$
</template>
<script>
export default {
name: 'ProductDetails',
computed: {
id(){
return this.$store.state.route.params.id
},
product(){
return this.$store.getters.getProductById(this.id)
}
}
}
</script>
這一節(jié)的邏輯要稍微復(fù)雜些:我們從路由中獲取當(dāng)前的ID刽宪,然后通過之前創(chuàng)建的getter
獲取相關(guān)的產(chǎn)品信息。
4. 創(chuàng)建App
我們開始使用剛才創(chuàng)建的組件界酒。
打開App.vue
文件圣拄,其內(nèi)容是腳手架(vue init webpack-simple
)生成的默認(rèn)內(nèi)容。我們來修改它:
<template lang="pug">
div(id="app")
TopContext
router-view
</template>
<script>
import TopContext from './components/TopContext.vue'
export default {
name: 'app',
components: { TopContext }
}
</script>
TopContext
組件不是很重要毁欣,它僅僅是一個header
庇谆。關(guān)鍵部分是router-view
:它將通過VueRouter
動態(tài)加載組件,而之前與之關(guān)聯(lián)的組件將被替換凭疮。
最后我們來更新一下index.html
饭耳。對于我們的用例來說,我們在src
中創(chuàng)建新的目錄static
执解,移動index.html
文件至static
并將其更新為如下內(nèi)容:
<!DOCTYPE html><html lang="en">
<head>
<meta charset="utf-8">
<title>vue-snipcart</title>
</head>
<body>
<div id="app">
</div>
<script src="/build.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<script src="https://cdn.snipcart.com/scripts/2.0/snipcart.js" data-api-key="YjdiNWIyOTUtZTIyMy00MWMwLTkwNDUtMzI1M2M2NTgxYjE0" id="snipcart"></script>
<link rel="stylesheet" type="text/css" />
</body>
</html>
你可以看到寞肖,我們在index.html
中添加了Snipcart
的必要腳本。如果將他們精細(xì)的劃分到各個組件之中代碼看起來會更加干凈材鹦,但逝淹,由于我們所有的View都需要它們,我們便這樣做了桶唐。
5. 使用Prerender插件處理Vue.js SEO
我們應(yīng)用中的所有內(nèi)容都是使用JS動態(tài)渲染的栅葡,這很不利于搜索引擎優(yōu)化(SEO):網(wǎng)頁中的異步內(nèi)容不能被“蜘蛛”(search engine bots)有效的識別并抓取,這樣的話尤泽,我們的電子商務(wù)網(wǎng)站錯過了所有有用的“網(wǎng)絡(luò)爬蟲”欣簇,這不是一個明智的選擇!
讓我們使用prerendering技術(shù)來為我們的Vue.js應(yīng)用程序帶來更多的SEO機(jī)會坯约。
相對于Vue的SSR(服務(wù)器端渲染)熊咽,prerendering則更容易使用。坦率地說闹丐,前者有些矯枉過正了横殴,除非你有大量的路由要處理。另外,這兩種技術(shù)在實現(xiàn)SEO層面所達(dá)到的效果是相似的衫仑。
預(yù)渲染將使我們能夠保持我們的前端作為一種快速梨与,輕量級的靜態(tài)網(wǎng)站,以便于“蜘蛛”進(jìn)行爬取文狱。
讓我們來看看如何使用它:轉(zhuǎn)到WebPack
配置文件粥鞋,在plugin
配置項中添加以下配置:
plugins: [
new CopyWebpackPlugin([{
from: 'src/static'
}]),
new PrerenderSpaPlugin(
path.join(__dirname, 'dist'),
[ '/', '/products/1', '/products/2', '/products/3']
)
]
好吧,它是如何工作的呢瞄崇?
CopyWebpackPlugin
將會復(fù)制static
文件夾中的文件到dist
文件夾中(只包含引用Vue App
的應(yīng)用程序的視圖)呻粹。然后,PrerenderSpaPlugin
使用PhantomJS
加載網(wǎng)頁的內(nèi)容苏研,并將結(jié)果作為我們的靜態(tài)資源等浊。
瞧!我們現(xiàn)在已經(jīng)為我們的Vue應(yīng)用程序提供了預(yù)渲染的楣富,SEO友好的產(chǎn)品頁面凿掂。
我們使用如下命令來進(jìn)行測試:
npm run build
這將生成一個dist
文件夾,其中包含生產(chǎn)環(huán)境所需的一切纹蝴。
其他重要的SEO因素
- 考慮為您的頁面添加適當(dāng)?shù)?code>meta標(biāo)記和站點地圖(sitemap)。您可以在“postProcessHtml”函數(shù)(prerender-spa-plugin插件的配置項)中了解有關(guān)
meta
標(biāo)記的更多信息踪少。 -
恰當(dāng)?shù)膬?nèi)容在現(xiàn)代SEO中起了重要作用塘安。建議您確保應(yīng)用程序中的內(nèi)容易于創(chuàng)建,編輯和優(yōu)化援奢。為了授權(quán)內(nèi)容編輯者兼犯,請考慮將
headless CMS
放入組合中并用來構(gòu)建真正的JAMstack。 - 現(xiàn)在集漾,HTTPS連接正式成為Google的排名因素切黔。我們在Netlify上托管這個演示,Netlify為我們提供了免費的SSL證書具篇。
- Mobile-first indexing和 mobile-friendliness也是排名的重要因素纬霞。確保您的移動體驗與桌面版一樣快速完整!
GitHub庫和在線演示
來吧驱显,這里是在線演示及代碼倉庫的地址诗芜!
總結(jié)
我之前使用過Vue,本教程的制作過程還是相當(dāng)順利的埃疫。我花了一個小時在Demo上伏恐,在使用CopyWebpackPlugin
時遇到了困難,好在我在他們的文檔中找到了答案栓霜。
我希望這篇文章能鼓勵開發(fā)人員在一些項目中開始使用Vue翠桦。就像我說的,您可以通過開發(fā)一個現(xiàn)有項目的一小部分來逐步地開始胳蛮,我認(rèn)為這絕對值得一試销凑。我們的開發(fā)主管正在使用Vue編寫最新的商業(yè)儀表盤功能愁铺,他非常喜歡Vue。另外闻鉴,如果配置正確茵乱,Vue完全可以驅(qū)動具有良好SEO結(jié)果的應(yīng)用程序。
如果你受到了啟發(fā)孟岛,可以看看Awesome-vue瓶竭,它包含了Vue示例和相關(guān)項目。
如果你真的喜愛Vue渠羞,cop some swag 或 support the creator
如果你覺得這篇文章有價值斤贰,請花一點時間分享到Twitter上。有什么遺漏或錯誤的次询?有關(guān)于Vue的荧恍?或其他框架處理SEO的一些想法?現(xiàn)在評論區(qū)是你的了屯吊!
End
作者:Maxime Laboissonniere
原文地址: Vue.js Tutorial: An Example to Build and Prerender an SEO-Friendly Site
譯者:jeneser
譯者GitHub:https://github.com/jeneser
版權(quán)聲明:自由轉(zhuǎn)載-非商用-非衍生-保持署名(創(chuàng)意共享3.0許可證)