前端VUE3+Vite -- 框架搭建

这里写目录标题

    • 整理环境
    • Vite
      • 为什么选 Vite
      • 构建
      • Vite总结
      • 初始化项目
    • element-plus
      • 构建使用
      • 配置全局
    • Vuex
      • 什么是VueX:
      • 为什么选用 Vuex
      • 总结
      • 构建
    • Router
      • 构建
      • 配置全局
    • axios
    • vite-plugin-mock
      • 引入依赖
      • 配置 vite.config.ts
    • nprogress
    • hooks
      • useState
      • useEffect
    • 搭建登录页
    • 整理 home 页面
    • 下面是搭建可能遇到的问题:
      • webstorm 识别不到 vite 的@
      • 启动可能会报错找不到@
      • 没有 scss包报错:internal server error preprocessor dependency sass not found. did you install it vite
      • 请求时vue中axios的post请求url自动带上本地ip

整理环境

卸载 vue – 安装 vue

npm uninstall vue-cli - g

vue install vue-cli -g

vue -v 

Vite

vite 官网学习

为什么选 Vite

开始构建越来越大型的应用时,需要处理的 JavaScript 代码量也呈指数级增长。包含数千个模块的大型项目相当普遍。我们开始遇到性能瓶颈 —— 使用 JavaScript 开发的工具通常需要很长时间(甚至是几分钟!)才能启动开发服务器,即使使用 HMR,文件修改后的效果也需要几秒钟才能在浏览器中反映出来。如此循环往复,迟钝的反馈会极大地影响开发者的开发效率和幸福感。

1.Vite旨在利用生态系统中的新进展解决上述问题:浏览器开始原生支持 ES 模块,且越来越多
2. JavaScript 工具使用编译型语言编写。
3. Vite 天然支持引入 .ts 文件。

构建

构建vite + vueTs

 npm init vite@latest

在这里插入图片描述

你还可以通过附加的命令行选项直接指定项目名称和你想要使用的模板。例如,要构建一个 Vite + Vue 项目,运行:

npm init vite@latest my-vue-app --template vue

# npm 7+, 需要额外的双横线:
npm init vite@latest my-vue-app -- --template vue

Vite总结

Vite 是什么:vite是下一代前端开发构建工具,同时它的插件APIJavaScript API 带来了高度的可扩展性,并有完整的类型支持

  1. 冷启动开发服务器
  2. 天然支持.ts 文件
  3. 解析速度块
  4. Vite 通过在一开始将应用中的模块区分为 依赖 和 源码 两类,改进了开发服务器启动时间。使用缓慢的更新

初始化项目

前端VUE3+Vite -- 框架搭建_第1张图片


element-plus

element-plus 中文官网

构建使用

# NPM
$ npm install element-plus --save

前端VUE3+Vite -- 框架搭建_第2张图片

配置全局

element.ts

import ElementPlus from 'element-plus'
import * as ElementPlusIcons from '@element-plus/icons-vue'
import {App} from 'vue'

export default (app: App) => {
    app.use(ElementPlus)
    for (const [key, component] of Object.entries(ElementPlusIcons)) {
        app.component(key, component)
    }
}

App.vue


import { createApp } from 'vue'
import App from './App.vue'
import elementPlus from "./plugins/element";
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'

const app = createApp(App)

// 注入element-plus
elementPlus(app)
app.use(ElementPlus)
app .mount('#app')

Vuex

VueX 官网

什么是VueX:

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

白话文,中间缓存件

为什么选用 Vuex

Vuex 的状态存储是响应式的。当Vue组件从store中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新

你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。

Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。

如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。

总结

Vuex 是一个状态存储响应式管理器,可以对于复杂应用提供一个很好的缓存中间件,方便抽出共享的变量响应式的存储进去,方便后期维护和开发维护

构建

npm install vuex@next --save

store/index.ts

import { createStore } from 'vuex'


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



export default store

main.ts

import { createApp } from 'vue'
import App from './App.vue'
import elementPlus from "./plugins/element";
import store from "./store";
const app = createApp(App)

// 注入element-plus
elementPlus(app)
// 使用store
app.use(store)

app .mount('#app')

一般在使用的时候,会进行拆分成模块使用,配置全局配置,和一些特殊状态变量改变一下举例:

创建store/modules

app.ts 主目录配置
user.ts 用户权限状态配置


import {Module} from "vuex";

/**
 * app.ts 主目录配置
 */
export interface AppModule {
    config: any
}

const app: Module<AppModule, any> = {
    namespaced: true,
    state: {
        config: {}
    },

    mutations: {
        setConfig(state, data) {
            state.config = data
        }
    }

}





import {Module} from "vuex";

/**
 * user.ts
 */
export interface UserModule {
    token: string
    user: Record<string, any>
    sidebar: any[]
    permissions: string[]
}

const user: Module<UserModule, any> = {
    namespaced: true,
    state: {
        token: '',
        user: {},
        // 菜单
        sidebar: [],
        // 权限
        permissions: []
    },

    mutations: {
        setToken(state, data) {
            state.token = data
        },
        setUser(state, data) {
            state.user = data
        },
        setSidebar(state, data) {
            state.sidebar = data
        },
        setPermissions(state, data) {
            state.permissions = data
        }
    }
}
export default user






import app, { AppModule } from './app'
import user, { UserModule } from './user'

/**
 * index.ts
 */
export interface rootState {
    app: AppModule
    user: UserModule
}

export default {
    app,
    user
}






import { GetterTree } from 'vuex'
import { rootState } from './moudules'

/**
 * getters.ts
 */
const getters: GetterTree<rootState, any> = {
    // token
    token: state => state.user.token,
    // 管理员信息
    userInfo: state => state.user.user,
    // 通用配置
    config: state => state.app.config,
    // 权限列表
    permissions: state => state.user.permissions,
    sidebar: state => state.user.sidebar
}

export default getters





import { createStore, Store, useStore as baseUseStore } from 'vuex'
import getters from './getters'
import modules from './moudules'

/**
 * index.ts
 */
const store = createStore({
    modules: modules,
    getters
})

export default store



Router

Vue 路由

构建

npm install vue-router@4

router/static.ts

import { RouteRecordRaw } from 'vue-router'
import Layout from '@/layout/index.vue'

// Symbol 其存在就是为了确保对象属性唯一,不会存在属性冲突。
export const constRoutes: RouteRecordRaw[] = [
    {
        path: '/login',
        component: () => import('@/views/account/login.vue')
    },
]

export const indexRouteName = Symbol()
export const indexRoute: RouteRecordRaw = {
    path: '/',
    component: Layout,
    name: indexRouteName
}

index.ts

import axios from "axios";
import { ElMessage } from "element-plus";
import { throttle } from "echarts";
import store from "@/store";


const request = axios.create({
    baseURL: `${import.meta.env.VITE_APP_BASE_URL}/api`,
    timeout: 3000,
    headers: {
        "Content-Type": "application/json",
        version: import.meta.env.version
    }
});


/**
 * 定义事件
 */
const eventResponse = {

    // 失败
    error: ({ msg }: any): Promise<any> => {
        return Promise.reject(msg);
    },

    // 重定向
    // throttle 函数节流的原理: 使用定时器做时间节流. 当触发一个事件时,
    // 先用setTimeout让这个事件延迟一小段时间在执行.
    // 如果在这个时间间隔内又触发了事件,
    // 就clearTimeout原来的定时器,
    // 在setTimeout一个新的定时器重复以上流程

    redirect: throttle(() => {
        store.dispatch("user/logout").then(() => {
            location.reload();
        });
        return Promise.reject();
    })
};


/**
 * 设置响应拦截器  推送事件弹窗。 同时增加组件封装弹窗
 */
request.interceptors.response.use(
  response => {
      switch (response.data.code) {
        // 成功
          case 200:
              return response.data

          case 500:
              return eventResponse.error(response.data);

        // 权限过期
          case -1:
              return eventResponse.redirect();
      }
  },
  error => {
      console.log(error);
      ElMessage({ type: "error", message: "系统错误联系开发" });
      return Promise.reject(error);
  }
);


export default request;










配置全局

main.ts

import { createApp } from 'vue'
import App from './App.vue'
import elementPlus from "./plugins/element";
import store from "./store";
import router from "./router";
const app = createApp(App)

// 注入element-plus
elementPlus(app)
// 使用store
app.use(store)
app.use(router)
app .mount('#app')

创建登录页面

login.vue

<template>
<div>
  你好
div>

template>

<script  lang="ts">
export default {
name: ''
}
script>

<style scoped>

style>

效果
前端VUE3+Vite -- 框架搭建_第3张图片

整个项目搭建完成之后。开始书写管理后台


axios

import axios from "axios";


const request = axios.create({
  baseURL: `${import.meta.env.VITE_APP_BASE_URL}/api`,
  timeout: 3000,
  headers: {
    "Content-Type": "application/json",
    version: import.meta.env.version
  }
});

export default request;




vite-plugin-mock

引入依赖

npm i mockjs -D
npm i vite-plugin-mock -D

配置 vite.config.ts

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
// 解决@ 映射到全路径地址信息
import path from "path";
import { viteMockServe } from "vite-plugin-mock";

// https://vitejs.dev/config/
export default defineConfig({

  // 解决编译@问题
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "src")
    }
  },

  base: "/admin/",
  
  plugins: [vue(),
    viteMockServe({
      mockPath: "./src/mock",
      localEnabled: true, // 开发
      prodEnabled: false,// 生产
      injectFile: path.resolve("src/main.ts"), // 解决读取不到mock.ts 问题注入文件
    })
  ],


  server: {
    host: "0.0.0.0",//ip地址
    port: 10086, // 设置服务启动端口号
    open: true // 设置服务启动时是否自动打开浏览器
  }
});

建立包./src/mock 映射上文配置mockPath: “./src/mock”,扫描
前端VUE3+Vite -- 框架搭建_第4张图片

书写mock请求

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

export default [
  {
    url: '/api/getConfig',
    method: 'get',
    response: () => {
       return 'mock 成功'
    }
  }
] as MockMethod[] // ts 类型返回数组


请求测试

前端VUE3+Vite -- 框架搭建_第5张图片

这里的请求axios请求的时候不能加api 因为在使用创建axios 实例的时候我们加了baseUrl,所以如果加了api 那么映射的 /api/api/getConfig
前端VUE3+Vite -- 框架搭建_第6张图片

同时因为是mock 数据在本机需要修改本机的baseurl 不要打到后端, 需要修改打到本机
修改前是后端路由, 127.0.0.1:8080
修改是服务本机地址 127.0.0.1:10086 用来访问mock

前端VUE3+Vite -- 框架搭建_第7张图片

配置页面点击试试

<template>
<button @click="getUrl">点击</button>
34324
</template>

<script lang='ts' setup>

import { apiConfig } from "@/api/app";

function getUrl(){
  apiConfig()
}

</script>

<style scoped>

</style>

测试
前端VUE3+Vite -- 框架搭建_第8张图片

可以访问


nprogress

进度条 官网

npm install --save nprogress

直接调用 start()或者done()来控制进度条。

vite-env.d.ts 配置

declare module 'nprogress' {
    export function configure(options: any): void
    export function start(): void
    export function done(): void
}
import 'nprogress/nprogress.css'
import NProgress from 'nprogress


NProgress.start();
NProgress.done();

hooks

官网解释

可让您在不编写类的情况下使用 state(状态) 常用 为 useState useEffect

useState

 const [count, setCount] = useState(0)
 // count 当前值
// setcount 当前参数自带一个设置方法

useEffect


 const [count, setCount] = useState(0);
 
// count 当前值
// setcount 当前参数自带一个设置方法

 useEffect(() => {
        let timer = setInterval(() => {
            setCount(count + 1)
        }, 1000);
        // 当在 useEffect 的回调函数中返回一个函数时,这个函数会在组件卸载前被调用
        return () => clearInterval(timer)
        // count发生变化时再次执行
    }, [count]);
// 组件调用 自动运行定时器,赋新值,容器销毁自动销毁,不会卡内存泄漏


搭建登录页


<template>

  <el-container class="heightMax">
    <el-aside width="77%">
      <div class="demo-image__lazy">
        <el-image class="heightMax" v-for="url in urls" :key="url" :src="url" lazy />
      div>
    el-aside>

    <el-main class="mainMax">


      <div class="login-body">
        <div class="login-container">
          <div class="head">
            <img class="logo" src="https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg" alt="" />
            <div class="name">
              <div class="title">商城div>
            div>
          div>

          <el-form label-position="top"
                   :model="state.ruleForm"
                   ref="formRef"
                   :rules="rules"
                   class="login-form">

            <el-form-item style="color:#545c64" prop="account">
              <el-input type="text"
                        v-model.trim="state.ruleForm.account"
                        autocomplete="off"
                        placeholder="账号">

              el-input>
            el-form-item>


            <el-form-item  prop="password">
              <el-input type="password"
                        v-model.trim="state.ruleForm.password"
                        autocomplete="off"
                        placeholder="密码">

              el-input>
            el-form-item>



            <el-form-item>
              <div style="color: rgb(67, 151, 84);">登录表示您已同意
                <a @click="visible = true" style=" color: chocolate;">《服务条款》a>
              div>

              <el-button style="width: 100%" type="primary" @click="submitForm(formRef)">立即登录el-button>
              <el-checkbox v-model="state.checked" @change="!state.checked">下次自动登录el-checkbox>
            el-form-item>

          el-form>
        div>
      div>

    el-main>
  el-container>


  <el-dialog v-model="visible" :show-close="false">
    <template #header="{ close, titleId, titleClass }">
      <div class="my-header">
        <h4 :id="titleId" :class="titleClass">服务条款!h4>
        <el-button type="danger" @click="close">
          <el-icon class="el-icon--left">
            <CircleCloseFilled />
          el-icon>
          关闭
        el-button>
      div>
    template>
    服务条款文档
  el-dialog>


template>


<script lang="ts" setup>
import type { FormInstance, FormRules } from "element-plus";

import { computed, onMounted, reactive, ref, Ref } from "vue";
import { ElForm, ElInput, ElMessage } from "element-plus";
import store from "@/store";
import { useAdmin } from "@/core/hooks/app";

const { route,router } = useAdmin()
const config = computed(() => store.getters.config);
const formRef = ref<FormInstance>();
const visible = ref(false);


const state = reactive({
  ruleForm: {
    account: "",
    password: ""
  },

  checked: true,

});



const rules = reactive<FormRules>({
  account: [
    { required: true, message: "账户不能为空", trigger: "blur" }
  ],

  password: [
    { required: true, message: "密码不能为空", trigger: "blur" }
  ]
})





function submitForm(formEl: FormInstance | undefined) {
  if (!formEl) return;
  formEl.validate((valid) => {
    if (valid) {

      store.dispatch("user/login", state.ruleForm).then(() => {
        // 增加搜索属性
        const {
          query: { redirect }
        } = route

        // 定位到搜索属性路由
        const path = typeof redirect === "string" ? redirect : "/";
        // 跳转连接
        router.replace(path);

      }).catch(err => {
        state.ruleForm.password = "";
        ElMessage.error("登录失败");
      });
    }
  });
}


const urls = [
  "https://fuss10.elemecdn.com/9/bb/e27858e973f5d7d3904835f46abbdjpeg.jpeg"
];


script>

<style scoped lang="scss">
@import "../src/styles/common";
style>



@import “…/src/styles/common”; 封装样式




.heightMax {
  height: 100%;
}

.mainMax {
  padding-top: 0;
  padding-bottom: 0;

  display: flex;
}

.demo-image__lazy {
  height: 100%;
  overflow-y: auto;
}

.demo-image__lazy .el-image {
  display: block;
  min-height: 200px;
  margin-bottom: 10px;
}

.demo-image__lazy .el-image:last-child {
  margin-bottom: 0;
}


.login-body {
  display: flex;
  width: 100%;
  background-color: #fff;

}

.my-header {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
}

.login-container {
  width: 100%;
  background-color: #fff;
  border-radius: 4px;
  box-shadow: 0 21px 41px 0 rgba(0, 0, 0, 0.2);
}

.head {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 40px 0 20px 0;
}

.head img {
  width: 100px;
  height: 100px;
  margin-right: 20px;
}

.head .title {
  font-size: 28px;
  color: #1BAEAE;
  font-weight: bold;
}

.head .tips {
  font-size: 12px;
  color: #999;
}

.login-form {
  width: 70%;
  margin: 0 auto;
}

存储token

import { Module } from "vuex";
import cache from "@/utils/cache";
import { TOKEN } from "@/config/cachekey";
import { apiLogin, apiLogout, apiUserInfo } from "@/api/user";

export interface UserModule {
    token: string;
    user: Record<string, any>;
    sidebar: any[];
    permissions: string[];
}

const user: Module<UserModule, any> = {
    namespaced: true,
    state: {
        token: cache.get(TOKEN) || "",
        user: {},
        // 菜
        sidebar: [],
        // 权限
        permissions: []
    },
    mutations: {
        setToken(state, data) {
            state.token = data;
        },
        setUser(state, data) {
            state.user = data;
        },
        setSidebar(state, data) {
            state.sidebar = data;
        },
        setPermissions(state, data) {
            state.permissions = data;
        }
    },
    actions: {
        //清除用户信息
        clearUserCache({ commit }) {
            commit("setToken", "");
            commit("setUser", {});
            commit("setPermissions", {});
        },


        // 登录
        login({ commit }, payload: any) {
            const { account, password } = payload;

            return new Promise((resolve, reject) => {
                apiLogin({
                    account: account.trim(),
                    password: password
                }).then((data: any) => {
                      commit("setToken", data.token);
                      cache.set(TOKEN, data.token);
                      resolve(data);
                  })
                  .catch((error) => {
                      reject(error);
                  });
            });
        },



        // 退出登录
        logout({ dispatch }) {
            return new Promise((resolve, reject) => {
                apiLogout()
                  .then((data) => {
                      cache.remove(TOKEN);
                      resolve(data);
                  })
                  .catch((error) => {
                      reject(error);
                  });
            });
        }
    }

};

export default user;



暴露 user

import { createStore, Module, Store, useStore as baseUseStore } from "vuex";
import app, { AppModule } from "@/store/modules/app";
import user, { UserModule } from "@/store/modules/user";

interface rootState {
    app: AppModule;
    user: UserModule;
}


const store = createStore<rootState>({
    modules: { app, user },
    getters: {
        config: state => state.app.config,
        token: state => state.user.token

    }

});


export default store;


整理 home 页面


下面是搭建可能遇到的问题:

webstorm 识别不到 vite 的@

在tsConfig.json 编译选项添加

 "paths": {
      "@/*": ["./src/*"]
    }

前端VUE3+Vite -- 框架搭建_第9张图片

启动可能会报错找不到@

vite.config.ts 配置

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

// https://vitejs.dev/config/
export default defineConfig({
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),
    },
  },
  plugins: [vue()]
})

没有 scss包报错:internal server error preprocessor dependency sass not found. did you install it vite

安装node-sass 或 sass

npm install node-sass

请求时vue中axios的post请求url自动带上本地ip

前端VUE3+Vite -- 框架搭建_第10张图片
如果是从环境里面获取,或者手写http时候记得写全

参考:VITE_APP_BASE_URL='http://127.0.0.1:8080'

前端VUE3+Vite -- 框架搭建_第11张图片

你可能感兴趣的:(TOP1--,必修课,vue.js,npm,javascript,vite,router)