文章目录
Vue核心
1.初始Vue
Vue是一套构建用户界面的渐进式JavaScript框架
- 想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象
- root容器里的代码依然符合html渲染规范,只不过混入了一些特殊的Vue语法
- root容器里的代码称为【Vue模板】
- Vue实例和容器是一一对应的
- 真实开发中只有一个Vue实例,并且会配合组件一起使用
- {{xxx}}中的xxx要写js表达式,且xxx可以自动读取到data中的所有属性
- 一旦data中的数据发生改变,那么模板中用到该数据地方也会自动更新
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>Hello,{{name}}</h1>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
//创建Vue实例
new Vue({
el:'#root', //el用于指定当前Vue实例为那个容器服务,值通常为css选择器字符串
data:{ //data中用于存储数据,数据供el所指定的容器去使用,值暂时先写成一个对象
name:'尚硅谷'
}
})
</script>
</body>
</html>
2.模板语法
1.插值语法
- 功能:用于解析标签体中的内容
- 语法:{{xxx}},xxx会作为js表达式解析,且可以直接读取到data中的所有属性
2.指令语法
- 功能:解析标签属性、解析标签体内容、绑定事件
- 举例:v-bind:href = ‘xxx’,xxx会作为js表达式解析,可以简写为:href=‘xxx’,xxx同样要写js表达式
- 备注:Vue中有很多指令,且形式都是:v-???,此时我们只是拿v-bind举例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>插值语法</h1>
<!-- 插值语法 {{name}} -->
<h3>你好,{{name}}</h3>
<hr>
<h1>指令语法</h1>
<!-- 指令语法 v-bind: 可以简写成:-->
<a v-bind:href="url">点我去{{school.name}}学习</a>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
new Vue({
el:'#root',
data:{
name:'jack',
school:{
name:'尚硅谷',
url:'http://www.atguigu.com'
}
}
})
</script>
</body>
</html>
3.数据绑定
1.单项绑定(v-bind)
- 数据只能从data流向页面
2.双向绑定(v-model)
- 数据不仅能从data流向页面,还可以从页面流向data
- 双向绑定一般都应用在表单类元素上(如:input、select等)
- v-model:value可以简写为v-model,因为v-model默认收集的就是value值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
<!-- 普通写法 -->
单项数据绑定:<input type="text" v-bind:value="name"><br>
双项数据绑定:<input type="text" v-model:value="name">
<!-- 简写 -->
单项数据绑定:<input type="text" :value="name"><br>
双项数据绑定:<input type="text" v-model="name">
<!-- 如下代码是错误的,因为v-model只能应用在表单类元素(输入类元素) -->
<!-- <h2 v-bind:x="name">你好啊</h2> -->
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
new Vue({
el:'#root',
data:{
name:'尚硅谷'
}
})
</script>
</body>
</html>
3.el和data两种写法
el有两种写法
- new Vue时配置el属性
- 先创建Vue实例,随后再通过vm.$mount(‘#root’)指定el的值
data有两种写法
- 对象式
- 函数式
目前哪种写法都可以,以后学到组件时,data必须使用函数式,否则会报错
重要原则:由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>你好,{{name}}</h1>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
// const v = new Vue({
// // el:'#root', //第一种写法
// data:{
// name:'尚硅谷'
// }
// })
// v.$mount('#root') //第二种写法
new Vue({
el:'#root',
//data第一种写法:对象式
// data:{
// name:'尚硅谷'
// }
//data第二种写法:函数式
data:function(){
return {
name:'尚硅谷'
}
}
})
</script>
</html>
4.MVVM模型
- M:模型(Model)对应data中的数据
- V:视图(View)模板
- VM:视图模型(ViewModel)Vue实例对象
5.数据代理
1.Object.defineProperty方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
let number = 18
let person = {
name:'张三',
sex:'男',
}
Object.defineProperty(person,'age',{
// value:18,
// enumerable:true, //控制属性是否可以枚举,默认值是false
// writable:true, //控制属性是否可以被修改,默认值是false
// configurable:true, //控制属性是否可以被修改,默认值是false
//当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
get:function(){
return 'hello'
},
set(value){
console.log('有人修改了age属性,且值时',value);
number = value
}
})
</script>
</body>
</html>
2.数据代理
数据代理:通过一个对象代理对另一个对象中的属性的操作
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
let obj = {x:100}
let obj2 = {y:200}
Object.defineProperty(obj2,'x',{
get(){
return obj.x
},
set(value){
obj.x = value
}
})
</script>
</body>
</html>
3.Vue中的数据代理
6.事件处理
1.事件基本使用
- 使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名
- 事件的回调需要配置在methods对象中,最终会在vm上
- methods中配置的函数,不要用箭头函数,否则this就不是vm了
- methods中配置的函数,都是被Vue所管理的函数,this的指向是vm或组件实例对象
- @click=“demo” 和 @click="demo($event)"效果一样,但后者可以传参
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>欢迎来到{{name}}学习</h2>
<!-- <button v-on:click="showInfo">点我提示信息</button> -->
<!-- 简写 -->
<button @click="showInfo1">点我提示信息</button>
<button @click="showInfo2(66,$event)">点我提示信息</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
new Vue({
el:'#root',
data:{
name:'尚硅谷'
},
methods:{
showInfo1(event){
// console.log(event.target);
// console.log(this);//此处是this
alert('同学你好1')
},
showInfo2(number,event){
console.log(number);
console.log(event);
// console.log(event.target);
// console.log(this);//此处是this
alert('同学你好2')
}
}
})
</script>
</body>
</html>
2.事件修饰符
-
prevent:阻止默认事件(常用)
阻止了事件的默认行为,相当于调用了
event.preventDefault
方法,提交事件将不再重新加载页面<form @submit.prevent="onSubmit"></form>
-
stop:阻止事件冒泡(常用)
阻止了事件冒泡,相当于调用了
event.stopPropagation
方法,单击事件将停止传递<div @click="shout(2)"> <button @click.stop="shout(1)">ok</button> </div> //只输出1
-
once:事件只触发一次(常用)
绑定了事件以后只能触发一次,第二次就不会触发
<button @click.once="shout(1)">ok</button>
-
capture:使用事件的捕获模式
添加事件监听器时,使用
capture
捕获模式,例如:指向内部元素的事件,在被内部元素处理前,先被外部处理。使事件触发从包含这个元素的顶层开始往下触发<div @click.capture="shout(1)"> obj1 <div @click.capture="shout(2)"> obj2 <div @click="shout(3)"> obj3 <div @click="shout(4)"> obj4 </div> </div> </div> </div> // 输出结构: 1 2 4 3
-
self:只有
event.target
是当前操作的元素是才触发事件仅当
event.target
是元素本身时才会触发事件处理器,例如:事件处理器不来自子元素 -
passive:事件的默认行为立即执行。无需等待事件回调之星完毕
在移动端,当我们在监听元素滚动事件的时候,会一直触发
onscroll
事件会让我们的网页变卡,因此我们使用这个修饰符的时候,相当于给onscroll
事件整了一个.lazy
修饰符。滚动事件的默认行为 (scrolling) 将立即发生而非等待
onScroll
完成,以防其中包含event.preventDefault()
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 --> <!-- 而不会等待 `onScroll` 完成 --> <!-- 这其中包含 `event.preventDefault()` 的情况 --> <div v-on:scroll.passive="onScroll">...</div>
3.键盘事件
Vue中常用的按键别名
- 回车enter
- 删除delete
- 退出esc
- 空格space
- 换行tab(配合keydown使用)
- 上up
- 下down
- 左left
- 右right
Vue中未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名)
系统修饰键(用法特殊):ctrl、alt、shift、meta
- 配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发
- 配合keydown使用:正常触发事件
也可以使用keyCode去指定具体的按键(不推荐)
Vue.config.KeyCodes.自定义键名 = 键码,可以去自定义按键别名
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>欢迎来到{{name}}学习</h2>
<!-- 输入之后按下enter才会触发事件 -->
<input type="text" placeholder="按下回车提示输入" @keyup.enter="showInfo">
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
new Vue({
el:'#root',
data:{
name:'尚硅谷'
},
methods:{
showInfo(e){
console.log(e.target.value)
}
}
})
</script>
</body>
</html>
7.计算属性与监视
1.计算属性
插值语法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
姓:<input type="text" v-model="firstName"><br><br>
名:<input type="text" v-model="lastName"><br><br>
姓名:<span>{{firstName}}-{{lastName}}</span>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三'
}
})
</script>
</body>
</html>
methods实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
姓:<input type="text" v-model="firstName"><br><br>
名:<input type="text" v-model="lastName"><br><br>
姓名:<span>{{fullName()}}</span>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三'
},
methods:{
fullName(){
return this.firstName+'-'+this.lastName
}
}
})
</script>
</body>
</html>
计算属性
- 定义:要用的属性不存在,要通过已有的属性计算得来
- 原理:底层借助了Object.defineproperty方法提供的getter和setter
- get函数什么时候执行
- 初次读取时会执行一次
- 当依赖的数据发生改变时会再次被调用
- 优势:与methods实现相比,内部有缓存机制,效率更高,调试方便
- 备注:
- 计算属性最终会出现在vm上,直接读取使用即可
- 如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生变化
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
姓:<input type="text" v-model="firstName"><br><br>
名:<input type="text" v-model="lastName"><br><br>
姓名:<span>{{fullName}}</span>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
const vm = new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三',
},
computed:{
fullName:{
// 当有人读取fullName时,get会被调用,且饭hi之就作为fullName的值
//get什么时候调用 1.初次读取时调用 2.所依赖的数据发生变化时
get(){
console.log('get被调用');
return this.firstName+'-'+this.lastName
},
//set什么时候调用 当fullName被修改时
set(value){
const arr = value.split('-')
this.firstName = arr[0]
this.lastName = arr[1]
}
}
}
//简写
// fullName:function(){
// return this.firstName+'-'+this.lastName
// }
})
</script>
</body>
</html>
2.监视属性
- 当被监视的属性发生变化时,回调函数自动调用,进行相关操作
- 监视的属性必须存在,才能进行监视
- 监视的两种写法:
- new Vue时传入watch配置
- 通过vm.$watch监视
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
const vm = new Vue({
el:'#root',
data:{
isHot:true
},
computed:{
info(){
return this.isHot?'炎热':'凉爽'
}
},
methods:{
changeWeather(){
this.isHot = !this.isHot
}
},
watch:{
isHot:{
//初始化时让handler调用
// immediate:true
//handler什么时候调用 当isHost发生改变时
handler(newValue,oldValue){
console.log('isHot被修改',newValue,oldValue);
}
}
},
})
// vm.$watch('isHot',{
// //初始化时让handler调用
// // immediate:true
// //handler什么时候调用 当isHost发生改变时
// handler(newValue,oldValue){
// console.log('isHot被修改',newValue,oldValue);
// }
// })
</script>
</body>
</html>
当然我们可以在idHot
对象面添加一个属性:immediate
,当此属性布尔值为真的时候,handler
回调函数在初始化的时候就会调用一次。
3.深度监视
- Vue中的watch默认不监视对象内部值的改变(一层)
- 配置deep:true可以监测对象内部值改变(多层)
备注:
- Vue自身可以检测对象内部值的改变,但Vue提供的watch默认不可以
- 使用watch时根据数据的具体结构,决定是否采用深度监视
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
<hr>
<h3>a的值是:{{numbers.a}}</h3>
<button @click="numbers.a++">点我让a+1</button>
<hr>
<h3>b的值是:{{numbers.b}}</h3>
<button @click="numbers.b++">点我让b+1</button>
<button @click="numbers = {a:777,b:888}">彻底替换numbers</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
const vm = new Vue({
el:'#root',
data:{
isHot:true,
numbers:{
a:1,
b:1
}
},
computed:{
info(){
return this.isHot?'炎热':'凉爽'
}
},
methods:{
changeWeather(){
this.isHot = !this.isHot
}
},
watch:{
isHot:{
//handler什么时候调用 当isHost发生改变时
handler(newValue,oldValue){
console.log('isHot被修改',newValue,oldValue);
}
},
// 监视多级结果中某个属性的变化
'numbers.a':{
handler(){
console.log('a被改变了');
}
},
//监视多级结构中所有属性变化
numbers:{
deep:true,
handler(){
console.log('numbers改变了')
}
}
},
})
</script>
</body>
</html>
简写形式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
const vm = new Vue({
el:'#root',
data:{
isHot:true
},
computed:{
info(){
return this.isHot?'炎热':'凉爽'
}
},
methods:{
changeWeather(){
this.isHot = !this.isHot
}
},
watch:{
//正常写法
// isHot:{
// //初始化时让handler调用
// // immediate:true,
// // deep:true,
// //handler什么时候调用 当isHost发生改变时
// handler(newValue,oldValue){
// console.log('isHot被修改',newValue,oldValue);
// }
// }
//简写
isHot(newValue,oldValue){
console.log('isHot被修改',newValue,oldValue);
}
},
})
vm.$watch('isHot',function(newValue,oldValue){
console.log('isHot被修改',newValue,oldValue);
})
</script>
</body>
</html>
4.对比
computed和watch之间的区别
- computed能完成的功能,watch都可以完成
- watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作
重要原则:
- 所有被Vue管理的函数,最好写成普通函数,这样this的指向才是vm或组件实例对象
- 所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写出箭头函数,这样this的指向才是vm或组件实例对象
8.class和style绑定
1.class样式
写法:
- class=‘xxx’ xxx可以是字符串、对象、数组
- 字符串写法适用于:类名不确定、要动态获取
- 对象写法适用于:要绑定多个样式,个数不确定,名字也不确定
- 数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用
2.style样式
写法:
- :style=‘{fontSize:xxx}’ 其中xxx是动态值
- :style=‘[a,b]’ 其中a、b是样式对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.basic{
width: 300px;
height: 100px;
border: 1px solid black;
}
.happy{
border:4px solid red;
background-color: rgba(255, 255, 0, 0.644);
background: linear-gradient(30deg,yellow,pink,orange,yellow);
}
.sad{
border:4px solid cyan;
background-color: rgba(12, 23, 0, 0.644);
background: linear-gradient(30deg,rgb(178, 60, 5),rgb(168, 72, 88),rgb(155, 47, 146),rgb(114, 40, 40));
}
.normal{
border:4px solid rgba(12, 107, 107, 0.608);
background-color: rgba(12, 23, 0, 0.644);
background: linear-gradient(30deg,rgb(178, 60, 5),rgb(121, 142, 38),rgb(157, 67, 150),rgb(239, 169, 169));
}
.hello1{
background-color: orchid
}
.hello2{
font-size: 40px;
}
.hello3{
color:red
}
</style>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
<!-- 绑定class样式---字符串写法 适用于:样式的类名不确定,还要动态指定 -->
<div class="basic" :class="mood" @click="changeMood">{{name}}</div><br><br><br>
<!-- 绑定class样式---数组写法 适用于:要绑定的样式个数不确定、名字也不确定 -->
<div class="basic" :class="classArr">{{name}}</div><br><br><br>
<!-- 绑定class样式---对象写法 适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 -->
<div class="basic" :class="classObj">{{name}}</div><br><br><br>
<!-- 绑定style样式---对象写法 -->
<div class="basic" :style="styleObj">{{name}}</div><br><br><br>
<div class="basic" :style="[styleObj,styleObj2]">{{name}}</div>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
const vm = new Vue({
el:'#root',
data:{
name:'尚硅谷',
mood:'normal',
classArr:['hello1','hello2','hello3'],
classObj:{
hello1:false,
hello2:false
},
styleObj:{
fontSize:'40px',
color:'red',
},
styleObj2:{
backgroundColor:'orange'
}
},
methods:{
changeMood(){
const arr = ['happy','sad','normal']
const num = Math.floor(Math.random()*3)
this.mood = arr[num]
}
}
})
</script>
</body>
</html>
9.条件渲染
1.v-if
写法:
- v-if=‘表达式’
- v-else-if=‘表达式’
- v-else=‘表达式’
- 适用于:切换频率较低的场景
- 特点:不展示DOM元素直接被移除
- 注意:v-if可以和v-else-if、v-else一起使用,但要求结果不能被打断
2.v-show
写法:
- v-show=‘表达式’
- 适用于:切换频率较高的场景
- 特点:不展示DOM元素未被移除,仅仅是使用样式隐藏掉
备注:使用v-if时,元素可能无法获取到,而使用v-show一定可以获取到
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>当前的n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>
<!-- 使用v-show做条件渲染 -->
<!-- <h2 v-show="false">欢迎来到{{name}}</h2>
<h2 v-show="1 === 1">欢迎来到{{name}}</h2> -->
<!-- 使用v-if做条件渲染 -->
<!-- <h2 v-if="false">欢迎来到{{name}}</h2>
<h2 v-if="1 === 1">欢迎来到{{name}}</h2> -->
<div v-show="n === 1">Angular</div>
<div v-show="n === 2">React</div>
<div v-show="n === 3">Vue</div>
<!-- v-else 和 v-else-if -->
<!-- <div v-if="n === 1">Angular</div>
<div v-else-if="n === 2">React</div>
<div v-else-if="n === 3">Vue</div> -->
<template v-if="n === 1">
<h2>你好</h2>
<h2>尚硅谷</h2>
<h2>北京</h2>
</template>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
new Vue({
el:'#root',
data:{
name:'尚硅谷',
n:0
}
})
</script>
</body>
</html>
10.列表渲染
v-for指令
- 用于展示列表数据
- 语法:
v-for="(item,index) in xxx" :key="yyy"
- 可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
<!-- 遍历数组 -->
<h2>人员列表</h2>
<ul>
<li v-for="p in persons" :key="p.id">
{{p.name}}-{{p.age}}
</li>
</ul>
<!-- 遍历对象 -->
<h2>汽车信息</h2>
<ul>
<li v-for="(value,k) of car" :key="k">
{{k}}---{{value}}
</li>
</ul>
<!-- 遍历字符串 -->
<h2>测试遍历字符串</h2>
<ul>
<li v-for="(char,index) of str" :key="index">
{{char}}---{{index}}
</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
new Vue({
el:'#root',
data:{
persons:[
{id:'001',name:'张三',age:18},
{id:'002',name:'李四',age:19},
{id:'003',name:'王五',age:20}
],
car:{
name:'奥迪A8',
price:'70万',
color:'黑色'
},
str:'hello'
}
})
</script>
</body>
</html>
1.列表过滤
watch实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
<!-- 遍历数组 -->
<h2>人员列表</h2>
<input type="text" placeholder="请输入名字" v-model="keyWord">
<ul>
<li v-for="p in filPersons" :key="p.id">
{{p.name}}-{{p.age}}---{{p.sex}}
</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
new Vue({
el:'#root',
data:{
keyWord:'',
persons:[
{id:'001',name:'马冬梅',age:18,sex:'女'},
{id:'002',name:'周冬雨',age:19,sex:'女'},
{id:'003',name:'周杰伦',age:21,sex:'男'},
{id:'004',name:'温兆伦',age:20,sex:'男'}
],
filPersons:[]
},
watch:{
keyWord:{
immediate:true,
handler(val){
this.filPersons = this.persons.filter((p)=>{
return p.name.indexOf(val) !== -1
})
}
}
}
})
</script>
</body>
</html>
computed实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue初识</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
<!--遍历数组-->
<h2>人员列表(遍历数组)</h2>
<input type="text" placeholder="请输入名字" v-model="keyword"/>
<ul>
<li v-for="(p,index) in filterPersons" :key="p.id">
{{p.name}}--{{p.age}}---{{p.sex}}
</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
//创建vue实例
new Vue({
el: "#root",
data: {
keyword: '',
persons: [
{id: 1, name: "马冬梅", age: 18, sex: "女"},
{id: 2, name: "周冬雨", age: 19, sex: "女"},
{id: 3, name: "周杰伦", age: 20, sex: "男"},
{id: 4, name: "温兆伦", age: 21, sex: "男"},
]
},
computed: {
filterPersons() {
return this.persons.filter((p) => {
return p.name.indexOf(this.keyword) !== -1
})
}
}
})
</script>
</body>
</html>
2.列表排序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue初识</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
<!--遍历数组-->
<h2>人员列表(遍历数组)</h2>
<input type="text" placeholder="请输入名字" v-model="keyword"/>
<button @click="sortType = 2">年龄升序</button>
<button @click="sortType = 1">年龄降序</button>
<button @click="sortType = 0">原顺序</button>
<ul>
<li v-for="(p,index) in filterPersons" :key="p.id">
{{p.name}}--{{p.age}}---{{p.sex}}
</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
//创建vue实例
new Vue({
el: "#root",
data: {
sortType:0, //0原顺序 1降序 2升序
keyword: '',
persons: [
{id: 1, name: "马冬梅", age: 23, sex: "女"},
{id: 2, name: "周冬雨", age: 19, sex: "女"},
{id: 3, name: "周杰伦", age: 20, sex: "男"},
{id: 4, name: "温兆伦", age: 18, sex: "男"},
]
},
computed: {
filterPersons() {
const arr = this.persons.filter((p) => {
return p.name.indexOf(this.keyword) !== -1
})
//判断是否需要排序
if(this.sortType){
arr.sort((p1,p2)=>{
return this.sortType == 1 ? p2.age-p1.age : p1.age-p2.age
})
}
return arr
}
}
})
</script>
</body>
</html>
11.监视数据
Vue监视数据的原理:
-
vue会监视data中所有层次的数据
-
如何检测对象中的数据
通过setter实现监视,且要在new Vue时传入要监测的数据
- 对象中后追加的属性,Vue默认不做响应式处理
- 如需给后添加的属性做响应式,请使用如下API
- Vue.set(target,propertyName/index,value)
- vm.$set(target,propertyName/index,value)
-
如何监测数组中的数据
通过包裹数组更新元素的方法实现,本质就是做了两件事
- 调用原生对应的方法对数组进行更新
- 重新解析模板,进而更新页面
-
在Vue修改数组中的某个元素一定要用如下的方法
- 使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
- Vue.set()或vm.$set()
特别注意:Vue.set()和vm.$set()不能给vm或vm根数据对象添加属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>学生信息</h1><br>
<button @click="student.age++">年龄+1岁</button><br>
<button @click="addSex">添加性别属性:默认值:男</button><br>
<button @click="student.sex = '未知'">修改性别属性</button><br>
<button @click="addFriend">在列表首位添加一个朋友</button><br>
<button @click="updateFirstFriend">修改第一个朋友的名字:张三</button><br>
<button @click="addHobby">添加一个爱好</button><br>
<button @click="updateHobby">修改第一个爱好为:开车</button><br>
<h3>姓名:{{student.name}}</h3>
<h3>年龄:{{student.age}}</h3>
<h3 v-if="student.sex">性别:{{student.sex}}</h3>
<h3>爱好:</h3>
<ul>
<li v-for="(h,index) in student.hobby" :key="index">
{{h}}
</li>
</ul>
<h3>朋友们:</h3>
<ul>
<li v-for="(f,index) in student.friends" :key="index">
{{f.name}}---{{f.age}}
</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
const vm = new Vue({
el:'#root',
data:{
student:{
name:'tom',
age:18,
hobby:['抽烟','喝酒','烫头'],
friends:[
{name:'jerry',age:29},
{name:'tony',age:30}
]
}
},
methods:{
addSex(){
// Vue.set(this.student,'sex','男')
this.$set(this.student,'sex','男')
},
addFriend(){
this.student.friends.unshift({name:"peng",age:32})
},
updateFirstFriend(){
this.student.friends[0].name = '张三'
},
addHobby(){
this.student.hobby.push('学习')
},
updateHobby(){
// this.student.hobby.splice(0,1,'开车')
// Vue.set(this.student.hobby,0,'开车')
this.$set(this.student.hobby,0,'开车')
}
}
})
</script>
</body>
</html>
12.收集表单数据
若:<input type="text"/>
,则v-model收集的是value值,用户输入的就是value值
若:<input type="radio"/>
,则v-model收集的是value值,且要给标签配置value值
若:<input type="checkbox"/>
- 没有配置input的value属性,那么收集的就是checked(勾选或未勾选,是布尔值)
- 配置input的value属性:
- v-model的初始值是非数组,那么收集的就是checked(勾选或未勾选,是布尔值)
- v-model的初始值是数组,那么收集的就是value组成的数组
备注:
v-model的三个修饰符:
- lazy:失去焦点再收集数据
- number:输入字符串转为有效数字
- trim:输入首尾空格过滤
13.过滤器
定义:对要显示的数据进行特定格式化后再显示(适用于简单逻辑的处理)
语法:
- 注册过滤器:Vue.filter(name,callback)或new Vue{filters:{}}
- 使用过滤器:{{xxx | 过滤器名}} 或 v-bind:属性=“xxx | 过滤器名”
备注:
- 过滤器也可以接受额外的参数、多个过滤器也可以串联
- 并没有改变原本的数据,是产生新的对应的数据
14.内置指令
1.v-text
- 作用:向其所在的节点中渲染文本内容
- 与插值语法的区别:v-text会替换掉节点中的内容,{{xxx}}不会
<div>{{name}}</div>
<div v-text="name"></div>
2.v-html
- 作用:向指定的节点渲染包含html结构的内容
- 与插值语法的区别:
- v-html会替换掉节点中所有的内容
- v-html可以识别html结构
- 严重注意:v-html有安全性问题
<div id="root">
<div>{{name}}</div>
<div v-html="str"></div>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
new Vue({
el:'#root',
data:{
name:'尚硅谷',
str:`<h3>你好啊</h3>`
}
})
3.v-cloak
- 本质是一个特殊的属性:Vue实例创建完毕并接管容器后,会删掉v-cloak属性
- 使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题
4.v-once
- v-once所在节点在初始动态渲染之后,就视为静态内容
- 以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
<h2 v-once>初始化n值:{{n}}</h2>
<h2>当前的n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
new Vue({
el:'#root',
data:{
n:1
}
})
</script>
</body>
</html>
5.v-pre
- 跳过其所在节点的编译过程
- 可利用它跳过:没有使用指令语法、没有使用插值语法。会加快编译
<div id="root">
<h2 v-pre>Vue其实很简单</h2>
<h2>当前的n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>
</div>
15.自定义指令
定义语法:
-
局部指令
new Vue({ directives:{指令名:配置对象} }) 或 new Vue({ directives{指令名:配置对象} })
-
全局指令
Vue.directives(指令名,配置对象)或Vue.directive(指令名,回调函数)
配置对象中常用的3个回调函数
- bind:指令与元素成功绑定时调用
- inserted:指令所在元素被插入页面时调用
- update:指令所在模板结构被重新解析时调用
备注:
- 指令定义时不加v-,但使用时要加v-
- 指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<!-- 需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值方法10倍-->
<!-- 需求2:定义一个v-fbing指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点-->
<div id="root">
<h2>当前的n值是:<span v-text="n"></span></h2>
<h2>放大10倍后n值是:<span v-big="n"></span></h2>
<button @click="n++">点我n+1</button><hr>
<input type="text" v-fbind:value="n">
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
new Vue({
el:'#root',
data:{
n:1
},
directives:{
//big函数何时会被调用 1.指令与元素成功绑定时 2.指令所在的模板被重新解析时
big(element,binding){
element.innerText = binding.value * 10
},
fbind:{
//指令与元素成功绑定时(一上来)
bind(element,binding){
element.value = binding.value
},
//指令所在元素被插入页面时
inserted(element,binding){
element.focus()
},
//指令所在模板被重新解析
update(element,binding){
element.value = binding.value
element.focus()
}
}
}
})
</script>
</body>
</html>
16.生命周期
- 又名:生命周期回调函数、生命周期函数、生命周期钩子
- 是什么:Vue在关键时刻帮我们调用一些特殊名称的函数
- 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的
- 生命周期函数中的this指向的是vm或组件实例对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
<h2 :style="{opacity}">欢迎学习Vue</h2>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
new Vue({
el:'#root',
data:{
opacity:1
},
methods:{
},
//Vue完成模板解析并把真实DOM元素放入页面后(挂载完毕)调用
mounted(){
setInterval(()=>{
this.opacity -= 0.01
if(this.opacity<=0) this.opacity=1
},16)
}
})
//通过外部定时器实现(不推荐)
// setInterval(()=>{
// vm.opacity -= 0.01
// if(vm.opacity<=0){
// vm.opacity=1
// }
// },16)
</script>
</body>
</html>
常用的生命周期钩子:
- mounted:发送ajax请求、启动定时器、绑定自定义事件、订阅消息等(初始化操作)
- beforeDestroy:消除定时器、绑定自定义事件、取消订阅消息
关于销毁Vue实例:
- 销毁后借助Vue开发者工具看不到任何消息
- 销毁后自定义事件会失效,但原生的DOM事件依然有效
- 一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程
Vue组件化编程
1.非单文件组件
Vue中使用组件的三大步骤:
-
定义组件
-
注册组件
-
使用组件
-
如何定义一个组件
使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别;
区别如下:
- el不要写,为什么?最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器
- data必须写成函数,为什么?避免组件被复用时,数据存在引用关系
备注:使用template可以配置组件结构
-
如何注册组件
- 局部注册:靠new Vue的时候传入component选项
- 全局注册:靠Vue.component(‘组件名’,组件)
-
编写组件标签
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>基本使用</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>{{msg}}</h1>
<hr>
<!-- 第三步:编写组件标签 -->
<school></school>
<hr>
<!-- 第三步:编写组件标签 -->
<student></student>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
//第一步:创建school组件
const school = Vue.extend({
//组件定义时,一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器。
template:`
<div class="demo">
<h2>学校名称:{{schoolName}}</h2>
<h2>学校地址:{{address}}</h2>
<button @click="showname">点我提示学校名</button>
</div>
`,
data(){
return {
schoolName:'尚硅谷',
address:'北京昌平'
}
},
methods:{
showname(){
alert('尚硅谷')
}
}
})
//第一步:创建student组件
const student = Vue.extend({
template:`
<div>
<h2>学生姓名:{{studentName}}</h2>
<h2>学生年龄:{{age}}</h2>
</div>
`,
data(){
return {
studentName:'JOJO',
age:20
}
}
})
//创建vm
new Vue({
el:'#root',
data:{
msg:'你好,JOJO!'
},
//第二步:注册组件(局部注册)
components:{
school,
student
}
})
</script>
</html>
1.注意点
关于组件名:
-
一个单词组成:
第一种写法(首字母小写):school
第二种写法(首字母大写):School
-
多个单词组成
第一种写法(kebab-case命名):my-schoole
第二种写法(CamelCase命名):MySchool(需要Vue脚手架)
-
备注
- 组件名尽可能回避HTML中已有的元素名称
- 可以使用name配置项指定的组件再开发者工具呈现的名字
关于组件标签:
- 第一种写法:
- 第二种写法:
- 备注:不要使用脚手架时,会到安置后续组件不能渲染
一个简写方式:
const school = Vue.extend(options)可简写为:const school = options
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>{{msg}}</h1>
<school></school>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
//定义组件
const school = Vue.extend({
template:`
<div>
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
`,
data(){
return {
name:'尚硅谷',
address:'北京'
}
}
})
new Vue({
el:'#root',
data:{
msg:'欢迎学习Vue'
},
components:{
school:school
}
})
</script>
</body>
</html>
2.组件的嵌套
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>组件的嵌套</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
//定义student组件
const student = Vue.extend({
template:`
<div>
<h2>学生名称:{{name}}</h2>
<h2>学生年龄:{{age}}</h2>
</div>
`,
data(){
return {
name:'JOJO',
age:20
}
}
})
//定义school组件
const school = Vue.extend({
template:`
<div>
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<student></student>
</div>
`,
components:{
student
},
data(){
return {
name:'尚硅谷',
address:'北京'
}
}
})
//定义hello组件
const hello = Vue.extend({
template:`
<h1>{{msg}}</h1>
`,
data(){
return {
msg:"欢迎学习尚硅谷Vue教程!"
}
}
})
//定义app组件
const app = Vue.extend({
template:`
<div>
<hello></hello>
<school></school>
</div>
`,
components:{
school,
hello
}
})
//创建vm
new Vue({
template:`
<app></app>
`,
el:'#root',
components:{
app
}
})
</script>
</html>
3.VueComponent
- school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的
- 我们只需要写或,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:new VueComponent(options)
- 特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent!
- 关于this指向:
- 组件配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是VueComponent实例对象
- new Vue(options)配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是Vue实例对象
4.一个重要的内置关系
- 一个重要的内置关系:VueComponent.prototype.proto_ === Vue.prototype
- 为什么要有这个关系:让组件实例对象(vc)可以访问Vue原型上的属性和方法
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>一个重要的内置关系</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<school></school>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
Vue.prototype.x = 99
const school = Vue.extend({
name:'school',
template:`
<div>
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<button @click="showX">点我输出x</button>
</div>
`,
data(){
return {
name:'尚硅谷',
address:'北京'
}
},
methods: {
showX(){
console.log(this.x)
}
},
})
const vm = new Vue({
el:'#root',
data:{
msg:'你好'
},
components:{school}
})
</script>
</html>
2.单文件组件
School.vue
<template>
<!--组件的结构-->
<div class="demo">
<h2>学校名称:{{ schoolName }}</h2>
<h2>学校地址:{{ address }}</h2>
<button @click="showName">点我提示学校名</button>
</div>
</template>
<script>
//组件交互代码(数据、方法等)
export default {
name:'School',
data(){
return {
schoolName:'尚硅谷',
address:'北京'
}
},
methods:{
showName(){
alert(this.showName)
}
}
}
</script>
<style>
/* 组建的样式 */
.demo {
background-color: red;
}
</style>
Student.vue
<template>
<!--组件的结构-->
<div class="demo">
<h2>学生姓名:{{ name }}</h2>
<h2>学校年龄:{{ age }}</h2>
</div>
</template>
<script>
//组件交互代码(数据、方法等)
export default {
name:'Student',
data(){
return {
name:'彭于晏',
age:32
}
}
}
</script>
<style>
/* 组建的样式 */
.demo {
background-color: red;
}
</style>
App.vue
<template>
<div>
<School></School>
<Student></Student>
</div>
</template>
<script>
//引入组件
import School from './School.vue'
import Student from './Student.vue'
export default {
name:'App',
components:{
School,
Student
}
}
</script>
<style>
</style>
main.js
import App from './App.vue'
new Vue({
el:'#root',
template:`<App></App>`,
components:{App},
})
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="root">
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript" src="./main.js"></script>
</body>
</html>
Vue脚手架
.文件目录
├── node_modules
├── public
│ ├── favicon.ico: 页签图标
│ └── index.html: 主页面
├── src
│ ├── assets: 存放静态资源
│ │ └── logo.png
│ │── component: 存放组件
│ │ └── HelloWorld.vue
│ │── App.vue: 汇总所有组件
│ └── main.js: 入口文件
├── .gitignore: git版本管制忽略的配置
├── babel.config.js: babel的配置文件
├── package.json: 应用包配置文件
├── README.md: 应用描述文件
└── package-lock.json: 包版本控制文件
1.ref属性
- 被用来给元素或子组件注册引用信息
- 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象
- 使用方式:
<template>
<div>
<h1 v-text="msg" ref="title"></h1>
<button ref="btn" @click="showDOM">点我输出上方的DOM元素</button>
<School id="sch"/>
</div>
</template>
<script>
//引入School组件
import School from './components/School'
export default {
name:'App',
data(){
return{
msg:'欢迎学习vue!'
}
},
components:{
School
},
methods:{
showDOM(){
console.log(this.$refs.title);//真实DOM
console.log(this.$refs.sch);//School组建的实例对象(vc)
console.log(this.$refs.btn);//真实DOM
}
}
}
</script>
2.props配置
功能:让组件接受外部传过来的数据
-
传递数据
-
接收数据
-
第一种方式(只接受)
props:['name']
-
第二种方式(限制类型)
props:{name:String}
-
第三种方式(限制类型、限制必要性、指定默认值)
//接受的同时对数据进行类型限制+默认值的指定 + 必要限制 props:{ name:{ type:String, required:true //名字是必要的 }, age:{ type:Number, default:99 }, sex:{ type:String, required:true } }
-
-
备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props到data中一份,然后去修改data中的数据。
3.mixin混入
功能:可以把多个组件共有的配置提取成一个混入对象
使用方式:
-
第一步定义混合,例如
{ data(){......}, methods:{......} }
-
第二步使用混合,例如
- 全局混入:Vue.mixin(xxx)
- 局部混入:mixins:[‘xxx’]
mixin.js
export const hunhe = {
methods:{
showName(){
alert(this.name)
}
}
}
<template>
<div>
<h2 @click="showName">学生姓名:{{ name }}</h2>
<h2>学生性别:{{ sex }}</h2>
</div>
</template>
<script>
//引入hunhe
import {hunhe} from '../mixin'
export default {
name:'Student',
data(){
return {
name:'张三',
sex:'男'
}
},
mixins:[hunhe]
}
</script>
<style>
</style>
4.插件
功能:用于增强Vue
本质:包含install方法的一个对象,install的第一个参数Vue,第二个以后的参数是插件使用者传递的数据
定义插件:
对象.install = function(Vue,option)
-
提娜佳全局过滤器
Vue.filter(...)
-
添加全局指令
Vue.directive(...)
-
配置全局混入
Vue.mixin(...)
-
添加实例方法
Vue.prototype.$myMethod = function(){...} Vue.prototype.$myProperty = xxx
使用插件:Vue.user()
5.scoped样式
作用:让样式在局部生效,防止冲突
写法:<style scoped>
6.Todo-list案例
- 组件编码流程:
- 拆分静态组件:组件要按照功能点拆分,命名不要与HTML元素冲突
- 实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用
- 一个组件在用:放在组件自身即可
- 一些组件在用:放在他们共同的父组件上(状态提升)
- 实现交互:从绑定事件开始
- props适用于:
- 父组件===>子组件 通信
- 子组件===>父组件通信(要求父组件给子组件一个函数)
- 使用v-model时要切记:v-model绑定的值不能是props传过来的,因为props是不可以修改的
- props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做
App.vue
<template>
<div id="root">
<div class="todo-container">
<div class="todo-wrap">
<MyHeader :addTodo="addTodo"/>
<MyList :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/>
<MyFooter :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"/>
</div>
</div>
</div>
</template>
<script>
import MyHeader from './components/MyHeader.vue'
import MyFooter from './components/MyFooter.vue'
import MyList from './components/MyList.vue'
export default {
name: 'App',
components: {
MyHeader,MyFooter,MyList
},
data(){
return {
todos:[
{id:'001',title:'吃饭',done:true},
{id:'002',title:'喝酒',done:true},
{id:'003',title:'唱歌',done:false},
{id:'004',title:'开车',done:true}
]
}
},
methods:{
//添加todo
addTodo(todoObj){
this.todos.unshift(todoObj)
},
//勾选或取消勾选
checkTodo(id){
this.todos.forEach((todo)=>{
if(todo.id === id) todo.done = !todo.done
})
},
//删除todo
deleteTodo(id){
this.todos = this.todos.filter((todo)=>{
return todo.id !== id
})
},
//全选或全不选
checkAllTodo(done){
this.todos.forEach((todo)=>{
todo.done = done
})
},
//清除所有完成todo
clearAllTodo(){
this.todos = this.todos.filter((todo)=>{
return !todo.done
})
}
}
}
</script>
<style>
/*base*/
body {
background: #fff;
}
.btn {
display: inline-block;
padding: 4px 12px;
margin-bottom: 0;
font-size: 14px;
line-height: 20px;
text-align: center;
vertical-align: middle;
cursor: pointer;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
border-radius: 4px;
}
.btn-danger {
color: #fff;
background-color: #da4f49;
border: 1px solid #bd362f;
}
.btn-danger:hover {
color: #fff;
background-color: #bd362f;
}
.btn:focus {
outline: none;
}
.todo-container {
width: 600px;
margin: 0 auto;
}
.todo-container .todo-wrap {
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
}
</style>
MyHeader.vue
<template>
<div class="todo-header">
<input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="title" @keyup.enter="add"/>
</div>
</template>
<script>
import {nanoid} from 'nanoid'
export default {
name:'MyHeader',
props:['addTodo'],
data(){
return {
title:''
}
},
methods:{
add(e){
if(!this.title.trim()) return
//将用户输入包装成为todo对象
const todoObj = {id:nanoid(),title:e.target.value,done:false}
//通知APP组件添加对象
this.addTodo(todoObj)
//清空输入
this.title = ''
}
},
}
</script>
<style scoped>
/*header*/
.todo-header input {
width: 560px;
height: 28px;
font-size: 14px;
border: 1px solid #ccc;
border-radius: 4px;
padding: 4px 7px;
}
.todo-header input:focus {
outline: none;
border-color: rgba(82, 168, 236, 0.8);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
}
</style>
MyList.vue
<template>
<ul class="todo-main">
<MyItem
v-for="todoObj in todos"
:key="todoObj.id"
:todo="todoObj"
:checkTodo="checkTodo"
:deleteTodo="deleteTodo"/>
</ul>
</template>
<script>
import MyItem from './MyItem.vue'
export default {
name:'MyList',
components:{MyItem},
props:['todos','checkTodo','deleteTodo']
}
</script>
<style scoped>
/*main*/
.todo-main {
margin-left: 0px;
border: 1px solid #ddd;
border-radius: 2px;
padding: 0px;
}
.todo-empty {
height: 40px;
line-height: 40px;
border: 1px solid #ddd;
border-radius: 2px;
padding-left: 5px;
margin-top: 10px;
}
</style>
MyItem.vue
<template>
<li >
<label>
<input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/>
<span>{{todo.title}}</span>
</label>
<button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
</li>
</template>
<script>
export default {
name:'MyItem',
//声明接受todo对象
props:['todo','checkTodo','deleteTodo'],
methods:{
handleCheck(id){
//通知App组件将对应的todo对象的done值取反
this.checkTodo(id)
},
handleDelete(id){
if(confirm('确定删除吗?')){
this.deleteTodo(id)
}
}
}
}
</script>
<style scoped>
/*item*/
li {
list-style: none;
height: 36px;
line-height: 36px;
padding: 0 5px;
border-bottom: 1px solid #ddd;
}
li label {
float: left;
cursor: pointer;
}
li label li input {
vertical-align: middle;
margin-right: 6px;
position: relative;
top: -1px;
}
li button {
float: right;
display: none;
margin-top: 3px;
}
li:before {
content: initial;
}
li:last-child {
border-bottom: none;
}
li:hover {
background-color: #ddd;
}
li:hover button{
display: block;
}
</style>
MyFooter.vue
<template>
<div class="todo-footer" v-show="total">
<label>
<input type="checkbox" :checked="isAll" @change="checkAll"/>
</label>
<span>
<span>已完成{{doneTotal}}</span> / 全部{{total}}
</span>
<button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
</div>
</template>
<script>
export default {
name:'MyFooter',
data(){
return {
}
},
props:['todos','checkAllTodo','clearAllTodo'],
computed:{
total(){
return this.todos.length
},
doneTotal(){
return this.todos.reduce((pre,current)=>{
return pre+(current.done?1:0)
},0)
},
isAll:{
get(){
return this.doneTotal === this.total && this.total>0
},
set(value){
this.checkAllTodo(value)
}
}
},
methods:{
checkAll(e){
this.checkAllTodo(e.target.checked)
},
clearAll(){
this.clearAllTodo()
}
}
}
</script>
<style scoped>
/*footer*/
.todo-footer {
height: 40px;
line-height: 40px;
padding-left: 6px;
margin-top: 5px;
}
.todo-footer label {
display: inline-block;
margin-right: 20px;
cursor: pointer;
}
.todo-footer label input {
position: relative;
top: -1px;
vertical-align: middle;
margin-right: 5px;
}
.todo-footer button {
float: right;
margin-top: 5px;
}
</style>
7.WebStorage
- 存储内容大小一般支持5MB
- 浏览器端通过Window.sessionStorage和Window.localStorage属性来实现本地存储机制
- 相关API
- xxxxStorage.setItem(‘key’,‘value’)
- 该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应值
- xxxxStorage.getItem(‘person’)
- 该方法接受一个键名作为参数,返回键名对应的值
- xxxxStorgae.removeItem(‘key’)
- 该方法接受一个键名作为参数,并把键名从存储中删除
- xxxxStorage.clear()\
- 该方法会清空存储中的所有数据
- xxxxStorage.setItem(‘key’,‘value’)
- 备注:
- SessionStorage存储的内容会随着浏览器的窗口关闭而消失
- LocalStorage存储的内容,需要手动清除才会消失
- xxxxStorage.getItem(xxx)如果xxx对应的value获取不到,那么getItem的返回值为null
- JSON.parse(null)的结果依然是null
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h2>localStorage</h2>
<button onclick="saveData()">点我保存一个数据</button>
<button onclick="readData()">点我读取一个数据</button>
<button onclick="deleteData()">点我删除一个数据</button>
<button onclick="deleteAllData()">点我清空一个数据</button>
<script>
function saveData(){
let p = {name:'张三',age:20}
localStorage.setItem('msg','hello')
localStorage.setItem('boy','彭于晏')
localStorage.setItem('age',43)
localStorage.setItem('person',JSON.stringify(p))
}
function readData(){
console.log(localStorage.getItem('msg'))
console.log(localStorage.getItem('age'))
const result = localStorage.getItem('person')
console.log(JSON.parse(result))
}
function deleteData(){
localStorage.removeItem('msg')
localStorage.removeItem('boy')
}
function deleteAllData(){
localStorage.clear()
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h2>sessionStorage</h2>
<button onclick="saveData()">点我保存一个数据</button>
<button onclick="readData()">点我读取一个数据</button>
<button onclick="deleteData()">点我删除一个数据</button>
<button onclick="deleteAllData()">点我清空一个数据</button>
<script>
function saveData(){
let p = {name:'张三',age:20}
sessionStorage.setItem('msg','hello')
sessionStorage.setItem('boy','彭于晏')
sessionStorage.setItem('age',43)
sessionStorage.setItem('person',JSON.stringify(p))
}
function readData(){
console.log(sessionStorage.getItem('msg'))
console.log(sessionStorage.getItem('age'))
const result = sessionStorage.getItem('person')
console.log(JSON.parse(result))
}
function deleteData(){
sessionStorage.removeItem('msg')
sessionStorage.removeItem('boy')
}
function deleteAllData(){
sessionStorage.clear()
}
</script>
</body>
</html>
8.组件的自定义事件
-
一种组件间通信的方式,适用于:子组件===>父组件
-
使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件
-
绑定自定义事件:
-
第一种方式,在父组件中
<Demo @atguigu='test'
或<Demo v-on:atguigu='test'
-
第二种方式,在父组件中:
<Demo ref='demo'/> mounted(){ this.$refs.xxx.$on('atguigu',this.test) }
-
若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法
-
-
触发自定义事件:
this.$emit('atguigu',数据)
-
解绑自定义事件:
this.$off('atguigu')
-
组件上也可以绑定原生DOM事件,需要使用native修饰符
-
注意:通过
this.$refs.xxx.$on('atguigu',回调)
绑定自定义事件时,回调要么配置在methods中,要么使用建瓯函数,否则this指向会出问题
App.vue
<template>
<div class="app">
<h1>{{ msg }},学生姓名:{{ studentName }}</h1>
<!-- 通过父组件给子组件传递函数类型的props实现:子给父传递数据 -->
<School :getSchoolName=" getSchoolName"/>
<!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第一种写法,使用v-on或@) -->
<Student v-on:atguigu="getStudentName" @demo="m1"/>
<!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第二种写法,使用ref) -->
<Student ref="student" @click.native="show"/>
</div>
</template>
<script>
import Student from './components/Student.vue'
import School from './components/School.vue'
export default {
name: 'App',
components: {
Student,School
},
data(){
return {
msg:'你好啊',
studentName:''
}
},
methods:{
getSchoolName(name){
console.log('App收到了学校名:',name);
},
getStudentName(name,...params){
console.log('App收到了学生名:',name.params);
},
m1(){
console.log('demo事件被触发了:');
}
},
mounted(){
this.$refs.student.$on('atguigu',this.getStudentName)
}
}
</script>
<style>
.app {
background-color: gray;
padding: 5px;
}
</style>
School.vue
<template>
<div class="school">
<h2>学校名称:{{ name }}</h2>
<h2>学校地址:{{ address }}</h2>
<button @click="sendSchoolName">把学校名给App</button>
</div>
</template>
<script>
export default {
name:'School',
data(){
return {
name:'尚硅谷',
address:'北京'
}
},
props:[' getSchoolName'],
methods:{
sendSchoolName(){
this.getSchoolName(this.name)
}
}
}
</script>
<style>
.school {
padding: 5px;
background-color: aqua;
}
</style>
Student.vue
<template>
`` <div class="student">
<h2>学生姓名:{{ name }}</h2>
<h2>学校性别:{{ age }}</h2>
<button @click="sendStudentName">把学生名给App</button>
<button @click="unbind">解绑atguigu事件</button>
<button @click="death">销毁当前Student组件的实例</button>
</div>
</template>
<script>
export default {
name:'Student',
data(){
return {
name:'张三',
address:'男'
}
},
methods:{
sendStudentName(){
//触发Student实例身上的atguigu事件
this.$emit('atguigu',this.name)
this.$emit('demo')
},
unbind(){
this.$off('atguigu')//解绑一个自定义事件
// this.off(['atguigu','demo'])//解绑多个自定义事件
this.$off() //解绑所有自定义事件
},
death(){
this.$destroy()//销毁当前Student组件的实例,销毁后所有Student实例的自定义事件全不奏效
}
}
}
</script>
<style scoped>
.student {
padding: 5px;
margin-top: 30px;
background-color: pink;
}
</style>
9.全局事件总线
-
一种组件间通信的方式,适用于任意组件间通信
-
安装全局事件总线:
new Vue({ render: h => h(App), beforeCreate(){ this.prototype.$bus = this //安装全局事件总线 } }).$mount('#app')
-
使用事件总线:
-
接收数据:A组件想要接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身
methods(){ demo(data){......} } mounted(){ this.$bus.$on('xxx',this.demo) }
-
提供数据:
this.$bus.$emit('xxx',数据)
-
-
最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件
beforeDestroy(){ this.$bus.$off('hello') }
10.消息订阅与发布
Student.vue
<template>
`` <div class="student">
<h2>学生姓名:{{ name }}</h2>
<h2>学校性别:{{ age }}</h2>
<button @click="sendStudentName">把学生名给School组件</button>
</div>
</template>
<script>
import pubsub from 'pubsub-js'
export default {
name:'Student',
data(){
return {
name:'张三',
address:'男'
}
},
methods:{
sendStudentName(){
// this.$bus.$emit('hello',this.name)
pubsub.publish('hello',666)
}
}
}
</script>
<style scoped>
.student {
padding: 5px;
margin-top: 30px;
background-color: pink;
}
</style>
School.vue
<template>
<div class="school">
<h2>学校名称:{{ name }}</h2>
<h2>学校地址:{{ address }}</h2>
</div>
</template>
<script>
import pubsub from 'pubsub-js'
export default {
name:'School',
data(){
return {
name:'尚硅谷',
address:'北京'
}
},
mounted(){
// this.$bus.$on('hello',(data)=>{
// console.log('我是School组件,我收到了数据',data);
// })
this.pubId = pubsub.subscribe('hello',function(msgName,data){
console.log('有人发布了hello消息,hello消息的回调执行了',data);
})
},
beforeDestroy(){
// this.$bus.$off('hello')
pubsub.unsubscribe(this.pubOd)
}
}
</script>
<style>
.school {
padding: 5px;
background-color: aqua;
}
</style>
11.$nextTick
- 语法:
this.$nextTick(回调函数)
- 作用:在下一次DOM更新结束后执行其指定的回调
- 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行
12.过渡与动画
-
作用:在插入、更新或移除DOM元素时,在合适的时候给元素添加样式类名
-
图示:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y45dbEcf-1686448066084)(…/%E5%9B%BE%E7%89%87/image-20230608230944042.png)]
-
写法:
-
准备好样式:
- 元素进入的样式
- v-enter:进入的起点
- v-enter-active:进入的过程
- v-enter-to:进入的终点
- 元素离开的样式
- v-leave:离开的起点
- v-leave-active:离开的过程
- v-leave-to:离开的终点
- 元素进入的样式
-
使用
<transition>
包裹要过渡的元素,并配置name属性<Transition name='hello'> <h1 v-show="isShow">你好啊</h1> </Transition>
-
备注:若有多个元素需要过渡,则需要使用:
<transition-group>
,且每个元素都要指定key值
-
Vue中Ajax
1.vue脚手架配置代理
1.方法一
在vue.config.js中添加如下配置:
//开启代理服务器(方式一)
// devServer: {
// proxy: 'http://localhost:5000'
// },
说明:
- 优点:配置简单,请求资源时直接发给前端(8080)即可
- 缺点:不能配置多个代理,不能灵活的控制请求是否走代理
- 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器(优先匹配前端资源)
2.方法二
编写vue.config.js配置具体代理规则:
//开启代理服务器(方式二)
devServer: {
proxy: {
'/atguigu': { //匹配所有以'/atguigu'开头的请求路径
target: 'http://localhost:5000',//代理目标的基础路径
pathRewrite:{'^/atguigu':''},
ws:true,//用于支持websocket
// ws: true,
// changeOrigin: false
},
'/demo': {
target: 'http://localhost:5001',
pathRewrite:{'^/demo':''},
ws:true,//用于支持websocket
// ws: true,
// changeOrigin: false
},
}
}
说明:
- 优点:可以配置多个代理,且可以灵活控制请求是否走代理
- 缺点:配置略微繁琐,请求资源时必须加前缀
2.guthub案例
App.vue
<template>
<div class="container">
<Search/>
<List/>
</div>
</template>
<script>
import axios from 'axios'
import Search from './components/Search.vue'
import List from './components/List.vue'
export default {
name: 'App',
components:{
Search,List
}
}
</script>
<style>
</style>
Search.vue
<template>
<section class="jumbotron">
<h3 class="jumbotron-heading">Search Github Users</h3>
<div>
<input type="text" placeholder="enter the name you search" v-model="keyWord"/>
<button @click="searchUsers">Search</button>
</div>
</section>
</template>
<script>
import axios from 'axios'
export default {
name:'Search',
data(){
return {
keyWord:''
}
},
methods:{
searchUsers(){
this.$bus.$emit('updateListData',{isFirst:false,isLoading:true,errMsg:'',users:[]})
axios.get(`https://api.github.com/search/users?q=${this.keyWord}`).then(
response => {
//请求成功后更新List数据
console.log('请求成功');
this.$bus.$emit('updateListData',{isLoading:false,errMsg:'',users:response.data.items})
},
error => {
//请求失败后
this.$bus.$emit('updateListData',{isLoading:false,errMsg:error.message,users:[]})
}
)
}
}
}
</script>
<style>
</style>
List.vue
<template>
<div class="row">
<!-- 展示用户列表 -->
<div class="card" v-for="user in info.users" :key="user.login" v-show="info.users.length">
<a :href="user.html_url" target="_blank">
<img :src="user.avatar_url" style='width: 100px'/>
</a>
<p class="card-text">{{user.login}}</p>
</div>
<!-- 展示欢迎词 -->
<h1 v-show="info.isFirst">欢迎使用!</h1>
<!-- 展示加载中 -->
<h1 v-show="info.isLoading">加载中....</h1>
<!-- 展示错误信息 -->
<h1 v-show="info.errMsg">{{ info.errMsg }}</h1>
</div>
</template>
<script>
export default {
name:'List',
data(){
return {
info:{
isFirst:true,
isLoading:false,
errMsg:'',
users:[
]
}
}
},
mounted(){
this.$bus.$on('updateListData',(dataObj)=>{
this.info = {...this.info,...dataObj}
})
}
}
</script>
<style scoped>
.album {
min-height: 50rem; /* Can be removed; just added for demo purposes */
padding-top: 3rem;
padding-bottom: 3rem;
background-color: #f7f7f7;
}
.card {
float: left;
width: 33.333%;
padding: .75rem;
margin-bottom: 2rem;
border: 1px solid #efefef;
text-align: center;
}
.card > img {
margin-bottom: .75rem;
border-radius: 100px;
}
.card-text {
font-size: 85%;
}
</style>
3.vue-resource
4.slot插槽
-
作用:让父组件可以向子组件指定位置插入html结构,也是组件间通信的方式,适用于父组件=>子组件
-
分类:默认插槽、具名插槽、作用域插槽
-
使用方式:
-
默认插槽
父组件中: <Category> <div>html结构1</div> </Category> 子组件中: <template> <div> <slot>插槽默认内容...</slot> </div> </template>
-
具名插槽
父组件中: <Category> <template slot="center"> <div>html结构1</div> </template> <template v-slot:footer> <div>html结构2</div> </template> </Category> 子组件中: <template> <div> <slot name="center">插槽默认内容...</slot> <slot name="footer">插槽默认内容...</slot> </div> </template>
-
作用域插槽
父组件中: <Category> <template scope="scopeData"> <!-- 生成的是ul列表 --> <ul> <li v-for="g in scopeData.games" :key="g">{{g}}</li> </ul> </template> </Category> <Category> <template slot-scope="scopeData"> <!-- 生成的是h4标题 --> <h4 v-for="g in scopeData.games" :key="g">{{g}}</h4> </template> </Category> 子组件中: <template> <div> <slot :games="games"></slot> </div> </template> <script> export default { name:'Category', props:['title'], //数据在子组件自身 data() { return { games:['红色警戒','穿越火线','劲舞团','超级玛丽'] } }, } </script>
-
1.默认插槽
App.vue
<template>
<div class="container">
<Category title="美食" >
<img src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
</Category>
<Category title="游戏" >
<ul>
<li v-for="(g,index) in games" :key="index">{{g}}</li>
</ul>
</Category>
<Category title="电影">
<video controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video>
</Category>
</div>
</template>
<script>
import Category from './components/Category'
export default {
name:'App',
components:{Category},
data() {
return {
games:['植物大战僵尸','红色警戒','空洞骑士','王国']
}
},
}
</script>
<style scoped>
.container{
display: flex;
justify-content: space-around;
}
</style>
Category.vue
<template>
<div class="category">
<h3>{{title}}分类</h3>
<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
<slot>我是一些默认值,当使用者没有传递具体结构时,我会出现</slot>
</div>
</template>
<script>
export default {
name:'Category',
props:['title']
}
</script>
<style scoped>
.category{
background-color: skyblue;
width: 200px;
height: 300px;
}
h3{
text-align: center;
background-color: orange;
}
video{
width: 100%;
}
img{
width: 100%;
}
</style>
2.具名插槽
App.vue
<template>
<div class="container">
<Category title="美食" >
<img slot="center" src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
<a slot="footer" href="http://www.atguigu.com">更多美食</a>
</Category>
<Category title="游戏" >
<ul slot="center">
<li v-for="(g,index) in games" :key="index">{{g}}</li>
</ul>
<div class="foot" slot="footer">
<a href="http://www.atguigu.com">单机游戏</a>
<a href="http://www.atguigu.com">网络游戏</a>
</div>
</Category>
<Category title="电影">
<video slot="center" controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video>
<template v-slot:footer>
<div class="foot">
<a href="http://www.atguigu.com">经典</a>
<a href="http://www.atguigu.com">热门</a>
<a href="http://www.atguigu.com">推荐</a>
</div>
<h4>欢迎前来观影</h4>
</template>
</Category>
</div>
</template>
<script>
import Category from './components/Category'
export default {
name:'App',
components:{Category},
data() {
return {
games:['植物大战僵尸','红色警戒','空洞骑士','王国']
}
},
}
</script>
<style scoped>
.container,.foot {
display: flex;
justify-content: space-around;
}
h4 {
text-align: center;
}
</style>
Category.vue
<template>
<div class="category">
<h3>{{title}}分类</h3>
<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
<slot name="center">我是一些默认值,当使用者没有传递具体结构时,我会出现</slot>
<slot name="footer">我是一些默认值,当使用者没有传递具体结构时,我会出现</slot>
</div>
</template>
<script>
export default {
name:'Category',
props:['title']
}
</script>
<style scoped>
.category{
background-color: skyblue;
width: 200px;
height: 300px;
}
h3{
text-align: center;
background-color: orange;
}
video{
width: 100%;
}
img{
width: 100%;
}
</style>
3.作用域插槽
App.vue
<template>
<div class="container">
<Category title="游戏" >
<template scope="jojo">
<ul>
<li v-for="(g,index) in jojo.games" :key="index">{{g}}</li>
</ul>
</template>
</Category>
<Category title="游戏" >
<template scope="jojo">
<ol>
<li v-for="(g,index) in jojo.games" :key="index">{{g}}</li>
</ol>
</template>
</Category>
<Category title="游戏" >
<template scope="jojo">
<h4 v-for="(g,index) in jojo.games" :key="index">{{g}}</h4>
</template>
</Category>
</div>
</template>
<script>
import Category from './components/Category'
export default {
name:'App',
components:{Category}
}
</script>
<style>
.container,.foot{
display: flex;
justify-content: space-around;
}
h4{
text-align: center;
}
</style>
Category.vue
<template>
<div class="category">
<h3>{{title}}分类</h3>
<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
<slot :games="games">我是一些默认值,当使用者没有传递具体结构时,我会出现1</slot>
</div>
</template>
<script>
export default {
name:'Category',
props:['title'],
data() {
return {
games:['植物大战僵尸','红色警戒','空洞骑士','王国']
}
},
}
</script>
<style scoped>
.category{
background-color: skyblue;
width: 200px;
height: 300px;
}
h3{
text-align: center;
background-color: orange;
}
video{
width: 100%;
}
img{
width: 100%;
}
</style>
Vuex
-
概念:专门在Vue中实现集中式状态(数据)管理的一个Vue插件。对Vue应用中多个组件的共享状态进行集中式的管理(读/写),也是组件间通信的方式,且适用于任意组件间通信
-
何时使用
- 多个组件依赖于同一个状态
- 来自不同组件的行为需要变更同一状态
1.搭建vuex环境
-
创建文件:src/store/index.js
//该文件用于创建Vuex中最为核心的store //引入vue核心库 import Vue from 'vue' //引入Vuex import Vuex from 'vuex' Vue.use(Vuex) //准备actions——用于响应组件中的动作 const actions = {} //准备Mutations——用于操纵数据(state) const mutations = {} //准备state——用于存储函数 const state = {} //创建store并暴露 export default new Vuex.Store({ actions, mutations, state, })
-
在main.js中创建vm时传入store配置项
...... //引入store import store from './store' ...... //创建vm new Vue({ el:'#app', render:h=>h(App), store })
2.基本使用
-
初始化数据、配置actions、配置mutations,操作文件index.js
//该文件用于创建Vuex中最为核心的store //引入Vuex import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) //准备actions——用于响应组件中的动作 const actions = { jia(context,value){ context.commit('JIA',value) }, jian(context,value){ context.commit('JIAN',value) }, jiaOdd(context,value){ if(context.state.sum%2){ context.commit('JIA',value) } }, jiaWait(context,value){ setTimeout(()=>{ context.commit('JIA',value) },500) } } //准备Mutations——用于操纵数据(state) const mutations = { JIA(state,value){ state.sum += value }, JIAN(state,value){ state.sum -= value } } //准备state——用于存储函数 const state = { sum:0,//当前和 } //创建store export default new Vuex.Store({ actions, mutations, state, }) //导出/暴露store
-
组件中读取vuex中的数据:
$store.state.sum
-
组件中修改vuex中的数据:
$store.dispatch('action中的方法名',数据)
或$store.commit('mutations中的方法名',数据)
3.getter配置项
-
概念:当state中的数据需要经过加工后再使用时,可以使用getters加工
-
在index.js中追加getter配置
//该文件用于创建Vuex中最为核心的store //引入Vuex import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) //准备actions——用于响应组件中的动作 const actions = { jia(context,value){ context.commit('JIA',value) }, jian(context,value){ context.commit('JIAN',value) }, jiaOdd(context,value){ if(context.state.sum%2){ context.commit('JIA',value) } }, jiaWait(context,value){ setTimeout(()=>{ context.commit('JIA',value) },500) } } //准备Mutations——用于操纵数据(state) const mutations = { JIA(state,value){ state.sum += value }, JIAN(state,value){ state.sum -= value } } //准备state——用于存储函数 const state = { sum:0,//当前和 } //准备getters——用于将state中的数据进行加工 const getters = { bigSum(state){ return state.sum*10 } } //创建store export default new Vuex.Store({ actions, mutations, state, getters }) //导出/暴露store
-
组件中读取数据:
$store.getter.bigSum
4.四个mapper方法
-
mapState方法:用于帮助我们映射state中的数据为计算属性
computed:{ // sum(){ // return $store.state.sum // }, // bigSum(){ // return $store.getters.bigSum // }, // school(){ // return $store.state.school // }, // subject(){ // return $store.state.subject // }, //借助mapState生成计算属性,从state中读取数据(对象写法) ...mapState({sum:'sum',school:'school',subject:'subject'}), //借助mapState生成计算属性,从state中读取数据(数组写法) //...mapState(['sum','school','subject']), }
-
mapGetters方法:用于帮助我们映射getters中的数据为计算属性
computed:{ // bigSum(){ // return $store.getters.bigSum // }, //借助mapGetters生成计算属性,从getters中读取数据(对象写法) ...mapGetters({bigSum:'bigSum'}), //借助mapGetters生成计算属性,从getters中读取数据(数组写法) ...mapGetters({bigSum:'bigSum'}) }
-
mapActions方法:用于帮助我们生成actions对话的方法,即包含:
$store.dispatch(xxx)
函数methods:{ //靠mapActions生成:incrementOdd、incrementWait(对象形式) ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'}) //靠mapActions生成:incrementOdd、incrementWait(数组形式) ...mapActions(['jiaOdd','jiaWait']) }
-
mapMutations方法:用于帮助我们生成与mutations对话的方法,即包含
$store.commit(xxx)
函数methods:{ //靠mapActions生成:increment、decrement(对象形式) ...mapMutations({increment:'JIA',decrement:'JIAN'}), //靠mapMutations生成:JIA、JIAN(对象形式) ...mapMutations(['JIA','JIAN']), }
-
备注:mapActions与mapMutations使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象
index.js
//该文件用于创建Vuex中最为核心的store
//引入Vuex
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
//准备actions——用于响应组件中的动作
const actions = {
jia(context,value){
context.commit('JIA',value)
},
jian(context,value){
context.commit('JIAN',value)
},
jiaOdd(context,value){
if(context.state.sum%2){
context.commit('JIA',value)
}
},
jiaWait(context,value){
setTimeout(()=>{
context.commit('JIA',value)
},500)
}
}
//准备Mutations——用于操纵数据(state)
const mutations = {
JIA(state,value){
state.sum += value
},
JIAN(state,value){
state.sum -= value
}
}
//准备state——用于存储函数
const state = {
sum:0,//当前和
school:'尚硅谷',
subject:'前端'
}
//准备getters——用于将state中的数据进行加工
const getters = {
bigSum(state){
return state.sum*10
}
}
//创建store
export default new Vuex.Store({
actions,
mutations,
state,
getters
})
//导出/暴露store
Count.vue
<template>
<div>
<h1>当前求和为:{{ sum }}</h1>
<h3>当前求和放大10倍后为:{{ bigSum }}</h3>
<h3>我在{{ school }},学习{{ subject }}</h3>
<select v-model.number="n">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment(n)">+</button>
<button @click="decrement(n)">-</button>
<button @click="incrementOdd(n)">当前求和为奇数再加</button>
<button @click="incrementWait(n)">等一等再加</button>
</div>
</template>
<script>
import {mapState,mapGetters,mapActions,mapMutations} from 'vuex'
export default {
name:'Count',
data(){
return {
n:1,//用户选择数字
}
},
methods:{
// increment(){
// this.$store.commit('JIA',this.n)
// },
// decrement(){
// this.$store.commit('JIAN',this.n)
// },
//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
...mapMutations({increment:'JIA',decrement:'JIAN'}),
//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
// ...mapMutations(['JIA','JIAN']),
// incrementOdd(){
// this.$store.dispatch('jiaOdd',this.n)
// },
// incrementWait(){
// this.$store.dispatch('jiaWait',this.n)
// },
...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
},
computed:{
// sum(){
// return $store.state.sum
// },
// bigSum(){
// return $store.getters.bigSum
// },
// school(){
// return $store.state.school
// },
// subject(){
// return $store.state.subject
// },
//借助mapState生成计算属性,从state中读取数据(对象写法)
...mapState({sum:'sum',school:'school',subject:'subject'}),
//借助mapState生成计算属性,从state中读取数据(数组写法)
//...mapState(['sum','school','subject']),
//借助mapGetters生成计算属性,从getters中读取数据(对象写法)
...mapGetters({bigSum:'bigSum'}),
//借助mapGetters生成计算属性,从getters中读取数据(数组写法)
...mapGetters({bigSum:'bigSum'})
}
}
</script>
<style lang="css">
button {
margin-left: 5px;
}
</style>
5.多组件共享数据
index.js
//该文件用于创建Vuex中最为核心的store
//引入Vuex
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
//准备actions——用于响应组件中的动作
const actions = {
jia(context,value){
context.commit('JIA',value)
},
jian(context,value){
context.commit('JIAN',value)
},
jiaOdd(context,value){
if(context.state.sum%2){
context.commit('JIA',value)
}
},
jiaWait(context,value){
setTimeout(()=>{
context.commit('JIA',value)
},500)
}
}
//准备Mutations——用于操纵数据(state)
const mutations = {
JIA(state,value){
state.sum += value
},
JIAN(state,value){
state.sum -= value
},
ADD_PERSON(state,value){
state.personList.unshift(value)
}
}
//准备state——用于存储函数
const state = {
sum:0,//当前和
school:'尚硅谷',
subject:'前端',
personList:[
{id:'001',name:'张三'}
]
}
//准备getters——用于将state中的数据进行加工
const getters = {
bigSum(state){
return state.sum*10
}
}
//创建store
export default new Vuex.Store({
actions,
mutations,
state,
getters
})
//导出/暴露store
Count.vue
<template>
<div>
<h1>当前求和为:{{ sum }}</h1>
<h3>当前求和放大10倍后为:{{ bigSum }}</h3>
<h3>我在{{ school }},学习{{ subject }}</h3>
<h3 style="color: red;">下方组件的总人数是:{{ personList.length }}</h3>
<select v-model.number="n">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment(n)">+</button>
<button @click="decrement(n)">-</button>
<button @click="incrementOdd(n)">当前求和为奇数再加</button>
<button @click="incrementWait(n)">等一等再加</button>
</div>
</template>
<script>
import {mapState,mapGetters,mapActions,mapMutations} from 'vuex'
export default {
name:'Count',
data(){
return {
n:1,//用户选择数字
}
},
methods:{
//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
...mapMutations({increment:'JIA',decrement:'JIAN'}),
//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
// ...mapMutations(['JIA','JIAN']),
...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
},
computed:{
//借助mapState生成计算属性,从state中读取数据(对象写法)
// ...mapState({sum:'sum',school:'school',subject:'subject'}),
//借助mapState生成计算属性,从state中读取数据(数组写法)
...mapState(['sum','school','subject','personList']),
//借助mapGetters生成计算属性,从getters中读取数据(数组写法)
...mapGetters({bigSum:'bigSum'})
}
}
</script>
<style lang="css">
button {
margin-left: 5px;
}
</style>
Person.vue
<template>
<div>
<h1>人员列表</h1>
<h3 style="color: red;">上方组件的求和为:{{ sum }}</h3>
<input type="text" placeholder="请输入名字" v-model="name">
<button @click="add">添加</button>
<ul>
<li v-for="p in personList" :key="p.id">{{ p.name }}</li>
</ul>
</div>
</template>
<script>
import { mapState } from 'vuex';
import {nanoid} from 'nanoid'
export default {
name:'Person',
data(){
return {
name:''
}
},
computed:{
// personList(){
// return this.$store.state.personList
// }
...mapState(['personList']),
sum(){
return this.$store.state.sum
}
},
methods:{
add(){
const obj = {id:nanoid,name:this.name}
this.$store.commit('ADD_PERSON',obj)
this.name = ''
}
}
}
</script>
6.模块化+命名控件
-
目的:让代码更好维护,让多种分类更加明确
-
修改store.js
const countAbout = { namespaced:true,//开启命名空间 state:{x:1}, mutations: { ... }, actions: { ... }, getters: { bigSum(state){ return state.sum * 10 } } } const personAbout = { namespaced:true,//开启命名空间 state:{ ... }, mutations: { ... }, actions: { ... } } const store = new Vuex.Store({ modules: { countAbout, personAbout } })
-
开启命名空间后,组件中读取state数据
//方式一:自己直接读取 this.$store.state.personAbout.list //方式二:借助mapState读取: ...mapState('countAbout',['sum','school','subject']),
-
开启命名空间后,组件读取getters数据
//方式一:自己直接读取 this.$store.getters['personAbout/firstPersonName'] //方式二:借助mapGetters读取: ...mapGetters('countAbout',['bigSum'])
-
开启命名空间后,组件中调用dispatch
//方式一:自己直接dispatch this.$store.dispatch('personAbout/addPersonWang',person) //方式二:借助mapActions: ...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
-
开启命名空间后,组件中调用commit
//方式一:自己直接commit this.$store.commit('personAbout/ADD_PERSON',person) //方式二:借助mapMutations: ...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),
src/store/index.js
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//引入count
import countOptions from './count'
//引入person
import personOptions from './person'
//应用Vuex插件
Vue.use(Vuex)
//创建并暴露store
export default new Vuex.Store({
modules:{
countAbout:countOptions,
personAbout:personOptions,
}
})
src/store/count.js
export default{
namespaced:true,
actions:{
addOdd(context,value){
console.log("actions中的addOdd被调用了")
if(context.state.sum % 2){
context.commit('ADD',value)
}
},
addWait(context,value){
console.log("actions中的addWait被调用了")
setTimeout(()=>{
context.commit('ADD',value)
},500)
}
},
mutations:{
ADD(state,value){
state.sum += value
},
SUBTRACT(state,value){
state.sum -= value
}
},
state:{
sum:0, //当前的和
name:'JOJO',
school:'尚硅谷',
},
getters:{
bigSum(state){
return state.sum * 10
}
}
}
src/store/person.js
import axios from "axios"
import { nanoid } from "nanoid"
export default{
namespaced:true,
actions:{
addPersonWang(context,value){
if(value.name.indexOf('王') === 0){
context.commit('ADD_PERSON',value)
}else{
alert('添加的人必须姓王!')
}
},
addPersonServer(context){
axios.get('http://api.uixsj.cn/hitokoto/get?type=social').then(
response => {
context.commit('ADD_PERSON',{id:nanoid(),name:response.data})
},
error => {
alert(error.message)
}
)
}
},
mutations:{
ADD_PERSON(state,value){
console.log('mutations中的ADD_PERSON被调用了')
state.personList.unshift(value)
}
},
state:{
personList:[
{id:'001',name:'JOJO'}
]
},
getters:{
firstPersonName(state){
return state.personList[0].name
}
}
}
count.vue
<template>
<div>
<h1>当前求和为:{{sum}}</h1>
<h3>当前求和的10倍为:{{bigSum}}</h3>
<h3>我是{{name}},我在{{school}}学习</h3>
<h3 style="color:red">Person组件的总人数是:{{personList.length}}</h3>
<select v-model.number="n">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment(n)">+</button>
<button @click="decrement(n)">-</button>
<button @click="incrementOdd(n)">当前求和为奇数再加</button>
<button @click="incrementWait(n)">等一等再加</button>
</div>
</template>
<script>
import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
export default {
name:'Count',
data() {
return {
n:1, //用户选择的数字
}
},
methods: {
...mapMutations('countAbout',{increment:'ADD',decrement:'SUBTRACT'}),
...mapActions('countAbout',{incrementOdd:'addOdd',incrementWait:'addWait'})
},
computed:{
...mapState('countAbout',['sum','school','name']),
...mapGetters('countAbout',['bigSum']),
...mapState('personAbout',['personList'])
}
}
</script>
<style>
button{
margin-left: 5px;
}
</style>
person.vue
<template>
<div>
<h1>人员列表</h1>
<h3 style="color:red">Count组件求和为:{{sum}}</h3>
<h3>列表中第一个人的名字是:{{firstPersonName}}</h3>
<input type="text" placeholder="请输入名字" v-model="name">
<button @click="add">添加</button>
<button @click="addWang">添加一个姓王的人</button>
<button @click="addPerson">随机添加一个人</button>
<ul>
<li v-for="p in personList" :key="p.id">{{p.name}}</li>
</ul>
</div>
</template>
<script>
import {nanoid} from 'nanoid'
export default {
name:'Person',
data() {
return {
name:''
}
},
computed:{
personList(){
return this.$store.state.personAbout.personList
},
sum(){
return this.$store.state.countAbout.sum
},
firstPersonName(){
return this.$store.getters['personAbout/firstPersonName']
}
},
methods: {
add(){
const personObj = {id:nanoid(),name:this.name}
this.$store.commit('personAbout/ADD_PERSON',personObj)
this.name = ''
},
addWang(){
const personObj = {id:nanoid(),name:this.name}
this.$store.dispatch('personAbout/addPersonWang',personObj)
this.name = ''
},
addPerson(){
this.$store.dispatch('personAbout/addPersonServer')
}
},
}
</script>
Vue-router
1.简介
1.理解
vue的一个插件库,专门用来实现SPA应用
2.对SPA应用的理解
- 单页Web应用
- 整个应用只有一个完整的页面
- 点击页面中的导航链接不会刷新页面,只会做页面的局部更新
- 数据需要通过ajax请求获取
2.路由
1.什么是路由
- 一个路由就是一组映射关系
- key为路径,value可能是function或component
2.路由分类
- 后端路由:
- 理解:value是function,用于处理客户端提交的请求
- 工作过程:服务器接收到一个请求时,根据请求路径找到匹配的函数来处理请求,返回响应数据
- 前端路由:
- 理解:value是component,用于展示页面内容
- 工作过程:当浏览器的路径改变时,对应的组件就会显示
3.基本路由
-
安装vue-router,命令:
npm i vue-router
-
应用插件:
Vue.user(VueRouter)
-
编写router配置项
// 该文件专门用于创建整个应用的路由器 import VueRouter from 'vue-router' //引入组件 import About from '../components/About' import Home from '../components/Home' //创建一个路由器并暴露 export default new VueRouter({ routes:[ { path:'/about', component:About }, { path:'/home', component:Home } ] })
-
实现切换(active-class可配置高亮样式)
<router-link class="list-group-item" active-class="active" to="/about">About</router-link> <router-link class="list-group-item" active-class="active" to="/home">Home</router-link>
-
指定展示位置
<router-view></router-view>
4.注意点
- 路由组件通常存放在pages文件夹,一般组件通常存放在components文件夹
- 通过切换,隐藏了路由组件,默认是被销毁掉的,需要的时候再去挂载
- 每个组件都有自己的
$route
属性,里面存储着自己的路由信息 - 整个应用只有一个router,可以通过组件的
$router
属性获取到
5.嵌套路由
-
配置路由规则,使用children配置项
// 该文件专门用于创建整个应用的路由器 import VueRouter from 'vue-router' //引入组件 import About from '../pages/About' import Home from '../pages/Home' import News from '../pages/News' import Message from '../pages/Message' //创建一个路由器并暴露 export default new VueRouter({ routes:[ { path:'/about', component:About }, { path:'/home', component:Home, children:[ { path:'news', component:News }, { path:'message', component:Message } ] } ] })
-
跳转(要写完整路径)
<router-link class="list-group-item" active-class="active" to="/home/news">News</router-link>
6.路由传参
-
传递参数
<!-- 跳转路由并携带query参数,to的字符串写法 --> <!-- <router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">{{ m.title }}</router-link> --> <!-- 跳转路由并携带query参数,to的对象写法 --> <router-link :to="{ path:'/home/message/detail', query:{ id:m.id, title:m.title } }"> {{ m.title }} </router-link>
-
接受参数
$route.query.id $route.query.title
7.命名路由
-
作用:可以简化路由的跳转
-
如何使用
-
给路由命名
{ path:'/demo', component:Demo, children:[ { path:'test', component:Test, children:[ { name:'hello' //给路由命名 path:'welcome', component:Hello, } ] } ] }
-
简化跳转
<!--简化前,需要写完整的路径 --> <router-link to="/demo/test/welcome">跳转</router-link> <!--简化后,直接通过名字跳转 --> <router-link :to="{name:'hello'}">跳转</router-link> <!--简化写法配合传递参数 --> <router-link :to="{ name:'hello', query:{ id:666, title:'你好' } }" >跳转</router-link>
-
8.params参数
-
配置路由,声明接受params参数
{ path:'/home', component:Home, children:[ { path:'news', component:News }, { component:Message, children:[ { name:'xiangqing', path:'detail/:id/:title', //使用占位符声明接收params参数 component:Detail } ] } ] }
-
传递参数
<!-- 跳转并携带params参数,to的字符串写法 --> <router-link :to="/home/message/detail/666/你好">跳转</router-link> <!-- 跳转并携带params参数,to的对象写法 --> <router-link :to="{ name:'xiangqing', params:{ id:666, title:'你好' } }" >跳转</router-link>
特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置
-
接收参数
$route.params.id $route.params.title
9.props配置
作用:让路由组件更方便的收到参数
{
name:'xiangqing',
path:'detail/:id',
component:Detail,
//第一种写法:props值为对象,该对象中所有的key-value的组合最终会通过props传给Detail组件
//props:{a:900}
//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
//props:true
//第三种写法:props值为函数,该函数返回的对象种每一组key-value都会通过props传给Detail组件
props(route){
return {
id:route.query.id,
title:route.query.title
}
}
}
10.replace属性
- 作用:控制路由跳转时操作浏览器历史记录的模式
- 浏览器的历史记录有两种写入方式:分别为push和replace,push是追加历史记录,replace是替换当前记录。路由跳转时候默认为push
- 如何开启replace模式:
<router-link replace ......></router-link>
11.编程式路由导航
-
作用:不借助
<router-link>
实现路由跳转,让路由跳转更加灵活 -
具体编码:
this.$router.push({ name:'xiangqing', params:{ id:xxx, title:xxx } }) this.$router.replace({ name:'xiangqing', params:{ id:xxx, title:xxx } }) this.$router.forward() //前进 this.$router.back() //后退 this.$router.go() //可前进也可后退
12.缓存路由组件
-
作用:让步展示的路由组件保持挂载,不被销毁
-
具体编码:
<keep-alive include="News"> <router-view></router-view> </keep-alive> <!-- 缓存多个路由组件 --> <keep-alive include="['News','Message']"> <router-view></router-view> </keep-alive>
13.两个新生命周期钩子
- 作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态
- 具体名字:
activated
路由组件被激活时触发deactivated
路由组件失活时触发
14.路由守卫
-
作用:对路由进行权限控制
-
分类:全局守卫、独享守卫、组件内守卫
-
全局守卫:
//全局前置守卫:初始化时执行、每次路由切换前执行 router.beforeEach((to,from,next)=>{ console.log('beforeEach',to,from) if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制 if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则 next() //放行 }else{ alert('暂无权限查看') } }else{ next() //放行 } }) //全局后置守卫:初始化时执行、每次路由切换后执行 router.afterEach((to,from) => { console.log('afterEach',to,from) if(to.meta.title){ document.title = to.meta.title //修改网页的title }else{ document.title = 'vue_test' } })
-
独享守卫
beforeEnter(to,from,next){ console.log('beforeEnter',to,from) if(localStorage.getItem('school') === 'atguigu'){ next() }else{ alert('暂无权限查看') } }
-
组件内守卫
//进入守卫:通过路由规则,进入该组件时被调用 beforeRouteEnter (to, from, next) {...}, //离开守卫:通过路由规则,离开该组件时被调用 beforeRouteLeave (to, from, next) {...},
15.路由器的两种工作模式
-
对于一个url来说,什么是hash值?——#及其后面的内容就是hash值
-
hash值不会包含在HTTP请求中,即:hash值不会带给服务器
-
hash模式:
- 地址中永远带着#号,不美观
- 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法
- 兼容性较好
-
history模式:
- 地址干净
- 兼容性和hash模式相比略差
- 应用部署上线时需要后端人员支持,解决刷新页面服务端404问题
class=“active” to=“/home/news”>News
## 6.路由传参
1. 传递参数
```vue
<!-- 跳转路由并携带query参数,to的字符串写法 -->
<!-- <router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">{{ m.title }}</router-link> -->
<!-- 跳转路由并携带query参数,to的对象写法 -->
<router-link :to="{
path:'/home/message/detail',
query:{
id:m.id,
title:m.title
}
}">
{{ m.title }}
</router-link>
-
接受参数
$route.query.id $route.query.title
7.命名路由
-
作用:可以简化路由的跳转
-
如何使用
-
给路由命名
{ path:'/demo', component:Demo, children:[ { path:'test', component:Test, children:[ { name:'hello' //给路由命名 path:'welcome', component:Hello, } ] } ] }
-
简化跳转
<!--简化前,需要写完整的路径 --> <router-link to="/demo/test/welcome">跳转</router-link> <!--简化后,直接通过名字跳转 --> <router-link :to="{name:'hello'}">跳转</router-link> <!--简化写法配合传递参数 --> <router-link :to="{ name:'hello', query:{ id:666, title:'你好' } }" >跳转</router-link>
-
8.params参数
-
配置路由,声明接受params参数
{ path:'/home', component:Home, children:[ { path:'news', component:News }, { component:Message, children:[ { name:'xiangqing', path:'detail/:id/:title', //使用占位符声明接收params参数 component:Detail } ] } ] }
-
传递参数
<!-- 跳转并携带params参数,to的字符串写法 --> <router-link :to="/home/message/detail/666/你好">跳转</router-link> <!-- 跳转并携带params参数,to的对象写法 --> <router-link :to="{ name:'xiangqing', params:{ id:666, title:'你好' } }" >跳转</router-link>
特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置
-
接收参数
$route.params.id $route.params.title
9.props配置
作用:让路由组件更方便的收到参数
{
name:'xiangqing',
path:'detail/:id',
component:Detail,
//第一种写法:props值为对象,该对象中所有的key-value的组合最终会通过props传给Detail组件
//props:{a:900}
//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
//props:true
//第三种写法:props值为函数,该函数返回的对象种每一组key-value都会通过props传给Detail组件
props(route){
return {
id:route.query.id,
title:route.query.title
}
}
}
10.replace属性
- 作用:控制路由跳转时操作浏览器历史记录的模式
- 浏览器的历史记录有两种写入方式:分别为push和replace,push是追加历史记录,replace是替换当前记录。路由跳转时候默认为push
- 如何开启replace模式:
<router-link replace ......></router-link>
11.编程式路由导航
-
作用:不借助
<router-link>
实现路由跳转,让路由跳转更加灵活 -
具体编码:
this.$router.push({ name:'xiangqing', params:{ id:xxx, title:xxx } }) this.$router.replace({ name:'xiangqing', params:{ id:xxx, title:xxx } }) this.$router.forward() //前进 this.$router.back() //后退 this.$router.go() //可前进也可后退
12.缓存路由组件
-
作用:让步展示的路由组件保持挂载,不被销毁
-
具体编码:
<keep-alive include="News"> <router-view></router-view> </keep-alive> <!-- 缓存多个路由组件 --> <keep-alive include="['News','Message']"> <router-view></router-view> </keep-alive>
13.两个新生命周期钩子
- 作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态
- 具体名字:
activated
路由组件被激活时触发deactivated
路由组件失活时触发
14.路由守卫
-
作用:对路由进行权限控制
-
分类:全局守卫、独享守卫、组件内守卫
-
全局守卫:
//全局前置守卫:初始化时执行、每次路由切换前执行 router.beforeEach((to,from,next)=>{ console.log('beforeEach',to,from) if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制 if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则 next() //放行 }else{ alert('暂无权限查看') } }else{ next() //放行 } }) //全局后置守卫:初始化时执行、每次路由切换后执行 router.afterEach((to,from) => { console.log('afterEach',to,from) if(to.meta.title){ document.title = to.meta.title //修改网页的title }else{ document.title = 'vue_test' } })
-
独享守卫
beforeEnter(to,from,next){ console.log('beforeEnter',to,from) if(localStorage.getItem('school') === 'atguigu'){ next() }else{ alert('暂无权限查看') } }
-
组件内守卫
//进入守卫:通过路由规则,进入该组件时被调用 beforeRouteEnter (to, from, next) {...}, //离开守卫:通过路由规则,离开该组件时被调用 beforeRouteLeave (to, from, next) {...},
15.路由器的两种工作模式
-
对于一个url来说,什么是hash值?——#及其后面的内容就是hash值
-
hash值不会包含在HTTP请求中,即:hash值不会带给服务器
-
hash模式:
- 地址中永远带着#号,不美观
- 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法
- 兼容性较好
-
history模式:
- 地址干净
- 兼容性和hash模式相比略差
- 应用部署上线时需要后端人员支持,解决刷新页面服务端404问题