VUE2
- 一、Vue.js devtools
- 二、Vue简介
- 1、什么是Vue
- 2、Vue的特性
- 1.数据驱动视图
- 2.双向数据绑定
- 3.MVVM
- 三、Vue的基本使用
- 1、基本使用步骤
- 四、Vue的指令与过滤器
- 1、什么是指令
- 2、内容渲染指令
- 1.v-text
- 2.{{}}
- 3.v-html
- 3、属性绑定指令
- 4、事件绑定指令
- 5、双向绑定指令
- 6、条件渲染指令
- 7、列表渲染指令
- 8、过滤器 Filter (VUE3就没有了)
- 9、私有过滤器和全局过滤器
- 10、使用过滤器格式化时间
- 10、连续使用多个过滤器及传参
- 五、Vue侦听器
- 1、什么是 watch 侦听器
- 2、watch 侦听器的格式及immediate选项
- 3、深度侦听 deep选项
- 六、计算属性
- 1、什么是计算属性
- 七、Axios的简单使用
- 1、什么是Axios
- 2、Axios传参
- 3、await使用
- 4、使用解构赋值
- 5、基于Axios.get 和 Axios.post 发起请求
- 八、vue-cli
- 1、什么是单页面应用程序
- 2、什么是vue-cli
- 3、vue-cli 安装和使用
- 4、vue-src目录下文件构成
- 5、vue项目运行流程
- 九、vue组件
- 1、什么是组件化开发
- 2、vue中的组件化开发
- 3、vue组件的三个组成部分
- 4、vue组件的methods方法
- 5、vue组件的根节点及less的使用
- 6、vue组件之间的父子关系
- 7、使用vue组件的三个步骤
- 8、注册全局组件
- 十、vue组件的props (道具)
- 1、什么是 props 属性
- 2、props 自定义属性只读
- 3、props 的 default 默认值
- 4、props 的 type 值类型
- 5、props 的 required 必填项
- 十一、vue组件之间样式冲突问题
- 1、scope
- 2、/deep/ 样式穿透
- 十二、vue生命周期
- 1、组件的生命周期
- 2、组件生命周期函数的分类
- 十三、组件之间的数据共享
- 1、组件之间的关系
- 2、父组件向子组件传值
- 3、子组件向父组件传值
- 4、兄弟组件之间的传值
- 十四、ref引用
- 1、什么是ref引用
- 2、使用ref引用DOM元素
- 3、使用ref引用组件
- 4、使用this.$nextTick(callback)
- 十五、数组中一些方法的补充
- 1、数组中的some循环方法
- 2、数组中的every方法
- 3、数组中的reduce方法
- 十六、动态组件
- 1、什么是动态组件
- 2、如何实现动态组件的渲染
- 3、keep-alive 的使用
- 4、keep-alive 对应的生命周期函数
- 5、keep-alive 的 include 属性
- 6、组件注册名称 和 组件声明时的 name 的区别
- 十七、插槽
- 1、什么是插槽
- 2、v-slot
- 3、v-slot 的后备内容
- 4、具名插槽的定义和使用
- 5、作用域插槽的用法
- 十八、自定义指令
- 1、什么是自定义指令
- 2、自定义指令的分类
- 3、私有自定义指令
- 4、全局自定义指令
- 十九、ESLint
- 1、什么是ESLint
- 2、安装ESLint
- 3、vscode 配置ESLint
- 二十、axios挂载到vue原型上
- 1、Axios的基本使用
- 2、Axios挂载到vue原型上使用
- 3、Axios直接挂载到vue原型上使用的缺点
- 二一、前端路由
- 1、什么是前端路由
- 2.SPA与路由
- 3.前端路由的工作方式
- 4.实现简易的前端路由
- 二一、vue-router的基本使用
- 1、什么是vue-router
- 2、vue-router 安装和配置的步骤
- 3、使用 router-link 替代 a 链接
- 4、路由重定向
- 5、嵌套路由
- 6、动态路由匹配
- 7、声明式导航&编程式导航
- 8、导航守卫
- 1、全局前置守卫
一、Vue.js devtools
https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd/related
直接添加到谷歌浏览器就行
打开扩展程序,点击详情按钮
二、Vue简介
1、什么是Vue
官方给出的概念: Vue(读音/vju:/,类似于view)是一套用于构建用户界面的前端框架。
1.构建用户界面
- 用vue往html页面中填充数据,非常的方便
2.框架
- 框架是一套现成的解决方案,程序员只能遵守框架的规范,去编写自己的业务功能!
- 要学习vue,就是在学习vue框架中规定的用法!
- vue的指令、组件(是对UI结构的复用)﹒路由、Vuex、vue组件库。
2、Vue的特性
主要体现在两方面:
-
数据驱动视图
-
双向数据绑定
1.数据驱动视图
2.双向数据绑定
在网页中,form表单负责采集数据,Ajax负责提交数据。
- js数据的变化,会被自动渲染到页面上
- 页面上表单采集的数据发生变化的时候,会被vue自动获取到,并更新到js数据中
3.MVVM
注意:数据驱动视图和双向数据绑定的底层原理是MVM (Mode数据源、View视图、ViewModel就是vue的实例)
三、Vue的基本使用
1、基本使用步骤
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<script src="./lib/vue@2.7.13.js" type="text/javascript" charset="utf-8"></script>
<body>
<!-- 希望 vue 控制这个div,帮助我们把数据填充到div内部 -->
<div id="app">
{{username}}
</div>
</body>
<script type="text/javascript">
//创建 Vue 的实例对象
const vm = new Vue({
//el 属性是固定的写法,表示当前 vm 实例要控制页面上的那个区域,接收的值是一个选择器
el: '#app',
//data 对象就是要渲染到页面上的数据
data: {
username: '坏蛋阿土'
}
})
</script>
</html>
MVVM
四、Vue的指令与过滤器
1、什么是指令
指令(Directives)是vue为开发者提供的模板语法,用于辅助开发者渲染页面的基本结构。
vue中的指令按照不同的用途可以分为如下6大类:
- ①内容渲染指令
- ②属性绑定指令
- ③事件绑定指令
- ④双向绑定指令
- ⑤条件渲染指令
- ⑥列表渲染指令
注意:指令是vue开发中最基础、最常用、最简单的知识点。
2、内容渲染指令
内容渲染指令用来辅助开发者渲染DOM元素的文本内容。常用的内容渲染指令有如下3个:
- v-text
- {{}}
- v-html
1.v-text
v-text指令的缺点是:会覆盖元素内部原有的内容
2.{{}}
3.v-html
3、属性绑定指令
插值表达式只能用在元素的内容节点中,不能用在元素的属性节点中!
在vue中,可以使用v-bind:指令,为元素的属性动态绑定值
-
简写是英文的 :
-
在使用v-bind属性绑定期间,如果绑定内容需要进行动态拼接,则字符串的外面应该包裹单引号
<div :tit1e=" 'id' + index">这是一个 div</div>
id1 id2 id3
index是一个变量
4、事件绑定指令
在methods中定义事件的处理函数
- methods中定义的方法中可以通过this访问到data中定义的数据 , 其中 this===vm
- 在绑定事件处理函数的时候,可以使用()传递参数
- 事件绑定简写 @
- vue提供了内置变量,名字叫做$event,它就是原生DOM的事件对象e,在需要使用事件对象但是函数又需要传参的时候使用
如果同时需要传参,那就使用$event
事件修饰符
按键修饰符
5、双向绑定指令
v-bind 与 v-model区别:
v-model指令的修饰符
oninput 当input的value值发生变化时就会触发,(与onchange的区别是不用等到失去焦点就可以触发了)
6、条件渲染指令
1.v-show的原理是:动态为元素添加或移除display: none 样式,来实现元素的显示和隐藏
- 如果要频繁的切换元素的显示状态,用 v-show 性能会更好
2.v-if的原理是:每次动态创建或移除元素,实现元素的显示和隐藏
- 如果刚进入页面的时候,某些元素默认不需要被展示,而且后期这个元素很可能也不需要被展示出来,此时 v-if 性能更好
疑问:vue 的话会先编译后执行
type 在 data 中
7、列表渲染指令
删除的时候可以用索引
注意事项:
1.
label for id得动态生成,要不然一直是第一个
2.v-model 内部会去判断 input 的 type,从而确定要把数据渲染给 text 还是 checkbox
3.数组中根据条件删除
8、过滤器 Filter (VUE3就没有了)
- 1.要定义到filters节点下,本质是一个函数
- 2.在过滤器函数中,一定要有return值
- 3.在过滤器的形参中,可以获取到“管道符"前面待处理的那个值
- 4.如果全局过滤器和私有过滤器名字一致,此时按照"就近原则",调用的是"私有过滤器”
- 1.要定义到filters节点下,本质是一个函数
- 2.在过滤器函数中,一定要有return值
- 3.在过滤器的形参中,就可以获取到"管道符"前面待处理的那个值
9、私有过滤器和全局过滤器
10、使用过滤器格式化时间
使用Day.js对时间进行格式化处理
10、连续使用多个过滤器及传参
五、Vue侦听器
1、什么是 watch 侦听器
监听用户名是否变化
2、watch 侦听器的格式及immediate选项
1.方法格式的侦听器
- 缺点1:在刚进入页面的时候,无法自动触发一次
- 缺点2:如果侦听的是一个对象,如果对象中的属性发生了变化,不会触发侦听器
2.对象格式的侦听器
- 好处1:可以通过immlediate选项,让侦听器自动触发一次
- 好处2:可以通过deep选项,让侦听器深度监听对象中每个属性的变化
3、深度侦听 deep选项
六、计算属性
1、什么是计算属性
定义的时候是一个方法,用的时候就当成VM的一个属性去用就好了
特点:
- 1.定义的时候,要被定义为“方法"
- ⒉.在使用计算属性的时候,当普通的属性使用即可
好处:
-
1.实现了代码的复用
-
2.只要计算属性中依赖的数据源变化了,则计算属性会自动重新求值
七、Axios的简单使用
1、什么是Axios
2、Axios传参
3、await使用
- 如果调用某个方法的返回值是Promise 实例,则前面可以添加 await
- await只能用在被 async 修饰的方法中
4、使用解构赋值
上面这个res.data 是真实数据中的data属性的值
解构赋值的时候,使用:进行重命名
- 1.调用axios 之后,使用async/ await进行简化
- 2.使用解构赋值,从 axios封装的大对象中,把 data 属性解构出来
- 3.把解构出来的 data属性,使用冒号进行重命名,一般都重命名为{ data: res }
5、基于Axios.get 和 Axios.post 发起请求
八、vue-cli
1、什么是单页面应用程序
单页面应用程序(英文名: Single Page Application)简称SPA,顾名思义,指的是一个 Web 网站中只有唯一的一个HTML页面,所有的功能与交互都在这唯一的一个页面内完成。
2、什么是vue-cli
vue-cli是Vue.js 开发的标准工具。它简化了程序员基于webpack 创建工程化的Vue项目的过程。
引用自vue-cli官网上的一句话:
程序员可以专注在撰写应用上,而不必花好几天去纠结webpack配置的问题。
3、vue-cli 安装和使用
vue-cli是 npm 上的一个全局包,使用npm install 命令,即可方便的把它安装到自己的电脑上:
npm install -g @vue/cli
基于vue-cli 快速生成工程化的Vue项目:
vue create 项目的名称
切记:vue的安装路径不能有中文,不仅仅是文件名,是整个路径都不能有中文
4、vue-src目录下文件构成
- assets文件夹:存放项目中用到的静态资源文件,例如: css样式表、图片资源
- components文件夹:程序员封装的、可复用的组件,都要放到components目录下
- main.js是项目的入口文件。整个项目的运行,要先执行main.js
- App.vue是项目的根组件。|
5、vue项目运行流程
在工程化的项目中,vue要做的事情很单纯:
通过 main.js 把 App.vue 渲染到 index.html 的指定区域中。
其中:
- App.vue 用来编写待渲染的模板结构
- index.html 中需要预留一个el区域
- main.js 把 App.vue 渲染到了 index.html 所预留的区域中
// 导入 vue 这个包 得到 vue 构造函数 相当于 <script src="./lib/vue@2.7.13.js"></script>
import Vue from 'vue'
// 导入 APP.vue 根组件,后面会把 APP.vue中的模板结构,渲染到 HTML 页面中
import App from './App.vue'
Vue.config.productionTip = false
// 创建 Vue 的实例对象
new Vue({
//把 render 函数指定的组件,渲染到 HTML 页面中
render: h => h(App),
}).$mount('#app')
// 这里 Vue 实例的 $mount()方法,和el属性的作用完全一样 绑定 index.html 页面中要被渲染的区域 div#app
// 最后会把 div#app 这一层覆盖掉
暂且这样,后面再理解
九、vue组件
1、什么是组件化开发
组件化开发指的是:
根据封装的思想,把页面上可重用的UI结构封装为组件,从而方便项目的开发和维护。
组件是对UI结构的复用
2、vue中的组件化开发
vue是一个支持组件化开发的前端框架。
vue 中规定︰组件的后缀名是.vue。之前接触到的App.vue 文件本质上就是一个vue的组件。
3、vue组件的三个组成部分
每个.vue 组件都由3部分构成,分别是:
- template ->组件的模板结构
- script ->组件的JavaScript行为
- style ->组件的样式
<template>
<div class="hello">
<h1>{{username}}</h1>
</div>
</template>
<script>
// 默认导出 固定写法
export default {
// data 数据源
// 注意:vue组件中 data 不和 html 中一样,不能指向对象
// 组件中的 data 必须是一个函数
data() {
// 这个 return 出去的 {} 中,可以定义数据
return {
username: 'hdat'
}
}
}
</script>
<style>
</style>
4、vue组件的methods方法
<template>
<div class="hello">
<h1>{{username}}</h1>
</div>
</template>
<script>
// 默认导出 固定写法
export default {
// data 数据源
// 注意:vue组件中 data 不和 html 中一样,不能指向对象
// 组件中的 data 必须是一个函数
data() {
// 这个 return 出去的 {} 中,可以定义数据
return {
username: 'hdat'
}
},
methods: {
change() {
// 在组件中,this 就表示当前组件的实例对象 不再是Vue的实例对象了
}
},
// 当前组件的侦听器
watch: {
},
// 当前组件的计算属性
computed: {
},
// 当前组件的过滤器 v3没有
filters: {
}
}
</script>
<style>
.hello {
background-color: antiquewhite;
}
</style>
在组件中,this 就表示当前组件的实例对象 不再是Vue的实例对象了
只有data有变化,其他的还和之前一样
5、vue组件的根节点及less的使用
使用 less ,需要在 style 属性后面添加 lang 属性
<style lang="less">
.hello {
background-color: antiquewhite;
}
</style>
组件中只有唯一一个根节点 一个div , 如果想用两个,就在上面再来个 div 包住
6、vue组件之间的父子关系
7、使用vue组件的三个步骤
导入注册使用
8、注册全局组件
通过components注册的是私有子组件
例如:
在组件A的 components节点下,注册了组件F。则组件F只能用在组件A中;不能被用在组件C中。
在vue项目的 main.js 入口文件 中,通过Vue.component()方法,可以注册全局组件。示例代码如下:
// 导入需要全局注册的组件
import Count from '@/components/Count.vue'
//参数1:字符串格式,表示组件的“注册名称”
//参数2:需要被全局注册的那个组件
Vue.component('MyCount',Count)
十、vue组件的props (道具)
1、什么是 props 属性
props是组件的自定义属性,在封装通用组件的时候,合理地使用props可以极大的提高组件的复用性!
可以结合 v-bind 使用自定义属性 ,传过去的就是一个数值
2、props 自定义属性只读
3、props 的 default 默认值
4、props 的 type 值类型
5、props 的 required 必填项
十一、vue组件之间样式冲突问题
1、scope
解决:
2、/deep/ 样式穿透
在父组件中修改子组件的样式
主要应用场景就是修改第三方组件样式的时候
103重新看
关于vue的原理
十二、vue生命周期
1、组件的生命周期
2、组件生命周期函数的分类
- created 生命周期函数,非常常用。
- 经常在它里面,调用 methods 中的方法,请求服务器的数据。
- 并且,把请求到的数据,转存到 data 中,供 template 模板渲染的时候使用
- 如果要操作当前组件的 DOM,最早只能在 mounted 阶段执行
- 当数据变化之后,为了能够操作到最新的 DOM 结构,必须把代码写到 updated 生命周期函数中
十三、组件之间的数据共享
1、组件之间的关系
2、父组件向子组件传值
- 子组件中的 props 的值只读,不建议修改,修改了之后不会影响父组件中绑定的值
- 一般就是把 props 中的值存到data中,再进行操作
3、子组件向父组件传值
- emit 发行 发表
4、兄弟组件之间的传值
也可以理解为 发布 和 订阅
十四、ref引用
1、什么是ref引用
2、使用ref引用DOM元素
3、使用ref引用组件
有了子组件的实例对象,就相当于子组件的this,就可以直接调用子组件中的方法了
ref 引用
4、使用this.$nextTick(callback)
在 数据变化并重新渲染DOM后再操作DOM 的情况下使用
点击按钮切换成文本框并自动聚焦,更改inputVisible的值,隐藏按钮,显示文本框,这个时候,不能直接去操作文本框,因为现在只是数据发生改变,DOM还没有重新渲染,所以,还没有Input这个标签,所以调用会报错,这个时候使用nextTick 等DOM重新渲染完成后有Input了再进行操作。
问题:为什么不能写在updated中?
首先updated是和methods是平级的,这一点要注意。写在updated中每次数据发生变化,都会触发一次updated函数,这样的话,当输入框显示的时候没有问题,当输入框隐藏藏了,就找不到input了(这里用的是v-if),所以注意区分是不是特有的,如果是特有的,就用nextTick,如果是公共的,那就用updated
十五、数组中一些方法的补充
1、数组中的some循环方法
使用foreach循环的话,会导致循环停不下来,所以当遍历数组元素,找到所需要的数组元素后便不希望继续往下循环的情况下,可以使用some循环
2、数组中的every方法
3、数组中的reduce方法
十六、动态组件
1、什么是动态组件
动态组件指的是动态切换组件的显示与隐藏
2、如何实现动态组件的渲染
vue 提供一个内置的 component 组件,专门用来实现动态组件的渲染。
<div class="box">
<!-- 渲染 Left 组件和 Right 组件 -->
<!-- 1. component 标签是 vue 内置的,作用:组件的占位符 -->
<!-- 2. is 属性的值,表示要渲染的组件的名字 -->
<!-- 3. is 属性的值,应该是组件在 components 节点下的注册名称 -->
<component :is="comName"></component>
</div>
<script>
import Left from '@/components/Left.vue'
import Right from '@/components/Right.vue'
export default {
data() {
return {
// comName 表示要展示的组件的名字
comName: 'Left'
}
},
components: {
// 如果在“声明组件”的时候,没有为组件指定 name 名称,则组件的名称默认就是“注册时候的名称”
Left,
Right
}
}
</script>
3、keep-alive 的使用
动态组件每次切换的时候,之前的组件就会被销毁。
使用 keep-alive 可以保持状态,组件被隐藏而不会被销毁,会被缓存。下次被激活还是原来的状态。
<button @click="comName = 'Left'">展示 Left</button>
<button @click="comName = 'Right'">展示 Right</button>
<div class="box">
<!-- 渲染 Left 组件和 Right 组件 -->
<!-- 1. component 标签是 vue 内置的,作用:组件的占位符 -->
<!-- 2. is 属性的值,表示要渲染的组件的名字 -->
<!-- 3. is 属性的值,应该是组件在 components 节点下的注册名称 -->
<!-- keep-alive 会把内部的组件进行缓存,而不是销毁组件 -->
<!-- 在使用 keep-alive 的时候,可以通过 include 指定哪些组件需要被缓存; -->
<!-- 或者,通过 exclude 属性指定哪些组件不需要被缓存;但是:不要同时使用 include 和 exclude 这两个属性 -->
<keep-alive exclude="MyRight">
<component :is="comName"></component>
</keep-alive>
</div>
4、keep-alive 对应的生命周期函数
当组件被缓存的时候,会自动触发组件的 deactivated 生命周期。
当组件被激活的时候,会自动触发组件的 activated 生命周期。
<script>
export default {
name: 'MyLeft',
data() {
return {
count: 0
}
},
created() {
console.log('Left 组件被创建了!')
},
destroyed() {
console.log('Left 组件被销毁了~~~')
},
// 当组件第一次被创建的时候,既会执行 created 生命周期,也会执行 activated 生命周期
// 当时,当组件被激活的时候,只会触发 activated 生命周期,不再触发 created。因为组件没有被重新创建
activated() {
console.log('组件被激活了,activated')
},
deactivated() {
console.log('组件被缓存了,deactivated')
}
}
</script>
5、keep-alive 的 include 属性
用来指定那些组件会被缓存
只有名称匹配的组件会被缓存,多个组件名之间使用英文的逗号进行分隔:
<!-- 只有MyRight 组件会被缓存 -->
<!-- 在使用 keep-alive 的时候,可以通过 include 指定哪些组件需要被缓存; -->
<!-- 或者,通过 exclude 属性指定哪些组件不需要被缓存;但是:不要同时使用 include 和 exclude 这两个属性 -->
<keep-alive include="MyRight">
<component :is="comName"></component>
</keep-alive>
6、组件注册名称 和 组件声明时的 name 的区别
<script>
export default {
// 当提供了 name 属性之后,组件的名称,就是 name 属性的值
// 对比:
// 1. 组件的 “注册名称” 的主要应用场景是:以标签的形式,把注册好的组件,渲染和使用到页面结构之中
// 2. 组件声明时候的 “name” 名称的主要应用场景:结合 <keep-alive> 标签实现组件缓存功能;以及在调试工具中看到组件的 name 名称
name: 'MyRight'
}
</script>
如果在“声明组件”的时候,没有为组件指定 name 名称,则组件的名称默认就是“注册时候的名称”
如果声明组件时为组件指定了 name 名称,那么组件的名称就是 name 属性的值, 注册组件的名称只是用来作为标签引入组件。
一般规范都会指定名称,如果不想区分,那就使这两个保持一致就好了。
十七、插槽
1、什么是插槽
插槽(slot)是 vue 为组件的组装者提供的能力,允许开发者在封装组件时,把不确定、希望由用户指定部分定义为插槽。
2、v-slot
vue 官方规定,每个 slot 插槽,都要有一个 name 名称。如果省略了,则有一个默认名称叫 default。
如果没有指定把自定义内容放到那个插槽中,他就会默认放到 default 插槽中
可以使用 v-slot 来指定内容放到那个插槽中,可以简写为 #
<!--App.vue-->
<div class="box" style="display: none;">
<!-- 渲染 Left 组件和 Right 组件 -->
<Left>
<!-- 默认情况下,在使用组件的时候,提供的内容都会被填充到名字为 default 的插槽之中 -->
<!-- 1. 如果要把内容填充到指定名称的插槽中,需要使用 v-slot: 这个指令 -->
<!-- 2. v-slot: 后面要跟上插槽的名字 -->
<!-- 3. v-slot: 指令不能直接用在元素身上,必须用在 template 标签上 -->
<!-- 4. template 这个标签,它是一个虚拟的标签,只起到包裹性质的作用,但是,不会被渲染为任何实质性的 html 元素 -->
<!-- 5. v-slot: 指令的简写形式是 # -->
<template #default>
<p>这是在 Left 组件的内容区域,声明的 p 标签</p>
</template>
</Left>
</div>
<!--Left.vue-->
<template>
<div class="left-container">
<h3>Left 组件</h3>
<hr />
<!-- 声明一个插槽区域 -->
<!-- vue 官方规定:每一个 slot 插槽,都要有一个 name 名称 -->
<!-- 如果省略了 slot 的 name 属性,则有一个默认名称叫做 default -->
<slot name="default"></slot>
</div>
</template>
3、v-slot 的后备内容
后备内容就是插槽的默认内容,当调用插槽的人并没有为其指定内容,那么可以展示默认内容
<slot name="default">
<h6>这是 default 插槽的后备内容</h6>
</slot>
4、具名插槽的定义和使用
5、作用域插槽的用法
<!--Article.vue -->
<!-- 文章的内容 -->
<div class="content-box">
<!-- 在封装组件时,为预留的 <slot> 提供属性对应的值,这种用法,叫做 “作用域插槽” -->
<slot name="content" msg="hello vue.js" :user="userinfo"></slot>
</div>
<script>
export default {
// 首字母要大写
name: 'Article',
data() {
return {
// 用户的信息对象
userinfo: {
name: 'zs',
age: 20
}
}
}
}
</script>
<!--App.vue -->
<template #content="{ msg, user }">
<div>
<p>啊,大海,全是水。</p>
<p>啊,蜈蚣,全是腿。</p>
<p>啊,辣椒,净辣嘴。</p>
<p>{{ msg }}</p>
<p>{{ user.name }}</p>
</div>
</template>
十八、自定义指令
1、什么是自定义指令
vue官方提供了v-text、v-for、v-model、v-if等常用的指令。除此之外vue还允许开发者自定义指令
2、自定义指令的分类
私有自定义指令
全局自定义指令
3、私有自定义指令
bind函数只调用1次。当指令第一次绑定到元素时调用,当DOM更新时 bind函数不会被触发。update函数会在每次DOM更新时被调用。
<div class="app-container">
<h1 v-color="color">App 根组件</h1>
<p v-color="'red'">测试</p>
<button @click="color = 'green'">改变 color 的颜色值</button>
<hr />
</div>
</template>
<script>
import Left from '@/components/Left.vue'
import Article from '@/components/Article.vue'
export default {
data() {
return {
color: 'blue'
}
},
components: {
Left,
Article
},
// 私有自定义指令的节点
directives: {
// 定义名为 color 的指令,指向一个配置对象
/* color: {
// 当指令第一次被绑定到元素上的时候,会立即触发 bind 函数
// 形参中的 el 表示当前指令所绑定到的那个 DOM 对象
bind(el, binding) {
console.log('触发了 v-color 的 bind 函数')
el.style.color = binding.value
},
// 在 DOM 更新的时候,会触发 update 函数
update(el, binding) {
console.log('触发了 v-color 的 update 函数')
el.style.color = binding.value
}
} */
//简写
color(el, binding) {
el.style.color = binding.value
}
}
}
</script>
4、全局自定义指令
全局共享的自定义指令需要通过“Vue.directive()”进行声明,在main.js中
// 全局自定义指令
Vue.directive('color', {
bind(el, binding) {
el.style.color = binding.value
},
update(el, binding) {
el.style.color = binding.value
}
})
//简写
Vue.directive('color', function(el, binding) {
el.style.color = binding.value
})
十九、ESLint
1、什么是ESLint
是一个代码检查工具,为了代码的规范化
2、安装ESLint
vue create demo3-eslint
3、vscode 配置ESLint
二十、axios挂载到vue原型上
1、Axios的基本使用
安装
npm i axios -S
单个组件使用
<template>
<div class="left-container">
<h3>Left 组件</h3>
<button @click="getInfo">发起 GET 请求</button>
</div>
</template>
<script>
import axios from 'axios'
export default {
methods: {
async getInfo() {
const { data: res } = await axios.get('http:www.liuolongbin.top:3006/api/get')
console.log(res)
}
}
}
</script>
<style lang="less" scoped>
.left-container {
background-color: orange;
min-height: 200px;
flex: 1;
}
</style>
2、Axios挂载到vue原型上使用
//main.js
import Vue from 'vue'
import App from './App.vue'
import axios from 'axios'
Vue.config.productionTip = false
// 全局配置 axios 的请求根路径
axios.defaults.baseURL = 'http://www.liulongbin.top:3006'
// 把 axios 挂载到 Vue.prototype 上,供每个 .vue 组件的实例直接使用 prototype原型
Vue.prototype.$axios = axios
// 今后,在每个 .vue 组件中要发起请求,直接调用 this.$axios.xxx
// 但是,把 axios 挂载到 Vue 原型上,有一个缺点:不利于 API 接口的复用!!!
new Vue({
render: h => h(App)
}).$mount('#app')
<!--left.vue-->
<template>
<div class="left-container">
<h3>Left 组件</h3>
<button @click="getInfo">发起 GET 请求</button>
<button @click="btnGetBooks">获取图书列表的数据</button>
</div>
</template>
<script>
// import axios from 'axios'
export default {
methods: {
async getInfo() {
const { data: res } = await this.$axios.get('/api/get')
console.log(res)
},
// 点击按钮,获取图书列表的数据
async btnGetBooks() {
const { data: res } = await this.$axios.get('/api/getbooks')
console.log(res)
}
}
}
</script>
<style lang="less" scoped>
.left-container {
background-color: orange;
min-height: 200px;
flex: 1;
}
</style>
3、Axios直接挂载到vue原型上使用的缺点
缺点:无法实现接口的复用
二一、前端路由
1、什么是前端路由
路由(router)就是对应关系。
Hash地址(#) 与 组件之间的对应关系。
2.SPA与路由
3.前端路由的工作方式
4.实现简易的前端路由
可以使用动态组件实现
<template>
<div class="app-container">
<h1>App 根组件</h1>
<a href="#/home">首页</a>
<a href="#/movie">电影</a>
<a href="#/about">关于</a>
<hr />
<component :is="comName"></component>
</div>
</template>
<script>
// 导入组件
import Home from '@/components/Home.vue'
import Movie from '@/components/Movie.vue'
import About from '@/components/About.vue'
export default {
name: 'App',
data() {
return {
// 在动态组件的位置,要展示的组件的名字,值必须是字符串
comName: 'Home'
}
},
created() {
// 只要当前的 App 组件一被创建,就立即监听 window 对象的 onhashchange 事件
window.onhashchange = () => {
console.log('监听到了 hash 地址的变化', location.hash)
switch (location.hash) {
case '#/home':
this.comName = 'Home'
break
case '#/movie':
this.comName = 'Movie'
break
case '#/about':
this.comName = 'About'
break
}
}
},
// 注册组件
components: {
Home,
Movie,
About
}
}
</script>
<style lang="less" scoped>
.app-container {
background-color: #efefef;
overflow: hidden;
margin: 10px;
padding: 15px;
> a {
margin-right: 10px;
}
}
</style>
二一、vue-router的基本使用
1、什么是vue-router
vue-router是vue.js官方给出的路由解决方案。它只能结合vue项目进行使用,能够轻松的管理SPA项目中组件的切换。
vue-router的官方文档地址:httpsa// router.vuejs.org/zh/
2、vue-router 安装和配置的步骤
1.安装
npm i vue-router@3.5.2 -S
2.创建
// src/router/index.js 就是当前项目的路由模块
import Vue from 'vue'
import VueRouter from 'vue-router'
// 把 VueRouter 安装为 Vue 项目的插件
// Vue.use() 函数的作用,就是来安装插件的
Vue.use(VueRouter)
// 创建路由的实例对象
const router = new VueRouter({
// routes 是一个数组,作用:定义 “hash 地址” 与 “组件” 之间的对应关系
routes: [
// 重定向的路由规则
{ path: '/', redirect: '/home' },
// 路由规则
{ path: '/home', component: Home },
// 需求:在 Movie 组件中,希望根据 id 的值,展示对应电影的详情信息
// 可以为路由规则开启 props 传参,从而方便的拿到动态参数的值
{ path: '/movie/:mid', component: Movie, props: true },
{
path: '/about',
component: About,
// redirect: '/about/tab1',
children: [
// 子路由规则
// 默认子路由:如果 children 数组中,某个路由规则的 path 值为空字符串,则这条路由规则,叫做“默认子路由”
{ path: '', component: Tab1 },
{ path: 'tab2', component: Tab2 }
]
},
{ path: '/login', component: Login },
{ path: '/main', component: Main }
]
})
//向外共享路由的实例对象
export default router
3.挂载
//main.js
import Vue from 'vue'
import App from './App2.vue'
// 导入路由模块,目的:拿到路由的实例对象
// 在进行模块化导入的时候,如果给定的是文件夹,则默认导入这个文件夹下,名字叫做 index.js 的文件
import router from '@/router'
// 导入 bootstrap 样式
import 'bootstrap/dist/css/bootstrap.min.css'
// 全局样式
import '@/assets/global.css'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
// 在 Vue 项目中,要想把路由用起来,必须把路由实例对象,通过下面的方式进行挂载
// router: 路由的实例对象
router
}).$mount('#app')
4.声明路由连接和占位符
//index.js
// 创建路由的实例对象
const router = new VueRouter({
// routes 是一个数组,作用:定义 “hash 地址” 与 “组件” 之间的对应关系
routes: [
// 重定向的路由规则
{ path: '/', redirect: '/home' },
// 路由规则
{ path: '/home', component: Home },
// 需求:在 Movie 组件中,希望根据 id 的值,展示对应电影的详情信息
// 可以为路由规则开启 props 传参,从而方便的拿到动态参数的值
{ path: '/movie/:mid', component: Movie, props: true },
{
path: '/about',
component: About,
// redirect: '/about/tab1',
children: [
// 子路由规则
// 默认子路由:如果 children 数组中,某个路由规则的 path 值为空字符串,则这条路由规则,叫做“默认子路由”
{ path: '', component: Tab1 },
{ path: 'tab2', component: Tab2 }
]
},
{ path: '/login', component: Login },
{ path: '/main', component: Main }
]
})
<!-- 只要在项目中安装和配置了 vue-router,就可以使用 router-view 这个组件了 -->
<!-- 它的作用很单纯:占位符 -->
<router-view></router-view>
3、使用 router-link 替代 a 链接
<!-- 当安装和配置了 vue-router 后,就可以使用 router-link 来替代普通的 a 链接了 -->
<!-- <a href="#/home">首页</a> -->
<router-link to="/home">首页</router-link>
<!-- 注意1:在 hash 地址中, / 后面的参数项,叫做“路径参数” -->
<!-- 在路由“参数对象”中,需要使用 this.$route.params 来访问路径参数 -->
<!-- 注意2:在 hash 地址中,? 后面的参数项,叫做“查询参数” -->
<!-- 在路由“参数对象”中,需要使用 this.$route.query 来访问查询参数 -->
<!-- 注意3:在 this.$route 中,path 只是路径部分;fullPath 是完整的地址 -->
<!-- 例如: -->
<!-- /movie/2?name=zs&age=20 是 fullPath 的值 -->
<!-- /movie/2 是 path 的值 -->
<router-link to="/movie/1">洛基</router-link>
<router-link to="/movie/2?name=zs&age=20">雷神</router-link>
<router-link to="/movie/3">复联</router-link>
<router-link to="/about">关于</router-link>
<hr />
4、路由重定向
路由重定向指的是:用户在访问地址A的时候,强制用户跳转到地址C,从而展示特定的组件页面。通过路由规则的redirect属性,指定一个新的路由地址,可以很方便地设置路由的重定向:
5、嵌套路由
<!--about.vue-->
<template>
<div class="about-container">
<h3>About 组件</h3>
<!-- 子级路由链接 -->
<router-link to="/about">tab1</router-link>
<router-link to="/about/tab2">tab2</router-link>
<hr />
<!-- 子级路由占位符 -->
<router-view></router-view>
</div>
</template>
//index.js
{
path: '/about',
component: About,
//路由重定向 从/about 重定向到 /about/tab1 也就是默认展示tab1
// redirect: '/about/tab1',
children: [
// 子路由规则
// 默认子路由:如果 children 数组中,某个路由规则的 path 值为空字符串,则这条路由规则,叫做“默认子路由”
{ path: '', component: Tab1 },
{ path: 'tab2', component: Tab2 }
]
},
通过 children 属性声明子路由规则
用重定向或者默认子路由都可以设置默认路由
6、动态路由匹配
动态路由指的是:把 Hash地址中可变的部分定义为参数项,从而提高路由规则的复用性。
获取参数值
//index.js
// 需求:在 Movie 组件中,希望根据 id 的值,展示对应电影的详情信息
// 可以为路由规则开启 props 传参,从而方便的拿到动态参数的值
{ path: '/movie/:mid', component: Movie, props: true },
<!-- movie.vue -->
<template>
<div class="movie-container">
<!-- this.$route 是路由的“参数对象” -->
<!-- this.$router 是路由的“导航对象” -->
<h3>Movie 组件 --- {{ $route.params.mid }} --- {{ mid }}</h3>
<button @click="showThis">打印 this</button>
</div>
</template>
<script>
export default {
name: 'Movie',
// 接收 props 数据
props: ['mid'],
methods: {
showThis() {
console.log(this)
}
}
}
</script>
<!-- app.vue -->
<!-- 注意1:在 hash 地址中, / 后面的参数项,叫做“路径参数” -->
<!-- 在路由“参数对象”中,需要使用 this.$route.params 来访问路径参数 -->
<!-- 注意2:在 hash 地址中,? 后面的参数项,叫做“查询参数” -->
<!-- 在路由“参数对象”中,需要使用 this.$route.query 来访问查询参数 -->
<!-- 注意3:在 this.$route 中,path 只是路径部分;fullPath 是完整的地址 -->
<!-- 例如: -->
<!-- /movie/2?name=zs&age=20 是 fullPath 的值 -->
<!-- /movie/2 是 path 的值 -->
<router-link to="/movie/1">洛基</router-link>
<router-link to="/movie/2?name=zs&age=20">雷神</router-link>
<router-link to="/movie/3">复联</router-link>
7、声明式导航&编程式导航
vue-router 中的编程式导航API
<template>
<div class="movie-container">
<button @click="goback">后退</button>
<!-- 在行内使用编程式导航跳转的时候,this 必须要省略,否则会报错! -->
<button @click="$router.back()">back 后退</button>
<button @click="$router.forward()">forward 前进</button>
</div>
</template>
<script>
export default {
name: 'Movie',
methods: {
goback() {
// go(-1) 表示后退一层
// 如果后退的层数超过上限,则原地不动
this.$router.go(-1)
}
}
}
</script>
8、导航守卫
1、全局前置守卫
//index.js
// 为 router 实例对象,声明全局前置导航守卫
// 只要发生了路由的跳转,必然会触发 beforeEach 指定的 function 回调函数
router.beforeEach(function(to, from, next) {
// to 表示将要访问的路由的信息对象
// from 表示将要离开的路由的信息对象
// next() 函数表示放行的意思
// 分析:
// 1. 要拿到用户将要访问的 hash 地址
// 2. 判断 hash 地址是否等于 /main。
// 2.1 如果等于 /main,证明需要登录之后,才能访问成功
// 2.2 如果不等于 /main,则不需要登录,直接放行 next()
// 3. 如果访问的地址是 /main。则需要读取 localStorage 中的 token 值
// 3.1 如果有 token,则放行
// 3.2 如果没有 token,则强制跳转到 /login 登录页
if (to.path === '/main') {
// 要访问后台主页,需要判断是否有 token
const token = localStorage.getItem('token')
if (token) {
next()
} else {
// 没有登录,强制跳转到登录页
next('/login')
}
} else {
next()
}
})
案例
p193