赞
踩
前言:参考Vue 官方文档,本文档主要学习组合式 API。
一款 JS 框架,并有两个核心功能:声明式渲染、响应性。
根据不同的需求场景,使用不同方式的 Vue,比如:
无需构建步骤,直接引入 vuejs。
在任何页面中作为 Web Components 嵌入
使用构建步骤,单页应用 (SPA)
全栈 / 服务端渲染 (SSR)
Jamstack / 静态站点生成 (SSG)
开发桌面端、移动端、WebGL,甚至是命令行终端中的界面
Vue 为什么可以称为“渐进式框架”:它是一个可以与你共同成长、适应你不同需求的框架。
单文件组件是 Vue 的标志性功能。*.vue、SFC 就是单文件组件:将一个组件的逻辑 (JS),模板 (HTML) 和样式 (CSS) 封装在同一个文件里。
选项式 API(Options API):包含多个选项的对象来描述组件的逻辑,例如 data、methods 和 mounted。
组合式 API(Composition API):使用导入的 API 函数来描述组件逻辑。通常会与 <script setup> 搭配使用。
综上:两种 API 是同一个底层系统构建的。选项式 API 是在组合式 API 的基础上实现的!
- // 安装并执行 create-vue,它是 Vue 官方的项目脚手架工具。
- npm create vue@latest
可以用于增强静态的 HTML 或与后端框架集成。但将无法使用单文件组件 (SFC) 语法:
- <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
-
- <div id="app">{{ message }}</div>
-
- <script>
- const { createApp, ref } = Vue;
-
- createApp({
- setup() {
- const message = ref("Hello vue!");
- return {
- message,
- };
- },
- }).mount("#app");
- </script>
通过 CDN 以及原生 ES 模块使用 Vue:
- <div id="app">{{ message }}</div>
-
- <script type="module">
- import {
- createApp,
- ref,
- } from "https://unpkg.com/vue@3/dist/vue.esm-browser.js";
-
- createApp({
- setup() {
- const message = ref("Hello Vue!");
- return {
- message,
- };
- },
- }).mount("#app");
- </script>
使用导入映射表 (Import Maps) 来告诉浏览器如何定位到导入的 vue:
- <script type="importmap">
- {
- "imports": {
- "vue": "https://unpkg.com/vue@3/dist/vue.esm-browser.js"
- }
- }
- </script>
-
- <div id="app">{{ message }}</div>
-
- <script type="module">
- import { createApp, ref } from "vue";
-
- createApp({
- setup() {
- const message = ref("Hello Vue!");
- return {
- message,
- };
- },
- }).mount("#app");
- </script>
分割代码成单独的 JS 文件,以便管理。
- <!-- index.html -->
- <div id="app"></div>
-
- <script type="module">
- import { createApp } from "vue";
- import MyComponent from "./my-component.js";
-
- createApp(MyComponent).mount("#app");
- </script>
- // my-component.js
- import { ref } from "vue";
- export default {
- setup() {
- const count = ref(0);
- return { count };
- },
- template: `<div>count is {{ count }}</div>`,
- };
注意:直接点击 index.html,会抛错误,因为 ES 模块不能通过 file:// 协议工作。只能通过 http:// 协议工作。需要启动一个本地的 HTTP 服务器,通过命令行在 HTML 文件所在文件夹下运行 npx serve。
通过 createApp 函数创建 Vue 应用实例:
- import { createApp } from "vue";
-
- const app = createApp({
- /* 根组件选项 */
- });
createApp 需要传入一个根组件,其他组件将作为其子组件:
- import { createApp } from "vue";
- // 从一个单文件组件中导入根组件
- import App from "./App.vue";
-
- const app = createApp(App);
调用 .mount() 方法,传入一个 DOM 元素或是 CSS 选择器。它的返回值是根组件实例而非应用实例:
<div id="app"></div>
- import { createApp } from "vue";
- // 从一个单文件组件中导入根组件
- import App from "./App.vue";
-
- const app = createApp(App);
- app.mount("#app");
注意:确保在挂载应用实例之前完成所有应用配置!
- import { createApp } from "vue";
- // 从一个单文件组件中导入根组件
- import App from "./App.vue";
-
- const app = createApp(App);
-
- // 应用实例的 .config 对象可以进行一些配置,例如配置错误处理器:用来捕获所有子组件上的错误:
- app.config.errorHandler = (err) => {
- /* 处理错误 */
- };
-
- // 全局挂载组件
- app.component("TodoDeleteButton", TodoDeleteButton);
-
- // 全局属性的对象。
- app.config.globalProperties.msg = "hello";
-
- app.mount("#app");
每个应用都拥有自己的用于配置和全局资源的作用域:
- const app1 = createApp({
- /* ... */
- });
- app1.mount("#container-1");
-
- const app2 = createApp({
- /* ... */
- });
- app2.mount("#container-2");
最基本的数据绑定是文本插值,使用“Mustache”语法 (即双大括号):
<span>Message: {{ msg }}</span>
双大括号会将数据解释为纯文本,若想插入 HTML,需要使用 v-html 指令。
安全警告:动态渲染 HTML 是很危险的,容易造成 XSS 漏洞。仅在内容安全可信时再使用 v-html,并且永远不要使用用户提供的 HTML 内容。
- <p>Using text interpolation: {{ rawHtml }}</p>
- <p>Using v-html directive: <span v-html="rawHtml"></span></p>
绑定 attribute,使用 v-bind 指令:
- <div v-bind:id="dynamicId"></div>
- <!-- 简写 -->
- <div :id="dynamicId"></div>
绑定多个值,通过不带参数的 v-bind。
- const objectOfAttrs = {
- id: "container",
- class: "wrapper",
- };
<div v-bind="objectOfAttrs"></div>
数据绑定都支持完整的 JS 表达式,也就是一段能够被求值的 JS 代码。一个简单的判断方法是是否可以合法地写在 return 后面:
- {{ number + 1 }}
-
- {{ ok ? 'YES' : 'NO' }}
-
- {{ message.split('').reverse().join('') }}
-
- <div :id="`list-${id}`"></div>
可以在绑定的表达式中使用一个组件暴露的方法:
- <time :title="toTitleDate(date)" :datetime="date">
- {{ formatDate(date) }}
- </time>
指 v- 前缀的特殊 attribute。它的值为 JS 表达式(v-for、v-on、v-slot 除外),指令值变化时响应式的更新 DOM,比如 v-if:
<p v-if="seen">Now you see me</p>
带参数的指令,用':'隔开:
- <a v-bind:href="url"> ... </a>
- <!-- 简写 -->
- <a :href="url"> ... </a>
指令的参数,也可以动态绑定,用 '[ ]' 包裹:
- <a v-bind:[attributeName]="url"> ... </a>
- <!-- 简写 -->
- <a :[attributeName]="url"> ... </a>
带修饰符的指令,用 '.' 隔开:
- <!-- 触发的事件调用 event.preventDefault() -->
- <form @submit.prevent="onSubmit">...</form>
官方推荐使用 ref() 函数来声明响应式状态:
- import { ref } from "vue";
-
- const count = ref(0);
ref() 接收参数,并返回一个带有 .value 属性的 ref 对象:
- const count = ref(0);
-
- console.log(count); // { value: 0 }
- console.log(count.value); // 0
-
- count.value++;
- console.log(count.value); // 1
在模板中使用 ref 变量,不需要添加 .value。ref 会自动解包。也可以直接在事件监听器中改变一个 ref:
<button @click="count++">{{ count }}</button>
通过单文件组件(SFC),使用 <script setup> 来大幅度地简化代码:
- <script setup>
- import { ref } from "vue";
-
- const count = ref(0);
-
- function increment() {
- count.value++;
- }
- </script>
-
- <template>
- <button @click="increment">
- {{ count }}
- </button>
- </template>
为什么使用 ref,而不是普通的变量。这是因为 Vue 需要通过.value 属性来实现状态响应性。基础原理是在 getter 中追踪,在 setter 中触发:
- // 伪代码,不是真正的实现
- const myRef = {
- _value: 0,
- get value() {
- track();
- return this._value;
- },
- set value(newValue) {
- this._value = newValue;
- trigger();
- },
- };
要等待 DOM 更新完成后再执行额外的代码,可以使用 nextTick() 全局 API:
- import { nextTick } from "vue";
-
- async function increment() {
- count.value++;
- await nextTick();
- // 现在 DOM 已经更新了
- }
reactive(),参数只能是对象类型,返回的是一个原始对象的 Proxy,它和原始对象是不相等的:
- const raw = {};
- const proxy = reactive(raw);
-
- // 代理对象和原始对象不是全等的
- console.log(proxy === raw); // false
reactive() 局限性包括:只能用于对象类型(对象,数组,Map,Set)、不能替换整个对象、对结构操作不友好:
- let state = reactive({ count: 0 });
- // 上面的 ({ count: 0 }) 引用将不再被追踪
- // (响应性连接已丢失!)
- state = reactive({ count: 1 });
-
- // 对解构不友好
- const state = reactive({ count: 0 });
- // 当解构时,count 已经与 state.count 断开连接
- let { count } = state;
- // 不会影响原始的 state
- count++;
- // 该函数接收到的是一个普通的数字
- // 并且无法追踪 state.count 的变化
- // 我们必须传入整个对象以保持响应性
- callSomeFunction(state.count);
reactive() API 有一些局限性,官方建议使用 ref() 作为声明响应式状态的主要 API。博主个人还是喜欢 ref,reactive 混着用,注意那些局限性就可以了。
一个 ref 会在作为响应式对象的属性被访问或修改时自动解包:
- const count = ref(0);
- const state = reactive({
- count,
- });
-
- console.log(state.count); // 0
-
- state.count = 1;
- console.log(count.value); // 1
computed() 方法期望接收一个 getter 函数,返回为一个计算属性 ref:
- <script setup>
- import { reactive, computed } from "vue";
-
- const author = reactive({
- name: "John Doe",
- books: [
- "Vue 2 - Advanced Guide",
- "Vue 3 - Basic Guide",
- "Vue 4 - The Mystery",
- ],
- });
-
- // 一个计算属性 ref
- const publishedBooksMessage = computed(() => {
- return author.books.length > 0 ? "Yes" : "No";
- });
- </script>
-
- <template>
- <p>Has published books:</p>
- <span>{{ publishedBooksMessage }}</span>
- </template>
计算属性值会基于其响应式依赖被缓存。方法调用则总会在重新渲染时再次执行。
计算属性默认是只读的。但可以通过设置 get 和 set 函数变成可读可写:
- <script setup>
- import { ref, computed } from "vue";
-
- const firstName = ref("John");
- const lastName = ref("Doe");
-
- const fullName = computed({
- // getter
- get() {
- return firstName.value + " " + lastName.value;
- },
- // setter
- set(newValue) {
- // 注意:我们这里使用的是解构赋值语法
- [firstName.value, lastName.value] = newValue.split(" ");
- },
- });
- </script>
使用计算属性,不要在里面做异步请求和修改 DOM。并且尽量保持只读。
通过对象来动态切换 class:
<div :class="{ active: isActive }"></div>
可以直接绑定一个对象:
- const classObject = reactive({
- active: true,
- "text-danger": false,
- });
<div :class="classObject"></div>
通过数组渲染多个 class:
- const activeClass = ref("active");
- const errorClass = ref("text-danger");
<div :class="[activeClass, errorClass]"></div>
数组中也可以使用 JS 表达式:
- <div :class="[isActive ? activeClass : '', errorClass]"></div>
- <!-- 等于 -->
- <div :class="[{ activeClass: isActive }, errorClass]"></div>
如果组件有多个根元素,透传的 class 需要通过组件的 $attrs 属性来实现指定:
<MyComponent class="baz" />
- <!-- MyComponent 模板使用 $attrs 时 -->
- <p :class="$attrs.class">Hi!</p>
- <span>This is a child component</span>
- <p class="baz">Hi!</p>
- <span>This is a child component</span>
值为对象,对应的是 style 属性:
- const activeColor = ref("red");
- const fontSize = ref(30);
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
直接绑定一个样式对象使模板更加简洁:
- const styleObject = reactive({
- color: "red",
- fontSize: "13px",
- });
<div :style="styleObject"></div>
还可以绑定一个包含多个样式对象的数组:
<div :style="[baseStyles, overridingStyles]"></div>
v-if 指令用于条件性地渲染内容。当值为真时才被渲染:
<h1 v-if="awesome">Vue is awesome!</h1>
v-else 为 v-if 添加一个“else 区块”。并且必须跟在一个 v-if 或者 v-else-if 元素后面:
- <button @click="awesome = !awesome">Toggle</button>
-
- <h1 v-if="awesome">Vue is awesome!</h1>
- <h1 v-else>Oh no 本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】推荐阅读