重新审视自己,重回前端开发。
重新思考后发现自己与当前的前端节奏存在一定的差距,所以需要重新调整自己,重拾前端的相关内容,并进行深度化学习。
本章既是一个对过去内容的延续补充,而且还是对底层逻辑结构的深度剖析,旨在将书读后,三思后将书再读薄。本章主要是读厚的一个章节,可能要持续更新一个月左右的时间,首先目的是将Vue3进行一个全方位的探索,同时对里面出现的基础性内容做一定的延伸;另一方面就是开始深入研究源码,思考Vue3的源码逻辑(感觉这一块将是一个大的模块,如果思考的很多,后面单独出一个章节进行源码结构的细化),同时也对源码的学习寻找一个自己适合的学习方式。
任重道远,望厚积而薄发。
Vue2 到 Vue3 过渡
区别
双向数据绑定原理不同
- Vue2:基于ES5的
Object.definePropert() - Vue3:基于ES6的
ProxyAPI
- Vue2:基于ES5的
碎片支持
Vue3支持碎片;支持多根节点写法;<template> <div>根节点</div> <div>同级节点</div> <div>同级节点</div> </template>API 类型差别
Vue3使用组合式API定义数据变量及方法差异
Vue3的新方法setup(),建立反应性数据。- 引入reactive()
- 使用reactive()来声明数据为响应性数据
- 使用setup()fanhu8i响应性数据,从template获取数据
生命周期与钩子函数
父子传参不同
- Vue2:调用
this.$emit传入事件名及对象 - Vue3:
Setup()
- Vue2:调用
指令与插槽不同
- Vue3中 v-if 与 v-for 同时使用时,前者优先级高于后者
- 具名插槽:Vue2使用
slot=;Vue3使用v-slot - 作用域插槽:Vue2父组件通过
slot-scope="data"获取子组件数据;Vue3父组件通过#data或#default={data}获取数据
main.js文件不同
Vue3使用结构的形式进行操作,引入工厂函数处理,可以没有根标签diff算法不同
区别解析
组合式API [ Compositon API ]
优点
- 更好的逻辑复用
- 更灵活的代码结构
- 更好的类型推导
- 生产包体积更小
Setup() 函数
Setup在创建组件之前执行,一旦props被解析,并充当合成API的入口。
Setup中不可使用this;Setup函数只会在组件初始化的时候执行一次。
<script>
export default{
setup(props, context){
/**
props:父组件传递过来的属性会放在props对象里
context:主要包含三个属性
a. attrs:所有非props的attribute
b. slots:父组件传递过来的插槽
c. emit:组件内部需要发出的事件
*/
return {
}
}
}
</script>
reactive 函数
<script>
import { reactive } from 'vue'
export default {
setup(){
const state = reactive({ // 把对象数据转为响应式对象
name: 'nike',
age: 18
})
return {
state
}
}
}
</script>
分类:
- reactive
- shallowReactive
- readonly
- shallowReadonly
toRef 函数与 toRefs 函数
<script>
import { reactive, toRef } from 'vue'
export default{
name: '',
setup(){
const obj = reactive({
name: 'nike',
age: 18
})
const name = toRef(obj, 'name') // 把obj中的name属性单独出来
return {
name
}
}
}
</script>
<script>
import { reactive, toRefs } from 'vue'
export default {
setup(){
const obj = reactive({
name: 'nike',
age: 18
})
return {
...toRefs(obj) // 该对象展开的每个属性都为响应式数据
}
}
}
</script>
ref 函数
<script>
import { ref } from 'vue';
export default{
setup(){
let item = ref(100) // item.value
return {
item
}
}
}
</script>
Effect
export function effect<T = any>(
fn: () => T,
options: ReactiveEffectOptions = EMPTY_OBJ
): ReactiveEffect<T> {
if (isEffect(fn)) {
fn = fn.raw
}
const effect = createReactiveEffect(fn, options)
if (!options.lazy) {
effect()
}
return effect
}
指令与插槽
diff 算法
工厂函数
Proxy
元编程,即对编程语言进行编程。
支持拦截。
简单来说定义Proxy的输入值,如果存在可以获取到对应的对象属性,若没有则返回失败,但是我们可以自由定义Proxy获取到的对象的子集,通过定义的函数类型也可以将说去的内容进行加载或做其他的处理,但是处理方式均需要在get中进行方法的定义。
var obj = new Proxy({}, {
get: function(target, propKey, receiver){
console.log(`getting ${propKey}!`);
return Reflect.get(target, propKey, receiver);
},
set: function(target, propKey, value, receiver){
console.log(`setting ${propKey}!`);
return Reflect.set(target, propKey, value, receiver);
}
})
get()
用于拦截某个属性的读取操作;
接受三个参数:目标对象、属性名、proxy实例本身[ 可选参数 ]
// Demo1 拦截读取操作
var person = {
name: 'nike'
}
var proxy = new Proxy(person, {
get: function(target, propKey){
if (propKey in target) {
return target[propKey]
} else {
throw new ReferenceError('')
}
}
})
proxy.name // 'nike'
proxy.age // error
get方法可以继承;
// Demo2 继承
let proto = new Proxy({}, {
get(target, propertyKey, receiver){
console.log('GET ' + propertyKey);
return target[propertyKey]
}
})
let obj = Object.create(proto);
obj.foo // "GET foo"
obj.hahah // "GET hahah"
get方法实现拦截;
function createArr(...ele){
let handler = {
get(target, propKey, receiver){
let index = Number(propKey);
if (index < 0) propKey = String(target.length + index);
return Reflect.get(target, propKey, receiver);
}
}
let target = []
target.push(...ele);
return new Proxy(target, handler)
}
let arr = createArr('a', 'b', 'c');
// index 0 1 2
arr[-2] // b
arr[-1] // c
arr[0] // a
链式操作;
var pipe = function(value){
var funcStack = [];
var oproxy = new Proxy({}, {
get: function (pObj, fnName){
if (fnName === 'get') {
return funcStack.reduce((val, fn) => {
return fn(val)
}, value);
}
funcStack.push(window[fnName]);
return oproxy;
}
})
return oproxy;
}
var double = n => n * 2;
var pow = n => n * n;
var reverseInt = n => n.toString().split("").reverse().join("") | 0;
pipe(6).double.pow.reverseInt.get; // 441
/**
* eg: 3
* step1: n * 2 6
* step2: n * n 36
* step3: reverse number 63
*/
生成DOM节点;[ 待补充代码 ]
对于属性(Object.defineProperties)的配置(configurable)及可写性(writable);
const target = Object.defineProperties({}, {
foo: {
value: 123,
writable: false,
configurable: true
},
});
const handler = {
get(target, propKey) {
return 'abc';
}
};
const proxy = new Proxy(target, handler);
proxy.foo; // 'abc'
set()
用于拦截某个属性的赋值操作;
let validator = {
set: function(obj, prop, value) {
if (prop === 'age') {
if(!Number.isInteger(value)){
throw new TypeError('The age is not an integer')
}
if (value > 200) {
throw new RangeError('The age seems invalid')
}
}
obj[prop] = value;
return true;
}
}
let person = new Proxy({}, validator);
person.age = 100;
person.age // 100
person.age = 'young' // error
person.age = 300 // error
在对象上设置内部属性,防止内部属性被外部读写。
const handler = {
get(target, key) {
invariant(key, 'get');
return target[key]
},
set(target, key,value) {
invariant(key, 'set');
target[key] = value;
return true
}
}
function invariant(key, action) {
if (key[0] === '_') {
throw new Error(`Invalid attempt to ${action} private "${key}" property`);
}
}
const target = {};
const proxy = new Proxy(target, handler);
proxy._prop
// Error: Invalid attempt to get private "_prop" property
proxy._prop = 'c'
// Error: Invalid attempt to set private "_prop" property
第四个参数
const handler = {
set: function(obj, prop, value, receiver) {
obj[prop] = receiver;
return true;
}
};
const proxy = new Proxy({}, handler);
proxy.foo = 'bar';
proxy.foo === proxy; // true
apply()
方法拦截函数的调用、call 和 apply 操作;
var target = function () {
return 'This is the target'
};
var handler = {
apply: function (){
return 'This is the proxy'
}
}
var p = new Proxy(target, handler)
p(); // This is the proxy
var twice = {
apply (target, ctx, args) {
return Reflect.apply(...arguments) * 2
}
}
function sum(left, right) {
return left + right;
}
var proxy = new Proxy(sum, twice);
proxy(1,2) // 6
proxy.call(null, 5, 6) // 22
proxy(null, [7, 8]) // 30
has()
用于拦截 hasProperty 操作,而不是 HasOwnProperty 操作;has 方法不判断一个属性是对象自身属性还是继承属性;
此方法用于接受两个参数,分别是目标属性(target)、需查询的属性名(key)。
var handler = {
has (target, key) {
if (key[0] === '_') {
return false;
}
return key in target;
}
}
var target = { _prop: 'foo', prop: 'foo' };
var proxy = new Proxy(target, handler);
'_prop' in proxy; // false
has 拦截对 for…in 循环不生效;
construct()
用于拦截 new 命令;
可接受三个参数;
- target:目标函数
- args:构建函数参数数组
- newTarget:创造实例对象时,new 命令的构造函数
deleteProperty()
defineProperty()
getOwnPropertyDescriptor()
getPrototypeOf()
isExtensible()
ownKeys()
preventExtensions()
setPrototypeOf()
Proxy.revocable()
补充概念
Object.defineProperties
Object.defineProperties(obj, props);
obj 为定义属性的对象,props 是一个属性描述符对象
属性描述符属性:
- configurable:是否可以被删除/配置属性的定义描述对象,默认值为 false
- enumerable:是否可以通过枚举进行遍历,默认值为 false
- writable:是否可以被修改,默认值为 false
- value:属性默认值,默认值为 undefined
- get:函数的 getter,默认值为 undefined
- set:函数的 setter,默认值为 undefined
注:get,set 与 value,writable 冲突,即不可同时使用
hasOwnProperty
obj.hasOwnProperty(propertyName)
判断对象是否包含指定属性(自身属性或自有属性,不包括继承属性);
返回值为布尔值;
Vue3 源码深入解析
暂时可参考 Vue3 源码解析
Vue 源码详解,未完待续…
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!