安装
Vue.js 不支持 IE8 及其以下版本。
1 2
| # 最新稳定版 $ npm install vue
|
1 2 3 4 5 6 7 8
| # 全局安装 vue-cli $ npm install --global vue-cli # 创建一个基于 webpack 模板的新项目 $ vue init webpack my-project # 安装依赖,走你 $ cd my-project $ npm install $ npm run dev
|
1 2 3 4
| git clone https://github.com/vuejs/vue.git node_modules/vue cd node_modules/vue npm install npm run build
|
介绍
一套构建用户界面的渐进式框架。与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计。Vue 的核心库只关注视图层。
申明式渲染:
1 2 3 4 5 6 7 8 9 10
| <div id="app"> {{ message }} </div>
var app = new Vue({ el: '#app', data: { message: 'Hello Vue!' } })
|
条件与循环
1 2 3 4 5 6 7 8 9 10
| <div id="app-3"> <p v-if="seen">现在你看到我了</p> </div>
var app3 = new Vue({ el: '#app-3', data: { seen: true } })
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <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: '整个牛项目' } ] } })
|
处理用户输入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <div id="app-5"> <p>{{ message }}</p> <button v-on:click="reverseMessage">逆转消息</button> </div>
var app5 = new Vue({ el: '#app-5', data: { message: 'Hello Vue.js!' }, methods: { reverseMessage: function () { this.message = this.message.split('').reverse().join('') } } })
|
1 2 3 4 5 6 7 8 9 10 11
| <div id="app-6"> <p>{{ message }}</p> <input v-model="message"> </div>
var app6 = new Vue({ el: '#app-6', data: { message: 'Hello Vue!' } })
|
组件化应用构建
1 2 3 4 5 6 7 8 9
| // 定义名为 todo-item 的新组件 Vue.component('todo-item', { template: '<li>这是个待办项</li>' })
<ol> <!-- 创建一个 todo-item 组件的实例 --> <todo-item></todo-item> </ol>
|
子单元通过 props 接口实现了与父单元很好的解耦。
Vue实例
1 2 3
| var vm = new Vue({ // 选项 })
|
属性和方法
1 2 3 4 5 6 7 8 9 10 11
| 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` 改变后调用 })
|
被代理的属性是响应的
生命周期
1 2 3 4 5 6 7 8 9 10
| var vm = new Vue({ data: { a: 1 }, created: function () { // `this` 指向 vm 实例 console.log('a is: ' + this.a) } }) // -> "a is: 1"
|
mounted、updated、destroyed。钩子的 this 指向调用它的 Vue 实例。
模板语法
插值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <span>Message: {{ msg }}</span>
<span v-once>这个将不会改变: {{ msg }}</span>
<p>Using mustaches: {{ rawHtml }}</p> <p>Using v-html directive: <span v-html="rawHtml"></span></p>
<div v-bind:id="dynamicId"></div>
<button v-bind:disabled="isButtonDisabled">Button</button>
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>
|
指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <p v-if="seen">现在你看到我了</p>
<a v-bind:href="url">...</a>
<a v-on:click="doSomething">...</a>
<form v-on:submit.prevent="onSubmit">...</form>
<!-- 完整语法 --> <a v-bind:href="url">...</a>
<!-- 缩写 --> <a :href="url">...</a>
<!-- 完整语法 --> <a v-on:click="doSomething">...</a>
<!-- 缩写 --> <a @click="doSomething">...</a>
|
计算属性和侦听器
计算属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <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('') } } })
|
计算属性是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变时才会重新求值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| //计算属性的 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] } } } // ...
|
侦听器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| <div id="watch-example"> <p> Ask a yes/no question: <input v-model="question"> </p> <p>{{ answer }}</p> </div>
<!-- 因为 AJAX 库和通用工具的生态已经相当丰富,Vue 核心代码没有重复 --> <!-- 提供这些功能以保持精简。这也可以让你自由选择自己更熟悉的工具。 --> <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> <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, oldQuestion) { 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>
|
Class 与 Style 绑定
绑定 HTML Class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <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="[activeClass, errorClass]"></div>
data: { activeClass: 'active', errorClass: 'text-danger' }
<div v-bind:class="[{ active: isActive }, errorClass]"></div>
|
绑定内联样式
1 2 3 4 5 6 7 8 9
| <div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data: { activeColor: 'red', fontSize: 30 }
//多个样式对象应用到同一个元素上 <div v-bind:style="[baseStyles, overridingStyles]"></div>
|
条件渲染
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <h1 v-if="ok">Yes</h1> <h1 v-else>No</h1>
<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>
|
声明“这两个元素是完全独立的——不要复用它们”。只需添加一个具有唯一值的 key 属性即可
v-show 是简单地切换元素的 CSS 属性 display 。不支持 语法,也不支持 v-else。
v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件不太可能改变,则使用 v-if 较好。
列表渲染
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <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' } ] } }) //对象迭代 <div v-for="(value, key, index) in object"> {{ index }}. {{ key }} : {{ value }} </div>
|
1 2 3 4 5 6 7
| <my-component v-for="(item, index) in items" v-bind:item="item" v-bind:index="index" v-bind:key="item.id" ></my-component> //组件有自己独立的作用域。为了传递迭代数据到组件里,我们要用 props
|
v-for 的优先级比 v-if 更高,这意味着 v-if 将分别重复运行于每个 v-for 循环中。
Vue 包含一组观察数组的变异方法,触发视图更新:
1 2 3 4 5 6 7
| push() pop() shift() unshift() splice() sort() reverse()
|
显示一个数组的过滤或排序副本
1 2 3 4 5 6 7 8 9 10 11 12
| <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-on 指令监听 DOM 事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <div id="example-1"> <button v-on:click="counter += 1">增加 1</button> <p>这个按钮被点击了 {{ counter }} 次。</p> </div>
var example1 = new Vue({ el: '#example-1', data: { counter: 0 } })
<button v-on:click="warn('Form cannot be submitted yet.', $event)"> Submit </button>
// ... methods: { warn: function (message, event) { // 现在我们可以访问原生事件对象 if (event) event.preventDefault() alert(message) } }
|
事件修饰符
1 2 3 4 5 6 7 8 9 10 11 12
| <!-- 阻止单击事件冒泡 --> <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> <!-- 只当事件在该元素本身(比如不是子元素)触发时触发回调 --> <div v-on:click.self="doThat">...</div>
|
键值修饰符
1 2 3 4 5 6 7 8 9
| .enter .tab .delete (捕获 “删除” 和 “退格” 键) .esc .space .up .down .left .right
|
修饰符
表单控件绑定
v-model 指令在表单控件元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。
1 2 3 4 5 6 7 8 9 10 11
| <input v-model="message" placeholder="edit me"> <p>Message is: {{ message }}</p>
<!-- 当选中时,`picked` 为字符串 "a" --> <input type="radio" v-model="picked" value="a"> <!-- `toggle` 为 true 或 false --> <input type="checkbox" v-model="toggle"> <!-- 当选中时,`selected` 为字符串 "abc" --> <select v-model="selected"> <option value="abc">ABC</option> </select>
|
1 2 3 4 5 6 7 8 9 10
| .lazy <!-- 在 "change" 而不是 "input" 事件中更新 --> <input v-model.lazy="msg" >
.number <input v-model.number="age" type="number">
.trim <input v-model.trim="msg"> v-model 与组件
|
组件
组件可以扩展 HTML 元素,封装可重用的代码。
使用组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 全局注册组件 Vue.component('my-component', { // 选项 }) //确保在初始化根实例之前注册了组件 <div id="example"> <my-component></my-component> </div>
局部注册 var Child = { template: '<div>A custom component!</div>' } new Vue({ // ... components: { // <my-component> 将只在父模板可用 'my-component': Child } }) Vue 构造器传入的各种选项大多数都可以在组件里用。 data 是一个例外,它必须是函数。
|
Vue 中,父子组件的关系可以总结为 props down, events up。父组件通过 props 向下传递数据给子组件,子组件通过 events 给父组件发送消息。
1 2 3 4 5 6 7 8 9
| Vue.component('child', { // 声明 props props: ['message'], // 就像 data 一样,prop 可以用在模板内 // 同样也可以在 vm 实例中像“this.message”这样使用 template: '<span>{{ message }}</span>' })
<child message="hello!"></child>
|
动态地绑定父组件的数据到子模板的 props,就是用 v-bind。
1
| <child v-bind:my-message="parentMsg"></child>
|
每次父组件更新时,子组件的所有 prop 都会更新为最新值。这意味着你不应该在子组件内部改变 prop。
props 指定验证规格:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| Vue.component('example', { 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 value > 10 } } } })
|
非prop属性
每个 Vue 实例都实现了事件接口 (Events interface),即:
使用 $on(eventName) 监听事件
使用 $emit(eventName) 触发事件
某个组件的根元素上监听一个原生事件。可以使用 .native 修饰 v-on。
有时候两个组件也需要通信 (非父子关系)。在简单的场景下,可以使用一个空的 Vue 实例作为中央事件总线:
1 2 3 4 5 6 7
| var bus = new Vue() // 触发组件 A 中的事件 bus.$emit('id-selected', 1) // 在组件 B 创建的钩子中监听事件 bus.$on('id-selected', function (id) { // ... })
|
slot分发内容
元素作为原始内容的插槽。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <div class="container"> <header> <slot name="header"></slot> </header> <main> <slot></slot> </main> <footer> <slot name="footer"></slot> </footer> </div>
<app-layout> <h1 slot="header">这里可能是一个页面标题</h1> <p>主要内容的一个段落。</p> <p>另一个主要段落。</p> <p slot="footer">这里有一些联系信息</p> </app-layout>
|
动态组件
动态地绑定到它的 is 特性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| var vm = new Vue({ el: '#example', data: { currentView: 'home' }, components: { home: { /* ... */ }, posts: { /* ... */ }, archive: { /* ... */ } } })
<component v-bind:is="currentView"> <!-- 组件在 vm.currentview 变化时改变! --> </component>
|
Props 允许外部环境传递数据给组件
Events 允许从外部环境在组件内触发副作用
Slots 允许外部环境将额外的内容组合在组件中。
1 2 3 4 5 6 7
| <div id="parent"> <user-profile ref="profile"></user-profile> </div>
var parent = new Vue({ el: '#parent' }) // 访问子组件 var child = parent.$refs.profile
|
过当组件中包含大量静态内容时,可以考虑使用 v-once 将渲染结果缓存起来
深入响应式原理
过渡效果
在 CSS 过渡和动画中自动应用 class
可以配合使用第三方 CSS 动画库,如 Animate.css
在过渡钩子函数中使用 JavaScript 直接操作 DOM
可以配合使用第三方 JavaScript 动画库,如 Velocity.js
Vue 提供了 transition 的封装组件:
自动嗅探目标元素是否应用了 CSS 过渡或动画,如果是,在恰当的时机添加/删除 CSS 类名。
如果过渡组件提供了 JavaScript 钩子函数,这些钩子函数将在恰当的时机被调用。
如果没有找到 JavaScript 钩子并且也没有检测到 CSS 过渡/动画,DOM 操作(插入/删除)在下一帧中立即执行。
js钩子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| // ... methods: { // -------- // 进入中 // -------- beforeEnter: function (el) { // ... }, // 此回调函数是可选项的设置 // 与 CSS 结合时使用 enter: function (el, done) { // ... done() }, afterEnter: function (el) { // ... }, enterCancelled: function (el) { // ... }, // -------- // 离开时 // -------- beforeLeave: function (el) { // ... }, // 此回调函数是可选项的设置 // 与 CSS 结合时使用 leave: function (el, done) { // ... done() }, afterLeave: function (el) { // ... }, // leaveCancelled 只用于 v-show 中 leaveCancelled: function (el) { // ... } }
|
过渡状态
数字和运算
颜色的显示
SVG 节点的位置
元素的大小和其他的属性
Render 函数
createElement参数
js代替模板功能
jsx
函数组件化
自定义指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| // 注册一个全局自定义指令 v-focus Vue.directive('focus', { // 当绑定元素插入到 DOM 中。 inserted: function (el) { // 聚焦元素 el.focus() } })
directives: { focus: { // 指令的定义--- } }
|
混合
混合 (mixins) 是一种分发 Vue 组件中可复用功能的非常灵活的方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| // 定义一个混合对象 var myMixin = { created: function () { this.hello() }, methods: { hello: function () { console.log('hello from mixin!') } } } // 定义一个使用混合对象的组件 var Component = Vue.extend({ mixins: [myMixin] }) var component = new Component() // -> "hello from mixin!" 选项合并
|
插件
添加全局方法或者属性,如: vue-custom-element
添加全局资源:指令/过滤器/过渡等,如 vue-touch
通过全局 mixin 方法添加一些组件选项,如: vue-router
添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。
一个库,提供自己的 API,同时提供上面提到的一个或多个功能,如 vue-router
单文件组件
生产环境部署提示
路由
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const NotFound = { template: '<p>Page not found</p>' } const Home = { template: '<p>home page</p>' } const About = { template: '<p>about page</p>' } const routes = { '/': Home, '/about': About } new Vue({ el: '#app', data: { currentRoute: window.location.pathname }, computed: { ViewComponent () { return routes[this.currentRoute] || NotFound } }, render (h) { return h(this.ViewComponent) } })
|
状态管理
单元测试
服务端渲染
TypeScript 支持