vue2第一章
1.安装两个vscode插件
vetur --让.vue文件高亮和有语法提示
Vue VsCode snippets -- 快捷写代码
2.vue组件
一个.vue文件就是一个组件,称为单文件组件
<template>
<div>
// 这里写 HTML 注意:template只能有一个根节点
</div>
</template>
<script>
// 导出一个vue的实例对象
export default {
}
</script>
<style lang="less" scoped> //scoped达到类似组件样式私有化,不污染全局
// 这里写CSS
</style>
3.自定义组件
自己封装的各种各样 的组件,这些.vue组件,像搭积木一样,拼装出整个页面。
//1.引入
import MyHeader from '自定义组件的路径'
//2.注册
export default {
components:{
MyHeader,
}
}
//3.使用,三种写法
<MyHeader/>
<MyHeader><MyHeader/>
<my-header><my-header/>
4. Mustach胡须表达式
语法
{{ 表达式 }} // 表达式: 简单理解,就是能够计算出唯一的结果
{{ 变量 }}
{{ 变量1 + 变量2 }}
{{ 函数() }}
{{ 变量++ }}
{{ flag ? 'true' : 'false' }}
作用: 渲染数据,让我们往html中动态渲染数据非常方便
5.指令
指令:指令是标签上的自定义属性,vue的指令,都以v-开头,帮助我们渲染数据,操作DOM
1)v-text & v-html:
v-text不能解析html元素标签,v-html可以
<h1 v-text="str"></h1> //不能解析str中的标签符
<h2 v-html="str"></h2> //可以解析str中的标签符
2)v-show & v-if:
都可以控制元素的显示隐藏,v-show 控制的是display属性,v-if会删除dom和重新渲染dom
<!-- <h1 v-show="isvisible">见不见我</h1> -->
<h2 v-if="isvisible">见不见我</h2> //isvisible 为true或false 控制显示隐藏
3)v-if & v-else-if & v-else
逻辑和js是一样的,从上往下找到满足条件的第一个渲染。
<h1 v-if="score>=90">优秀</h1>
<h2 v-else-if="score>=80">良好</h2>
<h3 v-else-if="score>=70">一般</h3>
<h4 v-else-if="score>=60">及格</h4>
<h5 v-else>不及格</h5>
4)v-for:
循环一切
<标签 v-for="(元素的值,元素的索引) in 数组" :key="不重复的值字符串或数字"></标签>
//注意:in前面如果是两项以上,可以省略()
<标签 v-for="(元素的值,元素的键名,元素的索引) in 对象":key="不重复的值字符串或数字"></标签>
5)v-model
让表单和数据双向绑定
注意:这个指令,只能适用于表单标签:input select textarea
<input v-model="msg" type="text" />
<button @click="getval">点我获取表单的值</button>
6.事件v-on
给元素绑定事件 自定义属性 v-on:事件名='函数或者表达式' v-on:可以简化为@
语法:
//简写
<标签 @事件类型="函数名"></标签>
<标签 @事件类型="函数名(实参)"></标签>
<标签 @事件类型="表达式"></标签>
export default {
components:{},
data(){
return {}
},
methods: {
函数名(){ //这里函数中的this指向当前组件的实例对象,可以获取到各个配置项第一层属性
}
}
}
注意:方法中的this指向当前vue组件实例化对象,可以拿到各个配置选项的第一层数据。
事件对象
如果函数没有传参,没有(),那么,函数的形参就是event事件对象。
如果函数有(),且传递了实参,必须手动传递一个实参$event在函数中,才可以获取到事件对象。
7.计算属性computed和methods的区别
1)计算属性必须有返回值,方法不是必须的。
2)方法使用的时候:方法名() ,计算属性使用的时候:方法名
3)计算属性有依赖缓存,只有依赖的数据发生变化,才会自动重新计算,否则不会重新计算,直接使用缓存结果
export default {
methods:{
方法名(){
//很多代码
}
},
//计算属性
computed:{
方法名(){
//很多代码
return 结果
}
}
}
8.v-bind -给属性绑定动态数据,简写为:
1)v-bind绑定style
<标签 :style="{width:'20px',height:'30px'}"></标签>
2)v-bind绑定class
<标签 class="classA" :class="{classB:表达式1,classC:表达式2}"></标签>
//如果表达式的值是true,就有这个类名,否则,没有这个类名
9.其他指令
v-pre --不编译mustache模板表达式 --基本没用。
v-cloak--隐藏mustache表达式({{ message }}),直到有数据后,才显示出来
v-once --只渲染一次,后续数据改变,不会重新渲染 --也没啥用,只渲染一次的话直接写就完了。
10.过滤器filter
作用:处理数据的格式
写法和computed一样,但有些区别。
区别:
1)过滤器--函数会接收一个参数,函数必须有返回值,对传入数据进行处理
2)过滤器--无缓存机制,调用次数取决于页面中有多少过滤器
3)过滤器 --被作为一个特殊方法处理
写法:
<h1>{{ message | 过滤器函数名 }}</h1>
export default{
components:{},
data(){},
methods:{},
computed:{},
filters:{ //过滤器
函数名(msg){
//msg表示要过滤的数据,返回什么,页面就能输出什么
return xxx
}
}
}
11.侦听器-watch
主要用于`暗中观察`数据的变化,如果数据变化,就会触发它的回调函数
export default {
components:{},
data(){},
methods:{},
computed:{},
filter:{},
//一般写法
watch:{
要观察的数据(newval,oldval){
//如果观察的数据,发生改变,就会触发这个回调函数
//函数中,可以获取到 newval:新的值【变化后的值】,oldval:旧的值【原来的值】
}
},
//【深度侦听】--如果嵌太深 性能开销大,不建议大规模使用。
watch:{
要观察的数据:{
handler(newval,oldval) {
//操作
},
deep:true //深度观察
}
}
}
12.vue生命周期
Vue的组件从初始化创建,到组件销毁。整个生命周期过程中,有8个【生命周期的钩子函数】,一进页面生命周期只会触发4个钩子函数--创建前后+挂载前后
1)创建前/后
beforeCreate
vue的实例创建之前,data中的属性都还没有初始化【不存在】
created--最常用
vue的实例创建之后,data属性已经有了,可以操作了。这个阶段【适合发送ajax】,初始化数据。
2)挂载前/后
beforeMount
template模板已经编译完成,但是还是虚拟的dom,存在于内存中,没有挂载到页面。还是不能操作dom。
mounted--最常用
挂载完成,一般需要操作dom,就在这个阶段,主要用于【操作dom】,加载echarts等。
3)更新前/后
beforeUpdate
用于获取更新之前的dom内容
updated
用于获取更新之后的dom内容
4)销毁前/后
beforeDestroy
destroyed
用于清除该组件持续执行的函数--性能优化(组件跳转后就会销毁)
性能优化:离开当前组件,清除组件内的持续执行的函数。可以最大程度性能优化。--比如清除定时器,清除onresize 等等。换句话说,进入页面执行,离开页面关闭。
13.前端路由vue-router
作用:做页面的跳转【基于onhashchange】
this.$router.back() ---返回上一级
1)安装:
方式1--手动安装:yarn add vue-router
方式2--创建项目时-多选一个路由Router
基本结构:
import Vue from 'vue' // 引入vue
import Router from 'vue-router' // 引入路由
Vue.use(Router) // 注册一下路由
//引入组件
import...from ... //路径可以使用@/代表 src目录 比如 @/views/home/home.vue
//路由的配置
const routes = [
{
path: '/',
redirect: '/home' // 重定向 一进页面就选中首页
},
// 路由的配置
{ path: '路径1', component: 组件1 },
{ path: '路径2', component: 组件2 },
{ path: '路径3', component: 组件3 },
]
const router = new Router({
routes
})
export default router
2)路由使用步骤
1.建--创建页面级别的组件 --
然后在router,js文件中引入组件
2.配--配置路由,router.js 中--配置路径path和组件component的一一对应关系。
然后给路由设置出口--
页面导航设置要跳转的路径--router-link :to
3.测试是否生效
注意:一定要有路由出口:组件要渲染出来的位置。
<router-view /> 或 <router-view></router-view>
3)路由跳转
<router-link :to="要跳转的路径"></router-link>
//最终这个路由自带的组件,会被渲染成a标签。
4)嵌套路由
//路由的配置
const routes=[
{ //一级路由
path:'/home',
component:Home,
redirect: '/home/recommend', // 重定向 当在这个页面时就默认加载recommend页面
children:[ //二级路由
{ path: '/home/路径1',component:儿子组件1},
{ path: '/home/路径2',component: 儿子组件2},
]
},
{ path:'/my',component:My},
{ path:'/friend',component:Friend},
]
14.两个路由对象和路由传参
1)$route:当前路由对象,主要用于获取一些路由信息,包含了routes中配置的 全部信息,以及默认的路由对象方法。
path --获取地址栏路径
query --获取query方式的参数--实际就是地址栏传参(原生js通过a标签href属性向地址栏传参)。
//A页面传出
this.router.push({
path:"路径",
query:{
参数名:参数值
}
})
//B页面获取
this.$route.query
params --获取params 方式的参数 --通过路由名传参,但是传到下个页面刷新数据旧没了-不靠谱,只能用于移动端。
//A页面发出
this.$router.push({
name: '路由的名字',
params: {
参数名: 参数值
}
})
//B页面接收
this.$route.params
2)$router:路由实例对象,相当于new Router() ,主包含了,push\beforeEach等路由处理方法
push() --跳转 go(-1) --返回
路由懒加载:配置项中-component:()=>import('路径') ---进入这个页面才会加载路由--性能优化
跨页面传参的两种方式:本地存储,路由传参(地址栏传参)
15.addRoutes
动态添加更多路由规则,参数必须是一个符合routes选项要求的数组。常用于动态路由的 追加。
16.组件通信
作用:组件之间相互传递数据。
1)父传子
父组件 在加载的子组件标签上绑数据,子组件用props接收即可
//父
<template>
<div class="father">
//直接绑定固定值或data中的动态值
<Son :money="money" desc="儿子,爸爸给你100块" :属性名="属性值" />
</div>
</template>
// 子组件
export default {
//简单方式
props: ['money', 'desc', '属性名'],
//高级版接收数据
props: {
// 注意: 如果不满足这些条件,会警告,但是仍然正常显示。
money:{
type: 类型, // String Number Boolean Object Array
required: 是否必填(可选参数) // true false
// 基本类型直接写 如果是对象或数组 写一个函数 再返回默认值
default: 默认值(可选参数)
}
}
}
2)子传父:子组件传递给父组件
//父组件
<template>
<子组件的标签 @子组件自定义的事件名="处理函数"></子组件的标签>
</template>
export default {
methdos:{
处理函数(data){
//data就是子组件传过来的数据
}
}
}
//子组件
this.$emit("自定义事件名",自身数据) //可以在函数内触发
3)中央事件总线Bus
注意:bus传递数据,两个组件需要同时存在,同时 渲染的状态
a)首先在main.js中定义一个空的vue实例,当成中介
Vue.prototype.$bus=new vue()
b)A组件数据传递出去
this.$bus.$emit("自定义事件名",数据) //发送数据出去需要用$emit
c)B组件接收
//接收数据的组件用到$on方法接收
this.$bus.$on("自定义事件名",(data)=>{
//data就是接收到的数据
})
4)$attrs
$attrs包含了所有未用props接收的,绑定在子组件上的属性。
//1.在子组件上绑定数据
<Son :sonData="data"></Son>
//2.子组件内部使用数据
created(){
console.log(this.$attrs) //$attrs包含了所有未用props接收的,绑定在子组件上的属性。
}
//3.子组件内部可以继续使用v-bind="$attrs" 向下传递数据
<GrondSon v-bind="$attrs"></GrondSon>
17.插槽
注意:父组件使用子组件的时候必须用双标签,要插入的内容写在双标签内
作用:slot是对组件的扩展,子组件预留插槽,将来使用组件的时候,父组件可以把内容插入插槽的位置
分类:匿名插槽、具名插槽、作用域插槽,解构插槽
//1.子组件通过slot标签占个坑
//2.父组件中标签上加slot='相同命名',标签内容就替换子组件中< slot name="相同命名">标签位置
//父组件
<Son2>
//slot方法
<p slot="header">我是内容 header</p>
<p slot="main">我是内容 main</p>
//插入作用域插槽,slot-scope取到的是子组件绑定在插槽上的数据
<p slot="footer" slot-scope="scope">{{scope}}我是内容 footer</p>
</Son2>
// 子组件
<div class="son2">
<header>
<!-- 匿名插槽 -->
<!-- <slot></slot> -->
<!-- 具名插槽 -->
<slot name="header"></slot>
</header>
<main>
<!-- <slot></slot> -->
<!-- 具名插槽 -->
<slot name="main"></slot>
</main>
<footer>
<!-- 作用域插槽 -->
//将自身数据绑定到slot上,父组件就可以使用slot-scope取到
<slot :content="content" :msg="msg" name="footer"></slot>
</footer>
</div>
指令:v-slot 【vue 2.6.x 推出的,取代插槽的以前的写法】
// 子组件写法不变 只是改不了父组件的写法
//ps:template 不会被解析,而新方法不能直接在标签上v-slot,所以写在template上
<Son2>
<template v-slot:header>
<p>我是内容 header header</p>
</template>
//v-slot:可以简写成#
<template #main>
<p>我是内容 main main</p>
</template>
//插入作用域插槽
<template #footer="scope">
<p>{{scope}}我是内容 footer footer</p>
</template>
</Son2>
解构插槽
<current-user v-slot="{ user }">
{{ user.firstName }}
</current-user>
18.vue结合element-ui的表单验证
表单四验证:1.非空 2.长度 3.正则 4.一致性
1)给el-form绑定属性
//写在el-form 最外层标签上的属性
ref="accAddForm" //ref="自定义表单的名字,用于验证的时候通过$refs.名字 获取整个表单 "
:model="accAddForm" //:model="要绑定的数据集合的对象名"
status-icon //status-icon --向输入框最后添加小icon的标识
:rules="rules" //:rules="rules" -- 自定义规则的对象-在data中指定规则,触发方式,验证函数等
size="small" //用于控制该表单内组件的尺寸(输入框的高度)
label-width="100px" //表单域标签的宽度(就是前面那个提示文字),el-form-item会继承
class="account-add-form"
2)给el-form-item添加属性 prop="要验证的数据名" ; 给el-input 绑定v-model=要绑定的数据集合的对象名.要验证的数据名
<!-- prop --表单域 model 字段,在使用 validate、resetFields 方法的情况下,该属性是必填的 label 标签文本-->
<el-form-item label="账号" prop="account">
<el-input v-model="accAddForm.account"></el-input>
</el-form-item>
3)data中 定义数据集合对象 、 rules 自定义规则对象 、 return同级定义自定义验证规则的验证函数、方法中触发统一验证,比如点击事件的回调函数。
//验证函数
/* rule --信息对象,用不上只作占位,value--用户输入的值 callback--验证回调函数 new Error是提示文字*/
const checkOldPwd = (rule, value, callback) => {
if (!value) {
callback(new Error("密码不能为空,这里的密码验证要发送ajax请求来判断"));
} else {
callback();
}
};
return {
passwordChange: { //数据集合 也是el-form中定义的:model值
oldPwd: "",
newPwd: "",
confirmPwd: "",
},
//验证规则对象 绑定在form上的 对象下的配置项:
//required--是否可选
//validator-自定义验证规则的触发函数,tirgger--触发方式
rules: {
oldPwd: { required: true, validator: checkOldPwd, trigger: "blur" },
newPwd: { required: true, validator: checkNewPwd, trigger: "blur" },
confirmPwd: {
required: true,
validator: checkConfirmPwd,
trigger: "blur",
},
},
};
//方法中触发的统一验证
methods:{
goodsAdd() {
//如果不传入回调函数,则返回一个promise
this.$refs.goodsAddForm.validate();
},
}
19.面包屑导航的实现
步骤:1.路由中配置自定义属性meta 2.计算面包屑 3.刷新和路由地址变化的时候调用计算函数即可。
1)在路由中配置meta字段
作用:为了渲染面包屑的时候取出来,作为面包屑的内容进行渲染。-
例:meta: { title: '个人中心' }, --this.$route.matched 获取到路由对象下的一个数组,数组元素中包含了meta和path
2)计算面包屑
就是将面包屑需要的内容和path 从路由对象中取出来放到一个数组里-最后进行渲染。
export default {
data() {
return {
breadArr: [] // 面包屑数组 默认是空--要进行渲染给面包屑的
}
},
methods: {
// 计算面包屑
calcBreadArr() {
let ret = [ { title: '首页', path: "/home" } ] //默认指定存在一个首页--就是默认已经添加一条数据,也可写在上面数组breaArr中
this.$route.matched.forEach(item => { //拿到metched 数组遍历
if (item.meta.title) { //取到包含title的元素--面包屑标题
ret.push({ //包含名字和路径的对象添加到数组中
title: item.meta.title,
path: item.path //面包屑的path
})
}
})
this.breadArr = ret; // 赋值给面包屑数组
}
},
created() {
this.calcBreadArr() // 刷新调用计算 created函数:vue生命周期创建后
},
watch: { //watch侦听器-监听路由变化
"$route.path"() {
this.calcBreadArr() // 切换导航调用计算
}
}
}
3)渲染面包屑
<!-- 面包屑 -->
<el-breadcrumb separator="/">
<!-- 循环渲染 -->
<el-breadcrumb-item
v-for="item in breadArr" // breadArr:面包屑数组
:key="item.title"
:to="{ path: item.path }"
>{{ item.title }}</el-breadcrumb-item>
</el-breadcrumb>
20.路由守卫
作用:路由跳转前做一些验证,比如登录验证,是网站中的普遍需求。
常用全局守卫:beforEach
守卫方法接收三个参数:
to:即将要进入的目标路由对象
from:当前导航正要离开的路由对象
next:执行下一步守卫
实例:判断登录状态
登录的时候可以在最后一栏输入框加上键盘事件keyup来调用提交函数-提升用于体验
router.beforeEach((to, from, next) => {
let isLogin = local.get('t_k') ? true : false //获取本地存储的token,就是看你登录没
if (isLogin) {
next() //放行
} else {
if (to.path === '/login') { //没有登录过,但是要去登录页,也放行
next()
} else {
next('/login') //没有登陆过,跳其他地方的时候默认跳登录页
}
}
})
退出登录
注意:this.$message是vue的方法
handleCommand(command) { //command 的回调函数, comman的是elementui定义的一个事件,下拉菜单点击的时候触发,写在下拉框最外层。 子菜单定义command="值"
//点击后会自动将点击的一栏中command值传入回调函数中
if (command === 'logout') { //点了退出
local.clear() // 全清本地存储,为了清除本地token,也可以用removeitem 只清除token就好
this.$message({ type: 'success', message: '退出成功,下次再来,哥哥' }) //弹出提示
this.$router.push('/login') // 跳转到登录
} else if (command === 'personal') {
this.$router.push('/account/personal')
}
},
21.获取到数据的分页处理(结合element-ui)
1)data中定义要渲染的数据
return {
tableData: [], // 表格数据--到时候做循环渲染用
currentPage: 1, // 当前页 默认1
pageSize: 5, // 每页条数 默认5
total: 0, // 数据总条数 初始值为0
}
2)调用api函数发送ajax获取数据并进行处理
methods: {
// ajax请求获取数据
async fetchData() { //async修饰函数-和await连用的
// 发送ajax 获取数据 解构得到值,total:总页数 data:获取到的数据
//await修饰promise 等待promise的状态流转
let { total, data } = await getAccountList({ //调用自定义的请求函数,并传入请求参数,得到的结果进行解构赋值
currentPage: this.currentPage,
pageSize: this.pageSize,
})
// 获取到的data是一个数组,将数组中的ctime元素用【moment时间格式处理库】处理一下
data.forEach(
(item) =>
(item.ctime = moment(item.ctime).format('YYYY-MM-DD HH:mm:ss')) // moment时间处理库需要安装-引入才能使用
)
// 赋值给data中的值
this.total = total
this.tableData = data
},
// 当前页面改变--饿了么ui的事件回调函数
handleCurrentChange(page) {
this.currentPage = page
this.fetchData() // 调用ajax请求
},
// 每页条数改变--饿了么ui的事件回调函数
handleSizeChange(size) {
this.pageSize = size
this.fetchData() // 每页条数改变 调用【3】次
},
created() { //vue生命周期-创建后
// 进入页面调用一次ajax请求数据
this.fetchData()
},
}