web component 【Template】 创建自己的简单SPA应用
来源:互联网 发布:淘宝贷款15万骗局 编辑:程序博客网 时间:2024/06/07 09:44
看到web component这么火爆,抽空写一篇关于template的博文,怎样创建一个简单的SPA应用。
什么是Template
在web component 中,template和它的名字一样,是一个模板标签,在它创建的上下文中,浏览器是不会去解析的,甚至不会去加载里面的任何资源,浏览器dom解析到template标签就跳过了,比如我们这样写 :
<template> <img src='http://localhost:9999/imgs/icon.png' /></template>
这段代码是要求发送一个请求至http://localhost:9999/imgs/icon.png,得到一张图片,而事实并不像我们想的这样,因为它被包含于Template的上下文中,浏览器根本就不会去理会。
那么这样做的好处在哪儿,我们为什么需要Template标签。
因为template标签的特性,我们甚至可以创建一大堆的视图模板,根据需求渲染相应的视图模板,而不必担心浏览器渲染的性能问题,因为Template是不会被浏览器渲染的,我们可以写一个js脚本来让它根据需求进行渲染,实现一个懒加载的效果。
如何根据template的特性创建一个简单的SPA应用
现在来进入今天的正题,以上简单的带过了一下template,可能大家对Template已经有了一个大概的轮廓,别急我们这就来进入实战,趁热打铁吧。
在实际的开发中,我们很多情况是需要多个视图模板的,这里作为demo我创建了两个视图模板,同时还有两个按钮,点击相应的按钮加载相应的视图模板。
整个demo大概是这样的
用一个h1标签显示index表示这是主页,随后有两个按钮,zhangsan 和 lisi
点击zhangsan,显示zhangsan的相关信息
点击lisi则显示lisi的相关信息
这样的一个简单的应用用的是传统pjax,即history API + Ajax 应用,不过源于作为演示,并没有搭建服务器后端,因此主要作用于history API。
现在贴上html部分源码
<template id="zhangsanTpl"> <h1>i'm ZhangSan</h1> <ul> <li>姓名:ZhangSan</li> <li>年龄:20</li> <li>性别: 男</li> </ul> </template> <template id="lisiTpl"> <h1>i'm Lisi</h1> <ul> <li>姓名:Lisi</li> <li>年龄:18</li> <li>性别: 男</li> </ul> </template> <div class="target"> <h1>Index</h1> </div> <div> <button onclick="targetZhangsan()">zhangsan</button> <button onclick="targetLisi()">lisi</button> </div>
这里用了两个template表示两个视图模板,分别封装了zhangsan和lisi的相关信息,介于template上下文中的节点在浏览器中是不会被渲染出来的,所以,我们还需要一个js脚本来帮我们做这些事情。
let targetDiv = document.querySelector(".target");//清除显示容器let cleanTargetDiv = () => targetDiv.innerHTML = "";//添加模板到容器let appendTpl = tpl => targetDiv.appendChild(document.importNode(tpl.content,true));//改变路由let updateUrl = url => { if(Object.prototype.toString.call(url) == '[object String]'){ history.pushState({},url,`/${url}`); } else { throw new Error('url must be a string'); }}//初始化路由updateUrl("host");//zhangsan点击回调let targetZhangsan = () => { cleanTargetDiv(); updateUrl('zhangsan'); appendTpl(document.querySelector('#zhangsanTpl'));}//lisi点击回调let targetLisi = () => { cleanTargetDiv(); updateUrl('lisi'); appendTpl(document.querySelector('#lisiTpl'));}
有点懵?别急,我一步一步的来说
首先我们需要拿到的是目标div的DOM对象,也就是我们需要渲染的容器
let targetDiv = document.querySelector(".target");
随后我们这里定义了两个方法用于,清除容器当前内容和添加视图模板到容器(可以简单的理解为显示当前视图模板)
//清除显示容器let cleanTargetDiv = () => targetDiv.innerHTML = "";//添加模板到容器let appendTpl = tpl => targetDiv.appendChild(document.importNode(tpl.content,true));
接下来的不可少的一步是更新路由,我们需要实现点击相关按钮,更新容器内容的同时还要更新路由。
比如我们上面点击zhangsan,路由就变为http://127.0.0.1:8020/zhangsan
点击lisi就变为http://127.0.0.1:8020/lisi
//改变路由let updateUrl = url => { if(Object.prototype.toString.call(url) == '[object String]'){ history.pushState({},url,`/${url}`); } else { throw new Error('url must be a string'); }}
这个方法接收一个参数,这里参数的命名用url表现的有点唐突,因为这里我们需要传入的仅仅只是一个相关路由,而不是整个url,但是由于时间关系,没有做修改,没事将就看。
在方法内部,我们做了一个判断,判断传入参数的类型是否是一个字符串,添加这个限制的原因,是由于传入错误类型导致整个应用崩溃,这“多此一举”主要归功于js的鸭子类型,但是为什么不用TS来写?有类型限制,类型声明不是更好?但是,由于本篇博文讲解的是用原生js来实现,因此这里避开使用TS吧。
如果参数类型不是一个字符串则抛出一个异常。异常信息为’url must be a string’。
//初始化路由updateUrl("host");
上面这句话是表明刚进入应用时的路由变更为http://127.0.0.1:8020/host,更加具有语义性。
//zhangsan点击回调let targetZhangsan = () => { cleanTargetDiv(); updateUrl('zhangsan'); appendTpl(document.querySelector('#zhangsanTpl'));}//lisi点击回调let targetLisi = () => { cleanTargetDiv(); updateUrl('lisi'); appendTpl(document.querySelector('#lisiTpl'));}
上面是为了给两个按钮绑定监听事件,并在元素上进行声明绑定
<button onclick="targetZhangsan()">zhangsan</button><button onclick="targetLisi()">lisi</button>
回调函数里的三条语句并不难,调用清除当前容器的方法和更新路由的方法。
重点在第三句。
//lisi
appendTpl(document.querySelector('#lisiTpl'));
//zhangsan
appendTpl(document.querySelector('#zhangsanTpl'));
现在回头看看上面的方法声明
//添加模板到容器let appendTpl = tpl => targetDiv.appendChild(document.importNode(tpl.content,true));
这个方法首先拿到了目标容器(.target)的DOM对象,随后向用appendChild添加子元素,而添加的子元素是
document.importNode(tpl.content,true)
document.importNode 用于递归import子节点,第一个参数为需要import的节点,第二个参数为一个boolean值,表示是否需要递归import所有的子节点。
tpl.content
tpl是该方法传入进来的参数,它是一个需要显示的视图模板的DOM对象,该DOM对象有一个content 属性表示该template下所有的子节点。
整个方法可以理解为,将要显示的template里的子节点都复制一份拿出来,然后append到目标div容器里进行显示。
现在就初步的完成一个简单的SPA应用,基于template。
下面贴上该demo完整的源码
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Document</title></head><body> <template id="zhangsanTpl"> <h1>i'm ZhangSan</h1> <ul> <li>姓名:ZhangSan</li> <li>年龄:20</li> <li>性别: 男</li> </ul> </template> <template id="lisiTpl"> <h1>i'm Lisi</h1> <ul> <li>姓名:Lisi</li> <li>年龄:18</li> <li>性别: 男</li> </ul> </template> <div class="target"> <h1>Index</h1> </div> <div> <button onclick="targetZhangsan()">zhangsan</button> <button onclick="targetLisi()">lisi</button> </div> <script type="text/javascript"> let targetDiv = document.querySelector(".target"); //清除显示容器 let cleanTargetDiv = () => targetDiv.innerHTML = ""; //添加模板到容器 let appendTpl = tpl => targetDiv.appendChild(document.importNode(tpl.content,true)); //改变路由 let updateUrl = url => { if(Object.prototype.toString.call(url) == '[object String]'){ history.pushState({},url,`/${url}`); } else { throw new Error('url must be a string'); } } //初始化路由 updateUrl("host"); //zhangsan点击回调 let targetZhangsan = () => { cleanTargetDiv(); updateUrl('zhangsan'); appendTpl(document.querySelector('#zhangsanTpl')); } //lisi点击回调 let targetLisi = () => { cleanTargetDiv(); updateUrl('lisi'); appendTpl(document.querySelector('#lisiTpl')); } </script></body></html>
如果有错误或者有疑问请在下面回复给我吧,我会及时的改正或答复。
这里附上本文的源码。在我的GitHub 上,欢迎大家学习下载
https://github.com/HaoDaWang/SPA-for-Template
- web component 【Template】 创建自己的简单SPA应用
- 单页Web应用(SPA)的简单介绍
- 单页web应用(SPA)的简单介绍
- Yii 框架创建自己的 web 应用
- Yii 框架创建自己的 web 应用
- SPA 单页Web应用
- SPA 单页Web应用
- StateMan:创建一个简单的SPA实例(一)
- StateMan:创建一个简单的SPA实例(二)
- SPA(单页面应用)的简单实现
- simple-spa 一个简单的单页应用实例
- simple-spa 一个简单的单页应用实例
- simple-spa 一个简单的单页应用实例
- simple-spa 一个简单的单页应用实例
- 单页面应用(SPA)的简单介绍
- 创建简单Web应用
- 创建一个简单的Web Service应用
- 【SPA】单页Web应用(一)
- SpaceNet on AWS遥感数据集下载教程
- Machine Learning In Action
- Spark 之 调优 篇
- POJ 1827 A Bunch Of Monsters(贪心)
- 记录jsp开发中的问题
- web component 【Template】 创建自己的简单SPA应用
- HDU 6058 Kanade's sum
- CSU-ACM2017暑假集训比赛2 HDU
- 迟到的2017年上半年年中总结
- HDU-1425(水题)
- js定时器(三)向左向右
- Qt项目crash常见原因以及解决办法
- HDU 1070找最便宜的牛奶
- 一些弱智BUG