Vue.js 要点与事项手记

Vue.js

对象更改响应

对象

只有当实例被创建时 data 中存在的属性才是响应式的(修改时会重新渲染视图);
如果你添加一个新的属性,不会触发任何视图的更新

<div id="demo">
    {{ name }}
</div>

<script>

var vm = new Vue({
    el: "#demo",
    data: {}        // 对比 data: {name: "AAA"}
});

vm.name = "AAA";

</script>

此例中在vue对象新加入的属性没有在视图中更新


数组

以下情况 Vue 不能检测以下变动的数组:

  1. 索引修改数组元素

  2. 直接修改数组长度

解决方案:(设Vue实例化对象为vm,data中的数组为 items )

  1. vm.$set(vm.items, indexOfItem, newValue)
  2. vm.items.splice(newLength)
<div id="demo">
    <div v-for="(item, index) of items" :key="index">
        {{ index }} - {{ item }}
    </div>
</div>

<script>

    var vm = new Vue({
        el: "#demo",
        data: {
            items: ['a', 'b', 'c', 'd']
        }
    });

    vm.$set(vm.items, 0, 'A'); //vm[0] = 'A'; 无效果
    vm.items.splice(2);        //vm.length = 2; 无效果

</script>



箭头函数 this 作用域

不要在选项属性或回调上使用箭头函数
因为箭头函数是和父级上下文绑定在一起的,this 不会是如你所预期的 Vue 实例。

<div id="demo">
    <p> {{ name }} </p>
    <button v-on:click="changName"> Chang Name </button> <br/><br/>
    <button v-on:click="arrowChangeName"> Arrow Function Chang Name </button>
</div>

<script>
    new Vue({
        el: "#demo",
        data: {
            name: "AAA"
        },

        methods: {
            changName: function() {
                console.log(this);
                this.name="BBB";
            },

            arrowChangeName: ()=> {
                console.log(this);
                this.name = "CCC";
            }
        }
    });
</script>

箭头函数中的 this 在这个例子中就是 window 对象



html 文本插入

双大括号会将数据解释为普通文本,而非 HTML 代码

使用 v-html 指令输出 HTML 代码

<div id="demo">
    <p>Using mustaches: {{ rawHtml }} </p>
    <p>Using v-html directive: <span v-html="rawHtml"></span></p>
</div>

<script>
    new Vue({
        el: "#demo",
        data: {
            rawHtml: `<span style='color:red'>
                        This shoule be red
                      </span>`
        }
    })
</script>



v- 指令缩写

  • v-bind: —— :
  • v-on: —— @
<div id="demo">
    <a :href="url" target="_blank">v-bind abbreviation</a> <br/><br/>
    <a @click="openUrl">v-on abbreviation</a>
</div>

<script>
    new Vue({
        el: "#demo",
        data: {
            url: "https://www.yuchen.website"
        },
        methods: {
            openUrl: function() {
                window.open(this.url);
            }
        }
    });
</script>


v-bind 对于 class与style 的绑定

style绑定

绑定style时,必须将css属性转换成驼峰命名法

<div id="demo" :style="[styleObj1, styleObj2]">        <!--使用数组来囊括多个对象叠加-->
    <p :style="{ color: myTextColor }"> 这是一段文本 </p>
</div>

<script>
    new Vue({
        el: "#demo",
        data: {
            styleObj1: {
                fontSize: "18px",
                backgroundColor: "black"
            },

            styleObj2: {
                display: "table-cell",
                verticalAlign: "middle",
                textAlign: "center",
                width: "240px",
                height: "180px"
            },

            myTextColor: "white"
        }
    });

</script>

class 绑定

<style>
    .style-1 {
        font-size: 18px;
    }
    .style-2 {
        background-color: black;
        color: white;
    }

</style>

<div id="demo" :class="myClass">
    <p> 这是一段文本 </p>
</div>

<script>
    new Vue({
        el: "#demo",
        data: {
            myClass: {
                'style-1': true,
                'style-2': true
            }
        }
    });
</script>

  • style 绑定的对象属性为 css属性键值对
  • class 绑定的对象属性为class true/false键值对



计算属性

计算属性 getter和setter

<div id="demo">
    <p>  {{ x }} </p>
    <input type="text" v-model="xBinder"/>
</div>

<script>
    new Vue({
        el: "#demo",
        data: {
            x: 1
        },
        computed: {
            xBinder: {
                get: function() {
                    return this.x;
                },
                set: function(val) {
                    this.x = val;
                }
            }
        },
    });
</script>


计算属性参数调用

  其实计算属性就是当做属性来使用的,调用参数不如直接使用 methods方法,如果非要传入参数可以使用闭包函数解决:

<div id="demo">
    methods:{{ methodsAdd(1) }}<br/><br/>
    computed:{{ computedAdd(1) }}
</div>

<script>
    new Vue({
        el: "#demo",
        data: {
            x: 1
        },
        methods: {
            methodsAdd: function(y) {
                return this.x + y;
            }
        },

        computed: {
            computedAdd: function() {
                return function(y) {
                    return this.x + y;
                }
            }
        },
    });

</script>


计算属性vs方法

计算属性是基于它们的依赖进行缓存的,只要依赖不变就返回之前所计算的结果

<div id="demo">
    methods:{{ methodsRandom() }} &ensp;
    computed:{{ computedRandom }}
    <hr/>
    methods:{{ methodsRandom() }} &ensp;
    computed:{{ computedRandom }}
</div>

<script>
    new Vue({
        el: "#demo",
        methods: {
            methodsRandom: function() {
                return Math.random();
            },
        },
        computed: {
            computedRandom: function() {
                return Math.random();
            }
        },
    });

</script>



条件与循环

Vue 风格指南:

  • 永远不要把 v-if 和 v-for 同时用在同一个元素上
  • 在组件上总是必须用 key 配合 v-for,以便维护内部组件及其子树的状态



事件修饰符

事件冒泡与传播

<div id="demo" style="font-size:24px">
    <div id="outer" @click="show">
        outer
        <div id="middle" @click="show">
            middle
            <div id="inner" @click="show">
                inner
            </div>
        </div>
    </div>
</div>

<script>
    new Vue({
        el: "#demo",
        methods: {
            show: function(event) {
                alert(event.currentTarget.id);
            }
        }
    })
</script>

执行顺序为 inner —> middle —> outer

stop 阻止事件的冒泡修饰

为上例中的 middle 添加stop修饰, inner和outer不修饰:

<div id="demo" style="font-size:24px">
    <div id="outer" @click="show">
        outer
        <div id="middle" @click.stop="show">
            middle
            <div id="inner" @click="show">
                inner
            </div>
        </div>
    </div>
</div>

执行顺序为 inner —> middle, outer不执行

capture 优先捕获事件修饰

由外而内触发事件, 捕获修饰优先

为 outer和inner 添加capture修饰, middle 不修饰:

<div id="demo" style="font-size:24px">
    <div id="outer" @click.capture="show">
        outer
        <div id="middle" @click="show">
            middle
            <div id="inner" @click.capture="show">
                inner
            </div>
        </div>
    </div>
</div>

执行顺序为 outer —> inner —> middle

self 只执行自身事件修饰

跳过冒泡和捕获,只执行自身事件

为 middle 添加self修饰, outer和inner不修饰

<div id="demo" style="font-size:24px">
    <div id="outer" @click="show">
        outer
        <div id="middle" @click.self="show">
            middle
            <div id="inner" @click="show">
                inner
            </div>
        </div>
    </div>
</div>

执行 inner —> outer, middle不执行

prevent 阻止默认事件

等效于js的 preventDefault

<div id="demo">
    <a href="https://www.yuchen.website" @click> 跳转到宇宸的博客 </a> <br/>
    <a href="https://www.yuchen.website" @click.prevent> 阻止默认跳转事件 </a>
</div>

<script>
    new Vue({
        el: "#demo",
    });
</script>


按键码 / 鼠标码修饰

按键码.enter.tab.delete (删除和退格).esc.space.up.down.left.right.ctrl.alt.shift.meta (command键)
鼠标码.left.right.middle


表单的数据绑定

 <div id="demo">
     <input type="checkbox" value="Foo" v-model="items"/>Foo
     <input type="checkbox" value="Bar" v-model="items"/>Bar
     <input type="checkbox" value="Baz" v-model="items"/>Baz

     <br/>
     <span>Checked names: {{ items }}</span>
</div>

<script>
    new Vue({
        el: "#demo",
        data: {
            items: []
        }
    });
</script>

v-model 修饰符

lazy 转变为使用 change 事件进行同步

 <div id="demo">
     <input type="text" v-model.lazy="str"/> <br/>
     {{ str }}
</div>

<script>
    new Vue({
        el: "#demo",
        data: {
            str: ""
        }
    });
</script>

当回车会输入框失去焦点时更新

number 转化为数值类型
trim 去首尾空格



组件 Component

全局和局部

全局注册:

<div id="demo">
    <my-span></my-span>
</div>

<script>

    Vue.component("my-span", {
        template: `<span>全局注册</span>`
    });

    new Vue({el: "#demo"});
</script>

局部注册:

<div id="demo">
    <my-span></my-span>
</div>

<script>

    var mySpan = {
        template: `<span>局部注册</span>`
    };

    new Vue({
        el: "#demo",
        components: {
            "my-span": mySpan
        }
    });
</script>


dom 元素限制

<div id="demo">
    <table>
        <my-row></my-row>
    </table>
</div>

<script>
    Vue.component("my-row", {
        template: `
               <tr>
                <td>元素限制</td>
            </tr>
        `
    });

    new Vue({el: "#demo"});
</script>

当执行以上代码时会发现元素被弹出 table 标签:

使用 is 属性来修复模版标签

<div id="demo">
    <table>
        <tr is="my-row"></tr>
    </table>
</div>
<script>
    Vue.component("my-row", {
        template: `
            <tr>
                <td> is属性修复限制 </td>
            </tr>
        `
    });
    new Vue({el: "#demo"});
</script>


data

data 必须是一个函数, 用函数来返回一个对象使每个实例可以维护一份被返回对象的独立的拷贝


prop 属性

动态 prop
<div id="demo">
    <input v-model="msg"/>
    <my-div :mymsg="msg"></my-div>
</div>

<script>
    Vue.component("my-div", {
        props: ['mymsg'],
        template: `
            <span>{{ mymsg }}</span>
        `
    });

    new Vue({
        el: "#demo",
        data: {msg: ''}
    });
</script>
prop 验证
Vue.component('example', {
  props: {
    // 基础类型检测 (`null` 指允许任何类型)
    propA: Number,
    // 可能是多种类型
    propB: [String, Number],
    // 必传且是字符串
    propC: {
      type: String,
      required: true
    },
    // 数值且有默认值
    propD: {
      type: Number,
      default: 100
    },
    // 数组/对象的默认值应当由一个工厂函数返回
    propE: {
      type: Object,
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数
    propF: {
      validator: function (value) {
        return value > 10
      }
    }
  }
})


自定义事件

  • 使用 $emit(eventName) 触发事件
  • 为事件绑定监听函数
<div id="demo">
    总共触发: {{ tot }}<br/>
    <my-button @message="listener"></my-button>
    <my-button @message="listener"></my-button>
</div>

<script>
    Vue.component("my-button", {
        methods: {
            trigger: function() {
                this.cnt ++;
                this.$emit('message');
            }
        },
        data: function() {
            return {cnt: 0}
        },
        template: `
            <button @click='trigger'>
                第 {{ cnt }} 次触发
            </button>
        `
    });

    new Vue({
        el: "#demo",
        data: {tot: 0},
        methods: {
            listener: function() {
                this.tot ++;
            }
        },
    });
</script>


插槽 slot

<div id="demo">
    <my-div>
        <p> 这是子元素 </p>
    </my-div>
</div>

<script>
    Vue.component("my-div", {
        template: `
            <div>
                <h3> 在slot处插入子元素 </h3>
                <slot></slot>
            </div>
        `
    });

    new Vue({el: "#demo"});

</script>

渲染结果如下:



自定义过滤器

<div id="demo">
    <p> {{ text | text-snippet }} </p>
</div>

<script>

    Vue.filter("text-snippet", (text)=> {
        return text.slice(0, 20);
    })

    new Vue({
        el: "#demo",
        data: {
            text: "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quibusdam consequuntur quod quaerat ad, quae doloribus incidunt aspernatur id suscipit praesentium ipsum debitis aperiam excepturi impedit pariatur molestiae assumenda voluptates eius."
        }
    });
</script>

此例中通过自定义的过滤器截取长文本的前20个字符


自定义指令

<div id="demo">
    <span v-color v-for="(token, index) of text.split(' ')" :key="index">
        {{ token }}
    </span>
</div>

<script>

    Vue.directive("color", {
        bind: function(el, binding, vnode) {
            var color = '#' + Math.random().toString(16).slice(2, 8);
            el.style.color = color;
        }
    });

    new Vue({
        el: "#demo",
        data: {
            text: "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quibusdam consequuntur quod quaerat ad, quae doloribus incidunt aspernatur id suscipit praesentium ipsum debitis aperiam excepturi impedit pariatur molestiae assumenda voluptates eius."
        }
    });
</script>

以上通过自定义指令将字体颜色样式改成随机, 结合 v-for 很方便地实现了彩色文本

tips:

  • bind 为钩子函数为第一次绑定元素时调用
  • 常用钩子函数的参数为:
    • el 是指令所绑定的元素,可以用来直接操作 DOM
    • binding 代表一个绑定的对象, 常用属性为:
      • value 是指令的绑定值,例如:v-my-directive="1+1" 中,绑定值为 2
      • arg 是传给指令的参数,例如 v-my-directive:foo 中,参数为 "foo"



父组件通信子组件

通过子组件的prop传递数据

即上文的动态prop的内容

<div id="demo">
    <input v-model="msg"/>
    <my-child :msg="msg"></my-child>
</div>

<script>
    Vue.component("my-child", {
        props: ['msg'],
        template: `
            <span>{{ msg }}</span>
        `
    });

    new Vue({
        el: "#demo",
        data: {msg: ''}
    });
</script>


子组件通信父组件

子组件抛出事件
<div id="demo">
    {{ cnt }}
    <my-child @event-message="listener"/>
</div>

<script>

    const child = {
        data: function() {
            return {
                cnt: 0
            }
        },
        methods: {
            send: function() {
                this.$emit('event-message',
                           ++ this.cnt);
            }
        },
        template: `
            <button @click="send">
                child component: {{ cnt }}
            </button>
        `
    };

    new Vue({
        el: "#demo",
        data: {
            cnt: 0
        },
        methods: {
            listener: function(val) {
                this.cnt = val;
            }
        },
        components: {
            "my-child": child
        }
    })

</script>



参考

Vue.js
Vue—事件处理(逐个学习事件修饰符)
Vue.js 组件
vue从入门到进阶:组件Component详解(六)
vue组件之间的通信以及如何在父组件中调用子组件的方法和属性

-------------本文结束-------------