Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@lgh06
Last active April 14, 2018 14:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lgh06/7bf9c4576737278e415208c0759eb93e to your computer and use it in GitHub Desktop.
Save lgh06/7bf9c4576737278e415208c0759eb93e to your computer and use it in GitHub Desktop.
Vue.js Guide Essentials-说人话-速记版

以下内容根据Vue.js Guide Essentials部分速记。
不含动画/mixin/SSR/路由/状态管理等部分.

Introduction

建议阅读原文 https://vuejs.org/v2/guide/index.html

什么是Vue
开始
声明式渲染
条件与循环
处理用户输入
组件

The Vue Instance

建议阅读原文 https://vuejs.org/v2/guide/instance.html

创建Vue实例
data & methods
实例生命周期及其钩子函数
生命周期图表

Template Syntax

模板语法 https://vuejs.org/v2/guide/syntax.html

Mustache模板中插入值

html标签内的文本:使用{{变量名}}
原始html:标签属性内使用v-html="变量名"
标签内的属性:v-bind:属性名="变量名" 也可用JS的表达式来替换上述"变量名",但只能是单条表达式,不能是语句

v-if等指令

argument:如v-bind:href中的href
modifier:如v-on:submit.prevent="onSubmit"中的.prevent

v-bind、v-on简写

v-bind:href等同于:href
v-on:click="doSth"等同于@cllick="doSth"

Computed Properties and Watchers

https://vuejs.org/v2/guide/computed.html

Computed Properties

js内的computed函数
computed属性会被缓存,出于性能考虑,不建议在{{}}中的表达式内执行method
computed属性会被缓存,即使是一个函数,也并不会每次都执行
computed是根据已有变量计算出的新变量

Watchers

watch用来监视已有变量,执行进一步操作.

Class and Style Bindings

(html标签中的)class与style绑定 https://vuejs.org/v2/guide/class-and-style.html

class

v-bind:class内的表达式会自动和class内已有class同时存在

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

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

也可以使用computed函数计算出的属性值/对象为class内的属性值/对象做数据来源

也可以使用数组来应用多个class名:

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

与其在array内使用三目运算符,不如嵌套使用object与array:

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

使用js定义组件时的class先出现,使用组件时添加的class(无论直接使用class还是v-bind:class)后出现.

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

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

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

2.
<my-component v-bind:class="{ active: isActive }"></my-component>  
结果: <p class="foo bar active">Hi</p>

style 内联样式

和class很像,但必须是object

可以:
<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'
  }
}  
也可以使用computed  

也可以用对象数组:
<div v-bind:style="[baseStyles, overridingStyles]"></div>
(baseStyles/overridingStyles都是object)

使用v-bind:style会自动加上prefix
也可以手动加:

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

Conditional Rendering

条件渲染 https://vuejs.org/v2/guide/conditional.html

v-if/v-else/v-else-if

<h1 v-if="ok">Yes</h1>
也可以配合else使用: 
<h1 v-else>No</h1>

v-if可以和template元素结合使用,最终结果不包含template标签:

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

v-else必须紧跟v-if或v-else-if v-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-else-if必须紧跟v-if或v-else-if

相同元素自动复用:

<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元素
会自动复用之前的元素
如果想避免自动复用,给input标签加上不同的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)

v-show

只控制css的display.
不支持template元素,不支持和v-else同时使用
经常切换显示隐藏,使用v-show
偶尔显示,变动少,使用v-if

v-if和v-for配合使用

v-for的优先级高.(见下一节)

List Rendering

列表渲染 https://vuejs.org/v2/guide/list.html

v-for遍历array

<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内部可以访问外部的所有变量, 还可以获取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' }
    ]
  }
})<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内的index和parentMessage

v-for遍历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: {
      firstName: 'John',
      lastName: 'Doe',
      age: 30
    }
  }
})
结果:
<ul id="v-for-object" class="demo">
  <li>John</li>
  <li>Doe</li>
  <li>30</li>
</ul>
(只有值,不管键名)

或者:

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

内部引擎使用Object.keys()来遍历,不保证所有浏览器都表现一致

key

如果数据的顺序变动,vue不是调整各个子元素的顺序,而是直接修改现有子元素的内容
为了规避以上情况,可以给每个子元素绑定key:

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

探测数组更改情况

以下的数组方法都已经被Vue封装好,vue可以观察到原有数组的更改

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

替换数组:

filter()
concat()
slice()
...

以上方法不改变原有数组,直接返回新数组. vue的使用方法如下:

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

数组更改警告

由于JS的限制,以下两种情况无法被vue观察到,请使用替代方案:

  • 直接用索引设置某项的值
// Vue.set
Vue.set(example1.items, indexOfItem, newValue)
或者
// Array.prototype.splice
example1.items.splice(indexOfItem, 1, newValue)
  • 修改数组的长度
example1.items.splice(newLength)

对象更改警告

由于JS限制,vue观察不到动态添加根级别的属性到已有的实例:

var vm = new Vue({
  data: {
    a: 1
  }
})
// `vm.a` is now reactive

vm.b = 2
// `vm.b` is NOT reactive

但是允许内层的object添加属性:

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

可以使用
Vue.set(vm.userProfile, 'age', 27)
或者
vm.$set(this.userProfile, 'age', 27)

如果需要更新多个属性,直接使用添加属性后的新对象替换原有对象,
不要:

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

而应该这样:

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

筛选与排序

不对原始数据修改,仅对需要显示的作筛选\排序等. 灵活运用computed或methods:

computed:

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

methods(在嵌套v-for循环内尤其有用):

<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 with a Range

v-for可以直接遍历整数:

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

v-for on a

与v-if类似,可以结合template使用

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

v-for与v-if

v-for has a higher priority than v-if.

v-if将在每个for循环内都执行
<li v-for="todo in todos" v-if="!todo.isComplete">
  {{ todo }}
</li>

如果想先if出结果,再for循环,请嵌套使用(也可用template):

<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>
在组件内使用v-for, :key为必须的


但仍需注意父子组件之间的数据传递.
(子组件的js内使用props, 标签内使用v-bind:变量名,使用$emit与父组件通信)

Event Handling

事件处理 https://vuejs.org/v2/guide/events.html

标签内使用v-on:事件名="xxx"

xxx可以是表达式(直接修改数据)

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

也可以是method方法名

<div id="example-2">
  <!-- `greet` is the name of a method defined below -->
  <button v-on:click="greet">Greet</button>
</div>
var example2 = new Vue({
  el: '#example-2',
  data: {
    name: 'Vue.js'
  },
  // define methods under the `methods` object
  methods: {
    greet: function (event) {
      // `this` inside methods points to the Vue instance
      alert('Hello ' + this.name + '!')
      // `event` is the native DOM event
      if (event) {
        alert(event.target.tagName)
      }
    }
  }
})

// you can invoke methods in JavaScript too
example2.greet() // => 'Hello Vue.js!'

也可以是一段js语句,可以选择性地传递参数到method内:

<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 event参数,使用$event:

<button v-on:click="warn('Form cannot be submitted yet.', $event)">
  Submit
</button>
// ...
methods: {
  warn: function (message, event) {
    // now we have access to the native event
    if (event) event.preventDefault()
    alert(message)
  }
}

Event Modifiers 事件修饰符

可以多个同时使用,也可以只使用修饰符,不指定事件handler

.stop 停止向上传播
.prevent preventDefault
.capture 捕获子元素内已处理的事件
.self 只管本元素的事件,不考虑子元素
.once 触发至多一次,可以用在组件的自定义事件中  

<!-- the submit event will no longer reload the page -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- modifiers can be chained -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- just the modifier -->
<form v-on:submit.prevent></form>

修饰符是有顺序的.
Therefore using @click.prevent.self will prevent all clicks while @click.self.prevent will only prevent clicks on the element itself.

按键修饰符

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

完整的按键修饰符:

.enter
.tab
.delete (captures both “Delete” and “Backspace” keys)
.esc
.space
.up
.down
.left
.right

自定义按键修饰符:

// enable `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112

自动按键修饰符

可以根据KeyboardEvent.key的名称,转换为小写-小写的形式,作为按键修饰符.
如: .page-down

<input @keyup.page-down="onPageDown">
In the above example, the handler will only be called if $event.key === 'PageDown'.

系统按键修饰符

.ctrl
.alt
.shift
.meta

Note: On Macintosh keyboards, meta is the command key (⌘). On Windows keyboards, meta is the windows key (⊞). On Sun Microsystems keyboards, meta is marked as a solid diamond (◆). On certain keyboards, specifically MIT and Lisp machine keyboards and successors, such as the Knight keyboard, space-cadet keyboard, meta is labeled “META”. On Symbolics keyboards, meta is labeled “META” or “Meta”.

.exact

单独按某个键,而不是组合按键中的其中一个键

<!-- this will fire even if Alt or Shift is also pressed -->
<button @click.ctrl="onClick">A</button>

<!-- this will only fire when Ctrl and no other keys are pressed -->
<button @click.ctrl.exact="onCtrlClick">A</button>

<!-- this will only fire when no system modifiers are pressed -->
<button @click.exact="onClick">A</button>

鼠标按钮修饰符

.left
.right
.middle

Form Input Bindings

表单输入绑定 https://vuejs.org/v2/guide/forms.html
input(输入框、单选、多选)、textarea、select单选多选 等

基本用法

v-model,将忽略任何标签上的初始值,只把vue实例的data作为数据来源

input text

textarea

input checkbox

单选一 一个checkbox绑定到一个v-model,data为Boolean
多选多 多个checkbox绑定到同一个v-model,data为数组

input radio(多选一 圆按钮)

多选一 多个input radio绑定到同一个v-model,data为字符串,结果为input标签内的value

select

单选 建议第一项为一个disabled的option 多选,添加multiple属性

数据绑定

radio/select options的v-model为字符串,checkbox为布尔值
可以把v-model与自定义的value值做绑定,而不是浏览器默认的value值.


<input
  type="checkbox"
  v-model="toggle"
  true-value="yes"
  false-value="no"
>
// when checked:
vm.toggle === 'yes'
// when unchecked:
vm.toggle === 'no'


<input type="radio" v-model="pick" v-bind:value="a">
// when checked:
vm.pick === vm.a


<select v-model="selected">
  <!-- inline object literal -->
  <option v-bind:value="{ number: 123 }">123</option>
</select>
// when selected:
typeof vm.selected // => 'object'
vm.selected.number // => 123

修饰符


<!-- synced after "change" instead of "input" -->
<input v-model.lazy="msg" >
转换为数字类型
<input v-model.number="age" type="number">
去空格
<input v-model.trim="msg">

Components

组件 https://vuejs.org/v2/guide/components.html

使用组件

https://vuejs.org/v2/guide/components.html#Using-Components

全局注册、局部注册

data必须是一个函数

父子组件的关系: props down, events up
父组件通过prop属性传递给子组件;子组件通过触发事件,把数据传递给父组件

Props

https://vuejs.org/v2/guide/components.html#Props

使用props传递数据

在子组件的js声明中,添加props(和data同级),props才能从父组件传递到子组件。
同时,需要在父组件标签中添加这个属性,该属性才能传递到子组件内。

camelCase-vs-kebab-case

html标签内不区分大小写,所以使用连接横杠-,js的props内使用驼峰式命名法(首字母小写)

动态属性

通过v-bind:子组件数据名称=父组件数据名称
也可以v-bind=对象名

todo: {
  text: 'Learn Vue',
  isComplete: false
}

<todo-item v-bind="todo"></todo-item>

等同于

<todo-item
  v-bind:text="todo.text"
  v-bind:is-complete="todo.isComplete"
></todo-item>

标签中的属性字面量与v-bind:xxx的动态变量

直接使用属性,是字面量;v-bind:xx是表达式。
因此,直接使用属性,数据类型都是字符串;v-bind的在经过表达式运算之后,可以是数字类型(等等)

单向数据流

父组件的数据通过props传递给子组件,子组件通过computed或data来进行数据处理。
不要在子组件直接改变父组件的数据。
把父组件传递的数据当做子组件的初始值。
js中的对象和数组传递的是引用值,子组件如果修改,会影响父组件

prop验证

可以在子组件的js内,定义父组件需要传递给子组件的props名称、类型、是否必须等

无prop的属性

https://vuejs.org/v2/guide/components.html#Non-Prop-Attributes
(子组件的js内)未定义某个prop,父组件直接在标签上传入某个prop
通常被用在class、style上

自定义事件

https://vuejs.org/v2/guide/components.html#Custom-Events
$on/$emit和浏览器原生的 EventTarget API 没有关系

在父组件的上下文环境中,使用v-on来监听子组件触发($emit)的事件

<div id="counter-event-example">
  <p>{{ total }}</p>
  <button-counter v-on:increment="incrementTotal"></button-counter>
  <button-counter v-on:increment="incrementTotal"></button-counter>
</div>
Vue.component('button-counter', {
  template: '<button v-on:click="incrementCounter">{{ counter }}</button>',
  data: function () {
    return {
      counter: 0
    }
  },
  methods: {
    incrementCounter: function () {
      this.counter += 1
      this.$emit('increment')
    }
  },
})

new Vue({
  el: '#counter-event-example',
  data: {
    total: 0
  },
  methods: {
    incrementTotal: function () {
      this.total += 1
    }
  }
})

使用.native修饰符去监听原生事件

使用.sync修饰符

<comp :foo.sync="bar"></comp>
实质为:
<comp :foo="bar" @update:foo="val => bar = val"></comp>

在子组件的js内,需要触发update:foo事件来更新foo的值

this.$emit('update:foo', newValue)

表单输入与自定义事件

https://vuejs.org/v2/guide/components.html#Form-Input-Components-using-Custom-Events

<input v-model="something">
实质为:
<input
  v-bind:value="something"
  v-on:input="something = $event.target.value">

如果想让自定义组件与v-model协作,自定义组件需要:

  • accept a value prop
  • 触发 input 事件,并传入新值

自定义组件的v-model

在js中添加model字段

Vue.component('my-checkbox', {
  model: {
    prop: 'checked',
    event: 'change'
  },
  props: {
    checked: Boolean,
    // this allows using the `value` prop for a different purpose
    value: String
  },
  // ...
})
<my-checkbox v-model="foo" value="some value"></my-checkbox>

以上等同于:

<my-checkbox
  :checked="foo"
  @change="val => { foo = val }"
  value="some value">
</my-checkbox>

非父子组件之间的通信

使用一个中间Vue实例来传递消息,或使用Vuex.

var bus = new Vue()
// in component A's method
bus.$emit('id-selected', 1)
// in component B's created hook
bus.$on('id-selected', function (id) {
  // ...
})

内容分发与slot

https://vuejs.org/v2/guide/components.html#Content-Distribution-with-Slots

单slot

用实际使用某个组件时写入到组件标签内的内容,
去替换组件原有的模板中的slot的内容.
(然后再将组件内的所有内容合并到父级元素内)

如果实际使用组件时,组件标签内没有内容,
则显示组件原有的模板中的slot内容.
(然后再将组件内的所有内容合并到父级元素内)

具有名字的slot

  <div class="parent" id='parent' >
    <my-comp>
      content at parent
      <div slot="footer">
        <h2>
          content at parent's footer slot
        </h2>
      </div>
    </my-comp>
  </div>

  <p>
  <h2>h2 inside p   (wrong!!!)</h2>
  </p>


  <script>
    
    ;(function () {
      // register
      Vue.component('my-comp', {
        template:
            `<div>
              content at my component. <br><br>
              <slot>content at my-comp's slot</slot>
              <pre>
                <slot name="footer">content at my-comp's footer slot</slot>
              </pre>
            </div>`
      })
      var parent = new Vue({
        el:'#parent'
      })
      
    })();
    
    
  </script>

动态组件

https://vuejs.org/v2/guide/components.html#Dynamic-Components

var vm = new Vue({
  el: '#example',
  data: {
    currentView: 'home'
  },
  components: {
    home: { /* ... */ },
    posts: { /* ... */ },
    archive: { /* ... */ }
  }
})
<component v-bind:is="currentView">
  <!-- component changes when vm.currentView changes! -->
</component>

If you prefer, you can also bind directly to component objects:
var Home = {
  template: '<p>Welcome home!</p>'
}

var vm = new Vue({
  el: '#example',
  data: {
    currentView: Home
  }
})

keep-alive

<keep-alive>
  <component :is="currentView">
    <!-- inactive components will be cached! -->
  </component>
</keep-alive>

杂项

https://vuejs.org/v2/guide/components.html#Misc

编写可复用组件

Props/Events/Slots 结合使用

引用子组件

直接用js访问子组件.
需要在使用子组件时,标签内添加ref="xxx"

var parent = new Vue({ el: '#parent' })
// 访问子组件实例
var child = parent.$refs.profile

异步组件

结合webpack的import()

高级异步组件

const AsyncComp = () => ({
  // The component to load. Should be a Promise
  component: import('./MyComp.vue'),
  // A component to use while the async component is loading
  loading: LoadingComp,
  // A component to use if the load fails
  error: ErrorComp,
  // Delay before showing the loading component. Default: 200ms.
  delay: 200,
  // The error component will be displayed if a timeout is
  // provided and exceeded. Default: Infinity.
  timeout: 3000
})

组件命名约定

(使用JS)声明组件时, PascalCase,
(标签内)使用组件时, <kebab-case>

递归组件

https://vuejs.org/v2/guide/components.html#Recursive-Components

组件之间的相互引用

添加beforeCreate 函数

beforeCreate: function () {
  this.$options.components.TreeFolderContents = require('./tree-folder-contents.vue')
}

内联模板

模板的标签内添加inline-template

X-Templates

<script type="text/x-template" id="hello-world-template">
  <p>Hello hello hello</p>
</script>

Vue.component('hello-world', {
  template: '#hello-world-template'
})

v-once

很长很长的,只渲染一次的内容,比如,用户协议.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment