### 优化
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)
①下载依赖:
②引入和添加配置项(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 v-lazy="info.avatar">
此时,在Singer.vue中Network的img只加载了首屏的图片,随着屏幕滚动,加载更多的图片。
### UI框架
pc端:iView框架:https://www.iviewui.com/
移动端:mint-ui
### mint-ui(Rank.vue)
①下载依赖:
②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依赖:
②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: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下拉刷新,上拉加载更多
①下载依赖:
②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的二次封装