欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

vue practice 1.1 企业网站--prerender-spa-插件预渲染

最编程 2024-04-16 10:18:51
...

(获取本节完整代码 GitHub/chizijijiadami/practice1.1
今天我们直接用入门教程的代码https://github.com/chizijijiadami/hand-to-hand来码一个企业官网。

0、写在前面

相关说明:这里相比入门教程 入门基础开发—手把手教你用vue开发,增加了css预处理器stylusstylus官网 )、vuex状态管理vuex官网 )、页面预渲染Prerenderingvue服务端渲染官网 ),并且结合了 vue-meta-info 达成网站搜索排名SEO优化的目的。其实这个项目里vuex不必要使用,面包屑导航可以用本地存储localStorage去写,只是为了后面的教程我们提前在这里提一下。
企业名称:大米工厂
页面模块:首页、旗下产品、新闻动态、关于我们、联系我们、招才纳贤
页面结构:

1、编写页面组件

拿到入门教程代码运行之前,我们修改一下package.json文件,这样运行项目后会自动打开浏览器访问。

"scripts": {
-    "serve": "vue-cli-service serve --mode development",
-    "serve-pro": "vue-cli-service serve --mode production ",
-    "serve-test": "vue-cli-service serve --mode testing ",
+    "serve": "vue-cli-service serve --mode development --open",
+    "serve-pro": "vue-cli-service serve --mode production --open",
+    "serve-test": "vue-cli-service serve --mode testing --open",
     "build": "vue-cli-service build --mode production",
     "build-dev": "vue-cli-service build --mode development",
     "build-test": "vue-cli-service build --mode testing",
     "lint": "vue-cli-service lint"
  },

● 添加头部底部组件
src>components>Header.vue

<template>
  <header>
    <div>
      <!--自己找张图片做logo-->
      <router-link class="logo" to="/index"><img src="../assets/images/logo.png" /></router-link>
      <nav>
        <router-link to="/index" active-class="actived">首页</router-link>
        <router-link to="/products" active-class="actived">产品</router-link>
        <router-link to="/news" active-class="actived">动态</router-link>
        <router-link to="/about" active-class="actived">关于我们</router-link>
        <router-link to="/contact" active-class="actived">联系我们</router-link>
        <router-link to="/recruit" active-class="actived">招贤纳士</router-link>
      </nav>
    </div>
  </header>
</template>

src>components>Footer.vue

<template>
  <footer class="content-center">大米工厂</footer>
</template>

● 添加各页面, 删掉pages/List文件以及子文件
src>pages>Products>index.vue

<template>
 <div class="content-center">Products</div>
</template>

其他页面内容如Products/index.vue,只是将 div 标签中的 Product 改为相应页面名,方便识别,完成后,pages文件结构如下图:

● 给新加页面添加路由
src>router>index.js

  -      {
  -             path: '/list',
  -             component: _import('List/index'),
  -             children: [
  -                 {
  -                     path: 'detail',
  -                     component: _import('List/Detail/index')
  -                 },
  -                 {
  -                     path: 'feature',
  -                     component: _import('List/Feature/index')
  -                 }
  -             ]
  -         },
  +         {
  +              path: '/products',
  +              component: _import('Products/index')
  +          }, {
  +              path: '/news',
  +              component: _import('News/index')
  +          }, {
  +              path: '/about',
  +              component: _import('About/index')
  +          }, {
  +              path: '/contact',
  +              component: _import('Contact/index')
  +          }, {
  +              path: '/recruit',
  +              component: _import('Recruit/index')
  +          }

另外将路由的模式改为 history

export default new Router({
+    mode:'history',
    routes: [
      ......
    ]

删掉文件src/components/Helloworld.vue,改写src/App.vue
src/App.vue

<template>
  <div id="app">
-    <header>
-      <nav><router-link to="/index">index</router-link><router-link to="/list">list</router-link></nav>
-    </header>
-    <router-view></router-view>
+    <Header />
+    <div class="content">
+      <router-view></router-view>
+    </div>
+    <Footer />
  </div>
</template>
<script>
+ import Header from "./components/Header";
+ import Footer from "./components/Footer";
export default {
   name: "App",
+  components: {
+    Header,
+    Footer
+  }
};
</script>

到此,浏览器里可以看到网站很丑的样子,下面我们就给网站加样式。


2、结合stylus加样式 stylus官网

这里在入门篇中提到过,可以减少css代码的编写等。
● 安装代码

yarn add stylus stylus-loader -D   //-D是因为只在开发的时候用到

● 给vs code添加stylus格式扩展,搜索stylus,安装language-stylus,另外这里为了阻止按【Shift+Alt+F】自动化格式代码后,css样式自动加上大括号等符号,我们添加Manta's Stylus Supremacy。

安装完成后需要设置一下防止自动添加样式符号,如下:
或者

找到如下截图的三个,去掉前面的勾勾,按【Ctrl+s】保存一下就可以了,如果没生效,可以先看看有没有保存好,如果是保存了的仍然没有生效,软件重启大法试一下。


● 分别添加头部、底部和中间内容最外部div的样式
src>components>Header.vue

+ <style lang="stylus" scoped>
+ header
+   width 100%
+   background-color #000000
+   height 72px
+ div
+   display flex
+   width 1200px
+   margin 0 auto
+ .logo
+   display inline-block
+   height 72px
+ nav a, nav a:visited
+   display inline-block
+   width 100px
+   text-align center
+   color white
+   font-size 16px
+   line-height 72px
+ a:hover, a.actived
+   background-color #444
+ </style>

src>components>Footer.vue

+ <style lang="stylus" scoped>
+ footer
+   text-align center
+   line-height 60px
+ </style>

src>App.vue

+ <style lang="stylus" scoped>
+ .content
+   min-height calc(100vh - 72px - 60px)
//给中间内容一个最小高宽撑起页面会好看一点
+ </style>

删掉src/assets/styles/style.css,新建src/assets/styles/style.styl

a,a:active,a:hover,a:visited
    text-decoration none
.content-center
  width: 1200px
  margin 0 auto

修改main.js文件的引入

  import './assets/styles/reset.css'
- import './assets/styles/style.css'
+ import './assets/styles/style.styl'

到这里我们一个简单的企业官网开发框架就搭起来了,不过vue开发的SPA应用不利于SEO,因为只是比较简单的几个信息也,我们用预渲染的方式去解决,服务端渲染会在下个实践1.2里实现。

3、prerender-spa-plugin 实现预渲染

● 安装 prerender-spa-plugin

 yarn add  prerender-spa-plugin -D

● 修改 vue.config.js

+ const PrerenderSPAPlugin = require('prerender-spa-plugin');
+ const Renderer = PrerenderSPAPlugin.PuppeteerRenderer;
  const path = require('path')
  const resolve = dir => path.resolve(__dirname, dir)
  module.exports = {
      publicPath: './',
      chainWebpack: config => {
          config.resolve.alias
              .set("@", resolve('src'))
      },
+      configureWebpack: config => {
+        if (process.env.NODE_ENV !== 'production') return;
+        return {
+            plugins: [
+                new PrerenderSPAPlugin({
+                    staticDir: path.join(__dirname, 'dist'),
+                    routes: ['/index', '/products', '/news', '/about', '/contact', '/recruit'],   //需要预渲染的路由名称
+                    renderer: new Renderer({
+                        inject: {
+                            foo: 'bar'
+                        },
+                        headless: false, //打包时浏览器进行渲染自动检测用的,false代表使用
+                        renderAfterDocumentEvent: 'render-event' //挂载后激发的事件名称,在main.js中使用
+                    })
+                }),
+            ],
+        };
+    }
}

修改 main.js

new Vue({
  router,
  render: h => h(App),
+  mounted() {
+    document.dispatchEvent(new Event('render-event'));
+  }
}).$mount('#app')

● 打包部署

yarn build

会发现有一闪而过的窗口,那是SEO自动测试可以忽略。成功后dist目录如下:

部署访问后发现好多文件路径都不对

原来是我们忽略了路径问题,由于参与预渲染的页面在dist中都单独生成了独立的文件夹,里面的页面引入文件时应该是以 ../ 开头,而不是我们设置的 publicPath:'./'。但是如果把 publicPath:'./',改成 publicPath:'../'重新启动程序,就会发现本地开发路径又不对了,如图多了两点

这时候我们的环境文件就该上场了,还记得.env.development和.env.production两个文件么,修改它们。
.env.development

  NODE_ENV = 'development'
  VUE_APP_BASE_API = 'http://192.168.0.100'
+ VUE_APP_PUBLIC_PATH = './'

.env.production

  NODE_ENV = 'production'
  VUE_APP_BASE_API = 'http://192.168.0.100'
+ VUE_APP_PUBLIC_PATH = '../'

vue.config.js

-    publicPath:'../',
+    publicPath: process.env.VUE_APP_PUBLIC_PATH,

重新启动程序,本地开发访问正常,接着试下重新打包部署,可以看到也OK了。但是浏览器标签里的标题却没有变化,这就要用到 vue-meta-info 了。
i
4、结合 vue-meta-info 进行SEO优化

● 安装

yarn add  vue-meta-info -D

● 使用
修改src/main.js

  import Vue from 'vue'
  import App from './App.vue'
  import router from './router'
+ import MetaInfo from 'vue-meta-info'
  Vue.config.productionTip = false
+ Vue.use(MetaInfo)

由于页面比较多,我们对meta信息进行统一管理,新建src/common/util/metaInfo.js文件
src/common/util/metaInfo.js

export default {
    Index: {
        title: "首页-大米工厂",
        meta: [{
            name: 'keyWords',
            content: 'content'
        }, {
            name:'description',
            content: 'content',
        }]
    },
    Products:{
        title: "产品-大米工厂",
        meta: [{
            name: 'keyWords',
            content: 'content'
        }, {
            name:'description',
            content: 'content',
        }]
    },
    News:{
        title: "新闻动态-大米工厂",
        meta: [{
            name: 'keyWords',
            content: 'content'
        }, {
            name:'description',
            content: 'content',
        }]
    },
    About:{
        title: "关于我们-大米工厂",
        meta: [{
            name: 'keyWords',
            content: 'content'
        }, {
            name:'description',
            content: 'content',
        }]
    },
    Contact:{
        title: "联系我们-大米工厂",
        meta: [{
            name: 'keyWords',
            content: 'content'
        }, {
            name:'description',
            content: 'content',
        }]
    },
    Recruit:{
        title: "招贤纳士-大米工厂",
        meta: [{
            name: 'keyWords',
            content: 'content'
        }, {
            name:'description',
            content: 'content',
        }]
    }
}

修改src/pages/Index/index.vue

<script>
+ import metaInfo from '@/common/util/metaInfo'
export default {
    name:"IndexIndex",
-    created(){    //这个输出没用了,我们给删掉
-      console.log(process.env.VUE_APP_BASE_API,'输出VUE_APP_BASE_API');
-    },                
+    metaInfo:metaInfo.Index,
}
</script>

修改src/pages/Products/index.vue

+ <script>
+ import metaInfo from '@/common/util/metaInfo'
+ export default {
+     metaInfo:metaInfo.Products,
+ }
</script>

其他页面依次修改引入使用。改完以后自己点点看观察浏览器标签里标题变化,再打包部署一下试试。

5、结合 vuex 写面包屑导航

在使用之前我们先做一些准备工作,给 关于我们 添加两个子路由。
新建 src>pages>About>Feature>index.vue

<template>
  <div>about-feature</div>
</template>
<style lang='stylus' scoped>
div
  background-color #e68cab
</style>

新建 src>pages>About>Team>index.vue

<template>
  <div>about-team</div>
</template>
<style lang='stylus' scoped>
div
  background-color #87bf8d
</style>

修改src>pages>About>index.vue

<template>
-  <div class="content-center">About</div>
+  <div class="content-center">
+    <p>About</p>
+    <p>
+      <router-link to="/about/feature">Feature</router-link><router-link to="/about/team">Team</router-link>
+    </p>
+    <router-view></router-view>
+  </div>
</template>
<script>
import metaInfo from "@/common/util/metaInfo";
export default {
  metaInfo: metaInfo.About
};
</script>
+ <style  lang='stylus' scoped>
+ div p a
+   padding 15px
+ </style>

修改src>router>index.js,添加访问路径,并且给每个路由配上meta信息,用于面包屑的显示,这里只贴了关于我们的,其余的自己加一下,metaInfo.js里的自己加加看。

      {
             path: '/about',
             component: _import('About/index'),
+            meta: {
+                title: "关于我们"
+            },
+            children: [
+                {
+                    path: 'feature',
+                    component: _import('About/Feature/index'),
+                    meta: {
+                        title: "特点"
+                    }
+                },
+                {
+                    path: 'team',
+                    component: _import('About/Team/index'),
+                    meta: {
+                        title: "团队"
+                    }
+                }
+            ]
        }

到这里可以看到about页面可以切换显示新添加的两个页面了,下面我们正式开始。

安装

yarn add vuex

使用
新建 src>data>store>index.js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
    state: {
        hasCrumbs: true,       //  是否需要面包屑导航
        indexPage: [    // 面包屑导航首页
            {
                path: '/index',
                meta: {
                    title: '首页',
                }
            }
        ],
        crumbsList: []     //面包屑导航数组
    },
    mutations: {
        SET_CRUMBS(state, list) {         //设置面包屑导航数组
            if (list.length == 1 && list[0].path == '/index') {
                state.crumbsList = []
            } else {
                state.crumbsList = state.indexPage.concat(list)
            }
        }
    },
    actions: {
        setCrumbs({ commit }, list) {         //分发设置面包屑导航数组
            commit("SET_CRUMBS", list)
        }
    }
})
export default store

修改main.js,引入状态文件

  import Vue from 'vue'
  import App from './App.vue'
  import router from './router'
+ import store from './data/store'
  import MetaInfo from 'vue-meta-info'
  Vue.config.productionTip = false
  Vue.use(MetaInfo)
  //样式
  import './assets/styles/reset.css'
  import './assets/styles/style.styl'

 new Vue({
   router,
+  store,
   render: h => h(App),
   mounted() {
     document.dispatchEvent(new Event('render-event'));
   }
}).$mount('#app')

新建面包屑导航组件 src>components>Crumbs.vue,在页面中使用

<template>
  <div class="content-center crumbs">
   <!-- 下面的三目运算主要针对最后一个路由,它不需要被点击,后面也不需要 > 符号-->
    <a
      v-for="(item,index) in getCrumbs"
      :class="index!=getCrumbs.length-1?'':'crumb-end'"
      :key="index"
      :href="index!=getCrumbs.length-1?item.path:'javascript:void(0)'"
    >
     <!-- item.meta.title中的meta就是我们在路由文件router/index.js中设置的 -->
      {{item.meta.title}}
      <span>{{index!=getCrumbs.length-1?" > ":""}}</span>
    </a>
  </div>
</template>
<script>
export default {
  computed: {
    getCrumbs() {
      return this.$store.state.crumbsList;
    }
  }
};
</script>
<style lang='stylus' scoped>
.crumbs
  line-height 30px
  font-size 14px
.crumbs a
  color #2973b7
.crumbs span
  color #333
.crumbs .crumb-end
  color #333
  cursor default
</style>

修改App.vue,引入使用面包屑导航

<template>
  <div id="app">
    <Header />
+   <!-- getCrumbs.length为0时,不显示面包屑导航 -->
+   <Crumbs v-show="getHasCrumbs&&getCrumbs.length" />
+  <!-- 有面包屑导航的时候添加class content-has-crumbs是为了控制有无面包屑导航时的页面最小高度 -->
    <div class="content" :class="{'content-has-crumbs':getHasCrumbs&&getCrumbs.length}">
      <router-view></router-view>
    </div>
    <Footer />
  </div>
</template>
<script>
  import Header from "./components/Header";
  import Footer from "./components/Footer";
+ import Crumbs from "./components/Crumbs";
export default {
  name: "App",
  components: {
    Header,
    Footer,
+    Crumbs
  },
+  computed: {
+    getHasCrumbs() {
+      return this.$store.state.hasCrumbs;
+    },
+    getCrumbs() {
+      return this.$store.state.crumbsList;
+    }
+  }
};
</script>
<style lang="stylus" scoped>
 .content
   min-height calc(100vh - 72px - 60px)
+ .content-has-crumbs
+   min-height calc(100vh - 72px - 60px - 30px)
</style>

利用全局导航守卫 router.afterEach 路由属性$route.matched 传入面包屑数组 state.crumbsList 中。
新建scr>common>guards>index.js

import router from '@/router'
import store from '@/data/store'
router.afterEach(to => {
    store.dispatch('setCrumbs', to.matched)  //状态文件actions里面定义的方法
})

修改main.js

 //样式
 import './assets/styles/reset.css'
 import './assets/styles/style.styl'
+ //导航守卫
+ import './common/guards'

现在点击页面看看就有面包屑导航了,如果整个项目都不需要,把 state.hasCrumbs 改为false 就好了。
稍微小结一下,vuex的初次使用就是(1)新建状态文件(2)main.js中引入(3)页面组件中使用。

6、补充说明

关于多级路由的预渲染,例如有个路径 list/detail ,那么在打包后detail页面的文件夹会在list文件夹里面,这样detail文件夹里面的页面引入的资源路径又是缺少了一层的,这里给出的解决方案是,专门写一个处理路径的js,在打包后运行。这里js做的处理应当是将属于那个子文件的资源移动到跟它同级的目录,同时这些资源需要在命名上配合。例如detail页面的资源名里都加上detail以区分。
这个js功能目前也没十分必要去用到,先留在这里,后面有时间就来补上具体代码。

感谢阅读,喜欢的话点个赞吧:)
更多内容请关注后续文章。。。

一、vue入门基础开发—手把手教你用vue开发
二、vue+ElementUI开发后台管理模板—布局
三、vue+ElementUI开发后台管理模板—功能、资源、全局组件
四、vue+ElementUI开发后台管理模板—方法指令、接口数据
五、vue+ElementUI开发后台管理模板—精确到按钮的权限控制

vue3 + vite + ElementPlus开发后台管理模板