day1
项目简介
目标:通过学习《Vue 大事件》项目,巩固 Vue 基础中所学的知识,掌握从 0 到 1 开发企业级项目的能力。
项目演示地址:
www.escook.cn:8086/ev/#/login
接口文档: www.showdoc.com.cn/14254575969…
接口根路径修改成: www.liulongbin.top:3008
所需的前置知识
-
Vue 基础
-
vue-cli、指令、组件、路由、vuex
-
axios
-
baseURL、拦截器
-
element-ui
-
安装与配置、常用的组件
-
npm 与 模块化
- 能使用 npm 维护项目中的依赖包
- ES6 模块化语法
学完本项目可以掌握的能力
- 登录注册的业务实现流程
- 在项目中使用 vuex 管理全局共享的数据
- element-ui 组件在实际开发中的应用
- 文件上传、富文本编辑器在 Vue 项目中的应用
项目初始化
基于 vue-cli 初始化 Vue2 模板的项目
目的:为后面的项目开发做准备。
初始化 vue-cli 的核心步骤:
-
Manually select features
- (*) Choose Vue version
- (*) Babel
- ( ) TypeScript
- ( ) Progressive Web App (PWA) Support
- (*) Router
- (*) Vuex
- (*) CSS Pre-processors
- (*) Linter / Formatter
- ( ) Unit Testing
- ( ) E2E Testing
-
Choose a version of Vue.js that you want to start the project with (Use arrow keys)
- 2.x
- 3.x
-
Use history mode for router? (Requires proper server setup for index fallback in production) (Y/n)
-
n
-
Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): (Use arrow keys)
- Sass/SCSS (with dart-sass)
- Sass/SCSS (with node-sass)
- Less
- Stylus
-
Pick a linter / formatter config: (Use arrow keys)
- ESLint + Airbnb config
- ESLint + Standard config
- ESLint + Prettier
-
Pick additional lint features: (Press to select, to toggle all, to invert selection)
- (*) Lint on save
- ( ) Lint and fix on commit
-
Where do you prefer placing config for Babel, ESLint, etc.? (Use arrow keys)
- In dedicated config files
- In package.json
-
Save this as a preset for future projects? (y/N)
-
N
首次运行项目
目的:检查项目是否初始化成功。
- cd 项目根目录
- npm run serve
梳理项目结构
目的:为项目开发做准备,把不需要的代码、文件删除掉。
-
重置
src/App.vue
组件中的代码1 <template> 2 <div>App 根组件</div> 3 </template> 4 5 <script> 6 export default { 7 name: 'App' 8 } 9 </script> 10 11 <style lang="less" scoped></style> 12
-
重置
src/router/index.js
路由模块中的代码1 import Vue from 'vue' 2 import VueRouter from 'vue-router' 3 4 Vue.use(VueRouter) 5 6 const routes = [] 7 8 const router = new VueRouter({ 9 routes 10 }) 11 12 export default router 13
-
清空
src/components
目录和src/views
目录。 -
把资料目录下的 images 文件夹(项目中需要用到的图片)和 global.less(项目中用到的全局样式),复制粘贴到 src/assets 目录下。
配置 element-ui
目的:为了提高页面布局的开发效率,因为 element-ui 提供了很多常用的 UI 组件。
参照 element-ui 的官方文档,进行安装、配置、使用:element.eleme.io/#/zh-CN/com…
配置 axios
目的:为后面请求数据做准备。
-
安装 axios
1 npm i axios -S
-
在 main.js 中导入 axios
1 import axios from 'axios'
-
设置 axios 的请求根路径
1 axios.defaults.baseURL = 'http://www.liulongbin.top:3008'
-
把 axios 挂载到 Vue.prototype 上
1 Vue.prototype.$http = axios
把项目上传到码云仓库
目标:能够使用远程仓库管理本地项目
- 把本地项目的 master 分支进行 commit 提交
- 在码云中新建空白远程仓库
- 把本地仓库的 master 分支上传到码云仓库中
注册功能
通过路由实现登录注册的切换
目标:复习路由的基本用法
-
在
src/views
目录下,新建Reg/Reg.vue
组件和Login/Login.vue
组件:-
Reg.vue 组件:
1 <template> 2 <div> 3 Reg.vue 4 </div> 5 </template> 6 7 <script> 8 export default { 9 name: 'Reg' 10 } 11 </script> 12 13 <style lang="less" scoped></style> 14
-
Login.vue 组件:
1 <template> 2 <div> 3 Login.vue 4 </div> 5 </template> 6 7 <script> 8 export default { 9 name: 'Login' 10 } 11 </script> 12 13 <style lang="less" scoped></style> 14
-
-
在
src/router/index.js
模块中,导入登录注册组件,并声明对应的路由规则:1 import Vue from 'vue' 2 import VueRouter from 'vue-router' 3 4 // 导入登录注册组件 5 import Reg from '@/views/Reg/Reg.vue' 6 import Login from '@/views/Login/Login.vue' 7 8 Vue.use(VueRouter) 9 10 // 声明路由规则 11 const routes = [ 12 { path: '/reg', component: Reg }, 13 { path: '/login', component: Login } 14 ] 15 16 const router = new VueRouter({ 17 routes 18 }) 19 20 export default router 21
-
在
App.vue
组件中,定义<router-view>
如下:1 <template> 2 <router-view></router-view> 3 </template> 4 5 <script> 6 export default { 7 name: 'App' 8 } 9 </script> 10 11 <style lang="less" scoped></style> 12
渲染注册组件的基础布局
目标:为渲染注册功能的表单做准备
-
在
src/assets
目录下,新建全局样式表global.less
:1 html, 2 body { 3 margin: 0; 4 padding: 0; 5 height: 100%; 6 } 7 8 .el-input__inner, 9 .el-button, 10 .el-link { 11 font-size: 12px; 12 } 13
-
在
src/main.js
中导入全局样式表:1 // 导入全局样式表 2 import '@/assets/global.less'
-
初始化注册页面的基础布局,并美化样式:
1 <template> 2 <!-- 注册页面的整体盒子 --> 3 <div class="reg-container"> 4 <!-- 注册的盒子 --> 5 <div class="reg-box"> 6 <!-- 标题的盒子 --> 7 <div class="title-box"></div> 8 <!-- 注册的表单区域 --> 9 </div> 10 </div> 11 </template> 12 13 <script> 14 export default { 15 name: 'Reg' 16 } 17 </script> 18 19 <style lang="less" scoped> 20 .reg-container { 21 background: url('../../assets/images/login_bg.jpg') center; 22 background-size: cover; 23 height: 100%; 24 25 .reg-box { 26 width: 400px; 27 height: 335px; 28 background-color: #fff; 29 border-radius: 3px; 30 position: absolute; 31 left: 50%; 32 top: 50%; 33 transform: translate(-50%, -50%); 34 35 .title-box { 36 height: 60px; 37 background: url('../../assets/images/login_title.png') center no-repeat; 38 } 39 } 40 } 41 </style> 42
渲染注册表单
-
基于 element-ui 组件库,渲染出登录表单的 DOM 结构:
1 <!-- 注册的表单区域 --> 2 <el-form :model="regForm" :rules="regRules" ref="regRef"> 3 <!-- 用户名 --> 4 <el-form-item prop="username"> 5 <el-input v-model="regForm.username" placeholder="请输入用户名"></el-input> 6 </el-form-item> 7 <!-- 密码 --> 8 <el-form-item prop="password"> 9 <el-input v-model="regForm.password" type="password" placeholder="请输入密码"></el-input> 10 </el-form-item> 11 <!-- 确认密码 --> 12 <el-form-item prop="repassword"> 13 <el-input v-model="regForm.repassword" type="password" placeholder="请再次确认密码"></el-input> 14 </el-form-item> 15 <el-form-item> 16 <el-button type="primary" class="btn-reg">注册</el-button> 17 <el-link type="info">去登录</el-link> 18 </el-form-item> 19 </el-form>
-
定义 data 数据项:
1 export default { 2 name: 'Reg', 3 data() { 4 return { 5 // 注册表单的数据对象 6 regForm: { 7 username: '', 8 password: '', 9 repassword: '' 10 }, 11 // 注册表单的验证规则对象 12 regRules: {} 13 } 14 } 15 }
-
美化样式:
1 .reg-container { 2 background: url('../../assets/images/login_bg.jpg') center; 3 background-size: cover; 4 height: 100%; 5 6 .reg-box { 7 width: 400px; 8 height: 335px; 9 background-color: #fff; 10 border-radius: 3px; 11 position: absolute; 12 left: 50%; 13 top: 50%; 14 transform: translate(-50%, -50%); 15 padding: 0 30px; 16 box-sizing: border-box; 17 18 .title-box { 19 height: 60px; 20 background: url('../../assets/images/login_title.png') center no-repeat; 21 } 22 23 .btn-reg { 24 width: 100%; 25 } 26 } 27 }
实现注册表单的校验
目标:在项目开发中,能够使用 Form 表单的必填项校验、正则验证和自定义校验规则。
-
在
regRules
中,定义用户名的校验规则:1 username: [ 2 { required: true, message: '请输入用户名', trigger: 'blur' }, 3 { pattern: /^[a-zA-Z0-9]{1,10}$/, message: '用户名必须是1-10的字母数字', trigger: 'blur' } 4 ],
-
在
regRules
中,定义密码的校验规则:1 password: [ 2 { required: true, message: '请输入密码', trigger: 'blur' }, 3 { pattern: /^\S{6,15}$/, message: '密码必须是6-15的非空字符', trigger: 'blur' } 4 ],
-
在
regRules
中,定义确认密码的校验规则:1 repassword: [ 2 { required: true, message: '请再次输入密码', trigger: 'blur' }, 3 { pattern: /^\S{6,15}$/, message: '密码必须是6-15的非空字符', trigger: 'blur' }, 4 { validator: samePwd, trigger: 'blur' } 5 ]
并在,在 data 函数中声明自定义校验规则
samePwd
如下:1 data() { 2 // 验证密码是否相同 3 const samePwd = (rule, value, callback) => { 4 if (value !== this.regForm.password) { 5 // 如果验证失败,则调用 回调函数时,指定一个 Error 对象。 6 callback(new Error('两次输入的密码不一致!')) 7 } else { 8 // 如果验证成功,则直接调用 callback 回调函数即可。 9 callback() 10 } 11 } 12 return { ... } 13 }
实现注册功能
目标:理解注册功能的实现步骤,今后在实际开发中,能够独自实现注册的功能
-
点击注册按钮时,进行表单的预验证:
1 <el-button type="primary" class="btn-reg" @click="regNewUser">注册</el-button>
声明点击事件处理函数:
1 methods: { 2 // 注册新用户 3 regNewUser() { 4 // 进行表单预验证 5 this.$refs.regRef.validate(valid => { 6 if (!valid) return 7 console.log('ok') 8 }) 9 } 10 }
-
表单校验通过之后,发起注册用户的请求:
1 // 注册新用户 2 regNewUser() { 3 // 进行表单预验证 4 this.$refs.regRef.validate(async valid => { 5 if (!valid) return 6 // 1. 发起注册请求 7 const { data: res } = await this.$http.post('/api/reg', this.regForm) 8 // 2. 注册失败,提示用户 9 if (res.code !== 0) return this.$message.error(res.message) 10 // 3. 注册成功,提示用户 11 this.$message.success(res.message) 12 // 4. 跳转到登录页面 13 this.$router.push('/login') 14 }) 15 }
reg 分支的合并与提交
-
把 reg 分支进行本地的提交:
1 git add . 2 git commit -m "完成了注册功能的开发"
-
把本地的 reg 分支,推送到码云仓库:
1 git push -u origin reg
-
把本地的 reg 分支,合并到本地的 master 分支:
1 git checkout master 2 git merge reg
-
把本地最新的 master 分支推送到码云的 master 分支:
1 git push
-
删除本地的 reg 分支,并且新建 login 分支,准备开发登录功能:
1 git branch -d reg 2 git checkout -b login
登录功能
快速绘制登录组件的基础布局
1 <template> 2 <!-- 登录页面的整体盒子 --> 3 <div class="login-container"> 4 <!-- 登录的盒子 --> 5 <div class="login-box"> 6 <!-- 标题的盒子 --> 7 <div class="title-box"></div> 8 <!-- 登录的表单区域 --> 9 <el-form :model="loginForm" :rules="loginRules" ref="loginRef"> 10 <!-- 用户名 --> 11 <el-form-item prop="username"> 12 <el-input v-model="loginForm.username" placeholder="请输入用户名" maxlength="10" minlength="1"></el-input> 13 </el-form-item> 14 <!-- 密码 --> 15 <el-form-item prop="password"> 16 <el-input 17 v-model="loginForm.password" 18 type="password" 19 placeholder="请输入密码" 20 maxlength="15" 21 minlength="6" 22 ></el-input> 23 </el-form-item> 24 <el-form-item> 25 <el-button type="primary" class="btn-login">登录</el-button> 26 <el-link type="info">去注册</el-link> 27 </el-form-item> 28 </el-form> 29 </div> 30 </div> 31 </template> 32 33 <script> 34 export default { 35 name: 'Login', 36 data() { 37 return { 38 // 登录表单的数据对象 39 loginForm: { 40 username: '', 41 password: '' 42 }, 43 // 登录表单的验证规则对象 44 loginRules: { 45 username: [ 46 { required: true, message: '请输入用户名', trigger: 'blur' }, 47 { pattern: /^[a-zA-Z0-9]{1,10}$/, message: '用户名必须是1-10的字母数字', trigger: 'blur' } 48 ], 49 password: [ 50 { required: true, message: '请输入密码', trigger: 'blur' }, 51 { pattern: /^\S{6,15}$/, message: '密码必须是6-15的非空字符', trigger: 'blur' } 52 ] 53 } 54 } 55 } 56 } 57 </script> 58 59 <style lang="less" scoped> 60 .login-container { 61 background: url('../../assets/images/login_bg.jpg') center; 62 background-size: cover; 63 height: 100%; 64 65 .login-box { 66 width: 400px; 67 height: 270px; 68 background-color: #fff; 69 border-radius: 3px; 70 position: absolute; 71 left: 50%; 72 top: 50%; 73 transform: translate(-50%, -50%); 74 padding: 0 30px; 75 box-sizing: border-box; 76 77 .title-box { 78 height: 60px; 79 background: url('../../assets/images/login_title.png') center no-repeat; 80 } 81 82 .btn-login { 83 width: 100%; 84 } 85 } 86 } 87 </style> 88
实现登录注册页面的切换
目标:使用编程式导航实现路由的切换
-
在
Login.vue
组件中,为去注册按钮绑定点击事件处理函数如下:1 <el-link type="info" @click="$router.push('/reg')">去注册</el-link>
-
在
Reg.vue
组件中,为去登录按钮绑定点击事件处理函数如下:1 <el-link type="info" @click="$router.push('/login')">去登录</el-link>
点击按钮发起登录请求
目标:能够知道如何发起登录的请求
-
为登录按钮绑定点击事件处理函数如下:
1 <el-button type="primary" class="btn-login" @click="login">登录</el-button>
-
在
methods
中,定义事件处理函数如下:1 // 实现登录功能 2 login() { 3 this.$refs.loginRef.validate(async valid => { 4 if (!valid) return 5 // 1. 发起登录的请求 6 const { data: res } = await this.$http.post('/api/login', this.loginForm) 7 // 2. 登录失败 8 if (res.code !== 0) return this.$message.error(res.message) 9 // 3. 登录成功 10 this.$message.success(res.message) 11 }) 12 }
把登录成功后的 token 记录到 vuex 中
目标:能够理解为什么要把 token 记录到 vuex 中,以及如何实现进行记录。
-
在
src/store/index.js
中,定义token
数据,以及更新 token 的updateToken
mutation 函数:1 export default new Vuex.Store({ 2 state: { 3 // 1. 用来存储登录成功之后,得到的 token 4 token: '' 5 }, 6 mutations: { 7 // 2. 更新 token 的 mutation 函数 8 updateToken(state, newToken) { 9 state.token = newToken 10 } 11 } 12 })
-
在
Login.vue
组件中,登录成功之后,调用 vuex 中的updateToken
函数:1 // 把 token 记录到 vuex 中 2 this.$store.commit('updateToken', res.token)
持久化存储 vuex 中的数据
目标:能够理解为什么要持久化存储 vuex 中的数据,以及如何实现持久化存储。
-
运行如下的命令,安装持久化存储 vuex 中数据的第三方包:
1 npm install --save vuex-persistedstate@3.2.1
-
在
src/store/index.js
模块中,导入并配置vuex-persistedstate
包:1 import Vue from 'vue' 2 import Vuex from 'vuex' 3 // 1. 导入包 4 import createPersistedState from 'vuex-persistedstate' 5 6 Vue.use(Vuex) 7 8 export default new Vuex.Store({ 9 // 2. 配置为 vuex 的插件 10 plugins: [createPersistedState()], 11 state: { 12 token: '' 13 }, 14 mutations: { 15 updateToken(state, newToken) { 16 state.token = newToken 17 } 18 } 19 }) 20
登录成功后跳转到后台主页
-
在
Login.vue
组件中,登录成功时,通过编程式导航 API,跳转到后台主页:1 // 实现登录功能 2 login() { 3 this.$refs.loginRef.validate(async valid => { 4 if (!valid) return 5 // 1. 发起登录的请求 6 const { data: res } = await this.$http.post('/api/login', this.loginForm) 7 // 2. 登录失败 8 if (res.code !== 0) return this.$message.error(res.message) 9 // 3. 登录成功 10 this.$message.success(res.message) 11 // 4. 把 token 记录到 vuex 中 12 this.$store.commit('updateToken', res.token) 13 // 5. 登录成功之后,跳转到后台主页 14 this.$router.push('/') 15 })
-
在
src/views
目录下,新建Main/Main.vue
后台主页组件:1 <template> 2 <el-container class="main-container"> 3 <!-- 头部区域 --> 4 <el-header> 5 <!-- 左侧的 logo --> 6 <img src="../../assets/images/logo.png" alt="" /> 7 <!-- 右侧的菜单 --> 8 <el-menu 9 class="el-menu-top" 10 mode="horizontal" 11 background-color="#23262E" 12 text-color="#fff" 13 active-text-color="#409EFF" 14 > 15 <el-submenu index="1"> 16 <template slot="title"> 17 <!-- 头像 --> 18 <img src="../../assets/logo.png" alt="" class="avatar" /> 19 <span>个人中心</span> 20 </template> 21 <el-menu-item index="1-1"><i class="el-icon-s-operation"></i>基本资料</el-menu-item> 22 <el-menu-item index="1-2"><i class="el-icon-camera"></i>更换头像</el-menu-item> 23 <el-menu-item index="1-3"><i class="el-icon-key"></i>重置密码</el-menu-item> 24 </el-submenu> 25 <el-menu-item index="2"><i class="el-icon-switch-button"></i>退出</el-menu-item> 26 </el-menu> 27 </el-header> 28 <el-container> 29 <!-- 侧边栏区域 --> 30 <el-aside width="200px">Aside</el-aside> 31 <el-container> 32 <!-- 页面主体区域 --> 33 <el-main> 34 Main.vue后台主页 35 </el-main> 36 <!-- 底部 footer 区域 --> 37 <el-footer>© www.itheima.com - 黑马程序员</el-footer> 38 </el-container> 39 </el-container> 40 </el-container> 41 </template> 42 43 <script> 44 export default { 45 name: 'Main' 46 } 47 </script> 48 49 <style lang="less" scoped> 50 .main-container { 51 height: 100%; 52 .el-header, 53 .el-aside { 54 background-color: #23262e; 55 } 56 .el-header { 57 padding: 0; 58 display: flex; 59 justify-content: space-between; 60 } 61 .el-main { 62 overflow-y: scroll; 63 height: 0; 64 background-color: #F2F2F2; 65 } 66 .el-footer { 67 background-color: #eee; 68 font-size: 12px; 69 display: flex; 70 justify-content: center; 71 align-items: center; 72 } 73 } 74 75 .avatar { 76 border-radius: 50%; 77 width: 35px; 78 height: 35px; 79 background-color: #fff; 80 margin-right: 10px; 81 object-fit: cover; 82 } 83 </style> 84
-
在
src/router/index.js
路由模块中,导入并声明后台主页的路由规则:1 // 1. 导入后台主页组件 2 import Main from '@/views/Main/Main.vue' 3 4 const routes = [ 5 { path: '/reg', component: Reg }, 6 { path: '/login', component: Login }, 7 // 2. 后台主页的路由规则 8 { path: '/', component: Main } 9 ]
退出登录
目标:在实际开发中,能够根据思路实现退出登录的功能
-
点击退出按钮时,提示用户是否退出登录:
1 <el-menu-item index="2" @click="logout"><i class="el-icon-switch-button"></i>退出</el-menu-item>
在 methods 中定义
logout
函数如下:1 // 退出登录 2 logout() { 3 // 询问用户是否退出登录 4 this.$confirm('您确认退出登录吗?', '提示', { 5 confirmButtonText: '确定', 6 cancelButtonText: '取消', 7 type: 'warning' 8 }) 9 .then(() => { 10 // TODO:执行退出登录的操作 11 }) 12 .catch(err => err) 13 }
-
在步骤 2 的 .then 中,清空 token,并跳转到登录页面:
1 // 退出登录 2 logout() { 3 this.$confirm('您确认退出登录吗?', '提示', { 4 confirmButtonText: '确定', 5 cancelButtonText: '取消', 6 type: 'warning' 7 }) 8 .then(() => { 9 // 1. 清空 token 10 this.$store.commit('updateToken', '') 11 // 2. 跳转到登录页面 12 this.$router.push('/login') 13 }) 14 .catch(err => err) 15 }
\
-
-
-
-
-
\