introduce

A project using vite + vue3 + Pinia + ant-design-Vue + typescript complete technical roadmap development, Second development update start, new VUe3 Composition API combined with setup to enjoy silky development experience, new Pinia state manager and excellent design experience (1K size), ANTD frictionless transition using UI component library Ant-Design-Vue, safe and efficient Typescript type support, code specification validation, and multiple levels of permission management

preface

A few days ago, I received a demand, which was to extract the main function module and user module permission system of an original project to make a new background project, and add some new functions iteratively. It seemed nothing

Get the source code to see the project, and boy, the original project is a small application project, the main application of the user module is the react technology stack, application module is vue2 technology stack, the CV solution is evidently not 👀 directly, I want to do, after all, is a single page application, determine a technical route, and ran to see the specific look at the code logic

Running up and trying, the two projects are basically about 1 minute to start, look at the code vUE project the whole business logic code is twisted together to write

Think of the time before asking the boss to source, said that is an old project, re-build a write should be faster

This is not wrong ah, words don’t say, directly open the whole, this time directly on vite + vue3

features

  • ✨ Scaffolding tools: Efficient, fast Vite
  • 🔥 front-end framework: Vue3 is currently the most fashionable
  • 🍍 Status manager:vue3rookiePiniaIs likereact zustandLike experience, friendly API and asynchronous processing
  • 🏆 development language: politically correct TypeScript
  • 🎉 UI components:antdDevelopers accessible transition useant-design-vueA familiar recipe a familiar taste
  • 🎨 CSS styles:lesspostcss
  • 📖 code specification: Eslint, Prettier, Commitlint
  • 🔒 Permission management: page level, menu level, button level, and interface level
  • ✊ dependency load on demand:unplugin-auto-import, can be automatically imported to usevue,vue-routerSuch dependence
  • 💪 Component import on demand:unplugin-vue-components, both third-party UI components and custom components can be automatically imported on demand as wellTSGrammar tip

Project directory

├ ─ ─. Husky// husky Git hooks configuration directory├ ─ ─ _// Directory file generated by the husky script├ ─ ─ a commit - MSG// commit- MSG hook to validate message format├ ─ ─ the pre - commit// Pre-commit hooks, mainly used with ESLint├ ─ ─ the config// Global configuration file├ ─ ─ vite// Vite configuration├ ─ ─ constant. Ts// Project configuration├ ─ ─ themeConfig. Ts// Theme configuration├ ─ ─ dist// Default build output directory├ ─ ─ the mock// Front-end data mock├ ─ ─ the public// Static directory under vite project└ ─ ─ the SRC// Source directory├ ─ ─ API// Interface correlation├ ─ ─ assets// Public files (image, CSS, font, etc.)├ ─ ─ the components// Project components├ ─ ─ directives// Custom instruction├ ─ ─ enums// Create a custom constant.├ ─ ─ hooks// Custom hooks├ ─ ─ layout// Global layout├ ─ ─ the router/ / routing├ ─ ─ store// State manager├ ─ ─ utils/ / tool library├ ─ ─ views// Page module directory├ ─ ─ login// login page module├ ─ ─... ├ ─ ─ App. Vue// Vue top-level file├ ─ ─ auto - imports. Which s// unplugin-auto-import plugin is generated├ ─ ─ components. D.D.T s// unplugin-vue-Components plugin is generated├ ─ ─ the main ts// Project entry file├ ─ ─ shimes - vue. Which s// Vite defaults to ts files├ ─ ─ types// project type type definition folder├ ─ ─ editorconfig// IDE format specification├ ─ ─ the env// Environment variables├ ─ ─ eslintignore/ / eslint ignored├ ─ ─ eslintrc// eslint configuration files├ ─ ─ gitignore/ / git is ignored├ ─ ─ NPMRC// NPM configuration file├ ─ ─ prettierignore/ / prettierc ignored├ ─ ─ prettierrc// prettierc configuration file├ ─ ─ index. HTML// Import file├ ─ ─ LICENSE. The md// LICENSE├ ─ ─ package. Json// package├ ─ ─ PNPM - lock. Yaml// pnpm-lock├ ─ ─ postcss. Config. Js// postcss├ ─ ─ the README, md// README├ ─ ─ tsconfig. Json// typescript configuration files└ ─ ─ vite. Config. Ts// vite
Copy the code

The development of

Project initialization

If you are using the VScode editor to develop vue3, be sure to install the Volar plugin to work with vue3 (not compatible with the original Vetur).

Use the Vite CLI to quickly create projects

yarn create vite project-name --template vue-ts

Installation-dependent dependencies

The new generation of PNPM package management tools are recommended for excellent performance and speed as well as node_modules dependency management

NPMRC configuration is recommended

Promote some dependency packages to node_modules
# Fix some package modules not found
# Used with PNPM
shamefully-hoist = true

# Node-sass download problem
# sass_binary_site="https://npm.taobao.org/mirrors/node-sass/"
Copy the code

Code specification

Tools: Husky, esLint, Prettier

Specific use of the way, a lot of online, I before another article also said, here no longer repeat ~

a Vite2 + Typescript + React + Antd + Less + Eslint + Prettier + Precommit template

It’s basically the idea of automation, doing the required things at the right time

  • Combined with the VsCode editor (automatic formatting when saving:editor.formatOnSave: true)
  • Execute with Git hooks before commitpre-commit= >npm run lint:lint-staged)

Note:

The installation of commitLint may be invalid on different systems

# Install commitlint cli and conventional config
npm install --save-dev @commitlint/{config-conventional,cli}
# For Windows:
npm install --save-dev @commitlint/config-conventional @commitlint/cli
Copy the code

function

Vue capability support

Template syntax with JSX syntax, very easy to use, flexible ~

Some necessary plug-ins

{
    // "@vitejs/plugin-legacy": "^1.6.2", // compatible with older browsers
    "@vitejs/plugin-vue": "^ 1.9.3." "./ / the vue support
    "@vitejs/plugin-vue-jsx": "^ 1.2.0"./ / JSX support
}
Copy the code

State manager Pinia

React Zustand is a new vUE state manager that will look familiar to those who have used React Zustand

Pinia is a wrapper around the Vue 3 Composition API. Therefore, you don’t have to initialize it as a plug-in, unless you need Vue DevTools support, SSR support, and webPack code splitting

  • Very lightweight, just 1 KB
  • Intuitive API use, intuitive, and easy to learn
  • Modular design for easy separation of states
  • Full TS support
/ /... Introducing dependent dependencies

interface IUserInfoProps{
  name: string;
  avatar: string;
  mobile: number;
  auths: string[]}interface UserState {
  userInfo: Nullable<IUserInfoProps>;
}

/ / create a store
export const useUserStore = defineStore({
  id: 'app-user'.// Unique ID that can be used with Vue devTools
  state: (a) :UserState= > ({
    // userInfo
    userInfo: null,}).getters: {
    getUserInfo(): Nullable<IUserInfoProps> {
      return this.userInfo || null; }},actions: {
    setUserInfo(info: Nullable<IUserInfoProps>) {
      this.userInfo = info ?? null;
    },
    resetState() {
      this.userInfo = null;
    },

    / * * *@description: fetchUserInfo
     */
    async fetchUserInfo(params: ReqParams) {
      const res = await fetchApi.userInfo(params);
      if (res) {
        this.setUserInfo(res); }}},})Copy the code

Use in components

// TS type inference, asynchronous function use is convenient
import { useHomeStore } from '/@/store/modules/home';

const store = useHomeStore();
const userInfo = computed(() = > store.getUserInfo);

onMounted(async() = > {await store.fetchInfo(); // Asynchronous functions
  // ...
});
Copy the code

UI components are loaded and automatically imported on demand

Understand the basics: Vite comes with load on demand (for JS), so here we focus on load on demand for styles

Solution 1: viet-plugin-style-import

import styleImport from 'vite-plugin-style-import'

// 
plugins:[
  styleImport({
    libs: [{libraryName: 'ant-design-vue'.esModule: true.resolveStyle: (name) = > {
          return `ant-design-vue/es/${name}/style/index`},}]})]Copy the code

Solution 2: unplugin-vue-Components

The unplugin-vue-components plugin is recommended

The plugin only needs to add the corresponding AntDesignVueResolver to the vite plugin, and also supports automatic registration of custom components, which is very convenient

import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers';
import Components from 'unplugin-vue-components/vite';

// vite.config.ts plugins add the following configuration
export default defineConfig({
  plugins: [
    Components({
      resolvers: [
        AntDesignVueResolver(), // ant-design-vue
        // ElementPlusResolver(), // Element Plus
        // VantResolver(), // Vant]]}}))Copy the code

If you don’t have the Resolver loader for your UI framework, you can customize it

Components({
  resolvers: [
    // example of importing Vant
    (name) = > {
      // where `name` is always CapitalCase
      if (name.startsWith('Van'))
        return { importName: name.slice(3), path: 'vant'}}}])Copy the code

Another powerful feature: the plug-in not only supports on-demand import of UI framework components, but also supports automatic on-demand import of project components

Specific performance is: if we use ant-design-Vue Card component or our own defined components/Icon and other components, we do not need to import, directly use, plug-in will automatically import for us as needed, combined with TS syntax tips, development efficiency bar ~

The configuration is as follows:

Components({
  // allow auto load markdown components under `./src/components/`
  extensions: ['vue'].// allow auto import and register components
  include: [/\.vue$/./\.vue\? vue/].dts: 'src/components.d.ts',})Copy the code

You need to add the components. D. ts file in the SRC directory to work with it. This file will be automatically updated by the plug-in

  • components.d.tsrole

The direct effect is: generate corresponding. D. type files under the project for syntax hints and type detection

  • Pay attention to

"Unplugin - vue - components", "^ 0.17.2"

Known issues of the current version: Issues 174

For the Notification/message component of Ant-Design-Vue, the plug-in does not perform automatic import capability (styles are not imported) when used in JS

The end result: message.success(‘xx’) can create DOM elements, but there is no style code

Because the plug-in is designed to be handled based on the use of components in the Vue Template template, the plug-in cannot be queried when invoked functionally

Solution:

  • To switch tovite-plugin-style-importThe plug-in
  • Manually introduce the message component style globally,import 'ant-design-vue/es/message/style'
  • Add it manually in the template of the VUE component<a-message />Used for plug-in index dependencies

Dependency automatic import on demand

  • unplugin-auto-import

Vue related defineComponent, computed, watch and other module dependencies are imported automatically based on usage. You don’t need to worry about import, just use it

By default, the plugin supports:

  • vue
  • vue-router
  • vue-i18n
  • @vueuse/head
  • @vueuse/core
  • .

You can also customize the unplugin-auto-import configuration

Usage:

import AutoImport from 'unplugin-auto-import/vite'

export default defineConfig({
  // ...
  plugins: [
    AutoImport({
      imports: [
        'vue'.'vue-router'.'vue-i18n'.'@vueuse/head'.'@vueuse/core',].dts: 'src/auto-imports.d.ts',})]})Copy the code

You need to add the auto-imports. D. ts file in the SRC directory. This file will be automatically updated by the plug-in

The final effect is:

Import {ref} from ‘vue’ import {ref} from ‘vue’

Custom themes

You can customize the theme by referring to the official document configuration

  1. Load fit on demandwebpack/viteLoader property modifies variables
  2. Full introduction, fitvariables.lessCustom styles override the frame theme styles

Here we use the first method through the loader configuration with on demand loading food

In the vite project, manually install less. PNPM add less -d

css: {
  preprocessorOptions: {
    less: {
      modifyVars: { 'primary-color': 'red' },
      javascriptEnabled: true.// This is a must}},}Copy the code

ImportStyle: ‘less’, unplugin-vue-components issues 160

AntDesignVueResolver({ importStyle: 'less' }) // This is very important
Copy the code

The mock data

  • Vite plugin – mock plug-in

Vite plugin configuration

viteMockServe({
  ignore: / ^ \ _ /,
  mockPath: 'mock'.localEnabled: true.prodEnabled: false.// The development environment is not a concern
  InjectCode is only affected by prodEnabled
  // https://github.com/anncwb/vite-plugin-mock/issues/9
  // The following code is injected into main.ts
  injectCode: ` import { setupProdMockServer } from '.. /mock/_createProductionServer'; setupProdMockServer(); `,})Copy the code

Root directory to create _createProductionServer. Ts file

import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer';

// Batch load
const modules = import.meta.globEager('./**/*.ts');

const mockModules: any[] = [];
Object.keys(modules).forEach((key) = > {
  if (key.includes('/ _')) {
    return; } mockModules.push(... modules[key].default); });/** * Used in a production environment. Need to manually import all modules */
export function setupProdMockServer() {
  createProdMockServer(mockModules);
}
Copy the code

All files in the mock directory that do not start with _ will be automatically loaded as mock files

Such as:

import Mock from 'mockjs';

const data = Mock.mock({
  'items|30': [{id: '@id'.title: '@sentence(10, 20)'.account: '@phone'.true_name: '@name'.created_at: '@datetime'.role_name: '@name',}]});export default[{url: '/table/list'.method: 'get'.response: () = > {
      const items = data.items;
      return {
        code: 0.result: {
          total: items.length,
          list: items, }, }; }},];Copy the code

Configure the proxy to request/API /table/list directly to get data

The Proxy agent

import proxy from './config/vite/proxy';

export default defineConfig({
  // server
  server: {
    hmr: { overlay: false }, // Set server.hmr.overlay to false to disable the server error mask layer
    // Service configuration
    port: VITE_PORT, // Type: number Specifies the server port.
    open: false./ / type: Boolean | string on server startup automatically open in a browser application;
    cors: false./ / type: Boolean | CorsOptions CORS for development server configuration. Any source is enabled and allowed by default
    host: '0.0.0.0'.// Enable access from IP
    proxy,
  },
})
Copy the code

The proxy is as follows

import {
  API_BASE_URL,
  API_TARGET_URL,
} from '.. /.. /config/constant';
import { ProxyOptions } from 'vite';

type ProxyTargetList = Record<string, ProxyOptions>;

const ret: ProxyTargetList = {
  // test
  [API_BASE_URL]: {
    target: API_TARGET_URL,
    changeOrigin: true.rewrite: (path) = > path.replace(new RegExp(` ^${API_BASE_URL}`), ' '),},// mock
  // [MOCK_API_BASE_URL]: {
  // target: MOCK_API_TARGET_URL,
  // changeOrigin: true,
  // rewrite: (path) => path.replace(new RegExp(`^${MOCK_API_BASE_URL}`), '/api'),
  // },
};

export default ret;
Copy the code

Environment variable.env

I have put the system configuration into config/constant.ts

To facilitate the management of interfaces and parameter configurations for different environments, you can use the environment variables.env,.env.local,.env.development,.env.production

It is very convenient to use with the Dotenv library

Package dependency analysis visualization

Plug-in: rollup – plugin – visualizer

import visualizer from 'rollup-plugin-visualizer';

visualizer({
  filename: './node_modules/.cache/visualizer/stats.html'.open: true.gzipSize: true.brotliSize: true,})Copy the code

Code compression

Plug-in: vite – plugin – compression will

import compressPlugin from 'vite-plugin-compression';

compressPlugin({
  ext: '.gz'.deleteOriginFile: false,})Copy the code

The Chunk unpacking

If you want to separate package dependencies such as Ant-Design-Vue, you can also manually configure the manualChunks property

// vite.config.ts
build: {
  rollupOptions: {
    output: {
      manualChunks: configManualChunk
    }
  }
}
Copy the code
// optimizer.ts
const vendorLibs: { match: string[]; output: string }[] = [
  {
    match: ['ant-design-vue'].output: 'antdv'}, {match: ['echarts'].output: 'echarts',},];export const configManualChunk = (id: string) = > {
  if (/[\\/]node_modules[\\/]/.test(id)) {
    const matchItem = vendorLibs.find((item) = > {
      const reg = new RegExp(`[\\/]node_modules[\\/]_? (${item.match.join('|')}`) (. *).'ig');
      return reg.test(id);
    });
    return matchItem ? matchItem.output : null; }};Copy the code

Compatible with the processing

Plug-in: @ vitejs/plugin – legacy

Compatible with browsers that do not support

// Native ESM
legacy({
  targets: ['defaults'.'not IE 11']})// IE11
/ / need regenerator - the runtime
legacy({
  targets: ['ie >= 11'].additionalLegacyPolyfills: ['regenerator-runtime/runtime']})Copy the code

rendering

Home page


Package dependency analysis visualization, partial screenshot


Open compression, open compatibility after the production of packaged products

Routing and Layout

// router/index.ts
import { createRouter, createWebHashHistory } from 'vue-router'
import routes from './router.config'

const router = createRouter({
  history: createWebHashHistory(), //
  routes,
})

// main.ts
app.use(router); 
      
Copy the code

Usage:

// router.config.ts
import BasicLayout from '/@/layouts/BasicLayout/index.vue'; // Basic layout
import BlankLayout from '/@/layouts/BlankLayout.vue'; / / null layout
import type { RouteRecordRaw } from 'vue-router';

const routerMap: RouteRecordRaw[] = [
  {
    path: '/app'.name: 'index'.component: BasicLayout,
    redirect: '/app/home'.meta: { title: 'home' },
    children: [{path: '/app/home'.component: () = > import('/@/views/home/index.vue'),
        name: 'home'.meta: {
          title: 'home'.icon: 'liulanqi'.auth: ['home',}}, {path: '/app/others'.name: 'others'.component: BlankLayout,
        redirect: '/app/others/about'.meta: {
          title: 'Other Menus'.icon: 'xitongrizhi'.auth: ['others'],},children: [{path: '/app/others/about'.name: 'about'.component: () = > import('/@/views/others/about/index.vue'),
            meta: { title: 'about'.keepAlive: true.hiddenWrap: true}, {},path: '/app/others/antdv'.name: 'antdv'.component: () = > import('/@/views/others/antdv/index.vue'),
            meta: { title: 'components'.keepAlive: true.breadcrumb: true},},],},]}... ]Copy the code

permissions

  • Supports page – and menu-level permission management and route management
  • Button level permission management is supported
  • Supports interface-level permission management

Several key words: router. AddRoutes dynamic routing, V-Auth instruction, AXIOS interception

userouter.beforeEachGlobal routing hook

The core logic is as follows, detailed in the repository code router/ Permission.ts

// No data is obtained
await permissioStore.fetchAuths();
// Filter permission routes
const routes = await permissioStore.buildRoutesAction();
// 404 route must be placed after permission route
routes.forEach((route) = > {
  router.addRoute(route);
});
/ / hack method
// Next () is not used because router.addroute
// The route that is not added to the original routing table will be No match
// replace Re-enters the route and matches itnext({ ... to,replace: true });
Copy the code

usev-authCommand control button level permissions

function isAuth(el: Element, binding: any) {
  const { hasPermission } = usePermission();

  const value = binding.value;
  if(! value)return;
  if(! hasPermission(value)) { el.parentNode? .removeChild(el); }}Copy the code

axiosintercept

Added to the AXIos request interceptor interceptors.request.use

// Interface permission interception
const store = usePermissioStoreWithOut();
const { url = ' ' } = config;
if(! WhiteList.includes(url) && store.getIsAdmin ===0) {
  if(! store.getAuths.includes(url)) {return Promise.reject('No operation permission'); }}Copy the code

conclusion

At the beginning of using Vite + vue3, it was also a process of learning and developing while trekking on the pit. Fortunately, now the community is more active and there are corresponding solutions for many problems, which can be used together with the documents and Github issue. The project also referred to some implementation and code management of VUe-Vben-admin. This article is used as a vuE3 learning record ~

Vue3 also supports THE JSX syntax. React is also possible. However, when used with vue templates, vue3 has a very different development experience from VUe2. Also has great flexibility, you can customize your own code according to the scene, in combination with the current script setup development, directly cool to take off

When using vue3’s Composition API development mode, it is necessary to abandon the previous development logic of options API, and the hooks and configurations can freely combine and split the code, which is extremely flexible and convenient for maintenance and management, so that the whole code in vuE2 era will not be tied together again

Vite + vue3 + setup + TS + VScode Volar plug-in, who uses who knows, cool batch ~

Warehouse address: github.com/JS-banana/v…

reference

  • vue3
  • Pinia
  • Vue Router
  • vue-vben-admin
  • A simple Vue button level permission scheme
  • Hand to hand, take you to use vUE masturbation background series two (login permission)
  • Separate the front end from the front end