标签搜索

VUE2.0

zyb
zyb
2022-10-14 / 0 评论 / 58 阅读 / 正在检测是否收录...

VUE

第一章 VUE核心

  • JS知识补充

    • 三种弹窗

      • alert () 提示弹窗
      • confirm() 带选择的弹窗
      • prompt () 接收用户输入的弹窗
    • var let const 区别

      • var与let的区别:

        • var声明变量可以重复声明,重复声明后之前变量值被覆盖;而let不可以重复声明,重复声明会报错
        • var声明的变量不受限于块级作用域,即var声明的变量是全局变量,不受当前(块级)作用域;let声明的变量当前(块级)作用域限制,只在作用域内有效。
        • let不存在变量提升:var声明变量的代码上面可以访问变量,而let不可以,在let声明的上面访问变量会报错,这就我们说的暂存死区。
        • var会与window相映射(会挂一个属性),而let不与window相映射
      • const声明变量的特点

        • const和let一样不会与window相映射、支持块级作用域、在声明的上面访问变量会报
        • const声明之后必须赋值,否则会报错
        • const定义不可变的量,改变了就会报错

    • js格式化对象方法 JSON.stringify(obj) 解析对象JSON.parse(obj),并且JSON.parse(null)的结果依然是null
    • Date.now()时间戳
    • Math.random()随机数
    • nanoid() 迷你版UUID
  • 前置准备

    • 安装vscode插件 (Vetur 作者Pine Wu) 否则不显示高亮
    • .vue文件提示插件(Vue 3 Snippets作者 hollowtree)
    • Open in External App 打开外置MD软件

1.1 简介

  • 用于构建用户界面的渐进式JavaScript框架
  • 作者:尤雨溪 官网:https://cn.vuejs.org/
  • 前端的主流框架之一,和Angular.js、React.js 一起,并成为前端三大主流框架

1.2 特点

  • 组件化模式,提高代码复用率
  • 声明式编码,提高开发效率

1.3 HelloWorld

  • Vue代码实例

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8" />
            <title>初识VUE</title>
            <!-- 引入vue -->
            <script type="text/javascript" src="../js/vue.js"></script>
        </head> 
        <body>
            <!-- 容器和实例只能一一对应,多个不会生效 -->
            <div id="root"> 
                <h1>hello {{name.toUpperCase()}} {{4+4}}</h1>
            </div>
            <script type="text/javascript">
                Vue.config.productionTip = false //关闭测试环境警告
                const x = new Vue({//创建vue对象
                el:'#root', // el用于指定当前操作的对象为哪个容器对服务,值通常为css选择器字符串
                data:{ //data用于存储数据,供el所指定的容器去使用
                    name:'world'
                }
                }); 
            </script>
        </body>
    </html>
  • 全量配置项

    Vue.config.productionTip = false
    new Vue({
        el: '#root',//绑定
        data: {//数据
        },
        computed: {//计算属性
        },
        methods: {//方法
        },
        watch: {//监视属性
        },
        filters: {//过滤器 vue3.0已经移除
        },
       directives:{//自定义指令  可以写成两种形式 函数&或对象
        }
    }); 

1.4 模板语法

1.4.1插值语法 {{}}

  • 作用: 用于解析标签体内容
  • 语法: {{xxx}} ,xxxx 会作为 js 表达式解析

1.4.2指令语法

  • v-bind:

    • 作用:绑定标签属性
    • 可以简写为 :

1.5数据绑定

  • v-model

    • 作用 :数据双向绑定
    • 只能用于表单类或者输入类
    • v-model:value 可以简写为 v-model
    • 修饰符

      • lazy:失去焦点再收集(适合大文本的输入)
      • number:输入的字符串转为有效的数组
      • trim:输入首尾空格过滤

1.6 el的两种写法

<body>
    <div id="root">
        <h1>会{{name}}</h1>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false
        //传统写法
        const v = new Vue({
            //el: '#root',
            data: {
                name:'出现'
            }
        }); 

        // 一秒后再绑定
        setTimeout(()=>{
            v.$mount('#root')
        },1000);
    </script>
</body>

1.7 data的两种写法

new Vue({
    el: '#root',
    // data: {  //对象式
    //     name:'出现'
    // }

        //由vue管理的函数,一定不要写箭头函数,否则返回的this就不是vue实例了
    data:function(){ //函数式,必须返回所需要的对象 vue组件都要用函数式
        return{
            name:'不会'
        }
    }
}); 

1.8 MVVM模型

  • M 模型(Model) :对应data中的数据
  • V 视图(View) : 模板
  • VM 视图模型(viewModel) :Vue实例对象

1.9 数据代理

  • vm中的_data就是data中的数据
  • 会把_data中的数据加到vm身上一份,编码更方便(数据代理)

1.10 事件处理

  • 事件绑定 v-on
  • 简写 @
  • 事件的回调需要配置在methods对象中,最终会在VM上
  • methods中配置的函数,不要用箭头函数,否则this就不是VM了
  • methods中配置的函数,都是被VUE管理的函数,this中指向的是VM或组件实例对象
  • @click="demo"和@click="demo($event)"效果一样,但是后者可以传参
  • 点击事件实例

    <head>
        <meta charset='UTF-8' />
        <title>事件处理的基本使用</title>
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    
    <body>
        <div id="root">
    <h2>欢迎来到{{name}}学习</h2>
    <button v-on:click="showInfo">点我提示信息</button>
        </div>
        <script type="text/javascript">
    Vue.config.productionTip = false
    new Vue({
      el: '#root',
      data: {
          name:"尚硅谷"
      },
      methods:{
          showInfo(name){
              alert('同学你好'+this.name)
          }
      }
    }); 
        </script>
    </body>
  • 常见事件绑定

    • mouseover 鼠标移入事件
    • mouseleave 鼠标移出事件
    • change 内容发生改变事件
    • load 加载完成事件
    • blur 失去焦点事件

1.11 事件修饰符

  • 常用事件修饰符

    • prevent:阻止默认事件
    • stop:阻止事件冒泡
    • once:事件只触发一次
    • capture:使用事件的捕捉模式
    • self:只有event.target是当前操作的元素时才触发事件
    • passive:事件的默认行为立即执行,无需等待事件回调执行完毕
  • 使用实例

     <!-- prevent属性阻止事件默认,否则点击完后会立刻跳转-->
    <a href="www.atguigu.com" @click.prevent="showInfo">点我提示信息</a></a>
    <!-- 可以连点使用 -->
    <a href="www.atguigu.com" @click.stop.prevent="showInfo">点我提示信息</a></a>

1.12 键盘事件

  • vue给实现别名的按键

    • 回车 enter Enter大写也可以
    • 删除 delete 捕获删除和退格
    • 退出 esc
    • 空格 space
    • 换行 tab (必须配合keydown去使用)
    • 上 up
    • 下 down
    • 左 left
    • 右 right
  • 未实现别名的 可以使用按键的原始key去绑定 但是要注意转为短横线命名

    • 例如caps-lock键
  • 系统修饰键(用法特殊):ctrl alt shift meta(win键)

    • 配合keyup使用 按下修饰键的同时,再按下其他键,然后释放其他键,事件才被触发
    • 配合keydown使用,正常触发事件
  • 也可以使用keyCode去指定具体的按键(不推荐)
  • 实例

    <body>
        <div id="root">
            <h2>欢迎来到{{name}}学习</h2>
            <input type="text" placeholder="按下回车提示输入" @keyup="showInfo"></input>
            <input type="text" placeholder="按下回车提示输入" @keyup.enter="showInfo1"></input>
                    <!-- 修饰键+按键使用 -->
            <input type="text" placeholder="按下回车提示输入" @keyup.ctrl.y="showInfo1"></input>
        </div>
        <script type="text/javascript">
            Vue.config.productionTip = false
            new Vue({
                el: '#root',
                data: {
                    "name":"尚硅谷"
                },
                methods: {
                    showInfo(e){
                        // 输出键盘按键对应的码值 和key名
                        console.log(e.keyCode,e.key)
                        //按键不为回车时返回方法,只有13时才会输出当前内容
                        if(e.keyCode!=13)return
                        console.log(e.target.value)
                    },
                    showInfo1(e){
                        console.log(e.target.value)
                    }
                }
            }); 
        </script>
    </body>

1.13 计算属性与监视

1.13.1计算属性

  • 计算属性定义:要用的属性不存在,通过已有的属性计算得来
  • 原理:底层借助了Object.definepropperty方法提供的getter和setter
  • get函数执行时机:

    • 初次使用时调用
    • 依赖的属性发生改变时会被再次调用
  • 优势:与methods实现相比,内部有缓存机制(复用),效率高,调试方便
  • 备注:

    • 计算属性最终会出现在vm身上,直接读取即可
    • 如果计算属性要被修改,必须写set方法去响应修改,且set中要引起计算时依赖的数据发生改变
  • 实例

    <body>
        <div id="root">
            姓:<input type="text" v-model="firstName"></input> <br/><br/>
            名:<input type="text" v-model="lastName"></input> <br/><br/>
            全名<span>{{fullName}}</span>
        </div>
        <script type="text/javascript">
            Vue.config.productionTip = false
           const vm =  new Vue({
                el: '#root',
                data: {
                    firstName:'赵',
                    lastName:'勇镔'
                },
                // 计算属性:拿着原有属性计算出来的属性就叫计算属性
                computed:{
                    //有人读取fullName,就会自动调用get方法
                    //初次调用或者所依赖的数据发生改变的时候会重新调用get方法
                    //默认有缓存操作,多处使用该值只调用一次
                    fullName:{
                        get(){
                            console.log('get被调用了')
                            return this.firstName+'_'+this.lastName
                        },
                        // fullName被赋值时会重调用set方法
                        set(value){
                            //不能直接改计算属性,只能改计算属性的依赖属性
                            console.log('set',value)
                            const arr = value.split('_')
                            this.firstName = arr[0]
                            this.lastName = arr[1]
                        }
                    }
                }
            }); 
        </script>
    </body>
    
    <!-- 控制台输入vm.fullName = '金_成武',调用set方法,原属性和计算属性发生响应改变 -->
  • 简写模式:只读不改的情况下才可以用简写

    <body>
        <div id="root">
     姓:<input type="text" v-model="firstName"></input> <br/><br/>
     名:<input type="text" v-model="lastName"></input> <br/><br/>
     全名<span>{{fullName}}</span>
        </div>
        <script type="text/javascript">
     Vue.config.productionTip = false
    const vm =  new Vue({
       el: '#root',
       data: {
           firstName:'赵',
           lastName:'勇镔'
       },
       computed:{
           // fullName:function(){
           //     console.log('get被调用了')
           //     return this.firstName+'_'+this.lastName
           // }
           fullName(){//最精简写法
             console.log('get被调用了')
             return this.firstName+'_'+this.lastName
           }
       }
     }); 
        </script>
    </body>

1.13.2监视属性

  • 作用:监视的属性进行变化时,回调函数自动调用,进行相关操作
  • 注意:必须监视的属性存在时,才能进行监视,可以监视计算属性
  • 写法:

    • new Vue时传入watch配置
    • 通过vm.$watch监视
  • 完整实例

    <body>
        <!-- 监视属性:
                1.当监视属性的值发生变化时,回调函数自动调用,进行相关操作
                2.监视的属性必须存在,才能进行监视
                3.两种写法
                 (1).new Vue时传入watch配置
                 (2).通过vm.$watch监视 -->
        <div id="root">
            <h2>今天天气{{info}}</h2>
            <button @click="changeWeather">切换天气</button>
        </div>
        <script type="text/javascript">
            Vue.config.productionTip = false
            const vm = new Vue({
                el: '#root',
                data: {
                    isHot:true
                },
                computed:{//计算属性
                    info(){
                        return this.isHot?'炎热':'凉爽'
                    }
                },
                methods: {
                    changeWeather(){
                        this.isHot = !this.isHot
                    }
                },
                watch:{//监视属性 ,可以监控属性和计算属性
                    isHot:{
                        //当属性isHot发生改变时,会调用handler方法
                        //默认两个传参,一个修改前的状态,一个修改后的状态
                        handler(newValue,oldValue){
                            console.log('isHot被修改了,修改前为'+oldValue+
                            '修改后为'+newValue)
                        },
                        immediate:true //初始化时让handler立即执行一次
                    }
                }
            }); 
            //直接调用vm api实现监测
            // vm.$watch('isHot',{
            //     immediate:true, //初始化时让handler立即执行一次
            //     handler(newValue,oldValue){
            //                 console.log('isHot被修改了,修改前为'+oldValue+
            //                 '修改后为'+newValue)
            //             }
            // });
        </script>
    </body>
  • 深度监视

    • .vue中的watch默认不监测对象内部值的改变(一层)
    • 配置deep:true可以监测对象内部值的改变(多层)
  • 备注

    • .vue自身可以监测对象内部值的改变,但是提供的watch默认不可见
    • 使用watch时根据数据的具体结构老决定是否采用深度监测
  • 实例

    <body>
        <div id="root">
            <h2>a的值是{{number.a}}</h2>
            <button @click="number.a++">点我让a+1</button>
            <br /><br /><br />
            <h2>b的值是{{number.b}}</h2>
            <button @click="number.b++">点我让b+1</button>
        </div>
        <script type="text/javascript">
            Vue.config.productionTip = false
            const vm = new Vue({
                el: '#root',
                data: {
                    isHot: true,
                    number: {
                        a: 0,
                        b: 2
                    }
                },
                computed: {
                },
                methods: {
                },
                watch: {
                    //原本属性去掉""是简写模式,要监测多级属性的变化必须加"" 直接写会报错
                    "number.a":{
                        handler(newValue,oldValue){
                            console.log('a被修改了,修改前为'+oldValue+
                            '修改后为'+newValue)
                        },
                        immediate:true
                    },
                    number: {
                        deep:true,//如果不加,监测的是整个number的变化
                        handler(newValue, oldValue) {
                            console.log('number被修改了,修改前为' + oldValue +
                                '修改后为' + newValue)
                        },
                        immediate: true
                    }
                }
            }); 
        </script>
    </body>
  • 监视属性简写形式

    <body>
        <div id="root">
    <h2>今天天气{{info}}</h2>
    <button @click="changeWeather">切换天气</button>
        </div>
        <script type="text/javascript">
    Vue.config.productionTip = false
    const vm = new Vue({
      el: '#root',
      data: {
          isHot: true
      },
      computed: {
          info() {
              return this.isHot ? '炎热' : '凉爽'
          }
      },
      methods: {
          changeWeather() {
              this.isHot = !this.isHot
          }
      },
      watch: {
          //只有handler方法时才能使用简写
          isHot(newValue, oldValue) {
              console.log('isHot被修改了,修改前为' + oldValue +
                  '修改后为' + newValue)
          },
      }
    }); 
    
    //直接调vm简写方式
    // vm.$watch('isHot', function (newValue, oldValue) {
    //     console.log('isHot被修改了,修改前为' + oldValue +
    //                 '修改后为' + newValue)
    // });
        </script>
    </body>
  • 监视属性实现计算属性效果

    • 计算属性实现更简洁方便,监视属性实现可以同时添加一些异步任务
    <body>
        <div id="root">
            姓:<input type="text" v-model="firstName"></input> <br/><br/>
            名:<input type="text" v-model="lastName"></input> <br/><br/>
            全名<span>{{fullName}}</span>
        </div>
        <script type="text/javascript">
            Vue.config.productionTip = false
           const vm =  new Vue({
                el: '#root',
                data: {
                    firstName:'赵',
                    lastName:'勇镔',
                    fullName:''
                },
                watch:{
                    firstName:{
                        immediate:true,
                        handler(val){
                            setTimeout(()=>{//等一秒再改变
                                 this.fullName = val+'_'+this.lastName
                            },1000)
                        }
                    },
                    lastName:{
                        immediate:true,
                        handler(val){
                            this.fullName = this.firstName+'_'+val
                        }
                    }
                }
            }); 
        </script>
    </body>

1.14 绑定样式

  • vue进行样式绑定

    • class样式

      • 写法:class="xxx" xxx可以是字符串、对象、数组
      • 字符串写法适用于:类名不确定,要动态获取
      • 对象写法适用于:要绑定多个样式,个数不确定,名字也不确定
      • 数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用
    • style样式

      • :style="{fontSize: xxx}"其中xxx是动态值
      • :style="[a,b]"其中a、b是样式对象
    • 实例
    <head>
        <meta charset="UTF-8" />
        <title>绑定样式</title>
        <style>
            .basic {
                width: 400px;
                height: 100px;
                border: 1px solid black;
            }
            .happy {
                border: 4px solid red;
                ;
                background-color: rgba(255, 255, 0, 0.644);
                background: linear-gradient(30deg, yellow, pink, orange, yellow);
            }
            .sad {
                border: 4px dashed rgb(2, 197, 2);
                background-color: gray;
            }
            .normal {
                background-color: skyblue;
            }
            .atguigu1 {
                background-color: yellowgreen;
            }
            .atguigu2 {
                font-size: 30px;
                text-shadow: 2px 2px 10px red;
            }
            .atguigu3 {
                border-radius: 20px;
            }
        </style>
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    
    <body>
        <div id="root">
            <!-- 绑定data中的属性,把class样式拼到一起 -->
    
            <!-- 字符串的写法,适用于样式的类名不确定,需要动态指定 -->
            <div class="basic" :class="mood" @click="changeMood">{{name}}</div><br /><br />
    
            <!-- 数组写法,适用于样式类名的数量和名字都不确定 -->
            <div class="basic" :class="classArr">{{name}}</div><br /><br />
    
            <!-- 对象写法,适用于要绑定的样式确定,名字也确定,但是要动态决定用不用 -->
            <div class="basic" :class="classObj">{{name}}</div><br /><br />
    
            <!-- 绑定style样式--对象写法 -->
            <div class="basic" :style="{fontSize: fsize+'px'}">{{name}}</div><br /><br />
            <div class="basic" :style="styleObj">{{name}}</div><br /><br />
    
            <!-- 绑定style样式--数组写法 -->
            <div class="basic" :style="[styleObj,styleObj2]">{{name}}</div><br /><br />
        </div>
        <script type="text/javascript">
            Vue.config.productionTip = false
            new Vue({
                el: '#root',
                data: {
                    name: '尚硅谷',
                    mood: 'normal',
                    classArr: ['atguigu1', 'atguigu2', 'atguigu3'],
                    classObj: {
                        atguigu1: true,
                        atguigu2: true,
                    },
                    fsize: 40,
                    styleObj: {
                        fontSize: '50px',
                        color: 'red',
                    },
                    styleObj2: {
                        backgroundColor: 'orange'
                    }
                },
                computed: {
                },
                methods: {
                    changeMood() {
                        const arr = ['happy', 'sad', 'normal']
                        //floor向下取整  random随机数
                        this.mood = arr[Math.floor(Math.random() * 3)]
                    }
                }
            });  
        </script>
    </body>

1.15 条件渲染

  • v-if

    • 写法

      • v-if="表达式"
      • v-else-if="表达式"
      • v-else
    • 特点

      • 适应于切换频率较低的场景
      • 不展示的DOM元素会被直接移除
    • 注意: v-if,v-else-if,v-else一起使用时结构中间不能被打断
  • v-show

    • 写法:v-show="表达式"
    • 特点

      • 适应于切换频率较高的场景
      • 不展示的DOM元素不会被移除,仅仅是使用样式隐藏
    • 注意:使用v-if时元素可能无法获取,但是使用v-show一定可以获取到
  • template

    • 特点:template不会破坏结构,渲染的时候会去掉template层级
    • 注意:只能配合v-if 不能配合v-show
  • 代码实例

    <body>
        <div id="root">
    <!-- 使用v-show做条件渲染 标签节点存在不显示,适用于经常切换的场景-->
    <h2 v-show="false">欢迎来到{{name}}</h2>
    <h2 v-show="1 === 1">欢迎来到{{name}}</h2>
    
    <!-- 使用v-if做条件渲染 节点不存在,但是每次渲染相对较慢-->
    <h2 v-if="false">欢迎来到{{name}}</h2>
    
    <button @click="n++">点我+1</button>
    <h2 >当前n的值为{{n}}</h2>
    <!-- if-else结构中间不能被打断 -->
    <h2 v-if="n === 1">A</h2>
    <h2 v-else-if="n === 2">B</h2>
    <h2 v-else-if="n === 3">C</h2>
    <h2 v-else>傻叉</h2>
    
    <!--template不会破坏结构,渲染的时候会去掉template层级
       只能配合v-if 不能配合v-show  -->
    <template  v-if="n === 1">
      <h2 >赵</h2>
      <h2 >勇</h2>
      <h2 >镔</h2>
    </template>
    
        </div>
        <script type="text/javascript">
    Vue.config.productionTip = false
    new Vue({
      el: '#root',
      data: {
          name:'尚硅谷',
          n:0
      }
    }); 
        </script>
    </body>

1.15 列表渲染

  • 关键字 v-for

    • 可遍历对象,数组,字符串,或者指定次数
    • 实例

      <body>
          <div id="root">
      <h2>人员列表</h2>
      <ul>
        <!-- key为节点标识 不写key也不会报错 有默认值 默认为index-->
        <!-- <li v-for="p in persons" :key="p.id">
            {{p.name}}-{{p.age}}
        </li> -->
      
        <!-- 遍历数组 -->
        <li v-for="(p,index) in persons" :key="p.id">
            {{p.name}}-{{p.age}}-{{index}}
        </li><br />
      
        <!-- 遍历对象 -->
        <li v-for="(value,k) in car" :key="k">
            {{k}}-{{value}}
        </li><br />
      
        <!-- 遍历字符串 -->
        <li v-for="(value,k) in str" :key="k">
            {{k}}-{{value}}
        </li><br />
      
        <!-- 遍历指定次数 -->
        <li v-for="(value,k) of 6" :key="k">
            {{k}}-{{value}}
        </li><br />
      </ul>
          </div>
          <script type="text/javascript">
      Vue.config.productionTip = false
      new Vue({
        el: '#root',
        data: {
            persons: [
                { id: '1', name: '赵四', age: 22 },
                { id: '2', name: '张三', age: 30 },
                { id: '3', name: '王五', age: 34 }
            ],
            car: {
                nane: "兰博基尼",
                price: "100w",
                color: "蓝色"
            },
            str: "hello"
        }
      }); 
          </script>
      </body>
  • key的原理:

vue渲染列表时,虚拟DOM转真实DOM的过程中,会先对比key是否一致,

如果一致会把相同key中的相同数据复用,不同数据替换,

如果key不同会继续匹配相同key的数据,

不写key,默认key为index

  • 实例

    <body>
        <div id="root">
    <h2>人员列表</h2>
    <ul>
      <!-- 遍历数组 -->
      <!-- 如果对数据顺序进行了破坏,例如从前插入数据,则不能使用index作为key
      会出现数据错乱 -->
      <li v-for="(p,index) in persons" :key="p.id">
          {{p.name}}-{{p.age}}-{{index}} <input type="text"></input>
      </li><br/>
    
      <!-- once只能触发一次 -->
      <button @click.once="add">点我 添加一个隔壁老王</button>
    </ul>
        </div>
        <script type="text/javascript">
    Vue.config.productionTip = false
    new Vue({
      el: '#root',
      data: {
          persons: [
              { id: '1', name: '赵四', age: 22 },
              { id: '2', name: '张三', age: 30 },
              { id: '3', name: '王五', age: 34 }
          ]
      },
      methods:{
          add(){
              const p = {id:"4", name: '隔壁的老王', age: 66 }
              // unshift向前插入一条
              this.persons.unshift(p)
          }
      }
    }); 
        </script>
    </body>
  • VUE数据监测

    • vue会监视data中所有层次的数据。
    • 如何监测对象中的数据 : 通过setter实现监视,且要在new Vue时就传入要监测的数据

      • 对象中后追加的属性,Vue默认不做响应式处理
      • 如需给后添加的属性做响应式,请使用如下API:

        Vue.set(target,propertyName/index,value) 或 
        vm.$set(target,propertyName/index,value)
        //Vue.set(对象, '属性名称', '值')
    • 如何监测数组中的数据: 通过包裹数组更新元素的方法实现,本质就是做了两件事:

      • 调用原生对应的方法对数组进行更新
      • 重新解析模板,进而更新页面
  • 在Vue修改数组中的某个元素一定要用如下方法

    • 使用这些API:push( ) 最后位置更新、pop( )最后位置删除、shift( )删除第一个、unshift( )添加第一个、splice(坐标,长度,'修改内容')任意位置插入替换 、sort( )排序、reverse( )反转
    • Vue.set() 或 vm.$set()
  • 特别注意:Vue.set()vm.$set() 不能给vm 或 vm的根数据对象 添加属性,只能给data中的对象添加属性
  • 实例

    <body>
        <div id="root">
            <h1>学生信息</h1>
    
            <button @click="student.age++">年龄+1岁</button> <br />
            <button @click="addSex">添加性别属性,默认值:男</button> <br />
            <button @click="student.sex = '未知' ">修改性别</button> <br />
            <button @click.once="addFriend">在列表首位添加一个朋友</button> <br />
            <button @click="updateFirstFriendName">修改第一个朋友的名字为:帅逼</button> <br />
            <button @click.once="addHobby">添加一个爱好</button> <br />
            <button @click="updateHobby">修改第一个爱好为:开车</button> <br />
            <button @click="removeSmoke">过滤掉爱好中的抽烟</button> <br />
    
            <h2>姓名:{{student.name}}</h2>
            <h2>年龄:{{student.age}}</h2>
            <h2 v-if="student.sex">性别:{{student.sex}}</h2>
            <h2>爱好</h2>
            <ul>
                <li v-for="(f,index) in student.hobby" :key="index">
                    {{f}}
                </li>
            </ul>
            <h2>朋友们</h2>
            <ul>
                <li v-for="(f,index) in student.friends" :key="index">
                    {{f.name}}--{{f.age}}
                </li>
            </ul>
        </div>
    </body>
    <script type="text/javascript">
        Vue.config.productionTip = false
    
        const vm = new Vue({
            el: '#root',
            data: {
                student: {
                    name: 'tom',
                    age: 20,
                    hobby: ['抽烟', '喝酒', '烫头'],
                    friends: [
                        { name: 'jerry', age: 35 },
                        { name: 'tony', age: 36 }
                    ]
                }
            },
            methods: {
                addSex() {
                    Vue.set(this.student, 'sex', '男')
                    //this.$set(this.student,'sex','男')
                },
                addFriend() {
                    this.student.friends.unshift({ name: 'zhaoyongbin', age: 18 })
                },
                updateFirstFriendName() {
                    //this.student.friends.splice(0, 1, { name: '帅逼', age: 18 })
                    //数组对象没有get set 但是数组对象的属性有get set
                    this.student.friends[0].name ='帅逼'
                },
                addHobby() {
                    this.student.hobby.push('魔方')
                },
                updateHobby() {
                    // this.student.hobby.splice(0, 1, '开车')
                    //通过set方法修改 
                    Vue.set(this.student.hobby,0,'开车')
                    //this.$set(this.student.hobby,0,'开车')
                },
                removeSmoke() {
                    this.student.hobby = this.student.hobby.filter((h) => {
                        return h !== '抽烟'
                    })
                }
            }
        })
    </script>

1.16 收集表单数据

  • 文本输入

    • 普通文本

      用户名:<input type="text" placeholder="请输入用户名" v-model.trim="userInfo.account">
  • 密码

    密码:<input type="password" v-model="userInfo.password"></input>
  • 纯数字

    <!-- type="number"可以使输入类型强制为数字  v-model.number使vue转化的为数字而不是字符串 -->
    年龄:<input type="number" v-model.number="userInfo.age"></input>
  • 单选框

    性别:
    男<input type="radio" value="male" name="sex" v-model="userInfo.sex"></input>
    女<input type="radio" value="female" name="sex" v-model="userInfo.sex"></input>
  • 多选框 (如果不配置value值 默认收集的是布尔值,而且初始值类型一定要是数组)

    爱好:
    学习<input type="checkbox" v-model="userInfo.hobby" value="study"></input>
    打游戏<input type="checkbox" v-model="userInfo.hobby" value="game"></input>
    吃饭<input type="checkbox" v-model="userInfo.hobby" value="eat"></input>
  • 下拉框

    所属校区
    <select v-model="userInfo.city">
        <option value="">请选择校区</option>
        <option value="beijing">北京</option>
        <option value="shanghai">上海</option>
        <option value="wuhan">武汉</option>
        <option value="shenzhen">深圳</option>
    </select>
  • 富文本

    <!-- 失去焦点后再收集 实时收集浪费内存 -->
    <textarea v-model.lazy="userInfo.other">5646546</textare
  • 确认框

    <input type="checkbox" v-model="userInfo.agree"></input>阅读并接受相关协议
    <a href="http://www.baidu.com">《用户协议》</a>
  • 完整实例

    <body>
        <div id="root">
    <!-- prevent阻止提交默认事件 -->
    <form @submit.prevent="demo">
      <!--  trim去掉输入前后空格-->
      用户名:<input type="text" v-model.trim="userInfo.account"></input><br /><br />
      密码:<input type="password" v-model="userInfo.password"></input><br /><br />
      <!-- type="number"可以使输入类型强制为数字  v-model.number使vue转化的为数字而不是字符串 -->
      年龄:<input type="number" v-model.number="userInfo.age"></input><br /><br />
      性别:
      男<input type="radio" value="male" name="sex" v-model="userInfo.sex"></input>
      女<input type="radio" value="female" name="sex" v-model="userInfo.sex"></input>
      <br /><br />
      爱好:
      学习<input type="checkbox" v-model="userInfo.hobby" value="study"></input>
      打游戏<input type="checkbox" v-model="userInfo.hobby" value="game"></input>
      吃饭<input type="checkbox" v-model="userInfo.hobby" value="eat"></input><br /><br />
      所属校区
      <select v-model="userInfo.city">
          <option value="">请选择校区</option>
          <option value="beijing">北京</option>
          <option value="shanghai">上海</option>
          <option value="wuhan">武汉</option>
          <option value="shenzhen">深圳</option>
      </select><br /><br />
      其他信息<br /><br />
      <!-- 失去焦点后再收集 实时收集浪费内存 -->
      <textarea v-model.lazy="userInfo.other">5646546</textarea><br /><br />
      <input type="checkbox" v-model="userInfo.agree"></input>阅读并接受相关协议
      <a href="http://www.baidu.com">《用户协议》</a><br /><br />
    
      <button>提交</button>
    </form>
        </div>
        <script type="text/javascript">
    Vue.config.productionTip = false
    new Vue({
      el: '#root',
      data: {
          userInfo: {//封装到一个对象里便于转换JSON对象,传到后端
              account: '',
              password: '',
              sex: 'male',//默认值
              hobby: [],//默认一定是数组 不然多选还是只能收集是否勾选
              city: 'beijing',
              other: '',
              agree: '',
              age:'',
          }
      },
      computed: {
      },
      methods: {
          demo() {
              //console.log(JSON.stringify(this._data))
              console.log(JSON.stringify(this.userInfo))
          }
      }
    }); 
        </script>
    </body>

1.16 过滤器 (vue3.0已经移除过滤器 )

  • 定义:要对显示的数据进行特殊的格式化后再显示(适用于一些简单的逻辑)
  • 用法

    • 全局配置

       Vue.filter('mySlice',function(val){
      return val.slice(0,4)
              })
    • 局部配置

            filters: {//局部过滤器 
        //str有传参时使用传参 无传参时使用默认值
        fimeFormater(val,str='YYYY年MM月DD日 HH:mm:ss') {
            return dayjs(val).format(str)
        },
      }
  • 备注

    • 使用过滤器可以接收额外参数,多个过滤器之间可以串联
    • 不改变原有数据,而是产生新的数据
  • 实例

    <body>
        <div id="root">
            <h2>显示格式化后的时间戳</h2><br />
            <!-- 计算属性实现 -->
            <h3>当前时间为: {{fmtTime}}</h3>
            <!-- methods实现 -->
            <h3>当前时间为: {{getfmtTime()}}</h3>
            <!-- 过滤器实现 -->
            <h3>当前时间为: {{time | fimeFormater}}</h3>
            <!-- 过滤器实现2 有传参    过滤器可以一层一层处理-->
            <h3>当前时间为: {{time | fimeFormater('YYYY_MM_DD') | mySlice}}</h3>
    
        </div>
        <script type="text/javascript">
            Vue.config.productionTip = false
            //全局过滤器 只能一个个配置
            Vue.filter('mySlice',function(val){
                return val.slice(0,4)
            })
            new Vue({
                el: '#root',//绑定
                data: {//数据
                    time: 1621561377603
                },
                computed: {//计算属性
                    fmtTime() {
                        return dayjs(this.time).format('YYYY年MM月DD日 HH:mm:ss')
                    }
                },
                methods: {//方法   dayjs()方法为dayjs.js提供 需要单独引用才能使用
                    getfmtTime() {
                        return dayjs(this.time).format('YYYY年MM月DD日 HH:mm:ss')
                    }
                },
                filters: {//局部过滤器   PS:vue3.0已经移除过滤器
                    //str有传参时使用传参 无传参时使用默认值
                    fimeFormater(val,str='YYYY年MM月DD日 HH:mm:ss') {
                        return dayjs(val).format(str)
                    },
                }
            }); 

1.17 VUE内置指令

  • v-bind :单项绑定可简写为 :xxx (data中的数据改变,页面就会对应改变)
  • v-model 双向数据绑定 (data 和页面数据改变互相影响)
  • v-on 绑定事件监听 简写为@
  • v-for 遍历数组对象字符串
  • v-if v-else v-show 条件渲染
  • v-text 向其所在标签插入文本
  • v-html 向其所在标签插入可解析的标签结构(有安全风险)
  • v-cloak 解决网速过慢时,隐藏未经解析的模板

    <head>
        <meta charset='UTF-8' />
        <title> </title>
        <!-- 先隐藏所有该标签 等vue接管后,vue会删除所有该标签 相当于隐藏失效 -->
        <style>
    [v-cloak] {
      display: none;
    }
        </style>
    </head>
    <body>
        <div id="root">
    <div v-clock>你好,{{name}}</div>
        </div>
        <!-- 假设该vue是从外部引入 且加载过慢 -->
        <script type="text/javascript" src="../js/vue.js"></script>
        <script type="text/javascript">
    Vue.config.productionTip = false
    new Vue({
      el: '#root',
      data: {
          name: '尘埃',
      }
    }); 
        </script>
    </body>
  • v-once指令 所在节点在初次动态渲染后,就视为静态内容了,以后数据的改变不会引起v-once所在结构的更新,用于优化性能

    <body>
        <div id="root">
    <h2 v-once>初始化的值是{{n}}</h2>
    <h2>当前n的值是{{n}}</h2></h2>
    <button @click="n++">点我n+1</button>
        </div>
        <script type="text/javascript">
    Vue.config.productionTip = false
    new Vue({
      el: '#root',
      data: {
          n:5,
      }
    }); 
        </script>
    </body>
  • v-pre指令 让vue跳过其所在节点的编译,可让其跳过没有指令语法插值语法的节点,可以加快编译速度

    <h2 v-pre>VUE学习</h2>

1.18 VUE自定义指令

  • 定义语法:

    • 局部指令

      new Vue({new Vue({ directives:{指令名:配置对象} //或           
      directives{指令名:回调函数} })     
    • 全局指令

      Vue.directive(指令名,配置对象) //或   
      Vue.directive(指令名,回调函数)
    • 配置对象和回调函数区别

      • 回调函数是配置对象的简写形式,适用于没有用到inserted的情况,并且bind和update操作内容一致
  • 配置对象中常用的3个回调

    • .bind:指令与元素成功绑定时调用
    • .inserted:指令所在元素被插入页面时调用
    • .update:指令所在模板结构被重新解析时调用。
  • 备注

    • 指令定义时不加v-,但使用时要加v-
    • 指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名
  • 实例

    <body>
        <div id="root">
    <h2>当前的n值是:{{n}}</h2>
    <!-- 自定义指定不需要加v- 使用的时候会自动匹配前缀 -->
    <h2>当前的n值十倍是:<span v-big="n"></span></h2>
    <button @click="n++">点我n+1</button>
    <input type="text" v-fbindx:value="n">
        </div>
        <script type="text/javascript">
    Vue.config.productionTip = false
    //全局自定义指令
    Vue.directive('fbindx', {
      bind(element, binding) {
          console.log('bind')
          element.value = binding.value
      },
      inserted(element, binding) {
          console.log('inserted')
          element.focus()
      },
      update(element, binding) {
          console.log('inserted')
          element.value = binding.value
      }
    })
    new Vue({
      el: '#root',
      data: {
          n: 0,
      },
      directives: {//自定义指令  可以写成两种形式 函数&或对象
          //element真实DOM binding指令接收的参数
          //调用时机,1.指令与元素绑定时
          // 2.指令所在的模板重新解析时
          big(element, binding) {//其实就是简写的 bind和updata生命周期里的执行内容
              element.innerText = binding.value * 10
          }
      }
    }); 
        </script>

1.19 VUE生命周期

  • 概述

    • 简介:VUE生命周期也叫生命周期函数,生命周期钩子
    • 作用:在关键时刻,vue帮我们调用的一些特殊名称的函数
    • 生命周期函数的名字不能更改,内容由开发人员自己定义
    • 生命周期函数中的this是指向vm或组件实例对象
    • 生命周期函数与其他vue配置属性平级
  • 重要生命周期方法

    • beforeCreate 将要创建
    • created 创建完毕,此时已经挂载了data中的数据
    • beforeMount 将要挂载,此时页面是未经VUE编译的结构,所有对DOM的操作最终都不奏效(被虚拟DOM替换了)
    • mounted 挂载完毕(重要),此时页面已经经过vue编译完成,初始化工作一般都在这个周期中进行,例如 发送ajax请求,启动定时器,绑定自定义事件,订阅消息等
    • beforeUpdate 将要更新,此时数据已更新,但是页面还没更新
    • updated 更新完毕,此时数据已经更新,页面也是最新的
    • beforeDestroy 将要销毁 (重要) (this.$destroy();销毁vue实例) 指的组件vue,此时对数据更改也不会生效了,数据不会进行更改,主要是执行关闭操作,清除定时器,解绑自定义事件,取消订阅等收尾操作
    • destroyed 销毁完毕, 彻底销毁 一般用不到
  • 关于VUE销毁

    • 销毁后借助Vue开发者工具看不到任何信息
    • 销毁后自定义事件会失效,但原生DOM事件依然有效
    • 一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了

第二章 VUE组件化编程

2.1 组件化编程简介

  • 传统方法编写问题

    • 依赖关系混乱
    • 代码复用率不高
  • 组件化编写优点

    • 可以实现应用中局部功能代码和资源的集合,提高复用率

2.2 非单文件组件

  • 定义:一个文件中包含有n个组件
  • vue中使用组件的三大步骤

    • 定义组件(创建组件)
    • 注册组件
    • 使用组件,即放对应组件名称的html标签也叫组件标签,每个引用组件中的数据互不影响
  • 定义组件

    //组件不能写el配置项,因为所有最终的组件都要被一个vm所管理,由vm决定服务于哪个容器
    //data要写成函数式,不然引用的都是同一个对象
    //这里的school只是变量名称 并不是组件名称 变量名称和组件注册名称最好统一 可以简写
    const school = Vue.extend({//vue2.0组件要有根元素 所以要加div
            name:'school',//不会改变注册中的名字 仅仅是在开发者工具中呈现的名字
            template: `
            <div>
                <h2>学校名称:{{schoolName}}</h2>
                <h2>学校地址:{{address}}</h2>
                <button @click='showName'>点我提示学校信息</button>
            </div>
            `,
            data() {
                return {
                    schoolName: '尚硅谷',
                    address: '宏福科技园'
                }
            },
            methods:{
                showName(){
                    alert(this.schoolName)
                }
            }
        })
    //简写方式 const school = Vue.extend(options) 可以直接写为 const school = options
    //vue底层会根据对象进行判断加Vue.extend(options)
  • 注册组件方式

    • 全局注册

      //全局注册组件  所有的vm实例都可以用这个组件
      Vue.component('school',school)
    • 局部注册(在vue实例中components配置项配置)

      new Vue({
             el: '#root',
             data:{
                 msg:'666'
             },
             components: {//局部注册组件
                 student,//如果组件名称与对象名称相同,则可以简写
             }
         }); 
    • 关于注册时的组件名称的注意事项

      • 一个单词的情况 纯小写 或者首字母大写
      • 两个单词的情况 要么全小写 单词中间用-连接,或者每个单词首字母大写(得用脚手架才行)
      • 组件命名尽量避免html原有标签名称,可以使用name配置项指定组件在开发者工具中呈现的名字
  • 引用组件

    <div id="root">
          <h2>{{msg}}</h2>
          <!-- 每个引用组件中的数据互不影响 -->
          <school></school><!--组件注册名称 -->
          <school></school>
            <!-- 脚手架才能使用 每个单词首字母大写,和自闭和标准 不然会报错有BUG-->
            <!-- <MySchool/> -->
          <hr />
          <student></stduent>
     </div>
  • 组件的嵌套使用

    • 一个组件中可以引入另一个组件进行嵌套使用,但是要注意先后顺序,不能把被引用组件放到引用组件后面声明

      const school = Vue.extend({
          template: `
      <div>
          <h2>学校名称:{{schoolName}}</h2>
          <h2>学校地址:{{address}}</h2>
          <stduent></stduent>
      </div>
      `,
          data() {
              return {
                  schoolName: '尚硅谷',
                  address: '宏福科技园'
              }
          },
          components: {
              stduent,//要注意前后声明顺序
          },
      })
  • 注意事项

    • 一般vue实例中不做组件的配置管理,只管理app组件,再由app组件进行统一管理
    • 如果容器中只有app标签,那么app标签可以放到vue配置属性template中实现
  • 嵌套实例

    <body>
        <div id="root">
        </div>
        <script type="text/javascript">
            Vue.config.productionTip = false
            //定义学生组件
            const stduent = Vue.extend({
                template: `
            <div>
                <h2>学生姓名:{{stduentName}}</h2>
                <h2>学生年龄:{{age}}</h2>
            </div>
            `,
                data() {
                    return {
                        stduentName: '王超',
                        age: '18'
                    }
                }
            })
            //定义学校组件
            const school = Vue.extend({
                template: `
            <div>
                <h2>学校名称:{{schoolName}}</h2>
                <h2>学校地址:{{address}}</h2>
                <stduent></stduent>
            </div>
            `,
                data() {
                    return {
                        schoolName: '尚硅谷',
                        address: '宏福科技园'
                    }
                },
                components: {
                    stduent,//要注意前后声明顺序
                },
            })
    
            //定义hello组件
            const hello ={
                template: `
            <div>
                <h2>同学,{{hello}}</h2>
            </div>
            `,
                data() {
                    return {
                        hello: '你好啊',
                    }
                }
            }
    
            //注册app组件 主要是作为管理使用 配置所有的组件
            const app = Vue.extend({
                template: `
            <div>
                <hello></hello>
                <school></school>
            </div>
            `,
            components: {
                school,
                hello
                },
            })
            new Vue({
                template:'<app></app>',//如果容器中只有app标签,可以直接配置在template中
                el: '#root',
                data: {
                    msg: '欢迎学习VUE'
                },
                components: {
                    app,//只管理app
                }
            }); 
        </script>
    </body>
  • VueComponent

    • 组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的
    • 我们只需要写<school/>或<school></school>,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:new VueComponent(options)
    • 特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent
    • 关于this指向:

      • 组件配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是VueComponent实例对象 (简称VC或叫组件实例对象,跟vm功能一致,vc没有el配置)
      • new Vue(options)配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是Vue实例对象 (简称vm)
    • 组件存在于vm身上的$children上,组件的组件同理
  • 重要的内置关系 :VueComponent.prototype._proto_ === Vue.prototype,可以让组件实例对象(vc)可以访问到 Vue原型上的属性、方法 (vc的原型对象的原型对象是vue的原型对象)

2.3 单文件组件

  • 定义:一个文件中只包含1个组件,组件以.vue结尾
  • .vue文件包含三部分

    • <template></template>标签内写结构
    • <script></script>标签内写JS代码(数据和方法)
    • <style></style>标签中写样式,样式是非必须的
  • 完整vue组件应用实例 (需要在脚手架环境下才能运行)

    • School.vue

      <template>
        <div class="demo">
          <h2>学校名称:{{ schoolName }}</h2>
          <h2>学校地址:{{ address }}</h2>
          <button @click="showName">点我提示学校信息</button>
        </div>
      </template>
      
      <script>
      //组件交互相关的代码(数据 方法等)
      // //export const school = Vue.extend({  分别暴露
      // const school = Vue.extend({
      //   data() {
      //     return {
      //       schoolName: "尚硅谷",
      //       address: "宏福科技园",
      //     };
      //   },
      //   methods: {
      //     showName() {
      //       alert(this.schoolName);
      //     },
      //   },
      // });
      // // export {school} //统一暴露 统一暴露和分别暴露时  import {???} from ???
      // export default school //默认暴露 默认暴露导包时 import ??? from ???
      
      //最终优化写法 不需要中转变量 Vue.extend()也可以省略
      export default {
        name: "School",
        data() {
          return {
            schoolName: "尚硅谷",
            address: "宏福科技园",
          };
        },
        methods: {
          showName() {
            alert(this.schoolName);
          },
        },
      };
      </script>
      
      <style >
      /* 组件的样式 */
      .demo {
        background-color: orange;
      }
      </style>
  • Student.vue

    <template>
      <div >
        <h2>学生姓名:{{ name }}</h2>
        <h2>学生年龄:{{ age }}</h2>
      </div>
    </template>
    
    <script>
    export default {
      name: "Student",
      data() {
        return {
          name: "王超",
          age: "18",
        };
      }
    };
    </script>
  • App.vue 只做管理组件工作

    <template>
      <div>
        <School></School>
        <Student></Student>
      </div>
    </template>
    
    <script>
    //先引入组件
    import School from "./School";
    import Student from "./Student";
    
    export default {
      name: "App",
      components: {
        School,
        Student,
      },
    };
    </script>
  • main.js 创建vue实例,绑定App组件

    import App from './App.vue'
    
    new Vue({
        el:"#root",
        template:'<App></App>',
        comments:{App}
    }) 
  • index.html 最终页面展示

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset='UTF-8' />
        <title>单文件组件语法使用 </title>
    </head>
    <body>
        <div id="root">
        </div>
          <!-- 注意引入先后顺序 -->
        <script type="text/javascript" src="../js/vue.js"></script>
        <script type="text/javascript" src="./main.js"></script>
    </body>
    </html>

第三章 使用VUE脚手架(CLI)

  • vscode打开命令行窗,并移动到项目所在目录
  • npm install 下载所需插件 win需要用管理员权限打开否则报错
  • npm run dev 运行项目
  • npm run build 项目打包 文件生成在dist目录下

3.1 初始化脚手架

  • 说明

  • 安装使用 (前置需要先安装node.js)

    • 第一步(仅第一次执行):全局安装@vue/cli。

    npm install -g @vue/cli

    • 第二步:切换到你要创建项目的目录,然后使用命令创建项目

    vue create xxxx

    • 第三步:启动项目

    npm run serve

  • win环境下安装脚手架

    • node版本过高导致安装失败
    • 先卸载16版本的node重新安装14版本 npm version查看版本
    • 重新安装脚手架
    • 如果vue -V报错,需要手动配置环境变量,把vue文件所在路径加到path
    • 如果提示禁止运行脚本,以管理员权限运行cmd

      • 先执行set-ExecutionPolicy
      • 再执行set-ExecutionPolicy RemoteSigned
      • 重新打开终端即可使用
  • mac环境下同理

    • 如果16版本安装失败,卸载安装14版本
    • sudo su 切换到管理员权限
    • 修改镜像源
    • 安装脚手架即可

3.2 关于vueDome实例的梳理

  • 目录结构说明

    • node_modules 第三方库
    • public 公共文件

      • favicon.ico: 页签图标
      • index.html: 主页面
    • src

      • assets 静态资源 (图片,音乐等)
      • components vue组件
      • App.vue 汇总vue所有组件的主组件
      • main.js 程序入口
    • .gitignore: git 版本管制忽略的配置
    • babel.config.js: babel 的配置文件
    • package-lock.json 包版本控制文件
    • package.json 应用包配置文件(配置文件类似java中的yml)
    • REDME.md 应用描述文件
  • 主要文件说明

    • main.js

      // 该文件是整个文件的入口文件
      //引入vue残缺版(没有模板解析器)
      import Vue from 'vue'
      //引入App组件 它是所有组件的副组件
      import App from './App.vue'
      
      //关闭VUE生产提示
      Vue.config.productionTip = false
      //创建VUE实例 --vm
      // new Vue({
      //   render: h => h(App),
      // }).$mount('#app')
      
      new Vue({
        el:'#app',
        //引入了残缺版VUE所以要用render渲染元素,直接用template会报错,因为残缺版删除了模板解析器
        //引入残缺版是为了节省空间,打包的时候就转化为普通的页面了,用不到模板解析器了
         
        //原始写法
        // render(createElement){
        //   //createElement可以创建具体的元素
        //   return createElement('h1','你好')
        // }
      
        //简写
        render: h => h(App)
      })
    • index.html

      <!DOCTYPE html>
      <html lang="">
        <head>
          <meta charset="utf-8">
          <!-- 针对IE浏览器的一个特殊配置,含义是让IE浏览器以最高的渲染级别渲染页面 -->
          <meta http-equiv="X-UA-Compatible" content="IE=edge">
          <!-- 开启移动端的理想视口 -->
          <meta name="viewport" content="width=device-width,initial-scale=1.0">
          <!-- 配置页签图表  BASE_URL为public路径-->
          <link rel="icon" href="<%= BASE_URL %>favicon.ico">
          <!-- 配置网页标题 类似java中的yml 引用的是package中的属性 -->
          <title><%= htmlWebpackPlugin.options.title %></title>
        </head>
        <body>
          <!-- 如果浏览器不支持js 就会输出该语句 -->
          <noscript>
            <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
          </noscript>
          <!-- 引入app  不需要引入main.js 脚手架已经帮忙配置好了-->
          <div id="app"></div>
          <!-- built files will be auto injected -->
        </body>
      </html>
  • 修改默认配置

    • Vue 脚手架隐藏了所有 webpack 相关的配置

    若想查看具体的 webpakc 配置, 请执行:vue inspect > output.js( 只是查看默认配置,修改该文件并不会生效)

    • 配置参考 https://cli.vuejs.org/zh/config/#vue-config-js
    • 创建同级目录创建vue.config.js

      // 配置默认属性  该配置最终会与默认属性融合.即便删除该文件也不会有影响
      module.exports = {
          pages: {
              index: {
                  //修改入口
                  entry: 'src/main.js'
              }
          },
          //关闭语法检查
          lintOnSave: false
      }

3.3 ref属性

  • 加在HTML标签上时获取该标签的DOM元素(id的替代者)
  • 加在组件标签上时获取该组件的实例对象(用于组件间通信)

    <template>
      <div>
          <!-- 自闭标签只能在VUE中使用 -->
          <School ref="school"/>
          <School id='sch'/>
          <School/>
          <Student ref="student"/>
          <button @click="showDOM">点我提示实例对象</button>
      </div>
    </template>
    
    <script>
    import School from './components/School'
    import Student from './components/Student'
    export default {
        name:'App',
        components:{School,Student},
        methods:{
            showDOM(){
                console.log(this.$refs)
                //原生写法只能获取组件标签的DOM结构,并不能获取组件对象
                console.log(document.getElementById('sch'))
            }
        }
    }
    </script>

3.4 组件配置项props属性

  • 作用:让组件接收外部传值
  • 传递数据(父组件中)

      <div>
          <!-- 组件传值 还要接收 -->
          <!-- 传值名称不能用属性关键字例如key -->
          <Student name='张三' sex='男' :age='18'/>
          <Student name='王超' sex='男' :age='22'/>
          <!-- 数字类型的需要动态绑定才行,引号里面的就是js表达式,也就是数字了 -->
          <Student name='丽思' sex='女' :age='26'/>
      </div>
  • 接收数据

    • 简单声明接收

      props:['name','sex','age']
    • 限制接收

        //接收的同时对数据类型进行限制  不对会提醒,但是还会接收
         props: {
           name: String,
           sex: String,
           age: Number,
         },
    • 完整写法

        //完整写法 类型限制+必要性限制+默认值设定
        props: {
          name: {
            type: String, //类型
            required: true, //是否必须  required和default不可能同时出现
          },
          sex: {
            type: String,
            required: true,
          },
          age: {
            type: Number,
            default: 99, //不传的默认值
          },
        },
    • 注意事项

      • props接收来的值,只能读不能改,能改成功但控制台会报错,如果有这种必须要修改的应用场景,应使用data中转变量来接收值,然后修改data中的属性就不会报错了
    • 接收完整代码

      <template>
        <div>
          <h1>{{ msg }}</h1>
          <hr />
          <h2>学生姓名 :{{ name }}</h2>
          <h2>学生性别 :{{ sex }}</h2>
          <h2>学生年龄 :{{ myAge }}</h2>
        </div>
      </template>
      <script>
      export default {
        name: "Student",
        data() {
          return {
            msg: "我是一个尚硅谷的学生",
            myAge:this.age 
          };
        },
        props: {
          name: {
            type: String, //类型
            required: true, //是否必须  required和default不可能同时出现
          },
          sex: {
            type: String,
            required: true,
          },
          age: {
            type: Number,
            default: 99, //不传的默认值
          },
        },
      };
      </script>

3.5 组件配置项mixin属性(混入 混合)

  • 作用:可以把多个组件共用的的配置提取成一个混入对象
  • 使用方法

    • 创建混入JS

      //混合实际上是复用配置 不只是可以用于methos 相当于插入一段配置代码
      export const hunhe = {
          methods: {
              showName() {
                alert(this.name);
              },
            },
      }
      
      //配置项并不是完全替换而是取并集 对于交集属性,原有属性优先级高于混合属性
      //生命周期里的方法特殊 混合和原方法里的生命周期方法都会起作用
      export const hunhe2 = {
          data() {
              return {
                name: "张老师",
                sex:'女'
              };
            },
      }
    • 引入混入配置

      • 单组件引入

        <template>
          <div>
            <h2 @click="showName">学生姓名 :{{ name }}</h2>
            <h2>学生性别 :{{ sex }}</h2>
          </div>
        </template>
        
        <script>
          //引入混入JS
         import {hunhe} from '../mixin'
         import {hunhe2} from '../mixin'
        export default {
          name: "Student",
          data() {
            return {
              name: "马老师",
            };
          },
          //调用 只有一个也要写在数组里
          mixins:[
             hunhe,hunhe2
          ]
        };
        </script>
+ 全局引入 (加在mian.js中)
import Vue from 'vue'
import App from './App.vue'
//引入js
import {hunhe,hunhe2} from './mixin'

Vue.config.productionTip = false
//全局引入混合
Vue.mixin(hunhe)
Vue.mixin(hunhe2)

new Vue({
    render: h=>h(App)
}).$mount('#app')

3.5 VUE插件

  • 功能: 用于增强VUE
  • 本质:包含一个install方法的一个对象,install的第一个对象是vue,第二个参数及后面的参数是插件使用者传递的参数
  • 使用插件

    • 定义或下载一个插件

      //只有一个的时候才可以用默认暴露
      export default {
          install(Vue,x,y,z){
              console.log(x,y,z)
              //全局过滤器
              Vue.filter('mySlice',function(value){
                  return value.slice(0,4)
              })
      
              //定义全局指令
              Vue.directive('fbind',{
                  //指令与元素成功绑定时(一上来)
                  bind(element,binding){
                      element.value = binding.value
                  },
                  //指令所在元素被插入页面时
                  inserted(element){
                      element.focus()
                  },
                  //指令所在的模板被重新解析时
                  update(element,binding){
                      element.value = binding.value
                  }
              })
      
              //定义混入
              Vue.mixin({
                  data() {
                      return {
                          x:100,
                          y:200
                      }
                  },
              })
      
              //给Vue原型上添加一个方法(vm和vc就都能用了)
              Vue.prototype.hello = ()=>{alert('你好啊')}
          }
      }
    • mian.js中引入(可以同时引入多个)

      import Vue from 'vue'
      import App from './App.vue'
      //引入插件
      import plugins from './plugins'
      
      Vue.config.productionTip = false
      
      //应用(使用)插件
      Vue.use(plugins)
      
      new Vue({
          render: h=>h(App)
      }).$mount('#app')
    • 插件中的功能就都可以使用了

3.6 VUE <style>相关问题

  • 默认所有组件的样式最终会汇总到一起,如果有同名样式,先引入的样式会被后引入的样式所覆盖

    • 解决办法

      • 避免使用相同样式名称(废话)
      • 给样式增加作用域scoped (原理:外层的div加了随机属性值,样式只能对应控制指定的div)

        <style scoped>
          /**App组件不适用,App组件一般放的都是全局配置*/
        .test{
            background-color: pink;
        }
        </style>
  • 默认style中不支持less写法

    • 解决办法 安装less-loader

      • 如果脚手架版本过低,自带的webpack是4版本的而安装默认是最新版

        npm view 名称 versions #查看版本
        npm i  less-loader@7 #7以上版本是为webpack的5版本服务的


+ 都是最新版本直接安装
npm i  less-loader
  • 测试

    <style scoped lang='less'>
    /*less可以嵌套写  lang指定样式类型 不写默认css */
    .test{
        background-color: pink;
        .atguigu{ 
            font-size:49px;
        }
    }
    </style>

3.7 TodoList案例总结

  • 组件化编码流程:

    • 拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突
    • 实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用

      • 一个组件在用:放在组件自身即可
      • 一些组件在用:放在他们共同的父组件上(<span style="color:red">状态提升</span>)
    • 实现交互:从绑定事件开始
  • props适用于:

    • 父组件 ==> 子组件 通信
    • 子组件 ==> 父组件 通信(要求父先给子一个函数)
  • 使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的!
  • props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做。

3.8 webStorage

  • 存储内容大小一般支持5MB左右(不同浏览器可能还不一样)
  • 浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制

3.8.1 localStorage

  • 作用:用来存储网页缓存数据到浏览器上 (在浏览器application中查看),即便关闭浏览器也还在

    • 存储一个kv键值对

      localStorage.setItem("msg", "niubi")
    • 根据k读取指定v

       alert(localStorage.getItem("wc"))
    • 根据k删除

      localStorage.removeItem("msg")
    • 清空所有

       localStorage.clear()
  • 存储注意事项

    • localStorage只能存储字符串,传入数字自动转为字符串,传入对象自动调用toString方法
    • 若已有同名Key则原有数据会被覆盖
    • 若读取不存在的k时返回null

3.8.2 sessionStorage

  • 作用:用来存储网页缓存数据到浏览器上 (在浏览器application中查看),

区别于localStorage,sessionStorage关闭浏览器以后就被清空了

  • 方法两者相同

3.9 组件的自定义事件

3.9.1 概念

  • 作用:是一种组件间通信方式,适用于子组件 ===>父组件
  • 使用场景: A父组件 B子组件 B想传值到A,那么就要在A中给B绑定自定义事件(事件的回调在A)
  • 自定义组件与props对比

    • 相同点:都可以子传父参数
    • 不同点: props主要用于父传给子

3.9.2 绑定自定义事件

  • 第一种写法

    • 父组件
     <!-- 自定义事件加上.once 只能调用一次 -->
    <Student @getStudentNameOn="getStudentName" />
    
    methods: {
          //可以这样取多个值 可变长度入参,剩余参数会变为一个数组
          // getStudentName(value,...params) {
          getStudentName(value) {
          console.log(value);
        },
    },
    • 子组件
    <button @click="sendStudentName">点我获取学生姓名</button>
      
      methods: {
        sendStudentName() {
        //第一个值为绑定自定义事件名称 第二个值为传递参数
        this.$emit("getStudentNameOn", this.name);
        //可以传递多个值 或者传递对象 接收用同样的方法接收即可
        // this.$emit("getStudentNameOn", this.name, 12, "343", 34);
        },
      },
  • 第二种写法

    • 父组件
      <!-- 通过ref把vc挂载到vm身上,可以更灵活的进行异步操作 -->
      <Student ref="Student" />
        
     methods: {
          //可以这样取多个值 可变长度入参,剩余参数会变为一个数组
          // getStudentName(value,...params) {
          getStudentName(value) {
          console.log(value);
        },
    },
      //生命周期函数 挂载方法
      mounted() {
        //延迟三秒再执行
        setTimeout(() => {
          //$on调用多次  $once只能调一次
          //this.$refs.组件名称.$on("自定义事件名称", 回调函数)
          this.$refs.Student.$on("getStudentNameOn", this.getStudentName);//这里可以直接把方法写进来,但是要注意写成箭头函数,不然this的指向就变为了原生DOM
        }, 3000);
      },
    • 子组件写法相同
  • 注意事项

    • 如果要给组件绑定VUE原生事件,需要加修饰符.native 不然会默认作为自定义事件处理

      <Student @click.native="getStudentName" /> 

3.9.3 解绑定自定义事件

  • 解绑某一个

    this.$off("getStudentNameOn"); //自定义事件名称   //子组件中调用
  • 解绑多个

    this.$off([ "getStudentNameOn","demo"] );
  • 全部解绑

    this.$off()
  • 其他解绑情况 (vm或vc销毁)

     //销毁VM也会使vc自定义事件不奏效  但是原生方法还是会生效的
    this.$destroy(); //销毁了当前Student组件的实例,销毁后所有的Student实例的自定义事件都不会奏效

3.10 全局事件总线

  • 作用:用于任意组件直接的通信 (跨层级用最适合)
  • 核心关系:VueComponent.prototype._proto_ === Vue.prototype ,让组件实例对象VC访问到Vue原型上的属性方法
  • 分析:

    • 组件间通信之前使用的自定义组件,需要用到$on $emit $off ,但是这些方法只有在vm或vc身上才有,我们可以考虑找一个中间件来完成组件间通信
    • 因为Vue.prototype身上的属性 就是每个vc原型的属性,所以所有的vc实例都能看到
    • 因此把vc或者vm实例放到Vue.prototype身上就可以实现功能
    • 我们创建的Vm可以在生命周期初期就把vm挂载上去,这样所有的组间就都可以通过这个vm来完成通信
  • 实现

    • 设置全局总线事件

      import Vue from 'vue'
      import App from './App.vue'
      
      Vue.config.productionTip = false
      
      //加到vue原型实例对象上 所有的vc都能看到
      // Vue.prototype.x = {a:1,b:2}
      
      new Vue({
          render: h=>h(App),
          beforeCreate(){
          //$bus可以改为任意名,统一是为了规范
              Vue.prototype.$bus = this //安装全局事件总线
          }
      }).$mount('#app')
    • 接收端组件(绑定事件端)

      //挂载完成时
      mounted(){
            this.$bus.$on("hello",(data)=>{
              console.log("我收到了Student的数据:",data)
            })
      }
    • 发送端组件(触发事件)

       <button @click="sendStudentName">点我发送姓名</button>
       
        methods: {
          sendStudentName(){
            this.$bus.$emit("hello",this.name)
          }
        }
    • 自定义事件名重复解决办法,只能在文件中事先规定好
    • 销毁组件前要解绑事件(绑定端写 接收端)

        //钩子函数 销毁前
        beforeDestroy() {
          this.$bus.$off("hello")
        }

3.11 消息订阅与发布 (借助第三方库)

  • 安装pubsub.js 可以在任意框架中实现消息订阅与发布

    npm i pubsub-js
  • 订阅方

    //import pubsub from "pubsub-js";  //注意引入
    
    //挂载后
    mounted() {
        //订阅消息  第二值才是传值 可以用下划线站位不会报错
        this.pubid = pubsub.subscribe("hello",(_,data)=>{
          console.log("有人发布了hello消息,hello消息的回调被执行了",data)
        });
      },
        
    //销毁前
    beforeDestroy() {
        //取消订阅  传的是订阅是返回的id而不是消息名称
        pubsub.unsubscribe(this.pubid)
      },
  • 发送方

      <button @click="sendStudentName">点我发送姓名</button>
      
    //import pubsub from "pubsub-js";  //注意引入
        methods: {
        //发布消息
        sendStudentName() {
          pubsub.publish("hello", 666);
        },
      },

3.12 TodoList案例用到的知识点汇总

  • 判断对象身上是否有某一个属性

    "edit" in todoObj  或者
    Object.prototype.hasOwnProperty.call(todoObj, "edit")
  • 按钮悬浮特效

    li button {
      float: right;
      display: none;
      margin-top: 3px;
    }
    li:before {
      content: initial;
    }
    li:last-child {
      border-bottom: none;
    }
    /* 悬浮效果实现 */
    li:hover {
      background-color: #ddd;
    }
    
    /* 实现作用与鼠标划入划出效果一致 */
    li:hover button {
      display: block;
    }
  • $event获取输入框标签属性

     @blur="blurEdit(todoObj.id, $event)" //绑定到失去焦点事件
     const title = e.target.value.trim();//获取输入
  • 点击编辑自动获取输入框焦点

     ref="inputTitle"  //先把输入框绑定到ref上
     
     //触发编辑点击事件时执行
    this.$nextTick(function () {
       this.$refs.inputTitle.focus();
     });
  • this.$nextTick(回调方法)

    • 作用:在下一次DOM更新结束后执行其指定回调
    • 应用场景:当数据改变时,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行(例如案例中的点击事件后获取输入框焦点)

3.13 Vue封装的过渡与动画

  • 作用: 在插入、更新或移除 DOM元素时,在合适的时候给元素添加样式类名
  • 使用<transition>标签包裹

    • name属性指定引用标签名前缀,不写默认为v
    • <transition-group>标签可以包裹多个属性使用,但是要加key值
    • appear属性在开始时即执行一次 添加属性

3.13.1 vue配合动画实现

  • 实例

     <!-- 用transition包裹 且动画名要使用指定的名称 -->
        <!-- 不写名字默认为v前缀 加了名字就是指定前缀的名称 -->
        <transition name="a"><h1 v-show="isShow" class="a">你好啊</h1></transition>
.a {
  background-color: rgb(236, 1, 1);
}

/* reverse反转  linear匀速*/
/* 开始动画*/
.a-enter-active {
  animation: atguigu 1s linear;
}

/* 结束动画*/
.a-leave-active {
  animation: atguigu 1s reverse linear;
}

/* 定义动画 */
@keyframes atguigu {
  from {
    transform: translateX(-100%);
  }
  to {
    transform: translateX(0px);
  }
}

3.13.2 vue动画过渡实现

  • 实例

    <!-- ransition-group 可以包裹多个元素 但是要加key值 否则报错 -->
    <transition-group name="hello">
      <h1 v-show="isShow" key="1">王超</h1>
      <h1 v-show="isShow" key="2">大儿</h1>
    </transition-group>
h1 {
  background-color: rgb(236, 1, 1);
  /* transition: 0.5s linear;
  最好不要破坏原先样式 */
}

/* 进入的过程 离开的过程 */
.hello-enter-active,
.hello-leave-active {
  transition: 0.5s linear;
}

/* 进入的起点 离开的终点 */
.hello-enter,
.hello-leave-to {
  transform: translateX(-100%);
}

/* 进入的终点 离开的起点 */
.hello-enter-to,
.hello-leave {
  transform: translateX(0);
}

3.13.3 vue整合第三方css库实现

  • 推荐插件 Animate.css 官网Animate.style
  • 安装插件 npm i animate.css
  • 实例

    <!-- appear一开始就执行一次 -->
    <transition-group
      appear
      name="animate__animated animate__bounce"
      enter-active-class="animate__swing"
      leave-active-class="animate__backOutUp">
      <h1 v-show="isShow" key="1">王超</h1>
      <h1 v-show="isShow" key="2">大儿</h1>
    </transition-group>
import "animate.css"//一定不能忘了引入
<style scoped>
/*加一个初始背景*/
h1 {
  background-color: rgb(236, 1, 1);
}
</style>

3.14 VUE解决Ajax请求时跨域问题

  • 后端需要加 @CrossOrigin注解
  • 第一种方式

    • 请求地址 http://127.0.0.1:8080/Test
    • 请求代码

      // 需要引入 import axios from "axios";
         
      getStudents() {
      //跨域问题 协议 域名 和端口号必须一致 不一致可以发请求但是获取不到数据
      //服务器直接没有同源策略  通过nginx开启代理服务器 或者通过cli开通代理服务器
      //请求端口号写自身端口号
      axios.get('http://127.0.0.1:8081/zhaoyongbin/Test').then(
      response => {
       console.log("请求后端成功",response.data);
      },
      error => {
      console.log("请求后端失败",error.message);
      }
            );
      },
    • vue.config.js

      // 配置默认属性  该配置最终会与默认属性融合.即便删除该文件也不会有影响
      module.exports = {
          pages: {
      index: {
        //修改入口
        entry: 'src/main.js'
      }
          },
          //关闭语法检查
          lintOnSave: false,
          //开启代理服务器  不能配置多个代理 也不能控制走不走代理
          //端口号设置与请求服务器相同端口
          devServer:{
      proxy:'http://127.0.0.1:8080'
           }
      }
  • 第二种方式

    • 请求地址 http://127.0.0.1:8080/Test
    • 请求代码

      getStudents() {     axios.get('http://127.0.0.1:8081/zhaoyongbin/Test').then(
      response => {
       console.log("请求后端成功",response.data);
      },
      error => {
      console.log("请求后端失败",error.message);
      }
            );
          },
    • vue.config.js

      module.exports = {
          pages: {
      index: {
        //修改入口
        entry: 'src/main.js'
      }
          },
          //关闭语法检查
          lintOnSave: false,
          devServer:{
      proxy:{
        '/zhaoyongbin':{
            target:'http://127.0.0.1:8080',//要请求代理的地址
            pathRewrite:{'^/zhaoyongbin':''},//路径中替换的地方
            ws:true,// 用于支持websocket  默认true
            changeOrigin:true //false显示自身真实地址 true 表示与请求服务器一致 默认true
        }
      }
          }
      }

3.15 vue-resource

  • 作用:发送ajax请求,与axios用法完全一致,vue官方现已不推荐使用
  • 安装 npm i vue-resource
  • 引入使用

    • mian.js 引入并使用
    import Vue from 'vue'
    import App from './App.vue'
    //导入插件
    import vueResource from 'vue-resource'
    
    Vue.config.productionTip = false
    //使用插件  vm vc 会多一个$http
    Vue.use(vueResource)
    
    new Vue({
        render: h => h(App),
        beforeCreate() {
            Vue.prototype.$bus = this
        }
    }).$mount('#app')
    • 发送请求
     //原本axios替换为了this.$http,使用方法完全一样
    this.$http.get("https://api.github.com/search/users?q=" + this.keyWord).then(
         (response) => {
           console.log("获取到数据了", response.data);
           this.$bus.$emit("getUsers",{isLoading:false,UserList: response.data.items});
         },
         (error) => {
           console.log("没有获取到数据", error.message);
             this.$bus.$emit("getUsers",{isLoading:false,errMsg: error.message});
         }
       );
     },

3.16 VUE插槽

  • 插槽概述

    • 当组件内容不确定时,可以通过插槽来实现同一组件不同内容的效果
  • 作用:

    • 让父组件可以向子组件指定的位置插入Html结构,也是一种组间通信的方式,适用于父组件==>子组件

3.16.1默认插槽

  • 适用场景:父组件插入单个HTMl结构到子组件
  • 父组件 插入内容直接写在组件标签中

内容会在父组件中编译完再传到子组件,所以css样式放到父组件和子组件中都会生效

 <div class="contaliner">
    <Category title="美食">
      <!-- <img src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="" /> -->
    </Category>

    <Category title="游戏">
      <ul>
        <li v-for="(g, index) in games" :key="index">{{ g }}</li>
      </ul>
    </Category>

    <Category title="电影">
      <video
        controls
        src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"
      ></video>
    </Category>
  </div>
  • 子组件 使用slot标签定义一个插槽,如果没有插入数据时会展示默认数据

      <div class="category">
        <h3>{{ title }}分类</h3>
        <!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
        <slot>我是一些默认值,当使用者没有传递具体结构时,我会出现</slot>
      </div>

3.16.2 具名插槽

  • 适用场景:父组件插入多个HTMl结构到子组件
  • 父组件 使用 slot属性指定要插入的插槽

    <Category title="美食">
       <img slot="center"  src="https://s1.ax1x.com/2022/04/14/L1NeJI.jpg" alt=""/>
       <!-- 同一个插槽可以放多个数据 但是不建议这么做 可以用template标签包裹 -->
       <!-- <span slot="footer">凉拌菜</span>
       <span slot="footer">好吃</span> -->
      
      <!-- <template v-slot=footer>--> <!--这种写法只能用于template标签 -->
       <template slot="footer">
         <span>凉拌菜</span>
         <span>好吃</span>
       </template>
     </Category>
     <Category title="游戏">
       <ul slot="center">
         <li v-for="(g, index) in games" :key="index">{{ g }}</li>
       </ul>
     </Category>
     <Category title="电影">
       <video slot="center" controls 
              src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"><
         /video>
     </Category>
    </div>
  • 子组件 使用slot标签的name属性指定插槽名称

    <div class="category">
      <h3>{{ title }}分类</h3>
      <slot name="center">我是一些默认值,当使用者没有传递具体结构时,我会出现</slot>
      <slot name="footer">我是一些默认值,当使用者没有传递具体结构时,我会出现</slot>
    </div>

3.16.3 作用域插槽

  • 适用场景: 数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定
  • 父组件

    <div class="contaliner">
      <Category title="游戏">
    <!--scope指定名称 是因为可以接收多个值  -->
    <template scope="a">
    <ul>
      <li v-for="(g, index) in a.games" :key="index">{{ g }}</li>
    </ul>
    </template>
      </Category>
      <Category title="游戏">
    <!--slot-scope和scope两种写法都可以  -->
    <template slot-scope="b">
    <ol>
      <li v-for="(g, index) in b.games" :key="index">{{ g }}</li>
    </ol>
    </template>
      </Category>
      <Category title="游戏">
    <!-- 或者通过解构赋值 只取想要的数据 -->
    <template scope="{games}">
    <h3 v-for="(g, index) in games" :key="index">{{ g }}</h3>
    </template>
      </Category>
    </div>
  • 子组件

    <div class="category">
    <h3>{{ title }}分类</h3>
    <slot :games="games">我是一些默认值,当使用者没有传递具体结构时,我会出现</slot>
      </div>
      
     data() {
    return {
    games: ["红色警戒", "穿越火线", "劲舞团", "超级玛丽"],
    };
    },

第四章 VUEX

4.1 vuex 概述

  • vuex是什么

    • 在vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间的通信技术,且适用于任意组件间通信
  • 什么时候用vuex

    • 多个组件需要共享数据时

4.2 安装VUEX

  • 2022年 vue3已经是默认版本,直接npm i vuex 安装的是vuex4(只能用于vue3),因此只能用vuex3

    npm i vuex@3    

4.3 搭建VUEX环境

  • 在src/store目录下创建index.js

    //该文件用于创建vuex中最为重要的store
    
    import Vue from 'vue'
    //引入vuex
    import Vuex from 'vuex'
    
    //必须在index中引用,不然无法使用
    Vue.use(Vuex)
    
    //准备actions用于响应组件中的动作
    const actions = {}
    
    //准备mutations用于操作数据(state)
    const mutations = {}
    
    //准备state用于存储数据
    const state = {}
    
    //创建并暴露store
    export default new Vuex.Store({
        actions: actions,
        mutations: mutations,
        state,//同名即可简写
    })
    
  • 在main.js中引入

    import Vue from 'vue'
    import App from './App.vue'
    import store from './store'//默认引入index.js
    Vue.config.productionTip = false
    
    new Vue({
        render: h => h(App),
        store,//挂载到vm身上
        beforeCreate() {
            Vue.prototype.$bus = this
        }
    }).$mount('#app')
  • 此时vm身上已经挂载$store

4.4 vuex版求和案例

  • 组件

    <template>
      <div>
        <h1>当前求和为{{$store.state.sum}}</h1>
        <select v-model.number="n">
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        <button @click="increment">+</button>
        <button @click="decrement">-</button>
        <button @click="incrementOdd">当前求和为奇数再加啊</button>
        <button @click="incrementWait">等一等再加</button>
      </div>
    </template>
    
    <script>
    export default {
      name: "Count",
      data() {
        return {
          n: 1, //用户选择的数字
        };
      },
      methods: {
        increment() {//没有业务逻辑的操作,可以直接调用commit
          this.$store.commit('JIA',this.n)
        },
        decrement() {
          this.$store.commit('JIAN',this.n)
        },
        incrementOdd() {
           this.$store.dispatch('jiaOdd',this.n)//需要在actions中定义
        },
        incrementWait() {
           this.$store.dispatch('jiaWait',this.n)
        },
      },
    };
    </script>
    
    <style scoped>
    button {
      margin-left: 5px;
    }
    </style>
  • vuex配置js

    //该文件用于创建vuex中最为重要的store
    
    import Vue from 'vue'
    //引入vuex
    import Vuex from 'vuex'
    
    //必须在index中引用,不然无法使用
    Vue.use(Vuex)
    
    //准备actions用于响应组件中的动作
    const actions = {
        // jia:function(){
        //     console.log("actions中的加被调用了")
        // }
        //context实际上就是迷你版的store
        // jia(context, value) {
        //     console.log("actions中的jia被调用了", context, value)
        //     context.commit('JIA', value)//需要在mutations中定义
        // },
        // jian(context, value) {
        //     context.commit('JIAN', value)//需要在mutations中定义
        // },
        //传递context的好处是 在actions中可以继续调用
        jiaOdd(context, value) {
            //连锁调用dispatch
            context.dispatch('demo',value)
            if (context.state.sum % 2) {
                context.commit('JIA', value)
            }
        },
        jiaWait(context, value) {
            setTimeout(() => {
                context.commit('JIA', value)
            }, 500)
        },
        demo(context, value){
            console.log('拿到值了',value)
            console.log(context)
        }
    }
    
    //准备mutations用于操作数据(state)
    //不要发送请求 也不用
    const mutations = {
        JIA(state, value) {
            console.log("mutations中的JIA被调用了")
            state.sum += value
        },
        JIAN(state, value) {
            state.sum -= value
        }
    }
    
    //准备state用于存储数据
    const state = {
        sum: 0
    }
    
    //创建并暴露store
    export default new Vuex.Store({
        actions: actions,
        mutations: mutations,
        state,//同名即可简写
    })
  • 调用过程

    • main.js先导入store
    • 组件方法通过$store中的dispatch方法调用actions配置项中的方法,如果没有网络请求或其他业务逻辑,组件也可以越过actions,直接通过commit方法调用mutations中的方法
    • action中的方法调用mutations中的方法(注意名称大写),也可以通过context再连锁调用action中的方法
    • mutations中的方法操作state中的数据

4.5 getters配置项

  • 用于将state中的数据进行加工
  • 配置

    const getters = {
        bigSum(state) {
            return state.sum * 10
        }
    }
  • 使用

        <h1>当前求和的十倍为{{$store.getters.bigSum}}</h1>

4.6 mapState 和 mapGetters

  • 作用:简化从state和Getters中读取数据
  • 组件中引入mapState和mapGetters

    import { mapState ,mapGetters} from "vuex"; 
  • 在计算属性中使用

     computed: {
        //借助mapState生成计算属性 对象写法
        // ...mapState({he:'sum',xuexiao:'school',xueke:'subject'}),
    
        //借助mapState生成计算属性 数组写法  此时名称必须与属性名一致才可以
        ...mapState(["sum", "school", "subject"]),
    
        ...mapGetters(["bigSum"]),
      },
  • 页面引用

    <h1>当前求和为{{ sum }}</h1>
    <h1>当前求和的十倍为{{bigSum }}</h1>
    <h2>我来自{{ school }},在学{{ subject }}</h2>

4.7 mapActions和mapMutations

  • 作用:简化从Actions(dispatch)和Mutations(commit)中调用方法
  • 组件中引入mapActions和mapMutations
  • 在方法中使用

      methods: {
        // increment() {
        //   //没有业务逻辑的操作,可以直接调用commit
        //   this.$store.commit("JIA", this.n);
        // },
        // decrement() {
        //   this.$store.commit("JIAN", this.n);
        // },
    
        //对象写法
        // ...mapMutations({increment:'JIA',decrement:'JIAN'}),
        ...mapMutations(["JIA", "JIAN"]),
    
        // incrementOdd() {
        //   this.$store.dispatch("jiaOdd", this.n); //需要在actions中定义
        // },
        // incrementWait() {
        //   this.$store.dispatch("jiaWait", this.n);
        // },
        //对象写法
        // ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'}),
        ...mapActions(["jiaOdd", "jiaWait"]),
      },
  • 页面引用

    <!-- 必须调用时传参 -->
    <button @click="JIA(n)">+</button>
    <button @click="JIAN(n)">-</button>
    <button @click="jiaOdd(n)">当前求和为奇数再加啊</button>
    <button @click="jiaWait(n)">等一等再加</button>

4.8 vuex模块化开发

  • 优点:便于多人协作开发,解决不同页面命名重复问题
  • 总index.js,只做分模块引入管理

    import Vue from 'vue'
    import Vuex from 'vuex'
    import countOptions from './count'
    import personOptions from './person'
    Vue.use(Vuex)
    
    export default new Vuex.Store({
        //模块要写在modules中,并且开启namespaced
        modules: {
            countAbout: countOptions,
            personAbout: personOptions
        }
    })
    
  • count.js

    //求和相关的配置
    export default {
        namespaced: true,//必须开启命名空间 否则Store中不识别
        actions: {
            //传递context的好处是 在actions中可以继续调用
            jiaOdd(context, value) {
                //连锁调用dispatch
                context.dispatch('demo', value)
                if (context.state.sum % 2) {
                    context.commit('JIA', value)
                }
            },
            jiaWait(context, value) {
                setTimeout(() => {
                    context.commit('JIA', value)
                }, 500)
            },
            demo(context, value) {
                console.log('拿到值了', value)
                console.log(context)
            }
        },
        mutations: {
            JIA(state, value) {
                console.log("mutations中的JIA被调用了")
                state.sum += value
            },
            JIAN(state, value) {
                state.sum -= value
            },
        },
        state: {
            sum: 0,
            school: '尚硅谷',
            subject: '前端',
        },
        getters: {
            bigSum(state) {
                return state.sum * 10
            }
        }
    }
  • person.js

    //人员相关的配置
    import axios from "axios"
    import { nanoid } from "nanoid"
    export default {
        namespaced: true,
        actions: {
            //只添加姓王的人
            addPersonWang(context, value) {
                if (value.name.indexOf('王') === 0) {
                    context.commit('ADD_PERSON', value)
                } else {
                    alert('添加的人必须姓王')
                }
            },
            //与后端交互的请求
            addPersonServer(context) {
                axios.get('http://api.uixsj.cn/hitokoto/get?type=social').then(
                    response => {
                        context.commit('ADD_PERSON',{id:nanoid(),name:response.data})
                    },
                    error => {
                        alert(error.message)
                    }
                )
            }
        },
        mutations: {
            ADD_PERSON(state, value) {
                console.log("mutations中的ADD_PERSON被调用了")
                state.personList.unshift(value)
            }
        },
        state: {
            personList: [
                { id: 1, name: '张三' }
            ]
        },
        getters: {
            firstPersonName(state) {
                return state.personList[0].name
            }
        }
    }
  • state写法

    • 原始写法

      //如果用原始方法取state需要state.模块名称.属性
      sum() {
      return this.$store.state.countAbout.sum;
      },
    • map写法

       ...mapState('countAbout',["sum", "school", "subject"]),
  • getters写法

    • 原始写法

      //如果用原始方法取Getters
      firstPersonName() {
      return this.$store.getters["personAbout/firstPersonName"];
      },
    • map写法

      ...mapGetters('countAbout',["bigSum"]),
  • mutations写法

    • 原始写法

      add() {
      const personObj = { id: nanoid(), name: this.name };
      console.log(personObj);
      //直接从commit取不出来 需要加 模块名称/方法
      this.$store.commit("personAbout/ADD_PERSON", personObj);
      this.name = "";
      },
    • map写法

      ...mapMutations('countAbout',["JIA", "JIAN"]),
  • actions写法

    • 原始写法

      addPersonWang() {
           this.$store.dispatch("personAbout/addPersonWang", 
           { id: nanoid(), name: this.name });
           this.name = "";//清空name
      },
    • map写法

        //需要引入对应js    import {mapActions } from "vuex";
        ...mapActions('personAbout',['addPersonServer'])

第五章 路由

5.1 路由概述

  • 什么是路由

    • 一个路由(route)就是一组映射关系 key-value (例如生活中的路由器),多个路由需要路由器(router)进行管理
    • key 为路径, value 可能是 function 或 component
    • vue的路由是实现SPA(single page web application)应用的插件

5.2 使用路由

  • 安装路由

    • 安装vue-router,命令:npm i vue-router (默认是vue3的版本)
    • vue2 安装命令为 npm i vue-router@3
  • 在mian.js中引入并使用路由

    import Vue from 'vue'
    import App from './App.vue'
    import VueRouter from 'vue-router'
    //引入路由器
    import router from './router'
    Vue.config.productionTip = false
    //应用插件
    Vue.use(VueRouter)
    
    new Vue({
        render: h => h(App),
        router,
        beforeCreate() {
            Vue.prototype.$bus = this
        }
    }).$mount('#app')
  • 编写route配置项 (src/router/index.js)

    //该文件专门用于创建整个应用的路由器
    import VueRouter from 'vue-router'
    import About from '../components/About'
    import Home from '../components/Home'
    
    //创建并暴露一个路由器
    export default new VueRouter({
        routes: [
            {path: '/about',
             component: About
            },
            {path: '/home',
             component: Home
            },
        ]
    })
  • 实现切换

    <!-- 原始HTML中我们使用a标签实现页面跳转 -->
    <!-- <a class="list-group-item active" href="./about.html">About</a>
    <a class="list-group-item" href="./home.html">Home</a> -->
    
    <!--  active-class="active"点击时再激活-->
    <router-link  class="list-group-item"  active-class="active"   
    to="/about">About</router-link>
    <router-link  class="list-group-item"  active-class="active" 
    to="/home"  >Home</router-link>
  • 指定显示位置 不必在同一组件

    <router-view></router-view>
  • 注意点

    • 路由器组件会放到pages目录下与一般组件(components)区分开
    • 通过切换,"隐藏"了的路由器组件,默认是被销毁的,需要的时候再去挂载
    • 每个组件都有自己的$route属性,里面存储着自己的路由信息
    • 整个应用只有一个router,可以通过$router属性获取到

5.3 嵌套(多级)路由

  • 编写route配置项

    • 子级路由写在父级路由的children配置项中

      //该文件专门用于创建整个应用的路由器
      import VueRouter from 'vue-router'
      import About from '../pages/About'
      import Home from '../pages/Home'
      import News from '../pages/News'
      import Message from '../pages/Message'
      
      //创建并暴露一个路由器
      export default new VueRouter({
          routes: [
              //一级路由
              { path: '/about',
                  component: About },
              { path: '/home',
                  component: Home,
                  //二级路由
                  children: [{ //子路由无需加/,子路由可以继续配置children
                          path: 'news',
                          component: News, },
                      {  path: 'message',
                          component: Message, },
                  ]},
          ]
      })
  • 实现切换 (写在父级路由组件中,注意路径要包含父级路由)

    <template>
      <div>
        <h2>Home组件内容</h2>
        <div>
          <ul class="nav nav-tabs">
            <li>
              <!-- <a class="list-group-item" href="./home-news.html">News</a> -->
              <router-link class="list-group-item" active-class="active" to='/home/news'>News</router-link>
            </li>
            <li>
              <!-- <a class="list-group-item active" href="./home-message.html">Message</a> -->
                <router-link class="list-group-item" active-class="active" to='/home/message'>Message</router-link>
            </li>
          </ul>
         <router-view></router-view>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      name: "Home",
    };
    </script>

5.4 路由传参

  • 传递参数

    <ul>
       <li v-for="m in messageList" :key="m.id">
         <!-- 跳转路由并携带参数 to字符串写法 -->
         <!-- <router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">
         {{m.title}}</router-link>=-->
    
         <!-- 跳转路由并携带参数 对象写法 -->
         <router-link
           :to="{
             path: '/home/message/detail',
             query: {
               id: m.id,
               title: m.title,
             },
           }"
         >
           {{ m.title }}</router-link
         >
       </li>
     </ul>
  • 接收参数

    <ul>
      <li>消息编号{{$route.query.id}}</li>
      <li>消息内容{{$route.query.title}}</li>
    </ul>

5.5 路由命名

  • 作用:可以简化路由跳转
  • 如何使用

    //配置项中加name属性
    {
         name:'guanyu',
         path: '/about',
         component: About
     },
     {
         path: '/home',
         component: Home,
         children: [{
                 //子路由无需加/,子路由可以继续配置children
                 path: 'news',
                 component: News,
             },
             {
                 path: 'message',
                 component: Message,
                 children:[
                     {
                         name:'xiangqing',
                         path: 'detail',
                         component: Detail,
                     }
                 ]
             },
         ]
     }
  • 简化跳转

    • to字符串简化

       <!-- 简化前--> 
      <router-link class="list-group-item" active-class="active"
       to="/about">About</router-link>
       <!-- 简化后--> 
      <router-link class="list-group-item" active-class="active"
       :to="{ name:'guanyu' }">About</router-link>
  • 对象简化

    <!-- 简化前--> 
     <router-link
        :to="{
          path: '/home/message/detail',
          query: {
            id: m.id,
            title: m.title,
          },
        }">{{ m.title }}</router-link>
    <!-- 简化后--> 
     <router-link
        :to="{
          name: 'xiangqing',
          query: {
            id: m.id,
            title: m.title,
          },
        }">{{ m.title }}</router-link>

5.6 路由的params参数

  • 配置路由声明接收参数

    {
        path:'/home',
        component:Home,
        children:[
            {
                path:'news',
                component:News
            },
            {
                component:Message,
                children:[
                    {
                        name:'xiangqing',
                        path:'detail/:id/:title', //使用占位符声明接收params参数
                        component:Detail
                    }
                ]
            }
        ]
    }
  • 传递参数

    <!-- 跳转并携带params参数,to的字符串写法 -->
    <router-link :to="/home/message/detail/666/你好">跳转</router-link>
                    
    <!-- 跳转并携带params参数,to的对象写法 -->
    <router-link 
        :to="{
            name:'xiangqing',
            params:{
               id:666,
                title:'你好'
            }
        }"
    >跳转</router-link>

特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!

  • 接收参数

    $route.params.id
    $route.params.title

5.7路由的props配置

  • 作用:让组件更方便的接收到参数
  • 三种写法

    • 对象写法 适用用固定值

      {
         name:'xiangqing',
         path: 'detail/:id/:title',
         component: Detail,
         //props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
         props:{a:'222'}
      }
<template>
  <div>
   <li>{{a}}</li>
  </div>
</template>

<script>
export default {
    name:'Detail',
    props:['a']
}
</script>
  • 布尔值写法 适用于接收params参数

    {
       name:'xiangqing',
       path: 'detail/:id/:title',
       component: Detail,
        //props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
       props:true
    }
<template>
  <div>
      <ul>
          <!--原本接收方法 <li>消息编号{{$route.params.id}}</li>
          <li>消息内容{{$route.params.title}}</li> -->
          <li>消息编号{{id}}</li>
          <li>消息内容{{title}}</li>
      </ul>
  </div>
</template>

<script>
export default {
    name:'Detail',
    props:['id','title']
}
</script>
  • 函数写法 适用于接收query参数

     {
        name:'xiangqing',
        path: 'detail',
        component: Detail,
       // props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
        props(route){
    return {
      id:route.query.id,
      title:route.query.title
         }
       }
          //可以结构赋值
          //props({query:{id,title}}){
          //return{id,title}
     }
<template>
  <div>
      <ul>
          <!--原本接收方法 <li>消息编号{{$route.params.id}}</li>
          <li>消息内容{{$route.params.title}}</li> -->
          <li>消息编号{{id}}</li>
          <li>消息内容{{title}}</li>
      </ul>
  </div>
</template>

<script>
export default {
    name:'Detail',
    props:['id','title']
}
</script>

5.8 <router-link>的replace属性

  • 作用:控制路由跳转时操作浏览器历史记录的模式
  • 浏览器的历史记录有两种写入方式:分别为pushreplacepush是追加历史记录,replace是替换当前记录。路由跳转时候默认为push
  • 如何开启replace模式:<router-link replace .......>News</router-link>

5.9 编程式路由导航

  • 作用:不借助<router-link> 实现路由跳转,让路由跳转更加灵活
  • 代码实例 通过$router.push()**和**$router.replace()实现

    <template>
      <div>
        <ul>
          <li v-for="m in messageList" :key="m.id">
            <!-- 跳转路由并携带参数 -->
            <!-- <router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">
            {{m.title}}</router-link>=-->
            <!-- 跳转路由并携带参数 对象写法 -->
            <router-link
              :to="{
                name: 'xiangqing',
                query: {
                  id: m.id,
                  title: m.title,
                },
              }"
            >
              {{ m.title }}</router-link
            >
            <button @click="pushShow(m)">push查看</button>
            <button @click="replaceShow(m)">replace查看</button>
          </li>
        </ul>
        <hr />
        <router-view></router-view>
      </div>
    </template>
    
    <script>
    export default {
      name: "Message",
      data() {
        return {
          messageList: [
            { id: "001", title: "消息001" },
            { id: "002", title: "消息002" },
            { id: "003", title: "消息003" },
          ],
        };
      },
      methods: {
        pushShow(m) {
          this.$router.push({
            name: "xiangqing",
            query: {
              id: m.id,
              title: m.title,
            },
          });
        },
        replaceShow(m) {
          this.$router.replace({
            name: "xiangqing",
            query: {
              id: m.id,
              title: m.title,
            },
          });
        },
      },
    };
    </script>
  • 控制页面前进后退API

    • this.$router.forward() //前进
    • this.$router.back() //后退
    • this.$router.go(n) //前进或后退n步

5.10 缓存路由组件

  • 作用:让不展示的路由组件保持挂载,不被销毁。
  • 具体编码:

    <!--include不指定时默认缓存所有路由,指定的是组件名  
         事实上只缓存有表单输入的路由即可,需要缓存多个的时候用数组字符串
    <keep-alive :include="['News','Message']"> -->
      <keep-alive include="News"> 
        <router-view></router-view>
    </keep-alive>

5.11 两个新的生命周期钩子

  • 作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
  • 具体名字:

    • activated 路由组件被激活时触发
    • deactivated 路由组件失活时触发 (失活并不是销毁)

5.12 路由守卫

  • 作用 :对路由进行权限控制

5.12.1 全局路由守卫

  • 路由属性meta用于配置路由原信息
  • 守卫入参

    • to: Route: 即将要进入的目标 路由对象
    • from: Route: 当前导航正要离开的路由
    • next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖
const router = new VueRouter({
    routes: [
        //一级路由
        {
            name: 'guanyu',
            path: '/about',
            component: About,
            meta: {
                title: "关于"
            }
        },
        {
            path: '/home',
            component: Home,
            meta:{
                title:"主页"
            },
            //二级路由
            children: [{
                //子路由无需加/,子路由可以继续配置children
                path: 'news',
                component: News,
                //meta路由原信息
                meta: {
                    isAuth: true,
                    title: "新闻"
                }
            },
            {
                path: 'message',
                component: Message,
                children: [
                    {
                        name: 'xiangqing',
                        //使用占位符声明接收params参数
                        path: 'detail',
                        component: Detail,
                        // props:{a:'222'} 对象写法
                        //props:true 布尔写法
                        //函数写法  解构赋值
                        props({ query: { id, title } }) {
                            return { id, title }
                        },
                        meta: {
                            title: "消息"
                        }

                    }
                ]
            },
            ]
        },
    ]
})

//全局前置路由守卫  初始化的时候被调用,每次路由切换的时候调用
router.beforeEach((to, from, next) => {
    console.log(to, from)
    if (to.meta.isAuth) {//判断是否要鉴权
        if (localStorage.getItem('school') === 'atguigu') {
            next()
        } else {
            alert('学校名称不对')
        }
    } else {
        next()
    }

})

//全局后置路由守卫  初始化时执行,每次路由切换后执行
router.afterEach((to, from,) => {
    console.log('afterEach', to, from)
    if (to.meta.title) {
        //标题默认读取项目名称  修改index.html中的title达到一致
        document.title = to.meta.title //修改网页的title
    } else {
        document.title = 'vue_test'
    }
})

export default router

5.12.2 独享守卫

  • 注意:独享守卫只有前置路由,没有后置路由,可以搭配全局后置路由使用
//加在路由配置项中,写法与全局前置路由守卫一致
beforeEnter(to,from,next){
    console.log('beforeEnter',to,from)
    if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
        if(localStorage.getItem('school') === 'atguigu'){
            next()
        }else{
            alert('暂无权限查看')
            // next({name:'guanyu'})
        }
    }else{
        next()
    }
}

5.12.3 组件守卫

//写在组件中的配置中
//进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter (to, from, next) {
},
//离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {
}

5.13 路由器的两种工作模式

  • 对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
  • hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
  • 配置路由工作模式

    const router = new VueRouter({
        mode:'history', //默认是hash模式
        routes: [...] })
  • hash模式:

    • 地址中永远带着#号,不美观
    • 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法
    • 兼容性较好
  • history模式:

    • 地址干净,美观
    • 兼容性和hash模式相比略差
    • 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题

第六章 Vue UI组件库

6.1 常用UI组件库

6.2 全局引入elementUI

  • 安装elementUI依赖

    //工程路径下
     npm i element-ui -S
  • main.js下引入插件

    import Vue from 'vue'
    import App from './App.vue'
    //引入elementUI组件库
    import ElementUI from 'element-ui';
    //引入elementUI样式
    import 'element-ui/lib/theme-chalk/index.css';
    Vue.config.productionTip = false
    //应用插件
    Vue.use(ElementUI)
    
    new Vue({
        render: h => h(App),
        beforeCreate() {
            Vue.prototype.$bus = this
        }
    }).$mount('#app')
  • 参照官网复制代码使用即可

6.3 按需引入elementUI

  • 安装 babel-plugin-component

    npm install babel-plugin-component -D
  • 修改babel.config.js 注意不要破坏之前结构

    {
      "presets": [["@babel/preset-env", { "modules": false }]],
      "plugins": [
        [
          "component",
          {
            "libraryName": "element-ui",
            "styleLibraryName": "theme-chalk"
          }
        ]
      ]
    }
  • 在main.js 中按需引入

    import Vue from 'vue'
    import App from './App.vue'
    //按需引入elementUI组件库
    import {Button,Row} from 'element-ui';
    //elementUI样式自动引入
    
    Vue.config.productionTip = false
    //应用插件 一个一行
     Vue.use(Row)
    //可以自定义组件名称
    Vue.component("zyb-button", Button);
    
    
    new Vue({
        render: h => h(App),
        beforeCreate() {
            Vue.prototype.$bus = this
        }
    }).$mount('#app')

第七章 VueJs ajax请求库 axios

7.1 vue-resource

​ vue-resource是Vue.js的插件提供了使用XMLHttpRequest或JSONP进行Web请求和处理响应的服务。 当vue更新到2.0之后,作者就宣告不再对vue-resource更新,而是使用推荐的axios

7.2 axios

Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中

  • 在浏览器中可以帮助我们完成 ajax请求的发送
  • 在node.js中可以向远程接口发送请

7.3 引入axios

import axios from "axios";

7.4 案例

  • get请求

    //通过给定的ID来发送请求
    axios.get('/user?ID=12345')
      .then(function(response){
        console.log(response);
    }).catch(function(err){
        console.log(err);
    }).finally(function(){
    });
    
    //以上请求也可以通过这种方式来发送
    axios.get('/user',{
        params:{
            ID:12345
        }
    }).then(function(response){//请求成功回调函数
        console.log(response);
    }).catch(function(err){//请求失败回调函数
        console.log(err);
    }).finally(function(){//回调函数
    });
  • post请求

    axios.post('/user',{
        name:'张三',
        age:'22'
    }).then(function(res){
        console.log(res);
    }).catch(function(err){
        console.log(err);
    }).finally(function(){
    });
  • 其他请求别名

    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]])
  • 代码实例

       <head>
    <meta charset="utf-8" />
    <title>vuejs中axios数据调用</title>
    <script type="text/javascript" src="js/vue.min.js" ></script>
    <script type="text/javascript" src="js/axios.min.js" ></script>
       </head>
       <body>
    <div id="app">
     {{message}}
    </div>
       </body>
       <script>
    var vm = new Vue({
     el: "#app",
     data: {
        message: 'helloworld'
     },
     methods: {
        init: function(){ 
            alert("传递的参数是:"+this.message);
            axios.get("./data/user.json").then(function(response){
                // alert(response);
                console.log(response);
                alert(JSON.stringify(response));
                alert(response.data[0].username);
           })
        }
     },
    //钩子函数:回调自定义方法
     created: function(){
         this.init();
     }
    });
       </script>
  • 同步请求

方法加async axios前加await

async function init(){

    var data = {}
    var res = await axios.get("./data1.json")
    data = res.data
    console.log(data)

}
init()

第八章 常用js插件整理

8.1 clipboard 点击复制插件

  • 安装 npm install clipboard --save
  • 引入组件

    import Clipboard from "clipboard";
  • 页面

    <el-button type="primary" class="copy_btn"
    :data-clipboard-text="this.baseUrl + this.form.key"
    @click="copy">复制链接 </el-button>
  • js方法

        copy() {
          var clipboard = new Clipboard(".copy_btn");
          clipboard.on("success", (e) => {
            setTimeout(() => {
              console.log(e);
            }, 2000);
            // 释放内存
            clipboard.destroy();
          });
    
          clipboard.on("error", (e) => {
            setTimeout(() => {
              console.log(e);
            }, 2000);
    
            // 释放内存
            clipboard.destroy();
          });
        },
0

评论 (0)

取消