文章目录
- vue基础知识
- Vue-x
- 路由
- ui组件
vue基础知识
初识:
Vue实例跟容器是一对一的关系。
尝试2容器1vue实例 vue实例只能接管前面一个 后面一个容器管不了
<h1 id="title">nihao,{{name}}</h1>
<h1 id="title">nihao,{{name}}</h1>
<script>
new Vue({
el:'#title',//尝试用了class 效果也是一样的 只照顾到前面那个
data:{
name:'北京'
}
})
</script>
响应式原理
插值语法&模板语法
插值语法:
{{js表达式}}
(js表达式可以理解成一个用变量去装的东西,a++,字符串拼接。。。)
插值里面的变量自动获得data对象里面的数据(其实是暴露VM模型的内部结构),data对象类似之前那个windows对象,属性为变量,里面再嵌套对象就按照对象:属性
来调用。
模板语法:
通过vue命令改变标签的属性、行为等,用于解析标签。
数据绑定(v-bind、v-model)
简写:
v-bind 单向数据绑定 只能data–>容器
动态地绑定一个或多个 attribute,或一个组件 prop 到表达式。
我的理解是:
v-bind就是动态地绑定dom元素的属性,让dom元素里面的属性的那个值跟表达式一样
eg:
v-bind: 将url当成一个变量 就去Vue实例里面找url属性了
可以简写成:
v-model 双向数据绑定 只能用在表单类元素(有value值的输入类元素)上
其实是对元素的value进行操作
<input class="inputbox" v-model:keywords="keywords" type="text" />
绑定class样式
补充数组操作
el&data的两种写法
可以使用v.$mount(‘css选择器’)来绑定对象
const v=new Vue({
// el: ".title",//第一种写法
data: {
name: "北京",
},
});
v.$mount('.title');//第二种写法
在写成普通函数声明的时候,函数的this指向vue实例,写成箭头函数的时候函数的this指向全局的window对象
MVVM模型
可以理解成dom是表示层 VueModel是逻辑层 Model是数据层。VM在页面dom元素设置监听,并且绑定数据层data中的数据,当data中数据更新时,将实时更新表示层dom,如果采用双向绑定,dom元素value改变数据层data中的数据也会实时改变。
补充:下面这样写会报Reference ERROR 说找不到alert属性 这是因为表示层dom绑死在VM上了,表示层只能看到VM作用域里面的数据 alert是window的 VM模型里面没有。。不是作用域链可以往外找这样。。可以将window作为内置对象解决(但是Vue实例是可以读到的window的属性的 那个是作用域链 要分清楚捏
数据代理
就是不直接操作对象,使用中间商操作数据
补充:Object.defineProperties(要修改的对象,属性,配置对象)
例子:obj2通过中间商x来操作obj对象
let obj={x:100}
let obj2={y:200}
Object.defineProperty(obj2,'x',{
get(){
return obj.x//每次通过obj2访问obj.x都是通过getter去访问的
},
set(value){
obj.x=value//每次通过obj2修改obj.x都是通过getter去访问的
}
})
VM模型采用的也是数据代理,使用一个对象去new一个vue实例,数据层data中的数据复制到vm模型里面,并进行加工之后使用_data属性存起来。使用数据代理,vm做中间商,好少写个_data
。好像还用了观察者模式(?
事件处理
<button id="root" v-on:click="show">nihao</button>
const v = new Vue({
data: {
name: "北京",
},
methods: {//记住不要写成method了 有s
show:function() {
console.log("hello");
},
},
});
v.$mount("#root");
事件修饰符
事件修饰符可以连用
点击事件
v-on:click
简写:v-on:简写成@
v-on:click===》@click
键盘事件
ctrl+y
计算属性
计算实例通过对Vue现有属性的计算得到新的属性,叫做计算属性。数据源改变,计算属性根据改变的数据源在原来数据的基础上进行修改。
我感觉跟数据代理很像,计算属性的数据源于Vue实例里面的data对象里面的值,双向绑定的。数据源改变,计算属性调用getter更新,注意,不更新绑定的数据就没什么用,不更新数据源计算属性不会变(其实跟页面元素很像耶。。不过计算属性也的的确确是Vue、VC实例里面的属性就是说。
不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值
这也同样意味着下面的计算属性将不再更新,因为 Date.now()
不是响应式依赖:
computed: {
now: function () {
return Date.now()
}
}
缓存这两个字很重要,缓存意味着是计算属性先保留了一份数据源的值,等到响应式依赖数据源发生变化的时候才会更新计算属性数据源。
methods的this是整个Vue实例,不加this就找不到的原因已经是不在methods这个对象里面,找上一层一直找到最外层的window也没有(作用域链想起没哇(。 以前的知识不要丢哇
<div id="root">
姓 <input type="text" v-model='fn'></br>
名<input type="text" v-model='sn'></br>
full<span>{{full()}}</span>
</div>
<script>
new Vue({
el:'#root',
data:{
fn:'fn',
sn:'sn'
},
methods:{
full(){
return this.fn+'-'+this.sn//为什么要加this才能用 不加this会报错
//[Vue warn]: Error in render: "ReferenceError: fn is not defined"
}
}
})
</script>
在最外面加了就能过了
fn=2
sn=1
new Vue({
el:'#root',
data:{
fn:'fn',
sn:'sn'
},
methods:{
full(){
return fn+'-'+sn
}
}
})
姓名例子:
let vm=new Vue({
el:'#root',
data:{
fn:'fn',
sn:'sn'
},
computed:{
full:{
get(){
console.log('getter');
return this.fn+'-'+this.sn
},
set(value){
const arr=value.split('-')
this.fn=arr[0]
this.sn=arr[1] }
}
}
})
计算属性简写(只读不改):
计算属性setter
计算属性默认只有 getter,不过在需要时你也可以提供一个 setter:
// ...
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
// ...
现在再运行 vm.fullName = 'John Doe'
时,setter 会被调用,vm.firstName
和 vm.lastName
也会相应地被更新。
计算属性setter是会更改数据源的。
监视属性 Watch
监视的属性不存在监视没有作用 但是不会报错(f u
watch:{
keyWord:{
immediate:true,
handler(value)//value传的是监控的值的新值,第二个变量传的是之前的值
{
this.filPer=this.per.filter((p)=>{//vue管理的函数this应该是vue实例
return p.id.indexOf(value)!==-1
})
}
}
}
深度监视 deep
监视引用变量的时候,watch默认检测的变量存的地址值,只要对象不变,watch是不会检测到变化的,可开启deep值,检测引用的对象的变化。
例子:
点击btn,obj的bool置反。watch监视的是对象obj,开了deep,能监视到对象里面的改变,但是由于监视的对象没变,所以传给handler的newValue跟oldValue都没有变化,打印的bool值就都是最新那个值啦。
<div id="root">
<button @click="sw">{{obj.bool}}</button>
</div>
new Vue({
el:"#root",
data:{
obj:{
bool:true
}
},
methods:{
sw(){
this.obj.bool=!this.obj.bool
// this.obj={}
}
},
watch:{
obj:{
deep:true,
handler(newValue,oldValue){
console.log(newValue.bool+','+oldValue.bool)
}
}
}
})
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6ebo4jka-1647068009438)(D:/Typora/image-20211025201456438.png)]
监视简写(只使用handler):
那个function别写成箭头函数了,会出this问题(。
Vue原生的数据监视原理
使用观察者模式
Vue.set()
this
补充箭头函数this
要知道js里面只有两种作用域(es5):函数作用域跟全局作用域。es6新增的作用域是对于let关键字而言的,对于其他还是只有两种作用域啦。
箭头函数本质是return语句啦,没有自己的this,所以用的定义时父级的this
如例子中 作用域只有一个:全局window 对象a并不是作用域哦!对象没有自己的作用域,所以不能充当箭头函数的this,套一个普通函数,里面再放箭头函数,移花接木一下估计可以。
var name = 'window';
var A = {
name: 'A',
sayHello: () => {
console.log(this.name)
}
}
A.sayHello();// 还是以为输出A ? 错啦,其实输出的是window
移花接木ver
var A = {
name: 'A',
sayHello:function (){
let a=()=> {
console.log(this.name)
}
a()
}
}
A.sayHello();//A
普通函数的this可以指向A 是因为自己分割出了一个作用域
var name = 'window'; // 其实是window.name = 'window'
var A = {
name: 'A',
sayHello: function(){
console.log(this.name)
}
}
A.sayHello();// 输出A
条件渲染(v-if、v-show)
v-show(操作css)
底层通过调整css的display v-show='false’是display:none 元素节点还是在的只不显示了。
<div v-show="false" id="root" >
<button @click="sw">{{obj.bool}}</button>
</div>
v-if(操作dom)
v-if
也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
条件判断语句,v-if条件为真时才渲染元素,条件为假时是不渲染元素的,所以节点根本就不在dom树里面。
<div v-show="false" id="root" >
<button @click="sw">{{obj.bool}}</button>
</div>
还有v-else-if v-else跟普通的一样 就是形成一组判断了就是说 btw v-else后面跟条件没有用(但是也不会报错就是说
为了形成一组判断 不可以被打断 被打断后的就判断作废 被没有相通的v-if
<div id="root" >
<div v-if="false"></div>
<button @click="sw">{{obj.bool}}</button>
<div v-else-if='true'>11111111</div>
</div>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RtFv9Tzi-1647068009443)(D:/Typora/image-20211026184032244.png)]
v-if 例子2
window.onload = function () {
//创建一个vue实例
var app = new Vue({
el: "#app",
data: {
type: "A",
loginType: "username",
},
methods: {
changeloginType() {
let self = this;
if (self.loginType == "username") {
self.loginType = "";
} else {
self.loginType = "username";
}
},
},
});
};
<div id="app">
<div style="color: red">v-if的简单实用</div>
<template>
<div v-if="type == 'A'">A</div>
<div v-else-if="type=='B'">B</div>
<div v-else>C</div>
</template>
<div style="color: green">v-if的弹框切换</div>
<template v-if="loginType === 'username'">
<label>用户名:</label>
<input placeholder="Enter your username" key="username-input" />
</template>
<template v-else>
<label>密码:</label>
<input placeholder="Enter your email address" key="email-input" />
</template>
<button @click="changeloginType">切换状态</button>
</div>
针对例子补充 template标签:
浏览器不对template进行渲染 渲染时自动丢掉 不会出现在dom节点里面 只能和v-if配合使用 v-show不行
<div>
<div id="root">
<template>
<div v-if="false"></div>
<button @click="sw">{{obj.bool}}</button>
<div>11111111</div>
</template>
</div>
</div>
而且会导致找不到root 不是外面包东西的原因 好像就是因为template
<div>
<template>
<div id="root">
<div v-show="false"></div>
<button @click="sw">{{obj.bool}}</button>
<div>11111111</div>
</div>
</template>
</div>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sqyf0ekB-1647068009444)(D:/Typora/image-20211026184944175.png)]
列表渲染(v-for)
基本概念
当 v-if
与 v-for
一起使用时,v-for
具有比 v-if
更高的优先级。(但是文档并不推荐两个一起用!)
基于源数据多次渲染元素或模板块。此指令之值,必须使用特定语法 alias in expression
,为当前遍历的元素提供别名
首先要明确,v-for
写在哪里就是列表渲染哪个,如下面。v-for
写在div
里面,渲染items.length
个div
!!!
v-for
别名的作用域是自己跟子元素。
<div v-for="item in items"><!--item就是别名-->
{{ item.text }}
</div>
另外也可以为数组索引指定别名 (或者用于对象的键):
<div v-for="(item, index) in items"></div>
<div v-for="(val, key) in object"></div>
<div v-for="(val, name, index) in object"></div>
v-for
的默认行为会尝试原地修改元素而不是移动它们。要强制其重新排序元素,你需要用特殊 attribute key
来提供一个排序提示(绑定的key
就变成数组元素的索引了)(有看到说key不止这种用法)
以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一
key
attribute(文档原文)
<div v-for="item in items" :key="item.id">
{{ item.text }}
</div>
key注意点:
- 建议尽可能在使用
v-for
时提供key
attribute,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升(有key性能会好一点?) - 不要使用对象或数组之类的非基本类型值作为
v-for
的key
。请用字符串或数值类型的值。
v-for就是做循环,就是包装了for循环
v-for可以连着in跟of一起(即支持for..in
、for...of
)
v-for…in遍历数组
<div id="root">
<ul>
<li v-for="p,index in person">
{{p}},{{index}}
</li>
</ul>
</div>
new Vue({
el: "#root",
data: {
person:[
{id:001,name:1},
{id:002,name:2},
{id:003,name:3},
]
},
});
v-for…in遍历对象
第一个参数是属性的值,第二个参数是属性名,第三个是index,属性排第几个(只有对象可以这么搞,数组不行,数组第二个一定是index)
<div id="root">
<ul>
<li v-for="(value,attr,index) in person">
{{value}}---{{attr}}---{{index}}
</li>
</ul>
</div>
new Vue({
el: "#root",
data: {
person:
{id:"dsasf",name:"sdgvsdf"},
},
});
使用v-for…of遍历数组、对象、次数、字符串(后两个基本不用)
遍历次数跟python的for in range一样。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yAteWrSX-1647068009452)(D:/Typora/image-20211026190235388.png)]
v-for注意点
-
在遍历对象时,会按
Object.keys()
的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎下都一致。 -
其实会涉及到一个作用域的问题 v-for迭代的对象在其子作用域内可见,但是在其兄弟作用域是不可见
<ul> <li v-for="item in items"> <li>{{items}}</li> </li> </ul>
这样嵌套的
<li>
编译会编译成<ul> <li></li> <li>{{items}}</li> </ul>
由于兄弟是看不见的 所以不能看到那个
items
也就出错了 -
v-for是先做循环 然后再跟着下面的(其实这个就是很普通的一个点,就是要记住v-for就是for就是经过包装的for 可以用来控制虚拟dom节点
-
<li @mouseenter="changeLiBgc(index)" v-for="(groupMenuitem, index) in groupMenuitems" :key="index" :class="{ cur: currentIndex == index }"></li>
changeLiBgc(index) { this.$data.currentIndex = index; console.log("index"+index) console.log(this.$data.currentIndex) },
这个是在写尚品汇的时候的一个小的
li
标签,是三级联动部分。当鼠标移动到
li
上的时候触发mouseenter
事件,函数changeLiBgc(index)
执行,传的是li
执行v-for
的索引,只会传鼠标enter的那个元素的index
。按照这个的话,后面就比较好理解,但是我自己说不出个所以然来。(是不是因为mouseenter
,传EventTarget
传了li
的index)我觉得可能不能完全按照以前的for循环去理解
v-for
,我以前学的for循环做完就不会再进行改变了,但是v-for
是动态的。=》
v-for
做完循环,将虚拟dom变成真实dom,它就会有各种各样的属性,渲染出来的li
对象里面有自己的index
,当鼠标放上去的时候触发mouseenter
事件,将currentIndex
赋值成index
,然后class
v-bind绑定了,计算{}
里面js表达式的值。
key原理
diff
在内存中对key相同的节点进行比较,如果内容一样则将之前渲染过的节点直接拿来用,否则就将放弃掉之前的节点,重新渲染虚拟dom节点成真实dom节点。
总结:
就是使用index作为key的时候(不写key默认将遍历时候的index作为唯一标识),当在前面插入/删除节点时,会造成节点与index的顺序改变 从而造成输入类元素对应不上
如果自定义唯一标识的话,就可以进行更多的重用 只新增新的节点 省去了重新生成旧节点 效率更高
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4xr6IwQ4-1647068009454)(D:/Typora/image-20211026222826520.png)]
补一个用index做key的坏处:
列表过滤
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6ufJExOV-1647068009459)(D:/Typora/image-20211027201206101.png)]
new Vue({
el:'#root',
data:{
keyWord:'',
per:[{id:'001',name:'aa'},
{id:'002',name:'bb'},
{id:'003',name:'cc'},
],
filPer:[]
},
watch:{
keyWord:{
immediate:true,
handler(value){
this.filPer=this.per.filter((p)=>{
return p.id.indexOf(value)!==-1
})
}
}
}
})
补充数组filter
Array.filter(),不会对空数组进行检测,不会改变原始数组。
Array.filter(()=>{
return 判断条件//这个函数的返回值要是布尔值 要么true 要么false
})//filter返回由旧数组符合条件的值的数构成的新数组
表单收集数据的技巧
其他指令
v-html
v-cloak
防止网速过慢出现插值语法。。
自定义指令
全局指令和局部指令的区别:
全局指令可供整个页面使用,而局部指令只作用于他定义的那个vue实例
全局指令就是直接定义在Vue上,局部就是包在一个vue实例里面
函数式声明
自己敲了一遍 修改name已经不会调用big了(不知道是不是vue3的优化?
调用big应该修改成:
1.第一次解析模板时 被调用
2.指令其绑定的数据源发生改变
const vm=new Vue({
el: "#root",
data: {
n: 1,
name:"shang"
},
directives: {
big(el, binding)//第一个传的是要操作htmlElement元素 第二个传的是这个绑定的对象
{
console.log('big')
el.innerText=binding.value*10
},
},
});
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YOyWisMA-1647068009466)(D:/Typora/image-20211028132122785.png)]
输出的binding:
对象式声明
官方文档的使用函数式获取不了焦点是因为一开始在vue执行指令的时还没有放到页面上 所以element.focus没有用
上面简写的函数式声明,只用了bind、update,没有inserted。自定义指令里面函数的this都是window,并不是vue去维护指令的。其实也好理解,因为自定义指令是直接对dom操作,不经vue手也很正常。
Vue.config.productionTip = false;
const vm=new Vue({
el: "#root",
data: {
n: 1,
name:"shang"
},
directives: {
big(el, binding) {
console.log(this)//window
el.innerText=binding.value*10
},
fbind:{
//指令与元素成功绑定时(元素还在内存 vue还没有将元素放到页面上 简写成函数式的话就是只用bind+update了
bind(el,binding){
el.value=binding.value
console.log(this)//window
},
//指令所在元素被插入到页面时
inserted(el,binding){
el.focus()
},
//指令所在模板被重新解析时(模板重新解析?
update(el,binding){
console.log('update')
}
}
}
});
全局指令
写在vue实例里面的是局部指令 只能那个vue实例用 其他实例用不了 写全局指令的话要放到Vue里面(写到构造函数里面 当成属性一起继承使用
生命周期
生命周期图详解:
beforeCreate:初始化生命周期、事件(mothods)但是还没执行数据代理(没有_data _data是undefined!beforeCreate的before是指在数据检测、数据代理之前
created:初始化数据检测、数据代理
然后开始找el、template编译虚拟dom
beforeMount:对dom的操作最终不奏效
在挂载之前,将虚拟dom编译,产生 e l 用 el 用 el用el替换掉el
将真实dom挂载到页面上
Mounted:东西都挂好了,页面上显示的是经过vue编译、解析的dom元素 可以对页面上的元素做点想做的操作了。
beforeUpdate:在内存中 已经更新了数据 但是页面上的数据还没更新 还没将更新的元素解析挂到页面上
updated:数据已经更新到页面了 页面数据与内存数据同步
beforeDestroy:可以读到data、method,但是不能够进行修改 因为更新要进行update 已经走到这里了 不能回头走update了
destoryed:
摧毁阶段 断掉事件监听、数据检测、跟其他实例的联系 虽然说断掉了事件监听 但是已经绑上的dom事件不会随着vue事件被摧毁而解开 比如给btn绑了点击事件 vue实例destoryed了btn的点击事件还是绑着的
生命周期函数mounted(挂载)
页加载渲染完成之后,会自动执行的方法。
Vue完成模板解析,并将真实dom放到页面(挂载)之后执行,只会执行一次。
Vue.createApp({
data() {
return {
counter: 1,
};
},
mounted(){
console.log("页面加载完成之后自动执行的函数")
setInterval(()=>{
this.counter+=1
},1000)
},
template: "<div>{{counter}}</div>",
}).mount("#app");
<div id="root">
姓 <input type="text" v-model='fn'></br>
名<input type="text" v-model='sn'></br>
full<span>{{full}}</span>
</div>
组件
模块化跟组件化是两种并行的思想。模块化跟组件化都是封装,就是封装内部逻辑,暴露一些接口。
data要用函数式:
对象为引用类型,当重用组件时,由于数据对象都指向同一个data对象,当在一个组件中修改data时,其他重用的组件中的data会同时被修改;而使用返回对象的函数,由于每次返回的都是一个新对象(Object的实例),引用地址不同,则不会出现这个问题
Vue.config.productionTip = false;
//组件
const school = Vue.extend({
template: `
<div>
{{names}}
<br>
{{address}}
</div>
`,
data() {
return {
names: "dsgfd",
address: "gz",
};
},
});
//反正只是一个配置对象 自动有个判断 其实也调用了 Vue.extend
const school = {
template: `
<div>
{{names}}
<br>
{{address}}
</div>
`,
data() {
return {
names: "dsgfd",
address: "gz",
};
},
}
new Vue({
el: "#root",
components: {//配置项都是对象 要写对 真无语了 我是笨蛋
schools: school,
},
});
VueComponent
在写props代码的时候一直没有注册 然后就提示没有使用子组件 原来是因为没有注册 人家不认识 就说没有用!!!
Vue.config.productionTip = false;
//组件
const school = Vue.extend({//在调用Vue.extend()的时候其实自动new VueComponent()来构造组件了
template: `
<div>
{{names}}
<br>
{{address}}
</div>
`,
data() {
return {
names: "dsgfd",
address: "gz",
};
},
})
new Vue({
el: "#root",
components: {//一定要注册 注册才算使用了 不登记谁tm知道你名字 人家不认识啊
schools: school,
},
});
console.log(school)
vc跟vm差不多 belike vm删减版
vm-》app(vc头子)-》其他vc 还是子父节点那套
Vc&vm的原型链
为了让vc访问vm上面的方法
VueComponent.prototpye.__proto__=Vue.prototpye
脚手架
render函数
//简写
new Vue({
render: h => h(App),
}).$mount('#app')
//完整版
new Vue({
render(createElement) {
return createElement(App)},
}).$mount('#app')
render(createElement)
渲染函数,根据拿到的虚拟节点渲染到页面上。一定要有返回值,用createElement返回,要拿到节点才能去渲染。
createElement(html元素|组件,元素属性,子节点内容)用于创建虚拟dom元素。约定的简写叫 h, vm中有一个方法 _c, 也是这个函数的别名。在Vue2中,虚拟DOM就是通过一种VNode类表达,每个DOM元素或组件都对应一个VNode对象。
ref
相当于原生的getElementById()
app.vue文件函数里面的this都是vc组件 确实(。不然呢
是vm上的一个属性$refs
show(){
console.log(this)
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UdUH3scQ-1647068009479)(D:/Typora/image-20211112001253639.png)]
show(){
console.log(this.$refs.btn)
}
给html标签加上ref属性之后,就自动加到vc的$refs里面,被收集了。跟以前加id是一样的。
如果加的是组件标签就将组件加到$refs里面
props
首先要明确 props传的东西只是变量里面的值(也就是belike浅复制) 传的只是对象的地址 并不是对象的值(props是只读的)
首先可以实现组件间通信,主要是父子间的通信,实现兄弟间的通信就比较麻烦,要借助父组件,子传父再传兄弟,不如直接使用vuex比较方便。
为什么说是父子组件间通信呢,这主要是通过给组件的属性赋值来传递。要注册组件这必然是父子组件了。父传子:
//home.vue
<div>
<three :ismove="true"></three>
</div>
此外,从子->父的通信 父给子一个函数子调用,传参,数据传回父组件里面。
除了上面的通过props
来静态地绑定,也可以通过v-bind
动态绑定。
prop 可以通过 v-bind
动态赋值,例如:
<!-- 动态赋予一个变量的值 -->
<blog-post v-bind:title="post.title"></blog-post>
<!-- 动态赋予一个复杂表达式的值 -->
<blog-post
v-bind:title="post.title + ' by ' + post.author.name"
></blog-post>
传入一个对象的所有 property
如果你想要将一个对象的所有 property 都作为 prop 传入,你可以使用不带参数的 v-bind
(取代 v-bind:prop-name
)。例如,对于一个给定的对象 post
:
post: {
id: 1,
title: 'My Journey with Vue'
}
下面的模板:
<blog-post v-bind="post"></blog-post>
等价于:
<blog-post
v-bind:id="post.id"
v-bind:title="post.title"
></blog-post>
改变一个prop
的方法(2种):
-
**这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。**在这种情况下,最好定义一个本地的 data property 并将这个 prop 用作其初始值:
props: ['initialCounter'], data: function () { return { counter: this.initialCounter } }
-
**这个 prop 以一种原始的值传入且需要进行转换。**在这种情况下,最好使用这个 prop 的值来定义一个计算属性:
props: ['size'], computed: { normalizedSize: function () { return this.size.trim().toLowerCase() } }
注意:
在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变变更这个对象或数组本身****将会影响到父组件的状态。
mixin
mixin的话this是指向组件的 纯纯滴工具人
//组件
<template>
<div>
<h1>子组件</h1>
<button @click="showname">showname</button>
</div>
</template>
<script>
import {mixin} from './mixin'
export default {
name:'child',
props:{
zsq:{
type:String,
default:'zzzsq'
}
},
methods: {
showzsq(){
console.log(this.zsq)
}
},
data(){
return {
name:'jack'
}
},
mixins:[mixin]//配置项一定是数组 一个也是
}
</script>
//mixin.js
export let mixin={
methods:{
showname(){
console.log(this.name)
}
}
}
插件
scoped
在vue单文件组件中,为了防止全局同css类名名样式的污染,vue-loade对单文件组件 <style>
标签增加了scoped
属性的处理。原理就是在html标签上添加data-v-xxxxxxxx
属性,然后在css类名后添加属性选择器,即利用css类选择 + 属性选择器实现样式局部化。
使用了scoped之后,就加了data-v-xxxxxxxx
属性属性而已,避免污染其他的。
todolist总结
本地存储
以键值对的形式保存,只能够保存字符串,对象用json.stringify转成字符串再保存,不然会就直接调用tostring方法没啥意义
sessionStorage是会话存储,打开一次浏览器算是一次会话,关闭浏览器就没有了。
LocalStorage清除:1.调用clear/removeStorage 2.浏览器设置里面清除缓存
组件自定义事件
绑定自定义事件:
有两种调用:
1.通过$emit
sname是自定义事件名 当事件触发是调用demo函数
App.vue
<template>
<student @sname="demo"></student>
</template>
student.vue
<template>
<div>
<span>{{ studentName }}</span>
<button @click="sendsm">获取学生姓名</button>
</div>
</template>
<script>
export default {
name: "student",
data() {
return {
studentName: "李华",
};
},
methods: {
sendsm() {
this.$emit('sname',this.studentName);//调用函数 $emit触发sname事件,后面就是给sname传的参,可以传无限多个
},
},
};
</script>
2.ref
<script>
export default {
name: 'App',
components: {
school:school,
student:student
},
methods: {
demo(sn){
console.log(sn)
}
},
mounted() {
this.$refs.student.$on("sname",this.demo)//this.refs.student拿到了App上的student子组件 然后通过$on给子组件绑定自定义事件sname,sname触发时调用demo函数
//这里有一个坑 当使用methods里面声明的函数,函数的this一定是methods的那个vue对象
//如果这里写成 这样虽然可以输出 但是app实际上是没有拿到sn的 因为下面这个函数的this指向的是student子组件
//回调函数也是普通的函数,js里面就函数跟对象可以切割作用域。
//回调函数有自己的this 里面保存的就是调用回调函数的对象=》谁调用,回调函数的this就是谁
this.$refs.student.$on("sname",function(sn){
console.log(sn)
this.sname=sn
console.log(this)
})
//改成箭头函数就行
//箭头没有自己的作用域,没有自己的this 所以用mounted的作用域(?用的就是当前组件的作用域,即App
this.$refs.student.$on("sname",(sn)=>{
console.log(sn)
this.sname=sn
console.log(this)
})
},
}
</script>
要加native才会当成原生 默认是自定义事件
解绑:
全局事件总线
能够实现任意组件间通信
我的理解就是在用一个所有人都能看到的中介,也有点像计网里面的总线结构。所有人都在总线(中介)里面做一个自定义事件,a要给b传数据就调用触发b的自定义事件。其实用的也是昨天学的使用自定义事件来实现子传父的那一套。
为了让所有人都能够看见中介使用直接在Vue里面定义。
beforeCreate()这时候还没有初始化完,所以可以在这里放个prototype,来让实例继承$bus。
补充$on:
绑定事件,用法:
this.$bus.$on("checkTodo",this.checkTodo)
消息订阅与发布
订阅者:
mounted(){pubsub.subscribe('hello',function(msgName,data){
console.log('hellllo订阅'+msgName+" "+data)
console.log(this)//undefined
//使用第三方库的话,普通函数的this就是undefined 毕竟第三方库vue也不知道你用的什么
//使用箭头函数就可以跳出调用的 直接出去找this 就是这个vc实例了
})
发布者:
methods: {
sendsm() {
pubsub.publish('hello','6666666666')//消息名 数据
//传的时候传的第一个参数是消息名,第二个参数才是数据..
//就是回调函数收到第一个参数是消息名,第二个才是数据
//改todo案例的时候给我整无语了 一直删不掉 才知道参数收的消息名
},
},
取消订阅:
todo发布:
del(id) {
this.$bus.$emit("delTodo", id);
// pubsub.publish('delTodo',id,'xx');
},
todo订阅:
delTodo(id){//这个参数是消息名 谁狠狠无语了。。
this.todos=this.todos.filter( (todo) =>{
return todo.id!=id
})
nextTick
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
补充Vue的异步更新队列
Vue的更新DOM是异步的。数据变化(比如vm.someData = 'new value'
),Vue不会立即重新渲染组件,执行DOM的更新。而是把这些要更新的事件放入一个队列里面,并进行去重。在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。
当你设置 vm.someData = 'new value'
,该组件不会立即重新渲染。当刷新队列时,组件会在下一个事件循环“tick”中更新。这样回调函数将在 DOM 更新完成后被调用。为了在数据变化之后等待 Vue 完成更新 DOM,可以在数据变化之后立即使用 Vue.nextTick(callback)
<div id="example">{{message}}</div>
var vm = new Vue({
el: '#example',
data: {
message: '123'
}
})
vm.message = 'new message' // 更改数据
vm.$el.textContent === 'new message' // false
Vue.nextTick(function () {
vm.$el.textContent === 'new message' // true
})
组件内使用:
回调函数中的 this
将自动绑定到当前的 Vue 实例上:
<script>
Vue.component('example', {
template: '<span>{{ message }}</span>',
data: function () {
return {
message: '未更新'
}
},
methods: {
updateMessage: function () {
this.message = '已更新'
console.log(this.$el.textContent) // => '未更新'
this.$nextTick(function () {
//这里的this是vc
console.log(this.$el.textContent) // => '已更新'
})
}
}
})
</script>
因为 $nextTick()
返回一个 Promise
对象,所以你可以使用新的 ES2017 async/await语法完成相同的事情:
methods: {
updateMessage: async function () {
this.message = '已更新'
console.log(this.$el.textContent) // => '未更新'
await this.$nextTick()
console.log(this.$el.textContent) // => '已更新'
}
}
总结?
这个是在尚硅谷那个项目里面看到的 顺便看了一下vue的响应式原理 才知道vue的更新是异步的
在组件里发请求,我们希望的是等到返回的数据,然后对数据执行v-for
,但是由于这个vue对dom更新是异步的。可能由于返回太久,先对没有返回的的数据执行v-for
,执行dom更新,对于后面返回的数据无动于衷。使用this.$nextTick
的话就可以把操作排到队伍最后面,等到异步完成了再上。
动画
下面这几种才能用vue的动画
- 条件渲染 (使用
v-if
) - 条件展示 (使用
v-show
) - 动态组件
- 组件根节点
vue的动画用的是transition
的封装组件,就是什么要加上动画效果,就用<transition><\transition>
包起来。
如果有多个组件执行动画:
- 用
v-for
然后用<transition-group><\transition-group>
包起来,而且要执行key
- 用
div
包起来,这样就是还是一个节点
下例(过渡、动画帧):
过渡例子:
<div id="demo">
<button v-on:click="show = !show">
Toggle
</button>
<transition name="fade">
<p v-if="show">hello</p>
</transition>
</div>
.fade-enter-active, .fade-leave-active {
transition: opacity .5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
}
动画帧例子:
<div id="example-2">
<button @click="show = !show">Toggle show</button>
<transition name="bounce">
<p v-if="show">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris facilisis enim libero, at lacinia diam fermentum id. Pellentesque habitant morbi tristique senectus et netus.</p>
</transition>
</div>
.bounce-enter-active {
animation: bounce-in .5s;
}
.bounce-leave-active {
animation: bounce-in .5s reverse;
}
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}
当插入或删除包含在 transition
组件中的元素时,Vue 将会做以下处理:
-
自动嗅探目标元素是否应用了 CSS 过渡或动画,如果是,在恰当的时机添加/删除 CSS 类名。
-
如果过渡组件提供了 JavaScript 钩子函数(
before-enter
,enter
,after-enter
,enter-cancelled
,before-leave
,leave
,after-leave
,leave-cancelled
),这些钩子函数将在恰当的时机被调用。(当只用 JavaScript 过渡的时候,在
enter
和leave
中必须使用done
进行回调。否则,它们将被同步调用,过渡会立即完成。推荐对于仅使用 JavaScript 过渡的元素添加
v-bind:css="false"
,Vue 会跳过 CSS 的检测。这也可以避免过渡过程中 CSS 的影响。) -
如果没有找到 JavaScript 钩子并且也没有检测到 CSS 过渡/动画,DOM 操作 (插入/删除) 在下一帧中立即执行。(注意:此指浏览器逐帧动画机制,和 Vue 的
nextTick
概念不同)
过渡的类名
在进入/离开的过渡中,会有 6 个 class 切换。
v-enter
:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。(我感觉就是插入的那一帧?)v-enter-active
:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。v-enter-to
:定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时v-enter
被移除),在过渡/动画完成之后移除。v-leave
:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。v-leave-active
:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。v-leave-to
:2.1.8 版及以上定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时v-leave
被删除),在过渡/动画完成之后移除。
对于这些在过渡中切换的类名来说,如果你使用一个没有名字的 <transition>
,则 v-
是这些类名的默认前缀。如果你使用了 <transition name="my-transition">
,那么 v-enter
会替换为 my-transition-enter
。
css动画补充:
@keyframes(关键帧)
关键帧 @keyframes
at-rule 规则通过在动画序列中定义关键帧(或waypoints)的样式来控制CSS动画序列中的中间步骤。
@keyframes slidein {
from {
transform: translateX(0%);
}
to {
transform: translateX(100%);
}
}
(mdn)要使用关键帧, 先创建一个带名称的
@keyframes
规则,以便后续使用animation-name
性将动画同其关键帧声明匹配。每个@keyframes
规则包含多个关键帧,也就是一段样式块语句,每个关键帧有一个百分比值作为名称,代表在动画进行中,在哪个阶段触发这个帧所包含的样式。也可以按任意顺序列出关键帧百分比;它们将按照其应该发生的顺序来处理。
(菜鸟教程)创建动画是通过逐步改变从一个CSS样式设定到另一个。
在动画过程中,您可以更改CSS样式的设定多次。
指定的变化时发生时使用%,或关键字"from"和"to",这是和0%到100%相同。
0%是开头动画,100%是当动画完成。
感觉有点像进度条,from
是开始0%处,干什么,然后中间20%、30%干什么,最后是到to
干什么。逐渐从0%相似到100%相似。
重复定义
如果关键帧重名的话,以最后一次定义的为准。一个动画在一个时间只会用一个关键帧。一个关键帧(@keyframes
)里面有多个一样的百分比,比如有两个20%,就将这两个20%的样式重叠。
配置代理(解决跨域问题)
为了解决跨域问题可以配置一个代理服务器
8080端口跟8080代理服务器的话就不算跨域,代理服务器跟真的要传数据的服务器之间也没什么跨域问题(跨域是ajax
浏览器的一个保护措施
这个public就是8080代理服务器的根目录 如果使用第一种方式的话,在里面找到就直接用自己的,就不进行转发了。
一定要写pathRewrite,不然url就会带api,在后端服务器中根本没这个路径,or拿错(。
插槽
默认插槽
在<slot>
<\slot>
的位置插入自定义标签的
app.vue
catagory.vue
具名插槽
允许在自定义标签里面放多个标签 为slot指定名字 然后再放进去
↓已废弃
这种是现在推荐的v-slot写法:但是只支持template标签
作用域插槽
通过给子组件里面的slot插槽绑定数据来传数据给父组件
slot-scope 废弃
推荐 2.6.0 新增的 v-slot。
-
预期:
function argument expression
-
用法:
用于将元素或组件表示为作用域插槽。attribute 的值应该是可以出现在函数签名的参数位置的合法的 JavaScript 表达式。这意味着在支持的环境中,你还可以在表达式中使用 ES2015 解构。它在 2.5.0+ 中替代了
scope
。此 attribute 不支持动态绑定。
-
参考:作用域插槽
scope 移除
被 2.5.0 新增的 slot-scope 取代。推荐 2.6.0 新增的 v-slot。
用于表示一个作为带作用域的插槽的 <template>
元素,它在 2.5.0+ 中被 slot-scope
替代。
-
用法:
除了
scope
只可以用于<template>
元素,其它和slot-scope
都相同。使用案例:
总结![image-20211221192526551](https://img-blog.csdnimg.cn/img_convert/07fe5cecde317df136498af4d4651107.png)
Vue-x
概念
专门在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中的多个组件的共享状态进行集中式的管理(读/写),也是一种组件间的通信方式,且适用于任意组件间通信。
集中式:把所有人集中在一起上课,上课只需要上一遍。
分布式:到每个人的家里面,给每一个人都上一遍课。
适用场景(多个组件要共享统一份数据)
-
多个视图依赖于同一状态。
-
来自不同视图的行为需要变更同一状态。
工作原理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9d4ro2yr-1647068009518)(D:/Typora/20220105180904.png)]
脚手架import机制:
先全局扫描一遍,将import的文件先执行一次
store/index.js
main.js
// 这个文件用于准备Vuex的核心store
import Vue from 'vue'
import Vuex from 'vuex'
// actions响应组件中的动作
const actions={
// 这里context上下文就是那种的ministore 传两个值 一个ministore一个是value
plus:function(context,value){
console.log("actions",context)
// 这样做的就是一个转发
context.commit("plus",value)
}
// plus:function(context,value){
// console.log("actions")
// }
}
// mutations用于操作数据(state)
const mutations={
// this.sum++
plus:function(state,value){
// console.log(state,value)
state.sum++
console.log(state,value)
// console.log("mutations")
}
}
// state储存数据
const state={
sum:0
}
//
Vue.use(Vuex)
export default new Vuex.Store({
actions,
mutations,
state
})
state
__ob__
: Observer这些数据是vue这个框架对数据设置的监控器,一般都是不可枚举的。
网上有很多解决的方案:
第一种:__ob__
: Observer 是 Vue 对数据监控添加的属性,如果想去掉可以用赋值的方式。例如Object.assign({},this.owner)。 用这种方式也是可以解决。
**第二种:**假设list里面存放的就是那些带有__ob__: Observer的可以用JSON.parse(JSON.stringify(this.list))完美解决
完整案例:
score/index.js
// 这个文件用于准备Vuex的核心store
import Vue from 'vue'
import Vuex from 'vuex'
// actions响应组件中的动作
const actions={
// 这里context上下文就是那种的ministore 传两个值 一个ministore一个是value
plus:function(context,value){
// 这样做的就是一个转发
context.commit("plus",value)
},
oddplus:function(context,value){
if(this.state.sum%2){
// 这样做的就是一个转发
context.commit("plus",value)
}
}
}
// mutations用于操作数据(state)
const mutations={
// this.sum++
plus:function(state,value){
// console.log(state,value)
state.sum++
console.log(state,value)
// console.log("mutations")
}
}
// state储存数据
const state={
sum:0
}
//
Vue.use(Vuex)
export default new Vuex.Store({
actions,
mutations,
state
})
count.vue
<template>
<div>
<span>计算结果:{{$store.state.sum}}</span>
<button @click="plus">+1</button>
<button @click="oddplus">奇数+1</button>
</div>
</template>
<script>
export default {
name:"count",
data() {
return {
// sum:0,
value:0
}
},
methods: {
plus(){
this.$store.dispatch('plus',2)
// console.log(this.$store)
},
oddplus(){
this.$store.dispatch('oddplus',2)
}
},
}
</script>
<style>
</style>
<template>
<div id="app">
<count></count>
</div>
</template>
<script>
import count from './components/count'
export default {
name: 'App',
components: {
count
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
actions
muation
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。
Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的事件类型 (type)和一个回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数
mutation 必须是同步函数。
mutations: {
someMutation (state) {
api.callAsyncMethod(() => {
state.count++
})
}
}
devtools需要记录mutation。然后如果mutation里面的函数是异步的话,不能控制异步函数什么时候做回调,也追踪不了回调函数里面的状态的改变,所以devtools根本就记录不了mutation了。
state
vuex使用单一状态树,用一个对象就包含了全部的应用层级状态,作为一个“唯一数据源 (SSOT)”而存在。整个应用只有一个store实例。
state作为构造器选项,定义了所有我们需要的基本状态参数。
getters
![image-20220107191416091](https://gitee.com/inotfine/typora-pics/raw/master/imgs//image-20220107191416091.png)
map(4个)
记得要在vuex里面进行引入
mapState
就是从state里面取出属性来当计算属性的
数组写法:
computed:{
//意思是将vuex中的city数据映射到组件的computed属性里
...mapState(["city"])
}
对象写法:
意思是将store里面的state里面的city属性映射到computed属性中的currentCity中(map本身就有映射的意思)。即currentCity代表了$store.state.city
computed:{
...mapState({
currentCity:"city"
})
}
总结
computed: mapState({
// 第一种写法
count: 'count',
// 第二种写法
sex: (state) => state.sex,
// 用普通函数this指向vc组件,要注意
from: function (state) {
return this.str + ':' + state.from
},
// 注意下面的写法看起来和上面相同,事实上箭头函数的this指针并没有指向vue实例(this是undefined 应该是es6用的严格模式 所以顶层的this是undefined的原因),因此不要滥用箭头函数
from: (state) => this.str + ':' + state.from
myCmpted: function () {
// 这里不需要state,测试一下computed的原有用法
return '测试' + this.str
}
}),
写成函数的都会在用那个属性的时候 函数会调用一下 我感觉是计算属性的原因
mapGetters![image-20220107204059878](https://img-blog.csdnimg.cn/img_convert/972e80ca35032b7b903e018a6a29ac27.png)
mapMutations
要是不传直接写函数名字的话 会默认传一个event 这样就计算不了
<button @click="increment">+1</button>
我传的是pointerevent
mapActions
小总结
其实就是跟谁交流就map谁 map其实还有一个映射的意思捏
模块化编码
分块暴露
里面记得写一个namespaced用以区分 让外面的人认识它!!注意是namespaced
![image-20220107230137019](https://gitee.com/inotfine/typora-pics/raw/master/imgs//image-20220107230137019.png)
路由
基本概念
通过切换页面内的部分组件 实现单页面部分内容(组件)的一个切换
使用方法(Vue2)
路由是vue的一个插件 app.use(router)
路由配置:
router/index.js
import VueRouter from 'vue-router'
import change from '../pages/change'
import home from '../pages/home'
import next from '../pages/next'
//这里就是路由配置
const router=new VueRouter({
//这里写的就是路由规则
routes:[
{
path:'/change',
component:change
},
{
path:'/home',
// name:'home',
component:home,
//嵌套了多级路由
children:[{
path:'next',
component:next,
}]
}
]
})
export default router
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push (location) {
return originalPush.call(this, location).catch(err => err)
}
模板
router-link
这个是放url来进行跳转的,放在模板里面 最后经过vue-router库的编译会变成a标签。这使得 Vue Router 可以在不重新加载页面的情况下更改 URL。
active-class 点击激活里面配置的css样式
一级路由的写法:
多级路由:
router-view
这个用于指定组件的呈现位置。放在那里就是哪块组件进行变化。
路由注意点
$router
、$route
1.文档
通过调用
app.use(router)
,我们可以在任意组件中以this.$router
的形式访问它,并且以this.$route
的形式访问当前路由。
所以可以this. r o u t e r 是 全 局 的 路 由 , 每 一 个 组 件 都 能 访 问 到 它 , t h i s . router是全局的路由,每一个组件都能访问到它,this. router是全局的路由,每一个组件都能访问到它,this.route是当前组件的路由。
我的理解:
$router
:全局路由的实例,是router构造方法的实例,用于编程式路由导航进行的路由跳转
$route
:当前的路由信息对象
$router
里面有全局的路由什么的 还有history、mode什么的,而且还能看到路由规则
route
我理解成当前的路由对象(也不是页面,就是当前的路由对象)
2.注册完路由组件之后,不管是路由、路由组件、非路由组件上都有$router
、$route
属性
两者之间的联系this. router中的currentRoute(其实就是当前路由、、)就是组件路由的this.route
ajax相关参数
路由的query参数
跳转路由并携带query参数 to的字符串写法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eryYzsk3-1647068009545)(D:/Typora/image-20220111231355329.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Iu14Ohn2-1647068009546)(D:/Typora/image-20220111231635073.png)]
params参数
如果使用params必须使用name 不能使用path 因为这个path已经找不到了。原路径预留的path有占位符:
(路径参数)。
当一个路由被匹配时,它的 params 的值将在每个组件中以 this.$route.params
的形式暴露出来。
拿这个过去匹配不到
模板
路由命名
一个配置项命一个名
原来的path改成name也可以正常使用 必须要写成对象然后配置name 写成字符串就是默认是路径了
props
以props的形式组件间传参数
params版
query版:
router-link(声明式路由导航)
push
有点像数组那种 一层一层追加再一层一层退出 默认就是push模式
replace
有点像无痕浏览 是采用取代 历史记录只有一条
![image-20220112220712468](https://gitee.com/inotfine/typora-pics/raw/master/imgs//image-20220112220712468.png)
编程式路由导航(不借助routerlink的路由导航)
不使用router-link的路由导航,用this.$router
使用路由器对象来进行路由的切换
// 字符串路径
router.push('/users/eduardo')
// 带有路径的对象
router.push({ path: '/users/eduardo' })
// 命名的路由,并加上参数,让路由建立 url params参数地址栏不显示参数
router.push({ name: 'user', params: { username: 'eduardo' } })
// 带查询参数,结果是 /register?plan=private
router.push({ path: '/register', query: { plan: 'private' } })
// 带 hash,结果是 /about#team
router.push({ path: '/about', hash: '#team' })
负数是后退 正数是前进 我感觉是读栈里面 前进就是往栈尾走 后退就是往后走(。 说一些废话
下面是后退两步(连点两下后退
面试题:
1.路由传递参数(对象写法) path是否可以结合params参数一起使用?
不可以 会报错 params和name一起用
this.$router.push({ path: '/Search/', params: { keywords:this.keywords } })
Uncaught (in promise) NavigationDuplicated: Avoided redundant navigation to current location: “/Search/”.
2.如何指定params参数可传可不传
3.params传空串
我的url有点问题 明明已经跳转到login了 但是还是显示这个
ps:路由规则是这样的
{
path: "/Login/:keywords",
component:Login ,
name:"Login",
meta:{
showFooter:"false"
}
},
4.路由能不能使用props
可以,
1)布尔值
在接收的路由组件路由规则上加props
{
path: "/Login/:keywords",
component:Login ,
name:"Login",
meta:{
showFooter:"false"
},
//传布尔值
props:true
//传对象
props:{keywords:"1111111111"}
//传函数
props:(route)=>({params:route.params,a:1,b:2})}
},
然后照常使用就ok
<script>
export default {
name:"Login",
props:["keywords"]
}
</script>
解决 错误NavigationDuplicated: Avoided redundant navigation to current location
这个错误主要是已经在这个页面然后路由又要转到这个页面导致的
goSearch() {
console.log("goSearch");
let re = this.$router.push(
{ name: "Login", params: { keywords: this.keywords } }
// ,
// () => {
// // console.log("yes");
// },
// () => {
// // console.log("no");
// }
);
console.log(re);//Promise对象
},
goSearch() {
console.log("goSearch");
let re = this.$router.push(
{ name: "Login", params: { keywords: this.keywords } }
,
() => {
console.log("yes");
return 1
},
() => {
console.log("no");
}
);
console.log(re);//undefined
},
不知道为什么会这样 加了两个函数返回的变成undefined了…
重写push、replace
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push (location) {
return originalPush.call(this, location).catch(err => err)
}
缓存路由组件
keep-alive
include里面填的组件名
home.vue
<router-link active-class="active" to="/home/next">next</router-link>
<keep-alive>
<router-view></router-view>//缓存的是跳转的那个next组件
</keep-alive>
路由组件独有的生命周期钩子
next.vue
<script>
export default {
name:'next',
beforeDestroy() {
console.log("next被销毁了")
},
activated() {
console.log("home actttttt")
},
deactivated() {
console.log("home deactttt")
},
}
</script>
重定向
通过配置route
router/index.js
{
// 重定向 访问/的时候,让它定位到
path:'*',
redirect:'/home'
}
也可以写别的path 下面例子是从 /a
重定向到 /b
const router = new VueRouter({
routes: [
{ path: '/a', redirect: '/b' }
]
})
甚至是一个方法,动态返回重定向目标:
const router = new VueRouter({
routes: [
{ path: '/a', redirect: to => {
// 方法接收 目标路由 作为参数
// return 重定向的 字符串路径/路径对象
}}
]
})
注意导航守卫并没有应用在跳转路由上,而仅仅应用在其目标上。在下面这个例子中,为 /a
路由添加一个 beforeEnter
守卫并不会有任何效果。(这个写法没有看过 下次遇到写写看
路由守卫
路由前置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rw6CvKZC-1647068009576)(D:/Typora/image-20220113021733886.png)]
meta元数据(就是所谓的路由元信息了) 配置对象里面的不能随便加属性的,自己在配置对象里面加的属性是读不到的 ,会被无视掉 要放属性就放到meta里面
我们称呼 routes
配置(就是router/index.js 暴露的那个全局路由)中的每个路由对象为 路由记录。路由记录可以是嵌套的,因此,当一个路由匹配成功后,它可能匹配多个路由记录。
例如,根据上面的路由配置,/posts/new
这个 URL 将会匹配父路由记录 (path: '/posts'
) 以及子路由记录 (path: 'new'
)。
一个路由匹配到的所有路由记录会暴露为 $route
对象(还有在导航守卫中的路由对象)的$route.matched
数组。我们需要遍历这个数组来检查路由记录中的 meta
字段,但是 Vue Router 还为你提供了一个 $route.meta
方法,它是一个非递归合并所有 meta
字段的(从父字段到子字段)的方法。这意味着你可以简单地写
router.beforeEach((to, from) => {
// 而不是去检查每条路由记录
// to.matched.some(record => record.meta.requiresAuth)
if (to.meta.requiresAuth && !auth.isLoggedIn()) {
// 此路由需要授权,请检查是否已登录
// 如果没有,则重定向到登录页面
return {
path: '/login',
// 保存我们所在的位置,以便以后再来
query: { redirect: to.fullPath },
}
}
})
路由后置
独享路由守卫(只有前置没有后置)
只对某一个组件进行限制
组件内路由守卫
通过路由规则,进入跟退出分别调用beforeRouterEnter
,beforeRouterLeave
。
进入组件跟离开组件与之前的前置/后置路由守卫完全不一样。前置、后置路由守卫是有一点点像钩子,自动启动,然后我觉得组件内路由守卫更像我本人,推一下动一下。。
点了button路由进入home组件才启动,离开home组件 切换才启动
又及 不通过路由规则 直接展示路由组件是不会触发beforeRouterEnter
,beforeRouterLeave
的
eg
直接放home组件没有触发 由于有初始化还是触发了前置、后置路由守卫(beforeEach、afterEach)
总结:
路由器的两种工作模式
可以使用nignx/node的一个中间件完成匹配