1、响应式(双向绑定)
2、组件化(模块化)
3、单文件组件(.vue)
- 将js(即功能)、css(即样式)、html(即模板)存在于一个文件内
- 得益于这些来解读成浏览器能识别的文件:Webpack + vue-loader
- 好处1:style的作用域
- 好处2:预加载器(在template、style中的lang属性)
也就是路由跳转时不会强制刷新
如果实例化组件时,用的是data对象,那所有用的都是同一份data
解决方案:
data () {
return {
// ...属性
}
}
1、全局Api
- Vue.component、Vue.extends等等
2、实例选项
- 在创建一个Vue实例对象是可以使用的功能性项目
vm.$options
可以获取这个组件的实例选项
3、实例属性、方法
- 把创建的那个Vue实例对象赋值给一个变量,该变量的属性、方法(一般有$)
4、指令
- 写在模板里面,例如v-html
5、内置组件
<component :is="组件名">component>
<transition>transition>
<keppl-alive>keep-alive>
<slot>slot>
也是要等到$set这样的响应式更新才可以监听到改变
1、kebab-case(短横线分割)
即全部改成小写,且在 转换处的字母前 加个
-
(首字母除外)
(1) 定义 子组件名
(2) 调用 子组件 、
(3) 使用 标签属性 (包括 v-on:)、
(4) is的属性值、
(5) 发射 emit事件
2、使用 驼峰写法:
(1) 引用 js变量 双向绑定、
(2) 在props中
例如:
在注册 子组件 时,
components: {
ComAHasChild
}
在<template>中引入 子组件 时,
<com-a-has-child></com-a-has-child>
// 或者:
//
因为事件名不会被用作一个JavaScript变量名或属性名,所以就没有理由使用camelCase(小驼峰)或PascalCase(大驼峰)了。并且 v-on
事件监听器在DOM模板中会被自动转换为全小写(因为HTML是大小写不敏感的),所 v-on:myEvent
将会变成 v-on:myevent
—— 导致 myEvent
不可能被监听到。
所以,推荐使用 kebab-case (短横线分割)事件名
// 1、数组
props: ['number-to-do', 'valueFromDad']
// 2、对象
props: {
// 单个类型
'numberToDo': Number,
// 多个类型
'valueFromDad': [Number, String],
// 任何类型
'anyType': null,
// 必填字符串
'requireString': {
type: 'String',
required: true
},
// 默认值数字
'defaultNum': {
type: Number,
default: 100
},
// 默认值对象也可以(自行查官方文档)
// 自定义验证函数
'myValid': {
validator (value) {
// 该值要是下列字符串中的其中一个。
// return 的东西,也就是为 当这个条件true时,能够验证成功的意思
return [ 'success', 'warning' ].indexOf(value) !== -1
}
}
}
// 无论如何,在HTML属性里,父组件传的都是字符串
// 例如:
// 除非在传值之前,加个v-bind
// 例如:
// 例如:
注意!
这些prop的验证是在 组件实例创建之前 就开始验证。所以,实例的属性,如data、computed等,在 default 或 validator 函数中是不可用的。
称为 “非Prop的特性”
inheritAttrs: false // 禁用特性继承
// 默认情况下父作用域的不被认作 props 的特性绑定 (attribute bindings) 将会“回退”且作为普通的 HTML 特性应用在子组件的根元素上。
$attrs
类似的 $listeners
上面说到,$attrs
是一个对象,它存着由父组件传给子组件的、没有在子组件里prop的“特性”(即没有prop的标签属性)
那如果组件A -> 组件B -> 组件C,这样组件C如何触发组件A的方法呢?
解决方法:$listeners
它存储的是,在父组件(即组件A)中,给第一个(即组件B)定义的所有方法的集合,如:
// 组件A:
<comp-b
@test1="onTest1" //此处监听了两个事件,可以在B组件或者C组件中直接触发
@test2="onTest2">
</comp-b>
// 则是事件test1、test2
// 组件B:
<comp-c v-bind="$attrs" v-on="$listeners"></comp-c>
// 通过v-bind="$attrs",即可向组件C传递那些从组件A给组件B的、且在B中没有prop的特征
// 通过v-on="$listeners",即可向组件C传递那些从组件A给组件B定义的事件,如test1、test2
// 组件C:
mounted () {
this.$emit('test1'); // 即可触发父组件的test1事件来执行OnTest1方法,多亏了组件B向组件C传递了$listeners
}
这样的缩写是,prop中, 传入一个对象的所有属性 的写法
有关 $listeners
的例子:http://www.jb51.net/article/132371.htm
// 传数字
<blog-post :count="42">blog-post>
// 传布尔值(prop没有值,默认true)
<blog-post favorited>blog-post>
<blog-post :facorited="false">blog-post>
// 传数组、对象同理,加个v-bind就好
Vue一般防止子组件改变父组件的状态,所以不应该在子组件内部改变prop
【只改子组件,不改父组件】
(1)data。(方便子组件改)
props: [ 'initialCounter' ],
data () {
return {
counter: this.initialCounter
}
}
(2)computed。
- 这种“改变”不太灵活,只能 “听父由命”,自己在子组件想改这个 prop属性 ,只能在 return 前修改一下返回的逻辑
props: [ 'size' ],
computed: {
normalizedSize () {
return this.size.trim().toLowerCase()
}
}
注意:对于一个数组或对象的prop来说,在子组件的改变会影响到父组件的状态
【改子组件,又改父组件】
(3).sync。
- sync修饰符的功能是:当一个子组件改变了prop值,这个变化也同步到父组件中所绑定。
"bar">
// 等价于:
"bar"
@update:foo="val => bar = val">
// 同样地,子组件也要在props中注册
props: [ 'foo' ]
// 当子组件需要更新foo时,它需要显式地触发一个更新事件:
this.$emit('update:foo', newVal);// 这里的newVal指的是新值
// 这样做的结果,子组件中foo的prop更新了,父组件的bar也更新了
出处:https://www.jianshu.com/p/6b062af8cf01
给普通的HTML标签监听一个事件,之后添加 .native
修饰符是 不起作用的
<button @click.native="say"> 按我 button>
// 无用
因为他是给某个组件的根元素上,监听一个事件的
<my-comp-a @click.native="saySomething"></my-comp-a>
// my-comp-a.vue:
Vue.component('my-comp-a', {
template: ``
})
// 这样做的结果,就是在子组件的根元素 button ,添加了一个事件,这个事件是用来触发在父组件中的 saySomething 方法的
// 写一个淡入淡出
<transition name="fade">
<p> text </p>
</transtion>
// css (.fade-leave不动,因为就是在那个位置)
.fade-enter-active, .fade-leave-active {
transition: opacity .5s ease-out; /* 第一个参数是要啥属性过渡 */
}
.fade-enter, fade-leave-active {
opacity: 0;
}
可以给
的 mode
来规定时间序列:
注意:因为vue 通常会复用已有元素(如同一种HTML标签),而不会重新渲染!
所以这样的情况下需要加个key
<transtion name="fade" mode="out-in">
<p v-if="show" key="1"> I am show p>
<p v-else key="2"> Not in show p>
transition>
各种钩子事件:
before-enter、before-leave、before-appear
enter、leave、appear
after-enter、after-leave、after-appear
enter-cancelled、leave-cancelled、appear-cancelled
局部注册:
- 在子组件里的实例选项中,
directives:{
指令名: {
}
}
全局注册:
- 在App.vue中,
Vue.directive('指令名', {
// ...指令选项...
})
bind:只执行一次,指令第一次绑定到元素时(因为是先绑元素,再插DOM)
inserted:被绑元素插入到父节点时
update:所在组件的VNode更新时
componentUpdated:指令所在组件的VNode及其子VNode全部更新后
unbind:一次,指令与元素解绑时
el:被绑定的DOM元素
binding:是一个对象
- name:指令名(不带 v-)
- rawName:完整指令名
- value:表达式的值
- expression:字符串形式的指令表达式(等号右边的所有东西)
- modifiers:
- arg:
vnode:DOM生成的虚拟节点
oldVnode:上一个虚拟节点
除 el 外,其他参数都应该是只读
如果需要在钩子之间共享数据,建议通过元素的 dataset 来进行。
如果想在bind、update这两个钩子都执行相同的函数,那就:
Vue.directive('both', function (el, binding) {
// ...
})
extend
扩展实例构造器 和 mixin
混入var myExtend = Vue.extend({
data () {
return {
// ...
}
},
methods: {}
})
// 直接 import 一个.vue的单文件组件即可
var vm1 = new myExtend({
el: '#root',
data () {
return {
// ...
}
},
methods: {}
})
extends
(常用)var vm1 = new Vue({
el: '#root',
// 为了便于扩展 单文件组件,直接 import 这个要被继承的单文件组件.vue 也可以
extends: myExtend
data () {
return {
// ...
}
},
methods: {}
})
混入 可以接受 对象数组,所以类似多继承。
- “混入对象” 可以包含任意的组件选项
- 当组件使用“混入对象”时,所有“混入对象”的选项都将被 适当地 混入(即合并)该组件本身的选项(类似于继承多个组件)
var myMixin = {
created () {
this.hello()
},
methods: {
hello () {
console.log('hi! I am from Mixin!')
}
}
}
// 直接 import 一个.vue的单文件组件即可
mixins
(常用)当想混入(即合并)到某个组件的时候,
var vm1 = new Vue({
el: '#root',
mixins: [ myMixin ],
data () {
return {
// ...
}
},
methods: {}
})
全局注册“混入对象”,将会影响到 所有 之后创建的 Vue实例。
Vue.mixin({
// created 钩子
created () {
// ...
}
})
只要是 继承,都有一定的合并规则:
例如 methods
、components
、directives
、data对象
等等
同一对象中,覆盖 冲突的属性。
最终展示的优先顺序:
该组件 > 混入对象(数组的最右最优) > Extend对象
例如 created
、mounted
等等
全部调用(只是顺序不同)。
调用优先顺序:
Extend对象 >混入对象(数组的最右最优) > 该组件
参考资料:http://mario.studio/vue-course/#/1?_k=i82q3b