Vuejs基础使用

快速入门

引入vue

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

写代码

<div id="app">
  {{ message }}
</div>

创建实例

var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
})

生命钩子

1.jpg

2.jpg

样式绑定

class绑定

  • 对象语法

    根据条件判断是否添加该class

    <div id="app" :class="{fontColor:isClass}">123</div>
    
    .fontColor{
        color: red;
    }
    
    var vm = new Vue({
        el: '#app',
        data: {
            isClass:true
        },
    })
    
  • 数组语法

    传数组以应用一个 class 列表

    <div id="app" :class="[fontClass,divClass]">123</div>
    
    var vm = new Vue({
        el: '#app',
        data: {
            fontClass:'baseFont',
            divClass:'baseDiv'
        },
    })
    
      .baseFont{
          font-size: 40px;
          font-weight: 700;
          color: brown;
      }
      .baseDiv{
          display: block;
          width: 200px;
          height: 200px;
          background-color: blueviolet;
      }
    
  • 三元表达式

    根据条件决定是否添加该class

    <div id="app" :class="[isFont?fontClass:'',divClass]">123</div>
    
    var vm = new Vue({
        el: '#app',
        data: {
            isFont:false,
            fontClass:"baseFont",
            divClass:"baseDiv"
        },
    })
    

    也可以用对象语法写,会更清晰。此时baseFont不是变量,而是类名

    <div id="app" :class="[{baseFont:isFont},divClass]">123</div>
    

style绑定

  • 对象语法

    与css相似

    <div id="app" :style="{color:baseColor,fontSize:baseSize}">123</div>
    
    var vm = new Vue({
        el: '#app',
        data: {
            baseColor:'red',
            baseSize:'40px'
        },
    })
    

    你也可以在直接绑定到一个样式对象,会更清晰

    <div id="app" :style="baseFont">123</div>
    
    var vm = new Vue({
        el: '#app',
        data: {
            baseFont:{
               color:'red',
               fontSize:'40px'
            }
        },
    })
    
  • 数组语法

    将多个样式对象应用到同一个元素上

    <div id="app" :style="[baseFont,basediv]">123</div>
    
    var vm = new Vue({
        el: '#app',
        data: {
            baseFont:{
               color:'red',
               fontSize:'40px'
            },
            basediv:{
                display:'block',
                width:'100px',
                height:'100px',
                backgroundColor:'green'
            }
        },
    })
    

条件渲染

v-if

  • 指令表达式返回 true时才会渲染内容

    <div id="app">
        <h1 v-if="cool">cool~~~</h1>
    </div>
    
    data: {
        cool: true
    },
    
  • 可以与v-else配合,如果不是true将返回v-else的内容

    <div id="app">
        <h1 v-if="cool">cool~~~</h1>
        <h1 v-else>what?</h1>
    </div>
    
  • 多个条件时可以用v-else-if

    <div id="app">
        <h1 v-if="cool===1">cool~~~~</h1>
        <h1 v-else-if="cool===2">what?</h1>
        <h1 v-else="cool===3">I don't know </h1>
    </div>
    
    data: {
        cool: 2
    },
    

key

  • 一般情况下,Vue会复用已有元素

    <div id="app">
        <template v-if="username">
            Username:<input placeholder="Enter your username">
        </template>
    
        <template v-else>
            Email:<input placeholder="Enter your email address">
        </template>
    
        <br/><input type="button" @click="username=!username" value="change">
    </div>
    
    data: {
        username: true
    },
    

    template可用于包裹元素。在上面例子中,如果你已经输入了内容,由于Vue的复用,切换时内容将不会清空

  • key管理可复用元素,将元素独立开。应用时只需在需要独立的元素上加上不同的key值

    Username:<input placeholder="Enter your username" key="1">
    Email:<input placeholder="Enter your email address" key="2">
    

v-show

  • v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS property display

    <h1 v-show="show">Hello!</h1>
    
  • 不支持<template>

列表渲染

v-for

  • 一个数组对应一组元素

    <ul id="list">
        <li v-for="animal in animals">
            {{animal.name}}
        </li>
    </ul>
    
    var vm = new Vue({
        el: '#list',
        data: {
            animals:[
                {name:'狮子'},
                {name:'黑豹'},
                {name:'小猪'},
            ]
        },
    })
    

    第二个参数:索引

    <li v-for="(animal,i) in animals">
        {{i}}-{{animal.name}}
    </li>
    
  • 使用对象遍历属性值

    <li v-for="info in lion">
        {{info}}
    </li>
    
    data: {
        lion:{
            name:'king',
            age:3,
            character:'violent'
        }
    },
    

    第二个参数:属性名称

    <li v-for="(info,name) in lion">
        {{name}}:{{info}}
    </li>
    

    第三个参数:索引

    <li v-for="(info,name,i) in lion">
        {{i}}:{{name}}:{{info}}
    </li>
    
  • 维护状态

    key用于跟踪每个节点的身份。

    <li v-for="animal in animals" :key="animal.id">
        {{animal.name}}
    </li>
    
    data: {
    	animals:[
    		{name:'狮子',id:1},
    		{name:'黑豹',id:2},
    		{name:'小猪',id:3},
    	]
    },
    
  • 可以在<template>和组件上使用,在组件是必须加上key

数组更新

  • push(元素):从数组末尾添加元素
  • pop():从数组中删除最后一个元素
  • shift():从数组中删除第一个元素
  • unshift(元素):从数组开头添加元素
  • splice(开始位置,移除个数,要添加(替换)的元素):对数组进行删除替换添加元素的操作
  • sort():排列
  • reverse():颠倒数组元素的位置

  • filter():返回符合标准的元素,返回新数组。如:arr.filter(item=>item.length>6)

  • concat():合并数组,然后新数组。如:arr1.concat(arr2)

  • slice(开始索引,结束索引):抽取数组,返回新数组。如:arr.slice(2, 4)

显示过滤/排序后的结果

  • 如果想要在不改变原数组的情况下显示过滤/排序后的结果,可以创建一个计算属性

    <li v-for="n in evenNumbers">{{ n }}</li>
    
    data: {
      numbers: [ 1, 2, 3, 4, 5 ]
    },
    computed: {
        evenNumbers: function () {
            return this.numbers.filter(number=>{
                return number % 2 === 0
            })
        }
    }
    
  • 如果是嵌套v-for,可以使用方法

    <ul v-for="set in sets">
      <li v-for="n in even(set)">{{ n }}</li>
    </ul>
    
    data: {
      sets: [[ 1, 2, 3, 4, 5 ], [6, 7, 8, 9, 10]]
    },
    methods: {
      even: function (numbers) {
        return numbers.filter(number=>{
          return number % 2 === 0
        })
      }
    }
    

    事件处理

v-on

v-on 指令监听 DOM 事件,缩写@

  • 简单事件直接写

    <div id="app">
      <button v-on:click="count += 1">Add 1</button>
      <p>按钮被点了 {{ count }} 下</p>
    </div>
    
    var vm = new Vue({
      el: '#app',
      data: {
        count: 0
      }
    })
    
  • 较复杂时在methods定义方法并监听

    <div id="app">
      <button @click="greet">Greet</button>
    </div>
    
    var vm = new Vue({
      el: '#app',
      data: {
        name: '小明'
      },
      methods: {
        greet: function (event) {
          alert('Hello ' + this.name + '!')
          // `event` 是原生 DOM 事件
          if (event) {
            alert(event.target.tagName)
          }
        }
      }
    })
    
  • 内联js语句中调用

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

    如果需要访问原始的 DOM 事件。可以传入 $event

    <button @click="say('hi',$event)">Say hi</button>
    

事件修饰符

<!-- 阻止单击事件继续传播 -->
<a @click.stop="doThis"></a>

<!-- 提交事件不再重载页面 -->
<form @submit.prevent="onSubmit"></form>

<!-- 修饰符可以串联 -->
<a @click.stop.prevent="doThat"></a>

<!-- 只有修饰符 -->
<form @submit.prevent></form>

<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div @click.capture="doThis">...</div>

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div @click.self="doThat">...</div>

<!-- 阻止对元素自身的点击 -->
<div @click.self.prevent="doThat">...</div>

按键修饰符

<input v-on:keyup.enter="submit">
<input v-on:keyup.page-down="onPageDown">
<!-- keyCode attribute  -->
<input v-on:keyup.13="submit">
<!-- 自定义按键修饰符别名 -->
Vue.config.keyCodes.f1 = 112

系统修饰符

<!-- Alt + C -->
<input @keyup.alt.67="clear">

<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>
  • .exact修饰符
    修饰符允许你控制由精确的系统修饰符组合触发的事件

    <!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
    <button v-on:click.ctrl="onClick">A</button>
    <!-- 有且只有 Ctrl 被按下的时候才触发 -->
    <button v-on:click.ctrl.exact="onCtrlClick">A</button>
    
  • 鼠标按钮修饰符
    .left.rightmiddle

    表单输入绑定

v-model

可以用 v-model 指令在表单 <input><textarea><select> 元素上创建双向数据绑定。v-model 会忽略所有表单元素的 value、checked、selected attribute 的初始值而总是将 Vue 实例的数据作为数据来源。

  • text 和 textarea 元素使用 value property 和 input 事件;
  • checkbox 和 radio 使用 checked property 和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件。
#1. 文本
<input v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p>

#2. 多行文本
<textarea v-model="message"></textarea>
<p>{{ message }}</p>

#3. 复选框
<!-- 当选中时,checked=true -->
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>

<!-- true-value与false-value属性,如下选中时为yes -->
<input type="checkbox" v-model="toggle"  true-value="yes" false-value="no">

#4. 多个复选框,绑定到同一个数组
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<p>Checked names: {{ checkedNames }}</p>

#5. 单选按钮
<!-- 当选中第一个的时候,picked="one" -->
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<p>Picked: {{ picked }}</p>

<!-- v-bind绑定,如下,选中时pick=a -->
<input type="radio" v-model="pick" v-bind:value="a">

#6. 选择框
<!-- 当选中A时,selected="A" -->
<select v-model="selected">
   <option disabled value="">请选择</option>
   <option>A</option>
   <option>B</option>
   <option>C</option>
</select>
<p>Selected: {{ selected }}</p>

#7. 选择框多选时绑定到一个数组----selected:[]
<select v-model="selected" multiple>
    <option>A</option>
    <option>B</option>
    <option>C</option>
</select>
<p>Selected: {{ selected }}</p>

#8. 选择框v-for动态渲染
<select v-model="selected">
  <option v-for="option in options" :value="option.value">
    {{ option.text }}
  </option>
</select>
<span>Selected: {{ selected }}</span>
#8. 选择框v-for动态渲染
data:{
    selected:'A',
    options:[
        { text: 'One', value: 'A' },
        { text: 'Two', value: 'B' },
        { text: 'Three', value: 'C' }
    ]
}

在文本区域插值不会生效

修饰符

  • .lazy
    在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步。 lazy 修饰符可以转为在 change 事件_之后_进行同步:

    <!-- 在“change”时而非“input”时更新 -->
    <input v-model.lazy="msg">
    
  • .number

    自动将用户的输入值转为数值类型

    <input v-model.number="age" type="number">
    
  • .trim

    自动过滤用户输入的首尾空白字符

    <input v-model.trim="msg">
    

组件

  • 组件是可复用的 Vue 实例,且带有一个名字
  • 每用一次组件,就会有一个它的新实例被创建
  • data 必须是一个函数
    data: function () {
      return {
        count: 0
      }
    }
    

1. 注册组件

  • 全局注册

    可以用在任何新创建的 Vue 根实例的模板中。第一个参数是组件名

    Vue.component('my-component-name', {
      // ... options ...
    })
    
  • 局部注册

    即在哪注册在哪用,这样就不会过多的占用内存,首先注册一个局部组件

    var ComponentA={
        props:['title'],
        template:`<h3>{{title}}</h3>`
    }
    

    然后实例的components对象中使用,属性名就是组件名,属性值就是你想传入的组件

    new Vue({
      el: '#app',
      components:{
          'blog-title':ComponentA
      }
    })
    

注意:推荐使用kebab-case(短横线分割命名)给组件命名,例如该例子中的<blog-title>,否则容易报错:

image.png

注意:每个组件必须只有一个根元素,即template里只能有一个根标签

2. Prop

#prop大小写

prop对大小写不敏感,所以可以这样使用:

Vue.component('blog', {
  props: ['postTitle'],
  template: '<h3>{{ postTitle }}</h3>'
})
<blog post-title="hello!"></blog>

#prop类型

一般情况下是以字符串数组形式列出的 prop:

props: ['title', 'likes', 'isPublished', 'commentIds', 'author']

也可以给每个prop指定值类型。属性名是prop名称,属性值是prop类型

props: {
  title: String,
  likes: Number,
  isPublished: Boolean,
  commentIds: Array,
  author: Object,
  callback: Function,
  contactsPromise: Promise // or any other constructor
}
//也可以是Data、Symbol

#传递prop

一个例子

Vue.component('blog-post', {
  props: ['title'],	//给组件定义一个属性
  template: '<h3>{{ title }}</h3>'
})
<blog-post title="My journey with Vue"></blog-post> //传值
  • 静态传递

    # 传入字符串
    <blog-post title="My journey with Vue"></blog-post>
    
    #有时候需要用v-bind告诉vue这是一个js表达式而不是字符串
    # 传入布尔值,
    <blog-post :is-published="false"></blog-post>
    # 传入数组
    <blog-post :comment-ids="[234, 266, 273]"></blog-post>
    # 传入对象
    <blog-post :author="{name: 'Veronica',company: 'Veridian Dynamics'}"></blog-post>
    # 传入一个对象所有的属性
    
  • 动态传递

    new Vue({
      el: '#blog',
      data: {
        posts: [
          { id: 1, title: 'My journey with Vue' },
          { id: 2, title: 'Blogging with Vue' },
          { id: 3, title: 'Why Vue is so fun' }
        ]
      }
    })
    
    <blog-post v-for="post in posts" :key="post.id" :title="post.title" ></blog-post>
    

    当需要传递的属性很多时,可以将这些属性成合成一个。在这个例子中,可以把所有post所有的属性如title、content写在一个post 中:

  1. 重构组件

    原本子组件

    Vue.component('blog-post', {
      props: ['title','content'],
      template: `
        <div>
          <h3>{{ title }}</h3>
          <div v-html="content"></div>
        </div>
      `
    })
    

    现在子组件

    Vue.component('blog-post', {
      props: ['post'],
      template: `
        <div>
          <h3>{{ post.title }}</h3>
          <div v-html="post.content"></div>
        </div>
      `
    })
    
  2. 创建实例

    new Vue({
      el: '#blog',
      data: {
        posts: [
          { id: 1, title: 'My journey with Vue',content:'111' },
          { id: 2, title: 'Blogging with Vue',content:'222' },
          { id: 3, title: 'Why Vue is so fun',content:'333'}
        ]
      }
    })
    
  3. 在实例中使用组件

    原本父级

    <blog-post v-for="post in posts" :key="post.id" :title="post.title" :content="post.comtent"></blog-post>
    

    现在父级

    <blog-post v-for="post in posts" :key="post.id" :post="post"></blog-post>
    

#单项数据流

父级 prop 的更新会向下流动到子组件中,但是反过来则不行

#prop验证

可以为组件的 prop 指定验证要求。

  • 当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。
  • prop 会在一个组件实例创建之前进行验证
Vue.component('my-component', {
  props: {
    //1. 基础的类型检查 (null和undefined会通过任何类型验证)  
    propA: Number,
      
    //2. 多个可能的类型
    propB: [String, Number],
      
    //3. 必填的字符串
    propC: {				 
      type: String,
      required: true
    },
      
    //4. 带有默认值的数字
    propD: {				 
      type: Number,
      default: 100
    },
      
    //5.自定义验证函数。下面例子表示必须匹配下列字符串中的一个
    propF: {				  
      validator: function (value) {
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
    }
  }
})

3. 自定义事件

#事件名

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

this.$emit('my-event')
<my-component @my-event="doSomething"></my-component>

#监听子组件事件

  1. 创建实例

    new Vue({
      el: '#demo',
      data: {
        posts: [/* ... */],
        postFontSize: 1
      }
    })
    
  2. $emit给子组件添加事件

    Vue.component('blog-post', {
      props: ['post'],
      template: `
        //...
          <button @click="$emit('enlarge-text')">Enlarge</button>
         //...
      `
    })
    
  3. v-on监听子组件事件。当子组件触发时,开始响应

    <div id="demo">
      <div :style="{ fontSize: postFontSize + 'em' }">
        <blog-post 
          ...
          @enlarge-text="postFontSize += 0.1"
         ></blog-post>
     </div>
    </div>
    

$emit可以接收第二个参数

  1. 给子组件事件添加参数

    <button v-on:click="$emit('enlarge-text', 0.1)">Enlarge</button>
    
  2. 父级通过$event获取整个参数值

    <blog-post
      ...
      @enlarge-text="postFontSize += $event"
    ></blog-post>
    

    第二种写法:

    <blog-post
    ...
    @enlarge-text="enlargeText"
    ></blog-post>
    

    传过来的参数值会作为该方法的第一个参数

    methods: {
       enlargeText: function (num) {
         this.postFontSize += num
       }
    }
    

#自定义事件中的v-model

  • 普通情况下自定义事件

    <input v-model="searchText">
    

    等价于

    <input :value="searchText" @input="searchText = $event.target.value">
    
  • 在组件上自定义事件

    <custom-input v-model="searchText"/>
    

    等价于

    <custom-input :value="searchText" @input="searchText = $event"/>
    

    这时候需要更改子组件,才能让其正常运作。

    Vue.component('custom-input', {
      props: ['value'],
      template: `
        <input
          :value="value"
          @input="$emit('input', $event.target.value)"
        >
      `
    })
    

    解析:这时子组件的input触发时,会抛出$event.target.value,父级会监听到并接收$event.target.value。此时父级的$event实际上就等于子组件抛出的$event.target.value,变化后的searchText的值会绑定在父级的value上,然后通过props传回子组件,子组件接收到这个props值后就绑定在input的value上,这时候就在页面呈现出效果了

#绑定原生事件

使用 v-on.native 修饰符:

<base-input @focus.native="onFocus"></base-input>

注意.native监听的是组件的根元素。如果想监听到子元素,需要用到$listeners对象

4. 插槽

#一个例子

  1. 在子组件模板中加上一个插槽<slot></slot>

    Vue.component('alert-box', {
      template: `
        <div>
          <strong>Error!</strong>
          <slot></slot>
        </div>
      `
    })
    
  2. 在父级组件内添加内容后,内容就会自动放进插槽内

    <alert-box>Something bad happened.</alert-box>
    

注意:如果子的 template 中没有包含一个 slot 元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃

#默认内容

默认内容会在没有提供内容的时候被渲染。只需要将内容放在slot中:

//在一个子组件的template中
<button>
  <slot>Submit</slot>
</button>
//1. 父级中没有添加任何内容
<submit-button></submit-button>
//   渲染后备内容:
<button>Submit</button>
//2. 父级中有内容
<submit-button>Save</submit-button>
//   后备内容会被取代:
<button>Save</button>

#具名插槽

当我们需要多个插槽时,具名插槽很有用:

  1. 组件模板中,给slot添加name属性

    //假设这是base-layout组件的模板,需要分别给header、main、footer添加插槽:
    <div class="container">
      <header>
        <slot name="header"></slot>
      </header>
      <main>
        <slot></slot> //这个插槽没有名字,name的值默认为default
      </main>
      <footer>
        <slot name="footer"></slot>
      </footer>
    </div>
    
  2. 父级中,用带有v-slottemplate包裹内容,v-slot指定插槽名称

    <base-layout>
      <template v-slot:header>
        <h1>I am a header</h1>
      </template>
    
      //任何没有按规定包裹的内容都会被视为默认插槽的内容
      <p>A paragraph for the main content.</p>
      <p>And another one.</p>
    
      <template v-slot:footer>
        <p>I am a footer</p>
      </template>
    </base-layout>
    

    v-slot可以缩写:

    <template #header>
       <h1>I am a header</h1>
    </template>
    

#编译作用域

插槽的作用域与实例相同。即能访问到实例的数据,而不能访问组件的数据

//可以访问
<navigation-link url="/profile">{{ user.name }}</navigation-link>
//不可以访问
<navigation-link url="/profile">{{ url }}</navigation-link>

#插槽作用域

如果想在父级插槽内容中访问到子组件的数据

  1. 在子组件的slot中绑定数据

    Vue.component('user-info', {
        //user作为属性绑定数据。在这里user属性被称为插槽prop
        template: `<span><slot :user="userdata">{{userdata.current}}</slot></span>`,
        data:()=>({
            userdata:{
                before:'lihua',
                current:'xiaoming'
            }
        }),
    })
    
  2. v-slot 来定义插槽 prop 的名字。名字也可以是其他

    <user-info v-slot:default="slotProps">
        {{slotProps.user.before}}
    </user-info>
    

    由于指定的插槽是default,所以以上也可以简写成:注意不要与具名插槽混淆

    <user-info v-slot="slotProps">
        {{slotProps.user.before}}
    </user-info>
    

自 2.6.0 起有所更新。已废弃的使用 slot-scope

# 动态插槽名

<base-layout>
  <template v-slot:[dynamicSlotName]>
    ...
  </template>
</base-layout>

5. 动态组件

如果在一个多标签界面实现组件切换,可以通过 Vue 的 <component> 元素加一个特殊的 is attribute 来实现::

<!-- 切换tab值 -->
<button v-for="tab in tabs" :key="tab" @click="currentTab = tab">{{tab}}</button>
<!-- 组件会在 `currentTabComponent` 改变时改变 -->
<component v-bind:is="currentTabComponent"></component>
Vue.component("com1", {
    template: "<div>com1</div>"
});
Vue.component("com2", {
    template: "<div>com2</div>"
});
Vue.component("com3", {
    template: "<div>com3</div>"
});

new Vue({
  el: '#app',
  data:{
    currentTab:"com1",
    tabs:["com1", "com2", "com3"]
  },
    computed: {
		//currentTabComponent可以是已注册组件的名字,或一个组件的选项对象        
        currentTabComponent: function () {
            return this.currentTab
        }
    }
})

过渡和动画

过渡的类名

  1. [name]-enter:进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。

  2. [name]-enter-active:进入过渡生效时的状态。在元素被插入之前生效,在过渡/动画完成之后移除。用来定义进入过渡的过程时间,延迟和曲线函数。

  3. [name]-enter-to:进入过渡的结束状态。在元素被插入之后下一帧生效 ,在过渡/动画完成之后移除。

  4. [name]-leave:离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。

  5. [name]-leave-active:离开过渡生效时的状态在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。

  6. [name]-leave-to:定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 ,在过渡/动画完成之后移除。

    image.png

单元素/组件的过渡

Vue 提供了 transition 的封装组件,在下列情形中,可以给任何元素和组件添加进入/离开过渡:

  • 条件渲染 (使用 v-if)
  • 条件展示 (使用 v-show)
  • 动态组件
  • 组件根节点

css过渡例子

  1. transition包裹元素,并命名

    <button @click="show = !show">切换</button>
    <transition name="fade">
        <p v-if="show">hello</p>
    </transition>
    
  2. 定义类

    //进入过渡
    .fade-enter-active {
      transition: all .3s ease;
    }
    //离开过渡
    .fade-leave-active {
      transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
    }
    //开始和结束的状态
    .fade-enter, .fade-leave-to {
      transform: translateX(10px);
      opacity: 0;
    }
    

css动画例子

  1. transition包裹元素,并命名

    <button @click="show = !show">切换</button>
    <transition name="bounce">
        <p v-if="show">hello</p>
    </transition>
    
  2. 定义类

    //进入动画
    .bounce-enter-active {
      animation: bounce-in .5s;
    }
    //离开动画
    .bounce-leave-active {
      animation: bounce-in .5s reverse;
    }
    
    //定义动画关键帧
    @keyframes bounce-in {
      0% {
        transform: scale(0);
      }
      50% {
        transform: scale(1.5);
      }
      100% {
        transform: scale(1);
      }
    }
    

多个元素/组件过渡

多个元素

<transition>
  <table v-if="items.length > 0"></table>
  <p v-else>Sorry, no items found.</p>
</transition>

当有相同标签名的元素切换时,需要通过 key 设置唯一的值来标记以让 Vue 区分它们

<transition>
  <button v-if="isEditing" key="save">Save</button>
  <button v-else key="edit">Edit</button>
</transition>

多个组件

不需要使用 key 。相反,需要使用动态组件

<input type="radio" value="com1" id="1"  v-model="view">com1
<input type="radio" value="com2" id="2"  v-model="view">com2
<transition name="fade" mode="out-in">
  <component v-bind:is="view"></component>
</transition>
new Vue({
  el: '#app',
  data: {
    view: 'com1'
  },
  components: {
    'com1': {template: '<div>Component A</div>'},
    'com2': {template: '<div>Component B</div>'}
  }
})
.fade-enter-active, .fade-leave-active {
  transition: opacity .3s ease;
}
.fade-enter, .fade-leave-to {
  opacity: 0;
}

过渡模式

  • in-out:新元素先进行过渡,完成之后当前元素过渡离开
  • out-in:当前元素先进行过渡,完成之后新元素过渡进入

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) {...},
      enter: function (el, done) { //当与 CSS 结合使用时,回调函数 done 是可选的
        // ...
        done()
      },
      afterEnter: function (el) {...},
      enterCancelled: function (el) {...},
    
      // 离开时
      beforeLeave: function (el) {...},
      leave: function (el, done) {// 当与 CSS 结合使用时,回调函数 done 是可选的
        // ...
        done()
      },
      afterLeave: function (el) {...},// leaveCancelled 只用于 v-show 中
      leaveCancelled: function (el) {...}
    }
    
  • 使用Velocity.js的例子

    <script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>
    <div id="app">
      <button @click="show = !show">
        Toggle
      </button>
      <transition
        v-on:before-enter="beforeEnter"
        v-on:enter="enter"
        v-on:leave="leave"
        v-bind:css="false"
      >
        <p v-if="show">
          Demo
        </p>
      </transition>
    </div>
    
    new Vue({
      el: '#pp
      data: {
        show: false
      },
      methods: {
        beforeEnter: function (el) {
          el.style.opacity = 0
          el.style.transformOrigin = 'left'
        },
        enter: function (el, done) {
          Velocity(el, { opacity: 1, fontSize: '1.4em' }, { duration: 300 })
          Velocity(el, { fontSize: '1em' }, { complete: done })
        },
        leave: function (el, done) {
          Velocity(el, { translateX: '15px', rotateZ: '50deg' }, { duration: 600 })
          Velocity(el, { rotateZ: '100deg' }, { loop: 2 })
          Velocity(el, {
            rotateZ: '45deg',
            translateY: '30px',
            translateX: '30px',
            opacity: 0
          }, { complete: done })
        }
      }
    })
    

初始渲染的过渡

可以通过 appear 设置节点在初始渲染的过渡,只渲染一次

<transition
  appear
  appear-class="enter-class"
  appear-active-class="enter-active-class"
  appear-to-class="enter-to-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>

列表过渡

同v-for渲染列表时,使用<transition-group>组件。需要提供key,此时过渡模式不可用。

不同于 transition,它会以一个真实元素呈现:默认为一个 span。你也可以通过 tag attribute 更换为其他元素。

<div id="app">
  <!--... -->
  <transition-group name="list" tag="p">
    <span v-for="item in items" :key="item">
      {{ item }}
    </span>
  </transition-group>
</div>

其他

混入

混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。当组件使用混入对象时,所有混入对象的选项将被混入进该组件本身的选项。

  1. 假设要混入一个方法
    const userMixin={
      methods:{
        getInfo(){
          //Fetch API 提供了一个获取资源的接口(包括跨域请求)
          return fetch(`/api/user/${userId}`)
          .then((res)=>res.json())
        }
      }
    }
    
  2. 使用
    import userMixin from './mixins/user'
    
    Vue.component('user-com',{
      mixins:[userMixin],//混入
      //...
      mounted(){
        //这个时候已经可以使用混合进来的方法了
        this.getInfo(this.userId)
        .then((res)=>{...})
      }
    })
    

当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”

  1. 数据对象同名,合并数据
var mixin = {
  data: function () {
    return {
      message: 'hello',
      foo: 'abc'
    }
  }
}

new Vue({
  mixins: [mixin],
  data: function () {
    return {
      message: 'goodbye',
      bar: 'def'
    }
  },
})

//这时候的data:
//{ message: "goodbye", foo: "abc", bar: "def" }
  1. 钩子函数同名,合并方法
var mixin = {
  created: function () {
    console.log('混入对象的钩子被调用')
  }
}
new Vue({
  mixins: [mixin],
  created: function () {
    console.log('组件钩子被调用')
  }
})

//这时候调用时:
// => "混入对象的钩子被调用"
// => "组件钩子被调用"

值为对象的选项,例如 methods、components 和 directives,将被合并为同一个对象。

** 全局混入**

Vue.mixin({...})

自定义指令

注册

  • 全局注册
    Vue.directive('focus', {
      inserted: function (el) {
        el.focus()
      }
    })
    
  • 局部注册
    directives: {
     focus: {
       // 指令的定义
       inserted: function (el) {
         el.focus()
       }
     }
    }
    

使用:<input v-focus>

钩子函数

  • bind:只调用一次,指令第一次绑定到元素时调用。
  • inserted:被绑定元素插入父节点时调用
  • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
  • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
  • unbind:只调用一次,指令与元素解绑时调用。

钩子函数参数

  • el:指令所绑定的元素,可以用来直接操作 DOM。
  • binding:一个对象,包含以下 property:
    • name:指令名,不包括 v- 前缀。
    • value:指令的绑定值,例如:v-my="1 + 1" 中,绑定值为 2
    • oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用
    • expression:字符串形式的指令表达式。例如 v-my="1 + 1" 中,表达式为 "1 + 1"。
    • arg:传给指令的参数。例如 v-my:foo 中,参数为 "foo"
    • modifiers:一个包含修饰符的对象。例如:v-my.foo.bar 中,修饰符对象为 { foo: true, bar: true }。
  • vnode:Vue 编译生成的虚拟节点。
  • oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用

插件

插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制——一般有下面几种:

  1. 添加全局方法或者 property。如:vue-custom-element
  2. 添加全局资源:指令/过滤器/过渡等。如 vue-touch
  3. 通过全局混入来添加一些组件选项。如 vue-router
  4. 添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。
  5. 一个库,提供自己的 API,同时提供上面提到的一个或多个功能。如 vue-router
//全局使用
Vue.use(MyPlugin)

//传入选项
Vue.use(MyPlugin, { someOption: true })
// 用 Browserify 或 webpack 提供的 CommonJS 模块环境时
var Vue = require('vue')
var VueRouter = require('vue-router')

// 不要忘了调用此方法
Vue.use(VueRouter)

过滤器

注册

  • 全局注册
    //将字母转为大写
    Vue.filter('capitalize', function (value) {
      if (!value) return ''
      value = value.toString()
      return value.charAt(0).toUpperCase() + value.slice(1)
    })
    
  • 局部注册
    //在组件选项中:
    filters: {
      capitalize: function (value) {
        //...
      }
    }
    

使用

过滤器可以用在两个地方:

  • 双花括号插值
    {{ message | capitalize }}
    
  • v-bind 表达式
    <div :id="rawId | formatId"></div>
    

链式调用

在下面的例子中,首先会调用round过滤器,它返回的结果会传递给formatCost过滤器进行处理:

{{ productOneCost | round |formatCost}}

参数

{{ message | filterA('arg1', arg2) }}

其中 message 的值作为第一个参数,普通字符串 'arg1' 作为第二个参数,表达式 arg2 的值作为第三个参数。

单文件组件

全局组件的缺点

  • 全局定义强制要求每个 component 中的命名不得重复
  • 字符串模板缺乏语法高亮,看不清晰
  • 不支持 CSS ,意味着当 HTML 和 JavaScript 组件化时,CSS 明显被遗漏
  • 没有构建步骤,限制只能使用 HTML 和 ES5 JavaScript,而不能使用预处理器,如 Pug 和 Babel

文件扩展名为 .vue 的 单文件组件为以上所有问题提供了解决方法,并且还可以使用 webpack 或 Browserify 等构建工具。以下是一个文件名为Hello.vue的简单实例

<template>
    <p>Hello!{{msg}}</p>
</template>

<script>
export default{
    name:'test',
    props:{
        msg:{
            type:String,
            default:"test msg"
        }
    },
    methods:{}
    data () {
        return {
            name: 'lihua'
        }
    }
}
</script>

<style scoped>
p{color: antiquewhite;}
</style>

在其他地方中使用

<template>
    <div id="app">
        <test></test>
    </div>
</template>

<script>
//1. 引入组件
import test form './components/test.vue'
export default{
    name:'app',
    //2. 注册组件
    components:{
        test
    }
}
</script>

render函数

使用模板并不是唯一能让vue知道应该在页面显示什么内容的方式:还可以使用render函数。当你将一个函数传递给Vue实例的render属性时,该函数会传入一个createElement函数,可以使用它来指定需要在页面上显示的HTML。

new Vue({
    el:'#app',
    render(createELement){
        return createELement('h1',"hello world!")
    }
})

createElement接收3个参数:标签名称、包含配置信息的数据对象(诸如HTML特性、属性、事件侦听器以及要绑定的class和style等)和一个子节点或是包含子节点的数组

标签名称

标签名称是最简单的,也是唯一一个必需的参数。它可以是一个字符串,或是一个返回字符串的函数。render函数中也可以访问this,所以可以将标签名称设置为data对象的某个属性、prop、计算属性或是任何类似的东西,例如下方代码,这是render函数优于template的一个很大的优势:

new Vue({
    el:'#app',
    render(createELement){
        return createELement('this.tagName',"hello world!")
    },
    data:{
        tagName:"h1"
    }
})

数据对象

数据对象是设置一系列配置属性的地方,这些属性会影响生成的组件或元素。

例如,对于<custom-button type="submit" v-bind:text="buttonText">。type是传递给组件的一个HTML属性,text是一个组件prop,与变量buttonText绑定。现在使用createElement来实现相同的示例:

new Vue({
    el:'#app',
    render(createELement){
        return createELement('custom-button',{
            attrs:{
                type:"submit"
            },
            props:{
                text:this.buttonText
            }
        })
    },
})

下面的示例,它列出了所有的选项:

{
    //html特性
    attrs:{
        type:"submit"
    },
        
    //传递给组件的prop
    props:{
        test:"单击我!"
    },
    
    //DOM属性,比如innerHtml
    domProps:{
        innerHtml:'...'
    },
        
        
    //事件监听器
    on:{
        click:this.handleClick
    },
    
    //当组件是某个组件的子组件时使用,等同于:slot="Slot"
    slot:'Slot',
    
    //用于某个循环产生的数组,等同于key="Key"
    key:"Key",
    
    //与ref="Ref"相同
    ref="Ref",
        
    class:['Aclass',{'Bclass':true}],
    style:{backgroundColor:'red'}
}

子节点

用来设置元素的子节点的。它可以是一个数组也可以是一个字符串。如果你不需要设置数据对象,只是需要配置子节点,那么可以将子节点设置为第二个参数,而不是原来的第三个参数。

<div>
    <button v-on:click="count++">单击增加计数</button>
    <p>你已经单击了{{count}}次</p>
</div>
render(createELement){
    return createELement('div',[
        createELement('button',{
            on:{
                click:()=>this.count++
            }
        },
        '单击增加计数'),
        createELement('p','你已经单击了${{this.count}}次')
    ])
},

JSX

babel-plugin-transform-vue-jsx插件的帮助下,可以使用JSX来编写render函数createElement函数通常会有个较短的别名,h

安装了插件之后你可以写成:

render(){
    return(
    	<div>
        	<button v-on:click="count++">单击增加计数</button>
   		    <p>你已经单击了{{count}}次</p>
        </div>
    )
}

除了像在模板中那样,可以导入并使用组件——只要将组件的名称设置为标签的名称——还可以像React那样导入组件。如果导入的组件存储在首字母大写的变量中,则不需要在components对象中指定或是使用Vue.component()函数注册,就可以使用该组件。例如:

import MyComponent from './components/MyComponent.vue'
new Vue({
    el:'#app',
    render(){
        return(
        	<MyComponent/>
        )
    }
})

JSX的展开操作符也同样得到支持。可以在属性对象上使用展开操作符,它会与其他已经设置的属性相合并,一起应用到元素上:

render(){
    const props={
        class:'world',
        href:'...'
    };
    return(
    	<a {...props}>hello</a>
    );
}

实例与笔记源代码

posted @ 2020-09-01 15:34  sanhuamao  阅读(139)  评论(0编辑  收藏  举报