vue.js methods this中的方法互相调用时变量的作用域是怎样的

Vue.js:轻量高效的前端组件化方案
发表于 16:24|
来源程序员杂志|
作者尤雨溪
摘要:Vue.js通过简洁的API提供高效的数据绑定和灵活的组件系统。在前端纷繁复杂的生态中,Vue.js有幸受到一定程度的关注,目前在GitHub上已经有5000+的star。本文将从各方面对Vue.js做一个深入的介绍。
Vue.js 是我在2014年2月开源的一个前端开发库,通过简洁的 API 提供高效的数据绑定和灵活的组件系统。在前端纷繁复杂的生态中,Vue.js有幸受到一定程度的关注,目前在 GitHub上已经有5000+的star。本文将从各方面对Vue.js做一个深入的介绍。
2013年末,我还在Google Creative Lab工作。当时在项目中使用了一段时间的Angular,在感叹数据绑定带来生产力提升的同时,我也感到Angular的API设计过于繁琐,使得学习曲线颇为陡峭。出于对Angular数据绑定原理的好奇,我开始
“造轮子”,自己实现了一个非常粗糙的、基于依赖收集的数据绑定库。这就是Vue.js的前身。同时在实际开发中,我发现用户界面完全可以用嵌套的组件树来描述,而一个组件恰恰可以对应MVVM中的ViewModel。于是我决定将我的数据绑定实验改进成一个真正的开源项目,其核心思想便是
“数据驱动的组件系统”。
MVVM 数据绑定
MVVM的本质是通过数据绑定链接View和Model,让数据的变化自动映射为视图的更新。Vue.js在数据绑定的API设计上借鉴了Angular的指令机制:用户可以通过具有特殊前缀的HTML
属性来实现数据绑定,也可以使用常见的花括号模板插值,或是在表单元素上使用双向绑定:&!-- 指令 --&
&span v-text="msg"&&/span&
&!-- 插值 --&
&span&{{msg}}&/span&
&!-- 双向绑定 --&
&input v-model="msg"&
插值本质上也是指令,只是为了方便模板的书写。在模板的编译过程中,Vue.js会为每一处需要动态更新的DOM节点创建一个指令对象。每当一个指令对象观测的数据变化时,它便会对所绑定的目标节点执行相应的DOM操作。基于指令的数据绑定使得具体的DOM操作都被合理地封装在指令定义中,业务代码只需要涉及模板和对数据状态的操作即可,这使得应用的开发效率和可维护性都大大提升。
图1 Vue.js的MVVM架构
与Angular不同的是,Vue.js的API里并没有繁杂的module、controller、scope、factory、service等概念,一切都是以“ViewModel
实例”为基本单位:
&!-- 模板 --&
&div id="app"&
// 原生对象即数据
var data = {
msg: 'hello!'
// 创建一个 ViewModel 实例
var vm = new Vue({
// 选择目标元素
el: '#app',
// 提供初始数据
data: data
渲染结果:
&div id="app"&
在渲染的同时,Vue.js也已经完成了数据的动态绑定:此时如果改动data.msg的值,DOM将自动更新。是不是非常简单易懂呢?除此之外,Vue.js对自定义指令、过滤器的API也做了大幅的简化,如果你有Angular的开发经验,上手会非常迅速。
数据观测的实现
Vue.js的数据观测实现原理和Angular有着本质的不同。了解Angular的读者可能知道,Angular的数据观测采用的是脏检查(dirty
checking)机制。每一个指令都会有一个对应的用来观测数据的对象,叫做watcher;一个作用域中会有很多个watcher。每当界面需要更新时,Angular会遍历当前作用域里的所有watcher,对它们一一求值,然后和之前保存的旧值进行比较。如果求值的结果变化了,就触发对应的更新,这个过程叫做digest
cycle。脏检查有两个问题:
任何数据变动都意味着当前作用域的每一个watcher需要被重新求值,因此当watcher的数量庞大时,应用的性能就不可避免地受到影响,并且很难优化。
当数据变动时,框架并不能主动侦测到变化的发生,需要手动触发digest cycle才能触发相应的DOM 更新。Angular通过在DOM事件处理函数中自动触发digest cycle部分规避了这个问题,但还是有很多情况需要用户手动进行触发。
Vue.js采用的则是基于依赖收集的观测机制。从原理上来说,和老牌MVVM框架Knockout是一样的。依赖收集的基本原理是:
将原生的数据改造成 “可观察对象”。一个可观察对象可以被取值,也可以被赋值。
在watcher的求值过程中,每一个被取值的可观察对象都会将当前的watcher注册为自己的一个订阅者,并成为当前watcher的一个依赖。
当一个被依赖的可观察对象被赋值时,它会通知所有订阅自己的watcher重新求值,并触发相应的更新。
依赖收集的优点在于可以精确、主动地追踪数据的变化,不存在上述提到的脏检查的两个问题。但传统的依赖收集实现,比如Knockout,通常需要包裹原生数据来制造可观察对象,在取值和赋值时需要采用函数调用的形式,在进行数据操作时写法繁琐,不够直观;同时,对复杂嵌套结构的对象支持也不理想。
Vue.js利用了ES5的Object.defineProperty方法,直接将原生数据对象的属性改造为getter和setter,在这两个函数内部实现依赖的收集和触发,而且完美支持嵌套的对象结构。对于数组,则通过包裹数组的可变方法(比如push)来监听数组的变化。这使得操作Vue.js的数据和操作原生对象几乎没有差别[注:在添加/删除属性,或是修改数组特定位置元素时,需要调用特定的函数,如obj.$add(key,
value)才能触发更新。这是受ES5的语言特性所限。],数据操作的逻辑更为清晰流畅,和第三方数据同步方案的整合也更为方便。
图2 Vue.js的数据观测和数据绑定实现图解
在大型的应用中,为了分工、复用和可维护性,我们不可避免地需要将应用抽象为多个相对独立的模块。在较为传统的开发模式中,我们只有在考虑复用时才会将某一部分做成组件;但实际上,应用类
UI 完全可以看作是全部由组件树构成的:
&图3 UI = 组件树
因此,在Vue.js的设计中将组件作为一个核心概念。可以说,每一个Vue.js应用都是围绕着组件来开发的。
注册一个Vue.js组件十分简单:
<ponent(&#039;my-component&#039;, {
template: &#039;&div&{{msg}} {{privateMsg}}&/div&&#039;,
// 接受参数
msg: String&br&
// 私有数据,需要在函数中返回以避免多个实例共享一个对象
data: function () {
privateMsg: &#039;component!&#039;
注册之后即可在父组件模板中以自定义元素的形式调用一个子组件:
&my-component msg="hello"&&/my-component&
渲染结果:
&div&hello component!&/div&
Vue.js的组件可以理解为预先定义好了行为的ViewModel类。一个组件可以预定义很多选项,但最核心的是以下几个:
模板(template):模板声明了数据和最终展现给用户的DOM之间的映射关系。
初始数据(data):一个组件的初始数据状态。对于可复用的组件来说,这通常是私有的状态。
接受的外部参数(props):组件之间通过参数来进行数据的传递和共享。参数默认是单向绑定(由上至下),但也可以显式地声明为双向绑定。
方法(methods):对数据的改动操作一般都在组件的方法内进行。可以通过v-on指令将用户输入事件和组件方法进行绑定。
生命周期钩子函数(lifecycle hooks):一个组件会触发多个生命周期钩子函数,比如created,attached,destroyed等等。在这些钩子函数中,我们可以封装一些自定义的逻辑。和传统的MVC相比,可以理解为 Controller的逻辑被分散到了这些钩子函数中。
私有资源(assets):Vue.js当中将用户自定义的指令、过滤器、组件等统称为资源。由于全局注册资源容易导致命名冲突,一个组件可以声明自己的私有资源。私有资源只有该组件和它的子组件可以调用。
除此之外,同一颗组件树之内的组件之间还可以通过内建的事件API来进行通信。Vue.js提供了完善的定义、复用和嵌套组件的API,让开发者可以像搭积木一样用组件拼出整个应用的界面。这个思路的可行性在Facebook开源的React当中也得到了印证。
基于构建工具的单文件组件格式
Vue.js的核心库只提供基本的API,本身在如何组织应用的文件结构上并不做太多约束。但在构建大型应用时,推荐使用Webpack+vue-loader这个组合以使针对组件的开发更高效。
Webpack是由Tobias Koppers开发的一个开源前端模块构建工具。它的基本功能是将以模块格式书写的多个JavaScript文件打包成一个文件,同时支持CommonJS和AMD格式。但让它与众不同的是,它提供了强大的loader
API来定义对不同文件格式的预处理逻辑,从而让我们可以将CSS、模板,甚至是自定义的文件格式当做JavaScript模块来使用。Webpack
基于loader还可以实现大量高级功能,比如自动分块打包并按需加载、对图片资源引用的自动定位、根据图片大小决定是否用base64内联、开发时的模块热替换等等,可以说是目前前端构建领域最有竞争力的解决方案之一。
我在Webpack的loader API基础上开发了vue-loader插件,从而让我们可以用这样的单文件格式 (*.vue) 来书写Vue组件:
.my-component h2 {
&template&
&div class="my-component"&
&h2&Hello from {{msg}}&/h2&
&other-component&&/other-component&
&/template&
// 遵循 CommonJS 模块格式
var otherComponent = require(&#039;./other-component&#039;)
// 导出组件定义
module.exports = {
data: function () {
msg: &#039;vue-loader&#039;
components: {
&#039;other-component&#039;: otherComponent
同时,还可以在*.vue文件中使用其他预处理器,只需要安装对应的Webpack loader即可:
&style lang="stylus"&
.my-component h2
&template lang="jade"&
div.my-component
h2 Hello from {{msg}}
&/template&
&script lang="babel"&
// 利用 Babel 编译 ES2015
export default {
msg: &#039;Hello from Babel!&#039;
这样的组件格式,把一个组件的模板、样式、逻辑三要素整合在同一个文件中,即方便开发,也方便复用和维护。另外,Vue.js本身支持对组件的异步加载,配合Webpack的分块打包功能,可以极其轻松地实现组件的异步按需加载。
Vue.js还有几个值得一提的特性:
异步批量DOM更新:当大量数据变动时,所有受到影响的watcher会被推送到一个队列中,并且每个watcher只会推进队列一次。这个队列会在进程的下一个 “tick” 异步执行。这个机制可以避免同一个数据多次变动产生的多余DOM操作,也可以保证所有的DOM写操作在一起执行,避免DOM读写切换可能导致的layout。
动画系统:Vue.js提供了简单却强大的动画系统,当一个元素的可见性变化时,用户不仅可以很简单地定义对应的CSS Transition或Animation效果,还可以利用丰富的JavaScript钩子函数进行更底层的动画处理。
可扩展性:除了自定义指令、过滤器和组件,Vue.js还提供了灵活的mixin机制,让用户可以在多个组件中复用共同的特性。
与Web Components的异同
对Web Components有了解的读者看到这里可能会产生疑问:Vue.js的组件和Web Components的区别在哪里呢?这里简要地做一下分析。
Web Components是一套底层规范,本身并不带有数据绑定、动画系统等上层功能,因此更合适的比较对象可能是Polymer。Polymer在API和功能上和Vue.js比较相似,但它对Web
Components的硬性依赖使得它在浏览器支持方面有一定的问题——在不支持Web Components规范的浏览器中,需要加载庞大的polyfill,不仅在性能上会有影响,并且有些功能,比如ShadowDOM,polyfill并没有办法完美支持。同时,Web
Components规范本身尚未定稿,一些具体设计上仍存在不小的分歧。相比之下,Vue.js在支持的浏览器中(IE9+)没有任何依赖。
除此之外,在支持Web Components的环境中,我们也可以很简单地利用Web Components底层API将一个Vue.js组件封装在一个真正的自定义元素中,从而实现Vue.js组件和其他框架的无缝整合。
在发布之初,Vue.js原本是着眼于轻量的嵌入式使用场景。在今天,Vue.js也依然适用于这样的场景。由于其轻量(22kb min+gzip)、高性能的特点,对于移动场景也有很好的契合度。更重要的是,设计完备的组件系统和配套的构建工具、插件,使得Vue.js在保留了其简洁API的同时,也已经完全有能力担当起复杂的大型应用的开发。
从诞生起到现在的一年半历程中,Vue.js经历了一次彻底的重构,多次API的设计改进,目前已经趋于稳定,测试覆盖率长期保持在100%,GitHub
Bug数量长期保持在个位数,并在世界各地都已经有公司/项目将Vue.js应用到生产环境中。在2015年晚些时候,Vue.js将发布1.0版本,敬请期待。
【参考链接】
Vue.js官方网站:
Vue.js GitHub仓库:
Webpack官方网站:&
vue-loader单页组件示例:
本文选自程序员电子版2015年8月A刊,该期更多文章请查看。2000年创刊至今所有文章目录请查看。欢迎(含iPad版、Android版、PDF版)。
欢迎加入CSDN前端交流群2:,进行前端技术交流。&
你也可以扫描下面二维码,加入CSDN前端大讲堂微信群,享受高含金量在线公开课的同时,还可与专家讲师在线切磋交流。(如果群满受限,你可加“Rachel_qg”为好友,并注明“参加CSDN前端大讲堂”,申请入群。)
近期CSDN前端大讲堂课程预告:分享主题简介:React 带来的革命性创新是前端世界过去几年最激动人心的变化。自从接触 React 以来,我们深信 React 会彻底改变客户端开发者(包括前端、iOS 和 Android)的开发体验。本公开课中,将从四个大的方向:目标平台(targets)、数据处理(data)、工具(tools)和新的挑战,分享 React 生态系统和社区的进展和未来趋势。同时也将在线解答前端开发者在使用React过程中所面临的难点。主讲人介绍:郭达峰, CTO、联合创始人,具有多年前端开发经验。2010年开发了三款Facebook平台应用,获取了超过千万的用户。2012年创立了建站平台Strikingly,成为第一家进入YC孵化器的华人团队。已使用React重写了大部分应用,是国内使用React比较多的公司之一。课前温习:
推荐阅读相关主题:
为了更好帮助企业深入了解国内外最新大数据技术,掌握更多行业大数据实践经验,进一步推进大数据技术创新、行业应用和人才培养,-12日,由中国计算机学会(CCF)主办,CCF大数据专家委员会承办,中国科学院计算技术研究所、北京中科天玑科技有限公司及CSDN共同协办的2015中国大数据技术大会(Big Data Technology Conference 2015,BDTC 2015)将在北京新云南皇冠假日酒店隆重举办。
相关热门文章Vue.js 快速入门
Vue.js 快速入门
什么是Vue.js
vue是法语中视图的意思,Vue.js是一个轻巧、高性能、可组件化的MVVM库,同时拥有非常容易上手的API。作者是,写下这篇文章时vue.js版本为1.0.7
我推荐使用sublime text作为编辑器,关于这个编辑器可以看我这篇文章。在package control中安装
Vuejs Snippets
Vue Syntax Highlight
推荐使用npm管理,新建两个文件app.html,app.js,为了美观使用bootstrap,我们的页面模板看起来是这样:
&!DOCTYPE html&
&html lang="en"&
&meta charset="UTF-8"&
&title&Document&/title&
&link rel="stylesheet" type="text/css" href="/bootstrap/3.3.5/css/bootstrap.min.css"&
&div class="container"&
&div class="col-md-6 col-md-offset-3"&
&h1&Vue demo&/h1&
&div id="app"&
使用npm安装:
npm install vue
当然你也可以在github上clone最新的版本并作为单文件引入,或者使用CDN:
http://cdn.jsdelivr.net/vue/1.0.7/vue.min.js
/ajax/libs/vue/1.0.7/vue.min.js
HelloWorld
动手写第一个Vue.js 应用吧。
&div id="app"&
&div&{{message}}&/div&
&input type="text" v-model="message"&
el:'#app',
message:'hello vue.js.'
创建Vue实例
在使用Vue.js之前,我们需要先像这样实例化一个Vue对象:
双向数据绑定
就像HelloWorld展示的那样,app.html是view层,app.js是model层,通过vue.js(使用v-model这个指令)完成中间的底层逻辑,实现绑定的效果。改变其中的任何一层,另外一层都会改变。
相信你也注意到了,通过{{value}}的形式就能取到value的值,并与value进行绑定。HelloWorld中改变input中的值时相应也改变了app.js中的message,从而{{message}}也得到改变。上面的代码改为这样:
{{*message}}
则message不会随着数据的改变而更新。同时还支持一些简单的表达式:
{{message + 'vue is awesome'}}
{{ message.split('').reverse().join('') }}
常用的指令
v-model可用于一些表单元素,常见的input,checkbox,radio,select:
&select v-model="selected" multiple&
&option selected&A&/option&
&option&B&/option&
&option&C&/option&
&span&Selected: {{ selected | json }}&/span&
列表渲染在实际开发中非常常见,vue.js使用v-for这个指令就能完成,v-for取代了1.0以前版本中的v-repeat。在app.js中准备一些数据:
el: '#app',
author: '',
author: '曹雪芹',
name: '红楼梦',
price: 32.0
author: '施耐庵',
name: '水浒传',
price: 30.0
author: '罗贯中',
name: '三国演义',
price: 24.0
author: '吴承恩',
name: '西游记',
price: 20.0
在data里我们设置了两个数据book和book[] books,在app.html中我们只要这样就能获取到数据了:
&tr v-for="book in books "&
&td&{{book.id}}&/td&
&td&{{book.name}}&/td&
&td&{{book.author}}&/td&
&td&{{book.price}}&/td&
如果你比较细心的话,在数据还未加载完时是会有闪烁的情况出现,解决方法也很简单,使用v-cloak,然后定义css:
[v-cloak] { display: none }
vue.js通过v-on完成事件处理与绑定,比如为一个button绑定click事件,我们就可以这么写:
&button v-on:click="doSomething"&doSomething&/button&
也可以缩写:
&button @click="doSomething"&doSomething&/button&
我们需要为v-on传入事件参数,然后在vue的实例中声明doSomething这个方法就可以调用了:
el: '#app',
methods: {
doSomething: function () {
接着上面书的例子,我们用v-model绑定form:
&div id="add-book"&
&legend&添加书籍&/legend&
&div class="form-group"&
&label for=""&书名&/label&
&input type="text" class="form-control" v-model="book.name"&
&div class="form-group"&
&label for=""&作者&/label&
&input type="text" class="form-control" v-model="book.author"&
&div class="form-group"&
&label for=""&价格&/label&
&input type="text" class="form-control" v-model="book.price"&
&button class="btn btn-primary btn-block" v-on:click="addBook()"&添加&/button&
在app.js中增加我们的addBook方法:
methods: {
addBook: function() {
//计算书的id
this.book.id = this.books.length + 1;
this.books.push(this.book);
//将input中的数据重置
this.book = '';
我们再健全一下功能,增加一个删除按钮:
&button type="button" class="btn btn-danger" @click="delBook(book)"&删除&/button&
delBook方法:
delBook:function(book){
this.books.$remove(book);
vue.js为数组扩展了$remove方法,查找并删除我们作为参数传递过去的book。
v-if/v-else/v-show
顾名思义,v-if用于条件判断,和v-else是一对。用法也很简单,下面的代码是将id为偶数的操作按钮换个样式:
&template v-if="book.id%2==0"&
&td class="text-right"&
&button type="button" class="btn btn-success" @click="delBook(book)"&删除&/button&
&/template&
&template v-else&
&td class="text-right"&
&button type="button" class="btn btn-danger" @click="delBook(book)"&删除&/button&
&/template&
这里用到了&template&标签,用于包含多个元素,当元素只有一个时,直接在元素上用v-if即可:
&h1 v-if="ok"&Yes&/h1&
&h1 v-else&No&/h1&
v-show作用与v-if类似,不同的是v-show的元素会始终渲染并保持在 DOM 中,且v-show不支持&template&标签。
与Linux中的管道类似,vue.js也使用的是|:
{{message | uppercase}}
这样就能输出message的大写了,过滤器也能串联在一起使用:
{{message | reverse | uppercase}}
这里reverse并不是内建的过滤器,我们可以用Vue.filter自定义:
Vue.filter('reverse', function (value) {
return value.split('').reverse().join('')
过滤器支持接收参数,比较常用的是orderBy [param]和filterBy [param],现在我们为表格增加自定义排序的功能,为表头绑定click事件:
&th class="text-right" @click="sortBy('id')"&序号&/th&
&th class="text-right" @click="sortBy('name')"&书名&/th&
&th class="text-right" @click="sortBy('author')"&作者&/th&
&th class="text-right" @click="sortBy('price')"&价格&/th&
想sortBy传递列的参数,定义sortBy和data:
sortparam: ''
sortBy: function(sortparam) {
this.sortparam =
添加过滤器:
&tr v-for="book in books | orderBy sortparam"&
计算属性可以帮助我们完成一些复杂的逻辑计算,比如我们需要添加一个书的总价,在vue实例中添加computed:
computed: {
sum: function() {
var result = 0;
for (var i = 0; i & this.books. i++) {
result = Number(this.books[i].price) +
在app.html中使用插值表达式:
vue-resource
vue-resource作为vue插件的形式存在,通过 XMLHttpRequest 或 JSONP 发起请求并处理响应。在开发中也非常常见,现在我们用vue-resource来请求books:
和vue类似:
npm install vue-resource --save
如果你的项目遵循CommonJS:
var Vue = require('vue');
Vue.use(require('vue-resource'));
也可以直接引入单文件或者CDN。
在vue中新增ready对象,当页面加载完成时就去请求:
el: '#app',
ready: function() {
this.$http.get('book.json', function(data) {
this.$set('books', data);
}).error(function(data, status, request) {
console.log('fail' + status + "," + request);
为了演示,这里将json格式的数据保存在book.json中,这段数据你可以直接使用JSON.stringify()得到:
[{"id":1,"author":"曹雪芹","name":"红楼梦","price":32},{"id":2,"author":"施耐庵","name":"水浒传","price":30},{"id":"3","author":"罗贯中","name":"三国演义","price":24},{"id":4,"author":"吴承恩","name":"西游记","price":20}]
接下来你需要将app.html中运行在一个服务器中,否则由于浏览器安全的限制,是无法直接读取的,如果你嫌麻烦可以用这个参数启动chrome。
.\chrome.exe --allow-file-access-from-files
如果你使用了npm,想要启动一个服务器就太简单了:
npm install http-server -g
//在当前目录
http-server
//然后访问localhost:8080/app.html
post的语法也很简单:
this.$http.post(url,postdata,function callback)
在使用的时候遇到一个小坑,这个$http请求和jquery的ajax还是有点区别,这里的post的data默认不是以form data的形式,而是request payload。解决起来也很简单:
在vue实例中添加headers字段:
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
后来翻了下vue-resource的源码,发现有更加简单的做法:
Vue.http.options.emulateJSON =
这里只简单介绍下,详细的文档请大家移步吧。
vue.js目前还有众多的插件,详情看。
这里简单介绍了下vue.js的基本用法,但只仅仅介绍了一小部分作为库使用的内容,想了解更多vue.js的内容,还是多多关注vue.js的,所用的例子我也分享了,可以在这里并运行结果。
作者:FullStackDeveloper
原文地址:
分享即可 +1积分
请登录后,发表评论
评论(Enter+Ctrl)
评论加载中...
评论加载中...
页面重构设计
微信号johncenaboy VIP 行星饭 GD sehun
改版之后唯一的好处就是能看到bigbang的大头像了!!
作者的热门文章
我的2015: 1、1月12日在武汉注册了自己的公司 2、2月有了第一笔业务 3、4月26日带着媳妇回武汉买了车,然后去云南玩了7天 4、9月19日老婆辞职回老家去装修房子 5、9月27日一个人去泰国清迈玩了7天 6、11月借着去重庆参加腾讯全球合作伙伴大会带着老婆在重庆玩了4天 7、12月4日房子装修完成,带着老婆去三亚玩了6天
Copyright (C)
All Rights Reserved | 京ICP备 号-2

我要回帖

更多关于 php变量作用域 的文章

 

随机推荐