1. Vscode extends Volar

The editor tool I use is vscode, and vue3’s vscode extension is called volar, and volar and vetur do not coexist.

1.1. Install Volar

Type volar in the extensions store and it will come up with three extensions. My advice is to install them all. If you don’t use TS then the TypeScript Vue Plugin (Volar) doesn’t need to be installed.

1.2, configuration,

The Vue Volar Extention Pack extension has 12 dependency extensions that are not installed and ready to use

Open Settings (File -> Options -> Settings) and then we need to customize the following extensions

The most important configuration items are:

1.2.1. Automatic Formatting.vuefile

If this option is not checked, your.vue will not be automatically formatted!!

1.2.2 disable built-in TS support for vscode

In the extension store we type: @builtin typescript, then find typescript and JavaScript Language Features and disable it, as shown here:

Volar has built-in TS support, which means that vscode has two TS processes. This is not necessary. The most direct expression is that two TS processes will appear as follows:

After we install volar, every time we open vscode, there will be the following prompt box in the lower right corner, click to view the original text.

2, the import. Meta. Env

Environment variables and modes TS module enhancements

In Vite we can use import.meta.env. XXX directly to use the data we defined in the.env file.

If we need precise type hints for custom data in TS, the official way is to use the TS module enhancement in the env.d.ts file.

But it has the following two pits.

2.1. Declaration inenv.d.tsThe invalid

In this case, MY solution is to create another arbitrarily named.d.ts file and place the ImportMetaEnv definition in it.

In 2.2,vite.confing.tsCannot be used inimport.meta.env.xxx

Ts does not support import.meta. Env. There are many related articles on the Internet.

Yarn add dotenv -d yarn add dotenv -d

3. Some component styles are missing when ElementPlus is imported on demand

There are a lot of element-Plus components, but we only use some of them. We can manually import the components that we need, but this will increase the maintenance cost. The official also provides an automatic import solution.

According to the official document, I configured the project with automatic import on demand. When I used ElMessage or ElMessageBox, I found that the function was ok, but it had no style……

The original idea was to import element-Plus globally, but the styles are already there, but some components have two styles, which is a complete departure from the original intent of importing on demand or even importing all the components at once.

Then I went to the source code of the automatic import component unplugin-vue-components to see its import rules

According to the style introduction rule of unplugin-vue-components, we go to the node_modules/element-plus/theme-chalk/ directory of the project to find the corresponding component style and import it manually.

The problem was fixed, but it didn’t feel elegant enough, and at one point IT occurred to me that when I was reading the Elemental-Plus documentation it seemed to have a plugin, so I went there and found it in a corner!

Install and configure it according to its GitHub documentation, and you’re done. My understanding of unplugin-element-plus is that it is a complement to, not a substitute for, unplugin-vue-components. Don’t ask me why I use this phrase (T, T).

4, <script lang=”ts” setup>

In

<script lang="ts" setup>
  const props = defineProps({
    name: String.color: String,
  })
</script>
Copy the code

But when we encounter complex data types, we can get confused

<script lang="ts" setup>
  interface Account {
    name: string
    age: number
    sex: 'male' | 'woman'
  }
  const props = defineProps({
    account: Account    // Not supported. The value here only supports data types that exist in JavaScript
  })
</script>
Copy the code

<script lang="ts" setup>
  interface Account {
    name: string
    age: number
    sex: 'male' | 'woman'
  }
  const props = defineProps<{ account: Account }>()
</script>
Copy the code

This generic writing is not surprising, even the parameters do not need to pass!

5, defineComponent

5.1. Props statement

When we customize components, we usually use defineComponent for better syntax hints, as follows:

<script lang="ts">
  import { defineComponent, ref } from 'vue'

  export default defineComponent({
    name: 'Demo'.setup(props) {},
  })
</script>
Copy the code

But I’m also having props declare problems on defineComponent, which is probably the newbie’s sad o ﹏ o.

In the defineComponent configuration item setup function, its first argument is the specific value of props and is reactive. So we can’t deconstruct it directly, only using toRef or toRefs.

<script lang="ts">
  import { defineComponent, ref, toRef, toRefs } from 'vue'

  export default defineComponent({
    name: 'Demo'.// Setup ({name, color}) {}, // Error writing, this will make the props unresponsive
    setup(props) {
      const name = toRef(props, 'name')
      / / or
      const { name, color } = toRefs(props)
    },
  })
</script>
Copy the code

This is a minor detail that you can’t make if you read the document carefully. So let’s get to the point. Why did props fail?

My initial thought was that defineComponent supported generic writing as well as defineProps, so I went to the.d.ts file of vue and saw that it did

At this point, you’ll be too happy. Although the syntax hints for TS are already there, you’ll find console.log({… Props. Account}) is an empty object with no data…….

Then I looked at the source code for element-Plus to see how they wanted to write props

That’s right, enforce its TS type with type assertion using AS.

5.2, DOM ref

In vue2, we usually use this.$refs. XXX to call a registered component or native DOM that registers a ref attribute. However, vue3’s setup function is a bit tricky to get a component or DOM that registers ref attributes, especially in TS environments.

5.2.1 Basic usage of DOM Ref

What is marked in the image is important, and these details are not marked in the official documentation. One of the most confusing areas for beginners is the definition. Ref (null) is used exactly the same as a reactive API ref, which makes it easy to get confused.

The template references the reactive API ref

5.2.2 TS type of DOM Ref

In TS, the type of ref(null) is automatically inferred to be ref

, which is very bad for our subsequent coding, such as:

The only way to get around this problem is to use as for type assertions, as long as our syntax meets the three basic requirements of DOM Ref.

5.2.3 requires,ElForm.validateTStype

When we use element-Plus forms, we generally validate the form data before submission. With previous experience you might write code like this:

“Validate” and “valid” TS are both of type any. So now we need to cheat again, and what I do is I create a custom ElFormInst type declaration that takes all the methods and attributes that I need out of it. Then replace the Typeof ElForm.

6. Unlimited menu bar

I mention this because it involves recursive references to components, and official recursive component descriptions are rare.

Although the official said that support, but I did not succeed, either TS check but not render recursion effect, difficult to top!! You can teach me if you have achieved it

Although it was not possible to implement the infinite level menu bar through recursive components, it was eventually implemented in pure TS code by looking through the VUE documentation and the Element-Plus documentation

/** * infinite level side navigation bar */
import UIconfont from '@@/UIconfont.vue'
import { ElMenu, ElMenuItem, ElSubMenu } from 'element-plus'
import { defineComponent, h, VNode } from 'vue'
import { useRouter } from 'vue-router'

interface MenuItem {
  icon: string
  title: string
  router: stringchildren? : MenuItem[] }export default defineComponent({
  name: 'Menus'.setup() {
    const router = useRouter()

    function select(index: string) {
      router.push(index)
    }

    return { select }
  },
  render() {
    function common(menu: VuexSidebarItem) : () = >VNode[] {
      return () = > [
        h(UIconfont, { name: menu.icon, class: 'menu-icon' }), 
        h('span'.null, menu.title)
      ]
    }
    
    function create(menu: VuexSidebarItem) :VNode {
      if (menu.children) {
        return h(
          ElSubMenu, 
          { index: menu.router },  // index prop
          { 
            title: common(menu),   // title slot
            default: () = >menu.children! .map(create)// default slot or childrens})}return h(ElMenuItem, { index: menu.router }, common(menu))
    }
    
    const defaultOpeneds: string[] = this.$store.getters.assideOpends
    
    return h(
      ElMenu, 
      { 
        defaultOpeneds,         // default-openeds prop
        onSelect: this.select,  // @select
        style: 'border: none'.// style attribute
      }, 
      // store.state.aside: MenuItem[]
      () = > this.$store.state.aside.map(create)  // default slot or childrens)}})Copy the code

For more information, see the element-Plus source code.

7. Axios encapsulation

By default, axios returns this data when the request succeeds

interface AxiosResponse<T = any, D = any>  {
  data: T
  status: number
  statusText: string
  headers: AxiosResponseHeaders
  config: AxiosRequestConfig<D> request? :any
}
Copy the code

This is how it will be used

interface ResponseData<T> {
  code: string
  data: T
  msg: string
}

axios.get<ResponseData<string[] > > ('xxx/xxx').then((response) = > {
  const { code, data, msg } = response.data
  if (parseInt(code) ! = =1) {
    return alert(msg)
  }
  console.log(data)
})
Copy the code

We typically use axios’s response interceptor to process the response data, and your data structure might look something like this

interface AxiosResponse<T = any, D = any>  {
  code: number
  data: T
  message: string
  status: number
  statusText: string
  headers: AxiosResponseHeaders
  config: AxiosRequestConfig<D> request? :any
}
Copy the code

The way it’s going to be used is going to be

interface ResponseData {
  pageTotal: number
  pageSize: number
  pageNum: number
  content: string[]
}

axios.get<ResponseData>('xxx/xxx').then((response) = > {
  const { pageTotal, content } = response.data
})
Copy the code

If you use JS, you can encapsulate it directly, but if you use TS, you will find that the validation fails

In this case, we can use the previously mentioned TS module enhancement to handle this. It is worth noting that this time we can put the declaration in the env.d.ts file as follows:

import { AxiosInstance } from 'axios'

declare module 'axios' {
  interface AxiosResponse {
    code: number
    message: string}}Copy the code

Final preview

7, at the end

Novice novice use, hope you big guy correct the wrong place (‘ ω · ´)