vue开发中常见问题

vue开发中常见问题及简单处理办法,model层数据变化,但视图未及时更新,路由切换时或路由参数变化时(即组件切换),页面(组件)不更新.在路由组件内部或子组件想刷新当前路由,而不刷新刷新整个页面.

vue关于img src动态赋值问题 或在vue中的script中引用图片时

只需要加上require即可

1
2
3
4
5
6
<!-- 场景1 -->
<img :src="require('../assets/images/'+imgsrc+'.png')"/>
<!-- 场景2 -->
<script>
this.photo = require('../assets/img/photo.png')
</script>

生产环境请将 productionSourceMap: false 即生产环境 关闭sourcemap 否则会在浏览器source里面看到所有源码

vue数据更新了但页面视图没更新问题

  1. 利用 Vue.$forceUpdate() 解决 vue数据更新了但页面视图没更新问题

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.2.2/vue.js"></script>
    <div id="app">
    <span>{{JSON.stringify(obj)}}</span>
    </div>
    <script>
    var vm = new Vue({
    el: '#app',
    data:{
    obj: { id: '001' }
    }
    })
    // vue无法检测实例被创建时不存在于data对象的属性即无法检测对象新增属性或移除属性
    vm.obj.msg = 'hello' // 不是响应式的 视图未反生变化 仍然展示 {"id":"001"}
    vm.$forceUpdate() // 加上强制更新视图 展示 {"id":"001","msg":"hello"}
    </script>
  2. 通过重新赋值解决vue数据更新了但页面视图没更新问题

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.2.2/vue.js"></script>
    <div id="app">
    <span>{{JSON.stringify(obj)}}</span>
    </div>
    <script>
    var vm = new Vue({
    el: '#app',
    data:{
    obj: { id: '001' }
    }
    })
    // vm.obj.msg = 'hello' // 不是响应式的 视图未反生变化 仍然展示 {"id":"001"}
    // 使用重新赋值解决 数据变化 视图未发生变化
    // 将属性添加到data对象上才能让 Vue.js 转换它,才能让它是响应的。
    // 方式1 直接构建新对象再重新赋值 推荐使用
    vm.obj = {
    ...vm.obj,
    msg: 'hello'
    }
    // 方式2 将对象合并到新对象上再重新赋值 会改变对象的属性顺序
    vm.obj = Object.assign({msg: 'hello'},vm.obj)
    // vm.obj = Object.assign(vm.obj, {msg: 'hello'}) // 将新对象合并到旧对象再赋值 无法实现
    </script>
  3. 使用 Vue.set()this.$set()
    Vue.set(对象, 属性, 值)this.$set(对象, 属性, 值)

  • 无法检测对象属性的添加或删除
    官方 - 由于 JavaScript(ES5) 的限制,Vue.js 不能检测到对象属性的添加或删除。因为 Vue.js 在初始化实例时将属性转为 getter/setter,所以属性必须在 data 对象上才能让 Vue.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
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.2.2/vue.js"></script>
    <div id="app">
    <span>{{JSON.stringify(obj)}}</span>
    </div>
    <script>
    var vm = new Vue({
    el: '#app',
    data:{
    obj: { id: '001' }
    }
    })
    // vm.obj.msg = 'hello' // 不是响应式的 视图未反生变化 仍然展示 {"id":"001"}
    // delete vm.obj.id // 不是响应式的 仍然展示 {"id":"001"}
    // 解决办法
    // 动态添加 - Vue.set
    // Vue.set(vm.obj, 'msg', 'hello') // 响应式的 视图展示 {"id":"001","msg":"hello"}
    // // 动态添加 - vm.$set
    // vm.$set(vm.obj, 'msg', 'hello') // 响应式的 视图展示 {"id":"001","msg":"hello"}
    // // 动态添加多个
    // vm.obj = Object.assign({}, vm.obj, { 'msg': 'hello' }) // 响应式的 视图展示 {"id":"001","msg":"hello"}
    // // 动态移除 - Vue.delete
    // Vue.delete(vm.obj, 'id') // 响应式的 视图展示 {}
    // // 动态移除 - vm.$delete
    vm.$delete(vm.obj, 'id') // 响应式的 视图展示 {}
    </script>
  • Vue 不能检测通过数组索引直接修改一个数组项也不能检测到数组长度的变化
    官方 - 由于 JavaScript 的限制,Vue 不能检测数组和对象的变化;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <div id="app">
    <span v-for="item in items">{{item}}</span>
    </div>
    <script>
    var vm = new Vue({
    el: '#app',
    data: {
    items: ['a', 'b', 'c']
    }
    })
    vm.items[1] = 'x' // 不是响应性的 页面中items依然为 ['a', 'b', 'c']
    // 解决办法
    // Vue.set
    // Vue.set(vm.items, 1, 'x') // 页面中items同步响应修改为 ['a', 'x', 'c']
    // vm.$set
    // vm.$set(vm.items, 1, 'x') // 页面中items同步响应修改为 ['a', 'x', 'c']
    // Array.prototype.splice
    // vm.items.splice(1, 1, 'x') // 页面中items同步响应修改为 ['a', 'x', 'c']
    // // 数组长度的变化
    // vm.items.length = 2 // 不是响应性的 页面中items依然为 ['a', 'b', 'c']
    // 解决数组长度未变化
    // vm.items.splice(2) // 页面中items同步响应修改为 ['a', 'x']
    </script>

路由切换时或路由参数变化时(即组件切换),页面(组件)不更新(数据不更新)

路由视图组件引用了相同组件时,当路由参会变化时,会导致该组件无法更新

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
<!-- app.vue -->
<div id="app">
<ul>
<li><router-link to="/home/foo">To Foo</router-link></li>
<li><router-link to="/home/baz">To Baz</router-link></li>
<li><router-link to="/home/bar">To Bar</router-link></li>
</ul>
<router-view></router-view>
</div>
<!-- home.vue -->
<template>
<div class="home">
<div>{{msg}}</div>
</div>
</template>
<script>
export default {
name: 'Home',
data () {
return { msg: this.$route.params.name }
}
}
</script>
<!-- router/index.js -->
<!-- 路由构建选项 routes 中配置了一个动态路由 '/home/:name',它们共用一个路由组件 Home,这代表他们复用 RouterView
当进行路由切换时,页面只会渲染第一次路由匹配到的参数,之后再进行路由切换时,msg 是没有变化的。 -->
new VueRouter({
routes: [
{path: '/home/:name', component: Home }
]
})

解决办法

  1. 通过 watch 监听 $route 的变化。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <!-- 在home.vue中监听 $route 变化 -->
    <template>
    <div class="home">
    <div>{{msg}}</div>
    </div>
    </template>
    <script>
    export default {
    name: 'Home',
    data () {
    return { msg: this.$route.params.name }
    },
    watch: {
    $route: {
    handler: function (route) {
    this.msg = route.params.name
    },
    immediate: true
    }
    }
    }
    </script>
  2. 利用组件内守卫beforeRouteUpdate路由参数更新时触发 可以代替watch

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <template>
    <div class="home">
    <div>{{msg}}</div>
    </div>
    </template>
    <script>
    export default {
    name: 'Home',
    data () {
    return { msg: this.$route.params.name }
    },
    beforeRouteUpdate (to, from, next) {
    this.msg = to.params.name
    next()
    }
    }
    </script>
  3. 给<router-view :key="key"></router-view>增加一个不同:key值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <!-- 修改app.vue -->
    <template>
    <div id="app">
    <ul>
    <li><router-link to="/home/foo">To Foo</router-link></li>
    <li><router-link to="/home/Bar">To Bar</router-link></li>
    </ul>
    <router-view :key="key"></router-view>
    </div>
    </template>

    <script>
    export default {
    name: 'app',
    computed: {
    key () {
    return this.$route.path + Math.random()
    }
    }
    }
    </script>
  4. 给<router-view v-if="routerAlive"></router-view>增加一个不同v-if值,先摧毁<router-link>,然后再重新创建

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <template>
    <div class="app">
    <ul>
    <li><router-link to="/home/foo" @click.native="routerRefresh">To Foo</router-link></li>
    <li><router-link to="/home/Bar" @click.native="routerRefresh">To Bar</router-link></li>
    </ul>
    <router-view v-if="routerAlive"></router-view>
    </div>
    </template>
    <script>
    export default {
    data () {
    return { routerAlive: true }
    },
    methods: {
    routerRefresh () {
    this.routerAlive = false
    this.$nextTick(() => {
    this.routerAlive = true
    })
    }
    }
    }
    </script>

总结: 如果使用的是编程式导航路由,还是使用watch监听路由或者使用组件内路由钩子beforeRouteUpdate比较好

在路由组件内部或子组件刷新当前路由即刷新当前路由页面

在日常开发中在当前路由组件中或当前路由子组件执行删除或新增数据之后需要刷新当前路由页面更新数据,但又不想使用location.reload(), 则可以使用provide和inject配合router-view的v-if来解决刷新问题

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<!-- 根组件 app.vue -->
<template>
<div class="app">
<router-view v-if="routerAlive"></router-view>
</div>
</template>
<script>
export default {
data () {
return { routerAlive: true }
},
provide () { // 在父组件中创建属性
return {
routerRefresh: this.routerRefresh
}
},
methods: {
routerRefresh () {
this.routerAlive = false
this.$nextTick(() => {
this.routerAlive = true
})
}
}
}
</script>
<!-- Home.vue 当前路由页面 -->
<template>
<div class="home">
<Child :parentData="parentData" />
</div>
</template>
<script>
import Child from './child'
export default {
name: 'Home',
components: {
Child
},
data () {
return {
parentData: [1, 2]
}
}
}
</script>
<!-- child.vue -->
<template>
<div>
<div>{{parentData}}</div>
<button @click="delData">更改数据</button><br />
<button @click="linkToHome">跳转到当前路由,parentData为修改后[1, 2, 3]</button><br />
<button @click="linkToHomeFresh">刷新当前路由,使parentData回到初始值[1, 2]</button>
</div>
</template>
<script>
export default {
props: ['parentData'],
name: 'child',
inject: ['routerRefresh'], // 在子组件中注入在父组件中出创建的属性
methods: {
delData () {
this.parentData.push(3)
},
linkToHome () {
this.$router.push('/') // 路由跳转到首页但未刷新首页
},
linkToHomeFresh () {
this.routerRefresh() // 刷新首页
}
}
}
</script>
<style lang="scss" scoped>
button{ margin-top: 10px }
</style>

为computed传入参数

1
2
3
4
5
6
7
8
9
10
11
12
13
<div v-for="item in list">
<div v-if="isloan(item)">{{item.name}}</div>
</div>
<script>
computed: {
// 直接使用 isLoan(item) 会报错
isLoan () {
return function(item) {
return this.loadArr.includes(item.id)
}
}
}
</script>
-------------本文结束感谢您的阅读-------------
0%