0到1搭建前端Vue2项目

1. vue-cli 创建 vue 工程

1.1  打开 cmd 命令提示符进入到存放项目的目录,在命令提示符上使用 “ vue create 项目名” 命令创建项目。

1.2 之后可以选择 vue 版本,虽然现在 vue2 已经停更,但是还是值得学习的。 

1.3 等待初始化 vue 脚手架后,会出现蓝色的两条命令,依次执行它们即可启动 vue。vue脚手架启动成功后会出现蓝色的两个网址,无论是哪个网址都可以打开页面。

1.4 浏览器输入网址后界面如下

2. 使用 elementUI

2.1 在该项目的根目录安装 elementUI , 执行以下命令即可安装

npm i element-ui -S

 2.2 在 src/main.js 中引入 elementUI 及 全局注册它:

import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';

Vue.use(ElementUI);

new Vue({
  el: '#app',
  render: h => h(App)
});

2.3 之后我在 HelloWord.vue 组件中测试 elementUI 是否可以正常使用。这里简单添加一个按钮测试。

重启项目后看到按钮出现了,说明elementUI 成功引入进来了。  

但是按钮还有一定的边距,我们可以 在assets 文件夹中添加一个全局样式清除默认样式。

3. 关闭 esline 语法检查

ESLint 是一个用于识别和报告 JavaScript 代码中问题的静态代码分析工具。它能够帮助开发者发现潜在的语法错误、代码风格问题以及不符合最佳实践的代码。但这也为我们开发带来诸多不便,如提前引入一个未使用的变量也会使得代码不能运行,因此在开发过程中我将其关闭。只需要在vue.config.js 中设置 lintOnSave: false 即可。如果想要动态判断只在开发环境关闭语法检查也很简单,只需要获取到当前的运行环境进行判断即可。

lintOnSave: process.env.NODE_ENV === 'development',  // 开发环境关闭语法检查

4. 登录模块

3.1 在 src/view 目录下新建一个 login 目录,创建login 组件

整体代码如下

<template>
    <!-- 登录页面 -->
    <div>
      <el-form ref="form" :model="loginForm" :rules="loginRules" class="loginContainer">
        <h3 class="formTitle">系统登录</h3>
        <el-form-item prop="username">
          <el-input type="text" autocomplate="false" v-model="loginForm.username" placeholder="请输入用户名"></el-input>
        </el-form-item>
        <el-form-item prop="password">
          <el-input type="password" autocomplate="false" v-model="loginForm.password" placeholder="请输入密码"></el-input>
        </el-form-item>
        <el-form-item prop="code">
          <el-input type="text" class="codeInput" autocomplate="false" v-model="loginForm.code" placeholder="点击图片更换验证码"></el-input>
          <img :src="captchaUrl">
        </el-form-item>
        <el-form-item prop="isAgree">
          <el-checkbox class="loginRember" v-model="loginForm.isAgree">用户平台使用协议</el-checkbox>
          <el-button type="primary" class="loginBtn" @click="submitForm">登录</el-button>
        </el-form-item>
      </el-form>
    </div>
</template>

<script>
export default {
    name: 'Login',
    data() {
        return {
            // 表单字段
            loginForm: {
                username: '',  // 用户名
                password: '',  // 密码
                code: '',  // 验证码
                isAgree: false,  // 用户平台使用协议
            },
            captchaUrl: '', // 验证码图片地址
            // 校验规则
            loginRules: {
                username: [{
                    required: true,  // required只能检测 null undefined ""
                    message: '请输入用户名',
                    trigger: 'blur'
                }],
                password: [{
                    required: true,
                    message: '请输入密码',
                    trigger: 'blur'
                }],
                code: [{
                    required: true,
                    message: '请输入验证码',
                    trigger: 'blur'
                }],
                isAgree: [{
                     validator: (rule, value, callback) => {
                      // rule校验规则
                      // value 校验的值
                      // callback 函数 - promise resolve reject
                      // callback() callback(new Error(错误信息))
                      value ? callback() : callback(new Error('您必须勾选用户的使用协议'))
                    }
                }]
            }
        }
    },
    methods: {
        // 登录方法
        submitForm() {
            this.$refs.form.validate((valid) => {
                if (valid) {
                    console.log('se')
                } else {
                    this.$message.error('请输入所有字段')
                    return false;
                }
            })
        }
    }
}
</script>

<style>
/* 登录表单 */
.loginContainer {
    width: 350px;
    padding: 15px 35px;
    margin: 80px auto;
    background-clip: padding-box;
    background-color: #fff;
    border-radius: 15px;
    border: 1px solid #eaeaea;
    box-shadow: 0 0 25px #cac6c6;
}
/* 表单标题 */
.formTitle {
    text-align: center;
    margin: 0 0 20px 0;
}
/* 验证码输入框 */
.codeInput {
    width: 65%;
}
/* 记住我 */
.loginRember {
    margin: 0 0 5px 0;
}
/* 登录按钮 */
.loginBtn {
    width: 100%
}
</style>

3.2 下载路由。如果是vue2项目,指定版本为3,因为现在脚手架默认的路由版本是适配vue3项目的,因此需要指定路由版本。

3.3 在src/router 目录下新建一个 index.js 文件,用于配置路由信息。

import Vue from 'vue'
import vueRouter from 'vue-router'

Vue.use(vueRouter)

// 静态路由
const constantRoutes = [
    {   
        path: '/login',
        name: 'Login',
        component: () => import('@/view/login/Login')
    }
]

const router =  new vueRouter({
    // mode: 'history',  // 两种模式, hash 、 history
    // scrollBehavior: () => ({ y: 0 }),
    routes: constantRoutes  // 默认引入静态路由
})


export default router

运行效果如下:

5. 数据持久化

1.  在 src 目录下创建一个 modules 文件夹,是存放每个模块的仓库。然后在其 modules 目录下创建一个 user.js 文件。

2. 在src/store 目录下 新建一个index.js 文件作为主文件。将 user.js 导入到 index.js 中。

import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
import user from '@/store/modules/user'

Vue.use(Vuex)

const store = new Vuex.Store({
    modules: {
        user
    },
    getters
})

export default store;

目录结构如下所示

在登录页调用派发登录接口 

登录后token已经存入进来了,但是刷新后token又立马变为初始值。这就要用到数据持久化了。 

只要一刷新token就会消失,这个没有做数据持久化处理,下面就来解决这个问题。

1. 首页下载 js-cookie

npm i js-cookie

2. 在src/utils 目录下创建一个 auth.js 文件,包含三个方法,分别是 获取、设置、移除token。

import Cookies from 'js-cookie'

const TokenKey = 'userToken';  // token 的key值,移除、添加token需要用到

// 获取 token
export function getToken() {
  return Cookies.get(TokenKey)
}

// 设置token
export function setToken(token) {
  return Cookies.set(TokenKey, token)
}

// 移除token
export function removeToken() {
  return Cookies.remove(TokenKey)
}

3.  在src/store/modules 目录下的user.js 文件引入 atuh.js 的三个方法(添加、获取、移除token)。

import { getToken, setToken, removeToken } from '@/utils/auth'

const state = {
    token: getToken()  // 从缓存中获取 token
}
const mutations = {
    // 设置token
    setToken(state, token) {
        state.token = token;
        setToken(token);  // 把 token 设置到缓存中
    },
    removeToken() {
        // 删除vuex的token
        state.token = null;
        removeToken()
    }
}
const actions = {
    // 登录
    async login(context, data) {
        // console.log(data)
        // const token = await reqLogin(data);  // 调用登录接口
        context.commit('setToken', 'token123')
    }
}
export default {
    // 开启命名空间, 以后调用 actions 的方法需要带上文件名+方法名(如 user/login)
    namespaced: true, 
    state,
    mutations,
    actions
}

登录后刷新发现vuex中的token还是存在,在浏览器控制台上的"应用"模块下找到 Cookie,发现token已经存入到本地了。这就已经完成了数据持久化。后续token如何得到,需要后端返回给前端,并不是和现在的代码一样固定写死的。

6. vue-cli 代理解决跨域

解决跨域有常见的三种方式:

1. Cors(最标准的处理):后端在返回响应的收添加特殊的响应头,不需要前端人员处理。

2. Jsonp: 巧妙的运用了 <script></script> 标签的 src 属性在引入外部资源时,不受同源策略限制的特点实现的。比较少使用,因为需要前后端共同完成,而且只能解决get请求的跨域。

3. 代理服务器:代理服务器与前端保持同源策略,这样代理服务器就可以和前端通信。而跨域问题只限制于浏览器中,代理服务器只是一个服务,所以代理服务器相当于一个前端和后端的一个桥梁,把前端的请求转发到后端。

module.exports = {
  devServer: {
    open: true,  // 启动服务时自动打开浏览器访问
    proxy: {  // 开发环境代理配置
      [process.env.VUE_APP_BASE_API]: {  // 自动获取,注意要[],在.env.development里配置的
        // 目标服务器地址,在.env.development里配置的。代理访问http://localhost:8001
        target: 'http://localhost:8083',
        ws: true,  // 用于支持websocket
        changeOrigin: true,  // 开启代理服务器
        pathRewrite:{
          // 匹配以 process.env.VUE_APP_BASE_API 获取到的值 开头的路径设置为空,
          // 假设有个地址:http://localhost:5000/api/employeeList, 不加下面语句会报 404错误,
          // 5000 服务器上只有 employeeList, 没有 /api/employeeList, 只有加上下面的语句才会只发送 /employeeList
          ['^' + process.env.VUE_APP_BASE_API]: ''  
        }
      }
    }
  }
}

7. 封装配置拦截器 

上面已经做完了表单校验工作,接下来我们需要请求后端接口实现登录功能。这里使用 axios 发送请求。为什么要封装axios呢?不封装不行吗?

  • 不封装也是可以的,只是很多组件都会用到axios发送请求,都需要导入axios,导致代码臃肿。
  • 每次发送请求都要填写完整的地址,如果需要变更请求地址,需要一个个手动改地址,后期难以维护。

1.  安装 axios。使用命令行进入到该项目的根目录。使用 npm i axios 命令安装axios

安装完毕后可以在 package.json 的 dependencies 中查看本项目安装的所有依赖 

2. 在 src/utils 目录下创建一个 request.js 文件,用于封装拦截器。这里主要分为三大部分。

  • 第一部分是封装基地址(以后请求都会带上这个基地址)+ 超时时间(请求超过这个时间还没有响应认为请求失败)。
  • 第二部分是请求拦截器;设置请求成功和请求失败的回调,设置 token
  • 第三部分是响应拦截器。
import axios from 'axios'  // 引入 axios 依赖
import store from '@/store/index'
import { Message } from 'element-ui'  // 按需引入 element 的消息提示框


// 基础路径
const service = axios.create({
   
    baseUrl: '/dev-api',
    timeout: 60000  // 超时时间 1分钟
})

// 请求拦截器
service.interceptors.request.use(config => {
    // 注入 token , this.$store.getters
    if (store.getters.token) {
        config.headers.Authorization = `User_${store.getters.token}`
    }
    return config;
}, error => {
    // 失败执行的 Promise
    return Promise.reject(error)
})

// 响应拦截器
service.interceptors.response.use(response => {
  // axios 默认包裹了data
  const { data, message, sucess } = response.data;
  if (sucess) {
    return data;
  } else {
    Message.error( {type: 'error', message: msg})
    return Promise.reject(new Error(message))
  }
}, error => {
    // console.log('响应拦截器error', error)
    Message.error( {type: 'error', message: error.message})
    return Promise.reject(error);
})

export default service;

8. axios 区分不同环境

1. 在项目的根目录上(和src目录同级,不是在src目录内),分别新建 (.env.production) 和 (.env.development) 两个文件,用于区分生产和开发环境。

# 生产环境
NODE_ENV = production

#Vue 代码中 NODE_ENV之外,所有的变量必须以VUE_APP_开头
VUE_APP_BASE_API = '/pro-api'
# 开发环境
NODE_ENV = development

#Vue 代码中 NODE_ENV之外,所有的变量必须以VUE_APP_开头
VUE_APP_BASE_API = '/dev-api'

目录位置如图所示

2. 在 src/utils 目录下找到 request.js 文件,修改基地址。

9. 主体布局(一)

1. 布局搭建

布局结构如下所示: 可以分为左右结构,右边结构又可以分为上下结构, 在elementUI官方文档的Container 容器部分可找到。

1. 在src/assets 目录下新建css目录,然后新建一个base.css文件,编写全局样式,在main.js中引入该样式。

在main中引入该样式

import '@/assets/css/base.css' 

h1,h2,h3,h4,h5,h6,p,ul,li,body {
  margin: 0;
  padding: 0;
}
ul {
  list-style: none;
}
a {
  text-decoration: none;
}
img {
  vertical-align: middle;
}
body {
  font-size: 14px;
  font-family: "微软雅黑", "Arial Narrow", Arial, sans-serif;
}

 2. 在src目录下新建views/layout 目录结构,在layout目录下新建index.vue组件用于做布局组件的总组件,在layout 目录下分别新建menu(左侧导航栏)和content(右侧内容)目录,在munu组件中新建index.vue组件做左侧侧边栏组件,在content目录下新建index.vue(内容显示区域)组件。文件结构如下所示。

menu.vue 组件结构如下

<template>
  <div>
    <h1>左侧导航</h1>
  </div>
</template>
<script>
export default {
}
</script>
<style lang="less" scoped>
  
</style>

content/index.vue 组件内容如下。

<template>
  <div>
    <!-- 顶部区域 -->
    <div class="header">
        <h3>顶部区域</h3
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值