vue相关

vue路由主动态引入

1.将子路由分模块封装

 

 

 例如  home.router.js

export default {
    path: '/',
    name: 'Home',
    component: () => import('../../views/Home.vue')
}

 

2. 主路由配置

mport Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)

//路由主动态引入
const routerList = [];

function importALL(r) {
  r.keys().forEach((key) => routerList.push(r(key).default));
}

//webpack 查找文件(代替所有import)
//1.文件路径
//2.是否子文件夹
//3.匹配规则
importALL(require.context('./', true, /\.router\.js/));

const routes = [
  ...routerList,
]

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

export default router

 

vue配置跨域

1.当前项目的根路径下新建一个文件,文件名是固定的 vue.config.js

  

 2.vue.config.js配置

module.exports = {
    devServer: {
        compress: false, //配置是否启用 gzip 压缩。所有来自 dist/ 目录的文件都做 gzip 压缩和提供为服务
        // open: true,
        // host: 'localhost',
        // port: 8080,
        // https: false,
        proxy: { //配置跨域
            '/api': {
                target: 'https://api-xxxxxxx', //后台接口
                ws: true,
                changOrigin: true, //允许跨域
                pathRewrite: {
                    '^/api': '' //请求的时候使用这个api就可以
                }
            }
        }
    }
}

  

axios,主文件设置

const service = axios.create({
    baseURL: '/api',
})

 

请求示例:

//gettRequest为封装的请求方法 , 与解决跨域问题无关

gettRequest("/goods/detail", { goods_id: 55345 }).then((res) => { console.log(res); });

 

vue路由权限控制

1.配置路由元对象 , 是否需要权限才能进入

{
    path: '/goods',
    name: 'goods',
    component: () => import('../../views/Goods.vue'),
    meta: {
        requierAuth: true //需要权限才能进入
    }
}

 

2.main文件配置全局路由守卫

main.js代码

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

Vue.config.productionTip = false;

//以token为例
router.beforeEach((to, from, next) => {
  let token = sessionStorage.getItem('token');

  //需要验证之后才能进入
  if (to.meta.requierAuth) {
    if (token) {
      next();
    } else {
      console.log(to.fullPath)
      next({
        path: './login',
        query: {
          redirect: to.fullPath // 将跳转的路由path作为参数,登录成功后跳转到该路由
        }
      })
    }
  } else {
    next()
  }
})


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

 

2.登录页 ,登录后获取token , 跳回上一路由

<template>
  <div class="page">
    <h1>login</h1>
    <button @click="loginClick">点我登录</button>
  </div>
</template>

<script>
export default {
  name: "",
  components: {},
  data() {
    return {};
  },
  methods: {
    loginClick() {
      sessionStorage.setItem("token", 123);
      let redirect = this.$route.query.redirect;
      this.$router.push(redirect); //.catch(() => {});
    },
  },
};
</script>

 

axios api层架构封装

1.目录结构

 

 

2. service.js文件    统一处理接口 , 拦截 , 状态处理

import axios from 'axios';
//从本地获取token
//token=>令牌 64 128 256 随机字符串 + 用户名 + 用户密码 + 当前时间 + ......
function getTokenByLocal() {
    let token = sessionStorage.getItem('token') || '';
    return token;
}

//创建axios实例
const service = axios.create({
    baseURL: '/api',
    timeout: 5000,
    // headers: {'X-Custom-Header': 'foobar'}
})
// 在实例已创建后修改默认值
// service.defaults.headers.common['Authorization'] = 'AUTH_TOKEN';
service.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

//请求拦截
service.interceptors.request.use(
    config => {
        if (getTokenByLocal()) {
            config.headers['Authorization'] = getTokenByLocal()
        }
        return config;
    },
    error => {
        return Promise.reject(error);
    }
)

//响应拦截
service.interceptors.response.use(
    response => {
        let res = response.data;
        if (res.code == "401") { //根据返回值 状态码进行不同操作
            location.href = "/login"
        }
        return Promise.resolve(res)
    },
    error => {
        return Promise.reject(error)
    }
)

export default service;

3.common.js   传参处理

import service from './service';
//post请求 80% 耦合度极低=> 复用性极高
//细分解耦
export function requestOfPost(url, data) {
    return service.post(url, data)
}
export function requestOfGet(url, data) {
    return service.get(url, {
        params: data
    })
}

4.api.js   返回值处理

import {
    requestOfPost,
    requestOfGet
} from './common'
export function postRequest(url, data) {
    return new Promise((resolve, reject) => {
        requestOfPost(url, data).then(res => {
            resolve(res)
        }).catch(error => reject(error))
    })
}

export function gettRequest(url, data) {
    return new Promise((resolve, reject) => {
        requestOfGet(url, data).then(res => {
            resolve(res)
        }).catch(error => reject(error))
    })
}

 

引入使用

import { gettRequest } from "../request/api";
export default {
  name: "",
  components: { Login },
  created() {
    gettRequest("/goods/detail", { goods_id: 55345 }).then((res) => {
      console.log(res);
    });
  },
  data() {
    return {};
  },
};

 

 vue引入Fastclick 插件解决 移动端300ms延迟

 1.安装

yarn add fastclick --save

2. 在main.js中 导入

import Fastclick from "fastclick"

3.在main.js中  调用

Fastclick.attach(document.body);

4.可能出现的问题 : 多词点击报错

解决方案

* {
  /*避免点击过快 fastclick 报错*/
  touch-action: pan-y;
}

 

 

vue懒加载

1.安装vue-lazyload

yarn add vue-lazyload --save

2.在main.js中 导入

import VueLazyLoad from "vue-lazyload"

3.安装配置懒加载插件

Vue.use(VueLazyLoad, {
  loading: require("./assets/img/common/placeholder.png")
});

4.使用

:src换为 v-lazy
<img v-lazy="goodsItem.goods_small_logo" alt="" />

 

 

vue 单位转换

转换vw

  1.安装postcss-px-to-viewport

  

yarn add postcss-px-to-viewport --save--dev

  2.package.json 同级文件下 ,新建postcss.config.js文件

module.exports = {
    plugins: {
        autoprefixer: {},
        "postcss-px-to-viewport": {
            viewportWidth: 375, // 视窗的宽度,对应的是我们设计稿的宽度.
            viewportHeight: 667, // 视窗的高度,对应的是我们设计稿的高度.(也可以不配置)
            unitPrecision: 5, // 指定`px`转换为视窗单位值的小数位数(很多时候无法整除)
            viewportUnit: 'vw', // 指定需要转换成的视窗单位,建议使用vw
            selectorBlackList: ['ignore', 'tab-bar'], // 指定不需要转换的类,后面再讲.
            minPixelValue: 1, // 小于或等于`1px`不转换为视窗单位.
            mediaQuery: false, // 允许在媒体查询中转换`px`
            exclude: [/^TabBar/]
        },
    }
}

 

转换rem

  版本  "postcss-pxtorem": "^5.1.1"
 
 vue.config.js
module.exports = {
  css: {
    loaderOptions: {
      less: {
        modifyVars: {
          'primary-color': '#3b53f1',
          ...obj
        },
        javascriptEnabled: true
      },
      postcss: {
        plugins: [
          require('postcss-pxtorem')({
            rootValue: 41.25, //换算基数,
            unitPrecision: 3, //允许REM单位增长到的十进制数字,小数点后保留的位数。
            propList: ['*'],
            exclude: function(file) {
            //指定文件不转为rem
              return !(file.indexOf('layout\\header.vue') >= 0 || file.indexOf('layout/header.vue') >= 0)
            },
            mediaQuery: false, //(布尔值)允许在媒体查询中转换px。
            minPixelValue: 0 //设置要替换的最小像素值
          })
        ]
      }
    }
  },
}

.postcssrc.js

// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
    plugins: {
        autoprefixer: {
            overrideBrowserslist: ['Android 4.1', 'iOS 7.1', 'Chrome > 31', 'ff > 31', 'ie >= 8']
        },
        'postcss-pxtorem': {
            rootValue: 41.25,
            propList: ['*']
        }
    }
}

  plugins / flexible.js    main.js引入

!(function(n) {
  var e = n.document,
    t = e.documentElement,
    i = 1920,
    d = i / 50,
    o = 'orientationchange' in n ? 'orientationchange' : 'resize',
    a = function() {
      var n = t.clientWidth || 1920
      n > 1920 && (n = 1920)
      t.style.fontSize = n / d + 'px'
    }
  e.addEventListener && (n.addEventListener(o, a, !1), e.addEventListener('DOMContentLoaded', a, !1))
})(window)

 方案二

使用postcss-pxtorem可以将CSS样式中的px单位自动转换为rem或者vw/vh单位,在不同屏幕下自适应缩放。具体步骤如下:

  1. 安装依赖 在项目根目录下执行以下命令安装所需依赖:

npm install postcss postcss-loader postcss-pxtorem --save-dev
  1. 添加postcss-loader 在webpack.config.js文件中添加postcss-loader,作为css-loader之前的一个loader,如下所示:

module.exports = {
 module: {
   rules: [
    {
       test: /\.css$/,
       use: [
         'style-loader',
         'css-loader',
         'postcss-loader'
      ]
    },
     // 其他规则
  ]
}
}
  1. 配置postcss-pxtorem 在项目根目录下新建postcss.config.js文件,并添加如下配置:

module.exports = {
 plugins: [
   require('postcss-pxtorem')({
     rootValue: 75, // 根据设计稿设置,例如设计稿宽度为750px,这里设置为75
     propList: ['*']
  })
]
}

其中,rootValue是根元素字体大小的值,可以根据设计稿宽度进行设置,propList设置需要进行转换的属性。

  1. 编写CSS样式 在CSS样式中直接使用px单位即可,插件会自动转换为rem或者vw/vh单位。

需要注意的是,使用postcss-pxtorem可能会影响到既定的逻辑和样式,需要根据具体情况灵活调整。同时,在使用时也需要考虑到低版本浏览器的兼容性问题。

 

自定义vue 插件

举例 :

toast/toast.vue
<template>
  <div class="toast" v-show="isShow">
    {{ message }}
  </div>
</template>

<script>
export default {
  name: "Toast",
  components: {},
  data() {
    return {
      isShow: false,
      message: "",
    };
  },
  methods: {
    show(message = "空内容", duration = 2000) {
      this.isShow = true;
      this.message = message;
      console.log(123);
      if (timer) {
        clearTimeout(timer);
      }
      let timer = setTimeout(() => {
        this.isShow = false;
        this.message = "";
        clearTimeout(timer);
      }, duration);
    },
  },
};
</script>

<style scoped>
.toast {
  padding: 8px 10px;
  background: rgba(0, 0, 0, 0.75);
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  color: #fff;
  z-index: 999;
  font-size: 24px;
}
</style>

 

toast/index.js

  toast/index.js方案一:

 

import Toast from "./Toast";
const obj = {};
obj.install = function (Vue) {
    // 1.构建组件构造器
    const ToastCompon = Vue.extend(Toast);
    //2.new的方式 创建组件对象
    const toast = new ToastCompon();
    //3.将组件对象挂载到某一个对象上
    toast.$mount(document.createElement('div'));
    //4.将toast.$el添加到body中
    document.body.appendChild(toast.$el)
    Vue.prototype.$toast = toast;
}
export default obj;

 

  toast/index.js方案二:

import Toast from './Toast';
let obj = {};
obj.install = function (Vue) {
    Vue.component(Toast.name,Toast);
}
export default obj;

 

 

main.js

import Toast from "components/common/toast/index"
...
Vue.use(Toast);

 

使用

this.$toast.show(res, 1500);

 

 

vue 封装滚动插件  better-scroll

 

  1.  wrapper里必须只有一个子元素;
  2. 子元素的高度要比wrapper要高;
  3. 使用的时候,要确定DOM元素是否已经生成,必须要等到DOM渲染完成后,再new BScroll();
  4. 滚动区域的DOM元素结构有变化后,需要执行刷新 refresh() ;
  5. 上拉或者下拉,结束后,需要执行finishPullUp()或者finishPullDown(),否则将不会执行下次操作;
  6. better-scroll,默认会阻止浏览器的原生click事件,如果滚动内容区要添加点击事件,需要在实例化属性里设置click:true;

 

版本1.15.2

封装  better-scroll

Scroll.vue

<template>
  <div class="wrapper" ref="wrapper">
    <div class="content">
      <slot></slot>
    </div>
  </div>
</template>

<script>
//scroll组件必须要设定高度
import BScroll from "better-scroll";
export default {
  name: "Scroll",
  props: {
    probeType: {
      type: Number,
      default() {
        return 0;
      },
    },
    pullUpLoad: {
      type: Boolean,
      default() {
        return false;
      },
    },
  },
  components: {},
  data() {
    return {
      scroll: null,
    };
  },
  mounted() {
    this.scroll = new BScroll(this.$refs.wrapper, {
      click: true,
      probeType: this.probeType,
      pullUpLoad: this.pullUpLoad,
      //  pullUpLoad: {
      // threshold: -30 // 当上拉距离超过30px时触发 pullingUp 事件
      //stop: 20 // 回弹停留在距离顶部20px的位置
      // },
      // observeDOM: true,
      // observeImage: true,
    });
    //监听滚动
    this.scroll.on("scroll", (position) => {
      this.$emit("scroll", position);
    });
    //监听上拉加载
    this.scroll.on("pullingUp", () => {
      this.$emit("loadMore", "上啦加载更多");
    });
  },
  methods: {
    scrollTo(x, y, timer = 300) { //滚动到指定位置
      this.scroll && this.scroll.scrollTo(x, y, timer);
    },
    finishPullUp() {
      //事情做完,需要调用此方法告诉 better-scroll 数据已加载,否则上拉事件只会执行一次
      this.scroll && this.scroll.finishPullUp();
    },
    refresh() {
      //刷新
      this.scroll && this.scroll.refresh();
    },
    getScrollY() {
      //获取滚动Y轴位置
      return this.scroll ? this.scroll.y : 0;
    },
  },
};
</script>

<style scoped></style>

 

如何使用及注意事项

以下举例都是通过 this.$refs.scroll 来调用组件中的方法

1.引入scroll  组件将需要滚动的内容 包裹  ,  scroll组件必须要有高度 , 并设置overflow:hidden

2.每次下拉加载更多 , 需要  调用 this.$refs.scroll.finishPullUp();

3.滚动到指定元素位置  this.$refs.scroll.scroll.scrollToElement(this.$refs.tabControl2.$el, 200);

4.滚动到指定位置  this.$refs.scroll.scrollTo(x, y);

5.被包裹的内容的宽高发生改变 , 则需要重新调用   this.$refs.scroll.refresh();

例如图片加载完成后 ,高度发生变化 ,执行this.$refs.scroll.refresh() 

item.vue
//item.vue
<img v-lazy="goodsItem.goods_small_logo" alt="" @load="imageLoad" />
  methods: {
    imageLoad() {
      this.$bus.$emit("ItemImageLoad");
    },
 }

  home.vue

data() {
        return {
            itemImgListener: null,
            refresh: null
        }
},
mounted() {
    this.refresh = debounce(this.$refs.scroll.refresh, 200); //debounce为防抖函数
  this.itemImgListener = () => { this.refresh(); }; 
  this.$bus.$on("ItemImageLoad", this.itemImgListener); }
}  



6.跳转路由 , 保留页面位置

因为使用了 keep-alive 进行页面缓存 , 所以调用以下两个函数

activated() {
    //创建时跳到获取保留的位置,并刷新下页面
    this.$refs.scroll.scrollTo(0, this.saveY, 1);
    this.$refs.scroll.refresh();
  },
  deactivated() {
    //离开时保留滚动位置
    this.saveY = this.$refs.scroll.getScrollY();
    this.$bus.$off("itemImageLoad", this.itemImageListener); //解绑itemImageLoad
  },

 

简单举例:   

<template>
  <div id="home">
    <scroll
      class="content"
      ref="scroll"
      @scroll="contentScrollTop"
      :probeType="3"
      :pullUpLoad="true"
      @loadMore="loadMore"
    >
     ...需要滚动的内容
    </scroll>
</div> </template> <script>

  import Scroll from "components/common/scroll/Scroll.vue";
export default {
  name: "Home",
  components: {
    Scroll,
  },
  props: {},
  data() {
    return {     
    };
  },
  created() {
   
  },
  methods: {
    getHomeGoods(type, numpage) {
      const page = this.goods[type + ""].page + 1;
      getHomeGoods(type, page, 10).then((res) => { //滚动加载更多
        this.goods[type + ""].list.push(...res.message.goods);
        this.goods[type + ""].page += 1;
        //下拉加载更多
        this.$refs.scroll.finishPullUp();
      });
    },
    tabClick(index) {//滚动到元素位置
      this.$refs.scroll.scroll.scrollToElement(this.$refs.tabControl2.$el, 200);
    },
    backClick() {
      //返回顶部
      this.$refs.scroll.scrollTo(0, 0);
    },
    debounce(func, delay) {
    //防抖
      let timer = null;
      return function (...args) {
          if (timer) {
              clearTimeout(timer);
          }
        timer = setTimeout(() => {
            func.apply(this, args);
        }, delay);
      }

  },
  computed: {
  
  },
  mounted() {
    this.refresh = debounce(this.$refs.scroll.refresh, 200);
        this.itemImgListener = () => {
            this.refresh();
        };
        this.$bus.$on("ItemImageLoad", this.itemImgListener);
  },
  activated() {
    //创建时跳到获取保留的位置,并刷新下页面
    this.$refs.scroll.scrollTo(0, this.saveY, 1);
    this.$refs.scroll.refresh();
  },
  deactivated() {
    //离开时保留滚动位置
    this.saveY = this.$refs.scroll.getScrollY();
    this.$bus.$off("itemImageLoad", this.itemImageListener);
  },
};
</script>

<style scoped>
.content {
  /**scroll组件必须要设定高度 */
  /* height: calc(100vh - 93px); */
  overflow: hidden;
  position: fixed;
  top: 44px;
  bottom: 49px;
  z-index: 10;
  left: 0;
  right: 0;
  overflow: hidden;
}
</style>

 

 

Vue防止按钮重复提交

src/directive/preventRepeatClick.js

import Vue from 'vue'

const preventRepeatClick = Vue.directive('preventRepeatClick', {
  inserted(el, binding) {
    el.addEventListener('click', () => {
      // 为了解决撤销恢复按钮禁用时间不同问题
      let timer, ele
      ele = el.children[0].innerHTML !== '撤 销' && el.children[0].innerHTML !== '恢 复'
      timer = ele ? 800 : 100
      if (!el.disabled) {
        el.disabled = true
        setTimeout(() => {
          el.disabled = false
        }, binding.value || timer)
      }
    })
  }
})

export {preventRepeatClick}

示例

 <a-button
  v-prevent-repeat-click
  type="link"
  :class="item.type == 'danger' ? '' : ''"
  :size="item.size || 'default'"
  :disabled="item.disabled || false"
 >
  {{ item.title }}
 </a-button>

 

...

 

posted @ 2021-03-31 17:18  混名汪小星  阅读(98)  评论(0编辑  收藏  举报