博客内容整理自b站up主技术蛋老师
引入
// cdn 引入 嵌入在 html中 <head>标签里
<script src="https://unpkg.com/vue@3"></script>
创建Vue实例与挂载
Vue.createApp({data(){}, computed, template, components, props})
// Vue 2创建实例
const vm = new Vue();
每次使用Vue都需要创建新的实例,只需调用vue里的createApp()
函数就行,此函数有个可选参数,是个对象{}
;
新的应用实例需要挂载到DOM的指定位置,即应用实例调用mount()
方法,这样就挂载到DOM的一个标签上了,没有挂载实例的标签不受影响,原生HTML、CSS、JS可与Vue并存。
实现以上步骤的代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>01</title>
<script src="https://unpkg.com/vue@3"></script>
</head>
<body>
<div id="app">
<h1>临时清单</h1>
</div>
<script>
Vue.createApp().mount("#app");
</script>
</body>
</html>
插值表达式
<!-- 插值表达式:用两个花括号直接引用data里的数据 -->
<h1> {{ title }} </h1>
Vue.createApp({
data(){
return{
title:'零食清单'
}
}
}).mount("#app");
v-for 循环遍历数组-基础语法
<li v-for="item in items">{{item}}</li>
// items 是data中的属性名,item 自定义名字,代表进行遍历的子元素
// 再把 item 用插值表达式的方式加入到 <li> 标签里
- 举个栗子:
Vue.createApp({
data() {
return {
title: '零食清单',
foods:
[
{ id: 1, name: '原味', image: './images/原味.png', purchased: false },
{ id: 2, name: '辣味', image: './images/辣味.png', purchased: false },
{ id: 3, name: '炭烧味', image: './images/炭烧味.png', purchased: false }
]
}
}
}).mount('#app');
- 上例可写为:
<ul>
<li v-for="food in foods">
<span>{{ food.name }}</sapn>
</li>
</ul>
v-bind 单向绑定属性
img
中的src
属性如何绑定数据?
用v-bind:src=""
来为HTML属性和数据进行绑定,在属性前加v-bind:
,然后在传入属性值的时候就是vue里的数据了。
<ul>
<li v-for="food in foods">
<img v-bind:src="food.image">
<span>{{ food.name }}</sapn>
</li>
</ul>
v-model 双向绑定
<ul>
<li v-for="food in foods">
<span>{{ food.name }}</sapn>
<input type="checkbox" v-model="food.purchased">
<span>{{ food.purchased }}</sapn>
</li>
</ul>
标题 | 内容 | 备注 |
---|---|---|
单向绑定v-bind | 属性值和Vue数据绑定 | Vue数据变动,DOM就会变动 |
双向绑定v-model | input 输入框的value 和vue数据绑定 | Vue数据变动,DOM跟着变动 DOM变动,Vue数据跟着变动 |
v-for 循环遍历数组-引申用法
// index 为数组的序号
<li v-for="(item, index)in items">
// key 是Vue的特殊属性,用于跟踪和更新元素
<li v-for="(item, index)in items" v-bind:key="index">
- 举个栗子:
<div id="app">
<section>
<h2>未购零食</h2>
<ul>
<li v-for="food in foods.filter(item =>!item.purchased)"
:key="food.id">
<img v-bind:src="food.image" />
<span>{{ food.name }}</span>
<input type="checkbox" v-model="food.purchased" />
</li>
</ul>
</section>
<section>
<h2>已购零食</h2>
<ul>
<li v-for="food in foods.filter(item =>item.purchased)"
:key="food.id">
<img v-bind:src="food.image" />
<span>{{ food.name }}</span>
<input type="checkbox" v-model="food.purchased" />
</li>
</ul>
</section>
</div>
v-show 显示隐藏
// v-show 接收的值为true就显示,false就不显示
<img v-show="true"/>
- 举例:
<div id="app">
// 若数组无元素,长度为0,就是false,则不显示
<section v-show="foods.filter(item=>!item.purchased).length">
<h2>未购零食</h2>
<ul>
<li v-for="food in foods.filter(item =>!item.purchased)"
:key="food.id">
<img v-bind:src="food.image" />
<span>{{ food.name }}</span>
<input type="checkbox" v-model="food.purchased" />
</li>
</ul>
</section>
<section v-show="foods.filter(item=>item.purchased).length">
<h2>已购零食</h2>
<ul>
<li v-for="food in foods.filter(item =>item.purchased)"
:key="food.id">
<img v-bind:src="food.image" />
<span>{{ food.name }}</span>
<input type="checkbox" v-model="food.purchased" />
</li>
</ul>
</section>
</div>
computed 计算属性
- 也是
createApp
对象参数里的一个属性,计算属性在计算时才更新,提升性能。
computed:{
beforeBuy(){
return this.foods.filter(item =>!item.purchased);
},
afterBuy(){
return this.foods.filter(item =>item.purchased);
}
}
- 重复的
filter(item =>item.purchased)
部分,用computed
计算属性,上面的栗子就可以简写如下,函数名后面不需加括号:
<div id="app">
<section v-show="beforeBuy.length">
<h2>未购零食</h2>
<ul>
<li v-for="food in beforeBuy"
:key="food.id">
<img v-bind:src="food.image" />
<span>{{ food.name }}</span>
<input type="checkbox" v-model="food.purchased" />
</li>
</ul>
</section>
<section v-show="afterBuy.length">
<h2>已购零食</h2>
<ul>
<li v-for="afterBuy"
:key="food.id">
<img v-bind:src="food.image" />
<span>{{ food.name }}</span>
<input type="checkbox" v-model="food.purchased" />
</li>
</ul>
</section>
</div>
template 属性
html
直接写在template
属性里,为Vue实例挂载到对应DOM节点上,解决了每个页面都有同样的元素,不用多次书写HTML,而是用Vue一对多地挂载上去,也就是组件的概念。
用es6-string-html
这个插件,可以解析出template里的html格式。
// App.js
export default{
template:/*html*/`
<section v-show="beforeBuy.length"> //函数名后面不需加括号
<h2>未购零食</h2>
<ul>
<li v-for="food in beforeBuy"
:key="food.id">
<img v-bind:src="food.image" />
<span>{{ food.name }}</span>
<input type="checkbox" v-model="food.purchased" />
</li>
</ul>
</section>
<section v-show="afterBuy.length">
<h2>已购零食</h2>
<ul>
<li v-for="afterBuy"
:key="food.id">
<img v-bind:src="food.image" />
<span>{{ food.name }}</span>
<input type="checkbox" v-model="food.purchased" />
</li>
</ul>
</section>
`,
data() {
return {
foods: [
{ id: 1, name: '原味', image: './images/原味.png', purchased: false },
{ id: 2, name: '辣味', image: './images/辣味.png', purchased: false },
{ id: 3, name: '炭烧味', image: './images/炭烧味.png', purchased: false }
]
}
},
computed:{
beforeBuy(){
return this.foods.filter(item =>!item.purchased);
},
afterBuy(){
return this.foods.filter(item =>item.purchased);
}
}
}
// index.html
<div id="app"></div>
<script type="module">
import App from './components/App.js';
Vue.createApp(App).mount('#app');
</script>
- 深度拆组件
// AppSection.js
export default{
template:/*html*/`
<section v-show="beforeBuy.length"> //函数名后面不需加括号
<h2>未购零食</h2>
<ul>
<li v-for="food in befforeBuy"
:key="food.id">
<img v-bind:src="food.image" />
<span>{{ food.name }}</span>
<input type="checkbox" v-model="food.purchased" />
</li>
</ul>
</section>
<section v-show="afterBuy.length">
<h2>已购零食</h2>
<ul>
<li v-for="afterBuy"
:key="food.id">
<img v-bind:src="food.image" />
<span>{{ food.name }}</span>
<input type="checkbox" v-model="food.purchased" />
</li>
</ul>
</section>
`,
data() {
return {
foods: [
{ id: 1, name: '原味', image: './images/原味.png', purchased: false },
{ id: 2, name: '辣味', image: './images/辣味.png', purchased: false },
{ id: 3, name: '炭烧味', image: './images/炭烧味.png', purchased: false }
]
}
},
computed:{
beforeBuy(){
return this.foods.filter(item =>!item.purchased);
},
afterBuy(){
return this.foods.filter(item =>item.purchased);
}
}
}
components 属性
// App.js
import AppSections from "./AppSections.js" // 引入 AppSections 对象
export default{
// 注册 AppSections 子组件
components:{AppSections}, // 用components属性,接收一个对象,就是组件名
template:/*html*/`
<app-sections></app-sections> // vue会把此 标签名 与 组件名 对应起来
`
}
- 继续拆组件,因为拆组件是vue逻辑流程,把上下两个列表拆出来
// AppSectionsList.js
export default {
template:/*html*/`
<section v-show="beforeBuy.length">
<h2>未购零食</h2>
<ul>
<li v-for="food in beforeBuy"
:key="food.id">
<img v-bind:src="food.image" />
<span>{{ food.name }}</span>
<input type="checkbox" v-model="food.purchased" />
</li>
</ul>
</section>
`,
data() {
return {
foods: [
{ id: 1, name: '原味', image: './images/原味.png', purchased: false },
{ id: 2, name: '辣味', image: './images/辣味.png', purchased: false },
{ id: 3, name: '炭烧味', image: './images/炭烧味.png', purchased: false }
]
}
},
computed:{
beforeBuy(){
return this.foods.filter(item =>!item.purchased);
},
afterBuy(){
return this.foods.filter(item =>item.purchased);
}
}
}
// AppSection.js
import AppSectionsList from "./AppSectionsList.js" // 引入 AppSectionsList 对象
export default{
components:{ AppSectionsList }
template:/*html*/`
<app-section-list></app-section-list>
<section v-show="afterBuy.length">
<h2>已购零食</h2>
<ul>
<li v-for="afterBuy"
:key="food.id">
<img v-bind:src="food.image" />
<span>{{ food.name }}</span>
<input type="checkbox" v-model="food.purchased" />
</li>
</ul>
</section>
`
props 属性
全称为properties
属性
- 每个子组件都可以调用
props
属性,这个属性接受一个对象:
// 在子组件里用 props 接收这个 headline 里面的数据 并注明接收数据的类型
props: {
headline:String
}
- 在父组件
AppSection
直接输入headline="未购零食"
// 在父组件标签里 显式 定义一个 headline 属性 并给属性传一个字符 未购零食
<app-section-list headline="未购零食"></app-section-list>
- 整理为如下:
// 父组件(使用层) AppSection.js
template:/*html*/`
<app-section-list headline="未购零食"></app-section-list>
// 子组件(底层) AppSectionsList.js
export default {
props:{
headline: String
}
}
- 如果用上父组件的数据,用
v-bind
绑定一个叫beforeBuyChild
的属性,可简写为:beforeBuyChild
,接着传入beforeBuy
数据:
// 父组件(使用层) AppSection.js
<app-section-list headline="未购零食" :beforeBuyChild="beforeBuy">
</app-section-list>
- 在子组件上用
props
接收,接收的属性名为beforeBuyChild
,接收的数据是数组,并把原来的beforeBuy
都统一为beforeBuyChild
,使其对应自己组件的数据名。
// 子组件(底层) AppSectionsList.js
export default {
template:/*html*/`
<section v-show="beforeBuyChild.length">
<h2>未购零食</h2>
<ul>
<li v-for="food in beforeBuyChild"
:key="food.id">
<img v-bind:src="food.image" />
<span>{{ food.name }}</span>
<input type="checkbox" v-model="food.purchased" />
</li>
</ul>
</section>
`,
props:{
headline: String,
beforeBuyChild: Array
}
}
props 用法小结
- 先在父组件标签里定义特殊属性,并且在属性值这里传入父组件的数据
- 子组件用
props
接收就可以使用了
- 因为已购和未购零食的结构是一样的,复用子组件
AppSectionsList
,但是在复用之前需要把计算属性合并:
// AppSection.js
computed:{
// 创建一个新的计算属性函数 fillters
fillters(){
return{
beforeBuy: this.foods.filter(item =>!item.purchased),
afterBuy: this.foods.filter(item =>item.purchased)
}
}
}
- 用对象属性值的方式访问:
// AppSection.js
import AppSectionsList from "./AppSectionsList.js"
export default{
components:{ AppSectionsList }
template:`
<app-section-list
headline="未购零食"
:BuyChild="fillters.beforeBuy">
</app-section-list>
<app-section-list
headline="已购零食"
:BuyChild="fillters.afterBuy">
</app-section-list>
`,
data() {
return {
foods: [
{ id: 1, name: '原味', image: './images/原味.png', purchased: false },
{ id: 2, name: '辣味', image: './images/辣味.png', purchased: false },
{ id: 3, name: '炭烧味', image: './images/炭烧味.png', purchased: false }
]
}
},
computed:{
fillters(){
return{
beforeBuy: this.foods.filter(item =>!item.purchased),
afterBuy: this.foods.filter(item =>item.purchased)
}
}
}
}
- 基于父组件计算属性的合并,并用对象属性值的方式进行访问,数组类型变为对象类型;子组件接收的是对象
Object
,把原来的beforeBuyChild
都改为buyChild
:
// AppSectionsList.js
export default {
template:/*html*/`
<section v-show="beforeBuyChild.length">
<h2>未购零食</h2>
<ul>
<li v-for="food in beforeBuyChild"
:key="food.id">
<img v-bind:src="food.image" />
<span>{{ food.name }}</span>
<input type="checkbox" v-model="food.purchased" />
</li>
</ul>
</section>
`,
props:{
headline: String,
buyChild: Object
}
}
- 小结:
未购清单设置buyChild
属性,并且传入beforeBuy
的返回内容,已购清单也设置buyChild
属性,但传入afterBuy
的返回内容,在子组件里用buyChild
一次性把两个内容都接收过来,完成复用。
v-on 监听事件
// AppSection.js
import AppSectionsList from "./AppSectionsList.js"
export default{
components:{ AppSectionsList }
template:`
<app-section-list
headline="未购零食"
:BuyChild="fillters.beforeBuy">
</app-section-list>
<app-section-list
headline="已购零食"
:BuyChild="fillters.afterBuy">
</app-section-list>
<form> // 加入表单元素
<input type="text" placeholder="输入爱吃的"/>
<button type="submit">添加</button>
</form>
`,
- 在需要监听的元素上添加
v-on:click=""
<button v-on:click="add" type="submit">添加</button>
methods属性
// .prevent去掉默认的刷新
<form v-on:submit.prevent="add"> // 加入表单元素
<form @submit.prevent="add"> // 缩写形式
<input type="text" placeholder="输入爱吃的"/>
<button type="submit">添加</button>
</form>
methods:{
add(){
this.foods.push({
id:this.foods.length + 1,
name:'',
image:'../images/youyu.png',
purchased:false
});
}
}
- 如何获取到
input
里面的内容?用v-model
双向数据绑定,先设置一个临时的空字符newFood
,用于临时存放v-model
的数据
data(){
return{
foods:[
{id:1, name:'原味'},
{id:2, name:'辣味'},
{id:3, name:'炭烧'}
],
newFood:‘’
}
}
- 在
input
里设置v-model
,并传入这个newFood
<input type="text" placeholder="输入爱吃的" v-model="newFood"/>
methods:{
add(){
this.foods.push({
id:this.foods.length + 1,
name:this.newFood, // 和input里的value绑定的
image:'../images/youyu.png',
purchased:false
});
this.newFood = '';
}
}
v-if 指令判断
判断传入的值是true
(显示元素)还是false
(调到下一个条件)
- 在
button
上添加条件判断:
// AppSections.js
<form @submit.prevent="add">
<input type="text" placeholder="输入爱吃的" v-model="newFood"/>
<button type="submit" v-if="foods.length <=3">添加</button>
<button type="submit" v-else-if="foods.length >3 && foods.length <5 ">再加</button>
<button type="submit" v-else>继续加</button>
- 添加样式:
// AppSections.js
<form @submit.prevent="add">
<input type="text" placeholder="输入爱吃的" v-model="newFood"/>
<button type="submit" v-if="foods.length <=3">添加</button>
<button type="submit" :style="{backgroundColor:aquamarine}"
v-else-if="foods.length >3 && foods.length <5 ">再加</button>
<button type="submit" v-else>继续加</button>
v-bind:class="{类名:布尔值}"
.buttonColor{
background-color:aquamarine;
}
<button type="submit" :class="{backgroundColor:true}"
v-else-if="foods.length >3 && foods.length <5 ">再加</button>
<button type="submit" :class="{backgroundColor:true}
v-else>继续加</button>
- 把
form
表单拆成一个单独组件
// AppSectionsForm.js
export default{
template:`
<form @submit.prevent="add">
<input type="text" placeholder="输入爱吃的" v-model="newFood"/>
<button type="submit">添加</button>
</form>
`
}
// AppSections.js
import AppSectionsList from "./AppSectionsList.js";
import AppSectionsForm from "./AppSectionsForm.js";
export default{
components:{AppSectionsList, AppSectionsForm},
template:`
<app-sections-list headline="未购零食" :buyChild="filter.beforeBuy"></app-sections-list>
<app-sections-list headline="已购零食" :buyChild="filter.afterBuy"></app-sections-list>
<app-sections-form></app-sections-form>
`
}
- 分析缺少的数据:
// AppSectionsForm.js
export default{
template:`
<form @submit.prevent="add">
<input type="text" placeholder="输入爱吃的" v-model="newFood"/>
<button type="submit">添加</button>
</form>
`,
data(){
return{
newFood:''
}
},
methods:{
add(){
// 用到的 foods 子组件中没有,不能直接使用
this.foods.push({
id:this.foods.length + 1,
name:this.newFood, // 和input里的value绑定的
image:'../images/youyu.png',
purchased:false
});
this.newFood = '';
}
}
}
$emit
- 上例中用到的foods子组件中没有,不能直接使用;用
props
传值也不方便,就可以用$emit
向全局暴露自己的位置,让父组件使用,上例修改add
写法:
// AppSectionsForm.js
methods:{
add(){
this.$emit('add', this.newFood)
this.newFood = '';
}
}
- 修改父组件的add方法,并监听:
// AppSections.js
<app-sections-form
@add=“fatherAdd”></app-sections-form>
methods:{
fatherAdd(youyusi){
id:this.foods.length + 1,
name:youyusi
image:'../images/youyu.png',
purchased:false
}
}
父子通信小结
- 父传子,子用
props
接收 - 子传父,子用
$emit
向外暴露