公司项目,最初只为了实现先后端分离式开发,直接选择了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:目前整个项目还在持续测试和完善中,后续会将遇到的问题和解决方案不按期的更新,有问题或者不完善的地方随时欢迎各位小伙伴提意见,咱们一块儿探讨呀