vue爬坑成长汇总

目录

1.阻止事件点击
例子:

1
2
3
4
<Col span="22" @click.native="getHouseInfo(houseItem)">
{{houseItem}}
<img :src="getSrc(houseItem)" @click.stop="" alt="">
</Col>

这里的click.stop即可阻止事件执行。

2.v-bind:src时图片加载问题

1
<img :src="getSrc(houseItem)" @click.stop="" alt="">

js

1
2
3
4
5
6
7
8
9
getSrc (houseItem) {
if (houseItem.selfUse === true) {
return require('@/images/officeSource/ziyong.png')
} else if (houseItem.isHired === true) {
return require('@/images/officeSource/yizu.png')
} else {
return require('@/images/officeSource/weiyong.png')
}
}

      一开始百思不得其解为什么:src动态加载不了图片呢,关键还没有报错,后来在知乎看到了答案,需要加require。这个跟webpack有关,需要详细了解下。网上有人解释道:图片的动态引入需要当做一个模块来进行打包 不然webpack会默认为字符串的。

3.props传值问题
直接看示例:
https://github.com/lizhongzhen11/myStudy/blob/master/vue/props.vue
https://github.com/lizhongzhen11/myStudy/blob/master/vue/components/childProps.vue

4. .native原生事件
https://segmentfault.com/q/1010000007896386/a-1020000007896602

5. vue-router 懒加载
回到目录

      最近开发后台管理系统项目,环境是苍老师搭的。抽空看了下苍老师搭的环境配置,发现他写的路由和通过vue-cli构建后默认的路由不一样!
      首先,他在router文件夹下有两个js,一个是默认的index.js,还有一个是他定义的router.js,以前没大注意,在他的环境中路由都是写在router.js里面的,但是一个经过vue-cli构建的项目,默认路由是下面这种写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
import Vue from 'vue'
import Router from 'vue-router'
import Hello from '@/views/hello'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'hello',
component: Hello
}
]
})

      但是,苍老师搭建的环境不是这样写的。苍老师环境中的index.js

1
2
3
4
5
6
7
8
9
import Vue from 'vue'
import Router from 'vue-router'
import routes from './router'
Vue.use(Router)
const RouterConfig = {
routes
}
const router = new Router(RouterConfig)
export default router

router.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
export const hello = {
path: '/hello',
name: 'hello',
// meta 字段就是路由元信息字段 https://router.vuejs.org/zh-cn/advanced/meta.html
meta: {
title: '你好'
},
// 路由懒加载,看 https://router.vuejs.org/zh-cn/advanced/lazy-loading.html
component: () => import('@/components/HelloWorld.vue')
}
export const routes = [
hello
]
export default routes

      他主要用到了路由懒加载,看https://router.vuejs.org/zh-cn/advanced/lazy-loading.html 。为什么采用这种方式呢?

      当采用默认的路由方式,那么组件会先全部在index.js头部import进来才会去加载,页面少就算了,如果页面多,那么加载速度明显会变慢;而采用懒加载方式,当跳转到某页面时才会去加载它对应的组件,这样会大大提高加载速度。

6. vue项目里使用阿里的 iconfont
回到目录

      在开发过程中需要用到iconfont,由于在上一个项目中用到过,所以有点印象。但是!!!那个项目的环境全是苍老师配置的,这个项目我想自己配置锻炼下,结果就悲催了。
      一开始,进展很顺利,看 https://www.cnblogs.com/chinabin1993/p/8184296.html 即可。可惜,当走到最后一步import ./iconfont/iconfont.css时,报错了,说找不到./iconfont/iconfont.svg?t=1524893607035 in...???那个链接上也有展示,不过他说把css-loader装一下就好了。不过我是用的vue-cli搭建的,这个css-loader貌似一开始就装好了。这样我就很郁闷了!!!
      我百度了起码2小时,愣是找不到原因,后来索性重新把下载下来的iconfont文件全部再拷贝进去,成功了!!!卧槽他大爷!!!为什么?因为我一开始是一个文件一个文件的拷贝进去的,我当时想某些文件可能没用,但是拷贝途中有可能遗漏了某个文件,操蛋啊,自作孽啊。。。

7. vue 父组件调用子组件方法并接收参数 $emit
回到目录
直接看示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// sealapply.vue
import shamData from '@/views/mis/shamData'
import SearchRow from '.././components/searchRow'
export default {
name: 'sealapply',
components: {
SearchRow
},
data () {
return {
sealApplyDetailBtn: [],
name: 'sealapplylist',
saveName: ''
}
},
mounted () {
this.sealApplyDetailBtn = shamData.sealApplyDetailBtn
},
methods: {
go () {
console.log(110)
this.saveName = this.name
this.name = 'sealapplylistDetail'
},
back (name) {
console.log(name)
this.name = name
}
}
}

父组件html

1
2
<!--sealapply.vue-->
<SearchRow :officeSourceBtn="sealApplyDetailBtn" :name="saveName" @back="back"></SearchRow>

子组件

1
2
3
4
5
6
7
8
9
10
11
export default {
name: 'searchRow',
props: ['searchRowInfo', 'officeSourceBtn', 'name'],
methods: {
back () {
console.log('back')
console.log(this.name)
let name = this.name
this.$emit('back', name)
}
}

子组件html

1
2
3
4
<Button type="primary" class="searchBtn" v-if="btn.type === 'back'" @click="back">
<i style="font-size: 14px;" class="iconfont icon-backicon1 ivu-icon"></i>
title
</Button>

8. vue watch 监听(父子组件在同一个页面里,数据变化时组件没有重新渲染情况下可用)
回到目录

      开发列表页和列表详情页时,由于老板要求这两个页面写在一个路由里,这样可以减少路由切换,所以采用了iview框架里的Tabs组件,该组件其实是利用css进行切换的,所以没有涉及到组件的重新挂载。
      当我用props进行父子组件传参时,发现当父组件数据改变后,子组件数据没有任何变化!我习惯性的在mounted()里面打印,但是如果组件没有重新挂载过,那么mounted()只在初始化渲染时有用!!!所以这时候在mounted()里面无论如何也无法得到父组件改变后传过来的数据了!!!

看例子,父组件:

1
2
3
4
5
6
7
8
9
<Tabs :value="pagePosition">
<TabPane label="list" name="list">
<whitespace :spaceheight="10"></whitespace>
<Table :loading="loading" ref="sealapplytable" border size="small" :columns="columns" @on-row-dblclick="rowDbclick" :data="data"></Table>
</TabPane>
<TabPane label="detail" name="detail">
<sealapplydetail @backList = "backList" :sealApplyData="row" :rowIndex="rowIndex"></sealapplydetail>
</TabPane>
</Tabs>

父组件js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import edittable from '../../../components/edittable';
import {whitespace} from '../../../template/listdetail'
import sealapplydetail from './sealapplydetail'
import shamData from '.././shamData'
export default {
name: 'sealapply',
components: {
edittable,
whitespace,
sealapplydetail
},
data () {
return {
columns: [],
data: [],
loading: false,
searchParams: {},
pagePosition: 'list',
row: {},
rowIndex: 0
};
},
mounted () {
this.columns = shamData.sealapplyColumn
this.data = shamData.sealapplyData;
},
methods: {
rowDbclick (row, index) {
console.dir(row);
console.log(index);
this.row = row
this.rowIndex = index
this.pagePosition = 'detail';
}
}

子组件js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import { whitespace } from '../../../template/listdetail';
export default {
name: 'sealapplydetail',
props: ['sealApplyData', 'rowIndex'],
components: {
whitespace
},
data () {
return {
form: {}
};
},
watch: {
sealApplyData: function (val) {
// console.log(val)
// 第一种写法
this.form = this._props.sealApplyData;
// 第二种写法
this.form = val
}
},
mounted () {
console.dir(this)
},
methods: {
backList () {
this.$emit('backList')
}
}
};

9. vue mixins
回到目录

      项目中有多个相似的页面及操作方法,所以可以使用mixins简化开发。但是在使用中没有仔细阅读文档,踩了个小坑。mixins有点类似java中的覆盖。如果组件中存在与mixins同名的属性或方法,那么会使用组件中定义的属性和方法覆盖mixins里面定义的。
https://cn.vuejs.org/v2/guide/mixins.html

10. iview form表单验证number类型
回到目录

https://segmentfault.com/q/1010000010333755?sort=created

1
2
{ required: true, message: '不能为空', trigger: 'blur' }
{ type: 'number', message: '请输入数字', transform(value) { return Number(value) } }

      在后期测试发现,如果将number类型的验证按照上述分成两步来写,那么刚进入页面时即使该输入框内有默认值0,还是会报红,后来发现将验证规则合在一起写不会有这个问题,但是,把默认值删了,直接传空的话就会有新问题了,因为Number('') === 0,可以改用parseFloat(),如下:

1
2
3
4
5
6
7
8
9
{
type: 'number',
required: true,
message: '请输入数字',
trigger: 'blur',
transform (value) {
return parseFloat(value);
}
}

11. iview menu菜单栏改写时遇到的问题
回到目录

      其实iview的菜单栏挺不错的,但是老板要求当屏幕缩放时,所有菜单都能显示,除非浏览器高度小于所有菜单高度,否则的话必须全部显示!语言表述可能不大准确,上图:

      就是要做成如上图的样子。可以看到,当浏览器缩放到一定程度时,政务办公下面的子菜单只显露了一个通讯录,其实这样是不大美观的,奈何老板需求这样,那么只能做了。
      一开始还好,给子菜单里加了overflow-y: auto;,然后在 https://www.lyblog.net/detail/314.html 找到了修改进度条样式的方法(有兼容性),接下来利用浏览器显示高度 - 父菜单总高度(这个是固定不变的) = 子菜单所在块高度,然后找到子菜单所在块改style.height不是很完美吗?
      可惜,想的太好,iview封装过的Menu组件很好,但是我也无法直接去操作子菜单所在的块,只能通过ref以及name等通过数组下标的形式慢慢找到对应的ul,没错,是一个拥有.ivu-menuclass的ul,虽然写的不优雅,但起码取到了啊,这时候配合window.onresize()完美完成需求。心里暗自高兴,结果,很快发现bug了。
      页面刚加载以及点击切换时不起作用!!!我在mounted()里面调用了该方法,同时也在iview提供的监听菜单展开关闭的方法中调用了改变ul.ivu-menu高度的方法,但是,不起作用!!!
      纠结了很长时间,用过this.$nextTick()以及在其内部调用setTimeout都没作用,一气之下看iview的源码,在https://github.com/iview/iview/blob/2.0/src/components/menu/submenu.vue里:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<li :class="classes" @mouseenter="handleMouseenter" @mouseleave="handleMouseleave">
<div :class="[prefixCls + '-submenu-title']" ref="reference" @click.stop="handleClick" :style="titleStyle">
<slot name="title"></slot>
<Icon type="ios-arrow-down" :class="[prefixCls + '-submenu-title-icon']"></Icon>
</div>
<collapse-transition v-if="mode === 'vertical'">
<ul :class="[prefixCls]" v-show="opened"><slot></slot></ul>
</collapse-transition>
<transition name="slide-up" v-else>
<Drop
v-show="opened"
placement="bottom"
ref="drop"
:style="dropStyle"><ul :class="[prefixCls + '-drop-list']"><slot></slot></ul>
</Drop>
</transition>
</li>

      如上所示,第一个li代表每个父菜单,父菜单内部包含一个div与一个ul标签(),div用来放父菜单标题,关键在于ul标签,iview内提供的MenuItem就是插在这个ul标签内的。现在,我想改的就是ul的样式,不过很抱歉,它没给我提供api。。。
      在仔细观察源码后依然无法解决,遂问了苍老师,苍老师说加个setTimeout看看,我心想我用过啊,结果直接在mounted()里加果然有效果,不需要在this.$nextTick()里面加,但是由于我写得渣,采取的是直接style.height形式,一上来连过渡效果都没有,很糙,本想着加个过渡效果,发现依然没用。
最后,只好把MenuItem组件注释掉,自己用ul li重写,当然写在MenuItem位置,这样iview里面的Menu以及Submenu组件还有name属性等就可以直接用了。
我改写的,

1
2
3
4
5
6
7
<ul class="transitionHeight" :style="changeHeight">
<li class="menuitem" ref="menuitem" :name="index+'-'+nextindex" v-for="(nextitem,nextindex) in menu[index].child"
:key="nextindex" v-on:click="selectmenu(nextitem.url, index, nextindex)"
:class="activename === index + '-' + nextindex ? 'selectedMenu' : ''">
{{nextitem.name}}
</li>
</ul>

老板又又又改需求了!!!难怪程序员都看产品不爽
老板说,有的模块下只有几个菜单,就不要跟其他模块固定同样的高度了,子菜单多的还按以前来。不知你们听懂了没,我截个他需要的图(忙了至少两个半小时才搞定)

如上图,第一个模块下只有两个菜单,比如在我的电脑上,算出来的固定高度是408px,第一个模块两个菜单肯定用不了这么大的高度,所以老板只想显示这两个菜单高度就好了,而第二张图代表第二个模块下的子菜单,其实有15个子菜单,但因为超过408的固定高度了,所以这里只显示刚好达到408px的子菜单,其他的通过滚动条可以查看。

好了,需求讲明白了,无非就是在原来基础上计算价格判断,当子菜单高度之和 < 固定高度的话,该模块展开后的高度应该 === 子菜单高度之和,其实不难,我很快实现了:

1
2
3
4
5
6
let childLen = this.menu[this.openname]['child'].length;
let num = diff / 41;
if (num >= childLen) {
this.diff = 41 * childLen;
return;
}

但是,结束了吗???如果这么容易就好了,真的fuck啊!
测试时发现,当我切换时,其他菜单展开时不够平滑,对用户不友好,更关键的是,默认展开第一个模块吧,然后我打开最后一个模块(第一个模块自动关上),重新切换到第一个模块时,第一个模块高度竟然和最后一个模块一样高!!!我蒙了???可是当我关闭第一个菜单后再度打开就好了???are you kidding me???
我不断的console去打印查看,发现,当我代码里对this.diff进行赋值时,影响到了iview里面Menu组件了,该组件提供了@on-open-change方法,默认接受被打开的菜单编号,当我改变this.diff时,@on-open-change方法中的name值不对了!!!
正常情况,打开菜单,@on-open-change="openchange"对应的openchange(name)中的name应该是打开菜单的编号数组啊,可是,我这里竟然完全相反,what a fuck !!!是的,我打开一个菜单,控制台打印

1
[__observer__]

???这TM不应是关闭菜单才会打印这个吗,我明明是打开菜单啊!!!我可以贴上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// computed
changeHeight () {
return 'height: ' + this.diff + 'px';
}
// methods
openchange (name) {
// 控制子菜单高度
let me = this;
if (name.length > 0) {
if (me.openname !== name[0]) {
me.oldopenname = me.openname;
me.openname = name[0];
window.onresize = () => {
me.controlHeight(me.openname, me.oldopenname);
};
}
}
},
controlHeight (openname, oldopenname) {
if (openname >= 0) {
let me = this;
let len = me.menu.length; // 菜单数
let windowHeight = document.documentElement.clientHeight || window.innerHeight;
let submenuHeight = (len + 1) * 49; // 49 === 每个父菜单的高度 + 上下padding
let diff = windowHeight - submenuHeight;
oldopenname = oldopenname || me.oldopenname;
// console.log(diff)
if (diff > 41) {
this.diff = diff;
} else {
this.diff = 0;
}
}
}

经过我不断尝试,确定问题就出在我给this.diff重新赋值这里,由于我这里采用的是computed计算属性来改变高度,但是现在看来,它变化有点慢,这应该是异步的原因,但是得解决啊,后来我想了最糙的办法,直接加ref,放弃计算属性,然后改写controlHeight

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
controlHeight () {
if (this.openname >= 0) {
let childLen = this.menu[this.openname]['child'].length;
let windowHeight = document.documentElement.clientHeight || window.innerHeight;
let submenuHeight = (this.len + 1) * 49; // 49 === 每个父菜单的高度 + 上下padding
let diff = windowHeight - submenuHeight;
let num = diff / 41;
if (diff <= 41) {
this.$refs['menuul'][this.openname].style.height = `0px`
return;
}
if (num >= childLen) {
this.$refs['menuul'][this.openname].style.height = `${41 * childLen}px`
return;
}
this.$refs['menuul'][this.openname].style.height = `${diff}px`
}
}

虽然解决了这个问题,但是我得真的理解,不然没有进步。所以我去看源码了:https://github.com/iview/iview/blob/2.0/src/components/menu/menu.vue#L119

看了源码发现,在updateOpenKeys内部多次用到了findComponentsDownward方法,而该方法又用到了reduce方法并且会递归调用自身,这样无疑是很耗时的,而我切换菜单又很快,这就造成了name值没有及时改变。个人猜想啊~

12. vue 附件上传
回到目录
自己的: https://github.com/lizhongzhen11/myStudy/blob/master/vue/upload.vue
iview的: https://github.com/iview/iview/blob/2.0/src/components/upload/upload.vue

13. 自己写的toast组件
回到目录
github: https://github.com/lizhongzhen11/myStudy/tree/master/vue/components/Toast

      vue虽然用了一段时间了,但是还没有写过真正意义上通用的组件,之前写得大多是半吊子刚好业务能用罢了。但是在九龙湖项目中发现苍老师对所有调接口报错进行了处理,当调接口报错时他会返回给前台一个空对象,然后利用toast弹出来,不会出现以前那种整个页面都是后台报错信息的情况,美观了不少,于是,我就好奇的去看看他怎么实现的。
      拦截很简单,利用了axiosinterceptors属性,但是,却让我歪打正着看到了他自己写的拦截报错后弹框显示报错信息的toast组件,由于和iview的有点相似,一开始我都没在意过,这次看到了大吃一惊,居然是自己写的,看了他的代码,我居然不会这样写。
      这样肯定不行的,所以我自己也尝试去写了下,当然不懂得还是看他的代码了。主要是const ToastConstructor = vue.extend(Toast)这一步创建构造函数,我竟然不知道!!!其实 https://cn.vuejs.org/v2/api/#Vue-extend 官方示例很明显的提出了创建构造器,我竟然没仔细看,以前都是当java的extend来用的。。。
      还有document.body.appendChild(newToast.$el)这一步需要将刚刚实例化的newToast添加到body上,不然怎么刷新页面也看不到的!!!

14. vue 防止跳转到不存在的页面
回到目录

      在做自己搭建的项目中偶然发现,浏览器中输入不存在的路由也可以跳转,只不过页面是空的,立即意识到这是个bug需要解决,好在网上已经有了答案,这个答案在刚接触vue时我还看过,只是没能实践过,很简单的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 页面跳转拦截,防止跳转到不存在的页面
router.beforeEach((to, from, next) => {
console.log(to)
console.log(from)
console.log(next)
// 如果未匹配到路由
if (to.matched.length === 0) {
// 如果上级也未匹配到路由则跳转登录页面,如果上级能匹配到则转上级路由
from.name ? next({ name: from.name }) : next('/')
} else {
iView.LoadingBar.start()
next() // 如果匹配到正确跳转
}
})

      这时,我突然想到九龙湖项目里也有用到过,但是我没在意过,现在回去头去看,果然用到了,还顺带加上了iView.LoadingBar.start()这段代码,这个一看就知道是iview的组件方法,其实就是页面加载进度条,官方也给出了示例。不过九龙湖里还打算在页面跳转中做权限验证,但是目前还没开发完毕。

15. iview form表单时间验证,类型不同会报错
回到目录

      开发九龙湖项目时,发现后台一开始传过来的是new Date()类型,而页面上需要展示yyyy-MM-dd HH:mm:ss,所以所性后台直接做了转换,传给前台需要的格式。但是利用iviewform表单验证发现了问题,第一次修改保存是ok的,但是第二次修改同一条数据,发现控制台报value.getTime is not a function,这就很奇怪了,后来不断console.log()发现第一次输出是new Date()类型,但是第二次输出是yyyy-MM-dd HH:mm:ss的字符串类型,两次类型不一致了,解决方法就是保持类型一致。
可以看:https://segmentfault.com/q/1010000012129675
以及:https://github.com/ElemeFE/element/issues/8020

16. vue $el一次小踩坑
回到目录

      一开始在iview Form组件内部插入了一个Card组件,并设置了ref属性,然后通过点击事件打印this.$refs.workflow.$el.className有输出,但是后来因为需求变更,将Card组件移出到与Form组件同一级别后,发现原先的方法报错,console后发现this.$refs.workflow.$el报错,没有$el属性,然后直接打印this.$refs.workflow.className就能得到值了。
      简直amazing!明明ref属性是加在Card组件上的,为何改变Card组件位置取值方法就要改变呢?

17. vue 升级后 nextTick 源码变化
回到目录
https://juejin.im/post/5a1af88f5188254a701ec230

18. 浅析Vue 中的patch和diff(上)
回到目录
https://github.com/kaola-fed/blog/issues/259
https://github.com/kaola-fed/blog/issues/263

      接触vue有一段时间了,以前一开始认为只要数据改变vue就会重新渲染页面,但是后来听同事说数据改变,vue并不会立即重新渲染,想想也是,数据一变就立即重新渲染,那么性能注定是极差的!但是我一直不大懂vue是怎么进行重新渲染的,直到最近接触了macrotask,microtask,event loop以及nextTick源码解析等知识,才大致了解。

      原来数据都会放在watcher里面监听,当数据变化时,更新watcher数组里面的数据,然后统一放到nextTick去更新。nextTick内默认使用microtask,但是在某些情况下会强制使用macrotask

19. Vue2.0 v-for 中 :key 到底有什么用?
回到目录
https://www.zhihu.com/question/61064119

20. iview Modal组件中使用Form组件,验证不通过但是却会关闭Modal
回到目录

      开发时发现,在Modal组件中使用Form组件时,给Form组件内加上验证,点击确认按钮时虽然验证生效了但是Modal依然关闭了,这样使用效果并不好,应该是验证不通过时Modal不会关闭让用户继续填写才对,填写完应该可以再度点击确认按钮才对。
      后来在iview官方发现Modal组件提供了loading属性,当设置为true时,点击确认按钮不会关闭Modal,但是,之后也不能点击确认按钮了。这样依然不行,没有达到我的期望。
      之后在 https://github.com/iview/iview/issues/3079 中也看到相似问题,那位朋友采用了setTimeout来解决,我也尝试了一下,可以解决。但是当快速点击确认按钮时依然会关闭Modal,这样依然不完美。
      不过,想到最近有看过macrotask等相关知识,加之vue源码中用到了MessageChannel,所以我也就尝试一下,果然解决了,但是有兼容性问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
onOk () {
let me = this
me.$refs['subform'].validate((valid) => {
if (valid) {
me.entlotModel = false
me.detailData.push(me.subform)
} else {
me.modalLoading = false
me.$Message.error('请检查填写内容')
// iview modal组件用于表单验证时有点bug,点击确定按钮即使不通过验证也会关闭modal
// 这种有点反人类,一开始用setTimeout来解决,但是当快速点击时依然会关闭modal
// 遂想到了用macrotask来解决,现在如果不通过验证即使快速点击确定也不会关闭modal
const channel = new MessageChannel()
const port = channel.port2
channel.port1.onmessage = () => {
me.modalLoading = true
}
port.postMessage(1)
}
})
},

21. Tabpane布局切换发现包含时间的属性只在第一次渲染,之后切换Tabpane时间不变
回到目录

      首先说一下,这个问题是苍老师发现并解决的,我从来都不知道存在这个bug,一点都不细心啊!
      由于后台管理系统里有太多列表页,而列表页也有相对应的详情页,为了简化开发任务,同时减少页面重绘优化性能,所以采取了iviewTabs组件,列表详情页成为列表页的子组件,改变name值即可切换,同时具有很高的通用性。这样做确实带来了很多方便,但同时也有一些小问题。
      比如这次苍老师发现的,第一次进入详情页,时间没什么问题,回到列表页后在该页面驻足一段时间后,发现计算机时间已经过了好几分钟,但是列表详情页的时间居然是第一次进入详情页的时间,也就是说再度进入页面时间没有更新。
      其实仔细想想也能理解,因为此时列表详情页是列表页的子组件了,子组件先于父组件渲染的,它们在同一个路由,当切换到列表详情页后并不会再度渲染,所以时间当然不变化了。
      怪自己不细心,写完一个页面看数据都有了能对接了就认为ok了,没有关注数据是否合适的问题。
      不过苍老师解决的方法也很巧妙,利用了函数式编程的思想,即将拥有时间属性的属性写成函数,然后每次切换Tabpane时调用函数就相当于执行一次new Date(),真的巧妙。因为如果我来解决,我可能会通过遍历找拥有时间的属性然后重新赋值,那样太麻烦了。
附上例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
dataInit: function () {
return {
trandate: new Date(),
}
}
// methods:
initForm () {
let me = this;
// TODO 目前通过深拷贝解决引用数据类型导致的问题 引用了 lodash
if (typeof me.dataInit === 'function') {
me.form = _.clone(me.dataInit());
} else {
me.form = _.clone(me.dataInit);
}
},

22. keep-alive 缓存页面防止重绘
回到目录

https://cn.vuejs.org/v2/api/#keep-alive
      开发后台管理系统时经常遇到列表和列表详情页,有一个需求是这样的:当在 A页面对应的详情页时点击菜单跳转至B页面,然后在点击菜单回到A页面对应的详情页,而不是回到A页面的列表页,以减少用户操作。
      后来苍老师做好了,我抽了时间翻遍了代码愣是没看到他在哪里写得,遂侥幸百度,得知<keep-alive></keep-alive>内置组件就是用来缓存阻止浏览器重绘的。老实说,我第一次看Vue官方文档时看过,但由于以前没用过,所以没想起来。。。
用法很简单,在路由跳转加上该组件即可:

1
2
3
<keep-alive>
<router-view/>
</keep-alive>

23. vue data里面的对象添加子属性的问题
回到目录

      其实看过官方文档并且写代码有意识的人基本上不会觉得这是什么大问题,但是,如果是去看他人写得代码,并且略微有点复杂的情况下,接手者很有可能不能立即找到问题所在。。。
      最近在做的项目中有用到附件上传,同事做了一个组件,其实项目上大部分组件都是他封装的,挺厉害的。因为不少页面用到该功能,所以我就按照他在其他页面上引用的方式一样引入并使用,但是,当我上传成功后发现附件table面板里没有刚刚上传的文件信息,反观他的示例页面却有,很奇怪。想直接问他的,但是考虑到自己成长以及没有看过他这个组件代码问题,想学习一下顺便自己发掘问题所在,遂自己去查看他的组件代码。
      组件不复杂,3个.vue文件以及一个config.js,其中一个是基础模板,另两个继承自它且侧重点不同,分为:attachuploadlist.vueimageuploadlist.vue,看名字就明白了。配置文件里放的是table的表头,且用到了render函数。该组件依赖于iview,不会写table里面的render函数的同学可以去iview的官方文档上找。
      接下来我将这些文件内的代码过了一遍,大致了解过程了,然后一处处去写console调试,最终发现在上传文件成功后关闭Modal框时出现了问题(没错,用的iviewModal),config.js内的表头column中定义的render方法没有调用,这我就很奇怪了。。。他也没在示例页面上做什么操作啊,怎么他能调用我不行呢?
      思考许久后,我还是决定去问同事了,后来同事说你看过updateForm()方法吗?我发誓看过,不就是简单的赋值吗。如下:

1
2
3
4
updateForm (listDate, detailKey) {
// 如果页面存在子表信息通过此方法更新页面当中的子表信息
this.form[detailKey] = listDate
},

      后来同事继续点醒我,this.formdata里面的对象,不是普通js对象,是隶属于vue的对象,而vue官方文档说过,data中的对象因为需要被监听的缘故,所以不能直接赋值,而需要通过$set()方法去设置新属性,但同事这里为了方便,所以只需要将被赋值的属性预先写在data里面就可以了,而我的问题就是没有在页面data内预先写下files属性,导致无法在form属性中直接添加files属性以及给它赋值,所以页面上才没有出现文件信息。。。
      有些知识点以为很简单,但是稍微一用就可能让人迷失错愕,经验不足!

24. vue 登录页监听回车事件
回到目录
https://segmentfault.com/q/1010000011347642/a-1020000011348067

Newer Post

js中 setTimeout 等定时器执行顺序问题 + async

requestAnimationFrame介绍请看 http://www.w3cplus.com/javascript/requestAnimationFrame.htmlsetTimeout和setInterval介绍请看 http://www.w3cplus.com/javascript/Jav …

继续阅读
Older Post

管中窥豹:synchronized

一、学习自:http://www.cnblogs.com/xrq730/p/4851350.htmlhttp://www.cnblogs.com/xrq730/p/4851530.htmlhttp://www.cnblogs.com/xrq730/p/4853578.htmlhttp://www.c …

继续阅读