vue.js中级进阶
组件注册 Vue.component()
组件名大小写
- 使用kebab-case:小写和连字符
-
; - 使用PascalCase:驼峰命名。
强烈建议,使用kebab-case命名。
当驼峰命名的首字母大写时,两种方法都能引用生效,都接受;
直接在DOM中使用时,只有kebab-case生效;
全局注册 和 局部注册
- 作用域不同,局部注册在自己子组件内不可用
- 打包时,全局会被打包,即使没有被使用
因此,将component
赋值变量,再组件components
内引用,实现复用。
模块系统
- 使用Webpack和Babel的模块系统时,推荐把各组件文件夹放在
components
根目录中,通过import ComponentA from "./components/componentA"
引用。 - 自动化全局注册:
//入口文件中导入自动化全局组件配置
import Vue from 'vue'
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'
const requireComponent = require.context(
// 其组件目录的相对路径
'./components',
// 是否查询其子目录
false,
// 匹配基础组件文件名的正则表达式
/Base[A-Z]\w+\.(vue|js)$/
)
requireComponent.keys().forEach(fileName => {
// 获取组件配置
const componentConfig = requireComponent(fileName)
// 获取组件的 PascalCase 命名
const componentName = upperFirst(
camelCase(
// 剥去文件名开头的 `./` 和结尾的扩展名
fileName.replace(/^\.\/(.*)\.\w+$/, '$1')
)
)
// 全局注册组件
Vue.component(
componentName,
// 如果这个组件选项是通过 `export default` 导出的,
// 那么就会优先使用 `.default`,
// 否则回退到使用模块的根。
componentConfig.default || componentConfig
)
})
全局注册行为必须在根Vue实例创建之前。
属性特性prop
prop的大小写
HTML中特性名(属性)对大小写不敏感,不能直接在DOM中使用驼峰命名prop,但是在``template中不受限制。 建议使用kebab-case命名,避开这些问题。 书写格式:
props:[“key”]props:{key:类型}`
key:类型,指定值类型string\number\boolean\array\object
传递动态或静态prop值
``v-bind:props=“val.title”`动态传值;
对象,数字,数组,布尔值等静态值,仍然需要
v-bind
绑定说明不是字符串,而是JS表达式:v-bind:props="42"
;
传入props下的一个对象时候,使用不带参数的v-bind=""
取代v-bind:prop-name=""
;
//javascript
props:{
post:{
id:1,
title:yo
}
}
//html
<blog-post v-bind="post"></blog-post>
//等价于
<blog-post :id="post.id" :title="post.title"></blog-post>
props单向数据流–父子通信
所有的prop都是父级单向下行绑定,但是对象或数组的props传入子组件函数,被改变时会影响父组件的状态。
props的验证
props会在组件创建之前进行验证,因此实例的属性,如data、computed等在default或validator函数中,不可用.
参考:生命周期钩子
语法:
props:{
prop:Number,
prop:[Number,String],
prop:{
//string、number、boolean、array、object、date、function、symbol
type:String,
required:true
},
prop:{
type:Number,
default:100
},
prop:{
type:Object,
default:function(){
return {message:'hello'}
}
},
prop:{
validator:function(value){
//fn...return true or false
}
}
}
prop的继承
组件接受任意的特性,并被添加到根元素上,是因为:显性定义向一个子组件传入信息,而当不能考虑周全时,增加灵活性。
禁止特性的继承:组件选项中设置inheritAttrs:false
配合
$attrs
属性,手动继承属性:子组件内v-bind="$attrs"
手动继承父级属性。
自定义事件
事件名对大小写敏感,但是HTML对大小写不敏感,因此推荐始终使用kebab-case命名。
v-model
组件中的v-model
默认利用名为value
的prop
。和名为input
的event
,但是单选框和复选框会将value
用于不同目的,需手动component
中model
选项设置避免。
重新定义change事件和checked属性:
Vue.component("yo",{
model:{
prop:"checked",
event:"change"
},
props:{
checked:"checked"
},
template:`
<input type="checkbox" v-bind:checked="checked" v-on:change="$emit('change',$event.target.checked)">`
})
怎么用?
监听原生事件 .native $listeners
一个组件的根元素上监听原生事件,v-on:event.native="fn"
或v-on="$listeners"
:
//javascript
//自定义input按钮
Vue.component('base-input', {
//取消子组件继承props
inheritAttrs: false,
props: ['label', 'value'],
computed: {
inputListeners: function () {
var vm = this
// `Object.assign` 将所有的对象合并为一个新对象
return Object.assign({},
// 我们从父级添加所有的监听器
this.$listeners,
// 然后我们添加自定义监听器,
// 或覆写一些监听器的行为
{
// 这里确保组件配合 `v-model` 的工作
input: function (event) {
vm.$emit('input', event.target.value)
}
}
)
}
},
template: `
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on="inputListeners"
>
</label>
`
})
如此,自定义组件可以监听到所有的事件,包括原生事件和内部子元素事件
prop的双向绑定 .sync
真正的双向绑定会带来维护上的问题:子组件可以修改父组件,且没有明显的改动来源。
因此,以updata:myPropName
的模式触发事件,实现。
子组件$emit
自定义事件updata:myPropName
,传参;父组件监听事件,更新
<text-doc
v-bind:propName="data.prop"
v-on:updata:propName="doc.prop=$event";>
</test-doc>
//简写
<text-doc v-bind:propName.sync="doc.prop">
带
.sync
的v-bind
不能使用JS表达式。无效。
Object.freeze(obj)
不追踪数据变化
插槽 slot
自定义模块可以插入slot,显示插入的任何东西,甚至是其他组件;
如:<component>slot将要显示的内容</component>
;
但是slot的作用域在目前组件,不在模板;
可以在自定义模板的slot标签内预设默认显示;
具名插槽:<slot name="default" ></slot>
;
组件:<component><template v-solt:default>指定插槽</template></component>
v-slot
只能用于template上;
子模板插槽prop上传,父组件v-slot:default="propName"
接收propName.user.data;
解构插槽prop:propName、{propName}、{propName:otherName}重命名、{propName = defaultValue}默认值
缩写:v-slot
=>#
;
动态插槽名:v-slot:[slotName]
;
实现子模板与父组件的通信与控制:插槽prop;
动态组件
使用v-bind:is=""
切换不同组件,为了避免反复渲染,用<keep-alive>
包裹组件,进行缓存。
<keep-alive>
<myComponent :is="currentComponent"></myComponent>
</keep-alive>
异步加载
vue自带的工厂函数,按需加载组件:
Vue.component("componentName",function(resolve,reject){
//成功,回调函数
resolve({template:"..."})
//失败,返回错误
reject("error")
})
webpack写法:
Vue.component("componentName",function(resolve){
//Ajax请求,传入回调函数
require(["./my-component"],resolve)
})
webpack + ES5 :
// 全局注册,Vue全局
Vue.component("async-webpack-example",
()=>import('./my-component')
)
// 局部注册,vm某实例下
var vm = new Vue({
components:{
"my-component":()=>import("./my-component")
}
})
**处理加载状态**
```javascript
const AsyncComponent= ()=>({
//加载的组件
component:import('./Mycomponent.vue'),
//异步加载时
loading:loadingComponent,
//加载失败
error:errorComponent,
//加载延迟时间
delay:200,
//超时时限,否则返回加载失败
timeout:3000
})
处理边界(通信)
访问元素和组件
- 子组件访问根级实例
$root
数据和方法; - 子组件访问父组件
$parent
,替代[prop]传递的父子通信方式; - 访问子组件和子元素,
ref
属性赋予子一个ID用以引用,父以this.$refs.refId
定位;
当
ref
和v-for
一起时,会定位到子组件数组。
$refs
只在组件渲染之后生效,且不是响应式的,避免在component和computed属性中使用。
- 依赖注入:实例选项provide和inject;相当于大范围的
props
;
//祖先组件注入
provide:function(){
return {
getMap:this.getMap
}
},
methods:{
getMap(){},
}
//后代组件依赖
inject:['getMap']
相关参考:Vuex
个例相关
程序化的事件
创建自定义事件$emit
,监听事件$on
一个事件,$once
一次事件,$off
停止监听。
例如:
$on(eventName,eventHandler)
,$emit , $on , $off并不是dispatchEvent,addEventListener,removeEventListener的别名
循环引用(终止无限循环)
- 递归组件:(自身)
组件自身模版调用自身,只能通过name
选项设置;
当使用全局定义组件时,自动设置组件名为name
值,所以可能到时无限循环,需要一个条件终止,例如v-if
。 - 组件间的循环引用
全局组件间循环,需子组件在beforeCreate
时注册,或使用异步加载子组件;
模版定义的替代品
- 内联模版
template的inline-template
特性 - X-Templates
script的typt="text/x-template"
属性,加上id后,实例引用id的模版;
强制更新数据或对象
未被响应式系统追踪的状态,可以使用强制更新$forceUpdate
;
通常是某个地方处理错误,注意处理BUG;
低开销静态组件 v-once
template模版根元素加上v-once
属性,可以只计算一次并且缓存。
过渡
进入/离开的过渡
单元素、组件的过度(预装transition组件)
- 类名
六种状态:
– .v-enter
进入过渡的开始状态,元素插入之前生效,下一帧移除
– .v-enter-active
生效时状态,插入之前生效,过度/动画完成移除,定义开始过渡过程时间、延迟、曲线函数(包含v-enter,v-enter-to)
– .v-enter-to
结束状态,元素插入下一帧生效,过度/动画完成移除
– .v-leave
离开过渡的开始状态
– .v-leave-active
生效时状态,定义离开过渡的时间、延迟、曲线函数(包含v-leave,v-leave-to)
– .v-leave-to
结束状态
当
<transition>
没有命名时,默认v-enter
,命名后name="my-transition"
,使用my-transition-enter
。
- 过渡
根据六种状态的类,进行CSS过渡效果;
- 动画
和 过渡不同的是,在animationded
事件触发后删除。
- 过渡和动画一起使用时
VUE识别transitioned
或animationed
来监听过渡结束,一起时需要指明指针type="animationed/transitioned"
明确申明。
- 自定义类名的属性
类似<transition>
的name
属性,定义类class,并使用第三方动画库结合VUE的过渡系统,达到效果:
enter-class
enter-active-class
enter-to-class
leave-class
leave-active-class
leave-to-class
- 设置时间 duration(属性)
<transition :duration="1000">...</transition>
或分别设置进入移除过渡时间
<transition :duration="{enter:1000,leave:1000}">...</transition>
- javascript钩子(事件)
<transition
// 自定义类名 style: v- = demo-
name="demo"
// 监听事件 animation | transition
type="animation"
// 过渡模式:in-out | out-in
mode="out-in"
// 过渡样式CSS-class
enter-class=""
enter-active-class=""
enter-to-class=""
leave-class=""
leave-active-class=""
leave-to-class=""
// 钩子JS hooks
@before-enter=""
@enter="()=>done()"
@after-enter=""
@enter-cancelled=""
@before-leave=""
@leave="()=>done()"
@after-leave=""
@leave-cancelled=""
// 延迟,1000或{enter:1000,leave:500}
:duration="1000"
>
<div v-if="isHide">
动画
</div>
</transiton>
添加v-bind:css="false"
,跳过css检测。
在enter和leave中必须使用done()函数进行回调,不然被同步调用,过渡立即完成。
初始渲染的过渡
初始渲染状态,同进入/离开的过渡一样,可以设置自定义类名属性,钩子事件
<transition
appear
appear-class=""
appear-to-class=""
appear-active-class=""
>...</transition>
<transition
appear
v-on:before-appear=""
v-on:appear=""
v-on:after-appear=""
v-on:appear-cancelled=""
></transition>
多个过渡
多个元素过渡,使用:key="keyValue"
标记来区分,触发效果
多个组件过渡,使用动态组件:is=""
即可
动画过渡特殊用法,以及状态过渡,有待开发。。。
混入
全局
Vue.mixin({
data(){
return{
//...
}
},
create:function(){
//...
},
})
局部
var mixinName = {
//...
}
var vm = new Vue({
mixins:[mixinName],
})
自定义指令
全局
Vue.directive("focus",{
bind:function(el,binding,vnode,oldVnode){},
inserted:function(el){
el.focus()
},
update:function(){},
componentUpdated:function(){},
unbind:function(){},
})
局部
new Vue({
el:"#app",
directives:{
focus:{
inserted:function(el){
el.focus()
}
}
}
})
渲染函数 & JSX
new Vue({
render:function(){
//...
}
})
过滤器
全局
Vue.filter("demo",fuction(value){
//...
return result
})
局部
new Vue({
filters:{
demo:function(value){
//...
return result
}
}
})
管道符“|”使用,可以传参数{{yo | filter("a","b")}}
,可以串联使用{{yo | filter1 | filter2}}
;