Vue组件化通讯
1. Vue的组成文件(.vue)
分为三部分,分别对应html,js,css
- <template></template>
- <script></script>
- <style></style>
2. Vue的生命周期函数(watch的时候dom也没有更新)
- beforeCreate() 创建数据之前
- created() 创建数据 我们在这里的得到我们在data里面创建的数据
- beforeMount() // Dom渲染完成前
- mounted() //Dom渲染完成
- beforeUpdate() // 更新视图 在beforeUpdate触发时,视图已经更新完成
- Updated() //更新数据调用的函数、。
<div id='app'>
<p>{{msg}}</p>
<input type='text' v-model='msg'>
</div>
var app = new Vue({
el: '#app',
data() {
return {
msg: 1
}
},
beforeCreate() {
console.log('beforeCreate', this.msg); //beforeCreate undefined
console.log('beforeCreate: ', document.getElementsByTagName('p')[0]) //beforeCreate <p>{{msg}}</p>
},
created() {
// 创建数据
console.log('created', this.msg); //beforeCreate 1
console.log('created: ', document.getElementsByTagName('p')[0]) //beforeCreate <p>{{msg}}</p>
// 异步处理得到渲染的dom数据
setTimeout(() => {
this.msg = 100
console.log('nextTick', document.getElementsByTagName('p')[0])
}, 100)
// nextTick <p>100</p>
},
beforeMount() {
console.log('beforeMount', this.msg) //beforeMount 1
console.log('beforeMount: ', document.getElementsByTagName('p')[0]) // beforeMount <p>{{msg}}</p>
},
mounted() {
// 渲染dom
console.log('mounted', this.msg) //mounted 1
console.log('mounted: ', document.getElementsByTagName('p')[0]) //mounted <p>1</p>
},
beforeUpdate() {
console.log('beforeUpdate', this.msg) //beforeUpdate 100
console.log('beforeUpdate: ', document.getElementsByTagName('p')[0]) //beforeUpdate <p>100</p>
},
updated() {
console.log('updated', this.msg) // updated 1
console.log('updated: ', document.getElementsByTagName('p')[0]) // updated <p>100</p>
}
})
3. export default
每一个模块都是自己的作用域,相应的属性来处理数据和函数
- data(声明数据,可以是函数和属性)
-
类型:
Object | Function
- 组件只接受函数
// 对象的形式
export default{
data: {
a:1
}
}
// 函数的形式
export default{
data(){
return {
a: 1
}
}
}
- methods(一些指令和其他属性的调用方法)
- 不要用箭头函数来写里面的函数
- this指向Vue的实例
export default{
methods: {
plus() {
this.a++
}
}
}
- components (组件化定义)
-
类型:
Object
- 自定义元素,增加代码的复用性
-
类型:
// 当我们引用一个.vue文件的时候,就像使用这个文件来充当我们主体的一部分
<div>
<hello></hello>
</div>
import hello from './hello.vue'
export default {
components: {
hello
}
}
-
computed(计算属性)
- 计算属性的结果会被缓存,依赖的数据发生变化才会重新渲染
- 注意计算属性和methods,watch的区别
{{this.total}} //[3,4] <button @click='add'>添加数据</button> //点击会更新this.total export default { data: () => ({ a: 1, b: [2,3] }), methods: { add(){ this.b.push(8); } }, computed: { total(){ return this.b.map((item)=>{ return item+this.a }) } } }
-
watch(监听对应的数据)
- 键值对。键是我们需要监督的数据,值是相应的回调函数
- 回调函数接受2个参数,新的值和旧的值(对于数组和对象不会出现旧值,对于简单的数据会出现旧值)
- 监听对象的内部值变化,需要添加deep:true(数组不用)
// 点击后相应的变化 data(){ return { a: 1, b: [2,4,6], c:{name:'hcc',age:22} } }, methods: { add(){ this.a++ this.b.push(8) this.c.name = 'yx' } }, watch: { b: function(val, oldVal){ console.log('new', val) //[2,4,6,8] console.log('new', oldVal) //[2,4,6,8] }, a: function(val, oldVal){ console.log(val); //2 console.log(oldVal); //1 }, c:{ handler(val){ console.log(val); //{name: 'yx',age: 22} } } },
-
props(用于接受父组件传来的数据)
- 规定和接受父组件的数据
- 单向数据流,子组件不能修改传递过来的数据
- 对象和数组是引用类型,指向同一个内存空间,如果 prop 是一个对象或数组,在子组件内部改变它会影响父组件的状态。
- 可以规定接受的数据类型和默认值,如果是对象和数组,默认值导出是一个函数
// 父组件 <hello :formParent='num'></hello> //html components: { hello }, data(){ return { num: 3 } } //子组件 //1. 数组规定接受的数据 props: ['hello'] //2. 验证的方式 props:{ hello: Number, hello: [String, Number], hello: { type: Object, default(){ return {message: 'hello'} } } }
-
v-on和v-emit(子组件向父元素传递数据)
-
vm.$emit: 子元素向父元素定义讯号和传递数据
this.$emit('规定的讯号名称', '想传递给父元素的数据')
-
vm.$on: 监听讯号,并触发相应的函数(函数内部不用传参)
@'规定的讯号名称'='调用自己组件的方法并可以接受传递的参数'
// 子组件 data () { return { msg: 'Welcome to Your Vue.js App' } }, methods: { change(){ this.$emit('sendMsg',this.msg) //把msg传递给父组件 } } // 父组件 // 引入子组件,并定义components components: { hello }, methods: { show(msg){ // 这里接受子组件传递的参数 console.log(msg); } } <hello @sendMsg='show'></hello> // 这里不用传递参数,不然会覆盖子元素传递的参数
-
-
ref(用来获取dom和子组件)
可以用来操作dom
<p ref="p">hello</p>
可以用来组件中的通讯
-
在组件中使用的this.refs是一个对象,包含了所有的绑定了的dom和子组件
// html <h1 ref="myElement">这是一个dom元素</h1> //dom元素 <hello :propnum="propnum" :obj='d' @getson='getMsg' ref='child'></hello> // 子组件 >-- 组件中this.refs => {myElement: h1, child: VueComponent} // 运用(在父元素中调用子元素的方法) // html <hello ref='child'></hello> // 子元素hello methods: { change() { this.$emit('getson',this.msg) this.obj.name = 'yx' }, drop(el) { el.style.background = 'red'; } }, // 父元素 methods: { add() { console.log(this.refs); //{child: VueComponent} this.$refs.child.drop('这里传递父元素的dom节点') } } //如果有一个需求是,一个父元素有2个子组件,其中一个子组件的方法要调用另一个子组件的dom元素 1. 一个子组件需要向父组件发送元素this.$emit('方法名',dom) 2. 父元素接受到子组件的传递得到对应dom 3. 父元素通过this.$refs调用对应的另一个子组件的方法并传入参数 // 子元素hello和world <div class="world"> <h1 ref="world">这是world的dom元素</h1> <button @click='send'>给父元素传递dom</button> </div> methods: { send(){ this.$emit('give',this.$refs.world); //给父元素发送dom } <div class='hello'> <button>改变dom</button> </div> methods: { changeDom(target){ console.log(target) } } // 父元素 <world @give='父亲自己的方法'></world> <hello ref='helloChild'></hello> methods: { // 这里接受子元素传递过来的dom元素 '父亲自己的方法'(target) { this.refs.helloChild.changeDom(target) //调用另一个子元素的方法,并把dom传递过去 } }
-
vm.nextTick(callback)
下次dom更新循环结束后执行对应的回调函数
在修改数据之后立即使用这个方法,可以获取更新后的DOM结构
- 使用场景
- 在 Vue 生命周期的 created() 钩子函数进行的 DOM 操作一定要放在 Vue.nextTick() 的回调函数中。原因是什么呢,原因是
在 created() 钩子函数执行的时候 DOM 其实并未进行任何渲染,而此时进行 DOM 操作无异于徒劳,所以此处一定要将 DOM 操作的 js 代码放进 Vue.nextTick() 的回调函数中。
与之对应的就是 mounted 钩子函数,因为该钩子函数执行时所有的 DOM 挂载和渲染都已完成,此时在该钩子函数中进行任何DOM操作都不会有问题 。 - 在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的 DOM 结构的时候,这个操作都应该放进 Vue.nextTick() 的回调函数中。
- 在 Vue 生命周期的 created() 钩子函数进行的 DOM 操作一定要放在 Vue.nextTick() 的回调函数中。原因是什么呢,原因是
// 需求一,如果我们想异步请求数据后,在钩子函数mounted之前只用对应的dom
// 思路: 通过异步等dom更新后,然后获得对应的dom
<template>
<h1 ref='title'>{{b}}</h1>
</template>
<script>
export default {
data() {
b: 'hello nextTick'
},
create() {
console.log(this.refs.title) // undefined
},
// 这样可以获得对应的dom (当dom全部渲染后再执行console.log())
// create() {
// this.$nextTick(()=>{
// console.log(this.refs.title); // <h1>hello nextTick</h1>(输出在mounted的后面)
// })
// },
mounted() {
console.log(this.refs.title) // <h1>hello nextTick</h1>
}
}
</script>