您的位置:首页 > Web前端 > Vue.js

Vue笔记0.2

2020-05-11 04:09 573 查看

文章目录

  • 2. 创建、显示、更新列表
  • 3. 案例:计数器
  • 4. 关于插值的操作
  • 5. v-bind 数据绑定
  • 6. 计算属性
  • 7. es6语法补充
  • 8. 事件监听
  • 9. 条件判断
  • 10. 循环遍历
  • 11. 书籍购物车案例
  • 12. v-model使用
  • 13. 组件化
  • 14. 组件化高级
  • 15 前端模块化
  • 16. webpack的一些配置
  • 17. vue-cli
  • 18. vue-router
  • 19. Promise
  • 20. vuex
  • Vue学习笔记

    1. 概述、特征、以及初始化

    • Vue 是一种灵活的渐进式相应框架
    • 采用声明式的编程范式

    1.1 引用Vue的方法

    <script src="../js/vue.js"></script>
    <script>
    const app = new Vue({
    el: '#app', //用于挂载需要挂载的元素
    data: {
    message: 'fuck you'
    }
    })
    </script>

    通过采用el挂载和HTML代码定义id的方式来使用Vue.
    data是vue实例对应的数据对象
    methods定义属于vue的一些方法,可以再其他地方调用,也可以在指令中使用

    2. 创建、显示、更新列表

    <div id="app">
    <ul>
    <li v-for="item in movies">{{item}}</li>
    </ul></div>
    <script src="../js/vue.js"></script>
    <script>
    const app = new Vue({
    el: '#app',
    data: {
    message: 'ssda',
    movies: ['星际穿越', '大话西游', '少年派'],
    }
    })
    </script>

    声明式的编程范式,使得列表操作变得简易。

    3. 案例:计数器

    包含新的指令,v-on 和新的属性 methods,可以通过函数的形式来匹配复杂的操作

    <button v-on:click="add">+</button>
    <button v-on:click="sub">-</button>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
    const app = new Vue({
    el: '#app',
    data: {
    counter: 0
    },
    methods: {
    add: function () {
    console.log('+++')
    this.counter++
    },
    sub: function () {
    console.log('---')
    this.counter--
    }
    
    },
    })
    </script>

    此处 v-on:click 可以用语法糖 @click 来替代。

    4. 关于插值的操作

    4.1 mustache 语法

    <h2>{{message}}, 李银河!</h2>
    <h2>{{firstName + lastName}}</h2>

    mustache语法中不仅可以写变量,亦可以写简单的表达式。

    4.2 v-once 指令

    表示页面的元素和组件只渲染一次,不会随着数据的改变而改变。

    <h2 v-once>{{message}}</h2>

    v-for 指令
    表示循环元素或组件中的内容,后接表达式。

    <ul>
    <li v-for="item in movies">{{item}}</li>
    </ul>

    v-html 指令
    表示有选择性地解析html中的内容

    <h2 v-html='url'>{{url}}</h2>

    v-text 指令

    <h2 v-text='message'>,栗子</h2>

    表示显示指定的数据,但是很不灵活,会直接覆盖原标签内的数据,而且不支持拼接等操作。
    该指令用得不多

    v-pre 指令

    <h2 v-pre>{{message}}</h2>

    表示原封不动地显示标签内的内容,而不会对其进行解析。
    该指令用得不多

    v-cloak 指令
    一般用来消除抖动

    <style>
    [v-cloak] {
    display: none
    }
    </style>
    
    <div id="app">
    <h2 v-cloak>{{message}}</h2>
    </div>
    <script src="../js/vue.js"></script>
    <script>
    // 由于 v-cloak 在被解析之后会被删除,所以
    // 在vue解析之前,div中有v-cloak属性
    // 在vue解析之后,div中没有v-cloak
    setTimeout(function () {
    const app = new Vue({
    el: '#app',
    data: {
    message: '你好'
    }
    })
    }, 1000)
    </script>

    5. v-bind 数据绑定

    5.1 v-bind 的基本使用

    绑定属性

    <img v-bind:src='imgURL' alt="谷歌">
    <a v-bind:href="ahref">百度一下</a>

    v-bind 语法糖

    <a :href="ahref">百度一下</a>

    实际上是省略了v-bind指令。

    5.2 v-bind 绑定到class有两种方式 对象语法和数组语法**

    用法一 可以直接通过{}绑定一个类
    用法二 可以通过判断传入多个值
    用法三 和普通的类同时存在,并不冲突

    v-bind 绑定到class(对象语法)
    此案例添加了一个按钮,并设置了一个监听事件,使得点击按钮时改变绑定到

    <h2>
    的class中的布尔值,从而实现对样式的改变。

    <div id='app'>
    <h2 v-bind:class='{active: isActive, line: isLine}'>{{message}}</h2>
    <button v-on:click='btnClick'>按钮</button>
    </div>
    <script src="../js/vue.js"></script>
    <script>
    const app = new Vue({
    el: '#app',
    data: {
    message: '你好',
    isActive: true,
    isLine: false,
    },
    methods: {
    btnClick: function () {
    this.isActive = !this.isActive
    }
    }
    })

    v-bind 绑定到class(数组语法)

    <div id='app'>
    <h2 class="title" :class="[active, line]">{{message}}</h2>
    <h2 class="title" :class="getClasses()">{{message}}</h2>
    </div>
    <script src="../js/vue.js"></script>
    <script>
    const app = new Vue({
    el: '#app',
    data: {
    message: 'hello',
    active: 'one',
    line: 'two'
    },
    methods: {
    getClasses: function () {
    return [this.active, this.line]
    }
    }
    })
    </script>

    v-bind 动态绑定style(对象语法)

    原理大致相同,引号后面改成style即可。

    <div id='app'>
    <!-- <h2 v-bind:style='{属性名:属性值}'>{{message}}</h2> -->
    <!-- <h2 v-bind:style='{fontSize: theSize + "px", color: theColor}'>{{message}}</h2> -->
    <h2 v-bind:style='getStyles()'>{{message}}</h2>
    </div>
    <script src="../js/vue.js"></script>
    <script>
    const app = new Vue({
    el: '#app',
    data: {
    message: 'hello',
    theSize: 100,
    theColor: 'red',
    },
    methods: {
    getStyles: function () {
    return { fontSize: this.theSize + "px", color: this.theColor }
    },
    }
    })
    </script>

    v-bind 动态绑定style(数组语法)

    <div id="app">
    <h2 :style='[baseStyle, baseStyle1]'>{{message}}</h2>
    </div>
    <script src='../js/vue.js'></script>
    <script>
    const app = new Vue({
    el: '#app',
    data: {
    message: 'hello',
    baseStyle: { backgroundColor: 'red' },
    baseStyle1: { fontSize: '88px' }
    }
    })
    </script>

    6. 计算属性

    特点:html代码里面非常清晰

    <h2>{{fullMessage}}</h2>
    </div>
    <script src="../js/vue.js"></script>
    <script>
    const app = new Vue({
    el: '#app',
    data: {
    message: 'asd',
    message2: 'dd',
    },
    computed: {
    fullMessage() {
    return this.message + ' ' + this.message2
    }
    },
    methods: {
    getFullMessage() {
    return this.message + ' ' + this.message2
    }
    }
    })
    </script>

    6.1 复杂操作

    <script>
    const app = new Vue({
    el: '#app',
    data: {
    books: [
    { id: 110, name: 'Unix程艺术', price: 119 },
    { id: 111, name: 'Unx编程艺术', price: 1192 },
    { id: 112, name: 'Uix编程艺术', price: 1191 },
    { id: 113, name: 'Uni编程艺术', price: 11945 },
    ]
    },
    methods: {},
    computed: {
    totalPrice: function () {
    let result = 0
    // for (let i = 0; i < this.books.length; i++) {
    //   result += this.books[i].price
    // }
    for (let book of this.books) {
    result += book.price
    }
    return result
    }
    }
    });
    </script>

    7. es6语法补充

    <script>
    // 属性的增强写法
    
    const name = 'chen'
    const age = 18
    const obj = {
    name: name,
    age: age
    }
    //以上是es5的写法
    // es60的写法如下
    const obj = {
    name, age
    }
    
    // 函数的增强写法
    //es5中的
    const obj = {
    run: function () {
    
    },
    eat: function () {
    
    },
    }
    //es6中的
    const obj = {
    run() {
    
    },
    eat() {
    
    },
    }
    </script>

    var没有块级作用域,所以用let
    const指定对象之后不可更改,但是const内部可以进行更改

    7.1 可变参数

    function sum(...num) {
    console.log(num)
    }
    sum(1,2,3,4,5)

    7.2 for of 的使用**

    var arr = ['nick','freddy','mike','james'];
    for(let item of arr){
    console.log(item);
    }

    数组对象都支持,非常的简洁!

    7.3 高阶函数 filter/map/reduce

    7.3.1.filter函数的使用

    filter中的回调函数有一个要求:必须返回一个boolean值
    true:当返回true时,函数内部会自动将这次回调的n加入到新的数组中
    false:当返回false时,函数内部会过滤掉这次的n

    const nums = [10, 230, 22, 55, 11]
    let newNums = nums.filter(function(n) {
    return n < 100
    })
    7.3.2.map函数的使用
    let new2Nums = newNums.map(function(n) {
    return n * 2
    })
    7.3.3.reduce函数的使用

    作用:对数组中的所有内容进行汇总。

    new2Nums.reduce(function(preValue, n) {
    return preValue + n
    }, 0)
    4.高阶函数链式编程
    const nums = [1,2,3,55,66,22,66,33]
    let total = nums.filter(function(n) {
    return n < 100
    }).map(function (n) {
    return n * 2
    }).reduce(function (prevValue, n) {
    return prevValue + n
    }, 0)
    
    // 箭头函数
    let total2 = num.filter(n => n < 100).map(n => n * 2).reduce((pre, n) => pre + n)

    8. 事件监听

    8.1 v-on的基本使用

    <div id='app'>
    <h2>{{message}}</h2>
    <h2>{{counter}}</h2>
    <!-- <button v-on:click="increment">+</button>
    <button v-on:click="decrement">-</button> -->
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
    
    </div>
    <script src='../js/vue.js'></script>
    <script>
    const app = new Vue({
    el: '#app',
    data: {
    message: 'Hello, world!',
    counter: 0
    },
    methods: {
    increment() {
    return this.counter++
    },
    decrement() {
    return this.counter--
    },
    },
    computed: {
    
    },
    })
    </script>

    8.2 v-on的参数问题

    <div id='app'>
    <!-- 事件调用的方法没有参数 -->
    <button @click="btnClick()">按钮1</button>
    <!-- 在事件定义时,写方法时省略了小括号,但是方法本身是需要一个参数的,
    这个时候,vue会默认将浏览器生成的event对象作为参数传入到方法。 -->
    <button @click="btn2Click">按钮2</button>
    <!-- 方法定义时,我们需要event对象,同时又需要其他参数 -->
    
    <!-- 在调用方法时,如何手动获取到浏览器参数的event对象:$event -->
    <button @click="btn3Click(123, $event)">按钮3</button>
    <button>按钮4</button>
    </div>
    <script src='../js/vue.js'></script>
    <script>
    const app = new Vue({
    el: '#app',
    data: {
    event: 'chen'
    },
    methods: {
    btnClick() {
    console.log('hello world!')
    },
    btn2Click(item) {
    console.log('====', item)
    },
    btn3Click(item, event) {
    console.log('+++++', item, event);
    }
    }
    })
    </script>

    8.3 v-on修饰符

    <div @click='divClick'>
    点这里
    <button @click.stop="btnClick">按钮1</button>
    </div>
    <br>
    <div>
    <!-- 2.prevent 修饰符使用,阻止默认事件 -->
    <form action='baidu'>
    <input type='submit' value="提交" @click.prevent='submitClick'>
    </form>
    
    <!-- 3.监听某个键盘案件的点击,加.enter 则监听回车 -->
    <input type='text' @keyup='keyUp'>
    
    <!-- 4.once修饰符的使用  只触发一次回调-->
    <button @click.once="btn2Click">按钮2</button>
    </div>

    9. 条件判断

    9.1 v-if 和 v-else的使用

    <div id='app'>
    <h2 v-if='score>=90'>优秀</h2>
    <h1 v-else-if='score>=80'>良好</h1>
    <h1 v-else-if='score>=60'>及格</h1>
    <h1 v-else>不及格</h1>
    </div>

    指令判断,不建议写到标签里,建议写计算属性

    9.2 登陆切换的一个小案例

    <div id='app'>
    <span v-if='isUser'>
    <label for="username">用户账户</label>
    <input type="text" id="username" placeholder="用户账户">
    </span>
    <span v-else=''>
    <label for="useremail">用户邮箱</label>
    <input type="text" id="useremail" placeholder="用户邮箱">
    </span>
    <button @click='isUser=!isUser'>切换类型</button>
    </div>

    如果不希望输入框内容复用。可以加入一个key属性

    <span v-if='isUser'>
    <label for="username">用户账户</label>
    <input type="text" id="username" placeholder="用户账户" key='username'>
    </span>
    <span v-else=''>
    <label for="useremail">用户邮箱</label>
    <input type="text" id="useremail" placeholder="用户邮箱" key='useremail'>
    </span>

    v-show的使用

    <div id='app'>
    <h2 v-if='isShow' id='aaa'>{{message}}</h2>
    <h2 v-show='isShow' id='bbb'>{{message}}</h2>
    </div>

    v-if中,条件为false时,包含v-if指令的元素,根本不会出现在dom中。
    v-show中,当条件为false时,v-show只是给元素增加了一个行内样式 display:none。
    如何选择:频繁切换时用show,否则用if。

    10. 循环遍历

    10.1 遍历数组

    <div id='app'>
    <!-- 1.在遍历的过程中,没有使用索引值 -->
    <h2>{{message}}</h2>
    <ul>
    <li v-for='item in names'>{{item}}</li>
    </ul>
    <!-- 2.在遍历的过程中,获取索引值。 -->
    <ul>
    <li v-for='(item, index) in names'>{{index+1}}.{{item}}</li>
    </ul>
    </div>

    10.2遍历对象

    <div id='app'>
    <!-- 1.在遍历对象的过程中,如果只是获取一个值,那么获取到的是value -->
    <ul>
    <li v-for='item in info'>{{item}}</li>
    </ul>
    <!-- 2.获取key和value 格式(value, key)-->
    <ul>
    <li v-for='(value, key) in info'>{{key}}-{{value}}</li>
    </ul>
    <!-- 3.获取key和value和index 格式(value, key index) -->
    <ul>
    <li v-for='(value, key, index) in info'>{{key}}-{{value}}-{{index}}</li>
    </ul>
    </div>

    10.2 v-for使用过程绑定key

    <ul>
    <li v-for='item in letters' v-bind:key='item'>{{item}}</li>
    </ul>

    添加唯一标志key进行绑定后,中间插入效率高。

    10.3哪些数组的方法是响应式的

    push(), pop(), shift(), unshift(), splice(), sort(), reverse()
    methods: {
    btnClick() {
    // 1.push方法。改变的时候是响应式的
    this.letters.push(this.mark)
    this.mark += 1
    
    // 2.pop(),响应式,删除数组中最后一个元素。
    this.letters.pop();
    
    // 3.shift(),响应式,删除数组中的第一个元素。
    this.letters.shift();
    
    // 4.unshift(), 响应式,在数组开头添加元素
    this.letters.unshift('thing', 'asd', 'ddd')
    
    5.splice()
    // 可以删除、插入、替换元素
    // 删除元素: 第二个参数传入你要删除几个元素(如果不传,则删除后面所有的元素)
    // 替换元素: 第二个参数表示我们要替换几个元素,后面接用于替换的元素
    // 插入元素:第二个参数,传入0,后面跟上要插入的元素。
    // splice(start, ), 响应式
    this.letters.splice()
    
    // 6.sort() 响应式,排序
    this.letters.sort()
    
    // 7.reverse() 响应式,反转
    this.letters.reverse()
    
    // 0.通过索引值修改数组中的元素,不是响应式
    this.letters[0] = 'haa'},

    通过索引值修改数组中的元素,不是响应式

    this.letters[0] = 'haa'

    Vue中定义的响应式修改方式:

    Vue.set(this.letters, 0, 'bbbbbb')
    10.3.1 一个点击变色小作业的实现
    <ul>
    <li v-for='(item, index) in movies' v-bind:key='item' v-bind:class='{active: currentIndexL === index}'
    @click='liClick(index)'>{{item}}
    </li>
    </ul>
    </div>
    <script src='../js/vue.js'></script>
    <script>
    const app = new Vue({
    el: '#app',
    data: {
    message: 'Hello world!',
    movies: ['海王', '肖申克的救赎', '海上钢琴师', '复仇者联盟'],
    currentIndex: 0,
    },
    methods: {
    liClick(index) {
    this.currentIndex = index
    }
    }
    })
    </script>

    解决此作业的精髓在于:在列表处循环时讲index 分离出来,并在点击事件中调用liClick(index)方法时把index传回去,并在data里面定义一个currentIndex来记录状态,作为动态绑定样式的依据,非常精妙!妙啊!

    11. 书籍购物车案例

    11.1过滤器的实现

    <td>{{item.price | getFinalPrice}}</td>
    filters: {
    getFinalPrice (price) {
    return '$' + price.toFixed(2)
    }

    11.2计算总价格的四种循环实现方法

    computed: {
    totalPrice () {
    let totalPrice = 0
    // 1.普通的for循环
    // for (let i = 0; i < this.books.length; i++) {
    //   totalPrice += this.books[i].price * this.books[i].count
    // }
    // return totalPrice
    
    // 2.for (let i in this.books)
    // for (let i in this.books) {
    //   totalPrice += this.books[i].price * this.books[i].count
    // }
    // return totalPrice
    
    // 3. for的
    // for (let item of this.books) {
    // totalPrice += item.price * item.count
    // }
    //return totalPrice
    //}
    //},
    // 4.reduce实现
    return this.books.reduce((pre, book) => pre + book.price * book.count)
    }
    },

    11.3 v-if/else的实现

    <div id='app'>
    <div v-if='books.length'>
    <table>
    <thead>
    <tr>
    <th></th>
    <th>书籍名称</th>
    <th>出版日期</th>
    <th>价格</th>
    <th>购买数量</th>
    <th>操作</th>
    </tr>
    </thead>
    <tbody>
    <tr v-for='(item, index) in books'>
    <td>{{item.id}}</td>
    <td>{{item.name}}</td>
    <td>{{item.date}}</td>
    <!-- <td>{{getFinalPrice(item.price)}}</td> -->
    <td>{{item.price | getFinalPrice}}</td><td>
    <button @click='decrement(index)' v-bind:disabled='item.count <= 1'>-</button>
    {{item.count}}
    <button @click='increment(index)'>+</button>
    </td>
    <td>
    <button @click='removeHandle(index)'>移除</button>
    </td>
    </tr>
    </tbody>
    </table>
    <h2>总价格 {{totalPrice | getFinalPrice}}</h2>
    </div>
    <h2 v-else>购物车为空</h2>
    </div>
    

    12. v-model使用

    12.1 v-model基本使用

    <div id='app'>
    <input type="text" v-model='message'>
    <h2>{{message}}</h2>
    </div>

    12.2 v-model原理

    v-model其实是一个语法糖,它的背后本质上时包含两个操作:
    1.v-bind绑定一个value属性.
    2.v-on指令给当前元素绑定input事件.

    <input type="text" v-bind:value='message' v-on:input='message = $event.target.value'>

    12.3 v-model结合radio类型

    <div id='app'>
    <h2>您选择的性别是:{{sex}}</h2>
    <label for='male'>
    <input type="radio" id='male' name='gender' value="男zxc" v-model='sex'>男
    </label>
    <label for='female'>
    <input type="radio" id='female' name='gender' value="女" v-model='sex'>女
    </label>
    </div>

    12.4 v-model结合checkbox单选、多选框使用

    单选框对应布尔值,多选框则对应数组类型。

    <div id='app'>
    <!-- checkbox单选框 -->
    <!-- <label for="license">
    <input type="checkbox" id='license' v-model='isAgree'>同意协议
    </label> -->
    <!-- <h2>您选择的是{{isAgree}}</h2>
    <button v-bind:disabled='!isAgree'>下一步</button> -->
    
    <!-- checkbox复选框 -->
    <label for="hobby1">
    <input type="checkbox" value="唱" id='hobby1' v-model='hobbies'>唱
    </label>
    <label for="hobby2">
    <input type="checkbox" value="跳" id='hobby2' v-model='hobbies'>跳
    </label>
    <label for="hobby3">
    <input type="checkbox" value="Rap" id='hobby3' v-model='hobbies'>Rap
    </label>
    <label for="hobby4">
    <input type="checkbox" value="篮球" id='hobby4' v-model='hobbies'>篮球
    </label>
    <h2>您的爱好是: {{hobbies}}</h2>
    </div>

    12.5 v-model结合checkbox进行值绑定

    值不要写死,去动态获取

    <label v-for='item in originHobbies' v-bind:for='item'>
    <input type="checkbox" v-bind:value="item" v-bind:id='item' v-model='hobbies'>{{item}}
    </label>
    <h2>您的爱好是: {{hobbies}}</h2>
    </div>
    <script src="../js/vue.js"></script>
    <script>
    const app = new Vue({
    el: '#app',
    data: {
    message: 'Hello world!',
    isAgree: false,
    hobbies: [],
    originHobbies: ['唱', '跳', 'rap', '篮球']
    }
    })
    </script>

    12.6 v-model结合select

    <div id='app'>
    <select name='name' v-model='fruit'>
    <option value="唱">唱</option>
    <option value="跳">跳</option>
    <option value="rap">rap</option>
    <option value="篮球">篮球</option>
    </select>
    <h2>{{message}} {{fruit}}</h2>
    </div>

    select多选现在用的不多,不做介绍。

    12.7 v-model修饰符的使用

    <div id='app'>
    <!-- 修饰符lazy,添加后不实时更新,失去焦点时再更新 -->
    <input type="text" v-model.lazy='message'>
    <h2>{{message}}</h2>
    <!-- 修饰符number,强制将绑定值更新为number类型 -->
    <input type="text" v-model.number='age'>
    <h2>{{age}}-{{typeof age}}</h2>
    <!-- 修饰符trim,剥除空格 -->
    <input type="text" v-model.trim='name'>
    <h2>您输入的名字:{{name}}</h2>
    </div>

    13. 组件化

    13.1 注册组建的基本步骤

    1. 创建组件构造器
    2. 注册组件
    3. 使用组件
    4. 调用Vue.extend()方法创建组件构造器
    5. 调用Vue.component()方法注册组件
    6. 在Vue实例的作用范围内使用组件
    <script>
    // 1. 创建组件构造器对象
    const cpnC = Vue.extend({
    template: `
    <div>
    <h2>我是蔡徐坤!</h2>
    <p>唱</p>
    <p>跳</p>
    <p>rap</p>
    <p>篮球</p>
    </div>
    `
    })
    // 2.注册组件
    Vue.component('my-cpn', cpnC)
    
    const app = new Vue({
    el: '#app',
    data: {
    message: 'Hello world!'
    }
    })
    </script>

    13.2 注册组件步骤解析

    Vue.extend():

    • 调用Vue.extend()创建的是一个组件构造器。
    • 通常在创建组件构造器时,传入template代表我们自定义的模板。
    • 该模板是在使用到组建的地方,要显示的HTML代码。
    • 事实上,现在多用语法糖来处理。

    13.3 局部组件和全局组件

    注:此为比较老的方法

    <div id='app'>
    <h2>{{message}}</h2>
    <!-- 3. 使用组件 -->
    <my-cpn></my-cpn>
    <cpn></cpn>
    </div>
    <div id='app2'>
    <my-cpn></my-cpn>
    <cpn></cpn>
    </div>
    <script src="../js/vue.js"></script>
    <script>
    // 1. 创建组件构造器对象
    const cpnC = Vue.extend({
    template: `
    <div>
    <h2>呵呵</h2>
    </div>
    `
    })
    // 2.注册组件(全局组件,意味着可以在多个Vue的实例下面使用)
    Vue.component('my-cpn', cpnC)
    
    // 疑问:怎么注册的组件才是局部组件?
    const app = new Vue({
    el: '#app',
    data: {
    message: 'Hello world!'
    },
    components: {
    // cpn-使用组件时的标签名
    cpn: cpnC
    }
    })
    
    const app2 = new Vue({
    el: '#app2',
    })
    
    </script>

    13.4 父组件和子组件

    这里像套娃一样,可以再父组件的构造器中注册子组件,之后可以在父组件的template中调用子组件的template。
    但是至少需要在根组件中注册父组件、而且子组件在未被根组件注册时并不能使用其中的模板!

    <script>
    // 构建子组件构造器?
    const cpn1 = Vue.extend({
    template: `
    <div>
    <p>呵呵</p>
    </div>
    `
    })
    // 构建父组件构造器
    const cpn2 = Vue.extend({
    template: `
    <div>
    <p>吼吼</p>
    <cpn1></cpn1>
    </div>`,
    components: {
    cpn1,
    }
    })
    const app = new Vue({
    el: '#app',
    data: {
    message: 'Hello world!'
    },
    components: {
    cpn2,
    cpn1
    }
    })
    </script>

    13.5 组件的语法糖注册方式

    <script>
    // 语法糖注册全局组件
    Vue.component('cpn1', {
    template: `
    <div>
    <h2>mdzz</h2>
    </div>
    `
    })
    const app = new Vue({
    el: '#app',
    data: {
    message: 'Hello world!',
    },
    // 语法糖注册局部组件
    components: {
    'cpn2': {
    template: `
    <div>
    <h2>mdzzzzzzzzzzzzzz</h2>
    </div>
    `
    }
    }
    })
    </script>

    13.6 模板的抽离写法

    <body>
    <div id='app'>
    <cpn></cpn>
    </div>
    <template id='cpn2'>
    <div>
    <h2>fuck</h2>
    </div>
    </template>
    
    <script src="../js/vue.js"></script>
    <script>
    Vue.component('cpn', {
    template: '#cpn2'
    })
    const app = new Vue({
    el: '#app',
    })
    </script>
    </body>

    13.7 组件中的数据存放问题

    <body>
    <div id='app'>
    <cpn></cpn>
    <cpn></cpn>
    <cpn></cpn>
    </div>
    <template id='cpn'>
    <h2>asd</h2>
    <h2>{{title}}</h2>
    <p>fuck</p>
    </template>
    <script src="../js/vue.js"></script>
    <script>
    Vue.component('cpn', {
    template: '#cpn',
    data() {
    return {
    title: 'the title'
    }
    }
    })
    const app = new Vue({
    el: '#app'
    })
    
    </script>
    </body>

    13.8 组件中的data为什么是函数

    因为函数有各自的作用域和地址,所以设置成函数能避免组件之间相互影响。

    <body>
    <div id='app'>
    <h2>{{message}}</h2>
    <cpn></cpn>
    </div>
    <template id='cpn'>
    <div>
    <h2>当前计数: {{counter}}</h2>
    <button @click='decrement'>-</button>
    <button @click='increment'>+</button>
    </div>
    </template>
    <script src="../js/vue.js"></script>
    <script>
    Vue.component('cpn', {
    template: '#cpn',
    data() {
    return { counter: 0 }
    },
    methods: {
    increment() {
    this.counter++
    },
    decrement() {
    this.counter--
    }
    }
    })
    const app = new Vue({
    el: '#app',
    data: {
    message: 'Hello world!',
    }
    })
    </script>
    </body>

    13.9 父组件向子组件添加数据

    <div id='app'>
    <!-- <h2>{{message}}</h2>
    <cpn v-bind:cmovies='movies' :cmessages='message'></cpn> -->
    <cpn :cmessages='message' :cmovies='movies'></cpn>
    </div>
    <template id='cpn'>
    <div>
    <ul>
    <li v-for='item in cmovies'>{{item}}</li>
    </ul>
    <h2>{{cmessages}}</h2>
    </div>
    </template>
    <script src="../js/vue.js"></script>
    <script>
    // 父传子,props
    const cpn = {
    template: '#cpn',
    // props: ['cmovies', 'cmessages'],
    
    props: {
    // # 类型限制传props
    //   cmovies: Array,
    //   cmessages: String,
    // },
    // 提供默认值以及必传值
    cmessages: {
    type: String,
    default: 'aaaaa',
    required: true,
    },
    // 类型是对象或者数组时,默认值必须是一个函数
    cmovies: {
    type: Array,
    default() {
    return []
    }
    }
    },
    data() {
    return {
    
    }
    },
    methods: {
    }
    }
    const app = new Vue({
    el: '#app',
    data: {
    message: 'Hello!',
    movies: ['海王', '海贼王', '海尔兄弟']
    },
    components: {
    cpn,
    }
    })
    </script>

    13.10 props中的驼峰标识

    在父组件引用子组件的模板中使用v-bind绑定时,不能用驼峰标识,要将其转换为’-'连接的形式。

    <body>
    <div id='app'>
    <cpn :c-info='info'></cpn>
    </div>
    <template id='cpn'>
    <div>
    <h2>{{cInfo}}</h2>
    </div>
    </template>
    <script src="../js/vue.js"></script>
    <script>
    
    const cpn = {
    template: '#cpn',
    props: {
    cInfo: {
    type: Object,
    dafault: { ss: 'ss' },
    required: true,
    },
    },
    data() {
    return {}
    },
    methods: {
    
    },
    }
    
    const app = new Vue({
    el: '#app',
    data: {
    message: 'Hello world!',
    info: {
    name: 'chen',
    age: 23,
    color: 'black'
    },
    },
    methods: {
    
    },
    components: {
    cpn
    }
    
    })
    </script>
    </body>

    13.11 组件通信-子传父(自定义事件)

    子组件自定义事件、并发射事件,由父组件来监听、接收、处理、显示。

    • 在子组件中,通过$emit()来触发事件
    • 在父组件中,通过v-on来监听子组件事件
    <body>
    <!-- 父组件模板 -->
    <div id='app'>
    <cpn @item-clicked='cpnClick'></cpn>
    </div>
    <!-- 子组件模板 -->
    <template id='cpn'>
    <div>
    <button v-for='item in categroies' @click='btnClick(item)'>{{item.name}}</button>
    </div>
    </template>
    <script src='../js/vue.js'></script>
    <script>
    // 这是子组件
    const cpn = {
    template: '#cpn',
    data() {
    return {
    categroies: [
    { id: '1', name: '唱' },
    { id: '2', name: '跳' },
    { id: '3', name: 'rap' },
    { id: '4', name: '篮球' },
    ],
    }
    },
    methods: {
    // 自定义事件,发射自定义事件!
    btnClick(item) {
    this.$emit('item-clicked', item)
    },
    }
    }
    // 这是父组件
    const app = new Vue({
    el: '#app',
    data: {
    message: 'Hello world!'
    },
    methods: {
    cpnClick(item) {
    console.log('secceed!!', item);
    }
    },
    // 这是注册子组件
    components: {
    cpn,
    }
    })
    </script>
    </body>

    13.12 父子组件通信案例

    <body>
    <div id='app'>
    <h2>{{message}}</h2>
    <cpn v-bind:number1='num1' v-bind:number2='num2' @num1change='num1change' @num2change='num2change'></cpn>
    </div>
    <template id='cpn'>
    <div>
    <h2>props:{{number1}}</h2>
    <h2>data:{{dnumber1}}</h2>
    <!-- <input type='text' v-model='dnumber1'> -->
    <input type="text" v-bind:value='dnumber1' @input='num1Input'>
    <h2>{{number2}}</h2>
    <h2>data:{{dnumber2}}</h2>
    <!-- <input type='text' v-model='dnumber2'> -->
    <input type="text" v-bind:value='dnumber2' @input='num2Input'>
    </div>
    </template>
    <script src="../js/vue.js"></script>
    <script>
    const app = new Vue({
    el: '#app',
    data: {
    message: 'Hello world!',
    num1: 1,
    num2: 0,
    },
    methods: {
    num1change(value) {
    this.num1 = parseFloat(value)
    },
    num2change(value) {
    this.num2 = parseFloat(value);
    }
    },
    components: {
    cpn: {
    template: '#cpn',
    props: {
    number1: Number,
    number2: Number
    },
    data() {
    return {
    dnumber1: this.number1,
    dnumber2: this.number2
    }
    },
    methods: {
    num1Input(event) {
    this.dnumber1 = event.target.value
    this.$emit('num1change', this.dnumber1)
    },
    num2Input(event) {
    this.dnumber2 = event.target.value
    this.$emit('num2change', this.dnumber2)
    }
    }
    }
    }
    })
    </script>
    </body>

    13.13 组件访问-父访问子-children-refs

    • refs用得很多,children基本不用
    <body>
    <div id='app'>
    <h2>{{message}}</h2>
    <cpn></cpn>
    <cpn></cpn>
    <cpn ref='mark'></cpn>
    <button @click="btnClick">按钮</button>
    </div>
    <template id='cpn'>
    
    </template>
    <script src="../js/vue.js"></script>
    <script>
    
    const cpn = {
    template: '#cpn',
    data() {
    return {
    name: '我是子组件的name'
    }
    },
    methods: {
    show() {
    console.log('show!!!!');
    }
    }
    }
    const app = new Vue({
    el: '#app',
    data: {
    message: 'Hello World!'
    },
    components: {
    cpn,
    },
    methods: {
    btnClick() {
    // 1. $children
    // console.log(this.$children)
    // for (let item of this.$children) {
    //   console.log(item.name)
    //   item.show()
    // 2. $refs => 对象类型,默认是一个空的对象 ref='item'
    console.log(this.$refs.mark.name)
    }
    }
    })
    </script>
    </body>

    13.14 组件访问-子访问父-parent-root

    • root用得很多,parent基本不用
    <body>
    <div id='app'>
    <h2>{{message}}</h2>
    <cpn></cpn>
    </div>
    <template id='cpn'>
    <div>
    <h2>我是子组件</h2>
    <button @click="btnClick">按钮</button>
    <div>
    </template>
    <script src="../js/vue.js"></script>
    <script>
    const cpn = {
    template: '#cpn',
    data() {
    return {}
    },
    methods: {
    btnClick() {
    // 1.访问父组件 $parent
    // console.log(this.$parent)
    
    // 2.访问根组件 $root
    console.log(this.$root)
    console.log(this.$root.message)
    }
    },
    }
    const app = new Vue({
    el: '#app',
    data: {
    message: 'Hello world!'
    },
    methods: {
    
    },
    components: {
    cpn,
    }
    })
    </script>
    </body>

    14. 组件化高级

    14.1 slot插槽的基本使用

    <body>
    <div id='app'>
    <h2>{{message}}</h2>
    <cpn></cpn>
    <cpn><button>按钮</button></cpn>
    <cpn><span>这是span</span></cpn>
    </div>
    <template id='cpn'>
    <!--
    1.插槽的基本使用 <slot></slot>
    2.插槽的默认值<slot>button</slot>
    3.如果有多个值,同时放入到组件中进行替换时,一起作为替换元素
    -->
    <div>
    <h2>插槽用法测试</h2>
    <p>我是组件</p>
    <slot></slot>
    <!-- 添加默认值 -->
    <!-- <v-slot><button>按钮!</button>按钮!</v-slot> -->
    </div>
    </template>
    
    <script src="../js/vue.js"></script>
    <script>
    const cpn = {
    template: '#cpn',
    data() {
    return {}
    },
    }
    const app = new Vue({
    el: '#app',
    data: {
    message: 'Hello World!',
    },
    components: {
    cpn
    }
    })
    </script>
    </body>

    14.2 具名插槽的使用(v-slot)

    • 新的v-slot用法代替了slot用法,语法和写的位置也都有变化
    <body>
    <div id='app'>
    <h2>{{message}}</h2>
    <cpn></cpn>
    <cpn v-slot:center><span>标题</span></cpn>
    <cpn><button slot='right'>按钮</button></cpn>
    </div>
    <template id='cpn'>
    <div>
    <p>这是模板</p>
    <slot name='left'><span>左</span></slot>
    <slot name='center'><span>中</span></slot>
    <slot name='right'><span>右</span></slot>
    </div>
    </template>
    <script src="../js/vue.js"></script>
    <script>
    const cpn = {
    template: '#cpn',
    
    }
    const app = new Vue({
    el: '#app',
    data: {
    message: 'Hello world!'
    },
    components: {
    cpn
    }
    })
    </script>
    </body>

    14.3 什么是编译的作用域

    • 在哪个模板,那么其中的任何变量、数据等等都属于模板本身内部的作用域。
    • 在哪个组件,就用哪个组建的数据。
    <body>
    <div id='app'>
    <h2>{{message}}<
    4000
    ;/h2>
    <cpn v-show='isShow'></cpn>
    </div>
    
    <template id='cpn'>
    <div>
    <h2>这是id为cpn的模板</h2>
    <p v-show='isShow'>你猜这行会不会显示?</p>
    </div>
    </template>
    <script src="../js/vue.js"></script>
    <script>
    const cpn = {
    template: '#cpn',
    data() {
    return {
    name: 'chen!',
    isShow: false
    }
    }
    }
    const app = new Vue({
    el: '#app',
    data: {
    message: 'Hello world!',
    isShow: true
    },
    components: {
    cpn
    }
    })
    </script>
    </body>

    14.4 作用域插槽

    父组件替换插槽的标签,但是内容由子组件来提供。
    已废弃,用v-slot

    15 前端模块化

    15.1 es6 模块化实现

    导出方式一 :

    export {
    flag, sum
    }

    导出方式二:

    export let num1 = 100

    导出函数\类:

    export function add(num1, num2) {
    return num1 + num2
    }
    export class Person {
    run() {
    console.log("在奔跑")
    }
    }

    导入export default 中的内容

    const address = '北京市'
    export default address
    // export default 只能有一个

    统一全部导入

    import * as all from './'

    16. webpack的一些配置

    // prod.config.js
    const UglifyjswebpackPlugin = require('uglifyjs-webpack-plugin')
    const webpackMerge = require('webpack-merge')
    const baseConfig = require('./base.config')
    
    module.exports = webpackMerge(baseConfig, {
    plugins: [
    new UglifyjswebpackPlugin()
    ],
    })
    //dev.config.js
    const webpackMerge = require('webpack-merge')
    const baseConfig = require('./base.config')
    
    module.exports = webpackMerge(baseConfig, {
    devServer: {
    contentBase: './dist',
    inline: true,
    },
    })
    //base.config.js
    const path = require('path');
    const VueLoaderPlugin = require('vue-loader/lib/plugin')
    const webpack = require('webpack')
    const HtmlwebpackPlugin = require('html-webpack-plugin')
    const UglifyjswebpackPlugin = require('uglifyjs-webpack-plugin')
    
    module.exports = {
    entry: './src/main.js',
    output: {
    path: path.resolve(__dirname, '../dist'),
    filename: 'bundle.js',
    // publicPath: '../dist/',
    },
    module: {
    rules: [
    {
    test: /\.css$/,
    use: ['style-loader', 'css-loader']
    },
    {
    test: /\.less$/,
    use: [{
    loader: "style-loader" // creates style nodes from JS strings
    }, {
    loader: "css-loader" // translates CSS into CommonJS
    }, {
    loader: "less-loader" // compiles Less to CSS
    }]
    },
    {
    test: /\.(png|jpg|gif)$/,
    use: [
    {
    loader: 'url-loader',
    options: {
    
    20000
    limit: 1300,
    name: 'img/[name].[hash:8].[ext]'
    },
    }
    ]
    },
    {
    test: /\.js$/,
    exclude: /(node_modules|bower_components)/,
    use: {
    loader: 'babel-loader',
    options: {
    presets: ['es2015']
    }
    }
    },
    {
    test: /\.vue$/,
    use: { loader: 'vue-loader' }
    }
    ]
    },
    plugins: [
    // 请确保引入这个插件!
    new VueLoaderPlugin(),
    new webpack.BannerPlugin('这是我的'),
    new HtmlwebpackPlugin({
    template: './src/index.html'
    }),
    new UglifyjswebpackPlugin()
    ],
    devServer: {
    contentBase: './dist',
    inline: true,
    },
    resolve: {
    alias: {
    'vue$': 'vue/dist/vue.esm.js',
    }
    }
    }

    入口文件

    import name from './js/info'
    // 使用es6的模块化规范
    const { add, mul } = require('./js/mathUtils')
    // 使用commonjs的模块化规范
    console.log(add(20, 30))
    console.log(mul(20, 30))
    console.log(name)
    // 依赖css文件
    require('./css/normal.css')
    // 依赖less文件
    require('./css/special.less')
    document.writeln('<h2>Hello</h2>')
    
    // 使用Vue进行开发
    import Vue from 'vue'
    import App from './vue/App.vue'
    
    new Vue({
    el: '#app',
    template: '<App/>',
    components: {
    App
    }
    })

    一些用到的package

    {
    "name": "meetwebpack",
    "version": "1.0.0",
    "description": "practice",
    "main": "index.js",
    "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --config ./build/prod.config.js",
    "dev": "webpack-dev-server --open --config ./build/dev.config.js"
    },
    "author": "",
    "license": "ISC",
    "dependencies": {
    "babel-core": "^6.26.3",
    "babel-loader": "^7.1.5",
    "babel-preset-es2015": "^6.24.1",
    "css-loader": "^2.0.2",
    "file-loader": "^3.0.1",
    "html-webpack-plugin": "^3.2.0",
    "less": "^3.9.0",
    "less-loader": "^4.1.0",
    "style-loader": "^0.23.1",
    "uglifyjs-webpack-plugin": "^1.1.1",
    "url-loader": "^1.1.2",
    "vue": "^2.5.21",
    "vue-loader": "^15.4.2",
    "vue-template-compiler": "^2.5.21",
    "webpack": "^3.6.0",
    "webpack-dev-server": "^2.9.3",
    "webpack-merge": "^4.1.5"
    }
    }

    17. vue-cli

    vue-cli2 中的项目初始化命令

    vue init webpack project-name

    vue-cli3 中的项目初始化命令

    vue creat project-name

    17.1 runtimecompiler 和 runtimeonly的区别

    runtime-compiler

    template => ast => render => vdom => ui

    runtime-only
    性能更高、代码更少

    render => vdom => ui

    17.2 vue的配置

    vue ui

    17.3 箭头函数补充

    标准格式

    const demo = (item1, item2) => {
    return item1 + item2
    }
    const demo = item => console.log(item)

    当函数中只有一个参数时,可以省略小括号,当函数中只有一行代码时,可以省略return。

    箭头函数中的this:向外层作用域中一层层查找,直到有this的定义。

    18. vue-router

    18.1 url的hash 和 html5 的history

    URL的hash也就是锚点(#),本质上是改变window.location的href属性
    我们可以直接赋值location.hash来改变href,而页面不发生刷新

    location.hash

    history 入栈和出栈

    history.pushState({}, '', 'item')```
    
    replace 不可返回
    ```history.replaceState({}, '', 'item')```
    
    history.go()方法和history.back()\history.forward()等价
    history.go(-1)
    history.go(1)

    18.2 配置路由相关的信息

    import VueRouter from 'vue-router'
    import Vue from 'vue'
    1. 通过Vue.use(插件), 安装插件
    Vue.use(VueRouter)
    1. 创建VueRouter对象
    const routes = [
    ]
    1. 配路由和组件之间的应用关系
    const router = new VueRouter({
    routes
    })
    1. 将router对象传入到 Vue 实例
    new Vue({
    router,
    render: h => h(App)
    }).$mount('#app')

    使用vue-router步骤
    第一步:创建路由组件
    第二步:配置路由映射-组件和路径映射关系
    第三步:使用路由

    <template>
    <div id="app">
    <router-view />
    <div id="nav">
    <router-link to="/home">Home</router-link> |
    <router-link to="/about">About</router-link>
    </div>
    </div>
    </template>

    18.3 router-link的其他属性补充

    <router-link> 中的to属性--指定路径
    tag属性--指定组建的类型(比如说渲染成button)
    <router-link to="/" tag="button" replace>
    replace模式不会留下历史记录,所以不能返回

    18.4 通过代码的方式修改路径

    pushState()方法:

    export default {
    name: 'App',
    methods: {
    btnHome () {
    this.$router.push('/home')
    },
    btnAbout () {
    this.$router.push('/about')
    }
    }
    }

    replace()方法:

    export default {
    name: 'App',
    methods: {
    btnHome () {
    this.$router.replace('/home')
    },
    btnAbout () {
    this.$router.replace('/about')
    }
    }
    }

    解决Vue-cli中进行两次路由切换的坑

    import Vue from 'vue'
    import VueRouter from 'vue-router'
    Vue.use(VueRouter)
    const VueRouterPush = VueRouter.prototype.push
    VueRouter.prototype.push = function push (to) {
    return VueRouterPush.call(this, to).catch(err => err)
    }
    

    以App.vue中的path为主,index.js中的path为配合,其中首字母大小写不做严格限制

    btnHome () {
    this.$router.push('/home')
    {
    path: '/Home',
    name: 'Home',
    component: Home
    }

    像这样也是可以的

    18.5 动态路由

    动态路由的使用(userId)

    {
    path: '/user/:userId',
    name: 'User',
    component: User
    }
    btnUser () {
    this.$router.push('/user' + '/' + this.userId)
    }
    
    <template>
    <div>
    <h2>用户界面</h2>
    <p>用户界面的内容</p>
    <p>你的id是:{{userId}}</p>
    </div>
    </template>
    
    <script>
    export default {
    name: 'User',
    computed: {
    userId () {
    return this.$route.params.userId
    }
    }
    }
    </script>

    18.6 路由的懒加载

    作用:动态加载资源,缩短加载时间,提升用户体验!

    const User = () => import('../views/user.vue')
    或者
    component () => import('./views/Home.vue')

    第一种更清晰!

    18.7 嵌套路由

    {
    path: '/Home',
    name: 'Home',
    component: Home,
    children: [
    {
    path: 'news',
    component: HomeNews
    },
    {
    path: 'message',
    component: HomeMessage
    }
    ]
    }

    然后在home.vue中

    <router-link to="/home/news">新闻</router-link>
    <router-link to="/home/message">消息</router-link>

    18.8 全局导航守卫

    {
    name: 'Profile',
    path: '/Profile',
    component: Profile,
    meta: { title: '档案' }
    }
    前置钩子 从from 跳转到 to
    router.beforeEach((to, from, next) => {
    document.title = to.matched[0].meta.title
    next()
    })

    keep-alive 保持状态,避免被频繁创建与销毁

    <keep-alive>
    <router-view />
    </keep-alive>

    使用keep-alive 之后,activated/deactivated的使用成为可能。

    进行排除的方法:

    <keep-alive exclude="Profile">
    <router-view />
    </keep-alive>

    19. Promise

    什么情况下会用到promise?

    一般情况下是有异步操作时,需要Promise对这个异步操作进行封装
    在执行传入的回调函数时,会传入两个参数,一个叫resolve,一个叫reject,而这两个参数本身又是函数
    new => 构造函数(1.保存了一些状态信息 2.执行传入的函数)
    
    成功时用resolve 失败时候用reject
    
    new Promise((resolve, reject) => {
    setTimeout(() => {
    // resolve('Hello world')
    reject('error')
    }, 1000)
    }).then((data) => {
    console.log(data)
    console.log(data)
    console.log(data)
    }).catch((error) => {
    console.log(error)
    })
    
    或者
    new Promise ((resolve, reject) => {
    setTimeout(() => {
    resove('Hello world!')
    }, 1000)
    }).then(data => {
    console.log(data)
    }, error => {
    console.log(error)
    })

    19.1 Promise 的三种状态

    penging:等待状态,比如正在进行网络请求,或者定时器没有到时间。
    fullfill:满足状态,当主动调用resolve时,就处于该状态,并且会回调.then()
    reject:拒绝状态,当主动调用reject时,就处于该状态,并且会回调.catch()

    20. vuex

    vuex的基本使用

    <template>
    <div id="app">
    <h2>{{message}}</h2>
    <h2>{{$store.state.counter}}</h2>
    <button @click="addition">+</button>
    <button @click="substraction">-</button>
    <hello-vuex></hello-vuex>
    </div>
    </template>
    <script>
    import HelloVuex from './components/HelloVuex.vue'
    export default {
    name: 'App',
    data () {
    return {
    message: '我是app组件',
    }
    },
    components: {
    HelloVuex
    },
    methods: {
    addition () {
    this.$store.commit('increment')
    },
    substraction () {
    this.$store.commit('decrement')
    }
    }
    }
    </script>
    
    <template>
    <div>
    <h2>
    {{$store.state.counter}}
    </h2>
    </div>
    </template>
    
    <script>
    export default {
    name: 'HelloVuex',
    }
    </script>
    Vue.use(Vuex)
    
    export default new Vuex.Store({
    state: {
    counter: 1000
    },
    mutations: {
    increment (state) {
    state.counter++
    },
    decrement (state) {
    state.counter--
    }
    },
    actions: {
    },
    modules: {
    }
    })

    axios的封装

    import axios from 'axios'
    
    export function request (config) {
    // return new Promise((resolve, reject) => {
    const instance = axios.create({
    baseUrl: 'http://123.207.32.32:8000/api/wh/',
    timeout: 5000
    })
    return instance(config)
    //   instance(config)
    //     .then(res => {
    //       resolve(res)
    //     })
    //     .catch(err => {
    //       reject(err)
    //     })
    // })
    }
    
    import { request } from './network/request.js'
    
    request({
    url: '/home/multidata'
    }).then(res => {
    console.log(res)
    }).catch(err => {
    console.log(err)
    })
    讨嫌喵 原创文章 7获赞 4访问量 196 关注 私信
    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: