Vue

Vue3

Vue3 与 Vue2区别

  • 引入了Composition API:更好的做逻辑划分。查看某个组件功能逻辑的时候,只需要关注这个函数即可。不像options API组合式,同一个功能分散在data,methods,props,computed等。
  • 响应式系统优化 ,使用Proxy 替代了Object.defineProperty:不仅能监听对象,还能监听数组。
  • 引入了Fragments:允许组件返回多个根节点
  • 更好的支持TS,vue3就是用ts写的
  • diff算法优化,加入了静态标记,标记的节点不会比对。
  • 编译优化静态提升 ,对不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用。
  • 对事件处理函数进行缓存,例如'click'等,下次调用的时候直接从缓存里读,减少了不必要的更新操作。
  • 对打包体积优化 ,去掉了不常用的API,如filter,引入了tree-shaking 前提是依赖于ESM,Commonjs是运行时,所以Commonjs下不能使用tree-shaking。还要看标记了副作用。
  • Vue3全局API名称发生了变化,同时新增了watchEffectHooks等功能

Object.definePropertyProxy 区别

  • Object.defineProperty 只能对对象的单个属性进行拦截和操作,需要深度遍历对象的每一个属性,给每个属性添加getter和setter。
  • proxy 可以监听数组。直接代理整个对象,而非对象属性,一层代理即可监听结构下所有属性变化,可直接新增/删除属性。

Vue3响应式原理

  • 使用Proxy代替Object.defineProperty实现数据劫持
  • 在编译阶段,执行render,触发get
  • effect代替了watcher
  • get中收集依赖,将负责渲染的effect存入deps,使用targetMapdepsMapdep来管理使用到的属性的依赖项
  • 当数据变动时触发set,依次执行该dep下所有effect,进行更新。

reactive 通过在 Proxy get 方法中收集依赖,在 set 方法中触发依赖。整个响应式数据存在变量 targetMap

Vite 与 基于Webpack的vue-cli区别

  1. 打包速度快/快速冷启动
    • vite :开发模式下,使用原生浏览器支持的ES Module方式加载模块,通过<script type='module'>加载模块,开发模式下不需要打包项目可直接运行 ,可快速冷启动
    • webpack:对整个项目进行扫描和分析,会打包整个项目,处理loader和plugin,如果项目比较大,速度会特别慢。
  2. 按需编译:只有当代码在加载的时候才会编译。vite 会开启一个开发服务器,会拦截浏览器发送的请求,浏览器会向服务器发送请求获取相应的模块, vite会对浏览器不识别的模块进行处理,如当import 后缀为.vue文件时,会在服务器上对.vue文件进行编译,把编译的结果返回给浏览器。
  3. 模块热更新
    • vite:浏览器重新请求该模块即可,它只会重新加载变化的模块,而不是整个页面。
    • webpack:模块以及模块依赖的模块需重新编译。
  4. 生产环境打包体积更小
    • vite:使用Rollup打包,Rollup直接使用原生浏览器的ESM进行打包,不需要使用babel把import转换成require,以及相应的转换函数。打包体积更小
  5. 构建原理
    • vite:是用浏览器原生支持的 ES Module(ESM)特性,以模块为单位进行开发。基于esbulid预构建依赖
    • webpack:是通过入口文件递归依赖构建。

Composition API

creatApp():用来创建Vue对象。

setup():是 Composition API 的入口。

  • 接两个参数 ,第一个参数是外部传入的参数props,并且props是响应式对象,不能被解构。第二个参数是 context,有三个成员 attrs emit slots。
  • 返回一个对象,用在templage,methods,computed,和生命周期钩子函数中。
  • 执行时机 :在解析props完毕,和创建组件实例之前执行的。所以在setup内部无法用this获取组件实例 。此时thisundefinedreactive():把一个对象转换成响应式对象,并且嵌套属性也都会变成响应式对象。返回的是一个proxy对象。

toRefs():需要传入一个proxy代理对象 。 内部会创建一个新的对象,然后遍历传入的所有属性,把所有属性都转化成响应式对象,带有.value属性,value属性具有getter/setter,最后挂载到新创建的对象上。

ref():把基本类型(如字符串、数字,布尔值等)转换成响应式对象带有.value属性 ,value属性具有getter/setter。也可以把引用数据类型(对象)转换为响应式,会调用reactive返回一个proxy代理对象。

ref() reactive()
✅支持基本数据类型+引用数据类型 ❌只支持对象和数组(引用数据类型)
❌在 <script><template> 使用方式不同(script中要.value) ✅在 <script><template> 中无差别使用
✅重新分配一个新对象不会失去响应 ❌重新分配一个新对象会丢失响应性
✅传入函数时,不会失去响应 ❌将对象传入函数时,失去响应
✅解构对象时会丢失响应性,需使用toRefs ❌解构时会丢失响应性,需使用toRefs
  • toRef(obj, key): 根据一个响应式对象中的一个属性,创建一个响应式的 ref,并且该 ref 和原对象中的属性保持同步。
  • toRefs(obj): 将一个响应式对象转换成一个普通对象,其中普通对象的每个属性都是响应式的 ref。
  • isRef(value): 判断某个值是否是 ref 对象。
  • unref(value): 用于解除响应式引用
  • shallowRef(value): 创建一个浅层的 ref,只有 value 属性是响应式的,深层的属性不具备响应式。
  • triggerRef(ref): 强制浅层的 ref 发生改变时触发响应式。
  • customRef(factory): 自定义 ref 对象,可以显式地追踪某个值的响应式变化。

Vue3中 HOOKS 的理解

hooks是函数式编程思想,用来封装通用逻辑。

和组件的区别:组件是有DOM或Template的,hooks只有函数。

和utils区别:util里封装的是通用方法,不包含状态。hooks里是包含状态,比如响应式状态。

Vue2

Vue2双向数据绑定原理(响应式原理)

采用数据劫持 结合发布者-订阅者模式 的方式,data数据在初始化的时候,会实例化一个Observe类,在它会将data数据进行递归遍历,并通过Object.defineProperty方法,给每个值添加上一个getter和一个setter。在数据读取的时候会触发getter进行依赖(Watcher)收集,当数据改变时,会触发setter,对刚刚收集的依赖进行触发,并且更新watcher通知视图进行渲染。

通过数据劫持+发布订阅模式实现的。通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

observer劫持监听属性变化->watcher通知变化,更新视图

compile解析指令,订阅数据变化,绑定更新函数->watcher通知变化,更新视图

当数据更新时,通知watcher更新组件

页面首次加载的时候会调用Compiler解析指令/解析差值表达式,会调用其中的方法,更新视图。

首次加载是Compiler更新视图。

数据变化都是通过watcher更新视图。

订阅数据变化:创建watcher对象,订阅数据变化,当数据变化时Dep会通知watcher。

绑定更新函数:当创建watcher对象的时候,会传入一个回调函数,回调函数中更新视图。

Vuex原理

  • vuex是响应式的全局状态管理工具,状态会保存在state内,一个状态改变,全局都会跟着变化。
  • getter是从state的派发状态,相当于state的计算属性
  • 改变state状态的唯一途径是提交(commit)mutation,且mutation是同步操作。
  • action像一个装饰器,可以包裹提交mutation,可以提交多个,action可以包含任意异步操作。
  • mocules模块化vuex,是store分割的模块,每个模块拥有自己的state、getters、mutations、actions。

为什么 Vuex 的 mutation 中不能做异步操作?

Vuex 中所有的状态更新的唯一途径都是mutation,异步操作通过Action 来提交 mutation 实现,这样可以方便地跟踪每一个状态的变化。如果 mutation 支持异步操作,就没有办法知道状态是何时更新的,无法很好的进行状态的追踪,给调试带来困难。

页面刷新后Vuex 状态丢失怎么解决?

Vuex 只是在内存中保存状态,刷新后就会丢失,如果要持久化就需要保存起来。

localStorage就很合适,提交mutation的时候同时存入localStorage,在store中把值取出来作为state的初始值即可。

也可以使用第三方插件,推荐使用vuex-persist插件,它是为 Vuex 持久化储存而生的一个插件,不需要你手动存取storage,而是直接将状态保存至 cookie 或者 localStorage中。

Pinia 和 Vuex

VuexStateGettesMutations(同步)、Actions(异步)
PiniaStateGettesActions(同步异步都支持)

  • Pinia 没有 Mutations

  • Actions 支持同步和异步

  • 没有模块的嵌套结构:Pinia 通过设计提供扁平结构 ,就是说每个 store 都是互相独立的,谁也不属于谁,也就是扁平化了,更好的代码分割且没有命名空间。当然你也可以通过在一个模块中导入另一个模块来隐式嵌套 store,甚至可以拥有 store 的循环依赖关系

  • 更好的 TypeScript 支持:不需要再创建自定义的复杂包装器来支持 TypeScript 所有内容都类型化,并且 API 的设计方式也尽可能的使用 TS 类型推断

  • 不需要注入、导入函数、调用它们,享受自动补全,让我们开发更加方便

  • 无需手动添加 store,它的模块默认情况下创建就自动注册的

  • Vue2 和 Vue3 都支持:除了初始化安装和SSR配置之外,两者使用上的API都是相同的

  • 支持 Vue DevTools

    • 跟踪 actions, mutations 的时间线
    • 在使用了模块的组件中就可以观察到模块本身
    • 支持 time-travel 更容易调试
    • 在 Vue2 中 Pinia 会使用 Vuex 的所有接口,所以它俩不能一起使用
    • 但是针对 Vue3 的调试工具支持还不够完美,比如还没有 time-travel 功能
  • 模块热更新

    • 无需重新加载页面就可以修改模块
    • 热更新的时候会保持任何现有状态
  • 支持使用插件扩展 Pinia 功能

  • 支持服务端渲染

vue 和 react区别

  • Vue API 多,React API
  • Vue 双向绑定,修改数据自动更新视图,而 React 单向数据流,需要手动 setState
  • Vue template 结构表现分离,Reactjsx 结构表现融合,html/css都可以写到js里
  • 都可以通过 props 进行父子组件数据传递,只是 Vue props 要声明,React 不用声明可能直接使用
  • Vue 可以用插槽,React 是万物皆可 props
  • Vue2 利用基本都是 MixinReact 可以用高阶函数、自定义 hook 实现
  • VuefragmenthookVue3 才有,Vue 还有丰富的指令

vue 和 react 如何选型?

  • 看公司基建,公司的基建能更好的支持哪个框架
  • 团队技术储备
  • 历史代码
  • 效率和产出是最高的

VuexRedux异同

相同:

  • state 共享数据,单一状态树的概念
  • 流程一致:定义全局state,触发,修改state
  • 原理相似,通过全局注入store。

不同:

  • vuex定义了state、getter、mutation、action四个对象;redux定义了store、reducer、action
  • vuex触发方式有两种commit同步和dispatch异步;redux同步和异步都使用dispatch
  • vuexstate统一存放,方便理解;reduxstate依赖所有reducer初始值
  • vuexgetter,目的是快捷得到state;redux没有这层,react-redux mapStateToProps参数做了这个工作。
  • vuexmutation只是单纯赋值(很浅的一层);reduxreducer只是单纯设置新state(很浅的一层)。他俩作用类似,但书写方式不同
  • Redux 使用的是不可变数据 ,而Vuex数据是可变的 。Redux每次都是用新的state替换旧的state ,而Vuex是直接修改
  • Redux 在检测数据变化的时候,是通过 diff 的方式比较差异的,而Vuex其实和Vue的原理一样,是通过 getter/setter来比较的

虚拟DOM

虚拟DOM是表示真实DOM的JS对象。包含 TagName标签名、props标签属性 和Children子标签名,子属性,文本节点。

diff算法原理:更高效地更新视图

Vue2同层比较新老 vnode ,新的不存在老的存在就删除,新的存在老的不存在就创建,子节点采用双指针头对尾两端对比的方式,全量diff,然后移动节点时通过 splice 进行数组操作

只比较同级节点。采用首尾指针法。新旧虚拟DOM都有首尾两个元素。

按下图1234的顺序依次进行比较,比较成功的两个节点的指针向中间靠拢移动,当新旧节点中有一个首指针跑到尾指针后时,结束比较。更新真实DOM。

视频链接www.bilibili.com/video/BV1JR...

Vue3中加入了静态标记。被标记的节点不会比较,标记值为-1。

采用 Map 数据结构以及动静结合的方式,在编译阶段提前标记静态节点,Diff 过程中直接跳过有静态标记的节点,并且子节点对比会使用一个 source 数组来记录节点位置及最长递增子序列算法优化了对比流程,快速 Diff,需要处理的边际条件会更少

vue-router模式和原理

  • hash :URL 中的路由信息以 # 符号开始,后面跟随着路由路径。浏览器不会将哈希值的变化发送到服务器 ,因此不会触发页面的重新加载 。因为只将 hash 前面的部分当作地址 ,服务端仍然会返回 index.html。 原理:Vue Router 会监听浏览器 hashchange 事件。当哈希值发生变化时,会在浏览器的访问历史 中增加一个记录。Vue Router 根据新的哈希值切换到对应的组件 ,更新视图。哈希值的变化不会使浏览器向服务器发送请求 ,因此切换路由时只是在客户端内部进行的操作。Hash 模式适用于不依赖于服务器的情况

  • history :URL上没有#。更美观。地址栏中的地址全部看作请求地址

    原理:Vue Router 会调用浏览器的 pushStatereplaceState 方法,将新的路由信息添加或替换到浏览器历史记录 中。历史记录发生变化时被触发,Vue Router 监听 popstate 事件,并根据新的路由信息切换到对应的组件,更新视图。切换路由时浏览器会向服务器发送请求

$nextTick原理

$nextTick是vue异步操作工具,能在DOM更新后执行回调函数。本质是js的事件循环机制

vue更新DOM是异步的 。当有数据变化时,会将变化塞到任务队列中 ,在组件更新时,Vue 会将这个队列中的变化应用到虚拟 DOM 中。$nextTick回调函数会被添加到一个微任务队列中。任务队列执行后会检查微任务队列是否有任务,如果有则执行。

computed和watch有什么不同?

  • computed :计算结果并返回,只有当被计算的属性发生改变时才会触发(即:计算属性的结果会被缓存 ,除非依赖的响应属性变化才会重新计算

    原理:基于vue的响应式原理。响应式数据发生改变时,会通知存储computed的Dep对象,标记该computed为dirty 。当下次访问该computed的属性值时 ,computed会检查 依赖的响应式数据是否发生了变更 ,如果没有直接返回已缓存的属性值 ;如果依赖的数据发生了变更,则重新计算 。并将计算后的结果缓存起来 。此时,该computed会将dirty标记重置为false,等待下次依赖项发生变化时再次重新计算。

  • watch:监听某一个值,当被监听的值发生变化时,执行相关操作。

    原理:对watch每个属性创建一个watcher ,watcher在初始化时会将监听的目标值缓存到watcher.value中,因此触发 data[key]的get方法,被对应的dep进行依赖收集;当data[key]发生变动时触发set方法,执行dep.notify方法,通知所有收集的依赖watcher,触发收集的watch watcher,执行watcher.cb,也就是watch中的监听函数

vue生命周期中异步加载在mouted还是create里实现

最常用的是在 created 钩子函数中调用异步请求 ,此时data已经挂载到vue实例了(有数据了,但是还没有真实的DOM)

有两个优点:

1、能更快获取到服务端数据,减少页面 loading 时间;

2、放在 created 中有助于一致性,因为ssr 不支持 beforeMount 、mounted 钩子函数。

Vue2 为什么要用 vm.$set() 解决对象新增属性不能响应的问题 ?

  1. Vue使用了Object.defineProperty实现双向数据绑定
  2. 在初始化实例时对属性执行 getter/setter 转化
  3. 属性必须在data对象上存在才能让Vue将它转换为响应式的 (这也就造成了Vue无法检测到对象属性的添加或删除)无法检测实例被创建时不存在于 data 中的 属性。

所以Vue提供了Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value)

vm.$set 的实现原理是:

  1. 如果目标是数组 ,直接使用数组的 splice 方法触发响应式
  2. 如果目标是对象 ,会先判读属性是否存在、对象是否是响应式
  3. 最终如果要对属性进行响应式处理,则是通过调用 defineReactive 方法进行响应式处理

deleteVue.delete

delete:会删除数组的值,但是它依然会在内存中占位置 Vue.delete:直接删除数组,改变数组的键值,删除数组在内存中的占位

js 复制代码
let arr1 = [1,2,3]
let arr2 = [1,2,3]
delete arr1[1]
this.$delete(arr2,2)
console.log(arr1)    // [1, empty, 3]
console.log(arr2)    // [1,2]

生命周期

  • beforeCreate(创建前):data属性还没有赋值,也没有DOM。
  • created(创建后):data属性有值了,但是DOM还没有生成,$el属性还不存在。
  • beforeMount(挂载前):this.$el有值,但是数据还没有挂载到页面上。
  • mounted(挂载后):模板编译完成,数据挂载完毕。
  • beforeUpdate(更新前):只有数据更新后,才能调用(触发)beforeUpdate。
  • updated(更新后):组件更新之后执行的函数。
  • activated(组件激活):keep-alive组件激活时调用。
  • deactivated(组件停用):keep-alive组件停用时调用。
  • beforeDestroy(销毁前):vue(组件)对象销毁之前。
  • destroyed(销毁后):vue组件销毁后。

Vue 父子组件生命周期执行顺序

  • 加载渲染阶段:父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted
  • 更新阶段:父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
  • 销毁阶段:父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed

Vue.js基础结构

创建Vue实例,传入eldata选项,会把data中的数据填充到el指向的模板中,并把模版渲染到浏览器 。

第二种使用vue-cli创建使用 render$mount

h函数:用来创建虚拟DOM

render函数:把虚拟DOM返回

$mount:虚拟DOM转成真实DOM,渲染到浏览器

Vue.use()

用来注册插件,并调用传入插件的install方法。

$route$router

  • $route:是一个Object对象,里面存放路由规则、路径。

  • $router:是一个VueRouter实例,实例里提供跟路由相关的方法,和路由模式mode等,currentRoute当前路由规则。

发布订阅模式

vue中的自定义事件 eventbus 和 node的事件机制都是发布订阅模式。

js 复制代码
// 构造函数是一个对象,同一事件可以定义多个注册方法。
// { 'click': [fn1, fn2], 'change': [fn] }
class EventBus {
    constructor() {
        this.eventObj = Object.create(null); // 定义一个无原型的空对象
    }
    // 订阅事件,类似监听事件$on('key',()=>{})  所以两个参数分别是  事件名 和 回调函数
    $on(eventName, callback) {
        // 如果同一事件已经有方法了,就获取出来,否则为空数组
        this.eventObj[eventName] = this.eventObj[eventName] || [];
        // 将订阅方法 push 进该事件中
        this.eventObj[eventName].push(callback);
    }
    // 发布事件,类似于触发事件$emit('key', params)  参数是事件名 和 参数
    $emit(eventName, ...args) {
        // 如果找到该注册的事件,则遍历事件中的方法  并 执行
        if(this.eventObj[eventName]) {
            const eventList = this.eventObj[eventName];
            for(let callback of eventList) {
                callback(...args);
            }
        }
    }
}

// 测试验证
const bus = new EventBus();
bus.$on('myEvent111',(name, age)=>{
    console.log('myEvent111  第一个', name, age);
})
bus.$on('myEvent111',(name, age)=>{
    console.log('myEvent111  第二个', name, age);
})
bus.$on('myEvent222',(age)=>{
    console.log('myEvent222', age);
})

bus.$emit('myEvent111', '张三', 18);
bus.$emit('myEvent222', 18);

观察者模式

观察者与发布订阅的区别是没有事件中心

  • 观察者(订阅者 )- Watcher
    • update():当事件发生变化的时候,具体要做的事。
  • 目标(发布者)- Dep (依赖)
    • sub数组:用来存储所有的 Watcher
    • addSub():用来添加 Watcher
    • notify():当事件发生时,调用所有观察者Watcherupdate方法。
  • 没有事件中心

当事件发生变化的时候,目标(发布者)Dep会调用所有观察者Watcher里的update方法,用来更新视图

js 复制代码
// 目标(依赖)- 发布者
class Dep {
    constructor() {
        this.subs = []; // 记录所有 watcher
    }

    // 添加 watcher
    addSub(sub) {
        if(sub && sub.update){ // 有 update 方法的才是 watcher
            this.subs.push(sub);
        }
    }

    // 发布通知
    notify() {
        for(let sub of this.subs) { // 遍历调用所有 watcher 的 update
            sub.update();
        }
    }
}

// 观察者 - 订阅者
class Watcher {
    update() {
        console.log('观察到了,哈哈哈')
    }
}

// 测试验证
let dep = new Dep();
let watcher = new Watcher();

dep.addSub(watcher);
dep.notify();

发布订阅模式 与 观察者模式 区别

  • 观察者模式:是由具体目标调度。比如事件触发,Dep会调用watcher中的方法,所以观察者模式中的发布者和订阅者之间是存在依赖的。
  • 发布订阅模式:由统一的事件中心调度。因此发布者与订阅者不需要知道对方的存在。
相关推荐
喵叔哟16 分钟前
重构代码之取消临时字段
java·前端·重构
还是大剑师兰特1 小时前
D3的竞品有哪些,D3的优势,D3和echarts的对比
前端·javascript·echarts
王解1 小时前
【深度解析】CSS工程化全攻略(1)
前端·css
一只小白菜~1 小时前
web浏览器环境下使用window.open()打开PDF文件不是预览,而是下载文件?
前端·javascript·pdf·windowopen预览pdf
方才coding1 小时前
1小时构建Vue3知识体系之vue的生命周期函数
前端·javascript·vue.js
阿征学IT1 小时前
vue过滤器初步使用
前端·javascript·vue.js
王哲晓1 小时前
第四十五章 Vue之Vuex模块化创建(module)
前端·javascript·vue.js
丶21361 小时前
【WEB】深入理解 CORS(跨域资源共享):原理、配置与常见问题
前端·架构·web
发现你走远了1 小时前
『VUE』25. 组件事件与v-model(详细图文注释)
前端·javascript·vue.js
Mr.咕咕1 小时前
Django 搭建数据管理web——商品管理
前端·python·django