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

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)

xml 复制代码
<!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>

数据劫持原理

javascript 复制代码
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开发方式

xml 复制代码
<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开发

ini 复制代码
// 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开发

命令式 (关注过程 直接操作DOM)

html和js耦合度高

声明式(关注结果 数据驱动视图)

view和model分层

css选择器、js全局污染

css 、js变量 仅作用于当前组件

Vue 是一个 MVVM模式的框架

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

Vue 生命周期

onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted

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

xml 复制代码
<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不依赖真实运行环境,从而实现跨平台

基本流程

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

diff算法

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

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

不一定

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

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

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

Vue是组件级更新的

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

xml 复制代码
<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,渲染视图

ini 复制代码
<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 数据更新,视图更新

相关推荐
leoZ2314 小时前
Claude 驱动的全栈开发:Spring Boot + Vue 的 BS 架构实践
vue.js·spring boot·架构
荒诞英雄7 小时前
从 17MB Vendor 大包到按需加载:Vite 多入口 Vue 组件库首屏优化实践
vue.js·vite
jsonbro7 小时前
手把手带你实现发布订阅模式
前端·vue.js·设计模式
Cobyte10 小时前
23.Vue Vapor 组件 attrs 的实现
前端·javascript·vue.js
斯特凡今天也很帅10 小时前
xml页面可以打开,不报错,但是显示数据的地方也不显示,我是怎么解决的
xml·vue.js
柯克七七12 小时前
给老项目加了 TypeScript,本来只想自己爽,结果全公司代码审查标准被我抬高了
前端·vue.js·typescript
自然 醒14 小时前
前端如何实现在线预览office常见文件功能?
前端·vue.js
肉肉不吃 肉14 小时前
前端调试跨域如何解决
前端·vue.js
卓怡学长15 小时前
w268基于springboot + vue 物流系统
java·数据库·vue.js·spring boot·spring·intellij-idea
2601_9628517415 小时前
计算机毕业设计之基于SSM的网络安全课程资源分享平台
vue.js·算法·安全·web安全·yolo·目标检测·课程设计