讲一讲对象
什么是Vue的双向数据绑定原理?
1.Vue的双向数据绑定是通过数据劫持和"发布-订阅者"模式实现的。
2.当要获取一个对象时,要调用Object.defineProperty()中的get()方法,当检测到属性更新时,调用 Object.defineProperty()中的set()方法。
讲一讲 Object 中的 Object.defineProperty()
// Object.defineProperty('obj(操作的对象)','prop(操作的对象属性)',{});
/* Object.defineProperty(obj,'name',{
value: '', // 设置将要赋值给 prop 的值
writable: boolean, // 设置该属性是否可以被修改
configurable: boolean, // 设置该属性是否可以配置,即可否被删除
enumerable: boolean, // 设置该属性是否可被枚举,即可否给循环遍历
})
*/
// 修改(删除、增加)的属性要加引号,因为属性是以字符串的形式传递进来的
// var myObj = {};
// myObj.name = "张三",
var myObj = {};
Object.defineProperty(myObj,'name',{
value: "李四",
enumerable: true,
});
Object.defineProperty(myObj,'age',{
value: 22,
enumerable: true,
});
var arr = [];
// for(key in myObj){
// arr.push(key);
// }
console.log(myObj);
console.log(arr);
console.log(Object.keys(myObj)); // 获取某个对象中的键
getters(get)方法 和 setters(set)方法
var obj = {};
var str = "张三";
Object.defineProperty(obj, "name", {
get(){ // 读取属性
return str;
},
set(val){ // set() 方法应该接受一个参数(val),用来接收修改后的值
str = val;
}
})
// 这个时候如果想要修改 obj.name 的值,就需要使用到 Object.defineProperty()
中的 set() 方法
str = "李四"
console.log(obj.name); // 张三 ---> 李四
自定义指令
自定义全局指令
- 当我们有多个标签需要使用到同样的指令的时候,我们可以将指令提取出来,设置为全局指令,以供重复使用,就不用在每一个Vue实例中都写同一个指令。
// 书写全局指令的格式
Vue.directive("指令的名称",{
// 插入一个指令数据
inserted(el,binding,vnode){
console.log(el); // 真实DOM
console.log(binding); // 就是我们从Vue实例的data里面传递到视图上的
变量数据
console.log(vnode); // 虚拟DOM
}
})
<div id="app">
<div id="app">
<h3 v-demo="flag">你好,叩丁狼</h3>
</div>
<script>
Vue.directive('demo', {
inserted(el, binding, vnode){
// console.log(el); // 真实DOM
// console.log(binding.value);
// 就是我们从Vue实例的data里面传递到视图上的变量数据
// console.log(vnode); // 虚拟DOM
if(binding.value){
el.style.display = "block";
}else{
el.style.display = "none";
}
}
})
var vm = new Vue({
el: "#app",
data: {
flag: true,
}
})
</script>
</div>
- 但是这样子写有一点不好,就是每次都需要手动去切换 flag 的值,那么我们可以设置一个 button 按钮,通过点击 button 来控制显示和隐藏。
- 注意:要注意一点,我们前面只写了
inserted来插入父节点中去,却没有实时更新数据,所以我们还需要添加一个update方法来实时更新数据
<div id="app">
<div id="app">
<h3 v-demo="flag">你好,叩丁狼</h3>
// 给按钮添加一个点击事件,并且实时更新最新的数据,只要每次点击按钮,
就给 flag 取反
<button @click="flag = !flag">按钮</button>
</div>
<script>
Vue.directive('demo', {
inserted(el, binding, vnode){
if(binding.value){
el.style.display = "block";
}else{
el.style.display = "none";
}
},
// 实时更新数据
update(el, binding, vnode){
if(binding.value){
el.style.display = "block";
}else{
el.style.display = "none";
}
},
})
var vm = new Vue({
el: "#app",
data: {
flag: true,
}
})
</script>
</div>
自定义局部指令
- 局部指令其实就是在父组件 data 的同级下定义一个 directives 方法,将我们的自定义指令写在 directives 中,这种我们称之为局部指令注册。
<div id="app">
<h3 v-myDemo="flag">你好,叩丁狼</h3>
<button @click="flag = !flag">按钮</button>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
flag: true,
},
directives: {
myDemo: {
inserted(el, binding, vnode) {
// console.log(el); // 真实DOM
// console.log(binding.value);
// 就是我们从实例Vue的data里面传递到标签上的变量数据
// console.log(vnode); // 虚拟DOM
if (binding.value) {
el.style.display = "block";
} else {
el.style.display = "none";
}
},
// 我们在 inserted 当中只是插入了数据,而没有更新的功能
update(el, binding, vnode) {
if (binding.value) {
el.style.display = "block";
} else {
el.style.display = "none";
}
}
}
}
})
</script>
组件化
什么是组件化?
-
面对复杂问题的解决方式,可以将大问题分割成为多个容易解决的小问题,逐一解决之后再重新拼接成一个整体。
- 我们在遇到复杂的业务逻辑和页面结构的时候,如果我们将全部的代码都放在同一个页面里面,这样的代码既不整洁,也不利于我们后期的维护和迭代更新。
- 如果我们可以将这个整体分割为多个不同的功能模块,每一个模块独立的完成不同的功能,然后再将多个功能模块拼接成一个整体,这样子的代码既整洁、易看,也有利于我们后期的维护和迭代更新
-
Vue的组件化思想?
- 组件化是Vue的一个重要思想
- 其实可以将我们平时所做的页面(或者看到的页面)抽象为一颗组件树,每个页面都可以分割为不同的区块,各各区块里面又可以分割为多个区块,每个区块执行的功能都不一样,最后将这个功能区块拼接成一个整体,就形成了我们一个完整的网页。

全局组件
- 我们通过
Vue.component('组件名称',{})这个方法注册出来的组件都是全局组件,全局组件注册之后可以供所有Vue实例出来的的对象使用。
<script>
Vue.component('table_el',{
template: '<h3>Hello World</h3>'
})
</script>
局部组件
-
我们通过
Vue.component('组件名称',{})创建出来的组件都称之为全局组件,但是全局组件往往是不够理想了的,相信很多人有这样一个疑问,全局组件只要定义一个,所有Vue实例化的对象都可以使用,为什么会说不够理想呢?- 这是因为全局注册的组件,在打包时会将组件代码也一并打包到项目中去,这样子会平白增加项目的大小,没办法按需导入。
-
借由全局注册的不完善性引出了 ---> 局部注册,局部注册只能在当前
Vue实例中使用,避免了像全局组件那样被打包进项目中去,可以按需导入。
<body>
<div id="app">
<my-comp></my-comp>
</div>
</body>
<template id="app2">
<div>
<h3>你好,我是组件</h3>
<h4>你好,我是组件</h4>
<button>按钮</button>
</div>
</template>
</html>
<script>
new Vue({
el: "#app",
data: {},
// 注册局部组件
components: {
'my-comp': {
template: '#app2',
}
}
})
</script>
注意:全局组件和局部组件注册的时候,template 里面组件标签的根元素只能有一个,如果根元素超过一个,会报如下错误,意思大概是 组件模板应该正确的被包含在一个根元素下,如果一定要有多个标签,那么必须在最外层包裹一个标签,就不会报错
- Component template should contain exactly one root element
- 错误书写格式如下:

- 正确书写格式如下:

组件可以访问 Vue 实例数据吗?
- 按正常来讲,组件是不能访问父组件中的data数据的。(后面会讲到如何去获取父组件中的data数据)
- 回到我们之前的问题,如果直接去访问父组件中的data数据的话,会报错,提示说该属性为定义。

-
那么如果组件是要使用data给自己定义属性并且保存数据。那么应该怎么做呢?
- 首先组件对象中也有data属性(methods、directives等属性(后面会用到))
- 如果要使用data属性那么它一定要是一个函数,并且返回值是一个对象,数据就存储在对象中(留给大家一个思考问题,问什么组件中的data属性一定要是一个函数呢?后面为大家解答,请大家可以思考一下)
<body>
<div id="app">
<my-comp></my-comp>
<my-comp1></my-comp1>
</div>
</body>
<template id="temp">
<div>
<h3>{{name}}</h3>
<button @click="name++">加1</button>
</div>
</template>
<template id="temp1">
<div>
<h3>{{name}}</h3>
<button @click="name++">加1</button>
</div>
</template>
</html>
<script>
new Vue({
el: "#app",
data: {
name: '李四'
},
// 注册组件
// 组件中data必须是函数
components: {
'my-comp': {
template: '#temp',
data() {
return {
name: "andy"
}
}
},
'my-comp1': {
template: '#temp1',
data() {
return {
name: "jack"
}
}
}
}
})
-
回到之前遗留的问题,为什么组件的data属性必须是一个函数?(重点)
- 因为如果data属性不是一个函数,那么所以的数据属性就都会指向同一个内存,只要组件A修改了数据时,组件B的数据也被修改了。
-
现在组件是可以访问data属性了,但是还是没有解决我们最初的问题,就是组件访问 父组件中的data属性,组件现在访问的data属性是它自己的data属性。现在为大家讲解组件怎么访问父组件中的data属性。
父子组件间的通信
-
父级向子级传递:其实父级是可以向子级传递data属性里面的数据的,只是子级没有定义一个属性来接收父级传递的数据 --- > 在子级中,我们使用
props来声明需要从父级接收的数据 -
props的值有两种形式:- 第一种是字符串数组,数组中的字符串就是传递时的名称,我们就是通过这个名称来给组件渲染视图的
components: { 'table_el': { template: '<div>{{name}} {{age}}</div>', props: ['name', 'age'], } }-
第二种是对象格式(这种格式也是比较复杂的),我们接收的每一个属性都是一个对象,需要给它设置数据类型(type)、以及默认值(default)
- 可以设置的数据类型(type):( String,Number,Boolean,Array, Object,Date,Function,Symbol )
- 默认值(default):默认值就是假设数据请求的时候网络出现卡顿、断网等突发情况,Vue 会先将默认值暂时的渲染到视图上,待数据请求回来后,再将真正的数据渲染到视图上。
components: { 'table_el': { template: '<div>{{name}} {{age}}</div>', props: [ name: { type: String, default: '张三', }, age: { type: Number, default: 24, } ], } }