赞
踩
- export default{
- data(){
- return {
- username:"admin"
- }
- },
- beforeCreate(){
- console.log(this.username); // 报错,data还不可用
- },
- // 通常在这个阶段发送请求拿数据
- created(){
- console.log(this.username); // 输出 admin
- }
- }
- export default{
- data(){
- return {
- username:"admin"
- }
- },
- beforeMount(){
- console.log(this.$el); // undefined
- },
- mounted(){
- console.log(this.$el); // DOM节点信息
- }
- }
使用自定义属性,父组件传递值给子组件。
注意:子组件中不要修改props的值。 如果传递的普通类型,JS会复制一份给子组件,子组件就算修改了,父组件的值也不会修改。 如果是引用类型虽然可以修改但也不建议子组件直接去修改。
- // 子组件
- <div @click="add">count +1</div>
-
- export default{
- data(){
- return {
- count:0
- }
- },
- methods:{
- add(){
- this.count += 1;
- // 产生一个自定义事件,并将count传递过去
- this.$emit('countChanged',this.count);
- }
- }
- }
- // 父组件
- <son @countChanged="countChangedHandler"></son>
-
-
- import son from "*****";
- export default{
- data(){
- return {
- bookCount:0
- }
- },
- // 注册组件
- components:{
- son
- },
- methods:{
- countChangedHandler(count){
- // count是子组件传递来的
- this.bookCount = count;
- }
- }
- }
eventBus.js 虽然创建了vue实例但并不去渲染任何东西,只负责帮助两个组件之间的数据传递。 eventBus.js这个文件仍然放在 components 文件夹下。
每个Vue组件实例上都有一个 $refs 对象。里面存储了对应的DOM元素或组件的引用。 默认$refs指向一个空对象。 当我们在vue中给任意一个Dom添加 ref属性后,这个Dom属性就会出现在$refs中。
- <template>
- <div>
- <h1 ref="title">Hello</h1>
- <button @click="btnClick">this</button>
- </div>
- </template>
-
- <script>
- expoprt default{
- methods:{
- btnClick(){
- console.log(this); // this是一个VueComponent里面的$refs中又一个title它就是h1这个DOM对象
- console.log(this.$refs.title); // 输出DOM对象
- }
- }
- }
- </script>
如果我们给子组件设置了ref 那么这个组件的实例对象将被存储在 $refs 中。所以可以在父组件中使用 $refs.refname 获取到子组件的实例对象。
- // son
- export default{
- data(){
- return {
- msg:"Hello!"
- }
- },
- methods:{
- sonMethod(){
- console.log(this.msg);
- }
- }
- }
-
-
- <template>
- <div>
- <son ref="sonComponent"></son>
- </div>
- </template>
-
- <script>
- import son from "....";
-
- export default {
- components:{
- son
- },
- methods:{
- test(){
- const son = this.$refs.sonComponent;
- son.sonMethod(); // 调用子组件里的sonMethod方法
- son.msg; // 获取子组件的数据
- }
- }
- }
- </script>
当使用v-if切换组件或dom元素时,如果是从false切换到true。这个组件的代码会被重新写入到HTML中,而这一个过程是需要时间的,如果在写入之前就去操作这个组件或DOM元素,就会出现该组件是一个undefine的错误。
this.$nextTick(cb()); 在Dom渲染完毕后回调cb方法。 操作DOM元素的代码应该写在回调方法中。
循环遍历数组,在方法中返回boolean。当有一个返回true时结果为true且立即结束循环。 如果都返回false,结果为false.
问题:为什么不使用forEach? 因为foreach一旦开始就无法在中途停止循环,如果使用return强制停止会终止整个方法的运行
- function haveA(arr){
- let result = arr.some((item,index)=>{
- if(item == 'a'){
- return true;
- }
- });
-
- if(result){
- console.log("have a");
- }
- }
-
- // forEach虽然可以实现但他会循环所有元素,消耗资源
- function haveA(arr){
- let result = false;
- arr.forEach((item,index)=>{
- if(item == 'a'){
- result = true;
- }
- });
-
- if(result){
- console.log("have a");
- }
- }
every和some的区别就是只有当所有列表项都返回true时,结果才为true.
reduce 就是累加器,最终结果是每次循环返回的数值的总和。
- const arr = [
- // status代表是否选中
- {name:'西瓜',status:true,price:10,count:2},
- {name:'榴莲',status:false,price:90,count:0},
- {name:'葡萄',status:true,price:12,count:3},
- {name:'草莓',status:false,price:20,count:0}
- ];
-
- // 求选中的水果的总价格
- let result = arr.filter(item => item.status).reduce((amt,item)=>{
- return amt += item.count * item.price;
- },0);
-
- // 解释
- arr.filter(item => item.status) //先过滤出被选中的
- .reduce((累加的结果,当前遍历项)=>{},初始值); //累加总价格
-
arr.filter(item=>item.status).reduce((amt,item)=>amt += item.count*item.price,0);
创建项目: vue create cart-demo 该项目只需要使用到 scss,babel。
打开App.vue在该文件中对标签清除一下样式
- <style lang="scss">
- *{
- margin:0px;
- padding: 0px;
- list-style: none;
- font-size: 14px;
- color: black;
- }
- #app{
- padding: 0px 20px;
- }
- </style>
在components文件夹下创建 header.vue文件
- <template>
- <div class="header">购物车</div>
- </template>
-
- <script>
- export default {
-
- }
- </script>
-
- <style lang="scss" scoped>
- .header{
- color: #7e74b1;
- font-size: 24px;
- padding: 10px 0px;
- }
- </style>
在App.vue中引入并使用该组件
- <template>
- <div id="app">
- <headerCom></headerCom>
- </div>
- </template>
-
- <script>
- // 不能叫header因为H5中有header标签
- import headerCom from '@/components/header.vue';
-
- export default {
- name: 'App',
- components: {
- headerCom
- }
- }
- </script>
安装 axios:
cnpm install axios -S
在App.vue中定义请求方法
- methods:{
- async initCartList(){
- const {data:res} = await axios.get('https://escook.cn/api/cart');
- console.log(res);
- }
- },
我们应该在渲染界面前,实例化对象后请求,所以应该在created生命周期中调用该方法
- created(){
- this.initCartList();
- },
将数据保存到data中
- data(){
- return {
- cartList:[]
- }
- },
- methods:{
- async initCartList(){
- const {data:res} = await axios.get('https://escook.cn/api/cart');
- if(res.status == 200){
- this.cartList = res.list;
- }
- }
- },
该组件就是用来显示每一条商品的信息的。
- <template>
- <div class="goodsitem-container flex align-item-center">
- <!-- 多选框 -->
- <div class="checkBox checkBox-checked flex flex-center flex-noshrink"></div>
- <!-- 商品图片 -->
- <img src="@/assets/logo.png" alt="商品图" class="goodsImg flex-noshrink">
- <!-- 商品信息 -->
- <div class="goodsInfo flex flex-column justify-content-between">
- <div class="goodsInfo-title">班俏BANQIAO超火ins潮卫衣女士2020秋季新款韩版宽松慵懒风薄款外套带帽上衣</div>
- <div class="goodsInfo-bottom flex justify-content-between">
- <div class="goodsInfo-price">$199</div>
- <div class="goodsInfo-count flex">
- <div class="goodsInfo-count-sub flex flex-center">-</div>
- <div class="goodsInfo-count-num flex flex-center">1</div>
- <div class="goodsInfo-count-plus flex flex-center">+</div>
- </div>
- </div>
- </div>
- <!-- 商品信息end -->
- </div>
- </template>
-
- <script>
- export default {
-
- }
- </script>
-
- <style lang="scss" scoped>
- // 总容器
- .goodsitem-container{
- width:100%;
- margin-bottom: 38px;
- // 商品信息
- .goodsInfo{
- margin-left: 12px;
- flex-grow: 1;
- height: 70px;
- overflow: hidden;
- max-width: 65%;
- div{
- font-weight: bold;
- font-size: 16px;
- color: #7e74b1;
- }
- .goodsInfo-title{
- white-space: nowrap;
- }
- // 计数器
- .goodsInfo-count{
- .goodsInfo-count-sub,.goodsInfo-count-plus{
- background-color: #f7f6fe;
- width:26px;
- height: 24px;
- border: 1px solid rgba($color: #000000, $alpha: 0.3);
- }
- .goodsInfo-count-sub{
- border-radius: 10px 0px 0px 10px;
- }
- .goodsInfo-count-plus{
- border-radius: 0px 10px 10px 0px;
- }
- .goodsInfo-count-num{
- width:44px;
- height: 24px;
- border-top: 1px solid rgba($color: #000000, $alpha: 0.3);
- border-bottom: 1px solid rgba($color: #000000, $alpha: 0.3);
- }
- }
-
- }
- // 商品图片
- .goodsImg{
- width:70px;
- height: 70px;
- border-radius: 10px;
- overflow: hidden;
- margin-left: 16px;
- background-color: #7e74b1;
- }
- // 多选框
- .checkBox{
- width:26px;
- height:26px;
- border-radius: 100px;
- background-color: #7e74b1;
- }
- // 多选框被选中的样式
- .checkBox-checked::after{
- content:'';
- display: table;
- width: 16px;
- height:16px;
- border-radius: 100px;
- background-color: rgb(212, 240, 89);
- }
- }
- .flex{
- display: flex;
- }
- .flex-noshrink{
- flex-shrink: 0;
- }
- .flex-center{
- justify-content: center;
- align-items: center;
- }
- .flex-column{
- flex-direction: column;
- }
- .justify-content-between{
- justify-content: space-between;
- }
- .align-item-center{
- align-items: center;
- }
- </style>
写好之后也是在App.vue中引用,注册并使用。
- <!-- 商品条目 -->
- <goodsItemVue v-for="item in cartList" :key="item.id"></goodsItemVue>
- <template>
- <div class="goodsitem-container flex align-item-center">
- <!-- 多选框 -->
- <div class="checkBox flex flex-center flex-noshrink" :class="{'checkBox-checked':state}"></div>
- <!-- 商品图片 -->
- <img :src="pic" alt="商品图" class="goodsImg flex-noshrink">
- <!-- 商品信息 -->
- <div class="goodsInfo flex flex-column justify-content-between">
- <div class="goodsInfo-title">{{ title }}</div>
- <div class="goodsInfo-bottom flex justify-content-between">
- <div class="goodsInfo-price">¥{{price}}</div>
- <div class="goodsInfo-count flex">
- <div class="goodsInfo-count-sub flex flex-center">-</div>
- <div class="goodsInfo-count-num flex flex-center">1</div>
- <div class="goodsInfo-count-plus flex flex-center">+</div>
- </div>
- </div>
- </div>
- <!-- 商品信息end -->
- </div>
- </template>
-
- <script>
- export default {
- props:{
- // 标题
- title:{
- defualt:'',
- type:String
- },
- // 图片
- pic:{
- defualt:'',
- type:String
- },
- // 价格
- price:{
- default:0,
- type:Number
- },
- // 选中状态
- state:{
- default:true,
- type:Boolean
- }
- }
- }
- </script>
- <!-- 商品条目 -->
- <goodsItemVue
- v-for="item in cartList" :key="item.id"
- :title="item.goods_name"
- :pic="item.goods_img"
- :price="item.goods_price"
- :state="item.goods_state"></goodsItemVue>
如果使用的不是自定义的多选框,而是checkbox不要使用 v-model 绑定选中状态,要使用v-bind因为我们不推荐组件中直接修改props的值。
刚刚我们将所有的数据都一 一传递过去,当然也可以直接将对象传递过去。
第一种方式的好处就是可以更好的复用组件,直接传递对象的话,如果另一个地方获取到的对象的属性名不一样就无法服用该组件。缺点就是麻烦。
在子组件中不可以直接修改props的值,但是我们可以通过发送事件通知父组件,让父组件去更新。
- <template>
- <div class="goodsitem-container flex align-item-center">
- <!-- 多选框 -->
- <!-- 添加一个点击事件 -->
- <div
- class="checkBox flex flex-center flex-noshrink"
- :class="{'checkBox-checked':state}"
- @click="stateChange"></div>
- ....
- </div>
- </template>
-
- <script>
- export default {
- props:{
- // 新增id 用于传递给父组件让父组件知道谁的选中状态被改变
- id:{
- type:Number,
- require:true
- },
- .....
- },
- methods:{
- /**
- * 多选框被点击
- */
- stateChange(){
- let event = {id:this.id,state:!this.state};
- // 发送消息
- this.$emit('state-change',event);
- }
- }
- }
- </script>
- <!-- 商品条目 -->
- <goodsItemVue
- v-for="item in cartList" :key="item.id"
- :id="item.id"
- :title="item.goods_name"
- :pic="item.goods_img"
- :price="item.goods_price"
- :state="item.goods_state"
- @state-change="goodsStateChange"></goodsItemVue>
-
-
- <script>
- ......
-
- /**
- * 商品选中状态改变
- */
- goodsStateChange(event){
- this.cartList.some(item=>{
- if(item.id == event.id){
- item.goods_state = event.state;
- // some可以结束循环
- return true;
- }
- });
- },
-
- ......
- </script>
- <template>
- <div class="footer-container flex justify-content-between align-item-center">
- <!-- 左边 -->
- <div class="left-box flex align-item-center">
- <!-- 多选框 -->
- <div
- class="checkBox flex flex-center flex-noshrink"
- :class="{'checkBox-checked':state}">
- </div>
- <span>全选</span>
- </div>
- <!-- 右边 -->
- <div class="right-box flex align-item-center">
- <span class="total">合计:¥{{ totalPrice }}</span>
- <!-- 结算按钮 -->
- <div class="settlement flex flex-center">下单</div>
- </div>
- </div>
- </template>
-
- <script>
- export default {
- props:{
- state:{
- type:Boolean,
- default:false
- },
- totalPrice:{
- type:Number,
- default:0
- }
- }
- }
- </script>
-
- <style lang="scss" scoped>
-
- // 右边
- .right-box{
- height: 100%;
- .total{
- color: #7e74b1;
- font-weight: bold;
- font-size: 18px;
- margin-right: 6px;
- }
- .settlement{
- background-color: #7e74b1;
- color: #fff;
- font-size: 18px;
- font-weight: bold;
- width: 110px;
- height: 100%;
- }
- }
-
- // 左边
- .left-box{
- padding-left:20px;
- span{
- color: #7e74b1;
- font-size: 16px;
- font-weight: bold;
- }
- // 多选框
- .checkBox{
- width:26px;
- height:26px;
- border-radius: 100px;
- background-color: #7e74b1;
- margin-right: 4px;
- }
- // 多选框被选中的样式
- .checkBox-checked::after{
- content:'';
- display: table;
- width: 16px;
- height:16px;
- border-radius: 100px;
- background-color: rgb(212, 240, 89);
- }
- }
-
- .footer-container{
- position:fixed;
- bottom: 0px;
- left: 0px;
- z-index: 100;
- width: 100vw;
- height: 60px;
- border-top: 1px solid rgba($color: #000000, $alpha: 0.3);
- border-bottom: 1px solid rgba($color: #000000, $alpha: 0.3);
- background-color: #fff;
- }
-
- .flex{
- display: flex;
- }
- .flex-noshrink{
- flex-shrink: 0;
- }
- .flex-center{
- justify-content: center;
- align-items: center;
- }
- .flex-column{
- flex-direction: column;
- }
- .justify-content-between{
- justify-content: space-between;
- }
- .align-item-center{
- align-items: center;
- }
- </style>
在App.vue中使用。
全选的状态取决于每个商品是否都被选中,所以我们要有一个计算属性去判断是否都被选中。
- <!-- 底部 -->
- <footerCom :state="fullState"></footerCom>
- computed:{
- fullState(){
- return this.cartList.every(item=>item.goods_state);
- // 上面的写法就相当于下面的写法
- return this.cartList.every(item=>{
- return item.goods_state === true;
- });
- }
- },
- <template>
- ...
- <div class="left-box flex align-item-center" @click="checkAll">
- <!-- 多选框 -->
- <div
- class="checkBox flex flex-center flex-noshrink"
- :class="{'checkBox-checked':state}">
- </div>
- <span>全选</span>
- </div>
- ...
- </template>
-
- <script>
- export default {
- ...
- methods:{
- checkAll(){
- this.$emit('check-all',{state:!this.state});
- }
- }
- }
- </script>
- // 给组件添加事件的HTML代码就不放出来了
-
- /**
- * 全选按钮被点击
- * @param {Object} event state选中状态
- */
- checkAllClick(event){
- this.cartList.forEach(item=>{
- item.goods_state = event.state;
- });
- },
- <!-- 左边 -->
- <div class="left-box flex align-item-center" @click="checkAll">
- <!-- 多选框 -->
- <div
- class="checkBox flex flex-center flex-noshrink"
- :class="{'checkBox-checked':state}">
- </div>
- <span>已选({{ checkCount }})</span>
- </div>
-
- // 将全选修改为 已选(选中个数)
-
- // 在props中传入
- checkCount:{
- type:Number,
- default:0
- }
App.vue中计算并传递
- <!-- 底部 -->
- <footerCom
- :state="fullState"
- :checkCount="checkCount"
- :totalPrice="totalPrice"
- @check-all="checkAllClick"></footerCom>
- // checkCount和totalPrice都是计算属性
- checkCount(){
- return this.cartList.reduce((count,item)=>{
- if(item.goods_state){
- return count+1;
- }else{
- return count;
- }
- },0);
- },
- totalPrice(){
- return this.cartList.reduce((price,item)=>{
- if(item.goods_state){
- return price + item.goods_price;
- }else{
- return price;
- }
- },0);
- }
- <div class="goodsInfo-count-sub flex flex-center" @click="subtractClick">-</div>
- <div class="goodsInfo-count-num flex flex-center">{{ count }}</div>
- <div class="goodsInfo-count-plus flex flex-center" @click="plusClick">+</div>
-
- <script>
- export default {
- props:{
- ......
- count:{
- type:Number,
- default:0
- }
- },
- methods:{
- /**
- * 减号被点击
- */
- subtractClick(){
- if(this.count <= 1) return;
- const data = {id:this.id,count:this.count};
- this.$emit('subtract-click',data);
- },
-
- /**
- * 加号点击
- */
- plusClick(){
- if(this.count >= 10) return;
- const data = {id:this.id,count:this.count};
- this.$emit('plus-click',data);
- },
-
- ........
- }
- }
- </script>
- // App.vue
- /**
- * 加号
- */
- plusClick(event){
- this.cartList.some(item=>{
- if(item.id == event.id){
- item.goods_count += 1;
- return true;
- }
- });
- },
-
- /**
- * 减号
- */
- subtractClick(event){
- this.cartList.some(item=>{
- if(item.id == event.id){
- item.goods_count -= 1;
- return true;
- }
- });
- },
一般数量的选择这种我们也会抽取成一个组件,此时就出现了两层嵌套,App嵌套goodsItem,goodsItem嵌套计数器。如果是父组件传递数据可以一层一层往里穿。如果计数器想通知App这个组件可以使用EventBus直接通知,而不需要一层层往上传递。
- // 修改计算总量和总价格的计算属性即可
-
- checkCount(){
- return this.cartList.reduce((count,item)=>{
- if(item.goods_state){
- return count+item.goods_count;
- }else{
- return count;
- }
- },0);
- },
- totalPrice(){
- return this.cartList.reduce((price,item)=>{
- if(item.goods_state){
- return price + (item.goods_price * item.goods_count);
- }else{
- return price;
- }
- },0);
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。