[yishen] 小慕读书web端学习笔记

课程常用链接

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

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

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

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

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

阅读器原理课程-免费课

概览

知识点脑图

[yishen] 小慕读书web端学习笔记_第1张图片

阅读器工作原理

  • epubjs文档

[yishen] 小慕读书web端学习笔记_第2张图片

环境搭建

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文件, 到/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 [email protected]
    

web字体引入

谷歌字体api
  • 谷歌字体api https://developers.google.com/fonts/docs/css2

使用方法

[yishen] 小慕读书web端学习笔记_第3张图片

[三方汉化]谷歌字体api
  • 谷歌中文字体api(第三方汉化):地址

[yishen] 小慕读书web端学习笔记_第4张图片

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; 才可以访问目录

[yishen] 小慕读书web端学习笔记_第5张图片

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-跨域问题

image-20200720160110196

什么是跨域:链接

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中设置

    [yishen] 小慕读书web端学习笔记_第6张图片

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种

[yishen] 小慕读书web端学习笔记_第7张图片

transition
  • 代码实现

  • html部分。src/Ebook.vue,外围使用包裹,带上name属性,被包裹的部分要有vshow或者vif

    
      
          
  
* 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">
    
      <div
        class="shelf-list-item"
        v-for="(item, index) in shelfList"
        :key="index"
      >
        ......
      div>
    transition-group>
  div>
template>

[yishen] 小慕读书web端学习笔记_第8张图片

  • :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> transition-group>

只是把:key由原先的index修改为了item.id,我也不知道什么原因

[yishen] 小慕读书web端学习笔记_第9张图片

[yishen] 小慕读书web端学习笔记_第10张图片

官网中关于key的说明:内部元素总是需要提供唯一的 key attribute 值 index不唯一吗?

搜到了一个比较靠谱的答案:

交换位置后(对应到我的项目,就是删除某个),元素的key发生了变化

解决:给key值设置一个不会因为位置变化而变化的值

vue中的mixin混入

  • 分发组件中的可复用功能

代码举例:

  • 定义混入对象
import {
      mapGetters } from 'vuex'
export const ebookMixin = {
     
  computed: {
     
    ...mapGetters(['fileName', 'menuVisible'])
  }
}

  • 使用混入对象

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'
    ])
  }
}

具体页面内使用


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>

[yishen] 小慕读书web端学习笔记_第11张图片

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

  • component,动态组件
  • 代码示例 src/components/ebook/EbookSlide.vue


事件修饰符

.passive
  • 参考链接-vuejs官网

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

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

<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>

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

  • 父组件代码 src/components/ebook/EbookSlideContents.vue
<template>
    <Scroll
      class="slide-search-list"
      :top="66"
      :bottom="49"
      v-show="searchVisible"
    >
      
      <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>
  • 父组件 src/components/shelf/ShelfGroupDialog.vue
<ebook-dialog
    :title="title"
    ref="dialog"
  >
    
  	
    <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下不同,通常全屏的消息提示框,选择框使用它
    • [yishen] 小慕读书web端学习笔记_第12张图片
  • 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

// 其他组件的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

参考链接-vue.js

  • 代码实例 src/components/shelf/ShelfGroupDialog.vue
<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的其他用法

标题起的可能不合适

[yishen] 小慕读书web端学习笔记_第13张图片

  • 还可以做居中效果

    @mixin absCenter {
      position: absolute;
      top: 0;
      bottom: 0;
      left: 0;
      right: 0;
      margin: auto;
    }
    
  • 效果演示

[yishen] 小慕读书web端学习笔记_第14张图片

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原理及常用方法

原理图
  • 文档地址

[yishen] 小慕读书web端学习笔记_第15张图片

常用方法

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实现进度条效果

  • 成品展示

image-20200528093323806

  • 代码实现
<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)
}
  • 可在以下位置查看缓存,可以手动修改

[yishen] 小慕读书web端学习笔记_第16张图片

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);
  }
}

[yishen] 小慕读书web端学习笔记_第17张图片

其他知识点

blob链接

  • 参考链接 简书-通俗一些 官网-MDN 维基百科-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

[yishen] 小慕读书web端学习笔记_第18张图片

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

构建过程中警告处理
文件大小超出

[yishen] 小慕读书web端学习笔记_第19张图片

// vue.config.js文件内,与devServer同级,扩大资源限制到512 * 1024 (根据自己的项目来)
configureWebpack: {
    performance: {
      hints: 'warning',
      maxAssetSize: 524288,
      maxEntrypointSize: 524288
    }
  }
  • 将引用的一些库,替换成线上cdn版本

[yishen] 小慕读书web端学习笔记_第20张图片

例如:将本项目中占用资源最大的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
      • [yishen] 小慕读书web端学习笔记_第21张图片
    • .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表状态字符

[yishen] 小慕读书web端学习笔记_第22张图片
字符状态

A: 工作区新增的文件

C: 文件的一个新拷贝

D: 你本地删除的文件,服务器上还在

M: 文件的内容或者mode被修改

R: 文件名被修改了

T: 文件的类型被修改了

U: 文件没有被合并,需要完成合并才能进行提交

X: 未知状态

颜色状态

绿色:新增文件

黑色:别删除文件

蓝色:之前已经存在,被修改文件

红色:新增,但是没有增加到git中

为git设置代理

  • 修改 ~/.gitconfig 文件

[yishen] 小慕读书web端学习笔记_第23张图片

  • 注意:ssr走的是socks5

参考链接 参考链接

已知的BUG&优化

已知的BUG

  • 当使用主题时,章节翻页时,会闪一下白色(老师的也会闪一下,但底色与主题颜色相同-背景色修改为了灰色不是白色不那么显眼-算是解决了吧)
  • 点击屏幕中央,呼出上下菜单栏,会出现滚动条(老师的不会)(猜测:动画过渡导致页面宽高发生变化)–已解决

[yishen] 小慕读书web端学习笔记_第24张图片

// 解决办法,隐藏进度条
html::-webkit-scrollbar{
     
  width:0px
}
  • 图书主题切换,短时间切换过多次,会没反应(主题已经调过来了,前端没有渲染)
  • 切换不同的菜单选项(下面的)没有过度动画**—已解决**
  • 点击目录蒙版区域隐藏目录,下面的菜单条没有被隐藏**—已解决**
  • 目录跳转有问题**—已解决**
  • 切换字体,有时会没有效果
  • 清完缓存,第一次进入,字体大小会没效果(缓存中有)

[yishen] 小慕读书web端学习笔记_第25张图片

​ 原因:第一次初始化,这两个值没有定义(刷新一下就有了,定位不到出错位置)

image-20200608123139318

  • 上下章,能跳转的比目录上的多,有的图书会导致章节的选中效果选择了错误的章节**—严重性BUG**
    • 应该验证章节跳转的逻辑,是依据什么进行跳转的(猜测是section的值有问题)
    • this.navigation有问题

[yishen] 小慕读书web端学习笔记_第26张图片

  • Scroll组件没有滚动条,当内容有很多屏时,不知道滑到哪里了(对 目录非常多的书 不友好)

    • 理想效果:高度小于两屏时,隐藏滚动条,高于两屏时显示滚动条
    • src/components/common/Scroll.vue----webkit-scrollbar
  • 高度异常:很多地方都高度异常,可能是scroll组件问题 http://localhost:8080/#/store/home,滚动部分的高度没有变化,初试时,mounted中调用了refresh重新计算高度,没有问题,后面高度没有变化了

[yishen] 小慕读书web端学习笔记_第27张图片

  • toast组件还是有问题,当切换页面时,toast不会自动消失

    • 例如:删除分组时
  • store/detail页面底部加入书架:已解决

    假设一本书已经在书架中,进入图书详情页detail底部的加入书架会变成已加入书架

    初次进入底部的加入书架,会变成 已加入书架,但刷新一下,就变成了 加入书架

    computed中inBookShelf变量的问题

    解决方法:刷新进入bookdatail页面是,shelfList中值为空,重新获取即可

可以优化的地方

  • http://localhost:8080/#/store/shelf页面

    • [yishen] 小慕读书web端学习笔记_第28张图片

    • pc端可以监听esc按键,调用取消的方法,移动端暂时不知道

你可能感兴趣的:(前端相关,vue,epub,node.js,vue.js,es6)