Fast Advanced Vue3.0

2019. 10.5 released a Vue3.0preview version of the source code, but it is expected to wait until the first quarter of 2020, the first official version 3.0 release possible.

You can see directly  github source.

The new Vue 3.0major infrastructure improvements and new features planned and realized:

  • Compiler (Compiler)

    • Use modular architecture
    • Optimization "Block tree"
    • More radical static tree hoisting function (detection of static grammar, a lift)
    • Support Source map
    • Built-identifier prefix (also known as "stripWith")
    • Built-in neat print (pretty-printing) function
    • After removing the prefix and the identifier Source map function, use Brotli compressed version of the browser to streamline the approximately 10KB
  • Runtime (Runtime)

    • Speed ​​significantly improved
    • Composition API supports and Options API, and typings
    • Proxy-based data change detection implemented
    • Fragments supported (allows a plurality of root component has from)
    • Support Portals (rendered in other locations to allow the DOM)
    • 支持 Suspense w/ async setup()
Not currently supported IE11

1. analyze Vue Composition API

You can see the official address

  • Vue 3 use tsto achieve a type inference, the new api in all normal function, when writing code can enjoy the full type inference (avoid using decorator)
  • Among multi-component logic to solve the reuse problem (to solve: the high-order components, mixin, scope slots)
  • Composition API easy to use

First look at the effect of early adopters Vue3.0

<script src="vue.global.js"></script> <div id="container"></div> <script> function usePosition(){ // 实时获取鼠标位置 let state = Vue.reactive({x:0,y:0}); function update(e) { state.x= e.pageX state.y = e.pageY } Vue.onMounted(() => { window.addEventListener('mousemove', update) }) Vue.onUnmounted(() => { window.removeEventListener('mousemove', update) }) return Vue.toRefs(state); } const App = { setup(){ // Composition API 使用的入口 const state = Vue.reactive({name:'youxuan'}); // 定义响应数据 const {x,y} = usePosition(); // 使用公共逻辑 Vue.onMounted(()=>{ console.log('当组挂载完成') }); Vue.onUpdated(()=>{ console.log('数据发生更新') }); Vue.onUnmounted(()=>{ console.log('组件将要卸载') }) function changeName(){ state.name = 'webyouxuan'; } return { // 返回上下文,可以在模板中使用 state, changeName, x, y } }, template:`<button @click="changeName">{{state.name}} 鼠标x: {{x}} 鼠标: {{y}}</button>` } Vue.createApp().mount(App,container); </script>
Here you will find a responsive is the Vue soul

2. The analysis of the source directory

packages directory contains Vue3.0all the features

├── packages
│   ├── compiler-core # 所有平台的编译器
│   ├── compiler-dom # 针对浏览器而写的编译器
│   ├── reactivity # 数据响应式系统
│   ├── runtime-core # 虚拟 DOM 渲染器 ,Vue 组件和 Vue 的各种API
│   ├── runtime-dom # 针对浏览器的 runtime。其功能包括处理原生 DOM API、DOM 事件和 DOM 属性等。 │   ├── runtime-test # 专门为测试写的runtime │   ├── server-renderer # 用于SSR │   ├── shared # 帮助方法 │   ├── template-explorer │   └── vue # 构建vue runtime + compiler

compiler
compiler-coremain function is to compile the exposure APIand baseCompilea method
compiler-dombased on compiler-corethe package for the browser compiler (browser process tag)

runtime
runtime-core virtual DOM renderer, and the various components of the API Vue Vue of
runtime-testthe DOMstructure into an object format, to facilitate testing
runtime-dom based runtime-corebrowser is written runtime (increasing change search node additions and deletions, style, etc.) to return render, createAppmethod

reactivity
separate data responsive system, the core method reactive, effectref,computed

vue
integration  compiler + runtime

We resolved this Vue3.0 directory structure, the entire project as a whole is still very clear

Taste fresh again:
We can look at how to use the official test casesVue3.0

const app = {
    template:`<div>{{count}}</div>`,
    data(){
        return {count:100} }, } let proxy = Vue.createApp().mount(app,container); setTimeout(()=>{ proxy.count = 200; },2000)
Now let's compare the difference in principle responsive and Vue 2 Vue 3

3.Vue2.0 responsive principle mechanism - defineProperty

This principle is nothing new, that is 拦截对象, to increase the object's properties set and  getmethods, because the core is definePropertyso needed for an array of methods to intercept

3.1 pairs of objects intercept

function observer(target){
    // 如果不是对象数据类型直接返回即可 if(typeof target !== 'object'){ return target } // 重新定义key for(let key in target){ defineReactive(target,key,target[key]) } } function update(){ console.log('update view') } function defineReactive(obj,key,value){ observer(value); // 有可能对象类型是多层,递归劫持 Object.defineProperty(obj,key,{ get(){ // 在get 方法中收集依赖 return value }, set(newVal){ if(newVal !== value){ observer(value); update(); // 在set方法中触发更新 } } }) } let obj = {name:'youxuan'} observer(obj); obj.name = 'webyouxuan';

3.2 array method hijacking

let oldProtoMehtods = Array.prototype;
let proto = Object.create(oldProtoMehtods);
['push','pop','shift','unshift'].forEach(method=>{ Object.defineProperty(proto,method,{ get(){ update(); oldProtoMehtods[method].call(this,...arguments) } }) }) function observer(target){ if(typeof target !== 'object'){ return target } // 如果不是对象数据类型直接返回即可 if(Array.isArray(target)){ Object.setPrototypeOf(target,proto); // 给数组中的每一项进行observr for(let i = 0 ; i < target.length;i++){ observer(target[i]) } return }; // 重新定义key for(let key in target){ defineReactive(target,key,target[key]) } }

test

let obj = {hobby:[{name:'youxuan'},'喝']} observer(obj) obj.hobby[0].name = 'webyouxuan'; // 更改数组中的对象也会触发试图更新 console.log(obj)
Here the process is not dependent on the collection described in detail, we focus on Vue3.0 the
  • Object.defineProperty shortcomings

    • Unable to monitor changes in the array
    • We need deep traversal, a waste of memory

4.Vue3.0 data response mechanism - Proxy

Before learning Vue3.0, you must first master the ES6 in the  Proxy , Reflect  and ES6 as we provide  the Map , the Set two data structures

Principle applied first to say:

let p = Vue.reactive({name:'youxuan'});
Vue.effect(()=>{ // effect方法会立即被触发 console.log(p.name); }) p.name = 'webyouxuan';; // 修改属性后会再次触发effect方法
Source code is the use of ts writing, in order to facilitate better understanding of the principles, here we use js to write from zero, then look at the source code it is very easy!

4.1 reactive way to achieve

Custom acquisition by proxy, add, delete and other acts

function reactive(target){
    // 创建响应式对象 return createReactiveObject(target); } function isObject(target){ return typeof target === 'object' && target!== null; } function createReactiveObject(target){ // 判断target是不是对象,不是对象不必继续 if(!isObject(target)){ return target; } const handlers = { get(target,key,receiver){ // 取值 console.log('获取') let res = Reflect.get(target,key,receiver); return res; }, set(target,key,value,receiver){ // 更改 、 新增属性 console.log('设置') let result = Reflect.set(target,key,value,receiver); return result; }, deleteProperty(target,key){ // 删除属性 console.log('删除') const result = Reflect.deleteProperty(target,key); return result; } } // 开始代理 observed = new Proxy(target,handlers); return observed; } let p = reactive({name:'youxuan'}); console.log(p.name); // 获取 p.name = 'webyouxuan'; // 设置 delete p.name; // 删除

How do we continue to consider a multi-layer proxy object implementation

let p = reactive({ name: "youxuan", age: { num: 10 } }); p.age.num = 11
Since we only agents of the first layer of the object, so the age object will not trigger the change is set methods, but it triggered get method, which is due to  p.age cause  get operation
get(target, key, receiver) {
      // 取值
    console.log("获取");
    let res = Reflect.get(target, key, receiver); return isObject(res) // 懒代理,只有当取值时再次做代理,vue2.0中一上来就会全部递归增加getter,setter ? reactive(res) : res; }
Here we will p.age take to the proxy object again, so to change the value to trigger set method

We continue to consider an array of issues
we can find Proxythe default array can support, including changes in the length of the array as well as changes in index values

let p = reactive([1,2,3,4]); p.push(5);
But this will trigger two set methods, the first update of the array is the first 4 item, the second is an array of updated length

Let masked trigger several times, update

set(target, key, value, receiver) {
    // 更改、新增属性
    let oldValue = target[key]; // 获取上次的值
    let hadKey = hasOwn(target,key); // 看这个属性是否存在 let result = Reflect.set(target, key, value, receiver); if(!hadKey){ // 新增属性 console.log('更新 添加') }else if(oldValue !== value){ // 修改存在的属性 console.log('更新 修改') } // 当调用push 方法第一次修改时数组长度已经发生变化 // 如果这次的值和上次的值一样则不触发更新 return result; }

Solving repeated use of reactive cases

// 情况1.多次代理同一个对象
let arr = [1,2,3,4]; let p = reactive(arr); reactive(arr); // 情况2.将代理后的结果继续代理 let p = reactive([1,2,3,4]); reactive(p);
By hash表 way to solve the case of duplicate proxy
const toProxy = new WeakMap(); // 存放被代理过的对象
const toRaw = new WeakMap(); // 存放已经代理过的对象 function reactive(target) { // 创建响应式对象 return createReactiveObject(target); } function isObject(target) { return typeof target === "object" && target !== null; } function hasOwn(target,key){ return target.hasOwnProperty(key); } function createReactiveObject(target) { if (!isObject(target)) { return target; } let observed = toProxy.get(target); if(observed){ // 判断是否被代理过 return observed; } if(toRaw.has(target)){ // 判断是否要重复代理 return target; } const handlers = { get(target, key, receiver) { // 取值 console.log("获取"); let res = Reflect.get(target, key, receiver); return isObject(res) ? reactive(res) : res; }, set(target, key, value, receiver) { let oldValue = target[key]; let hadKey = hasOwn(target,key); let result = Reflect.set(target, key, value, receiver); if(!hadKey){ console.log('更新 添加') }else if(oldValue !== value){ console.log('更新 修改') } return result; }, deleteProperty(target, key) { console.log("删除"); const result = Reflect.deleteProperty(target, key); return result; } }; // 开始代理 observed = new Proxy(target, handlers); toProxy.set(target,observed); toRaw.set(observed,target); // 做映射表 return observed; }
To this reactive method basically completed, the next step is to Vue2 logic implementations rely collect and update as trigger

tupian

get(target, key, receiver) {
    let res = Reflect.get(target, key, receiver);
+   track(target,'get',key); // 依赖收集
    return isObject(res) 
    ?reactive(res):res;
},
set(target, key, value, receiver) {
    let oldValue = target[key];
    let hadKey = hasOwn(target,key);
    let result = Reflect.set(target, key, value, receiver);
    if(!hadKey){
+     trigger(target,'add',key); // 触发添加
    }else if(oldValue !== value){
+     trigger(target,'set',key); // 触发修改
    }
    return result;
}
the role of track is dependent on the collection, compilation of the effect , let's implement effect the principle, after the perfection  track and trigger methods

4.2 effect to achieve

effect means that the side effects, this method will first perform a default. If the data changes trigger this callback again.

let school = {name:'youxuan'}
let p = reactive(school);
effect(()=>{ console.log(p.name); // youxuan })

We do this effectmethod, we need to be effectpackaged into a responsive method effect.

function effect(fn) {
  const effect = createReactiveEffect(fn); // 创建响应式的effect effect(); // 先执行一次 return effect; } const activeReactiveEffectStack = []; // 存放响应式effect function createReactiveEffect(fn) { const effect = function() { // 响应式的effect return run(effect, fn); }; return effect; } function run(effect, fn) { try { activeReactiveEffectStack.push(effect); return fn(); // 先让fn执行,执行时会触发get方法,可以将effect存入对应的key属性 } finally { activeReactiveEffectStack.pop(effect); } }

When you call fn()you may trigger getmethod, this time will triggertrack

const targetMap = new WeakMap();
function track(target,type,key){ // 查看是否有effect const effect = activeReactiveEffectStack[activeReactiveEffectStack.length-1]; if(effect){ let depsMap = targetMap.get(target); if(!depsMap){ // 不存在map targetMap.set(target,depsMap = new Map()); } let dep = depsMap.get(target); if(!dep){ // 不存在set depsMap.set(key,(dep = new Set())); } if(!dep.has(effect)){ dep.add(effect); // 将effect添加到依赖中 } } }

When the update property will trigger triggerexecution, to find out the corresponding set of stored effectsequentially executed

function trigger(target,type,key){
    const depsMap = targetMap.get(target); if(!depsMap){ return } let effects = depsMap.get(key); if(effects){ effects.forEach(effect=>{ effect(); }) } }

We have found the following problems

let school = [1,2,3];
let p = reactive(school); effect(()=>{ console.log(p.length); }) p.push(100);
Added value, effect the method does not re-run, because push the modification length we have been masked the trigger trigger method, when the new item should manually trigger length property in the dependency.
function trigger(target, type, key) {
  const depsMap = targetMap.get(target); if (!depsMap) { return; } let effects = depsMap.get(key); if (effects) { effects.forEach(effect => { effect(); }); } // 处理如果当前类型是增加属性,如果用到数组的length的effect应该也会被执行 if (type === "add") { let effects = depsMap.get("length"); if (effects) { effects.forEach(effect => { effect(); }); } } }

4.3 ref achieve

ref primitive data types can also be converted into a responsive data, need to .valueget the value attribute

function convert(val) {
  return isObject(val) ? reactive(val) : val; } function ref(raw) { raw = convert(raw); const v = { _isRef:true, // 标识是ref类型 get value() { track(v, "get", ""); return raw; }, set value(newVal) { raw = newVal; trigger(v,'set',''); } }; return v; }

The problem again we have to write cases

let r = ref(1);
let c = reactive({
    a:r
});
console.log(c.a.value);
To do so would mean that every time more than one .value , so too hard to use

In getdetermining if the acquisition process is refof value, this value is valuereturned directly to

let res = Reflect.get(target, key, receiver);
if(res._isRef){
  return res.value
}

4.4 computed achieve

computed Implementations are based  effect to achieve, is characterized by computedthe function will not be executed immediately, there is many times the value of caching mechanism

First look at usage:

let a = reactive({name:'youxuan'});
let c = computed(()=>{ console.log('执行次数') return a.name +'webyouxuan'; }) // 不取不执行,取n次只执行一次 console.log(c.value); console.log(c.value);
function computed(getter){
  let dirty = true; const runner = effect(getter,{ // 标识这个effect是懒执行 lazy:true, // 懒执行 scheduler:()=>{ // 当依赖的属性变化了,调用此方法,而不是重新执行effect dirty = true; } }); let value; return { _isRef:true, get value(){ if(dirty){ value = runner(); // 执行runner会继续收集依赖 dirty = false; } return value; } } }

Modification effectmethod

function effect(fn,options) {
  let effect = createReactiveEffect(fn,options); if(!options.lazy){ // 如果是lazy 则不立即执行 effect(); } return effect; } function createReactiveEffect(fn,options) { const effect = function() { return run(effect, fn); }; effect.scheduler = options.scheduler; return effect; }

In triggerjudging

deps.forEach(effect => {
  if(effect.scheduler){ // 如果有scheduler 说明不需要执行effect
    effect.scheduler(); // 将dirty设置为true,下次获取值时重新执行runner方法 }else{ effect(); // 否则就是effect 正常执行即可 } });
let a = reactive({name:'youxuan'});
let c = computed(()=>{ console.log('执行次数') return a.name +'webyouxuan'; }) // 不取不执行,取n次只执行一次 console.log(c.value); a.name = 'zf10'; // 更改值 不会触发重新计算,但是会将dirty变成true console.log(c.value); // 重新调用计算方法
This will be Vue3.0 core  Composition Api  to explain finished! Whether interview or late applications are also no longer have to worry about it! ~

Guess you like

Origin www.cnblogs.com/alsohui/p/12028340.html