一、vue的核心插件

1. vuex

vuex用于集中存储管理应用的所有组件的状态(state),在一个项目的开发过程中,如果一些值或者方法被多个组件频繁的使用,就把这些值或者方法定义在vuex中,便于组件的调用。

2. vue-router

这是一个Vue的官方路由器,让构建单页面应用变得十分简单。

二、服务器代理配置

当向服务器发送请求的时候,可以对请求进行处理后再发送,可以在vue.config.js中进行配置

module.exports = {
    devServer: {
        proxy: {
            '/api': {
                // 指向新路径
                target: 'http://localhost:8081',
                pathRewrite: {
                    '^/api': ''
                }
            }
        }
    }
}
  • 请求服务器之前,如果匹配到url是以/api开头的就进行拦截,指向新的路径,并且可以重写路径,去掉/api,因为目标服务器里没有对应的url,这里写正则^/api的时候,不要忘记/

三、路由配置

import Vue from "vue";
import VueRouter from "vue-router";
import store from "../store";
import Login from '../views/Login.vue'
import Photo from '../views/Photo.vue'
Vue.use(VueRouter);
const routes = [
	// /login路径,打开Login组件显示在App的view-router处
  {
    path: '/login',
    name: 'Login',
    component: Login,
	// isAuth,功能优化,当向服务器发送请求的时候,可以检测该路由是否需要鉴权,如果不需要鉴权,可以取消本次的发送请求
    meta: {
      isAuth: false
    }
  },
  {
	  // 获取用户信息之后,打开该路由
    path: '/photo',
    name: 'Photo',
    component: Photo,
    meta: {
      isAuth: true
    }
  },
  {
	  // 点击图片的时候跳转到该路由
    path: '/detail/:id',
    name: 'Detail',
    // 获取id参数
    props: true,
    component: () => import('../views/Detail.vue')
    
  }
];

const router = new VueRouter({
  mode: "history",
  base: process.env.BASE_URL,
  routes
});
router.beforeEach((to, from, next) => {
  if(to.meta.isAuth){
    if(store.state.token){
      next()
    }else{
      next({
        name: 'Login'
      })
    }
  }else{
    next()
  }
})

export default router;

  • Vue安装vue-router插件
    Vue.use(VueRouter)
  • meta里面可以存放一些路由自定义信息
  • 获取路由参数,props:true
  • 动态加载组件component: () => import('../views/Detail.vue'),因为用户可能不需要这个功能,如果需要,就动态加载。
  • router.beforeEach,可以在这个路由守卫里判断该页面是否存在token,如果有就进入该路由内,如果没有,就跳转到Login路由

四、自定义axios

import axios from 'axios' 
import store from './store'
import router from './router'
// 自己配置axios
const myAxios = axios.create({
    baseURL: '/api'
})
// 请求拦截器,携带token
myAxios.interceptors.request.use(config => {
    // token
    const token = store.state.token
    if(token){
        config.headers.authorization = "Bearer " + token
    }
    // 不要忘记return配置
    return config 
})
// 优化
myAxios.interceptors.response.use(res => {
    return res 
},
err => {
    if(err.response.status === 401){
        router.replace({
            name: 'Login'
        })
    }
})
export default myAxios
  • 在发出axios请求之前加入逻辑myAxios.interceptors.request.use(config => {...})
    在请求发送之前,给头信息添加authorization
  • 在axios接收请求之前加入逻辑myAxios.interceptors.response.use(res=> {...})
    当请求状态为401或其它的时候,跳转到登录界面

五、请求服务器逻辑

import http from '../http'
export function apiLogin({username, password}){
	// 因为Axios的baseURL的配置,其实访问的是local: 8080/api/login
    return http.post('/login',{
        username,
        password
    })
}
export function apiGetPhotos(){
	// 服务端请求/getPhotos页面的数据
    return http.get('/getPhotos')
}
export function apiUpLoad(file){
    const formData = new FormData()
    formData.append('img', file)
	// 把图片上传到后端。
    return http.post('/upload', formData)
}
  • 前缀加api是开发规范
  • http是自定义的axios,在定义的时候配置了baseURL,所以请求的时候url可以省略/api
  • axios返回的是Promise

六、vuex-store

import Vue from "vue";
import Vuex from "vuex";
import {apiLogin, apiGetPhotos} from '../api'
Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    token: localStorage.getItem('token') || '',
    photos: []
  },
  getters: {
    getPhotoById(state){
      // 获取对应图片
      return id => {
        return state.photos.find(item => item.id == id)
      }
    }
  },
  mutations: {
    login(state,payload){
      state.token = payload
    },
    updatePhotos(state,payload){
      state.photos = payload
    }
  },
  actions: {
    // 需要等待获取apiLogind获取数据完成
    async getInfo({commit}, payload){
      const { username, password } = payload 
      let res = await apiLogin({username, password})
      const data = res.data.data
      localStorage.setItem('token',data.token)
      commit('login', data.token)
    },
    getPhotos({commit}){
      apiGetPhotos().then(res => {
        console.log(res.data)
        let data = res.data
        commit('updatePhotos',data.data.photos)
      })
    }
  },
  modules: {}
});

七、登录界面

实现功能:

  1. 获取用户信息,生成token,便于后端验证用户信息
  2. 点击按钮,后端验证用户信息,验证成功跳转到图片展示页面(Photo)

Login.vue

<template>
  <div class="loginContainer">
    <h1>登录</h1>
    <div>
      姓名:
      <input
        class="inputStyle"
        id="username"
        type="text"
        name="username"
        v-model="username"
      /><br />
      密码:
      <input
        class="inputStyle"
        id="password"
        type="password"
        name="pwd"
        v-model="password"
      /><br />
      <button class="loginStyle" id="loginBtn" @click='getInfo'>登录</button>
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      username: '',
      password: ''
    }
  },
  methods: {
    getInfo() {
      this.$store.dispatch('getInfo', {
        username: this.username,
        password: this.password
      }).then(() =>{
        this.$router.push('/photo')
      })
    }
  },
};
</script>

<style lang="scss" scoped></style>

  • 当需要“花时间”来完成的行为,就需要调用异步函数
  • 获取用户信息后,需要向后端进行验证(需要花时间),验证成功后设置token,并且跳转到photo页面
  • 因为多个页面需要用到token,所以就把token存在vuex的state里。
  • 调用vuex里的异步函数,需要用到this.$store.dispatch

八、图片展示界面

页面功能:

  • 把数据库的图片显示在图片展示框
  • 当点击 上传图片的时候,出现上传图片框
  • 当点击图片的时候,显示图片
  • 因为点击上传按钮的时候需要把 打开上传框的状态发送给子组件,所以,需要设置一个boolean的属性名(visible)传递给子组件来控制子组件框的开关。

Photo.vue

<template>
  <div>
    <!-- 展示相关 -->
    <div class="container">
      <div class="photoHeader">
        <div class="imgContainer">
          <img class="photoName" src="public/img/1.jpg" />
        </div>
        <div class="btnContainer">
          <span class="photoTitle">相册名称</span>
          <button class="mybtn" @click="showUpLoadView = true">上传照片</button>
        </div>
      </div>
      <div class="photoContainer">
        <template v-for="item in photos">
          <router-link :key="item.id" :to="{path:`/detail/${item.id}`}">
            <div class="photoItem" :key="item.id">
              <img :src="item.imgUrl" />
              <span>
                {{ item.name }}
              </span>
            </div>
          </router-link>
        </template>
      </div>
    </div>
    <up-load-view :visible.sync="showUpLoadView" />
  </div>
</template>
<script>
// 显示上传框的组件
import upLoadView from "./upLoadView.vue";
export default {
  components: { upLoadView },
  data() {
    return {
		// 用于控制是否打开子组件框
      showUpLoadView: false,
    };
  },
  computed: {
    photos() {
		// 获取图片信息,展示到DOM里
      return this.$store.state.photos;
    },
  },
  created() {
	  // 页面渲染之前需要获取图片数据
	  // 一般需要时间的操作都需要用到异步操作
	  // 这里需要用到异步调用,因为获取服务器图片数据需要时间
    this.$store.dispatch("getPhotos");
  },
};
</script>
  • 页面渲染之前需要从服务器获取图片信息(花时间),所以这里调用vuex里的异步操作

九、上传框界面

组件功能:

  1. 获取上传的图片信息
  2. 把对应的图片上传到待上传框内
  3. 点击开始上传,把图片信息存入后端的数据库

UpLoadView.vue

<template>
  <!-- 上传相关 -->
  <div class="masking" v-show="visible">
    <div class="addPhotoContainer"></div>
    <div class="addController">
      <h3 class="addTitle">
        上传照片-普通上传(H5)<span class="close" @click="closeWrapper"></span>
      </h3>
      <div class="photoTitles">
        <span class="uploadTo">上传到</span>
        <div class="photoSelect">
          <img class="showPhoto" src="public/img/1.jpg" />
          相册名称
        </div>
      </div>

      <!-- 上传按钮 -->
      <div class="showContainer" v-show="upBtn">
        <div class="uploadContainer">
          <span class="fileinput-button">
            <span>上传图片</span>
            <!-- 绑定input事件监听为uploadPhoto函数 -->
            <input
              class="imgFile"
              type="file"
              name=""
              multiple="multiple"
              @input="uploadPhoto"
            />
          </span>
          <span class="hint">
            按住Ctrl可多选
          </span>
        </div>
      </div>

      <!-- 显示待上传图片  -->
      <div class="loadContainer" v-show="upWrapper">
        <div class="wantUpload">
          <template v-for="(item, index) in toBeUpLoaded">
			  <!-- 显示待上传图片 -->
            <UpLoadPhotoItem :key="index" :item="item" />
          </template>
        </div>
        <div class="addStyle">
          <span class="fileinput-add">
            <span></span>
            <input class="imgFile-add" type="file" multiple="multiple" />
          </span>
        </div>
        <!-- 开始上传按钮 -->
        <div class="bottomStyle" @click="beginUpload">
          <span class="uploadBtn">开始上传</span>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
// 待上传图片的组件
import UpLoadPhotoItem from "./upLoadPhotoItem.vue";
//不是export default导出的模块都要用{}来包括导出。
import { apiUpLoad } from "../api";
export default {
  components: {
    UpLoadPhotoItem,
  },
  // visible是父级传入的数据
  props: ["visible"],
  data() {
    return {
		// 用于存放待上传的图片信息
      toBeUpLoaded: [],
    };
  },
  computed: {
	  // 如果有需要上传的图片,隐藏上传按钮,把待上传框显示,如果没有要上传的图片,就把上传按钮显示出来,隐藏待上传框
    upBtn() {
      return this.toBeUpLoaded.length === 0;
    },
    upWrapper() {
      return this.toBeUpLoaded.length > 0;
    },
  },
  methods: {
	  // 点击上传按钮,触发该函数
    async beginUpload() {
	  // 遍历待上传的图片
	  // 当图片上传到后端,后端需要把数据存到数据库(花时间)
      for (const photos of this.toBeUpLoaded) {
        await apiUpLoad(photos);
      }
      // 写好代码,表达好意图
      this.upLoadCompleted();
    },
    upLoadCompleted() {
		// 图片上传完成后,清空待上传数组
      this.toBeUpLoaded = [];
	  // 获取服务端的图片信息,更新vuex里的photos,从而更新DOM
      this.$store.dispatch('getPhotos');
    },
    closeWrapper() {
      // 响应父级的visible,把false值传递给父级的visible
      this.$emit("update:visible", false);
    },
    uploadPhoto(e) {
		// 获取上传图片的信息
      // Array.from 把类数组整理成数组,...解构出来每一个数组对象
	  // 把上传图片的信息全部放到待上传数组里
      this.toBeUpLoaded.push(...Array.from(e.target.files));
    },
  },
};
</script>
  • 不是export default导出的模块都要用{}来包裹导出。
  • 向父级传递数据,语法糖
    子组件: this.$emit('update:visible', false)
    父组件: <up-load-view :visible.sync="showUpLoadView" />
  • Array.from可以把类数组整理成数组

十、图片待上传界面

模块功能: 将图片转成base64
UpLoadPhotoItem.vue

<template>
  <div class="uploadPhotoItem">
    <span class="myProgress">
      <span class="plan"></span>
      30%
    </span>
	<!-- 把baseURL赋值给src,直接显示图片 -->
    <img :src="imgBase" />
    <span class="pictureName">
      {{ item.name }}
    </span>
  </div>
</template>
<script>
export default {
	// 获取父级传入的图片信息
  props: ['item'],
  data() {
    return {
      imgBase: ''
    }
  },
  created () {
    // 把图片转成base64
    const fileRender = new FileReader()
    fileRender.onload = () => {
		// 文件转化完成后执行该逻辑
      this.imgBase = fileRender.result
      // 显示转化后的结果
    }
    // 把文件信息转成base64
    fileRender.readAsDataURL(this.item)
  },
};
</script>

十一、图片详情界面

模块功能: 点击图片显示图片详情
Detail.vue

<template>
  <div>
    <img :src='photoInfo.imgUrl'>
    <p>{{ photoInfo.name }}</p>
    <p><button @click='click'>返回</button></p>
  </div>
</template>
<script>
	// 点击图片的时候,跳转到对应路由
export default {
  props: ["id"],
  computed: {
	  // 获取对应的图片信息
    photoInfo() {
    // 返回对应的图片数据信息
      return this.$store.getters.getPhotoById(this.id);
    },
  },
  methods: {
      click() {
          this.$router.back()
      }
  },
};
</script>
  • 返回上一个路由
    this.$router.back()
  • 获取响应的数据
    this.$store.getters.getPhotoById(this.id)
  • Photo.vue组件内的router-link组件,to的路由指向了该组件

十二、引入css文件

import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
// 在入口文件引入样式
import './assets/css/login.css'
import './assets/css/photo.css'

Vue.config.productionTip = false;

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

Logo

前往低代码交流专区

更多推荐