重新认识一下 vue 应用实例
💕 创建应用实例
每个 Vue 应用都是通过 createApp
函数创建一个新的 应用实例
应用实例必须在调用了 .mount()
方法后才会渲染出来。该方法接收一个“容器”参数,可以是一个实际的 DOM 元素或是一个 CSS 选择器字符串
// main.js
import App from './App.vue'
import { createApp } from 'vue'
const app = createApp(App)
app.mount('#app')
因此我们可以在入口文件中,通过创建多个 DOM 节点,并在 main.js
文件中创建多个应用实例
💕 app.createApp()、app.createSSRApp()
✔createApp
: 除了可以传递第一个参数是根组件外,还可以传递第二个参数(可选),它是要传递给根组件的 props
// main.js
import App from './App.vue'
import { createApp } from 'vue'
const app = createApp(App, { msg: '我是通过 createApp 传递给到 根组件 的' })
app.mount('#app')
// App.vue
const props = defineProps({
msg: {
type: String
}
})
onMounted(() => {
console.log(props.msg) // 我是通过 createApp 传递给到 根组件 的
})
✔createSSRApp()
:以 SSR 激活模式创建一个应用实例。用法与 createApp
完全相同。
💕 app.mount()、app.unmount()
✔mount
:将应用实例挂载在一个容器元素中。对于每个应用实例,mount
仅能调用一次。
参数可以是一个实际的 DOM 元素或一个 CSS 选择器 (使用第一个匹配到的元素)
app.mount('#app')
app.mount(document.body.firstChild) // 挂载到一个实际的 DOM 元素
✔unmount
:卸载一个已挂载的应用实例。卸载一个应用会触发该应用组件树内所有组件的卸载生命周期钩子。
import App from './App.vue'
import { createApp } from 'vue'
const app = createApp(App)
app.mount('#app')
// 2s 后 销毁掉 应用实例
setTimeout(() => {
app.unmount()
}, 2000)
在组件 HelloWorld
中,当应用挂载完成后,2s 后销毁应用,可以发现其应用组件树内所有组件的卸载生命周期钩子都会触发
<script setup>
import { onBeforeMount, onBeforeUnmount, onMounted, onUnmounted } from 'vue'
onMounted(() => {
console.log('HelloWorld Mounted')
})
onBeforeMount(() => {
console.log('HelloWorld BeforeMount')
})
onBeforeUnmount(() => {
console.log('HelloWorld BeforeUnmount')
})
onUnmounted(() => {
console.log('HelloWorld UnMounted')
})
</script>
💕 app.component()
✔component
:用于全局组件的注册,后续在该应用实例下的所有组件都可以使用该组件,无需再次局部注册
import App from './App.vue'
import { createApp } from 'vue'
import HelloWorld from './components/HelloWorld'
const app = createApp(App)
app.mount('#app')
// 注册全局组件
app.component('HelloWorld', HelloWorld)
但全局注册有以下几个问题:
- ✨如果你全局注册了一个组件,即使它并没有被实际使用,它仍然会出现在打包后的 JS 文件中(tree-shaking)
- ✨在父组件中使用子组件时,不太容易定位子组件的实现。和使用过多的全局变量一样,这可能会影响应用长期的可维护性
相比之下,局部注册的组件需要在使用它的父组件中显式导入,并且只能在该父组件中使用。它的优点是使组件之间的依赖关系更加明确,并且对 tree-shaking 更加友好。
<script setup>
import HelloWorld from './components/HelloWorld.vue'
</script>
<template>
<HelloWorld />
</template>
💕 app.directive()
✔ directive
:全局注册自定义指令
👨:什么是自定义指令
🧒:利用组件的生命周期钩子函数重用涉及普通元素的底层 DOM 访问的逻辑。vue 提供了内置指令(如:
v-model
、v-show
、v-if
)
在组件中:实现自定义指令
<script setup>
const customFocus = {
// 组件挂载时,自动获取焦点
mounted: (el) => el.focus()
}
</script>
<template>
<input custom-focus />
</template>
全局注册(这里实现一个权限控制的自定义指令)
<button v-auth="['importUser']"></button>
// authBtn.js
import store from '@/store'
function checkPermission (el, binding) {
// 获取绑定的值,此处为权限 value: ['importUser']
const { value } = binding
// 获取所有的功能指令(后端请求回来的数据)
const points = store.getters.userInfo.permission.points
// 当传入的指令集为数组时
if (value && value instanceof Array) {
// 匹配对应的指令
const hasPermission = points.some(point => {
return value.includes(point)
})
// 如果无法匹配,则表示当前用户无该指令,那么删除对应的功能按钮
if (!hasPermission) {
// 移除节点
el.parentNode && el.parentNode.removeChild(el)
}
} else {
// eslint-disabled-next-line
throw new Error('v-permission value is ["admin","editor"]')
}
}
export default {
// 在绑定元素的父组件被挂载后调用
mounted (el, binding) {
checkPermission(el, binding)
},
// 在包含组件的 VNode 及其子组件的 VNode 更新后调用
update (el, binding) {
checkPermission(el, binding)
}
}
// 指令入口文件,将定义的组件进行抛出
import authBtn from './authBtn'
export default app => {
app.directive('v-auth', authBtn)
}
import App from './App.vue'
import { createApp } from 'vue'
import HelloWorld from './components/HelloWorld'
const app = createApp(App)
app.mount('#app')
// main.js
import installDirective from '@/directives'
installDirective(app)
💕 app.use()
✔use
:安装一个插件
👨:如何安装一个插件
🧒:安装一个插件的本质是通过传递应用实例对象给到自定义插件中,在插件中针对这个对象进行操作
👨:那它是通过什么方式将应用实例对象进行传入的
🧒:通过
use
方法进行安装,默认会调用插件的install
方法,有点component
的使用方式
import App from './App.vue'
import { createApp } from 'vue'
import myPlugin from './plugin'
const app = createApp(App)
// 安装插件
app.use(myPlugin, {
type: '参数类型',
msg: '自定义插件'
})
app.mount('#app')
const myPlugin = {
install: (app, options) => {
console.log('安装自定义的组件')
console.log(app, '应用实例对象')
console.log(options, '配置参数')
// 进行安装操作。。。。
// 如挂载一个全局变量,注册全局组件,依赖注入等
}
}
export default myPlugin
💕 app.mixin()
✔mixin
:应用一个全局 mixin (适用于该应用的范围)。一个全局的 mixin 会作用于应用中的每个组件实例。
不推荐
Mixins 在 Vue 3 支持主要是为了向后兼容,因为生态中有许多库使用到。在新的应用中应尽量避免使用 mixin,特别是全局 mixin
// main.js
import App from './App.vue'
import { createApp } from 'vue'
const app = createApp(App)
const myMixin = {
data() {
return {
message: 'Hello World'
}
},
created() {
console.log('Mixin created');
},
methods: {
someMethod() {
console.log('Mixin method');
},
}
}
// 注入 mixin
app.mixin(myMixin)
app.mount('#app')
<!-- HelloWorld -->
<div>
{{ message }}
<button @click="someMethod">测试</button>
</div>
<script>
export default {
created() {
console.log('Component created')
},
methods: {
componentMethod() {
console.log('Component method');
}
}
}
</script>
💕 app.provide()
✔provide
:提供一个值,可以在应用中的所有后代组件中注入使用
import App from './App.vue'
import { createApp } from 'vue'
const app = createApp(App)
// 全局注入
app.provide('msg', 'hello')
app.mount('#app')
在应用的某个组件中:
<!-- HelloWorld -->
<script setup>
import { inject } from 'vue'
const msg = inject('msg')
</script>
<template>
{{ msg }}
</template>
同理:在某个组件中,也可以通过provide
的方式将变量(方法)进行注入
<script setup>
import { provide } from 'vue'
const location = ref('North Pole')
const updateLocation = () => {
location.value = 'South Pole'
}
provide('location', {
location,
updateLocation
})
</script>
<script setup>
import { inject } from 'vue'
const { location, updateLocation } = inject('location')
</script>
<template>
<button @click="updateLocation">{{ location }}</button>
</template>
💕 app.runWithContext()
✔runWithContext
:使用当前应用作为注入上下文执行回调函数(vue3.3 以上)
import { inject } from 'vue'
app.provide('id', 1)
const injected = app.runWithContext(() => {
return inject('id')
})
console.log(injected) // 1
💕 app.version
✔version
:提供当前应用所使用的 Vue 版本号
通过判断当前 vue 版本,执行不同的安装插件方式
export default {
install(app) {
const version = Number(app.version.split('.')[0])
if (version < 3) {
console.warn('This plugin requires Vue 3')
}
}
}
💕 app.config.globalProperties
✔globalProperties
:用于注册能够被应用内所有组件实例访问到的全局属性的对象
app.config.globalProperties.$msg = '123'
<template>
<HelloWorld :msg="$msg" />
</template>
💕 不常用 app.config.optionMergeStrategies
✔optionMergeStrategies
:一个用于定义自定义组件选项的合并策略的对象
const app = createApp({
// 自身的选项
msg: 'Vue',
// 来自 mixin 的选项
mixins: [
{
msg: 'Hello '
}
],
mounted() {
// 在 this.$options 上暴露被合并的选项
console.log(this.$options.msg)
}
})
// 为 `msg` 定义一个合并策略函数
app.config.optionMergeStrategies.msg = (parent, child) => {
return (parent || '') + (child || '')
}
app.mount('#app')
// 打印 'Hello Vue'
💕 不常用 app.config.errorHandler()
✔errorHandler
:用于为应用内抛出的未捕获错误指定一个全局处理函数
interface AppConfig {
errorHandler?: (
err: unknown, // 错误对象
instance: ComponentPublicInstance | null, // 触发该错误的组件实例
info: string // 指出错误来源类型信息的字符串
) => void
}
它可以从下面这些来源中捕获错误:
- 组件渲染器
- 事件处理器
- 生命周期钩子
setup()
函数- 侦听器
- 自定义指令钩子
- 过渡 (Transition) 钩子
app.config.errorHandler = (err, instance, info) => {
console.log(err, 'err')
console.log(instance, 'instance')
console.log(info, 'info')
}
<script setup>
const throwError = () => {
throw new Error('错误信息')
}
</script>
<template>
<button @click="throwError">抛出错误</button>
</template>
💕 不常用 app.config.warnHandler()
✔warnHandler
:用于为 Vue 的运行时警告指定一个自定义处理函数
interface AppConfig {
warnHandler?: (
msg: string, // 警告信息
instance: ComponentPublicInstance | null, // 组件实例
trace: string // 组件追踪字符串
) => void
}
💕 不常用 app.config.performance
✔performance
:设置此项为 true
可以在浏览器开发工具的“性能/时间线”页中启用对组件初始化、编译、渲染和修补的性能表现追踪。
💕 不常用 app.config.compilerOptions
✔ 官方解释
✔ 自己的理解:运行编译 vue 文件时,需要调用一些内部内部compilerOptions
配置好的方法。我们可以通过修改 compilerOptions
提供的方法,来改写编译过程的一些方法。(不对的话,请多多指教)
💕 不常用 app.config.compilerOptions.isCustomElement()
✔isCustomElement
:用于指定一个检查方法来识别原生自定义元素
// 将所有标签前缀为 `ion-` 的标签视为自定义元素
app.config.compilerOptions.isCustomElement = (tag) => {
return tag.startsWith('ion-')
}
💕 不常用 app.config.compilerOptions.whitespace
✔whitespace
:用于调整模板中空格的处理行为
-
类型
'condense' | 'preserve'
-
默认
'condense'
-
详细信息
Vue 移除/缩短了模板中的空格以求更高效的模板输出。默认的策略是“缩短”,表现行为如下:
- 元素中开头和结尾的空格字符将被缩短为一个空格。
- 包含换行的元素之间的空白字符会被删除。
- 文本节点中连续的空白字符被缩短成一个空格。
设置该选项为
'preserve'
则会禁用 (2) 和 (3) 两项。
💕 不常用 app.config.compilerOptions.delimiters
✔delimiters
:用于调整模板内文本插值的分隔符
-
类型
[string, string]
-
默认
['{{', '}}']
-
详细信息
此项通常是为了避免与同样使用 mustache 语法的服务器端框架发生冲突。
💕 不常用 app.config.compilerOptions.comments
✔comments
:用于调整是否移除模板中的 HTML 注释
-
详细信息
默认情况下,Vue 会在生产环境移除所有注释,设置该项为
true
会强制 Vue 在生产环境也保留注释。在开发过程中,注释是始终被保留的。这个选项通常在 Vue 与其他依赖 HTML 注释的库一起使用时使用