vue11----前端优化、路由懒加载(异步组件)、keep-alive、localStorage二次封装、vue-lazyload图片懒加载、mint-ui、封装加载loading条、axios请求x-www格式的数据、nginx代理、路由守卫、mint-ui实现下拉刷新,上拉加载更多、自己封装过哪些组件和模块

### 优化

    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的二次封装

猜你喜欢

转载自www.cnblogs.com/wuqilang/p/12310518.html