导航


HTML

CSS

JavaScript

浏览器 & 网络

版本管理

框架

构建工具

TypeScript

性能优化

软实力

算法

UI、组件库

Node

业务技能

针对性攻坚

AI


createApp 创建 App 实例

const app = createApp({
  name: 'MyApp',
  data() {},
  ...
})

// 相当于 Vue2
const vm = new Vue({
  name: 'App',
  ...
})

app.component

// Vue2.0
Vue.component();

// Vue3.0
const app = createApp(App);
app.component('组件名', 组件对象); // 全局注册

app.mount

app.unmount

app.config

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')

Untitled

🔥 app.config.globalProperties

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>

⚪️ 组件与全局属性同名的 props 优先级更高

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”的假设,这不仅有学习成本,而且有实现成本。取而代之的是,建议用方法调用或计算属性来替换它们。

app.use 注册插件

案例:自定义组件

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 refs

ref 绑定在元素上

<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>

ref 绑定在组件上

<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>

v-for 中绑定 ref

<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>

Untitled

🔥 例外情况 <script setup>

有一个例外的情况,使用了 <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 都会自动解包,和一般的实例一样)。