vuePress继承使用

376 阅读4分钟

由于最近公司需要处理大量官网,且内容不会经常变化,因此选择vuePress,通过只要更改md文件内容,就可以实现不同官网的布置

介绍

vuePress是一个极简静态网站生成器,它包含由vue驱动的主题系统和插件。它有个主题系统,可以设置自定义主题和默认主题,通过服务端渲染,生成对应的HTML文件,只需要设置好需要展示的md文件内容就可以

快速使用

  • 创建目录
mkdir vuepress-starter && cd vuepress-starter
  • 初始化
yarn init # npm init
  • 安装
yarn add -D vuepress # npm install -D vuepress
  • 创建md文件
mkdir docs && echo '# Hello VuePress' > docs/README.md
  • 设置配置文件
{
  "scripts": {
    "dev": "vuepress dev docs",
    "build": "vuepress build docs"
  }
}
  • 启动
yarn dev # npm rundev

目录结构

image.png

主要遵循“约定大于配置”的原则

目录主要集中在.vuepress中

其中比较重要且常用的目录如下:

  • docs/.vuepress/theme: 用于存放本地主题。
  • docs/.vuepress/public: 静态资源目录。
  • docs/.vuepress/components: 该目录中的 Vue 组件将会被自动注册为全局组件。
  • docs/.vuepress/config.js: 配置文件的入口文件,也可以是 YML 或 toml

页面路径规则:

文件的相对路径页面路由地址
/README.md/
/guide/README.md/guide/
/config.md/config.html

默认主题

可以访问标题链接,可以查阅默认主题配置,非常详细,基本可以通过配置文件docs/.vuepress/config.js,在里面设置好对应的配置,就可以实现各个功能(导航栏、侧边栏、搜索等等),由于默认主题配置,不是本文主要介绍,所以快速跳过

第三方主题

可以访问标题链接

  • 安装
 npm install vuepress-theme-simple --save-dev
 or
 yarn add vuepress-theme-simple --dev
  • 引用设置主题 在docs/.vuepress/config.js设置
// .vuepress/config.js
module.exports = {
  theme: 'simple',
  themeConfig: {
    // 请参考文档来查看所有可用的选项。
  }
}

主题继承开发

由于需要快速上线,而且无需全部开发属于自己的主题库,因此需要用高效的方法,可以使用主题继承来实现,只需额外更改属于自己的风格组件就好。

主题继承覆盖原理: 所有的组件都必须使用 @theme 别名来引用其他组件

<script>
import Navbar from '@theme/components/Navbar.vue'
// ...
</script>

在这样的前提下,当你在子主题中同样的位置创建一个 Navbar 组件时: @theme/components/Navbar.vue 会自动地映射到子主题中的 Navbar 组件,当你移除这个组件时,@theme/components/Navbar.vue 又会自动恢复为父主题中的 Navbar 组件。

快速入手主题小demo

  • 1、.vuepress/theme/index.js创建
// .vuepress/theme/index.js
module.exports = (themeConfig, ctx) => {
  return {
     // ...
  }
}
  • 2、docs/.vuepress/config.js文件配置
module.exports = {
  title: 'hello vuepress',
  description: 'vupress',
  port: 3334,
  theme: require('./theme/index'),
  themeConfig: {
    copyright: `Copyright © 2018-present`
  }
}
  • 3、docs/.vuepress/theme/layouts/Layout.vue创建
<template>
  <div class="theme-container">
    <SideBar/>
    <Home/>
  </div>
</template>
<script>
import Home from '../components/Home'
import SideBar from '../components/SideBar'
export default {
  components: {
    Home,
    SideBar
  }
}
</script>
  • 4、docs/.vuepress/theme/components/Home.vue
<template>
  <div>Home</div>
</template>
  • 5、docs/.vuepress/theme/components/SideBar.vue
<template>
  <div>
    SideBar {{$site.title}}
    <FooterBar/>
  </div>
</template>
<script>
import FooterBar from './FooterBar'
export default {
  components: {
    FooterBar
  }
}
</script>
  • 6、docs/.vuepress/theme/components/FooterBar.vue
<template>
  <div>FooterBar
    <p class="copyright" v-html="$themeConfig.copyright"></p>
  </div>
</template>

基于继承,项目应用

继承开发目录结构:

image.png

官方默认主题库源码,可以在基础上,提取有用信息,通过继承快速二次开发

其中主要涉及到的theme目录,主要操作如下:

  • 1、docs/.vuepress/config.js文件配置
module.exports = {
  port:3334,
  title: 'Luckin Game', //需要替换
  head: [
    ['link', { rel: 'icon', href: '/game6.png' }],
  ],
  locales: {
    '/': {
      lang: 'en-US', 
      title: 'Luckin Game', //需要替换
      description: 'h5 and mobile games publisher', //需要替换
      footer:{
        copyright:"Copyright © 2018-present",//需要替换
        police:""//需要替换
      },
      header:{
        backgroundImage:"/game6.png"//需要替换
      }
    },
    '/zh/': {
      lang: 'zh-CN',
      title: 'Luckin Game',//需要替换
      description: '致力于手游发行,助力游戏开发者',//需要替换
      footer:{
        copyright:"Copyright © 2018-present",//需要替换
        police:""//需要替换
      },
      header:{
        backgroundImage:"/game6.png"//需要替换
      }      
    }
  },  
  themeConfig: {
    background:'background:#2F2F2F;',//底部背景颜色,可以替换
    gtagId:'xxxxxxx',//谷歌gaid,需要替换
    search: false,
    smoothScroll: true,
    locales: {
      '/': {
        lang: 'en-US',
        title: 'Luckin Game',//需要替换
        description: 'Committed to mobile game publishing',//需要替换
        nav: [
          { text: 'Home', link: '/' },
          { text: 'Privacy', link: '/privacy/' },
          { text: 'Terms', link: '/terms/' },
        ]
      },
      '/zh/': {
        lang: 'zh-CN',
        title: 'Luckin Game',//需要替换
        description: '致力于手游发行,助力游戏开发者',//需要替换
        nav: [
          { text: '主页', link: '/zh/' },
          { text: '隐私政策', link: '/zh/privacy/' },
          { text: '服务条款', link: '/zh/terms/' },
        ]
      }
    },       
  },

  plugins: ['@vuepress/back-to-top']
}
  • 2、docs/.vuepress/theme/index.js 进行配置继承
// .vuepress/theme/index.js
module.exports = {
  extend: '@vuepress/theme-default'
}
  • 3、layouts 文件夹新增layout.vue文件,主要通过调整官方主题,然后嵌套自己的组件,就可以实现快速修改部分风格布局等效果
<template>
  <div
    class="theme-container"
    :class="pageClasses"
    @touchstart="onTouchStart"
    @touchend="onTouchEnd"
  >
  <!-- Google tag (gtag.js) -->
  <script async :src="`https://www.googletagmanager.com/gtag/js?id=${$themeConfig.gtagId}`"></script>
  <script>
    window.dataLayer = window.dataLayer || [];
    function gtag(){dataLayer.push(arguments);}
    gtag('js', new Date());
    gtag('config', '{{$themeConfig.gtagId}}');
  </script>  
    <Navbar
      v-if="shouldShowNavbar"
      @toggle-sidebar="toggleSidebar"
    />
    <div
      class="sidebar-mask"
      @click="toggleSidebar(false)"
    />
    <Sidebar
      :items="sidebarItems"
      @toggle-sidebar="toggleSidebar"
    >
      <template #top>
        <slot name="sidebar-top" />
      </template>
      <template #bottom>
        <slot name="sidebar-bottom" />
      </template>
    </Sidebar>
    <Home v-if="$page.frontmatter.home" />
    <Page
      v-else
      :sidebar-items="sidebarItems"
    >
      <template #top>
        <slot name="page-top" />
      </template>
      <template #bottom>
        <slot name="page-bottom" />
      </template>
    </Page>
    <Footer/>
  </div>
</template>
<script>
import Home from '@theme/components/Home.vue'
import Navbar from '@theme/components/Navbar.vue'
import Page from '@theme/components/Page.vue'
import Sidebar from '@theme/components/Sidebar.vue'
import Footer from '@theme/components/Footer.vue'
import { resolveSidebarItems } from '../util'
export default {
  name: 'Layout',
  components: {
    Home,
    Page,
    Sidebar,
    Navbar,
    Footer
  },
  data () {
    return {
      isSidebarOpen: false
    }
  },
  computed: {
    shouldShowNavbar () {
      const { themeConfig } = this.$site
      const { frontmatter } = this.$page
      if (
        frontmatter.navbar === false
        || themeConfig.navbar === false) {
        return false
      }
      return (
        this.$title
        || themeConfig.logo
        || themeConfig.repo
        || themeConfig.nav
        || this.$themeLocaleConfig.nav
      )
    },
    shouldShowSidebar () {
      const { frontmatter } = this.$page
      return (
        !frontmatter.home
        && frontmatter.sidebar !== false
        && this.sidebarItems.length
      )
    },
    sidebarItems () {
      return resolveSidebarItems(
        this.$page,
        this.$page.regularPath,
        this.$site,
        this.$localePath
      )
    },
    pageClasses () {
      const userPageClass = this.$page.frontmatter.pageClass
      return [
        {
          'no-navbar': !this.shouldShowNavbar,
          'sidebar-open': this.isSidebarOpen,
          'no-sidebar': !this.shouldShowSidebar
        },
        userPageClass
      ]
    }
  },
  mounted () {
    this.$router.afterEach(() => {
      this.isSidebarOpen = false
    })
  },
  methods: {
    toggleSidebar (to) {
      this.isSidebarOpen = typeof to === 'boolean' ? to : !this.isSidebarOpen
      this.$emit('toggle-sidebar', this.isSidebarOpen)
    },
    // side swipe
    onTouchStart (e) {
      this.touchStart = {
        x: e.changedTouches[0].clientX,
        y: e.changedTouches[0].clientY
      }
    },
    onTouchEnd (e) {
      const dx = e.changedTouches[0].clientX - this.touchStart.x
      const dy = e.changedTouches[0].clientY - this.touchStart.y
      if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 40) {
        if (dx > 0 && this.touchStart.x <= 80) {
          this.toggleSidebar(true)
        } else {
          this.toggleSidebar(false)
        }
      }
    }
  }
}
</script>
<style lang="stylus">
  .go-to-top{
    color:#000 !important;
  }
  .go-to-top:hover {
    color: #FA6400 !important;
  }
  .header-anchor{
    display: none !important;
  }
  .theme-default-content{
    overflow-x: hidden !important;
  }
</style>
  • 4、通过引用自己的footer.vue和navbar.vue组件,就可以实现替换官方模板这两个组件

navbar.vue:

<template>
  <header class="new-navbar" :style="`background:linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.6)),url(${$localeConfig.header.backgroundImage}) repeat center/ contain;`">
    <div class="center">{{$localeConfig.title}}</div>
    <div class="center">{{$localeConfig.description}}</div>
  </header>
</template>
<style lang="stylus">
.new-navbar
    .center 
        display: flex;
        justify-content: center;
        align-items: center;
    padding:20px 0;
    width: 100%;  
    min-height:200px;
    display: flex;
    justify-content: center;
    align-items: center;   
    flex-direction: column;
    text-align: center;   
    div 
        flex: 1;
        color: #fff;
        width: 100%;
    div:first-child 
        font-size: 80px;
        font-weight: 800;
    div:last-child 
        font-size: 26px;
        font-weight: 400;
</style>

footer.vue:

<template>
    <div class="new_footer" :style="$themeConfig.background">
        <div class="header">
            <div class="center">{{$localeConfig.title}}</div>
            <div>
                <DropdownLink v-if="languages" :item="languages" />
            </div>
        </div>
        <div class="end center">
            <div>{{$localeConfig.description}}</div>
            <div class="center">
                <a v-for="(v,i) in links" :key="i" :href="v.link" style="color:#fff; margin: 0 20px;">{{v.text}}</a>
            </div>
            <div class="center" :style="`${$localeConfig.footer.police ?'':'justify-content: right;'}`">
                 {{$localeConfig.footer.copyright}}
            </div>
            <div class="center" v-if="$localeConfig.footer.police">
                 {{$localeConfig.footer.police}}
            </div>
        </div>
    </div>
</template>
<script>
import DropdownLink  from '@theme/components/DropdownLink.vue'
export default {
    name: 'Footer',
    components: {
        DropdownLink
    },    
    computed: {
    links () {
      return this.$themeLocaleConfig?.nav ||[]
    },
    languages () {
      const { locales } = this.$site
      if (locales && Object.keys(locales).length > 1) {
        const currentLink = this.$page.path
        const routes = this.$router.options.routes
        const themeLocales = this.$site.themeConfig.locales || {}
        const languageDropdown = {
          text: this.$themeLocaleConfig.selectText || 'Languages',
          ariaLabel: this.$themeLocaleConfig.ariaLabel || 'Select language',
          items: Object.keys(locales).map(path => {
            const locale = locales[path]
            const text = themeLocales[path] && themeLocales[path].label || locale.lang
            let link
            // Stay on the current page
            if (locale.lang === this.$lang) {
              link = currentLink
            } else {
              // Try to stay on the same page
              link = currentLink.replace(this.$localeConfig.path, path)
              // fallback to homepage
              if (!routes.some(route => route.path === link)) {
                link = path
              }
            }
            return { text, link }
          })
        }
        return languageDropdown
      }
      return null
    },    
  },    
}
</script>
<style lang="stylus">
.new_footer
    a:hover {
        color: #FA6400 !important;
    }
    .center 
        display: flex;
        justify-content: center;
        align-items: center;    
    min-height 200px;
    background #2F2F2F;
    display: flex;
    flex-direction: column; 
    padding:30px 150px;   
    .header 
        height 40%;
        display: flex;
        color:#fff;
        div:first-child
            flex:1; 
            font-size: 36px;
            font-weight: 800;
            justify-content: flex-start;          
        div:last-child
            flex:1; 
            text-align: right;
            position: relative;
            display: flex; 
            justify-content: flex-end; 
        .dropdown-wrapper .nav-dropdown .dropdown-item
            font-size: 20px;
        .dropdown-wrapper .dropdown-title, .dropdown-wrapper .mobile-dropdown-title   
            color: #fff; 
        .dropdown-wrapper .nav-dropdown .dropdown-item a
            color:#000;
        .dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active   
            color: #FA6400 !important; 
        .dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active::after  
            border-left: 5px solid #FA6400;                                  
    .end 
        margin-top: 20px;
        min-height 60%;
        color: #fff;
        align-items: baseline; 
        flex-wrap: wrap;
        div
            margin: 10px 0;                  
        div:nth-child(1)   
            width: 300px
        div:nth-child(2)
            flex: 1;
        div:nth-child(3)   
            min-width: 300px
        div:nth-child(4) 
            min-width: 300px               

@media (max-width: $MQMobile)
  .new_footer
    a:hover {
        color: #FA6400 !important;
    }
    .center 
        display: flex;
        justify-content: center;
        align-items: center;
    min-height 200px;
    background #2F2F2F;
    display: flex;
    flex-direction: column; 
    padding: 20px 10px;        
    .header 
        display: flex;
        flex-direction: column;
        height: auto;   
        div:first-child
            flex:1; 
            font-size: 36px;
            font-weight: 800;
            justify-content: center;         
        div:last-child
            flex:1; 
            text-align: right;
            position: relative;
            display: flex; 
            justify-content: center;
        .dropdown-wrapper .nav-dropdown .dropdown-item
            font-size: 20px;
        .dropdown-wrapper .dropdown-title, .dropdown-wrapper .mobile-dropdown-title   
            color: #fff; 
        .dropdown-wrapper .nav-dropdown .dropdown-item a
            color:#000;
        .dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active   
            color: #FA6400 !important; 
        .dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active::after  
            border-left: 5px solid #FA6400;  
        .dropdown-wrapper .nav-dropdown .dropdown-item::marker 
            display: none;                                        
    .end 
        display: flex;
        flex-direction: column;
        height: auto;
        color: #fff;
        div
            margin: 10px 0; 
            text-align: center;                             
        div:nth-child(1)   
            width: 300px
        div:nth-child(2)
            flex: 1;
        div:nth-child(3)   
            min-width: 300px
        div:nth-child(4) 
            min-width: 300px                  
</style>

最终效果如图所示 官网 20221105172207.png

参考

vuepress

vuepress相关资源