Let's talk about the "boundary grasp" and "state-driven" of vue component development


Let's talk about the "boundary grasp" and "state-driven" of
vue component development. Vue has a complete component development mechanism, but the official website only gives the development method. We still need to explore the best practices for development specifications and component development. . This article discusses the two topics of "grasping boundaries" and "state-driven" based on the experience in normal development.

Boundary grasping
Boundary grasping is actually very easy to understand. In modular programming, we usually define the functional boundaries of a module, what to do, what not to do, what to receive from the outside, and what to provide to the outside. Under the componentized system of Vue, these problems are more specific and need to be carefully grasped. The

principle of dividing business logic applies to any modular development. Which business a component is responsible for should be very clear at the beginning of writing, otherwise the boundaries will be easily blurred.

For example, there is a popup layer on the page, which will display the user name. So in the pop-up layer component, does it need to have such a data as username?

Obviously not needed. The task of the pop-up layer is: pop-up, close, and display content. As for what the content is, the component doesn't need to care. So at most we will define a generic content field, or simply use a slot.

The components are simple and easy to grasp. When the business is more complex, it needs to be carefully considered. This is a basic thinking.

Attention points for father-son communication

This topic must be familiar to everyone. You can even recite it easily: the father transmits data to the son through props, and the son sends messages to the father through emit. What's there to say about this?

The easily overlooked problem with props is that when a parent component passes an object to a child component, the pass is no longer "one-way". Because the child component gets a reference, when the child component modifies the property value on the object, the data of the parent component will change accordingly. The data flow becomes bidirectional, and the child component should not directly modify the data of the parent component. So we want to pass only simple values ​​in props. Reference types such as objects and arrays should be avoided.

In order to ensure the data type passed by props, it is recommended to specify the type and default value when defining props:

copy code props
: {
    name: {
        type: string,
        default: ''
    }
} Speaking of a principle, what subcomponents need to notify externally is "what happened to me", not "what are you going to do". It's just a difference in semantics, and it's just a matter of naming. But logically speaking, lack is a behavior with unclear boundaries. It's also easy to figure out that if the child component determines the behavior of the parent component, then they are logically coupled. For example: Click the OK button on the pop-up layer, and the parent component requests the product list. Then the message sent by the child component should be called "confirm" or "ok", not called "request-product". Avoid global operations In our usual programming, we usually use some BOM methods such as history, or use the methods on the document. This kind of behavior of accessing global objects is also regarded as "out-of-bounds" behavior. After all, it has already stepped out of the component. Once a component has the behavior of manipulating global objects, it can be considered a potential threat. Therefore, you should usually pay attention to the following aspects: use this.$el.querySelector instead of document.querySelector, do not query the BOM interface used by the DOM outside the component, encapsulate it into a module, and introduce and use the local , the localStorage related operations are unified into one storage.

















Subcomponents try to avoid listening to window events, let the outermost component listen, and then pass

the data Vuex state management

If you use Vuex, then the data management in the store also needs to be paid attention to. Vue perfectly integrates vuex, a global state management tool, which can access/submit state through this.store in any component.

Since it is a global state, we are worried again. Isn't the operation of global things in the component an out-of-bounds behavior? And various commits are scattered in various components, won't it be troublesome to find in the future?

My approach is like this, define a separate module, let's call it storeMonitor, all the methods of modifying the global state are defined in it, and components use this storeMonitor to modify the data in the store, which is equivalent to a facade mode. The advantage of this is that components modify the global state indirectly, which is equivalent to establishing an isolation layer. On the other hand, all commit operations are concentrated in this file, which is clear at a glance.



State-driven
What is state-driven

State-driven can also be said to be data-driven, but the data exists specifically (such as a js object), and "state" is an abstract description. State-driven means that the code logic is focused on data manipulation, rather than DOM manipulation and style manipulation.

For example, a form submit button has a gray background when it is not clickable, and a blue background when it is clickable. Then we control it through a js variable disabled, the rough code is as follows:

<button :class="disabled ? 'bg-gray' : 'bg-blue'">Submit</button>
This is not the ultimate meaning of mvvm two-way binding Well, talking nonsense for a long time.

Actually the above code is wrong. If you vaguely feel that the names of bg-gray and bg-blue are a bit awkward, and even the disabled one doesn't look pleasing to the eye, then you may understand what I want to say.

Where is the problem? Think about what semantics this code expresses. "Gray background when the button is unavailable, and blue background when it is available", this is obviously still the way of the DOM world. It's just a skin of two-way binding, not state-driven at all.

The essence of state-driven is to retain business logic and eliminate all thinking related to DOM and styles. And what might our real business logic be? "Make the button available when the verification passes, and invalid when it fails". So, the correct code should be written like this:

<button :class="validate ? 'enable' : 'disabled'">Submit</button>
What? do not lie to me! You just changed the name.

I didn't lie to you, "naming is thinking", this is the principle I have always adhered to. People who randomly name variables must have a messy head. When you understand the principle of sacrificing life for righteousness, you will naturally come back and recite with me: "naming is thinking".

All functions on the page are completely abstracted into states, that is, state-driven, and this state is not the state of styles. So, how to have the right state-driven thinking? The answer is: object-oriented.

Object-oriented thinking

does not look at appearances, but looks at abstractions. The object-oriented thinking that front-end needs to have is almost like this.

What is the appearance? It is an input box, a pop-up layer, a list, a table, and various colors.

What is abstraction? It is the user name, the password, the login status, and various business data. We abstract the content of the page into the properties of the object, and abstract the interaction into the methods of the object.

Let's take an example, look at the following ugly prototype diagram:



then the object we abstract should be roughly like this:

Copy code
{
    businessOptions: [],
    currentIndex: 0,
    selectedList: [],
    select: function(index){ //Select operation }
    remove: function(index){ //Remove operation }
}
Copy code
Our code logic should be Toggle currentIndex, and call the select method to add options to the selectedList array. If you want to use active to represent the currently active tab, or left/right to represent the left/right columns, you are making a big mistake of representationalism.

There are many object-oriented thinking that may be used when writing small games. In component development, this thinking should also be used for overall design. A component is a very concrete entity, so it is necessary to "materialize" it.

CSS also "states"

CSS as a style description language, and there are various rules for its naming and organization. Under the state-driven development thinking, I tend to let CSS also have the ability to "describe state". Look at the following piece of sass code:

copy code
.sidebar{
    position: absolute;
    bottom: 0;
    width: 80%;
    &.show{
        display: block;
    }
    &.hidden{
        display: none;
    }
    .btn{
        display: inline-block;
        width: 200px;
        height: 20px;
    }
    &.open{
        left: 0;
        .btn{
            background-image: url(left.png);
        }
    }
    &.close{
        left: -80%;
        .btn{
            background-image: url(right.png);
        }
    }
}
Copy
code We can already know the display of the interface by just looking at the css and not the js code Logical: there is a sidebar named sidebar, which has four states: show, hidden, open, close. There is a button btn under the sidebar, which is the background image to the left when the sidebar is open, and the background image to the right when the sidebar is closed.

Such a set of CSS rules with clear structure and clear semantics can help us quickly sort out the logic of the page, and others can see it at a glance when they look at your code. The above is just a simple example. In practice, there will be complex scenarios. You can divide their scopes (nested syntax) according to specific functions. It takes a little time to design, and the exchange is clear code.

There is no need to dynamically create components

. When using the mvvm framework to write the popup component, there is often such a confusion: in the jquery era, we call the popup by $.msg('content'), and then dynamically on the page Create a node and remove the node when the popup is closed. Getting used to this, we'd love to be able to handle popups in the same way.

Of course, this can also be done in Vue. The way is to dynamically create a label, and dynamically create a new component instance to render it, and manually delete this node when listening to the close message. The general code is as follows:

copy code
const MessageConstructor = Vue.extend(alert);

const Message = (config) => {
   instance = new MessageConstructor({
       el: document.createElement('div')
   });
   document.body.appendChild( instance.$el);

   Vue.nextTick(()=>{
       instance.show = true;
       instance.content = config.content || '';
       instance.type = config.type || 'danger';
       instance.$on('close', function(){
           this.show = false;
           document.body.removeChild(this.$el);
       });
       instance.$ on('confirm', config.onConfirm)
   });
}

export default Message;
Copy code
This method can indeed be implemented, but its idea is contrary to the state-driven, an application pops up a window at a certain time, this It can be understood as the state of this application. We only need to use a variable to mark the state, and we don't need to manually create and delete nodes. In fact, the author of Vue also respects this way of dealing with pop-up windows. The node is always mounted on the page, and it can be displayed when it needs to pop up.

At the end of this article, the above are the best practices summarized by the author in actual developers. Of course, this is just a development mode, and there is no right or wrong. We can refer to, or lead to other thinking.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326570039&siteId=291194637