学习vue3 八 全局函数和变量,编写插件,scoped,样式穿透,vue3新增的css特性

目录

Vue3定义全局函数和变量

vue3编写插件

scoped和样式穿透

scoped的原理

1. 插槽选择器

2. 全局选择器

3. 动态css

4. css module


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三条渲染规则:

  1. 给HTML的DOM节点加一个不重复data属性(形如:data-v-123)来表示他的唯一性
  2. 在每句css选择器的末尾(编译后的生成的css语句)加一个当前组件的data属性选择器(如[data-v-123])来私有化样式
  3. 如果组件内部包含有其他组件,只会给其他组件的最外层标签加上当前组件的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>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值