1 介绍
是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。
2 特点:
- 核心只关注视图层(view)
- 灵活、轻量、灵活的特点
- 适用于移动端项目
- 渐进式框架
3 什么是库,什么是框架?
- 库是将代码集合成一个产品,库是我们调用库中的方法实现自己的功能
- 框架则是为解决一类问题而开发的产品,框架是我们在指定的位置编写代码,框架帮我们调用。
框架是库的升级版
4 渐进式
- 声明式渲染(无需关心如何实现)
- 组件系统
- 客户端路由(vue-router)
- 大规模状态管理(vuex)
- 构建工具(vue-cli)
5 Vue的两个核心点
- 响应的数据变化
- 当数据发生改变->视图的自动更新
- 组合的视图组件
- ui页面映射为组件树
- 划分组件可维护、可复用、可测试
6 MVC(backbone,react)
- model 数据
- view 视图
- controller 控制器
7 MVVM(angular,vue) 双向
- model 数据
- view 视图
- viewModel视图模型
8 Object.defineProperty(es5)没有替代方案
- 不支持ie8<=
2 vue基础指令
2.1 安装vue
- cdn方式
- npm 方式
2.2 简单的尝试
这里使用cdn方便测试
<html>
<head>
<meta charset="utf-8" />
<title>vuetitle>
head>
<body>
<div id="content">
{{ msg }}
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script>
// 引用vue后会给一个vue构造函数
var vm = new Vue({ // vm === viewModel
el: '#content', // 告诉vue管理哪一部分,querySelector "document.querySelector("#content")"
data: { // data中的数据会被vm所代理
msg: 'Hello Vue!' // 可以通过vm.msg获取对应的呢日用
}
})// Object.defineProperty
vm.msg = "wjw" // 修改视图
script>
html>
复制代码
2.3 模板语法
综上所属得出了一套模板语法
2.3.1 文本
<span>Message:{{msg}}span>
复制代码
2.3.2 表达式
{{number + 1}}
{{ok?'YES':'NO'}}
{{message.split('').reverse().join('')}}
复制代码
但是vue的表单元素 input checkbox textarea radio select 非文本处理
vue的指令 directive 只是dom上的行间属性,vue给这类属性赋予了一些意义,来实现特殊功能所有指令都以v-开头value属性默认情况下回vue忽略掉 selected checked 都没有意义
2.3.3表单输入
v-model 会将msg赋予输入框,输入框的值改变会影响数据
<input v-model="msg">
<input type="checkbox" v-model="msg1" value="爬山">
复制代码
2.3.4 原始HTML
<p>Using mustache:<span v-html='rawHtml'>spn>p>
复制代码
2.3.5 指令
<p v-if='seen'>现在看到我了p>
复制代码
2.3.6 特性
<div v-bind:id='dynamicld'>div>
复制代码
2.4 Object.defineProperty原理
<html>
<head>
<meta charset="utf-8" />
<title>vuetitle>
head>
<body>
<div id="content">div>
<input type="text" id="input">
body>
<script>
let obj = {}
let temp = {};
document.getElementById("content").innerHTML = obj.name
// 'name' 代表属性
Object.defineProperty(obj,'name',{
configurable:false, //是否可删除
// writable:true,// 是否可赋值(如果使用set方法,则不能使用)
enumerable:true, // 是否可枚举,也是就for..in..
// value:1,// 值(如果使用get方法,则不能使用)
get(){ // 取obj的name会触发get方法
return temp['name']
},
set(val){// 给obj赋值会触发get方法
// console.log(val);
temp['name'] = val // 改变temp的结果
input.value = val // 将值赋值给输入框
}
});
input.value = obj.name // 页面一加载,会将调用get方法
input.addEventListener('input',function(){ // 等待输入框的变化
obj.name = this.value // 当值变化时会调用set方法
document.getElementById("content").innerHTML = obj.name
})
script>
html>
复制代码
最后可以实现双向绑定的雏形
3 数据响应的变化
vue会循环data中的数据(数据劫持) 依次的增加getter和setter
let vm = new Vue({
el:'#content',
data:{
a:{}
}
})
复制代码
但是这时候我想添加一个school方法,发现没有产生getter和setter
1.1 方法一 $set
使用变量时 先要初始化,否则新加的属性不会导致页面刷新
vm.$set(vm.a,"school",'1')// 此方法可以给对象添加响应式的变化
复制代码
1.2 方法二 替换原对象
vm.a = {"school":"heihei",age:8};
复制代码
1.3 数组问题
去改变数组中的某一项监控不到的,也不能改变数组的长度方法
let vm = new Vue({
el:'#content',
data:{
a:[1,2,3,4,5,6]
}
})
复制代码
错误方法
vm.a[0] =100
vm.a.length -=2
复制代码
变异方法:pop push shift unshit sort reserve splice
vm.a = vm.a.map(item=>item*3)
复制代码
4 数组的循环v-for
vue 提供了一个v-for 解决循环问题 更高效 会复用原有结构
4.1 代码
<html>
<head>
<meta charset="utf-8" />
<title>vuetitle>
head>
<body>
<div id="content">
<li v-for="(todo,index) in todos">
{{ todo.text }} {{index+1}}
li>
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script>
let vm = new Vue({
el:'#content',
data:{
todos: [
{ text: '学习 JavaScript' },
{ text: '学习 Vue' },
{ text: '整个牛项目' }
]
}
})
script>
html>
复制代码
v-for循环数组 当用for来更新已被渲染的元素时,vue的“就地复用”机制 是不会改变数据项的顺序的。要想重新排序,需为每项添加key属性(也就是每项唯一的id)
想要改变
会改变原始数组的方法,为变异方法 例如push(),pop()等; 非变异方法,不会改变原始数组,但是会返回一个新数组
4.2 为什么v-for一定要有key
<html>
<head>
<meta charset="utf-8" />
<title>vuetitle>
head>
<body>
<div id="app">
<div>
<input type="text" v-model="name">
<button @click="add">添加button>
div>
<ul>
<li v-for="(item, i) in list">
<input type="checkbox"> {{item.name}}
li>
ul>
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
name: '',
newId: 3,
list: [
{ id: 1, name: '蔬菜' },
{ id: 2, name: '奶酪' },
{ id: 3, name: '肉' }
]
},
methods: {
add() {
//注意这里是unshift
this.list.unshift({ id: ++this.newId, name: this.name })
this.name = ''
}
}
});
script>
div>
html>
复制代码
当你输入汤时
就会变成这个样子 =>
但是当你换成了key
可以简单的这样理解:加了key(一定要具有唯一性) id的checkbox跟内容进行了一个关联。是我们想达到的效果
vue和react的虚拟DOM的Diff算法大致相同,其核心是基于两个简单的假设
首先讲一下diff算法的处理方法,对操作前后的dom树同一层的节点进行对比,一层一层对比,如下图:
当某一层有很多相同的节点时,也就是列表节点时,Diff算法的更新过程默认情况下也是遵循以上原则。
比如一下这个情况:
我们希望可以在B和C之间加一个F,Diff算法默认执行起来是这样的:
即把C更新成F,D更新成C,E更新成D,最后再插入E,是不是很没有效率?
所以我们需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点。
vue中列表循环需加:key="唯一标识" 唯一标识可以是item里面id index等,因为vue组件高度复用增加Key可以标识组件的唯一性,为了更好地区别各个组件 key的作用主要是为了高效的更新虚拟DOM
5 事件
5.1 定义&缩写
事件定义以及缩写
<div id="app">
<button @click="msg">button>
<button @mousedown="add">button>
div>
let vm = new Vue({
el:"#app",
methods:{
msg(){
console.log(Math.random());
}
}
})
复制代码
methods和data中的数据会全部放在vm上,而且名字不能冲突,冲突会报错,methods中的this指向的都是实例
5.2 mousedown
当鼠标指针移动到元素上方,并按下鼠标按键(左、右键均可)时,会发生 mousedown 事件。
与 click 事件不同,mousedown 事件仅需要按键被按下,而不需要松开即可发生。
5.3 mouseup
当在元素上松开鼠标按键(左、右键均可)时,会发生 mouseup 事件。
与 click 事件不同,mouseup 事件仅需要松开按钮。当鼠标指针位于元素上方时,放松鼠标按钮就会触发该事件。
5.4 click
当鼠标指针停留在元素上方,然后按下并松开鼠标左键时,就会发生一次 click 事件。
注意:触发click事件的条件是按下并松开鼠标左键!,按下并松开鼠标右键并不会触发click事件。
三个事件的触发顺序
5.5 总结
若在同一个元素上按下并松开鼠标左键,会依次触发mousedown、mouseup、click,前一个事件执行完毕才会执行下一个事件
若在同一个元素上按下并松开鼠标右键,会依次触发mousedown、mouseup,前一个事件执行完毕才会执行下一个事件,不会触发click事件
6 事件修饰符的使用
1 事件处理
如果需要在内联语句处理器中访问原生DOM事件。可以使用特殊变量$event
,把它传入到methods
中的方法中。
在Vue中,事件修饰符处理了许多DOM事件的细节,让我们不再需要花大量的时间去处理这些烦恼的事情,而能有更多的精力专注于程序的逻辑处理。在Vue中事件修饰符主要有:
.stop
:等同于JavaScript中的event.stopPropagation()
,防止事件冒泡.prevent
:等同于JavaScript中的event.preventDefault()
,防止执行预设的行为(如果事件可取消,则取消该事件,而不停止事件的进一步传播).capture
:与事件冒泡的方向相反,事件捕获由外到内.self
:只会触发自己范围内的事件,不包含子元素.once
:只会触发一次
1.1 stop 防止事件冒泡
冒泡事件:嵌套两三层父子关系,然后所有都有点击事件,点击子节点,就会触发从内至外 子节点-》父节点的点击事件
<div id="app">
<div class="outeer" @click="outer">
<div class="middle" @click="middle">
<button @click="inner">点击我(^_^)button>
div>
div>
<p>{{ message }}p>
div>
let app = new Vue({
el: '#app',
data () {
return { message: '测试冒泡事件' }
},
methods: {
inner: function () {
this.message = 'inner: 这是最里面的Button'
},
middle: function () {
this.message = 'middle: 这是中间的Div'
},
outer: function () {
this.message = 'outer: 这是外面的Div'
}
}
})
复制代码
防止冒泡事件的写法是:在点击上加上.stop相当于在每个方法中调用了等同于event.stopPropagation(),点击子节点不会捕获到父节点的事件
<div id="app">
<div class="outeer" @click.stop="outer">
<div class="middle" @click.stop="middle">
<button @click.stop="inner">点击我(^_^)button>
div>
div>
div>
复制代码
1.2 prevent取消默认事件
.prevent
等同于JavaScript的event.preventDefault()
,用于取消默认事件。比如我们页面的标签,当用户点击时,通常在浏览器的网址列出
#
:
1.3 .capture 捕获事件
捕获事件:嵌套两三层父子关系,然后所有都有点击事件,点击子节点,就会触发从外至内 父节点-》子节点的点击事件
<div id="app">
<div class="outeer" @click.capture="outer">
<div class="middle" @click.capture="middle">
<button @click.capture="inner">点击我(^_^)button>
div>
div>
div>
复制代码
1.4 .self
修饰符.self
只会触发自己范围内的事件,不会包含子元素。
<div id="app">
<div class="outeer" @click.self="outer">
<div class="middle" @click.self="middle">
<button @click.stop="inner">点击我(^_^)button>
div>
div>
div>
复制代码
1.5 .once 只执行一次点击
如果我们在@click
事件上添加.once
修饰符,只要点击按钮只会执行一次。
2 键盘修饰符
在JavaScript事件中除了前面所说的事件,还有键盘事件,也经常需要监测常见的键值。在Vue中允许v-on
在监听键盘事件时添加关键修饰符。记住所有的keyCode
比较困难,所以Vue为最常用的键盘事件提供了别名:
.enter
:回车键.tab
:制表键.delete
:含delete
和backspace
键.esc
:返回键.space
: 空格键.up
:向上键.down
:向下键.left
:向左键.right
:向右键
3 鼠标修饰符
鼠标修饰符用来限制处理程序监听特定的滑鼠按键。常见的有:
.left
:鼠标左键.middle
:鼠标中间滚轮.right
:鼠标右键
4 修饰键
可以用如下修饰符开启鼠标或键盘事件监听,使在按键按下时发生响应:
.ctrl
.alt
.shift
.meta
5 自定义按键修饰符别名
在Vue中可以通过config.keyCodes
自定义按键修饰符别名。例如,由于预先定义了keycode 116
(即F5
)的别名为f5
,因此在文字输入框中按下F5
,会触发prompt
方法,出现alert
。
"app">
type="text" v-on:keydown.f5="prompt()">
Vue.config.keyCodes.f5 = 116;
let app = new Vue({
el: '#app',
methods: {
prompt: function() {
alert('我是 F5!');
}
}
});
复制代码
6 总结
在Vue中,使用v-on
来给元素绑定事件,而为了更好的处理逻辑方面的事物,Vue提供了一个methods
。在methods
中定义一些方法,这些方法可以帮助我们处理一些逻辑方面的事情。而在这篇文章中,我们主要介绍了一些事件的修饰符,比如常见的阻止事件冒泡,键盘修饰符等。除此之外,还提供了config.keyCodes
提供自定义按键修饰符别名。
7 缩写
7.1 指令缩写
<a v-bind:href='url'>a>
<a :href='url'>a>
<a v-on:click='doSomething'>a>
<a @click='doSomething'>a>
复制代码
7.2 函数缩写
缩写后
8 组件化管理
1.组件化开发
我们可以很直观的将一个复杂的页面分割成若干个独立组件,每个组件包含组件的逻辑和样式,再将这些独立组件完成一个复杂的页面。这样既减少了逻辑复杂度,又实现了代码的重用。页面是组件的容器,组件自动组合形成完整的界面,当不需要某个组件时,或者想要替换某个组件时,可以随时进行替换和删除,而不影响整个应用的运行。
2、组件化开发的好处
- 提高开发效率
- 方便重复使用
- 便于协同开发
- 更容易被管理和维护
在vue中例如div、span都可以看做一个组件
3、全局组件
- 全局组件:可以声明一次在任何地方使用
- 局部组件:必须告诉这个组件属于谁
一般写插件的时候全局组件使用的多一些
<html>
<head>
<meta charset="utf-8" />
<title>vuetitle>
head>
<body>
<div id="app">
<my-handsom>my-handsom>
<my-handsom>my-handsom>
<my-handsom>my-handsom>
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script>
Vue.component("my-handsom",{ //一个对象可以看成一个组件
data: function () {
return {
count: 0
}
},
template: ''
})
var vm = new Vue({
el: '#app'
})
script>
html>
复制代码
- 组件名不要带大写,多组件使用 -
- 只要组件和定义相同是可以的(首字母可以大写)
- html采用短横线隔开命名法js中转驼峰也是可以的
深入了解组件
props
组件的参数传递
slot
插槽在组件抽象设计中的应用
自定义事件
父子组件的通信方式
9 全局api- Vue.extend
使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。
data
选项是特例,需要注意 - 在 Vue.extend()
中它必须是函数
"mount-point">div>
复制代码
// 创建构造器
var demo = Vue.extend({
template: '{{firstName}} {{lastName}} aka {{alias}}
',
data: function () {
return {
firstName: 'Walter',
lastName: 'White',
alias: 'Heisenberg'
}
}
})
// 创建 Profile 实例,并挂载到一个元素上。
new demo().$mount('#mount-point')
复制代码
10 全局api-nextTick
官方说明
参数:
{Function} [callback]
{Object} [context]
用法:
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
// 修改数据
vm.msg = 'Hello'
// DOM 还没有更新
Vue.nextTick(function () {
// DOM 更新了
})
// 作为一个 Promise 使用 (2.1.0 起新增,详见接下来的提示)
Vue.nextTick()
.then(function () {
// DOM 更新了
})
复制代码
2.1.0 起新增:如果没有提供回调且在支持 Promise 的环境中,则返回一个 Promise。请注意 Vue 不自带 Promise 的 polyfill,所以如果你的目标浏览器不原生支持 Promise (IE:你们都看我干嘛),你得自己提供 polyfill。
示例
先来一个示例了解下关于Vue中的DOM更新以及nextTick
的作用。
模板
{{msg}}
Message got outside $nextTick: {{msg1}}
Message got inside $nextTick: {{msg2}}
Message got outside $nextTick: {{msg3}}
复制代码
Vue实例
**
new Vue({
el: '.app',
data: {
msg: 'Hello Vue.',
msg1: '',
msg2: '',
msg3: ''
},
methods: {
changeMsg() {
this.msg = "Hello world."
this.msg1 = this.$refs.msgDiv.innerHTML
this.$nextTick(() => {
this.msg2 = this.$refs.msgDiv.innerHTML
})
this.msg3 = this.$refs.msgDiv.innerHTML
}
}
})
复制代码
点击前
点击后
从图中可以得知:msg1和msg3显示的内容还是变换之前的,而msg2显示的内容是变换之后的。其根本原因是因为Vue中DOM更新是异步的(详细解释在后面)。
应用场景
下面了解下nextTick
的主要应用的场景及原因。
- 在Vue生命周期的
created()
钩子函数进行的DOM操作一定要放在Vue.nextTick()
的回调函数中
在created()
钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进Vue.nextTick()
的回调函数中。与之对应的就是mounted()
钩子函数,因为该钩子函数执行时所有的DOM挂载和渲染都已完成,此时在该钩子函数中进行任何DOM操作都不会有问题 。
- 在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的DOM结构的时候,这个操作都应该放进
Vue.nextTick()
的回调函数中。
具体原因在Vue的官方文档中详细解释:
Vue 异步执行 DOM 更新。只要观察到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作上非常重要。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。Vue 在内部尝试对异步队列使用原生的 Promise.then
和MessageChannel
,如果执行环境不支持,会采用 setTimeout(fn, 0)
代替。 例如,当你设置vm.someData = 'new value'
,该组件不会立即重新渲染。当刷新队列时,组件会在事件循环队列清空时的下一个“tick”更新。多数情况我们不需要关心这个过程,但是如果你想在 DOM 状态更新后做点什么,这就可能会有些棘手。虽然 Vue.js 通常鼓励开发人员沿着“数据驱动”的方式思考,避免直接接触 DOM,但是有时我们确实要这么做。为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用Vue.nextTick(callback)
。这样回调函数在 DOM 更新完成后就会调用。
11 全局api-set
官网说明
Vue.set( target, propertyName/index, value )
- 参数:
{Object | Array} target
{string | number} propertyName/index
{any} value
- 返回值:设置的值。
- 用法:
向响应式对象中添加一个属性,并确保这个新属性同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新属性,因为 Vue 无法探测普通的新增属性 (比如 this.myObject.newProperty = 'hi'
)
注意对象不能是 Vue 实例,或者 Vue 实例的根数据对象。
示例
{{items}}
复制代码
1 设置数组元素
Vue.set(vm.items,2,"ling") : 表示 把vm.items 这个数组的下标为2 的元素,改为"ling"
把数组 ["a","b","c"] 修改 后是 ["a","b","ling"]
2 向响应式对象添加属性
{{person}}
复制代码
注意:person 是data 里面的子对象,所以可以使用 Vue.set( ) 方法。data 这个根对象就不能使用 set 方法
说明:控制台可以在person 里找到age 这个属性,说明添加成功 (响应式)
**
对比非响应式方法
vm.food="chocolate"
alert(vm.food)
控制台和网页上的 {{person}} 都没有显示food 这个属性,说明food 这个属性没有被添加 (非响应式)
**
12 全局api-delete
Vue.delete( target, propertyName/index )
- 参数:
{Object | Array} target
{string | number} propertyName/index
仅在 2.2.0+ 版本中支持 Array + index 用法。
- 用法:
删除对象的属性。如果对象是响应式的,确保删除能触发更新视图。这个方法主要用于避开 Vue 不能检测到属性被删除的限制,但是你应该很少会使用它。
在 2.2.0+ 中同样支持在数组上工作。
- 目标对象不能是一个 Vue 实例或 Vue 实例的根数据对象。
data:{
namelist : {
id : 1,
name : '叶落森'
}
}
复制代码
// 删除name
delete this.namelist.name;//js方法
Vue.delete(this.namelist,'name');//vue方法
复制代码
13 全局api-fifer过滤器
8.1 介绍
允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值和 v-bind
表达式 (后者从 2.1.0+ 开始支持)。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示
8.2 优势
1、在Vue中使用过滤器(Filters)来渲染数据是一种很有趣的方式。
2、首先我们要知道,Vue中的过滤器不能替代Vue中的methods
、computed
或者watch
,
3、过滤器不改变真正的data
,而只是改变渲染的结果,并返回过滤后的版本。
4、在很多不同的情况下,过滤器都是有用的,比如尽可能保持API响应的干净,并在前端处理数据的格式。
5、在你希望避免重复和连接的情况下,它们也可以有效地封装成可重用代码块背后的所有逻辑。
8.3 过滤器例子
<html>
<head>
<meta charset="utf-8" />
<title>vuetitle>
head>
<body>
<div id="app">
<div>{{ message | capitalize }}div>
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script>
var vm= new Vue({
el: '#app',
data: {
message: 'world'
},
filters: { // 可以有好多的自定义过滤器
capitalize(value) { // 这里的this指向的window
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
});
script>
html>
复制代码
8.4 过滤器串连
<html>
<head>
<meta charset="utf-8" />
<title>vuetitle>
head>
<body>
<div id="app">
{{ message | filterA | filterB }}
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script>
var vm= new Vue({
el: '#app',
data: {
message: 'world'
},
filters: { // 可以有好多的自定义过滤器
filterA(value){
return value.split('').reverse().join('');
},
filterB(value){
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
});
script>
html>
复制代码
8.5 过滤器传参
<html>
<head>
<meta charset="utf-8" />
<title>vuetitle>
head>
<body>
<div id="app">
{{ message | filterA('hello',hi) }}
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script>
var vm= new Vue({
el: '#app',
data: {
hi:'!',
message: 'world'
},
filters: { // 可以有好多的自定义过滤器
filterA(value1,value2,value3){
return `${value2} ${value1} ${value3}`;
}
}
});
script>
html>
复制代码
这里,filterA 被定义为接收三个参数的过滤器函数。其中 message 的值作为第一个参数,普通字符串 'hello' 作为第二个参数,表达式 hi 的值作为第三个参数。
14 插槽-slot
老版本vue
模板中只能有一个根元素
HTML内容模板(template)元素是一种用于保存客户端内容机制,该内容在加载页面时不会呈现,但随后可以在运行时使用JavaScript实例化。
<div id="app">
<modal>modal>
div>
<template id="modal">
<div>
<h1>是否删除h1>
div>
template>
复制代码
let modal = {
template:"#modal"
}
const app = new Vue({
el:'#app',
components:{
modal
},
data:{
}
})
复制代码
我们通常是想把h1的值动态放入,所以就要用到插槽
单个插槽 | 默认插槽 | 匿名插槽
首先是单个插槽,单个插槽是vue的官方叫法,但是其实也可以叫它默认插槽,或者与具名插槽相对,我们可以叫它匿名插槽。因为它不用设置name属性。 单个插槽可以放置在组件的任意位置,但是就像它的名字一样,一个组件中只能有一个该类插槽。相对应的,具名插槽就可以有很多个,只要名字(name属性)不同就可以了。
<div id="app">
<modal>
<h1>插入成功h1>
modal>
div>
<template id="modal">
<div>
<slot>slot>
div>
template>
复制代码
当我们看到插入成功的时候,匿名插入就实现了
具名插槽
匿名插槽没有name属性,所以是匿名插槽,那么,插槽加了name属性,就变成了具名插槽。具名插槽可以在一个组件中出现N次,出现在不同的位置。下面的例子,就是一个有两个具名插槽和单个插槽的组件,这三个插槽被父组件用同一套css样式显示了出来,不同的是内容上略有区别。
简单的来说,就是,我们可能遇到一个问题 我们想插入不同的插槽内的内容不一样
在 2.6.0+ 中已弃用
<div id="app">
<modal>
<h1>插入成功h1>
<h2 slot="title">标题h2>
<h2 slot="content">内容h2>
modal>
div>
<template id="modal">
<div>
<slot name="default">slot>
<slot name="title">slot>
<slot name="content">slot>
div>
template>
复制代码
我们可以发现没有name的情况下,默认就是default
作用域插槽 | 带数据的插槽
最后,就是我们的作用域插槽。这个稍微难理解一点。官方叫它作用域插槽,实际上,对比前面两种插槽,我们可以叫它带数据的插槽。什么意思呢,就是前面两种,都是在组件的template里面写
在 2.6.0+ 中已弃用
```html Vue作用域插槽
{{users[props.$index].id}}
{{users[props.$index].name}}
{{users[props.$index].age}} 编辑 删除
```
这种写法,习惯了element-ui的朋友一定就很熟悉了。
总结:
1 . 使用slot可以在自定义组件内插入原生HTML元素,需要搭配使用name和slot属性,否则多个slot可能会返回重复的HTML元素。
2 . 使用slot-scope可以将slot内部的作用域指向该子组件,否则默认作用域指向调用slot的父组件。
新版本的 v-slot
从 [email protected] 开始,Vue 为具名和范围插槽引入了一个全新的语法,即我们今天要讲的主角:v-slot
指令。目的就是想统一 slot
和 scope-slot
语法,使代码更加规范和清晰。既然有新的语法上位,很明显,slot
和 scope-slot
也将会在 [email protected]
中彻底的跟我们说拜拜了。而从 [email protected]
开始,官方推荐我们使用 v-slot
来替代后两者。
#### 具名插槽 > 实例化一个vue
// 组件
Vue.component('lv-hello', {
template: `
我的天呀
`
})
new Vue({
el: '#app1',
data: {
}
});
复制代码
老版本
<div id="app1">
<lv-hello>
<p slot="header">我是头部p>
lv-hello>
div>
复制代码
新版本的变化
<lv-hello>
<template v-slot:header>
<p>我是头部p>
template>
lv-hello>
div>
复制代码
具名插槽的缩写
将 v-slot:
替换成 #
号
<div id="app">
<lv-hello>
<template #header>
<p>我是头部p>
template>
<template #default>
<p>我是默认插槽p>
template>
lv-hello>
div>
复制代码
作用域插槽
所谓作用域插槽,就是让插槽的内容能够访问子组件中才有的数据。
Vue.component('lv-hello', {
data: function () {
return {
firstName: '张',
lastName: '三'
}
},
template: `
我的天呀
`
})
复制代码
<div id="app">
<lv-hello>
<p slot="header" slot-scope="hh">我是头部 {{ hh.firstName }} {{ hh.lastName }}p>
lv-hello>
<lv-hello>
<template v-slot:header="hh">
<p>我是头部 {{ hh.firstName }} {{ hh.lastName }}p>
template>
lv-hello>
div>
复制代码
15 动态绑定样式-v-bind
13.1 对象语法
:class 绑定的样式和class绑定的不冲突
13.1.1 直接绑定一个data
<div v-bind:class="{ active: isActive }">div>
复制代码
active
这个 class 存在与否将取决于数据属性 isActive
的 布尔值
<div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }">div>
复制代码
13.1.2 data中使用一个对象绑定
data: {
classObject: {
active: true,
'text-danger': false
}
}
复制代码
13.1.3 计算属性中绑定
data: {
isActive: true,
error: null
},
computed: {
classObject: function () {
return {
active: this.isActive && !this.error,
'text-danger': this.error && this.error.type === 'fatal'
}
}
}
复制代码
13.2 数组语法
<div v-bind:class="[activeClass, errorClass]">div>
复制代码
13.2.1 直接动态绑定一个class
data: {
activeClass: 'active',
errorClass: 'text-danger'
}
复制代码
13.2.2 三元表达式
<div v-bind:class="[isActive ? activeClass : '', errorClass]">div>
复制代码
不过,当有多个条件 class 时这样写有些繁琐。所以在数组语法中也可以使用对象语法:
<div v-bind:class="[{ active: isActive }, errorClass]">div>
复制代码
16 数据-计算属性(computed)
1 什么是计算属性
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。例如:
<html>
<head>
<meta charset="utf-8" />
<title>vuetitle>
head>
<body>
<div id="app">
<div id="example">
{{ message.split('').reverse().join('') }}
div>
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script>
var vm = new Vue({
el: '#app',
data: {
message: 'Hello'
}
});
script>
div>
html>
复制代码
这里的表达式包含3个操作,并不是很清晰,所以遇到复杂逻辑时应该使用Vue特带的计算属性computed来进行处理。
2 计算属性的用法
<html>
<head>
<meta charset="utf-8" />
<title>vuetitle>
head>
<body>
<div id="app">
<div id="example">
{{getMessage}}
div>
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script>
var vm = new Vue({
el: '#app',
data: {
message: 'Hello'
},
computed: { // 放在computed中最后也会放在vm上,不能和methods与data重名
getMessage() {
return this.message.split('').reverse().join('')
}
}
});
script>
div>
html>
复制代码
3 计算属性使用技巧
计算属性可以依赖其他计算属性
计算属性不仅可以依赖当前Vue 实例的数据,还可以依赖其他实例的数据
<html>
<head>
<meta charset="utf-8" />
<title>vuetitle>
head>
<body>
<div id="app1">div>
<div id="app2">
{{getMessage}}
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script>
var vm= new Vue({
el: '#app1',
data: {
message: 'World'
}
});
var vm2 = new Vue({
el: '#app2',
data: {
message: 'Hello'
},
computed: {
getMessage() {
return `${this.message} ${vm.message}`
}
}
});
script>
div>
html>
复制代码
4 getter和setter
每一个计算属性都包含一个getter 和一个setter ,我们上面的两个示例都是计算属性的默认用法, 只是利用了getter 来读取。
在你需要时,也可以提供一个setter 函数, 当手动修改计算属性的值就像修改一个普通数据那样时,就会触发setter 函数,执行一些自定义的操作
<html>
<head>
<meta charset="utf-8" />
<title>vuetitle>
head>
<body>
<div id="app">
<input type="text" v-model="getMessage"> <--模拟修改--!>
{{getMessage}}
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script>
var vm= new Vue({
el: '#app',
data: {
hi:'Hello',
message: 'World'
},
computed:{
getMessage:{ //get,set方法
// getter
get(){
return this.hi + ' ' + this.message
},
// setter
set(newValue){
console.log('====================================');
console.log(newValue);
console.log('====================================');
var names = newValue.split(' ');
this.hi = names[0];
this.message = names[names.length - 1];
}
}
}
});
script>
html>
复制代码
绝大多数情况下,我们只会用默认的getter 方法来读取一个计算属性,在业务中很少用到setter,所以在声明一个计算属性时,可以直接使用默认的写法,不必将getter 和setter 都声明。
5 质疑什么不直接用methods
我们可以将同一函数定义为一个方法而不是一个计算属性,两种方式的最终结果确实是完全相同的。只是一个使用getMessage()取值,一个使用getMessage取值。
然而,不同的是计算属性是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变时才会重新求值。
这就意味着只要 hi还没有发生改变,多次访问 getMessage计算属性会立即返回之前的计算结果,而不必再次执行函数。
<html>
<head>
<meta charset="utf-8" />
<title>vuetitle>
head>
<body>
<div id="app">
<div>{{getMessage}}div>
<div> {{getMessage1()}}div>
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script>
var vm= new Vue({
el: '#app',
data: {
hi:'Hello',
message: 'World'
},
computed:{
getMessage(){ //get,set方法
return this.hi + ' ' + this.message
//而使用计算属性,只要title没变,页面渲染是不会重新进这里来计算的,而是使用了缓存。
}
},
methods:{
getMessage1(){
return this.hi + ' ' + this.message
//进这个方法,再次计算。不是刷新,而是只要页面渲染,就会进方法里重新计算。
}
}
});
script>
html>
复制代码
17 数据-观察(watch)
一个对象,键是需要观察的表达式,值是对应回调函数。值也可以是方法名,或者包含选项的对象。Vue 实例将会在实例化时调用 $watch()
,遍历 watch 对象的每一个属性。
为什么一定要有watch,不用可以吗?我们已经有了computed,能不能不去使用?
1 watch的出现
做一个实验
<html>
<head>
<meta charset="utf-8" />
<title>vuetitle>
head>
<body>
<div id="app">
<input type="text" v-model="a">
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script>
var vm = new Vue({
el:'#app',
data:{
a:"1"
},
computed: {
a(){
setTimeout(() => {
this.a=1;
}, 500);
}
}
})
script>
html>
复制代码
不难发现在_异步的情况下就不好使用了_
2 代码实现
vue
复制代码
3 computed与watch的区别
Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch
<html>
<head>
<meta charset="utf-8" />
<title>vuetitle>
head>
<body>
<div id="app">
{{ fullName }}
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
html>
复制代码
var vm = new Vue({
el: '#app',
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
}
}
})
复制代码
上面代码是命令式且重复的。将它与计算属性的版本进行比较:
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
复制代码
是不是感觉优雅很多
4 侦听器
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
<html>
<head>
<meta charset="utf-8" />
<title>vuetitle>
head>
<body>
<div id="app">
<input type="text" v-model="something">
{{somethingShow}}
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script>
var vm = new Vue({
el: '#app',
data: {
something: '',
somethingShow:''
},
watch: {
something(val){
this.somethingShow = "loading"
this.getSomething()
}
},
methods:{
getSomething(){
setTimeout(() => {
this.somethingShow = "hello"
}, 1000);// 我们使用延迟模拟一个网络请求
}
}
})
script>
html>
复制代码
5 vm.$watch
vm.$watch( expOrFn, callback, [options] )
观察 Vue 实例变化的一个表达式或计算属性函数。回调函数得到的参数为新值和旧值。表达式只接受监督的键路径。对于更复杂的表达式,用一个函数取代。
<html>
<head>
<meta charset="utf-8" />
<title>vuetitle>
head>
<body>
<div id="app">
<input type="text" v-model="something">
{{somethingShow}}
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script>
var vm = new Vue({
el: '#app',
data: {
something: '',
somethingShow:''
}
})
vm.$watch('something',(newVal,oldVal)=>{// watch的属性名要和观察的人名字一致
vm.somethingShow = "loading"
console.log('====================================');
console.log(newVal);
console.log('====================================');
vm.somethingShow = newVal
})
script>
html>
复制代码
18 数据-属性(props)
组件接受的选项之一 props 是 Vue 中非常重要的一个选项。父子组件的关系可以总结为: props down, events up 父组件通过 props 向下传递数据给子组件;子组件通过 events 给父组件发送消息。
父子级组件
比如我们需要创建两个组件 parent 和 child。需要保证每个组件可以在相对隔离的环境中书写,这样也能提高组件的可维护性。
这里我们先定义父子两个组件和一个 Vue 对象
var childNode = {
template: `
childNode
`
};
var parentNode = {
template: `
`,
components: {
child: childNode
}
};
new Vue({
el: "#example",
components: {
parent: parentNode
}
});
复制代码
复制代码
这里的 childNode 定义的 template 是一个 div,并且内容是"childNode"字符串。 而在 parentNode 的 template 中定义了 div 的 class 名叫 parent 并且包含了两个 child 组件。
静态 props
组件实例的作用域是孤立的。这意味着不能(也不应该)在子组件的模板中直接引用父组件的数据。要让子组件使用父组件的数据,需要通过子组件的 props 选项。 父组件向子组件传递数据分为两种方式:动态和静态,这里先介绍静态方式。 子组件要显示的用 props 声明它期望获得的数据 修改上例中的代码,给 childNode 添加一个 props 选项和需要的forChildMsg
数据; 然后在父组件中的占位符添加特性的方式来传递数据。
var childNode = {
template: `
{{forChildMsg}}
`,
props: ["for-child-msg"] // 直接把参数作为数组放进去
};
var parentNode = {
template: `
parentNode
`,
components: {
child: childNode
}
};
复制代码
命名规范
**
对于 props 声明的属性,在父组件的 template 模板中,属性名需要使用中划线写法; 子组件 props 属性声明时,使用小驼峰或者中划线写法都可以;而子组件的模板使用从父组件传来的变量时,需要使用对应的小驼峰写法。别担心,Vue 能够正确识别出小驼峰和下划线命名法混用的变量,如这里的forChildMsg
和for-child-msg
是同一值。
动态props
原则上很简单,for-child-msg作为一个变量
var parentNode = {
template: `
parentNode
`,
components: {
child: childNode
},
data: function() {
return {
childMsg1: "child-1",
childMsg2: "child-2"
};
}
};
复制代码
在父组件的 data 的 return 数据中的 childMsg1 和 childMsg2 会被传入子组件中
props 验证
验证传入的 props 参数的数据规格,如果不符合数据规格,Vue 会发出警告。
能判断的所有种类(也就是 type 值)有: String, Number, Boolean, Function, Object, Array, Symbol
Vue.component("example", {
props: {
// 基础类型检测, null意味着任何类型都行
propA: Number,
// 多种类型
propB: [String, Number],
// 必传且是String
propC: {
type: String,
required: true
},
// 数字有默认值
propD: {
type: Number,
default: 101
},
// 数组、默认值是一个工厂函数返回对象
propE: {
type: Object,
default: function() {
console.log("propE default invoked.");
return { message: "I am from propE." };
}
},
// 自定义验证函数
propF: {
isValid: function(value) {
return value > 100;
}
}
}
});
let childNode = {
template: "{{forChildMsg}}",
props: {
"for-child-msg": Number
}
};
let parentNode = {
template: `
`,
components: {
child: childNode
},
data() {
return {
// 当这里是字符串 "123456"时会报错
msg: 123456
};
}
};
复制代码
还可以在 props 定义的数据中加入自定义验证函数,当函数返回 false 时,输出警告。 比如我们把上述例子中的 childNode 的for-child-msg
修改成一个对象,并包含一个名叫validator
的函数,该命名是规定叫validator
的,自定义函数名不会生效
let childNode = {
template: "{{forChildMsg}}",
props: {
"for-child-msg": {
validator: function(value) {
return value > 100;
}
}
}
};
复制代码
在这里我们给for-child-msg
变量设置了validator
函数,并且要求传入的值必须大于 100,否则报出警告。
单向数据流
props 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是不会反过来。这是为了防止子组件五一修改父组件的状态。
所以不应该在子组件中修改 props 中的值,Vue 会报出警告。
let childNode = {
template: `
子组件数据
{{forChildMsg}}
`,
props: {
"for-child-msg": String
}
};
let parentNode = {
template: `
父组件数据
{{msg}}
`,
components: {
child: childNode
},
data() {
return {
msg: "default string."
};
}
};
复制代码
传递的过程将短横分割命名,转成驼峰命名法即可
这里我们给父组件和子组件都有一个输入框,并且显示出父组件数据和子组件的数据。当我们在父组件的输入框输入新数据时,同步的子组件数据也被修改了;这就是 props 的向子组件传递数据。而当我们修改子组件的输入框时,浏览器的控制台则报出错误警告
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "forChildMsg"
修改 props 数据
通常有两种原因:
-
prop 作为初始值传入后,子组件想把它当做局部数据来用
-
prop 作为初始值传入后,由子组件处理成其他数据输出
-
定义一个局部变量,并用 prop 的值初始化它
但是由于定义的 ownChildMsg 只能接受 forChildMsg 的初始值,当父组件要传递的值变化发生时,ownChildMsg 无法收到更新。
let childNode = {
template: `
子组件数据
{{forChildMsg}}
ownChildMsg : {{ownChildMsg}}
`,
props: {
"for-child-msg": String
},
data() {
return { ownChildMsg: this.forChildMsg };
}
};
复制代码
这里我们加了一个
用于查看 ownChildMsg 数据是否变化,结果发现只有默认值传递给了 ownChildMsg,父组件改变只会变化到 forChildMsg,不会修改 ownChildMsg。
- 定义一个计算属性,处理 prop 的值并返回
由于是计算属性,所以只能显示值,不能设置值。我们这里设置的是一旦从父组件修改了 forChildMsg 数据,我们就把 forChildMsg 加上一个字符串"---ownChildMsg",然后显示在屏幕上。这时是可以每当父组件修改了新数据,都会更新 ownChildMsg 数据的。
let childNode = {
template: `
子组件数据
{{forChildMsg}}
ownChildMsg : {{ownChildMsg}}
`,
props: {
"for-child-msg": String
},
computed: {
ownChildMsg() {
return this.forChildMsg + "---ownChildMsg";
}
}
};
复制代码
- 更加妥帖的方式是使用变量存储 prop 的初始值,并用 watch 来观察 prop 值得变化。发生变化时,更新变量的值。
let childNode = {
template: `
子组件数据
{{forChildMsg}}
ownChildMsg : {{ownChildMsg}}
`,
props: {
"for-child-msg": String
},
data() {
return {
ownChildMsg: this.forChildMsg
};
},
watch: {
forChildMsg() {
this.ownChildMsg = this.forChildMsg;
}
}
};
复制代码
19 生命周期
1 vue生命周期简介
2 生命周期探究
<html>
<head>
<meta charset="utf-8" />
<title>vuetitle>
head>
<body>
<div id="app">{{message}}div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script>
var app = new Vue({
el: '#app',
data: {
message: "hello is world"
},
beforeCreate() {
console.group('beforeCreate 创建前状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el); //undefined
console.log("%c%s", "color:red", "data : " + this.$data); //undefined
console.log("%c%s", "color:red", "message: " + this.message)
},
created() {
console.group('created 创建完毕状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el); //undefined
console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化
console.log("%c%s", "color:red", "message: " + this.message); //已被初始化
},
beforeMount() {
console.group('beforeMount 挂载前状态===============》');
console.log("%c%s", "color:red", "el : " + (this.$el)); //已被初始化
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化
console.log("%c%s", "color:red", "message: " + this.message); //已被初始化
},
mounted() {
console.group('mounted 挂载结束状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el); //已被初始化
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化
console.log("%c%s", "color:red", "message: " + this.message); //已被初始化
},
beforeUpdate() {
console.group('beforeUpdate 更新前状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message);
},
updated() {
console.group('updated 更新完成状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message);
},
beforeDestroy() {
console.group('beforeDestroy 销毁前状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message);
},
destroyed() {
console.group('destroyed 销毁完成状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message)
}
})
script>
html>
复制代码
chrome
浏览器里打开,F12
看console
就能发现
3 beforecreated
el 和 data 并未初始化
4 created
完成了 data 数据的初始化,el没有
5 beforeMount
完成了 el 和 data 初始化
6 mounted
完成挂载
7 update
在console控制台中输入
app.message= 'hello!!';
复制代码
8 destroy
我们在console里执行下命令对 vue实例进行销毁。销毁完成后,我们再重新改变message的值,vue不再对此动作进行响应了。但是原先生成的dom元素还存在,可以这么理解,执行了destroy操作,后续就不再受vue控制了。
app.$destroy();
复制代码
9 生命周期总结
9.1 beforecreate
可以在这加个loading事件,加载的动画
9.2 created
在这结束loading,还做一些初始化,实现函数自执行
9.3 mounted
在这发起后端请求,拿回数据,配合路由钩子做一些事情
9.4 beforeDestroy
你确认删除XX吗? destroyed :当前组件已被删除,清空相关内容
20 指令-条件判断(v-if&v-show)
1 v-if&v-show
- 条件渲染 (使用
v-if
)
- 条件展示 (使用
v-show
)
if操作的是dom show 操作的样式 如果频繁切换dom使用v-show,当数据一开时就确定下来使用v-if更好一些,如果if通过内部指令不会执行了 只有dom从显示到隐藏 或者隐藏到显示 才能使用vue的动画
<html>
<head>
<meta charset="utf-8" />
<title>vuetitle>
head>
<body>
<div id="app">
<span v-if="flag">你看的见我span>
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script>
var vm = new Vue({
el:'#app',
data:{
flag:true
}
})
script>
html>
复制代码
2 区别总结
- v-show:操作的是元素的display属性
- v-if:操作的是元素的创建和插入
- 相比较而言v-show的性能要高
21 内置组件-动画(transition)
1 组件的过渡
Vue 提供了 transition
的封装组件,在下列情形中,可以给任何元素和组件添加进入/离开过渡
在进入/离开的过渡中,会有 6 个 class 切换。
v-enter
:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。
v-enter-active
:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。
v-enter-to
: 2.1.8版及以上 定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter
被移除),在过渡/动画完成之后移除。
v-leave
: 定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。
v-leave-active
:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。
v-leave-to
: 2.1.8版及以上 定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave
被删除),在过渡/动画完成之后移除。
1.1 初步代码实现
<html>
<head>
<meta charset="utf-8" />
<title>vuetitle>
head>
<style>
div>div{
width:100px;height: 100px;background: red;
}
.v-enter{
opacity: 1;
}
/* 激活的时候 */
.v-enter-avtive{
opacity: 0;
transition: 1s linear;
}
/* 离开 */
.v-leave-active{
opacity: 0;
background: black;
transition: 1s linear;
}
style>
<body>
<div id="app">
<button @click="flag=!flag">切换button>
<transition>
<div v-show="flag">div>
transition>
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script>
var vm = new Vue({
el:'#app',
data:{
flag:true
}
})
script>
html>
复制代码
1.2 多个transition
遇上了多个transition的时候,同一个class肯定是会冲突的,那么如何处理呢
<html>
<head>
<meta charset="utf-8" />
<title>vuetitle>
head>
<style>
div>div{
width:100px;height: 100px;background: red;
}
.jw-enter-active {
transition: all .3s ease;
}
.jw-leave-active {
transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
}
.jw-enter, .jw-leave-to
{
transform: translateX(10px);
opacity: 0;
}
style>
<body>
<div id="app">
<button @click="flag=!flag">切换button>
<transition name="jw">
<div v-show="flag">div>
transition>
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script>
var vm = new Vue({
el:'#app',
data:{
flag:true
}
})
script>
html>
复制代码
简单的理解就是就 transition有一个name属性
在css中name-状态即可调用
22 自定义指令-directives
1 介绍
Vue 也允许注册自定义指令。注意,在 Vue2.0 中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。
举一个栗子:
<html>
<head>
<meta charset="utf-8" />
<title>vuetitle>
head>
<body>
<div id="app">
<div v-color='flag'>123div>
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script>
var vm = new Vue({
directives:{
color(el,bindings){ //el值指代的是button按钮
console.log(arguments);
el.style.background = bindings.value;
}
},
el: '#app',
data: {
flag: 'red'
},
methods:{
getSomething(){
return "hello"
}
}
})
script>
html>
复制代码
出现如图情况
再来个栗子
<html>
<head>
<meta charset="utf-8" />
<title>vuetitle>
head>
<style>
.a{
position: absolute;width: 100px;height: 100px;background: red;
}
style>
<body>
<div id="app">
<div class="a" v-drag>div>
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script>
var vm = new Vue({
directives:{
drag(el){
el.onmousedown = function (e) {
var disx = e.pageX - el.offsetLeft;
var disy = e.pageY - el.offsetTop;
document.onmousemove = function (e) {
el.style.left = e.pageX - disx +'px';
el.style.top = e.pageX - disy + 'px';
}
document.onmouseup = function (e) {
document.onmousemove = document.onmousemove = null;
}
e.preventDefault();
}
}
},
el: '#app',
data: {
flag: 'red'
},
methods:{
getSomething(){
return "hello"
}
}
})
script>
html>
复制代码
可以拖动
2 钩子函数
一个指令定义对象可以提供如下几个钩子函数 (均为可选):
bind
:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
inserted
:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
update
:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
3 钩子函数参数
el
:指令所绑定的元素,可以用来直接操作 DOM 。
binding
:一个对象,包含以下属性:
name
:指令名,不包括 v-
前缀。
value
:指令的绑定值,例如:v-my-directive="1 + 1"
中,绑定值为 2
。
oldValue
:指令绑定的前一个值,仅在 update
和 componentUpdated
钩子中可用。无论值是否改变都可用。
expression
:字符串形式的指令表达式。例如 v-my-directive="1 + 1"
中,表达式为 "1 + 1"
。
arg
:传给指令的参数,可选。例如 v-my-directive:foo
中,参数为 "foo"
。
modifiers
:一个包含修饰符的对象。例如:v-my-directive.foo.bar
中,修饰符对象为 { foo: true, bar: true }
。
oldVnode
:上一个虚拟节点,仅在 update
和 componentUpdated
钩子中可用。
23 实例属性-$ref
官网针对-ref的解释
- 预期:
string
ref
被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs
对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例:
"p">hello
"child">
复制代码
- 当
v-for
用于元素或组件的时候,引用信息将是包含 DOM 节点或组件实例的数组。
关于 ref 注册时间的重要说明:因为 ref 本身是作为渲染结果被创建的,在初始渲染的时候你不能访问它们 - 它们还不存在!$refs
也不是响应式的,因此你不应该试图用它在模板中做数据绑定。
操作dom
如果我们用jQuery的话,一般性都可以操作dom
$("#id").text('xxx') // 使用Jquery
document.getElementById("id") // 使用原生Dom
复制代码
现在我们牛逼了,我们用vue。那vue中,如果我要获取Dom,该怎么做?
这就进入本文的主题ref, $refs,官网解释:
<div id="app">
<div>{{msg}}div>
div>
复制代码
在JavaScript中我们习惯了使用document.getElementsByTagName
```javascript var vm = new Vue({ el: '#app', data:{ msg:'hello' }, mounted() { console.log(document.getElementsByTagName("div")[0].innerHTML); } }) ```
vue操作dom
那么我们在vue中呢
<div id="app">
<div ref="msg">{{msg}}div>
div>
复制代码
var vm = new Vue({
el: '#app',
data:{
msg:'hello'
},
mounted() {
// console.log(document.getElementsByTagName("div")[0].innerHTML);
console.log('====================================');
console.log(this.$refs.msg);
console.log('====================================');
}
})
复制代码
转载于:https://juejin.im/post/5d08b86b6fb9a07eaf2b93a9