preface

The Vue 3.0 Beta was released in the last few days, which was supposed to be a happy event, but the forums have seen a lot of opposition. The mainstream objections might be as follows:

  1. Spaghetti code structure:

“I’m so disappointed. I might as well just use React.”

Oh my god, if 3.0 is done this way, the code structure is not clear and the semantics are not clear, which is like throwing away all the advantages of Vue itself

How to feel the code structure is not 2.0 clear 😂 this if the amount of code up is not good maintenance ah

  1. React:

Copy to copy without their own personality

Do you smell react? It’s starting to look like React

In my opinion, the dark day of Vue is far from over. In fact, many people have not read the motivation chapter of Vue comcomposition Api document carefully. This article will take this chapter as a clue to dispel some of your concerns from the aspects of code structure and underlying principles.

At the beginning of the article, the author’s position should be marked first. I like both React and Vue very much. They all have their own advantages and disadvantages, this article is not intended to provoke war. Both frameworks are great! They just have their pros and cons. React Immutable actually also brings a lot of benefits, and the Hook idea is pioneered by the Facebook team leaders, it is really an amazing design, I pay 100% tribute to React!

The motivation

For a globally popular framework such as Vue3, the design of any breaking-change must have its own deliberation and tradeoffs. So what problem does comjunction-API appear to solve? This is a question we need to think about first.

Let’s start by throwing out a few problems with Vue2’s code mode.

  1. As functionality grows, the code for complex components becomes harder and harder to maintain. Especially if you’re taking over someone else’s code. The root cause is that Vue’s existing API organizes code by “options,” but in most cases, it makes more sense to organize code by logical considerations.
  2. Lack of a “clean” mechanism to extract and reuse logic across multiple components.
  3. Type inference is not friendly.

Logic reuse

It is believed that many people who have been exposed to React Hook have a certain understanding of the simplicity of logic reuse between components in this mode. Since the release of React 16.7, a large number of Hook wheels have emerged in the community, as well as the mainstream ecological library React-Router. React-redux and others all embrace Hook, which shows that the community’s friends agree with the development mechanism of Hook.

Mixin -> HOC & render-props -> Hook. Mixin -> mixin -> HOC & render-props -> Hook. Mixin -> mixin -> mixin -> Hook. The latter two have their own problems, such as adding nesting components and not knowing the origin of props. So far, Hook is a relatively perfect solution.

Of course, my style is always to compare code, so I use HOC as an example, and this is what happened in a real open source project on Github:

HOC contrast Hook

class MenuBar extends React.Component {
  // Props is a mixture of attributes passed in by each of the hoses and attributes passed in by the parent component.
  handleClickNew() {
    const readyToReplaceProject = this.props.confirmReadyToReplaceProject(
      this.props.intl.formatMessage(sharedMessages.replaceProjectWarning)
    );
    this.props.onRequestCloseFile();
    if (readyToReplaceProject) {
      this.props.onClickNew(this.props.canSave && this.props.canCreateNew);
    }
    this.props.onRequestCloseFile();
  }
  handleClickRemix() {
    this.props.onClickRemix();
    this.props.onRequestCloseFile();
  }
  handleClickSave() {
    this.props.onClickSave();
    this.props.onRequestCloseFile();
  }
  handleClickSaveAsCopy() {
    this.props.onClickSaveAsCopy();
    this.props.onRequestCloseFile(); }}export default compose(
  / / the internationalization
  injectIntl,
  / / the menu
  MenuBarHOC,
  // react-redux
  connect(mapStateToProps, mapDispatchToProps)
)(MenuBar);
Copy the code

Yeah, there’s a couple of hocks in there with the compose function, and there’s something like connect that takes a couple of arguments and returns a function that takes a component as a function, and if you’re new to this whole React thing (or even a React veteran), You would say, “What props does this come from?” “, “Is the props external or HOC?” These problems get lost in the brain, and eventually degenerate (false).

Without talking about HOC, my brain is already bursting. Let’s see what happens when we reuse logic using hooks.

function MenuBar(props) {
  // Props contains only the properties passed in by the parent component
  const { show } = props;
  / / the menu
  const { onClickRemix, onClickNew } = useMenuBar();
  / / the internationalization
  const { intl } = useIntl();
  // react-redux
  const { user } = useSelector((store) = > store.user);
}

export default MenuBar;
Copy the code

Everything became clear, I knew exactly where the method came from, where the intL was injected, and clicked on useMenuBar to automatically jump to the corresponding logic, greatly improving maintenance and readability.

Sure, this is a “deliberate” example, but trust me, I’ve experienced this kind of payoff with React development. As the “responsibilities” of components grow, your components won’t swell to unreadable as long as you get the idea of how to organize your code.

Common request scenarios

Take another very common request scenario.

In Vue2, if I need to request a copy of data and display the corresponding view in loading and error, generally we would write:

<template>
  <div>
    <div v-if="error">failed to load</div>
    <div v-else-if="loading">loading...</div>
    <div v-else>hello {{fullName}}!</div>
  </div>
</template>

<script>
import { createComponent, computed } from 'vue'

export default {
  data() {
    // A centralized data definition is easily confused with other logically related data
    return {
        data: {
            firstName: ' '.lastName: ' '
        },
        loading: false.error: false,}},async created() {
      try {
        Loading / / management
        this.loading = true
        / / get data
        const data = await this.$axios('/api/user')
        this.data = data
      } catch (e) {
        / / the error management
        this.error = true
      } finally {
        Loading / / management
        this.loading = false
      }
  },
  computed() {
      // No one knows which part of the fullName is related to the asynchronous request and which part of the data unless they read it carefully
      // This is especially true when components are larger
      fullName() {
          return this.data.firstName + this.data.lastName
      }
  }
}
</script>
Copy the code

This code, by no means elegant, does just fine, and has zero reusability for loading, error, and so on.

Data and logic are also scattered among the options, and this is just one logic. What if there is more logic, more data, computed, methods? If you are new to the file, how can you quickly tell if the method is associated with two fields in the data?

Let’s copy zeit/ SWR logic to Vue3,

Take a look at the SWR in Vue3:

<template>
  <div>
    <div v-if="error">failed to load</div>
    <div v-else-if="loading">loading...</div>
    <div v-else>hello {{fullName}}!</div>
  </div>
</template>

<script>
import { createComponent, computed } from 'vue'
import useSWR from 'vue-swr'

export default createComponent({
  setup() {
      // useSWR helps you manage fetching, caching, even TAB focus rerequests, even Suspense...
      const { data, loading, error } = useSWR('/api/user', fetcher)
      // Easily define computed attributes
      const fullName = computed((a)= > data.firstName + data.lastName)
      return { data, fullName, loading, error }
  }
})
</script>
Copy the code

It’s that simple, right? The logic is more aggregated.

By the way, there’s more to use-SWR than meets the eye. Just a few of its abilities:

  1. The polling interval

  2. Request deduplication

  3. Cache data for the same key

  4. Update the data optimistically

  5. Re-initiate the request while the TAB is in focus

  6. Paging support

  7. Full TypeScript support

Wait, wait, wait… And with so much so powerful power in one little useSWR() function, who can say that it’s not magic?

There are countless similar examples.

umi-hooks

react-use

Code organization

All that said, and that’s just one of the advantages of hooks. This doesn’t really solve the spaghetti code problem. As the logic grows, does the logic of the components blend together and become a mess?

Start with the need to get the location of the mouse

We have this requirement across components, and I want to get a responsive variable in the component that points to where my mouse is in real time.

The example of custom Hook given by Vue official is as follows:

import { ref, onMounted, onUnmounted } from "vue";

export function useMousePosition() {
  const x = ref(0);
  const y = ref(0);

  function update(e) {
    x.value = e.pageX;
    y.value = e.pageY;
  }

  onMounted((a)= > {
    window.addEventListener("mousemove", update);
  });

  onUnmounted((a)= > {
    window.removeEventListener("mousemove", update);
  });

  return { x, y };
}
Copy the code

Used in components:

import { useMousePosition } from "./mouse";

export default {
  setup() {
    const { x, y } = useMousePosition();
    // other logic...
    return{ x, y }; }};Copy the code

It’s that simple. No need to say more. When we need to “get the responsive mouse position” in any component associated with our “view layer,” it’s just a simple sentence. And the x and y returned here are response variables processed by ref, we can listen to them with watch, and we can pass them to other custom hooks for further use. You can do almost anything you want, just use your imagination.

Let’s start with the Vue official example

The above example is enough to get us started and streamlined enough to get us into the real world. For example, Vue CLI UI File Explorer is a complex file browser component in the Vue CLI GUI (i.e., the graphical console where we type Vue UI). This is the Vue official team of the big guy wrote, I believe it is a more convincing case.

This component has the following functions:

  1. Tracks the current folder status and displays its contents

  2. Handle folder navigation (open, close, refresh…)

  3. Handle the creation of new folders

  4. Toggle display favorites

  5. Toggle to show hidden folders

  6. Process current working directory changes

The document asks a poignant question: can you, as a new developer, find out which function a variable belongs to among the options of Method, Data, computed, etc.? For example, the Create New Folder feature uses two data properties, a compute property and a method, where the method is defined “more than a hundred lines” from the data property.

When maintaining the same logic across hundreds of lines of “space distance” in a group, even if I was asked to maintain the Vue official team’s code, I would sneer and say, “What is this writing? What is this variable for?”

Youda was kind enough to present a picture in which different color blocks represent different functional points.

It’s pretty good, but it’s a disaster when it comes to maintenance, like the light blue block. I wanted to get the logic of it all right, and I had to “jump up and down and down,” and I had done that many times.

And after using Hook? We can draw the “New folder” function into a function:

function useCreateFolder(openFolder) {
  // originally data properties
  const showNewFolder = ref(false);
  const newFolderName = ref("");

  // originally computed property
  const newFolderValid = computed((a)= > isValidMultiName(newFolderName.value));

  // originally a method
  async function createFolder() {
    if(! newFolderValid.value)return;
    const result = await mutate({
      mutation: FOLDER_CREATE,
      variables: {
        name: newFolderName.value,
      },
    });
    openFolder(result.data.folderCreate.path);
    newFolderName.value = "";
    showNewFolder.value = false;
  }

  return {
    showNewFolder,
    newFolderName,
    newFolderValid,
    createFolder,
  };
}
Copy the code

We agree that these “custom hooks” are prefixed with “use” to distinguish them from ordinary functions.

On the right side, the code after Hook is used to organize the color blocks:

We want to maintain the logic of the purple part of the function, it is good to find in the purple part, anyway, there will be no other “color block” in the variable or method to affect it, soon we changed the requirements, 6:00 on time!

This is an overview of components in Hook mode, and it’s really easy to see. I feel like I can maintain @vue/ UI too (fake).

export default {
  setup() {
    // ...}};function useCurrentFolderData(networkState) {
  // ...
}

function useFolderNavigation({ networkState, currentFolderData }) {
  // ...
}

function useFavoriteFolder(currentFolderData) {
  // ...
}

function useHiddenFolders() {
  // ...
}

function useCreateFolder(openFolder) {
  // ...
}
Copy the code

Take a look at the setup function, jokingly referred to as “spaghetti code.”

export default {
  setup() {
    // Network
    const { networkState } = useNetworkState();

    // Folder
    const { folders, currentFolderData } = useCurrentFolderData(networkState);
    const folderNavigation = useFolderNavigation({ networkState, currentFolderData });
    const { favoriteFolders, toggleFavorite } = useFavoriteFolders(currentFolderData);
    const { showHiddenFolders } = useHiddenFolders();
    const createFolder = useCreateFolder(folderNavigation.openFolder);

    // Current working directory
    resetCwdOnLeave();
    const { updateOnCwdChanged } = useCwdUtils();

    // Utils
    const { slicePath } = usePathUtils();

    return{ networkState, folders, currentFolderData, folderNavigation, favoriteFolders, toggleFavorite, showHiddenFolders, createFolder, updateOnCwdChanged, slicePath, }; }};Copy the code

Whose little fairy is so beautiful! That logic is so clear that it has nothing to do with pasta!

contrast

Hook versus Mixin & HOC

Having said that, the official Mixin & HOC model has to be sorted out.

  1. The source of the properties exposed in the rendering context is unclear. For example, when multiple mixins are used to read a component’s template, it may be difficult to determine which mixin is injecting a particular property.
  2. Namespace conflict. Mixins might collide over property and method names, and HOC over expected prop names.
  3. Performance issues, HOC and non-rendered components require additional instances of stateful components, which can degrade performance.

The benefits of the “Hook” model are:

  1. The attributes exposed to the template have an explicit source because they are returned values from the Hook function.
  2. The value returned by the Hook function can be arbitrarily named, so no namespace conflicts occur.
  3. No unnecessary component instances are created for logical reuse only.

Of course, there are some drawbacks to this model, such as the mental burden of ref, see drawbacks.

React Hook vs. Vue Hook

In fact, React Hook has many restrictions. For example, there is a special chapter in the official document to introduce its restrictions:

  1. Do not call hooks inside a loop, condition, or nested function
  2. Be sure to always call them at the top of your React function.
  3. By following this rule, you can ensure that hooks are called in the same order every time you render. This allows React to keep the hook state correct between multiple useState and useEffect calls.

The difference Vue brings is:

  1. Same level of logical composition functionality as React Hooks, but with some important differences. Unlike React Hook, the setup function is called only once, which gives it a performance advantage.

  2. There is no requirement on the call order, Hook function will not be called repeatedly in each rendering, resulting in less GC pressure.

  3. Never mind that the useCallback is almost always needed to prevent a change in reference to the transfer function prop to a child component, resulting in unnecessary re-rendering.

  4. React Hook has the infamous closure trap problem (even becoming a popular interview question, OMG), where useEffect and useMemo may catch outdated variables if the user forgets to pass the correct array of dependencies. Vue’s automatic dependency tracking ensures that the observer and calculated values are always correct.

  5. By the way, React Hooks require you to declare “dependencies” manually, and there is an esLint plugin that is useful most of the time, but can also be annoying, requiring you to manually close them with an ugly comment.

We recognize the creativity of React Hooks, which were the main inspiration for the Viee-comification-API. The problems mentioned above do exist in the design of React Hook, and we noticed that Vue’s responsive model perfectly solves these problems.

By the way, the mental burden of React Hook is really serious. If you are interested in it, please refer to:

Do the benefits of using React Hooks justify the costs of using them? – Li Yuanqiu answer – zhihu www.zhihu.com/question/35…

I also encountered a lot of problems in my own development, especially when I was trying to optimize the performance of components using Memo, and the closure problem exploded. Finally, I solved many of the problems with the useReducer method, which made me wonder if it was Dan’s conspiracy from beginning to end… (Don’t try to escape reducer)

React Hook + TS Shopping cart (performance optimization, closure trap, custom Hook)

The principle of

Since there is a contrast, let’s talk about the difference from a point of principle,

In Vue, the reason why the setup function is executed only once, and subsequent updates to the data can also drive view updates, lies in its “responsive mechanism.” For example, we define a responsive property like this:

Vue

<template>
  <div>
    <span>{{count}}</span>
    <button @click="add">+ 1</button>
  </div>
</template>

export default {
    setup() {
        const count = ref(0)

        const add = () => count.value++

        return { count, add }
    }
}
Copy the code

I only did setup once, but count is in principle a “reactive object,” and if you change the value property on it,

Trigger the re-execution of the “Render function” compiled by template.

If we need to do something when the count changes, we just need to introduce effect:

<template>
  <div>
    <span>{{count}}</span>
    <button @click="add">+ 1</button>
  </div>
</template>export default { setup() { const count = ref(0) const add = () => count.value++ effect(function log(){ console.log('count changed! ', count.value) }) return { count, add } } }Copy the code

The log function is generated only once, and the function collects it as a dependency when it reads count.value, so the next update to count.value automatically triggers the log function to re-execute.

Think about the data relationship, and you’ll quickly understand why it can only be executed once, but it’s so powerful.

In fact, the Vue3 Hook only needs an “initialization” process, which is called setup. The key word is “Execute once only.”

React

The same logic in React is written like this:

export default function Counter() {
  const [count, setCount] = useState(0);

  const add = (a)= > setCount((prev) = > prev + 1);

  // For the following explanation
  const [count2, setCount2] = useState(0);

  return (
    <div>
      <span>{count}</span>
      <button onClick={add}>+ 1</button>
    </div>
  );
}
Copy the code

It is a function that is introduced by the parent component using
, and it is actually compiled into a function like React. CreateElement (Counter), which means that the function is executed in its entirety every time it is rendered.

The count and setCount returned by useState are stored on the component’s corresponding Fiber node. Each React function must execute hooks in the same order each time, for example. When useState is first executed in this example, since useState is executed twice, it will save a list structure like {value, setValue} -> {value2, setValue2} on Fiber.

The next render will execute useState for count and useState for count2, so how will React find the values from the last render on the Fiber node? Only in order, of course.

{value, setValue}, {value2, setValue2}, {value2, setValue2}, {value2, setValue2}

This is why React severely restricts the execution order of hooks and disallows conditional calls.

If the first render executes the useState twice, and the first useState is canceled by the if condition on the second render, then the second count2 useState will get the first value in the list, completely confusing.

If you want to listen for count changes in React and do something, useEffect, then render next time

After that, we will make a shallow comparison of the deps dependency value of useEffect’s second parameter in The render twice (call object.is for each item in turn), for example

export default function Counter() {
  const [count, setCount] = useState(0);

  const add = (a)= > setCount((prev) = > prev + 1);

  useEffect((a)= > {
    console.log("count updated!", count);
  }, [count]);

  return (
    <div>
      <span>{count}</span>
      <button onClick={add}>+ 1</button>
    </div>
  );
}
Copy the code

So, when React detects that the count has changed after rendering, it executes the useEffect callback. (If you look carefully, you can see that each rendering regenerates a function reference, which is useEffect’s first argument).

Yes, React does inevitably introduce the concept of dependency, but it’s a dependency we have to write by hand, and the “mental burden” discussed in the React community in real time is basically due to this dependency…

Since each rendering is constantly executed and generates closures, both performance and GC pressure are slightly worse than Vue3. Its keyword is “re-execute every render”.

About copying React Hook

In fact, it is not good to talk about plagiarism in the front-end open source community. The emergence of a new model is worth the mutual reference and learning between frameworks. After all, the ultimate purpose of the framework is not to “boast their own maverick”, but to “facilitate the majority of developers”. This is a point worth thinking about. Many people seem to think that if one framework uses a certain model, the other will not work, but this is not good for the progress and development between frameworks.

Here’s a direct quote from Yooi’s response to “Vue borrows from the virtual DOM” in 2017:

Besides vdom. The React VDOM is actually not that good. The main reason Vue 2.0 introduced VDOM is that VDOM abstracts the rendering process, which improves the abstraction capability of components and can be adapted to render targets outside the DOM. It’s not controversial to borrow this from React, because I think VDOM is a really good idea. However, it should be clear that Vue introduced VDOM not because “React has it, so we should have it”, but because it does have technical superiority. The community has built a sea of wheels based on vDOM ideas, and the ng2 render abstraction layer and the Ember Glimmer 2 template -> OpCode compiler have many ideas similar to VDOM.

The programmers all advocate open source spirit, why some people become stingy between Vue and React? Say ugly point, Vue keeps his own maverick, that you if a new company wants you to use Vue, you don’t have to learn again from the beginning.

What’s more, the React community also borrows from Vue. For example, if you look at the API at react-router@6, you will see that it is very similar to Vue-Router in many ways. Such as “configured routing” for useRoutes, and the code structure that makes child routing in components, and so on. Of course, this is just my superficial cognition, wrong place is also welcome to correct.

Further reading

For the difference between the two kinds of hooks, students who want to further study can also read huang Ziyi’s great article:

Vue3.0 Function API

In the official issue of React Hook, you xiaoright detailed view:

Why remove time slicing from vue3?

conclusion

Actually summed up down, in the community are part of the opposing view is due to “no good documentation”, that in this article I will spend some spare time to sort out some views from the community and the official as an article, as to read the article you will later view of Vue3 change, this is not I can decide, It’s just that I love Vue3, and I’d like to do my part to make sure people don’t misunderstand it.

For the pasta code:

  1. Extract shared custom hooks (I extracted more than 3 globally reusable hooks when writing the React shopping cart component).
  2. Organize code based on “logical function,” notstatePut them together,methodTogether, this is not fundamentally different from using Vue2 (many, many new people make this mistake with React Hook, myself included).

For mental burden:

  1. More power means more learning costs, but overall I think Vue3 does a good job of keeping the mental burden under control. forrefThis thing, it really takes a little bit of thought to understand.
  2. React Hook is notorious for its mental burden, and during the actual development process I sometimes actually got bald… Especially with some custom hooks,depsDependencies pass from layer to layer (get any layer wrong and your application explodes).
  3. Don’t study how can be promoted pay, marry white rich beauty, towards the peak of life! (nonsense)

How fragrant is Vue3? Even Lucas HC, author of React State Management and Actual Isomorphism and a loyal React fan, what are the advantages of Vue and React respectively in this article? They all say the following sentence:

No more jokes: A React fan salutes Vue3.0!

Vue3 currently has some Hook attempts:

Github.com/u3u/vue-hoo…

Anyway, I hope that after reading this article, you will enjoy Vue3 more. I can’t wait for it to come.

Finally, I would like to emphasize the author’s position again. I like both React and Vue very much. They all have their own advantages and disadvantages, this article is not intended to provoke war. Both frameworks are great! They just have their pros and cons. React Immutable actually also brings a lot of benefits, and the Hook idea is pioneered by the Facebook team leaders, it is really an amazing design, I pay 100% tribute to React!

The only purpose of this article is to eliminate some friends for Vue 3.0 misunderstanding, no intention, if any offence please understand ~

Please thumb up

If this post is helpful to you, please give me a like. Your like is what keeps me going. Let me know you like reading my post

❤️ Thank you all

Follow the public account “front from advanced to hospital”, have the opportunity to draw “Gold nugget booklet 50% discount code”

Follow the public number and friends, pull you into the “front-end advanced exchange group”, everyone together to communicate and progress.