花了一个月半弄完可惜视频里案例的接口我都是产生跨域报错问题可能是里面的老师离职关闭了
不建议观看因为视频里的vue3内容并不多 还是需要找其他的视频观看比如最基本的setup就没有讲
就是讲了拦截器
Vuex自定义
指令vue2vue3的区别
Proxy跨域代理
如果接口是成功的 请留言私信我 爱你呦
Vue简介
1.什么是vue
官方给出的概念:Vue是一套用于构建用户界面的前端框架
构建用户界面<->框架
1.1 解读核心关键词:构建用户界面
前端开发者最主要的工作,就是为网站的使用者构建出美观、舒适、好用的网页
-美化样式 基于HTML
构建用户界面 -编写结构 基于CSS样式
-处理交互 基于JS
1.2 构建用户界面的传统方式
在传统的Web前端开发中,是基于jQuery + 模板引擎的方式来构建用户界面的
编写结构
基于模板结构技术 把数据渲染到页面上
优点:初步解放了前端开发者 从此不用手动拼接字符串来渲染网页结构
缺点:需要定义大量的模板结构 缺少语法高亮和智能提示 数据变化时需要重新调用模板编译的函数
美化样式
基础CSS样式 美化网页的可视化效果
处理交互
基于jQuery技术,处理用户和网页之间的交互行为
优点:屏蔽了DOM API 之间的兼容性,提高了DOM操作的效率和体验
缺点:当业务复杂时、数据变化频繁时,前端开发者需要把大量的时间和精力浪费在DOM的操作上
而不是核心业务的处理上
1.3 使用vue构建用户界面
使用vue构建用户界面,解决了jQuery + 模板引擎的诸多痛点 极大的提高了前端开发的效率和体验
编写结构
基于vue中提供的指令,可以方便快捷的渲染页面的结构
数据驱动视图图(只要页面依赖的数据源变化,则页面自动重新渲染)
Ps:指令是vue为开发者提供的模板语法,用来辅助开发者渲染页面的结构
美化样式
基础CSS样式 美化网页的可视化效果
处理交互
基于vue中提供的时间绑定 可以轻松处理用户和页面之间的交互行为
Ps:开发者把工作的重心放在核心业务的实现上
1.4解读核心关键词:框架
官方给vue的定位是前端框架,因为它提供了构建用户界面的一整套解决方案(vue全家桶)
vue(核心库)
vue-router(路由方案)
vuex(状态管理方案)
vue组件库(快速搭配页面UI效果的方案)
以及辅助vue项目开发的一系列工具
vue-cli(npm全局包:一键生成工程化的vue项目 - 基于webpack、大而全)
vite(npm全局包:一键生成工程化的vue项目-小而巧)
vue-devtools(浏览器插件:辅助调试的工具)
vetur(vscode插件:提供语法高亮和智能提示)
1.5总结:什么是vue
vue是一套用于构建用户界面的前端框架
指令 构建用户界面的一整天解决方案
数据驱动视图 构建用户界面<->框架 vue全家桶(vue+vue-router+vuex+组件库)
事件绑定 提供了辅助项目开发的配套工具
(vue-cli + vue-devtools)
2.vue的特性
vue框架的特性,主要体现如下两个方面
数据驱动视图
双向数据绑定
2.1数据驱动视图
在使用vue的页面中,vue会监听数据的变化,从而自动渲染页面的结构
自动渲染 变化
页面结构<-----------vue监听数据的变化<-------->页面所依赖的数据
好处 程序员只要把数据维护好,当页面数据发送变化时,页面会自动重新渲染
注意:数据驱动视图是单向的数据 绑定
2.2 双向数据绑定
在填写表单时,双向数据绑定可以辅助开发者在不操作DOM的前提下
自动把用户填写的内容同步到数据源中
自动渲染 变化
页面结构<-----------vue<-------->页面所依赖的数据
-----------> --------->
值发生变化 自动同步
好处 开发者不需要手动操作DOM元素 来获取表单元素最新的值
2.3 MVVM
MVVM是vue实现数据驱动视图和双向数据绑定的核心原理
MVVM指的是Model View 和ViewModel
Model 表示当前页面渲染时所依赖的数据源
View 表示当前页面所渲染的DOM结构
ViewModel 表示vue的实例 它是MVVM的核心
View ViewModel Model
DOM vue js Object
2.4 MVVM的工作原理
ViewModel作为MVVM的核心,是它把当前页面的数据源(Model)和页面结构(View)连接在一起
自动更新 监听数据源变化
View<-----------ViewModel<----------->Model
-----------> ----------->
监听DOM变化 自动同步
3.vue的版本
当前vue共有3个大版本其中
2.x版本的vue是目前企业级项目开发中的主流版本
3.x版本的vue于2020-09-19发布,生态还不完善,尚未在企业级项目开发中普及和推广
1.x版本的vue几乎被淘汰,不再建议学习与使用
总结:
3.x版本的vue是未来企业级项目开发的趋势
2.x版本的vue在未来会被逐渐淘汰
3.1 vue3.x和vue2.x版本的对比
vue2.x中绝大多数的API与特性,
在vue3.x中同样支持。同时,vue3.x中还新增了3.x所特有的功能
并废弃了某些2.x中的旧功能
新增的功能
组合式API 多根节点组件 更好的TypeScript
废弃的旧功能如下
过滤器 不再支持%on $off和$once实例方法
vue的基本使用
1.基本使用步骤
1 导入vue.js的script脚本文件
2 在页面中声明一个将要被vue所控制的DOM区域
3 创建vm实例对象
vue的指令与过滤器
1.指令的概念
指令是vue为开发者提供的模板语法 用于复制开发者渲染页面的基本结构
vue中的指令按照不同的用途可以分为如下6大类
内容渲染指令 属性绑定指令 事件绑定指令
双向绑定指令 条件渲染指令 列表渲染指令
注意:指令是vue开发中最基础 最常用 最简单的知识点
1.1 内容渲染指令
内容渲染指令依赖辅助开发者渲染DOM元素的文本内容 常用的有3个
v-text {{}} v-html
v-text
缺点:会覆盖元素原有的内容
用法示例
<!-- 把username 对应的值 渲染到第一个p标签中 -->
<p v-text="username"></p>
<!-- 把gender对应的值 渲染到第二个p标签中0 -->
<!-- 注意: 第二行p标签中 默认的文本 性别会被gender的值覆盖掉 -->
<p v-text="gender">性别</p>
{{}}
vue提供的{{}}语法,专门用来解决v-text会覆盖默认文本内容的值问题
{{}}语法专业名称是插值表达式
实际开发中用的最多,只是内容的占位符 不会覆盖原有的内容
v-html
v-text指令和插值表达式只能渲染纯文本内容。
如果要包含html标签的字符串渲染为页面的html元素 则用v-html
1.2 属性绑定指令
注意:
插值表达式只能用在元素的内容节点中 不能用在元素的属性节点中
如果需要为元素的属性动态绑定属性值 则需要用到v-bind属性绑定指令
简写
vue规定v-bind:指令可以简写为 :
使用JS表达式
在vue提供的模板渲染语法中,除了支持绑定简单的数据值之外 还支持js表达式的运算
{{number + 1}}
{{ok ? 'yes':'no'}}
在v-bind期间 如果绑定内容需要进行动态拼接 则字符串的外面应该包裹单引号
<div :title="'box'+index">这是一个div</div>
1.3 事件绑定指令
vue提供了v-on事件绑定指令,用来辅助程序员为DOM元素绑定事件监听。
通过v-on绑定的事件处理函数,需要在methods节点中进行声明
const vm = new Vue({
el:'#app',
data:{count:0},
methods:{
add(){
this表示当前new出来的vm实例对象
通过this可以访问到data中的数据
this.count +=1
}
}
})
注意:原生DOM对象有onclick oninput onkeyup等原生事件
分别为:v-on:click v-on:input v-on:keyup
事件对象event
在原生的DOM事件绑定中,可以在事件处理函数的形参处,接收事件对象event
同理v-on指令所绑定的事件处理函数中,同样可以接收到事件对象event
绑定事件并传参
在使用v-on指令绑定事件时,可以使用()进行传参,
<p>count的值是:{{ count }}</p>
<button v-on:click="add(2)">+1</button>
methods: {
add(step) {
this.count+=step
}
}
$event
$event是vue提供的特殊变量,用来表示原生的事件参数对象event
$event可以解决事情参数对象event被覆盖的问题
<button @click="add(n,$event)">+N</button>
method: {
add(n,e) {
this.count += n
const bgColor=e.target.style.backgroundColor
e.target.style.backgroundColor = bgColor==='red' ? '' : 'red'
}
}
}
事件修饰符
在事件处理函数中调用event.preventDefault()或event.stopPropagation()
因此vue提供了事件修饰符的概念,来辅助程序员更方便的对事件的触发进行控制
.prevent 阻止默认行为 *
.stop 阻止事件冒泡 *
.capture 以捕获模式触发当前的事件处理函数
.once 绑定的事件只触发1次
.self 只有在event.target是当前原生自身时触发事件处理函数
语法格式
.prevent 阻止默认行为 *
<a href="http://www.bnaidu.com" @click.prevent="show">跳到百度首页</a>
.stop 阻止事件冒泡 *
<div class="outer" @click="onOuterClick">
外层的div
<div class="inner" @click.stop="onInnerClick">内部的div</div>
</div>
最终输出 点击inner事件 没有outer事件
.capture 以捕获模式触发当前的事件处理函数
<div class="outer" @click="onOuterClick">
外层的div
<div class="inner" @click.stop="onInnerClick">内部的div</div>
</div>
最终输出 点击inner盒子 先触发outer事件 再触发inner事件
.once 绑定的事件只触发1次
<div class="inner" @click.once="onInnerClick">内部的div</div>
.self 只有在event.target是当前原生自身时触发事件处理函数
<div class="box" @click="onOuterClick">
<div class="outer" @click="onOuterClick">
外层的div
<div class="inner" @click.stop="onInnerClick">内部的div</div>
</div>
</div>
最终输出 点击outer触发 box和outer事件 点击inner触发 inner和box事件
按键修饰符
在监听键盘事件 外面经常需要判断详细的按键 此时可以为键盘相关的事件添加按键修饰符
<!-- 只有在key是Esc时调用vm.clearInput -->
<input type="text" @keyup.esc="clearInput" @keyup.enter="comit">
<!-- 只有在key是Esc时调用vm.comit -->
<input type="text" @keyup.enter="comit">
1.4 双向绑定指令
vue提供了v-model双向数据绑定指令 用来辅助开发者不操作DOM元素的前提 快速获取表单数据
v-model是双向 v-bind是单向
语法格式
<p>用户得到名字是{{ username }}</p>
<input type="text" v-model="username">
<select v-modelv="city">
<option value="1">白金</option>
<option value="2">阿松大</option>
<option value="3">请问</option>
</select>
注意 v-model指令只能配合表单元素一起使用
为了方便对用户输入的内容进行处理 vue为v-model提供了3个修饰符
.number 自动将用户的输入值转化为数值类型
.trim 自动过滤用户输入的首尾空白字符
.lazy 在"change"时而非"input时更新"
<input type="text" v-model.number="age">
<input type="text" v-model.trim="msg">
<input type="text" v-model.lazy="msg">
1.5 条件渲染指令
条件渲染指令用来辅助开发者按需控制DOM的显示与隐藏
条件渲染指令有如下两个
v-if v-show
v-if v-show的区别
实现原理不同
v-if指令会动态地创建或移除DOM元素 从而控制元素在页面上的显示与隐藏
v-show指令会动态为元素添加或移除style="display:none"样式 从而控制元素在页面上的显示与隐藏
性能消耗不同:
v-if有更高的切换开销 而v-show有更高的初始渲染开销
如果需要非常频繁地切换 使用v-show
如果运行时条件很少改变 使用v-if
v-else
v-if可以单独使用 或配合v-else指令一起使用
v-else-if
v-else-if指令,充当v-if的 else-if块 可以连续使用
<div v-if="type==='A'">优秀</div>
<div v-else-if="type==='B'">良好</div>
<div v-else-if="type==='C'">一般</div>
<div v-else="type==='D'">差</div>
1.6 列表渲染指令
vue提供了v-for指令 用来辅助开发者基于一个数组来循环渲染相似的UI结构
v-for指令需要使用item in items的特殊语法
items是待循环的数组
item是当前的循环项
data:{
list:[
{id:1,name:'zs'}.
{id:2,name:'ls'}
]
}
<ul>
<li v-for="item in list">姓名是:{{item.name}}</li>
</ul>
v-for中的索引
v-for指令还支持一个可选的第二个参数,即当前项的索引
语法格式为(item,index)in items
注意:v-for指令中的item项和index索引都是形参,可以根据需要进行重命名
使用key维护列表的状态
当列表的数据变化时,默认情况下,vue会尽可能的复用已存在的DOM元素,从而提升渲染的性能
这种默认的性能优化策略,会导致有状态的列表无法被正确更新
为了给vue一个提示,以便它能跟踪每个节点的身份,从而保证的列表被正确更新的前提下
提升渲染的性能。此时,需要为每项提供一个唯一的key属性
key的注意事项
1 key的值只能是字符串或数字类型
2 key的值必须具有唯一性(即:key的值不能重复)
3 数据项id属性的值作为key的值(id属性的值具有唯一性)
4 使用index的值当作key的值没有任意意义(index的值不具有唯一性)
5 使用v-for指令时一定要指的key的值(即提升性能、又防止列表状态絮乱)
单页面应用程序
1.什么是单页面应用程序
单页面应用程序(Single Page Application) SPA
指的是一个Web网站中只有唯一的一个HTML页面
所有功能与交互都在这唯一的一个页面内完成
2. 单页面应用程序的优点
1 良好的交互体验
单页应用的内容的改变不需要重新加整个页面
获取数据也是通过ajax异步获取
没有页面之间的跳转,不会出现白屏现象
2 良好的前后端工作分离模式
后端专注于提供API接口,更易实现API接口的复用
前端专注于页面的渲染,更利于前端工程化的发展
3 减轻服务器的压力
服务器只提供数据,不负责页面的合成与逻辑的处理,吞吐能力会提供几倍
3.单页面应用程序的缺点
任何一种技术都有自己的局限性,对于SPA单页面应用程序来说,主要的缺点有如下两个
1 首屏加载慢
路由懒加载
代码压缩
CDN加速
网络传输压缩
2 不利于SEO
SSR服务器端渲染
4.如何快速创建vue的SPA项目
vue官方提供了两种快速创建工程化的SPA项目的方式
基于vite创建SPA项目
基于vue-cli创建SPA项目
vite vue-cli
支持的vue版本 仅支持3.x 支持3.x和2.x
是否基于webpack 否 是
运行速度 快 较慢
功能完整度 小而巧 大而全
是否建议在企业级中使用 不建议 建议
vite的基本使用
1.创建vite的项目
即可基于vite创建vue3.x的工程化项目
npm init vite-app 项目名称
Ok to proceed? (y) y
cd 项目名称
npm install
npm run dev
2.梳理项目的结构
其中:
node_modules目录用来存放第三方依赖包
public是公共的静态资源目录
src是项目的源代码目录
.gitignore是Git的忽略文件
index.html是SPA单页面应用程序中唯一的HTML页面
package.json是项目的包管理配置文件
src这个项目源代码目录之下,包含了如下的文件和文件夹
assets目录用来存放项目中所有的静态资源文件
component目录用来存放项目中所有的自定义组件
App.vue是项目的根组件
index.css是项目的全局样式表文件
main.js是整个项目的打包入口文件
3.vite项目的运行流程
在工程化的项目中
vue要做的事情很单纯:通过main.js把App.vue渲染到index.html的指定区域中
其中:
App.vue用来编写待渲染的模板结构
index.html中需要预留一个el区域
main.js把App.vue渲染到了index.html所预留的区域中
组件化开发思想
1.什么是组件化开发
组件化开发指的是:根据封装的思想,
把页面上可重用的部分封装为组件,从而方便项目的开发和维护
2.组件化开发的好处
前端组件化开发的好处主要体现在以下两方面
提高了前端代码的复用性和灵活性
提升了开发效率和后期的可维护性
3.vue中的组件化开发
vue 是一个完全支持组件库开发的框架,vue中规定组件的后缀名是.vue
之前App.vue文件本质上就是一个vue的组件
vue组件的构成
1.vue组件组成结构
每个.vue组件都是由3部分构成,分别是:
template->组件的模板结构
script->组件的JavaScript行为
style->组件的样式
其中,每个组件中必须包含template模板结构,而script行为和style样式是可选的组成部分
2.组件的template节点
vue规定:每个组件对应的模板结构,需要定义到<template>节点中
<template>
<!-- 当前组件的DOM结构,需要定义到tempate标签的内部-->
</template>
注意:<template>是vue提供的容器标签,只起到包裹性质的作用,它不会被渲染为真正的DOM元素。
2.1 在<template>中使用指令
在组件的<template>节点中,支持使用前面所学的指令语法,来辅助开发者渲染当前组件的DOM结构
2.2 在template中定义根节点
在vue 2.x的版本中,<template>节点内的DOM结构仅支持单个根节点
<template>
<div>
<h1>这是App.vue根组件</h1>
<h3>abc</h3>
</div>
</template>
但是,在vue3.x的版本中,<template>中支持定义多个根节点
<template>
<h1>这是App.vue根组件</h1>
<h3>abc</h3>
</template>
3.组件的script节点
vue规定:组件内的<script>节点是可选的
开发者可以在<script>节点中封装组件的JS业务逻辑
<script>
//今后,组件相关的data数据、methods方法等
//都需要定义到export default所导出的对象中
export default {}
</script>
3.1 script中的name节点
可以通过name节点为当前组件定义一个名称
<script>
export default {
//name属性指向的是当前组件就的名称
name:'MyApp'
}
</script>
使用vue-devtools进行项目调试的时候,自定义的组件名称可以清晰的区分每个组件
3.2 script中的data节点
vue组件渲染期间需要用到的数据,可以定义在data节点中;
3.3 script中的methods节点
组件中的事件处理函数,必须定义到methods节点中
export default {
name:'MyApp',//组件的名称
data(){//组件的数据
return{
username:'zs',
count:0
}
},
methods:{//处理函数
add(){
this.count++
}
}
}
4.组件的style节点
vue规定: 组件内的<style>节点是可选的,
开发者可以在<style>节点中编写样式美化当前组件的UI结构
<style lang="css">
h1{
font-weight: normal;
}
</style>
其中<style>标签上的lang="css"属性是可选的 它表示所使用的样式语言
默认只支持普通的css语法 可选值还有less、scss
默认css属性
4.1 让style中支持less语法
如果希望使用less语法编写组件的style样式
运行npm install less -D命令安装依赖包 从而提供less语法的编译支持
在<style>标签上添加lang="less"属性 即可使用less语法编写组件的样式
组件的基本使用
1.组件的注册
组件之间可以进行相互的引用
App.vue
Home.vue <- Home.vue组件
About.vue <- About.vue组件
vue中组件的引用原则:先注册后使用
1.1 注册组件的两种方式
vue中注册组件的方式分为"全局注册"和"局部注册"
被全局注册的组件,可以在全局任何一个组件内使用
1.2全局注册组件
vue官方强烈推荐w3c规范自定义组件名(字母全小写必须包含一个连字符)
以此来避免与当前及未来HTML元素冲突
// 1.按需导入createApp函数
import { createApp } from 'vue'
// 2.导入待渲染的App.vue组件
import App from './App.vue'
// 3.调用createApp函数,创建SPA应用的实例
import Swiper from './components/01-globalReg/Swiper.vue'
import Swiper from './components/01-globalReg/Test.vue'
const app= createApp(App)
// 2.调用app.component()
app.component('my-swiper',Swiper)
app.component('my-test',Test)
//4.将app模板内容渲染到指定的el区域中
app.mount('#app')
1.3 使用全局注册组件
使用app.component()方法注册全局组件,直接以标签的形式进行使用
import Swiper from './components/01-globalReg/Swiper.vue'
import Swiper from './components/01-globalReg/Test.vue'
// 2.调用app.component()
app.component('my-swiper',Swiper)
app.component('my-test',Test)
<template>
<h1>这是<i>App.vue</i>根组件</h1>
<!-- <h3>abc---{{ username }}</h3>
<hr>
<p>count值是:{{ count }}</p>
<button v-on:clilck="add">+1</button> -->
<my-swiper></my-swiper>
<!-- <my-test></my-test> -->
</template>
1.4 局部注册组件
<template>
<h1>这是<i>App.vue</i>根组件</h1>
<my-swiper></my-swiper>
<my-test></my-test>
</template>
<script>
import Search from './components/02-privateReg/Search.vue'
export default {
components:{//通过components节点,为当前的组件注册私有子组件
'my-search':Search
}
}
</script>
1.5 全局注册和局部注册的区别
被全局注册的组件,可以在全局任何一个组件内使用
被局部注册的组件,只能在当前注册的范围内使用
应用场景:
如果某系组件在开发期间的使用频率很高,推荐进行全局注册
如果某些组件只在特定的情况下会被用到,推荐进行局部注册
1.6组件注册时名称的大小写
在进行组件的注册时,定义组件注册名称的方式有两种
使用kebab-case命名法(短横线命名法,例如 my-swiper和my-search)
使用PascalCase命名法(帕斯卡命名法或大驼峰命名法,例如MySwiper和MySearch)
短横线命名法的特点
必须严格按照短横线命名法进行使用
帕斯卡命名法的特点
即可以严格按照帕斯卡名称进行使用,又可以转换为短横线名称进行使用
注意:在实际开发中,推荐使用帕斯卡命名法为组件注册名称,业务它的适应性更强
1.7 通过name属性注册组件
在注册组件期间,除了可以直接提供组件的注册名称之外
还可以把组件的name属性作为注册后组件的名称
<template>
<h3>Swiper 轮播图组件</h3>
</template>
<script>
export default defineComponent({
name:'MySwiper' //name属性为当前组件的名字
})
</script>
import Swiper from './components/01-globalReg/Swiper.vue'
app.component('Swiper.name',Swiper)//相对于app.component('my-swiper',Swiper)
2.组件之间的样式冲突问题
默认情况下,写在.vue组件中的样式会全局生效,因此容易造成多个组件之间的样式冲突问题
根本原因:
1 单页面应用程序中,所有组件的DOM结构,都是基于唯一的index.html页面进行呈现的
2 每个组件中的样式,都会影响整个index.html页面中的DOM元素
2.1思考:如何解决组件样式冲突的问题
为每个组件分配唯一的自定义属性,在编写组件样式时,通过属性选择器来控制样式的作用域
<template>
<div class="container" data-v-001>
<h1>这是app.vue组件</h1>
<p>APp中的p标签</p>
<p>APp中的p标签</p>
<hr>
<my-list></my-list>
</div>
</template>
<script>
import MyList from './List.vue'
export default {
name:'MyApp',
components:{
MyList
}
}
</script>
<style lang="less">
.container[data-v-001]{
border: 1px solid red;
}
</style>
2.2 style节点的scoped属性
为了提高开发效率和开发体验,vue为style节点提高了scoped属性,防止组件之间样式冲突
加上了scoped,vue会自动为组件的dom结构以及样式分配一个唯一的自定义属性
2.3 /deep/样式穿透
如果给当前组件的style节点添加scoped属性 则当前组件的样式对其子组件是不生效的
如果想让某些样式对子组件生效,可以使用/deep/深度选择器
<style lang="less" scoped>
title{
color: blue; //没有加/deep/ 选择器格式为.title[data-v-052242de]
}
/deep/.title{
color: blue;// 生时的选择器分时为[data-v-052242de].title
}
</style>
注意:/deep/是vue2.x中实现样式穿透的方案 在vue3.x中推荐使用:deep()替代/deep/
3.组件的props
为了提高组件的复用性,在封装vue组件时需要遵守如下的原则
组件的DOM结构、Style样式要尽量复用
组件中要展示的数据,尽量由组件的使用者提供
为了方便使用者为组件提供要展示的数据,vue组件提供了props的概念
3.1 什么是组件的props
props是组件的自定义属性,组件的使用可以通过props把数据传递到子组件内部,供子组件内部进行使用
通过自定义props 把文章的标题和作者 传递到my-article组件中
<my-article title="asd" author="作者"></my-article>
props的作用:父组件通过props向子组件传递要展示的数据
props的好处:提高了组件的复用性
3.2 在组件中声明props
在封装vue组件时,可以把动态的数据项声明为props自定义属性。
自定义属性可以在当前组件的模板结构中被直接使用
3.3 无法使用未声明的props
如果父组件给子组件传递了未说明的props 则这些属性会被忽略,无法被子组件使用
<my-article title="朝大海" author="作者"></my-article>
<template>
<div>
<h3>标题:{{ title }}</h3>
<h5>作者:{{ author }}</h5>
</div>
</template>
<script>
export default {
name:'MyArticle',
// 外界可以传递指定的数据,到当前的组件中
props:['title']//'author'属性没有声明,因此子组件无法访问到author的值
}
3.4 动态绑定props的值
可以使用v-bind属性绑定的形式,为组件动态绑定props的值
<my-article :title="info.title" :author="'post.by'+info.author"></my-article>
通过v-bind属性绑定,为title动态绑定一个变量的值
通过v-bind属性绑定,为author动态绑定一个表达式的值
3.5 props的大小写命名
组件中使用"camelCaase(驼峰命名法)"声明了props属性的名称
<my-article pubTime="1234"></my-article> //驼峰命名
<my-article pub-Time="1234"></my-article> //短横线分隔
<template>
<div>
<h3>标题:{{ title }}</h3>
</div>
</template>
<script>
export default {
name:'MyArticle',
// 外界可以传递指定的数据,到当前的组件中
props:['title']
}
4.动态绑定HTML的class
<h3 class="thin" :class="isItalic ? 'italic' : ''">MyStyle 组件</h3>
<button @click="isItalic=!isItalic">Toggle Italic</button>
可以通过三元表达式,动态的为元素绑定class的类名
4.2 以数组语法绑定HTML的class
如果元素需要动态绑定多个class的类名,此时可以使用数组的语法格式
<h3 class="thin" :class="[isItalic ? 'italic' : '',isDelete ? 'delete' : '']">MyStyle 组件</h3>
<button @click="isItalic=!isItalic">Toggle Italic</button>
<button @click="isDelete=!isDelete">Toggle isDelete</button>
data(){
return{
// 字体是否倾斜
isItalic:false,
isDelete:false
}
}
4.3 以对象语法绑定HTML的class
使用数组语法动态绑定class会导致模板结构臃肿问题。
此时可以使用对象语法进行简化
<h3 class="thin" :class="classObj">MyStyle 组件</h3>
<button @click="classObj.italic =!classObj.italic">Toggle Italic</button>
<button @click="classObj.delete =!classObj.delete">Toggle isDelete</button>
data(){
return{
// 字体是否倾斜
isItalic:false,
isDelete:false,
classObj:{
italic:false,
delete:false
}
}
}
4.4 以对象语法绑定内联的style
:style的对象语法十分直观看着像css 但其实是js
CSS property名可以驼峰式或短横线分隔来命名
封装组件的案例
封装要求
允许用户自定义title题目
允许用户自定义bgcolor背景色
允许用户自定义color文本颜色
MyHeader组件需要在页面顶部进行fixed固定定位 且z-index等于999
props验证
1.什么是props验证
指的是:在封装组件时对外界传递过来的props数据进行合法性的校验,从而防止数据不合法的问题
使用数组类型的props节点的缺点:无法为每个prop指定具体的数据类型
2.对象类型的props节点
使用对象类型的props节点,可以对每个props进行数据类型的校验
props{
count:Number
}
3. props验证
对象类型的props节点提供了多种数据验证方案
基础的类型检查
多个可能的类型
必填项校验
属性默认值
自定义验证函数
3.1 基础的类型检查
可以直接为组件的prop属性指定基础的校验类型,从而防止组件的使用者为其绑定错误类型的数据
export default {
props:{
propA:Number,
propA:Boolean,
propC:Boolean,
propD:Array,
propE:Object
propF:Date 日期类型
propG:Function函数类型
propH:Symbol 符号类型
}
}
3.1 多个可能的类型
如果某个prop属性值的类型不唯一,此时可以通过数组的形式,为其指定多个可能的类型
export default{
props:{
//propA属性的值可以是字符串或数字
propA:[String,Number],
}
}
3.3 必填项校验
如果组件的某个prop属性是必填项,必须让组件的使用者为其传递属性的值
此时,可以通过如下的方式将其设置为必填项
export default{
props:{
//通过配置对象的形式,来定义propB属性的验证规则
propB:{
type:String,//属性的值必须是String字符串类型
required:true//属性的值是必填项,使用者没指定propB属性的值 则在终端进行警告提示
},
}
}
3.4 属性默认值
在封装组件时,可以为某个prop属性指定默认值。
export default{
props:{
//通过配置对象的形式,来定义propC属性的验证规则
propC:{
type:Number,//属性的值必须是String字符串类型
default:100//如果使用者没指定propC的值,则propC属性的默认值为100
},
}
}
3.5 自定义验证函数
在封装组件时,可以为prop属性指定自定义的验证函数,从而对prop属性的值进行更加精确的控制
<my-count type="success"></my-count>
export default {
props:{
count:{
tyep:Number,
},
type:{
validator(value){
return ['success','warning','danger'].indexOf(value) !== -1
}
}
}
}
计算属性
1.什么是计算属性
计算属性本质上就是一个function函数,
它可以实时监听data中数据的变化,并return一个计算后的新值
供组件渲染DOM时使用
2.如何声明计算属性
计算属性需要以function函数的形式声明到组件的computed选项中
<input type="text" v-model.number="count"/>
<p>{{ count }}乘以2的值为:{{ plus }}</p>
data(){
return { count:1 }
}
computed:{
plus(){
return this.count * 2
}
}
注意:计算属性侧重于得到一个计算的结果,因此计算属性中必须有return返回值
3.计算属性的使用注意点
计算属性必须定义在computed节点
计算属性必须是一个function函数
计算属性必须有返回值
计算属性必须当做普通属性使用
4.计算属性vs方法
相对于方法来说,计算属性会缓存计算的结果,只有计算属性的依赖项发生变化时,才会重新进行运算
因此计算属性的性能更好
computed:{
plus(){
console.log("执行");
return this.count * 2
},
methods:{
plus(){
console.log("执行了");
return this.count * 2
}
}
自定义事件
1.什么是自定义事件
在封装组件时,为了让组件的使用者可以监听到组件内状态的变化,此时需要用到组件的自定义事件
2.自定义事件的3个使用步骤
在封装组件时
声明自定义事件
触发自定义事件
在使用组件时
监听自定义事件
2.1声明自定义事件
开发者为自定义组件封装的自定义事件,必须事先在emits节点中声明
<template>
<h3>Counter组件</h3>
<button>+1</button>
</template>
<script>
export default {
//my-counter组件的自定义事件,必须事先声明到emits节点中
emits:['change']
}
</script>
2.2触发自定义事件
在emits节点下声明的自定义事件,可以通过this.$emit('自定义事件')方法进行触发
<template>
<h3>Counter组件</h3>
<button @click="onBtnClick">+1</button>
</template>
<script>
export default {
//my-counter组件的自定义事件,必须事先声明到emits节点中
emits:['change']
methods:{
onBtnClick(){
this.$emit('change')
//当点击+1按钮时,调用this.$emit()方法 触发自定义的change事件
},
}
}
</script>
2.3监听自定义事件
在使用自定义的组件时,可以通过v-on的形式监听自定义事件
<使用 v-on指令绑定事件监听>
<my-couter @change="getCount"></my-counter>
methods:{
getCount(){
console.log('监听到了count值的变化')
}
}
2.4总结
在封装组件时
声明自定义事件 emits[?]
触发自定义事件 this.$emit[?]
在使用组件时
监听自定义事件 @?="函数触发"
3.自定义事件传参
在调用this.$emit()方法触发自定义事件时,可以通过第2个参数为自定义事件传参
<template>
<div>
<p>count的值是:{{ count }}</p>
<button @click="add">+1</button>
</div>
</template>
<script>
export default {
emits:['countChange'],
methods:{
add(){
this.$emit('countChange',this.count)
}
}
}
</script>
组件上的v-model
1.为什么需要在组件上使用v-model
v-model是双向数据绑定指令,当需要维护组件内外数据的同步时
可以在组件上使用v-model指令。
外界数据的变化会自动同步到子组件中
2.在组件上使用v-model的步骤
父组件通过v-bind:属性绑定的形式,把数据传递给子组件
子组件,通过props接收父组件传递过来的数据
2.在组件上使用v-model的步骤
在v-bind:指令之前添加v-model指令
在子组件中声明emits自动事件 格式为update:xxx
调用$emit()触发自定义事情,更新父组件中的数据
组件高级(上)
能够掌握watch侦听器的基本使用
能够知道vue中常用的生命周期函数
能够知道如何实现组件之间的数据共享
能够知道如何在vue3.x的项目中全局配置axios
watch侦听器
1.什么是watch侦听器
watch侦听器允许开发者监视数据的变化,从而针对数据的变化做特定的操作。
监视用户名的变化并发起请求,判断用户名是否可用
2.watch侦听器的基本语法
开发者需要在watch节点下,定义自己的侦听器
export default {
data(){
return {username:''}
},
watch:{
//监听username的值的变化
//形参列表中,第一个值是变化后的新值 第二个值是变化之前的旧值
username(newVal,oldVal){
console.log(newVal,oldVal)
},
}
}
3.使用watch检查用户名是否可用
监听username值的变化,并使用axios发起Ajax请求 检查当前输入的用户名是否可用
<script>
import axios from "axios"
export default {
name:'MyWatch',
data(){
return{
username:''
}
},
watch:{
// 监听username的值的变化
// 形参列表 第一个值是变化后的新值 第二给值是变化前的新值
username(newVal,oldVal){
console.log(newVal,oldVal);
const {data: res} = axios.get('https://www.escook.cn/api/finduser/'+newVal)
console.log(res);
}
},
}
4.immdiate选项
默认情况下 组件在初次加载完毕后不会调用watch侦听器,如果想让watch侦听器立即被调用
则需要使用immediate选项
watch:{
//1 监听username值的变化
username:{
//2 handler属性是固定写法 当username变化是 调用handler
async handler(newVal,oldVal){
const {data: res} = axios.get('https://www.escook.cn/api/finduser/'+newVal)
console.log(res);
},
//3 表示组件加载完毕后立即调用一次当前的watch侦听器
immediate:true
}
},
5.deep
当watch侦听的是一个对象,如果对象中的属性发生了变化,则无法被监听到。此时需要deep选项
export default {
name:'MyWatch',
data(){
return{
username:'admin',
info:{
username:'zs'
}
}
},
watch:{
// 监听username的值的变化
// 形参列表 第一个值是变化后的新值 第二给值是变化前的新值
info:{
async handler(newVal){
const {data: res} = axios.get('https://www.escook.cn/api/finduser/'+newVal.username)
console.log(res);
},
deep:true//需要使用deep选项 否则username值的变化无法被监听到
},
}
6.监听对象单个属性的变化
如果只想监听对象中单个属性的变化
7.计算属性vs 侦听器
计算属性和侦听器侧重的应用场景不同:
计算属性侧重于监听多个值的变化,最终计算并返回一个新值
侦听器侧重于监听单个数据的变化,最终执行特定的业务处理,不需要有任何返回值
组件的生命周期
1.组件运行的过程
开始 -> import 导入组件 -> components注册组件 ->以标签的形式使用组件
结束 <- 组件切换时销毁需要被隐藏的组件 <- 把创建的组件实例渲染到页面上 <-在内存中创建组件的实例对象
组件的生命周期指的是:组件从创建->运行(渲染)->销毁的整个过程,强调一个时间段
2如何监听组件的不同时刻
vue框架为组件内置了不同时刻的生命周期函数,生命周期函数会伴随着组件的运行而自动调用
当组件在内存中被创建完毕之后,会自动调用created函数
当组件被成功的渲染到页面上之后,会自动调用mounted函数
当组件被销毁完毕之后之后,会自动调用unmounted函数
3如何监听组件的更新
当组件的data数据更新之后,vue会自动重新渲染组件的DOM结构,
从而保证View视图展示的数据和Model数据源保持一致
当组件被重新渲染完毕之后,会自动调用updated函数
4.组件中主要的生命周期函数
生命周期函数 执行时机 所属阶段 执行次数 应用场景
created 组件在内存中创建完毕 创建阶段 唯一1次 发ajax请求数据
mounted 组件初次在页面中渲染完毕后 创建阶段 唯一1次 操作DOM元素
updated 组件在页面中被重新渲染完毕后 运行阶段 0或多次 -
unmounted 组件被销毁后 销毁 唯一1次 -
注意:在实际开发中,created是最常用的生命周期函数
5.组件中全部的生命周期函数
生命周期函数 执行时机 所属阶段 执行次数 应用场景
beforecreated 在内存中开始创建组件之前 创建阶段 唯一1次 -
created 组件在内存中创建完毕 创建阶段 唯一1次 发ajax请求数据
beforeMount 在把组件初次渲染到页面之前 创建阶段 唯一1次
mounted 组件初次在页面中渲染完毕后 创建阶段 唯一1次 操作DOM元素
beforeupdated 在组件被重新渲染之前 运行阶段 0或多次 -
updated 组件在页面中被重新渲染完毕后 运行阶段 0或多次 -
beforeunmounted 在组件被销毁之后 销毁阶段 唯一1次 -
unmounted 组件被销毁后 销毁阶段 唯一1次 -
疑问:为什么不在beforecreated中发ajax请求初始数据
6.完整的生命周期图示 进一步理解组件生命周期执行的过程
组件之间的数据共享
1.组件之间的关系
在项目开发中,组件之间的关系分为如下3种
父子关系
兄弟关系
后代关系
2.父子组件之间的数据共享
父子组件之间的数据共享又分为:
父 -> 子共享数据
子 -> 父共享数据
父 <-> 子双向数据同步
2.1 父组件向子组件共享数据
父组件通过v-bind属性绑定向子组件共享数据 同时子组件需要使用porps接收数据
父组件:
<template>
<div>
<h1>App根组件 ---{{count}}</h1>
<button type="button" class="btn btn-primary" @click="count+=1">+1</button>
<hr>
<my-son :num="count"></my-son>
</div>
</template>
<script>
// 导入子组件
import MySon from './Son.vue';
export default {
name:'MyApp',
data(){
return{
count:0,
}
},
// 注册子组件
components:{
MySon,
},
}
子组件
<template>
<div>
<h3>Son 子组件 ---{{num}}</h3>
</div>
</template>
<script>
export default {
name:'MySon',
props:['num']
}
</script>
2.2 子组件向父组件共享数据
子组件通过自定义事件的方式向父组件共享数据
子组件
export default {
name:'MySon',
props:['num'],
// 声明自定义事件
emits:['numchange'],
methods:{
add(){
// 触发自定义事件
this.$emits('numchange',this.num +1)
}
}
}
父组件
<my-son :num="count" @numchange="getNum"></my-son>
export default {
name:'MyApp',
data(){
return{
count:0,
}
},
methods:{
getNum(num){
this.count=num
}
},
// 注册子组件
components:{
MySon,
},
}
2.3 父子组件之间数据的双向同步
父组件在使用子组件期间,可以使用v-model指令维护组件内外数据的双向同步
import MySon from './Son.vue';
父组件
<my-son v-model:num="count" ></my-son>
export default {
name:'MyApp',
data(){
return{
count:0,
}
},
// 注册子组件
components:{
MySon,
},
}
子组件
export default {
name:'MySon',
props:['num'],
// 声明自定义事件
emits:['update:num'],
methods:{
add(){
// 触发自定义事件
this.$emit('update:num',this.num+1)
}
}
}
3.兄弟组件之间的数据共享
兄弟组件之间实现数据共享的方案是EventBus。
借助于第三方的包mitt来创建eventBus对象
从而实现兄弟组件之间的数据共享
3.1 安装mitt依赖包
npm install mitt@2.1.0
3.2 创建公共的EventBus模块
在项目中创建公共的eventBus模块
import mitt from './eventBus.js'
const bus = mitt()
export default bus
3.3在数据接收方自定义事件
在数据接收方,调用bus.on('事件名称',事件处理函数)方法注册一个自定义事件。
import bus from './eventBus.js'
export default {
name:'MyRight',
data(){
return {
num:0,
}
},
// 当组件在内存中创建完成 立即通过bus声明事件
created(){
bus.on('countChange',(count)=>{
this.num = count
// 外面的count值转成到data去
})
}
}
3.4在数据接发送方触发事件
数据发送方,调用bis.emit('事件名称',要发生的数据)方法触发自定义事件
import bus from './eventBus.js'
<button type="button" class="btn btn-primary" @click="add">+1</button>
export default {
name:'MyLeft',
data(){
return {
count:0,
}
},
// 当组件在内存中创建完成 立即通过bus声明事件
methods:{
add(){
this.count++
bus.emit('countChange',this.count)
}
}
}
4.后代关系组件之间的数据共享
后代关系组件之间共享数据,指的是父节点的组件向其子孙组件共享数据。
此时嵌套共享比较复杂,可以使用provide(发送向外)和inject(接收向内)实现后代关系组件之间的数据共享
4.1 父节点通过provide 共享数据
父节点的组件可以通过provide方法 对其子孙组件共享数据
export default {
name:'MyLeft',
data(){
return {
color:'red',
}
},
// 当组件在内存中创建完成 立即通过bus声明事件
provide(){//provide函数 return的对象包含了要向对象子孙组件共享数据
return{
color:this.color
}
}
}
4.3 子孙节点通过inject接收数据
<template>
<div>
<h3>数据发送方 --- count的值为{{ color }}</h3>
</div>
</template>
<script>
import bus from './eventBus.js'
export default {
//子孙组件,使用inject接收父节点向下共享的color数据,并在页面上使用
inject:['color']
}
</script>
4.3 父节点对外共享响应式的数据
父节点使用provide向下共享数据时,可以结合computed函数向下共享响应式的数据
import {computed} from 'vue' //从vue中按需导入computed函数
export default {
name:'MyApp',
data(){
return{
color:'red',
}
},
provide(){
// 返回要共享的数据对象
return{
color:computed(()=>this.color)
}
},
}
4.4 子孙节点使用响应式的数据
如果父级节点共享的是响应式的数据,则子孙节点必须以.value的形式进行使用
<template>
<div>
<h5>Level Three 三级组件{{ color.value }}</h5>
</div>
</template>
<script>
export default {
name:'LevelThree',
inject:['color']
}
</script>
5.vuex
vuex是终极的组件之间的数据共享方案。
在企业级的vue项目开发中,vuex可以让组件之间的数据共享变得高效 清晰 且易于维护
6.总结
父子关系
父 -> 子 属性绑定
子 -> 父 事件绑定
父 <-> 子 组件上的v-model
兄弟关系
EventBus
后代关系
provide & inject
全局数据共享
vuex
vue3.x中全局配置axios
1.为什么要全局配置axios
在实际项目开发中,几乎每个组件中都会用到axios发起数据请求
每个组件中都需要导入axios(代码臃肿)
每次发请求都需要填写完整的请求路径(不利于后期的维护)
2如何全局配置axios
在main.js入口文件中,通过app.config.globalProperties(固定写法)全局挂载axios
例如
// 挂载请求的根路径
axios.defaults.baseURL='https://www.escook.cn'
// 全局挂载自定义属性
app.config.globalProperties.$http = axios
组件高级(下)
能够知道如何使用ref引用DOM和组件实例
能够做到$nextTick的调用时机
能够说出keep-alive元素的左右
能够掌握插槽的基本用法
能够做到如何自定义指令
ref引用
组件高级(下)
ref用来辅助开发者在不依赖jQuery的情况下,获取DOM元素或组件的引用
每个vue的组件实例上 都包含一个$refs对象,里面存储着对于的DOM元素或组件的引用
组件的$refs指向一个空对象
<template>
<h3>MyRef组件</h3>
<buttton @click="getRef">获取 $refs 引用 </button>
</template>
<script>
export default{
methods:{
GetRef(){ console.log(this) }//this代表当前组件的实例对象 this$refs 默认执行空对象
}
}
</script>
2.使用ref引用DOM元素
如果想要使用ref引用页面上的DOM元素,则可以按照如下的方式进行操作
<h3 ref="myh3">MyRef 组件</h3>
<button @click="getRef">获取$refs引用</button>
methods:{
getRef(){
//通过this.$refs.引用的名称 可以获取到DOM元素的引用
console.log(this.$refs.myh3)
操作DOM元素 把文本颜色改为红色
this.$refs.myh3.style.color = 'red'
},
}
3.使用ref引用组件实例
如果需要使用ref引用页面山的组件实例 则可以按照如下的方式进行操作
<my-style ref="counterRef"></my-style>
<button type="button" class="btn btn-primary" @click="getInfo">发起GET 请求</button>
methods:{
getInfo(){
console.log(this.$refs.counterRef)
this.$refs.counterRef.add()
}
}
4.控制文本框和按钮的按需切换
通过布尔值inputVisible来控制组件中的文本框与按钮的按需切换
<template>
<input type="text" v-if="inputVisible">
<button v-else @click="showInput">展示输入框</button>
</template>
<script>
export default {
name:'MyApp',
data(){
return{
inputVisible:false
}
},
methods:{
showInput(){
this.inputVisible = true
}
}
}
</script>
5.让文本框自动获得焦点
当文本框展示出来之后,如果希望它立即获得焦点,则可以为其添加ref引用
并调用原生DOM对象的.focus()方法即可
export default {
name:'MyApp',
data(){
return{
inputVisible:false
}
},
methods:{
showInput(){
// 展示文本框
this.inputVisible = true
this.$refs.ipt.focus()
}
}
}
6.this.$nextTick(cb)方法
组件的this.$nextTick(cb)方法,会把cb回调推迟到下一个DOM更新周期之后执行
通俗:等组件的DOM异步重新渲染完成后,再执行cb回调函数。从而保证cb回调函数可以操作到最新DOm原生
动态组件
1.什么是动态组件
动态组件指的是动态切换组件的显示与隐藏,vue提供了一个内置的<component>组件
专门用来实现组件的动态渲染
<component>是组件的占位符
通过is属性动态指定要渲染的组件名称
<component is="要渲染的组件的名称"></component>
2.如何实现动态组件渲染
<template>
<div>
<div class="mb-4">App根组件</div>
//点击按钮,动态切换组件的名称
<button type="button" class="btn btn-primary" @click="comName='MyHome'">首页</button>
<button type="button" class="btn btn-info ml-2" click="comName='MyMovie'">首页</button>
<hr>
//is属性 动态指定要渲染的组件的名称
<component :is="comName"></component>
</div>
</template>
data(){
return{
comName:'MyHome' //要渲染的组件
}
},
3. 使用keep-alive保持状态
默认情况下,切换动态组件时无法保持组件的状态,此时可以使用vue内置的<keep-alive>组件保持动态组件的状态
<keep-alive>
<component :is="comName"></component>
</keep-alive>
插槽
1.什么是插槽
插槽是vue为组件的封装者提供的能力。
允许开发者在封装组件,把不确定的、希望由用户指定的部分定义为插槽
可以把插槽认为是组件封装期间,为用户预留内容的占位符
2.体验插槽的基础用法
在封装组件时,可以通过<slot>元素定义插槽,从而为用户预留内容占位符
<template>
<div class="com-container">
<h3>MyCom组件 -- 插槽基本的用法</h3>
<hr>
<!-- 使用组件 -->
<p>这是第一个</p>
<slot></slot>
<p>最后</p>
</div>
</template>
<template>
<div>
<h1>App根组件</h1>
<hr>
<!-- 使用组件 -->
<my-com>
<p>---</p>
</my-com>
</div>
</template>
2.1没有预留插槽的内容会被丢弃
<template>
<div class="com-container">
<h3>MyCom组件 -- 插槽基本的用法</h3>
<hr>
<!-- 使用组件 -->
<p>这是第一个</p>
//就是一定要定义slot
<p>最后</p>
</div>
</template>
<template>
<div>
<h1>App根组件</h1>
<hr>
<!-- 使用组件 -->
<my-com>
<p>---</p>
</my-com>
</div>
</template>
2.2后备内容
封装组件时,可以为预留的<slot>插槽提供后备内容(默认内容)。如果组件的使用者没有为插槽提供任何内容
则后背内容会生效
<template>
<p>这是第一个</p>
<slot>后备内容</slot>
<p>最后</p>
</template>
3.具名插槽
如果在封装组件时需要预留多个插槽节点,则需要为每个<slot>插槽指定具体的name名称
这种带有具体名称的插槽叫做具名插槽
<template v-slot:header>
<h1>asd</h1>
</template>
<p>adsfadsfas</p>
<p>adsffas</p>
<p>addsfas</p>
<template v-slot:fotter>
<p>afas</p>
</template>
注意:没有指定name名称的插槽会有隐含的名称叫做default
3.2具名插槽的简写形式
跟v-on和v-bind一样,v-slot也有缩写
即把参数之前的所有内容(v-slot:)替换为字体#
<template #header>
4.作用域插槽
在封装组件的过程中,可以为预留的<slot>插槽绑定props数据
这种带有props数据的<slot>叫做作用域插槽
<div>
<h3>这是TEST 组件</h3>
<slot :info="infomation"></slot>
</div>
<my-test>
<template #defualt="scoped">
<p>{{ scoped }}</p>
</template>
</my-test>
4.1 结构作用域插槽的Prop
<template #defualt="{scoped}">
<p>{{ scoped }}</p>
</template>
</my-test>
<slot :info="infomation"></slot>
infomation:{
phone:'1232312312',
address:'八十多'
}
自定义指令
1.什么是自定义指令
vue官方提供了v-for v-model v-if等常用的内置指令
除此之外vue还允许开发中自定义指令
vue中的自定义指令分两类,分别是:
私有自定义指令
全局自定义指令
2.声明私有自定义指令的语法
在每个vue组件中 可以在directives节点下声明私有自定义指令
directives:{
私有自定义指令
focus:{
//当被绑定的元素插入到DOM中时,自动触发mounte()函数
//el就是当前指令所绑定到的那个DOM元素
mounted(el){
el.focus()//让被绑定的元素自动获取焦点
}
}
}
3.声明全局自定义指令的语法
const app =createApp(App)
app.directive('focus',{
//当被绑定的元素插入到DOM 自动触发mounted函数
mounted(el){
//Focus the element
el.focus()
}
})
4. updated函数
vue2里是update,vue3里是updated
mounted函数只在元素第一次插入DOM时被调用,当DOM更新时mounted函数不会被触发
updated函数会在每次DOM更新完成后被调用。
app.directive('focus',{
mounted(el){ //第一次插入DOM时触发这个函数
el.focus()
},
updated(el){ //每次DOM更新时都会触发updated函数
el.focus()
}
})
注意:在vue2的项目中使用自定义指令时,[mounted -> bind] [updated -> update]
5.函数简写
如果mounted和updated函数中的逻辑完全相同,则可以简写
app.directive('focus',(el)=>{
el.focus()
})
6.指令的参数值
在绑定指令时,可以通过等号的形式为指令绑定具体的参数值
<h3 v-color="red">MyHome组件---{{ count }}</h3>
自定义v-color指令
app.directive('color',(el,binding)=>{
//binding.value就是通过等号为指令绑定的值
el.style.color=binding.value
})
路由
能够说出前端路由工作的过程
能够知道如何在vue3中配置路由
能够知道如何使用嵌套路由
能够知道如何实现动态路由匹配
能够知道如何使用编程式导航
能够知道如何使用导航守卫
前端路由的概念与原理
1.什么是路由
路由就是对应关系。路由分为两大类
后端路由
前端路由
2.后端路由
后端路由指的是:请求方式 请求地址与function处理函数之间的对应关系
在node.js中 express路由的基本用法
3.SPA与前端路由
SPA指的是一个web网站只有唯一的一个html页面 所有组件的展示与切换都在这唯一的一个页面内完成
此时,不同组件之间的切换需要通过前端路由来实现
结论:在SPA项目中 不同功能之间的切换 要依赖于前端路由来完成
4.什么是前端路由
概念:Hash地址与组件之间的对应关系
5.前端路由的工作方式
用户点击了页面上的路由链接
导致了URL地址中的Hash的值发送了变化
前端路由监听了到Hash地址的变化
前端路由把当前Hash地址对应的组件渲染在浏览器中
结论:前端路由Hash地址与组件之间的对应关系
vue-router的基本用法
vue-router是vue.js官方给出的路由解决方案。
它只能结合vuw项目进行使用,能够使用的管理SPA项目中组件的切换
2.vue-router的版本
vue-router目前有3.x的版本和4.x的版本
vue-router 3.x只能结合vue2进行使用
vue-router 4.x只能结合vue3进行使用
vue-router 3.x https://router.vuejs.org/zh/
vue-router 4.x https://next.router.vuejs.org/
3.vue-router 4.x的基本使用步骤
在项目中安装vue-router
定义路由组件
声明路由链接和占位符
创建路由模块
导入并挂载路由模块
3.1 在项目中安装vue-router
在vue3的项目中,只能安装并使用1vue-router 4.x 安装的命令如下
npm install vue-router@next -S
3.2 定义路由组件
在项目中定义MyAbout,MyHome,MyMovie,将来要使用vue-router来控制它们的展示与切换
3.3声明路由链接和占位符
可以使用<router-link>标签来声明路由链接,并使用<router-view>标签来声明路由占位符
3.4 创建路由模块
在项目router.js路由模块 在其中按照如下4个步骤创建并得到路由的实例对象
从vue-router中按需导入两个方法
createRouter方法用于创建路由的实例对象
createWebHashHistory用于指定路由的工作模式
import {createRouter,createWebHashHistory} from 'vue-router'
创建路由实例对象
import {createRouter,createWebHashHistory} from 'vue-router'
import About from './MyAbout.vue'
import Home from'./MyHome.vue'
import Movie from './MyMovie.vue'
const router = createRouter({
history:createWebHashHistory(),
routes:[
{path:'/home',component:Home},
{path:'/movie',component:Movie},
{path:'/about',component:About}
]
})
export default router
vur-router的高级用法
1.路由重定向
路由重定向指的是:用户在访问A的时候,强制用户跳转到C 从而展示特定的组件页面
通过路由规则的redirect属性,指定一个新的路由地址 可以很方便地设置路由的重定向
const router = createRouter({
history:createWebHashHistory(),
routes:[
// 其中,path表示需要被重定向的原地址 redirect表示将要被重定向到的新地址
{path:'/',redirect:'/home'}.
{path:'/home',component:Home},
{path:'/movie',component:Movie},
{path:'/about',component:About}
]
})
2.路由高亮
可以通过如下的两种方式,讲激活的路由链接进行高亮显示
使用默认的高亮class类
自定义路由高亮的class类
2.1 默认的高亮class类
被激活的路由链接,默认会应用一个叫做router-link-active的类名
开发中使用此类目选择器 为激活的路由链接设置高亮的样式
.router-link-active{
background-color: red;
color: white;
font-weight: bold;
}
2.2 自定义路由高亮的class类
在创建路由的实例对象时,开发中可以基于linkActiveClass属性,自定义路由链接被激活时所应用的类名
const router = createRouter({
history:createWebHashHistory(),
linkActiveClass:'active-router',
routes:[
// 其中,path表示需要被重定向的原地址 redirect表示将要被重定向到的新地址
{path:'/',redirect:'/home'},
{path:'/home',component:Home},
{path:'/movie',component:Movie},
{path:'/about',component:About}
]
})
.active-router{
background-color: blue;
color: white;
font-weight: bold;
}
3.嵌套路由
通过路由实现路由的嵌套展示,叫做嵌套路由
1 声明子路由链接和子路由占位符
2 父路由规则中,通过children属性嵌套声明子路由规则
3.1 声明子路由链接和子路由占位符
<template>
<div>
<h3>MyAbout组件</h3>
<hr>
<!-- 声明子路由链接 -->
<router-link to="/about/tab1">Tab1</router-link>
<router-link to="/about/tab2">Tab2</router-link>
<!-- 子路由占位符 -->
<router-view></router-view>
</div>
</template>
3.2 通过children属性声明子路由规则
在router.js路由模块中,导入需要的组件,并使用children属性声明子路由规则
const router = createRouter({
history:createWebHashHistory(),
linkActiveClass:'active-router',
routes:[
// 其中,path表示需要被重定向的原地址 redirect表示将要被重定向到的新地址
{path:'/',redirect:'/home'},
{path:'/home',component:Home},
{path:'/movie',component:Movie},
{path:'/about',component:About,
// 通过children属性嵌套声明子级路由规则
children:[
{path:'tab1',component:'Tab1'},
{path:'tab2',component:'Tab2'},
]}
]
})
4. 动态路由匹配
<router-link to="/movie1">电影1</router-link>
<router-link to="/movie2">电影2</router-link>
<router-link to="/movie3">电影3</router-link>
定义如下3个路由规则,是否可行
{path:'/movie1',component:Movie},
{path:'/movie2',component:Movie},
{path:'/movie3',component:Movie},
缺点:路由规则的复用性差
4.1 动态路由的概念
动态路由指的是:把Hash地址中可变的部分定义为参数项,从而提高路由规则的复用性
在vue-router中使用英文的冒号(:)来定义路由的参数项
// 路由中的动态参数以:进行声明.冒号后面的时动态参数的名称
{path:'./movie/:id',component:Movie}
//将以下3个路由规则,合并成了一个,提高了路由规则的复用性
{path:'/movie1',component:Movie},
{path:'/movie2',component:Movie},
{path:'/movie3',component:Movie},
4.2 $route.params参数对象
通过动态路由匹配的方式渲染出来的组件中,可以使用$route.params对象访问动态匹配的参数值
<template>
<div>
<h3>MyMovie组件---{{$route.params.id}}</h3>
</div>
</template>
<script>
export default {
name:'MyMovie'
}
</script>
4.3 使用props接收路由参数
为了简化路由参数的获取形式,vue-router允许在路由规则中开启props传参
{path:'/movie/:id',component:Movie,props:true},
<template>
<div>
<h3>MyMovie组件---{{id}}</h3>
</div>
</template>
<script>
export default {
name:'MyMovie',
props:['id']
}
</script>
5.编程式导航
通过调用API编程式导航的方式,叫做编程式导航。与之对应,
通过点击链接实现导航的方式,叫做声明式导航
普通网页中点击<a>链接,vue项目中点击<router-link>都属于声明式导航
普通网页中调用location.href跳转到新页面的方式,属于编程式导航
5.1 vue-router中的编程式导航API
vue-router提供了许多编程式导航的API,其中最常用的两个API分别是
this.$router.push('hash地址')
跳转到指定Hash地址,从而展示对应的组件
this.$router.go(数值n)
实现导航历史的前进、后退
5.2 $router.push
调用$router.push()方法 可以跳转到指定的hash地址,从而展示对应的组件页面
<template>
<div>
<h3>MyMovie组件---{{id}}</h3>
<button type="button" @click=goToMovie(3)>导航到Movie页面</button>
</div>
</template>
<script>
export default {
name:'MyMovie',
methods:{
goToMovie(id){
this.$router.push('/movie/' + id)
}
}
}
</script>
5.2 $router.go
调用this.$router.go()方法 可以在浏览历史中进行前进和后退。
6. 命名路由
通过name属性为路由规则定义名称的方式,叫做命名路由。
{path:'/movie/:id',
component:Movie,
name:'mov',
props:true},
注意:命名路由的name值不能重复,必须保持唯一性
6.1 使用命名路由实现声明式导航
为<router-link>标签动态绑定to属性的值,并通过name属性指定要跳转到的路由规则
期间还可以用params属性指定跳转期间要携带的路由参数。
<router-link :to="{ name:'mov',params:{id:3} }">go to</router-link>
6.2 使用命名路由实现编程式导航
调用push函数期间指定一个对象,name是要跳转到的路由规则,params是携带的路由参数
<button type="button" class="btn btn-primary" @click="goToMovie">go to movie</button>
methods:{
goTOMovie(id){
this.$router.push({
name:'mov',
params:{
id:id,
}
})
}
}
7 导航守卫
导航守卫可以控制路由的访问权限
7.1如何声明全局导航守卫
全局导航守卫会拦截每个路由规则,从而对每个路由进行访问控制的权限,
const router = createRouter({...})
调用路由实例对象beforeEach函数 称全局导航守卫
fn必须是一个函数,每次拦截路由的请求,都会调用fn进行处理
因此fn叫做守卫方法
router.beforeEach(()=>{
console.log('ok');
})
7.2 守卫方法的3个形参
全局导航守卫的守卫方法中接收到3个形参
const router = createRouter({...})
调用路由实例对象beforeEach函数 称全局导航守卫
fn必须是一个函数,每次拦截路由的请求,都会调用fn进行处理
因此fn叫做守卫方法
router.beforeEach((to,from,next)=>{
//to 目标路由对象
//from 当前导航正要离开的路由对象
//next 是一个函数,表示放行
})
注意:
1 在守卫方法中如果不声明next形参,则默认允许用户访问每一个路由
2 在守卫方法中如果声明了next形参,则必须调用next()函数 否允许用户访问任何一个路由
7.3 next函数的3种调用方式
直接放行:next()
强制其停留在当前页面:next(false)
强制其跳转到指定页面:next('/login')
7.4 结合token控制后台主页的访问权限
router.beforeEach((to,from,next)=>{
const tokenStr = localStorage.getItem('token') 1.读取token
if(to.path === '/main' && !tokenStr){ 2. 访问后台主页且token
//next(false) //3.1不允许跳转
next('/login') //3.2强制跳转到登录页面
}else{
next() // 3.3 直接放行 允许访问后台主页
}
})
vue的综合案例
能够知道如何使用vue-cli创建vue项目
能够知道如何在项目中安装与配置element-ui
能够知道element-ui中常见组件的用法
能够知道如何知道axios中的拦截器
能够知道如何配置proxy接口代理
vue-cli
1.什么是vue-cli
vue-cli(vue脚手架)是vue官方提供的,快速生成vue工程化项目的工具
特点:
开箱即用
基于webpack
功能丰富且易于扩展
支持创建vue2和vue3的项目
vue-cli的中文官网首页:https://cli.vuejs.org/zh/
2.安装vue-cli
vue-cli是基于Node.js开发出来的工具,因此需要使用npm将它安装为全局可用的工具:
全局安装 vue-cli
npm install -g@vue/cli
查看vue-cli的版本
vue --version
2.1 解决Windows PowerShell不识别vue的问题
默认情况下 在PowerShell中执行vue--version命令会提示如下的错误信息
解决方案
以管理员身份运行PowerShell
执行 set-ExecutionPolicy RemoteSigned命令
输入字符Y 回车即可
3.创建项目
vue-cli提供了创建项目的两种方式:
基于命令行的方式创建vue项目
vue create 项目名称
基于可视化面板创建vue项目
vue ui
4.基于vue ui创建vue项目
1 在终端下运行vue ui命令, 自动在浏览器中打开创建项目的可视化面板
2 在详情页面填写项目名称
3 在预设页面选择手动配置项目:
4 在功能页面勾选需要安装的功能(less配置文件 CSS预处理器)
5 在配置页面勾选vue的版本和需要的预处理器
6 将刚才所有的配置保持为预设(模板)方便下一次创建项目时直接复用之前的配置
7 创建项目并自动安装依赖包:
vue ui的本质: 通过可视化的面板采集到用户的配置信息后,在后台基于命令行的方式自动初始化项目:
4.基于vue ui创建vue项目
项目创建完成后,自动进入项目仪表盘:
5.基于命令行创建vue项目
1 在终端下运行vue create deomo2命令 基于交互式的命令行创建vue的项目
6在vue2的项目中使用路由
vue2的项目中,只能安装并使用3.x版本的vue-router
版本3和版本4的路由最主要的区别:创建路由模块的方式不同
组件库
1.什么是vue组件库
在实际开发中 前端开发者可以把自己封装的.vue组件整理、打包并发布为npm的包,从而供其他人下载
和使用。这种可以直接下载并在项目中使用的现成组件,就叫做vue组件库
2.vue组件库和bootstrap的区别
二者之间存在本质的区别
bootstrap只提供了纯粹的原材料(css样式 HTML结构以及JS特效),需要由开发者做进一步的组装和改造
vue组件库是遵循vue语法、高度定制的现成组件,开箱即用
3.最常用的vue组件库
PC端
Element UI
View UI
移动端
Mint UI
Vant
4.Element UI
Element UI是饿了么前端团队开源的一套PC端vue组件库。支持vue2和vue3的使用
vue2的项目使用旧版的Element UI
vue3的项目使用新版的Element Plus
4.1在vue2的项目中安装element-ui
npm i element-ui -S
4.2 引入Element UI
开发中可以一次性完整引入所有的Element UI组件,或者是根据需求,只按需引入用到Element UI组件
完整引入:操作简单 但是会额外引入一些用不到的组件,导致项目体积过大
按需引入:操作相对复杂一些,但是只会引入用到的组件,能起到优化项目体积的目的
4.3 完整引入
在main.js中写入以下内容
import Vue from 'vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import App from './App.vue'
Vue.use(ElementUI)
new Vue({
el: '#app',
render: h => h(App)
})
4.4按需导入
借助babel-plugin-component,可以只引入需要的组件,以达到减小项目体积的目的
安装babel-plugin-component
npm install babel-plugin-component -D
修改根目录下的babel.config.js配置文件 新增plugins节点
{
"plugins": [["component", [
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]]]
}
如果只希望引入部分组件,比如Button 那么需要在main.js以下内容
4.5 把组件的导入和注册封装为独立的模块
在src目录下新建elemen-ui/index.js模块,并声明如下的代码
/src/element-ui/index.js
import Vue from 'vue'
//按需导入 element ui的组件
import {Button,Input} from 'element-ui'
注册需要的组件
Vue.use(Button)
Vue.use(Input)
//在main.js中导入
import './element-ui'
axios拦截器
1.回顾:在vue3的项目中全局配置axios
import axios from ‘./axios’
//配置请求根路径
axios.defaults.baseURL = 'https://www.escook.cn'
//全局配置axios
app.config.globalProperties.$http = axios
2.在vue2的项目中全局配置axios
需要在main.js入口文件中,通过Vue构造函数的prototype原型对象全局配置axios
import axios from ‘./axios’
//配置请求根路径
axios.defaults.baseURL = 'https://www.escook.cn'
//通过vue构造函数的原型对象
Vue.prototype.$http = axios
3.什么是拦截器
拦截器会在每次发起ajax请求和得到响应的时候会自动被触发
应用场景
ToKen身份认证
Loading效果
etc
4配置请求拦截器
通过axios.interceptors.request.use(成功回调,失败回调)可以配置请求拦截器。
axios.interceptors.request.use(function(config){
//return config;
},function(error){
return Promise.reject(error);
}
})
注意失败的回调函数可以被省略
4.1请求拦截器-Token认证
import axios from ‘./axios’
//配置请求根路径
axios.defaults.baseURL = 'https://www.escook.cn'
axios.interceptors.request.use(function(config){
//为当前请求配置Token认证字段
config.headers.Authorization = 'Bearer xxx'
//Authorization是自定义的
//固定用法
return config;
})
Vue.prototype.$http = axios
4.2 请求拦截器 - 展示Loading效果
借助于element ui 提供的Loading效果组件
可以方便的实现Loading效果的展示
//按需导入 element ui的组件
import {Loading} from 'element-ui'
let loadingInstance = null
import axios from ‘./axios’
axios.defaults.baseURL = 'https://www.escook.cn'
axios.interceptors.request.use(function(config){
//展示Loading效果
loadingInstance = Loading.service({fullscreen:true})
config.headers.Authorization = 'Bearer xxx'
return config;
})
Vue.prototype.$http = axios
5.配置响应拦截器
通过axios.interceptors.request.use(成功回调,失败回调)可以配置响应拦截器
axios.interceptors.request.use(function(response){
return response
},function(error){
return Promise.reject(error)
})
注意失败的回调函数可以被省略
5.1 响应拦截器 - 关闭Loading效果
通过Loading实例提供的close()方法即可关闭Loading效果
axios.interceptors.request.use(function(response){
//调用Loading实例close()方法即可关闭Loading效果
loadingInstance.close()
return response
})
proxy跨域代理
1.回顾:接口的跨域问题
vue项目运行的地址:http//localhost:8080/
API接口运行的地址:https://www.escook.cn/api/users
由于当前的API接口没有开启CORS跨域资源共享,因此默认情况下,上面的接口无法请求成功
2.通过代理解决接口的跨域问题
通过vue-cli场景的项目在遇到接口跨域问题时,可以通过代理的方式来解决:
1把axios的请求根路径设置为vue项目的运行地址
2vue项目发现请求的接口不存在,把请求转交给proxy代理
3代理把请求根路径替换为devServer.proxy属性的值,发起真正的数据请求
4代理把请求到的数据,转发给axios
3.在项目中配置proxy代理
在main.js入口文件中 把axios的请求根路径改为当前web项目的根路径
//配置请求根路径
axios.defaults.baseURL = 'http://localhost:8080'
在项目根路径下创建vue.config.js的配置文件
module.exports={
devServer:{
//当前项目在开发调试阶段
//
proxy:'https://www.escook.cn'
}
}
注意
devServer.proxy提供的代理功能,仅在开发调试阶段生效
项目上线发布时,依旧需要API接口服务器开启CORS跨域资源共享