This is my second day of the Gwen Challenge

preface

  • Vite asVue's grandfather, YouAnother work of god. Worthy of our use
  • This article mainly through vite + VUe3 + element-plus + TS to build a background management system shelf

1, install,

  • Build our first project with Vite scaffolding
Yarn create @vitejs/app my-vue-app --template vue-ts yarn create @vitejs/app my-vue-app --template vue-tsCopy the code
  • This string of commands allows us to generate a TS based project
  • The directory structure is as follows

2, integration,vue-router@next

  • Installation supportvue3The grammaticalvue-router
    yarn add vue-router@next
Copy the code
  • Next we create the routes folder in the SRC directory and create the index.ts file in the routes folder and write a test route
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'

const routes: RouteRecordRaw[] = [
    {
        path: '/login'.component: () = > import(".. /views/login/index.vue")}, {path: '/'.component: () = > import(".. /views/home/index.vue")},]const router = createRouter({
    history: createWebHistory(),
    routes
})

export default router
Copy the code
  • Now we have added the route for the login component and the home page component to the route file
  • Then we go to main.ts to mount the exported route to app and modify app.vue
    //main.ts
    import { createApp } from 'vue'
    // Import the route we exported
    import router from './routes'
    import App from './App.vue'

    createApp(App)
    // Mount routing information to app using the use method
        .use(router)
        .mount('#app')
        
    // app.vue
    <template>
      <router-view />
    </template>

Copy the code
  • Once you’ve done that, you can write the login.vue and home.vue test components
// login.vue
    <template>
        <div>login</div>
    </template>
 // home.vue
    <template>
        <div>home</div>
    </template>
Copy the code

  • We initiate project accesslocalhost:3000/loginIf you see the login field, it proves that the route has been successfully integrated, so we enter it directlylocalhost:3000You can go to the home page

3. Integration vuex@next

  • Installation supportvue3The grammaticalvuex
    yarn add vuex@next
Copy the code
  • Next we create a store folder in the SRC directory, create an index.ts file in the store folder, and write a test data
  • For example, 🌰, we implement an incremental feature that is displayed in the home component by clicking on the increment in the login component
import { createStore } from 'vuex'

const store = createStore({
    state() {
        return {
            count: 0}},mutations: {
        // add function
        increment(state) {
            state.count++
        }
    }
})

export default store
Copy the code

  • We will see the TS type error, so we can modify
  import { createStore } from 'vuex'

    // Define a state interface
    export interface State {
        count: number
    }

    const store = createStore<State>({
        state() {
            return {
                count: 0}},mutations: {
            // add function
            increment(state) {
                state.count++
            }
        }
    })
    export default store
Copy the code
  • Next we modify the main.ts
    import { createApp } from 'vue'
    // Import the route we exported
    import router from './routes'
    // Import the vuex we exported
    import store from './store'
    import App from './App.vue'

    createApp(App)
    // Mount routing information to app using the use method
        .use(router)
    // Mount vuEX information to vUE using the use method
        .use(store)
        .mount('#app')
Copy the code
  • Next we retrieve the contents of vuex in the Home component
    <template>
        <div>The count of vuex: {{count}}</div>
    </template>
    <script setup>
    import { useStore } from 'vuex'
    const store = useStore()
    const count = store.state.count
    </script>
Copy the code
  • Next, we set up the increment operation in the Login component
    <template>
      <div>
        login
        <button @click="handleClickIncrement">cumulative</button>
        <router-link to="/">Go to the home page</router-link>
      </div>
    </template>
    <script setup>
    import { useStore } from "vuex";
    const store = useStore();
    function handleClickIncrement() {
      store.commit("increment");
    }
    </script>
Copy the code
  • Finally, we started the project at 👁 to see what the renderings looked like

  • First, when we enter the home page, count is 0. Then, when we enter the login page, click autoincrement and then click to go to the home page. Then, count in the home page will realize the function of accumulation, changing from 0 to 1, realizing vuEX data sharing

4, integration,element-plus

  • The installation
    yarn add element-plus
Copy the code
  • Import mode – Complete import
import { createApp } from 'vue'
// Import the route we exported
import router from './routes'
// Import the vuex we exported
import store from './store'
// elementPlus is fully introduced
import ElementPlus from 'element-plus';
import 'element-plus/lib/theme-chalk/index.css';

import App from './App.vue'
createApp(App)
// Mount routing information to app using the use method
    .use(router)
// Mount vuEX information to vUE using the use method
    .use(store)
    .use(ElementPlus)
    .mount('#app')
Copy the code
  • Modify the login code
    <el-button type="primary" @click="handleClickIncrement"> accumulation < / el - button >Copy the code

  • At this timeelement-plusIt’s already been introduced successfully
  • Introduction method – load on demand
  • Because our project is built based on Vite, we cannot use Ali based on Webpackplugin-babel-importThis on demand package, but the community is always wheel brothervite-plugin-babel-importA vite based load on demand package
  • The installation
 yarn add vite-plugin-babel-import -D
Copy the code
  • Modify thevite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// Load packages on demand
import vitePluginBabelImport from 'vite-plugin-babel-import'

export default defineConfig({
  plugins: [vue(),
  vitePluginBabelImport([
    {
      libraryName: 'element-plus'.libraryDirectory: 'es'.ignoreStyles: [].style(name) {
        return `element-plus/lib/theme-chalk/${name}.css`; }}])]})Copy the code
  • Loading on demand has been configured, so let’s comment out the global import code in main.ts
    // import ElementPlus from 'element-plus';
    // import 'element-plus/lib/theme-chalk/index.css';
    // .use(ElementPlus)
Copy the code
  • Next, we’ll still modify the code in the Login component
    <template>
      <div>
        login
        <el-button type="primary" @click="handleClickIncrement">cumulative</el-button>
        <router-link to="/">Go to the home page</router-link>
      </div>
    </template>
    <script setup>
    import { ElButton } from 'element-plus'
    import { useStore } from "vuex";
    const store = useStore();
    function handleClickIncrement() {
      store.commit("increment");
    }
    </script>
Copy the code
  • This time we look at the effect drawing, still no problem

5. Vite configuration

  • supportvue-jsxgrammar
    / / installation
    yarn add vite-plugin-babel-import -D
    / / package
    / / vuejsx support
    import vueJsx from "@vitejs/plugin-vue-jsx"
    // Add to plugin
    vueJsx()
Copy the code
  • Support alias
// Install path and @types/node, Yarn add path yarn add @types/node -d import {defineConfig} from "vite"; yarn add path yarn add @types/node -d import {defineConfig} from "vite";  Import path from 'path' // vue support import vue from "@vitejs/plugin-vue"; Import vuejsx from "@vitejs/plugin-vue-jsx"; Import vitePluginBabelImport from "viet-plugin-babel-import "; // Import vitePluginBabelImport from" viet-plugin-babel-import "; const resolve = dir => path.resolve(__dirname, dir) export default defineConfig({ resolve: { alias: { "@": resolve("./src") }, }, plugins: [ vue(), vueJsx(), vitePluginBabelImport([ { libraryName: "element-plus", libraryDirectory: "es", ignoreStyles: [], style(name) { return `element-plus/lib/theme-chalk/${name}.css`; }, }, ]), ], });Copy the code

So we can use it in the project@The symbol

  • serverThe related configuration
    import { defineConfig } from "vite";
    import path from "path";
    / / the vue support
    import vue from "@vitejs/plugin-vue";
    / / vuejsx support
    import vueJsx from "@vitejs/plugin-vue-jsx";

    // Load packages on demand
    import vitePluginBabelImport from "vite-plugin-babel-import";

    const resolve = (dir) = > path.resolve(__dirname, dir);
    export default defineConfig({
      resolve: {
        alias: {
          "@": resolve("./src"),}},server: {
        port: 8080.// Project startup port
        open: true.// Whether the browser is automatically opened when the project starts
        proxy: {
          / / agent
          "/foo": "http://localhost:4567/".Agents / / way/foo - - > http://localhost:4567/foo
          // This is an option
          "/api": {
            target: "http://123.456.com".// Proxy mode/API --> http://123.456.com
            changeOrigin: true.rewrite: (path) = > path.replace(/^\/api/.""),}}},build: {
        outDir: resolve("./dist1"), // Package the output directory, dist by default
      },
      plugins: [
        vue(),
        vueJsx(),
        vitePluginBabelImport([
          {
            libraryName: "element-plus".libraryDirectory: "es".ignoreStyles: [].style(name) {
              return `element-plus/lib/theme-chalk/${name}.css`; },},]),],});Copy the code

6. CSS Precompilation (less)

  • The installation
    yarn add less -D
Copy the code
  • If you are using a single-file component, you can pass <style lang="sass">(or other preprocessor) automatically turn on.
  • Vite has improved @import resolution for Sass and Less to ensure that Vite aliases can also be used. In addition, Sass/Less files in a different directory than the root file referenced by the relative path in URL () are automatically rebased to ensure correctness.

7. Import Glob

  • Now let’s look at a problem. When the page reaches a certain size, there will be a lot of page routes in routes/index.ts. At this time, we will split the page routes into different files according to different modules to store the corresponding routing information, and then introduce it into index.ts uniformly. To scan a folder under the specified rules of the file, according to this rule to the current folder under the file dynamic introduction to the current component, to achieve an automatic mount operation. If you think aboutnodeIn thefsIf you are familiar with the module, you can implement this functionality (common examples are NextJS, Egg, nuxT and other convention-based configuration development). But our front-end engineering projects end up packaged in the browser, not using Node’s FS module,viteIs based on native browser module development. Vite supports the use of the special import.meta.glob function to import multiple modules from the file system.
  • First, let’s rework the page structure

  • At this point we split the routing

– Add routes/index.ts to modules files with the TS suffix dynamically

// Load all ts files in the modules folder in the current folder
const  modules = import.meta.glob("./modules/*.ts")
// Iterate over the key of the Modules object to access the corresponding module
for (const path in modules) {
   const mod =  await modules[path]()
   console.log(mod);
}
Copy the code
  • After the modification, you will find the console error

  • The reason is thatvite-plugin-babel-importThis plugin version 2.0.5 does not support top-levelawaitThe writing of

The solution was to downgrade the viet-plugin-babel-import plug-in to 2.0.2

– at this timevite.config.tsAn error again

  • We need to putignoreStylesJust delete the parameters

  • Starting the project again, we open the console and see the routes/index.ts message printed

  • If we expand the Module, we’ll see that the default array contains the exported data from login and home TS files in our module folder,

Add routes/index.ts to routes/index.ts

import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
// Load all ts files in the modules folder in the current folder
const  modules = import.meta.glob("./modules/*.ts")

const routes: RouteRecordRaw[] = []

// Iterate over the key of the Modules object to access the corresponding module
for (const path in modules) {
   const mod =  await modules[path]()
   // Add data to routesroutes.push(... mod.default) }const router = createRouter({
    history: createWebHistory(),
    routes
})

export default router
Copy the code

– It is no problem for us to access login and the home page again at this time

  • At this point the vite+ VUE base shelf is set up

8. Background management system – Login

  • Login related operations are relatively simple, I will directly post the code
<template>
  <el-form
    status-icon
    label-width="100px"
    class="login-form"
  >
    <el-form-item label="Username">
      <el-input
        v-model="form.name"
      ></el-input>
    </el-form-item>
    <el-form-item label="Password">
      <el-input
        type="password"
        v-model="form.pwd"
      ></el-input>
    </el-form-item>
    <el-form-item>
      <el-button class="block" type="primary" @click="submitForm"
        >Submit < / el - button ></el-form-item>
  </el-form>
</template>
<script lang="ts" setup>
import { reactive } from "vue";
import { useRouter, useRoute } from 'vue-router'
import { ElMessage } from 'element-plus'
interface Form {
  name: string,
  pwd: string
}
const form = reactive<Form>({
  name: "".pwd: ""
});
const router = useRouter()
const route = useRoute()
function submitForm() {
  const { name, pwd } = form
  if(name && pwd) {
    // Timer simulates login
  setTimeout(() = >{
      // The simulation information is stored locally
      sessionStorage.setItem("user".JSON.stringify({name,pwd}))
      If the user goes directly to the login page, it goes to the '/' home page
      const path: string= (route.query as{url? : string}).url ||"/"
      router.push(path)
  })
  }else {
    ElMessage.error("User name and password cannot be 🙅‍♀️ empty")}}</script>
<style lang="less">
.login-form{
width: 500px;
margin: 200px auto;
}
.block{
  width:100%
}
</style>

Copy the code

  • The page is like this, still quite good to see, ha ha

9, home page transformation

    <template>
  <div>
    <el-row>
      <el-col :span="24"
        ><div class="layout-head pdlr28">{{ userName || "--" }}</div></el-col
      >
    </el-row>
    <el-row>
      <el-col :span="4">
        <el-menu
          :uniqueOpened="true"
          default-active="2"
          class="menu-wrap"
          background-color="#545c64"
          text-color="#fff"
          active-text-color="#ffd04b"
        >
          <el-submenu index="1">
            <template #title>
              <i class="el-icon-location"></i>
              <span>A navigation</span>
            </template>
            <el-menu-item-group>
              <template #title>Group a</template>
              <el-menu-item index="1-1">Option 1</el-menu-item>
              <el-menu-item index="1-2">Option 2</el-menu-item>
            </el-menu-item-group>
            <el-menu-item-group title="Group 2">
              <el-menu-item index="1-3">Option 3</el-menu-item>
            </el-menu-item-group>
            <el-submenu index="1-4">
              <template #title>Option 4</template>
              <el-menu-item index="1-4-1">Option 1</el-menu-item>
            </el-submenu>
          </el-submenu>
          <el-menu-item index="2">
            <i class="el-icon-menu"></i>
            <template #title>Navigation 2</template>
          </el-menu-item>
          <el-menu-item index="3" disabled>
            <i class="el-icon-document"></i>
            <template #title>Navigation three</template>
          </el-menu-item>
          <el-menu-item index="4">
            <i class="el-icon-setting"></i>
            <template #title>Navigation four</template>
          </el-menu-item>
          <el-submenu index="5">
            <template #title>
              <i class="el-icon-location"></i>
              <span>A navigation</span>
            </template>
            <el-menu-item-group>
              <template #title>Group a</template>
              <el-menu-item index="5-1">Option 1</el-menu-item>
              <el-menu-item index="5-2">Option 2</el-menu-item>
            </el-menu-item-group>
            <el-menu-item-group title="Group 2">
              <el-menu-item index="5-3">Option 3</el-menu-item>
            </el-menu-item-group>
          </el-submenu>
        </el-menu>
      </el-col>
      <el-col :span="20" class="pdl28">
        <el-breadcrumb separator="/" class="pdt28 pdb28">
          <el-breadcrumb-item :to="{ path: '/' }">Home page</el-breadcrumb-item>
          <el-breadcrumb-item><a href="/">Activity management</a></el-breadcrumb-item>
          <el-breadcrumb-item>List of activities</el-breadcrumb-item>
          <el-breadcrumb-item>Event details</el-breadcrumb-item>
        </el-breadcrumb>
        <div class="com">
          <el-table :data="tableData" style="width: 100%">
            <el-table-column prop="date" label="Date" width="180">
            </el-table-column>
            <el-table-column prop="name" label="Name" width="180">
            </el-table-column>
            <el-table-column prop="address" label="Address"> </el-table-column>
          </el-table>
        </div>
      </el-col>
    </el-row>
  </div>
</template>
<script  lang="ts" setup>
import { reactive } from "vue";
interface User {
  name: string;
  pwd: string;
}

interface Table {
  date: string;
  name: string;
  address: string;
}

const user: string | null = sessionStorage.getItem("user");
const userName: string | null = user && (JSON.parse(user) as User).name;
const tableData = reactive<Table[]>([
  {
    date: "2016-05-02".name: "Wang Xiaohu".address: Lane 1518, Jinshajiang Road, Putuo District, Shanghai}, {date: "2016-05-04".name: "Wang Xiaohu".address: Lane 1517, Jinshajiang Road, Putuo District, Shanghai}, {date: "2016-05-01".name: "Wang Xiaohu".address: Lane 1519, Jinshajiang Road, Putuo District, Shanghai}, {date: "2016-05-03".name: "Wang Xiaohu".address: Lane 1516, Jinshajiang Road, Putuo District, Shanghai,}]);</script>
<style lang="less" scoped>
.layout-head {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: flex-end;
  height: 56px;
  background-color: #99a9bf;
}
.text-center {
  text-align: center;
}
.menu-wrap {
  height: calc(100vh - 56px);
}
.pdlr28 {
  padding-left: 28px;
  padding-right: 28px;
}
.pdt28 {
  padding-top: 28px;
}

.pdr28 {
  padding-right: 28px;
}
.pdb28 {
  padding-bottom: 28px;
}
.pdl28 {
  padding-left: 28px;
}
</style>
Copy the code

10. Home page effect drawing

11. Route interception

  • In this case, we need to intercept the route, and users who do not log in will be redirected to the login page
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
// Load all ts files in the modules folder in the current folder
const  modules = import.meta.glob("./modules/*.ts")

const routes: RouteRecordRaw[] = []

// Iterate over the key of the Modules object to access the corresponding module
for (const path in modules) {
   const mod =  await modules[path]()
// Add data to routesroutes.push(... mod.default) }const router = createRouter({
    history: createWebHistory(),
    routes
})

router.beforeEach((to, format, next) = > {
    // Determine whether to route to the login page
    // If yes, clear the sessionStorage
    // Otherwise verify whether the user has logged in
        // If there is a login record, release
        // Otherwise clear the sessionStorage and redirect to login
    if(to.path === '/login') {
        // Clear the sessionStorage
        sessionStorage.clear()
        next()
    }else {
       let user = sessionStorage.getItem('user')
        if(user) {
            next()
        }else {
            // Clear the sessionStorage
            sessionStorage.clear()
            // Redirect to login
            next("/login? url="+to.path)
        }
    }
})

export default router
Copy the code

12. Complete code

  • Github.com/Tyf2345/jue…

Write in the last

  • When you read this, first of all you are a very persistent person, this article has no illustrations, it is dry, give yourself a thumbs-up if you read it from beginning to end
  • This article mainly built a vite + VUe3 + TS shelf, from layout to style to interface forwarding, the basic functions have been built
  • You are welcome to comment and point out any imperfections