本节讲了Vue基础语法的使用,包括插值语法、动态绑定属性(v-bind)、计算属性(computed)、事件监听(v-on)、条件判断(v-if…)、v-if和v-show的不同、循环遍历(v-for)、过滤器的使用以及双向绑定(v-model)

插值语法

mustache

Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。所有 Vue.js 的模板都是合法的 HTML,所以能被遵循规范的浏览器和 HTML 解析器解析。

  • 可以在模板中使用 JavaScript 表达式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div id="app">
<h1>{{message}}</h1>
<h2>{{a + b}}</h2>
<h2>{{a + ' ' + b}}</h2>
<h2>{{a + '' + b}}</h2>
<h2>{{c * 6}}</h2>
</div>
<script src="../node_modules/vue/dist/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
a: 1,
b: 2,
c: 3,
}
})
</script>

效果

image-20200801173845681

其它

  1. v-once:只显示第一次的数据
  2. v-text:解析为文本,同mustache
  3. v-html:解析为html
  4. v-cloak:等解析完后再显示数据
  5. v-pre:不解析mustache语法

动态绑定属性(v-bind)

作用:v-bind用于动态绑定一个或多个属性值,或者向另一个组件传递props值(这个学到组件时再介绍)

缩写:

预期:any (with argument) | Object (without argument)

参数:attrOrProp (optional)

动态绑定src以及href

1
2
3
4
5
6
7
8
9
10
11
12
13
<div id="app">
<img v-bind:src="imgUrl" alt="" width="500px">
<a v-bind:href="url">Dong</a>
</div>
<script src="../node_modules/vue/dist/vue.min.js"></script>
<script>
var app = new Vue({
el: '#app',
data: {
imgUrl: 'https://ypigy.gitee.io/img/code.jpg',
url: 'https://ypigy.gitee.io/'
}
})

动态绑定class(对象语法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<div id="app">
<!-- 使用函数返回类,注意函数小括号 -->
<h2 v-bind:class="a()">{{message}}</h2>
<!-- 直接使用对象 -->
<!-- <h2 v-bind:class="{pink:this.ispink,blue:this.isblue}">{{message}}</h2> -->
<button v-on:click='change'>点我变色</button>
</div>
<script src="../node_modules/vue/dist/vue.min.js"></script>
<script>
var app = new Vue({
el: '#app',
data: {
message: '你好哦',
ispink: true,
isblue: false
},
methods: {
change: function(){
this.ispink = !this.ispink
},
a: function(){
return {pink:this.ispink,blue:this.isblue}
}
}
})
</script>

动态绑定class(数组语法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="app">
<!-- 注意数组格式 ['a','b']和[a,b]的区别,'a'直接绑定类,a会根据寻找a值去找到对应的类名-->
<h2 :class='[a,b]'>{{message}}</h2>
<!-- <h2 :class="['a','b']">{{message}}</h2> -->
</div>
<script src="../node_modules/vue/dist/vue.min.js"></script>
<script>
var app = new Vue({
el: "#app",
data: {
message: "你好啊",
a: 'aaa',
b: 'bbb'
}
})
</script>

动态绑定style

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="app">
<!-- <h2 :style="{color: 'pink', fontSize:'100px'}">{{message}}</h2> -->
<h2 :style="{color: c, fontSize: s}">{{message}}</h2>
</div>
<script src="../node_modules/vue/dist/vue.min.js"></script>
<script>
var app = new Vue({
el: '#app',
data: {
message: "你好",
c: 'skyblue',
s: '100px'
}
})
</script>

计算属性

简介:一般情况下,我们会使用插值语法将我们要展示的值渲染到页面,但有时候我们可能需要对一些数据进行相应的计算后再显示,这个时候我们就可以用实例化的Vue对象中定义一个computed属性中去定义详细的方法

简单计算属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<div id="app">
<h2>{{firstName}} {{lastName}}</h2>
<h2>{{firstName + " " + lastName}}</h2>
<h2>{{fullname}}</h2>
</div>
<script src="../node_modules/vue/dist/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
firstName: 'z',
lastName: 'yy'
},
computed: {
fullname: function() {
return this.firstName + ' ' + this.lastName
}
}
})
</script>

复杂计算属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<div id="app">
<h2>总价格{{totalPrice}}</h2>
</div>
<script src='../node_modules/vue/dist/vue.min.js'></script>
<script>
const app = new Vue({
el: '#app',
data: {
fruits: [{
id: 100,
name: '苹果',
price: 6
}, {
id: 101,
name: '菠萝',
price: 10
}, {
id: 102,
name: '西瓜',
price: 5
}, {
id: 103,
name: '栗子',
price: 15
}, {
id: 104,
name: '百香果',
price: 12
}, ]
},
computed: {
totalPrice: function() {
let total = 0
// //forin方法
// for (a in this.fruits) {
// total += this.fruits[a].price
// }
//reduce方法
total = this.fruits.reduce((accumulator, currentValue) => {
// console.log(accumulator)
return accumulator + currentValue.price
}, 0)
return total
}
}
})
</script>

计算属性的setter和getter以及缓存

  • 每个计算属性都包含一个getter和一个setter
  • 使用getter来读取
  • 在某些情况下也可以提供一个setter方法(不常用)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<div id="app">
<h2>{{totalprice}}</h2>
</div>
<script src="../node_modules/vue/dist/vue.min.js"></script>
<script>
var app = new Vue({
el: '#app',
data: {
a: 'aaa',
b: 'bbb',
},
//computed会保存缓存,若a和b的数据不变,则totalprice不变,而methods无论如何每次都会执行一次函数,浪费性能
computed: {
totalprice: {
get: function(){
return this.a + ' ' + this.b
},
set: function(c){
let d = c.split(' ')
this.a = d[0]
this.b = d[1]
}
}
}
})
</script>

事件监听

v-on介绍

作用:绑定事件监听器

缩写:@

预期:Function | Inline Statement | Object

参数:event

v-on基础

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<body>
<div class="app">
<h1>{{message}}</h1>
<!-- 直接在绑定事件中定义方法(不常用) -->
<button v-on:click="message ++">+</button>
<!-- 定义方法名,在methods中实现 -->
<button v-on:click="reduce">-</button>
<!-- 语法糖 v-on:===@ -->
<!-- <button @click="reduce">-</button> -->
</div>
<script src="../node_modules/vue/dist/vue.min.js"></script>
<script>
const app = new Vue({
el: '.app',
data:{
message: 12,
},
methods:{
reduce(){
this.message --
}
},
})
</script>
</body>

v-on参数

  1. 当形参没有定义时,动态绑定的事件名可以不加括号
  2. 当形参定义后,不加括号则会默认绑定windows创建的event对象,如若加括号没定义参数则会返回underfinde
  3. 当有参数且需要定义实参,同时需要用到windows创建的event对象,则使用$event定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<body>
<div id="app">
<!-- 当形参没有定义时,可以不加括号 -->
<!-- <button @click="hhh">点我啊</button> -->
<!-- <button @click="hhh()">点我啊</button> -->

<!-- 当形参定义后,不加括号,则会默认绑定浏览器创建的event对象,如若加括号且没定义实参,则会返回underfinde(不报错) -->
<!-- <button @click="hhh()">点我啊</button> -->

<!-- 当有形参,且需要实参定义,并且同时需要浏览器默认创建的event对象时,则用$event定义(注意书写,不能拼错) -->
<button @click="hhh(123,$event)">点我爱</button>

</div>
<script src="../node_modules/vue/dist/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
methods: {
hhh(abc,e){
console.log(abc,e)
}
}
})
</script>
</body>

v-on修饰符

  • 阻止事件冒泡:.stop
  • 阻止默认事件:.prevent
  • 监听键盘按键:{.keyCode,.keyAlies}
  • 只触发一次回调:.once
  • 监听组件根元素的原生事件:.native
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<body>
<div id="app" @click="d">
啦啦啦啦啦
<!-- 阻止冒泡 .stop -->
<button @click.stop='c'>点我</button>

<!-- 阻止默认事件 .prevent-->
<form action="dong">
<input type="submit" @click.prevent='e'>
</form>

<!-- 监听键盘按键 {.keyCode} or {.keyAlies}-->
<!-- 判断是否键入空格,keyup:鼠标弹起 -->
<input type="text" @keyup.space='f'>

<!-- 触发一次回调 .once-->
<button @click.once='c'>我只能点一次</button>

<!-- 监听组件根元素的原生事件 .native (后面补充)-->
</div>
<script src="../node_modules/vue/dist/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
methods: {
c() {
console.log('ccc')
},
d() {
console.log('ddd')
},
e(){
console.log('aaa')
},
f(){
console.log('你键入了空格')
}
}
})
</script>
</body>

条件判断

v-if、v-else-if、v-else

  • 这三个指令与JavaScript的条件语句if、else、else if类似。

  • Vue的条件指令可以根据表达式的值在DOM中渲染或销毁元素或组件

案例1(布尔判断)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="app">
<!-- 当条件为真时 -->
<h1 v-if="ifTrue">我是一</h1>
<!-- 当条件不成立时 -->
<h1 v-else>我是二</h1>
</div>
<script src="../node_modules/vue/dist/vue.min.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
ifTrue: false
}
})
</script>

案例2(成绩判断器)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 成绩判断 -->
<div id="app">
<h1 v-if="score >= 80">优秀</h1>
<h1 v-else-if="score >= 60">良好</h1>
<h1 v-else>不及格</h1>
</div>
<script src="../node_modules/vue/dist/vue.min.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
score: 80
}
})
</script>

登录切换案例以及问题

实现按钮切换登陆方式(用户名-邮箱)

  1. 定义初步页面
  2. 为不同输入框进行条件判断(v-if、v-else)
    • 若条件为true,则使用用户名
    • 条件为false,则使用邮箱
  3. 为切换按钮绑定事件,动态改变条件(v-on)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<div id="app">
<span v-if="isTrue">
<h5>请输入用户名</h5>
<input type="text" placeholder="请输入用户名">
</span>
<span v-else>
<h5>请输入邮箱</h5>
<input type="text" placeholder="请输入邮箱">
</span>
<button @click="c">切换登录方式</button>
</div>
<script src="../node_modules/vue/dist/vue.min.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
isTrue: true,
},
methods: {
c(){
this.isTrue = !this.isTrue
}
}
})
</script>

出现的小问题:

如果我们在有输入内容的情况下,切换了类型,我们会发现文字依然显示之前的输入的内容。

但是按道理讲,我们应该切换到另外一个input元素中了。

在另一个input元素中,我们并没有输入内容。

为什么会出现这个问题呢?

问题解答:

这是因为Vue在进行DOM渲染时,出于性能考虑,会尽可能的复用已经存在的元素,而不是重新创建新的元素。

在案例中,Vue内部会发现原来的input元素不再使用,直接作为else中的input来使用了。

解决方案:

如果我们不希望Vue出现类似重复利用的问题,可以给对应的input添加key

并且我们需要保证key的不同

解决主要代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!-- 问题:在点击登录切换后,原先在文本框输入的内容还是会保留(涉及到vue底层,虚拟DOM的问题)-->
<div id="app">
<span v-if="isTrue">
<h5>请输入用户名</h5>
<!-- 解决方法:在控件后面添加key关键字 -->
<input type="text" placeholder="请输入用户名" key="username">
</span>
<span v-else>
<h5>请输入邮箱</h5>
<input type="text" placeholder="请输入邮箱">
</span>
<button @click="c">切换登录方式</button>
</div>
<script src="../node_modules/vue/dist/vue.min.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
isTrue: true,
},
methods: {
c(){
this.isTrue = !this.isTrue
}
}
})
</script>

v-show

v-show的用法和v-if非常相似,也用于决定一个元素是否渲染:

v-if和v-show对比

v-if和v-show都可以决定一个元素是否渲染

v-if当条件为false时,压根不会有对应的元素在DOM中。

v-show当条件为false时,仅仅是将元素的display属性设置为none而已。

开发中的选择

当需要在显示与隐藏之间切换很频繁时,使用v-show

当只有一次切换时,通过使用v-if

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
<div id="app">
<!-- 隐藏而非去除元素,相当于display:none -->
<h1 v-show="a">你好啊</h1>
</div>
<script src="../node_modules/vue/dist/vue.min.js"></script>
<script>
const app = new Vue({
el: "#app",
data:{
a: false
}
})
</script>

遍历循环

v-for遍历数组

v-for=”(项, 索引号) in 数组”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div id="app">
<ul>
<!-- 1.遍历数组中的每个项 -->
<li v-for="item in fruits">{{item}}</li>
<!-- 2.遍历数组中的值和索引 -->
<li v-for="(item, index) in fruits">{{++index +'-'+item}}</li>
</ul>
</div>
<script src="../node_modules/vue/dist/vue.min.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
fruits:['火龙果','橙子','香蕉','西瓜','栗子','苹果']
}
})
</script>

v-for遍历对象

v-for=”(值, 键, 索引) in 对象”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div id="app">
<ul>
<!-- 1.遍历对象中的每个值 -->
<li v-for="item in fruits">{{item}}</li>
<!-- 2.遍历对象的键值对 -->
<li v-for="(item, key) in fruits">{{key +'-'+item}}</li>
<!-- 3.遍历对象的键值对和索引 不常用-->
<li v-for="(item, key, index) in fruits">{{++index + '-'+key +'-'+item}}</li>
</ul>
</div>
<script src="../node_modules/vue/dist/vue.min.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
fruits: {
name: "火龙果",
qualityGuaranteePeriod: "30天",
producingArea: "China"
}
}
})
</script>

组件的key属性

作用key的作用主要是为了高效的更新虚拟DOM

为什么需要key属性

这个其实和Vue的虚拟DOM的Diff算法有关系。

这里我们借用React’s diff algorithm中的一张图来简单说明一下:

当某一层有很多相同的节点时,也就是列表节点时,我们希望插入一个新的节点

我们希望可以在B和C之间加一个F,Diff算法默认执行起来是这样的。即把C更新成F,D更新成C,E更新成D,最后再插入E,是不是很没有效率?

而如果有key属性,则每个值会与对应的key属性一一绑定,那么这个默认算法也就不再执行,而是通过像键值对一样的方式去匹配这些数据,保证代码的高效率

image-20200909164718380

🔺vue中的数据是响应式的

  • 因为Vue是响应式的,所以当数据发生变化时,Vue会自动检测数据变化,视图会发生对应的更新。

  • 🔺根据索引修改数组数据是不能成功的,要么通过vue自带方法Vue.set(修改对象,修改索引,修改后的值)去修改数组数据,要么使用splice()方法修改

  • Vue中包含了一组观察数组编译的方法,使用它们改变数组也会触发视图的更新

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

splice()

简介:可以删除、修改、替换元素

格式:目标数组.splice(开始索引位置,删除(替换)元素个数,…元素(插入替换的元素))

过滤器

filters 官网介绍

作用:用于一些常见的文本格式化(过滤到你做想要的数据结果格式)

  1. 在html中定义过滤器

    1
    <td>{{item.price | price}}</td>
  2. vue对象中编写过滤器的具体实现方法

    1
    2
    3
    4
    filters: {
    price(p) {
    return '¥' + p.toFixed(2)
    }

    v-model

前言: 表单控件在实际开发中是非常常见的。特别是对于用户信息的提交,需要大量的表单。

作用:Vue中使用v-model指令来实现表单元素和数据的双向绑定。

简单使用

当我们使用text控件并绑定某个数据时,修改text的输入内容时,相应的,这个数据也会被修改,同时,这个修改完后的数据也可以在其他地方渲染出来,这个时候,就实现了双向绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
<div id="app">
<input type="text" v-model="message">
<h2>{{message}}</h2>
</div>
<script src="../node_modules/vue/dist/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
}
})
</script>

原理(v-on结合v-bind)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<div id="app">
<!-- input控件包含input事件,当对该控件操作时会有相应的反应 -->
<input type="text" :value="message" v-on:input="change">
<!-- <input type="text" :value="message" @input="change"> -->
<h2>{{message}}</h2>
</div>
<script src="../node_modules/vue/dist/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
},
methods: {
change(event) {
this.message = event.target.value //获取到文本框输入数据
}
}
})
</script>

结合radio使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<div id="app">
<!-- for用来绑定与该label标签关联的控件 name属性用来确定控件名称,以达到互斥效果-->
<label for="male">
<input type="radio" name="sex" value="男" id="male" v-model="sex">
</label>
<label for="female">
<input type="radio" name="sex" value="女" id="female" v-model="sex">
</label>
<h2 v-if="sex.length === 1">你选择的性别是{{sex}}</h2>
<h2 v-else>{{sex}}</h2>s
</div>
<script src="../node_modules/vue/dist/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
sex: '请选择你的性别',
},
})
</script>

结合checkbox使用

  1. 单个勾选框
    • v-model即为布尔值。
    • input的value并不影响v-model的值。
  2. 多个勾选框
    • 当是多个复选框时,因为可以选中多个,所以对应的data中属性是一个数组
    • 当选中某一个时,就会将input的value添加到数组中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!-- 单选框 -->
<div id="app">
<label for="agree">
<input type="checkbox" v-model="isAgree" id="agree">同意协议
</label>
<h2>你选择的是{{isAgree}}</h2>
<button :disabled="!isAgree">注册</button>
</div>

<!-- 多选框 -->
<div id="app">
<input type="checkbox" v-model="fruits" value="苹果">苹果
<input type="checkbox" v-model="fruits" value="栗子">栗子
<input type="checkbox" v-model="fruits" value="李子">李子
<input type="checkbox" v-model="fruits" value="梨子">梨子
<input type="checkbox" v-model="fruits" value="橙子">橙子
<input type="checkbox" v-model="fruits" value="哈密瓜">哈密瓜
<h2>你选择的水果是{{fruits}}</h2>
</div>

<script src="../node_modules/vue/dist/vue.min.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
isAgree: false,
fruits: []
}
})
</script>

结合select使用

  1. 单选
    • v-model绑定的是一个值。
    • 当我们选中option中的一个时,会将它对应的value赋值到mySelect中
    • 注意定义的位置
  2. 多选
    • v-model绑定的是一个数组。
    • 当选中多个值时,就会将选中的option对应的value添加到数组mySelects中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<div id="app">
<!-- 单选 -->
<select v-model="fruit">
<option value="苹果">苹果</option>
<option value="梨子">梨子</option>
<option value="西瓜">西瓜</option>
<option value="香蕉">香蕉</option>
<option value="桃子">桃子</option>
<option value="哈密瓜">哈密瓜</option>
</select>
<h5 id="a">你选择的水果是{{fruit}}</h5>
<!-- 多选 -->
<select v-model="fruits" multiple>
<option value="苹果">苹果</option>
<option value="梨子">梨子</option>
<option value="西瓜">西瓜</option>
<option value="香蕉">香蕉</option>
<option value="桃子">桃子</option>
<option value="哈密瓜">哈密瓜</option>
</select>
<h5 id="b">你选择的水果是{{fruits}}</h5>

</div>
<script src="../node_modules/vue/dist/vue.min.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
fruit: '香蕉',
fruits: []
}
})
</script>

值绑定

根据已有数据去渲染页面的同时,动态绑定这些数据

就是v-bind动态绑定值,同时,使用v-model双向绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="app">
<label v-for="item in fruits" :for="item">
<input type="checkbox" :value="item" :id="item" v-model="fruits">{{item}}
</label>
<h5>你选择了{{fruits}}水果</h5>
</div>
<script src="../node_modules/vue/dist/vue.min.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
fruits:['火龙果','橙子','香蕉','西瓜','栗子','苹果']
}
})
</script>

v-model修饰符

  1. lazy修饰符:

    • 默认情况下,v-model默认是在input事件中同步输入框的数据的。
    • 一旦有数据发生改变对应的data中的数据就会自动发生改变。
    • lazy修饰符可以让数据在失去焦点或者回车时才会更新:
  2. number修饰符:

    • 默认情况下,在输入框中无论我们输入的是字母还是数字,都会被当做字符串类型进行处理。
    • 但是如果我们希望处理的是数字类型,这个就可以直接将内容当做数字处理。
    • number修饰符可以让在输入框中输入的内容自动转成数字类型:
  3. trim修饰符:

    • 如果输入的内容首尾有很多空格,通常我们希望将其去除
    • trim修饰符可以过滤内容左右两边的空格
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div id="app">
<!-- 1.lazy:失去焦点或回车才会实现加载更新 -->
<input type="text" v-model.lazy="message">
<h2>{{message}}</h2>
<!-- 2.number:当只需要数值型数据时使用该修饰符,当没有定义时除了初始值是数字外都会变成字符串类型 -->
<input type="number" v-model.number="num">
<h2>{{typeof num}}</h2>
<!-- 3.trim:去除输入的空格(页面显示是不会显示空格的,但在具体数据上会显示空格)-->
<input type="text" v-model.trim="hi">
<h2>{{hi}}</h2>
</div>
<script src="../node_modules/vue/dist/vue.min.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
message: "你好啊",
num: 16,
hi: "hhhhh"
}
})
</script>