目录
Vue3定义全局函数和变量
因为vue3没有prototype属性所以要使用 app.config.globalProperties 来在全局绑定函数或者变量
vue2的用法
Vue.prototype.$http = () => {}
vue3的用法
在main.ts内
import { createApp } from 'vue'
import App from './App.vue'
import "animate.css"
const app = createApp(App)
type Filter = {
format: (str:string)=>string
}
declare module 'vue' {
export interface ComponentCustomProperties {
$filters: Filter,
$api: string
}
}
app.config.globalProperties.$api = "https://api.themoviedb.org/3"
app.config.globalProperties.$filtter = {
format: (str:string):string=> {
return '加上' + str
}
}
app.mount('#app')
需要声明文件declare module 在组件中使用时,才会有提示
在组件中使用,模板中直接插值语法,setup中需要使用getCurrentInstance获取当前实例,然后通过当前实例的props属性找到
<script setup lang="ts">
import { ref } from 'vue';
import {getCurrentInstance} from "vue";
const instance = getCurrentInstance();
const text = ref('');
function changeInput() {
text.value = instance?.proxy?.$filters.format(text.value) as string;
}
</script>
<template>
<div class="context">
<p>{{$api}}</p>
<input type="text" v-model="text" @input="changeInput">
{{text}}
</div>
</template>
<style scoped>
.image img{
width: 300px;
}
</style>
vue3编写插件
在使用 createApp()
初始化 Vue 应用程序后,你可以通过调用 use()
方法将插件添加到你的应用程序中。
实现一个Loading
app.vue
<script setup lang="ts">
import {getCurrentInstance} from "vue";
const instance = getCurrentInstance()
instance?.proxy?.$loading.show()
setTimeout(()=> {
instance?.proxy?.$loading.hide()
},1000)
</script>
<template>
</template>
<style scoped>
</style>
Loading。vue
<template>
<div v-if="isShow" class="loading">
<div class="loading-content">Loading...</div>
</div>
</template>
<script setup lang='ts'>
import { ref } from 'vue';
const isShow = ref(false)//定位loading 的开关
const show = () => {
isShow.value = true
}
const hide = () => {
isShow.value = false
}
//对外暴露 当前组件的属性和方法
defineExpose({
isShow,
show,
hide
})
</script>
<style scoped lang="css">
.loading {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.8);
display: flex;
justify-content: center;
align-items: center;
}
.loading-content {
font-size: 30px;
color: #fff;
}
</style>
Loading/index
import {createVNode,render,App} from "vue";
import Loading from "../Loading.vue";
export default {
install(app:App) {
const Vnode = createVNode(Loading);
render(Vnode,document.body); // 将组件渲染到body上
// 将组件的方法进行挂载
app.config.globalProperties.$loading = {
show: Vnode.component?.exposed?.show,
hide: Vnode.component?.exposed?.hide
}
}
}
main.ts
import { createApp } from 'vue'
import App from './App.vue'
import "animate.css"
import Loading from './components/Loading/index.ts'
const app = createApp(App)
type LoadShow = {
show: ()=> void
hide: () => void
}
declare module 'vue' {
export interface ComponentCustomProperties {
$loading: LoadShow
}
}
app.use(Loading)
app.mount('#app')
scoped和样式穿透
scoped的原理
vue中的scoped 通过在DOM结构以及css样式上加唯一不重复的标记:data-v-hash的方式,以保证唯一(而这个工作是由过PostCSS转译实现的),达到样式私有化模块化的目的。
scoped三条渲染规则:
- 给HTML的DOM节点加一个不重复data属性(形如:data-v-123)来表示他的唯一性
- 在每句css选择器的末尾(编译后的生成的css语句)加一个当前组件的data属性选择器(如[data-v-123])来私有化样式
- 如果组件内部包含有其他组件,只会给其他组件的最外层标签加上当前组件的data属性
PostCSS会给一个组件中的所有dom添加了一个独一无二的动态属性data-v-xxxx,然后,给CSS选择器额外添加一个对应的属性选择器来选择该组件中dom,这种做法使得样式只作用于含有该属性的dom——组件内部dom, 从而达到了'样式模块化'的效果.
案例:
修改element-plus ui的input框的样式,我们会发现,使用css选择器无法对input生效,所以要采用:deep(css选择器)的形式来进行样式穿透
<script setup lang="ts">
</script>
<template>
<div class="content">
<el-input class="input" placeholder="Please input" />
</div>
</template>
<style scoped>
.content {
width: 500px;
margin: 0 auto;
height: 300px;
background-color: #cccccc;
}
.content :deep(.el-input__wrapper) {
background-color: #bfa;
color: #1a1a1a;
}
</style>
在vue3中还有其他的一些css选择器
1. 插槽选择器
子组件
<script setup lang="ts">
</script>
<template>
<div class="content">
<h3>我是子组件</h3>
<slot></slot>
</div>
</template>
<style scoped>
.content {
width: 300px;
height: 300px;
margin: 10px auto;
}
:slotted(.toRed) {
color: red;
}
</style>
父组件
<script setup lang="ts">
import BaseRefAndReactive from "./components/BaseRefAndReactive.vue";
</script>
<template>
<BaseRefAndReactive>
<div class="toRed">我是插槽,我想通过子组件变成红色字体</div>
</BaseRefAndReactive>
</template>
<style scoped>
</style>
一般来讲子组件不能通过父组件插槽的class名去改变其样式,但是vue3提供了:slotted(名)的方式给我们
2. 全局选择器
以前我们想让样式成为全局样式,就像拿去scoped,现在有了更加优雅的方式去实现
:global(css选择器)的方式,即可在scoped存在的情况下,让样式成为全局样式
案例:
<script setup lang="ts">
import BaseRefAndReactive from "./components/BaseRefAndReactive.vue";
</script>
<template>
<BaseRefAndReactive>
<div class="toRed">我是插槽,我想通过子组件变成红色字体</div>
</BaseRefAndReactive>
</template>
<style scoped>
:global(div) {
background-color: #cccccc;
}
</style>
执行效果图:
3. 动态css
单文件组件的 <style>
标签可以通过 v-bind
这一 CSS 函数将 CSS 的值关联到动态的组件状态上:
那么我们就可以更加方便的通过js去修改样式
案例:2s之后背景变成绿色
<script setup lang="ts">
import BaseRefAndReactive from "./components/BaseRefAndReactive.vue";
import {ref} from "vue";
const color = ref("#ccc")
setTimeout(() => {
color.value = "#f00"
},2000)
</script>
<template>
<BaseRefAndReactive>
<div class="toRed">我是插槽,我想通过子组件变成红色字体</div>
</BaseRefAndReactive>
</template>
<style scoped>
:global(div) {
background-color: v-bind(color);
}
</style>
也可以使用对象的写法,不过对象的话需要加上“”
background-color: v-bind('color.name');
4. css module
<style module>
标签会被编译为 CSS Modules 并且将生成的 CSS 类作为 $style
对象的键暴露给组件
案例:
<script setup lang="ts">
import BaseRefAndReactive from "./components/BaseRefAndReactive.vue";
</script>
<template>
<div :class="$style.red">
我是父组件
</div>
<BaseRefAndReactive>
<div class="toRed">我是插槽,我想通过子组件变成红色字体</div>
</BaseRefAndReactive>
</template>
<style module>
.red {
color: red;
font-size: 20px;
text-align: center;
}
</style>
也可以给其命名
案例:
<script setup lang="ts">
import BaseRefAndReactive from "./components/BaseRefAndReactive.vue";
</script>
<template>
<div :class="[module.red]">
我是父组件
</div>
<BaseRefAndReactive>
<div class="toRed">我是插槽,我想通过子组件变成红色字体</div>
</BaseRefAndReactive>
</template>
<style module="module">
.red {
color: red;
font-size: 20px;
text-align: center;
}
</style>