Vue学习要点总结

198 阅读15分钟

Vue2

第一章 Vue使用

QQ截图20220326233544

脚手架文件结构介绍

├── node_modules 
├── public
   ├── favicon.ico: 页签图标
   └── index.html: 主页面
├── src
   ├── assets: 存放静态资源
      └── logo.png
   │── component: 存放组件
      └── HelloWorld.vue
   │── App.vue: 汇总所有组件
   │── main.js: 入口文件
├── .gitignore: git版本管制忽略的配置
├── babel.config.js: babel的配置文件
├── package.json: 应用包配置文件 
├── README.md: 应用描述文件
├── package-lock.json:包版本控制文件

Vue基础知识

Vue指令

v-text:用于操作纯文本 它会替代显示对应的数据对象的值即data里面的值 但是注意:此处为单项数据绑定,数据对象上的值改变了,插值会发生改变;但是改变插值并不会影响数据对象的值。v-text的简写就是{{}}

<div id="root">
        <div>你好,{{name}}</div>
        <div v-text="name">哈哈哈哈</div>//这里的哈哈哈会被替换为尚硅谷
        <div v-text="str"></div><!-- 把所有的字符串都当成正常的文本去解析 纵使里面有标签结构-->
        <div v-html="str"></div>
    </div>
</body><script type="text/javascript">
    Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。new Vue({
        el: '#root',
        data: {
            name: '尚硅谷',
            str: '<h3>你好啊!</h3>'
        }
    })
</script>

v-html:用于输出html,它与v-text的区别在于v-text是输出纯文本的内容,浏览器不会再对其进行html解析,但v-html会将其进行html解析

<div id="app">
    <p v-html="html"><p>//span标签会被解析 
</div>
​
let app = new Vue({
    el:"#app",
    data:{
        html:'<span style='color:red'>v-html</span>'
    }
})

v-if:如果为true,当前标签才会显示到页面上

<h2 v-if="false">欢迎来到{{name}}</h2>
<h2 v-if="1 === 1">欢迎来到{{name}}</h2> //这里的“1 === 1”为true

v-else:与v-if v-else-if 一起使用 当v-if v-else都为false时 它控制的标签才会显示

<div v-if="n === 1">Angular</div>
        <div v-else-if="n === 2">React</div>
        <div v-else-if="n === 3">Vue</div>
        <div v-else>哈哈</div>

v-show:通过控制css样式 display来显示和隐藏标签 适用于标签反复显示隐藏的情况

v-for:用于循环渲染,遍历数组和对象把它们的数据展示出来

v-on:绑定事件监听 一般可以简写为@

v-bind:单向数据绑定,数据只能从data流向页面 ,页面的数据改变了并不会影响data data的改变会影响页面的数据

v-bind:src 可以简写为 :src

<div id="root">
        <!-- 普通写法 -->
        <!-- 单向数据绑定:<input type="text" v-bind:value="name"><br/>
            双向数据绑定:<input type="text" v-model:value="name"><br/> --><!-- 上面可以简写为下面 -->
        单向数据绑定:<input type="text" :value="name"><br />
        双向数据绑定:<input type="text" v-model="name"><br /><!-- 如下代码是错误的,因为v-model只能应用在表单类元素(输入类元素)上 -->
        <!-- <h2 v-model:x="name">你好啊</h2> -->
    </div>

v-model:v-model可以实现数据的双向绑定 应用于input,textarea,select元素上创建数据的双向绑定 v-model:value="name" 可以简写成v-model="name" 因为v-model默认收集的就是value值

ref:为某个元素指定唯一的标识,vue对象通过$refs访问这个元素

// 子组件
<template>
  <div>
    我是子组件
  </div>
</template><script>
export default {
  data() {
    return {
      name: "myhua"
    };
  }
};
</script>
​
​
// 父组件
​
<template>
  <div id="app">
    <Son ref="myref"></Son>
  </div>
</template><script>
import Son from "./components/son";
export default {
  mounted() {
    console.log(this.$refs.myref.name); //输出子组件data中的name的值:myhua
  },
  components: {
    Son
  }
};
</script>

Vue基本使用

computed和watch

QQ截图20220327001200

两者有何区别?

computed(计算属性)是依赖已有的变量来计算的一个目标变量,大多数情况都是多个变量凑在一起计算出一个变量,并且computed具有缓存机制,依赖值不变的情况下其会直接读取缓存进行复用,computed不能进行异步操作

watch(监听属性)是监听某一个变量(现有的数据)的变化,并执行相对应的回调函数,通常是一个变量的变化决定多个变量的变化,watch可以进行异步操作

methods没有缓存 每次调用都要重新读取一次

export default{
    data(){
        return {
            name:'11',
            city:'22'
        }
    },
    methods:{
        getUserInfo(){
            return this.name + this.city
        }
    }
}

简单记就是 一般情况下 computed是多对一 watch是一对多

深度监听实现

const vm = new Vue({
        el: '#root',
        data: {
            isHot: true,
            numbers: {
                a: 1,
                b: 1,
                c: {
                    d: {
                        e: 100
                    }
                }
            }
        },  
        watch: {
        
            //监视多级结构中【某个】属性的变化
            /* 'numbers.a':{
                handler(){
                    console.log('a被改变了')
                }
            } */
            //监视多级结构中【所有】属性的变化
            numbers: {
                deep: true,//开启深度监听 如果不开启的话 当整个numbers改变时 才能监听到  开启后里面只要有一个变化就可以监听到
                handler() {
                    console.log('numbers改变了')
                }
            }
        }
    })

ref属性

  1. 被用来给元素或子组件注册引用信息(id的替代者)

  2. 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)

  3. 使用方式:

    1. 打标识:<h1 ref="xxx">.....</h1><School ref="xxx"></School>
    2. 获取:this.$refs.xxx

props配置项

Vue中的数据代理

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

<body>
	<!-- 
				1.Vue中的数据代理:
							通过vm对象来代理data对象中属性(name address)的操作(读/写) vm最终改的都是data中的name address
				2.Vue中数据代理的好处:
							更加方便的操作data中的数据
				3.基本原理:
							通过Object.defineProperty()把data对象中所有属性添加到vm上。
							为每一个添加到vm上的属性,都指定一个getter/setter。
							在getter/setter内部去操作(读/写)data中对应的属性。
		 -->

	<!-- 准备好一个容器-->
	<div id="root">
		<h2>学校名称:{{name}}</h2>
		<h2>学校地址:{{address}}</h2>
	</div>
</body>

<script type="text/javascript">
	Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。

	const vm = new Vue({
		el: '#root',
		data: {
			name: '尚硅谷',
			address: '宏福科技园'
		}
	})
	//在控制台利用vm.name = 'hhh'  修改name的值 这里是setter发挥了作用
	//利用 vm._data.name === vm.name  验证通过vm去修改name是否可以修改data.name
</script>

动态绑定class和style

使用动态属性

命名时驼峰命名法

<template>
  <div>
    <p :class="{ black: isBlack, yellow: isYellow }">使用 class(对象)</p>
    <p :class="[pink1, yellow]">使用 class (数组)</p>
    <p :style="styleData">使用 style</p>
  </div>
</template><script>
export default {
  data() {
    return {
      //对象
      isBlack: true,
      isYellow: true,
​
      //数组
      pink1: "pink",
      black: "black",
      yellow: "yellow",
​
      //内联样式style
      styleData: {
        fontSize: "40px", // 转换为驼峰式
        color: "red",
        backgroundColor: "#ccc", // 转换为驼峰式
      },
    };
  },
};
</script><style scoped>
.black {
  background-color: #999;
}
.yellow {
  color: yellow;
}
.pink {
  background-color: hotpink;
}
</style>

条件渲染

v-if v-else-if的用法,可使用变量,也可以使用 === 表达式

<h2 v-if="false">欢迎来到{{name}}</h2>
<h2 v-if="1 === 1">欢迎来到{{name}}</h2> //这里的“1 === 1”为true
<div v-if="n === 1">Angular</div>
        <div v-else-if="n === 2">React</div>
        <div v-else-if="n === 3">Vue</div>
        <div v-else>哈哈</div>//当v-if v-else-if都不执行的时候才执行这里

v-if 和 v-show 的区别?

v-if是通过控制dom元素的删除和生成来实现显隐的,每一次显隐都会使组件重新跑一遍生命周期,因为显隐决定了组件的生成和销毁

v-show是通过控制dom元素的css样式来实现显隐的,不会销毁

实现一次性切换或者切换不频繁的话用 v-if v-else-if

切换功能频繁的话我们就用v-show

v-if 和 v-show 的使用场景

实现一次性切换或者切换不频繁的话用 v-if v-else-if

切换功能频繁的话我们就用v-show

循环(列表)渲染

如何遍历数组和对象?

<template>
  <div>
    <p>遍历数组</p>
    <ul>
      <li v-for="(item, index) in listArr" :key="item.id">
        {{ index }} - {{ item.id }} - {{ item.title }}
      </li>
    </ul>
​
    <p>遍历对象</p>
    <ul>
      <li v-for="(val, key, index) in listObj" :key="key">
        {{ index }} - {{ key }} - {{ val.title }}
        <!-- 遍历对象时key就是下面的 a b c -->
      </li>
    </ul>
  </div>
</template><script>
export default {
  data() {
    return {
      flag: false,
      listArr: [
        { id: "a", title: "标题1" }, // 数据结构中,最好有 id ,方便使用 key
        { id: "b", title: "标题2" },
        { id: "c", title: "标题3" },
      ],
      listObj: {
        a: { title: "标题1" },
        b: { title: "标题2" },
        c: { title: "标题3" },
      },
    };
  },
};
</script>

key的重要性以及key为什么不能写成index或者random?(但是实际写了也不会报错)

key的主要作用是为了高效的更新虚拟DOM。key需要使用唯一的标识(如id,手机号等) ,而index,和random并不是对象的唯一的标识(因为index对应的只是一个固定的位置 当我们在数组中间插入一条数据时 其余后面的index都会发生改变 那么就会导致重新渲染)当我们进行逆序操作时就会产生没必要的DOM更新操作,会影响性能

<div v-for="(item,index) in list" :key='index'>{{item.name}}</div>
//没有插入时的数据
const list = [
    {
        id: 1 ,
        name:'1'
    },
     {
        id: 2 ,
        name:'2'
    },
     {
        id: 3 ,
        name:'3'
    },
]
//在中间插入一条数据
const list = [
      {
        id: 1 ,
        name:'1'
    },
     {
        id: 4 ,
        name:'4'
    },
     {
        id: 1 ,
        name:'1'
    },
     {
        id: 1 ,
        name:'1'
    },
]
​
//那么此时 假设key="index"
之前的数据                         之后的数据
​
key: 0  index: 0 name: 1     key: 0  index: 0 name: 1
key: 1  index: 1 name: 2     key: 1  index: 1 name: 我是插队的那条数据
key: 2  index: 2 name: 3     key: 2  index: 2 name: 2
                             key: 3  index: 3 name: 3
//之后的数据除了第一条的index没有改变之后 其余的index都改变了 故只插入一条数据的情况小要引起三条数据的渲染 增加了耗能

因为Vue是数据驱动视图,通过改变数据进而达到改变视图,加上key之后Vue的虚拟DOM的diff算法更容易定位到相应的元素,避免去遍历DOM造成性能的浪费

v-for和v-if不能一起使用!

在进行if判断的时候,v-for的优先级是比v-if高的,而且如果写在同一元素上会带来性能方面的浪费(每次渲染都会先循环再进行条件判断),因此为了避免出现这种情况,可以在外层嵌套template(页面渲染不生成dom节点),在这一层进行v-if判断,然后再内部进行v-for循环

在进行`if`判断的时候,`v-for`的优先级是比`v-if`高的,而且如果写在同一元素上会带来性能方面的浪费(每次渲染都会先循环再进行条件判断)
<ul>
    <li v-for="(item,index) in list" v-if="item.isActive" :key="item.id">
        {{ item.name }}
    </li>
</ul>

解决方案

1、如果是在循环外部可以在外层嵌套已成template(页面不生成dom节点)、在外层v-if判断、然后在进行使用v-for循环

<template v-if="isShow">
    <div v-for="(item,index) in list" v-if="item.isActive" :key="item.id">
        {{ item.name }}
    </div>
</template>

2、如果条件出现在循环内部、可以通过计算属性computed提前过滤掉那些不需要展示的数据

computed: {
    listData: function() {
        return this.list.filter(function (item) {
            return item.isShow
        })
    }
}

事件(event)

没有参数时默认传入的是event参数,有参数时显示在最后传入$event

<template>
  <div>
    <p>{{ num }}</p>
    <button @click="increment1">+1</button>
    <button @click="increment2(2, $event)">+2</button>
  </div>
</template><script>
export default {
  data() {
    return {
      num: 0,
    };
  },
  methods: {
    increment1(event) {
      // eslint-disable-next-line
      console.log("event1", event, "--", event.__proto__.constructor); // event是原生的 event 对象
      // eslint-disable-next-line
      console.log(event.target);
      // eslint-disable-next-line
      console.log(event.currentTarget); //事件是被注册到当前元素的,和 React 不一样
      this.num++;
​
      // 1. event 是原生的
      // 2. 事件被挂载到当前元素
      // 和 DOM 事件一样
    },
    increment2(val, event) {
      // eslint-disable-next-line
      console.log(event.target);
      this.num = this.num + val;
    },
    loadHandler() {
      // do some thing
    },
  },
  mounted() {
    window.addEventListener("load", this.loadHandler);
  },
  beforeDestroy() {
    //【注意】用 vue 绑定的事件,组建销毁时会自动被解绑
    // 自己绑定的事件,需要自己销毁!!!
    window.removeEventListener("load", this.loadHandler);
  },
};
</script>

事件修饰符,按键修饰符

QQ截图20220327095738

QQ截图20220327095819

表单

收集表单数据:

type="number"规定文本框只能输入数字型 v-model.number 则把收集到的数据进行转化为数字型 一般来说这两个同时使用

若:,则v-model收集的是value值,用户输入的就是value值。

若:,则v-model收集的是value值,且要给标签配置value值。

若:

1.没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值 true false)

2.配置input的value属性:

(1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)

(2)v-model的初始值是数组,那么收集的的就是value组成的数组

备注:v-model的三个修饰符:

lazy:失去焦点再收集数据

number:输入字符串转为有效的数字

trim:过滤输入内容的首尾空格

v-model

常见表单项 textarea checkbox radio select

修饰符 lazy number trim

总结

必须掌握

Vue组件间通信

props 父 => 子

props配置项

  1. 功能:让子组件组件接收父组件传过来的数据 父 => 子 即给子组件添加一个属性 子组件再接收就可以用该属性的数据

  2. 传递数据:<Demo name="xxx"/>

  3. 接收数据:

    1. 第一种方式(只接收):props:['name']

    2. 第二种方式(限制类型):props:{name:String}

    3. 第三种方式(限制类型、限制必要性、指定默认值):

      props:{
          name:{
          type:String, //类型
          required:true, //必要性
          default:'老王' //默认值
          }
      }
      

    备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。

$emit 子组件 ===> 父组件 用自定义事件实现

自定义事件

  1. 一种组件间通信的方式,可以适用于:子组件 ===> 父组件

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

  3. 绑定自定义事件:

    1. 第一种方式,在父组件中:<Demo @atguigu="test"/><Demo v-on:atguigu="test"/>

    2. 第二种方式,在父组件中:

      <Demo ref="demo"/>
      ......
      mounted(){
         this.$refs.xxx.$on('atguigu',this.test)
      }
      
    3. 若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法。

  4. 触发自定义事件:this.$emit('atguigu',数据)

  5. 解绑自定义事件this.$off('atguigu')

  6. 组件上也可以绑定原生DOM事件,需要使用native修饰符。

  7. 注意:通过this.$refs.xxx.$on('atguigu',回调)绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!

任意组件间通信

全局事件总线event

全局事件总线bus

  1. 一种组件间通信的方式,适用于任意组件间通信。组件太多的情况就不太好用了,多组件共享数据时应该使用Vuex

  2. 安装全局事件总线:

    new Vue({
        ......
        beforeCreate() {
            Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
        },
        ......
    }) 
    
  3. 使用事件总线:

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

      methods(){
        demo(data){......}
      }
      ......
      mounted() {
        this.$bus.$on('xxxx',this.demo)
      }
      
    2. 提供数据:this.$bus.$emit('xxxx',数据)

  4. 最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。不然会造成内存泄漏

vuex

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

截图20220402001805

何时使用?

多个组件需要共享数据时使用 :

  • 1.多个组件依赖于同一状态(例如都依赖下面的x)

QQ截图20220505131108

A组件更改了Vuex中的数据x之后 数据x的更改状态也同步到其他用到x数据的组件中

  • 2.来自不同组件的行为需要变更同一数据的状态

列如A组件中有一个按钮 点击后使x加一 B组件中鼠标划过一个图片就会使x加一 这就是不同的组件行为需要变更同一数据的状态

搭建过程

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 = {}
​
//创建并暴露store
export default new Vuex.Store({
    actions,
    mutations,
    state
})
​
​

2.在main.js中创建vm时传入store配置项

......
//引入store
import store from './store'
......
​
//创建vm
new Vue({
    el:'#app',
    render: h => h(App),
    store
})
基本使用

1.在store下面的index.js中 初始化数据 ,配置actions,mutations

//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//引用Vuex
Vue.use(Vuex)
​
//actions——用于响应组件中的动作(服务员)这里面是有业务逻辑的
const actions = {
    //响应组件中加的动作   context是上下文对象  用于触发commit里面的方法  value为外部传进来的参数
jiaOdd(context,value){
        console.log('actions中的jiaOdd被调用了')
     //if里面默认的是不等于0即true才会进入
        if (context.state.sum % 2) {
            // context.state.sum += value //这样写也可以实现加的操作但是开发者工具失效了(即开发者工具里面捕获不到操作步骤了) 开发中我们是需要开发者工具的介入的 因为开发者工具一直是在跟Mutations对话
            context.commit('JIA',value)
        }
    },
    jiaWait(context,value){
        console.log('actions中的jiaWait被调用了')
        setTimeout(()=>{
            context.commit('JIA',value)
        },500)
    }
}
​
const mutations = {
    //执行加
    JIAN(state,value){//state为store里面的index.js配置的state 用来给其他组件调用  这一步是真正改变state的步骤
        // console.log('mutations中的JIA被调用了',state,value)
        state.sum += value
    }
}
​
//初始化数据
const state = {
   sum:0
}
​
//创建并暴露store
export default new Vuex.Store({
    actions,
    mutations,
    state,
})

2.组件中读取Vuex中的数据:$store.state.sum

3.组件修改Vuex中的数据:$store.dispatch('actions中的方法名',需要传入的数据) 或者

$store.commit('mutations中的方法名',需要传入的数据) 备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写dispatch,直接编写commit

//实际使用vuex
<template>
  <div>
    <h1>当前求和为:{{ $store.state.sum }}</h1> //这里读取了vuex里面的数据
    <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() {
      //不需要通过服务员(actions)操作的话就直接commit去联系mutation
      this.$store.commit("JIA", this.n);//直接调用mutations里面的方法 把本组件的n传到mutations的value里面去  JIA(state,value)
    },
    decrement() {
      this.$store.commit("JIAN", this.n);
    },
    incrementOdd() {
      this.$store.dispatch("jiaOdd", this.n);//这里触发的是actions里面的操作 
    },
    incrementWait() {
      this.$store.dispatch("jiaWait", this.n);
    },
  },
  mounted() {
    console.log("Count", this);
  },
};
</script><style lang="css">
button {
  margin-left: 5px;
}
</style>
getters的使用

1.概念:当state里面的数据需要加工的时候再使用 可以用getters

2.使用方法:直接在store/index.js 里面追加getters配置

下面代码和上面代码的不同之处

25_src求和案例getters

const getters = {
    bigSum(state){
        return state.sum * 10
    }
}
​
//创建并暴露store
export default new Vuex.Store({
    ......
    getters
})
//实际使用vuex
<template>
  <div>
    <h1>当前求和为:{{ $store.state.sum }}</h1> //这里读取了vuex里面没有被加工的数据
    <h1>当前求和为:{{ $store.getters.bigSum }}</h1> //这里读取了state加工后的数据
    <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>
四个map方法的使用(方便代码编辑)

引入

import { mapState, mapGetters } from "vuex"; //帮助生成代码的函数
  1. mapState方法: 用于帮助我们映射state中的数据为计算属性

    computed: {
        //借助mapState生成计算属性:sum、school、subject(对象写法)
         ...mapState({sum:'sum',school:'school',subject:'subject'}),
             
        //借助mapState生成计算属性:sum、school、subject(数组写法)
        ...mapState(['sum','school','subject']),
    },
    
  2. mapGetters方法: 用于帮助我们映射getters中的数据为计算属性

    computed: {
        //借助mapGetters生成计算属性:bigSum(对象写法)
        ...mapGetters({bigSum:'bigSum'}),
    ​
        //借助mapGetters生成计算属性:bigSum(数组写法)
        ...mapGetters(['bigSum'])
    },
    
  3. mapActions方法: 用于帮助我们生成与actions对话的方法,即:包含$store.dispatch(xxx)的函数

    methods:{
        //靠mapActions生成:incrementOdd、incrementWait(对象形式)
        ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
    ​
        //靠mapActions生成:incrementOdd、incrementWait(数组形式)
        ...mapActions(['jiaOdd','jiaWait'])
    }
    
  4. mapMutations方法: 用于帮助我们生成与mutations对话的方法,即:包含$store.commit(xxx)的函数

    methods:{
        //靠mapActions生成:increment、decrement(对象形式)
        ...mapMutations({increment:'JIA',decrement:'JIAN'}),
        
        //靠mapMutations生成:JIA、JIAN(对象形式)
        ...mapMutations(['JIA','JIAN']),
    }
    

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

消息订阅与发布

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

  2. 使用步骤:

    1. 安装pubsub:npm i pubsub-js

    2. 引入: import pubsub from 'pubsub-js'

    3. 接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。

      methods(){
        demo(data){......}
      }
      ......
      mounted() {
        this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息 'xxx'为消息名字
      }
      
    4. 提供数据:pubsub.publish('xxx',数据)

    5. 最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid)去取消订阅。

组件生命周期(巨tm重要)

生命周期钩子是 从组件被创建添加到DOM再到销毁的整个过程中 各个阶段调用对应的函数

beforecreate 是当组件实例初始化之前被触发

  • 创建一个空白的Vue实例
  • data method尚未被初始化,不可使用

created 是组件实例初始化之后,但是被添加到DOM之前触发

  • Vue实例初始化完成,完成响应式绑定
  • data method都已经初始化完成,可以调用
  • 尚未开始渲染模板

beforeMount 会在元素已经准备好添加到DOM上,但还没有添加的时候触发

  • 编译模板,调用render生成vdom(此时的vdom只是js级别的)
  • 还没有开始渲染DOM

mounted 会在元素被创建后触发(但是并不能保证 以及被添加到了DOM上 但是可以用nextTick来拿到最新的DOM)

  • 完成DOM渲染
  • 组件创建完成
  • 开始由创建阶段进入运行阶段

beforeupdate 会在组件更新前调用

  • data发生变化之后
  • 准备更新DOM(尚未更新DOM)

updated 会在组件已经更新后触发

  • data发生变化之后,且DOM更新完成
  • 不要在updated中修改data,可能会导致死循环

beforeDestroy 会在组件即将被销毁前触发

  • 组件进入销毁阶段(尚未销毁,可正常使用)
  • 可移除,解绑一些全局事件,自定义事件

destroyed 会在组件已经被销毁后触发

  • 组件被销毁了
  • 所有的子组件也都被销毁了

keep-alive组件

  • onActivated 缓存组件被激活
  • onDeactivated 缓存组件被隐藏

vue2.x的生命周期lifecycle_2

vue3.0的生命周期lifecycle_2

生命周期的连环问?

Vue什么时候操作DOM比较合适

  • mounted和updated都不能保证子组件全部挂载完成
  • 使用$nextTick渲染操作DOM

Ajax应该在哪个生命周期

  • created和mounted里面都可以 理论上created快一点(但也只是毫秒级别的快)

Vue3 Composition API生命周期有何区别?

  • 使用setup代替了beforeCreate 和 created
  • 使用Hooks函数的形式,如mounted改为onMounted()

Vue高级特性(拉开差距的地方)

QQ截图20220327122755

vue自定义组件实现 v-model

  • 概念

    • v-model可以看成是value+input方法的语法糖。
    • 组件的v-model就是value+input方法的语法糖。
    • 可以绑定v-model的有:input,checkbox,select,textarea,radio
    • 下面是实现自定义 v-model

QQ截图20220327135549

QQ截图20220327135556

$nextTick refs (基本上都会考)

Vue是异步渲染

异步渲染:vue中的data改变之后,DOM不会立即渲染

$nextTick会在DOM渲染之后被触发,立刻获取最新的DOM节点

refs :可以拿到DOM元素

<template>
  <div id="app">
    <ul ref="ul1">
      <li v-for="(item, index) in list" :key="index">
        {{ item }}
      </li>
    </ul>
    <button @click="addItem">添加一项</button>
  </div>
</template><script>
export default {
  name: "app",
  data() {
    return {
      list: ["a", "b", "c"],
    };
  },
  methods: {
    addItem() {
      this.list.push(`${Date.now()}`);
      this.list.push(`${Date.now()}`);
      this.list.push(`${Date.now()}`);
​
      // // 获取 DOM 元素
      // const ulElem = this.$refs.ul1;
      // // eslint-disable-next-line
      // console.log(ulElem.childNodes.length);
​
      // 1. 异步渲染,$nextTick待DOM渲染完再回调 可以拿到更新后的DOM节点信息
      // 3. 页面渲染时会将 data 的修改做整合,多次 data 修改只会渲染一次
      this.$nextTick(() => {
        // 获取 DOM 元素
        const ulElem = this.$refs.ul1;
        // eslint-disable-next-line
        console.log(ulElem.childNodes.length);
      });
    },
  },
};
</script>

slot

匿名插槽

子组件中定义一个标签 父组件中可以给子组件传入任何内容替换子组件的内容

//父组件   父给子传    
<template>
  <div>
    <SlotDemo >//子组件
          {{ website.title }}//传给子组件的插槽里面
     </SlotDemo> 
   </div>
</template>
​
//子组件里面接收
 <template>
    <a :href="url">
        <slot>
            默认内容,即父组件没设置内容时,这里显示
        </slot>
    </a>
</template>

作用域插槽

作用域插槽: 可以理解为子组件中的作用域插槽通过子组件在slot上绑定属性值 然后父组件通过子组件绑定的属性值可以拿到子组件的数据 之后父组件可以用子组件里面的数据去渲染子组件的插槽 (插槽的作用域为子组件)

QQ截图20220327144138

具名插槽

具名插槽: 有具体名字的插槽,使用时指定替换模板中哪个插槽的内容,需要多个插槽时,可以利用<slot> 元素的一个特殊的特性:name来定义具名插槽

左边是NamedSlot子组件 右边是NamedSlot的父组件

QQ截图20220327143929

动态,异步组件

动态组件:

  • :is = "component-name" 用法
  • 用来动态的挂载不同的组件,相当于一个组件占位符,等待填充
  • <template>
      <div>
        <!-- 动态组件 -->
        <component :is="NextTickName" />
      </div>
    </template><script>
    // import CustomVModel from "./CustomVModel";
    import NextTick from "./NextTick";
    // import SlotDemo from "./SlotDemo";
    // import ScopedSlotDemo from "./ScopedSlotDemo";
    // import KeepAlive from './KeepAlive'
    // import MixinDemo from "./MixinDemo";export default {
      components: {
        // CustomVModel,
        NextTick,
        // SlotDemo,
        // ScopedSlotDemo,
        // FormDemo: () => import('../BaseUse/FormDemo'),//这里是利用import()函数实现组件的异步加载 即什么时候用什么时候再加载
        // KeepAlive
        // MixinDemo,
      },
      data() {
        return {
          name: "双越",
          website: {
            url: "http://imooc.com/",
            title: "imooc123",
            subTitle: "程序员的梦工厂",
          },
          // NextTickName: "NextTick",//组件名字在data中填充
          // NextTickName: "MixinDemo",
          NextTickName: "",//等待填充
          showFormDemo: false,
        };
      },
    };
    </script>
    

异步组件(会常考)--会提高性能

import()函数

按需加载,异步加载(什么时候用什么时候加载)大组件

不通过 import FromDemo from '../BaseUse/FormDemo' 的形式引入

QQ截图20220327150254

keep-alive

为什么需要?

  • 平常开发中某些组件不需要进行多次的初始化 因为某些组件我们需要持续保持它的状态 即下一次展示时继续保持之前的状态
  • 经常和Vue-router一起使用 keep-alive里面包裹一个router-view (所有路径匹配到的组件都会被缓存)

属性

  • keep-alive里面有三个属性 exclude include max

  • exclude 排除的组件(可以为字符串,数组,以及正则表达式,任何匹配的组件都不会被缓存)

    //组件name为c的组件不会被缓存
    <keep-alive exclude="c">
        <component>
    </keep-alive>
    
  • include 指定对应name的组件(可以为字符串,数组,以及正则表达式,任何匹配的组件都不会被缓存)

//组件name为a,b的组件才会被缓存
<keep-alive include="a,b">
    <component>
</keep-alive>
  • include 和 exclude同时使用时 优先exclude

  • max 缓存组件的最大值(类型为字符或者数字,可以控制缓存组件的个数)

    //如果缓存的组件数超过了max 那么就会删除第一个缓存的组件
    <keep-alive max="5">
        <component>
    </keep-alive>
    

缓存组件:不会执行生命周期中的卸载过程 当组件被缓存时会调用deactivated钩子 当组件被激活时就调用activated生命周期函数 这两个钩子是在加载后和更新前调用

频繁切换,不需要重复渲染

Vue常见性能优化方式之一

mixin

用于多个组件有相同的逻辑,把它们抽离出来

mixin并不是完美的解决方案,会有一些问题

Vue3提出的 Composition API 旨在解决这些问题

mixin问题:

变量来源不明确,不利于阅读

引入多个mixin时可能会造成命名冲突

mixin和组件可能会出现多对多的关系,复杂度较高

目前虽然存在问题 但是在开发中也经常用到

面试小技巧

QQ截图20220327153503

Vuex(面试中问的不难 具体见去哪儿笔记)

QQ截图20220327154011

Vuex基本概念:

state

getters

action

mutation

用于Vue组件:

dispatch

commit

mapState

mapGetters

mapActions

mapMutations

异步操作必须在Actions里面做

Vue-router使用

(面试中问的不难 具体见去哪儿笔记)

QQ截图20220327154800

路由模式

QQ截图20220327154809

路由配置

路由配置 动态路由

动态路由以冒号开头

QQ截图20220327155034

路由配置 懒加载

QQ截图20220327155023

vuex和vue-router面试地位

QQ截图20220327155216

接下来

QQ截图20220327161247

第二章

Vue原理(大厂必考)

面试为何会考察原理?

知其然知其所以然----各行各业都是

了解原理才能应用的更好--提高竞争优势

大厂造轮子(业务定制一个框架,技术API)

面试如何考察?以何种方式

考察重点,而不是考察细节。2/8原则

和使用相关联的原理,例如vdom,模板渲染

整体流程是否全面?热门技术是否有深度?

Vue与哪里包括哪些?

组件化和MVVM

组件化的历史

‘很久以前’就有组件化

asp jsp php 已经有组件化

node.js 中也有类似的组件化

数据驱动视图(框架核心)

(MVVM,setState)通过修改数据去改变视图,避免了dom操作

传统组件,只是静态渲染,更新还要依赖于操作DOM 所以jquery当时比较流行

数据驱动视图---Vue MVVM ;React setState

数据驱动视图:组件data等数据发生变化->生成新的vnode 触发diff算法->对比新旧vnode 再替换 生成新的DOM 这种方式让我们更加的能专注于业务的开发

jquery:是需要手动找到节点 并进行手动操作

MVVM

MVVM -Model(数据) View(视图) ViewModel(vue提供的一个能力 view和model之间的一个连接层),最核心的就是 ViewModelViewModel 包含 DOM ListenersData Bindings img

Data Bindings 用于将数据绑定到 View 上显示,DOM Listeners 用于监听操作。

  • ModelView 的映射,也就是 Data Bindings 。这样可以大量省略我们手动 update View 的代码和时间。
  • ViewModel 的事件监听,也就是 DOM Listeners 。这样我们的 Model 就会随着 View 触发事件而改变。数据的请求和视图的请求完全解耦(相互影响)。

什么是MVVM思想?

MVVM -Model View ViewModel,它包括 DOM Listenters 和 Data bindings,前者实现了页面与数据的绑定,当数据发生变化的时候会自动渲染页面。后者实现了数据与页面的绑定,当页面操作数据的时候 DOM 和 Model 也会发生相应的变化。

MVVM思想有两个方向。

  • 一是将模型转换成视图,即将后端传递的数据转换成看到的页面。

实现方式是:数据绑定

  • 二是将视图转换成模型,即将看到的页面转换成后端的数据。实现的方式是:DOM 事件监听

这两个方向都实现的,就称为数据的双向绑定

响应式(重点)

数据代理

1.Vue中的数据代理:

通过vm对象来代理data对象中属性(name address)的操作(读/写) vm最终改的都是data中的name address

2.Vue中数据代理的好处:

更加方便的操作data中的数据

3.基本原理:

通过Object.defineProperty()把data对象中所有属性添加到vm上。

为每一个添加到vm上的属性,都指定一个getter/setter。

在getter/setter内部去操作(读/写)data中对应的属性。

const vm = new Vue({
        el: '#root',
        data: {
            name: '尚硅谷',
            address: '宏福科技园'
        }
    })
    //在控制台利用vm.name = 'hhh'  修改name的值 这里是setter发挥了作用
    //利用 vm._data.name === vm.name  验证通过vm去修改name是否可以修改data.name

Vue监视数据的原理

\1. vue会监视data中所有层次的数据。

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

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

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

(2).如需给后添加的属性做响应式,请使用如下API:

Vue.set(target,propertyName/index,value) 或

vm.$set(target,propertyName/index,value)

data:{
                school:{
                    name:'尚硅谷',
                    address:'北京',
                },
                student:{
                    name:'tom',
                    age:{
                        rAge:40,
                        sAge:29,
                    },
                    hobby:['抽烟','喝酒','烫头'],
                    friends:[
                        {name:'jerry',age:35},
                        {name:'tony',age:36}
                    ]
                }
            },
    methods: {
                addSex(){
                    // Vue.set(this.student,'sex','男')//这样就有响应式了 可以监测sex这个属性以后的变化
                    this.$set(this.student,'sex','男')
                }
            }

\3. 如何监测数组中的数据?

法一:通过包装数组更新元素的方法(7个API)实现,本质就是做了两件事:

(1).调用原生对应的方法(7个API方法 此时的原生API是被Vue包装过的)对数组进行更新。

(2).重新解析模板,进而更新页面。

法二:通过Vue.set(target,propertyName/index,value) 或

vm.$set(target,propertyName/index,value)

this.$set(this.student.hobby, 1, '打台球')

\4.在Vue修改数组中的某个元素一定要用如下方法(因为这样才能具有响应式):

1.使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()这些API不是数组原生的API 这几个API是经过Vue底层加工的 修改后自带响应式

//之所以没有filter是因为filter原数组进行操作后 并不影响原数组 过滤的值要重新赋值给一个数组

2.Vue.set() 或 vm.$set()

特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性!!!

组件data的数据一旦变化,立刻触发视图的更新

实现数据驱动视图的第一步

是考察Vue原理的第一题

核心API-Object.definedProperty

Object.definedProperty的一些缺点

vue3用proxy 但是proxy有兼容性问题

QQ截图20220328102415

核心API—Object.definedProperty

如何监听(深度监听)对象变化?

 // 核心 API
    Object.defineProperty(target, key, {
        get() {
            return value
        },
        set(newValue) {
            if (newValue !== value) {
                // 设置新值的时候也要深度监听
                observer(newValue)
​
                // 设置新值
                // 注意,value 一直在闭包中,此处设置完之后,再 get 时也是会获取最新的值
                value = newValue
​
                // 触发更新视图
                updateView()
            }
        }
    })

监听对象,监听数组

复杂对象,深度监听

如何监听数组变化?

见代码

几个缺点

1.深度监听data数据中的属性,需要递归到底,一次性计算量大;

2.无法监听新增属性/删除属性(vue2中需要额外用Vue.set Vue.delete这两个API去监听新增属性和删除属性)

3.无法原生监听数组,需要特殊处理

虚拟DOM(vdom)和diff

虚拟DOM

核心是diff算法

框架的核心 用来实现组件的更新

用js对象模拟DOM节点数据

vdom真的很快吗?

  • vdom并不快,js直接操作DOM才是最快的
  • 但“数据驱动视图”要有合适的技术方案,不能全部DOM重建
  • 而vdom就是目前最合适的技术方案(并不是因为他快,而是因为他合适)

应用背景?

DOM操作非常耗费性能

以前用jQuery,可以自行控制DOM操作的时机,手动调整

Vue和React是数据驱动视图,如何有效控制DOM操作?

解决方案

QQ截图20220328133806

用js模拟DOM结构 html其实就是xml的一个特别版本

QQ截图20220328133952

通过snabbdom学习vdom

QQ截图20220328134646

Vue3.0重写了vdom的代码,优化了性能

但vdom的基本理念不变,面试考点也不变

React vdom具体实现和Vue也不同,但不妨碍我们统一学习

vdom总结

用js模拟DOM结构(vnode)

新旧vnode对比,得出最小更新范围,最后更新DOM

数据驱动视图的模式下,有效控制DOM操作

diff算法(面试中最深入最细节的部分)

介绍

diff算法是vdom中最核心,最关键的部分 但是并不是为了vdom而产生的

diff算法能在日常使用Vue React中体现出来

diff算法是前端热门话题,面试’宠儿‘

diff即对比,是一个广泛的概念,如linux中的diff 还有git中的diff命令 两个js对象也可以做diff

两棵树做diff,如这里的vdom diff ,一个vdom就可以看成是一颗树

diff算法时间复杂度O(n)的技术突破

QQ截图20220328141655

优化方法

QQ截图20220328141912

子节点通过key区分

左边是vnode通过patch转换到右边newvnode

QQ截图20220328142020

diff算法总结

patchVnode

addVnodes removeVnodes

updateChildren(体现了key的重要性)

Vue2和Vue3,React三者的diff算法区别

  • Vue2-双端比较
  • Vue3-最长递增子序列
  • React-仅右移

Vue React 为何循环时必须使用key?

  • vdom diff算法会根据key判断元素是否要删除?
  • 匹配了key,则只移动元素-性能较好
  • 未匹配key,则删除重建-性能较差

总结

QQ截图20220328143739

模板编译

介绍

模板是vue开发中最常用的部分,即与使用相关联的原理

它不是html,有指令,插值,js表达式(这些浏览器不能识别),所以到底是什么?

html只是标签语言,只有js才能实现判断,循环(图灵完备的语言)

面试不会直接问,但会通过’组件渲染和更新过程‘考察

js的with语法

QQ截图20220328145128

with语法不要乱用

QQ截图20220328145037

vue template complier将模板编译为render函数

执行render函数生成vnode(将模板变为vnode)vnode就可以实现页面更新渲染

编译模板为render函数

QQ截图20220328145411

QQ截图20220328151715

补充知识点:vue组件中使用render代替template

QQ截图20220328151859

QQ截图20220328152225

总结

QQ截图20220328152359

讲完模板编译再讲渲染和更新那么整个vue原理层面的知识点就串起来了 就形成了一个完整的流程

渲染更新过程

初次渲染过程

QQ截图20220328160338

执行render函数会触发getter

QQ截图20220328160443

更新过程

QQ截图20220328160648

注意这里触发setter重新执行render函数 是要之前已经被getter过才行

渲染和更新流程图

QQ截图20220328160711

  • 黄色框里面是初次渲染的时候已经生成render函数(生成的render函数是根据template中的模板解析得的 )的这一步
  • render函数执行生成vnode的时候 render函数会去Touch(触发) Data中的getter(模板里面没有用到的数据 render不会去Data中touch)
  • 触发Data中某个数据的getter的时候 这个触发的数据就会被收集为依赖 即被观察起来
  • 更新:当Data中的数据被修改的时候 我们回去Notify Watcher里面看是否被收集过依赖 如果之前是作为依赖被收集起来的那么就会重新触发生成新的render函数(图上的re-render) 然后执行render函数生成vnode 再渲染到页面上去

异步渲染

回顾$nextTick

汇总data的修改,一次性更新视图

减少DOM操作次数,提高性能

前端路由实现方式

前端路由原理

介绍

QQ截图20220328161632

SPA单页面应用 MPA多页面应用

回顾vue-router的路由模式

网页url组成

QQ截图20220328161919

hash

QQ截图20220328162227

hash完全是属于前端的事情

修改hash的方法

a. JS 修改 url

<body>
    <p>hash test</p>
    <button id="btn1">修改 hash</button>
​
    <script>
        // hash 变化,包括:
        // a. JS 修改 url
        // b. 手动修改 url 的 hash
        // c. 浏览器前进、后退
        window.onhashchange = (event) => {
            console.log('old url', event.oldURL)
            console.log('new url', event.newURL)
​
            console.log('hash:', location.hash)
        }
​
        // 页面初次加载,获取 hash
        document.addEventListener('DOMContentLoaded', () => {
            console.log('hash:', location.hash)
        })
​
        // JS 修改 url
        document.getElementById('btn1').addEventListener('click', () => {
            location.href = '#/user'
        })
    </script>
</body>

b. 手动修改 url 的 hash

c. 浏览器前进、后退

H5 history

是url规范的路由,跳转时不刷新页面

history.pushState(打开新路由,不刷新页面)

window.onpopstate(监听前进后退)

H5 history需要后端支持

两者如何选择

QQ截图20220328164927

to B的系统(管理系统)

总结

QQ截图20220328165227

\

Vue2面试真题

v-show和v-if的区别?

v-show是通过控制css样式来显示和隐藏DOM元素

v-if是通过真正的渲染和销毁DOM元素,而不是显示和隐藏

频繁切换用v-show,否则用v-if

为何在v-for中用key?

必须用key,且不能是index和random

diff算法中要通过tag和key来判断是否是sameNode(相同的节点),这样一来减少了渲染次数,提升渲染性能

描述Vue组件生命周期(父子组件)

考单组件生命周期图和父子组件生命周期关系

Vue组件如何通讯?(常见)

QQ截图20220328171059

vuex适用所有组件之间的通讯

描述组件渲染和更新过程?

QQ截图20220328160711

说到这个我们就要回顾一下vue原理的三大模块:

响应式(监听属性的变化 就是getter setter)

vdom

模板渲染(Component那个黄色的部分)执行render函数的时候会触发touch touch会去data里面触发getter getter会产生依赖然后到watch里面 ;如果我们触发data更改的时候会触发setter setter会通知notify去watch里面看之前是否收集过依赖 有的话就触发re-render重新渲染

双向数据绑定v-model的实现原理?

QQ截图20220329105746

对MVVM的理解?

computed有何特点?

有缓存机制,data不变不会重新计算

提高性能

为何组件data必须是一个函数?

每一个定义的组件都是一个class类 每一次使用组件的时候相当于是对类的一个实例化

如果data不是函数而是对象的话那么由于JavaScript的特性 所有的实例化组件都会共用一个data 实例化的组件彼此的数据修改就会影响其他的组件 就会造成全局污染;通过把data变成一个函数,每次创建一个新实例后,就会调用一次data,从而返回一个初始数据的全新副本数据对象。

ajax请求应该放在哪个生命周期?

应该放在mounted里面当DOM渲染完成之后在请求数据 因为js是单线程的,ajax是异步获取数据的 ;如果放在mounted之前也没有用 因为就算数据拿过来了js也没有渲染完 此时ajax拿到的数据还是在排队的过程中 只会让逻辑变得混乱

如何将组件所有props传递给子组件?

QQ截图20220329111059

如何自己实现v-model?

利用input事件实现的

QQ截图20220329111319

多个组件有相同逻辑时如何抽离?

何时要使用异步组件?

加载大组件

路由异步加载

何时需要使用keep-alive

缓存组件,不需要重复渲染

如多个静态tab页切换

优化性能

何时需要使用beforeDestory?

解绑自定义事件event.$off 不解绑的话会造成内存泄漏

清除定时器

解绑自定义的DOM事件,如window scroll等

什么是作用域插槽?

Vuex中的action和mutation有什么区别?

action中处理异步,mutation不可以

mutation做原子操作 即每次只能进行一步操作

action可以整合多个mutation

Vue-router常用的路由模式? 以及它们是怎样实现的?异步加载?

hash默认

H5 history(需要服务端支持

两者比较

QQ截图20220329112830

场景题:请用vnode描述一个DOM结构

左边DOM结构 右边vnode

QQ截图20220329113058

监听data变化的核心API是什么?

Object.defineProperty

以及深度监听和监听数组

有何缺点(VUe3的proxy可以弥补它的缺点 但是proxy也有兼容性问题)

Vue如何监听数组变化?

QQ截图20220329113433

请描述响应式原理?

说出下面这两点并且 结合起来说说就可以了

1.如何监听data的变化

2.组件渲染和更新的流程

diff算法的时间复杂度

在O(n的三次方)上做了调整 即通过对比targe的相同与否来直接替换

QQ截图20220329113734

Vue为何是异步渲染,$nextTick何用?

异步渲染(以及合并data修改),以提高渲染性能

$nextTick在DOM更新完之后,触发回调

Vue常见性能优化?

合理使用v-show和v-if

合理使用computed

v-for时加key,以及避免和v-if同时使用

因为v-for的优先级更高 每次v-for的时候v-if都要重新计算一遍 这是对性能的一种浪费

自定义事件,DOM事件及时销毁 (因为会阐述内存泄漏 会导致页面卡顿)

合理使用异步组件

合理使用keep-alive

data层级不要太深 (因为会导致响应式 在做监听的时候 递归次数会变多)

使用vue-loader在开发环境做模板编译

webpack层面的优化

前端通用的性能优化,如图片懒加载,防抖节流

使用SSR

vue3

第一章vue3新功能

createApp

QQ截图20220329223222

emits属性

QQ截图20220329223618

多事件处理

QQ截图20220329223723

Fragment

QQ截图20220329223729

移除.sync改为v-model参数

QQ截图20220329223820

异步组件的引用方式

QQ截图20220329223913

移除filter

QQ截图20220329223929

Teleport

点击之后弹窗放在body上

QQ截图20220329224208

Suspense

提高用户体验的一个API

QQ截图20220329224443

<template>
  <div class="app">
    <h3>我是App组件</h3>
    <Suspense>
      <template v-slot:default>
        <Child />
      </template>
      <template v-slot:fallback>
        <h3>稍等,加载中...</h3>
      </template>
    </Suspense>
  </div>
</template>

Composition API(区分能力的地方)

reactive

ref toRef toRefs

readonly

computed

watch watchEffect

setup()

生命周期钩子函数

第二章

原理

Proxy实现原理

编译优化

PatchFlag静态标记

hoistStatic静态提升

cacheHandler缓存事件

SSR优化

Tree-shaking优化

Vite-ES6 module

\

Vue3面试题

vue3比vue2有什么优势?

性能更好

体积更小

更好的ts支持

更好的代码组织

更好的逻辑抽离

更多新功能

**描述vue3的生命周期和vue2的生命周期有什么区别

Options API生命周期:

QQ截图20220329145844

Composition API

 //Composition API
  // setup()相当于beforeCreate 和 created
  setup() {
    console.log("setup");
​
    onBeforeMount(() => {
      console.log("onBeforeMount");
    });
    onMounted(() => {
      console.log("onMounted");
    });
    onBeforeUpdate(() => {
      console.log("onBeforeUpdate");
    });
    onUpdated(() => {
      console.log("onUpdated");
    });
    onBeforeUnmount(() => {
      console.log("onBeforeUnmount");
    });
    onUnmounted(() => {
      console.log("onUnmounted");
    });
  },

如何看待 Composition API和Options API?

Composition API带来了什么?(相对于大的项目来说)

更好的代码组织

更好的逻辑复用(有一道专门的面试题)

更好的类型推导


Options API的逻辑是零散分开的 Composition API逻辑是集中的

QQ截图20220329153320

如何选择?

不建议公用,会引起混乱

小型项目,业务逻辑简单,用Options API

中大型项目,业务逻辑复杂,用Composition API


别误解Composition API!

QQ截图20220329154730

ref toRef toRefs

如何理解ref toRef和toRefs?(重点)

ref

生成值类型的响应式数据

ref可以获取DOM节点 但是需要在加载之后

可用于模板和reactive

修改时:js中操作数据通过.value修改值

读取时:在模板中和reactive({})中读取ref数据: 不需要.value,

模板中:<div>{{xxx}}</div>

reactive({})中读取

reactive({})中读取 
const ageRef = ref(20); //值类型 响应式
    const nameRef = ref("双越"); //值类型 响应式
​
    const state = reactive({
      name: nameRef,
    });

toRef

针对一个响应式对象(reactive封装)的prop

创建一个ref,具有响应式

两者保持引用关系

  • 作用:创建一个 ref 对象,其value值指向另一个对象中的某个属性。
  • 语法:const name = toRef(person,'name')
  • 应用: 要将响应式对象中的某个属性单独提供给外部使用时。

toRefs

将响应式对象(reactive封装)转换为普通对象

对象的每个属性都是对应的ref

两者保持引用关系

扩展:toRefstoRef功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person)

ref toRef和toRefs的最佳使用方式?

用reactive做对象的响应式,用ref做值类型响应式

setup中返回toRefs(state),或者toRefs(state,’xxx‘)

ref的变量命名都用xxxRef

合成函数返回响应式对象时,使用toRefs

QQ截图20220329212425

进阶,深入了解

为什么需要ref?

返回值类型会丢失响应式

如在setup,computed,合成函数,都有可能返回值类型

Vue如果不定义ref,用户将自造ref,反而混乱

为何需要.value?

QQ截图20220329221236

为何需要toRef toRefs?

初衷:不丢失响应式的情况下,把对象数据 分解/扩散

前提:针对的是响应式对象(reactive封装的)非普通对象

注意:它们不创造响应式,而是延续响应式 使响应式数据用起来更加的方便 (创造响应式是reactive和ref的事情)

Vue3升级了哪些重要的功能?

见下面

Composition API如何实现代码逻辑的复用?

抽离逻辑代码到一个函数

函数命名约定为useXxx格式(React Hooks也是)

在setup中引用函数

尽量用ref toRefs去返回数据 不要直接用reactive

Vue3如何实现响应式?(proxy)

Proxy实现响应式

基本使用

QQ截图20220330004128

receiver是proxyData target是data key是属性 value是值

Reflect(不是很重要)

作用:

和Proxy能力一 一 对应的

规范化,标准化,函数化

替代Object上的工具函数

Object应该就是一个单纯的对象数据结构 它身上的工具函数太多了 应该交给别的来做即Reflect 把Object的能力慢慢弱化

QQ截图20220330010112

Proxy实现响应式

深度监听,性能更高 不是一次性递归完内部属性

可监听 新增/删除 属性

可监听原生数组变化

Proxy能规避Object.defineProperty的问题

Proxy无法兼容所有浏览器,无法polyfill

v-model参数的用法

watch和watchEffect的区别是什么?

两者都可以监听data属性变化

watch需要明确监听哪个属性

watch默认初始化的时候不执行

watchEffect默认初始化的时候执行一次(因为要收集监听的数据)

watchEffect会根据其中的属性,自动监听其变化

setup中如何获取组件实例?

在setup和其他的Composition API中没有this(考点)打印出来就是undefined

可通过getCurrentInstance获取当前实例 此时this就指向当前的实例

若使用Options API可照常使用this

Vue3为何比Vue2快?

Proxy响应式

PatchFlag

截图20220330141201

hoistStatic

截图20220330145435

cacheHandler

缓存事件

SSR优化

静态节点直接输出,绕过了vdom

动态节点,还是需要动态渲染

tree-shaking

模板编译时,根据不同的情况,引入(import)不同的API

Vite是什么?

vite是一个web开发构建工具,由于其原生ES模块导入方式,可以实现闪电般的冷服务器启动,通过在终端中运行以下命令,可以使用vite快速构建Vue项目

截图20220330150838

为何快?

开发环境使用ES6 Module,无需打包——非常快

生产环境下vite是使用rollup打包的,并不会快很多

Composition API和React Hooks的对比

\