03 Vue 基础语法
基础语法
mustache语法
{{msg}}
<!-- 可计算 -->
<!-- 可以是表达式,但不能是语句 -->
{{msg + site}}
{{msg}} {{site}}
双大括号只能作用于标签的内容,不能作为标签的属性
插值语法
v-once
<body>
<div id="app">
<h1>会变化:{{ msg }}</h1>
<h1 v-once>不会变化:{{ msg }}</h1>
</div>
<script src="js/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
msg: "sdu"
}
}
}).mount("#app");
</script>
</body>
还没想到什么常用的应用场景
v-text
<body>
<div id="app">
<!-- 这个更灵活 -->
<h1>{{ msg }},你好。</h1>
<p>--------------</p>
<h1 v-text="msg">默认内容</h1>
</div>
<script src="js/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
msg: "sdu"
}
}
}).mount("#app");
</script>
</body>
v-html
<body>
<div id="app">
{{site}}
<p>---------------</p>
<p>适用于展示性信息,业务型尽量不用这个</p>
<div v-html="site"></div>
<div v-html="intro"></div>
</div>
<script src="js/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
site: '<a href="http://www.sdu.edu.cn">sdu</a>',
intro: '<button>click</button>'
}
}
}).mount("#app");
</script>
</body>
v-pre
<body>
<div id="app">
<p v-pre>希望不产生编译:{{msg}}</p>
<h1>{{msg}}</h1>
</div>
<script src="js/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
msg: "sdu"
}
}
}).mount("#app");
</script>
</body>
v-cloak
<style>
[v-cloak] {
display: none;
}
</style>
<body>
<div id="app">
<h1>无v-cloak:{{msg}}</h1>
<h1 v-cloak>有v-cloak:{{msg}}</h1>
</div>
<script src="js/vue.js"></script>
<script>
// vue实例解析之前,h1标签有一个v-cloak属性
// vue实例解析之后,h1标签的v-cloak属性自动消失
setTimeout(()=>{
const app = Vue.createApp({
data() {
return {
msg: "sdu"
}
}
}).mount("#app");
},1000);
</script>
</body>
绑定属性
v-bind
<body>
<div id="app">
<h1>{{msg}}</h1>
<!-- 不显示 -->
<img src="imgUrl" alt="">
<!-- 动态绑定 -->
<!-- 针对标签里的属性 -->
<img v-bind:src="imgUrl" v-bind:alt="alt">
<a v-bind:href="site">SDU</a>
<!-- 简写 -->
<img :src="imgUrl" :alt="alt">
<a :href="site">SDU</a>
</div>
<script src="js/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
msg: "sdu",
imgUrl: "https://v3.cn.vuejs.org/logo.png",
alt: "Vue Logo",
site: "http://www.sdu.edu.cn"
}
}
}).mount("#app");
</script>
</body>
动态绑定
<style>
.yellow {
color: yellow;
}
.blue {
color: blue;
}
.f60 {
font-size: 60px;
}
</style>
<body>
<div id="app">
<h1 class="yellow">{{msg}}</h1>
<h1 :class="yellow">{{msg}}</h1>
<p>---------------</p>
<!-- <p :class="red f60">{{ msg }}</p> -->
<!-- 对象赋值 -->
<p :class="{yellow: isBlue, f60: isF60}">{{ msg }}</p>
<button @click="change">toggle</button>
<p style="background-color:wheat;" :class="{yellow: isBlue, f60: isF60}">{{ msg }}</p>
</div>
<script src="js/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
msg: "sdu",
yellow: "blue",
f60: "f60",
isBlue: true,
isF60: true
}
},
methods: {
change(){
this.isBlue = !this.isBlue;
this.isF60 = !this.isF60;
}
}
}).mount("#app");
</script>
</body>
数组赋值class
<body>
<div id="app">
<h3 class="red f60">{{ msg }}</h3>
<h3 :class="[red, f60]">{{ msg }}</h3>
<h3 :class="getClass()">{{ msg }}</h3>
</div>
<script src="js/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
msg: "sdu",
red: "red",
f60: "f60"
}
},
methods: {
getClass() {
return [this.red,this.f60];
}
}
}).mount("#app");
</script>
</body>
绑定style
以对象的方式为主
<body>
<div id="app">
<p style="font-size: 50px;">{{ msg }}</p>
<!-- <p :style="font-size: 50px;">{{ msg }}</p> -->
<p :style="{fontSize: '75px'}">{{ msg }}</p>
<p :style="{fontSize: fs}">{{ msg }}</p>
<p :style="{fontSize: fs, backgroundColor: bColor}">{{ msg }}</p>
<p :style="getStyle()">{{ msg }}</p>
</div>
<script src="js/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
msg: "sdu",
fs:"100px",
bColor:"wheat"
}
},
methods: {
getStyle(){
return {fontSize: "125px", backgroundColor: this.bColor};
}
}
}).mount("#app");
</script>
</body>
事件监听
v-on
<body>
<div id="app">
<h1>运算结果:{{ count }}</h1>
<button v-on:click="sub()">-</button>
<!-- <button @click="add()">+</button> -->
<button @click="add">+</button>
</div>
<script src="js/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
count: 0
}
},
methods: {
add(){
this.count++;
},
sub(){
this.count--;
}
}
}).mount("#app");
</script>
</body>
<body>
<div id="app">
<!-- 1. 不带参数 -->
<button @click="btnClick1()">btn1</button>
<button @click="btnClick2()">btn2</button>
<!-- 2. 带参数 -->
<button @click="btnClick3(123,'heihei',true,msg)">btn3</button>
<!-- 3. 带参数又需要事件对象 -->
<!-- $event -->
<button @click="btnClick4(123,'heihei',true,msg, $event)">btn4</button>
</div>
<script src="js/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
msg: "sdu"
}
},
methods: {
btnClick1(args){
console.log(args);
},
btnClick2(args){
console.log(args);
},
btnClick3(arg1, arg2, arg3, arg4){
console.log(arg1, arg2, arg3, arg4);
},
btnClick4(arg1, arg2, arg3, arg4,event){
console.log(arg1, arg2, arg3, arg4,event);
}
}
}).mount("#app");
</script>
</body>
$event
传递事件对象
- 事件冒泡取消
- 默认事件取消
- 响应一次事件
- 键盘事件
<style>
.box {
background-color: red;
width: 100px;
height: 100px;
}
</style>
<body>
<div id="app">
<!-- 1. 阻止冒泡 -->
<div class="box" @click="boxClick">
<button @click.stop="btnClick">click</button>
</div>
<!-- 2. 阻止默认事件 -->
<form action="http://www.sdu.edu.cn">
<input type="submit" value="提交" @click.prevent="doSubmit">
</form>
<a href="http://www.sdu.edu.cn" @click.prevent="aClick">SDU</a>
<br>
<!-- 3. 响应一次事件 -->
<button @click.once="btnClick">click</button>
<br>
<!-- 4. 键盘事件修饰符 -->
<input type="text" @keyup="getMsg"><br>
<input type="text" @keyup.enter="getMsg">
</div>
<script src="js/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
msg: "sdu"
}
},
methods: {
boxClick() {
console.log("Click box");
},
btnClick(event) {
// js中的事件默认是冒泡方式,逐层往上传播,
// 可以通过stopPropagation()函数停止事件在DOM层次中的传播,
// 通过dojo中的event.stop(evt);效果是相同的。
// event.stopPropagation();
console.log("Click btn");
},
doSubmit(event) {
// preventDefault它是事件对象(Event)的一个方法,
// 作用是取消一个目标元素的默认行为。既然是说默认行为,
// 当然是元素必须有默认行为才能被取消,
// 如果元素本身就没有默认行为,调用当然就无效了。
// 阻止默认事件
// event.preventDefault();
console.log("form submit");
},
aClick(){
console.log("Click a");
},
getMsg(event){
console.log(event);
}
}
}).mount("#app");
</script>
</body>
计算属性
<body>
<div id="app">
<p>SDU · Honglou</p>
<p>------1. 常规用法-------</p>
<p>{{ msg + ' · ' + campus }}</p>
<p>------2. 函数-----------</p>
<p>{{ getPosi() }}</p>
<p>------3. 计算属性-------</p>
<p>{{ fullPosi }}</p>
</div>
<script src="js/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
msg: "SDU",
campus: "Honglou"
}
},
computed: {
fullPosi() {
return this.msg + " · " + this.campus;
}
},
methods: {
getPosi() {
return this.msg + " · " + this.campus;
}
}
}).mount("#app");
</script>
</body>
复杂一点的计算属性
<body>
<div id="app">
<p>------------------------</p>
<p>TotalArea:{{ campuses[0].area + campuses[1].area + campuses[2].area + campuses[3].area }}</p>
<p>------------------------</p>
<p>TotalArea:{{ totalArea }}</p>
</div>
<script src="js/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
campuses: [
{name: "Honglou", area: 300},
{name: "Zhongxin", area: 200},
{name: "Ruanjian", area: 400},
{name: "xinglong", area: 1300}
]
}
},
computed: {
totalArea() {
total = 0;
for(let campus of this.campuses){
total += campus.area;
}
return total;
}
}
}).mount("#app");
</script>
</body>
计算属性的内部实现
<script>
const app = Vue.createApp({
data() {
return {
msg: "SDU",
campus: "Honglou"
}
},
computed: {
fullPosi: {
set(newValue) {
console.log("-----set()-----");
const arr = newValue.split("·");
this.msg = arr[0];
this.campus = arr[1];
},
get() {
console.log("-----get()-----");
return this.msg + " · " + this.campus;
}
}
},
methods: {
getPosi() {
return this.msg + " · " + this.campus;
}
}
}).mount("#app");
</script>
使用计算属性只会计算一次,内部会缓存,函数的话每调用一次就会重算一次.
结构
v-if
<body>
<div id="app">
<div v-if="flag">解封</div>
<div v-else>不解封</div>
<p>------------------------------</p>
<div v-show="flag">不想出去捏</div>
<div v-show="!flag">想出去捏</div>
<!-- v-show 和 v-if 的区别 -->
<button @click="flag=!flag">在干嘛捏</button>
</div>
<script src="js/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
msg: "SDU",
flag: true
}
}
}).mount("#app");
</script>
</body>
v-if
是反复销毁创建元素.
v-show
是通过css的 display:none;
实现的.
v-show
有点类似于switch-case
<body>
<!-- v-if 和 v-show 分别实现,看切换前后的区别 -->
<div id="app">
<div v-if="loginType === 'phone'">
<label for="">Phone:</label>
<input type="text" placeholder="请输入手机号"><br>
<label for="">Pwd:</label>
<input type="password" placeholder="请输入密码">
</div>
<div v-else-if=" loginType === 'email'">
<label for="">Email:</label>
<input type="text" placeholder="请输入邮箱"><br>
<label for="">Pwd:</label>
<input type="password" placeholder="请输入密码">
</div>
<button @click="change">切换登陆方式</button>
</div>
<script src="js/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
loginType: "phone"
}
},
methods: {
change() {
this.loginType === "phone"?this.loginType="email":this.loginType="phone";
}
}
}).mount("#app");
</script>
</body>
v-for
<body>
<div id="app">
<ul>
<li v-for="p in personArr">{{ p }}</li>
</ul>
<p>----------------------------------------</p>
<ul>
<li v-for="(p, index) in personArr">{{ index }} -- {{ p }}</li>
</ul>
<p>----------------------------------------</p>
<ul>
<li v-for="(p, index) in persons">{{ index }} -- {{ p.name }} - {{ p.age }} - {{ p.address }}</li>
</ul>
</div>
<script src="js/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
personArr: ["SDU",300,"Math"],
persons: [
{name:"Luo7",age:22,address:"SDU"},
{name:"Luo8",age:27,address:"SEU"},
{name:"Luo9",age:28,address:"SFU"}
]
}
}
}).mount("#app");
</script>
</body>
例子2:
<body>
<div id="app">
<ul>
<li v-for="item in person">{{ item }}</li>
</ul>
<p>----------------------------</p>
<ul>
<li v-for="(item, key) in person">{{key}}: {{ item }}</li>
</ul>
<ul>
<li v-for="(item, key, index) in person">{{ index }} -- {{ key }}: {{ item }}</li>
</ul>
</div>
<script src="js/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
person: {
name: "Luo",
age: 22,
friends: ["刘大海", "洛建国"]
}
}
}
}).mount("#app");
</script>
</body>
在2.x
中,如果在一个元素上同时使用 v-if
和 v-for
时,v-for
优先级更高.
在 3.x
中,v-if
优先级高于 v-for
.
因此尽量避免在一个元素上同时使用v-for
和 v-if
.
小例子
<body>
<div id="app">
<h3>搜索列表</h3>
<label for=""><input type="text" placeholder="请输入搜索的姓名" v-model="searchStr"></label>
<div>
<h3>排序</h3>
<button @click="setOrderType(2)">年龄升序</button>
<button @click="setOrderType(1)">年龄降序</button>
<button @click="setOrderType(0)">换原</button>
</div>
<ul>
<li v-for="(p, index) in filterPersons">
{{ index + 1 }} - 姓名: {{ p.name }}, 性别: {{ p.gender }}, 年龄: {{p.age}}, 手机: {{p.phone}}
</li>
</ul>
</div>
<script src="js/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
persons: [
{name: "Luo", gender: "M", age: 22, phone: "3357"},
{name: "Luo7", gender: "M", age: 23, phone: "9527"},
{name: "胖大海", gender: "M", age: 9, phone: "113357"},
{name: "张福兴", gender: "F", age: 66, phone: "13357"},
{name: "刘保健", gender: "F", age: 91, phone: "3231357"},
{name: "牛德华", gender: "M", age: 17, phone: "6663357"},
],
// 关联输入框
searchStr: "",
// flag: 0 默认, 1 降序, 2 升序
orderType: 0
}
},
computed: {
filterPersons() {
// 1. 取出相关的属性
const {persons, searchStr, orderType} = this;
// 2. 定义过滤数组
let arr = [...persons];
// 3. 根据条件过滤
if(searchStr.trim()){
arr = persons.filter((p)=>{
return p.name.indexOf(searchStr) != -1;
});
}
// 4. 排序
if(orderType) {
arr.sort((p1,p2)=>{
if(orderType === 1){
return p2.age - p1.age;
}else {
return p1.age - p2.age;
}
});
}
// 5. 返回过滤后的数组
return arr;
}
},
methods: {
setOrderType(orderType) {
this.orderType = orderType;
}
}
}).mount("#app");
</script>
</body>
为什么要绑定key?
- vue内部采用的是DOM
- 运用了Different算法
- key的作用主要是为了高效的更新虚拟DOM
- 运行过程
- 当页面中某一层级有许多相同的节点时(列表展示)
- 如果在头部或者中间插入一个新节点
- 默认流程效率低
- 绑定一个key作为唯一标识符,Diff算法可以正确识别节点,找到争取的位置插入新的节点
- key 最好使用字符串或数值类型的值
- key 可以避免潜在的bug
- key是给每个vnode的唯一id,可以依靠key,更准确/更快地拿到oldVnode中对应的vnode节点
<body>
<div id="app">
<div>
<label for="">ID: <input type="text" v-model="id"></label>
<label for="">Name: <input type="text" v-model="name"></label>
<button @click="add">add</button>
</div>
<p>----------------------------------</p>
<ul>
<li v-for="item in listArr" :key="item.id">
<input type="checkbox">
{{item.id}} --- {{item.name}}
</li>
</ul>
</div>
<script src="js/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
id: "",
name: "",
listArr: [
{id: 1, name: "张大炮"},
{id: 2, name: "张萝卜"},
{id: 3, name: "钱大富"},
{id: 4, name: "严丽"},
{id: 5, name: "刘大海"},
{id: 6, name: "金鑫鑫"},
]
}
},
methods: {
add() {
// 尾部追加
// this.listArr.push({id:this.id, name: this.name});
// 头部追加
// this.listArr.unshift({id:this.id, name: this.name});
// 中间追加
this.listArr.splice(2, 0, {id:this.id, name: this.name})
}
}
}).mount("#app");
</script>
</body>
表单绑定
v-model
实现方式
<body>
<div id="app">
<!-- <input type="text" v-model="msg"> -->
<h1>{{ msg }}</h1>
<!-- <input type="text" :value="msg" @input="valueChange"> -->
<input type="text" :value="msg" @input="msg=$event.target.value;">
</div>
<script src="js/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
msg: "SDU"
}
},
methods: {
valueChange(event){
this.msg = event.target.value;
}
}
}).mount("#app");
</script>
</body>
v-model
单选
<body>
<div id="app">
<label for=""><input type="radio" value="男" name="gender" v-model="gender">男</label>
<label for=""><input type="radio" value="女" name="gender" v-model="gender">女</label>
<p>-----------------------</p>
<h2>选择的性别是:{{ gender }}</h2>
</div>
<script src="js/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
msg: "SDU",
gender: "男"
}
}
}).mount("#app");
</script>
</body>
v-model
进行值绑定
<body>
<div id="app">
<!-- 1. 只选一个 -->
<label for="">
<input type="checkbox" name="" v-model="isAgree">同意霸王条款
</label>
<h2>是否同意协议: {{ isAgree ? "同意":"未同意" }}</h2>
<button :disabled="!isAgree">下一步</button>
<p>---------------------------</p>
<!-- 2. 多个选择 -->
<label for="">爱好</label>
<input type="checkbox" name="hobbies" v-model="hobbies" value="JAVA">JAVA
<input type="checkbox" name="hobbies" v-model="hobbies" value="C">C
<input type="checkbox" name="hobbies" v-model="hobbies" value="Python">Python
<input type="checkbox" name="hobbies" v-model="hobbies" value="JavaScript">JavaScript
<h2>用户画像:{{hobbies}}</h2>
<!-- 3. 值绑定 -->
<label for="" v-for="star in stars">
<input type="checkbox" name="" :value="star" v-model="likes">{{star}}
</label>
<h2>用户画像:{{likes}}</h2>
</div>
<script src="js/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
isAgree: false,
hobbies: [],
stars: ["牛德华","周木伦","成尤"],
likes: []
}
}
}).mount("#app");
</script>
</body>
结合多选,单选下拉菜单
<body>
<div id="app">
<p>选择校区:</p>
<select name="campus" v-model="campus">
<option value="中心">中心</option>
<option value="洪楼">洪楼</option>
<option value="软件">软件</option>
</select>
<p>选择的校区: {{ campus }}</p>
<p>----------------------------------</p>
<p>选择校区:</p>
<select name="campus" v-model="campuses" multiple>
<option value="中心">中心</option>
<option value="洪楼">洪楼</option>
<option value="软件">软件</option>
</select>
<p>选择的校区: {{ campuses }}</p>
</div>
<script src="js/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
campus: "中心",
campuses: []
}
}
}).mount("#app");
</script>
</body>
修饰符
<body>
<div id="app">
<!-- 1. 懒加载 -->
<input type="text" v-model.lazy="msg">
<h1>{{ msg }}</h1>
<!-- 2. number -->
<!-- <input type="number" v-model="age"> -->
<input type="number" v-model.number="age">
<h1>{{ age }}--- {{ typeof age }} </h1>
<!-- 3.trim -->
<input type="text" v-model.trim="name">
<h1>xxx{{ name }}yyy</h1>
</div>
<script src="js/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
msg: "sdu",
age: 0,
name: ""
}
}
}).mount("#app");
</script>
</body>