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
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/] }, } }
自定义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.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" />
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>
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); },
}, computed: { }, mounted() {
}, 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>
...