vue.js中级进阶

本文详细介绍了Vue.js的组件注册,包括组件名大小写的规范、全局与局部注册的区别。同时,探讨了属性特性prop的使用,如prop的大小写、动态与静态prop值的传递,以及单向数据流。此外,还涵盖了自定义事件、插槽、动态组件、异步加载等高级特性,帮助开发者深入理解Vue.js的组件通信和应用构建。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

组件注册 Vue.component()

组件名大小写

  • 使用kebab-case:小写和连字符-
  • 使用PascalCase:驼峰命名。
    强烈建议,使用kebab-case命名。

当驼峰命名的首字母大写时,两种方法都能引用生效,都接受;
直接在DOM中使用时,只有kebab-case生效;

全局注册 和 局部注册

  • 作用域不同,局部注册在自己子组件内不可用
  • 打包时,全局会被打包,即使没有被使用
    因此,将component赋值变量,再组件components内引用,实现复用。

模块系统

  1. 使用Webpack和Babel的模块系统时,推荐把各组件文件夹放在components根目录中,通过import ComponentA from "./components/componentA"引用。
  2. 自动化全局注册:
//入口文件中导入自动化全局组件配置
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默认利用名为valueprop。和名为inputevent,但是单选框和复选框会将value用于不同目的,需手动componentmodel选项设置避免。
重新定义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">

.syncv-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定位;

refv-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识别transitionedanimationed来监听过渡结束,一起时需要指明指针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}};

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值