Vue计算属性原理
1.计算属性的初始化12345678910111213function initComputed(vm) { const computed = vm.$options.computed; const watchers = (vm._computedWatchers = {}); //用来存放计算watcher for (let k in computed) { const userDef = computed[k]; //获取用户定义的计算属性 const getter = typeof userDef === "function" ? userDef : userDef.get; //创建计算属性watcher使用 // 创建计算watcher lazy设置为true watchers[k] = new Watcher(vm, getter, () => {}, { lazy: true }); defineComputed(vm, k, u ...
Vue监听属性原理
1.监听属性的初始化12345678910111213141516171819202122232425262728// 初始化watchfunction initWatch(vm) { let watch = vm.$options.watch; for (let k in watch) { const handler = watch[k]; //用户自定义watch的写法可能是数组 对象 函数 字符串 if (Array.isArray(handler)) { // 如果是数组就遍历进行创建 handler.forEach((handle) => { createWatcher(vm, k, handle); }); } else { createWatcher(vm, k, handler); } }}// 创建watcher的核心function createWatcher(vm, exprO ...
Vue模板组件原理
123456789101112131415161718192021222324252627// 全局组件 Vue.component("parent-component", { template: `<div>我是全局组件</div>`, }); // Vue实例化 let vm = new Vue({ el: "#app", data() { return { aa: 1, }; }, // render(h) { // return h('div',{id:'a'},'hello') // }, template: `<div id="a"> hello 这是我自己写的Vue{{aa} ...
Vue的mixin原理
1.定义全局Mixin函数12345678910import {mergeOptions} from '../util/index'export default function initMixin(Vue){ Vue.mixin = function (mixin) { // 合并对象 this.options=mergeOptions(this.options,mixin) };}};initMixin(Vue);//在Vue的入口文件引入initMixin方法
2.mergeOptions方法1234567891011121314151617181920212223242526//先定义生命周期,合并生命周期,然后为生命周期队列添加合并方法//mixin核心方法export function mergeOptions(parent, child) { const options = {}; // 遍历父亲 for (let k i ...
Vue的Diff算法原理
1.patch核心渲染方法改写1const isRealElement = oldVnode.nodeType;//oldVnode是真实DOM代表初次渲染
当oldNode为真实DOM即初次渲染,反之为虚拟DOM,更新过程需要使用diff算法,算法中,判断新旧标签是否一致,同级比较,不一致则新替换旧。如果旧节点为文本节点,直接替换标签文本内容。如果不符合以上两种情况,直接把旧的虚拟DOM对应的真实DOM赋值给新的虚拟DOM的EL属性。其中包括子节点判断。diff算法主要进行同级比较!2.updateProperties更新属性1234567891011121314151617181920const newProps = vnode.data || {};//新的vnode属性const el = vnode.el;//真实节点for (const k in oldProps) { //新节点没有,则把老节点属性移除 if (!newProps[k]) { el.removeAttribute(k); } ...
Vue异步更新原理
1.watcher更新的改写1234567891011export default class Watcher { update() { // 每次watcher进行更新的时候 是否可以让他们先缓存起来 之后再一起调用 // 异步队列机制 queueWatcher(this); } run() { // 真正的触发更新 this.get(); }}
在update更新方法中加入异步队列的机制
2.queueWatcher实现队列机制123456789101112131415161718192021222324let queue = [];let has = {};function flushSchedulerQueue() { for (let index = 0; index < queue.length; index++) { // 调用watcher的run方法 执行真正的更新操作 queue[index].run ...
Vue响应式更新原理
1.定义Watcher123456789101112131415161718192021// 全局变量id 每次new Watcher都会自增let id = 0;export default class Watcher { constructor(vm, exprOrFn, cb, options) { this.vm = vm; this.exprOrFn = exprOrFn; this.cb = cb; //回调函数 比如在watcher更新之前可以执行beforeUpdate方法 this.options = options; //额外的选项 true代表渲染watcher this.id = id++; // watcher的唯一标识 // 如果表达式是一个函数 if (typeof exprOrFn === "function") { this.getter = exprOrFn; } // 实例化就会默认调用get方法 this.get( ...
虚拟DOM渲染原理
1.组件挂载入口通过compile转化成render之后,调用mountComponent核心方法进行组件实例的挂载。该函数与生命周期相关,位于beforeMount和mounted生命周期钩子之间。
2.核心方法mountComponent12345678910export function mountComponent(vm, el) { // 上一步模板编译解析生成了render函数 // 下一步就是执行vm._render()方法 调用生成的render函数 生成虚拟dom // 最后使用vm._update()方法把虚拟dom渲染到页面 // 真实的el选项赋值给实例的$el属性 为之后虚拟dom产生的新的dom替换老的dom做铺垫 vm.$el = el; // _update和._render方法都是挂载在Vue原型的方法 类似_init vm._update(vm._render());}
核心方法mountComponent中主要调用vm._render和vm._updatte函数进行实例挂载
3.render函数转化成虚拟DO ...
vue模板编译原理
1.模板编译入口在初始化时,传入的options参数中有el属性,则进行模板渲染。其中主要的渲染函数为$mount()。//将模板挂载在HTML上进行render判断,template判断,什么情况下,最终都是将模板转换为render函数进行渲染。12345678910111213141516171819// 如果有el属性 进行模板渲染 if (vm.$options.el) { vm.$mount(vm.$options.el); }if (!options.render) { // 如果存在template属性 let template = options.template; if (!template && el) { // 如果不存在render和template 但是存在el属性 直接将模板赋值到el所在的外层html结构(就是el本身 并不是父元素) template = el.outerHTML; ...
防抖与节流
防抖含义合并多次函数为一个
思路将目标方法包装在setTimeout里面,然后该方法是一个事件的回调函数,如果一直执行该回调,则通过clearTimeout将连续动作给删除,待不触发该事件时,setTimeout就会执行这个方法
使用场景页面resize需要做页面适配,需要根据页面最终情况做DOM渲染。或者input输入框在进行校验的情况下,只校验输入最后的结果。
简易实现123456789101112131415/** fn [function] 需要防抖的函数* delay [number] 毫秒,防抖期限值*/function debounce(fn,delay){ let timer = null //借助闭包 return function() { if(timer){ clearTimeout(timer) //进入该分支语句,说明当前正在一个计时过程中,并且又触发了相同事件。所以要取消当前的计时,重新开始计时 timer = setTimeout(fn,delay) ...