搭建一个vue项目(vue+vuex+vue-router+scss+es6+element+axios)

一、安装依赖

  • 选择创建项目所在文件夹,cmd进入该项目 全局安装vue-cli
  • npm install -g @vue/cli //全局安装vue-cli
  • npm uninstall -g vue-cli //卸载全局vue-cli
    在这里插入图片描述
  • 查看是否安装成功
    键入vue --version ,出现以下版本号,则代表成功
    搭建一个vue项目(vue+vuex+vue-router+scss+es6+element+axios)_第1张图片

二、创建项目

vue create vue-test // vue-test为项目名称
搭建一个vue项目(vue+vuex+vue-router+scss+es6+element+axios)_第2张图片
搭建一个vue项目(vue+vuex+vue-router+scss+es6+element+axios)_第3张图片
分别代表的是:

  1. 选择模式:手动选择配置 选择需要的配置:babel(es6编译)、vue-router(路由)、vuex(状态机)、css
  2. pre-porcessors(css预编译器,我后面选择的scss)、linter(语法、拼写等错误检查)
  3. 是否选择history路由模式:否 选择css预编译器:dart-sass编译,即scss 使用的错误检查机制:formatter
  4. config:Prettier 检查错误的条件:保存的时候 配置项放置位置:package.json
  5. 是否保存以上配置在以后项目中使用:否
  • 切换到myblog文件夹,执行cd mybolg => npm run serve启动项目

三、创建项目文件

(一)引入axios
  • 安装axios
    在这里插入图片描述
  • 创建token存储文件
    1、安装js-cookie,用来存储token
    在这里插入图片描述
    2、在项目下创建文件夹utils并在utils下创建auth.js,编写读写token方法
    搭建一个vue项目(vue+vuex+vue-router+scss+es6+element+axios)_第4张图片
    搭建一个vue项目(vue+vuex+vue-router+scss+es6+element+axios)_第5张图片
  • 在utils文件夹下创建request_http.js用于拦截配置
import qs from 'qs';
import axios from "axios";
// import store from "@/store";
import { getAccessToken } from "@/utils/auth";
import { notification } from "ant-design-vue";

export const http = (function http() {
  // 创建 axios 实例
  const request = axios.create({
    // API 请求的默认前缀
    baseURL: '/api',
    timeout: 10000, // 请求超时时间,10s
    transformRequest: [ // 处理接口返回数据格式
      (data, config) => {
          if (!data) {
              return data;
          }

          // 如果是Form表单就直接跳过JSON转换
          if (data instanceof FormData) {
              // 如果上传包含文件, 更改 Content-Type
              // if(data.has('file')) {
              //   config['Content-Type'] = 'multipart/form-data';
              //   config['put']['Content-Type'] = 'multipart/form-data';
              //   config['post']['Content-Type'] = 'multipart/form-data';
              //   config['patch']['Content-Type'] = 'multipart/form-data';
              // }
              return data;
          }

          // 序列化data
          // if (data instanceof Object) {
          //   for (let key in data) {
          //     if (data.hasOwnProperty(key) && !data[key]) {
          //       delete data[key];
          //     }
          //   }
          //   return JSON.stringify(data);
          // }
          // return data;
          return qs.stringify(data, {
              arrayFormat: 'brackets',
              strictNullHandling: false
          });
      }
  ]
  });
  
  // 异常拦截处理器
  const errorHandler = errorRep => {
    if (errorRep.response) {
      const {
          data: { error },
          status
        } = errorRep.response,
        // 从 coockie 获取 token
        token = getAccessToken();
      // 身份验证失败
      if (status === 401) {
        notification.error({
          message: "身份验证",
          description: "登录过期,需要重新验证身份"
        });
        // 如果登录了,则退出登录
        if (token) {
          // store.dispatch("Logout").then(() => {
          //   setTimeout(() => {
          //     window.location.reload();
          //   }, 1500);
          // });
        }
      }
  
      if (status === 403) {
        notification.error({
          message: "拒绝访问",
          description: error.message
        });
      } else {
        notification.error({
          message: "错误消息",
          description: error.message
        });
      }
      return Promise.reject(error);
    }
    return Promise.reject(errorRep);
  };
  
  // 请求前拦截
  request.interceptors.request.use(config => {
    const token = getAccessToken();
    // 如果 token 存在
    // 让每个请求携带自定义 token 请根据实际情况自行修改
    if (token) {
      config.headers["authorization"] = `Bearer ${token}`;
    }
    return config;
  }, errorHandler);
  
  // 请求后拦截
  request.interceptors.response.use(response => {
    return response.data;
  }, errorHandler);
})();

// 配置http请求方式到Window全局空间中
window.http = http;

let VueHttp = function(vue, ins) {
    if (VueHttp.installed) {
        return;
    }
    VueHttp.installed = true;

    if (!axios) {
        console.error('You have to install axios');
        return;
    }

    vue['axios'] = axios;

    Object.defineProperties(vue.prototype, {
        axios: {
            get() {
                return axios;
            }
        },

        $http: {
            get() {
                return http;
            }
        }
    });
};

export { VueHttp };
export default http;

  • 配置与后面服务器接口请求地址,在项目文件夹下创建vue.config.js文件
/* eslint-disable indent */
// nodejs中的path模块
const path = require("path");

/**
 * 拆分本地API代理接口
 * 通过不同的启动方式, 可以直接运行线上的测试接口
 */
const ApiLocalProxyList = {
      '/api/common': {
        target: 'http://localhost:6002',
        pathRewrite: {
          '^/api/common': '',
        },
        secure: false,
      },
      '/api/design': {
        target: 'http://localhost:6001',
        pathRewrite: {
          '^/api/design': '',
        },
        secure: false,
      },
};

/**
 * 线上代理接口
 */
const ApiOnlineProxyList = {
    "/api": {
        target: "http://xxx.xxx.xxx.xxx:xxxx",
        // pathRewrite: {
        //   '^/api': ''
        // },
        secure: false,
    },
};

module.exports = {
    publicPath: "/",
    assetsDir: "assets",
    productionSourceMap: false,
    chainWebpack: (config) => {
        // 修改入口文件
        config
            .entry("app")
            .clear()
            .add("./src/main.js")
            .end()
            .resolve.modules.prepend(path.resolve(__dirname, "./src"))
            .end()
            .alias.set("vue$", "vue/dist/vue.js")
            .set("resize-detector$", "resize-detector/dist/index.js")
            .end()
            .end()
            .plugin("define")
            .tap((args) => {
                let options = args[0];
                let rst = [
                    Object.assign({
                        options,
                        // 增加 环境变量 __DEV__
                        __DEV__: process.env.NODE_ENV !== "production",
                    }),
                ];
                return rst;
            })
            .end()
            .plugin("html")
            .tap((args) => {
                let options = args[0];
                return [
                    Object.assign({
                        options,
                        template: "public/index.html",
                        minify: {
                            removeComments: true,
                            collapseWhitespace: true,
                            removeAttributeQuotes: true,
                        },
                    }),
                ];
            })
            .end()
            .plugin("copy")
            .tap((args) => {
                let options = args[0];
                if (!options) {
                    return args;
                }
                options = [
                    {
                        from: path.resolve(__dirname, "./public"),
                        to: path.resolve(__dirname, `dist/public`),
                        ignore: ["index.html", ".DS_Store"],
                    },
                ];

                return [options];
            });
    },
    devServer: {
        host: "0.0.0.0",
        contentBase: [path.join(__dirname)],
        port: 8072,     //项目启动端口
        proxy: process.env.online ? ApiOnlineProxyList : ApiLocalProxyList,
    },
};

  • 在src下建api文件夹,再建一个js用于接口测试
    搭建一个vue项目(vue+vuex+vue-router+scss+es6+element+axios)_第6张图片
(二)vuex常用设置
  • 在store文件夹下修改index.js用于存放用户信息。
import Vue from 'vue';
import Vuex from 'vuex';
import http from "@/utils/request_http";
import { setAccessToken, removeAccessToken } from "@/util/auth";
Vue.use(Vuex);

export const MU_UPATE_AUTH = 'MU_UPATE_AUTH';

export default new Vuex.Store({
    state: { //对象数据
        auth: {
            code: -101, // 没有发送用户信息请求
            loading: false
        },
    },
    mutations: { //操作变更state数据
        [MU_UPATE_AUTH](state, value) {
            if (value && value.isLogin) {
                state.auth = value;
                setAccessToken(value.token);
            } else {
                // 传空值时将登录状态初始化
                state.auth = {
                    code: -101,
                    loading: false,
                };
                removeAccessToken();
            }
        }
    },
    actions: { //触发向上回调到mutations
        [MU_UPATE_AUTH](context) {
            const req = http.post('/api/common/Login/getAuthInfo').then(    // 获取系统登录状态
                function(res) {
                    let roles = res.code > 0 ? {} : {}; //根据业务处理,没有可以去掉

                    let auth = {
                        loading: false,
                        token: res.token,
                        isLogin: res.code > 0, // true:已经登录   false: 未登录
                        ...res,
                        ...roles
                    };
                    context.commit(MU_UPATE_AUTH, auth);
                    return auth;
                },
                function() {
                    console.log('请求失败处理');
                }
            );

            // 保存登录状态
            context.commit(MU_UPATE_AUTH, { code: -101, loading: true, request: req });
            return req;
        }
    }
});
(三)route拦截设置
  • 打开router文件夹下的index.js 设置全局拦截路由
    搭建一个vue项目(vue+vuex+vue-router+scss+es6+element+axios)_第7张图片
    路由拦截代码
import Vue from "vue";
import VueRouter from "vue-router";
import Home from "../views/Home.vue";

Vue.use(VueRouter);

const routes = [
  {
    path: "/",
    name: "Home",
    component: Home,
  },
  {
    path: "/about",
    name: "About",
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () =>
      import(/* webpackChunkName: "about" */ "../views/About.vue"),
  },
];

const router = new VueRouter({
  routes,
});

// 全局拦截路由
router.beforeEach((to, from, next) => {
  // 获取登录状态
  const auth = store.state.auth;
  // 验证是否需要权限
  to.matched.forEach((record) => {
      if (record.meta.requiresAuth) {
          // 没有用户信息
          // 常见情况:
          // 1.用户强制刷新了页面, 需要重新发送用户登录信息请求
          // 2.用户没有登录直接拷贝的地址粘贴
          if (auth.code === -101) {
              // 之前没有加载用户信息
              let request = auth.request || store.dispatch(MU_UPATE_AUTH);
              request.then((rst) => {
                  // 路由之后
                  // 确定没有登录
                  next(afterAuth(to, rst, record) || undefined);
              });
          } else {
              // 路由之后
              next(afterAuth(to, auth, record) || undefined);
          }
      } else {
          // 不需要验证的路由
          next(); // 确保一定要调用 next()
      }
  });
});

/**
 * 路由跳转验证
 * 没有登录或者用户类型不匹配
 *
 * @param {IAuth} auth
 * @param {RouteRecord} record
 * @returns {boolean}
 */
 function requiresAuth(auth, record) {
  // 返回需要验证的情况:
  // 1.没有登录
  // 2.用户类型定义并且用户类型和对应的路由要求不匹配

  // 还没登录、跳转到登录页
  if (auth.code < 0) {
      return true;
  }

  // meta设置了userType字段, 需要验证身份
  if (record.meta.userType !== undefined) {
      if (typeof record.meta.userType === "number") {
          // 只需一种角色进入
          // 不满足、跳转登录页
          return auth.userType !== record.meta.userType;
      } else if (record.meta.userType instanceof Array) {
          // 允许多重角色
          // 不满足、跳转登录页
          return record.meta.userType.indexOf(auth.userType) === -1;
      } else {
          // 其他情况均跳转到登录页
          return true;
      }
  } else {
      // 没有设置设置meta的userType字段
      // 放过进入下一个路由
      return false;
  }
}

function afterAuth(to, auth, record) {
  // 已经请求过用户信息
  if (requiresAuth(auth, record)) {
      // 普通页面调转,没有丢失用户登录信息
      return {
          name: "Home", // 原来是login
          query: { redirect: to.fullPath },
      };
  } else {
      return false;
  }
}

const originalPush = Router.prototype.push;

Router.prototype.push = function push(location) {
    return originalPush.call(this, location).catch((err) => err);
};

export default router;

  • 下面我们可以改进一下添加路由根据定义系统角色来判断是否有权限访问
    (1)首先在api文件夹下面添加一个role.ts文件
    搭建一个vue项目(vue+vuex+vue-router+scss+es6+element+axios)_第8张图片
    store文件夹下的index.js路由文件修改
    搭建一个vue项目(vue+vuex+vue-router+scss+es6+element+axios)_第9张图片
(四)引入Element
  • 安装Element(npm 安装)
npm i element-ui -S
  • 在 main.js 中写入以下内容
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);

搭建一个vue项目(vue+vuex+vue-router+scss+es6+element+axios)_第10张图片
之后我们的页面就可以使用Element的组件来渲染页面了
搭建一个vue项目(vue+vuex+vue-router+scss+es6+element+axios)_第11张图片
Element官网地址:https://element.eleme.cn/#/zh-CN/component/installation

  • 安装scss 跟 scss-loader
cnpm install scss-loader scss --save
  • 安装node-sass 跟 sass-loader
cnpm install sass-loader node-sass --save

在页面文件即可使用

<style lang="scss" scoped>
.aaa{
  .bbb {
    color: red;
  }
}
</style>

参考文章:https://blog.csdn.net/liuxin00020/article/details/106617524

你可能感兴趣的:(搭建完整的vue项目,vue,前端)