今日学习记录之vue3/vue2
1.vue2/vue3不同点
1.异步组件:
  defineAsyncComponent
  例如:
    const asyncComponent = defineAsyncComponent(
      ()=> import('xxx.vue')
    )
2.自定义指令
  2.x语法
     bind: 指令绑定到元素后发生,只发生一次。
     inserted: 元素插入父DOM后发生。
     update: 当元素更新,但子元素尚未更新时,将调用此钩子。
     componentUpdated: 一旦组件和子级被更新,就会调用这个钩子。
     unbind: 一旦指令被移除,就会调用这个钩子,只调用一次。
     例如:
     Vue.directive('highlight',{
       bind(el, binding, vnode){
         el.style.background = binding.value
       }
     })
     <div v-highlight="'red'">字体颜色为红色</div>
  3.x语法
     const MyDirective = {
       beforeMount(el, binding, vnode, prevVnode){},
       mounted(){},
       beforeUpdate(){},
       updated(){},
       beforeUnmount(){},
       unmounted(){}
     }
     例如:
       const app = Vue.createApp()
       app.directive('highlight',{
         beforeMount(el, binding, vnode){
           el.style.background = binding.value
         }
       })
2.vue3.0性能提升体现在哪些方面?
a.编译阶段
b.源码体积
c.响应式系统
    vue2中采用defineProperty来劫持整个对象,然后进行深度遍历所有属性,给每个属性添加getter和setter,实现响应式。
    vue3采用proxy重写了响应式系统,因为proxy可以对整个对象进行监听,所以不需要深度遍历就可以监听动态属性的添加,
    数组的索引和数组的length属性以及监听删除属性。
   
    proxy的监听针对一个对象,即对这个对象的所有操作会进入监听操作。
    //定义一个响应式reactive函数
    function reactive(obj){
      if(typeof obj !== 'object' && obj != null){
         return obj
      }
      const observed = new Proxy(obj, {
         get(target, key, receiver) {
           const res = Reflect.get(target, key, receiver)
           return isObject(res) ? reactive(res) : res;
         }
      })
      return observed;
    }
    proxy直接可以劫持整个对象,并返回一个新对象。
3.vue2.0响应式原理
// 定义一个响应式函数 defineReactive
function update(){
app.content = obj.foo
}
function defineReactive(obj, key, val){
Object.defineProperty(obj, key, {
get(){
return val;
},
set(nv){
if(nv !== val){
val = nv;
update();
}
}
})
}
调用defineReactive,数据发生变化触发update方法,实现数据响应式。如果存在多个key,嵌套对象,则需要进行遍历,递归等操作。
Object.defineProperty 只能遍历对象属性进行劫持。
4.Treeshaking特性
Treeshaking 是一种通过清除多余代码方式来优化项目打包体积的技术。
即 在保持代码运行结果不变的前提下,去除无用的代码。
如何做:
  Treeshaking 基于 es6 模板语法(import与export),借助es6模块的静态编译思想,在编译时就确定模块的依赖关系,
  以及输入和输出的变量。
  编译阶段利用es6 module判断哪些模块已经加载和判断那些模块和变量未被使用或者引用,进而删除对应代码。
作用:
   减少程序体积,减少程序执行时间,便于对程序架构进行优化。
5.vue2中v-if和v-for使用时应注意什么?
注意点:
1.永远不要把v-if和v-for同时用在同一个元素上,带来性能方面的浪费(每次渲染都会先循环再进行条件判断)。
2.如果避免出现这种情况,则在外层嵌套template(页面渲染不生成dom节点),在这一层进行v-if判断,然后在内部进行v-for循环。
3.如果条件出现在循环内部,可通过计算属性computed提前过滤掉那些不需要显示的项。
源码:genElement函数中在进行if判断的适合, v-for比v-if先进行判断,得出结论:v-for优先级高于v-if。

6.SPA首屏加载速度慢如何解决?
//解决方案
减小入口文件体积:
常用的手段是路由懒加载,把不同路由对应的组件分割成不同的代码块,待路由被请求的时候会单独打包路由,
使得入口文件变小,加载速度大大增加。及在vue-router配置路由的时候,采用动态加载路由的形式。
静态资源本地缓存:
合理利用localStorage。
ui框架按需加载;
图片资源的压缩:
对于所有的图片资源,我们可以进行适当的压缩
对页面上使用到的icon,可以使用在线字体图标,或者雪碧图,将众多小图标合并到同一张图上,用以减轻http请求压力。
    组件重复打包:
       在webpack的config文件中,修改 CommonsChunkPlugin的配置。
       例如: 
         minChunks: 2 //表示会把使用2次以上的包抽离出来,放进公共依赖文件,避免重复加载组件。
    开启gzip压缩;
    使用SSR。
7.vue2中data属性是一个函数而不是对象原因?
1.根实例对象data可以是对象也可以是函数(根实例是单例),不会产生数据污染情况。
2.组件实例对象data必须为函数,目的是为了防止多个组件实例对象之间共用一个data,产生数据污染。
采用函数的形式,initData时会将其作为工厂函数都会返回全新data对象。
8.vue2中给对象添加新属性界面不刷新,如何解决?
例如:
  <div v-for="item in obj" :key="item">{{item}}</div>
  <button @click="add">点击新增属性</button>
  export default {
    data(){
      return {
        obj: {
          a: 'aa'
        }
      }
    },
    methods: {
      add(){
        this.obj.b = 'bb';
        console.log(this.obj);//这里能输出所有属性,但是页面没有显示刚刚新增的b属性。
      }
    }
  }
导致原因是:由于vue2采用的是Object.defineProperty实现数据响应式,obj的a属性被设成了响应式数据,而新增的b属性
没有通过Object.defineProperty设置成响应式数据。
解决方案:
   Vue.set(target, propertyName/index, value),
   Object.assign({}, this.obj,{b:1}...)//创建一个新对象,合并源对象和混入对象的属性。
   $forceUpdated():使vue实例重新渲染。
注意: vue3用proxy实现数据响应式,直接动态添加新属性仍可以实现数据响应式。
9.组件和插件区别
组件: 在vue中每一个.vue文件都可以视为一个组件。
插件:通常为vue添加全局功能。
例如:
添加全局方法或者属性;添加全局资源(指令/过滤器/过渡等);通过全局混入来添加一些组件选项;
添加vue实例方法,通过把他们添加到vue.prototype上实现。
组件格式:
<template></template>
<script>
export default {}
</script>
<style></style>
插件格式:【应该暴露一个install方法。这个方法的第一个参数是vue构造器,
第二个参数是一个可选的选项对象。】
TestPlugin.install = function(Vue,options){
//添加全局方法或property
Vue.globalMethod = function(){}
//添加全局资源
Vue.directive('my-directive',{
bind(el, binding, vnode, oldVnode){}
})
...
}
组件注册:
import ComponentTest from '../xx.vue';
Vue.component('component-name', ComponentTest)
//or
export default {
components: { ComponentTest }
}
插件注册:
Vue.use(插件名字)
10.组件通信
vue中8种常规的通信方案:
   通过props传递;
   通过$emit触发自定义事件
   使用ref;
   EventBus;
   $parent/$root;
   $attrs与listeners;
   provide与inject;
   vuex;
组件关系:
  1.父子组件
      props/$emit 或者使用ref
  2.兄弟组件
      eventbus  或者 $parent
  3.祖孙与后代组件
      attrs/$listeners 或者 provide/inject
  4非关系组件
      vuex
11.双向绑定
![]()
Vue中的双向绑定流程是:
new Vue()首先执行初始化,对data执行响应化处理,这个过程发生Observe中- 同时对模板执行编译,找到其中动态绑定的数据,从
data中获取并初始化视图,这个过程发生在Compile中 - 同时定义⼀个更新函数和
Watcher,将来对应数据变化时Watcher会调用更新函数 - 由于
data的某个key在⼀个视图中可能出现多次,所以每个key都需要⼀个管家Dep来管理多个Watcher - 将来data中数据⼀旦发生变化,会首先找到对应的
Dep,通知所有Watcher执行更新函数 
12.nextTick
定义: 在下次DOM更新循环结束之后执行延迟回调。【即:在修改数据之后立即使用这个方法,可以获取更新后的DOM结构】
例如:
  //修改数据
  this.msg = '修改了';
  //DOM还没有更新
  console.log(this.$refs.el.textContent);//原始的值
  this.$nextTick(()=>{
    //DOM更新了
    console.log(this.$refs.el.textContent);//修改后的值
  })
总结:把回调函数放入callbacks等待执行;将执行函数放到微任务或宏仁务中;
     事件循环到了微任务或者宏仁务,执行函数依次执行callbacks中的回调。
13.vue2中mixin作用
定义:mixin提供了一种灵活的方式,来分发vue组件中的可复用功能。
本质其实就是一个js对象,可以包含你组件中任意功能选项,例如data,components,methods,created,computed等。
混入分为: 全局混入和局部混入
局部混入:
   先定义一个mixin对象,里面包含data,methods, computed, created等属性。
   组件通过 mixins属性调用mixin对象即可。
全局混入:
   通过 Vue.mixin()进行全局混入。
   Vue.mixin({
      created(){},
   })
14.vue2中slot作用
slot分为: 默认插槽, 具名插槽, 作用域插槽。
v-slot属性只能在template上使用,但只有默认插槽时可以在组件标签上使用。
默认插槽名为 default,可以省略default直接写v-slot。
缩写为#时不能不写参数,写成#default。
可以通过解构获取v-slot={user},还可以重命名v-slot="{user:newName}"和定义默认值v-slot="{user="默认值"}"。
15.vue2中key作用
key的作用:key是给每一个vnode唯一id,也是diff的一种优化策略,可以根据key,更准确,更快的找到对应的vnode节点。 
16.vue2中keep-alive作用
keep-alive是vue的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM。
keep-alive包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。
keep-alive三个属性:
    include: 只有名称匹配的组件会被缓存。
    exclude: 任何名称匹配的组件都不会被缓存。
    max: 最多可以缓存多少组件实例。
例如:
   在路由配置中添加meta属性
   meta: { keepAlive: true, title:'xx' }
   <keep-alive>
     <router-view v-if="$route.meta.keepAlive"></router-view> 
   </keep-alive>
   <router-view v-if="!$route.meta.keepAlive"></router-view>
缓存后如何获取数据:
   1.beforeRouteEnter: 每次组件渲染时,都会执行beforeRoteEnter
   2.actived: 在keep-alive缓存的组件被激活的时候,都会执行actived钩子。
17.vue2中常用修饰符
lazy: 表单修饰符, v-model.lazy
trim:自动过滤用户输入的空格, v-model.trim
number: 自动将用户的输入值转为数值类型,v-model.number
事件修饰符:
  .stop 阻止事件冒泡
  .prevent  阻止事件的默认行为
  .self 将事件绑定在自身,相当于阻止事件冒泡。当在event.target 是当前元素自身时触发处理函数。
  .once  绑定事件以后只能触发一次。
  .capture  使事件触发从包含这个元素的顶层开始往下触发
  .passive 不想阻止事件的默认行为。
  .native  绑定原生事件。
18.vue2中自定义指令
指令分为全局注册和局部注册。
全局注册通过 Vue.directive 方法进行注册。
   Vue.directive('focus',{
     bind(){},
     inserted(el){
       el.focus(); 
     },
     update(){},
     componentUpdated(){},
     unbind(){},
   })
局部注册: 通过在组件options选项中设置 directive属性即可。
   directives: {
     focus: {
       inserted:function(el){
         el.focus()
       }
     }
   }
19.vue2中过滤器
vue允许自定义过滤器,用于一些常见的文本格式化。
注意:vue3已废弃filter。
全局过滤器:
Vue.filter('filter_name', function(value){
  //...
})
局部过滤器:
filters:{
  filterName:function(value){
    //...
  }
}
20.vue2中虚拟DOM?
createElement创建VNode的过程,每个VNode有children,children每个元素也是一个VNode,这样就形成了一个虚拟结构,用于
描述真实的DOM树结构。
即它是一层对真实DOM的抽象,以js对象作为基础的树,用对象的属性来描述节点,最终可以通过一系列操作使这棵树映射到真实环境上。
21.vue2中的diff算法
diff算法两个特点:
  1.比较只会在同层级进行,不会跨层级比较;
  2.在diff比较的过程中,循环从两边向中间比较。
当数据发生改变时,set方法会调用Dep.notify通知所有订阅者watcher,订阅者就会调用patch给真实的DOM打补丁,更新相应视图。
22.axios封装
封装axios时需要考虑哪些问题:
   1.设置即可请求前缀,分开发,测试,生成环境
   2.请求头携带token参数与超时时间等
   3.状态码,根据接口返回不同的status,执行不同的业务。
   4.请求方法
   5.请求拦截器
   6.响应拦截器
23.SSR
SSR解决方案,后端渲染出完整的首屏dom结构返回,前端拿到的内容包括首屏及完整spa结构,应用激活后依然按照spa方式运行。
    如果快乐太难,那祝你平安。
                    
                
                
            
        
浙公网安备 33010602011771号