Vue (Vue2)基础知识梳理
插值操作
1. mustache 语法
首先定义一些变量:
const app = new Vue({
el:"app",
data:{
meseage:"hello world",
firstName:"zhang",
lastName:"heng"
count:100
}
})
然后我们可以在html标签中使用{{}}形式来将变量展现出来
<div id="app">
<h2>{{message}}</h2>
<h2>{{message}},one</h2>
<h2>{{firstName}} {{lastName}}</h2>
</div>
也可以在语法中写一些简单的表达式
<h2>{{firstName + " "+lastName}}</h2>
<h2>{{content*2}}</h2>
2.常见标签指令的使用
v-once
该指令后面没有表达式,用于执行一次性的插值.当数据再次改变时,插值处的数据是不会更新的
<h2 v-once>{{message}}</h2>
v-html
双大括号会将数据解释为普通的文本节点, 如果你要是渲染html代码,就使用该指令
<div id="app">
<h2>{{url}}</h2>
<h2 v-html="url"></h2>
</div>
``` ```js const app = new Vue({ el: '#app', data: {
---------------------------------------------------
message: 'helloWorld!',
url:'<a href="https://www.baidu.com">百度一下</a>'
}
});
渲染出来的效果如下:
DOM节点如下:
注意:这个指令使用需要注意安全问题,不要随意渲染任何html代码,有可能会导致XSS攻击,所以要对可信的html代码进行渲染
v-text
该标签和插值语法一样,但是不够灵活
v-pre
用于跳过这个元素和它子元素的编译过程
<h2 v-pre>{{message}}</h2>
结果:
v-bind
Mustache 语法不能作用在 HTML attribute 上,v-bind可以动态改变属性值
1. 基本使用
动态修改img标签的src属性值
<img v-bind:src="imgURL" alt="">
v-bind的简写形式
<img :src="imgURL" alt="">
此时imgURL就是一个变量
2. 动态绑定class属性
对象形式
<h2 class="类名" v-bind:class="{类名1:Boolean,类名2:Boolean}">{{message}}</h2>
可以通过这样的方式来绑定class属性,若对应类名的布尔值为true,则就添加这个类名,反之则去除,同时你用了这种动态绑定的方式绑定的类名,也可以用最原始的方式来再添加类名,然后这些类名会合并赋予给标签
数组形式
<h2 :class="['类名1','类名2']">{{message}}</h2>
数组项也可以是变量
<h2 :class="[active,line]">{{message}}</h2>
const app = new Vue({
el: '#app',
data: {
message: 'helloWorld!',
active:'aaa',
line:'bbb'
}
});
3. 动态绑定style属性
对象语法
<!-- 写法: <h2 :style="{属性名:属性值}">{{message}}</h2> -->
<h2 :style="{fontSize:'50px'}">{{message}}</h2>
<h2 :style="{fontSize:finalSize}">{{message}}</h2>
<h2 :style="{fontSize:finalSize1+'px',color:finalColor}">{{message}}</h2><h2 :style="getStyle()">{{message}}</h2>
数组语法
单独把一个对象当成数组每一个数组对象
<h2 :style="[baseStyle,baseStyle1]">{{message}}</h2>
const app = new Vue({
el: '#app',
data: {
message: 'helloWorld!',
baseStyle: {
backgroundColor: 'red'
},
baseStyle1: {
fontSize: '100px'
}
}
});
计算属性
模板中虽然可以写表达式,但是在模板中写大量的表达式会显得很混乱,对于一些复杂的逻辑都该使用计算属性
1. 基本使用
<div id="app"> <h2>{{fullName}}</h2> </div>
const app = new Vue({
el: '#app',
data: {
message: 'helloWorld!',
firstName:'阿尔托莉雅',
lastName:'潘德拉贡'
},
//计算属性
computed: {
fullName:function(){
return this.firstName+" "+this.lastName;
}
}
});
2. 复杂操作
<div id="app"><h2>总价格:{{totalPrice}}</h2></div>
const app = new Vue({
el: '#app',
data: {
message: 'helloWorld!',
books:[
{id:110,name:'xxx从入门到放弃',price:19},
{id:111,name:'yyy从入门到放弃',price:29},
{id:112,name:'zzz从入门到放弃',price:39},
{id:113,name:'java从入门到放弃',price:49}
]
},
computed: {
totalPrice:function(){
let result=0;
for(let i=0;i<this.books.length;i++) {
result+=this.books[i].price;
}
return result;
}
},
});
这里我们使用方法也可以实现,但是计算属性有缓存机制,是基于响应式的缓存机制,如果相关响应式依赖没有发生改变,计算属性是会利用缓存,多次访问只会返回之前的结果,而方法则是访问一次就会执行一次,比较消耗性能
3. 计算属性的getter和setter
之前我们的写法是一种简写方式
computed: {
fullName:function(){
return this.firstName+" "+this.lastName;
}
}
完整写法如下:
fullName:{
//set方法就是监视fullName的值,当fullName发生改变时会自动回调set方法,当然你也可以在set方法里设置修改
fullName的值
//可以给set方法设置一个参数来引入修改值,从而修改fullName
//一般set是不设置的所以就不写
set:function(newValue){
//return '123';
const names=newValue.split(' ');
this.firstName=names[0];
this.lastName=names[1];
},
//使用fullName这个属性本质上就是调用get这个方法,所以就可以使用上便的简写方式,而且也可以解释为啥fullName不用加(),而使用方法是需要加()的
get:function(){
//return 'abc';
return this.firstName+" "+this.lastName;
}
}
}
侦听器
计算属性可以做到的watch都能做到反之不行,当然能用计算属性就用计算属性,watch中可以执行任何逻辑,如函数节流,Ajax异步获取数据,操作DOM等
基本使用
watch: {
// 如果 `question` 发生改变,这个函数就会运行,函数有两个参数,变量改变后的值,变量改变前的值
question: function (newQuestion, oldQuestion) {
this.answer = 'Waiting for you to stop typing...'
this.debouncedGetAnswer()
},
//如果你要单独监听对象中的某个属性也可这样写
"info.name":function(new, old) {//...}
深度侦听
此时可以将要侦听的对象设置成对象形式,就可以设置一些其他的选项了
watch:{
info:{
handler(new, old) {
//...
},
deep:true,//是否开启深度监听
immediate:true//是否立即调用一次
}
}
$watch
//通过以下形式使用
const unwatch = this.$watch("info",
function(new, old) {},
{//option
deep:true,
}
)
//$watch函数返回一个函数,调用该函数可以取消监听
unwatch();
如果我们要侦听对象数组中某一个对象的属性变化,可以使用一个组件,将数组的每一项传入渲染出来的组件中,在组件中去侦听对象的属性改变,而不是在父组件中通过deep侦听。
动态渲染
1. 条件渲染
v-if
用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 truthy 值的时候被渲染。
<h1 v-if="true">Vue is awesome!</h1>
当然也有 v-else
, 元素必须紧跟在带 v-if
或者 v-else-if
的元素的后面,否则它将不会被识别。
<div v-if="Math.random() > 0.5"> Now you see me</div><div v-else> Now you don't</div>
v-show
与v-if
用法一样,区别就是 v-show
仅仅只是给dom节点修改display属性,而 v-if
是删除和重新渲染dom节点,v-if
的开销较大,所以一般频繁显示的dom推荐使用 v-show
2. 列表渲染
v-for
用于遍历数组,生成列表
<ul id="example-1"> <li v-for="item in items" :key="item.message"> {{ item.message }} </li></ul>
const example1 = new Vue({
el: '#example-1',
data: {
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
}
})
在 v-for
作用域中我们可以访问父级所有的属性值, v-for
可以指定第二个参数就是当前项的索引
v-for
中的key作用 key作用
事件绑定
1. 绑定到原生DOM元素上
通过v-on
标签监听 DOM 事件,并且绑定一个方法,在事件触发时执行
<button v-on:click="handleButton1">原生DOM中</button>
<!--语法糖-->
<button @click="handleButton1">原生DOM中</button>
export default {
name:'',
data () {
return {}
},
methods: {
handleButton1(){
console.log("原生dom事件");
}
},
}
vue会把默认事件对象作为参数传入方法,如果需要手动手动获取浏览器的event对象,可以使用 $event
2. 绑定到组件上
我们引入一个组件,并为其绑定一个事件
<btn @click="handleButton2" />
btn.vue组件
<template>
<div>
<button>组件中的dom</button>
</div>
</template>
<script type="text/ecmascript-6">
export default {
name:'',
data () {
return {}
},
methods: {},
}
</script>
直接在组件上绑定点击是不会触发的,有两种方式可以
- 使用修饰符
.native
在一个组件的根元素上直接监听一个原生事件。
<btn @click.native="handleButton2" />
- 在组件内部绑定一个事件,然后通过
$emit
方式 定义一个自定义事件,让父组件来执行相应逻辑
<template>
<div>
<button @click="handleChildBtn">组件中的dom</button>
</div>
</template>
<script type="text/ecmascript-6">
export default {
name:'',
data () {
return {}
},
methods: {
handleChildBtn(e){
console.log("点击");
this.$emit("click", e);
}
},
}
</script>
父组件:
<btn @click="handleButton2" />
一般组件的点击事件可以通过第二种实现,保证父组件来处理逻辑
过滤器
用于一些常用的文本格式化
<!-- 在双花括号中 -->{{ message | capitalize }}<!-- 在 `v-bind` 中 --><div v-bind:id="rawId | formatId"></div>
在组建的选项中定义局部过滤器
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
定义全局的过滤器
import Vue from 'vue'
Vue.filter('capitalize', function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
数据双向绑定
1. 基本使用
使用v-model
指令进行数据绑定
<input type="text" v-model="message">{{message}}
const app = new Vue({
el: '#app',
data: {
message: 'helloWorld!'
}
});
输入框修改, 会同步修改message的值 同理修改message也会修改输入框的值
2. 和其他表单控件使用
textarea
<textarea v-model="message" placeholder="add multiple lines"></textarea>
radio
radio 原来是需要name属性来保证选项互斥,使用v-model后就不需要了
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<br>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<br>
<span>Picked: {{ picked }}</span>
checkbox
单个复选框绑定布尔值
<label for="license">
<input type="checkbox" id="license" v-model="isAgree" />同意协议
</label>
<h2>你选择的是:{{ isAgree }}</h2>
多个复选框需要绑定数组
<!--多选框-->
<input type="checkbox" value="篮球" v-model="hobbies" />篮球
<input type="checkbox" value="足球" v-model="hobbies" />足球
<input type="checkbox" value="排球" v-model="hobbies" />排球
<h2>你的爱好:{{ hobbies }}</h2>
data() {
return {
isAgree:true,
hobbies:[]
};
},
select
默认单选
<select name="abc" id="" v-model="fruit" >
<option value="苹果" >苹果</option>
<option value="香蕉" >香蕉</option>
<option value="榴莲" >榴莲</option>
<option value="梨子" >梨子</option>
</select>
<h2>你选了个{{fruit}}</h2>
select
标签中添加 multiple
属性即可单选,需要ctrl
键配合,fruit将展示为数组
3. v-model基本原理
以input文本框为例, v-model
可以看作以下代码的简写
<input type="text" v-bind:value="message" v-on:input="message=$event.target.value">
总之v-model
就是语法糖
4. v-model 在组件中使用
<childCpn v-model="propsData" />
默认相当于:
<childCpn :value="propsData" @input="propsData=$event" />
子组件:
<input type="text" :value="value" @input="$emit('input', $event.target.value)">
props:['value']
注意事项:组件props
要改成 value
,$emit
事件要声明成 input
如果我们组件有多个数据需要双向绑定的话可以使用.sync修饰符, 因为v-model
只能在一个组件标签中出现一个
<childCpn :otherData.sync="otherData" />{{otherData}}
该修饰符也是一种语法糖,实际写法如下:
@update:属性名="userInfo.name = $event" :属性名="userInfo.name"
子组件:
<div @click="$emit('update:otherData', otherData+1)">得到{{otherData}}</div>
这里注意子组件$emit
发出的事件要根据该形式来编写,另外也不要直接修改props中的otherData,把改动传到父组件来修改是最好的,否则会出现警告
插槽
1. 基本使用
父组件引入子组件:
<childCpn> 我是插槽中的内容 </childCpn>
子组件:
<template>
<div>
<div>你好</div>
<slot></slot>
</div>
</template>
父组件引用的子组件中的文字内容就会替换子组件中的slot
标签,插槽内可以包含任何模板代码,包括 HTML,
注意:父组件里面向子组件传递插槽内容的作用域也是父组件,不能访问子组件中的内容,总之父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的,还可以给子组件中的slot
指定默认内容,如果父组件没有传递插槽内容给子组件们,会默认显示
2. 具名插槽
有时我们需要多个插槽
<div class="container">
<header>
<!-- 我们希望把页头放这里 -->
</header>
<main>
<!-- 我们希望把主要内容放这里 -->
</main>
<footer>
<!-- 我们希望把页脚放这里 -->
</footer>
</div>
slot
有个属性name
, 不带name
属性的插槽也会有个隐含的name
值为default
<template>
<div class="">
<div>你好</div>
<slot></slot>
<slot name="first"></slot>
<slot name="second"></slot>
</div>
</template>
父组件使用方式:
<childCpn>
我是插槽中的内容
<template v-slot:first>
<div>我是第一</div>
</template>
<template v-slot:second>
<div>饿哦是第二</div>
</template>
</childCpn>
现在 <template>
元素中的所有内容都将会被传入相应的插槽。任何没有被包裹在带有 v-slot
的 <template>
中的内容都会被视为默认插槽的内容。
3. 作用域插槽
我们需要父组件能够访问到子组件中的内容
子组件:
<slot name="first" :value="value"></slot>
类似props
的方式将value
所谓slot
的一个属性
父组件:
<template v-slot:first="slotProps">
<div>我是第一{{slotProps.value}}</div>
</template>
父组件将传递过来的props
对象保存在我们自己命名的slotProps
中,我们可取任意,当然也可以使用es6的解构语法省去取名了
<template v-slot:first="{value}">
<div>我是第一{{value}}</div>
</template>