课程常用链接
【前奏-课程】快速入门Web阅读器开发
【小慕读书web端】Vue 实战商业级读书Web APP 全面提升技能
【epub图书免费下载站点 · 中文书】http://www.ziliaoh.com/epub.html
【项目代码gitee】https://gitee.com/yishen_yishen/vue-ebook 如果对你有帮助欢迎点个 star
【md格式 源文档下载】下载链接 在线阅读可能会乱码(我知道是编码问题,但在七牛云里不知道怎么修改),请下载后查看
阅读器原理课程-免费课
概览
知识点脑图
阅读器工作原理
环境搭建
vue-cli环境
- 环境准备
iMac-Pro:code yishen$ node -v
v14.0.0
iMac-Pro:code yishen$ npm -v
6.14.5
iMac-Pro:code yishen$ vue -V
2.9.6
-
离线版安装
- GitHub下载webpack 下载到桌面,然后解压
cd ~ cd .vue-templates/ cp -R ~/Desktop/webpack-develop webpack # -R 是将目录下所有文件复制到新目录webpack下 cd ~/Desktop # 项目路径,后面会在此路径下新建项目文件夹 vue init webpack --offline ebook-read # 新建ebook-read项目,并初始化
-
启动vue项目
npm run dev
sass支持
npm install node-sass sass-loader --save-dev
epubjs扩展
npm install epubjs --save
项目配置
viewport配置
-
viewport用来设置用户在手机上的可视区域
-
width=device-width:指定viewport宽度为设备宽度;inital-scale=1.0:指定默认缩放比例为1:1
-
通过maximum-scale和minimun-scale限定屏幕缩放比例为1:1,通过user-scalable限制用户对屏幕进行缩放
-
项目根目录Index.html文件,内部
<meta name="viewport" content="width=device-width,initial-scale=1.0,
maximum-scale=1.0,minimum-scale=1.0,user-scalable=no">
rem配置
-
rem是css3新增的一个相对长度单位
-
rem相当于根元素font-size值的倍数
- 2rem = 根元素font-size *2
-
DOMCotentLoaded事件动态设置根元素font-size
html.style.fontSize = window.innerWidth / 10 + 'px'
-
/src/App.vue文件,<script>内部
document.addEventListener('DOMContentLoaded', () => { const html = document.querySelector('html') let fontSize = window.innerWidth / 10 fontSize = fontSize > 50 ? 50 : fontSize html.style.fontSize = fontSize + 'px' })
reset.scss和global.scss
- Reset.scss是为了消除不同浏览器默认样式的不一致性
- Global.scss规定整个站点的公共样式,公共方法和公共参数
- 实现px2rem方法,将px转化为rem
- Reset.scss代码 参考链接
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
html,body{
width: 100%;
height: 100%;
font-family: 'PingFangSC-Light','PingFang SC','StheitiSC-Light',
'Helvetica-Light','Arial','sans-serif';
}
- global.scss代码
@import 'reset';
// 1rem = fontSize px
// 1px = (1 / fontSize)rem
$fontSize:37.5;
@function px2rem($px) {
@return ($px / $fontSize)+rem;
}
@mixin center() {
display: flex;
justify-content: center;
align-items: center;
}
web端小慕读书-付费课
环境搭建
Node.js环境
nvm工具的使用
-
nvm作用:node version manger,node版本管理工具
-
nvm github地址https://github.com/nvm-sh/nvm
-
nvm安装
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash
nvm常用命令
- 换淘宝源
export NVM_NODEJS_ORG_MIRROR=https://npm.taobao.org/mirrors/node
- 其他
nvm install node // 安装最新版nodejs
nvm install 10.10.0 // 安装指定版本
nvm use 10.10.0 // 切换到指定版本
Vue CLI 3.0环境搭建
- 卸载老版本
npm unistall vue-cli -g
- 安装新版本
npm install -g @vue/cli
- 原型开发
npm install -g@vue/cli-service-global
npm i -g @vue/cli-service-global
vue.config.js文件配置
-
作用:解决production模式下路径问题(虽然我没看懂代码╮( ̄▽ ̄)╭)
module.exports = {
publicPath: process.env.NODE_ENV === 'production'
? './'
: '/'
}
Vue-remote-devtools调试工具
此工具与Chrome插件功能相同,一个是浏览器扩展,一个独立分离出来,可以不安装
-
安装
npm install -g @vue/devtools
添加--verbose
可以查看安装进度,以及请求地址
Electron,安装上述工具时,可能会需要安装这个Electron,不过由于网络的原因,会下载失败。
修改 ~/.npmrc 文件,添加一行ELECTRON_MIRROR=“https://cdn.npm.taobao.org/dist/electron/”
- 添加
<script src="http://localhost:8098"></script>
到/public/index.html- 项目上线时,要删除掉这句话
epubjs扩展
npm i --save epubjs
sass扩展
npm i --save-dev node-sass sass-loader
sass报错:this.getResolve is not a function
-
版本过高引起的,或其他低版本的不适用高版本sass
-
可降低sass版本解决
npm uninstall sass-loader npm i -D sass-loader@7.3.1
web字体引入
谷歌字体api
使用方法
[三方汉化]谷歌字体api
- 谷歌中文字体api(第三方汉化):地址
font-family: 'Hanalei Fill', cursive;
font-family: 'Kirang Haerang', cursive;
font-family: 'Merriweather', serif;
font-family: 'MedievalSharp', cursive;
font-family: 'Ranga', cursive;
Nignx搭建静态服务器
-
mac上安装Nignx需要先安装brew
-
brew 安装nginx
brew install nginx
-
运行nginx
sudo nginx
配置文件地址:
/usr/local/etc/nginx/nginx.conf
-
停止运行
sudo nginx -s stop
-
重新加载
sudo nginx -s reload
外链配置
项目根目录新建.env.development
文件和 .env.production
文件
VUE_APP_RES_URL=http://127.0.0.1:9001/
-
外部引用举例
-
initGlobalStyle () { console.log(this.defaultTheme) addCss(`${process.env.VUE_APP_RES_URL}/themes/theme_eye.css`) },
nginx配置相关
- 需将
autoindex on;
才可以访问目录
nodejs环境接口搭建、相关知识点
-
新建node-imooc-ebook文件夹,app.js入口文件
-
npm init
初始化npm项目,生成package.json文件 -
安装mysql操作库
npm i mysql -S
-
Nodemon 插件
-
每次修改完文件,需要重新node app.js 才能执行
Nodemon app.js 在项目保存后自动重新运行项目
-
-
cors-跨域问题
什么是跨域:链接
npm i -S cors
app.js中
const cors = require('cors') app.use(cors())
知识点
vue
vue引用中的@符号
地址中的@符
-
当引用文件时
import '@/assets/styles/global.scss'
-
@代表/src
- 可以在
build/webpack.base.conf.js
中设置
- 可以在
import前面的@符
script中的import是js的语法, 是在js中去引用css文件
style中的@import是stylus的语法,是在css中引用css文件
备用链接
transition动画原理
- 使用v-show动态显示或隐藏元素时,会触发过渡动画
- transition需要指定name,并包裹一个包含v-show的div
- vue会为transition包裹的div动态添加class,公6种
transition
-
代码实现
-
html部分。src/Ebook.vue,外围使用包裹,带上name属性,被包裹的部分要有vshow或者vif
<transition name="slide-down"> <!-- 下面就是要展示动画的部分 --> <div class="title-wrapper" v-show="isTitleAndMenuShow" > <div class="left"> <span class="iconfont iconbackarrow"></span> </div> <div class="right"> <div class="icon-wrapper"> <span class="iconfont iconbooks"></span> </div> <div class="icon-wrapper"> <span class="iconfont iconiconfontcart-copy"></span> </div> <div class="icon-wrapper"> <span class="iconfont iconi-more"></span> </div> </div> </div> </transition>
* css 部分
~~~scss
.slide-down-enter-to,
.slide-down-leave {
transform: translate3d(0, 0, 0);
}
.slide-down-enter-active,
.slide-down-leave-active {
transition: all 0.3s linear;
}
.slide-down-enter,
.slide-down-leave-to {
transform: translate3d(0, -100%, 0);
}
transition-group
作为多个元素/组件的过渡效果。 渲染一个真实的 DOM 元素(通过tag指定)
<template>
<div class="shelf-list">
<transition-group
name="list"
tag="div" // tag='div',这该元素渲染文div
id="shelf-list"
>
<div
class="shelf-list-item"
v-for="(item, index) in shelfList"
:key="index"
>
......
</div>
</transition-group>
</div>
</template>
- :key引发的bug
bug描述:动画没有效果,动画的类没有加到dom上面
// 原始代码,没有任何效果,dom上没有添加.list-move,.list-leave-active等类名
<transition-group
name="list"
tag="div"
id="shelf-list"
>
<div
class="shelf-list-item"
v-for="(item, index) in shelfList"
:key="index"
>
......
</div>
</transition-group>
// 修改后代码,效果正常
<transition-group
name="list"
tag="div"
id="shelf-list"
>
<div
class="shelf-list-item"
v-for="(item) in shelfList"
:key="item.id" // 修改了这里
>
......
</div>
</transition-group>
只是把:key由原先的index修改为了item.id,我也不知道什么原因
官网中关于key的说明:内部元素总是需要提供唯一的
key
attribute 值 index不唯一吗?搜到了一个比较靠谱的答案:
交换位置后(对应到我的项目,就是删除某个),元素的key发生了变化
解决:给key值设置一个不会因为位置变化而变化的值
vue中的mixin混入
代码举例:
- 定义混入对象
import { mapGetters } from 'vuex'
export const ebookMixin = {
computed: {
...mapGetters(['fileName', 'menuVisible'])
}
}
- 使用混入对象
<script>
import { ebookMixin } from '../../utils/mixin'
export default {
mixins: [ebookMixin]
}
</script>
Vuex(仅代表个人理解)
概念
-
一句话简介:解决组件非常多时,组件之间传参的问题(个人理解) 官方地址
-
核心概念:state、mutations、getters、actions、module
-
State:相当于父组件中的props:{},定义一些子组件需要使用的变量(布尔、数组、对象、数值、字符串等)
-
props: { isTitleAndMenuShow: { type: Boolean, default: false }, fontSizeList: Array, defaultFontSize: Number, defaultTheme: Number, themesList: Array, // 图书是否加载完毕 bookAvailable: Boolean, navigation: Object },
-
使用举例
const book = {
state: {
fileName: '',
menuVisible: false,
// 底部的菜单项,倒数第二条,-1:不显示,0:字号,1:主题、2:进度条、3:目录
settingVisible: -1,
},
mutations: {
SET_FILENAME: (state, fileName) => {
state.fileName = fileName
},
SET_MENU_VISIBLE: (state, visible) => {
state.menuVisible = visible
},
SET_SETTING_VISIBLE: (state, visible) => {
state.settingVisible = visible
}
}
}
export default book
const getters = {
fileName: state => state.book.fileName,
menuVisible: state => state.book.menuVisible,
settingVisible: state => state.book.settingVisible
}
export default getters
const actions = {
setFontFamilyVisible: ({ commit }, visible) => {
return commit('SET_FONT_FAMILY_VISIBLE', visible)
},
setDefaultFontFamily: ({ commit }, font) => {
return commit('SET_DEFAULT_FONT_FAMILY', font)
},
setDefaultFontSize: ({ commit }, fontSize) => {
return commit('SET_DEFAULT_FONT_SIZE', fontSize)
}
}
export default actions
import Vue from 'vue'
import Vuex from 'vuex'
import book from './modules/book'
import getters from './getters'
import actions from './actions'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
mutations: {
},
actions,
getters,
modules: {
book
}
})
使用
import { mapGetters, mapActions } from 'vuex'
export const ebookMixin = {
computed: {
...mapGetters([
'fileName',
'menuVisible',
'settingVisible'
])
},
methods: {
// 下面的方法用来设置上面变量的值
...mapActions([
'setMenuVisible',
'setFileName',
'setSettingVisible'
])
}
}
<script>
import { ebookMixin } from '../../utils/mixin'
export default {
methods: {
hideTitleAndMenu () {
this.setSettingVisible(-1) // 调用setSettingVisible方法将settingVisible的值修改为-1
this.setMenuVisible(false) // 可以直接this.调用
}
}
}
</script>
vue-i18n实现国际化
- 安装插件
npm i --save vue-i18n
- 初始化i18n对象
import Vue from 'vue'
import VueI18N from 'vue-i18n'
import en from './en'
import cn from './cn'
import { getLocale, setLocale } from '../utils/localStorage'
Vue.use(VueI18N)
const messages = {
en, cn
}
// 通过读取缓存获取,默认语言,缓存中无数据,默认设置为cn
let locale = getLocale()
if (!locale) {
locale = 'cn'
setLocale('locale', locale)
}
const i18n = new VueI18N({
locale,
messages,
// 下面这项,是为了消除过多的警告
silentFallbackWarn: true
})
export default i18n
- vue-html中解析对应文本
<span>{{$t('book.selectFont')}}</span>
vue中alias别名的使用
-
解决循环渲染,需要国际化的问题
-
实例:设置主题 EbookSettingTheme src/utils/book.js
<div class="setting-theme">
<div
class="setting-theme-item"
v-for="(item, index) in themesList"
:key="index"
@click="setTheme(index)"
>
<div
class="preview"
:style="{background: item.style.body.background}"
:class="{'no-border':item.style.body.background !== '#fff'}"
></div>
<div
class="text"
:class="{'seleted':item.name===defaultTheme}"
>{{item.alias}}</div>
</div>
</div>
export function themeList (vue) {
return [
{
alias: vue.$t('book.themeDefault'),
name: 'Default',
style: {
body: {
color: '#000', background: '#fff'
}
}
}
]
Vue中的updated钩子
由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。无论是组件本身的数据变更,还是从父组件接收到的 props 或者从vuex里面拿到的数据有变更,都会触发虚拟 DOM重新渲染和打补丁,并在之后调用 updated。官网介绍
updateProgress(){
this.$refs.progress.style.backgroundSize = `${this.progress}% 100%`;
},
动态组件component
<template>
<div>
<component :is="currentTab === 1 ? content : bookmart"></component>
</div>
</template>
<script>
data () {
return {
currentTab: 1, // 通过改变currentTab的值,就能分别渲染、切换不同组件
content: EbookSlideContents, // 这是组件,记得引入,这里省略引入
bookmart: EbookSlideBookMark
}
},
</script>
事件修饰符
.passive
-
这个
.passive
修饰符尤其能够提升移动端的性能。
<div
class="scroll-wrapper"
:class="{'no-scroll':ifNoScroll}"
@scroll.passive="handleScroll()"
ref="scrollWrapper"
>
<slot></slot>
</div>
.stop,阻止点击事件向下冒泡
-
代码描述 [src/components/shelf/ShelfItem.vue](# TODO)
-
描述:避免上层的点击事件触发后,还会触发下层的点击事件
OnItemSelected触发之后不会在触发onItemClick
<template>
<div
class="shelf-item shelf-item-shadow"
:class="{'hide-shadow':data.type===3}"
@click="onItemClick"
>
<component
:is="item"
:data="data"
></component>
<div
class="shelf-item-selected"
v-show="isEditMode && data.type ===1"
@click.stop="OnItemSelected"
>
<span class="iconfont iconselected"></span>
</div>
</div>
</template>
插槽slot标签
单插槽
- 业务描述,定义一个组件里面需要接受一些标签(父组件传递过来的)
- 子组件代码 src/components/common/Scroll.vue
<template>
<div
class="scroll-wrapper"
:class="{'no-scroll':ifNoScroll}"
@scroll.passive="handleScroll"
ref="scrollWrapper"
>
<!-- 预留插槽,用来替换传递过来的内容 -->
<slot></slot>
</div>
</template>
<template>
<Scroll
class="slide-search-list"
:top="66"
:bottom="49"
v-show="searchVisible"
>
<!-- 这中间的内容都会被子组件中<slot>标签替换 -->
<div>
我是内容
</div>
</Scroll>
</template>
多插槽
- 子组件 src/components/common/Dialog.vue,好像叫子组件不合适呢?
<div class="dialog-wrapper">
<div class="dialog-title-wrapper">
<span class="dialog-title-text">{{title}}</span>
</div>
<slot>
<!-- 插槽一 -->
</slot>
<div class="dialog-btn-wrapper">
<slot name="btn">
<!-- 插槽二,中间有默认内容,父组件不传递内容,就使用默认内容 -->
<div
class="dialog-btn"
@click="hide"
>{{$t('shelf.cancel')}}</div>
<div class="dialog-btn">{{$t('shelf.confirm')}}</div>
</slot>
</div>
</div>
<ebook-dialog
:title="title"
ref="dialog"
>
<!-- 这里是插槽一的内容,省略了,没粘进来 -->
<!-- 下方div有slot="btn"与子组件<slot name="btn">匹配 -->
<div
slot="btn"
class="group-dialog-btn-wrapper"
>
......
</div>
</ebook-dialog>
ref指向性问题
- Ref:给元素或子组件注册引用信息
// 指向dom元素
<div ref="div"></div>
// 指向组件对象
<Button ref="btn"></Button>
// this.refs.btnList是一个列表,列表里的每一项是一个组件对象
<Button v-for="xx in xx" ref="btnList">
当指向dom元素时获取样式:this.$refs.scroll.style.height
当执行组件对象,获取样式:this. r e f s . s c r o l l . refs.scroll. refs.scroll.el.style.height
$nextTick
- 在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM
- 我理解的太浅,不懂vue的dom更新,以及响应式序列啥的
代码示例 src/components/home/SearchBar.vue
showHotSearch () {
if (this.hotSearchOffsetY === 0) {
this.hideShadow()
} else if (this.hotSearchOffsetY > 0) {
this.showShadow()
}
this.hideTitle()
this.hotSearchVisible = true
// TODO 重置阅读进度
this.$nextTick(() => {
this.$refs.hotSearch.reset()
})
}
代码示例 src/views/store/StoreShelf.vue
watch: {
isEditMode (isEditMode) {
this.scrollBottom = isEditMode ? 48 : 0
this.$nextTick(() => {
// 等所有dom结构更新完后,在计算scroll的高度
this.$refs.scroll.refresh()
})
}
},
vue-create-api
-
一个能够让Vue组件通过API方式调用的插件
- 可以大幅度降低引用组件的代码
- 传统父组件使用子组件,需要js中引入,components中声明,html中写入DOM
- 使用此插件,可以很简单的简化
- 此插件是在body下创建dom,与vue在#app下不同,通常全屏的消息提示框,选择框使用它
-
安装
npm i -S vue-create-api
-
代码演示
- 初始化、create-api.js(mian.js中需要引入这个文件)
import CreateAPI from 'vue-create-api'
import Vue from 'vue'
import Toast from '../components/common/Toast.vue'
Vue.use(CreateAPI)
Vue.createAPI(Toast, true)
- Toast.vue组件src/components/common/Toast.vue
<script>
// 省略了很多无关代码
export default {
// 必须有name,否则create-api无法使用此组件
name: 'Toast',
props: {
text: [String, Number],
timeout: {
type: Number,
default: 1500
}
},
methods:{
show(){
this.visible = true
},
hide(){
this.visible = false
}
}
}
</script>
// 其他组件的methods中使用
this.$createToast({
$props: {
// 这里就相当于Toase组件中的props
text: 'hello imooc'
}
}).show()
- 更一步的简化
create-api.js文件
// 新增混入全局方法
Vue.mixin({
methods: {
toast (settings) {
return this.$createToast({
$props: settings
})
}
}
})
- 使用
this.toast({ text: 'hello' }).show()
v-for和v-if一起使用
可能会报错:The ‘undefined’ variable inside ‘v-for’ directive should be replaced with a computed property that returns filtered array instead. You should not mix ‘v-for’ with ‘v-if’
原因:v-for的优先级会高于v-if,因此v-if会重复运行在每个v-for中
- 解决方法,新加一个外层()使用vfor或者vif
<template v-for="(item, index) in categoryList">
<div
class="dialog-list-item"
:class="{'is-add': item.edit ? item.edit === 1 : false}"
:key="index"
@click="onGroupClick(item)"
v-if="(item.edit === 2 && isInGroup) || item.edit !== 2 || !item.edit"
>
</div>
</template>
样式问题以及scss
样式
绝对定位子元素居中父元素
- 父元素css
position: relative;
- 子元素css
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%); // 向左向上偏移自身的50%
超出部分用省略号代替
// 超出的部分用省略号...代替
text-overflow: ellipsis;
// 超出的部分隐藏
overflow: hidden;
// 不换行
white-space: nowrap;
显示两行,多余省略号
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
white-space: normal;
text-overflow: ellipsis;
// 允许打断单词进行换行
word-break: break-all;
// 不允许打断单词,进行换行
word-break: keep-all;
Position:absolute的其他用法
标题起的可能不合适
-
还可以做居中效果
@mixin absCenter { position: absolute; top: 0; bottom: 0; left: 0; right: 0; margin: auto; }
-
效果演示
scss语法
@mixin:混入
- 定义
// 定义一段垂直居中,水平居中的代码段
@mixin center() {
display: flex;
justify-content: center;
align-items: center;
}
// 混入允许带参数
@mixin ellipsis2($line) {
display: -webkit-box;
-webkit-box-orient: vertical;
// 要显示多少行
-webkit-line-clamp: $line;
white-space: normal;
text-overflow: ellipsis;
// 允许打断单词进行换行
word-break: break-all;
}
- 使用
// 是left类,垂直居中,水平居中
.left{
@include center;
}
.text{
// 带参数的形式
@include ellipsis2(2);
}
&符号
- 作用:引用父元素
.dashboard {
&-container {
margin: 30px;
}
&-text {
font-size: 30px;
line-height: 46px;
}
}
- 编译为css
.dashboard-container {
margin: 30px;
}
.dashboard-text {
font-size: 300px;
line-height: 46px;
}
animation
动画属性,将动画绑定到一个div元素,css提供的属性,非scss
代码演示src/components/home/FlapCard.vue
.flap-card-bg {
// scale:缩放
transform: scale(0);
opacity: 0;
&.animation {
// both 是动画完成后,保持在100%的效果(替代上面两行)
animation: flap-card-move 0.3s ease-in both;
}
// 刚开始进入翻转加载的动画效果
@keyframes flap-card-move {
0% {
transform: scale(0);
opacity: 0;
}
50% {
transform: scale(1.2);
opacity: 1;
}
70% {
transform: scale(0.9);
opacity: 1;
}
100% {
transform: scale(1);
opacity: 1;
}
}
epubjs
epubjs原理及常用方法
原理图
常用方法
api地址(网站比较慢,开全局代理)
this.rendition.prev() // 后退翻页
this.rendition.next() // 下一页
this.rendition.themes.fontSize(16px) // 设置字号
// 主题
this.themeList.forEach(theme => {
// 注册主题
this.rendition.themes.register(theme.name, theme.style)
})
this.rendition.themes.select(theme.name) // 选择主题
项目中的问题
HTML5、input实现进度条效果
- 成品展示
- 代码实现
<input
class="progress"
type="range"
max="100"
min="0"
step="1"
@change="onProgressChange($event.target.value)"
@input="onProgressChange($event.target.value)"
:value="progress"
:disabled="!bookAvailable"
ref="progress"
>
.progress {
width: 100%;
// 清除默认样式
-webkit-appearance: none;
height: px2rem(2);
background: -webkit-linear-gradient(#53575d, #53575d) no-repeat, #abacae;
// 下面代表已读 总的。前45%background:#53575d,其余#abacae
background-size: 45% 100%;
&:focus {
outline: none;
}
&::-webkit-slider-thumb {
// 滑块的样式
-webkit-appearance: none;
height: px2rem(15);
width: px2rem(15);
border-radius: 50%;
background: #fff;
box-shadow: 0 4px 4px 0 rgba($color: #000000, $alpha: 0.15);
border: px2rem(1) solid #ddd;
}
}
项目中设置字体
- 引入选好的字体API api地址 (此api是英文字体,对中文不起作用,没找到中文字体的Api)
由于epub解析图书,是依赖于iframe的,外层的字体文件无法直接传递到内层的iframe(大概是这个意思,不太懂),所以需要使用epub提供的一个register()方法,来使内层iframe获取到字体样式
src/components/ebook/EbookReader.vue
this.rendition.hooks.content.register(contents => {
Promise.all([
contents.addStylesheet(
'https://fonts.font.im/css?family=Hanalei+Fill|Kirang+Haerang|Merriweather|MedievalSharp|Ranga')
]).then(() => {
// console.log('字体全部加载完毕。。。')
})
})
src/components/ebook/EbookSettingFontPopup.vue
- 调用themes下的font()方法设置字体
setFontFamily (font) {
// font 是字体名称
this.setDefaultFontFamily(font)
this.currentBook.rendition.themes.font(font)
}
缓存问题,相当于小程序中的storage
- 安装包
npm i --save web-storage-cache
- 使用,封装常用函数
import Storage from 'web-storage-cache'
const localStorage = new Storage()
export function setLocalStorage (key, value) {
return localStorage.set(key, value)
}
export function getLocalStorage (key) {
return localStorage.get(key)
}
export function removeLocalStorage (key) {
return localStorage.delete(key)
}
export function clearLocalStorage (key) {
return localStorage.deleteAllExpires(key)
}
- 可在以下位置查看缓存,可以手动修改
cssText设置属性!important
- 说明:需要为一个属性设置!important
src/components/ebook/EbookSettingProgress.vue
updateProgressBg () {
// 背景色变化,样式已经写好,更改background-size即可
// ASK 标记,浪费了老子贼多时间,搞这个问题,不知道哪里冲突不加!important就是不行
this.$refs.progress.style.cssText = `background-size:${this.progress}% 100% !important;`
// this.$refs.progress.style.backgroundSize = `${this.progress}% 100%`
},
// 方法一
var obj = document.getElementById('no');
function setStyle(obj, css) {
for(var attr in obj){
obj.style[attr] = css[attr];
}
}
setStyle(obj,{width:"400px",height:"300px"});
// 方法二,个人感觉此方法简单,还可以设置!important,方法一不知道可不可以
var obj = document.getElementById('no');
obj.style.cssText = "width:400px; height:300px;";
font-size=0消除空行
.slide-contents-book-progress {
font-size: 0;
.progress {
font-size: px2rem(14);
}
.progress-text {
font-size: px2rem(12);
}
}
其他知识点
blob链接
业务代码:src/components/ebook/EbookReader.vue
// 解析电子书内容,获取封面,标题,作者等信息
parseBook () {
// 获取cover
this.book.loaded.cover.then(cover => {
this.book.archive.createUrl(cover).then(url => {
// url----blob:http://localhost:8080/ecf9934c-1313-4b3d-9647-caa7acafb152
this.setCover(url)
})
})
}
js语法二维数组变一维数组.concat.apply用法
// .concat()用法:连接两个数组
a=[1,2,3,4,5]
b=[2,3,4,5,6,7]
[].concat(a,b) --> [1, 2, 3, 4, 5, 2, 3, 4, 5, 6, 7]
// .apply()用法,使用apply将他们([],c)作为形参传入,将数组中的对象逐一的传入到concat中(老师原话,不太理解)
c=[[1,2,3],[9,9,9]]
[].concat.apply([],c) -->[1, 2, 3, 9, 9, 9]
apply()用法:能改变this的指向(个人理解),参考链接-w3school,菜鸟教程
数组常用方法
filter()
创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素;
不会对空数组进行检测;
不会改变原始数组;
项目实例:src/components/ebook/EbookBookmark.vue
removeBookmark () {
console.log('删除书签')
const currentLocation = this.currentBook.rendition.currentLocation()
const cfi = currentLocation.start.cfi
this.bookmark = getBookmark(this.fileName)
if (this.bookmark) {
// 保留缓存中,cfi项与要删除的cfi不相同的----> 删除与cif相同的
saveBookmark(this.fileName, this.bookmark.filter(item => item.cfi !== cfi))
}
}
参考链接 菜鸟教程
Array.some()
检测数组中国的元素是否满足指定条件
有一个满足条件的元素,即返回true,剩余的元素不在执行检测
Array.every()
every() 方法用于检测数组所有元素是否都符合指定条件(通过函数提供)。
every() 方法使用指定函数检测数组中的所有元素:
- 如果数组中检测到有一个元素不满足,则整个表达式返回 false ,且剩余的元素不会再进行检测。
- 如果所有元素都满足条件,则返回 true。
Array.map()
map()方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值
map()方法按照原始数组元素顺序依次处理元素
注意: map() 不会对空数组进行检测。
注意: map() 不会改变原始数组
mock.js–生成随机数据,拦截Ajax请求
- 安装
npm i mockjs --D/--save-dev
,安装axios-发送网络请求用的npm - axios --save
indexDB
localforage
- 安装
npm i -S localforage
- 对indexDB进行操作的一个库
项目构建、发布
修改到线上地址
根目录的.env.production文件是线上版本的一些接口路径
.env.development 是开发版本的一些接口路径
.env.production文件内容
# nginx跟地址,不知道能不能加注释,反正没报错 VUE_APP_RES_URL=http://127.0.0.1:9001 # nginxepub目录 # VUE_APP_EPUB_URL=http://127.0.0.1:9001/epub VUE_APP_EPUB_URL=http://47.99.166.157/epub # api请求的根地址 VUE_APP_BASE_URL=http://localhost:8080 # 线上的api接口地址(老师的) VUE_APP_BOOK_URL=http://47.99.166.157:3000 # 所有的电子书,相当于nginx的资源目录 VUE_APP_EPUB_OPF_URL=http://47.99.166.157/epub2 # 老师的封装好的讯飞文字转语音api地址 VUE_APP_VOICE_URL=http://47.99.166.157:3000
构建生产版本
npm run build
构建过程中警告处理
文件大小超出
// vue.config.js文件内,与devServer同级,扩大资源限制到512 * 1024 (根据自己的项目来)
configureWebpack: {
performance: {
hints: 'warning',
maxAssetSize: 524288,
maxEntrypointSize: 524288
}
}
- 将引用的一些库,替换成线上cdn版本
例如:将本项目中占用资源最大的epubjs换成线上cdn版本(引用的版本应与本地版本一致,不要盲目最新版)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.5/jszip.min.js"></script>
添加到public/index.html的内部
console.log警告
- 少的化,搜索全部注释或者删除
- 多的化,https://blog.youkuaiyun.com/u013611033/article/details/104152300
需要注意的地方
驼峰命名&’-'命名
vue html部分,类名尽量使用"-",不要使用驼峰命名法(好像是因为不识别大小写的原因)
在有些地方,驼峰和’-'好像是一样的,例如fileName 和 file-name是表示同一个东西;
css中font-size、js操作css属性可以写作fontSize;
项目中各组件z-index
read阅读器页面
- #slide-content-wrapper,目录的侧边栏300
- #title-wrapper,上面的菜单栏201
- #menu-wrapper,下面的菜单栏201
- EbookSettingProgress中的#setting-wrapper,阅读进度200
- EbookSettingTheme中的#setting-wrapper,设置主题200
- EbookSettingFont中的#setting-wrapper,设置字体字号200
- #ebook-reader-mask,主页的蒙版(用来触发点击事件)150
StoreHome页面
-
searchBar组件
- .search-bar ,标题行,和输入框行 z-index:150
- .title-icon-back-wrapper ,左上方的返回图标,要大于.search-bar。z-index:200
-
FlapCard组件
- .flap-card-wrapper,用来显示推荐图书(伴有动画),浮在最上层:z-index:1000
- class=“flap-card”,展示动画变化用的,是动态变化的,范围是100-96
- .read-btn,推荐图书的立即阅读按钮,要在.flap-card-wrapper(1000)之上,zIndex:1100
StoreShelf页面
-
shelfTitle组件
- .shelf-title,zindex-130,要在当前页面的最上层
-
主页面内
- .store-shelf-scroll-wrapper,zindex:101
-
shelfFooter组件
- .shelf-footer,编辑状态下,下面的菜单栏,zindex:120
- .popup,点击下方的tab弹出的菜单框,zindex:2080
git的使用
首次使用,配置公钥。。。
- 留白,以后补充 //TODO
初始化,首次push
git init
touch README.md
git add README.md
git commit -m "提交显示的信息"
git remote add origin git地址
git push -u origin master
第二次push
git add .
git commit "第二次push"
git push
git表状态字符

字符状态
A: 工作区新增的文件
C: 文件的一个新拷贝
D: 你本地删除的文件,服务器上还在
M: 文件的内容或者mode被修改
R: 文件名被修改了
T: 文件的类型被修改了
U: 文件没有被合并,需要完成合并才能进行提交
X: 未知状态
颜色状态
绿色:新增文件
黑色:别删除文件
蓝色:之前已经存在,被修改文件
红色:新增,但是没有增加到git中
为git设置代理
- 修改 ~/.gitconfig 文件
- 注意:ssr走的是socks5
已知的BUG&优化
已知的BUG
- 当使用主题时,章节翻页时,会闪一下白色(老师的也会闪一下,但底色与主题颜色相同-背景色修改为了灰色不是白色不那么显眼-算是解决了吧)
- 点击屏幕中央,呼出上下菜单栏,会出现滚动条(老师的不会)(猜测:动画过渡导致页面宽高发生变化)–已解决
// 解决办法,隐藏进度条
html::-webkit-scrollbar{
width:0px
}
- 图书主题切换,短时间切换过多次,会没反应(主题已经调过来了,前端没有渲染)
- 切换不同的菜单选项(下面的)没有过度动画**—已解决**
- 点击目录蒙版区域隐藏目录,下面的菜单条没有被隐藏**—已解决**
- 目录跳转有问题**—已解决**
- 切换字体,有时会没有效果
- 清完缓存,第一次进入,字体大小会没效果(缓存中有)
原因:第一次初始化,这两个值没有定义(刷新一下就有了,定位不到出错位置)
- 上下章,能跳转的比目录上的多,有的图书会导致章节的选中效果选择了错误的章节**—严重性BUG**
- 应该验证章节跳转的逻辑,是依据什么进行跳转的(猜测是section的值有问题)
- this.navigation有问题
-
Scroll组件没有滚动条,当内容有很多屏时,不知道滑到哪里了(对 目录非常多的书 不友好)
- 理想效果:高度小于两屏时,隐藏滚动条,高于两屏时显示滚动条
- src/components/common/Scroll.vue----webkit-scrollbar
-
高度异常:很多地方都高度异常,可能是scroll组件问题 http://localhost:8080/#/store/home,滚动部分的高度没有变化,初试时,mounted中调用了refresh重新计算高度,没有问题,后面高度没有变化了
-
toast组件还是有问题,当切换页面时,toast不会自动消失
- 例如:删除分组时
-
store/detail页面底部加入书架:已解决
假设一本书已经在书架中,进入图书详情页detail底部的加入书架会变成已加入书架
初次进入底部的加入书架,会变成 已加入书架,但刷新一下,就变成了 加入书架
computed中inBookShelf变量的问题
解决方法:刷新进入bookdatail页面是,shelfList中值为空,重新获取即可
可以优化的地方
-
http://localhost:8080/#/store/shelf页面
-
-
pc端可以监听esc按键,调用取消的方法,移动端暂时不知道
-