阅读 362

vue 系列-实战问题汇总

是什么

vue 开发前后端分离的项目的时候,会遇到一些问题。

以下做个笔记

  1. 问题: axios 封装
  2. 问题: 接口跨域
  3. 问题:UI 库的按需加载
  4. 问题:css 深度选择器
  5. 问题:配置二级路由
  6. 问题:路由的拆分
  7. 问题:路由延迟加载
  8. 问题:清除定时器
  9. 问题:双向数据绑定
  10. 问题:mixins 混入
  11. 问题:路径错误的问题
  12. 问题:页面传参问题
  13. 问题:图片懒加载

问题: axios 封装

封装 request.js 主要是用来帮我们进行请求的拦截和响应的拦截, 在请求的拦截中我们可以携带 token,post 请求头、qs 对 post 提交数据的序列化等。在响应的拦截中,我们可以进行根据状态码来进行错误的统一处理。

import axios from "axios";
import { MessageBox, Message } from "element-ui";
import store from "@/store";
import { getToken } from "@/utils/auth";

// create an axios instance
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
  // withCredentials: true, // send cookies when cross-domain requests
  timeout: 5000, // request timeout
});

// request interceptor
service.interceptors.request.use(
  (config) => {
    // do something before request is sent

    if (store.getters.token) {
      // let each request carry token
      // ['X-Token'] is a custom headers key
      // please modify it according to the actual situation
      config.headers["X-Token"] = getToken();
    }
    return config;
  },
  (error) => {
    // do something with request error
    console.log(error); // for debug
    return Promise.reject(error);
  }
);

// response interceptor
service.interceptors.response.use(
  /**
   * If you want to get http information such as headers or status
   * Please return  response => response
   */

  /**
   * Determine the request status by custom code
   * Here is just an example
   * You can also judge the status by HTTP Status Code
   */
  (response) => {
    const res = response.data;

    // if the custom code is not 20000, it is judged as an error.
    if (res.code !== 20000) {
      Message({
        message: res.message || "Error",
        type: "error",
        duration: 5 * 1000,
      });

      // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
      if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
        // to re-login
        MessageBox.confirm(
          "You have been logged out, you can cancel to stay on this page, or log in again",
          "Confirm logout",
          {
            confirmButtonText: "Re-Login",
            cancelButtonText: "Cancel",
            type: "warning",
          }
        ).then(() => {
          store.dispatch("user/resetToken").then(() => {
            location.reload();
          });
        });
      }
      return Promise.reject(new Error(res.message || "Error"));
    } else {
      return res;
    }
  },
  (error) => {
    console.log("err" + error); // for debug
    Message({
      message: error.message,
      type: "error",
      duration: 5 * 1000,
    });
    return Promise.reject(error);
  }
);

export default service;
复制代码

问题: 接口跨域

配置代理

proxyTable: {
      // 用/api开头,代理所有请求到目标服务器
      '/api': {
        target: 'http://dehall.xxx.com', // 接口域名
        changeOrigin: true, // 是否启用跨域
        pathRewrite: { //
          '^/api': ''
        }
      }
}
复制代码

问题:UI 库的按需加载

以 vant UI 为例

  1. 安装: cnpm i vant -S
  2. 安装 babel-plugin-import 插件使其按需加载: cnpm i babel-plugin-import -D
  3. 在 .babelrc 文件中中添加插件配置 :
libraryDirectory {
    "plugins": [
        [
					"import",
           {
                "libraryName": "vant",
                "libraryDirectory": "es",
                "style": true
            }
        ]
    ]
}
复制代码
  1. 在 main.js 中按需加载需要的插件
// 按需引入 vant 组件
import { Button, List } from "vant";
Vue.use(Button).use(List);
复制代码
  1. element-ui 按需加载也是类似的 , 基本都是通过安装 babel-plugin-import 插件来支持按需加载的

问题:css 深度选择器

  1. less 语言 深度选择器/deep/
  2. less/sass 语言 可以用>>>符号
.van-tabs /deep/ .box {
  color: blue;
}
复制代码

问题:配置二级路由


import ClassMain from '@/views/class/main';
import ClassDetail from '@/views/class/detail';

{
path: '/class',
name: 'ClassMain',
component: ClassMain,
children: [
	{
	path: '/score/detail/:id',
	name: 'ScoreDetail',
	component: ScoreDetail
	},
	{
	path: '/score/index',
	name: 'ScoreIndex',
	component: ScoreIndex
	}
]
}

// 在 main.vue 中 配置子路由
<template>
	<router-view></router-view>
</template>

// 页面配置
<router-link to='/class/edit' class="aLink">编辑</router-link>
复制代码

问题:路由的拆分

路由拆分指的是将路由的文件,按照模块拆分,这样方便路由的管理,更主要的是方便多人开发。

import Vue from "vue";
import Router from "vue-router";

import Hello from "./modules/hello";
import Word from "./modules/world";

Vue.use(Router);

// 定义路由
const router = new Router({
  mode: "history",
  routes: [...Hello, ...Word],
});

// 路由变化时
router.beforeEach((to, from, next) => {
  if (document.title !== to.meta.title) {
    document.title = to.meta.title;
  }
  next();
});

// 导出
export default router;
复制代码

问题:路由延迟加载

延迟加载可以减少静态资源的加载,优化页面加载速度。

export default new Router({
  routes: [
    {
      path: "/",
      name: "Index",
      component: (resolve) => require(["@/view/index/index"], resolve),
    },
  ],
});
复制代码

问题:清除定时器

在页面离开的时候,可以清除定时器。防止定时器过度滥用

this.$once("hook:beforeDestroy", () => {
  clearInterval(timer);
});
复制代码

问题:双向数据绑定

  1. 父组件通过 props 向子组件传值,子组件通过 emit 触发父组件自定义事件。
  2. v-model="msg" 实则是 :value="msg" @input="msg = $event.target.value" 的语法糖
// 父组件
<empty v-model="msg"></empty>

// 子组件 : 点击该按钮触发父子组件的数据同步
<div class="send-btn" @click="send">确定</div>

<script>
	props: {
			value: {
					type: Boolean,
					default: false
			}
	},
	methods: {
			send () {
					// 双向数据绑定父组件:value对应的值
					// 通过$emit触发父组件input事件,第二个参数为传递给父组件的值,这里传递了一个false值
					// 可以理解为最上面展示的@input="msg = $event.target.value"这个事件
					// 即触发父组件的input事件,并将传递的值‘false’赋值给msg
					this.$emit('input', false)
			}
	}
</script>
复制代码
  1. vm.$attrs:可以获取到父组件传递的除 class 和 style 外的所有自定义属性。
  2. vm.$listeners:可以获取到父组件传递的所有自定义事件
// 父组件
<empty
    :msg="message"
    :title="articleTitle"
    @confirm="func1"
    @cancel="func2"
></empty>

// 子组件
created() {
    const msg = this.$attrs.msg; // 获取父组件传递的msg
    this.$listeners.confirm && this.$listeners.confirm(); //若组件传递事件confirm则执行
},
复制代码

问题:mixins 混入

我们在开发中经常会遇到一些公用函数的操作。每次我们会写成一个公共函数,然后在页面里面的 filters 进行过滤。

我们这时候可以使用 mixin 混入下

import { fixedNumber } from "./utils";

const Mixins = {
  filters: {
    // 保留两位小数
    numbers(val) {
      return fixedNumber(val);
    },
  },
};
export default Mixins;

import Mixins from "@/utils/Mixins";

export default {
  mixins: [Mixins],
};
复制代码

问题:路径错误的问题

  1. 打包之后文件、图片路径: build: { assetsPublicPath: "../../"}
  2. css 中的背景图的路径:背景图是通过 loader 解析的,所以自然在 loader 的配置中修改, 打开 build 文件夹下的 utils 文件,找到 exports.cssLoaders 的函数, {publicPath: "../../"}

问题:页面传参问题

vue 传参方式有:query、params 、动态路由传参

  1. query 通过 path 切换路由,params 通过 name 切换路由
// query通过path切换路由
<router-link :to="{path: 'Detail', query: { id: 1 }}"
  >前往Detail页面</router-link
>
// params通过name切换路由
<router-link :to="{name: 'Detail', params: { id: 1 }}"
  >前往Detail页面</router-link
>
复制代码
  1. query 通过 this.route.query来接收参数,params通过this.route.query来接收参数,params通过this.route.params 来接收参数。
// query通过this.$route.query接收参数
created () {
    const id = this.$route.query.id;
}

// params通过this.$route.params来接收参数
created () {
    const id = this.$route.params.id;
}
复制代码
  1. 动态路由的 url 方式:/detail/123

动态路由: 如果没有在路由中定义参数,也是可以传过去的,同时也能接收到,但是一旦刷新页面,这个参数就不存在了。只能接收到 :id 这个参数

{
    path: '/detail/:id',
    name: 'Detail',
    component: Detail
},
复制代码

问题:图片懒加载

  1. "vue-lazyload": "1.0.5",
// main.js
// 引入懒加载插件
import VueLazyload from "vue-lazyload";
// 定义懒加载插件
Vue.use(VueLazyload, {
  error: require("./assets/img/404.jpg"),
  loading: require("./assets/img/loading.png"),
  attempt: 1,
});

<img v-lazy="albums.picUrl" />;
复制代码

未完待续....

文章分类
前端
文章标签