0到1搭建前端Vue2项目_创建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 组件
整体代码如下
系统登录
用户平台使用协议 登录 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; } }) } }}/* 登录表单 */.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%}
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需要用到// 获取 tokenexport function getToken() { return Cookies.get(TokenKey)}// 设置tokenexport function setToken(token) { return Cookies.set(TokenKey, token)}// 移除tokenexport 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: 巧妙的运用了 标签的 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 的消息提示框import router from \'@/router\'// 获取当前环境,判断当前环境是否为开发环境let isDev = process.env.NODE_ENV === \'development\';const service = axios.create({ // baseUrl 是用来配置基础路径的,以后请求就不用写这个请求路径了 // baseURL: \'/dev-api\', baseURL: isDev ? process.env.VUE_APP_BASE_DEV_API : process.env.VUE_APP_BASE_PRO_API, timeout: 6000 // 超时时间 6 秒钟})// 请求拦截器service.interceptors.request.use(config => { // 注入 token , this.$store.getters if (store.getters.token) { config.headers.Authorization = `${store.getters.token}` } return config;}, error => { // 失败执行的 Promise return Promise.reject(error)})// 响应拦截器service.interceptors.response.use(response => { // axios 默认包裹了data const { data, msg, code } = response.data; // console.log(response) if (code == 200) { return data; } else { Message.error( {type: \'error\', message: msg}) return Promise.reject(new Error(msg)) }}, // async error => {// if (error.response.status == 401) {// // 说明 token 失效了// Message.error( {type: \'error\', message: \'登录失效了\'})// await store.dispatch(\'user/loginout\') // 退出登录// router.replace(\'/login\') // 跳转到登录页// }// // console.log(\'响应拦截器error\', error)// return Promise.reject(error);// }async err => { if (err.response.status == 504 || err.response.status == 404) { Message.error({message: \'服务器被吃了⊙﹏⊙∥\'}); } else if (err.response.status == 403) { Message.error({message: \'权限不足,请联系管理员!\'}); } else if (err.response.status == 401) { // Message.error({message: err.response.data.msg}); Message.error( {type: \'error\', message: \'登录失效了\'}) const currentRoute = router.history.current.fullPath; // 当前路由路径 await store.dispatch(\'user/loginout\') // 退出登录 if (currentRoute !== \'/login\') { router.push(`/login?redirect=`+ currentRoute); } // router.replace(\'/login\') } else { if (err.response.data.msg) { Message.error({message: err.response.data.msg}); }else{ Message.error({message: \'未知错误!\'}); } } // return Promise.resolve(err);})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 组件结构如下
左侧导航
export default {}
content/index.vue 组件内容如下。
顶部区域
export default { }.header { height: 50px; background-color: skyblue;}
layout/index.vue 组件内容如下。引入左侧菜单栏和右侧内容组件