vue3学习随便记8
Posted sjg20010414
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue3学习随便记8相关的知识,希望对你有一定的参考价值。
组件基础
我们其实前面已经看过很多组件的例子,都是用 app.component(...) 注册一个全局组件,组件的“视图”是直接用 template 定义的字符串模板。这样的组件主要用来举例和学习原理,实际工程中通常会使用单文件组件,即一个组件是单个文件定义的(在编译构建系统中常常是 .vue 文件)。
组件是自定义元素,可以像元素一样复用,复用时,每个组件都是新的实例,它的属性方法都是它自己的。
组件通常可以嵌套组织成树状。
模板中要使用组件,必须先注册。注册分为全局注册和局部注册。我们用 app.component(...) 注册的都是全局注册,全局注册的组件可以在应用中的任何组件的模板中使用。用 app.component(...) 方法注册的组件是全局组件,但创建app时在其配置 components 中定义的组件是 app 的组件,却不是全局组件(属于局部注册),因为组件在其子组件中不可用。
const app = Vue.createApp(
components:
'component-a': ComponentA,
'component-b': ComponentB
)
上面的代码中,可以在app中使用 ComponentA 和 ComponentB,但不能在 ComponetB 中使用 ComponentA,如果希望 ComponentB 可以使用 ComponentA,必须明确声明
const ComponentA =
/* ... */
const ComponentB =
components:
'component-a': ComponentA
// ...
通过 prop (属性)向子组件传递数据
组件的 prop 就是组件中自定义的 attribute,它是组件向外暴露的通信接口,是外部向子组件传递数据的方式。组件 prop 就是在组件配置对象的配置项 props: [...] 中定义,在组件模板中使用,然后外部像寻常的 attribute 一样绑定数据。
<html>
<head>
<script src="vue.global.js"></script>
</head>
<body>
<div id="app">
<blog-post v-for="post in posts" :key="post.id" :title="post.title"></blog-post>
</div>
<script>
const app = Vue.createApp(
data()
return
posts: [
id:1, title:'Vue3 之旅',
id:2, title:'Vue3 博客',
id:3, title:'Vue3 学习之趣'
]
)
app.component('blog-post',
props: ['title'],
template: `<h4> title </h4>`
)
const vm = app.mount('#app')
</script>
</body>
</html>
监听子组件事件
子组件是自定义的元素,除了可以用 props 扩展属性(attribute),还可以用 emits 扩展事件。我们为 blog-post 组件扩展 enlarge-text事件(在该事件触发后放大所有博文的字号):先给app添加一个属性变量表示博文的字号
data()
return
posts: [
/* ... */
],
postFontSize: 1
然后模板中用该变量控制博文的字号,触发 blog-post 的 enlarge-text 事件时,增加该变量的值
<div id="app">
<div :style=" fontSize: postFontSize + 'em' ">
<blog-post v-for="post in posts" :key="post.id"
:title="post.title" @enlarge-text="postFontSize += 0.1">
</blog-post>
</div>
</div>
我们来修改 blog-post 组件,它应该向外暴露 enlarge-text 事件,同时,扩展的事件 enlarge-text 不会自动能触发,它要么手动用代码去触发,要么转嫁到组件已有的 HTML 事件上,在 HTML事件中用代码去触发。我们修改组件模板,给组件添加一个按钮,在该按钮的 click 事件中用代码去触发 enlarge-text事件。(在JS代码中,使用camelCased驼峰写法,如大驼峰的组件名BlogPost,小驼峰的变量 enlargeText,在HTML中,使用kebab-case短横分隔写法,如blog-post,enlarge-text )
app.component('blog-post',
props: ['title'],
emits: ['enlargeText'],
template: `
<div class="blog-post">
<h4> title </h4>
<button @click="$emit('enlargeText')">放大文本</button>
</div>
`
)
上面的代码中,我们放大文本的步进量是使用组件的一方决定的,如果我们希望这个步进是组件自身决定的,那么我们在用vm的API方法$emit触发enlargeText事件时,可以同时抛出一个值,然后让使用方使用这个值。
<button @click="$emit('enlargeText', 0.1)">放大文本</button>
使用方可以通过 $event 参数访问到抛出的值
<blog-post v-for="post in posts" :key="post.id"
:title="post.title" @enlarge-text="postFontSize += $event">
</blog-post>
如果事件处理函数是一个方法,那么抛出的值会成为该方法的第一个参数
<div id="app">
<div :style=" fontSize: postFontSize + 'em' ">
<blog-post v-for="post in posts" :key="post.id"
:title="post.title" @enlarge-text="onEnlargeText">
</blog-post>
</div>
</div>
<script>
const app = Vue.createApp(
data()
return
posts: [
/* ... */
],
postFontSize: 1
,
methods:
onEnlargeText(amount)
this.postFontSize += amount
)
app.component('blog-post',
/* ... */
)
const vm = app.mount('#app')
</script>
组件作为自定义的元素,也可以是自定义的输入元素,从而可以使用 v-model。首先我们要来了解 v-model 双向绑定的含义:值绑定到vm变量,input事件处理中把vm变量设置为输入元素的值
<input v-model="searchText" />
上述代码等价于
<input :value="searchText" @input="searchText = $event.target.value" />
当在自定义的组件custom-input上使用 v-model 时,
<custom-input v-model="searchText"></custom-input>
等价于
<custom-input :model-value="searchText"
@update:model-value="searchText = $event"
></custom-input>
上面的代码意味着,自定义元素(组件)必须向外暴露一个名为 modelValue 的 prop(属性),因为这是一个输入元素,所以,组件内部的 input 元素的 value 属性(attribute)应该绑定到 modelValue 属性上。自定义元素(组件)必须向外暴露一个名为 update:modelValue 的事件,组件内部 input 的 input 事件中,用vm API方法 $emit(...) 发射 update:modelValue 事件。即组件应定义如下
app.component('custom-input',
props: ['modelValue'],
emits: ['update:modelValue'],
template: `
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
>
`
)
我们观察模板部分,value值绑定 和 input事件,这本就是 v-model 的含义,我们可以换一种方式,使用 计算属性 及其 getter/setter 来定义组件
app.component('custom-input',
props: ['modelValue'],
emits: ['update:modelValue'],
template: `
<input v-model="value">
`,
computed:
value:
get()
return this.modelValue
,
set(value)
this.$emit('update:modelValue', value)
)
通常计算属性是getter,对上述组件的 getter,动作路径是:使用者视图输入-> modelValue -> 内部 value,而内部value变化影响到使用者视图输入框则是 setter,动作路径是:内部value -> modelValue -> 使用者视图输入
通过slot插槽分发内容
props 是组件这个自定义元素的属性,它是变量的概念,我们有时候希望和HTML元素一样,把自定义元素的内容(元素的innerHTML)从外部传递到组件内部, 这是静态内容概念。实现内容从外到内的传递,可以使用 slot插槽,即 innerHTML ---> <slot></slot>。
我们给前面的blog-post组件插入slot,用来存放正文内容
<div id="app">
<div :style=" fontSize: postFontSize + 'em' ">
<blog-post v-for="post in posts" :key="post.id"
:title="post.title" @enlarge-text="onEnlargeText">
<div> post.body </div>
</blog-post>
</div>
</div>
<script>
const app = Vue.createApp(
data()
return
posts: [
id:1, title:'Vue3 之旅', body: 'Vue3 之旅正文',
id:2, title:'Vue3 博客', body: 'Vue3 博客开启新篇章',
id:3, title:'Vue3 学习之趣', body: 'Vue3 学习之趣无穷无尽'
],
postFontSize: 1
,
methods:
onEnlargeText(amount)
this.postFontSize += amount
)
app.component('blog-post',
props: ['title'],
emits: ['enlargeText'],
template: `
<div class="blog-post">
<h4> title </h4>
<slot></slot>
<button @click="$emit('enlargeText', 0.1)">放大文本</button>
</div>
`
)
const vm = app.mount('#app')
</script>
红色部分是 slot 分发的内容
动态组件
所谓动态组件,就是一个组件容器,可以动态确定要渲染的具体组件,一般用 :is 属性指定组件名(或者一个组件的选项对象,即组件的完整配置)。下面的例子,根据用按钮选择的当前的tab名称,动态渲染对应组件
<div id="app">
<button v-for="tab in tabs" :key="tab.name"
:class="['tab-button', active: currentTab === tab.name]"
@click="currentTab = tab.name">
tab.text
</button>
<component :is="currentTabComponent" class="tab"></component>
</div>
<script>
const app = Vue.createApp(
data()
return
currentTab: 'Home',
tabs: [
name:'Home', text:'首页',
name:'Posts', text:'帖子',
name:'Archive', text:'归档'
]
,
computed:
currentTabComponent()
return 'tab-' + this.currentTab.toLowerCase()
)
app.component('tab-home',
template: `<div class="demo-post">首页内容</div>`
)
app.component('tab-posts',
template: `<div class="demo-post">帖子……</div>`
)
app.component('tab-archive',
template: `<div class="demo-post">(归档)</div>`
)
const vm = app.mount('#app')
</script>
Vue解析DOM模板的注意事项
如果想在 DOM 中直接书写 Vue 模板, Vue 就不得不从 DOM 中获取字符串,这时,会因为原生 HTML的解析行为带来一些小问题。这句话的意思是,我们在以下情况使用组件时,组件都是一个独立的整体:
- 注册组件时使用字符串模板(例如 template: `...`)
- 使用单文件组件
- 使用 <script type="text/x-template">定义的组件
Vue 解析模板没有问题。而原生的 HTML DOM 和 Vue模板混在一起, Vue解析时势必要从混合物中捞出Vue模板,这样,Vue模板必须迁就原生HTML的一些特性。哪些特性呢?
元素位置受限
<table>
<blog-post-row></blog-post-row>
</table>
<table>内部允许的元素是有限制的,这样,上述代码中的<blog-post-row>会被认为是无效的内容提升到外部,导致渲染结果是错误的。这种情况,可以使用 is 属性来变通:
<table>
<tr is="vue:blog-post-row"></tr>
</table>
这里is的值必须用 vue: 开头,表示这个不是HTML原生的自定义元素,而是Vue组件。
和这个例子类似的元素还有 <ul>、<ol>、<select>,而 <li>、<tr>、<option>只能出现在特定元素内部。
大小写不敏感
HTML 属性名不区分大小写的,从而浏览器将所有大写字母解释为小写,这个问题带来的一点就是JS代码中组件名称之类的是有大小写驼峰写法,驼峰写法的组件名不能直接在HTML中使用(TabA组件和taba组件会被认为是相同组件),解决办法我们前面已经看到过了,就是HTML中用驼峰写法等价的kebab-cased(短横分隔)写法(TabA对应tab-a)
以上是关于vue3学习随便记8的主要内容,如果未能解决你的问题,请参考以下文章