一、vue资源
1.vue知识库汇总
vuejs2.chm 下载
vue官网 vue官网
vuejs api vuejs api
2.vuejs组件
vuejs组件介绍 去官网瞧瞧
3.Vue.js 组件编码规范
目标
本规范提供了一种统一的编码规范来编写 Vue.js 代码。这使得代码具有如下的特性:
* 其它开发者或是团队成员更容易阅读和理解。 * IDEs 更容易理解代码,从而提供高亮、格式化等辅助功能 * 更容易使用现有的工具 * 更容易实现缓存以及代码包的分拆
本指南为 De Voorhoede 参考 RiotJS 编码规范 而写。
#目录
* 将 this 赋值给 component 变量 * 使用组件名作为样式作用域空间 * 对组件文件进行代码校验
#基于模块开发
始终基于模块的方式来构建你的 app,每一个子模块只做一件事情。 Vue.js 的设计初衷就是帮助开发者更好的开发界面模块。一个模块是应用程序中独立的一个部分。
#怎么做?
每一个 Vue 组件(等同于模块)首先必须专注于解决一个 单一的问题 , 独立的 , 可复用的 , 微小的 and 可测试的 。 如果你的组件做了太多的事或是变得臃肿,请将其拆分成更小的组件并保持单一的原则。一般来说,尽量保证每一个文件的代码行数不要超过 100 行。也请保证组件可独立的运行。比较好的做法是增加一个单独的 demo 示例。
#Vue组件命名
组件的命名需遵从以下原则:
* 有意义的 : 不过于具体,也不过于抽象 * 简短 : 2 到 3 个单词 * 具有可读性 : 以便于沟通交流
同时还需要注意:
* 必须符合 自定义元素规范 : 使用连字符 分隔单词,切勿使用保留字。 * app- 前缀作为命名空间 : 如果非常通用的话可使用一个单词来命名,这样可以方便于其它项目里复用。
#为什么?
* 组件是通过组件名来调用的。所以组件名必须简短、富有含义并且具有可读性。
#如何做?
<!-- 推荐 --> <app-header></app-header> <user-list></user-list> <range-slider></range-slider> <!-- 避免 --> <btn-group></btn-group> <!-- 虽然简短但是可读性差. 使用 `button-group` 替代 --> <ui-slider></ui-slider> <!-- ui 前缀太过于宽泛,在这里意义不明确 --> <slider></slider> <!-- 与自定义元素规范不兼容 -->
#组件表达式简单化
Vue.js 的表达式是 100% 的 Javascript 表达式。这使得其功能性很强大,但也带来潜在的复杂性。因此,你应该尽量 保持表达式的简单化 。
#为什么?
* 复杂的行内表达式难以阅读。 * 行内表达式是不能够通用的,这可能会导致重复编码的问题。 * IDE 基本上不能识别行内表达式语法,所以使用行内表达式 IDE 不能提供自动补全和语法校验功能。
#怎么做?
如果你发现写了太多复杂并难以阅读的行内表达式,那么可以使用 method 或是 computed 属性来替代其功能。
<!-- 推荐 --> <template> <h1> {{ `${year}-${month}` }} </h1> </template> <script type="text/javascript"> export default { computed: { month() { return this.twoDigits((new Date()).getUTCMonth() + 1); }, year() { return (new Date()).getUTCFullYear(); } }, methods: { twoDigits(num) { return ('0' + num).slice(-2); } }, }; </script> <!-- 避免 --> <template> <h1> {{ `${(new Date()).getUTCFullYear()}-${('0' + ((new Date()).getUTCMonth()+1)).slice(-2)}` }} </h1> </template>
#组件props原子化
虽然 Vue.js 支持传递复杂的 JavaScript 对象通过 props 属性,但是你应该尽可能的使用原始类型的数据。尽量只使用 JavaScript 原始类型 (字符串、数字、布尔值) 和 函数。尽量避免复杂的对象。
#为什么?
* 使得组件 API 清晰直观 * 只使用原始类型和函数作为 props 使得组件的 API 更接近于 HTML(5) 原生元素。 * 其它开发者更好的理解每一个 prop 的含义,作用 * 传递过于复杂的对象使得我们不能够清楚的知道哪些属性或方法被自定义组件使用,这使得代码难以重构和维护。
#怎么做?
组件的每一个属性单独使用一个 props,并且使用函数或是原始类型的值。
<!-- 推荐 --> <range-slider :values="[10, 20]" min="0" max="100" step="5" :on-slide="updateInputs" :on-end="updateResults"> </range-slider> <!-- 避免 --> <range-slider :config="complexConfigObject"></range-slider>
#验证组件的props
在 Vue.js 中,组件的 props 即 API,一个稳定并可预测的 API 会使得你的组件更容易被其他开发者使用。 组件 props 通过自定义标签的属性来传递。属性的值可以是 Vue.js 字符串( :attr="value" 或v-bind:attr="value" )或是不传。你需要保证组件的 props 能应对不同的情况。
#为什么?
验证组件 props 可以保证你的组件永远是可用的(防御性编程)。即使其他开发者并未按照你预想的方法使用时也不会出错。
#怎么做?
* 提供默认值 * 使用 type 属性 校验类型 * 使用 props 之前先检查该 prop 是否存在
<template> <input type="range" v-model="value" :max="max" :min="min"> </template> <script type="text/javascript"> export default { props: { max: { type: Number, // 这里添加了数字类型的校验 default() { return 10; }, }, min: { type: Number, default() { return 0; }, }, value: { type: Number, default() { return 4; }, }, }, }; </script>
#将this赋值给component变量
在 Vue.js 组件上下文中, this 指向了组件实例。因此当你切换到了不同的上下文时,要确保this 指向一个可用的 component 变量。 换句话说,不要在编写这样的代码 const self = this; ,而是应该直接使用变量 component。
#为什么?
* 将组件 this 赋值给变量 component 可用让开发者清楚的知道任何一个被使用的地方,它代表的是组件实例。
#怎么做?
<script type="text/javascript">export default { methods: { hello() { return 'hello'; }, printHello() { console.log(this.hello()); }, }, }; </script> <!-- 避免 --> <script type="text/javascript">export default { methods: { hello() { return 'hello'; }, printHello() { const self = this; // 没有必要 console.log(self.hello()); }, }, }; </script>
#组件结构化
按照一定的结构组织,使得组件便于理解。
#为什么?
* 导出一个清晰、组织有序的组件,使得代码易于阅读和理解。同时也便于标准化。 * 按首字母排序properties, data, computed, watches 和 methods使得这些对象内的属性便于查找。 * 合理组织,使得组件易于阅读。(name; extends; props, data and computed; components; watch and methods; lifecycle methods, 等.); * 使用 name 属性。借助于 vue devtools 可以让你更方便的测试 * 合理的 CSS 结构,如 BEM 或 rscss -; * 使用单文件 .vue 文件格式来组件代码
#怎么做?
组件结构化
<template lang="html"> <div class="Ranger__Wrapper"> <!-- ... --> </div> </template> <script type="text/javascript"> export default { // 不要忘记了 name 属性 name: 'RangeSlider', // 组合其它组件 extends: {}, // 组件属性、变量 props: { bar: {}, // 按字母顺序 foo: {}, fooBar: {}, }, // 变量 data() {}, computed: {}, // 使用其它组件 components: {}, // 方法 watch: {}, methods: {}, // 生命周期函数 beforeCreate() {}, mounted() {}, }; </script> <style scoped> .Ranger__Wrapper { /* ... */ }</style>
#组件事件命名
Vue.js 提供的处理函数和表达式都是绑定在 ViewModel 上的,组件的每一个事件都应该按照一个好的命名规范来,这样可以避免不少的开发问题,具体可见如下 ** 为什么**。
#为什么?
* 开发者可以随意给事件命名,即使是原生事件的名字,这样会带来迷惑性。 * 过于宽松的事件命名可能与 DOM模板不兼容 。
#怎么做?
* 事件命名也连字符命名 * 一个事件的名字对应组件外的一组意义操作,如:upload-success, upload-error 以及 dropzone-upload-success, dropzone-upload-error (如果需要前缀的话)。 * 事件命名应该以动词(如 client-api-load) 或是 形容词(如 drive-upload-success)结尾。( 出处 )
#避免this.$parent
Vue.js 支持组件嵌套,并且子组件可访问父组件的上下文。访问组件之外的上下文违反了 基于模块开发 的 第一原则 。因此你应该尽量避免使用 this.$parent 。
#为什么?
* 组件必须相互保持独立,Vue 组件也是。如果组件需要访问其父层的上下文就违反了该原则。 * 如果一个组件需要访问其父组件的上下文,那么该组件将不能再其它上下文中复用。
#怎么做?
* 通过 props 将值传递给子组件 * 通过 props 传递回调函数给子组件来达到调用父组件方法的目的 * 通过在子组件触发事件来通知父组件
#谨慎使用this.$refs
Vue.js 支持通过 ref 属性来访问其它组件和 HTML 元素。并通过 this.$refs 可以得到组件或 HTML 元素的上下文。在大多数情况下,通过 this.$refs 来访问其它组件的上下文是可以避免的。在使用的的时候你需要注意避免调用了不恰当的组件 API,所以应该尽量避免使用 this.$refs 。
#为什么?
* 组件必须是保持独立的,如果一个组件的 API 不能够提供所需的功能,那么这个组件在设计、实现上是有问题的。 * 组件的属性和事件必须足够的给大多数的组件使用
#怎么做?
* 提供良好的组件 API * 总是关注于组件本身的目的 * 拒绝定制代码。如果你在一个通用的组件内部编写特定需求的代码,那么代表这个组件的 API 不够通用,或者你可能需要一个新的组件来应对该需求 * 检查所有的 props 是否有缺失的,如果有提一个 issue 或是完善这个组件 * 检查所有的事件。子组件向父组件通信一般是通过事件来实现的,但是大多数的开发者更多的关注于 props 从忽视了这点。 * Props向下传递,事件向上传递! 。以此为目标升级你的组件,提供良好的 API 和 独立性。 * 当遇到 props 和 events 难以实现的功能时,通过 this.$refs 来实现。 * 当需要操作 DOM 无法通过指令来做的时候可使用 this..$ref 而不是 JQuery , document.getElement* , document.queryElement 。
<!-- 推荐,并未使用 this.$refs --> <range :max="max" :min="min" @current-value="currentValue" :step="1"></range> <!-- 使用 this.$refs 的适用情况--> <modal ref="basicModal"> <h4>Basic Modal</h4> <button class="primary" @click="$refs.basicModal.close()">Close</button> </modal> <button @click="$refs.basicModal.open()">Open modal</button> <!-- Modal component --> <template> <div v-show="active"> <!-- ... --> </div> </template> <script> export default { // ... data() { return { active: false, }; }, methods: { open() { this.active = true; }, hide() { this.active = false; }, }, // ... }; </script> <!-- 如果可通过 emited 来做则避免通过 this.$refs 直接访问 --> <template> <range :max="max" :min="min" ref="range" :step="1"></range> </template> <script> export default { // ... methods: { getRangeCurrentValue() { return this.$refs.range.currentValue; }, }, // ... }; </script>
#使用组件名作为样式作用域空间
Vue.js 的组件是自定义元素,这非常适合用来作为样式的根作用域空间。可以将组件名作为 css 类的命名空间。
#为什么?
* 给样式加上作用域空间可以避免组件样式影响外部的样式 * 保持模块名、目录名、样式根作用域名一样,可以很好的将其关联起来,便于开发者理解。
#怎么做?
使用组件名作为样式命名的前缀,可基于 BEM 或 OOCSS 范式。同时给style标签加上 scoped 属性。加上 scoped 属性编译后会给组件的 class 自动加上唯一的前缀从而避免样式的冲突。
<style scoped> /* 推荐 */ .MyExample { } .MyExample li { } .MyExample__item { } /* 避免 */ .My-Example { } /* not scoped to component or module name, not BEM compliant */ </style>
#提供组件API文档
使用 Vue.js 组件的过程中会创建 Vue 组件实例,这个实例是通过自定义属性配置的。为了便于其他开发者使用该组件,对于这些自定义属性即组件API应该在 README.md 文件中进行说明。
#为什么?
* 良好的文档可以让开发者比较容易的对组件有一个整体的认识,而不用去阅读组件的源码,也更方便开发者使用 * 组件配置属性即组件的 API,对于组件的用户来说他们更感兴趣的是 API 而不是实现原理。 * 正式的文档会告诉开发者组件 API 变更以及向后的兼容性情况 * README.md 是标准的我们应该首先阅读的文档文件。代码托管网站 (github/bitbucket/gitlab 等) 会默认在仓库中展示 该文件作为仓库的介绍。
#怎么做?
在模块目录中添加 README.md 文件:
range-slider/ ├── range-slider.vue ├── range-slider.less └── README.md 在 README 文件中说明模块的功能以及使用场景。对于 vue 组件来说,比较有用的描述是组件的自定义属性即 API 的描述介绍。
#Range slider功能
range slider 组件可通过拖动的方式来设置一个给定范围内的数值。 该模块使用 noUiSlider 来实现夸浏览器和 touch 功能的支持。
#如何使用
range-slider支持如下的自定义属性: attribute type description min Number 可拖动的最小值. max Number 可拖动的最大值. values Number[] optional 包含最大值和最小值的数组. 如. values="[10, 20]" . Defaults to [opts.min, opts.max] . step Number optional 增加减小的数值单位,默认为 1. on-slide Function optional 用户拖动开始按钮或者结束按钮时的回调函数,函数接受 (values, HANDLE) 格式的参数。 如: on-slide={ updateInputs } , component.updateInputs = (values, HANDLE) => { const value = values[HANDLE]; } . on-end Function optional 当用户停止拖动时触发的回调函数,函数接受 (values, HANDLE) 格式的参数。 如需要自定义 slider 的样式可参考 noUiSlider 文档
#提供组件demo
添加 index.html 文件作为组件的 demo 示例,并提供不同配置情况的效果,说明组件是如何使用的。
#为什么?
* demo 可以说明组件是独立可使用的 * demo 可以让开发者预览组件的功能效果 * demo 可以展示组件各种配置参数下的功能
#对组件文件进行代码校验
代码校验可以保持代码的统一性以及追踪语法错误。.vue 文件可以通过使用 eslint-plugin-html 插件来校验代码。你可以通过 vue-cli 来开始你的项目, vue-cli 默认会开启代码校验功能。
#为什么?
* 保证所有的开发者使用同样的编码规范。 * 更早的感知到语法错误
#怎么做?
为了校验工具能够校验 *.vue 文件,你需要将代码编写在script标签中,并使,因为校验工具无法理解行内表达式,配置校验工具可以访问全局变量 vue 和组件的 props 。 ESLint ESLint 需要通过 ESLint HTML 插件 来抽取组件中的代码。 通过 .eslintrc 文件来配置 ESlint,这样 IED 可以更好的理解校验配置项。 ESlint,这样.
{ "extends": "eslint:recommended", "plugins": ["html"], "env": { "browser": true }, "globals": { "opts": true, "vue": true } }
运行 ESLint
eslint src/**/*.vue
JSHint
JSHint 可以解析 HTML (使用 --extra-ext 命令参数) 和 抽取代码(使用 --extract=auto 命令参数).
通过 .jshintrc 文件来配置 ESlint,这样 IED 可以更好的理解校验配置项。
{
"browser": true,
"predef": ["opts", "vue"]
}
运行 JSHint
jshint --config modules/.jshintrc --extra-ext=html --extract=auto modules/
注:JSHint 不接受 vue 扩展名的文件,只支持 html 。
4.vue-router初阶
vue-router 权威指导,戳我
5.vuex资讯
vuex官网 vuex官网
6.elementUI资源
elementUI官网 去官网瞧瞧
elementUI组件 发现新组件
二、markdown学习
7.Markdown 快速开始
简介
Markdown是为那些需要经常码字或者进行文字排版的、对码字手速和排版顺畅度有要求的人群设计的,他们希望用键盘把文字内容啪啪啪地打出来后就已经排版好了,最好从头到尾都不要使用鼠标。
这些人包括经常需要写文档的码农、博客写手、网站小编、出版业人士等等。
Markdown的语法简洁明了、学习容易,得到了许多著名网络平台的支持,例如代码托管平台Github、博客平台WordPress等等。
#语法快速入门
#【标题】
在行首插入1到6个#,对应1到6阶标题 # 这是 H1 ## 这是 H2 ### 这是 H3 #### 这是 H4 ##### 这是 H5 ###### 这是 H6 渲染效果:
#这是 H1
#这是 H2
#这是 H3
#这是 H4
#这是 H5
#这是 H6
#【修辞和强调】
使用星号和底线来标记需要强调的区段
**加粗**
__加粗__
*斜体*
_斜体_
渲染效果:
加粗
加粗
斜体
斜体
#【删除线】
~~要删掉的内容~~
渲染效果:要删掉的内容
#【列表】
无序列表 使用星号、加号和减号来做为列表的项目标记 * Candy. * Gum. + Booze. * Booze. 长文本长文本长文本长文本长文本长文本长文本长文本长文本长文本长文本长文本长文本长文本长文本长文本长文本长文本长文本长文本长文本
这里是断行-文本长文本长文本长文本
这里是断行-文本长文本长文本长文本 - Booze. + 嵌套 * 嵌套
渲染效果:
- Candy.
- Gum.
- Booze.
- Booze. 长文本长文本长文本长文本长文本长文本长文本长文本长文本长文本长文本长文本长文本长文本长文本长文本长文本长文本长文本长文本长文本
这里是断行-文本长文本长文本长文本
这里是断行-文本长文本长文本长文本 - Booze.
- 嵌套
- 嵌套
有序列表 则是使用一般的数字接着一个英文句点作为项目标记 1. Red 50. Green 1000. Blue
渲染效果:
- Red
- Green
- Blue
#【链接】
在方括号后面用圆括号接上链接
这是一个[链接显示文本](http://www.baidu.com "链接title文本")
渲染效果:
这是一个链接显示文本
#【图片】
图片的语法和链接很像
![alt文本](amWiki/images/logo.png "Title")
渲染效果:
![alt文本](amWiki/images/logo.png "Title")
#【代码】
使用反引号 ` 来标记代码区段
我是`code`,`<div>division</div>`
渲染效果:
我是code
,<div>division</div>
#【代码段】
如果要建立一个已经格式化好的代码区块,只要每行都缩进 4 个空格或是一个 tab 就可以了
var name = 'Candy'
渲染效果:
var name = 'Candy'
#【表格】
使用竖线分割内容,且同时使用“---”与“:”指定对齐方式
| Header01 | Header02 | Header03
| -------- | :------: | ---:
| 默认 | 居中 | 右
渲染效果:
Header01 | Header02 | Header03 |
默认 | 居中 | 右 |
#【引用】
只需要在文本前加入 > 这种尖括号(大于号)即可
>这里是一段引用
渲染效果:
这里是一段引用
#【分割线】
只需要三个 - 号
---
渲染效果:
#【换行】
只需要两个以上的空格然后回车
我是首行
我换行了
渲染效果:
我是首行
我换行了
#【html】
可以直接在文档里书写 HTML,不需要额外标注这是 HTML
<div>division</div>
渲染效果:
division
#研究更多 markdown 语法详细细节
8.wiki 与语法高亮
wiki使用 highlight.js 进行预语法高亮渲染,它能对多达一百多种语言、样式提供语法高亮解析
使用两组每组三个反引号分单独两行将代码包围起来,并在第一组反引号后写上语言类型即可使用语法高亮,例如: js //some js code here
#javascript / js 代码
普通代码块效果:
//发送验证码 function cd(num) { $('#code').val(num + '秒后可重发'); setTimeout(function() { if (num - 1 >= 0) { cd(num - 1); } else { $('#code').removeClass('bg-gray').prop('disabled', false).val('重新发送验证码'); } }, 1000); }
{ "state": { "code": 10200, //code状态码 "msg": "ok" //状态描述 }, "data": { "team_num": 13, //队伍数 "position": "海珠区新港中路" //位置 } }
添加 js
、javascript
标记后的效果:
//发送验证码 function cd(num) { $('#code').val(num + '秒后可重发'); setTimeout(function() { if (num - 1 >= 0) { cd(num - 1); } else { $('#code').removeClass('bg-gray').prop('disabled', false).val('重新发送验证码'); } }, 1000); }
{ "state": { "code": 10200, //code状态码 "msg": "ok" //状态描述 }, "data": { "team_num": 13, //队伍数 "position": "海珠区新港中路" //位置 } }
wiki对javascript代码片段做了再次增强,可以点击代码块右上角按钮隐藏/显示注释
当注释处于隐藏状态时不会被复制,比较适合模拟返回json数据的接口时直接拷贝(json不允许注释)
#Html 代码
普通代码段效果:
<body> <div class="loading"><img src="/assets/images/loading.gif"></div> <header>some text</header> <script type="text/javascript" src="/assets/js/jquery-2.1.4.min.js"></script> </body>
添加 html
标记后的效果:
<body> <div class="loading"><img src="/assets/images/loading.gif"></div> <header>some text</header> <script type="text/javascript" src="/assets/js/jquery-2.1.4.min.js"></script> </body>
#css 代码
普通代码段效果:
/* 紧凑 */ html,body{display:block;width:100%;height:100%;min-width:320px;} a,img{-webkit-touch-callout:none;} /* 展开 */ input[type="button"], input[type="submit"], input[type="reset"], textarea { -webkit-appearance: none; }
添加 css
标记后的效果:
/* 紧凑 */ html,body{display:block;width:100%;height:100%;min-width:320px;} a,img{-webkit-touch-callout:none;} /* 展开 */ input[type="button"], input[type="submit"], input[type="reset"], textarea { -webkit-appearance: none; }
#php 代码
普通代码段效果:
private function addQuestData($data, $filing_id) { $quest_num = $data['status'] == 10 ? 1 : 2; $where = [ ['user_filing_id', '=', $filing_id], ['project_id', '=', $data['project_id']], ['mobile','=', $data['mobile']], ['quest_num', '=', $quest_num] ]; }
添加 php
标记后的效果:
private function addQuestData($data, $filing_id) { $quest_num = $data['status'] == 10 ? 1 : 2; $where = [ ['user_filing_id', '=', $filing_id], ['project_id', '=', $data['project_id']], ['mobile','=', $data['mobile']], ['quest_num', '=', $quest_num] ]; }
#sql 代码
普通代码段效果:
SELECT Company, OrderNumber FROM Orders ORDER BY Company, OrderNumber
添加 sql
标记后的效果:
SELECT Company, OrderNumber FROM Orders ORDER BY Company, OrderNumber
#java 代码
普通代码段效果:
public class Test { public static void main(String args[]) { int x = 10; while( x < 20 ) { System.out.print("value of x : " + x ); x++; System.out.print("\n"); } } }
添加 java
标记后的效果:
public class Test { public static void main(String args[]) { int x = 10; while( x < 20 ) { System.out.print("value of x : " + x ); x++; System.out.print("\n"); } } }
9.微信JS-SDK说明文档(超长文档页内目录示例)
概述
微信JS-SDK是微信公众平台面向网页开发者提供的基于微信内的网页开发工具包。
通过使用微信JS-SDK,网页开发者可借助微信高效地使用拍照、选图、语音、位置等手机系统的能力,同时可以直接使用微信分享、扫一扫、卡券、支付等微信特有的能力,为微信用户提供更优质的网页体验。
此文档面向网页开发者介绍微信JS-SDK如何使用及相关注意事项。
#JSSDK使用步骤
#步骤一:绑定域名
先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。如果你使用了支付类接口,请确保支付目录在该安全域名下,否则将无法完成支付。
备注:登录后可在“开发者中心”查看对应的接口权限。
#步骤二:引入JS文件
在需要调用JS接口的页面引入如下JS文件,(支持https): http://res.wx.qq.com/open/js/jweixin-1.0.0.js
请注意,如果你的页面启用了https,务必引入https://res.wx.qq.com/open/js/jweixin-1.0.0.js ,否则将无法在iOS9.0以上系统中成功使用JSSDK
如需使用摇一摇周边功能,请引入 jweixin-1.1.0.js
备注:支持使用 AMD/CMD 标准模块加载方法加载
#步骤三:通过config接口注入权限验证配置
所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用,目前Android微信客户端不支持pushState的H5新特性,所以使用pushState来实现web app的页面会导致签名失败,此问题会在Android6.2中修复)。
wx.config({ debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId: '', // 必填,公众号的唯一标识 timestamp: , // 必填,生成签名的时间戳 nonceStr: '', // 必填,生成签名的随机串 signature: '',// 必填,签名,见附录1 jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 });
#步骤四:通过ready接口处理成功验证
wx.ready(function(){ // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。 });
#步骤五:通过error接口处理失败验证
wx.error(function(res){ // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。 });
#接口调用说明
所有接口通过wx对象(也可使用jWeixin对象)来调用,参数是一个对象,除了每个接口本身需要传的参数之外,还有以下通用参数:
- success:接口调用成功时执行的回调函数。
- fail:接口调用失败时执行的回调函数。
- complete:接口调用完成时执行的回调函数,无论成功或失败都会执行。
- cancel:用户点击取消时的回调函数,仅部分有用户取消操作的api才会用到。
- trigger: 监听Menu中的按钮点击时触发的方法,该方法仅支持Menu中的相关接口。
备注:不要尝试在trigger中使用ajax异步请求修改本次分享的内容,因为客户端分享操作是一个同步操作,这时候使用ajax的回包会还没有返回。
以上几个函数都带有一个参数,类型为对象,其中除了每个接口本身返回的数据之外,还有一个通用属性errMsg,其值格式如下:
- 调用成功时:"xxx:ok" ,其中xxx为调用的接口名
- 用户取消时:"xxx:cancel",其中xxx为调用的接口名
- 调用失败时:其值为具体错误信息
#基础接口
#判断当前客户端版本是否支持指定JS接口
wx.checkJsApi({ jsApiList: ['chooseImage'], // 需要检测的JS接口列表,所有JS接口列表见附录2, success: function(res) { // 以键值对的形式返回,可用的api值true,不可用为false // 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"} } });
备注:checkJsApi接口是客户端6.0.2新引入的一个预留接口,第一期开放的接口均可不使用checkJsApi来检测。
#分享接口
请注意不要有诱导分享等违规行为,对于诱导分享行为将永久回收公众号接口权限,详细规则请查看:朋友圈管理常见问题。
#获取“分享到朋友圈”按钮点击状态及自定义分享内容接口
wx.onMenuShareTimeline({ title: '', // 分享标题 link: '', // 分享链接 imgUrl: '', // 分享图标 success: function () { // 用户确认分享后执行的回调函数 }, cancel: function () { // 用户取消分享后执行的回调函数 } });
#获取“分享给朋友”按钮点击状态及自定义分享内容接口
wx.onMenuShareAppMessage({ title: '', // 分享标题 desc: '', // 分享描述 link: '', // 分享链接 imgUrl: '', // 分享图标 type: '', // 分享类型,music、video或link,不填默认为link dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空 success: function () { // 用户确认分享后执行的回调函数 }, cancel: function () { // 用户取消分享后执行的回调函数 } });
#获取“分享到QQ”按钮点击状态及自定义分享内容接口
wx.onMenuShareQQ({ title: '', // 分享标题 desc: '', // 分享描述 link: '', // 分享链接 imgUrl: '', // 分享图标 success: function () { // 用户确认分享后执行的回调函数 }, cancel: function () { // 用户取消分享后执行的回调函数 } });
#获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口
wx.onMenuShareWeibo({ title: '', // 分享标题 desc: '', // 分享描述 link: '', // 分享链接 imgUrl: '', // 分享图标 success: function () { // 用户确认分享后执行的回调函数 }, cancel: function () { // 用户取消分享后执行的回调函数 } });
#获取“分享到QQ空间”按钮点击状态及自定义分享内容接口
wx.onMenuShareQZone({ title: '', // 分享标题 desc: '', // 分享描述 link: '', // 分享链接 imgUrl: '', // 分享图标 success: function () { // 用户确认分享后执行的回调函数 }, cancel: function () { // 用户取消分享后执行的回调函数 } });
#图像接口
#拍照或从手机相册中选图接口
wx.chooseImage({ count: 1, // 默认9 sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有 sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有 success: function (res) { var localIds = res.localIds; // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片 } });
#预览图片接口
wx.previewImage({ current: '', // 当前显示图片的http链接 urls: [] // 需要预览的图片http链接列表 });
#上传图片接口
wx.uploadImage({ localId: '', // 需要上传的图片的本地ID,由chooseImage接口获得 isShowProgressTips: 1, // 默认为1,显示进度提示 success: function (res) { var serverId = res.serverId; // 返回图片的服务器端ID } });
备注:上传图片有效期3天,可用微信多媒体接口下载图片到自己的服务器,此处获得的 serverId 即 media_id,参考文档 ../12/58bfcfabbd501c7cd77c19bd9cfa8354.html 目前多媒体文件下载接口的频率限制为10000次/天,如需要调高频率,请邮件weixin-open@qq.com,邮件主题为【申请多媒体接口调用量】,请对你的项目进行简单描述,附上产品体验链接,并对用户量和使用量进行说明。
#下载图片接口
wx.downloadImage({ serverId: '', // 需要下载的图片的服务器端ID,由uploadImage接口获得 isShowProgressTips: 1, // 默认为1,显示进度提示 success: function (res) { var localId = res.localId; // 返回图片下载后的本地ID } });
#音频接口
#开始录音接口
wx.startRecord();
#停止录音接口
wx.stopRecord({ success: function (res) { var localId = res.localId; } });
#监听录音自动停止接口
wx.onVoiceRecordEnd({ // 录音时间超过一分钟没有停止的时候会执行 complete 回调 complete: function (res) { var localId = res.localId; } });
#播放语音接口
wx.playVoice({ localId: '' // 需要播放的音频的本地ID,由stopRecord接口获得 });
#暂停播放接口
wx.pauseVoice({ localId: '' // 需要暂停的音频的本地ID,由stopRecord接口获得 });
#停止播放接口
wx.stopVoice({ localId: '' // 需要停止的音频的本地ID,由stopRecord接口获得 });
#监听语音播放完毕接口
wx.onVoicePlayEnd({ success: function (res) { var localId = res.localId; // 返回音频的本地ID } });
#上传语音接口
wx.uploadVoice({ localId: '', // 需要上传的音频的本地ID,由stopRecord接口获得 isShowProgressTips: 1, // 默认为1,显示进度提示 success: function (res) { var serverId = res.serverId; // 返回音频的服务器端ID } });
备注:上传语音有效期3天,可用微信多媒体接口下载语音到自己的服务器,此处获得的 serverId 即 media_id,参考文档 ../12/58bfcfabbd501c7cd77c19bd9cfa8354.html 目前多媒体文件下载接口的频率限制为10000次/天,如需要调高频率,请邮件weixin-open@qq.com,邮件主题为【申请多媒体接口调用量】,请对你的项目进行简单描述,附上产品体验链接,并对用户量和使用量进行说明。
#下载语音接口
wx.downloadVoice({ serverId: '', // 需要下载的音频的服务器端ID,由uploadVoice接口获得 isShowProgressTips: 1, // 默认为1,显示进度提示 success: function (res) { var localId = res.localId; // 返回音频的本地ID } });
#智能接口
#识别音频并返回识别结果接口
wx.translateVoice({ localId: '', // 需要识别的音频的本地Id,由录音相关接口获得 isShowProgressTips: 1, // 默认为1,显示进度提示 success: function (res) { alert(res.translateResult); // 语音识别的结果 } });
#设备信息
#获取网络状态接口
wx.getNetworkType({ success: function (res) { var networkType = res.networkType; // 返回网络类型2g,3g,4g,wifi } });
#地理位置
#使用微信内置地图查看位置接口
wx.openLocation({ latitude: 0, // 纬度,浮点数,范围为90 ~ -90 longitude: 0, // 经度,浮点数,范围为180 ~ -180。 name: '', // 位置名 address: '', // 地址详情说明 scale: 1, // 地图缩放级别,整形值,范围从1~28。默认为最大 infoUrl: '' // 在查看位置界面底部显示的超链接,可点击跳转 });
#获取地理位置接口
wx.getLocation({ type: 'wgs84', // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入'gcj02' success: function (res) { var latitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90 var longitude = res.longitude; // 经度,浮点数,范围为180 ~ -180。 var speed = res.speed; // 速度,以米/每秒计 var accuracy = res.accuracy; // 位置精度 } });
#摇一摇周边
#开启查找周边ibeacon设备接口
wx.startSearchBeacons({ ticket: "", // 摇周边的业务ticket, 系统自动添加在摇出来的页面链接后面 complete: function(argv) { // 开启查找完成后的回调函数 } });
备注:如需接入摇一摇周边功能,请参考:申请开通摇一摇周边
#关闭查找周边ibeacon设备接口
wx.stopSearchBeacons({ complete:function(res){ //关闭查找完成后的回调函数 } });
#监听周边ibeacon设备接口
wx.onSearchBeacons({ complete:function(argv){ //回调函数,可以数组形式取得该商家注册的在周边的相关设备列表 } });
备注:上述摇一摇周边接口使用注意事项及更多返回结果说明,请参考:摇一摇周边获取设备信息
#界面操作
#隐藏右上角菜单接口
wx.hideOptionMenu();
#显示右上角菜单接口
wx.showOptionMenu();
#关闭当前网页窗口接口
wx.closeWindow();
#批量隐藏功能按钮接口
wx.hideMenuItems({ menuList: [] // 要隐藏的菜单项,只能隐藏“传播类”和“保护类”按钮,所有menu项见附录3 });
#批量显示功能按钮接口
wx.showMenuItems({ menuList: [] // 要显示的菜单项,所有menu项见附录3 });
#隐藏所有非基础按钮接口
wx.hideAllNonBaseMenuItem(); // “基本类”按钮详见附录3
#显示所有功能按钮接口
wx.showAllNonBaseMenuItem();
#微信扫一扫
#调起微信扫一扫接口
wx.scanQRCode({ needResult: 0, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果, scanType: ["qrCode","barCode"], // 可以指定扫二维码还是一维码,默认二者都有 success: function (res) { var result = res.resultStr; // 当needResult 为 1 时,扫码返回的结果 } });
#微信小店
#跳转微信商品页接口
wx.openProductSpecificView({ productId: '', // 商品id viewType: '' // 0.默认值,普通商品详情页1.扫一扫商品详情页2.小店商品详情页 });
#微信卡券
微信卡券接口中使用的签名凭证api_ticket,与步骤三中config使用的签名凭证jsapi_ticket不同,开发者在调用微信卡券JS-SDK的过程中需依次完成两次不同的签名,并确保凭证的缓存。
#获取api_ticket
api_ticket 是用于调用微信卡券JS API的临时票据,有效期为7200 秒,通过access_token 来获取。
开发者注意事项:
- 此用于卡券接口签名的api_ticket与步骤三中通过config接口注入权限验证配置使用的jsapi_ticket不同。
- 由于获取api_ticket 的api 调用次数非常有限,频繁刷新api_ticket 会导致api调用受限,影响自身业务,开发者需在自己的服务存储与更新api_ticket。
#接口调用请求说明
http请求方式: GET https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=wx_card
#参数说明
参数 | 是否必须 | 说明 |
access_token | 是 | 调用接口凭证 |
#返回数据
数据示例:
{ "errcode": 0, "errmsg": "ok", "ticket": "bxLdikRXVbTPdHSM05e5u5sUoXNKdvsdshFKA", "expires_in": 7200 }
参数名 | 描述 |
errcode | 错误码 |
errmsg | 错误信息 |
ticket | api_ticket,卡券接口中签名所需凭证 |
expires_in | 有效时间 |
#拉取适用卡券列表并获取用户选择信息
wx.chooseCard({ shopId: '', // 门店Id cardType: '', // 卡券类型 cardId: '', // 卡券Id timestamp: 0, // 卡券签名时间戳 nonceStr: '', // 卡券签名随机串 signType: '', // 签名方式,默认'SHA1' cardSign: '', // 卡券签名 success: function (res) { var cardList= res.cardList; // 用户选中的卡券列表信息 } });
参数名 | 必填 | 类型 | 示例值 | 描述 |
shopId | 否 | string(24) | 1234 | 门店ID。shopID用于筛选出拉起带有指定location_list(shopID)的卡券列表,非必填。 |
cardType | 否 | string(24) | GROUPON | 卡券类型,用于拉起指定卡券类型的卡券列表。当cardType为空时,默认拉起所有卡券的列表,非必填。 |
cardId | 否 | string(32) | p1Pj9jr90_SQRaVqYI239Ka1erk | 卡券ID,用于拉起指定cardId的卡券列表,当cardId为空时,默认拉起所有卡券的列表,非必填。 |
timestamp | 是 | string(32) | 14300000000 | 时间戳。 |
nonceStr | 是 | string(32) | sduhi123 | 随机字符串。 |
signType | 是 | string(32) | SHA1 | 签名方式,目前仅支持SHA1 |
cardSign | 是 | string(64) | abcsdijcous123 | 签名。 |
cardSign详见附录4。开发者特别注意:签名错误会导致拉取卡券列表异常为空,请仔细检查参与签名的参数有效性。
特别提醒
拉取列表仅与用户本地卡券有关,拉起列表异常为空的情况通常有三种:签名错误、时间戳无效、筛选机制有误。请开发者依次排查定位原因。
#批量添加卡券接口
wx.addCard({ cardList: [{ cardId: '', cardExt: '' }], // 需要添加的卡券列表 success: function (res) { var cardList = res.cardList; // 添加的卡券列表信息 } });
cardExt详见附录4,值得注意的是,这里的card_ext参数必须与参与签名的参数一致,格式为字符串而不是Object,否则会报签名错误。
#查看微信卡包中的卡券接口
wx.openCard({ cardList: [{ cardId: '', code: '' }]// 需要打开的卡券列表 });
#核销后再次赠送卡券接口
wx.consumeAndShareCard({ cardId: '', code: '' });
参数说明:
参数 | 说明 |
cardId | 上一步核销的card_id,若传入错误的card_id会报错 |
code | 上一步核销的code,若传入错误的code会报错 |
注意: 该接口只支持微信6.3.6以上版本的客户端,开发者在调用时需要注意两点:
- 需要引入1.1.0版本的js文件: https://res.wx.qq.com/open/js/jweixin-1.1.0.js
- 需要判断用户客户端版本号,做出容错处理,详情点击:判断当前客户端版本是否支持指定JS接口
#微信支付
#发起一个微信支付请求
wx.chooseWXPay({ timestamp: 0, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符 nonceStr: '', // 支付签名随机串,不长于 32 位 package: '', // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=***) signType: '', // 签名方式,默认为'SHA1',使用新版支付需传入'MD5' paySign: '', // 支付签名 success: function (res) { // 支付成功后的回调函数 } });
备注:prepay_id 通过微信支付统一下单接口拿到,paySign 采用统一的微信支付 Sign 签名生成方法,注意这里 appId 也要参与签名,appId 与 config 中传入的 appId 一致,即最后参与签名的参数有appId, timeStamp, nonceStr, package, signType。
请注意该接口只能在你配置的支付目录下调用,同时需确保支付目录在JS接口安全域名下。
微信支付开发文档:https://pay.weixin.qq.com/wiki/doc/api/index.html
三 、单元测试,e2e测试
10.使用 karma + mocha + sinon-chai E2E单元测试
用 karma 测试单文件组件
我们使用karma为测试管理工具,mocha 框架撰写测试,同时使用 chai 作为断言库。
karma是一个基于Node.js的JavaScript测试执行过程管理工具(Test Runner)。该工具可用于测试所有主流Web浏览器,也可集成到CI(Continuous integration)工具,也可和其他代码编辑器一起使用。
mocha:mocha是一个基于nodejs和浏览器集合的各种特性的JavaScript测试库,并且让异步测试变得简单,支持TDD(测试驱动开发)和BDD(行为驱动开发),在测试中捕获到异常时,会给出灵活准确的报告。
chai:chai是一个基于nodejs的断言库,并且完美支持各种主流的JavaScript测试框架。
#Karma 配置
var webpackConfig = require('../../build/cooking.test'); module.exports = function(config) { var configuration = { browsers: ['Chrome'],// 浏览器 customLaunchers: { Chrome_travis_ci: { base: 'Chrome', flags: ['--no-sandbox'] } }, frameworks: ['mocha', 'sinon-chai'],// 测试框架 reporters: ['spec', 'coverage'], // 测试报告 files: ['./index.js'], // 测试入口文件 preprocessors: { // 预处理器 karma-webpack './index.js': ['webpack', 'sourcemap'] }, webpack: webpackConfig,// Webpack配置 webpackMiddleware: { // Webpack中间件 noInfo: true }, coverageReporter: { // 测试覆盖率报告 dir: './coverage', reporters: [ { type: 'lcov', subdir: '.' }, { type: 'text-summary' } ] }, client: { mocha: { timeout: 4000 } } }; if (process.env.TRAVIS) { configuration.browsers = ['Chrome_travis_ci']; } config.set(configuration); };
#Mocha和chai
所谓断言,就是对组件做一些操作,并预言产生的结果。如果测试结果与断言相同则测试通过。 测试脚本都要放在 test/unit/specs/ 目录下。 脚本命名方式为 [组件名].spec.js。 单元测试默认测试 src 目录下除了 main.js 之外的所有文件,可在 test/unit/index.js 文件中修改。
#按钮组件示例
import { createTest, createVue, destroyVM } from '../util'; // 导入工具类 import Button from 'packages/button'; // 导入buttoe组件 // 测试脚本里面应该包括一个或多个describe块,称为测试套件(test suite) describe('Button', () => { let vm; afterEach(() => { // 在本区块的每个测试用例之后执行 销毁操作,同样的钩子还有before/after/beforeEach destroyVM(vm); }); // 每个describe块应该包括一个或多个it块,称为测试用例(test case) it('create', () => { vm = createTest(Button, { // 创建一个测试组件实例 type: 'primary' }, true); let buttonElm = vm.$el; //断言:DOM中button元素中的class 包含了'el-button--primary' expect(buttonElm.classList.contains('el-button--primary')).to.be.true; }); it('icon', () => { vm = createTest(Button, { icon: 'el-icon-search' }, true); let buttonElm = vm.$el; expect(buttonElm.querySelector('.el-icon-search')).to.be.ok; }); it('click', done => { let result; vm = createVue({ template: ` <vd-button @click="handleClick"></vd-button> `, methods: { handleClick(evt) { result = evt; } } }, true); vm.$el.click(); setTimeout(_ => { expect(result).to.exist; done(); }, 20); }); });
- Util.js 方法包含了大多数Vue组件化的测试需求。
createTest创建一个测试组件实例,参数:Compo-组件对象,propsData-props 数据,mounted-是否添加到 DOM 上
createVue创建一个 Vue 的实例对象,参数:Compo 组件配置,mounted 是否添加到 DOM 上 - vm.$el vm.$nextTick 和 vm.$ref 都是在测试过程中比较常用的一些Vue语法糖。
- 需要注意: vm.$nextTick 方法是异步的,所以需要在里面使用done方法。
- 异步断言,方法参数需要是 _ 或者 done
- 查询元素通过 querySelector 方法查询class获得 vm.$el.querySelector('.el-breadcrumb').innerText
- 查询是否存在某个Class通过 classList.contains 方法获得,查找的结果为 true 或 false vm.$el .classList.contains('el-button--primary')
- 异步测试必须以 done() 方法结尾。setTimeout 和 vm.$nextTick 是常用的异步测试。
- 实现按钮点击:通过获取按钮元素 btn,执行 btn.click() 方法实现。
- 由于 Vue 进行 异步更新DOM 的情况,一些依赖DOM更新结果的断言必须在 Vue.nextTick 回调中进行。 异步测试必须以 done() 方法结尾。setTimeout 和 vm.$nextTick 是常用的异步测试。
#elementUI中单元测试工具脚本Util
/** * 回收 vm */ exports.destroyVM =z function(vm) { /** * 创建一个 Vue 的实例对象 */ exports.createVue = function(Compo, mounted = false) /** * 创建一个测试组件实例 */ exports.createTest = function(Compo, propsData = {}, mounted = false) { /** * 触发一个事件 */ exports.triggerEvent = function(elm, name, ...opts) { /** * 触发 “mouseup” 和 “mousedown” 事件 */ exports.triggerClick = function(elm, ...opts) {
11.使用Nightwatch进行E2E测试中文教程
E2E测试
E2E(end to end)测试是指端到端测试又叫功能测试,站在用户视角,使用各种功能、各种交互,是用户的真实使用场景的仿真。在产品高速迭代的现在,有个自动化测试,是重构、迭代的重要保障。对web前端来说,主要的测试就是,表单、动画、页面跳转、dom渲染、Ajax等是否按照期望。
#E2E测试驱动重构
重构代码的目的是什么?是为了使代码质量更高、性能更好、可读性和拓展性更强。在重构时如何保证修改后正常功能不受影响?E2E测试正是保证功能的最高层测试,不关注代码实现细节,专注于代码能否实现对应的功能,相比于单元测试、集成测试更灵活,你可以彻底改变编码的语法、架构甚至编程范式而不用重新写测试用例。
#Nightwatch
知道nightwatch是因为vue-cli工具安装的时候会询问是否需要安装nightwatch。本身vue项目也是使用nightwatch来e2e测试的。nightwatch是一个使用selenium或者webdriver或者phantomjs的nodejs编写的e2e自动测试框架,可以很方便的写出测试用例来模仿用户的操作来自动验证功能的实现。selenium是一个强大浏览器测试平台,支持firefox、chrome、edge等浏览器的模拟测试,其原理是打开浏览器时,把自己的JavaScript文件嵌入网页中。然后selenium的网页通过frame嵌入目标网页。这样,就可以使用selenium的JavaScript对象来控制目标网页。
#Nightwatch安装
通过npm安装nightwatch。
$ npm install [-g] nightwatch
根据需要安装Selenium-server或者其他Webdriver,比手动去下载jar文件要方便很多。安装哪些Webdriver取决于你想要测试哪些浏览器,如果只测试Chrome甚至可以不装Selenium-server
通过npm安装nightwatch。
$ npm install selenium-server $ npm install chromedriver
#Nightwatch的配置
nightwatch的使用很简单,一个nightwatch.json或者nightwatch.config.js(后者优先级高)配置文件,使用runner会自动找同级的这两个文件来获取配置信息。也可以手动使用--config来制定配置文件的相对路径。
{ "src_folders" : ["tests"], "output_folder" : "reports", "custom_commands_path" : "", "custom_assertions_path" : "", "page_objects_path" : "", "globals_path" : "", "selenium" : { "start_process" : false, "server_path" : "", "log_path" : "", "port" : 4444, "cli_args" : { "webdriver.chrome.driver" : "", "webdriver.gecko.driver" : "", "webdriver.edge.driver" : "" } }, "test_settings" : { "default" : { "launch_url" : "http://localhost", "selenium_port" : 4444, "selenium_host" : "localhost", "silent": true, "screenshots" : { "enabled" : false, "path" : "" }, "desiredCapabilities": { "browserName": "firefox", "marionette": true } }, "chrome" : { "desiredCapabilities": { "browserName": "chrome" } }, "edge" : { "desiredCapabilities": { "browserName": "MicrosoftEdge" } } } }
son配置文件大概就是上面这样,分为基本配置、selenium配置和测试配置三个部分。基本配置依次为测试用例源文件路径、输出路径、基础指令路径、全局配置路径等。selenium设置包括是否开启、路径、端口等,cli_args指定将要运行的webdriver。test_settings制定测试时各个环境的设置,默认是default,通过--env加环境名可以指定配置的任意环境。只要把测试用例放在对应的文件夹使用module.exports暴露一个对象,其中key是测试名,value是一个接受browser实例的函数,在函数中进行断言,nightwatch会自动依次调用文件夹中的测试用例。
#API
Nightwatch的API分为四个部分
#1.Expect
在browser实例上以.expect.element开头的BDD(行为驱动测试)风格的接口,0.7及以上版本nightwatch可用。通过.element方法传入一个selector(参考querySelector或者jq的语法)获取到dom实例,通过.text、.value、.attribute等方法获取到实例属性。还有一些语意明确的修饰:
#2.Assert
.attributeContains(selector, attribute, expected[, message]) 检查指定元素(selector)的指定属性(attribute)是否包含有期待的值(expected)打印出指定信息(可选填的message)其他方法讲解类似,不一一赘述 .attributeEquals(selector, attribute, expected[, message]) 检查元素指定属性是否等于预期 .containText(selector, expectedText[, message]) 包含有指定的文本 .cssClassPresent(selector, className[, message]) 检查元素指定class是否存在 .cssClassNotPresent(selector, className[, message]) 检查元素指定class是否不存在 .cssProperty(selector, cssProperty, expected[, message]) 检查元素指定css属性的值是否等于预期 .elementPresent(selector[, message) 检查指定元素是否存在于DOM中 .elementNotPresent(selector[, message) 检查指定元素是否不存在于DOM中 .hidden(selector[, message) 检查指定元素是否不可见 .title(expected[, message]) 检查页面标题是否等于预期 .urlContains(expectedText[, message]) 检查当前URL是否包含预期的值 .urlEquals(expected[, message]) 检查当前URL是否等于预期的值 .value(selector, expectedText[, message]) 检查指定元素的value是否等于预期 .valueContains(selector, expectedText[, message]) 检查指定元素的value是否包含预期的值 .visible(selector[, message) 检查指定元素是否可见
#3.Commands
.clearValue(selector[, message]) 清空input、textarea的值 .click(selector[, callback]) callback为执行完命令后需要执行的回调 .closeWindow([callback]) .deleteCookie(cookieName[, callback]) .deleteCookies([callback]) .end([callback]) 结束会话(关闭窗口) .getAttribute(selector, attribute, callback) .getCookie(cookieName, callback) .getCookies(callback) .getCssProperty(selector, cssProperty, callback) .getElementSize(selector, callback) .getLocation(selector, callback) .getLocationInView(selector, callback) .getLog(typeString, callback) 获取selenium的log,其中type为string或者function .getLogTypes(callback) .getTagName(selector, callback) .getText(selector, callback) .getTitle(callback) .getValue(selector, callback) .init([url]) url方法的别名,如果不传url则跳转到配置中的launch_url .injectScript(scriptUrl[, id, callback]) 注入script .isLogAvailable(typeString, callback) typeString为string或者function,用来测试log的type是否可用 .isVisible(selector, callback) .maximizeWindow([callback]) 最大化当前窗口 .moveToElement(selector, xoffset, yoffset[, callback]) 移动鼠标到相对于指定元素的指定位置 .pause(ms[, callback]) 暂停指定的时间,如果没有时间,则无限暂停 .perform(callback) 一个简单的命令,允许在回调中访问api .resizeWindow(width, height[, callback]) 调整窗口的尺寸 .saveScreenshot(fileName, callback) .setCookie(cookie[, callback]) .setValue(selector, inputValue[, callback]) .setWindowPosition(offsetX, offsetY[, callback]) .submitForm(selector[, callback]) .switchWindow(handleOrName[, callback]) .urlHash(hash) .useCss() 设置当前选择器模式为CSS .useXpath() 设置当前选择器模式为Xpath .waitForElementNotPresent(selector, time[, abortOnFailure, callback, message]) 指定元素指定时间内是否不存在 .waitForElementNotVisible(selector, time[, abortOnFailure, callback, message]) 指定元素指定时间内是否不可见 .waitForElementPresent(selector, time[, abortOnFailure, callback, message]) .waitForElementVisible(selector, time[, abortOnFailure, callback, message])
#4.webdriver protocol
可以操作一些更底层的东西,比如:
四、其他
11.Javascript模块化编程(三):require.js的用法
这个系列的第一部分和第二部分,介绍了Javascript模块原型和理论概念,今天介绍如何将它们用于实战。
我采用的是一个非常流行的库require.js。
#一、为什么要用require.js?
最早的时候,所有Javascript代码都写在一个文件里面,只要加载这一个文件就够了。后来,代码越来越多,一个文件不够了,必须分成多个文件,依次加载。下面的网页代码,相信很多人都见过。
<script src="1.js"></script> <script src="2.js"></script> <script src="3.js"></script> <script src="4.js"></script> <script src="5.js"></script> <script src="6.js"></script>
这段代码依次加载多个js文件。
这样的写法有很大的缺点。首先,加载的时候,浏览器会停止网页渲染,加载文件越多,网页失去响应的时间就会越长;其次,由于js文件之间存在依赖关系,因此必须严格保证加载顺序(比如上例的1.js要在2.js的前面),依赖性最大的模块一定要放到最后加载,当依赖关系很复杂的时候,代码的编写和维护都会变得困难。
require.js的诞生,就是为了解决这两个问题:
(1)实现js文件的异步加载,避免网页失去响应;
(2)管理模块之间的依赖性,便于代码的编写和维护。
#二、require.js的加载
使用require.js的第一步,是先去官方网站下载最新版本。
下载后,假定把它放在js子目录下面,就可以加载了。
<script src="js/require.js"></script>
有人可能会想到,加载这个文件,也可能造成网页失去响应。解决办法有两个,一个是把它放在网页底部加载,另一个是写成下面这样:
<script src="js/require.js" defer async="true" ></script>
async属性表明这个文件需要异步加载,避免网页失去响应。IE不支持这个属性,只支持defer,所以把defer也写上。
加载require.js以后,下一步就要加载我们自己的代码了。假定我们自己的代码文件是main.js,也放在js目录下面。那么,只需要写成下面这样就行了:
<script src="js/require.js" data-main="js/main"></script>
data-main属性的作用是,指定网页程序的主模块。在上例中,就是js目录下面的main.js,这个文件会第一个被require.js加载。由于require.js默认的文件后缀名是js,所以可以把main.js简写成main。
#三、主模块的写法
上一节的main.js,我把它称为"主模块",意思是整个网页的入口代码。它有点像C语言的main()函数,所有代码都从这儿开始运行。
下面就来看,怎么写main.js。
如果我们的代码不依赖任何其他模块,那么可以直接写入javascript代码。
// main.js
alert("加载成功!");
但这样的话,就没必要使用require.js了。真正常见的情况是,主模块依赖于其他模块,这时就要使用AMD规范定义的的require()函数。
// main.js require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){ // some code here });
require()函数接受两个参数。第一个参数是一个数组,表示所依赖的模块,上例就是['moduleA', 'moduleB', 'moduleC'],即主模块依赖这三个模块;第二个参数是一个回调函数,当前面指定的模块都加载成功后,它将被调用。加载的模块会以参数形式传入该函数,从而在回调函数内部就可以使用这些模块。
require()异步加载moduleA,moduleB和moduleC,浏览器不会失去响应;它指定的回调函数,只有前面的模块都加载成功后,才会运行,解决了依赖性的问题。
下面,我们看一个实际的例子。
假定主模块依赖jquery、underscore和backbone这三个模块,main.js就可以这样写:
require(['jquery', 'underscore', 'backbone'], function ($, _, Backbone){ // some code here });
require.js会先加载jQuery、underscore和backbone,然后再运行回调函数。主模块的代码就写在回调函数中。
#四、模块的加载
上一节最后的示例中,主模块的依赖模块是['jquery', 'underscore', 'backbone']。默认情况下,require.js假定这三个模块与main.js在同一个目录,文件名分别为jquery.js,underscore.js和backbone.js,然后自动加载。
使用require.config()方法,我们可以对模块的加载行为进行自定义。require.config()就写在主模块(main.js)的头部。参数就是一个对象,这个对象的paths属性指定各个模块的加载路径。
require.config({ paths: { "jquery": "jquery.min", "underscore": "underscore.min", "backbone": "backbone.min" } });
上面的代码给出了三个模块的文件名,路径默认与main.js在同一个目录(js子目录)。如果这些模块在其他目录,比如js/lib目录,则有两种写法。一种是逐一指定路径。
require.config({ paths: { "jquery": "lib/jquery.min", "underscore": "lib/underscore.min", "backbone": "lib/backbone.min" } });
另一种则是直接改变基目录(baseUrl)。
require.config({ baseUrl: "js/lib", paths: { "jquery": "jquery.min", "underscore": "underscore.min", "backbone": "backbone.min" } });
如果某个模块在另一台主机上,也可以直接指定它的网址,比如:
require.config({ paths: { "jquery": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min" } });
require.js要求,每个模块是一个单独的js文件。这样的话,如果加载多个模块,就会发出多次HTTP请求,会影响网页的加载速度。因此,require.js提供了一个优化工具,当模块部署完毕以后,可以用这个工具将多个模块合并在一个文件中,减少HTTP请求数。
#五、AMD模块的写法
require.js加载的模块,采用AMD规范。也就是说,模块必须按照AMD的规定来写。
具体来说,就是模块必须采用特定的define()函数来定义。如果一个模块不依赖其他模块,那么可以直接定义在define()函数之中。
假定现在有一个math.js文件,它定义了一个math模块。那么,math.js就要这样写:
// math.js define(function (){ var add = function (x,y){ return x+y; }; return { add: add }; });
加载方法如下:
// main.js require(['math'], function (math){ alert(math.add(1,1)); });
如果这个模块还依赖其他模块,那么define()函数的第一个参数,必须是一个数组,指明该模块的依赖性。
define(['myLib'], function(myLib){ function foo(){ myLib.doSomething(); } return { foo : foo }; });
当require()函数加载上面这个模块的时候,就会先加载myLib.js文件。
#六、加载非规范的模块
理论上,require.js加载的模块,必须是按照AMD规范、用define()函数定义的模块。但是实际上,虽然已经有一部分流行的函数库(比如jQuery)符合AMD规范,更多的库并不符合。那么,require.js是否能够加载非规范的模块呢?
回答是可以的。
这样的模块在用require()加载之前,要先用require.config()方法,定义它们的一些特征。
举例来说,underscore和backbone这两个库,都没有采用AMD规范编写。如果要加载它们的话,必须先定义它们的特征。
require.config({ shim: { 'underscore':{ exports: '_' }, 'backbone': { deps: ['underscore', 'jquery'], exports: 'Backbone' } } });
require.config()接受一个配置对象,这个对象除了有前面说过的paths属性之外,还有一个shim属性,专门用来配置不兼容的模块。具体来说,每个模块要定义(1)exports值(输出的变量名),表明这个模块外部调用时的名称;(2)deps数组,表明该模块的依赖性。
比如,jQuery的插件可以这样定义:
shim: { 'jquery.scroll': { deps: ['jquery'], exports: 'jQuery.fn.scroll' } }
#七、require.js插件
require.js还提供一系列插件,实现一些特定的功能。
domready插件,可以让回调函数在页面DOM结构加载完成后再运行。
require(['domready!'], function (doc){ // called once the DOM is ready });
text和image插件,则是允许require.js加载文本和图片文件。
define([ 'text!review.txt', 'image!cat.jpg' ], function(review,cat){ console.log(review); document.body.appendChild(cat); } );
类似的插件还有json和mdown,用于加载json文件和markdown文件。
(完)
12.VS Code 的常用快捷键
一、vs code 的常用快捷键
1、注释:
a) 单行注释:[ctrl+k,ctrl+c] 或 ctrl+/
b) 取消单行注释:[ctrl+k,ctrl+u] (按下ctrl不放,再按k + u)
c) 多行注释:[alt+shift+A]
d) 多行注释:/**
2、移动行:alt+up/down
3、显示/隐藏左侧目录栏 ctrl + b
4、复制当前行:shift + alt +up/down
5、删除当前行:shift + ctrl + k
6、控制台终端显示与隐藏:ctrl + ~
7、查找文件/安装vs code 插件地址:ctrl + p
8、代码格式化:shift + alt +f
9、新建一个窗口 : ctrl + shift + n
10、行增加缩进: ctrl + [
11、行减少缩进: ctrl + ]
12、裁剪尾随空格(去掉一行的末尾那些没用的空格) : ctrl + shift + x
13、字体放大/缩小: ctrl + ( + 或 - )
14、拆分编辑器 : ctrl + 1/2/3
15、切换窗口 : ctrl + shift + left/right
16、关闭编辑器窗口 : ctrl + w
17、关闭所有窗口 : ctrl + k + w
18、切换全屏 : F11
19、自动换行 : alt + z
20、显示git : ctrl + shift + g
21、全局查找文件:ctrl + shift + f
22、显示相关插件的命令(如:git log):ctrl + shift + p
23、选中文字:shift + left / right / up / down
24、折叠代码: ctrl + k + 0-9 (0是完全折叠)
25、展开代码: ctrl + k + j (完全展开代码)
26、删除行 : ctrl + shift + k
27、快速切换主题:ctrl + k / ctrl + t
28、快速回到顶部 : ctrl + home
29、快速回到底部 : ctrl + end
30、格式化选定代码 :ctrl + k / ctrl +f
#二、vs code 的常用插件
1、Auto Rename Tag 修改html标签,自动帮你完成尾部闭合标签的同步修改,和webstorm一样。
2、Auto Close Tag 自动闭合HTML标签
4、Beautiful 格式化代码的工具
5、Dash Dash是MacOS的API文档浏览器和代码段管理器
6、Ejs Snippets ejs 代码提示
7、ESLint 检查javascript语法错误与提示
8、File Navigator 快速查找文件
9、Git History(git log) 查看git log
10、Gulp Snippets 写gulp时用到,gulp语法提示。
11、HTML CSS Support 在HTML标签上写class智能提示当前项目所支持的样式
12、HTML Snippets 超级好用且初级的H5代码片段以及提示
13、Debug for Chrome 让vs code映射chrome的debug功能,静态页面都可以用vscode来打断点调试、配饰稍微复杂一点
14、Document this Js的注释模板
15、jQuery Code Snippets jquery提示工具
16、Html2jade html模板转pug模板
17、JS-CSS-HTML Formatter 格式化
18、Npm intellisense require 时的包提示工具
19、Open in browser 打开默认浏览器
20、One Dark Theme 一个vs code的主题
21、Path Intellisense 自动路径补全、默认不带这个功能
22、Project Manager 多个项目之间快速切换的工具
23、Pug(Jade) snippets pug语法提示
24、React Components 根据文件名创建反应组件代码。
25、React Native Tools reactNative工具类为React Native项目提供了开发环境。
26、Stylelint css/sass代码审查
27、Typings auto installer 安装vscode 的代码提示依赖库,基于typtings的
28、View In Browser 默认浏览器查看HTML文件(快捷键Ctrl+F1可以修改)
29、Vscode-icons 让vscode资源目录加上图标、必备
30、VueHelper Vue2代码段(包括Vue2 api、vue-router2、vuex2)
31、Vue 2 Snippets vue必备vue代码提示
32、Vue-color vue语法高亮主题
33、Auto-Open Markdown Preview markdown文件自动开启预览
34、EverMonkey 印象笔记
35、atom one dark atom的一个高亮主题(个人推荐)
#三、常用的电脑快捷键
1、ctrl + shift + delete 快速清除浏览器缓存
2、ctrl + alt + delete 快速进入任务管理器页面
3、window + L 快速锁定电脑
4、window + d 所有窗口最小化
5、 window + e 打开我的资源管理器(我的电脑)
6、 window + f 快速打开搜索窗口
7、 alt + tab 快速查看打开的应用与窗口
#VS Code 快捷键(中英文对照版)
常用 General
按 Press | 功能 | Function |
Ctrl + Shift + P,F1 | 显示命令面板 | Show Command Palette |
Ctrl + P | 快速打开 | Quick Open |
Ctrl + Shift + N | 新窗口/实例 | New window/instance |
Ctrl + Shift + W | 关闭窗口/实例 | Close window/instance |
基础编辑 Basic editing
按 Press | 功能 | Function |
Ctrl+X | 剪切行(空选定) | Cut line (empty selection) |
Ctrl+C | 复制行(空选定) | Copy line (empty selection) |
Alt+ ↑ / ↓ 向上/向下移动行 Move line up/down
Shift+Alt + ↓ / ↑ 向上/向下复制行 Copy line up/down
Ctrl+Shift+K 删除行 Delete line
Ctrl+Enter 在下面插入行 Insert line below
Ctrl+Shift+Enter 在上面插入行 Insert line above
Ctrl+Shift+\ 跳到匹配的括号 Jump to matching bracket
Ctrl+] / [ 缩进/缩进行 Indent/outdent line
Home 转到行首 Go to beginning of line
End 转到行尾 Go to end of line
Ctrl+Home 转到文件开头 Go to beginning of file
Ctrl+End 转到文件末尾 Go to end of file
Ctrl+↑ / ↓ 向上/向下滚动行 Scroll line up/down
Alt+PgUp / PgDown 向上/向下滚动页面 Scroll page up/down
Ctrl+Shift+[ 折叠(折叠)区域 Fold (collapse) region
Ctrl+Shift+] 展开(未折叠)区域 Unfold (uncollapse) region
Ctrl+K Ctrl+[ 折叠(未折叠)所有子区域 Fold (collapse) all subregions
Ctrl+K Ctrl+] 展开(未折叠)所有子区域 Unfold (uncollapse) all subregions
Ctrl+K Ctrl+0 折叠(折叠)所有区域 Fold (collapse) all regions
Ctrl+K Ctrl+J 展开(未折叠)所有区域 Unfold (uncollapse) all regions
Ctrl+K Ctrl+C 添加行注释 Add line comment
Ctrl+K Ctrl+U 删除行注释 Remove line comment
Ctrl+/ 切换行注释 Toggle line comment
Shift+Alt+A 切换块注释 Toggle block comment
Alt+Z 切换换行 Toggle word wrap
导航 Navigation
按 Press 功能 Function
Ctrl + T 显示所有符号 Show all Symbols
Ctrl + G 转到行... Go to Line...
Ctrl + P 转到文件... Go to File...
Ctrl + Shift + O 转到符号... Go to Symbol...
Ctrl + Shift + M 显示问题面板 Show Problems panel
F8 转到下一个错误或警告 Go to next error or warning
Shift + F8 转到上一个错误或警告 Go to previous error or warning
Ctrl + Shift + Tab 导航编辑器组历史记录 Navigate editor group history
Alt + ←/→ 返回/前进 Go back / forward
Ctrl + M 切换选项卡移动焦点 Toggle Tab moves focus
搜索和替换 Search and replace
按 Press 功能 Function
Ctrl + F 查找 Find
Ctrl + H 替换 Replace
F3 / Shift + F3 查找下一个/上一个 Find next/previous
Alt + Enter 选择查找匹配的所有出现 Select all occurences of Find match
Ctrl + D 将选择添加到下一个查找匹配 Add selection to next Find match
Ctrl + K Ctrl + D 将最后一个选择移至下一个查找匹配项 Move last selection to next Find match
Alt + C / R / W 切换区分大小写/正则表达式/整个词 Toggle case-sensitive / regex / whole word
多光标和选择 Multi-cursor and selection
按 Press 功能 Function
Alt +单击 插入光标 Insert cursor
Ctrl + Alt +↑/↓ 在上/下插入光标 Insert cursor above / below
Ctrl + U 撤消上一个光标操作 Undo last cursor operation
Shift + Alt + I 在选定的每一行的末尾插入光标 Insert cursor at end of each line selected
Ctrl + I 选择当前行 Select current line
Ctrl + Shift + L 选择当前选择的所有出现 Select all occurrences of current selection
Ctrl + F2 选择当前字的所有出现 Select all occurrences of current word
Shift + Alt + → 展开选择 Expand selection
Shift + Alt + ← 缩小选择 Shrink selection
Shift + Alt + (拖动鼠标) 列(框)选择 Column (box) selection
Ctrl + Shift + Alt +(箭头键) 列(框)选择 Column (box) selection
Ctrl + Shift + Alt + PgUp / PgDown 列(框)选择页上/下 Column (box) selection page up/down
丰富的语言编辑 Rich languages editing
按 Press 功能 Function
Ctrl + 空格 触发建议 Trigger suggestion
Ctrl + Shift + Space 触发器参数提示 Trigger parameter hints
Tab Emmet 展开缩写 Emmet expand abbreviation
Shift + Alt + F 格式化文档 Format document
Ctrl + K Ctrl + F 格式选定区域 Format selection
F12 转到定义 Go to Definition
Alt + F12 Peek定义 Peek Definition
Ctrl + K F12 打开定义到边 Open Definition to the side
Ctrl + . 快速解决 Quick Fix
Shift + F12 显示引用 Show References
F2 重命名符号 Rename Symbol
Ctrl + Shift + . /, 替换为下一个/上一个值 Replace with next/previous value
Ctrl + K Ctrl + X 修剪尾随空格 Trim trailing whitespace
Ctrl + K M 更改文件语言 Change file language
编辑器管理 Editor management
按 Press 功能 Function
Ctrl+F4, Ctrl+W 关闭编辑器 Close editor
Ctrl+K F 关闭文件夹 Close folder
Ctrl+\ 拆分编辑器 Split editor
Ctrl+ 1 / 2 / 3 聚焦到第1,第2或第3编辑器组 Focus into 1st, 2nd or 3rd editor group
Ctrl+K Ctrl+ ←/→ 聚焦到上一个/下一个编辑器组 Focus into previous/next editor group
Ctrl+Shift+PgUp / PgDown 向左/向右移动编辑器 Move editor left/right
Ctrl+K ← / → 移动活动编辑器组 Move active editor group
文件管理 File management
按 Press 功能 Function
Ctrl+N 新文件 New File
Ctrl+O 打开文件... Open File...
Ctrl+S 保存 Save
Ctrl+Shift+S 另存为... Save As...
Ctrl+K S 全部保存 Save All
Ctrl+F4 关闭 Close
Ctrl+K Ctrl+W 关闭所有 Close All
Ctrl+Shift+T 重新打开关闭的编辑器 Reopen closed editor
Ctrl+K 输入保持打开 Enter Keep Open
Ctrl+Tab 打开下一个 Open next
Ctrl+Shift+Tab 打开上一个 Open previous
Ctrl+K P 复制活动文件的路径 Copy path of active file
Ctrl+K R 显示资源管理器中的活动文件 Reveal active file in Explorer
Ctrl+K O 显示新窗口/实例中的活动文件 Show active file in new window/instance
显示 Display
按 Press 功能 Function
F11 切换全屏 Toggle full screen
Shift+Alt+1 切换编辑器布局 Toggle editor layout
Ctrl+ = / - 放大/缩小 Zoom in/out
Ctrl+B 切换侧栏可见性 Toggle Sidebar visibility
Ctrl+Shift+E 显示浏览器/切换焦点 Show Explorer / Toggle focus
Ctrl+Shift+F 显示搜索 Show Search
Ctrl+Shift+G 显示Git Show Git
Ctrl+Shift+D 显示调试 Show Debug
Ctrl+Shift+X 显示扩展 Show Extensions
Ctrl+Shift+H 替换文件 Replace in files
Ctrl+Shift+J 切换搜索详细信息 Toggle Search details
Ctrl+Shift+C 打开新命令提示符/终端 Open new command prompt/terminal
Ctrl+Shift+U 显示输出面板 Show Output panel
Ctrl+Shift+V 切换Markdown预览 Toggle Markdown preview
Ctrl+K V 从旁边打开Markdown预览 Open Markdown preview to the side
调试 Debug
按 Press 功能 Function
F9 切换断点 Toggle breakpoint
F5 开始/继续 Start/Continue
Shift+F5 停止 Stop
F11 / Shift+F11 下一步/上一步 Step into/out F10 跳过 Step over
Ctrl+K Ctrl+I 显示悬停 Show hover
集成终端 Integrated terminal
按 Press 功能 Function
Ctrl+` 显示集成终端 Show integrated terminal
Ctrl+Shift+` 创建新终端 Create new terminal
Ctrl+Shift+C 复制选定 Copy selection
Ctrl+Shift+V 粘贴到活动端子 Paste into active terminal
Ctrl+↑ / ↓ 向上/向下滚动 Scroll up/down
Shift+PgUp / PgDown 向上/向下滚动页面 Scroll page up/down
Ctrl+Home / End 滚动到顶部/底部 Scroll to top/bottom