By Michael Thiessen

Have a dream, have dry goods, wechat search [big move world] pay attention to this in the wee hours of the morning is still brushing the dishes of the wisdom.

In this paper, making github.com/qq449245884… Has been included, there are a line of big factory interview complete test points, information and my series of articles.

Learning to be a better Vue developer isn’t always about big concepts that take time and effort to master. There are a few tricks and tricks that can make our programming life easier — without a lot of rework.

Over the years of development with Vue, I have learned many useful skills. Some are handy, some are used almost every day, some are more advanced — but they’re all useful.

1. Restrict a prop to a list of types

You can restrict a prop type to a specific set of values using the Validator option in the Prop definition.

export default {
  name: 'Image',
  props: {
    src: {
      type: String,
    },
    style: {
      type: String,
      validator: s => ['square', 'rounded'].includes(s)
    }
  }
};
Copy the code

This validation function accepts a prop and returns true or false if the prop is valid or invalid.

I usually use this method when passing true or false alone to control some condition does not satisfy a requirement.

Button type or warning type (message, success, Danger, warning) are the most common uses. Color is also a good use.

2. Default content and extension points

Slots in Vue can have default content, which allows us to make components that are easier to use.

<button class="button" @click="$emit('click')"> <slot> <! -- Used if no slot is provided --> Click me </slot> </button>Copy the code

My favorite use of default slots is to use them to create extension points.

We can take any part of the component, encapsulate it in a slot, and on the outside we can override that part of the component with anything we want. By default, it still works the same way, but with more options

<template>
  <button class="button" @click="$emit('click')">
    <slot>
      <div class="formatting">
        {{ text }}
      </div>
    </slot>
  </button>
</template>
Copy the code

Now we can use this component in many different ways. Simple, default ways, or custom ways.

<! -- Uses default functionality of the component --> <ButtonWithExtensionPoint text="Formatted text" /> <ButtonWithExtensionPoint> <div class="different-formatting"> Do something a little different here </div> </ButtonWithExtensionPoint>Copy the code

3. Use quotes to listen for nested attributes

You may not know this, but we can easily listen directly to nested values by using quotes:

watch { '$route.query.id'() { // ... }}Copy the code

Know when to use itv-if(and when to avoid it)

Instead of using V-if, sometimes v-show is used instead for better performance.

<ComplicatedChart v-show="chartEnabled" />
Copy the code

When V-IF is turned on or off, it creates and completely destroys the element. Instead, V-show creates the element and leaves it there, hiding it by setting its style to display: None.

This is more efficient if you are switching components that have high rendering costs.

Conversely, if you don’t need to execute an expensive component immediately, you can use v-if, which skips rendering it and makes the page load faster.

5. Shorthand for a single scope slot (no template tag required)

Scoped slots are interesting, but in order to use them, you must also use a number of template tags.

Fortunately, there is a shorthand to get rid of it, but only if we use a single scope slot.

Common writing:

<DataTable>
  <template #header="tableAttributes">
    <TableHeader v-bind="tableAttributes" />
  </template>
</DataTable>
Copy the code

Do not use the template:

<DataTable #header="tableAttributes">
  <TableHeader v-bind="tableAttributes" />
</DataTable>
Copy the code

Simple, straightforward and admirably straightforward.

6. Conditionally render slots

Let’s see how first, and then discuss why we want to hide the slot.

Each Vue component has a special $Slots object that contains all of your slots. The key of the default slot is default, and any named slot uses its name as the key.

const $slots = {
  default: <default slot>,
  icon: <icon slot>,
  button: <button slot>,
};
Copy the code

But the $Slots object only applies to slots for that component, not each defined slot.

Take this component that defines several slots, including several named slots.

<! -- Slots.vue --> <template> <div> <h2>Here are some slots</h2> <slot /> <slot name="second" /> <slot name="third" /> </div> </template>Copy the code

If we apply only one slot to the component, only that slot will be shown in our $Slots object.

<template>
  <Slots>
    <template #second>
      This will be applied to the second slot.
    </template>
  </Slots>
</template>
Copy the code
$slots = { second: <vnode> }
Copy the code

We can use this in our component to detect which slots have been applied to the component, for example, by hiding the slot’s wrapper element.

<template>
  <div>
    <h2>A wrapped slot</h2>
    <div v-if="$slots.default" class="styles">
      <slot />
    </div>
  </div>
</template>
Copy the code

Now, the style wrapper div will only be rendered if we fill the slot with something.

If v-if is not used, then you get an empty and unnecessary div if there is no slot. Depending on the style of the div, this might disrupt our layout and make the interface look strange.

So why do we want to be able to render slots conditionally?

There are three main reasons for using conditional slots:

  • When using encapsulateddivTo add a default style
  • The slot is empty
  • If we combine the default content with nested slots

For example, when we are adding the default style, we add a div around the slot:

<template> <div> <h2>This is a pretty great component, amirite? </h2> <div class="default-styling"> <slot > </div> <button @click="$emit('click')">Click me! </button> </div> </template>Copy the code

However, if the parent component does not apply content to the slot, we end up rendering an empty div on the page.

<div> <h2>This is a pretty great component, amirite? </h2> <div class="default-styling"> <! There is no content in the slot, but the div is still rendered. Bad -- -- > < / div > < button @ click = "$emit (' click ')" > click me! </button> </div>Copy the code

The solution is to do what I said above, multiple conditions, and that’s it.

7. How do I listen for changes in a slot

Sometimes we need to know when the contents of a slot have changed.

<! --> <slot @change="update" />Copy the code

Unfortunately, Vue has no built-in way for us to detect this.

However, my friend Austin has come up with a very clean way to do this using MutationObserver.

The MutationObserver interface provides the ability to monitor changes made to the DOM tree. It is designed as a replacement for the old Mutation Events feature, which is part of the DOM3 Events specification.

Export default {mounted() {// call 'update' const observer = new MutationObserver(this.update); Observe.observe (this.$el, {childList: true, subtree: true}); }};Copy the code

This involves a lot of content, will be a separate article later.

8. Will be local and globalstyleMix it together

Usually, when dealing with styles, we want them to be partitioned into a separate component.

<style scoped>
  .component {
    background: green;
  }
</style>
Copy the code

However, you can also add a non-scoped style block to add global styles if desired

Word-wrap: break-word! Important; "> < span style =" max-width: 100%; } < span style = "box-sizing: border-box; color: RGB (50, 50, 50); line-height: 22px; white-space: inherit;" } </style>Copy the code

But be careful, global styles are dangerous and hard to track. But sometimes, they’re a perfect escape hatch, just what you need.

9. Override child component styles — the right way

Scoped CSS is great for keeping content clean and tidy without introducing styles into other components of your application.

But sometimes you need to override the style of a child component and get out of the scope.

Vue has a deep selector:

<style scoped>
.my-component >>> .child-component {
  font-size: 24px;
}
</style>
Copy the code

Note: If you are using a CSS preprocessor like SCSS, you may need to use /deep/ instead.

10. Create magic with context aware components

** Context-aware components ** are “magical” in that they automatically adapt to what’s happening around them, dealing with edge cases, state sharing, and so on.

There are three main types of context-aware, but Configuration is the one I’m most interested in.

1. Status sharing

When you break a large component into smaller components, they often still need to share state.

We can do it “behind the scenes” instead of pushing it off to the users.

We usually split the Dropdown component into Select and Option components for more flexibility. But for ease of use, the Select and Option components share the selected state with each other.

<! -- For simplicity, use as a single component --> <Dropdown v-model="selected" :options="[]" /> <! -- Divided into multiple components, <Option > <Option value="ketchup"> ketchup </Option> <div class="relish-wrapper"> <Option value="relish">Relish</Option> </div> </Select>Copy the code

2. Configuration

Sometimes, the behavior of a component needs to change depending on what happens to other parts of the application. This is usually done to automatically handle edge cases that would otherwise be annoying to handle.

A Popup or Tooltip should be repositioned so that it does not overflow the page. However, if the component is within a modal, it should be repositioned so that it does not overflow the modal.

This can be done automatically if the Tooltip knows it is in a mode.

Styles 3.

Context-aware CSS is created, with different styles applied depending on the parent or sibling element.

.statistic {
  color: black;
  font-size: 24px;
  font-weight: bold;
}

.statistic + .statistic {
  margin-left: 10px;
}
Copy the code

CSS variables take us a step further, allowing us to set different values in different parts of a page.

11. How to create a responsive variable outside of Vue (Vue2 and 3)

If you get a variable out of Vue, it’s good to make it reactive.

That way, we can use it on computed props, watch, and anywhere else, and it works just like any other state in Vue.

If we use the option API, all we need to do is put it in the data section of the component:

const externalVariable = getValue(); export default { data() { return { reactiveVariable: externalVariable, }; }};Copy the code

If you’re using Vue3’s composite API, you can just use refs or reactive.

import { ref } from 'vue'; Const externalVariable = getValue(); const externalVariable = getValue(); const reactiveVariable = ref(externalVariable); console.log(reactiveVariable.value);Copy the code

Use reactive instead:

import { reactive } from 'vue'; Const externalVariable = getValue(); const externalVariable = getValue(); Const anotherReactiveVariable = reactive(externalVariable); // Access directly console.log(anotherReactiveVariable);Copy the code

If you’re still using Vue2, you can achieve exactly the same result by using Observables instead of reactive.

12. Deconstruction in V-for

Did you know that you can use deconstruction in -vfor?

<li
  v-for="{ name, id } in users"
  :key="id"
>
  {{ name }}
</li>
Copy the code

More commonly, indexes can be retrieved from v-for by using such tuples.

<li v-for="(movie, index) in [
  'Lion King',
  'Frozen',
  'The Princess Bride'
]">
  {{ index + 1 }} - {{ movie }}
</li>
Copy the code

When using an object, you can use a key like this:

<li v-for="(value, key) in {
  name: 'Lion King',
  released: 2019,
  director: 'Jon Favreau',
}">
  {{ key }}: {{ value }}
</li>
Copy the code

You can also combine the two methods to get the index of the key and the attribute.

<li v-for="(value, key, index) in {
  name: 'Lion King',
  released: 2019,
  director: 'Jon Favreau',
}">
  #{{ index + 1 }}. {{ key }}: {{ value }}
</li>
Copy the code

13. Loop within the specified range

The V-for directive allows us to iterate through sets of numbers, but it also allows us to iterate over a range

<template>
  <ul>
    <li v-for="n in 5">Item #{{ n }}</li>
  </ul>
</template>
Copy the code

Render results:

Item #1
Item #2
Item #3
Item #4
Item #5
Copy the code

When we use v-for with a range, it starts at 1 and ends at the number we specify.

14. Listen to anything in your component

export default {
  computed: {
    someComputedProperty() {
      // Update the computed prop
    },
  },
  watch: {
    someComputedProperty() {
      // Do something when the computed prop is updated
    }
  }
};
Copy the code

We can listen in:

  • Calculate attribute
  • props
  • Nested values

If you use the composite API, any value can be monitored as long as it is a ref or reactive object.

15. Steal prop type

I copied prop types from a child component just to use them in a parent component. But I’ve found that stealing these prop types is much better than just copying them.

For example, we use an Icon component in this component.

<template>
  <div>
    <h2>{{ heading }}</h2>
    <Icon
      :type="iconType"
      :size="iconSize"
      :colour="iconColour"
    />
  </div>
</template>
Copy the code

To make it work, we need to add the correct prop type copied from the Icon component.

import Icon from './Icon';
export default {
  components: { Icon },
  props: {
    iconType: {
      type: String,
      required: true,
    },
    iconSize: {
      type: String,
      default: 'medium',
      validator: size => [
        'small',
        'medium',
        'large',
        'x-large'
      ].includes(size),
    },
    iconColour: {
      type: String,
      default: 'black',
    },
    heading: {
      type: String,
      required: true,
    },
  },
};
Copy the code

How painful.

When the Icon component’s Prop type is updated, we definitely forget to return the component and update them. Over time, errors are introduced when the prop type of that component begins to deviate from the prop type in the Icon component.

Therefore, this is why we want to steal the component’s prop type:

import Icon from './Icon'; export default { components: { Icon }, props: { ... Icon.props, heading: { type: String, required: true, }, }, };Copy the code

It doesn’t need to be complicated.

Except in our example, we add icon to the beginning of each prop name. So we have to do some extra work to make that happen.

import Icon from './Icon'; const iconProps = {}; Object.entries(Icon.props).forEach((key, val) => { iconProps[`icon${key.toUpperCase()}`] = val; }); export default { components: { Icon }, props: { ... iconProps, heading: { type: String, required: true, }, }, };Copy the code

Now, if the prop type in the Icon component is changed, our component will stay up to date.

But what if a prop type is added or removed from the Icon component? To cope with these situations, we can use v-bind and a computational prop to keep things dynamic.

16. Check the external (or internal) click of the element

Sometimes I need to detect whether a click is happening inside or outside of a particular element EL. That’s the way I usually do it.

Window.addeventlistener (' mouseDown ', e => {// get the element that was clicked const clickedEl = e.target; If (el.contains(clickedEl)) {else {// contains(clickedEl));Copy the code

17. Recursive slots

At one point, I decided to see if I could make a V-for component using only templates. In the process, I also discovered how to use slots recursively.

<! -- VFor.vue --> <template> <div> <! {{list[0]}} <! If we have more projects, keep going! <v-for v-if="list.length > 1" :list="list.slice(1)" /> </div> </template>Copy the code

If you want to do this with scope slots, it just takes some tweaking

<template> <div> <! -- Pass the item into the slot to be rendered --> <slot v-bind:item="list[0]"> <! -- Default --> {{ list[0] }} </slot> <v-for v-if="list.length > 1" :list="list.slice(1)" > <! -- Recursively pass down scoped slot --> <template v-slot="{ item }"> <slot v-bind:item="item" /> </template> </v-for> </div> </template>Copy the code

Here’s how to use this component.

<template> <div> <! <v-for :list="list" /> <! - bold project list - > < v - for: list = "list" > < template v - slot = "{item}" > < strong > {{item}} < / strong > < / template > < / v - for > < / div >  </template>Copy the code

18. Component metadata

Not every bit of information added to a component is state. Sometimes we need to add metadata to provide more information to other components.

For example, if you are working for an analytics meter like Google Analytics:

If you want the layout to know how many columns each widget should have, you can add metadata directly to the component.

Export default {name: 'LiveUsersWidget', // 👇 add props: 3, props: {//... }, data() { return { //... }; }};Copy the code
Export default {name: 'LiveUsersWidget', // 👇 add props: 3, props: {//... }, data() { return { //... }; }};Copy the code

You’ll notice that this metadata is a property on the component.

import LiveUsersWidget from './LiveUsersWidget.vue';
const { columns } = LiveUsersWidget;
Copy the code

We can also access metadata from within the component through the special $options property.

export default {
  name: 'LiveUsersWidget',
  columns: 3,
  created() {
    // 👇 `$options` contains all the metadata for a component
    console.log(`Using ${this.$options.metadata} columns`);
  },
};
Copy the code

Just remember that this metadata is the same for every instance of the component and is not responsive.

Other uses in this regard include (but are not limited to) :

  • Keep the version number of individual components
  • Custom flags used to build tools to treat components differently
  • Add custom functionality to components in addition to computing properties, data, watch, and so on
  • other

19. Multi-file single-file components

This is a known feature of SFC(single file component).

You can import files just like regular HTML files:

<template src="./template.html"></template>
<script src="./script.js"></script>
<style scoped src="./styles.css"></style>
Copy the code

This can come in handy if you need to share styles, files, or anything else.

Reusable components are not what you think they are

Reusable components don’t have to be big or complex things.

I often make small and short components reusable.

Because I’m not rewriting this code everywhere, it’s easier to update it, and I can make sure that every OverflowMenu looks and works exactly the same — because they are!” .

<! -- OverflowMenu.vue --> <template> <Menu> <! -- Add a custom button to trigger our menu --> <template #button v-slot="bind"> <! <template #icon> < img SRC ="./ellipsis.svg" /> </template> </Button> </template> </Menu> </template>Copy the code

Here, we take a menu component, but add an ellipsis icon to the button that fires it. (ellipsis) icon to trigger its opening.

It doesn’t seem worth it to make it a reusable component because it’s only a few lines long. Can’t we just add ICONS every time we want to use a menu like this?

But this OverflowMenu will be used dozens of times, and now if we want to update the icon or its behavior, we can do so very easily. And it’s easier to use.

Call a method from outside the component

We can call a method from outside a component by giving it a ref.

<! -- Parent.vue --> <template> <ChildComponent ref="child" /> </template>Copy the code
// Somewhere in Parent.vue
this.$refs.child.method();
Copy the code

Explain the problem again.

Sometimes “best practices” don’t apply to what you’re doing, and you need an escape hatch like this.

Normally, we use props and events to communicate between components. Props are delivered to the child component, and events are uploaded to the parent component.

<template>
  <ChildComponent
    :tell-me-what-to-do="someInstructions"
    @something-happened="hereIWillHelpYouWithThat"
  />
</template>
Copy the code
// Child.vue export default { props: ['trigger'], watch: { shouldCallMethod(newVal) { if (newVal) { // Call the method when the trigger is set to `true` this.method(); }}}}Copy the code

This works fine, but only on the first call. If you need to trigger this action more than once, you must clean up and reset the state. Here’s the logic

  • The parent components willtruePass to triggerprop
  • Watch is triggered, and the Child component calls the method
  • The child emits an event to tell the parent that the method has been successfully fired
  • The Parent components willtriggerReset tofalseSo we can do it all over again

Instead, if we set a ref on a child component, we can call this method directly:

<! -- Parent.vue --> <template> <ChildComponent ref="child" /> </template>Copy the code
// Somewhere in Parent.vue
this.$refs.child.method();
Copy the code

Yes, we broke the “props down, events up” rule, we broke the encapsulation, but it was clearer, it was easier to understand, so it was worth it

Sometimes the “best” solution turns out to be the worst solution.

22. Listen for arrays and objects

The trickiest part of using Watcher is that sometimes it doesn’t seem to trigger correctly.

Typically, this is because we are trying to listen on an array or object without setting deep to true

export default { name: 'ColourChange', props: { colours: { type: Array, required: true, }, }, watch: {// use object syntax, not just method colours: {// this will let Vue know to look inside The array deep: true, handler() console.log('The list of colours has changed! '); }}}}Copy the code

Using the Vue 3 API would look like this:

watch( colours, () => { console.log('The list of colours has changed! '); }, { deep: true, } );Copy the code

23. Use Vue Router for deep link

We can store (a bit) of state in the URL, allowing us to jump directly to a particular state on the page.

For example, if you load a page with a date range filter selected:

someurl.com/edit?date-range=last-week
Copy the code

This is great for parts of an application where users are likely to share a large number of links, for server-rendered applications, or for communicating between two separate applications with more information than normal links typically provide.

We can store filters, search values, whether the modal box is on or off, or where to scroll in the list to perfect infinite paging.

Getting query parameters using Vue-Router works like this (this also applies to most VUE frameworks, such as Nuxt and Vuepress) :

const dateRange = this.$route.query.dateRange;
Copy the code

To change this, we use the RouterLink component and update the Query.

<RouterLink :to="{
  query: {
    dateRange: newDateRange
  }
}">
Copy the code

24. templateAnother use for labels

The template tag can be used anywhere in the template to better organize the code.

I like to use it to simplify v-if logic, and sometimes V-for.

In this example, we have several elements that all use the same V-if condition.

<template>
  <div class="card">
    <img src="imgPath" />
    <h3>
      {{ title }}
    </h3>
    <h4 v-if="expanded">
      {{ subheading }}
    </h4>
    <div
      v-if="expanded"
      class="card-content"
    >
      <slot />
    </div>
    <SocialShare v-if="expanded" />
  </div>
</template>
Copy the code

It’s a little clunky and not obvious at first, a bunch of these elements are shown and hidden together. On a larger, more complex component, this could be an even worse situation

But we can optimize it.

We can use the template tag to group these elements and promote v-if to the template itself.

<template> <div class="card"> <img src="imgPath" /> <h3> {{ title }} </h3> <template v-if="expanded"> <h4> {{ subheading  }} </h4> <div class="card-content"> <slot /> </div> <SocialShare /> </template> </div> </template>Copy the code

Now it looks a lot easier to understand, and it’s pretty clear what it’s doing.

25. Better ways to handle errors (and warnings)

We can provide a custom handler for errors and warnings in Vue.

// Vue 3
const app = createApp(App);
app.config.errorHandler = (err) => {
  alert(err);
};

// Vue 2
Vue.config.errorHandler = (err) => {
  alert(err);
};
Copy the code

Error tracking services like Bugsnag and Rollbar hook these handlers to log errors, but you can also use them to handle errors more gracefully for a better user experience.

For example, if an error is not handled and the application doesn’t crash directly, you can display a full error screen and let the user refresh or try something else.

In Vue3, the error handler can only handle Template and watcher errors, but Vue2’s error handler catches almost any error. The warning handlers in both versions are only available during the development phase.

~ over, I am dishwashing wisdom, dishwashing, sleep, play Lol.


The possible bugs after the deployment of code can not be known in real time, in order to solve these bugs, spent a lot of time log debugging, here by the way to recommend you a good BUG monitoring toolFundebug.

Original text: dev. To/michaelthie…

communication

Have a dream, have dry goods, wechat search [big move world] pay attention to this in the wee hours of the morning is still brushing the dishes of the wisdom.

In this paper, making github.com/qq449245884… Has been included, there are a line of big factory interview complete test points, information and my series of articles.