您的位置:首页 > Web前端 > Vue.js

【学习笔记】vue.js基础---组件

2020-01-12 18:32 579 查看

文章目录

  • 3.组件数据data
  • 4.组件模板template
  • 参数
  • 修饰符
  • 5.组件方法methods
  • 6.计算属性computed
  • 7.组件事件
  • 局部注册
  • 二.使用组件的完整步骤
  • 三.把vue实例中的data传给子组件
  • 四.当data中传递的数据prop较多时
  • 五.用key管理可复用的元素
  • 六.父组件如何监听子组件的事件
  • 六.
  • 一.组件注册

    全局注册

    Vue.component(‘my-component-name’, {
    // … 选项 …
    })

    1.组件名

    短横线分隔命名例如 my-component-name

    2.组件属性prop

    属性名

    • 一般使用短横线分隔命名

    属性值类型的约束

    • 给属性确定类型
    props: {
    title: String,
    likes: Number,
    isPublished: Boolean,
    commentIds: Array,
    author: Object,
    callback: Function,
    contactsPromise: Promise // or any other constructor
    }

    给属性传值

    //直接传值
    <blog-post title="My journey with Vue"></blog-post>
    //v-bind动态赋值
    
    <!-- 动态赋予一个变量的值 -->
    <blog-post v-bind:title="post.title"></blog-post>
    <!-- 动态赋予一个复杂表达式的值 -->
    <blog-post
    v-bind:title="post.title + ' by ' + post.author.name"
    ></blog-post>
    //数字
    <!-- 即便 `42` 是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
    <!-- 这是一个 JavaScript 表达式而不是一个字符串。-->
    <blog-post v-bind:likes="42"></blog-post>
    
    <!-- 用一个变量进行动态赋值。-->
    <blog-post v-bind:likes="post.likes"></blog-post>
    //布尔值
    <!-- 包含该 prop 没有值的情况在内,都意味着 `true`。-->
    <blog-post is-published></blog-post>
    
    <!-- 即便 `false` 是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
    <!-- 这是一个 JavaScript 表达式而不是一个字符串。-->
    <blog-post v-bind:is-published="false"></blog-post>
    
    <!-- 用一个变量进行动态赋值。-->
    <blog-post v-bind:is-published="post.isPublished"></blog-post>
    //数组
    <!-- 即便数组是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
    <!-- 这是一个 JavaScript 表达式而不是一个字符串。-->
    <blog-post v-bind:comment-ids="[234, 266, 273]"></blog-post>
    
    <!-- 用一个变量进行动态赋值。-->
    <blog-post v-bind:comment-ids="post.commentIds"></blog-post>
    //对象
    <!-- 即便对象是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
    <!-- 这是一个 JavaScript 表达式而不是一个字符串。-->
    <blog-post
    v-bind:author="{
    name: 'Veronica',
    company: 'Veridian Dynamics'
    }"
    ></blog-post>
    
    <!-- 用一个变量进行动态赋值。-->
    <blog-post v-bind:author="post.author"></blog-post>
    //传入一个对象的所有属性
    将对象post的所有属性传给模板
    post: {
    id: 1,
    title: 'My Journey with Vue'
    }
    <blog-post v-bind="post"></blog-post>
    等价于
    <blog-post
    v-bind:id="post.id"
    v-bind:title="post.title"
    ></blog-post>

    属性值的单向传递

    • 父级 prop 的更新会向下流动到子组件中,但是反过来则不行
    • 额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值
    • 不因该在子组件都内部改变porp,除非以下两种情况:
    • 1.这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用,在这种情况下,在子组件中最好定义一个本地的 data 属性并将这个 prop 用作其初始值:
    props: ['initialCounter'],
    data: function () {
    return {
    counter: this.initialCounter
    }
    }
    • 2.这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性:
    props: ['size'],
    computed: {
    normalizedSize: function () {
    return this.size.trim().toLowerCase()
    }
    }

    prop的验证

    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
    }
    }
    }
    })

    非prop特性

    在组件实例上添加的属性,但是prop里面没有定义,这些属性会被存到$arrts属性里面,并且些特性会被添加到这个组件的根元素上。如果你不希望组件的根元素继承特性,你可以在组件的选项中设置 inheritAttrs: false

    Vue.component('my-component', {
    inheritAttrs: false,
    // ...
    })

    注意 inheritAttrs: false 选项不会影响 style 和 class 的绑定。
    假设注册如下组件base-input

    Vue.component('base-input', {
    inheritAttrs: false,
    props: ['label', 'value'],
    template: `
    <label>
    {{ label }}
    <input
    v-bind="$attrs"
    v-bind:value="value"
    v-on:input="$emit('input', $event.target.value)"
    >
    </label>
    `
    })

    现在在组件外部添加required和 placeholder这两个属性
    {
    required: true,
    placeholder: ‘Enter your username’
    }
    就这样写

    <base-input
    v-model="username"
    required
    placeholder="Enter your username"
    ></base-input>

    这时required和 placeholder这两个属性会被$attrs属性接收,而不会直接传递到label(根元素)里面去

    替换合并已有都属性

    假设组件bootstrap-date-input的模板是这样的:

    <input type="date" class="form-control">

    现在我们在外部加入了 data-date-picker和class这两个属性:

    <bootstrap-date-input
    data-date-picker="activated"
    class="date-picker-theme-dark"
    ></bootstrap-date-input>

    在这种情况下,我们定义了两个不同的 class 的值:

    form-control,这是在组件的模板内设置好的
    date-picker-theme-dark,这是从组件的父级传入的
    对于绝大多数特性来说,从外部提供给组件的值会替换掉组件内部设置好的值。所以如果传入 type=“text” 就会替换掉 type=“date” 并把它破坏!庆幸的是,class 和 style 特性会稍微智能一些,即两边的值会被合并起来,从而得到最终的值:form-control date-picker-theme-dark。

    3.组件数据data

    -1.data必须是一个函数而不是一个对象

    -2.当一个prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。在这种情况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值:

    props: ['initialCounter'],
    data: function () {
    return {
    counter: this.initialCounter
    }
    }
    

    4.组件模板template

    文本

    <span>Message: {{ msg }}</span>

    Mustache 标签将会被替代为对应数据对象上 msg 属性的值。无论何时,绑定的数据对象上 msg 属性发生了改变,插值处的内容都会更新。

    <p>{{number+1}}</p>
    <p>{{ok?'YES':'ON'}}</p>
    <p>{{message.split('').reverse().join('')}}</p>

    也可以用表达式或者函数,这些表达式会在所属 Vue 实例的数据作用域下作为 JavaScript 被解析。有个限制就是,每个绑定都只能包含单个表达式,所以下面的例子都不会生效。

    <!-- 这是语句,不是表达式 -->
    {{ var a = 1 }}
    
    <!-- 流控制也不会生效,请使用三元表达式 -->
    {{ if (ok) { return message } }}

    指令

    v-once :当数据改变时,插值处的内容不会更新

    v-html :用于输出真正的html,而不是被识别成普通文本

    v-bind :用于属性的绑定

    v-if :用于条件渲染一块内容
    v-else:else块
    v-else-if :else-if块

    v-show:带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS 属性 display。注意,v-show 不支持 元素,也不支持 v-else

    v-for :v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名。在 v-for 块中,我们可以访问所有父作用域的属性。v-for 还支持一个可选的第二个参数,即当前项的索引。当在组件上使用 v-for 时,key 现在是必须的。

    <ul id="example-2">
    <li v-for="(item, index) in items">
    {{ parentMessage }} - {{ index }} - {{ item.message }}
    </li>
    </ul>

    v-for 里面使用对象

    <div v-for="(value, name, index) in object">
    {{ index }}. {{ name }}: {{ value }}
    </div>
    
    new Vue({
    el: '#v-for-object',
    data: {
    object: {
    title: 'How to do lists in Vue',
    author: 'Jane Doe',
    publishedAt: '2016-04-10'
    }
    }
    })

    v-model:

    结果

    v-on :指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。
    1.后面直接跟js代码

    <button v-on:click="counter += 1">Add 1</button>

    2.后面跟方法名称

    <!-- `greet` 是在下面定义的方法名 -->
    <button v-on:click="greet">Greet</button>
    var example2 = new Vue({
    el: '#example-2',
    data: {
    name: 'Vue.js'
    },
    // 在 `methods` 对象中定义方法
    methods: {
    greet: function (event) {
    // `this` 在方法里指向当前 Vue 实例
    alert('Hello ' + this.name + '!')
    // `event` 是原生 DOM 事件
    if (event) {
    alert(event.target.tagName)
    }
    }
    }
    })
    
    // 也可以用 JavaScript 直接调用方法
    example2.greet() // => 'Hello Vue.js!'

    3.在内联js语句中调用方法

    <div id="example-3">
    <button v-on:click="say('hi')">Say hi</button>
    <button v-on:click="say('what')">Say what</button>
    </div>
    new Vue({
    el: '#example-3',
    methods: {
    say: function (message) {
    alert(message)
    }
    }
    })

    4.在内联语句处理器中访问原始dom事件,可以用特殊变量$event把它传入方法

    <button v-on:click="warn('Form cannot be submitted yet.', $event)">
    Submit
    </button>
    // ...
    methods: {
    warn: function (message, event) {
    // 现在我们可以访问原生事件对象
    if (event) event.preventDefault()
    alert(message)
    }
    }
    指令的缩写

    v-bind

    <!-- 完整语法 -->
    <a v-bind:href="url">...</a>
    <!-- 缩写 -->
    <a :href="url">...</a>

    v-on

    <!-- 完整语法 -->
    <a v-on:click="doSomething">...</a>
    
    <!-- 缩写 -->
    <a @click="doSomething">...</a>

    参数

    <a v-bind:href="url">...</a>

    在这里 href 是参数,告知 v-bind 指令将该元素的 href 特性与表达式 url 的值绑定。

    动态参数

    从 2.6.0 开始,可以用方括号括起来的 JavaScript 表达式作为一个指令的参数:

    <!--
    注意,参数表达式的写法存在一些约束,如之后的“对动态参数表达式的约束”章节所述。
    -->
    <a v-bind:[attributeName]="url"> ... </a>

    这里的 attributeName 会被作为一个 JavaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用。例如,如果你的 Vue 实例有一个 data 属性 attributeName,其值为 “href”,那么这个绑定将等价于 v-bind:href。

    修饰符

    修饰符 (modifier) 是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。例如,.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault():

    <form v-on:submit.prevent="onSubmit">...</form>
    事件修饰符
    <!-- 阻止单击事件继续传播 -->
    <a v-on:click.stop="doThis"></a>
    
    <!-- 提交事件不再重载页面 -->
    <form v-on:submit.prevent="onSubmit"></form>
    
    <!-- 修饰符可以串联 -->
    <a v-on:click.stop.prevent="doThat"></a>
    
    <!-- 只有修饰符 -->
    <form v-on:submit.prevent></form>
    
    <!-- 添加事件监听器时使用事件捕获模式 -->
    <!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
    <div v-on:click.capture="doThis">...</div>
    
    <!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
    <!-- 即事件不是从内部元素触发的 -->
    <div v-on:click.self="doThat">...</div>
    
    <!-- 点击事件将只会触发一次 -->
    <a v-on:click.once="doThis"></a>
    
    <!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
    <!-- 而不会等待 `onScroll` 完成  -->
    <!-- 这其中包含 `event.preventDefault()` 的情况 -->
    <div v-on:scroll.passive="onScroll">...</div>

    使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。
    这个 .passive 修饰符尤其能够提升移动端的性能。

    按键修饰符

    在监听键盘事件时,我们经常需要检查详细的按键。Vue 允许为 v-on 在监听键盘事件时添加按键修饰符:

    <!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
    <input v-on:keyup.enter="submit">
    //keyup指键盘监听事件
    按键码

    为了在必要的情况下支持旧浏览器,Vue 提供了绝大多数常用的按键码的别名:

    .enter
    .tab
    .delete (捕获“删除”和“退格”键)
    .esc
    .space
    .up
    .down
    .left
    .right

    系统修饰键

    可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器。

    .ctrl
    .alt
    .shift
    .meta
    .exact 修饰符允许你控制由精确的系统修饰符组合触发的事件。

    鼠标按钮修饰符

    .left
    .right
    .middle
    这些修饰符会限制处理函数仅响应特定的鼠标按钮。

    5.组件方法methods

    6.计算属性computed

    这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性:

    props: ['size'],
    computed: {
    normalizedSize: function () {
    return this.size.trim().toLowerCase()
    }
    }

    7.组件事件

    事件名

    推荐始终使用 kebab-case 的事件名。

    局部注册

    1.先用普通的js定义组件

    var ComponentA = { /* ... */ }
    var ComponentB = { /* ... */ }
    var ComponentC = { /* ... */ }

    2.在vue实例中到compons中定义需要使用到组件

    • 属性名为自定义元素名,组件值就是之前定义的对象
    new Vue({
    el: '#app',
    components: {
    'component-a': ComponentA,
    'component-b': ComponentB
    }
    })
    • 局部注册的组件在其子组件中不可用 在组件b中注册组件a
    var ComponentA = { /* ... */ }
    
    var ComponentB = {
    components: {
    'component-a': ComponentA
    },
    // ...
    }

    在模块系统中局部注册

    • 在局部注册之前导入组件,比如在假设的ComponentB.js 或 ComponentB.vue 文件中:
    import ComponentA from './ComponentA'
    import ComponentC from './ComponentC'
    
    export default {
    components: {
    ComponentA,
    ComponentC
    },
    // ...
    }

    二.使用组件的完整步骤

    • 1.注册组件
    Vue.component('button-counter',{
    props:['title'],
    data:function(){
    return {
    count:0
    }
    },
    template:'<div><h1>111111</h1><button v-on:click="clickfun">{{title}}You clicked me {{ count }} times.</button><slot></slot></div>',
    methods:{
    clickfun:function(){
    this.count++;
    this.$emit('clicknow',this.count);
    }
    }
    
    })
    • 2.在vue根实例中注册根元素
    new Vue({ el: '#components-demo' })
    • 在注册中的根元素中使用组件
    <div id="components-demo">
    <button-counter></button-counter>
    </div>

    三.把vue实例中的data传给子组件

    • 这是我们注册好的组件
    Vue.component('blog-post', {
    props: ['title'],
    template: '<h3>{{ title }}</h3>'
    })
    • 假设vue实例有这样一组数据
    new Vue({
    el: '#blog-post-demo',
    data: {
    posts: [
    { id: 1, title: 'My journey with Vue' },
    { id: 2, title: 'Blogging with Vue' },
    { id: 3, title: 'Why Vue is so fun' }
    ]
    }
    })

    -在使用组件的时候这样写

    <div id='blog-post-demo'>
    <blog-post
    v-for="post in posts"
    v-bind:key="post.id"
    v-bind:title="post.title"
    ></blog-post>
    ></div>

    四.当data中传递的数据prop较多时

    • 假设vue实例有这样一组数据
    new Vue({
    el: '#blog-post-demo',
    data: {
    posts: [
    { id: 1, title: 'My journey with Vue' ,content:'lalalalalalalalaallala',publishedAt:'12.23'},
    { id: 2, title: 'Blogging with Vue' ,content:'opopopopoppooooooo,publishedAt:'},
    { id: 3, title: 'Why Vue is so fun' content:'sanasnaassnansasnasn,publishedAt:'}
    ]
    }
    })
    • 这是注册好的组件
    Vue.component('blog-post', {
    props: ['post'],
    template:<div class="blog-post">
    <h3>{{ post.title }}</h3>
    <div v-html="post.content"></div>
    <div v-html="post.publishedAt"></div>
    </div>
    `
    })
    • 在html上渲染出来
    <div id='blog-post-demo'>
    <blog-post
    v-for="post in posts"
    v-bind:key="post.id"
    v-bind:post="post"
    ></blog-post>
    ></div>

    五.用key管理可复用的元素

    为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性:

    <div v-for="item in items" v-bind:key="item.id">
    <!-- 内容 -->
    </div>

    建议尽可能在使用 v-for 时提供 key attribute,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。不要使用对象或数组之类的非基本类型值作为 v-for 的 key。请用字符串或数值类型的值。

    六.父组件如何监听子组件的事件

    • 假设现在有这样一个组件,组件里面有一个按钮,当点击这个按钮时,我们需要告诉父级组件放大所有博文的文本
    Vue.component('blog-post', {
    props: ['post'],
    template: `
    <div class="blog-post">
    <h3>{{ post.title }}</h3>
    <button>
    Enlarge text
    </button>
    <div v-html="post.content"></div>
    </div>
    `
    })

    1.在父组件中添加一个postFontSize 数据属性来支持这个功能,并且写一个onEnlargeText方法来改变这个postFontSize的大小:

    new Vue({
    el: '#blog-post-demo',
    data: {
    posts: [
    { id: 1, title: 'My journey with Vue' },
    { id: 2, title: 'Blogging with Vue' },
    { id: 3, title: 'Why Vue is so fun' }
    ],
    postFontSize: 1
    },
    methods:{
    onEnlargeText:function(enlargeAmout){
    this.postFontSize +=enlargeAmout
    }
    }
    })
    
    然后再html面利用这个postFontSize来改变文本博文大小
    <div id="blog-post-demo">
    <div :style="{ fontSize: postFontSize + 'em' }">
    <blog-post
    v-for="post in posts"
    v-bind:key="post.id"
    v-bind:post="post"
    ></blog-post>
    </div>
    </div>
    
    //这个时候按钮并没有起作用,我们需要让按钮告诉父组件放大博文
    
    首先给按钮绑定一个方法click1,只要点击这个按钮就会触发这个方法click1,click1方法会将一个叫enlarge-text的方法和一个0.1的参数传给父组件blog-post
    Vue.component('blog-post',{
    props:['post'],
    template:'<div class="blog-post"><h3>{{post.title}}</h3><button v-on:click="click1">Enlarge text</button><div v-html="post.content"></div></div>',
    methods:{
    click1:function(){
    this.$emit('enlarge-text',0.1);
    }
    }
    })
    然后我们让父组件blog-post在接收到enlarge-text方法后调用onEnlargeText方法
    ,并且把0.1传给onEnlargeText方法的参数enlargeAmout,这个时候postFontSize已经变大了,这就实现从button 到中间的blog-post再到外层style div的传递
    <div id='blog-post-demo'>
    <div :style="{fontSize:postFontSize+'em'}">
    <blog-post v-for='post in posts' v-bind:key='post.id' v-bind:post='post' v-on:enlarge-text='onEnlargeText'></blog-post>
    </div>
    </div>

    六.

    • 点赞
    • 收藏
    • 分享
    • 文章举报
    安柠儿_Anni 发布了3 篇原创文章 · 获赞 0 · 访问量 104 私信 关注
    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: