Vue项目开发一
-
划分目录结构
— 通过
vue create my_Project
VueCLI3创建项目!—目录结构:
(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中;
-
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)
的形式进行引用; -
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.$router
和this.$store
进行引用;—这里需要注意,当我们通过
import
引入路径时,通过上述别名配置无问题,但是当在标签中通过HTML的src
属性中引用路径,需要在路径前加入~
符号;采用VueCLI2创建项目的过程,默认会生成一个.editorconfig文件,这个文件会对代码风格进行统一,比如缩进大小等;在VueCLI3中是没有这个文件的,因此我们可以将.editorconfig文件拷贝到自己项目下,进行项目代码风格的统一;
-
底部导航条封装
—项目需求:底部导航可以根据用户自定义内容,并且点击导航条内容,可以跳转到对应页面下;
项目实现:
—因为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-if
和v-else
控制图片的显示;文字颜色变换,我们可以对文字颜色变换进行封装,用户可以自定义变换颜色,只需要在使用我们封装的组件时传入颜色参数即可,通过props
可以获得父组件的数据(用户自定义数据),并在computed
属性中进行动态绑定属性:style
实现文字样式切换;—路由路径跳转:一般在较大项目中,像我们需要通过不同路由切换不同页面,我们可以将这些独立组件放置在./src/pages或者叫做views下(便于分组管理),components文件夹下一般放置公共组件;因为路由跳转并不是固定值,我们在点击时,由父组件 传入跳转路由路径,并且通过
props
获取路径,在methods
设置监听事件,通过this.$route.replace(this.path)
跳转相关页面;如果跳转页面还能返回原来页面,就使用push
进行路由跳转;—如何动态的获取激活状态:获取当前处于活跃的路由是否与当前路径一致,
this.$router.path.indexOf(this.path) !==-1
-
首页视图的顶部导航条封装
—项目需求:可以自定义导航条内容,主要分为左右中三部分;
项目实现:
—顶部导航条可以在不同的项目下进行复用,因此我们可以将其放在compnoents/common下;
—使用具名插槽便于用户插入自定义内容,并且在外层添加一个容器包裹;
—可以在当前组件设置相关导航样式,这里的样式一般是公共样式,常用样式
display:flex
设置弹性盒样式以实现子内容的均分排列;—只需要在用到顶部导航的页面下导入导航组件即可,并且可以在当前页面下设置私有的导航CSS样式;
-
首页视图轮播图封装
—项目需求:首页视图下,请求服务器数据,并以轮播图的形式展现;
项目实现
—在进行轮播图的创建时,必须要将数据请求过来,因此需要用到网络请求模块axios;
—这里需要注意:为了避免网络请求模块和首页的高度耦合度,我们对网络请求进行封装,增加一个模块 ,这样当前开发的网页只需要面向我们封装的模块实现网络请求,并且一个网页可能有多个网络请求,方便统一管理页面的网络请求!!我们只需要导入封装的网络请求函数即可在首页完成网络请求;
—网络请求模块一般在组件创建完毕即引入,因此在生命周期函数
created()
中导入网络数据请求函数;一定要注意,需要个变量完成网络请求数据的保存,因为函数调用(压入函数栈,保存函数调用过程中所有的变量,临时的),当函数调用结束后,弹出函数栈,释放函数中所有的变量,因此我们需要将其保存在一个变量中,以防止函数调用完毕后,数据被释放;另外,网络请求是异步操作模块;—轮播图组件的封装:(待添加)将轮播图封装为一个组件,使得Home.vue简洁清晰;
—轮播图请求的数据是首页请求的,因此我们需要通过
props
将父组件的数据传入轮播图组件中以便于实现轮播图;—根据网络响应的数据通过
v-for
循环遍历swiperItem的数量;预留了插槽,只需要在插入网络请求的轮播数据即可,a
标签下的跳转链继和图片内容都封装在轮播数据中; -
首页推荐栏的封装
—项目需求:推荐栏分别展示商品和标题内容;
项目实现
—这里需要从父组件网路请求的数据中通
props
过获取数据;—直接在组件中封装图片和文字,并通过
v-for
遍历请求数据项的数目,将请求的图片和文字添加到a
链接中;—直接在组件下设置样式相关CSS样式;
-
首页中本周流行栏的封装
—项目需求:这里设计简单,只是将一张图片放在流行栏中展示;
项目实现
—只需要在组件下将图片插入
a
标签下,并在Home.ve导入挂载组件即可; -
首页中切换栏内容封装
—项目需求:点击切换项,首页展示不同切换项下的商品和信息
项目实现
—切换栏切换效果实现
—这个组件可以在其他页面进行复用,我们可以封装在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
建立索引和切换项之间的映射关系,做出相应的商品切换; -
滚动条和Better-Scroll的使用
—项目需求:由于原生滚动条容易产生卡顿,安装使用开源的滚动条模块
项目实现
—在该项目的开发中,由于内容居多需要使用到滚动条,但是原生的滚动条滑动会卡顿,我们可以进行安装别人开发好的滚动条模块:github上的iscroll开源模块(但是是现在不在维护)、因此可以使用better-scroll;
—另外如果在开发项目过程中,想要搜索某个模块的原生代码,可以去GitHub上在Tag中找到最新版本下的dist文件夹,里面存储着是原生代码;
—安装
better-scroll
:npm 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)可以直接在当前组件下监听点击事件,然后传递给父组件,父组件在我们上面封装的Scroll滚动条组件中完成事件响应函数(通过
scrollTo()
滚动返回顶部);较麻烦;(2)直接在首页中监听组件,注意:原生元素可以直接进行监听事件,但是组件监听需要添加
native
修饰符才能进行监听;—返回顶部事件响应:当前在首页下可以直接通过
this.$refs.scroll
拿到Scroll组件对象,这样我们就可以获取滚动条对象的属性和方法;这里需要用到scrollTo
函数实现返回顶部操作,内传三个参数,x,y和返回时间大小;—实现返回顶部的隐藏:在首页中默认不显示,只有滚动到一定位置下才会显示返回顶部,因此需要监听滚动事件,当滚动到特定位置显示返回顶部,这里需要将Scroll子组件的监听滚动事件传递出去以供父组件进行监听调用;并通过
v-show
判断是否显示返回顶部;
-
首页离开缓存状态
—项目需求:当用户离开首页去其他页面时,保存记录当前首页的状态和位置,便于用户返回首页时,还在原来的位置和状态;
项目实现
—默认情况下,当进行页面跳转时候,路由会自动将其状态进行自动销毁,进入
destroyed
生命周期函数,当返回首页又会自动创建组件;如果不想将其状态销毁,可以在路由外包装一下keep-alive
记录离开状态,不会随意进入销毁函数;—但是由于应用了Better-Scroll,有时候
keep-alive
不起效果,我们可以在离开首页时保存一个位置信息,而返回首页时,将位置信息设置为原来保存的位置信息;—这里就用到了我们的
activated
和deactivated
生命周期函数,分别在deactivated
离开时记录当前首页位置,在activated
进入时设置离开前的位置,并需要首先进行一次刷新this.$refs.scroll.refresh()
;