Vue.js 入门实战(第五部分)
组件基础知识
第二级——一些更有用的东西
组件属性
组件数据与属性
这次我们终于要开始学习组件了!所以,泡杯咖啡☕️,让我们开始吧。
今天的文章可以从零开始啦😁
<html>
<head>
<title>Vue 101</title>
</head>
<body>
<div id="app">
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
}
});
</script>
</body>
</html>
组件基础知识
我们将直接进入组件创建环节,因为真正有趣的部分就从这里开始。但请放心,这只是组件强大功能的冰山一角。此外,我们还需要学习computed属性和watchers类,它们也将对我们大有帮助。不过,我们会在下次学习时,结合今天的内容来讲解这些。
组件是Vue.js框架的核心部分,它们就像乐高积木一样,可以用来构建响应式、功能丰富的应用程序——幸运的是,它们学习和使用起来都非常简单!
你可以把组件想象成网页/应用程序中任何你想使用一次或多次的元素。它可以小到按钮或输入框,也可以大到整个菜单栏,甚至是整个页面视图。
创建组件的优势在于,你只需编写一次组件的显示逻辑(HTML/CSS)以及与用户交互的逻辑(JS),然后就可以在整个应用程序中使用它。
和往常一样,我们将从最简单的例子——按钮——开始。
首先,让我们定义这个组件的蓝图或模板awesome-button,我们暂且称之为“按钮”!
我们来定义一个新的awesome-button组件。将这段代码复制到你的new Vue声明上方。
Vue.component('awesome-button', {
template: `<button @click="clickHandler">Click me for some awesomeness</button>`,
methods: {
clickHandler() {
alert('YAAAS 😎');
}
}
});
我们在第一天添加的脚本标签Vue中获取的对象包含我们在这里调用的方法。正如预期的那样,它允许我们创建一个新组件。我们设置的第一个参数是一个字符串,它将作为我们组件的名称。Vue.jscomponent
第二个参数是一个 JavaScript 对象object,令人惊讶的是,它实际上与我们之前用于主组件的配置对象类型相同Vue instance!这对你意味着什么?这意味着你已经知道如何为这个新组件分配属性和方法。
在上面的例子中,你会注意到一个区别——template属性。这里我们声明了一个字符串(注意,我们还使用了反引号将其括起来,这样以后如果需要声明多行,就无需连接多个字符串),这个字符串将包含this 组件的实际 HTML 代码。在这个例子中,一个简单的<button>标签就足够了。
如果你现在刷新页面,什么都不会发生。还记得我之前说过这只是个蓝图/模板吗?现在是时候把它实际渲染到页面上了。
前往<div id="app">我们放置所有标记的位置,并创建一个新<awesome-button>元素。
你的HTML代码现在应该看起来像这样:
<div id="app">
<awesome-button></awesome-button>
</div>
加载页面后,您现在应该可以看到按钮已经渲染到页面上了。awesome-button在页面上放置几个,或者十个(读者自行决定)标签。现在您应该开始感受到组件的强大之处了,不过我觉得我们还可以再深入探讨一下。
额外提示:如果您好奇心强,不妨查看一下您的代码page source,并使用浏览器的开发者工具将其与该inspect功能进行比较。页面加载时,Vue.js会使用该<awesome-button>标签作为占位符,指示它应该将解析后的模板内容放置在哪里。
第二级——一些更有用的东西
让我们回顾一下上周的例子,并继续处理我们的games数据。
首先,让我们将这个游戏数组重新添加到data()主Vue实例中。
const app = new Vue({
el: '#app',
data: {
games: [
{ name: 'Super Mario 64', console: 'Nintendo 64', rating: 4 },
{ name: 'The Legend of Zelda Ocarina of Time', console: 'Nintendo 64', rating: 5 },
{ name: 'Secret of Mana', console: 'Super Nintendo', rating: 4 },
{ name: 'Fallout 76', console: 'Multiple', rating: 1 },
{ name: 'Super Metroid', console: 'Super Nintendo', rating: 6 }
]
}
});
和以前一样,欢迎您随时更新您喜欢的标题。
不过这一次,我们将创建一个game-card组件,用它来显示我们的数据会更有意义。
准备好了吗?
Vue.component('game-card', {
props: ['gameData'],
template: `
<div style="border-radius: .25rem; border: 1px solid #ECECEC; width: 400px; margin: 1rem; padding: 1rem;">
<h2>{{ gameData.name }} - <small>{{ gameData.console }}</small></h2>
<span v-for="heart in gameData.rating">❤️</span>
<button @click="increaseRating">Increase Rating</button>
</div>
`,
methods: {
increaseRating() {
// this.game.rating++ ?
}
}
});
别慌,你已经知道这些/大部分内容了😃!
我们正在创建一个新的对象Vue.component并为其命名game-card。让我们先跳过props这一步,看看这个对象template。
这里没有什么新内容,只是你可能已经注意到,我们正在访问一个属性的属性,gameData该属性没有在内部定义data,而是在props属性内部定义。
接下来,我们声明methods对象,并increaseRating在其中定义方法。我特意注释掉了this.game.rating++你可能想要调用这个特定函数的部分,但它不起作用!现在是时候讨论props……
组件属性
自定义组件的一个保留属性是 `variables` props。最简单的形式是,它接受一个字符串数组来定义变量。在之前的示例中,我们告诉组件蓝图/模板,我们希望它能够识别一个名为 `variables` 的属性game。
Props这将使我们能够从外部向组件传递信息!让我们来看一个实际的例子,这样更容易理解。
首先,让我们向应用程序中添加一些<game-card>项目。我们将像之前一样使用v-for循环,但这次我们将在自定义组件之上进行循环!
<div id="app">
<awesome-button></awesome-button>
<hr>
<game-card v-for="game in games" :game-data="game" :key="game.name"></game-card>
</div>
目前有很多game说法被提及,所以我们来详细分析一下。
第一步,我们要创建<game-card>组件,就像我们之前讨论的那样。
v-for="game in games"接下来,我们像上周那样添加循环。这将创建一个game变量来保存循环中的当前游戏,我们可以立即使用它!
最后,我们给模板的prop赋值,gameData这里赋的就是game循环中得到的变量。注意,这里我们用连字符代替了驼峰命名法,game-data因为 HTML 不区分大小写。如果你觉得难以理解,可以尝试用对象的方式来思考。我们做的类似于……game-card.props.gameData = game
别忘了:key!
这里有个很大的陷阱要提一下,我们把球传给game了我们的game-data道具,但其实背后还有个:机关。你注意到了吗?
当我们给组件实例赋值时,有两种方法。以我们的例子为例,我们可以在赋值:语句前加上 `!`(这是 `!` 的简写)。这样可以确保 `!`后面v-bind:传递的数据被JavaScript 用作变量或实际的代码片段。="<here>"
如果你输入的是 `!` gameData="game",那么Vue会将其视为将字符串gameData赋值给prop 。例如:`!` "game"game-card.props.gameData = "game"
不妨暂时放下理论,实际在浏览器中运行一下这段代码。你会发现,正如预期的那样,我们<game-card>组件的整个模板会针对每个元素进行渲染game。
这样做最大的好处是,如果我们对 HTML 进行更改,应用程序中的所有代码都会同步更新。
此外,最重要的是,组件允许你将特定组件的逻辑封装起来game-card。让我们再回顾一下这个increaseRating()方法。
组件数据与属性
组件的属性实际上是一个非常复杂的话题,但有一条非常重要的经验法则你必须始终牢记:属性绝对不能在组件内部修改。
事实上,如果你尝试这样做,Vue会在控制台中抛出各种警告并发出警告——因为这必然会导致意想不到的行为。如果你想了解更多,可以阅读以下文档:在 Vue2 中修改 props 是一种反模式。
rating那么,我们该如何修改组件存储中的值呢?关键就在问题本身!我们需要将其复制prop到我们的存储中data,才能真正对其进行修改。
首先,让我们将它添加data到game-card组件中,并为其分配一个新的不冲突的名称(如果 props 和 data props 名称相同,则会冲突),然后用 prop 的值预先填充它。
data() {
return {
game: {...this.gameData}
}
},
这里有几点需要注意,但在此之前,如果你还不了解它的{...gameData}作用,它是一个展开运算符。我不会在这里详细解释,稍后会写一篇简短的文章来讲解,但简单来说,我们是在复制属性,gameData因为我们永远不想在子对象中修改它。
该data房产的return:
当我们学习这个data属性时,我告诉过你它需要包含一个带有所有必要属性的对象,这也是我们一直以来在主Vue 实例中的做法。但是对于组件来说,我们实际上需要将其变成一个函数,方法是添加 ` ()$ ('$('$('$('$('$('$('$( return'$('$('$('$('$('$('$('$('$('$('$('$('$('$('$('$('$(1$(('$(('$(( ( ...) )`
但为什么?!😫
简单来说,你的组件可以有一个或多个实例,对吗?
每个实例都需要一个唯一的数据对象!你肯定不想让data所有实例共享同一个对象,否则它们都会共享同一个对象title——这样一来,整个应用程序的意义就荡然无存了。
所以,之所以把它做成一个函数并返回一个对象,是因为Vue可以在每次创建我们的对象时调用这个函数game-cards。这样,每个对象都会获得一个唯一的数据对象来进行操作!
获取我们的道具:
当我们创建game` data`属性时,我们会对其进行赋值this.gameData,所以这里有几点需要注意。`data` 属性也props可以像本地状态属性一样,通过 `.` 在组件脚本中访问。因此,在这里,我们将 `data` 属性的值设置为等于该属性的值。thisdatagamegameData
这意味着现在我们需要更新 HTML,所以在组件内部切换引用,gameData如下game所示:
<div style="border-radius: .25rem; border: 1px solid #ECECEC;">
<h2>{{ game.name }} - <small>{{ game.console }}</small></h2>
<span v-for="heart in game.rating">❤️</span>
<button @click="increaseRating">Increase Rating</button>
</div>
在浏览器中再次运行此程序,您应该会得到相同的结果。
终于,我们的increaseRating方法可以正常运行了!请前往该方法,并将注释替换为以下内容:
methods: {
increaseRating() {
this.game.rating++
}
}
每次点击,我们都会递增组件的内部 data属性,该属性保存的是game“s rating”,而不是 prop。
关于组件有很多理论需要学习,我只是略窥门径,但希望你现在能更清楚地了解为什么像Vue这样的框架如此流行,以及使用起来如此有趣。
从现在开始,我们将开始学习一些我认为是中级的主题,例如computed属性、watchers组件events等等。希望你们很快就能兴奋地了解 Vue 的核心内容。
如果您需要,这里是今天的完整代码,感谢阅读!🤗😋
<html>
<head>
<title>Vue 101</title>
</head>
<body>
<div id="app">
<awesome-button></awesome-button>
<game-card v-for="game in games" :game-data="game" :key="game.name"></game-card>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('awesome-button', {
template: `<button @click="clickHandler">Click me for some awesomeness</button>`,
methods: {
clickHandler() {
alert('YAS. 😎');
}
}
});
Vue.component('game-card', {
props: ['gameData'],
data() {
return {
game: {...this.gameData}
}
},
template: `<div style="border-radius: .25rem; border: 1px solid #ECECEC; width: 400px; margin: 1rem; padding: 1rem;">
<h2>{{ game.name }} - <small>{{ game.console }}</small></h2>
<span v-for="heart in game.rating">❤️</span>
<button @click="increaseRating">Increase Rating</button>
</div>`,
methods: {
increaseRating() {
this.game.rating++
}
}
});
const app = new Vue({
el: '#app',
data: {
games: [
{ name: 'Super Mario 64', console: 'Nintendo 64', rating: 4 },
{ name: 'The Legend of Zelda Ocarina of Time', console: 'Nintendo 64', rating: 5 },
{ name: 'Secret of Mana', console: 'Super Nintendo', rating: 4 },
{ name: 'Fallout 76', console: 'Multiple', rating: 1 },
{ name: 'Super Metroid', console: 'Super Nintendo', rating: 6 }
]
}
});
</script>
</body>
</html>