vue、react对于开发单页应用来说带来了很好的用户的体验,但是同样有缺点,比如首页加载慢,白屏或SEO等问题的产生。为什么会出现这种情况呢?我们之前开发单页应用是这样开发的,比如首页 index.html页面或许是这样的:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>webpack+vue项目架构</title>
  5. <meta charset="utf-8">
  6. <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">
  7. </head>
  8. <body>
  9. <div id="app">
  10. </div>
  11. </body>
  12. </html>

在理解之前,我们还是和之前一样,先把我们的整个项目架构是一个什么样的,来简单的介绍下,方便有个简单的理解:

  1. ### 目录结构如下:
  2. demo1 # 工程名
  3. | |--- dist # 打包后生成的目录文件
  4. | |--- node_modules # 所有的依赖包
  5. | |--- app
  6. | | |---index
  7. | | | |-- views # 存放所有vue页面文件
  8. | | | | |-- home.vue
  9. | | | | |-- index.vue
  10. | | | | |-- java.vue
  11. | | | | |-- node.vue
  12. | | | |-- components # 存放vue公用的组件
  13. | | | |-- js # 存放js文件的
  14. | | | |-- app.js # vue入口配置文件
  15. | | | |-- router.js # 路由配置文件
  16. | |--- views
  17. | | |-- index.html # html文件
  18. | |--- webpack.config.js # webpack配置文件
  19. | |--- .gitignore
  20. | |--- README.md
  21. | |--- package.json
  22. | |--- .babelrc # babel转码文件

然后我们会通过webpack打包,将单页应用中的入口文件打包到一个js文件中去,如下配置:

  1. module.exports = {
  2. // 入口文件
  3. entry: {
  4. main: './app/index/app.js'
  5. },
  6. output: {
  7. filename: process.env.NODE_ENV === 'production' ? '[name].[contenthash].js' : 'bundle.js',
  8. // 将输出的文件都放在dist目录下
  9. path: path.resolve(__dirname, 'dist')
  10. },
  11. plugins: [
  12. new HtmlWebpackPlugin({
  13. hash: true, //为了开发中js有缓存效果,所以加入hash,这样可以有效避免缓存JS。
  14. template: './views/index.html' // 模版文件
  15. }),
  16. new ClearWebpackPlugin(['dist']),
  17. new ExtractTextPlugin("style.css"),
  18. ]
  19. };

基本的配置代码如上所示,它会把 我项目中 /views/index.html 当做模板页面,然后会把css文件样式和js文件会自动打包到index.html文件中,如下打包后的文件代码如下:

dist/index.html 源码如下:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>webpack+vue项目架构</title>
  5. <meta charset="utf-8">
  6. <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">
  7. <link href="style.css?26aab5a9debce5432af2" rel="stylesheet"></head>
  8. <body>
  9. <div id="app">
  10. </div>
  11. <script type="text/javascript" src="bundle.js?26aab5a9debce5432af2"></script></body>
  12. </html>

如上打包的主页index.html文件,我们可以看到,页面上只用一个 div, 然后有一个样式文件,和一个js文件。并没有其他的。
那么如上代码,我们很容易想到会出现如下几种缺点:第一:SEO不友好,也就是说,我通过百度或google搜索引擎搜索不到我网站的主页到,第二是:很容易出现白屏情况,为什么呢?因为我页面中的所有的内容都是通过 bundle.js这个动态加载进行,那么浏览器在加载及解析这段时间内,页面会一直是空白的。也就是我们说的白屏。当然对于我们首页来讲,加载也是非常慢的。因此为了解决这个问题,webpack中的有个插件 prerender-spa-plugin 可以解决上面这些问题。

我们再使用这个插件之前,我们来理解下我们之前是怎么样进行SEO优化的呢?我们很早很早之前,我们是使用 velocity 语法来编写页面,然后写完后把该页面部署到开发那边去,那么这样就要来回折腾了,并且我们前端开发成本也非常高。

那么第二种方式是使用SSR技术(服务器端渲染),比如Nuxt.js,最主要的思想是,是通过Node.js完成渲染逻辑,然后会将html视图直接返回给客户端。这样的方式也可以解决SEO的问题。但是对于开发成本还是比较大。比如需要考虑Node.js环境中的内存泄露,运行速度,并发压力等问题。并且开发成本增加,研发周期变长等这些问题。因此我们在webpack中有一种插件能将seo,首屏加载的问题可以解决掉了。
2. 如何使用 prerender-spa-plugin ?

1. 安装命令如下:

  1. npm install prerender-spa-plugin --save-dev

2. 在我们的webpack.config.js 需要配置如下:

  1. const PrerenderSPAPlugin = require('prerender-spa-plugin');
  2. const Renderer = PrerenderSPAPlugin.PuppeteerRenderer;
  3.  
  4. var config = {
  5. // 入口文件
  6. entry: {
  7. main: './app/index/app.js'
  8. },
  9. output: {
  10. filename: process.env.NODE_ENV === 'production' ? '[name].[contenthash].js' : 'bundle.js',
  11. // 将输出的文件都放在dist目录下
  12. path: path.resolve(__dirname, 'dist')
  13. },
  14. module: {
  15. // ....
  16. },
  17. resolve: {
  18. extensions: ['*', '.js', '.json', '.vue', '.styl']
  19. },
  20. devtool: 'cheap-module-eval-source-map',
  21. devServer: {
  22. // ....
  23. },
  24. plugins: [
  25. new HtmlWebpackPlugin({
  26. hash: true, //为了开发中js有缓存效果,所以加入hash,这样可以有效避免缓存JS。
  27. template: './views/index.html' // 模版文件
  28. }),
  29. new ClearWebpackPlugin(['dist']),
  30. new ExtractTextPlugin("style.css"),
  31. // .....
  32. ]
  33. }
  34. // 这里判断 如果是 正式环境打包的话,就使用该插件
  35. if (process.env.NODE_ENV === 'production') {
  36. config.plugins.push(
  37. new PrerenderSPAPlugin({
  38. staticDir: path.join(__dirname, '/dist'),
  39. // 列出需要预渲染的路由
  40. routes: [ '/home' ],
  41. renderer: new Renderer({
  42. inject: {
  43. foo: 'bar'
  44. },
  45. // 监听到自定事件时捕获
  46. renderAfterDocumentEvent: 'render-event'
  47. })
  48. })
  49. )
  50. }
  51. module.exports = config;

下面我们来理解下PrerenderSPAPlugin中的几个配置的含义:
staticDir:指的是预渲染输出的页面地址。
routes: 指的是需要预渲染的路由地址。
renderer:指的是所采用的渲染引擎是什么。目前用的是 V3.4.0 版本支持 PuppeteerRenderer。
inject:指的是预渲染过程中能拿到的值。是否需要渲染这部分代码,可以通过该值进行判断。比如如下代码:

  1. isshowRender() {
  2. if(window.__PRERENDER_INJECTED && window.__PRERENDER_INJECTED.foo =='bar') return;
  3. this.message = '我是测试预加载拦截';
  4. }

上面执行的方法中的代码是不会被渲染的。

renderAfterDocumentEvent的含义是监听 document.dispatchEvent 事件,决定什么时候开始预渲染。

3. 因此在我们的app.js 代码要改成如下:

  1. import Vue from 'vue';
  2. import Index from './views/index';
  3.  
  4. // 引入路由
  5. import router from './router';
  6.  
  7. new Vue({
  8. el: '#app',
  9. router: router,
  10. render: h => h(Index),
  11. mounted() {
  12. document.dispatchEvent(new Event('render-event'))
  13. }
  14. });

在实例化 new Vue的时候 在mounted生命周期中加上代码:document.dispatchEvent(new Event('render-event')),触发render-event事件进行渲染。

4. 路由配置(router.js):

  1. import Vue from 'vue';
  2. import VueRouter from 'vue-router';
  3.  
  4. // 引入组件
  5. import home from './views/home';
  6. import path from 'path';
  7.  
  8. // 告诉 vue 使用 vueRouter
  9. Vue.use(VueRouter);
  10.  
  11. const routes = [
  12. {
  13. path: '/home',
  14. name: 'home',
  15. component: resolve => require(['./views/home'], resolve),
  16. // 子路由
  17. children: [
  18. {
  19. path: 'java',
  20. name: 'java',
  21. component: resolve => require(['./views/java'], resolve)
  22. },
  23. {
  24. path: 'node',
  25. name: 'node',
  26. component: resolve => require(['./views/node'], resolve)
  27. }
  28. ]
  29. },
  30. {
  31. path: '*', // 其他没有的页面都重定向到 home页面去
  32. redirect: '/home'
  33. }
  34. ]
  35.  
  36. var router = new VueRouter({
  37. mode: 'history', // 访问路径不带井号 需要使用 history模式
  38. // base: path.resolve(__dirname, '/app/index'), // 配置单页应用的基路径
  39. routes: routes
  40. });
  41.  
  42. export default router;

如上路由配置据说要改成 模式变成 mode: 'history'这个模式,该插件才会渲染。

当我们认为配置一切成功的时候,我们在项目的根目录中运行 npm run build 的时候,发现打包报错了,如下提示:

Chromium revision is not downloaded. Run "npm install" or "yarn install" 提示这样的信息,我们可以根据这个信息去百度搜索下,看看是什么错误,百度结果后,他们的意思是需要我们安装 puppeteer 这个插件,但是安装这个插件也是有条件的。我们需要使用国内Chromium源.如下命令:

  1. npm install -g cnpm --registry=https://registry.npm.taobao.org
  2. cnpm i puppeteer

如下图所示:

一切安装完成后,我们再运行 npm run build 后可以看到在我们的dist目录下 会多生成一个 home文件夹了,该文件夹有一个 index.html文件,如下所示:

生成完成后,我们查看下 dist/home/index.html页面变成如下:

  1. <!DOCTYPE html><html><head>
  2. <title>webpack+vue项目架构</title>
  3. <meta charset="utf-8">
  4. <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">
  5. <link href="style.css?e8ed6db9e5869aa22ba2" rel="stylesheet"><script charset="utf-8" src="1.465ef3291c29aadf53df.js"></script></head>
  6. <body>
  7. <div id="app"><header><li class="router-link-exact-active router-link-active">Home</li> <a href="/home/java" class="">java</a> <a href="/home/node" class="">node</a></header> <div class="home-container"><h1>欢迎来到Home</h1> <p>我是Home组件</p> <!----></div></div>
  8. <script type="text/javascript" src="main.5aea82d6c5e9feeac132.js?e8ed6db9e5869aa22ba2"></script>
  9. </body></html>

如上代码,可以看到,我们的路由 /home 确实所有的静态页面都渲染了。因此实现SEO,页面不会白屏的问题已经解决了。
github源码查看

webpack中插件 prerender-spa-plugin 来进行SEO优化(二十四)的更多相关文章

  1. 二十四、Struts2中的UI标签

    二十四.Struts2中的UI标签 Struts2中UI标签的优势: 数据回显 页面布局和排版(Freemark),struts2提供了一些常用的排版(主题:xhtml默认 simple ajax) ...

  2. 设计模式学习(二十四):Spring 中使用到的设计模式

    设计模式学习(二十四):Spring 中使用到的设计模式 作者:Grey 原文地址: 博客园:设计模式学习(二十四):Spring 中使用到的设计模式 CSDN:设计模式学习(二十四):Spring ...

  3. webpack 中导入 vue 和普通网页使用 vue 的区别(四)

    一:在普通网页中使用 vue 使用 script 标签,引入 vue 包 在 ndex 页面中,创建一个 id 为 App 的 div 容器 通过 new Vue 得到一个 vue 实例 二:在 we ...

  4. webpack中如何编写一个plugin

    loader和plugin有什么区别呢?什么是loader,什么是plugin. 当我们在源代码里面去引入一个新的js文件或者一个其他格式的文件的时候,这个时候,我们可以借助loader去帮我们处理引 ...

  5. webpack 中,loader、plugin 的区别

    loader 和 plugin 的主要区别: loader 用于加载某些资源文件. 因为 webpack 只能理解 JavaScript 和 JSON 文件,对于其他资源例如 css,图片,或者其他的 ...

  6. 【Android Studio安装部署系列】二十四、Android studio中Gradle插件版本和Gradle版本关系

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 概述 在从Android Studio3.0.0版本升级到Android Studio3.0.1版本的时候,出现了一个问题,需要升级Gra ...

  7. YbSoftwareFactory 代码生成插件【二十四】:MVC中实现动态自定义路由

    上一篇介绍了 公文流转系统 的实现,本篇介绍下MVC下动态自定义路由的实现. 在典型的CMS系统中,通常需要为某个栏目指定个友链地址,通过指定友链地址,该栏目的地址更人性化.方便记忆,也有利用于搜索引 ...

  8. python接口自动化(二十四)--unittest断言——中(详解)

    简介 上一篇通过简单的案例给小伙伴们介绍了一下unittest断言,这篇我们将通过结合和围绕实际的工作来进行unittest的断言.这里以获取城市天气预报的接口为例,设计了 2 个用例,一个是查询北京 ...

  9. 二十四、详述 IntelliJ IDEA 中自动生成 serialVersionUID 的方法

    当我们用 IntelliJ IDEA 编写类并实现 Serializable(序列化)接口的时候,可能会遇到这样一个问题,那就是: 无法自动生成serialVersionUID. 而serialVer ...

随机推荐

  1. dnSpy 强大的.Net反编译软件

    作者:D.泡沫 一说起.net的反编译软件,大家首先想到的就是Reflector,ILSpy,dotPeek等等.而dnSpy同样是一款优秀的反编译软件,同时它是开源免费的.官方的描述是: dnSpy ...

  2. vnc server的安装

    vnc是一款使用广泛的服务器管理软件,可以实现图形化管理.我在安装vnc server碰到一些问题,也整理下我的安装步骤,希望对博友们有一些帮助. 1 安装对应的软件包 [root@centos6 ~ ...

  3. 从设计模式的角度看Java程序优化

    一.前言 Java程序优化有很多种渠道,比如jvm优化.数据库优化等等,但都是亡羊补牢的措施,如果能在设计程序架构时利用设计模式就把程序的短板解决,就能使程序更加健壮切容易维护迭代 二.常用的设计模式 ...

  4. 消息队列中间件(二)使用 ActiveMQ

    ActiveMQ 介绍 Active MQ 是由 Apache 出品的一款流行的功能强大的开源消息中间件,它速度快,支持跨语言的客户端,具有易于使用的企业集成模式和许多的高级功能,同时完全支持 JSM ...

  5. 改善 C# 的语言习惯(一) - 使用属性而不是可访问的数据成员(整理中)

    改善 C# 的语言习惯(一) - 使用属性而不是可访问的数据成员 序 为什么我们的程序运行得棒棒的,还要改呢?Why? 答:我们要让程序运行得更快,执行的效率更高,代码的可读性更强,维护的成本更低.. ...

  6. MySQL 笔记整理(3) --事务隔离,为什么你改了我还看不见?

    笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> 3) --事务隔离,为什么你改了我还看不见? 简单来说,事务就是要保证一组数据操作,要么全部成功,要么全部失败.在MySQL中,事务 ...

  7. 使用EMQ搭建MQTT服务器

    前言寒假的时候开始搭建mqtt服务器,一开始使用的是RabbitMQ,基于Erlang语言.但是RabbitMQ的本职工作是AMQP,MQTT只是他的一个插件功能,似乎有些大材小用,很多MQTT的功能 ...

  8. Android破解学习之路(九)—— 练手破解游戏集合

    1.魔塔50层 https://www.taptap.com/app/48859 选择需要购买的东西,之后在支付宝支付界面点击取消,即可支付成功 2.布布合丁丁 https://www.coolapk ...

  9. GNOME图形界面的基本操作

    成功登录进入CentOS系统之后,我们首先看到的桌面就是GNOME图形界面,下面来看一下相关的基本操作. 个性化设置 1,设置屏幕分辨率 进入菜单 2,更换桌面背景 进入下面菜单. 选择一张背景图片, ...

  10. Spring(一)JdbcTemplate的环境搭建

    1.建立一个项目,导入jar包(ioc aop dao 连接池 数据库驱动包)拷贝Spring容器对应的配置文件到src下 2.在配置文件中引入外部属性文件 3.配置数据源 4.配置JdbcTempl ...