苏打笔记

2019-11-04  本文已影响0人  前端陈陈陈

每日生鲜知识体系

相关资料

需求地址

接口地址

二、Sticky 粘性布局(吸顶) 请看有赞ui文档

三、sku 商品规格

SKU全称为Stock Keeping Unit(库存量单位),即库存进出计量的基本单元,可以是以件,盒,托盘等为单位。SKU这是对于大型连锁超市DC(配送中心)物流管理的一个必要的方法。现在已经被引申为产品统一编号的简称,每种产品均对应有唯一的SKU号。单品:对一种商品而言,当其品牌、型号、配置、等级、花色、包装容量、单位、生产日期、保质期、用途、价格、产地等属性中任一属性与其他商品存在不同时,可称为一个单品。

四、滚动后跳转页面返回顶部问题

const routes = [...];
const router = new Router({
  scrollBehavior: () => ({
    y: 0
  }),
  routes
});

五、全局filter的使用

vue中分全局mixin和局部mixin ,平时记得使用

// 在mixin里面定义filters
export default {
  data() {
    return {};
  },

  filters: { //这里主要是设置格式化时间的
    fomatDate(time) {
      let date = new Date(time);
      let Y = date.getFullYear();
      let M = date.getMonth() + 1;
      let D = date.getDate();
      return `${Y}年-${M}月-${D}日`;
    },

    formatMoney(num) {
      return '¥' + num / 100;
    }

  },
    
    
    //filters使用的的时候,在vue文档需要使用的时候,只需要添加:|formatMoney
    //如下栗子:
    <!-- 购物车编辑完成的 -->
    <div class="goods-price" v-show="show">
     <span class="price">{{item.price|formatMoney}}</span> //这里是实用的例子
     <span class="old-price">¥55.9</span>
     <span class="count">X {{item.buyNum}}</span>
     </div>

//===================================================================================

  methods: {
    $loading(flag) {  //这里是全局封装loading 方法一:
      if (flag) {
        this.$toast.loading("努力加载中...");
      } else {
        this.$toast.clear();
      }
    }
  }
             //==============================
//全局封装loading方法二:
  $loading() {   //封装losding方法
        const toast = this.$toast.loading({
          duration: 0, // 持续展示 toast
          forbidClick: true,
          message: '加载中...'
        });
      },
      $loadingclon() {  //封装清除loading
        this.$toast.clear();
      }
   //方法二实际使用例子:

  //获取购物车所有列表
    grtcart() {
      this.$loading()  //加载loading  //使用
      let url = "/cart/all";
      this.$axios
        .get(url)
        .then(res => {
          this.Shoppinglist = res.list;
          console.log(res);
         this.$loadingclon()  //清除loading   //清除  注意:在移动端,失败的时候不需要清除loading
        })
        .catch(err => {
          console.log(err);
        });
    }
};
// 使用
 <span class="price">{{item.price | formatMoney }}</span>

六、组件的事件传参问题

七、keep-alive

// router的配置
{
    path: "list",
    meta: {
      title: "商品列表",
      keepAlive: true //表示需要缓存
    },
    component: () => import("@/views/product/list/index")
},
// app.vue配置
<template>
  <div>
    <keep-alive>
      <router-view v-if="$route.meta.keepAlive"></router-view>
    </keep-alive>
    <router-view v-if="!$route.meta.keepAlive"></router-view>
  </div>
</template>

八、登陆和注册共用组件

  1. 判断页面是注册还是登陆
  2. mixin的使用
  3. 正则表达式

九、组件内的路由守卫

  data() {
    return {
      // 从哪个页面跳过来
      from: "",
      phone: "15013795539",
      smsCode: "",
      password: ""
    };
  },

  beforeRouteEnter(to, from, next) {
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
    // console.log('router',this);
    console.log(to.path, from.path);
    next(vm => {
      // 通过 `vm` 访问组件实例
      console.log(vm.from);
      vm.from = from.path;
    });
  },

十、token和axios拦截器

// import axios from "axios";
import store from "../store";
import router from "../router";
// 创建axios实例
const service = axios.create({
  baseURL: process.env.VUE_APP_URL, // api 的 VUE_APP_URL
  timeout: 50000 // 请求超时时间(因为需要调试后台,所以设置得比较大)
});

// request拦截器,在请求之前做一些处理
service.interceptors.request.use(
  config => {
    if (store.state.token) {
      // 给请求头添加user-token
      config.headers["user-token"] = store.state.token;
    }
    return config;
  },
  error => {
    console.log(error); // for debug
    return Promise.reject(error);
  }
);

十一、路由守卫之登录验证

  1. 在需要登录的路由上添加 needLogin: true

    {
        path: 'submit',
        meta: {
           title: '确认订单',
           needLogin: true
        },
        component: ()=>import('@/views/order/children/submit')
    }
    
  2. 在路由守卫进行判断

    router.beforeEach((to, from, next) => {
      let { title, needLogin } = to.meta;
      let { isLogin } = store.state;
      document.title = title;
    
      if (needLogin && !isLogin) {
        next({
          path: "/login"
        });
      } else {
        next();
      }
    });
    

十二、需要登陆的接口,可以使用axios统一进行拦截

service.interceptors.response.use(
  response => {
    const res = response.data;
    if (res.code == "666") {
      return res;
    } else if (res.code == "603") {
      // code为603代表token已经失效,
      // 提示用户,然后跳转到登陆页面
      router.push("/login");
    } else {
      return Promise.reject({
        message: res.msg
      });
    }
  },
  error => {
    return Promise.reject(error);
  }
);

十三、vuex模块化

// cart.js
export default {
    state: {
        preOrderId: ''
    },

    mutations: {
        updatePreOrderId(state, payload) {
            state.preOrderId = payload;
        }
    }
}
// index.js
export default Vuex.Store({
    modules: {
        cart
    }
})
// 使用
  computed: {
    preOrderId() {
      return this.$store.state.cart.preOrderId;
    }
  },

十四、阻止form表单的默认行为

  <form onsubmit="return false;"></form>

十五、router的history模式(了解)

http://huruqing.cn/docs/Vue/advance/03.router.html

十六、devtools

十六、active-class

http://huruqing.cn/docs/Vue/advance/03.router.html

十七、定制主题和按需加载组件

定制主题的原理,通过修改less变量来实现

http://huruqing.cn/docs/Vant/list/101-%E5%AE%9A%E5%88%B6%E4%B8%BB%E9%A2%98%E6%A0%B7%E5%BC%8F.html

十八、rem

http://huruqing.cn/docs/Vue/advance/06.rem.html

vue-cli3用rem进行适配步骤

  1. 安装手淘的flexible,插件名称叫amfe-flexible

    npm i amfe-flexible --save-dev
    
  2. 在main.js导入

    import 'amfe-flexible'
    
  3. 在/vue.config.js添加px2rem插件,把项目中的px转为rem

    const autoprefixer = require("autoprefixer");
    const pxtorem = require("postcss-pxtorem");
    
    module.exports = {
      // 关闭eslint检查
      lintOnSave: false,
      // 配置css前缀,px转rem
      css: {
        loaderOptions: {
            // 后处理器配置
          postcss: {
            plugins: [
              // 配置样式前缀
              autoprefixer(),
              // 把px转为rem
              pxtorem({
                rootValue: 37.5,
                propList: ["*"]
              })
            ]
          }
        }
      }
    };
    

十九、父子通信踩坑指南

有一种场景: 父给子传参(本例是username),子组件通过input标签修改后再传回给父的情况

例1, 子组件有输入的情况, 使用原生js实现

通过event.target.value获取输入况的值,如果想使用v-model请看例子2(无异步请求)和例子3(有异步请求)

// 父组件
<template>
  <div>
      <p>
        父组件的username: {{username}}
      </p>

      <Child :username="username" @changeName="changeName"/>
  </div>
</template>

<script>
import Child from './Child'
export default {
  components: {
    Child
  },

  data() {
    return {
      username: 'huruqing'
    }
  },

  methods: {
    changeName(data) {
      this.username = data;
    }
  }
};
</script>

// 子组件
<template>
  <div>
    <hr />
    <p>
      子组件的username:
      <input type="text" :value="username" @input="fn" />
    </p>
  </div>
</template>

<script>
export default {
  props: {
    username: {
      type: String,
      required: true
    }
  },

  methods: {
    fn(evnet) {
      // console.log(event.target.value);
      let value = event.target.value;
      this.$emit("changeName", value);
    }
  }
};
</script>

例子2

通过定义个中间变量来实现父子传参(没有异步请求),同时也能使用v-model

// 父组件
<template>
  <div>
    <p>父组件的username: {{username}}</p>
    <Child :username="username" @changeName="changeName" />
  </div>
</template>
<script>
import Child from "./Child";
export default {
  components: {
    Child
  },
  data() {
    return {
      username: ""
    };
  },
  methods: {
    changeName(data) {
      this.username = data;
    }
  }
};
</script>

// 子组件
<template>
  <div>
    <hr />
    <p>
      子组件的username:
      <input type="text" v-model="username2" @input="fn" />
    </p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      username2: ""
    };
  },

  props: ["username"],

    created() {
        this.username2 = this.username;
    },

  methods: {
    fn() {
      this.$emit("changeName", this.username2);
    }
  }
};
</script>

例子3 通过定义个中间变量来实现父子传参(有异步请求),同时也能使用v-model

ps: 需要用watch来监听username的变化,如果用created只能拿到第一次的值

// 父组件
<template>
  <div>
    <p>父组件的username: {{username}}</p>
    <Child :username="username" @changeName="changeName" />
  </div>
</template>
<script>
import Child from "./Child";
export default {
  components: {
    Child
  },
  data() {
    return {
      username: ""
    };
  },

  created() {
    this.getData();
  },
  methods: {
    getData() {
      // 获得数据 username
      setTimeout(() => {
        let res = "huruqing";
        this.username = res;
      }, 5000);
    },

    changeName(data) {
      this.username = data;
    }
  }
};
</script>

// 子组件
<template>
  <div>
    <hr />
    <p>
      子组件的username:
      <input type="text" v-model="username2" @input="fn" />
    </p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      username2: ""
    };
  },

  props: ["username"],

  //   created() {
  //     console.log("username", this.username);
  //     this.username2 = this.username;
  //   },

  watch: {
    username: {
      handler(newVal, oldVal) {
        console.log("username的值:", newVal);
        this.username2 = newVal;
      },
      immediate: true
    }
  },

  methods: {
    fn() {
      this.$emit("changeName", this.username2);
    }
  }
};
</script>

二十、vuex踩坑指南

  1. 手动更改state的值,却发现值没有变化
    原因: 我们使用了本地持久化插件 vuex-persistedstate,只有使用mutation修改state的值才会生效,手动修改无效,state的值都会存储到localStoreage里面,如果想修改默认的初始值,需要先清除localStoreage里面的数据,清除方法有两种:
    • 清除localStoreage某个值,使用localStoreage.removeItem('vuex');
    • localStoreage.clear(); 清除所有的缓存信息
  2. 类似十九点父子通信的情况,有以下应用场景
    从vuex取值,修改后重新存储,并且使用的input标签进行修改

例子(没有异步请求情况), 有异步操作的情况像十九那样,使用watch监听

<template>
  <div>
    <div>vuex里的username {{username}}</div>

    <hr />
    <p>
      子组件的username:
      <input type="text" v-model="username2" @input="fn" />
    </p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      username2: ""
    };
  },

  computed: {
    username() {
      return this.$store.state.username;
    }
  },

  created() {
    console.log("username", this.username);
    this.username2 = this.username;
  },

  methods: {
    fn() {
      this.$store.commit("updateUsername", this.username2);
    }
  }
};
</script>

二十一、$toast轻提示的处理

  1. 设置默认显示时间

    import Vue from 'vue';
    import {Toast} from 'vant'
    Toast.setDefaultOptions({
      duration: 500
    });
    Vue.use(Toast);
    
  2. 错误提示问题,根据不同的情况去做处理

    // 在mixin封装一个$faild 的方法
       // 封裝失敗的提示
    $fail(err) {
        debugger;
        if (typeof err === 'object') {
            if (err.code !=603) {
                this.$toast.fail(err.message);
            }
    
        } else {
            this.$toast.fail(err);
        }
    },
    

    ps: 当我们要对错误进行提示的时候就调用上面的方法this.$fail(xxObj)

二十二、性能优化

  1. 使用cdn https://www.bootcdn.cn/

    // 用script导入库
    <script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.min.js"></script>
    
    // vue.config.js(vue-cli3)的配置
    module.exports = {
      configureWebpack: {
        externals: {
          axios: "axios" // 配置使用CDN
        }
      }
    }
    
  2. 压缩图片 https://tinypng.com/

  3. 默认图片占位

二十三、 vuex使用

1、首先在vue文档中建立一个vuex文件夹:store,然后建立一个子文件index

import Vue from "vue"
import Vuex from "vuex"
import createPersistedState from 'vuex-persistedstate'
import index from "../store/modules/index"

Vue.use(Vuex)

const config = {
    // 本地持久化
    plugins: [createPersistedState()],
    modules:{index},  //将modules导入进来,记得是对象{},别写成数组了。这里是vuex模块化管理
    state:{  //这里是vuex存值的地方
        username:"",
        payWayFlag:false,
        phone:'',
    },

    getters:{
        payWayFlag:state=>state.payWayFlag,
        phone:state=>state.phone,
    },

    mutations:{   //这里是mutations方法,需要用vuex 必须要用这个
        // 支付方式页面的显示与隐藏
        payWayFlagChange(state,payload){
            state.payWayFlag = payload
        },
        updatedPhone(state,payload){ //获取手机号
            state.phone = payload
        },
    },

}

export default new Vuex.Store(config)

2、更改数据/存数据:

  Order() {
      //点击提交传数据
      if (this.show) {
        let url = "/preOrder/add";
        let data = {
          cartId: this.result, //这里的result是个数组,是组件自带的一个装有所有cartid的数组
          totalMoney: this.allPrice //这里是将总价传过去
        };
        this.$axios
          .post(url, data)
          .then(res => {
            console.log(res);
            this.$store.commit("updatedPreOrderId", res.result.preOrderId);  //这里存数据
            this.$store.commit("payWayFlagChange", true); //这里是为了防止支付框开始就弹起来
          })
          .catch(err => {
            console.log(err);
          });
      }
      this.$router.push("/order/confirm");
    },

3、使用你存在vuex中的数据:

 getOrder() {
  let url = "/order/detail";
  let data={
  orderId:this.$store.state.index.orderId  //这里就是vuex你需在哪里使用,就按照这个格式来写就行
      }

二十四 map方法;

   // 获取购物车列表
    getCartList() {
      this.$loading(true);
      let url = "/cart/all";
      this.$axios
        .get(url)
        .then(res => {
          this.cartList = res.list.map(item => {  //map方法
            return {
              ...item,
              checked: false
            };
          });
        })
        .catch(err => {
          this.$toast.fail(err.message);
        })
        .finally(() => {
          this.$loading(false);
        });
    },

二十五 filter方法;

   selectOne(value) {
      // 被选中商品的数量
      let selectList = this.cartList.filter(item => {  //filter方法
        // return item.checked === true;
        return item.checked;
      });

      // 判断被选中商品和全部商品的数量是否相等
      if (selectList.length === this.cartList.length) {
        this.allChecked = true;
      } else {
        this.allChecked = false;
      }
    },

二十六 合并登录和注册页面

1、首先在vue页面中监听方法中监听:

  computed: {
    pageName() {
      //这里是将登录和注册的页面合并,监听路径
      let path = this.$route.path;
      let name = path === "/user/login" ? "login" : "register";
      return name;
    }
  }

2、 然后在html文档使用的时候按如下格式填写:

   <div class="register-hander flex jc-sb aic">
      <span></span>
      <span class="ml-40" v-if="pageName ==='register'">注册</span>
      <router-link v-if="pageName === 'login'" to="/user/register" tag="span" class="mr-15">注册</router-link>    // 登录的页面用v-if判断
      <router-link v-else to="/user/login" tag="span" class="mr-15">登录</router-link>
    </div>         //注册的页面用v-else判断

二十七 forEach方法

  getDeilist() {
      //获取城市列表和地区列表

      this.$loading()  //加载loading
      let categoryUrl = "/category/all"; //获取商品列表,路径是接口里的
      let productareaUrl = "/product/all"; //获取所有的商品,路径是接口里的

      let categoryPormise = this.$axios.get(categoryUrl); //获取城市列表的Promise
      let productPormise = this.$axios.get(productareaUrl); //获取地区列表的Promise
      Promise.all([categoryPormise, productPormise])
        .then(res => {
          console.log(res);
          // 把商品列表和所有商品需要的数据
          let categoryList = res[0].list; // 获取到的商品列表赋值给声明的一个categoryList
          let productList = res[1].list;
          let newCommodityList = categoryList.map(item => {
            //在这里用map方法给侧边栏增加一个 children
            let children = []; //新增一个 children数组
            productList.forEach(i => {
              //用forEach选出相同的id,
              if (item.categoryId === i.categoryId) {
                children.push(i); //将相同的id,push进新建的数组
              }
              this.$loadingclon()  //清除loading
            });
            return {
              ...item,
              children //给侧边栏返回children值
            };
          });
          console.log(newCommodityList);
          this.items = newCommodityList;
        })
        .catch(err => {
          console.log(err);
        });
    }

二十八 合并地址栏的编辑和新建

1、在router里面讲这两个页面的路由设置如下:

 {
        path: "/address",
        component: () => import("@/pages/address/Index"),
        meta: {
            title: '地址',
        },
        children: [
            {
                path: "area",
                meta: {
                    title: '新增地址',
                },
                component: () => import("@/pages/address/children/SelectArea")
            },
            {
                path: "编辑地址",
                component: () => import("@/pages/address/children/SelectArea")
            }
        ]
    },

2、在计算属性中监听方法:

  computed: {
    isArea() {
      //这里是合并新建地址和编辑地址
      return this.$route.path.includes(`area`);
    }
  },

3、页面上实际应用:

  <van-nav-bar :title="isArea?'新建地址':'编辑地址'" left-text left-arrow @click-left="onClickLeft" />

二十九 组件内路由守卫

  beforeRouteEnter(to, from, next) {
    //组件内路由守卫
    // ...
    next(vm => {
      //组件内的this无法指向data,所以用vm ,这个可以上网查资料
      vm.from = from.path;
    });
  },

三十、计算优惠券

优惠券是分为父子组件的

//父组件

    // 获取优惠券对象
    getCoupon(couponObj) {
      // // 根据优惠券对象,计算优惠金额
      this. discountMoney = this.getDiscountMoney(couponObj);
      // // 计算总价
      // this.allFees =
      //   this.totalMoney / 1 + this.expressFee / 1 - this.discountMoney / 1; //这里除以1是将他转换成数字
      // this.$store.commit("updatedAllFee", this.allFees); // 这里是将总价存在vuex里面
    },
    // 根据优惠券对象,计算优惠金额
    getDiscountMoney(couponObj) {
      // 不适用优惠券
      if (!couponObj) {  //后面遇到这个情况,可以不要写这个判断,很麻烦
         let discount = 0;  //这里是将优惠金额存进vuex
        this.$store.commit("updatedCouponObj", discount); //这里是将优惠金额存进vuex
        return 0;
      }
      // 选中折扣券
      if (couponObj.type === "02") {
         let discount = this.totalMoney * (1- couponObj.value/10);   //这里是将优惠金额存进vuex
        this.$store.commit("updatedCouponObj", discount);  //这里是将优惠金额存进vuex
        return (this.totalMoney * (1- couponObj.value/10)) ;
      }
      // 选中普通的优惠券
      let discount=couponObj.value  //这里是将优惠金额存进vuex
       this.$store.commit("updatedCouponObj", discount);  //这里是将优惠金额存进vuex
      return couponObj.value;
    },
        
        
  computed: {
    list() {
      return this.$store.state.index.list; //这里是在vuex存的地址,在这里用
    },
    // 计算总价,这里的总价直接渲染在页面上面
   allFee() {
     let allFees=this.totalMoney / 1 + this.expressFee / 1 - this.discountMoney / 1   //这里除以1是将他转换成数字
     this.$store.commit("updatedAllFee", allFees);   // 这里是将总价存在vuex里面
      return  this.totalMoney / 1 + this.expressFee / 1 - this.discountMoney / 1;
    }, 
     
  }

//子组件
   // 选中优惠券时的事件
    onChange(index) {
      this.showList = false;
      this.chosenCoupon = index;
      this.$emit("getCoupon", this.coupons[index]);
    }
    
      computed: {
    coupons() {
      let list = this.couponList.filter(item => {
        //这里是筛选出总价格和能用优惠券的金额
        return item.conditionValue < this.totalMoney; //如果商品总价格大于优惠券金额,那么优惠券就可以用
      });
      return list;
    },
    disabledCoupons() {
      let list = this.couponList.filter(item => {
        //这里是筛选出总价格和不能用优惠券的金额
        return item.conditionValue >= this.totalMoney; //如果商品总价格大于优惠券金额,那么优惠券就可以用
      });
      return list;
    },
    discount(){
      return  this.$store.state.index.discount
    }

  },

三十一 父子组件通信

1、子传父

//子组件
  // 选中优惠券时的事件
    onChange(index) {
      this.showList = false;
      this.chosenCoupon = index;
      this.$emit("getCoupon", this.coupons[index]); //通过emit传值
    }
    
//父组件
//template里面的标签
<offer @getCoupon="getCoupon"></offer> //建立一个点击事件

   //script
   // 获取优惠券对象
      getCoupon(couponObj) {
        // // 根据优惠券对象,计算优惠金额
        this. discountMoney = this.getDiscountMoney(couponObj);
      }

2、父传子 不懂的地方可以参考卖座网后台管理系统

//父组件
//template里面的标签
 <offer :totalMoney="totalMoney"></offer>
 
//子组件
1:  props: ["totalMoney"],
2: 
  computed: {
    coupons() {
      let list = this.couponList.filter(item => {
        //这里是筛选出总价格和能用优惠券的金额
        return item.conditionValue < this.totalMoney; //如果商品总价格大于优惠券金额,那么优惠券就可以用
      });
      return list;
    },
    disabledCoupons() {
      let list = this.couponList.filter(item => {
        //这里是筛选出总价格和不能用优惠券的金额
        return item.conditionValue >= this.totalMoney; //如果商品总价格大于优惠券金额,那么优惠券就可以用
      });
      return list;
    },
    discount(){
      return  this.$store.state.index.discount
    }

  },

三十二 watch监听

 watch: {
     totalMoney: {
       //子组件传来的值监听
       handler(n, o) {
         //深度监听
         console.log(n, o); //n 和o 是参数,n是新值,o是旧值,  n才是我们要的值
         this.listArr = n;
       },
       deep: true, //深度监听
     immediate: true //深度监听
    }
   }
上一篇下一篇

猜你喜欢

热点阅读