1 Vue 脚手架
1.1 Vue 脚手架安装
Vue CLI
是一个基于 Vue.js 进行快速开发的完整系统。
安装最新版本 vue-cli
npm install -g @vue/cli
安装vue-cli 3.x及以上指定版本
npm install '@vue-cli@3.x.x' -g
检查安装是否成功
vue -V
创建项目
vue create xxx
看项目需求,可以选择vue2和vue3
运行项目
npm run serve
1.2 项目示例
模板项目结构
├── node_modules
├── public
│ ├── favicon.ico: 页签图标
│ └── index.html: 主页面
├── src
│ ├── assets: 存放静态资源
│ │ └── logo.png
│ │── component: 存放组件
│ │ └── School.vue
│ │── App.vue: 汇总所有组件
│ │── main.js: 入口文件
├── .gitignore: git版本管制忽略的配置
├── babel.config.js: babel的配置文件
├── package.json: 应用包配置文件
├── README.md: 应用描述文件
├── package-lock.json:包版本控制文件
├── vue.config.js:vue可选的配置文件
代码展示
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8" />
<!--让IE浏览器以最高的渲染级别渲染页面 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<!-- 开启移动端的理想视口 -->
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<!-- 配置页签图标 -->
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
<!-- 配置网页标题 -->
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<!-- 当浏览器不支持js时 noscript中的元素就会被渲染 -->
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<!-- 容器 -->
<div id="app"></div>
<!-- 此处不用App.vue vue脚手架默认App.vue在此编译 -->
</body>
</html>
/*
该文件是整个项目的入口文件
*/
//引入Vue
import Vue from 'vue';
//引入父组件App
import App from './App.vue';
//关闭vue的生产提示
Vue.config.productionTip = false;
//创建Vue实例对象
new Vue({
//挂载dom元素:该实例为#app标签服务
el: '#app',
//创建App模板,将App组件放到容器中
render: h => h(App),
});
<template>
<!-- 组件模板必须只包含一个根元素,这个根元素为遍历起始点 -->
<div>
<img src="./assets/logo.png" alt="log" />
<!-- 使用子组件 -->
<School></School>
<Student></Student>
</div>
</template>
<script>
//引入子组件
import School from "./components/School.vue";
import Student from "./components/Student.vue";
export default {
name: "App",//可以不写
//注册子组件
components: { Student, School },
};
</script>
<style>
</style>
<template>
<!-- 组件模板 -->
<div class="demo">
<h2>学校名称:{{ name }}</h2>
<h2>学校地址:{{ address }}</h2>
<button @click="showName">点我提示学校名</button>
</div>
</template>
<script>
// 组件交互(数据、方法相关代码)
export default {
//此处省略了Vue.extend()
//组件名
name: "School",
data() {
return {
name: "尚硅谷",
address: "北京",
};
},
methods: {
showName() {
alert(this.name);
},
},
};
</script>
<style>
/* 组件样式 */
.demo {
background-color: pink;
}
</style>
<template>
<div>
<h2>学生姓名:{{ name }}</h2>
<h2>学校年龄:{{ age }}</h2>
</div>
</template>
<script>
export default {
//name: "Student",
data() {
return {
name: "张三",
age: 18,
};
},
};
</script>
<style>
</style>
项目报错
报错原因
:eslint语法检查的时候把命名不规范的代码当成了错误
解决方案
:
- 更改组件名,使其符合Vue推荐的双驼峰或-衔接命名规范,如: StudentName 或者 student-name
- 修改配置项,关闭eslint语法检查
1.在项目的根目录找到(没有就创建)vue.config.js文件
2.在文件中添加如下内容,随后保存文件重新编译即可
module.exports = {
lintOnSave: false, //关闭eslint检查
};
关于不同版本的Vue
vue.js与vue.runtime.xxx.js(main.js中引入的运行版)的区别:
vue.js是完整版的Vue,包含:核心功能 + 模板解析器。
vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。
因为vue.runtime.xxx.js没有模板解析器,所以不能使用template这个配置项,需要使用render函数接收到的createElement函数去指定具体内容。render 函数和 template 一样都是创建 html 模板的
vue.config.js配置文件
使用vue inspect > output.js可以查看到Vue脚手架的默认配置。
使用vue.config.js可以对脚手架进行个性化定制,详情见:https://cli.vuejs.org/zh
2 Vue组件小知识
2.1 ref属性
ref
被用来给元素或子组件注册引用信息(id的替代者),应用在html标签上获取的是真实DOM元素或应用在组件标签上是组件实例对象(vc)
使用方式
:
//打标识
<h1 ref="xxx">.....</h1>或 <School ref="xxx"></School>
//获取
this.$refs.xxx
<template>
<div>
<h1 v-text="msg" ref="title"></h1>
<button ref="btn" @click="showDOM">点我输出上方的DOM元素</button>
<School ref="sch" />
</div>
</template>
<script>
//引入School组件
import School from "./components/School";
export default {
name: "App",
components: { School },
data() {
return {
msg: "欢迎学习Vue!",
};
},
methods: {
showDOM() {
console.log(this.$refs.title); //真实DOM元素
console.log(this.$refs.btn); //真实DOM元素
console.log(this.$refs.sch); //School组件的实例对象(vc)
},
},
};
</script>
2.2 props配置项
props配置项
:让组件接收外部传过来的数据(prop属性)
父组件:传递数据
//name="xxx"为传递的数据
<Student name="xxx"/>
子组件:接收数据
//第一种方式(只接收)最常用
props:['name']
//第二种方式(限制类型)
props:{name:String}
//第三种方式(限制类型、限制必要性、指定默认值)
props:{
name:{
type:String, //类型
required:true, //必要性
default:'张三' //默认值
}
}
备注
:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。
<template>
<div>
<!-- 传递数据 -->
<Student name='李四' sex='女' :age='18'></Student>
</div>
</template>
<script>
//引入子组件
import Student from "./components/Student.vue";
export default {
name: "App",
components: { Student },
};
</script>
<style>
</style>
<template>
<div>
<h2>学生姓名:{{ name }}</h2>
<h2>学生性别:{{ sex }}</h2>
<h2>学生年龄:{{ myAge + 1 }}</h2>
<button @click="updateAge">尝试修改收到的年龄</button>
</div>
</template>
<script>
export default {
name: "Student",
data() {
return {
//若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据
myAge: this.age,
};
},
methods: {
updateAge() {
this.myAge++;
},
},
//接收数据
//1.简单声明接收
props:['name','age','sex']
//2.接收的同时对数据进行类型限制
/* props:{
name:String,
age:Number,
sex:String
} */
//3.接收的同时对数据:进行类型限制+默认值的指定+必要性的限制
/*props: {
name: {
type: String, //name的类型是字符串
required: true, //name是必要的
},
age: {
type: Number,
default: 23, //默认值
},
sex: {
type: String,
required: true,
},
},*/
};
</script>
2.3 mixin(混入)
mixin(混入)
:可以把多个组件共用的配置提取成一个混入对象
使用方式
:
第一步:定义混合
{
data(){....},
methods:{....}
....
}
//定义混合
export const mixin = {
methods: {
showName() {
alert(this.name);
},
},
};
第二步:使用混入
全局混入:Vue.mixin(xxx)
//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
import {mixin} from './mixin'
//全局混入
Vue.mixin(mixin)
//创建vm
new Vue({
el:'#app',
render: h => h(App)
})
局部混入:mixins:[‘xxx’]
<template>
<div class="demo">
<h2 @click="showName">学校名称:{{ name }}</h2>
<h2>学校地址:{{ address }}</h2>
</div>
</template>
<script>
import { mixin } from "../mixin";
export default {
data() {
return {
name: "尚硅谷",
address: "北京",
};
},
mixins: [mixin],
};
</script>
<style>
</style>
<template>
<div>
<h2 @click="showName">学生姓名:{{ name }}</h2>
<h2>学生性别:{{ sex }}</h2>
</div>
</template>
<script>
import {mixin} from '../mixin'
export default {
name: "Student",
data() {
return {
name: "张三",
sex: "男",
};
},
//局部混入
mixins:[mixin]
};
</script>
2.4 Vue插件
Vue插件
:用于增强Vue
本质
:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。
定义插件
对象.install = function (Vue, options) {
// 1. 添加全局过滤器
Vue.filter(....)
// 2. 添加全局指令
Vue.directive(....)
// 3. 配置全局混入(合)
Vue.mixin(....)
// 4. 添加实例方法
Vue.prototype.myMethod = function () {...}
Vue.prototype.myProperty = xxxx
}
使用插件
Vue.use()
实例
export default {
install(Vue) {
//全局过滤器
Vue.filter("mySlice", function (value) {
return value.slice(0, 4);
});
//给Vue原型上添加一个方法(vm和vc就都能用了)
Vue.prototype.hello = () => {
alert("你好啊");
};
},
};
//引入Vue
import Vue from "vue";
//引入App
import App from "./App.vue";
//引入插件
import plugins from "./plugins";
//使用插件
Vue.use(plugins);
//创建vm
new Vue({
el: "#app",
render: (h) => h(App),
});
<template>
<div>
<h2>学校名称:{{ name | mySlice }}</h2>
<h2>学校地址:{{ address }}</h2>
<button @click="test">点我测试一个hello方法</button>
</div>
</template>
<script>
export default {
name: "School",
data() {
return {
name: "尚硅谷atguigu",
address: "北京",
};
},
methods: {
test() {
this.hello();
},
},
};
</script>
2.5 scoped样式
scoped样式
:让样式在局部生效,防止冲突。
写法
:
<style scoped>
</style>
2.6 nextTick
语法:this.$nextTick(回调函数)
作用:在下一次 DOM 更新结束后执行其指定的回调。
什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。
3. 组件间数据交互
父向子传值
:子组件设置props + 父组件设置v-bind属性绑定
子向父传值
: 子组件的$emit + 父组件设置v-on事件绑定
兄弟组件任意组件通信
:通过事件车的方式传递数据, 新建一个空的全局Vue对象 EventBus,利用$emit发送 , $on接收
3.1 父组件向子组件传值props
1.子组件内部通过props接收父组件传递过来的值
props传递数据原则:单向数据流,只能父传子
v-bind是不支持使用驼峰标识的,例如cUser
要改成c-User
。
Vue.component(‘menu-item',{
//props接受父组件传递的数据
props: ['title'],
template: '<div>{{ title }}</div>'
})
2.父组件通过属性绑定v-bind将值传递给子组件
//传统方式
<menu-item title="来自父组件的数据"></menu-item>
//动态绑定
<menu-item :title="title"></menu-item>
//07-父组件向子组件传值-props命名规则.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="app">
<div>{{pmsg}}</div>
//传统方式
<menu-item title='来自父组件的值'></menu-item>
//动态绑定
<menu-item :title='ptitle' content='hello'></menu-item>
</div>
<script type='text/javascript' src='js/vue.js'></script>
<script type='text/javascript'>
//子组件(相对根组件而言)
Vue.component('menu-item', {
//props接受父组件传递的数据
props: ['title', 'content'],
data: function () {
return {
msg: '子组件本身的数据'
}
},
template: `
<div>{{msg + '-----' +title + '-----' +content}}</div>
`
})
//Vue实例为根组件
const app = new Vue({
el: "#app",
data: {
pmsg: "父组件中内容",
ptitle: '动态绑定的值'
}
})
</script>
</body>
</html>
3.props属性名规则
-
在props中使用驼峰形式,在html中需要使用短横线的形式
-
字符串形式的模板中没有这个限制
Vue.component(‘menu-item', { // 在JavaScript中是驼峰式的 props: ['menuTitle'], template: '<div>{{ menuTitle }}</div>' }) //在html中是短横线方式的 <menu-item menu-title=“nihao"></menu-item>
4.props属性值类型
-
字符串String
-
数值Number
-
布尔值Boolean
-
数组Array
-
对象Object
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div id="app"> <div>{{pmsg}}</div> <menu-item :pstr='pstr' :pnum='12' pboo='true' :parr='parr' :pobj='pobj'></menu-item> </div> <script type="text/javascript" src="js/vue.js"></script> <script type="text/javascript"> Vue.component('menu-item', { props: ['pstr','pnum','pboo','parr','pobj'], template: ` <div> <div>{{pstr}}</div> <div>{{12 + pnum}}</div> <div>{{typeof pboo}}</div> <ul> <li :key='index' v-for='(item,index) in parr'>{{item}}</li> </ul> <span>{{pobj.name}}</span> <span>{{pobj.age}}</span> </div> </div> ` }); var vm = new Vue({ el: '#app', data: { pmsg: '父组件中内容', pstr: 'hello', parr: ['apple','orange','banana'], pobj: { name: 'lisi', age: 12 } } }); </script> </body> </html>
3.2 子组件向父组件传值$emit
1.子组件通过自定义事件$emit向父组件传递信息
子组件向父组件传值,使用自定义事件$emit
。$emit(自定义事件名称)
<button @click='$emit("enlarge-text")'>扩大父组件字体</button>
2.在父组件中通过v-on监听子组件的事件
<menu-item @enlarge-text='handle'></menu-item>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<!-- 整个app为父组件 -->
<div id="app">
<div :style='{fontSize:fontsize+"px"}'>{{msg}}</div>
<!-- 在父组件中监听子组件事件 -->
<menu-item @enlarge-text='handle'></menu-item>
</div>
<script type='text/javascript' src='js/vue.js'></script>
<script type='text/javascript'>
//子组件点击按钮控制父组件字体大小
//创建子组件
Vue.component('menu-item', {
template: `
<button @click='$emit("enlarge-text")'>扩大父组件字体</button>
`
})
const app = new Vue({
el: "#app",
data: {
msg: '父组件中的内容',
fontsize: 10
},
methods: {
handle: function () {
this.fontsize += 5;
}
}
})
</script>
</body>
</html>
3.子组件通过自定义事件向父组件传递信息,携带额外的值
5为另外额外的值
<button @click='$emit("enlarge-text", 5)'>扩大父组件中字体大小</button>
4.父组件监听子组件的事件
通过$event接受额外的值
<menu-item @enlarge-text='handle($event)'></menu-item>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="app">
<div :style='{fontSize:fontsize+"px"}'>{{msg}}</div>
<menu-item @enlarge-text='handle($event)'></menu-item>
</div>
<script type='text/javascript' src='js/vue.js'></script>
<script type='text/javascript'>
Vue.component('menu-item', {
template: `
<button @click='$emit("enlarge-text",5)'>扩大父组件字体</button>
`
})
const app = new Vue({
el: "#app",
data: {
msg: '父组件中的内容',
fontsize: 10
},
methods: {
handle: function (val) {
this.fontsize += val;
}
}
})
</script>
</body>
</html>
3.3 非父子组件间传值
1、任意组件之间传递数据需要借助于事件车,通过事件车的方式传递数据
2、创建一个Vue的实例,让各个组件共用同一个事件机制。
3、传递数据方,通过一个事件触发eventBus.
e
m
i
t
(
方
法
名
,
传
递
的
数
据
)
。
4
、
接
收
数
据
方
,
通
过
m
o
u
n
t
e
d
(
)
触
发
e
v
e
n
t
B
u
s
.
emit(方法名,传递的数据)。 4、接收数据方,通过mounted(){}触发eventBus.
emit(方法名,传递的数据)。4、接收数据方,通过mounted()触发eventBus.on (方法名,function(接收数据的参数){用该组件的数据接收传递过来的数据}),此时函数中的this已经发生了改变,可以使用箭头函数。
首先创建一个单独的js文件bus.js,内容如下
import Vue from 'vue'
export default new Vue
父组件如下:
<template>
<components-a></components-a>
<components-b></components-b>
</template>
子组件a如下:
<template>
<div>
<button @click="abtn">A按钮</button>
</div>
</template>
<script>
import from '../../js/bus.js'
export default {
name: 'components-a',
data () {
return {
‘msg':"我是组件A"
}
},
methods:{
abtn:function(){
let _this = this;
//$emit这个方法会触发一个事件
bus .$emit("myFun", _this.msg)
}
}
}
</script>
子组件b如下:
<template>
<div>
<div>{{btext}}</div>
</div>
</template>
<script>
import bus from '../../js/bus.js'
export default {
name: 'components-b',
data () {
return {
'btext':"我是B组件内容"
}
},
created:function(){
this.bbtn();
},
methods:{
bbtn:function(){
bus .$on("myFun",(message)=>{
//这里最好用箭头函数,不然this指向有问题
this.btext = message
})
}
}
}
</script>
这样的话点击组件A里面的按钮,组件B即可获取A组件传来的值。
4. 组件插槽
匿名插槽 唯一存在 <slot></slot>
具名插槽 可存在多个 <slot name="up"></slot>
作用域插槽:
插槽的使用:子组件定义插槽的位置,父组件定义插槽的内容, 控制内容的显示与隐藏
4.1 组件插槽的作用
编译作用域
:父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译。
插槽的目的
是让我们原来的设备具备更多的扩展性。比如电脑的USB我们可以插入U盘。
组件插槽是Vue的内置组件,为了让我们封装的组件更加具有扩展性,让使用者可以决定组件内部的一些内容到底展示什么。
组件插槽的作用:父组件向子组件传递内容
4.2 组件插槽基本用法
在子组件中,使用特殊的元素<slot>
就可以为子组件开启一个插槽。
该插槽插入什么内容取决于父组件如何使用。
如何封装合适呢?
抽取共性,保留不同。最好的封装方式就是将共性抽取到组件中,将不同预留为插槽。
1.插槽位置
Vue.component('alert-box', {
template: `
<div class="demo-alert-box">
<strong>Error!</strong>
<slot></slot>
</div>
`
})
2.插槽内容
<alert-box>Something had happened.</alert-box>
//12-插槽基本用法.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<alert-box>有bug发生</alert-box>
<alert-box>有一个警告</alert-box>
<alert-box></alert-box>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
Vue.component('alert-box', {
template: `
<div>
<strong>ERROR:</strong>
<slot>默认内容</slot>
</div>
`
});
var vm = new Vue({
el: '#app',
data: {
}
});
</script>
</body>
</html>
4.3 具名插槽用法
1. 插槽定义
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
2.插槽内容
//根据名称进行匹配,没有匹配到的放在默认插槽中
<base-layout>
<h1 slot="header">标题内容</h1>
<p>主要内容1</p>
<p>主要内容2</p>
<p slot="footer">底部内容</p>
</base-layout>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<base-layout>
<template slot='header'>
<p>标题信息1</p>
<p>标题信息2</p>
</template>
<p>主要内容1</p>
<p>主要内容2</p>
<template slot='footer'>
<p>底部信息信息1</p>
<p>底部信息信息2</p>
</template>
</base-layout>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
Vue.component('base-layout', {
template: `
<div>
<header>
<slot name='header'></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name='footer'></slot>
</footer>
</div>
`
});
var vm = new Vue({
el: '#app',
data: {
}
});
</script>
</body>
</html>
4.4 作用域插槽
应用场景
:父组件对子组件的内容进行加工处理
1.插槽定义
<ul>
<li v-for= "item in list" v-bind:key= "item.id" >
<slot v-bind:item="item">
{{item.name}}
</slot>
</li>
</ul>
2.插槽内容
<fruit-list v-bind:list= "list">
<template slot-scope="slotProps">
<strong v-if="slotProps.item.current">
{{ slotProps.item.text }}
</strong>
</template>
</fruit-list>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<style type="text/css">
.current {
color: orange;
}
</style>
<body>
<div id="app">
<fruit-list :list='list'>
<template slot-scope='slotProps'>
<strong v-if='slotProps.info.id==3' class="current">{{slotProps.info.name}}</strong>
<span v-else>{{slotProps.info.name}}</span>
</template>
</fruit-list>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
Vue.component('fruit-list', {
props: ['list'],
template: `
<div>
<li :key='item.id' v-for='item in list'>
<slot :info='item'>{{item.name}}</slot>
</li>
</div>
`
});
var vm = new Vue({
el: '#app',
data: {
list: [{
id: 1,
name: 'apple'
},{
id: 2,
name: 'orange'
},{
id: 3,
name: 'banana'
}]
}
});
</script>
</body>
</html>
5. 基于组件的案例:购物车
案例需求分析
:按照组件化方式实现业务需求
根据业务功能进行组件化划分
①标题组件(展示文本)
②列表组件(列表展示、商品数量变更、商品删除)
③结算组件(计算商品总额)
功能实现步骤
:
- 实现整体布局和样式效果
- 划分独立的功能组件
- 组合所有的子组件形成整体结构
- 逐个实现各个组件功
- 标题组件
- 列表组件
- 结算组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<!-- 引入css样式 -->
<link rel="stylesheet" type="text/css" href="购物车案例.css" />
</head>
<body>
<div id="app">
<div class="container">
<my-cart></my-cart>
</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
//标题组件
var CartTitle = {
//传递用户名数据
props: ['uname'],
template: `
<div class="title">{{uname}}的商品</div>
`
}
//列表组件
var CartList = {
props: ['list'],
//列表展示
//列表名
//商品数量变更
//商品数量减
//呈现数据 失去焦点呈现数据
//商品数量加
//商品删除
template: `
<div>
<div :key='item.id' v-for='item in list' class="item">
<img :src="item.img"/>
<div class="name">{{item.name}}</div>
<div class="change">
<a href="" @click.prevent='sub(item.id)'>-</a>
<input type="text" class="num" :value='item.num' @blur='changeNum(item.id, $event)'/>
<a href="" @click.prevent='add(item.id)'>+</a>
</div>
<div class="del" @click='del(item.id)'>×</div>
</div>
</div>
`,
methods: {
//商品数量输入域修改
changeNum: function (id, event) {
this.$emit('change-num', {
id: id,
type: 'change',
num: event.target.value //当前输入域最新值
});
},
//商品数量减
add: function (id) {
this.$emit('change-num', {
id: id,
type: 'add'
});
},
//商品数量加
sub: function (id) {
this.$emit('change-num', {
id: id,
type: 'sub'
});
},
//商品删除
del: function (id) {
// 把id传递给父组件
this.$emit('cart-del', id);
}
}
}
//结算组件
var CartTotal = {
props: ['list'],
template: `
<div class="total">
<span>总价:{{total}}</span>
<button>结算</button>
</div>
`,
computed: {
total: function () {
// 计算商品的总价
var t = 0;
this.list.forEach(item => {
t += item.price * item.num;
});
return t;
}
}
}
Vue.component('my-cart', {
data: function () {
return {
uname: '张三',
list: [{
id: 1,
name: 'TCL彩电',
price: 1000,
num: 1,
img: 'img/a.jpg'
}, {
id: 2,
name: '机顶盒',
price: 1000,
num: 1,
img: 'img/b.jpg'
}, {
id: 3,
name: '海尔冰箱',
price: 1000,
num: 1,
img: 'img/c.jpg'
}, {
id: 4,
name: '小米手机',
price: 1000,
num: 1,
img: 'img/d.jpg'
}, {
id: 5,
name: 'PPTV电视',
price: 1000,
num: 1,
img: 'img/e.jpg'
}]
}
},
template: `
<div class='cart'>
<cart-title :uname='uname'></cart-title>
<cart-list :list='list' @change-num='changeNum($event)' @cart-del='delCart($event)'></cart-list>
<cart-total :list='list'></cart-total>
</div>
`,
components: {
'cart-title': CartTitle,
'cart-list': CartList,
'cart-total': CartTotal
},
methods: {
changeNum: function (val) {
// 商品数量变更
//分为三种情况:商品数量输入域变更、商品数量加、商品数量减
if (val.type == 'change') {
// 根据子组件传递过来的数据,跟新list中对应的数据
this.list.some(item => {
if (item.id == val.id) {
item.num = val.num;
// 终止遍历
return true;
}
});
} else if (val.type == 'sub') {
// 减一操作
this.list.some(item => {
if (item.id == val.id) {
item.num -= 1;
// 终止遍历
return true;
}
});
} else if (val.type == 'add') {
// 加一操作
this.list.some(item => {
if (item.id == val.id) {
item.num += 1;
// 终止遍历
return true;
}
});
}
},
//列表删除功能
delCart: function (id) {
// 根据id删除list中对应的数据
// 1、找到id所对应数据的索引
var index = this.list.findIndex(item => {
return item.id == id;
});
// 2、根据索引删除对应数据
this.list.splice(index, 1);
}
}
});
var vm = new Vue({
el: '#app',
});
</script>
</body>
</html>