vue3学习随便记4-模板语法、property、方法

模板语法

插值

文本插值:插值 Mustache语法,会将数据解释为普通文本

<span>Message: {{ msg }}</span>

组件实例 msg property 改变时,插值内容自动改变。如果希望插值处的内容只被替换一次,可以附加使用 v-once 指令

<span v-once>这个将不会改变: {{ msg }}</span>

原始HTML:Mustache语法会将数据解释为普通文本,如果希望输出HTML,需要使用 v-html 指令

<html>

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

<body>
    <div id="app">
        <p>Using mustaches: <span>{{ rawHtml }}</span></p>
        <p>Using v-html directive: <span v-html="rawHtml"></span></p>
    </div>
    <script>
        const App = {
            data() {
                return {
                    rawHtml: `<span style="color:red">红色文本</span>`
                }
            }
        }
        Vue.createApp(App).mount('#app')
    </script>
</body>

</html>

查看结果 

相关源码 

 所以,表面看源码一样,主要是解析方式不同(Mustache语法中,直接变量值替换,而 v-html 指令中的变量值会去解析 property值中的数据绑定)。

不能期望用 v-html 来复合局部模板,因为Vue不是基于字符串的模板引擎,Vue中应该用组件方式复合。一般较少使用原始HTML,因为它容易导致 XSS攻击,只对可信内容使用原始HTML插值,绝对不能将用户提供的内容作为原始HTML插值。

Attribute:对于元素属性Attribute,不能用 Mustache语法,应该用 v-bind指令

<div v-bind:id="dynamicId"></div>

上述 v-bind 指令表示,id属性的值是变量dynamicId的值,简单理解就是这个id的值是需要解析获得的,不是表面看到的。对于 Attribute 的值绑定,有几种特别情况需要了解。

如果绑定的值是 null 或 undefined (表示“空”),那么这个 attribute 就不会包含在渲染的元素中。

对于布尔型属性 (它们存在就是 true,例如 checkbox 的 checked),如果绑定的值等价于 true,那么该属性就会被包含,如果绑定的值是空字符串,该属性也会被包含(即空字符串这里等价于true),其他绑定的值等价于false,该属性就被省略。

<html>

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

<body>
    <div id="app">
        <button v-bind:disabled="b1">按钮b1</button>
        <button v-bind:disabled="b2">按钮b2</button>
        <button v-bind:disabled="b3">按钮b3</button>
    </div>
    <script>
        const App = {
            data() {
                return {
                    b1: 3 + 5 > 7,
                    b2: '',
                    b3: 3 + 5 > 9
                }
            }
        }
        Vue.createApp(App).mount('#app')
    </script>
</body>

</html>

效果 

使用Javascript表达式: 绑定的值,还可以是 Javascript表达式 (但不能是语句或流控制)

<html>

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

<body>
    <div id="app">
        <p>{{ number+1 }}</p>
        <p>{{ ok ? 'YES' : 'NO' }}</p>
        <p>{{ message.split('').reverse().join('') }}</p>
        <div :id="'list-' + id">List1</div>
        <!-- <p>{{ var a = 1 }}</p>
        <p>{{ if (ok) { return message } }}</p> -->
    </div>
    <script>
        const App = {
            data() {
                return {
                    number: 999,
                    ok: false,
                    message: 'Hello, Vue3',
                    id: 1
                }
            }
        }
        Vue.createApp(App).mount('#app')
    </script>
</body>

</html>

上述代码中,注释部分如果去掉注释符号,将编译出错。效果如下:

指令

指令是带有 v- 前缀的特殊 attribute,多数指令的值是单个Javascript表达式。指令的作用是,表达式的值改变时,将这种影响响应式地作用到DOM,例如,我们用 v-if 指令,根据表达式值的真假来决定渲染该DOM还是移除该DOM。

指令参数:像 v-bind 这样的指令,需要用指令参数来明确要把值绑定给谁(这个谁往往是HTML本身的attribute)

<a v-bind:href="url"> ... </a>
<a v-on:click="doSomething"> ... </a>

动态参数: 所谓动态参数,就是前面的这个“谁”是不确定的,是变量,语法是用方括号括起来

<a v-bind:[attributeName]="url"> ... </a>
<a v-on:[eventName]="doSomething"> ... </a>

修饰符:修饰符是对指令参数的额外说明,语法是用句号

<form v-on:submit.prevent="onSubmit">...</form>

对 submit事件使用了修饰符 .prevent,这等于告诉 v-on 指令,在触发该事件执行对应方法时,需要额外调用 submit.preventDefault(),即 .prevent 修饰符等价于额外调用事件的 preventDefault()方法。

缩写

对于 v-bind缩写,就是把它去掉,即冒号:开头的就是 v-bind。

对于 v-on缩写,是连同后面的冒号替换成@

Data Property 和方法

组件的 data 对应的是一个函数,而非数据对象,尽管这个函数本身是返回一个对象。Vue在创建新组件实例的过程中会调用 data() 函数,Vue通过响应性系统把data()返回的对象包裹起来,以 $data 的形式存储在组件实例中。

<html>

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

<body>
    <div id="app">
        <p>{{ message }}</p>
        <input v-model="message" />
        <p>{{ nonReactive }}</p>
        <input v-model="nonReactive" />
    </div>
    <script>
        const App = {
            data() {
                return {
                    message: 'Hello Vue3'
                }
            }
        }
        var vm = Vue.createApp(App).mount('#app')
        setInterval(function () {
            console.log(vm.$data.message)
            console.log(vm.message)
            console.log(vm.$data.nonReactive)
            console.log(vm.nonReactive)
        }, 5000)
        vm.message = 'New message'
        console.log(vm.$data.message)
        vm.$data.message = 'Message from $data'
        console.log(vm.message)
        vm.nonReactive = 'Add nonReactive data'
    </script>
</body>

</html>

 对于第一个input文本框,它绑定了data()内的变量message,这样,input值改变立刻影响message,再影响上部<p>显示。对于第二个文本框,它试图绑定动态添加到vm的属性nonReactive,这个属性不会被包裹到 $data,不会被监视,从而不具有响应性,即修改第二个文本框的值不会影响到它上面的<p>中的显示。但神奇的是,vm.nonReactive 修改后,等到上面一个文本框的值改变从而影响上一个<p>时,同时会影响到下一个<p>的显示。这似乎表明,初始vm.* 包裹到 vm.$data.*,后者是被监视的变量,并且值总是和前者保持一直,渲染值总是从vm.*读取,被监视的变量有改变时,触发渲染更新。

(上图不代表内部工作原理 ),红色路径表示触发了响应,vm.* 的值投射到 <p>{{ ... }}</p>,绿色路径不会触发投射。

方法

组件的 methods 对应的是对象,只是对象中每个键值对都是函数,使用 ES2015+ 语法可以将键值对直接写成函数。Vue自动为 methods 中的函数绑定组件实例,所以这些函数称为方法,在方法中可以用 this 引用组件实例。

<html>

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

<body>
    <div id="app">
        <p>{{ count }}</p>
        <button @click="increment">投票</button>
        <p><span :title="toTitleDate(date)">{{ formatDate(date) }}</span></p>
    </div>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    count: 4,
                    date: new Date()
                }
            },
            methods: {
                increment() {
                    this.count++
                    this.date = new Date()
                },
                toTitleDate(d) {
                    return d.toString()
                },
                formatDate(d) {
                    let year = d.getFullYear()
                    let month = d.getMonth()
                    let date = d.getDate()
                    let hours = d.getHours()
                    let minutes = d.getMinutes()
                    let seconds = d.getSeconds()
                    return year + '年' + month + '月' + date + '日'
                        + hours + '时' + minutes + '分' + seconds + '秒'
                }
            }
        })
        var vm = app.mount('#app')
        console.log(vm.count)
        
        vm.increment()
        console.log(vm.count)
    </script>
</body>

</html>

方法通常被当作事件监听的handler使用,如上图中的 increment。也可以直接从模板中调用方法,如上图中的 toTitleDate、formatDate,这两个方法访问了响应式数据(调用时的参数date是data()中定义的),从而它们作为渲染依赖项被跟踪,即我们点击按钮触发事件,执行 increment,date被修改后,toTitleDate(date) 和 formatDate(date) 也会自动执行更新。当然,多数情况下,用计算属性替换直接从模板中调用方法会更好。

防抖(debounce)和节流(throttle)

函数防抖(debounce)就是说事件触发后,在一定时间范围内只能执行一次,如果在这个时间范围内又触发了该事件,则重新计算函数延迟执行的时间。debounce/throttle 存在的目的是,对于前端的一些事件,例如onresize、scroll、mousemove、mousehover等,会频繁触发,如果响应函数内部执行了其他函数,尤其是操作 DOM 或者 ajax 请求,就会卡死浏览器或造成网络拥塞。

debounce 其实就类似电路中的施密特触发,延迟触发,只执行一次。但有些场合,只执行一次是不合适的,例如鼠标拖拽一个东西,只执行一次就会出现突跳感,这时需要节流(throttle),就是减少触发,但保持在一个感觉流畅的水平。

Vue没有内置的 debounce 和 throttle,可以使用 Lodash 库(Lodash Documentation)有关功能实现。

<html>

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

<body>
    <div id="app">
        <save-button :text="label"></save-button>
    </div>
    <script src="https://unpkg.com/lodash@4.17.20/lodash.min.js"></script>
    <script>
        const app = Vue.createApp({    
            data() {
                return {
                    label: '保存'
                }
            }
        })

        app.component('save-button', {
            created() {
                this.debouncedClick = _.debounce(this.click, 500)
            },
            unmounted() {
                this.debouncedClick.cancel()
            },
            methods: {
                click() {
                    alert('保存数据')
                }
            },
            props: ['text'],
            template: `
                <button @click="debouncedClick">{{ text }}</button>
            `
        })

        var vm = app.mount('#app')
        
    </script>
</body>

</html>

例子中,我们为保存按钮组件的点击事件创建了带防抖的处理方法 debouncedClick,值得注意的是,debouncedClick 并不是组件 methods 中定义的方法,因为 methods 中定义的方法,是所有实例共享的,而 debounce 的特点是延迟响应且响应一次,这对于可复用组件有潜在问题(多个实例会相互影响),解决办法是让 debounce方法局限于每个实例,即在 created钩子中动态创建(debounce方法包裹了原始点击处理方法),在 unmounted钩子中调用 debounce方法随带的cancel方法取消掉延迟调用的方法。对实际点击事件,是 debounce方法(代理者)进行响应的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值