vue3学习随便记6-条件渲染、列表渲染

条件渲染

v-if

有v-if并没有v-end,所以v-if必须用在元素上,靠元素本身的配对来决定起止。如

<h1 v-if="awesome">Vue is awesome!</h1>

有 v-else

<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>

也有 v-else-if  (这个使用频率不高)

<div v-if="type === 'A'">
  A
</div>
<div v-else-if="type === 'B'">
  B
</div>
<div v-else-if="type === 'C'">
  C
</div>
<div v-else>
  Not A/B/C
</div>

v-show

和 v-if 对应的有 v-show,这个前面的帖子已经分析过。

v-if 和 v-for 一起使用

v-if 和 v-for 一般不应该一起使用,而两者如果一起使用,v-if 的优先级是要高于 v-for 的。

列表渲染

用 v-for 把一个 JS 数组映射成一组元素

<html>

<head>
    <script src="vue.global.js"></script>
</head>

<body>
    <div id="list-rendering">
        <ul>
            <li v-for="item in items">
                {{ item.message }}
            </li>
        </ul>
        <hr>
        <ul>
            <li v-for="(item, index) in items">
                {{ parentMessage }} - {{ index }} - {{ item.message }}
            </li>
        </ul>
        <hr>
        <ul>
            <li v-for="item of items">
                {{ item.message }}
            </li>
        </ul>
    </div>
    <script>
        const ListRendering = {
            data() {
                return {
                    parentMessage: 'Parent',
                    items: [
                        { message: 'Foo' },
                        { message: 'Bar' },
                        { message: 'Tee' }
                    ]
                }
            }
        }
        Vue.createApp(ListRendering).mount('#list-rendering')
    </script>
</body>

</html>

渲染结果为

 如果不使用索引,那么第三种 of 语法其实更符合 Javascript 的迭代器风格,只是多数情况我们习惯了 in 语法。对于 v-for 块,它可以使用父作用域的所有属性,例如第二段中的 parentMessage。

用 v-for 遍历对象

<html>

<head>
    <script src="vue.global.js"></script>
</head>

<body>
    <div id="object-rendering">
        <ul>
            <li v-for="value in myObject">
                {{ value }}
            </li>
        </ul>
        <hr>
        <ul>
            <li v-for="(value, key) in myObject">
                {{ key }}: {{ value }}
            </li>
        </ul>
        <hr>
        <ul>
            <li v-for="(value, key, index) in myObject">
                {{ index }}. {{ key }}: {{ value }}
            </li>
        </ul>
    </div>
    <script>
        const ObjectRendering = {
            data() {
                return {
                    myObject: {
                        title: 'How to do list in vue',
                        author: 'Jane Doe',
                        publishedAt: '2022-01-01'
                    }
                }
            }
        }
        Vue.createApp(ObjectRendering).mount('#object-rendering')
    </script>
</body>

</html>

用 v-for 遍历对象和遍历数组并没有什么不同,只是遍历对象通常随带key(也可以同时用index,但意义不大)。vue内部按 Object.keys() 顺序来遍历,所以,不同的浏览器JS引擎遍历结果有可能不一致,应该是 JS 对对象的键散列算法决定了顺序。

维护状态

v-for 渲染元素列表,默认使用“就地更新”策略,即两个数据项顺序改变,Vue不是去移动DOM节点来匹配数据项的顺序,而是就地更新每个元素。虽然这个模式是高效的,但 Vue 推荐在多数情况下,为每个DOM节点绑定一个唯一的key(这是Vue识别节点的通用机制),从而可以识别跟踪和复用重排这些节点。

<div v-for="item in items" :key="item.id">
  <!-- 内容 -->
</div>

不绑定 key 适合于简单的 DOM 渲染或者刻意需要就地更新来获取性能提升。

数组更新检测

变更方法

Vue3 中被侦听的数组的变更方法会被包裹,从而它们会触发视图的更新。被包裹过的方法包括:

4个队列操作方法 push()pop()shift()unshift(),任意位置增删 splice(),排序 sort(),逆序 reverse()

替换数组

对于非变更方法(不会变更原始数组的方法),例如 filter()concat() slice(),它们总是返回一个新数组,要实现响应,可以用这个新数组替换旧数组。Vue在替换数组时,会最大程度复用相同的元素。

<html>

<head>
    <script src="vue.global.js"></script>
</head>

<body>
    <div id="list-rendering">
        <ul>
            <li v-for="item in items">
                {{ item.message }}
            </li>
        </ul>
        <hr>
        <ul>
            <li v-for="(item, index) in items">
                {{ parentMessage }} - {{ index }} - {{ item.message }}
            </li>
        </ul>
        <hr>
        <ul>
            <li v-for="item of items">
                {{ item.message }}
            </li>
        </ul>
        <button @click="change1">修改数组1</button>
        <button @click="change2">修改数组2</button>
    </div>
    <script>
        const ListRendering = {
            data() {
                return {
                    parentMessage: 'Parent',
                    items: [
                        { message: 'Foo' },
                        { message: 'Bar' },
                        { message: 'Tee' }
                    ]
                }
            },
            methods: {
                change1() {
                    this.items.push({message: 'Baz ' + (new Date())})
                },
                change2() {
                    this.items = this.items.filter(item => item.message.match(/Ba/))
                }
            }
        }
        Vue.createApp(ListRendering).mount('#list-rendering')
    </script>
</body>

</html>

上述代码中,change1 是变更方法 push 触发更新,change2 是替换数组触发更新。

显示过滤/排序后的结果

如果不想实际变更或者替换掉原始数组,但需要显示数组经过过滤或排序后的版本,我们可以创建一个计算属性,返回过滤或排序后的数组。

<html>

<head>
    <script src="vue.global.js"></script>
</head>

<body>
    <div id="list-rendering">
        <ul>
            <li v-for="n in evenNumbers" :key="n">
                {{ n }}
            </li>
        </ul>
        <button @click="change">修改</button>
    </div>
    <script>
        const ListRendering = {
            data() {
                return {
                    numbers: [1, 2, 3, 4, 5]
                }
            },
            computed: {
                evenNumbers() {
                    return this.numbers.filter(number => number % 2 === 0)
                }
            },
            methods: {
                change() {
                    this.numbers.push(Math.floor(Math.random()*100))
                    console.log(this.numbers)
                }
            }
        }
        Vue.createApp(ListRendering).mount('#list-rendering')
    </script>
</body>

</html>

 从运行结果来看,我们知道,对于vm的属性中的数组(vm.numbers),会被包裹成一个 Proxy,而计算属性 evenNumbers 则返回数组(console(this.evenNumbers) 将打印出数组)。

在 v-for 里使用值的范围

v-for 迭代的对象,除了数组、对象,也可以是一个整数n,此时,表示1~n的一个序列。

<div id="range" class="demo">
  <span v-for="n in 10">{{ n }} </span>
</div>

上述代码显示12345678910

在 <template> 中使用 v-for

        <ul>
            <template v-for="item in items" :key="item.message">
                <li>{{ item.message }}</li>
                <li class="divider" role="presentation"></li>
            </template>
        </ul>

v-for 与 v-if 一起使用

v-for 与 v-if 一起使用是不被推荐的,如果的确一起使用,需要注意优先级和相关的坑。

<li v-for="todo in todos" v-if="!todo.isComplete">
  {{ todo.name }}
</li>

上面的代码是错误的!原因是 v-for 和 v-if 一起使用,v-if 的优先级高于 v-for,从而 todo 是找不到的!解决办法是把 v-if 移到 v-for 内,外面用 <template>

<template v-for="todo in todos" :key="todo.name">
  <li v-if="!todo.isComplete">
    {{ todo.name }}
  </li>
</template>

在组件上使用 v-for 

因为组件就是自定义的元素,所以,可以用 v-for 来产生一堆组件实例

<html>

<head>
    <script src="vue.global.js"></script>
</head>

<body>
    <div id="todo-list">
        <form v-on:submit.prevent="addNewTodo">
            <label for="new-todo">添加待办事项</label>
            <input v-model="newTodoText" id="new-todo" placeholder="例如: 收拾房间" />
            <button>添加</button>
        </form>
        <ul>
            <todo-item v-for="(todo, index) in todos" :key="todo.id"
                :title="todo.title" @remove="todos.splice(index, 1)"
            ></todo-item>
        </ul>
    </div>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    newTodoText: '',
                    todos: [
                        {
                            id: 1,
                            title: '收拾碗筷'
                        },
                        {
                            id: 2,
                            title: '清除垃圾'
                        },
                        {
                            id: 3,
                            title: '拖地'
                        }
                    ],
                    nextTodoId: 4
                }
            },
            methods: {
                addNewTodo() {
                    this.todos.push({
                        id: this.nextTodoId++,
                        title: this.newTodoText
                    })
                    this.newTodoText = ''
                }
            }
        })
        app.component('todo-item', {
            template: `
                <li>
                    {{ title }}
                    <button @click="$emit('remove')">移除</button>
                </li>
            `,
            props: ['title'],
            emits: ['remove']
        })
        app.mount('#todo-list')
    </script>
</body>

</html>

我们观察组件模板,组件通过 props 定义了属性 title,以便组件实例可以从外部接收数据,组件用emits 定义了可发射事件 remove,以便组件实例可以从外部响应 remove事件。通常,额外定义的emits事件往往转嫁到HTML已有的某个事件上(可以认为这样自定义事件是被动触发的),例如,这里click事件的处理方式是调用实例API函数$emit('remove') 来触发 remove 事件。

从接口思想的角度看,props和emits都是子组件向外暴露的接口,前者是暴露了属性字段(“回调字段”,父组件提供具体值),后者是暴露了事件方法(回调方法,父组件提供具体实现)。子组件的emits事件,必须在子组件内部得到触发才有意义,可以用Vue的实例API $emit 手动触发,也可以用 $emit “嫁接”到HTML本身的某个事件上(像上面的例子中那样)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值