Vue.js 知识库总结

178 阅读6分钟

1 / 概述

1.1 / 了解

Vue.js(读音 /vjuː/,类似于 view)是一套构建用户界面的渐进式框架

Vue 只关注视图层, 采用自底向上增量开发的设计。

原生JS是命令式编程,而Vue是声明式编程,更方便于前后端分离

1.2 / 安装

  1. js文件引入

    • vue.min.js 用于生产环境
    • vue.js 用于开发环境
  2. CDN方法

  3. NPM方法

    npm需要依赖node.js环境,先下载安装node工具包

    # 传统安装 npm
    
    # 查看版本
    node -v
    npm -v
    
    # 国内淘宝镜像安装 cnpm
    # 同步频率10min, 尽量与官方同步, 使用 cnpm 命令代替 npm
    
    # 安装
    npm install -g cnpm --registry=https://registry.npm.taobao.org
    
    # 升级npm
    cnpm install npm -g
    

1.3 / 项目目录结构

目录/文件说明
/build项目构建(webpack)相关代码
/config配置目录、端口号等
/node_modulesnpm 加载的项目依赖模块
/src开发目录
/static静态资源目录(图片、字体等),会原封不动上传
/test初始测试目录(可删除)
index.html首页入口文件
package.json项目配置文件
README.md项目的说明文档
.xxxx文件一些配置文件,包括语法配置,git配置等
/src 文件夹下的文件说明
/assets放置一些图片,如logo等
/components组件存放文件
App.vue项目入口文件(组件也可以直接写在这里)
main.js项目核心js文件
根目录下的.xxxx文件说明
.babelrcJavaScript 最新语法向下兼容编译配置
.editorconfig协同团队开发人员之间的代码风格规范化的默认配置文件
.eslintignore忽略ESLint语法检查配置文件
.eslintrc.jsESLint语法检查配置规则
.gitignore写入过滤文件规则,git过程中会忽略上传

1.4 / Hello Vue

<body>
<!-- 配置id为对应实例名称 -->
<!-- 实例中的数据会传入显示于{{}}中 -->
<div id="app">
  {{message}}
  <p>{{info()}}</p>
 </div>	
  
<script src="../vue.js"></script>
<script>
const App = new Vue({   //声明一个app实例
  el: '#app',  	//用于需要挂载管理的元素
  data: {      	//定义数据
  	message: 'Hello Vue'
  },
  methods: {
  	info(){
  		return this.message + "- GOOD!!!";
		}
	}
})
</script>
</body>

每个 Vue 应用都需要通过实例化 Vue 来实现,Vue 构造器的结构如下:

el :它是 DOM 元素中的 id,其改动全部在以上指定的 div 内,div 外部不受影响

data :用于定义数据属性

methods :用于定义的函数,可以通过 return 来返回函数值

{{ }} :用于输出对象属性和函数返回值

除了数据属性,我们可以使用Vue自身提供的实例属性与方法,使用前缀 ,以便与用户定义的属性区分开来,如,以便与用户定义的属性区分开来,如`data$el`

定义data的三种方式

data: {
  msg: 'hi'
}
-----
data: function() {
  return {
  	msg: 'hi'
  }
}
-----
data() {
  return {
  	msg: 'hi'
  }
}

data:{} 是一个对象,data(){} 是一个函数。

若不使用return,包裹的数据会在项目中全局可见,造成变量污染;而使用return包裹后,数据中的变量只在当前组件中生效,不会影响其他组件。

为了不让多处的组件共享同一个 data 对象,只能返回函数。

2 / 基础

2.1 / Mastache语法

// 基本变量替换
{{x}}	//hi你好 (const x = 'hi你好')

// 拼接
{{aaa + bbb}}		//aaabbb
{{aaa + ' ' + bbb}}	//aaa bbb
{{aaa}} {{bbb}}	//aaa bbb

// 简单表达式
{{xx * 2}}		//6 (let xx = 3) 

// 注释
{{!comments}}	//不会渲染输出

2.2 / 指令

判断循环

v-if

用于条件性地渲染一块内容,在指令的表达式返回 true 时被渲染。

<h1 v-if="true">Show Me!</h1>

v-else

作为条件逻辑为假的选项渲染,必须搭配 v-if来使用,否则不会被识别。

<div v-if="num1+num2 > 1">大于1</div>
<div v-else>小于1</div>

v-for

通常用于循环遍历,渲染输出数组或对象里的多个数据。

<!-- 迭代普通数组 -->
<p v-for="(item,i) in list">索引:{{i}} 项:{{item}}</p>
<!-- 迭代对象数组 -->
<p v-for="(user,i) in obj">编号:{{user.id}} 名称:{{user.name}}</p>
<!-- 迭代对象 -->
<p v-for="(val,key) in user">键:{{key}} 值:{{val}}</p>
<!-- 迭代数字 值从1开始 -->
<p v-for="count in 4">第{{count}}次循环</p>
data:{
	list:['红','黄','蓝 ','绿'],
  obj:[
    {id:1, name:'小红'},
    {id:2, name:'小黄'},
    {id:3, name:'小蓝'}
  ],
  user:{
    id:1,
    name:'小红',
    gender:'女'
  }
}

!!! key的使用:(由于vue中存在虚拟DOM,有时需要绑定key来提高性能)

无key属性时,状态默认绑定的是位置;有key属性时,状态根据key的属性值绑定到了相应的数组元素。

必须用v-bind属性绑定的形式,指定key的值。

key属性只能使用 number 或 String。

由Vue的高复用性,key值需要具有唯一性,不推荐使用index作为key值。

对数组列表里的数据进行操作,可以使用 push()、pop()、shift()、unshift()、splice()、sort()、reverse() 等方法实现响应式操作

或者使用 filter()、concat()、slice() 等非响应式方法来对整个数组进行改变

注:通过索引值修改数组元素并非响应式

push():在集合中添加元素,并返回新的长度

pop():从集合中把最后一个元素删除,并返回这个元素的值。

shift():从集合中把第一个元素删除,并返回这个元素的值。

unshif():在集合开头添加一个或更多元素,并返回新的长度

splice():修改Array的“万能方法”,可以从指定的索引开始删除若干元素,然后再从该位置添加若干元素。

// 情况数组的三种方式
Arr.splice(0, Arr.length);
Arr.length = 0;
Arr = [];	

// 方法3并非真正清空数组,而是重新建立个对象,相比其它效率更快更高

事件

v-on

用于指令监听 DOM 事件,并在触发时运行指定命令。v-on 可以用 @ 的语法糖代替。

<button @click="counter += 1">加1</button>
<p>每点击一次按钮counter增加{{ counter }}次</p>

若处理逻辑更为复杂的事件,直接调用方法名称。

<button @click="showit">Click it</button>
methods: {
  showit(){
    alert('弹出警示窗!')
  }
}

v-on 绑定的事件函数名,若不需要传递参数,可以不加括号;

若存在自定义参数需要传递,则要在函数名后加上括号,并填上对应参数。

在事件处理中调用类似 event.preventDefault() 的方法,在vue中提供了多个事件修饰符

  • .stop:调用 event.stopPropagation(),阻止事件继续传播
  • .prevent:调用 event.preventDefault()
  • .native:监听组件根元素的原生事件
  • .capture:使用事件捕获模式,即内部元素触发的事件先在此处理后,才交由内部元素处理
  • .self:只当在 event.target 是当前元素自身时触发处理函数,即事件不是从内部元素触发的
  • .once:事件只触发一次回调
  • .{keyCode | keyAlias}:只有当事件从特定键触发时才触发回调

@focus @input @click 事件区别 @focus 获取 input 焦点事件 (失去焦点,再次获取焦点时触发)

@input 用于监听事件 只要输入的值变化了就会触发input

@click 点击事件 点击触发

表单绑定

v-bind

用于元素属性的动态绑定,在 html 标签属性内传递数据,简写语法糖为 :

(1)绑定图片src、超链接href、value等

<a v-bind:href="url">点击我</a>
<img :src="imgsrc" />
data:{
  url:"https://sina.com",
  imgsrc:"https://cn.vuejs.org/images/logo.png",
  imgurl:require("../../assets/img/mbe/qingtian@3x.png")
}
// 引用本地图片,data中必须用require加载,否则会当成字符串来处理

(2)绑定class

<h2 class="static" 
    :class="{changeColor:isActive, changeBgc:isActive}">对象语法</h2>
<h2 :class="classObj">class对象组</h2>

<button @click="btnClick">改变颜色按钮</button>
<!-- 执行后标签变为<h2 class="static changeColor changeBgc"> -->
data:{
  isActive: true,
  classObj: {
    changeColor: true,
    changeBgc: false
  }
},
methods:{
  btnClick() {
    this.isActive = !this.isActive
  }
}
<style>
  .changeColor {
    color: red;
  }
  .changeBgc {
    background-color: gray;
  }
</style>

(3)绑定style:CSS 属性名可以用驼峰式或短横线(需用单引号括起来)来命名

<div :style="{color:colorStyle, fontSize:fontStyle + 'px'}"></div>
<div :style="styleObj"></div>
data: {
  colorStyle: 'red',
  fontStyle: 30,

  // 直接绑定一个样式对象组通常更好,这会让模板更清晰
  styleObj: {
    color: 'red',
    fontSize: '13px'
  }
}
  • 引用本地图片,必须用 require('url')加载,否则会当成字符串来处理
  • 本地图片路径名的坑:有时为了方便引入一组icon,需要利用icon的名称动态拼接
<!-- 推荐此方法,使用ES6语法 -->
<img :src="require(`../../assets/img/wicon/${iconcode}`+`.png`)" />
<img :src="require(`../../assets/img/wicon/${iconcode}.png`)" />

<!-- 这种方法能正常运行,但偶尔会报错 -->
<img :src="require('../../assets/img/wicon/'+iconcode+'.png')" /> 
<img :src="require('../../assets/img/wicon/'+imgurl)" /> 
data() {
  return {
    iconcode: '101',		//图片编号
    imgurl: '101.png'		//完整图片文件名
  }
}

v-model

用于在表单元素上实现数据双向绑定,实际实现原理是 v-bind 和 v-on (@input) 的结合共同实现的语法糖

<input v-model="msg">
<p>在输入框里输入文字,msg对应跟着改变: {{msg}}</p>

对文本域进行双向绑定,不要像这样 <textarea>{{text}}</textarea>进行插值,因为它并不会生效,应用 v-model 来代替。

复选框checkbox、单选按钮radio、下拉选择框select等也常使用v-model,具体参考

注意:v-model会忽略所有表单元素的 value、checked、selected 的初始值,而将Vue实例的数据作为数据来源。你应该通过JavaScript在组件的data选项中声明初始值!

值绑定

修饰符

  • .lazy:懒加载,v-model默认下是实时同步更新,但有些情况下不想让其更新太频繁,可使用.lazy可以式数据在失去焦点或回车时才会更新
  • .number:默认输入框中无论输入字母或数字,都会被当作字符串处理,若只希望处理数字类型,可以使用.number锁定类型
  • .trim:自动过滤用户输入的首尾空白字符

v-bind 和 v-model 的区别:

  1. v-bind是数据绑定,没有双向绑定效果,但不一定在表单元素上使用,任何有效元素上都可以使用;
  2. v-model是双向绑定,基本上只用在表单元素上;
  3. 当v-bind和v-model同时用在一个元素上时,它们各自的作用没变,但v-model优先级更高,而且需区分这个元素是单个的还是一组出现的。

插值语法

v-once

只会渲染一次(通常为首次加载时),随后的渲染,使用了此指令的元素/组件及其所有的子节点,都会当作静态内容忽略并跳过,这可以用于优化更新性能。

<p>{{msg}}</p>		//使用v-model双向绑定,随着输入框内容的修改,此内容会跟着变化
<p v-once>{{msg}}</p>  //msg不会改变
<p><input type="text" v-model="msg" /></p>

v-html

可以用于更新替换元素的innerHTML内容,可以转化为html页面的形式展示,不推荐多用,常用于应急更改元素。

v-text

仅渲染数据,不解析 HTML 代码标签

v-pre

即使遇到Mastache标签,也会跳过vue的编译过程,跳过传入值,直接显示原始内容,可以加快编辑。

<!-- 用msg1的信息更新此div里的innerHTML内容,即使里面有文本也会被忽略 -->
<div v-html="msg1">忽略我</div>
<!-- 直接把div标签替换成h1标签,并显示其内容 -->
<div v-html="msg2"></div>		
<!-- 直接渲染显示 <em>Hello</em> Vue! 不会解析标签直接把数据输出 -->
<div v-text="msg3"></div>
<!-- 依然显示{{msg4}} -->
<div v-pre>{{msg4}}</div> 
data: {
  msg1: '替换此元素里的内容',
  msg2: '<h1>我是一个大标题</h1>',
  msg3: '<em>Hello</em> Vue!',
  msg4: '我不会被编译'
}

v-cloak

斗篷。当网速较慢时,只加载了html部分但未加载到vue.js解析部分,导致 Vue 来不及渲染,此时页面上会先出现mastache标签,才先显示传入的数据,导致页面闪烁,但这看起来并不友好,所以需要先用此方法做“隐藏”。

使用前需要先在css中设置规则,有时候v-cloak会失效,还通常需要通过@import "style.css"来导入

<div id="app" v-cloak>{{context}}</div>
[v-cloak]{
  display: none;
}

使用与不使用的区别 在简单项目中,使用 v-cloak 指令是解决屏幕闪动的好方法。

但在大型、工程化的项目中(webpack、vue-router)只有一个空的 div 元素,元素中的内容是通过路由挂载来实现的,这时我们就不需要用到 v-cloak 指令咯。

其它指令

v-show

根据表达式之真假值,控制切换元素的 display CSS属性的显示与隐藏,当条件变化时该指令触发过渡效果。

即当v-show="false"时,带有v-show的标签,其CSS属性 display: none,即该标签不进行显示。

与v-if都可以实现相同功能,但v-if的原理的对整个语句进行删除与重写,若多次执行,会消耗内存,使用叫频繁时推荐使用v-show

2.3 / 计算属性

vue模板内可以使用一些简单便利的表达式,但设计初衷仅用于简单运算。在模板中放入太多的逻辑会让模板过重且难以维护。在遇到一些复杂逻辑时应该使用Vue独有的特性——计算属性computed来进行处理。

使用 computed 创建一个计算属性,它实际是一个函数,只是将它当作属性用,一般配合 return 使用,用于处理一些复杂的逻辑(包括运算、函数调用等),只要最终返回一个结果即可,它们通常不会被多次调用。

<p>原始msg: "{{msg}}"</p>
<p>使用计算属性后返回的msg: "{{reversedMsg}}"</p>
<!-- 把复杂处理放在了计算属性里 -->
<!-- 显示结果 olleH -->
data: {
	msg: 'Hello'
},
computed: {
  reversedMsg() {
    return this.msg.split('').reverse().join('') //this指向vm实例
  }
}

除了上例简单的用法,**计算属性还可以依赖多个Vue 实例的数据,**只要其中任一数据变化,计算属性就会重新执行,视图也会更新。

<button @click="add()">补充货物1</button>
<div>总价为:{{price}}</div>
data: {
  package1: {
    count: 5,
    price: 5
  },
  package2: {
    count: 10,
    price: 10
  }
},
computed: {
	price: function(){
		return this.package1.count*this.package1.price+this.package2.count*this.package2.price
		//总价随着货物或价格的改变会重新计算
	}
},
methods: {
  add: function(){
  	this.package1.count++
	}
}

计算属性还有两个很实用的小技巧容易被忽略:**一是计算属性可以依赖其他计算属性; 二是计算属性不仅可以依赖当前Vue 实例的数据,还可以依赖其他实例的数据,**例如:

<div id="app1"></div>
<div id="app2">{{reverseText}}</div>
var app1 = new Vue({
	el: '#app1',
	data: {
    text: 'computed'
  }
});
var app2 = new Vue({
  el: '#app2',
  computed: {
    reverseText: function(){
      return app1.text.split('').reverse().join('');  //使用app1的数据进行计算
    }
  }
});

每一个计算属性都包含一个getter 和一个setter ,我们上面的两个示例都是计算属性的默认用法, 只是利用了getter 来读取。

在你需要时,也可以提供一个setter 函数, 当手动修改计算属性的值就像修改一个普通数据那样时,就会触发setter 函数,执行一些自定义的操作,例如:

var vm = new Vue({
    el: '#demo',
    data: {
        firstName: 'Foo',
        lastName: 'Bar'
    },
    computed: {
        fullName: {
            // getter
            get: function () {
                return this.firstName + ' ' + this.lastName
            },
            // setter
            set: function (newValue) {
                var names = newValue.split(' ');
                this.firstName = names[0];
                this.lastName = names[names.length - 1];
            }
        }
    }
});
//现在再运行 vm.fullName = 'John Doe' 时,setter 会被调用,vm.firstName 和 vm.lastName 也会相应地被更新。

绝大多数情况下,我们只会用默认的getter 方法来读取一个计算属性,在业务中很少用到setter,所以在声明一个计算属性时,可以直接使用默认的写法,不必将getter 和setter 都声明。

计算属性缓存

上面的例子,除了使用计算属性外,我们也可以通过在表达式中调用方法来达到同样的效果,如

<div>{{reverseTitle()}}</div>
// 在组件中
methods: {
  reverseTitle: function () {
    return this.title.split('').reverse().join('')
  }
}

我们可以将同一函数定义为一个方法而不是一个计算属性,两种方式的最终结果确实是完全相同的。只是一个使用reverseTitle()取值,一个使用reverseTitle取值。

然而,不同的是计算属性是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变时才会重新求值。

这就意味着只要 title还没有发生改变,多次访问 reverseTitle计算属性会立即返回之前的计算结果,而不必再次执行函数。

<div>{{reverseTitle}}</div>
<div>{{reverseTitle1()}}</div>
<button @click="add()">补充货物1</button>
<div>总价为:{{price}}</div>
computed: {
  reverseTitle: function(){
    return this.title.split('').reverse().join('')  
    //而使用计算属性,只要title没变,页面渲染是不会重新进这里来计算的,而是使用了缓存。
  },
 price: function(){
     return this.package1.count*this.package1.price+this.package2.count*this.package2.price
 }
},
methods: {
    add: function(){
        this.package1.count++
    },
    reverseTitle1: function(){
      return this.title.split('').reverse().join('')
      //点击补充货物,也会进这个方法,再次计算。不是刷新,而是只要页面渲染,就会进方法里重新计算。
    }
},

相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。

我们为什么需要缓存?假设我们有一个性能开销比较大的的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A

如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。

本质:原理中有set()方法和get()方法,但是set()方法不常用,一般简写即可,并当作属性使用

computed与methods区别:method会执行多次,computed只会致谢一次,然后把结果放于缓存,下次直接调用,更加高效

2.4 / 组件化

组件(Component)是可复用的 Vue 实例,用来扩展 HTML 元素,封装可重用的代码。

几乎任意类型的应用的界面都可以抽象为一个组件树:

注册组件的几种方式:

Vue.extend 属于 Vue 的全局 API,在实际业务开发中很少使用,而局部组件只能在这个实例中使用,比较常用的是全局组件。

Vue.component('cpnName', {
	template: '<h1>自定义组件!</h1>'		// 配合template创建模板
})

注册后,就能在页面中以自定义组件名作为标签的方式来调用组件:

<div id="app">
    <cpn-name></cpn-name>
</div>

在自定义组件命名时,可以允许使用驼峰式命名(componentName)或短横线命名(component-name),但当应用到 DOM 中时,由于 html 对大小写不敏感,使用驼峰式命名会报错,应该使用短横线分割命名方式。

父子组件通讯

在 Vue 实例里使用components注册的组件为父组件,而使用 Vue.component 的方式注册的全局组件为子组件,通常会将其插入到页面中,若子组件只在 Vue.component 中注册过,而未在 Vue 实例中注册,单独使用它时会报错。并且若需要父子之间进行数据传递,树妖使用以下的方法:

(1)父传子:props

<div id="app">
    <child msg="hello!"></child>
</div>
Vue.component('child', {
  props: ['msg'],  // 声明 props
  template: '<span>{{msg}}</span>'
})

(2)子传父:$emit

子组件要把数据传递给父组件,就需要使用自定义事件

<div id="app">
</div>

组件访问

  • 父访问子
    • $children:用得较少
    • $refs
  • 子访问父
    • $parent
    • $root

组件不可以直接访问 Vue 实例,即不能直接在 Vue 实例里传数据到模板中,但组件内部有一个data属性用来存放数据,且有一个返回值;在组件中data必须是一个函数而不是对象,因为在每次调用此组件模板时,为避免多个模板之间的数据共用,被相互影响,需要使用函数来创建多个内存空间来存放组件(包括内部数据),各自取用各自的data数据

组件里的data才使用 return

data(){
	return ...
}

2.5 / 模块化

webpack

本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个bundle。

Webpack是当下最热门的前端资源模块化管理和打包工具,它可以将许多松散耦合的模块按照依赖和规则打包成符合生产环境部署的前端资源。还可以将按需加载的模块进行代码分离,等到实际需要时再异步加载。通过 loader 转换,任何形式的资源都可以当做模块,比如 CommonsJS、AMD、ES6、CSS、JSON、CoffeeScript、LESS 等;伴随着移动互联网的大潮,当今越来越多的网站已经从网页模式进化到了WebApp模式。它们运行在现代浏览器里,使用HTML5、CSS3、ES6等新的技术来开发丰富的功能,网页已经不仅仅是完成浏览器的基本需求;WebApp通常是一个SPA(单页面应用),每一个视图通过异步的方式加载,这导致页面初始化和使用过程中会加载越来越多的JS代码,这给前端的开发流程和资源组织带来了巨大挑战。

前端开发和其他开发工作的主要区别,首先是前端基于多语言、多层次的编码和组织工作,其次前端产品的交付是基于浏览器的,这些资源是通过增量加载的方式运行到浏览器端,如何在开发环境组织好这些碎片化的代码和资源,并且保证他们在浏览器端快速、优雅的加载和更新,就需要一个模块化系统,这个理想中的模块化系统是前端工程师多年来一直探索的难题。

3 / 深入

3.x / vue-cil

# 安装vue-cil

# 1.全局安装 vue-cli
npm install --global vue-cli
# 2.创建一个基于 webpack 模板的新项目
## -- Vue 2.x version --
vue init webpack my-project
## -- Vue 3.x version --
vue create my-project

# 3.配置项目(ver2.x)
? Project name my-project	//项目名称
? Project description A Vue.js project //项目描述
? Author youyuxi <yyx990803@gmail.com>	//作者
? Vue build	//打包方式 runtime-compiler/runtime-only
? Install vue-router?	//安装vue路由
? Use ESLint to lint your code? //使用ESLint规范
? Setup unit tests? //单元测试
? Setup e2e tests with Nightwatch? //端对端测试
? Should we run 'npm install' for you .. //使用NPM或Yarn命令

# 4.进入项目
cd my-project
npm install
npm run dev

# 5.成功执行以上命令后访问 http://localhost:8080/
# 6.打包项目 (build.js)
npm run build

Promise

Promise 是异步编程的一种解决方案,ES6中一个非常重要的特性,支持链式编程

什么情况下会用到Promise? 一般情况下是有异步操作时,使用Promise对这个异步操作进行封装

什么时候我们会来处理异步事件呢? 进行网络请求的时候,在数据请求成功时,将数据通过传入的函数回调出去。但当网络请求非常复杂时,就会出现回调地狱。

Promise 三种状态

当我们开发中有异步操作时,就可以给异步操作包装一个Promise,异步操作之后会有三种状态:

  • pending:等待状态,比如正在进行网络请求,或者定时器没有到时间。
  • fulfill:满足状态,当我们主动回调了resolve时,就处于该状态,并且会回调.then
  • reject:拒绝状态,当我们主动回调了reject时,就处于该状态,并且会回调.catch
new Promise((resolve, reject) => {		 // 1.处理网络请求
  // 1.1 等待处理
  
  resolve('success result') // 1.2成功时调用resolve 结果传给data
  
  reject('error msg')	// 1.3失败时调用reject 结果传给err
  
}).then((data) => {		// 成功时在then里处理操作
  console.log(data);
  
}).catch((err) => {		// 失败时在catch里处理操作
  console.log(err);
})
// Promise 另外的处理形式 (成功或失败处理都写在then里)
new Promise((resolve, reject) => {
  resolve('success result')
  reject('error msg')	
}).then(data => {
  console.log(data);
}, err => {
  console.log(err);
})
// Promise 成功处理的链式调用(一)
new Promise((resolve, reject) => {
  resolve('success result')
}).then(res => {
  console.log(res);	// 第一次处理
  return Promise.resolve(res + '1')
}).then(res => {
  console.log(res);	// 第二次处理
  return Promise.resolve(res + '2')
}).then(res => {
  console.log(res);	// 第三次处理
})
// Promise 成功处理的链式调用(二) -化简
new Promise((resolve, reject) => {
  resolve('success result')
}).then(res => {
  console.log(res);	// 第一次处理
  return res + '1'
}).then(res => {
  console.log(res);	// 第二次处理
  return res + '2'
}).then(res => {
  console.log(res);	// 第三次处理
})
// Promise 失败处理的链式调用(三) 
new Promise((resolve, reject) => {
  resolve('success result')
}).then(res => {
  console.log(res);
	throw 'error msg'	// throw 抛出异常信息
}).then(res => {
  console.log(res);
})

Promise all 并发请求

Promise.all([
  new Promise((resolve, reject) => { }),
  new Promise((resolve, reject) => { })
]).then(result => {
  console.log(result);
  console.log(result[1]);
  console.log(result[2]);
})