课程常用链接

【前奏-课程】快速入门Web阅读器开发

【小慕读书web端】Vue 实战商业级读书Web APP 全面提升技能

【epub图书免费下载站点 · 中文书】http://www.ziliaoh.com/epub.html

【项目代码gitee】https://gitee.com/yishen_yishen/vue-ebook 如果对你有帮助欢迎点个 star

【md格式 源文档下载】下载链接 在线阅读可能会乱码(我知道是编码问题,但在七牛云里不知道怎么修改),请下载后查看

阅读器原理课程-免费课

概览

知识点脑图

阅读器工作原理

  • epubjs文档

环境搭建

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 / 10fontSize = fontSize > 50 ? 50 : fontSizehtml.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 | 20110126License: 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模式下路径问题(虽然我没看懂代码╮( ̄▽ ̄)╭)

  • 注意:baseUrl在Vue CLI 3.3被废弃,新版本使用publicPath

module.exports = {publicPath: process.env.NODE_ENV === 'production'? './': '/'
}
Vue-remote-devtools调试工具

此工具与Chrome插件功能相同,一个是浏览器扩展,一个独立分离出来,可以不安装

GitHub地址

  • 安装 npm install -g @vue/devtools 添加 --verbose可以查看安装进度,以及请求地址

    npm 配置

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 https://developers.google.com/fonts/docs/css2

使用方法

[三方汉化]谷歌字体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搭建静态服务器

Nginx.org

  • mac上安装Nignx需要先安装brew

    • /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
      
    • 一般来说会由于网络原因,安装失败(挂代理也不能使用:git默认不走代理,即使能正常访问GitHub,clone仓库时也非常慢,所以需要为git配置代理)

    • 国内下载方式/bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"

    • 参考链接,知乎 , gitee

  • brew 安装nginx

brew install nginx
  • 运行nginxsudo 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文件

  • 安装express,npm i express -S ;api参考手册

  • 安装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文件

参考链接:详解vue中常用的几种import引入方式

​ 备用链接

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"><!-- 下面就是要展示动画的部分 --><divclass="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

参考链接-vue.js

作为多个元素/组件的过渡效果。 渲染一个真实的 DOM 元素(通过tag指定)

  • 代码演示 src/components/shelf/ShelfList.vue
<template><div class="shelf-list"><transition-groupname="list"tag="div"   // tag='div',这该元素渲染文divid="shelf-list"><divclass="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-groupname="list"tag="div"id="shelf-list"><divclass="shelf-list-item"v-for="(item, index) in shelfList":key="index">......</div>
</transition-group>// 修改后代码,效果正常
<transition-groupname="list"tag="div"id="shelf-list"><divclass="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},
      
使用举例

src/store/modules/book.js

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

src/store/getters.js

const getters = {fileName: state => state.book.fileName,menuVisible: state => state.book.menuVisible,settingVisible: state => state.book.settingVisible
}
export default getters

src/store/actions.js

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

src/store/index.js

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}
})
使用

使用了混入技术,…mapGetters作用

src/utils/mixin.js

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的值修改为-1this.setMenuVisible(false)    // 可以直接this.调用}}
}
</script>

vue-i18n实现国际化

vue-i18n说明文档

  • 安装插件
npm i --save vue-i18n
  • 初始化i18n对象

src/lang/index.js

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

silentFallbackWarn: true的作用

  • vue-html中解析对应文本
<span>{{$t('book.selectFont')}}</span>

vue中alias别名的使用

  • 解决循环渲染,需要国际化的问题

  • 实例:设置主题 EbookSettingTheme src/utils/book.js

<div class="setting-theme"><divclass="setting-theme-item"v-for="(item, index) in themesList":key="index"@click="setTheme(index)"><divclass="preview":style="{background: item.style.body.background}":class="{'no-border':item.style.body.background !== '#fff'}"></div><divclass="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

  • component,动态组件
  • 代码示例 src/components/ebook/EbookSlide.vue
<template><div><component :is="currentTab === 1 ? content : bookmart"></component></div>
</template>
<script>
data () {return {currentTab: 1, // 通过改变currentTab的值,就能分别渲染、切换不同组件content: EbookSlideContents, // 这是组件,记得引入,这里省略引入bookmart: EbookSlideBookMark}},
</script>

事件修饰符

.passive
  • 参考链接-vuejs官网

  • 这个 .passive 修饰符尤其能够提升移动端的性能。

  • 代码示例 src/components/common/Scroll.vue

<divclass="scroll-wrapper":class="{'no-scroll':ifNoScroll}"@scroll.passive="handleScroll()"ref="scrollWrapper"><slot></slot></div>
.stop,阻止点击事件向下冒泡
  • 代码描述 [src/components/shelf/ShelfItem.vue](# TODO)

  • 描述:避免上层的点击事件触发后,还会触发下层的点击事件

OnItemSelected触发之后不会在触发onItemClick

<template><divclass="shelf-item shelf-item-shadow":class="{'hide-shadow':data.type===3}"@click="onItemClick"><component:is="item":data="data"></component><divclass="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><divclass="scroll-wrapper":class="{'no-scroll':ifNoScroll}"@scroll.passive="handleScroll"ref="scrollWrapper"><!-- 预留插槽,用来替换传递过来的内容 --><slot></slot></div>
</template>

参考链接-官网插槽最基础,深入了解插槽

  • 父组件代码 src/components/ebook/EbookSlideContents.vue
<template><Scrollclass="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"><!-- 插槽二,中间有默认内容,父组件不传递内容,就使用默认内容 --><divclass="dialog-btn"@click="hide">{{$t('shelf.cancel')}}</div><div class="dialog-btn">{{$t('shelf.confirm')}}</div></slot></div>
</div>
  • 父组件 src/components/shelf/ShelfGroupDialog.vue
<ebook-dialog:title="title"ref="dialog"><!-- 这里是插槽一的内容,省略了,没粘进来 --><!-- 下方div有slot="btn"与子组件<slot name="btn">匹配 --><divslot="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.refs.scroll.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 : 0this.$nextTick(() => {// 等所有dom结构更新完后,在计算scroll的高度this.$refs.scroll.refresh()})}},

vue-create-api

  • 一个能够让Vue组件通过API方式调用的插件

    • 可以大幅度降低引用组件的代码
    • 传统父组件使用子组件,需要js中引入,components中声明,html中写入DOM
    • 使用此插件,可以很简单的简化
    • 此插件是在body下创建dom,与vue在#app下不同,通常全屏的消息提示框,选择框使用它
  • GitHub文档地址

  • 安装 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组件中的propstext: '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

参考链接-vue.js

  • 代码实例 src/components/shelf/ShelfGroupDialog.vue
<template v-for="(item, index) in categoryList"><divclass="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);
}
  • 更多关于@mixin
&符号
  • 作用:引用父元素
.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实现进度条效果

  • 成品展示

  • 代码实现
<inputclass="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,其余#abacaebackground-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%`},

常用js操作css方法

// 方法一
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消除空行

  • 代码举例 src/components/ebook/EbookSlideContents.vue
.slide-contents-book-progress {font-size: 0;.progress {font-size: px2rem(14);}.progress-text {font-size: px2rem(12);}
}

其他知识点

blob链接

  • 参考链接 简书-通俗一些 官网-MDN 维基百科-blob

业务代码:src/components/ebook/EbookReader.vue

// 解析电子书内容,获取封面,标题,作者等信息
parseBook () {// 获取coverthis.book.loaded.cover.then(cover => {this.book.archive.createUrl(cover).then(url => {// url----blob:http://localhost:8080/ecf9934c-1313-4b3d-9647-caa7acafb152this.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.cfithis.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.csdn.net/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按键,调用取消的方法,移动端暂时不知道

[yishen] 小慕读书web端学习笔记相关推荐

  1. 小慕读书后台管理平台笔记

    setTimeout用法: 错误用法: 正确用法:

  2. web前端学习笔记(最新)

    web前端学习笔记 大家好,我是链表哥,新的学期,新的学习,我会为您展示我的学习进程. 一:什么是WEB前端? 所谓的Web前端指的是用户所能接触到的,并服务于用户的前沿端口,经我们程序员编辑修饰后展 ...

  3. vue3小兔鲜商城项目学习笔记+资料分享06

    建议大家先去看我第一篇小兔鲜的文章,强烈建议,非常建议,十分建议,从头开始看更完整. 最近正在学习vue3小兔鲜 下面是学习笔记 购物车模块 购物车功能分析 [外链图片转存失败,源站可能有防盗链机制, ...

  4. Java web与web gis学习笔记(二)——百度地图API调用

    系列链接: Java web与web gis学习笔记(一)--Tomcat环境搭建 Java web与web gis学习笔记(二)--百度地图API调用 JavaWeb和WebGIS学习笔记(三)-- ...

  5. java webpack web项目_官方出品,微信小程序和 Web 端同构解决方案——kbone

    介绍 最近在琢磨一些小程序开发和移动web开发,偶然间在Github上看到了这样一个项目--kbone,一个致力于微信小程序和 Web 端同构的解决方案.微信小程序的底层模型和 Web 端不同,我们想 ...

  6. 【Python-pywt】 小波变化库—Pywavelets 学习笔记

    (转载) [Python ]小波变化库--Pywavelets 学习笔记_nanbei2463776506的博客-CSDN博客 https://blog.csdn.net/nanbei24637765 ...

  7. Web Components 学习笔记一: Web Components是什么?解决了什么问题?

    公众号:妙蛙种子前端 文章原文地址:Web Components笔记一: Web Components是什么?解决了什么问题? | 妙蛙种子 - 记录WEB前端技术学习成长过程的博客 Web Comp ...

  8. vue3小兔鲜商城项目学习笔记+资料分享08

    建议大家先去看我第一篇小兔鲜的文章,强烈建议,非常建议,十分建议,从头开始看更完整. 最近正在学习vue3小兔鲜 下面是学习笔记 支付模块 路由和组件 任务目标: 完成支付页路由和组件 [外链图片转存 ...

  9. web安全学习笔记--sql语句(sql注入基础上)

    一.基础知和表内操作语法 1.sql语句对大小写不敏感!!! SELECT - 从数据库表中获取数据:select * from (columns/tables/databases); UPDATE ...

最新文章

  1. ES6 对象的新功能与解构赋值介绍
  2. 从VS2008+QT4到VS2015+QT5迁移过程中遇到的问题及解决方法
  3. 链表学习(一)静态链表的构造
  4. LNMP架构环境搭建之PHP、Nginx源码编译安装及其简单配置应用
  5. python_45_目录编程
  6. python xlrd xlwt pandas 模块 区别_python如何读写excel文件|python教程|python入门|python教程...
  7. 过磅系统更换服务器,无人值守过磅系统改造方案
  8. Altium Designer之Preferences
  9. 容器编排技术 -- AWS EC2快速入门
  10. 解密初、中、高级程序员的进化之路(前端)
  11. Spring Security OAuth2.0_实现分布式认证授权_网关资源服务配置_Spring Security OAuth2.0认证授权---springcloud工作笔记152
  12. 拦截器RetryAndFollowUpInterceptor分析
  13. jquery的基本使用-入口函数
  14. 数字转换成货币类型的方法
  15. 【POCKET 51】用 pocket 51学51单片机 之四:pocket 51功能模块测试
  16. 高通 MSM8K bootloader 之四: ramdump
  17. 滴滴的大数据可视化效果
  18. 好用的 Windows 软件授权管理工具 - slmgr
  19. 去除迅雷右侧资源信息栏和迅雷广告,以及迅雷的速度限制修改
  20. Sublime插件安装与提高Verilog编写效率插件推荐

热门文章

  1. meo学习笔记3:并行与并发,线程与进程的区别
  2. Fusion APP-添加检查软件更新功能
  3. 数据类型及其表现形式
  4. 新手看过来:示波器的工作原理
  5. 虚拟机下 centos7 网络间歇性断网
  6. 手机app服务器端开发初期---工具选择
  7. 每日刷题之数独简单版 AcWing 1613
  8. Web scraper 爬虫傻瓜教程(不断更新中)
  9. border设置1px看起来很粗问题
  10. Keil出现Error:Flash Download failed - Could not load file