结合情景联想记忆是一种很棒的记忆方法,本篇博客咱们通过一个小案例来加深对部分vue基础知识的理解和应用。
实现初版效果
小案例的应用场景是有一家好吃的炒面店,店里炒面的种类有很多,咱们要将炒面分成吃过和没吃过的两部分,同时因为店里炒面的种类可能会增加,所以咱们还要有添加炒面的部分,下面咱们来着手实现一下。最后的效果如下图:
首先新建一个noodles.html文件,这个文件就是开始案例的入口,再新建一个components文件夹,不同部分放到不同的JS文件(组件)中,在该文件夹下新建App.js(充当根组件),在noodles.html文件中对vue进行引入,由于根组件被导入之后直接用在createApp()中,所以并不需要进行组件注册,noodles.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>Document</title>
<script src="https://unpkg.com/vue@3.2.31/dist/vue.global.js"></script>
</head>
<body>
<div id="app"></div>
<script type="module">
import App from "./components/App.js"
Vue.createApp(App).mount('#app')
</script>
</body>
</html>
在componets文件夹下新建StoreSection.js文件,因为App.js充当根组件,根组件放的内容相对简洁,所以只需在根组件中引入写有主要内容的组件即可,咱们会在StoreSection.js文件写主要内容,这里就需要进行组件注册之后使用,使用组件标签时用驼峰命名法,“驼峰”之间的连接用的是 - ,App.js的完整代码如下:
import StoreSection from './StoreSection.js'
export default {
components: { StoreSection },
template: /*html*/`
<store-section></store-section>
`
}
在StoreSection.js中完成最初版效果,通过将炒面菜单存放到foods数组中,每一份炒面味道和食材不同,所以它们是数组中不同的food对象,利用v-for将炒面遍历到li标签中,同时我们为了方便区分,利用filter判断food中的state来区分吃过还是没吃过,如果一种炒面都没吃过,就先不显示吃过部分,如果每种炒面都吃完了,那就不显示没吃过部分,通过吃过或者没吃过的炒面数组长度,利用v-show来决定是否显示该部分。如下图:
添加新口味炒面的部分,通过v-on(可以用@代替)绑定提交的事件,再用prevent实现局部刷新,添加的按钮是为submiit类型,点击之后触发form标签调用add方法,添加的方法直接利用push添加新的炒面对象,属性与原来的炒面对象一致(包括id,name,state),同时通过newFood变量利用v-model双向获取输入框中的内容,进行新口味炒面的添加,添加完毕之后将newFood置零,如下图所示:
StoreSection.js初版完整代码如下:
export default {
template: /*html*/`
<section v-show="foods.filter(item => !item.state).length">
<h2>没吃过</h2>
<ul>
<li v-for="food in foods.filter(item => !item.state)" :key="food.id">
<span>{{ food.name }}</span>
<input type="checkbox" v-model="food.state">
</li>
</ul>
</section>
<section v-show="foods.filter(item => item.state).length">
<h2>吃过</h2>
<ul>
<li v-for="food in foods.filter(item => item.state)" :key="food.id">
<span>{{ food.name }}</span>
<input type="checkbox" v-model="food.state">
</li>
</ul>
</section>
<form @submit.prevent="add">
<input type="text" v-model="newFood">
<button type="submit">添加</button>
</form>
`,
data() {
return {
foods: [
{ id: 1, name: '干妈肉丝炒面', state: false},
{ id: 2, name: '火爆肉丝炒面', state: false},
{ id: 3, name: '玉米鸡丁炒面', state: false},
],
newFood: '',
}
},
methods: {
add() {
this.foods.push({
id: this.foods.length + 1,
name: this.newFood,
state: false,
}),
this.newFood = ''
}
},
}
这样初版本就完成了。
初版代码简化
上面箭头所指部分内容相同,可以封装成一个方法,便于复用,需要注意对data中的数据进行操作需要放到computed中进行,封装之后代码如下:
computed: {
filters() {
return {
beforeBuy: this.foods.filter(item => !item.state),
afterBuy: this.foods.filter(item => item.state)
}
}
}
拆分组件进行代码优化
可以看到代码中仍有许多重复的部分,为了进一步提高代码简洁性,咱们可以对是否吃过和添加新类型炒面两部分分别进行整合封装,这样可以深化理解组件关系,还便于进行复用。
遍历部分优化
在componets文件夹下新建StoreEat.js组件,将遍历炒面的部分复制到StoreEat.js(子组件)中,如果把StoreSection.js(父组件)中关于炒面的数据全都拿过来就有些复杂了,理理逻辑,咱们想在子组件中用父组件的数据,那么就可以用props将子组件需要数据的格式标准定义出来,在父组件中给予数据,具体见下图:
添加部分优化
在components文件夹下新建StoreAdd.js(子组件)文件,将form表单部分剪切到StoreAdd.js文件中,并在子组件中新建add方法,为了便于区分父子组件之中的add方法,将StoreSection.js中的add方法改为fatherAdd方法,我们通过$emit将add方法暴露给父组件,当sumbit触发之后,调用子组件的add方法,最后调用父组件的fatherAdd方法实现添加炒面的操作,父组件的调整如下图
StoreAdd.js(子组件)的完整代码如下:
export default {
template: /*html*/`
<form @submit.prevent="add">
<input type="text" v-model="newFood" placeholder="输入新添加的炒面">
<button type="submit">添加</button>
</form>
`,
data() {
return {
newFood: ''
}
},
methods: {
add() {
this.$emit('add', this.newFood);
}
},
}
修改完毕,咱们就得到拆分之后的版本,同时知识也在场景中得到应用和加深。
注意事项
1.在运行为了解决跨域问题,安装Live Server插件,运行时右键,选择open with live server强制在同一作用域下运行;
2.使用/*html*/之前,安装es6-string-html插件;
3.如果报错,可以多留意是否漏写return和this。