Vue开源项目学习一

Vue项目开发一

  1. 划分目录结构

    — 通过vue create my_ProjectVueCLI3创建项目!

    —目录结构:

    (1)src/assets:资源目录,放置图片、CSS样式文件等资源;可以分别建立两个文件夹images和css文件目录,用于存放图片和css文件!

    (2)src/components:组件目录,放置各种组件,但是如果将所有的组件都放置里面,会十分混乱,因此可以在src下再创建一个views文件夹,将不同的视图组件放置里面,而components中主要放置公共组件;同时可以在components中细分为两类common和content,common中放置的不仅仅为该项目下,还可以直接被其他项目所复用的组件;content只放置与当前项目业务相关的组件,不可被其他项目所复用;

    (3)src/router:路由目录,放置路由相关的文件;

    (4)src/store:vuex状态管理目录;

    (5)src/network:网络请求相关所有的封装目录;

    (6)src/common:公共的js文件目录,可以将所有的公共常量抽取放置于该目录下的const.js文件中;也可以封装一些工具类放置在utils.js中;

  2. CSS文件的引入

    —为了保持CSS样式一致性,我们通常需要引用第三方normalize.css文件进行样式统一,参考网址:https://github.com/necolas/normalize.css

    —除了引用第三方的css文件,我们也可以自己建一个base.css文件,然后通过@import "./normalize.css"引入当前base.css文件下;同时通过@import "./assets/css/base.css"将base.css引入到App.vue的<style>标签下;

    CSS相关知识

    可以在css文件夹下通过伪类选择器:root获取根元素html,并且在该伪类选择器中定义变量:

    :root{--large-size:14px;},然后可以在其他样式设置中通过var(--large-size)的形式进行引用;

  3. vue.config和editorconfig配置文件

    VueCLI3中配置别名:

    —首先自己在项目目录(根目录)下创建一个vue.config.js文件;

    —然后在文件下通过module.exports = {}进行别名配置:

    module.exports = {
    configureWebpack:{
      extension:['.js','.vue','.json'],
      resolve:{
          //配置别名
          alias:{
              '@':'src',
              'assets':'@/asstes',
              'common':'@/common',
              'components':'components',
              'netwok':'@/network'
          }
      }
    }
    }
    

    —这里可以不用配置router和store,因为它们可以通过this.$routerthis.$store进行引用;

    —这里需要注意,当我们通过import引入路径时,通过上述别名配置无问题,但是当在标签中通过HTML的src属性中引用路径,需要在路径前加入~符号;

    采用VueCLI2创建项目的过程,默认会生成一个.editorconfig文件,这个文件会对代码风格进行统一,比如缩进大小等;在VueCLI3中是没有这个文件的,因此我们可以将.editorconfig文件拷贝到自己项目下,进行项目代码风格的统一;

  4. 底部导航条封装

    项目需求:底部导航可以根据用户自定义内容,并且点击导航条内容,可以跳转到对应页面下;

    项目实现

    —因为tabbar这个业务可以供多个项目进行复用,将其放在components/common/tabbar下;

    —因为tabbar涉及到路由映射管理,因此需要安装路由:npm install vur-router --save;安装完毕在router文件夹下创建index.js文件进行路由配置和导出,并在main.js中挂载router

    —项目模块划分完成tabbar和路由映射关系的配置;

    另外可以在public目录下的index.html文件下<link>修改网页的图标;

    —这里通过<%= BASE_URL %>获取当前文件所在的路径,可以动态的获取路径,jsp语法;

    tabbar项目的创建思路:封装一个TabBar作为底部导航条的最外层容器,容器内的内容不是固定的,用户可以自定义设置,所以定义插槽,动态添加内容;并且底部导航条的内容可以细分为多个不同的子部分,对每个子部分进行封装为TabBarItem,同时使用具名插槽动态添加图片和文字;可以将以上两个组件进一步封装为一个组件MainTabBar;

    项目相关问题

    (1)我们在使用插槽的时候,一般会在最外层包装一层容器,因为插槽会被完全替换,则我们绑定在插槽的属性会被替换掉而无法显示,所以如果进行插槽相关的属性设置,通常在外层包装一层容器div

    (2)点击响应事件:该操作下,不仅对应的点击的DOM元素样式需要变换,而且还要跳转到对应的网页,这里涉及到路由路径跳转问题;

    样式变换:图片变换,我们可以再添加一个具名插槽,当被点击激活时,该具名插槽下的图片显示,否则显示原始图片;可以通过v-ifv-else控制图片的显示;文字颜色变换,我们可以对文字颜色变换进行封装,用户可以自定义变换颜色,只需要在使用我们封装的组件时传入颜色参数即可,通过props可以获得父组件的数据(用户自定义数据),并在computed属性中进行动态绑定属性:style实现文字样式切换;

    路由路径跳转:一般在较大项目中,像我们需要通过不同路由切换不同页面,我们可以将这些独立组件放置在./src/pages或者叫做views下(便于分组管理),components文件夹下一般放置公共组件;因为路由跳转并不是固定值,我们在点击时,由父组件 传入跳转路由路径,并且通过props获取路径,在methods设置监听事件,通过this.$route.replace(this.path)跳转相关页面;如果跳转页面还能返回原来页面,就使用push进行路由跳转;

    如何动态的获取激活状态:获取当前处于活跃的路由是否与当前路径一致,this.$router.path.indexOf(this.path) !==-1

  5. 首页视图的顶部导航条封装

    —项目需求:可以自定义导航条内容,主要分为左右中三部分;

    项目实现

    —顶部导航条可以在不同的项目下进行复用,因此我们可以将其放在compnoents/common下;

    —使用具名插槽便于用户插入自定义内容,并且在外层添加一个容器包裹;

    —可以在当前组件设置相关导航样式,这里的样式一般是公共样式,常用样式display:flex设置弹性盒样式以实现子内容的均分排列;

    —只需要在用到顶部导航的页面下导入导航组件即可,并且可以在当前页面下设置私有的导航CSS样式;

  6. 首页视图轮播图封装

    —项目需求:首页视图下,请求服务器数据,并以轮播图的形式展现;

    项目实现

    —在进行轮播图的创建时,必须要将数据请求过来,因此需要用到网络请求模块axios;

    —这里需要注意:为了避免网络请求模块和首页的高度耦合度,我们对网络请求进行封装,增加一个模块 ,这样当前开发的网页只需要面向我们封装的模块实现网络请求,并且一个网页可能有多个网络请求,方便统一管理页面的网络请求!!我们只需要导入封装的网络请求函数即可在首页完成网络请求;

    —网络请求模块一般在组件创建完毕即引入,因此在生命周期函数created()中导入网络数据请求函数;一定要注意,需要个变量完成网络请求数据的保存,因为函数调用(压入函数栈,保存函数调用过程中所有的变量,临时的),当函数调用结束后,弹出函数栈,释放函数中所有的变量,因此我们需要将其保存在一个变量中,以防止函数调用完毕后,数据被释放;另外,网络请求是异步操作模块;

    —轮播图组件的封装:(待添加)将轮播图封装为一个组件,使得Home.vue简洁清晰;

    —轮播图请求的数据是首页请求的,因此我们需要通过props将父组件的数据传入轮播图组件中以便于实现轮播图;

    —根据网络响应的数据通过v-for循环遍历swiperItem的数量;预留了插槽,只需要在插入网络请求的轮播数据即可,a标签下的跳转链继和图片内容都封装在轮播数据中;

  7. 首页推荐栏的封装

    —项目需求:推荐栏分别展示商品和标题内容;

    项目实现

    —这里需要从父组件网路请求的数据中通props过获取数据;

    —直接在组件中封装图片和文字,并通过v-for遍历请求数据项的数目,将请求的图片和文字添加到a链接中;

    —直接在组件下设置样式相关CSS样式;

  8. 首页中本周流行栏的封装

    —项目需求:这里设计简单,只是将一张图片放在流行栏中展示;

    项目实现

    —只需要在组件下将图片插入a标签下,并在Home.ve导入挂载组件即可;

  9. 首页中切换栏内容封装

    —项目需求:点击切换项,首页展示不同切换项下的商品和信息

    项目实现

    —切换栏切换效果实现

    —这个组件可以在其他页面进行复用,我们可以封装在components/content目录下;

    —切换栏样式在不同页面下只是文字内容不同,尽量避免使用插槽,并且文字内容是父组件在调用过程中用户自定义传入的,通过props进行访问和v-for填充文字内容;

    —设计选中文字时的状态,绑定class属性,并设置监听点击事件响应函数itemClick,通过判断当前点击切换项的索引值是否与切换项索引值(v-for下获取的index值)相等,改变选中文字的状态::class = {active:index === currentIndex}

    —设置文字选中状态这里我们为了项目开发方便,可以在公共base.css文件下通过伪类选择器获取根元素,然后设置相关变量,并通过var(变量名)的形式进行样式设置;

    —CSS样式相关:根据项目需求,可以在调用该切换栏的组件下,给该组件设置样式:position:sticky粘性定位,使得当滚动到某个位置(可自定义值)时,切换栏的定位变为fixed;这个属性部分浏览器不支持;因此不建议使用该种方法;

    切换栏的吸顶效果

    【1】可以根据滚动位置将切换栏进行固定,形成吸顶效果;首先需要获取到切换栏的offsetTop大小,这里我们在首页组件下通过$refs获取到的只是组件,并不是DOM元素,无法直接得到offset值,所有组件都有一个$el属性,用户可以获取组件内的元素,因此可以通过$refs.refName.$el.offsetTop获得当前滚动高度;另外要注意,在mounted函数下,获取的高度只是图片未加载的高度,因此offsetTop值并不准确,因此这需要我们等到图片加载完成后,才可以计算offsetTop高度;

    【2】这里设计简单,只考虑轮播图片加载完成,其他图片加载速度较快,影响不大;在轮播图组件下设置图片加载监听事件,并传递到首页组件下(这里不需要事件总线,因为它们两个是父子组件);在首页下进行监听调用,当图片加载完毕后,获取其offsetTop值;另外,这里发送事件会根据轮播图数量的多少进行发送,因此我们可以进行一次判断,设置一个isLoad值默认为false,进入加载图片的判断和发送事件,然后将isLoad改为true,这样只发送一次事件,后续不会在调用发送事件了;

    【3】监听滚动,动态改变切换栏样式,之前我们在进行返回顶部的时候,已经完成监听滚动事件,可以在其下进行切换栏的吸顶效果(position:fixed);设置一个数据值isFixed,默认是fasle,并在滚动监听函数下判断滚动位置是否大于切换栏的滚动高度,然后绑定class属性完成吸顶效果设置;经验证,行不通!下面商品内容会突然上移,并且切换栏虽然设置了fixed,但是也会跟着Better-scroll一起滚动;

    【4】另一种实施方案hack方案,复制一份切换栏组件对象放在顶部导航条下,利用它实现停留效果;设置定位层级,将其显示在最高层,通过v-show默认是不显示,当滚动到切换栏位置下将其显示出来;一般在项目开发中常使用这种方案,另外这里需要注意,新复制的切换组件要和之前组件点击事件后,请求到的数据保持一致,以便于实现切换栏下的数据在对应的点击项中是一致的;

    —切换栏商品内容展示效果

    设计商品数据结构:向服务器端请求全部商品数据,但是我们在使用数据的时候,需要设计数据结构以便于在切换栏中不同切换项下显示;

    商品数据结构:首先请求的数据设置为对象格式,包括多个切换项的数据;每一个切换项的数据格式也是一个对象,保存的是一个切换项下页面和当前页面下加载的商品数组;可以将默认的商品数据结构放在Home组件的data()中;

    网络请求数据:这里我们可以将全部的网络请求包装在methods中,直接在生命周期函数created中调用封装好的请求函数,避免全部的网络请求函数具体实现代码在created()中,不便于管理;设计网络请求函数,动态传入两个参数,当前切换项type和当前切换项下所加载的页数page;另外通过push函数和扩展操作符...完成当前切换项商品数据数组的拼接;

    商品数据的展示:封装一个大组件,大组件下放置多个子组件项,子组件项中进行商品数据展示;首先我们需要在大组件下通过props获取首页请求的商品数据,根据获取的数据大小决定子组件的数量并通过子组件进行展示;我们子组件同时需要通过props拿到父组件遍历的数据项以便于展示商品;可以在首页computed下创建展示函数,将当前切换项的数据进行展示:this.goods[currentType].list

    点击切换效果:需要完成在点击切换项时,商品展示也进行切换;之前我们已经在切换导航中设置了监听点击响应事件,这里需要将切换栏的点击事件通过自定义事件this.$emit('tabClick',index)传到首页父组件中,并在首页中@tabClick进行调用,这里点击事件将当前切换项的索引index传出来,我们可以根据切换索引通过switch建立索引和切换项之间的映射关系,做出相应的商品切换;

  10. 滚动条和Better-Scroll的使用

—项目需求:由于原生滚动条容易产生卡顿,安装使用开源的滚动条模块

项目实现

—在该项目的开发中,由于内容居多需要使用到滚动条,但是原生的滚动条滑动会卡顿,我们可以进行安装别人开发好的滚动条模块:github上的iscroll开源模块(但是是现在不在维护)、因此可以使用better-scroll;

—另外如果在开发项目过程中,想要搜索某个模块的原生代码,可以去GitHub上在Tag中找到最新版本下的dist文件夹,里面存储着是原生代码;

—安装better-scrollnpm install better-scroll --save

better-scroll在使用过程中的官方要求:需要在最外层包裹一个有固定高度的父元素wrapper,并且父元素下只能有一个容器content,这样可以实现局部滚动;

better-scroll封装:另外在开发项目过程中,尽量不要过分依赖better-scroll以避免过于耦合,因此我们可以对better-scroll进行封装,以方便后续开发中如果遇到问题,避免大量页面重构修改;另外我们可以封装成可以自定义滚动条特性的组件,需要用户自定义传入参数probeType(定义是否滚动监听)和pullUpLoad(是否上拉加载更多监听),因此我们可以在封装的滚动条上通过pros进行获取用户自定义的参数值;

元素查询方式:一般在Vue中进行元素查询需要通过ref的方式,ref如果绑定在组件上,通过this.$refs.refName获取的是该组件对象;ref中绑定在一个DOM元素上,通过this.$refs.refName所获取的就是该DOM元素;而通过document.querySelector获取的DOM元素是页面中第一个DOM元素,有时候与我们的目标元素不符;

—滚动区域大小计算:一种方法是可以通过calc()进行滚动条区域大小计算;但是不同浏览器或者移动端下,因为屏幕大小不同,中间滚动区域也会不相同,我们可以使用定位进行滚动区域大小的设置;

上拉加载更多的实现:Scroll内置上拉加载更多监听事件scroll.on('pullingUp',()={}),需要设置pullUpLoad属性,用户可以自定义传入属性值以实现上拉加载更多事件的监听是否;Scroll组件将内置的上拉加载更多监听事件传递出去,在首页组件下进行调用,并通过调用网络请求当前页面下的数据;这里需要注意,当我们完成上拉加载更多时,必须要进行scroll.finishPullUp的调用,才能进行下一次的上拉加载更多;

滚动条区域的Bug:Better-Scroll在决定有多少区域可以滚动时,是根据scrollerHeight属性决定的;scrollerHeight属性是根据Better-Scroll的content中的子组件高度决定的;因为页面中的图片数据是异步加载的,然后在better-scroll计算可滚动长度时,计算值可能为未加载图片的高度;因此在进行上拉过程中出现卡顿设置无法上拉;

解决方法:首先监听图片加载完成后,通过scroll.refresh()进行重新刷新计算滚动高度大小;如何监听图片加载完成:在原生JS中使用img.onload = function(){}图片 加载完成就执行回调函数,Vue中封装了图片加载监听事件,只需要在图片标签中添加@load = '监听方法'

这里涉及到如何在商品子组件拿到Scroll对象?

首先明确两者的关系,GoodsListItem是GoodsList的子组件,GoodsList又是Home的子组件,而Scroll也是Home的子组件,因此GoodsListItem不能直接从Scroll中获取scroll对象及其方法;解决方式有三种,一种是将GoodsListItem中的图片加载完成事件传递给GoodsList,然后GoodsList在将该事件传递给Home,在Home下监听并获取scroll对象以完成刷新;太麻烦!

另一种是通过Vuex对象完成数据状态管理,GoodsListItem中可以直接拿到this.$store改变Vuex中某个属性的数据状态,在Home中引用该Vuex中的属性,并且实时监听Vuex中属性的改变,一旦数据状态发生变化就可以实现scroll.refresh

还有一种是事件总线,管理事件,不同于Vuex状态管理,在GoodsListItem中通过this.$bus.$emit('test')向事件总线发送一个事件,然后在Home中进行监听事件,this.$bus.$on('test',function(){});默认情况下$bus的对象是没有内容的,因此需要在main.js通过向Vue的原型对象添加相关方法和属性,因此将Vue实例赋值$bus,这样就可以实现Vue相关方法:Vue.prototype.$bus = new Vue()

注意!!这里涉及到生命周期函数,组件对象是否加载完成的问题,我们在created生命周期函数中加载使用scroll对象及其相关方法,有时候可能会出错,因为在mounted中组件对象可能还未挂载,所以我们访问的对象是null,因此我们在封装Scroll相关方法时,通常做一个判断:this.scroll && this.scroll.scrollTo $$ this.scroll.scrollTo(x,y,time);另外我们的监听函数需要在mounted生命周期函数中;

—总结:因为涉及到非父子组件通信,所以我们选择了事件总线,通过$bus创建一个事件总线,这里需要将$bus添加到Vue原型对象上Vue.prototype.$bus = new Vue();然后可以通过this.$bus.$emit('事件名称',参数)将事件传递到事件总线;并通过this.$bus.$on('事件名称',回调函数(参数))实现监听事件;

优化—刷新频繁的防抖动函数

—例如在搜索框进行搜索的时候,会监听搜索栏中的信息,当搜索栏的内容发生变化,就会向服务器发送请求,但是频繁请求会对服务器压力很大;因此需要频繁刷新的防抖动操作!可以将防抖动创建一个组件,便于复用

这里是对refresh非常频繁的刷新问题进行防抖debounce/节流throttle操作!

—防抖函数起作用的流程:

【1】如果直接执行refresh函数,那么他会执行多次(图片有多少,执行多少次);

【2】可以将refresh函数传入到debounce函数中,生成一个新的函数,并且设置定时器函数,进行等待,在等待时间内,完成下一次刷新,这里设置取消上一次刷新操作以实现避免多次刷新,起到防抖作用;

函数实现

debounce(func,delay){
 let timer = null
//返回一个新的函数 
 return function(...args){
    //取消上一次执行的timer 
     if(timer) clearTimeout(timer)
     timer = setTimeout(() => {
         //延时一段时间执行刷新函数,并且在延时过程中已经取消之前的刷新操作,所以不会频繁的进行刷新
         func.apply(this,args)
     },delay)
 }
}

浏览器事件循环机制

—生命周期函数:created()mounted(),前者是创建组件后就调用,这时只是完成数据的初始化,可以进行网络请求,无法访问页面的DOM元素;mounted()中是组件已经完成挂载,可以获取DOM元素,因此我们滚动条一般都是在mounted()中进行创建调用的;这里created()函数和mounted()钩子函数的使用区别:https://blog.csdn.net/ygy211715/article/details/80079603

  1. 返回顶部组件的封装

—项目需求:点击按钮,直接返回顶部位置;

项目实现

监听点击事件:该需求下对点击事件进行监听,可以通过两种方式实现:

(1)可以直接在当前组件下监听点击事件,然后传递给父组件,父组件在我们上面封装的Scroll滚动条组件中完成事件响应函数(通过scrollTo()滚动返回顶部);较麻烦;

(2)直接在首页中监听组件,注意:原生元素可以直接进行监听事件,但是组件监听需要添加native修饰符才能进行监听;

返回顶部事件响应:当前在首页下可以直接通过this.$refs.scroll拿到Scroll组件对象,这样我们就可以获取滚动条对象的属性和方法;这里需要用到scrollTo函数实现返回顶部操作,内传三个参数,x,y和返回时间大小;

实现返回顶部的隐藏:在首页中默认不显示,只有滚动到一定位置下才会显示返回顶部,因此需要监听滚动事件,当滚动到特定位置显示返回顶部,这里需要将Scroll子组件的监听滚动事件传递出去以供父组件进行监听调用;并通过v-show判断是否显示返回顶部;

  1. 首页离开缓存状态

    —项目需求:当用户离开首页去其他页面时,保存记录当前首页的状态和位置,便于用户返回首页时,还在原来的位置和状态;

    项目实现

    —默认情况下,当进行页面跳转时候,路由会自动将其状态进行自动销毁,进入destroyed生命周期函数,当返回首页又会自动创建组件;如果不想将其状态销毁,可以在路由外包装一下keep-alive记录离开状态,不会随意进入销毁函数;

    —但是由于应用了Better-Scroll,有时候keep-alive不起效果,我们可以在离开首页时保存一个位置信息,而返回首页时,将位置信息设置为原来保存的位置信息;

    —这里就用到了我们的activateddeactivated生命周期函数,分别在deactivated离开时记录当前首页位置,在activated进入时设置离开前的位置,并需要首先进行一次刷新this.$refs.scroll.refresh()

上一篇:2022GDUT寒假专题学习-3 图论


下一篇:vue学习笔记九:vue系统性学习笔记之内置组件