vue3的生命周期钩子 服务端渲染概念 vue3的组建通信 vue3的ref属性应用 vue3(vuex和pinia的使用)
vue3的生命周期钩子
常规
要点、总结
1. 实例期(最特殊)
Vue3 用 setup 函数代替了 Vue2 中实例化期的两个钩子函数 beforeCreate 和 created,而 setup 原本函数的形式又被< script setup> 的语法糖取代。所以在 Vue3 的语法中,写在 script 标签中的语句都相当于是写在组件实例化期的钩子函数中。包括其他挂载期、更新期、卸载期的生命周期钩子也被定义在实例化期的钩子中。(所以,直接写在 script 标签中的函数执行时间要早于套上一层其他生命周期钩子的代码,因为他等于是直接写到了实例化期的函数里面,会在组件实例化的时候就执行,而实例化期钩子,是组件最早执行的钩子,包括后期想要访问 DOM 和子组件实例,都不能直接写在 script 标签之间,也是因为会导致代码执行时间过早,组件还未挂载,是访问不到 DOM 节点和子组件实例的)
2. 挂载期
语法:
onBeforeMount ( ( ) => { 需要在挂载前执行的代码} )
onMounted ( ( ) => { 需要在挂载完成时执行的代码} ))
3. 更新期
语法:
onBeforeUpdate ( ( ) => { 需要在更新期前执行的代码} )
onUpdated ( ( ) => { 需要在更新期完成时执行的代码} )
4. 卸载期
语法:
onBeforeUnmount ( ( ) => { 需要在卸载期前执行的代码} )
onUnmounted ( ( ) => { 需要在卸载期完成时执行的代码} )
5. 特点与变化
1. 都是 Vue2 的生命周期钩子函数加上 on 前缀
2. 都是从选项变成了函数写法:接受一个回调函数,需要在相应时期执行的代码,写在回调函数里面
代码演示
< script setup> -- -- -- - 实例化期钩子在这里,vue2中的实例化期 的方法 beforeCreate 和 created 被 setup 代替了
import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, ref, provide } from 'vue' ; -- -- -- -- -- -- -- Vue3中所有用到的钩子函数在使用前都要引入,除了setup语法糖
生命周期方法
console. log ( '组件实例化' ) ; -- -- -- -- -- -- 这里的加了setup的script标签就相当于是实例化期的函数体,所有直接写在标签里面的代码都会在实例化期执行
挂载期-- -- -- -- -- 可以在这里发送网络请求, 创建定时器, 监听事件
onBeforeMount ( ( ) => {
console. log ( 'onBeforeMount 挂载前' ) ;
} )
onMounted ( ( ) => {
console. log ( 'onMounted 挂载完成' ) ;
更新期
onBeforeUpdate ( ( ) => {
console. log ( 'onBeforeUpdate 更新前' ) ;
} )
onUpdated ( ( ) => {
console. log ( 'onUpdated 更新完成' ) ;
} )
卸载期-- -- -- -- - 可以在这里取消发送网络请求, 销毁定时器, 取消事件监听
onBeforeUnmount ( ( ) => {
console. log ( 'onBeforeUnmount 卸载前' ) ;
} )
onUnmounted ( ( ) => {
console. log ( 'onUnmounted 卸载完成' ) ;
} )
< / script>
补充
要点、总结
除了常规的生命周期钩子函数之外,还有一些不太常用的,这一类主记功能与特点
1. onErrorCaptured ( ) :
1. 功能:子组件内部错误捕获
2. onRenderTracked ( ) :
1. 功能:调试钩子,当组件渲染过程中追踪到响应式依赖时调用(含义为建立起了组件和响应式数据的依赖关系,也就是当响应式数据发生变动,会将组件自动更新,比较类似于自动的双向绑定)
2. 这个钩子仅在开发模式下可用,且在服务器端渲染期间不会被调用。
3. onRenderTriggered ( ) :
1. 功能:调试钩子,当响应式依赖的变更触发了组件渲染时调用
2. 这个钩子仅在开发模式下可用,且在服务器端渲染期间不会被调用。
4. onActivated ( ) :
1. 功能:组件激活钩子(若组件实例是 < KeepAlive> 缓存树的一部分,当组件被插入到 DOM 中时调用。)
2. 这个钩子在服务器端渲染期间不会被调用。
5. onDeactivated ( ) :
1. 功能:组件缓存钩子(若组件实例是 < KeepAlive> 缓存树的一部分,当组件从 DOM 中被移除时调用。)
2. 这个钩子在服务器端渲染期间不会被调用。
6. onServerPrefetch():预加载函数,
服务端渲染概念
要点总结
服务端渲染和客户端渲染都是 Web 应用中常用的渲染方式。
服务端渲染(SSR )
概念:服务端渲染是指服务器端将数据和模板合并后生成 HTML 代码,然后将 HTML 代码响应给客户端浏览器,客户端浏览器只需展示 HTML 页面即可。
优点如下:
1. 对搜索引擎友好:由于搜索引擎爬虫只能抓取 HTML 页面,因此服务端渲染能够让搜索引擎更好地抓取页面内容,提升 SEO 效果。
2. 首屏加载快:服务端渲染在服务器端就已经将页面渲染完成,客户端只需展示 HTML 页面,因此首屏加载速度快,用户体验好。
3. 代码安全:服务端渲染只需要将 HTML 代码响应给客户端,不会将 JavaScript 代码暴露给客户端,因此安全性高。
缺点如下:
1. 页面切换慢:由于每次页面切换都需要向服务器请求 HTML 页面,因此页面切换速度较慢,用户体验较差。
2. 开发成本高:服务端渲染需要在服务器端编写渲染逻辑,开发成本较高。
客户端渲染(CSR )
概念:客户端渲染是指客户端浏览器在接收到 HTML 代码后,使用 JavaScript 代码将数据和模板合并后生成 HTML 页面,然后展示给用户。
优点如下:
1. 页面切换快:客户端渲染只需要向服务器请求数据,然后使用 JavaScript 代码在客户端生成 HTML 页面,因此页面切换速度快,用户体验好。
2. 开发成本低:客户端渲染只需要在客户端编写渲染逻辑,开发成本较低。
缺点如下:
1. 对搜索引擎不友好:由于客户端渲染是在客户端生成 HTML 页面,因此搜索引擎爬虫无法获取到完整的 HTML 页面,影响 SEO 效果。
2. 首屏加载慢:客户端渲染需要先加载 JavaScript 代码,然后再进行数据和模板的合并,生成 HTML 页面,因此首屏加载速度较慢,用户体验较差。
3. 安全性低:客户端渲染需要将 JavaScript 代码发送给客户端浏览器,容易被恶意攻击者利用,安全性较低。
综上所述,服务端渲染和客户端渲染各有优缺点,应根据具体的业务需求和场景选择合适的渲染方式。
vue3的组建通信
要点总结
1. 父对子传值
1. 父组件在子组件渲染标签上面添加自定义属性传值
1. 语法:< 子组件 : 自定义属性名= '自定义属性值' / >
2. 子组件引入并调用 defineProps()进行收值(收到的值需要设置类型)
1. 语法:
import { defineProps } from 'vue' ;
let { 值1 , 值2 , ... . . } = defineProps ( {
值1 : 数据类型,
值2 : 数据类型
... ...
} )
2. 子对父传值
1. 父组件在子组件身上绑定自定义属性并设置回调函数带形参收值
1. 语法:< 子组件 @自定义事件类型= '回调函数' / >
2. 子组件引入并调用 defineEmits()方法获取触发事件的对象,然后调用该对象触发事件并传值
1. 语法:
import { defineEmits } from 'vue' ;
Let emit = defineEmits ( [ '自定义事件名' , ... ... . ] ) ;
emit ( '自定义事件名' , 要传的值) ;
3. 跨层级组件传值
1. 外层传值组件引入并调用 provide()方法进行传值
import { provide } from 'vue' ;
provide ( '传出的值名称' , 属性值) ;
2. 里层收值组件引入并调用 inject()方法进行收值
import { inject } from 'vue' ;
let 变量名 = inject ( '接收的值名称' ) ;
4. 兄弟组件传值
与 vue2 相同,还是引入 mitt 插件创建实例化对象,然后分别引入到传值方和收值方,在传值方用 mitt 实例调用其 emit 方法触发自定义事件并传值,在收值方用 mitt 实例调用其 emit 方法监听自定义事件被触发并收值
父对子传值
父组件 :
< script setup>
父组件向子组件传值 : 父组件在子组件身上通过自定义属性传值, 子组件内部通过defineProps接收值
< / script>
子组件
< template>
< div class = "child" > -- -- -- -- -- 收到传来的值后和普通数据一样应用即可(使用解构会使收到的值失去响应式),如果是使用单个变量接收对象,则需要使用对象打点调用(这样才能保留响应式)
< h3> child 组件 - { { count} } - { { str} } < button @click= "haneleClick" > 传值< / button> < / h3>
< / div>
< / template>
< script setup>
import { defineProps } from 'vue' ;
var { count, str } = defineProps ( { -- -- -- -- -- -- -- -- -- - 通过defineProps接收值(此处解构写法是不正确的,会使收到的值失去响应式特性,且子组件收到的值是只读的,无法修改,也不能用torefs套defineProps,解构的话无法保留响应式特性)
count : Number,
str : String
} )
var json = defineProps ( { -- -- -- -- -- - 这种写法是正确的,不使用解构,保证数据响应式特性,但使用时要注意使用对象名打点调用:json. count, json. str;
count : Number,
str : String
} )
< / script>
写法::::::::::::::::::::::::::::::
父组件:::
< template>
< ChildComponent : message= "message" / >
< / template>
< script>
import ChildComponent from './ChildComponent.vue' ;
export default {
components : {
ChildComponent,
} ,
data ( ) {
return {
message : 'Hello World' ,
} ;
} ,
} ;
< / script>
子组件:::
< template>
< div> { { message } } < / div>
< / template>
< script>
export default {
props : {
message : String,
} ,
} ;
< / script>
在 : message= "message" 中, : 表示绑定的意思,message 是子组件中定义的 Props 名称,而第二个 message 则是父组件中的数据名称。这种写法的含义是将父组件中的 message 数据绑定到子组件的 message Props 上。
如果你使用 v- bind ,那么语法是这样的:v- bind: message= "message" ,效果和 : message= "message" 是一样的,都是将父组件中的 message 数据绑定到子组件的 message Props 上。
子对父传值
子组件
< script setup>
import { defineProps, defineEmits, toRef, toRefs } from 'vue' ;
var emit = defineEmits ( [ 'msg' ] ) ; -- -- -- -- -- -- - 获取emit, 以数组形式传入自定义事件作为参数
var haneleClick = ( ) => {
emit ( 'msg' , 888 ) ; -- -- -- -- - 调用emit触发事件并传值,第一个参数是自定义事件,第二个参数是传出的值
}
< / script>
父组件
< template>
< div class = "app" >
< Child : count= "count" str= "vue3 composition api" @msg= "getMsg" / > -- -- -- - 父组件在子组件渲染标签上绑定自定义事件并设置带参数的回调函数,此处回调函数注意不能带括号
< / div>
< / template>
< script setup>
import Child from './components/Child.vue' ;
var getMsg = ( data ) => { -- -- -- -- -- - 将回调函数在此处定义,等到子组件中事件被触发,则此处将收到的值打印出来
console. log ( '接收到来自子组件的数据:' , data) ;
}
< / script>
代码演示:::::::::::::::::::::::::::::::::
子组件:::
< template>
< button @click= "notifyParent" > Click me< / button>
< / template>
< script>
export default {
methods : {
notifyParent ( ) {
this . $emit ( 'child-event' , 'Data from child component' ) ;
} ,
} ,
} ;
< / script>
父组件:::
< template>
< div>
< ChildComponent @child- event= "handleChildData" / >
< p> Data received from child: { { receivedData } } < / p>
< / div>
< / template>
< script>
import ChildComponent from './ChildComponent.vue' ;
export default {
components : {
ChildComponent,
} ,
data ( ) {
return {
receivedData : '' ,
} ;
} ,
methods : {
handleChildData ( data ) {
this . receivedData = data;
} ,
} ,
} ;
< / script>
首先,子组件需要使用 $emit 方法来触发一个自定义事件,并将需要传递的数据作为参数传递给该事件
当按钮被点击时,子组件会触发一个名为 child- event 的自定义事件,并且将 'Data from child component' 作为参数传递给该事件。然后,在父组件中,你可以通过监听这个自定义事件来接收子组件传递过来的数据。
父组件中,我们通过 @child- event= "handleChildData" 监听了子组件触发的 child- event 自定义事件,并在 handleChildData 方法中接收了子组件传递过来的数据。
跨层级组件传值
外层组件 :
< script setup>
import { provide } from 'vue' ; -- -- -- -- - 导入provide方法,不是选项
provide ( 'money' , 6666 ) ; -- -- -- - 调用provide ( ) 规定传出值的名字和数值
< / script>
内层组件
< template>
< div class = "c" >
< h3> c组件 - { { money} } < / h3> -- -- -- -- - 收到值后应用格式和本组件的响应式变量相同
< / div>
< / template>
< script setup>
import { inject } from "vue" ; -- -- -- -- -- - 导入inject ( ) 方法
var money = inject ( 'money' ) ; -- -- -- -- -- 调用inject ( ) 方法收值,参数为外层组件规定的传出值名称
< / script>
代码演示::::::::::::::::::::::::::::
provide 和 inject 是一对用于跨层级组件传值的 API 。provide 允许父组件提供数据,而 inject 允许子组件注入这些数据,无论这些组件之间的层级有多深。
1. 在父组件中使用 provide 提供数据:
< template>
< div>
< ChildComponent / >
< / div>
< / template>
< script>
export default {
data ( ) {
return {
message : 'Data from parent component' ,
} ;
} ,
provide ( ) {
return {
message : this . message,
} ;
} ,
} ;
< / script>
2. 在子组件中使用 inject 注入数据:
< template>
< div>
< p> Data received from parent: { { injectedMessage } } < / p>
< / div>
< / template>
< script>
export default {
inject : [ 'message' ] ,
computed : {
injectedMessage ( ) {
return this . message;
} ,
} ,
} ;
< / script>
vue3的ref属性应用
获取DOM节点
要点总结
通过 ref 属性可以获取并操作对应的 DOM 节点
步骤:
为指定的 DOM 节点添加 ref 属性:< div ref= '属性值' > < / div>
在 js 中通过 ref 方法先定义响应式变量赋值为 null :let 与ref属性值相同的变量名 = ref ( null ) ;
在生命周期钩子中获取 DOM 节点:与ref属性值相同的变量名. value即可获取到 DOM 节点。(此处要注意,不能直接写在 script 标签内,添加了 setup 语法糖的 script 标签相当于当前组件的实例化期钩子,而在组件实例化期的时候,还没有完成挂载,是拿不到 DOM 节点的)
代码演示
< template>
< div class = "app" >
< input ref= "inputbox" type= "text" placeholder= "搜索商品" >
< / div>
< / template>
< script setup>
import { onMounted, ref } from 'vue' ; -- -- -- -- -- 导入获取DOM 节点需要的相关方法,生命周期钩子也需要导入再使用
var inputbox = ref ( null ) ; -- -- -- -- -- - 先使用ref ( ) 方法定义一个响应式变量,赋值为空,变量名要和对应DOM 节点的ref属性值一致
onMounted ( ( ) => {
console. log ( inputbox. value ) ; -- -- -- -- -- - 此处要注意,不能直接写在script标签内,添加了setup语法糖的script标签相当于当前组件的实例化期钩子,而在组件实例化期的时候,还没有完成挂载,是拿不到DOM 节点的
} )
< / script>
用途:::
1. 访问元素:通过在 < input> 元素上设置 ref= "inputbox" ,可以在 JavaScript 代码中使用 this . $refs. inputbox 来访问该元素。这使得我们可以直接操作该元素,例如获取输入框的值、修改样式、添加事件监听器等。
2. 表单操作:当需要在表单提交时获取输入框的值时,可以使用 ref 来引用输入框元素,并在提交表单时通过 this . $refs. inputbox. value 来获取输入框的值。
3. 聚焦元素:通过在组件的 mounted 钩子函数中使用 this . $refs. inputbox. focus ( ) ,可以在组件挂载后自动将焦点设置到输入框上,方便用户直接开始输入。
4. 执行元素方法:某些 HTML 元素(如视频播放器、音频播放器等)具有自己的方法和属性,可以通过 this . $refs. inputbox 来访问和调用这些方法。例如,可以使用 this . $refs. inputbox. play ( ) 来播放视频。
获取组件实例
要点总结
步骤:::
1. 先在子组件身上定义一个 ref 属性
2. 在组件实例化时定义一个 ref 变量,变量的名称必须和标签身上的 ref 属性值相同
3. 然后在组件挂载完成后就可以通过 ref 变量获取子组件的实例 ( 注意: 当子组件实例内的js代码以组合式API 的形式书写时,子组件中所有方法和状态变量默认是子组件私有化的,此时父组件内获取到的子组件实例无法调用子组件中的方法,所以子组件内部必须使用 defineExpose 暴露对应的方法, 父组件才可以获取子组件的实例并调用子组件的方法 )
代码演示
父组件
< template>
< div class = "app" >
< Dialog ref= "dialogcom" / > -- -- -- - 先在子组件身上定义一个ref属性
< / div>
< / template>
< script setup>
import Dialog from './components/Dialog.vue' ;
import { onMounted, ref } from 'vue' ;
var dialogcom = ref ( null ) ; -- -- 在组件实例化时定义一个ref变量 变量的名称必须和标签身上的ref属性值相同
onMounted ( ( ) => {
console. log ( inputbox. value ) ; -- -- -- -- 然后在组件挂载完成后就可以通过ref变量获取子组件的实例
} )
var handleClick2 = ( ) => {
dialogcom. value. show ( ) ; -- -- -- -- -- -- -- -- -- 让子组件这个弹框组件显示出来 ( 注意: 子组件内部必须使用defineExpose暴露对应的方法, 父组件才可以获取子组件的实例并调用子组件的方法)
}
< / script>
子组件 :
< script setup> -- -- - 在子组件中, 所有的状态变量, 方法 都被私有化了, 其他组件想要调用子组件的方法, 子组件内部必须暴露对应的方法.
import { ref, defineExpose } from "vue" ;
var isvisiable = ref ( false ) ;
var show = ( ) => {
console. log ( 'show' ) ;
isvisiable. value = true ;
}
var hide = ( ) => {
isvisiable. value = false ;
}
defineExpose ( { -- -- -- -- -- -- 对外暴露一些方法, 只有进行暴露操作,在父组件内获取到子组件实例才能调用这些方法和状态变量
show
} )
< / script>
vue3(vuex和pinia的使用)
vue3中的vuex
要点总结
需要在Vue3的组件之中使用vuex的实例,需要引入vuex提供的组合式API :useStore ( ) 方法
注意:mapState,mapMutation的方法在vue3是被弃用的,因为其映射功能会将简写方式转化成vuex获取vuex实例的标准语法然后再调用方法或获取状态,中间会用到在vue3中已经不存在的this ,这个过程中会出现报错从而无法运行
代码演示
< template>
< div class = "tasklist" >
< ! -- 动态渲染任务列表 ( 从vuex中获取 ) -- >
< div class = "task" v- for = "(item,index) in $store.state.tl.tasklist" : key= "index" > -- -- 组件模板中的$store可以是省略了this 的this . $store, 在script标签外部,不受setup影响
< div class = "left" >
< input type= "checkbox" : checked= "item.status" @click= "handleClick(item.id)" >
< span> { { item. name} } < / span>
< / div>
< span @click= "handleDelete(item.id)" > 删除< / span>
< / div>
< / div>
< / template>
< script setup>
import { useStore } from "vuex" ; -- -- -- -- -- -- -- -- - 组合式API 使用前要先导入
获取$store实例
var $store = useStore ( ) -- -- -- -- -- - 此处$store只是随意地变量命名,用于承接vuex实例,不是固定写法
var handleClick = ( id ) => {
触发同步方法 ( 调用同步方法) changeTaskStatus
$store. commit ( 'tl/changeTaskStatus' , id) ;
} ;
var handleDelete = ( id ) => {
触发同步方法 ( 调用同步方法) deleteTask
$store. commit ( 'tl/deleteTask' , id) ;
}
export default {
methods : {
handleClick ( id ) {
触发同步方法 ( 调用同步方法) changeTaskStatus
this . $store. commit ( 'tl/changeTaskStatus' , id) ;
} ,
handleDelete ( id ) {
触发同步方法 ( 调用同步方法) deleteTask
this . $store. commit ( 'tl/deleteTask' , id) ;
}
}
}
< / script>
vue3中的pinia
要点总结
defineStore ( '模块名' , ( ) => { 数据与方法; return 定义好的数据与方法; } ) 定义一个数据模块 ( 负责管理一部分数据 )
参数一 : 模块名
参数二 : 回调函数, 回调函数当中的代码决定了该模块管理了哪些数据, 以及如何管理这些数据
代码演示
模块文件. js
import { defineStore } from 'pinia' ;
import { computed, ref } from 'vue' ;
export var useUserinfoStore = defineStore ( 'userinfo' , ( ) => { -- -- -- -- -- - 定义一个新的模块 并导出,该模块的模块名是useUserinfoStore ,而不是'userinfo' ;
var userinfo = ref ( { } ) ; -- -- -- -- -- 定义状态
var token = computed ( ( ) => { -- -- -- -- -- - 定义计算方法
return userinfo. token;
} )
var save_userinfo = ( user ) => { -- -- -- -- -- - 定义方法
userinfo. value = user;
}
return { -- -- -- -- 将定义好的数据与方法对外暴露
userinfo,
token,
save_userinfo
}
} )
引用组件. vue
< script setup>
import { ref } from "vue" ;
import { usetlStore} from '../stores/tl' ; -- -- -- -- -- 导入tl模块
var tl = usetlStore ( ) -- -- -- - 实例化tl模块
var taskname = ref ( '' ) ;
var handleAdd = ( ) => { -- -- -- -- -- - 定义方法
tl. addTask ( taskname. value ) ; -- -- -- -- -- 新增任务
taskname. value = '' ; -- -- -- -- -- 清空输入框的值
}
< / script>