是一套用于构建用户界面的渐进式框架。与其它大型框架不一样的是,Vue 被设计为能够自底向上逐层应用。Vue 的核心库只关注视图层,不只易于上手,还便于与第三方库或既有项目整合。javascript
框架是库的升级版css
这里使用cdn方便测试html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>vue</title>
</head>
<body>
<div id="content">
<!-- moustache 小胡子语法 表达式 能够放赋值 取值 三元-->
{{ msg }}
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<!-- 使用官网的vue地址 -->
<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>
复制代码
综上所属得出了一套模板语法前端
<span>Message:{{msg}}</span>
复制代码
{{number + 1}}
{{ok?'YES':'NO'}}
{{message.split('').reverse().join('')}}
复制代码
可是vue的表单元素 input checkbox textarea radio select 非文本处理vue
vue的指令 directive 只是dom上的行间属性,vue给这类属性赋予了一些意义,来实现特殊功能全部指令都以v-开头value属性默认状况下回vue忽略掉 selected checked 都没有意义java
v-model 会将msg赋予输入框,输入框的值改变会影响数据node
<input v-model="msg">
<input type="checkbox" v-model="msg1" value="登山">
复制代码
<p>Using mustache:<span v-html='rawHtml'></spn></p>
复制代码
<p v-if='seen'>如今看到我了</p>
复制代码
<div v-bind:id='dynamicld'></div>
复制代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>vue</title>
</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>
复制代码
最后能够实现双向绑定的雏形react
vue会循环data中的数据(数据劫持) 依次的增长getter和settergit
let vm = new Vue({
el:'#content',
data:{
a:{}
}
})
复制代码
可是这时候我想添加一个school方法,发现没有产生getter和settergithub
使用变量时 先要初始化,不然新加的属性不会致使页面刷新
vm.$set(vm.a,"school",'1')// 此方法能够给对象添加响应式的变化
复制代码
vm.a = {"school":"heihei",age:8};
复制代码
去改变数组中的某一项监控不到的,也不能改变数组的长度方法
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)
复制代码
vue 提供了一个v-for 解决循环问题 更高效 会复用原有结构
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>vue</title>
</head>
<body>
<div id="content">
<!--要循环谁就在谁身上增长v-for属性,相似于for...in..-->
<!--默认是value of 数组/ (value,index) of 数组-->
<li v-for="(todo,index) in todos">
<!-- 会改变原始数组的方法,为变异方法 例如push(),pop()等; 非变异方法,不会改变原始数组,可是会返回一个新数组 -->
{{ todo.text }} {{index+1}}
</li>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<!-- 使用官网的vue地址 -->
<script> let vm = new Vue({ el:'#content', data:{ todos: [ { text: '学习 JavaScript' }, { text: '学习 Vue' }, { text: '整个牛项目' } ] } }) </script>
</html>
复制代码
v-for循环数组 当用for来更新已被渲染的元素时,vue的“就地复用”机制 是不会改变数据项的顺序的。要想从新排序,需为每项添加key属性(也就是每项惟一的id)
想要改变
会改变原始数组的方法,为变异方法 例如push(),pop()等; 非变异方法,不会改变原始数组,可是会返回一个新数组
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>vue</title>
</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>
<!-- 使用官网的vue地址 -->
<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树同一层的节点进行对比,一层一层对比,以下图:
即把C更新成F,D更新成C,E更新成D,最后再插入E,是否是很没有效率?
因此咱们须要使用key来给每一个节点作一个惟一标识,Diff算法就能够正确的识别此节点,找到正确的位置区插入新的节点。
vue中列表循环需加:key="惟一标识" 惟一标识能够是item里面id index等,由于vue组件高度复用增长Key能够标识组件的惟一性,为了更好地区别各个组件 key的做用主要是为了高效的更新虚拟DOM
事件定义以及缩写
<div id="app">
<button @click="msg"></button>
<button @mousedown="add"></button>
<!--若是不传递参数,则不要写括号会自动传入事件源,若是写括号了,要手动传入$event属性-->
</div>
let vm = new Vue({
el:"#app",
methods:{
msg(){
console.log(Math.random());
}
}
})
复制代码
methods和data中的数据会所有放在vm上,并且名字不能冲突,冲突会报错,methods中的this指向的都是实例
当鼠标指针移动到元素上方,并按下鼠标按键(左、右键都可)时,会发生 mousedown 事件。
与 click 事件不一样,mousedown 事件仅须要按键被按下,而不须要松开便可发生。
当在元素上松开鼠标按键(左、右键都可)时,会发生 mouseup 事件。
与 click 事件不一样,mouseup 事件仅须要松开按钮。当鼠标指针位于元素上方时,放松鼠标按钮就会触发该事件。
当鼠标指针停留在元素上方,而后按下并松开鼠标左键时,就会发生一次 click 事件。
注意:触发click事件的条件是按下并松开鼠标左键!,按下并松开鼠标右键并不会触发click事件。
三个事件的触发顺序
若在同一个元素上按下并松开鼠标左键,会依次触发mousedown、mouseup、click,前一个事件执行完毕才会执行下一个事件
若在同一个元素上按下并松开鼠标右键,会依次触发mousedown、mouseup,前一个事件执行完毕才会执行下一个事件,不会触发click事件
若是须要在内联语句处理器中访问原生DOM事件。可使用特殊变量$event
,把它传入到methods
中的方法中。
在Vue中,事件修饰符处理了许多DOM事件的细节,让咱们再也不须要花大量的时间去处理这些烦恼的事情,而能有更多的精力专一于程序的逻辑处理。在Vue中事件修饰符主要有:
.stop
:等同于JavaScript中的event.stopPropagation()
,防止事件冒泡.prevent
:等同于JavaScript中的event.preventDefault()
,防止执行预设的行为(若是事件可取消,则取消该事件,而不中止事件的进一步传播).capture
:与事件冒泡的方向相反,事件捕获由外到内.self
:只会触发本身范围内的事件,不包含子元素.once
:只会触发一次冒泡事件:嵌套两三层父子关系,而后全部都有点击事件,点击子节点,就会触发从内至外 子节点-》父节点的点击事件
<!-- HTML -->
<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(),点击子节点不会捕获到父节点的事件
<!-- HTML -->
<div id="app">
 <div class="outeer" @click.stop="outer">
 <div class="middle" @click.stop="middle">
 <button @click.stop="inner">点击我(^_^)</button>
</div>
</div>
</div>
复制代码
.prevent
等同于JavaScript的event.preventDefault()
,用于取消默认事件。好比咱们页面的<a href="#">
标签,当用户点击时,一般在浏览器的网址列出#
:
捕获事件:嵌套两三层父子关系,而后全部都有点击事件,点击子节点,就会触发从外至内 父节点-》子节点的点击事件
<!-- HTML -->
<div id="app">
 <div class="outeer" @click.capture="outer">
 <div class="middle" @click.capture="middle">
 <button @click.capture="inner">点击我(^_^)</button>
</div>
</div>
</div>
复制代码
修饰符.self
只会触发本身范围内的事件,不会包含子元素。
<!-- HTML -->
<div id="app">
 <div class="outeer" @click.self="outer">
 <div class="middle" @click.self="middle">
 <button @click.stop="inner">点击我(^_^)</button>
</div>
</div>
</div>
复制代码
若是咱们在@click
事件上添加.once
修饰符,只要点击按钮只会执行一次。
在JavaScript事件中除了前面所说的事件,还有键盘事件,也常常须要监测常见的键值。在Vue中容许v-on
在监听键盘事件时添加关键修饰符。记住全部的keyCode
比较困难,因此Vue为最经常使用的键盘事件提供了别名:
.enter
:回车键.tab
:制表键.delete
:含delete
和backspace
键.esc
:返回键.space
: 空格键.up
:向上键.down
:向下键.left
:向左键.right
:向右键鼠标修饰符用来限制处理程序监听特定的滑鼠按键。常见的有:
能够用以下修饰符开启鼠标或键盘事件监听,使在按键按下时发生响应:
.ctrl
.alt
.shift
.meta
在Vue中能够经过config.keyCodes
自定义按键修饰符别名。例如,因为预先定义了keycode 116
(即F5
)的别名为f5
,所以在文字输入框中按下F5
,会触发prompt
方法,出现alert
。
<!-- HTML -->
<div id="app">
<input type="text" v-on:keydown.f5="prompt()">
</div>
Vue.config.keyCodes.f5 = 116;
let app = new Vue({
el: '#app',
methods: {
prompt: function() {
alert('我是 F5!');
}
}
});
复制代码
在Vue中,使用v-on
来给元素绑定事件,而为了更好的处理逻辑方面的事物,Vue提供了一个methods
。在methods
中定义一些方法,这些方法能够帮助咱们处理一些逻辑方面的事情。而在这篇文章中,咱们主要介绍了一些事件的修饰符,好比常见的阻止事件冒泡,键盘修饰符等。除此以外,还提供了config.keyCodes
提供自定义按键修饰符别名。
<a v-bind:href='url'></a>
<a :href='url'></a>
<a v-on:click='doSomething'></a>
<a @click='doSomething'></a>
复制代码
缩写后
咱们能够很直观的将一个复杂的页面分割成若干个独立组件,每一个组件包含组件的逻辑和样式,再将这些独立组件完成一个复杂的页面。这样既减小了逻辑复杂度,又实现了代码的重用。页面是组件的容器,组件自动组合造成完整的界面,当不须要某个组件时,或者想要替换某个组件时,能够随时进行替换和删除,而不影响整个应用的运行。
在vue中例如div、span均可以看作一个组件
通常写插件的时候全局组件使用的多一些
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>vue</title>
</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: '<button v-on:click="count++">You clicked me {{ count }} times.</button>' }) var vm = new Vue({ el: '#app' }) </script>
</html>
复制代码
props
组件的参数传递
slot
插槽在组件抽象设计中的应用
自定义事件
父子组件的通讯方式
使用基础 Vue 构造器,建立一个“子类”。参数是一个包含组件选项的对象。
data
选项是特例,须要注意 - 在 Vue.extend()
中它必须是函数
<div id="mount-point"></div>
复制代码
// 建立构造器
var demo = Vue.extend({
template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
data: function () {
return {
firstName: 'Walter',
lastName: 'White',
alias: 'Heisenberg'
}
}
})
// 建立 Profile 实例,并挂载到一个元素上。
new demo().$mount('#mount-point')
复制代码
参数:
{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
的做用。
模板
<div class="app">
<div ref="msgDiv">{{msg}}</div>
<div v-if="msg1">Message got outside $nextTick: {{msg1}}</div>
<div v-if="msg2">Message got inside $nextTick: {{msg2}}</div>
<div v-if="msg3">Message got outside $nextTick: {{msg3}}</div>
<button @click="changeMsg">
Change the Message
</button>
</div>
复制代码
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
的主要应用的场景及缘由。
created()
钩子函数进行的DOM操做必定要放在Vue.nextTick()
的回调函数中在created()
钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操做无异于徒劳,因此此处必定要将DOM操做的js代码放进Vue.nextTick()
的回调函数中。与之对应的就是mounted()
钩子函数,由于该钩子函数执行时全部的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 更新完成后就会调用。
Vue.set( target, propertyName/index, value )
{Object | Array} target
{string | number} propertyName/index
{any} value
this.myObject.newProperty = 'hi'
)注意对象不能是 Vue 实例,或者 Vue 实例的根数据对象。
<div id="div">
<p >{{items}}</p>
</div>
<script>
var vm = new Vue({
el:"#div",
data: {
items: ['a', 'b', 'c']
}
});
Vue.set(vm.items,2,"ling")
</script>
复制代码
Vue.set(vm.items,2,"ling") : 表示 把vm.items 这个数组的下标为2 的元素,改成"ling"
把数组 ["a","b","c"] 修改 后是 ["a","b","ling"]
<div id="div">
<p>{{person}}</p>
</div>
<script>
var vm = new Vue({
el:"#div",
data: {
person:{
name:"ling",
job:"engineer"
}
},
created:function(){
alert(this.person.age)
}
});
Vue.set(vm.person,"age","26")
</script>
复制代码
注意:person 是data 里面的子对象,因此可使用 Vue.set( ) 方法。data 这个根对象就不能使用 set 方法
说明:控制台能够在person 里找到age 这个属性,说明添加成功 (响应式)
vm.food="chocolate"
alert(vm.food)
控制台和网页上的 {{person}} 都没有显示food 这个属性,说明food 这个属性没有被添加 (非响应式)
Vue.delete( target, propertyName/index )
{Object | Array} target
{string | number} propertyName/index
仅在 2.2.0+ 版本中支持 Array + index 用法。
在 2.2.0+ 中一样支持在数组上工做。
data:{
namelist : {
id : 1,
name : '叶落森'
}
}
复制代码
// 删除name
delete this.namelist.name;//js方法
Vue.delete(this.namelist,'name');//vue方法
复制代码
容许你自定义过滤器,可被用于一些常见的文本格式化。过滤器能够用在两个地方:双花括号插值和 v-bind
表达式 (后者从 2.1.0+ 开始支持)。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示
一、在Vue中使用过滤器(Filters)来渲染数据是一种颇有趣的方式。
二、首先咱们要知道,Vue中的过滤器不能替代Vue中的methods
、computed
或者watch
,
三、过滤器不改变真正的data
,而只是改变渲染的结果,并返回过滤后的版本。
四、在不少不一样的状况下,过滤器都是有用的,好比尽量保持API响应的干净,并在前端处理数据的格式。
五、在你但愿避免重复和链接的状况下,它们也能够有效地封装成可重用代码块背后的全部逻辑。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>vue</title>
</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>
复制代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>vue</title>
</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>
复制代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>vue</title>
</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 的值做为第三个参数。
模板中只能有一个根元素
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+ 中已弃用
这种写法,习惯了element-ui的朋友必定就很熟悉了。
总结:
1 . 使用slot能够在自定义组件内插入原生HTML元素,须要搭配使用name和slot属性,不然多个slot可能会返回重复的HTML元素。
2 . 使用slot-scope能够将slot内部的做用域指向该子组件,不然默认做用域指向调用slot的父组件。
从 vue@2.6.x 开始,Vue 为具名和范围插槽引入了一个全新的语法,即咱们今天要讲的主角:
v-slot
指令。目的就是想统一slot
和scope-slot
语法,使代码更加规范和清晰。既然有新的语法上位,很明显,slot
和scope-slot
也将会在vue@3.0.x
中完全的跟咱们说拜拜了。而从vue@2.6.0
开始,官方推荐咱们使用v-slot
来替代后二者。
// 组件
Vue.component('lv-hello', {
template: ` <div> <slot name="header"></slot> <h1>个人天呀</h1> </div>`
})
new Vue({
el: '#app1',
data: {
}
});
复制代码
老版本
<div id="app1">
<!-- 老版本使用具名插槽 -->
<lv-hello>
<p slot="header">我是头部</p>
</lv-hello>
</div>
复制代码
新版本的变化
<!-- 新版本使用具名插槽 -->
<lv-hello>
<!-- 注意:这块的 v-slot 指令只能写在 template 标签上面,而不能放置到 p 标签上 -->
<template v-slot:header>
<p>我是头部</p>
</template>
</lv-hello>
</div>
复制代码
将
v-slot:
替换成#
号
<div id="app">
<lv-hello>
<template #header>
<p>我是头部</p>
</template>
<!-- 注意: #号后面必须有参数,不然会报错。即使是默认插槽,也须要写成 #default -->
<template #default>
<p>我是默认插槽</p>
</template>
</lv-hello>
</div>
复制代码
所谓做用域插槽,就是让插槽的内容可以访问子组件中才有的数据。
Vue.component('lv-hello', {
data: function () {
return {
firstName: '张',
lastName: '三'
}
},
template: ` <div> <slot name="header" :firstName="firstName" :lastName="lastName"></slot> <h1>个人天呀</h1> </div> `
})
复制代码
<div id="app">
<!-- 老版本使用具名插槽 -->
<lv-hello>
<p slot="header" slot-scope="hh">我是头部 {{ hh.firstName }} {{ hh.lastName }}</p>
</lv-hello>
<!-- 新版本使用具名插槽 -->
<lv-hello>
<!-- 注意:这块的 v-slot 指令只能写在 template 标签上面,而不能放置到 p 标签上 -->
<template v-slot:header="hh">
<p>我是头部 {{ hh.firstName }} {{ hh.lastName }}</p>
</template>
</lv-hello>
</div>
复制代码
:class 绑定的样式和class绑定的不冲突
<div v-bind:class="{ active: isActive }"></div>
复制代码
active
这个 class 存在与否将取决于数据属性isActive
的 布尔值
<div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }"></div>
复制代码
data: {
classObject: {
active: true,
'text-danger': false
}
}
复制代码
data: {
isActive: true,
error: null
},
computed: {
classObject: function () {
return {
active: this.isActive && !this.error,
'text-danger': this.error && this.error.type === 'fatal'
}
}
}
复制代码
<div v-bind:class="[activeClass, errorClass]"></div>
复制代码
data: {
activeClass: 'active',
errorClass: 'text-danger'
}
复制代码
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
复制代码
不过,当有多个条件 class 时这样写有些繁琐。因此在数组语法中也可使用对象语法:
<div v-bind:class="[{ active: isActive }, errorClass]"></div>
复制代码
模板内的表达式很是便利,可是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板太重且难以维护。例如:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>vue</title>
</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来进行处理。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>vue</title>
</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>
复制代码
计算属性能够依赖其余计算属性
计算属性不只能够依赖当前Vue 实例的数据,还能够依赖其余实例的数据
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>vue</title>
</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>
复制代码
每个计算属性都包含一个getter 和一个setter ,咱们上面的两个示例都是计算属性的默认用法, 只是利用了getter 来读取。
在你须要时,也能够提供一个setter 函数, 当手动修改计算属性的值就像修改一个普通数据那样时,就会触发setter 函数,执行一些自定义的操做
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>vue</title>
</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 都声明。
咱们能够将同一函数定义为一个方法而不是一个计算属性,两种方式的最终结果确实是彻底相同的。只是一个使用getMessage()取值,一个使用getMessage取值。
然而,不一样的是计算属性是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变时才会从新求值。
这就意味着只要 hi尚未发生改变,屡次访问 getMessage计算属性会当即返回以前的计算结果,而没必要再次执行函数。
<!DOCTYPE html>
<html> <head> <meta charset="utf-8" /> <title>vue</title> </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> 复制代码
一个对象,键是须要观察的表达式,值是对应回调函数。值也能够是方法名,或者包含选项的对象。Vue 实例将会在实例化时调用 $watch()
,遍历 watch 对象的每个属性。
为何必定要有watch,不用能够吗?咱们已经有了computed,能不能不去使用?
作一个实验
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>vue</title>
</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>
复制代码
不难发如今_异步的状况下就很差使用了_
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>vue</title>
</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:""
},
watch: { // 只有值变化的时候才会触发 支持异步了,其余状况咱们更善于使用
a(newVal,oldVal){ // watch的属性名字要和观察的人的名字一致
console.log(newVal);
console.log(oldVal);
}
},
})
</script>
</html>
复制代码
Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变更:侦听属性。当你有一些数据须要随着其它数据变更而变更时,你很容易滥用 watch
<!DOCTYPE html>
<html> <head> <meta charset="utf-8" /> <title>vue</title> </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
}
}
})
复制代码
是否是感受优雅不少
虽然计算属性在大多数状况下更合适,但有时也须要一个自定义的侦听器。这就是为何 Vue 经过 watch 选项提供了一个更通用的方法,来响应数据的变化。当须要在数据变化时执行异步或开销较大的操做时,这个方式是最有用的。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>vue</title>
</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>
复制代码
vm.$watch( expOrFn, callback, [options] )
观察 Vue 实例变化的一个表达式或计算属性函数。回调函数获得的参数为新值和旧值。表达式只接受监督的键路径。对于更复杂的表达式,用一个函数取代。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>vue</title>
</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>
复制代码
组件接受的选项之一 props 是 Vue 中很是重要的一个选项。父子组件的关系能够总结为: props down, events up 父组件经过 props 向下传递数据给子组件;子组件经过 events 给父组件发送消息。
好比咱们须要建立两个组件 parent 和 child。须要保证每一个组件能够在相对隔离的环境中书写,这样也能提升组件的可维护性。
这里咱们先定义父子两个组件和一个 Vue 对象
var childNode = {
template: `
<div>childNode</div>
`
};
var parentNode = {
template: `
<div>
<child></child>
<child></child>
</div>
`,
components: {
child: childNode
}
};
new Vue({
el: "#example",
components: {
parent: parentNode
}
});
复制代码
<div id="example">
<parent></parent>
</div>
复制代码
这里的 childNode 定义的 template 是一个 div,而且内容是"childNode"字符串。 而在 parentNode 的 template 中定义了 div 的 class 名叫 parent 而且包含了两个 child 组件。
组件实例的做用域是孤立的。这意味着不能(也不该该)在子组件的模板中直接引用父组件的数据。要让子组件使用父组件的数据,须要经过子组件的 props 选项。 父组件向子组件传递数据分为两种方式:动态和静态,这里先介绍静态方式。 子组件要显示的用 props 声明它指望得到的数据 修改上例中的代码,给 childNode 添加一个 props 选项和须要的
forChildMsg
数据; 而后在父组件中的占位符添加特性的方式来传递数据。
var childNode = {
template: ` <div> {{forChildMsg}} </div> `,
props: ["for-child-msg"] // 直接把参数做为数组放进去
};
var parentNode = {
template: ` <div> <p>parentNode</p> <child for-child-msg="aaa"></child> <child for-child-msg="bbb"></child> </div> `,
components: {
child: childNode
}
};
复制代码
命名规范
**
对于 props 声明的属性,在父组件的 template 模板中,属性名须要使用中划线写法; 子组件 props 属性声明时,使用小驼峰或者中划线写法均可以;而子组件的模板使用从父组件传来的变量时,须要使用对应的小驼峰写法。别担忧,Vue 可以正确识别出小驼峰和下划线命名法混用的变量,如这里的
forChildMsg
和for-child-msg
是同一值。
原则上很简单,for-child-msg做为一个变量
var parentNode = {
template: ` <div> <p>parentNode</p> <child :for-child-msg="childMsg1"></child> <child :for-child-msg="childMsg2"></child> </div> `,
components: {
child: childNode
},
data: function() {
return {
childMsg1: "child-1",
childMsg2: "child-2"
};
}
};
复制代码
在父组件的 data 的 return 数据中的 childMsg1 和 childMsg2 会被传入子组件中
验证传入的 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: "<div>{{forChildMsg}}</div>",
props: {
"for-child-msg": Number
}
};
let parentNode = {
template: ` <div class="parent"> <child :for-child-msg="msg"></child> </div>`,
components: {
child: childNode
},
data() {
return {
// 当这里是字符串 "123456"时会报错
msg: 123456
};
}
};
复制代码
还能够在 props 定义的数据中加入自定义验证函数,当函数返回 false 时,输出警告。 好比咱们把上述例子中的 childNode 的
for-child-msg
修改为一个对象,并包含一个名叫validator
的函数,该命名是规定叫validator
的,自定义函数名不会生效
let childNode = {
template: "<div>{{forChildMsg}}</div>",
props: {
"for-child-msg": {
validator: function(value) {
return value > 100;
}
}
}
};
复制代码
在这里咱们给for-child-msg
变量设置了validator
函数,而且要求传入的值必须大于 100,不然报出警告。
props 是单向绑定的:当父组件的属性变化时,将传导给子组件,可是不会反过来。这是为了防止子组件五一修改父组件的状态。
因此不该该在子组件中修改 props 中的值,Vue 会报出警告。
let childNode = {
template: `<div class="child"> <div> <span>子组件数据</span> <input v-model="forChildMsg"/> </div> <p>{{forChildMsg}}</p> </div>`,
props: {
"for-child-msg": String
}
};
let parentNode = {
template: ` <div class="parent"> <div> <span>父组件数据</span> <input v-model="msg"/> </div> <p>{{msg}}</p> <child :for-child-msg="msg"></child> </div>`,
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"
一般有两种缘由:
prop 做为初始值传入后,子组件想把它当作局部数据来用
prop 做为初始值传入后,由子组件处理成其余数据输出
定义一个局部变量,并用 prop 的值初始化它
可是因为定义的 ownChildMsg 只能接受 forChildMsg 的初始值,当父组件要传递的值变化发生时,ownChildMsg 没法收到更新。
let childNode = {
template: ` <div class="child"> <div> <span>子组件数据</span> <input v-model="forChildMsg"/> </div> <p>{{forChildMsg}}</p> <p>ownChildMsg : {{ownChildMsg}}</p> </div>`,
props: {
"for-child-msg": String
},
data() {
return { ownChildMsg: this.forChildMsg };
}
};
复制代码
这里咱们加了一个
用于查看 ownChildMsg 数据是否变化,结果发现只有默认值传递给了 ownChildMsg,父组件改变只会变化到 forChildMsg,不会修改 ownChildMsg。
因为是计算属性,因此只能显示值,不能设置值。咱们这里设置的是一旦从父组件修改了 forChildMsg 数据,咱们就把 forChildMsg 加上一个字符串"---ownChildMsg",而后显示在屏幕上。这时是能够每当父组件修改了新数据,都会更新 ownChildMsg 数据的。
let childNode = {
template: ` <div class="child"> <div> <span>子组件数据</span> <input v-model="forChildMsg"/> </div> <p>{{forChildMsg}}</p> <p>ownChildMsg : {{ownChildMsg}}</p> </div>`,
props: {
"for-child-msg": String
},
computed: {
ownChildMsg() {
return this.forChildMsg + "---ownChildMsg";
}
}
};
复制代码
let childNode = {
template: ` <div class="child"> <div> <span>子组件数据</span> <input v-model="forChildMsg"/> </div> <p>{{forChildMsg}}</p> <p>ownChildMsg : {{ownChildMsg}}</p> </div>`,
props: {
"for-child-msg": String
},
data() {
return {
ownChildMsg: this.forChildMsg
};
},
watch: {
forChildMsg() {
this.ownChildMsg = this.forChildMsg;
}
}
};
复制代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>vue</title>
</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
就能发现
el 和 data 并未初始化
完成了 data 数据的初始化,el没有
完成了 el 和 data 初始化
完成挂载
在console控制台中输入
app.message= 'hello!!';
复制代码
咱们在console里执行下命令对 vue实例进行销毁。销毁完成后,咱们再从新改变message的值,vue再也不对此动做进行响应了。可是原先生成的dom元素还存在,能够这么理解,执行了destroy操做,后续就再也不受vue控制了。
app.$destroy();
复制代码
能够在这加个loading事件,加载的动画
在这结束loading,还作一些初始化,实现函数自执行
在这发起后端请求,拿回数据,配合路由钩子作一些事情
你确认删除XX吗? destroyed :当前组件已被删除,清空相关内容
v-if
)v-show
)if操做的是dom show 操做的样式 若是频繁切换dom使用v-show,当数据一开时就肯定下来使用v-if更好一些,若是if经过内部指令不会执行了 只有dom从显示到隐藏 或者隐藏到显示 才能使用vue的动画
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>vue</title>
</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>
复制代码
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
被删除),在过渡/动画完成以后移除。<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>vue</title>
</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>
<!-- vue自定义的组件 -->
<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>
复制代码
赶上了多个transition的时候,同一个class确定是会冲突的,那么如何处理呢
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>vue</title>
</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>
<!-- vue自定义的组件 -->
<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-状态便可调用
Vue 也容许注册自定义指令。注意,在 Vue2.0 中,代码复用和抽象的主要形式是组件。然而,有的状况下,你仍然须要对普通 DOM 元素进行底层操做,这时候就会用到自定义指令。
举一个栗子:
<!DOCTYPE html>
<html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="app"> <div v-color='flag'>123</div> </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> 复制代码
出现如图状况
再来个栗子
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>vue</title>
</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>
复制代码
能够拖动
一个指令定义对象能够提供以下几个钩子函数 (均为可选):bind
:只调用一次,指令第一次绑定到元素时调用。在这里能够进行一次性的初始化设置。
inserted
:被绑定元素插入父节点时调用 (仅保证父节点存在,但不必定已被插入文档中)。
update
:所在组件的 VNode 更新时调用,可是可能发生在其子 VNode 更新以前。指令的值可能发生了改变,也可能没有。可是你能够经过比较更新先后的值来忽略没必要要的模板更新 (详细的钩子函数参数见下)。
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
钩子中可用。string
ref
被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs
对象上。若是在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;若是用在子组件上,引用就指向组件实例:<!-- `vm.$refs.p` will be the DOM node -->
<p ref="p">hello</p>
<!-- `vm.$refs.child` will be the child component instance -->
<child-component ref="child"></child-component>
复制代码
v-for
用于元素或组件的时候,引用信息将是包含 DOM 节点或组件实例的数组。$refs
也不是响应式的,所以你不该该试图用它在模板中作数据绑定。若是咱们用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
那么咱们在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('====================================');
}
})
复制代码