10 Vue Development Tips

Routing parameter decoupling

Generally use route parameters inside components, most people will do this:

export default {
    methods: {
        getParamsId() {
            return this.$route.params.id
        }
    }
}

Use in a component makes it highly coupled with its corresponding route, so that the component can only be used on some specific URLs, limiting its flexibility. $route 

The correct way to do this is through decoupling props 

const router = new VueRouter({
    routes: [{
        path: '/user/:id',
        component: User,
        props: true
    }]
})

After setting the attribute of the route to, the parameters can be received in the component through props  true  props  params 

export default {
    props: ['id'],
    methods: {
        getParamsId() {
            return this.id
        }
    }
}

In addition, you can also use the function pattern to return props

const router = new VueRouter({
    routes: [{
        path: '/user/:id',
        component: User,
        props: (route) => ({
            id: route.query.id
        })
    }]
})

 

functional component

A functional component is stateless, it cannot be instantiated, it does not have any lifecycle and methods. Creating functional components is also as simple as adding a declaration to the template. It is generally suitable for components that only depend on changes in external data. Because of their light weight, rendering performance will be improved. functional 

Everything a component needs is passed through parameters. It is a context object, see documentation for specific properties. Here is an object containing all bound properties. context  props 

functional component

<template functional>
    <div class="list">
        <div class="item" v-for="item in props.list" :key="item.id" @click="props.itemClick(item)">
            <p>{{item.title}}</p>
            <p>{{item.content}}</p>
        </div>
    </div>
</template>

parent component use

<template>
    <div>
        <List :list="list" :itemClick="item => (currentItem = item)" />
    </div>
</template>
import List from '@/components/List.vue'
export default {
    components: {
        List
    },
    data() {
        return {
            list: [{
                title: 'title',
                content: 'content'
            }],
            currentItem: ''
        }
    }
}

Documentation: cn.vuejs.org/v2/guide/re…

style penetration

It is very common to modify the styles of third-party components in development, but due to the style isolation of attributes, it may be necessary to remove or create a new one . These practices will bring side effects (component style pollution, not elegant enough), and style penetration will only take effect when used in the CSS preprocessor. scoped  scoped  style 

We can use or work around this: >>>  /deep/ 

<style scoped>
外层 >>> .el-checkbox {
  display: block;
  font-size: 26px;

  .el-checkbox__label {
    font-size: 16px;
  }
}
</style>
<style scoped>
/deep/ .el-checkbox {
  display: block;
  font-size: 26px;

  .el-checkbox__label {
    font-size: 16px;
  }
}
</style>

Advanced use of watch

execute immediately

watch It is triggered when the listener property changes. Sometimes, we want to execute it immediately after the component is created. watch 

The possible way to think of it is to call it once in the life cycle, but this way of writing is not elegant, maybe we can use such a method create 

export default {
    data() {
        return {
            name: 'Joe'
        }
    },
    watch: {
        name: {
            handler: 'sayName',
            immediate: true
        }
    },
    methods: {
        sayName() {
            console.log(this.name)
        }
    }
}

Deep listening

When listening to an object, it cannot be triggered when the properties inside the object are changed , we can set deep monitoring for it watch 

export default {
    data: {
        studen: {
            name: 'Joe',
            skill: {
                run: {
                    speed: 'fast'
                }
            }
        }
    },
    watch: {
        studen: {
            handler: 'sayName',
            deep: true
        }
    },
    methods: {
        sayName() {
            console.log(this.studen)
        }
    }
}

Trigger listener to execute multiple methods

Use arrays to set multiple items in the form of strings, functions, objects

export default {
    data: {
        name: 'Joe'
    },
    watch: {
        name: [
            'sayName1',
            function(newVal, oldVal) {
                this.sayName2()
            },
            {
                handler: 'sayName3',
                immaediate: true
            }
        ]
    },
    methods: {
        sayName1() {
            console.log('sayName1==>', this.name)
        },
        sayName2() {
            console.log('sayName2==>', this.name)
        },
        sayName3() {
            console.log('sayName3==>', this.name)
        }
    }
}

 

watch listens to multiple variables

watch itself cannot listen to multiple variables. But we can return the object by calculating the properties of multiple variables that need to be monitored, and then monitor this object to achieve "listening to multiple variables"

export default {
    data() {
        return {
            msg1: 'apple',
            msg2: 'banana'
        }
    },
    compouted: {
        msgObj() {
            const { msg1, msg2 } = this
            return {
                msg1,
                msg2
            }
        }
    },
    watch: {
        msgObj: {
            handler(newVal, oldVal) {
                if (newVal.msg1 != oldVal.msg1) {
                    console.log('msg1 is change')
                }
                if (newVal.msg2 != oldVal.msg2) {
                    console.log('msg2 is change')
                }
            },
            deep: true
        }
    }
}

Event parameter $event

$event It is a special variable of the event object. In some scenarios, it can provide us with more available parameters for implementing complex functions.

Primitive case

Behaves the same as the default event object in native events

<template>
    <div>
        <input type="text" @input="inputHandler('hello', $event)" />
    </div>
</template>
export default {
    methods: {
        inputHandler(msg, e) {
            console.log(e.target.value)
        }
    }
}

custom event

Behaves in custom events to capture values ​​thrown from child components

my-item.vue :

export default {
    methods: {
        customEvent() {
            this.$emit('custom-event', 'some value')
        }
    }
}
复制代码

App.vue

<template>
    <div>
        <my-item v-for="(item, index) in list" @custom-event="customEvent(index, $event)">
            </my-list>
    </div>
</template>
export default {
    methods: {
        customEvent(index, e) {
            console.log(e) // 'some value'
        }
    }
}

Documentation: cn.vuejs.org/v2/guide/ev…

cn.vuejs.org/v2/guide/co…

Two-way binding of custom components

Component options: model 

Allows a custom component to customize props and events when using v-model. By default, v-model on a component will use value as prop and input as event, but some input types such as radio buttons and checkbox buttons may want to use the value prop for different purposes. Use the model option to avoid conflicts in these situations.

input By default, it is used as the update event of the two-way binding, and the value of the binding can be updated by $emit 

<my-switch v-model="val"></my-switch>
export default {
    props: {
        value: {
            type: Boolean,
            default: false
        }
    },
    methods: {
        switchChange(val) {
            this.$emit('input', val)
        }
    }
}

Modify component options, customize bound variables and events model 

<my-switch v-model="num" value="some value"></my-switch>
export default {
    model: {
        prop: 'num',
        event: 'update'
    },
    props: {
        value: {
            type: String,
            default: ''
        },
        num: {
            type: Number,
            default: 0
        }
    },
    methods: {
        numChange() {
            this.$emit('update', this.num++)
        }
    }
}

Documentation: cn.vuejs.org/v2/api/#mod…

Monitor component life cycle

Usually we use the monitoring component life cycle , and the parent component receives events to notify $emit 

Subassembly

export default {
    mounted() {
        this.$emit('listenMounted')
    }
}

parent component

<template>
    <div>
        <List @listenMounted="listenMounted" />
    </div>
</template>

In fact, there is a simple method, you can use it to monitor the component life cycle, and you don't need to make any changes in the component. Similarly, , , etc. can also use this method. @hook  created  updated 

<template>
    <List @hook:mounted="listenMounted" />
</template>

Programmatic event listeners

For example, if you define a timer when the page is mounted, you need to clear the timer when the page is destroyed. This looks fine. But if you take a closer look , the only function is to be able to get the timer serial number inside , and there is no use other than that. this.timer  beforeDestroy 

export default {
    mounted() {
        this.timer = setInterval(() => {
            console.log(Date.now())
        }, 1000)
    },
    beforeDestroy() {
        clearInterval(this.timer)
    }
}

It is best if only the lifecycle hooks can access it. This isn't a serious problem, but it can be considered a clutter.

We can solve this problem by listening for page lifecycle destruction: $on  $once 

export default {
    mounted() {
        this.creatInterval('hello')
        this.creatInterval('world')
    },
    creatInterval(msg) {
        let timer = setInterval(() => {
            console.log(msg)
        }, 1000)
        this.$once('hook:beforeDestroy', function() {
            clearInterval(timer)
        })
    }
}

After using this method, even if we create multiple timers at the same time, it will not affect the effect. Because they are automatically cleared programmatically after the page is destroyed.

 

Manually mount components

In some requirements, manually mounting components can make our implementation more elegant. For example, a popup window component, the most ideal use is through an imperative call, like . Instead of passing state toggles in the template, this implementation is really bad. elementUI  this.$message 

Let's start with the simplest example:

import Vue from 'vue'
import Message from './Message.vue'

// 构造子类
let MessageConstructor = Vue.extend(Message)
// 实例化组件
let messageInstance = new MessageConstructor()
// $mount可以传入选择器字符串,表示挂载到该选择器
// 如果不传入选择器,将渲染为文档之外的的元素,你可以想象成 document.createElement()在内存中生成dom
messageInstance.$mount()
// messageInstance.$el获取的是dom元素
document.body.appendChild(messageInstance.$el)

The following implements a simple popup window component message 

Message/index.vue

<template>
    <div class="wrap">
        <div class="message" :class="item.type" v-for="item in notices" :key="item._name">
            <div class="content">{{item.content}}</div>
        </div>
    </div>
</template>
// 默认选项
const DefaultOptions = {
    duration: 1500,
    type: 'info',
    content: '这是一条提示信息!',
}
let mid = 0
export default {
    data() {
        return {
            notices: []
        }
    },
    methods: {
        add(notice = {}) {
            // name标识 用于移除弹窗
            let _name = this.getName()
            // 合并选项
            notice = Object.assign({
                _name
            }, DefaultOptions, notice)

            this.notices.push(notice)

            setTimeout(() => {
                this.removeNotice(_name)
            }, notice.duration)
        },
        getName() {
            return 'msg_' + (mid++)
        },
        removeNotice(_name) {
            let index = this.notices.findIndex(item => item._name === _name)
            this.notices.splice(index, 1)
        }
    }
}
.wrap {
    position: fixed;
    top: 50px;
    left: 50%;
    display: flex;
    flex-direction: column;
    align-items: center;
    transform: translateX(-50%);
}

.message {
    --borderWidth: 3px;
    min-width: 240px;
    max-width: 500px;
    margin-bottom: 10px;
    border-radius: 3px;
    box-shadow: 0 0 8px #ddd;
    overflow: hidden;
}

.content {
    padding: 8px;
    line-height: 1.3;
}

.message.info {
    border-left: var(--borderWidth) solid #909399;
    background: #F4F4F5;
}

.message.success {
    border-left: var(--borderWidth) solid #67C23A;
    background: #F0F9EB;
}

.message.error {
    border-left: var(--borderWidth) solid #F56C6C;
    background: #FEF0F0;
}

.message.warning {
    border-left: var(--borderWidth) solid #E6A23C;
    background: #FDF6EC;
}

Message/index.js

import Vue from 'vue'
import Index from './index.vue'

let messageInstance = null
let MessageConstructor = Vue.extend(Index)

let init = () => {
    messageInstance = new MessageConstructor()
    messageInstance.$mount()
    document.body.appendChild(messageInstance.$el)
}

let caller = (options) => {
    if (!messageInstance) {
        init(options)
    }
    messageInstance.add(options)
}

export default {
    // 返回 install 函数 用于 Vue.use 注册
    install(vue) {
        vue.prototype.$message = caller
    }
}

main.js

import Message from '@/components/Message/index.js'

Vue.use(Message)

use

this.$message({
    type: 'success',
    content: '成功信息提示',
    duration: 3000
})

Collect and organize: 10 Vue development tips

{{o.name}}
{{m.name}}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324131369&siteId=291194637