典型 MVVM 前端框架 Vue

1、引入

   <script src="http://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
    <script src="https://unpkg.com/vue"></script>

2、简单指令

一、起步文本插值javascript

 <div id="app">
      {{message}}
  </div>
  var app=new Vue({
     el: "#app",
     data: {
        message:"hello"
     }
  })

Vue.js 的核心是一个容许采用简洁的模板语法来声明式的将数据渲染进 DOM 的系统: 如今数据和 DOM 已经被绑定在一块儿,全部的元素都是响应式的。css

二、声明式渲染html

 <div id="app-2">
    <span v-bind:title="message">
      鼠标悬停几秒钟查看此处动态绑定的提示信息!
    </span>
  </div>
  
  var app2=new Vue({
    el:'#app-2',
    data:{
      message:'页面加载于'+new Date().toLocaleString()
    }
  })

指令带有前缀 v-,以表示它们是 Vue 提供的特殊属性 它们会在渲染的 DOM 上应用特殊的响应式行为。简言之,这里该指令的做用是:“将这个元素节点的 title 属性和 Vue 实例message属性保持一致”。前端

三、条件与循环vue

 <div id="app-3">
    <p v-if="seen">
      如今你看到我了
    </p>
  </div>
  var app3=new Vue({
    el:'#app-3',
    data:{
      seen:true
    }
  })

绑定 DOM 文本到数据,也能够绑定 DOM 结构到数据java

 <div id="app-4">
    <ol>
      <li v-for="todo in todos">
        {{todo.text}}}
      </li>
    </ol>
  </div>
  var app4=new Vue({
    el:"#app-4",
    data:{
      todos:[
        {text:"学习javascript"},
        {text:"学习Vue"},
        {text:"整个牛项目"}
      ]
    }
  })

v-for 指令能够绑定数组的数据来渲染一个项目列表node

四、处理用户输入webpack

 <div id="app-5">
    <p>{{message}}</p>
    <button v-on:click="reverseMessage">逆转消息</button>
  </div>

绑定 DOM 文本到数据,也能够绑定 DOM 结构到数据 没有触碰 DOM,DOM 操做都由 Vue 来处理,你编写的代码不须要关注底层逻辑。ios

 var app5=new Vue({
    el:"#app-5",
    data:{
      message:"Hello Vue.js"
    },
    methods:{
      reverseMessage:function(){
        this.message=this.message.split('').reverse().join('')
      }
    }
  })

v-model 指令,它能轻松实现表单输入和应用状态之间的双向绑定web

<div id="app-6">
  <p>{{message}}</p>
  <input v-model="message">
</div>

3、组件化应用构建

1.组件系统:容许咱们使用小型、独立和一般可复用的组件构建大型应用。(仔细想一想,几乎任意类型的应用界面均可以抽象为一个组件树)

  • 在 Vue 里,一个组件本质上是一个拥有预约义选项的一个 Vue 实例,在 Vue 中注册组件很简单:

    // 定义名为 todo-item 的新组件
    Vue.component('todo-item', {
    template: '<li>这是个待办项</li>'
    })
  • 如今你能够用它构建另外一个组件模板:

    <ol>
    <!-- 建立一个 todo-item 组件的实例 -->
    <todo-item></todo-item>
    </ol>
  • 可是这样会为每一个待办项渲染一样的文本,这看起来并不炫酷,咱们应该能将数据从父做用域传到子组件。让咱们来修改一下组件的定义,使之可以接受一个属性:

    Vue.component('todo-item', {
      // todo-item 组件如今接受一个
      // "prop",相似于一个自定义属性
      // 这个属性名为 todo。
      props: ['todo'],
      template: '<li>{{ todo.text }}</li>'
    })
  • 如今,咱们可使用 v-bind 指令将 todo 传到每个重复的组件中:

    <div id="app-7">
      <ol>
        <!--
          如今咱们为每一个 todo-item 提供 todo 对象
          todo 对象是变量,即其内容能够是动态的。
          咱们也须要为每一个组件提供一个“key”,晚些时候咱们会作个解释。
        -->
        <todo-item
          v-for="item in groceryList"
          v-bind:todo="item"
          v-bind:key="item.id">
        </todo-item>
      </ol>
    </div>
    Vue.component('todo-item', {
      props: ['todo'],
      template: '<li>{{ todo.text }}</li>'
    })
    var app7 = new Vue({
      el: '#app-7',
      data: {
        groceryList: [
          { id: 0, text: '蔬菜' },
          { id: 1, text: '奶酪' },
          { id: 2, text: '随便其余什么人吃的东西' }
        ]
      }
    })
  • 这只是一个假设的例子,可是咱们已经设法将应用分割成了两个更小的单元,子单元经过 props 接口实现了与父单元很好的解耦。咱们如今能够进一步为咱们的 todo-item 组件实现更复杂的模板和逻辑的改进,而不会影响到父单元。

    使用了组件的应用模板是什么样的例子:

     <div id="app">
        <app-nav></app-nav>
        <app-view>
          <app-sidebar></app-sidebar>
          <app-content></app-content>
        </app-view>
      </div>

4、模板语法:

Vue.js 使用了基于 HTML 的模板语法,容许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。全部 Vue.js 的模板都是合法的 HTML ,因此能被遵循规范的浏览器和 HTML 解析器解析。在底层的实现上,Vue 将模板编译成虚拟 DOM 渲染函数。结合响应系统,在应用状态改变时,Vue 可以智能地计算出从新渲染组件的最小代价并应用到 DOM 操做上。若是你熟悉虚拟 DOM 而且偏心 JavaScript 的原始力量,你也能够不用模板,直接写渲染 (render) 函数,使用可选的 JSX 语法。

插值

(1) 文本

数据绑定最多见的形式就是使用“Mustache”语法 (双大括号) 的文本插值:

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

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

经过使用 v-once 指令,你也能执行一次性地插值,当数据改变时,插值处的内容不会更新。但请留心这会影响到该节点上全部的数据绑定

<span v-once>这个将不会改变: {{ msg }}</span>

(2) 原始 HTML

双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你须要使用 v-html 指令:

<p>Using mustaches: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>

(3) 特性

Mustache 语法不能做用在 HTML 特性上,遇到这种状况应该使用 v-bind 指令:

<div v-bind:id="dynamicId"></div>

(4) 使用 JavaScript 表达式

迄今为止,在咱们的模板中,咱们一直都只绑定简单的属性键值。但实际上,对于全部的数据绑定,Vue.js 都提供了彻底的 JavaScript 表达式支持

{ number + 1 }}
    
{{ ok ? 'YES' : 'NO' }}
    
{{ message.split('').reverse().join('') }}

<div v-bind:id="'list-' + id"></div>

模板表达式都被放在沙盒中,只能访问全局变量的一个白名单,如 Math  Date 。你不该该在模板表达式中试图访问用户定义的全局变量。

(5) 指令 (Directives) 是带有 v- 前缀的特殊属性。当表达式的值改变时,将其产生的连带影响,响应式地做用于 DOM。

<p v-if="seen">如今你看到我了</p>

这里,v-if 指令将根据表达式 seen 的值的真假来插入/移除元素。

  • 参数 

    一些指令可以接收一个“参数”,在指令名称以后以冒号表示。例如,v-bind 指令能够用于响应式地更新 HTML 属性:

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

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

    另外一个例子是 v-on 指令,它用于监听 DOM 事件:

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

    <form v-on:submit.prevent="onSubmit">...</form>

    缩写: 

    • v-bind 缩写

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

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

      它们看起来可能与普通的 HTML 略有不一样,但 : 与 @ 对于特性名来讲都是合法字符,在全部支持 Vue.js 的浏览器都能被正确地解析。并且,它们不会出如今最终渲染的标记中。

5、计算属性

模板内的表达式很是便利,可是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板太重且难以维护。例如:

<div id="example">
    {{ message.split('').reverse().join('') }}
</div>

在这个地方,模板再也不是简单的声明式逻辑。你必须看一段时间才能意识到,这里是想要显示变量 message 的翻转字符串。当你想要在模板中屡次引用此处的翻转字符串时,就会更加难以处理。因此,对于任何复杂逻辑,你都应当使用计算属性。

基础例子

<div id="example">
    <p>Original message: "{{ message }}"</p>
    <p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>

var vm = new Vue({
    el: '#example',
    data: {
      message: 'Hello'
    },
    computed: {
      // 计算属性的 getter
      reversedMessage: function () {
        // `this` 指向 vm 实例
        return this.message.split('').reverse().join('')
      }
    }
})

结果:

Original message: “Hello”
Computed reversed message: “olleH”

这里咱们声明了一个计算属性 reversedMessage。咱们提供的函数将用做属性 vm.reversedMessage  getter 函数:

console.log(vm.reversedMessage) // => 'olleH'
vm.message = 'Goodbye'
console.log(vm.reversedMessage) // => 'eybdooG'

你能够像绑定普通属性同样在模板中绑定计算属性。Vue 知道 vm.reversedMessage 依赖于 vm.message,所以当 vm.message 发生改变时,全部依赖 vm.reversedMessage 的绑定也会更新。并且最妙的是咱们已经以声明的方式建立了这种依赖关系:计算属性的 getter 函数是没有反作用 (side effect) 的,这使它更易于测试和理解。

计算属性缓存 vs 方法

<p>Reversed message: "{{ reversedMessage() }}"</p>
// 在组件中
methods: {
  reversedMessage: function () {
    return this.message.split('').reverse().join('')
  }
}

咱们能够将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是彻底相同的。然而,不一样的是计算属性是基于它们的依赖进行缓存的。 

计算属性只有在它的相关依赖发生改变时才会从新求值。 这就意味着只要 message 尚未发生改变,屡次访问 reversedMessage 计算属性会当即返回以前的计算结果,而没必要再次执行函数。这也一样意味着下面的计算属性将再也不更新,由于 Date.now() 不是响应式依赖:

computed: {
    now: function () {
      return Date.now()
    }
}

相比之下,每当触发从新渲染时,调用方法将总会再次执行函数。咱们为何须要缓存?假设咱们有一个性能开销比较大的的计算属性 A,它须要遍历一个巨大的数组并作大量的计算。而后咱们可能有其余的计算属性依赖于 A 。若是没有缓存,咱们将不可避免的屡次执行 A 的 getter!若是你不但愿有缓存,请用方法来替代。

计算属性 vs 侦听属性

Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变更:侦听属性。 当你有一些数据须要随着其它数据变更而变更时,你很容易滥用 watch——特别是若是你以前使用过 AngularJS。 然而,一般更好的作法是使用计算属性而不是命令式的 watch 回调。细想一下这个例子:

<div id="demo">{{ fullName }}</div>
var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar',
    fullName: 'Foo Bar'
  },
  watch: {
    firstName: function (val) {
      this.fullName = val + ' ' + this.lastName
    },
    lastName: function (val) {
      this.fullName = this.firstName + ' ' + val
    }
  }
})

上面代码是命令式且重复的。将它与计算属性的版本进行比较:

var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }
})
..

如今再运行 vm.fullName = ‘John Doe’ 时,setter 会被调用,vm.firstName  vm.lastName 也会相应地被更新。

侦听器

虽然计算属性在大多数状况下更合适,但有时也须要一个自定义的侦听器。 这就是为何 Vue 经过 watch 选项提供了一个更通用的方法,来响应数据的变化。

当须要在数据变化时执行异步或开销较大的操做时,这个方式是最有用的。

<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
  <meta charset="UTF-8">
  <title>api</title>
  <link href="http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet">
  <script src="http://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
  <script src="https://unpkg.com/vue"></script>
  <script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script>
        
</head>
<body>
  <div id="watch-example">
    <p>
      Ask a yes/no question:
      <input v-model="question">
    </p>
    <p>{{ answer }}</p>
  </div>
</body>
<script>
  var watchExampleVM = new Vue({
    el: '#watch-example',
    data: {
      question: '',
      answer: 'I cannot give you an answer until you ask a question!'
    },
    watch: {
      // 若是 `question` 发生改变,这个函数就会运行
      question: function (newQuestion) {
        this.answer = 'Waiting for you to stop typing...'
        this.getAnswer()
      }
    },
    methods: {
      // `_.debounce` 是一个经过 Lodash 限制操做频率的函数。
      // 在这个例子中,咱们但愿限制访问 yesno.wtf/api 的频率
      // AJAX 请求直到用户输入完毕才会发出。想要了解更多关于
      // `_.debounce` 函数 (及其近亲 `_.throttle`) 的知识,
      // 请参考:https://lodash.com/docs#debounce
      getAnswer: _.debounce(
        function () {
          if (this.question.indexOf('?') === -1) {
              this.answer = 'Questions usually contain a question mark. ;-)'
              return
          }
          this.answer = 'Thinking...'
          var vm = this
          axios.get('https://yesno.wtf/api')
            .then(function (response) {
                vm.answer = _.capitalize(response.data.answer)
            })
            .catch(function (error) {
                vm.answer = 'Error! Could not reach the API. ' + error
            })
        },
        // 这是咱们为断定用户中止输入等待的毫秒数
        500
      )
    }
  })
</script>
</html>

在这个示例中,使用 watch 选项容许咱们执行异步操做 (访问一个 API),限制咱们执行该操做的频率,并在咱们获得最终结果前,设置中间状态。这些都是计算属性没法作到的。

5、Class 与 Style 绑定

绑定 HTML Class

对象语法

一、咱们能够传给 v-bind:class 一个对象,以动态地切换 class:

<div v-bind:class="{ active: isActive }"></div>

上面的语法表示 active 这个 class 存在与否将取决于数据属性 isActive  truthiness

二、你能够在对象中传入更多属性来动态切换多个 class。此外,v-bind:class指令也能够与普通的 class 属性共存。当有以下模板:

<div class="static"
  v-bind:class="{ active: isActive, 'text-danger': hasError }">
</div>
和以下 data:
data: {
    isActive: true,
    hasError: false
}

结果渲染为:

当 isActive 或者 hasError 变化时,class 列表将相应地更新。例如,若是 hasError 的值为 true,class 列表将变为 “static active text-danger”。

三、绑定的数据对象没必要内联定义在模板里:

<div v-bind:class="classObject"></div>
data: {
    classObject: {
      active: true,
      'text-danger': false
    }
}

四、渲染的结果和上面同样。咱们也能够在这里绑定一个返回对象的计算属性。这是一个经常使用且强大的模式:

 <div v-bind:class="classObject"></div>
  data: {
    isActive: true,
    error: null
  },
  computed: {
    classObject: function () {
      return {
        active: this.isActive && !this.error,
        'text-danger': this.error && this.error.type === 'fatal'
      }
    }
  }

数组语法

一、咱们能够把一个数组传给 v-bind:class,以应用一个 class 列表:

 <div v-bind:class="[activeClass, errorClass]"></div>
  data: {
    activeClass: 'active',
    errorClass: 'text-danger'
  }

渲染为:

<div class="active text-danger"></div>

二、若是你也想根据条件切换列表中的 class,能够用三元表达式:

<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
这样写将始终添加 errorClass,可是只有在 isActive 是 truthy[1] 时才添加 activeClass。

三、不过,当有多个条件 class 时这样写有些繁琐。因此在数组语法中也可使用对象语法:

<div v-bind:class="[{ active: isActive }, errorClass]"></div>
用在组件上

当在一个自定义组件上使用 class 属性时,这些类将被添加到该组件的根元素上面。这个元素上已经存在的类不会被覆盖。

例如,若是你声明了这个组件:

Vue.component('my-component', {
    template: '<p class="foo bar">Hi</p>'
})

而后在使用它的时候添加一些 class:

<my-component class="baz boo"></my-component>

HTML 将被渲染为:

<p class="foo bar baz boo">Hi</p>

绑定内联样式

对象语法

v-bind:style 的对象语法十分直观——看着很是像 CSS,但实际上是一个 JavaScript 对象。CSS 属性名能够用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用单引号括起来) 来命名:

<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data: {
  activeColor: 'red',
  fontSize: 30
}

直接绑定到一个样式对象一般更好,这会让模板更清晰:

<div v-bind:style="styleObject"></div>
data: {
  styleObject: {
    color: 'red',
    fontSize: '13px'
  }
}

一样的,对象语法经常结合返回对象的计算属性使用。

数组语法

v-bind:style 的数组语法能够将多个样式对象应用到同一个元素上:

<div v-bind:style="[baseStyles, overridingStyles]"></div>

自动添加前缀 

 v-bind:style 使用须要添加浏览器引擎前缀的 CSS 属性时,如 transform,Vue.js 会自动侦测并添加相应的前缀。

多重值 

从 2.3.0 起你能够为 style 绑定中的属性提供一个包含多个值的数组,经常使用于提供多个带前缀的值,例如:

<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>

这样写只会渲染数组中最后一个被浏览器支持的值。在本例中,若是浏览器支持不带浏览器前缀的 flexbox,那么就只会渲染 display: flex 

6、条件渲染

v-if

<h1 v-if="ok">Yes</h1>
<h1 v-else>No</h1>

在 元素上使用 v-if 条件渲染分组 由于 v-if 是一个指令,因此必须将它添加到一个元素上。可是若是想切换多个元素呢?此时能够把一个 元素当作不可见的包裹元素,并在上面使用 v-if。最终的渲染结果将不包含<template>元素。

<template v-if="ok">
    <h1>Title</h1>
    <p>Paragraph 1</p>
    <p>Paragraph 2</p>
</template>

v-else

你可使用 v-else 指令来表示 v-if 的“else 块”:

<div v-if="Math.random() > 0.5">
  Now you see me
</div>
<div v-else>
  Now you don't
</div>

v-else 元素必须紧跟在带 v-if 或者 v-else-if 的元素的后面,不然它将不会被识别。

v-else-if

v-else-if,顾名思义,充当 v-if 的“else-if 块”,能够连续使用:相似于 v-else,v-else-if 也必须紧跟在带 v-if 或者 v-else-if的元素以后。用 key 管理可复用的元素 ##### Vue 会尽量高效地渲染元素,一般会复用已有元素而不是从头开始渲染。这么作除了使 Vue 变得很是快以外,还有其它一些好处。例如,若是你容许用户在不一样的登陆方式之间切换:

<template v-if="loginType === 'username'">
    <label>Username</label>
    <input placeholder="Enter your username">
</template>
<template v-else>
    <label>Email</label>
    <input placeholder="Enter your email address">
</template>

那么在上面的代码中切换 loginType 将不会清除用户已经输入的内容。由于两个模板使用了相同的元素, 不会被替换掉——仅仅是替换了它的 placeholder

这样也不老是符合实际需求,因此 Vue 为你提供了一种方式来表达“这两个元素是彻底独立的,不要复用它们”。只需添加一个具备惟一值的 key 属性便可:

<template v-if="loginType === 'username'">
  <label>Username</label>
  <input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
  <label>Email</label>
  <input placeholder="Enter your email address" key="email-input">
</template>

v-show

另外一个用于根据条件展现元素的选项是 v-show 指令。用法大体同样:

<h1 v-show="ok">Hello!</h1>

不一样的是带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS 属性 display。

v-if 是“真正”的条件渲染,由于它会确保在切换过程当中条件块内的事件监听器和子组件适当地被销毁和重建。

v-if 也是惰性的:若是在初始渲染时条件为假,则什么也不作——直到条件第一次变为真时,才会开始渲染条件块。 相比之下,v-show 就简单得多——无论初始条件是什么,元素老是会被渲染,而且只是简单地基于 CSS 进行切换。 通常来讲,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。所以,若是须要很是频繁地切换,则使用 v-show 较好;若是在运行时条件不多改变,则使用 v-if 较好。

v-if 与 v-for 一块儿使用

当 v-if 与 v-for 一块儿使用时,v-for 具备比 v-if 更高的优先级。

7、列表渲染

 v-for 把一个数组对应为一组元素:

<ul id="example-1">
  <li v-for="item in items">
    {{ item.message }}
  </li>
</ul>
var example1 = new Vue({
  el: '#example-1',
  data: {
    items: [
      { message: 'Foo' },
      { message: 'Bar' }
    ]
  }
})

 v-for 块中,咱们拥有对父做用域属性的彻底访问权限。v-for 还支持一个可选的第二个参数为当前项的索引。

<ul id="example-2">
  <li v-for="(item, index) in items">
    {{ parentMessage }} - {{ index }} - {{ item.message }}
  </li>
</ul>
var example2 = new Vue({
  el: '#example-2',
  data: {
    parentMessage: 'Parent',
    items: [
      { message: 'Foo' },
      { message: 'Bar' }
    ]
  }
})

你也能够用 of 替代 in 做为分隔符,由于它是最接近 JavaScript 迭代器的语法:

<div v-for="item of items"></div>

对象的 v-for

  • 你能够用 v-for 经过一个对象的属性来迭代。

    <ul id="v-for-object" class="demo">
        <li v-for="value in object">
          {{ value }}
        </li>
    </ul>
    new Vue({
    el: '#v-for-object',
    data: {
      object: {
        firstName: 'John',
        lastName: 'Doe',
        age: 30
      }
    }
    })
  • 你也能够提供第二个的参数为键名:

    <div v-for="(value, key) in object">
    {{ key }}: {{ value }}
    </div>
  • 第三个参数为索引:

    <div v-for="(value, key, index) in object">
    {{ index }}. {{ key }}: {{ value }}
    </div>`

在遍历对象时,是按 Object.keys() 的结果遍历,可是不能保证它的结果在不一样的 JavaScript 引擎下是一致的。

key

当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。若是数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每一个元素,而且确保它在特定索引下显示已被渲染过的每一个元素。

这个默认的模式是高效的,可是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。

为了给 Vue 一个提示,以便它能跟踪每一个节点的身份,从而重用和从新排序现有元素,你须要为每项提供一个惟一 key 属性。理想的 key 值是每项都有的且惟一的 id,它的工做方式相似于一个属性,因此你须要用 v-bind 来绑定动态值 (在这里使用简写):

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

建议尽量在使用 v-for 时提供 key,除非遍历输出的 DOM 内容很是简单,或者是刻意依赖默认行为以获取性能上的提高。

数组更新检测

(1) 变异方法

Vue 包含一组观察数组的变异方法,因此它们也将会触发视图更新。这些方法以下:

push() 
pop() 
shift() 
unshift() 
splice() 
sort() 
reverse()

(2) 替换数组

变异方法 (mutation method),顾名思义,会改变被这些方法调用的原始数组。相比之下,也有非变异 (non-mutating method) 方法,例如:filter(), concat()  slice() 。这些不会改变原始数组,但老是返回一个新数组。当使用非变异方法时,能够用新数组替换旧数组:

example1.items = example1.items.filter(function (item) {
  return item.message.match(/Foo/)
})

(3) 注意事项

因为 JavaScript 的限制,Vue 不能检测如下变更的数组:

  • 当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue

  • 当你修改数组的长度时,例如:vm.items.length = newLength

为了解决第一类问题,如下两种方式均可以实现和 vm.items[indexOfItem] = newValue 相同的效果,同时也将触发状态更新:

// Vue.set
Vue.set(example1.items, indexOfItem, newValue)
// Array.prototype.splice
example1.items.splice(indexOfItem, 1, newValue)
为了解决第二类问题,你可使用 splice:
example1.items.splice(newLength)

对象更改检测注意事项

(1) 仍是因为 JavaScript 的限制,Vue 不能检测对象属性的添加或删除:

var vm = new Vue({
  data: {
    a: 1
  }
})
// `vm.a` 如今是响应式的

vm.b = 2
// `vm.b` 不是响应式的

对于已经建立的实例,Vue 不能动态添加根级别的响应式属性。可是,可使用 Vue.set(object, key, value) 方法向嵌套对象添加响应式属性。例如,对于:

var vm = new Vue({
    data: {
      userProfile: {
        name: 'Anika'
      }
    }
})

你能够添加一个新的 age 属性到嵌套的 userProfile 对象:

Vue.set(vm.userProfile, 'age', 27)

你还可使用 vm.$set 实例方法,它只是全局 Vue.set 的别名:

this.$set(this.userProfile, 'age', 27)

(2) 有时你可能须要为已有对象赋予多个新属性,好比使用 Object.assign() 或 _.extend()。在这种状况下,你应该用两个对象的属性建立一个新的对象。因此,若是你想添加新的响应式属性,不要像这样:

Object.assign(this.userProfile, {
    age: 27,
    favoriteColor: 'Vue Green'
})

你应该这样作:

this.userProfile = Object.assign({}, this.userProfile, {
  age: 27,
  favoriteColor: 'Vue Green'
})

显示过滤/排序结果

有时,咱们想要显示一个数组的过滤或排序副本,而不实际改变或重置原始数据。在这种状况下,能够建立返回过滤或排序数组的计算属性。

<li v-for="n in evenNumbers">{{ n }}</li>
data: {
  numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
  evenNumbers: function () {
    return this.numbers.filter(function (number) {
      return number % 2 === 0
    })
  }
}

在计算属性不适用的状况下 (例如,在嵌套 v-for 循环中) 你可使用一个 method方法:

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

一段取值范围的 v-for

v-for 也能够取整数。在这种状况下,它将重复屡次模板。

<div>
    <span v-for="n in 10">{{ n }} </span>
</div>
v-for on a

相似于 v-if,你也能够利用带有 v-for 的 渲染多个元素。好比:

<ul>
 <template v-for="item in items">
  <li>{{ item.msg }}</li>
  <li class="divider"></li>
 </template>
</ul>
v-for with v-if

当它们处于同一节点,v-for 的优先级比 v-if 更高,这意味着 v-if 将分别重复运行于每一个 v-for 循环中。当你想为仅有的一些项渲染节点时,这种优先级的机制会十分有用,以下:

<li v-for="todo in todos" v-if="!todo.isComplete">
  {{ todo }}
</li>

上面的代码只传递了未 complete  todos。 而若是你的目的是有条件地跳过循环的执行,那么能够将 v-if 置于外层元素 (或 )上。如:

<ul v-if="todos.length">
    <li v-for="todo in todos">
      {{ todo }}
    </li>
</ul>
<p v-else>No todos left!</p>

一个组件的 v-for

在自定义组件里,你能够像任何普通元素同样用 v-for 

<my-component v-for="item in items" :key="item.id"></my-component>
2.2.0+ 的版本里,当在组件中使用 v-for 时,key 如今是必须的。然而,任何数据都不会被自动传递到组件里,由于组件有本身独立的做用域。为了把迭代数据传递到组件里,咱们要用 props 
 <my-component
    v-for="(item, index) in items"
    v-bind:item="item"
    v-bind:index="index"
    v-bind:key="item.id"
  ></my-component>

不自动将 item 注入到组件里的缘由是,这会使得组件与 v-for 的运做紧密耦合。明确组件数据的来源可以使组件在其余场合重复使用。 下面是一个简单的 todo list的完整例子:

<div id="todo-list-example">
    <input
      v-model="newTodoText"
      v-on:keyup.enter="addNewTodo"
      placeholder="Add a todo"
    >
<ul>
  <li
    is="todo-item"
    v-for="(todo, index) in todos"
    v-bind:key="todo.id"
    v-bind:title="todo.title"
    v-on:remove="todos.splice(index, 1)"
  ></li>
</ul>
</div>

注意这里的 is=”todo-item” 属性。这种作法在使用 DOM 模板时是十分必要的,由于在ul元素内只有li元素会被看做有效内容。这样作实现的效果与 相同,可是能够避开一些潜在的浏览器解析错误。查看 DOM 模板解析说明 来了解更多信息。

Vue.component('todo-item', {
template: '\
  <li>\
    {{ title }}\
    <button v-on:click="$emit(\'remove\')">X</button>\
  </li>\
',
props: ['title']
})

new Vue({
el: '#todo-list-example',
data: {
  newTodoText: '',
  todos: [
    {
      id: 1,
      title: 'Do the dishes',
    },
    {
      id: 2,
      title: 'Take out the trash',
    },
    {
      id: 3,
      title: 'Mow the lawn'
    }
  ],
  nextTodoId: 4
},
methods: {
  addNewTodo: function () {
    this.todos.push({
      id: this.nextTodoId++,
      title: this.newTodoText
    })
    this.newTodoText = ''
  }
}
})

8、监听事件

能够用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。

<div id="example-1">
    <button v-on:click="counter += 1">Add 1</button>
    <p>The button above has been clicked {{ counter }} times.</p>
</div>

var example1 = new Vue({
el: '#example-1',
    data: {
      counter: 0
    }
})

事件处理方法

然而许多事件处理逻辑会更为复杂,因此直接把 JavaScript 代码写在 v-on 指令中是不可行的。所以 v-on 还能够接收一个须要调用的方法名称。

<div id="example-2">
    <!-- `greet` 是在下面定义的方法名 -->
    <button v-on:click="greet">Greet</button>
</div>
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!'

内联处理器中的方法

(1)除了直接绑定到一个方法,也能够在内联 JavaScript 语句中调用方法:

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

(2)有时也须要在内联语句处理器中访问原始的 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)
  }
}

事件修饰符

在事件处理程序中调用 event.preventDefault()  event.stopPropagation() 是很是常见的需求。尽管咱们能够在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。为了解决这个问题,Vue.js  v-on 提供了事件修饰符。以前提过,修饰符是由点开头的指令后缀来表示的。

.stop
.prevent
.capture
.self
.once
<!-- 阻止单击事件继续传播 -->
<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>    

使用修饰符时,顺序很重要;相应的代码会以一样的顺序产生。所以,用 @click.prevent.self 会阻止全部的点击,而 @click.self.prevent 只会阻止对元素自身的点击。

2.1.4 新增
<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>

不像其它只能对原生的 DOM 事件起做用的修饰符,.once 修饰符还能被用到自定义的组件事件上。若是你尚未阅读关于组件的文档,如今大可没必要担忧。 按键修饰符 在监听键盘事件时,咱们常常须要检查常见的键值。Vue 容许为 v-on 在监听键盘事件时添加按键修饰符:

<!-- 只有在 `keyCode` 是 13 时调用 `vm.submit()` -->
<input v-on:keyup.13="submit">
记住全部的 keyCode 比较困难,因此 Vue 为最经常使用的按键提供了别名:
<!-- 同上 -->
<input v-on:keyup.enter="submit">
    
<!-- 缩写语法 -->
<input @keyup.enter="submit">
.enter
.tab
.delete (捕获“删除”和“退格”键)
.esc
.space
.up
.down
.left
.right

能够经过全局 config.keyCodes 对象自定义按键修饰符别名:

// 可使用 `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112

自动匹配按键修饰符

  • 你也可直接将 KeyboardEvent.key 暴露的任意有效按键名转换为 kebab-case 来做为修饰符

<input @keyup.page-down="onPageDown">

在上面的例子中,处理函数仅在 $event.key === ‘PageDown’ 时被调用。 有一些按键 (.esc 以及全部的方向键) 在 IE9 中有不一样的 key 值, 若是你想支持 IE9,它们的内置别名应该是首选。

系统修饰键

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

.ctrl
.alt
.shift
.meta

注意:在 Mac 系统键盘上,meta 对应 command 键 (⌘)。在 Windows系统键盘 meta 对应 Windows 徽标键 (⊞)。在 Sun 操做系统键盘上,meta 对应实心宝石键 (◆)。在其余特定键盘上,尤为在MIT  Lisp机器的键盘、以及其后继产品,好比 Knight 键盘、space-cadet 键盘,meta 被标记为“META”。在 Symbolics 键盘上,meta被标记为“META”或者“Meta”

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

<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>

请注意修饰键与常规按键不一样,在和 keyup 事件一块儿用时,事件触发时修饰键必须处于按下状态。换句话说,只有在按住 ctrl 的状况下释放其它按键,才能触发keyup.ctrl。而单单释放 ctrl 也不会触发事件。

.exact 修饰符

.exact 修饰符容许你控制由精确的系统修饰符组合触发的事件

<!-- 即便 Alt 或 Shift 被一同按下时也会触发 -->
<button @click.ctrl="onClick">A</button>

<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button>

<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button @click.exact="onClick">A</button>

鼠标按钮修饰符

.left
.right
.middle

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

9、组件

组件 (Component)  Vue.js 最强大的功能之一。组件能够扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能。在有些状况下,组件也能够表现为用 is 特性进行了扩展的原生 HTML 元素。 全部的 Vue 组件同时也都是 Vue 的实例,因此可接受相同的选项对象 (除了一些根级特有的选项) 并提供相同的生命周期钩子。

使用组件

(1)全局注册

咱们已经知道,能够经过如下方式建立一个 Vue 实例:

new Vue({
  el: '#some-element',
  // 选项
})

要注册一个全局组件,可使用 Vue.component(tagName, options)。例如:

Vue.component('my-component', {
  // 选项
})

组件在注册以后,即可以做为自定义元素 <my-component></my-component> 在一个实例的模板中使用。注意确保在初始化根实例以前注册组件: 实例:

<div id="example">
  <my-component></my-component>
</div>
// 注册
Vue.component('my-component', {
  template: '<div>A custom component!</div>'
})
// 建立根实例
new Vue({
  el: '#example'
})

渲染为:

<div id="example">
  <div>A custom component!</div>
</div>

(2)局部注册

你没必要把每一个组件都注册到全局。你能够经过某个 Vue 实例/组件的实例选项 components 注册仅在其做用域中可用的组件:

var Child = {
  template: '<div>A custom component!</div>'
}

new Vue({
  // ...
  components: {
    // <my-component> 将只在父组件模板中可用
    'my-component': Child
  }
})

这种封装也适用于其它可注册的 Vue 功能,好比指令。

(3)DOM 模板解析注意事项

当使用 DOM 做为模板时 (例如,使用 el 选项来把 Vue 实例挂载到一个已有内容的元素上),你会受到 HTML 自己的一些限制,由于 Vue 只有在浏览器解析、规范化模板以后才能获取其内容。尤为要注意,像<ul>、<ol>、<table>  <select>这样的元素里容许包含的元素有限制,而另外一些像<li>、<tr>  <option>这样的元素只能出如今某些特定元素的内部。

在自定义组件中使用这些受限制的元素时会致使一些问题,例如:

<table>
  <my-row>...</my-row>
</table>

自定义组件 会被看成无效的内容,所以会致使错误的渲染结果。变通的方案是使用特殊的 is 特性:

<table>
  <tr is="my-row"></tr>
</table>

在模块系统中局部注册

import ComponentA from './ComponentA'
import ComponentC from './ComponentC'
    
export default {
  components: {
    ComponentA,
    ComponentC
  },
  // ...
}

Prop*

(1)使用 Prop 传递数据

组件实例的做用域是孤立的。这意味着不能 (也不该该) 在子组件的模板内直接引用父组件的数据。父组件的数据须要经过 prop 才能下发到子组件中。 子组件要显式地用 props 选项声明它预期的数据:

Vue.component('child', {
  // 声明 props
  props: ['message'],
  // 就像 data 同样,prop 也能够在模板中使用
  // 一样也能够在 vm 实例中经过 this.message 来使用
  template: '<span>{{ message }}</span>'
})

而后咱们能够这样向它传入一个普通字符串:

<child message="hello!"></child>

(2)camelCase vs. kebab-case

HTML 特性是不区分大小写的。因此,当使用的不是字符串模板时,camelCase (驼峰式命名) 的 prop 须要转换为相对应的 kebab-case (短横线分隔式命名):

Vue.component('child', {
  // 在 JavaScript 中使用 camelCase
  props: ['myMessage'],
  template: '<span>{{ myMessage }}</span>'
})
<!-- 在 HTML 中使用 kebab-case -->
<child my-message="hello!"></child>

(3)动态 Prop

与绑定到任何普通的 HTML 特性相相似,咱们能够用 v-bind 来动态地将 prop 绑定到父组件的数据。每当父组件的数据变化时,该变化也会传导给子组件:

<div>
    <input v-model="parentMsg">
    <br>
    <child v-bind:my-message="parentMsg"></child>
</div>

你也可使用 v-bind 的缩写语法:

<child :my-message="parentMsg"></child>

单向数据流:

全部的 prop 都使得其父子 prop 之间造成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,可是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而致使你的应用的数据流向难以理解

1. 这个 prop 用来传递一个初始值;这个子组件接下来但愿将其做为一个本地的 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` 匹配任何类型)
    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类型检查:String、Number、Boolean、Function、Object、Array、Symbol

额外的,type 还能够是一个自定义的构造函数,而且经过 instanceof 来进行检查确认。例如,给定下列现成的构造函数:

function Person (firstName, lastName) {
  this.firstName = firstName
  this.lastName = lastName
}

你可使用:

Vue.component('blog-post', {
  props: {
    author: Person
  }
})

来验证 author prop 的值是不是经过 new Person 建立的。

生命周期函数:

 beforeCreate(建立前),

  created(建立后),

  beforeMount(载入前),

  mounted(载入后),

  beforeUpdate(更新前),

  updated(更新后),

  beforeDestroy(销毁前),

  destroyed(销毁后)

不得不提的一个官方脚手架:vue-cli(需了解node jswebpack npm等)

命令行:

1.npm install -g vue-cli

2.vue init webpack my-project

3.cd my project

4.npm install

5.npm run dev

若是报错:Module build failed: Error: No parser and no file path given,couldn't infer a parser.

解决方法:

npm i prettier@~1.12.0

而后从新启动:

npm run dev

仅几行命令即快速构建了一个web单页应用,涵盖vue、vue-router、axios、webpack等.

官方技术文档:

  • vue.js官方文档及API

  • vue-cli

  • 路由管理器vue-router



(end)



----------------------------------------


转载请注明出处 


长按二维码或搜索 fewelife 关注咱们哦


本文分享自微信公众号 - 云前端(fewelife)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索