原生H5封装并使用vue的相关命令
场景:当你使用vue开发习惯了的时候,再写原生html和js是不是有点感觉麻烦,不能响应式,而且循环个页面的数据还要用字符串拼接,于是乎就自己把这些常用的命令封装起来。有“v-for/v-if/v-html”等等。
注:“v-for”在循环元素上面还存在一点bug
效果图:
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"/>
<meta name="format-detection" content="telephone=no">
<title>简易H5框架</title>
<!-- <script src="./js/common.js"></script> -->
<script src="./js/myCommon.js"></script>
<script src="./js/config.js"></script>
<link rel="stylesheet" href="./css/index.css">
<style>
.f-ol{
color: #ff6767;
}
.s-ol{
color: gold;
}
</style>
</head>
<body>
<div id="datas">
<h1>数组一</h1>
<ol>
<li v-for="modelsData1" class="f-ol">
<div>姓名:<span v-html="name"></span></div>
<div>性别:<span v-html="sex"></span></div>
<div>年龄:<span v-html="age"></span></div>
</li>
</ol>
<h1>数组二</h1>
<ol>
<li v-for="modelsData2" class="s-ol" v-if="name!='小亮'">
<div>姓名:<span v-html="name"></span></div>
<div>性别:<span v-html="sex"></span></div>
<div>年龄:<span v-html="age"></span></div>
</li>
</ol>
<h2>弹框事件</h2>
<button id="btn" onclick="$event.touch()">touch me</button>
</div>
<script src="./js/index.js"></script>
</body>
</html>
common.js:
/**
* alert 弹框
* @param option 如 defaultOption 所示, 或者直接串字符串进来
*/
// 重写alert 类似confirm的样子
window.alert = function (option) {
var defaultOption = {
msg: '提示信息',
title: '提示',
confirmText: '我知道了',
callback: function () {}
}
//直接传文字进来
if (typeof option !== 'object') option = {msg: option}
option = Object.assign(defaultOption, option)
var alertElem = document.createElement('div')
//干嘛用ny 回调函数
window.alertCb = option.callback
alertElem.className = 'dialog'
alertElem.id = 'dialog'
var innerHTML = ''
innerHTML += '<div class="dialog-cont">'
innerHTML += '<p class="dialog-cont-title">' + option.title + '</p>'
innerHTML += '<div class="dialog-cont-text">' + option.msg + '</div>'
innerHTML += '<button type="primary" οnclick="window.alertCb();window.removeDialog()">' + option.confirmText + '</button>'
innerHTML += '</div>'
alertElem.innerHTML = innerHTML
document.body.appendChild(alertElem)
}
//关闭回调
window.alertCb = function () {}
// 重写confirm
window.confirm = function (option) {
var defaultOption = {
msg: '提示信息',
title: '提示',
callback: function () {},
confirmText: '确定',
cancelText: '取消'
}
if (typeof option !== 'object') option = {msg: option}
option = Object.assign(defaultOption, option)
window.confirm.callback = function (isOk) {
if (isOk) option.callback()
window.removeDialog()
}
var alertElem = document.createElement('div')
alertElem.className = 'dialog'
alertElem.id = 'dialog'
var innerHTML = ''
innerHTML += '<div class="dialog-cont">'
innerHTML += '<p class="dialog-cont-title">' + option.title + '</p>'
innerHTML += '<div class="dialog-cont-text">' + option.msg + '</div>'
innerHTML += '<button type="primary" οnclick="window.confirm.callback(true)" style="margin-right:20px">' + option.confirmText + '</button>'
innerHTML += '<button οnclick="window.confirm.callback(false)">' + option.cancelText + '</button>'
innerHTML += '</div>'
alertElem.innerHTML = innerHTML
document.body.appendChild(alertElem)
}
/**
* 隐藏弹框
*/
window.removeDialog = function () {
var dialog = document.getElementById('dialog')
document.body.removeChild(dialog)
}
/* 关闭网页 */
window.closeWindow = function () {
close()
document.addEventListener('WeixinJSBridgeReady', close)
function close () {
window.WeixinJSBridge && WeixinJSBridge.invoke('closeWindow')
window.AlipayJSBridge && AlipayJSBridge.call('closeWebview')
window.open('', '_self', '')
window.close()
}
}
/**
* 模拟 jquery 的极小部分功能
* @param selector 选择器
* @returns 模拟 jquery
*/
window.$ = function (selector) {
var target = typeof selector === 'string' ? document.querySelectorAll(selector) : selector
//啥情况会用这个?
if (!(target instanceof NodeList)) {
target = [target]
}
return new JQ(target)
}
//$就是来自JQ,那么JQ原型上的方法就可以在$().render的时候被调用 没啥问题
// 这个地方搞啥用?
function JQ (nodeList) {
var self = this
Array.prototype.forEach.call(nodeList, function (item, index) {
self[index] = item
self.length = index + 1
})
}
//JQ.prototype = [] ?为啥是[] 不是{}
Object.assign(JQ.prototype = [], {
///
addClass: function (className) {
this.forEach(function (item) {
item.className += ' ' + className
})
return this
},
removeClass: function (className) {
this.forEach(function (item) {
item.className = item.className.replace(new RegExp('\\s*' + className, 'g'), '')
})
return this
},
show: function () {
this.forEach(function (item) {
item.style.display = 'block'
})
return this
},
hide: function () {
this.forEach(function (item) {
item.style.display = 'none'
})
return this
},
remove: function () {
this.forEach(function (item) {
item.remove() // https://developer.mozilla.org/zh-CN/docs/Web/API/ChildNode/remove
})
},
//有bug
render: function (data, option) {
// console.log('识别命令',data,option)
//第一次:进来啥也没有undefined 第二次:下面会通过this进行识别 重新调用这个渲染函数识别命令 {forCmd: "不存在的", htmlCmd: "v-html-8635", ifCmd: "v-if-8635"}
option = option || {}
var forCmd = option.forCmd || 'v-for'
var htmlCmd = option.htmlCmd || 'v-html'
var ifCmd = option.ifCmd || 'v-if'
var bindCmd = option.bindCmd || 'v-bind'
var disabledCmd = option.disabledCmd || 'v-disabled'
var self = this
console.log(this) //应该是递归执行 最初的this等于$('#datas')
//第一次:JQ [div#datas] 第二次:JQ [ol]
this.forEach(function (item,index) {
/******************** v-for **************************/
var vFor = item.querySelectorAll('[' + forCmd + ']')
// console.log('这是获取该元素下所有的包含v-for的节点数组:',item.querySelectorAll('[' + forCmd + ']'))
//第一次:NodeList(2) [li.f-ol, li.s-ol] 第二次:NodeList []
$(vFor).forEach(function (v) { //v 这就是vfor所在的元素节点
//分别循环每个需要循环的东西
var parent = v.parentNode //v-for元素的父节点 v-for元素的父节点以及下面的的自身
var key = v.getAttribute(forCmd) //当前循环的数组名称
var html = v.outerHTML //好像基本结构的意思
var outerHTML = html
var currentForCmd = 'v-for-' //这个currentForCmd 出现在元素身上 每次循环的唯一标识
var currentHtmlCmd = 'v-html-'
var currentIfCmd = 'v-if-'
var currentDisabledCmd = 'v-disabled-'
if (!data[key] instanceof Array) {
console.log(data, key)
}
//模板生成 data[key] //当前循环的数组 因为data是对象 key是循环的数组名,所以这样拿
data[key].forEach(function (dItem, index) {
outerHTML += html //先循环后匹配替换里面的键名 下面v-html进行匹配赋值 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//懂了 css把所有带有v-for的都隐藏了 这里把数据数组渲染出来的vfor加上特殊标识 再结合css就可以去重了
//没有去除空白的模板
.replace(new RegExp(forCmd + '=".+?"'), currentForCmd + 'item') // 第一个 v-for 要替换掉
//目前只能一层循环
.replace(new RegExp(htmlCmd + '="(.+?)"', 'g'), currentHtmlCmd + '="[' + index + '].$1"') //这里加了index !!!!!!!!!!!!!!!!!!!!!!!!!!
.replace(new RegExp(ifCmd + '="(.+?)"', 'g'), currentIfCmd + '="[' + index + '].$1"')
.replace(new RegExp(disabledCmd + '="(.+?)"', 'g'), currentDisabledCmd + '="[' + index + '].$1"')
})
v.outerHTML = outerHTML //整合一波之后 重新调用渲染函数 已经循环完了 就去除掉v-for
$(parent).render(data[key], {forCmd: '不存在的', htmlCmd: currentHtmlCmd, ifCmd: currentIfCmd})
})
/******************** v-html **************************/
var vHtml = $(item.querySelectorAll('[' + htmlCmd + ']'))
vHtml.forEach(function (v) {
// console.log(htmlCmd,v.getAttribute(htmlCmd)) //上面循环的时候就已经指定了使用哪个索引下面的值
//v-html-2747 [0].age 也指定了本次循环的目标数组data !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
var value = self.getData(v.getAttribute(htmlCmd), data)
v.innerHTML = value === null ? 'null' : value
})
/******************* v-if *************************/
// '[v-if]:not([v-for] [v-if])' 是不合法的选择器
var vIf = $(item.querySelectorAll('[' + ifCmd + ']'))
vIf.forEach(function (v) {
v.style.display = self.getData(v.getAttribute(ifCmd), data) ? 'block' : 'none'
})
})
// return this //return个啥呀 哪里也没接收
},
getData: function (key, data) {
var value
try {
if (/^\[/.test(key)) {
var index = key.match(/^\[(.+?)\]/)[1]
key = key.replace(/^\[.+?\]\.*/, '')
data = data[index]
}
with (data) {
value = eval(key)
}
} catch (e) {
console.log(data, '表达式:' + key + '无效, 可能是数据里没有这个属性, 或者是表达式真的错了')
}
return value
},
})
// ajax
/**
* 业务 http 请求
* @param uri 接口文档地址, 比如: GET /parkingOrder/{parkingOrderId}/payInfo
* @param input 入参
* @param callback 响应
* @param final 不论成功失败都执行
*/
window.http = function (uri, input,type, callback, final) {
http.loading.startHttp(uri)//旋转加载图标
//入参少了input的时候
if (typeof input === 'function') {
final = callback
callback = input
input = {}
}
var res = ''
var realUri = uri
baseHTTP({
uri: realUri,
input: input,
type:type,
success: function (responce) {
res = responce
},
error: function (http) {
res = {code: http.status, msg: '网络错误 http.status: ' + http.status, http: http}
alert({msg:realUri+'接口出错,请退出重试', callback: window.closeWindow})
},
complete: function () {
http.loading.completeHttp(uri)
callback && callback(res, realUri)
}
})
}
/**
* 底层 http 请求
* @param option 如 defaultOption 所示
*/
function baseHTTP (option) {
var defaultOption = {
uri: '',
input: {},
id: '',
type:'GET',
host: window.config.host,
success: function (res) {}, // 请求成功
error: function (err) {}, // 请求错误
complete: function () {} // 请求完成
}
option = Object.assign(defaultOption, option)
var http = new XMLHttpRequest()
http.onreadystatechange = function () {
if (http.readyState === 4) {
if (http.status === 200) {
var result = JSON.parse(http.responseText)
option.success(result)
} else {
option.error(http)
}
option.complete(http)
}
}
var type = option.type?option.type:'GET'//ny 判断get/post \s匹配任何空白字符 [^\s]匹配任何非空白字符
var url = option.host + option.uri//id 在上一步已近被替换了,在这应该用不到了
if (type === 'GET') {
var getParam = '?'
option.input && Object.keys(option.input).forEach(function (key) {
var item = option.input[key]
if (item && typeof item === 'object') {
getParam += key + '=' + JSON.stringify(item) + '&'
} else {
getParam += key + '=' + (item || '') + '&'
}
})
//将最后一个&结尾字符去掉
url += getParam.replace(/[&|?]$/, '')
}
if(type === 'POST'){
}
//处理当open之前--->readyState=4 之前发生的错误
http.onerror = function(){
// 处理连接级别的错误
option.error(http)
return
};
console.log(type,url)
http.open(type, url, true)
http.setRequestHeader('Content-type', 'application/json')
localStorage.sessionId && http.setRequestHeader('sessionId', localStorage.sessionId)
//超过7秒视为超时
http.timeout = 7000
http.ontimeout=function(){
alert({msg: option.uri+'接口'+objs.type+',请退出重试', callback: window.closeWindow})
}
http.send(JSON.stringify(option.input))
}
/** 如果请求时间超过 1 秒钟还没响应, 那么自动 loading **/
http.loading = {
timeout: 1000,
timeoutList: {},
startHttp: function (uri) {
clearTimeout(this.timeoutList[uri]) // TODO 同一个请求在没有响应的时候又发起了, 那么这时有漏洞
this.timeoutList[uri] = setTimeout(this.show, this.timeout)
},
completeHttp: function (uri) {
//判断是否存在接口超时的情况 如果有就提示,跳出弹框 关闭页面
clearTimeout(this.timeoutList[uri])
this.timeoutList[uri] = undefined
this.hide()
},
show: function () {
if (document.body.className.indexOf('http-loading') > -1) return
$(document.body).addClass('http-loading')
},
hide: function () {
var isOk = true // 可以隐藏
var t = this
//这里用forEach 不好 用some 之类的可能会好(兼容?)
Object.keys(t.timeoutList).forEach(function (key) {
var item = t[key]
if (item !== undefined) {
isOk = false
}
})
if (!isOk) return
$(document.body).removeClass('http-loading')
}
}
有了上面common.js的处理,就可以很好的渲染数据了
这里面还有手动封装的生命周期函数,后续补上博客
index.js:
var data = {
modelsData1:[
{
name:'小明',
sex:"男",
age:13
},
{
name:'小红',
sex:"女",
age:12
},
],
modelsData2:[
{
name:'小亮',
sex:"男",
age:13
},
{
name:'小刘',
sex:"女",
age:12
},
]
}
var life = {
created: function () {
console.log('when')
$('#datas').render(data)
},
}
var $event = {
touch:function(){
methods.touchMethods()
}
}
var methods = {
touchMethods:function(){
// window. alert({
// msg: '提示个锤子',
// callback: window.closeWindow
// })
window.confirm({
msg: '提示个锤子',
callback: window.closeWindow
})
}
}
life.created && life.created()
life.mounted && document.body.addEventListener('load', life.mounted)