likes
comments
collection
share

Vue 框架基本应用和设计思想

作者站长头像
站长
· 阅读数 4

Vue 框架基本应用和设计思想

前置知识

Html CSS JS

  1. 页面内容结构: Html

  2. 页面样式: CSS

  3. 业务逻辑: JS (ECMAscript + DOM + BOM 核心是window对象)

描述语法 + 操作文档出现的接口 + 控制浏览器行为而出现的接口 (跳转 / 前进/后退/获取屏幕大小/获取浏览器信息window.navigatior.useragent)

渲染进程

每一个tab页面,都对应一个独立的渲染进程

渲染进程是多线程的,主要包括 GUI渲染线程、JS引擎线程 (互斥)

  1. GUI渲染线程(主要负责渲染页面)

    1. 解析HTML、CSS

    2. 构建DOM/Render树

    3. 初始布局与绘制

  2. JS引擎线程

    1. 解析JS脚本

    2. 运行JS代码

Event Loop

Event Loop 一种解决js单线程运行时不会阻塞的一种运行机制

  1. 同步任务

  2. 异步任务: 宏任务(setTimeout), 微任务(Promise)

Vue 框架基本应用和设计思想

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
</head>
<body>
<script>
   document.body.style.backgroundColor = 'blue';
   console.log(1);

   setTimeout(() => {
       document.body.style.backgroundColor = 'red';
       console.log(2);
   }, 0);

   Promise.resolve(3).then(number => {
       document.body.style.backgroundColor = 'orange';
       console.log(number);
   })
   console.log(4);
</script>
</body>
</html>

requestAnimationFrame和requestIdleCallback什么时候执行?

先看浏览器没一祯大致运行了哪些东西?

Vue 框架基本应用和设计思想

通过上图可看到,一帧内需要完成如下六个步骤的任务:

  • 处理用户的交互
  • JS 解析执行
  • 帧开始。窗口尺寸变更,页面滚去等的处理
  • requestAnimationFrame(rAF)
  • 布局
  • 绘制

再看什么时候会执行requestIdleCallback?

Vue 框架基本应用和设计思想

上面六个步骤完成后没超过 16 ms,说明时间有富余,此时就会执行 requestIdleCallback 里注册的任务。

从上图也可看出,和 requestAnimationFrame 每一帧必定会执行,requestIdleCallback 是捡浏览器空闲来执行任务,不一定每一祯都执行。

**总结: requestAnimationFrame**不是宏任务也不是微任务,执行时间位于微任务之后;

requestIdleCallback 在浏览器空闲时候执行,是宏任务,它比 setTimeout 更晚触发。

数据劫持原理

const o = {}
o.b=1
console.log(o.b)

function defineReactive(obj, key) {
  let val;
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get() {
      console.log('get');
      // 做一些事情
      return val;
    },
    set(newVal) {
      console.log('set');
      // 做一些事情
      val = newVal;
    }
  });
}

const o = {};
defineReactive(o, 'b');
o.b = 1;
console.log(o.b);

原生JS开发方式

<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
  </head>
  <!-- 实现静态页面 -->
<form>
  <p>
  Name:
  <span id="name-value"></span>
  </p>
  <input type="text" name="name" id="name-input" />
  <p>
    Email:
    <span id="email-value"></span>
  </p>
  <input type="email" name="email" id="email-input" />
</form>
<script>
  var nameInputEl = document.getElementById("name-input");
  var emailInputEl = document.getElementById("email-input");
  // 监听输入事件,此时 updateValue 函数未定义
  nameInputEl.addEventListener("input", updateNameValue);
  emailInputEl.addEventListener("input", updateEmailValue);

  var nameValueEl = document.getElementById("name-value");
  var emailValueEl = document.getElementById("email-value");

  nameInputEl.value = nameValueEl.innerText = '1234'
  emailInputEl.value = emailValueEl.innerText = '5678'
  
  // 定义 updateValue 函数,用来更新页面内容
  function updateNameValue(e) {
    nameValueEl.innerText = e.srcElement.value;
  }
  function updateEmailValue(e) {
    emailValueEl.innerText = e.srcElement.value;
  }
</script>
<body>

</body></html>

Vue开发

// view层
<template>
  <form>
    <p>
    Name:
    <span>{{ name }}</span>
  </p>
  <input
    type="text"
    name="name"
    :value="name"
    @input="updateNameValue"
  />
  <p>
  Email:
  <span>{{ email }}</span>
  </p>
  <input
    type="email"
    name="email"
    :value="email"
    @input="updateEmailValue"
  />
 </form>
</template>

<script setup lang="ts">
// modal层
import { ref, watch, computed } from 'vue'

const props = defineProps<{
  id: number;
}>();

const name = ref('1234')
const email = ref('5678')

// method 
const updateNameValue = (event: Event) => {
  name.value = (event.target as HTMLInputElement).value;
}
const updateEmailValue = (event: Event) => {
  email.value = (event.target as HTMLInputElement).value;
}
</script>

<style scoped>
</style>

对比

Vue 是一个 MVVM模式的框架

MVVMModel-View-ViewModel的缩写。ViewModel通过双向数据绑定将 View和Model联系起来,保证了视图和数据的一致性。

Vue 框架基本应用和设计思想

Vue 生命周期

onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted

Vue 响应式原理

Vue 框架基本应用和设计思想

每个组件挂载时都会创建一个渲染watcher, 这个watcher会放在一个全局属性Dep.target上,挂载时,会立即执行render函数。render函数执行过程中,会取响应式数据。

每一个响应式数据,都会默认初始化一个dep对象(const dep = new Dep()),这个dep对象就是用来收集watcher的。

当取响应式数据的值时会看有没有Dep.target,有的话,就执行 dep.depend方法,会把这个渲染watcher记住。

当数据发生变化时 ,会调用dep.notify函数,执行渲染watcher,更新页面.

watch 、 computed 、 method

<template>
   <div>
    method: {{getFullName()}}  {{getFullName()}} 
    <hr>
    computed: {{fullName}}
    
    watch:{{userInfo?.age}}
   </div>
</template>

<script setup lang="ts">
  const firstName = ref('zhang')
  const lastName = ref('san')
  const userInfo = ref({})
  const getFullName = () => {
    return firstName.value + ' ' + lastName.value
  }
  // computed作用: 自身依赖的数据源发生变化后,更新自身的值, 触发页面更新
  const fullName = computed(() => firstName.value + ' ' + lastName.value)
  // watch作用: 监听某些数据源变化后,执行一些操作去影响其他数据源
  watch(() => fullName.value, async(name) => {
  // 可以做一些操作, 如发送一个请求后更新数据 或者调用某个方法,更新数据源
  userInfo.value = await getData({name})
})

</script>

computed: 是作用于模板的,有缓存,是其他数据的变化影响自身的值, 不支持异步

watch: 用于监控数据的变化,执行一些异步任务或复杂计算任务,从而影响其他数据的变化

method: 函数的封装, 调用一次执行一次

computed原理

在初始化阶段,会为每个计算属性创建一个计算 watcher,这个watcher(lazy: true) , 默认不会立刻执行。而且每一个计算属性内部维护一个dirty属性,默认初始值 dirty: true。

在计算computed值的过程中会将 计算 watcher、渲染 watcher添加到依赖属性的Dep中。

当依赖属性发生变化会先触发计算 watcher的更新,将dirty置为true。

然后触发渲染 watcher的更新,从而获取最新的计算属性的值,并将dirty置为false,这样做的目的是再次获取计算属性时发现dirty是false,就直接返回缓存值。

虚拟DOM & DOM Diff算法

  1. 基本上所有框架都引入了虚拟DOM来描述真实DOM .其实就是用js对象描述dom节点

  2. 虚拟DOM不依赖真实运行环境,从而实现跨平台

Vue 框架基本应用和设计思想

基本流程

当组件挂载结束后,会记录第一次生成的虚拟DOM - oldVNode 当响应式数据发送变化时,会引起组件重新 render,此时就会生成新的虚拟DOM-newVNode 将oldVNode与newVNode做diff操作,将更改的部分应用到真实DOM上。

diff算法

diff算法保证最小量的更新DOM,减少性能消耗

Virtual DOM 真的比直接操作 DOM 快吗

不一定

针对任何的dom操作,我都可以写出比任何框架更快的手动优化。但那有什么意义呢?

在构建一个实际应用的时候,你难道为每一个地方都去做手动优化吗?出于可维护性的考虑,这显然不可能。

框架给你的保证是,你在不需要手动优化的情况下,我依然可以给你提供过得去的性能。

Vue是组件级更新的

Vue 对于响应式数据的更新,只会精确当前组件,而不会递归的去更新子组件

<template>
    <ul>
      <li>{{name1}}</li>
      <li>3</li>
      <li><Childcomponent :name1="name1">1</Childcomponent></li>
    <ul>
</template>
<script setup lang="ts">
  const name1 = ref('5')
  onMounted(() => {
      setTimeout(() => {
        name1.value = '6';
      }, 2000)
  )}
</script>

Vue异步批量执行watcher,渲染视图

<template>
    <ul>
      <li>{{name1}}</li>
    <ul>
</template>
<script setup lang="ts">
  const name1 = ref('name1')
  onMounted(() => {
      setTimeout(() => {
         name1.value = 'name11';
         name1.value = 'name12';
         name1.value = 'name13';
      })
  )}
</script>

queue = [watcher1, watcher2, watcher3, ...]
const flushQueue = () => {
    queue.forEach((watcher) => watcher.run())
}
nextTick(flushQueue)

前端是怎么开发需求的

  1. UI稿还原 & 基本流程处理

  2. 逻辑处理 (主要是发请求 + 处理数据)

    1. onMounted钩子里,发送请求,初始化页面数据, 视图初次渲染

    2. watch监听到某数据源的变化--> 触发其他数据源更新-->视图重新渲染

    3. computed 发现依赖的数据源更新-- >视图重新渲染

    4. 父组件更新,传给子组件的props 数据更新,视图更新