Vue

UI Libraries

Config

vuejs/tsconfig, Vue’s recommended TypeScript config.

According to documentation of eslint-plugin-vue, we need to specify parser in languageOptions, and add vue as extra extension.

import ts from "typescript-eslint";
 
export default ts.config(
  ...
  {
    languageOptions: {
      parserOptions: {
        extraFileExtensions: ["vue"],
        parser: ts.parser,
        projectService: true,
      },
    },
  },
  ...
);

Development

  • Directives for handling events, with extended modifiers.
  • $el becomes available to refer to the DOM node once mounted.
  • A component will only unmount itself after all children unmount.
  • Use ref attribute point to a DOM element. this.$ref.refName. The reference instance contains all properties and methods of a specific DOM element (or all children components.)
  • Mixin
    • Define components to provide common functionalities to components. (No template, so possible to implement in .js or .ts).
    • Mixin hooks are invoked before components’ hooks, and according to the order in mixins array.
    • Consider other alternatives. Mixins can affect performance.
  • Style
    • Use :deep(p) directive to apply styles to all children p element.
    • Scoped styles are not applied to slots by default. We can use :slot([selector]) to force it.
    • Use pseudo class to reference a variable in styles: color: v-bind(colorVar)
    • Using CSS Modules with <style module> to make CSS rules accessible through $style. Modules can be assigned a name.
  • Creating composables for fetching data, and a FetchComponent with different slots to abstract the handling of loading/empty/error/content states.
  • Use h function (hyper-script) and functional components to have fine-grained control on the rendering process.

Composing Component

  • setup(props, context)
    • Returns an object that contains all the references to the component’s internal reactive state and methods and any static data.
    • <script setup> adds TypeScript supported by default. Variables defined in setup(0) don’t have to be in data().
    • Used to define stateless functional component. return () => h('div', message)
  • beforeCreated(), suitable for loading external logic without changing the component state.
  • created(), runs before first render, can perform tasks that require this binding. Suitable for loading external logic into the component state.
  • updated() runs after render updates. Must not mutate component’s state here.
  • watch: { 'user.name': { handler() { ... } } } to specify specific nested property to watch.
  • this.$watch() creates a watcher, returning a function to stop the watcher.
<script lang="ts">
import { defineComponent } from "vue";
import MyComponent from "./components/MyComponent.vue";
// whatever defined here will be made available to the tempaltes
// no this needed, no .values needed
export default defineComponent({
  name: "MyNewComponent",
  // template will be compiled to be part of the component
  template: "<div>Hello World!</div>",
  components: { MyComponent }, // make the component imported aware to template
  setup() {
    console.log("setup hook");
    console.log(this);
  },
  data() {
    // data passed to template are all static
    return { message: "a message to the template" };
  },
  emits: ["task-toggle"], // specify the emits
  // methods of the component, we can use `this` here
  methods: {
    onTaskCompleted(event: Event) {
      this.$emit("task-toggle", {
        // emit an event to the
        ...this.task, // we can use this
        completed: (event.target as HTMLInputElement)?.checked,
      });
    },
  },
  watch: {}, // watchers
  provide: { providedVal: [1] }, // provide/inject
  props: {
    task: {
      type: Object as PropType<Task>,
      required: true,
      default: defaultTask,
      // validator: () => true // we can also add other validators
    },
  }, // dataflow is one way only
});
</script>

In setup script:

<script lang="ts" setup>
// we can use generic for type-only declaration
const props = defineProps<PropsType>();
 
// similarly, we can have type-only emits declaration
type EmitEvents = { (e: "task-toggle", task: Task): void };
const emits = defineEmits<EmitEvents>();
const onTaskCompleted = (event: Event) => {
  emits("task-toggle", {
    id: props.task.id,
    completed: (event.target as HTMLInputElement)?.checked,
  });
};
</script>

Composition API

  • ref for primitives, shallowRef for objects that needs observation, reactive for deeply reactive objects

Special Tags

  • Slots
    • Inside the component: <slot :var1="var1", :var2="var2>, then we can use v-slot="{ var1, var2 }" to get the slot with it’s parameters.
    • For more slots, we can name them and use v-slot directives in the templates.
    • Scoped styles of a component doesn’t apply to the slot.
  • Use to implement dialogs
  • Dynamically render component with <component :is="componentName">.
  • Keep states of an unmounted component alive with <keep-alive>