Vue基础(三)

一、Vue指令

vue框架特点:双向数据绑定与组件化开发

1.1、(重点) 深入双向数据绑定原理

Vue 最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新。Vue里面是怎么做到的的呢?其实就是使用了Object.defineProperty 把Vue内的属性全部转成 getter/setterObject.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。

Object.defineProperty 实现了对象劫持这个功能

vue双向数据绑定原理:

借助Object.defineProperty()对数据进行劫持,并结合发布-订阅者模式,来实现双向数据绑定

语法:

Object.defineProperty(obj, prop, desc)

  1. obj 需要定义属性的当前对象
  2. prop 当前需要定义的属性名
  3. desc 属性描述符

数据属性:

通过Object.defineProperty()为对象定义属性,有两种形式,分别为数据描述符,存取描述符,下面分别描述两者的区别:

  1. value 表示它的默认值
  2. writable 如果为true标识可以被修改,如果为false标识不能被修改(默认值为false)
  3. configurable 描述属性是否配置,以及可否删除,可以认为是总开关 默认值 false(不可删除)
  4. enumerable 描述属性是否出现在for in 或者 Object.keys()的遍历中 默认值false(不能遍历)
let obj = {};
Object.defineProperty(obj, 'name', {
    value: '张三'
})
obj.name = '李四'
console.log(obj.name) // 张三

let obj = {};
Object.defineProperty(obj, 'name', {
    value: '张三',
    writable: true
})
obj.name = '李四'
console.log(obj.name)
let obj = {};
  Object.defineProperty(obj, 'name', {
    value: '张三',
    writable: true,
    configurable: true,
    enumerable: true
  })
  obj.name = '李四'
  // delete obj.name
  console.log(obj.name) // 李四
  console.log(Object.keys(obj)) // ['name']

存取属性:

let obj = {};
let temp = null;
Object.defineProperty(obj, 'name', {
    get() {
        return temp
    },
    set(val) {
        temp = val
    }
})
obj.name = '李四'
console.log(obj.name)

面试题回答:

vue的双向数据绑定原理是什么?

vue数据双向绑定是通过数据劫持结合“发布者-订阅者模式”的方式来实现的。
vue是通过Object.defineProperty()来实现数据劫持,其中会有getter()和setter方法;当读取属性值时,就会触发getter()方法,在view中如果数据发生了变化,就会通过Object.defineProperty()对属性设置一个setter函数,当数据改变了就会来触发这个函数;

参考:https://segmentfault.com/a/1190000014274840

参考:https://zhuanlan.zhihu.com/p/51357583

1.2、自定义指令(仅做了解,不常用)

除了一些内置的制定(v-model和v-show…),Vue也允许注册自定义指令。

全局自定义指令格式:

// 注册一个全局自定义指令 v-demo
Vue.directive('demo', {
	inserted: function (el, binding) {
		
		console.log(el, binding);
	},
    update(el, binding){}
})

局部自定义指令格式:

// 组件中注册局部指令
new Vue({
	el: '#app',
	data: {},
	directives: {
		demo: {
			inserted: function (el, binding) {
				cosnole.log(el, binding);
			}
		}
	}
})

自定义指令的使用:

// 在模板中使用自定义指令
<div v-demo>
    
</div>

函数:

  • inserted :被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
  • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。

参数:

  • el:指令所绑定的元素,可以用来直接操作 DOM 。
  • binding:一个对象,包含以下属性:
    • name:指令名,不包括 v- 前缀。
    • value:指令的绑定值,例如:v-demo="1 + 1" 中,绑定值为 2
    • oldValue:指令绑定的前一个值
    • expression:字符串形式的指令表达式。例如 v--demo="1 + 1" 中,表达式为 "1 + 1"
    • modifiers:一个包含修饰符的对象。例如:v-demo.foo.bar 中,修饰符对象为 { foo: true, bar: true }

实现类似v-show的自定义指令

  <input type="text" id="oipt">
    <p id="op"></p>

    <script>
		let obj = {};
		let val = "默认值"
		
		Object.defineProperty(obj, "iptVal", {
			get() {
				return val
			},
			// 第三方
			set(newVal) {
				// val = newVal
				oipt.value = newVal;
				op.innerHTML = newVal;
			}
		})
				
		// 订阅者
		oipt.value = obj.iptVal;
		op.innerHTML = obj.iptVal;
		
		oipt.addEventListener("keyup", function(e) {
			// 我们希望修改obj.iptVal的值,达到修改oipt.value和op.innerHTML的值
			// 发布者
			 obj.iptVal = e.target.value // 触发了set
		})		
		// 借助Object.defineProperty()对数据进行劫持,并结合发布-订阅者模式,来实现双向数据绑定			
		</object>
    </script>
</html>

二、(重点)Vue组件化开发

2.1、什么是组件化?

面对复杂问题的处理方式,把问题拆解成很多个能处理的小问题,再将其放在整体中,会发现大的问题也会迎刃而解。

而组件化的思想也类似:

  1. 如果我们实现一个页面结构和逻辑非常复杂的页面时,如果全部一起实现会变得非常复杂,而且也不利于后续的维护和迭代功能。
  2. 但如果我们这时候把页面分成一个个小的功能块,每个功能块能完成属于自己这部分独立的功能,那么整个页面之后的维护和迭代也会变得非常容易。
  • 组件化开发的优势:可维护性高 可复用性高
2.2、Vue组件化思想

组件化是Vue重要的思想

  1. 它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用。
  2. 任何的应用都会被抽象成一颗组件树。

组件化思想的应用开发:

  1. 有了组件化的思想,我们在之后的开发中就要充分的利用它。
  2. 尽可能的将页面拆分成一个个小的、可复用的组件。
  3. 这样让我们的代码更加方便组织和管理,并且扩展性也更强。
2.3、全局组件

通过Vue.component('组件名称', {}),通过这个方法注册的都是全局组件,也就是他们再注册之后可以用在任何新创建的Vue 实例挂载的区域内。

<body>
  <div id="app">
    <my-con></my-con>
    <div>
      <my-con></my-con>
    </div>
  </div>
  <my-con></my-con>
</body>
<script>
  Vue.component('my-con', {
    template: '<section><h3>组件标题</h3><p>组件内容</p></section>'
  })
  const vm = new Vue({
    el: '#app'
  })
</script>

上面案例中,在<div id="app">...</div> 外的组件 my-con 没有替换成组件真正的页面结构,是因为 new Vue() 挂载在 id=app 的节点上,不在这个节点上标签,不会受到Vue的影响。

2.4、局部组件

通过 Vue.component 方式注册的组件,称之为全局组件。任何地方都可以使用。全局注册往往是不够理想的。比如,如果你使用一个像 webpack 这样的构建系统,全局注册所有的组件意味着即便你已经不再使用一个组件了,它仍然会被包含在你最终的构建结果中。这造成了用户下载的 JavaScript 的无谓的增加。

注册局部组件

<body>
  <div id="app">
    <my-con></my-con>
    <div>
      <my-con></my-con>
    </div>
  </div>
  <div id="app1">
    <my-con1></my-con1>
  </div>
</body>
<template id="template1">
  <section>
    <h3>组件标题</h3>
    <p>组件内容</p>
  </section>
</template>
<template id="template2">
  <section>
    <h3>组件标题B</h3>
    <p>组件内容B</p>
  </section>
</template>
<script>
  var componentA = {
    template: '#template1'
  }

  var componentB = {
    template: '#template2'
  }
  const vm = new Vue({
    el: '#app',
    components: {
      'my-con': componentA
    }
  })
  const vm1 = new Vue({
    el: '#app1',
    components: {
      'my-con1': componentB
    }
  })
</script>

父组件和子组件

组件和组件之间存在层级关系,而其中一种最重要的关系就是父子组件关系。

2.5、组件可以访问Vue实例数据吗?

那组件如果要使用data定义自己属性保存数据要怎么做呢?

  1. 组件对象也有一个data的属性(也有methods等属性,下面我们有用到)
  2. 只是这个data属性必须是一个函数,而且函数返回一个对象 ,对象保存着数据
<body>
  <div id="app">
    <my-con></my-con>
    <div>
      <my-con></my-con>
    </div>
  </div>
  <div id="app1">
    <my-con1></my-con1>
  </div>
</body>
<template id="template1">
  <section>
    <h3>{{title}}</h3>
    <p>组件内容</p>
  </section>
</template>
<template id="template2">
  <section>
    <h3>{{title}}B</h3>
    <p>组件内容B</p>
    <aa></aa>
  </section>
</template>
<script>
  var componentA = {
    template: '#template1',
    data() {
      return {
        title: 'zujianbiaoti'
      }
    }
  }

  var componentB = {
    template: '#template2',
    data() {
      return {
        title: 'zj'
      }
    },
    components: {
      'aa': {
        template: '<div>aa</div>'
      }
    }
  }
  const vm = new Vue({
    el: '#app',
    data: {title: '组件标题'},
    components: {
      'my-con': componentA
    }
  })
  const vm1 = new Vue({
    el: '#app1',
    components: {
      'my-con1': componentB
    }
  })
</script>

为什么data在组件中必须是一个函数呢?

原因是在于Vue让每个组件对象都返回一个新的对象,因为如果是同一个对象的,组件在多次使用后会相互影响。

2.6、父子组件间的通讯
父级向子级传递

在组件中,使用选项props来声明需要从父级接收到的数据。

props的值有两种方式:

  1. 字符串数组,数组中的字符串就是传递时的名称。
  2. 对象,对象可以设置传递时的类型(String,Number,Boolean,Array, Object,Date,Function,Symbol),也可以设置默认值等。
<body>
  <div id="app1">
    <my-con1></my-con1>
  </div>
</body>

<template id="template2">
  <section>
    <h3>{{title}}B</h3>
    <p>组件内容B</p>
    <!-- my-con1组件内的aa组件 --> 
    <aa v-bind:parent-txt="childtxt"></aa>
  </section>
</template>
<script>
  var componentB = {
    template: '#template2',
    data() {
      return {
        title: 'zj',
        childtxt: 'child text'
      }
    },
    components: {
      'aa': {
        template: '<div>{{parentTxt}}</div>',
        props: ['parentTxt']
      }
    }
  }
  
  const vm1 = new Vue({
    el: '#app1',
    components: {
      'my-con1': componentB
    }
  })
</script>
子级向父级传递

父组件向子组件传递数据,通过自定义事件

<body>
  <div id="app1">
    <my-con1></my-con1>
  </div>
</body>

<template id="template2">
  <section>
    <h3>{{title}}B</h3>
    <p>组件内容B</p>
    <aa v-bind:parent-txt="childtxt" v-on:changetitle="changeTitle"></aa>
  </section>
</template>
<script>
  var componentB = {
    template: '#template2',
    data() {
      return {
        title: 'zj',
        childtxt: 'child text'
      }
    },
    components: {
      'aa': {
        template: '<div v-on:click="change">{{parentTxt}}</div>',
        props: ['parentTxt'],
        methods: {
          change() {
            this.$emit('changetitle', {
              a: 1
            })
          }
        }
      }
    },
    methods: {
      changeTitle(obj) {
        console.log(obj)
        this.title = obj.a
      }
    }
  }

  const vm1 = new Vue({
    el: '#app1',
    components: {
      'my-con1': componentB
    }
  })
</script>

案例分析:

  1. 现在的需求是点击子组件aa 然后把父组件my-con1上的标题给改变;
  2. 首先,在父组件的具体页面结构找到子组件aa ,在子组件aa 上使用v-on:changetitle="changeTitle" , changetitle是子组件的自定义事件名称,changeTitle是父组件my-con1里的methods属性定义的方法;
  3. 其次,在子组件aa里为div绑定点击事件 v-on:click="change", 在子组件aa里的methods定义change方法,change方法里使用this.$emit('changetitle'),使用 e m i t 方 法 来 触 发 绑 定 在 子 组 件 上 的 自 定 义 事 件 , emit方法来触发绑定在子组件上的自定义事件, emitemit第一个参数就是上一步定义的自定义事件changetitle,第二个参数就是传递到父组件的参数,可以不传。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值