1 Star 0 Fork 1

百川居士 / VUE

forked from tHero / VUE 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

#VUE

组件化应用构建


组件系统是 Vue 的另一个重要概念,因为它是一种抽象,允许我们使用小型、自包含和通常可复用的组件构建大型应用。仔细想想,几乎任意类型的应用界面都可以抽象为一个组件树:

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

1、 定义一个组件

  Vue.component('todo-item',{
        props: ['todo'],
        template:'<li>{{todo.text}}</li>'
    });

2、在body中定义一个标签

<ol>
    <todo-item v-for="item in groceryList" v-bind:todo="item"></todo-item>
</ol>

3、定义VUE对象

var app1 =new Vue({
    el:'#app-1',
    data:{
        message:'hello vue',
        groceryList:[{
            text:'蔬菜'
        },{
            text:'奶酪'
        },{
            text:'苹果'
        }]
    }
});

在上述的代码中我们可以总结下面的规律

  • new Vue({})可以进行创建一个vue的对象,在{}中定义的data,可以在该对象中的message可以进行双向的绑定,在该对象 下可以定义数组,对象,字符串,整型等类型;
  • v-for表示循环当前的dom元素内容,其中item表示循环体,groceryList表示要循环的对象;
  • 注意下图中的描述问题

1、在图中1标注的位置,表示第一个参数为dom标签,vue会根据该标签进行渲染元素,将元素替换为template定义的值 还需要注意的是,两个红色选中的部分的内容是一致的; 注意,如果component第一个参数为驼峰命名时,dom则需要使用-进行连接

2、在图中2标注的位置,表示todo可以访问到vue定义的数组对象,todo是在VUEprops定义的属性,通过配置在图中5位置 中v-bind:todo来标记属性,个人理解为钩子,连接jsdom直接对象传递的桥梁,tododomjs传递的钩子;

3、 在图中3标注的位置,表示VUE的作用域,e1表示绑定的dom元素;

4、在图中5标注的位置,表示循环体,获得到vue内容data定义的对象,并通过for标签的形式来循环

  • 使用component中的template进行渲染的页面;html元素将会被替换,就是说<todo-item>将会被template元素替换
  • 在使用component的时候,在dom的外层必须有一个标签包裹,下面有代码解释一下
 Vue.component('user-list',{
        props:['user'],
        template:'<td>用户名</td><td>{{user.userName}}</td><td>性别</td><td>{{user.sex}}</td>'
    })

如果我们使用了上述的代码进行设置template模板将会报下图中出现的错误,提示必须添加一个根部的元素,加上<tr>将会解决问题

其实这个很好理解,VUE将页面进行组件化了,他相当于一个树形的结构,为了满足树形结构,就必须 存在着根组织节点,通过他来完成页面的拼接

  • 注意,如果component第一个参数为驼峰命名时,dom则需要使用-进行连接
 Vue.component('todoItem',{
        props: ['todo'],
        template:'<li>{{todo.text}}</li>'
    });
 <todo-item v-for="item in groceryList" v-bind:todo="item"></todo-item>

如果为

 <todoItem v-for="item in groceryList" v-bind:todo="item"></todoItem>

则不会显示循环

  • template中输入传递的是一个对象,例如下图所示,我们在user对象中定义了families对象,在template 中进行了输出,VUE会将整个对象按照JSON的格式进行输出,我们也可以直接通过对象的方式进行进一步的访问

VUE 构造器


每个 Vue.js 应用都是通过构造函数 Vue 创建一个 Vue 的根实例 启动的:

var vm = new Vue({
  // 选项
})

虽然没有完全遵循 MVVM 模式, Vue 的设计无疑受到了它的启发。因此在文档中经常会使用 vm (ViewModel 的简称) 这个变量名表示 Vue 实例。 在实例化 Vue 时,需要传入一个选项对象,它可以包含数据、模板、挂载元素、方法、生命周期钩子等选项。全部的选项可以在 API 文档中查看。 可以扩展 Vue 构造器,从而用预定义选项创建可复用的组件构造器:

var MyComponent = Vue.extend({
  // 扩展选项
})
// 所有的 `MyComponent` 实例都将以预定义的扩展选项被创建
var myComponentInstance = new MyComponent()

尽管可以命令式地创建扩展实例,不过在多数情况下建议将组件构造器注册为一个自定义元素,然后声明式地用在模板中。我们将在后面详细说明组件系统。 现在你只需知道所有的 Vue.js 组件其实都是被扩展的 Vue 实例。

属性与方法


每个 Vue 实例都会代理其 data 对象里所有的属性:

var data = { a: 1 }
var vm = new Vue({
  data: data
})
vm.a === data.a // -> true
// 设置属性也会影响到原始数据
vm.a = 2
data.a // -> 2
// ... 反之亦然
data.a = 3
vm.a // -> 3

注意只有这些被代理的属性是响应的。如果在实例创建之后添加新的属性到实例上,它不会触发视图更新。我们将在后面详细讨论响应系统。

注意只有这些被代理的属性是响应的。如果在实例创建之后添加新的属性到实例上,它不会触发视图更新。我们将在后面详细讨论响应系统。 除了 data 属性, Vue 实例暴露了一些有用的实例属性与方法。这些属性与方法都有前缀 $,以便与代理的 data 属性区分。例如:

var data = { a: 1 }
var vm = new Vue({
  el: '#example',
  data: data
})
vm.$data === data // -> true
vm.$el === document.getElementById('example') // -> true
// $watch 是一个实例方法
vm.$watch('a', function (newVal, oldVal) {
  // 这个回调将在 `vm.a`  改变后调用
})

Vue 实例暴露了一些有用的实例属性与方法。这些属性与方法都有前缀 $,如下图所示;

我们自己的定义的方法,如messageuser都是不含有$符合的如下图

下面我们尝试使用控制台的形式来操作一下对象,观察页面元素,可以通过app1来访问对象性中的属性

如果我们修改其中的值页面是否会跟随其改变呢?通过在控制台处理的对象中的属性,我们看到页面也跟随其 改变

注意,不要在实例属性或者回调函数中(如 vm.$watch('a', newVal => this.myMethod()))使用箭头函数。因为箭头函数绑定父上下文, 所以 this 不会像预想的一样是 Vue 实例,而是 this.myMethod 未被定义。

实例生命周期


每个 Vue 实例在被创建之前都要经过一系列的初始化过程。例如,实例需要配置数据观测(data observer)、 编译模版、挂载实例到 DOM ,然后在数据变化时更新 DOM 。在这个过程中,实例也会调用一些 生命周期钩子 ,这就给我们提供了执行自定义逻辑的机会。例如,created 这个钩子在实例被创建之后被 调用:

var vm = new Vue({
  data: {
    a: 1
  },
  created: function () {
    // `this` 指向 vm 实例
    console.log('a is: ' + this.a)
  }
})
// -> "a is: 1"

也有一些其它的钩子,在实例生命周期的不同阶段调用,如 mountedupdateddestroyed 。 钩子的 this 指向调用它的 Vue 实例。一些用户可能会问 Vue.js 是否有“控制器”的概念?答案是, 没有。组件的自定义逻辑可以分布在这些钩子中。

如何更好的理解钩子呢?下面的图展示的是js与DOM直接的钩子,进行的参数绑定,来完成页面 的渲染,有时候我们还componetVUEDOM之间同样存在着类似的钩子,我们看下是如 何控制与管理的

查看下面的图可以观察一下componetVUEDOM之间的交互

生命周期图示


下图说明了实例的生命周期。你不需要立马弄明白所有的东西,不过以后它会有帮助。

根据我们之前学习的东西,我们可以简单的对生命周期进行测试

var app1 =new Vue({
        el:'#app-1',
        data: {
            message: 'hello vue',
        },
        created: function () {
            console.log("执行created")
        },
        beforeCreate:function(){
            console.log("执行beforeCreate")
        },
        beforeMount:function () {
            console.log("执行beforeMount");
        },
        mounted:function () {
            console.log("执行mounted");
        },
        beforeUpdate:function () {
            console.log("执行beforeUpdate");
        },
        updated:function () {
            console.log("执行updated");
        },
        beforeDestory:function () {
            console.log("beforeDestory");
        },
        destroyed:function () {
            console.log("beforeDestory");
        }

    });
<div id="app-1">
    {{message}}
</div>

先看下官方的图

根据上面的代码我们测试获得生命周期为

  • 页面加载的时候执行顺序为 beforeCreatecreatebeforeMountmounted,从图中标注的1 可以看出执行属性
  • 页面加载完成,其中的beforeUpdateUpdate并没有被执行;如图控制台的执行顺序可以观察出
  • 我们在控制台调整一下message的值,在图中2步骤,执行后,图中3被执行了,输出了beforeUpdateUpdate 由此我们可以得知,如果想处理页面绑定中的逻辑时,可以在该方法中进行处理

模板语法


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

这里描述了一个概念即虚拟DOM,这里简单的描述一下这个概念

1.为什么需要虚拟DOM

DOM是很慢的,其元素非常庞大,页面的性能问题鲜有由JS引起的,大部分都是由DOM操作引起的。 如果对前端工作进行抽象的话,主要就是维护状态和更新视图;而更新视图和维护状态都需要DOM操作。 其实近年来,前端的框架主要发展方向就是解放DOM操作的复杂性。

2.理解虚拟DOM

虚拟的DOM的核心思想是:对复杂的文档DOM结构,提供一种方便的工具,进行最小化地DOM操作。这句话,也许过于抽象, 却基本概况了虚拟DOM的设计思想

  • 用JS表示DOM结构
  • 比较两棵虚拟DOM树的差异
  • 对真实DOM进行最小化修改

具体的内容可以查看http://www.ituring.com.cn/article/211352进行学习

插值


文本

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

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

Mustache 标签将会被替代为对应数据对象上 msg 属性的值。无论何时,绑定的数据对象上 msg 属性发生了改变,插值处的内容都会更新。 通过使用v-once指令,你也能执行一次性地插值,当数据改变时,插值处的内容不会更新。但请留心这会影响到该节点上所有的数据绑定:

<span v-once>This will never change: {{ msg }}</span>

  • 在页面中要绑定的变量,在VUE data中没有进行定义那么会报错,如下图所示

这里还需要注意一个问题,当我们在控制台进行更改某一个变量的值时,上面的错误仍会抛出!

被标记为v-once的元素,在控制台进行修改该值得时候,是无法进行修改的,需要特别注意, 该标签下的所有被绑定的属性,都只能被绑定一次

纯HTML

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

<div v-html="rawHtml"></div>

被插入的内容都会被当做 HTML —— 数据绑定会被忽略。注意,你不能使用 v-html 来复合局部模板,因为 Vue 不是基于字符串的模板引擎。 组件更适合担任 UI 重用与复合的基本单元。

你的站点上动态渲染的任意 HTML 可能会非常危险,因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值, 绝不要对用户提供的内容插值。

我在mustacheHtml.html页面中测试了html绑定的内容;

  • v-html可以向页面中输出dom元素,但是在dom中绑定的属性,是不能被渲染的,如上图所示;
  • 另外不建议使用v-hmtl进行大量的DOM渲染,也就是不能使用复合布局排版,如果想使用的话,推荐使用component的方法
属性

Mustache 不能在 HTML 属性中使用,应使用v-bind指令:

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

这对布尔值的属性也有效 —— 如果条件被求值为 false 的话该属性会被移除:

<button v-bind:disabled="someDynamicCondition">Button</button>

下面的代码很好的演示了,使用{{}}的形式来绑定属性是报出的错误

v-bind:id="dynamicId" 在属性的前面添加v-bind:进行标注,表示要绑定的属性,双引号直接则表示在VUE data定义的属性名称

使用 JavaScript 表达式

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

{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>

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

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

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

譬如

 <p>{{Math.random()}}</p>

是否可以访问到我们自定仪的全局变量呢?

通过下面图我们可以看出,在{{}}直接我们是可以直接访问到Math对象的,但是我们定义在页面中的全局变量topName是不能访问到的; {{}}不支持定义变量,不支持含有大括号

指令

指令(Directives)是带有v-前缀的特殊属性。指令属性的值预期是单一 JavaScript 表达式(除了 v-for,之后再讨论)。指令 的职责就是当其表达式的值改变时相应地将某些行为应用到DOM上。让我们回顾一下在介绍里的例子:

<p v-if="seen">Now you see me</p>

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

v-if 不仅仅可以可以支持单独的校验,还支持使用计算;如下图中的number+1==2

参数

一些指令能接受一个“参数”,在指令后以冒号指明。例如, v-bind 指令被用来响应地更新HTML属性:

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

在这里 href 是参数,告知 v-bind 指令将该元素的 href 属性与表达式 url 的值绑定。 另一个例子是v-on指令,它用于监听DOM事件:

<a v-on:click="doSomething">

在这里参数是监听的事件名。我们也会更详细地讨论事件处理。

这里要说明一个问题,在上面我们使用了v-for的形式来进行遍历数据,在我们就是使用了参数这种形式

修饰符

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

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

之后当我们更深入地了解 v-on v-model时,会看到更多修饰符的使用。

过滤器


Vue.js 允许你自定义过滤器,可被用作一些常见的文本格式化。过滤器可以用在两个地方:mustache 插值和 v-bind 表达式。 过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符指示:

<!-- in mustaches -->
{{ message | capitalize }}
<!-- in v-bind -->
<div v-bind:id="rawId | formatId"></div>

Vue 2.x 中,过滤器只能在 mustache 绑定和 v-bind 表达式(从 2.1.0 开始支持)中使用, 因为过滤器设计目的就是用于文本转换。为了在其他指令中实现更复杂的数据变换,你应该使用计算属性。

过滤器函数总接受表达式的值作为第一个参数。

new Vue({
  // ...
  filters: {
    capitalize: function (value) {
      if (!value) return ''
      value = value.toString()
      return value.charAt(0).toUpperCase() + value.slice(1)
    }
  }
})

过滤器可以串联:

{{ message | filterA | filterB }}

  • 在表单式中定义了属性lightColor 3 个属性,分别表示着红绿灯的颜色,如图中1标签;
  • vue中定义了一个方法,过滤方法,通过该方法我们可以过滤字段,这里传入一个参数,在图中3标志, 通过该value可以获得属性值,然后在进行过滤
  • {{}}中定义了| 后面的表示过滤方法,我们定义的是filterColor
  • {{}}是支持多重过滤的,我们可以定义多个过滤方法,来过滤相关属性

由于{{}}是支持多重过滤的,所以我们要注意一下每一层过滤后的值是否发生了改变?从上面的图中,我们 为lightColor中添加了两个过滤器,分别为filterColorfilterPass ,在经过第一个过滤器filterColor 后,进入到filterPass中的Value发生了变化,有1变成了黄灯

上述的例子展示出了{{}}支持v-bind指令

过滤器是 JavaScript 函数,因此可以接受参数: {{ message | filterA('arg1', arg2) }} 这里,字符串 'arg1' 将传给过滤器作为第二个参数, arg2 表达式的值将被求值然后传给过滤器作为第三个参数。 从上面的图中我们可以看出,在html中定义的个参数,filterhttp('12','32')进入filter方法后,将会变为第2和第3个参数

如果在程序中没有定义filter 方法,将会报出下面的错误

缩写


v- 前缀在模板中是作为一个标示 Vue 特殊属性的明显标识。当你使用Vue.js为现有的标记添加动态行为时,它会很有用, 但对于一些经常使用的指令来说有点繁琐。同时,当搭建 Vue.js 管理所有模板的 SPA 时,v- 前缀也变得没那么重要了。因此, Vue.js 为两个最为常用的指令提供了特别的缩写:

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" v-bind:title="url | filterHttp('12','32')" >百度么?</a>
<a :href="url" :title="url | filterHttp('12','32')" >缩写绑定v-bind</a>

它们看起来可能与普通的 HTML 略有不同,但 :@ 对于属性名来说都是合法字符,在所有支持 Vue.js 的浏览器都能被正确地解析。而且,它们不会出现在最终渲染的标记。缩写语法是完全可选的,但随着你更深入地了 解它们的作用,你会庆幸拥有它们。

计算属性


模板内的表达式是非常便利的,但是它们实际上只用于简单的运算。在模板中放入太多的逻辑会让模板过重且难以维护。例如:

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

在这种情况下,模板不再简单和清晰。在意识到这是反向显示 message 之前,你不得不再次确认第二遍。当你想要在模板中多次反向显示 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: {
    // a computed getter
    reversedMessage: function () {
      // `this` points to the vm instance
      return this.message.split('').reverse().join('')
    }
  }
})

结果:

Original message: "Hello"
Computed reversed message: "olleH"
这里我们声明了一个计算属性 reversedMessage 。我们提供的函数将用作属性 vm.reversedMessagegetter

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

你可以打开浏览器的控制台,自行修改例子中的 vm vm.reversedMessage 的值始终取决于 vm.message 的值。

你可以像绑定普通属性一样在模板中绑定计算属性。Vue知道 vm.reversedMessage 依赖于 vm.message ,因此当 vm.message 发生改变时,所有依赖于 vm.reversedMessage 的绑定也会更新。而且最妙的是我们已经以声明的方式创建了这种依赖关系: 计算属性的 getter 是没有副作用,这使得它易于测试和推理。

computed方法中定义的reversedMessage,这个方法中的this只向的是vm从图中可以看出

我们在学习filter的时候,官方介绍提供的是一个方法,可以添加参数,那么computed中是否也支持方法 传递参数呢 ?通过下图我们可以看出,我们添加了方法,但是控制台报错了!

同样我们去掉{{}}中的方法中的参数仍然报错

由此我们可以得出一个结论,在computed定义的方法,虽然在结构上可以看出他是个方法,但是,是不能通过方法的形式来调用的 其最终返回的时候一个属性

我们这里使用了2中方法来进行输出message的值,当我们在控制台调用reverMessage的时候,发现抛出不是个function的错误

计算缓存 vs Methods

你可能已经注意到我们可以通过调用表达式中的 method 来达到同样的效果:

<p>Reversed message: "{{ reversedMessage() }}"</p>
// in component
methods: {
  reversedMessage: function () {
    return this.message.split('').reverse().join('')
  }
}

我们可以将同一函数定义为一个 method 而不是一个计算属性。对于最终的结果,两种方式确实是相同的。然而,不同的是计算属性 是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变时才会重新求值。这就意味着只要 message 还没有发生改变, 多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。 这也同样意味着下面的计算属性将不再更新,因为 Date.now() 不是响应式依赖:

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

相比而言,只要发生重新渲染,method 调用总会执行该函数。 我们为什么需要缓存?假设我们有一个性能开销比较大的的计算属性 A ,它需要遍历一个极大的数组和做大量的计算。然后我们可能有 其他的计算属性依赖于A。如果没有缓存,我们将不可避免的多次执行 A getter!如果你不希望有缓存,请用method替代。

通过项目中的computed.html我们可以看出上面的结论,当页面初始化时 定义在computed reverMessagemethods reverMessage1都被执行了 下面通过一些步骤来测试

  • 当我重新对message进行赋值时,值不发生改变,如下图中的2 computed reverMessagemethods reverMessage1都没有被执行;
  • 当我对messsage的值进行修改时,computed reverMessagemethods reverMessage1均被执行了;如图中第3
  • 当我调用methods reverMessage1的方法是,computed reverMessage没有被执行

  1. computed reverMessage中定义的值是存在缓存的,当值没有发生变化的时候,定义在computed reverMessage将不会发生改变;
  2. methods reverMessage1只要调用就会被执行;

Computed 属性 vs Watched 属性

Vue 确实提供了一种更通用的方式来观察和响应Vue实例上的数据变动:watch 属性。当你有一些数据需要随着其它数据变动而变动时, 你很容易滥用 watch——特别是如果你之前使用过 AngularJS。然而,通常更好的想法是使用 computed 属性而不是命令式的 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
    }
  }
})

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

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

下面我们通过实例来研究一下问题,watch方法,提供两个参数,在图中5所示的位置,val表示原始数据;b表示更改 后的数据;

在图1中,如果我们修改了lastName的值;lastNamewatchfullNamewatch都会被执行;原则是,当oldValuenewValue不同时,对应的watch都会被执行;个人认为官方提供的watch替换为computed的例子有些特殊;当属性没有 相互关联时,就可以使用watch方法

。当你想要在数据变化响应时,执行异步操作或开销较大的操作,这是很有用的。

计算 setter

计算属性默认只有 getter ,不过在需要时你也可以提供一个setter

// ...
computed: {
  fullName: {
    // getter
    get: function () {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set: function (newValue) {
      var names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}
// ...

现在在运行 vm.fullName = 'John Doe' 时, setter 会被调用, vm.firstNamevm.lastName也相应地会被更新。

  • 从上面的代码,当我们修改了属性值得时候,将set方法中定义的内容可以放到get中可以达到同样的效果

ClassStyle绑定

数据绑定一个常见需求是操作元素的 class 列表和它的内联样式。因为它们都是属性 ,我们可以用v-bind 处理它们:只需要计算出表达式最 终的字符串。不过,字符串拼接麻烦又易错。因此,在 v-bind 用于 class style 时,Vue.js专门增强了它。表达式的结果类型除了字 符串之外,还可以是对象或数组。

绑定 HTML Class

对象语法

我们可以传给 v-bind:class 一个对象,以动态地切换 class

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

上面的语法表示 classactive 的更新将取决于数据属性 isActive 是否为真值 。 我们也可以在对象中传入更多属性用来动态切换多个class。此外, v-bind:class 指令可以与普通的 class 属性共存。如下模板:

<div class="static"
     v-bind:class="{ active: isActive, 'text-danger': hasError }">
</div>

如下 data:

data: {
  isActive: true,
  hasError: false
}

渲染为:

<div class="static active"></div>

isActive 或者 hasError 变化时,class 列表将相应地更新。例如,如果 hasError 的值为trueclass列表将 变为 "static active text-danger"。

  • 1页面初始化的时候,页面进行加载起初为红色 ;
  • 2当修改app.isActivie = false的时候;{ active: isActive, 'text-danger': hasError }没有任何一个class被显示出来;
  • 34表明了data中的数据是可以动态的进行修改;
  • 5上面的格式类似于javascript中对象的定义,为true的将会被返回,返回css样式,页面会被加载

你也可以直接绑定数据里的一个对象:

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

渲染的结果和上面一样。

  • 如果classObjectapp中没有定义,将会抛出下面的错误
vue.js:435 [Vue warn]: Property or method "classObject" is not defined 
on the instance but referenced during render. Make sure to declare reactive 
data properties in the data option.
(found in <Root>)

  • class的定义出现语法错误的时候,譬如:
    <div v-bind:class="{active:isActive , greenColor:}">active</div>

,将会抛出下面的错误

  • 我们来讨论一下东西,根据前面的定义我们可以发现,class可以理解为对象; 那么在data定义的东西我们是否可以写成函数,然后返回对象呢?
  var app = new Vue({
        el:'#app-1',
        data:{
            isActive:true,
            hasPass:false,
            classObject:{
                active:true,
                greenColor:function () {
                    return false;
                }
            }
        }
    });

通过上面的代码我们发现,greenColor可以被浏览器正确的解析,但是有个问题, 如果我们在控制台直接访问greenColor的时候是需要通过函数式的方式;譬如: app.classObject.greenColor(),尝试在greenColor定义一个有参的数据譬如:

 greenColor:function (a) {
    if(a=="123"){
        a = true;
    }else{
        a = false;
    }
    console.log(a);
    return a;
}

在控制台进行进行测试,发现虽然a的值发生了改变,但是却不能引起页面的渲染

我们也可以在这里绑定返回对象的计算属性。这是一个常用且强大的模式:

<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',
    }
  }
}
  • 这里有个问题,如果我们使用computedClassObject来修改他的值得时候,页面并没有发生 变化如下图

数组语法

我们可以把一个数组传给 v-bind:class ,以应用一个 class 列表:

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

渲染为:

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

如果你也想根据条件切换列表中的 class ,可以用三元表达式:

<div v-bind:class="[isActive ? activeClass : '', errorClass]">

此例始终添加 errorClass ,但是只有在 isActivetrue 时添加 activeClass 。 不过,当有多个条件 class 时这样写有些繁琐。可以在数组语法中使用对象语法:

<div v-bind:class="[{ active: isActive }, errorClass]">

我们可以通过修改app定义的activeClasserrorClass属性来修改class样式

  • 数组的另外一种表示方式,在app中定义一个数组,如下图的arrayObject,我们还可以通过动态的处理数组内容 来控制 css 样式

  • 使用数组方式的一个问题,当数组中已经存在一个active的时候,我们通过控制台添加相同的class class会被 重复性的添加

用在组件上

当你在一个定制的组件上用到 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>

同样的适用于绑定HTML class:

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

isActive true 的时候,HTML 将被渲染成为:

<p class="foo bar active">Hi</p>

  • 定义在组件中的v-bind是不能够起到作用的,譬如,控制台将会报错;错误内容为如下;但是却可以添加到DOM
vue.js:435 [Vue warn]: Property or method "arrayObject" is not defined on the instance 
but referenced during render.
 Make sure to declare reactive data properties in the data option.
found in
<MyComponent>
       <Root>

绑定内联样式

对象语法

v-bind:style 的对象语法十分直观——看着非常像 CSS ,其实它是一个 JavaScript 对象。 CSS 属性名可以用驼峰式(camelCase) 或短横分隔命名(kebab-case)

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

sytle的绑定同时也是支持动态修改的,我们在控制台修改active的 样式时,页面的样式会被修改

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

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

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

  • 使用下面的这种形式进行绑定内链样式的时候,定义在数组内的元素,必须是个对象,如下图,
  <div v-bind:style="[fontSizes]">数组形式样式</div>

  • 在图中标注1的位置,我们在data中顶一个font-size是个字符串,但是该字符串并不能应用到html
  • 在图中标注2的位置, styleObject定义的就是对象,可以进行页面的渲染;

自动添加前缀

v-bind:style 使用需要特定前缀的CSS属性时,如 transformVue.js 会自动侦测并添加相应的前缀。 多重值

2.3.0+ 从 2.3 开始你可以为style绑定中的属性提供一个包含多个值的数组,常用于提供多个带前缀的值:

条件渲染

v-if

在字符串模板中,如 Handlebars ,我们得像这样写一个条件块:

{{#if ok}}
  <h1>Yes</h1>
{{/if}}

在 Vue.js ,我们使用 v-if 指令实现同样的功能:

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

也可以用 v-else 添加一个 “else” 块:

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

  • 如果只定义了v-else没有v-if将会报错,如下:
vue.js:435 [Vue warn]: Error compiling template:
<div id="app-1">
   <h1 v-else="">NO</h1>
</div>
- v-else used on element <h1> without corresponding v-if.

(found in <Root>)

  • v-if中动态修改data中的值,是可以动态显示与隐藏的

<template>v-if 条件组

因为 v-if 是一个指令,需要将它添加到一个元素上。但是如果我们想切换多个元素呢?此时我们可以把一个 <template>元素当做包装元素,并在上面使用 v-if。最终的渲染结果不会包含<template>元素。

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

  • template没有被渲染到页面上

v-else-if

2.1.0 新增

v-else-if,顾名思义,充当v-if的“else-if 块”。可以链式地使用多次:

<div v-if="type === 'A'">
  A
</div>
<div v-else-if="type === 'B'">
  B
</div>
<div v-else-if="type === 'C'">
  C
</div>
<div v-else>
  Not A/B/C
</div>

类似于 v-elsev-else-if 必须紧跟在 v-if 或者 v-else-if 元素之后。

  • 这里我们来看一下什么叫紧跟? 在上面的代码中我们加上一段代码,如下图,我们断开了代码就报错了!

[Vue warn]: Error compiling template:
- v-else-if=" type === 'C' " used on element <div> without corresponding v-if.
- v-else used on element <div> without corresponding v-if.
(found in <Root>)

用 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 将不会清除用户已经输入的内容。因为两个模版使用了相同的元素,<input> 不会被替换掉——仅仅是替换了它的的 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>

现在,每次切换时,输入框都将被重新渲染。你自己看:

注意, <label> 元素仍然会被高效地复用,因为它们没有添加key属性。

  • 按照上面的说法,VUE会最大程度上的利用元素,如果是这样,我们在第一个input中输入一个数据,如果他复用了 那么input表单中的值将不会消失,如下图:同时仔细观察页面中的domVUE只是修改了label中的标签和input中的name

下面我们添加上了key,观察下面的Gif动图,页面中定义的<p>这个内容没有发生任何的变化,同时input的表单 也发生了改变

v-show

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

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

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

v-if vs v-show

v-if 是“真正的”条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。 v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。 相比之下, v-show就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。 一般来说, v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换, 则使用 v-show 较好;如果在运行时条件不太可能改变,则使用v-if较好。 这里我们可以理解为,如果是类似于数据字典类型的显示与隐藏使用v-if如果div或者模块显示与隐藏使用show

  • v-show 是支持动态修改的,如下图

  • v-show 不支持template,如下图,修改okfalse template中间的元素不会被隐藏
 <template v-show="ok">
        <p>hhh</p>
        <p>hhh2</p>
    </template>

列表渲染

v-for

我们用 v-for 指令根据一组数组的选项列表进行渲染。 v-for 指令需要以item in items形式的特殊语法, items 是源数据数组并且 item 是数组元素迭代的别名。 基本用法

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

结果:

Foo
Bar

  • item in items其中的item表示循环的变量,代表着每一个元素;可以据此来获得message

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

结果:

Parent - 0 - Foo
Parent - 1 - Bar

  • 1处:{{item.message}}获得定义在数组中过的对象;
  • 2处:{{index}}获得索引的下标,这里要注意,索引的下标是可以直接访问的,不需要item来访问;
  • 3处:(item,index)定义两个参数,第一个item循环变量;index索引下标,该处的in可以更改为of也可以完成循环

你也可以用 of 替代 in 作为分隔符,因为它是最接近 JavaScript迭代器的语法:

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

Template v-for

如同v-if模板,你也可以用带有v-for<template>标签来渲染多个元素块。例如:

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

  • 通过查看html元素,可以发现,template并没有渲染到对应页面中;

对象迭代 v-for

你也可以用 v-for 通过一个对象的属性来迭代。

<ul id="repeat-object" class="demo">
  <li v-for="value in object">
    {{ value }}
  </li>
</ul>
new Vue({
  el: '#repeat-object',
  data: {
    object: {
      FirstName: 'John',
      LastName: 'Doe',
      Age: 30
    }
  }
})

结果:

John
Doe
30

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

  • 3处:单个对象遍历属性的方法; value in Object可以进行遍历对象属性;直接通过{{value}}获得值

如果获得属性的名称?

你也可以提供第二个的参数为键名:

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

第三个参数为索引:

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

  • 1处:v-for 定义中 (value,index)可以进行访问值,和索引下标;
  • 2处:v-for 定义中还可以接收3个参数,value,key index分别代码值,键,索引
  • 需要特别注意,v-for 定义的参数,是和顺序有关系,和名称无关;譬如:

  • 通过上面的图看出,key,value的值进行了调换,但是并没有影响输出结果,由此可知,和定义的名称无关,和顺序有关

整数迭代 v-for

v-for 也可以取整数。在这种情况下,它将重复多次模板。

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

v-for with v-if

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

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

上面的代码只传递了未completetodos。 而如果你的目的是有条件地跳过循环的执行,那么将 v-if 置于包装元素 (或 <template>)上。如:

<ul v-if="shouldRenderTodos">
  <li v-for="todo in todos">
    {{ todo }}
  </li>
</ul>

  • 在循环中,每一次的循环都会经过v-if的判断,来输出内容,上图的代码中,只有status2的被展示出来了

而如果你的目的是有条件地跳过循环的执行,那么将 v-if 置于包装元素 (或 <template>)上。如:

<ul v-if="shouldRenderTodos">
  <li v-for="todo in todos">
    {{ todo }}
  </li>
</ul>

key

Vue.js v-for 正在更新已渲染过的元素列表时,它默认用 “就地复用” 策略。如果数据项的顺序被改变,Vue将不是移动 DOM 元素 来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。这个类似 Vue 1.x track-by="$index" 。这个默认的模式是有效的,但是只适用于不依赖子组件状态或临时 DOM 状态(例如:表单输入值)的列表渲染输出。 为了给Vue一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一key属性。理想的 key 值是每项都有唯一 id。这个特殊的属性相当于Vue 1.xtrack-by ,但它的工作方式类似于一个属性,所以你需要用 v-bind 来绑定动态值 (在这里使用简写):

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

建议尽可能使用 v-for 来提供 key ,除非迭代 DOM 内容足够简单,或者你是故意要依赖于默认行为来获得性能提升。 因为它是Vue识别节点的一个通用机制,key并不特别与v-for关联,key 还具有其他用途,我们将在后面的指南中看到其他用途。

  • 在上面的图我们可以发现,虽然进行绑定了key,但是在html页面中,并没有将id渲染到对应的元素上!

数组更新检测

变异方法

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

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse() 你打开控制台,然后用前面例子的 items 数组调用变异方法:example1.items.push({ message: 'Baz' }) 。 重塑数组

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

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

  • 我们在控制台中将数组进行了filter,页面中的DOM元素并没有发生改变;上图中1 2表述了这个现象,究其原因,是因为filter返回的 是一个新的数组,并没有影响到原有数组内容;使用上面的代码可以进行替换旧数组

你可能认为这将导致 Vue 丢弃现有DOM并重新渲染整个列表。幸运的是,事实并非如此。 Vue 实现了一些智能启发式方法来最大化 DOM 元素重用,所以用一个含有相同元素的数组去替换原来的数组是非常高效的操作。 注意事项

由于 JavaScript 的限制, Vue 不能检测以下变动的数组: 当你利用索引直接设置一个项时,例如: vm.items[indexOfItem] = newValue 当你修改数组的长度时,例如: vm.items.length = newLength 为了解决第一类问题,以下两种方式都可以实现和vm.items[indexOfItem] = newValue相同的效果, 同时也将触发状态更新:

  • 通过上面的图我们来观察一下问题;
    • app.todos[0].todoName='拜见女方家长' 通过数组的方式直接访问到元素,并修改它的属性,页面进行了渲染;
    • app.todos[1] ={} 通过直接修改一个数组的元素;页面没有发生任何的改变;
    • 通过查看app.todos发现数据发生了改变,但是页面并没有渲染;

通过下面的方法可以修改数组中的值;并进行渲染页面,这个方法会替换原来indexOfItem对应的元素

// Vue.set
Vue.set(example1.items, indexOfItem, newValue)
// Array.prototype.splice`
example1.items.splice(indexOfItem, 1, newValue)

为了解决第二类问题,你也同样可以使用 splice

example1.items.splice(newLength)

显示过滤/排序结果

有时,我们想要显示一个数组的过滤或排序副本,而不实际改变或重置原始数据。在这种情况下,可以创建返回过滤或排序数组的计算属性。 例如:

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

  • 注意页面中箭头指向的定义关系;循环时,每一个元素都会经过evenNumber的过滤

或者,你也可以在计算属性不适用的情况下 (例如,在嵌套 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
    })
  }
}

  • VUE中定义的方法;even传入数组,然后在过滤,该方法的复用率高;

  • 注意上面监听代码的问题,在DOM处是没有添加()的,即v-on:click=green 此时传递到greet中的方法event则是一个事件对象,如下图;

如果我们在DOM处是添加()的并传递一个参数,那么:event将不存在,被替换为传入的参数;

如何既获得参数,又获得event对象呢?

  • 传入2个参数,第一个参数为字符串,第二个参数为时间对象event,需要特别注意的是 如果在DOM进行传递参数就必须使用$event的方式来传递特殊的对象;

事件修饰符问题;

官方文档有一个对按钮添加一次监听的功能;

<a v-on:click.once="doThis"></a>`

需要注意的是,该按钮确实只能被点击一次,下次激活的时候,需要刷新页面才能激活 从下图我们可以看出,不刷新页面的情况下,该按钮不起作用

  • 父元素向子元素通过props的方式进行传递参数;

  • 实现动态绑定model的方法

  • 子元素向父元素传递事件;

  • 子组件和app的传递关系,定义位置;关联关系图;

  • Prop 验证

  • 非父子组件通信

  • 具名 Slot

  • 在子元素内部定义slot=AAA名称与组件中的template slot name='AAA'一致时,将会替换 template中的元素

  • 作用域插槽

  1. 从上面的页面我们可以看出slot定义了3个,template中的内容都会被渲染进去; 也就是说,在页面中渲染了3组html如图中所示;
  2. 在页面中,scope="props" props.text 引入的属性会读取slot定义的属性,如果没有值 页面也会渲染,span也会输出;
  3. scope是父组件应用子组件中的slot中的属性;
  • 如何循环获得子元素内部定义的值;

上面的两种方式 ,实现了替换子组件中的slot内容,已经循环插入值得功能;

空文件

简介

vue学习 展开 收起
JavaScript
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
JavaScript
1
https://gitee.com/kuaihailong/vue.git
git@gitee.com:kuaihailong/vue.git
kuaihailong
vue
VUE
master

搜索帮助