Vue 常见知识点集合

637 阅读10分钟

ref的使用

一般用ref来拿到某一个DOM元素,虽然Vue不推荐来操作DOM,但是复杂情况除外。 如下例:子组件复用,通过点击子组件来修改父组件中的值,用到的知识点有子组件向父组件传值,和获取子组件的数据。

refs只能获取到子组件,不能获取到孙级组件,refs只能获取到子组件,不能获取到孙级组件,refs不是响应式的,所以避免在计算属性或者模板中去使用$refs.

  <div id="app">
    <couter @change="handleChange" ref="one"></couter>
    <couter @change="handleChange" ref="two"></couter>
    <div>{{total}}</div>
  </div>
Vue.component('couter', {
      template: '<div @click="handleClick">{{number}}</div>',
      data() {
        return {
          number: 0
        }
      },
      methods: {
        handleClick() {
        //  this.number++ 控制的是子组件数据
          this.number++;
          // 数据改变的同时,触发`change`事件,父组件就会监测到这个事件,就会执行对应的方法
          this.$emit('change')
        }
      }
    })
    var vm = new Vue({
      el: '#app',
      data: {
        total: 0
      },
      methods: {
        handleChange() {
        // 由于子组件通过触发change事件来执行了这个函数,所以子组件每点击一次,父组件都要重新计算total
          this.total = this.$refs.one.number + this.$refs.two.number
          // this.$refs.one 就是这个Vue实例组件
        }
      }
    })

子组件想改变父组件传过来的值

思路:父组件传过来的值,子组件不能修改(避免数据造成混乱),可以将父组件传过来的值,copy一份,然后进行修改。

<div id="app">
    <couter number="0"></couter>
</div>
var couter = {
      template: '<div @click = "handleClick">{{couterNumber}}</div>',
      props: ['number'],
      data() {
        return {
          couterNumber: this.number
        }
      },
      methods: {
        handleClick() {
          this.couterNumber++
          // 不直接修改父组件传过来的值,应当将传过来的值赋值一份,操作复制的那份
        }
      }
    }
    var vm = new Vue({
      el: "#app",
      components: {
        couter
      }
    })

1.png

给组件绑定原生事件

思路:在子组件里面的事件添加.native事件修饰符,在父组件中的子组件上写click事件不会起作用。

<div id="app">
// 在子组件标签上绑定点击事件,不会起作用。
    <couter @click.native="handleClick"></couter>
</div>

跨组件之间的传值:发布与订阅

思路:兄弟组件想要相互控制,相当于相互通信。设置一个公共bus,该bus包含所有vue实例的方法,因为涉及到$emit来触发事件和$on方法来监听事件。

<div id="app">
        <Child content="hello"></Child>
        <Child content="world"></Child>
</div>
        Vue.prototype.bus = new Vue()
      //  每一个Vue实例都会有这个bus属性,相当于每一个组件(包括子组件)都会有这个属性
        Vue.component('Child', {
            template: '<div @click="handleClickChild">{{myContent}}</div>',
            props: ['content'],
            data() {
                return {
                    myContent: this.content
                }
            },
            mounted() {
                // this指向当前组件
                var _this = this
                // 用$on来监测这个change事件,只要一触发就会执行
                this.bus.$on('change', function (val) {
                // 也要注意一下这个this的指向,是把当前组件的值更改,所以可以提前存一下this值。
                    _this.myContent = val
                })
            },
            methods: {
                handleClickChild() {
                // 因为每个组件都会有bus这个属性,而这个属性又是Vue的实例,所以都有$emit方法,触发change事件并传值
                // 注意bus是公共的。
                    this.bus.$emit('change', this.myContent)
                }
            },
        })
        var vm = new Vue({
            el: "#app",
        })

beforecCreate 生命周期函数中,所调用的函数都是未声明的,在created()中,声明了,要想调用函数,要在created()里面,否则函数会有is not undefined报错。

组件中的细节点

  1. 在标签中利用子组件渲染的时候,注意一些未知的bug,可以考虑一下is属性
  2. 子组件中的data必须是一个函数,而不是对象。是为了让每一个子组件都有一个独立的数据存储,不会出现多个子组件互相影响的情况。
  3. 通过$refs.ref来获取DOM节点,也可以在子组件上绑定ref来获取这个子组件的引用,里面的数据可以直接写this.$refs.ref.number(number是组件中data中的数据)
  4. 事件名一般采用动宾的形式,程序中出现的常量,可以单独写在文件前面,以大写表示。
  5. props参数校验,可配置哪些属性:(注意一下自定义校验器)

2.png

插槽的使用

插槽的使用场景用于:父组件通过子组件,向子组件中传递DOM标签,而子组件中通过<slot></slot>标签来接收传过来的内容。

5.png

具名插槽,在外围包含一个template标签,用v-slot来什么插槽名称.注意 v-slot 只能添加在 <template> 上 (只有一种例外情况),

 <div id="app">
    <Child>
      <template v-slot:"header">
        <p slot="header">
          <span>
            返回
          </span>
        </p>
      </template>
      <template v-slot:"footer">
        <p slot="footer">
          尾部
        </p>
      </template>
    </Child>
  </div>
Vue.component('Child', {
      template: `<div>
                   <div @click="handleClick">
                   <slot name='header'>默认值header</slot>
                    </div>
                   <p>hello world</p>
                   <slot name='footer'>默认值footer</slot>
                </div>`,
      methods:{
        // 注意 插槽不能绑定方法 可以在slot标签外放一个div,事件绑在div上
        handleClick(){
          console.log('返回')
        }
      }
    })
    // slot标签中可以设置默认值,不传标签过来的时候,以默认显示的内容来占位,传了DOM元素过来,直接替换,不管里面是啥
    var vm = new Vue({
      el:'#app'
    })

动态组件

顾名思义,就是动态的去渲染哪个组件<component :is=type></component>,其中的type为对应的组件名称,可以通过js来获得.可实现两个组建的切换显示

        <button @click="change('Child')">点击切换Child</button>
        <button @click="change('myfooter')">点击切换myfooter</button>
        <component :is="componentId"></component>
import Child from './childOne' 
import myfooter from './childTwo'

methods:{
    change(val){
           this.componentId = val
           // componentId写对应的组件名
     }
}

组件的刷新

<div @click="refresh">点击刷新组件</div> 
<myfooter v-if="isShow"></myfooter >
import myfooter from './myfooter'
export default {
	data(){
		isShow: true
	},
	methods:{
		refresh(){
			this.isShow = false;
			this.$nextTick(() => {  
                            //DOM更新之后,重新加载组件
       			this.isShow = true
      		})
		}
	},
        componets:{
		myfooter
	},
}

v-once指令

可以更好地展示一些静态内容,就是将一些静态内容只展示一次,再放在内存中,再次渲染的时候,直接在内存中获取.可以很好地提高性能.

computed 和 watch

html模板里面最好不要写逻辑性的代码,可以写在computed里面,在computed里面可以写get()和set()方法,来获取属性,或者设置属性。 watch侦听器可以侦听data里面的属性,属性一旦改变,就会立即执行。 比如:

watch:{
    letter () {
        console.log(this.letter) //这个letter是当前的这个data里面的当前值
    }
}
  • computed

    1. 支持缓存,只有依赖数据发生改变,才会重新进行计算,计算属性可用于快速计算视图(View)中显示的属性。这些计算将被缓存,并且只在需要时更新。computed是计算属性的; 它会根据所依赖的数据动态显示新的计算结果, 该计算结果会被缓存起来。computed的值在getter执行后是会被缓存的。如果所依赖的数据发生改变时候, 就会重新调用getter来计算最新的结果。
    2. 不支持异步,当computed内有异步操作时无效,无法监听数据的变化
    3. 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed
    4. 如果computed属性属性值是函数,那么默认会走get方法;函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个set方法,当数据变化时,调用set方法。
    5. computed和methods:计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。响应式依赖:data和props里面的数据?
       computed: {
               fullName: { 
                   // getter
                   get: function () { 
                       return this.firstName + ' ' + this.lastName 
                   }, 
                   // setter
                   set: function (newValue) {
                       var names = newValue.split(' ') 
                       this.firstName = names[0] 
                       this.lastName = names[names.length - 1]
                   }
               }
           }
    

    获取属性,默认调用get方法。

  • watch

    当需要在数据变化时执行异步开销较大的操作时,这个方式是最有用的。

    大部分的使用场景是,在输入框绑定v-model的时候,监测里面的值,在回调中会传入newVal和oldVal两个参数。可以参考官网的例子,可以在watch里面发异步请求,而computed不可以。

        <div>
            <van-field 
                @focus="focus('num_s',800,350,'up')" 
                @blur="blur('up')" 
                placeholder="现金值" 
                v-model="begin_cash"
                maxlength="7" 
                type="number" 
                input-align="center" 
            />
        </div>
    
    watch: {
            begin_cash(newVal, oldVal) {
                    if (newVal < 0) {
                        this.$toast('输入金额不能为负数!');
                        this.begin_cash = '';
                    }
                    if (newVal.toString().indexOf('.') === 0) {
                        this.begin_cash = '';
                    }
                    if (newVal === '' && oldVal.toString().indexOf('.') > 0) {
                        this.begin_cash = oldVal;
                        return;
                    }
                    if (newVal) {
                        newVal = newVal.toString();
                        var pointIndex = newVal.indexOf('.');
                        if (pointIndex > 0 && (newVal.length - pointIndex) > 3) {
                            this.begin_cash = oldVal;
                            return;
                        }
                    }
    }
    


    作者:夏末_阳光依然耀眼
    链接:juejin.cn/post/699992… 来源:掘金
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

vue中数组更新时的注意点

比如,要改变data中的数据,如果这个数据是数组格式,数据本身可能已经更新了,但是视图不会更新。

  <div id="app">
    <p v-for="item in arr">{{item}}</p>
    <!-- 还是打印出 1 2 3 -->
  </div>
let vm = new Vue({
      el: '#app',
      data: {
        arr: [1, 2, 3]
      },
      created() {
        setTimeout(() => {
          this.arr[0] = 100
          console.log(this.arr) // [100,2,3],但是视图不会更新
        }, 2000)
      }
    })

vue钩子函数

  • beforeCreate(实例创建前)

    实例组件刚开始创建,元素dom和数据都还没有初始化

    应用场景:可以在这加个loading事件;可以混入公共逻辑,Vue.mixin()

  • created(实例创建后) 数据data已经初始化完成,方法也已经可以调用,但是dom未渲染,在这个周期里面如果进行请求是可以改变数据并渲染,由于dom未挂载,请求过多或者占用时间过长会导致页面线上空白。

    应用场景:在这结束loading,还做一些初始化,实现函数自执行

  • beforeMoute(元素挂载前) dom未完成挂载,数据初始化完成,但是数据的双向绑定还是{{}},这是因为vue采用了虚拟dom技术。

  • render函数执行 创建虚拟DOM,将它进行挂载。

  • mouted(元素挂载后) 数据和dom都完成挂载,在上一个周期占位的数据把值渲染进去,一般请求会放在这个地方,因为这边请求改变数据之后刚好能渲染。

  • beforeUpdate(实例更新前) 只要是页面数据改变了都会触发,数据更新之前,页面数据还是原来的数据,当你请求赋值一个数据的时候就会执行这个周期,如果没有数据改变不执行。

  • updated(实例更新后) 第一次加载数据也会执行,只要是页面数据改变了都会触发,数据更新完毕,页面的数据是更新完成的,beforeUpdatedupdated要谨慎使用,因为页面更新数据的时候都会触发,在这里操作数据很影响性能和死循环。 注意:beforeUpdateupdated用的都不多,可以用watch来监测。

  • beforeDestory(实例销毁前) 实例销毁之前调用,在这一步,实例仍然完全可用。 应用场景:可以做自定义事件的解绑,去除DOM事件的绑定,以及定时器的清理。

  • destory(实例销毁后) vue实例销毁后调用,调用后,Vue实例指示的所有内容都会解除绑定,所有的事件监听器都会被移除,所有的子实例也会被销毁。 注意:切换路由,组件会自动执行beforeDestorydestory钩子函数。如果切换的很频繁,可以用<keep-alive>来做缓存。

父子组件执行的顺序

同步和异步组件,父子组件执行的钩子函数顺序不同。

  • 同步组件:import Page from '@/components/page'

    生命周期钩子函数执行顺序: 父组件的beforeCreatecreatedbeforeMount --> 所有子组件的beforeCreatecreatedbeforeMount --> 所有子组件的mounted --> 父组件的mounted

  • 异步组件:

    1. const Page = () => import('@/components/page')
    2. const Page = resolve => require(['@/components/page'], page)

    生命周期钩子函数执行顺序: 父组件的beforeCreate、created、beforeMount、mounted --> 子组件的beforeCreate、created、beforeMount、mounted

Vue.mixin()

混入逻辑:是将各个钩子函数,或者data数据组成一个数组,依次执行。抽离公共方法和编写插件 [beforeCreate,beforeCreate],Vue.mixin()中可以增加一些钩子函数。

Vue.mixin({
    beforeCreate(){
        console.log('1111111111') // 最先执行
    }
})

缺陷:导致数据来源不明,Vue3用compositionApi来解决这个问题。

<transition>动画:切换显示与隐藏效果

<transiton></transition>标签里面包裹要切换的元素

    <transition name="fade">
      <!-- transition把要切换动画的元素包裹起来 -->
      <div 
        style="width:100px;height:100px" 
        class="box" 
        v-show="isShow"
        >
      </div>
    </transition>
    <button 
      style="width:100px;height:40px"
      @click="change"
      >点我切换
    </button>
.box{
  background red
}
// 动画一开始的一瞬间,比如显现的一瞬间
.fade-enter{
  background green
}
// 持续时间为2.fade-enter-active{
  transition all 2s linear
}
// 最后的颜色为蓝色
.fade-enter-to{
  background blue
}
// 总体效果就是:green => blue(持续2s),最后变成红色
// 动画结束之后,会移除所有样式
.fade-leave{
  // 看不出效果
}
.fade-leave-active{
  transition all 2s linear
}
.fade-leave-to{
  background pink
}
// // 总体效果就是:red => pink(持续2s),最后隐藏
// 结束之后直接隐藏

注意:v-指令可能会重名,如果动画很多的情况下,所以可以取名字。

组件化开发

  • 为什么组件化开发?

    1. 实现组件复用
    2. 可以方便维护代码
    3. 只用组件级别更新,会给每一个组件给一个watcher,不用所有组件都去看,哪个组件出现问题,就去查找该组件。
    4. 能抽离的就尽早抽离,可以减少更新。
  • 组件实例化过程

    会将当前传入的对象,创建出一个Vue实例来。 将当前组件的html,css,js放在一起。

    Vue.component('Child', {
            template: '<div @click="handleClickChild">{{myContent}}</div>',
            data() {
                return {
                    myContent: this.content
                }
            },
    

    Vue.component传入一个对象,会默认调用Vue.extend()

    Vue.extend(
        {
            template: '<div @click="handleClickChild">{{myContent}}</div>',
            data() {
                return {
                    myContent: this.content
                }
            }
    )
    

    data为什么要是一个函数? 函数可以返回一个对象,在每一个组件中,返回的值不同,可以避免data中的数据冲突。

父子组件传值(单向数据流)

  • Props的大小写

    在父组件中以短横线来传递

    <!-- 在 HTML 中是 kebab-case 的 --> 
    <blog-post post-title="hello!"></blog-post>
    

    在子组件中用驼峰命名法来接收

    Vue.component('blog-post', { 
    // 在 JavaScript 中是 camelCase 的 
        props: ['postTitle'],
        template: '<h3>{{ postTitle }}</h3>' 
    })
    

    注意: 任何类新的数据都可以传给prop

  • 想把整个对象都传给prop

    post: { 
        id: 1, 
        title: 'My Journey with Vue' 
    }
    <blog-post v-bind="post"></blog-post>
    // 相当于
    <blog-post 
        v-bind:id="post.id" 
        v-bind:title="post.title">
    </blog-post>
    

    注意:父子传递,默认是单向数据流,子组件更改,父组件是不会更改的,如果传递给prop是对象或者数组,则传递的是引用(地址),在子组件中更改数据,(不推荐),则会影响父组件。

  • 也可以传function给子组件,子组件直接使用,像props属性一样,直接使用。并且传对应的值给父组件 在parent父组件中:

     <div>
       parent
       <son1 :money="mny" :change-money="changeMoney"></son1>
     </div>
    
     data(){
       return {
         mny: 100
       }
     },
     methods:{
       changeMoney(val){
         console.log(val)
       }
     },
     components: {
       son1
     }
    

    在子组件son1中:

     <div>
       {{money}}
       <button @click="changeMoney(500)">点我</button>
       <!-- 点击直接调用父组件传过来的方法 -->
     </div>
    
    props: {
       money: {
         type: Number
       },
       changeMoney: {
         type: Function, // 注意这里是大写
         default: () => {}
       }
     }
    

父子组件传值(同步数据)

若想子组件通过$emit把值传给父组件,父组件又可以同步给子组件,可以这样:

在父组件中:

<!-- 父组件把值改了,顺便又传给了子组件,就实现了父子组件的同步更新 -->
<ShopList :value="mny" @input="val => mny = val"></ShopList>
<!-- 这里注意一下,input是绑定给子组件的,绑定的对应时间是啥,是父组件的,所以下面的$emit方法触发的,也是自己组件本身的方法。-->
data() {
  return {
     mny: 100
  };
},

在子组件中:

<button @click="go">点击按钮</button>
{{value}}
props:{
  value:Number
},
methods: {
    go(){
       // 将值传给父组件
        this.$emit('input',500)
     }
}

可以直接优化为:

<ShopList v-model="mny"></ShopList>

这个valueinput为固定搭配,v-model是他俩的语法糖,这里的500会替换掉v-model中的mny属性,同时更新到了子组件中。

-自定义v-model属性和事件名

有时候不想使用value+input组合,可以自己更改名字,如

<HomeList v-model="mny"></HomeList>
model:{
 prop: 'mny', // 自定义属性名
 event: 'handleClick' // 自定义事件名
},
props:{
 mny:{
   type: Number // 接收数据名
 }
},
methods:{
 goto(){
   this.$emit('handleClick',300)
 }
}

又有一种简洁的语法:

<HomeList :money.sync="mny"></HomeList>
// 等价于
<HomeList :money="mny" @update:money="val => mny = val"></HomeList>
// 在子组件的$emit中,也以update:money为事件名把值传给父组件。

注意:这个.sync语法糖在子组件中接收数据和事件的时候有具体的格式,注意。

这个详细案例可以看vue.js官网,在这里我自己认为在子组件上绑定v-model的含义的,想让父子组件数据同步,一般子组件传数据给父组件就无了,现在子组件也想保持数据同步,就可以想到用v-model.

父孙组件传值

  1. 父 => 子 => 孙,props传值,性能差,速度慢
  2. 利用provideinject,父组件中使用provide将当前父组件实例暴露出去,孙级组件用inject来接收。注意不要在子组件中直接更改父组件数据!可以通过调用方法来改变数据。通常是在自己写的库中进行使用。
    • provideinject的使用示例

      这里有三级组件,parent,son2,grandson

      parent.vue:

        <div>
          parentdata属性:
          {{mny}}
          <son2></son2> 
        </div>
      
      import son2 from './son2.vue'
      export default {
          provide() {
              return {
                parent: this // 将当前实例暴露出去
              }
            },
            data() {
              return {
                mny: 100
              }
            },
      }        
      

      son2.vue:

        <div> 部分代码省略
           son2引入的孙组件:
          <grandson></grandson>
        </div>
      

      grandson.vue:

        <div>
          孙级组件直接用parent属性:
          {{parent.mny}} 
        </div>
      
      export default {
        inject: ['parent'] // 注入parent实例
      }
      

      111.png

  3. $parent,$children,获取当前组件的父组件和子组件,可以直接触发儿子的事件或者父亲的事件。尽量少用,不知道父级是谁,子是谁
    • $parent,$children使用示例

      在父组件parent.vue中给son2绑定方法,准备在孙级组件grandson中去触发

      parent.vue:

        <div>
          <son2 @eat="eat()"></son2> 
          // 给son2绑定eat方法
        </div>
      
      eat(){
        console.log('触发了son2上的eat方法')
      }
      

      grandson.vue:

      <button @click="$parent.$emit('eat')">
          点击触发父组件的eat方法,当前的父组件是son2,相当于触发son2上的eat方法。
      </button>
      
  4. 可以使用$dispatch,一层一层的找父级元素,直到该父级有这个事件。可以说是$parent的一种改良方案。
  • 改写组件原型上的$dispatch方法
   // 向上派发这个事件,只要组件上绑定过这个事件,就会触发
   // 想要某个特定组件触发,传入一个参数,这个参数是组件名称
  Vue.prototype.$dispatch = function(eventName,componentName, value){
    let parent = this.$parent
    while(parent){
      if(parent.$options.name === componentName){
        parent.$emit(eventName, value) // 触发某一组件的eventName事件。
        return
      }
      parent = parent.$parent
    }
  }
    <HomeList v-model="ischecked" @eat="eat"></HomeList>

这个是给HomeList组件绑定了一个eat事件。 5. 通过父组件,给具体子组件派发事件$broadcast

// 向下通知某个触发某个事件,可能会有多个子组件,所以会涉及到循环
Vue.prototype.$broadcast = function (eventName, componentName, value) {
  let children = this.$children // 获取的是个数组,直接子元素,不包含孙级
  // 递归函数,一层一层地找
  function broadcast(children) {
    for (let i = 0; i < children.length; i++) {
      if (children[i].$options.name === componentName) {
        children[i].$emit(eventName, value)
        return
      } else {
        if (children[i].$children) {
          broadcast(children[i].$children)
        }
      }
    }
  }
  broadcast(children)
}
// 在父级的父级组件里,想要找绑定了drink事件的子组件
<button @click="$broadcast('drink','grandson',300)">
      点击触发孙级组件的broadcast
</button>
// 在父级组件里
<grand-son @drink="drink"></grand-son>
drink(val){
      console.log('=====================',val)
}
// =====================,300
  1. $attrs$listeners来传递所有属性和方法,传递属性: v-bind,传递方法:v-on

在App.vue里面,给parent组件传了属性和方法

  <div style="height:30px">
    <parent @sing="sing" a="111"></parent>
  </div>

在parent组件中,可以直接使用这些属性和方法,也可以把属性和方法传给下一个孙级组件son1.

<div>
    属性:{{$attrs}} // {"a":"111"}
    <son1 v-bind="$attrs"></son1>
    <son1 v-on="$listeners"></son1>
</div>
mounted(){
    console.log(this.$listeners) // 有sing方法
 },

在son1组件中

mounted(){
    console.log('son1',this.$listeners) // 有sing方法
},

这样就实现了父 => 子 => 孙,数据及方法传递

  • 关于$attrs$listeners来传递所有属性和方法注意的地方
    1. $attrsprops声明的属性是互斥的,相当于你在props声明的属性是不会出现在$attrs对象里面的。
    2. $attrs是响应式的
    3. props没有声明的属性,但是父级组件又传过来了,这些属性会默认渲染到传给这个组件的最外层的div标签上,如果不想要添加到DOM上,可以直接在组件中声明:inheritAttrs: false,
    4. $listeners在html模板中看不到,可以在钩子函数中看到,$attrs可以在html和钩子中都可以看到。
    5. $attrs$listeners传递的是所有的属性和方法。
  1. ref直接引用组件的值和方法

    比如一个弹框组件,里面暴露了一些属性和方法,供外界使用,局限性就在于你要向外提供一些方法。

兄弟组件传值

两种方法:

  1. 找到共同的父亲
  2. eventBus,发布订阅模式,在任何组件中去订阅事件($on()),也可以在任何组件触发($emit()) 注意 :
  3. $on()$emit()是要对一个Vue实例而言的,相当于操作的是同一个this
  4. $on()$emit()触发和监听的事件名要相同。
  5. 缺陷: 如果项目庞大,事件名可能会重名,比如一个$emit(),可能会触发多个$on(),一呼百应。只适合小规模通信。

案例:子组件如何监听父组件中的mounted?

就可以在子组件中去监听事件,等父亲搞完了,在去$emit()这个事件

main.js中:

Vue.prototype.$bus = new Vue() // 创建一个公共实例

在子组件中:

mounted() {
   this.$bus.$on('事件名', function(params){
       console.log('监听到了',params)
   }) 
}

在父组件中:

mounted() {
   // ...
   this.$bus.$emit('事件名', 111) 
}
//子组件感应到触发了事件,立即执行回调函数
// 打印:监听到了',111

扩展:$off:移除事件监听,一般是放在beforeDestory()钩子函数中

beforeDestory() {
     this.$bus.$off('事件名',function(){...})
}

在子组件中,如果需要等父组件mounted同步任务执行完,再去执行子组件mounted,可以在子组件中增加$nextTick()函数,在同步组件中,一般是要把子组件中的mounted执行完,再去执行父组件中的mounted的,用这个方法,可以解决,但是情况得是同步任务,还有一个解决方案是,把父组件中的mounted方法移到created里面,这肯定就先执行了。

插槽slot

在一个组件中放slot标签,感觉是占了一个位置,而这个位置上的样式,可以在外面套一个div,先把样式写死,传过来的是啥,由父组件决定传啥过来。

在myDialog组件中:

<template>
  <div>
    <!-- 定义了两个slot样式,提前写死了样式 -->
    <div class="header">
      <slot name="header"></slot>
    </div>
    我是弹框组件
    <div class="footer">
      <slot name="footer"></slot>
    </div>
  </div>
</template>

在父组件中引用myDialog组件,以下为老版本写法,部分代码省略 老版本:

<template>
  <div>
    <myDialog>
      <div slot="header">header</div>
      <div slot="footer">footer</div>      
    </myDialog>
  </div>
</template>

新版本:

<myDialog>
        // 注意这里header和footer不加引号
        // 这里的mny是当前组件的数据,也会带到插槽里面去的。
      <template v-slot:header>header{{mny}}</template>
      <template v-slot:footer>footer</template>     
</myDialog>

这里相当于一个数组: [header:div,footer:div],在子组件中去查找对应的名字,也就是headerfooter,找到了,就把对应的slot替换成了div.

作用域插槽

相当于是想把子组件的数据带到父组件中,利用插槽来传递,这个需要子组件提供一些属性和方法给父组件使用。

在父组件中:

<myDialog>
        // 父组件把属性传个子组件
      <template v-slot:header>header{{mny}}</template>
      <template v-slot:footer="slotProps">{{slotProps.myobj}}</template>     
</myDialog>

在myDialog组件中:

<template>
  <div>
    <!-- 定义了两个slot样式 -->
    <div class="header">
      <slot name="header"></slot>
    </div>
    我是弹框组件
    <div class="footer">
      // 子组件把属性传给父组件
      <slot name="footer" :myobj="obj"></slot>
      // 这里的obj是子组件的属性
    </div>
  </div>
</template>
export default {
  data () {
    return{
      obj:{
        a: 'a',
        b: 'b',
        c: 111,
        d: true
      }
      
    }
  }
}

slotProps相当于是传过来的属性的名字对象,属性都放在这个对象里面,这样通过作用域插槽,就实现了父子组件通信。

Vue.use(xxx)

use是vue的一个全局的api,会自动调用xxx插件的install方法,这个install方法是内部封装的必须要export default出来的。

Vuex

import store from './store.js'

new Vue({
  store // 在vue初始化过程中,注入了一个store属性,内部会将这个store属性注入到每一个vue组件的$store中
  render: h => h(App)
}).$mount('#app')

先创建new Vue,再创建App action

actions: {
// {commit},解构,也可以写直接写store
    asyncChange({commit}, payload){
      setTimeout(() => {
        commit('changeValue', payload)
      }, 1000); //最后也是通过提交mutation来修改数据
    }
  },

扩展:strict: true // 表示严格模式

注意

  1. mutation中,不要写异步,包括(setTimeOut),mutation更改状态,只能在同步下更改。

$nextTick

在修改数据之后,使用$nextTick可以获取更新后的DOM,数据可能变了,但是DOM还没有变。

使用场景: 一般轮播图数据是接口返回的,数据有了之后,需要DOM更新之后才会去定义new Swiper('xxx')

$attrs

是父组件给子组件传值,但是又不想用props来传,可以在子组件中,通过$attrs来获得,该属性是一个对象,并且这个传的值会放到子组件的HTML根元素上。在子组件上,可以使用inheritAttrs: false来进行控制,不需要放到组件的根元素上。

子组件又可以把attrs通过vbind="attrs通过v-bind="attrs"来传给子组件的内部组件。

label标签

label标签把input框包裹,可以点击文字的时候也可以选择input框

Array.prototype.splice()

arr.splice()会影响原数组

标签缓存组件

场景:想实现tabs切换,缓存数据的那种效果,首先看要不要求数据的实时性。 用法1:缓存整个组件,需要切换路由,从而切换组件

用法2:配合is属性,切换组件

子组件中的mounted钩子函数中去触发$emit写法改造

<template>  
    <List @hook:mounted="listenMounted" />  
</template>