重新审视自己,重回前端开发。

  重新思考后发现自己与当前的前端节奏存在一定的差距,所以需要重新调整自己,重拾前端的相关内容,并进行深度化学习。
  本章既是一个对过去内容的延续补充,而且还是对底层逻辑结构的深度剖析,旨在将书读后,三思后将书再读薄。本章主要是读厚的一个章节,可能要持续更新一个月左右的时间,首先目的是将Vue3进行一个全方位的探索,同时对里面出现的基础性内容做一定的延伸;另一方面就是开始深入研究源码,思考Vue3的源码逻辑(感觉这一块将是一个大的模块,如果思考的很多,后面单独出一个章节进行源码结构的细化),同时也对源码的学习寻找一个自己适合的学习方式。
 任重道远,望厚积而薄发。

Vue2 到 Vue3 过渡

区别

  1. 双向数据绑定原理不同

    • Vue2:基于ES5的 Object.definePropert()
    • Vue3:基于ES6的 ProxyAPI
  2. 碎片支持
     Vue3支持碎片;支持多根节点写法;

    <template>
       <div>根节点</div>
       <div>同级节点</div>
       <div>同级节点</div>
    </template>
    
  3. API 类型差别
     Vue3使用组合式API

  4. 定义数据变量及方法差异
     Vue3的新方法 setup(),建立反应性数据。

    • 引入reactive()
    • 使用reactive()来声明数据为响应性数据
    • 使用setup()fanhu8i响应性数据,从template获取数据
  5. 生命周期与钩子函数

  6. 父子传参不同

    • Vue2:调用 this.$emit 传入事件名及对象
    • Vue3:Setup()
  7. 指令与插槽不同

    • Vue3中 v-if 与 v-for 同时使用时,前者优先级高于后者
    • 具名插槽:Vue2使用 slot=;Vue3使用 v-slot
    • 作用域插槽:Vue2父组件通过 slot-scope="data" 获取子组件数据;Vue3父组件通过 #data#default={data} 获取数据
  8. main.js 文件不同
     Vue3使用结构的形式进行操作,引入工厂函数处理,可以没有根标签

  9. diff算法不同

区别解析

组合式API [ Compositon API ]

优点

  1. 更好的逻辑复用
  2. 更灵活的代码结构
  3. 更好的类型推导
  4. 生产包体积更小

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 源码详解,未完待续…



笔记      Learning Notes

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!