前言
我们需要同步并明确一些词汇的概念,比如:声明式、命令式、运行时、编译时.。这些词汇在框架设计中被经常涉及到。
为什么需要框架?🧰
首先我们知道在目前的前端开发中,有两种编程范式,命令式编程 和声明式编程 ,简单来讲,命令式编程是指我们在学习JS
原生开发时所使用的编程方式,而声明式编程则是我们在使用Vue
时进行的编程方式。
Vue
作为一个流行框架,它所使用的编程方式必然是在某些方面优于原生开发的 ,并且它的优点能够让企业去坚定的选择它 ,下面我们来看看究竟是怎么个事儿。
命令式
命令式是:关注过程 的一种编程范式,他描述了完成一个功能的 详细逻辑与步骤
例子:为指定的div的子元素div的子元素p标签,展示变量msg
声明式
声明式是:**关注结果 **的一种编程范式,他 并不关心完成一个功能的 详细逻辑与步骤 。(注意:这并不意味着声明式不需要过程!声明式只是把过程进行了隐藏而已!)
例子:(与上述相同例子)
"当我们看到 声明式 时,心情是非常舒爽 的!原来这就是**框架带来的好处!"
命令式 VS 声明式
从视觉上还不够,我们要理论分析,通常情况下,我们评价一个编程范式通常会从两个方面入手:
性能⚡
typescript
div.innerText="hello world"//耗时为:1
html
<div>{{msg}</div> <!--耗时为:1+n-->
那么:已知修改text最简单的方式是innerText ,浏览器是无法直接解析{{}}
这种插值语法的,所以说无论声明式的代码是如何实现的文本切换,那么它的耗时一定是 >1 的,我们把它比作 1+n (对比的性能消耗)
可维护性🔧
可维护性代表的维度非常多,但是通常情况下,所谓的可维护性指的是:对代码可以方便的阅读、修
改、删除、增加。
在之前的例子中,我们可以清楚的认识到 声明式 的代码更利于阅读,所以更加利于维护。
所以,由以上举例可知:命令式的可维护性<声明式的可维护性 !!
企业应用为何使用框架开发?
无论什么类型的企业,也无论它们在开发什么类型的项目,那么最关注的点无非就是两个:
项目成本💰
开发周期越长,所付出的人员成本就会越高,从而导致项目成本变得越高。
通过我们前面的分析可知,声明式的开发范式在可维护性 上是大于命令式的。而可维护性从一定程度上就决定了,它会使项目的:**开发周期变短、升级变得更容易 **从而大量节约开发成本。
开发体验🌈
决定开发者开发体验的核心要素,主要是在开发时和阅读时的难度,这个被叫做:心智负担 。
根据我们之前所说,声明式开发难度明显低于命令式 ,对于开发体验而言,声明式的心智负担更低,也就是开发体验更好。
框架是怎样设计的?🚀
我们之前提到过 命令式的性能 >声明式的性能 ,难道就对性能不管不顾了吗!?太不负责了!!
Vue 作者 尤雨溪 在一次演讲中说道:"框架的设计过程其实是一个不断取舍的过程"
我们知道对于 Vue 而言,当我们使用它的是通过声明式的方式进行使用 ,但是对于Vue内部而言,是通过命令式来进行的实现 。对于开发者而言,不需要关注实现过程,只需要关注最终的结果即可。
所以我们可以理解为:Vue 封装了命令式的逻辑,而对外暴露出了声明式的接口。
Vue 所需要做的就是:封装命令式逻辑,同时尽可能的减少性能的损耗!它需要在
性能与可维护性 之间,找到一个平衡。从而找到一个可维护性更好,性能相对更优的一个点。
什么是运行时?
场景:渲染出下面这个dom元素
使用Vue的Api
尝试一下,可以看到关键在于render
html
// render.html
<head>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.2.37/vue.global.js"></script>
</head>
<body>
<div id="app"></div>
</body>
<script>
const { render, h } = Vue
const vnode = h(
'div',
{
class: 'test'
},
'hello render'
)
const container = document.querySelector('#app')
render(vnode, container)
</script>
浏览器中渲染:
那么render
是怎么实现的呢?我们自己简单模拟一下
typescript
const vnode = {
type: 'div',
props: {
class: 'test'
},
children: 'hello render'
}
function render(vnode) {
const ele = document.createElement(vnode.type)
ele.className = vnode.props.class
ele.innerText = vnode.children
document.body.appendChild(ele)
}
render(vnode)
浏览器中同样渲染出:
没错,我们刚刚就编写了一个小小的 "框架",就是 运行时 的代码框架。
"每次这么写个这么复杂的vnode
,太麻烦了,能不能直接写 HTML标签结构的方式 来进行渲染"
"可以的,但是那就不是 运行时 的代码可以解决的了!"
总结:运行时可以利用 render
把 vnode
渲染成真实的 dom节点
什么是编译时?
上面我们提到了,能不能直接写 HTML标签结构的方式 来进行渲染,要想实现这一点,我们需要来学习一下 编译时(编译器) 。其实 Vue 是 编译时+运行时 的一个框架,我们来看看一段代码:
html
<head>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.2.37/vue.global.js"></script>
</head>
<body>
<div id="app"></div>
</body>
<script>
const { compile, createApp } = Vue
const html = `
<div class="test">hello compiler</div>
`
const renderFn = compile(html) //compile即编译时(编译器)
const app = createApp({
render: renderFn
})
app.mount('#app')
</script>
对于编译器 而言,它的主要作用就是:把template中的html编译成render函数 。然后再利用运行时通过render挂载对应的DOM。
那么最后,我们做一个总结:编译时可以把 html的节点,编译成render函数
为什么要编译时+运行时?
我们要明确一点:dom 操作比 js 操作更耗时,即更耗性能 !
- 针对于 纯运行时而言:因为不存在编译器,所以我们只能够提供一个复杂的JS对象(vnode)。
- 针对于 纯编译时 而言:因为缺少运行时,所以它只能把分析差异的操作,放到 编译时进行,难以处理动态数据,同样因为省略了运行时,所以速度可能会更快。但是这种方式这将损失灵活性。
- 运行时+编译时:比如 vue或 react都是通过这种方式来进行构建的,使其可以在保持灵活性的基础上,尽量的进行性能的优化,从而达到一种平衡。