Web前端最全学习vue源码(10)学习render渲染函数(1),2024年前端笔试题总

下面是我在学习HTML和CSS的时候整理的一些笔记,有兴趣的可以看下:

HTML、CSS部分截图

进阶阶段

进阶阶段,开始攻 JS,对于刚接触 JS 的初学者,确实比学习 HTML 和 CSS 有难度,但是只要肯下功夫,这部分对于你来说,也不是什么大问题。

JS 内容涉及到的知识点较多,看到网上有很多人建议你从头到尾抱着那本《JavaScript高级程序设计》学,我是不建议的,毕竟刚接触 JS 谁能看得下去,当时我也不能,也没那样做。

我这部分的学习技巧是,增加次数,减少单次看的内容。就是说,第一遍学习 JS 走马观花的看,看个大概,去找视频以及网站学习,不建议直接看书。因为看书看不下去的时候很打击你学下去的信心。

然后通过一些网站的小例子,开始动手敲代码,一定要去实践、实践、实践,这一遍是为了更好的去熟悉 JS 的语法。别只顾着来回的看知识点,眼高手低可不是个好习惯,我在这吃过亏,你懂的。

1、JavaScript 和 ES6

在这个过程你会发现,有很多 JS 知识点你并不能更好的理解为什么这么设计,以及这样设计的好处是什么,这就逼着让你去学习这单个知识点的来龙去脉,去哪学?第一,书籍,我知道你不喜欢看,我最近通过刷大厂面试题整理了一份前端核心知识笔记,比较书籍更精简,一句废话都没有,这份笔记也让我通过跳槽从8k涨成20k。

JavaScript部分截图

2、前端框架

前端框架太多了,真的学不动了,别慌,其实对于前端的三大马车,Angular、React、Vue 只要把其中一种框架学明白,底层原理实现,其他两个学起来不会很吃力,这也取决于你以后就职的公司要求你会哪一个框架了,当然,会的越多越好,但是往往每个人的时间是有限的,对于自学的学生,或者即将面试找工作的人,当然要选择一门框架深挖原理。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

以 Vue 为例,我整理了如下的面试题。

Vue部分截图

[_v(111)]

)

上面这些参数都会传给 Vnode,并保存在创建的 Vnode 中

function VNode(

tag, data, children, text

) {

this.tag = tag;

this.data = data;

this.children = children;

this.text = text;

}

然后得到这么一个 Vnode

{

tag:“div”,

data:{

attrs: {“data”: 111}

},

children:[{

tag:undefined,

data:undefined,

text:111

}]

}

说到这里,已经能很清楚 render 内部是如何创建Vnode 了

但是这里只是其中一种小小的简单 render

要是项目中的render,数据是很多,很复杂的

而我们主要要把握的是主要流程就可以了

不过,还有必要记录其他 render,那就是遍历

遍历相关


看下面这个 template

解析成下面的render

function render() {

with(this) {

return _c(‘div’,

_l(2,function(item, index) {

return _c(‘span’)

})

)

}

}

看到一个 _l, 他必定就是遍历生成 Vnode 的幕后黑手了

同样的,_l 和 _v 在同一个地方 installRenderHelpers 注册的

function installRenderHelpers(target) {

target._l = renderList;

}

不客气地搜出 renderList 源码出来

先跳到后面的分析啊,源码有点长了,虽然很简单

function renderList(val, _render) {

var ret, i, l, keys, key;

// 遍历数组

if ( Array.isArray(val) ) {

ret = new Array(val.length);

// 调用传入的函数,把值传入,数组保存结果

for (i = 0, l = val.length; i < l; i++) {

ret[i] = _render(val[i], i);

}

}

// 遍历数字

else if (typeof val === ‘number’) {

ret = new Array(val);

// 调用传入的函数,把值传入,数组保存结果

for (i = 0; i < val; i++) {

ret[i] = _render(i + 1, i);

}

}

// 遍历对象

else if (typeof val ==“object”) {

keys = Object.keys(val);

ret = new Array(keys.length);

// 调用传入的函数,把值传入,数组保存结果

for (i = 0, l = keys.length; i < l; i++) {

key = keys[i];

ret[i] = _render(val[key], key, i);

}

}

// 返回 vnode 数组

return ret

}

看到 renderList 接收两个参数,val 和 render,而 _l 调用的时候,也就是传入的这两个参数,比如下面

_l(2,function(item, index) {

return _c(‘span’)

})

val 就是 2,_render 就是上面的函数

1 遍历的数据 val

遍历的数据分为三种类型,一种是对象,一种是数字,一种是数组

2 单个 vnode 渲染回调 _render

重要是这个回调

1、renderList 每次遍历都会执行回调,并把的每一项 item 和 index 都传入 回调中

2、回调执行完毕,会返回 vnode

3、使用数组保存 vnode,然后 遍历完毕就返回 数组

于是可以看上面的 render 函数 ,传入了 数字2,和 创建 span 的回调

_l(2,function(item, index) {

return _c(‘span’)

})

_l 执行完毕,内部遍历两次,最后返回 两个 span vnode 的数组,然后传给外层的 _c ,作为 vnode.children 保存

render 执行完毕,得到这样的 vnode

{

tag:“div”,

data:undefined,

children:[{

tag:“span”,

data:undefined

},{

tag:“span”,

data:undefined

}]

}

都灰常简单啊,没写之前,我还觉得内容应该挺多的,写完发现还可以

当然还有其他的 render

比如要模板含有 filter,我们来看看

Filters - 源码版


下面的讲解会以下面例子 作为讲解模板

这里有一个过滤器 all,用来过滤 parentName

{{parentName|all }}

new Vue({

el:document.getElementsByTagName(“div”)[0],

data(){

return {

parentName:111

}

},

filters:{

all(){  return “我是过滤器” }

}

})

页面的 filter 解析成什么

首先,上面的例子会被解析成下面的渲染函数

(function() {

with(this) {

return _c(‘div’,[

_v(_s(_f(“all”)(parentName)))

])

}

}

这段代码继续解释下

  1. _c 是渲染组件的函数,这里会渲染出根组件

  2. 这是匿名自执行函数,后面渲染的时候调用,会 绑定当前实例为作用域

  3. with 的作用是,绑定大括号内代码的 变量访问作用域,所以里面的所有变量都会从 实例上获取

然后,你可以看到 ' parentName | all ' 被解析成 _f('all')( parentName )

怎么解析的?

简单说就是,当匹配到 | 这个符号,就知道你用过滤器,然后就解析成 _f 去获取对应过滤器 并调用,这个过程不赘述

_f 是什么?


_f 是获取具体过滤器的函数

1_f 会在Vue 初始化的时候,注册到 Vue 的原型上

// 已简化

function installRenderHelpers(target) {

target._s = toString;

target._f = resolveFilter;

}

installRenderHelpers(Vue.prototype);

所在在 上面的 渲染函数 with 绑定当前实例vm为作用域 之后,_f 从vm 获取,成了这样 vm._f

_f 是 resolveFilter,一个可以获取 具体filter 的函数

使用 _f(“all”) 就能获取到 all 这个过滤器,resolveFilter 下面会说

怎么获取下面继续…

设置的 filter 如何被调用

由上面可以看到,_f 是 resolveFilter 赋值的,下面是 resolveFilter 源码

// 已简化

function resolveFilter(id) {

return resolveAsset(

this.$options, ‘filters’, id, true

) || identity

}

要是你看过学习vue源码(3) 手写Vue.directive、Vue.filter、Vue.component方法,相信这里你很熟悉,其实就是从this.options.filters里找对应的过滤器函数来调用,如图所示

this.options 会拿到当前组件的所有选项

你问我为什么?

根据上一个问题知道

  1. _f  会使用 实例去调用 ,vm._f  类等 vm.resolveFilter

  2. 所以,resolveFilter 的 执行上下文 this 是 vm

  3. 所以,this.$options 就是 实例的 options 啦

接着,调用 resolveAsset ,目的就是拿到 组件选项中的 具体 filter

传入 当前组件的选项 ,指定要其选项 filters ,指定具体 filter 名

function resolveAsset(

options, type, id, warnMissing

) {

// g:拿到  filters 选项

var assets = options[type];

// g:返回 调用的 filter

return assets[id]

}

_f(“all”) 流程 就成了下面这样

  1. 拿到 组件选项 中的 filters

  2. 然后再从 filters 中,拿到 all 这个filter

  3. 执行返回的 all 过滤函数时,传入需要过滤的值 parentName

  4. 得到 返回了 过滤后的值

所以,当渲染函数解析的时候,碰到使用过滤器的地方,按流程拿到过滤值后,就可以渲染到页面上了

_f(“all”)(parentName)) 就会变成 “我是过滤器” 放到 渲染函数中,最后,就是渲染到页面了

总结

fitler 其实就是从组件选项 filters 获取你设置的某个filter,并调用,然后使用你函数执行的返回值渲染

太简单了,总结跟没总结一样…

render 什么时候开始执行?

如果你看过学习vue源码(4) 手写vm.$mount方法,我相信你已经知道了

如图所示,是在挂载阶段执行的。

总结


每个模板经过 compile 都会生成一个 render 函数

render 作为 渲染三部曲的第二部,主要作用就是 执行 render,生成 Vnode

把 template 上绑定的数据,都保存到 vnode 中

然后,生成 Vnode,就是为了给 渲染三部曲的 第三部 Diff 提供源动力

从而完成 DOM 挂载

到这里其实基本就已经结束了render的思路,但是源码中有个静态render,这个 对渲染性能的提高有极大的帮助,所以必须看下。

没错,就是 静态 render,看过学习vue源码(8)手写优化器的人,应该知道什么是 静态 render

静态 render 就是用于渲染哪些不会变化的节点

大家可以先看看,Vue 是怎么判断某个节点是否是静态节点

好,下面开始我们的正文,想了想,我们还是以几个问题开始吧

1、静态 render 是什么样子的

2、静态 render 是怎么生成和 保存

3、静态 render 怎么执行

什么是 静态Render


静态 render 其实跟 render 是一样的,都是执行得到 Vnode

只是静态 render,没有绑定动态数据而已,也就是说不会变化

比如说,一个简单 render 是这样的

绑定了动态数据,需要从实例去获取

_c(‘div’,[_v(_s(aa))])

而静态 render 是这样的

没有动态数据,这个静态render 的执行结果是永远不会变的

_c(‘div’,[_c(‘span’,[_v(“1”)])])

生成保存静态Render


静态 render 是在 generate 阶段生成的,生成的方式和 render 是一样的

比如在一个模板中,有很多个静态 根节点,像这样

首先,Vue 会在遍历模板的时候,发现 span 和 strong 本身以及其子节点都是静态的

那么就会给 span 和 strong 节点本身设置一个属性 staticRoot,表示他们是静态根节点

然后这两个静态根节点就会生成自己专属的 静态 render

如果你有一直看我的Vue 笔记的话,你应该这里是会有点印象的

之后

静态 render 生成之后是需要保存的,那么保存在哪里呢?

保存在一个数组中,名叫 staticRenderFns,就是直接push 进去

当然了,此时的 push 进去的 静态 render 还是字符串,并没有变成函数

以上面的模板为例,这里的 staticRenderFns 就是这样,包含了两个字符串

staticRenderFns  = [

“_c(‘span’,[_c(‘b’,[_v(“1”)])])”,

“_c(‘strong’,[_c(‘b’,[_v(“1”)])])”

]

但是在后面会逐个遍历变成可执行的函数

staticRenderFns = staticRenderFns.map(code => {

return new Function(code)

});

那么 这个 staticRenderFns 又是什么啊?

每个 Vue 实例都有一个独立的 staticRenderFns,用来保存实例本身的静态 render

staticRenderFns 的位置是

vm.$options.staticRenderFns

执行静态Render


静态 render 需要配合 render 使用,怎么说

看个例子

这个模板的 render 函数是

_c(‘div’,[

_m(0),

_v(_s(a),

_m(1)

])

文末

篇幅有限没有列举更多的前端面试题,小编把整理的前端大厂面试题PDF分享出来,一共有269页

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

  • 10
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值