Vue学习笔记

1.vue基础入门

1.1 vue基础概念

Vue是一个渐进式的框架

  1. 渐进式意味着你可以将Vue作为你应用的一部分嵌套其中
  2. Vue的核心库以及其生态系统
  3. Core + Vue-router + Vuex

1.2安装vue.js

方式一:CDN引入

<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

<!-- 生产环境版本,优化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>


方式二:下载和引入

// 开发环境
https://vuejs.org/js/vue.js

// 生产环境
https://vuejs.org/js/vue.min.js

1.3 MVVM理解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2BcZCUsQ-1649957421207)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20211223182614086.png)]

M: Model 数据模型

  • 数据层:数据可能是固定的思数据, 更多的是来自服务器, 从网络上请求下来的数据

V: View 视图模板

  • 视觉层:写在前端开发中, 通常是DOM层。作用: 是给用户展示各种信息

VM: View-Model 视图模型

  • 视图模型层:是View和Model沟通的桥梁,一方面实现了Data Binding (数据绑定), 讲Model的改变实时的反应到View中,另一方面实现了DOM Listener (DOM监听), 当DOM发生一些时间 (点击, 滚动, touch等) 时, 可以监听到, 并在需要的情况下改变对应的Data

1.4 vue初体验

展示单个字符串:

<div id="app">
    {{ message }}
</div>

<script type="text/javascript">
    let app  = new Vue({
        el:'#app',
        data:{
            message:"hello world"
        }
    })
</script>

展示列表:

<div id="app">
    <ul v-for="item in movies">
        <li>{{item}}</li>
    </ul>
</div>
<script type="text/javascript">
    // 展示列表
    const app = new Vue({
        el: "#app",
        data:{
            movies:["海贼王","寒战","大话西游","海上钢琴师"]
        }
    });


</script>

小案例:计数器


<div id="app">
    <h2>当前计数{{count}}</h2>

<!--
方式1:
<button v-on:click="count++">+</button>
<button v-on:click="count&#45;&#45;">-</button>
-->
<!-- 方式2: -->

 <button v-on:click="incremental">+</button>
 <button v-on:click="subtract">-</button>
</div>

<script type="text/javascript">

    const app = new Vue({
        el:"#app",
        data:{
            count:0
        },
        methods:{
            incremental:function () {
                console.log("递增");
                this.count++;
            },
            subtract:function () {
                console.log("递减");
                this.count--;
            }
        }
    })
</script>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BKnPWQWp-1649957421207)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20211223183056060.png)]

2.Vue基础语法

2.1 options参数

  • el
  • data
  • methods
  • 生命周期函数
  • 等…

2.2 Vue生命周期

每一个vue实例从创建到销毁的过程,就是这个vue实例的生命周期。在这个过程中,他经历了从开始创建、初始化数据、编译模板、挂载Dom、渲染→更新→渲染、卸载等一系列过程。

在这里插入图片描述

  1. new Vue()实例化一个vue实例,然后init初始化event 和 lifecycle, 其实这个过程中分别调用了3个初始化函数(initLifecycle(), initEvents(), initRender()),分别初始化了生命周期,事件以及定义createElement函数,初始化生命周期时,定义了一些属性,比如表示当前状态生命周期状态得_isMounted ,_isDestroyed ,_isBeingDestroyed,表示keep-alive中组件状态的_inactive,而初始化event时,实际上就是定义了 o n c e 、 once、 onceoff、 e m i t 、 emit、 emiton几个函数。而createElement函数是在初始化render时定义的(调用了initRender函数)
  2. 执行 beforeCreate 生命周期函数
  3. beforeCreate执行完后,会开始进行数据初始化,这个过程,会定义data数据,方法以及事件,并且完成数据劫持observe以及给组件实例配置watcher观察者实例。这样,后续当数据发生变化时,才能感知到数据的变化并完成页面的渲染。
  4. 执行created生命周期函数,所以,当这个函数执行的时候,我们已经可以拿到data下的数据以及methods下的方法了,所以在这里,我们可以开始调用方法进行数据请求了
  5. created执行完后,我们可以看到,这里有个判断,判断当前是否有el参数(这里为什么需要判断,是因为我们后面的操作是会依赖这个el的,后面会详细说),如果有,我们再看是否有template参数。如果没有el,那么我们会等待调用$mount(el)方法(后面会详细说)。
  6. 确保有了el后,继续往下走,判断当有template参数时,我们会选择去将template模板转换成render函数(其实在这前面是还有一个判断的,判断当前是否有render函数,如果有的话,则会直接去渲染当前的render函数,如果没有那么我们才开始去查找是否有template模板),如果没有template,那么我们就会直接将获取到的el(也就是我们常见的#app,#app里面可能还会有其他标签)编译成templae, 然后在将这个template转换成render函数。
  7. 之后再调用beforMount, 也就是说实际从creted到beforeMount之间,最主要的工作就是将模板或者el转换为render函数。并且我们可以看出一点,就是你不管是用el,还是用template, 或者是用我们最常用的.vue文件(如果是.vue文件,他其实是会先编译成为template),最终他都是会被转换为render函数的。
  8. beforeMount调用后,我们是不是要开始渲染render函数了,首先我们会先生产一个虚拟dom(用于后续数据发生变化时,新老虚拟dom对比计算),进行保存,然后再开始将render渲染成为真实的dom。渲染成真实dom后,会将渲染出来的真实dom替换掉原来的vm. e l ( 这 一 步 我 们 可 能 不 理 解 , 请 耐 心 往 下 看 , 后 面 我 会 举 例 说 明 ) , 然 后 再 将 替 换 后 的 el(这一步我们可能不理解,请耐心往下看,后面我会举例说明),然后再将替换后的 el,el append到我们的页面内。整个初步流程就算是走完了
  9. 之后再调用mounted,并将标识生命周期的一个属性_isMounted 置为true。所以mounted函数内,我们是可以操作dom的,因为这个时候dom已经渲染完成了。
  10. 再之后,只有当我们状态数据发生变化时,我们在触发beforeUpdate,要开始将我们变化后的数据渲染到页面上了(实际上这里是有个判断的,判断当前的_isMounted是不是为ture并且_isDestroyed是不是为false,也就是说,保证dom已经被挂载的情况下,且当前组件并未被销毁,才会走update流程)
  11. beforeUpdate调用之后,我们又会重新生成一个新的虚拟dom(Vnode),然后会拿这个最新的Vnode和原来的Vnode去做一个diff算,这里就涉及到一系列的计算,算出最小的更新范围,从而更新render函数中的最新数据,再将更新后的render函数渲染成真实dom。也就完成了我们的数据更新
  12. 然后再执行updated,所以updated里面也可以操作dom,并拿到最新更新后的dom。不过这里我要插一句话了,mouted和updated的执行,并不会等待所有子组件都被挂载完成后再执行,所以如果你希望所有视图都更新完毕后再做些什么事情,那么你最好在mouted或者updated中加一个 n e x t T i c k ( ) , 然 后 把 要 做 的 事 情 放 在 nextTick(),然后把要做的事情放在 nextTicknetTick()中去做.
  13. 再之后beforeDestroy没啥说的,实例销毁前,也就是说在这个函数内,你还是可以操作实例的
  14. 之后会做一系列的销毁动作,解除各种数据引用,移除事件监听,删除组件_watcher,删除子实例,删除自身self等。同时将实例属性_isDestroyed置为true
  15. 销毁完成后,再执行destroyed

案例:在Vue实例初始化完成后就会调用beforeCreate()方法

<div id="app">
    <h1>{{message}}</h1>
</div>
<script type="text/javascript">
    const app = new Vue({
        el:"#app",
        data:{
            message:"hello world"
        },
        beforeCreate:function () {
            console.log("Vue初始化完成");
        }
    });
</script>

2.3 mustache语法

我们可以像下面这样来使用,并且数据是响应式的

<div id="app">
    <!-- 直接插入  -->
    <h2>message:{{message}}</h2>
    <!--  可以插入多个-->
    <h2>{{firstName}}{{lastName}}</h2>
    <!-- 也可以是一个表达式 -->
    <h2> {{count*6}}</h2>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            count: 10,
            firstName: "法外狂徒",
            lastName: "张三",
            message: "hello world"
        }
    });
</script>

2.4 v-once

在某些情况下,我们可能不希望界面随意的跟随改变,这个时候,我们就可以使用一个Vue的指令

v-once:

  • 该指令后面不需要跟任何表达式(比如之前的v-for后面是由跟表达式的)
  • 该指令表示元素和组件(组件后面才会学习)只渲染一次,不会随着数据的改变而改变
<div id="app">
    <h2 v-once> {{message}}</h2>
</div>
<script type="text/javascript">
    const app = new Vue({
        el:"#app",
        data:{
            message:"hello world"
        }
    });
</script>

2.5 v-html

如果我们希望将字符串中的内容按照HTML解析出展示
可以使用v-html指令

  • 该指令后面往往会跟上一个string类型
  • 会将string的html解析出来并且进行渲染
  • 此方式会将标签中原有的内容进行替换掉
<div id="app">
    <p v-html="message"> </p>
</div>

<script type="text/javascript">
    const app = new Vue({
        el:"#app",
        data:{
            message:`<a href="https://www.baidu.com/">点击此处跳转到百度</a>`
        }
    });
</script>

2.6 v-text

v-text作用和Mustache比较相似:都是用于将数据显示在界面中,区别就是,v-text即使字符串中有HTML代码,也会被当做普通字符串解析,而且会替换掉标签中的全部内容。

<div id="app">
    <h2 v-text="message"> </h2>
</div>

<script type="text/javascript">
    const app = new Vue({
        el:"#app",
        data:{
            message:`<b> hello world</b>`
        }
    });
</script>

2.7 v-pre

v-pre用于跳过这个元素和它子元素的编译过程,用于显示原本的Mustache语法,不对mustache语法进行解析,显示标签内的原始内容

<div id="app">
<h2 v-pre>{{message}}</h2>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            message:"hello world"
        }
    });
</script>

2.8 v-cloak

在某些情况下,我们浏览器可能会直接显然出未编译的Mustache标签。v-cloak可以保证mustache语法被正确解析后才进行渲染

在vue解析之前标签中会有 v-cloak属性,解析完毕后,标签中的v-cloak属性就自动删除。

<div id="app" v-cloak>
<h2 >{{message}}</h2>
</div>

<style>
    [v-cloak]{
        display: none;
    }
</style>

<script type="text/javascript">
    // 延时一秒钟
   setTimeout(function () {
       const app = new Vue({
           el: "#app",
           data: {
               message: "hello world"
           }
       });
   },1000)

</script>

2.9 v-bind

v-bind:动态绑定属性,将data中的数据,动态绑定掉标签中的属性中去。v-bind:属性名=“data”

v-bind简写: :attributeName=“data”

<div id="app">
    <a v-bind:href="baiduUrl"> <img v-bind:src="baiduIco"></a>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
         baiduIco:`https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png`,
         baiduUrl:`https://www.baidu.com/`
        }
    });
</script>

v-bind:动态绑定Class属性【对象语法】:原有的class属性不会被覆盖,而是将动态绑定的class属性添加到原有的class中去

<style>
    .active{
        color: red;
    }
    .background-green{
        background: #5cb85c;
    }
</style>
<div id="app">
<!--  v-bind:class="{类名1:boolean,类名2:boolean}"  -->
<b class="background-green" v-bind:class="{active:isActive}">{{message}}</b>
 <button v-on:click="conversion">点击换色</button>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            message: "hello world",
            isActive:true
        },
        methods:{
            // 取反操作
            conversion:function () {
                this.isActive=!this.isActive;
            }
        }
    });
</script>

--------------------------------------------- 绑定方法的一个用法--------------------------------------------------
<style>
    .active{
        color: red;
    }
    .background-green{
        background: #5cb85c;
    }
</style>
<div id="app">
    <!--  v-bind:class="functionName()"  -->
    <b class="background-green" v-bind:class="getClass()">{{message}}</b>
    <button v-on:click="conversion">点击换色</button>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            message: "hello world",
            isActive:true
        },
        methods:{
            // 取反操作
            conversion:function () {
                this.isActive=!this.isActive;
            },
            getClass:function(){
                return {active:this.isActive};
            }
        }
    });
</script>

**v-bind:动态绑定Class属性【数组语法】:**用的比较少,还是对象语法用的比较多

<style>
    .active{
        color: red;
    }
    .background-green{
        background: #5cb85c;
    }
</style>
<div id="app">
    <!--  v-bind:class="['ClassName1','ClassName2']"  注意:加单引号或双引号表示字符串,不加表示变量名-->
    <b class="background-green" v-bind:class="['active','background-green']">{{message}}</b>
    <button v-on:click="conversion">点击换色</button>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            message: "hello world",

        },

    });
</script>

小案例:点击当前行变色

<style>
    .active{
        color: red;
    }
</style>
<div id="app">
    <ul  v-for="(value,index) in movies">
        <li v-on:click="isShow(index)"  v-bind:class="{active:index==currentIndex}"  >{{value}}</li>
    </ul>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            movies: ["亚索","剑圣","提莫","剑姬","剑魔","洛手"],
             currentIndex:-1,
        },
        methods:{
            isShow:function (index) {
                this.currentIndex=index;
            }

        }
    });
</script>

2.10 v-bind绑定style

我们可以利用v-bind:style来绑定一些CSS内联样式。
在写CSS属性名的时候,比如font-size

  • 我们可以使用驼峰式 case: fontSize
  • 或短横线分隔 (记得用单引号括起来) ‘font-size’
  • 没有加单引号时,vue会当成变量来解析

对象语法: :style="{key(attributeName):value(attributeValue)}

<div id="app">
    <!--  :style="{key(attributeName):value(attributeValue)}"
    属性名是多个单词组合时:语法1:font-size; 语法2:fontSize
     -->
    <p :style="{color:'red',background:'green'}">{{message}}</p>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            message: "hello world"
        }
    });
</script>

数组语法: :style="[object1,object2,…]

<div id="app">
    <p :style="[myColor,myBackground]">{{message}}</p>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            message: "hello world",
            myColor:{color:'red'},
            myBackground:{background:'green'}
        }
    });
</script>

2.11 计算属性 computed

在某些情况,我们可能需要对数据进行一些转化后再显示,或者需要将多个数据结合起来进行显示,就可以使用计算属性

<div id="app">
    <!-- 完整的把firstName和lastName拼接起来 ,并且中间留个空格 -->
    <h2>方式1:{{firstName}} {{lastName}}</h2>
    <h2>方式2:{{firstName+" "+lastName}}</h2>
    <h2>方式3:{{fullName}}</h2>
    <h2>方式4:{{getFullName()}}</h2>

</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            firstName: "法外狂徒",
            lastName: "张三"
        },
        // 计算属性的方式
        computed: {
            fullName: function () {
                return this.firstName + ' ' + this.lastName;
            }
        },
        // 方法
        methods: {
            getFullName: function () {
                return this.firstName + ' ' + this.lastName;
            }
        }

    });
</script>

**计算属性案例:**计算表格中书本的总价格

computed和methods的区别:computed无论你引用多少次,都执行一次,methods每调用一次就执行一次

会实时监控data中的数据,当data中的数据发生改变时,会重新进行计算

<div id="app">
    <table border="1" cellspacing="0">
        <tr>
            <th>bookName</th>
            <th>price</th>
            <th>count</th>
        </tr>
        <tr v-for="(item,key,index) in books">
            <td>{{item.bookName}}</td>
            <td>{{item.price}}</td>
            <td>{{item.count}}</td>
        </tr>
    </table>

<b>总价格为:{{totalPrice}}</b>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
           books:[
               {bookName:"java编程思想",price:10,count:10},
               {bookName:"sql查询艺术",price:10,count:20},
               {bookName:"spring揭秘",price:10,count:30}

           ]
        },
        computed:{
            totalPrice:function () {
                return  this.books.reduce((temp,item,index,data)=>{
                    /**
                     * temp:临时累加变量
                     * item:当前项
                     * index:当前元素下标
                     * data:源数组的值
                     * 如果reduce传入了第二个参数,那么数组从第一项开始循环,第一次循环的temp值是传入的第二个参数
                     * 如果reduce没有传入第二个参数,数组从第二项开始循环,第一次循环的temp值是数组的第一项元素
                     */
                    return temp+(item.price*item.count);
                },0)

            }
        }
    });
</script>

computed的set和get

<div id="app">
    <h2>{{fullName}}</h2>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            firstName:"法外狂徒",
            lastName:"张三"
        },
        computed:{
            fullName:{
                // 当尝试给computed属性重新赋值的时候,set方法就会被调用
                set:function (newValue) {
                    console.log("set方法被调用 newValue="+newValue);
                    let names=newValue.split(' ');
                    this.firstName=names[0];
                    this.lastName=names[1];
                },
                get:function () {
                    // 重新给fullName赋值,触发set方法
                    // this.fullName="懂法狂魔 李四";
                    return this.firstName+' '+this.lastName;
                }
            }
        }
    });
</script>

2.12 v-on 事件监听

可以用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。[不带任何参数]

<div id="app">
 <h2>当前计数: {{count}}  </h2>
<!--1. 简写方式: @click="increment();原生方式:v-on:click="increment()" 
    2.当方法没有参数的时候,可以省略掉括号,有参数时,不可省
    3.@click="count=+1" 这种写法也是支持的
-->
    <button @click="increment()">+</button>
    <button @click="decrement()">-</button>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            count:0
        },
        methods:{
            increment(){
                this.count++;
            },
            decrement(){
                if (this.count>0){
                    this.count--;
                }else {
                    alert("对不起!已经减到底了哦!")
                }
            }
        }
    });
</script>

可以用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。[带参数]

<div id="app">
<button @click="show('vue')">按钮</button>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            message: "welcome come "
        },
        methods:{
            show(message){
                alert(this.message+message);
            }
        }
    });
</script>

有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event 把它传入方法:

<div id="app">
    <button @click="show('vue',$event)">按钮</button>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            message: "welcome come "
        },
        methods: {
            show(message, event) {
                console.log(this.message + message)
                //获取事件类型
                console.log(event.type)
                // 事件源,就是发生事件的元素;
                console.log(event.target)
            }
        }
    });
</script>

v-one事件修饰符

在事件处理程序中调用 event.preventDefault()event.stopPropagation() 是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。

<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>

<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>

<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
// 2.1.4新增
<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>
<div id="app">
    <!-- 只会触发一次 -->
    <button @click.once="show('hello',$event)">按钮</button>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            message: " world"
        },
        methods: {
            show(message, event) {
                console.log(message + this.message);
            }
        }
    });
</script>

注意:使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent只会阻止对元素自身的点击。

2.3.0 新增:Vue 还对应 addEventListener 中的 passive 选项提供了 .passive 修饰符。

<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成  -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>

这个 .passive 修饰符尤其能够提升移动端的性能。

注意:不要把 .passive.prevent 一起使用,因为 .prevent 将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive 会告诉浏览器你不想阻止事件的默认行为。

2.13 v-if

v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 true 值的时候被渲染。

Vue的条件指令可以条件根据表达式的值在DOM中渲染或销毁元素组件

<div id="app">
    <h2 v-if="score>0">{{messageOne}}</h2>
    <!-- else:根据自身需求加,可以加,可以不加 -->
    <h2 v-else>{{messageTwo}}</h2>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            messageOne: "hello world one",
            messageTwo: "hello world two",
            score:80
        }
    });
</script>

**v-else-if: ** 顾名思义,充当 v-if 的“else-if 块”,可以连续使用:[ 2.1.0 新增 ]

<div id="app">
    <h2 v-if="score>=80">{{excellent}}</h2>
    <h2 v-else-if="score>=60">{{good}}</h2>
    <h2 v-else>{{lousy}}</h2>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            excellent: "优秀",
            good: "良好",
            lousy: "差劲",
            score: 80
        }
    });
</script>

小练习:用户类型切换

<div id="app">
    <span v-if="type==='username'">
        <label>用户账号:</label>
        <input placeholder="用户名">
    </span>
    <span v-else>
    <br/>
    <label>邮箱地址:</label>
    <input placeholder="邮箱地址">
</span>
    <button @click="show()">切换类型</button>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
          type:'username'
        },
        methods:{
            show(){
                this.type=this.type==='email'?'username':'email';
                console.log( this.type)
            }
        }
    });
</script>

2.14 v-show

  • v-show 就简单得多——不管初始条件是什么,元素总是会被渲染(渲染DOM,只是被因此2掉了而已),并且只是简单地基于 CSS 进行切换。
  • 一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。
<div id="app">
    <h2 :style="{color:'red'}" v-show="isShow">{{message}}</h2>
    <button @click="trigger()">切换显示</button>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            message: "hello world",
            isShow:true
        },
        methods:{
            trigger(){
                this.isShow=!this.isShow;
               
            }
        }
    });
</script>

2.15 v-for

  • 当我们有一组数据需要进行渲染时,我们就可以使用v-for来完成。
  • p格式如下:v-for=“item in items” 的形式。

v-for遍历数组:

<div id="app">
<!-- 1.需要下标的写法: v-for="(item,index) in books"
     2.不需要下标的写法: v-for="item in books"
   -->
    <ul v-for="(item,index) in books">
        <li>{{item}} index={{index}}</li>
    </ul>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            books: ['JavaScript权威指南','深入java虚拟机','Netty权威指南','java编程思想','MySQL入门到精通']
        }
    });
</script>
------------------------------------------------遍历数组对象------------------------------------------------------
    <div id="app">
<!-- 1.遍历数组对象-->
    <ul v-for="(user, index) in userList">
        <li>id:{{user.id}} name:{{user.name}} phone:{{user.phone}} index={{index}}</li>
    </ul>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            userList:[
                {id:"101",name:"张三",phone:"123456"},
                {id:"102",name:"李四",phone:"123457"},
                {id:"103",name:"老王",phone:"123458"},
                {id:"104",name:"小明",phone:"123459"},
            ]
        }
    });
</script>

v-for 遍历对象

  • 建议尽可能在使用 v-for 时提供 key attribute,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升
  • 因为它是 Vue 识别节点的一个通用机制,key 并不仅与 v-for 特别关联。后面我们将在指南中看到,它还具有其它用途。
<div id="app">
<!-- 1.遍历数组对象 key和index,可以加,可以不加,根据需求来变化-->
    <ul v-for="(value, key,index) in userList">
        <li>{{key}}--{{value}}--{{index}} </li>
    </ul>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            userList:  {id:"104",name:"小明",phone:"123459"}
        }
    });
</script>

v-for 的数组更新检测

因为Vue是响应式的,所以当数据发生变化时,Vue会自动检测数据变化,视图会发生对应的更新。

Vue中包含了一组观察数组编译的方法,使用它们改变数组也会触发视图的更新。

  • push(args…):往数组的末尾添加一个或多个元素
  • pop():删除掉数组末尾的最后一个元素
  • shift(): 从数组的头部移除掉一个元素
  • unshift(): 在数组的头部插入一个或多个元素
  • splice():传入一个index,从index开始向后删除所有元素,也可以跟第二个参数,代表从该index开始向后删除多少个元素
  • sort():将数组中的元素进行排序,升序排列
  • reverse():将数组的内容进行反转

小练习:

<div id="app">
    <table border="1" cellspacing="0">
        <tr>
            <th>id</th>
            <th>书籍名称</th>
            <th>出版日期</th>
            <th>价格</th>
            <th>购买数量</th>
            <th>操作</th>
        </tr>
        <tr v-for="(item,index) in books">
            <td>{{index+1}}</td>
            <td>{{item.bookName}}</td>
            <td>{{item.date}}</td>
            <td>{{item.price|getPrice}}</td>
            <td>
                <button @click="increment(index)">+</button>
                {{item.count}}
                <button @click="decrement(index)">-</button>
            </td>
            <td>
                <button @click="remove(index)">移除</button>
            </td>
        </tr>
    </table>
    <p>总价格:{{totalPrice}}</p>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            message: "",
            books: [
                {bookName: "java编程思想", date: "2021-8-9", price: 10, count: 0},
                {bookName: "sql查询艺术", date: "2021-8-1", price: 10, count: 0},
                {bookName: "spring揭秘", date: "2021-8-2", price: 10, count: 0},
                {bookName: "http权威指南", date: "2021-8-3", price: 10, count: 0},
                {bookName: "深入理解java虚拟机", date: "2021-8-4", price: 10, count: 0}
            ]
        },
        computed: {
            totalPrice: function () {

               return  this.books.reduce((temp,item,index,data)=>{
                return temp+(item.price*item.count);
            },0)

            }
        },
        methods: {
            remove(index) {
                this.books.splice(index, 1);
            },
            increment(index) {
            this.books[index].count++;
            },
            decrement(index) {
               if (this.books[index].count>0){
                   this.books[index].count--;
               }
            }
        },
        // 过滤器的使用,主要是用于渲染之前文本的一些格式化处理
        filters:{
            getPrice:function(price){
                console.log("price="+price);
                return '¥'+price.toFixed(2);
            }
        }
    });
</script>

2.16v-model(双向绑定)

input输入框:

Vue中使用v-model指令来实现表单元素和数据的双向绑定

  • 因为input中的v-model绑定了message,所以会实时将输入的内容传递给message,message发生改变input输入框也会随之改变
  • 当message发生改变时,因为上面我们使用Mustache语法,将message的值插入到DOM中,所以DOM会发生响应的改变
<div id="app">
    <p>
        <label for="username">用户名:</label>
        <input v-model="username" type="text" placeholder="请输入用户名" name="username"  id="username">
        <label for="password" >密  码:</label>
        <input v-model="password"  type="text" placeholder="请输入密码" name="password"  id="password">
    </p>
    <button @click="change">修改用户名和密码</button>
    <button @click="reaction">还原用户名和密码</button>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            username: "123456",
            password: "admin"
        },
        methods:{
            change(){
                this.username="456789";
                this.password="root";
            },
            reaction(){
                this.username="123456";
                this.password="admin";
            }
        }
    });
</script> 

textarea输入框:

<div id="app">
    <span>多行文本框双向绑定案例</span>
    <p style="white-space: pre-line;"> message={{ message }}</p>
    <br>
    <textarea  v-model="message" placeholder="请添加文本:"> </textarea>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            message: "hello world"
        }
    });
</script>

checkbox复选框:

  • 单个复选框绑定到布尔值
<div id="app">
    <input type="checkbox" id="checkbox" v-model="checked">
    <label for="checkbox"> 是否选中:{{ checked }}</label>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
             checked:true
        }
    });
</script>
  • 多个复选框,绑定到同一个数组:
<div id="app">
    <input type="checkbox" id="jack" value="杰克" v-model="checkedNames">
    <label for="jack">Jack</label>
    <input type="checkbox" id="john" value="约翰" v-model="checkedNames">
    <label for="john">John</label>
    <input type="checkbox" id="mike" value="迈克" v-model="checkedNames">
    <label for="mike">Mike</label>
    <br>
    <span>选中结果: {{ checkedNames }}</span>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            checkedNames:[]
        }
    });
</script>
-----------------------------------------动态绑定----------------------------------------------------------
<div id="app">

    <label  v-for="item in fruits" :for="item.id">
        <input type="checkbox" :id="item.id" :value="item.value" v-model="fruitList">{{item.name}}
    </label>

    <span>选中结果: {{ fruitList }}</span>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: { 
            fruitList:[],
            fruits:[
                {name:"苹果",value:"apple",id:"101"},
                {name:"梨子",value:"pear",id:"102"},
                {name:"香蕉",value:"banana",id:"103"},
            ]

        }
    });
</script>

radio单选按钮

<div id="app">
    <input type="radio" id="apple" value="apple" v-model="picked">
    <label for="apple">苹果</label>

    <input type="radio" id="pear" value="pear" v-model="picked">
    <label for="pear">梨子</label>

    <span>选中结果: {{ picked }}</span>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            picked: ''
        }
    });
</script>

select选择框

  • select单选框:

如果 v-model 表达式的初始值未能匹配任何选项,select 元素将被渲染为“未选中”状态。在 iOS 中,这会使用户无法选择第一个选项。因为这样的情况下,iOS 不会触发 change 事件。因此,更推荐像上面这样提供一个值为空的禁用选项。

<div id="app">
    <select v-model="selected">
        <option disabled value="">请选择</option>
        <option>亚索</option>
        <option>剑圣</option>
        <option>剑姬</option>
    </select>
    <span>选择状态: {{ selected }}</span>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            selected: ""
        }
    });
</script>
  • select复选框:

多选时在select中添加multiple属性,将之前绑定的单个字符串改为绑定为数组即可

<div id="app">
    <select v-model="selected" multiple >
        <option disabled value="">请选择</option>
        <option>亚索</option>
        <option>剑圣</option>
        <option>剑姬</option>
    </select>
    <span>选择状态: {{ selected }}</span>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            selected: []
        }
    });
</script>
  • 值绑定:
<!-- 当选中时,`picked` 为字符串 "a" -->
<input type="radio" v-model="picked" value="a">

<!-- `toggle` 为 true 或 false -->
<input type="checkbox" v-model="toggle">

<!-- 当选中第一个选项时,`selected` 为字符串 "abc" -->
<select v-model="selected">
  <option value="abc">ABC</option>
</select>

-----------------------------------select动态渲染-----------------------------------------------------------------
<div id="app">
    <select v-model="selected">
        <lable  v-for="item in options">
            <option disabled value="">请选择</option>
        <option v-for="option in options" :value="option.value">{{ option.text }}</option>
        </lable>
    </select>
    <span>选择状态: {{ selected }}</span>
</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            selected: 'jack',
            options: [
                { text: '杰克', value: 'jack' },
                { text: '约翰', value: 'John' },
                { text: '迈克', value: 'Mike' }
            ]
        }
    });
</script>

v-model修饰符:

**.lazy: ** 在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步 (除了上述输入法组合文字时)。你可以添加 lazy 修饰符,从而转为在 change 事件_之后_进行同步:

<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg">

**.number: ** 如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符:

<input v-model.number="age" type="number">

trim : 如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符:

<input v-model.trim="msg">

3.组件化开发

组件化:将问题进行拆分,把问题拆分问小问题,提高可复用性

  1. 调用Vue.extend()方法 - 创建组件构造器
  2. 调用Vue.component()方法 - 注册组件
  3. 在Vue实例的作用范围内 - 使用组件

3.1 组件注册

该案例是:全局组件

<div id="app">
    <!-- 3.使用组件 -->
    <my-cmp></my-cmp>
</div>
<script type="text/javascript">
    // 1. 创建组件构造器
    const cnpC = Vue.extend({
        template: `<div>
                         <h2>标题</h2>
                         <p>模板内容</p>
                  <div/>`
    });
    // 2.注册全局组件(tagName,component)
    Vue.component('my-cmp', cnpC);
    const app = new Vue({
        el: "#app",
        data: {
            message: ""
        }
    });
</script>

3.2 局部组件

<div id="app">
    <cmp></cmp>
</div>
<script type="text/javascript">
    const cpnC = Vue.extend({
        template: `
        <div>
            <h1>标题</h1>
            <p>内容项</p>
        </div>
        `
    })
    // 注册到app下的局部组件
    const app = new Vue({
        el: "#app",
        data: {
            message: ""
        },
        components: {
            cmp: cpnC
        }
    });
</script>

全局组件和局部组件的区别:全局组件在任意的实例、父级组件中都能使用,局部组件只能在创建自己的父级组件或者实例中使用

全局组件都是由Vue.component构建的

**语法糖:**Vue为了简化注册组件的过程, 提供了注册的语法塘, 省去了调用Vue.extend()的步骤. 而是可以直接使用一个对象来代替

<script type="text/javascript">

    // 注册到app下的局部组件
    const app = new Vue({
        el: "#app",
        data: {
            message: ""
        },
        components: {
            cmp: {
                template: `  <div>
                                 <h1>标题</h1>
                                 <p>内容项</p>
                             </div>
                            `
            }
        }
    });
</script>

组件模板抽离写法

  • 方法一:借助script标签【全局组件】:
<div id="app">
    <cpn></cpn>
</div>

<script type="text/x-template" id="cpn">
    <div>
        <h2>标题</h2>
        <p>内容</p>
    </div>
</script>

<script type="text/javascript">

    Vue.component('cpn',{
        template:'#cpn'
    });
    
    new Vue({
        el:"#app"
    })
</script>
  • 方法2 : 借助template标签【局部组件】
<div id="app">
  <cmp></cmp>
</div>

<template id="cpn">
    <div >
        <h2>标题</h2>
        <p>内容</p>
    </div>
</template>

<script type="text/javascript">
    const app = new Vue({
        el:"#app",
        components:{
            'cmp':{
                template:'#cpn'
            }
        }
    });
</script>

3.3 父子/组件

一个组件要想使用,要么在vue实例下注册过,要么注册成全局组件。

父子组件错误用法:以子标签的形式在Vue实例中使用

  • 因为当子组件注册到父组件的components时,Vue会编译好父组件的模块
  • 该模板的内容已经决定了父组件将要渲染的HTML(相当于父组件中已经有了子组件中的内容了)
  • 是只能在父组件中被识别的。
  • 类似这种用法,是会被浏览器忽略的。
<div id="app">
    <fathers> </fathers>
</div>
<script type="text/javascript">
    // 子组件
    let son = Vue.extend({
        template:` <div> <h2>标题2</h2> <p>内容2</p></div>`
    })

    // 父组件
    let father = Vue.extend({
        // 注意必须有个div将其包起来
        template:`<div> <h2>标题1</h2> <p>内容1</p> <sonCpn></sonCpn> </div> `,
        components:{
            sonCpn:son
        }

    })

    const app = new Vue({
        el: "#app",
        data: {
            message: ""
        },
        components:{
          fathers:father
        }
    });
</script>

组件存放数据:

  • 组件对象也有一个data属性(也可以有methods等属性)
  • 只是这个data属性必须是一个函数
  • 而且这个函数返回一个对象,对象内部保存着数据

组件可以访问VUE实例数据吗?

  • 不能访问,组件是一个单独功能模块的封装 - 这个模块有自己的HTML模板, 也应该有属性自己的数据data
<div id="app">
    <cmp></cmp>
    <cmp></cmp>
</div>
<template id="banner">
    <div>
      当前计数:<button @click="increment()">+</button> <span> {{count}}</span> <button @click="decrement()">-</button>
    </div>
</template>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            content: "app data"
        },
        components: {
            'cmp': {
                template: "#banner",
                data() {
                    // 如果需要公用,那么就定义个全局变量,在此处返回该对象
                    return {message: "hello world",count:0}
                },
                methods:{
                 increment(){
                      this.count+=1;
                     console.log("increment")
                 },
                 decrement(){
                     if (this.count<=0){
                         alert("对不起!已经到底了")
                         return;
                     }
                     this.count-=1;
                     console.log("decrement")
                 }
                }
            }
        }
    });
</script>
</script>
// 可以通过app对象访问vue实例中的数据,this是不行的
// 组件复用,他们之间的数组是私有的,不共享

3.4 父子组件之间的通信

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YdZGmSJa-1649957421208)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20211224231655551.png)]

子组件是不能引用父组件或者Vue实例的数据的。但是,在开发中,往往一些数据确实需要从上层传递到下层:

  • 比如在一个页面中,我们从服务器请求到了很多的数据。
  • 其中一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。
  • 这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件)将数据传递给小组件(子组件)。
  • 通过 Prop 向子组件传递数据

父组件向子组件传递数据:

<template id="cmp">
    <div>
        <table border="1" cellspacing="0">
          <tr>
              <th>书名</th>
              <th>价格</th>
              <th>数量</th>
          </tr>
            <tr v-for="item in bookList">
                <td>{{item.bookName}}</td>
                <td>{{item.price}}</td>
                <td>{{item.count}}</td>
            </tr>
        </table>

        <button @click="show()">点击此处查看数据</button>
    </div>
</template>



<div id="app">
<!-- 如果采用的是驼峰命名,这里必须使用 - 进行分割 -->
<table-list v-bind:book-list="books" v-bind:info="message"></table-list>

</div>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            books:[
                {bookName:"java编程思想",price:10,count:10},
                {bookName:"sql查询艺术",price:10,count:20},
                {bookName:"spring揭秘",price:10,count:30},
                {bookName:"http权威指南",price:10,count:40},
                {bookName:"深入理解java虚拟机",price:10,count:50}

            ],
            message:"hello world"
        },
        components:{
            'table-list':{
                template:"#cmp",
               // props:['book_list','info'], // 写法1:数组类型
               /* props:{
                    book_list:Array,info:String //写法2:可以是对象,而且还可以指定数据类型
                },*/
                //写法3: required:必须传递 ,default:默认值,如果是对象,必须是一个函数返回一个对象
                props:{
                   bookList:{type:Array,default:[ {bookName:"java编程思想",price:10,count:10}, 
                                                 {bookName:"sql查询艺术",price:10,count:20}]},
                   info:{type:String,default:"hello",required:true}
                },
                methods:{
                    show(){
                        alert(this.info);

                    }
                },
                data(){
                    return {};
                }


            }
        }
    });
</script>

子组件向父组件传递数据(自定义事件)

<!-- 子组件模板 -->
<template id="son">
    <div> <button v-for="item in categories" @click="btnClick(item)">{{item.name}}</button></div>
</template>
<div id="app">
<!--  将子组件的事件传递到父组件-->
    <cpn @item_click="sonClick"></cpn>
</div>
<script type="text/javascript">
    const  product = {
        template:'#son',
        data(){
            return{
                categories:[
                    {id:101,name:"热门推荐"},
                    {id:102,name:"手机数码"},
                    {id:103,name:"生活用品"},
                    {id:104,name:"家用家电"}

                ],
                message:"hello"
            }
        },
        methods:{
            // 将此事件发送的父组件
            btnClick(item){
                alert(item.id)
                this.$emit('item_click',item)
            }
        },


    }
    const app = new Vue({
        el: "#app",
        data: {
            message: "hello"
        },
        methods: {
            // 监听子组件传递过来的事件
            sonClick(item){
                console.log("sonClick",item)
                this.message=item;
            }
        },
        components:{
            cpn:product
        },
        // 如果message的值发生改变,就会触发该函数
        watch:{
            message(newValue,oldValue){
                console.log("watch被触发:"+newValue,oldValue)
            }
        }
    });

**this. c h i l d r e n : ∗ ∗ 在 父 组 件 中 , 可 以 通 过 < f o n t c o l o r = ′ r e d ′ > t h i s . children :** 在父组件中,可以通过<font color='red'>this. children:<fontcolor=red>this.children直接拿到子组件对象

<div id="app">
    <parent-cpn></parent-cpn>
</div>
<template id="parentCpn">
    <div>
        <child-cpn1></child-cpn1>
        <child-cpn2></child-cpn2>
        <button @click="showChildren">按钮</button>
    </div>
</template>
<template id="child-cpn1">
    <h2>子组件1</h2>
</template>
<template id="child-cpn2">
    <h2>子组件2</h2>
</template>
<script type="text/javascript">
    const cpn1 = Vue.component('child-cpn1', {
        template: '#child-cpn1',
        methods: {
            showMessage() {
                console.log("我是子组件1")
            }
        }

    })
    const cpn2 = Vue.component('child-cpn2', {
        template: '#child-cpn2',
        methods: {
            showMessage() {
                console.log("我是子组件2")
            }
        }

    })
    const cpn = Vue.component('parent-cpn', {
        template: '#parentCpn',
        methods: {
            showChildren() {
                // 通过 this.$children直接进行访问子组件,$children是一个数组对象,里面都是存放的子组件
                this.$children[0].showMessage();
                this.$children[1].showMessage();
            }
        },
        components: {
            cpn1, cpn2
        }
    })

    const app = new Vue({
        el: "#app",
        data: {
            message: ""
        },
        components: {
            cpn
        }
    });
</script>

$children的缺陷:

  • 通过$children访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值。
  • 但是当子组件过多,我们需要拿到其中一个时,往往不能确定它的索引值,甚至还可能会发生变化。
  • 有时候,我们想明确获取其中一个特定的组件,这个时候就可以使用$refs

$refs访问子组件:[ 重点 ]

  • $refsref指令通常是一起使用的。
  • 首先,我们通过ref给某一个子组件绑定一个特定的ID。
  • 其次,通过this.$refs.ID就可以访问到该组件了。
<div id="app">
    <cpn1></cpn1>
</div>
<template id="cpn1">
    <div>
         <cpn2 ref="cpn2"></cpn2>
         <cpn3 ref="cpn3"></cpn3>
        <button @click="btnClick">cpn1</button>
    </div>
</template>

<template id="cpn2">
    <div> <h2>子组件1</h2> </div>
</template>
<template id="cpn3">
    <div> <h2>子组件2</h2> </div>
</template >
<script type="text/javascript">

    const  cpn2=Vue.component('cpn2',{
        template:'#cpn2',
        methods:{
            showMessage(){
                console.log("我是子组件1")
            }
        }
    })

    const cpn3=Vue.component('cpn3',{
        template: '#cpn3',
        methods: {
            showMessage(){
                console.log("我是子组件2")
            }
        }
    })

    const  cpn1=Vue.component('cpn1',{
        template:'#cpn1',
        components:{
            cpn2,cpn3
        },
        methods:{
            btnClick(){
                // 在父组件中直接进行访问子组件
               this.$refs.cpn2.showMessage()
               this.$refs.cpn3.showMessage()
            }
        }
    })



    const app = new Vue({
        el: "#app",
        data: {
            message: "",
            components:{
                cpn1
            },
            methods:{
                btnClick(){
                    console.log("根组件")
                }
            }
        }
    });
</script>

**$parent 访问父组件 : ** 访问的上一级父组件,并不是vue根组件

<div id="app">
    <cpn></cpn>
</div>
<template id="cpn">
    <div>
        <h2>我是app的子组件</h2>
        <button @click="btnClick">按钮</button>
    </div>
</template>
<script type="text/javascript">


    const app = new Vue({
        el: "#app",
        data: {
            message: "hello world"
        },
        components: {
            cpn: {
                template: '#cpn',
                methods: {
                    btnClick(){
                        alert(this.$parent.message)
                    }
                }
            }

        }
    });
</script>

3.5 插槽的使用

组件的插槽:

  • 组件的插槽也是为了让我们封装的组件更加具有扩展性。
  • 让使用者可以决定组件内部的一些内容到底展示

基本使用:

<div id="app">
    <cpn> <button> slot </button> </cpn>
    <!-- 替换一个  -->
    <cpn> <b> slot </b></cpn>
    <!-- 使用默认值  -->
    <cpn></cpn>
<!-- 当只有一个slot时全部被替换   -->
    <cpn>
        <a href="https://www.baidu.com/">百度一下</a>
       <i>i标签</i>
       <b>b标签</b>
    </cpn>
</div>
<template id="cpn">
    <div>
        <h2>标题</h2>
        <p>内容</p>
                <!-- 默认值-->
        <slot>   <button style="background: #5cb85c"> slot </button> </slot>
    </div>
</template>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            message: ""
        },
        components:{
            cpn:{
                template:'#cpn'
            }
        }
    });
</script>

具名插槽:

<div id="app">
<!-- 使用默认值   -->
      <cpn>  </cpn>
<!-- 指定替换  -->
      <cpn> <span slot="left" style="color: red">world</span> </cpn>
</div>
<template id="cpn">
    <div>
        <h2>标题</h2>
        <p>内容</p>
        <slot name="left"> <span>left</span> </slot>
        <slot name="center"> <span>center</span> </slot>
        <slot name="right"> <span>right</span> </slot>
    </div>
</template>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            message: ""
        },
        components:{
            cpn:{
                template:"#cpn"
            }
        }
    });
</script>

**编译作用域:**父组件模板所有的东西都在父级作用域内编译, 子组件模板的所有东西会在子级作用域内编译

<div id="app">
<!--  使用的是app下面的isShow  -->
<cpn v-show="isShow"></cpn>
</div>
<template id="cpn">
    <div>
        <h2>标题</h2>
        <p>内容</p>

    </div>
</template>
<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
           isShow:true
        },
        components:{
            cpn:{
                template:"#cpn"
            },
            data(){
                return {isShow:false}
            }
        }
    });
</script>

作用域插槽: 父组件替换插槽的标签,但是内容由子组件来提供。

<div id="app">
    <cpn></cpn>
<!--  目的获取子组件的数据,传递给父组件  -->
    <cpn>
        <template slot-scope="slot">
           <ol>
               <li v-for="item in slot.data" style="color: red">{{item}}</li>
           </ol>
        </template>
    </cpn>
</div>

<template id="cpn">
    <div>
<!--   将此data传递给slot  -->
      <slot :data="language">
          <ul>
              <li v-for=" item in language"> {{item}} </li>
          </ul>
      </slot>
    </div>
</template>

<script type="text/javascript">
    const app = new Vue({
        el: "#app",
        data: {
            message: ""
        },
        components:{
            cpn:{
                template:'#cpn',
                data(){
                    return {
                        language:['java','c','c#','php','mysql','javaScript','python','go','object-c']
                    }
                }
            }
        }
    });
</script>

4.webpack 模块化开发

目录结构:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G81CpW7f-1649957421209)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20211225015936054.png)]

export和import的使用:

a.js

let user={id:10,name:'小明'};
  let  flag=true;
function sum(number1,number12) {
    return number1+number12;
}
if (flag){
    console.log(sum(10, 20));
}
// 导出方式1:将这两个变量进行导出
export{
    flag,sum
}
// 导出方式2:直接在定义的时候就导出
export var count=100;

// 导出方式3:导出类
export class Person{
    constructor(id,name) {
        this.id=id;
        this.name=name;
    }
    show(){
        console.log("hello show")
    }
}
// 导出方式4:默认导出,只有一个
const  address ='重庆';
export default address;



b.js

let user={id:11,name:'小红'};
let flag = false;
function sub(number1,number12) {
    return number1+number12;
}

c.js

// 导入a.js中的flag和sum变量到c.js中使用
// import{flag,sum} from "./a.js";

// 将a.js所有的内容进行导出。取名交a_obj,a.js中所有的变量都挂载在a_obj这个对象上
import * as a_obj from './a.js'

if (a_obj.flag){
    console.log("hello world");
    console.log("c.js(sum)="+a_obj.sum(25,25));
}

// 对address重命名
import address2 from './a.js';

console.log('address='+address2)


index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<!-- 解决命名冲突的问题 -->
<script src="a.js" type="module"></script>
<script src="b.js" type="module"></script>
<script src="c.js" type="module"></script>
<body>

</body>
</html>

4.1 webpack安装

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NYMJkAHg-1649957421209)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20211225020417925.png)]

Vue.js官方脚手架工具就使用了webpack模板

  • 对所有的资源会压缩等优化操作,核心两个功能 模块化和打包
  • 它在开发过程中提供了一套完整的功能,能够使得我们开发过程中变得高效
  • 安装webpack之前先安装node,并且配置环境变量 node-v 查看是否安装node环境
  • 配置阿里云镜像:npm config set registry https://registry.npm.taobao.org/
  • 检测是否配置成功 npm config get registry
  • 安装命令(全局安装):cnpm install webpack@3.6.0 -g
  • 测试是否安装成功:webpack -v

4.2 webpack入门使用

目录结构:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y5fjvtS9-1649957421210)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20211225142955772.png)]

index.html 只需要引入bundle.js即可,webpack打包的时候会将依赖到的都引入进来

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../dist/bundle.js"></script>
</head>

<body>

</body>
</html>

main.js

const {add, mul} = require('./mathUtil.js')
console.log(add(20,30));
console.log(mul(20,30));

mathUtil.js


function add(number1,number2) {
        return number1+number2;
}

function mul(number1,number2) {
        return number2*number1;
}

module.exports = {
        add,
        mul
}

-------------------------------------------ES6模块化---------------------------------------------------------
 // 导出  
    export {
        add,mul
}
// 导入
    import {name, age, height} from "./info";

如果最后运行index.html,控制台成功进行打印,说明打包成功

4.3 webpack配置

初始化项目:cnpm init

package name: (02webpack配置) meetwebpack(包名)
version: (1.0.0)
description: webpack-init(描述)
entry point: (webpack.config.js) index.js(入口文件)
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to C:\Users\14823\WebstormProjects\v2\02webpack配置\package.json:

执行成功得到一个package.json 这样的文件,如果里面有依赖别的东西直接执行 cnpm install

在项目下,新建一个文件 webpack.config.js

内容如下:直接执行 webapck 就可以进行打包

// 从node中导入
const path=require('path')
module.exports={
    // 入口
    entry:'./src/main.js',
    // 出口
    output:{
        // 将当前路径和dist进行拼接
        path: path.resolve(__dirname, 'dist'),
        filename:'bundle.js'
    }
}

package.json 的script中配置命令 直接使用 npm run build 代替 webpack 命令

  "scripts": {
     // 优先从本地webpack中找,如果本地没有就去全局中找 [使用脚本使用的就是本地的,否则都是使用的是全局的]
     // 如果想通过cm命令的方式使用本地webpack,那么就在node_modules/bin目录下执行:webpack命令 
    "build": "webpack"
  }

在项目本地安装 webpack 命令: npm install webpack@3.6.0 --save-dev

webpack本身来说无法处理css, 图片, 高级ES6转化ES5的能力, 所以需要webpack扩展对应的loader就好了

css-loader配置: 加载 CSS 文件并解析 import 的 CSS 文件,最终返回 CSS 代码

  • 步骤一: 通过npm安装需要使用的loader (https://webpack.docschina.org/loaders/css-loader/)
  • 步骤二: 在webpack.config.js的modules关键字下配置

1.安装css-loader: npm install style-loader@2.0.2 --save-dev

2.在main.js中依赖normal.css[ require(’./css/normal.css’) ]

3.在webpack.config.js中进行配置

   module: {
        // 配置规则
        rules: [
            {
                test: /\.css$/,
                use: [ "css-loader"],
            },
        ],
    },

style-loader配置 : 安装往css-loader后,并不能进行解析运行,css-loader只负责加载进来,需要额外的在安装style-loader

1.安装style-loader: npm install style-loader@0.23.1 --save-dev

2.在webpack.config.js中进行配置 ,在use中添加一个 style-loader 即可

less-loader配置:

  1. 安装 less-loader: npm install less-loader@4.1.0 --save-dev
  2. webpack.config.js中进行配置
  3. 在rules中的第二个下标中配置该对象
   		 {
                test: /\.less$/,
               // 使用多个loader时,从右向左进行加载,使用对象还可以传递参数: { loader:"less-loader",arg:value},
                use:[
                    {loader:"style-loader"},
                    {loader:"css-loader"},
                    {loader:"less-loader"}
                ]
            },

url-loader配置:用于打包图片

1.安装url-loader: npm install --save-dev url-loader@1.1.2

2.在webpack.config.js中进行配置 注意根据这些版本来,否则不指定版本,会出现问题

    {
                test: /\.(png|jpg|gif)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            // 限制文件的大小,当加载的图片大于限制的时候使用file-loader进行加载,
                            // 小于limit将图片编译为base64字符串进行显示
                            limit:625664,
                        },
                    },
                ],
            }

file-loader: 安装命令:npm install file-loader@2.0.0 --save -dev (解决图片超过limit大小时无法进行打包或显示)

解决file-load路径问题:

   output:{
        // 将当前路径和dist进行拼接
        path: path.resolve(__dirname, 'dist'),
        filename:'bundle.js',
        // 从dist目录下开始寻找资源
        publicPath:'dist/'
    },
  ----------------------------------------在rules添加如下配置-------------------------------------------------------
           {
                test: /\.(png|jpg|gif)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            // 限制文件的大小,当加载的图片大于限制的时候使用file-loader进行加载,
                            // 小于limit将图片编译为base64字符串进行显示
                            limit:13000, //625664
                            // 设置打包路径和文件名
                            name:'img/[name].[hash:8].[ext]'
                        },
                    },
                ],
            }

ES6语法转ES5语法

1.安装对应的依赖: npm install --save-dev babel-loader@7 babel-core babel-preset-es2015

2.配置 webpack.config.js

    	 {
                test: /\.m?js$/,
                // 在进行ES6转ES5时只对src目录下js进行转化
                exclude: /(node_modules|bower_components)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['es2015']
                    }
                }
            }

// 如果出现以下错误:去对应的目录下,把.babelrc文件删除掉即可
Module build failed: SyntaxError: C:\Users\14823\.babelrc: Error while parsing JSON - Unexpected EOF at line 1 column 2 of the JSON5 data. Still to read: ""

配置Vue环境

1.先进行安装 npm install vue --save

2.在mian.js中写入如下测试代码

const app = new Vue({
    el:'#app',
    message:'hello world',
    methods:{
        showMessage(){
            console.log("welcome to Vue")
        }
    }
})

3.在index.html中写一个app

<div id="app">
    <button @click="showMessage"> 按钮 </button>
</div>
// 把引入bundle.js放在后面,避免元素未加载完,js代码开始执行找不到dom

4.解决错误:

方式1import Vue from 'vue/dist/vue.esm.js'
方式2:在webpack.config.js中添加如下代码,和moudle同级

resolve: {
    alias: {
        'vue$': 'vue/dist/vue.esm.js' //内部为正则表达式  vue结尾的
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FzdjLQII-1649957421211)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20211225192338112.png)]

不过这种方式还是很臃肿,还可以进行优化。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YRgtLcja-1649957421211)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20211225193028297.png)]

上面这种代码是优化了不少,但是main.js作为入口文件,不应该在里面写入太多逻辑,所以再次进行抽取

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HgsxOg9F-1649957421212)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20211225201151457.png)]

安装vue-loader : npm install vue-loader@15.4.2 vue-template-compiler@2.6.14–save-dev

在配置文件中做以下修改:

   		{
                test: /\.vue$/,
                use: {
                    loader: 'vue-loader',
                }
            }

在vue目录下新建AApp.vue代替app.js ,内容如下:

<!-- 组件模板代码 -->
<template>
    <div>
        <p>{{message}}</p>
        <button @click="showMessage"> 按钮</button>
    </div>
</template>

<!-- js代码 -->
<script>
    export default {
        name: 'App',
        data() {
            return {
                message: "hello world"
            }
        },
        methods: {
            showMessage() {
                alert("welcome to Vue");
            }
        }
    }
</script>

<!-- 写css代码 -->
<style scoped>
    .title {
        color: #ff0000
    }
</style>

main.js 中使用即可

//引入App.vue
import App from "./vue/App.vue";

new Vue({
    el: '#app',
    template:'<App/>',
    components:{ App }

})

4.4 plugin

配置BannerPulgin : 在buildle.js 中开头声明版权信息

1.在 webpack.config.js 引入webpack

const webpack=require('webpack')

2.在 moudle 同级下配置插件

    // 插件
    plugins:[
        // 版权信息插件
        new webpack.BannerPlugin('最终版权归test所有')
    ]

配置 HtmlWebpackPlugin : 该插件将 index.html 打包到 buildle.js 中去

  1. 安装该插件 : npm install html-webpack-plugin@2.30.1 --save-dev

  2. 注释掉 webpack.config.js 中的 publicPath:‘dist/’

  3. webpack.config.js中引入 const htmlWebpackPlugin = require(‘html-webpack-plugin’)

  4. webpack.config.js中配置 new htmlWebpackPlugin({template:‘index.html’ })

**配置uglifyjs : **对 buildle.js 进行压缩

  1. 安装 npm install uglifyjs-webpack-plugin@1.1.1 --save-dev
  2. webpack.config.js 中引入 const uglifyJsPlugin = require(‘uglifyjs-webpack-plugin’)
  3. webpack.config.js 中配置 new uglifyJsPlugin()

使用 webpack 搭建本地服务器

  1. 安装 npm install webpack-dev-server@2.9.1 --save-dev
  2. package.json 中的 script 中配置 “dev”:“webpack-dev-server”
  3. webpack.config.js 中添加如下配置
    // 加载本地服务器
    devServer: {
        contentBase: './dist',
        // 实时监听页面
        inline: true,
        // 80端口
        port: 80,
        // 如下 npm run dev 后自动打开浏览器
        open: true,
    }

配置文件的分离抽取:

1.安装 npm install webpack-merge --save -dev

base.config.js: 开发时和运行时都需要的配置

// base.config.js:开发时和运行时都需要的配置

// 从node中导入
const path=require('path')
// 引入插件
const webpack=require('webpack')
const htmlWebpackPlugin = require('html-webpack-plugin')
module.exports={
    // 入口
    entry:'./src/main.js',
    // 出口
    output:{
        // 将当前路径和dist进行拼接
        path: path.resolve(__dirname, '../dist'),
        filename:'bundle.js',
        // 从dist目录下开始寻找资源
        // publicPath:'dist/'
    },

    module: {
        // 配置规则
        rules: [
            {
                test: /\.css$/,
                // css-loader:只负责将css进行加载,不负责解析和加载到index.html中
                // style-loader :将模块导出的内容作为样式并添加到 DOM 中
                use: [ "style-loader","css-loader"],
            },
            {
                test: /\.less$/,
                // 使用多个loader时,从右向左进行加载,使用对象还可以传递参数: { loader:"less-loader",arg:value},
                use:[
                    {loader:"style-loader"},
                    {loader:"css-loader"},
                    {loader:"less-loader"}
                ]
            },
            {
                test: /\.(png|jpg|gif)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            // 限制文件的大小,当加载的图片大于限制的时候使用file-loader进行加载,
                            // 小于limit将图片编译为base64字符串进行显示
                            limit:13000, //625664
                            // 设置打包路径和文件名
                            name:'img/[name].[hash:8].[ext]'
                        },
                    },
                ],
            },
            {
                test: /\.m?js$/,
                // 在进行ES6转ES5时只对src目录下js进行转化
                exclude: /(node_modules|bower_components)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['es2015']
                    }
                }
            },
            {
                test: /\.vue$/,
                use: {
                    loader: 'vue-loader',
                }
            }
        ],

    },
    // 解析
    resolve: {
        extensions:['.css','.js','.vue'],
        alias: {
            'vue$': 'vue/dist/vue.esm.js' //内部为正则表达式  vue结尾的
        }
    },
    // 插件
    plugins:[
        // 版权信息插件
        new webpack.BannerPlugin('最终版权归test所有'),
        new htmlWebpackPlugin({
            template:'index.html'
        }),
    ],

}

dev.config.js: 开发时配置

// proud.config.js:开发时需要的配置
const  baseConfig = require('./base.config.js')
const  webpackMerge=require('webpack-merge')
// 将base.config和dev.config.js进行合并

module.exports = webpackMerge.merge(baseConfig,{
    // 加载本地服务器
    devServer: {
        contentBase: './dist',
        // 实时监听页面
        inline: true,
        // 80端口
        port: 80,
        // 如下 npm run dev 后自动打开浏览器
        open: true,
    }
})

proud.config.js: 运行时配置

// proud.config.js:运行时都需要的配置
const uglifyJsPlugin = require('uglifyjs-webpack-plugin')
const  baseConfig = require('./base.config.js')
const  webpackMerge=require('webpack-merge')

// 将base.config和proud.config.js进行合并
module.exports=webpackMerge.merge(baseConfig,{
    // 插件
    plugins:[
        // 进行压缩
        new uglifyJsPlugin()
    ],
})

抽取完后把 webpack.config.js 删除掉,因为用不上了。

package.json 指定配置文件路径

  "scripts": {
    "build": "webpack  --config  ./build/proud.config.js",
    "dev": "webpack-dev-server  --config  ./build/dev.config.js"
  },

5.vue-cli

5.1vue-cli2

CLI是什么意思?

  • CLI是 Command-Line Interface, 翻译为命令行界面, 但是俗称脚手架.
  • Vue CLI是一个官方发布 vue.js 项目脚手架
  • 使用 vue-cli 可以快速搭建Vue开发环境以及对应的webpack配置.

1.安装vue

如果之前有安装先卸载避免版本不同出现问题 : npm uninstall -g @vue/cli(执行该命令)
安装指定版本: npm install @vue/cli@3.2.1 -g (选择该版本)
安装最新版本:npm install @vue/cli -g  
即可用vue-cli2也可以用vue-vli3的桥梁:npm install -g @vue/cli-init@4.3.1(执行该命令)

2.Vue-cli2初始化

Vue CLI2初始化项目:vue init webpack projectName(执行该命令)

Vue CLI3初始化项目: vue create projectName

// 安装不成功的时候, 可以清除缓存 (要从管理员权限打开)
npm clean cache --force

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XErStOfi-1649957421212)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20211226182549913.png)]

3.项目目录结构解析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qkhUUAop-1649957421213)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20211226184651762.png)]

**package.json:**上图是项目的整体目录结构。我们读这个项目结构的时侯要先从package.json开始读,首先是看package.json中的scripts中的build,可以看出来它是执行 node build/build.js, node是可以直接执行js文件的。package.json主要就是项目的描述,以及一些依赖模块的版本管理,以及一些脚本启动命令。

buuild目录: webpack.base.conf.js是webpack的公共配置;webpack.dev.conf.js是开发时配置;webpack.prod.conf.js是发布时配置

config目录: 该文件夹中主要都是一些变量的配置,比如index.js,里面会有一些变量的配置,这些变量会被上面的builid文件夹里面的js文件所引用。当然了,index.js中的变量值也是可以自定义改变的。所以说config文件夹和build文件夹都是webpack的相关配置。

node_modules目录: 该文件夹中存放的是项目依赖的包

.babelrc文件:.babelrc主要就是对哪些浏览器可以进行es6转es5进行了限制。如果该浏览器占市场的份额大于1%且该浏览器的版本是它的最新的两个版本之一,那么可以进行es6到es5的转换。如果该浏览器是版本小于8的IE浏览器,那不要直接不考虑转换。总之,.babelrc就是在进行es转换的时候需要读取的配置文件。

.editorconfig: 该文件主要就是对代码进行一些统一的设置

.eslintignore: 表示我们在写代码的时候,有些文件夹里面的代码就算写的不太规范,但是我们也可以忽略它的不规范,在这个文件里面配置。

slintrc.js文件: 用于设置如何进行代码检查。

.gitignore文件: 表示当我们向服务器上传东西的时候,有些东西是可以不要上传的,可以在该文件中配置哪些东西不上传。

.postcssrc.js文件: 项目在进行css转换的时候,所需要读取的配置文件。

index.html文件: html文件的一个模板,打包的时候会以这个index.html为模板生成一个html文件放入dist文件夹中。

package.json文件: 主要用于对项目所依赖包的配置和一些指令的设定(scripts属性)。

package-lock.json文件: 在package.json中有DevDependences中有类似3.1.0或者∧3.1.0。和∧的含义不同,~表示安装依赖的版本是大于3.1.0但是小于3.2.0 ,也就是3.1.x , 大版本号和次版本号不变;∧表示安装的版本是大于3.1.0但是小于4.0.0的,也就是3.x.x,大版本号不能变。所以我们知道当在这里使用了这两个符号之后,安装依赖的版本是一个范围,那么到底最终它安装的是哪个版本的呢?package-lock.json就是记录项目最终安装的依赖的版本是哪个的。

Vue程序运行过程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8bTrWMin-1649957421214)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20211226203825391.png)]

ue build的选择-runtiome+compilter和runtiome-only 区别

runtiome+compilter 和 runtiome-only 区别只在 main.js文件里面

// runtiome+compilter 流程 
template -> ast (抽象语法树) -> render -> Virtual dom (虚拟DOM) -> UI

// runtiome-only 流程  (1. 性能更好  2. 代码量更少)
render -> Virtual dom (虚拟DOM) -> UI 
------------------------------------------------------------------------------------------------------------------

rebder函数的使用 (runtiome-only)


-- 使用方式一: 
return createElement('标签', '相关数据对象, 可不传', ['内容数组']) 

new Vue({
    el: "#app",
    render: (createElement) => {
        //render函数基本使用
        return createElement('div', {class: 'box'}, ['codewhy']) 
        //嵌套render函数
        return createElement('div', {class: 'box'}, ['codewhy', createElement('h2', ['标题啊'])]) 
    }
})

-- 使用方式二: 传入一个组件对象  
const cpn = Vue.component('cpn', {
    template: '<div>我是cpn组件</div>',
    data () {
        return {
            
        }
    }
})

new Vue({
    el: "#app",
    render: (createElement) => {
        return createElement(cpn) 
    }
})

npm run build 流程 :

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-limzaufn-1649957421214)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20211226203922728.png)]

npm run dev 流程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gTAcDCKG-1649957421214)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20211226204019868.png)]

5.2 vue-cli3

vue-cli 3 与 vue-cli2 版本有很大区别

  • vue-cli 3 是基于 webpack 4 打造,vue-cli 2 还是 webapck 3
  • vue-cli 3 的设计原则是“0配置”,移除的配置文件根目录下的,build和config等目录
  • vue-cli 3 提供了 vue ui 命令,提供了可视化配置,更加人性化
  • 移除了static文件夹,新增了public文件夹,并且index.html移动到public中

使用 vue create vuecli3 创建vue-cli3项目

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sC8VvWIS-1649957421215)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20211226205803478.png)]

修改vue配置信息方式:

  • vue-cli3图形化界面配置:在控制台输入: vue ui
  • node_modules/@vue/cli-service下进行修改
  • 在当前项目目录下 创建 vue.config.js 自定义配置

6.vue-router

路由器提供了两种机制: 路由和转送.

  • 路由是决定数据包从来源到目的地的路径.
  • 转送将输入端的数据转移到合适的输出端.
    路由中有一个非常重要的概念叫路由表.
  • 路由表本质上就是一个映射表, 决定了数据包的指向

先使用vue-cli构建一个项目,构建的时候选择vueroter

后端路由: 对于普通的网站,所有的超链接都是URL地址,所有的URL地址都对应服务器上对应的资源;

前端路由: 对于单页面应用程序来说,主要通过URL中的hash(#号)来实现不同页面之间的切换,同时,hash有一个特点:HTTP请求中不会包含hash相关的内容;所以,单页面程序中的页面跳转主要用hash实现;不会涉及到页面刷新,只是切换组件

前端可以通过: 如下代码来直接进行页面的跳转

window.location.href="https://www.taobao.com/";

前端前进和后退:

// 向前进
history.pushState({},'name','login')
// 向后退
history.replaceState({},'name','login')

// 向前进
history.go(1) === history.back()
// 向后退
history.go(-1) ===  history.forward()

vue-router了解

  • vue-router是Vue.js官方的路由插件,它和vue.js是深度集成的,适合用于构建单页面应用。
  • 我们可以访问其官方网站对其进行学习: https://router.vuejs.org/zh/

vue-router是基于路由和组件的

  • 路由用于设定访问路径, 将路径和组件映射起来.
  • 在vue-router的单页面应用中, 页面的路径的改变就是组件的切换.

6.1 vue-router配置

1.在router目录下的 index.js 文件中添加如下配置:


// 导入vueRouter
import VueRouter from "vue-router";
import Vue from "Vue";

// 1.通过Vue.use(plugin),安装插件
Vue.use(VueRouter)
// 2.创建路由对象
const  router = new VueRouter({
  // 配置路由和组件相关的东西
  routers:[
    
  ]
})

// 3. 将router传入到Vue实例中

export default router

2.在 main.js 中使用即可

import Vue from 'vue'
import App from './App'
// 导入router
import router from './router/index'

Vue.config.productionTip = false

new Vue({
  el: '#app',
  router,
  render: h => h(App)
})

6.2 路由映射配置

该标签是一个vue-router中已经内置的组件, 它会被渲染成一个标签

该标签会根据当前的路径, 动态渲染出不同的组件

网页的其他内容, 比如顶部的标题/导航, 或者底部的一些版权信息等会和处于同一个等级

在路由切换时, 切换的是挂载的组件, 其他内容不会发生改变

1.在components下新建如下两个文件

**About.vue:**内如如下

<template>
  <div id="about">
    <h2>我是关于页标题</h2>
    <p>我是关于页内容</p>
  </div>
</template>

<script>
  export default {
    name: "About"
  }
</script>

<style scoped>
  #about {
    width: 300px;
    height: 300px;
    border: red solid 1px;

  }
</style>

Home.vue:内如如下

<template>
  <div id="home">
    <h2>我是首页标题</h2>
    <p>我是首页内容</p>
  </div>
</template>

<script>
  export default {
    name: "Home"
  }
</script>

<style scoped>
  #home {
    width: 300px;
    height: 300px;
  border: red solid 1px;
  }
</style>

**2.router 下的index.js中添加如下内容 **

// 配置路由相关的信息
import VueRouter from 'vue-router'
import Vue from 'vue'

import Home from '../components/Home'
import About from '../components/About'

// 解决该错误NavigationDuplicated: Avoided redundant navigation to current location: "/about".
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
  return originalPush.call(this, location).catch(err => err)
}


// 1.通过Vue.use(插件), 安装插件
Vue.use(VueRouter)

// 2.创建VueRouter对象
const routes = [
  {
    path: '',
    // redirect重定向,默认跳转路径
    redirect: '/home'
  },
  {
    path: '/home',
    component: Home
  },
  {
    path: '/about',
    component: About
  }
]
const router = new VueRouter({
  // 配置路由和组件之间的应用关系
  routes,
  // 改变模式 默认是hash模式(有#号)
  mode: 'history',
  // 选择当前组件的时候需要添加的className
  linkActiveClass: 'active'
})

// 3.将router对象传入到Vue实例
export default router


3.App.vue中添加如下内容:

<template>
  <div id="app">
<!-- tag:以什么形式的标签进行渲染,
   replace:不会留下history记录, 所以指定replace的情况下, 后退键返回不能返回到上一个页面中
 -->
      <router-link to="/home" tag="button" replace>首页</router-link>
      <router-link to="/about" tag="button" replace>关于</router-link>
      <!-- 此处是一个占位标签,当路由跳转到某个组件的时候,该组件中的内容就会被渲染到此标签中 -->
       <router-view> </router-view>
  </div>
</template>

<script>

export default {
  name: 'App'
}
</script>

<style>
  .active{
      color: red;
  }
</style>

使用代码手动跳转

<template>
  <div id="app">
<!--  自定义事件通过$router进行控制跳转  -->
    <button @click="homeClick"   tag="button" >首页</button>
    <button @click="aboutClick"   tag="button" >关于</button>
       <router-view> </router-view>
  </div>
</template>

<script>

export default {
  name: 'App',
  methods:{
   // Vue-router在所有组件里都加入了$router
    homeClick(){

      console.log("homeClick")

      // this.$router.replace('/home') // 可以点浏览器回退
      this.$router.push('/home')    // 不可以点浏览器回退

    },
    aboutClick(){

       this.$router.push('/about')
      // this.$router.replace('/about')
       console.log("aboutClick")
    }
  }
}
</script>

<style>
  .active{
      color: red;
  }
</style>

6.3 动态路由(参数传递)

某些情况下,一个页面的path路径可能是不确定的,比如我们进入用户界面时,希望是如下的路径:

  • /user/userId,路径后面跟上用户的id
  • 这种path和Component的匹配关系,我们称之为动态路由(也是路由传递数据的一种方式)

1.添加一个 User.vue 内容如下:

<template>
    <div id="user">
      <div id="home">
        <h2>我是用户页标题</h2>
        <p>我是用户页内容</p>
        <h2>用户id:{{userId}}</h2>
      </div>
    </div>
</template>

<script>
    export default {
        name: "User",
      computed:{
          userId(){
            // $route:指向的是当前活跃的路由
            return this.$route.params.userId;
          }
      }
    }
</script>

<style scoped>
#user{
  width: 300px;
  height: 300px;
  border: red solid 1px;
}
</style>

  1. router目录下的 index.js 中添加如下配置:

    // 2.创建VueRouter对象
    const routes = [
      {
        path: '',
        // redirect重定向,默认跳转路径
        redirect: '/home'
      },
      {
        path: '/home',
        component: Home
      },
      {
        path: '/about',
        component: About
      },{
      path: '/user/:userId',
        component: User
      }
    ]
    

3.在 App.vue 中添加如下代码:

<template>
  <div id="app">
    <router-link  to="/home" tag="button" replace>首页</router-link>
    <router-link  to="/about" tag="button" replace>关于</router-link>
    <router-link  :to="'/user/'+userId" tag="button" replace>用户</router-link>
       <router-view> </router-view>
  </div>
</template>

<script>

export default {
  name: 'App',
  data(){
    return {userId:"101"}
  }
}
</script>

<style>
  .active{
      color: red;
  }
</style>

** r o u t e r : 是 当 前 处 于 活 动 状 态 的 路 由 包 含 的 参 数 信 息 , 在 ∗ ∗ t h i s . router: 是当前处于活动状态的路由包含的参数信息,在 **this. router:this.route.params .attruibuteName即可获取到该属性值,$route为当前router跳转对象里面可以获取name、path、query、params等

**this. r o u t e . p a r a m s : ∗ ∗ 是 V u e R o u t e r 实 例 , 想 要 导 航 到 不 同 U R L , 则 使 用 route.params :** 是VueRouter实例,想要导航到不同URL,则使用 route.paramsVueRouterURL使router.push方法

6.4 路由懒加载

路由懒加载的意义

  • 当打包构建应用时,Javascript 包会变得非常大,影响页面加载。
  • 如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jZ8wRN6j-1649957421216)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20211227041854843.png)]

路由懒加载做了什么?

  • 路由懒加载的主要作用就是将路由对应的组件打包成一个个的js代码块.
  • 只有在这个路由被访问到的时候, 才加载对应的组件

懒加载的三种方式

  • 方式一: 结合Vue的异步组件和Webpack的代码分析
    • const Home = resolve => { require.ensure(['../components/Home.vue'], () => { resolve(require('../components/Home.vue')) })};
      
  • 方式二: AMD写法
    • const About = resolve => require(['../components/About.vue'], resolve);
      
  • 方式三: 在ES6中, 我们可以有更加简单的写法来组织Vue异步组件和Webpack的代码分割.
    • const Home = () => import('../components/Home.vue')<font color='red'>(最终使用)</font>
      

6.5 路由嵌套

嵌套路由是一个很常见的功能

  • 比如在home页面中, 我们希望通过/home/news和/home/message访问一些内容.
  • 一个路径映射一个组件, 访问这两个路径也会分别渲染两个组件.

嵌套路由实现

1.在components下准备两个组件作为Home的子组件

HomeMessage.vue:内容如下

<template>
<div>
  <ul>
    <li>消息0</li>
    <li>消息1</li>
    <li>消息2</li>
    <li>消息3</li>
    <li>消息4</li>
  </ul>
</div>
</template>

<script>
    export default {
        name: "HomeMessage"
    }
</script>

<style scoped>

</style>

HomeNews.vue:内容如下


<template>
<div>
  <ul>
    <li>新闻0</li>
    <li>新闻1</li>
    <li>新闻2</li>
    <li>新闻3</li>
    <li>新闻4</li>
  </ul>
</div>
</template>

<script>
    export default {
        name: "HomeNews"
    }
</script>

<style scoped>

</style>

2.在 router 下的 index.js 下降这两个组件注册为Home的子组件

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

    ]
  },

3.在 Home 页进行引用这两个组件

<template>
  <div id="home">
    <h2>我是首页标题</h2>
    <router-link to="/home/news" tag="button">新闻</router-link>
    <router-link to="/home/message" tag="button">消息</router-link>
   <router-view></router-view>
  </div>
</template>

6.6 参数传递

**参数传递:**也就是从一个路由跳转到另外一个路由的时候,我们希望传递一些消息到跳转的路由

params的类型:

  • 配置路由格式: /router/:id
  • 传递的方式: 在path后面跟上对应的值
  • 传递后形成的路径: /router/123, /router/abc
    query的类型:
  • 配置路由格式: /router, 也就是普通配置
  • 传递的方式: 对象中使用query的key作为传递方式
  • 传递后形成的路径: /router?id=123, /router?id=abc

**实现方式1:**通过query的方式

1.准备 Profile.vue 页面

<template>
<div>
  <h1>我的档案</h1>
  <ul>
    <li>姓名:{{$route.query.name}}</li>
    <li>年龄:{{$route.query.age}}</li>
    <li>性别:{{$route.query.sex}}</li>
  </ul>
</div>
</template>

<script>
    export default {
        name: "Profile",
    }
</script>

<style scoped>

</style>

2.在 router 下的 index.js 下配置路由

  {
    path: '/profile',
    component: Profile
  }

3.在 App.vue 下配置如下内容

<router-link  :to="{path: '/profile', query: {name: '张三', age: 18,sex:''}}"tag="button" >档案</router-link>

实现方式2:自定义点击事件

1.准备 Order.vue 页面

<template>
    <div>
      <h2>订单页面</h2>
      <ul>
        <li>订单号:{{$route.query.orderId}}</li>
        <li>物品名称:{{$route.query.productName}}</li>
        <li>价格:{{$route.query.price}}</li>
      </ul>
    </div>
</template>

<script>
    export default {
        name: "Order",

    }
</script>

<style scoped>

</style>

2.在 router 下的 index.js 下配置路由

 {
    path: '/order',
    component: Order
  }

3.在 App.vue 添加如下配置

<template>
  <div id="app">
    <button @click="orderClick"tag="button" replace>订单</button>
    <router-view></router-view>

  </div>
</template>

<script>
  export default {
    name: 'App',
    methods: {
      orderClick() {
        this.$router.push({
          path: '/order',
          query: {orderId: 101, productName: '奥特曼真皮大衣', price: 8888}
        })
      }

    },
   
  }
</script>

6.7 导航守卫

为什么使用守卫导航?
我们来考虑一个需求: 在一个 SPA 应用中, 如何改变网页的标题呢?

  • 网页标题是通过 title 来显示的, 但是 SPA 只有一个固定的HTML, 切换不同的页面时, 标题并不会改变.

  • 但是我们可以通过 JavaScript 来修改title的内容 .window.document.title = ‘newTitle’.

  • 那么在Vue项目中, 在哪里修改? 什么时候修改比较合适呢?

  • 普通的修改方式:

  • 我们比较容易想到的修改标题的位置是每一个路由对应的组件.vue文件中.

  • 通过mounted声明周期函数, 执行对应的代码进行修改即可.

  • 但是当页面比较多时, 这种方式不容易维护(因为需要在多个页面执行类似的代码).

什么是导航守卫?

  • vue-router提供的导航守卫主要用来监听监听路由的进入和离开的.
  • vue-router提供了beforeEach和afterEach的钩子函数, 它们会在路由即将改变前和改变后触发.

1.在 router index.js 的路由配置中增加一个 meta 属性,用于存放 title ,并且实现一个钩子函数

const routes = [
  {
    path: '',
    // redirect重定向,默认跳转路径
    redirect: '/home'
  },
  {
    path: '/home',
    component: Home,
    meta: { title:'首页' },
    children: [
      {
        path: '',
        redirect: 'news'
      },
      {
        path: 'message',
        component: HomeMessage
      },
      {
        path: 'news',
        component: HomeNews
      },
    ]
  },
  {
    path: '/about',
    component: About,
    meta: { title:'关于' }
  },
  {
    path: '/user/:userId',
    component: User,
    meta: { title:'用户' }
  },
  {
    path: '/profile',
    component: Profile,
    meta: { title:'档案' }
  },
  {
    path: '/order',
    component: Order,
    meta: { title:'订单' }
  }
]

/**
 * 跳转之前调用
 * to: 即将要进入的目标的路由对象.
 *from: 当前导航即将要离开的路由对象.
 *next: 调用该方法后, 才能进入下一个钩子.
 */
router.beforeEach((to,from,next)=>{
    window.document.title=to.matched[0].meta.title;
    next();
})

/**
 * 跳转之后调用
 * to: 即将要进入的目标的路由对象.
 *from: 当前导航即将要离开的路由对象.
 */
router.afterEach(((to, from) => {
     console.log(to)
}))

补充一:如果是后置钩子, 也就是afterEach, 不需要主动调用next()函数.

补充二: 我们使用的beforeEach导航守卫, 被称之为全局守卫.

也可以在某个路由下添加一个该路由独享的 beforeEach

  {
    path: '/order',
    component: Order,
    meta: { title:'订单' },
    beforeEnter:(to,form,next)=>{
      console.log('进入订单页面')
     next()
    }
  }

组件内导航守卫:

const Foo = {
  template: `...`,
  beforeRouteEnter(to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate(to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave(to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}

6.8 keep-alive

  • keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
  • 它们有两个非常重要的属性:
    • <font color='red'>include </font>字符串或正则表达,只有匹配的组件会被缓存
      
    • <font color='red'>exclude </font>字符串或正则表达式,任何匹配的组件都不会被缓存
      
  • router-view 也是一个组件,如果直接被包在 keep-alive 里面,所有路径匹配到的视图组件都会被缓存:

**router-view: **会被缓存下来,并不会频繁创建

 <!-- exclude:组件name 对某些路径进行排除,如果是多个添加 , 继续添加
  include:组件name  只有匹配的组件会被缓存
-->
<keep-alive exclude="Profile">
      <router-view></router-view>
    </keep-alive>

在 Home.app 下添加如下内容,保证点击回来时,还是之前的界面

<script>
  export default {
    name: "Home",
    data(){
      return {path:"/home/news"}
    },
    // 该组件创建时调用
    created() {
      console.log("Home created")
    },
   // 该组件销毁时调用
    destroyed() {
    console.log("Home destroyed")
    },
    // 进入到该路由时会进入到该函数
    activated() {
    this.$router.push(this.path)
      console.log("Home activated")
    },
    // 在该页面离开之前调用
    beforeRouteLeave(to,form,next){
      // 在离开之前把当前的路径保存下来,下次进入时直接使用该路径
      this.path=this.$route.path
      next();
    }
  }
</script>

7.vueX

官方解释:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。font>
它采用 集中式存储管理 应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。

状态管理到底是什么?
状态管理就是响应式的管理多个组件的共享状态,比如说多个页面需要共享 token

单界面的状态管理:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-prS22S3y-1649957421216)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20211228145509680.png)]

多界面的状态管理:

我们现在要做的就是将共享的状态抽取出来,交给我们的vueX,统一进行管理。之后,你们每个视图,按照我规定好的规定,进行访问和修改等操作。全局单例模式这就是Vuex背后的基本思想。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BOWuaUex-1649957421216)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20211228145136351.png)]

vuex状态管理图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qOu7Hosb-1649957421217)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20211228153446685.png)]

7.1 state

1.安装vuex: npm install vuex --save

2.在 src 目录下 新建 store目录 并创建 index.js 文件,添加如下内容

import Vue from "vue";
import  Vuex from 'vuex' // 导入Vuex
// 1.安装vuex插件
Vue.use(Vuex)
// 2.注册vuex对象
const  store = new Vuex.Store({
  // 共享状态属性
  state:{
    counter:1
  },
  mutations:{

  },
  actions:{

  },
  getters:{

  },
  modules:{

  }
})
//导出vuex对象
export default store


3.注册到 App.vue 实例上 : 通过无论在那个组件中通过 $store.state.counter 就可以进行访问 counter

<script>
  import HelloVuex from "./components/HelloVuex";
  import store from "./store/index"
export default {
  name: 'App',
  store,
  components:{
    HelloVuex
  }  ,
  data(){
    return {
      message:"我是App组件",
      counter:0
    }
  },
  methods:{
    increment(){
      // 可以修改,但是不建议这样做
      this.$store.state.counter+=1
    },
    decrement(){
      this.$store.state.counter-=1
    }
  }
}
</script>

安装 devtools : https://chrome.pictureknow.com/extension?id=d50143a5f53d406dbe992277bfc90521

7.2mutations

1.在store目录下的 index.js mutations 中增加如下内容

 mutations:{
    // 会自动将state参数传递进来
    increment(state){
     state.counter+=1;
    },
    decrement(state){
     state.counter-=1;
    }
  },

2.在 App.vue 中进行调用(此方法修改可以通过devtools工具查看counter的修改记录)

  methods:{
    incrementApp(){
      //  通过commit进行修改可以监听到修改的时间以及次数
      this.$store.commit('increment')
    },
    decrementApp(){
      this.$store.commit('decrement')
    }
  }

**muation参数传递:**可以向 store.commit 传入额外的参数,即 mutation 的 载荷(payload):

在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读

mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}
store.commit('increment', {
  amount: 10
})

对象风格的提交方式

提交 mutation 的另一种方式是直接使用包含 type 属性的对象:

store.commit({
  type: 'increment',
  amount: 10
})

当使用对象风格的提交方式,整个对象都作为载荷传给 mutation 函数,因此 handler 保持不变:

mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}

使用常量替代 Mutation 事件类型

// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
// store.js
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'

const store = new Vuex.Store({
  state: { ... },
  mutations: {
    // 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名
    [SOME_MUTATION] (state) {
      // mutate state
    }
  }
})

7.3getters

**getters:**有时候我们需要从 store 中的 state 中派生出一些状态,不需要直接获取state,而是通过对state进行一些处理再返回state就可以通过getter来处理

  // 获取共享属性
  getters:{
      getCounter(state){
        return state.counter*state.counter;
      }
  },

获取 getters

   <span>{{$store.getters.getCounter}} </span>

getters 接受 state 作为其第一个参数,将 getters 作为第二个参数

  getters:{
      getCounter(state,getters){
        console.log(getters.getUserList)
        return state.counter*state.counter;
      },
    getUserList(state){
       return  state.user;
    }
  },

getters 接收参数,根据参数筛选出数据并返回

    getUserList(state){
       return  function (age) {
         return  state.user.filter(user=>user.age>18)
       };
    }
//调用,筛选出年龄大于18的用户
<p>getUserList:{{$store.getters.getUserList(18)}} </p>

state中的属性如果一开始就初始化好,都会被加入到响应式系统中,如果一开始未初始化,然后进行添加,并不会加入到vex的响应式系统中。如果要实现响应式需要利用另外一个方法:Vue.set(object,key[String|Number],value) 例如: Vue.set(user,‘name’,‘小明’) ,如果想从响应式系统中删除属性,那么可以使用 : Vue.delete(object,key) 例如: Vue.delete(user,name)

通常情况下, Vuex要求我们Mutation中的方法必须是同步方法.

  • 主要的原因是当我们使用devtools时, 可以devtools可以帮助我们捕捉mutation的快照.
  • 但是如果是异步操作, 那么devtools将不能很好的追踪这个操作什么时候会被完成.

7.4 actions

不要再Mutation中进行异步操作. Action类似于Mutation, 但是是用来代替Mutation进行异步操作的.

同步使用:Mutation

异步使用:Action

 <button @click="updateUserInfo('tom')">修改用户信息</button>

  methods:{
    updateUserInfo(newName){
      this.$store.dispatch('updateUserInfoActions',{name:newName})
    }
  }

  // 异步操作
  actions:{
   updateUserInfoActions(context,payload){
     // 模拟异步操作
     setTimeout(()=>{
       context.commit('updateUserInfoMutations',payload)
     },1000)
    }
  }
      
   mutations:{
    updateUserInfoMutations(state,payload){
      state.userInfo.name=payload.name;
    }
  }     

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3io1M0rX-1649957421217)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20211228200817420.png)]

同样的, 也是支持传递payload

如果我们想要知道异步处理结果是否完成可以使用以下方式进行操作:

  methods:{
    updateUserInfo(newName){
       // 因为那么边返回的是一个promise对象,这边可以直接后面继续调用then和catch方法 
      this.$store
        .dispatch('updateUserInfoActions',{name:newName})
        .then(result=>{
          console.log("回传过来的数据="+result)
        }).catch(error=>{
          console.log("错误信息="+error)
      })
    }
  }

// 异步操作
  actions:{
   updateUserInfoActions(context,payload){
     // 模拟异步操作
    return   new Promise(((resolve, reject) => {
       setTimeout(()=>{
         context.commit('updateUserInfoMutations',payload)
         console.log("传递过来的数据[处理成功]="+payload.name)
         resolve("我是执行成功回传的数据")
         // 如果失败调用该方法 返回错误信息
         //reject("我是回传的错误信息[处理失败]")
       },1000)
     }))
    }
  }
      
  mutations:{
	 teUserInfoMutations(state,payload){
      state.userInfo.name=payload.name;
    }
  }  

7.5 modules

  • Module是模块的意思, 为什么在Vuex中我们要使用模块呢?
  • Vue使用单一状态树,那么也意味着很多状态都会交给Vuex来管理.
  • 当应用变得非常复杂时,store对象就有可能变得相当臃肿.
  • 为了解决这个问题, Vuex允许我们将store分割成模块(Module), 而每个模块拥有自己的state、mutations、actions、getters等
  • 其实modules中的对象实例对象都是挂载到rootState上的

state:

const loginModules = {
  state: {
    name:'admin'
  }
    
 // 使用 : <p>state:{{$store.state.login.name}}</p>    

mutations:

<button @click="updateName('root')"> 修改name</button>

updateName(newName){
      this.$store.commit('setName',{name:newName})
    }

  mutations: {
    setName(state,payload){
      state.name=payload.name
    }
  }

getters:

  getters: {
    // 可以多一个参数,那就是根对象
    getRoot(state,getters,rootState){
      return "state.name="+state.name+" getters.getName="+getters.getName+" rootState.counter="+rootState.counter
    }
  }

// 使用 : <p> getRoot(state,getters,rootState): {{$store.getters.getRoot}}</p>

actions:

<button @click="AsyncUpdateNameM"> AsyncUpdateNameM</button>

  methods:{
    AsyncUpdateNameM(){
      this.$store.dispatch('AsyncUpdateName',{name:'lisa'})
    }
  }

actions: {
    // context: getters,rootGetters,dispatch, commit,state,rootState
    AsyncUpdateName(context,payload){
      console.log(context)
      context.commit('setName',{name:payload.name})
    }
  }

 mutations: {
    setName(state,payload){
      state.name=payload.name
    }
  },

对于大型应用,我们会希望把 Vuex 相关代码分割到模块中。下面是项目结构示例:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1de8Ifz3-1649957421217)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20211228224524995.png)]

8.axios

axios 是一个基于 Promise 用于浏览器和 nodejs HTTP client。

  • 从浏览器创建 XMLHttpRequests
  • 从node.js 发出 http 请求
  • 支持 Promise API
  • 拦截请求(request) 和响应(response)
  • 转换请求和响应数据
  • 终止请求
  • 自动转换 JSON 数据
  • Client 端支持防范 XSRF

安装: npm install axios

cdn: <script src="https://unpkg.com/axios/dist/axios.min.js"></script>

axios支持多种请求方式:

  • axios(config)
  • axios.request(config)
  • axios.get(url[, config])
  • axios.delete(url[, config])
  • axios.head(url[, config])
  • axios.post(url[, data[, config]])
  • axios.put(url[, data[, config]])
  • axios.patch(url[, data[, config]])

当使用别名方法时,不需要在在config中指定 url, method, 和 data 属性。

8.1 创建axios实例

// 直接使用导入的是全局的,耦合度太高,开发中不适合,开发中需要自己定制
const instance = axios.create({
  baseURL: 'https://www.taobao.com/',
  timeout:5000,
  headers: {'X-Custom-Header': 'foobar'}
});

8.2 请求参数配置

只有url是必须的,其余的是可选项,默认是get请求

{
  // `url` 是用来请求的服务器URL
  url: '/user',

  // `method` 是发请求时的请求方式
  method: 'get', // 默认是get

  // `baseURL` 会被加到`url`前面,除非`url`已经写全了。
  // 它可以方便的为axios实例设置`baseURL`,然后传递相关联的URLs给实例对应的方法
  baseURL: 'https://some-domain.com/api/',

  // `transformRequest`允许请求数据在发送到服务器之前对其进行更改
  // 仅适用于'PUT', 'POST', 'PATCH'
  // 数组中的最后一个函数必须返回一个string或者Buffer、ArrayBuffer、FormData或Stream的实例
  // 你可以修改headers对象.
  transformRequest: [function (data, headers) {
    // Do whatever you want to transform the data

    return data;
  }],

  // `transformResponse` 允许数据在传到then/catch之前对其进行更改
  transformResponse: [function (data) {
    // Do whatever you want to transform the data

    return data;
  }],

  // `headers` 发送自定义headers
  headers: {'X-Requested-With': 'XMLHttpRequest'},

  // `params` URL参数,必须做位一个普通对象或者URLSearchParams对象发送
  params: {
    ID: 12345
  },

  // `paramsSerializer` 负责序列化`params`的可选函数
  // (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
  paramsSerializer: function (params) {
    return Qs.stringify(params, {arrayFormat: 'brackets'})
  },

  // `data` 作为请求主体发送的数据
  // 仅适用于 'PUT', 'POST', 'PATCH'
  // 当没有设置`transformRequest`时, 必须是以下类型之一:
  // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
  // - Browser only: FormData, File, Blob
  // - Node only: Stream, Buffer
  data: {
    firstName: 'Fred'
  },

  // `timeout` 指定请求超时的时间,单位:毫秒
  // 如果请求的时间超过`timeout`, 请求即被中止
  timeout: 1000, // default is `0` (no timeout)

  // `withCredentials` 跨站点访问是否适用证书
  withCredentials: false, // 默认不使用

  // `adapter` 允许自定义处理请求,这使得测试更容易。
  // 返回一个promise并提供一个有效的响应 (see lib/adapters/README.md).
  adapter: function (config) {
    /* ... */
  },

  // `auth` 使用HTTP认证, 并提供凭据。
  // 设置一个带`Authorization'的header,覆盖任何现有的用`headers`参数设置的`Authorization'自定义headers.
  auth: {
    username: 'janedoe',
    password: 's00pers3cret'
  },

  // `responseType` 服务器将响应的数据类型,包括'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
  responseType: 'json', // default

  // `responseEncoding` 用于解码响应的编码方式
  // 注: 忽略`responseType`的'stream',或者客户端请求
  responseEncoding: 'utf8', // default

  // `xsrfCookieName` 用作 xsrf 令牌的值的cookie的名称
  xsrfCookieName: 'XSRF-TOKEN', // default

  // `xsrfHeaderName` 携带xsrf令牌值的http header的名称
  xsrfHeaderName: 'X-XSRF-TOKEN', // default

  // `onUploadProgress` 允许处理上传的进度事件
  onUploadProgress: function (progressEvent) {
    // Do whatever you want with the native progress event
  },

  // `onDownloadProgress` 允许处理下载的进度事件
  onDownloadProgress: function (progressEvent) {
    // Do whatever you want with the native progress event
  },

  // `maxContentLength` 定义允许的http响应内容的最大bytes
  maxContentLength: 2000,

  // `validateStatus` 对于一个给定的HTTP响应状态码,是resolve 还是reject 这个promise。如果`validateStatus`返回`true` (或者 `null`,`undefined`), 这个promise will be resolved; 否则,这个promise will be rejected.
  validateStatus: function (status) {
    return status >= 200 && status < 300; // default
  },

  // `maxRedirects` 在node.js中要遵循的重定向的最大数量。
  // 如果设为0,不会有重定向,默认5
  maxRedirects: 5, // default

  // `socketPath` 定义一个在node.js里面用的UNIX Socket。
  // e.g. '/var/run/docker.sock' 是发送请求到docker后台.
  // 只能指定`socketPath`和`proxy`中的一个。
  // 如果都被指定,按`socketPath`的生效。
  socketPath: null, // default

  // `httpAgent` and `httpsAgent` 在node.js中,分别执行http和https请求时使用的自定义代理。
  // 允许配置类似`keepAlive`这样在默认情况下不启用的选项。
  httpAgent: new http.Agent({ keepAlive: true }),
  httpsAgent: new https.Agent({ keepAlive: true }),

  // 'proxy' 定义代理服务器的主机名和端口。
  // 你也可以用常规的`http_proxy`和`https_proxy`环境变量来定义你的代理。
  // 如果给你的代理配置使用环境变量,你也可以定义一个不代理的`no_proxy`环境变量,内容是一个以逗号分隔的域名列表。
  // 设成`false`就禁用代理,忽略环境变量。
  // `auth`表示HTTP Basic auth会被用于连接到代理,并提供凭证。
  // 这将设置一个`Proxy-Authorization` header,覆盖先前`headers`参数里面设置的`Proxy-Authorization` 自定义headers。
  proxy: {
    host: '127.0.0.1',
    port: 9000,
    auth: {
      username: 'mikeymike',
      password: 'rapunz3l'
    }
  },

  // `cancelToken` 指定可用于取消请求的取消令牌
  // (详情参阅下面的消除部分)
  cancelToken: new CancelToken(function (cancel) {
  })
}

8.3 响应模式

响应体中包含如下信息:

{
  // `data` 提供服务器的响应
  data: {},

  // `status` 来自服务器响应的HTTP状态码
  status: 200,

  // `statusText` 来自服务器响应的HTTP状态消息
  statusText: 'OK',

  // `headers` 来自服务器响应的headers,所有header names都是小写
  headers: {},

  // `config` 提供了`axios`请求的配置
  config: {},

  // `request` 生成这个响应的请求
  // 这是node.js中最后的客户端请求实例(重定向)和浏览器XMLHttpRequest实例
  request: {}
}

8.4 全局axios默认值

你可以给axios对象设置默认配置

axios.defaults.baseURL = 'https://www.taobao.com/';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/json';

创建 axios 对象后也可继续修改默认值

// 创建实例时设置默认配置
const instance = axios.create({
axios.defaults.baseURL = 'https://www.taobao.com/';
});

// 实例创建后改变默认配置
axios.defaults.baseURL = 'https://jd.com/';

**配置优先级顺序:**配置将按优先顺序改变,顺序是 lib/defaults.js库中的默认值,然后是实例的 defaults 属性,最后 config 请求的参数。后者的配置优

可以使用validateStatus配置选项定义自定义HTTP状态码错误范围。

// 仅当状态代码大于或等于500时拒绝
return status < 500; 

8.1 拦截器和axios封装

axios提供了拦截器,用于我们在发送每次请求或者得到相应后,进行对应的处理。

import  axios from 'axios'
export function request(config) {
  // 创建 axios 实例
 const instance = axios.create({
   baseURL:'http://123.207.32.32:8000/',
   timeout:5000
 })

// 添加请求拦截器
  instance.interceptors.request.use(config=>  {
    // 可以做一些请求过滤
    return config;
  }, error => {
    // 失败时处理的逻辑
    return Promise.reject(error)
  });

// 添加响应拦截器
  instance.interceptors.response.use(response =>  {
    // 响应成功处理
    return response.data;
  }, error =>  {
    // 响应错误信息处理
    return Promise.reject(error)
  });
  
  // 返回一个axios,axios实际上是一个promise对象,调用者可以自己调用then或catch进行处理
  return instance(config)
}

// 调用者
request({
  url:'/home/multidataq'
}).then(result=>{
  console.log("result: ",result)
}).catch(error=>{
   console.log("error: ",error)
})



9. 附录

9.1 ES6 补充

**let关键字描述: **

  • let关键字就是用来声明变量的
  • 使用let关键字声明的变量具有块级作用域
  • 在一个大括号中 使用let关键字声明的变量才具有块级作用域 var关键字是不具备这个特点的
  • 防止循环变量变成全局变量,使用let关键字声明的变量没有变量提升

const关键字描述:

  • 使用const关键字声明的常量具有块级作用域
  • 使用const关键字声明的常量必须赋初始值
  • 常量声明后值不可更改

对象增强写法:

    let id=101;
    let name='admin';
    let email='admin@qq.com';
    let telephone='12345678910';
    // ES5写法
    let user1={id:id,name:name,email:email,telephone:telephone};
   console.log(JSON.stringify(user1));//{"id":101,"name":"admin","email":"admin@qq.com","telephone":"12345678910"}
    // ES6新语法
    let user2={id,name,email,telephone};
   console.log(JSON.stringify(user2));//{"id":101,"name":"admin","email":"admin@qq.com","telephone":"12345678910"}

函数的增强写法:

// ES6函数的增强写法
const object={
    show(){
        console.log("show");
    },
    test(){
        console.log("test");
    }
}
object.show();
object.test();

filter函数:用于过滤数组中的元素

let numbers=[10,12,13,14,15,16,20,25];
    /**
     * 可以对数组中的某些元素进行遍历,或者进行修改
     * item:数组中的每一项元素
     * index:数组下标
     * self:数组本身
     * @return 返回一个布尔值,如果返回的是true,就会把当前元素添加到一个新的数组,不会改变原数组
     */
    let arr= numbers.filter(function (item,index,self) {
   return item>=14;
})
    console.log(arr,numbers)

reduce:方法对数组中的每个元素执行一个由我们提供的reducer函数(升序执行),将其结果汇总为单个返回值。

    let numbers=[10,12,13,14,15,16,20,25];
    /**
     * temp:临时计算变量
     * item:数组中的当前项
     * index:数组下标
     * self:数组本身自己
     *  如果reduce传入了第二个参数,那么数组从第一项开始循环,第一次循环的temp值是传入的第二个参数
     * 如果reduce没有传入第二个参数,数组从第二项开始循环,第一次循环的temp值是数组的第一项元素
     */
    let sum = numbers.reduce((temp,item,index,self)=>{
        return temp+item;
    },0)
    console.log(sum)

map:对数组的每一项进行遍历,返回值作为当前下标位置的元素

    let numbers=[10,12,13,14,15,16,18,19];
    /**
     * item:数组中的当前项
     * index:数组下标
     * self:数组本身自己
     * 功能:对数组的每一项进行遍历,返回值作为当前下标位置的元素
     */
    let arr = numbers.map((item,index,self)=>{
        return item*10;
    },0)
    console.log(arr)

箭头函数:

<script type="text/javascript">
    // 使用字面量接收函数
    const  add = function (number1,number2) {
        return number1+number2;
    }
    console.log(add(10,20))

    // 直接声明一个函数
    function sum(number1,number2) {
        return number1+number2;
    }
    console.log(sum(20,25))

    // 在对象中定义方法
    const  object={
        add(number1,number2){
            return  number1+number1;
        }
    }
    console.log(sum(25,25))

    //什么时候使用箭头函数比较多?当需要调用一个函数作为另外一个函数的参数的时候
    // 箭头函数,只有一个参数时,可以省略掉括号
    const fun = (number1,number2)=>  {
        return number1+number2
    }
    console.log(fun(30,30))

    // 箭头函数,函数体只有一行代码时,可以省略掉大括号,如果函数体中有返回值返回给fun2,没有返回值返回undefined
    const fun2 = (number1,number2)=>    number1+number2

    console.log(fun2(40,40))

    // 箭头函数的this使用
    //结论:箭头函数中的this,向外层一层一层查找,查找到最近作用域的this
    const obj={
        show(){
            setTimeout(function () {
                console.log("1:",this) // window
            })

            console.log("2:",this) // show

            setTimeout(()=>{
                console.log("3:",this) // show
            })
        }
    }

    obj.show()
    

</script>

promise: 异步任务处理方法

1.什么是Promise

Promise 是异步编程的一种解决方案,其实是一个构造函数,自己身上有all、reject、resolve这几个方法,原型上有 then、catch等方法。

Promise对象有以下两个特点。

(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

Promise用法1: prmoise参数是一个函数,而这个函数有两个参数,resolve,reject,这两参数也是一个函数,可以在传递参数的函数中调用,resolve表示执行异步任务成功的时候调用,reject表示异步任务执行失败的时候调用,而在prmoise对象后面有两个方法,then和catch,执行的是resolve就调用then,执行的是reject就调用cathc。

  new Promise((resolve,reject)=>{
      // 模拟一个异步任务
      setTimeout(()=>{
          console.log('执行完promise')
          // 模拟异步任务的返回结果,偶数代表成功,奇数代表失败
         let result= Math.floor(Math.random()*(50-10+1)+10);
         console.log('result='+result)
          if (result%2==0){
              // 执行这个函数将异步任务的返回值传递过去在then中继续处理(成功时调用)
              resolve('模拟执行成功返回的数据');
          }else {
              // 执行这个函数将异步任务的返回值传递过去在catch中继续处理(失败时调用)
              reject('模拟执行失败返回的数据');
          }
          
      },1000)
  }).then((data)=>{
      // 得到resolve传递过来的参数进行处理
      console.log(data)
  }).catch((error)=>{
      //得到reject传递过来的参数进行处理
      console.log(error)
  })

Promise用法2: all方法的使用,当我们需要执行多个异步任务,需要多个任务返回成功数据才去执行下一步操作的时候,就可以使用到该方法

<script type="text/javascript">
    let  result1=4;
    let  result2=2;
    Promise.all([
        // 异步任务1
        new Promise((resolve, reject) => {
            setTimeout(() => {
                let flag=result1%2==0
                if (flag){
                    resolve(flag)
                }else {
                    reject(flag)
                }

            }, 1000)
        }),
        // 异步任务2
        new Promise( (resolve, reject) => {
            setTimeout(() => {
                let flag=result2%2==0
                if (flag){
                    resolve(flag)
                }else {
                    reject(flag)
                }
            }, 2000)
        })
    ]).then( success=>{
           // 多个异步任务执行成功,在此处执行下一步操作
            console.log('success:',success)
        }
    ).catch( error=>{
        // 多个异步任务执行失败,在此处执行下一步操作
        console.log("error:",error)
    })

</script>

Promise用法3: 链式调用,也就是说多个异步任务,需要上一次执行成功,才可以继续执行下一部这样的情况

<script type="text/javascript">
    new Promise((resolve, reject) => {

        // 第一次网络请求的代码
        setTimeout(() => {
            resolve("success1")
            // 处理失败的情况
            // reject('failed1')
        }, 1000)

    }).then((result) => {
        //  第一次处理的代码
        console.log('第一次网络请 result='+result);
        return new Promise((resolve, reject) => {

            // 第二次网络请求的代码
            setTimeout(() => {
                resolve("success2")
            }, 1000)
        })
    }).then((result) => {

        // 第二次处理的代码
        console.log('第二次网络请求 result='+result);

        return new Promise((resolve, reject) => {

            // 第三次网络请求的代码
            setTimeout(() => {
                resolve("success3")
            })
        })
    }).then((result) => {

        console.log('第三次网络请求 result='+result);
    }).catch(( error)=>{
        // 处理失败的情况
        console.log('调用失败')
    })

--------------------------------------------链式调用的方式2------------------------------------------------------
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('000')
        }, 1000)
    }).then(result => {
        console.log(result, '第一层');
        return Promise.resolve(result + '111')
    }).then(result => {
        console.log(result, '第二层');

        return Promise.resolve(result + '222')
    }).then(result => {
        console.log(result, '第三层');
    }).catch(err => {
        console.log(err);
    })
</script>

race的用法: all是等所有的异步操作都执行完了再执行then方法,那么race方法就是相反的,谁先执行完成就先执行回调。先执行完的不管是进行了race的成功回调还是失败回调,其余的将不会再进入race的任何回调

<script type="text/javascript">
    function Async1(){
        let promise = new Promise(function(resolve, reject){
            setTimeout(function(){
                let num = Math.floor(Math.random()*(10-1+1)+1);
                console.log('Async1 随机数生成的值:',num)
                if(num<=8){
                    resolve(num);
                }
                else{
                    reject('执行失败回调');
                }
            }, 1000);
        })
        return promise
    }
    function Async2(){
        let promise = new Promise(function(resolve, reject){
            setTimeout(function(){
                let num = Math.ceil(Math.random()*20); //生成1-10的随机数
                console.log('Async2 随机数生成的值:',num)
                if(num<=8){
                    resolve(num);
                }
                else{
                    reject('执行失败回调');
                }
            }, 2000);
        })
        return promise
    }
    function Async3(){
        let promise = new Promise(function(resolve, reject){
            setTimeout(function(){
                let num = Math.ceil(Math.random()*20); //生成1-10的随机数
                console.log('Async3 随机数生成的值:',num)
                if(num<=8){
                    resolve(num);
                }
                else{
                    reject('执行失败回调');
                }
            }, 3000);
        })
        return promise
    }

    Promise.race([Async1(), Async2(), Async3()])
        .then(function(results){
            console.log('执行成功',results);
        },function(error){
            console.log('执行失败',error);

</script>

9.2 vue常用官网

axios:  http://www.axios-js.com/zh-cn/docs/
router: https://router.vuejs.org/zh/
vuex: https://vuex.vuejs.org/zh/
vue-cli: https://cli.vuejs.org/zh
vue: https://cn.vuejs.org/index.html
webhttps://webpack.docschina.org/

//得到reject传递过来的参数进行处理
console.log(error)
})


**Promise用法2:** all方法的使用,当我们需要执行多个异步任务,需要多个任务返回成功数据才去执行下一步操作的时候,就可以使用到该方法

```javascript
<script type="text/javascript">
    let  result1=4;
    let  result2=2;
    Promise.all([
        // 异步任务1
        new Promise((resolve, reject) => {
            setTimeout(() => {
                let flag=result1%2==0
                if (flag){
                    resolve(flag)
                }else {
                    reject(flag)
                }

            }, 1000)
        }),
        // 异步任务2
        new Promise( (resolve, reject) => {
            setTimeout(() => {
                let flag=result2%2==0
                if (flag){
                    resolve(flag)
                }else {
                    reject(flag)
                }
            }, 2000)
        })
    ]).then( success=>{
           // 多个异步任务执行成功,在此处执行下一步操作
            console.log('success:',success)
        }
    ).catch( error=>{
        // 多个异步任务执行失败,在此处执行下一步操作
        console.log("error:",error)
    })

</script>

Promise用法3: 链式调用,也就是说多个异步任务,需要上一次执行成功,才可以继续执行下一部这样的情况

<script type="text/javascript">
    new Promise((resolve, reject) => {

        // 第一次网络请求的代码
        setTimeout(() => {
            resolve("success1")
            // 处理失败的情况
            // reject('failed1')
        }, 1000)

    }).then((result) => {
        //  第一次处理的代码
        console.log('第一次网络请 result='+result);
        return new Promise((resolve, reject) => {

            // 第二次网络请求的代码
            setTimeout(() => {
                resolve("success2")
            }, 1000)
        })
    }).then((result) => {

        // 第二次处理的代码
        console.log('第二次网络请求 result='+result);

        return new Promise((resolve, reject) => {

            // 第三次网络请求的代码
            setTimeout(() => {
                resolve("success3")
            })
        })
    }).then((result) => {

        console.log('第三次网络请求 result='+result);
    }).catch(( error)=>{
        // 处理失败的情况
        console.log('调用失败')
    })

--------------------------------------------链式调用的方式2------------------------------------------------------
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('000')
        }, 1000)
    }).then(result => {
        console.log(result, '第一层');
        return Promise.resolve(result + '111')
    }).then(result => {
        console.log(result, '第二层');

        return Promise.resolve(result + '222')
    }).then(result => {
        console.log(result, '第三层');
    }).catch(err => {
        console.log(err);
    })
</script>

race的用法: all是等所有的异步操作都执行完了再执行then方法,那么race方法就是相反的,谁先执行完成就先执行回调。先执行完的不管是进行了race的成功回调还是失败回调,其余的将不会再进入race的任何回调

<script type="text/javascript">
    function Async1(){
        let promise = new Promise(function(resolve, reject){
            setTimeout(function(){
                let num = Math.floor(Math.random()*(10-1+1)+1);
                console.log('Async1 随机数生成的值:',num)
                if(num<=8){
                    resolve(num);
                }
                else{
                    reject('执行失败回调');
                }
            }, 1000);
        })
        return promise
    }
    function Async2(){
        let promise = new Promise(function(resolve, reject){
            setTimeout(function(){
                let num = Math.ceil(Math.random()*20); //生成1-10的随机数
                console.log('Async2 随机数生成的值:',num)
                if(num<=8){
                    resolve(num);
                }
                else{
                    reject('执行失败回调');
                }
            }, 2000);
        })
        return promise
    }
    function Async3(){
        let promise = new Promise(function(resolve, reject){
            setTimeout(function(){
                let num = Math.ceil(Math.random()*20); //生成1-10的随机数
                console.log('Async3 随机数生成的值:',num)
                if(num<=8){
                    resolve(num);
                }
                else{
                    reject('执行失败回调');
                }
            }, 3000);
        })
        return promise
    }

    Promise.race([Async1(), Async2(), Async3()])
        .then(function(results){
            console.log('执行成功',results);
        },function(error){
            console.log('执行失败',error);

</script>

9.2 vue常用官网

axios:  http://www.axios-js.com/zh-cn/docs/
router: https://router.vuejs.org/zh/
vuex: https://vuex.vuejs.org/zh/
vue-cli: https://cli.vuejs.org/zh
vue: https://cn.vuejs.org/index.html
webhttps://webpack.docschina.org/
  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值