如何优化你的vue项目

本文介绍了前端项目优化的多种方法,包括路由懒加载、组件缓存、代码高亮、请求反馈、无感知刷新token、图片懒加载、自定义指令等,旨在提升用户体验和减轻服务器压力。通过实例解析了如何实现这些优化策略,并提供了具体的代码示例,如使用highlight.js进行代码高亮,利用Vue自定义指令实现自动聚焦等。
摘要由CSDN通过智能技术生成

目录

为何优化项目

路由懒加载

组件缓存

文章内容 代码高亮

优化请求时给用户一个反馈

无感知刷新token

图片懒加载

自定义指令 (自动聚焦)

 抽离组件注册

 持久化储存方式

封装统一的提示信息

滚动条位置

 与上述对应(频道列表滚动条位置)


  • 为何优化项目

    • 我们的项目写好之后,为了优化用户的体验和减轻服务器的压力,我们回头一系列的优化,使自己的项目加载更快,所以要进行代码的优化
    • 而优化的方式有很多,具体如下
      • 路由懒加载
      • 组件缓存
      • 用户无感觉刷新token(登录后完成未完成的操作)
      • 图片懒加载
      • 减少代码的冗余(复用的代码封装函数)
      • 为了后期的维护,可以封装以下工具文件
        • 增删改查token
        • 封装api接口(各种接口可以再分文件)
        • 引入的第三方工具包(类似于day.js,moment..)
        • 本地储存增删改
        • 引入的组件库(vant..)
        • 各种功能组件文件(首页为首页组件,个人信息为个人信息组件...)
        • 自定义指令
        • 网络请求(axios,jq...)
        • Vuex , router
        • 全局前置守卫(后置守卫)
  • 路由懒加载

    • 使用路由懒加载来进行优化, 详细方式见作者博客路由懒加载(链接)
  • 组件缓存

    • 组件缓存在路由挂载点router-view外层套一个keep-alive组件
    • 一级路由和二级路由的挂载点不同 , 根据需求加缓存
    • 缓存之后会出现的问题
      • 文章列表也会被缓存 , 多次进入同一篇 , 会出现内容一致
      • 缓存之后的个人信息 , 下次登陆的时候会是上次登陆的信息
      • 搜索结果组件搜索到的为同一个结果
    •  解决方法
    • exclude 不缓存的组件
      include 缓存的组件
      ArticleDetail,Login,Search,SearchResult 为 组件内部的name名字
      <template>
        <div>
          <keep-alive exclude="ArticleDetail,Login,Search,SearchResult">
            <router-view></router-view>
          </keep-alive>
        </div>
      </template>
      组件缓存后会生成两个生命周期钩子函数
      activated(组件激活) , deactivated(失去激活)
      将初始化换成activated也可以
    •  组件缓存之后 , 会造成一系列可能存在的问题 , 针对不同的问题 , 通过不进行缓存或者修改生命周期函数 , 类似于以下的用户头像不更新问题
    • 组件缓存之后头像不更新问题
      • 解决方案一 : 把created换成activated钩子函数即可
      • 解决方案二 
        • 将头像变量保存到vuex中,定义mutations修改头像函数
        • 在请求到头像数据之后 ,  存到vuex变量之中
      • 引申:头像 , 昵称 , 手机号 , 性别 也是同样道理
  • 文章内容 代码高亮

    • 获取的文章列表之后没有代码高亮的效果
    • 想要让代码高亮怎么办 , 必须在用户发布文章的时候,就要使用富文本编辑器,将想要的代码分段用pre+code标签包裹
    • 前端可以通过获取这些标签名,指定类名 , 分别给予相应的样式
    • 解决: 基于highlight.js 美化详情页的代码片段(highlight.js中文网 )
    • 使用方式
      • 下载此插件到项目之中
      • yarn add highlight.js -D
      • 在入口文件main.js引入
      • import hljs from 'highlight.js' // hljs对象
        import 'highlight.js/styles/default.css' // 代码高亮的样式
      • 注册高亮代码(自定义指令)
      •  
        Vue.directive('highlight', function (el) { // 自定义一个代码高亮指令
            const highlight = el.querySelectorAll('pre, code') // 获取里面所有pre或者code标签
            highlight.forEach((block) => {
                hljs.highlightElement(block) // 突出显示这些标签(以及内部代码, 会自动识别语言)
            })
        })
      • 在铺设文章的标签上使用自定义指令 v-highlight 指令即可 
  • 优化请求时给用户一个反馈

    • 在网络较慢的时候 , 可以给用户一个提示正在加载中
    • 在没有文章的时候 , 提示文章正在加载中
    • 使用组件库中特有的加载效果提示(vant , element-ui)
    • 以vant为例子
    • 找到组件库的加载效果提示组件 , 在main.js全局注册
    • import { Loading } from 'vant';
      
      Vue.use(Loading);
    • 思路 : 当发起网络请求加载的时候 , 这个时候文章和加载组件应该你死我活,当文章没有全部加载的时候,这个时候显示loading组件,加载完毕之后loading应该消失,文章显示 , 所以使用v-if和v-else , 而进行v-if判断的时候 , 条件使用文章内容请求的数据进行判断 , 当数据为undefined的时候 , 文章不显示 , 请求完毕后 数据已经不为undefined , 即文章显示
    • artObj.title 为请求过来的文章信息
      <!-- 文章加载中... -->
      <van-loading color="#1989fa" class="loading" v-if="artObj.title === undefined">文章疯狂加载ing...</van-loading>
      <div v-else>
      	<!-- 文章信息区域 -->
      </div>
    • 注意 : 使用组件的时候记得看一下div结构 , 组件不显示可能是上面的元素覆盖了组件
  • 无感知刷新token

    • 无感知刷新token , 需要后台有对应的接口 , 登陆的时候收到两个token , 一个用于用户认证 , 另一个用于登陆过期 , 无感刷新token
    • 当用户认证失败之后 , 用户会跳转到登录页
    • 有些地方使用不到用户认证 , 但执行了登陆后的操作 , 就需要用户去登录页面 , 登陆完成之后 , 继续执行刚才的操作
    • 思路 : 在发送网络请求的时候 , axios提供了请求响应器和拦截响应器 , 只需要在响应错误的时候 , 进行相应的判断即可
    • // axios添加响应拦截器
      axios.interceptors.response.use(function (response) {
        // 对响应数据做点什么
        return response
      }, async function (error) {
       // 打印错误信息 寻找需要判断的条件
        console.dir(error)
        // 对响应错误做点什么
        if (error.response.status === 401) {
          // 清除token
          removeToken()
          // 刷新token的请求
          const res = await newToken()
          // 拿到刷新后的token 继续设置新的token
          setToken(res.data.data.token)
          // 携带请求头 , 这个请求头为刷新token后后台给予的新的token 
          // 不加请求头 , 会进入死循环
          error.config.headers.Authorization = 'Bearer ' + res.data.data.token
          // error.config 存储了用户上述操作的请求 , 过期之后登录token继续完成上次未完成的操作
          //  记得return出去 才可以执行
          return axios(error.config)
          // router.replace('/login')
        } else if (error.response.status === 500 && error.response.config.url === '/v1_0/authorizations') {
          // 刷新token和retoken都过期之后 , 进行的判断
          // 清除token
          removeToken()
          // 将获取到的存储到本地
          localStorage.removeItem('refresh_token')
          // 跳转页面 携带失败后的路由地址和参数
          // 在登陆的同时 进行判断
          router.push({
            path: '/login',
            query: {
              pa: router.currentRoute.fullPath
            }
          })
        }
        return Promise.reject(error)
      })
    •  登录的时候 进行判断 路由地址是否有参数
    • this.$router.push({
                path: this.$route.query.pa || '/layout' 
              // 有未遂的地址就回去, 如果没有就去/layout
              })
  • 图片懒加载

    • 当图片标签进入到视口才加载图片
    • 图片的src会调用浏览器请求的图片资源
    • 将src属性替换掉 , 在恰当的时机 , 即图片到了视口之中 才开始加载图片
    • 原生实现图片懒加载的原理 : 将src替换为另外的属性data-src , 监听用户的滚动事件 , 当dom文档内卷的距离加上窗口的距离, 大于了图片到文档顶端的距离 说明图片已经存在到视口当中,这个时候将data-src替换为src,既可以加载图片
    • 组件使用(vant为例子),查阅文档 , 找到图片懒加载组件 , 引入到main.js使用
    • 使用方式 : 将所有的img的src换成v-lazy指令即可
    • <!-- 标题区域的插槽 -->
      <template #title>
      <div class="title-box">
          <!-- 标题 -->
          <span>{{ obj.title }}</span>
          <!-- 单图 -->
          <img
               class="thumb"
               v-lazy="obj.cover.images[0]"
               v-if="obj.cover.type === 1"
               />
      </div>
      <!-- 三张图片 -->
      <div class="thumb-box" v-if="obj.cover.type > 1">
          <img
               class="thumb"
               v-for="(imgUrl, index) in obj.cover.images"
               :key="index"
               v-lazy="imgUrl"
               />
      </div>
      </template>
    • Vue.use(Lazyload, {
        preLoad: 1.0 // 图片开始加载判断范围
        // 1.0 (出现在视口就加载)
        // 1.3 (多往下加载30%范围)
      })
      // 全局入口main.js文件配置
      // 根据vant文档使用
  • 自定义指令 (自动聚焦)

    • 用户修改弹框类型的内容时候 , 类似于 发表评论 , 修改昵称 姓名 手机号 ...
    • 只有第一次自动聚焦的问题(点击第二次没有自动聚焦了)
    • 自动聚焦依赖自定义指令inserted执行
      • 而Dialog只有第一次出现是插入到真实DOM , 才会触发inserted方法
      • 而Dialog以后初选是css层面的县市出现 , 不会触发inserted方法
    • 解决 : 给自定义指令添加updata方法 , 指定所在DOM更新时执行
    • export default function (Vue) {
        Vue.directive('fofo', {
          // 指令所在标签, 插入到真实DOM时, 才触发
          inserted (el) {
            // van-search组件封装一套div里包含input
            // el是原生的div标签
            // 往里获取到input
            // JS触发标签事件, 直接.事件名()\
            if (el.nodeName === 'TEXTAREA' || el.nodeName === 'INPUT') {
              el.focus()
            } else {
              // el不是输入框, 尝试往里查找
              const inp = el.querySelector('input')
              const textA = el.querySelector('textarea')
              if (inp) {
                inp.focus()
              }
              if (textA) {
                textA.focus()
              }
            }
          },
          // 指令所在标签, 发生更新时触发, (例如: display:none隐藏->出现)
          update (el) {
            if (el.nodeName === 'TEXTAREA' || el.nodeName === 'INPUT') {
              el.focus()
            } else {
              // el不是输入框, 尝试往里查找
              const inp = el.querySelector('input')
              const textA = el.querySelector('textarea')
              if (inp) {
                inp.focus()
              }
              if (textA) {
                textA.focus()
              }
            }
          }
        })
      }
  •  抽离组件注册

    • 入口文件main.js入口代码过多 , 将一致的分散出去 , 方便后期管理维护
    • 以vant组件库为例子
    • 将vant注册代码复制到另一个文件中
    • import Vue from 'vue'
      import { NavBar, Form, Field, Button, Tabbar, TabbarItem, Icon, Tab, Tabs, Cell, List, PullRefresh, ActionSheet, Popup, Row, Col, Badge, Search, Divider, Tag, CellGroup, Image, Dialog, DatetimePicker, Loading, Lazyload } from 'vant'
      
      ... // 省略上面的一些
      Vue.use(Lazyload)
      Vue.use(Field)
      Vue.use(Button)
      Vue.use(NavBar)
    • 在main.js引入 , 只需要将代码执行的话 就不用按需或者全部导出
    •  
      import './VantRegister'
      // VantRegister js文件的名字
  •  持久化储存方式

    • 将本地持久化储存封装到一个文件下
    • 创建utils/storage.js文件, 定义4个方法
    • // 本地存储方式
      // 如果同时有sessionStorage和localStorage, 可以封装2份
      // 现在我只封装一种统一的方式
      export const setStorage = (key, value) => {
        localStorage.setItem(key, value)
      }
      export const getStorage = (key) => {
        return localStorage.getItem(key)
      }
      export const removeStorage = (key) => {
        localStorage.removeItem(key)
      }
      export const clearStorage = () => {
        localStorage.clear()
      }
      
    • 把所有使用本地存储的地方, 都统一换成这里定义的方法
    • 好处 : 以后如果切换本地存储的方式 , 可以直接修改utils/storage.js内的真正实现即可
  • 封装统一的提示信息

    • 以后的项目如果需要更换提示框 , 只需要在这里统一修改即可
    • 类似于vant里的Notity提示
    • // 整个项目的统一的通知
      // 先用vant组件库里的Notify方法
      // import { Notify } from 'vant'
      import { Toast } from 'vant'
      
      export default {
      引入到main.js, 用Vue.use注册导致install执行
        install (Vue) {
          // Vue原型上, 添加属性$notify -> 通知方法
          // 后期只需要修改原型的组件库方法即可
          Vue.prototype.$notify = Toast
        }
      }
      /* 
      export const MyNotify = ({ type, message }) => {
      //   Notify({
      //     type: type,
      //     message: message
      //   })
      
        if (type === 'warning') {
          Toast({
            type: 'fail',
            message
          })
        } else if (type === 'success') {
          Toast({
            type,
            message
          })
        }
      }
      */
    •  使用的时候只需要调用原型上的方法即可
    • 封装统一UI弹窗, 以后更改封装的自定义函数内部实现, 整个项目所有UI弹窗都换掉
  • 滚动条位置

    • 用户滚动之后 , 切换其他的组件 , 仍然可以回到用户移动到的地方
    • 如果不进行优化 , 用户切换之后仍然回到第一条上面,用户体验不好
    • 原生的思路 : 监听window的滚动事件 ,记录滚动的位置 , 当用户离开之后回来 , 将滚动的值重新赋值给html的scrollTop即可
    • created () {
            window.addEventListener('scroll', () => {
            console.log('滚动了')
            // 滚动事件window/document
            // 获取/设置滚动位置 -> html
            // console.log(document.documentElement.scrollTop)
            this.$route.meta.scrollT = document.documentElement.scrollTop
          })
        },
      // 组件激活
      activated () {
          // 路由对象上存额外信息, 滚动位置设置回来即可
          document.documentElement.scrollTop = this.$route.meta.scrollT
      },
      特别注意 : 重点重点 
      1. 不能将滚动的值赋给变量 , 因为keep-alive组件缓存之后 , 会将变量的值直接改为0
      2. 那么问题 将变量存到哪里去呢 , 路由元信息 meta:{ } 中
      {
              path: 'home',
              meta: {
                scrollT: 0 // 首页滚动的位置
              }, // 路由元信息(路由对象里保存更多有用的数据)
              component: () => import('@/views/Home')
      }
  •  与上述对应(频道列表滚动条位置)

    • 明确结构 , 当我们点击不同的频道时 , 滚动后将值存到相对于的频道中
    • 使用对象的一一映射关系
    • // “频道名称”和“滚动条位置”之间的对应关系,
      // 格式 { '推荐ID': 211, 'htmlID': 30, '开发者资讯ID': 890 }
      const nameToTop = {}
    • 查阅vant组件库文档 , 找到对于tabs对应方法
    •  
          // tabs切换事件
          tabsChangeFn () {
            // 2. 把频道ID对应的滚动条位置 (在nameToTop对象上), 设置上
            // active 为频道的id信息 和vant组件进行的绑定
            const st = nameToTop[this.active]
            // 注意: 切换tab栏的, DOM更新是异步的, 所以滚动位置, 在DOM缓过来以后, 再执行滚动
            setTimeout(() => {
              document.documentElement.scrollTop = st
            }, 0)
          },
          // tabs切换"前"的回调函数
          // return false阻止tabs切换
          beforeChangeFn () {
            // tabs切换, 如果从底下 切换到别的tab上, 滚动条会上来, 会拿到0的位置覆盖进去
            // 所以不能在这里存值
            // 1. 再切换频道之前, 把当前频道滚动位置, 先保存到对象里
            nameToTop[this.active] = document.documentElement.scrollTop
            return true
          }

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值