coderwhy老师vue.js b站视频教程笔记——第四部分

从项目开始


07.(掌握)项目开发-项目创建和github托管

1.项目目录列表有config文件夹是脚手架2,没有的话是脚手架3

2.使用github

2.1 通过拷贝的方式提交代码到github--------------------

1)github创建项目

2)本地创建项目vue create supermall

3)git clone https://...git

4)本地项目复制到git的clone的文件夹中(node_modules和.git不需要复制)

5)添加:git add .

6)提交到本地:git commit -m '项目初始化'

7)提交到远程:git push

2.2 不通过拷贝的方式直接提交本地代码到github--------------------

git init
git add .
git commit -m "first commit"
git branch -M main
git remote add origin https://github.com/q124467623/supermall_test.git
git push -u origin main

08.(掌握)项目开发-划分目录结构

common公用的常量或工具

components/common可能下一个项目也可以用到的组件

content本项目用到的公共组件

network: axios

router :路由跳转

store:vuex状态管理

home和category视图划分

coderwhy老师vue.js b站视频教程笔记——第四部分_第1张图片

09.(掌握)项目开发-css文件的引入

1.--color-text相当于设置的全局变量

coderwhy老师vue.js b站视频教程笔记——第四部分_第2张图片

第二处引用:

coderwhy老师vue.js b站视频教程笔记——第四部分_第3张图片

10.(掌握)项目开发-vue.config和editorconfig

1.vue.config可以配置一些别名,在项目最外层目录创建即可:

module.exports = {
    configureWebpack:{
        resolve:{
            alias:{
                'assets':'@/assets',
                'common':'@/common',
                'components':'@/components',
                'network':'@/network',
                'views':'@/views',
            }
        }
    }
}

2.cli3默认把.editorconfig删掉了,但是其实很重要,里边管理格式(如缩进2空格等),需要手动在最外层创建:

root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

11.(掌握)项目开发-tabbar引入和项目模块划分

注意事项:

1.slot在cli4中依然可以使用,使用过程中名字要对应

12.(理解)项目开发-小图标的修改以及路径问题

1.小图标的文件是放在public/favicon.ico

2.小图标的引用是在public/index.html中:

favicon.ico">

public文件夹的内容在打包的时候,会原封不动的到dist文件夹

13.(掌握)首页开发-首页导航栏的封装(★★★)

1.因为导航栏(NavBar.vue)在多个页面会用到,因此考虑封装,采用插槽的方式:

coderwhy老师vue.js b站视频教程笔记——第四部分_第4张图片

2.Home.vue开始使用导航栏:

coderwhy老师vue.js b站视频教程笔记——第四部分_第5张图片

3。导航栏颜色在不同的组件中不一样,可以考虑在Home.vue中写,这里采用变量的方式:

coderwhy老师vue.js b站视频教程笔记——第四部分_第6张图片

注:base.css::root {  --color-tint: #ff8198; }

14.(掌握)项目开发-请求页面的多个数据

1.network文件夹新建request.js用于axios的创建:

import axios from 'axios'

export function request(config) {
    //1.创建axios实例
    const instance = axios.create({
        baseURL: 'http://1xx.xxx.xxx.xxx:7878/api/m5',
        timeout: 5000
    })

    //2.axios拦截器
    instance.interceptors.request.use(config => {
        return config //如果不返回出去,后边就拿不到config了
    }, err => {
        console.log(err);
    });
    instance.interceptors.response.use(res => {
        return res.data
    }, err => {
        console.log(err);
    });

    //3.返回结果
    return instance(config)
}

2.network文件夹新建home.js,封装函数:设置请求的url,导出函数用于创建home.vue组件需要的数据:

import { request } from './request'

export function getHomeMultifata() {
  return request({
        url: 'home/multidata'
    })
}

3.Home.vue组件中使用函数,拿到后台传来的数据:

...
import {getHomeMultifata} from '@/network/home'

export default {
    name:'Home',
    data(){
      return {
        banners:[],
        recommends:[],
      }
    },
    components:{
      NavBar
    },
    created(){
      getHomeMultifata().then(res=>{
        this.banners = res.data.banner.list;
        this.recommends = res.data.recommend.list;
      })
    }
}

15.(掌握)首页开发-轮播图的展示

1.轮播图可以找网上的组件,目前老师提供的轮播图组件:Swiper.vue和SwiperItem.vue,可以通过引入到home/childComps/HomeSwiper.vue中进行组件的使用(首页的内容都可以写到childComps内):

使用模板:

   

       

     

HomeSwiper.vue代码:



2.组件引入到Home.vue中:



16.(掌握)首页开发-推荐信息的展示

1.创建组件home/childComps/HomRecommendView.vue:



2.Home.vue中使用,并发送数据到子组件:



17.(了解)知识回顾2


day 10


01.(掌握)首页开发-FeatureView的封装

FeatureView只有一张图片,不建议在Home.vue中直接写,因此封装FeatureView.vue组件,组件内包裹再导出。

coderwhy老师vue.js b站视频教程笔记——第四部分_第7张图片

02.(掌握)首页开发-TabControl的封装

1.仅仅是文字不一样,没必要设置插槽:

coderwhy老师vue.js b站视频教程笔记——第四部分_第8张图片

2.components/content/tabControl/TabControl.vue:



...


//Home.vue设置停顿
...
.tab-control{
  position: sticky;
  top: 44px;
  background-color: #fff;
}

03.(掌握)首页开发-保存商品的数据结构设计

Home.vue:

coderwhy老师vue.js b站视频教程笔记——第四部分_第9张图片

04.(掌握)首页开发-首页数据的请求和保存

2.数据的请求和保存:

        1)请求network/home.js:

export function getHomeGoods(type, page) {
    return request({
        url: 'home/data',
        params: {
            type,
            page
        }
    })
}

        2)保存Home.vue:

        注:数组push到另一个数组:

totalNums.push(...nums1)  等价于nums1.forEach( item => { totalNums.push( item ) } )

data(){
  return {
    banners:[],
    recommends:[],
    titles:["流行","新款","精选"],
    goods:{
      'pop':{page:0,list:[]},
      'new':{page:0,list:[]},
      'sell':{page:0,list:[]},
    }
  }
},
created(){
  //1.请求多个数据
  this.getHomeMultifata()
  //2.请求商品数据
  this.getHomeGoods('pop')
  this.getHomeGoods('new')
  this.getHomeGoods('sell')
},
methods:{
  getHomeMultifata(){
    getHomeMultifata().then(res=>{
    this.banners = res.data.banner.list;
    this.recommends = res.data.recommend.list;
    })
  },
  getHomeGoods(type){
    const page = this.goods[type].page +1;
    getHomeGoods(type,page).then(res=>{
      this.goods[type].list.push(...res.data.list)
      this.goods[type].page += 1
    })
  }
}

05.(掌握)首页开发-首页商品数据的展示

1. 数据从Home.vue -> GoodsList.vue ->GoodsListItem.vue通过props传递

1)Home.vue::cgoods="goods['pop'].list"/>

2)GoodsList.vue:   

   cgoods" :key="index" :goodsItem="item"/>

3)GoodsListItem.vue:

coderwhy老师vue.js b站视频教程笔记——第四部分_第10张图片

2.补充flex布局(GoodsList.vue中):

coderwhy老师vue.js b站视频教程笔记——第四部分_第11张图片

06.(掌握)首页开发-TabControl点击切换商品

1.TabControl.vue组件中,有商品点击事件,TabControl.vue又是Home.vue组件的子组件,因此是子组件向父组件传递事件:

TabControl.vue:

coderwhy老师vue.js b站视频教程笔记——第四部分_第12张图片

Home.vue:



...

07.(掌握)分类页面-Better-Scroll的安装和使用

1.该插件的好处是在移动端更流畅和有弹簧效果

2.使用过程:

    1)npm install better-scroll --save

    2)导入import BScroll from 'better-scroll'

    3)在mounted()内使用new BScroll('.wrapper')

    4)文档结构要求,.wrapper内只能是单标签,如:

       

  • ...

3.Category.vue代码:





08.(掌握)分类页面-本地测试Better-Scroll的基本使用解析

1. 本地测试better-scroll插件,中途要装插件:npm install @better-scroll/pull-up --save




  


  
  • 列表数据1
  • 列表数据2
  • ...

09.(掌握)分类页面-vue项目中使用Better-Scroll






10.(掌握)首页开发-BScroll的封装及使用

1.如果每个组件都直接导入BScroll使用,如果组件需要换,维护成本太大,因此考虑对BScroll封装一层,封装之后,别的组件使用过程:

        1)import Scroll from '@/components/common/scroll/Scroll'

        2)注册Scroll

        3)使用需要滚动的放这里

2.ref绑定元素:

ref如果是绑定在组件中的,通过this.$refs.refname获取到的是一个组件对象;

ref如果是绑在普通元素中,通过this.$refs.refname获取到的是一个元素对象;

3.scoped的含义是作用域,样式只在本组件中起作用;

11.(掌握)首页开发-BackTop组件的封装和使用

1.组件不能直接监听点击事件,如下错误用法:

@click="backClick">

正确用法:

@click.native="backClick">

2.$refs是从父组件中拿子组件data中的数据 或者 methods的某个方法:

ref="scroll">

通过 this.$refs.scroll.子组件属性或方法

属性

this.$refs.scroll.scroll.scrollTo(0,0,500)

包装一次拿方法

this.$refs.scroll.scrollTo(0,0)

12.(掌握)首页开发-BackTop的显示和隐藏

1.过程:

1)Scroll子组件发出位置事件:

this.scroll.on('scroll',position=>{ this.$emit('scroll',position) })

2)Home父组件拿到事件并把postion保存起来:

3)back-top组件通过v-show判断显隐,isShowBack为计算属性,判断postion数值:

注:props中的类型:default默认值为数组或对象时,使用函数,其他类型不用函数。

13.(掌握)首页开发-完成上拉加载更多

1.过程:

  1)Scroll组件中监听上拉事件,并发出消息给Home:

        this.scroll.on('pullingUp',()=>{  this.$emit('pullingUp') })

  2)Home中接收消息,并请求数据:

        

        loadMore(){

        this.getHomeGoods(this.currentType)

        this.$refs.scroll.finishPullUp() },

2.注意:cprobe-type="3"    @pullingUp="loadMore">属性可以驼峰,消息不可以


day 11


01.(掌握)对昨天内容的回顾

02.(掌握)首页开发-滚动区域的bug分析和解决

1.Better-Scroll在决定有多少区域滚动时,根据scrollerHeight属性决定

        - scrollerHeight属性是根据放Better-Scroll的content中的子组件的高度
        - 但是我们的首页中, 刚开始在计算scrollerHeight属性时, 是没有将图片计算在内的
        - 所以, 计算出来的告诉是错误的(1300+)
        - 后来图片加载进来之后有了新的高度, 但是scrollerHeight属性并没有进行更新.
        - 所以滚动出现了问题

2.如何解决这个问题了?

        - 监听每一张图片是否加载完成, 只要有一张图片加载完成了, 执行一次refresh()
        - 如何监听图片加载完成了?
        - 原生的js监听图片: img.onload = function() {}
        - Vue中监听: @load='imageLoad'
        - 调用scroll的refresh()

coderwhy老师vue.js b站视频教程笔记——第四部分_第13张图片

coderwhy老师vue.js b站视频教程笔记——第四部分_第14张图片

3.如何将GoodsListItem.vue中的事件传入到Home.vue中

  1. 因为涉及到非父子组件的通信, 所以这里我们选择了事件总线

  • bus ->总线

  • Vue.prototype.$bus = new Vue()

  • this.$bus.$emit('事件名称', 参数) 

  • this.$bus.$on('事件名称', 回调函数(参数))

2.在GIS开发中:

        this.$root.$emit('show-area', this.form.coordinate);

        this.$root.$on('show-area', this.showarea);

        this.$root.$off('show-area', this.showarea);

4.(掌握)刷新频繁的防抖函数处理

  • 什么是防抖:比如用户搜索iphone,如果不加防抖,每次输入字母会向服务器发送一次请求,服务器压力很大,因此可以采用防抖,设置延迟,减轻服务器压力。

1、防抖(debounce):触发高频事件后 n 秒内函数只会执行一次,如果 n 秒内高频事件再次被触发,则重新计算时间

举例:就好像在百度搜索时,每次输入之后都有联想词弹出,这个控制联想词的方法就不可能是输入框内容一改变就触发的,他一定是当你结束输入一段时间之后才会触发。

节流(thorttle):高频事件触发,但在 n 秒内只会执行一次,所以节流会稀释函数的执行频率

举例:预定一个函数只有在大于等于执行周期时才执行,周期内调用不执行。就好像你在淘宝抢购某一件限量热卖商品时,你不断点刷新点购买,可是总有一段时间你点上是没有效果,这里就用到了节流,就是怕点的太快导致系统出现bug。

3、区别:防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行。

4.异步打印顺序排在非异步之后:

coderwhy老师vue.js b站视频教程笔记——第四部分_第15张图片

4.防抖具体实现:

如果直接执行: this.$bus.$on('itemImageLoad',()=>{ this.$refs.scroll.refresh() })的话,refresh会被执行30次,服务器压力大,可以把refresh函数传入到debounce中,生成一个新的函数,新函数不会被频繁调用,如果下次执行非常快,会将上次的函数清理掉:

coderwhy老师vue.js b站视频教程笔记——第四部分_第16张图片

5.(掌握)首页开发-上拉加载更多的完成(略)

6.(掌握)首页开发-tabControl的offsetTop获取分析

1.this.tabOffsetTOP = this.$refs.tabControl.$el.offsetTop; 

        $el是拿到组件的元素;

        offsetTop是拿到当前对象到其上级层顶部的距离.

注意:1)拿到offsetTop之前,最好等上边的元素加载完,如在mounted中获取就是错的,因为没有等轮播图加载完成

        2)想要获取正确的值,要等轮播图加载完成

7.(掌握)首页开发-tabControl的吸顶效果完成

1.过河拆桥法实现轮播图加载一张的时候就发出消息,之后停止发消息

coderwhy老师vue.js b站视频教程笔记——第四部分_第17张图片

2.吸顶效果实现原理:scroll滚动的y值 > tabControl距顶部距离时,对第二个tabControl设置显示

1)this.tabOffsetTOP = this.$refs.tabControl2.$el.offsetTop;在Home中拿到tabControl距顶部距离;

2)this.isTabFixed = -position.y>this.tabOffsetTOP 判断距离

3) 设置显隐,采用了复制技巧

coderwhy老师vue.js b站视频教程笔记——第四部分_第18张图片

8.(掌握)首页开发-Home离开时记录状态和位置

1.App.vue设置keep-alive

          

2.如果1中不生效,增加以下内容1)记录离开组件时y 2)激活时,定位到y:

coderwhy老师vue.js b站视频教程笔记——第四部分_第19张图片

9.(掌握)跳转到详情页并且携带iid

1.通过路由技术实现:

1)index.js:{  path: '/detail/:iid',   component: Detail    }

2)GoodsListItem设置点击事件,并设置路由跳转:

        itemClick(){ this.$router.push('/detail/'+this.goodsItem.iid) }

3)详情页Detail获取iid:

        created(){ this.iid = this.$route.params.iid }

        //activated(){    this.iid = this.$route.params.iid    }

10.(掌握)详情页-导航栏的封装

1.只封装导航栏,考虑使用NavBar组件如下所示:

coderwhy老师vue.js b站视频教程笔记——第四部分_第20张图片

2.DetailNavBar:左边是返回,右边空,中间用循环遍历:

coderwhy老师vue.js b站视频教程笔记——第四部分_第21张图片

3.返回事件:this.$router.back()

11.(掌握)详情页-数据请求以及轮播图展示

1.数据请求:

import { request } from './request'

export function getDetail(iid) {
    return request({
        url: '/detail',
        params: {
            iid
        }
    })
}

2.Detail组件中获取去请求的数据并传到DetailSwiper中:

coderwhy老师vue.js b站视频教程笔记——第四部分_第22张图片

3.HomeSwiper接收数据并导入轮播图插件,显示数据



...
import {Swiper , SwiperItem} from '@/components/common/swiper'

12.(掌握)详情页-商品信息的解析和展示

  • 重点:1)先把数据整合(class里创建构造函数)2)父组件导入class新建对象;3)父组件对象数据传给子组件,子组件根据数据渲染
  • 图片展示3个过程

coderwhy老师vue.js b站视频教程笔记——第四部分_第23张图片

coderwhy老师vue.js b站视频教程笔记——第四部分_第24张图片

coderwhy老师vue.js b站视频教程笔记——第四部分_第25张图片

  • 知识点:

1.复杂的数据可以整合成一个对象之后传给子组件

2.

{{index}}

遍历index的时候,值从1开始的

3.判断对象是否为空:const obj = {} ;   Object.keys(obj).length === 0 说明为空对象

4.null和{}区别:{}是一个不完全空的对象,因为他的原型链上还有Object呢,而null就是完全空的对象,啥也没有,原型链也没有,所以null instanceof Object === false;[]就更不用说了,它的原型链上还比{}多一个Array。
所以,纯粹意义上初始化一个空对象应该用null,{}更像是初始化对象,和new一个没有key的Obejct是一样的。

13.(掌握)详情页-店铺信息的解析和显示(DetailShopInfo.vue):



14.(掌握)详情页-加入滚动效果Scroll

核心代码:



15.(掌握)详情页-商品详情数据的展示

1.商品详情数据还是一个组件,DetailGoodsInfo.vue主要代码:

2.注意事项:滚动条卡顿问题,解决:要等图片加载完的时候refresh()一下

1)

2)图片加载完之后发出消息 imgLoaded(){  if(++this.counter ===this.imagesLength)  {  this.$emit('imageLoad')  }  }

3)watch监听属性变化,属性变化(peops数据传过来的时候)的时候调用

coderwhy老师vue.js b站视频教程笔记——第四部分_第26张图片

16.(掌握)详情页-商品参数信息的展示(同上)

17.(了解)内容的回顾3


day 12


12.(掌握)详情页-商品评论信息展示

1.时间戳转年月日格式化:formatDate(date, 'yyyy-MM-dd') 或者formatDate(date, 'yyyy-MM-dd hh:mm')

import {formatDate} from "@/common/utils";
export default {
		name: "DetailCommentInfo",
    props: {
		  commentInfo: {
		    type: Object,
      }
    },
    filters: {
		showDate: function (value) {
          let date = new Date(value*1000);
          return formatDate(date, 'yyyy-MM-dd')
      }
    }
	}
...
utils的内容
export function formatDate(date, fmt) {
  if (/(y+)/.test(fmt)) {
    fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
  }
  let o = {
    'M+': date.getMonth() + 1,
    'd+': date.getDate(),
    'h+': date.getHours(),
    'm+': date.getMinutes(),
    's+': date.getSeconds()
  };
  for (let k in o) {
    if (new RegExp(`(${k})`).test(fmt)) {
      let str = o[k] + '';
      fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? str : padLeftZero(str));
    }
  }
  return fmt;
};

13.(掌握)详情页-商品推荐数据的展示

1.还利用GoodsList和GoodsListItem组件,传数组给GoodsList就行:

        

2.注意,GoodsListItem拿到的单个图片对象goodsItem的数据结构可能不一样,需要用计算属性重新计算路径:(还有img和image注意区别)

 
...

computed:{
    showImage(){
      return this.goodsItem.image || this.goodsItem.show.img
    }
},

14.(掌握)详情页-首页和详情页监听全局事件和mixin的使用

1.this.$route.path.indexOf('/home')>-1 ,indexOf用于判断字符串中包含的某个字符串首次出现的位置,-1代表没找到,大于-1证明找到了

2.mixin混入(Vue高级用法),如两个组件中的mounted中(也可以是data)部分代码完全相同,考虑使用混入技术:

        1)混入和类的继承的区别,混入针对的是对象

        2)官网案例:

var mixin = {
  created: function () { console.log(1) }
}
var vm = new Vue({
  created: function () { console.log(2) },
  mixins: [mixin]
})
// => 1
// => 2

        3)我的案例,Home组件和Detail组件mounted和data的部分值相同,1)考虑新建混入文件Mixin.js;2)组件中import {itemListenerMixin}; 3)mixins:[itemListenerMixin],

//Home.vue
data(){
    return {
      itemImgListener:null
    }
  },
mounted() {
    let newRefresh = debounce(this.$refs.scroll.refresh,100)
    this.itemImgListener = ()=>{newRefresh()}
    this.$bus.$on('itemImageLoad',this.itemImgListener)
    // console.log('我是混入内容');
},

//Detail.vue同上

//Mixin.js:
import {debounce} from './utils'
const itemListenerMixin = {
  data(){
    return {
      itemImgListener:null  //加这个可以让对监听的事件进行保存,使得Home组件和Detail组件各自执行自己的事件
    }
  },
  mounted() {
    let newRefresh = debounce(this.$refs.scroll.refresh,100)
    this.itemImgListener = ()=>{newRefresh()}
    this.$bus.$on('itemImageLoad',this.itemImgListener)
    // console.log('我是混入内容');
  },
}
export {itemListenerMixin}

15.(了解)-客养内容回顾


day 13


1.(掌握)bug处理-首页TabControl不一致问题(学过了)

  • 两个组件指向同一个currentIndex文件

        this.$refs.tabControl1.currentIndex = index;

        this.$refs.tabControl2.currentIndex = index;

2.(掌握)详情页-不能滚动的问题

详情页图片加载两种防抖方法:

1)法1防抖,在DeatilGoodsList设置++this.counter ===this.imagesLength再发出事件

2)法2防抖,Mixin内有防抖函数

    methods:{
    //1.法1防抖,在DeatilGoodsList设置++this.counter ===this.imagesLength再发出事件
     detailImageLoad(){
       this.$refs.scroll.refresh()
     },
    //2.法2防抖,Mixin内有防抖函数
     detailImageLoad(){
       this.newRefresh()
      }
    },

3.(掌握)详情页-点击标题滚动到对应内容

1.标题点击事件传给父组件(Detail):        this.$emit('titleClick',index)

2.父组件接收子组件的点击事件,可以确定点击的是哪一个,this.$refs.scroll.scrollTo可以用于定位到,某个

        titleClick(index) { this.$refs.scroll.scrollTo(0,-this.themeTopYs[index]+44,500) }

3.themeTopYs数组的获取,通过子组件名字拿到子组件距离顶部的距离

        this.themeTopYs.push(this.$refs.Name.$el.offsetTop);

4.在哪里获取组件的offsetTop值呢:

        1)created内不行,因为还没获取到原素

        2)mounted内也不行,数据还没获取到

        3)created内的获取到数据的回调也不行,DOM还没渲染完

        4)$nextTick也不行,因为图片高度还没被计算在内

        5)detailImageLoad(){}内可以,因为该函数是图片加载完之后$emit的事件

4.(掌握)详情页-滚动位置显示对应标题

1)监听滚动事件,发出消息传到Detail中:

@scroll="contentScroll">

2)contentScroll方法(普通判断方法):

        判断position.y和this.themeTopYs = [0 , 553.2 , 1567.3 , 2343.4 ]数组中四个值的比较分别

contentScroll(position){
  // console.log(this.themeTopYs);
  // console.log(-position.y);
  this.themeTopYs.forEach((item,index) =>{
     if((this.currentIndex !== index)&&((index=item&&-position.y=item))){
        this.currentIndex = index;
        console.log(this.currentIndex);
        this.$refs.navbar.currentIndex = this.currentIndex
    }
  })
},

2)contentScroll方法(hack方法):push进去一个最大值:[0 , 553.2 , 1567.3 , 2343.4 ,Number.MAX_VALUE ]

        ”空间换时间”

//方法2:this.themeTopYs.push(Number.MAX_VALUE)
    this.themeTopYs.push(Number.MAX_VALUE);
    let length = this.themeTopYs.length;
    for(let i=0;i=this.themeTopYs[i]&&-position.y

3)currentIndex传给子组件:

        this.$refs.navbar.currentIndex = this.currentIndex

5.(掌握)详情页-对复杂判断条件分析和优化(见上)

6.(掌握)详情页-底部工具栏的封装

1.DetailBottomBar组件:

7.(掌握)详情页-Back-Top的混入封装

1. 因为Home页面已经做过返回顶部功能了,如果不使用混入,Detail需要:引入组件--注册组件--中保存v-show的变量--methods中写backClick方法,相当于重复性工作,因此考虑混入:

把data,methods抽到Mixin.js:

注意:混入的内容中,导入部分、data()、components、methods、生命周期函数可以进行合并,如created(),但是methods内部的方法不能进行混入,相混入只能在源组件this.listenShowBackTop(position) 然后把方法抽到listenShowBackTop()方法在Mixin中完成

import BackTop from '@/components/content/backTop/BackTop'
import { BACK_POSITION } from "@/common/const";

export const backTopMixin = {
    components: {
        BackTop
    },
    data() {
        return {
            isShowBackTop: false
        }
    },
    methods: {
        listenShowBackTop(position) {
            //1.判断BackTop是否显示
            this.isShowBackTop = position.y < BACK_POSITION
        },
        backClick() {
            this.$refs.scroll.scrollTo(0, 0)
        },
    },
}

8.(掌握)详情页-将商品添加到购物车中

  • 子组件发出点击事件,父组件Detail接收:

        @addCart="addCart"/>

        //只需获取购物车中需要展示的信息:图片、标题、描述、价格、数量、iid

      addCart(){
        //只需获取购物车中需要展示的信息:图片、标题、描述、价格、数量
        const product = {}
        product.image = this.topImages[0]
        product.title = this.goods.title
        product.desc = this.goods.desc
        product.realPrice = this.goods.realPrice
        product.iid = this.iid
        console.log(product);
      }
    },

9.10.(掌握)详情页-将商品添加到store中

1.采用vuex进行状态管理(相当于全局变量),先安装:npm install vuex --save

2.Detail组件发出消息:

        选择第二种封装的多

如果是直接在mutations中处理: this.$store.commit('addCart',product)
如果在actions中处理:

this.$store.dispatch('addCart',product)

3.按第二种:

   1)首先安装插件、创建store对象、然后挂载到Vue实例(main中导入一下)

import Vue from 'vue'
import Vuex from 'vuex'
import actions from './actions'
import mutations from './mutations'


//1.安装插件
Vue.use(Vuex)
//2.创建store对象
const state = {
    cartList: []
}
const store = new Vuex.Store({
    state,
    mutations,
    actions,
})
//3.挂载Vue实例上
export default store

2)actions.js中接收this.$store.dispatch('addCart',product)传来的方法和参数:

import { ADD_COUNTER, ADD_TO_CART } from './mutation-types'

export default {
    // context={state,commit}
    addCart(context, payload) {
        //数组常用方法:push pop unshift shift sort reverse slice splice forEach map some filter every reduce flat join find
        let product = context.state.cartList.find(item => item.iid === payload.iid)
        if (product) {
            context.commit(ADD_COUNTER, product)
        } else {
            payload.count = 1
            context.commit(ADD_TO_CART, payload)
        }
    }
}

3)mutations.js接收actions.js发出的消息:

注:export const ADD_COUNTER = 'add_counter'

import { ADD_COUNTER, ADD_TO_CART } from './mutation-types'

export default {
    //方法1:简单写法,state是累积的数据集,payload是每次传过来的数据
    // addCart(state, payload) {
    //     let product = state.cartList.find(item => item.iid === payload.iid)
    //     if (product) {
    //         product.count += 1
    //     } else {
    //         payload.count = 1
    //         state.cartList.push(payload)
    //     }
    //     console.log(product.count);
    // },

    //mutations唯一的目的就是修改state中的状态
    //mutations中的每个方法尽可能完成的事情比较单一,下边的判断是两个事情
    //方法2:重构
    [ADD_COUNTER](state, payload) {
        payload.count++;
        console.log("payload.count++ :", payload.count);
    },
    [ADD_TO_CART](state, payload) {
        state.cartList.push(payload)
    }
}

11.(掌握)购物车-导航栏实现,vuex知识点

1.可以采用nav-bar组件的插槽:


        购物车({{cartLength}})

2.购物车中商品种类的数量:

方法一:

购物车({{$store.state.cartList.length}})

方法二getters的用法,计算属性:

注: ...mapGetters(['cartLength','cartList'])的第二种用法 ...mapGetters([length:'cartLength',list:'cartList']),然后{{ }}内部就可以用length和list去代替了

//index.js
const store = new Vuex.Store({
    state,
    mutations,
    actions,
    getters,
})

//getters.js
export default {
    cartLength(state) {
        return state.cartList.length
    },
    cartList(state) {
        return state.cartList
    }
}

//cart.vue
购物车({{cartLength}})
import {mapGetters} from 'vuex' computed:{ ...mapGetters(['cartLength','cartList']) }

11-12.(掌握)购物车-购物车商品列表滚动和显示

  • Cart - CartList -CartListItem - CheckButton组件顺序

1.滚动:Cart组件中对cart-list包scroll:

  
     
  

2.显示:CartList中通过mapGetters获得getters.js的数据(如下),在通过cart-list-item对数据进行解析(itemInfo.image、itemInfo.title、itemInfo.realPrice等)和显示


13-14.(掌握)购物车-item的选中与否

  1. 需要在push商品的时候就对商品添加checked属性,在action.js:payload.checked = true
  2. 因此,CartListItem组件中获取的itemInfo中含checked属性:
  3. CartListItem中把checked属性传给CheckButton子组件::is-checked="itemInfo.checked"/>
  4. 在CheckButton设置calss样式,并在CartListItem组件中写checkedChange方法:this.itemInfo.checked = !this.itemInfo.checked
  5. 附CheckButton代码:

15.(掌握)购物车-底部工具的封装和应用

1.关于position

        1)父级相对定位+100vh之后,子级是相对父级的最上角位置进行定位的

        2)top和left同时设置可以上下拉伸,left和right同理

        附:购物车组件代码



2.父级display: flex;左右固定宽度,中间flex:1把两边顶走

3.合计和去计算:

合计:¥ {{totalPrice}}
去计算({{checkLength}})

16.(了解)内容回顾


day 14


1-2.(掌握)购物车 - 全选按钮的状态显示

第一部分:通过判断列表中是否全部选择 -> 确定全选按钮是否显示 isSelectAll()

  some()是对数组中每一项运行给定函数,如果该函数对任一项返回true,则返回true。

  //some方法效率更高,找到一个没有选中的,就判定全选按钮为false:

    this.isAllChecked =  !this.cartList.some(item => {return !item.checked})       
    this.isAllChecked = this.checkLength === this.cartList.length? true:false

第二部分:点击全选按钮,控制商品里列表:allClick()


...

3.(掌握)vuex-actions返回Promise和mapActions的映射

  • 补充知识点:Actions.js

1)补充知识点:Actions.js返回一个Promise,增加return new Promise(resolve,reject){}

coderwhy老师vue.js b站视频教程笔记——第四部分_第27张图片

2)Detail.js接受回调结果:

  • 补充知识点:mapActions的映射关系,映射之后,可以直接this.方法()
import { mapActions } from 'vuex'

methods:{
    ...mapActions(['addCart']),
    //非映射法:
    this.$store.dispatch('addCart',product)
      .then(res=>{
        console.log(res);
    })
    //映射法
    this.addCart(product).then(res => {
      console.log(res);
    })
}

两种映射对比:

mapGetters

mapActions 

import {mapGetters} from 'vuex'

import { mapActions } from 'vuex'

computed:{

      ...mapGetters(['cartLength'])

},

methods{

 this.addCart(product).then(res => { console.log(res) })

}

4.(掌握)Toast封装-普通方式的封装

//Toast.vue


外部调用:

//Detail.vue
this.addCart(product).then(res => {
  this.message = res;
  this.isShow = true;
  setTimeout(() => {
    this.isShow = false;
    this.message = '';
  }, 1000);
})

5.(掌握)Toast封装-插件方式的封装

1)main.js中安装toast插件

import toast from '@/components/common/toast'
//安装toast插件
Vue.use(toast)

2)toast插件内容:toast/index.js:

//插件和混入的区别就是混入还要导入,插件直接在vue对象上面直接使用就可以了
import Toast from './Toast'

const obj = {}

obj.install = function(Vue) {
    //1.创建组件构造器
    const toastContrustor = Vue.extend(Toast)
        //2.new的方式,根据组件构造器,可以创建出来一个组件对象
    const toast = new toastContrustor()
        //3.将组件对象,手动挂载到某一个元素上
    toast.$mount(document.createElement('div'))
        //4.toast.$el对应的就是div
    document.body.appendChild(toast.$el)

    Vue.prototype.$toast = toast
}

export default obj

3)Toast具体内容如下,被注册到index.js中:



4)任意一个地方使用,如Detail.vue:

this.addCart(product).then(res => {
  this.$toast.show(res,2000)
})

6.(掌握)fastclick-解决移动端300ms延迟

三步走(2和3在main.js中):

1)安装npm install --save fastclick

2)导入import Fastclick from 'fastclick'

3)使用Fastclick.attach(document.body)

7.(掌握)图片懒加载-vue-lazyload框架

懒加载:图片需要显示在屏幕上时,再加载

1) 安装 npm install vue-lazyload --save

2) 导入 import VueLazyload  from 'vue-lazyload'

3)使用 Vue.use(VueLazyload),也可以下面设置占位图

        Vue.use(VueLazyload, {

            loading: require('@/assets/img/common/placeholder.png')

        })

4)修改替换成

8.(掌握)px2vw-css单位转换插件

  •  像素转vw,更好的适配各种机型,使用过程:

1)安装,因为是开发时依赖(打包时用到):npm i postcss-px-to-viewport --save-dev

2)最外层写配置文件:postcss.config.js:

module.exports = {
    plugins: {
        autoprefixer: {},
        "postcss-px-to-viewport": {
            viewportWidth: 375, // UI设计稿的宽度
            viewportHeight: 667, // UI设计稿的宽度
            unitPrecision: 5, // 转换后的精度,即小数点位数
            viewportUnit: "vw", // 指定需要转换成的视窗单位,默认vw
            selectorBlackList: ['ignore', 'tab-bar', 'tab-bar-item'], //不需要转换的类
            minPixelValue: 1, // 默认值1,小于或等于1px则不进行转换
            mediaQuery: false, // 是否在媒体查询的css代码中也进行转换,默认false
            unitToConvert: "px", // 要转化的单位
            // propList: ["*"], // 指定转换的css属性的单位,*代表全部css属性的单位都进行转换
            fontViewportUnit: "vw", // 指定字体需要转换成的视窗单位,默认vw
            selectorBlackList: ["wrap"], // 指定不转换为视窗单位的类名,
            replace: true, // 是否转换后直接更换属性值
            exclude: [/node_modules/], // 设置忽略文件,用正则做目录名匹配
            landscape: false, // 是否处理横屏情况

        }
    }
}

9.(理解)nginx-项目在windows下的部署

1.服务器:一台电脑(没有显示器,只有一个主机),24小时开着的,为用户提供服务。

        主机 -> 操作系统 -> Windows/Linux -> Tomcat/nginx(服务器上提供服务的软件)

2.自己计算机nginx安装过程(win):下载nginx稳定版 ->双击exe ->把dist内的所有文件放到nginx-1.20.1/html文件夹内 -> 浏览器输入localhost回车。(远程服务器同理)

3.也可以修改默认启动文件:

        1)任务管理器关闭nginx

        2)打开nginx-1.20.1\conf\nginx.conf 修改html为dist

        3)重新打开exe,运行localhost

10.(理解)nginx-项目在远程Linux下的部署

1. 学习Linux一般用Ubuntu版本、真正使用选择Centos版本

2.yum是Linux安装包管理工具

3.安装过程(以centos为例):

        1)yum install nginx

        2)systemctl start nginx service  开启nginx服务

        3)systemctl enable nginx.service  跟随系统启动

        注:windows上通过xshell可以控制linux命令(如上面3个),传文件用xftp(如修改nginx.conf)

11-12.(理解)响应式原理

(面试题)

  • 如何理解Vue生命周期
    {{name}} {{name}} {{name}} {{age}} {{age}}
  • 如何进行非父子组件通讯
  • Vue响应式原理(重要):
    • 一旦值发生了改变,然后被监听到,找到对应的值(name还是message)对应的发布者Dep(每个key对应一个Dep);
    • 一个发布者Dep内部可能有多个订阅者如上边三个name是3个订阅者,2个age是2个订阅者,Subs(this.subs.push(watcher)),遍历所有的订阅者,调用订阅者的update:notify() {  this.subs.forEach(item => { item.update() })}
    • update()根据界面上目前显示的东西,对视图View进行一个更新。

coderwhy老师vue.js b站视频教程笔记——第四部分_第28张图片

1.问题:app.message修改数据,Vue内部是如何监听message数据的改变?

        答:Object.defineProperty -> 监听对象属性的改变,每个属性都有一个Dep对象,name -> Dep对象 -> subs ->[watcher1,watcher 2]

2.问题:当数据发生改变,Vue是如何知道要通知哪些message,界面要发生刷新?

        答:发布 订阅者模式

{{message}} {{message}} {{message}}

你可能感兴趣的:(笔记,vue.js,javascript)