计算属性
模板中放入太多的逻辑会让模板过重且难以维护,使用计算属性可以让模板变得简洁易于维护。计算属性是基于它们的响应式依赖进行缓存的,计算属性比较适合对多个变量或者对象进行处理后返回一个结果值,也就是数多个变量中的某一个值发生了变化则我们监控的这个值也就会发生变化。
如果对于一个结果,进行计算,可以使用vue提供计算属性来完成,而且计算属性它还具有缓存功能
如果你的依赖项,没有发生改变,则它会在再一次调用时,会读取缓存中的数据。
计算属性定义在Vue对象中,通过关键词computed属性对象中定义一个个函数,并返回一个值,使用计算属性时和data中的数据使用方式一致
<div id="app">
<!-- 当多次调用 cfn计算属性时只要里面的 num值不改变,它会把第一次计算的结果直接返回直到data中的num值改变 计算属性才会重新发生计算 -->
<div>{{ cfn }}</div>
<div>{{ cfn }}</div>
<!-- 调用methods中的方法的时候 他每次会重新调用 -->
<div>{{ fn() }}</div>
<div>{{ fn() }}</div>
</div>
<script type="text/javascript">
const vm = new Vue({
el: '#app',
data: {
num: 10
},
// 方法
methods: {
fn() {
console.log('methods')
return this.num
}
},
// 计算属性
computed: {
cfn() {
console.log('computed')
return this.num
}
}
})
</script>
注:只要依赖的数据源不发生改变,我就调用1次,其它使用缓存
// 计算[属性]
computed: {
// 调用时,直接写名称就可以,不用写小括号
// 计算属性中的依赖项,可以是一个,也是可以N个,取决于你在计算属性中调用多少
// 注:此方法中不能写异步
// 简写 使用最多的写法
// total() {
// console.log('computed -- total')
// // 在计算属性中,调用了 n1和n2,则n1和n2就是它的依赖项,如果这两个依赖项,有一个发生改变,则它会重新计算,如果两个都没有发生改变,则第2之后调用,读取缓存中的数据
// return this.n1 + this.n2
// }
total: {
// 简写只是实现的了get
get() {
return this.n1 + this.n2
},
set(newVal) {
if (newVal > 10) {
this.msg = '值有点的大'
}
}
}
},
methods: {
sum() {
// console.log('sum --- methods', this.total);
// 如果你定义的计算属性,为简写方式,则给计算属性赋值,会报错
// 只有标准的写法时,它才可以对于计算属性进行赋值操作
// 赋值只会触发标准方式中的set方法,然后你可以得到它,完成一些别的工作
if (this.total > 6) {
this.total = 101
}
return this.n1 + this.n2
}
}
})
// console.log(vm);
监听器
使用watch来侦听data中数据的变化,watch中的属性一定是data 中已经存在的数据。
当需要监听一个对象的改变时,普通的watch方法无法监听到对象内部属性的改变,只有data中的数据才能够监听到变化,此时就需要deep属性对对象进行深度监听。
<div id="app">
<input type="text" v-model="username">
</div>
<script>
const vm = new Vue({
el: '#app',
data: {
username: 'aaaa'
},
watch: {
username(newVal, oldVal) {
console.log(newVal, oldVal)
}
}
})
</script>
-----------------------------------------------------------
# 对象侦听
<div id="app">
<input type="text" v-model="obj.username">
</div>
<script type="text/javascript">
const vm = new Vue({
el: '#app',
data: {
obj: {
username: '张三'
}
},
watch: {
obj: {
handler(newVal, oldVal) {
console.log(newVal, oldVal)
},
// 深度侦听
deep: true
}
}
})
</script>
监听器,它用来监听data配置中的数据的变化,一但有变化,就会自动触发,默认情况,初始化不触发
在监听器中是可以得到this对象的
监听器它的依赖项,只有一个,一对一
监听器中可以写异步
watch: {
// 方法名或属性名 就是你要观察的data中的属性名称
// 标准写法
// username: {
// // 变化后的值,变化前的值
// handler(newValue, oldValue) {
// console.log(newValue, oldValue);
// if (newValue.length >= 3) this.errorUsername = '账号过长'
// else this.errorUsername = ''
// },
// // 初始时,执行1次 --- 一般情况下,不启用 只有在标准写法下面,才有此配置
// immediate: true
// }
// 简写
// username(newValue, oldValue) {
// if (newValue.length >= 3) this.errorUsername = '账号过长'
// else this.errorUsername = ''
// // setTimeout(() => {
// // this.errorUsername = '账号过长'
// // }, 1000);
// }
// 监听对象中的指定的属性数据的变化 推荐如果你监听的是一个对象中数据变化,建议这样的方式
// 'user.id'(newValue, oldValue){
// console.log(newValue, oldValue);
// }
// 监听对象,只能使用标准方式来写
// 监听对象变化,它的前后值是一样的,无法区分
user: {
// 深度监听
deep: true,
handler(newValue, oldValue) {
console.log(newValue, oldValue);
},
}
}
})
过滤器 - filters
在数据被渲染之前,可以对其进行进一步处理,比如将字符截取或者将小写统一转换为大写等等,过滤器本身就是一个方法。
过滤器的作用就是为了对于界面中显示的数据进行处理操作
参数1:过滤器的名称,可以随意起名
参数2:回调函数,回调函数中的参数最后要有一个,最1位参数,永远指向为要过滤的数据
过滤器可以定义全局或局部
# 全局
// 回调函数中的参数1永久是绑定的数据
Vue.filter(过滤器名称,(value,[…args])=>{})
# 局部过滤器定义
此过滤器定义在Vue对象的配置中的filters配置选项中,以方法形式来定义,参数1永久是绑定的数据
new Vue({
filters:{
过滤器名(value){
return 数据
}
}
})
例子
// Vue.filter('phoneCrypt', value => {
// return value.slice(0, 3) + '~~~~' + value.slice(7)
// })
// Vue.filter('phoneCrypt', (value, salt = '****') => {
// return value.slice(0, 3) + salt + value.slice(7)
// })
const vm = new Vue({
el: '#app',
data: {
phone: '13523235235'
},
// 局部过滤器
filters:{
phoneCrypt(value, salt = '****'){
return value.slice(0, 3) + salt + value.slice(7)
}
},
methods: {
showPhone() {
return this.phone.slice(0, 3) + '****' + this.phone.slice(7)
}
}
})
混入 - Mixins
混入(mixins)是一种分发Vue组件中可复用功能的非常灵活的方式,用于公共代码的复用。混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。
混入的配置,它是可以把几乎所有new Vue配置中的所有配置都能混入,但是el配置它不可以混入
data配置,在混入方式中,只能写函数的方式,且函数一定要返回一个对象,混入可能被调用多次,如果直接是对象的话,就会有污染
data混入的优先级 组件 > 局部 > 全局 => 只会调用一个
生命周期方法,执行顺序 全局 -> 局部 --> 组件 依次执行
混入分为:全局和局部
# 全局混入
Vue.mixin({
created(){},
methods:{}
})
# 局部混入
let mixinObj = {}
new Vue({
mixins:[mixinObj]
})
实例
Vue.mixin({
data() {
return {
name: '张三 -- 全局'
}
},
computed: {
showName() {
return 'abc -- 全局'
}
},
methods: {
run() {
return 'run -- 全局'
}
},
created() {
console.log('created -- 全局');
}
})
// 局部混入
const mix = {
data() {
return {
name: '张三 -- 局部'
}
},
computed: {
showName() {
return 'abc -- 局部'
}
},
methods: {
run() {
return 'run -- 局部'
}
},
created() {
console.log('created -- 局部');
}
}
const vm = new Vue({
el: '#app',
data: {
// name: '张三 -- 实例'
},
mixins: [mix],
methods: {
run() {
return 'run -- 实例'
}
},
created() {
console.log('created -- 实例');
}
})
插件
在vue中提供插件机制,可以通过它的要求来完成插件的封装,运用到项目中
Vue.use(函数|类|对象,[可选参数])
函数
function plugin(Vue类,options可选参数){}
类
class Plugin {
// 必须是静态方法,名称必须为install
static install(Vue类,options可选参数){}
}
// 定义静态属性 Plugin.install(Vue类,options可选参数)
// 对象
const obj = {
// 属性名称必须为 install
install(Vue类,options可选参数){}
}
实例生命周期
每个 Vue 实例在被创建之前都要经过一系列的初始化过程。例如需要设置数据监听、编译模板、挂载实例到 DOM、在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,给予用户机会在一些特定的场景下添加他们自己的代码。
在vue2中页面钩子函数有11个,在vue3中有13个
beforeCreate | 在实例初始化之后,数据观测和事件配置之前被调用 此时data 和 methods 以及页面的DOM结构都没有初始化 什么都做不了, 执行1次 |
created | 在实例创建完成后被立即调用此时data 和 methods已经可以使用, 但是页面还没有渲染出来 。执行1次 用this对象 |
beforeMount | 在挂载开始之前被调用 ,此时页面上还看不到真实数据 ,只是一个模板页面而已 。执行1次 |
mounted | el被新创建的vm.$el替换,并挂载到实例上去之后调用该钩子。 数据已经真实渲染到页面上 在这个钩子函数里面可以进行数据请求等 。执行1次 |
beforeUpdate | 数据更新时调用,页面上数据还是旧的 。执行n次 |
updated | 由于数据更新完毕,页面上数据已经替换成最新的,执行 n次 |
beforeDestroy | 实例销毁之前调用 , 执行1次 |
destroyed | 实例销毁后调用, 执行1次 |
activated | keep-alive 组件激活时调用 |
deactivated | keep-alive 组件停用时调用 |
errorCapture | 当捕获一个来自子孙组件的错误时被调用 |
生命周期图
注:created : 在这个地方写Ajax 会解决跨域,但是会出现白屏对加载比较慢的浏览器
mounted 写Ajax,此时已经有结构存在。
网络请求
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和node.js中。能拦截请求和响应,自动转换JSON数据。axios也是vue作者推荐使用的网络请求库
############ 使用语法
# GET
## 方法1
axios.get('/user?ID=12345').then(res=>{})
## 方法2
axios.get('/user', {
params: {
ID: 12345
}
}).then(res=>{})
# POST
axios.post('/user', {
firstName: 'aa',
lastName: 'bb'
}).then(res=>{})
# 标准写法
axios({
method: 'post',
url: '/user/12345',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'},
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
})
# axios 全局配置
# 配置公共的请求头
axios.defaults.baseURL = 'https://api.example.com';
# 配置 超时时间 毫秒数(0 表示无超时时间)
axios.defaults.timeout = 1000;
# 配置公共的请求头
axios.defaults.headers['token'] = token;
### 拦截器
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
第一个监听所有异常,第二个只监听自身的一个
vue组件
组件 (Component) 是 Vue.js 最强大的功能之一,它是html、css、js等的一个聚合体。
组件化:
将一个具备完整功能的项目的一部分分割多处使用
加快项目的进度
可以进行项目的复用
组件注册分为:全局注册和局部注册
全局注册
Vue.component(‘组件名称’, { })第1个参数是标签名称,第2个参数是一个选项对象。全局组件注册后,任何vue实例都可以用。
组件注意事项:
构造 Vue 实例时传入的各种选项大多数都可以在组件里使用,只有一个例外:data必须是函数,同时这个函数要求返回一个对象 ,数据唯一性
组件模板必须是单个根元素(html标签)
组件模板的内容可以是模板字符串
Vue.component('HelloWorld', {
data: function(){
return {
msg: 'HelloWorld'
}
},
template: '<div>{{msg}}</div>'
});
局部注册
只能在当前注册它的vue实例中使用,通过某个 Vue 实例/组件的实例选项 components 注册仅在其作用域中可用的组件
var Child = {
template: '<div>A custom component!</div>'
}
new Vue({
components: {
// <my-component> 将只在父组件模板中可用
'my-component': Child
}
})
动态组件
通过使用保留的 元素,动态地绑定到它的 is 特性,我们让多个组件可以使用同一个挂载点,并动态切
<component v-bind:is="currentView"></component>
<script>
var vm = new Vue({
el: '#root',
data: {
currentView: 'home'
},
components: {
home,
posts
}
})
</script>
keep-alive
如果把切换出去的组件保留在内存中,可以保留它的状态或避免重新渲染。
<keep-alive>
<component :is="currentView"></component>
</keep-alive>
<script>
var vm = new Vue({
el: '#root',
data: {
currentView: 'posts'
},
components: {
home,
posts
}
})
</script>
注:父组件没有挂载完毕之前生命周期都是先执行,只有mounted等着所有的子组件完成挂载后才执行。
组件间传值
父组件向子组件传值
父组件以属性的形式绑定值到子组件身上
子组件通过使用属性props接收(props是单向数据流(只读属性):当父组件的属性变化时,将传导给子组件,但是反过来不会)
子组件向父组件传值
子组件用
e
m
i
t
(
)
定义自定义事件,
emit()定义自定义事件,
emit()定义自定义事件,emit()第一个参数为 自定义的事件名称 第二个参数为需要传递的数据
父组件用v-on(@)绑定子组件定义的自定义事件名,监听子组件的事件,实现通信
事件总线
非父子组件或更多层级间组件间传值,在Vue中通过单独的事件中心来管理组件间的传值。
前提条件:必须先订阅和发布 发布 与 订阅
建立统一的事件中心 const bus = new Vue()
传递数据方,通过一个事件触发bus.$emit(方法名,传递的数据)
接收数据方,在生命周期函数中,通过bus.$on(方法名,(参数)=>{})
来监听
销毁事件,在接受数据方,通过bus.$off(方法名)
销毁之后无法监听数据
Ref
ref被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
# 普通dom元素上
<p ref="p">hello</p>
# 子组件上
<child-comp ref="child"></child-comp>
# 调用
this.$refs.引用名
root/parent/children
获取父组件对象或子组件对象集合
this.$root
this.$parent
this.$children
provide/inject
provide 和 inject 主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。
这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。
var Provider = {
provide: {
foo: 'bar'
},
// ...
}
var Child = {
inject: ['foo'],
created () {
console.log(this.foo) // => "bar"
}
// ...
}