小满vue3笔记(含源码解读)

第一章

1.mvvm架构

 

2.回顾vue2对比vue3

 

区别:

vue2选项式api

vue3组合式api

关于这两个的区别,你可以不准确的理解为,选项式api更贴近原生标准html文件结构;

而组合式api就像在html标签中写css;当然做了优化,没准升华了

就这么个东西

3.vue3新特性

 

重写双向绑定

vue2和vue3都是用的js原生方法;

vue3重写了双向绑定,改为用ES6的新特性(好像是)proxy

 

vue2部分源码

 

 

 

vue3部分源码

由于vue2的劫持和特别是重写数组;对于数组(好像是劫持数组)不太友好(具体可能是劫持不到);所以用proxy重写了

 

vue3 tree shaking

 

vue3 composition api

setup语法糖

不懂😎

第二章

nvm 与node

nvm : node的版本管理工具,坑:空,中文路径,node版本重新添加或者卸载重安

nodejs部分架构

 

 

nodejs的源码好像包含些c

 

处理最总要的好像是io流,毕竟是对并发处理好的机制(优点)

 

 

第三章

关于vite项目的目录

待补充

vscode中vue2和vue3插件

智能提示书写语法会冲突,2,3要禁用一个

npm run dev 是如何执行的

在vite中查看packag.json

会发现软连接bin制定到...

之后这个文件做了linux系统,window系统的一些相关配置(🤔。。。)

第四章vue3模版语法

vue3书写风格

 

支持模版语法

api调用

 

三元表达

 

vue指令用法

v-html模板(也许是自定义模板???🤔)

。。。

v-if

关于v-if是如何在false下消失的,打开浏览器开发者模式查看,发现被注释掉了

为什么说v-show比v-if性能高

v-show在被设置为false后,是添加了display:none

看起来切换css要比注释性能高(look🤨)

@click点击

 

冒泡😅

v-bind绑定

简写:":"

 

常用动态绑定css,style,class,id....

v-model绑定

一般绑定的表单元素;

ref属性觉定v-model是否是响应式

v-for遍历数组

.....

 

v-once;v-memo

v-once,只渲染一次

 

 

v-memo如果跟空数组,效果和v-once是一致的

v-memo节省一小部分性能;大概是一万条数据,5s左右;不执行则跳过

 

第五章虚拟DOM和diff算法

介绍虚拟DOM

AST语法数:这个东西在ES6转ES5的插件babel;ts转js中会进行ast转换

根据满哥说的,我的理解是vue3在用js去描述DOM对象;这个描述和操作不一样(js操作dom)

 

正在上传…重新上传取消

为什么不直接操作dom:

原因:dom的属性太多,影响性能

 

我看是说这一次描述dom,会全部描述进去,影响性能

 

最长递增子序列

 

不清楚,刷算法再说喽

 

第六章ref全家桶

全家...

内置的特殊 Attributes | Vue.js

 

手写防抖

 

在 Vue 中,我们可以很轻松的实现防抖和节流。防抖指令 v-debounce:

html
<input @input="doSomething" v-debounce="500">

该指令会在用户输入完后 500ms 后再执行 doSomething 方法。我们也可以手工实现一个防抖的 debounce 方法:

js
export default {
  methods: {
    debounce(func, wait) {
      let timeout
      return function() {
        let context = this
        let args = arguments
        clearTimeout(timeout)
        timeout = setTimeout(() => {
          func.apply(context, args)
        }, wait)
      }
    }
  }
}

然后在组件中这样使用:

html
<input @input="debounce(doSomething, 500)">

节流指令 v-throttle:

html
<input @scroll="doSomething" v-throttle="500">

该指令会在用户连续滚动 500ms 内只执行 doSomething 方法一次。我们也可以手工实现 throttle 方法:

js
export default {
  methods: {
    throttle(func, wait) {
      let timeout, context, args  
      return function() {
        context = this
        args = arguments   
        if (!timeout) {    
          timeout = setTimeout(() => {
            timeout = null
            func.apply(context, args)
          }, wait)
        }
      }
    }
  }
}

然后在组件中这样使用:

html 
<input @scroll="throttle(doSomething, 500)">

所以,在 Vue 中使用防抖和节流非常简单,既可以使用内置的 v-debounce 和 v-throttle 指令,也可以手动实现 debounce 和 throttle 方法,在组件方法中使用。希望这个介绍能帮助你在 Vue 项目中熟练使用防抖和节流技术。它们能有效优化组件的性能,提高用户体验,是 Vue 开发中很重要的技能。

第七章reactive全家桶

reactive

不可以绑定普通的数据类型这样是不允许 会给我们报错

绑定普通的数据类型 我们可以 使用昨天讲到ref

reactive基础用法

import{reactive} form"vue"
let person=reactive({
name:'xiaoman'
})
person.name="daman"

数组异步赋值问题

脱离响应式的情况

let person =reactive<number[]>([])
setTimeout(()=>{
person=[1,2,3]
console.log(person);
},1000)
​

解决方案1

使用push

import{reactive} from "vue"
let person =reactive<number[]>([])
setTimeout(()=>{
person.push(...arr)
console.log(person);
},1000)

方案2

包裹一层对象

ts

type Person={
list?:Array<number>
}
let person =reactive<Person>({
list:[]
})
setTimeout(()=>{
const arr =[1,2,3]
person.list=arr;
console.log(person);
},1000)

这创建了一个反应式变量person的类型Person,初始值为list的空数组。

1秒后,这个定时器函数会运行。它创建一个数组arr,[1, 2, 3]并将其分配给person.list。 由于person是反应性的,此赋值将触发反应性更新。 然后我们记录person并看到更新的值,列表[1, 2, 3]。总之,这显示了一个基本示例,创建一个反应性变量person并更新其中一个属性list,从而触发反应性更新,更新控制台日志。所以这个示例展示了在TypeScript中使用响应式变量的基础知识。定义一个带可选属性的接口,创建一个该接口的响应式变量,然后更新该变量的属性,触发更新并查看更新后的值。

readonly

拷贝一份proxy对象将其设置为只读??

import {reactive,readonly} from 'vue'
const person = reactive{{count:1}}
const copy = readonly(person)
//person.count++
copy.count++
​

shallowReactive

hallowReactive 是 Vue 中实现浅响应式 (shallow reactive) 的一个函数。正常来说,在 Vue 中数据对象的所有属性都是深度响应式的,也就是说,如果对象的嵌套属性改变了,此对象的 reactive 响应式代理也会产生变更通知。有时候我们只希望外层属性是响应式的,而不希望内部嵌套对象是响应式的。这种情况下可以使用 shallowReactive 函数来创建一个浅层响应式代理:

只能对浅层的数据 如果是深层的数据只会改变值 不会改变视图

<template>
<div>
{{state}}
<button @click="change1">
test1
</button>
<button @click="change1">
test2
</button>
</div>
</template>
​
<script setup lang="ts">
import{sahwllowReactive}from "vue"
const obj={
    a:1,
    first:{
        b:2,
        second:{
        c:3
        }
     }
}
​
const state = shallowReactive(obj)
function change1(){
state.a=7
}
function change2(){
state.first.b=8
state.first.second.c=9
consloe.log(state);
}
</script>

第八章to系列全家桶

toRef toRefs toRaw

<template>
<div>0
    <button @click="change">按</button>
    {{state}}
</div>
<template>
<script setup lang="ts">
import{reactive,toRef}from "vue"
const obj={
foo:1,
bar:1
}
const state = toRef(obj,'bar')
//bar 转化为响应式对象??
const change=()=>{
state.value++
console.log(obj,state);
}
<script>
​

如果原始对象是响应式的是会更新视图并且改变数据的

在这段代码中,bar 实际上并没有转化为响应式对象。这段代码做了以下几件事:1. 定义了一个普通对象 obj,包含 foo 和 bar 属性2. 使用 toRef 从 obj 中获取 bar 属性的响应式引用,赋值给 state3. 定义一个 change 方法,对 state.value 进行更改4. 在模板中使用 state 显示数据,并绑定 click 事件到 change 方法当我们点击按钮,调用 change 方法时,会发生以下事情:1. state.value 由于是响应式的,所以更改它会触发界面更新2. 但是 obj 中的 bar 属性本身并不是响应式的,所以 obj 对象自己并不会更新3. 我们在 change 方法中 console.log 了 obj 和 state,会看到:- obj 中的 bar 仍然是更改前的值 - state.value 已经更新了所以,关键的是要理解:- toRef 创建的响应式引用 state 与原对象 obj 中的属性 bar 实际上不是同一个东西 - state 是响应式的,会触发更新 - obj 的 bar 属性本身仍然是普通属性,不具有响应性只有当我们 wieder 给 obj.bar 赋值时,state.value 才会跟着更新,因为它 internally 还是连接着 obj.bar 的。所以 toRef 并没有使 obj 中的 bar 属性本身变为响应式的,它只是创建了一个 bar 属性的响应式引用 state 而已。

toRefs

可以帮我们批量创建ref对象主要是方便我们解构使用

import {reactive,toRefs} from "vue"
const obj = reactive({
foo:1,
bar:1
})
let {foo,bar} = toRefs(obj)
foo.value++
console.log(foo,bar);

toRaw

将响应式对象转化为普通对象

import {reactive,toRaw} from "vue"
const obj=reactive({
    foo:1,
    bar:1
})
const state= toRaw(obj)
//响应式对象转换为普通对象
cosnt change()=>{
    console.log(obj,state);
}

源码解析toRef

如果是ref 对象直接返回 否则 调用 ObjectRefImpl 创建一个类ref 对象

​
export function toRef<T extends object, K extends keyof T>(
  object: T,
  key: K,
  defaultValue?: T[K]
): ToRef<T[K]> {
  const val = object[key]
  return isRef(val)
    ? val
    : (new ObjectRefImpl(object, key, defaultValue) as any)
​

- T 表示对象的类型,它必须是 object 或 object 的子类型 - K 表示对象的键的类型,它必须是 T 的键之一 - defaultValue 是一个可选参数,表示默认值这个函数的作用是:- 检查 object[key] 是否已经是一个 ref 对象,如果是的话直接返回 - 否则创建一个 ObjectRefImpl 的实例,它实现了 ToRef 接口,并将 object、key 和 defaultValue 作为构造函数的参数 - 最后将这个 ObjectRefImpl 的实例作为 ToRef<T[K]> 的实例返回所以简而言之,这个函数的作用是:如果对象的某个键还没有关联的 ref,那么为其创建一个 ref,从而让使用者可以对这个键所指向的属性进行响应式监测。这个函数看起来像是 Vue3 中创建 ref 的工具函数,它通过检查属性是否已经是 ref 来防止重复创建 ref 的情况出现。

类ref 对象只是做了值的改变 并未处理 收集依赖 和 触发依赖的过程 所以 普通对象无法更新视图

class ObjectRefImpl<T extends object, K extends keyof T> {
  public readonly __v_isRef = true
 
  constructor(
    private readonly _object: T,
    private readonly _key: K,
    private readonly _defaultValue?: T[K]
  ) {}
 
  get value() {
    const val = this._object[this._key]
    return val === undefined ? (this._defaultValue as T[K]) : val
  }
 
  set value(newVal) {
    this._object[this._key] = newVal
  }
}

源码解析toRefs

其实就是把reactive 对象的每一个属性都变成了ref 对象循环 调用了toRef

export type ToRefs<T = any> = {
  [K in keyof T]: ToRef<T[K]>
}
export function toRefs<T extends object>(object: T): ToRefs<T> {
  if (__DEV__ && !isProxy(object)) {
    console.warn(`toRefs() expects a reactive object but received a plain one.`)
  }
  const ret: any = isArray(object) ? new Array(object.length) : {}
  for (const key in object) {
    ret[key] = toRef(object, key)
  }
  return ret
}

toRaw 源码解析

通过 ReactiveFlags 枚举值 取出 proxy 对象的 原始对象

export const enum ReactiveFlags {
  SKIP = '__v_skip',
  IS_REACTIVE = '__v_isReactive',
  IS_READONLY = '__v_isReadonly',
  IS_SHALLOW = '__v_isShallow',
  RAW = '__v_raw'
}
 
export function toRaw<T>(observed: T): T {
  const raw = observed && (observed as Target)[ReactiveFlags.RAW]
  return raw ? toRaw(raw) : observed
}

枚举是什么

枚举(Enum)是 TypeScript 中的一种类型,它定义了一些带名字的常量。枚举可以使代码更易读和理解。TypeScript 支持数字的和基于字符串的枚举。数字枚举:

ts
enum Direction {
  Up = 1,
  Down,
  Left,
  Right 
}

这里,Up 的初始值为 1,其他成员会从 2 开始自动增长。我们可以这样使用枚举成员:

ts
let dir = Direction.Up;

字符串枚举:

ts
enum Message {
  Success = "SUCCESS",
  Fail = "FAIL"
}

我们也可以不给枚举成员赋初始值,此时第 0 个成员默认为 0,之后的成员值会自动增 1:

ts
enum Response {
  No = 0,
  Yes 
}
​
let res: Response = Response.Yes;  // res = 1

枚举也可以拥有常数成员和计算成员:

ts
enum Color {
  Red,     // 常数成员,默认值为 0
  Green,   // 1
  Blue,    // 2 
  Cyan = Blue + 2  // 计算成员,值为 4
}

枚举类型也可以被混入到接口和类型别名中:

ts
interface WindowState {
  state: WindowStates  // 枚举类型
}
​
type WindowStates = "open" | "closed" | "minimized";  // 字符串类型

总之,枚举是 TypeScript 中一种简单但非常实用的类型,它常用于属性值域的定义和替换魔法字符串。

魔法字符串

魔法字符串指的是在代码中嵌入的不可读的字符串值。例如:

ts
function handleMessage(message) {
  if (message === "SUCCESS") {
    // ...
  } else if (message === "FAIL") {
    // ...
  }
}

这里的 "SUCCESS" 和 "FAIL" 就是魔法字符串。魔法字符串有以下几个问题:1. 不易理解:魔法字符串的意思不容易被人理解,阅读者无法从字符串值本身推导出其语义。2. 容易拼写错误:由于字符串值不具有自我描述性,所以很容易拼写错误却不被检测出来。3. 难以改变:如果需要改变字符串值,需要改变所有使用该字符串的地方,这很容易遗漏某些位置而导致 bug。4. 没有类型安全:字符串可以随意修改,而它所代表的语义却没有变化,这会导致类型错误。所以,通常我们会使用枚举来替换魔法字符串:

ts
enum Message {
  Success = "SUCCESS",
  Fail = "FAIL" 
}
​
function handleMessage(message: Message) {
  if (message === Message.Success) {
    // ...
  } else if (message === Message.Fail) {
    // ...
  }
}

这样可以解决上述所有的问题:1. 易于理解:枚举成员的名字本身就具有语义。

  1. 没有拼写错误:我们操作的都是枚举成员,拼写是由编译器保证的。

  2. 易于改变:如果需要改变枚举的值,只需要改变枚举定义处的内容,使用处不需要修改。

  3. 类型安全:枚举是一个类型,用它来约束变量可以实现类型安全。所以,通过使用枚举,我们彻底消除了魔法字符串,使代码更健壮、易于理解和维护。

第九章 computed计算属性

computed用法

计算属性就是当依赖的属性的值发生变化的时候,才会触发他的更改,如果依赖的值,不发生变化的时候,使用的是缓存中的属性值

1.函数形式

import{ computed,reactive,ref} from"vue"
let m =computed<string>(()=>{
return `$`+ price.value
})
price.value=500

2.对象形式

<template>
 <div>{{mul}}</div>
 <div @click="mul=100">
 click
 </div>
</template>
<script setup lang="ts">
import {computed,ref} from "vue"
let price = ref<number| string>(1)//$0
let mul=computed({
get:()=>{
return price.value
},
set:(value)=>{
price.value='set'+value
}
})
​
</script>

computed购物车案例

<template> <div> <input placeholder="请输入名称" v-model="keyWord" type="text"> <table style="margin-top:10px;" width="500" cellspacing="0" cellpadding="0" border> <thead> <tr> <th>物品</th> <th>单价</th> <th>数量</th> <th>总价</th> <th>操作</th> </tr> </thead> <tbody> <tr v-for="(item, index) in searchData"> <td align="center">{{ item.name }}</td> <td align="center">{{ item.price }}</td> <td align="center"> <button @click="item.num > 1 ? item.num-- : null">-</button> <input v-model="item.num" type="number"> <button @click="item.num < 99 ? item.num++ : null">+</button> </td> <td align="center">{{ item.price * item.num }}</td> <td align="center"> <button @click="del(index)">删除</button> </td> </tr> </tbody> <tfoot> <tr> <td colspan="5" align="right"> <span>总价:{{ total }}</span> </td> </tr> </tfoot>

    </table>
</div>

</template>

<script setup lang='ts'> import { reactive, ref,computed } from 'vue' let keyWord = ref<string>('') interface Data { name: string, price: number, num: number } const data = reactive<Data[]>([ { name: "小满的绿帽子", price: 100, num: 1, }, { name: "小满的红衣服", price: 200, num: 1, }, { name: "小满的黑袜子", price: 300, num: 1, } ])

let searchData = computed(()=>{ return data.filter(item => item.name.includes(keyWord.value)) })

let total = computed(() => { return data.reduce((prev: number, next: Data) => { return prev + next.num * next.price }, 0) })

const del = (index: number) => { data.splice(index, 1) }

</script>

<style scoped lang='less'></style>

手写源码 effect.ts

interface Options { scheduler?: Function } let activeEffect; export const effect = (fn: Function,options:Options) => { const _effect = function () { activeEffect = _effect; let res= fn() return res } _effect.options = options _effect() return _effect }

const targetMap = new WeakMap() export const track = (target, key) => { let depsMap = targetMap.get(target) if (!depsMap) { depsMap = new Map() targetMap.set(target, depsMap) } let deps = depsMap.get(key) if (!deps) { deps = new Set() depsMap.set(key, deps) }

deps.add(activeEffect) }

export const trigger = (target, key) => { const depsMap = targetMap.get(target) const deps = depsMap.get(key) deps.forEach(effect => { if(effect?.options?.scheduler){ effect?.options?.scheduler?.() }else{ effect() } }) } reactive.ts

import { track, trigger } from './effect'

const isObject = (target) => target != null && typeof target == 'object'

export const reactive = <T extends object>(target: T) => { return new Proxy(target, { get(target, key, receiver) { const res = Reflect.get(target, key, receiver) as object

        track(target, key)
 
        if (isObject(res)) {
            return reactive(res)
        }
 
        return res
    },
    set(target, key, value, receiver) {
        const res = Reflect.set(target, key, value, receiver)
 
        trigger(target, key)
 
        return res
    }
})

}

computed.ts

import { effect } from './effect'

export const computed = (getter: Function) => { let value = effect(getter, { scheduler: () => { _dirty = true } }) let catchValue let _dirty = true class ComputedRefImpl { get value() { if (dirty) { catchValue = _value() _dirty = false; } return catchValue } }

return new ComputedRefImpl()

} html

​​

<script type="module">
    import {computed} from './computed.js'
    import {reactive} from './reactive.js'
    window.a = reactive({name: 'a', age: 18})
    window.b = computed(() => {
        console.log('重新计算')
        return a.age + 10
    })
    
</script>

</body> </html>

第九章代码选自大佬小满博客,资料参考小满博客和视频

小满zs的博客_CSDN博客-Vue3,typeScript,nest-js领域博主

  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白村第一深情

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值