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

  • {{item + key +index}}
  • 数组更新检测

    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监听事件

    
    

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

    
    

    表单输入绑定

    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': {}
      }
    });
    

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

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

    父子组件通信

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

    
    实现双向数据绑定
    

    非父子组件通信

    简单场景使用一个空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: '

    '
    });

    动态组件(通过is)

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

    
      
    
    
    
    
    
    //通过实例属性$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 (
    {this.level}
    ) } });

    $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()

    {{val}}
    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字符串,直接发送到浏览器,最后静态标记为“混合”,将它们变成响应式,成为客户端上完全交互的应用

    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
          自定义插件
    
    
    
    //index.js
    import MyLoading from './Loading.vue'
    const Loading = {
        //install导出必须是一个component
        install: function(Vue){
            Vue.component('Loading', MyLoading);
        }
    };
    
    export default Loading;
    

    使用自定义插件

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

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

    new Vue({
      el: '#app',
      template: ''
    });
    
    const author = Vue.extend({
      template: "
    author:{{name}}
    ", data: function () { return }, props: ['name'] }); //挂载后显示author:66 //通过propsData传入参数 new author({propsData: {name: 66}}).$mount('author');

    你可能感兴趣的:(Vue)