Vue基础

572 阅读6分钟

引包

CDN:<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

下载:https://cn.vuejs.org/js/vue.js

初始化

Vue通过new来初始化实例

let vm = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
})

声明式渲染

Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统:数据驱动视图

<body>
    <div id="app"> <!-- 3.html写出el的绑定元素,默认app -->
         {{msg}} <!-- 6.实例中的对象通过插值表达式{{ }}渲染到html中 -->
    </div>
        
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        
    <script>
        let vm = new Vue({
            el: '#app',
            data: { //data存放实例中的数据
                msg: 'Hello Vue!',
                msg2: 'Content'
            },
            methods: { //methods存放声明的方法
                getContent() {
                    return this.msg + ' ' + this.data;//通过this来引用实例抛出的属性
                }
            }
        })

    </script>
</body>
{{ }} 插值表达式内的数据类型
<h2>{{msg}}</h2>
<!--插入vue属性  Hello Vue! -->
<h3>{{ 2 }}</h3>
<!--插入数字  2 -->
<h3>{{ 'hello' }}</h3>
<!--插入字符串  Hello -->
<h3>{{ {id:1} }}</h3>
<!--插入对象  { "id": 1 } -->
<h3>{{ 1>2 ? '真的':'假的' }}</h3>
<!--插入三元运算  假的 -->
<h3>{{ msg.split('').reverse().join('') }}</h3>
<!--插入js方法 !euV olleH -->
<h3>{{ getContent() }}</h3>
<!--插入函数 Hello Vue! Content -->

v-text & v-html 指令

向html内添加文本还有两个方法 v-textv-html ,在标签内写入 v-text='属性' 就可以向标签中间插入属性的值,而 v-html 的作用则是可以同时插入html标签渲染到页面中,类似于js的 innerTextinnerHTML

{{}} 和 v-text的作用是一样的,都是插值直接渲染

v-html 则是既能插入值又能插入标签

<h2 v-text='msg'></h2> <!-- Hello Vue! -->
//-------------------------------------------
htmlMsg:'<h3>Max</h3>'
<h2 v-html='htmlMsg'></h2> <!-- Max -->

v-show & v-if 指令

vue中的条件渲染,分为 v-showv-if v-else-if v-else 两个

v-show 通过属性的布尔值来进行判断是否显示,为false则渲染到页面后通过行内 display: none 隐藏

isShow:true,
<div v-show = 'isShow'>显示</div> //---显示 <div>显示</div>
//-------------------------------
isShow:false,
<div v-show = 'isShow'>显示</div> //---不显示 <div style="display: none;">显示</div>

v-if 则是判断真实的是否渲染到html,是“真正”的条件渲染,如果是false,则不会在html内渲染

isRender:true,
<div v-if = 'isRender'>
    显示
</div>//渲染 <div>显示</div>
//-------------------------------
isRender:false,
<div v-if = 'isRender'>
    显示
</div>//不渲染

v-else-if则是提供了不符合 v-if 条件之后的判断的方法,在标签内进行判断返回布尔值,v-else-if可以进行多层判断,如果以上条件均不满足,则显示 v-else ,需要注意的是 v-else 不写条件判断

<div v-if='num > 5'>num大于5</div>
<div v-else-if='5 > num'>num小于5</div>
<div v-else>num不是数字</div>
//-------------------------------------------
num:6 →		  <div>num大于5</div>
num:2 →		  <div>num小于5</div>
num:'字符串'	<div>num不是数字</div>

选择使用 v-show 还是 v-if 要看具体适用情况

v-show:

  • 初始化就被渲染到了html内,有更高的初始渲染开销
  • 通过css来控制是否显示块,切换开销较小,状态经常改变,推荐使用 v-show

v-if:

  • 初始化时条件判断为false则不会渲染到html内,直到变为true才会开始渲染块
  • 通过条件判断渲染和销毁块,有更高的切换开销,状态很少改变,则推荐使用 v-if

v-bind 指令

vue中通过 v-bind 通过返回的数据给标签添加属性

src:'./img/logo.png'
<div class="box" v-bind:src='src'></div>

如果需要给标签动态的添加或删除属性,需要在属性内写一个对象 { 需要添加的属性:vue声明的属性} 通过修改vue声明属性的布尔值来添加或删除该属性

<div class="box" v-bind:class='{ active:isActive }'></div>
//-----------------------------------------------------------
isActive:true
<div class="box active"></div>
isActive:false
<div class="box"></div>

{ 需要添加的属性:vue声明的属性} 还有一个使用方法是,需要给标签绑的一个属性绑定多个实例中的属性

<h4 v-bind:style='{color:color,fontSize:fontsize+"px"}'>小标题</h4>

v-bind 指令可以简写为 : 单独写在标签属性前面,例如 :class :src :title

v-on 事件处理指令

vue提供了 v-on 来绑定事件触发操作,'' 内可直接写一些简单的表达式

num: 0
<h3>{{ num }}</h3>
<button v-on:click='num+=1'>+1</button>

当整个事件的逻辑变得复杂了,需要以事件名的方式引入

<button v-on:click='plusOne'>+1</button>

methords:{
	plusOne(){
		this.num+=1;
	}
}

结合 v-bind 指令,可以完成点击后更改属性的布尔值,来动态的更改属性的添加和删除

<div class="box" v-bind:class='{active:isActive}'></div>
<button v-on:click='changeColor'>改变颜色</button>

methods: {
	changeColor() {
		this.isActive = true;
		this.isActive = !this.isActive;//可以通过!取反来实现点击反复切换
	}
}

v-on 指令可以简写为 @ 单独写在标签属性前面,例如 @click @mouseover @focus

事件修饰符

修饰符是由点开头的指令后缀来表示的。

  • .stop 是阻止冒泡行为,不让当前元素的事件继续往外触发,如阻止点击div内部事件,触发div事件
  • .prevent 是阻止事件本身行为,如阻止超链接的点击跳转,form表单的点击提交
  • .self 是只有是自己触发的自己才会执行,如果接受到内部的冒泡事件传递信号触发,会忽略掉这个信号
  • .capture 是改变js默认的事件机制,默认是冒泡,capture功能是将冒泡改为倾听模式
  • .once 是将事件设置为只执行一次,如 .click.once 代表只允许事件执行一次
  • .passive 滚动事件的默认行为 (即滚动行为) 将会立即触发,而不会等待 onScroll 完成,这个修饰符尤其能够提升移动端的性能
<button v-on:click.once='plusOne'>+1</button> <!-- 点击事件将只会触发一次 -->
<form v-on:submit.prevent='onSubmit'></form> <!-- 提交表单不再重载页面 -->
<a v-on:click.stop.prevent="doThat"></a> <!-- 修饰符可以串联 -->

使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。

按键修饰符

在监听键盘事件时,我们经常需要检查详细的按键。Vue 允许为 v-on 在监听键盘事件时添加按键修饰符:

<!-- 只有在 key 是 Enter 时调用 vm.submit() -->
<input v-on:keyup.enter="submit">
<!-- 处理函数只会在 $event.key 等于 PageDown 时被调用-->
<input v-on:keyup.page-down="onPageDown">

为了在必要的情况下支持旧浏览器,Vue 提供了绝大多数常用的按键码的别名:

  • .enter
  • .tab
  • .delete (捕获“删除”和“退格”键)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right

你还可以通过全局 config.keyCodes 对象自定义按键修饰符别名:

// 可以使用 `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112

为什么在 HTML 中监听事件?

v-on 这种事件监听的方式违背了关注点分离 (separation of concern) 这个长期以来的优良传统。但不必担心,因为所有的 Vue.js 事件处理方法和表达式都严格绑定在当前视图的 ViewModel 上,它不会导致任何维护上的困难。

使用 v-on 有几个好处:

  • 扫一眼 HTML 模板便能轻松定位在 JavaScript 代码里对应的方法。

  • 无须在 JavaScript 里手动绑定事件,ViewModel 代码可以是非常纯粹的逻辑,和DOM完全解耦,易于测试

  • 当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除,你无须担心如何清理它们

v-for 列表渲染指令

v-for 指令基于一个数组来渲染一个列表,v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名,可以随意命名

<div id="app">
    <ul>
        <li v-for='item in menus'>
        	<h3>序号:{{item.id}} - 名字:{{item.name}}</h3>
        </li>
    </ul>

</div>
<script src="./vue.js"></script>
<script>
    let vm = new Vue({
        el: '#app',
        data: {
            menus:[
                {id:1,name:'Zhang'},
                {id:1,name:'Lee'},
                {id:1,name:'Wang'},
                {id:1,name:'Zhao'}
            ]
        }
    })
</script>
  • 学号:1 - 名字:Zhang
  • 学号:2 - 名字:Lee
  • 学号:3 - 名字:Wang
  • 学号:4 - 名字:Zhao

v-for 中,还可以访问组件内的所有属性,v-for 还支持一个可选的第二个参数,即当前项的索引

<div id="app">
    <ul>
        <li v-for='(info,index) in menus'> <!-- 这里我index+1 -->
            <h3>{{info.classNum+'班'}} - {{index+1}} - {{'学号:'+info.id}} - {{'名字:'+info.name}}</h3>
        </li>
    </ul>

</div>
<script src="./vue.js"></script>
<script>
    let vm = new Vue({
        el: '#app',
        data: {
            classNum: 34,
            menus: [
                { id: 1, name: 'Zhang' },
                { id: 2, name: 'Lee' },
                { id: 3, name: 'Wang' },
                { id: 4, name: 'Zhao' }
            ]
        }
    })
</script>
  • 34班 - 1 - 学号:1 名字:Zhang
  • 34班 - 2 - 学号:2 名字:Lee
  • 34班 - 3 - 学号:3 名字:Wang
  • 34班 - 4 - 学号:4 名字:Zhao

也可以用 of 替代 in 作为分隔符,因为它更接近 JavaScript 迭代器的语法:

<div v-for="item of items"></div>

v-for 中使用对象

也可以用 v-for 来遍历一个对象的属性,并提供了两个参数,属性名 和 索引

<div id="app">
    <ul>
        <li v-for='(value,key,index) of person'>
            <h3>{{index}} - {{key}}: {{value}}</h3>
        </li>
    </ul>
</div>
<script src="./vue.js"></script>
<script>
    let vm = new Vue({
        el: '#app',
        data: {
            classNum: 34,
            person: {
                name: 'Max',
                age: 23,
                height: '182cm'
            }
        }
    })
</script>
  • 0 - name: Max
  • 1 - age: 23
  • 2 - height: 182cm

:key='index'

vue建议在使用 v-for 的时候,通过 :key='index':key='key' 给每个元素绑定一个属性,这样是为了给每一个元素添加独有的身份,当修改其中某个数据的时候,只更新对应的元素,而不是更新整个 v-for

<li v-for='(value,key,index) of person' :key='index'>
    <h3>{{index}} - {{key}}: {{value}}</h3>
</li>

不要使用对象或数组之类的非基本类型值作为 v-forkey,请用字符串或数值类型的值

v-model 双向数据绑定

v-model 指令在表单元素 <input> <textarea> <select> 上创建双向数据绑定,它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。

v-model 会忽略所有表单元素的 valuecheckedselected 属性的初始值而总是将 Vue 实例的数据作为数据来源,应该通过 JavaScript 在组件的 data 选项中声明初始值

v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:

  • text 和 textarea 元素抛出 value 属性,和 input 事件;
  • checkbox 和 radio 抛出 checked 属性,和 change 事件;
  • select 字段将 value 作为属性抛出,并将 change 作为事件。

通过 {{ }}v-modul='' 为两个元素绑定同一个属性,当更改input中的内容时,v-modul 会实时更改所绑定的属性的值,并实时更新到绑定该属性的元素上

# 文本

<p>Message is: {{ message }}</p>
<input v-model="message" placeholder="输入信息">
message:''

# 多行文本

<p>Multiline message is:<br>{{ message }}</p>
<textarea v-model="message" placeholder="输入说明"></textarea>
message:'有什么想说的'

<textarea> 插值并不会生效,如果有需要可以给message赋值

# 复选框

单个复选框,绑定到布尔值:

<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{checked}}</label>
checked: false
//以上代码checked文字会显示为true或false
//-----------------------------------
//如果需要根据状态显示不同的文本,需要在标签内加上判断属性
<input type="checkbox" v-model="checked" true-value="选中了" false-value="没有选中">
<p>{{toggle}}</p>
checked:'没有选中',

这里的 true-valuefalse-value 属性并不会影响输入控件的 value 属性,因为浏览器在提交表单时并不会包含未被选中的复选框。如果要确保表单中这两个值中的一个能够被提交,(即“yes”或“no”),请换用单选按钮。

多个复选框,绑定到同一个数组:勾选对应的选项 v-model 会把对应元素的value添加到数组中

<div>
    <span>Checked names: {{checkedNames}}</span>
    <br>
    <input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
    <label for="jack">Jack</label>
    <input type="checkbox" id="john" value="John" v-model="checkedNames">
    <label for="john">John</label>
    <input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
    <label for="mike">Mike</label>
</div>
checkedNames: []

# 单选按钮

勾选对应的选项 v-model 会把对应元素的value赋值给 v-model 绑定的属性

<div>
    <span>Picked: {{picked}}</span>
    <br>
    <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>
</div>
picked: ''

# 选择框

下拉选择对应的选项 v-model 会把对应元素的value赋值给 v-model 绑定的属性,如果没有value,则会选择option中间的值

单选时:

<div>
    <select v-model="selected">
        <option disabled value="">请选择</option>
        <option>A</option>
        <option>B</option>
        <option>C</option>
    </select>
    <span>Selected: {{ selected }}</span>
</div>
selected: ''

如果 v-model 表达式的初始值未能匹配任何选项,<select> 元素将被渲染为“未选中”状态。在 iOS 中,这会使用户无法选择第一个选项。因为这样的情况下,iOS 不会触发 change 事件。因此,更推荐像上面这样提供一个值为空的禁用选项。

多选时 (绑定到一个数组):

<div>
  <select v-model="selected" multiple style="width: 100px;">
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </select>
  <br>
  <span>Selected: {{ selected }}</span>
</div>
selected: []

# 用 v-for 渲染动态选项

<select v-model="selected">
    <option v-for="option in options" v-bind:value="option.value">
        {{ option.text }}
    </option>
</select>
<span>Selected: {{ selected }}</span>

selected: 'A',
options: [
    { text: 'One', value: 'A' },
    { text: 'Two', value: 'B' },
    { text: 'Three', value: 'C' }
]

# 修饰符

.lazy

在默认情况下,v-model 在每次 input 输入每个新的字符后都会更新绑定属性的值,可以添加 .lazy 修饰符,让 input 失去焦点后再更新:

<input v-model.lazy="msg">

.number

如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符:

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

因为即使在 type="number" 时,HTML 输入元素的值也总会返回字符串。如果这个值无法被 parseFloat() 解析,则会返回原始的值。

.trim

如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符:

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

侦听属性 watch

vue通过 watch 提供了一个方法,来响应数据的变化,当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的

把需要监听的属性放到 watch 内,函数接收两个参数,新值和旧值,可以实时侦听数值每次的变化,并调用函数通过条件判断触发相应的事件

watch: {
    num: function (newV, oldV) {
        console.log(newV,oldV);
    }
},

例:

<p>{{num}}</p>
<input v-model="num" type="number">
data: {
	num: ''
},
watch: {
	num: function (newV, oldV) {
		if (newV > 100) {
			console.log('超出了');
		}
	}
},

深度侦听

上面的方法只能侦听基本数据类型,做不到复杂数据类型 对象数组 这种内部值的侦听,对此vue提供了深度监听方法,将需要监听的对象放入watch中,{ }内写入 deep: truehandler 函数,函数名必须为 handler 不可更改

<h3>{{person[0].name}}</h3>
<button @click='person[0].name = "Jerry"'>Jerry</button>
watch: {
	person: {
		deep: true,
		handler: function (newV, oldV) {
			console.log(newV[0].name);
		}
	}
},

计算属性 computed

{{ }} 内的表达式非常便利,但是设计它的初衷是用于简单运算的,在模板中放入太多的逻辑会让模板过重且难以维护,例如:

<p>{{ say.split('').reverse().join('') }}</p>

这里不再是简单的声明式逻辑,需要时间理解,当需要在模板中多次引用时,就会让模板变得非常复杂

所以对于任何复杂逻辑,都应当使用计算属性

computed默认只有getter方法

<h3>{{reverseSay}}</h3>
say: 'hello world!'

computed: {
	reverseSay: function () {
		return this.say.split('').reverse().join('')
	}
},

计算属性最大优点:获得属性的值后会立即计算好结果并存入缓存中,需要可立即调用,多次调用不需要函数重新计算,当数据发生变化会重新计算,重新缓存,性能开销小

<h3>{{fullName}}</h3>
<button @click='nameBtn'>更改</button>
data: {
	firstName: 'Max',
	lastName: 'uan'
},
methods: {
	nameBtn: function () {
		return this.lastName = 'Max';
	}
},
computed: {
	fullName: function () {
		return this.firstName + this.lastName;
	}
},

为什么需要缓存?假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算,然后我们可能有其他的计算属性依赖于 A,如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果不希望有缓存,可用方法来替代。

计算属性的 setter

计算属性默认获取属性(getter),只能通过引入方法来显示结果,不过在需要时也可以对属性进行修改(setter):

触发事件1对val1赋值,Value会跟着被计算属性修改,触发事件2对Value赋值会通过set方法接收新值对val2赋值

但是由于计算属性是在监测到 get 方法内属性变化才开始计算,所以在 set 内被赋值的属性没有出现在 get 中时,对计算属性赋值并不会改变和更新计算属性自身的值,只是通过 set 的 newV 参数传入了函数

<div id="app">
    <h3>val1:{{val1}}</h3>
    <h3>value:{{value}}</h3>
    <h3>val2:{{val2}}</h3>
    <button @click='handleClick1'>Val1 = 5</button>
    <button @click='handleClick2'>Value = 10</button>
</div>
<script src="./vue.js"></script>
<script>
    let vm = new Vue({
        el: '#app',
        data: {
            val1: 1,
            val2: 2
        },
        methods: {
            handleClick1: function () {
                this.val1 = 5;
            },
            handleClick2: function () {
                this.value = 10;
            }
        },
        computed: {
            value: {
                get: function () {
                    return this.val1;
                },
                set: function (newV) {
                    this.val2 = newV;
                }
            }
        }
    })
</script>

计算属性 vs 侦听属性

Vue 提供的侦听属性很方便,当有一些数据需要随着其它数据变动而变动时,很容易滥用 watch,但通常更好的做法是使用计算属性而不是命令式的 watch 回调,例子:

<div>{{ fullName }}</div>
data: {
	firstName: 'Foo',
	lastName: 'Bar',
	fullName: 'Foo Bar'
},
watch: {
	firstName: function (val) {
		this.fullName = val + ' ' + this.lastName
	},
	lastName: function (val) {
		this.fullName = this.firstName + ' ' + val
	}
}

上面代码是命令式且重复的,将它与计算属性的版本进行比较:

data: {
    firstName: 'Foo',
    lastName: 'Bar'
},
computed: {
    fullName: function () {
        return this.firstName + ' ' + this.lastName
    }
}

监听属性是更改属性值,所以需要声明fullName,以及分别监听 first 和 last 两个属性的改变

计算属性不再需要声明fullName,而是直接通过计算属性的方法名来引入

过滤器 filters

vue过滤器可用于对数据的修饰处理,过滤器可以用在两个地方:双花括号插值和 v-bind 表达式,过滤器应该被添加在 js 表达式的尾部,由“管道”符号指示:

属性名 | 过滤器名(可传参)
<!-- 在双花括号中 -->
{{ price | myPrice($) }} // $50

<!-- 在 v-bind 中 -->
<div v-bind:id="rawId | formatId"></div>
price: 50
filters:{
    myPrice:function(price,a){
        return a + price;
    }
}

创建全局过滤器

全局过滤器只需创建一次,就可以在所有组件中使用,过滤器名需要用 '' 包裹,否则会报错

Vue.filter('myFilter', (val) => {
    return val.split('').reverse().join('');
})