美文网首页
浅谈Vue.js官方文档

浅谈Vue.js官方文档

作者: NoobYPP | 来源:发表于2018-12-22 13:41 被阅读0次
image
最近的工作,需要写点类似自己公司内部bms系统的一些web页面。由于不是对外的,一些简单的页面就交给后端自己来完成。为了符合公司整个前端代码风格,研究了下 Vue.js
Vue.js 官方文档有中文的,整个上手难度也比较低,看完文档再稍加整合下饿了么的 Element 组件基本就能满足大部分像我这样的需要了。js还是啃大学那会用JQuery的老本,并不是很会。自己又习惯做点md笔记,于是干脆在官方文档的基础上加了一点自己的理解,整理出来share一下,再加上本身就是Javaer的角度,很基础,若对看的人有一点点帮助就算有意义了。

打个照面:

  • 自定义标签 嵌入原生HTML标签中去:
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: '随便其它什么人吃的东西' }
    ]
  }
})  

完了之后调用:

<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 应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的:
var vm = new Vue({
  // 选项
})

当一个 Vue 实例被创建时,它向 Vue 的响应式系统中加入了其 data 对象中能找到的所有的属性。当这些属性的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。

这个自然也是可以控制的:

var obj = {
  foo: 'bar'
}

Object.freeze(obj)

new Vue({
  el: '#app',
  data: obj
})

这里对foo的任何修改不会生效(后话了 这里应该是涉及到js的浅拷贝和深拷贝问题

new 出来的Vue实例还会暴露出自己的一些属性和方法:通过这写方法和属性,可以操作对应这个new 出来的实例
使用 vm.$watch形式来操作,具体参见API

  • Vue实例生命周期:


    image

上面的生命周期图比较关键,是理解Vue.js很多地方语法结构的助推剂。整体还算比较清晰明了。

  • 一般情况下,使用{{}}引用Vue实例中data域的内容,但是HTML元素标签中并不支持此( {{}} )语法。使用v-bind绑定数据域内内容。(id='xx'形式) 而且{{}}内部支持js语法:
{{ number + 1 }}

{{ ok ? 'YES' : 'NO' }}

{{ message.split('').reverse().join('') }}

<div v-bind:id="'list-' + id"></div>
  • 调用方法去执行计算和调用computed中的方法区别:后者依赖上一次计算后的缓存,只要不需要重新计算(Vue提供支持)就不会重新计算。
var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }
})

上面的代码能同时监听firstName, lastName两个data域值。

绑定class样式

这里的绑定比较灵活而且强大:

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

这样子灵活的将active类绑定到data里面isActive的true/false身上。

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

然后对应的data如下:

data: {
  classObject: {
    active: true,
    'text-danger': false
  }
}

可以注意到这里的 class = 后面是一个json格式的对象,那么直接就可以传一个对象进去了:

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

主要就是这里用json格式来表达一个js对象

在自定义组件时是一种extend的关系,如下:

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

其等价于

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

上面是绑定class相关,大致就是js对象绑定和数组字面量绑定。
下面是内联样式相关:
首先仍然是对象语法:

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

条件渲染

  • 使用template标签(此标签不可见)批量进行渲染:
<template v-if="ok">
  <h1>Title</h1>
  <p>Paragraph 1</p>
  <p>Paragraph 2</p>
</template>

上面的例子中,如果条件成立,页面元素将被渲染出来。但template本身不会显示。

也有类似于java中的语法:

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

使用if else-if else语句,页面渲染时使用的是同一个不显示的template元素。一方面意味高效,另一方面可以实现元素的共用:

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

以上代码,实际上是一个template下面的元素。只会渲染其中之一。意味着这里面包裹的元素若是一致的(比如 input ),那么他们在条件变换时也是共用的,即用户在 input 中输入的内容在条件变化过程中不会改变(毕竟是渲染在同一个 input 下的)。
理解共用的道理比这个语法本身貌似更重要些。
当然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>

上面的内容通过key标记的绑定,就可以实现二次渲染。

  • v-show 渲染
    使用 v-show 的元素会被动态切换其css的 display 样式。和上面的动态渲染不同。这个dom元素会始终被渲染在dom树中,只是css样式的变换而已。
    使用如下:
<h1 v-show="ok">Hello!</h1>

其也不支持 template 语法。


列表渲染

  • v-for语句
    类似于java中的 foreach 语法。比较简单:
<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' }
    ]
  }
})

比java好的地方是这里提供一个 index 可选,获取当前元素的下标(毕竟脚本语言,编译语言中要实现这个功能所牺牲的复杂度想必会更高)

<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' }
    ]
  }
})
  • 如果 for 后面传入的不是数组对象而是其他object,则可以遍历其json格式下的所有属性:
<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
    }
  }
})

输出会是:

  • John
  • Doe
  • 30

另外,in 前面的参数若是 (value, key) 的形式,则遍历 k-v 键值对:

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

结果显然:

firstName:John
lastName:Doe
age:30

最后是 value, key, index 的复合使用:

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

这里使用到一个 变异 非变异 方法的概念:前者就是会改变原数组,后者而是在返回值体现改变的效果。
由于使用Vue进行渲染,就必须保证其能检测到我们的修改。但是第三方框架检测不到原生js数组的一些方法。例子中给出:

var vm = new Vue({
  data: {
    items: ['a', 'b', 'c']
  }
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的

可以使用vue提供的方法:

// Vue.set
Vue.set(vm.items, indexOfItem, newValue)

// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)

即可。
或者这里使用Vue全局方法: Vue.set

vm.$set(vm.items, indexOfItem, newValue)

另外,使用下面的方法改变长度:

vm.items.splice(newLength)

总之,要响应式的改变dom渲染,就必须让Vue感知到这种变化,这里的注意事项给出了一种值得关注的启发:使用Vue就要关注它全方面的js替代性。

  • 添加属性

使用Vue提供的方式:

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

一次赋值多个属性:

vm.userProfile = Object.assign({}, vm.userProfile, {
  age: 27,
  favoriteColor: 'Vue Green'
})
  • 使用类似java匿名内部类的比较函数(或者java8的filter的lambda表达函数)返回数组的过滤对象:
data: {
  numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
  evenNumbers: function () {
    return this.numbers.filter(function (number) {
      return number % 2 === 0
    })
  }
}
<li v-for="n in evenNumbers">{{ n }}</li>

类似的在方法中调用:

data: {
  numbers: [ 1, 2, 3, 4, 5 ]
},
methods: {
  even: function (numbers) {
    return numbers.filter(function (number) {
      return number % 2 === 0
    })
  }
}
<li v-for="n in even(numbers)">{{ n }}</li>
  • 更加直接地调用常量:
div>
  <span v-for="n in 10">{{ n }} </span>
</div>
  • 在<template>中调用 v-for :

上面说过在template中调用 v-if 起到动态加载dom。这里同样可以使用 v-for 渲染:

<ul>
  <template v-for="item in items">
    <li>{{ item.msg }}</li>
    <li class="divider" role="presentation"></li>
  </template>
</ul>
  • 代码中混用 v-if v-for
<li v-for="todo in todos" v-if="!todo.isComplete">
  {{ todo }}
</li>

以上的两个条件位于同一个待渲染元素中,官方文档给出的解释是前者运算优先级更高。但个人理解这里的 todo 是在for中声明的,后者当然是在for中去执行的。Vue的确分这两者的执行优先级,只是可能这个demo不能很好诠释这一点。当后遇到这样的困惑时,自然是前者优先级高。

官网还给出了如下demo作为上一个demo的补充去强调 v-ifv-for 的关系:

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

从一个javaer的角度去看,按照需要的逻辑实现即可,这里不必过多追究。

  • 在组件(component)中使用 v-for

这里文档给了一个比较综合的例子:

<div id="todo-list-example">
  <form v-on:submit.prevent="addNewTodo">
    <label for="new-todo">Add a todo</label>
    <input
      v-model="newTodoText"
      id="new-todo"
      placeholder="E.g. Feed the cat"
    >
    <button>Add</button>
  </form>
  <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>
Vue.component('todo-item', {
  template: '\
    <li>\
      {{ title }}\
      <button v-on:click="$emit(\'remove\')">Remove</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 = ''
    }
  }
})

有两点值得关注:

  1. 文档中提到:

任何数据都不会被自动传递到组件里,因为组件有自己独立的作用域。为了把迭代数据传递到组件里,我们要用 props。

所以在定义自定义组件时声明了 props 属性。(组件相关的介绍被排在Vue基础官方文档的最后优先级,再往后就是 深入了解组件 有一大章节,再往后就是一些高级特性如动画过渡,复用性相关,开发部署等。可见组件还是Vue中的重点和上限比较高的一块)

  1. <li> 标签中使用到的 is="todo-item" 属性。这里等同使用模板中定义的 <todo-item> 但是可以起到浏览器兼容的效果。

事件处理

  • v-on 监听DOM事件

既可以在 v-on 绑定一些触发事件,绑定调用函数,也可以在触发事件后直接写入js代码(个人不建议这么做) :

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

通常是:

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

上面new出来的example2变量可以在js中直接引用使用其成员属性:

example2.greet() // => 'Hello Vue.js!'

文档处给处 内联语法 使用demo,其实就是在html中去使用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)
    }
  }
})

内联语法 中使用原生DOM事件,则要加上$引用:

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

在事件响应中,默认的事件发生行为有时候不是我们想要的。比如一个按钮中嵌套另一个按钮,两个按钮同时绑定点击响应时间,但是需求是内部按钮点击事件结束,外部不做任何操作。其实也就是阻断事件的 冒泡捕获 行为。原生js已经提供了这种支持。 Vue 提供 .xxx 的语法封装了这种行为:

<!-- 阻止单击事件继续传播 -->
<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>

*绑定按键修饰符:
监听键盘行为:

<!-- 只有在 `keyCode` 是 13 时调用 `vm.submit()` -->
<input v-on:keyup.13="submit">

显然这种绑定关系要求我们清楚每个按键的编码,但是比较通用的键盘码已经被封装成代码:

<!-- 同上 -->
<input v-on:keyup.enter="submit">

<!-- 缩写语法 -->
<input @keyup.enter="submit">

enter按键的响应如此。常用的其他的可以在使用过程中查询,反正看了也不一定记得住。Vue可以在全局配置中config此按钮码为字面常量方便使用:

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

表单输入绑定

  • 使用 v-model 实现双向绑定数据
<input v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p>  

<span>Multiline message is:</span>
<p style="white-space: pre-line;">{{ message }}</p>
<br>
<textarea v-model="message" placeholder="add multiple lines"></textarea>  

<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>  

这里比较容易。Vue 下面的数组绑定比较灵活:

<div id='example-3'>
  <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>
  <br>
  <span>Checked names: {{ checkedNames }}</span>
</div>
new Vue({
  el: '#example-3',
  data: {
    checkedNames: []
  }
})

使用 v-bind 渲染数据域的值:

<select v-model="selected">
  <option v-for="option in options" v-bind:value="option.value">
    {{ option.text }}
  </option>
</select>
<span>Selected: {{ selected }}</span>
new Vue({
  el: '...',
  data: {
    selected: 'A',
    options: [
      { text: 'One', value: 'A' },
      { text: 'Two', value: 'B' },
      { text: 'Three', value: 'C' }
    ]
  }
})

一些没有用户输入的表单选项,可以使用 v-bind 绑定想要的值:

<input type="radio" v-model="pick" v-bind:value="a">
// 当选中时
vm.pick === vm.a

其实也就是对这个单选项进行一个值的绑定,但是为了让 Vue 知道,需要使用它的方式来。文档还给出这样的提示:

v-model 会忽略所有表单元素的 value、checked、selected 特性的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data 选项中声明初始值。

自然解释了一切。

Vue还对这个表单数据和数据域内数据双向绑定附加了一些小功能,可能在某些细节优化和刁钻需求的地方会用到:

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

在内部数据发生改变才触发响应

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

切割掉用户输入的首尾空白符


组件

// TODO 组件是 Vue.js 比较进阶的一块,得空的话可能会深入研究下,简单的文档demo后续会整理......
: )

相关文章

网友评论

      本文标题:浅谈Vue.js官方文档

      本文链接:https://www.haomeiwen.com/subject/iveikqtx.html