美文网首页
苏打笔记

苏打笔记

作者: 前端陈陈陈 | 来源:发表于2019-11-04 21:44 被阅读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 //深度监听
        }
       }
    

    相关文章

      网友评论

          本文标题:苏打笔记

          本文链接:https://www.haomeiwen.com/subject/szrjbctx.html