前言

若文章有误,欢迎读者留言反馈

只需一篇文章学会Vue2的PC端项目搭建流程

搭建Vue后台项目

Vue工具安装

vue脚手架工具(vue命令行工具)
vue command line tool,简单的来讲,就是一个基于命令行的vue开发工具。
[注:Vue-CLI ≠ Vue,Vue-CLI就是一个Vue工具,重点在于指令,安装了就可以使用指令创建项目。]

npm官网

1
2
3
4
5
6
7
8
9
10
11
# 安装
# -g:全局安装
npm i -g @vue/cli

# 安装成功后,检查
vue --version
# 或者
vue -V

# 卸载(了解)
npm uninstall -g @vue/cli

如果需要安装其他版本,可以使用npm install -g @vue/cli@版本号的方式进行指定版本

myadmin【后台管理项目】

vue创建项目

通过脚手架创建vue项目主要有两种,其实还有一种不使用脚手架,即自己使用npm创建项目安装vue框架和打包工具

  • 通过UI界面方式去创建(了解),在命令行中输入以下命令启动UI界面
    1
    vue ui
  • 通过命令行的方式切创建(推荐),按照如下命令敲即可
  1. vue create [项目名称]

    vue create myadmin
  2. Please pick a preset: (Use arrow keys)

    Manually select features 【上下方向键选中手动配置然后回车】
  3. Check the features needed for your project:
    【空格进行选择和不选择】选择Babel、Router、Vuex、CSS Pre-processors
  4. Choose a version of Vue.js that you want to start the project with (Use arrow keys)
    选择2.x
  5. Use history mode for router? (Requires proper server setup for index fallback in production)

    输入n ==> no
  6. Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): (Use arrow keys)

    选择Less即可
  7. Where do you prefer placing config for Babel, ESLint, etc.? (Use arrow keys)

    In package.json
  8. Save this as a preset for future projects?

    输入n ==> no

项目初始配置

  1. 忽略node_modules文件夹

    【vscode一般右下方会有提示窗口是否要忽略node_modules文件夹】

    Git 存储库“d:.Study2022\三阶段\code\day8\myadmin”中存在大量活动更改,将仅启用部分 Git 功能。 是否要将“node_modules”添加到 .gitignore?
    选择是

    如果没有就到.gitignore最上面添加node_modules

  2. cd 进入项目根目录[按tab键会提示你当前目录下的文件,可以切换]

  3. 开发阶段关闭eslint

    • 在项目根目录创建vue.config.js 文件,配置如下:
      1
      2
      3
      module.exports = {
      lintOnSave: false// eslint-loader 是否在保存的时候检查
      }
  4. 运许项目
    • 运行npm run serve命令来启动项目
    • 注意:默认端口号会从8080开始,如果再次启动其他项目后续会以8081、8082……进行监听。

    • 如果需要停止正在运行的项目,可以选择以下两种方式任一:

      • 关闭终端
      • 在终端中按下组合键Ctrl + C(Cancel),随后选择Y并键入回车
      • 也可以按下两次Ctrl + C
    • ==关于项目运行时,如果修改了项目代码是否需要重启的说明:==
      • 是否需要重启取决于我们修改了什么内容,如果只是修改了代码部分(js、css、vue文件等)是不需要开发者手动重启项目的,系统会自动重新编译(有点nodemon感觉);但是如果修改的是配置文件,则必须需要自己先去停止项目,然后再去启动项目(手动实现重启)。
  5. 清除文件【初次做项目,删除了很多不必要的文件,熟悉这些不同文件夹、文件的用途】

    • 分别删除src目录下的assets文件夹、components文件夹、router文件夹、store文件夹和views文件夹
    • 清除一下App.vue里面内容【App.vue,只保留一个容器,它不需要script,里面不写js,main.js可以说就是App.vue里的js分离出去的】
      1
      2
      3
      4
      5
      6
      7
      8
      <template>
      <!-- 容器,展示内容用 -->
      <router-view></router-view>
      </template>

      <style lang="less">

      </style>
    • 根组件去掉scoped 【scoped:把当前组件的样式变成局部样式】
  6. 搭建项目目录结构,在src目录下创建文件夹

    • api文件夹 统一api控制
    • assets文件夹 存放静态资源
    • components文件夹 公共组件
    • layout文件夹 布局+导航栏子组件【固定不变的内容作为子组件也一起放在这里,注意一点,子组件不和路径url,相关联,只做展示用,路由父组件得注册路由,总路由【路由实例】是挂载在根组件上的】
      • 主要放Layout.vue路由组件
    • mock文件夹 mock接口
    • router文件夹 路由,主要做跳转配置文件
      • 在里面可以提前新建一个index.js
    • store文件夹 vuex,管理数据中心
      • 在里面可以提前新建一个modules文件夹和index.js文件
    • utils文件夹 工具类,存放封装好的方法【如axios请求的再次封装】
    • views 页面,内容组件,展示内容用
      • 可以提前创建一个404的vue路由组件,NotFound.vue,放点内容如页面飞走了!!

项目开发

  1. 重置样式
    • 在assets目录创建reset.css文件重置样式
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      *{box-sizing: border-box;}
      body,h1,h2,h3,h4,h5,h6,p,ul,li,ol,dl,dd,fieldset,legend,button,input,textarea,th,td{padding:0;margin:0;}
      html{font-family:"微软雅黑";font-size:12px;background-color:#eeeeee;}
      .clear:after{content:"";display:block;clear:both;height:0;visibility:hidden;overflow:hidden;}
      .clear{zoom:1;}
      li{list-style:none;}
      a{text-decoration:none;}
      img{vertical-align:top;border:0;}
      input,select,button,textarea{outline:none;}
      textarea{resize:none;}
      input[type="button"],input[type="submit"],input[type="file"] {cursor:pointer;}

      引入该文件

      在main.js文件中引入

      1
      import './assets/reset.css'

使用重置样式插件

介绍:Normalize.css只是一个很小的css文件,但它在磨人的HTML元素样式上提供了跨浏览器的高度一致性。相比于传统的CSS reset,Normalize.css是一种现代的、为HTML5准备的优质替代方案。总之,Normalize.css是一种CSS reset的替代方案。

1
2
3
4
# 安装重置样式插件
npm install --save normalize.css /或者"reset-css": "^5.0.1",
# main.js 引入
import 'normalize.css/normalize.css'

  1. 安装ui库

npm i element-ui@2.15.6 -S

日期组件会有问题,版本选择2.15.6

  • main.js入口文件引入【全局js文件】
    1
    2
    3
    4
    import ElementUI from 'element-ui'
    import 'element-ui/lib/theme-chalk/index.css' // 全局引入样式

    Vue.use(ElementUI,{ size: 'mini'}) // 设置所有的ui都为小号
  1. 构件项目的布局页面【layout文件夹下操作】

首先得有一个大的路由组件,由于之前在App.vue已经放了展示容器,直接在layout文件夹下创建一个Layout.vue文件,并添加点内容

注册,找到路由文件夹创建一个index.js

index.js文件配置路由,导入vue、vue-router、导入该路由组件取个组件名,注意大驼峰

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import Vue from "vue"
import VueRouter from 'vue-router'
import Layout from '../layout/Layout.vue'
import NotFound from '../views/NotFound.vue'

Vue.use(VueRouter) // Vue上注册使用

const routes = [
{
path: '/',
redirect: '/layout'
},
{
path: '/layout', // 注意一级路由路径带/,而从二级路由开始不带/,一般最多三级路由。
name: 'layout',
component: Layout
},
{
path: '*',
component: NotFound
}
]

const router = new VueRouter({
routes
})

export default router

在入口文件main.js导入刚刚暴露的路由,挂到vue实例身上,npm run serve运行即可打开页面看一下Layout.vue里面内容是否生效

预览布局划分,划分完在element-ui找到合适的布局容器,复制代码粘贴过来【直接粘贴到template下,外层可以包div也可以不包】

1
2
3
4
5
6
7
8
9
10
<el-container>
<el-header>Header</el-header>
<el-container>
<el-aside width="200px">Aside</el-aside>
<el-container>
<el-main>Main</el-main>
<el-footer>Footer</el-footer>
</el-container>
</el-container>
</el-container>

注意样式也要copy过来放到style标签里面[可带style也可不带]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
.el-header, .el-footer {
background-color: #B3C0D1;
color: #333;
text-align: center;
line-height: 60px;
}

.el-aside {
background-color: #D3DCE6;
color: #333;
text-align: center;
line-height: 200px;
}

.el-main {
background-color: #E9EEF3;
color: #333;
text-align: center;
line-height: 160px;
}

body > .el-container {
margin-bottom: 40px;
}

.el-container:nth-child(5) .el-aside,
.el-container:nth-child(6) .el-aside {
line-height: 260px;
}

.el-container:nth-child(7) .el-aside {
line-height: 320px;
}

再看一下效果怎么样,发现高度存在一定问题,这时候需要把三个根元素高度设置100%,并设置到全局{公有}样式里=>App.vue

1
2
3
html,body,#app{
height: 100%;
}

发现还是没占满,鼠标右键再接着看里面元素,发现咱们复制粘贴过来的容器高度不是100%,于是再回到Layout.vue,在样式最上面设置容器高度100%

1
2
3
.el-container {
height: 100%;
}

复制粘贴过来的样式清除一下不需要的,只保留区块背景色

可以发现主题内容区域是切换的并是由侧边栏控制,可以想到二级路由,先提前到切换内容的地方放上容器占位[把main文本替换位容器即可],创建二级路由组件[layout文件夹一般只放最大的布局路由组件],我们需要在views文件夹下创建home文件夹=>home目录下创建home.vue和components未来放子组件[注意vue文件名要采用大驼峰]

可以在Home.vue放一点内容文本如Home,方便查看路由是否配置成功

【注:路由组件三步,容器占位=>创建路由组件=>路由里面注册】

创建完就得注册了,进入router/index.js,首先就是引入二级路由组件,注意一个关键属性children,【注意路由配置这里都是数组里套对象,数组可以放多个路由配置】,然后写上配置对象

1
2
3
4
5
6
7
8
9
10
11
12
{
path: '/layout',
name: 'layout',
component: Layout,
children: [
{
path: 'home', // 不要带/
name: 'home',
component: Home
}
]
},

接着咱们可以多创建几个二级路由组件,并进行配置,平级的路由只需要第一个放容器就行,路径=>互相切换内容,只需要注册好即可,在地址栏把hash值home替换为user测试一下

左边侧边栏可以先用类似a标签的router-link,to是path值[已经是在layout下了,也可以写完整路径/layout/home]

1
2
<router-link to="home">home</router-link>
<router-link to="user">user</router-link>

测试一下可以跳转即可

  1. 开始书写内容区,划分内容区组件【路由组件home为父组件在里面划分出子组件】

划分为上中下,mySearch、myTable、myPage,首先子组件别名占位【注意是在父组件里】=>创建子组件=>在父组件里注册【这三步和路由组件差不多,组件别名个人习惯大驼峰包括组件文件名也是】

搜索组件应用:
书写MySearch.vue里面内容:
在子组件MySearch.vue文件里开始写内容,观察布局一左一右,刚好外层有个div里面直接使用两个div,再使用Element-ui组件【书写内容之前布置布局很重要,一般都是外面套div使用flex布局】

【注意把v-model对应的数据也一起复制过来,不然会报错】

复制过来后样式需要调整,采用样式覆盖【采用less嵌套语法,层级不够就在类名前面加上/deep/

书写MySearch.vue里面内容:
这个可以直接复制粘贴表格组件即可

表格组件应用:

1
2
3
4
5
6
表格两种写法:
<!-- slot-scope作用域插槽,template空标签,scope就当成变量即可,scope.row就是当前行数据 -->
<template slot-scope="scope">{{ scope.row.date }}</template>

<!-- label表头,prop指定的字段, 宽度去掉就会自适应 -->
<el-table-column prop="name" label="姓名" width="120">

自定义表格数据,替换字段即可

分页组件

直接复制过来,对齐进行位置处理外面套个div,使用flex布局,静态页面内容基本完成

写功能:
写功能之前,涉及到发送数据,请求接口,后端模拟数据
以往是使用ajax发送请求,但是vue里面为我们提供了axios,axios就是利用promise对ajax进行了再次封装

使用axios,安装一下axios

  1. 在工具类utils目录里面我们可以对axios进行再次封装,为了得到一个全局请求工具类,每个请求接口调用这个请求,配置get还是post以及传参
    1. 可以把bus通信也一起配置了【注意都需要单独创建文件】
    2. 开发中把Axios进行二次封装,可以更好的使用Axios的请求拦截器,以及响应拦截器去处理数据
    3. baseURL,主要放域名【一般后端接口都有相同域名,hash值不同】,由于没有真实后端接口地址,这里可以随便写,或者写空字符串就行了,最终它会和请求接口里配置得url拼接,而mock接口名采用正则,比如包含/home

  1. 所有请求接口都进行统一管理,在api目录创建一个homeApi.js文件【这个文件用来处理home页面所有的请求】
    1. 将我们的请求工具类引入
    2. 页面加载完得向后端请求数据,然后渲染,伪造数据和后端接口【使用mock进行模拟后端接口】
    3. 安装mockjs
      • npm i -S mockjs
      • 在mock目录下创建一个index.js文件用来放后端提供数据接口,先引入一下Mock,然后把它注入到main.js
      • 在mock目录下创建一个homeData.js文件伪造home页面的数据,这时我们需要用到一个工具类,地址栏参数解析【参数转对象,提取url参数】
      • 在utils目录下新建一个urlUtils.js文件,这个直接使用即可
      • 回到homeData.js文件直接引入提取参数工具类,伪造100条数据,利用for循环往List添加数据,并使用Mock生成【List是我们用来存放伪造数据】
      • 可以把之前表格静态数据拿过来,添加一个id,并把值全部替换为Mock来生成,每遍历一次都会生成一条数据

        /mock/homeData.js文件暴露该对象,让index.js接收该对象

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        export default {
        getHomeData: config => {
        console.log(config)
        return {
        code: 200,
        msg: '请求成功',
        res: List
        }
        }
        }

        /mock/index.js文件

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        // index.js主要提供数据接口,最终注入到main.js,某个页面数据增删改查需要创建js文件,进行操作然后暴露,暴露对象
        import Mock from 'mockjs'
        // 引入homeData对象
        import homeData from './homeData'

        // 写数据接口,与请求接口对应
        // 参数1:接口的正则表达式
        // 参数2:请求类型 get post put delete
        // 参数3:返回给前端的数据
        Mock.mock(/\/home/, 'get', homeData.getHomeData)

  1. 回到home组件,请求数据统一放到父组件中,方便后续操作,不需要每个子组件都去请求数据
    1. 父传子采用props,elementUI里表格有个tableData我们只需要父组件传值过去,替换一下就可以了
    2. 引入请求接口,来帮我们请求数据
    3. 在父组件mounted里请求数据,不要直接调用请求接口,进行封装,方便刷新操作能接着调用,请求数据不会只用一次一定要封装
    4. 请求成功会得到一个后端返回给我们的res对象,该对象包含了状态码、请求信息、数据【一般都是数组】

正确的接口调用方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
mounted() {
this.initGetHome()
},
methods: {
initGetHome() {
getHomeApi().then((res) => {
console.log("res", res)
if (res.code == 200) {
this.arr = res.data
}
})
},
},

从简单功能开始写,查询=>删除=>分页=>添加=>编辑

  1. MySearch组件里面的搜索功能,这个搜索功能可以放到初始请求数据里面,把keyword传进去,后端接收对数组过滤即可
    1. 子传父,这里我们采用this.$emit(事件类型,关键词),父组件里用个keyword接收即可【工具类有键名,无键值默认为空串,不搜索,初次进入页面,始终都是有keyword的键名】
    2. 后端进行处理,回到/mock/homeData.js,对于关键字进行过滤,返回给前端
    3. 让父组件刷新数据

/home/Home.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
methods: {
searchFunParent(val) {
this.keyword = val
this.initGetHome()
},
// 请求接口调用进行封装
// initGetHome() {
// getHomeApi().then((res) => {
// // console.log("res", res)
// if (res.code == 200) {
// this.arr = res.data
// }
// })
// },
// 引入关键词,对于请求接口调用进行升级[注意参数是对象]
initGetHome() {
getHomeApi({ keyword: this.keyword }).then((res) => {
// console.log("res", res)
if (res.code == 200) {
this.arr = res.data
}
})
},
},

/mock/homeData.js
1
2
3
4
5
6
7
8
9
10
11
12
getHomeData: config => {
// console.log(config) config是个对象里面有url,这个url刚好携带了get方式的参数【正好利用上参数解析该工具类】,同时里面还有请求方式
// console.log(config.url)
var { keyword } = param2Obj(config.url) // 参数解析
// console.log(1, keyword)
var res = List.filter(item => item.username.indexOf(keyword) != -1)
return {
code: 200,
msg: '请求成功!',
data: res
}
}

get和post两种请求config不同,数据所在位置的不同【get需要参数解析】

1
2
3
4
5
6
get方式:config值如下
{url: '/home?keyword=%E9%87%91', type: 'GET', body: null}


post方式:config值如下
{url: '/deleteById', type: 'POST', body: '{"id":"CC5523bd-5cBF-58c9-b345-98B7fF39FFf2"}'}

  1. MyTable组件的单删功能,单删功能【逻辑:直接调用请求接口把id发给后端,后端删除,如果成功就发送事件给父组件,因为咱们的数据都是放在父组件,父组件重新请求一下数组就更新了,该自定义事件执行初始化数据函数】
    1. 点击每一个删除按钮它都对应一条数据对应一个id,这个id我们可以通过scope.row拿到,scope.row就是当前行数据
    2. 点击删除有个elementUI自带的handleDelete方法,把里面参数改为scope.row.id,注意scope.row就是当前行数据,数组里的遍历得到的一个对象,直接拿id
    3. 写删除请求接口
    4. 引入该请求接口,子组件里直接调用,数据是放在父组件,如果成功就发送事件[写上打印res.msg],让父组件刷新数据
    5. 子传父,发送自定义事件,该自定义事件执行初始化数据函数【刷新数据】
    6. 后端处理,先简单处理,跑通前后端,再写后端逻辑[请求成功一定要打印res.msg测试一下是否跑通]
    7. 在mock的index.js由于首次已经引入homeData就不需要再引入,配置数据接口,与前端对应
    8. 写后端逻辑,post方式id是在请求体中,config.body,然后利用JSON.parse解析得到对象,再解构拿到id
    9. 使用过滤,id不符合的全部过滤出来,再重新赋值给List
    10. 发送自定义事件,让父组件刷新数据

get和post两种请求config不同,数据所在位置的不同

1
2
3
4
5
6
get方式:config值如下
{url: '/home?keyword=%E9%87%91', type: 'GET', body: null}


post方式:config值如下[id值是这样的,mock伪造时就长这样,JSON解析也还是长这样]
{url: '/deleteById', type: 'POST', body: '{"id":"CC5523bd-5cBF-58c9-b345-98B7fF39FFf2"}'}

  1. MySearch批量选中删除,涉及两兄弟组件通信采用Bus通信
    1. 选中数据是在MyTable组件中,有个v-model绑定的数组,里面放的是选中每一条数据,而批量删除按钮在MySearch组件里
    2. 思路:MySearch组件点击发送事件给MyTable组件,MyTable组件里面的选中数据提取出id放在一个数组里面,调用批量删除请求接口,发给后端
    3. MySearch组件Bus通信MyTable组件,首先引入事件中心,发送一个自定义事件
      1. 传递数据
        • eventBus.$emit(‘自定义事件名’,传递的数据) // 这里不需要传递数据
    4. MyTable组件接收,一样先引入事件中心,选择挂载后接收,mounted里接收
      1. 接收数据
        • eventBus.$on(‘自定义事件名’, callback) // 回调函数第一个参数就是传递过来的数据
    5. 这里callback我们可以替换为MyTable组件里的批量删除函数,单独写一个删除函数[建议删除函数名字不要与发送接收自定义事件名重复,不然报错]
    6. 写批量删除请求接口,引入接口,调用接口
    7. 后端数据操作,依旧是先跑通,逻辑后写【数据操作文件里面写】
    8. 写数据接口【mock的index.js】
    9. 如果能打印批量删除成功说明已跑通
    10. 后端逻辑,使用过滤,把过滤的值赋值给List
    11. 发送自定义事件,让父组件刷新数据

先写成下面这样,不要直接写逻辑,把前后端跑通再写逻辑

1
2
3
4
5
6
7
8
batchDelete: config => {
console.log(config) // 查看请求信息,放的是前端发送过来的相关请求信息,包括携带的数据
return {
code: 200,
msg: '批量删除成功',
data: null
}
}

  1. MyPage组件分页功能[分页后端有个公式,page页码和pageSize每页显示多少条数据,是必须传给后端]
    1. 分页组件一般较为常用,封装为公共组件,提到src/components目录下,main.js入口文件注册,同时注释或删除掉之前在Home.vue组件里注册和引入
    2. 将elementUI里的页码、当前页、每页多少条、总条数替换为变量, 数据依旧放到父组件
    3. 父传子,把页码、当前页、每页多少条、总条数传给MyPage组件【虽然是公共组件,但是是在Home组件里面使用,父传子】
    4. 分页组件接收,不建议使用数组,使用对象有默认值
    5. 分页组件点击第几页和条数时,向Home组件发送数据,更改父组件里的页码和每页显示多少条的值
    6. 有了页码和每页显示多少条,由于是获取数据,可以直接作为参数和搜索请求接口合并,在请求接口调用里接着加入参数
    7. 每次更改和初始是需要请求数据并渲染的,都需要重新请求数据,分别在调用一下请求数据
    8. 后端处理数据操作,过滤条件为(page - 1) * pageSize <= index && index < page * pageSize
    9. 后端数据接口依旧是用原来初始数据接口

1
2
3
4
5
6
// page=1 控制当前页码
// pageSize=20 控制当前页显示条数
// total=0 控制总条数
// pageSizes=[20, 40, 60, 80, 100] 控制当前每页多少条
// changepageSize自定义事件 是切换pageSize触发
// changePage 自定义事件,切换page触发

  1. MySearch组件的添加功能,添加需要弹框,有一个嵌套表单的对话框组件【对于这个组件采用v-if做,对话框内部的表单组件elementUI自带的是v-show,使用它自带的显示隐藏有问题,每次验证失败,下次点击依旧是验证失败的显示】
    1. 创建对话框组件,注册并使用
    2. elementUI里面找到合适的嵌套表单的对话框组件,代码复制过来,默认不显示弹框,自带的显示隐藏dialogVisible: true, //控制弹框的显示和隐藏的,我们让它显示,使用v-if控制它组件显示隐藏
    3. 对话框组件在父组件下,控制显示隐藏放到父组件下即可,在父组件用一个变量布尔值来控制对话框显示隐藏
    4. 读懂elementui里的参数意思,title改为添加,form对应数据的属性名修改一下,对话框里面有些没有,对话框里面嵌套的是表单这里我们可以去表单里面找可以使用的数据,例如表单验证规则,动态绑定,找对应的表单籍贯=>级联框
    5. 关于自定义验证规则src/views/home/components/MyDialog.vue文件里的注释写的比较详细
    6. 开始书写前端逻辑,点击提交按钮[elementUI是有提供方法,我们只需要数据对上,验证成功把表单的数据发送给后端,后端执行添加逻辑,通知前端添加成功,Home.vue刷新表格发出请求获取最新数据即可]
    7. 补充一个表单籍贯,双向数据绑定options,我们直接引入中国的省市区json文件就行作为一个变量的值,这个文件里面放的就是数组
    8. 表单提交把elementUI自带的参数修改为我们对应的数据,如果验证跳过这个时候就要发请求获取数据
    9. 写添加数据请求接口,引入调用,提前写好请求成功返回的res.msg 【注意表单数据是个对象,axios内部会转json字符串,后端解析得到对象】
    10. 写后端数据接口,先写数据操作,别急着写逻辑,这一步主要是前后端跑通,数据操作直接return一个成功,再配置一下数据接口
    11. 前后端跑通,接着把前端传递数据进行解析得到表单数据对象,解构拿到对应数据
    12. 数据添加选择添加到头部选择unshift()方法,添加完不用返回【它是直接添加到最初的List】,我们最终是重新让父组件在请求数据,对接的是后端init数据接口,我们只需要改变List就行,让父组件重新刷新数据就行。。。【id依旧选择Mock伪造】
    13. 后端逻辑写完,前端在状态码成功,发送自定义事件让父组件刷新数据,再次调用父组件里的init数据方法即可
    14. 把表单默认显示的布尔值改为false

对话框组件三个非常重要的数据保存到父组件里面

1
2
3
show: false, // 控制弹框显示,由编辑和添加控制,作用于组件
row: {}, // 保存当前编辑行数据,编辑和添加都会重新赋值
btnType: 0 // 开关阀:传递给对话框组件,让对话框组件知道我是添加还是编辑,写不同的逻辑,发送不同的请求

  1. MyTable组件编辑功能【使用之前的对话框】
    1. 点击编辑让对话框显示,发送事件改变父组件show的值取反
    2. 点击编辑elementUI已经提供了方法发现有个scope.row这个就是当前行数据,也就是我们要编辑的数据,通信的同时我们还需要把数据传给父组件,动态绑定书写row传给对话框组件
    3. props接收父组件数据,成功拿到数据,但是得注意,这条数据是对象,引用传递进行深拷贝,深拷贝完重新赋值给表单原本数据,点击编辑发现成功渲染【mounted阶段进行深拷贝】
    4. 对于添加按钮和编辑按钮做区分,在父组件声明一个变量,使用开关法判断是哪个按钮,方便对话框组件分开写逻辑
    5. 根据类型来写逻辑,如果是添加就写添加逻辑和调用请求接口,修改就写修改逻辑和请求接口
    6. 这一块也比较复杂,结合代码注释,看views/home/components/MyDialog.vue文件

  1. 登录页静态和登录功能

    1. 登录页是个一级路由组件,容器已经在App.vue里面有了,直接在view目录下创建一个login文件夹再创建Login.vue文件
    2. 引入和注册该路由,查看是否跑通,跑通开始写静态页面
    3. 给个div容器,书写静态页面
    4. submitForm(formName) { // 注意formName是形参,最终通过中括号拿到组件,实参得是字符串的表单键名【那个表单对象存储用户名和密码】// 验证表单数据,这个是最终验证,之前每个输入框对应自己的验证规则都通过,代表最终验证通过【所有的都通过才表示都通过才能提交】
    5. 这一块也比较复杂,结合代码注释,看views/login/Login.vue文件
    6. 涉及到一个传递参数使用query传参
  2. header区域要有退出登录功能

    1. 创建Header组件【属于布局路由组件的子组件】
    2. 静态页面书写
    3. 写退出功能
    4. 通过query取出值,this.$route.query.username
    5. 点击退出按钮,移除token,跳转到登录页,不登陆不让访问首页
  3. 侧边导航栏

    1. 写一个组件,使用elementUI会发现有些时候会有滚动条,直接找父元素设置overflow: hidden;
    2. 多创建几个页面,路由组件
    3. 使用router-link包裹写上path值即可
  4. 反向代理【涉及到三个文件】

    1. 前端向后端发送请求的时候有个bug叫跨域问题,协议、域名、端口号有一个不同就报跨域错误
    2. 解决跨域问题:
      1. 后端解决 cors 后台允许跨域,后台来配置,常见
      2. 前端解决 jsonp 反向代理
      3. 服务器来解决 tomcat nginx 软件服务器,可以配置反向代理
    3. 在vue.config.js文件里进行配置
      1. devServer配置咱们服务的
      2. 域名、端口号都可以配置
    4. 请求工具类axios的创建服务里修改baseurl为空字符串【以/api开头】
    5. api文件请求接口封装时,把axios创建的服务通过import导入,往服务里传入url时,值以/api开头
配置完之后,会将所有以`/api`开头的请求全部代理到https://m.maoyan.com这个服务器上
  1. 动态渲染菜单
    1. 涉及到三个文件一个是路由文件做路由配置的src/router/index.js、一个是保存用户信息和权限菜单的文件src/store/index.js和最后一个文件渲染权限菜单文件src/layout/Aside.vue
    2. 直接看这三个文件注释很详细【结合代码看】

项目中的一些注意事项

  1. 项目中不能使用push来退出,页面不会刷新,vuex数据还是缓存状态
  2. 动态添加路由【路由会改变】vue2才有的bug,放行里面得加点参数,next({ …to, replace: true })
  3. 计算属性的bug,它是参数发送改变函数才会重新执行,获取某个数据最开始是undefined,参数需要发生改变才会重新执行,这时候可以使用短路运算符&&处理,发现computed有的类似watch监听,watch监听只是自己组件里的data但是computed直接去监听这个vuex管理的数据。
  4. 连接且 前面条件是true才执行后面的代码
  5. 连接或 前面条件是false才执行后面的代码
  6. 三元【常用】

vue搭建项目总结:网站只有一个index.html同时里面只放了一个div同时添加一个id,值为app,它就是我们的App.vue的挂载点,而App.vue作为根组件,它里面模板只放一个router-view,它是容器,页面上展示的内容不到这里面写,style里面一般只放公共样式,去掉作用域scoped,由于不放页面内容,访问首页、注册页、登录页都得用到一个—>路由,首页布局就是一个大的路由组件,注册登录页也是,路由组件对应一个路径,所以我们需要到路由上进行注册,而路由最终也会挂到vue实例上,路由组件与App.vue如何产生联系,主要就是一起作为配置项,整合到一块了,页面输什么路径展示对应的路由组件,除了对应路径它也对应一个路由容器,主要是放在父组件里,占位置;至于路由组件的内容又得划分【一般不会超过3层】,哪一块内容固定的[例如导航栏、侧边栏一般固定,可以在路由组件里面直接写],哪一块内容切换的【这些内容一般针对于布局,像主内容区来说】,切换某一块区域内容整个区域就得归为路由组件[二级路由],按照顺序一般是先在父组件里找到位置放容器,然后创建二级路由组件,随便写上一点内容,去router目录下index.js进行注册,注册先引入二级路由组件,注意二级路由的path值不能带/,接着运行一下,直接在地址栏后面输入path值,看下二级路由是否生效,写上的内容是否能展示。路由跳转一般就两种,声明式跳转,编程式跳转。

一个路由组件对应一个容器[要展示的地方]和一个路径hash值

【备注:】同时二级重定向写在父亲的路由配置项上,注意写完整路径,/父亲/儿子。

面试题:开发中有没有封装过公共【可复用】组件?【例如:分页组件】

1
2
3
4
5
6
7
8
9
// 公共组件:当前这个组件被其他多个组件同时调用。 定义统一的参数,统一的返回值。
// 分页器做成一个公共组件,所以你要定义清楚不同参数代表的意义和用法。
// 定义入参每个变量的作用
// page=1 控制当前页码
// pageSize=20 控制当前页显示条数
// total=0 控制总条数
// pageSizes=[20, 40, 60, 80, 100] 控制当前每页多少条
// changepageSize自定义事件 是切换pageSize触发
// changePage 自定义事件,切换page触发

dialog对话框,所包含的组件
对话框el-dialog->表单el-form->表单每一项,el-form-item对应包裹一个什么框或者一个、几个按钮->基本的输入框el-input->下拉框el-select->Cascader级联选择器例如籍贯就要使用它->按钮重置、提交


vue模板里面像动态绑定、双大括号语法等里面的变量和方法都必须是挂到vue上的才能使用,变量挂data,方法简写挂到methods;
但是在methods里面封装方法使用变量和方法时,或者是钩子函数里面也是可以直接使用不需要挂到vue上的。
【经常会有外部引入的什么方法,模板里面使用就要挂到vue上;不在模板里就【script标签内部】可以直接使用,不需要挂到vue上】

vue.config.js如果项目里面没有,就在根目录下自己创建一个【配置文件的修改都是需要重启服务】

+ 开发时我们一般都是关闭语法检查
+ 目录映射
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

// 引入内置path模块
const path = require('path')

function resolve(dir) {
return path.join(__dirname, dir) // path内置模块,可以把后面的参数拼接形成一个新地址
}

module.exports = {
lintOnSave: false, // 关闭=>保存时语法检查
chainWebpack: config => {
config.resolve.alias
.set('@', resolve('src')) // key,value自行定义,@取代src
.set('@c', resolve('src/components')) // @c取代src/components
.set('@a', resolve('src/api')) // @a取代src/api
}
}

Mock的数据不像保存到数据库中那样,一刷新或者下次运行项目进来数据就被还原了。
vuex是把数据保存到内存当中[一般会把用户信息和权限菜单保存到vuex里面]

elementUI表单这块经常遇到自动填充,与浏览器有关,解决方法:

  1. 首先找到password输入框
  2. auto-complete=”new-password”
  3. autocomplete=”off”
  4. autocomplete=”new-password”
  5. 都试试,总有一个是可以的

    详解

    在做登录相关的页面时,只要input框设置为:type="password",浏览器就会对input框进行自动填充行为,设置为type="password"的框会自动填充密码,另一个随机input输入框会自动填充账号。

原因

设置inputtype属性为password后,当页面进行过提交,并且允许浏览器记住密码后,那么再次加载该页面时,此password及附近的input就会被自动填充。

安全问题

如果是个人电脑,用户体验是挺好,但若不是,会存在很大的安全问题,很容易被他人盗取用户名及密码。
严重问题:新建表单时,自动填充会导致自动填充的用户名与密码可以提交到后台(本来需要自己主动去填写),从而导致得到不是自己需要的结果,且用户名与密码被暴露出来。