“This is the 14th day of my participation in the First Challenge 2022.

Last time I published an 8-night 2-3 hour refactoring of Vuecli’s template project – Nuggets (Juejin.cn) in Vite, the initial project was created using the createVue scaffolding base project structure, how to build the base project if we didn’t use it, let’s take a look.

This project uses PNPM package manager, nodeJS version V17.2.0

Initialize the Vite project

  • Executing script commands
pnpm create vite
Copy the code
  • Create project name
  • Select Create vUE project
  • Select vuE-TS project
✔ Project name:... vue-project ? Select a framework: Use arrow-keys. Return to submit. Vanilla ❯ vue react preact lit svelte? Select a variant: › -use arrow-keys. Return to submit. Vue ❯ vue-tsCopy the code
  • A basic project based on vite+ Vue + TS is complete, so let’s start configuring our project template

directory

  • √ Configure IP access items
  • √ Configure multiple environment variables
  • Square root integration Tsx
  • √ Configure the alias
  • √ Sass global style
  • √ Identify the built-in NodeJS module
  • √ Static resource usage
  • Tick the Vue – the router
  • √ Pinia Status management
  • √ Eslint + Prettier Unified development specification
  • √ HusKY + Lint-Staged submitted validations
  • √ Use Mock data

✅ Configure IP address access items

  • “Network: Use –host to Expose” appears after Vite boots
vite v23.7. dev server running at:

  > Local: http://localhost:3000/
  > Network: use `--host` to expose
Copy the code
  • The IP address is not configured, so the server cannot be started from the IP address. You need to configure the server to 0.0.0.0 in viet.config. js
export default defineConfig({
  plugins: [vue()],
  // Add the following content to the file
  server: {
    host: '0.0.0.0'}})Copy the code
  • Displayed after the restart
vite v23.7. dev server running at:

  > Local:    http://localhost:3000/
  > Network:  http:/ / 192.168.199.127:3000 /
Copy the code

✅ Configure multiple environment variables

  • Documents: cn. Vitejs. Dev/guide/env – a…
  • In a production environment, the value of import.meta.env is converted to the corresponding value
  1. Add environment variable files, each file writes configuration, define env environment variables must be preceded by VITE_
  • .env.development
# must start with VITE_
VITE_ENV = 'development'
VITE_OUTPUT_DIR = 'dev'
Copy the code
  • .env.production
# must start with VITE_
VITE_ENV = 'production'
VITE_OUTPUT_DIR = 'dist'
Copy the code
  • .env.test
# must start with VITE_
VITE_ENV = 'test'
VITE_OUTPUT_DIR = 'test'
Copy the code
  1. Modify scripts command
  • --modeTo identify our environment
"dev": "vite --mode development"."test": "vite --mode test"."prod": "vite --mode production".Copy the code
  1. Access in the project
console.log(import.meta.env)
Copy the code
  1. Typescript smart tips
  • Modify thesrc/env.d.tsFile if one is not created
/// <reference types="vite/client" />

interface ImportMetaEnv extends Readonly<Record<string, string>> {
  readonly VITE_ENV: string; / / environment
  readonly VITE_OUTPUT_DIR: string; // Package directory
}
interface ImportMeta {
  readonly env: ImportMetaEnv;
}
Copy the code

✅ integration Tsx

  • Documents: cn. Vitejs. Dev/guide/featu…
  1. Install dependencies
pnpm i -D @vitejs/plugin-vue-jsx
Copy the code
  1. Modify the vite. Config. ts configuration
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    vueJsx({
      include: /\.(jsx|tsx)/})].server: {
    host: '0.0.0.0'}})Copy the code

✅ Configure the alias

  • Documents: cn. Vitejs. Dev/config / # res…
  • Modify the vite. Config. ts configuration
  resolve: {
    alias: {
      "@": "/src",}},Copy the code

✅ Sass global style

  • Documents: cn. Vitejs. Dev/guide/featu…
  1. Install dependent usedart-sass, the installation speed is relatively fast, and there is a high probability that the installation will not fail
pnpm i -D sass
Copy the code
  1. Each page’s own style is written in its own.vue filescopedIt adds the concept of a domain to CSS as the name suggests.
<style lang="scss">
  /* global styles */
</style>

<style lang="scss" scoped>
  /* local styles */
</style>
Copy the code

css modules

  • Currently, tests can only be used in TSX, vue-template can be imported and used in JS,<template>Don’t know how to use it yet
  • To define a*.module.scssor*.module.cssfile
  • Used in TSX
import { defineComponent } from 'vue'
import classes from '@/styles/test.module.scss'
export default defineComponent({
  setup() {
    console.log(classes)
    return () = > {
      return <div class={`rootThe ${classes.moduleClass} `} >Test the CSS - modules</div>}}})Copy the code

Vite recognizes sASS global variables

  • Documents: cn. Vitejs. Dev/config / # CSS…
  • Vite.config.js adds the configuration
css: {
    preprocessorOptions: {
      scss: {
        additionalData: ` @import "@/styles/mixin.scss"; @import "@/styles/variables.scss"; `,}}},Copy the code

✅ Identifies the nodeJS built-in module

  • The Path module is a built-in feature of Node.js, but Node.js does not support typescript itself, so using it directly in typescript projects is not an option
  • Solution: Install @types/ Node
pnpn i -D @types/node
Copy the code
  • It is used in viet.config.js
import { resolve } from 'path'
Copy the code

✅ Static resource usage

  • Documents: cn. Vitejs. Dev/guide/featu…
// staticTest.vue
import img from '@ / assets/images/pictures. JPG' // Returns the image resource path
import demo from './demo.tsx? url' // Load the resource explicitly as a URL
import test from '@/test/test? raw' // Load resources as strings
import Worker from '@/test/worker? worker' // If the code is computation-heavy, you can use worker to start a new thread loading and communicate with the main thread
import jsonText from '@/test/jsonText.json' // Read the json file
console.log('Static image --', img)
console.log('Explicitly load resource URL --', demo)
console.log('Load the resource as a string --'.` typeThe ${typeof test}`, test)
console.log('read the json -', jsonText)

const worker = new Worker()
worker.onmessage = function (e) {
  console.log('the worker to monitor -, e)
}
Copy the code

Dynamic import picture

  • Documents: cn. Vitejs. Dev/guide/asset…

  • Reference link: juejin.cn/post/703069…

  • Problems with new URL and import.meta. URL

    • import.meta.urlGet the full URL of the current page
    • new URLMust be filled inRelative paths
    • Packaging does not support Chinese path, not solved for now[vite:asset-import-meta-url] ENOENT: no such file or directory, open '/Users/zhangyong/code/oneself/template/vite-vue3-h5-template/src/assets/images/png/\u5E74\u7EC8\u603B\u7ED3.png'
// src/components/HelloWorld.vue
new URL('.. /assets/images/png/year.png'.import.meta.url).href
/ / import. Meta. Urls to get to the address: http://192.168.124.4:3000/src/components/HelloWorld.vue? t=1641037446646
After mosaics of address: / / http://192.168.124.4:3000/src/assets/images/png/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93.png
Copy the code

✅ Vue – router4

  • Documents: next.router.vuejs.org/zh/installa…
  • Composition – API used: next.router.vuejs.org/zh/guide/ad…

1. Install dependencies

pnpm install vue-router@4
Copy the code

2. Configure the routing API

  • In the SRC directory, create a Router folder and create it in the folder
    • Index. ts Management routing API
    • Router.config. ts Manages routing information
// router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
import { routes } from './router.config'

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

export default router
Copy the code
// router/router.config.ts
import { RouteRecordRaw, createRouter, createWebHistory } from 'vue-router'

export const routes: Array<RouteRecordRaw> = [
  {
    path: '/'.name: 'Home'.redirect: '/home'.meta: {
      title: 'home'.keepAlive: false
    },
    component: import('@/views/layouts/index.vue'),
    children: [{path: '/home'.name: 'Home'.component: import('@/views/home.vue'),
        meta: { title: 'home'.keepAlive: false.showTab: true}}]}]Copy the code

3. Import the router in mian

import { createApp } from 'vue'
import router from './router'
import App from './App.vue'
// Introduce global styles
import '@/styles/index.scss'

const app = createApp(App)
app.use(router)
app.mount('#app')
Copy the code

4. App. vue and layout configure router-view

// app.vue
<script setup lang="ts">
// This starter template is using Vue 3 <script setup> SFCs
// Check out https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup
console.log('View global environment'.import.meta.env);
</script>

<template>
  <router-view />
</template>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
Copy the code
// layouts/index.vue
<script setup lang='ts'>
import { useRoute } from 'vue-router';

    const route = useRoute()
    console.log(route.meta);

</script>
<template>
    <div class="layout-content">
        <keep-alive v-if="route.meta.keepAlive">
            <router-view></router-view>
        </keep-alive>
        <router-view v-else></router-view>
    </div>
</template>
<style lang='scss' scoped>

</style>
Copy the code

✅ Pinia Status management

  • Documents: pinia.vuejs.org/
  • Reference: juejin.cn/post/704919…
  • Pinia features:
    • Full typescript support;
    • Lightweight enough, compressed volume is only 1.6KB;
    • Remove mutations, only state, getters, actions (one of my favorite features);
    • Actions support synchronous and asynchronous;
    • There is no module nesting, only the concept of stores, stores can be used freely, better code segmentation;
    • There is no need to manually add stores, stores are automatically added once created;

Install dependencies

pnpm i pinia
Copy the code

Create the Store

  • Create a SRC /store directory and create index.ts under it to export store
// src/store/index.ts

import { createPinia } from 'pinia'

const store = createPinia()

export default store
Copy the code

Introduced and used in main.ts

// src/main.ts

import { createApp } from 'vue'
import App from './App.vue'
import store from './store'

const app = createApp(App)
app.use(store)
Copy the code

Define the State

  • Create a user.ts under SRC /store
//src/store/user.ts

import { defineStore } from 'pinia'
import { useAppStore } from './app'

export const useUserStore = defineStore({
  id: 'user'.state: () = > {
    return {
      name: 'Joe'.age: 18}},getters: {
    fullName: (state) = > {
      return state.name + 'company'}},actions: {
    updateState(data: any) {
      this.$state = data
      this.updateAppConfig()
    },
    updateAppConfig() {
      const appStore = useAppStore()
      appStore.setData('app-update')}}})Copy the code
//src/store/app.ts
import { defineStore } from 'pinia'

export const useAppStore = defineStore({
  id: 'app'.state: () = > {
    return {
      config: 'app'}},actions: {
    setData(data: any) {
      console.log(data)
      this.config = data
    }
  }
})
Copy the code

Get/update State

<script setup lang="ts"> import { useUserStore } from '@/store/user' import { useAppStore } from '@/store/app' import { storeToRefs } from 'pinia' import { computed } from 'vue' const userStore = useUserStore() const appStore = useAppStore() console.log(appStore.config) console.log(userStore) console.log(userStore.name) const name = computed(() => userStore.name) const { age } = storeToRefs(userStore) const updateUserState = () => { const { name, age } = userStore.$state userStore.updateState({ name: name + 1, age: The age + 1})} < / script > < template > < div > name: {{name}} < / div > < div > age: {{age}} < / div > < div > computing's name: {{userstore. fullName}}</div> <div>app config: </div> <button @click="updateUserState"> </button> </template> <style lang=" SCSS" scoped></style>Copy the code

Data persistence

  • Documents: github.com/prazdevs/pi…
  • Pinia-plugin-persistedstate can assist in data persistence.

  • Data is stored in sessionStorage by default, and the store ID is used as the key.

  • Install dependencies

pnpm i pinia-plugin-persistedstate
Copy the code
  • The plug-in
// src/store/index.ts

import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const store = createPinia()
store.use(piniaPluginPersistedstate)
export default store
Copy the code
  • Simply enable Persist in the corresponding store
export const useUserStore = defineStore({
  id: 'user'.state: () = > {
    return {
      name: 'Joe'}},// Enable data caching
  persist: {
    key: 'user'.storage: sessionStorage, // Data storage location, localStorage by default
    paths: ['name'].// Array of dot notation paths for partial persistent state, indicating that no state will be persisted (default and keep the entire state)
    overwrite: true}})Copy the code

✅ Eslint + Prettier Unified development specification

1. Install dependencies

pnpm i -D eslint eslint-plugin-vue prettier @vue/eslint-config-prettier @vue/eslint-config-typescript @rushstack/eslint-patch
Copy the code

2. Write relevant documents

  • .eslintrc.js
/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')

module.exports = {
  root: true.extends: [
    'plugin:vue/vue3-essential'.'eslint:recommended'.'@vue/eslint-config-typescript/recommended'.'@vue/eslint-config-prettier'].env: {
    'vue/setup-compiler-macros': true
  },
  parserOptions: {
    ecmaVersion: 12
  },
  rules: {
    'prettier/prettier': 'warn'.'@typescript-eslint/no-explicit-any': 'off'.'@typescript-eslint/no-unused-vars': 'off'}}Copy the code
  • .prettierc.js
module.exports = {
  // Customize the formatting requirements
  overrides: [{files: '.prettierrc'.options: {
        parser: 'json'}}].printWidth: 100.// A line of up to 100 characters
  tabWidth: 2.// Use 4 Spaces for indentation
  semi: false.// A semicolon is required at the end of the line
  singleQuote: true.// Use single quotes instead of double quotes
  useTabs: false.// Use tabs instead of space indentations
  quoteProps: 'as-needed'.// Add quotes around object attributes only when needed
  jsxSingleQuote: false.// Use single quotes instead of double quotes in JSX
  trailingComma: 'none'.// Do not need a comma at the end
  bracketSpacing: true.// Spaces are required at the beginning and end of braces
  bracketSameLine: false.// Backangle brackets for multi-line HTML (HTML, JSX, Vue, Angular) elements require line breaks
  arrowParens: 'always'.// The arrow function, which has only one argument, also needs the parentheses avoid
  rangeStart: 0.// Each file format ranges from beginning to end
  rangeEnd: Infinity.// The range in which each file is formatted is the entire content of the file
  requirePragma: false.// There is no need to write @prettier at the beginning of the file
  insertPragma: false.// There is no need to automatically insert @prettier at the beginning of a file
  proseWrap: 'preserve'.// Use the default line folding standard always
  htmlWhitespaceSensitivity: 'css'.// Depending on the display style, HTML should be folded or not
  vueIndentScriptAndStyle: false.// (default) For.vue files, do not indent 
  endOfLine: 'lf'.// The newline character uses LF in Linux and macOS as well as git repositories
  embeddedLanguageFormatting: 'auto' // (default) allows automatic formatting of embedded code blocks
}
Copy the code
  • .vscode/settings.json
{
    "editor.formatOnSave": false.// Automatically format each time you save
    "editor.formatOnPaste": true.// Automatically format pasted content
    "editor.tabCompletion": "on".// TAB auto-complete
    "editor.codeActionsOnSave": { // Save using ESLint fixes fixes errors
        "source.fixAll": true."source.fixAll.eslint": true.// Save using ESLint fixes fixes errors
        // "source.fixAll.stylelint": true
    },
    // File Settings
    "files.eol": "\n".// The default end-of-line character. Git config --global core. Trigger lf false
    / / eslint Settings
    "eslint.alwaysShowStatus": true.// always display ESLint status in VSCode
    "eslint.probe": [ // EsLint validates the language type - new version
        "javascript"."javascriptreact"."typescript"."typescriptreact"."html"."vue"."markdown"."tsx"],}Copy the code

✅ husky + Lint-staged validation submission

1. Install dependencies

pnpm i -D husky lint-staged
Copy the code

2. Add script commands

npm set-script prepare "husky install"  // Add the "prepare": "husky install" command to package.json/scripts. This command is available only on Linux /uinx operating systems
npm run prepare  // Initialize husky, pass the git hooks to husky to execute, and create the.husky folder in the root directory
npx husky add .husky/pre-commit "npx lint-staged" // pre-commit Execute NPX Lint-staged directives
Copy the code

3. Create. Lintstagedrc. Json

{
  "**/*.{js,ts,tsx,jsx,vue,scss,css}": [
    "prettier --write \"src/**/*.ts\" \"src/**/*.vue\""."eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix"]}Copy the code

✅ Use Mock data

  • Documents: github.com/vbenjs/vite…
  • Mock data is currently tested and is valid in both XHR and FETCH in the development environment. In the production environment, it can only be invoked using the XHR type request library, and fetch does not take effect

1. Install dependencies

PNPM i-d viet-plugin-mock # If you do not use mockJS, there is no need to install the mockJS related dependency PNPM I mockjs PNPM i-d@types /mockjsCopy the code

2. Production environment-related packaging

// mock/_createProductionServer.ts
import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer'

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
// mock/_util.ts
// Interface data format used to return a unified format

import { Recordable } from 'vite-plugin-mock'

export function resultSuccess<T = Recordable> (result: T, { message = 'ok' } = {}) {
  return {
    code: 0,
    result,
    message,
    type: 'success'}}export function resultPageSuccess<T = any> (
  page: number,
  pageSize: number,
  list: T[],
  { message = 'ok' } = {}
) {
  const pageData = pagination(page, pageSize, list)

  return {
    ...resultSuccess({
      items: pageData,
      total: list.length
    }),
    message
  }
}

export function resultError(message = 'Request failed', { code = -1, result = null } = {}) {
  return {
    code,
    result,
    message,
    type: 'error'}}export function pagination<T = any> (pageNo: number, pageSize: number, array: T[]) :T[] {
  const offset = (pageNo - 1) * Number(pageSize)
  const ret =
    offset + Number(pageSize) >= array.length
      ? array.slice(offset, array.length)
      : array.slice(offset, offset + Number(pageSize))
  return ret
}

export interface requestParams {
  method: string
  body: anyheaders? : { authorization? :string }
  query: any
}

/ * * *@description This function is used to obtain tokens from the request data. Please modify * */ according to the actual situation of the project
export function getRequestToken({ headers }: requestParams) :string | undefined {
  returnheaders? .authorization }Copy the code
// mock/sys/user
import { MockMethod } from 'vite-plugin-mock'
import { resultError, resultSuccess, getRequestToken, requestParams } from '.. /_util'

export default[{url: '/basic-api/getUserInfo'.method: 'get'.response: (request: requestParams) = > {
      console.log('---- requested getUserInfo-- ')

      return resultSuccess({
        name: 'chapter three'.age: 40.sex: 'male'}}})as MockMethod[]
Copy the code

3. Modify the configuration of vite.config.ts

export default ({ mode, command }: ConfigEnv): UserConfigExport= > {
  const isBuild = command === 'build'
  return defineConfig({
    plugins: [
      viteMockServe({
        ignore: / / ^ _.// Re matches ignored files
        mockPath: 'mock'.// Set the mock. Ts file storage folder
        localEnabled: true.// Set whether to enable the local xxx.ts file, do not open it in production. Setting it to false disables the mock feature
        prodEnabled: true.// Set whether the mock function is enabled in the production environment
        watchFiles: true.// Sets whether to monitor changes in files in the folder corresponding to mockPath
        // Code injection
        injectCode: ` import { setupProdMockServer } from '.. /mock/_createProductionServer'; setupProdMockServer(); `})]})}Copy the code
  • In this way, we have completed a basic project template, no matter we want to transform it into H5 project or PC project.
  • Github address: ynzy/ Vite-vue3-template: 🎉 Basic template for Vite2 + Vue3.2 + TypeScript + Pinia + Mock + sass (github.com)