<script setup>
//props是响应式的不能解构
//方法1:不能设置默认值(使用withDefaults解决)
const props = defineProps({
foo?: String,
id: [Number, String],
onEvent: Function, //Function类型
metadata: null
})
//方法2
const props = defineProps({ foo: { type: String, required: true, default: '默认值' }})
//方法3-弊端:不能设置默认值(使用withDefaults解决)
interface Props {
data?: number[]
}
//const props = defineProps<Props>();
const props = withDefaults(defineProps<Props>(), { data: () => [1, 2] })
const emit = defineEmits(['change', 'delete']) //声明从父组件来的事件
//将事件传递出去
emit('change', 参数);
emit('delete', 参数);
</script>
<!-- 父组件 -->
<template>
<child
v-model:isIpPanel="isIpPanel"
v-model:isNetworkPanel="isNetworkPanel"
@cancel="handleDelete"
></child>
</template>
function handleDelete() { }
// 子组件
const { proxy } = getCurrentInstance() //获取当前组件实例,需要放置最前面,不能放到函数里面
// defineModel可以在子组件直接更改父组件的值,不需要使用emit;一个公共组件在多个地方使用时,不需要重复写emit
const ipModel = defineModel('isIpPanel')
const netModel = defineModel('isNetworkPanel', false, {
event: 'update:isNetworkPanel'
})
const cancelFunc = () => {
// 方式1:
proxy.$emit('update:isIpPanel', false)
proxy.$emit('update:isNetworkPanel', false)
// 方式2:
ipModel.value = false
netModel.value = false
proxy.$emit('cancel', '传输的数据')
}
<!-- <script setup >模式将子组件的属性、方法暴露给父组件;setup()则是return返回 -->
<!--父组件parent.vue -->
<template>
<child ref="childRef"></child>
<div ref="divEl"></div>
</template>
<script setup lang="ts">
import child from './components/child.vue'
import type { ComponentInternalInstance } from 'vue'
//import { getCurrentInstance, ComponentInternalInstance } from 'vue'; 我用了自动导入,不需要引getCurrentInstance
//方法一(常用推荐):
//typeof P 是获取到类,InstanceType<类>是拿到类的实例,一个是类一个是实例不一样
//为了获取组件的类型,我们首先需要通过 `typeof` 得到其类型,再使用 TypeScript 内置的 `InstanceType` 工具类型来获取其实例类型:
//获取组件。这个变量名和 DOM 上的 ref 属性必须同名,会自动形成绑定。变量名不能和组件名同名,即chilRef不能命名为child
let childRef = ref<InstanceType<typeof child> | null>(null)
//获取dom
let divEl = ref<HTMLDivElement | null>(null)
//方法二:(不推荐)
//这样写会标红:类型“ComponentInternalInstance | null”上不存在属性“proxy”。使用类型断言
const { proxy } = getCurrentInstance() as ComponentInternalInstance
let parentNum = ref(1)
onMounted(() => {
console.log(divEl.value) //直接拿到dom本身
console.log(childRef.value?.msg) //.value的方式调用子组件的数据和方法(defineExpose暴露)
childRef.value?.open()
console.log(proxy?.$refs) //proxy对象{childRef: Proxy(Object), divEl: div}
})
defineExpose({
parentNum
})
</script>
<!--子组件-->
<script setup lang="ts">
import type { ComponentInternalInstance } from 'vue'
let msg: string = '111'
const open = function () {
console.log(222)
}
const { proxy } = getCurrentInstance() as ComponentInternalInstance
onMounted(() => {
//标红:类型“ComponentPublicInstance”上不存在属性“parentNum”
console.log('parent', proxy?.$parent?.parentNum)
})
defineExpose({
msg,
open
})
</script>
//parent.vue
<template>
<child
foo="222"
foo2="333"
class="child"
:style="{}"
@test="handleTest"
@test2="handleTest2"
></child>
</template>
<script setup lang="ts">
function handleTest() {}
function handleTest2() {}
</script>
//child.vue
<template>
<div></div>
</template>
<script setup lang="ts">
const props = defineProps(['foo2'])
const emits = defineEmits(['test2'])
console.log(props) //{foo2: '333'}
const attrs = useAttrs()
console.log(attrs) // {foo: '222', class: 'child', style: {…}, onTest: f handleTest(),onTest: f handleTest2()}
</script>
//main.ts
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.config.globalProperties.name = '猪八戒'
app.mount('#app')
// xx.vue
<script setup lang="ts">
import type { ComponentInternalInstance } from 'vue'
// proxy 就是当前组件实例,可以理解为组件级别的 this,没有全局的、路由、状态管理之类的
const { proxy, appContext } = getCurrentInstance() as ComponentInternalInstance
//global 就是全局实例
const global = appContext.config.globalProperties
console.log(global.name)
console.log(proxy?.name) //会标红
</script>
5.1 在 setup 中定义组件名称
<script lang="ts" setup>
defineOptions({
name: 'draft'
})
</script>
5.2 利用插件 vite-plugin-vue-setup-extend-plus vite-plugin-vue-setup-extend
<script setup lang="ts" name="home">
//
</script>
const obj = reactive({
a: 1,
b: 2,
c: {
d: 1,
e: 2
},
f: []
})
//法一:深层次,当直接侦听一个响应式对象时,侦听器会自动启用深层模式:
//可以监听到obj.f = [1]和obj.c.d ++
watch(obj, (newValue, oldValue) => {
})
//法二:深层次,必须写deep: true,不然浅层的也监听不到
watch(() => obj, (newValue, oldValue) => {
}, { deep: true })
//法三:浅层, 监听不到obj.f = [1]和obj.c.d ++
watch(() => { ...obj }, (newValue, oldValue) => {
})
//法一:
watch([() => obj.a, name], ([newA, newName], [oldA, oldName]) => {})
//法二:
watch(
() => [obj.a, obj.b],
(newValue, oldValue) => {}
)
watch(id, async (newId, oldId, onCleanup) => {
const { response, cancel } = doAsyncWork(newId)
// 当 `id` 变化时,`cancel` 将被调用,
// 取消之前的未完成的请求
onCleanup(cancel)
data.value = await response
})
watch: {
bookList: {
handler(val, oldVal) {
console.log('book list changed')
},
deep: true, //不会立即执行,只有值变化才会更新
// immediate:true, //会立即执行
},
}
import { watch, toRefs } from 'vue'
const props = defineProps({
visible: {
type: Boolean,
default: () => {
return false
}
}
})
// 方案一:
const visible = toRefs(props).visible //将数据转成响应式
watch(visible, (newValue, oldValue) => {
console.log(newValue)
})
// 方案二:
watch(
() => props.visible,
(newValue, oldValue) => {
console.log(newValue)
}
)
watch([() => user.name, () => user.sex], (newValue, oldValue) => {})
// 添加配置参数
watch(
() => user.hobbies,
(newValue, oldValue) => {},
{
immediate: true,
deep: true,
// 回调函数的执行时机,默认在组件更新之前执行,更新之后执行参数为‘post’
flush: 'pre'
}
)
//父级
const name = ref('猪八戒');
const changeName = (newName: string) => {
name.value = newName;
};
provide('name', name);
provide('changeName', changeName); //更改name的方法
//子级孙级
const name = inject('name') as string; //使用类型断言,不然会有红色波浪线
const changeName = inject('changeName') as Fn;
const props = defineProps({
status: {
type: String,
required: true,
validator: value => {
return ['created', 'loading', 'loaded'].includes(value)
}
}
})
<script lang="tsx">
import { defineComponent, ref, Transition } from 'vue'
export default defineComponent({
setup() {
const count = ref(0);
const handleClick = () => {
count.value ++;
}
return () => (
<div>
<button onClick={handleClick}>click me!</button>
<Transition name="slide-fade">
{count.value % 2 === 0 ?
<h1>count: {count.value}</h1>
: null}
</Transition>
</div>
)
}
})
</script>
https://github.com/fengcms/vue3-demo
<template v-slot:licon>
<img src="@/assets/img/help.png" />
</template>
<div>
<slot name="licon"/>
</div>
<template>
<Son>
<template #default><div>默认插槽内容</div></template>
<template #slotName><div>具名插槽内容</div></template>
<template #propsSlot="scope">
<div>
作用域插槽内容:name,{{scope.data.name}};age,{{scope.data.age}}
</div>
</template>
</Son>
</template>
<script setup>
import Son from './Son.vue'
<script>
@input在移动端不生效
@change 代替或者使用 @blur
// A.vue
<script>
import { provide } from 'vue'
export default {
setup() {
const obj = {
name: '前端印象',
age: 22
}
// 向子组件以及子孙组件传递名为info的数据
provide('info', obj)
}
}
</script>
// B.vue
<script>
import { inject } from 'vue'
export default {
setup() {
// 接收A.vue传递过来的数据
inject('info') // {name: '前端印象', age: 22}
}
}
</script>
// C.vue
<script>
import { inject } from 'vue'
export default {
setup() {
// 接收A.vue传递过来的数据
inject('info') // {name: '前端印象', age: 22}
const state = reactive({ count: 0 })
// watch监听多个值
const state = reactive({ count: 0, name: 'zs' })
watch(
[() => state.count, () => state.name],
([newCount, newName], [oldvCount, oldvName]) => {
console.log(oldvCount) // 旧的 count 值
console.log(newCount) // 新的 count 值
console.log(oldName) // 旧的 name 值
console.log(newvName) // 新的 name 值
}
)
}
}
</script>
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() =>
import('./components/AsyncComponent.vue')
)
app.component('async-component', AsyncComp)
<template>
<div>
<Suspense>
<template #default> //此处编写你的组件或者异步请求回来的数据 </template>
<template #fallback>
<h1>Loading...</h1>
</template>
</Suspense>
</div>
</template>
teleport 能使 Dom 不在#app 的跟节点下
<template>
<teleport to="#modal"> //需要渲染的DOM的id在。
<div id="center">
<h2>JSPang11</h2>
</div>
</teleport>
</template>
<script lang="ts">
export default {};
</script>
<style>
#center {
width: 200px;
height: 200px;
border: 2px solid black;
background: white;
position: fixed;
left: 50%;
top: 50%;
margin-left: -100px;
margin-top: -100px;
}
</style>
<template>
<div>点餐完毕</div>
<modal/>
</template>
import modal from "./components/Modal.vue";
const app = {
name: "App",
components: {
modal,
}
}
<div id="app"></div>
<div id="modal"></div>
//bus.js
import mitt from 'mitt'
const bus = {}
const emitter = mitt()
bus.$on = emitter.on
bus.$off = emitter.off
bus.$emit = emitter.emit
bus.$all = emitter.all.clear()
export default bus
import bus from '@/bus.js'
app.config.globalProperties.$bus = bus
proxy.$bus.$emit('dr-workOrd-change', 11)
proxy.$bus.$on("dr-workOrd-change", (val) => {
console.log('你的逻辑',val)
})
<img :src="getAssetsFile('images/home-0.png')" />
export const getAssetsFile = (url = '') => {
return new URL(`../assets/${url}`, import.meta.url).href
}
<template>
<div>
<button @click="callChildMethod">调用子组件的 increment 方法</button>
<ChildComponent ref="childComponentRef" />
</div>
</template>
<script setup>
// 子组件
defineExpose({ acceptParams, increment }) //暴露两个方法
function acceptParams(param) {
console.log('acceptParams ', param)
}
function increment(param) {
console.log('acceptParams ', param)
}
// 子组件
// 父组件
const childComponentRef = ref(null)
const callChildMethod = () => {
nextTick(() => {
console.log('---', childComponentRef.value)
childComponentRef.value.acceptParams()
})
}
// 父组件
</script>
<template>
<Comp1></Comp1>
<Comp2></Comp2>
</template>
<script setup lang="tsx">
import {
type FunctionalComponent,
h,
ref,
defineComponent,
Fragment
} from 'vue'
const count = ref(0)
const increment = () => count.value++
// 定义渲染函数
const renderContent = () => {
return h('div', [
h('p', `计数: ${count.value}`),
h('button', { onClick: increment }, '增加')
])
}
const Comp1 = defineComponent({
props: {
age: {
type: Number,
default: 18
}
},
setup(props) {
return () => {
// return renderContent()
return (
<>
<div onClick={increment}>333:{count.value}</div>
</>
)
}
}
})
const Comp2: FunctionalComponent = () => {
return h('div', { onClick: increment }, `Comp2: ${count.value}`)
}
</script>
npm i @vue/babel-plugin-jsx @vitejs/plugin-vue-jsx -D
//vite.config.js
import vueJsx from '@vitejs/plugin-vue-jsx'
plugins: [vueJsx()]
//.babelrc
"plugins": [ "@vue/babel-plugin-jsx"]
// tsconfig.json
"compilerOptions": {
"strict": true,
"jsx": "preserve",
"jsxFactory": "h",
"jsxFragmentFactory": "Fragment"
},