仿vue.js实现简单模板引擎(2kB)

5 篇文章 0 订阅

仿vue.js实现简单模板引擎(2kB)

点击预览

在这里插入图片描述

在这里插入图片描述


index.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>导航</title>
</head>
<style type="text/css">
    *{
        box-sizing: border-box;
    }
    body {
        margin: 0;
        padding: 0;
        background: #f2f2f2;
        overflow-x: hidden;
        overflow-y: auto;
        font-weight: 500;
        font-family: "Microsoft YaHei","宋体","Segoe UI", "Lucida Grande", Helvetica, Arial,sans-serif, FreeSans, Arimo;
    }
    a {
        text-decoration: none;
    }
    body ul {
	padding-left: 0px;
        list-style: none;
    }
    input, button {
        border: none;
        outline: none;
    }
    .search-container {
        width: 80%;
        margin: 50px auto;
    }
    .search {
        width: 50%;
        margin: auto;
        white-space:nowrap;
    }
    .search input {
        height: 42px;
        padding-left: 10px;
        width: 80%;
        border: 1px solid #A3D0C3;
    }
    .search button {
        height: 42px;
        width: 20%;
        background: #A3D0C3;
        border: 1px solid #A3D0C3;
    }
    .history {
        margin: 10px auto;
        width: 50%;
    }
    .history li {
        float: left;
        margin-left: 10px;
    }
    .link-container {
        width: 80%;
        margin: 100px auto;
        clear: left;
    }
    .box {
        float: left;
        width: 33.33%;
    }
    .box h3 {
        min-width: 80px;
        min-height: 18px;
        font-size: 12px;
        font-weight: 400;
        color: #b7b7b7;
        display: inline-block;
        margin-bottom: 5px;
        position: relative;
    }
    .box ul li {
        margin: 1%;
        background: rgba(255,255,255,1);
        float: left;
        width: 31%;
        line-height: 40px;
        display: block;
        position: relative;
        text-align: center;
        border-radius: 2px;

        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        transition: all 0.3s ease;
        -moz-transition: all 0.3s ease;
        -webkit-transition: all 0.3s ease;
        -o-transition: all 0.3s ease;

    }
    .box ul li:hover {
        background: rgb(232 231 231);
    }
    .box ul li a {
        font-size: 14px;
        color: #6b7184;
    }
</style>
<body>
<div show="showSearch" class="search-container">
    <form class="search" action="https://www.baidu.com/s" method="get" target="_blank">
        <input name="wd" type="search" autofocus="autofocus" placeholder="百度一下"/>
        <button type="submit">搜索</button>
    </form>
    <ul class="history" for="item in searchHistory">
        <li><a href="{{item.href}}" target="_blank">{{item.word}}</a></li>
    </ul>
</div>
<div class="link-container">
    <div for="data in navData">
        <div class="box">
            <h3>{{data.group}}</h3>
            <ul for="link in data.links">
                <li><a href="{{link.href}}" target="_blank">{{toUpper(link.word)}}</a></li>
            </ul>
        </div>
    </div>
</div>
</body>
<script type="application/javascript" src="js/template.min.js"></script>
<script type="application/javascript">
    const ENTER_KEY_CODE = 13
    document.onkeydown = function (event) {
        if (event === ENTER_KEY_CODE) {
            window.open('https://www.baidu.com/s?wd=' + document.querySelector("input[name=wd]").value);
        }
    };
    let body = new TPL({
        el: "body",
        data: {
            showSearch: true,
            searchHistory: [
                {"word": "拜登时代", "href": "https://www.baidu.com/s?wd=%E6%8B%9C%E7%99%BB%E6%97%B6%E4%BB%A3"},
                {"word": "特朗普:现在放弃为时过早", "href": "https://www.baidu.com/s?wd=%E7%89%B9%E6%9C%97%E6%99%AE%3A%E7%8E%B0%E5%9C%A8%E6%94%BE%E5%BC%83%E4%B8%BA%E6%97%B6%E8%BF%87%E6%97%A9"}
            ],
            navData: [
                {"group": "常用 · 工作", "links": [{"word": "Google 翻译", "href": "https://translate.google.cn/"}, {"word": "百度指数", "href": "http://index.baidu.com/"}, {"word": "百度网盘", "href": "http://pan.baidu.com/"}, {"word": "有道词典", "href": "http://dict.youdao.com/"}, {"word": "印象笔记", "href": "https://www.yinxiang.com/"}, {"word": "QQ邮箱", "href": "https://mail.qq.com/"}, {"word": "网页版微信", "href": "https://wx.qq.com/"}, {"word": "必应", "href": "http://cn.bing.com/"}, {"word": "百度", "href": "https://www.baidu.com/"}]},
                {"group": "常用 · 社交", "links": [{"word": "微博", "href": "http://weibo.com/"}, {"word": "知乎", "href": "https://www.zhihu.com/"}, {"word": "豆瓣", "href": "https://www.douban.com/"}, {"word": "百度贴吧", "href": "http://tieba.baidu.com/index.html"}, {"word": "QQ空间", "href": "http://qzone.qq.com/"}, {"word": "简书", "href": "http://www.jianshu.com/"}, {"word": "果壳", "href": "http://www.guokr.com/"}, {"word": "V2EX", "href": "https://www.v2ex.com/"}, {"word": "虎扑", "href": "http://www.hupu.com/"}]},
                {"group": "常用 · 生活", "links": [{"word": "穷游", "href": "http://www.qyer.com/"}, {"word": "携程", "href": "http://www.ctrip.com/"}, {"word": "去哪儿", "href": "http://www.qunar.com/"}, {"word": "赶集网", "href": "http://ganji.com/"}, {"word": "58同城", "href": "http://58.com/"}, {"word": "饿了么", "href": "https://www.ele.me/"}, {"word": "百度糯米", "href": "https://nuomi.com/"}, {"word": "大众点评", "href": "http://www.dianping.com/"}, {"word": "美团", "href": "http://gz.meituan.com/"}]},
                {"group": "视频 · 音乐", "links": [{"word": "网易云音乐", "href": "http://music.163.com/"}, {"word": "QQ音乐", "href": "https://y.qq.com/"}, {"word": "音悦台", "href": "http://www.yinyuetai.com/"}, {"word": "虾米音乐", "href": "http://www.xiami.com/"}, {"word": "哔哩哔哩", "href": "https://www.bilibili.com/"}, {"word": "腾讯视频", "href": "https://v.qq.com/"}, {"word": "搜狐视频", "href": "http://tv.sohu.com/"}, {"word": "爱奇艺", "href": "http://www.iqiyi.com/"}, {"word": "优酷", "href": "http://www.youku.com/"}]},
                {"group": "消费 · 购物", "links": [{"word": "淘宝", "href": "https://www.taobao.com/"}, {"word": "天猫", "href": "https://www.tmall.com/"}, {"word": "京东", "href": "https://www.jd.com/"}, {"word": "1688", "href": "https://1688.com/"}, {"word": "小米网", "href": "http://www.mi.com/"}, {"word": "一号店", "href": "http://www.yhd.com/"}, {"word": "唯品会", "href": "http://www.vip.com/"}, {"word": "当当", "href": "http://www.dangdang.com/"}, {"word": "亚马逊", "href": "http://z.cn/"}]},
                {"group": "资讯 · 新闻", "links": [{"word": "好奇日报", "href": "http://www.qdaily.com/"}, {"word": "铁血网", "href": "http://www.tiexue.net/"}, {"word": "汽车之家", "href": "http://www.autohome.com.cn/"}, {"word": "人民网", "href": "http://www.people.com.cn/"}, {"word": "搜狐", "href": "http://www.sohu.com/"}, {"word": "网易", "href": "http://www.163.com/"}, {"word": "百度新闻", "href": "http://news.baidu.com/"}, {"word": "腾讯", "href": "http://news.qq.com/"}, {"word": "新浪", "href": "http://www.sina.com.cn/"}]},
                {"group": "工作 · 求职", "links": [{"word": "前程无忧", "href": "https://www.51job.com/"}, {"word": "智联招聘", "href": "http://www.zhaopin.com/"}, {"word": "乔布堂", "href": "http://cv.qiaobutang.com/"}, {"word": "兼职猫", "href": "https://www.jianzhimao.com/"}, {"word": "庞果网", "href": "http://job.csdn.net/"}, {"word": "内推网", "href": "http://www.neitui.me/"}, {"word": "大街", "href": "https://www.dajie.com/"}, {"word": "猎聘", "href": "https://www.liepin.com/"}, {"word": "拉勾", "href": "https://www.lagou.com/"}]},
                {"group": "娱乐 · 游戏", "links": [{"word": "NGA", "href": "http://bbs.ngacn.cc/"}, {"word": "3DM", "href": "http://www.3dmgame.com/"}, {"word": "游民星空", "href": "http://www.gamersky.com/"}, {"word": "52pk", "href": "http://www.52pk.com/"}, {"word": "178", "href": "http://www.178.com/"}, {"word": "4399", "href": "http://www.4399.com/"}, {"word": "电玩巴士", "href": "http://www.tgbus.com/"}, {"word": "17173", "href": "https://www.17173.com/"}, {"word": "多玩", "href": "http://www.duowan.com/"}]},
                {"group": "金融 · 银行", "links": [{"word": "平安银行", "href": "http://bank.pingan.com/"}, {"word": "民生银行", "href": "http://www.cmbc.com.cn/"}, {"word": "广发银行", "href": "http://www.cgbchina.com.cn/"}, {"word": "交通银行", "href": "http://www.bankcomm.com/BankCommSite/default.shtml"}, {"word": "中国银行", "href": "http://www.boc.cn/"}, {"word": "农业银行", "href": "http://www.abchina.com/cn/"}, {"word": "建设银行", "href": "http://www.ccb.com/cn/home/indexv3.html"}, {"word": "工商银行", "href": "http://www.icbc.com.cn/icbc/"}, {"word": "招商银行", "href": "http://www.cmbchina.com/"}]}
            ]
        },
        methods:{
            init:function(){
                this.render();
            },
            toUpper:function(str) {
                return str.toUpperCase()
            }
        }
    })
    window.onload = function () {
        body.init()
    }
</script>
</html>

js/template.js

let TPL = function (params) {

    let variables = {};
    let alias = {};
    let reg = /([a-zA-Z_][0-9a-zA-Z_]*)|(\.[a-zA-Z_][0-9a-zA-Z_]*)/g;

    let translateExpress = function (express, alias) {
        //console.debug("original ======= " + express)
        let result;
        for (let key in alias) {
            let i = 10;
            while ((result = reg.exec(express)) != null && i-- > 0) {
                if (result[0] === key) {
                    reg.lastIndex = 0;
                    express = express.substring(0, result.index) + alias[key] + express.substring(result.index + key.length, express.length);
                }
            }
        }
        //console.debug("finally  ======= " + express)
        return express;
    }

    alias.put = function (key, properties, index) {
        this[key] = translateExpress(properties, this);
        this[key] = index === undefined ? this[key] : this[key] + '[' + index + ']';
    }

    this.create = function () {
        this.el = params.el
        for (let x in params.data) {
            this[x] = params.data[x]
            alias.put(x, "this." + x)
        }
        for (let method in params.methods) {
            this[method] = params.methods[method]
            alias.put(method, "this." + method)
        }
    }

    this.render = function (node) {
        const TEXT_NODE = 3

        // 设置默认数据
        node = node ? node : document.querySelector(this.el)
        node.id = node.id === '' ? node.id = this.guid() : node.id;

        // 渲染if属性
        if (node.hasAttribute && node.hasAttribute('if')) {
            if (!this.calculate(node.getAttribute('if'))) {
                node.remove()
                return
            }
        }

        // 渲染文本节点
        if (node.nodeType === TEXT_NODE && this.hasEl(node.data)) {
            let text = node.data
            const [prefix, suffix, express] = this.getEl(text);
            node.data = prefix + this.calculate(express) + suffix
        }

        // 渲染节点属性
        if (node.getAttributeNames) {
            node.getAttributeNames().forEach(attrName => {
                let text = node.getAttribute(attrName)
                if (this.hasEl(text)) {
                    const [prefix, suffix, express] = this.getEl(text);
                    node.setAttribute(attrName, prefix + this.calculate(express) + suffix)
                    // // 正向渲染
                    // this.binding(node, express, function (node, variable) {
                    //     if (node.tagName === "INPUT" && attrName === "value") {
                    //         node.value = variable;
                    //     } else {
                    //         node.setAttribute(attrName, prefix + variable + suffix)
                    //     }
                    // });
                    // // 反向更新
                    // this.watch(node, attrName, function (value) {
                    //     variables[express] = value;
                    // })
                }
            })
        }

        // 渲染show属性
        if (node.hasAttribute && node.hasAttribute('show')) {
            let express = node.getAttribute('show');
            node.style.display = this.calculate(express) ? '' : 'none';
        }

        // 渲染循环节点
        if (node.hasAttribute && node.hasAttribute('for')) {
            let text = node.getAttribute('for')
            let items = text.split(/ in | of /i)

            let subNodeTemplates = []
            for (let i = 0; i < node.childNodes.length; i++) {
                subNodeTemplates.push(node.childNodes[i].cloneNode(true))
            }
            node.innerHTML = ""

            this.calculate(items[1]).forEach((subData, index) => {
                subNodeTemplates.forEach(template => {
                    alias.put(items[0], items[1], index)
                    let clone = template.cloneNode(true)
                    this.render(clone)
                    node.append(clone)
                })
                //console.log(clone)
            })
            return
        }

        // 递归渲染子节点
        [].slice.call(node.childNodes).forEach(child => {
            this.render(child)
        })
    }

    this.hasEl = function (str) {
        return /{{.*}}/.test(str)
    }

    this.getEl = function (str) {
        return [
            str.substring(0, str.indexOf("{{")),
            str.substring(str.indexOf("}}") + 2),
            str.substring(str.indexOf("{{") + 2, str.indexOf("}}")).trim()
        ]
    }

    // 计算表达式
    this.calculate = function (express) {
        try {
            return eval(translateExpress(express, alias))
        } catch (e) {
            console.error(e.message)
            return null
        }
    }

    this.guid = function () {
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
            let r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
            return v.toString(16);
        });
    }

    this.binding = function (node, variable, cb) {
        this.binding[variable] = node.id;
        Object.defineProperty(this, variable, {
            set: function (value) {
                variables[variable] = value;
                cb(node, variables[variable]);
            },
            get: function () {
                return variables[variable];
            }
        });
    }

    this.watch = function (targetNode, attribute, cb) {
        if ("value" === attribute) {
            targetNode.oninput = function (e) {
                cb(targetNode.value);
            }
            return
        }
        let observer = new MutationObserver(function (mutationsList) {
            mutationsList.forEach(function (item) {
                if (attribute === item.attributeName) {
                    cb(targetNode.getAttribute(attribute));
                }
            });
        });
        observer.observe(targetNode, {attributes: true});
    }

    this.create()
}

页面样式参考小呆导航
js参考一个基于jquery的仿vue的简易js模板(支持ie8)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值