使⽤vue全家桶制作博客⽹站
前⾯的话
  笔者在做⼀个完整的博客上线项⽬,包括、、和服务器配置。本⽂将详细介绍使⽤vue全家桶制作的博客⽹站
概述
  该项⽬是基于vue全家桶(vue、vue-router、vuex、vue SSR)开发的⼀套博客前台页⾯,主要功能包括⾸页显⽰、认证系统、⽂章管理、评论管理和点赞管理
【访问地址】
  或者可以直接扫描⼆维码访问
【项⽬介绍】
  该项⽬的内容以笔者⾃学前端的过程中写的600多篇博客为基础,对于同样学习前端的同学可能会有所帮助。许多博客都有直接可以操作的DEMO,对知识的理解可能会更直观
  采⽤移动优先的响应式布局,移动端、桌⾯端均可适配;字体⼤⼩使⽤em单位,桌⾯端的⽂字相应变⼤;移动端可使⽤滑屏操作,桌⾯端通过光标设置、⾃定义滚动条、回车确定等,提升交互体验
  全站采⽤服务器端渲染SSR的⽅式,有利于SEO,减少了⾸屏渲染时间;使⽤service worker和manifest实现了PWA⽅案的离线缓存和添加到桌⾯的功能
  根据HTML标签内容模型,使⽤语义化标签,尽量减少标签层级,尽量减少⽆语义的div标签
  CSS⼤量使⽤类选择器,尽量减少选择器层级,在vue组件中使⽤CSS module和postCSS,使⽤styleLint规范CSS代码,按照布局类属性、盒模型属性、⽂本类属性、修饰类属性的顺序编写代码,并使⽤order插件进⾏校验
  使⽤esLint规范JS代码,代码风格参照airbnb规范,所有命名采⽤驼峰写法,公共组件以Base为前缀,事件函数以on为前缀,异步函数以async为后缀,布尔值基本以do或is为前缀
  没有引⽤第三⽅组件库,如bootstrap或element组件,⽽是⾃⼰开发了项⽬中所需的公共组件。在common⽬录下,封装了头像、全屏、loading、遮罩、搜索框、联动选择等组件,⽅便开发
  使⽤配置数据,实现了数据和应⽤分离,以常量的形式存储在constants⽬录下
  使⽤了阿⾥云的短信模块,实现了短信验证功能
  该项⽬有两个隐藏彩蛋,⼀个是摇⼀摇功能,可以直接摇到后台页⾯,另⼀个是陀螺仪功能,上下晃动⼿机时,头像会进⾏旋转
  项⽬进⾏了代码优化,最终优化评分如下所⽰
功能演⽰
  主要功能包括⾸页显⽰、认证系统、⽂章管理、评论管理和点赞管理
【⾸页显⽰】
  ⾸页包括可拖拽轮播图、专题推荐、⽂章推荐和类别推荐
【认证系统】
  认证系统包括⽤户注册、⽤户登录、短信验证
  1、⽤户处于未登录态时,可以阅读⽂章,但不能点赞和评论,否则会弹出登录框
  2、⽤户注册
  3、⽤户登录
【⽂章管理】
  ⽂章管理包括浏览推荐⽂章、按类别筛选、⽂章搜索、按⽬录查看
  1、浏览推荐⽂章
  2、⽂章筛选
  3、⽂章搜索
  4、按⽬录查看
【点赞管理】
【评论管理】
  评论管理包括查看评论、添加评论、修改评论和删除评论
⽬录结构
  src⽬录下,包括assets(静态资源)、common(公共组件)、components(功能组件)、constants(常量配置)、router(路由)、store(vuex)和utils(⼯具⽅法)这7个⽬录
- assets // 存放静态资源,主要是图⽚
-imgs
  css.png // CSS⽂章背景图
    ...
- common // 存放公共组件
-SVG // 存放VUE图标组件
SVGAdd.vue // "添加到"按钮
SVGBack.vue // "返回"按钮
...
BaseArticle.vue // ⽂章组件
BaseAvatar.vue // 头像组件
.
..
- components // 存放功能组件
-Post // ⽂章组件
module.js //⽂章状态管理
Post.vue // ⽂章显⽰组件
PostContent.vue // ⽂章⽬录组件
PostList.vue // ⽂章列表组件
SearchPost.vue // 搜索⽂章组件
...
- constants // 存放常量配置
API.js // 存放API调⽤地址
-
router // 存放路由
index.js
- store // 存放vuex
index.js
- utils // 存放⼯具⽅法
async.js // axios⽅法
fnVarificate.js // 表单验证⽅法
util.js // 其他⼯具⽅法
【公共组件】
  没有引⽤第三⽅组件库,如bootstrap或element组件,⽽是⾃⼰开发了项⽬中所需的公共组件
  封装了⽂章组件、头像组件、返回组件、按钮组件、卡⽚组件、全屏组件、输⼊框组件、loading组件
、遮罩组件、搜索框组件、多⾏输⼊框组件、标题组件、⾯包屑组件、按钮组组件、反⾊按钮组件、密码框组件、包含检测的输⼊框组件和联动选择组件
BaseAdd.vue // "添加到"组件
BaseArticle.vue  // ⽂章组件
BaseAvatar.vue // 头像组件
BaseBack.vue // 返回组件
BaseButton.vue // 按钮组件
BaseCard.vue // 卡⽚组件
BaseFullScreen.vue // 全屏组件
BaseInput.vue  // 输⼊框组件
BaseLoading.vue  // loading组件
BaseMask.vue // 遮罩组件
BaseSearchBox.vue  // 搜索框组件
BaseTextArea.vue // 多⾏输⼊框组件
BaseTitle.vue  // 标题组件
BreadCrumb.vue // ⾯包屑组件
ButtonBox.vue  // 按钮组组件
ButtonInverted.vue // 反⾊按钮组件
InputPassword.vue  // 密码框组件
InputWithTest.vue // 包含检测的输⼊框组件
LinkageSelector.vue // 联动选择组件
【功能组件】
  按照功能来设置⽬录,如下所⽰
弹出框(Alert)
类别管理(Category)
评论管理(Comment)
主页(Home)
点赞管理(Like)
⽂章管理(Post)
页⾯尺⼨(Size)
公共头部(TheHeader)
⽤户管理(User)
整体思路
【全屏布局】
  使⽤设置⾼度的全屏布局⽅式,主要通过calc来实现
<div
id="root"
:class="$style.wrap"
:
>
...
<TheHeader :class="$style.header"/>
<main :class="$style.main">
<transition :name="transitionName">
<router-view :class="$uter" />
</transition>
</main>
</div>
.header {
height: 40px;
}
.main {
position: relative;
height: calc(100% - 40px);
overflow: auto;
}
【层级管理】
  项⽬的层级z-index,只使⽤0-3
  全屏的弹出框优化级最⾼,设置为3;侧边栏设置为2;页⾯元素默认为0,如有需要,要设置为1
【全局弹出层】
  在⼊⼝⽂件App.vue中设置全局的弹出层和loading,所有组件都可以共⽤
// App.vue
<template>
<div
id="root"
:class="$style.wrap"
:
>
<AlertWithLoading v-show="doShowLoading" />
<AlertWithText
v-show="alertText !== ''"
:text="alertText"
:onClick="() => {$storemit(HIDE_ALERTTEXT)}"
/>
<TheHeader :class="$style.header"/>
<main :class="$style.main">
<transition :name="transitionName">
<router-view :class="$uter" />
</transition>
</main>
</div>
</template>
【路由管理】
  vue-router使⽤静态路由表的形式对路由进⾏管理,虽然没有react-router-dom灵活,但⽅便寻,⼀⽬了然  按路由设置按需加载组件,并设置滚动⾏为
import Vue from'vue'
import Router from'vue-router'
Vue.use(Router)
export default function createRouter() {
return new Router({
mode: 'history',
routes: [
{
path: '/',
component: () => import(/* webpackChunkName:'home' */'@/components/Home/Home'),
name: 'home',
meta: { index: 0 }
},
{
path: '/posts',
component: () => import(/* webpackChunkName:'post' */'@/components/Post/PostList'),
name: 'postlist'
},
{
path: '/posts/search',
component: () => import(/* webpackChunkName:'post' */'@/components/Post/SearchPost'),
name: 'searchpost'
},
{
path: '/posts/:postid',
component: () => import(/* webpackChunkName:'post' */'@/components/Post/Post'),
name: 'post',
children: [
{
path: 'comments',
name: 'commentlist',
component: () => import(/* webpackChunkName:'comment' */'@/components/Comment/CommentList'),
children: [
{
path: 'add',
name: 'addcomment',
component: () => import(/* webpackChunkName:'comment' */'@/components/Comment/AddComment')              },
{
path: ':commentid/update',
name: 'updatecomment',
component: () => import(/* webpackChunkName:'comment' */'@/components/Comment/UpdateComment')              },
{
path: ':commentid/delete',
name: 'deletecomment',
component: () => import(/* webpackChunkName:'comment' */'@/components/Comment/DeleteComment')              }
]
}
]
},
{
path: '/categories',
component: () => import(/* webpackChunkName:'category' */'@/components/Category/CategoryList'),
name: 'categorylist'
},
{
制作svg图片path: '/categories/:number',
component: () => import(/* webpackChunkName:'category' */'@/components/Category/Category'),
name: 'category'
},
{
path: '/topics/:number',
component: () => import(/* webpackChunkName:'category' */'@/components/Category/CategoryTopic'),
name: 'topic'
},
// 注册
{
path: '/signup',
component: () => import(/* webpackChunkName:'user' */'@/components/User/AuthSignup'),
name: 'signup'
},
// 按⼿机号登录
{
path: '/signin_by_phonenumber',
component: () => import(/* webpackChunkName:'user' */'@/components/User/AuthSigninByPhoneNumber'),
name: 'signin_by_phonenumber'
},
// 按⽤户名登录
{
path: '/signin_by_username',
component: () => import(/* webpackChunkName:'user' */'@/components/User/AuthSigninByUsername'),
name: 'signin_by_username'
},
// ⽤户页⾯
{
path: '/users/:userid',
component: () => import(/* webpackChunkName:'user' */'@/components/User/UserDesk'),
name: 'user'
}
],
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
}
return { x: 0, y: 0 }
}
})
}