Vue基础-Day06

前端工程化

基本概念介绍

软件工程:可行性分析、需求分析、设计(概要设计、详细设计)、软件开发、测试、实施(运维) 前端工程化:独立的设计、独立的开发;独立测试;独立运维

为了实现更加方便的前端工程化,Vue的技术栈提供了一个非常方便的工具:脚手架

脚手架可以非常方便快速的让前端开发人员基于前端工程化的模式进行项目开发。

总结:什么是前端工程化?前端有一套独立的设计、开发、测试、打包、上线、运维…一整套操作流程 如何快速实现这套流程?基于一套现有的工具:脚手架

脚手架基本使用

目标:熟悉脚手架用法

Vue CLI 致力于将 Vue 生态中的工具基础标准化。它确保了各种构建工具能够基于智能的默认配置即可平稳衔接,这样你可以专注在撰写应用上,而不必花好几天去纠结配置的问题

  • 安装脚手架(安装工具)
  1. npm install -g @vue/cli
  • 查看版本号
  1. # 如果看到了版本号就证明安装成功了
  2. vue --version
  • 基于脚手架创建项目(使用工具创建项目)
  1. # project-demo 为项目的名称
  2. vue create mydemo
  • 启动项目
  1. # 切换到项目目录
  2. cd mydemo
  3. # 启动项目
  4. npm run serve
  • 浏览器地址栏输入如下网址,如果能看到页面效果,证明项目已经启动成功
  1. http://localhost:8080/

总结:

  1. 安装脚手架工具
  2. 使用这个工具创建项目
  3. 运行项目

脚手架项目结构分析

目标:熟悉脚手架创建项目代码的基本结构

  • 项目目录结构说明
  1. |-public -----------------------------------------------单页面
  2. |-src --------------------------------------------------项目源代码
  3. |-assets -------------------------------------------项目用到的相关资源
  4. |-components ---------------------------------------Vue组件
  5. |-App.vue ------------------------------------------项目跟组件
  6. |-main.js ------------------------------------------项目入口文件
  7. |-.gitignore -------------------------------------------git配置文件(忽略文件管理)
  8. |-babel.config.js --------------------------------------babel配置文件(控制ES6编译)
  9. |-package.json -----------------------------------------项目包管理文件
  10. |-README.md --------------------------------------------项目说明文档

总结:熟悉脚手架创建的项目的目录结构

ES6模块化开发

模板:熟悉ES6模块化规则 CommonJS模块化规范

  1. 导出 module.exports = {}
  2. 导入 require()

ES6模块化规范

  1. 导出 export
  2. 导入 import
  • 基本的导入导出
  1. // 导出一个变量
  2. export const info = 'hello'
  3. // 导出一个函数
  4. export function showInfo () {
  5. console.log('nihao')
  6. }
  1. // 导入ES6模块的成员
  2. // 单个成员的导入(对象的解构赋值用法)
  3. import {info, showInfo} from './module/01.js'
  4. console.log(info)
  5. showInfo()

总结:

  1. 通过export关键字导出模块成员(包括变量和函数)
  2. 通过import { 成员名称 } from '模块路径' 实现导入
  • 默认导出用法
  1. // 通过export default方式实现默认导出
  2. // 默认导出只能有一个
  3. const fn = function () {
  4. console.log('hello')
  5. }
  6. export default {
  7. info: 'nihao',
  8. fn: fn
  9. }
  1. // 导入默认的信息,此时可以自定义名称,并且不可以添加花括号解构
  2. import obj from './module/m2.js'
  3. console.log(obj.info)
  4. obj.fn()

总结:

  1. 默认导出只能有一个
  2. 导入默认信息可以自定义名称,但是不可以在自定义名称外面包裹花括号
  • 有名字导出和默认导出结合使用
  1. // 基本导出和默认导出结合使用
  2. // 有名字的导出
  3. export const msg = 'hello'
  4. // 默认导出
  5. export default {
  6. info: 'nihao'
  7. }
  1. // 导入默认和有名字导入结合
  2. // 默认导入写到前面,解构导入写后面
  3. import obj, { msg } from './module/m3.js'
  4. console.log(obj.info)
  5. console.log(msg)

总结:

  1. 默认导入写到前面,解构导入写后面
  2. 两者可以结合使用

入口文件分析

目标:熟悉入口文件相关代码业务逻辑

  1. // 导入Vue核心构造函数
  2. import Vue from 'vue'
  3. // 导入根组件
  4. import App from './App.vue'
  5. // 配置开发提示信息
  6. Vue.config.productionTip = false
  7. // 实例化根组件
  8. new Vue({
  9. // render的作用:生成组件模板,类似于template作用
  10. render: h => h(App),
  11. }).$mount('#app')
  12. // $mount('#app')的作用类似于el: '#app'的作用,用于挂载组件到页面容器div中。
  • productionTip启用后的提示信息

image.png

总结:入口文件实现了根组件挂载页面的功能

单文件组件

目标:熟悉单文件组件的基本结构

  1. <template>
  2. <div id="app">
  3. <!-- 组件模板 -->
  4. </div>
  5. </template>
  6. <script>
  7. // 组件配置选项
  8. export default {
  9. name: 'App'
  10. }
  11. </script>
  12. <style>
  13. /* 这里添加组件相关样式 */
  14. </style>

单文件组件的组成

  • template用来提供组件的模板
  • script用来提供组件的配置选项
  • style用来提供组件的样式

综合项目实战

项目介绍

项目整体功能:图书管理;人员管理;楼层管理

  • 图书管理
    • 图书列表
    • 添加图书
    • 删除图书
    • 修改图书

image.png

初始化项目

目标:能够基于VueCli创建项目

  1. 通过vue create命令创建项目
  1. vue create mybook
  1. 进入项目跟目录
  1. cd mybook
  1. 运行项目
  1. npm run serve

页面基本布局

目标:能够基于Bootstrap实现案例的基本布局结构

  • 分析页面的组件结构
    • NavBar.vue 顶部导航组件
    • Aside.vue 左侧菜单组件
    • BookList.vue 英雄列表组件
    • BookAdd.vue 添加英雄组件
    • BookEdit.vue 编辑英雄组件
    • PersonList.vue 装备列表组件
    • FloorList.vue 技能列表组件
  • 安装bootstrap包
  1. npm i bootstrap@3.3.7
  • 导入样式
  1. // 引入bootstrap
  2. import 'bootstrap/dist/css/bootstrap.min.css'

导航组件

实现顶部导航组件

  • 拆分组件实现布局 NavBar.vue
  1. <template>
  2. <nav class="navbar navbar-inverse">
  3. <a class="navbar-brand" href="#">CURD</a>
  4. </nav>
  5. </template>
  6. <script>
  7. export default {
  8. name: 'NavBar'
  9. }
  10. </script>
  • 导入组件并使用
  1. <!-- 组件的模板HTML -->
  2. <template>
  3. <div id="app" class="container">
  4. <!-- 顶部导航栏组件 -->
  5. <NavBar/>
  6. </div>
  7. </template>
  8. <!-- 组件的配置选项JS -->
  9. <script>
  10. // 导入另外一个组件
  11. import NavBar from './components/NavBar.vue'
  12. export default {
  13. // 组件的名称,方便调试使用
  14. name: 'App',
  15. components: {
  16. NavBar
  17. }
  18. }
  19. </script>

侧边栏组件

拆分侧边栏组件布局

  • 组件模板布局
  1. <template>
  2. <div class="col-md-2">
  3. <div class="row">
  4. <div class="list-group">
  5. <a href="#" class="list-group-item active">图书列表</a>
  6. <a href="#" class="list-group-item">人员列表</a>
  7. <a href="#" class="list-group-item">楼层列表</a>
  8. </div>
  9. </div>
  10. </div>
  11. </template>
  • 导入组件用法
  1. <template>
  2. <div class="container">
  3. <!-- 在脚手架的环境下,使用如下的两种命名方式都可以 -->
  4. <!-- <NavBar/> -->
  5. <!-- 顶部导航栏组件 -->
  6. <nav-bar/>
  7. <!-- 左侧菜单组件:单个单词的组件名称不可以使用纯小写的方式,但是首字符大写是可以的 -->
  8. <aside-menu/>
  9. </div>
  10. </template>
  11. <script>
  12. // 导入子组件
  13. import NavBar from './components/NavBar.vue'
  14. import AsideMenu from './components/Aside.vue'
  15. export default {
  16. // 配置局部子组件
  17. components: {
  18. NavBar,
  19. AsideMenu
  20. }
  21. }
  22. </script>

路由配置

目标:实现点击侧边栏的连接。需要切换右侧的内容。这个需要路由来实现。

  1. 安装vue-router
  1. npm i vue-router
  1. 导入路由
  1. import VueRouter from 'vue-router'
  1. 注册路由(配置路由插件)
  1. Vue.use(VueRouter)
  1. 准备路由组件
  • BookList.vue
  • PersonList.vue
  • FloorList.vue
  1. 配置路由规则
  1. import BookList from './components/BookList.vue'import PersonList from './components/PersonList.vue'import FloorList from './components/FloorList.vue'// 配置路由映射const routes = [ {path: '/', redirect: '/books'}, {path: '/books', component: BookList}, {path: '/persons', component: PersonList}, {path: '/floors', component: FloorList},]
  1. 实例化路由对象
  1. const router = new VueRouter({ routes })
  1. 挂载路由对象到Vue实例上
  1. new Vue({ render: h => h(App), router}).$mount('#app')
  1. 配置路由链接

components/Aside.vue

  1. <router-link to="/heroes" class="list-group-item active">英雄列表</router-link>
  2. <router-link to="/zb" class="list-group-item">装备列表</router-link>
  3. <router-link to="/jn" class="list-group-item">技能列表</router-link>
  1. 配置路由填充位

App.vue

  1. <div class="col-md-10">
  2. <!-- 路由对应组件显示的位置 -->
  3. <router-view></router-view>
  4. </div>

控制路由跳转菜单激活

目标:实现路由跳转菜单高亮控制

激活(高亮):点击哪一个链接,哪一个链接应该添加一个类名active,没有点中的链接标签去掉类名

实现原理:基于vue-router的相关配置,自动给链接标签添加指定的类名

  1. // linkExactActiveClass属性的作用:控制点中链接标签后添加的类名名称,默认的名称是router-link-active
  2. const router = new VueRouter({ routes , linkExactActiveClass: 'active'})

拆分路由模块

目标:main.js 的职责足够单一,让代码更好维护。全局资源导入,根实例初始化。

  • 封装路由模块:src/router/index.js
  1. // 1. 导入插件import VueRouter from 'vue-router'// 2. 注册路由import Vue from 'vue'Vue.use(VueRouter)// 3. 导入路由组件import HeroesList from './views/HeroesList.vue'import ZbList from './views/ZbList.vue'import JnList from './views/JnList.vue'// 4. 配置路由规则const routes = [ { path: '/', redirect: '/heroes' }, { path: '/heroes', component: HeroesList }, { path: '/zb', component: ZbList }, { path: '/jn', component: JnList }]// 5. 初始化路由const router = new VueRouter({ routes , linkExactActiveClass: 'active'})// 6. 导出路由实例export default router
  • 导入路由模块
  1. import Vue from 'vue'import App from './App.vue'// 引入bootstrapimport 'bootstrap/dist/css/bootstrap.min.css'// 导入路由实例import router from './router'Vue.config.productionTip = falsenew Vue({ render: h => h(App), // 挂载路由实例 router}).$mount('#app')

准备接口

目标:基于json-server模拟接口。

  • 创建db.json文件
  1. { "books": [ { "id": 1, "bname": "西游记", "author": "吴承恩", "ctime": "2020-03-21 10:38:20" }, { "id": 2, "bname": "红楼梦", "author": "曹雪芹", "ctime": "2020-03-21 10:38:20" }, { "id": 3, "bname": "三国演义", "author": "罗贯中", "ctime": "2020-03-21 10:38:20" } ]}
  • 启动接口
  1. json-server db.json测试接口地址:http://localhost:3000/books
  • 安装axios包
  1. npm i axios

英雄列表组件

目标:实现英雄列表组件

  • 组件基本布局
  1. <template> <div class="list-container"> <a href="heroes-form.html" class="btn btn-primary">添加英雄</a> <hr /> <table class="table table-hover"> <thead> <tr> <th>ID</th> <th>英雄名称</th> <th>英雄性别</th> <th>创建时间</th> <th>操作</th> </tr> </thead> <tbody> <tr> <td>101</td> <td>亚索</td> <td></td> <td>2019-02-10 10:50:12</td> <td> <button class="btn btn-success">编辑</button>&nbsp; <button class="btn btn-danger">删除</button> </td> </tr> </tbody> </table> </div></template><script>export default {}</script><style></style>
  • 在组件初始化时,获取英雄列表数据
  1. // 导入axios
  2. import axios from 'axios'
  3. export default {
  4. data () {
  5. return {
  6. bookList: []
  7. }
  8. },
  9. created () {
  10. this.getList()
  11. },
  12. methods: {
  13. // 获取英雄列表数据
  14. loadList () {
  15. // 通过axios发查询列表请求
  16. axios.get('http://localhost:3000/books').then(res=>{
  17. this.bookList = res.data
  18. })
  19. }
  20. }
  21. }
  • 根据list数据,进行模板渲染。
  1. <tbody>
  2. <tr :key='item.id' v-for='item in bookList'>
  3. <td>{{item.id}}</td>
  4. <td>{{item.bname}}</td>
  5. <td>{{item.author}}</td>
  6. <td>{{item.ctime}}</td>
  7. <td>
  8. <button class="btn btn-success">编辑</button>
  9. <button class="btn btn-danger">删除</button>
  10. </td>
  11. </tr>
  12. </tbody>

添加英雄组件

目标:实现添加英雄功能

  • 添加链接跳转
  1. <router-link to="/heroes/add" class="btn btn-primary">添加英雄</router-link>
  • 创建组件,基础布局 src/views/HeroesAdd.vue
  1. <template> <form action method="post" role="form"> <legend>添加英雄</legend> <div class="form-group"> <label>英雄名称</label> <input type="text" class="form-control" /> </div> <div class="form-group"> <label>英雄性别</label> <div> <input type="radio" name="gender" value="男"><input type="radio" name="gender" value="女"></div> </div> <button type="submit" class="btn btn-primary">提交</button> </form></template><script>export default {}</script><style></style>
  • 配置路由规则 src/router.js
  1. // 导入组件import HeroesAdd from './views/HeroesAdd.vue'// 配置路由映射{ path: '/heroes/add', component: HeroesAdd },
  • 收集英雄表单数据
  1. <div class="form-group"> <label>英雄名称</label>+ <input v-model="heroesForm.hName" type="text" class="form-control" /></div><div class="form-group"> <label>英雄性别</label> <div>+ <input v-model="heroesForm.hGender" type="radio" name="gender" value="男"> 男+ <input v-model="heroesForm.hGender" type="radio" name="gender" value="女"></div></div>
  • 绑定表单的提交事件,阻止默认的提交行为,使用axios来异步提交。
  1. <form @submit.prevent="submit()">
  • 在处理函数中,进行添加,如果添加成功,跳转列表
  1. methods: { submit() { // 使用axios进行添加英雄请求 // 传参对象:hName hGender cTime 后台需要 // 操作:把 heroesForm 与 cTime 合并到传参对象中 axios .post("http://localhost:3000/heroes", { cTime: new Date(), ...this.heroesForm }) .then(() => { // 成功 跳转列表 this.$router.push('/heroes') }).catch(()=>{ alert('添加失败') }) }}

编辑英雄组件

目标:实现编辑英雄功能

  • 实现动态跳转:views/HeroesList.vue
  1. <router-link :to="'/heroes/edit/'+item.id" class="btn btn-success">编辑</router-link>
  • 准备编辑组件,基础布局。 src/views/HeroesEdit.vue
  1. <template>
  2. <form action method="post" role="form">
  3. <legend>编辑英雄</legend>
  4. <div class="form-group">
  5. <label>英雄名称</label>
  6. <input type="text" class="form-control" />
  7. </div>
  8. <div class="form-group">
  9. <label>英雄性别</label>
  10. <div>
  11. <input type="radio" name="gender" value="男">
  12. <input type="radio" name="gender" value="女">
  13. </div>
  14. </div>
  15. <button type="submit" class="btn btn-success">修改</button>
  16. </form>
  17. </template>
  18. <script>
  19. export default {}
  20. </script>
  21. <style>
  22. </style>
  • 配置路由规则,通过动态路由规则,来指向编辑组件。 src/router.js
  1. // 导入组件
  2. import HeroesEdit from './views/HeroesEdit.vue'
  3. // 路由映射配置
  4. { path: '/heroes/add', component: HeroesAdd },
  5. + // 编辑
  6. + { path: '/heroes/edit/:id', component: HeroesEdit },
  7. { path: '/zb', component: ZbList },
  • 绑定表单数据
  1. export default {
  2. data () {
  3. return {
  4. // 英雄表单数据对象
  5. heroesForm: {
  6. hName: '',
  7. hGender: ''
  8. }
  9. }
  10. }
  11. }
  1. <div class="form-group">
  2. <label>英雄名称</label>
  3. + <input v-model="heroesForm.hName" type="text" class="form-control" />
  4. </div>
  5. <div class="form-group">
  6. <label>英雄性别</label>
  7. <div>
  8. + <input v-model="heroesForm.hGender" type="radio" name="gender" value="男">
  9. + <input v-model="heroesForm.hGender" type="radio" name="gender" value="女">
  10. </div>
  11. </div>
  • 组件初始化,获取当前英雄的信息,展示在表单中。
  1. created() {
  2. this.getHeroes();
  3. },
  4. methods: {
  5. getHeroes() {
  6. // 根据地址栏路径上的传参 名字叫id 去后台获取对应的英雄数据
  7. // 当前编辑的英雄ID
  8. const id = this.$route.params.id;
  9. axios.get(`http://localhost:3000/heroes/${id}`).then(res => {
  10. // console.log(res.data);
  11. // 填充表单,就是去修改 heroesForm 数据
  12. this.heroesForm.hName = res.data.hName
  13. this.heroesForm.hGender = res.data.hGender
  14. });
  15. }
  16. }
  • 点击修改按钮,发起修改,修改成功后,列表组件。
  1. <form @submit.prevent="update()">
  1. methods: {
  2. submit() {
  3. // 使用axios进行添加英雄请求
  4. // 传参对象:hName hGender cTime 后台需要
  5. // 操作:把 heroesForm 与 cTime 合并到传参对象中
  6. axios
  7. .post("http://localhost:3000/heroes", {
  8. cTime: new Date(),
  9. ...this.heroesForm
  10. })
  11. .then(() => {
  12. // 成功 跳转列表
  13. this.$router.push('/heroes')
  14. }).catch(()=>{
  15. alert('添加失败')
  16. })
  17. }
  18. }

删除功能

目标:实现删除英雄功能

  1. 绑定删除按钮点击事件
  2. 弹出确认框
  3. 点击确认,发送删除请求
  4. 删除成功,更新当前列表
  1. <button @click="delHeroes(item.id)" class="btn btn-danger">删除</button>
  1. // 删除英雄
  2. delHeroes(id) {
  3. if (confirm("是否确定删除英雄?")) {
  4. axios.delete(`http://localhost:3000/heroes/${id}`).then(()=>{
  5. // 删除成功 更新列表
  6. this.getList()
  7. }).catch(()=>{
  8. alert('删除失败')
  9. })
  10. }
  11. },

时间过滤器

目标:基于moment包实现时间格式化过滤器

  • 安装 moment
  1. npm i moment
  • 导入moment HeroesList.vue
  1. // 导入moment
  2. import moment from 'moment';
  • 定义过滤器
  1. filters: {
  2. // formatTime 过滤器名字,对应的函数处理格式。
  3. formatTime (value) {
  4. // 使用过滤器的时候,管道符 | 前的js表达式执行结果,就是value
  5. return moment(value).format('YYYY-MM-DD HH:mm:ss')
  6. }
  7. },
  • 使用过滤器
  1. <td>{{item.cTime|formatTime}}</td>

axios全局配置

目标:全局配置axios

  • 在组件中使用axios
  • 每一个组件其实也是vue实例
  • 实例都可以访问当构造函数的原型(方法|属性)
  • 所以,把axios挂载到Vue的原型上
  • 将来,任何组件都可以访问axios
  1. // 进行axios的全局挂载
  2. import axios from 'axios'
  3. // 将来通过vue的实例访问$http,其实就是axios。
  4. Vue.prototype.$http = axios