公司項目,最初只爲了實現先後端分離式開發,直接選擇了vue框架進行開發,然而如今項目基本完成了,發現蜘蛛根本就抓取不到網站數據,搜索引擎搜出來,都是一片空白沒有數據,須要對項目作SEO優化。css
本人第一次接觸SEO的優化,徹底是零基礎,開啓了小白菜的SEO探索之旅,記錄一下一路的過程,方便本身回顧與你們的探討,若有不度之處,還請路過的大神指導一下。html
在查閱了一些資料後,常見的SEO優化有如下兩種:vue
在選擇預渲染模式仍是服務端渲染的模式時,簡單作了個demo進行了一下測試,因爲公司項目以檢索爲主的產品,後期須要蜘蛛抓取的界面太龐大,最終選擇用vue提供的nuxt.js框架去改造現有的項目。jquery
簡單寫一下prerender-spa-plugin客戶端預渲染的過程webpack
相關文檔可查看:prerender-spa-pluginios
直接在vue項目中,經過npm或者yarn進行安裝 ##### Yarn $ yarn add prerender-spa-plugin ##### NPM $ npm i prerender-spa-plugin -S
頂部引入:git
const PrerenderSPAPlugin = require('prerender-spa-plugin') const Renderer = PrerenderSPAPlugin.PuppeteerRenderer
在plugins中配置github
new PrerenderSPAPlugin({ //生成的預渲染html文件存放路徑 staticDir: path.join(__dirname, '../dist'), //須要預渲染的界面路徑(router) routes: ['/', '/index'], //跨域轉發配置,預渲染時獲取數據的api地址 server: { proxy: { '/api': { target: 'http://192.168.1.223:9002', changeOrigin: true, pathRewrite: { '^/api': '/' } } } }, //在必定時間後再捕獲頁面信息,使得頁面數據信息加載完成, 必選,否則界面依然會沒有數據 captureAfterTime: 5000, //忽略打包錯誤 maxAttempts: 10, renderer: new Renderer({ //可選,頁面在被搜索時的關鍵詞 inject: { foo: 'bar' }, //可選,無桌面系統可去掉 headless: false, //必選,‘render-event'必須與main.js中mounted下的配置名稱一致 renderAfterDocumentEvent: 'render-event' }) })
new Vue({ el: '#app', router, store, template: '<App/>', mounted () { document.dispatchEvent(new Event('render-event')) } })
// build時須要將'/'切換爲'./'
assetsPublicPath: './'web
到此整個預渲染模式,改造完成了。
這次demo中還沒有處理meta信息,須要配置meta信息的,可查看文檔vue-meta-info,這是一個基於vue 2.0的插件,它會讓你更好的管理你的vue頁面裏面的meta信息,詳細配置過程可參考:muwoo做者寫的「處理 Vue 單頁面 Meta SEO的另外一種思路」文章,裏邊整個過程寫的挺詳細的express
做爲新手,並不介意本身動手去配置環境,建議直接用nuxt.js提供的腳手架(create-nuxt-app)進行快速搭建項目,具體搭建過程可查看官網文檔:Nuxt.js
確保安裝了npx(npx在NPM版本5.2.0默認安裝了)
$ npx create-nuxt-app <項目名>
或者
$ yarn create nuxt-app <項目名>
以後會有一系列的選項:
在集成的服務器端框架之間進行選擇:
選擇您喜歡的UI框架:
官網都有詳細的講解,根據本身的需求選擇就行了,當安裝完後,項目就能夠直接運行了
npm run dev
問題
在啓動時遇到以下問題:
找了一圈,發現搭建項目時默認的element-ui版本過低,直接用npm uninstall element-ui
卸載當前版本,從新使用npm install element-ui@版本號
安裝便可,版本號使用2.7.2及以上都可
既然是對現有Vue項目的改造,就先看一下nuxt.js各目錄的功能及vue項目目錄的對比吧
├── assets // 資源文件。用於組織未編譯的靜態資源入LESS、SASS 或 JavaScript │ └── logo.jpg // 默認logo圖片 ├── components // 組件。用於本身編寫的Vue組件,好比滾動組件,日曆組件,分頁組件 │ └── AppLogo.vue // 默認logo組件 ├── layouts // 佈局。頁面都須要有一個佈局,默認爲 default。它規定了一個頁面如何佈局頁面。全部頁面都會加載在佈局頁面中的 <nuxt /> 標籤中。 │ └── default.vue // 默認模板頁面,相似mvc中的layout ├── middleware // 中間件。存放中間件。能夠在頁面中調用: middleware: 'middlewareName' 。 ├── pages // 頁面。一個 vue 文件即爲一個頁面。 │ └── index.vue // 默認首頁面 ├── plugins // 用於存放JavaScript插件的地方 │ └── element-ui.js // 上邊咱們安裝的UI框架 ├── static // 用於存放靜態資源文件,好比圖片,此類文件不會被 Nuxt.js 調用 Webpack 進行構建編譯處理。 服務器啓動的時候,該目錄下的文件會映射至應用的根路徑 / 下。 ├── store // 用於組織應用的Vuex 狀態管理。 ├── .editorconfig // 開發工具格式配置 ├── .eslintrc.js // ESLint的配置文件,用於檢查代碼格式 ├── .gitignore // 配置git不上傳的文件 ├── nuxt.config.js // 用於組織Nuxt.js應用的個性化配置,好比網站title,已便覆蓋默認配置 ├── package.json // npm包管理配置文件 └── README.md // 說明文檔
總體來看,目錄結構沒有太大的變更,區別比較大的就是router文件夾,nuxt.js項目中並無router路由的配置,這個就是 nuxt 框架的獨到之處,爲了能實現更好的SSR渲染,它使用的是根據頁面結構,自動路由,因此你的文件名,就是你的路由名稱,具體規則可查看官網文檔路由。
好了,nuxt項目啓動了,目錄結構也清楚了,接下來就是整個現有Vue項目的遷移了。
由於以前寫習慣了vue項目,並不太想改動目錄結構,就簡單粗暴的在nuxt目錄下新建了一個src目錄,將assets、components、layouts、middleware、pages、plugins以及store所有拖到了src中,src繼續保持與static同級,這樣整個項目結構跟vue沒有啥區別了。
最終的項目結構以下如:
PS:在添加了src後須要修改一下項目的啓動配置,在nuxt.config.js中修改srcDir爲'src/'
將vue中對應的頁面放到如今的nuxt目錄下對應的位置,注意一下vue文件的命名就能夠
vue項目中有用到一些全局配置文件和第三方文件,這部分js的話,直接放在plugins中,以擴展組件的形式在項目啓動時,掛載到全局中
將自定義的變量綁定到vue的原型中,Vue.use註冊到vue項目中,在vue文件中能夠直接用$config(自定義的變量名)調用該變量,而不須要再單獨去import了;最後用export default拋出該變量,是爲了在其餘js中使用。
PS:只有在vue頁面中使用該變量時不須要import,若是要在其餘的js中使用,仍是須要import進來的。
直接用npm install將第三方組件加載到項目中,在須要的vue界面用import載入就能夠,可是須要注意的一點是,第三方組件中可能用到了document、window等瀏覽器對象,而nuxt項目是須要在客戶端和服務端都要進行運行的,服務端並無window等對象,在服務端運行時會報錯,因此第三方組件也跟自定義組件相似的用plugins組件的形式載入比較安全,在plugins下單首創建一個同名的js文件,斷定是客戶端時再去加載該組件就好了。
在plugins中建立的js須要再項目啓動時註冊到項目中,也就是在nuxt.config.js中的plugins中進行配置
plugins: [ { src: '@/plugins/config.js', ssr: true }, { src: '@/plugins/d3.js', ssr: true } ]
全局樣式文件css,在nuxt.config.js配置文件中的css中引入
css: [ '@/assets/index.css' ]
項目比較着急,實在懶得用nuxt提供的方式再去改造這部分代碼,直接沿用了vue中mutations和actions方式,暫時項目並無出現問題(後期若是有問題再作修改
)
PS:若是該js中用到了window等瀏覽器的對象,加個process.client去判斷就行,其他的能夠不用修改
跟vue同樣先npm install element-ui --save,以後再plugins下新建一個element-ui.js文件,內容以下:
import Vue from 'vue' import Element from 'element-ui' import locale from 'element-ui/lib/locale/lang/en' Vue.use(Element, { locale })
而後再nuxt.config.js中進行配置:
plugins: [ { src: '@/plugins/element-ui', ssr: true } ], css: [ 'element-ui/lib/theme-chalk/index.css' ]
防止element-ui屢次被打包,在nuxt.config.js下的build中進行配置
build: { vendor: ['element-ui'] }
npm install jquery --save
而後再nuxt.config.js下的build中配置
build: { plugins: [ new webpack.ProvidePlugin({ '$': 'jquery', 'jQuery': 'jquery', 'window.jQuery': 'jquery' }) ] }
由於vue中用了axios,後期也沒有修改原來的api請求,因此就繼續使用了axios,直接npm install axios --save安裝,在須要使用的地方import便可。
若是在vue項目中已經封裝了axios,直接能夠把vue中寫的關於api的js都挪到plugins下,把export default axios拋出,再在nuxt.config.js下按照擴展的配置在plugins中添加就能夠正常使用了。
plugins: [ { src: '@/plugins/api/index.js', ssr: true } ],
正常客戶端的請求使用axios並無什麼問題,而在asyncData
預加載服務端請求時就比較麻煩了,在asyncData
請求中使用了nuxt默認集成的$axios,這個須要再nuxt.config.js下的modules中配置
modules: [ '@nuxtjs/axios', '@gauseen/nuxt-proxy' ],
跨域代理配置:
proxyTable: { '/api/': { target: '數據請求的ip地址', ws: false, pathRewrite: { '^/api': '/' } } },
asyncData下數據請求
單個請求:
async asyncData ({ app, params }) { let { data } = await app.$axios.get(url).then(res => {...}) }
多個請求:
async asyncData ({ app, query }) { // 請求帶參數時的寫法,query指的是當前訪問的url中攜帶的參數 let searchQuery = { type: query.searchTag, q: query.searchKeys, page: 1 } const [nounList, resultList] = await Promise.all([ app.$axios.get('請求的api地址', { params: { q: query.searchKeys }}), app.$axios.get(`/api/search/${searchQuery.type}`, { params: searchQuery }) ]) return { nounList: nounList.data, resultList: resultList.data } }
通常狀況下數據請求
this.$axios.get(url).then(res => {...})
PS:整個項目中能夠同時使用axios和nuxt默認集成的$axios,能夠根據本身的需求合理使用
到如今整個vue項目基本上都改造完了,能夠正常使用了。
第一次接觸nuxt,對其中的一些原理不是很懂,查閱了大量的文檔和別人的博客,雖然完成了此次的改造,但整個項目仍是存在一些瑕疵,還在不斷的改善中。
不一樣界面的title等設置,每一個vue界面都提供了head鉤子函數
head () { return { title: '百度--搜了個啥', meta: [ { hid: 'index', name: 'index', content: 'index page'} ] } },
PS:目前整個項目還在持續測試和完善中,後續會將遇到的問題和解決方案不按期的更新,有問題或者不完善的地方隨時歡迎各位小夥伴提意見,咱們一塊兒探討呀