Route parameter decoupling
Generally, routing parameters are used in components. Most people will do this:
export default { methods: { getParamsId() { return this.$route.params.id } } }
Using $route in a component will form a high coupling with its corresponding route, so that the component can only be used on some specific URL s, which limits its flexibility.
The correct way is to decouple by props
const router = new VueRouter({ routes: [{ path: '/user/:id', component: User, props: true }] })
After the props attribute of the route is set to true, the params parameter can be received in the component through props
export default { props: ['id'], methods: { getParamsId() { return this.id } } }
In addition, you can return props through function mode
const router = new VueRouter({ routes: [{ path: '/user/:id', component: User, props: (route) => ({ id: route.query.id }) }] })
file: router.vuejs.org/zh/guide/es...
Functional component
Functional component is stateless, it cannot be instantiated, and there is no life cycle and method. Creating functional components is also very simple. You only need to add functional declarations to the template. It is generally suitable for components that only depend on the change of external data. Because of its light weight, the rendering performance will be improved.
Everything the component needs is passed through the context parameter. It is a context object, and the specific properties view the document. Here props is an object that contains all the binding properties.
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 usage
<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: '' } } }
file: cn.vuejs.org/v2/guide/re...
Style penetration
It is common to modify the style of third-party components in development, but due to the style isolation of scoped attributes, it may be necessary to remove scoped or create another style. These practices will bring side effects (component style pollution and lack of elegance), and style penetration will take effect only when it is used in css preprocessor.
We can use > > > or / deep / to solve this problem:
<style scoped> Outer layer >>> .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>
watch high order usage
Execute immediately
The watch is triggered only when the listening attribute changes. Sometimes, we want the watch to be executed immediately after the component is created
The possible way of thinking is to call once in the create life cycle, but such writing is not elegant. Maybe we can use this method.
export default { data() { return { name: 'Joe' } }, watch: { name: { handler: 'sayName', immediate: true } }, methods: { sayName() { console.log(this.name) } } }
Depth monitoring
When listening to an object, the watch cannot be triggered when the internal properties of the object are changed. We can set deep listening for it
export default { data: { studen: { name: 'Joe', skill: { run: { speed: 'fast' } } } }, watch: { studen: { handler: 'sayName', deep: true } }, methods: { sayName() { console.log(this.studen) } } }
Trigger listening to execute multiple methods
Using an array, you can set multiple items, including strings, functions, and 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) } } }
file: cn.vuejs.org/v2/api/#wat...
watch listens to multiple variables
watch itself cannot listen to multiple variables. However, we can return multiple variables that need to be monitored to the object by calculating the attribute, and then monitor this object to realize "monitoring 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 is a special variable of event object. In some scenarios, it can provide us with more available parameters for implementing complex functions
Native event
In the native event, the performance is the same as the default event object
<template> <div> <input type="text" @input="inputHandler('hello', $event)" /> </div> </template> export default { methods: { inputHandler(msg, e) { console.log(e.target.value) } } }
Custom event
In a custom event, it is expressed as capturing the value thrown from the sub component
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' } } }
file: cn.vuejs.org/v2/guide/ev...
cn.vuejs.org/v2/guide/co...
Custom component bidirectional binding
Component model options:
Allows a custom component to customize prop and event when using v-model. By default, the v-model on a component will use value as prop and input as event, but some input types such as radio box and check box buttons may want to use value prop for different purposes. Use the model option to avoid conflicts caused by these situations.
input is used as the update event of bidirectional binding by default. The bound value can be updated through $emit
<my-switch v-model="val"></my-switch>
export default { props: { value: { type: Boolean, default: false } }, methods: { switchChange(val) { this.$emit('input', val) } } }
Modify the model option of the component and customize the bound variables and events
<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++) } } }
file: cn.vuejs.org/v2/api/#mod...
Listening component life cycle
Usually, we will use $emit to monitor the component life cycle, and the parent component will receive events for notification
Subcomponents
export default { mounted() { this.$emit('listenMounted') } }
Parent component
<template> <div> <List @listenMounted="listenMounted" /> </div> </template>
In fact, there is another simple method. You can use @ hook to monitor the component life cycle without making any changes in the component. Similarly, this method can also be used for created, updated, etc.
<template> <List @hook:mounted="listenMounted" /> </template>
Programmed event listener
For example, if you define a timer when a page is mounted, you need to clear the timer when the page is destroyed. It doesn't look like a problem. But take a closer look at this The only function of timer is to get the timer serial number in beforeDestroy. In addition, it is of no use.
export default { mounted() { this.timer = setInterval(() => { console.log(Date.now()) }, 1000) }, beforeDestroy() { clearInterval(this.timer) } }
If you can, it's best that only the lifecycle hook can access it. This is not a serious problem, but it can be regarded as debris.
We can solve this problem through $on or $once monitoring page life cycle destruction:
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, the effect will not be affected. Because they will be programmatically cleared after the page is destroyed.
file: cn.vuejs.org/v2/guide/co...
Manual mount assembly
Among some requirements, manually mounting components can make our implementation more elegant. For example, for a pop-up component, the most ideal use is to call it by command, just like this in elementUI$ message . Instead of switching states in the template, this implementation is really bad.
//Let's start with the simplest example: import Vue from 'vue' import Message from './Message.vue'
// Construction subclass let MessageConstructor = Vue.extend(Message) // Instantiate component let messageInstance = new MessageConstructor() // $mount can pass in a selector string, indicating that it is mounted to the selector // If you don't pass in the selector, it will be rendered as an element outside the document, which you can imagine as document Createelement() generates dom in memory messageInstance.$mount() // messageInstance.$el gets the dom element document.body.appendChild(messageInstance.$el) //Here is a simple message pop-up component 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> // Default options const DefaultOptions = { duration: 1500, type: 'info', content: 'This is a prompt message!', } let mid = 0 export default { data() { return { notices: [] } }, methods: { add(notice = {}) { // The name flag is used to remove pop ups let _name = this.getName() // Merge options 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() } messageInstance.add(options) } export default { // Returns the install function for Vue Use registration install(vue) { vue.prototype.$message = caller } } main.js import Message from '@/components/Message/index.js' Vue.use(Message) this.$message({ type: 'success', content: 'Success information prompt', duration: 3000 })
file: cn.vuejs.org/v2/api/#vm-...
Author: WahFung
Link: https://juejin.cn/post/6844904115949027336
Source: Nuggets
The copyright belongs to the author. For commercial reprint, please contact the author for authorization. For non-commercial reprint, please indicate the source.