Vue

虚拟DOM

为啥要用?
DOM操作开销大,虚拟DOM提供一种方便的工具,保证开发效率,保证最小化DOM操作
用Js对象虚拟DOM树结构,当页面状态发生变化需要操作DOM时,先操作虚拟DOM计算出对真实DOM最小修改量,然后再修改真实DOM结构,保证最小化DOM操作

虚拟DOM是纯粹JS对象,操作高效,但最终会转换成真实DOM,因此需要一套高效的虚拟DOM diff算法

Vue的diff基于snabbdom,仅仅在同级的DOM间做diff, 递归的进行同级DOM的diff,最终实现整个DOM树更新
Vue的diff实现
oldStart+oldEnd, newStart+newEnd,分别对应oldVdom和newVdom起止点。Vue不断对虚拟DOM处理同时移动指针直到其中任一对起点和终点相遇,处理过的节点会在oldVdom和newVdom中同时标记为已处理,整个过程逐步找到更新前后vnode差异,然后将差异反应到DOM树(就是patch),Vue的patch是即时,并非打包所有修改最后一起操作DOM(React是将更新放入队列后集中处理)
1.优先处理特殊场景,头部同类型节点,尾部同类型节点
1.原地复用,Vue尽可能复用DOM,尽可能不发生DOM移动(key管理可复用元素)

问题:
1.内存:虚拟DOM需要在内存中维护一份DOM副本,在DOM更新速度和使用内存间取得平衡
2.适合一次大量更新虚拟DOM,但单一频繁跟新,虚拟DOM会花费更多时间处理计算工作。如页面DOM节点相对少,用虚拟DOM可能会更慢,但对于大多数单页应用,应该会更快

实例生命周期

beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,beforeDestroy,destroyed
create, mount, update, destroy
beforeCreate:$el,$data全部undefined
created:$el,undefine, $data初始化
beforeMount: $el, $data初始化
Mount:

模板

模板编译为虚拟DOM渲染函数(render函数)
状态改变,计算渲染组件最小代价更新DOM

v-bind:href="url"缩写:href
v-on:click="funClick"缩写@click

计算属性

var app = new Vue({
  data:{
    message:66
  },
  computed:{
    myMessage:function(){return this.message},
    myMessage2:{
      get:function(){return this.message},
      set:function(newValue){this.message = newValue;}
    }
  },
  watch:{
    message:function(newValue, oldValue){}
   }
});

v-if元素惰性渲染(直到条件为true,才渲染),更高切换开销
v-show元素总被渲染,更高初始渲染开销

v-for默认使用就地复用策略跟新已渲染元素列表,为追踪每个节点,需要为每项提供一个唯一key,优先级高于v-if

<li v-for="(item, key, index) in items" :key="index">{{item + key +index}}</li>
<!-- 组件有自己独立作用域,传递数据到组件要用props,避免耦合,组件v-for必绑定key -->
<my-component v-for="item in items" :item="item" :key="item.id"></my-component>

数组更新检测

Vue重写了数组的push, pop, shift, unshift, splice, sort, reverse会触发视图更新
filter,concat,slice等返回新数组,可用新数组赋值替换,触发视图更新

//Vue不能检测
vm.items[0] = 6;
vm.items.length = 0;
//替代方案
Vue.set(vm.items, 0, 6);

对象更新检测

Vue.set(app.items, "third", 3);
Vue.delete(app.items, "third");

v-on监听事件

<button @click="funClick($event)"></button>

事件修饰符:
.stop(阻止事件冒泡)
.prevent(阻止事件默认动作)
.capture(在捕获阶段监听事件)
.self(跳过冒泡和捕获事件,只有直接作用在该元素上的事件才执行)
.once(事件只触发一次)
键盘修饰符:

<input @keyup.enter="submit" />

表单输入绑定

v-model指令在表单元素上创建双向数据绑定(忽略value, checked, selected初始值)
修饰符:
.lazy(v-model绑定值同步延迟到change事件)
.number(v-model绑定值转换为Number)
.trim(v-model绑定值去首位空格)

组件

//全局组件
Vue.component('my-component', {});
//局部组件
new Vue({
  components:{
    'my-component': {}
  }
});
<table>
  <tr is="my-row"></tr>
</table>

为啥component的data被设计为一个函数?

返回一个唯一的对象,提示你避免和其他组件共用一个对象,通过function(){return {count:0};}返回一个新对象

父子组件通信

父组件通过props(单向绑定,父组件属性变化会传给子组件,反之不会)向下传递数据给子组件,子组件通过events给父组件发送消息
props可以指定验证规则
$on(eventName)
$emit(eventName)
v-model语法糖实际转化为

<input :value="value" 
@input="value=$event.target.value"/>
实现双向数据绑定

非父子组件通信

简单场景使用一个空Vue实例做事件中转
var bus = new Vue();
bus.$emit('my-event', 6);
bus.$on('my-event', function(arg){});
复制情况考虑Vuex(状态管理)

slot

Vue.component('my-list', {
props: ['items'],
template: '<ul><slot name="listItem" v-for="item in items" :text="item"></slot></ul>'
});

动态组件(通过is)

keep-alive保留切换出去的组件在内存,避免重新渲染

<keep-alive>
  <component :is="currentVie"></component>
</keep-alive>
<!-- ref为子组件指定索引,用来在javascript中直接访问子组件 -->
<my-component ref="my"></my-component>
//通过实例属性$refs访问子组件
app.$refs.my.name;

异步组件

Vue.component('async-component', function(resolve, reject){resolve({template:''})});

混合

分发Vue组件中可复用功能

var mixin = {};
var app = new Vue({
  mixins: [mixin]
});

指令

对纯DOM元素进行底层操作
钩子函数:bind(指令第一次绑定到元素调用,只调用一次), inserted(绑定元素插入父节点), update, componentUpdated, unbind

//注册全局指令
Vue.directive('focus', {
  inserted:function(el){
    el.focus();
  }
});
//注册局部指令
new Vue({
  el:"#app",
  directives:{
    focus:{...}
  }
});

渲染函数&JSX

Vue.component('myHeader', {
  props:['level'],
  //h为createElement别名为Vue惯例
  render:function(h){
    return h('h' + this.level);
    //支持JSX
    //return (<div>{this.level}</div>)
  }
});

$mount

将Vue实例(逻辑应用),挂靠在某个DOM上
Vue实例渲染过程:
Vue构造函数自动运行this._init(启动函数)
new Vue();

hook beforeCreate();

Observe Data; //data变成发布者,watch变订阅者
Init Events;

hook created();

Has el?No when vm.$mount("el") is called
Yes has template?
Complie template or Complie el as template
开始编译template模板生成的HTML

hook beforeMount();

create vm.$el and replace "el"
将编译好的html替换掉el属性所指向的dom对象或替换对应HTML标签内容

hook mounted();

Mounted;
when data changes;

hook beforeUpdate();

virtual DOM re-render and patch;

hook updated();

when vm.$destroy() is called;

hook beforeDestroy();

Teardown watchers, child components and event listeners

hook destroyed();

独立构建:
html template -> render函数 -> vnode -> DOM
运行时构建(少一个模板编辑器):
render函数 -> vnode -> DOM

$mount()手动挂载
Vue实例没有el属性,该实例尚未挂载到某个DOM上,可以手动调用vm.$mount()

<div id="app">{{val}}</div>
var app = new Vue({
    data:{val:6}
});
//手动挂载后{{val}}才显示6
app.$mount("#app");

响应式原理

通过Object.defineProperty(IE8以下不支持)将对象转为getter/setter
因为Vue在实例初始化时执行getter/setter转换,所以不能检测后续对象属性的添加删除(受JavaScript限制),除非使用set方法

数据变化后立即使用Vue.nextTick(callback)在DOM更新后调用

SSR

解决SEO和首屏渲染性能
webpack插件prerender-spa-plugin添加预渲染
将一个组件渲染为服务器端HTML字符串,直接发送到浏览器,最后静态标记为“混合”,将它们变成响应式,成为客户端上完全交互的应用

<div id="app" data-server-rendered="true">

data-server-rendered特殊属性,让客户端Vue知道标记由服务器渲染,并应该以混合模式挂载
app.$mount("#app");

框架对比

VS React
相同点:
1.使用虚拟DOM
2.提供响应式,组件化
不同:
1.Vue默认使用Templates,虽然Vue提供了render,支持JSX
2.Vue和Weex合作替代ReactNative
VS Angular
1.Vue更简单
2.Angular使用双向绑定,Vue在不同组件间强制使用单向数据流
3.Vue将指令与组件分得更清晰
4.可能会有更好的性能,不使用脏检查,watcher越多性能越差
5.体积小

项目

http请求自定义头部

Vue.http.headers.common["x-user-email"] = gon.user_email.toString();

事件分发

var app = new Vue({
    el:"#app",
    data:{
        eventHub:new Vue()
    }
});

//监听事件
this.$root.eventHub.$on(eventName, handler);
//触发事件
this.$root.eventHub.$emit(eventName, data);

自动搜索

Vue.component('typeAhead', {
    data:function(){
        return {keyword:"", isLoading:false};
    },
    watch:{
        keyword: function(){
            this.isLoading = true;
            this.search();
        },
        'data.isLoading':{
            handler:function(val, oldVal){...},
            //深度观察,如果想监控对象
            deep:true
        }
    },
    methods:{
        search:_.debounce(function(){
            var that = this;
            Vue.http({url:this.filterUrl})
                .then().catch(function(){
                  that.isLoading = false;
                });
         }, 500)
    }
});

Vue.use
使用别人开发的插件,第一步install, 第二步main.js引入,第三步Vue.use

  • selfComponents
    • loading
      • index.js
      • Loading.vue
        自定义插件
<!-- Loading.vue -->
<template>
    <div>loading...</div>
</template>
//index.js
import MyLoading from './Loading.vue'
const Loading = {
    //install导出必须是一个component
    install: function(Vue){
        Vue.component('Loading', MyLoading);
    }
};

export default Loading;

使用自定义插件

<template>
    <Loading></Loading>
</template>
//在main.js中引入
import Loading from 'selfComponents/loading';
Vue.use(Loading);

extend
Vue构造器创建一个子类,返回一个扩展实例构造器(就是预设了某些选项的Vue实例构造器)

new Vue({
  el: '#app',
  template: '<author></author>'
});

const author = Vue.extend({
  template: "<div>author:{{name}}</div>",
  data: function () {
    return
  },
  props: ['name']
});

//挂载后显示author:66
//通过propsData传入参数
new author({propsData: {name: 66}}).$mount('author');
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容