Vue 学习整理(持续更新)

611 阅读8分钟

渐进式框架

  • 声明式渲染 -> 组件系统 -> 路由 -> 大规模状态管理 -> 构建工具

  • 优势

  • react:函数式编程

  • angular:模块,依赖注入,特殊形式的组件

  • 声明式渲染(核心)

  • {{data}} 使用模板语法,声明式的将数据渲染进DOM系统

  • 过程:

  • 新建一个vue实例,创建dom元素并给予一个id,根据id将dom元素挂载到dom实例上:el:"#app"

  • 创建组件实例的data属性,给data新增一个age属性,使用模板语法将age声明到dom中

  • 响应式数据绑定(MVVM)


  • 组件化开发

  • 页面由多个组件构成(可复用)

  • 虚拟DOM

  • 减少DOM操作,提升渲染性能

vue程序

<template>
    <div id="app">
        {{message}}
    </div>
</template>


<script>
    const data = {
        message:'first vue'
    };
    const app = new Vue({
        el:"#app",
        data:data
    })
    //在组件外部定义某一个监听事件,可以使用$符号
    app.$watch('message',function(newValue,oldValue){
        ...
    })
</script>

生命周期

钩子函数eldatamessage(data属性)
beforeCreateundefinedundefinedundefined
createdundefined[ object Object ]' first vue '
beforeMount[ object HTMLDivElement ]<div id="app">...</div>[ object Object ]' first vue '
mounted[ object HTMLDivElement ]<div id="app">...</div>[ object Object ]' first vue '
beforeUpdate[ object HTMLDivElement ]<div id="app">...</div>[ object Object ]' first vue2 '
updated[ object HTMLDivElement ]<div id="app">...</div>[ object Object ]' first vue2 '
beforeDestory
destoryed

Class Style绑定

  • Class绑定

  • 字符串 其实就是变量名,变量名对应的值为实际的class值

  • 对象 { class类名(或者变量名):布尔值 ,...可以有多个 } 布尔值用来控制这个class是否生效

  • 数组 [ 变量名1,变量名2,... ]

  • Style绑定

  • 对象 { style属性名:属性值 } 属性名遇到有-的,可以用引号引起来,也可以用驼峰命名法,如:'background-color',backgroundColor

  • 数组 [对象1,对象2,...] 这里的对象就是上面的对象形式,数组形式可以同时添加多个对象,当对象中有相同名字的属性时,后面的会覆盖前面的,名字不相同的属性则会叠加

表单修饰符

  • v-model.lazy 只有在失去焦点的时候才触发双向绑定值的变化

  • v-model.number 将用户的输入值自动转为数字,其他符号被过滤掉(底层用的parseFloat方法)

  • v-model.trim 过滤首尾的空白字符

组件(可复用的vue实例)组成

  • 组件没有el属性,只有Vue的根实例才有el属性

  • data( function函数形式 )

  • props 数组,接受外部的参数

  • components 对象,引入的子组件

  • template:模板,dom元素

  • watch

  • computed

  • methods

  • 生命周期的钩子函数

组件注册

  • 全局注册:

  • Vue.component( '组件名' ,组件实例对象 )

  • 上面注册时的组件名可以用驼峰命名法,但组件在父组件的HTML中引用的时候要用短横线的形式

  • 如,注册时为‘buttomComponent’,使用的时候为<buttom-item></buttom-item>

  • 可以随处使用,因为data是function的形式,所以每个组件之间的数据都是独立的,如果不是用function的形式,则data数据不独立,且浏览器也会报错

  • 缺点,即使不使用,代码也会被打到生产的包中

  • 局部注册

  • 创建组件对象

  • 将组件引入到需要用的页面上

  • 在vue实例的components属性中引用

组件中的数据传递

  • 父子组件通信

  1. props,$emit

    // section父组件
        <template>
          <com-article :articles="articleList" @emitIndex = "onEmitIndex"></com-article>
        </template>
    
    
        <script>
        import comArticle from './test/article.vue'
        export default {
          name: 'HelloWorld',
          components: { comArticle },
          data() {
            return {
              articleList: ['红楼梦', '西游记', '三国演义'],
        currentIndex:-1        }
          },
           methods: {
               onEmitIndex(idx) {
                   this.currentIndex = idx
               }
           }
        }
        </script>
    
    
        // 子组件 article.vue
        <template>
          <div @click="clickContent(1)"></div>
        </template>
    
    
        <script>
            export default {
                props: ['articles'],
                methods: {
                    clickContent(index) {                	this.$emit('emitIndex', index)                }
                }
            }
        </script>

  2. $children,$parent

    通过$parent和$children就可以访问组件的实例, $children 的值是数组,而$parent是个对象

  • 深层次父子间通信

  1. 依赖注入(provide,inject)父组件提供(provide)变量,子组件注入(inject)变量

        <script>
           import comC from '../components/test/comC.vue'
           export default {
                name: "B",
                provide: [for,this.demo],
                data() {
                    return {
                        demo: 'demo'
                    }
                },
                components: {
                    comC
                }
            }
        </script>
    
    
        //子组件C继承B
        <template>
            <div>
                {{demo}}
            </div>
        </template>
    
    
        <script>
        export default {
            name: "C",
            inject: ['for'],
            data() {
                return {
                    demo: this.for
                }
            }
        }
        </script>

  2. $attrs

    $attrs 获取的是用v-bind(:)绑定的值(不用props,因为和props功能是一样的)

    //若想要隔代,则孙子组件上,将爷爷组件的$attrs使用v-bind绑定
    <child-com2 v-bind="$attrs"></child-com2>
  • 兄弟组件通信

  1. 直接获取组件实例 (ref,$refs),ref用在子组件上,$refs引用就指向组件实例

        // 父组件
        <template>
            <component-a ref="comA"></component-a>
        </template>
        <script>
        export default {
            mounted () {
                const comA = this.$refs.comA;
                console.log(comA.name);  // Vue.js
                comA.sayHello();  // hello
            }
        }
        </script>
  2. 事件总线

    当作多个组件的共同事件中心,可向事件中心发送事件或者接受事件,实现多个组件的通信

    //1.创建事件总线(空的vue实例)并导出,使得其他模块可以监听他
    // event-bus.js
    import Vue from 'vue'
    export const EventBus = new Vue()
    
    //2.在其他模块中引入事件总线
    //引入组件showNumCom,additionNumCom
        <template>
            <div>
                <show-num-com></show-num-com>
                <addition-num-com></addition-num-com>
            </div>
        </template>
    
    
        <script>
            import showNumCom from './showNum.vue'
            import additionNumCom from './additionNum.vue'
            export default {
                components: { showNumCom, additionNumCom }
            }
        </script>
        
     //3.additionNumCom中发送事件(事件总线.$emit(事件名称,param))
     // addtionNum.vue 中发送事件
        <template>
          <div>
            <button @click="additionHandle">+加法器</button>    
          </div>
        </template>
    
        <script>
        import {EventBus} from './event-bus.js'
        console.log(EventBus)
        export default {
          data(){
            return{
              num:1
            }
          },
          methods:{
            additionHandle(){
              EventBus.$emit('addition', {
                num:this.num++
              })
            }
          }
        }
        </script>
        
    //4.showNum中接收事件(事件总线.$on(事件名称,func(param){}))
    // showNum.vue 中接收事件
        <template>
          <div>计算和: {{count}}</div>
        </template>
    
    
        <script>
        import { EventBus } from './event-bus.js'
        export default {
          data() {
            return {
              count: 0
            }
          },
          mounted() {
            EventBus.$on('addition', param => {
              this.count = this.count + param.num;
            })
          }
        }
        </script>
        
    //5.移除事件监听($off)
        import { eventBus } from 'event-bus.js'
      	EventBus.$off('addition', {})
  3. 状态管理Vuex

    解决:多个视图依赖同一状态,多个视图需要更改同一状态的问题

    模块:

    1)state: 数据的存储

    2)getters: 如vue中的计算属性一样,基于state数据的二次包装

    3)mutations: 类似函数,改变state数据的唯一途径,且不能用于处理异步事件

    4)actions:类似于mutation,用于提交mutation来改变状态,而不直接变更状态,可以包含任意异步操作

    5)modules:类似于命名空间,用于项目中将各个模块的状态分开定义和操作,便于维护

插槽

父组件想向子组件传递dom元素时, 直接在使用子组件的时候包裹dom元素是无效的,如:

<child>    <p>向子组件中传递dom元素</p></child>

想要上面的形式生效,需要使用插槽,即在子组件中使用<slot></slot>标签,插槽主要类别有三种,分别为默认插槽,具名插槽,作用域插槽。

默认插槽 

<!-- 在父组件中使用子组件child,并向子组件中传递<p>包裹的dom元素 -->
<child>    
    <p>向子组件中传递dom元素</p>
</child>
<!-- 其中,<slot></slot>中包裹的dom元素是父组件没有向子组件传递dom元素时,子组件所展示的默认内容
 -->
<template>    
    <div>       
         ...        
        <slot>slot中的默认内容</slot>    
    </div>
</template>

具名插槽  

想在子组件的不同位置显示父组件中传递的dom元素

在父组件中 v-slot指令(v-slot指令应该定义在temlpate模板上,而不是普通标签上,不然控制台报错),默认插槽其实就是v-slot:default;

在子组件中使用name='XXX'属性匹配父组件的v-slot:XXX,在对应的位置展示dom元素

<!-- 在父组件中使用子组件child -->
<child>    
    <!-- 具名插槽 -->    
    <template>        
        <p v-slotheader>向子组件中传递dom元素1</p>    
    </template>    
    <!-- 默认插槽 -->    
        <p>向子组件中传递dom元素2</p>    
        <p>2</p>    
    <!-- 具名插槽 -->    
    <template>        
        <p v-slotfoot>向子组件中传递dom元素3</p>    
    </template>
</child>

<template>    
    <div>        
        <slot name="header"></slot>        
        ...        
        <slot name="footer"></slot>        
        <slot>slot中的默认内容</slot>    
    </div>
</template>

作用域插槽  

在父组件中向子组件传递dom元素,其中dom元素中包含了子组件的data属性中的属性值,让插槽内容可以访问到子组件中才有的数据

在父组件中使用v-slot:插槽名 = "自定义变量,用来接收子组件的data属性"接收子组件的data属性,在子组件中通过name='插槽名'匹配dom元素,再使用v-bind来获取父组件的dom元素传递过来的变量数据

注意,如果既有作用域插槽,又有默认插槽,则默认插槽的v-slot:default必须写完整,不可以省略。

<!-- 在父组件中使用子组件child -->
<child>    
    <!-- 自定义属性slotProps相当于传入了子组件的data对象 -->    
    <template v-slotheader = "slotProps">        
        <p>子组件中的数据{{slotProps.lastName}}</p>    
    </template>
    <template v-slotdefault = "slotProps">        
        <p>子组件中的数据{{slotProps.lastName}}</p>    
    </template>
</child>

<!-- 在子组件中 -->
data:()=>{    
    return {        
        lastName:'1',    
    }
}
<template>    
    <div>        
        ...        
        <!-- 在子组件中使用v-bind接收父组件的slot传来的值 -->        
        <slot :lastName="lastName" name="header">slot中的默认内容</slot>
        <slot :lastName="lastName" name="default">slot中的默认内容</slot>
    </div>
</template>

动态组件

使用<component :is="componentName"></component>动态切换组件

其中,componentName的值为子组件名称

<!-- 子组件一 -->
Vue.component('child1',{
    data:()=>{
    },
    template:'<div>组件一</div>'
})
<!-- 子组件二 -->
Vue.component('child2',{
    data:()=>{
    },
    template:'<div>组件二</div>'
})
<!-- 父组件中:currentName的值(子组件的名字)决定当前渲染的是哪一个子组件 -->
data()=>{
    currentName:'child2'
}
<div id="app">
     <component :is="currentName"></component>
</div>

组件缓存

使用<keep-alive></keep-alive>包裹子组件,可以缓存子组件中的一些状态,如单选按钮的状态等。