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