Recently, I went into a Vue project and felt that I fell into the ancestral shit mountain. The readability is very poor, let alone maintainability. Therefore, I would like to take this column to make some suggestions on the readability of Vue code. If I think it is useful, I would like to praise it. If I think it is unreasonable, I would like to comment and criticize it. If there are better suggestions, please comment and supplement.
1, Make good use of components to make the code more organized
Never put the implementation code of a page in a. vue file. Unless the page is very simple, the code in the. vue file will be long and smelly.
Vue provides components not only for reuse, but also for code segmentation, and even makes good use of components to optimize the rendering and updating speed of pages. This is because the Vue page will not update the components in the page when rendering and updating, unless the data referenced by the props or slot of the component changes.
For code optimization, see this article: 10 practical tips to make your Vue code more elegant
You can use the following steps to split a Vue page into components to make the code more organized
1.1. Extract UI components
How to define UI components? Personally, I suggest distinguishing UI components from business components by whether or not to process server-side data. For example, loading pop-up window, secondary confirmation pop-up window, message prompt box, etc. belong to UI interactive components.
After extracting the UI components, you can separate the UI interaction code from the business interaction code. Remember not to write business code in UI components, so UI components cannot be reused.
For a counter example, add the business code to be processed after the secondary confirmation in the secondary confirmation pop-up window, resulting in the UI components will not be reused. We can implement a secondary confirmation pop-up component by imitating the call of secondary confirmation pop-up in ElementUI.
this.$confirm(message, title, options) .then(res =>{}) .catch(err =>{})
In this way, the business code can be written in the callback function of then. The core implementation code of the component is as follows:
//confirm.vue <template> <div v-show="show"> //... <div @click="ok"></div> <div @click="cancel"></div> </div> </template> <script> export default { data() { return { show: false, } }, methods: { ok() { this.show = false; this.resolve(); }, cancel() { this.show = false; this.resolve(); }, } } </script>
//index.js import Vue from 'vue'; import options from './confirm.vue'; const Confirm = Vue.extend(options); let confirm = undefined; const ConfirmInit = (options = {}) => { return new Promise((resolve, reject) => { options.resolve = resolve; options.reject = reject; confirm = new Confirm({ el: document.createElement('div'), data: options }) document.body.appendChild(confirm.$el); Vue.nextTick(() => { if (confirm) confirm.show = true; }) return confirm; }) } Vue.prototype.$confirm = ConfirmInit;
//main.js import 'components/confirm/index.js';//Global registration secondary confirmation pop-up confirm component
1.2. Extract business components by module
A page can be divided into multiple areas, such as header, bottom, sidebar, commodity list, member list, etc. each area can be used as a module to extract business components.
1.3. Extract functional components by function
After extracting the business components by module, the business components may still be very large at this time, so we should further extract the functional components by function.
There are big and small functions. Several principles should be paid attention to when extracting:
Too simple functions are not extracted
For example, a collection function can be completed as long as an interface is requested. Functions like this should not be extracted. Only the functions of logical operations with a certain complexity can be extracted.
The function should be single, and a functional component only processes one business.
For example, a file reader component has a requirement to automatically collect the file after opening the file. Where should the collection logic code be written?
Maybe you didn't even think about it, you wrote the collection logic code in the method of successfully opening the listening file in the component. After a period of time, you need to add it to the reading record first, and then click the collection button to collect. When you modify the code in the component, you find that another page also references this component, so you need to add an additional parameter in the component to distinguish business scenarios, As the change of requirements leads to the superposition of business scenarios, various judgment logic will be added to the code of components, which will become long and smelly over time. Obviously, this practice cannot be ignored.
The correct approach is to customize an event on FileOpen success on the component tag, and use the handleFileOpenSuccess function to listen for this event.
<fileReader @on-fileOpen-success="handleFileOpenSuccess" > </fileReader>
This event is triggered by executing this. $emit ('on FileOpen success', data) in the method of listening for the successful opening of files in the component, where data can pass the file information and handle business interactions such as collecting or adding history records and collecting again in the handleFileOpenSuccess function. This approach makes the file reader component single.
The functional components shall contain as few UI parts as possible, and the UI part shall be passed in through the slot, so as to make the components more pure and reusable.
For example, the upload icon of the upload component cannot be added with the change of the UI design draft. At this time, the upload icon can be transferred in by using the slot slot.
//upload.vue <template> <div> <slot name="icon"></slot> </div> </template>
//index.vue <template> <div> <upload> <template #icon> //Upload Icon </template> </upload> </div> </template>
2, Using v-bind to make the properties of components more readable
If you want to pass all attributes of an object into component componentA as prop, you can use v-bind without parameters. For example, for a given object params:
params: { id: 1, name: 'vue' }
Before optimization
<componentA :id="params.id" :name="params.name"></componentA>
After optimization
<componentA v-bind="params"></componentA>
3, Using attrs and attrs and attrs and listeners to encapsulate third-party components
1,$attrs
In encapsulating third-party components, we often encounter a problem: how to use the properties and events of the third-party components through the encapsulated components.
For example, the Input input box component myInput in an elementUi component is encapsulated. When the wrong content is entered, an error prompt is displayed under the Input box.
The myInput component code is as follows:
<template> <div> <el-input v-model="input"></el-input> <div>{{errorTip}}</div> </div> </template> <script> export default { props: { value: { type: String, default: '', }, errorTip: { type: String, default: '', } }, data() { return { } }, computed: { input: { get() { return this.value }, set(val) { this.$emit('input', val) } } } } </script>
This invokes the myInput component, where errorTip prompts the input box for an error.
<myInput v-model="input" :errorTip="errorTip"></myInput>
If you want to add a disabled attribute on the myInput component to disable the input box, how do you implement it? Most students do this
<template> <div> <el-input v-model="input" :disabled="disabled"></el-input> <div>{{errorTip}}</div> </div> </template> <script> export default { props: { //... disabled: { type: Boolean, default: false } }, //... } </script>
After a period of time, you have to add other attributes of El input components to myInput components. There are more than 27 El input components in total. How can you pass them in one by one with prop? This is not only poor readability but also cumbersome. You can use $attrs in one step. Let's take a look at the definition of attrs first.
Attribs: contains attribute bindings (except class and style) in the parent scope that are not recognized (and obtained) as props. When a component does not declare any prop, the binding of all parent scopes (except class and style) will be included, and can be used through v-bind=“
<template> <div> <el-input v-model="input" v-bind="$attrs"></el-input> <div>{{errorTip}}</div> </div> </template>
This is not enough. You have to set the inheritAttrs option to false. Why? Just look at the definition of the inheritAttrs option.
By default, attribute bindings of the parent scope that are not recognized as props will be "rolled back" And as a normal HTML attribute, it is applied to the root element of the sub component. When writing a component that wraps a target element or another component, this may not always conform to the expected behavior. By setting inheritattributes to false, these default behaviors will be removed. These attributes can be made effective through $attributes, and can be explicitly bound to non components through v-bind Note: this option does not affect class and style binding.
Simply put, set inheritAttrs to false for v-bind="$attrs" to take effect.
<template> <div> <el-input v-model="input" v-bind="$attrs"></el-input> <div>{{errorTip}}</div> </div> </template> <script> export default { inheritAttrs: false, props: { value: { type: String, default: '', }, errorTip: { type: String, default: '', } }, data() { return { } }, computed: { input: { get() { return this.value }, set(val) { this.$emit('input', val) } } } } </script>
In this way, the properties of El input component and myinput component can be clearly distinguished, and the readability of props option of component is greatly improved.
2,$listeners
So how to implement the custom events on the El input component on the myIpput component? Maybe your first reaction is this.$emit.
<template> <div> <el-input v-model="input" v-bind="$attrs" @blur="blur"> </el-input> <div>{{errorTip}}</div> </div> </template> <script> export default { //... methods: { blur() { this.$emit('blur') } } } </script>
<myInput v-model="input" :errorTip="errorTip" @blur="handleBlur"> </myInput>
El input components have four custom events, which are not too many. What should we do if we encounter more third-party components with custom events? Adding them one by one will not only add a pile of unnecessary methods, but also have poor readability, and it is easy to mix with myInput's own methods. In fact, you can use listeners in one step. Let's take a look at the definition of listeners.
Listeners: contains v-on event listeners in the parent scope (without. native modifiers). It can be via v-on=“
<template> <div> <el-input v-model="input" v-bind="$attrs" v-on="$listeners"> </el-input> <div>{{errorTip}}</div> </div> </template> <script> export default { //... } </script>
<myInput v-model="input" :errorTip="errorTip" @blur="handleBlur"> </myInput>
In myInput component, as long as v-on="$listeners" is added to El input component, custom events of El input component can be used on myInput component.
For code readability, see this: Five ways to improve code readability
About this article Author: red dust refining heart https://juejin.cn/post/7005751368937897991