VBlog项目代码理解之前端

本文介绍了VBlog项目的前端代码,包括资源、配置问题和组件详细讲解。重点讨论了如何通过配置解决跨域,路由控制页面跳转,API调用后端方法,以及登录验证、主页面、文章列表、博客详情等关键组件的工作原理。此外,还涵盖了Vue组件的交互,如登录验证、下拉框、分页功能和过滤器的使用。
摘要由CSDN通过智能技术生成

VBlog项目代码理解之前端

资源

项目地址
前后端交互理解
后端代码理解
推荐:整个项目几乎是只用到了SpringBoot、Vue、Mybatis、ElementUI,没有用到Redis、RabbitMQ等内容,很适合刚学完SpringBoot和Vue的同学练手,感谢作者!帮作者打个广告吧~
在这里插入图片描述
PS:这是本人第一个学习的项目,难免会有错误的地方,哪里有问题烦请指正,感谢!


配置问题

解决前后端交互、跨域、页面跳转等问题

config/index:通过代理解决跨域问题

  • 核心内容就是proxyTable,这块根据SpringBoot的配置来配置,具体内容都在注释里了。
  • config/index.js参数详解
'use strict'
// Template version: 1.2.7
// see http://vuejs-templates.github.io/webpack for documentation.

// 这个不知道有啥用
const path = require('path')

// 话说这边并没有用env: require()来指定环境啊
module.exports = {
   
  // 管开发的时候,既有前后端交互的跨域配置,也有在前端玩的配置
  dev: {
   
    // Paths
    // 静态资源子目录和公开地址
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',
    // 这边是设置代理,解决跨域问题,开发的时候才用
    proxyTable: {
   
      // 路径啥也不加
      '/': {
   
        // 改写,相当于把http://localhost:8080改成下面的内容+/
        target: 'http://localhost:8081',
        changeOrigin: true, // 指示是否跨域
        pathRewrite: {
   
          '^/': ''  // 啥都没有,就等价于http://localhost:8081/
        }
      }
    },

    // 这边是前端自己玩
    // Various Dev Server settings
    host: 'localhost', // can be overwritten by process.env.HOST

    // dev-server的端口号
    port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
    autoOpenBrowser: false,
    errorOverlay: true,
    notifyOnErrors: true,
    poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-


    /**
     * Source Maps
     */

    // https://webpack.js.org/configuration/devtool/#development
    devtool: 'eval-source-map',


    // debug工具出问题的时候可以试试改为false,可能会有用
    cacheBusting: true,


    // 是否生成css、map文件,说可能存在问题,但是没啥必要用这个,出问题了可以控制台
    // 所以这里于默认不同,设为false
    cssSourceMap: false,

  },
  // 配置build、打包问题
  // 这一套很固定,根本不用动,创建完了就是这样的
  build: {
   
    // 这块好像都是说运行npm run build之后,生成的文件应在的位置
    // build之后生成的index位置?
    index: path.resolve(__dirname, '../dist/index.html'),

    // Paths
    // 静态资源的根目录
    assetsRoot: path.resolve(__dirname, '../dist'),
    // 静态资源子目录
    assetsSubDirectory: 'static',
    // 静态资源的公开路径,也就是真正的引用路径(引用路径指使用时?)
    assetsPublicPath: '/',

    /**
     * Source Maps
     */
    // 是否生成生产环境的sourcemap,sourcemap用来对编译后的文件进行debug,方法是映射回编译前的文件
    // 编译后的代码人是看不懂的
    productionSourceMap: true,

    // 这应当是一种映射工具
    devtool: '#source-map',


    // 是否在生产环境中压缩代码,如果要压缩必须安装compression-webpack-plugin
    productionGzip: false,
    // 指定要压缩的文件类型
    productionGzipExtensions: ['js', 'css'],


    // 开启编译完成后的报告,只有运行了npm run build --report才有吧
    bundleAnalyzerReport: process.env.npm_config_report
  }
}


router:页面跳转控制

  • 所有的跳转都通过路由控制,在router/index.js文件中配置,path可以通过children设置子路径,如果有多个子模块,就会变成可选模式。
  • 同时每个路径都绑定了component,子路径的component会在父路径的组件的<router-view>位置显示,所以路径的跳转本质上就是组件的不同组合
  • 还可以配置一些信息,如组件名字、是否隐藏、保持激活等内容。
import Vue from 'vue'
import Router from 'vue-router'
import Login from '@/components/Login'
import Home from '@/components/Home'
import ArticleList from '@/components/ArticleList'
import CateMana from '@/components/CateMana'
import DataCharts from '@/components/DataCharts'
import PostArticle from '@/components/PostArticle'
import UserMana from '@/components/UserMana'
import BlogDetail from '@/components/BlogDetail'
import Doex from '@/components/Doex'

Vue.use(Router)

export default new Router({
   
  routes: [
    {
   
      path: '/',
      name: '登录',
      hidden: true,
      component: Login
    }, {
   
      path: '/home',
      name: '',
      component: Home,
      hidden: true
    }, {
   
      path: '/home',
      component: Home,
      name: '文章管理',
      iconCls: 'fa fa-file-text-o',
      children: [
        {
   
          path: '/articleList',
          name: '文章列表',
          component: ArticleList,
          meta: {
   
            keepAlive: true
          }
        }, {
   
          path: '/postArticle',
          name: '发表文章',
          component: PostArticle,
          meta: {
   
            keepAlive: false
          }
        }, {
   
          path: '/blogDetail',
          name: '博客详情',
          component: BlogDetail,
          hidden: true,
          meta: {
   
            keepAlive: false
          }
        }, {
   
          path: '/editBlog',
          name: '编辑博客',
          component: PostArticle,
          hidden: true,
          meta: {
   
            keepAlive: false
          }
        }
      ]
    }, {
   
      path: '/home',
      component: Home,
      name: '用户管理',
      children: [
        {
   
          path: '/user',
          iconCls: 'fa fa-user-o',
          name: '用户管理',
          component: UserMana
        }
      ]
    }, {
   
      path: '/home',
      component: Home,
      name: '栏目管理',
      children: [
        {
   
          path: '/cateMana',
          iconCls: 'fa fa-reorder',
          name: '栏目管理',
          component: CateMana
        }
      ]
    }, {
   
      path: '/home',
      component: Home,
      name: '数据统计',
      iconCls: 'fa fa-bar-chart',
      children: [
        {
   
          path: '/charts',
          iconCls: 'fa fa-bar-chart',
          name: '数据统计',
          component: DataCharts
        }
      ]
    }, {
   
      path: '/home',
      component: Home,
      name: '身体记录',
      iconCls: 'el-icon-date',
      children: [
        {
   
          path: '/doex',
          iconCls: 'el-icon-date',
          name: '身体记录',
          component: Doex
        }
      ]
    }
  ]
})


utils/api.js:调用后端方法并接受返回值

关于两种不同的Content-Type:

  • application/x-www-form-urlencoded会对参数进行编码,键值对参数用&连接,空格转换为+,有特殊符号就转换为ASCII HEX值。然后这个类型就是编码格式,也是浏览器默认的编码格式。如果是Get请求,就将参数转化成?key=value&key=value的格式接在url后面。
  • multipart/form-data不会进行编码,使用分割线来相当于&。常用于文件等二进制,也可以用于键值对参数。
  • application/json也经常使用。
  • 两种post接口的解读

四种常用请求含义

  • Get(SELECT):从服务器查询,可以在服务器通过请求的参数区分查询的方式。
  • POST(CREATE):在服务器新建一个资源,调用insert操作。
  • PUT(UPDATE):在服务器更新资源,调用update操作。
  • DELETE(DELETE):从服务器删除资源,调用delete语句。
import axios from 'axios'
// base意义是啥
let base = '';
// 这些要在component用到的时候import
export const postRequest = (url, params) => {
   
  return axios({
   
    method: 'post',
    url: `${
     base}${
     url}`,
    data: params,
    transformRequest: [function (data) {
   
      // 这边就是参数传递的标准表达式了
      let ret = ''
      for (let it in data) {
   
        ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
      }
      return ret
    }],
    headers: {
   
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  });
}
export const uploadFileRequest = (url, params) => {
   
  return axios({
   
    method: 'post',
    url: `${
     base}${
     url}`,
    data: params,
    headers: {
   
      'Content-Type': 'multipart/form-data'
    }
  });
}
export const putRequest = (url, params) => {
   
  return axios({
   
    method: 'put',
    url: `${
     base}${
     url}`,
    data: params,
    transformRequest: [function (data) {
   
      let ret = ''
      for (let it in data) {
   
        ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
      }
      return ret
    }],
    headers: {
   
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  });
}
export const deleteRequest = (url) => {
   
  return axios({
   
    method: 'delete',
    url: `${
     base}${
     url}`
  });
}
export const getRequest = (url,params) => {
   
  return axios({
   
    method: 'get',
    data:params,
    transformRequest: [function (data) {
   
      let ret = ''
      for (let it in data) {
   
        ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
      }
      return ret
    }],
    headers: {
   
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    url: `${
     base}${
     url}`
  });
}


filter_utils:过滤器

  • 在一个单独的文件中定义过滤器,使用方法是{ { 值 | 过滤器函数名}}
    过滤器的定义
import Vue from 'vue'
// 定义全局过滤器,在main.js中导入
Vue.filter("formatDate", function formatDate(value) {
   
  var date = new Date(value);
  var year = date.getFullYear();
  var month = date.getMonth() + 1;
  var day = date.getDate();
  if (month < 10) {
   
    month = "0" + month;
  }
  if (day < 10) {
   
    day = "0" + day;
  }
  return year + "-" + month + "-" + day;
});
Vue.filter("formatDateTime", function formatDateTime(value) {
   
  var date = new Date(value);
  var year = date.getFullYear();
  var month = date.getMonth() + 1;
  var day = date.getDate();
  var hours = date.getHours();
  var minutes = date.getMinutes();
  if (month < 10) {
   
    month = "0" + month;
  }
  if (day < 10) {
   
    day = "0" + day;
  }
  return year + "-" + month + "-" + day + " " + hours + ":" + minutes;
});

main:导入依赖

导入使用的外部组件,如ElementtUIVCharts,还有过滤器也是在这里导入的。

import Vue from 'vue'
import App from './App'
import router from './router'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
// import './styles/element-variables.scss'
import 'font-awesome/css/font-awesome.min.css'
// 再次导入了过滤器
import './utils/filter_utils.js'
import VCharts from 'v-charts'

Vue.use(ElementUI)
Vue.use(VCharts)

Vue.config.productionTip = false;
window.bus = new Vue();
new Vue({
   
  el: '#app',
  router,
  template: '<App/>',
  components: {
   App}
})

组件

  • 最核心的部分,通过各种组件的组合、跳转来实现页面的展示。

Login:登录与权限验证

  • 登录页面是Login,两个输入框绑定rules校验规则,绑定方法是el-form:rules,然后通过prop关联rules中的性质。
  • 然后登录按钮绑定了方法,传递地址为后端的spring security的/login/{username, password},执行后判断

v:onclick.nativa.prevent:“方法名”:

  • 给vue组件绑定事件时候,必须加上native ,否则会认为监听的是来自Item组件自定义的事件
  • 但父组件想在子组件上监听自己的click的话,需要加上native修饰符,故写法就像上面这样。
  • prevent 是用来阻止默认的 ,相当于原生的event.preventDefault()

auto-complete=“off”:
autocomplete 属性规定输入字段是否应该启用自动完成功能。自动完成允许浏览器预测对字段的输入。当用户在字段开始键入时,浏览器基于之前键入过的值,应该显示出在字段中填写的选项。
注意:autocomplete 属性适用于 <form>,以及下面的 <input> 类型:text, search, url, telephone, email, password, datepickers, range 以及 color。

完整代码

<template>
  <!--v-bind:rules,是绑定一些数据?答:是绑定校验规则,通过prop的方法使用rules的校验规则-->
  <!--所有的class都可以在下面设置style-->
  <el-form :rules="rules" class="login-container" label-position="left"
           label-width="0px" v-loading="loading">
    <h3 class="login_title">系统登录</h3>
    <el-form-item prop="account">
      <!--输入用户名,关闭了自动提示。-->
      <el-input type="text" v-model="loginForm.username" auto-complete="off" placeholder="账号"></el-input>
    </el-form-item>
    <!--注意这个prop,这是用来设置校验规则的,-->
    <el-form-item prop="checkPass">
      <el-input type="password" v-model="loginForm.password" auto-complete="off" placeholder="密码"></el-input>
    </el-form-item>
    <!--这个左右对齐好像没差别-->
    <el-checkbox class="login_remember" v-model="checked" label-position="left">记住密码</el-checkbox>
    <el-form-item style="width: 100%">
      <!--v:on绑定自定义方法-->
      <el-button type="primary" @click.native.prevent="submitClick" style="width: 100%">登录</el-button>
    </el-form-item>
  </el-form>
</template>
<!--调用utils/api里面的HTTP传递方法-->
<script>
  import {
   postRequest} from '../utils/api'
  import {
   putRequest} from '../utils/api'
  export default{
   
    data(){
   
      // 下面这些值会传递给上边
      // 这个return是要传给后端码?
      return {
   
        // rules:规则,校验规则
        rules: {
   
          // 跟上面的prop绑定的,触发器为失去焦点
          account: [{
   required: true, message: '请输入用户名', trigger: 'blur'}],
          checkPass: [{
   required: true, message: '请输入密码', trigger: 'blur'}]
        },
        // 默认被选择
        checked: true,
        // 默认文本内容,因为由v-model,所以会显示
        loginForm: {
   
          username: 'sang',
          password: '123'
        },
        loading: false
      }
    },
    methods: {
   
      submitClick: function () {
   
        var _this = this;
        // 改变参数
        this.loading = true;
        // 方法里用的axios,第一个参数是url,第二个是参数
        // 方法通往Spring security的权限管理
        postRequest('/login', {
   
          username: this.loginForm.username,
          password: this.loginForm.password
        }).then(resp=> {
   
          _this.loading = false;
          if (resp.status == 200) {
   
            //成功
            var json = resp.data;
            if (json.status == 'success') {
   
              _this.$router.replace({
   path: '/home'});
            } else {
   
              // 第一个参数是内容,第二个参数是标题
              _this.$alert('登录失败!', '失败!');
            }
          } else {
   
            //失败
            _this.$alert('登录失败!', '失败!');
          }
        }, resp=> {
   
          _this.loading = false;
          _this.$alert('找不到服务器⊙﹏⊙∥!', '失败!');
        });
      }
    }
  }
</script>
<!--给绑定的class设置style-->
<style>
  .login-container {
   
    border-radius: 15px;
    background-clip: padding-box;
    margin: 180px auto;
    width: 350px;
    padding: 35px 35px 15px 35px;
    background: #fff;
    border: 1px solid #eaeaea;
    box-shadow: 0 0 25px #cac6c6;
  }

  .login_title {
   
    margin: 0px auto 40px auto;
    text-align: center;
    color: #505458;
  }

  .login_remember {
   
    margin: 0px 0px 35px 0px;
    text-align: left;
  }
</style>


Home:主页面框架


登录成功就来到Home。

v-if="!item.hidden"
通过hidden来控制是否显示

slot=“title”:是系统自带的吗,找不到东西

Breadcrumb 面包屑
el-breadcrumb作用显示当前页面的路径,快速返回之前的任意页面,之前的页面用el-breadcrumb-item表示
可以控制最上面的导航,加了点东西就变成下一行了

激活判断
代码中可以根据keep-alive参数来决定子组件显示的位置

          <!--根据激活状态判断在哪里显示-->
          <keep-alive>
            <div>
              {
   {
   '激活'}}
            </div>
            <router-view v-if="this.$route.meta.keepAlive"></router-view>
          </keep-alive>
          <div>
            {
   {
   '未激活'}}
          </div>
          <router-view v-if="!this.$route.meta.keepAlive"></router-view>

通过:index来绑定路径path

              <!--但是这个怎么跳转呢?-->
              <!--index这里绑定了path, 为啥index绑定了就能跳?是因为el-menu-item的特性?-->
              <el-menu-item :index="item.children[0].path">
                <i :class="item.children[0].iconCls"></i>
                <span slot="title">{
   {
   item.children[0].name}}</span>
              </el-menu-item>

登录声明
通过一个钩子函数触发,当转到该页面的时候,会调用

    // 钩子函数,转到的时候触发
    mounted: function () {
   
      this.$alert('为了确保所有的小伙伴都能看到完整的数据演示,数据库只开放了查询权限和部分字段的更新权限,其他权限都不具备,完整权限的演示需要大家在自己本地部署后,换一个正常的数据库用户后即可查看,这点请大家悉知!', '友情提示', {
   
        confirmButtonText: '确定',
        callback: action => {
   
        }
      });
      var _this = this;
      getRequest("/currentUserName").then(function 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值