React 与其说是一种框架,倒不如说是一种开发范式。它的核心理念非常简单:
界面/视图就是数据结构的可视化表达
UI = f(data)
而界面/视图由组件组合而来
UI = f1(data) + f2(data) + f3(data) + ...
That's all.
React 扮演的角色就是上述公式中的 f,它完全是函数式的,组件就是函数,给它一个输入(传参),它就返回一个输出(UI)。只有理解了这种开发理念,用 React 才会得心应手。如果只是去学“语法”,仅满足于“能用”,React 确实会显得非常怪异而麻烦。
Vue 的核心理念与 React 其实是一致的,但在 API 的实现上 Vue 对于用户而言更加友好。不像 React,Vue 不会要求用户“Think in Vue”,因为不需要,Vue 是一个渐进式的框架,它并没有大幅改变用户原有的开发方式,它既可以作为插件植入老的 jQuery 项目,也可以作为整个项目的核心框架层。并且 Vue 提供了各种实用的 API,而 React 本身的 API 非常少,许多功能需要自行封装或引用现成的组件。
前言
使用vue的时候经常会遇到一些问题,其实仔细阅读查阅官方文档,就会发现文档中已提到一些格外需要注意的点; 为了深入的理解官方文档中对这些问题的解释,查阅了一些资料,再加上自己的理解,整理了一些常见的问题;如果哪方面解释的不太合理希望各路大神指出;
文章篇幅较长,但是很实用;
目录
- 组件里面, data必须是一个函数
- vue中$set的使用场景
- vue生命周期详解
- vue组件通信
- vue组件之keep-alive
- 生命周期函数/methods/watch里面不应该使用箭头函数
- methods/computed/watch
1.组件里面, data必须是一个函数
类比引用数据类型
Object是引用数据类型, 每个组件的data 都是内存的同一个地址,一个数据改变了其他也改变了;
那么用什么方法可以使每个组件的data相互独立,不受影响呢?
当一个组件被定义,data 必须声明为返回一个初始数据对象的函数,因为组件可能被用来创建多个实例。如果 data 仍然是一个纯粹的对象,则所有的实例将共享引用同一个数据对象!通过提供 data 函数,每次创建一个新实例后,我们能够调用 data 函数,从而返回初始数据的一个全新副本数据对象。
2.vue中$set的使用场景
场景1:
通过数组的下标去修改数组的值,数据已经被修改了,但是不触发updated函数,视图不更新,
1
|
export default {
|
执行changeItem1, 控制台打印 111 ‘x’, 没有触发updated,视图不更新
执行changeItem1, 控制台打印 222 ‘x’, 数据更新 ‘x’; 触发updated,视图更新
场景2: vue中检测不到对象属性的添加和删除
1
|
data() {
|
想要给userProfile加一个age属性
1
|
addProperty () {
|
执行addProperty函数时,打印如下
1
|
555 { name: '小明', age: '12'}
|
但是没有触发updated, 视图未更新
改成下面这种
1
|
addProperty () {
|
再次执行, 数据发生变化, 触发updated, 视图更新;
有时你想向已有对象上添加一些属性,例如使用 Object.assign() 或 _.extend() 方法来添加属性。但是,添加到对象上的新属性不会触发更新。在这种情况下可以创建一个新的对象,让它包含原对象的属性和新的属性:
1
|
// 代替 `Object.assign(this.someObject, { a: 1, b: 2 })`
|
这是vue中很典型的一个问题,使用的时候一定要注意!
简单的解释一下原理:
vue在创建实例的时候把data深度遍历所有属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。让 Vue 追踪依赖,在属性被访问和修改时通知变化。所以属性必须在 data 对象上存在才能让 Vue 转换它,这样才能让它是响应的。
当你在对象上新加了一个属性 newProperty,当前新加的这个属性并没有加入vue检测数据更新的机制(因为是在初始化之后添加的),vue.$set是能让vue知道你添加了属性, 它会给你做处理
3.vue生命周期详解
1. vue的生命周期
- beforeCreate: 组件实例刚刚被创建,组件属性计算之前,如data属性
- created: 组件实例创建完成,属性已绑定,但是DOM还未完成,$el属性还不存在
- beforeMount:模板编译/挂载之前
- mounted: 模板编译/挂载之后
- beforeUpdate: 组件更新之前
- updated: 组件更新之后
- activated: for
keep-alive
,组件被激活时调用 - deactivated: for
keep-alive
,组件被移除时调用 - beforeDestroy: 组件销毁前被调用
- destoryed: 组件销毁后调用
ps:下面代码可以直接复制出去执行
1
|
<!DOCTYPE html>
|
beforeCreated: el和data并未初始化
created: 完成data数据的初始化,el没有
beforeMount: 完成了el和data初始化
mounted: 完成挂载
1
|
打开命令行在命令行中输入vm.a = 'change';查看效果
|
4.vue组件通信
1.父组件给子组件传递数据
vue中使用props向子组件传递数据
1): 子组件在props中创建一个属性,用于接收父组件传过来的值
2): 父组件中注册子组件
3): 在子组件标签中添加子组件props中创建的属性
4): 把需要传给子组件的值赋给该属性
2.子组件向父组件传递数据
子组件主要通过事件传递数据给父组件
1), 子组件中需要以某种方式,例如点击事件的方法来触发一个自定义事件
2),将需要传的值作为$emit的第二个参数,该值将作为实参数传给相应自定义事件的方法
3),在父组件中注册子组件并在子组件标签上绑定自定义事件的监听
3.子组件向子组件传递数据
vue找那个没有直接子组件对子组件传参的方法,建议将需要传递数据的在组件,都合并为一个组件,如果一定需要子组件对子组件传参,可以先传到父组件,再传到子组件,为了方便开发,vue推出了一个状态管理工具vuex,可以啃方便的实现组件之间的参数传递
具体的实例代码如下:可以自行参考相关代码在编辑器中尝试
父组件向子组件传递数据
1
|
// 父组件向子组件传递数据
|
子组件向父组件传递数据// 子组件向父组件传递数据<!--
1.@ : 是 v-on的简写
2.子组件主要通过事件传递数据给父组件
3.当input的值发生变化时,将username传递给parent.vue,首先声明了一个setUser,用change事件来调用setUser
4.在setUser中,使用了$emit来遍历transferUser事件,并返回this.username,其中transferuser是一个自定义事件,功能类似一个中转,this.username通过这个事件传递给父组件
-->
<template>
<div>
<div>{{msg}}</div>
<span>用户名</span>
<input v-model="username" @change='setUser'>向父组件传值</button>
</div>
</template>
<script>
export default {
data() {
return {
username: '测试',
};
},
props: {
msg: {
type: String,
},
},
methods: {
setUser() {
this.$emit('transferuser', this.username);
},
},
};
</script>
在父组件
<Child @transferuser='fn'/>
export default {
name: 'parent',
data: function () {
},
components: {
child
},
methods: {
fn: function (msg) { //回调方法,接收子组件传的参数
this.msg = msg;
}
},
mounted: function () {
});
}
};
5.vue组件之keep-alive
项目中写vue也没注意到<keep-alive></keep-alive>
这个组件,最近在深入的研究vue组件的生命周期函数,每一个函数都是干嘛的,然后其中有activated
和deactivated
这两个函数与<keep-alive></keep-alive>
这个组件有关
activated
: keep-alive组件激活时调用deactivated
: keep-alive组件停用时调用keep-alive用法
<keep-alive>
包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们<keep-alive>
是一个抽象组件:它自身不会渲染一个DOM元素,也不会出现在父组件链中- 当组件在
<keep-alive>
内被切换,它的activated
和deactivated
这两个生命周期钩子函数将会被对应执行具体的实例如下
- 是一个简单的tab切换,可以尝试把
<keep-alive>
去掉之后,对比一下,然后就会发现它的好处
test.vue
1
|
<template>
|
测试结果如下:
注意看一下页面和控制台输出的信息,可以更加直观的注意到<keep-alive>
的作用及activated
和deactivated
这两个函数什么时候会被触发
用setTimeout模拟请求后端接口的场景
test1.vue
和test2.vue
的相关代码如下:
test1.vue
1
|
<template>
|
test2.vue
1
|
<template>
|
6. 生命周期函数/methods/watch里面不应该使用箭头函数
es6的箭头函数的出现,是我们可以用更少的代码实现功能,但是应该注意箭头函数和普通函数的最大区别是this的指向问题: 箭头函数的this指向函数所在的所用域,普通函数的this指向函数的调用者;
官方文档中特别提醒中已经指出这一点:
vue中生命周期函数, methods, watch 自动绑定 this 上下文到实例中,因此你可以访问数据,对属性和方法进行运算。这意味着 你不能使用箭头函数来定义一个生命周期方法, 这是因为箭头函数绑定了父上下文,因此 this 与你期待的 Vue 实例不同
7.methods/computed/watch
methods VS computed
我们可以将同一个函数定义为methods或者computed,用这两种方式,得到的结果是相同的,不同的是computed是基于它们的依赖进行缓存的,计算属性只有在它相关的依赖发生改变时才重新求值;
适用场景:
重新计算开销很大的话,选computed; 不希望有缓存的选methods
computed vs watch
watch 有新旧值两个参数, 计算属性没有,但是计算属性可以从setter获得新值
关于computed
对于计算属性要特别说明一点: vue的计算属性computed默认只有getter,需要使用getter的时候需要自己加一个setter
1
|
export default {
|
执行 changeFullName 发现报错[Vue warn]: Computed property "fullame" was assigned to but it has no setter.
我们需要给计算属性fullName添加一个setter
1
|
computed: {
|