为什么写这篇vue的分析文章?

对于天资愚钝的前端(我)来说,阅读源码是件不容易的事情,毕竟有时候看源码分析的文章都看不懂。每次看到大佬们用了1~2年的vue就能掌握原理,甚至精通源码,再看看自己用了好几年都还在基本的使用阶段,心中总是羞愧不已。如果一直满足于基本的业务开发,怕是得在初级水平一直待下去了吧。所以希望在学习源码的同时记录知识点,可以让自己的理解和记忆更加深刻,也方便将来查阅。

目录结构

本文以vue的第一次 commit a879ec06 作为分析版本

├── build
│  └── build.js        // `rollup` 打包配置
├── dist            
│  └── vue.js  
├── package.json
├── src            // vue源码目录
│  ├── compiler        // 将vue-template转化为render函数
│  │  ├── codegen.js     // 递归ast提取指令,分类attr,style,class,并生成render函数
│  │  ├── html-parser.js   // 通过正则匹配将html字符串转化为ast
│  │  ├── index.js      // compile主入口
│  │  └── text-parser.js   // 编译{{}}
│  ├── config.js       // 对于vue的全局配置文件
│  ├── index.js        // 主入口
│  ├── index.umd.js      // 未知(应该是umd格式的主入口)
│  ├── instance        // vue实例函数
│  │  └── index.js      // 包含了vue实例的初始化,compile,data代理,methods代理,watch数据,执行渲染
│  ├── observer        // 数据订阅发布的实现
│  │  ├── array.js      // 实现array变异方法,$set $remove 实现
│  │  ├── batcher.js     // watch执行队列的收集,执行
│  │  ├── dep.js       // 订阅中心实现
│  │  ├── index.js      // 数据劫持的实现,收集订阅者
│  │  └── watcher.js     // watch实现,订阅者
│  ├── util          // 工具函数
│  │  ├── component.js
│  │  ├── debug.js
│  │  ├── dom.js
│  │  ├── env.js       // nexttick实现
│  │  ├── index.js
│  │  ├── lang.js
│  │  └── options.js
│  └── vdom
│    ├── dom.js       // dom操作的封装
│    ├── h.js        // 节点数据分析(元素节点,文本节点)
│    ├── index.js      // vdom主入口
│    ├── modules      // 不同属性处理函数
│    │  ├── attrs.js    // 普通attr属性处理
│    │  ├── class.js    // class处理
│    │  ├── events.js   // event处理
│    │  ├── props.js    // props处理
│    │  └── style.js    // style处理
│    ├── patch.js      // node树的渲染,包括节点的加减更新处理,及对应attr的处理
│    └── vnode.js      // 返回最终的节点数据
└── webpack.config.js     // webpack配置

从template到html的过程分析

我们的代码是从new Vue()开始的,Vue的构造函数如下:

constructor (options) {
 // options就是我们对于vue的配置
 this.$options = options
 this._data = options.data
 // 获取元素html,即template
 const el = this._el = document.querySelector(options.el)
 // 编译模板 -> render函数
 const render = compile(getOuterHTML(el))
 this._el.innerHTML = ''
 // 实例代理data数据
 Object.keys(options.data).forEach(key => this._proxy(key))
 // 将method的this指向实例
 if (options.methods) {
  Object.keys(options.methods).forEach(key => {
   this[key] = options.methods[key].bind(this)
  })
 }
 // 数据观察
 this._ob = observe(options.data)
 this._watchers = []
 // watch数据及更新
 this._watcher = new Watcher(this, render, this._update)
 // 渲染函数
 this._update(this._watcher.value)
}

当我们初始化项目的时候,即会执行构造函数,该函数向我们展示了vue初始化的主线:编译template字符串 => 代理data数据/methods的this绑定 => 数据观察 => 建立watch及更新渲染

1. 编译template字符串

const render = compile(getOuterHTML(el))

其中compile的实现如下:

export function compile (html) {
 html = html.trim()
 // 对编译结果缓存
 const hit = cache[html]
 // parse函数在parse-html中定义,其作用是把我们获取的html字符串通过正则匹配转化为ast,输出如下 
                                    
一句话新闻
一口气升级7个大模型SaaS应用,百度智能云:突出一个“开箱即用” - 2024/5/7

这一波大模型产业落地浪潮里,不少企业其实处在 “干瞪眼“的状态。

一种情况是,很多大模型产品看得见却摸不着,在台上一个个遥遥领先——今天Sora技精四座,明天英伟达的机器人又赢得满堂彩,可是到了台下一问:啥时候能用上啊?答曰:遥遥无期。

另一种情况是,企业想用上大模型,却又难免瞻前顾后——既要考虑场景融合,又得兼顾安全性,还要考虑打通现有系统,再加上各种部署成本和繁琐的采购流程……最后只能拂袖:罢了,再等等吧。

稳了!魔兽国服回归的3条重磅消息!官宣时间再确认!

昨天有一位朋友在大神群里分享,自己亚服账号被封号之后居然弹出了国服的封号信息对话框。

这里面让他访问的是一个国服的战网网址,com.cn和后面的zh都非常明白地表明这就是国服战网。

而他在复制这个网址并且进行登录之后,确实是网易的网址,也就是我们熟悉的停服之后国服发布的暴雪游戏产品运营到期开放退款的说明。这是一件比较奇怪的事情,因为以前都没有出现这样的情况,现在突然提示跳转到国服战网的网址,是不是说明了简体中文客户端已经开始进行更新了呢?