精华内容
下载资源
问答
  • 深入理解vue核心设计模式
    千次阅读
    2020-07-15 01:08:34

    从今天听了大圣老师的课,感觉vue2到vue3的整体设计思路上还是没有变化的,依旧是通过建立一个被观察的对象,通过get方法收集依赖,set方法派发的方式去建立核心的“响应式”。

    这个设计模式我也看过不少文章,被称为订阅-发布设计模式确实更合理,在vue2的架构中,我们把核心的设计模式分为Observer,Dep,Watcher,Observer被称为观察者,观察着我们的数据,Dep为数据分配中心,收集数据和通知数据更新,Watcher作为订阅者,收集每个被订阅的对象,就比如我们vue中常见的{{value}},这就是一个订阅对象,每当发现一个需要被订阅的对象,就往对应Dep中新增一个Watcher类,这就是收集依赖的过程,而当value的值发生改变时,观察者就会告诉Dep,让Dep通知所有的Watcher进行数据更新,这就是所谓的数据响应式。

    再举个更简单的例子,当老板往内部系统发送一条公告时,我和我的同事都会在内部系统收到一条这条公告。注意:我们不是Watcher,而是当我们登录了这个内部系统,就创建了一个Watcher来关联我和内部系统。

    再看vue3的reactive实现方法,当我看到源码直接用一个WeakMap关联了被观测对象,关联数据,关键数据所相应的队列。其结构为{关联数据(作为observer被观测的对象为键值):{关联数据:所收集的依赖队列}},这就直接把Observer和Watcher给废除了,这也太牛了!具体可以看我的另一篇实现一个小Reactive原理的文章。

    更多相关内容
  • VUE设计模式详解

    2021-09-23 10:58:05
    设计模式(1):只执行一次的函数 概述 最近最近做项目的时候总会思考一些大的应用设计模式相关的问题,我把自己的思考记录下来,供以后开发时参考,相信对其他人也有用。 只执行一次的函数 我们经常会遇到这种...

    设计模式(1):只执行一次的函数

    概述

    最近最近做项目的时候总会思考一些大的应用设计模式相关的问题,我把自己的思考记录下来,供以后开发时参考,相信对其他人也有用。

    只执行一次的函数

    我们经常会遇到这种情况,就是希望某个函数只执行一次,以后就不执行了。一般情况下,我们会这么写:

    <script>
    export default {
      data() {
        return {
          runOnce: true,
        };
      },
      methods: {
        func() {
          console.log('hello world', this);
        },
        funcRunOnce() {
          if (this.runOnce) {
            this.func();
            this.runOnce = false;
          }
        },
      },
    };
    </script>

    但是这样并不优雅,不仅污染了data,还用2个方法进行实现,实在难看。

    用闭包改进

    于是我们考虑用闭包,把data里面的runOnce这个变量放到闭包里面去,这样就不会污染data了。代码如下:

    <script>
    export default {
      methods: {
        func() {
          console.log('hello world', this);
        },
        funcRunOnce(params) {
          let runOnce = true;
          return () => {
            if (runOnce) {
              this.func();
              runOnce = false;
            }
          }();
        },
      },
    };
    </script>

    但是这么写显然是错了,因为每次调用funcRunOnce都会构造一次闭包,里面的runOnce这个变量根本不会共享。所以继续改写如下:

    // 方法1
    <script>
    export default {
      created() {
        this.funcRunOnce = this.runOnce(this.func);
      },
      methods: {
        func() {
          console.log('hello world', this);
        },
        runOnce(func) {
          let runOnce = true;
          return (params) => {
            if (runOnce) {
              func(params);
              runOnce = false;
            }
          };
        },
      },
    };
    </script>
    
    // 方法2
    <script>
    export default {
      methods: {
        func() {
          console.log('hello world', this);
        },
        runOnce(func) {
          let runOnce = true;
          return (params) => {
            if (runOnce) {
              func(params);
              runOnce = false;
            }
          };
        },
        funcRunOnce: this.runOnce(this.func),
      },
    };
    </script>

    使用utils

    可以看到,上面的方法仍然很不优雅,要么用一个created和2个方法实现,要么用三个方法实现。而都用了一个公共的方法runOnce。所以我们考虑把runOnce放到utils.js里面去。

    // utils.js
    export function runOnce(func) {
      let runOnce = true;
      return (params) => {
        if (runOnce) {
          func(params);
          runOnce = false;
        }
      };
    }
    
    //example.vue
    import { runOnce } from '@/utils';
    <script>
    export default {
      methods: {
        funcRunOnce: runOnce(() => {
          console.log('hello world', this);
        }),
      },
    };
    </script>

    上面的写法看起来非常简洁,但是实际上是不行的,因为this的指向错了。由于runOnce返回的函数并不是vue实例的方法,所以里面的this指向的是undefined

    注意:即使看起来我们好像在funcRunOnce方法中用箭头函数捕获了外面实例的this,但是实际上它捕获的并不是外面的实例的this,而是runOnce返回的函数里面的this。

    捕获this

    能用箭头函数的地方我们都用了,但是为什么我们还是捕获不了this呢?如此一来是不是完成不了这个任务了?

    并不是,方法还是有的,方法是不用箭头函数捕获this。代码如下:

    // utils.js
    export function runOnce(func) {
      let runOnce = true;
      return function(params) {
        if (runOnce) {
          func.apply(this, params);
          runOnce = false;
        }
      };
    }
    
    //example.vue
    import { runOnce } from '@/utils';
    <script>
    export default {
      methods: {
        funcRunOnce: runOnce(function h() {
          console.log('hello world', this);
        }),
      },
    };
    </script>

    通过查看代码可以看出,2个地方的箭头函数都被改写成了function,并且还用到了apply函数来强制施加this。

    理由很简单,由于runOnce函数里面没有用箭头函数,所以它返回的函数是属于vue实例的,所以它返回的函数的this,是指向vue实例的;又因为funcRunOnce里面没有用箭头函数,所以我们可以用apply把这个this强制附加到func里面去!

    同理我们还可以写出第一次不执行,后续才执行的函数:

    // utils.js
    // 第一次不执行,后续再执行
    export function notRunOnce(func) {
      let once = false;
      return function(params) {
        if (once) {
          func.apply(this, params);
        }
        once = true;
      };
    }

    学到了什么

    1. 在vue里面可以用赋值的形式初始化方法,或者在created里面初始化方法。
    2. 箭头函数虽然能捕获this,但不是万能的;有时候我们需要用function和apply结合来捕获this。


    设计模式(2): 响应store中数据的变化

    概述

    最近最近做项目的时候总会思考一些大的应用设计模式相关的问题,我把自己的思考记录下来,供以后开发时参考,相信对其他人也有用。

    store里面响应数据变化

    通常情况下,我们会把数据存在store里面,并且,有时我们也需要跟踪store里面的数据变化,并作出响应。例子如下:

    export default {
      computed: {
        categories: state => state.categories.categories,
      },
      watch: {
        categories() {
          this.fetchCardData();
        },
      },
      methods: {
        fetchCardData() {
          // 请求卡片数据
        },
      },
    }

    如上所示,当store里面的categories改变的时候,我们会自动调用api去请求数据

    不响应store里面的数据变化

    上面的例子里面,每次当categories改变的时候,fetchCardData方法都会被调用。有些时候,这并不是我们想要的,我们想要的是,当xxxx的时候,categories会改变,fetchCardData方法会跟着被调用;当xxxx的时候,categories会改变,fetchCardData方法又不会跟着被调用,怎么办呢?

    方法是创造一个标记,但是如何优雅的创造标记呢?我有一个方法如下所示:

    // store.js
    const state = {
      categories: [],
      categoriesChanges: 0,
    };
    
    const actions = {
      updateCategories({ commit }, value) {
        // 如果带有shouldNotChange,则表示不要刷新页面
        if (value.shouldNotChange) {
          commit(types.UPDATE_CATEGORIES, value.data);
        } else {
          commit(types.UPDATE_CATEGORIES, value);
          commit(types.UPDATE_CATEGORIES_CHANGES);
        }
      },
    };
    
    const mutations = {
      [types.UPDATE_CATEGORIES](state, value) {
        state.categories = value;
      },
      [types.UPDATE_CATEGORIES_CHANGES](state) {
        state.categoriesChanges += 1;
      },
    };
    
    // component.js
    export default {
      computed: {
        categories: state => state.categories.categories,
        categoriesChanges: state => state.categories.categoriesChanges,
      },
      watch: {
        categoriesChanges() {
          this.fetchCardData();
        },
      },
      methods: {
        fetchCardData() {
          // 利用this.categories的部分数据来请求卡片数据
        },
      },
    }
    
    // business.js
    this.$store.dispatch('updateCategories', value); // 会自动调用fetchCardData方法
    
    const payload = {
      shouldNotChange: true,
      data: [...value],
    };
    this.$store.dispatch('updateCategories', payload); // 不会自动调用fetchCardData方法

    这样,我们发出同一个action,却能达到2种不同的效果,非常方便。


    设计模式(3): 二次封装与高阶组件 

    概述

    最近最近做项目的时候总会思考一些大的应用设计模式相关的问题,我把自己的思考记录下来,供以后开发时参考,相信对其他人也有用。

    二次封装组件

    PM的需求无奇不有,所以很多时候,我们使用的组件满足不了PM的需求,怎么办呢?比如,组件需要传入一个数组,但是我们必须传2个变量;或者我们需要在组件focus的时候调用一个方法,但是组件并没有暴露focus事件等等。虽然都是些很简单的需求,但是组件就是没有暴露实现这些需求的方法。咋办?

    方法是对组件进行二次封装

    二次封装主要运用了vue的如下属性:

    • vm.attrs:包含了父作用域中不作为prop被识别(且获取)的特性绑定(class和style除外)。当一个组件没有声明任何prop时,这里会包含所有父作用域的绑定(class和style除外),并且可以通过v−bind="attrs:包含了父作用域中不作为prop被识别(且获取)的特性绑定(class和style除外)。当一个组件没有声明任何prop时,这里会包含所有父作用域的绑定(class和style除外),并且可以通过v−bind="attrs" 传入内部组件。

    • vm.$props: 当前组件接收到的 props 对象。

    • vm.listeners:包含了父作用域中的(不含.native修饰器的)v−on事件监听器。它可以通过v−on="listeners:包含了父作用域中的(不含.native修饰器的)v−on事件监听器。它可以通过v−on="listeners" 传入内部组件。

    props可以拿到传给当前组件的所有props,props可以拿到传给当前组件的所有props,attrs可以拿到传给组件的所有非props的属性,$listeners可以拿到所有传给组件的事件监听器

    例子

    举个例子,比如说el-upload需要传一个数组,但是我们只能传2个变量;并且,我们需要在el-upload上传success的时候做点其它的事。封装的代码如下:

    export default {
      name: 'YmUpload',
      props: {
        name: {
          type: String,
          default: '',
        },
        url: {
          type: String,
          default: '',
        },
        onSuccess: {
          type: Function,
          default: () => 1,
        },
      },
      data() {
        return {
          fileList: [],
        };
      },
      watch: {
        url() {
          this.init();
        },
      },
      computed: {
        uploadAttr() {
          return {
            ...this.$attrs,
            fileList: this.fileList,
            onSuccess: this.handleSuccess,
          };
        },
      },
      created() {
        this.init();
      },
      methods: {
        init() {
          // 组件初始化
          const payload = {
            name: this.name || this.url,
            url: this.url,
          };
          this.fileList = [payload];
        },
        handleSuccess(res, file, fileList) {
          // 做点其它的事
        },
      },
    };


    设计模式(4): 给组件实现单独的store 

    概述

    最近最近做项目的时候总会思考一些大的应用设计模式相关的问题,我把自己的思考记录下来,供以后开发时参考,相信对其他人也有用。

    组件自身的store

    我们在开发组件的时候,时常都有这种需求,就是希望给组件一个独立的store,这个store可能被用来储存数据,共享数据,还可以被用来对数据做一些处理,抽离核心代码等。

    store的数据不共享

    如果组件自身的store是每个实例独自拥有的并且不共享的话,我们可以直接用一个类来实现。

    // store.js
    export default class Store {
      constructor(data, config) {
        this.config = config;
        this.init(data);
      }
    
      init(data) {
        // 对数据做处理
      }
    
      // 其它方法
    }

    然后我们在组件中实例化这个store,然后挂载到data属性里面去:

    <script>
    import Store from './store';
    
    export default {
      data() {
        return {
          store: [],
        };
      },
      methods: {
        initStore() {
          // 生成 options 和 config
          this.store = new Store(options, config);
        },
      },
    };
    </script>

    store的数据需要共享 

    如果store的数据需要共享,我们建议用动态挂载vuex的store的方法,示例如下: 

    // store.js
    const state = {
      data: [],
    };
    
    const getters = {};
    
    const actions = {};
    
    const mutations = {
      setData(state, value) {
        this.state.data = [...value];
      },
    };
    
    export default {
      state,
      getters,
      actions,
      mutations,
    };

    然后我们在注册这个组件的时候动态挂载这个store:

    import Store from './store';
    
    export default {
      install(Vue, options) {
        Vue.store.registerModule('xxx', store);
      },
    };

    最后我们就可以在组件中使用这个store的数据啦~~~ 


     设计模式(5): vue 不监听绑定的变量

    概述

    最近最近做项目的时候总会思考一些大的应用设计模式相关的问题,我把自己的思考记录下来,供以后开发时参考,相信对其他人也有用。

    绑定变量

    一般情况下,如果我们需要在组件中使用某个变量,会这么使用:

    data() {
      return {
        myData: [],
      };
    }

    如果这个变量是外部变量,例如从外部文件引入的话,就会这么使用:

    import { provinces } from '@/util/consts';
    
    export default {
      data() {
        return {
          myData: provices,
        };
      },
    }

    问题 

    但是如果这个变量是一个嵌套层级很深,数据量很大的对象的话,如果按照上面那样使用,vue 就会去遍历这个变量的所有属性,来监听这个变量的变化。非常的消耗性能,一个典型的例子是:

    export default {
      data() {
        return {
          bannerBg: null,
        };
      },
      mounted() {
        this.loadScript('/js/three.min.js', () => {
          this.loadScript('/js/vanta.net.min.js', () => {
            this.bannerBg = window.VANTA.NET({
              el: '#bannerBg',
              color: 0x2197F3,
              backgroundColor: 0x071E31,
            });
          });
        });
      },
      beforeDestroy() {
        this.bannerBg.destroy();
      },
      methods: {
        loadScript(path, callback) {
          const script = document.createElement('script');
          script.src = path;
          script.language = 'JavaScript';
          script.onload = () => callback();
          document.body.appendChild(script);
        },
      },
    }

    上面的例子中,我们为了避免内存泄漏,在 beforeDestroy 生命周期里面进行回收,而为了获取回收的变量,我们把它绑定给了 this.bannerBg。

    但是事实是,我们并不需要监听 this.bannerBg 这个变量,而这么绑定的结果是,这个 vue 组件在 mounted 的时候需要遍历 this.bannerBg 来增加 vue 的监听属性,非常消耗性能

    解决方案

    所以,我们建议不把 bannerBg 放到 data() 里面去监听,而是**直接绑定给 this **就行了。优化后的代码如下:

    export default {
      mounted() {
        this.loadScript('/js/three.min.js', () => {
          this.loadScript('/js/vanta.net.min.js', () => {
            this.bannerBg = window.VANTA.NET({
              el: '#bannerBg',
              color: 0x2197F3,
              backgroundColor: 0x071E31,
            });
          });
        });
      },
      beforeDestroy() {
        this.bannerBg.destroy();
      },
      methods: {
        loadScript(path, callback) {
          const script = document.createElement('script');
          script.src = path;
          script.language = 'JavaScript';
          script.onload = () => callback();
          document.body.appendChild(script);
        },
      },
    }

    如果这个变量不是过程中生成的,而是初始化的时候生成的,我们建议在 data() 方法里面这么做:

    import { provinces } from '@/util/consts';
    
    export default {
      data() {
        this.myData = provices;
    
        return {
          // 移到上面去了
          // myData: provices,
        };
      },
    }


    设计模式(6): 数据抽象与业务封装 

    概述

    最近最近做项目的时候总会思考一些大的应用设计模式相关的问题,我把自己的思考记录下来,供以后开发时参考,相信对其他人也有用。

    情景描述

    我们在做项目的时候,经常会碰到各种各样的业务情景,然后为了实现这些需求,就不断地在 vue 单文件组件里面加代码来实现,最终业务越来越多单文件组件越来越大,非常难以维护。

    解决方案

    我们都知道,vue 是通过数据来处理视图的,所以很多业务可以抽象成只处理数据,然后这些业务可以再抽象成 class 来进行业务封装。

    event-bus

    举个例子来说,vuex 或者 redux 这些状态管理的库,就是用的这个思想,把数据层脱离出去,带来的好处是简化了组件之间的数据流动。它们的源码有些复杂,我们以 event-bus 来举例说明。

    首先,我们可以自己实现一个 bus 类,这个类能够储存数据,还能够进行事件的分发与监听

    import Vue from 'vue';
    import Bus from 'xxxx';
    
    Vue.prototype.$bus = new Bus();

    然后,分别在组件 A 和 B 里面,我们可以监听事件和分发事件。 

    // 组件A -- 监听事件
    created() {
      this.$bus.on('xxxx', this.xxx);
    },
    beforeDestroy() {
      this.$bus.off('xxxx', this.xxx);
    },
    
    // 组件B -- 分发事件
    methods: {
      xxxx() {
        this.$bus.emit('xxxx', this.xxx);
      }
    }

    这样,即使处于不同层级,组件 A 和 B 也能流畅的进行数据交互。

    抽象方法

    我们抽象一下实现方法,我们先把业务抽象为数据和对数据的操作,然后在组件之外实现一个 class,最后用这个 class 进行保存数据和业务处理

    上面这个例子把这个 class 放在了 Vue 实例上面,可能没有那么明显,下面举一个把它放在单文件组件里面的例子。

    cascader

    这一段参考了 element-cascader 的实现。

    比如说,我们要自己实现一个 cascader,要怎么做?

    我们上面提到过,我们对 cascader 的操作其实就是对数据的操作,所以我们可以把整个数据抽象出来,然后给它加上选中的业务功能:

    import { capitalize } from '@/utils/util';
    
    export default class Node {
      constructor(data, parentNode) {
        this.parent = parentNode || null;
    
        this.initState(data);
        this.initChildren(data);
      }
    
      initState(data) {
        // 加上本身的属性
        for (let key in data) {
          if (key !== 'children') {
            this[key] = data[key];
          }
        }
    
        // 自定义属性
        this.isChecked = false;
        this.indeterminate = false;
    
        // 用于自动取消
        this.isCheckedCached = false;
        this.indeterminateCached = false;
      }
    
      initChildren(data) {
        this.children = (data.children || []).map(child => new Node(child, this));
      }
    
      setCheckState(isChecked) {
        const totalNum = this.children.length;
        const checkedNum = this.children.reduce((c, p) => {
          const num = p.isChecked ? 1 : (p.indeterminate ? 0.5 : 0);
          return c + num;
        }, 0);
    
        this.isChecked = isChecked;
        this.indeterminate = checkedNum !== totalNum && checkedNum > 0;
      }
    
      doCheck(isChecked) {
        this.broadcast('check', isChecked);
        this.setCheckState(isChecked);
        this.emit('check', isChecked);
      }
    
      broadcast(event, ...args) {
        const handlerName = `onParent${capitalize(event)}`;
    
        this.children.forEach(child => {
          if (child) {
            child.broadcast(event, ...args);
            child[handlerName] && child[handlerName](...args);
          }
        });
      }
    
      emit(event, ...args) {
        const { parent } = this;
        const handlerName = `onChild${capitalize(event)}`;
    
        if (parent) {
          parent[handlerName] && parent[handlerName](...args);
          parent.emit(event, ...args);
        }
      }
    
      onParentCheck(isChecked) {
        if (!this.disabled) {
          this.setCheckState(isChecked);
        }
      }
    
      onChildCheck() {
        const validChildren = this.children.filter(child => !child.disabled);
        const isChecked = validChildren.length
          ? validChildren.every(child => child.isChecked)
          : false;
    
        this.setCheckState(isChecked);
      }
    }

    上面实现的 class 封装了如下业务:

    1. 通过 initState 加入了各种自定义的状态,这个状态有了业务:选中状态,半选中状态和未选中状态
    2. 通过 setCheckState 实现了 点击 的业务。
    3. 通过 broadcast 和 emit 实现了 父子组件联动 的业务。

    当然,实际情形可能比这个更加复杂,我们只需要在上面的代码中加入各种状态和处理方法即可。

    更进一步

    上面封装的底层的业务,再高一层,我们可能有 搜索、自动选中 等业务,这个时候要怎么办呢?

    方法是在 Node 类和单文件组件之间再封装一层,来实现这些业务,示例代码如下:

    export default class Store {
      constructor(data) {
        this.nodes = data.map(nodeData => new Node(nodeData));
      }
    
      // 自动选中
      autoSelect(query, label) {
    
      }
    
      // 搜索
      search(searchString) {
    
      }
    }

    然后我们可以在单文件组件里面直接使用它

    data() {
      return {
        store: null;
      };
    },
    watch: {
      data(newVal) {
        this.store = new Store(newVal);
      }
    },

     推荐阅读:

    学习Web前端 自学宝典

    【uni-app】uniapp项目优化方式及建议

    前端跨域设置 withCredentials: true

    展开全文
  • vue 策略模式的应用; vue filters的使用

    策略模式(Strategy )是属于设计模式中 对象行为型模式, 主要是定义一系列的算法 , 把这些算法一个个封装成单独的类 .
    实际应用:【替换if-else,switch】

    普通if-else

    var performanceS = function( salary ){
        //...
        return salary * 4;
    };
    var performanceA = function( salary ){
        //...
        return salary * 3;
    };
    var performanceB = function( salary ){
        //...
        return salary * 2;
    };
    var calculateBonus = function( performanceLevel, salary ){
        if ( performanceLevel === 'S' ){
            return performanceS( salary );
        }
        if ( performanceLevel === 'A' ){
            return performanceA( salary );
        }
        if ( performanceLevel === 'B' ){
            return performanceB( salary );
        }
    };
    calculateBonus( 'A' , 10000 ); // 输出:30000

    js策略模式写法

    var strategies = {
        "S": function( salary ){
            return salary * 4;
        },
        "A": function( salary ){
            return salary * 3;
        },
        "B": function( salary ){
            return salary * 2;
        }
    };
    
    var calculateBonus = function( level, salary ){
        return strategies[ level ]( salary );
    };
    
    console.log( calculateBonus( 'A', 10000 ) ); // 输出:30000

    vue写法:

    # template部分
    <template>
      <div>{{ salary | calculateBonus }}<div> <!-- 一个参数 -->
      <div>{{ level  | calculateBonus(salary) }}<div> <!-- 2个参数 -->
    </template>
    
    
    //官网:https://cn.vuejs.org/v2/guide/filters.html
    
    <!--
     1.filters 用在双花括号插值和 v-bind 表达式:
      <div>{{ salary | calculateBonus }}<div>
      <div v-bind:id="rawId | formatId"></div>
     -->
    
    <!-- 2. filters 管道符‘|’ 前面的为第一个参数,多个参数的使用函数方式传入-->
    
    <!-- 3. 多个filters依次执行
        message | filterA | filterB  
        # message 传入filterA;
        # filterA 结果 传入filterB
      -->
    
    
    export default {
      name: "App",
      data(){
        return {
            level:'A',
            salary: 10000
        }
      },
      filters:{
        calculateBonus(level, salary) {
          const strategies = {
            "S": function( salary ){
                  return salary * 4;
                 },
            "A": function( salary ){
                  return salary * 3;
                 },
            "B": function( salary ){
                  return salary * 2;
                 }
           };
          return strategies[ level ]( salary );
        },
      }, 
      methods:{}
    }

    参考:JS设计模式 之 策略模式 - 雨中愚 - 博客园icon-default.png?t=M0H8https://www.cnblogs.com/yuzhongyu/p/14203862.html

    展开全文
  • vue设计模式_笔记

    2021-09-28 20:00:04
    Vue设计模式响应式原理认识Object.prototype.constructor对象的构造函数改变constructorget、setObject.defineProperty()MVVM设计模式(响应式原理) 响应式原理 认识Object.prototype.constructor 返回创建实例...

    响应式原理

    认识Object.prototype.constructor

    返回创建实例对象的 Object 构造函数的引用。注意,此属性的值是对函数本身的引用,而不是一个包含函数名称的字符串。对原始类型来说,如1,true和"test",该值只可读。

    var o = {};
    o.constructor === Object; // true
    
    var o = new Object;
    o.constructor === Object; // true
    
    var a = [];
    a.constructor === Array; // true
    
    var a = new Array;
    a.constructor === Array // true
    
    var n = new Number(3);
    n.constructor === Number; // true
    

    在这里插入图片描述

    对象的构造函数
    function Tree(name) {
       this.name = name;
    }
    
    var theTree = new Tree("Redwood");
    console.log( "theTree.constructor is " + theTree.constructor );
    

    在这里插入图片描述

    改变constructor

    obj instanceof Object 检测Object.prototype是否存在于参数obj的原型链上。

    function Type() {};
    var types = [
        new Array, [],
        new Boolean,
        true, // remains unchanged
        new Date,
        new Error,
        new Function,
        function() {},
        Math,
        new Number,
        1, // remains unchanged
        new Object,
        {},
        new RegExp,
        /(?:)/,
        new String,
        "test" // remains unchanged
    ];
    
    for (var i = 0; i < types.length; i++) {
        types[i].constructor = Type;
        types[i] = [types[i].constructor, types[i] instanceof Type, types[i].toString()];
    };
    
    console.log(types.join("\n"));
    

    输出:
    在这里插入图片描述

    get、set

    obj.__defineGetter__(prop, func)
    

    prop
    一个字符串,表示指定的属性名。
    func
    一个函数,当 prop 属性的值被读取时自动被调用。

    // 请注意,该方法是非标准的:
    
    var o = {};
    o.__defineGetter__('gimmeFive', function() { return 5; });
    console.log(o.gimmeFive); // 5
    
    
    // 请尽可能使用下面的两种推荐方式来代替:
    
    // 1. 在对象字面量中使用 get 语法
    var o = { get gimmeFive() { return 5; } };
    console.log(o.gimmeFive); // 5
    
    // 2. 使用 Object.defineProperty 方法
    var o = {};
    Object.defineProperty(o, 'gimmeFive', {
      get: function() {
        return 5;
      }
    });
    console.log(o.gimmeFive); // 5
    
    obj.__defineSetter__(prop, fun)
    

    prop
    一个字符串,表示指定的属性名。
    fun
    一个函数,当试图去为 sprop 属性赋值时被调用。通常你要给这个函数指定一个参数:

    Object.defineProperty()

    JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。Object.defineProperty 是 ES5 中一个无法 shim 的特性。 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 能够追踪依赖,在 property 被访问和修改时通知变更。
    每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。
    在这里插入图片描述

    MVVM设计模式(响应式原理)

    View和Model之间并没有直接的联系,而是通过ViewMode进行交互,ViewModel通过双向数据绑定把View层和Model层连接了起来

    const data = {};
    let name = "张三";
    
    Object.defineProperty(data, 'name', {
        get: function() {
            console.log('触发get')
            return name
        },
        set: function(newVal) {
            console.log('触发set')
            name = newVal
        }
    })
    
    //测试
    console.log(data.name) // 触发get  张三
    data.name = '李四' // 触发set
    

    在这里插入图片描述

    展开全文
  • 用到的面向对象设计的基本原则 OCP(开闭原则,Open-Closed Principle):一个软件的实体应当对扩展开放,对修改关闭。 DIP(依赖倒转原则,Dependence Inversion Principle):要针对接口编程,不要针对实现编程...
  • 目录 vue的基本介绍 什么是vue? 为什么使用vue?...mvc设计模式 MVVM设计模式 vue的基本介绍 什么是vue? 1.渐进式javascript框架 2.作者:尤雨溪 个人开发 3.Vue (读音 /vjuː/,类似于 ..
  • 1、Vue源码分析 2、手把手教 保姆级 撸代码 3、无惧面试,学以致用,继承创新 4、谈谈前端发展与学习心得 5、手写源码技术栈,附上详细注释 6、从源码中学习设计模式,一举两得 7、编程思想的提升及代码质量的提高 8...
  • vue源码看观察者模式

    千次阅读 2018-01-30 00:42:56
    摘要:源码解读设计模式系列文章将陆陆续续进行更新中 ~ 观察者模式 首先话题下来,我们得反问一下自己,什么是观察者模式? 概念 观察者模式(Observer):通常又被称作为发布-订阅者模式。它定义了一种一...
  • 设计模式vue中的应用(一) 设计模式vue中的应用(二) 设计模式vue中的应用(三) 设计模式vue中的应用(四) 设计模式vue中的应用(五) 设计模式vue中的应用(六) 为什么要写这些文章呢。正如设计...
  • 项目描述:自己实现的vue源码(自我实现的vue源代码)目前v-model命令,响应式对象,副本,双向绑定已经完成 基本原理:非数组使用object.defineProperty设置获取和设置监听,在数组原型对象中extend7种变量方法来...
  • Vue - MVVM设计模式

    2021-06-24 16:12:48
    转变思维, 用数据驱动视图改变, 操作dom的事, vue源码内干了 设计模式: 是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。 MVVM,一种软件架构模式,决定了写代码的思想和层次 M: ...
  • Vue源码的整体架构无非是初始化Vue对象,挂载数据data/props等,在不同的时期触发不同的事件钩子,如created() / mounted() / update()等,后面专门整理各个模块的文章。这里先讲双向数据绑定的部分,也是最主要的...
  • VUE源码相关面试题汇总

    千次阅读 多人点赞 2020-07-12 10:52:41
    Q1: vue3今年发布了,请你说一下他们之间在相应式的实现上有什么区别? A: vue2采用的是defineProperty去定义get,set,而vue3改用了proxy。...请你说一下vue设计架构。 A: vue2采用的是典型的混入式架构,类似于exp
  • vue 源码目录设计简述

    2022-04-25 18:08:57
    学习vue.js的源码,首先要学习源码目录,vue.js的源码都在src目录下,目录结构如下: src ├── compiler # 编译相关 ├── core # 核心代码 ├── platforms # 不同平台的支持 ├── server # 服务端渲染 ├...
  • Vue源码解读(个人见解 + 网友理解)

    千次阅读 2020-11-30 16:13:07
    【个人总结】Vue源码解读 文章目录【个人总结】Vue源码解读前言目录解说Vue的实例Mixin的解说Vue生命周期钩子Vue响应式原理数据劫持收集依赖与派发更新虚拟DOM 前言 作为一名前端开发攻城狮来讲,我觉得学习源码是...
  • vue 源码详解(一): 生成 Vue 实例前的准备工作 1. 从 new Vue() 开始 vue/src/core/index.js : import Vue from './instance/index' // 1. 引入 Vue 构造函数 import { initGlobalAPI } from './global-api/index...
  • vue已是目前国内前端web端三分天下之一,同时也作为本人主要技术栈之一,在日常使用中知其然也好奇着所以然,另外最近的社区涌现了一大票vue源码阅读类的文章,在下借这个机会从大家的文章和讨论中汲取了一些营养,...
  • 源码这个东西对于实际的工作其实没有立竿见影的效果,不会像那些针对性极强的文章一样看了之后就立马可以运用到实际项目中,产生什么样的效果,源码的作用是一个潜移默化的过程,它的理念、设计模式、代码结构等看了...
  • 写在前面前端时间给大家分享了一篇开源文档 《 Vue 源码分析与讲解 》,详情请戳:[ 资料分享 ] Vue 源码分析与讲解 - 附下载地址这段时间我也是把文档和配套的课程学习了一遍,对Vue的原理有了更深层次的理解,由于...
  • 手写Vue源码,实现核心技术,麻雀虽小五脏俱全:”响应式原理,模板编译,依赖收集”算法:Diff算法实现”设计模式:发布-订阅模式,装饰者模式,代理模式”数据结构:AST树,vnode,vDom 文件名,方法名称,变量得...
  • vue源码一刷

    2022-01-29 16:18:26
    vue源码一刷 前言 ​ 2022年一月二十四日,一个平平常常的日子,细蒙蒙的雨丝夹着一星半点的雪花,正纷纷淋淋地向大地飘洒着。时令已快到小年,厚厚的雪层铺落在地面,然而在熙熙攘攘的大街上,雪花往往还没等落地,...
  • 大前端-Vue源码分析

    2020-07-12 22:56:53
    Vue源码解析-响应式原理 笔记将会对以下三点进行总结 Vue.js 的静态成员和实例成员初始过程 首次渲染的过程 数据响应式原理 一. 准备工作 Vue源码的获取 项目地址 Vue源码获取 Fork 一份到自己的仓库,克隆到本地,...
  • Vue 源码学习

    千次阅读 2019-05-02 11:27:58
    Vue 源码学习 资料 熟悉设计模式 AST 抽象语法树 https://www.cnblogs.com/ztfjs/p/vue2.html (未完,待续)
  • 前言:好久没写博客了,今年确实有点小忙,学习跟工作都很充实,身边也有小伙伴也经常说:技术是学不完的,能不能专一搞点自己喜欢的东西呢?学完新技术然后过几年又没...前面有写过两篇vue源码的文章,有兴趣的童...
  • 1. 观察者模式 概念 观察者模式又被称为 发布-订阅 模式,这种模式定义了对象间的一种一对多的依赖关系。当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并完成自动更新。 优点 观察者与被观察...
  • vue2源码解读笔记(一)

    2022-01-19 15:54:27
    目的:读懂vue部分源码,体验vue设计方式,弥补源码涉及到但本人不具备的知识 一 源码下载,调试见歹佬原文:https://juejin.cn/post/6949370458793836580 (可以直接看歹佬的原文,不止技术厉害,文章也写得好。...
  • vue源码解析(二)

    2021-02-25 16:29:25
    vue源码解析Virtual DOMcreateElementupdate组件化createComponentpatch合并配置 Virtual DOM Virtual DOM 这个概念相信大部分人都不会陌生,它产生的前提是浏览器中的 DOM 是很“昂贵"的,为了更直观的感受,可以...
  • 由于项目使用的技术栈是vue +elementUI,所以加校验规则也容易实现。但是如果每个单文件组件里面都要写一遍校验规则,重复率高,工作量大。于是就思考能不能把校验规则抽取出来,统一维护。看到网上也有相关的做法,...
  • vue2.0源码学习资源

    2020-12-09 10:10:54
    vue2.0源码学习资源,深入分析vue设计原理,ast对象,编译原理,观察者模式,双向绑定及相关的实例等

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 12,641
精华内容 5,056
关键字:

vue源码设计模式