Loading... # Vue的一些特性 ## vue-template-compiler Vue 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层的 Vue 实例的数据。vue-template-compiler 是一个将 Vue 模板编译为渲染函数的工具,它是 Vue 的一个核心部分。 ### 插槽 是 Vue 组件中的一个重要特性,它提供了一种方式能在组件模板中预留一块区域,然后让父组件在使用子组件时,能将自己的内容注入到这块区域中。 #### 默认插槽 是没有名字(or名字是 default)的插槽。当一个组件的插槽没有匹配到父组件传入的内容时,它会显示默认插槽的内容。 在使用组件时,通常会在外部(即父组件)维护所有需要传入的数据和结构,然后将它们通过 props 或者插槽传入。而组件内部则负责决定这些数据和内容在模板中的摆放位置。 **插槽聚合**:是 Vue 中实现插槽的一种方式,它通过将父组件的模板和子组件的模板在一个共同的上下文中进行编译和渲染,实现了插槽的功能。 **合并渲染**:如果一个组件有多个插槽,那么在渲染时,所有的插槽内容都会被合并在一起,然后在各自对应的位置进行渲染。 #### 具名插槽 具名插槽是 Vue 组件中的一种特性,它允许你在组件模板中预留多个区域,并使用 `name` 属性对它们进行标识。当你在父组件中使用该组件时,你可以使用具有相同 `name` 的 `<template>` 标签将内容注入到相应的插槽中。 子组件Example: ```vue <template> <header> <slot name="header"></slot> </header> <main> <slot></slot> </main> <footer> <slot name="footer"></slot> </footer> </template> ``` 父组件调用Example: ```vue <MyComponent> <template #header> <h1>这是标题</h1> </template> <p>这是主要内容</p> <template #footer> <p>这是页脚</p> </template> </MyComponent> ``` 内容就会被插入到子组件对应的插槽中。 `name` 属性在这里的作用并不仅仅是作为索引,它还决定了插槽内容在哪里渲染,也就是说,每个插槽都会生成一个独立的节点。 在 Vue 中,你可以使用 v-slot 指令(或者在 Vue 3 中的 # 简写)在插槽中传递数据。 Example: ```vue <template> <button @click="handleClick"> <slot name="button" :clicked="clicked"></slot> </button> </template> ``` 父组件调用: ``` <MyComponent> <template #button="{ clicked }"> <span v-if="clicked">按钮已被点击</span> <span v-else>按钮未被点击</span> </template> </MyComponent> ``` 这样就实现了在插槽中混合传参的功能。 #### 作用域插槽 在Vue中,作用域插槽允许子组件在父组件定义的插槽结构内使用自己的数据。它允许父组件提供插槽的整体结构,而子组件决定插槽内部的具体内容或变量。 这样的设计使得组件之间的通信更加灵活。父组件提供插槽结构,子组件则能够填充这些结构,并且可以基于自己的数据、状态或逻辑来决定插槽内部的内容。 父组件Example: ```vue <template> <div> <header> <slot :data="headerData"> <!-- 默认内容 --> </slot> </header> <footer> <slot :data="footerData"> <!-- 默认内容 --> </slot> </footer> </div> </template> <script> export default { data() { return { headerData: { title: 'Header Title' }, footerData: { message: 'Footer Message' } }; } }; </script> ``` 子组件Example: ```vue <template> <div> <Layout> <!-- 使用作用域插槽 --> <template v-slot:default="slotProps"> <p>{{ slotProps.data.title }}</p> <p>{{ slotProps.data.message }}</p> </template> </Layout> </div> </template> <script> // 假设这个是父组件 import Layout from './Layout.vue'; export default { components: { Layout } }; </script> ``` 父组件 Layout 提供了两个插槽,分别是 header 和 footer。这两个插槽通过 `slotProps` 来传递数据给子组件。子组件内部则通过 `slotProps.data` 来访问父组件传递过来的数据,并根据这些数据渲染出实际内容。 父组件提供了整体结构,而子组件根据需要填充这些结构,使得组件之间的通信更为灵活和可定制。 ### 模板二次加工 `watch`和`computed`是Vue中用于响应式地监听数据变化并做出相应处理的机制。通过`watch`可以监听数据的变化并执行自定义的操作,而`computed`允许基于已有的数据计算出新的属性,这些属性会随着依赖的数据变化而自动更新。 其他方案: * 过滤器:Vue中的过滤器允许在模板中对数据进行一些简单的处理和转换。它们以独立的管道符形式 `|` 使用,但是无法直接访问Vue实例。过滤器主要用于在模板中对数据进行格式化显示。 * v-html:允许将模板中的数据作为HTML渲染。但是会带来了安全性问题,直接使用v-html渲染的内容,如果不经过严格的过滤,可能会引入恶意代码。 ### JSX 1. **指令实现:** * 在Vue模板中可以使用指令(Directives),比如`v-if`、`v-for`、`v-bind`等来操作DOM,响应用户交互,以及处理数据的展示和更新。 * 在JSX中,更接近于JavaScript语法,没有直接的指令概念,而是通过JavaScript来控制DOM的生成和操作。直接使用JavaScript的条件语句(`if`语句)、JavaScript的`map()`函数等来实现类似Vue指令的功能。 2. **JSX的优势和劣势:** * **优势:** * **更加自由:** JSX提供了更接近于JavaScript的语法,可以更灵活地组织和控制代码。 * **符合JavaScript的书写习惯与方式:** 可以使用JavaScript语法,更容易上手和理解。 * **劣势:** * **无法使用Vue指令:** JSX本身没有指令的概念,需要开发者通过原生JavaScript或其他库来实现类似的功能。 * **无法使用Vue的性能优化策略:** Vue框架针对模板进行了优化,例如静态分析、虚拟DOM等方式提高性能,而JSX不直接享受这些优化。 3. **Vue的编译路径和JSX:** * 在Vue中,模板会被编译为`render()`函数,这个函数生成了虚拟DOM,进而更新实际的DOM。 * 使用JSX时,代码本身更加直接地描述了要渲染的内容,但并不像Vue的模板那样经过特定的编译过程。因此,Vue的编译路径中会有模板到`render()`函数的转换,而JSX本身就是JavaScript的一部分,更直接地表达了UI的结构和逻辑。 ## 组件化 利用Vue中的组件系统来构建可复用、独立和可组合的代码块。 使用`Vue.component()`来创建全局可用的组件 Example: ```JavaScript Vue.component('my-component', { template: '<h1>Hello Dracowyn</h1>' }); new Vue({ el: '#app' }); ``` 这样做的好处包括: 1. **抽象复用:** 组件允许将页面分割成多个独立可复用的模块。这些组件可以在不同的地方使用,带来了代码复用性,减少了重复编写相似功能的代码。 2. **精简 & 聚合:** 通过组件化,可以将功能单一、可复用的代码片段封装为一个组件。这有助于提高代码的可维护性、可读性和可测试性,同时也使得对组件的修改更为方便。 ### 混入mixin - 逻辑混入 是Vue中一种重用组件中可复用功能的灵活方式。它允许你创建可重用的模块,其中包含一些组件可以使用的选项,如数据、方法或生命周期钩子。 在应用中,混入的使用场景常常是抽离一些公共逻辑,尤其是当多个组件之间有相同的逻辑,但模板结构不同的情况下。通过混入可以在多个组件中使用相同的逻辑代码,实现逻辑复用,减少冗余。 Example: ```JavaScript const mixinA = { data() { return { message: 'Mixin A Message', count: 0 }; } }; ``` ```JavaScript const componentB = { mixins: [mixinA], data() { return { message: 'Component B Message' }; } }; ``` `componentB`中的`message`属性会覆盖`mixinA`中的`message`属性,但`count`属性会被额外补充到`componentB`的数据中,不会覆盖现有属性。 假设有两个混入对象`mixinC`和`mixinD` Example: ```JavaScript const mixinC = { created() { console.log('Mixin C Created'); } }; const mixinD = { created() { console.log('Mixin D Created'); } }; ``` ```JavaScript const componentE = { mixins: [mixinC, mixinD], created() { console.log('Component E Created'); } }; ``` `Mixin C Created` -> `Mixin D Created` -> `Component E Created`。即使组件对象本身也有一个`created`生命周期钩子,混入对象的`created`钩子也会在组件的`created`钩子之前执行。 ### 继承拓展extends `extends`是用于实现组件扩展的一个选项。它允许你基于一个已有的组件来创建一个新的组件,同时可以添加新的属性和方法。 应用: #### 应用 * **核心逻辑的功能继承:** 通过`extends`可以在现有的组件基础上创建一个新组件,并且继承原组件的功能,同时可以进行拓展或修改,比如添加新的方法、改变现有方法的行为等。 #### 合并策略: - **变量补充维度:** 当子组件和父组件具有相同属性时,`extends`会额外补充父组件的属性到子组件,不会覆盖已存在的属性。 - **生命周期:**`extends`创建的组件的生命周期钩子会在父组件的钩子之后执行。无论是业务代码还是mixin中的生命周期,都会在`extends`之后执行。 Last modification:January 10, 2024 © Reprint prohibited Support Appreciate the author AliPayWeChat Like 1 If you think my article is useful to you, please feel free to appreciate