经过三天的学习和尝试,LeeDua终于搭起了人生中第一个博客。关于vuepress的原因在关于中已经说明。下文简单记录一下实现中的一些收获和感想。
# 项目结构
官方文档对vuepress项目结构的说明还是比较清楚的,但还是有几点是我摸索后才明白的。
- theme文件夹的位置不必是固定的,可以在config.js中theme:folder指定,所以其实文件夹叫什么都可以
- GlobalLayout.vue必须在theme\layouts下才有效,经过尝试目前在index.js中指定globalLayout无效
- 使用外部vue component时在enhanceApp.js中注册后即可全局使用
import live2d from 'live2d-vue'
export default ({
Vue,
options,
router,
siteData
}) => {
Vue.use(live2d)
}
# 路由映射和页面渲染
- vuepress默认任何一个文件夹someFolder下如果有README.md则为这个文件夹生成一条路由
someFolder
,以README.md - 可以在md frontmatter中指定
layout:vue-component
,未指定时使用的是layout.vue而不是folderName.vue - blog插件提供了额外的路由聚合功能,即根据frontmatter中的某个prop生成路由
/prop/
,在该路径下整合所有有此prop的post - GlobalLayout
- 理解blog的UI调用逻辑的难点就只有GlobalLayout,其他都只是标准的vue-components
- 以下是官网上给出的使用方式,实际上是对GlobalLayout源代码的截取。
<!-- themePath/layouts/GlobalLayout.vue -->
<template>
<div id="global-layout">
<header><h1>Header</h1></header>
<component :is="layout"/>
<footer><h1>Footer</h1></footer>
</div>
</template>
<script>
export default {
computed: {
layout () {
if (this.$page.path) {
if (this.$frontmatter.layout) {
// You can also check whether layout exists first as the default global layout does.
return this.$frontmatter.layout
}
return 'Layout'
}
return 'NotFound'
}
}
}
</script>
- 使用过程中可能导致组件注册的相关问题。对比源代码,容易发现少了检查组件是否已注册的过程。
<template>
<component :is="layout" />
</template>
<script>
import Vue from 'vue'
import { setGlobalInfo } from '@app/util'
export default {
name: 'GlobalLayout',
computed: {
layout () {
const layout = this.getLayout()
setGlobalInfo('layout', layout)
return Vue.component(layout)
}
},
methods: {
getLayout () {
if (this.$page.path) {
const layout = this.$page.frontmatter.layout
if (layout && (this.$vuepress.getLayoutAsyncComponent(layout)
|| this.$vuepress.getVueComponent(layout))) {
return layout
}
return 'Layout'
}
return 'NotFound'
}
}
}
</script>
- 正确的使用方式如下
import GlobalLayout from '@app/components/GlobalLayout.vue'
export default {
components: {
DefaultGlobalLayout: GlobalLayout,
},
- 读了源代码后就容易理解GlobalLayout做了什么。GlobalLayout先查frontmatter里是否指定了layout,如果有则使用,如果没有则使用Layout,这也默认使用Layout.vue的原因。
# dynamic layout
改变窗口大小可以发现中间的post的width在不同阈值下跳变,sidebar和小埋也在页面窄到一定程度时就消失,转而由topbar和footer替换。实现起来就是media query多个阈值的调整。
- 具体实现时sidebar的宽度实际上是动态计算得到的,因为sidebar是fixed的,不能通过float left直接紧挨着post,所以要每次resize时要动态计算post右边缘到页面左边缘的距离作为left的偏移值。
onResize(event) {
if(this.$refs["pagelist"]==undefined)return;
let rect = this.$refs["pagelist"].getBoundingClientRect();
let window_width = window.innerWidth || document.documentElement.clientWidth ||
document.body.clientWidth;
let sw = window_width - rect.right - 30;
this.sidebarWidth = sw;
if(window_width < 840 && this.isMobileHeaderOpen == false){
this.isMobileHeaderOpen = true;
}
else if(this.isMobileHeaderOpen && window_width > 840){
this.isMobileHeaderOpen = false;
}
}
# font-family
第一次接触font-family。font-family
往往由一组不同的字体组成,每种字体对应不同unicode 不同font-weight下的情况。明白这一点就容易进行字体配置了。
# 其他
- stylus真香,我竟然这么久以来一直不依赖preprocessor写css(太懒,之前听说过也读过sass scss但是以为只是改改语法就没系统学习过),简直就是憨憨
- 学习了pwa和service-worker的相关概念,发现一个不错的pwa视频系列教程
- 写这篇博客中发现在vue代码块前加-会导致vue stack exceed limit网页崩溃,算是一个bug?
- vuepress的热刷新
- 需要手动重启的场景其实都是vuepress build过程中的一次性步骤,即只有在build时才会做的事。如生成路由、抽取frontmatter分类、增删或重命名文件(根据功能容易想象vuepress内部维护了文件与页面、component相对引用的映射结构),以及某些情况下由于某些插件的版本冲突可能导致热刷新失败
- 背景的实现
- background-image是不能设置opacity的
- 一种实现是
body::before
设置img并设置opacity - 我使用的实现是使用blend
background-image url('/bg_h4.png')
background-repeat no-repeat
background-size cover
background-position center
background-attachment fixed
background-color rgba(#fff,0.97)
background-blend-mode lighten
- 最后,附上blog的源代码。多余的component和blog插件没用上的功能相关的代码并没有删去。vuepress其实提供了extend一个theme的功能但我并没有使用,一是想看看default theme是怎么写的,二是觉得要改的地方比较多,extend还不如直接改方便。