小满vue3笔记(含源码解读)
。。AST语法数:这个东西在ES6转ES5的插件babel;ts转js中会进行ast转换根据满哥说的,我的理解是vue3在用js去描述DOM对象;这个描述和操作不一样(js操作dom)正在上传…重新上传取消为什么不直接操作dom:原因:dom的属性太多,影响性能我看是说这一次描述dom,会全部描述进去,影响性能。
第一章
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全家桶
全家...
手写防抖
在 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. 易于理解:枚举成员的名字本身就具有语义。
-
没有拼写错误:我们操作的都是枚举成员,拼写是由编译器保证的。
-
易于改变:如果需要改变枚举的值,只需要改变枚举定义处的内容,使用处不需要修改。
-
类型安全:枚举是一个类型,用它来约束变量可以实现类型安全。所以,通过使用枚举,我们彻底消除了魔法字符串,使代码更健壮、易于理解和维护。
第九章 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>
第九章代码选自大佬小满博客,资料参考小满博客和视频
更多推荐
所有评论(0)