#在vue lic3.0后创建的项目中,它会自动去帮我们安装git,进行git的初始化
1、阅读器解析和渲染
1.1配置ebook入口文件
我们把Home、About这些删掉,views下建个名为ebook的文件夹,该文件夹下建个index.js作为阅读器的入口文件,再去配置router.js:入口文件嘛,所以path2为/使就让它重定向到/ebook,路径为/ebook是就异步加载index.vue组件的页面,通过()=>import('路径')这就是动态引入,就是懒加载,就是一种按需加载,就你要看这个页面我再给你加载这样子,就提高性能嘛
1.2实现动态路由
我们想要实现如localhost:8081/#/ebook/History|2017_Book_InterdisciplinaryPerspective这种ebook后面跟了一串字符串,字符串前半部分是分类名称History然后一个竖线再后面是一个电子书名称这种格式,也就是动态路由的方式去获取一本电子书,如果我们将History或将后面的电子书名称改变那么获取的电子书也会更换,我们可以通过这种方式来寻找我们nginx服务器上的电子书。
要实现动态路由,第一步需要有动态路由的组件,我们在components下建一个ebook文件夹,ebook文件下建一个EbookReater.vue,用来接收我们的动态路由。然后我们要到router.js中加上这个组件,以children接在path为/ebook后面,如下:
//注意children它是一个数组,数组里面的内容就是动态路由,动态路由是以冒号作为起点,冒号后面是接收的参数名称即fileName,fileName就会传入History|2017_Book_InterdisciplinaryPerspective这样的内容进来
然后还需要在其父组件也就是ebook的index.vue当中引入这个子组件
引入:import EbookReader from '../../components/ebook/EbookReader';注册components:{EbookReader} ;使用<ebook-reader></ebook-reader>
然后我们到EbookReader当中,建一个class用来接收这个参数
如下图所示,我们url中ebook后面的fileName就接收到啦,说明动态路由生效了,也就是当前页面会根据我动态路由的内容而展示不同的内容
1.2.3接下来去拼接出一个路由,这个路由可以访问到nginx服务器,并且根据你拼接的这个路由里面的电子书从而把电子书下载出来。
就你访问这个路由你就可以下载到nginx里面相对应的电子书,我们需要下载这个电子书然后放到我们的阅读器中。
你怎么知道你要搞出一个什么样的路由才能下载到nginx服务器中的电子书呢?
如下nginx服务器我们是直接点击里面的epub里面的某一本书,一点击它就下载到我们的电脑中啦,所以我们仿造它这个路径去访问localhost:8081/epub/History/2017_Book_InterdisciplinaryPerspectivesO.epub,可以发现和访问nginx服务器到epub中点击某本书从而下载出来是一样的。
接下来我们就可以根据动态路由来展示电子书,将动态路由挂载到id为read的DOM下,然后在这个EbookReader的div中就放这个id为read的DOM,也就是说电子书放在这里,跟上面同理,你放不同的电子书那当前页面就展示不同的电子书
即可实现如下访问这本电子书时fileName就保存下了
下面我们就来解析这个路由,我们需要将它拼接成nginx目录,这里我们用正则表达式,如下我们用split('|')把它们分开,然后再用join('/')来将它们拼接在一起
我们要做的是去访问nginx服务器里面的内容,就需要一个访问nginx服务器里面文件的路径,上面是电子书的路径已经拼接好了,再往前是nginx服务器的路径,如下,这两个拼接到一起就是我们要的访问nginx服务器中这本书的路径。最终我们就会得到控制台输出的那种路径
如上还不行,因为nginx的epub文件夹下的电子书文件后面都是.epub结尾的,所以应该加上.epub
最终你访问如下路径时就会打印出下面第一个图打印出的路径http://192.168.112.42:8080/#/ebook/History|2017_Book_InterdisciplinaryPerspectivesO
你一点击控制台的那个路径,就会跳到nginx那边把这边电子书下载到你电脑上
1.2.4接下来引入epubjs
1.2.4.1使用vuex
由于这个fileName经常用,所以我们把它放到vuex中。大致如下
首先把fileName放到state中,mutations中定义一个方法去修改state中的fileName值,actions去定义一个方法去调用mutations中方法。
然后在getters.js中设置fileName,哪里用fileName哪里就引入引入mapGetters、...mapGetters,然后如下,如果我们没有getters,那么我们获取state中的fileName是通过this.state.book.fileName才能获取,但是因为有了getters,getters中我们已经把state.book.fileName变成了fileName,所以只需要访问this.fileName即可获取到state中的fileName
最后哪里要修改fileName的值,就在哪里通过this.$store.dispatch去调用actions中的setFileName方法去调用mutations中的方法去修改state中的fileName值
最终initEpub中就用vuex中的fileName获取到了该电子书的下载路径,如下
渲染电子书的时候,电子书渲染不出来报错如下问题
这个报错我暂时搞不出来,怪我太菜,为了先往后做,我就先用一个固定的电子书路径做,后续再来解决这个问题了。
2、阅读器翻页手势操作功能实现
之前是通过蒙版分left、center、right三个区域,点击left即翻上一页,点击right即翻下一页。
这次就实现用移动端的手势来实现翻页,即从左往右滑动就会返回上一页,从右往左滑动就会返回下一页,滑动的位置是不限制的,可以在任意位置进行滑动翻页,点击屏幕就会出现标题和菜单,同样点击的位置也不受限制,我们翻页的时候如果标题菜单在就会先隐藏标题菜单再翻页。
电子书epub对象实现原理是使用iframe,那么我们如何向iframe里面绑定事件呢?epubjs为我们提供了一个on方法,即通过this.rendition.on('',)将事件绑定到iframe上,如下
我们主要是通过event下面的changedTouches下面的clientX是指当前点击的位置,主要通过它来判断
同时我们对手势操作的时间也有要求,如果操作时间过长我们就不确认这次操作,如下可以通过event里面的这个timeStamp规定,我们自己规定它不能超过500毫秒
下面代码实现
然后在计算好偏移量和时间后实现我们的手势,如下
这个禁止事件默认方法调用event.preventDefault(); 和 阻止事件冒泡event.stopPropagation(); 在事件处理程序中调用它们是非常常见的需求。阻止默认事件:某些标签带有默认的事件,例如a标签默认跳转页面,submit属性默认提交并刷新页面,如果不需要触发这些默认的事件,就需要阻止默认事件了。阻止事件冒泡:如果不需要让事件向上传播就需要阻止事件冒泡了
3、标题栏和菜单栏实现
3.1标题栏
components下的ebook文件夹下创建标题栏组件和菜单栏组件,即EbookTitle.vue和EbookMenu.vue,并且在ebook的入口文件即views下的ebook的index.vue中引入这两个组件
之前的时候做过这两个,所以直接拷贝源码过来即可
然后上面的@include center是在global.css中做的,现在我们如下
如下,即可完成标题栏,然后调整一下图标大小和标题栏显示问题即可
标题栏显示问题
我们之前是阅读器与标题菜单栏是父子关系,但是我们现在是平行的即兄弟的关系,我们是希望点击阅读器那么标题菜单栏就进行显示或隐藏,那么这之间的值该怎么传递呢,已经不再是之前的父子组件之间的传递方式了。
我们需要借助vuex来传值,这个值我们定义为menuVisible。
首先我们先到modules下的book.js中定义menuVisible,然后到getter中定义
然后在标题组件中把getter直接引入进来,然后就可以直接用menuVisible了
此时点击发现标题栏没出现,因为你menuVisible默认是false呀,是true的时候才显现,也就是说你点击屏幕事件中应该去改变state中这个menuVisible的值。所以到阅读器组件中点击事件加入如下,并且注意...mapGetters([])中记得要加入menuVisible才行
点击标题栏出现,再次点击标题栏隐藏实现了,但是过渡动画没有实现,接下来实现,我们把之前的过渡动画都拷贝过来,然后放在一个新建的transition.scss中,然后在global.scss中引入这个transition.scss中即可,然后过渡动画就可以实现了
3.2菜单栏
同理拷贝代码到菜单栏中,然后到ebook下的入口文件index.vue中引入这个菜单栏,之前我们已经引入了
然后标题菜单显示的时候翻页,我们想要翻页前标题菜单自动隐藏起来再翻页。我们之前的toggleTitleAndMenu方法是显示时就隐藏、隐藏时就显示。我们翻页的时候去调动这个方法的话,即如下,就会翻页过去标题菜单栏会隐藏起来了,再翻页的时候它到下一页标题栏就又出来了,因为false、true状态一直在切换嘛
而我们想要的是翻页的时候它一直为false隐藏起来,所以应该重新定义一个函数hideTitleAndMenu,如下
3.3mixin机制
这里我们发现有很多重复性的代码,如引入mapGetters,...mapGetters()这些等,我们想把这些重复的代码抽象到一个固定的地方去维护,可以通过vue的mixin机制解决这个问题
具体怎么做:首先在src下面创建一个目录叫utils,其下建一个文件叫mixin.js,在mixin当中我们指定ebookMixin这样的一个对象,这个对象当中我们可以将重复的代码放到这里来,我们把mapGetters引入进来,然后把...mapGetters引入进来,如下然后让所有的组件都引用这个mixin,以后我们修改只需要在这一处进行修改即可,从而实现我们代码的复用。所以其他地方我们可以把这些重复的代码去掉,然后如下引入mixin,通过vue中mixin这样的标签把我们混入的内容放进来,那么vue在实例化过程中就会将ebookMixin里面的内容与我们当前EbookReader当中的内容进行混合,从而实现mixin的复用
后面我们的EbookTitle等组件都可以这样做。当然复用内容即ebookMixin中的内容不止可以是计算属性computed也可以是方法也可以是一些变量等都是可以用来复用的。通过vuex和mixin我们就可以实现组件间的解耦和复用。
接下来再讲一下,我们之前使用vuex中actions中的方法时都是使用this.$store.dispatch('setMenuVisible',false)方法,还有一种更优雅的方法直接通过this.setMenuVisible(false)来实现调用actions中的方法,和getters相类似,vuex就为我们提供了这种机制mapActions。怎么实现:先引入mapActions,然后mapGetters与mapActions的区别是...mapGetters是混入到计算属性computed当中,而...mapActions是混入到methods=当中,如下
所以引入mixin的地方使用actions中的方法时就不再需要通过this.$store.dispatch('actions中的方法',传的值)了而是直接this.actions中的方法(传的值),如下
4、菜单栏字号设置面板
把之前的html和css样式拷贝过来
首先设置面板的显示与否是通过菜单栏的来决定是否显示,点击了菜单栏中的图标就显示,也就是这里我们需要通过vuex来进行值的传递(vuex在store下modeules下的book.js中),在book.js、getters.js、mixin.js下如下
然后字号显示面板中引入mixin
设置面板要显示要2个条件,第一个菜单栏要显示第二控制面板显示,所以控制菜单栏的menuVisible要为true同时控制设置面板的settingVisible要显示
然后我们去设置菜单栏中设置面板中展示字号、主题、进度条、目录其中哪一个的方法。
咋实现呢,我们之前在vuex中设置了面板如果为-1就不显示、为0则显示字号、为1则显示主题、为2则显示进度条、为3则显示目录,然后如果settingVisible为0-3就显示嘛,那默认为-1即默认面板不显示,那你改变settingVisible的值即可实现显示哪种面板了嘛,所以方法就是去改变vuex中的值即可,如下
我们再来搞这个fontsizeList以及setFontsize以及defaultSize(这个在vuex,mixin中引入即可)
我们可以直接放在这个组件data下面,我们也可以考虑在utils下创建个book.js,将所有静态的变量都放到这里进行统一管理如下图2这样更便于管理
哪里需要用就哪里直接引入即可,如下
此时若设置栏显示状态,然后我们点击屏幕,菜单栏标题栏设置栏一起回去了,但是再点击它们出来的时候设置栏是显示状态就有点奇怪,我们希望点击显示菜单栏时设置栏是隐藏状态,点击菜单栏图标它再显示,所以我们再hideTitleMenu中设置this.setSettingVisible为-1即隐藏状态
此时点击字号切换是无效的,接下来我们来实现字号切换功能。
通过this.book.rendition.themes.fontSize()可以实现字体大小切换功能,往括号里传你点击的大小即可。但是现在这里.book是没有办法获取到电子书的,因为当前这个this.book对象还没有写入到vuex当中,此时book对象只是在EbookReader中new出来,所以我们可以到EbookReader把new出来的book对象放到vuex当中,这样任何组件就都可以使用这个book对象啦,同理加到modules中的book.js中,以及geeters以及mixin中
在EbookReader中获取到电子书的book对象后就修改vuex中的book对象,然后其他组件就可以通过this.currentBook来用这个book对象了
然后字号设置栏就可以使用这个this.currentBook.rendition.themes.fontSize()去修改电子书的字体大小了,而且注意要加上单位,还要注意defaultFontSize默认字号也设置上哦
接下来我们做字号的离线缓存功能,缓存字号到浏览器中
我们设置好字号后,一旦刷新页面,所有配置都会回到默认配置,这是因为这些配置都没有进行缓存,这里我们使用localStorage来存储这些数据,我们在浏览器中打开Application即应用可以看到Storage。这是html5加入的,是为了解决cookie存储空间不足的问题,cookie每一条存储空间只有4K,而localStorage一般为5兆。
下面我们就将localStorage来引入我们的项目,先停止项目,安装一个localStorage库 : npm i --save web-storage-cache
安装完后我们在utils下面创建一个localStorage.js,把这个库即web-storage-cache传入,这个库好处是能把我们传入的字符串,比如我们传入的是一个对象它可以变为JSON再存储,然后我们读取的时候它可以将JSON字符串再转化为对象,使用起来就很方便。
然后我们要创建一个localStorage的变量,通过new Storage()。往localStorage写入东西用localStorage.set(key,value)即可,从localStorage中取出东西用localStorage.get(key)即可,删除localStorage中的值可以通过localStorage.delete(key),全部清空可以通过localStorage.clear()
因为我们每一本电子书它所存储的内容可能都不一样,所以我们为每一本电子书都开一个localStorage空间,所以我们定义一个setBookObject,我们将localSotrage的key定义为fileName即电子书路径,这个fileName下面又包含了一个key value。
定义一个book对象 通过getLocalStorage去取一下,看看通过这个fileName能不能取的到,如果能取的到,能够取到就把book的key、value赋值进去;不能取到就先把book定义为一个对象,然后再进行key、value赋值;写入完成之后保存到localStorage中。然后我们再写一个获取的代码,这样我们就封装完了一个最基础的源码
在此基础上,比如说我们想要存储获取我们的字号该怎么做呢,可以如下
方法封装完成,我们到具体的应用场景中应用,如下我们做字号的缓存
如下,切换字号的方法中保存字号到本地缓存中。然后浏览器中本地存储就能保存到了。
但是这里有一个问题,就是我们刷新页面后可以看到默认的时候是没有字号的,也就是字号没有初始化过程,下面我们做一下初始化的过程,在display()之后做初始化,
5、阅读器主题设置功能实现
我们之前只是实现阅读器的主题切换,现在我们来实现整体的全局的主题切换
内层阅读器由于它嵌套在iframe里面,它的样式切换我们仍然需要通过themes对象来实现,而外层主题样式的切换需要通过动态加载css来实现。
我们先实现阅读器主题设置,我们之前有设置过,下面我们就来做代码集成
如上图可知我们还需要一个主题数组,然后一个改变主题的方法,接下来我们就做,主题数组是一个静态数据所以我们在utils的book.js中写(这个book.js我们就是专门用来放静态资源的)
写好themeList后,在要用到themeList的组件中引入utils/book下的themeList后去使用这个themeList
我们发现好几个地方可能要用到这个themeList,所以我们把它引入到mixin当中
然后我们去搞defaultTheme,同理在vuex、actions.js、getters.js、mixin.js中定义好这个默认主题
然后到EbookMenu中把这个EbookSettingTheme组件引入进来
然后选中的主题我们是让它有绑定class即特殊有selected样式的,可是这里没有显示出来,这是因为我们defaultTheme默认的时候是通过name来匹配的,而原先这个是用index匹配,所以我们要改一下把index改为name即可
接下来写切换主题的方法,同理调用book对象的theme对象实现主题切换,只不过主题这里要注意用户点击主题我们获取到的是index,而不是直接获取到主题所以需要先转换一下,而且我们主题是以theme中的name为辨识的而不是theme为辨识,所以像改变默认主题这些是传入theme的name值而不是theme值。
首先我们先注册主题以及select渲染主题
这是初始化电子书的时候应该做的所以在EbookReader中做注册主题以及select主题,这只是电子书初始化时注册select主题
然后接下来我们来实现设置主题功能
主题切换就是获取到当前点击的主题名称后去改变vuex中的默认主题,然后再通过book的thmes去调用select方法去切换主题即可
同样我们将主题进行缓存,在ebookReader当中增加缓存功能,
接下来我们做动态加载css去切换全局样式
动态切换全局样式的原理是为DOM动态添加或删除CSS文件,我们知道css的引入是通过head标签下添加link标签,那么动态添加或删除css文件的原理也是一样的,就扫它的head标签,往head中添加我们自定义的link标签即可。
到utils中的book.js中添加一个方法,即动态添加css
去初始化全局样式。
nginx中有这四个主题样式的css文件了,我们通过如下可以打开nginx中的这个css文件
把这个路径拷贝下来放到如下,可以看到标题栏菜单栏设置栏就都是这种颜色的css样式啦
接下来我们需要根据当前默认的主题来动态的切换,怎么实现呢,通过switch case,如果当前默认主题为Default则加载xxx的css样式文件,然后如果当前默认主题为xxx则加载xxx的
此时去切换主题,发现标题菜单栏没有跟着切换,去看看head最后,点击切换主题,head后面没有动态添加link,也就是上面的addCss没有成功
因为这个initGlobalStyle是在EbookReader中,而切换主题时在主题设置栏中,还没有把initGlobalStyle传过去,所以你主题设置栏中点击跟这个EbookReader中的这个方法没有关系。也就是在主题设置栏中的切换主题的setTheme方法中需要添加一个设置全局样式的方法。我们把initGlobalStyle放到mixin中进行复用,然后主题设hi在栏中调用该方法即可实现全局样式切换啦
这里还要解决一个问题,就是我们每点击一次就会在最后添加一个css的link文件,当用户反复点击后那么就会添加很多样式文件,后一个会覆盖前一个,这样会降低我们的渲染效率,下面我们就来做一个清除样式的css方法来解决这个问题
然后在设置全局样式前先移除所有的动态css样式文件即那link
此时切换主题,到head标签看,可以看到之前的主题link会被清空,全局只会保留一个css样式