### 优化
1、加载速度的优化
①雪碧图---->base64、iconfont
②代码压缩
③图片视频压缩
④cdn缓存
⑤路由懒加载(异步组件) 首页引入的大文件进行分批次引入
2、运行效率优化
①减少http请求,页面打开之后基本不涉及到数据更改
<keep-alive include="[‘a‘,‘b‘]" exclude="[‘a‘,‘b‘]"></keep-alive>
正常的组件切换的时候执行创建和销毁,加上keep-alive后,组件保存到缓存中,不会执行创建和销毁。
②数据本地化(localStorage session(可以用keep-alive代替) cookie)
a、发起ajax前,先看一下本地缓存有没有,如果有就在缓存中取,如果没有再发起请求
b、localStorage的二次封装
(1)考虑兼容问题
(2)简化读取与存储
(3)有时间限制
c、图片懒加载
ui框架
插件:vue-lazyload
3、用户体验优化
①加载体验
②加载的loading条
### 路由懒加载(异步组件)(router.js)
原理:将每个组件都打成一个包,这样首页引入的大文件就进行分批引入。
实现:将所有的同步组件引入方式改为异步组件引入方式。
// import Recommend from "pages/Recommend.vue"; // import Singer from "pages/Singer.vue"; // import Detail from "pages/Detail.vue"; // 改为路由懒加载方式引入:引入组件的方式从同步变为异步 const Recommend=()=>import("pages/Recommend.vue"); const Singer=()=>import("pages/Singer.vue"); const Detail=()=>import("pages/Detail.vue");
此时,npm run build后在dist/js中多一个 chunk-00e2fa9f.7be196b5.js 文件。注意:npm run build后dist/js中的map文件可以全部删除。
### <keep-alive></keep-alive>(App.vue)
原理:<keep-alive>是vue的一个内置组件,被<keep-alive>包裹的组件,不会反复的经历创建与销毁,它在第一次创建后就会保存在缓存中,下次页面切换时直接从缓存中读取。
场景:页面打开后,基本涉及到数据更改的组件用<keep-alive>包裹,<keep-alive>和<template>一样,不会显示在页面上。
生命周期:加上keep-alive后产生的两个生命周期:activated、deactivated
created() { console.log("歌手创建") }, mounted() { console.log("歌手挂载") }, activated() { console.log("歌手缓存激活,进入keep-alive") }, deactivated() { console.log("歌手缓存停用,离开keep-alive") },
第一次进入歌手页面,会触发创建、挂载、缓存激活,离开时触发缓存停用,后面再进入和离开只会触发缓存激活和缓存停用。这样用keep-alive包裹的组件不进行数据请求了,以此来减少http请求,如果需要数据请求,在activated生命周期进行数据请求。
属性:<keep-alive include="" exclude="" max=5></keep-alive>
include:包括,这里填写的组件是将被缓存的组件,属性值为字符串或正则
exclude:排除,这里填写的组件是不需要被缓存的组件(实时更新的组件),属性值为字符串或正则
max:最多能缓存多少个组件,属性值为Number
注意:
1、include 和 exclude 属性允许组件有条件地缓存。二者都可以用逗号分隔字符串、正则表达式或一个数组来表示:
<!-- 逗号分隔字符串 --> <keep-alive include="a,b"> <component :is="view"></component> </keep-alive> <!-- 正则表达式 (使用 `v-bind`) --> <keep-alive :include="/a|b/"> <component :is="view"></component> </keep-alive> <!-- 数组 (使用 `v-bind`) --> <keep-alive :include="[‘a‘, ‘b‘]"> <component :is="view"></component> </keep-alive>
2、include和exclude不能同时使用
例子:App.vue中给router-view套上keep-alive标签:
<keep-alive> <router-view></router-view> </keep-alive>
此时,在推荐和歌手组件来回切换的时候,在Network里的js,就会进行缓存,不会一直请求。
### 数据本地化:localStorage的二次封装
思路:
(1)考虑兼容问题
(2)简化读取与存储
(3)有时间限制
代码实现:
①在utils中新建localStorage.js文件:
export default{ set(key,data,expiresTime){ let obj={ data:data, ctime:(new Date()).getTime(),//时间戳,同Date.now() expiresTime:expiresTime||1000*60*60 // 如果没有传过期时间,则设置过期时间一个小时 } localStorage.setItem(key,JSON.stringify(obj)); }, get(key){ let obj=JSON.parse(localStorage.getItem(key)); let getItem=(new Date()).getTime(); if(getItem-obj.ctime>=obj.expiresTime){ // 如果超时 localStorage.removeItem(key); return null; }else{ // 未超时 return obj.data; } } }
②main.js中引入并注册到Vue对象中:
import LocalStorage from "utils/localStorage.js";
Vue.prototype.$localStorage=LocalStorage;
③在任意组件,如Singer.vue中,通过this.$localStorage对象可以使用set()和get():
this.$localStorage.set("name","吴启浪") console.log(this.$localStorage.get("name"))
### 图片懒加载插件:vue-lazyload(Singer.vue)
①下载依赖:
npm install vue-lazyload
②引入和添加配置项(main.js):
import VueLazyLoad from "vue-lazyload"; Vue.use(VueLazyLoad,{ preLoad:1.3, // 预加载 error:require("./assets/musicLogo.png"), // 错误时显示 loading:require("./assets/musicLogo.png"), // 加载时显示 attempt:1 // 每次加载多少张 });
③v-lazy指令(Singer.vue):
将需要图片懒加载的img中 :src 替换为 v-lazy:
<img :src="info.avatar">
替换为
<img v-lazy="info.avatar">
此时,在Singer.vue中Network的img只加载了首屏的图片,随着屏幕滚动,加载更多的图片。
### UI框架
pc端:iView框架:https://www.iviewui.com/
移动端:mint-ui
### mint-ui(Rank.vue)
①下载依赖:
npm install mint-ui
②main.js中做全局引入,任何组件都可以用:
import MintUI from "mint-ui"; import "mint-ui/lib/style.css"; Vue.use(MintUI);
③Rank.vue中引入想要使用的组件(Toast提示和loading条):
import {Toast,Indicator} from "mint-ui";
④设置Toast提示的按钮和点击事件:
<button @click="toast">toast</button> methods: { toast(){ Toast({ message:"点击我了,操作成功", // 信息 position:"center", // 位置 duration:3000 // 持续时间 }); } }
⑤模拟数据加载时的loading条:
created() { Indicator.open({ text:"加载中", spinnerType:"fading-circle" }); setTimeout(() => { Indicator.close(); }, 2000); }
⑥在main.js中已经引入mint-ui和css文件的前提下,可以直接在组件中使用mt-button组件(Toast()需要在使用的时候在组件中进行引入):
<mt-button type="default">default</mt-button>
<mt-button type="primary">primary</mt-button>
<mt-button type="danger">danger</mt-button>
### 封装加载loading条(Recommend.vue)
①创建Loading组件:
<template> <div class="loading"> <img :src="img"> </div> </template> <script> // 以base64的方式引入图片 import img from "../assets/loading.gif"; export default { data() { return { img:img } }, } </script> <style lang="less"> @import "~style/index.less"; .loading{ position: fixed; top: 0;bottom: 0; left: 0;right: 0; margin: auto; display: flex; justify-content: center; align-items: center; img{ .w(30); .h(30); } } </style>
②Recommend.vue中引入组件,并且在data中声明 loading:true
import Loading from "components/Loading.vue"; data() { return { loading:true, songList:[] } }
③对ul中数据进行loading条优化,Loading组件和ul是二选一的关系:
<Loading v-if="loading"></Loading> <ul v-else class="songList"> <!-- --> <li v-for="(item,index) in songList" :key="index" @click="goDateil(item.creator.encrypt_uin)"> <img :src="item.imgurl"> <div class="info"> <h2>{{item.creator.name}}</h2> <p>{{item.dissname}}</p> </div> </li> </ul>
④在拿到songList数据的同时,设置 this.loading=false;
axios.get(url,(err,data)=>{ }).then((data)=>{ this.songList=data.data.list; this.loading=false; });
### axios请求以post方式请求 x-www 格式的数据(demo)
原理:axios请求格式默认为json,有的时候需要请求 x-www-form-urlencoded 的格式,此时页面会报404,需要将 content-type 属性设置为 application/x-www-form-urlencoded ,再将data参数用qs.stringify()进行转化。
①下载axios和qs依赖:
npm install axios qs
②main.js中引入axios,并设置给Vue:
import axios from "./utils/axios.js";
Vue.prototype.$axios=axios;
③根目录下新建vue.config.js文件处理跨域:
module.exports={ devServer:{ port:8989, proxy:{ "/maoEr":{ target:"https://www.missevan.com",// 目标服务器路径 changeOrigin:true,// 是否允许改变源,默认为true pathRewrite:{// 重写路径 "^/maoEr":"" } }, } } }
④App.vue中做数据请求(引入qs:import qs from "qs";):
mounted() { let url="/maoEr/site/getcomment"; let data={ order:1, pagesize:10, type:1, eId:1284079, p:1 } const options={ method:"POST", headers:{"content-type":"application/x-www-form-urlencoded"}, data:qs.stringify(data),// 需要下载qs依赖,npm i qs,node中queryString()有同样效果 url:url, } this.$axios(options).then((data)=>{ console.log(data) }); }
注意:如果使用node中的queryString有一样的效果,可以不用下载qs依赖。
①先引入queryString:
const querystring = require(‘querystring‘);
②
data:qs.stringify(data),
替换为
data:querystring.stringify(data),
### nginx代理(demo)
线上环境:npm run build 打包成一个静态文件夹,丢到服务器目录下,上线完成。
场景:解决npm run build之后的代理失效
原理:在vue.config.js中配置的代理服务器是由本地的npm run serve启动的服务器做支持,而npm run build后的index.html没有服务器支持,代理失效,数据请求全部失效。
注意:npm run build后的index.html直接打开会报错,此时将其中所有的css和js文件前面加上 . ,表示当前路径下。
步骤:
①下载nginx:http://nginx.org/en/download.html
②将dist文件夹拷贝到nginx/html文件夹中
③双击nginx.exe,cmd打开nginx目录,输入nginx -s reload,没有报错就成功了
④在地址栏输入当前电脑的ip,可以进入welcome to nginx,相当于双击打开index.html,在地址后面加上 /dist 可以打开 npm run build 后的项目。此时打开Network,可以发现请求地址由 http://localhost:52330/maoEr/site/getcomment 变为 http://192.168.43.185/maoEr/site/getcomment
⑤nginx/conf/nginx.conf文件下设置跨域代理:
location /maoEr {
proxy_pass https://www.missevan.com/;#注意这个域名后面有 / ,如果是ip不用加 /
}
⑥远程服务器上双击nginx.exe,nginx文件夹进入cmd,输入nginx -s reload
nginx命令:
nginx -s reload
start nginx
nginx -s stop
### 前端拦截 前端安全
1、鉴权 token session+cookie
2、拦截 路由拦截器 路由守卫 路由导航
### 路由守卫
1、全局前置守卫(所有的路由跳转都会经过这个函数):(router/index.js)
router.beforeEach((to,from,next)=>{ console.log("去哪里",to) console.log("从哪来",from) let token=false; if(to.name=="About"){ if(token){ next(); }else{ next("/login"); } }else{ next(); } })
场景:控制某些页面未登录情况下不可进,跳转至登录页面。
2、路由独享守卫(路由配置项中使用,只对当前路由起作用):(router/index.js)
{ path: ‘/login‘, name: ‘Login‘, component: () => import(‘../views/Login.vue‘), beforeEnter: (to, from, next) => { console.log("去哪儿",to) console.log("从哪来",from) next(); } }
3、组件内守卫:(About.vue)
(1)进入路由前(此时路由还没有进来,组件未创建,没有this):
beforeRouteEnter(to, from, next) { console.log("组件进入前") next(); }
(2)路由更新时:
组件复用时,例如用动态导航传参(在路由中更改about的path为 /about/:id,通过params接收参数),在mounted生命周期中只有第一次能获取到参数,这会造成在详情页的地址栏更换,但页面不变。此时,可以用到beforeRouterUpdate(),可以用to.params.id获取到参数。
beforeRouterUpdate(to, from, next) { console.log("组件更新",to.params.id) next(); }
另外,可以通过watch监听获取参数:
watch: { $route(newValue){ console.log("watch",newValue.params.id) } }
(3)路由离开时,可以在离开前做一些判断:
beforeRouteLeave(to, from, next) { console.log("组件要离开",this) // console.log("去哪儿", to); // console.log("从哪来", from); let state = confirm("确定要离开吗"); if (state) { next(); } }
### mint-ui下拉刷新,上拉加载更多
①下载依赖:
npm install mint-ui
②main.js中做全局引入:
import MintUI from "mint-ui" import "mint-ui/lib/style.css" Vue.use(MintUI)
③Home.vue中:
<template> <div class="home"> 这里是首页 <mt-loadmore :top-method="loadTop" :bottom-method="loadBottom" :bottom-all-loaded="allLoaded" ref="loadmore" > <ul> <li v-for="(item,index) in list" :key="index">{{item.name}}</li> </ul> </mt-loadmore> </div> </template> <script> export default { data() { return { list: [ { name: "AAA" }, { name: "BBB" }, { name: "CCC" }, { name: "DDD" }, { name: "EEE" }, { name: "FFF" }, { name: "RRR" }, { name: "HHH" }, { name: "NNN" } ], allLoaded: false }; }, methods: { loadTop() { let url = "/api/city/city/getHotCityList?version=6.0.4&referer=2"; this.$axios .get(url, () => {}) .then(data => { this.list = data.data.hot_city_List; // 拿到数据后让页面回到原位 this.$refs.loadmore.onTopLoaded(); }); }, loadBottom() { setTimeout(() => { this.list = this.list.concat([ { name: "窗前明月光" }, { name: "疑是地上霜" }, { name: "举头望明月" }, { name: "低头思故乡" } ]); console.log(this.list); this.allLoaded = false; this.$refs.loadmore.onBottomLoaded(); }, 1000); } } }; </script> <style> .home { overflow: scroll; // 注意:如果上拉函数无法触发,是因为这里没有设置超出滚动 } li { height: 50px; font-size: 20px; } .mint-loadmore-text { font-size: 20px !important; } .mint-loadmore-content { font-size: 20px !important; } </style>
### Q:
1、自己封装过哪些组件和模块?
组件:loading条的封装
模块:localStorage的二次封装