vue3学习随便记9
Posted sjg20010414
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue3学习随便记9相关的知识,希望对你有一定的参考价值。
深入组件
组件注册
注册组件时必须给组件命名。组件名的命名规则和组件要使用在哪里有关,如果只是要混在 DOM 中使用组件,那名字应该全部小写,多个单词用连字符连接(即kebab-case),这样可以避免与 html元素发生冲突。如果在字符串模板或单文件组件中定义组件,那么既可以用 kebab-case,也可以用 PascalCase (首字母大写的驼峰式)。当使用后者时,使用组件时,即可以 PascalCase,也可以 kebab-case,但混入 DOM 使用仍然必须 kebab-case。
我们前面用的都是全局注册,即使用 app.component() 函数。和变量的情形一样,一旦全局注册,那么它可以在 app 的模板中使用,也可以在后续组件的模板中使用。
和全局注册相对的是局部注册,即在应用 app 的配置项 components 或者组件的配置项 components 中进行声明(每个组件包含一个组件名作为键,值则是该组件的配置),然后在app范围内或者(父)组件范围内使用。局部注册可以使得构建工具编译时跳过那些并未被实际使用的组件。
const ComponentA =
/* ... */
const ComponentB =
/* ... */
const ComponentC =
/* ... */
const app = Vue.createApp(
components:
'component-a': ComponentA,
'component-b': ComponentB
)
const ComponentA =
/* ... */
const ComponentB =
components:
'component-a': ComponentA
// ...
当我们使用 Babel 和 webpack 那样的构建工具,按 ES2015+模块方式使用则如下
import ComponentA from './ComponentA.vue'
export default
components:
ComponentA
// ...
组件文件可以是 ComponentA.vue 或 ComponentA.js,导入时,可以不带后缀,即
import ComponentA from './ComponentA'
尽管一般使用构建工具来使用组件,但我们的确可以直接按javascript模块来使用组件。下面的例子纯粹为了演示,我们把前面的 todo-list 例子改一改。
先编写模块 TodoItem.mjs (模块概念可以参考 JavaScript modules 模块 - JavaScript | MDN,不使用Vue编译构建工具,我们只能使用JS内的字符串模板),TodoItem模块导出 TodoItem对象
const TodoItem =
template: `
<li>
title
<button @click="$emit('remove')">移除</button>
</li>
`,
props: ['title'],
emits: ['remove']
export TodoItem
然后编写 TodoList.mjs 模块,该模块从 TodoItem.mjs模块 导入 TodoItem对象,同时自身又导出 TodoList 对象。TodoList 组件内部局部注册了 TodoItem组件。
import TodoItem from "./TodoItem.mjs"
const TodoList =
components:
TodoItem
,
data()
return
newTodoText: '',
todos: [
id:1, text: 'Learn JavaScript' ,
id: 2, text: 'Learn Vue' ,
id: 3, text: 'Build something awesome'
],
nextTodoId: 4
,
methods:
addNewTodo()
this.todos.push(
id: this.nextTodoId++,
text: this.newTodoText
)
this.newTodoText = ''
export TodoList
最后,编写 main.mjs 模块,它引入 TodoList,并作为最后给 HTML 使用的模块
import TodoList from './TodoList.mjs'
Vue.createApp(TodoList).mount('#list-rendering')
<html>
<head>
<script src="vue.global.js"></script>
</head>
<body>
<div id="list-rendering">
<form v-on:submit.prevent="addNewTodo">
<label for="new-todo">添加待办事项</label>
<input v-model="newTodoText" id="new-todo" placeholder="例如: 收拾房间" />
<button>添加</button>
</form>
<ol>
<todo-item v-for="todo in todos" :key="todo.id"
:title="todo.text" @remove="todos.splice(index, 1)"
></todo-item>
</ol>
</div>
</body>
<script type="module" src="./main.mjs"></script>
</html>
然后,在这些文件所在目录,启动 Web服务监听某端口 (例如用php命令 php -S 0.0.0.0:8000),再在浏览器访问 http://localhost:8000/test.html 即可。(不能使用本地加载Html文件的方式来访问,否则会遇到 CORS错误,这是Javascript模块的安全性限制)
Props
我们前面是以字符串数组形式列出 prop,从而这些属性的取值类型是没有明确限定的。我们可以以对象形式列出 prop,对象 property的名称和值分别对应 prop的名称和类型:
props:
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // 或任何其他构造函数
使用组件时,可以给 prop传入静态的值(字符串),如
<blog-post title="My journey with Vue"></blog-post>
更常见的,我们会为 prop 动态绑定变量的值或者表达式的值
<!-- 动态赋予一个变量的值 -->
<blog-post :title="post.title"></blog-post>
<!-- 动态赋予一个复杂表达式的值 -->
<blog-post :title="post.title + ' by ' + post.author.name"></blog-post>
当传入数字时,我们需要用动态绑定,以明确不是字符串,而是表达式
<!-- 即便 `42` 是静态的,我们仍需通过 `v-bind` 来告诉 Vue -->
<!-- 这是一个 JavaScript 表达式而不是一个字符串。 -->
<blog-post :likes="42"></blog-post>
<!-- 用一个变量进行动态赋值。-->
<blog-post :likes="post.likes"></blog-post>
传入布尔值,同样如此
<!-- 包含该 prop 没有值的情况在内,都意味着 `true`。 -->
<!-- 如果没有在 props 中把 is-published 的类型设置为 Boolean,
则这里的值为空字符串,而不是“true”。 -->
<blog-post is-published></blog-post>
<!-- 即便 `false` 是静态的,我们仍需通过 `v-bind` 来告诉 Vue -->
<!-- 这是一个 JavaScript 表达式而不是一个字符串。 -->
<blog-post :is-published="false"></blog-post>
<!-- 用一个变量进行动态赋值。 -->
<blog-post :is-published="post.isPublished"></blog-post>
传入数组
<!-- 即便数组是静态的,我们仍需通过 `v-bind` 来告诉 Vue -->
<!-- 这是一个 JavaScript 表达式而不是一个字符串。 -->
<blog-post :comment-ids="[234, 266, 273]"></blog-post>
<!-- 用一个变量进行动态赋值。 -->
<blog-post :comment-ids="post.commentIds"></blog-post>
传入对象
<!-- 即便对象是静态的,我们仍需通过 `v-bind` 来告诉 Vue -->
<!-- 这是一个 JavaScript 表达式而不是一个字符串。 -->
<blog-post
:author="
name: 'Veronica',
company: 'Veridian Dynamics'
"
></blog-post>
<!-- 用一个变量进行动态赋值。 -->
<blog-post :author="post.author"></blog-post>
如果要把一个对象的 property 都作为 prop 传入,即对于以下对象
post:
id: 1,
title: 'My Journey with Vue'
要实现下述绑定效果
<blog-post v-bind:id="post.id" v-bind:title="post.title"></blog-post>
可以有更简单的绑定语法(v-bind = "对象名",对象的每个 property 自动绑定到每个 prop)
<blog-post v-bind="post"></blog-post>
Vue组件 prop 的数据流向是单向的,即从父组件向子组件单向下行绑定:父级 prop 的更新会流动到子组件,导致子组件 prop 刷新为最新的值。用户不应该在子组件内部改变 prop。可能让用户想在子组件内部改变 prop 值的情形有:
1、想用 prop 传递一个初始值,之后子组件想把它作为一个本地的 prop 数据来使用。例如,一个计算器变量 counter,希望父组件传递一个初始值。这种情形下,不应该把 counter 定义为 prop,应该额外定义一个 initialCounter 作为 prop,counter 作为组件的数据属性变量,并初始化为 initialCounter的值,即
props: ['initialCounter'],
data()
return
counter: this.initialCounter
2、想用 prop 传递一个值,但这个值需要经过转换才适合子组件使用。这种情况下,最好定义一个计算属性来转换 prop 的值,例如
props: ['size'],
computed:
normalizedSize()
return this.size.trim().toLowerCase()
说明:在 Javascript 中,数组和对象是通过引用传递的,所以,对于 数组或者对象类型的 prop,如果在子组件中改变这个对象或数组本身将会影响到父组件的状态,这一点也足以说明 prop 单向数据流的必要性。
因为子组件可能是被他人使用的,所以,对 prop 进行相关的类型和数据验证就是必要的(对子组件开发者来说其实也必要)。
app.component('my-component',
props:
// 基础的类型检查 (`null` 和 `undefined` 值会通过任何类型验证)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC:
type: String,
required: true
,
// 带有默认值的数字
propD:
type: Number,
default: 100
,
// 带有默认值的对象
propE:
type: Object,
// 对象或数组的默认值必须从一个工厂函数返回
default()
return message: 'hello'
,
// 自定义验证函数
propF:
validator(value)
// 这个值必须与下列字符串中的其中一个相匹配
return ['success', 'warning', 'danger'].includes(value)
,
// 具有默认值的函数
propG:
type: Function,
// 与对象或数组的默认值不同,这不是一个工厂函数——这是一个用作默认值的函数
default()
return 'Default function'
)
prop 验证发生在组件实例创建之前,因此,default() 函数 或 validator() 函数中无法使用实例的 property (data、computed等)
验证中的类型检查,type 除了可以是原生构造函数 String、Number、Boolean、Array、Object、Date、Function、Symbol 之一,还可以是自定义构造函数,通过 instanceof 来进行检查确认。例如,给定如下构造函数
function Person(firstName, lastName)
this.firstName = firstName
this.lastName = lastName
就可以使用
app.component('blog-post',
props:
author: Person
)
来验证 author 这个 prop 的值是否是通过 new Person 创建的(实例)。
prop 的大小写命名规则,同样遵循 HTML 中使用 kebab-case 和 JS 中使用 camelCase (字符串模板则没有限制): JS中 prop postTitle 对应 HTML 中 attribute post-title
非 prop 的 Attribute
组件作为自定义元素,我们除了添加在 props 或 emits 有对应定义的 attribute,还可以添加非 prop 的 attribute,常见的非 prop attribute 有 class、style、id。组件内部可以通过实例 property $attrs 来访问到这些 attributes。
Attribute 继承
当组件返回的是单个根节点时,非 prop 的 attribute 将自动添加到根节点的 attribute 中。例如,日期选择组件 date-picker 返回单个根节点
app.component('date-picker',
template: `
<div class="date-picker">
<input type="datetime-local" />
</div>
`
)
如果我们用 data-status attribute 来表示 <data-picker> 组件的状态,即
<date-picker data-status="activated"></date-picker>
data-status attribute 会合并到根节点 div.data-picker,从而渲染为
<div class="date-picker" data-status="activated">
<input type="datetime-local" />
</div>
对于事件监听器,也有同样的规则。我们下面的例子来演示非prop attribute的合并情况和父组件向子组件传递初始值的情况:
<div id="app">
<data-picker :initial-date="date"
data-status="activated" @change="submitChange">
</data-picker>
</div>
<script>
const app = Vue.createApp(
data()
return
date: '2021-11-11'
,
methods:
submitChange(e)
console.log(e)
)
app.component('data-picker',
props: ['initialDate'],
data()
return
date: this.initialDate
,
template: `
<div class="data-picker">
<input type="datatime-local" :value="date" />
</div>
`,
created()
console.log(this.$attrs)
)
app.mount('#app')
</script>
对于一个HTML根元素本身具有 change事件的组件来说,给它附加非prop的事件监听器change是有意义的,因为该事件监听器会从父组件传递到子组件,我们不需要显式从 date-picker 用代码去引发事件,因为子组件可以自动触发事件。
<div id="app">
<data-picker @change="showChange"></data-picker>
</div>
<script>
const app = Vue.createApp(
methods:
showChange(e)
console.log(e.target.value)
)
app.component('data-picker',
template: `
<select>
<option value="1">昨天</option>
<option value="2">今天</option>
<option value="3">明天</option>
</select>
`,
created()
console.log(this.$attrs)
)
app.mount('#app')
</script>
禁用 Attribute 继承
和继承相反,如果不希望组件的根元素继承 attribute,可以在组件选项中设置 inheritAttrs: false,这样组件就不会进行自动合并的操作了。禁用 attribute 自动继承的常见场景是希望将 attribute 应用到异于根元素的其他元素。
app.component('date-picker',
inheritAttrs: false,
template: `
<div class="date-picker">
<input type="datetime-local" v-bind="$attrs" />
</div>
`
)
上面的代码,会把外部 attribute 绑定到组件内的 input 上,即把
<date-picker data-status="activated"></date-picker>
渲染为
<div class="date-picker">
<input type="datetime-local" data-status="activated" />
</div>
多个根节点的 Attribute 继承
Vue 没有规定多个根节点如何自动实现 attribute 继承,所以,多个根节点时,必须类似禁用Attribute那样手动绑定来实现继承(对某个元素 v-bind="$attrs"),不然会出现运行时警告。
<custom-layout id="custom-layout" @click="changeValue"></custom-layout>
app.component('custom-layout',
template: `
<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>
`
)
以上是关于vue3学习随便记9的主要内容,如果未能解决你的问题,请参考以下文章