响应式数据的说明
响应式数据: 把data中的数据挂到vm身上,vm身上的这个数据其实就是响应式的
一旦数据发生了改变,页面中的内容也会跟着改变
动态添加的数据是无效的以及$set的使用
- data中的数据一定要先声明,再使用,动态给对象添加的属性不是响应式的
<div id="app">
<p>{{person.name}}---{{person.age}}---{{person.gender}}</p>
</div>
<script src="vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
person: {
name: 'zs',
age: 18
}
}
})
</script>
// 动态给data中添加的数据是无效的
// 在vue实例创建的时候,会把data中的数据绑定到vm上,所以data中的数据是响应的
// 但是动态绑定的数据不是响应是的。
vm.person.gender = '男'
- 如果想要动态绑定数据,并且是响应式的,需要使用
vm.$set方法
this.$set(this.person, 'gender', '男')
结论:
- 如果是对象,动态给对象添加或者删除一个属性,vue是检测不到的,所以不是响应的,如果想用动态添加的数据是响应式,需要使用
Vue.set(obj, key, value)
vm.$set(obj, key ,value)
- 如果是数组,通过数组的下标或者是数组的长度,vue也是检测不到,所以操作也不是响应式的
- 使用
Vue.set
方法 - 数组的变异的方法,这个方法会改变原数组,能够触发vue的更新。
- 使用
异步DOM更新以及$nextTick的说明
在vue中数据发生了改变,DOM中的数据也会跟着发生改变,但是这个过程是异步的
vue的数据发生改变之后,DOM不会立即更新,会等到下一次渲染工作执行的时候才会更新DOM
目的:为了提高渲染的性能
clickFn () {
// 数据变化了, view中的内容也要跟着变
this.msg = '你好啊,vue1'
this.msg = '你好啊,vue2'
this.msg = '你好啊,vue3'
this.msg = '你好啊,vue4'
this.msg = '你好啊,vue5'
// 为什么:DOM操作是非常消耗性能的,简单DOM的操作能够提升我们的性能,如果每次操作数据,都立即更新DOM,无疑性能会非常的低,所以vue中会等待数据都修改完成
let result = document.querySelector('p').innerHTML
console.log(result)
}
$nextTick
方法会在DOM更新之后执行
// 在实际开发中,有可能需要在数据改变之后,获取到更新之后的DOM数据
// 这个时候可以使用 $nextTick函数
// 当vue更新完DOM后,会自动调用$nextTick函数,确保在这个函数中可以获取到DOM结构是最新的
this.$nextTick(function() {
let result = document.querySelector('p').innerHTML
console.log(result)
})
组件化开发
组件化的概念
模块化
- 概念:
- 一个js文件就是一个模块,把一个独立的功能写到一个单独的js文件,称之为一个模块。
- 模块化的好处
- 容易维护
- 可以复用
- 假如没有模块化:
a. 把会所有的js逻辑写到一个js文件(app.js),难以维护 - 模块化仅仅是针对于js的
组件化
- 概念:
- 组件针对于一个独立的结构 样式 js
- 一个应用(页面)完全由组件构成,,,一个组件会包含有结构,样式,功能(js)
- 组件化的优点:
- 复用
- 容易维护
vue组件分类
vue组件分为全局组件和局部组件
- 全局组件:在所有的vue实例中都能使用
- 局部组件:只有在当前vue实例中能够使用
全局组件
组件是一个可以复用的vue实例
定义组件
Vue.component(参数一,参数二)
- 参数1:组件名
- 参数2:组件的配置项, 可以配置和vue实例相同的配置 methods computed watch template
Vue.component('demo', {
template: `<div>
<div>你好啊,我是一个组件</div>
<button>按钮</button>
</div>`
})
使用组件
把组件当成html标签来使用
<demo></demo>
注意点
- 定义的组件名不能是html原先就存在的标签。
- 组件的模版只能有一个根元素
- 组件中不会指定el,,但是一定会指定template,模版
局部组件
- 全局组件:将来在所有的vue实例中都可以使用
- 局部组件:只有在当前实例中可以使用
const vm = new Vue({
el: '#app',
data: {
msg: 'hello vue'
},
// 提供局部组件
components: {
demo: {
template: `<div>这是一个局部组件</div>`
}
}
})
组件和vue实例的不同点
- vue实例有el参数
- 组件要提供template参数
- 组件中的data必须是一个函数,函数内部需要返回一个对象。
<script>
Vue.component('hello', {
template: `
<div>
<button @click="fn">加10块</button>
<p>我有 {{money}} 块钱</p>
</div>
`,
methods: {
fn() {
console.log('哈哈哈')
this.money += 10
}
},
created() {
console.log('我是created钩子函数')
},
//组件中的data必须是一个函数,返回的对象在每个组件中都是独立的
data: function() {
return {
money: 100
}
}
})
</script>
组件是特殊的vue实例
可以将组件看成是一个vue的实例,因此,在vue实例中能配置的属性,在组件中依旧能够配置。
比如:data,method,watch,computed,钩子函数等
注意:组件中data属性必须是一个函数,返回值才是data的数据
//可以把组件看成一个vue实例
Vue.component("my-button", {
template: `
<div>
<h1 @click="add">我是一个标题</h1>
<p>{{msg}}</p>
</div>
`,
//组件可以通过data属性提供组件自己的数据,注意,组件的data参数必须是一个函数,不能是用一个对象。
data() {
return {
msg: 'hello vue'
};
},
methods: {
add() {
console.log("哈哈");
}
}
});
- 组件是一个独立封闭的个体,组件之间的数据是无法相互使用的
var vm = new Vue({
el: '#app',
data: {
outer: '我是实例的数据'
},
});
//可以把组件看成一个vue实例
Vue.component("my-button", {
template: `
<div>
<p>{{inner}}</p> //正确,组件可以使用自己的数据
<p>{{outer}}</p> //报错,组件无法使用其他组件的数据
</div>
`,
data() {
return {
inner: '我是组件的数据'
};
}
});
vue-devTool浏览器插件的使用
vue-devtools是一款基于chrome游览器的插件,用于调试vue应用,这可以极大地提高我们的调试效率。
默认插件必须在http协议下打开才能看到。
插件详细信息—-》允许访问文件网址 ,这样file协议打开也能看到效果了
组件通讯
因为组件是一个独立的个体,组件无法使用到外部的数据
但是在真实开发中,多个组件之间是需要相互使用彼此的数据的,因此需要使用组件通讯的技术,让组件之间能够相互传值。
组件通讯分为三类
- 父组件传递值给子组件
- 子组件传递值给父组件
- 非父子组件之间的传值
组件通讯-父传子
- 定义两个组件
Vue.component("parent", {
template: `
<div class="parent">
<p>这是父组件</p>
<son></son>
</div>
`,
data () {
return {
car: '玛莎拉蒂',
month: 1000000
}
}
});
Vue.component("son", {
template: `
<div class="son">
<p>这是子组件</p>
</div>
`
});
<div id="app">
<parent></parent>
</div>
- 组件的通讯
1. 在父组件的模版中,给子组件增加一个自定义的属性。
<son :car="car"></son>
2. 子组件通过props属性进行接收
//接收父组件传递过来的值
props: ['car']
3. 子组件使用父组件传递过来的值
template: `
<div class="son">
<p>这是子组件</p>
<p>这是父组件传递过来的值----{{car}}</p>
</div>
`,
- 思考,怎么获取父组件的money
注意:props负责获取父组件的传递过来的,props中的值是只读的,不允许修改
组件通讯-子到父
参考链接:https://blog.csdn.net/jsxiaoshu/article/details/79058940
整体思路
1. 父组件给子组件注册一个自定义事件
2. 子组件触发这个自定义事件,触发事件时把数据传递给父组件
- 父组件给子组件注册事件
<son @fn='getData'></son>
methods: {
//1. 父组件中定义了一个方法,用于获取数据
getData () {
console.log("父组件中提供的方法");
}
}
- 子组件触发自定义事件,并且把要传递的数据作为参数进行传递
//$emit可以出发当前实例的事件
this.$emit('getData', this.car);
- 父组件获取值
methods: {
//1. 父组件中定义了一个方法,用于获取数据
getData (skill) {
console.log("父组件中提供的方法", skill);
this.skill = skill;
}
}
案例-todomvc
组件通讯-非父子
非父子组件之间通过一个空的Vue实例来传递数据。
const bus = new Vue(); //bus:公交车 事件总线
- 核心逻辑
组件A给组件B传值:
1. 组件A给bus注册一个事件,监听事件的处理程序
2. 组件B触发bus上对应的事件,把 值当成参数来传递
3. 组件A通过事件处理程序获取数据
- 组件A给bus注册事件
//rose在组件创建的时候,给bus注册了一个事件
created () {
bus.$on("get", (msg)=>{
console.log("这是rose注册的事件", msg);
this.msg = msg;
});
}
- 组件B触发bus的事件
<button @click="send">表白</button>
methods: {
send() {
bus.$emit("get", this.msg);
}
}
- 组件A通过事件处理程序可以获取到传递的值
bus.$on("get", (msg)=>{
console.log("这是rose注册的事件", msg);
this.msg = msg;
});
注意点:1. 必须是同一辆公交车 2. 注册的事件和触发的事件必须保持一致
案例:开关灯案例
bus是一种通用的组件通讯方案
我们有三种组件通讯的方案
1. 父传子
2. 子传父
3. 非父子(bus)
其实bus方案也适用于父传子和子传父的方案
slot插槽
当组件中某一项需要单独定义,那么就应该使用solt
单个slot
除非子组件模板包含至少一个
<slot>
插口,否则父组件的内容将会被丢弃 ,当子组件模板只有一个没有属性的 slot 时,父组件整个内容片段将插入到 slot 所在的 DOM 位置,并替换掉 slot 标签本身。
在组件的模版中定义slot
插槽
Vue.component("modal", {
template: `
<div class="modal">
<p>温馨提示</p>
<div>
<slot></slot>
</div>
<button>关闭</button>
</div>
`,
});
父组件传值
<modal>你确定要退出系统吗?</modal>
<modal>你确定要删除这个内容吗?</modal>
具名插槽
如果一个组件中想使用多个slot那么此时就应该使用具名slot。
Vue.component("modal", {
template: `
<div class="modal">
<slot name="header"></slot>
<slot name="content"></slot>
<slot name="footer"></slot>
</div>
`,
});
<modal>
<p slot="header">温馨提示</p>
<div slot="content">你要删除内容吗</div>
<button slot="footer">关闭</button>
</modal>
<modal>
<p slot="header">提示</p>
<div slot="content">你要删除这条记录吗</div>
<button slot="footer">开启</button>
</modal>