后端开发者的Vue学习之路(一)
首发日期:2019-01-19
前言:
有时候,一个后端开发者“不得不”自己去搭建前端界面。如果有的选,当然选一个比较好学的前端“框架”咯(框架很多时候封装了普通的 html 元素,使得能更加方便地使用)。
如果你做的项目的界面是一个偏后台管理的而且要求并不高的界面的时候,你可以考虑 easy UI 这个较为知名的前端框架。
如果你想要界面变得好看一些(easy UI 的界面太简单了,缺乏较强的定制性),那么你可以考虑 vue 这个框架。vue 这个框架本身并没有多少好看的样式,但学习了这个框架就可以学会怎么使用它的第三方组件库了(这些第三库看起来都还行)。
iview 组件库示例
element 组件库示例
【Vue 自己有官方教程,为什么写这个呢?主要是感觉自己的前端知识不太稳固,看教程的时候有点迷糊。推己及人,所以有了这个博文】
Vue 的介绍
- 官网:https://cn.vuejs.org/
- [官方的话]:Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
- 小菜鸟前端:Vue 是一个简单容易上手前端框架,在网上有很多基于 vue 的 web UI 框架(element 等),由于有大量类似于 easyUI 这些快速搭建页面的框架,所以我们可以使用 vue 来快速搭建页面。如果我们作为一个后端开发者想掌握一个前端框架,vue 是一个好选择,因为它足够的易学。
例如下面的代码可以快速构建一个表格:
<template> <el-table :data="tableData" style="width: 100%"> <el-table-column prop="date" label="日期" width="180"></el-table-column> <el-table-column prop="name" label="姓名" width="180"></el-table-column> <el-table-column prop="address" label="地址"> </el-table-column> </el-table> </template>
<script>
export default {
data() {
return {
tableData: [{
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
}, {
date: '2016-05-04',
name: '王小虎',
address: '上海市普陀区金沙江路 1517 弄'
}, {
date: '2016-05-01',
name: '王小虎',
address: '上海市普陀区金沙江路 1519 弄'
}, {
date: '2016-05-03',
name: '王小虎',
address: '上海市普陀区金沙江路 1516 弄'
}]
}
}
}
</script>
## 兼容性: Vue 不支持 IE8 及以下版本,因为 Vue 使用了 IE8 无法模拟的 ECMAScript 5 特性。但它支持所有 [兼容 ECMAScript 5 的浏览器](https://caniuse.com/#feat=es5)。【** 所以如果项目兼容性要求较高,那么不适合使用 Vue**】
学习 Vue 需要的前置知识:
- html+ js 【基础的内容,前端的基础,不了解不行。但只要你能看得懂基本的 html 和 js 代码即可,不了解的可以查。】
- es6 【 ECMAScript 6(ES6) 是 JavaScript 语言的下一代标准。但不需要了解太多,Vue 里面有些语法是 ES6 的,但我们可以看着例子了解有哪些常见的语法即可】
- webpack 【不需要了解太多,是一个项目构建工具,会涉及一些 Vue 项目的运行知识。】
- npm 【vue 入门不需要了解太多,可能只需要了解 npm 的几个命令】
MVVM 模型
- MVVM 是 Model-View-ViewModel 的简写。
- MVVM(Model-View-ViewModel)的基础是 MVP 模式,【MVP 类似于 MVC,后端开发者应该对 MVC 比较熟悉了】
【常见的 MVP 前端开发有几个步骤:1. 创建页面元素,2. 在 script 中给元素绑定事件,3. 在 script 中处理事件(这个事件可能会影响页面的元素的显示或返回数据)】
【上面的几个步骤可以说是内容(页面元素)行为(js 操作)分离了】
【为什么要有 MVVM 呢?主要是 MVP 模式有太多的 dom 操作了(大量的 DOM 操作使页面渲染性能降低,加载速度变慢)。MVVM 解决了这个问题,并且提供了一些其他的好处。】
【如果你了解 ORM 模型,你应该很能体会到 MVVM 模型的好处,有了 VM 层帮我们管理了数据,我们就只需要处理好 Model 层了,这就好像 ORM 中定义了数据映射关系,然后我们只需要操作实体类。】
补充:
- Vue 的核心特点是简单易用,所以它相对适合非专业前端的学习。它需要一些前置的前端知识,而且它自己的知识也是需要时间来学习的,如果与 easy UI 相比,easy UI 的学习速度会更快,但 vue 更能构建比较优美的页面。
安装 / 导入
【要想使用 Vue,必须先导入 / 安装,就像使用 jquery 必须导入 jquery.js 一样;而 vue 可以使用 webpack 来构建,所以也可以使用 webpack 安装 Vue,但如果你是初学者,先掌握静态导入的方法吧】
导入 Vue
Vue 的本质也是 javascript, 所以它也可以通过导入 js 文件来使用功能(js 可以用 cdn 的,也可以手动导入本地的 vue.js)。
这种也是最简单的使用 vue 的方式,所以我们可以这种方式来作为入门学习,但正式使用时都会使用 webpack 来构建 vue 项目。
在 html 文件中使用如下代码:【这里先讲导入,后面讲使用】
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.js"></script>
安装
- 安装 vue:
npm install vue
- 全局安装 vue-cli:
npm install --global vue-cli
- 创建一个基于 webpack 模板的新项目:
vue init webpack 项目名
两种方式的区别:
- 对于普通的静态 js 导入,在面对多个组件共同开发的时候,会比较不方便。【如果你是后端开发者,你应该知道把多个类写在一个文件中是多么地不方便,这也是一样的】
- js:
- 简单,不需要安装,直接导入 js 即可。
- npm
- 在用 Vue 构建大型应用时推荐使用 NPM 安装
- NPM 能很好地和诸如 webpack 或 Browserify 模块打包器配合使用。
- 同时 Vue 也提供配套工具来开发单文件组件。
在入门部分将使用 js 导入方式的例子,在涉及构建多个组件的时候(页面需要多个组件时,如果把多个组件定义在一个文件中会显得赘余。这好比把多个类放到同一个文件中定义。)将使用 npm 安装方式演示。
HelloWorld 示例
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<span class="hljs-tag"><<span class="hljs-name">body</span>></span>
<span class="hljs-comment"><!--2.这是被vue实例管理的区域--></span>
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>></span>
{{ message }}
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
<span class="hljs-tag"></<span class="hljs-name">body</span>></span>
<span class="hljs-comment"><!--1.导入js--></span>
<span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"</span>></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
<span class="hljs-tag"><<span class="hljs-name">script</span>></span><span class="language-javascript">
<span class="hljs-comment">//3. 创建实例:new Vue()里面有一个{}类型的参数,属性el是一个元素的id名,代表被vue实例管理的区域;</span>
<span class="hljs-keyword">var</span> app = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Vue</span>({
<span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
<span class="hljs-attr">data</span>: {
<span class="hljs-attr">message</span>: <span class="hljs-string">'Hello Vue!'</span>
}
})
</span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
</html>
代码分析:
- 导入了 js:
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
- 定义一个元素,给它 id 起名 app。[名字是可以随意的,但在下面的 new Vue 创建实例时,el 参数必须是这个 id 名]
- 新建一个 vue 实例,属性 el 代表把对应元素区域交给 vue 管理(
el: '#app'
代表把 id 为 app 的区域交给 vue 管理)。那么对应元素区域里面就可以使用 vue 的语法了。 - data 属性:data 里面可以定义一系列变量,可用于这个实例掌控的区域。
- {{message}} : vue 语法的内容,代表获取变量名为 message 的数据,并显示出来。
代码效果:
导入了 js 之后,vue 把管理的区域里面的 vue 语法都解析了,所以 {{message}} 就取出了 vue 实例中名叫 message 的数据。
现在我们了解了一些 vue 的渲染页面的知识,下面我们来了解更多的渲染技巧。
实例中可以定义的内容
每个 Vue 应用都是通过使用 Vue 函数创建一个新的 Vue 实例开始的。
实例中的定义的内容就是我们可以使用在 Vue 应用中的内容。
下面讲实例中可以定义哪些内容。
- 数据
- 方法
- 生命周期钩子函数
- 其他(有些内容比较重要,留到后面讲)
定义数据
- 定义了数据,那么就可以在 Vue 管理的区域中使用 Vue 的获取数据的语法来获取数据。
- 这种操作就像是我们把数据装到实例中,实例再把数据传给视图组件。
- 任何你想显示到页面的数据你都可以考虑定义在实例中。
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
//<div id="app">
// {{message}}
//</div>
定义方法 methods
- 在 js 中,方法是很重要的,Vue 实例中可以定义方法。【为什么要有方法这个就不解释了吧?】
<body>
<div id="app">
<!-- 触发事件,建议使用 Vue 的语法来绑定事件(@事件类型,代表绑定什么事件) -->
<!-- 实例内定义的函数,使用 Vue 的语法来绑定;实例外定义的,可以使用原生的方式绑定事件 -->
<button @click="myFunction">按钮</button>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
},
methods: {
myFunction: function () {
alert('haha')}}
})
</script>
生命周期与生命周期钩子函数
【要结合图来看(懂英文是多么地好)】
- beforeCreate:在创建了 vue 示例时,在进行了一部分基础的初始化之后就会自动执行 beforeCreate 函数
- created:创建完示例后,会自动执行 created 函数
- beforeMounted:渲染完模板后(可以说是 vue 知道区域内是一个怎么的 html 环境,但还没把数据显示到这个环境中的时候),会自动执行这个函数
- mouted:模板与数据结合之后自动执行这个函数。【此时页面已经渲染完毕】
- beforeUpdated:数据发生变化后,新数据被渲染前会自动执行这个函数。
- updated:新数据被渲染成功会自动执行这个函数。
- beforeDestory:当实例被 destory 时会自动执行这个函数。
- destory:当实例被完全销毁是自动执行这个函数。
【如果你对生命周期钩子感兴趣,可以自查,这里不多讲,后面之后根据开发需求来讲一些】
测试代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<span class="hljs-tag"><<span class="hljs-name">body</span>></span>
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>></span>
<span class="hljs-tag"><<span class="hljs-name">button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">'updateValue'</span>></span>点击触发beforeUpdate,updated<span class="hljs-tag"></<span class="hljs-name">button</span>></span>
<span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">'deleteApp()'</span>></span>点击触发beforeDestory,destoryed<span class="hljs-tag"></<span class="hljs-name">button</span>></span>
<span class="hljs-tag"><<span class="hljs-name">p</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">'a'</span>></span>{{ msg }}<span class="hljs-tag"></<span class="hljs-name">p</span>></span>
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
<span class="hljs-tag"></<span class="hljs-name">body</span>></span>
<span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"</span>></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
<span class="hljs-tag"><<span class="hljs-name">script</span>></span><span class="language-javascript">
<span class="hljs-keyword">var</span> app = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Vue</span>({
<span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
<span class="hljs-attr">data</span>: {
<span class="hljs-attr">msg</span>: <span class="hljs-string">'hello'</span>
},
<span class="hljs-attr">beforeCreate</span>: <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>){
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'beforeCreate'</span>)
},
<span class="hljs-attr">created</span>: <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>){
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'created'</span>)
},
<span class="hljs-attr">beforeMount</span>: <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>){
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-variable language_">this</span>.<span class="hljs-property">$refs</span>.<span class="hljs-property">a</span>)
<span class="hljs-comment">// 上面的你会看到undefined,因为数据还没绑定上</span>
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'beforeMount'</span>)
},
<span class="hljs-attr">mounted</span>: <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>){
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-variable language_">this</span>.<span class="hljs-property">$refs</span>.<span class="hljs-property">a</span>)
<span class="hljs-comment">// 上面的你会看到p,因为数据绑定上了</span>
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'mounted'</span>)
},
<span class="hljs-attr">beforeUpdate</span>: <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>){
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'beforeUpdate'</span>)
},
<span class="hljs-attr">updated</span>: <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>){
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'updated'</span>)
},
<span class="hljs-attr">beforeDestroy</span>: <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>){
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'beforeDestory'</span>)
},
<span class="hljs-attr">destroyed</span>: <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>){
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'destoryed'</span>)
},
<span class="hljs-attr">methods</span>: {
<span class="hljs-attr">updateValue</span>: <span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {
<span class="hljs-variable language_">this</span>.<span class="hljs-property">msg</span> = <span class="hljs-string">'haha'</span>
}
}
})
<span class="hljs-keyword">function</span> <span class="hljs-title function_">deleteApp</span>(<span class="hljs-params"></span>) {
app.$destroy()
}
</span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
</html>
补充:
- 实例中其实还可以定义别的内容(如计算属性,监听器等)。但主要内容是上面这些,暂时了解这些就够了。
- 创建了实例,就可以在控制台中使用实例名来获取数据了,比如上面定义了实例 app, 那么 app.message 就可以获取定义在 data 中名为 message 的数据,app.myFunction() 就可以调用定义在 methods 中名为 myFunction 的函数。【所以后面的例子中的一些数据更改都是使用控制台来更改实例中的数据】
渲染
插入文本
在上面已经演示了使用 Mustache 表达式 (可以俗称插值表达式){{ }}
来获取实例中的数据了,其实还可以使用其他方式来输出数据
v-text
v-text 可以向元素中输出文本内容
<div id="app">
<span v-text="message"></span>
<!-- message: 'Hello Vue!' -->
<!--
var app = new Vue({
el: '#app',
data: {message: 'Hello Vue!'}
})
-->
</div>
v-html
v-html 可以向元素中输出 html 内容:
<div id="app">
<span v-html="message"></span>
<!-- message: 'Hello Vue!'(带标题样式的) -->
</div>
<!--
var app = new Vue({
el: '#app',
data: {message: '<h1>Hello Vue!</h1>'}
})
-->
**Mustache 语法不能作用在 HTML 特性上,也就是说在 html 元素中没有 id="{{id}}" 这样的操作 ,这种时候需要使用 vue 语法:v-bind:id="id" **
{{}} 里面可以使用 javascript 表达式,例如:{{message.split('').reverse().join('') }}
用 v-bind 绑定属性:
v-bind 用于给元素的属性绑定值
- 绑定标题:v-bind:title
<div id="app">
<span v-bind:title="message">悬浮几秒,查看标题</span>
</div>
<!--
var app = new Vue({
el: '#app',
data: {message: 'Hello Vue!'}
})
-->
- 给 a 元素绑定 href : v-bind:href='xxx'
- 给元素绑定 id : v-bind:id='xxx'
【理论上,可以使用 v-bind 给元素的任意属性绑定值】
v-bind 的简写
<!-- 完整语法 -->
<a v-bind:href="url">...</a>
<!-- 缩写 -->
<a :href="url">...</a>
把对象的所有属性绑定到元素:
v-bind 的参数可以是一个对象,是一个对象时,会把对象的所有属性都绑定到元素上。具体如下图。
条件渲染
- v-if 可以判断是否渲染一个元素,如果不渲染,这个元素将不会产生(不是隐藏!可以理解为这个元素被 delete 了。)
<div id="app">
<p v-if="seen">现在你看到我了</p>
<!-- seen: true 时渲染,seen: false 时不渲染 -->
</div>
<!--
var app = new Vue({
el: '#app',
data: {seen: false}
})
-->
- 除了 v-if, 还有 v-else 和 v-else-if,v-else 不可以再带条件判断,v-else-if 可以带条件判断。
<div id="app">
<h1 v-if="gender ===' 男 '">男</h1>
<h1 v-else-if="gender ===' 女 '">女</h1>
<h1 v-else>unknown</h1>
</div>
<!--
var app = new Vue({
el: '#app',
data: {gender: '女'},
})
-->
使用 v-else 的元素必须紧跟在带 v-if 或者 v-else-if 的元素的后面,否则它将不会被识别。
- v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS 属性 display。
<div id="app">
<h1 v-show="ok">Hello!</h1>
</div>
<!--
var app = new Vue({
el: '#app',
data: {ok: true},
})
-->
循环
- v-for 可以循环渲染出若干个元素。
- v-for 指令需要使用 item in items 形式的特殊语法,items 是源数据数组,而 item 是数组元素迭代的别名。
<div id="app">
<ol>
<!-- 每一次迭代了出数据给 todo,都会有一个 li 元素,并会在 li 元素中输出 todo 的 text -->
<li v-for="todo in todos">
{{ todo.text }}
</li>
</ol>
</div>
<!--
var app = new Vue({
el: '#app',
data: {
todos: [{ text: '学习 JavaScript'},
{text: '学习 Vue'},
{text: '搞事情'}
]
}
})
-->
代码效果:
在 v-for 块中,我们拥有对父作用域属性的完全访问权限。[这是来自官网的话,我觉得有点多余,感觉子元素获取父元素的数据是很正常的操作。]
v-for 还支持一个可选的第二个参数为当前项的索引,它会从 0 开始。用来标识当前迭代的是第几个元素。
可以用 of 替代 in 作为分隔符,因为它是最接近 JavaScript 迭代器的语法:<div v-for="item of items"></div>
也可以用 v-for 迭代一个对象的所有属性,第一个参数是 value,第二个可选参数是 key,第三个可选参数为索引
第二个参数为 key
<div v-for="(value, key) in object">
{{ key }}: {{ value }}
</div>
第三个参数为索引:
<div v-for="(value, key, index) in object">
{{ index }}. {{ key }}: {{ value }}
</div>
使用三个参数时的代码效果:
在遍历对象时,是按 Object.keys() 的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎下是一致的。
v-for 也可以取一个整数作为源数据(v-for="i in 10"
)。在这种情况下,它将重复多次模板。
当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级。【这个问题官网提了一下,这里我也提一下,注意使用】
计算属性
- 当某个数据需要另外一个数据计算得到的时候,你可能会定义两个数据, 其中一个数据初始值由另一个数据计算得出。但上面的值只能初始化一次,如果希望数据 A 变的时候,数据 B 也变,那么你可能需要去监听数据的变化,从而每次数据变化时再做操作。
- 计算属性简化了这样的情况,它的返回值是一个计算得到的数据,当内部的计算元素发生变化的时候,返回值也会变化。
- 计算属性使用 computed 来定义。
<div id="app">
<p>原消息: "{{ message }}"</p>
<p>逆反后: "{{ reversedMessage }}"</p>
</div>
<!--
var app = new Vue({
el: '#app',
data: {message: 'Hello'},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
-->
与函数的区别
下面的代码也可以达到上面的效果
(也是即时的,有人不懂为什么这个函数会重复调用,而非页面初始化时调用一次;因为当页面中数据更新的时候,涉及页面数据的函数也会重新执行。)
<div id="app">
<p>原消息: "{{ message }}"</p>
<p>逆反后: "{{ reversedMessage() }}"</p>
<p>{{ Date.now() }}"</p>
<!-- 后面的 now 是用来测试数据刷新时,函数会重新执行 -->
</div>
<!--
var app = new Vue({
el: '#app',
data: {message: 'Hello'},
methods: {reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
-->
函数也可以达到同样的效果,但是函数没有缓存效果,而计算属性有缓存。没有缓存时,函数每一次都要重新计算来得到结果,如果这是一个比较消耗资源的计算的话,那么会减慢页面的速度;而计算属性有缓存,只要内部的计算元素没有发生变化,那么会使用缓存来作为计算结果。
与侦听属性的区别
上面的计算属性达到效果需要留意计算元素的变化,你可能会想到一些类似的监听数据变化的方法,而 vue 中也是有这样的东西的。
下面的代码监听了 firstName 和 lastName,当这两个数据变化的时候会重新给 fullName 赋值。
<div id="app">
<div>{{ fullName }}</div>
</div>
<!--
var app = new Vue({
el: '#app',
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
- 上面的 fullName 例子中,当 firstName 和 lastName 更改的时候,fullName 会更改。
- 但有没有可能要求 fullName 更改的时候,firstName 和 lastName 会对应更改呢?
- 上面的获取变化后的 fullName 是 getter,其实还可以设置 setter
<div id="app">
<div>{{ fullName }}</div>
</div>
<!--
var app = new Vue({
el: '#app',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
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]
}
}
}
})
-->
侦听器
虽然计算属性已经提供了很多的好处,但有些时候计算属性也不能满足我们的要求。比如我们希望监听某个属性的变化来得到另一个属性的结果,但是不希望它马上得到结果,那么这时候计算属性就达不到需求了。而监听器里面可以写一些其他代码(比如一些延迟去得到结果的代码)。
当需要在数据变化时执行异步或开销较大的操作时,watch 是最有用的。
为什么有时候不希望计算新结果那么快呢?这就好比有人在百度搜索中输入一个个字,如果每个字都要进行检索的话,那么就对百度来说开销就很大了,如果稍微等等,确认用户输入完毕再去计算,那么就节省了很多资源了,下面的来自官网的例子正是一种这样的考虑了资源开销的例子。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<span class="hljs-tag"><<span class="hljs-name">body</span>></span>
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"watch-example"</span>></span>
<span class="hljs-tag"><<span class="hljs-name">p</span>></span>
Ask a yes/no question:
<span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"question"</span>></span>
<span class="hljs-tag"></<span class="hljs-name">p</span>></span>
<span class="hljs-tag"><<span class="hljs-name">p</span>></span>{{ answer }}<span class="hljs-tag"></<span class="hljs-name">p</span>></span>
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
<span class="hljs-tag"></<span class="hljs-name">body</span>></span>
<span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"</span>></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
<span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"</span>></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
<span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"</span>></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
<span class="hljs-tag"><<span class="hljs-name">script</span>></span><span class="language-javascript">
<span class="hljs-keyword">var</span> watchExampleVM = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Vue</span>({
<span class="hljs-attr">el</span>: <span class="hljs-string">'#watch-example'</span>,
<span class="hljs-attr">data</span>: {
<span class="hljs-attr">question</span>: <span class="hljs-string">''</span>,
<span class="hljs-attr">answer</span>: <span class="hljs-string">'I cannot give you an answer until you ask a question!'</span>
},
<span class="hljs-attr">watch</span>: {
<span class="hljs-comment">// 如果 `question` 发生改变,这个函数就会运行</span>
<span class="hljs-attr">question</span>: <span class="hljs-keyword">function</span> (<span class="hljs-params">newQuestion, oldQuestion</span>) {
<span class="hljs-variable language_">this</span>.<span class="hljs-property">answer</span> = <span class="hljs-string">'Waiting for you to stop typing...'</span>
<span class="hljs-variable language_">this</span>.<span class="hljs-title function_">debouncedGetAnswer</span>()
}
},
<span class="hljs-attr">created</span>: <span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {
<span class="hljs-comment">// `_.debounce` 是一个通过 Lodash 限制操作频率的函数。</span>
<span class="hljs-comment">// 在这个例子中,我们希望限制访问 yesno.wtf/api 的频率</span>
<span class="hljs-comment">// AJAX 请求直到用户输入完毕才会发出。想要了解更多关于</span>
<span class="hljs-comment">// `_.debounce` 函数 (及其近亲 `_.throttle`) 的知识,</span>
<span class="hljs-comment">// 请参考:https://lodash.com/docs#debounce</span>
<span class="hljs-variable language_">this</span>.<span class="hljs-property">debouncedGetAnswer</span> = _.<span class="hljs-title function_">debounce</span>(<span class="hljs-variable language_">this</span>.<span class="hljs-property">getAnswer</span>, <span class="hljs-number">500</span>)
},
<span class="hljs-attr">methods</span>: {
<span class="hljs-attr">getAnswer</span>: <span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {
<span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">question</span>.<span class="hljs-title function_">indexOf</span>(<span class="hljs-string">'?'</span>) === -<span class="hljs-number">1</span>) {
<span class="hljs-variable language_">this</span>.<span class="hljs-property">answer</span> = <span class="hljs-string">'Questions usually contain a question mark. ;-)'</span>
<span class="hljs-keyword">return</span>
}
<span class="hljs-variable language_">this</span>.<span class="hljs-property">answer</span> = <span class="hljs-string">'Thinking...'</span>
<span class="hljs-keyword">var</span> vm = <span class="hljs-variable language_">this</span>
axios.<span class="hljs-title function_">get</span>(<span class="hljs-string">'https://yesno.wtf/api'</span>)
.<span class="hljs-title function_">then</span>(<span class="hljs-keyword">function</span> (<span class="hljs-params">response</span>) {
vm.<span class="hljs-property">answer</span> = _.<span class="hljs-title function_">capitalize</span>(response.<span class="hljs-property">data</span>.<span class="hljs-property">answer</span>)
})
.<span class="hljs-title function_">catch</span>(<span class="hljs-keyword">function</span> (<span class="hljs-params">error</span>) {
vm.<span class="hljs-property">answer</span> = <span class="hljs-string">'Error! Could not reach the API. '</span> + error
})
}
}
})
</span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
</html>
作者:progor
本文为作者原创,转载请注明出处