Vue3 + Vite2 + Vue-Router 4.x + Vuex 4.x + Element-Plus + Axios + Mockjs 项目搭建

752 阅读5分钟

项目结构预览

附上源码地址github.com/wjr-dev/vue… overview.png

Vite配置

配置预览

// 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 ({ command }) => {
  return {
    plugins: [
      vue(),
      viteMockServe({
        mockPath: 'mock', // 指定 mock 数据文件夹路径
        supportTs: false, // 启用 ts
        // command === serve: 由开发服务器调用的插件
        // command === build: 由 Rollup 调用的插件
        localEnabled: command === 'serve',
      }),
    ],
    css: {
      preprocessorOptions: {
        scss: {
          additionalData: `@import "@/assets/style/common.scss";`
        }
      }
    },
    resolve: {
      alias: {
        '@': path.resolve(__dirname, 'src'),
      },
    },
    server: {
      // proxy: {
      //  '/api': {
      //    target: 'http://localhost:64368',
      //    changeOrigin: true,
      //    rewrite: path => path.replace(/^\/api/, ''),
      //  },
      // },
    },
  }
}

  1. 配置 base 公共基础路径, vite.config.js
export default defineConfig({
  base: '/components', // 配置后项目访问路径为 "http://localhost:3000/components/"
})

  1. 配置 alias 别名, vite.config.js
import path from 'path'

export default defineConfig({
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'), // 配置后使用可以@代替src路径
    },
  },
})

  1. 配置 proxy 代理, vite.config.js
export default defineConfig({
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:64368',
        changeOrigin: true,
      },
    },
  },
})

CSS预处理器

  1. 安装对应的CSS处理器,这里以scss为例,

yarn add sass node-sass sass-loader -D

package.json

{
  "devDependencies": {
    "node-sass": "^6.0.1",
    "sass": "^1.38.1",
    "sass-loader": "^12.1.0",
  }
}

  1. 配置全局scss,vite.config.js
// https://vitejs.dev/config/
export default ({ command }) => {
  return {
    css: {
      preprocessorOptions: {
        scss: {
          additionalData: `@import "@/assets/style/common.scss";`
        }
      }
    },
    
  }
}

eslint

  1. 首先在项目安装依赖

yarn add @vue/eslint-config-prettier babel-eslint eslint eslint-plugin-prettier eslint-plugin-vue prettier -D

package.json

{
  "scripts": {
    "lint": "eslint \"src/**/*.{js,vue}\""
  },
  "devDependencies": {
    "@vue/eslint-config-prettier": "^6.0.0",
    "babel-eslint": "^10.1.0",
    "eslint": "^6.7.2",
    "eslint-plugin-prettier": "^3.1.3",
    "eslint-plugin-vue": "^7.0.0-0",
    "prettier": "^1.19.1"
  }
}
  1. 配置lint规则,.eslintrc.js
module.exports = {
  root: true,
  env: {
    node: true,
  },
  extends: ["plugin:vue/vue3-recommended", "eslint:recommended", "@vue/prettier"],
  parserOptions: {
    parser: "babel-eslint",
  },
  rules: {
    "no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
    "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
    "prettier/prettier": [
      "warn",
      {
        singleQuote: none,
        semi: false,
        trailingComma: "es5",
      },
    ],
  },
};

  1. 修改prettier的默认格式化规则,prettier.config.js
module.exports = {
  semi: false, // 行末不跟分号
  trailingComma: 'all', // 有尾随逗号
  arrowParens: 'avoid', // 箭头函数单个参数不加分号
  singleQuote: true, // 使用单引号
  printWidth: 110, // 换行长度
}

  1. 根目录下新建.vscode/settings.json
{
  // VScode主题配置
  "editor.tabSize": 2,
  "editor.lineHeight": 24,
  "javascript.updateImportsOnFileMove.enabled": "always", // 移动文件或者修改文件名时,是否自动更新引用了此文件的所有文件。
  // 每次保存是否自动格式化文件
  "editor.formatOnSave": false,
  // 每次保存的时候将代码按格式进行修复
  "editor.codeActionsOnSave": {
    // 自动引入缺少的库
    "source.addMissingImports": true,
    // 为代码及Eslint添加自动修复功能
    "source.fixAll": true,
    "source.fixAll.eslint": true
  },
  // 针对.vue、.ts 和 .tsx 文件开启 ESLint 的 autoFix
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "vue",
    "typescript",
    "typescriptreact"
  ]
}

Vue-Router 4.x

  1. 安装依赖,yarn add vue-router@4package.json
{
  "dependencies": {
    "vue-router": "4",
  },
}

  1. 在src目录创建router文件夹,在文件夹中创建index.js文件,router/routes.js
import Home from '@/pages/Home.vue'

export default [
  { path: '/', component: Home },
  { path: '/about', component: () => import('@/pages/About.vue') },
]

  1. 在src目录创建router文件夹,在文件夹中创建index.js文件,router/index.js
import { createRouter, createWebHashHistory } from 'vue-router'
import routes from './routes'

export default createRouter({
  history: createWebHashHistory(),
  routes,
})

  1. src/App.vue,添加<router-view>
<template>
  <router-view></router-view>
</template>

  1. src/main.jsuse(router)
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

createApp(App).use(router).mount('#app')

Vuex 4.x

  1. 安装依赖,yarn add vuex@nextpackage.json
{
  "dependencies": {
    "vuex": "^4.0.2"
  },
}

  1. 在src目录创建store文件夹,在文件夹中创建index.js文件,store/index.js
import { createStore } from 'vuex'

// 创建一个新的 store 实例
export default createStore({
  state () {
    return {
      count: 0
    }
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions:{
    increment ({ commit }) {
      commit('increment')
    }
  }
})

  1. main.js中引入storemain.js
import { createApp } from 'vue'
import App from './App.vue'
import store from './store'

createApp(App).use(store).mount('#app')

  1. 组件中使用,使用 useStore() 获取实例
<template>
  <div @click="increment">{{ count }}</div>
</template>

<script setup>
import { computed } from '@vue/reactivity';
import { useStore } from 'vuex'

const store = useStore() // 获取 store 实例
const count = computed(() => store.state.count)

const increment = () => {
  store.commit('increment')
}

const incrementDispatch =()=>{
  store.dispatch('increment')
}

</script>

Element-Plus

  1. 安装依赖,yarn add element-pluspackage.json
{
  "dependencies": {
    "element-plus": "^1.0.2-beta.71",
  },
}

  1. main.js中引入,main.js
import { createApp } from 'vue'
import App from './App.vue'

import ElementPlus from 'element-plus'
import 'element-plus/lib/theme-chalk/index.css' // 这里官网给的 import 'element-plus/dist/index.css' 是错的

createApp(App).use(ElementPlus).mount('#app')

Axios

  1. 安装依赖,yarn add axiospackage.json
{
  "dependencies": {
    "axios": "^0.21.1",
  },
}

  1. 新建api文件夹,新建api/instance.js
import axios from 'axios'
import {
    ElLoading,
    ElMessage
} from 'element-plus';
//创建axios的一个实例 
var instance = axios.create({
    baseURL: import.meta.env.VITE_APP_URL, //接口统一域名
    timeout: 6000, //设置超时
    headers: {
        'Content-Type': 'application/json;charset=UTF-8;',
    }
})
let loading;
//正在请求的数量
let requestCount = 0
//显示loading
const showLoading = () => {
    if (requestCount === 0 && !loading) {
        loading = ElLoading.service({
            text: "Loading  ",
            background: 'rgba(0, 0, 0, 0.7)',
            spinner: 'el-icon-loading',
        })
    }
    requestCount++;
}
//隐藏loading
const hideLoading = () => {
    requestCount--
    if (requestCount == 0) {
        loading.close()
    }
}

//请求拦截器 
instance.interceptors.request.use((config) => {
    showLoading()
    // 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加了
    const token = window.localStorage.getItem('token');
    token && (config.headers.Authorization = token)
    //若请求方式为post,则将data参数转为JSON字符串
    if (config.method === 'POST') {
        config.data = JSON.stringify(config.data);
    }
    return config;
}, (error) =>
    // 对请求错误做些什么
    Promise.reject(error));

//响应拦截器
instance.interceptors.response.use((response) => {
    hideLoading()
    //响应成功
    return response.data;
}, (error) => {
    console.error(error)
    //响应错误
    if (error.response && error.response.status) {
        const status = error.response.status
        switch (status) {
            case 400:
                message = '请求错误';
                break;
            case 401:
                message = '请求错误';
                break;
            case 404:
                message = '请求地址出错';
                break;
            case 408:
                message = '请求超时';
                break;
            case 500:
                message = '服务器内部错误!';
                break;
            case 501:
                message = '服务未实现!';
                break;
            case 502:
                message = '网关错误!';
                break;
            case 503:
                message = '服务不可用!';
                break;
            case 504:
                message = '网关超时!';
                break;
            case 505:
                message = 'HTTP版本不受支持';
                break;
            default:
                message = '请求失败'
        }
        ElMessage.error(message);
        return Promise.reject(error);
    }
    return Promise.reject(error);
});

export default instance;

  1. 新建api/http.js
import instance from "./instance"
/**
 * @param {String} method  请求的方法:get、post、delete、put
 * @param {String} url     请求的url:
 * @param {Object} params    请求的参数
 * @param {Object} config  请求的配置
 * @returns {Promise}     返回一个promise对象,其实就相当于axios请求数据的返回值
 */

export default ({
    method,
    url,
    data,
    config
}) => {
    method = method.toLowerCase();
    if (method == 'post') {
        return instance.post(url, data, { ...config })
    } else if (method == 'get') {
        return instance.get(url, {
            params: data,
            ...config
        })
    } else if (method == 'delete') {
        return instance.delete(url, {
            params: data,
            ...config
        })
    } else if (method == 'put') {
        return instance.put(url, data, { ...config })
    } else {
        console.error('未知的method' + method)
        return false
    }
}

  1. 新建index.js
import http from "./http"

//请求示例
//get
function mockGet(params) {
  return http({ url: "/api/get", method: "get", params, })
}
// post
function mockPost(params) {
  return http({ url: "/api/post", method: "post", params, })
}

export default{
  mockGet,
  mockPost
}

  1. 组件中调用接口
<script setup>
import { onMounted } from 'vue'

onMounted(async () => {
  try {
    const res = await mockGet()
    console.log(res, 'res');
  } catch (error) {
    console.error(error);
  }
})

</script>

Mockjs

  1. 安装依赖,package.json

yarn add mockjs

yarn add vite-plugin-mock -D

{
  "dependencies": {
    "mockjs": "^1.1.0",
  },
  "devDependencies": {
    "vite-plugin-mock": "^2.9.6"
  }
}

  1. vite.config.js
import { viteMockServe } from 'vite-plugin-mock'

export default ({ command }) => {
  return {
    plugins: [
      viteMockServe({
        mockPath: 'mock',  // 指定 mock 数据文件夹路径
        supportTs: false,  // 启用 ts
        // command === serve: 由开发服务器调用的插件
        // command === build: 由 Rollup 调用的插件
        localEnabled: command === 'serve',
      }),
    ],
  }
}

  1. 在项目根目录创建mock文件夹,在文件夹中创建test.js文件,mock/test.js
export default [
  {
    url: '/api/get',
    method: 'get',
    response: () => {
      return {
        code: 0,
        data: {
          name: 'vben',
        },
      }
    },
  },
  {
    url: '/api/post',
    method: 'post',
    timeout: 2000,
    response: {
      code: 0,
      data: {
        name: 'vben',
      },
    },
  },
  {
    url: '/api/text',
    method: 'post',
    rawResponse: async (req, res) => {
      let reqbody = ''
      await new Promise(resolve => {
        req.on('data', chunk => {
          reqbody += chunk
        })
        req.on('end', () => resolve(undefined))
      })
      res.setHeader('Content-Type', 'text/plain')
      res.statusCode = 200
      res.end(`hello, ${reqbody}`)
    },
  },
]

  1. 组件中使用 fetch 测试
<script setup>

fetch('/api/get').then(res => {
  console.log(res)
})

</script>