vue集合
介绍
vue是饿了么团队开发并维护的一个渐进式框架,是一个基于mvvm的框架,一般开发使用vue-cli来构建vue的项目(可以使用npm/yarn来进行模块化开发)
v-if 和 v-show 区别
v-show
通过css
中的display
来控制显示和隐藏(不占位);v-if
操作的是元素的创建和插入,组件是真正的渲染和销毁,而不是显示和隐藏;- 当频繁切换显示状态时,用
v-show
,否则用v-if
。
v-if 是真正的条件渲染,因为它会确保在切换的过程中条件块内的事件监听器和子组件适当的被销毁或重建,v-if 也是惰性的:如果在初始渲染时为假,那么它什么也不做,直到第一次变为真时,才会开始渲染条件块v-show:前台数据的展示
v-if:管理系统权限列表的展示
v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
(1)必须使用 key ,且不能是 index 和 random ;原因在于,在 vue 的 diff 算法中,通过对 tag 和 key 来判断是否为相同节点 ,如果是相同节点,则会尽可能的复用原有的 DOM 节点。
使用 key 的好处是:减少渲染次数,提升渲染性能
v-for 遍历必须为 item 添加 key
在列表数据进行遍历渲染时,需要为每一项 item 设置唯一 key 值,方便 Vue.js 内部机制精准找到该条列表数据。当 state 更新时,新的状态值和旧的状态值对比,较快地定位到 diff 。
(2)
v-for 遍历避免同时使用 v-if 【永远不要把 v-if 和 v-for 同时用在同一个元素上。】
v-for 比 v-if 优先级高,如果每一次都需要遍历整个数组,将会影响速度,尤其是当之需要渲染很小一部分的时候,必要情况下应该替换成 computed 属性。
组件通信
一、
父组件向子组件传值
:父组件通过绑定自定义属性,子组件接收通过props 进行接收
- 传递的属性不能出现大写。需要用分割。
- 大写转为小写、从父级接收过来的数据是不允许直接修改。
- 接收过来的属性名,不允许与当前组件的数据名(data属性名)相同)
① 父组件通过 v-bind: 属性绑定的形式,把数据传递给子组件
② 子组件中,通过 props 接收父组件传递过来的数据
二、
子组件向父组件传值
:子父(向上)传值:通过自定义事件、子组件通过$eimt发送事件传递呼叫给父组件
- 子组件中声明自定义事件、触发事件
- 父组件使用
① 在子组件中声明 emits 自定义事件,格式为 update:xxx
② 调用 $emit() 触发自定义事件,更新父组件中的数据
三、
非父子组件传值 - 兄弟组件
:创建公共的Vue 实例对象——EventBus 发布者和订阅者的关系是一对多的 给vue原型属性中增加一个方法叫$bus,值是一个vue实例
安装 mitt 依赖包
npm install mitt
四、Vue高级组件之provide/inject【后代关系组件】
以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效,这成为了跨组件通信的基础1 父节点通过 provide 共享数据
- 父节点的组件可以通过 provide 方法,对其子孙组件共享数据
2.子孙节点通过 inject 接收数据
- 子孙节点可以使用 inject 数组,接收父级节点向下共享的数据。
3.父节点对外共享响应式的数据
- 父节点使用 provide 向下共享数据时,可以结合 computed 函数向下共享响应式的数据。
4.子孙节点使用响应式的数据
- 如果父级节点共享的是响应式的数据,则子孙节点必须以 .value 的形式进行使用。
//父组件
export default {
name: 'MyApp',
data() {
return {
color: 'red',//定义父节点组件向子孙组件共享的数据
}
},
provide() {
return {
color: this.color
}
},
}
//子组件
<template>
<div>
<h5> --- {{ color }} </h5>
</div>
</template>
<script>
export default {
inject: ['color'],//子孙组件接受父节点组件向下传递的数据
}
</script>
//结合 computed 函数向下共享响应式的数据
import { computed } from 'vue'//按需引入
export default {
data() {
return {
color: 'red',
}
},
provide() {
// 返回要共享的数据对象
return {
color: computed(() => this.color),
}
},
}
//
<template>
<div>
<h5>--- {{ color.value }}</h5>
</div>
</template>
<script>
export default {
inject: ['color'],//接收父节点传递的数据并在页面上使用
}
</script>
ref
使用ref获取DOM元素
<template>
<div>
//使用ref属性,为对应的DOM元素添加引用名称
<h1 ref="myh11">App 根组件</h1>
<hr />
<button type="button" @click="getRefs">获取 $refs 引用</button>
</div>
</template>
methods: {
getRefs() {
//通过this.$refs.引用名称可以获取到DOM元素
console.log(this.$refs)
//操作DOM元素将文本颜色修改
this.$refs.myh11.style.color = 'red'
},
},
使用ref获取组件的引用
//APP组件
<template>
<div>
<my-counter ref="counterRef"></my-counter>
</div>
</template>
methods: {
getRefs() {
console.log(this.$refs.counterRef)
//通过ref操作组件实例,重置
this.$refs.counterRef.reset()
}
},
//MyCounter组件
<template>
<div class="counter-container">
<h3>Counter 组件 --- {{ count }}</h3>
<hr />
<button type="button" class="btn btn-info" @click="count += 1">+1</button>
</div>
</template>
export default {
name: 'MyCounter',
data() {
return {
count: 0,
}
},
methods: {
reset() {
this.count = 0
},
},
}
this.$nextTick
组件的 $nextTick(cb) 方法,会把 cb 回调推迟到下一个 DOM 更新周期之后执行。通俗的理解是:等组件的DOM 异步地重新渲染完成后,再执行 cb 回调函数。从而能保证 cb 回调函数可以操作到最新的 DOM 元素。
动态组件
- 动态组件:指的是动态切换组件的显示与隐藏。vue 提供了一个内置的
<component>
组件,专门用来实现组件
的动态渲染。
①<component>
是组件的占位符
② 通过 is 属性动态指定要渲染的组件名称
③<component is="要渲染的组件的名称"></component>
- 使用 keep-alive 保持状态
默认情况下,切换动态组件时无法保持组件的状态。此时可以使用 vue 内置的 组件保持动态组件的状态。示例代码如下:
<keep-alive>
<component :is="comName"></component>
</keep-alive>
keep-alive的作用是什么?
原理:在created的时候,将需要缓存的虚拟dom节点放到cache中,在render的时候,根据name再进行取出
在vue项目中,如果需要点击列表页跳转到详情页,返回列表页时,如果不对列表页进行缓存,那么返回来的时候会回到初始状态,这时候就可以使用keep-alive ,keep-alive 是vue中的一个内置组件 ,可以使 被包含的组件保留状态,或避免被重新渲染,keep-alive的用法也很简单,但是需要与activated和deactivated配合使用。当在这些组件之间切换的时候,想保持这些组件的状态,以避免反复渲染导致的性能问题 。说白了,可以让不活动的组件活着,a-b b-a a页面不会再次进行数据请求,直接从缓存里面获取。减少性能消耗
提供了两个属性:允许组件有条件的缓存include/exclude
<keep-alive>
<!--<keep-alive exclude="recommend,singer">-->
<router-view>
<!-- 所有路径匹配到的视图组件都会被缓存! -->
</router-view>
</keep-alive>
插槽 - slot
slot是什么?如何使用
- 具名插槽
描述:如果在封装组件时需要预留多个插槽节点,则需要为每个
<slot>
插槽指定具体的 name 名称。这种带有具体名称的插槽叫做“具名插槽”。
- 在向具名插槽提供内容的时候,我们可以在一个
<template>
元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称。
//具名插槽
//没有指定 name 名称的插槽,会有隐含的名称叫做 “default”
<template #default>
<p>默认</p>
</template>
<div class="info">
<slot name="header"></slot>
<slot></slot> //没有指定name的插槽,提供的内容都会被渲染到名字为 default 的插槽之中
<slot name="footer" say="hello"></slot>
</div>
//将p标签内容渲染
<template v-slot = header> //简写#
<p>头部</p>
</template>
- 作用域插槽
描述:在封装组件的过程中,可以为预留的
<slot>
插槽绑定 props 数据,这种带有 props 数据的<slot>
叫做“作用域插槽”。
//父组件
<my-test>
<template #default="{ msg, info }">
<p>{{ msg }}</p> //abc
<p>{{ info.address }}</p> //中国北京
</template>
</my-test>
//子组件
<template>
<div>
<h3>这是 TEST 组件</h3>
<slot :info="infomation" :msg="message"></slot>
</div>
</template>
<script>
export default {
name: 'MyTest',
data() {
return {
// 信息数据
infomation: {
phone: '138xxxx6666',
address: '中国北京',
},
message: 'abc'
}
},
}
</script>
自定义指令
vue 中的自定义指令分为两类,分别是:
- 私有自定义指令
- 全局自定义指令
注意
:在 vue2 的项目中使用自定义指令时,【 mounted -> bind 】【 updated -> update 】
//在使用自定义指令时,需要加上 v- 前缀
<input type="text" class="form-control" v-focus />
//在每个 vue 组件中,可以在 directives 节点下声明私有自定义指令。
directives: {
//
},
const app = createApp(App)
// 声明全局自定义指令
//注册一个全局自定义指令"v-focus"
/* app.directive('focus', {
mounted(el) {//第一次插入DOM时触发
console.log('mounted')
el.focus()
},
updated(el) {//每次DOM更新时都会触发
console.log('updated')
el.focus()
},
*/
//在mounted和undated都会触发相同业务时触发,可以简写
app.directive('focus', (el) => {
el.focus()
})
<template>
<div class="home-container">
//在使用v-color指令时,可以通过“等号” 绑定指令的值
<input type="text" class="form-control" v-focus v-color="'cyan'" />
<button type="button" class="btn btn-primary" @click="count += 1">+1</button>
</div>
</template>
//自定义v-color指令
app.directive('color', (el, binding) => {
//binding.value 就是通过“等号” 为指令绑定的值
el.style.color = binding.value
})
计算属性的使用注意点
① 计算属性必须定义在 computed 节点中
② 计算属性必须是一个 function 函数
③ 计算属性必须有返回值
④ 计算属性必须当做普通属性使用Vue computed 实现
建立与其他属性(如:data、 Store)的联系;属性改变后,通知计算属性重新计算
实现时,主要如下
- 初始化 data, 使用 Object.defineProperty 把这些属性全部转为 getter/setter。
初始化 computed, 遍历 computed 里的每个属性,每个 computed 属性都是一个 watch 实例。
每个属性提供的函数作为属性的 getter,使用 Object.defineProperty 转化。
Object.defineProperty getter 依赖收集。用于依赖发生变化时,触发属性重新计算。
若出现当前 computed 计算属性嵌套其他 computed 计算属性时,先进行其他的依赖收集
计算属性和侦听器
- 计算属性和侦听器侧重的应用场景不同:
计算属性侧重于监听多个值的变化,最终计算并返回一个新值
侦听器侧重于监听单个数据的变化,最终执行特定的业务处理,不需要有任何返回值
methods、computed、watch三者的区别
三者之间的共同点:
methods,watch和computed都是以函数为基础的
computed与watch都是以Vue的依赖为基础,当所依赖的数据发生变化的时候,会触发相关的函数去实现数据的变动。methods里面是用来定义函数的,需要手动才能执行,不像computed与watch"自动执行"函数
三者之间的不同点:
computed:
1、computed:是一个计算属性,当computed 所依赖的属性发生变化时,计算属性才会重新计算,并进行
缓存
。当其他数据发生改变的时候computed 属性不会重新计算。从而提升性能 只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值。
watch:
2、watch:观察,类似于某些数据的监听回调
- 默认情况下,组件在初次加载完毕后不会调用 watch 侦听器,只有依赖的属性发生变化才会执行。。如果想让 watch 侦听器立即被调用,则需要使用 immediate 选项。
通过声明immediate 选项为 true,
可以立即执行一次 ,watch 用在监听数据变化,给后台发数据请求 ,watch中的handler默认只能监听属性引用的变化
,但内部属性是监听不到的 。设置deep为true
可以进行深度监听,但是性能开销也会变大 ,watch无法监听数组值(特殊情况下)的变化
当 watch 侦听的是一个对象,如果对象中的属性值发生了变化,则无法被监听到。此时需要使用 deep 选项,
methods
3、
methods计算的结果无法被缓存
,相对于computed来说,计算属性会缓存计算的结果,只有计算属性的依赖项发生变化时,才会重新进行运算。因此计算属性的性能更好
watch与computed使用的场景
1、watch:一个数据影响多个数据
- 例如:网络请求 模糊查询 浏览器自适应 监控路由对象
当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
2、computed:一个数据受多个数据的影响
- 例如:商品购物车的结算 过滤某些商品数据
当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
watch无法监听数组的情况,以及解决方案
- 无法监听数组的情况
利用索引直接设置一个数组项时,例如:arr[indexOfItem] = newValue;
修改数组的长度时,例如:arr.length = newLength;
解决方案
1.this.$set(arr, index, newVal);
2.使用数组 splice 方法
vue生命周期v2
概念: Vue 实例从创建到销毁的过程,从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、销毁等一系列过程,称之为 Vue 的生命周期。
- beforeCreate
在实例初始化之后,组件实例刚刚被创建(el 和data 并未初始化) , 数据观测(data observer) 和 event/watcher 事件配置之前被调用,vue实例的挂载元素el和数据对象data都为undefined,还未初始化。- created
页面还没有渲染,但是vue的实例已经初始化结束。在created阶段,vue实例的数据对象data有了,el还没有(在 beforeCreate 钩子函数调用的时候,是获取不到 props 或者 data 中的数据的,因为这些数据的初始化都在 initState 中。然后会执行created钩子函数,在这一步的时候已经可以访问到之前不能访问到的数据,但是这时候组件还没被挂载,所以是看不到的。) 在created中进行的dom操作一定要放在vue.nextTick()的回调函数中- beforeMount
在beforeMount阶段,vue实例的$el
和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换 。- mounted
vue实例挂载完成,data.message成功渲染,页面已经渲染完毕(接下来会先执行 beforeMount 钩子函数,开始创建 VDOM,最后执行 mounted 钩子,并将 VDOM 渲染为真实 DOM 并且渲染数据。组件中如果有子组件的话,会递归挂载子组件,只有当所有子组件全部挂载完毕,才会执行根组件的挂载钩子)【注意:mounted 不会保证所有的子组件也都被挂载完成,如果需要等到视图都渲染完成之后执行某些操作的话,可以在mounted 内部使用$nextTick,【该钩子在服务器端渲染期间不被调用】- beforeUpdate
数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。- updated
由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。
- 另外还有 keep-alive 独有的生命周期,分别为 activated 和 deactivated 。用 keep-alive 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 deactivated 钩子函数,缓存渲染后会执行 actived 钩子函数。)
- beforeDestroy
实例销毁之前调用。在这一步,实例仍然完全可用。常用场景有: 自定义事件的绑定要解除、setTimeout等定时任务需要销毁、自己绑定的window或者document事件需要销毁- destroyed
Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁
- activated
keep-alive 组件激活时调用。- deactivated
keep-alive 组件停用时调用 ( 默认情况下,切换动态组件时无法保持组件的状态。此时可以使用 vue 内置的 组件保持动态组件的状态。)
//keep-alive 会把内部的组件进行缓存,而不是销毁组件
//在使用 keep-alive 的时候,可以通过 include 指定哪些组件需要被缓存;或者,
//通过 exclude 属性指定哪些组件不需要被缓存;但是:不要同时使用 include 和 exclude 这两个属性
//is 属性的值,表示要渲染的组件的名字
//is 属性的值,应该是组件在 components 节点下的注册名称
<keep-alive exclude="MyRight">
<component :is="comName"></component>
</keep-alive>
生命周期钩子v3
组件运行的过程
组件的生命周期指的是:组件从创建 -> 运行(渲染) -> 销毁的整个过程,强调的是一个时间段。
如何监听组件的不同时刻
vue 框架为组件内置了不同时刻的生命周期函数,生命周期函数会伴随着组件的运行而自动调用。例如:
① 当组件在内存中被创建完毕之后,会自动调用 created 函数
② 当组件被成功的渲染到页面上之后,会自动调用 mounted 函数
③ 当组件被销毁完毕之后,会自动调用 unmounted 函数
如何监听组件的更新
当组件的 data 数据更新之后,vue 会自动重新渲染组件的 DOM 结构,从而保证 View 视图展示的数据和Model 数据源保持一致。当组件被重新渲染完毕之后,会自动调用 updated 生命周期函数。
组件中主要的生命周期函数
- created 组件在内存中创建完毕后、 创建阶段 、唯一1次 、发 ajax 请求初始数据
- mounted 组件初次在页面中渲染完毕后 、创建阶段、 唯一1次、 操作 DOM 元素
- updated 组件在页面中被重新渲染完毕后、 运行阶段 、0 或 多次
- unmounted 组件被销毁后(页面和内存)、 销毁阶段、 唯一1次
注意
:在实际开发中,created 是最常用的生命周期函数!
- 组件中全部的生命周期函数
生命周期函数 - 执行时机 - 所属阶段 - 执行次数 - 应用场景
- beforeCreate 在内存中开始创建组件之前 、创建阶段 、唯一1次
- created 组件在内存中创建完毕后、 创建阶段、 唯一1次 、发 ajax 请求初始数据
- beforeMount 在把组件初次渲染到页面之前 、创建阶段 、唯一1次
- mounted 组件初次在页面中渲染完毕后 、创建阶段 、唯一1次、 操作 DOM 元素
- beforeUpdate 在组件被重新渲染之前 、运行阶段、 0 或 多次
- updated 组件在页面中被重新渲染完毕后、 运行阶段、0 或 多次
- beforeUnmount 在组件被销毁之前 、销毁阶段、 唯一1次
- unmounted 组件被销毁后(页面和内存) 、销毁阶段、 唯一1次
疑问
:为什么不在 beforeCreate 中发 ajax 请求初始数据?
- 完整的生命周期图示
可以参考 vue 官方文档给出的“生命周期图示”,进一步理解组件生命周期执行的过程:
https://vuejs.org/guide/essentials/lifecycle.html#lifecycle-diagram
Vue在哪个生命周期内调用异步请求?
可以在钩子函数created、beforeMount、mounted中进行调用,因为在这三个钩子函数中,data已经创建,可以将服务器端返回的数据进行赋值,但是我推荐在created钩子函数中调用异步请求,因为能更快获取到服务器端的数据,减少页面加载时间
- ssr不支持beforeMount和mounted钩子函数,所以放在created中有助于一致性。
vue 中 ajax 请求代码应该写在组件的 methods 中还是
如果请求来的数据不是要被其他组件公用,仅仅在请求的组件内使用,就不需要放入 vuex 的 state 里
如果被其他地方复用,请将请求放入 action 里,方便复用,并包装成 promise 返回
nextTick
swiper插件从后台获取数据没问题,css代码啥的也没有问题,但是图片没动,怎么解决?
主要原因是swiper提前初始化了,但是数据还没有完全出来。
this.$nextTick()
主要作用是等数据改变引发dom重新渲染完成之后才会执行
this.$nextTick() 主要作用是等数据改变引发dom重新渲染完成之后才会执行
<div class="swiper-container index-banner">
<div class="swiper-wrapper">
<div class="swiper-slide"
v-for="item in banners"
:key="item.id"
>
<img :src="getImages(item.images.small)" alt="">
</div>
</div>
</div>
getBanners(){
axios.get("/db/v2/movie/in_theaters",{
params:{
count:5
}
}).then(res=>{
this.banners = res.data.subjects;
this.$nextTick(()=>{
new Swiper(".swiper-container",{
loop:true
})
});
})
}
observer:true,
observeParents:true//修改swiper子元素的话,会自动帮助我们初始化swiper
如果想要从后台请求图片放上去 new Swiper要写在网络请求成功的函数里面,否则不会出来数据
router
- 什么是路由 - 通俗易懂的概念:
Hash 地址与组件之间的对应关系。
路由(英文:router)就是对应关系。路由分为两大类:
① 后端路由
② 前端路由
后端路由
后端路由指的是:请求方式、请求地址与 function 处理函数之间的对应关系。在 node.js 课程中,express路由的基本用法如下:
- SPA 与前端路由
SPA 指的是一个 web 网站只有唯一的一个 HTML 页面,所有组件的展示与切换都在这唯一的一个页面内完成。此时,不同组件之间的切换需要通过前端路由来实现。
结论:在 SPA 项目中,不同功能之间的切换,要依赖于前端路由来完成!
- 前端路由的工作方式
① 用户点击了页面上的路由链接
② 导致了 URL 地址栏中的 Hash 值发生了变化
③ 前端路由监听了到 Hash 地址的变化
④ 前端路由把当前 Hash 地址对应的组件渲染都浏览器中
实现简单的前端路由
步骤1:导入并注册 MyHome、MyMovie、MyAbout 三个组件。示例代码如下:
步骤2:通过 标签的 is 属性,动态切换要显示的组件。示例代码如下:
步骤3:在组件的结构中声明如下 3 个<a>
链接,通过点击不同的<a>
链接,切换浏览器地址栏中的 Hash 值:
步骤4:在 created 生命周期函数中监听浏览器地址栏中 Hash 地址的变化,动态切换要展示的组件的名称:
vue-router 4.x 的基本使用步骤
① 在项目中安装 vue-router
② 定义路由组件
③ 声明路由链接和占位符
④ 创建路由模块
⑤ 导入并挂载路由模块
- 在 vue3 的项目中,只能安装并使用 vue-router 4.x。安装的命令如下:
npm installvue-router@next -S- 定义路由组件
在项目中定义 MyHome.vue、MyMovie.vue、MyAbout.vue 三个组件,将来要使用 vue-router 来控制它们的展示与切换:
- 声明路由链接和占位符
可以使用<router-link>
标签来声明路由链接,并使用<router-view>
标签来声明路由占位符。示例代码如下:- 创建路由模块
在项目中创建 router.js 路由模块,在其中按照如下 4 个步骤创建并得到路由的实例对象:
① 从 vue-router 中按需导入两个方法
② 导入需要使用路由控制的组件
③ 创建路由实例对象
④ 向外共享路由实例对象
⑤ 在 main.js 中导入并挂载路由模块- 从 vue-router 中按需导入两个方法
import { createRouter, createWebHashHistory } from 'vue-router'
- 创建路由实例对象
路由重定向
{ path: ‘/’, redirect: ‘/home’ },//path:原地址;redirect:新地址路由高亮
可以通过如下的两种方式,将激活的路由链接进行高亮显示:
- ① 使用默认的高亮 class 类
- 被激活的路由链接,默认会应用一个叫做 router-link-active 的类名。开发者可以使用此类名选择器,为激活的路由链接设置高亮的样式:
//在index.css全局样式表种,重新设置router-link-active的样式
.router-link-active {
background-color: red;
…
}- ② 自定义路由高亮的 class 类
- 在创建路由的实例对象时,开发者可以基于 linkActiveClass 属性,自定义路由链接被激活时所应用的类名:
//指定被激活的路由链接,会应用router-active这个类名(默认的会被覆盖)
linkActiveClass: ‘active-router’,
嵌套路由
通过 children 属性声明子路由规则
动态路由
指的是:把 Hash 地址中可变的部分定义为参数项,从而提高路由规则的复用性。
// 动态路由匹配 - 路由中的动态参数以 :进行声明,冒号后面是动态参数的名称- { path: ‘/movie/:id’, component: Movie },
路由传参
$route.params
参数对象
通过动态路由匹配的方式渲染出来的组件中,可以使用 $route.params 对象访问到动态匹配的参数值。
<h3>MyMovie 组件 --- {{ $route.params.mid }} </h3>
使用 props
接收路由参数
为了简化路由参数的获取形式,vue-router 允许在路由规则中开启 props 传参。示例代码如下:
导航
- 编程式导航
通过调用 API 实现导航的方式,叫做编程式导航。与之对应的,通过点击链接实现导航的方式,叫做声明式导航。例如:
- 普通网页中点击
<a>
链接、vue 项目中点击<router-link>
都属于声明式导航- 普通网页中调用 location.href 跳转到新页面的方式,属于编程式导航
- vue-router 中的编程式导航 API
$router.push
调用 this.$router.push() 方法,可以跳转到指定的 hash 地址,从而展示对应的组件页面。$router.go
调用 this.$router.go() 方法,可以在浏览历史中进行前进和后退。- 命名路由
通过 name 属性为路由规则定义名称的方式,叫做命名路由。
注意:命名路由的 name 值不能重复,必须保证唯一性!
- 使用命名路由实现
声明式导航
为<router-link>
标签动态绑定 to 属性的值,并通过 name 属性指定要跳转到的路由规则。期间还可以用params 属性指定跳转期间要携带的路由参数。
<router-link :to="{ name: 'mov', params: { mid: 2 } }">...</router-link>
- 使用命名路由实现
编程式导航
调用 push 函数期间指定一个配置对象,name 是要跳转到的路由规则、params 是携带的路由参数:
this.$router.push({ name: ‘mov’, params: { mid: id } })
<template>
<div>
<h3>MyHome 组件</h3>
<button type="button" class="btn btn-primary" @click="goToMovie(3)">导航到Movie页面</button>
</div>
</template>
<script>
export default {
name: 'MyHome',
methods: {
goToMovie(id) {
this.$router.push('/movie/' + id)
},
//goBack() {//后退
//this.$router.go(-1)
//},
},
}
</script>
Vue实现数据双向绑定的原理:Object.defineProperty
- vue实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty() 来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty() 将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。
- vue的数据双向绑定 将MVVM作为数据绑定的入口,整合Observer,Compile和Watcher三者,通过Observer来监听自己的model的数据变化,通过Compile来解析编译模板指令(vue中是用来解析 {{}}),最终利用watcher搭起observer和Compile之间的通信桥梁,达到数据变化 —›视图更新;视图交互变化(input)—›数据model变更双向绑定效果
v-model的原理
用于表单的据双向绑定的指令
做了两个操作:
1.v-bind
绑定了一个value的属性
2.v-on
把当前元素绑定到了一个事件上Vue的双向数据绑定是由数据劫持结合发布者订阅者实现的。数据劫持是通过Object.defineProperty()来劫持对象数据的setter和getter操作。在数据变动时做你想做的事
原理 : 通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 => 视图更新 在初始化vue实例时,遍历data这个对象,给每一个键值对利用Object.definedProperty对data的键值对新增get和set方法,利用了事件监听DOM的机制,让视图去改变数据
Vue中如何封装组件?什么是组件,为何要封装?组件中data为啥是一个函数?
为何要封装?主要目的就是为了解耦
使用组件,可以提升高复用性通用组件,必须具备高性能、低耦合的特性。并且还会留一些插槽
- 每个组件都是 Vue 的实例 ,组件之间会进行数据共享,当 data 的值是同一个引用类型的值时,改变其中一个会影响其他在 ,JavaScript 中,在一个函数中返回这个对象就可以了:
Vue.component('some-comp', {
data: function () {
return {
foo: 'bar'
}
}
})
export default {
data () {
return {
foo: 'bar'
}
}
}