[Blog]前端Vue代码分析

Vue代码分析

  • 项目结构
    • assets目录
    • components目录
      • Header.vue代码分析
        • 代码分析
    • router目录
      • index.js
      • $route
      • $routes
      • $router
      • 代码分析
    • store目录
      • localStorage和sessionStorage
      • store属性
      • 代码分析
    • views目录
      • 代码分析
        • Login.vue
      • Blogs.vue
      • BlogsDetails.vue
      • BlogdEdit.vue
    • main.js
    • App.vue
    • axios.js
    • permission.js

项目结构

  • 打包完后的项目结构如图所示
    [Blog]前端Vue代码分析_第1张图片

  • /public目录下有 favicon.ico (标签图标),index.html (当前项目唯一的页面)

  • /src下的目录结构如图所示

[Blog]前端Vue代码分析_第2张图片

assets目录

  • assets里面的资源会被webpack打包进代码,static里面的资源就直接引用了(如下所示)
  • 一般在static里放一些类库的文件,assets放属于项目的静态资源文件
    [Blog]前端Vue代码分析_第3张图片

components目录

/components用来存放小组件

在这里插入图片描述

Header.vue代码分析

	//template,可以把列表项放入template标签中,然后进行批量渲染

//



  • //template,可以把列表项放入template标签中,然后进行批量渲染

  • /class属性代表一个选择器,可以理解为一个标识用来识别特定标签

  • < scrtpt>

    • $data: vue实例观察的数据对象,Vue实例代理了对其data对象属性的访问
    • $el: vue实例使用的根DOM元素
    • methods:标签绑定的事件函数
    • created() 生命周期方法

代码分析

  • Header.vue的代码比较简单,一个标题标签显示欢迎内容,一个头像组件显示头像,一个小标题标签显示名字;用两个el-divider组件分隔开了三个el-link文字标签

  • 主页标签的超链接是/blogs 博客列表

  • 发表文章的超链接是/blogs/add

  • 退出这个超链接,在!haslogin时显示为登录,绑定的请求是/login,haslogin时绑定的超链接是/logout

  • 值得注意的是

    methods: {
            logout() {
              const _this = this
              _this.$axios.get("/logout", {
                headers: {
                  "Authorization": localStorage.getItem("token")
                }
              }).then(res => {
                _this.$store.commit("REMOVE_INFO")
                _this.$router.push("/login")
      
              })
            }
          },
         -  这个方法中,通过axios发出get请求,在headers中设置了一个字段Authorization": localStorage.getItem("token“)
         - 发送完请求后再store执行REMOVE_INFO,删除掉用户信息,再页面路由到登录界面
    

[Blog]前端Vue代码分析_第4张图片

router目录

存放路由脚本文件(配置路由 url链接 与 页面组件的映射关系)
在这里插入图片描述

index.js

[Blog]前端Vue代码分析_第5张图片
vue-router配置路由,使用vue的异步组件技术,可以实现按需加载

每一个路由规则都是一个对象

  • path表示监听哪一个路由链接地址
  • componrnt,表示路由是前面匹配到的path,则显示component对应的那个组件
  • name, 给这个页面路径定义一个名字,当在页面进行跳转的时候也可以使用名字跳转,具有唯一性

$route

表示当前的路由信息(表示一条路由),包含了当前URL解析得到的信息(包含当前路径,参数,query对象等)

  • 1.$route.path** 字符串,对应当前路由的路径,总是解析为绝对路径,如"/foo/bar"。

  • 2.$route.params** 一个 key/value 对象,包含了 动态片段 和 全匹配片段, 如果没有路由参数,就是一个空对象。

  • 3. r o u t e . q u e r y ∗ ∗ 一 个 k e y / v a l u e 对 象 , 表 示 U R L 查 询 参 数 。 例 如 , 对 于 路 径 / f o o ? u s e r = 1 , 则 有 route.query** 一个 key/value 对象,表示 URL 查询参数。 例如,对于路径 /foo?user=1,则有 route.querykey/valueURL/foo?user=1route.query.user == 1, 如果没有查询参数,则是个空对象

$routes

一组路由,把上面的每一条路由组合起来,形成一个数组

$router

$router对象是全局路由的实例,是router构造方法的实例,实例方法包括:

  • push(和< router-link :to="…">是等同的)

    1. 字符串this.$router.push(‘home’)

    2. 对象this.$router.push({path:‘home’})

  • go( 页面路由跳转 )

    前进或者后退this.$router.go(-1) // 后退

代码分析

import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '../views/Login.vue'
import Blogs from '../views/Blogs.vue'
import BlogEdit from '../views/BlogEdit.vue'
import BlogDetail from '../views/BlogDetail.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Index',
    redirect: {name: "Blogs"}
  },
  {
    path: '/blogs',
    name: 'Blogs',
    component: Blogs
  },
  {
    path: '/login',
    name: 'Login',
    component: Login
  },
  {
    path: '/blog/add',
    name: 'BlogAdd',
    component: BlogEdit,
    meta: {
      requireAuth: true
    }
  },
  {
    path: '/blog/:blogId',
    name: 'BlogDetail',
    component: BlogDetail
  },
  {
    path: '/blog/:blogId/edit',
    name: 'BlogEdit',
    component: BlogEdit,
    meta: {
      requireAuth: true
    }
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

路由中心的配置

  • 在routes中配置了一组route,分别用{}隔离开,注意的是这里有个重定向操作,把"/"重定向到"Blogs"组件
  • 配置route时的路由配置meta指路由元信息,设置一些自定义信息供页面组件或路由钩子函数使用,这次是配置资源管理permission.js控制请求的,对于该博客页面想要编辑是需要登录认证的 requireAuth: true

store目录

存放仓库脚本文件(vuex插件的配置文件,数据仓库)
在这里插入图片描述

localStorage和sessionStorage

  • vuex存储在内存
  • localstorage(本地存储)则以文件的方式存储在本地,永久保存;- sessionstorage( 会话存储 ) ,临时保存
  • localStorage和sessionStorage只能存储字符串类型,对于复杂的对象可以使用ECMAScript提供的JSON对象的stringify和parse来处理
  • vuex用于组件之间的传值,localstorage则主要用于不同页面之间的传值

store属性

  • state :对数据的全局存储
  • getter:对数据进行计算
  • mutations:用与更新store中所存放的所有状态
    • mutations需要自定义方法且必须是同步方法,每个方法称为mutation
  • actions:对数据的异步更改
  • modules:模块化vuex,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理
  • commit:同步操作,数据提交至 mutations ,可用于登录成功后读取用户信息写到缓存里

代码分析

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    token: '',
    userInfo: JSON.parse(sessionStorage.getItem("userInfo"))
  },
  mutations: {
    // set
    SET_TOKEN: (state, token) => {
      state.token = token
      localStorage.setItem("token", token)
    },
    SET_USERINFO: (state, userInfo) => {
      state.userInfo = userInfo
      sessionStorage.setItem("userInfo", JSON.stringify(userInfo))
    },
    REMOVE_INFO: (state) => {
      state.token = ''
      state.userInfo = {}
      localStorage.setItem("token", '')
      sessionStorage.setItem("userInfo", JSON.stringify(''))
    }

  },
  getters: {
    // get
    getUser: state => {
      return state.userInfo
    }

  },
  actions: {
  },
  modules: {
  }
})
  • mutations中
    • SET_TOKEN中将token保存在state中同时存储到localStorage
    • SET_USERINFO将userinfo保存到sessionStorage同时存储到state中
    • REMOVE_INFO将state中的token和userinfo都置空将localStorage中的token和sessionStorage中的userinfo也置空

views目录

存放页面(视图)组件

代码分析

Login.vue






  • 在一个el-container中设置了Headers.vue和一个表单用以提交用户名密码,设置来了”立刻创建“按钮用以提交,”重置“按钮重置信息
  • script中,在data对象中做了表单输入数据的规则校验;methods中定义了submit方法发送post请求,请求路径为/login然后取出wt和userinfo存储到store中,再页面路由到博客列表做了错误处理(全局axios拦截弹窗显示)

Blogs.vue






  • 引入Header.vue,用一个时间线组件做样式和分页显示,用el-card显示v-for标签遍历出的每一个博客对象,卡片上还放置了一个router-link标签页面路由到博客详情

  • script中methods发起了通过axios做了get请求然后将结果中的内容赋值,底层用了分页插件PageHelper

      page(currentPage) {
              const _this = this
              _this.$axios.get("/blogs?currentPage=" + currentPage).then(res => {
                console.log(res)
                _this.blogs = res.data.data.records
                _this.currentPage = res.data.data.current
                _this.total = res.data.data.total
                _this.pageSize = res.data.data.size
      
              })
            }
    

BlogsDetails.vue






  • 引入Header.vue,用标题标签显示了博客名,如果是博客拥有者(根据ownblog判断吧)生成一个表示可编辑的图片链接,然后显示博客内容
  •   	
      	        
      	        编辑
      	        
      	      
    
  • 在script中,生命周期函数ctreated()中从动态路由中拿到blogid并发起get请求拿到blog的 id,title和body,data()中返回了这些数据已经ownblog

BlogdEdit.vue






  • 博客编辑页面引入Header.vue后,放置了三个表单分别输入标题,摘要和内容,注意在内容输入时进入了mavon-editor标签用于博客内容的编辑,还引入了一个”立即创建“按钮,和”重置“按钮

       
        
      
    
  • script中methods做了表单提交(axios.post)这里需要拿到jwt设置在请求头中,提交成功时会弹窗提示失败做了全局asios处理

    methods: {
    submitForm(formName) {
    this.$refs[formName].validate((valid) => {
    if (valid) {

              const _this = this
              //请求中将表单的内容也提交了
              this.$axios.post('/blog/edit', this.ruleForm, {
                headers: {
                  "Authorization": localStorage.getItem("token")
                }
              }).then(res => {
                console.log(res)
                _this.$alert('操作成功', '提示', {
                  confirmButtonText: '确定',
                  callback: action => {
                    _this.$router.push("/blogs")
                  }
                });
    
              })
    
            } else {
              console.log('error submit!!');
              return false;
            }
          });
        },
        resetForm(formName) {
          this.$refs[formName].resetFields();
        }
      },
    
  • 数据的双向绑定在这段代码里体现的格外明显,template中标题,摘要,内容通过prop属性绑定为title,description,content,在- 生命周期函数created()中根据动态路由参数的blogid先去后台查询如果有则显示在页面上(通过data()中的return),没有则显示为空,这样的操作正好实现了逻辑上无论是发表博客还是修改博客的请求都可以由一个请求实现

main.js

全局脚本文件(项目的入口)项目中所有的页面都会加载main.js

  • 1.项目中所有的页面都会加载main.js

  • 2.放置项目中经常会用到的插件和CSS样式。例如: 网络请求插件:axios和vue-resource、图片懒加载插件:vue-lazyload

  • 3.存储全局变量

    import Vue from ‘vue’
    import App from ‘./App.vue’
    import router from ‘./router’
    import store from ‘./store’
    import Element from ‘element-ui’
    import axios from ‘axios’

    import mavonEditor from ‘mavon-editor’

    import “element-ui/lib/theme-chalk/index.css”
    import ‘mavon-editor/dist/css/index.css’

    import “./axios”
    import “./permission”

    Vue.use(Element)
    Vue.use(mavonEditor)

    Vue.config.productionTip = false
    Vue.prototype.$axios = axios

    new Vue({
    router,
    store,
    render: h => h(App)
    }).$mount(’#app’)

App.vue

  • 根组件,所有页面都是在App.vue下进行切换的(可以理解为所有的路由router也是App.vue的子组件)

  • 一定要引入 < router-view/> 标签

axios.js

  • 项目中在axios.js中做全局axios拦截

  • 实现了前置拦截,后置拦截(根据返回结果数据中json中的code(代表操作是否合法,此时请求时完成的)和返回头的status(代表的请求是否正常)进行错误原因判断和显示以达到更好的用户体验)

  • 在项目目录下(注意层级)的axios.js要在main.js中import 才生效

      import axios from 'axios'
      import Element from 'element-ui'
      import router from './router'
      import store from './store'
      
      
      axios.defaults.baseURL = "http://localhost:8081"
      
      // 前置拦截
      axios.interceptors.request.use(config => {
        return config
      })
      
      axios.interceptors.response.use(response => {
          let res = response.data;
    
      console.log("=================")
      console.log(res)
      console.log("=================")
    
      if (res.code === 200) {
        return response
      } else {
    
        Element.Message.error('错了哦,这是一条错误消息', {duration: 3 * 1000})
    
        return Promise.reject(response.data.msg)
      }
    },
    error => {
      console.log(error)
      if(error.response.data) {
        error.message = error.response.data.msg
      }
    
      if(error.response.status === 401) {
        store.commit("REMOVE_INFO")
        router.push("/login")
      }
    
      Element.Message.error(error.message, {duration: 3 * 1000})
      return Promise.reject(error)
    }
    

    )

permission.js

  • 主要作用时做路由权限拦截

  • 进行路由判断登录,每次路由时先判断该路由元数据meta中的requireAuth是否为true,是则从localStorage中取出token,存在则继续业务逻辑,否则页面路由到登录页面

      import router from "./router";
      
      // 路由判断登录 根据路由配置文件的参数
      router.beforeEach((to, from, next) => {
      
        if (to.matched.some(record => record.meta.requireAuth)) { // 判断该路由是否需要登录权限
      
      const token = localStorage.getItem("token")
      console.log("------------" + token)
    
      if (token) { // 判断当前的token是否存在 ; 登录存入的token
        if (to.path === '/login') {
    
        } else {
          next()
        }
      } else {
        next({
          path: '/login'
        })
      }
    } else {
      next()
    }
    })
    

你可能感兴趣的:(Blog项目实战)