Vue使用策略
文章目录
开发 更新中,在vue开发中有了新想法会保存在这里,如果各位有更简便的想法,请告知,thanks in advance…
路由组件params
组件为:
{
path: '/fruits/:id',
component: () => import('./../pages/Fruits/Fruits.vue'),
name:'fruits'
props:true,
},
Fruits.vue中props设置.
props:['id'],
只要props设置为true,就可以将params转变成组件自身的属性.
比如说$router.push(‘fruits/3’)
那么Fruits.vue中this.id=3
(此处是个人测试且成功了的)
另外一个关于使用params的注意点:
倘若$router.push({
path:‘fruits’
params:{
id:3
}
})
一定会报错.,如果你使用对象形式的传参,一定记得要使用name来代表该路由,否则出错(具体原因不清楚,总之栽了好多次的跟斗.)
正确填写
$router.push({ name:'fruits', params:{ id:3 } })
但是
$router.push('fruits/3')
是不报错的.
v-for删除某一个元素.
开发中经常遇到要删掉某一个v-for的某一个元素.可以使用如下方法,可以使用但不唯一.
比如
<div class="one">
<ul>
<li v-for="( item,index ) of test" :key="item.one">{{item.one}}</li>
</ul>
</div>
vue代码
new Vue({
el:".one",
data() {
return {
test:[{one:'1'},{'one':2},{one:'3'}]
}
},
})
结果为:
那么你想在点击某个图标之后删除某个内容.
可以这么做:
在元素上添加上@click,并将index传入.
<div class="one">
<ul>
<li v-for="( item,index ) of test" :key="item.one" @click="deleteItem(index)">{{item.one}}</li>
</ul>
</div>
new Vue({
el: ".one",
data() {
return {
test: [{
one: '1'
}, {
'one': 2
}, {
one: '3'
}]
}
},
methods: {
// 关键点在这里.
deleteItem(index){
this.test.splice(index,1);
}
},
})
在点击第二个之后:
2消失.
这种方法很适合前后端开发,前端删除v-for中的某一个元素时所用.
一般接收json数据会从created开始,而点击删除按钮的时候只是给后端发送了删除的请求,前端的json并不会因此发生变化,因为在删除之后并不会再次接收一次json数据,(当然,可以显式设计),这也就意味着在created中接收的数据不会发生变化…
那么在发送了删除的请求之后,有两种选择(个人只想到这两种)可以进行动态的渲染该页面.
重新接收一次json数据,并将原来的created中接收的json数据更新
将json数据中的内容删除掉而不必再次从服务器端接受json数据.
显而易见,第一种方法加重服务器负担,第二种方法并不会有什么损失.
虽然将此种策略存放到Vue中,但事实上可以应用到任何前端框架.
鼠标移入元素转变颜色,移出转变为另一种颜色
<div v-for=(i,index) in list :key="i"> <h3 :class="{ cur: currentIndex == index }" @mouseenter="currentIndex=index" @mouseleave="currentIndex==-1" > <a>{{ c1.categoryName }}</a> </h3> </div> //注意:如果是在项目中,建议将@mouseenter和@mouseleave的值设置为一个函数,在函数中实现相应的功能,这样起码能够实现html和js的解耦
currentIndex是组件data中的一个属性默认为currentIndex=-1;这也就是说如果鼠标还没有移入的情况下是不会有颜色的转变的.
而这里实现颜色转变的主要方法就是@mouseenter @mouseleave;
只要鼠标移入,就将currentIndex设置为当前元素的索引,由于vue是响应式的,那么class中的currentIndex==index就会成立,cur类就会被添加到class类中去,cur中设置的类就会生效.如果你不设置@mouseLeave的话,那么即便鼠标离去,最后一个被指向的元素仍然包含cur类
vuex简化开发
前提是vuex的知识你都知道
当你发现store/index.js中单个文件的内容太冗杂,可以分成模块.
如下提取某个模块中的state:
...mapState({ categoryList: (state) => state.home.categoryList, }),
home是其中的某个模块.
当然,你可以使用getters来进行简化使用.如下:
const getters = { //当前形参state,当前仓库中的state,并非大仓库中的那个state goodsList(state){ //state.searchList.goodsList如果服务器数据回来了,没问题是一个数组 //假如网络不给力|没有网state.searchList.goodsList应该返回的是undefined //计算新的属性的属性值至少给人家来一个数组 // 突然想到一个新的知识点,倘若.够长,比如state.a.b.c.d.e.f,因为在其中任何一处地方都有可能为空,那么下一处就会报错.所以要么使用if,要么使用? :,但是这里要用到好多处判断.一个简便的方法就是:state?.a?.b?.c?.d?.e?.f||[] 这里是es10的链判断运算符`?.`倘若其中一处是null,那么下一处就返回[].这样就节省了if的判断. return state.searchList.goodsList||[]; } , trademarkList(state){ return state.searchList.trademarkList||[]; }, attrsList(state){ return state.searchList.attrsList||[]; } };
另外由于getters本身是没有模块化的.
所以在使用时直接写成
...mapGetters(['goodsList'])
即可.
// 以下是在另一个vue模板中找到的.
const getters = { //getters.js sidebar: state => state.app.sidebar, device: state => state.app.device, token: state => state.user.token, avatar: state => state.user.avatar, name: state => state.user.name, roles: state => state.user.roles, permission_routes: state => state.permission.routes } export default getters /* */ --------- //store/index.js const store = new Vuex.Store({ modules: { app, permission, settings, user, }, getters, //引入getters; })
这样也直接简化了开发.也就是说,即便你分了模块之后,也不必向之前那样
this.$store.state.user.sidebar
提取sidebar了,可以使用this.$store.state.sidebar
省了一步.
基本架构
最近看到的模板大多是这两种方法:
将ajax的基本配置存储到request.js(可以设置多个文件)中.暴露一个示例.api为它的文件夹.
store的actions添加方法得到ajax的数据.vue组件接收数据局并利用数据进行布局.
还有一种方法就是
跳过store这一个步骤,直接利用request.js导出的实例在组件中获取数据.
这样耦合性比较大,并且代码比较冗杂.设计上比上一个方法要简单.
某些关于开发vue的感悟
最近在看别人写的模板,有好多方法自己都想不到.所以先记下来再说.
(注意:以下代码为了方便读懂,大多并未截取那些模板中的内容,纯属自己手写,并未经过编译运行,所以有错很正常)
常见的vue单页面
之所以写了两个,是因为只想到这两个
1.上中下
2.(左右)
]
看一下以下的部分:(不必看的太详细,你只需要看到每一个路由所转向的组件都是Layout以及其children路由即可. layout组件就是上面图片中的main_body组件)
{ path: '/core', component: Layout, redirect: '/core/dict/list', name: 'coreDict', children: [ { path: 'dict/list', name: '数据字典', component: () => import('@/views/core/dict/list'), meta: { title: '数据字典' }, }, ], }, { path: '/example', component: Layout, redirect: '/example/table', name: 'Example', children: [ { path: 'table', name: 'Table', component: () => import('@/views/table/index'), meta: { title: 'Table', icon: 'table' }, }, { path: 'tree', name: 'Tree', component: () => import('@/views/tree/index'), meta: { title: 'Tree', icon: 'tree' }, }, ], },
如果你想通了,你会觉得特别惊奇!
LayOut组件就是上面图片中的main_body
但是你所请求的路径是什么?在哪里请求的路径?
你会发现每个路由中的跳转到的都是LayOut组件中.而layOut组件又是单页面中的一部分.
这不就实现了单页面跳转了吗?
注意:children中的组件就是LayOut组件中的<router-view/>中要显示的内容.
而children的位置一般在上面图片中的menu或header的位置.这样只要menu中一点击,main_body就会切换页面.单页面应用就此实现!
看一下Layout中的代码:
<template> <section class="app-main"> <transition name="fade-transform" mode="out-in"> <router-view :key="key" /> </transition> </section> </template>
关于App.vue的<router-view/>
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
你会发现这里并没有<router-link/> ,那页面会显示什么?
注意:routerVue和routerLink并不是在每个页面中都是配对的.
至于显示的是什么,看下段代码(router/index.js):
{ path: '/', component: Layout, redirect: '/dashboard', children: [ { path: 'dashboard', name: 'Dashboard', component: () => import('@/views/dashboard/index'),//懒加载 meta: { title: '首页', icon: 'dashboard' },//元数据 }, ], },
以上就是显示的页面.
这也就是说,如果你只写routerView,没有routerLink的话,就会默认显示path:’/'的组件.
事实上,你可以在输入框中试试:
baidu.com
和baidu.com/
的效果是一样的.
axios
在vue中,利用axios来接收和发送给后端的数据.
当时从官网上看了好几天,终于想通了很多事情.
将axios的实例单独创建在一个文件中.
const service = axios.create({
//
baseURL: 'http://localhost:8080/',
timeout: 5000 // request timeout
})
export default service
以后访问的时候就不是直接使用axios而是使用service实例来访问.
这也就是说:以后就不必每次都写入完整的url了(以后只需要写相对的路径),也将每次访问的超时时间改成了5000. 如下:
request({ url: '/admin/core/integralGrade/list', method: 'get', })
拦截器
请求拦截器
service.interceptors.request.use( config => { if (store.getters.token) { config.headers['X-Token'] = getToken() } return config }, error => { console.log(error) return Promise.reject(error) } )
在请求的时候加上令牌:‘token’. 这样就不必每次请求时都添加上token了.
注意:请求拦截器和响应拦截器都是promise实例,甚至于axios返回的就是promise实例.
响应拦截器
这里不看响应的具体内容,只看基本构架.
后端一般返回给的数据如下:
{ code:200, message:'数据请求成功', data:{ items:[...] } }
那么为了使得用户体验好一点,你可以在接收到数据的时候做出一些其他的内容丰富用户的体验. 比如说你根据code来判断你是否请求成功.如果请求失败,就弹出类似于以下的错误.而这就是上面图片的Message内容.
前后端文件及代码规范
另外,为了前端和后端日后修改明确,在前端和后端的文件夹相对一致.(为了前端和后端代码的共同修改)
以下为integral-grade.js的文件.(前端vue文件.)
以下为AdminIntegralGradeController的部分代码(利用springboot实现):
其实你完全可以发现,前端和后端的接口一一对应.
前端统一了接口,日后方便修改.倘若后端某个接口写错,前端和后端可以一同修改.
但在前端应该如何应用这些接口呢?
如下(不必看太过详细,因为有好多 其他的组件及函数)
import intergralGradeApi from '@/api/core/integral-grade' export default { ........ methods:{ saveData() { // 在这里直接引用了接口. intergralGradeApi intergralGradeApi.save(this.integralGrade).then((res) => { this.$message.success(res.message) // 路由跳转.路由跳转. this.$router.push('/core/integral-grade/list') }) } }
有没有觉得和后端的mvc有点相似.
前端的接口文件(integral-grade.js)从后端获取数据就像是后端从数据库中获取数据------------mapper,
引入到某个vue文件并使用其中的函数从后端获取数据并添加一些额外的功能就像是后端将mapper引入并利用mapper做一些丰富的功能----------service
而将从后端获取的数据经过引用布局到页面上就像controller在引用service------controller
所以前端也有一定的框架逻辑
后端数据存放位置
从服务器中得到的数据应该放置在哪里?
我个人只知道两种.
第一种:
直接在使用这些数据的组件中利用axios实例来进行布局.
第二种:
在store/index中(或分模块的模块中)中书写得到数据的逻辑.,然后将store中的数据引入到该组件中.
如下
<script> export default{ created(){ this.$store.dispatch("getCategoryList")//获取后台数据的逻辑在store/index.js中 } data(){ return { } } computed:{ ...mapState(['categoryList']) } } </script>
对于这两种其实各有利弊:
第一种每切换到该组件或者创建了该组件后都会从后台获取数据.那么如果后台数据频繁更改第一种适用.
如果某些内容久久不更改,就不必每次都去请求服务器,直接获取store中存储的数据即可.这适用于第二种.
设置属性为undefined
当发送ajax时,如下:
ajax(url,{ method:'get' params:{ a:'', b:'', c:'', d:'' } })
你会发现a,b,c,d的属性值都是空字符串.
事实上,即便是空字符串,也会将数据传送到服务器中.
为了减缓服务器的压力,让这些含有空字符串的内容不被传送到服务器.
可以将其设置为undefined.这样ajax就不会将内容传送过去.
容易出现的错误
当项目较大时,在mounted(在网页数据部署完成后执行的方法)中编写的不只一个功能.这个时候如果包含axios请求就很容易出现一些问题.
如下:
mounted(){ axios.get("/getList"); let swiper=new Swiper({......}) }
问题所在就是axios是异步的.而其余的功能是同步的.
这就说明,倘若后面的内容需要axios获取的数据,但是由于axios获取数据有一定的延时,后面的逻辑就会因为没有数据而出现渲染页面的错误.
当然因为axio是异步的,你也可以设置setTimeout来进行异步处理.但是对页面渲染产生了一定的延迟.
这个时候使用$nextTick(function(){
})
nextTick可以在页面渲染完毕之后在执行,这个时候就不会出错.
千万要记住,倘若在mounted中不只向后台获取数据一个功能,很大可能会出现此错误
常识
“serve”: “vue-cli-service serve --open --port=8888”,
–open 是说你利用npm run serve 或者(npm run dev)启动程序,会自动帮你打开网页,–port=8888 是说此项目会在8888端口下打开.(如果8888端口被占用,会跳转到8889,以此类推)
上面的代码是从package.json中截取出来的.
下面的图片是从官网上查到的.
在不同组件中的某个组件的不同内容显示
就是说,一个常用的组件你需要在其他各种不同的组件中使用.
如下:
(该组件为home/index.vue)
<template> <LY/> ...... </template>
该组件为search/index.vue
<template> ...... <LY/> ...... </template>
常常某个组件在不同组件下有不同的显示.那么如何来设置这个组件的内容呢?.
组件中内容固定
倘若组件中固定是这些内容,只是决定某些内容在不同的组件中是否显示.
那么有3种方法(只想到三种,方法可能不限)
props
如下:
(组件位置Search/index.vue)
<template>
<div v-if="isShow"></div>
</template>
<script>
export default {
props:['isShow']
}
</script>
(组件位置Home/index.vue)
<template>
<Search :isShow="true"/>//或<Search :isShow="false"/>
</template>
这样就可以很轻易的设置对内容是否显示.
$route.path和meta
假设有这样一种逻辑,某个组件中的内容是否显示已经在每个路由组件中确定下来,
那么这样就有了两种方法.
在本组件中直接根据路由的path来判断是否显示.
<template>
<div v-show="isShow"></div>
</template>
<script>
export default {
data(){
return {
isShow:true,
}
}
mounted(){
//如果觉得这样利用一一比较比较麻烦,可以将路由(/home,/search)设置成一个数组,然后利用数组中的any,some方法来进行比较.
if(this.$route.path=='/home'||this.$route.path=='/Search'){
isShow=true
}
else {
....
}
}
}
</script>
利用路由中的meta来进行显示.
(位置:router/index.js)
{
path: "/login",
name: "login",
meta:{
isShow:true
}
component: () => import( "@/pages/Login"),
},
{
path: "/home",
name: "home",
meta:{
isShow:false
}
component: () => import( "@/pages/home"),
},
其实,你可以自我评测一下,isShow的false值用的多还是true值用的多.
如果true用的多的话,那么就不必填写其他路由的false了.最后只需要使用:
if($route.meta.isShow){
…
}
else {
}
组件中内容不固定
倘若组件中内容不固定那么只有一种方法(slot)
就是说在不同的组件中填充不同的内容.
这里就直接将官网的链接放在这里
组件使用优化
一. 重复请求固定数据
如果确定某些从后台得到的内容是固定的,且该组件常用.这也就意味着该组件的多个实例会经常向服务器请求相同的数据.
为了优化可以将该数据的请求放置到vuex中.(location:store/index.js)
同样的,如果想要只请求一次,可以如何做?
- keep-alive
- 利用App.vue(这是根组件)
记住:App.vue在程序启动后只会运行一次.至于路由跳转就不会引起app.vue销毁和创建了.
如下:
<template>
....
</template>
<script>
export default {
mounted:{
this.$store.dispatch("getCategoryList");// 只要app.vue启动,就可以请求到服务器的数据,并将其存储到vuex中.便于以后的访问. 但是千万要注意:如果服务器中的数据变了,就需要重新获取数据了.(对于后端的话,可以应用redis作缓存来做类似的事情)
}
}
</script>
搜索栏
![image-20211204224248129](https://gitee.com/luoyangchuchu/image1/raw/master/img/image-20211204224248129.png)
// 点击搜索栏后,会跳转到"/search"页面.
如上,如果点击搜索栏后,输入的内容会被服务器处理后显示在<router-view/>页面上.(注意:home组件是共享的.)
那么就出现了一个问题,如果当前的路径是"/“的话,搜索没有问题,可以直接显示在桌面上.但是如果当前路径是”/info?categoryName=123",就出现了问题.点击搜索栏之后路径就直接变成了"/search",之前的"/info?categoryName=123"就没有了.(因为本身搜索栏的路由就是"/search",就是说从根路径开始的,那么之前的各种信息自然就没了.)
如果你想存留下之前的params和query参数,那么就需要重新配置一些$route的内容了.
如下:
let location; if(!!this.$route.query){ location.query=this.$route.query } if(!!this.$route.params){ location.params=this.$route.params } this.$router.push(location);
这样就完全可以实现你想要的效果了!