Vue
https://cn.vuejs.org/v2/guide/
https://vue-loader.vuejs.org/
https://cli.vuejs.org/
Vue渲染效率更高,双向绑定,使用HTML、CSS、JavaScript原生语法(容易上手),React依赖于JSX,JSX是XML扩展而来,都使用VNode
请求数据模块
-
vue-resource(官方提供的请求插件)github —推荐使用
Vue插件使用:在main.js入口文件引入插件,然后Vue.use(plugin);
-
axios --github 哪都可以用react、Vue 、HTML,第三方插件,哪里用就哪里引入
-
fetch-jsonp
父子传递数据
- 传递数据也可以是方法(同react)、组件(传递this)
- 可以通过指定给子组件指定ref,父组件通过this. parent
- 可以使用插槽,子组件向父组件传值或者自定义事件
- 非父子组件可以使用广播的形式传递,新创建一个Vue实例专门使用 on监听进行传递(学了Vuex就不用了)
基本语法
<div id="app">
{{ message }}
</div>
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
指令
v-bind:title='message'
绑定元素title特性为Vue实例的messagev-if='message'
控制渲染,并且Vue再插入/更新/移除时自带比较好的过渡效果,可以配合v-else
/v-else-if
使用v-for='todo in todos'
,绑定数组数据循环渲染项目
<div id="app">
<li v-for='todo in todos'>{{todo.name}}</li>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
todos:[{
name:'forcehack',
age:18,
},
{
name:'bb',
age:19,
}
]
},
})
v-on:click='changeMessage'
,添加click事件,为实例的changeMessage方法
<div id="app">
<p>{{message}}</p>
<button v-on:click='changeMessage'>改变信息</button>
</div>
<script>
var app = new Vue({
el: '#app',
data: {message:'这是没改变前的信息!'},
methods:{
changeMessage(){
this.message='这是改变后的信息!';
}
}
})
</script>
- 通过
v-model='message'
,可以实现表单输入与应用状态的双向绑定 - 通过
v-once
属性指定只进行一次插值,数据改变插值处内容不会更新 - 通过
v-html='message'
渲染html
修饰符
修饰符是以.
指明的特殊后缀 ,用于告诉一个指令以特殊方式绑定
- .prevent修饰符告诉
v-on
指令触发事件时调用e.preventDefault()
- .stop阻止单击事件继续传播
- 修饰符可以串联多个使用.prevent.stop
- .capture添加事件监听器使用事件捕获模式
- .self只在e.target是注册事件时触发处理函数
- .once点击事件只触发一次
- .passive滚动事件的默认行为将会立即触发,而不等到’onScroll’完成,与prevent相对应,告诉浏览器不用判断是否阻止立即触发,可以提高流畅度
- 修饰符的顺序很重要
v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。
- .enter,只有按键是回车才会触发
<input v-on:keyup.enter="submit">
- 可以将KeyboardEvent.key暴露的任意有效按键名作为修饰符
<input v-on:keyup.page-down="onPageDown">
-
也支持keyCode,但是keyCode已经被遗弃,可能不被最新浏览器支持
扫描二维码关注公众号,回复: 10372976 查看本文章Vue提供了常用的按键码的别名
.enter
.tab
.delete
(捕获“删除”和“退格”键).esc
.space
.up
.down
.left
.right
-
可以通过全局的
Vue.config.keyCodes
自定义按键修饰符别名
Vue.config.keyCodes.f1 = 112
- 可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器。(同时)
.ctrl
.alt
.shift
.meta
- 添加.exact修饰符精确匹配(不能按其他键,只能按相应的键)
- 鼠标按钮修饰符
- left
- right
- middle
组件化应用构建
- 通过
Vue.component('component',{})
创建组件
<div id="app">
<ol>
<todo-item
v-for='item in message'
v-bind:todo='item'
v-bind:key='item.id'></todo-item>
</ol>
</div>
<script>
// 创建组件,接收todo参数
Vue.component('todo-item', {
props: ['todo'],
template: '<li>{{todo.name}}</li>'
})
var app = new Vue({
el: '#app',
data: {
message: [
{ id: 0, name: 'aaa', },
{ id: 1, name: 'bbb', },
{ id: 2, name: 'ccc', }
]
},
})
</script>
Vue实例
- 一个组件就是一个Vue实例,只有创建实例时就存在data的属性才是响应式的
- 通过
Object.freeze()
可以阻止修改现有属性 - Vue实例还暴露一些实例属性和方法,用$与用户属性区分
$data
:当前状态$options.data()
初始状态
var data = { a: 1 }
var vm = new Vue({
el: '#example',
data: data
})
vm.$data === data // => true
vm.$el === document.getElementById('example') // => true
// $watch 是一个实例方法
vm.$watch('a', function (newValue, oldValue) {
// 这个回调将在 `vm.a` 改变后调用
})
实例生命周期钩子
- created,实例被创建之后执行
生命周期钩子this指向实例,不要在选项属性或者回调上使用箭头函数
- beforeCreate
- created
- beforeMount
- mounted
- beforeUpdate
- updated
- beforeDestory
- destroyed
模板语法
- 双括号会将数据解析为文本,如果想要渲染为html,使用
v-html='message'
,但是需要谨防xss攻击 - *如果HTML特性通过
v-bind:disabled='message'
如果message为null、undefined、false,那么特性不会被包含在元素中并渲染出来 - 在{{}}中使用JavaScript只能包含单个表达式,不能为多个或者语句
- 可以使用[]实现动态参数
<span v-bind:[attr]='message'>{{ok?'这是ok的':'这是不ok'}}</span>
需要注意的是,再[]中使用表达式会发出编译警告,以及不能使用大写的动态属性,因为浏览器会自动将属性转化为小写
缩写
v-bind
和v-on
可以进行缩写
v-bind的缩写
<span :href='message' ></span>
//不适用缩写
v-bind:href='message'
v-on的缩写
<span @click='changeMessage' ></span>
//不使用缩写
<span v-on:click='changeMessage' ></span>
计算属性和侦听器
计算属性
模板内的表达式应该尽量简单,不应该使用大量的计算表达式,对于复杂逻辑应该使用计算属性
<span >{{message.split('').reverse().join('')}}</span>
使用Vue实例的computed处理
<div id="app">
<span >{{reversemessage}}</span>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message:'123',
ok:false,
},
computed:{
// getter
reversemessage(){
return this.message.split('').reverse().join('');
}
}
})
也可以使用方法进行代替
<div id="app">
<span >{{reversemessage()}}</span>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message:'123',
ok:false,
},
methods:{
reversemessage(){
return this.message.split('').reverse().join('');
}
}
})
</script>
可以达到相同的效果,但是与computed不同的时,computed会进行缓存,只要computed使用的依赖没有改变就不会触发,方法每次一定会调用
setter
计算属性默认也可以添加setter
<div id="app">
<span>{{reversemessage}}</span>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message: '123',
ok: false,
},
computed: {
reversemessage: {
set(newValue) {
this.message=''+newValue;
},
get() {
return this.message.split('').reverse().join('');
}
}
}
})
</script>
监听
计算属性常常已经能满足要求,但是有时需要监听属性,Vue的监听通过watch选项进行监听,也可以通过实例的vm.$watch
<div id="app">
<input type="text" v-model='message'>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message: '123',
},
// 添加监听器,message改变的时候打印
watch:{
message(){
console.log('这是监听器打印的结果');
}
},
})
</script>
class与style绑定
- 使用
v-bind:class='active:isactive'
进行绑定,通过isactive决定是否使用active,也可以写在data或者计算属性里
<div id="app">
<div v-bind:class='activeObj'></div>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
isactive: false,
},
computed:{
activeObj(){
return {
active:this.isactive,
}
},
}
})
</script>
也可以使用数组或者对象语法
-
使用
v-bind:style=''
绑定style,传入应该是一个对象(通常写在data里更好),也可以传入数组添加多个style对象 -
Vue会自动为css添加前缀
-
可以使用数组为一个属性添加多个值,常用于提供多个带前缀的值
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
只会渲染一个被浏览器支持的值。
条件渲染
v-if
渲染多分组可以将v-if
放在template标签v-show
也可以控制渲染,但是不是真正的条件渲染,只是普通的css样式控制,v-if
有更高的切换开销,v-show有更高的初始渲染开销
,如果频繁的切换样式,推荐使用v-show
- 不推荐
v-for
和v-if
同时使用(在同一元素上),如果两者同时使用v-for
具有比v-if
更高的优先级(vue-cli默认的eslint规则会报错)
列表渲染
-
可以使用of代替in
-
可以使用
v-for
遍历对象,第一个参数为属性值,第二个为属性名,第三个参数为索引值 -
在使用
v-for
使尽可能指定key(不要使用index作为key) -
被监听的数组执行数组操作时也会进行视图更新
-
Vue不能检测以下数组变动
- 利用索引值直接设置一个数组项
- 修改数组的长度
第一类问题可以使用
vm.$set
或者Vue.Set
Vue.Set(items,index,newValue)
第二类问题可以使用splice
vm.items.splice(newLength)
-
Vue也不能检测对象属性的添加,也可以使用
Vue.Set(item,property,value)
或者vm.$set(item,property,value)
进行添加 -
在
v-for
里还可以添加值范围可以将模板重复对应次数
事件处理
- 有时需要访问访问原始的DOM事件,可以用特殊的$event变量传入方法
<div id="app">
<button v-on:click='changeMessage($event)'>点击获取事件对象</button>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message:'this is messages!',
},
methods:{
changeMessage(event){
console.log(event);
}
},
})
</script>
表单输入绑定
- 输入法组合文字过程不会进行更新,如果需要可以使用input事件
- v-model修饰符
- .lazy在change时渲染而不是input
- .number将用户输入的值转化为number类型
- .trim自动过滤首尾空白字符
组件基础
- 组件也是Vue实例,所以组件也有实例的各种属性和方法,除了el这种根实例特有的选项
- 组件的data必须是个函数,返回数据对象
- 通过$emit触发自定义事件
<button v-on:click="$emit('enlarge-text')">Enlarge text
</button>
- 可以通过第二个参数抛出值,父组件中通过$.event获取,如果事件处理是一个函数,那么通过第一个参数接收
- v-model用于组件
普通的v-model
<input v-model="searchText">
//相当于
<input
v-bind:value="searchText"
v-on:input="searchText = $event.target.value"
>
组件的v-model相当于
<custom-input
v-bind:value="searchText"
v-on:input="searchText = $event"
></custom-input>
所以要让他工作
<div id="app">
<custom-input v-model='message'></custom-input>
</div>
<script>
Vue.component('custom-input', {
//定义input事件以及将value传递出去
props: ['value'],
template: `<input type='text'
v-bind:value='value'
v-on:input="$emit('input',$event.target.value)"
>`
})
var app = new Vue({
el: '#app',
data: {
message: 'this is messages!',
},
methods: {
changeMessage(event) {
alert(123);
}
},
})
</script>
深入了解组件
- 推荐自定义组件名小写,且使用连字符(与已有组件进行区分)
- 使用Vue.component注册的是全局组件,在实例里边通过components定义的是局部组件
const ComponentA={
};
const ComponentB={
};
const app=new Vue({
el:'#app',
components:{
'components-a':ComponentA,
'components-b':ComponentB,
}
})
这样定义的组件在子组件中是不可用的,如果需要则进行嵌套关系
<script>
const ComponentA={
template:'<span>这是组件A</span>',
components:{
'components-b':ComponentB,
}
};
const ComponentB={
template:'<span>这是组件B</span>',
};
const app=new Vue({
el:'#app',
components:{
'components-a':ComponentA,
}
})
</script>
- 组件中的prop为大写的情况时,HTML中需小写加短横线的形式,因为HTML对大小写不敏感,字符串模板不存在这个限制
- props可以为对象的形式,指定接收参数的类型,也可以指定默认值,自定义验证等(prop会在实例创建前beforeCreate进行验证)
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // or any other constructor
}
Vue.component('my-component', {
props: {
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})
type还可以为自己创建的构造函数
- 使用v-bind传递静态属性,可以告诉Vue传递的是一个表达式,而不是字符串
- 非prop特性会添加到组件的根元素(不是整个实例的根元素)上
- 在组建内定义
inheritAttrs:false
禁用继承非prop属性,可以配合v-bind='$attrs'
进行配合继承。
- 在组建内定义
- 始终推荐小写分隔符的方式进行命名(自定义事件也如此)
- 一个组件的v-model默认为value和input事件,但是像单选框、复选框等输入控件会将value用于不同目的,这个时候就要自定义v-model,通过model选项进行配置
<div id="app">
<custom-input v-model='message'></custom-input>>
</div>
<script>
Vue.component('coustom-input',{
model:{
prop:"checked",
event:"change",
},
props:{
checked:Boolean,
},
template:`
<input type="checkbox" v-bind:checked="checked" v-on:change="$emit('change',$event.target.checked)"></input>
`
})
const app=new Vue({
el:'#app',
data:{
message:'forcehack',
}
})
</script>
这样v-model就会传入名为checked的prop,并指定change事件触发
- 将原生事件绑定到组件可以使用v-on加.native的方式(不然自定义事件没有原生事件),如果现在在尝试监听一个类似的非常特定的元素时,可能会出现一些问题(如果组件的顶层不是input),这个时候就要使用$listeners将所有事件监听器指向这个组件的某个特定子元素
<div id="app">
<custom-input v-model='message' v-on:focus.nactive="onFocus"></custom-input>
</div>
<script>
Vue.component('custom-input', {
props:["value"],
computed:{
getAllListeners(){
let vm=this;
return Object.assign({},this.$listeners,{
input:function(event){
vm.$emit("input",event.target.value)
}
})
}
},
template: `
<input type="text" v-bind="$attrs" v-bind:value="value" v-on="getAllListeners"></input>
`
})
const app = new Vue({
el: '#app',
data: {
message: 'forcehack',
},
methods: {
onFocus() {
console.log('focus');
}
},
})
</script>
- 在有些情况下,我们可能需要对一个prop进行"双向绑定"(既传递又进行更新)。使用
update:myPropName
代替,在一个包含title的组件中。
this.$emit('update:title', newTitle)
父组件:
<text-document
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
></text-document>
可以缩写为:
<text-document v-bind:title.sync="doc.title"></text-document>
插槽
- 如果想显示组件内部内容时可以使用插槽
<div id="app">
<slot-example v-bind:url='message'>这是插槽要显示的内容</slot-example>
</div>
<script>
Vue.component('slot-example',{
props:["url"],
template:`
<a v-bind:href="url">
<slot></slot>
</a>
`
})
const app=new Vue({
el:"#app",
data:{
message:'/profile'
}
})
</script>
- 后备内容,当没有传递内容时使用
<slot>没有传递时使用我这个</slot>
- 可以配合v-slot和template使用具名插槽,v-slot新版本只能使用在template中
<div id="app">
<slot-example v-bind:url='message'>
<template v-slot:header>
<h1>这是name为header的slot</h1>
</template>
<template v-slot:default>
<h1>这是默认插槽</h1>
</template>
<template v-slot:footer>
<h1>这是name为footer的插槽</h1>
</template>
</slot-example>
</div>
<script>
Vue.component('slot-example', {
template: `
<div>
<header>
<slot name="header"></slot>
</header>
<main>
<slot ></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
`
})
const app = new Vue({
el: "#app",
data: {
message: '/profile'
}
})
</script>
- 有时候想让插槽能够访问子组件的内容,可以通过子组件v-bind,父组件为v-slot命名的方式传递(作用域插槽)
<div id="app">
<current-user >
<template v-slot:default="slotProps">
{{ slotProps.user.firstname }}
</template>
</current-user>
</div>
<script>
Vue.component("current-user", {
data(){
return {
user:{
firstname:"这是姓",
lastname:"这是名",
},
}
},
template: `
<span>
<slot v-bind:user="user">{{ user.lastname }}</slot>
</span>
`,
})
const app=new Vue({
el:'#app',
})
</script>
- 当只有默认插槽时可以缩写为v-slot=“slotProps”,并且可以放在组件标签,如果有多个插槽则只能使用多个template
<div id="app">
<current-user v-slot:default="slotProps" >
{{ slotProps.user.firstname }}
</current-user>
</div>
<script>
Vue.component("current-user", {
data(){
return {
user:{
firstname:"这是姓",
lastname:"这是名",
},
}
},
template: `
<span>
<slot v-bind:user="user">{{ user.lastname }}</slot>
</span>
`,
})
const app=new Vue({
el:'#app',
})
</script>
- 插槽内部其实就是用函数包括,传参进入,所以如果支持的情况下可以使用解构获取值
<template v-slot:default="{user}">
{{user.firstname}}
</template>
- 插槽名也可以是动态的
<template v-slot:[dynamicName]="{user}">
{{user.firstname}}
</template>
- v-slot也可以缩写为#header、#footer,default不能缩写为单个#,需写为#default
动态组件和异步组件
- 可以通过component内置组件和v-bind:is切换组件
<div id="app">
<button v-on:click="changeComponents('a')">组件A</button>
<button v-on:click="changeComponents('b')">组件B</button>
<button v-on:click="changeComponents('c')">组件C</button>
<component v-bind:is='currentView'></component>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
currentView: 'component-a',
},
methods: {
changeComponents(o) {
this.currentView='component-'+o;
}
},
components: {
'component-a': {
template:`<span>这是组件A</span>`
},
'component-b': {
template:`<span>这是组件B</span>`
},
'component-c': {
template:`<span>这是组件C</span>`
},
}
})
</script>
- 可以使用keep-alive包裹进行缓存
<keep-alive >
<component v-bind:is='currentView'></component>
/keep-alive>
- 异步组件就是定义的时候什么都不做,在需要的时候进行引入(并且进行缓存),官方推荐结合webpack的代码分割功能进行按需加载,Vue允许使用工厂函数的形式定义
Vue.component('async-example', function (resolve, reject) {
setTimeout(function () {
// 向 `resolve` 回调传递组件定义
resolve({
template: '<div>I am async!</div>'
})
}, 1000)
})
与webpack的代码分割结合使用
Vue.component('async-webpack-example', function (resolve) {
// 这个特殊的 `require` 语法将会告诉 webpack
// 自动将你的构建代码切割成多个包,这些包
// 会通过 Ajax 请求加载,请求成功后会进行resolve
require(['./my-async-component'], resolve)
})
结合ES6返回一个promise,import函数实现动态加载,返回一个Promise(使其能够有require一样的动态加载效果),区别于import引入
require是运行时加载,是一种动态加载方式,import是编译时加载,无法达到require一样的动态加载效果,但是import函数可以实现动态加载,返回一个promise
Vue.component(
'async-webpack-example',
// 这个 `import` 函数会返回一个 `Promise` 对象。
() => import('./my-async-component')
)
处理加载状态(加载失败或者正在加载)
const AsyncComponent = () => ({
// 需要加载的组件 (应该是一个 `Promise` 对象)
component: import('./MyComponent.vue'),
// 异步组件加载时使用的组件
loading: LoadingComponent,
// 加载失败时使用的组件
error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
})
处理边界情况(迫不得已才使用的)
- 访问根组件实例使用$root
- 访问父组件实例$parent
- 如果需要直接访问子组件实例,可以通过给子组件指定ref="username"属性,父组件通过this.$refs.username访问
- 如果出现$parent无法扩展到更深层次时(或者说很麻烦)可以使用provide/inject,provide可以指定想要提供给后代的属性、方法(可以看作大范围的prop传播,并且父组件不需要知道子组件哪个需要)
provide: function () {
return {
getMap: this.getMap
}
}
后代通过inject指定接收
inject: ['getMap']
需要注意的是,这是非响应式的,并且会让你的项目重构非常困难
- 手动监听事件
- 通过
$on(eventName, eventHandler)
侦听一个事件 - 通过
$once(eventName, eventHandler)
一次性侦听一个事件 - 通过
$off(eventName, eventHandler)
停止侦听一个事件
- 通过
比如有下列需求
// 一次性将这个日期选择器附加到一个输入框上
// 它会被挂载到 DOM 上。
mounted: function () {
// Pikaday 是一个第三方日期选择器的库
this.picker = new Pikaday({
field: this.$refs.input,
format: 'YYYY-MM-DD'
})
},
// 在组件被销毁之前,
// 也销毁这个日期选择器。
beforeDestroy: function () {
this.picker.destroy()
}
可以使用如下策略
mounted: function () {
var picker = new Pikaday({
field: this.$refs.input,
format: 'YYYY-MM-DD'
})
this.$once('hook:beforeDestroy', function () {
picker.destroy()
})
}
每个实例有自己的清理方法
-
循环引用组件的解决方案
- 如果是在全局注册的组件,不会有问题
- 将产生悖论的组件在生命钩子beforeCreate时注册它
- 或者使用webpack异步加载这个组件
beforeCreate: function () { this.$options.components.TreeFolderContents = require('./tree-folder-contents.vue').default }
components: { TreeFolderContents: () => import('./tree-folder-contents.vue') }
-
当在组件中添加inline-template属性时它会被当作模板使用,但不推荐这么使用,推荐使用内置的template(会定义在Vue所属DOM内)
-
也可以script添加text/x-template类型,定义,然后使用id进行引用(会定义在Vue所属DOM外),不推荐会将模板与该组件的其他定义分离开
<script type="text/x-template" id="hello-world-template">
<p>Hello hello hello</p>
</script>
Vue.component('hello-world', {
template: '#hello-world-template'
})
- 大多数情况下不不能更新时代码出现了问题,如果非要进行强制更新使用$forceUpdate
- 通过添加v-once创建低开销的组件,但是谨慎使用
过渡和动画
- Vue提供transition组件,可以自动检测添加过渡
<transition name="fade">//没有添加name会使用默认前缀v-
<p v-if="show">hello</p>
</transition>
.fade-enter-active, .fade-leave-active {
transition: opacity .5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
}
-
过渡的类名
- v-enter:过渡的开始,插入前
- v-enter-active:整个过渡阶段
- v-enter-to:进入过渡的结束阶段,与此同时v-enter被移除
- v-leave:离开的开始
- v-leave-active:整个离开过程
- v-leave-to:离开过渡的结束阶段,与此同时v-leave被移除
-
动画过渡同css,不同的是,v-enter在animationend事件触发时删除
-
自定义类名可以添加下列属性定义
enter-class
enter-active-class
enter-to-class
(2.1.8+)leave-class
leave-active-class
leave-to-class
(2.1.8+)
-
如果同时使用过渡和动画可以使用type指定需要监听的类型,可以指定为animation或transition(没懂)
-
通常情况下Vue会等待在过渡的根元素的第一个anmationend或transitionend事件,但是有时一些内部元素需要更长的时间时,可以显示指定:duration
<transition :duration="{ enter: 500, leave: 800 }">...</transition>
- 过渡的JavaScript钩子
<transition
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:after-enter="afterEnter"
v-on:enter-cancelled="enterCancelled"
v-on:before-leave="beforeLeave"
v-on:leave="leave"
v-on:after-leave="afterLeave"
v-on:leave-cancelled="leaveCancelled"
>
<!-- ... -->
</transition>
// ...
methods: {
// --------
// 进入中
// --------
beforeEnter: function (el) {
// ...
},
// 当与 CSS 结合使用时
// 回调函数 done 是可选的
enter: function (el, done) {
// ...
done()
},
afterEnter: function (el) {
// ...
},
enterCancelled: function (el) {
// ...
},
// --------
// 离开时
// --------
beforeLeave: function (el) {
// ...
},
// 当与 CSS 结合使用时
// 回调函数 done 是可选的
leave: function (el, done) {
// ...
done()
},
afterLeave: function (el) {
// ...
},
// leaveCancelled 只用于 v-show 中
leaveCancelled: function (el) {
// ...
}
}
当只用JavaScript过渡时,在enter和leave中必须使用done回调(否则他们会同步调用,立即完成过渡),当只使用JavaScript过渡的元素添加v-bind:css=“false”,Vue会跳过css检测(性能优化)
- 可以使用Velocity 和 jQuery.animate使用JavaScript过渡 ,工作方式类似,也是用来实现 JavaScript 动画的一个很棒的选择
- 初始渲染的过渡使用appear(第一次渲染时的过渡)
<transition appear>
<!-- ... -->
</transition>
这里默认和进入/离开过渡一样,同样也可以自定义 CSS 类名。
<transition
appear
appear-class="custom-appear-class"
appear-to-class="custom-appear-to-class" (2.1.8+)
appear-active-class="custom-appear-active-class"
>
<!-- ... -->
</transition>
自定义 JavaScript 钩子:
<transition
appear
v-on:before-appear="customBeforeAppearHook"
v-on:appear="customAppearHook"
v-on:after-appear="customAfterAppearHook"
v-on:appear-cancelled="customAppearCancelledHook"
>
<!-- ... -->
</transition>
- 多组件过渡如果内部有相同元素时,用唯一的key进行区分
- 默认过渡离开和进入是同时发生的,但是有时不想这样,可以设置过渡模式mode
- in-out:新元素先过渡,完成后当前元素过渡离开
- out-in:当前元素先过渡,完成后新元素过渡进入
- 多个组件的过渡可以使用动态组件
- 如果同时渲染整个列表,比如v-for的情况下使用过渡,可以使用transition-group组件,其是以一个真实元素呈现,默认为span,可以通过tag指定,不能使用过渡模式,内部元素都要指定唯一的key,过渡会应用到内部,而不是容器本身
- 列表要实现单个元素的过渡模式,可以使用v-move特性(因为列表没有过渡模式in-out、out-in),内部元素实现平滑的过渡
- 可复用的过渡组件只需将transition或者transition-group作为根组件即可
//使用template
Vue.component('my-special-transition', {
template: '\
<transition\
name="very-special-transition"\
mode="out-in"\
v-on:before-enter="beforeEnter"\
v-on:after-enter="afterEnter"\
>\
<slot></slot>\
</transition>\
',
methods: {
beforeEnter: function (el) {
// ...
},
afterEnter: function (el) {
// ...
}
}
})
- 还可以通过方法通过JS实现动态的过渡(绑定name)
可复用性和组合
- 可以定义混合来分发Vue组件的可复用功能
const myMixin={
created () {
this.hello();
},
methods: {
hello(){
console.log("这是可复用的方法");
},
},
};
// 定义可复用组件
const Component=Vue.extend({
mixins: [myMixin],
})
// 使用组件
const component=new Component();//这是可复用的方法
- 当混入对象与组件有同名数据冲突时会进行合并,发生冲突时组件数据优先,同名钩子函数都会被调用,并且混入对象先调用
- 值为对象的选项,比如methods、components将被合并到一个对象,组件对象优先
- 混入也可以进行全局注册,使用
Vue.mixin({})
但需谨慎使用,因为一旦创建会影响之后的每一个创建的Vue实例,每个实例都会有这个混入 - 如果想自定义混入选项合并可以向
Vue.config.optionMergeStrategies
添加一个函数
Vue.config.optionMergeStrategies.myOption = function (toVal, fromVal) {
// 返回合并后的值
}
自定义指令
- Vue允许自定义指令,使用
Vue.directive
,也可以在实例内通过directives属性进行局部注册 - 钩子函数
- bind,只调用一次,第一次绑定到元素时调用
- inserted,被绑定元素插入到父节点时调用
- update,所在组件更新时调用
- componentUpdated,指令所在组件及字节点全部更新时调用
- unbind,只调用一次,指令与元素解绑时调用
Vue.directive('focus', {
inserted(){
console.log("插入父节点时调用");
},
bind(){
console.log("第一次被绑定到元素时调用");
},
update(){
console.log("更新时调用,但不保证子节点也更新了");
},
componentUpdated(){
console.log("组件及子节点全部更新时调用");
},
unbind(){
console.log("指令解绑时调用");
},
})
const app=new Vue({
el:'#app',
created () {
console.log("这是组件自己的方法");
}
})
-
钩子函数的参数
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 }
。
vnode
:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。oldVnode
:上一个虚拟节点,仅在update
和componentUpdated
钩子中可用。
-
自定义指令参数可以是动态的,那么就可以使用这个参数做一些事
v-mydirective:[argument]="value"
- 如果想在bind和update时做相同的事(前提是只关心这两个钩子),可以直接将指令写成函数形式
Vue.directive('color-swatch', function (el, binding) {
el.style.backgroundColor = binding.value
})
- 指令value能够接收所有合法的表达式
<div v-demo="{ color: 'white', text: 'hello!' }"></div>
渲染函数和jsx
- 有些时候我们可能需要用到函数式的渲染,使用是实例的render进行指定
<div id="app">
<slot-example v-slot:default v-bind:level='1'>这是slot</slot-example>
</div>
<script>
Vue.component('slot-example',{
render: function (createElement, context) {
return createElement('h'+this.level,this.$slots.default);
},
props: {
level:Number,
required:true
},
});
const app=new Vue({
el:'#app',
})
</script>
- createElement返回的不是真实DOM,而是虚拟DOM,称为VNode
- createElement参数对象,第一个可以为一个HTML标签名、组件选项对象,或者resolve了上边任意一种的async函数,第二个参数是与组件相对应的数据对象(可选),第三个为子级虚拟节点,由createElement生成,可以为字符串,可以为数组
- 数据对象(创建VNode时可以使用里边的数据进行创建)
{
// 与 `v-bind:class` 的 API 相同,
// 接受一个字符串、对象或字符串和对象组成的数组
'class': {
foo: true,
bar: false
},
// 与 `v-bind:style` 的 API 相同,
// 接受一个字符串、对象,或对象组成的数组
style: {
color: 'red',
fontSize: '14px'
},
// 普通的 HTML 特性
attrs: {
id: 'foo'
},
// 组件 prop
props: {
myProp: 'bar'
},
// DOM 属性
domProps: {
innerHTML: 'baz'
},
// 事件监听器在 `on` 属性内,
// 但不再支持如 `v-on:keyup.enter` 这样的修饰器。
// 需要在处理函数中手动检查 keyCode。
on: {
click: this.clickHandler
},
// 仅用于组件,用于监听原生事件,而不是组件内部使用
// `vm.$emit` 触发的事件。
nativeOn: {
click: this.nativeClickHandler
},
// 自定义指令。注意,你无法对 `binding` 中的 `oldValue`
// 赋值,因为 Vue 已经自动为你进行了同步。
directives: [
{
name: 'my-custom-directive',
value: '2',
expression: '1 + 1',
arg: 'foo',
modifiers: {
bar: true
}
}
],
// 作用域插槽的格式为
// { name: props => VNode | Array<VNode> }
scopedSlots: {
default: props => createElement('span', props.text)
},
// 如果组件是其它组件的子组件,需为插槽指定名称
slot: 'name-of-slot',
// 其它特殊顶层属性
key: 'myKey',
ref: 'myRef',
// 如果你在渲染函数中给多个元素都应用了相同的 ref 名,
// 那么 `$refs.myRef` 会变成一个数组。
refInFor: true
}
- VNode必须唯一,如果要创建重复的VNode可以使用
Array.apply(null,{length:20}).map
的形式多次创建 - 模板函数中的v-if和v-for可以轻松使用if和map完成
- 模板中的v-model也需要自己实现
Vue.component('custom-input', {
props: ['value'],
render: function (createElement) {
var self = this;
return createElement('input', {
domProps: {
value: self.value
},
on: {
input: function (event) {
self.$emit('input', event.target.value)
}
}
})
}
});
- 事件修饰符,Vue提供了相应的前缀使用
修饰符 | 前缀 | |||
---|---|---|---|---|
.passive | & | |||
.capture | ! | |||
.once | ~ | |||
.capture .once或者.once.capture | ~! |
on: {
'!click': this.doThisInCapturingMode,
'~keyup': this.doThisOnce,
'~!mouseover': this.doThisOnceInCapturingMode
}
其他修饰符都可以使用JavaScript完成
- 可以通过this. scopedSlots访问作用域插槽,每个作用域插槽都是绑定若干VNode的函数,简单的说作用域插槽就是子组件可以向父组件传递数据的插槽,可以通过向函数传递数据到底向父组件传递数据的目的
<div id="app">
<slot-example v-slot:default='{text}'>{{text}}</slot-example>
<!-- <slot-example v-slot:header>这是header插槽</slot-example> -->
</div>
<script>
Vue.component('slot-example', {
data () {
return {
message:'这是默认插槽',
}
},
render: function (createElement) {
return createElement('div', [this.$scopedSlots.default({
text:this.message
})])
}
});
- 如果想向子组件传递作用域插槽可以通过子组件的VNode数据对象的scopedSlots字段(不懂)
render: function (createElement) {
return createElement('div', [
createElement('child', {
// 在数据对象中传递 `scopedSlots`
// 格式为 { name: props => VNode | Array<VNode> }
scopedSlots: {
default: function (props) {
return createElement('span', props.text)
}
}
})
])
}
- 函数式组件(类似react的function),没有管理任何状态,也没有监听任何传递给它的状态,也没有生命周期,只接收prop,这样的组件我们可以使用函数组件,为组件添加funtional字段,函数式组件的开销是小的,之后一切参数通过context传递
Vue.component('slot-example', {
functional:true,
render: function (createElement,context) {
return createElement('div',context.data,[context.scopedSlots.default({
text:123
})]);
}
});
通过向createElement传入context.data作为第二个参数,就可以把所有特性和事件监听都传递下去
过滤器
- Vue可以自定义过滤器,使用|符号分割,只能使用在v-bind或者双括号中
- 过滤器可以全局注册(Vue.filter)也可以局部注册(使用实例filter属性定义)
<div id="app">
<input type="text" v-bind:value='message | myfilter' v-on:input='message = $event.target.value'>
</div>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'forcehack',
},
filters: {
myfilter(value) {
console.log(value);
return value.charAt(0).toUpperCase() + value.slice(1);
},
},
})
</script>
全局指令
this.$nextTick()
Dom更新之后执行回调