-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfeed.json
1 lines (1 loc) · 42.4 KB
/
feed.json
1
{"title":"贾小强的博客","description":"在痛苦中成长,在成长中感受痛苦","language":"zh-CN","link":"//qiangck.club","pubDate":"Sat, 31 Aug 2019 07:49:00 GMT","lastBuildDate":"Tue, 18 Feb 2020 04:39:54 GMT","generator":"hexo-generator-json-feed","webMaster":"qiangck","items":[{"title":"Html5语音播报","link":"//qiangck.club/2019/08/31/Html5语音播报/","description":"一个需求引发的实践 需求:供应商为了减少订单的丢失率,想在SaaS平台增加试试订单语音播报功能 技术方案:由后台使用第三方服务,将文案转化成音频文件并保存在服务器,生成音频地址返回给前端,前端使用Audio标签对音频文件进行播放。由于开发工期和使用三方服务的使用成本考虑,考虑由前端来处理语音播放问题,后台只处理订单信息。 最终方案:使用Html5 Web Speech API API使用 1、属性 SpeechSynthesis.paused 是否为暂停 SpeechSynthesis.pending 是否有未完成播放 SpeechSynthesis.speaking 是否正在播放 2、方法 cancel 取消 getVoices 返回设备可播放语音列表 pause 暂停 resume 重启 speak 播放,接收SpeechSynthesisUtterance实例 实现 1、首先简单封装一下使用方法 123456789101112131415161718192021222324252627282930313233343536class VoicePlay { constructor () { this.volume = 1 this.speechSynthesis = window.speechSynthesis } // 简单使用,配置默认写死 speak (text, callback) { let utterance = new SpeechSynthesisUtterance() utterance.text = text utterance.lang = 'zh-CN' utterance.volume = this.volume utterance.onend = function () { callback && callback() } // 添加到队列 this.speechSynthesis.speak(utterance) } // 播放 play (message, callback) { this.speak(message, callback) } // 停止 stop () { this.speechSynthesis.cancel() } // 静音 mute (value) { this.volume = value || 0 } // 是否播放 isPlaying () { return this.speechSynthesis.speaking }} 2、由于订单信息可能会同时返回很多条,所以需要生成一个队列来进行逐条播放 123456789101112131415function voicePlay () { let queues = [] let fn = function (index) { // 显示模态窗 VoicePlay.play(/* 需要播放的内容 */, () => { if (queues[index + 1]) { fn(index + 1) } else { VoicePlay.stop() } // 关闭模态窗 }) } fn(0)} 3、加个定时器轮询接口,拿订单数据,插入到队列中,如果有新消息停止播放清空队列重新播放新消息。 后记 简单的消息播放实现了,SpeechSynthesis属性其实就是调用本地TTS语音引擎进行播放的,如果window系统不能正常播放,很有可能是本地卸载了或不存在要播放的语言包。SpeechSynthesis属性的兼容还可以,除了IE浏览器不支持外,其他大部分的浏览器都是支持的,如果想支持IE浏览器,可以使用ActiveX控件进行支持或者使用百度的接口。 1\"http://tts.baidu.com/text2audio?lan=zh&ie=UTF-8&text=\" + encodeURI(text)","pubDate":"Sat, 31 Aug 2019 07:49:00 GMT","guid":"//qiangck.club/2019/08/31/Html5语音播报/","category":"JavaScript"},{"title":"前端异常监控思考","link":"//qiangck.club/2018/12/20/前端异常监控思考/","description":"什么是前端代码异常 前端代码异常分两种,1、代码语法错误 2、代码运行时产生错误,第一种错误基本上在项目build的时候就能避免,但是第二种可能无法测试出来,在上线后很难发现,只有用户使用在特殊情况使用时才可能测试出错误。例如:约定好的接口返回数据是个Array格式,但是因为没有做好异常处理导致了错误的发生。 null.map() //error 如何捕获异常 1、主动判断 这个很好理解,就是开发者认为执行过程中某处可能会发上错误,预判断错误可能发生的位置,这个考验程序员的基本功。 123456789const format = num => { // 一些对入参的处理内容 return num}if (typeof format() !== 'number') { // 可以执行错误内容上传动作 console.error('错误啦,赶紧看看')} 2、try…catch 这个也是工程师经常使用的,比如使用async的时候或者JSON.parse()的时候,这个方式避免了页面挂掉的风险,不过也是需要程序员单独处理。 123456try { JSON.parse()} catch (err) { // 可以执行错误内容上传动作 console.error(err, '转换格式错误啦')} 3、window.onerror 这个方法浏览器很早就支持了,能够捕获全局的错误,大家也不用考虑兼容问题,但是需要考虑浏览器返回的堆栈的不同,这后面说。 123456789window.onerror = (message, source, lineno, colno, error) => { console.error(message, '有错误及时检查啊') // message:错误信息(字符串) // source:发生错误的脚本URL(字符串) // lineno:发生错误的行号(数字) // colno:发生错误的列号(数字) // error:Error对象(对象) // 若该函数返回true,则阻止执行默认事件处理函数。} 这三种方式如果想收集到所有的错误,用第三种方式还是比较理想的,前两种方式再某种情况下也是可以使用的,比较灵活可控,但是我们需要的是一种SDK的植入方式来捕获错误,所以还是需要获取到所有的错误的。JavaScript环境下执行脚本的时候,一旦发现错误,不管目前的堆栈有多深,不管代码运行到了何处,直接跑到顶层或者 try..catch 捕获的那一层。 window.onerror使用 window.onerror方法会返回大部分浏览器会返回错误信息,错误地址,错误代码的行、列数和堆栈对象,虽然这个方法没有什么兼容性,但是他的返回参数再各个浏览器下会有差异,在这里只阐述现代浏览器,注意是现代浏览器,如果有对IE感兴趣或者需求的话,可以自己测试一下,IE10以下是没有堆栈信息的,需要使用特殊方式获取。 window.event && window.event.errorCharacter 现代浏览器中也分了三种种情况: 1、正常返回所有参数,但是其中的行数、列数和地址是错误的,所有需要从堆栈中获取准确的错误信息,具体的堆栈信息也是有不同的,这个下面具体说明。 2、Android手机下或部分浏览器下没有返回堆栈信息,不过还好里面返回的错误行数列数等信息是比较准确的,所以只能返回这些信息来判断具体的错误内容。 3、还有一种比较特殊的情况就是获取跨域的脚本错误时候,浏览器为了避免信息泄露message会返回‘Script error’并且不会拿到任何可用的错误信息,这个下面具体说明一下。 123456789101112131415161718192021222324252627282930const ONERROR = window.onerrorwindow.onerror = (msg, source, lineno, colno, errorObj) => { try { let message = msg ? msg.toLowerCase() : '' let _err = null if (!errorObj || message.indexOf('script error') > 0) { send({ message: message, url: source, row: lineno, col: colno }) ONERROR && ONERROR.apply(window, arguments) return true } if (errorObj && errorObj.stack) { // 格式化堆栈信息 _err = formatStackError(errorObj) } send({ url: _err.url || source, col: _err.col || colno, row: _err.row || lineno, message: _err.message || message }) ONERROR && ONERROR.apply(window, arguments) } catch (err) { ONERROR && ONERROR.apply(window, arguments) }} onerror堆栈信息 堆栈信息的内容也可以分为三种情况,哎~~浏览器厂商真的是不能让人消停下来 1、部分浏览器的堆栈对象里面没有行和列的信息,只能从堆栈对象里面的stack属性解析出来,url信息需要使用正则表达式解析。 2、Firefox和Chrome的stack属性也有所不一样,里面的行和列分别为 (columnNumber,lineNumber)|| (column,line) 3、部分浏览器是没有stack属性的,这样就只能拿到message了,头疼。 1234567891011121314151617181920212223242526272829303132function formatStackError(errorObj) { try { if (errorObj.stack) { const col = errorObj.column || errorObj.columnNumber const row = errorObj.line || errorObj.lineNumber let message = errorObj.message let stackCol = null let stackRow = null let stackText = '' let url = errorObj.stack.match(/https?:\\/\\/[^\\n]+/) url = url ? url[0] : '' const rowsAndCols = url.match(/:(\\d+):(\\d+)/) if (rowsAndCols && rowsAndCols.length >= 3) { [stackText, stackCol, stackRow] = rowsAndCols } return { message: message, col: Number(col || stackCol), row: Number(row || stackRow), url: url.replace(stackText, '') || url } } else { if (errorObj.name && errorObj.message && errorObj.description) { return { message: JSON.stringify(errorObj) } } } } catch (err) { return errorObj }} 这个处理堆栈信息也可以简单的直接把stack的内容json后上报上去 跨域 跨域资源使用window.onerror捕获错误应该是无解的,没办法浏览器限制的,网上的比较好的办法就是使用anonymous属性,这个属性也是有副作用的,需要服务器配合的并且Safari浏览器是不支持muted errors的,所以这个不是一个很优雅的解决办法。 1<script type=\"text/javascript\" src=\"https://abc/***.js\" crossorigin=\"anonymous\"></script> 服务器要返回的头信息包括:Access-Control-Allow-Origin: * 现在比较好的解决方案就是使用try…catch对固定函数做切片处理,但是这样的就比较不够灵活了。 sourceMap 现在的前端工程大部分的都是被打包压缩的,这样对捕获错误带来了困难,因为捕获到的错误都是1行1000+列,根本无法真正的知道具体错误的内容,这样就需要对打包后的文件通过sourceMap文件进行定位,可以使用source-map对压缩都的文件今天读取。 123456789101112131415161718192021async function formatSourceMap(fileName, line, column) { try { const content = await fs.readJson(fileName) const sources = content.sources sources.forEach(element => { sourcesPathMap[fixPath(element)] = element }) const consumer = await new sourceMap.SourceMapConsumer(content) const lookup = { line: parseInt(line), column: parseInt(column) } const result = consumer.originalPositionFor(lookup) const originSource = sourcesPathMap[result.source] const sourcesContent = content.sourcesContent[sources.indexOf(originSource)] result.sourcesContent = sourcesContent return result } catch (err) { return err }} 这里需要发布的时候把sourceMap文件上传到服务器,上报日志到服务器如果是压缩后的代码,就去服务器找到对应的sourceMap文件进行读取。 后记 window.onerror各浏览器下表现总结","pubDate":"Thu, 20 Dec 2018 09:00:00 GMT","guid":"//qiangck.club/2018/12/20/前端异常监控思考/","category":"JavaScript"},{"title":"放弃npm拥抱yarn","link":"//qiangck.club/2016/12/28/放弃npm拥抱yarn/","description":"yarn介绍 Yarn 是Facebook最近发布的一款依赖包安装工具. 官方认为其快速, 安全, 可靠。你下载的包将不再重新下载。而且确保在不同系统中可以正常工作。(Yarn is a package manager for your code. It allows you to use and share code with other developers from around the world. Yarn does this quickly, securely, and reliably so you don’t ever have to worry.) yarn优点 一致性:Yarn允许使用某个lockfile来保证团队中的所有人使用相同版本的npm依赖包,这一点会大大减少因为某个人系统本身问题而导致的Bug 多样性: Yarn还允许用户将npm包以tar.gz形式打包上传到版本控制系统中,这一点能够利用NPM包本身已经对不同版本的Node或者操作系统做了容错这一特性 离线: Yarn允许离线安装某些依赖,这点对于CI系统特别适用。CI系统就不需要保证有稳定的网络连接,特别是在有墙的地方 速度: Yarn采用了新的算法来保证速度, 比NPM快到2~7倍, 同时也允许使用离线包的方式本地安装依赖 安全: 下载前会检查签名及包的完整性 网络优化 :力求网络资源最大利用化,让资源下载完美队列执行,避免大量的无用请求,下载失败会自动重新请求,避免整个安装过程失败 扁平化: 对于不匹配的依赖版本的包创立一个独立的包,避免创建重复的 缓存:缓存已经下载过的包,避免重复下载 yarn命令 yarn init: 初始化某个项目 yarn install/link: 默认的安装依赖操作 yarn add taco: 安装某个依赖,并且默认保存到package yarn remove taco: 移除某个依赖项目 yarn add taco —dev: 安装某个开发时依赖项目 yarn upgrade taco: 更新某个依赖项目 yarn global add taco: 安装某个全局依赖项目 yarn publish/login/logout: 发布/登录/登出,一系列NPM Registry操作 yarn run/test: 运行某个命令 yarn安装 这里我只说mac的安装方法,win安装较为简单,可以使用npm的方式安装。 1.安装Homebrew包管理工具,方便快捷,一条指令即可安装完成。 /usr/bin/ruby -e “$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 2.安装yarn。 brew update brew install yarn 3.验证安装是否完成,如果有显示版本号说明安装完成。 yarn --version 开始使用yarn 如果已有repo,则删除node_modules,使用“yarn”命令进行重新安装;如果新repo则直接使用“yarn“命令安装,可以对npm项目进行无缝衔接 rm -rf node_modules yarn","pubDate":"Wed, 28 Dec 2016 15:50:00 GMT","guid":"//qiangck.club/2016/12/28/放弃npm拥抱yarn/","category":"yarn"},{"title":"HTML5 WebSocket介绍","link":"//qiangck.club/2016/06/15/HTML5-WebSocket介绍/","description":"WebSocket是一种基于ws协议的技术,它可以建立全双工连接,常见于浏览器,但是不受平台限制,这里只介绍浏览器相关的内容。 浏览器支持 Chrome Firefox Internet Explorer Opera Safari >16 >11 >10 >12.1 >7 请求 ws开头是普通的websocket连接,wss是安全的websocket连接,类似于https。 客户端到服务端 GET /echo HTTP/1.1Upgrade: WebSocketConnection: UpgradeHost: 127.0.0.1:9999Origin: http://127.0.0.0.1 服务端到客户端 HTTP/1.1 101 Web Socket Protocol HandshakeUpgrade: WebSocketConnection: UpgradeWebSocket-Origin: http://127.0.0.0.1WebSocket-Location: ws://127.0.0.1:9999 WebSocket 服务器端 Kaazing WebSocket Gateway:一个 Java 实现的 WebSocket Server mod_pywebsocket:一个 Python 实现的 WebSocket Server Netty:一个 Java 实现的网络框架其中包括了对 WebSocket 的支持 node.js:一个 Server 端的 JavaScript 框架提供了对 WebSocket 的支持 WebSocket 应用 1.创建一个 WebSocket 对象 1var ws = new WebSocket(\"ws://127.0.0.1:9999\", protocols); protocols【可选】:可以是一个单个的协议名字字符串或者包含多个协议名字字符串的数组。 一旦连接建立(也就是说 readyState 变成 OPEN),ws 将会告诉你服务器选择了哪一种协议。 状态readyState 值 介绍 CONNECTING 0 连接还没开启。 OPEN 1 连接已开启并准备好进行通信 CLOSING 2 连接正在关闭的过程中 CLOSED 3 连接已经关闭,或者连接无法建立 2.发送数据到服务器 1ws.onopen = function (event) { ws.send(\"sucess\"); }; 建立连接的过程是异步的,而且可能会出错,因此刚刚连接就调用 send() 可能会失败。 我们可以设置 onopen 回调函数来确定什么时候连接成功。 3.使用 JSON 来传输接收数据 传输: 需要把要传的对象Object转成JSON,JSON.stringify(obj); 接收: 需要把接受的数据转成对象Object,JSON.parse(JSON); 4.事件描述 onmessage:一个用于消息事件的事件监听器,这一事件当有消息到达的时候该事件会触发。这个事件会被传入一个名为”message”的对象 onopen:一个用于连接打开事件的事件监听器。当readyState的值变为OPEN的时候会触发该事件。该事件表明这个连接已经准备好接受和发送数据。这个监听器会接受一个名为”open”的事件对象 onclose:用于监听连接关闭事件监听器。当WebSocket对象的readyState状态变为CLOSED时会出发该事件。这个监听器会接收一个叫close的事件对象 onerror:当错误发生时用于监听error事件的事件监听器。会接受一个名为error的event对象 注意事项 在 Gecko 9之前的版本上,UTF-8 文本里的某些非文本字符会导致连接断开,但是现在的 Gecko 已经解决了这个问题。 不要将 WebSockets 用于混合内容环境;不要在 HTTPS 安全页面下创建非安全的 WebSocket 连接,反之亦然。有些浏览器对此是强行禁止的,例如 Firefox 8 和后续版本。 从Gecko 6.0开始,构造器含有前缀,你需要调用 MozWebSocket(),直到 Gecko 8.0 才被支持 WebSocket。 在 Gecko 11.0之前,用send()方法发送的数据被限制在16MB以内。现在数据大小可以达到2 GB。","pubDate":"Wed, 15 Jun 2016 08:50:00 GMT","guid":"//qiangck.club/2016/06/15/HTML5-WebSocket介绍/","category":"HTML5"},{"title":"模块化开发框架Sea.js上手指南","link":"//qiangck.club/2016/05/12/模块化开发框架Sea-js上手指南/","description":"Sea.js的目的是追求简单的代码书写和组织方式,Sea.js并没有过多功能而是主要对前端程序的部署结构作出约束,下面我们就来看一下JavaScript的模块化开发框架Sea.js上手指南: Sea.js所有源码都存放在 GitHub 上:https://github.com/seajs/examples,目录结构为: 12345678910examples/ |-- sea-modules 存放 seajs、jquery 等文件,这也是模块的部署目录 |-- static 存放各个项目的 js、css 文件 | |-- hello | |-- lucky | `-- todo `-- app 存放 html 等文件 |-- hello.html |-- lucky.html `-- todo.html 引入seajs主文件12345678910111213141516171819202122232425<script src=\"js/sea.js\"></script><script type=\"text/javascript\"> // seajs配置项 seajs.config({ //设置基本的JS路径(引用外部文件的根目录) base:\"./js\", //设置别名(方便后面引用使用) alias:{ \"jQuery\":\"jquery.js\" }, //路径配置(跨目录调用或者目录比较深时使用) paths: { 'jQuery': 'http://libs.baidu.com/jquery/2.0.0/' }, //设置文件编码 charset:\"utf-8\", //预加载文件 preload:['jQuery'] }); // 引用主入口文件 seajs.use(['main','jQuery'],function(e,$){ //回调函数 alert(\"全部加载完成\"); });</script> seajs主入口文件(main)12345678910111213define(function(require, exports, module) { // 主入口文件引入其他文件依赖 //var testReQ = require('index'); var testReQ = require.async('index',function(){ //异步加载回调 alert(\"我是异步加载的index的回调函数\"); }); // 运行index释放的接口方法 // testReQ.testInit(); // 运行index释放的接口方法(module) testReQ.textFun();}); seajs依赖文件(index)12345678910111213141516define(function(require, exports, module) { // 对外释放接口 exports.testInit = function(){ alert(\"我是一个接口\"); }; // 如果需要释放大量接口,可以使用module var testObj = { name:\"qiangck\", sex:\"man\", textFun:function(){ alert(\"我是一个使用module.exports的对象方法\"); } } // module.exports接收obj对象 module.exports = testObj;}); 文件的加载顺序 下面我们从 hello.html 入手,来瞧瞧使用 Sea.js 如何组织代码。 在页面中加载模块在 hello.html 页尾,通过 script 引入 sea.js 后,有一段配置代码: 123456789// seajs 的简单配置seajs.config({ base: \"../sea-modules/\", alias: { \"jquery\": \"jquery/jquery/1.10.1/jquery.js\" }})// 加载入口模块seajs.use(\"../static/hello/src/main\") sea.js 在下载完成后,会自动加载入口模块。页面中的代码就这么简单。 模块代码这个小游戏有两个模块 spinning.js 和 main.js,遵循统一的写法: 12345678910// 所有模块都通过 define 来定义define(function(require, exports, module) { // 通过 require 引入依赖 var $ = require('jquery'); var Spinning = require('./spinning'); // 通过 exports 对外提供接口 exports.doSomething = ... // 或者通过 module.exports 提供整个接口 module.exports = ...}); 上面就是 Sea.js 推荐的 CMD 模块书写格式。如果你有使用过 Node.js,一切都很自然。","pubDate":"Thu, 12 May 2016 02:05:00 GMT","guid":"//qiangck.club/2016/05/12/模块化开发框架Sea-js上手指南/","category":""},{"title":"favicon.ico重复请求问题","link":"//qiangck.club/2016/04/03/favicon-ico重复请求问题/","description":"favicon.ico图标用于收藏夹图标和浏览器标签上的显示,如果不设置,浏览器会请求网站根目录的这个图标,如果网站根目录也没有这图标会产生 404的请求,并会重复请求,出于优化的考虑,要么就有这个图标,要么就禁止产生这个请求,在移动端下不希望产生favicon.ico的请求,可以在页面的’‘区域,加上如下代码实现屏蔽:1234<!-- 简洁版 --><link rel=\"icon\" href=\"data:;base64,=\"/><!-- 完整版 --><link rel=\"icon\" href=\"data:image/ico;base64,aWNv\"/>","pubDate":"Sat, 02 Apr 2016 17:44:00 GMT","guid":"//qiangck.club/2016/04/03/favicon-ico重复请求问题/","category":"HTML5"},{"title":"弹性盒模型的简单使用","link":"//qiangck.club/2015/11/12/弹性盒模型的简单使用/","description":"基本使用方法 Flexible Box(弹性盒子)能让页面的分布更合理和方便,这是之前使用常规的布局方式所做不到的。 123456<div class=\"warp\"> <div class=\"modular\">1</div> <div class=\"modular\">2</div> <div class=\"modular\">3</div> <div class=\"modular\">4</div></div> display:flex和display:box有什么区别? 前者是flex 2012年的语法,也将是以后标准的语法,大部分浏览器已经实现了无前缀版本。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354.warp{ width: 100%; height: 100px; /*这个是用来定义伸缩容器,是内联还是块取决于设置的值。*/ /*这个时候,他的所有子元素将变成flex文档流,称为伸缩项目。*/ /*---------------------*/ display: -webkit-box; /* Chrome 4+, Safari 3.1, iOS Safari 3.2+ */ display: -moz-box; /* Firefox 17- */ display: -webkit-flex; /* Chrome 21+, Safari 6.1+, iOS Safari 7+, Opera 15/16 */ display: -moz-flex; /* Firefox 18+ */ display: -ms-flexbox; /* IE 10 */ display: flex; /* Chrome 29+, Firefox 22+, IE 11+, Opera 12.1/17/18, Android 4.4+ */ /*---------------------*/ /*一起使用box-align 和 box-pack 属性,对子元素进行居中*/ -webkit-box-pack:justify; -webkit-box-align: ustify; -moz-box-pack:justify; -moz-box-align:justify; box-pack:justify; box-align:justify; /*---------------------*/ /*定义了伸缩项目放置在伸缩容器的方向*/ flex-direction:row; -webkit-flex-direction:row; /*定义伸缩容器里是单行还是多行显示*/ flex-wrap:nowrap; /*flex-direction”和“flex-wrap”属性的缩写版本,默认row nowrap*/ flex-flow:row nowrap; /*设置伸缩容器在横向方向上的对齐方式。*/ justify-content:center; /*设置伸缩容器在纵向方向上的对齐方式。*/ align-items:center; /*当伸缩容器的纵向还有多余空间时,可以用来调准伸缩容器横向在伸缩容器里的对齐方式*/ align-content:center;} /*子级*/.warp .modular{ width:80px; height:80px; background-color:#eee; text-align:center; /*设置伸缩项目出現的順序。*/ order:-1; /*设置伸缩项目扩展的比例。*/ flex-grow:1; /*设置伸缩项目收缩的比例。*/ flex-shrink:2; /*设置伸缩项目的伸缩基准值。*/ flex-basis:auto; /*flex-grow”、“flex-shrink”和“flex-basis”三个属性的缩写*/ flex:1 2 100px; /*用来在单独的伸缩项目上覆写默认的对齐方式。*/ align-self:center;} 直接上代码,写了个Demo便于理解,其中warp为父元素,称为“伸缩容器,modular为子元素,称为“伸缩项目”。 应用场景 Flexbox布局最适合应用程序的组件和小规模的布局,而网格布局更适合那些更大规模的布局。 目前没有浏览器支持 box-flex 属性,Firefox 支持替代的 -moz-box-flex 属性,Safari、Opera 以及 Chrome 支持替代的 -webkit-box-flex 属性。 CSS的columns在伸缩容器上没有效果。 float、clear和vertical-align在伸缩项目上没有效果。 例:移动端导航(居中,左右,自适应居中,自适应竖向排列) 12345678910111213141516171819202122232425262728293031323334353637<style type=\"text/css\"> .nav{ background:#00BFFF; display:-webkit-box; display:-moz-box; display:-ms-flexbox; display:-webkit-flex; display:flex; -webkit-flex-flow:row wrap; /*自适应竖向排列*/ -webkit-flex-flow:column wrap; /* 所有列面向主轴起始位置靠齐 */ justify-content:flex-start; /* 所有列面向主轴终点位置靠齐 */ justify-content:flex-end; } .nav a{ text-decoration:none; display:block; padding:1em; color:#fff; /*居中平铺*/ flex:1; } @media all and (max-width: 800px) { .nav{ /* 所有列面向主轴两端位置平均分配 */ justify-content:space-around; } }</style><ul class=\"nav\"> <li><a href=\"#\">1</a></li> <li><a href=\"#\">2</a></li> <li><a href=\"#\">3</a></li> <li><a href=\"#\">4</a></li></ul> 例:移动端常用自适应布局 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061<style type=\"text/css\"> .wrapper{ display:-webkit-box; display:-moz-box; display:-ms-flexbox; display:-webkit-flex; display:flex; -webkit-flex-flow:row wrap; flex-flow:row wrap; } .wrapper > *{ padding:10px; flex:1 100%; } .header{ background:#FF6347; } .footer{ background:#90EE90; } .main{ background:#00BFFF; } .aside-1{ background:#FFD700; } .aside-2{ background:#FF69B4; } @media all and (min-width: 600px) { .aside{ flex:1 auto; } } @media all and (min-width: 800px) { .main{ flex:3 0px; } .aside-1{ order:1; } .main{ order:2; } .aside-2{ order:3; } .footer{ order:4; } }</style><div class=\"wrapper\"> <header class=\"header\">顶部</header> <article class=\"main\"> <p>我在中间显示的文字</p> </article> <aside class=\"aside aside-1\">我在左边显示的文字</aside> <aside class=\"aside aside-2\">我在右边显示的文字</aside> <footer class=\"footer\">底部</footer></div>","pubDate":"Thu, 12 Nov 2015 03:36:00 GMT","guid":"//qiangck.club/2015/11/12/弹性盒模型的简单使用/","category":"css"},{"title":"Deferred对象与异步编程","link":"//qiangck.club/2015/11/03/Deferred对象与异步编程/","description":"以Deferred方式执行Ajax回调 123456789101112131415161718192021$.ajax('/test').done(function(data){ //请求成功后的操作 alert('done1');}).done(function(){ //请求成功后的操作 alert('done2');}).fail(function(){ //请求失败后的操作 alert('fail');}).always(function(){ //请求完成后的操作 alert('always');});$.ajax('/test').then(function(){ //如果成功执行 alert('success');},function(){ //如果失败执行 alert('error');}); 例:Deferred的实现方法 1234567891011121314151617181920212223242526//常规方式function testDeferred(){ setTimeout(function(){ var val = Math.random(); if(val > 0.5){ alert('done' + val); }else{ alert('fail' + val); } },100);}//使用Deferred方式function tt(){ var defer = $.Deferred(); setTimeout(function(){ var val = Math.random(); var > 0.5 ? defer.resolve(val) : defer.reject(val); },100); return defer;}tt().done(function(data){ alert('done' + data);}).fail(function(data){ alert('fail' + data);}); 例:多个请求同时发起(when) 1234567891011$.when( $.ajax('/test?err=n&a=1'), $.ajax('/test?err=n&a=2'), $.ajax('/test?err=n&a=3')).then(function(){ alert('success'); console.log(arguments);},function(promise,statusText,errObj){ alert('error'); console.log(arguments);}); 例:多个请求一次发起(then) 1234567891011$.ajax('/test?err=n').then(function(){ alert('success'); //如果成功执行,并返回 return $.ajax('/test');},function(){ alert('error1'); //如果失败执行,并返回 return $.ajax('/test?a=no');}).then(function(){ alert('success2');});","pubDate":"Tue, 03 Nov 2015 03:09:00 GMT","guid":"//qiangck.club/2015/11/03/Deferred对象与异步编程/","category":"jQuery"},{"title":"jQuery $.ajax详解使用方法","link":"//qiangck.club/2015/11/03/jQuery-ajax详解使用方法/","description":"$.ajax快捷方法 $.get(url,[data],[callback],[type])$.post(url,[data],[callback],[type]) 两种方法请求方式不同,其他方式相同 参数:url[请求地址],data[请求的数据内容(obj对象)],callback[回调函数(只能处理请求成功事件)],type[请求返回数据的编码格式(默认ContentType指定格式)] 12345678$.get('/test?x=1');$.get('/test',{z:2});$.post('/test',{y:2});$.get('/user',function(data,callbacktype,jqXHR){ data//返回数据 callbacktype//返回数据的状态信息(字符串) jqXHR//jQuery封装的XHR对象}); $(selector).load(url,[data],[callback]) 将页面片段载入到selector的容器里面 1$(\"#content\").load('/user'); $.getJSON(url,[data],[callback]) 如果是JSON数据回调会成功,否则失败 123$.getJSON('/test',{type:1},function(){ console.log(argument)}); $.getScript(url,[claaback]) 动态加载脚本文件 123$.gerScript('/js/test.js',function(){ alert(test(1,2));}); $.ajax详细使用方法 $.ajax(url,[settings]); 123456$.ajax({ url:'/test', success:function(){ alert('ok'); }}); 处理响应结果的回调函数: success[成功],error[请求失败], statusCode[指明返回的状态码的回调函数], complete[请求返回前的回调函数(处理返回不同状态码的请求)] 12345678910111213141516171819202122232425262728$.ajax('/test',{ success:function(data){ console.log(arguments); }, error:function(jqXHR,textStatus,err){ //jqXHR:jQuery增强的XHR //textStatus:请求完成状态 //err:底层通过throw抛出的异常对象,类型与值与错误类型有关 console.log(arguments); }, complete:function(jqXHR,textStatus){ //jqXHR:jQuery增强的XHR //textStatus:请求完成状态success | error console.log(arguments); }, statusCode:function(){ '403':function(jqXHR,textStatus,err){ //jqXHR:jQuery增强的XHR //textStatus:请求完成状态 //err:底层通过throw抛出的异常对象,类型与值与错误类型有关 console.log(arguments); console.log(400); }, '400':function(){ console.log(400); } } }); 请求的数据:data,processData,contentType,traditional 123456789101112131415$.ajax('/test',{ //请求的数据内容 data:{ a:1, b:2 }, //请求的方式 type:'POST', //是否对请求的数据进行转码(用于传输数据为html节点内容) processData:true, //当前的数据是否使用传统方式进行url编码 traditional:true, //请求数据编码格式 contentType:'application/json'}); 响应数据:dataType,dataFilter 123456789101112$.ajax('/test',{ success:function(data){ console.log(typeof data) }, //定义的返回数据的类型 dataType:'json | html | text | jsonp | script', //返回底层的原始数据进行预处理 dataFilter:function(data,type){ //data:原始数据 //type:指定的数据类型 } }); 前置处理:beforeSend 12345678910$.ajax('/test',{ beforeSend:function(jqXHR,settings){ console.log(arguments); jqXHR.setRequestHeader('test','haha'); jqXHR.testData = {a:1,b:2}; }, complete:function(jqXHR){ console.log(jqXHR.testData) }}); 请求类型:GET(默认) | POST | PUT | DELETE 同步异步:async(默认true) 是否缓存:cache(默认true) 其他参数: global:是否触发全局事件 ifModifed:仅在服务器数据改变时候加载数据 username,password:http需要验证时候 timeout:设置请求超时时间,若请求超时触发error context:回调中this指向的对象 其他相关的API $.ajaxSetup(option) 设置全局默认参数 1234567891011121314//默认为get请求$.ajax('/test');//修改全局请求方式为post$.ajaxSetup({ type:'post', headers:{ test:new Date().getTime }, cache:false});//请求方式改变为post$.ajax('/test'); $.ajaxPrefilter([dataTypes],handler(option,originalOptions,jqXHR)) 请求发起前的预处理,提供了一种AOP(面向切面)编程模式,常见用途: 根据option设定执行特定处理逻辑 修改option值改变请求默认行为 通过return修改默认dataType 例:通过return修改默认dataType 12345678910111213141516$.ajaxPrefilter('text html json',function(options,originalOptions,jqXHR){ //options请求参数,含默认值 //originalOptions请求时传入的参数,不含默认值 //jqXHR:jQuery增强的XHR console.log(arguments); if(options.url == '/test'){ return 'text'; }});$.ajax('/test',{ type:'post', dataType:'text', //自定义属性 test:'haha'}); 例:多次请求仅最后一次有效,避免多次请求导致的数据混乱 12345678910var requests = {};$.ajaxPrefilter(function(options,originalOptions,jqXHR){ if(requests[options.url]){ requests[options.url].abort(); } requests[options.url] = jqXHR;});$.ajax('/test/'); // 第一次$.ajax('/test/'); // 第二次 例:统一修改请求路径 12345678910$.ajaxPrefilter(function(options){ if(options.url.substr(0,5) == '/usr'){ options.url = options.url.replace('/usr/','/user/'); options.header = { a:1 } }});$.ajax('/usr/'); 全局事件 jQuery-1.9以后,全局事件必须绑定在document上 $(document).ajaxSuccess(globalEventHander); $(document).ajaxError(globalEventHander); $(document).ajaxComplete(globalEventHander); $(document).ajaxStart(globalEventHander); $(document).ajaxStop(globalEventHander); $(document).ajaxSend(globalEventHander); 123456function globalEventHander(event){ console.log(arguments); console.log(event.type);}$.ajax('/test?err=y');//请求成功$.ajax('/test?err=n');//请求失败 请求顺序:1.ajaxStart2.ajaxSend3.ajaxSuccess4.ajaxError5.ajaxComplete6.ajaxStop 序列化 param:序列化一个 key/value 对象 serialize:通过序列化表单值,创建 URL 编码文本字符串 serializeArray:通过序列化表单值来创建对象数组(名称和值) 例:param() 123var params = { a:1, b:2 };var str = $.param(params);console.log(str); //a=1&b=2\" 例:serialize() 1234567891011<form> <div><input type=\"text\" name=\"a\" value=\"1\"/></div> <div><input type=\"text\" name=\"b\" value=\"2\"/></div> <div><input type=\"hidden\" name=\"c\" value=\"3\"/></div></form><script type=\"text/javascript\"> $('form').submit(function() { console.log($(this).serialize()); //a=1&b=2&c=3 return false; });</script> 例:serializeArray() 12345678910<form> First:<input type=\"text\" name=\"First\" value=\"1\" /><br /> Last :<input type=\"text\" name=\"Last\" value=\"2\" /><br /></form><script type=\"text/javascript\"> $('form').click(function(){ x=$(\"form\").serializeArray(); console.log(x); //{[name:First,value:1],[name:Last,value:2]} });</script>","pubDate":"Mon, 02 Nov 2015 20:29:00 GMT","guid":"//qiangck.club/2015/11/03/jQuery-ajax详解使用方法/","category":"jQuery"},{"title":"HTML5 Audio/Video 使用方法汇总","link":"//qiangck.club/2015/10/31/HTML5-Audio-Video-使用方法汇总/","description":"<audio>标签属性: src:音乐的URL preload:预加载 autoplay:自动播放 loop:循环播放 controls:浏览器自带的控制条 1234<audio id=\"media\" src=\"http://www.abc.com/test.mp3\" controls> <source src=\"http://www.abc.com/test.mp3\" type=\"audio/mp3\"></source> <source src=\"http://www.abc.com/test.ogg\" type=\"audio/ogg\"></source></audio> <video>标签属性: src:视频的URL poster:视频封面,没有播放时显示的图片 preload:预加载 autoplay:自动播放 loop:循环播放 controls:浏览器自带的控制条 width:视频宽度 height:视频高度 1<video id=\"media\" src=\"http://www.abc.com/test.mp4\" controls width=\"400px\" heigt=\"400px\"></video> 获取HTMLVideoElement和HTMLAudioElement对象 1234//audio可以直接通过new创建对象Media = new Audio(\"http://www.abc.com/test.mp3\");//audio和video都可以通过标签获取对象Media = document.getElementById(\"media\"); Media方法和属性: 1234567891011121314151617181920212223242526272829303132333435363738394041//错误状态Media.error; //null:正常Media.error.code; //1.用户终止 2.网络错误 3.解码错误 4.URL无效//网络状态Media.currentSrc; //返回当前资源的URLMedia.src = value; //返回或设置当前资源的URLMedia.canPlayType(type); //是否能播放某种格式的资源Media.networkState; //0.此元素未初始化 1.正常但没有使用网络 2.正在下载数据 3.没有找到资源Media.load(); //重新加载src指定的资源Media.buffered; //返回已缓冲区域,TimeRangesMedia.preload; //none:不预载 metadata:预载资源信息 auto://准备状态Media.readyState; //1:HAVE_NOTHING 2:HAVE_METADATA 3.HAVE_CURRENT_DATA 4.HAVE_FUTURE_DATA 5.HAVE_ENOUGH_DATAMedia.seeking; //是否正在seeking//回放状态Media.currentTime = value; //当前播放的位置,赋值可改变位置Media.startTime; //一般为0,如果为流媒体或者不从0开始的资源,则不为0Media.duration; //当前资源长度 流返回无限Media.paused; //是否暂停Media.defaultPlaybackRate = value; //默认的回放速度,可以设置Media.playbackRate = value; //当前播放速度,设置后马上改变Media.played; //返回已经播放的区域,TimeRanges,关于此对象见下文Media.seekable; //返回可以seek的区域 TimeRangesMedia.ended; //是否结束Media.autoPlay; //是否自动播放Media.loop; //是否循环播放Media.play(); //播放Media.pause(); //暂停//控制Media.controls; //是否有默认控制条Media.volume = value; //音量Media.muted = value; //静音//TimeRanges(区域)对象TimeRanges.length; //区域段数TimeRanges.start(index) //第index段区域的开始位置TimeRanges.end(index) //第index段区域的结束位置 事件介绍 1234567891011121314151617181920212223242526eventTester = function(e){ Media.addEventListener(e,function(){ console.log((new Date()).getTime(),e); });}eventTester(\"loadstart\"); //客户端开始请求数据eventTester(\"progress\"); //客户端正在请求数据eventTester(\"suspend\"); //延迟下载eventTester(\"abort\"); //客户端主动终止下载(不是因为错误引起),eventTester(\"error\"); //请求数据时遇到错误eventTester(\"stalled\"); //网速失速eventTester(\"play\"); //play()和autoplay开始播放时触发eventTester(\"pause\"); //pause()触发eventTester(\"loadedmetadata\"); //成功获取资源长度eventTester(\"loadeddata\"); //提示当前帧的数据是可用的eventTester(\"waiting\"); //等待数据,并非错误eventTester(\"playing\"); //开始回放eventTester(\"canplay\"); //可以播放,但中途可能因为加载而暂停eventTester(\"canplaythrough\"); //可以播放,歌曲全部加载完毕eventTester(\"seeking\"); //寻找中eventTester(\"seeked\"); //寻找完毕eventTester(\"timeupdate\"); //播放时间改变eventTester(\"ended\"); //播放结束eventTester(\"ratechange\"); //播放速率改变eventTester(\"durationchange\"); //资源长度改变eventTester(\"volumechange\"); //音量改变","pubDate":"Sat, 31 Oct 2015 07:59:00 GMT","guid":"//qiangck.club/2015/10/31/HTML5-Audio-Video-使用方法汇总/","category":"HTML5"},{"title":"博客的诞生","link":"//qiangck.club/2015/09/24/博客的诞生/","description":"一时兴起建立了此博客,本人是个前端小白,技术不佳,就想留些东西给未来的自己(大牛勿喷哈)。本博客使用了Hexo框架,发布在Github page上面,建立博客属于个人爱好,请勿商业使用,也请转载的朋友请标注好出处和相关内容。 域名的注册 我的域名是在阿里巴巴收购万网后搞活动购买的,当时只要了我20大洋,这个域名我一直搁置了将近一年的时间才开始想弄个博客的。域名的话随便买个就好了,也不是很贵的。 注册GitHub账号 GitHub的注册非常简单: Pick a username:用户名 Your email:邮箱 Create a password:密码 点击Sign up for GitHub注册 使用msysgit管理Github msysgit是 Git 版本控制系统在 Windows 下的版本。 安装与设置 下载msysgit安装非常简单。点击这里百度百科,与Github绑定,使用msysgit找到系统[用户名]文件夹 > .ssh文件夹。 例:C:\\Users\\Administrator.ssh 1234567[~/.ssh]$ ssh-keygen -t rsaGenerating public/private rsa key pair. Enter file in which to save the key (/home/tom/.ssh/id_rsa): [Enter]Enter passphrase (empty for no passphrase): <输入key的密码,或直接按下enter使用空密码>Enter same passphrase again: <再输入一次密码>Your identification has been saved in /home/tom/.ssh/id_rsa.Your public key has been saved in /home/tom/.ssh/id_rsa.pub.The key fingerprint is:50:43:77:c6:97:af:61:82:dc:ea:9b:6b:67:d4:1b:61 tom@volcano 可以直接回车(默认的密码为空),用编辑器找到ssh文件夹下面的id_rsa.pub,其中id_rsa.pub是公钥,而id_rsa则是私钥,将公钥粘贴到你github帐号中的SSH Keys中。 1Hi han1202012! You've successfully authenticated, but GitHub does not provide shell access。 就说明配置成功, 可以连接上GitHub。","pubDate":"Thu, 24 Sep 2015 04:05:00 GMT","guid":"//qiangck.club/2015/09/24/博客的诞生/","category":"default"}]}