一、计算属性 computed
- 把computed中的方法当做属性使用,返回一个数据供使用
- 会主动获取数据源data中的数据 不需要传参 只是一种程序处理
- 计算属性就是处理数据源中的数据 然后用于渲染 同时监听计算属性中用到的数据源 然后把计算的结果缓存
<div id="eg1">
<h1>methods------{{birth}},{{age1(birth)}}</h1>
<h1>computed-----{{birth}},{{age}}</h1>
<button @click="change">点击</button>
<p>{{shuxing}}</p>
</div>
<script>
new Vue({
el: "#eg1",
data: {
birth: "2001-04-16"
},
methods: {
age1(str) {
return new Date().getFullYear() - new Date(str).getFullYear()
},
change() {
this.shuxing = 200
}
},
computed: {
//第一种写法:语法糖
age() {
return new Date().getFullYear() - new Date(this.birth).getFullYear()
},
//第二种写法 把shuxing当属性看
shuxing: {
//获取 取值
get() {
console.log("computed执行了")
return new Date().getFullYear() - new Date(this.birth).getFullYear() + "岁"
},
//设置 存值
set(val) {
console.log(val)
this.birth = `${2022-v}-02-03`
}
}
},
})
</script>
结果显示:
计算属性和方法的区别:(面试题)
- 计算属性会把使用到的data中的属性缓存起来,防止页面发生大量重复计算,提升运行效率。仅当计算属性中使用到的data中那部分数据变了才会重新调用计算属性刷新页面,否则直接使用缓存中的数据。
- methods方法没有计算结果缓存起来,data任何数据发生改变,模板会重新取值渲染页面 ,方法都会被重新调用一遍
- 方法常常是作用的事件使用,计算属性常常是动态计算结果时使用
补充:关于计算属性 函数什么情况下调用(面试题)
- 计算属性使用时当做属性使用
- 计算属性设计时当做函数设计(就像es6中的属性)
- 当计算属性的函数中使用到的data中的数据发生变化时,计算属性就会重新执行并刷新UI
1.如果是修改了data中监听的某个的属性值 计算属性就会运行
2.如果是修改了data中监听的某个属性值内部的数据,计算属性就不会重新运行
比如:计算属性使用的是data中的一个数组,某个交互把数组内部的某个下标的值改了,但是这个数 组没有改变,就不会触发计算属性
解决办法1:把修改后的数组重新赋值给data,让引用发生变化,来触发计算属性
解决办法2:赋值 JSON.parse(JSON.stringfy(data))
二、属性侦听器 watch
- watch:{x(){}}中的方法名必须跟要监听的data中的属性名一样,才代表监听指定属性
- 当侦听器监听的属性发生变化时,就会调用watch中对应的方法
- 侦听器属性比计算属性计算效率消耗大
<div id="eg1">
<p>{{n}}</p>
<button @click="change1">修改n</button>
<button @click="change2">修改obj</button>
<p>{{obj.age}}</p>
</div>
<script>
new Vue({
el: "#eg1",
data: {
n: 100,
obj:{age:20}
},
methods: {
change1() {
this.n = 90
},
change2() {
//修改了引用数据内部的属性值,页面会重新渲染,但是侦听器属性不会触发 因为引用没有发生变化
this.obj.age = "21"
//只有修改了引用数据的引用 才会触发侦听器属性
this.obj={age:22}
//若想只修改引用数据的属性值也触发侦听器,必须深度监听
}
},
watch: {
n() {
console.log(1111)
},
// obj(){
// console.log("obj改变了")
// },
//深度监听 引用数据内部的属性值
obj:{
deep:true,
handler:()=>{
console.log("obj改变了")
}
}
}
})
</script>
结果显示:
计算属性、属性侦听器、方法、过滤器有什么区别?(面试题)
计算属性:会把计算的结果缓存起来,并监听计算过的数据源。如果监听的数据源发生改变就会重新计算(监听的属性没有发生变化 模板重新渲染 但直接取缓存中的计算结果)
方法:一般是提供给模板事件和其他方法(如钩子函数)使用,如果在模板中使用了方法,而且不是事件,只要模板中的渲染的任意数据源改变了,它都会重新调用
过滤器:用于数据渲染器的数据处理,除了用法与方法不一样 其他都一样。但是它没有被this劫持。
属性侦听器:只有侦听的属性发生变化时才会触发(deep:true 深度侦听,但消耗内存)
将以上函数分类的原因:为了业务更明显 功能更好调试
被this对象劫持的:方法、计算属性
三、自定义指令 directives
- 指令:标签中v-开头的一种自定义的标签的属性 在vue运行后就具有封装好的功能 使用时非常简洁
除了默认设置的核心指令( v-model 和 v-show 等),Vue 也允许注册自定义指令。
在Vue里,代码复用的主要形式和抽象是组件。
有的情况下,仍然需要对纯 DOM 元素进行底层操作,这时候就会用到自定义指令 。
钩子函数
一个指令定义对象可以提供如下几个钩子函数 (均为可选):
bind
:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
inserted
:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
update
:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
componentUpdated
:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind
:只调用一次,指令与元素解绑时调用。钩子函数参数
指令钩子函数会被传入以下参数:
el
:指令所绑定的元素,可以用来直接操作 DOM。binding
:一个对象,包含以下 property:
name
:指令名,不包括v-
前缀。value
:指令的绑定值,例如:v-my-directive="1 + 1"
中,绑定值为2
。oldValue
:指令绑定的前一个值,仅在update
和componentUpdated
钩子中可用。无论值是否改变都可用。expression
:字符串形式的指令表达式。例如v-my-directive="1 + 1"
中,表达式为"1 + 1"
。arg
:传给指令的参数,可选。例如v-my-directive:foo
中,参数为"foo"
。modifiers
:一个包含修饰符的对象。例如:v-my-directive.foo.bar
中,修饰符对象为{ foo: true, bar: true }
。vnode
:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。oldVnode
:上一个虚拟节点,仅在update
和componentUpdated
钩子中可用。
<div id="eg1">
<p v-html="msg"></p>
<input type="text" v-model="count">
<div v-red>字体变红</div>
<div v-color="'pink'">字体变粉</div>
<div v-color="mycolor">data中的变量</div>
<input type="text" v-focus value="获取光标">
</div>
<script>
new Vue({
el: "#eg1",
data: {
msg: "<h1>案例1</h1>",
count: 200,
mycolor: "blue"
},
directives: {
red: {
inserted(el) {
//el:绑定这个指令的节点对象:DOM操作
console.log(el)
el.style.color = "red"
}
},
color: {
//inserted:指令插入节点以后 钩子函数
inserted(el, option) {
console.log(el, option.value)
el.style.color = option.value
}
},
focus: {
inserted(el) {
el.focus()
}
}
}
})
</script>
结果显示:
四、生命周期函数
生命周期函数(钩子函数):
- 钩子:c语言中有一类系统回调的函数执行业务
- 在某种条件成立时,系统会去调用vue中设定的函数 这些函数叫做生命周期函数
- 当vm实例在整个运行的过程中,会在不同的时期去执行特定的函数 这些函数叫做生命周期函数
vue组件中8个:当前vm实例从创建到销毁的过程中会调用的函数:
- beforeCreate()
- created()
- beforeMount()
- mounted() //以上4个钩子都只执行一次
- beforeUpdate()
- updated() //以上2个第一次构建不会调用,以后每次data被更新了才会调用
- beforeDestroy()
- destroyed() 只执行一次
-
beforeCreate()
- 此函数由系统调用
- 函数在vm创建完成之前运行时,vm正在创建中:劫持data,methods 所以此时this对象中还不能访问到数据
能否网络请求数据 设置到数据源中?
- 可以做网络请求 因为函数在运行时XMLHttpRuquest是可以访问做AJAX请求
- 不能设置到数据源中 因为此时this还在创建
在此函数中可以做:
- 预加载:网页中同源加载的优化(同一个页面中的img script等 src属性请求资源) 将资源请求下来 存着 但不渲染
- 提前配置
- 只要不是用于页面渲染的都可以
-
created()
- 在vm对象创建完毕后挂载到DOM树前触发钩子(调用函数)
- 即此函数可以操作this对象 但不能操作DOM
能否网络请求数据 设置到数据源中?
- 可以做网络请求 因为函数在运行时XMLHttpRuquest是可以访问做AJAX请求
- 可以设置到数据源中 因为此时this已经创建完毕
在此函数中可以做:
- 请求首屏数据
-
beforeMount()
- 挂载/渲染前的操作
- vm对象创建完毕后挂载到DOM树前调用函数
- 可以做网络请求 可以设置到数据源中 可以操作this对象 无法操作DOM
-
mounted()
- vm已经挂载到页面上 DOM渲染已经完成
- 请求首屏数据
- 网络请求时 页面已经出来了 请求后数据驱动页面 局部刷新页面
-
beforeUpdate()
(数据源已经更新 )在页面重新渲染前 触发的钩子
数据更新时调用,发生在虚拟 DOM 打补丁之前。
这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器,该函数在服务器端渲染期间不被调用,因为只有初次渲染会在服务端进行
此函数中不能网络请求数据更新数据源 会导致死循环
-
updated()
- 页面已经重新渲染后 触发钩子
- 此函数中不能网络请求数据更新数据源 会导致死循环
-
beforeDestroy()
- vm实例对象销毁之前 触发的函数 此时this还存在
- 可以做最后的操作 如 保存用户的行为配置文件
-
destroyed()
- vm对象销毁后触发的函数 无法操作this
- 调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监 听器会被移除,所有的子实例也会被销毁
- 清除当前组件中的计时器 把body的滚动条滚动到顶部
五、组件
- 是可复用的 Vue 实例
- 主要用于开发中 具有相同特征不同数据的模块 把它集成为一个组件 供重复利用
注册组件的注意事项:
- 注册组件时,组件名不能与系统标签同名
- 组件template模板中 只能有一个根元素
- 注册时用驼峰命名法,使用时用连字符 如<MyBox> <==> <my-box>
<div id="eg1">
<Box></Box>
<Nav1></Nav1>
<my-box></my-box>
</div>
<script type="module">
//第三种:import 导入
import MyBox from "./components/MyBox.js"
//第一种:写在全局 components中引用变量名
let cpt1={
template: `<div>
<h3>我是全局声明的</h3>
</div>`
}
new Vue({
el: "#eg1",
data: {},
methods: {},
components: {
//注册组件
//第一种:写在全局
Box:cpt1,
//第二种:直接注册
Nav1:{
template:`<div>
<h3>我是在components中直接注册的</h3>
</div>`,
data:function(){
return {msg:"msg"}
},
methods:{
fn(){console.log("组件的全部方法")}
}
},
//第三种:import 导入
MyBox
},
})
</script>
结果显示: