脚本宝典收集整理的这篇文章主要介绍了

Vue项目预渲染机制引入实践

脚本宝典小编觉得挺不错的,现在分享给大家,也给大家做个参考,希望能帮助你少写一行代码,多一份安全和惬意。
<p><code></p> <p>周末想顺便把已经做好静态页面的webApp项目做一下SEO优化,由于不想写蹩脚的<a href="https://ssr.vuejs.org/zh/" rel="nofollow noreferrer" target="_blank">SSR</a>代码,所以准备采用预渲染,本来想着网上有这么多<a href="https://ssr.vuejs.org/zh/#%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%AB%AF%E6%B8%B2%E6%9F%93-vs-%E9%A2%84%E6%B8%B2%E6%9F%93-ssr-vs-prerendering" rel="nofollow noreferrer" target="_blank">预渲染</a>的文章,随便找个来跟着做不就完了嘛,结果年轻的我付出了整个周末..... 这篇文章就记录一下最后是怎么配置的 T.T</p> <p>声明:</p> <ol> <li>以下配置只保留有必要的</li> <li>生成目录这里使用<code>base</code>代替,请自行修改</li> <li>vue-cli模板使用<code>webpack</code>,其他模板类推</li> <li><a href="http://sherlocked93.club/vue-style-codebase/" rel="nofollow noreferrer" target="_blank">webApp - 在线预览</a></li> <li><a href="https://github.com/SHERlocked93/vue-style-codebase/tree/prerender" rel="nofollow noreferrer" target="_blank">Github - 配置了预渲染的demo</a></li> </ol> <h2 id="articleHeader0">1. 简介与使用场景</h2> <p>我们知道SPA有很多优点,不过一个缺点就是对(不是Google的)愚蠢的搜索引擎的SEO不友好,为了照顾这些引擎,目前主要有两个方案:<strong>服务端渲染</strong>(Server Side Rendering)、<strong>预渲染</strong>(Prerending)。</p> <p>如果你只需要改善少数页面(例如 <code>/</code>, <code>/about</code>, <code>/contact</code> 等)的 <code>SEO</code>,那么你可能需要预渲染。无需使用 web 服务器实时动态编译 <a href="http://www.js-code.com/tag/html" title="HTML" target="_blank">HTML</a> (服务端渲染, SSR),而是使用预渲染方式,在构建时(build time)简单地生成针对特定路由的静态 <a href="http://www.js-code.com/tag/html" title="HTML" target="_blank">HTML</a> 文件。它主要使用 <a href="https://github.com/chrisvfritz/prerender-spa-plugin" rel="nofollow noreferrer" target="_blank">prerender-spa-plugin</a> 插件,其与SSR一样都可以加快页面的加载速度,并且侵入性更小,在已上线的项目稍加改动也可以轻松引入预渲染机制,而SSR方案则需要将整个项目结构推翻;</p> <div class="google-auto-placed ap_container" style="text-align: center; width: 100%; height: auto; clear: none;"><ins data-ad-format="auto" class="adsbygoogle adsbygoogle-noablate" data-ad-client="ca-pub-6330872677300335" data-adsbygoogle-status="done" style="display: block; margin: auto; background-color: transparent;"><ins id="aswift_4_expand" style="display:inline-table;border:none;height:175px;margin:0;padding:0;position:relative;visibility:visible;width:697px;background-color:transparent;"><ins id="aswift_4_anchor" style="display:block;border:none;height:175px;margin:0;padding:0;position:relative;visibility:visible;width:697px;background-color:transparent;"><iframe width="697" height="175" frameborder="0" marginwidth="0" marginheight="0" vspace="0" hspace="0" allowtransparency="true" scrolling="no" allowfullscreen="true" onload="var i=this.id,s=window.google_iframe_oncopy,H=s&amp;&amp;s.handlers,h=H&amp;&amp;H[i],w=this.contentWindow,d;try{d=w.document}catch(e){}if(h&amp;&amp;d&amp;&amp;(!d.body||!d.body.firstChild)){if(h.call){setTimeout(h,0)}else if(h.match){try{h=s.upd(h,i)}catch(e){}w.location.replace(h)}}" id="aswift_4" name="aswift_4" style="left:0;position:absolute;top:0;border:0px;width:697px;height:175px;"></iframe></ins></ins></ins></div> <p>访问预渲染出来的页面在访问时与<code>SSR</code>一样快,并且它将服务端编译<a href="http://www.js-code.com/tag/html" title="浏览关于“HTML”的文章" target="_blank" class="tag_link">HTML</a>的时机提前到了构建时,因此也降低了服务端的压力,如果你的服务器跟我的一样买的 <strong>1M1G1核</strong> 的小水管服务器 ( 穷 ),那么预渲染可能更适合你。不过SSR和预渲染的使用场景还是有较明显的区别的。预渲染的使用场景更多是简单的静态页面。服务端渲染适用于复杂、较大型、与服务端交互频繁的功能型网站,比如电商网站。</p> <h2 id="articleHeader1">2. 安装配置</h2> <p>首先来看看相关技术栈:vue^2.5.2、vue-router^3.0.1、vue-cli^2.9.6、webpack^3.6.0、prerender-spa-plugin^3.3.0</p> <h3 id="articleHeader2">2.1 安装</h3> <p>安装跟其他库一样</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="# Yarn $ yarn add prerender-spa-plugin -D # or NPM $ npm install prerender-spa-plugin --save-dev" title="" data-original-title="复制"></span> </div> </p></div> <pre class="bash hljs"><code class="bash"><span class="hljs-comment"># Yarn</span> $ yarn add prerender-spa-plugin -D <span class="hljs-comment"># or NPM</span> $ npm install prerender-spa-plugin --save-dev</code></pre> <h3 id="articleHeader3">2.2 前端配置</h3> <p>首先看看文件结构,用的是vue-cli2的webpack模板生成的文件结构</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="│ .babelrc │ index.html │ package.json │ README.md ├─build │ build.js │ check-versions.js │ utils.js │ vue-loader.conf.js │ webpack.base.conf.js │ webpack.dev.conf.js │ webpack.prod.conf.js ├─config │ dev.env.js │ index.js │ prod.env.js ├─src │ │ App.vue │ │ main.js │ │ │ ├─assets │ ├─components │ ├─router │ │ index.js │ ├─styles │ ├─utils │ └─views │ BigData.vue │ CompanyHonor.vue" title="" data-original-title="复制"></span> </div> </p></div> <pre class="bash hljs"><code class="bash">│ .<a href="http://www.js-code.com/tag/babel" title="浏览关于“babel”的文章" target="_blank" class="tag_link">babel</a>rc │ index.html │ package.json │ README.md ├─build │ build.js │ check-versions.js │ utils.js │ vue-loader.conf.js │ webpack.base.conf.js │ webpack.dev.conf.js │ webpack.prod.conf.js ├─config │ dev.env.js │ index.js │ prod.env.js ├─src │ │ App.vue │ │ main.js │ │ │ ├─assets │ ├─components │ ├─router │ │ index.js │ ├─styles │ ├─utils │ └─views │ BigData.vue │ CompanyHonor.vue</code></pre> <p>然后是router/index.js的配置,预渲染要求是histroy模式,有的文章说不需要history模式,这是错的,否则生成的页面都是同一个html。另外注意加上<code>base</code>否则如果你希望跳转到二级页面的<code>localhost/base/home</code>时候,在页面中点击<code>&lt;router-link to="/home"&gt;home&lt;/router-link&gt;</code>的时候会跳转<code>localhost/home</code></p> <p>// src/router/index.js</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="import Vue from 'vue' import Router from 'vue-router' Vue.use(Router) export default new Router({ mode: 'history', base: '/base/', routes: [...] }) " title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="js"><span class="hljs-keyword">import</span> <a href="http://www.js-code.com/tag/vue" title="浏览关于“Vue”的文章" target="_blank" class="tag_link">Vue</a> <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span> <span class="hljs-keyword">import</span> Router <span class="hljs-keyword">from</span> <span class="hljs-string">'vue-router'</span> Vue.use(Router) <span class="hljs-keyword"><a href="http://www.js-code.com/tag/export" title="浏览关于“export”的文章" target="_blank" class="tag_link">export</a></span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">new</span> Router({ <span class="hljs-attr">mode</span>: <span class="hljs-string">'history'</span>, <span class="hljs-attr">base</span>: <span class="hljs-string">'/base/'</span>, <span class="hljs-attr">routes</span>: [...] }) </code></pre> <p>然后是config,这里注意<code>assetsPublicPath</code>不是<code>./</code>,</p> <p>// config/index.js</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="const path = require(&quot;path&quot;) module.exports = { build: { index: path.resolve(__dirname, &quot;../base/index.html&quot;), assetsRoot: path.resolve(__dirname, &quot;..&quot;), assetsSubDirectory: &quot;base/static&quot;, assetsPublicPath: &quot;/&quot;, } }" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="js"><span class="hljs-keyword"><a href="http://www.js-code.com/tag/const" title="浏览关于“const”的文章" target="_blank" class="tag_link">const</a></span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">"path"</span>) <span class="hljs-built_in">module</span>.exports = { <span class="hljs-attr">build</span>: { <span class="hljs-attr">index</span>: path.resolve(__dirname, <span class="hljs-string">"../base/index.html"</span>), <span class="hljs-attr">assetsRoot</span>: path.resolve(__dirname, <span class="hljs-string">".."</span>), <span class="hljs-attr">assetsSubDirectory</span>: <span class="hljs-string">"base/static"</span>, <span class="hljs-attr">assetsPublicPath</span>: <span class="hljs-string">"/"</span>, } }</code></pre> <p>然后是插件的配置,是放在<code>prod</code>中的,因为只有build的时候会用</p> <p>// build/webpack.prod.conf.js</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="const path = require('path') const config = require('../config') const PrerenderSPAPlugin = require('prerender-spa-plugin') const Renderer = PrerenderSPAPlugin.PuppeteerRenderer const webpackConfig = merge(baseWebpackConfig, { new PrerenderSPAPlugin({ staticDir: config.build.assetsRoot, outputDir: path.join(config.build.assetsRoot, 'base'), indexPath: config.build.index, // 对应路由文件的path routes: [ '/', '/BigData', '/CompanyHonor' ], renderer: new Renderer({ headless: false, // 无桌面系统去掉 renderAfterDocumentEvent: 'render-event' }) }) })" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="js"><span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">'path'</span>) <span class="hljs-keyword">const</span> config = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../config'</span>) <span class="hljs-keyword">const</span> PrerenderSPAPlugin = <span class="hljs-built_in">require</span>(<span class="hljs-string">'prerender-spa-plugin'</span>) <span class="hljs-keyword">const</span> Renderer = PrerenderSPAPlugin.PuppeteerRenderer <span class="hljs-keyword">const</span> webpackConfig = merge(baseWebpackConfig, { <span class="hljs-keyword">new</span> PrerenderSPAPlugin({ <span class="hljs-attr">staticDir</span>: config.build.assetsRoot, <span class="hljs-attr">outputDir</span>: path.join(config.build.assetsRoot, <span class="hljs-string">'base'</span>), <span class="hljs-attr">indexPath</span>: config.build.index, <span class="hljs-comment">// 对应路由文件的path</span> routes: [ <span class="hljs-string">'/'</span>, <span class="hljs-string">'/BigData'</span>, <span class="hljs-string">'/CompanyHonor'</span> ], <span class="hljs-attr">renderer</span>: <span class="hljs-keyword">new</span> Renderer({ <span class="hljs-attr">headless</span>: <span class="hljs-literal">false</span>, <span class="hljs-comment">// 无桌面系统去掉</span> renderAfterDocumentEvent: <span class="hljs-string">'render-event'</span> }) }) })</code></pre> <p>注意了,如果你的项目是部署在linux/centOS之类没有桌面的系统,需要把<code>headless: false</code>去掉,如果centOS报没有找到lib的错,请参考 <a href="https://github.com/chrisvfritz/prerender-spa-plugin/issues/200" rel="nofollow noreferrer" target="_blank">issue-200</a> 的解决办法。</p> <p>另外注意上面一个<code>renderAfterDocumentEvent: 'render-event'</code>了么,这个意思是在<code>render-event</code>事件触发之后执行prerender,这个事件我们在main.js中mounted钩子触发</p> <p>// src/main.js</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="import Vue from 'vue' import App from './App' new Vue({ el: '#app', render: h => h(App),<br /> mounted() {<br /> document.dispatchEvent(new Event('render-event'))<br /> }<br /> })" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="js"><span class="hljs-keyword">import</span> Vue <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span> <span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">'./App'</span> <span class="hljs-keyword">new</span> Vue({ <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>, <span class="hljs-attr">render</span>: <span class="hljs-function"><span class="hljs-params">h</span> =&gt;</span> h(App), mounted() { <span class="hljs-built_in">document</span>.dispatchEvent(<span class="hljs-keyword">new</span> Event(<span class="hljs-string">'render-event'</span>)) } })</code></pre> <p>还有个配置要注意下在 build/utils.js 中的 <code>ExtractTextPlugin.extract</code> 的 <code>publicPath</code> ,否则一些vue中引用的资源会找不到</p> <p>// build/utils.js</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="ExtractTextPlugin.extract({ use: loaders, fallback: 'vue-style-loader', // publicPath: '../../' })" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="js">ExtractTextPlugin.extract({ <span class="hljs-attr">use</span>: loaders, <span class="hljs-attr">fallback</span>: <span class="hljs-string">'vue-style-loader'</span>, <span class="hljs-comment">// publicPath: '../../'</span> })</code></pre> <p>这时候执行<code>npm run build</code>就可以生成刚刚配置在<code>PrerenderSPAPlugin</code>插件中routes中的页面html了,这过程中会一闪而过的短暂打开chromium浏览器,不用管。</p> <p>最后生成的目录树:</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="│ index.html ├─BigData │ index.html ├─CompanyHonor │ index.html └─static ├─css ├─fonts ├─img └─js" title="" data-original-title="复制"></span> </div> </p></div> <pre class="bash hljs"><code class="bash">│ index.html ├─BigData │ index.html ├─CompanyHonor │ index.html └─static ├─css ├─fonts ├─img └─js</code></pre> <p>最后如果希望进一步优化生成出来页面的SEO,可以配合 <a href="https://github.com/muwoo/vue-meta-info" rel="nofollow noreferrer" target="_blank">vue-meta-info</a> 这个网上有很多文章,就不赘述了</p> <h3 id="articleHeader4">2.3 nginx配置</h3> <p>顺便贴一下nginx配置</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="server { listen 80; server_name localhost; root /nginx-1.14.0/html; error_page 500 502 503 504 /50x.html; location ~ ^/base/ { try_files $uri $uri/ /base/index.html; } location = /50x.html { root html; } }" title="" data-original-title="复制"></span> </div> </p></div> <pre class="nginx hljs"><code class="nginx"><span class="hljs-section">server</span> { <span class="hljs-attribute">listen</span> <span class="hljs-number">80</span>; <span class="hljs-attribute">server_name</span> localhost; <span class="hljs-attribute">root</span> /nginx-<span class="hljs-number">1</span>.<span class="hljs-number">14</span>.<span class="hljs-number">0</span>/html; <span class="hljs-attribute">error_page</span> <span class="hljs-number">500</span> <span class="hljs-number">502</span> <span class="hljs-number">503</span> <span class="hljs-number">504</span> /50x.html; <span class="hljs-attribute">location</span> <span class="hljs-regexp">~ ^/base/</span> { <span class="hljs-attribute">try_files</span> <span class="hljs-variable">$uri</span> <span class="hljs-variable">$uri</span>/ /base/index.html; } <span class="hljs-attribute">location</span> = /50x.html { <span class="hljs-attribute">root</span> html; } }</code></pre> <hr> <p>网上的帖子大多深浅不一,甚至有些前后矛盾,在下的文章都是学习过程中的总结,如果发现错误,欢迎留言指出~</p> <blockquote> <p>参考:</p> <ol> <li><a href="https://ssr.vuejs.org/zh/" rel="nofollow noreferrer" target="_blank">Vue.js 服务器端渲染指南</a></li> <li><a href="https://blog.csdn.net/csdn_yudong/article/details/80769424" rel="nofollow noreferrer" target="_blank">Vue 服务端渲染 &amp; 预渲染</a></li> <li><a href="https://github.com/chrisvfritz/prerender-spa-plugin/issues/215#" rel="nofollow noreferrer" target="_blank">vue-cli v3, can't get it to work.. issue #215</a></li> <li><a href="https://github.com/chrisvfritz/prerender-spa-plugin/issues/200" rel="nofollow noreferrer" target="_blank">unable to start Puppeteer. Failed to launch ... issue #200</a></li> <li><a href="https://github.com/chrisvfritz/prerender-spa-plugin/issues/176" rel="nofollow noreferrer" target="_blank">When assetsPublicPath is set, the... issue #176</a></li> <li><a href="https://segmentfault.com/a/1190000015753352?utm_source=tag-newest">vue.js vue-router history模式路由,域名二级目录子目录nginx配置</a></li> <li><a href="https://zhuanlan.zhihu.com/p/29148760?group_id=890298677627879424" rel="nofollow noreferrer" target="_blank">处理 Vue 单页面 Meta SEO的另一种思路</a></li> </ol> </blockquote> <p></code></p>

总结

以上是脚本宝典为你收集整理的

Vue项目预渲染机制引入实践

全部内容,希望文章能够帮你解决

Vue项目预渲染机制引入实践

所遇到的程序开发问题,欢迎加入QQ群277859234一起讨论学习。如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典网站推荐给程序员好友。 本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。

80%的人都看过