导图社区 Vue
Vue学习记录,分享给需要的人 个人总结,不喜欢不要喷我。
编辑于2020-03-09 08:11:07Vue学习
Vue入门学习路线
扎实的javascript、html、css基本功底
通读官方文档的基础篇,不使用任何构建工具,使用简单的<script>,将教程中的额例子都写一遍,理解用法,没有node、webpack基础不建议直接用vue-cli构建项目
掌握了基本用法后,实现一些小功能来熟练基础,加深理解
阅读官方文档的进阶部分内容(Custom Directive),理解Vue的响应式机制和组件生命周期
阅读学习关于路由和状态管理的章节,根据需要学习vue-router和vuex,以理解用法为主,尽量手敲代码
前端生态、工程化
1. 了解 JavaScript 背后的规范,ECMAScript 的历史和目前的规范制定方式。学习 ES2015/16 的新特性,理解 ES2015 modules。
2. 学习命令行的使用。
3. 学习 Node.js 基础。建议使用 nvm 这样的工具来管理机器上的 Node 版本,并且将 npm 的 registry 注册表配置为淘宝的镜像源。至少要了解 npm 的常用命令,npm scripts 如何使用,语义化版本号规则,CommonJS 模块规范(了解它和 ES2015 Modules 的异同),Node 包的解析规则,以及 Node 的常用 API。应当做到可以自己写一些基本的命令行程序。注意最新版本的 Node (6+) 已经支持绝大部分 ES2015 的特性,可以借此巩固 ES2015。
4. 了解如何使用 / 配置 Babel 来将 ES2015 编译到 ES5 用于浏览器环境。
5. 学习 Webpack。Webpack 是一个极其强大同时也复杂的工具,作为起步,理解它的『一切皆模块』的思想,并基本了解其常用配置选项和 loader 的概念/使用方法即可,比如如何搭配 Webpack 使用 Babel。学习 Webpack 的一个挑战在于其本身文档的混乱,建议多搜索搜索,应该还是有质量不错的第三方教程的。英文好的建议阅读 Webpack 2.0 的文档,比起 1.0 有极大的改善,但需要注意和 1.0 的不兼容之处。
Vue进阶
1. 有了 Node 和 Webpack 的基础,可以通过 vue-cli 来搭建基于 Webpack ,并且支持单文件组件的项目了。建议用 webpack-simple 这个模板开始,并阅读官方教程进阶篇剩余的内容以及 vue-loader 的文档,了解一些进阶配置。有兴趣的可以自己亲手从零开始搭一个项目加深理解。
2. 根据 例子 尝试在 Webpack 模板基础上整合 vue-router 和 vuex
3. 深入理解 Virtual DOM 和『渲染函数 (Render Functions)』这一章节(可选择性使用 JSX),理解模板和渲染函数之间的对应关系,了解其使用方法和适用场景。
4. (可选)根据需求,了解服务端渲染的使用(需要配合 Node 服务器开发的知识)。其实更重要的是理解它所解决的问题并搞清楚你是否需要它。
5. 阅读开源的 Vue 应用、组件、插件源码,自己尝试编写开源的 Vue 组件、插件。
6. 参考 贡献指南 阅读 Vue 的源码,理解内部实现细节。(需要了解 Flow)
7. 参与 Vue GitHub issue 的定位 -> 贡献 PR -> 加入核心团队 -> 升任 CTO -> 迎娶白富美…(误
基础学习
安装
可以直接下载vue.js文件引入文档就可以开始使用了 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
# 最新稳定版 $ npm install vue
命令行工具 (CLI)
// 需要编译器 new Vue({ template: '<div>{{ hi }}</div>' })
// 不需要编译器 new Vue({ render (h) { return h('div', this.hi) } }) 当使用 vue-loader 或 vueify 的时候,*.vue 文件内部的模板会在构建时预编译成 JavaScript。你在最终打好的包里实际上是不需要编译器的,所以只用运行时版本即可。
webpack odule.exports = { // ... resolve: { alias: { 'vue$': 'vue/dist/vue.esm.js' // 用 webpack 1 时需用 'vue/dist/vue.common.js' } } }
Rollup const alias = require('rollup-plugin-alias') rollup({ // ... plugins: [ alias({ 'vue': require.resolve('vue/dist/vue.esm.js') }) ] })
Browserify添加到你项目的 package.json: { // ... "browser": { "vue": "vue/dist/vue.common.js" } }
Parcel在你项目的 package.json 中添加: { // ... "alias": { "vue" : "./node_modules/vue/dist/vue.common.js" } }
开发环境 vs 生产环境模式
CommonJS 和 ES Module 版本同时保留原始的 process.env.NODE_ENV 检测
webpack在 webpack 4+ 中,你可以使用 mode 选项: module.exports = { mode: 'production' }
但是在 webpack 3 及其更低版本中,你需要使用 DefinePlugin: var webpack = require('webpack') module.exports = { // ... plugins: [ // ... new webpack.DefinePlugin({ 'process.env': { NODE_ENV: JSON.stringify('production') } }) ] }
Rollup使用 rollup-plugin-replace: const replace = require('rollup-plugin-replace') rollup({ // ... plugins: [ replace({ 'process.env.NODE_ENV': JSON.stringify('production') }) ] }).then(...) Browserify为你的包应用一次全局的 envify 转换。 NODE_ENV=production browserify -g envify -e main.js | uglifyjs -c -m > build.js也可以移步生产环境部署。
CSP 环境
有些环境,如 Google Chrome Apps,会强制应用内容安全策略 (CSP),不能使用 new Function() 对表达式求值。这时可以用 CSP 兼容版本。完整版本依赖于该功能来编译模板,所以无法在这些环境下使用。 另一方面,运行时版本则是完全兼容 CSP 的。当通过 webpack + vue-loader 或者 Browserify + vueify 构建时,模板将被预编译为 render 函数,可以在 CSP 环境中完美运行。
重要: GitHub 仓库的 /dist 文件夹只有在新版本发布时才会提交。如果想要使用 GitHub 上 Vue 最新的源码,你需要自己构建! git clone https://github.com/vuejs/vue.git node_modules/vue cd node_modules/vue npm install npm run build
什么是vue.js
是一套用于构建用户界面的渐进式框架 Vue 被设计为可以自底向上逐层应用
什么是渐进式框架?
渐进式:主张很少 放入多少就做多少
灵活使用,可以整个全家桶引入,也可以只用一两个组件,当做jquery来用
可以使用他的视图,下层使用你自己的js实现
也可以使用vue的函数式
vue 是 The ProgressiveJavaScript Framework
比如说,Angular,它两个版本都是强主张的,如果你用它,必须接受以下东西: - 必须使用它的模块机制- 必须使用它的依赖注入- 必须使用它的特殊形式定义组件(这一点每个视图框架都有,难以避免)
所以Angular是带有比较强的排它性的,如果你的应用不是从头开始,而是要不断考虑是否跟其他东西集成,这些主张会带来一些困扰。
比如React,它也有一定程度的主张,它的主张主要是函数式编程的理念,比如说,你需要知道什么是副作用,什么是纯函数,如何隔离副作用。它的侵入性看似没有Angular那么强,主要因为它是软性侵入。
Vue可能有些方面是不如React,不如Angular,但它是渐进的,没有强主张,你可以在原有大系统的上面,把一两个组件改用它实现,当jQuery用;也可以整个用它全家桶开发,当Angular用;还可以用它的视图,搭配你自己设计的整个下层用。你可以在底层数据逻辑的地方用OO和设计模式的那套理念,也可以函数式,都可以,它只是个轻量视图而已,只做了自己该做的事,没有做不该做的事,仅此而已
没有多做职责之外的事。
Vue的核心的功能,是一个视图模板引擎,但这不是说Vue就不能成为一个框架。如下图所示,这里包含了Vue的所有部件,在声明式渲染(视图模板引擎)的基础上,我们可以通过添加组件系统、客户端路由、大规模状态管理来构建一个完整的框架。更重要的是,这些功能相互独立,你可以在核心功能的基础上任意选用其他的部件,不一定要全部整合在一起。可以看到,所说的“渐进式”,其实就是Vue的使用方式,同时也体现了Vue的设计的理念
只关注视图层
声明式渲染
<div id="app"> {{message}} </div>
var app = new Vue({ el: '#app', data: { message: 'Hello Vue!' } })
<div id="app-2"> <span v-bind:title="message"> 鼠标悬停几秒钟查看此处动态绑定的提示信息! </span> </div> var app2 = new Vue({ el: '#app-2', data: { message: '页面加载于 ' + new Date().toLocaleString() } }) v-bind attribute 被称为指令。指令带有前缀 v-,以表示它们是 Vue 提供的特殊 attribute。可能你已经猜到了,它们会在渲染的 DOM 上应用特殊的响应式行为。在这里,该指令的意思是:“将这个元素节点的 title attribute 和 Vue 实例的 message 属性保持一致”。 如果你再次打开浏览器的 JavaScript 控制台,输入 app2.message = '新消息',就会再一次看到这个绑定了 title attribute 的 HTML 已经进行了更新。
条件控制
<div id="app-3"> <p v-if="seen">现在你看到我了</p> </div> var app3 = new Vue({ el: '#app-3', data: { seen: true } }) app3.seen = false可以让p消失
这个例子演示了我们不仅可以把数据绑定到 DOM 文本或 attribute,还可以绑定到 DOM 结构。此外,Vue 也提供一个强大的过渡效果系统,可以在 Vue 插入/更新/移除元素时自动应用过渡效果。
循环
<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: '整个牛项目' } ] } })
在控制台里,输入 app4.todos.push({ text: '新项目' }),你会发现列表最后添加了一个新项目。
处理用户输入
为了让用户和你的应用进行交互,我们可以用 v-on 指令添加一个事件监听器,通过它调用在 Vue 实例中定义的方法:
<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('') } } })
注意在 reverseMessage 方法中,我们更新了应用的状态,但没有触碰 DOM——所有的 DOM 操作都由 Vue 来处理,你编写的代码只需要关注逻辑层面即可。
v-model 指令 将表单号与页面的元素节点双向绑定
<div id="app-6"> <p>{{ message }}</p> <input v-model="message"> </div>
var app6 = new Vue({ el: '#app-6', data: { message: 'Hello Vue!' } })
组件化应用
组件是Vue的一个重要概念,一种抽象,允许我们使用小型、独立和可复用的组件构建大型应用
一个组件本质上是一个拥有预定义选项的vue实例
// 定义名为 todo-item 的新组件 Vue.component('todo-item', { template: '<li>这是个待办项</li>' }) var app = new Vue(...)
<ol> <!-- 创建一个 todo-item 组件的实例 --> <todo-item></todo-item> </ol>
传递数据的组件
Vue.component('todo-item', { // todo-item 组件现在接受一个 // "prop",类似于一个自定义 attribute。 // 这个 prop 名为 todo。 props: ['todo'], template: '<li>{{ todo.text }}</li>' })
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.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: '随便其它什么人吃的东西' } ] } })
数据和方法
当一个 Vue 实例被创建时,它将 data 对象中的所有的属性加入到 Vue 的响应式系统中。当这些属性的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。
// 我们的数据对象 var data = { a: 1 } // 该对象被加入到一个 Vue 实例中 var vm = new Vue({ data: data }) // 获得这个实例上的属性 // 返回源数据中对应的字段 vm.a == data.a // => true // 设置属性也会影响到原始数据 vm.a = 2 data.a // => 2 // ……反之亦然 data.a = 3 vm.a // => 3
当这些数据改变时,视图会进行重渲染。值得注意的是只有当实例被创建时就已经存在于 data 中的属性才是响应式的。也就是说如果你添加一个新的属性,
如果你知道你会在晚些时候需要一个属性,但是一开始它为空或不存在,那么你仅需要设置一些初始值。比如:
data: { newTodoText: '', visitCount: 0, hideCompletedTodos: false, todos: [], error: null }
这里唯一的例外是使用 Object.freeze(),这会阻止修改现有的属性,也意味着响应系统无法再追踪变化。
var obj = { foo: 'bar' } Object.freeze(obj) new Vue({ el: '#app', data: obj }) <div id="app"> <p>{{ foo }}</p> <!-- 这里的 `foo` 不会更新! --> <button v-on:click="foo = 'baz'">Change it</button> </div>
Vue实例属性和方法,前缀用$
var data = { a: 1 } var vm = new Vue({ el: '#example', data: data }) vm.$data === data // => true vm.$el === document.getElementById('example') // => true
也就是说vue的获取对象方法应该是 vm.$属性名;
// $watch 是一个实例方法 vm.$watch('a', function (newValue, oldValue) { // 这个回调将在 `vm.a` 改变后调用 })
实例生命周期钩子
created
new Vue({ data: { a: 1 }, created: function () { // `this` 指向 vm 实例 console.log('a is: ' + this.a) } }) // => "a is: 1"
mounted 当被加载完毕之后调用
updated 数据发生更新后调用
destroyed 销毁后调用,可以用于销毁某些东西.不如销毁token
注意:
不要在选项属性或回调上使用箭头函数,比如 created: () => console.log(this.a) 或 vm.$watch('a', newValue => this.myMethod())。因为箭头函数并没有 this,this会作为变量一直向上级词法作用域查找,直至找到为止,经常导致 Uncaught TypeError: Cannot read property of undefined 或 Uncaught TypeError: this.myMethod is not a function 之类的错误。
模板语法
Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据
在底层的实现上,Vue 将模板编译成虚拟 DOM 渲染函数。结合响应系统,Vue 能够智能地计算出最少需要重新渲染多少组件,并把 DOM 操作次数减到最少
如果你熟悉虚拟 DOM 并且偏爱 JavaScript 的原始力量,你也可以不用模板,直接写渲染 (render) 函数,使用可选的 JSX 语法。
文本
数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值
<span>MEssage:{{msg}}</span>
Mustache 标签将会被替代为对应数据对象上 msg 属性的值。无论何时,绑定的数据对象上 msg 属性发生了改变,插值处的内容都会更新。
通过使用 v-once 指令,你也能执行一次性地插值,当数据改变时,插值处的内容不会更新。但请留心这会影响到该节点上的其它数据绑定: <span v-once>这个将不会改变: {{ msg }}</span>
原始 HTML
双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用 v-html 指令: <p>Using mustaches: {{ rawHtml }}</p> 这个就相当于innerHTML <p>Using v-html directive: <span v-html="rawHtml"></span></p> 这个相当于write()
Attribute
Mustache 语法不能作用在 HTML attribute 上,遇到这种情况应该使用 v-bind 指令 <div v-bind:id="dynamicId"></div>
对于布尔 attribute (它们只要存在就意味着值为 true),v-bind 工作起来略有不同,在这个例子中: <button v-bind:disabled="isButtonDisabled">Button</button> 如果 isButtonDisabled 的值是 null、undefined 或 false,则 disabled attribute 甚至不会被包含在渲染出来的 <button> 元素中。
使用 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 } }}
注意
模板表达式都被放在沙盒中,只能访问全局变量的一个白名单,如 Math 和 Date 。你不应该在模板表达式中试图访问用户定义的全局变量。
指令
指令 (Directives) 是带有 v- 前缀的特殊 attribute。指令 attribute 的值预期是单个 JavaScript 表达式 (v-for 是例外情况,稍后我们再讨论)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。
<p v-if="seen">现在你看到我了</p> 这里,v-if 指令将根据表达式 seen 的值的真假来插入/移除 <p> 元素。
参数
一些指令能够接收一个“参数”,在指令名称之后以冒号表示。例如,v-bind 指令可以用于响应式地更新 HTML attribute: <a v-bind:href="url">...</a> 在这里 href 是参数,告知 v-bind 指令将该元素的 href attribute 与表达式 url 的值绑定。
另一个例子是 v-on 指令,它用于监听 DOM 事件: <a v-on:click="doSomething">...</a> 在这里参数是监听的事件名,这里可以防止方法名或者是vue语句 比如变量名重新赋值 foo='xxx'
动态参数 2.6.0新增
可以用方括号括起来的 JavaScript 表达式作为一个指令的参数: <!-- 注意,参数表达式的写法存在一些约束,如之后的“对动态参数表达式的约束”章节所述。 --> <a v-bind:[attributeName]="url"> ... </a> 这里的 attributeName 会被作为一个 JavaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用。例如,如果你的 Vue 实例有一个 data 属性 attributeName,其值为 "href",那么这个绑定将等价于 v-bind:href
动态参数是事件名 <a v-on:[eventName]="doSomething"> ... </a> 在这个示例中,当 eventName 的值为 "focus" 时,v-on:[eventName] 将等价于 v-on:focus。
参数的约束:预期会请求到一个字符串,异常情况下为null,会被显性的用于移除绑定,非字符串会触发警告
参数表达式的约束: <a v-bind:['foo' + bar]="value"> ... </a> 某些字符串会触发警告如:空格和引号 变通的办法是使用没有空格或引号的表达式,或用计算属性替代这种复杂表达式
在 DOM 中使用模板时 (直接在一个 HTML 文件里撰写模板),还需要避免使用大写字符来命名键名,因为浏览器会把 attribute 名全部强制转为小写 <!-- 在 DOM 中使用模板时这段代码会被转换为 `v-bind:[someattr]`。 除非在实例中有一个名为“someattr”的 property,否则代码不会工作。 --> <a v-bind:[someAttr]="value"> ... </a>
修饰符
饰符 (modifier) 是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定 如,.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault() <form v-on:submit.prevent="onSubmit">...</form>
缩写
v- 前缀作为一种视觉提示,用来识别模板中 Vue 特定的 attribute。当你在使用 Vue.js 为现有标签添加动态行为 (dynamic behavior) 时,v- 前缀很有帮助,然而,对于一些频繁用到的指令来说,就会感到使用繁琐。同时,在构建由 Vue 管理所有模板的单页面应用程序 (SPA - single page application) 时,v- 前缀也变得没那么重要了。因此,Vue 为 v-bind 和 v-on 这两个最常用的指令,提供了特定简写: <!-- 完整语法 --> <a v-bind:href="url">...</a> <!-- 缩写 --> <a :href="url">...</a> 绑定数据到href上面
<!-- 完整语法 --> <a v-on:click="doSomething">...</a> <!-- 缩写 --> <a @click="doSomething">...</a>
计算属性和侦听器
计算属性 模板中的表达式设计是为了简单运算,不应该放入太多逻辑导致难以维护,如: <div id="example"> {{ message.split('').reverse().join('') }} </div> 在这个地方,模板不再是简单的声明式逻辑。你必须看一段时间才能意识到,这里是想要显示变量 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: { // 计算属性的 getter reversedMessage: function () { // `this` 指向 vm 实例 return this.message.split('').reverse().join('') } } }) 计算属性就是一个函数
这里我们声明了一个计算属性 reversedMessage。我们提供的函数将用作属性 vm.reversedMessage 的 getter 函数: 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 函数是没有副作用 (side effect) 的,这使它更易于测试和理解。
计算属性可以用方法代替: <p>Reversed message: "{{ reversedMessage() }}"</p> // 在组件中 methods: { reversedMessage: function () { return this.message.split('').reverse().join('') } }
计算属性和方法的不同之处
我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。 比如: computed: { now: function () { return Date.now() } } 指定后,将不会在执行,再次调用时,会显示之前的值,可以认为是缓存值。
每当触发重新渲染时,调用方法将总会再次执行函数
我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A 。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。
侦听属性
Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性
当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch——特别是如果你之前使用过 AngularJS。然而,通常更好的做法是使用计算属性而不是命令式的 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 } } }) 上面的代码是命令式且重复的 使用计算属性: var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar' }, computed: { fullName: function () { return this.firstName + ' ' + this.lastName } } })
计算属性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] } } } // ...
侦听器
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器 这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化 <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.debouncedGetAnswer() } }, created: function () { // `_.debounce` 是一个通过 Lodash 限制操作频率的函数。 // 在这个例子中,我们希望限制访问 yesno.wtf/api 的频率 // AJAX 请求直到用户输入完毕才会发出。想要了解更多关于 // `_.debounce` 函数 (及其近亲 `_.throttle`) 的知识, // 请参考:https://lodash.com/docs#debounce this.debouncedGetAnswer = _.debounce(this.getAnswer, 500) }, methods: { getAnswer: 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 }) } } }) </script> 在这个示例中,使用 watch 选项允许我们执行异步操作 (访问一个 API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。 除了 watch 选项之外,您还可以使用命令式的 vm.$watch API。
class和style绑定 因为都是属性,可以使用v-bind
绑定HTML Class
对象语法
v-bind:class动态切换class <div v-bind:class="{ active: isActive }"></div> 上面的语法表示 active 这个 class 存在与否将取决于数据属性 isActive 的 truthiness 你可以在对象中传入更多属性来动态切换多个 class。此外,v-bind:class 指令也可以与普通的 class 属性共存。当有如下模板: <div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }" ></div> data: { isActive: true, hasError: false } 结果渲染为: <div class="static active"></div> 当 isActive 或者 hasError 变化时,class 列表将相应地更新。例如,如果 hasError 的值为 true,class 列表将变为 "static active text-danger"。 绑定的数据对象不必内联定义在模板里: <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' } } }
数组语法
我们可以把一个数组传给 v-bind:class,以应用一个 class 列表 <div v-bind:class="[activeClass, errorClass]"></div> data: { activeClass: 'active', errorClass: 'text-danger' } 渲染结构: <div class="active text-danger"></div>
如果你也想根据条件切换列表中的 class,可以用三元表达式: <div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
不过当有多个条件class时,这样写繁琐,就可以使用对象语法 <div v-bind:class="[{ active: isActive }, errorClass]"></div>
用在组件上
当在一个自定义组件上使用 class 属性时,这些 class 将被添加到该组件的根元素上面
例如,如果你声明了这个组件: Vue.component('my-component', { template: '<p class="foo bar">Hi</p>' })
<my-component class="baz boo"></my-component> HTML 将被渲染为: <p class="foo bar baz boo">Hi</p>
对于带数据绑定 class 也同样适用: <my-component v-bind:class="{ active: isActive }"></my-component> 当isActive为truthy渲染结果: <p class="foo bar active">Hi</p>
绑定内联样式
对象语法
v-bind:style 的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象
<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>
当 v-bind:style 使用需要添加浏览器引擎前缀的 CSS 属性时,如 transform,Vue.js 会自动侦测并添加相应的前缀。 比如ie和chrome添加的前缀,火狐浏览器也需要添加前缀 这样的话就不用关心不同浏览器添加多个前缀了
多重值
从 2.3.0 起你可以为 style 绑定中的属性提供一个包含多个值的数组,常用于提供多个带前缀的值,例如: <div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div> 这样写只会渲染数组中最后一个被浏览器支持的值。在本例中,如果浏览器支持不带浏览器前缀的 flexbox,那么就只会渲染 display: flex。
条件渲染
v-if 相当于if 指令表达式为truthy时才会被渲染: <h1 v-if="awesome">Vue is awesome!</h1> 也可以用 v-else 添加一个“else 块”: <h1 v-if="awesome">Vue is awesome!</h1> <h1 v-else>Oh no </h1>
在 <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> 控制整块代码的显示与否?
<div v-if="Math.random() > 0.5"> Now you see me </div> <div v-else> Now you don't </div> 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,v-else-if 也必须紧跟在带 v-if 或者 v-else-if 的元素之后。
用 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>
v-show
用于根据条件暂时接口,用法和v-if差不多 <h1 v-show="ok">Hello!</h1> 不同的是带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS 属性 display 也就是说这个元素是预先被写到文档中,知识控制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-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' } ] } })
在 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' } ] } })
你也可以用 of 替代 in 作为分隔符,因为它更接近 JavaScript 迭代器的语法: <div v-for="item of items"></div>
在v-for中使用对象 遍历对象属性: <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>
在遍历对象时,会按 Object.keys() 的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎下都一致。
维护状态
当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染
这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性: <div v-for="item in items" v-bind:key="item.id"> <!-- 内容 --> </div> 建议尽可能在使用 v-for 时提供 key attribute,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。 因为它是 Vue 识别节点的一个通用机制,key 并不仅与 v-for 特别关联。后面我们将在指南中看到,它还具有其它用途
不要使用对象或数组之类的非基本类型值作为 v-for 的 key。请用字符串或数值类型的值。
数据更新检测
变异方法
push() pop() shift() unshift() splice() sort() reverse()
你可以打开控制台,然后对前面例子的 items 数组尝试调用变异方法。比如 example1.items.push({ message: 'Baz' })。
替换数组
非变异 (non-mutating method) 方法,例如 filter()、concat() 和 slice() 。它们不会改变原始数组,而总是返回一个新数组。当使用非变异方法时,可以用新数组替换旧数组: example1.items = example1.items.filter(function (item) { return item.message.match(/Foo/) }) 并不会冲寻渲染整个dom列表,当数组的内容一致的时候,只是替换显示内容, 这个可以用来做关键字替换
注意:
由于 JavaScript 的限制,Vue 不能检测以下数组的变动: 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue 当你修改数组的长度时,例如:vm.items.length = newLength 举个例子: var vm = new Vue({ data: { items: ['a', 'b', 'c'] } }) vm.items[1] = 'x' // 不是响应性的 vm.items.length = 2 // 不是响应性的
为了解决第一类问题,以下两种方式都可以实现和 vm.items[indexOfItem] = newValue 相同的效果,同时也将在响应式系统内触发状态更新: // Vue.set Vue.set(vm.items, indexOfItem, newValue) // Array.prototype.splice vm.items.splice(indexOfItem, 1, newValue) vm.items.splice(newLength)
你也可以使用 vm.$set 实例方法,该方法是全局方法 Vue.set 的一个别名: vm.$set(vm.items, indexOfItem, newValue)
vue不能检测对象的添加和删除: var vm = new Vue({ data: { a: 1 } }) // `vm.a` 现在是响应式的 vm.b = 2 // `vm.b` 不是响应式的 可以使用 Vue.set(object, propertyName, value) 方法向嵌套对象添加响应式属性 var vm = new Vue({ data: { userProfile: { name: 'Anika' } } }) 添加新属性:Vue.set(vm.userProfile, 'age', 27) 或者 vm.$set(vm.userProfile, 'age', 27)
添加多个属性: vm.userProfile = Object.assign({}, vm.userProfile, { age: 27, favoriteColor: 'Vue Green' })
过滤、排序
<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 }) } } 通过计算属性来进行过滤操作
<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中使用数值
v-for 也可以接受整数。在这种情况下,它会把模板重复对应次数
<div> <span v-for="n in 10">{{ n }} </span> </div>
在template上使用v-for
类似于 v-if,你也可以利用带有 v-for 的 <template> 来循环渲染一段包含多个元素的内
<ul> <template v-for="item in items"> <li>{{ item.msg }}</li> <li class="divider" role="presentation"></li> </template> </ul>
v-for 与 v-if 一同使用
注意我们不推荐在同一元素上使用 v-if 和 v-for。更多细节可查阅风格指南。
当它们处于同一节点,v-for 的优先级比 v-if 更高,这意味着 v-if 将分别重复运行于每个 v-for 循环中。当你只想为部分项渲染节点时,这种优先级的机制会十分有用,如下: <li v-for="todo in todos" v-if="!todo.isComplete"> {{ todo }} </li> 上面的代码将只渲染未完成的 todo。
而如果你的目的是有条件地跳过循环的执行,那么可以将 v-if 置于外层元素 (或 <template>)上。如: <ul v-if="todos.length"> <li v-for="todo in todos"> {{ todo }} </li> </ul> <p v-else>No todos left!</p>
在组件上使用
<my-component v-for="item in items" :key="item.id"></my-component> 当在组件上使用 v-for 时,key 现在是必须的。
然而,任何数据都不会被自动传递到组件里,因为组件有自己独立的作用域。为了把迭代数据传递到组件里,我们要使用 prop: <my-component v-for="(item, index) in items" v-bind:item="item" v-bind:index="index" v-bind:key="item.id" ></my-component>
不自动将 item 注入到组件里的原因是,这会使得组件与 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>
注意这里的 is="todo-item" 属性。这种做法在使用 DOM 模板时是十分必要的,因为在 <ul> 元素内只有 <li> 元素会被看作有效内容。这样做实现的效果与 <todo-item> 相同,但是可以避开一些潜在的浏览器解析错误。查看 DOM 模板解析说明 来了解更多信息。 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 = '' } } })
事件处理
使用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) } } } }) // 也可以用 JavaScript 直接调用方法 example2.greet() // => 'Hello Vue.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 事件。可以用特殊变量 $event 把它传入方法 <button v-on:click="warn('Form cannot be submitted yet.', $event)"> Submit </button> // ... methods: { warn: function (message, event) { // 现在我们可以访问原生事件对象 if (event) { event.preventDefault() } alert(message) } }
事件修饰符
在事件处理程序中调用 event.preventDefault() 或 event.stopPropagation() 是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。
Vue.js 为 v-on 提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。 .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>
使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。
<!-- 点击事件将只会触发一次 --> <a v-on:click.once="doThis"></a> 可以同时针对DOM事件和自定义组件事件
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 --> <!-- 而不会等待 `onScroll` 完成 --> <!-- 这其中包含 `event.preventDefault()` 的情况 --> <div v-on:scroll.passive="onScroll">...</div>
这个 .passive 修饰符尤其能够提升移动端的性能。
不要把 .passive 和 .prevent 一起使用,因为 .prevent 将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive 会告诉浏览器你不想阻止事件的默认行为。
按键修饰符
在监听键盘事件时,我们经常需要检查详细的按键。Vue 允许为 v-on 在监听键盘事件时添加按键修饰符: <!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` --> <input v-on:keyup.enter="submit">
你可以直接将 KeyboardEvent.key 暴露的任意有效按键名转换为 kebab-case 来作为修饰符 <input v-on:keyup.page-down="onPageDown"> 处理函数只会在 $event.key 等于 PageDown 时被调用
为了在必要的情况下支持旧浏览器,Vue 提供了绝大多数常用的按键码的别名: enter .tab .delete (捕获“删除”和“退格”键) .esc .space .up .down .left .right
你还可以通过全局 config.keyCodes 对象自定义按键修饰符别名 // 可以使用 `v-on:keyup.f1` Vue.config.keyCodes.f1 = 112
系统修饰符
可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器 .ctrl .alt .shift .meta mac上meta对应的是command,window上对应的是win键(田) 在sun操作系统键盘上对应的是菱形宝石键
<!-- Alt + C --> <input @keyup.alt.67="clear"> <!-- Ctrl + Click --> <div @click.ctrl="doSomething">Do something</div>
请注意修饰键与常规按键不同,在和 keyup 事件一起用时,事件触发时修饰键必须处于按下状态。换句话说,只有在按住 ctrl 的情况下释放其它按键,才能触发 keyup.ctrl。而单单释放 ctrl 也不会触发事件。如果你想要这样的行为,请为 ctrl 换用 keyCode:keyup.17。
exact修饰符
.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-on 有几个好处
扫一眼 HTML 模板便能轻松定位在 JavaScript 代码里对应的方法。
因为你无须在 JavaScript 里手动绑定事件,你的 ViewModel 代码可以是非常纯粹的逻辑,和 DOM 完全解耦,更易于测试。
当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除。你无须担心如何清理它们。
表单输入绑定
你可以用 v-model 指令在表单 <input>、<textarea> 及 <select> 元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,但 v-model 本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。 什么叫语法糖?
v-model 会忽略所有表单元素的 value、checked、selected attribute 的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data 选项中声明初始值。 也就是说我需要初始化?
v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件: text 和 textarea 元素使用 value 属性和 input 事件; checkbox 和 radio 使用 checked 属性和 change 事件; select 字段将 value 作为 prop 并将 change 作为事件。
文本: <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>
在文本区域插值 (<textarea>{{text}}</textarea>) 并不会生效,应用 v-model 来代替。
复选框 默认绑定布尔值 <input type="checkbox" id="checkbox" v-model="checked"> <label for="checkbox">{{ checked }}</label>
多个复选框,绑定value到数组 <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: [] } })
单选按钮 <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: '' } })
单选框 <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: '' } }) 如果 v-model 表达式的初始值未能匹配任何选项,<select> 元素将被渲染为“未选中”状态
多选时 (绑定到一个数组): <div id="example-6"> <select v-model="selected" multiple style="width: 50px;"> <option>A</option> <option>B</option> <option>C</option> </select> <br> <span>Selected: {{ selected }}</span> </div> new Vue({ el: '#example-6', data: { selected: [] } })
v-for动态渲染选项框 <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-model 绑定的值通常是静态字符串 (对于复选框也可以是布尔值): <!-- 当选中时,`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>
但是有时我们可能想把值绑定到 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 在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步 (除了上述输入法组合文字时)。你可以添加 lazy 修饰符,从而转变为使用 change 事件进行同步: <!-- 在“change”时而非“input”时更新 --> <input v-model.lazy="msg" >
.number 如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符: <input v-model.number="age" type="number"> 这通常很有用,因为即使在 type="number" 时,HTML 输入元素的值也总会返回字符串。如果这个值无法被 parseFloat() 解析,则会返回原始的值。
.trim 如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符: <input v-model.trim="msg">
组件基础
// 定义一个名为 button-counter 的新组件 Vue.component('button-counter', { data: function () { return { count: 0 } }, template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>' })
组件是可复用的 Vue 实例,且带有一个名字:在这个例子中是 <button-counter>。我们可以在一个通过 new Vue 创建的 Vue 根实例中,把这个组件作为自定义元素来使用: <div id="components-demo"> <button-counter></button-counter> </div> new Vue({ el: '#components-demo' })
data必须是一个函数
为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别 组件都只是通过 Vue.component 全局注册的: Vue.component('my-component-name', { // ... options ... })
局部注册
定义组件: var ComponentA = { /* ... */ } var ComponentB = { /* ... */ } var ComponentC = { /* ... */ }
然后在 components 选项中定义你想要使用的组件: new Vue({ el: '#app', components: { 'component-a': ComponentA, 'component-b': ComponentB } })
在src/main.js中注册全局组件
prop向子组件传递数据
Prop 是你可以在组件上注册的一些自定义 attribute
Vue.component('blog-post', { props: ['title'], template: '<h3>{{ title }}</h3>' }) <blog-post title="My journey with Vue"></blog-post>
然而在一个典型的应用中,你可能在 data 里有一个博文的数组: new Vue({ el: '#blog-post-demo', data: { posts: [ { id: 1, title: 'My journey with Vue' }, { id: 2, title: 'Blogging with Vue' }, { id: 3, title: 'Why Vue is so fun' } ] } })
<blog-post v-for="post in posts" v-bind:key="post.id" v-bind:title="post.title" ></blog-post>
如上所示,你会发现我们可以使用 v-bind 来动态传递 prop。这在你一开始不清楚要渲染的具体内容,比如从一个 API 获取博文列表的时候,是非常有用的。 到目前为止,关于 prop 你需要了解的大概就这些了,如果你阅读完本页内容并掌握了它的内容,我们会推荐你再回来把 prop 读完。
积累
bind和v-model的区别
:model v-bind: model的简写,用于绑定model属性 v-model 用于表单中的input,它是一个语法糖,用于数据双向绑定;
子主题
例如: <input v-model="message"> 等价于 <input v-bind:value="message" v-on:input="message = $event.target.value" /> 上面这个例子中,v-bind:value="message" 只是将message变量的值赋给了input的value,并没有双向绑定,再此声明一下以防混淆