导航
const app = createApp({
name: 'MyApp',
data() {},
...
})
// 相当于 Vue2
const vm = new Vue({
name: 'App',
...
})
app.component 返回 app 实例app.component 返回 undefined// Vue2.0
Vue.component();
// Vue3.0
const app = createApp(App);
app.component('组件名', 组件对象); // 全局注册
#app)import { createApp } from 'vue'
import App from './App_Lifecycle_onRenderTracked_onRenderTriggered.vue'
const app = createApp(App)
console.log('app:', app);
console.log('app.config:', app.config);
app.mount('#app')

Vue2.0 中的 Vue.prototype.$http = 全局挂载utils/index.js
function add(a, b) {
return a + b;
}
export {
add
}
main.js
import utils from '@/utils';
// Vue2.0
Vue.prototype.utils = utils;
// Vue3.0
app.config.globalProperties.utils = utils;
App.vue
<template></template>
<script>
import { getCurrentInstance } from 'vue';
export default {
name: 'App',
setup() { // setup 中使用全局挂载的属性或方法
const { proxy } = getCurrentInstance();
console.log(proxy.utils.add(1, 2));
},
mounted() { // 实例上使用
console.log(this.utils.add(1, 2));
}
}
</script>
App.vue 传入同名 utils
<template>
<hello-world :utils="utils" />
</template>
<script>
import { getCurrentInstance } from 'vue';
import HelloWorld from '@/components/HelloWorld';
export default {
name: 'App',
components: {
HelloWorld
},
setup() {
const { proxy } = getCurrentInstance();
console.log(proxy.utils); // 这里还是 add
const utils = { a: 1 };
return {
utils
}
},
mounted() {
console.log(this.utils); // 这里也被改成了 { a: 1 }
}
}
</script>
HelloWorld.vue 组件接收
<template></template>
<script>
import { getCurrentInstance } from 'vue';
export default {
name: 'HelloWorld',
props: {
utils: Object
},
setup() {
const { proxy } = getCurrentInstance();
console.log(proxy.utils); // 变为 { a:1 }
},
mounted() {
console.log(this.utils); // 变为 { a:1 }
}
}
</script>
过滤器在 Vue3 被移除了,我们只好可以使用全局函数代替 Filters
app.config.globalProperties.$filters = {
format<T extends any>(str: T): string {
return `$${str}`
}
}
如果用 TS,得声明文件,不然 TS 无法正确类型推导
type Filter = {
format<T>(str: T): string
}
// 声明要扩充@vue/runtime-core包的声明.
// 这里扩充"ComponentCustomProperties"接口, 因为他是vue3中实例的属性的类型.
declare module 'vue' {
export interface ComponentCustomProperties {
$filters: Filter
}
}
为什么移除 filter?
因为 filter 需要一个自定义语法,打破了 {{}} 大括号内的表达式“只是 JavaScript”的假设,这不仅有学习成本,而且有实现成本。取而代之的是,建议用方法调用或计算属性来替换它们。
libs/MyUI
import MyButton from './MyButton';
import MyInput from './MyInput';
const componentPool = [
MyButton,
MyInput
]
export default {
install(app, options) {
if (options.components) { // 按需加载
options.components.map(c => {
componentPool.map(component => {
console.log(component.name, c);
if (component.name === c) {
app.component(component.name, component);
}
});
});
} else {
// 默认全局加载
componentPool.map(component => {
app.component(component.name, component);
});
}
}
}
<template>
<button class="my-button"><slot></slot></button>
</template>
<script>
export default {
name: 'MyButton'
}
</script>
<style>
.my-button {
background-color: orange;
color: #fff;
}
</style>
<template>
<input type="text" class="my-input" :placeholder="placeholderText">
</template>
<script>
export default {
name: 'MyInput',
props: {
placeholderText: {
type: String,
default: 'This is my input'
}
}
}
</script>
<style>
.my-input {
border: 1px solid blue;
}
</style>
<template>
<div>
<my-button>我的button</my-button>
<my-input placeholderText="my input"></my-input>
</div>
</template>
<script>
export default {
name: 'App',
setup() {
}
}
</script>
import { createApp } from 'vue';
import App from './App_use.vue'
import MyUI from './libs/MyUI';
const app = createApp(App)
app.use(MyUI, {
components: [
'MyButton',
'MyInput'
]
});
app.mount('#app')
<template>
<div>
<span ref="child">张三</span>
<button @click="changeName">改变名字</button>
</div>
</template>
<script>
import { ref } from 'vue';
export default {
name: 'Child',
setup() {
const child = ref(null);
const changeName = () => {
child.value.innerText = '李四';
console.log(child.value); // <span>李四</span>
}
return {
child,
changeName
}
}
}
</script>
<template>
<div>
<child ref="child" />
<button @click="changeName">改变child内容</button>
</div>
</template>
<script>
import { ref } from 'vue';
import Child from '@/components/Child';
export default {
name: 'App',
components: {
Child
},
setup() {
const child = ref(null);
const changeName = () => {
console.log(child.value); // child 组件实例
// 改变名字方案1 - 通过 $el 获取 dom 更新
// const container = child.value.$el;
// const span = container.getElementsByTagName('span')[0];
// span.innerText = 'Lance';
// 改变名字方案2 - 直接调用 child 组件的 changeName 方法
child.value.changeName();
}
return {
child,
changeName
}
}
}
</script>
<template>
<ul>
<li
v-for="(student, idx) of students"
:key="idx"
:ref="el => { if (el) list[idx] = el }"
>
{{ student.name }}
</li>
</ul>
</template>
<script>
import { reactive, ref, onMounted } from 'vue';
export default {
name: 'App',
setup() {
const list = ref([]);
const students = reactive([
{
id: 1,
name: 'Lance'
},
{
id: 2,
name: 'GC'
},
{
id: 3,
name: 'Sherry'
}
]);
onMounted(() => {
console.log(list.value);
console.log(list.value[0]);
});
return {
students,
list
}
}
}
</script>

有一个例外的情况,使用了 <script setup> 的组件是默认私有的:一个父组件无法访问到一个使用了 <script setup> 的子组件中的任何东西,除非子组件在其中通过 defineExpose 宏显式暴露:
<script setup>
import { ref } from 'vue'
const a = 1
const b = ref(2)
defineExpose({
a,
b
})
</script>
当父组件通过模板引用获取到了该组件的实例时,得到的实例类型为 { a: number, b: number } (ref 都会自动解包,和一般的实例一样)。