In the vUE 3.0 era, the combined API was added, and its setup syntax dramatically changed some of the ways in which Vue 2.x could be used. Here are a few simple examples of how to use setup.

One, “I want them all!”

In vue2. X, all parameters are initialized in data and then called elsewhere, there are props to receive the parameters passed by the parent component, and methods to manage all execution methods and each lifecycle function. In vue3. X, data and methods remain, but now a setup can use those methods.

<script lang="ts">
import { defineComponent, reactive, toRefs, ref, onMounted } from "vue";
export default defineComponent({
props: {
    detailQueryParams: {
      type: Object.required: true}},setup(props) {
    const { detailQueryParams } = toRefs(props);

    const unCommitParams = Object.assign(detailQueryParams.value);
    const areaTypeList = useAreaType();

    const UncommittedTableData: ItableData<PartialIWeeklyStatisticDetail> = {
      data: [].columns: useWeeklyStatisticItemTableColumn("Uncommitted")};const tableRequestFunc = getWeeklyUncommit;
    const usetableFunc = useTableData;
    const failSimpleTableRef = ref<ComponentRef>(null);

    const reactiveData = reactive({
      UncommittedTableData,
      unCommitParams,
      areaTypeList,
      tableRequestFunc,
      usetableFunc
    });

    const dataRefs = toRefs(reactiveData);
    const methods = {
      loadTableData():void {
        const simpleTable = failSimpleTableRef.value as any;
        simpleTable.loadSimpleTableData();
      },

      onSelectStoreType(): void{ methods.loadTableData(); }}; onMounted(() = > {
      methods.loadTableData();
    });

    return {
      ...dataRefs,
      ...methods,
      failSimpleTableRef
    };
  }
})
Copy the code

The above code is how vue3. X + TS is used, and we can see some similarities and differences. While props is still used to accept arguments passed by the parent component, setup has some different things like the names of the lifecycle functions, props and Reactive and toRefs as row parameters, and we’ll look at them one by one.

Setup

In the latest official documentation, the attr attribute has been removed and only the props and context parameters remain. As a side note, this is not a reference to the active instance inside setup because setup() is called before the other component options are resolved, so this inside setup() behaves completely differently than this in the other options. This makes setup() confusing when used with other optional apis. This. XXX can be used as the props parameter to access the props data. Setup looks like this in the 3.2.6 source code

setup? :(this: void, props: Readonly<LooseRequired<Props & UnionToIntersection<ExtractOptionProp<Mixin>> & UnionToIntersection<ExtractOptionProp<Extends>>>>, ctx: SetupContext<E>) = > Promise<RawBindings> | RawBindings | RenderFunction | void;
Copy the code

In the code above we have the first parameter props, and the second parameter context. Context is a normal JavaScript object that contains the following values

// Attribute (non-responsive object, equivalent to $attrs)
console.log(context.attrs)

// slots (non-responsive object, equivalent to $slots)
console.log(context.slots)

// Trigger event (method, equivalent to $emit)
console.log(context.emit)

// Expose public property (function)
console.log(context.expose)
Copy the code

Since they are just plain JS objects and not reactive data, you can use a deconstruction method on them

setup(props, { attrs, slots, emit, expose }){... } orsetup(props, content) {
   const { attrs, slots, emit, expose } = content
 }
Copy the code

Note here that attrs and slots are stateful objects that are always updated as the component itself is updated. This means you should avoid deconstructing them and always refer to properties as attrs.x or slots.x. Note that, unlike props, attrs and slots properties are non-reactive. If you are going to apply side effects based on changes to attrs or slots, you should do this in the onBeforeUpdate lifecycle hook.

Three, on demand introduction

After we look at the type structure of setup, we should know what this setup(props) code means. The following is

const { detailQueryParams } = toRefs(props);
Copy the code

Because our props sometimes needed to keep a data responsive, using ES6’s deconstruction method would make it unresponsive, so toRefs came into being. ToRefs, Reactive is vue3’s new responsive API. More responsive apis can be found in the official documentation

The official definition of toRefs is to convert a reactive object to a normal object, where each property of the resulting object is a ref to the corresponding property of the original object, so we allow the consuming component to deconstruct/expand the returned object without losing responsiveness. And vue3 now introduces the corresponding API according to the needs of the business scenario, which greatly optimizes the performance of the page.

Single file component

In Vue3. X the single file Component (SFC) is presented in two ways.

  • Plain Script: Its default export should be a Vue component option object, which is either a plain object or a defineComponent return value, as in the above code
  • Script setup form:
    1. Each *.vue file can contain up to one script setup block at a time (excluding regular script tags)
    2. The script is preprocessed and used as a setup() function for the component, meaning that it is executed in each component instance. The top-level bindings of Script Setup are automatically exposed to the template

This means that when using a single file setup, you don’t need defineComponent and return values to achieve a more concise code volume

<script lang="ts" setup>
import { ref, withDefaults, defineProps, defineEmits } from "vue";
import { IselectCommon } from "/@/model/app-modules";
import { IEditDepartmentsType } from "/@/model/system/api";
import { ElForm } from "element-plus";
import { useMessage } from "/@/hooks/common";


// The props interface is removed, but an error will be reported if the interface is defined in the props component
// https://github.com/vuejs/vue-next/issues/4294 How to import interface for defineProps #4294
interface IOrganizetionOptionProp {
  drawerVisiable: boolean;
  departmentTypes: IselectCommon[];
  recordItem: Iobj | null;
}

const privateProps = withDefaults(defineProps<IOrganizetionOptionProp>(), {
  drawerVisiable: false.departmentTypes: () = > {
    const list: IselectCommon[] = [];
    returnlist; }});const privateEmit = defineEmits<{ (e: "update:drawerVisiable", drawerVisiable): void; (e: "closeOption") :void} > ();const { createMessage } = useMessage();
const organizationFormRef = ref<typeof ElForm>();

const optionForm = ref<IEditDepartmentsType>({
  id: 0.type: "".typeName: ""
});

const organizationFormRuls = {
  isBranch: [{require: true.message: "Please select a store.".trigger: "change"}].type: [{require: true.message: "Please select department type".trigger: "change"}};function onChangeDepartment(value: string) :void {
  const departmentItem = privateProps.departmentTypes.find((item) = >item.value === value); optionForm.value.typeName = departmentItem? .labelas string;
}

function onCancelForm() {
  privateEmit("update:drawerVisiable", !privateProps.drawerVisiable);
}
function onSubmitForm() {
  const organizationForm = organizationFormRef.value as any;
  const isValidForm = organizationForm.validate();
  if(isValidForm) { optionForm.value.id = privateProps.recordItem? .id; setOrganizationType(optionForm.value).then((res) = > {
      const { code } = res;
      if(! code) { createMessage.success("Modified successfully");
        privateEmit("update:drawerVisiable", !privateProps.drawerVisiable);
        privateEmit("closeOption"); }}); } } </script><template>
  <div>
    <el-form :model="optionForm" ref="organizationFormRef" :rules="organizationFormRuls">
      <el-form-item label="Department Type" prop="type">
        <el-select clearable v-model="optionForm.type" @change="onChangeDepartment" class="w100">
          <el-option
            v-for="item in privateProps.departmentTypes"
            :key="item.id"
            :label="item.label"
            :value="item.value"
          ></el-option>
        </el-select>
      </el-form-item>
    </el-form>
    <div class="form-btn">
      <el-button size="mini" @click="onCancelForm">cancel</el-button>
      <el-button size="mini" type="primary" @click="onSubmitForm">save</el-button>
    </div>
  </div>
</template>
<style lang="scss" scoped>
.form-btn {
  text-align: right;
}
</style>

Copy the code

DefineProps, defineEmits, and withDefaults syntaxes were added to vue3.2.x to serve script setup, And the defineProps and defineEmits apis must be used to declare props and emits

Js when using

const props = defineProps({
  foo: String
})

const emit = defineEmits(['change'.'delete'])
// setup code
Copy the code

Ts when using

withDefaults(defineProps<{ size? :numberlabels? :string[]} > (), {size: 3.labels: () = > ['default label']})// PropType can also be used with TS for interface type management
const failTableProps = defineProps({
 // Time range
 rangeDateTime: {
   type: Array as PropType<string[]>,
   required: true}});const emit = defineEmits<{
   (event: 'change') :void
   (event: 'update'.id: number) :void
 }>()

 emit('change')
 emit('update'.1)
Copy the code

Note:

  • DefineProps and defineEmits are compiler macros that can only be used in Script Setup. They do not need to be imported and are compiled along with the Script Setup process.
  • DefineProps receives the same value as the props option, and defineEmits receives the same value as the emits option.
  • DefineProps and defineEmits provide appropriate type inference when the option is passed in.
  • The options passed to defineProps and defineEmits are promoted from setup to the scope of the module. Therefore, the passed option cannot refer to local variables declared in the setup scope. Doing so will cause a compilation error. However, it can refer to imported bindings because they are also module scoped.

Some other setup calls

<script setup>
import { useSlots, useAttrs } from 'vue'

const slots = useSlots()
const attrs = useAttrs()
</script>
Copy the code

UseSlots and useAttrs are real runtime functions that return values equivalent to setupContext.slots and setupContext.attrs

vue-router
<script setup>
import {useRoute,useRouter} from "vue-router";

$route = useRoute(); $route = useRoute()
const privateRoute = useRoute();
const privateRouter = useRouter();
</script>
Copy the code
  1. UseRoute: returns the current route address. Equivalent to using $route in the template
  2. UseRouter: Returns the router instance. Equivalent to using $router in the template