响应式数据的说明

响应式数据: 把data中的数据挂到vm身上,vm身上的这个数据其实就是响应式的

一旦数据发生了改变,页面中的内容也会跟着改变

动态添加的数据是无效的以及$set的使用

  • data中的数据一定要先声明,再使用,动态给对象添加的属性不是响应式的
  1. <div id="app">
  2. <p>{{person.name}}---{{person.age}}---{{person.gender}}</p>
  3. </div>
  4. <script src="vue.js"></script>
  5. <script>
  6. var vm = new Vue({
  7. el: '#app',
  8. data: {
  9. person: {
  10. name: 'zs',
  11. age: 18
  12. }
  13. }
  14. })
  15. </script>
  16. // 动态给data中添加的数据是无效的
  17. // 在vue实例创建的时候,会把data中的数据绑定到vm上,所以data中的数据是响应的
  18. // 但是动态绑定的数据不是响应是的。
  19. vm.person.gender = '男'
  • 如果想要动态绑定数据,并且是响应式的,需要使用vm.$set方法
  1. 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

目的:为了提高渲染的性能

  1. clickFn () {
  2. // 数据变化了, view中的内容也要跟着变
  3. this.msg = '你好啊,vue1'
  4. this.msg = '你好啊,vue2'
  5. this.msg = '你好啊,vue3'
  6. this.msg = '你好啊,vue4'
  7. this.msg = '你好啊,vue5'
  8. // 为什么:DOM操作是非常消耗性能的,简单DOM的操作能够提升我们的性能,如果每次操作数据,都立即更新DOM,无疑性能会非常的低,所以vue中会等待数据都修改完成
  9. let result = document.querySelector('p').innerHTML
  10. console.log(result)
  11. }
  • $nextTick方法会在DOM更新之后执行
  1. // 在实际开发中,有可能需要在数据改变之后,获取到更新之后的DOM数据
  2. // 这个时候可以使用 $nextTick函数
  3. // 当vue更新完DOM后,会自动调用$nextTick函数,确保在这个函数中可以获取到DOM结构是最新的
  4. this.$nextTick(function() {
  5. let result = document.querySelector('p').innerHTML
  6. console.log(result)
  7. })

组件化开发

组件化的概念

模块化

  1. 概念:
    1. 一个js文件就是一个模块,把一个独立的功能写到一个单独的js文件,称之为一个模块。
  2. 模块化的好处
    1. 容易维护
    2. 可以复用
  3. 假如没有模块化:
    a. 把会所有的js逻辑写到一个js文件(app.js),难以维护
  4. 模块化仅仅是针对于js的

组件化

  1. 概念:
    1. 组件针对于一个独立的结构 样式 js
    2. 一个应用(页面)完全由组件构成,,,一个组件会包含有结构,样式,功能(js)
  2. 组件化的优点:
    1. 复用
    2. 容易维护

vue组件分类

vue组件分为全局组件和局部组件

  • 全局组件:在所有的vue实例中都能使用
  • 局部组件:只有在当前vue实例中能够使用

全局组件

组件是一个可以复用的vue实例

定义组件

  1. Vue.component(参数一,参数二)
  • 参数1:组件名
  • 参数2:组件的配置项, 可以配置和vue实例相同的配置 methods computed watch template
    1. Vue.component('demo', {
    2. template: `<div>
    3. <div>你好啊,我是一个组件</div>
    4. <button>按钮</button>
    5. </div>`
    6. })

使用组件

把组件当成html标签来使用

  1. <demo></demo>

注意点

  1. 定义的组件名不能是html原先就存在的标签。
  2. 组件的模版只能有一个根元素
  3. 组件中不会指定el,,但是一定会指定template,模版

局部组件

  • 全局组件:将来在所有的vue实例中都可以使用
  • 局部组件:只有在当前实例中可以使用
    1. const vm = new Vue({
    2. el: '#app',
    3. data: {
    4. msg: 'hello vue'
    5. },
    6. // 提供局部组件
    7. components: {
    8. demo: {
    9. template: `<div>这是一个局部组件</div>`
    10. }
    11. }
    12. })

组件和vue实例的不同点

  1. vue实例有el参数
  2. 组件要提供template参数
  3. 组件中的data必须是一个函数,函数内部需要返回一个对象。
  1. <script>
  2. Vue.component('hello', {
  3. template: `
  4. <div>
  5. <button @click="fn">加10块</button>
  6. <p>我有 {{money}} 块钱</p>
  7. </div>
  8. `,
  9. methods: {
  10. fn() {
  11. console.log('哈哈哈')
  12. this.money += 10
  13. }
  14. },
  15. created() {
  16. console.log('我是created钩子函数')
  17. },
  18. //组件中的data必须是一个函数,返回的对象在每个组件中都是独立的
  19. data: function() {
  20. return {
  21. money: 100
  22. }
  23. }
  24. })
  25. </script>

组件是特殊的vue实例

可以将组件看成是一个vue的实例,因此,在vue实例中能配置的属性,在组件中依旧能够配置。

比如:data,method,watch,computed,钩子函数等

注意:组件中data属性必须是一个函数,返回值才是data的数据

  1. //可以把组件看成一个vue实例
  2. Vue.component("my-button", {
  3. template: `
  4. <div>
  5. <h1 @click="add">我是一个标题</h1>
  6. <p>{{msg}}</p>
  7. </div>
  8. `,
  9. //组件可以通过data属性提供组件自己的数据,注意,组件的data参数必须是一个函数,不能是用一个对象。
  10. data() {
  11. return {
  12. msg: 'hello vue'
  13. };
  14. },
  15. methods: {
  16. add() {
  17. console.log("哈哈");
  18. }
  19. }
  20. });
  • 组件是一个独立封闭的个体,组件之间的数据是无法相互使用的
  1. var vm = new Vue({
  2. el: '#app',
  3. data: {
  4. outer: '我是实例的数据'
  5. },
  6. });
  7. //可以把组件看成一个vue实例
  8. Vue.component("my-button", {
  9. template: `
  10. <div>
  11. <p>{{inner}}</p> //正确,组件可以使用自己的数据
  12. <p>{{outer}}</p> //报错,组件无法使用其他组件的数据
  13. </div>
  14. `,
  15. data() {
  16. return {
  17. inner: '我是组件的数据'
  18. };
  19. }
  20. });

vue-devTool浏览器插件的使用

vue-devtools是一款基于chrome游览器的插件,用于调试vue应用,这可以极大地提高我们的调试效率。

默认插件必须在http协议下打开才能看到。

插件详细信息—-》允许访问文件网址 ,这样file协议打开也能看到效果了

组件通讯

因为组件是一个独立的个体,组件无法使用到外部的数据

但是在真实开发中,多个组件之间是需要相互使用彼此的数据的,因此需要使用组件通讯的技术,让组件之间能够相互传值。

组件通讯分为三类

  • 父组件传递值给子组件
  • 子组件传递值给父组件
  • 非父子组件之间的传值

组件通讯-父传子

  • 定义两个组件
  1. Vue.component("parent", {
  2. template: `
  3. <div class="parent">
  4. <p>这是父组件</p>
  5. <son></son>
  6. </div>
  7. `,
  8. data () {
  9. return {
  10. car: '玛莎拉蒂',
  11. month: 1000000
  12. }
  13. }
  14. });
  15. Vue.component("son", {
  16. template: `
  17. <div class="son">
  18. <p>这是子组件</p>
  19. </div>
  20. `
  21. });
  22. <div id="app">
  23. <parent></parent>
  24. </div>
  • 组件的通讯
  1. 1. 在父组件的模版中,给子组件增加一个自定义的属性。
  2. <son :car="car"></son>
  3. 2. 子组件通过props属性进行接收
  4. //接收父组件传递过来的值
  5. props: ['car']
  6. 3. 子组件使用父组件传递过来的值
  7. template: `
  8. <div class="son">
  9. <p>这是子组件</p>
  10. <p>这是父组件传递过来的值----{{car}}</p>
  11. </div>
  12. `,
  • 思考,怎么获取父组件的money

注意:props负责获取父组件的传递过来的,props中的值是只读的,不允许修改

组件通讯-子到父

参考链接:https://blog.csdn.net/jsxiaoshu/article/details/79058940

整体思路

  1. 1. 父组件给子组件注册一个自定义事件
  2. 2. 子组件触发这个自定义事件,触发事件时把数据传递给父组件
  • 父组件给子组件注册事件
  1. <son @fn='getData'></son>
  2. methods: {
  3. //1. 父组件中定义了一个方法,用于获取数据
  4. getData () {
  5. console.log("父组件中提供的方法");
  6. }
  7. }
  • 子组件触发自定义事件,并且把要传递的数据作为参数进行传递
  1. //$emit可以出发当前实例的事件
  2. this.$emit('getData', this.car);
  • 父组件获取值
  1. methods: {
  2. //1. 父组件中定义了一个方法,用于获取数据
  3. getData (skill) {
  4. console.log("父组件中提供的方法", skill);
  5. this.skill = skill;
  6. }
  7. }

案例-todomvc

组件通讯-非父子

非父子组件之间通过一个空的Vue实例来传递数据。

  1. const bus = new Vue(); //bus:公交车 事件总线
  • 核心逻辑
  1. 组件A给组件B传值:
  2. 1. 组件Abus注册一个事件,监听事件的处理程序
  3. 2. 组件B触发bus上对应的事件,把 值当成参数来传递
  4. 3. 组件A通过事件处理程序获取数据
  • 组件A给bus注册事件
  1. //rose在组件创建的时候,给bus注册了一个事件
  2. created () {
  3. bus.$on("get", (msg)=>{
  4. console.log("这是rose注册的事件", msg);
  5. this.msg = msg;
  6. });
  7. }
  • 组件B触发bus的事件
  1. <button @click="send">表白</button>
  2. methods: {
  3. send() {
  4. bus.$emit("get", this.msg);
  5. }
  6. }
  • 组件A通过事件处理程序可以获取到传递的值
  1. bus.$on("get", (msg)=>{
  2. console.log("这是rose注册的事件", msg);
  3. this.msg = msg;
  4. });

注意点:1. 必须是同一辆公交车 2. 注册的事件和触发的事件必须保持一致

案例:开关灯案例

bus是一种通用的组件通讯方案

我们有三种组件通讯的方案

  1. 1. 父传子
  2. 2. 子传父
  3. 3. 非父子(bus
  4. 其实bus方案也适用于父传子和子传父的方案

slot插槽

当组件中某一项需要单独定义,那么就应该使用solt

单个slot

除非子组件模板包含至少一个 <slot> 插口,否则父组件的内容将会被丢弃 ,当子组件模板只有一个没有属性的 slot 时,父组件整个内容片段将插入到 slot 所在的 DOM 位置,并替换掉 slot 标签本身。

在组件的模版中定义slot插槽

  1. Vue.component("modal", {
  2. template: `
  3. <div class="modal">
  4. <p>温馨提示</p>
  5. <div>
  6. <slot></slot>
  7. </div>
  8. <button>关闭</button>
  9. </div>
  10. `,
  11. });

父组件传值

  1. <modal>你确定要退出系统吗?</modal>
  2. <modal>你确定要删除这个内容吗?</modal>

具名插槽

如果一个组件中想使用多个slot那么此时就应该使用具名slot。

  1. Vue.component("modal", {
  2. template: `
  3. <div class="modal">
  4. <slot name="header"></slot>
  5. <slot name="content"></slot>
  6. <slot name="footer"></slot>
  7. </div>
  8. `,
  9. });
  1. <modal>
  2. <p slot="header">温馨提示</p>
  3. <div slot="content">你要删除内容吗</div>
  4. <button slot="footer">关闭</button>
  5. </modal>
  6. <modal>
  7. <p slot="header">提示</p>
  8. <div slot="content">你要删除这条记录吗</div>
  9. <button slot="footer">开启</button>
  10. </modal>