preface

It’s been nearly a year since Vue3 was first released, Vite has gone from 1.0 to 2.x, and the surrounding ecosystem has matured enough that it’s time to use new technologies to optimize the development experience, hence this article.

Initialize the project

Initiate the project through the official scaffolding

Enter the project name, select the template, and select whether to support TS
npm init vite@latest

You can also specify fast build directly
npm init vite@latest json-cms --template vue-ts
Copy the code

After the execution, enter the project directory and run NPM run dev to start the project in seconds

cd json-cms

npm install
npm run dev
Copy the code

Looking at the project structure, the generated directory structure is not enough to support the complexity of the project, so we extend the structure, the extended structure is shown on the right

Vite customized configuration

In the initializations of the project, viet.config. js only introduces plugins that provide support for Vue 3 single-file components. Some excellent viet-plugins are recommended, and see awesome-vite for more.

@vitejs/plugin-legacy

Traditional browser compatibility support for packaged files. Since Vite is based on the ESM mechanism supported by modern browsers, the file module is still ESM. If you want to support older browsers, you need to use @Vitejs/plugin-Legacy.

Installation and use

npm i -D @vitejs/plugin-legacy
Copy the code
// vite.config.ts
import { defineConfig } from 'vite'
import legacy from '@vitejs/plugin-legacy'

export default defineConfig({
  plugins: [
    legacy({
      targets: ['defaults'.'not IE 11'],}),],})Copy the code

vite-plugin-element-plus

Provide on-demand import capabilities for ElementPlus. The full import of ElementPlus results in a build package that is too large, and the introduction on demand effectively reduces the package size. The idea behind this package is to dynamically write CSS for each component imported on demand.

import { ElButton } from 'element-plus'↓ ↓ ↓ ↓ ↓import { ElButton } from 'element-plus'
import 'element-plus/es/components/button/style/css'
Copy the code

Installation and use

npm i -D vite-plugin-element-plus
Copy the code
// vite.config.ts
import { defineConfig } from 'vite'
import importElementPlus from 'vite-plugin-element-plus'

export default defineConfig({
  plugins: [
    // @ts-ignore uses ignore for the time being
    // The reason is that options inside the package are not optional compatible
    // We have already mentioned that PR is not merged, so we can wait and see
    importElementPlus(),
  ],
})
Copy the code

@vitejs/plugin-vue-jsx

Vue 3 JSX & TSX support (via a dedicated Babel conversion plug-in).

Installation and use

npm i -D @vitejs/plugin-vue-jsx
Copy the code
// vite.config.ts
import { defineConfig } from 'vite'
import vueJsx from '@vitejs/plugin-vue-jsx'

export default defineConfig({
  plugins: [
    vueJsx({
      // Options will be passed to @vue/babel-plugin-jsx})],})Copy the code

rollup-plugin-visualizer

Visualize and analyze build packages to see which modules take up space to optimize build package size. This is a Rollup plugin and is recommended as a feature of Vite. Vite already supports most Rollup plugins by default, so the vite plugin library is much richer.

Installation and use

npm i -D rollup-plugin-visualizer
Copy the code
// vite.config.ts
import { defineConfig } from 'vite'
import visualizer from 'rollup-plugin-visualizer'

export default defineConfig({
  plugins: [visualizer()],
})
Copy the code

vite-plugin-html

Index. HTML extends dynamic capabilities, provides compression and EJS template functions, dynamic injection, if you don’t know EJS, check it out.

Installation and use

npm i -D vite-plugin-html
Copy the code
// vite.config.ts
import { defineConfig } from 'vite'
import html from 'vite-plugin-html'

// This is how to dynamically set the title and inject the JS path
export default defineConfig({
  plugins: [
    html({
      inject: {
        injectData: {
          title: 'JSON CMS'.tinymce: '/js/tinymce/tinymce.min.js',},},}),],})Copy the code

Before compiling

<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>
  <script src="<%- tinymce %>" rel="preload"></script>
</head>
Copy the code

The compiled

<head>
  <meta charset="UTF-8" />
  <link rel="icon" href="/favicon.ico" />
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
  <title>JSON CMS</title>
  <script src="/js/tinymce/tinymce.min.js" rel="preload"></script>
</head>
Copy the code

Standardization based on HusKY + Lint-Staged projects

Husky is a tool that makes Git hooks easier to use. Once installed, it will automatically add hooks to the.git/ directory in your repository, such as the pre-commit hook that fires when you perform a Git commit.

You can implement things like Lint checking, unit testing, and code beautification in pre-commit. Of course, make sure that the commands executed in the pre-commit phase are not too slow, and waiting too long for each commit is not a good experience.

Lint-staged, a tool for filtering out Git code staged files (files added by Git). This is useful because if we do a review of the entire project code, it can take a long time, but if we do an older project, we have to do a code specification review and modify the previous code, which can be troublesome and lead to big changes in the project.

Lint-staged is therefore a great tool for team projects and open source projects, which act as a specification and discipline for the code that individuals must commit.

Coding standards

Encoding specification via esLint Prittiter stylelint, fix.

Installation and use

npm i -D eslint prittiter stylelint eslint-config-prettier eslint-define-config eslint-plugin-prettier eslint-plugin-vue  vue-eslint-parser @typescript-eslint/eslint-plugin @typescript-eslint/parserCopy the code

Here are the ESLint and PritTier configuration files

// .eslintrc.js
const { defineConfig } = require('eslint-define-config')
module.exports = defineConfig({
  root: true.env: {
    browser: true.node: true.es6: true
  },
  parser: 'vue-eslint-parser'.parserOptions: {
    parser: '@typescript-eslint/parser'.ecmaVersion: 2020.sourceType: 'module'.jsxPragma: 'React'.ecmaFeatures: {
      jsx: true}},extends: [
    'plugin:vue/vue3-recommended'.'plugin:@typescript-eslint/recommended'.'prettier'
    'plugin:prettier/recommended'].rules: {
    // ...}})Copy the code
// prettier.config.js
module.exports = {
  printWidth: 100.tabWidth: 2.semi: false.singleQuote: true.bracketSpacing: true.trailingComma: 'none'.jsxBracketSameLine: false.jsxSingleQuote: false.arrowParens: 'always'.insertPragma: false.requirePragma: false.proseWrap: 'never'.htmlWhitespaceSensitivity: 'strict',}Copy the code

Given the relevant Lint configuration file, husky and Lint-staged configuration needs to be configured. Here is a quick configuration command

npx mrm@2 lint-staged
Copy the code

This command will configure husky and Lint-staged installations from eslint and prettier in the package.json dependency so that Git hooks are configured. A git commit will trigger lint validation, which will fix the offending file and add git to the staging area again to complete the commit.

{
  "scripts": {
    "prepare": "husky install"
  },
  "devDependencies": {
    // ...
    "eslint": "^ 7.32.0"."husky": "^ 7.0.2"."lint-staged": "^ 11.1.2." "."prettier": "^ 2.4.0." "
    // ...
  },
  "lint-staged": {
    "*.js": "eslint --cache --fix"."*.{js,css,md}": "prettier --write"}}Copy the code

Code submission specification

Currently, the most widely used commits are Conventional commits, and coding platform commits are also in conventional commits. The validation of coding platform is usually triggered when a developer pushes, and a COMMIT has been executed. Therefore, we need to front-load the commit specification, validate it at COMMIT time and prompt for changes.

The above effects need to be implemented in Commitizen, which is installed globally or used as NPM script. Commitizen-friendly mode is recommended and initialized after global installation.

# global install
npm install -g commitizen

# Commitizen - friendly way
commitizen init cz-conventional-changelog --save-dev --save-exact
Copy the code
// package.json
{
  "scripts": {
    "commit": "cz"
  },
  "config": {
    "commitizen": {
      "path": "cz-conventional-changelog"}}}Copy the code

With the above configuration, you can replace the git commit command with git cz or NPM run commit.

However, if someone wants to use git commit to customize commit information, there is no way to do so. Therefore, add commitLint to check whether the commit information complies with the specification.

Installation and use process

npm i -D @commitlint/config-conventional @commitlint/cli

# Since husky is already installed, we just need to add another hook
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'
Copy the code

This enables checking of submitted information. In addition, you can set cZ-Customizable to a Submission prompt in Chinese.

Changelog is generated based on the version

Just because of the above “contract submission” specification, we can also generate the corresponding version of Changelog through Xconventional – Changelog – CLI.

{
  "scripts": {
    "log": "conventional-changelog -p angular -i CHANGELOG.md -s"}}Copy the code

Vue3 development ecology

routing

The first version 4.0 of VuE-Router4.x was released in December 20 and has been relatively stable since then. The usage of router4.x has not changed much since Vue2. You can compare the differences between the 3.x version and the official migration guide. For details, go to the migration Guide.

State management

New options for state management include PINA and VUex.

  • piniaIt is based on the vuEX 5.x proposal, arguably the future-oriented VUEX, which proposes a non-centralizedstoreManagement model, no nestingstoreModule, removedmutations, leavingstate,gettersactionsPersonally, I also prefer this model.
    • Vuex5. X
    • Proposal API Design
  • vuex4.xThe official version was released at the beginning of 21, as an official status management, presumably everyone is also familiar with, I will not introduce too much, the use of Vue2 has not changed much.
    • Migration guide

The UI component library

There is also a rich library of UI components to choose from

  • PC:
    • ElementUI, once suspected of being abandoned, is now called elementPlus
    • Early support for Vue3’s Ant Design Vue
    • From The Future in Tucson, a very Naive UI
  • M side:
    • Nutui from JD.com’s retail team
    • From the awesome Vant

Also useful CSS related tools

  • Tailwindcss is a tool that allows you to do things like layout without writing a single line of CSS.

  • Windcss is also the same type of CSS tool, compatible with tailwindCSS
  • Modern browser CSS styles reset modern-Normalize

Vue3 & Vite development notes

This section describes the problems encountered in real projects as a development note, relevant only to the composite API.

Vue3.2 is released, a new SFC feature

  • support<script setup>Syntactic sugar, in a composite API, does not require the values required by the templatereturnProcessing, make the code more concise.
  • support<style> v-bind Syntax sugar, can be directly to the value<style>More convenient to use.
<script setup>
  import { ref } from 'vue'

  const color = ref('red')
</script>

<template>
  <button @click="color = color === 'red' ? 'green' : 'red'">
    Color is: {{ color }}
  </button>
</template>

<style scoped>
  button {
    color: v-bind(color);
  }
</style>
Copy the code
  • The component’spropsemitWriting changes and type definitions in TypeScript.
/ / js writing
<script setup>
const props = defineProps({
  foo: String
})

const emit = defineEmits(['change'.'delete'])
// setup code
</script>
Copy the code
/ / ts
<script lang="ts" setup>
const props = defineProps<{
  foo: stringbar? :number} > ()const emit = defineEmits<{
  (e: 'change'.id: number) :void
  (e: 'update'.value: string) :void
}>()
</script>
Copy the code

Vue3 Abandoned filter and alternatives

As of Vue 3.0, filters have been removed and are no longer supported.

  • In 2.x, you can use filters to handle generic text formats.
<template>
  <p>Today is {{date | weeekFormat}}</p>
</template>

<script>
  export default {
    props: {
      date: {
        type: [Date | String | Number].required: true,}},filters: {
      /** * returns Friday ** /
      weeekFormat(value) {
        return new Intl.DateTimeFormat('zh-CN', { weekday: 'long' }).format(
          new Date(value)
        )
      },
    },
  }
</script>
Copy the code
  • In 3.x, you need to replace them with method calls or computed properties.
<template>
  <p>Today is {{date | weeekFormat}}</p>
</template>

<script>
  export default {
    props: {
      date: {
        type: [Date | String | Number].required: true,}},computed: {
      /** * returns Friday ** /
      weeekFormat() {
        return new Intl.DateTimeFormat('zh-CN', { weekday: 'long' }).format(
          new Date(this.date)
        )
      },
    },
  }
</script>
Copy the code

Global filters can also be implemented using globalProperties. A good practice is to implement a custom plugin in which global properties are defined.

// /plugin/filters.ts
import type { App } from 'vue'
import { timeFormat } from '@/utils'

export default {
  install: (app: App) = > {
    app.config.globalProperties.$filters = {
      format(value: string | Date | number) {
        return timeFormat(value)
      },
    }
  },
}
Copy the code
// main.ts
import { createApp } from 'vue'
import filters from '@/plugins/filters'

// The type definition here is required, otherwise the type will fail during build
declare module '@vue/runtime-core' {
  interface ComponentCustomProperties {
    $filters: Record<string.any>
  }
}

createApp(App).use(filters).mount('#app')
Copy the code

After the above configuration can support global use, on the type of problems can refer to the relevant PR and source code.

<el-table-column label="Creation time">
  <template #default="{ row }">
    {{ $filters.format(row.createTime) }}
  </template>
</el-table-column>
Copy the code

About global is not defined

Vite is an ESM system, and some packages use node’s global object internally. You can solve this problem by creating a pollfill and adding it to the top of main.ts.

// polyfills
if (typeof (window as any).global === 'undefined') {
  ;(window as any).global = window
}

// main.ts
import './polyfills'
import { createApp } from 'vue'
Copy the code

conclusion

At this point, the construction of the engineering foundation using Vue3 + Vite has been completed, and the subsequent in-depth improvement of efficiency and standards in the business has reached the purpose of engineering.