Vue 框架基本应用和设计思想
Vue 框架基本应用和设计思想
前置知识
Html CSS JS
-
页面内容结构: Html
-
页面样式: CSS
-
业务逻辑: JS (ECMAscript + DOM + BOM 核心是window对象)
描述语法 + 操作文档出现的接口 + 控制浏览器行为而出现的接口 (跳转 / 前进/后退/获取屏幕大小/获取浏览器信息window.navigatior.useragent)
渲染进程
每一个tab页面,都对应一个独立的渲染进程
渲染进程是多线程的,主要包括 GUI渲染线程、JS引擎线程 (互斥)
-
GUI渲染线程(主要负责渲染页面)
-
解析HTML、CSS
-
构建DOM/Render树
-
初始布局与绘制
-
-
JS引擎线程
-
解析JS脚本
-
运行JS代码
-
Event Loop
Event Loop 一种解决js单线程运行时不会阻塞的一种运行机制
-
同步任务
-
异步任务: 宏任务(setTimeout), 微任务(Promise)
<!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什么时候执行?
先看浏览器没一祯大致运行了哪些东西?
通过上图可看到,一帧内需要完成如下六个步骤的任务:
- 处理用户的交互
- JS 解析执行
- 帧开始。窗口尺寸变更,页面滚去等的处理
- requestAnimationFrame(rAF)
- 布局
- 绘制
再看什么时候会执行requestIdleCallback?
上面六个步骤完成后没超过 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模式的框架
MVVM
是Model-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
<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算法
-
基本上所有框架都引入了虚拟DOM来描述真实DOM .其实就是用js对象描述dom节点
-
虚拟DOM不依赖真实运行环境,从而实现跨平台
基本流程
当组件挂载结束后,会记录第一次生成的虚拟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)
前端是怎么开发需求的
-
UI稿还原 & 基本流程处理
-
逻辑处理 (主要是发请求 + 处理数据)
-
onMounted钩子里,发送请求,初始化页面数据, 视图初次渲染
-
watch监听到某数据源的变化--> 触发其他数据源更新-->视图重新渲染
-
computed 发现依赖的数据源更新-- >视图重新渲染
-
父组件更新,传给子组件的props 数据更新,视图更新
-
转载自:https://juejin.cn/post/7345105895929626635