This is the third day of my participation in the More text Challenge. For details, see more text Challenge

preface

Since the release of the official version of Vue3, the project began to use Vue3+TS+Vite mode, Vite fast is really let people can no longer give up, no longer need to change a line of code waiting for half a day, this TM is really too happy, development happiness index directly dry full. (a. ^ – ^ -)

The following is a record of the recent development of the Vue3+TS+Vite model, and a summary here. If you have any other strange problems, please feel free to comment in the comments section.

Tips/questions summary

Volar plug-in

Recommend a VSCode plug-in, Volar is an official plug-in for the creation of Vue, in the fourth VueConf in yu Yu Creek has made a recommendation.

Lucky for those who use VSCode, it’s only around 21000 downloads now, but I think it will increase in the future. Because it is really very powerful, especially in Vue3 and some of Vue’s new RFC has a good fit and timely update.

The installation method is very simple, directly in vsCode plug-in market search Volar, and then click install can be, the specific use of their own experience slowly.

The TS type specification for props is provided in Vue3

It is very important to specify the data types required in a component, so that the component can be maintainable. Let’s try to specify the three common cases of objects, arrays, and functions.

Writing a

When using Vue3+TS to perform complex type validation for props, you can cast it directly with the PropType attribute provided by Vue:

Vue <script lang='ts'> import {defineComponent, PropType} from 'vue' interface IGoods {id: number; goodsName: string; } interface IFun {(): IGoods} export default defineComponent({props: {// object goods: {required: true, type: : // Array list: {required: true, type: Object as PropType<IGoods[]>}, // function getInfo: { required: true, type: Function as PropType<IFun> } } }) </script>Copy the code

Write two

It can also be written as a function:

Import {defineComponent} from 'vue' interface IGoods {id: number; goodsName: string; } interface IFun { (): IGoods } export default defineComponent({ props: { goods: { required: true, type: Object as () => IGoods }, list: { required: true, type: Array as () => IGoods[] }, getInfo: { required: true, type: Function as unknown as () => IFun } } }) </script>Copy the code

How do I mount global methods in Vue3

In the Vue2 project, we can often see scenes like this:

$dateFormat = () => {console.log(' date conversion method ')}; $dateFormat(); $dateFormat();Copy the code

Almost all Vue2 projects will have some global variables and methods directly mounted on the Vue prototype, which makes it easier to use this call. But there is no this in Vue3!! There is no this in Vue3!! There is no this in Vue3!! (Say important things three times)

globalProperties

Although Vue3 is compatible with Vue2, we can still use this, but only in the original Option API, not in the new Composition API. Vue3 is not recommended to work on the prototype, but in order to smooth the transition for users who want to upgrade Vue2 to Vue3, yuxi has also introduced the globalProperties solution instead of vue. prototype.

How to mount global methods:

// main.ts import { createApp } from 'vue' import App from './App.vue' const app = createApp(App) App. Config. GlobalProperties $dateFormat = () = > {the console. The log (' date conversion method ')}; app.mount('#app')Copy the code

Specific use:

<script lang="ts"> import {defineComponent, onMounted, getCurrentInstance} from 'vue' export default defineComponent({ // Options API mounted() { this.$dateFormat() }, // Composition API setup() { const { proxy } = getCurrentInstance()! ; onMounted(() => { proxy.$dateFormat() }) return {} } }) </script>Copy the code

The above shows how the Option API and Composition API get global methods, respectively.

The getCurrentInstance() method is used to access the internal component instance, which has many methods attached to it. We can access the global methods mounted to it through its proxy property. There is also an alternative way to access globalProperties:

const { $dateFormat } = getCurrentInstance()! .appContext.config.globalProperties; $dateFormat();Copy the code

One thing to note here is that I’ve seen a lot of articles on the web that access something mounted on globalProperties in the form const {CTX} = getCurrentInstance(); ctx.$dateFormat(); However, this method can only be used in the development environment. CTX in the production environment will not have access to globalProperties. Yes, an error will be reported. Uncaught TypeError: CTX.$dateFormat is not a function Uncaught TypeError: CTX.$dateFormat is not a function

It is not recommended to use the globalProperties method as an alternative to the vue. prototype method in the Composition API. Check out this VUE/RFCS for details.

While Vue3 does not recommend how to mount some variables or methods, it does recommend using the provide() and inject() forms of dependency injection.

provide()/inject()

Bind dependencies using project() :

import { createApp } from 'vue' import App from './App.vue' const app = createApp(App) app.provide('$dateFormat', () = > {the console. The log (' date conversion method ')}) app. Mount (' # app)Copy the code

Use inject() to obtain:

<script lang="ts">
import {defineComponent, inject} from 'vue'
export default defineComponent({
  setup() {
    const $dateFormat: (() => void) | undefined = inject('$dateFormat')
    $dateFormat && $dateFormat()
    
    return {}
  }
})
</script>
Copy the code

Provide /inject is simple to use, but it is important to note that both can only be invoked during setup() of the current active instance. Am I

A plug-in for dynamic HTML content in Vite

In the project of building a use vue – cli, the default provide us with % > < % = htmlWebpackPlugin. The options. The title in the form of a dynamic insert content as HTML. The process is to use the HtmlWebpackPlugin provided by WebPack to replace the template variable with the actual title value when compiling.

In Vite, it’s very easy to do this, and we can easily do it with the help of v-plugin-html.

Installation:

npm install vite-plugin-html -D
Copy the code

Configuration:

// vite.config.ts import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import { injectHtml } from 'ite-plugin-html' export default defineConfig({plugins: [vue(), injectHtml({injectData: {title:' user management system '}}),})Copy the code

In particular, the dynamic variable syntax is much simpler:

// index.html <! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" href="/favicon.ico" /> <meta name="viewport" content="width=device-width, Initial scale = 1.0 "/ > < title > < % = title % > < / title > < / head > < body > < div id =" app "> < / div > < script type =" module" src="/src/main.ts"></script> </body> </html>Copy the code

For more configuration details, check out the ite-plugin-html document. (a. ^ – ^ -)

Problem with configuring project alias “@” in Vite

Project alias configuration is now an essential part of the project, because the project uses Vite to build, Vite documentation also describes the alias configuration, we first according to the document to configure.

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'

export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      '@': resolve(__dirname, 'src')
    }
  }
})
Copy the code

Simple, isn’t it? (^ ^) omega

Let’s assume that we introduce a CSS file and a TS file:

Uh… ????? The red! But the imported style works, and the methods in the imported TS file can be called as normal.

Because esLint is using esLint, we suspect that esLint does not recognize the project alias, so we should modify its configuration. There are a number of ways to resolve esLint’s failure to recognize project aliases, so let’s go ahead and select the most violent one and turn off its associated ESLint rules:

// .eslintrc.js module.exports = { env: { browser: true, es2021: true }, extends: ['plugin:vue/essential', 'airbnb-base', 'plugin:prettier/recommended'], parserOptions: { ecmaVersion: 12, parser: '@typescript-eslint/parser', sourceType: 'module' }, plugins: ['vue', '@typescript-eslint'], rules: {'import/no-unresolved': 'off', 'import/ no-extranillion-dependencies ': 'off', 'import/ no-extranillion-dependencies ': 'off', 'import/ no-extranillion-dependencies ': 'off'}}Copy the code

After modifying esLint’s configuration, we can see that the CSS files that were introduced are no longer reported in red, but the TS files are still reported in red. Why?

This should not be an ESLint problem, but probably a visual studio code editor review syntax problem. The VSCode syntax check and TSC compilation depend on the tsconfig.json file configuration, so let’s configure the TS alias again.

// tsconfig.json { "compilerOptions": { "target": "esnext", "module": "esnext", "moduleResolution": "node", "strict": true, "jsx": "preserve", "sourceMap": true, "resolveJsonModule": true, "esModuleInterop": true, "lib": [" esnext ", "dom"], "paths" : {" @ / * ": [". / SRC / *"], / / configuration ts alias}}, "include" : ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] }Copy the code

Tsconfig. json: tsconfig.json: tsconfig.json: tsconfig.json: tsconfig.json: tsconfig.json: tsconfig.json: tsconfig.json (a. ^ – ^ -)

CSS variables are introduced globally in Vite

Less variable

The installation

npm install less less-loader -D
Copy the code

configuration

// vite.config.ts import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({  plugins: [vue()], css: { preprocessorOptions: { less: { modifyVars: { hack: `true; @import (reference) "${resolve('src/assets/lessVar.less')}"; ` }, javascriptEnabled: true } } } })Copy the code

The specific use

// lessVar.less
@primary: #43AFFF;
Copy the code
<style lang="less" scoped> h1 {color: @primary; } </style>Copy the code

Scss variable

The installation

NPM install sass sass-loader [email protected] -dCopy the code

configuration

// vite.config.ts import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({  plugins: [vue()], css: { preprocessorOptions: { scss: { additionalData: '@import "src/assets/scssVar.scss"; '}}}})Copy the code

The specific use

// scssVar.less
$primary: #43AFFF;
Copy the code
<style lang=" SCSS "scoped> h1 {color: $primary; } </style>Copy the code

Stepping on two pits while using SCSS:

inViteThe use ofscssMandatory downloadsass, or keep reporting errors.

I use thewin7System, fornode-sassDependencies can only be installed for versions below 5, mainly becausenode-sassnodeVersion has requirements, requirementsnode15Above, butnodeIt has not been supported since around 14win7Is installed.

Changes to the rewrite attribute of the proxy function in Vite

Configuring a custom proxy for the development server is also an old practice. Vite also provides a server.proxy to configure the proxy, which uses http-proxy as the underlying layer just like WebPack.

Many times when using WebPack we might do the following configuration:

Proxy: {'/ API ': {target: 'proxy service address ', secure: true, // Configure HTTPS changeOrigin: PathRewrite: {// ignore the prefix, that is, do not add/API layer '^/ API ': "}}}Copy the code

However, in a project built with Vite, this configuration is incorrect!!

The pathRewrite attribute is now renamed to rewrite and accepts a function form, correctly configured:

// vite.config.ts export default defineConfig({ plugins: [vue()], server: { proxy: { '/api': { target: Rewrite: (path) => path.replace(/^\/ API /, ")}}},})Copy the code

Environment variables in Vite

When using vue-cli, we often use the environment variable to determine the environment in which the program is running:

const BASE_URL = process.env.NODE_ENV === 'production' ? 'http://production.com' : 'http://development.com';
Copy the code

ViteIt also provides its ownThe environment variableWell, XDM learns and learns.

Vite uses import.meta. Env to access related environment variables. Let’s print import.meta first:

You can see that it contains a lot of things, but let’s focus on the env property, which has some environment variables built in:

  • import.meta.env.BASE_URL: The value is string. It is the basic URL of the application deploymentThe base configuration itemsDecision.
  • import.meta.env.DEV: Boolean Specifies whether the application is running in the development environment (always withimport.meta.env.PRODOn the contrary).
  • import.meta.env.MODE: String. The mode in which the application is run. It is made up ofMode configuration itemsDecision.
  • import.meta.env.PROD: Boolean Indicates whether the application is running in the production environment.
  • import.meta.env.SSR: Boolean type, whether it is a SSR application,For more details.

Note that in a production environment, these environment variables are statically replaced at build time, so use completely static strings when referencing them. Dynamic keys cannot take effect. For example, the dynamic key value import.meta. Env [key] is invalid.

How do I create additional environment variables

We can load additional variables by creating configuration files in different modes. For example, we create.env.development and.env.production files in the project root directory.

//.env.development VITE_TITLE = 'orange someone-development environment' OTHER_TITLE = 'Other names-development environment'Copy the code
//.env.production VITE_TITLE = 'OTHER_TITLE =' other name - production environment 'Copy the code

The file content is as above, print import. Meta to check, and remember to restart.

We can see that we’ve already read these extra variables, since we started the project with the command NPM run dev, so we’ll be reading the contents of the.env.development file. If you run NPM run build, the.env.production file will be read.

In addition, Vite protects itself from accidentally leaking environment variables to the client by exposing only variables prefixed with vite_to Vite processed code.

Of course, we can not only create files for the development and Production modes. If you configure other modes, such as test mode, you can also create.env.test files to load additional variables.

Env.[mode] # Loads only in the specified mode, but is ignored by gitCopy the code

Vite uses globEager instead of require.context

In our vue-cli project, webpack provides us with require.context(), which makes it easy to import all the files in a folder. But this method is provided by Webpack, in Vite naturally can not be used, but do not worry, Vite for us to provide Glob mode module import, also can achieve the file import function.

Let’s take a look at the difference between the two to import all the files under the @/components/ file and register them as global components.

require.context

// main.js
const allCom = require.context('@/components/', true, /\.vue/);

allCom.keys().forEach(key => {
    const fullName = key.substr(key.lastIndexOf('/') + 1)
    const comName = fullName.split('.')[0].toUpperCase()
    Vue.component(comName, allCom(key).default || allCom(key))
})
Copy the code

globEager

// main.ts const allCom = import.meta.globEager('./components/*.vue') Object.keys(allCom).forEach((key) => { const files  = key.substr(key.lastIndexOf('/') + 1) const name = files.split('.')[0].toUpperCase() app.component(name, allCom[key].default) })Copy the code

The Glob pattern is treated as an import identifier: must be a relative path (starting with a./) or an absolute path (starting with a /, resolved relative to the project root), project aliases cannot be used

GlobEager is recommended instead of import.meta. Glob because it is lazy loading by default. And split into separate chunks at build time.

Common Eslint configurations

Eslint is a grinding goblin, loved and hated at the same time. While it makes us happy about the cleanliness and uniformity of the code it brings, it also makes us trapped in its clutches.

But a lot of times we choose it, for no other reason than, well, to have fun.

Remove the WARN alert for console

Use the Console method to print anything with a yellow line, ah, uncomfortable. (T_T)

Solution:

// .eslintrc.js module.exports = { ... Rules: {// the development environment does not review console' no-console': process.env.node_env === 'production'? 'warn' : 'off' } }Copy the code

Increment (‘++’) and decrement (‘–‘) symbols are allowed

In my opinion, it is very useful to increase and decrease the value. Many suggestions on the Internet can be written in the form of number. Value += 1.

Solution:

// .eslintrc.js module.exports = { ... Rules: {/ / allows you to use the increase since decreases symbols' no - plusplus: [' off '{allowForLoopAfterthoughts: true}]}}Copy the code

Remove the warn hint from alert()

Sometimes for convenience to directly use the system prompt box, this is also prompted yellow line.

Solution:

// .eslintrc.js module.exports = { ... Rules: {// call warn 'no-alert': 0}}Copy the code

Allows reassignment to function arguments

This is how I encapsulate the entire Axios from the previous part – separate API management, parameter serialization, cancel repeat request, Loading, status code… A problem with the article in TS form.

The general problem is that BY default, ESLint does not allow any further assignment to function arguments, otherwise it will send a red alert.

Doing assignments to variables in function arguments can be misleading, confusing, and can change arguments objects. This is a bit of a risky operation, but sometimes I just want to change (T_T). As I mentioned in the article above, I encapsulated a method to cancel repeated requests:

For convenience, I just want to change the cancelToken property in this addPending method. But reported red prompt, then is there any way to remove this prompt? The answer is yes, we need to configure the no-param-reassign rule.

Solution:

// .eslintrc.js module.exports = { ... Rules: {/ / allows modifying function into the parameter 'no - param - reassign: [' error' {props: true, ignorePropertyModificationsFor: [ 'config', ] } ], } }Copy the code

After configuration, the config is not submitted to the red, if more of the other parameters to configure, can continue to add the ignorePropertyModificationsFor attribute.

New component in ElementPlus – SelectV2 – virtual list selector

While I was writing this article, I happened to find a new component on the ElementPlus website. Here is a recommendation for you to update the latest version of the library.

The TS type of Loading component is used in ElementPlus

When we use TS to encapsulate ElementPlus components, it is easier to use its type.

<template> <div> < button@click ="clickEvent"> </button> </div> </template> <script lang="ts"> import {defineComponent } from 'vue' import { ElLoading } from 'element-plus' import { ILoadingOptions } from 'element-plus/lib/el-loading/src/loading.type' import 'element-plus/lib/theme-chalk/index.css' export default DefineComponent ({setup() {function openLoading(loadingOptions? : ILoadingOptions) { ElLoading.service(loadingOptions) } function clickEvent() { openLoading({ fullscreen: true }) } return { clickEvent } } }) </script>Copy the code

The type specification of other components is also imported by the corresponding type found in the source code.

Indexable types in TS

This is a feature that might be overlooked, but it does the same thing: you can use it to describe types that can be “indexed”, such as a[10] or ageMap[” Daniel “]. The document

This feature comes from seeing other members of the project write code that looks like this:

Interface Goods {goodsName: string} let Goods: Goods = {goodsName: '1'} Goods = {goodsName: '1'} Goods = {goodsName: string} 'Goods no. 2 ', aliasGoodsName:' asGoods';Copy the code

The above code bypasses editor inspection by adding an aliasGoodsName property to the Goods object through the AS assertion statement, but the Goods interface is not described, which can sometimes be confusing to other project members because the Goods object is specified using the Goods interface. All the properties should be clear, there should be no uncertainty.

The correct way to solve this problem is to use the optional attribute:

interface Goods { goodsName: string aliasGoodsName? : string} let goods: goods = {goodsName: 'goodsName'} goods = {goodsName: 'goodsName'};Copy the code

Optional attributes are good for this situation, but adding individual attributes is fine. If the uncertainty of the canonical object is very high, what’s ten? Twenty (in the extreme)? If we continue to use this approach, we will have to change the Goods interface every time, which will become troublesome.

Laziness is an important motivation to stimulate potential. Is there a way to do it once and for all? The answer, of course, is:

interface Goods { goodsName: string [proName: string]: any } let goods: Goods = { goodsName: } goods = {goodsName: 'goodname ', price: 100} goods = {goodsName:' goodname ', price: 100}Copy the code

With indexable type properties, we are able to implement arbitrary additions to the canonical type properties without having to worry about them anymore.

Uh, but!! Any is used here and did you suddenly feel that the entire interface type is a virtual diagram, not very meaningful?

Hahaha, I’m telling you…………….. That’s an illusion. Don’t worry about the details. Our first goal is to write code that runs, not code that doesn’t work.

I’ll share some of the temporary memories of Vue3+TS+Vite2+ElementPlus+Eslint project practices, but if there are others, I’ll add them later in this article. (= ^ del ^ =)

At this point, this article is finished, flower flower.

I hope this article has been helpful to you and look forward to your comments if you have any questions. Same old, like + comment = you got it, favorites = you got it.