二.vue3基础

本文是Vue3基础教程,涵盖模板语法、class与style的绑定、条件渲染、列表渲染、事件处理器、表单控件的v-model绑定等内容。介绍如何使用Volar插件进行Vue3开发,以及Vue3中v-html的陷阱和数组变动侦测。还探讨了v-if与v-show的区别、v-for的key设置、计算属性和watch的使用,以及Vue3中不再支持过滤器的替代方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

二.vue3基础

1.模板语法

1-1 第一个 vue 应用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<body>
    <div id="app">
        {{10 + 20}}
        {{name}}
    </div>
</body>
<script>
    Vue.createApp(
        {
            data(){  // data:function(){}
                return{
                    name: "phoenix"
                }
            }
        }
    ).mount("#app")
</script>
</html>

image

  • 推荐使用的 IDE 是 VSCode,配合 Vue 语言特性 (Volar) 插件。该插件提供了语法高亮、TypeScript 支持,以及模板内表达式与组件 props 的智能提示。
  • Volar 取代了我们之前为 Vue 2 提供的官方 VSCode 扩展 Vetur。如果你之前已经安装了 Vetur,请确保在 Vue 3 的项目中禁用它。

1-2 应用动态绑定原理

Object.defineProperty(这种是 JS 中实现的方法,也是 Vue2 中使用的方法)

    var obj = {}
    var obox = document.getElementById("box")
    Object.defineProperty(obj, "myname", {
        get() {
            console.log("有人访问了")
            return obox.innerHTML
        },
        set(value) {
            console.log("有人改变我了", value)
            obox.innerHTML = value
        }
    })
/*
* 缺陷:无法监听数组的改变, 无法监听class改变, 无法监听Map Set结构。
*/
  
// ES6 proxy
var obj = {
    }
    var obox = document.getElementById("box")
    var vm = new Proxy(obj, {
        get(target, key) {
            console.log("get")
            return target[key]
        },
        set(target, key, value) {
            console.log("set")
            target[key] = value
            obox.innerHTML = value
        }
    })
/*
	vue3 基于Proxy ,ES6 Proxy,对不兼容ES6的浏览器,使用ES5的写法
	if(支持proxy){
	// proxy进行拦截处理, 实现功能
	}else{
	// object.defineProtery
	}

浏览器插件 vue-tools

1-3 模板语法

  1. 最基本的数据绑定形式是文本插值,它使用的是“Mustache”语法 (即双大括号):

    <span>Message: {{ msg }}</span>
    
  2. 双大括号标签会被替换为相应组件实例中 msg 属性的值。同时每次 msg 属性更改时它也会同步更新。

  3. 双大括号不能在 HTML attributes 中使用。想要响应式地绑定一个 attribute,应该使用 v-bind 指令:

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

    v-bind 指令指示 Vue 将元素的 id attribute 与组件的 dynamicId 属性保持一致。如果绑定的值是 null 或者 undefined ,那么该 attribute 将会从渲染的元素上移除。

    当一个 html 标签上需要绑定多个值时候,可以使用 v-bind="xxx 对象"进行对象绑定,xxx 表示的是对象名称

  4. 表达式的支持

    {{ number + 1 }}
    {{ ok ? 'YES' : 'NO' }}
    {{ message.split('').reverse().join('') }}
    <div :id="`list-${id}`"></div>
    
  5. 指令 v-on、@

    <a v-on:click="doSomething"> ... </a>
    <!-- 简写 -->
    <a @click="doSomething"> ... </a>
    

1-4 Todolist-案例

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

<body>
    <div id="app">
        <input type="text" v-model="mytext" @input="handleInput">
        <button @click="handleAdd">add</button>
        <ul>
            <li v-for="data,index in datalist">{{data}}
                <button @click="handleDel(index)">del</button>
            </li>
         
        </ul>
        <div v-if="datalist.length === 0">暂无代办</div>
    </div>
</body>
<script>
    var obj = {
        data() {
            return {
                mytext: "",
                datalist: ["1", "2", "3"],
            }
        },
        methods: {
            handleAdd(e) {
                this.datalist.push(this.mytext)
                console.log(e)
              
            },
            handleDel(index) {
                this.datalist.splice(index, 1)
            },
            handleInput(e){
                console.log(e.target.value)
            }
        }
    }
    var app = Vue.createApp(obj).mount("#app")
</script>

</html>

1-5 点击变心案例

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
    .active{
        background-color: red;
    }
</style>
<body>
    <div id="app">
        <input type="text" v-model="mytext" @input="handleInput">
        <button @click="handleAdd">add</button>
        <ul>
            <li v-for="data,index in datalist" :class="current === index ? 'active':''" @click = handleClick(index)>{{data}}
                <button @click="handleDel(index)">del</button>
            </li>
         
        </ul>
        <div v-if="datalist.length === 0">暂无代办</div>
    </div>
</body>
<script>
    var obj = {
        data() {
            return {
                mytext: "",
                datalist: ["1", "2", "3"],
                current: 0,
            }
        },
        methods: {
            handleAdd(e) {
                this.datalist.push(this.mytext)
                console.log(e)
              
            },
            handleDel(index) {
                this.datalist.splice(index, 1)
            },
            handleInput(e){
                console.log(e.target.value)
            },
            handleClick(index){
                this.current = index
            }
        }
    }
    var app = Vue.createApp(obj).mount("#app")
</script>

</html>

1-6 v-html- 模板的陷阱

双大括号会将数据解释为纯文本,而不是 HTML。若想插入 HTML,你需要使用 v-html 指令:

<p>Using text interpolation: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>

一般都是后台返回一段 HTML 片段,需要前端展示(一般存在于富文本编辑器,进行文章发布等),对于自己熟悉的后台,可以使用 v-html,但是对于不信任网站,可能会造成信息泄露

image

2.class 与 style

2-1 class 的绑定

对象写法

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
    .active{
        background-color: aquamarine;
    }
    .text-danger{
        color: red;
    }
</style>
<body>
    <div id="app">
        <h3 :class="classObject">mainIndex</h3>
        <button @click="changeClass">changeClass</button>
    </div>
</body>
<script>
    var obj = {
        data() {
            return {
                classObject:{
                    active: true,
                    'text-danger': false
                }
            }
        },
        methods: {
            changeClass(){
                this.classObject.active = !this.classObject.active;
                this.classObject['text-danger'] = !this.classObject['text-danger']
            }
        }
    }
    var app = Vue.createApp(obj).mount("#app")
</script>

</html>

数组写法

data() {
return {
activeClass: 'active',
errorClass: 'text-danger'
}
}

<div :class="[activeClass, errorClass]"></div>

2-2 style 的绑定-同上

对象写法

data() {
return {
styleObject: {
color: 'red',
fontSize: '13px'
}
}
}
<div :style="styleObject"></div>

数组写法

data() {
	return {
		arr:[{
		width:"200px",
		height:"200px",
		backgroundSize:"cover" // 可以采用驼峰命名或者‘background-size':  'cover'
		}],
		}
	}
	this.arr.push({
	backgroundImage:"url(https://pic.maizuo.com/usr/movie/862ab6736237acd11599e5eecbbc83d7.jpg?x-ossprocess=image/quality,Q_70)"
})

数组就 push、pop,对象就直接写对象名称,删除对象属性就是为对象赋值为 undefined

3.条件渲染

3-1 条件渲染

v-if 是“真实的”按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子组件都会被销毁与重建。

v-if 也是惰性的:如果在初次渲染时条件值为 false,则不会做任何事。条件区块只有当条件首次变为 true 时才被渲染。

相比之下, v-show 简单许多,元素无论初始条件如何,始终会被渲染,只有 CSS display 属性会被切换。

总的来说, v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要频繁切换,则使用 v-show 较好;如果在运行时绑定条件很少改变,则 v-if 会更合适。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
    .active{
        background-color: aquamarine;
    }
    .text-danger{
        color: red;
    
    }
    .text-size{
        font-size: 100px;
    }
</style>
<body>
    <div id="app">
        <ul>
            <li v-for="value,index in datalist">
                {{value.title}}
                <div v-if="value.state === 0">未付款</div>
                <div v-else-if="value.state === 1">未发货</div>
                <div v-else-if="value.state === 2">已发货</div>
                <div v-else>已完成</div>
            </li>
            <!-- 也可以配合template模板语法渲染页面,渲染后不会在页面上出现template标签 -->
            <template v-if="true">
                <div>1111</div>
                <div>2222</div>
                <div>3333</div>
            </template>
        </ul>
    </div>
</body>
<script>
    var obj = {
        data() {
            return {
                datalist:[
                    {
                        state: 0,
                        title: "111"
                    },
                    {
                        state: 1,
                        title: "222"
                    },
                    {
                        state: 2,
                        title: "333"
                    }
                ]
            }
        },
        methods: {

        }  
    }
    var app = Vue.createApp(obj).mount("#app")
</script>
</html>

4.列表渲染

4-1 v-for 列表渲染

列表渲染中分为对象渲染、数组渲染、数字渲染

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

<body>
    <div id="app">
        <!-- 列表渲染 -->
        <ul>
            <li v-for="value,key in list" :key="key">{{value}}-{{key}}</li>
        </ul>

        <!-- 对象渲染 -->
        <ul>
            <li v-for="(value,key,index) in objList" :key="key">{{key}}--{{value}}</li>
        </ul>

        <!-- 数字渲染 -->
        <ul>
            <li v-for="value,key in num" :key="key">{{value}}--{{key}}</li>
        </ul>
    </div>
</body>
<script>
    var app = Vue.createApp(
        {
            data() {
                return {
                    list: ["111","2222","3333"],
                    objList:{
                        "1":"a",
                        "2":"b",
                        "3":"c",
                    },
                    num:10
                }
            }
        }
    ).mount("#app")
</script>
</html>

​​image​​

  • 支持小括号
  • 支持解构
  • in === of (js 迭代器)

v-for 与 v-if

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

<ul >
<template v-for="{title,state},index in datalist" >
<li v-if="state===1">{{title}}</li>
</template>
</ul>

4-2 key 设置 - 性能的保障

Vue 默认按照“就地更新”的策略来更新通过 v-for 渲染的元素列表。当数据项的顺序改变时,Vue 不会随之移动 DOM 元素的顺序,而是就地更新每个元素,确保它们在原本指定的索引位置上渲染。

为了给 Vue 一个提示,以便它可以跟踪每个节点的标识,从而重用和重新排序现有的元素,你需要为每个元素对应的块提供一个唯一的 key attribute 来高效的对比新、旧虚拟 dom,从而改变真实 dom:

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

虚拟 dom

{
type: 'div',
props: {
id: 'container'
},
children: [
{
type: 'span',
props: {
class: 'text1'
},
children: 'hello '
},
{
type: 'span',
props: {
class: 'text2'
},
children: 'kerwin'
},
]
}

真实 dom

<div id="container">
<span class="text1">hello </span>
<span class="text2">kerwin</span>
</div>

4-3 数组变动侦测

在 Vue2 中通过索引更新数组或对象,不会被侦听到,页面上不会发生变化,但是在 Vue3 因为使用的是 proxy,所以会被侦听到,从而改变页面渲染

Vue 能够侦听响应式数组的变更方法,并在它们被调用时触发相关的更新。这些变更方法包括:

push()

pop()

shift()

unshift()

splice()

sort()

reverse()

对于一些不可变 (immutable) 方法,例如 filter() ,concat() 、slice()、map() ,这些都不会更改原数组,而总是返回一个新数组。当遇到的是非变更方法时,我们需要将旧的数组替换为新的:

this.items = this.items.filter((item) => item.message.match(/Foo/))

this.datalist = this.datalist.map(item => "phoenix")

4-4 模糊搜索案例

方案 1

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
    .active {
        background-color: aquamarine;
    }

    .text-danger {
        color: red;

    }

    .text-size {
        font-size: 100px;
    }
</style>

<body>
    <div id="app">
        <input type="text" v-model="mytext">
        <ul>
            <template v-for="value in datalist" :key="value">
                <li v-if="value.includes(mytext)">{{value}}</li>   <!-- includes:判断字符串是否包含,包含true/false-->
            </template>
        </ul>
    </div>
</body>
<script>
    var obj = {
        data() {
            return {
                mytext: "",
                datalist: ["aaa", "abb", "aab", "bcc", "abc", "bcd", "add", "acd"]
            }
        },
        methods: {

        }

    }
    var app = Vue.createApp(obj).mount("#app")
</script>

</html>

方案 2

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
    .active {
        background-color: aquamarine;
    }

    .text-danger {
        color: red;

    }

    .text-size {
        font-size: 100px;
    }
</style>

<body>
    <div id="app">
        <input type="text" v-model="mytext" />
        <ul>
            <li v-for="data in filterList()" :key="data">{{data}}</li>
        </ul>
    </div>
</body>
<script>
    var obj = {
        data() {
            return {
                mytext: "",
                datalist: ["aaa", "abb", "aab", "bcc", "abc", "bcd", "add", "acd"]
            }
        },
        methods: {
            filterList(evt) {
                return this.datalist.filter(item => item.includes(this.mytext))
            }
        }
    }
    var app = Vue.createApp(obj).mount("#app")
</script>
</html>

Vue3 支持在{{}}和指令中中使用表达式(函数)

5.事件处理器

5-1 事件处理器 - 告别原生事件

事件分类

@submit

@click

@

内联事件处理器

<button @click="count++">Add 1</button>
<button @click="test('hello')">test hello</button>

方法事件处理器

<button @click="test">test</button>

事件处理器默认会接收一个evt(用于事件的接受)
test(evt){
	console.log(evt)
}

如果需要传入标签的事件对象,需要在函数最后传入 $event

<button @click="handleClick(1,2,3,$event)">test hello</button>

handleClick(a,b,c,evt){
console.log("111",a,b,c)
console.log(evt)
console.log(evt.target)

或者结合方法事件处理器
<li @click="(evt)=>handleClick(1,2,3,evt)">2222</li>
同样也会获得该标签的传递参数和事件

当子事件发生,默认父亲事件也会同样发生,这就是冒泡,可以通过阻止默认行为

evt.stopPropagation()

5-2 事件修饰符 - 事件偷懒符

Vue 为 v-on 提供了事件修饰符。修饰符是用 . 表示的指令后缀,包含以下这些:

名称作用
.stop阻止事件冒泡
.prevent阻止默认行为
.self只有当事件是从事件目标本身触发时才触发处理程序
.capture以捕获模式添加事件侦听器
.once只触发一次
.passive指示事件处理程序不会调用 preventDefault()

其中,.stop ​和 .prevent ​是最常用的事件修饰符,用于阻止事件冒泡和默认行为。通常在高级组件开发中,.capture ​也会比较常用。.self ​则适用于当事件目标本身与父级元素都绑定相同的事件时,只需要处理目标元素的情况。.once ​和 .passive ​则针对特定场景使用,例如只需要执行单次操作和优化事件处理性能等。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

常用的键盘事件

  • @keyup:键盘松开时触发,可在后面加上修饰符来指定触发的键,例如 @keyup.enter 表示按下回车键时触发。(这里用到了按键别名)
  • @keydown:键盘按下时触发,也可以加上修饰符来指定触发的键。
  • @keypress:键盘按键被按下并松开时触发,同样可以加上修饰符。

Vue 中提供的按键别名如下:

名称说明
.enter:回车键
.tab:制表键
.delete / .del:删除键
.esc:Esc 键
.space:空格键
.up:上箭头键
.down:下箭头键
.left:左箭头键
.right:右箭头键
.ctrl:Ctrl 键
.alt:Alt 键
.shift:Shift 键

.meta:Meta 键对于 Windows 键盘就是 Windows 键,而对于苹果键盘就是 Command 键

vue3 还提供了键盘编号对应的键盘名,只需要

<input type="text" @keyup.a="handleTest">

handleTest(evt){
    console.log(1111)

}
当输入键盘a时候,就会触发键盘事件,调用事件函数

模态框案例

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
    #overlay {
        background: rgba(0, 0, 0, 0.6);
        width: 100%;
        margin: auto;
        position: fixed;
        top: 0;
        Left: 0;
        right: 0;
        bottom: 0;
    }

    #center {
        background: #ffff;
        border-radius: 5px;
        /* 边框圆角 */
        padding-top: 15px;
        padding-Left: 30px;
        padding-bottom: 15px;
        width: 290px;
        height: 160px;
        position: fixed;
        margin: auto;
        Left: 0;
        right: 0;
        top: 0;
        bottom: 0;
    }
</style>

<body>
    <div id="app">
        <button @click="isShow=!isShow">显示模态框</button>
        <div id="overlay" v-show="isShow" @click="isShow=!isShow">
        <!-- <div id="overlay" v-show="isShow" @click.self="isShow=!isShow"> -->
            <div id="center" @click.stop>
                username: <input/>
                <button>login in</button>
                <button @click="isShow=!isShow">关闭</button>
            </div>
        </div>
    </div>
</body>
<script>
    var obj = {
        data() {
            return {
                isShow:false,
            }
        },
        methods: {
        }

    }
    var app = Vue.createApp(obj).mount("#app")
</script>

</html>

6.表单控件绑定 v-model

6-1 表单输入绑定-一根绳上的蚂蚱

普通文本

<input v-model="message" placeholder="edit me" />

复选框

<input type="checkbox" id="checkbox" v-model="checked" />
checked:true

复选框就是给定一个数组,为每个复选框选项赋值一个value,
当点击以后,value值会加入到这个v-model绑定的数组中
<div>
	你喜欢的运动:{{favList}}
	<div>
		<input type="checkbox" v-model="favList" value="篮球">篮球
		<input type="checkbox" v-model="favList" value="足球">足球
		<input type="checkbox" v-model="favList" value="乒乓球">乒乓球
		<button>提交</button>
	</div>
</div>

favList: []

v-model 绑定的是 Boolean 值,即 true 或者 false

单选框

<div>Picked: {{ picked }}</div>
<input type="radio" id="one" value="One" v-model="picked" />

<div>
	你最喜欢的运动:{{picked}}
	<div>
		<input type="radio" v-model="picked" value="篮球">篮球
		<input type="radio" v-model="picked" value="足球">足球
		<input type="radio" v-model="picked" value="乒乓球">乒乓球
		<button>提交</button>
	</div>
</div>


picked:"足球"

选择器

<div>Selected: {{ selected }}</div>
<select v-model="selected">
<option disabled value="">Please select one</option>
	<option value="a">A</option>
	<option value="b">B</option>
	<option value="c">C</option>
</select>

6-2 购物车案例 🌟

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
    li{
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 10px;
        border: 1px solid lightgray;
    }

    li img{
        width: 100px;
    }
</style>

<body>
    <div id="app">
        <ul>
            <li>
                <div>
                    <input type="checkbox"  v-model="isAllChecked" @change="handleAllChange">
                    <span>全选/全不选</span>
                </div>
            </li>
            <template v-if="datalist.length !== 0">
                <li v-for="(value,index) in datalist" :key="value.id">
                    <div>
                        <input type="checkbox" v-model="checkList" :value="value" @change="handleItemChange">
                    </div>
                    <div>
                        <img :src="value.poster" :alt="value.title">
                    </div>
                    <div>
                        <div>{{value.title}}</div>
                        <div style="color: red;">{{value.price}}</div>
                    </div>
                    <div>
                        <button @click="value.number--" :disabled="value.number === 1">-</button>
                        {{value.number}}
                        <button @click="value.number++" :disabled="value.number === value.limit">+</button>
                    </div>
  
                    <div>
                        <button @click="handleDel(index,value.id)">删除</button>
                    </div>
                </li>
                <li>
                    <div>
                        总金额:{{sum(checkList)}}
                    </div>
                </li>
            </template>
      
            <template v-else>
                <li>购物车为空</li>
            </template>
  
        </ul>
        <!-- {{checkList}}
        -----------------------
        {{datalist}} -->
    </div>
</body>
<script>
    var obj = {
        data() {
            return {
                isAllChecked:false,
                checkList:[], // 勾选的商品列表
                datalist: [
                    {
                        id: 1,
                        title: "商品1",
                        price: 10,
                        number: 1,
                        poster: "https://fc.sinaimg.cn/large/008w3CKjgy1hfk6lcrud0j307i0alwfq.jpg",
                        limit: 5,
                    },
                    {
                        id: 2,
                        title: "商品2",
                        price: 20,
                        number: 2,
                        poster: "https://fc.sinaimg.cn/large/008w3CKjgy1hfoyxf73s5j307i0arwfo.jpg",
                        limit: 6,
                    },
                    {
                        id: 3,
                        title: "商品3",
                        price: 30,
                        number: 3,
                        poster: "https://fc.sinaimg.cn/large/008w3CKjgy1hfnacjnf46j307i0al3zy.jpg",
                        limit: 8,
                    }
                ]
            }
        },
        watch:{

        },
        methods: {
            handleAllChange(){
                this.checkList = this.isAllChecked? this.datalist:[]
            },
            handleItemChange(){
                if(this.datalist.length === 0){
                    this.isAllChecked =false
                    return
                }
                this.isAllChecked = this.checkList.length === this.datalist.length
            },
            sum(checkList){
                var sum = 0;
                checkList.forEach(element => {
                    sum += element.price * element.number
                });
                return sum;
            },
            handleDel(index,id){
                this.datalist.splice(index,1)

                // 同步更新总价格
                this.checkList = this.checkList.filter(element => element.id !== id)

                // 同步全选
                this.handleItemChange()
            }
        }
    }
    var app = Vue.createApp(obj).mount("#app")
</script>
</html>

6-3 表单修饰符

.lazy(当页面失去焦点并且内容发生改变时候才会进行触发,这个是 @change 语法糖)

<!-- 在 "change" 事件后同步更新而不是 "input" -->
<input v-model.lazy="msg" />

.number

用户输入自动转换为数字,你可以在 v-model 后添加 .number 修饰符来管理输入:

number 修饰符会在输入框有 type=“number” 时自动启用。

<input v-model.number="age" />

.trim

默认自动去除用户输入内容中两端的空格,你可以在 v-model 后添加 .trim 修饰符:

<input v-model.trim="msg" />

7.计算属性

7-1 计算属性 - 会缓存

模板中的表达式虽然方便,但也只能用来做简单的操作。如果在模板中写太多逻辑,会让模板变得臃肿,难以维护。因此我们推荐使用计算属性来描述依赖响应式状态的复杂逻辑。

   {
        data() {
            return {
                author: {
                    name: 'John Doe',
                    books: [
                        'Vue 2 - Advanced Guide',
                        'Vue 3 - Basic Guide',
                        'Vue 4 - The Mystery'
                    ]
                }
            }
        },
// 结果会被缓存
        computed: {
            // 一个计算属性的 getter
            publishedBooksMessage() {
                // `this` 指向当前组件实例
                return this.author.books.length > 0 ? 'Yes' : 'No'
            }
        }
        { { publishedBooksMessage } }
    }

若我们将同样的函数定义为一个方法而不是计算属性,两种方式在结果上确实是完全相同的,然而,不同之处在于计算属性值会基于其响应式依赖被缓存。一个计算属性仅会在其响应式依赖更新时才重新计算。

7-2 之前案例的小改造

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
    li{
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 10px;
        border: 1px solid lightgray;
    }

    li img{
        width: 100px;
    }
</style>

<body>
    <div id="app">
        <ul>
            <li>
                <div>
                    <input type="checkbox"  v-model="isAllChecked">
                    <span>全选/全不选</span>
                </div>
            </li>
            <template v-if="datalist.length !== 0">
                <li v-for="(value,index) in datalist" :key="value.id">
                    <div>
                        <input type="checkbox" v-model="checkList" :value="value">
                    </div>
                    <div>
                        <img :src="value.poster" :alt="value.title">
                    </div>
                    <div>
                        <div>{{value.title}}</div>
                        <div style="color: red;">{{value.price}}</div>
                    </div>
                    <div>
                        <button @click="value.number--" :disabled="value.number === 1">-</button>
                        {{value.number}}
                        <button @click="value.number++" :disabled="value.number === value.limit">+</button>
                    </div>
  
                    <div>
                        <button @click="handleDel(index,value.id)">删除</button>
                    </div>
                </li>
                <li>
                    <div>
                        总金额:{{sum}}
                    </div>
                </li>
            </template>
        
            <template v-else>
                <li>购物车为空</li>
            </template>
  
        </ul>
    </div>
</body>
<script>
    var obj = {
        data() {
            return {
                // isAllChecked:false,
                checkList:[], // 勾选的商品列表
                datalist: [
                    {
                        id: 1,
                        title: "商品1",
                        price: 10,
                        number: 1,
                        poster: "https://fc.sinaimg.cn/large/008w3CKjgy1hfk6lcrud0j307i0alwfq.jpg",
                        limit: 5,
                    },
                    {
                        id: 2,
                        title: "商品2",
                        price: 20,
                        number: 2,
                        poster: "https://fc.sinaimg.cn/large/008w3CKjgy1hfoyxf73s5j307i0arwfo.jpg",
                        limit: 6,
                    },
                    {
                        id: 3,
                        title: "商品3",
                        price: 30,
                        number: 3,
                        poster: "https://fc.sinaimg.cn/large/008w3CKjgy1hfnacjnf46j307i0al3zy.jpg",
                        limit: 8,
                    }
                ]
            }
        },
        watch:{

        },
        computed:{
            sum(){
                var sum = 0;
                this.checkList.forEach(element => sum += element.price * element.number
               );
                return sum;
            },
            isAllChecked:{
                get(){
                    return this.checkList.length === this.datalist.length
                },
                set(checked){
                   this.checkList = checked?this.datalist:[]
                }

            }
        },
        methods: {
       
            handleDel(index,id){
                this.datalist.splice(index,1)

                // 同步更新总价格
                this.checkList = this.checkList.filter(element => element.id !== id)
            }
        }

    }
    var app = Vue.createApp(obj).mount("#app")
</script>

</html>

注意:

  1. Getter 不应有副作用

    计算属性的 getter 应只做计算而没有任何其他的副作用,这一点非常重要,请务必牢记。举例来说,不要在 getter 中做异步请求或者更改 DOM

  2. 避免直接修改计算属性值

    从计算属性返回的值是派生状态。可以把它看作是一个“临时快照”,每当源状态发生变化时,就会创建一个新的快照。更改快照是没有意义的,因此计算属性的返回值应该被视为只读的,并且永远不应该被更改——应该更新它所依赖的源状态以触发新的计算。

7-3 侦听器 watch 的对比

watch 选项期望接受一个对象,其中键是需要侦听的响应式组件实例属性 (例如,通过 data 或 computed 声明的属性)——值是相应的回调函数。该回调函数接受被侦听源的新值和旧值。

监听属性,需要具有深度绑定,当对象内部某一个发生变化时候,就会触发监听属性

watch: {
		// 侦听根级属性
		a(val, oldVal) {
		console.log(`new: ${val}, old: ${oldVal}`)
		},
		// 字符串方法名称
		b: 'someMethod',
		// 该回调将会在被侦听的对象的属性改变时调动,无论其被嵌套多深
		c: {
		handler(val, oldVal) {
		console.log('c changed')
		},
		deep: true
		},
		// 侦听单个嵌套属性:
		'c.d': function (val, oldVal) {
		// do something
	},
	// 该回调将会在侦听开始之后立即调用
	e: {
		handler(val, oldVal) {
			console.log('e changed')
			},
			immediate: true
		}
}

8.数据请求

8-1 Fetch

XMLHttpRequest 是一个设计粗糙的 API,配置和调用方式非常混乱, 而且基于事件的异步模型写起来不友好。

兼容性不好 polyfill: GitHub - camsong/fetch-ie8: A window.fetch JavaScript polyfill supporting IE8

fetch("http://localhost:3000/users")
.then(res=>res.json())
.then(res=>{
console.log(res)
})
fetch("http://localhost:3000/users",{
method:"POST",
headers:{
"content-type":"application/json"
},
body:JSON.stringify({
username:"kerwin",
password:"123"
})
})
.then(res=>res.json())
.then(res=>{
console.log(res)
})
fetch("http://localhost:3000/users/5",{
method:"PUT",
headers:{
"content-type":"application/json"
},
body:JSON.stringify({
username:"kerwin",
password:"456"
})
})
.then(res=>res.json())
.then(res=>{
console.log(res)
})
fetch("http://localhost:3000/users/5",{
method:"DELETE"
})
.then(res=>res.json())
.then(res=>{
console.log(res)
})

8-2 axios🌟🌟

Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。

axios - npm (npmjs.com)

<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  1. get 请求

    axios.get("http://localhost:3000/users",{
    	params:{
    	name:"kerwin"
    }
    }).then(res=>{
    	console.log(res.data)
    })
    
  2. post 请求

    axios.post("http://localhost:3000/users",{
    	name:"kerwin",
    	age:100
    }).then(res=>{
    	console.log(res.data)
    })
    
  3. put 请求

    axios.put("http://localhost:3000/users/12",{
    	name:"kerwin111",
    	age:200
    }).then(res=>{
    	console.log(res.data)
    })
    
  4. delete 请求

    axios.delete("http://localhost:3000/users/11").then(res=>{
    	console.log(res.data)
    })
    
  5. axios(config)配置

    axios({
    method: 'post',
    url: 'http://localhost:3000/users',
    data: {
    name: 'kerwin',
    age: 100
    }
    })
    .then(res => {
    console.log(res.data)
    }).catch(err=>{
    console.log(err)
    })
    

Axios 会将获取的 json 数据包装起来,我们需要通过 result.data 来获取后端返回的请求值

9.过滤器

9-1 vue3 过滤器不支持了 - 怎么办?

在 2.x 中,开发者可以使用过滤器来处理通用文本格式。

<p>{{ accountBalance | currencyUSD }}</p>
filters: {
currencyUSD(value) {
return '$' + value
}
}

虽然这看起来很方便,但它需要一个自定义语法,打破了大括号内的表达式“只是 JavaScript”的假设,这不仅有学习成本,而且有实现成本。

在 3.x 中,过滤器已移除,且不再支持。取而代之的是,我们建议用方法调用或计算属性来替换它们。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

unravel space

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值