模仿vue
第一次写于: 2020年5月2日
既然要最求刺激,那就要贯彻到底. — 品如
想要模仿Vue, 那就要知道Vue实现了什么功能.
以及根据我现在的个人能力,我能够实现的功能.
为了方便区分, 我把自己实现的内容称为 Jue
功能
vue的功能
- 模板编译
- virtual node
- 响应式数据
- 自定义组件
- vue命令
- 数据的双向绑定
- hook
Jue想要实现的功能
- 模板编译
- 响应式数据
- v-bind, v-on
- 自定义组件
实现
模板编译
目前实现的只有插值表达式
其它的在日后可能更新
想要的效果
<div id="app">
<ul>
<li>{
{name.firstName}} {
{sex}}</li>
<li>{
{age}} {
{sex}}</li>
</ul>
</div>
能够转化成
<div id="app">
<ul>
<li>Jue male</li>
<li>18 male</li>
</ul>
</div>
思路
思路1.
- 获取到id = app 的div元素
- 创建一个
fragment
- 一个个地取出div中的元素
- 遇到插值表达式, 进行替换
- 将处理完成后的文本放入fragment中
思路2.
- 获取到id = app 的div元素
- 将html元素转化成
vnode
- 通过
vnode
获取到 render函数 - 通过render 函数进行渲染
这种方法实现起来比较麻烦.用到了vue AST(抽象语法树)
我暂时还没有太深入地了解;
联想到webpack的作用, 我总感觉现在前端越来越有趣了.
JS的执行肯定都是通过JS的解析器.
而现在有各种语法,JS的解析器可能还不能处理;
那么,通过各种编译工具去把这些代码处理成JS解析器可以执行代码;
让我不禁回忆起学习编译原理的青葱岁月.
编译原理可是真地难.
我的实现
处理表达式的一个类
class JueExpr {
constructor (vm, expr) {
this.vm = vm
this.data = vm.$data
this.expr = expr
}
getValue () {
// 根据字符串来取值
// 'name.firstName' => this.data.name.firstName
return this.expr.split('.').reduce((acc, cur) => {
return acc[cur]
}, this.data)
}
text () {
let value = this.getValue()
return value.toString()
}
}
处理模板编译的类;
注释掉的内容, 是和下面响应式数据相关的;
可以暂时忽略.
class Compiler {
constructor (vm) {
this.vm = vm
this.loadTemplate(vm.$el)
}
loadTemplate (el) {
// 把节点加载到内存中
let node = document.querySelector(el)
let fragment = document.createDocumentFragment()
let currentDom = node.firstChild
// 循环调用取dom, 处理后放入fragment
while (currentDom != null) {
this.compile(currentDom)
fragment.appendChild(currentDom)
currentDom = node.firstChild
}
node.appendChild(fragment)
}
compile (dom) {
this.compiledByNodeType(dom, dom.nodeType)
}