🫵Vue脚手架
💥安装脚手架工具Vue2(Vue CLI)
- 安装 CLI
npm install -g @vue/cli
# OR
yarn global add @vue/cli
- 升级. 如需升级全局的 Vue CLI 包,请运行:
npm update -g @vue/cli
或者
yarn global upgrade --latest @vue/cli
- 查看 CLI 的版本号
vue --version
vue -V
注意-V :V 是大写
如果已经安装的版本是 2.x 或 2.x 以下的版本,需要卸载后重新安装最新的版本。
# OR
npm i yarn -g
- 删除存档
💥vue2创建项目
- 终端运行以下命令来创建一个新项目 vue-demo :(命名不能有大写字母,不信你试试)
vue create vue-demo
- 选择项目预设 模式配置
Vue CLI v5.0.8
? Please pick a preset:
Default ([Vue 3] babel, eslint)
Default ([Vue 2] babel, eslint)
> Manually select features //自定义配置
- 选择插件,上下键控制,空格选择,回车下一步
Vue CLI v5.0.8
? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed)
(*) Babel //转码器,可以将ES6代码转为ES5代码,从而在现有环境执行。
( ) TypeScript //TypeScript是一个JavaScript(后缀.js)的超集(后缀.ts)包含并扩展了 JavaScript 的语法,需要被编译输出为 JavaScript在浏览器运行,目前较少人再用
( ) Progressive Web App (PWA) Support //渐进式Web应用程序
(*) Router // vue-router(vue路由)SPA应用的核心组件
(*) Vuex // vuex(vue的状态管理模式)
>(*) CSS Pre-processors // CSS 预处理器(如:less、sass)
( ) Linter / Formatter // 代码风格检查和格式化(如:ESlint)
( ) Unit Testing // 单元测试(unit tests)
( ) E2E Testing // e2e(end to end) 端到端测试
- 选择 Vue版本
Vue CLI v5.0.8
? Choose a version of Vue.js that you want to start the project with
3.x
> 2.x
- 对路由器使用历史模式? (需要在生产中为索引回退进行适当的服务器设置)
Vue CLI v5.0.8
? Use history mode for router? (Requires proper server setup for index fallback in production) (Y/n)
//Vue的路由有两种模式:
hash模式:默认: #/相对路径 —— 不需要服务器端支持就可使用。
history模式:/相对路径 —— 需要服务器端配置首页重定向机制!
- 选择样式预处理
Vue CLI v5.0.8
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): (Use arrow keys)
> Sass/SCSS (with dart-sass)
Less
Stylus
- 选择插件配置代码位置,babel,postcss,eslint这些配置文件放哪
Vue CLI v5.0.8
? Where do you prefer placing config for Babel, ESLint, etc.? (Use arrow keys)
❯ In dedicated config files // 独立文件放
In package.json // 放package.json里
- 是否保存以上配置
? Save this as a preset for future projects? (y/N)
- 配置别名
Save preset as: //配置别名,下次创建在 第二步 模式配置显示
- 安装完成
$ cd vue-demo //进入项目
$ npm run serve //启动项目
💥vue2项目目录
- node_module:用于存放我们项目的各种依赖
- public:用于存放静态文件,是网站的根目录
-
- favicon.ico:网站的小图标
-
- index.html:网站的首页,是一个模板文件,作用是生成项目的入口文件,webpack打包的js,css也会自动注入到该页面中。我们浏览器访问项目的时候就会默认打开生成好的index.html
- src:src下就是我们写代码的地方
-
- assets:用于存放各种静态文件,如图片
-
- components:用于存放我们的公共组件,如 header、footer等
-
- App.vue:主vue模块,引入其他模块,app.vue是项目的主组件,所有页面都是在app.vue下切换的
-
- main.js:入口文件,主要作用是初始化vue实例,同时可以在此文件中引用某些组件库或者全局挂在一些变量
-
- router.js:路由文件,这个里边可以理解为各个页面的地址路径,用于我们访问,同时可以直接在里边编写路由守卫
-
- store.js:主要用于项目里边的一些状态的保存
- gitignore:git上传需要忽略的文件格式
- babel.config.js:是一个工具链,主要用于在当前和较旧的浏览器或环境中将ECMAScript 2015+代码转换为JavaScript的向后兼容版本
- package.json:模块基本信息项目开发所需要模块,版本,项目名称
- package-lock.json:是在 npm install时候生成一份文件,用以记录当前状态下实际安装的各个npm package的具体来源和版本号
- vue.config.js:保存vue配置的文件,可以用于设置代理,打包配置等
💥webpack打包
npm run build
- 默认情况下,在构建整个组件树的过程中,因为组件和组件之间是通过模块化直接依赖的,那么webpack在打包时就会将组件模块打包到一起
- 随着项目的不断庞大,app.js文件的内容过大,会造成首屏的渲染速度变慢
💥webpack分包
- 对于一些不需要立即使用的组件,我们可以单独对它们进行拆分,拆分成一些小的代码块(js文件)
- 这些小的代码块(js文件)会在需要时从服务器加载下来,并运行代码,显示对应的内容
- 利用 异步import
- defineAsyncComponent 定义异步组件 写法一:工厂函数
- 写法二:对象,可以进行配置
💥安装脚手架工具Vue3(Vite)
- 注意:使用 Vite 需要 12.x 以上的 Node.js 版本。
npm init vue@latest
npm init vite@latest
🫵组件化开发
概念
- 我们将一个完整的页面 分成 多个组件
- 每个组件都用于实现页面的一个功能块;
- 而每一个组件又可以进行细分;
- 而组件本身又可以在多个地方进行复用;
🌀注册组件
💥全局组件
- 在任何其他的组件中都可以使用的组件
<div id="app">
<!--调用全局注册组件-->
<button-counter></button-counter>
<!--调用第二个注册组件-->
<lk-box></lk-box>
</div>
<script>
// 1.创建Vue的实例对象
const app = Vue.createApp({
data() {
return {
msg: '你好,Vue3!'
}
}
});
// 2.定义一个组件(全局组件)
app.component('button-counter', {
data() {
return {
count: 0,
count2: 0
}
},
template: `
<button @click="count++">你点击了{{count}}次</button>
`
})
// 定义第二个全局组件
app.component('lk-box', {
template: `
<button @click="count2--">你点击了{{count2}}次</button>
`
})
// 3. 挂载vue实例
app.mount('#app');
</script>
💥局部组件
- 只有在 注册的组件中 才能使用的组件
<div id="app">
<lk-count></lk-count>
<cc-count></cc-count>
</div>
<script>
// 注册一个局部组件
const Counter = {
data() {
return {
count: 0
}
},
template: `
<button @click="count++">你点击了{{count}}次</button>
`
}
// 注册第二个局部组件
const Box = {
template: `
<div style="width: 200px;height: 200px;background-color:pink;">
盒子组件
</div>
`
}
// 创建Vue的实例对象
const app = Vue.createApp({
data(){
return {
msg: '你好,Vue3!'
}
},
// 组件选项
components: {
'lk-count': Counter,
'cc-count': Box
}
});
// 挂载vue实例
app.mount('#app');
</script>
💥总结
- 全局组件:在整个Vue实例中都可以被调用,若想要全局组件之间相互使用,只需将想使用全局组件的名称 直接写入 template 中
- 局部组件:只能在当前组件中被使用,若想在其他组件中使用,必须使用 components 将其挂载在想使用的组件中,(components选项对应的是一个对象,对象中的键值对是 组件的名称: 组件对象),然后再如全局组件那样向模板template写入名称标签调用
🌀组件的命名规范
💥kebab-case(短横线分割符)
- 当使用 kebab-case (短横线分隔命名) 定义一个组件时,你也必须在引用这个自定义元素时使用 kebab-case,例如
<my-component-name>
<template>
<div class='app'>
<my-component-name></my-component-name>
</div>
</template>
<script>
app.component('my-component-name',{
template: ``
data(){
return {
}
},
})
<script>
💥PascalCase(驼峰标识符)
当使用 PascalCase (首字母大写命名) 定义一个组件时, <my-component-name></my-component-name>
和 <MyComponentName></MyComponentName>
都可以
但是我们直接在DOM(即非字符串的模板)中使用时只有短横线法是有效的。在后面CLI调用两种方法都可以
<template>
<div class='app'>
<my-component-name></my-component-name>
<MyComponentName></MyComponentName>
</div>
</template>
<script>
app.component('MyComponentName',{
template: ``
data(){
return {
}
},
})
<script>
🌀组件标签化
template 模块写法不够清晰,如果我们能将其中的HTML分离出来写,然后挂载到对应的组件上,必然结构会变得非常清晰
Vue 提供了两种方案来定义HTML模板内容
💥使用<script>
标签
<div id="app">
<lk-count></lk-count>
</div>
<!-- 1. script标签, 注意:类型必须是text/template-->
<script type="text/x-template" id="mycount">
<button @click="count++" > 你点击了{ { count } }次</button >
</script>
<script>
// 1.注册一个局部组件
const Counter = {
data() {
return {
count: 0
}
},
// 使用#id 将template模块抽离出去
template: '#mycount'
// template: `
// <button @click="count++">你点击了{{count}}次</button>
// `
}
// 2.创建Vue的实例对象
const app = Vue.createApp({
data() {
return {
msg: '你好,Vue3!'
}
},
// 组件选项
components: {
'lk-count': Counter,
}
});
// 3.挂载vue实例
app.mount('#app');
</script>
💥使用<template>
标签🔥
- template 标签内只允许出现一个父级标签
<template id="mycount">
<button @click="count++">你点击了{{ count }}次</button>
</template>
<script>
// 注册一个局部组件
const Counter = {
data() {
return {
count: 0
}
},
template: '#mycount'
}
// 创建Vue的实例对象
const app = Vue.createApp({
data(){
return {
msg: '你好,Vue3!'
}
},
// 组件选项
components: {
'lk-count': Counter,
}
});
// 挂载vue实例
app.mount('#app');
</script>
🌀单文件组件 .vue
- createApp函数传入了一个对象App,这个对象其实本质上就是一个组件
- 可以通过一个后缀名为 .vue 的single-file components (单文件组件) 来解决,并且可以使用webpack或者vite或者rollup等构建工具来对其进行处理
-
<template>
内只允许出现一个父级标签
-
<script>
可以没有,表示是设置标签对象的实例
-
<style>
可以没有,表示css效果,scoped:表示样式只有当前实例中有效,没有 scoped 限制时,都会作用于全局,lang=“scss” 表示支持sass的写法.
<template>
<div class='app'>
<HeaderCom></HeaderCom> //3调用
<HeaderCom/> //单标签也可以
</div>
</template>
<script>
import HeaderCom from './components/HeaderCom.vue'; //1引入
export default {
//import引入的组件需要注入到对象中才能使用
components: { HeaderCom }, //2注册
data() {
//这里存放数据
return {
};
},
}
</script>
<style lang=scss scoped>
</style>
- 引入样式
//main.js 全局引入样式
import "./assets/css/main.min.css";
import "bootstrap/dist/css/bootstrap.min.css" //所有样式
// 局部引用 其它方法引入
<style scoped lang="scss">
@import "@/assets/css/sty.scss"; /*引用其它文件*/
</style>
//简写文法
<style scoped lang="scss" src="@/assets/css/sty.scss">
/*引用其它文件*/
</style>
🌈面试题:注意 main.js 中 引入的 vue 版本(vue-loader)
💥scoped的原理
-
组件的私有化样式:每个组件都会给标签生成一个唯一编号,scoped 会在 DOM 结构及 css 样式上加上唯一性的标记 data-v-xxx 属性,从而达到样式私有化,不污染全局的作用。
-
HTML的DOM节点加同一个随机data-v开头属性(形如:data-v-2311c06a)来表示他的唯一性,在每句css选择器的末尾(编译后的生成的css语句)加一个当前组件的data属性选择器(如[data-v-2311c06a])来私有化样式
<div data-v-2311c06a class="button-warp">
<button data-v-2311c06a class="button">text</button>
</div>
.button-warp[data-v-2311c06a]{
display:inline-block;
}
.button[data-v-2311c06a]{
padding: 5px 10px;
font-size: 12px;
border-radus: 2px;
}
🌈解决vue中样式不起作用:样式穿透/深度选择器
🌀内置组件
💥动态组件 component
- is: 的组件名 必须是 全局 或 局部 注册的组件
- element ui-plus 小图标使用动态组件实现
- 可以传递数据
💥Keep-Alive 缓存组件
-
include
- string | RegExp | Array。只有名称 (name值) 匹配的组件会被缓存; -
exclude
- string | RegExp | Array。任何名称匹配的组件都不会被缓存 -
max
- number | string。最多可以缓存多少组件实例,一旦达到这个数字,那么缓存组件中最近没有被访问的实例会被销毁 -
作用:
在组件切换过程中 把切换出去的组件保留在内存中,防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验性 -
原理:
在 created钩子函数调用时将需要缓存的 VNode 节点保存在 this.cache 中/在 render(页面渲染) 时,如果 VNode 的 name 符合缓存条件(可以用 include 以及 exclude 控制),则会从 this.cache 中取出之前缓存的 VNode实例进行渲染。
💥缓存组件的生命周期
activated
:在<keep-alive>
中组件被激活时调用,即进入组件时触发;deactivated
:在<keep-alive>
中组件停用时调用,即离开组件时触发。- 使用
<keep-alive>
包裹组件,会将组件的数据保留在内存中。如果希望每次进入页面时都获取最新的数据,需要在activated
阶段获取数据,承担原来created
钩子函数中获取数据的任务。
🌈注意坑:
实际上keep-alive缓存组件前,底层会有一个判断,
先获取包裹的第一个节点,然后判断该节点是否属于组件节点(是不是一个组件),
如果是就会进行缓存,如果不是就不会进行缓存。
<keep-alive>
<div></div> //这个不是组件 不被缓存
<router-view/>
<keep-alive>
<div> //第一个节点不是组件 不被缓存
<router-view/>
</div>
</keep-alive>
<keep-alive>
<Child1/> //自定义组件 子组件
<Child2/>
</keep-alive>
🫵组件通信
🌀父子组件之间通信
- 父组件向子组件传递数据 属于 浅拷贝
💥父组件传递给子组件:通过props
属性
- Props是你可以在组件上注册一些自定义的attribute
- 父组件给这些attribute赋值,子组件通过attribute的名称获取到对应的值
- 对象类型可以随意修改对象中的属性但是不能重新创建新的对象 , 否则 vue 底层也会报错
props传数组
- 专门用于接受别的组件发送过来的数据
- 数组中的字符串就是attribute的名称
v-bind
将这些属性变成动态属性- Vue 中规定,通过
props
获取到的值,可以访问,但是不能修改
props传对象(类型验证)
- Props 的验证是指子组件可以对外部接收的数据的类型或内容设置验证规则,不满足验证,则浏览器中就会抛出报错
- type 类型,值还可以为数组,写多个类型
- required 是否必传,开启后不传值会报错
- default 默认值 (默认值是对象时使用 函数方式 返回)
- 自定义验证规则 validator 验证器
money:{
type:Number,
validator:(value)=>{
console.log(value);
if(value>0){
return true
}else {
alert("参数不对")
return false
}
} //自定义验证 函数(值) 返回真,通过验证,假 不通过验证
}
以上代码不符合验证器规则的判断,会抛出检测失败的错误
- Invalid prop: custom validator check failed for prop "dataAge".
props 的大小写命名
- html中命名规范为 kebab-case (短横线分隔命名) ,js中命名规范为camelCase (驼峰命名法)
- HTML 中的 attribute 名是大小写不敏感的,浏览器会把所有大写字符解释为小写字符
- camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 在 DOM 中的模板 命名 属性名;
非Prop的Attribute
- 父组件传值,但是子组件没有prop接收
- 当我们传递给一个组件某个属性 , 但是该属性并没有定义对应的
props
或者emits
时 , 就称之为 非Prop 的 Attribute
Attribute 继承
- inheritAttrs:true 默认值,继承除 props 之外的所有属性
- inheritAttrs:false, 只继承 class style 属性
- 当子组件有单个根节点时 , 非 Prop 的 Attribute 将自动添加到根节点的 Attribute 中
- 当子组件有多个根节点时 ,非 Prop 的 Attribute 没有明确绑定会警告
- 禁用 attribute 继承的常见情况是需要将 attribute 应用于根元素之外的其他元素
- 如果不想继承, 需要关闭
inheritAttrs
export default {
inheritAttrs:false,
}
- 关闭
inheritAttrs
,但是又想拿到数据:通过 对象$attrs
访问 非props的attribute
<template>
<div class='app'>
<h2 :class="$attrs.abc">姓名:{{ name }}{{ $attrs.abc }}</h2>
</div>
</template>
$attrs / $listeners
💥子组件传递给父组件:通过 $emit
- this.$emit(“父组件事件名”,实参数)
- 在父组件中,通过 v-on 来监听 自定义事件
- 在子组件中,通过
$emit()
来调用(触发)自定义事件
💥vue3 新增 emits 属性
- 数组语法: 注册自定义事件,多人开发来提示传参,并且父组件会有 代码提示
- 对象语法:写个函数,可以验证参数,不满足提示警告,依然可以传递,不会报错
💥ref / $refs
- $refs是一个对象,属性名就是绑定在模板中的ref值
- $refs:是 ref 的集合,只会在组件渲染完成之后生效,并且它们不是响应式的。你应该避免在模板或计算属性中访问 $refs
- ref 绑定在 H5元素标签中,给元素或者子组件注册引用信息
- ref 绑定在 标签 中,$refs 为对应元素标签 的 虚拟 Dom对象
- ref 绑定在 组件 中,$refs 为 对应 组件实例,但是和 子组件this不同
- $el 用于获取组件内 DOM(包括子组件,当前.vue组件,以及父组件)
💥$parent / $children
- $parent:访问父组件实例对象(this),直接操作父组件的data数据,不需要再使用属性传值,但是容易出现渲染混乱之后只渲染一个的情况
this.$parent //打印出来为 VueComponent对象
- $children:访问子组件实例对象 数组,不能保证顺序,也不是响应式的(vue3移除)
this.$children //打印出来为 []
- $root: 访问根组件vm对象,所有的子组件都可以将这个实例作为一个全局 store 来访问或使用,现在有更好的技术vuex代替。
🤢$nextTick()
- DOM 更新时机
当你更改响应式状态后,DOM 会自动更新。然而,你得注意 DOM 的更新并不是同步的。相反,Vue 将缓冲它们直到更新周期的 “下个时机(下一次事件循环)” 以确保无论你进行了多少次状态更改,每个组件都只更新一次。避免出现重复渲染的情况。 - 在下次 DOM 更新循环结束之后执行延迟回调 。 在修改数据之后立即使用这个方法 , 获取更新后的 DOM,并返回一个 Promise
this.$nextTick(function () {
// DOM 现在更新了
// `this` 绑定到当前实例
this.doSomethingElse()
})
🌈$nextTick不知真假的坑
🌀插槽 Slot
💥默认插槽(单个插槽/匿名插槽)
- 通过插槽,在子组件中设置一个占位符
<slot></slot>
,外部来传递一些(多个)
模板片段 - 抽取共性,预留不同,将共同的元素,内容依然在组件内进行封装
<slot></slot>
元素是一个作为承载分发内容的插槽出口 (slot outlet),标示了父元素提供的插槽内容 (slot content) 将在哪里被渲染。
- 插槽默认内容
<slot>
<p>外部没有传入模板代码时,这里就是默认内容显示</p>
</slot>
💥具名插槽
- 给插槽命名。当一个子组件中有多个插槽时,就需要通过命名来区分它们。
- 一个不带 name 的 slot 插槽, 会带有隐含的名字 default
v-slot
指令还可以简写成#
v-slot
指令只能在子组件或<template>
身上使用。<template #header>
,<template v-slot:header>
将这部分模板片段传入子组件的 header 插槽中
- v-slot动态绑定一个名称,动态名字为变量在data中定义
💥渲染作用域
Vue中有渲染作用域的概念:
- 父级模板里的所有内容都是在父级作用域中编译的
- 子模板里的所有内容都是在子作用域中编译的
- 子组件不能
直接
修改父组件的内容
💥作用域插槽
- 当插槽中需要渲染的数据保存在子组件中,但数据的渲染节点(标签)需要由父组件决定,使用作用域插槽
- 父组件 通过
v-slot="slotProps"
指令来接收 子组件 插槽内的 数据传递 - 子组件渲染 父组件传过来的数据时, 又想 父组件决定 数据的标签类型
- 独占默认插槽的缩写
v-slot:default="slotProps"
可以简写为v-slot="slotProps"
-
如果我们的插槽只有默认插槽时,可以省略template标签,组件的标签可以被当做插槽的模板来使用,这样,我们就可以将v-slot 直接用在组件上
-
同时有默认插槽和具名插槽不能简写
-
$slots无法获取具名作用域插槽,在使用具名作用域插槽时 v-slot:btn=“{ userName }”, 该插槽无法用 $slots 获取到, 要使用 $scopedSlots 获取
🌀非父子组件之间通信(爷孙,兄弟)
💥依赖注入(provide / inject)属性
- 外层传内层
- provide和inject是vue提供的两个钩子,和data、methods是同级的。
- 父组件provide 钩子用来发送数据或方法,来提供数据(一般写函数)
- 子组件inject 钩子用来接收数据或方法,来使用这些数据
- 数据不是响应式的,数据挂载到当前实例(this)上
- inject值类型可以为 数组,对象(指明来源,默认值)
- 但是可以通过 computed计算属性 变成响应式数据
💥事件总线 (vue2)
- 通过
new Vue()
创建一个新的 Vue 实例对象bus
,来作为组件之间传值的中转站 - eventBus 其实就是一个发布订阅模式,利用 Vue 的自定义事件机制,在触发的地方通过 $emit 向外发布一个事件,在需要监听的页面,通过 $on 监听事件。
- 总线事件名 busEvent 可以按函数名随意写,只要保证,设置与执行的是同一事件即可.
- 首先我们需要在项目中新建一个
.js
文件,用来创建事件总线,例如bus.js
:
import Vue from "vue"
const bus=new Vue();
export default bus;
- 给 bus 设置事件监听器
bus.$on
- 由于是组件 b 想要接收数据,因此我们在组件 c 的生命周期函数
created
中给 bus 设置事件监听器 - 在组件 b 中引入 bus,并通过
$on
方法给 bus 设置一个 getData 方法的事件监听器。当 getData 方法被触发时,就会执行箭头函数,并通过 data 接收到传递进来的数据。
import bus from "../plugins/bus.js"
data(){
return {
num:null
}
},
mounted(){
//监听 事件总线,(事件名,回调函数)
bus.$on("busEvent",(result)=>{
console.log(result)
this.num=result
})
}
- 触发 bus 监听的事件,发送事件
bus.$emit
- 组件 A 负责传递数据,因此在组件 A 的事件触发 bus 监听的事件,并将数据传递过去
- 当send方法被调用时,组件 A 中就会触发getData方法,并将
"this.x"
传递给组件 B
<button @click="send">开始发数据</button>
<script>
import bus from "../plugins/bus.js"
...
methods: {
send(){
//向事件总线发送数据
this.x = + new Date()
bus.$emit("busEvent",this.x)
}
},
</script>
🤢事件总线 缺点
- 出现很多个组件传递时,没法找都在哪里定义
- 生命周期的问题(EventBus事件未在实例销毁的时候,需要注销监听事件,导致触发上一次监听事件,遗留数据也是上一次事件中的)
💥全局事件总线mitt库 ( vue3 )
- Vue3从实例中移除了
$on
、$off
和$once
方法,所以我们如果希望继续使用全局事件总线,要通过第三方的库 - Vue3官方有推荐一些库,例如 mitt 或 tiny-emitter;
- 库: hy-event-store
🌀Vue2组件的生命周期
- 每个组件都可能会经历从创建、挂载、更新、卸载等一系列的阶段过程,这个阶段过程就是生命周期
- 具体来说就是vue实例从开始创建、初始化数据、编译模板、挂载Dom、渲染数据、更新数据、最后销毁这样的一个过程
- 生命周期函数是一些钩子函数(回调函数),在某个 阶段 会被Vue源码内部进行回调
- 生命周期是同步执行的
- 7.beforeDestroy (懒得改了)
- 8.destroyed (懒得改了)
函数名 | 阶段 | 描述 |
---|---|---|
beforeCreate | 创建 | 组件创建前。此阶段组件的选项还未挂载,因此无法访问 methods、data、computed 上的方法或数据。 |
created | 创建 | 组件创建完成。此阶段可以查看到数据,但是组件的 DOM 节点并未渲染。 |
beforeMount | 挂载 | 组件挂载前。组件 DOM 节点渲染之前,相关的 render 函数首次被调用。 |
mounted | 挂载 | 组件挂载完成。组件 DOM 节点渲染完成,此阶段可以操作真实 DOM 节点,也可以向后端发起请求。 |
beforeUpdate | 更新 | 组件更新前。当组件的数据更新前进入此阶段。此时虚拟节点都还没有重新渲染。 |
updated | 更新 | 组件更新完成。数据更改,DOM 节点重新渲染。 |
activated | 缓存 | 组件缓存激活时调用。 |
deactivated | 缓存 | 组件缓存停用时调用。 |
beforeDestroy | 销毁 | 组件销毁前。调用vm.$destroy() 方法可销毁组件,在此阶段,组件仍然完全可用。 |
destroyed | 销毁 | 组件销毁完成。此阶段组件所有的属性都会解绑,所有的事件监听器被移除。所有的子组件也都会呗销毁。 |
- 注意:应避免在
updated
中更改状态,可能会导致更新无限循环
对比
🤢vue中ajax请求放在哪个生命周期中:
为什么不在 created 里去发ajax?created 可是比 mounted 更早调用啊,更早调用意味着更早返回结果,那样性能不是更高?
首先,一个组件的 created 比 mounted 也早调用不了几微秒,性能没啥提高;
而且,等到异步渲染开启的时候,created 就可能被中途打断,中断之后渲染又要重做一遍,想一想,在 created 中做ajax调用,代码里看到只有调用一次,但是实际上可能调用 N 多次,这明显不合适。
相反,若把发ajax 放在 mounted,因为 mounted 在第二阶段,所以绝对不会多次重复调用,这才是ajax合适的位置.
在created的时候,视图中的dom并没有被渲染出来,所以此时如果直接去操作dom节点,无法找到相关元素。
在mounted中,由于此时的dom元素已经渲染出来了,所以可以直接使用dom节点。
一般情况下,都放在mounted中,保证逻辑的统一性。因为生命周期是同步执行的,ajax是异步执行的。
服务端渲染不支持mounted方法,所以在服务端渲染的情况下统一放在created中
🌀组件的 Mixin 混入
- 组件和组件之间有时候会存在相同的代码逻辑,对相同的代码逻辑进行抽取
- 一个Mixin对象可以包含任何组件选项(data、computed、watch 以及生命周期函数等)
- 当组件使用Mixin对象时,所有Mixin对象的选项将被 混合 进入该组件本身的选项中
- 用法:新建一个js文件,抛出一个对象。哪个组件想用就引入
- 合并规则
- 混入得钩子函数,会先执行混入得钩子函数,后执行本地的钩子函数
- 全局混入Mixin
const app = createApp(App)
app.mixin({
created() {
console.log("mixin created")
}
})
app.mount('#app')