vue遇到的问题和解决方法,vue 问题

  vue遇到的问题和解决方法,vue 问题

  本文主要介绍了vue中常见问题及解决方法的相关信息,通过示例代码进行了详细介绍,对您的学习或工作有一定的参考价值。有需要的朋友就跟着下面的边肖学习吧。

  有些问题不仅限于Vue,也适用于其他类型的SPA项目。

  

1. 页面权限控制和登陆验证页面权限控制

  页面权限控制是什么意思?

  即一个网站有不同的角色,比如管理员和普通用户,需要不同的角色访问不同的页面。如果一个页面被一个超越其权限的角色访问,那么就必须进行限制。

  一种方法是动态添加路由和菜单进行控制,不可访问的页面不添加到路由表中。这是方法之一。详情请参考下一节《动态菜单》。

  还有一种方式是所有页面都在路由表里,访问时判断角色权限即可。有权限就允许访问,没权限就拒绝访问。跳到404页。

  思路

  在每个路由的元属性中,将可以访问路由的角色添加到角色中。每次登录后,都会返回用户的角色。然后,在访问页面时,将路由的元属性与用户的角色进行比较。如果用户的角色在路由的角色中,那么它可以被访问,如果不是,那么它将被拒绝。

  代码示例

  路由信息

  路线:[

  {

  路径:“/login”,

  名称:登录,

  元:{

  角色:[管理员,用户]

  },

  组件:()=导入(./components/Login.vue )

  },

  {

  路径:“主页”,

  姓名:家,

  元:{

  角色:[admin]

  },

  组件:()=导入(./views/home . vue’)

  },

  ]

  页面控制

  //假设有两个角色:管理员和用户

  //下面是从后台获取的用户角色

  常量角色=用户

  //进入页面前触发router.beforeEach事件。

  router.beforeEach((收件人,发件人,下一个)={

  if(to . meta . roles . includes(role)){

  下一个()

  }否则{

  下一步({路径:/404})

  }

  })

  登陆验证

  一般只要登录过一次网站,就可以直接访问网站的其他页面,不需要再次登录。我们可以通过令牌或cookie来实现它。下面的代码展示了如何使用令牌来控制登录验证。

  router.beforeEach((收件人,发件人,下一个)={

  //如果有令牌,说明用户已经登录。

  if(local storage . getitem( token ){

  //如果您已登录,访问登录页面会将您重定向到主页。

  if (to.path===/login) {

  下一步({路径:/})

  }否则{

  下一步({path: to.path /})

  }

  }否则{

  //如果不登录,访问的任何页面都会被重定向到登陆页面。

  if (to.path===/login) {

  下一个()

  }否则{

  下一个(`/登录?redirect=${to.path} `)

  }

  }

  })

  

2. 动态菜单

  写一个后台管理系统,估计很多人都遇到过这样的需求:根据后台数据动态添加路线和菜单。你为什么这么做?因为不同的用户有不同的权限,所以他们可以访问的页面是不同的。

  动态添加路线

  可以使用vue-router的addRoutes方法动态添加路由。

  先看看官方介绍:

  router.addRoutes

  router . add routes(routes:ArrayRouteConfig)

  动态添加更多路由规则。该参数必须是满足routes选项要求的数组。

  例如:

  常量路由器=新路由器({

  路线:[

  {

  路径:“/login”,

  名称:登录,

  组件:()=导入(./components/Login.vue )

  },

  {路径:“/”,重定向:“/home”},

  ]

  })

  上面的代码与下面的代码具有相同的效果。

  常量路由器=新路由器({

  路线:[

  {路径:“/”,重定向:“/home”},

  ]

  })

  router.addRoutes([

  {

  路径:“/login”,

  名称:登录,

  组件:()=导入(./components/Login.vue )

  }

  ])

  在动态添加路由的过程中,如果有404页面,一定要加在最后,否则登录时会被重定向到404页面。

  与此类似,这个规则必须最后添加。

  {路径: * ,重定向:/404}

  动态生成菜单

  假设从后台返回的数据如下所示:

  //左侧菜单栏数据

  菜单项:[

  {

  名称:家,//要跳转的路由名称不是路径

  尺寸:18,//图标大小

  类型: md-home ,//icon类型

  文本: 主页 //文本内容

  },

  {

  文本: 二级菜单,

  键入: ios-paper ,

  儿童:[

  {

  键入: ios-grid ,

  名称: t1 ,

  文本: 表格

  },

  {

  文本: 三级菜单,

  键入: ios-paper ,

  儿童:[

  {

  键入: IOs-通知-大纲,

  名称:消息,

  文本: 查看消息

  },

  ]

  }

  ]

  }

  ]

  来看看怎么将它转化为菜单栏,我在这里使用了接口的组件,不用重复造轮子。

  !-菜单栏-

  menu ref= aside menu theme= dark width= 100% @ on-select= gotoPage

  accordion:open-names= openMenus :active-name= current page @ on-open-change= menu change

  !-动态菜单-

  div v-for=(item,index) in menuItems :key=index

  子菜单v-if= item。“children”:name=“index”

  模板槽=标题

  icon:size= item。 size :type= item。类型/

  span v-show= isshowaditle“{ item。text } }/span

  /模板

   div v-for=(subItem,i) in item.children :key=index i

  子菜单v-if=子项。 children :name= index - I

  模板槽=标题

  icon:size= subitem。 size :type=子项目。类型/

  span v-show= isshowaditle“{ subitem。text } }/span

  /模板

  菜单项中的MenuItem class= menu-level-3 v-for=(三项,k)。 children :name=三项。 name :key= index I k

  icon:size=三个项目。 size :type=三项。类型/

  span v-show= isshowaditle { three item。text } }/span

  /MenuItem

  /子菜单

  MenuItem v-else v-show= isshowasitle :name= subitem。名称

  icon:size= subitem。 size :type=子项目。类型/

  span v-show= isshowaditle“{ subitem。text } }/span

  /MenuItem

  /div

  /子菜单

  MenuItem v-else :name=item.name

  icon:size= item。 size :type= item。类型/

  span v-show= isshowaditle“{ item。text } }/span

  /MenuItem

  /div

  /菜单

  代码不用看得太仔细,理解原理即可,其实就是通过三次迭代不停的对子数组进行循环,生成三级菜单。

  不过这个动态菜单有缺陷,就是只支持三级菜单。一个更好的做法是把生成菜单的过程封装成组件,然后递归调用,这样就能支持无限级的菜单。在生菜菜单时,需要判断一下是否还有子菜单,如果有就递归调用组件。

  动态路由因为上面已经说过了用添加路线来实现,现在看看具体怎么做。

  首先,要把项目所有的页面路由都列出来,再用后台返回来的数据动态匹配,能匹配上的就把路由加上,不能匹配上的就不加。最后把这个新生成的路由数据用添加路线添加到路由表里。

  const asyncRoutes={

  主页:{

  路径:"主页",

  姓名:家,

  组件:()=导入(./views/home。vue’)

  },

  t1 :

  路径:“t1”,

  名称: t1 ,

  组件:()=导入(./views/t1。vue’)

  },

  密码:{

  路径:"密码",

  名称:密码,

  组件:()=导入(./视图/密码。vue’)

  },

  消息:{

  路径:"邮件",

  名称:消息,

  组件:()=导入(./views/msgvue’)

  },

  userinfo: {

  路径:“userinfo”,

  名称:用户信息,

  组件:()=导入(./views/userinfo。vue’)

  }

  }

  //传入后台数据生成路由表

  菜单路由(菜单数据)

  //将菜单信息转成对应的路由信息动态添加

  函数菜单路由(数据){

  常量结果=[]

  const children=[]

  结果。推送({

  路径:"/",

  组件:()=导入(./components/Index.vue ),

  孩子们,

  })

  data.forEach(item={

  生成路由(子项,项目)

  })

  children.push({

  路径:"错误",

  名称:"错误",

  组件:()=导入(./components/Error.vue )

  })

  //最后添加404页面否则会在登陆成功后跳到404页面

  结果。推送(

  {路径:"*",重定向:"/error"},

  )

  回送结果

  }

  函数生成器路线(子项,项目){

  if (item.name) {

  儿童推送(异步路由[项目。名称])

  } else if (item.children) {

  item.children.forEach(e={

  生成路由(子进程,e)

  })

  }

  }

  动态菜单的代码实现放在github上,放在这个项目的src/components/Index.vue、src/permission.js和src/utils/index.js文件中。

  

3. 前进刷新后退不刷新需求一:

  在列表页面中,当你第一次进入时,你需要数据。

  单击列表项目,跳转到详细信息页面,然后从详细信息页面返回到列表页面,无需刷新。

  也就是说,如果从其他页面进入列表页面,需要刷新才能获取数据。从详细信息页面返回列表页面时不刷新。

  解决办法

  在App.vue中设置:

  keep-alive include=list

  路由器-视图/

  /保持活力

  假设列表页是list.vue,详情页是detail.vue,两者都是子组件。

  我们在keep-alive中添加列表页面的名称,并缓存列表页面。

  然后在列表页的创建函数中添加ajax request,这样只有第一次进入列表页时才会请求数据,从列表页跳转到详情页再从详情页返回时列表页不会刷新。这将解决问题。

  需求二:

  在需求1的基础上,再增加一个需求:可以在详情页中删除对应的列表项,然后返回列表页时需要刷新才能再次获取数据。

  我们可以在路由配置文件的detail.vue中添加一个元属性。

  {

  路径:“/detail”,

  名称:详细信息,

  组件:()=导入(./view/detail . vue’),

  meta: {isRefresh: true}

  },

  这个元属性可以被这个。$ route . meta . is在详细信息页面中刷新。

  设置该属性后,在App.vue文件中设置watch $route属性。

  观察:{

  $路线(至,自){

  const fname=from.name

  const tname=to.name

  if (from.meta.isRefresh (fname!= detail tname== list ){

  from.meta.isRefresh=false

  //在此处再次请求数据

  }

  }

  },

  这样就不需要用ajax在列表页面的created函数中请求数据,放到App.vue中进行处理了。

  触发请求数据有两种情况:

  当您从其他页面(除了详细信息页面)进入列表时,您需要请求数据。当你从详情页返回到列表页时,如果详情页的meta属性中的isRefresh为true,你也需要再次请求数据。

  当我们在细节页面中删除相应的列表项时,我们可以将细节页面的meta属性中的isRefresh设置为true。然后返回列表页面,页面会再次刷新。

  解决方案二

  其实需求二还有一个更简洁的方案,就是使用router-view的关键属性。

  点火电极

  router-view:key= $ route . full path /

  /保持活力

  首先,keep-alive使所有页面都被缓存。当你不想缓存某个路由页面,又想重新加载的时候,可以在跳转的时候传递一个随机字符串,让它重新加载。例如,如果您从列表页进入详细信息页,然后在详细信息页中删除列表页中的选项,当您从详细信息页返回列表页时,该选项将被刷新。我们可以这样跳:

  这个。$router.push({

  路径:“/list”,

  查询:{ randomID: id Math.random() },

  })

  这样的方案相对简单。

  

4. 多个请求下 loading 的展示与关闭

  一般在vue中,拦截器结合axios控制加载显示和关闭,如下:

  在App.vue中配置全局加载

  div class=" app "

  keep-alive:include= keepAliveData

  路由器-视图/

  /保持活力

  div class= loading v-show= is show loading

  旋转大小=大/旋转

  /div

  /div

  同时设置axios拦截器。

  //添加请求拦截器

  这个。$ axios . interceptors . request . use(config={

  this.isShowLoading=true

  返回配置

  },错误={

  this.isShowLoading=false

  退货承诺.拒绝(错误)

  })

  //添加响应拦截器

  这个。$ axios . interceptors . response . use(response={

  this.isShowLoading=false

  返回响应

  },错误={

  this.isShowLoading=false

  退货承诺.拒绝(错误)

  })

  这个拦截器的功能是在请求之前打开加载,当请求完成或出现错误时关闭加载。

  如果一次只有一个请求,这很好。但是,如果有多个并发请求,就会出现问题。

  示例:

  现在,如果同时发出两个请求,拦截器this.isShowLoading=true将在请求之前打开加载。

  现在有一个请求结束了。This.isShowLoading=false拦截器关闭了加载,但另一个请求由于某种原因没有结束。

  结果就是页面请求还没有完成,加载却关闭了,用户会以为页面加载完成了,导致页面无法正常运行,造成用户体验差。

  解决方案

  添加一个loadingCount变量来计算请求的数量。

  加载计数:0

  再添加两个方法来增加或减少loadingCount。

  方法:{

  addLoading() {

  this.isShowLoading=true

  this.loadingCount

  },

  isCloseLoading() {

  this.loadingCount -

  if (this.loadingCount==0) {

  this.isShowLoading=false

  }

  }

  }

  拦截器看起来像这样:

  //添加请求拦截器

  这个。$ axios . interceptors . request . use(config={

  this.addLoading()

  返回配置

  },错误={

  this.isShowLoading=false

  this.loadingCount=0

  这个。$Message.error(网络异常,请稍后再试)

  退货承诺.拒绝(错误)

  })

  //添加响应拦截器

  这个。$ axios . interceptors . response . use(response={

  this.isCloseLoading()

  返回响应

  },错误={

  this.isShowLoading=false

  this.loadingCount=0

  这个。$Message.error(网络异常,请稍后再试)

  退货承诺.拒绝(错误)

  })

  这个拦截器的功能是:

  每当发出请求时,加载就被打开,并且loadingCount增加1。

  每次请求结束,loadingCount减1,判断loadingCount是否为0,如果为0,则关闭加载。

  这样就可以解决在多个请求下,由于某个请求提前终止而导致加载关闭的问题。

  

5. 表格打印

  打印所需的组件是print-js。

  普通形式印刷

  通用表格打印可以直接模仿组件提供的示例。

  printJS({

  printable: id,//DOM id

  类型:“html”,

  scanStyles: false,

  })

  Element-ui表格打印(同样适用于其他组件库的表格)

  element-ui的表,表面上看起来是一个表,实际上是由两个表组成的。

  表头是表格,表体是表格,这就导致了一个问题:打印时表体和表头错位。

  另外,当滚动条出现在表格中时,也会造成错位。

  解决方案

  我的想法是把两张桌子合二为一。print-js组件打印时,实际上是提取id对应的DOM的内容并打印出来。因此,在传入id之前,可以先提取表头所在的表的内容,插入到第二个表中,这样就合并了两个表。这时候印刷就不会出现错位。

  函数printHTML(id) {

  const html=document . query selector( # id)。innerHTML

  //创建新的DOM

  const div=document . createelement( div )

  const print domid= printDOMElement

  div.id=printDOMID

  div.innerHTML=html

  //提取第一个表格的内容,即表头。

  const ths=div.querySelectorAll(。El-table _ _ header-wrapper th’)

  const ThsTextArry=[]

  for(设i=0,len=ths.length我leni ) {

  如果(this[I]。innerText!==) ThsTextArry.push(ths[i]。内部文本)

  }

  //删除多余的头

  div.querySelector(。隐藏列)。移除()

  //第一个表的内容提取出来后就没用了。删除它

  div.querySelector(。El-table _ _ header-wrapper’)。移除()

  //将第一个表格的内容插入第二个表格

  让newHTML=tr

  for(设i=0,len=ThsTextArry.length我leni ) {

  new html= TD style= text-align:center;font-weight:bold ThsTextArry[I]/TD

  }

  newHTML=/tr

  div.querySelector(。El-table _ _ body-wrapper table’)。insertAdjacentHTML(afterbegin ,newHTML)

  //向页面添加一个新的DIV,然后删除它。

  document.querySelector(body )。appendChild(div)

  printJS({

  可打印:printDOMID,

  类型:“html”,

  scanStyles: false,

  style: Table { border-collapse:collapse } //表格样式

  })

  div.remove()

  }

  

6. 下载二进制文件

  通常前端下载文件有两种方式,一种是后台提供一个URL然后用window.open(URL)下载,另一种是后台直接返回文件的二进制内容,然后前端转换后再下载。

  由于第一种方法相对简单,这里就不讨论了。本文主要说明如何实现第二种方法。

  第二种方法需要使用Blob对象,这在mdn文档中描述如下:

  Blob表示不可变的原始数据类文件对象。Blob不一定以JavaScript本地格式表示数据。

  具体使用方法

  axios({

  方法: post ,

  URL:“/export”,

  })。然后(res={

  //假设数据是返回的二进制数据

  常量数据=res.data

  常量url=window。URL.createObjectURL(新Blob([数据],{ type: application/vnd . openxml formats-office document . spreadsheetml . sheet }))

  const link=document . createelement( a )

  link.style.display=none

  link.href=url

  link.setAttribute(下载, excel.xlsx )

  document.body.appendChild(链接)

  link.click()

  document.body.removeChild(链接)

  })

  下载文件,看看结果是否正确。

  一堆乱码.

  一定有什么不对劲。

  最后,我们发现了参数responseType的问题,它指示服务器响应的数据类型。由于后台返回的是二进制数据,我们只好将其设置为arraybuffer,然后看结果是否正确。

  axios({

  方法: post ,

  URL:“/export”,

  responseType: arraybuffer ,

  })。然后(res={

  //假设数据是返回的二进制数据

  常量数据=res.data

  常量url=window。URL.createObjectURL(新Blob([数据],{ type: application/vnd . openxml formats-office document . spreadsheetml . sheet }))

  const link=document . createelement( a )

  link.style.display=none

  link.href=url

  link.setAttribute(下载, excel.xlsx )

  document.body.appendChild(链接)

  link.click()

  document.body.removeChild(链接)

  })

  这次没有问题,文件可以正常打开,内容正常,不再乱码。

  根据后台接口内容决定是否下载文件

  作者项目中大量页面都有下载文件的要求,这个要求有点变态。

  具体要求如下

  如果下载文件的数据量符合要求,就正常下载(每个页面对下载数据量的限制不一样,所以不能写死在前端)。

  如果文件太大,后台会返回{code: 199999,msg:文件太大,请重置查询词,data: null},然后前端会给出错误提示。

  先来分析一下。首先根据上面我们都知道下载文件的接口响应数据类型是arraybuffer。无论返回的数据是二进制文件还是JSON字符串,前端实际接收的都是arraybuffer。所以我们要对arraybuffer的内容做一个判断,在接收数据的时候转换成一个字符串,判断是否有代码:199999。如果是,会给出错误提示;如果没有,就是正常文件,可以下载。具体实现如下:

  xios . interceptors . response . use(response={

  const res=response.data

  //确定响应数据类型是否为ArrayBuffer。true是下载文件的界面,false是正常界面。

  if (res instanceof ArrayBuffer) {

  const utf8 decoder=new text decoder()

  const u8arr=new Uint8Array(res)

  //将二进制数据转换为字符串

  const temp=utf8 decoder . decode(u8arr)

  if(temp . includes( { code:199999 ){

  消息({

  //字符串到JSON对象中

  消息:JSON.parse(temp)。味精,

  类型:“错误”,

  时长:5000,

  })

  返回Promise.reject()

  }

  }

  //普通类型接口,省略代码.

  返回资源

  },(错误)={

  //省略代码.

  退货承诺.拒绝(错误)

  })

  

7. 自动忽略 console.log 语句

  导出函数rewirteLog() {

  console.log=(函数(日志){

  return process . ENV . node _ ENV== development ?日志:函数(){}

  }(console.log))

  }

  在main.js中引入这个函数,执行一次就可以实现忽略console.log语句的效果。

  

总结

  关于vue中常见问题及解决方案的这篇文章就到这里了。有关vue常见问题的更多信息,请搜索我们以前的文章或继续浏览下面的相关文章。希望大家以后能多多支持我们!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: