Vue 2

Vue核心

Vue简介

动态构建用户界面渐进式JavaScript框架

特点:

  1. 遵循MVVM模式
  2. 编码简洁、体积小,运行效率高,适合移动 / PC开发
  3. 它本身只关注 UI, 也可以引入其它第三方库开发项目

初识Vue

  1. 想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象
  2. 容器内依旧符合HTML语法,只是加了一些特殊的Vue语法
  3. 容器内的代码被称为:Vue模板

模板语法

插值语法

  1. 功能:用于解析标签体内容
  2. 语法:{{xxx}},xxx会作为js表达式解析
<body>
    full name: <span>{{fullname()}}</span>
</body>
<script>
	new Vue({
        el:'',
        data: {
            
        },
        methods: {
            fullname(){
                return 'xx';
            }
        }
    })
</script>

指令语法

  1. 功能:解析标签属性,解析标签体内容,绑定事件
  2. v-bing:href=‘xxx',xxx会作为js表达式解析
//作为js表达式解析,意味着可以直接使用js能用的方法
<h1>{{name}}</h1>
<a :href="school.name.toUpperCase()">xx</a>

数据绑定

  1. 单向绑定(v-bind):数据只能从data流向页面
  2. 双向绑定(v-model):data和页面互通
  1. 双向绑定一般都应用在表单类元素(如input,select)
  2. v-model:value可以简写为v-model,因为默认采集的就是value值

el和data的两种写法

el

//第一种
new Vue({
    el: '#app',
    data: {
        
    },
})

//第二种
const vm = new Vue({
    data: {
        
    },
})
vm.$mount('#app');

data

//第一种,对象式
data: {
    name: 'xx',
}
//第二种,函数式。组件一定要用这种写法
data(){
    return{
        name: 'xx',
    }
}

MVVM

  1. M:model,模型。对应data中的数据
  2. V:view,视图。模板
  3. VM:viewmodel,vue实例对象

请添加图片描述

  1. data中所有的属性最终都出现在了vm身上
  2. vm身上的所有属性以及Vue原型上所有属性,在Vue模板中都可以直接使用

数据代理

通过一个对象代理对另一个对象中的属性的操作(读 / 写)

let number = 18;
let person = {
    name: 'xx',
    sex: 'a',
};

Object.defineProperty(person, 'age', {
    value: 18;//值
    enumrable: true;//可枚举的,就是能不能遍历得到
    writeble: true;//可修改的
    configurable: true;//可删除的
	
    //当有人读取person的age属性时,该函数就会被调用,返回值就是age的值
    get(){
    	return number;
	}

	//当有人修改age属性时,该函数就会被调用,且会收到修改的具体值
	set(value){
        number = value;
    }
})

事件

基本使用

  1. 使用v-on或者@xx绑定事件,xx是事件名
  2. 事件的回调需要配置在methods对象中
  3. methods中配置的函数**不要使用箭头函数!**否则this就会变成window
  4. @click="demo"@click="demo($event)"效果一致,但是后者可以传参。$event是默认传递的,如果配置了其他形参,则需要在形参列表使用$event占位
  5. 绑定事件的时候可以写一些简单的语句(但是不推荐)

事件修饰符

  1. prevent:阻止默认事件(常用)

    <a href="https://www.baidu.com" @click.prevent="show">click me</a>
    
  2. stop:阻止事件冒泡(常用)

    <div @click="show">
        <!--没加stop修饰之前,会调用两次show方法,就是事件冒泡-->
        <button @click.stop="show">
            click me
        </button>
    </div>
    
  3. once:事件只触发一次(常用)

  4. capture:使用事件的捕获模式

  5. self:只有event.target是当前操作的元素才触发事件

  6. passive:事件的默认行为立刻触发,无需等待事件回调执行完毕

事件修饰符可以连这些,先后发挥作用

@click.stop.prevent="show"

键盘事件

  1. 常用的按键别名:

    • 回车,enter
    • 删除,delete
    • 退出,esc
    • 空格,space
    • 换行,tab(特殊,必须配合keydown使用才有效果)
    • 上,up
    • 下,down
    • 左,left
    • 右,right
    <!-- 松开 enter键 后调用方法-->
    <input type="text" @keyup.enter="show"/>
    
  2. Vue未提供别名的按键,可以使用按键原始的key值去绑定,但要注意转为kebab-case(短横线命名)

    <!-- 松开 capslock键 后调用方法 -->
    <!-- 所有按键的名,可以通过默认传递的event参数,通过event.key输出按键名,event.keyCode输出按键值-->
    <input type="text" @keyup.caps-lock="show"/>
    
  3. 系统修饰符(用法特殊):ctrl,alt,shift,meta

    • 配合keyup事件使用:按下修饰符的同时,再按下其他键,随后释放其他键,事件才会触发
    • 配合keydown事件使用:正常触发事件
    <!-- 按下修饰符的同时,再按下其他键,随后释放其他键,事件才会触发 -->
    <input type="text" @keyup.ctrl="show"/>
    <!-- 也可以直接指定使用什么组合 如下就是使用ctrl+y才会触发 -->
    <input type="text" @keyup.ctrl.y="show"/>
    
  4. 也可以使用keycode去指定具体的按键(不推荐)

    <input type="text" @keyup.13="show"/>
    
  5. Vue.config.keyCodes.自定义键名 = 键码,可以定制

    <body>
        <input type="text" @keyup.自定义键名="show"/>
    </body>
    
    <script>
    	Vue.config.keyCodes.自定义键名 = 自定义键码
    </script>
    

自定义事件

  1. 一种组件间通信的方式,适用于:子组件—》父组件

  2. 使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件,事件的回调函数在A中

  3. 父组件绑定自定义事件:

    1. 在父组件中:<Demo @myEvent="callback" /><Demo v-on:myEvent="callback" />,callback函数定义在父组件中

    2. 在父组件中:

      <template>
      	<!-- 1.声明ref -->
      	<Demo ref="demo"/>
      </template>
      
      <script>
      	new Vue({
              methods: {
                  // 2.定义处理方法
                  test(){
                      ...
                  }
              },
              mounted(){
                  // 3.绑定,接收并使用test方法处理myEvent事件
                  this.$refs.demo.$on('myEvent', this.test);
              }
          })
      </script>
      
  4. 子组件触发自定义事件:this.$emit('myEvent', 数据)

    <template>
    	<button @click="xx"></button>
    </template>
    
    <scrpit>
        methods: {
            xx(){
                this.$emit('myEvent', data);
            }
        }
    </scrpit>
    
  5. 解绑自定义事件:this.$off('myEvent'),解绑myEvent事件;this.$off([]'myEvent','demo']),解绑myEvent和demo事件;this.$off(),解绑所有事件;

  6. 组件上也可以绑定原生DOM事件,需要使用native修饰符,例如:<Demo @click.native="callback" />。没加修饰符只能通过$emit来触发

全局事件总线

  1. 一种组件间通信的方式,适用于任意组件间通信

  2. 安装全局事件总线:

    new Vue({
        ...
        beforeCreate(){
        	// 一般取名叫$bus,也是为了迎合Vue的规范。
        	// this就是当前的vm。因为vm身上有$on,$off,$emit,$once这些方法,所以弄出一个$bus这样的傀儡对象来使用
        	Vue.prototype.$bus = this;
    	}
        ...
    })
    
  3. 使用:

    1. 接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调函数在A中

      methods:{
          demo(data){}
      }
      mounted(){
          // 接收并使用demo方法处理xxx事件
          this.$bus.$on('xxx', this.demo)
      }
      
    2. 提供数据:this.$bus.$emit('xxx', data)

  4. 最好在beforeDestroy中,用$off去解绑当前组件所用到的事件,因为bus只有一个,事件之间可能会重名

计算属性和监视

computed

  1. 定义:要用的属性不存在,要通过已有属性计算得来
  2. 原理:底层借助了Object.defineproperty方法提供的getter和setter
  3. get函数什么时候执行?
    • 初次读取的时候执行,并缓存可供复用
    • 当依赖的数据发生变化
  4. 计算属性最终会出现在vm中,可以直接使用
<body>
    <span>{{fullName1()}}</span>
    <!--表面上fullName2是一个函数,实则是vue执行了fullName2声明的函数然后生成了一个fullName2属性,放在了vm里可供直接调用 -->
    <span>{{fullName2}}</span>
</body>

<script>
	computed: {
        //完整写法
        fullName1: {
            get(){
                ...
            }
            set(){
                ....
            }
        }
            
        //简写,考虑到计算属性一般不会涉及修改才能简写
        fullName2(){
            ....
        }
    }
</script>

watch

  1. 监视属性必须存在才有意义,可以是data里的,也是可以computed计算出来的
  2. 当属性变化时,回调函数自动调用,在函数内部进行计算
两种写法
  • new Vue传入watch配置
  • 通过vm.$watch监视
<script>
    //方式一
	new Vue({
        el:'',
        data: {
            name: 'ss'.
        }
        watch: {
            name: {
        		//具体的配置信息以及回调函数
    		}
       }
    })
    
    //方式二
    let vm = new Vue({
        ...
    })
   	vm.$watch('要监视的值', {
        //具体的配置以及回调函数
    })
</script>
监视多级结构中某个属性的变化
<script>
	new Vue({
        data: {
            numbers: {
                a: 1,
                b: 2,
            }
        },
        
        watch: {
        	//最原始的写法,只是平时我们省略了引号
            'numbers.a':{
                //hanlder函数可以获取新旧值
        		handler(newValue, oldValue){
        			...
    			}
    		},
            'numbers.b':{
        		handler(){
        			...
    			}
    		},
        },
        
        watch: {
        	//第二种写法
            numbers: {
                deep: true,
                handler(){
                    ...
                }
            }
        }
    })
</script>
简写形式

只关注handler函数,其他配置不关心时才可以这么写

<script>
	new Vue({
        data: {
            name: 'xx',
        }
        watch: {
            name(){
        		//平时handler的处理逻辑照常写	
    		}
        }
    })
</script>

区别

computed能完成的,watch也能完成;反之不能,watch可以进行异步操作

绑定样式

绑定class

<style>
    .normal{}
    .happy{}
    .sad{}
    .class1{}
    .class2{}
    .class3{}
</style>
<body>
    <!--字符串写法,适用于:样式类名不确定,需要动态指定 -->
    <div :class="mood" @click="changeMood"></div>
    <!--数组写法,适用于:要绑定的样式个数不确定,名字也不确定 -->
    <div :class="classArr"></div>
    <!--对象写法,适用于:要绑定的样式个数确定,名字也确定,但需要动态指定用不用 -->
    <div :class="classObj"></div>
</body>

<script>
	new Vue({
        el:'#root',
        data:{
            name: 'xx',
            mood: 'normal',
            classArr: ['class1', 'class2', 'class3'],
            classObj: {
                class1: false,
                class2: false,
            }
        },
        methods:{
            changeMood(){
                this.mood = 'happy';
            }
        }
    })
</script>

绑定style

<body>
    <div :style="styleObj"></div>
    <div :style="{fontSize: }"></div>
</body>

<script>
	new Vue({
        el:'#root',
        data:{
            name: 'xx',
            styleObj: {
                //这里需要写style原本就有样式名,原本的短横线写法该为驼峰写法,比如
                fontSize: '40px',
                backgroudColor: 'orange',
            }
        },
    })
</script>

条件渲染

v-if

v-if="表达式"
v-else-if="表达式"
v-else

适用于:切换频率比较低的场景

特点:不展示的DOM元素直接移除

注意:上三个可以配合使用,但是中间不能被其他的打断

v-show

v-show="表达式"

适用于:切换频率比较高的场景

特点:不展示的DOM元素未被移除,只是隐藏

使用v-if的时候元素可能无法获取到,v-show一定可以获取到

本质就是因为v-if会移除DOM元素

列表渲染

基本使用

可以使用v-for遍历数组,对象,字符串(用的少),指定次数(用的更少)

v-for="(item,index) in xxx" :key="xxx"
<!-- 括号里写的是遍历时的默认参数 -->
<!-- 遍历数组 -->
<h2>人员列表(遍历数组)</h2>
<ul>
    <li v-for="(p,index) in persons" :key="index">
        {{p.name}}-{{p.age}}
    </li>
</ul>

<!-- 遍历对象 -->
<h2>汽车信息(遍历对象)</h2>
<ul>
    <li v-for="(value,k) in car" :key="k">
        {{k}}-{{value}}
    </li>
</ul>

<!-- 遍历字符串 -->
<h2>测试遍历字符串(用得少)</h2>
<ul>
    <li v-for="(char,index) in str" :key="index">
        {{char}}-{{index}}
    </li>
</ul>

<!-- 遍历指定次数 -->
<h2>测试遍历指定次数(用得少)</h2>
<ul>
    <li v-for="(number,index) in 5" :key="index">
        {{index}}-{{number}}
    </li>
</ul>

key的原理

  1. 虚拟DOM中key的作用

    key是虚拟DOM对象的标识,当数据发生改变时,Vue会根据新数据生成新的虚拟DOM,随后Vue进行DOM差异比较,规则如下:

    1. 旧DOM中找到了与新DOM相同的key
      • 如内容没变,则直接使用
      • 如内容改变,生成新的DOM替换
    2. 未找到相同的key,创建新的DOM渲染到页面中
  2. 用index作为key的问题

    1. 若对数据进行:逆序添加,逆序删除等破坏顺序的操作,会产生没有必要的真实DOM更新。界面没问题,但是效率低
    2. 如果结构中还包含输入类的DOM,页面就会出错,对应不上的问题,错位。
  3. 最好使用唯一标识来作为key

监视数据原理

  1. Vue会监视data中所有层次的数据

  2. 如何监测对象中的数据?

    通过setter实现监视,且要在new Vue时就传入要监测的数据

    1. 对象中后追加的属性,Vue默认不做响应式处理

    2. 如需给后添加的属性做响应式处理,请使用:

      Vue.set(target, propertyName/index, value)this.$set(target, propertyName/index, value)
      
  3. 如何监测数组中的数据?

    通过包裹数组更新元素的方法实现,本质上就是做了两件事:

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

    1. push(),pop(),shift(),unshift(),splice(),sort(),reverse()
    2. Vue.set() this.$set()
    

Vue.set()this.$set()不能给vm或vm的根数据对象添加属性

v-model

主要用于收集表单数据。

  • input.type=text,则收集的是用户输入的value
  • input.type=radio,则收集的是用户输入的value,且需要配置value的值
  • input.type=checkbox
    • 没有配置value属性,则收集的是checked(勾选与否,布尔值)
    • 配置了value属性:
      • v-model的初始值是非数组,收集的是checked
      • 初始值是数组,收集的就是value组成的数组

v-model有三个修饰符:

  • lazy:控件失去焦点后收集数据
  • number:输入的字符串转为有效的数字
  • trim:去除数据的首尾空格

过滤器

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

语法:

  1. 注册过滤器:Vue.filter(name, callbakc)或者new Vue({ filters: {} })
  2. 使用过滤器:{{ xxx | 过滤器名 }}v-bind:xx="xxx | 过滤器"

注意:

  1. 过滤器默认传递正在使用该过滤器的值作为参数,也可以接收额外的参数。
  2. 多个过滤器可以串联,xx传递给过滤器1,过滤器1的返回值传递给过滤器2做参数
  3. 并没有改变原本的数据,是产生了新的对应的数据

内置指令

常用内置指令

  1. v-text : 更新元素的 textContent

  2. v-html : 更新元素的 innerHTML。会替换掉节点中所有的内容,{{xx}}不会;且可以识别HTML结构

  3. v-if : 如果为 true, 当前标签才会输出到页面

  4. v-else: 如果为 false, 当前标签才会输出到页面

  5. v-show : 通过控制 display 样式来控制显示/隐藏

  6. v-for : 遍历数组/对象

  7. v-on : 绑定事件监听, 一般简写为@

  8. v-bind : 绑定解析表达式, 可以省略 v-bind

  9. v-model : 双向数据绑定

    以下只是一个属性,不需要接收一个表达式

  10. v-cloak : 防止闪现(数据太多有时候绑定没那么快,会在页面直接显示出插值语法的花括号),一般与css的属性选择器配合使用: [v-cloak] { display: none }

  11. v-once:所在节点在初次动态渲染后,就视为静态内容了。以后数据改变不会影响所在结构的更新

  12. v-pre:跳过其所在节点的编译过程,可以利用它跳过没有指令语法,插值语法的节点,加快编译

自定义指令

注册全局指令
<script>
    //el和binding分别接收两个参数:一个是原生DOM元素,就是当前指令和哪个DOM绑定;另一个是一个绑定关系,内部会有一些相关的数据
    Vue.directive('my-directive', function(el, binding){
        el.innerHTML = binding.value.toupperCase()
    })
</script>
注册局部指令
<script>
    new Vue({
        //指令里出现的this都是只想window,因为指令本质就是DOM操作
        directives : {
            //完整写法,多级单词需要用 - 隔开,且用引号包裹
            'my-directive': {
                //bind是内置的函数,类似接口。现在是重写,还有inserted,update
                bind (el, binding) {
                    el.innerHTML = binding.value.toupperCase()
                },
                inserted (el, binding) {
                    ...
                },
                update (el, binding) {
                    ...
                }
            },
            
            //简写,相当于只写了bind函数和update函数
            'my-directive'(el, binding){
                el.innerHTML = binding.value.toupperCase();
            },
        },
    })
</script>

插槽—slot

  1. 作用:让父组件可以向子组件指定位置插入html结构,也是一种父子通信方式,适用于父组件—》子组件

  2. 分类:默认插槽,具名插槽,作用于插槽

  3. 使用方式:

    1. 默认插槽:只有一个坑

      父组件:
      <Category>
          <!-- 以下div会出现在子组件的slot中 -->
          <!-- 在这填坑 -->
      	<div>
              html结构
          </div>
      </Category>
      
      子组件Category:
      <template>
      	<div>
              <!-- 在这挖个坑,声明有个坑 -->
              <slot>插槽默认内容...</slot>
          </div>
      </template>
      
    2. 具名插槽:多个坑

      父组件:
      <Category>
          <!-- 第一个坑,写法1 -->
      	<template slot="center">
          	<div>
                  html结构
              </div>
          </template>
          <!-- 第二个坑,写法2 -->
          <template v-slot:footer>
          	<div>
                  html结构
              </div>
          </template>
      </Category>
      
      子组件Category:
      <template>
      	<div>
              <!-- 定义插槽,挖了两个坑 -->
              <slot name="center">default things...</slot>
              <slot name="footer">default things...</slot>
          </div>
      </template>
      
    3. 作用域插槽:根据Category使用的作用域不同,会有不同的样式。例如第一个作用域想用无序列表,第二个作用域想用有序列表

      父组件中:
      <Category>
      	<template scope="scopeData">
          	<!-- 生成的是ul列表 -->
              <ul>
                  <!-- scopeData是一个对象,里面含有子组件传过来的值 -->
                  <li v-for="g in scopeData.games" :key="g">{{g}}</li>
              </ul>
          </template>
      </Category>
      <Category>
      	<template scope="scopeData">
          	<!-- 生成的是ol列表 -->
              <ol>
                  <!-- scopeData是一个对象,里面含有子组件传过来的值 -->
                  <li v-for="g in scopeData.games" :key="g">{{g}}</li>
              </ol>
          </template>
      </Category>
      
      子组件Category中:
      <template>
      	<div>
              <!-- 挖坑,给使用这个插槽的人传递对象数据,对象中包含games属性,games属性的值是gamesHere -->
              <slot :games="gamesHere"></slot>
          </div>
      </template>
      <script>
      	export default {
              name: 'Category',
              data(){
                  return{
                      gamesHere: ['a','b','c'],
                  }
              },
          }
      </script>
      

生命周期

又叫:生命周期回调函数,生命周期函数,生命周期钩子,是Vue在关键时刻帮我们调用的一些特殊名称的函数。

生命周期函数的名字不可更改,但函数的具体内容由实际而定

生命周期函数中的this指向的是vm或组件实例对象

请添加图片描述

Vue组件化编程

非单文件组件

基本使用

  1. 使用Vue.extend(options)创建,其中options和new Vue时传入的那个options几乎一样,区别如下:

    1. el不要写——最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器
    2. data必须写成函数——避免组件被复用时,数据存在引用关系

    使用template标签可以配置组件结构,也就是HTML结构

  2. 注册组件。

    1. 局部注册:new Vue时传入components选项
    2. 全局注册:Vue.component('组件名', 组件)
  3. 在HTML中编写组件标签即可

注意点

  1. 关于组件名:

    1. 一个单词组成:

      • 首字母大 / 小写
    2. 多个单词组成:

      • kebab-case命名:my-school
      • CameCase命名:MySchool(需要Vue脚手架支持
      1. 组件名尽可能回避HTML中已有的元素名称
      2. 可以使用name配置项指定组件在开发者工具中呈现的名字
  2. 关于组件标签:

    1. 不用脚手架会导致后续组件不能渲染

VueComponent

  1. 组件本质是一个名为VueComponent构造函数,且不是程序员定义的,是Vue.extend生成的
  2. 我们只需要写自定义的组件名,Vue解析时会帮我们创建组件的实例对象,即Vue帮我们执行的:new VueComponent(options)
  3. 特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent
  4. 关于this的指向:
    1. 组件配置中:data函数,methods中的函数,watch中的函数,computed中的函数,都是指向VueComponent实例对象
    2. new Vue()配置中:data函数,methods中的函数,watch中的函数,computed中的函数,都是指向Vue实例对象
    3. vm管理着一个个的vc

请添加图片描述

一个重要的内置关系

具体组件.prototype.__proto__ === Vue.prototype

意义:让组件实例对象vc可以访问到Vue原型上的属性、方法

请添加图片描述

单文件组件

创建组件

<template>
	<div class="demo">
		<h2>学校名称:{{name}}</h2>
		<h2>学校地址:{{address}}</h2>
		<button @click="showName">点我提示学校名</button>	
	</div>
</template>

<script>
	 export default {
		name:'School',
		data(){
			return {
				name:'尚硅谷',
				address:'北京昌平'
			}
		},
		methods: {
			showName(){
				alert(this.name)
			}
		},
	}
</script>

<style>
	.demo{
		background-color: orange;
	}
</style>
<template>
	<div>
		<h2>学生姓名:{{name}}</h2>
		<h2>学生年龄:{{age}}</h2>
	</div>
</template>

<script>
	 export default {
		name:'Student',
		data(){
			return {
				name:'张三',
				age:18
			}
		}
	}
</script>

App组件汇总

<template>
	<div>
		<School></School>
		<Student></Student>
	</div>
</template>

<script>
	//引入组件
	import School from './School.vue'
	import Student from './Student.vue'

	export default {
		name:'App',
		components:{
			School,
			Student
		}
	}
</script>

程序的主入口——main.js

import App from './App.vue'

new Vue({
	el:'#root',
	template:`<App></App>`,
	components:{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>

流程

  1. 拆分静态组件:组件要按照功能点拆分,命名不要与HTML冲突
  2. 实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:
    1. 一个组件在用:放在组件自身就可以
    2. 多个组件在用:放在他们共同的父组件(状态提升
  3. 实现交互

使用Vue脚手架

render函数

new Vue({
    el: '#root',//挂载方式1
    render: h => h(App组件,程序入口的那个组件),
}).$mount('#root')//挂载方式2
  1. vue.js与vue.runtime.xxx.js的区别:vue.js是完整版的vue,包含:核心功能+模板解析器;另一个没有模板解析器
  2. 因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去制定具体内容

修改默认配置

  1. 使用vue inspect > output.js命令可以查看vue脚手架的默认配置
  2. 创建并使用vue.config.js可以对脚手架进行个性化定制,具体参数详情看官网

ref属性

  1. 被用来给元素或子组件注册引用信息(id的替代者)
  2. 应用在html标签上获取的是真实的DOM元素,应用在组件标签上获取的是组件实例对象vc
  3. 使用方式:
    1. <h1 ref="xxx"></h1> 或 <school ref="xxx"></school>
    2. 获取:this.$ref.具体的ref名称

props配置

功能:让组件接收外部传来的数据,就类似HTML标签的各种属性。还可以用来父子组件通信(但是更多的还是使用自定义事件来通信)

  1. 传递数据:<Demo name="xxx" :age="18">
  2. 接收数据:
    1. 只接收:props: ['name', 'age']
    2. 限制类型:props: {name: String, age: Number}
    3. 限制类型+限制必要性+指定默认值:props: {name: {type: String, required: true, default: 'xx'}, }

props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告

这里要理解好什么算修改:对于基本数据类型,修改值就是修改;对于对象数组而言,没有改变地址就不算修改,修改内部的数据不算

适用于:

  1. 父组件—》子组件通信

  2. 子组件—》父组件通信,要求父先给子传递一个函数

    <!-- 父组件 -->
    <template>
    	<!-- 2.给子组件传递 -->
    	<MyFooter :checkAllTodo="checkAllTodo"></MyFooter>
    </template>
    <script>
    	import MyFooter from './components/MyFooter'
        export default {
            name: 'App',
            components: {MyFooter},
            methods: {
                // 1.声明一个需要给子组件使用的方法
                checkAllTodo(){
                    ...
                }
            }
        }
    </script>
    
    
    <!-- 子组件 -->
    <template>
    	<input type="checkbox" @change="checkAll"/>
    </template>
    <script>
    	import MyFooter from './components/MyFooter'
        export default {
            name: 'App',
            // 3.声明接收
            props: ['checkAllTodo'],
            methods: {
                checkAll(){
                    // 4.使用
                    this.checkAllTodo();
                }
            }
        }
    </script>
    

mixin混入

功能:可以把多个组件共用的配置提取成一个混入对象

使用方式:

  1. 定义混合,例如:

    //xx.js
    export const mixin = {
        data(){},
        methos:{}
    }
    
  2. 使用混合

    1. 全局混入:在main.js中import对应的混入文件,使用Vue.mixin(xxx)

    2. 局部混入:

      new Vue({
          data(){},
          methods: {},
          mixins: [mixin],//只有一个也要写成数组形式
      })
      

如果mixin配置的是钩子函数,比如mounted,则会先执行mixin中配置的钩子函数,再执行本身的钩子函数

如果配置的是其他信息,则就近原则。

插件

功能:用于增强Vue

本质:包含install方法的一个对象,install方法的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据

定义插件:

//plugins.js
export default {
    install(Vue, options){
        ...
    }
}

使用插件:

//main.js
import 插件名(随意) from '插件路径'
Vue.user(插件名, 参数1, 参数2...);

scoped样式

作用:让样式在局部生效,防止同名冲突

写法:<style scoped>

浏览器存储

  1. 存储内容大小一般支持5MB左右,不同浏览器不一样
  2. 浏览器通过Window.sessionStorageWindow.localStorage属性来实现本地存储机制
  3. 相关API:
    1. xxxStorage.setItem
    2. xxxStorage.getItem
    3. xxxStorage.removeItem
    4. xxxStorage.clear
  1. sessionStorage存储的内容会随着浏览器窗口关闭而消失
  2. LocalStorage存储的内容,需要手动消除才会消失
  3. getItem如果对应的value获取不到,则会返回null
  4. JSON.parse(null)的结果依然是null,JSON.stringfy()用于转化对象

消息订阅与发布(以pubsub举例)

  1. 一种组件间的通信的方式,适用于任意组件间通信
  2. 使用步骤:
    1. 安装pubsub:npm i pubsub-js
    2. 引入pubhub:import pubhub from 'pubhub-js'
    3. 接收数据:this.pid = pubhub.subscribe('xxx', 回调函数),pid是用于解绑事件
    4. 提供数据:pubhub.publish('xxx',data)
    5. 最好在beforeDestroy中使用pubhub.unsubscribe('xxx')取消订阅

$nextTick

  1. 语法:this.$netxTick(回调函数)
  2. 作用:在下一次DOM更新结束后执行其指定的回调
  3. 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行

过度与动画

  1. 作用:在插入、更新或移除DOM元素时,在合适的时候给元素添加样式类名。

  2. 图示:

请添加图片描述

  1. 写法:

    1. 准备好样式(css):

      • 元素进入的样式:
        1. v-enter:进入的起点
        2. v-enter-active:进入过程中
        3. v-enter-to:进入的终点
      • 元素离开的样式:
        1. v-leave:离开的起点
        2. v-leave-active:离开过程中
        3. v-leave-to:离开的终点
    2. 使用<transition>包裹要过度的元素,并配置name属性

      <transition name="hello">
      	<h1 v-show="isShow">xx</h1>
      </transition>
      
      • 配置了name属性后,上述的v-enter等就需要改写成hello-enterv-enter只是默认
      • 若有多个元素需要过度,则需要使用<transition-group>,且每个元素都要制定key值

Vue中的Ajax

配置代理

代理服务器就像一个中间商,对前端来说,和前端保持同源;对后端来说也保持同源(伪同源),这样就可以接收数据并转发给前端

方式一

在Vue.config.js中添加如下配置:

devServer: {
    proxy: "http://localhost:5000"
}
  1. 优点:配置简单,请求资源时直接发给前端即可。前端直接使用axios不会有跨域问题
  2. 缺点:不能配置多个代理,不能灵活的控制请求是否走代理
  3. 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器(优先匹配前端资源,public目录下的文件如果匹配上了就会直接返回,不走代理)

方式二

编写Vue.config.js配置,具体如下:

module.export = {
    devServer: {
        proxy: {
            '/api1': {//匹配所有以‘/api1’开头的请求
                target: 'http://localhost:5000',//代理目标的基础路径
                changeOrigin: true,//设置为true时,服务器收到的请求头中的host为:localhost:5000;反之为8080。
                //就类似是否说谎,false就表示老老实实承认自己是为8080代理。
                
                pathRewrite: {"正则表达式" : "替换成什么字符"},
            },
            '/api2': {//匹配所有以‘/api2’开头的请求
                target: 'http://localhost:5000',
                changeOrigin: true,
                pathRewrite: {"正则表达式" : "替换成什么字符"},
            }
        }
    }
}
  1. 优点:可以配置多个代理,且可以灵活的控制请求是否走代理
  2. 缺点:配置繁琐,请求资源时必须加前缀

Vuex

概念

专门在Vue中实现集中式状态(数据)管理的一个Vue插件,对Vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间的通信方式,且适用于任意组件间的通信

请添加图片描述

三大部分的外围其实是由store所管理

何时使用

  1. 多个组件依赖同一状态
  2. 来自不同组件的行为需要变更同一状态

搭建

  1. 创建文件:src/store/index.js

    //引入Vue核心库
    import Vue from 'vue'
    //引入Vuex
    import Vuex from 'vuex'
    //应用Vuex
    Vue.use(Vuex);
    //准备actions对象——响应组件中用户的动作
    const actions = {};
    //准备mutations对象——修改state中的数据
    const mutations ={};
    //准备state对象——保存具体的数据
    const state = {};
    //创建并暴露
    export default new Vuex.Store({
        actions,
        mutations,
        state
    })
    
  2. 在main.js中创建vm时传入store配置项

    import store from './store/index.js'
    
    new Vue({
        el: '#app',
        render: h => h(App),
        store,
    })
    

基本使用

  1. 初始化数据,配置actions,mutations,操作文件store.js

    //引入Vue核心库
    import Vue from 'vue'
    //引入Vuex
    import Vuex from 'vuex'
    //应用Vuex
    Vue.use(Vuex);
    //准备actions对象——响应组件中用户的动作
    const actions = {
        //默认传递两个参数,一个是上下文对象,一个是传递的value
        //context对象里包含commit,dispatch方法,state数据
        jia(context, value){
            context.commit('JIA', value);
        },
    };
    //准备mutations对象——修改state中的数据
    const mutations ={
        JIA(state, value){
            state.sum += value;
        },
    };
    //准备state对象——保存具体的数据
    const state = {
        sum: 0,
    };
    //创建并暴露
    export default new Vuex.Store({
        actions,
        mutations,
        state
    })
    
  2. 组件中读取vuex中的数据:$store.state.sum

  3. 组件中修改vuex中的数据:$store.dispatch(actions中的方法名, 数据)$store.commit(mutations中的方法名, 数据)

    若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写dispatch,直接写commit

getters的使用

概念:当state中的数据需要加工后才能使用时,可以使用getters加工

  1. 操作store/index.js
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex
Vue.use(Vuex);

//准备actions对象——响应组件中用户的动作
const actions = {
    ...
};
//准备mutations对象——修改state中的数据
const mutations ={
    ...
};
//准备state对象——保存具体的数据
const state = {
    ...
};
// 用于将state中的值进行加工
const getters = {
    bigSum(state){
        return state.xx * 10;
    }
};

//创建并暴露
export default new Vuex.Store({
    actions,
    mutations,
    state,
    getters
})
  1. 组件中使用:$store.getters.bigSum

四个map方法的使用

  1. mapState:用于帮助我们映射state中的数据为计算属性
  2. mapGetters:用于帮助我们映射getters中的数据为计算属性
  3. mapActions:用于帮助我们生成actions对话的方法,即:包含$store.dispatch(xxx)的函数
  4. mapMutations:用于帮助我们映射mutations对话的方法,即:包含$store.commit(xxx)的函数
<script>
    import {mapState, mapGetters, mapActions, mapMutations} from 'vuex'

    export default {
        name: 'xx',
        data() {
        },
        computed: {
            // 1.mapState
            // 借助mapState生成计算属性:sum,school,subject(对象写法)
            // 一定要写成字符串形式,因为不写就会被当成变量,在state中就可能出现找不到该变量的情况,写成字符串才会找对应名称的值
            ...mapState({sum: 'sum', school: 'school', subject: 'subject'}),

            // 数组写法
            ...mapState(['sum', 'school', 'subject']),


            // 2.mapGetters
            // 借助mapGetters生成计算属性:bigSum(对象写法)
            ...mapGetters({bigSum: 'bigSum'}),

            // 数组写法
            ...mapGetters(['bigSum']),
        },
        method: {
            // 1.mapActions
            // 靠mapActions生成:incrementOdd,incrementWait(对象写法)
            ...mapActions({incrementOdd:'jiaOdd', incrementWait:'jiaWait'}),

            // 靠mapActions生成:incrementOdd,incrementWait(数组写法)
            ...mapActions(['jiaOdd', 'jiaWait']),

            // 2.mapMutations
            // 靠mapMutations生成:increment,decrement(对象写法)
            ...mapMutations({increment:'JIA', decrement:'JIAN'}),

            // 靠mapMutations生成:increment,decrement(数组写法)
            ...mapMutations(['JIA', 'JIAN']),
        }
    }
</script>

mapActions和mapMutations使用时,若需要传递参数,需要在模板中绑定事件时传递好参数,否则参数是事件对象

例如:<button @click="increnment(n)"></button>

因为使用mapActions生成时,它生成的是:increment(value){…对value的操作…},所以在绑定事件时,如果不指明参数,方法也会默认传递参数,这个参数是事件对象

模块化+命名空间

  1. 目的:让代码更好维护,让多种数据分类更加明确
  2. 修改store/index.js
const countAbout = {
    // 开启命名空间
    namespaced: true,
    state: {x: 1},
    mutations: {...},
    actions: {...},
    getters: {...}
}
              
const personAbout = {
    // 开启命名空间
    namespaced: true,
    state: {x: 1},
    mutations: {...},
    actions: {...},
    getters: {...}
}
              
const store = new Vuex.Store({
	modules: {
    	countAbout,
    	personAbout,
   	}
})
  1. 开启命名空间后,组件中读取state数据

    // 1.自己直接读取
    this.$store.state.personAbout.list
    // 2.借助mapState,不开启命名空间,就会报错:countAbout找不到
    ...mapState('countAbout', ['sum','school','subject'])
    ...mapState('countAbout',{sum: 'sum', school: 'school', subject: 'subject'}),
    
  2. 开启命名空间后,组件中读取getters数据

    // 1.自己直接读取,getters里是不分的,它是用如下很长的键,来标注
    this.$store.getters['personAbout/firstPersonName']
    // 2.借助mapGetters,不开启命名空间,就会报错
    ...mapGetters('countAbout', ['bigSum'])
    
  3. 组件中调用dispatch方法

    // 1.自己直接读取,dispatch里是不分的,它是用如下很长的键,来标注
    this.$store.dispatch('personAbout/firstPersonName', 参数)
    // 2.借助mapActions,不开启命名空间,就会报错
    ...mapActions('countAbout', {bigSum:'bigSum'})
    
  4. 组件中调用commit方法

    // 1.自己直接读取,commit里是不分的,它是用如下很长的键,来标注
    this.$store.commit('personAbout/firstPersonName', 参数)
    // 2.借助mapMutations,不开启命名空间,就会报错
    ...mapMutations('countAbout', ['bigSum'])
    

Vue-Router

相关理解

  1. vue-router:vue的一个插件库,专门用来实现SPA(single page web application)应用

SPA:

  1. 单页web应用
  2. 整个应用只有一个完整的页面
  3. 点击页面中的导航链接不会刷新页面,只会做页面的局部更新
  4. 数据需要通过ajax请求获取
  1. 路由

    路由就是一组映射关系(key-value),key为路径,value可能是function或component

    路由分类:

    1. 后端路由:value为function,用于处理客户端提交的请求。

      工作过程:服务器接收到一个请求时,根据请求路径找到匹配的函数来处理请求,返回响应数据

    2. 前端路由:value为component,用户展示页面内容

      工作过程:当浏览器的路径改变时,对应的组件就会显示。

基本使用

  1. 安装vue-router,命令:npm i vue-router

  2. 应用插件:Vue.use(VueRouter)

  3. 编写router配置项:

    // 引入VueRouter
    import VueRouter from 'vue-router'
    // 引入所需组件
    import About from '../../'
    import Home from './../..'
    
    // 创建并暴露
    export default new VueRouter({
        routes: [
            {
                path: '/about',
                component: About,
            },
            {
                path: '/home',
                component: Home,
            }
        ]
    })
    
  4. 实现切换(active-class可配置激活时的样式)

    <!-- to表示指向的路径 -->
    <router-link active-class="active" to="/about">About</router-link>
    
  5. 指定展示位置

    <router-view></router-view>
    
  1. 路由组件通常存放在pages文件夹,一般组件通常存放在component文件夹
  2. 通过切换,“隐藏”了的路由组件,默认是被销毁的,需要的时候再重新挂载
  3. 每个组件都有自己的$route属性,里面存储着自己的路由信息
  4. 整个应用只有一个router,可以通过组件的$router属性获取到

嵌套路由

  1. 配置路由规则,使用children配置项

    // 引入VueRouter
    import VueRouter from 'vue-router'
    // 引入所需组件
    import About from '../../'
    import Home from './../..'
    
    // 创建并暴露
    export default new VueRouter({
        routes: [
            {
                path: '/about',
                component: About,
            },
            {
                path: '/home',
                component: Home,
                children: [
                    {
                        // 此处不要再写 斜杠
                        path: 'news',
                        component: News
                    }
                ]
            }
        ]
    })
    
  2. 跳转(要写完整路径)

    <router-link to="/home/news">News</router-link>
    

路由传参(query参数)

  1. 传递参数

    <!-- 跳转并携带query参数,to的字符串写法 -->
    <router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link>
    <router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">跳转</router-link>
    
    <!-- 跳转并携带query参数,to的对象写法 -->
    <router-link :to="{
                      path: '/home/message/detail',
                      query:{
                      id: 666,
                      title: 你好
                      }
                      }">跳转</router-link>
    
  2. 接收参数

    $route.query.id
    $route.query.title
    

命名路由

  1. 作用:可以简化路由的跳转

    query参数是用&拼接的,params参数是直接出现在路由层级里

  2. 使用

    1. 给路由命名

      routes: [
          {
              name: 'guanyu',
              path: '/about',
              component: About,
          },
          {
              path: '/home',
              component: Home,
              children: [
                  {
                      name: 'xin',
                      // 此处不要再写 斜杠
                      path: 'news',
                      component: News
                  }
              ]
          }
      ]
      
    2. 简化跳转

      <!-- 简化前,需要写完整路径 -->
      <router-link to="/home/xin">跳转</router-link>
      <!-- 简化后,直接使用name属性跳转 -->
      <router-link :to="{name: 'xin'}">跳转</router-link>
      <router-link :to="{
                        	name: 'xin',
                        	query: {
                        		id: 666,
                        		title: '你好'
                        	}
                        }">跳转</router-link>
      

param参数

  1. 配置路由,声明接收params参数

    routes: [
        {
            name: 'guanyu',
            path: '/about',
            component: About,
        },
        {
            path: '/home',
            component: Home,
            children: [
                {
                    name: 'xin',
                    // 使用占位符声明接收params参数
                    path: 'news/:id/:title',
                    component: News
                }
            ]
        }
    ]
    
  2. 传递参数

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

    路由携带params参数时,若使用to的对象写法,必须使用name配置项,不能使用path配置项

  3. 接收参数

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

props配置

作用:让路由组件更方便的收到参数

 {
        path: '/home',
        component: Home,
       	// 第一种写法,props为对象,该对象中的所有key-value的组合最终都会通过props传给detail组件
        props: {a:900,b:'aa'},
            
        // 第二种写法,props为布尔值,为真时,路由收到的所有params参数通过props传给detail组件
        props: true,
           
        // 第三种写法,props为函数,该函数饭返回的对象中的所有key-value的组合最终都会通过props传给detail组件
        // 这里的route就是回调函数时默认传递的参数,就是$route
        props(route){
            return {
                id: route.query.id,
                title: route.params.title
            }
        }
}

replace属性

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

编程式路由导航

  1. 作用:不借助<router-link>来实现路由跳转,让路由跳转更加灵活

  2. 具体编码

    // $router的API
    this.$router.push({
        name: 'xin',
        params:{
            id: 4,
            title: 'aa'
        }
    })
    
    this.$router.replace({
        name: 'xin',
        params:{
            id: 4,
            title: 'aa'
        }
    })
    // 浏览器的后退功能
    this.$router.back()
    // 浏览器的前进功能
    this.$router.forward()
    // 浏览器的跳转,想跳几步跳几步
    this.$router.go(-3)
    

缓存路由组件

  1. 作用:让不展示的路由组件保持挂载,不被销毁

  2. 具体:

    <keep-alive include="组件名">
    	<router-link></router-link>
    </keep-alive>
    
    <keep-alive :include="['News', 'XXX']">
    	<router-link></router-link>
    </keep-alive>
    

keep-alive需要写在需要缓存的页面的父页面中

两个新的生命周期钩子

  1. 作用:路由组件所独有的钩子,用于捕获路由组件的激活状态
  2. 具体名字:
    1. activated:路由组件被激活时触发
    2. deactivated:路由组件失活时触发

路由守卫

  1. 作用:对路由进行权限控制
  2. 分类:全局守卫,独享守卫,组件内守卫

全局守卫

// 全局前置守卫:初始化时执行,每次路由切换前执行
router.beforeEach((to, from, next) => {
    // meta.isAuth是在路由配置的时候就写好的,路由的元数据
    if(to.meta.isAuth){
        // 权限控制的具体规则
        if(localStorage.getItem('school') === 'xxx'){
            // 放行
            next();
        }else{
            alert("school is wrong!");
        }
    }else{
        next();
    }
})

// 全局后置守卫:初始化时执行,每次路由切换后执行
router.afterEach((to, from, next) => {
    if(to.meta.title){
        // 修改网页的title
        document.title = to.meta.title;
    }else{
        document.title = "xxxxx";
    }
})

独享守卫

 {
        path: '/home',
        component: Home,
        beforeEnter(to, from, next){
    		......
		}	    
}

独享守卫就是写在某个路由配置项里,且只有前置守卫

组件内守卫

// 进入守卫:必须是通过路由规则进入该组件时调用
beforeRouteEnter(to, from, next){}
// 离开守卫:必须是通过路由规则离开该组件时调用
afterRouteEnter(to, from, next){}

路由器的两种工作模式

  1. 对于一个url来说,什么是hash值?——#及其后面的内容就是hash值
  2. hash值不会包含在http请求中,即:hash值不会带给服务器
  3. hash模式:
    • 地址中永远带着#号,不美观
    • 若以后将地址通过第三方手机app分享,若app检验严格,则地址会被标记为不合法
    • 兼容较好
  4. history模式:
    • 地址干净,美观
    • 兼容性和hash模式相比略差
    • 应用部署上线时需要后端人员支持,解决刷新页面404的问题
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值