原生H5封装并使用vue的相关命令(v-for v-if v-html)

原生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)
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_小郑有点困了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值