文章目录
- 视频讲解地址:[点击打开视频讲解地址](https://www.bilibili.com/video/BV17d4y1S7Cj/?vd_source=66e2692cc471862d6c3f85dc4b9ea5dd)
- 前言
- 一、介绍
- 二、Vue实例
- 三、模板语法
- 四、计算属性和侦听器
- 五、Class 与 Style 绑定
- 六、条件渲染
- 七、列表渲染
- 八、事件处理
- 九、表单输入绑定
- 十、组件基础
- 十一、组件注册
- 十二、Prop
- 十三、自定义事件
- 十四、插槽
- 十五、动态组件 & 异步组件
- 十六、混入
- 十七、自定义指令
- 十八、渲染函数 & JSX
- 十九、插件
- 二十、过滤器
- 二十一、Vuex
- 二十二、Vue Router路由管理器
视频讲解地址:点击打开视频讲解地址
前言
Vue2入门到精通
文章目录与[Vue官网](https://cn.vuejs.org/)目录对应
一、介绍
1. Vue是一款前端渐进式框架,可以提高前端开发效率。
2. Vue是一套构建用户界面的框架,只关注视图层,它不仅易于上手,还便于与第三方库或既有项目整合。(Vue有配套的第三方类库,可以整合起来做大型项目的开发)
3. 作者:尤雨溪
特点
遵循MVVM模式。Vue通过MVVM模式,能够实现视图与模型的双向绑定。
MVVM是前端视图层的概念,主要关注于视图层分离。
MVVM把前端的视图层分为了三部分:Model,View,VM ViewModel。
简单来说,就是数据变化的时候, 页面会自动刷新, 页面变化的时候,数据也会自动变化
编码简洁,体积小,运行效率高,适合移动/PC端开发
它本身只关注UI,可以引入其它第三方库开发项目
Vue周边库
vue-cli:vue脚手架
vue-resource 是一个非常轻量的用于处理HTTP请求的插件,Vue2.0之后,就不再对vue-resource
更新,而是推荐使用axios。
axios 处理HTTP请求的插件
vue-router:路由
vuex:状态管理
element-ui:基于vue的UI组件库(PC端)
Vue三种安装方式
CDN
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<!-- 生产环境版本,优化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
<!-- 本地导入 -->
<script src="../node_modules/vue/dist/vue.js"></script>
NPM
npm install vue
命令行工具 (CLI)脚手架
命令行创建
npm install -g @vue/cli @vue/cli-service-global
创建项目
vue create vue-demo
cd vue-demo
npm run serve
二、Vue实例
Vue生命周期(钩子函数)
概述:其实就是在特定的时期执行对应事件
生命周期图示
什么是Vue生命周期?
Vue 实例从创建到销毁的过程,就是生命周期。也就是从开始创建、初始 化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。
Vue生命周期的作用是什么?
Vue生命周期中有多个事件钩子,让我们在控制整个Vue实例过程时更容易形成好的逻辑。
Vue生命周期总共有几个阶段?
总共分为8个阶段:创建前/后, 载入前/后,更新前/后,销毁前/后。
创建阶段:
beforecreate:实例已经初始化,但不能获取DOM节点。(没有data,没有el)
created:实例已经创建,仍然不能获取DOM节点。(有data,没有el)
载入阶段:
beforemount:模板编译完成,但还没挂载到界面上。(有data,有el)
mounted:编译好的模板已挂载到页面中(数据和DOM都已经渲染出来)。
更新阶段:
beforeupdate:数据发生变化立即调用,此时data中数据是最新的,但页面上数据仍然是旧的(检测到数据更新时,但DOM更新前执行)。
updated:更新结束后执行,此时data中的值和页面上的值都是最新的。
销毁阶段:
beforedestroy:当要销毁vue实例时,在销毁之前执行。
destroy:在销毁vue实例时执行
第一次页面加载会触发哪几个钩子?
第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子
DOM 渲染在 哪个周期中就已经完成?
DOM 渲染在 mounted 中就已经完成了。
简单描述每个周期具体适合哪些场景?
beforecreate : 可以在此阶段加loading事件,在加载实例时触发;
created : 初始化完成时的事件写在这里,如在这结束loading事件,异步请求也适宜在这里调用;
mounted : 挂载元素,获取到DOM节点;
updated : 如果对数据统一处理,在这里写上相应函数;
beforeDestroy : 可以做一个确认停止事件的确认框;
nextTick : 更新数据后立即操作dom;
Vue基本代码结构
const vm = new Vue({
el:'#app',//所有的挂载元素会被 Vue 生成的 DOM 替换
data:{ // this->window },
methods:{ // this->vm},
//注意,不应该使用箭头函数来定义 method 函数 ,this将不再指向vm实例
props:{} ,// 可以是数组或对象类型,用于接收来自父组件的数据
//对象允许配置高级选项,如类型检测、自定义验证和设置默认值
watch:{ // this->vm},
computed:{},
render(){},
// 声明周期钩子函数
})
el与data的两种写法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>el与data的两种写法</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>Hello,{
{name}}!</h1>
</div>
<script>
Vue.config.productionTip = false
//el的两种写法:
// const vm = new Vue({
// // el:'#root', //第一种写法
// data:{
// name:'JOJO'
// }
// })
// vm.$mount('#root')//第二种写法
//data的两种写法:
new Vue({
el:'#root',
//data的第一种写法:对象式
// data:{
// name:'JOJO'
// }
//data的第二种写法:函数式
data(){
return{
name:'JOJO'
}
}
})
</script>
</body>
</html>
总结:
el有2种写法:
创建Vue实例对象的时候配置el属性
先创建Vue实例,随后再通过vm.$mount('#root')指定el的值
data有2种写法:
对象式
函数式
如何选择:目前哪种写法都可以,以后学到组件时,data必须使用函数,否则会报错
概述
当一个Vue实例被创建时,它将data对象中的所有的property加入到Vue的响应式系统中。
当这些property的值发生改变时,视图将会产生 响应,即匹配更新为新的值。
例外
Vue实例外部新增的属性改变时不会更新视图。
Object.freeze(),会阻止修改现有的property,响应系统无法追踪其变化。
实例属性和方法
访问el属性:vm.$el
访问data属性:vm.$data
访问data中定义的变量:vm.a,vm.$data.a
访问methods中的方法:vm.方法名()
访问watch方法:vm.$watch()
注意:
以_或$开头的property不会被Vue实例代理,因为它们可能和Vue内置的property、API方法冲突。你可以使用例如vm.$data._property的方式访问这些property。
不要在选项property或回调上使用箭头函数,this将不会指向Vue实例 比如created: () => console.log(this.a)或vm.$watch('a', newValue => this.myMethod())。
因为箭头函数并没有this,this会作为变量一直向上级词法作用域查找,直至找到为止,经常导致Uncaught TypeError: Cannot read property of undefined或Uncaught TypeError: this.myMethod is not a function之类的错误。
vue项目中各文件说明
node_modules文件夹:项目依赖文件
public文件夹:放置静态资源(图片),webpack在打包时会原封不动的打包到dist文件夹中。
src文件夹(程序员源代码文件夹):
assets文件夹:放置静态资源(多个组件共用的静态资源),在webpack打包时会把静态资源当成
模块打包到JS文件中
components:放置非路由组件(全局组件)
APP.vue:唯一的根组件
main.js:程序入口文件,整个程序中最先执行的文件
api设计请求相关的
babel.config.js:配置文件(与babel相关),一般不去修改
package.json:相当于项目的身份证,配置信息,记录项目叫做什么、项目中有哪些
依赖、项目怎么运行,
package-lock.json:可以删除,是一个缓存文件
README.md:说明性文件
vue.config.js:项目的配置文件
三、模板语法
插值
语法:{
{ 变量名/对象.属性名 }}
功能:用于解析标签体内容,且可以直接读取到data中的所有区域,插值表达式允许用户输入"JS代码片段"
注意:VSCode编辑中,使用.HTML文件,在浏览器打开,需要在插件商店安装 “open in browser”
文本插值
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>ls-demo</title>
<!-- 引入本地vue依赖 -->
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1>Message: {
{ msg }}</h1>
</div>
<script type="text/javascript">
//创建vue对象
var app = new Vue({
//让vue接管div标签
el:"#app",
//定义数据,里边包含一个属性
data:{
msg:"我是文本插值吖!!!"
}
});
</script>
</body>
</html>
原始 HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>ls-demo</title>
<!-- 引入本地vue依赖 -->
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1>原始 HTML: {
{ rawHtml }}</h1>
<h1>v-html 指令<span v-html="rawHtml"></span></h1>
</div>
<script type="text/javascript">
//创建vue对象
var app = new Vue({
//让vue接管div标签
el:"#app",
//定义数据,里边包含一个属性
data:{
rawHtml :'Using mustaches: <span style="color: red">This should be red.</span>'
}
});
</script>
</body>
</html>
原始 HTML效果图
原始 HTML小结
原始HTML(插值):不解析html代码,直接显示;
v-html指令:可解析HTML代码后显示;
Attribute => v-bind
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>ls-demo</title>
<!-- 引入本地vue依赖 -->
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div v-bind:id="dynamicId">我是作用在HTML attribute id</div>
<button v-bind:disabled="isButtonDisabled">Button</button>
</div>
<script type="text/javascript">
//创建vue对象
var app = new Vue({
//让vue接管div标签
el:"#app",
//定义数据,里边包含一个属性
data:{
dynamicId :'id',
isButtonDisabled:true,
}
});
</script>
</body>
</html>
Attribute效果图
Attribute小结
使用了v-bind后指定某个属性名,那么在这个属性的属性值当中我们就可以使用data数据进行绑定了
v-bind绑定的是一个变量
v-bind的语法糖(简写格式)为 " : "
<button :disabled="isButtonDisabled">Button</button>
使用 JavaScript 表达式
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>ls-demo</title>
<!-- 引入本地vue依赖 -->
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!-- 数字相加 -->
<div>{
{ number + 1 }}</div>
<!-- 使用三元表达式判断 -->
<div>{
{ ok ? 'YES' : 'NO' }}</div>
<!-- 这是语句,不是表达式 -->
<!-- {
{ var a = 1 }} -->
<!-- 流控制也不会生效,请使用三元表达式 -->
<!-- {
{ if (ok) { return message } }} -->
<div>{
{ message.split('').reverse().join('') }}</div>
<!-- message.split('') => ['m', 'o', 'c', 'h', 'e', 'n', 'g', 'x', 'i', 'y', 'a', '!', '!', '!'] -->
<!-- message.split('').reverse() => ['!', '!', '!', 'a', 'y', 'i', 'x', 'g', 'n', 'e', 'h', 'c', 'o', 'm'] -->
<!-- message.split('').reverse().join('') => '!!!ayixgnehcom' -->
<div v-bind:id="'list-' + id">list-{
{id}}</div>
<!-- id为变量 => data中 bindId -->
<!-- 结果: id="list-bindId" -->
</div>
<script type="text/javascript">
//创建vue对象
var app = new Vue({
//让vue接管div标签
el:"#app",
//定义数据,里边包含一个属性
data:{
number:1,
ok:true,
message:'mochengxiya!!!',
id:'bindId'
}
});
</script>
</body>
</html>
JavaScript 表达式效果图
指令
指令 (Directives) 是带有 v- 前缀的特殊 attribute。指令 attribute 的值预期是单个 JavaScript 表达式 (v-for 是例外情况,稍后我们再讨论)。
指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。
参数
一些指令能够接收一个“参数”,在指令名称之后以冒号表示。例如,v-bind 指令可以用于响应式地更新 HTML attribute。
<a v-bind:href="url">...</a>
在这里 href 是参数,告知 v-bind 指令将该元素的 href attribute 与表达式 url 的值绑定。
另一个例子是 v-on 指令,它用于监听 DOM 事件
<a v-on:click="doSomething">...</a>
动态参数
语法:[ 动态参数 ]
<template>
<div>
<a v-bind:[attributeName]="url"> 动态参数attributeName </a>
<!-- attributeName 会被作为一个 JavaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用。
这个绑定将等价于 v-bind:href。 -->
<br>
<a v-on:[eventName]="doSomething"> 动态的事件名 </a>
<!-- 同样地,你可以使用动态参数为一个动态的事件名绑定处理函数 -->
<!-- 当 eventName 的值为 "click" 时,v-on:[eventName] 将等价于 v-on:click -->
</div>
</template>
<script>
export default {
data(){
return {
attributeName:'href',
url:'url',
eventName:'click'
}
},
methods:{
doSomething(){
//动态的事件
}
}
}
</script>
<style>
</style>
动态参数小结
1.对动态参数的值的约束:动态参数预期会求出一个字符串,异常情况下值为 null。
这个特殊的 null 值可以被显性地用于移除绑定。任何其它非字符串类型的值都将会触发一个警告。
2.对动态参数表达式的约束:某些字符,如空格和引号,放在 HTML attribute 名里是无效的。
变通的办法是使用没有空格或引号的表达式,或用计算属性替代这种复杂表达式。
修饰符
语法:半角句号 . 指明的特殊后缀
例:.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault()
<form v-on:submit.prevent="onSubmit">...</form>
其他修饰符总结:
.stop - 调用 event.stopPropagation()。
.prevent - 调用 event.preventDefault()。
.capture - 添加事件侦听器时使用 capture 模式。
.self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
.{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
.native - 监听组件根元素的原生事件。
.once - 只触发一次回调。
.left - (2.2.0) 只当点击鼠标左键时触发。
.right - (2.2.0) 只当点击鼠标右键时触发。
.middle - (2.2.0) 只当点击鼠标中键时触发。
.passive - (2.3.0) 以 { passive: true } 模式添加侦听器
缩写
v-bind 缩写
<!-- 完整语法 -->
<a v-bind:href="url">...</a>
<!-- 缩写 -->
<a :href="url">...</a>
<!-- 动态参数的缩写 (2.6.0+) -->
<a :[key]="url"> ... </a>
v-on 缩写
<!-- 完整语法 -->
<a v-on:click="doSomething">...</a>
<!-- 缩写 -->
<a @click="doSomething">...</a>
<!-- 动态参数的缩写 (2.6.0+) -->
<a @[event]="doSomething"> ... </a>
四、计算属性和侦听器
计算属性
概述:计算属性就是一个提前定义好的方法, 该方法可以看作是一个特殊的值, 可以在插值表达式中使用。
计算属性必须放在Vue的computed中。
基础例子
<template>
<div>
<!-- 模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护 -->
<!-- 所以,对于任何复杂逻辑,你都应当使用计算属性。 -->
<div>{
{ message.split('').reverse().join('') }}</div>
<p>计算属性 (Computed) 的 getter : "{
{ reversedMessage }}"</p>
姓:<input type="text" v-model="firstName"><br><br>
名:<input type="text" v-model="lastName"><br><br>
姓名:<span>{
{fullName}}</span>
</div>
</template>
<script>
export default {
data(){
return {
message:'末晨曦!',
firstName:'',
lastName:''
}
},
computed:{
// 计算属性的 getter => 函数
reversedMessage () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
},
// 计算属性的 getter、setter => 对象
fullName:{
//get 获取值
get(){
return this.firstName + ' - ' + this.lastName
},
//set 修改值
set(value){
const arr = value.split('-')
this.firstName = arr[0]
this.lastName = arr[1]
}
}
},
methods:{
}
}
</script>
<style>
</style>
计算属性效果图
计算属性小结
定义:要用的属性不存在,需要通过已有属性计算得来。
原理:底层借助了Objcet.defineproperty()方法提供的getter和setter。
get函数什么时候执行?
初次读取时会执行一次
当依赖的数据发生改变时会被再次调用
优势:与方法实现相比,计算属性内部有缓存机制(复用),效率更高,调试方便
注意:
计算属性最终会出现在vm上,直接读取使用即可
如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变
如果计算属性确定不考虑修改,可以使用计算属性的简写形式(直接写函数形式,默认是getter )
计算属性缓存 vs 方法
<template>
<div>
<!-- 模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护 -->
<!-- 所以,对于任何复杂逻辑,你都应当使用计算属性。 -->
<div>{
{ message.split('').reverse().join('') }}</div>
<p>计算属性 (Computed) 的 getter : "{
{ reversedMessage }}"</p>
姓:<input type="text" v-model="firstName"><br><br>
名:<input type="text" v-model="lastName"><br><br>
姓名:<span>{
{fullName}}</span>
<p>方法: "{
{ reversedMessageMethod() }}"</p>
</div>
</template>
<script>
export default {
data(){
return {
message:'末晨曦!',
firstName:'',
lastName:''
}
},
computed:{
// 计算属性的 getter => 函数
reversedMessage () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
},
// 计算属性的 getter、setter => 对象
fullName:{
//get 获取值
get(){
return this.firstName + ' - ' + this.lastName
},
//set 修改值
set(value){
const arr = value.split('-')
this.firstName = arr[0]
this.lastName = arr[1]
}
}
},
methods:{
reversedMessageMethod () {
return this.message.split('').reverse().join('')
}
}
}
</script>
<style>
</style>
方法效果图
计算属性缓存 vs 方法小结
两种方式的最终结果确实是完全相同的。
然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。
只在相关响应式依赖发生改变时它们才会重新求值。
这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。
相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。
计算属性 vs 侦听属性
<template>
<div>
<div id="demo">{
{ fullName }}</div>
<!-- 相比侦听属性,使用计算属性更加简洁 -->
</div>
</template>
<script>
export default {
data(){
return {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
}
},
//侦听属性
watch: {
firstName(val) {
this.fullName = val + ' ' + this.lastName
},
lastName(val) {
this.fullName = this.firstName + ' ' + val
}
},
//计算属性
computed:{
// 计算属性的 getter => 函数
fullName () {
return this.firstName + ' ' + this.lastName
}
},
methods:{
}
}
</script>
<style>
</style>
计算属性的 setter
概述:计算属性默认只有 getter,不过在需要时你也可以提供一个 setter
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
侦听器
概述:当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
侦听属性基本用法
<template>
<div>
<div id="demo">{
{ info }}</div>
<button @click="changeLearning">改变</button>
</div>
</template>
<script>
export default {
data(){
return {
isMessage: true,
}
},
computed:{
info(){
return this.isMessage ? '侦听属性' : 'watch'
}
},
//侦听属性
watch: {
//监听message属性值,newValue代表新值,oldValue代表旧值
//正常写法
isMessage:{
immediate:true, //初始化时让handler调用一下,默认只有属性发生改变时会调用
//handler什么时候调用?当isHot发生改变时
handler(newValue,oldValue){
console.log('isMessage被修改了',newValue,oldValue);
}
},
//简写
// isMessage (newValue,oldValue) {
// console.log('isMessage被修改了',newValue,oldValue);
// },
},
methods:{
changeLearning(){
this.isMessage = !this.isMessage
}
}
}
</script>
<style>
</style>
侦听属性基本用法效果图
侦听属性基本用法小结
当被监视的属性变化时,回调函数自动调用,进行相关操作
监视的属性必须存在,才能进行监视
immediate:true, //初始化时让handler调用一下
监视有两种写法:
创建Vue时传入watch配置
通过vm.$watch监视
vm.$watch('isMessage',{
immediate:true,
handler(newValue,oldValue){
console.log('isMessage被修改了',newValue,oldValue)
}
})
深度监视
<template>
<div>
<div id="demo">姓名:{
{ person.name }}</div>
<div id="demo">年龄:{
{ person.age }}</div>
</div>
</template>
<script>
export default {
data(){
return {
person:{
"name":"末晨曦吖", "age":18}
}
},
//侦听属性
watch: {
//监控person对象的值,对象的监控只能获取新值
person:{
//开启深度监控;监控对象中的属性值变化
deep: true,
//获取到对象的最新属性数据(obj代表新对象)
handler(obj){
console.log("name = " + obj.name + "; age=" + obj.age);
}
},
//监视多级结构中某个属性的变化
'person.name':{
handler(){
console.log('person中name被改变了')
}
}
},
}
</script>
<style>
</style>
深度监视小结
Vue中的watch默认不监测对象内部值的改变(一层)
在watch中配置 deep:true 可以监测对象内部值的改变(多层)
注意:
Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以
使用watch时根据监视数据的具体结构,决定是否采用深度监视
五、Class 与 Style 绑定
绑定 HTML Class
对象语法
<template>
<div>
<!-- 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定 -->
<div class="basicStyle" :class="classString" @click="changeMood">字符串写法</div>
<!-- 绑定class样式--对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 -->
<div class="basicStyle" :class="classObj">对象写法1</div>
<div class="basicStyle" :class="{class1:true}">对象写法2</div>
</div>
</template>
<script>
export default {
data(){
return {
classString:'string',
classObj:{
class1:false,
class2:true,
},
}
},
}
</script>
<style scoped>
.string{
color: red;
font-weight:bold;
}
.class1{
color: blue;
}
.class2{
font-weight:bold;
}
</style>
对象语法小结
写法:class="xxx",xxx可以是字符串、对象、数组
字符串写法适用于:类名不确定,要动态获取
对象写法适用于:要绑定多个样式,个数不确定,名字也不确定
数组语法
<template>
<div>
<!-- 绑定class样式--数组写法,适用于:要绑定的样式个数不确定、名字也不确定 -->
<div :class="classArr">数组写法1</div>
<!-- 渲染结果:<div class="activeClass errorClass"></div> -->
<div :class="[activeClass, errorClass]">数组写法2</div>
<!-- 渲染结果:<div class="active text-danger"></div> -->
<div :class="[isActive ? 'activeClass' : '', 'errorClass']">数组写法3</div>
<!-- 这样写将始终添加 errorClass,但是只有在 isActive 是 true 时才添加 activeClass -->
<!-- 渲染结果:<div class="activeClass errorClass"></div> -->
<div v-bind:class="[{ active: isActive4 }, 'errorClass']">数组写法4</div>
<!-- 渲染结果:<div class="active errorClass"></div> -->
<div v-bind:class="[errorClass]">数组写法5</div>
<!-- errorClass 变量 渲染data中数据 -->
<!-- 渲染结果:<div class="text-danger"></div> -->
</div>
</template>
<script>
export default {
data(){
return {
classArr:['activeClass','errorClass'],
activeClass: 'active',
errorClass: 'text-danger',
isActive:true,
isActive4:true
}
},
}
</script>
<style scoped>
</style>
数组语法小结
数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用
用在组件上
当在一个自定义组件上使用 class property 时,这些 class 将被添加到该组件的根元素上面。
这个元素上已经存在的 class 不会被覆盖。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>ls-demo</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!-- 组件 -->
<my-component class="baz boo"></my-component>
<!-- 渲染结果:<p class="foo bar baz boo">Hi</p> -->
<!-- 对于带数据绑定 class 也同样适用:<my-component v-bind:class="{ active: isActive }"></my-component> -->
</div>
<script>
Vue.component("my-component",{
//1.组件名为"my-component"; 2.data 写函数; 3.template 写组件的内容(元素和触发的事件)
//template 是模板的意思,在 html 里面是一个可以同时控制多个子元素的父元素。在这里定义了组件的内容
template: '<p class="foo bar">Hi</p>'
})
new Vue({
el:"#app"
});
</script>
</body>
</html>
绑定内联样式
对象语法
<template>
<div>
<!-- 绑定style样式--对象写法 -->
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }">对象写法1</div>
<div v-bind:style="styleObject">对象写法2</div>
</div>
</template>
<script>
export default {
data(){
return {
activeColor:'red',
fontSize:16,
styleObject: {
color: 'red',
fontSize: '16px'
}
}
},
}
</script>
<style scoped>
</style>
数组语法
<template>
<div>
<!-- 绑定style样式--数组写法 -->
<div v-bind:style="[baseStyles, overridingStyles]">数组写法1</div>
<div v-bind:style="sumStyles">数组写法2</div>
</div>
</template>
<script>
export default {
data(){
return {
baseStyles:{
fontSize: '40px',
color:'blue',
},
overridingStyles:{
backgroundColor:'blue',
},
sumStyles:[
{
fontSize: '40px',
color:'blue',
},
{
backgroundColor:'blue'
}
]
}
}
}
</script>
<style scoped>
</style>
自动添加前缀
当 v-bind:style 使用需要添加浏览器引擎前缀的 CSS property 时,如 transform,Vue.js 会自动侦测并添加相应的前缀。
多重值
从 2.3.0 起你可以为 style 绑定中的 property 提供一个包含多个值的数组,常用于提供多个带前缀的值
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
这样写只会渲染数组中最后一个被浏览器支持的值。
在本例中,如果浏览器支持不带浏览器前缀的 flexbox,那么就只会渲染 display: flex。
六、条件渲染
v-if
<template>
<div>
<!-- 在 <template> 元素上使用 v-if 条件渲染分组 -->
<!-- 把<template> 元素当做不可见的包裹元素,并在上面使用 v-if。最终的渲染结果将不包含 <template> 元素 -->
<template v-if="ok">
<h1>Title</h1>
</template>
<!-- v-else -->
<!-- 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-else-if -->
<!-- 类似于 v-else,v-else-if 也必须紧跟在带 v-if 或者 v-else-if 的元素之后。 -->
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
<!-- 用 key 管理可复用的元素 -->
<!-- Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。
这么做除了使 Vue 变得非常快之外,还有其它一些好处。
例如,如果你允许用户在不同的登录方式之间切换: -->
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address" key="email-input">
</template>
<!-- 那么在上面的代码中切换 loginType 将不会清除用户已经输入的内容。
因为两个模板使用了相同的元素,<input> 不会被替换掉——仅仅是替换了它的 placeholder -->
<!-- 这样也不总是符合实际需求,所以 Vue 为你提供了一种方式来表达“这两个元素是完全独立的,不要复用它们”。
只需添加一个具有唯一值的 key attribute 即可 -->
<!-- 注意,<label> 元素仍然会被高效地复用,因为它们没有添加 key attribute。 -->
</div>
</template>
<script>
export default {
data(){
return {
ok:true,
type:'A',
loginType:'username'
}
}
}
</script>
<style scoped>
</style>
v-if小结
适用于:切换频率较低的场景
特点:不展示的DOM元素直接被移除
v-show
<template>
<div>
<!-- v-show 指令 -->
<!-- 不同的是带有 v-show 的元素始终会被渲染并保留在 DOM 中。
v-show 只是简单地切换元素的 CSS property display。 -->
<!-- 注意,v-show 不支持 <template> 元素,也不支持 v-else。 -->
<h1 v-show="ok">Hello!</h1>
</div>
</template>
<script>
export default {
data(){
return {
ok:true,
}
}
}
</script>
<style scoped>
</style>
v-if vs v-show
v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。
因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。
v-if是动态的向DOM树内添加或者删除DOM元素;v-show是通过设置DOM元素的display样式属性控制显隐
v-if 与 v-for 一起使用
当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级。
不推荐同时使用 v-if 和 v-for
七、列表渲染
数组语法:
v-for="item in items"
v-for="(item,index) in items"
items:要迭代的数组(数组名)
item:存储数组元素的变量名(数组每条对象)
index:迭代到的当前元素索引,从0开始。
用 v-for 把一个数组对应为一组元素
<template>
<div>
<ul id="example-1">
<!-- <div v-for="item of data"></div> -->
<!-- 你也可以用 of 替代 in 作为分隔符,因为它更接近 JavaScript 迭代器的语法: -->
<li v-for="(item,index) in data" :key="item.message">
当前项的索引:{
{ index }}
<br>
当前对象:{
{item}}
<br>
当前对象中message数据: {
{ item.message }}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
data: [
{
message: 'Foo' },
{
message: 'Bar' }
]
};
}
};
</script>
<style>
</style>
在 v-for 里使用对象
对象语法:
v-for="value in object"
v-for="(value,key) in object"
v-for="(value,key,index) in object"
value,对象的值
key, 对象的键
index, 索引,从0开始
<template>
<div>
<ul id="v-for-object" class="demo">
<li v-for="(value,key,index) in object" :key="index">
value值:{
{ value }}
<br>
key值:{
{ key }}
<br>
索引值:{
{ index }}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
object: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
}
};
}
};
</script>
<style>
</style>
维护状态(key的作用与原理)
<template>
<div>
<ul id="v-for-object" class="demo">
<li v-for="(p,index) in persons" :key="index">
{
{p.name}} - {
{p.age}}
</li>
<button @click="handerAdd">前面添加一条数据</button>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
persons:[
{
id:'001',name:'张三',age:18},
{
id:'002',name:'李四',age:19},
{
id:'003',name:'王五',age:20}
]
};
},
methods: {
handerAdd(){
const p = {
id:'004',name:'老刘',age:40}
this.persons.unshift(p)
}
},
};
</script>
<style>
</style>
key的原理
面试题:react、vue中的key有什么作用?(key的内部原理)
虚拟DOM中key的作用:
key是虚拟DOM中对象的标识,当数据发生变化时,Vue会根据【新数据】生成
【新的虚拟DOM】,随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下
对比规则:
旧虚拟DOM中找到了与新虚拟DOM相同的key
若虚拟DOM中内容没变, 直接使用之前的真实DOM
若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM
旧虚拟DOM中未找到与新虚拟DOM相同的key:创建新的真实DOM,随后渲染到到页面
用index作为key可能会引发的问题:
若对数据进行逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==> 界面
效果没问题, 但效率低
若结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题
开发中如何选择key?
最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值
如果不存在对数据的逆序添加、逆序删除等破坏顺序的操作,仅用于渲染列表,使用index作为key
是没有问题的
数组更新检测
变更方法
Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
<template>
<div>
<div>points:{
{points}}</div>
<ul id="v-for-object" class="demo">
<li v-for="(p,index) in persons" :key="index">
{
{p.name}} - {
{p.age}}
</li>
<button @click="handerPush">push</button>
<button @click="handerPop">pop</button>
<button @click="handerShift">shift</button>
<button @click="handerUnshift">unshift</button>
<button @click="handerSplice">splice</button>
<button @click="handerSort">sort</button>
<button @click="handerReverse">reverse</button>
</ul>
</div>
</template>