1 Star 1 Fork 0

SyncGithub / template-explorer

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

template-explorer

每个成熟的前端框架,模板引擎是其中重要的组成部分,提升了视图的可维护性和编写效率,目前市面上的模板引擎分为两种:

  • string base 基于模板字符串中,加上用户数据变量或者函数,编译成view字符串,通过innerHTML填充到对应节点,借助浏览器能力翻译成具体节点。
  • dom base 解析阶段和string base类似,但是强调着render之后,dom和数据间的联动关系,数据更新后不依赖手动操作dom来更新view

工程搭建

安装依赖

    yarn add  rollup @babel/core @babel/plugin-syntax-dynamic-import @babel/preset-env  rollup-plugin-babel  rollup-plugin-commonjs rollup-plugin-node-resolve rollup-plugin-serve --dev

rollup.config.js

import resolve from 'rollup-plugin-node-resolve'
import babel from 'rollup-plugin-babel'
import commonjs from 'rollup-plugin-commonjs'
import serve from 'rollup-plugin-serve'


export default {
    input: 'src/index.js',
    output: {
        file: './lib/template-exporer.js',
        format: 'esm',
        name: 'tExporer',
        sourcemap: true,
    },
    plugins: [
        resolve(),
        commonjs(),
        babel({
            exclude: /node_modules/
        }),
        process.env.SERVE ? serve({
            open: true,
            contentBase: '',
            openPage: '/public/index.html',
            host: '127.0.0.1',
            port: '9999'
        }) : null
    ]

}

string base

大致过程

template-> split = lines -> compile = function +data -> string -> innerHTML = dom

要实现如下的模板:

<div>
    <% if (test > 1 ) { %>
        <%= test %>
    <% } %>
</div>

我们需要按照:

const TYPE_TAG = 1;     // 标签节点
const TYPE_TEXT = 2;    // 文本节点
const TYPE_VAR = 3;     // 变量和语句节点
const TYPE_COM = 4;     // 注释节点

类型对html进行spilt,最终的lines:

[
   '<div>',
   '<% if (test > 1 ) { %>',
   '<%= test %>',
   '<% } %>',
   '</div>'
]

接下来,根据不同的节点类型,来拼凑不同的解析规则,最终compile函数,大致如下:

let code = 'const l=[];'
code += 'with(obj){'
code += `
        l.push('<div>');
        if (test > 1 ) {
            l.push(test);
        }
        l.push('</div>');
    }
    return l.join('')
`
// compile fn:
const fn = new Function('obj',`return ${code}`)
fn.call(scope)

dom base

参考 Vue的渲染

template -> parse = AST -> compile=> data + render function => vitual dom -> dom

要实现这样的模板:

<div id="app" >
    <p>{{name}}</p>
    <span>年龄:{{age}}</span>
</div>

首先转换成AST:

{
    tag:'div',
    type:1,
    attrs: {
        id:'app'
    },
    children:[{
        tag:'p',
        type:1,
        children:[{
            type:3,
            text:'{{name}}'
        }]
    },{
        tag:'span',
        type:1,
        children:[{
            type:3,
            text:'年龄:'
        },{
            type:3,
            text:'{{age}}'
        }]
    }]
}

compile render函数:

// 函数体
const code = _c('div',{id:'app'},_c("p",undefined,_v('年龄:'+_s(name))),_s(age))

// 主函数
function render() {
  with(this) {
    return _c('div', {
      attrs: {
        "id": "app"
      }
    }, [_c('p', [_v(_s(name))]), _c('span', [_v("年龄:" + _s(age))])])
  }
}

然后在渲染Watcher 通过render函数,解析成vnode,创建真实dom, 再次视图更新通过dom diff部分更新dom节点:

_c :createElement
_v: createTextNode
_s: stringify

dom diff Vue的diff算法是基于snabbdom改造过来的,Vue的diff算法是同级的vnode间做diff,递归地进行同级vnode的diff,最终实现整个DOM树的更新.

patch函数会维护oldStart+oldEnd,newStart+newEnd这样2对指针,分别对应oldVdom和newVdom的起点和终点。起止点之前的节点是待处理的节点,Vue不断对vnode进行处理同时移动指针直到其中任意一对起点和终点相遇。处理过的节点Vue会在oldVdom和newVdom中同时将它标记为已处理。Vue通过以下措施来提升diff的性能。

参考:

空文件

简介

取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
JavaScript
1
https://gitee.com/sync-github/template-explorer.git
git@gitee.com:sync-github/template-explorer.git
sync-github
template-explorer
template-explorer
master

搜索帮助