Vue基础

308 阅读17分钟

本文主要参考了Vue的官方文档,还结合了其他的优秀博客内容,提炼出最常用的知识点,通过本文能快速地了解或者复习Vue的基础知识。

安装

Vue Devtools

浏览器插件,允许开发者在更友好的界面调试Vue应用

script标签引入

构建文件引入

1.最新版本
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

2.特定版本
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.0"></script>

3.兼容 ES Module 
<script type="module">
  import Vue from 'https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.esm.browser.js'
</script>

4.更小构建
vue.js换成vue.min.js

开发版本和生产版本

开发版本包含完整的警告和调试模式
生产版本删除了警告,33.30KB min+gzip

NPM

构建大型Vue应用时推荐用npm,因为它能和打包器webpack配合使用

npm install vue

命令行工具CLI

命令行界面(英语:command-line interface,缩写:CLI)是在图形用户界面得到普及之前使用最为广泛的用户界面,它通常不支持鼠标,用户通过键盘输入指令,计算机接收到指令后,予以执行。
命令行界面要较图形用户界面节约计算机系统的资源。在熟记命令的前提下,使用命令行界面往往要较使用图形用户界面的操作速度要快。

CLI的起源和优势,及不同操作系统上的实现

CLI (@vue/cli) 是一个全局安装的 npm 包,提供了终端里的 vue 命令。它可以通过 vue create 快速创建一个新项目的脚手架,或者直接通过 vue serve 构建新想法的原型。你也可以通过 vue ui 通过一套图形化界面管理你的所有项目。

介绍

核心概念

  • javascript框架,网页越复杂,js文件越多,其连接了许多的html与css,缺乏正规的组织形式,这就需要js框架来组织这些文件
  • 渐进式,如果已存在一个应用,可将Vue作为一小部分嵌入其中
  • 组件化,将页面划分为一个个组件,每个组件都有自己的html、css、js,且组件可复用
  • 响应式,数据改变,页面上所有用到该数据的地方都会随之更新
  • 命令行工具,用于快速初始化大型项目
  • 单文件组件,即.vue文件,本身就是一个单独的组件(与之对应的是在.html文件中的script标 签内new Vue({el:"xxx"})),拥有template、style、script,通过vue-loader对.vue进行解析,因为浏览器不认识.vue文件。

声明式渲染

模板语法{{ x }}
对属性 v-bind:属性名="x"

条件与循环

v-if="x"
v-for="x in arrays"

处理用户输入

v-on:click="func"
<input v-model="message">

组件化应用构建

注册组件
Vue.component('todo-item', {
  props: ['todo'],
  template: '<li>{{ todo.text }}</li>'
})
使用组件
<div id="app-7">
  <ol>
    <todo-item
      v-for="item in groceryList"
      v-bind:todo="item"
      v-bind:key="item.id"
    ></todo-item>
  </ol>
</div>

组件和自定义元素的关系

相同点:实现Slot API和is特性

不同点:相比自定义元素,组件在所有支持的浏览器下表现一致(兼容性好),多了一些功能,如跨组件数据流、自定义事件通信以及构建工具集成

Vue实例

创建一个vue实例

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

数据和方法

数据-响应式
var data = { a: 1 }
var vm = new Vue({
  data: data
})

//只有当实例被创建时就已经存在于 data 中的属性才是响应式的
data.b = 'xx' // 非响应式

//响应系统无法再追踪变化
Object.freeze(data)
方法
都带有前缀$
vm.$data
vm.$el
vm.$watch('a', function (newValue, oldValue) {
  // 这个回调将在 `vm.a` 改变后调用
})
...

生命周期钩子

超详细的解释
更全的解释

本人提炼一下

beforeCreate
啥都不存在

created
Vue实例的data数据已存在,可更改

判断el是否存在
存在则继续生命周期,否则停止

页面渲染
优先级:render函数>template>html

beforeMount
<div>{{ x }}</div>

mounted
<div>内容</div>

beforeUpdate
$el$data都改变,实际是data数据改变,但页面未渲染的阶段

updated
更新完毕

beforeDestroy
此阶段实例仍可使用,可做路由跳转

destroyed
vue 实例指示的所有东西都会解绑,所有的事件监听器会被移除,所有的子实例也会被销毁。

模板语法

基于HTML,将DOM与数据绑定,Vue在底层上将模板语法编译成虚拟DOM渲染函数,这样可智能计算出最少需要重新渲染多少组件,减少DOM操作。

也可不用模板,用渲染render函数,jsx语法。

插值

文本插值( {{ }} )
<span>Message: {{ msg }}</span>

一次性插值(v-once)
<span v-once>这个将不会改变: {{ msg }}</span>

原始HTML(v-html)
<span v-html="rawHtml"></span>

特性(v-bind)
<div v-bind:id="dynamicId"></div>
<button v-bind:disabled="isButtonDisabled">Button</button>
// 若disablefalse,特性disabled不会被渲染在模板中
强调 :给属性添加变量时要加v-bind:(本人老是忘记)

js表达式
{{ }} 中可以放变量的算数操作,三元表达式,数组方法等,但不可放语句(if else var)
""引号中也可写js表达式(变量是响应的,也能识别布尔值truefalse),
不过如果想要字符串的话,加''单引号,可用+连接js表达式和字符串

指令

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

动态参数
<a v-bind:[attributeName]="url"> ... </a>

修饰符
<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>

计算属性和侦听属性

计算属性computed

语法格式如下:

<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('')
    }
  }
})
  • reversedMessage为一个属性,不是一个方法,它和普通的变量使用方式一样。
  • 无需在data中再声明一边,外面也可以和访问变量一样访问该属性(this.计算属性名)
  • 给它设置的函数,为getter函数,当访问这个属性时就返回这个函数的返回值。
  • 它依赖于message这个变量,但内部依赖变化,该计算属性也会响应式地变化。
  • 也可以设置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]
    }
  }
}

// 使用setter函数
vm.fullName = "name1 name2"

计算属性和方法methods的区别

<p>Reversed message: "{{ reversedMessage() }}"</p>

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

从以上例子可以看出,利用methods访问同样可以实现computed的功能。

不同之处如下:

  • computed放的是属性,和变量一样使用;methods放的是函数,使用时后面要加括号
  • 计算属性基于响应式依赖进行缓存的,如果依赖没变,多次访问该属性,都是直接调用缓存的,不必再次执行函数;而methods下的方法,多次访问就要多次执行函数。

但也要注意,computed下用到Date()的话,计算属性不会更新的

侦听器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
    }
  }
})

watch下监听的都是在data中声明过的变量,若变量变化,则会执行回调函数。

最佳使用场景:在数据变化时执行异步或开销较大的操作。

不过一般情况下都建议用计算属性computed,往往代码更简单,在一些数据需要随着其它数据变动而变动时更为明显。

样式绑定——class和style

属性的绑定都用v-bind:属性名="js表达式+'字符串'+'js表达式'"

原本属性的赋值只能用字符串,由于Vue的增强,还可以用对象和数组,但是还是要包在""下

绑定class

对象语法

"{类名: 变量或布尔值, 类名:变量或布尔值, ...}"

变量也要是布尔值,作用是设置是否绑定对应的类样式

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

// 允许普通赋值和对象赋值同时
<div
  class="static"
  v-bind:class="{ active: isActive, 'text-danger': hasError }"
></div>

// 更加清晰的写法
<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'
    }
  }
}

数组语法

"[变量, 变量, ...]"

变量存放着类名,作用是绑定哪些类名,也可在[]中放三元表达式

本人觉得和直接字符串赋值一样,只是要绑定多个类名,字符串要用+' '+将它们隔开,数组语法直接逗号隔开

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

// 三元表达式
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>

// 数组语法和对象语法结合
<div v-bind:class="[{ active: isActive }, errorClass]"></div>

用在组件上

语法和上面一样,注意的就是使用组件时加的类名不会覆盖原本定义组件时加的类名,两者共同存在。

绑定style

对象语法

"{css属性:变量,css属性:变量 + '单位',...}"

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

// 也常和计算属性结合

数组语法

"[变量,变量,...]"

变量存放着样式,作用是将多个样式对象添加到同一个元素上

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

对于不同的浏览器,Vue会自动添加前缀

条件渲染——v-if/v-show

v-if

v-if = "变量/表达式"

为truthy则渲染对应的内容

v-else

要有v-if或v-else-if与之相对应

v-else-if

用法同v-if

用v-if渲染分组

可以把一个<template> 元素当做不可见的包裹元素,并在上面使用 v-if,最终的渲染结果将不包含 <template> 元素。

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

key

Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染

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

// input的元素中不加key属性的话,即使用条件切换展示的元素后,之前在input的输入信息仍存在
// 因为Vue的高效渲染机制,不会替换<input>,而是改变它的placeholder属性值,其他不变

v-show

注意:作用相当于display: none而不是visibility: hidden

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

两者区别

  • v-if会真正地销毁和重建元素
  • v-if是惰性的,起初为false,则就不渲染了
  • v-show必渲染,然后改变css而已
  • 切换频繁的话用v-show,省资源;切换少的话用v-if,万一就不展示了呢!

列表渲染——v-for

v-for遍历数组

语法:

单参数:v-for="item in items"

双参数:v-for="(item, index) in items"

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

v-for遍历对象

语法:

单参数:v-for="value in object"

双参数:v-for="(value, name) in object"

三参数:v-for="(value, name, index) in object"

<ul id="v-for-object" class="demo">
  <li v-for="value in object">
    {{ value }}
  </li>
</ul>

new Vue({
  el: '#v-for-object',
  data: {
    object: {
      title: 'How to do lists in Vue',
      author: 'Jane Doe',
      publishedAt: '2016-04-10'
    }
  }
})

// 双参数
<div v-for="(value, name) in object">
  {{ name }}: {{ value }}
</div>

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

状态维护——key

Vue比较懒,能不操作DOM就不操作DOM,对v-for,当其数组变化时,Vue不会移动DOM的位置,而是重新渲染DOM使其与数组对应,这样的话虽然消耗少,但有问题(比如表单输入值还留在原位置)

建议使用v-for的时候都加key

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

数组更新检测

对v-for遍历的数组而言,其发生变化,视图应该要响应式地更新

能响应式更新视图的操作数组的方法

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

需要进一步操作的

filter()、concat() 和 slice(),因为它们不改变原数组,而是返回新数组

操作方法:

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

不能响应式更新视图

  • 通过索引值改变数组元素
  • 改变数组.length来改变数组
var vm = new Vue({
  data: {
    items: ['a', 'b', 'c']
  }
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的

解决问题1:

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

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

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

解决问题2:

vm.items.splice(newLength)

对象更新检测

Vue 不能检测对象属性的添加或删除

响应式方法:

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

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

// 添加多个对象
vm.userProfile = Object.assign({}, vm.userProfile, {
  age: 27,
  favoriteColor: 'Vue Green'
})

其他

显示过滤/排序后的结果

v-for循环的数组放在计算属性中,该计算属性返回对原数组的操作后的一个新数组,这样既不会改变原数组的顺序,又能按自己的想法显示过滤或排序后的结果

v-for可以对数字循环

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

// 输出
1 2 3 4 5 6 7 8 9 10

可以在<template>上使用v-for

v-forv-if一同使用

v-for的优先级大于v-if,从而先循环出列表,再根据条件决定是否渲染单一元素

要想实现先条件再循环的效果,将v-if放在v-for的外层元素上即可

组件上也可使用v-for

但是组件内部是无法获取循环的元素的,还是要通过prop

注意,组件用v-for的话key是必须的

事件处理——v-on

v-on语法

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>

v-on="methods中定义的方法"

注意该方法的第一个参数 event 为原生 DOM 事件

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

v-on="methods方法(js语句)"

还可传入特殊变量 $event,表示DOM事件

<div id="example-3">
  <button v-on:click="say('hi')">Say hi</button>
  <button v-on:click="say('what', $event)">Say what</button>
</div>

new Vue({
  el: '#example-3',
  methods: {
    say: function (message, event) {
      alert(message)
    }
  }
})

事件修饰符

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

按键修饰符

  • .enter
  • .tab
  • .delete (捕获“删除”和“退格”键)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right
<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<input v-on:keyup.enter="submit">

按键码的方式(快被废弃了)

<input v-on:keyup.13="submit">

自定义按键修饰符

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

系统修饰键

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

<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>
  • .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

表单绑定——v-model

在表单元素 <input><textarea><select>上创建双向数据绑定,根据控件类型自动选取正确的方法来更新元素。

本质是语法糖

基础用法

  • text 和 textarea 元素使用 value 属性和 input 事件;
  • checkbox 和 radio 使用 checked 属性和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件。

文本text

<input v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p>

多行文本textarea

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

复选框checkbox

<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: []
  }
})

全选结果 checkedNames: ["Jack", "John", "Mike"]

// 单个的话,可以不用指定value,绑定的是一个布尔值
// 多个的话,为了区分要指定value,不然就是一起勾选,一起不选,绑定的是一个数组,元素为对应value

单选框radio

<div id="example-4">
  <input type="radio" id="one" value="One" v-model="picked">
  <label for="one">One</label>
  <br>
  <input type="radio" id="two" value="Two" v-model="picked">
  <label for="two">Two</label>
  <br>
  <span>Picked: {{ picked }}</span>
</div>

new Vue({
  el: '#example-4',
  data: {
    picked: ''
  }
})

// 对于单选框,只有一个的话就没有意义,因为点了就无法取消
// 多个的话绑定的就是对应的value值

选择框

// 单选
<div id="example-5">
  <select v-model="selected">
    <option disabled value="">请选择</option>
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </select>
  <span>Selected: {{ selected }}</span>
</div>

new Vue({
  el: '...',
  data: {
    selected: ''
  }
})

// 多选
在select头部加属性multiple
Vue实例中的selected定义为空数组

值绑定

把值绑定到 Vue 实例的一个动态属性上,这时可以用 v-bind 实现,并且这个属性的值可以不是字符串。

复选框

<input
  type="checkbox"
  v-model="toggle"
  true-value="yes"
  false-value="no"
>

// 当选中时
vm.toggle === 'yes'
// 当没有选中时
vm.toggle === 'no'

单选框

<input type="radio" v-model="pick" v-bind:value="a">

// 当选中时
vm.pick === vm.a

选择框

<select v-model="selected">
    <!-- 内联对象字面量 -->
  <option v-bind:value="{ number: 123 }">123</option>
</select>

// 当选中时
typeof vm.selected // => 'object'
vm.selected.number // => 123

修饰符

.lazy

在“change”时而非“input”时更新

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

.number

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

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

.trim

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