主页 > 电脑硬件  > 

Vue系列之:基础知识

Vue系列之:基础知识
什么是 MVVM

MVVM(Model-View-ViewModel)一种软件设计模式,旨在将应用程序的数据模型(Model)与视图层(View)分离,并通过 ViewModel 来实现它们之间的通信。降低了代码的耦合度。

Model 代表数据模型,是应用程序中用于处理数据的部分。在 Vue.js 中,Model 通常指的是组件的 data 函数返回的对象,或者 Vue 实例的 data 属性。

View 是用户界面,是用户与应用程序进行交互的部分。在 Vue.js 中,View 通常指的是 Vue 组件的模板(template),它使用声明式的语法来描述如何渲染数据。Vue 的模板语法允许开发者以声明的方式将 DOM 绑定到底层 Vue 实例的数据上。

ViewModel 是 MVVM 模式的核心,它作为 View 和 Model 之间的桥梁,负责将 Model 的数据同步到 View 显示出来,以及将用户对 View 的操作反映到 Model 上。

Vue 实例通过其响应式系统(基于 ES6 的 Proxy 或 Object.defineProperty 实现)来监听 Model 的变化,并自动更新 DOM(View)

Vue 钩子函数

钩子函数(hook)是在系统在进行消息的传递处理时候利用钩子机制截取消息,可以对一些特定的消息进行处理。

为什么要叫钩子?

钩子就是用来挂的嘛,在线程执行的过程中,我们想要在步骤1和步骤2直接挂一个函数,什么东西可以挂呢?钩子!所以就有钩子函数 这个叫法了。

Vue 八大生命周期钩子函数:

Vue2Vue3调用时间beforeCreatesetupvue 实例初始化之前调用createdsetupvue 实例初始化之后调用beforeMountonBeforeMount挂载到 DOM 树之前调用mountedonMounted挂载到 DOM 树之后调用beforeUpdateonBeforeUpdate数据更新之前调用updatedonUpdated数据更新之后调用beforeDestroyonBeforeUnmountvue实例销毁之前调用destroyedonUnmountedvue实例销毁之后调用

Vue.js 官方文档

Vue 实例

每个 Vue 应用都是通过用 Vue 构造函数创建一个新的 Vue 实例开始的

// vue2 var vm = new Vue({ // 选项 }) // vue3 import { createApp } from 'vue' const app = createApp({ /* 根组件选项 */ })

Vue 实例和 Vue 生命周期有什么关系?

在 Vue 中,当创建一个组件或 Vue 实例时,它会经历一系列初始化步骤,这就是所谓的生命周期。

Vue 生命周期描述了 Vue 实例从创建到销毁的整个过程。

Vue2 实例方法

Vue3 应用实例

Vue3 组件实例

Vue.prototype

用途:

Vue.prototype 是 Vue 构造函数的原型对象,用于向所有 Vue 实例添加共享的方法和属性。

通过在 Vue.prototype 上定义方法或属性,可以确保这些方法或属性在所有 Vue 实例中都是可用的。

用法:

添加全局方法:Vue.prototype.$myMethod = function() { ... };

添加全局属性:Vue.prototype.$myProperty = 'some value';

为什么要以 $ 符号开头?

这是 Vue 中的一个简单约定,为了避免和组件中定义的数据、方法、计算属性产生冲突。

Vue.prototype 加一个变量,只是给每个组件加了一个属性,这个属性的值并不具有全局性。

例如:

// main.js Vue.prototype.$test = "测试" //test1.vue mounted() { this.$test = "哈哈" console.log(this.$test) // 哈哈 this.$router.push("/test2") } //test2.vue mounted() { console.log(this.$test) // 测试 }

如果要实现全局变量的功能,需要把属性变为引用类型

// main.js Vue.prototype.$test = { name: "测试" } //test1.vue mounted() { this.$test.name= "哈哈" console.log(this.$test) // 哈哈 this.$router.push("/test2") } //test2.vue mounted() { console.log(this.$test) // 哈哈 } created 和 mounted 区别

created:在模板渲染成 html 前调用,通常初始化某些属性值,然后再渲染成视图。

mounted:在模板渲染成 html 后调用,通常是初始化页面完成后,再对 html 的 dom 节点进行一些需要的操作。

例如 chart.js 的使用 var ctx = document.getElementById(id),这个就一定要等 html 渲染完成后才可以完成,这就要用 mounted

computed 和 watch 区别

computed 计算属性:

只有当计算属性所依赖的响应式数据发生变化时,计算属性才会重新求值。如果多次访问计算属性,但依赖项没有变化,那么它将返回之前的计算结果,而不是重新计算。

在模板中可以直接使用计算属性

计算属性总是返回一个值

<div>{{test}}</div> data() { return { name: '张三', age: 10 } }, computed: { test() { return `姓名${this.name},年龄${this.age}`; } }

watch 监听器:

watch 中的回调函数可以执行异步操作,而 computed 则不可以。

没有缓存,每次触发时重新计算

接收两个参数(newValue, oldValue)

data() { return { user: { name: "张三", age: 10, }, }; }, watch: { user: { handler(newVal, oldVal) { console.log("用户信息发生变化:", newVal); }, deep: true, // 深度监听 immediate: true, // 立即触发 }, }

总结:

当多个属性通过计算影响一个属性的时候,建议用 computed

当一个值发生变化之后,会引起一系列的操作,这种情况就适合用 watch

computed 如何传参 <div :title="text('123')"></div> computed: { text(){ return function (params) { console.log(params) // 123 return params } } } 执行顺序 父子组件生命周期执行顺序

页面初始化时:

父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted-> 父 mounted

页面销毁时:

父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed-> 父 destroyed

页面跳转的生命周期执行顺序

旧页面跳转到新页面:

新页面 created > 新 beforeMount > 旧 beforeDestroy > 旧 destroyed > 新 mounted

computed 、watch、created 、mounted 的先后顺序

immediate [ɪˈmiːdiət] 为 false 时: created => computed => mounted => watch

immediate 为 true 时: watch =>created=> computed => mounted

$nextTick 原理

Vue 采用异步更新策略,当你修改响应式数据时,Vue 不会立即更新 DOM,而是将这些 DOM 更新操作放入一个队列中,等到下一个事件循环时,再批量执行这些更新操作。$nextTick 的作用就是将回调函数(即传递给 $nextTick 的函数)添加到这个队列的末尾,确保在 DOM 更新完成后再执行回调。

批量的意思是指:当有多个 DOM 更新操作时,Vue 不会立即逐个地去更新 DOM,而是将这些更新操作收集起来,在下次事件循环时一次性处理,而不是循环依次更新。

在 Vue 里,数据是响应式的。当一个响应式数据发生变化时,与之绑定的 DOM 就需要更新。要是每次数据变化都立即更新 DOM,会带来大量的重排和重绘操作,严重影响性能。重排和重绘是浏览器渲染页面时开销较大的操作,频繁进行会导致页面卡顿。

JavaScript 的事件循环机制

实现细节

Vue 会根据不同的环境,选择不同的异步执行方法,优先顺序如下:

Promise.then:现代浏览器支持的异步方法,优先使用。

MutationObserver:用于监听 DOM 变化的 API,在不支持 Promise 的环境中使用。

setImmediate:IE 浏览器和 Node.js 环境支持的异步方法。

setTimeout:作为最后的兜底方案,所有环境都支持。

使用场景

当你修改了数据后,需要立即操作更新后的 DOM 元素时,必须使用 $nextTick。

当你修改了数据后,需要立即获取更新后的组件状态(如计算属性、子组件的状态等),必须使用 $nextTick。

当你使用 v-if 或 v-for 动态渲染 DOM 元素后,需要立即操作这些元素时,必须使用 $nextTick。

当你在 watch 监听器中监听数据变化,并需要操作 DOM 时,必须使用 $nextTick 确保 DOM 已经更新。

总结起来就是在操作执行完成时需要理解操作 DOM 或子组件的,都要使用 $nextTick

@click.native

@click:主要用于监听 Vue 组件内部自定义的点击事件,适用于普通 HTML 元素和自定义组件。当用于自定义组件时,需要在组件内部通过 $emit 触发自定义的 click 事件,外部才能监听到。

@click.native:用于监听原生 DOM 元素的点击事件,即使是在自定义组件上使用,它监听的也是组件根元素的原生点击事件,而不是自定义事件。对于普通 HTML 元素,@click.native 和 @click 效果相同。

意思就是:如果你想在组件内的子组件上绑定一个点击事件,如果你使用的是 @click,如果你的子组件内部没有通过 $emit 触发自定义的 click 事件,那么 @click 无效。

例如:

<!--子组件--> <template> <div class="children"> <div class="my-class">子组件内容</div> </div> </template> <!--父组件--> <template> <div id="app"> <Children @click="handleClick"></Children> </div> </template> <script> import Children from "./Children.vue"; export default { components: { Children }, methods: { handleClick(e) { console.log(e); } } } </script>

此时 handleClick 是不会触发的,除非把子组件改成:

<template> <div class="children" @click="handleChildClick"> <div class="my-class">子组件内容</div> </div> </template> <script> export default { methods: { handleChildClick() { // 通过 $emit 触发父组件的 click 事件 this.$emit('click', '这是子组件触发的事件'); } } } </script>

但是如果你使用的是 @click.native 就不会有这个问题,因为它监听的是组件根元素的原生点击事件,而不是自定义事件。

再举个例子加深理解:

<el-card @click="handleClick1"></el-card> <el-card @click.native="handleClick2"></el-card> <script> methods: { handleClick1() { // 不会触发,因为 element-ui 的 el-card 组件没有定义 click 事件 }, handleClick2() { // 可以触发,因为是原生点击事件 } } </script>

你以为你已经掌握了全部吗?不,对于 Vue2 来说,确实是这样,但是对于 Vue3 来说,情况又有不同:

<!--子组件--> <template> <div class="children"> <div class="my-class">子组件内容</div> </div> </template> <!--父组件--> <template> <div id="app"> <Children @click="handleClick"></Children> </div> </template> <script setup> import Children from "./Children.vue"; const handleClick = e => { console.log(e); } </script>

发现也可以触发!为什么?

因为在 Vue 3 里,当父组件向子组件传递事件监听器时,如果子组件没有通过 emits 选项显式声明这些事件,那么这些事件监听器会被自动绑定到子组件的根元素上,这就是所谓的事件继承特性。

上面的例子就是因为子组件没有显式定义 emits 选项,父组件中的 @click 会被当作原生 DOM 事件绑定到子组件的根元素上。

@click.stop

阻止事件冒泡,即阻止事件向父级元素传递。

<div id="app"> <div v-on:click="parentClick"> <button v-on:click="childClick">阻止单击事件继续传递</button> </div> </div> <script> var app = new Vue({ el: "#app", data: { name: "Vue.js" }, methods: { parentClick: function () { alert("parentClick"); }, childClick: function () { alert("childClick"); }, } }); </script> <!--点击按钮,将会先弹出 "childClick", 再弹出 "parentClick"--> <div id="app"> <div v-on:click="parentClick"> <button v-on:click.stop="childClick">阻止单击事件继续传播</button> </div> </div> <script> var app = new Vue({ el: "#app", data: { name: "Vue.js" }, methods: { parentClick: function () { alert("parentClick"); }, childClick: function () { alert("childClick"); }, } }); </script> <!--点击按钮,只弹出 "childClick"-->

使用场景:点击子元素区域的的时候,不触发父级元素的点击事件。

@click.prevent

阻止事件的默认行为。

<!--点击链接时,阻止 a 标签跳转,仅执行函数 test4--> <a href="http:// .baidu " @click.prevent="test4">百度一下</a> <!--点击按钮时,阻止表单默认提交行为 action,仅执行函数 test5--> <form action="/xxx" @submit.prevent="test5"> <input type="submit" value="注册"> </form> @keyup.enter

按键修饰符。

<!--按下键盘 enter 健时,执行方法 test7--> <input type="text" @keyup.enter="test7"> directive 自定义指令

什么是自定义指令?

vue2.js 官方链接

vue3.js 官方链接

Vue 的自定义指令允许你对 DOM 进行低级别的操作。自定义指令通过 Vue 的全局方法 Vue.directive() 或组件的 directives 选项来注册。注册之后,你可以在 Vue 模板中通过 v- 前缀加上指令名来使用这个指令。

低级别操作是指直接通过 JavaScript 对 DOM 树中节点的进行增删改查,以及对节点的属性、样式、事件等进行操作。

全局自定义指令

Vue2:

// 在main.js中 // 注册一个全局自定义指令 `v-focus` Vue.directive('focus', { // 当被绑定的元素插入到 DOM 中时…… inserted: function (el) { // 聚焦元素 el.focus() } })

Vue3:

const app = createApp({}) // 使 v-focus 在所有组件中都可用 app.directive('focus', { /* ... */ }) 局部组件指令

Vue2:

created() {}, directives: { focus: { // 指令的定义 inserted: function (el) { el.focus() } } }

Vue3:

在 <script setup> 中,任何以 v 开头的驼峰式命名的变量都可以被用作一个自定义指令。

<template> <input v-focus /> </template> <script setup> // 在模板中启用 v-focus const vFocus = { mounted: (el) => el.focus() } </script>

或:

<template> <input v-focus /> </template> <script> export default { setup() { /*...*/ }, directives: { // 在模板中启用 v-focus focus: { /* ... */ } } } </script> 钩子函数

Vue2:

函数描述bind只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。inserted被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。update所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。componentUpdated指令所在组件的 VNode 及其子 VNode 全部更新后调用。unbind只调用一次,指令与元素解绑时调用。

Vue3:

函数描述created在绑定元素的 attribute 前或事件监听器应用前调用beforeMount在元素被插入到 DOM 前调用mounted在绑定元素的父组件及他自己的所有子节点都挂载完成后调用beforeUpdate绑定元素的父组件更新前调用updated在绑定元素的父组件及他自己的所有子节点都更新后调用beforeUnmount绑定元素的父组件卸载前调用unmounted绑定元素的父组件卸载后调用

钩子函数参数:

el:指令绑定到的元素。这可以用于直接操作 DOM。

binding:一个对象,包含指令值(value)、旧的指令值(oldValue)、参数(arg)等等

vnode

oldVnode

原理

当 Vue 组件的模板被编译时,Vue 会识别出模板中所有的 v- 前缀指令,这会包含 Vue 的内置指令和自定义指令。对于自定义指令,Vue 会根据注册的钩子函数生成相应的指令对象,这些对象包含了指令的名称、参数、表达式等信息。然后会将这些信息存储在之后生成的 VNode 对象中。最后在生成真实 DOM 的时候,Vue 会在相应的 DOM 元素上调用自定义指令的钩子函数。

v-model 的原理

v-model 是 Vue.js 中的一个语法糖,它内部实际上是使用了 v-bind (:)和 v-on (@)两个指令。

v-bind:实现数据到 DOM 的绑定

修改 data 中的属性值会体现在页面上

原理主要是基于 Vue 的响应式系统,当数据变化时触发相应的 setter,来触发渲染过程,以更新 DOM

v-on:实现 DOM 到数据的绑定

在页面上修改的属性值会作用到 data 中

原理是监听 DOM 元素上指定的事件,当事件发生时,执行相应的 JavaScript 代码或方法,从而更新属性值。

Vue 中使用 CSS 变量 <template> <!-- 方式一: 在根或父或当前节点使用 --varName 来定义变量 然后当前节点使用 var(--varName) 来使用变量 --> <div class="parent"> <!-- 方式二:可以在 data 中定义变量,在 template 中直接使用 --> <span :style="{ color: myColor1 }">parent</span> <!-- 方式三:data 中定义变量,css 中使用 v-bind 绑定变量 --> <div class="child1">child1</div> <!-- 方式四:通过 style module --> <div :class="classModule.child2">child2</div> <!-- 方式五:不好理解,不知道这样写的意义在哪里 --> <div class="child3" :style="{ '--my-color': myColor3 }">child3</div> </div> </template> <script lang="ts" setup> const myColor1 = 'red'; // 方式二 const myColor2 = 'yellow'; // 方式三 const myColor3 = 'blue'; // 方式五 </script> <style> .parent { /* 方式一 */ --parent-bg-color: skyblue; /* 方式一 */ background-color: var(--parent-bg-color); } .child1 { /* 方式三 */ color: v-bind(myColor2); } .child3 { /* 方式五 */ color: var(--my-color); } </style> <style module="classModule"> /* 方式四 */ .child2 { color: green; } </style> Vue 项目结构 文件名注释build项目构建(webpack)相关代码config项目配置dev.env.js开发环境变量prod.env.js生产环境变量test.env.js测试环境变量index.js项目配置文件package.jsonnpm 包配置文件,npm 脚本src日常开发主要在这个文件夹asset放置静态资源,会被 webpack 构建main.js项目入口文件components公共组件App.vue根组件pages页面文件api接口文件router路由文件static纯静态资源,不会被 webpack 构建index.html首页入口文件README.md项目的说明文档dist打包之后的文件
标签:

Vue系列之:基础知识由讯客互联电脑硬件栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“Vue系列之:基础知识