基于vue ui搭建前端工程以及项目分层实践浅谈

前言

最近在看前端工程化的内容,在思考如何快速从0开始搭建一个前端项目以及前端项目如何进行代码分层。有一些收获,随手记一下。最近也在写一些demo工程,欢迎大佬来star一下:

基于vue ui研发的飞雪随笔平台前端源码:https://github.com/tdx1997tdx/my_tapd_frontend

vue ui

偶然间发现了vue ui工具,是真的香。直接省略了很多繁琐的配置,将插件,依赖,配置等信息直接可视化出来,特别利于新手研发人员专注业务开发。

直接打开一个vue3项目,可以直接clone上面的地址,输入命令vue ui,就会出现下面页面:

在这里插入图片描述
在这边可以直接搜索前端插件并配置,不需要再去webpack中配置了

在这里插入图片描述
对于vue的周边依赖,如vue-router,vuex,element-ui-plus等,就可以在这边进行配置

在这里插入图片描述
如果要启动项目,就可以在这个页面进行启动或者编译,方便快捷

在这里插入图片描述
具体可以去google一下

前端代码分层

为了方便研发与源码阅读,有必要对代码进行分层,在飞雪测试平台项目中,做了如下分层
在这里插入图片描述

api层

这个包主要存放与后台交互的接口方法

import http from "../utils/http";
import proxy from "../config/proxy";

const config = {proxy, headers: {"Content-Type": "application/json"}, timeout: 1000000};

// 登录
export const login = (data) => http({url: "/login", method: "post", data, config: config});
//注册
export const regist = (data) => http({url: "/regist", method: "post", data, config: config});
//心跳
export const report = () => http({url: "/report", method: "get", config: config});

assets层

主要存放图片以及其他文件

component层

主要存放页面公用组件,这边以Login.vue为例

<template>
  <el-form :model="formData" ref="formData" label-width="0px" class="form_box" :rules="rules">
    <el-form-item>
      <h1 style="margin: 0 auto; text-align: center">欢迎来到飞雪测试平台</h1>
    </el-form-item>
    <el-form-item prop="username">
      <el-input placeholder="请输入用户名" v-model="formData.username" prefix-icon="el-icon-search"/>
    </el-form-item>
    <el-form-item prop="password">
      <el-input placeholder="请输入密码" v-model="formData.password" prefix-icon="el-icon-lock" show-password/>
    </el-form-item>
    <el-form-item>
      <el-checkbox v-model="formData.isCheck" label="记住密码" size="large"/>
      <router-link to="registItem" style="float: right">用户注册</router-link>
    </el-form-item>
    <el-form-item style="float: right">
      <el-button type="primary" @click="submitForm('formData')">登录</el-button>
      <el-button type="info" @click="resetForm('formData')">清空</el-button>
    </el-form-item>
  </el-form>
</template>

<script>
import {login} from "@/api/api";

export default {
  name: "LoginView",
  data() {
    return {
      formData: {
        username: "",
        password: "",
        isCheck: false
      },
      rules: {
        username: [
          {required: true, message: "请输入用户名", trigger: "blur"}
        ],
        password: [
          {required: true, message: "请输入密码", trigger: "blur"}
        ]
      }
    };
  },
  methods: {
    submitForm: function (formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          login({
            username: this.formData.username,
            password: this.formData.password
          }).then(res => {
            if (res.code === 0) {
              this.$message.success("登录成功");
              localStorage.setItem("token", res.data.token);
              localStorage.setItem("loginName", this.formData.username);
              if (this.formData.isCheck) {
                localStorage.setItem("password", this.formData.password);
              } else {
                localStorage.setItem("password", "");
              }
              this.$router.push("/home");
            } else {
              this.$message.warning(res.msg);
            }
          });
        } else {
          return false;
        }
      }).catch(error => {
        this.$message.warning("服务器错误" + error);
      });
    },
    resetForm: function (formName) {
      this.$refs[formName].resetFields();
      this.formData.password = "";
      this.formData.username = "";
    },
    initForm: function () {
      let username = localStorage.getItem("loginName");
      let password = localStorage.getItem("password");
      if (username) {
        this.formData.username = username;
      }
      if (password) {
        this.formData.password = password;
        this.formData.isCheck = true;
      }
    }
  },
  components: {},
  mounted() {
    this.initForm();
  }
};
</script>

<style scoped>

.form_box {
  margin: 30px;
}

</style>

在这里插入图片描述

config层

主要存放配置信息,比如环境分离

export default {
    development: {
        // 开发环境接口请求
        host: "http://localhost:5000/api/"
    },
    production: {
        // 正式环境接口地址
        host: "http://139.198.36.243/api/"
    }
};

router层

主要存放前端路由信息

import {createRouter, createWebHashHistory} from "vue-router";
import HomeView from "../views/HomeView.vue";
import LoginView from "../views/LoginView.vue";
import Regist from "../components/Regist.vue";
import Login from "../components/Login.vue";


const routes = [
    {
        path: "/",
        redirect: "/home"
    },
    {
        path: "/home",
        name: "home",
        component: HomeView
    },
    {
        path: "/login",
        name: "login",
        component: LoginView,
        redirect: "/login/loginItem",
        children: [
            {
                title: "登录组件",
                path: "loginItem",
                component: Login
            },
            {
                title: "注册组件",
                path: "registItem",
                component: Regist
            }
        ]
    }
];

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

export default router;

store层

存放vuex全局变量

import { createStore } from 'vuex'

export default createStore({
  state: {
  },
  getters: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
})

utils层

存放通用工具,如请求通用工具,封装axios等

/** **   http.js   ****/
// 导入封装好的axios实例
import axios from "axios";

const http = ({method, url, data, config, headers = {"Content-Type": "application/json;charset=UTF-8;"}}) => {
    const env = process.env.NODE_ENV || "development";
    const {host} = config.proxy[env];
    const baseURL = host;
    const instance = axios.create({
        baseURL, // 接口统一域名
        timeout: 6000, // 设置超时
        headers: headers
    });
    // 请求拦截器
    instance.interceptors.request.use(
        (config) => {
            // 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加 了
            const token = window.localStorage.getItem("token");
            const loginName = localStorage.getItem("loginName");
            token && (config.headers.Authorization = token);
            loginName && (config.headers.loginName = loginName);
            // 若请求方式为post,则将data参数转为JSON字符串
            if (config.method === "post" || config.method === "put") {
                config.data = JSON.stringify(config.data);
            }
            return config;
        },
        (error) =>
            // 对请求错误做些什么
            Promise.reject(error)
    );
    // 响应拦截器
    instance.interceptors.response.use(
        (response) =>
            // hideLoading();
            // 响应成功
            // console.log('响应成功');
            response.data,
        (error) => {
            console.log(error);
            // 响应错误
            if (error.response && error.response.status) {
                return Promise.reject(error);
            }
            return Promise.reject(error);
        }
    );
    method = method.toLowerCase();
    if (method === "get" || method === "delete") {
        return instance({url, method, params: data, ...config});
    }
    return instance({url, method, data, ...config});
};

// 导出
export default http;

view层

存放界面,如LginView.vue,对应的是一整个界面

<template>
  <div class="login_container">
    <div class="login_box">
      <router-view></router-view>
    </div>
  </div>
</template>

<script>

export default {
  name: "LoginView",
  data() {
    return {};
  },
  methods: {},
  components: {}
};
</script>

<style scoped>
.login_container {
  width: 100%;
  height: 100%;
  background-image: url('../assets/loginBackgroud.jpg');
  background-position: center center;
  background-attachment: fixed;
  background-repeat: no-repeat;
  background-size: cover;
}

.login_box {
  width: 460px;
  height: 360px;
  background-color: white;
  border-radius: 3px;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  position: absolute
}

</style>

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值