智能家居
js进度条(一文带你搞懂Vue3 底层源码)

作者:妹红大大

转发链接:https://mp.weixin.qq.com/s/D_PRIMAD6i225Pn-a_lzPA

前言

最容易搭建 vue3 的方式就是使用作者的 vite《ntent="mp" href="https://www.toutiao.com/i6832604684847415811/?group_id=6832604684847415811" rel="noopener noreferrer" target="_blank">一个由 Vue 作者尤雨溪开发的 web 开发工具—vite

也可以通过 yarn 安装

  $ yarn create vite-app <project-name>  $ cd <project-name>  $ yarn  $ yarn dev

异常1:本菜翻阅了 vite 的 issue,然后 google + baidu 一无所获, 最后发现是因为本菜 node 版本为 13.5.0导致的(版本过高),

最后的解决方式是:本菜通过 nvm 将 node 版本切换到 12.12.0,至于 nvm 没使用过的童鞋们可以去尝试下哦。特别好用

vite 原理解析

那么就可以通过通过拦截路由 / 和 .js 结尾的请求。然后通过 node 去加载对应的 .js 文件

    const fs = require('fs')    const path = require('path')    const Koa = require('koa')    const app = new Koa()    app.use(async ctx=>{        const {request:{url} } = ctx        // 首页        if(url=='/'){n            ctx.type="text/html"            ctx.body = fs.readFileSync('./index.html','utf-8')        }else if(url.endsWith('.js')){            // js文件            const p = path.resolve(__dirname,url.slice(1))            ctx.type = 'application/javascript'            const content = fs.readFileSync(p,'utf-8')            ctx.body = content        }    })    app.listen(3001, ()=>{        console.log('听我口令,3001端口,起~~')    })

但是遇到第三方库以上代码就会找不到 .js 文件的位置了,此时 vite 会用 es-module-lexer 把文件解析成 ast,拿到 import 的地址。

如果是第三方库就去 node_modules 中查找,vite 中通过在第三方库中添加前缀 /@modules/,然后发现了 /@modules/ 后走 第三方库逻辑

    if(url.startsWith('/@modules/')){        // 这是一个node_module里的东西        const prefix = path.resolve(__dirname,'node_modules',url.replace('/@modules/',''))        const module = require(prefix+'/package.json').module        const p = path.resolve(prefix,module)        const ret = fs.readFileSync(p,'utf-8')        ctx.type = 'application/javascript'        ctx.body = rewriteimport(ret)    }

首先 xx.vue 返回的格式大概是这样的

const __script = {    setup() {        ...    }}import {render as __render} from "/src/App.vue?type=template&t=1592389791757"__script.render = __renderexport default __script

解析 .css 就更加简单了。通过 document.createElement('style')然后再注入就好了

reactive

作为 vue2 的使用者最想知道的肯定是 vue3 的数据劫持和双向绑定了。在 vue3中,双向绑定和可选项,如果需要使用双向绑定的需要通过 reactive方法进万数据劫持。

现在开始写一个简单的 vue

  <template>    <div>      <div>{{ count }}</div>      <button @click="increment">count++</button>    </div>  </template>  <script>    import { reactive } from 'vue'    export default {      setup() {        let count = reactive({          num: 0        })        const increment = () => count.num++        return {          count,          increment        }      }    }  </script>

现在开始解读 reactive 源码。

上面的 __v_isReadonly 其实是一个 typescript 的枚举值

export const enum ReactiveFlags {  skip = '__v_skip',  isReactive = '__v_isReactive',  isReadonly = '__v_isReadonly',  raw = '__v_raw',  reactive = '__v_reactive',  readonly = '__v_readonly'}

然后进入 createReactiveObject 在 649 行,意思就是:「创建响应式对象」

function createReactiveObject(target, isReadonly, baseHandlers, collectionHandlers) {    // 略...        // 如果target已经代理了, 返回target    if (target.__v_raw && !(isReadonly && target.__v_isReactive)) {        return target;    }    // target already has corresponding Proxy    if (hasOwn(target, isReadonly ? "__v_readonly"  : "__v_reactive" )) {        return isReadonly ? target.__v_readonly : target.__v_reactive;    }        if (!canObserve(target)) {        return target;    }    // 重点...    // collectionHandlers:对引用类型的劫持,    // baseHandlers: 对进行基本类型的劫持    const observed = new Proxy(target, collectionTypes.has(target.constructor) ? collectionHandlers : baseHandlers);    def(target, isReadonly ? "__v_readonly"  : "__v_reactive" , observed);    return observed;}

实现劫持的主要方法是通过 Proxy 方法,(Proxy 使用可以看看阮老师的博客),顺藤摸瓜找到 mutableHandlers 定义的地方。在 338 行

const mutableHandlers = {    get,    set,    deleteProperty,    has,    ownKeys};// 229行const get =  createGetter();// 251 行function createGetter(isReadonly = false, shallow = false) {    return function get(target, key, receiver) {        // 一些 __v_isReactive、__v_isReadonly、__v_raw的处理        // 略...        // 数组操作        const targetIsArray = isArray(target);        if (targetIsArray && hasOwn(arrayInstrumentations, key)) {            return Reflect.get(arrayInstrumentations, key, receiver);        }        // 非数组        const res = Reflect.get(target, key, receiver);                // 其他 调用 track 返回 res 的情况        // 略...                // 如果可写,那么会调用 track        !isReadonly && track(target, "get" , key);        // 如果是对象呢。那么递归        return isObject(res)            ? isReadonly                ? // need to lazy access readonly and reactive here to avoid                    // circular dependency                    readonly(res)                : reactive(res)            : res;    };}

get 指向了方法 createGetter, 「创建 get 劫持」

那么数组的 arrayInstrumentations 是什么呢?我们来到源码的 第 234 行。

const arrayInstrumentations = {};['includes', 'indexOf', 'lastIndexOf'].forEach(key => {    arrayInstrumentations[key] = function (...args) {        //         const arr = toRaw(this);        for (let i = 0, l = this.length; i < l; i++) {            track(arr, "get" , i + '');        }        // we run the method using the original args first (which may be reactive)        // 我们首先 以原始args 运行该方法(可能是反应性的)        const res = arr[key](...args);        if (res === -1 || res === false) {            // if that didn't work, run it again using raw values.            // 如果那不起作用,则使用原始值再次运行它。            return arr[key](...args.map(toRaw));        }        else {            return res;        }    };});

arrayInstrumentations 中还是调用了 track 方法,那么 track 方法就更加神秘了。来看看它的源码吧?源码在 126 行

function track(target, type, key) {    if (!shouldTrack || activeEffect === undefined) {        return;    }    let depsMap = targetMap.get(target);    if (!depsMap) {        targetMap.set(target, (depsMap = new Map()));    }    let dep = depsMap.get(key);    if (!dep) {        depsMap.set(key, (dep = new Set()));    }    if (!dep.has(activeEffect)) {        dep.add(activeEffect);        activeEffect.deps.push(dep);        if ( activeEffect.options.onTrack) {            activeEffect.options.onTrack({                effect: activeEffect,                target,                type,                key            });        }    }}

在不考虑 activeEffect 的情况下。track 所做的事情就是

  1. 创建包含自身的 map
  2. 将 activeEffect 赛道 map 中
  3. 触发 onTrack

createReactiveEffect 是在 effect 中被调用的

先开始讲述 trigget 相关的代码(核心哦)

function trigger(target, type, key, extraInfo) {    const depsMap = targetMap.get(target);        // 略...    const effects = new Set();    const computedRunners = new Set();    if (type === "clear" ) {      // collection being cleared, trigger all effects for target      depsMap.forEach(dep => {        addRunners(effects, computedRunners, dep);      });    }    // 略...     const run = (effect) => {      scheduleRun(effect, target, type, key, extraInfo);    };        computedRunners.forEach(run);    effects.forEach(run);  }

在源码 3900 行中,被 mutableHandlers、readonlyHandlers 等函数中被使用。

所以,这里就成环了。

  1. 其实 effect 才是响应式的核心,在 mountComponent、doWatch、reactive 中被调用。
  2. 在 reactive 中 通过 Proxy 实现劫持。
  3. 在 Proxy 劫持set时调用 trigger。
  4. 然后在 targger 中清除收集并触发目标的所有 effects
  5. 最终触发 patch 游戏结束。

推荐Vue学习资料文章:

ntent="mp" href="https://www.toutiao.com/i6844798155439997448/?group_id=6844798155439997448" rel="noopener noreferrer" target="_blank">细聊Single-Spa + Vue Cli 微前端落地指南「实践」

ntent="mp" href="https://www.toutiao.com/i6844774085323391499/?group_id=6844774085323391499" rel="noopener noreferrer" target="_blank">通俗易懂的Vue响应式原理以及依赖收集

ntent="mp" href="https://www.toutiao.com/i6844689305885999628/?group_id=6844689305885999628" rel="noopener noreferrer" target="_blank">Vue.js轮播库热门精选

ntent="mp" href="https://www.toutiao.com/i6844084369510892040/?group_id=6844084369510892040" rel="noopener noreferrer" target="_blank">Vue+CSS3 实现图片滑块效果

ntent="mp" href="https://www.toutiao.com/i6844039600957030923/?group_id=6844039600957030923" rel="noopener noreferrer" target="_blank">教你Vue3 Compiler 优化细节,如何手写高性能渲染函数(下)

ntent="mp" href="https://www.toutiao.com/i6843577013324743179/?group_id=6843577013324743179" rel="noopener noreferrer" target="_blank">一用惊人的Vue实践技巧「值得推荐」

ntent="mp" href="https://www.toutiao.com/i6843280010338370051/?group_id=6843280010338370051" rel="noopener noreferrer" target="_blank">Vue常见的面试知识点汇总(下)「附答案」

ntent="mp" href="https://www.toutiao.com/i6842199418926531083/?group_id=6842199418926531083" rel="noopener noreferrer" target="_blank">为什么我不再用Vue,改用React?

ntent="mp" href="https://www.toutiao.com/i6841453662301061639/?group_id=6841453662301061639" rel="noopener noreferrer" target="_blank">20个免费的设计资源 UI套件背景图标CSS框架

ntent="mp" href="https://www.toutiao.com/i6841074942197367299/?group_id=6841074942197367299" rel="noopener noreferrer" target="_blank">前端骨架屏都是如何生成的

ntent="mp" href="https://www.toutiao.com/i6840670396283355660/?group_id=6840670396283355660" rel="noopener noreferrer" target="_blank">用vue简单写一个音乐播放组件「附源码」

ntent="mp" href="https://www.toutiao.com/i6839667518764745228/?group_id=6839667518764745228" rel="noopener noreferrer" target="_blank">「干货」学会这些Vue小技巧,可以早点下班和女神约会

ntent="mp" href="https://www.toutiao.com/i6838524135186891278/?group_id=6838524135186891278" rel="noopener noreferrer" target="_blank">细品30张脑图带你从零开始学Vue

ntent="mp" href="https://www.toutiao.com/i6839659749596725771/?group_id=6839659749596725771" rel="noopener noreferrer" target="_blank">手把手教你Electron + Vue实战教程(五)

ntent="mp" href="https://www.toutiao.com/i6836162531908649483/?group_id=6836162531908649483" rel="noopener noreferrer" target="_blank">手把手教你Electron + Vue实战教程(三)

ntent="mp" href="https://www.toutiao.com/i6835960304526950916/?group_id=6835960304526950916" rel="noopener noreferrer" target="_blank">手把手教你Electron + Vue实战教程(一)

ntent="mp" href="https://www.toutiao.com/i6833564314566132227/?group_id=6833564314566132227" rel="noopener noreferrer" target="_blank">如何写出优秀后台管理系统?11个经典模版拿去不谢「干货」

ntent="mp" href="https://www.toutiao.com/i6833366419619447308/?group_id=6833366419619447308" rel="noopener noreferrer" target="_blank">基于 Vue 和高德地图实现地图组件「实践」

ntent="mp" href="https://www.toutiao.com/i6832575653900976644/?group_id=6832575653900976644" rel="noopener noreferrer" target="_blank">是什么让我爱上了Vue.js

ntent="mp" href="https://www.toutiao.com/i6832164205668336141/?group_id=6832164205668336141" rel="noopener noreferrer" target="_blank">1.1万字深入细品Vue3.0源码响应式系统笔记「下」

ntent="mp" href="https://www.toutiao.com/i6832110835305808387/?group_id=6832110835305808387" rel="noopener noreferrer" target="_blank">尤大大细说Vue3 的诞生之路「译」

ntent="mp" href="https://www.toutiao.com/i6831803495805354503/?group_id=6831803495805354503" rel="noopener noreferrer" target="_blank">大厂Code Review总结Vue开发规范经验「值得学习」

ntent="mp" href="https://www.toutiao.com/i6831324768256393739/?group_id=6831324768256393739" rel="noopener noreferrer" target="_blank">带你五步学会Vue SSR

ntent="mp" href="https://www.toutiao.com/i6829927955137823246/?group_id=6829927955137823246" rel="noopener noreferrer" target="_blank">Vue 3.x 如何有惊无险地快速入门「进阶篇」

ntent="mp" href="https://www.toutiao.com/i6829479963222082060/?group_id=6829479963222082060" rel="noopener noreferrer" target="_blank">带你了解 vue-next(Vue 3.0)之 炉火纯青「实践」

ntent="mp" href="https://www.toutiao.com/i6829118872772149772/?group_id=6829118872772149772" rel="noopener noreferrer" target="_blank">「干货」Vue+Element前端导入导出Excel

ntent="mp" href="https://www.toutiao.com/i6828799889397252612/?group_id=6828799889397252612" rel="noopener noreferrer" target="_blank">细品pdf.js实践解决含水印、电子签章问题「Vue篇」

ntent="mp" href="https://www.toutiao.com/i6827805121653506573/?group_id=6827805121653506573" rel="noopener noreferrer" target="_blank">Vue仿蘑菇街商城项目(vue+koa+mongodb)

ntent="mp" href="https://www.toutiao.com/i6827742275716514316/?group_id=6827742275716514316" rel="noopener noreferrer" target="_blank">「实践」Vue项目中标配编辑器插件Vue-Quill-Editor

ntent="mp" href="https://www.toutiao.com/i6827647187745243651/?group_id=6827647187745243651" rel="noopener noreferrer" target="_blank">消息队列助你成为高薪 Node.js 工程师

ntent="mp" href="https://www.toutiao.com/i6828036352286654983/?group_id=6828036352286654983" rel="noopener noreferrer" target="_blank">「干货」Deno TCP Echo Server 是怎么运行的?

ntent="mp" href="https://www.toutiao.com/i6827632377976586763/?group_id=6827632377976586763" rel="noopener noreferrer" target="_blank">「干货」通俗易懂的Deno 入门教程

ntent="mp" href="https://www.toutiao.com/i6826720047453438478/?group_id=6826720047453438478" rel="noopener noreferrer" target="_blank">「实践」基于Apify+node+react/vue搭建一个有点意思的爬虫平台

ntent="mp" href="https://www.toutiao.com/i6826152826318619144/?group_id=6826152826318619144" rel="noopener noreferrer" target="_blank">前端网红框架的插件机制全梳理(axios、koa、redux、vuex)

ntent="mp" href="https://www.toutiao.com/i6826138907583709709/?group_id=6826138907583709709" rel="noopener noreferrer" target="_blank">深入学习Vue的data、computed、watch来实现最精简响应式系统

ntent="mp" href="https://www.toutiao.com/i6825828475878769166/?group_id=6825828475878769166" rel="noopener noreferrer" target="_blank">10个实例小练习,快速入门熟练 Vue3 核心新特性(二)

ntent="mp" href="https://www.toutiao.com/i6825384959620940296/?group_id=6825384959620940296" rel="noopener noreferrer" target="_blank">2020前端就业Vue框架篇「实践」

ntent="mp" href="https://www.toutiao.com/i6824798616025039364/?group_id=6824798616025039364" rel="noopener noreferrer" target="_blank">Vue项目部署及性能优化指导篇「实践」

ntent="mp" href="https://www.toutiao.com/i6824323319924261383/?group_id=6824323319924261383" rel="noopener noreferrer" target="_blank">尤大大细品VuePress搭建技术网站与个人博客「实践」

ntent="mp" href="https://www.toutiao.com/i6823619809029128716/?group_id=6823619809029128716" rel="noopener noreferrer" target="_blank">是什么导致尤大大选择放弃Webpack?【vite 原理解析】

ntent="mp" href="https://www.toutiao.com/i6823361775694512653/?group_id=6823361775694512653" rel="noopener noreferrer" target="_blank">带你了解 vue-next(Vue 3.0)之 初入茅庐【实践】

ntent="mp" href="https://www.toutiao.com/i6822256635658895884/?group_id=6822256635658895884" rel="noopener noreferrer" target="_blank">一篇文章教你并列比较React.js和Vue.js的语法【实践】

ntent="mp" href="https://www.toutiao.com/i6820686462694982158/?group_id=6820686462694982158" rel="noopener noreferrer" target="_blank">深入浅出通过vue-cli3构建一个SSR应用程序【实践】

ntent="mp" href="https://www.toutiao.com/i6818344300082889224/?group_id=6818344300082889224" rel="noopener noreferrer" target="_blank">聊聊昨晚尤雨溪现场针对Vue3.0 Beta版本新特性知识点汇总

ntent="mp" href="https://www.toutiao.com/i6810954020031562252/?group_id=6810954020031562252" rel="noopener noreferrer" target="_blank">Vue真是太好了 壹万多字的Vue知识点 超详细!

ntent="mp" href="https://www.toutiao.com/i6815452062881415683/?group_id=6815452062881415683" rel="noopener noreferrer" target="_blank">深入浅出Vue3 跟着尤雨溪学 Typescript 之 Ref 【实践】

ntent="mp" href="https://www.toutiao.com/i6817773725736239628/?group_id=6817773725736239628" rel="noopener noreferrer" target="_blank">Vue 3.0 Beta 和React 开发者分别杠上了

ntent="mp" href="https://www.toutiao.com/i6805432820110983688/?group_id=6805432820110983688" rel="noopener noreferrer" target="_blank">Vue3 尝鲜

ntent="mp" href="https://www.toutiao.com/i6808329189901468174/?group_id=6808329189901468174" rel="noopener noreferrer" target="_blank">Vue 开源项目 TOP45

ntent="mp" href="https://www.toutiao.com/i6806264785508762124/?group_id=6806264785508762124" rel="noopener noreferrer" target="_blank">尤雨溪:Vue 3.0的设计原则

ntent="mp" href="https://www.toutiao.com/i6805425165132890635/?group_id=6805425165132890635" rel="noopener noreferrer" target="_blank">实现全栈收银系统(Node+Vue)(上)

ntent="mp" href="https://www.toutiao.com/i6809939243440275981/?group_id=6809939243440275981" rel="noopener noreferrer" target="_blank">vue引入原生高德地图

ntent="mp" href="https://www.toutiao.com/i6805067432382693902/?group_id=6805067432382693902" rel="noopener noreferrer" target="_blank">多年vue项目实战经验汇总

ntent="mp" href="https://www.toutiao.com/i6805729533463888388/?group_id=6805729533463888388" rel="noopener noreferrer" target="_blank">基于 Vue 的两层吸顶踩坑总结

ntent="mp" href="https://www.toutiao.com/i6805083839644303876/?group_id=6805083839644303876" rel="noopener noreferrer" target="_blank">Vue 开发必须知道的 36 个技巧【近1W字】

ntent="mp" href="https://www.toutiao.com/i6810630570251387406/?group_id=6810630570251387406" rel="noopener noreferrer" target="_blank">深入理解vue中的slot与slot-scope

ntent="mp" href="https://www.toutiao.com/i6806116083720782348/?group_id=6806116083720782348" rel="noopener noreferrer" target="_blank">使用vue+node搭建前端异常监控系统

ntent="mp" href="https://www.toutiao.com/i6808322447729754632/?group_id=6808322447729754632" rel="noopener noreferrer" target="_blank">基于Vue实现拖拽升级(九宫格拖拽)

ntent="mp" href="https://www.toutiao.com/i6805725101749699076/?group_id=6805725101749699076" rel="noopener noreferrer" target="_blank">手摸手,带你用vue撸后台 系列三(实战篇)

ntent="mp" href="https://www.toutiao.com/i6811330273926447620/?group_id=6811330273926447620" rel="noopener noreferrer" target="_blank">Vue组件间通信几种方式,你用哪种?【实践】

ntent="mp" href="https://www.toutiao.com/i6812984956667560455/?group_id=6812984956667560455" rel="noopener noreferrer" target="_blank">10个Vue开发技巧助力成为更好的工程师

ntent="mp" href="https://www.toutiao.com/i6805795810026979847/?group_id=6805795810026979847" rel="noopener noreferrer" target="_blank">1W字长文+多图,带你了解vue的双向数据绑定源码实现

ntent="mp" href="https://www.toutiao.com/i6807204223193711116/?group_id=6807204223193711116" rel="noopener noreferrer" target="_blank">干货满满!如何优雅简洁地实现时钟翻牌器(支持JS/Vue/React)

ntent="mp" href="https://www.toutiao.com/i6807019844597187079/?group_id=6807019844597187079" rel="noopener noreferrer" target="_blank">手把手教你D3.js 实现数据可视化极速上手到Vue应用

ntent="mp" href="https://www.toutiao.com/i6804675576037638663/?group_id=6804675576037638663" rel="noopener noreferrer" target="_blank">吃透 Vue 项目开发实践|16个方面深入前端工程化开发技巧【中】

ntent="mp" href="https://www.toutiao.com/i6819081157213159947/?group_id=6819081157213159947" rel="noopener noreferrer" target="_blank">Vue3.0权限管理实现流程【实践】

ntent="mp" href="https://www.toutiao.com/i6820294027150098948/?group_id=6820294027150098948" rel="noopener noreferrer" target="_blank">后台管理系统,前端Vue根据角色动态设置菜单栏和路由

作者:妹红大大

转发链接:https://mp.weixin.qq.com/s/D_PRIMAD6i225Pn-a_lzPA


顶一下()     踩一下()

热门推荐

发表评论
0评