首页IT科技vue必会知识(Vue的基础知识(三))

vue必会知识(Vue的基础知识(三))

时间2025-05-05 17:52:39分类IT科技浏览3154
导读:本章将继续和大家分享Vue的一些基础知识。话不多说,下面我们直接上代码:...

本章将继续和大家分享Vue的一些基础知识           。话不多说           ,下面我们直接上代码:

本文内容大部分摘自Vue的官网:https://v2.cn.vuejs.org/v2/guide/

首先我们先来看一下Demo的目录结构                ,如下所示:

一           、侦听器

二                、Vue组件基础

自定义组件<button-counter> 代码如下:

define([ axios ], function (axios) { /* 因为组件是可复用的 Vue 实例     ,所以它们与 new Vue 接收相同的选项           ,例如 data     、computed      、watch                、methods 以及生命周期钩子等                。 仅有的例外是像 el 这样根实例特有的选项     。 */ return { template: <button v-on:click="count++">You clicked me {{ count }} times.</button>, props: [], //一个组件的 data 选项必须是一个函数                 ,因此每个实例可以维护一份被返回对象的独立的拷贝 data: function () { return { count: 0 } }, mounted: function () { }, methods: { }, watch: { } }; });

自定义组件 <blog-post> 代码如下:

define([ axios ], function (axios) { /* 因为组件是可复用的 Vue 实例     ,所以它们与 new Vue 接收相同的选项     ,例如 data           、computed      、watch                、methods 以及生命周期钩子等      。 仅有的例外是像 el 这样根实例特有的选项                。 */ return { //每个组件必须只有一个根元素 template: ` <div class="blog-post" desc="根元素"> <h3>{{ post.title }}</h3> <p>子组件中的titleNew:<input type="text" v-model="titleNew"></p> <slot></slot> <button @click="handleEnlargeText">Enlarge text</button> <div v-html="post.content"></div> </div> `, /* 1           、通过 Prop 向子组件传递数据           。 2、一个组件默认可以拥有任意数量的 prop                 ,任何值都可以传递给任何 prop      。 在上述模板中           ,你会发现我们能够在组件实例中访问这个值     ,就像访问 data 中的值一样                。 3                、所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中                ,但是反过来则不行           。 这样会防止从子组件意外变更父级组件的状态           ,从而导致你的应用的数据流向难以理解。 */ props: [post, title], //一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝 data: function () { return { enlargeFontSize: 0.1, //需要放大字体的大小 titleNew: this.title, //初始值为props中父组件传递过来的值 } }, mounted: function () { }, methods: { //处理放大文本字体 handleEnlargeText: function () { var _this = this; //子组件可以通过调用内建的 $emit 方法并传入事件名称来触发一个父组件的事件 //第二个参数为调用父组件事件所需传的参数 //enlarge-text为自定义事件 _this.$emit(enlarge-text, _this.enlargeFontSize); } }, watch: { //监听器完整写法 title: { handler(newValue, oldValue) { this.titleNew = newValue; }, //deep: true, // 深度监听 immediate: true // 强制立即执行回调(一般用于父组件向子组件动态传值时) }, //监听器简写                ,当需要设置 deep 或者 immediate 时需使用完整写法 titleNew: function (newValue, oldValue) { /* 注意在 JavaScript 中对象和数组是通过引用传入的                ,所以对于一个数组或对象类型的 prop 来说, 在子组件中改变变更这个对象或数组本身将会影响到父组件的状态                。 这种情况下就不需要以 update:myPropName 的模式触发更新事件了                。 */ this.$emit(update:title, newValue); //更新父组件title属性绑定的值 /* 自定义事件 .sync 修饰符: 在有些情况下           ,我们可能需要对一个 prop 进行“双向绑定           ”。 不幸的是                ,真正的双向绑定会带来维护上的问题     ,因为子组件可以变更父组件           ,且在父组件和子组件两侧都没有明显的变更来源           。 这也是为什么我们推荐以 update:myPropName 的模式触发事件取而代之                。 举个例子                 ,在一个包含 title prop 的假设的组件中     ,我们可以用以下方法表达对其赋新值的意图: this.$emit(update:title, newTitle) 然后父组件可以监听那个事件并根据需要更新一个本地的数据 property     。例如: <text-document v-bind:title="doc.title" v-on:update:title="doc.title = $event"> </text-document> 为了方便起见     ,我们为这种模式提供一个缩写                 ,即 .sync 修饰符: <text-document :title.sync="doc.title"></text-document> */ } } }; });

Vue组件基础.html 代码如下:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue组件基础</title> </head> <body> <div id="app"> <button-counter></button-counter> <button-counter></button-counter> <button-counter></button-counter> <div :style="{ fontSize: postFontSize + em }"> <blog-post v-for="post in posts" :key="post.id" :title.sync="post.title" :post="post" v-on:enlarge-text="onEnlargeText"> <!-- 默认插槽的内容 --> <template v-slot:default> <p>父组件中的post.title:<input type="text" v-model="post.title" /></p> </template> </blog-post> </div> </div> <script src="/js/lib/require.js"></script> <script src="/js/common/require_config.js"></script> <script src="/js/ComponentsDemo.js"></script> </body> </html>

其中 ComponentsDemo.js 代码如下:

//Vue组件基础 require([../common/base, ../components/blogPost], function (base, blogPost) { let axios = base.axios; var vm = new base.vue({ el: #app, //挂载点 mixins: [base.mixin], //混入           ,类似基类的概念 components: { blog-post: blogPost //局部注册组件     ,注意局部注册的组件在其子组件中不可用           。 }, data: { posts: [ { id: 1, title: My journey with Vue }, { id: 2, title: Blogging with Vue }, { id: 3, title: Why Vue is so fun } ], postFontSize: 1 }, //created钩子函数 created: function () { console.log(This is index created); }, //mounted钩子函数 mounted: function () { console.log(This is index mounted); }, //方法 methods: { //放大文本 onEnlargeText: function (enlargeFontSize) { var _this = this; _this.postFontSize += enlargeFontSize } } }); });

其中require_config.js 代码如下:

//主要用来配置模块的加载位置(设置短模块名) require.config({ baseUrl: /js/lib, //设置根目录 paths: { //如果没有设置根目录则需要填写完整路径 vue: vue, axios: axios, jquery: jquery-3.6.3, //paths还有一个重要的功能                ,就是可以配置多个路径           ,如果远程cdn库没有加载成功,可以加载本地的库                ,如下: //jquery: [http://libs.baidu.com/jquery/2.0.3/jquery, /js/lib/jquery-3.6.3], } });

其中base.js 代码如下:

//define用来自定义模块 //第一个参数:加载依赖模块                ,可以是require_config中定义的短模块名,也可以是完整的模块路径(去掉.js后缀名) //第二个参数:执行加载完后的回调函数 define([vue, axios, ../components/buttonCounter], function (vue, axios, buttonCounter) { //TODO 此处可以处理一些公共的逻辑 //vue.component(component-a, { /* ... */ }); //全局注册组件 //vue.mixin({...}); //全局混入 /* 定义组件名的方式有两种: 1                、使用 kebab-case (短横线分隔命名) 当使用 kebab-case (短横线分隔命名) 定义一个组件时           ,你也必须在引用这个自定义元素时使用 kebab-case                ,例如 <my-component-name> 2、使用 PascalCase (首字母大写命名) 当使用 PascalCase (首字母大写命名) 定义一个组件时     ,你在引用这个自定义元素时两种命名法都可以使用                。 也就是说 <my-component-name> 和 <MyComponentName> 都是可接受的     。 注意           ,尽管如此                 ,直接在 DOM (即非字符串的模板) 中使用时只有 kebab-case 是有效的      。 */ //Vue.component(...) 的第一个参数为组件名                。 vue.component(button-counter, buttonCounter); //全局注册 return { vue: vue, axios: axios, //Vue混入 mixin: { //数据 data: function () { return { domain: , //域名 } }, //组件 components: { }, //created钩子函数 created: function () { console.log(This is base created); }, //mounted钩子函数 mounted: function () { console.log(This is base mounted); }, //方法 methods: { //测试 doTest: function () { console.log(This is base doTest); }, //获取域名 getDomain: function () { var _this = this; _this.domain = https://www.baidu.com; }, } }, }; });

运行结果如下:

三           、组件注册

1                、组件名大小写

定义组件名的方式有两种:

1)使用 kebab-case

Vue.component(my-component-name, { /* ... */ })

当使用 kebab-case (短横线分隔命名) 定义一个组件时     ,你也必须在引用这个自定义元素时使用 kebab-case     ,例如<my-component-name>           。

2)使用 PascalCase

Vue.component(MyComponentName, { /* ... */ })

当使用 PascalCase (首字母大写命名) 定义一个组件时                 ,你在引用这个自定义元素时两种命名法都可以使用      。也就是说<my-component-name>和<MyComponentName>都是可接受的                。注意           ,尽管如此     ,直接在 DOM (即非字符串的模板) 中使用时只有 kebab-case 是有效的           。

2     、全局注册

到目前为止                ,我们用过Vue.component来创建组件:

Vue.component(my-component-name, { // ... 选项 ... })

这些组件是全局注册的。也就是说它们在注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中                。

3           、局部注册

全局注册往往是不够理想的                。比如           ,如果你使用一个像 webpack 这样的构建系统,全局注册所有的组件意味着即便你已经不再使用一个组件了                ,它仍然会被包含在你最终的构建结果中。这造成了用户下载的 JavaScript 的无谓的增加           。

在这些情况下                ,你可以通过一个普通的 JavaScript 对象来定义组件:

var ComponentA = { /* ... */ } var ComponentB = { /* ... */ } var ComponentC = { /* ... */ }

然后在components选项中定义你想要使用的组件:

new Vue({ el: #app, components: { component-a: ComponentA, component-b: ComponentB } })

对于components对象中的每个 property 来说,其 property 名就是自定义元素的名字           ,其 property 值就是这个组件的选项对象                。

注意局部注册的组件在其子组件中不可用     。例如                ,如果你希望ComponentA在ComponentB中可用     ,则你需要这样写:

var ComponentA = { /* ... */ } var ComponentB = { components: { component-a: ComponentA }, // ... }

四                、组件中的Prop

1     、Prop 的大小写 (camelCase vs kebab-case)

HTML 中的 attribute 名是大小写不敏感的           ,所以浏览器会把所有大写字符解释为小写字符           。这意味着当你使用 DOM 中的模板时                 ,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:

Vue.component(blog-post, { // 在 JavaScript 中是 camelCase 的 props: [postTitle], template: <h3>{{ postTitle }}</h3> })
<!-- 在 HTML 中是 kebab-case 的 --> <blog-post post-title="hello!"></blog-post>

重申一次     ,如果你使用字符串模板     ,那么这个限制就不存在了                。

2      、Prop 类型

到这里                 ,我们只看到了以字符串数组形式列出的 prop:

props: [title, likes, isPublished, commentIds, author]

但是           ,通常你希望每个 prop 都有指定的值类型     。这时     ,你可以以对象形式列出 prop                ,这些 property 的名称和值分别是 prop 各自的名称和类型:

props: { title: String, likes: Number, isPublished: Boolean, commentIds: Array, author: Object, callback: Function, contactsPromise: Promise // or any other constructor }

这不仅为你的组件提供了文档           ,还会在它们遇到错误的类型时从浏览器的 JavaScript 控制台提示用户      。

3                、单向数据流

所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行                。这样会防止从子组件意外变更父级组件的状态                ,从而导致你的应用的数据流向难以理解           。

额外的                ,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值      。这意味着你不应该在一个子组件内部改变 prop                。如果你这样做了           ,Vue 会在浏览器的控制台中发出警告           。

这里有两种常见的试图变更一个 prop 的情形:

1)这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。

在这种情况下                ,最好定义一个本地的 data property 并将这个 prop 用作其初始值:

props: [initialCounter], data: function () { return { counter: this.initialCounter } }

2)这个 prop 以一种原始的值传入且需要进行转换                。

在这种情况下     ,最好使用这个 prop 的值来定义一个计算属性:

props: [size], computed: { normalizedSize: function () { return this.size.trim().toLowerCase() } }

注意在 JavaScript 中对象和数组是通过引用传入的           ,所以对于一个数组或对象类型的 prop 来说                 ,在子组件中改变变更这个对象或数组本身将会影响到父组件的状态                。

五           、自定义事件

1      、事件名

不同于组件和 prop     ,事件名不存在任何自动化的大小写转换。而是触发的事件名需要完全匹配监听这个事件所用的名称           。举个例子     ,如果触发一个 camelCase 名字的事件:

this.$emit(myEvent)

则监听这个名字的 kebab-case 版本是不会有任何效果的:

<!-- 没有效果 --> <my-component v-on:my-event="doSomething"></my-component>

不同于组件和 prop                 ,事件名不会被用作一个 JavaScript 变量名或 property 名           ,所以就没有理由使用 camelCase 或 PascalCase 了                。并且v-on事件监听器在 DOM 模板中会被自动转换为全小写 (因为 HTML 是大小写不敏感的)     ,所以v-on:myEvent将会变成v-on:myevent——导致myEvent不可能被监听到     。

因此                ,我们推荐你始终使用 kebab-case 的事件名           。

2                、.sync 修饰符

在有些情况下           ,我们可能需要对一个 prop 进行“双向绑定                ”                。不幸的是,真正的双向绑定会带来维护上的问题                ,因为子组件可以变更父组件                ,且在父组件和子组件两侧都没有明显的变更来源     。

这也是为什么我们推荐以update:myPropName的模式触发事件取而代之      。举个例子,在一个包含titleprop 的假设的组件中           ,我们可以用以下方法表达对其赋新值的意图:

this.$emit(update:title, newTitle)

然后父组件可以监听那个事件并根据需要更新一个本地的数据 property                。例如:

<text-document v-bind:title="doc.title" v-on:update:title="doc.title = $event" ></text-document>

为了方便起见                ,我们为这种模式提供一个缩写     ,即.sync修饰符:

<text-document v-bind:title.sync="doc.title"></text-document>

六           、插槽

1、具名插槽

有时我们需要多个插槽           。例如对于一个带有如下模板的<base-layout>组件:

<div class="container"> <header> <!-- 我们希望把页头放这里 --> </header> <main> <!-- 我们希望把主要内容放这里 --> </main> <footer> <!-- 我们希望把页脚放这里 --> </footer> </div>

对于这样的情况           ,<slot>元素有一个特殊的 attribute:name      。这个 attribute 可以用来定义额外的插槽:

<div class="container"> <header> <slot name="header"></slot> </header> <main> <slot></slot> </main> <footer> <slot name="footer"></slot> </footer> </div>

一个不带name的<slot>出口会带有隐含的名字“default     ”                。

在向具名插槽提供内容的时候                 ,我们可以在一个<template>元素上使用v-slot指令     ,并以v-slot的参数的形式提供其名称:

<base-layout> <template v-slot:header> <h1>Here might be a page title</h1> </template> <template v-slot:default> <p>A paragraph for the main content.</p> <p>And another one.</p> </template> <template v-slot:footer> <p>Heres some contact info</p> </template> </base-layout>

现在<template>元素中的所有内容都将会被传入相应的插槽           。

最终渲染结果如下所示:

<div class="container"> <header> <h1>Here might be a page title</h1> </header> <main> <p>A paragraph for the main content.</p> <p>And another one.</p> </main> <footer> <p>Heres some contact info</p> </footer> </div>

注意 v-slot 只能添加在 <template> 上 (只有一种例外情况)     ,这一点和已经废弃的 slot attribute 不同。

2                、后备内容

有时为一个插槽设置具体的后备 (也就是默认的) 内容是很有用的                 ,它只会在没有提供内容的时候被渲染                。例如在一个<submit-button>组件中:

<button type="submit"> <slot></slot> </button>

我们可能希望这个<button>内绝大多数情况下都渲染文本“Submit      ”                。为了将“Submit                ”作为后备内容           ,我们可以将它放在<slot>标签内:

<button type="submit"> <slot>Submit</slot> </button>

现在当我在一个父级组件中使用<submit-button>并且不提供任何插槽内容时:

<submit-button></submit-button>

后备内容“Submit           ”将会被渲染:

<button type="submit"> Submit </button>

但是如果我们提供内容:

<submit-button> Save </submit-button>

则这个提供的内容将会被渲染从而取代后备内容:

<button type="submit"> Save </button>
3                、动态插槽名

动态指令参数也可以用在 v-slot 上     ,来定义动态的插槽名:

<base-layout> <template v-slot:[dynamicSlotName]> ... </template> </base-layout>
4、具名插槽的缩写

跟v-on和v-bind一样                ,v-slot也有缩写           ,即把参数之前的所有内容 (v-slot:) 替换为字符#。例如v-slot:header可以被重写为#header:

<base-layout> <template #header> <h1>Here might be a page title</h1> </template> <template #default> <p>A paragraph for the main content.</p> <p>And another one.</p> </template> <template #footer> <p>Heres some contact info</p> </template> </base-layout>

七           、混入

1                、基础

混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能           。一个混入对象可以包含任意组件选项                。当组件使用混入对象时                ,所有混入对象的选项将被“混合      ”进入该组件本身的选项     。

例子:

// 定义一个混入对象 var myMixin = { created: function () { this.hello() }, methods: { hello: function () { console.log(hello from mixin!) } } } // 定义一个使用混入对象的组件 var Component = Vue.extend({ mixins: [myMixin] }) var component = new Component() // => "hello from mixin!"
2     、选项合并

当组件和混入对象含有同名选项时                ,这些选项将以恰当的方式进行“合并                ”           。

比如,数据对象在内部会进行递归合并           ,并在发生冲突时以组件数据优先                。

var mixin = { data: function () { return { message: hello, foo: abc } } } new Vue({ mixins: [mixin], data: function () { return { message: goodbye, bar: def } }, created: function () { console.log(this.$data) // => { message: "goodbye", foo: "abc", bar: "def" } } })

同名钩子函数将合并为一个数组                ,因此都将被调用     。另外     ,混入对象的钩子将在组件自身钩子之前调用      。

var mixin = { created: function () { console.log(混入对象的钩子被调用) } } new Vue({ mixins: [mixin], created: function () { console.log(组件钩子被调用) } }) // => "混入对象的钩子被调用" // => "组件钩子被调用"

值为对象的选项           ,例如methods           、components和directives                 ,将被合并为同一个对象                。两个对象键名冲突时     ,取组件对象的键值对           。

var mixin = { methods: { foo: function () { console.log(foo) }, conflicting: function () { console.log(from mixin) } } } var vm = new Vue({ mixins: [mixin], methods: { bar: function () { console.log(bar) }, conflicting: function () { console.log(from self) } } }) vm.foo() // => "foo" vm.bar() // => "bar" vm.conflicting() // => "from self"

注意:Vue.extend()也使用同样的策略进行合并      。

3                、全局混入

混入也可以进行全局注册                。使用时格外小心!一旦使用全局混入     ,它将影响每一个之后创建的 Vue 实例           。使用恰当时                 ,这可以用来为自定义选项注入处理逻辑。

// 为自定义的选项 myOption 注入一个处理器                。 Vue.mixin({ created: function () { var myOption = this.$options.myOption if (myOption) { console.log(myOption) } } }) new Vue({ myOption: hello! }) // => "hello!"

请谨慎使用全局混入           ,因为它会影响每个单独创建的 Vue 实例 (包括第三方组件)                。大多数情况下     ,只应当应用于自定义选项                ,就像上面示例一样。推荐将其作为插件发布           ,以避免重复应用混入           。

Demo源码:

链接:https://pan.baidu.com/s/1jc5SGf3_8qb6pZT4T-5mxA 提取码:broa

此文由博主精心撰写转载请保留此原文链接:https://www.cnblogs.com/xyh9039/p/17139196.html

版权声明:如有雷同纯属巧合,如有侵权请及时联系本人修改                ,谢谢!!!

声明:本站所有文章                ,如无特殊说明或标注,均为本站原创发布                。任何个人或组织           ,在未征得本站同意时                ,禁止复制     、盗用      、采集                、发布本站内容到任何网站           、书籍等各类媒体平台     。如若本站内容侵犯了原著者的合法权益     ,可联系我们进行处理           。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
wp地图软件(WordPress地图插件-让你的网站一目了然)