美文网首页填坑之路程序员
填坑之路:Vue中那些容易被忽略的

填坑之路:Vue中那些容易被忽略的

作者: 哦啦吧啦丶 | 来源:发表于2018-07-15 16:34 被阅读49次

    记录一些在使用Vue开发中常见的坑。

    声明本人菜鸟一只,一些描述可能会有偏颇,大佬轻锤,欢迎拍砖。

    小葵花课堂开课啦!

    想给第三方UI库Event加自定义参数时

    <el-date-picker v-model="editForm[item.name]" 
        placeholder="选择日期"
        :editable="false" style="width:100%"
        @change="(value) => changeHandler(value, item.name)">
    </el-date-picker>
    

    如上,使用箭头函数,return一个新的自定义函数,在自定义函数changeHandler中加入你想要的参数。

    同路由不同参数时

    如果由 app/12345 跳转到 app/23456,两个路由可能都是app/:id,用的同一个组件,所以vue并不会重新走一遍生命周期,导致一些可能的数据没更新的bug

    所以,一个比较好的解决方案是监控路由的变化来刷新一个必要的参数。

    watch: {
        '$route.params': function (newValue) {
          this.init()
        }
    }
    

    watch

    如上是我们常用的 watch,但可能你不知道watch 还有两个比较实用的配置:deepimmediate

    deep

    deep,默认值是 false。顾名思义即表示深入观察,watch会将obj一层层往下遍历,给对象的所有属性都添加了监听。可想而知,这一做法的性能耗费比较大。

    watch: {
        obj: {
            handler (newVal, oldVal) {
                console.log('obj的属性变化了')
            },
            deep: true
        },
        
    }
    // 建议使用
    watch: {
        'obj.key': {
            handler (newVal, oldVal) {
                console.log('obj的属性key变化了')
            }
        },
        
    }
    
    immediate

    immediate,默认值为 false。顾名思义即表示立即监听,在定义了 handler 方法后将立即执行一次。

    watch: {
     firstName: {
      handler (newval, oldVal) {
       this.fullName = `${newval} ${this.lastName}`
      },
      immediate: true
     }
    }
    

    定时器

    很多时候我们可能会有写定时器的需求(比如有个轮询任务),当我们离开这个页面时,你会发现定时任务还在跑,性能炸了。

    于是我们可以这么干:

    data() {
        return {
            timer: null
        }
    }
    methods: {
        timing() {
            this.timer = setInterval(() => {
                // dosomething
            }, 100)
        }
    }
    beforeDestroy() {
        clearInterval(this.timer) // 在组件即将销毁时清掉定时任务
        this.timer = null // 变量释放
    }
    

    关键就在当我们离开这个页面(组件销毁前)时清掉定时任务。

    路由懒加载

    Vue的首屏渲染慢饱受诟病。

    究其原因,在于webpack打出来的包太大,导致在进入首页的时候需要加载的文件大且多。

    那么我们就可以使用懒加载将页面切分(vue-router支持webpack内置的异步模块加载系统,使用较少的路由组件不再打包到bundles中,只在路由被访问到时按需加载),随用随载,减少首页加载的负担。

    常规非懒加载路由配置
    import Vue from 'vue'
    import Router from 'vue-router'
    import Login from '@/components/Login'
    import Home from '@/components/Home'
    import Profile from '@/components/Profile'
    
    Vue.use(Router)
    
    export default new Router({
        routes: [{
            path: '/login',
            name: 'Login',
            compontent: Login
        }, {
            path: '/home',
            name: 'Home',
            compontent: Home
        }, {
            path: '/profile',
            name: 'Profile',
            compontent: Profile
        }]
    })
    
    
    懒加载路由配置

    以下列了常用的三种写法,webpack都支持。

    import Vue from 'vue'
    import Router from 'vue-router'
    
    Vue.use(Router)
    
    export default new Router({
        routes: [{
            path: '/login',
            name: 'Login',
            compontent: resolve => require(['@/component/Login'], resolve)
        }, {
            path: '/home',
            name: 'Home',
            compontent: () => import(/* webpackChunkName: "home" */  '@/component/Home')
        }, {
            path: '/profile',
            name: 'Profile',
            compontent: r => require.ensure([], () => r(require('@/component/Profile')), 'profile')
        }]
    })
    
    

    Computed

    先来看一段代码:

    <!DOCTYPE html>
    <html>
    
        <head>
            <meta charset="utf-8">
            <title>Computed</title>
    
        </head>
    
        <body>
            <div id="app">
                <label for="">FirstName:<input v-model="firstName"/></label><br>
                <label for="">LastName:<input v-model="lastName"/></label><br>
                <label for="">NickName<input v-model="nickName"/></label><br>
                <p>名称:{{name}}</p>
            </div>
            <script src="https://cdn.jsdelivr.net/npm/vue"></script>
            <script>
                new Vue({
                    el: '#app',
                    data: function() {
                        return {
                            firstName: '',
                            lastName: '',
                            nickName: ''
                        }
                    },
                    computed: {
                        name: function() {
                            console.log('触发computed了')
                            if (this.nickName) {
                                return this.nickName
                            } else {
                                return this.firstName + '.' + this.lastName
                            }
                        }
                    }
                })
            </script>
        </body>
    </html>
    

    天真时候的我们可能会以为每当nickNamefirstNamelastName三者任一变化都将重新触发name的变更。

    其实不然,当nickName有值(不为空)后,重新触发依赖收集,此后将移除对firstNamelastName的依赖;而当nickName再次为空值时,又将会收集到对firstNamelastName等的依赖。

    也就是说,依赖收集是动态的。

    re-render相关

    还是看一段代码:

    <!DOCTYPE html>
    <html>
    
        <head>
            <meta charset="utf-8">
            <title>Re-render</title>
    
        </head>
    
        <body>
            <div id="app">
                <div v-for="(item, index) in items" :key="index">
                    <div v-if="index === 0">
                        <input v-model="formData[item.key]">
                    </div>
                    <div v-else>{{item.key}}:{{Date.now()}}</div>
                </div>
            </div>
            <script src="https://cdn.jsdelivr.net/npm/vue"></script>
            <script>
                new Vue({
                    el: '#app',
                    data: function() {
                        return {
                            items: [{
                                key: 'input'
                            }, {
                                key: 'Re-render'
                            }],
                            formData: {
                                input: ''
                            }
                        }
                    }
                })
            </script>
        </body>
    </html>
    

    以上可能是一种常见的场景。我们会惊奇的发现,当input框中值发生变化,页面re-render了一个新的时间戳。不难看出问题出在Date.now(),这是一个方法(虽然没定义在实例的methods中),故而每当template重新渲染时会再次触发一次method,进而引发意料之外的事故。

    <div id="app">
        <div>
            <label>input:<input v-model="model"/></label>
        </div>
        <div>
            <child :items="makeItems()"></child>
        </div>
    </div>
    

    以上可能更能说明问题,一些场景下我们可能没深考虑或下意识地直接使用一个method做一些props传给子组件。那么当输入值model变化时触发父组件模板重新渲染,childitems是从method来的所以会被重新做一遍,产生不必要的麻烦。

    所以,一些场景下为了避免这种不必要的麻烦,还是推荐使用computed,因为计算属性是基于它们的依赖进行缓存的,而method在触发重新渲染时总会再次执行函数,不做缓存。

    ...

    未完待续。。。

    相关文章

      网友评论

        本文标题:填坑之路:Vue中那些容易被忽略的

        本文链接:https://www.haomeiwen.com/subject/havppftx.html