首页
留言
友链
关于
Search
1
思源笔记docker私有化部署及使用体验分享
2,529 阅读
2
windows11 远程提示:为安全考虑,已锁定该用户帐户,原因是登录尝试或密码更改尝试过多。
1,147 阅读
3
解决 nginxProxyManager 申请证书时的SSL失败问题
657 阅读
4
Pointer-Focus:一款功能强大的教学、录屏辅助软件
635 阅读
5
使用cspell对项目做拼写规范检查
598 阅读
Web前端
CSS
JavaScript
交互
Vue
小程序
后端
运维
项目
生活
其他
转载
软件
职场
登录
Search
标签搜索
docker
DevOps
magic-boot
Linux
酷壳
frp
RabbitMQ
gitlab
Node
git
工具
MybatisPlus
clickhouse
Syncthing
规范
前端
产品
nginx
markdown
axios
朱治龙
累计撰写
146
篇文章
累计收到
9
条评论
首页
栏目
Web前端
CSS
JavaScript
交互
Vue
小程序
后端
运维
项目
生活
其他
转载
软件
职场
页面
留言
友链
关于
搜索到
146
篇与
朱治龙
的结果
2020-07-04
JS小技巧
数字转字符串/字符串转数字let num = 15 let s = num + '' // number to string let n = +s // string to number填充数组let filledArray = new Array(10).fill(null).map((item, idx)=> ({'name' : 'value' + idx})) // filledArray = [{"name":"value0"},{"name":"value1"}]对象的动态属性 let dynamic = "value" let user = { id: 1, [dynamic]: "other value" } // user = {id: 1, value: 'other value'}删除重复项let array = [100, 23, 23, 23, 23, 67, 45] let outputArray = Array.from(new Set(array)) //outputArray = [100, 23, 67, 45]数组转对象let arr = ["value1", "value2", "value3"] let arrObject = {...arr} // arrObject = {0: 'value1', 1: 'value2', 2: 'value3'}对象转数组 let n umber = { one: 1, two: 2, } let key = Object.keys(numbers) // key = [ 'one', 'two' ] let value = Object.values(numbers) // value = [ 1, 2 ] let entry = Object.entries(numbers) // entry = [['one' : 1], ['two' : 2]]使用 ^ 检查数字是否不相等let a = 1234 if(a^123) { console.log('not equal') } // 输出:not equal循环对象 const userObj = { shiyu: 32, zhuzl: 36 }; // 方法一 - 获取对象 keys 后循环 Object.keys(userObj).forEach(key => userObj[key]++) console.log(userObj) // { shiyu: 33, zhuzl: 37 } // 方法二:实用 for ..in 循环 for(let key in userObj){ userObj[key]++ } console.log(userObj) // { shiyu: 34, zhuzl: 38 }对象键按插入顺序存储 const obj = { name: "zhuzl", age: 36, address: "湖南省长沙市", profession: "前端开发工程师" } console.log(Object.keys(obj)) // name, age, address, profession
2020年07月04日
11 阅读
0 评论
0 点赞
2020-06-05
VPS常用测试脚本
经常买VPS的人一般买来个新VPS都会跑一遍脚本,这里整理出来方便自己也方便看官。主要是测试vps的性能和网速:Bench.sh秋水逸冰大佬的写的Bench.sh脚本特点显示当前测试的各种系统信息;取自世界多处的知名数据中心的测试点,下载测试比较全面;支持 IPv6 下载测速;IO 测试三次,并显示平均值。使用wget -qO- bench.sh | bash # OR curl -Lso- bench.sh | bash # OR wget -qO- 86.re/bench.sh | bash 示例效果:SuperBench.sh老鬼大佬的SuperBench测试脚本特点改进了显示的模式,基本参数添加了颜色,方面区分与查找。I/O测试,更改了原来默认的测试的内容,采用小文件,中等文件,大文件,分别测试IO性能,然后取平均值。速度测试替换成了 Superspeed 里面的测试,第一个默认节点是,Speedtest 默认,其他分别测试到中国电信,联通,移动,各三个不同地区的速度。使用wget -qO- --no-check-certificate https://ghproxy.com/https://raw.githubusercontent.com/oooldking/script/master/superbench.sh | bash # OR curl -Lso- -no-check-certificate https://ghproxy.com/https://raw.githubusercontent.com/oooldking/script/master/superbench.sh | bashLemonBench.shLemonBench工具(别名LBench、柠檬Bench),是一款针对Linux服务器设计的服务器性能测试工具。通过综合测试,可以快速评估服务器的综合性能,为使用者提供服务器硬件配置信息。特点服务器基础信息(CPU信息/内存信息/Swap信息/磁盘空间信息等)Speedtest网速测试 (本地到最近源及国内各地域不同线路的网速)磁盘测试(4K块/1M块 直接写入测试)路由追踪测试(追踪到国内和海外不同线路的路由信息)Spoofer测试(获取详细网络信息,快速判断服务器接入线路)使用curl -fsL https://ilemonra.in/LemonBenchIntl | bash -s fast
2020年06月05日
13 阅读
0 评论
0 点赞
2020-03-25
浅谈vue中axios的封装
在Vue项目中我们一般都会使用 axios 作为 Ajax 请求库。它是基于promise的http库,他有很多优秀的特性,例如拦截请求和响应、取消请求、转换json、客户端防御cSRF等。所以我们的尤大大也是果断放弃了对其官方库vue-resource的维护,直接推荐我们使用axios库。在实际开发过程我们一般都会将常用的方法做一些封装以便于在项目中调用。工程添加axios依赖yarn add axios -S建立axios封装文件htttp.jsimport axios from 'axios'; import { Message } from 'element-ui'; axios.defaults.timeout = 5000; axios.defaults.baseURL =''; //http request 拦截器 axios.interceptors.request.use( config => { // const token = getCookie('名称');注意使用的时候需要引入cookie方法,推荐js-cookie config.data = JSON.stringify(config.data); config.headers = { 'Content-Type':'application/x-www-form-urlencoded' } // if(token){ // config.params = {'token':token} // } return config; }, error => { return Promise.reject(err); } ); //http response 拦截器 axios.interceptors.response.use( response => { if(response.data.errCode ==2){ router.push({ path:"/login", query:{redirect:router.currentRoute.fullPath}//从哪个页面跳转 }) } return response; }, error => { return Promise.reject(error) } ) /** * 封装get方法 * @param url * @param data * @returns {Promise} */ export function fetch(url,params={}){ return new Promise((resolve,reject) => { axios.get(url,{ params:params }) .then(response => { resolve(response.data); }) .catch(err => { reject(err) }) }) } /** * 封装post请求 * @param url * @param data * @returns {Promise} */ export function post(url,data = {}){ return new Promise((resolve,reject) => { axios.post(url,data) .then(response => { resolve(response.data); },err => { reject(err) }) }) } /** * 封装patch请求 * @param url * @param data * @returns {Promise} */ export function patch(url,data = {}){ return new Promise((resolve,reject) => { axios.patch(url,data) .then(response => { resolve(response.data); },err => { reject(err) }) }) } /** * 封装put请求 * @param url * @param data * @returns {Promise} */ export function put(url,data = {}){ return new Promise((resolve,reject) => { axios.put(url,data) .then(response => { resolve(response.data); },err => { reject(err) }) }) }在入口文件main.js中引入并注册为vue原型方法import axios from 'axios' import {post,fetch,patch,put} from './utils/http.js' //定义全局变量 Vue.prototype.$post=post; Vue.prototype.$fetch=fetch; Vue.prototype.$patch=patch; Vue.prototype.$put=put;在vue组件里直接使用mounted(){ this.$fetch('/api/v2/contents') .then((response) => { console.log(response) }) }, //其余的方法一样
2020年03月25日
104 阅读
0 评论
0 点赞
2020-02-25
总结移动端H5开发常用技巧
HTML 篇常用的meta属性设置meta对于移动端的一些特殊属性,可根据需要自行设置<meta name="screen-orientation" content="portrait"> //Android 禁止屏幕旋转 <meta name="full-screen" content="yes"> //全屏显示 <meta name="browsermode" content="application"> //UC应用模式,使用了application这种应用模式后,页面讲默认全屏,禁止长按菜单,禁止收拾,标准排版,以及强制图片显示。 <meta name="x5-orientation" content="portrait"> //QQ强制竖屏 <meta name="x5-fullscreen" content="true"> //QQ强制全屏 <meta name="x5-page-mode" content="app"> //QQ应用模式电话号码识别在 iOS Safari (其他浏览器和 Android 均不会)上会对那些看起来像是电话号码的数字处理为电话链接,比如:7 位数字,形如:1234567带括号及加号的数字,形如:(+86)123456789双连接线的数字,形如:00-00-0011111 位数字,形如:13800138000关闭识别<meta name="format-detection" content="telephone=no" />开启识别<a href="tel:123456">123456</a>邮箱识别(Android)安卓上会对符合邮箱格式的字符串进行识别,我们可以通过如下的 meta 来管别邮箱的自动识别:<meta content="email=no" name="format-detection" />同样地,我们也可以通过标签属性来开启长按邮箱地址弹出邮件发送的功能:<a mailto:dooyoe@gmail.com">dooyoe@gmail.com</a>CSS 篇0.5px细线移动端 H5 项目越来越多,设计师对于 UI 的要求也越来越高,比如 1px 的边框。在高清屏下,移动端的 1px 会很粗。那么为什么会产生这个问题呢?主要是跟一个东西有关,DPR(devicePixelRatio) 设备像素比,它是默认缩放为 100%的情况下,设备像素和 CSS 像素的比值。目前主流的屏幕 DPR=2(iPhone 8),或者 3(iPhone 8 Plus)。拿 2 倍屏来说,设备的物理像素要实现 1 像素,而 DPR=2,所以 css 像素只能是 0.5。下面介绍最常用的方法/* 底边框 */ .b-border { position: relative; } .b-border:before { content: ''; position: absolute; left: 0; bottom: 0; width: 100%; height: 1px; background: #d9d9d9; -webkit-transform: scaleY(0.5); transform: scaleY(0.5); -webkit-transform-origin: 0 0; transform-origin: 0 0; } /* 上边框 */ .t-border { position: relative; } .t-border:before { content: ''; position: absolute; left: 0; top: 0; width: 100%; height: 1px; background: #d9d9d9; -webkit-transform: scaleY(0.5); transform: scaleY(0.5); -webkit-transform-origin: 0 0; transform-origin: 0 0; } /* 右边框 */ .r-border { position: relative; } .r-border:before { content: ''; position: absolute; right: 0; bottom: 0; width: 1px; height: 100%; background: #d9d9d9; -webkit-transform: scaleX(0.5); transform: scaleX(0.5); -webkit-transform-origin: 0 0; transform-origin: 0 0; } /* 左边框 */ .l-border { position: relative; } .l-border:before { content: ''; position: absolute; left: 0; bottom: 0; width: 1px; height: 100%; background: #d9d9d9; -webkit-transform: scaleX(0.5); transform: scaleX(0.5); -webkit-transform-origin: 0 0; transform-origin: 0 0; } /* 四条边 */ .setBorderAll { position: relative; &:after { content: ' '; position: absolute; top: 0; left: 0; width: 200%; height: 200%; transform: scale(0.5); transform-origin: left top; box-sizing: border-box; border: 1px solid #e5e5e5; border-radius: 4px; } }屏蔽用户选择禁止用户选择页面中的文字或者图片div { -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; }清除输入框内阴影在 iOS 上,输入框默认有内部阴影,以这样关闭:div { -webkit-appearance: none; }如何禁止保存或拷贝图像代码如下img { -webkit-touch-callout: none; }输入框默认字体颜色设置 input 里面 placeholder 字体的颜色input::-webkit-input-placeholder, textarea::-webkit-input-placeholder { color: #c7c7c7; } input:-moz-placeholder, textarea:-moz-placeholder { color: #c7c7c7; } input:-ms-input-placeholder, textarea:-ms-input-placeholder { color: #c7c7c7; }用户设置字号放大或者缩小导致页面布局错误设置字体禁止缩放body { -webkit-text-size-adjust: 100% !important; text-size-adjust: 100% !important; -moz-text-size-adjust: 100% !important; }android系统中元素被点击时产生边框部分android系统点击一个链接,会出现一个边框或者半透明灰色遮罩, 不同生产商定义出来额效果不一样。去除代码如下a,button,input,textarea{ -webkit-tap-highlight-color: rgba(0,0,0,0) -webkit-user-modify:read-write-plaintext-only; }iOS 滑动不流畅ios 手机上下滑动页面会产生卡顿,手指离开页面,页面立即停止运动。整体表现就是滑动不流畅,没有滑动惯性。 iOS 5.0 以及之后的版本,滑动有定义有两个值 auto 和 touch,默认值为 auto。解决方案在滚动容器上增加滚动 touch 方法.wrapper { -webkit-overflow-scrolling: touch; }设置 overflow 设置外部 overflow 为 hidden,设置内容元素 overflow 为 auto。内部元素超出 body 即产生滚动,超出的部分 body 隐藏。body { overflow-y: hidden; } .wrapper { overflow-y: auto; }JS 篇移动端click屏幕产生200-300 ms的延迟响应移动设备上的web网页是有300ms延迟的,往往会造成按钮点击延迟甚至是点击失效。解决方案:fastclick可以解决在手机上点击事件的300ms延迟zepto的touch模块,tap事件也是为了解决在click的延迟问题触摸事件的响应顺序ontouchstartontouchmoveontouchendonclickaudio 和 video 在 ios 和 andriod 中自动播放这个不是bug,由于自动播放网页中的音频或视频,会给用户带来一些困扰或者不必要的流量消耗,所以苹果系统和安卓系统通常都会禁止自动播放和使用 JS 的触发播放,必须由用户来触发才可以播放。加入自动触发播放的代码$('html').one('touchstart', function() { audio.play() })iOS 上拉边界下拉出现空白手指按住屏幕下拉,屏幕顶部会多出一块白色区域。手指按住屏幕上拉,底部多出一块白色区域。在 iOS 中,手指按住屏幕上下拖动,会触发 touchmove 事件。这个事件触发的对象是整个 webview 容器,容器自然会被拖动,剩下的部分会成空白。解决方案document.body.addEventListener( 'touchmove', function(e) { if (e._isScroller) return // 阻止默认事件 e.preventDefault() }, { passive: false } )ios 日期转换 NAN 的问题将日期字符串的格式符号替换成'/''yyyy-MM-dd'.replace(/-/g, '/')软键盘问题IOS 键盘弹起挡住原来的视图可以通过监听移动端软键盘弹起 Element.scrollIntoViewIfNeeded(Boolean)方法用来将不在浏览器窗口的可见区域内的元素滚动到浏览器窗口的可见区域。 如果该元素已经在浏览器窗口的可见区域内,则不会发生滚动。true,则元素将在其所在滚动区的可视区域中居中对齐。false,则元素将与其所在滚动区的可视区域最近的边缘对齐。 根据可见区域最靠近元素的哪个边缘,元素的顶部将与可见区域的顶部边缘对准,或者元素的底部边缘将与可见区域的底部边缘对准。window.addEventListener('resize', function() { if ( document.activeElement.tagName === 'INPUT' || document.activeElement.tagName === 'TEXTAREA' ) { window.setTimeout(function() { if ('scrollIntoView' in document.activeElement) { document.activeElement.scrollIntoView(false) } else { document.activeElement.scrollIntoViewIfNeeded(false) } }, 0) } })onkeyUp 和 onKeydown 兼容性问题IOS 中 input 键盘事件 keyup、keydown、等支持不是很好, 用 input 监听键盘 keyup 事件,在安卓手机浏览器中没有问题,但是在 ios 手机浏览器中用输入法输入之后,并未立刻相应 keyup 事件IOS12 输入框难以点击获取焦点,弹不出软键盘定位找到问题是 fastclick.js 对 IOS12 的兼容性,可在 fastclick.js 源码或者 main.js 做以下修改FastClick.prototype.focus = function(targetElement) { var length if ( deviceIsIOS && targetElement.setSelectionRange && targetElement.type.indexOf('date') !== 0 && targetElement.type !== 'time' && targetElement.type !== 'month' ) { length = targetElement.value.length targetElement.setSelectionRange(length, length) targetElement.focus() } else { targetElement.focus() } }IOS 键盘收起时页面没用回落,底部会留白通过监听键盘回落时间滚动到原来的位置window.addEventListener('focusout', function() { window.scrollTo(0, 0) }) //input输入框弹起软键盘的解决方案。 var bfscrolltop = document.body.scrollTop $('input') .focus(function() { document.body.scrollTop = document.body.scrollHeight //console.log(document.body.scrollTop); }) .blur(function() { document.body.scrollTop = bfscrolltop //console.log(document.body.scrollTop); })IOS 下 fixed 失效的原因软键盘唤起后,页面的 fixed 元素将失效,变成了 absolute,所以当页面超过一屏且滚动时,失效的 fixed 元素就会跟随滚动了。不仅限于 type=text 的输入框,凡是软键盘(比如时间日期选择、select 选择等等)被唤起,都会遇到同样地问题。解决方法: 不让页面滚动,而是让主体部分自己滚动,主体部分高度设为 100%,overflow:scroll<body> <div class='warper'> <div class='main'></div> <div> <div class="fix-bottom"></div> </body>.warper { position: absolute; width: 100%; left: 0; right: 0; top: 0; bottom: 0; overflow-y: scroll; -webkit-overflow-scrolling: touch; /* 解决ios滑动不流畅问题 */ } .fix-bottom { position: fixed; bottom: 0; width: 100%; }作者:lzg9527链接:https://juejin.im/post/5e4a0162f265da57133b2005来源:掘金
2020年02月25日
84 阅读
0 评论
0 点赞
2019-12-31
2019年年终工作总结
本文来自公司的年终总结模板,类似公司年度 KPI 考核的一部分1、2019年你主要进行的10件工作?2019年的大部分时间参与了如下3个项目,3个项目中耗时比较长的事项整理如下:ZCMS3.x产品开发规范相关接口并输出swagger文档nginxsync插件功能实现政务版嘉宾访谈插件开发易错词导入导出功能内容编辑页面完善:整合多维度及同时发布到功能、完善易错词及敏感词校验机制;调整界面布局产品前端权限过滤处理及二次开发权限控制机制完善专题功能完善:专题加锁、关联区块选择动态数据、整合调查投票、专题模板批量导入导出功能等二次开发:整理产品二次开发文档、素材并录制视频内容扩展模型功能完善:优化管理界面及交互;增加布局选项并优化显示效果产品发版,常规bug修复及日常项目反馈问题响应广汽丰田项目二次开发培训调研单点登录及素材库一期需求及部分开发工作调研丰云行APP内容对接需求龙湖项目界面OEM:整体调整成龙湖蓝色主色调整理模板制作培训及采编人员培训ppt及模板示例站点模板制作培训及技术指导扩展模型增加百度地图组件整合阿里云OSS,改造前端上传逻辑部署测试及UAT环境整理文档:《服务器环境部署文档》、《公众号接入说明》、《模板制作流程》、《项目详细设计说明书》指导客户建站流程及制作模板U享家站点内容预览、浏览功能实现2、2019年你有哪些成长和进步?对微服务开发模式有所了解,对高并发高可用的应用开发及部署场景有了更多的了解。2019年开发技能层面的提升感觉不多,但是经过多个项目的需求调研及实际开发工作,客户需求理解及转化为技术实现的能力有所提升。3、2019年自我评价2019年是忙碌而充实的一年,经过ZCMS开发小组一年的完善及项目实践,产品已逐步趋于稳定。这一年参与项目的过程中感觉自己考虑问题还是有些太技术化了,面对客户的各种奇葩需求,首先是带着抵触心理,在这种心理的作用下,也就错失了产品进一步完善的机会,在以后的工作中应该多跟商务人员学习跟客户的沟通技巧。4、2020年希望在工作上有什么成长和进步个人技能及职业发展方向定位一直都不清晰,感觉逐步步入了中年IT从业人员的迷茫阶段,出于个人年龄及家庭环境因素,接下来的两年时间想结合多年编程从业经验逐步转型儿童编程教育领域。计划2020年学习一些编程逻辑教学的课程,报一些软考,考java、python、软件设计师或架构师相关的证书。5、为公司或部门的发展,提出意见和建议建议提高公司出差补贴,公司现阶段的出差补贴标准不及6年前待过的产品实施型公司的一半,但是出差的强度却大很多,导致大多数同事都不愿意出差。完善产品相关文档及培训体系(二次开发培训、采编人员培训、模板制作人员培训、管理员培训等)丰富产品演示站,可考虑投入些人力完善政务演示站、集团演示站、新闻演示站、高校演示站。可以考虑把ZAS在公司内部运用起来,把日常使用比较多的几个业务系统,如ZRDM、PMS、gitlab整合到单点登录系统。
2019年12月31日
1 阅读
0 评论
0 点赞
2019-06-10
ZCMS二次开发讲解——话术准备
2019-06-20更新 :相关视频内容已更新到ZCMS产品官网,可通过如下链接查看: https://www.zving.com/c/2019-06-20/285820.shtml 第一幕:引导语大家好,我是泽元软件长沙子公司的朱治龙,今天由我来给大家讲解ZCMS3.x二次开发。本次分享主要根据这个脑图上的一些知识点结合二次开发手册进行讲解。由于ZCMS3.x采用的是前后端分离的技术架构,本次分享的内容大纲主要分为前端和后端两大块。由于我们本次学习的重点是ZCMS二次开发,二次开发涉及一些前置知识需要大家利用其它的时间进行学习,涉及的主要前置知识包括:熟悉 Eclipse 的使用、Maven 多模块开发、SpringBoot、ZCF前端采用的主要是Vue生态相关的热门技术,包括:ES6、Node、Webpack、Vue全家桶、element-ui 等现在根据二次开发文档的步骤讲解下二次开发原理及流程。首先给大家对ZCMS3做一些简单介绍。ZCMS3.X是一个功能强大的使用J2EE技术开发的站群管理系统,后端基于Springboot+ZCF+Maven多模块开发,前端使用Vue全家桶+ElementUI等主流前端技术。第二幕: 基本介绍可以通过Eclipse的插件 ZDeveloper 进行二次开发支持,通过 ZDeveloper 可以很方面的扩展ZCMS的接口、菜单、权限、定时任务、模板标签等,并允许通过系统预留的扩展点及扩展项扩展系统内容采编发、权限、工作流在内的各种行为。还提供了非常方便的数据库模型设计工具概述及技术架构现在我们打开二次开发手册,首先阅读一下ZCMS二次开发概述的内容。其中需要重点关注的点我已做了加粗标记。从加粗的几个词我们可以了解到,使用的是前后端分离的技术架构,前端使用Vue,后端基于Springboot 和ZCF,开发工具建议使用Eclipse或SpringToolSuite。推荐使用SpringToolSuite.后端主要采用Maven多模块开发模式,使用ZDeveloper插件进行二次开发。下面我们了解一下ZCMS的技术架构:从这张技术架构图中我们可以了解到前端使用的是Vue、ElementUI、Webpack、axios相关的前端技术后端使用SpringBoot来简化Java应用的初始搭建以及开发过程,使用Restful接口规范给前端提供API接口,使用SpringBoot中集成的SpringMVC来响应前端的接口请求。通过ZCP集成了组织机构、角色、用户、配置项等系统管理相关的基础功能。基于Contentcore模块提供内容管理相关的核心功能,在整个生命周期中有ZCF框架提供定时任务、事务、日志、安全监控及扩展服务、扩展项、扩展点、扩展行为等功能,使用基于ZCF的ORM模块实现数据持久层的管理。下面我们里哦啊接一下ZCF的响应流程,也就是前后端交互的流程:首先从前端页面发起一个异步请求,后端在Controller层由SpringMVC接收到请求并根据具体URL匹配规则定位到具体的Controller类中对应的方法,在方法中执行相应的后端业务逻辑处理,处理结束后返回到Controller层,由Controller层已json格式的方式将内容返回给前端,前端根据返回的数据渲染界面。第三幕:后端二次开发流程演示下面我们根据二次开发手册讲解开发环境搭建流程。首先讲解后端开发环境搭建,2.1中所提及的基础软件安装就不着重讲了,下面我们讲解ZDeveloper插件的安装,目前最新的ZDeveloper插件的版本号是3.0.7,我们将这个插件拷贝到Eclipse的Dropins或Plugins目录。我本地使用的是SpringToolSuite 4,后面我们简称为STS,这是一个为开发spring应用定制的eclipse,我们打开STS所在的目录,将文件拷贝到到对应的目录里,启动STS。为了贴近真实开发流程场景,我们新建一个新的工作区,前端开发环境前后端分离的开发模式下,后端服务只通过接口给前端界面提供服务,所以我们需要通过UI界面查看显示效果的话,还需要把前端工程也运行起来。现在我们根据前端开发环境的文档把前端工程也跑起来。下面我将以上次广汽丰田项目二次开发培训用到的示例就二次开发涉及的知识点给大家做一个简单的讲解。示例效果演示首先我们看一下这次要讲解的示例效果这个示例效果是上次为广汽丰田项目做二次开发培训准备的,主要是实现了在配置菜单下添加了一个车型管理的子菜单,在该子菜单下主要是仿造广汽丰田官网中的品牌车型栏目内容管理功能,并提供了仿照该页面的预览视图,下面我们演示一下相关的增删改查功能。单击新建按钮,在打开的“新建车型”对话框中填写相应的车型信息,选择响应的LOGO图及车型图,单击保存按钮接口规范下面我们开始讲解相关的二次开发功能实现,首先回到二次开发手册,找到2.9开发说明,开发说明中其他点大家自己看一下,我这里主要说明下第四小点,该小点主要说明的是在ZCMS中有3类业务接口,分别是数据API服务接口、后端应用服务接口、网站前端服务接口,这3类接口分别用于跟其他应用对接的API接口,为client工程提供服务的ui接口,为网站应用提供服务的front接口,3种接口在包名、类名及接口路径上都有相应的约定:名称包名类名接口路径数据API服务接口apiXxxAPI/api/xxx后端应用服务接口uiXxxUI/ui/xxx后端应用服务接口frontXxxFront/front/xxx这在我们的工程里主要体现是这样的形式:相应的模块分别放在api、front、ui包下面,里面的java类分别以API、Front、UI结尾,java类中的接口路径分别以/api/、/front/、/ui/开头
2019年06月10日
17 阅读
0 评论
0 点赞
2019-04-13
git部分高级用法
Rebase 合并该命令可以让和 merge 命令得到的结果基本是一致的。通常使用 merge 操作将分支上的代码合并到 master 中,分支样子如下所示:使用 rebase 后,会将 develop 上的 commit 按顺序移到 master 的第三个 commit 后面,分支样子如下所示:rebase 对比 merge,优势在于合并后的结果很清晰,只有一条线,劣势在于如果一旦出现冲突,解决冲突很麻烦,可能要解决多个冲突,但是 merge 出现冲突只需要解决一次。使用 rebase 应该在需要被 rebase 的分支上操作,并且该分支是本地分支。如果 develop 分支需要 rebase 到 master 上去,那么应该如下操作## branch develop git rebase master git checkout master ## 用于将 `master` 上的 HEAD 移动到最新的 commit git merge developstashstash 用于临时保存工作目录的改动。开发中可能会遇到代码写一半需要切分支打包的问题,如果这时候你不想 commit 的话,就可以使用该命令。git stash使用该命令可以暂存你的工作目录,后面想恢复工作目录,只需要使用git stash pop这样你之前临时保存的代码又回来了reflogreflog 可以看到 HEAD 的移动记录,假如之前误删了一个分支,可以通过 git reflog 看到移动 HEAD 的哈希值从图中可以看出,HEAD 的最后一次移动行为是 merge 后,接下来分支 new 就被删除了,那么我们可以通过以下命令找回 new 分支git checkout 37d9aca git checkout -b newPS:reflog 记录是有时效性的,只会保存一段时间内的记录。reset如果你想删除刚写的 commit,就可以通过以下命令实现git reset --hard HEAD^但是 reset 的本质并不是删除了 commit,而是重新设置了 HEAD 和它指向的 branch。
2019年04月13日
63 阅读
0 评论
0 点赞
2019-02-13
2018年年终总结
本文来自公司的年终总结模板,类似公司年度 KPI 考核的一部分列举过去一年你主要的10件工作ZCMS3.0:自定义表单无障碍浏览:提供项目技术支持服务:北京法网、辽宁海事局项目有使用;增加快捷键输出加密版权信息;改进功能及修复XSS安全问题响应式专题:改造专题、专题模板新建及管理流程;改进专题组件(分享组件、新浪微博、地图组件);结合前后端分离逻辑调整专题资源文件;专题删除、移动、恢复相关后端逻辑完善完善首页、系统检测、更新license、nginxsync等页面功能一键转载功能增强:使用postMessage方式改造数据交互机制;新增记住选择栏目及下载远程图片选项微信公众号中管理稿件ZCMS3.0微信小程序:根据3.0相关接口规范调整页面;界面改造;调整小程序相关配置项到站点扩展配置中融媒体功能整合及界面布局调整海关项目前端文档整理:基于element-ui 的文档工程制作;添加项目定制说明信息及新增的组件内容北外国际微信小程序实现小程序的粒子动效及性能优化北汽绅宝朗读者小程序项目参与前期功能调研小程序前端布局及动效无限极资讯小程序项目项目管理根据需求梳理相关接口规范整理相关交付文档教考融合平台前期前端工程搭建eVideoVplayer播放器改造项目前端问题技术支持交通年鉴项目启动过程增加loading效果增加收集客户端启动信息发送到指定邮箱功能增加客户端安全软件检测机制个人成长和遇到的问题个人成长微信小程序开发:通过北外国际微信小程序、北汽绅宝朗读者小程序、无限极资讯小程序3个项目的实战经验,对微信小程序相关开发、发布流程有了整体的了解。文档类网站:通过海关项目对使用VuePress或基于Node静态化项目文档机制有所实战经验。electronNW.js客户端开发及打包:通过教考融合平台项目的eVideoPlayer离线播放器、交通年鉴项目及ZECM项目,对基于electron、NW.js开发客户端及打包发布有基本了解。视频录制及后期处理:通过录制自定义表单功能演示及ZCMS响应式专题功能演示视频,掌握相关软件的使用。遇到的问题加入泽元2年多了,回顾这两年多的经历,感觉下面这张 邓宁-克鲁格心理效应图 很能应景这两年自己的成长历程。到泽元前接触过各种不同规模的门户建设项目,总感觉自己很牛逼了。现在回顾那段经历,发现其实那样的经验相对比较单一,只是基于不同的CMS建站:技能上也只是局限于切片、做模板、迁移数据以及做一些需求定制开发。前端技能层面也仅局限于写些简单的js及处理一些浏览器兼容性的问题。到泽元后,发现老板们都是走在前沿的技术大牛,身边的同事也各有所长,才意识到自己技术基础的巨大差距,感觉技术实力上跌入了绝望之谷。还好有领导及同事们的正确引导,让自己也平缓过度到现在的艰难爬坡阶段。希望接下来的2019年能在业余时间多学习,努力成长为自己想要的样子。2019年希望的成长和进步阅读开源框架源码(layui、jquery1.12.4、zeptojs)阅读清单:《深入理解计算机系统》、《SVG精髓》、《javascript权威指南》、《ElasticSearch源码解析及优化指南》、《Node.js实战:使用Egg.js+Vue.js+Docker构建渐进式、可持续集成与交付应用》掌握python开发网站及数据爬取学习数学对公司的意见和建议可以定期组织员工体检。去年一个前公司的看起来很健康壮实的同事突发脑溢血,住了1周的ICU后没能抢救过来,这样的事故对公司和员工都是不可控的风险和不可挽回的损失。优化项目问题反馈流程,可以考虑尽量安排测试工程师及实施工程师优先响应项目反馈的问题。
2019年02月13日
12 阅读
0 评论
1 点赞
2019-01-13
ZCMS专题剥离及改造方案
背景说明自ZCMS 3.x版本推出专题编辑器以来,该功能已成功吸引了众多新闻媒体单位的目光。众所周知,许多媒体单位都拥有自己的内容管理系统(CMS)。在与我们的潜在客户进行深入交流时,我们了解到一个普遍的需求:他们希望利用ZCMS强大的专题编辑器功能,同时保持内容来源的独立性,即专题展示的内容依然由他们自己的CMS提供。针对这一特定的使用场景,设计了这套初步的改造方案,它巧妙地融合了ZCMS专题编辑器的先进功能,旨在满足客户的需求,实现系统间的无缝对接。剥离后的专题编辑器功能清单简易版CMS专题模板管理(新建、编辑、删除、设为共享、取消共享、预览、模板分类)专题管理(新建、编辑、删除、预览、发布)增加组件:FAQ、时间轴、Tab标签、评论部件、自定义表单部件等统计信息:专题浏览数据(PV、UV等)动态数据:分栏部件、轮播图部件改造说明尽量改造成可独立运营的产品专题不再基于内容类型,定位于栏目层级,一个专题对应一个栏目,专题下可设置子栏目(子栏目层级仅为1级)一个专题(模板)对应多个ResponsivePage,至少包含首页、列表页、内容页专题静态化说明:一个专题相关的静态化页面及资源文件统一发布到一个目录简化权限:新建专题、可访问、可操作(编辑、发布、删除、内容管理)、查看统计数据内容管理层面可通过接口跟其他CMS进行对接、或通过数据采集方式爬取其他站点的数据轮播图组件的动态数据也跟分栏部件的动态数据配置方式保持一致实现方案专题剥离现有专题涉及的插件有:pageweaver、framework、platform、contentcore、comment、imageplayer、stat、staticize、article、media。内容核心(站点、栏目、文章等)相关的功能可整合成简易CMS。菜单 :专题(专题概览、内容管理、数据分析、域名设置、操作日志)、模板(模板管理、模板分类、模板)、互动(评论管理、自定义表单、调查投票)、设置(系统信息、组织机构、角色管理、用户管理、配置项管理、定时任务、系统日志)界面布局 :参考 云凤蝶专题改造基本流程1.扩展ZCSpecial表,用于存储专题和专题模板数据,增加pages字段存储ZCResponsivePage的关联关系,数据为json内容,内容格式如下:[{ fileName: 'home' // 文件名 fileType: 'index', // 类型,值为index|list|detail label:'首页', // 名称 pageID: 23444321 // 关联页面ID },{ fileName: 'list' fileType: 'list', label:'列表页', pageID: 10301 },{ fileName: 'detail' fileType: 'detail', label:'内容页', pageID: 10302 }]专题的动态数据配置界面增加层级配置项,值为Child、Root、Current、CurrentAndChild,用于列表页取当前栏目的数据。专题编辑器中无数据的情况下使用mock数据,在编辑器中内置部分栏目、内容mock数据。增加分页条组件,可基于kkpager实现,可配置几种风格,分页组件仅用于专题列表页。栏目页数据分页是否有必要做静态化待沟通。简化ZCResponsivePage表跟CMS整合通过 SSO 整合身份认证专题系统提供新建专题入口URL、专题模板管理URL专题系统提供专题管理接口:专题列表(ID、名称、创建时间、创建者、封面图、状态、专题发布后的URL)、删除、发布、排序相关数据表内容核心 :ZCSite、ZCCatalog、ZCContent、ZCResources专题 :ZCSpecial、ZCSpecialBlock、ZCResponsivePage、ZCPageSnapshot、ZCPageWidgetLibrary、、ZCSubjectType、ZCPageDynamicConfig评论 :ZCComment、ZCCommentStatistics、ZCFaceVote、ZCFaceVoteIP、ZCFace自定义表单 :ZCCustomFormType、ZCCustomFormModel、ZCCustomFormData调查 :ZCVote、ZCVoteSubject、ZCVoteItem、ZCVoteLog访问统计 :ZCStatItem、ZCVisitLogTODO操作手册资源同步到CDN资源分发到其他服务器
2019年01月13日
22 阅读
0 评论
0 点赞
2018-08-01
raml mock工具测评
调研工具raml-mockup开源地址:https://github.com/gextech/raml-mockup该项目实现层面是基于express、json-schema-faker、raml-1-parser,faker做了些封装提供mock服务,返回结果不是很严谨,可能不怎么适合项目二次开发基于 raml-1-parser解析raml,支持版本:0.8、1.0基本使用npm install -g raml-mockup $ raml-mockup src/api.raml -d src/schemas -f http://json-schema.org -r src/formats.js -p 5000 -wraml-mocker开源站点:https://github.com/repocho/raml-mocker模拟数据及解析基于faker,raml-parser实现raml支持版本:0.8,用肝炎app的raml文档(raml版本为1.0)提示版本不正确看提供的示例代码是可以跟express结合使用osprey-mock-service开源地址:https://github.com/mulesoft-labs/osprey-mock-service基于 raml-1-parser解析用肝炎app的raml文档测试报错,控制台提示UnhandledPromiseRejectionWarning: Unhandled promise rejection.,raml-1-parser解析的时候将文档鉴定为无效的raml文档。结论这3款工具都不能满足现有项目简单的提供mock数据的支持。建议可以在rap2的基础上增加导入raml文件的功能,导入时可以考虑使用raml2html解析用到的raml2obj类做解析rap2基本情况:阿里妈妈前端团队基于node开发,很好的集成了 mockjs,能生成更符合国情的mock数据,类似apizza,可以较方便的在线管理接口。官网及线上示例:http://rap2.taobao.org/github地址:https://github.com/thx/rap2-delos
2018年08月01日
9 阅读
0 评论
0 点赞
2018-04-29
简易版DOM封装
在无障碍工具项目的实际开发中,为了方便的获取页面元素,对页面DOM操作进行了轻量级封装,虽没有jQuery强大,但也可满足大部分使用场景了。相关核心代码如下所示: var getDom = function (el) { if (!el) { return el } if (typeof el === 'string') { return document.getElementById(el.replace(/^#/, '')) } if (!el.nodeName && el.length && el[0].nodeName) { // 如果是一个jquery对象 return el[0] } return el } var Dom = {} Dom.hasClass = function (elem, className) { var el = getDom(elem) className = className.replace(/\-/, '\\-') return new RegExp('(?:^|\\s)' + className + '(?:\\s|$)').test(el.className) } Dom.addClass = function (elem, className) { var el = getDom(elem) if (!Dom.hasClass(el, className)) { el.className = el.className ? el.className + ' ' + className : className } return el } Dom.removeClass = function (elem, className) { var el = getDom(elem) className = className.replace(/\-/, '\\-') var classToRemove = new RegExp('(^|\\s)' + className + '(?=\\s|$)', 'i') el.className = el.className .replace(classToRemove, '') .replace(/^\s+|\s+$/g, '') return el } Dom.events = {} Dom.idSeed = 0 Dom.addEvent = function (el, type, className, handler) { var id = el.id || (el.id = 'id' + ++Dom.idSeed) if (typeof className === 'function' && !handler) { handler = className className = undefined } var delegate = function (evt) { return handler.call(el, evt) } if (className) { className = className.replace(/\./, '') delegate = function (evt) { evt = evt || window.event var node = evt.target || evt.srcElement while (node && node != el) { if (Dom.hasClass(node, className)) { return handler.call(node, evt) } node = node.parentNode } } } delegate.origHandler = handler delegate.delegateSelector = className if (!Dom.events[id]) { Dom.events[id] = {} } if (!Dom.events[id][type]) { Dom.events[id][type] = [] var callFuncList = function (evt) { if (Dom.events[id][type]) { for (var i = 0; i < Dom.events[id][type].length; i++) { var fn = Dom.events[id][type][i] fn(evt) } } } if (el.addEventListener) { el.addEventListener(type, callFuncList) } else { el.attachEvent('on' + type, callFuncList) } } Dom.events[id][type].push(delegate) } Dom.removeEvent = function (el, type, className, handler) { var id = el.id || (el.id = 'id' + ++Dom.idSeed) if (typeof className === 'function' && !handler) { handler = className className = undefined } if (!Dom.events[id]) { return false } if (type && !Dom.events[id][type]) { return false } if (!handler && !className) { if (!type) { Dom.events[id] = {} } else { Dom.events[id][type] = [] } return true } for (var i = Dom.events[id][type].length - 1; i >= 0; i--) { var fn = Dom.events[id][type][i] if ( fn.origHandler == handler && (!className || fn.delegateSelector == className) ) { Dom.events[id][type].splice(i, 1) } } } Dom.attr = function (el, name, value) { if (arguments.length === 1) { var map = {} var attributes = el.attributes var aLength = attributes.length for (var a = 0; a < aLength; a++) { map[attributes[a].name.toLowerCase()] = attributes[a].value } return map } if (arguments.length === 2) { return el.getAttribute(name) } el.setAttribute(name, value) }
2018年04月29日
86 阅读
0 评论
0 点赞
2017-12-02
向客户解释为什么PC端网页要提供宽为 960或1200的设计稿
一个网页的尺寸和浏览器及显示器的尺寸大为相关,我们不可能满足所有用户的显示器尺寸,我们能做的是让绝大多数用户在访问网站时没有横向滚动条。需要统计客户的显示器分辨率,以百度近六个月的浏览器统计数据为例:1920x1080 44.5% 1366x768 10.19% 1440x900 8.27% 1536x864 7.22% 1600x900 6.43% 1280x720 4.31% 1024x768 3.27% 其他 15.81%从统计来看-要让绝大多数(约99%)的PC用户浏览网站没有横向滚动条,网页宽在1024以下,可以是960像素,960是2、3、4、5、6的公约数,980是2、4、5、6、7的公约数。-要页面美观大气,兼顾大部分(约80%)PC用户浏览网站没有横向滚动条,网页宽在1366以下为佳,可以是1170/1200/1260像素。1170是2、3、5、6的公约数。1200是2、3、4、5、6的公约数。1260是2、3、4、5、6、7的公约数。建议在不做响应式设计的情况下,提供宽1200分辨率的设计稿,并以像素为单位标注尺寸在做响应式设计的情况下,分别提供宽360(手机)、宽750(平板)、宽960(横向平板或窄屏PC)、宽1200(PC),四种尺寸下的设计稿。并以像素为单位标注尺寸以上宽指的是页面主体内容宽,在PS里设计时画布要更宽一些用于示意两侧留白效果 360/400 750/800 960/1260 1200/1600 1800/2400
2017年12月02日
41 阅读
0 评论
0 点赞
2017-11-08
Linux运维常用命令
查看某文件夹下所有的文件数量:# command 1 : find -type f -o -type s -o -type p -o -type d |wc -l # command 2 : ls -al|grep '^-'|wc -l # command 3 : find -type f |wc -l删除0字节文件find -type f -size 0 -exec rm -rf {} \; 查看进程# 按内存从大到小排列 ps -e -o "%C : %p : %z : %a"|sort -k5 -nr # 按cpu利用率从大到小排列 ps -e -o "%C : %p : %z : %a"|sort -nr打印说cache里的URLgrep -r -a jpg /data/cache/* | strings | grep "http:" | awk -F'http:' '{print "http:"$2;}' 查看http的并发请求数及其TCP连接状态:netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'快速替换# sed在这个文里Root的一行,匹配Root一行,将no替换成yes. sed -i '/Root/s/no/yes/' /etc/ssh/sshd_config 如何杀掉mysql进程:# commond 1 ps aux |grep mysql |grep -v grep |awk '{print $2}' |xargs kill -9 # commond 2 killall -TERM mysqld # commond 3 通过pid文件查杀进程PID kill -9 `cat /usr/local/apache2/logs/httpd.pid` 显示运行3级别开启的服务:ls /etc/rc3.d/S* |cut -c 15- 如何在编写SHELL显示多个信息,用EOFcat << EOF +--------------------------------------------------------------+ | === Welcome to Tunoff services === | +--------------------------------------------------------------+ EOFfor 的巧用(如给mysql建软链接)cd /usr/local/mysql/bin for i in * do ln /usr/local/mysql/bin/$i /usr/bin/$i done取IP地址:# commond 1 ifconfig eth0 |grep "inet addr:" |awk '{print $2}'|cut -c 6- # commond 2 ifconfig | grep 'inet addr:'| grep -v '127.0.0.1' | cut -d: -f2 | awk '{ print $1}'内存的大小:free -m |grep "Mem" | awk '{print $2}' 查看Apache的并发请求数及其TCP连接状态:netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' 因为同事要统计一下服务器下面所有的jpg的文件的大小,写了个shell给他来统计.原来用xargs实现,但他一次处理一部分,搞的有多个总和....,下面的命令就能解决啦.find / -name *.jpg -exec wc -c {} \;|awk '{print $1}'|awk '{a+=$1}END{print a}' CPU的数量cat /proc/cpuinfo |grep -c processorCPU负载 cat /proc/loadavg检查前三个输出值是否超过了系统逻辑CPU的4倍。 查看CPU负载# 检查%idle是否过低(比如小于5%) mpstat 1 1 内存空间free # 检查free值是否过低 也可以用 # cat /proc/meminfo swap空间# 检查swap used值是否过高 free # 如果swap used值过高,进一步检查swap动作是否频繁: vmstat 1 5 # 观察si和so值是否较大磁盘空间df -h # 检查是否有分区使用率(Use%)过高(比如超过90%) 如发现某个分区空间接近用尽,可以进入该分区的挂载点,用以下命令找出占用空间最多的文件或目录: du -cks * | sort -rn | head -n 10 磁盘I/O负载iostat -x 1 2 # 检查I/O使用率(%util)是否超过100% 网络负载sar -n DEV # 检查网络流量(rxbyt/s, txbyt/s)是否过高网络传输情况netstat -i # 检查是否有网络错误(drop fifo colls carrier) 也可以用命令: cat /proc/net/dev网络连接数目netstat -an | grep -E "^(tcp)" | cut -c 68- | sort | uniq -c | sort -n查看端口占用sudo netstat -tunlp | grep 8401 # OR sudo lsof -i:8401 进程总数# 检查进程个数是否正常 (比如超过250) ps aux | wc -l进程# 观察是否有异常进程出现 top -id 1 用户who | wc -l # 检查登录用户是否过多 (比如超过50个) 也可以用命令:# uptime系统日志 cat /var/log/rflogview/*errors # 检查是否有异常错误记录 也可以搜寻一些异常关键字,例如: grep -i error /var/log/messages grep -i fail /var/log/messages 核心日志# 检查是否有异常错误记录 dmesg系统时间date 打开文件数目lsof | wc -l 日志# 需安装logwatch logwatch –print # 配置/etc/log.d/logwatch.conf,将 Mailto 设置为自己的email 地址,启动mail服务 (sendmail或者postfix),这样就可以每天收到日志报告了。 # 缺省logwatch只报告昨天的日志,可以用# logwatch –print –range all 获得所有的日志分析结果。 # 可以用# logwatch –print –detail high 获得更具体的日志分析结果(而不仅仅是出错日志)。杀掉80端口相关的进程lsof -i :80|grep -v "PID"|awk '{print "kill -9",$2}'|sh清除僵死进程。ps -eal | awk '{ if ($2 == "Z") {print $4}}' | kill -9tcpdump 抓包 ,用来防止80端口被人攻击时可以分析数据tcpdump -c 10000 -i eth0 -n dst port 80 > /root/pkts查看有多少个活动的php-cgi进程netstat -anp | grep php-cgi | grep ^tcp | wc -l chkconfig --list | awk '{if ($5=="3:on") print $1}' 在某个路径下查文件# 在/etc下查找“*.log”的文件 find /www/wwwlogs/ -name "*.log"列出某个路径下所有文件,包括子目录。find /etc -name "*"在某个路径下查找所有包含“hello abcserver”字符串的文件。find /etc -name "*" | xargs grep "hello zhuzl" # 或者 find /etc -name "*" | xargs grep "hello zhuzl" > ./zhuzltest.txt 拷贝远程文件# 拷贝远程(192.168.1.3)的/home2/backup/ 到本地的 /home/mover00/shadow_bak/sites/ scp -P 4400 -r root@192.168.1.3:/home2/backup/ /home/mover00/shadow_bak/sites/ # 拷贝本地的/home2/backup/ 到远程(192.168.1.3)的 /home/mover00/shadow_bak/sites/ scp -P 4400 -r /home2/backup/ root@192.168.1.3:/home/mover00/shadow_bak/sites/ # 格式如下: scp [-1246BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file] [-l limit] [-o ssh_option] [-P port] [-S program] [[user@]host1:]file1 [...] [[user@]host2:]file2如何知道哪个应用锁定了文件lsof <文件全路径>
2017年11月08日
22 阅读
0 评论
0 点赞
2017-09-07
基于nodejs的微服务的注册与发现机制实现的调研
https://github.com/lehoangduc/node-registrify基于hapi和redis的服务注册,同时提供命令行操作被设计为一个注册中心,注册服务和服务发现由客户端通过RESTAPI去获取优点:代码逻辑比较清晰,接口比较全问题:github星:22颗 参考价值:4分 接口全https://github.com/samuelkitazume/nodeservices基于express和nedb,包括服务注册与服务发现,提供命令行操作。被设计为一个注册中心。优点:代码简洁,问题:没有健康检查github星:1颗 参考价值:5分https://github.com/Digipolitan/yemma-discoveryhttps://github.com/Digipolitan/yemma基于express、mongoDB、socket.io,被设计为子应用(客户端)调用,通过token鉴权。优点:有心跳机制,有服务token鉴权,注册服务非常简单。问题:要配合yemma框架,代码不直观github星:1颗 参考价值:3分https://github.com/ParthaBoocha/service-registry基于glue(glue)基于hapi,看代码迁移到express也很容易,用的json文件存储数据对跨域有处理,被设计为一个注册中心。优点:代码简洁问题:对微服务没有特别的处理,例如没有心跳机制,只是一个简单的数据存储读取github星:0颗 参考价值:5分 json文件存数据https://github.com/express-components/registry基于 express、nedb、socket.io的,基于udp协议的服务注册,有健康检查,健康检查没使用数据训的TTL特性,是用的时间比较,然后主支去请求各服务的状态,看来是被设计为一个注册中心。服务发现由客户端通过RESTAPI去获取。优点:基于udp协议,有健康检查问题:github星:0颗 参考价值:7分 udp协议与健康检查https://github.com/RCT-Solutions/rct-nars基于express,处理了跨域情况,使用自己写的迷你内存数据库,有健康检查接口(待客户端去调用),注册和服务发现都是由客户端通过RESTAPI去获取,看来是设计为一个注册中心优点:模块分的比较细,对心跳包等的数据结构定义规范问题:模块分的比较细,健康检查没示例github星:0颗 参考价值:6分 内存数据库,https://github.com/nswbmw/etcd-proxy基于koa和etcd的,有服务注册,本身的实现为子应用(客户端),非注册中心,注册中心即为数据库,心跳机制为每5秒去更新etcd里的过期时间(TTL,即Time To Life),服务发现由客户端通过RESTAPI去获取通过不断地注册来作心跳,通过工具库ip获取ip,通过工具库get-port获取可用端口,好像是相同的应用名允许注册多个,返回时随机取一个优点:121行代码,代码简洁,功能完备,赞问题:依赖etcd,使用了etcd的过期机制TTLgithub星:3颗 参考价值:8分 代码简洁https://github.com/koalazak/redis-registry基于redis,有服务注册,本身的实现为子应用(客户端),非注册中心,注册中心即为数据库,心跳机制为每5秒去更新redis里的过期时间,服务发现由客户端通过查询redis去获取通过工具库network-address获取,好像是相同的应用名允许注册多个,返回时随机取一个优点:174行代码,代码简洁,问题:依赖redis,使用了redis的过期机制expiregithub星:3颗 参考价值:7分 代码简洁{name: 'my-service-name',port: 8080,hostname: '192.168.1.10',host: '192.168.1.10:8080',url: 'http://192.168.1.10:8080'}发现几个工具包https://www.npmjs.com/package/get-port获得一个没有被占用的端口https://www.npmjs.com/package/network-address获取本地iphttps://www.npmjs.com/package/ip获取ip,及掩码等,功能比network-address多https://www.npmjs.com/package/network-address获取机器ip或ipv6https://www.npmjs.com/package/nedb-promisehttps://github.com/louischatriot/nedbjs数据库,执久或内存数据库,API为MongoDB子集https://www.npmjs.com/package/glue管理hapi.js创建的服务https://www.npmjs.com/package/hapi知名web服务框架https://github.com/coreos/etcdgo语言写的分布式高可用键值对数据库https://www.npmjs.com/package/request-promise
2017年09月07日
21 阅读
0 评论
0 点赞
30分钟教你入门Git
Git近些年的火爆程度非同一般,这个版本控制系统被广泛地用在大型开源项目(比如Linux),不同规模的团队开发,以及独立开发者,甚至学生之中。初学者非常容易被git里的 各种命令、参数 吓得不愿意继续去学。但实际上刚上手的时候,你并不需要了解所有命令的用途。你可以从掌握一些简单、常用又强大的命令开始,然后逐步去学习。这就是我们这篇文章要讲的内容。让我们快开始吧!基本了解Git是一些命令行工具的集合,可以用来跟踪、记录文件的变动,经常用于开源代码。比如你可以进行旧版本恢复、比对、分析、合并等等。这个过程被称之为版本控制。已经有一系列的版本控制系统,比如SVN、Mercurial、Perforce、CVS、Bitkeepe等等。Git是分布式的,这意味着它并不依赖于中心服务器来保存你文件的旧版本。任何一台机器都可以有一个本地版本的控制系统,其实就是一个硬盘上的文件,我们称之为仓库(repository)。如果是多人协作的话,你还需要一个线上仓库,用来同步代码等信息。这就是GitHub、Gitlab、Gittee、BitBucket等网站做的工作。安装Git在你的机器上安装git非常简单:Linux – 打开终端,然后通过包管理安装,在Ubuntu上命令是: sudo apt-get install gitWindows – 推荐使用 git for windows ,它包括了图形工具以及命令行模拟器。OS X – 最简单的方式是使用 homebrew 安装,命令行执行brew install git如果你是新手,推荐使用图形工具 Github desktop、 TortoiseGit 和 Sourcetree 。不过即使使用图形界面的应用,知道一些基本的git命令依然很重要。接下来的内容我们集中在命令行控制上。2.配置Git安装完git,首要任务是做一些简单的配置,最重要的是用户名及邮箱,打开终端,执行以下命令。$ git config --global user.name "My Name" $ git config --global user.email myEmail@example.com配置好这两项,Git就能记录下来是谁做的动作,一切都更有组织性了。3.创建一个新仓库 – git initgit会把所有文件以及历史记录直接记录成一个文件夹保存在你的项目中。创建一个新的仓库,首先要去到项目路径下,执行git init。这时Git会创建一个隐藏的文件夹.git,所有的历史和配置信息都储存在其中。比如我们在桌面创建一个文件夹 git_exercise, 打开终端,输入:$ cd Desktop/git_exercise/ $ git init命令行会出现类似于如下的提示信息Initialized empty Git repository in /home/user/Desktop/git_exercise/.git/这说明我们的仓库已经建立好了,但现在是空的,试着新建一个hello.txt文本文件到这个文件夹里。4.检查状态 – git statusGit status是另一个非常重要的命令,它反馈给我们仓库当前状态的信息:是否为最新代码,有什么更新等等。在我们新建的仓库中执行git status会得到以下内容:$ git status On branch master Initial commit Untracked files: (use "git add ..." to include in what will be committed) hello.txt反馈信息告诉我们,hello.txt尚未跟踪,这是说这个文件是新的,git不知道是应该跟踪它的变动还是直接忽略。为了跟踪我们的新文件,我们需要暂存它。5.暂存 – git addGit有个概念叫“暂存区“,你可以把它看成一块空白的画布,包裹着所有你可能会提交的变动。它一开始是空的,可以通过 git add 命令添加内容,最后使用 git commit 提交(创建一个快照)。这个例子中只有一个文件,让我们先add它:$ git add hello.txt如果需要提交目录下的所有内容,可以这样做:$ git add -A再次使用git status查看状态试试:$ git status On branch master Initial commit Changes to be committed: (use "git rm --cached ..." to unstage) new file: hello.txt我们的文件已经准备好可以提交了。状态信息还告诉我们暂存区文件发生了什么变动,这里我们新增了一个文件,同样可以做修改和删除。取决于我们在上一次git add之后发生了什么。6.提交 – git commit一次提交代表着我们的仓库到了一个新的状态,就像是一个快照,允许我们像使用时光机一样回到之前的某个时间点。创建提交,需要我们至少在到暂存区有一次修改(刚才我们做了git add),然后输入命令:$ git commit -m "Initial commit."这就创建了一次从暂存区的提交(加入hello.txt),-m “Initial commit.”是用户对这次提交的描述,建议写成有意义的描述性信息。远程仓库到目前为止,我们的操作都是在本地的——只存在于.git文件中。为了能够协同开发,我们需要把代码部署到远程仓库服务器上。1.链接远程仓库 – git remote add为了能够上传到远程仓库,我们需要先建立起链接。在这篇教程中,我们远程仓库的地址为:https://github.com/igeekbar/awesome-project。但你应该自己在Github、或BitBucket上搭建仓库,自己一步一步尝试。把本地仓库链接到Github上,在命令行执行以下内容:# This is only an example. Replace the URI with your own repository address. $ git remote add origin https://github.com/igeekbar/awesome-project.git一个项目可以同时拥有好几个远程仓库,为了区分通常会起不同的名字。通常主要的远程仓库被称为origin。2.上传到服务器 – git push把本地的提交传送到服务器的动作叫做push。每次我们要提交修改到服务器上时,都会使用到git push。git push命令有两个参数,远程仓库的名字,以及分支的名字:$ git push origin master Counting objects: 3, done. Writing objects: 100% (3/3), 212 bytes | 0 bytes/s, done. Total 3 (delta 0), reused 0 (delta 0) To https://github.com/igeekbar/awesome-project.git * [new branch] master -> master取决于你使用的服务器,push过程中你可能需要验证身份(输入用户名、密码,请先在网站上进行注册)。如果没有出差错,现在用浏览器看你的远程仓库上已经有hello.txt了。3.克隆仓库 – git clone其他人可以看到你放在Github上的开源项目,他们可以用git clone命令下载到本地。$ git clone https://github.com/igeekbar/awesome-project.git本地也会创建一个新的仓库,并自动将github上的版本设为远程仓库。4.从服务器上获得修改 – git pull如果你更新了远程仓库上的内容,其他人可以通过git pull命令拉取你的变动:$ git pull origin master From https://github.com/igeekbar/awesome-project * branch master -> FETCH_HEAD Already up-to-date.因为在我们git clone之后还没有提交过修改,所有没有任何变动。分支当你在做一个新功能的时候,最好是在一个独立的区域上开发(原始项目的拷贝),通常称之为分支。分支之间相互独立,并且拥有自己的历史记录,直到你决定把他们合并到一起。这样做的原因是:已经可以运行的稳定版本的代码不会被破坏不同的功能可以由不同开发者同时开发开发者可以专注于自己的分支,不用担心被其他人破坏在不确定哪个版本更好之前,同一个特性可以在不同的分支上创建多个版本,便于比较1.创建新分支 – git branch每一个仓库的默认分支都叫master, 创建新分支可以用git branch <name>命令:$ git branch amazing_new_feature创建了一个名为amazing_new_feature的新分支,它目前和master分支是一样的内容。2.切换分支 – git checkout使用git branch,可以查看分支状态:$ git branch amazing_new_feature * master号表示当前活跃分支为master,现在我们想在新分支上开发新的特性,使用git checkout切换分支。有一个参数表示要切换到的分支。$ git checkout amazing_new_feature3.合并分支 – git merge我们在“amazing_new_feature”分支想添加一个feature.txt。和之前一样我们来创建文件、添加到暂存区、提交。$ git add feature.txt $ git commit -m "New feature complete."新分支任务完成了,回到master分支。$ git checkout master现在去查看文件夹内容,你会惊奇地发现之前刚刚创建的feature.txt文件不见了,因为我们现在回到了master分支上,这里并没有feature.txt。想把文件添加到这里,我们需要使用git merge把amazing_new_feature分支合并到master上。$ git merge amazing_new_feature现在master分支是最新的了,amazing_new_feature分支可以删掉了。$ git branch -d amazing_new_feature进阶功能在这篇教程的最后一节,我们来看一些高级并且实用的技巧。1.比对两个不同提交之间的差别每次提交都有一个标识id,查看所有历史提交和他们的id,可以使用 git log:$ git log commit ba25c0ff30e1b2f0259157b42b9f8f5d174d80d7 Author: igeekbar Date: Fri July 29 17:15:28 2016 +0300 New feature complete commit b10cc1238e355c02a044ef9f9860811ff605c9b4 Author: igeekbar Date: Fri July 29 16:30:04 2016 +0300 Added content to hello.txt commit 09bd8cc171d7084e78e4d118a2346b7487dca059 Author: igeekbar Date: Thu July 28 17:52:14 2016 +0300 Initial commitid很长,但是当使用它的时候你并不需要复制整个字符串,前几个字符就够了。查看某一次提交更新了什么,使用 git show [commit]:$ git show b10cc123 commit b10cc1238e355c02a044ef9f9860811ff605c9b4 Author: igeekbar Date: Fri July 29 16:30:04 2016 +0300 Added content to hello.txt diff --git a/hello.txt b/hello.txt index e69de29..b546a21 100644 --- a/hello.txt +++ b/hello.txt @@ -0,0 +1 @@ +Nice weather today, isn't it?查看两次提交的不同,可以使用git diff [commit-from]..[commit-to]:$ git diff 09bd8cc..ba25c0ff diff --git a/feature.txt b/feature.txt new file mode 100644 index 0000000..e69de29 diff --git a/hello.txt b/hello.txt index e69de29..b546a21 100644 --- a/hello.txt +++ b/hello.txt @@ -0,0 +1 @@ +Nice weather today, isn't it?比较首次提交和最后一次提交,我们可以看到中间所有的更改。使用git difftool命令可以用图形化界面查看所有更改。2.回滚某个文件到之前的版本Git允许我们将某个特定的文件回滚到特定的提交,使用的也是 git checkout命令。下面我们将hello.txt回滚到最初的状态,需要指定回滚到哪个提交(以id作为参数),以及文件的全路径。$ git checkout 09bd8cc1 hello.txt3.回滚提交如果你发现最新的一次提交忘记加入某个文件,或是信息输入的不正确,你可以通过 git commit --amend来改正,它会把最新的提交打回暂存区,并尝试重新提交。如果是更复杂的情况,比如不是最新的提交除了问题,你可以使用git revert。最新的一次提交别名也叫HEAD。$ git revert HEAD其他提交需要指明id:$ git revert b10cc123回滚提交时,发生冲突是非常频繁的。比如文件被指定回滚的提交之后的某次提交修改过,git不能正确回滚。4.解决合并冲突冲突经常出现在合并分支或者是拉取别人的代码。有些时候git能自动处理冲突,其他时候需要我们手动处理。我们来看以下的例子,John 和 Tim 分别在各自的分支上写了一段代码,来显示一个数组中所有的元素。John使用了for循环:// Use a for loop to console.log contents. for(var i=0; i<arr.length; i++) { console.log(arr[i]); }Tim使用forEach:// Use forEach to console.log contents. arr.forEach(function(item) { console.log(item); });它们都提交了代码到各自的分支上,现在假设John尝试合并Tim的代码:$ git merge tim_branch Auto-merging print_array.js CONFLICT (content): Merge conflict in print_array.js Automatic merge failed; fix conflicts and then commit the result.这时候git并不能自动解决冲突,于是它在代码中插入冲突标记。<<<<<<< HEAD // Use a for loop to console.log contents. for(var i=0; i<arr.length; i++) { console.log(arr[i]); } ======= // Use forEach to console.log contents. arr.forEach(function(item) { console.log(item); }); >>>>>>> Tim’s commit.==== 号上方是当前最新一次提交,下方是冲突的代码。这样我们可以清晰地看出区别,决定使用哪一个版本,或者重新写一个。假设我们对于这两个版本都不满意,我们把代码改成以下代码:// Not using for loop or forEach. // Use Array.toString() to console.log contents. console.log(arr.toString());好了,再提交一下:$ git add -A $ git commit -m "Array printing conflict resolved." ··· 在大型项目中,我们可能在合并过程中出现很多冲突,大部分开发者会借助GUI工具来获得帮助,运行推行界面可以使用git mergetool命令。 ### 5.配置 .gitignore 大部分项目中,会有些文件、文件夹是我们不想提交的。为了防止使用git add -A时不小心提交,我们可以利用.gitignore文件: - 在项目根目录创建.gitignore文件 - 在文件中列出不需要提交的文件名、文件夹名,每个一行 - .gitignore文件需要像普通文件一样add、commit和push 通常会被ignore的文件有: - log文件 - task runner builds - node.js项目中的node_modules文件夹 - IDEs比如NetBeans和IntelliJ生成的文件 - 个人笔记 以下是一个.gitignore文件的例子: *.logbuild/node_modules/.idea/my_notes.txt“/”说明是一个文件夹,里面的所有内容都被递归忽略 ------------ Git教程到这里就结束啦! Git很复杂,还有很多的特性和技巧等着你去挖掘,这篇教程只涵盖了冰山一角,希望你不要因为太多繁琐的命令而停下前进的脚步!加油! 再推荐一个学习git分支的在线交互网站: [Learn Git Branching](https://learngitbranching.js.org/?locale=zh_CN) via: [tutorialzine.com](https://tutorialzine.com/2016/06/learn-git-in-30-minutes)
2017年08月10日
34 阅读
0 评论
0 点赞
2017-06-28
Git常用命令
前言Git,是一个开源的分布式版本控制系统,用以有效、高速的处理从很小到非常大的项目版本管理。支持克隆/下载。Git教程: Git Book 官方中文文档Git GUI客户端: TortoiseGit(Windows)Git常用命令仓库# 在当前目录新建一个Git代码库 $ git init # 新建一个目录,将其初始化为Git代码库 $ git init [project-name] # 下载一个项目和它的整个代码历史 $ git clone [url]配置# 显示当前的Git配置 $ git config --list # 编辑Git配置文件 $ git config -e [--global] # 设置提交代码时的用户信息 $ git config [--global] user.name "[name]" $ git config [--global] user.email "[email address]"增加/删除文件# 添加指定文件到暂存区 $ git add [file1] [file2] ... # 添加指定目录到暂存区,包括子目录 $ git add [dir] # 添加当前目录的所有文件到暂存区 $ git add . # 添加每个变化前,都会要求确认 # 对于同一个文件的多处变化,可以实现分次提交 $ git add -p # 删除工作区文件,并且将这次删除放入暂存区 $ git rm [file1] [file2] ... # 停止追踪指定文件,但该文件会保留在工作区 $ git rm --cached [file] # 改名文件,并且将这个改名放入暂存区 $ git mv [file-original] [file-renamed]代码提交# 提交暂存区到仓库区 $ git commit -m [message] # 提交暂存区的指定文件到仓库区 $ git commit [file1] [file2] ... -m [message] # 提交工作区自上次commit之后的变化,直接到仓库区 $ git commit -a # 提交时显示所有diff信息 $ git commit -v # 使用一次新的commit,替代上一次提交 # 如果代码没有任何新变化,则用来改写上一次commit的提交信息 $ git commit --amend -m [message] # 重做上一次commit,并包括指定文件的新变化 $ git commit --amend [file1] [file2] ...分支# 列出所有本地分支 $ git branch # 列出所有远程分支 $ git branch -r # 列出所有本地分支和远程分支 $ git branch -a # 新建一个分支,但依然停留在当前分支 $ git branch [branch-name] # 新建一个分支,并切换到该分支 $ git checkout -b [branch] # 新建一个分支,指向指定commit $ git branch [branch] [commit] # 新建一个分支,与指定的远程分支建立追踪关系 $ git branch --track [branch] [remote-branch] # 切换到指定分支,并更新工作区 $ git checkout [branch-name] # 切换到上一个分支 $ git checkout - # 建立追踪关系,在现有分支与指定的远程分支之间 $ git branch --set-upstream [branch] [remote-branch] # 合并指定分支到当前分支 $ git merge [branch] # 选择一个commit,合并进当前分支 $ git cherry-pick [commit] # 删除分支 $ git branch -d [branch-name] # 删除远程分支 $ git push origin --delete [branch-name] $ git branch -dr [remote/branch]标签# 列出所有tag $ git tag # 新建一个tag在当前commit $ git tag [tag] # 新建一个tag在指定commit $ git tag [tag] [commit] # 删除本地tag $ git tag -d [tag] # 删除远程tag $ git push origin :refs/tags/[tagName] # 查看tag信息 $ git show [tag] # 提交指定tag $ git push [remote] [tag] # 提交所有tag $ git push [remote] --tags # 新建一个分支,指向某个tag $ git checkout -b [branch] [tag]查看信息# 显示有变更的文件 $ git status # 显示当前分支的版本历史 $ git log # 显示commit历史,以及每次commit发生变更的文件 $ git log --stat # 搜索提交历史,根据关键词 $ git log -S [keyword] # 显示某个commit之后的所有变动,每个commit占据一行 $ git log [tag] HEAD --pretty=format:%s # 显示某个commit之后的所有变动,其"提交说明"必须符合搜索条件 $ git log [tag] HEAD --grep feature # 显示某个文件的版本历史,包括文件改名 $ git log --follow [file] $ git whatchanged [file] # 显示指定文件相关的每一次diff $ git log -p [file] # 显示过去5次提交 $ git log -5 --pretty --oneline # 显示所有提交过的用户,按提交次数排序 $ git shortlog -sn # 显示指定文件是什么人在什么时间修改过 $ git blame [file] # 显示暂存区和工作区的差异 $ git diff # 显示暂存区和上一个commit的差异 $ git diff --cached [file] # 显示工作区与当前分支最新commit之间的差异 $ git diff HEAD # 显示两次提交之间的差异 $ git diff [first-branch]...[second-branch] # 显示今天你写了多少行代码 $ git diff --shortstat "@{0 day ago}" # 显示某次提交的元数据和内容变化 $ git show [commit] # 显示某次提交发生变化的文件 $ git show --name-only [commit] # 显示某次提交时,某个文件的内容 $ git show [commit]:[filename] # 显示当前分支的最近几次提交 $ git reflog远程同步# 下载远程仓库的所有变动 $ git fetch [remote] # 显示所有远程仓库 $ git remote -v # 显示某个远程仓库的信息 $ git remote show [remote] # 增加一个新的远程仓库,并命名 $ git remote add [shortname] [url] # 取回远程仓库的变化,并与本地分支合并 $ git pull [remote] [branch] # 上传本地指定分支到远程仓库 $ git push [remote] [branch] # 强行推送当前分支到远程仓库,即使有冲突 $ git push [remote] --force # 推送所有分支到远程仓库 $ git push [remote] --all撤销# 恢复暂存区的指定文件到工作区 $ git checkout [file] # 恢复某个commit的指定文件到暂存区和工作区 $ git checkout [commit] [file] # 恢复暂存区的所有文件到工作区 $ git checkout . # 重置暂存区的指定文件,与上一次commit保持一致,但工作区不变 $ git reset [file] # 重置暂存区与工作区,与上一次commit保持一致 $ git reset --hard # 重置当前分支的指针为指定commit,同时重置暂存区,但工作区不变 $ git reset [commit] # 重置当前分支的HEAD为指定commit,同时重置暂存区和工作区,与指定commit一致 $ git reset --hard [commit] # 重置当前HEAD为指定commit,但保持暂存区和工作区不变 $ git reset --keep [commit] # 新建一个commit,用来撤销指定commit # 后者的所有变化都将被前者抵消,并且应用到当前分支 $ git revert [commit] # 暂时将未提交的变化移除,稍后再移入 $ git stash $ git stash pop其他# 生成一个可供发布的压缩包 $ git archive
2017年06月28日
24 阅读
0 评论
0 点赞
2017-04-23
git命令之git branch系列
1、git branch查看本地当前所在分支,并且在当前分支前面加“*”号标记2、git branch -r查看远程分支,r 是 remote 的简写3、git checkout -b production origin/production取远程分支 并 分化一个新的分支到本地;然后此刻,本地已经切换到了该新分支,执行 git pull ,将代码拉下来,本地才有了完整的对应分支;前提:先查看远程分支情况,执行 git branch -r例子:git checkout -b hotfix_v2003 origin/hotfix_v20034、git branch -a列出本地分支和远程分支5、git branch 分支名创建一个新的本地分支,需要注意,此处只是创建分支,不进行分支切换;git checkout -b 分支名 创建一个新的本地分支,同时切换到刚新建的分支上。6、git branch -m | -M oldbranch newbranch重命名分支,如果newbranch名字已经存在,则需要使用-M强制重命名,否则,使用-m进行重命名。7、git branch -d | -D branchname删除branchname分支,D表示强制删除7.1、git branch -d mybranch删除分支7.2、git branch -D mybranch强制删除分支8、git branch -d -r branchname删除远程 branchname 分支,通知还需要执行 push 命令,才能真正删除:git push origin:branchname补充:如果不执行 push 命令,虽然通过git branch -r已经看不到 branchname 了,但在GitHub的网页上依然能看到branchname,而且执行git fetch命令后,再git branch -r,由可以看到branchname,说明如果不push没有真正删除远程分支。9、git branch -v查看各个分支最后一次提交10、git branch -merged查看哪些分支合并入当前分支11、git branch -no-merged查看哪些分支未合并入当前分支12、git fetch origin更新远程库到本地13、git push origin mybranch推送分支14、git merged origin/mybranch去远程分支合并到本地15、git push origin:mybranch删除远程分支
2017年04月23日
25 阅读
0 评论
0 点赞
2016-06-07
链接应该在新窗口打开吗
从易用性的观点来说,强制在新窗口打开链接,违反了一个用户界面设计的基础原则:应该让用户对他们正在交互的界面有控制权。 一个友好并且实际有效的界面设计,在用户做操作的时候,总是能让他们按自己的意志做出决定。当用户在使用界面元素的时候,他们必须知道、理解、并且能预料到什么将会发生。这才是以用户为中心的设计。 有经验的用户,非常强烈的希望由他们来操控整个系统,系统对他们的行为产生回应。 我自己的浏览习惯是,使用多标签式浏览器,找到要浏览的信息列表页,例如论坛的一个版块,或搜索引擎返回的结果页,然后连续拖拽好几个自己感兴趣的链接让页面在新标签非激活状态打开,然后切换到最选装载完成的标签来查看内容。 强制新窗口打开链接的主要问题是,没有强制新窗口打开的链接用户可以通过右键菜单或按shift键(在一些浏览器里可以是拖拽)这种比较容易的方法来在新窗口打开,而强制在新窗口打开的链接要让用户在本身窗口打开却不容易(可以拖动链接到地址栏来在当前窗口打开链接),所以强制新窗口打开链接,超越了用户自己的决定,剥夺了用户的控制权。 对于浏览网站比较熟练的国内用户来说有几个特点: 1、就算是浏览目的很明确的浏览者,也未必能立即找到完全满足自己需要的信息,搜寻和比较必不可少; 2、目前的网速和国内的网页体积来说,网页很少能够即点即开般在一两秒钟内打开,所以许多人习惯一次点开多个页面,让等待页面加载的时间集中在一起。 3、非常讨厌意料之外的弹出窗口。 我记得在2000年,我刚接触网络的时候,机器配置都比较差,网速却又很慢,一方面我不能打开太多窗口以免占用大多资源让机器反应变慢,另一方面我又不时地打开新窗口,以便同时加载多个页面,选择最先加载完成的页面浏览。 为了比较准确地控制窗口的数量,我希望每个链接都能够由我来控制是在自身窗口打开或是新窗口打开。 我的意见是,谨慎地使用新开窗口,并且新开窗口要给予适当的暗示。一个链接是不是在新窗口打开,尽量交给用户来决定。如果他们想在新窗口打开他们会自己去做,不要低估他们的智力帮他们去做决定什么的。 当你的鼠标移到一个链接上的时候,浏览器并不会提示你是自身窗口打开或是新窗口打开。 1、要么给新窗口打开的链接一种特殊的颜色或图标;采用ICON通知访问者外部链接是一种很常见的做法。使用css属性选择器或javascript都可以实现对链接的筛选、外观修改。 2、大部分链接让用户自己按shift键(在一些浏览器里是拖拽)来决定在新窗口打开链接。 考虑到有一部分刚接触网络的用户,使用浏览器还不熟练,甚至也不知道使用shift键(或拖拽)来新开窗口,所以在一些必需的情况下还是应该强制在新窗口打开链接。 在以下几种情况下,强制在新窗口打开链接是比较合适的: 1、链接指向一个本域名之外的网站:如友情链接一 般使用新窗口,新的网站新的窗口,基本上是可预料的。 2、提供帮助类的链接:如一个购物页面上对支付方式的帮助说明可以新开窗口,如果内容不多的话使用弹出层比新窗口更好。 3、页面跳转有可能打断一个正在进行的进程:如在注册页面上指向免责条款、版权声明等页面的链接,页面跳转会导致用户正在填写的注册信息丢失。 4、链接指向一个非HTML文件。例如指向一个pdf文件的链接最好使用新窗口,也许这个pdf会在新窗口直接浏览,也许会弹出文件下载对话框。
2016年06月07日
84 阅读
0 评论
0 点赞
2016-06-07
IE6/7下的console.log()
相信很多人和我一样,在firefox下常常用console.log()输出一些对象或变量的值,在ie6/7里常常用alert()弹出一些对象或变量的值,来作调试。有时候忘记了删除console.log()语句(或alert语句),结果在ie6/7(或没有开启firebug控制台的firefox下也会)下弹出了错误信息。 我常常在自己的common.js里加入以下代码来容错:if(!window.console){ window.console={} window.console.log=function(){return;} } 当然,能够在ie下使用console.log的调试功能当然是最好的,哪怕只是最简单的一个变量值的输出也比alert友好一些,于是有了下面这个简易版的console.log<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>ie下的console.log</title> <style> body {background: #ffffff; color: #444;} a{ color: #08e; text-decoration: none; border: 0; background-color: transparent;} body,div,q,iframe,form,h5{ margin: 0; padding: 0;} a img,fieldset { border: none 0; } body,td,textarea { word-break: break-all; word-wrap: break-word; line-height:1.5;} body,input,textarea,select,button { margin: 0; font-size: 12px; font-family: Tahoma, SimSun, sans-serif;} div,p,table,th,td { font-size:1em; font-family:inherit; line-height:inherit;} </style> <script type="text/javascript"> var debug=true; var $id = function (id) {//避免与jQuery的$函数冲突 return typeof id == "string" ? document.getElementById(id) : id; }; if(!$){var $=$id;} if(!window.console){ window.console={} window.console.cache=[]; window.console.constr=function(_value){ if(!_value)return; var result = []; if (_value instanceof Function){ result.push(_value); }else if(_value!=undefined&&Boolean(_value.nodeName)&&Boolean(_value.nodeType)){ result.push(_value.nodeName.toLowerCase()); result.push(_value.getAttribute("id")?"id="+_value.getAttribute("id"):""); result.push(_value.getAttribute("className")?"class="+_value.getAttribute("className"):""); return "<"+result.join(" ")+">"; }else if(_value instanceof Array){ for(var i=0; i< _value.length; i++) result.push(_value[i]); return "["+result.join(", ")+"]"; }else if(typeof _value == "object"){ for (var p in _value){ if(_value.hasOwnProperty(p) && p!='prototype'){ result.push("'"+p+"':"+_value[p]); } }; return "{"+result.join(", ")+"}"; }else if(typeof _value == 'string'){ return "\""+_value+"\""; }else if(typeof _value == 'number' && isFinite(_value)){ result.push(_value); }else{ result.push(_value); } return result.join(""); } window.console.log=function(outputValue){ if(!debug)return; if(!outputValue)return null; var bgColor=bgColor||"#fff"; consoleDiv =$id("_console"); if(!consoleDiv){ consoleDiv=document.createElement("div"); consoleDiv.id="_console"; consoleDiv.style.cssText="position:absolute; z-index:9999; left:0%;top:"+Math.max(document.documentElement.scrollTop, document.body.scrollTop)+"px; width:62%; background-color:#fff; border:1px solid #359; opacity:0.9; filter:alpha(opacity=90); padding:4px;" consoleDiv.innerHTML='<div id="_consoleHead" style="background-color:#cde; height:20px; color:#000; font-size:12px; line-height:20px; cursor:move;"><a style="color:#123; float:right; text-decoration:none; margin:1px 2px 0;" href="javascript:$id(\'_console\').style.display=\'none\';void(0);">[关闭]</a><a style="color:#123; float:right; text-decoration:none; margin:1px 2px 0;" href="javascript:$id(\'_consoleBody\').innerHTML=\'\';void(0);">[清空]</a></div>'; consoleDivBody=document.createElement("div"); consoleDivBody.id="_consoleBody"; consoleDivBody.style.cssText="font-size:12px; line-height:1.5;color:#333; width:100%; max-height:150px; overflow:auto;" consoleDivBody.innerHTML=''; consoleDiv.appendChild(consoleDivBody); document.getElementsByTagName("BODY")[0].appendChild(consoleDiv); if(Drag) Drag.init(consoleDiv,consoleDiv);//注册拖拽方法,可以使用自己的拖拽方法来代替,以减少代码量 } consoleDiv.style.display=""; var consoleDivTop=consoleDiv.style.top.replace(/\D/gi,""); if(consoleDivTop<Math.max(document.documentElement.scrollTop, document.body.scrollTop)||consoleDivTop>Math.max(document.documentElement.scrollTop, document.body.scrollTop)+(document.compatMode == "BackCompat"?document.body.clientHeight:document.documentElement.clientHeight)) consoleDiv.style.top=Math.max(document.documentElement.scrollTop, document.body.scrollTop)+"px"; var newItem=document.createElement("div"); newItem.style.cssText="border-top:1px solid #cde; padding:3px;font-family:'Courier New'; font-size:13px; background-color:"+bgColor; var content = []; for(var i=0, len=arguments.length; i<len; i++){ content.push( window.console.constr(arguments[i]) ); } newItem.innerHTML= content.join(" "); $id("_consoleBody").appendChild(newItem); $id("_consoleBody").scrollTop=9999; }; } /***小巧的拖拽类***/ var Drag={ "obj":null, "init":function(handle, dragBody, e){ if (e == null) { handle.onmousedown=Drag.start; } handle.root = dragBody; if(isNaN(parseInt(handle.root.style.left)))handle.root.style.left="0px"; if(isNaN(parseInt(handle.root.style.top)))handle.root.style.top="0px"; handle.root.onDragStart=new Function(); handle.root.onDragEnd=new Function(); handle.root.onDrag=new Function(); if (e !=null) { var handle=Drag.obj=handle; e=Drag.fixe(e); var top=parseInt(handle.root.style.top); var left=parseInt(handle.root.style.left); handle.root.onDragStart(left,top,e.pageX,e.pageY); handle.lastMouseX=e.pageX; handle.lastMouseY=e.pageY; document.onmousemove=Drag.drag; document.onmouseup=Drag.end; } }, "start":function(e){ var handle=Drag.obj=this; e=Drag.fixEvent(e); var top=parseInt(handle.root.style.top); var left=parseInt(handle.root.style.left); //alert(left) handle.root.onDragStart(left,top,e.pageX,e.pageY); handle.lastMouseX=e.pageX; handle.lastMouseY=e.pageY; document.onmousemove=Drag.drag; document.onmouseup=Drag.end; return false; }, "drag":function(e){ e=Drag.fixEvent(e); var handle=Drag.obj; var mouseY=e.pageY; var mouseX=e.pageX; var top=parseInt(handle.root.style.top); var left=parseInt(handle.root.style.left); if(document.all){Drag.obj.setCapture();}else{e.preventDefault();};//作用是将所有鼠标事件捕获到handle对象,对于firefox,以用preventDefault来取消事件的默认动作: var currentLeft,currentTop; currentLeft=left+mouseX-handle.lastMouseX; currentTop=top+(mouseY-handle.lastMouseY); handle.root.style.left=currentLeft +"px"; handle.root.style.top=currentTop+"px"; handle.lastMouseX=mouseX; handle.lastMouseY=mouseY; handle.root.onDrag(currentLeft,currentTop,e.pageX,e.pageY); return false; }, "end":function(){ if(document.all){Drag.obj.releaseCapture();};//取消所有鼠标事件捕获到handle对象 document.onmousemove=null; document.onmouseup=null; Drag.obj.root.onDragEnd(parseInt(Drag.obj.root.style.left),parseInt(Drag.obj.root.style.top)); Drag.obj=null; }, "fixEvent":function(e){//格式化事件参数对象 var sl = Math.max(document.documentElement.scrollLeft, document.body.scrollLeft); var st = Math.max(document.documentElement.scrollTop, document.body.scrollTop); if(typeof e=="undefined")e=window.event; if(typeof e.layerX=="undefined")e.layerX=e.offsetX; if(typeof e.layerY=="undefined")e.layerY=e.offsetY; if(typeof e.pageX == "undefined")e.pageX = e.clientX + sl - document.body.clientLeft; if(typeof e.pageY == "undefined")e.pageY = e.clientY + st - document.body.clientTop; return e; } }; /***测试***/ function sometext(ele,n){ var strArr=["可","以","清","心","也"]; var writeStr="" for(i=0;i<n;i++){ index=parseInt(Math.random()*5); for(j=0;j<5;j++){ str=index+j>4?index+j-5:index+j; writeStr+=strArr[str]; } } $(ele).innerHTML=writeStr; } function test1(){ console.log("这是一个js对象:",{w:100,h:50,area:function(){return this.w*this.h}}) } function test2(){ console.log("这是一个dom对象:",$id("div1")) } function test3(){ var arr=function(w,h){return w*h;} console.log("这是一个js函数:",arr) } function test4(){ var arr=[1,2.2,"a","abc",true,false,new Date(),/<[^>]*>/,function(){alert(0);}] console.log("这是一个数组:",arr) } </script> </head> <body> <div id="div1" class="style1"></div> <p><input type="button" value="返回一个object" onclick="test1()" /> <input type="button" value="返回一个element" onclick="test2()" /> <input type="button" value="返回一个function" onclick="test3()" /> <input type="button" value="返回一个arrary" onclick="test4()" /> </p> <div id="div2" class="style1"></div> <script>sometext("div1",200);sometext("div2",200);</script> </body> </html>如果不要自带的drag方法(大家的js库里一般都有自己的drag方法吧)的话,代码大约60行,方便调试,也不会让你的js库变得臃肿。
2016年06月07日
49 阅读
0 评论
0 点赞
2016-05-29
Web前端常用的JavaScript方法封装
1、输入一个值,返回其数据类型function type(para) { return Object.prototype.toString.call(para) }2、数组去重function unique1(arr) { return [...new Set(arr)] } function unique2(arr) { var obj = {}; return arr.filter(ele => { if (!obj[ele]) { obj[ele] = true; return true; } }) } function unique3(arr) { var result = []; arr.forEach(ele => { if (result.indexOf(ele) == -1) { result.push(ele) } }) return result; }3、字符串去重String.prototype.unique = function () { var obj = {}, str = '', len = this.length; for (var i = 0; i < len; i++) { if (!obj[this[i]]) { str += this[i]; obj[this[i]] = true; } } return str; } //去除连续的字符串 function uniq(str) { return str.replace(/(\w)\1+/g, '$1') }4、深拷贝 浅拷贝//深克隆(深克隆不考虑函数) function deepClone(obj, result) { var result = result || {}; for (var prop in obj) { if (obj.hasOwnProperty(prop)) { if (typeof obj[prop] == 'object' && obj[prop] !== null) { // 引用值(obj/array)且不为null if (Object.prototype.toString.call(obj[prop]) == '[object Object]') { // 对象 result[prop] = {}; } else { // 数组 result[prop] = []; } deepClone(obj[prop], result[prop]) } else { // 原始值或func result[prop] = obj[prop] } } } return result; } // 深浅克隆是针对引用值 function deepClone(target) { if (typeof (target) !== 'object') { return target; } var result; if (Object.prototype.toString.call(target) == '[object Array]') { // 数组 result = [] } else { // 对象 result = {}; } for (var prop in target) { if (target.hasOwnProperty(prop)) { result[prop] = deepClone(target[prop]) } } return result; } // 无法复制函数 var o1 = jsON.parse(jsON.stringify(obj1));5、reverse底层原理和扩展// 改变原数组 Array.prototype.myReverse = function () { var len = this.length; for (var i = 0; i < len; i++) { var temp = this[i]; this[i] = this[len - 1 - i]; this[len - 1 - i] = temp; } return this; }6、圣杯模式的继承function inherit(Target, Origin) { function F() {}; F.prototype = Origin.prototype; Target.prototype = new F(); Target.prototype.constructor = Target; // 最终的原型指向 Target.prop.uber = Origin.prototype; }7、找出字符串中第一次只出现一次的字母String.prototype.firstAppear = function () { var obj = {}, len = this.length; for (var i = 0; i < len; i++) { if (obj[this[i]]) { obj[this[i]]++; } else { obj[this[i]] = 1; } } for (var prop in obj) { if (obj[prop] == 1) { return prop; } } }8、找元素的第n级父元素function parents(ele, n) { while (ele && n) { ele = ele.parentElement ? ele.parentElement : ele.parentNode; n--; } return ele; }9、返回元素的第n个兄弟节点function retSibling(e, n) { while (e && n) { if (n > 0) { if (e.nextElementSibling) { e = e.nextElementSibling; } else { for (e = e.nextSibling; e && e.nodeType !== 1; e = e.nextSibling); } n--; } else { if (e.previousElementSibling) { e = e.previousElementSibling; } else { for (e = e.previousElementSibling; e && e.nodeType !== 1; e = e.previousElementSibling); } n++; } } return e; }10、封装mychildren,解决浏览器的兼容问题function myChildren(e) { var children = e.childNodes, arr = [], len = children.length; for (var i = 0; i < len; i++) { if (children[i].nodeType === 1) { arr.push(children[i]) } } return arr; }11、判断元素有没有子元素function hasChildren(e) { var children = e.childNodes, len = children.length; for (var i = 0; i < len; i++) { if (children[i].nodeType === 1) { return true; } } return false; }12、我一个元素插入到另一个元素的后面Element.prototype.insertAfter = function (target, elen) { var nextElen = elen.nextElenmentSibling; if (nextElen == null) { this.appendChild(target); } else { this.insertBefore(target, nextElen); } }13、返回当前的时间(年月日时分秒)function getDateTime() { var date = new Date(), year = date.getFullYear(), month = date.getMonth() + 1, day = date.getDate(), hour = date.getHours() + 1, minute = date.getMinutes(), second = date.getSeconds(); month = checkTime(month); day = checkTime(day); hour = checkTime(hour); minute = checkTime(minute); second = checkTime(second); function checkTime(i) { if (i < 10) { i = "0" + i; } return i; } return "" + year + "年" + month + "月" + day + "日" + hour + "时" + minute + "分" + second + "秒" }14、获得滚动条的滚动距离function getScrollOffset() { if (window.pageXOffset) { return { x: window.pageXOffset, y: window.pageYOffset } } else { return { x: document.body.scrollLeft + document.documentElement.scrollLeft, y: document.body.scrollTop + document.documentElement.scrollTop } } }15、获得视口的尺寸function getViewportOffset() { if (window.innerWidth) { return { w: window.innerWidth, h: window.innerHeight } } else { // ie8及其以下 if (document.compatMode === "BackCompat") { // 怪异模式 return { w: document.body.clientWidth, h: document.body.clientHeight } } else { // 标准模式 return { w: document.documentElement.clientWidth, h: document.documentElement.clientHeight } } } }16、获取任一元素的任意属性function getStyle(elem, prop) { return window.getComputedStyle ? window.getComputedStyle(elem, null)[prop] : elem.currentStyle[prop] }17、绑定事件的兼容代码function addEvent(elem, type, handle) { if (elem.addEventListener) { //非ie和非ie9 elem.addEventListener(type, handle, false); } else if (elem.attachEvent) { //ie6到ie8 elem.attachEvent('on' + type, function () { handle.call(elem); }) } else { elem['on' + type] = handle; } }18、解绑事件function removeEvent(elem, type, handle) { if (elem.removeEventListener) { //非ie和非ie9 elem.removeEventListener(type, handle, false); } else if (elem.detachEvent) { //ie6到ie8 elem.detachEvent('on' + type, handle); } else { elem['on' + type] = null; } }19、取消冒泡的兼容代码function stopBubble(e) { if (e && e.stopPropagation) { e.stopPropagation(); } else { window.event.cancelBubble = true; } }20、检验字符串是否是回文function isPalina(str) { if (Object.prototype.toString.call(str) !== '[object String]') { return false; } var len = str.length; for (var i = 0; i < len / 2; i++) { if (str[i] != str[len - 1 - i]) { return false; } } return true; }21、检验字符串是否是回文function isPalindrome(str) { str = str.replace(/\W/g, '').toLowerCase(); console.log(str) return (str == str.split('').reverse().join('')) }22、兼容getElementsByClassName方法Element.prototype.getElementsByClassName = Document.prototype.getElementsByClassName = function (_className) { var allDomArray = document.getElementsByTagName('*'); var lastDomArray = []; function trimSpace(strClass) { var reg = /\s+/g; return strClass.replace(reg, ' ').trim() } for (var i = 0; i < allDomArray.length; i++) { var classArray = trimSpace(allDomArray[i].className).split(' '); for (var j = 0; j < classArray.length; j++) { if (classArray[j] == _className) { lastDomArray.push(allDomArray[i]); break; } } } return lastDomArray; }23、运动函数function animate(obj, json, callback) { clearInterval(obj.timer); var speed, current; obj.timer = setInterval(function () { var lock = true; for (var prop in json) { if (prop == 'opacity') { current = parseFloat(window.getComputedStyle(obj, null)[prop]) * 100; } else { current = parseInt(window.getComputedStyle(obj, null)[prop]); } speed = (json[prop] - current) / 7; speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed); if (prop == 'opacity') { obj.style[prop] = (current + speed) / 100; } else { obj.style[prop] = current + speed + 'px'; } if (current != json[prop]) { lock = false; } } if (lock) { clearInterval(obj.timer); typeof callback == 'function' ? callback() : ''; } }, 30) }24、弹性运动function ElasticMovement(obj, target) { clearInterval(obj.timer); var iSpeed = 40, a, u = 0.8; obj.timer = setInterval(function () { a = (target - obj.offsetLeft) / 8; iSpeed = iSpeed + a; iSpeed = iSpeed * u; if (Math.abs(iSpeed) <= 1 && Math.abs(a) <= 1) { console.log('over') clearInterval(obj.timer); obj.style.left = target + 'px'; } else { obj.style.left = obj.offsetLeft + iSpeed + 'px'; } }, 30); }25、封装自己的forEach方法Array.prototype.myForEach = function (func, obj) { var len = this.length; var _this = arguments[1] ? arguments[1] : window; // var _this=arguments[1]||window; for (var i = 0; i < len; i++) { func.call(_this, this[i], i, this) } }26、封装自己的filter方法Array.prototype.myFilter = function (func, obj) { var len = this.length; var arr = []; var _this = arguments[1] || window; for (var i = 0; i < len; i++) { func.call(_this, this[i], i, this) && arr.push(this[i]); } return arr; }27、数组map方法Array.prototype.myMap = function (func) { var arr = []; var len = this.length; var _this = arguments[1] || window; for (var i = 0; i < len; i++) { arr.push(func.call(_this, this[i], i, this)); } return arr; }28、数组every方法Array.prototype.myEvery = function (func) { var flag = true; var len = this.length; var _this = arguments[1] || window; for (var i = 0; i < len; i++) { if (func.apply(_this, [this[i], i, this]) == false) { flag = false; break; } } return flag; }29、数组reduce方法Array.prototype.myReduce = function (func, initialValue) { var len = this.length, nextValue, i; if (!initialValue) { // 没有传第二个参数 nextValue = this[0]; i = 1; } else { // 传了第二个参数 nextValue = initialValue; i = 0; } for (; i < len; i++) { nextValue = func(nextValue, this[i], i, this); } return nextValue; }30、获取url中的参数function getWindonHref() { var sHref = window.location.href; var args = sHref.split('?'); if (args[0] === sHref) { return ''; } var hrefarr = args[1].split('#')[0].split('&'); var obj = {}; for (var i = 0; i < hrefarr.length; i++) { hrefarr[i] = hrefarr[i].split('='); obj[hrefarr[i][0]] = hrefarr[i][1]; } return obj; }31、数组排序// 快排 [left] + min + [right] function quickArr(arr) { if (arr.length <= 1) { return arr; } var left = [], right = []; var pIndex = Math.floor(arr.length / 2); var p = arr.splice(pIndex, 1)[0]; for (var i = 0; i < arr.length; i++) { if (arr[i] <= p) { left.push(arr[i]); } else { right.push(arr[i]); } } // 递归 return quickArr(left).concat([p], quickArr(right)); } // 冒泡 function bubbleSort(arr) { for (var i = 0; i < arr.length - 1; i++) { for (var j = i + 1; j < arr.length; j++) { if (arr[i] > arr[j]) { var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } return arr; } function bubbleSort(arr) { var len = arr.length; for (var i = 0; i < len - 1; i++) { for (var j = 0; j < len - 1 - i; j++) { if (arr[j] > arr[j + 1]) { var temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } return arr; }32、遍历Dom树// 给定页面上的DOM元素,将访问元素本身及其所有后代(不仅仅是它的直接子元素) // 对于每个访问的元素,函数讲元素传递给提供的回调函数 function traverse(element, callback) { callback(element); var list = element.children; for (var i = 0; i < list.length; i++) { traverse(list[i], callback); } }33、原生js封装ajaxfunction ajax(method, url, callback, data, flag) { var xhr; flag = flag || true; method = method.toUpperCase(); if (window.XMLHttpRequest) { xhr = new XMLHttpRequest(); } else { xhr = new ActiveXObject('Microsoft.XMLHttp'); } xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { console.log(2) callback(xhr.responseText); } } if (method == 'GET') { var date = new Date(), timer = date.getTime(); xhr.open('GET', url + '?' + data + '&timer' + timer, flag); xhr.send() } else if (method == 'POST') { xhr.open('POST', url, flag); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.send(data); } }34、异步加载scriptfunction loadScript(url, callback) { var oscript = document.createElement('script'); if (oscript.readyState) { // ie8及以下版本 oscript.onreadystatechange = function () { if (oscript.readyState === 'complete' || oscript.readyState === 'loaded') { callback(); } } } else { oscript.onload = function () { callback() }; } oscript.src = url; document.body.appendChild(oscript); }35、cookie管理var cookie = { set: function (name, value, time) { document.cookie = name + '=' + value + '; max-age=' + time; return this; }, remove: function (name) { return this.setCookie(name, '', -1); }, get: function (name, callback) { var allCookieArr = document.cookie.split('; '); for (var i = 0; i < allCookieArr.length; i++) { var itemCookieArr = allCookieArr[i].split('='); if (itemCookieArr[0] === name) { return itemCookieArr[1] } } return undefined; } }36、实现bind()方法Function.prototype.myBind = function (target) { var target = target || window; var _args1 = [].slice.call(arguments, 1); var self = this; var temp = function () {}; var F = function () { var _args2 = [].slice.call(arguments, 0); var parasArr = _args1.concat(_args2); return self.apply(this instanceof temp ? this : target, parasArr) } temp.prototype = self.prototype; F.prototype = new temp(); return F; }37、实现call()方法Function.prototype.myCall = function () { var ctx = arguments[0] || window; ctx.fn = this; var args = []; for (var i = 1; i < arguments.length; i++) { args.push(arguments[i]) } var result = ctx.fn(...args); delete ctx.fn; return result; }38、实现apply()方法Function.prototype.myApply = function () { var ctx = arguments[0] || window; ctx.fn = this; if (!arguments[1]) { var result = ctx.fn(); delete ctx.fn; return result; } var result = ctx.fn(...arguments[1]); delete ctx.fn; return result; }39、防抖function debounce(handle, delay) { var timer = null; return function () { var _self = this, _args = arguments; clearTimeout(timer); timer = setTimeout(function () { handle.apply(_self, _args) }, delay) } }40、节流function throttle(handler, wait) { var lastTime = 0; return function (e) { var nowTime = new Date().getTime(); if (nowTime - lastTime > wait) { handler.apply(this, arguments); lastTime = nowTime; } } }41、requestAnimFrame兼容性方法window.requestAnimFrame = (function () { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) { window.setTimeout(callback, 1000 / 60); }; })();42、cancelAnimFrame兼容性方法window.cancelAnimFrame = (function () { return window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || function (id) { window.clearTimeout(id); }; })();43、jsonp底层方法function jsonp(url, callback) { var oscript = document.createElement('script'); if (oscript.readyState) { // ie8及以下版本 oscript.onreadystatechange = function () { if (oscript.readyState === 'complete' || oscript.readyState === 'loaded') { callback(); } } } else { oscript.onload = function () { callback() }; } oscript.src = url; document.body.appendChild(oscript); }44、获取url上的参数function getUrlParam(sUrl, sKey) { var result = {}; sUrl.replace(/(\w+)=(\w+)(?=[&|#])/g, function (ele, key, val) { if (!result[key]) { result[key] = val; } else { var temp = result[key]; result[key] = [].concat(temp, val); } }) if (!sKey) { return result; } else { return result[sKey] || ''; } }45、格式化时间function formatDate(t, str) { var obj = { yyyy: t.getFullYear(), yy: ("" + t.getFullYear()).slice(-2), M: t.getMonth() + 1, MM: ("0" + (t.getMonth() + 1)).slice(-2), d: t.getDate(), dd: ("0" + t.getDate()).slice(-2), H: t.getHours(), HH: ("0" + t.getHours()).slice(-2), h: t.getHours() % 12, hh: ("0" + t.getHours() % 12).slice(-2), m: t.getMinutes(), mm: ("0" + t.getMinutes()).slice(-2), s: t.getSeconds(), ss: ("0" + t.getSeconds()).slice(-2), w: ['日', '一', '二', '三', '四', '五', '六'][t.getDay()] }; return str.replace(/([a-z]+)/ig, function ($1) { return obj[$1] }); }46、验证邮箱的正则表达式function isAvailableEmail(sEmail) { var reg = /^([\w+\.])+@\w+([.]\w+)+$/ return reg.test(sEmail) }47、函数柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术function curryIt(fn) { var length = fn.length, args = []; var result = function (arg) { args.push(arg); length--; if (length <= 0) { return fn.apply(this, args); } else { return result; } } return result; }48、大数相加function sumBigNumber(a, b) { var res = '', //结果 temp = 0; //按位加的结果及进位 a = a.split(''); b = b.split(''); while (a.length || b.length || temp) { //~~按位非 1.类型转换,转换成数字 2.~~undefined==0 temp += ~~a.pop() + ~~b.pop(); res = (temp % 10) + res; temp = temp > 9; } return res.replace(/^0+/, ''); }49、单例模式function getSingle(func) { var result; return function () { if (!result) { result = new func(arguments); } return result; } }50、前端随机字符串var uuid = Date.now().toString(36) + Math.random().toString(36).substr(4)51、检测浏览器是否支持transformlet isSuportsTransform = false; (function () { if (typeof CSS === 'object' && typeof CSS.supports === 'function') { isSuportsTransform = CSS.supports('transform:scale(1.2)'); } else if('transform' in document.body.style){ isSuportsTransform = true } })();52、获取当前js文件的路径 var getCurrentScript = function (base) { if (document.currentScript) { return document.currentScript.src // FF,Chrome } var stack try { a.b.c() // 强制报错,以便捕获e.stack } catch (e) { // safari的错误对象只有line,sourceId,sourceURL stack = e.stack if (!stack && window.opera) { // opera 9没有e.stack,但有e.Backtrace,但不能直接取得,需要对e对象转字符串进行抽取 stack = (String(e).match(/of linked script \S+/g) || []).join(' ') } } if (stack) { stack = stack.split(/[@ ]/g).pop() // 取得最后一行,最后一个空格或@之后的部分 stack = stack[0] === '(' ? stack.slice(1, -1) : stack.replace(/\s/, '') // 去掉换行符 return stack.replace(/(:\d+)?:\d+$/i, '') // 去掉行号与或许存在的出错字符起始位置 } var nodes = (base ? document : head).getElementsByTagName('script') // 只在head标签中寻找 for (var i = nodes.length, node; (node = nodes[--i]);) { if (node.readyState === 'interactive') { return node.src } } var node = nodes[nodes.length - 1] return node.hasAttribute ? node.src : node.getAttribute('src', 4) } var jsPath = getCurrentScript(true) var cssPath = jsPath.substr(0, jsPath.lastIndexOf('/')) + '/xxxx.css' // 获取同目录的css文件
2016年05月29日
12 阅读
0 评论
0 点赞
1
...
6
7
8