07.vue-charp-07 组件详解(一)

组件的注册

<body>
    <div id="app1">
        <h1>app1</h1>
        <my-global-componet></my-global-componet>
        <app1-my-component></app1-my-component>
        <table>
            <p>Vue组件的模板在某些情况下会受到HTML的限制,比如
                table内规定只允许是tr、td、
                th等这些表格元素,
                所以在table内直接使用组件是无效的。这种情况下,可以使用特殊的is属性来挂载组件</p>
            <tbody is="my-global-componet"></tbody>
        </table>
    </div>
    <div id="app2">
        <h1>app2</h1>
        <my-global-componet></my-global-componet>
        <app1-my-component></app1-my-component>
    </div>
    <script src="../lib/vue.2.6.11.js"></script>
    <script>
        //全局注册组件
        Vue.component('my-global-componet', {
            template: '<div>这里是全局组件的内容</div>'
        });

        var app1 = new Vue({
            el: '#app1',
            components: {
                'app1-my-component': {
                    template: '<div>app1的局部组件,只能在 app1 中使用</div>'
                }
            },
            data: {
            }
        })

        var app2 = new Vue({
            el: '#app2',
            data: {
            }
        })
    </script>
</body>
  • 全局注册组件 Vue.component()

  • 局部组件:new Vue({components:...})

  • is="my-global-componet"

    Vue组件的模板在某些情况下会受到HTML的限制,比如table内规定只允许是tr、td、 th等这些表格元素,所以在table内直接使用组件是无效的。这种情况下,可以使用特殊的is属性来挂载组件

组件的选项

data

组件的data必须是函数,然后将数据return出去

<body>
    <div id="app">
        <my-global-componet></my-global-componet>
        <my-global-componet></my-global-componet>
    </div>
    <script src="../lib/vue.2.6.11.js"></script>
    <script>
        Vue.component('my-global-componet', {
            template: '<div> 
                          组件的data.message:{{message}} 
                          <button @click="count++">组件的data.count:{{ count }}</button> 
                          <button @click="count++">组件的data.count:{{ count }}</button> 
                     </div>',
            data: function () {
                //组件的data必须是函数,然后将数据return出去,
                return {
                    message: '组件的内容!!!',
                    count: 0
                }
            }
        });

        var app = new Vue({
            el: '#app'
        })

    </script>
</body>

使用props传递数据

props传递数据之数组


<body>
    <div id="app">
        <h1>组件</h1>
        <my-componet message="来自父组件的数据1" warming-text="来自父组件的数据2" :pare-msg="parentDynamicMsg">
        </my-componet>
        <h1>父页面</h1>
        <input type="text" v-model="parentDynamicMsg">
    </div>
    <script src="../lib/vue.2.6.11.js"></script>
    <script>
        Vue.component('my-componet', {
            template: '<div> 
                          <p>message:{{message}}</p> 
                          <p>warmingText:{{warmingText}}</p> 
                          <p>pareMsg:{{pareMsg}}</p> 
                      <div>',
            props: ['message', 'warmingText', 'pareMsg'] //驼峰命名(camelCase)的props名称要转为短横分隔命名
        });

        var app = new Vue({
            el: '#app',
            data: {
                parentDynamicMsg: "来自父组件的动态数据"
            }
        })

    </script>
</body>
  • 使用【字符串数组】来定义组件的一系列属性:props: ['message', 'warmingText', 'pareMsg']
  • 属性命名规则:驼峰命名(camelCase)的props名称要转为短横分隔命名
  • 使用v-bing:绑定来自父组件的动态数据,:pare-msg="parentDynamicMsg"

props传递数据之对象

参见 数据验证

单向数据流

Vue 2.x与Vue 1.x比较大的一个改变就是,Vue 2.x通过props传递数据是单向的了,也就是父组件数据变化时会传递给子组件,但是反过来不行。而在Vue 1.x里提供了.sync修饰符来支持双向绑定。

<body>
    <div id="app">
        <h1>组件</h1>
        <my-componet :pare-msg="parentDynamicMsg">
        </my-componet>
        <h1>父页面</h1>
        <input type="text" v-model="parentDynamicMsg">
    </div>
    <script src="../lib/vue.2.6.11.js"></script>
    <script>
        Vue.component('my-componet', {
            template: '<div> 
                          <p>pareMsg:{{pareMsg}}</p> 
                          <input type="text" v-model="pareMsg">
                      </div>',
            props: ['pareMsg']
        });

        var app = new Vue({
            el: '#app',
            data: {
                parentDynamicMsg: "来自父组件的动态数据"
            }
        })

    </script>
</body>

在组件中修改pareMsg的值并不会影响到 父组件parentDynamicMsg的值

如何使组件的值独立

<body>
    <div id="app">
        <h1>组件</h1>
        <my-componet :pare-msg="parentDynamicMsg">
        </my-componet>
        <h1>父页面</h1>
        <input type="text" v-model="parentDynamicMsg">
    </div>
    <script src="../lib/vue.2.6.11.js"></script>
    <script>
        Vue.component('my-componet', {
            template: '<div> 
                          <p>myComponetMessage:{{myComponetMessage}}</p> 
                          <input type="text" v-model="myComponetMessage">
                      </div>',
            props: ['pareMsg'],
            data: function () {
                return {
                    myComponetMessage: this.pareMsg //之后myComponetMessage就父组件的parentDynamicMsg无关,两者互不影响
                }
            }
        });

        var app = new Vue({
            el: '#app',
            data: {
                parentDynamicMsg: "来自父组件的动态数据"
            }
        })

    </script>
</body>

在组件的data定义一个变量myComponetMessage引用属性pareMsg,之后myComponetMessage就父组件的parentDynamicMsg无关,两者互不影响

组件的计算属性

<body>
    <div id="app">
        <h1>组件</h1>
        <my-componet :my-width="componentWidth">
        </my-componet>
        <h1>父页面</h1>
        <input type="text" v-model="componentWidth" placeholder="请输入宽度">
    </div>
    <script src="../lib/vue.2.6.11.js"></script>
    <script>
        Vue.component('my-componet', {
            template: '<div> 
                          <input type="text" :style="styles" :value="myWidth"/> 
                      </div>',
            props: ['myWidth'],
            computed: {
                styles: function () {
                    return {
                        color: 'red',
                         this.myWidth + 'px'
                    }
                }
            }
        });

        var app = new Vue({
            el: '#app',
            data: {
                componentWidth: 100
            }
        })
    </script>
</body>

父组件的componentWidth无单位px,在组件中通过计算属性styles.width 把单位px补上

数据验证

我们上面所介绍的props选项的值都是一个数组,一开始也介绍过,除了数组外,还可以是对象,当prop需要验证时,就需要对象写法。
一般当你的组件需要提供给别人使用时,推荐都进行数据验证,比如某个数据必须是数字类型,如果传入字符串,就会在控制台弹出警告。

(示例略)

组件数据传递之-对象

<body>
    <div id="app">
        <my-list :cpt-books="mybooks" :cpt-msg="myMsg"></my-list>
    </div>
    <script src="../lib/vue.2.6.11.js"></script>
    <script>
        Vue.component('my-list', {
            template: '<div> 
                            <h1>cptMsg={{cptMsg}}</h1> 
                            <ul>
                                <li v-for="book in cptBooks">{{book.name}}</li> 
                            </ul>
                       </div>',
            props: {
                cptMsg: {
                    type: String,
                    default: function () {
                        return "--";
                    }
                },
                cptBooks: {
                    type: Array,
                    default: function () {
                        return [];
                    }
                }
            }

        });

        var app = new Vue({
            el: '#app',
            data: {
                myMsg: "来自父组件的消息",
                mybooks: [
                    { name: '《Vue.js实战》' },
                    { name: '《JavaScript语言精粹》' },
                    { name: '《JavaScript高级程序设计》' }
                ]
            }
        })
    </script>
</body>

组件通信

从父组件向子组件通信,通过props传递数据就可以了,但Vue组件通信的场景不止有这一种,

组件关系可分为

  • 父子组件通信、
  • 兄弟组件通信、
  • 跨级组件通信。

本节将介绍各种组件之间通信的方法。

自定义事件

当子组件需要向父组件传递数据时,就要用到自定义事件。我们在介绍指令v-on时有提到,v-on除了监听DOM事件外,还可以用于组件之间的自定义事件。

  • 子组件用$emit()来触发事件,

  • 父组件用$on()来监听子组件的事件。
    父组件也可以直接在子组件的自定义标签上使用v-on来监听子组件触发的自定义事件,

<body>
    <div id="app">
        <h1>组件</h1>
        <my-componet @increase="handleGetTotal" @reduce="handleGetTotal">
        </my-componet>
        <h1>父组件</h1>
        <p>total={{total}}</p>
    </div>
    <script src="../lib/vue.2.6.11.js"></script>
    <script>
        Vue.component('my-componet', {
            template: '<div> 
                          <button @click="handleIncrease">+1</button> 
                          <button @click="handleReduce">-1</button> 
                      </div>',
            data: function () {
                return {
                    counter: 0
                }
            },
            methods: {
                handleIncrease: function () {
                    this.counter++;
                    //发起自定义事件
                    this.$emit('increase', this.counter);
                },
                handleReduce: function () {
                    this.counter--;
                    //发起自定义事件
                    this.$emit('reduce', this.counter);
                },
            },
        });

        var app = new Vue({
            el: '#app',
            data: {
                total: 0
            }, methods: {
                handleGetTotal: function (total) {
                    this.total = total;
                }
            }
        })
    </script>
</body>
  • 发起自定义事件,参数可以是若干个

​ this.$emit('increase', this.counter);

子组件向父组件传递数据-使用v-model和input事件名

<body>
    <div id="app">
        <h1>组件</h1>
        <my-componet v-model="total">
        </my-componet>
        <h1>父组件</h1>
        <p>total={{total}}</p>
    </div>
    <script src="../lib/vue.2.6.11.js"></script>
    <script>
        Vue.component('my-componet', {
            template: '<div> 
                          <button @click="handleIncrease">+1</button> 
                          <button @click="handleReduce">-1</button> 
                      </div>',
            data: function () {
                return {
                    counter: 0
                }
            },
            methods: {
                handleIncrease: function () {
                    this.counter++;
                    this.$emit('input', this.counter);
                },
                handleReduce: function () {
                    this.counter--;
                    this.$emit('input', this.counter);
                },
            },
        });

        var app = new Vue({
            el: '#app',
            data: {
                total: 0
            }
        })
    </script>
</body>

​ 子组件向父组件传递数据-使用v-model时,事件名必须是input

任何组件传递数据

在Vue.js 2.x中,推荐使用一个空的Vue实例作为中央事件总线(bus),也就是一个中介。

<body>
    <div id="app">
        <h1>组件</h1>
        <my-componet></my-componet>
        <h1>父组件</h1>
        <p>total={{total}}</p>
    </div>
    <script src="../lib/vue.2.6.11.js"></script>
    <script>
        //1.使用一个空的Vue实例作为中央事件总线(bus)
        var bus = new Vue();
        Vue.component('my-componet', {
            template: '<div> 
                          <button @click="handleIncrease">+1</button> 
                          <button @click="handleReduce">-1</button> 
                      </div>',
            data: function () {
                return {
                    counter: 0
                }
            },
            methods: {
                handleIncrease: function () {
                    this.counter++;
                    //2.bus发起事件
                    bus.$emit('on-counter-change', this.counter);
                },
                handleReduce: function () {
                    this.counter--;
                    //2.bus发起事件
                    bus.$emit('on-counter-change', this.counter);
                },
            },
        });

        var app = new Vue({
            el: '#app',
            data: {
                total: 0
            }, mounted: function () {
                let _this = this;
                //3.bus监听事件
                bus.$on('on-counter-change', function (counter) {
                    _this.total = counter;
                })
            }
        })
    </script>
</body>

首先创建了一个名为bus的空Vue实例,里面没有任何内容;

组件间通信-父链

在子组件中,使用this.$parent可以直接访问该组件的父实例或组件,父组件也可以通过this.$children访问它所有的子组件,而且可以递归向上或向下无限访问,直到根实例或最内层的组件

<body>
    <div id="app">
        <h1>组件</h1>
        <my-componet></my-componet>
        <h1>父组件</h1>
        <p>total={{total}}</p>
    </div>
    <script src="../lib/vue.2.6.11.js"></script>
    <script>

        Vue.component('my-componet', {
            template: '<div> 
                          <button @click="handleIncrease">+1</button> 
                          <button @click="handleReduce">-1</button> 
                      </div>',
            data: function () {
                return {
                    counter: 0
                }
            },
            methods: {
                handleIncrease: function () {
                    this.$parent.total++;
                },
                handleReduce: function () {
                    this.$parent.total++;
                },
            },
        });

        var app = new Vue({
            el: '#app',
            data: {
                total: 0
            }
        })
    </script>
</body>

尽管Vue允许这样操作,但在业务中,子组件应该尽可能地避免依赖父组件的数据,更不应该去主动修改它的数据,因为这样使得父子组件紧耦合,只看父组件,很难理解父组件的状态,因为它可能被任意组件修改,理想情况下,只有组件自己能修改它的状态。父子组件最好还是通过props和$emit来通信。

组件间通信-组件索引

当子组件较多时,通过this.$children来一一遍历出我们需要的一个组件实例是比较困难的,尤其是组件动态渲染时,它们的序列是不固定的。Vue提供了子组件索引的方法,用特殊的属性ref来为子组件指定一个索引名称

<body>
    <div id="app">
        <h1>组件</h1>
        <my-componet ref="refMyComponent"></my-componet>
        <h1>父组件</h1>
        <button @click="handleIncrease">+1</button>
        <button @click="handleReduce">-1</button>
    </div>
    <script src="../lib/vue.2.6.11.js"></script>
    <script>

        Vue.component('my-componet', {
            template: '<div> 
                          counter={{counter}} 
                      </div>',
            data: function () {
                return {
                    counter: 0
                }
            }
        });

        var app = new Vue({
            el: '#app',
            data: {
                total: 0
            },
            methods: {
                handleIncrease: function () {
                    this.$refs.refMyComponent.counter++;
                },
                handleReduce: function () {
                    this.$refs.refMyComponent.counter--
                },
            }
        })
    </script>
</body>

  • <my-componet ref="refMyComponent"></my-componet>
  • this.$refs.refMyComponent.counter++;

$refs只在组件渲染完成后才填充,并且它是非响应式的。它仅仅作为一个直接访问子组件的应急方案,应当避免在模板或计算属性中使用$refs。

什么是slot

当需要让组件组合使用,混合父组件的内容与子组件的模板时,就会用到slot,这个过程叫作内容分发(transclusion)。以<app>为例,它有两个特点:<app>组件不知道它的挂载点会有什么内容。挂载点的内容是由<app>的父组件决定的。<app>组件很可能有它自己的模板。
props传递数据、events触发事件和slot内容分发就构成了Vue组件的3个API来源,再复杂的组件也是由这3部分构成的。

单个slot

在子组件内使用特殊的<slot>元素就可以为这个子组件开启一个slot(插槽),在父组件模板里,插入在子组件标签内的所有内容将替代子组件的<slot> 标签及它的内容。

<body>
    <div id="app">
        <my-componet></my-componet>
        <my-componet>
            <p>父子组件内容</p>
        </my-componet>
    </div>
    <script src="../lib/vue.2.6.11.js"></script>
    <script>

        Vue.component('my-componet', {
            template: '<div> 
                          <slot>如果父组件没有插入内容,我将作为默认出现</slot> 
                      </div>'
        });

        var app = new Vue({
            el: '#app'
        })
    </script>
</body>

具名slot

<slot>元素指定一个name后可以分发多个内容,具名Slot可以与单个Slot共存

<body>
    <div id="app">
        <my-componet>
            <h2 slot="header">标题</h2>
            <p>正文内容</p>
            <p>更多的正文内容</p>
            <div slot="footer">底部信息</div>
        </my-componet>
    </div>
    <script src="../lib/vue.2.6.11.js"></script>
    <script>

        Vue.component('my-componet', {
            template: '<div class="container"> 
                          <div class="header"> 
                              <slot name="header">slot中的默认标题</slot> 
                          </div> 
                          <div class="main"> 
                              <slot></slot> 
                          </div> 
                          <div class="main2"> 
                              <slot></slot> 
                          </div> 
                          <div class="footer"> 
                              <slot name="footer"></slot> 
                          </div> 
                      </div>'
        });

        var app = new Vue({
            el: '#app'
        })
    </script>
</body>

html最后被解析为

    <div id="app">
        <div class="container">
            <div class="header">
                <h2>标题</h2>
            </div>
            <div class="main">
                <p>正文内容</p>
                <p>更多的正文内容</p>
            </div>
            <div class="main2">
                <p>正文内容</p>
                <p>更多的正文内容</p>
            </div>
            <div class="footer">
                <div>底部信息</div>
            </div>
        </div>
    </div>

<slot> 没有使用name特性,它将作为默认slot出现,父组件没有使用slot特性的元素与内容都将出现在这里。
如果没有指定默认的匿名slot,父组件内多余的内容片段都将被抛弃。

作用域插槽

作用域插槽是一种特殊的slot,使用一个可以复用的模板替换已渲染元素。

<body>
    <div id="app">
        <my-componet>
            <template slot-scope="props">
                <p>来自父组件的内容</p>
                <p>{{props.msg}}</p>
            </template>
        </my-componet>
    </div>
    <script src="../lib/vue.2.6.11.js"></script>
    <script>

        Vue.component('my-componet', {
            template: '<div class="container"> 
                           <slot msg="来自子组件的内容"></slot> 
                      </div>'
        });

        var app = new Vue({
            el: '#app'
        })
    </script>
</body>
  • scope="this api replaced by slot-scope in 2.5.0+",使用slot-scope

观察子组件的模板,在<slot>元素上有一个类似props传递数据给组件的写法msg="xxx",将数据传到了插槽。父组件中使用了<template>元素,而且拥有一个scope="props"的特性,这里的props 只是一个临时变量,就像v-for="item in items"里面的item一样。template内可以通过临时变量 props 访问来自子组件插槽的数据msg。

  • 作用域插槽更具代表性的用例是列表组件,允许组件自定义应该如何渲染列表每一项。
<body>
    <div id="app">
        <my-list :cptbooks="mybooks">
            <!-- 作用域插槽也可以是具名的Slot -->
            <template slot="book" slot-scope="props">
                <li>{{ props.bookName }}</li>
            </template>
        </my-list>
    </div>
    <script src="../lib/vue.2.6.11.js"></script>
    <script>
        Vue.component('my-list', {
            props: {
                cptbooks: {
                    type: Array,
                    default: function () {
                        return [];
                    }
                }
            },
            template: '
        <ul>
            <slot name="book"
                v-for="book in cptbooks"
                :book-name="book.name">
                <!-- 这里也可以写默认 slot内容 -->
            </slot>
        </ul> '
        });

        var app = new Vue({
            el: '#app',
            data: {
                mybooks: [
                    { name: '《Vue.js实战》' },
                    { name: '《JavaScript语言精粹》' },
                    { name: '《JavaScript高级程序设计》' }
                ]
            }
        })
    </script>
</body>

访问slot

Vue.js 2.x提供了用来访问被slot分发的内容的方法·$slots

<body>
    <div id="app">
        <my-componet>
            <h2 slot="header">标题</h2>
            <p>正文内容</p>
            <p>更多的正文内容</p>
            <div slot="footer">底部信息</div>
        </my-componet>
    </div>
    <script src="../lib/vue.2.6.11.js"></script>
    <script>

        Vue.component('my-componet', {
            template: '<div class="container"> 
                          <div class="header"> 
                              <slot name="header">slot中的默认标题</slot> 
                          </div> 
                          <div class="main"> 
                              <slot></slot> 
                          </div> 
                          <div class="footer"> 
                              <slot name="footer"></slot> 
                          </div> 
                      </div>',
            mounted: function () {
                /*
                使用$slot.访问slot
                */
                var header = this.$slots.header;
                var main = this.$slots.default;
                var footer = this.$slots.footer;
                console.log(footer);
                console.log(footer[0].elm.innerHTML);
            }
        });

        var app = new Vue({
            el: '#app'
        })
    </script>
</body>

通过$slots可以访问某个具名slot,this.$slots.default包括了所有没有被包含在具名slot中的节点。尝试编写代码,查看两个console 打印的内容。
$slots在业务中几乎用不到,在用render函数(进阶篇中将介绍)创建组件时会比较有用,但主要还是用于独立组件开发中。

组件高级用法

递归组件

组件在它的模板内可以递归地调用自己,只要给组件设置name的选项就可以了

设置name后,在组件模板内就可以递归使用了,不过需要注意的是,必须给一个条件来限制递归数量,否则会抛出错误:max stack size exceeded。

<body>
    <div id="app">
        <my-component :count="appAcount"></my-component>
        <button @click="add()">+1</button>
    </div>
    <script src="../lib/vue.2.6.11.js"></script>
    <script>
        Vue.component('my-component', {
            name: "my-component",
            template: '
                    <div>
                        {{count}} 
                        <my-component :count="count + 1" v-if="count < 5"> </my-component> 
                    </div> '
            , props: {
                count: {
                    type: Number,
                    default: 0
                }
            }

        });

        var app = new Vue({
            el: '#app',
            data: {
                appAcount: 1
            }, methods: {
                add: function () {
                    this.appAcount++;
                }
            },
        })
    </script>
</body>

组件递归使用可以用来开发一些具有未知层级关系的独立组件,比如级联选择器和树形控件等

内联模板


<body>
    <div id="app">
        <child-component inline-template>
            <div>
                <h2>在父组件中定义子组件的模板</h2>
                <p>{{ message }}</p>
                <p>{{ msg }}</p>
            </div>
        </child-component>
    </div>
    <script src="../lib/vue.2.6.11.js"></script>
    <script>
        Vue.component('child-component', {
            data: function () {
                return {
                    msg: '在子组件声明的数据'
                }
            }
        });

        var app = new Vue({
            el: '#app',
            data: {
                message: '在父组件声明的数据'
            }
        });
    </script>
</body>

组件的模板一般都是在template选项内定义的,Vue提供了一个内联模板的功能,在使用组件时,给组件标签使用inline-template特性,组件就会把它的内容当作模板,而不是把它当内容分发,这让模板更灵活。

在父组件中声明的数据message和子组件中声明的数据msg,两个都可以渲染(如果同名,优先使用子组件的数据)。这反而是内联模板的缺点,就是作用域比较难理解,如果不是非常特殊的场景,建议不要轻易使用内联模板。

实际报错:

vue.2.6.11.js:634 [Vue warn]: Property or method "message" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.

无法识别父组件中的message变量

动态组件

Vue.js提供了一个特殊的元素<component>用来动态地挂载不同的组件,使用is特性来选择要挂载的组件。

<body>
    <div id="app">
        <component :is="currentView"></component>

        <button @click="handleChangeView('A')">切换到A</button>
        <button @click="handleChangeView('B')">切换到B</button>
        <button @click="handleChangeView('C')">切换到C</button>
        <button @click="handleChangeView('D')">切换到D</button>
    </div>
    <script src="../lib/vue.2.6.11.js"></script>
    <script>
        Vue.component('global-component', {
            template: '<div>组件D</div>'
        });

        var app = new Vue({
            el: '#app',
            data: {
                currentView: 'comA'
            },
            components: {
                comA: {
                    template: '<div>组件A</div>'
                },
                comB: {
                    template: '<div>组件B</div>'
                },
                comC: {
                    template: '<div>组件C</div>'
                }
            },
            methods: {
                handleChangeView: function (component) {
                    if (component === "D") {
                        this.currentView = 'global-component'; 
                    } else {
                        this.currentView = 'com' + component;
                    }
                }
            }
        });
    </script>
</body>
  • <component :is="currentView"></component>

异步组件

使用的组件足够多时,是时候考虑下性能问题了,因为一开始把所有的组件都加载是没必要的一笔开销。好在Vue.js允许将组件定义为一个工厂函数,动态地解析组件。Vue.js只在组件需要渲染时触发工厂函数,并且把结果缓存起来,用于后面的再次渲染。

<body>
    <div id="app">
        <my-component></my-component>
    </div>
    <script src="../lib/vue.2.6.11.js"></script>
    <script>
        Vue.component('my-component', function (resolve, reject) {
            setTimeout(function () {
                resolve({
                    template: '<div>异步组件</div>'
                });
            }, 2000)
        });

        var app = new Vue({
            el: '#app'
        });
    </script>
</body>

​ 工厂函数接收一个resolve回调,在收到从服务器下载的组件定义时调用。也可以调用reject(reason)指示加载失败。这里setTimeout只是为了演示异步,具体的下载逻辑可以自己决定,比如把组件配置写成一个对象配置,通过Ajax来请求,然后调用resolve传入配置选项。

原文地址:https://www.cnblogs.com/easy5weikai/p/13257696.html