vue学习----2.条件、列表渲染,props属性,事件处理,表单输入绑定

条件渲染

if—>else-if—>else:
v-else 元素必须紧跟在带 v-if 或者 v-else-if 的元素的后面,否则它将不会被识别。
不推荐同时使用 v-if 和 v-for,当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级,循环一次判断一次,大大降低性能

    <p v-if="active1">你看得见我吗</p>
    <p v-else-if="active2">看不见</p>
    <p v-else>是啊</p>

多个标签时可以使用template标签:

    <template v-if="active">
        <h1>sss</h1>
        <h2>sss</h2>
        <h3>sss</h3>
    </template>

在这里插入图片描述
渲染的结果不会有template标签。
tips:Vue会尽可能高效的渲染元素,通常会复用已有元素而不是从头开始渲染,这是我们可以用key来管理克复用的元素。
例如:

    <template v-if="active3">
        <label>username</label>
        <input type="text" placeholder="Enter your username">
    </template>
    <template v-else>
        <label>email</label>
        <input type="text" placeholder="Enter your email">
    </template>
    <button @click="changeActive3">点我</button>
    
            methods: {
            changeActive3: function () {
                this.active3 = !this.active3;
            }
        }

但是从输入用户名切换到输入邮箱,仅仅替换了placeholder,内容还保留在原来的输入框中,说明vue复用了input标签,如果想要切换输入时输入框被重新渲染,我们可以添加key属性来标识。
在这里插入图片描述

还有一个指令与v-if非常的相似:v-show:

    <p v-show="active1">你好我是active1</p>
    <p v-show="active2">你好我是active2</p>

在这里插入图片描述
v-if与v-show的区别:

  • v-if是真正的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当的销毁和重建
  • v-if也是惰性的:如果在初始渲染时条件为假,则什么也不做-直到第一次条件变为真时,才会开始渲染条件块
  • 相比之下,v-show就简单的多——无论初始条件是什么,元素总是会被渲染,并且知识简单的基于css进行切换
  • 一般来说,v-if有更高的切换开销,而v-show有更高的初始渲染开销,因此,如果需要非常频繁的切换,则使用v-show较好,如果在运行时条件很少改变,则使用v-if比较好。

列表渲染

v-for遍历数组和对象:

<div id="app">

    遍历数组:index为数组的索引
    <ul>
        <li v-for="hobby,index in user.zs.hobbies" v-text="index+'====>'+hobby.content"></li>
    </ul>
    遍历对象:index为对象的字段名,在对象上进行迭代时,该顺序基于 Object.keys() 的枚举顺序,这不能保证在 JavaScript 引擎实现之间是一致的。
    <ol>
        <li v-for="obj,index of user.zs" v-text="index+'====>'+obj"></li>
    </ol>

</div>

</body>
<script src="../js/vue.js"></script>
<script type="text/javascript">
    const vm = new Vue({
        el: "#app",
        data: {
            user: {
                zs: {
                    name: "张三",
                    age: 20,
                    sex: "男",
                    hobbies: [
                        {content: "吃饭"},
                        {content: "睡觉"},
                        {content: "看电影"}
                    ]
                }
            }
        }
    });
</script>

维护状态:当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。这个类似 Vue 1.x 的 track-by="$index"。
这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。
最好不要使用对象或者数组这样复杂的类型作为v-for的key,尽量使用数值或者字符串

<div v-for="item,index in items" v-bind:key="index"></div>

变更方法

Vue将被侦听的数组的变更方法进行了包裹,所以他们将会触发视图更新,这些方法是:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

变更方法会调用原始数组,而非变更方法则不会,非变更方法是返回一个新数组,可以将原始数组替换为新的数组:

  • fiter()
  • concat()
  • slice()

tips:您可能会认为这将导致 Vue 丢弃现有 DOM 并重新呈现整个列表-幸运的是,事实并非如此。 Vue 实现了一些智能试探法,以最大化 DOM 元素的重用,因此,用另一个包含重叠对象的数组替换一个数组是非常有效的操作。
在这里插入图片描述
筛选成绩大于60:
使用计算属性:

    计算属性筛选出的成绩:
    <p v-for="score in findScores" v-text="score"></p>

scores: [60,70,80,90,100]

        computed: {
            findScores: function () {
                return this.user.zs.scores.filter(function (score) {
                    return score > 60;
                })
            }
        }

使用方法:

    方法筛选出的成绩:
    <p v-for="score in find(user.zs.scores)" v-text="score"></p>

scores: [60,70,80,90,100]

methods: {
            /* 感觉有点像函数中套函数 */
            find: function (scores) {
                return scores.filter(function (score) {
                    return score > 60;
                })
            }
        },

tips:由于javscript的限制,vue不能检测数组和对象的变化。

练习

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<div id="app">

    <form @submit.prevent="addNewLi"><!-- 阻止表单的默认提交,调用我们自己的函数 -->
        <label for="inputText"></label>
        请输入添加的内容:<input type="text" id="inputText" v-model="inputText">
        <button>add</button>
    </form>
    <div>
        <ul>
            <li is="t1"
                v-for="(item,index) in liList"
                v-bind:key="item.id"
                v-bind:content="item.content"
                v-on:remove="liList.splice(index,1)"
            >
                <!-- splice(index,number,content),index为删除的索引,number为删除的数量,为0就不删除,content为添加的内容 -->
            </li>
        </ul>
    </div>


</div>

</body>
<script src="../js/vue.js"></script>
<script type="text/javascript">
    const vm = new Vue({
        el: "#app",
        data: {
            //准备初始数据
            liList:[
                {id: 1,content: "我是1号"},
                {id: 2,content: "我是2号"},
                {id: 3,content: "我是3号"}
            ],
            inputText: "",
            nextId: 4
        },
        methods: {
            addNewLi: function () {
                this.liList.push({
                    id: this.nextId++,
                    content: this.inputText
                });
            }
        },
        components: {
            "t1": {
                template: "<li>{{content}}<button v-on:click='$emit(\"remove\")'>删除</button></li>",
                props: ["content"] /* 父子组件传递数据 */
            }
        }
    });
</script>
</html>

props属性

组件接收的选项之一props是vue中非常重要的一个选项,父组件通过props向下传递数据给子组件,子组件通过events给父组件发送消息,HTML中的特姓名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符,这意味着当你使用DOM中的模板时,camelCase(驼峰命名法)的props名需要使用其等价的kebab-case(短横线分隔命名)命名。

    <t1 v-bind:message="name"></t1>
    <t1 v-bind:message="arr"></t1>
    <t1 v-bind:message="person"></t1>
    <t1 v-bind:message="false"></t1>
    <t1 v-bind:message="isMan"></t1>
    <t1 v-bind:message="[1,2,3,4,5]"></t1>
    <t1 v-bind:message="{name: 'wangwu',age: 22}"></t1>

<script type="text/javascript">
    const vm = new Vue({
        el: "#app",
        data: {
            message: "Hello",
            name: "zhangsan",
            isMan: true,
            arr: [1,2,3,4,5],
            person: {name: "lisi",age: 20}
        },
        components: {
            "t1": {
                template: "<h1></h1>",
                props: ["message"]
            }
        }
    });
</script>

所有的props都使得其父子prips之间形成了一个单向下行绑定,父级props的更新会向下流动到子组件中,但是反过来不行,这样会防止子组件意外改变父组件的状态,从而导致你的应用的数据流难以理解,额外的,每次父组件发生更新时,子组件中所有的props属性都会刷新为最新的值,这意味着你不应该在一个子组件内部改变props,如果这样做了,vue会在浏览器的控制台发出警告:

[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "xxxx"。

两种常见的视图改变一个props的情况:
1.props用来传递一个初始值,这个子组件希望将其作为一个本地的props数据来使用,在这种情况下,最好定义一个本地的data属性并将这个props用作其初始值。

            "t2": {
                template: "<h1>{{t2msg}}</h1>",
                data: function () {
                    return {t2msg: this.msg}
                },
                props: ["msg"]
            },

2.这个props以一种原始的值传入且需要进行转换,这种情况下,最好使用这个props的值来定义一个计算属性:

            "t3": {
                template: "<h1>{{t3msg}}</h1>",
                props: ["msg"],
                computed: {
                    t3msg: function () {
                        return this.msg.substring(0,1);
                    }
                }
            }

props验证:
我们可以为组件的 prop 指定验证要求,例如你知道的这些类型。如果有一个需求没有被满足,则 Vue 会在浏览器控制台中警告你。这在开发一个会被别人用到的组件时尤其有帮助。
为了定制 prop 的验证方式,你可以为 props 中的值提供一个带有验证需求的对象,而不是一个字符串数组。例如:

   <my-component v-bind:prop-c="'hello'" :prop-g="fullName"></my-component>


Vue.component('my-component', {
        template: "<h1>{{propG}}</h1>",
        props: {
            // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
            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 ['success', 'warning', 'danger'].indexOf(value) !== -1
                }
            },
            propG: {
                type: Object,
                function (firstName,lastName) {
                    this.firstName = firstName;
                    this.lastName = lastName;
                }
            }
        }
    })

tips:prop 会在一个组件实例创建之前进行验证,所以实例的 property (如 data、computed 等) 在 default 或 validator 函数中是不可用的。

非prop的Attribute:一个非 prop 的 attribute 是指传向一个组件,但是该组件并没有相应 prop 定义的 attribute。
因为显式定义的 prop 适用于向一个子组件传入信息,然而组件库的作者并不总能预见组件会被用于怎样的场景。这也是为什么组件可以接受任意的 attribute,而这些 attribute 会被添加到这个组件的根元素上。
例如,想象一下你通过一个 Bootstrap 插件使用了一个第三方的 <bootstrap-date-input> 组件,这个插件需要在其 <input> 上用到一个 data-date-picker attribute。我们可以将这个 attribute 添加到你的组件实例上:
然后这个 data-date-picker=“activated” attribute 就会自动添加到 <bootstrap-date-input> 的根元素上。

替换/合并已有的Attribute:(来源于官方文档!!!)
在这里插入图片描述
如果你不希望组件的根元素继承attribute:可以设置:inheritAttrs:false

配合实例的$attrs property使用:
{
  required: true,
  placeholder: 'Enter your username'
}

Vue.component('base-input', {
  inheritAttrs: false,
  props: ['label', 'value'],
  template: `
    <label>
      {{ label }}
      <input
        v-bind="$attrs"
        v-bind:value="value"
        v-on:input="$emit('input', $event.target.value)"
      >
    </label>
  `
})

渲染结果:

<base-input
  v-model="username"
  required
  placeholder="Enter your username"
></base-input>

事件处理

使用v-on可以监听dom事件:

    <button @click="count++">点我</button>
    <p>这个按钮被点击了{{count}}次</p>

当事件处理逻辑比较复杂时就不能写在v-on指令中,可以写一个方法:

    请输入:<input type="text" v-model="inputText">
    <button @click="hello(inputText,$event)">hello</button>
    <button @click="error('Error Action!',$event)">error</button>

        methods: {
            hello: function (inputText,$event) {
                if($event){
                    alert($event.target.tagName);
                }
                if(inputText!==""){
                    alert("Hello"+inputText);
                }else{
                    alert("你好,陌生人");
                }
            },
            error: function (errorMsg,event) {
                if (event){
                    event.preventDefault(); //取消事件默认动作,例如提交表单
                }
                alert(errorMsg);
            }
        }

事件修饰符:在事件处理程序中调用 event.preventDefault() 或 event.stopPropagation() 是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。
例如:

<!-- 阻止单击事件继续传播,js事件会向上传播 -->
<a v-on:click.stop="doThis"></a>

<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>

<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
    <a href="https://www.baidu.com" v-on:click.self.prevent="hello('111',$event)">111</a><!-- 只能点击一次 -->
    <a href="http://www.baidu.com" v-on:click="$event.preventDefault()">aaa</a>
    <input type="text" v-on:keyup.enter="hello('111',$event)"> <!-- 回车键调用hello方法 -->
    <input @keyup.ctrl.67="hello('111',$event)" />
            hello: function (inputText,$event) {
                if($event){
                    alert($event.target.tagName);
                }
                if(inputText!==""){
                    alert("Hello"+inputText);
                }else{
                    alert("你好,陌生人");
                }
            },
            error: function (errorMsg,event) {
                if (event){
                    event.preventDefault(); //取消事件默认动作,例如阻止链接跳转和提交表单
                }
                alert(errorMsg);
            }

表单输入绑定

v-model指令可以在表单input、textarea和select元素上创建双向数据绑定,会根据控件类型自动选取正确的方法来更新元素。(v-model会忽略所有表单元素的value、checked、selected特性的初始值而总是将Vue的实例作为数据来源)
v-model在内部为不同的输入元素使用不同的属性并抛出不同的事件:

  • text和textarea元素使用value属性和input事件
  • checkbox和radio使用checked属性和change事件
  • selec字段将value作为prop并将change作为事件

tips:对于需要使用输入法 (如中文、日文、韩文等) 的语言,你会发现 v-model 不会在输入法组合文字过程中得到更新。如果你也想处理这个过程,请使用 input 事件。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>vue学习</title>
</head>
<body>

<div id="app">
    文本:<input type="text" v-model="text" value="ss"/>{{text}}
    多行文本:
    <textarea cols="30" rows="10" v-model="textarea"></textarea>{{textarea}}
    复选框:
    <input type="checkbox" id="c1" value="c1" v-model="checkbox">
    <label for="c1">c1</label>
    <input type="checkbox" id="c2" value="c2" v-model="checkbox">
    <label for="c2">c2</label>
    <input type="checkbox" id="c3" value="c3" v-model="checkbox">
    <label for="c3">c3</label>
    已选择:{{checkbox}}<br />
    单选按钮:
    <input type="radio" id="r1" value="r1" v-model="isPicked">
    <label for="r1">r1</label>
    <input type="radio" id="r2" value="r2" v-model="isPicked">
    <label for="r2">r2</label>
    <input type="radio" id="r3" value="r3" v-model="isPicked">
    <label for="r3">r3</label>
    <!--
    如果 v-model 表达式的初始值未能匹配任何选项,<select> 元素将被渲染为“未选中”状态。
    在 iOS 中,这会使用户无法选择第一个选项。因为这样的情况下,iOS 不会触发 change 事件。
    因此,更推荐像上面这样提供一个值为空的禁用选项。
    -->
    Picked:{{isPicked}}
    <select v-model="selected"> <!-- 绑定option的值 -->
        <option v-for="option in options" v-bind:value="option.content">
            {{option.text}}
        </option>
    </select>
    selected: {{selected}}
</div>


</body>
<script src="../js/vue.js"></script>
<script type="text/javascript">
    const vm = new Vue({
        el: "#app",
        data: {
            text: "",
            textarea: "",
            checkbox: [],
            isPicked: "",
            selected: "A",
            options: [
                {text: "one",content: "A"},
                {text: "two",content: "B"},
                {text: "three",content: "C"}
            ]
        }
    });
</script>
</html>

值绑定

//当选中时,`picked` 为字符串 "a" 
<input type="radio" v-model="picked" value="a">

//`toggle` 为 true 或 false 
<input type="checkbox" v-model="toggle">

//当选中第一个选项时,`selected` 为字符串 "abc" 
<select v-model="selected">
  <option value="abc">ABC</option>
</select>

//复选框
<input
  type="checkbox"
  v-model="toggle"
  true-value="yes"
  false-value="no"
>
// 当选中时
vm.toggle === 'yes'
// 当没有选中时
vm.toggle === 'no'

//单选框
<input type="radio" v-model="pick" v-bind:value="a">
// 当选中时
vm.pick === vm.a


//选择框的选项
<select v-model="selected">
   // 内联对象字面量 
   <option v-bind:value="{ number: 123 }">123</option>
</select>
// 当选中时
typeof vm.selected // => 'object'
vm.selected.number // => 123

修饰符:
.lazy:在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步 (除了上述输入法组合文字时)。你可以添加 lazy 修饰符,从而转变为使用 change 事件进行同步:

<input type="text" v-model.lazy="text" value="ss"/>{{text}}

.number:自动将用户输入的值转为数值类型:


版权声明:本文为weixin_44246189原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。