├── .browserslistrc ├── docs ├── favicon.ico ├── img │ └── harry.5b15f1e0.png ├── fonts │ ├── element-icons.732389de.ttf │ └── element-icons.535877f5.woff ├── index.html ├── css │ └── chunk-vendors.d7b521e7.css └── js │ └── app.fd4e1ef4.js ├── .prettierrc.json ├── public ├── favicon.ico └── index.html ├── src ├── assets │ └── harry.png ├── hooks │ ├── index.ts │ ├── use-async.ts │ └── use-pages.ts ├── types │ └── index.ts ├── shims-vue.d.ts ├── context │ ├── index.ts │ └── books.ts ├── styles │ ├── element-variables.scss │ ├── colors.ts │ └── global.css ├── hacks │ ├── fetch │ │ └── index.ts │ └── data │ │ └── books.ts ├── shims-tsx.d.ts ├── main.ts ├── router │ └── index.ts ├── views │ ├── FinishedBooks.vue │ └── AllBooks.vue ├── components │ ├── StatusButtons.vue │ ├── Books.vue │ └── Book.vue └── App.vue ├── jest.config.js ├── vue.config.js ├── babel.config.js ├── .gitignore ├── tests └── unit │ └── example.spec.ts ├── .eslintrc.js ├── tsconfig.json ├── package.json └── README.md /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sl1673495/vue-bookshelf/HEAD/docs/favicon.ico -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": false, 4 | "tabWidth": 2 5 | } 6 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sl1673495/vue-bookshelf/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/assets/harry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sl1673495/vue-bookshelf/HEAD/src/assets/harry.png -------------------------------------------------------------------------------- /docs/img/harry.5b15f1e0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sl1673495/vue-bookshelf/HEAD/docs/img/harry.5b15f1e0.png -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: "@vue/cli-plugin-unit-jest/presets/typescript-and-babel" 3 | } 4 | -------------------------------------------------------------------------------- /docs/fonts/element-icons.732389de.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sl1673495/vue-bookshelf/HEAD/docs/fonts/element-icons.732389de.ttf -------------------------------------------------------------------------------- /docs/fonts/element-icons.535877f5.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sl1673495/vue-bookshelf/HEAD/docs/fonts/element-icons.535877f5.woff -------------------------------------------------------------------------------- /src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | import { useAsync } from "./use-async" 2 | import { usePages } from "./use-pages" 3 | 4 | export { useAsync, usePages } 5 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | import books from "@/hacks/data/books" 2 | 3 | export type Books = Book[] 4 | export type Book = typeof books[number] 5 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | outputDir: "docs", 3 | publicPath: process.env.NODE_ENV === "production" ? "/vue-books" : "" 4 | } 5 | -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.vue" { 2 | import Vue from "vue" 3 | export default Vue 4 | } 5 | 6 | declare module "*.json" 7 | 8 | declare module "*.jepg" 9 | -------------------------------------------------------------------------------- /src/context/index.ts: -------------------------------------------------------------------------------- 1 | import { useBookListProvide, useBookListInject } from "./books" 2 | 3 | export { useBookListInject } 4 | 5 | export const useProvider = () => { 6 | useBookListProvide() 7 | } 8 | -------------------------------------------------------------------------------- /src/styles/element-variables.scss: -------------------------------------------------------------------------------- 1 | /* 改变主题色变量 */ 2 | $--color-primary: #42b983; 3 | 4 | /* 改变 icon 字体路径变量,必需 */ 5 | $--font-path: '~element-ui/lib/theme-chalk/fonts'; 6 | 7 | @import "~element-ui/packages/theme-chalk/src/index"; -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ["@vue/cli-plugin-babel/preset"], 3 | plugins: [ 4 | [ 5 | "component", 6 | { 7 | libraryName: "element-ui", 8 | styleLibraryName: "theme-chalk" 9 | } 10 | ] 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /src/hacks/fetch/index.ts: -------------------------------------------------------------------------------- 1 | import books from "@/hacks/data/books" 2 | 3 | const mockBooks = books 4 | const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)) 5 | 6 | export const getBooks = async () => { 7 | await wait(1000) 8 | return mockBooks 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /src/shims-tsx.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from "vue" 2 | 3 | declare global { 4 | namespace JSX { 5 | // tslint:disable no-empty-interface 6 | interface Element extends VNode {} 7 | // tslint:disable no-empty-interface 8 | interface ElementClass extends Vue {} 9 | interface IntrinsicElements { 10 | [elem: string]: any 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tests/unit/example.spec.ts: -------------------------------------------------------------------------------- 1 | import { shallowMount } from "@vue/test-utils" 2 | import HelloWorld from "@/components/HelloWorld.vue" 3 | 4 | describe("HelloWorld.vue", () => { 5 | it("renders props.msg when passed", () => { 6 | const msg = "new message" 7 | const wrapper = shallowMount(HelloWorld, { 8 | propsData: { msg } 9 | }) 10 | expect(wrapper.text()).toMatch(msg) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /src/hooks/use-async.ts: -------------------------------------------------------------------------------- 1 | import { ref, onMounted } from "@vue/composition-api" 2 | 3 | export const useAsync = (func: () => Promise) => { 4 | const loading = ref(false) 5 | 6 | onMounted(async () => { 7 | try { 8 | loading.value = true 9 | await func() 10 | } catch (error) { 11 | throw error 12 | } finally { 13 | loading.value = false 14 | } 15 | }) 16 | 17 | return loading 18 | } 19 | -------------------------------------------------------------------------------- /src/styles/colors.ts: -------------------------------------------------------------------------------- 1 | export const base = "white" 2 | export const text = "#434449" 3 | export const gray = "#f1f2f7" 4 | export const gray10 = "#f1f1f4" 5 | export const gray20 = "#e4e5e9" 6 | export const gray80 = "#6f7077" 7 | export const indigo = "#3f51b5" 8 | export const indigoDarken10 = "#364495" 9 | export const indigoLighten80 = "#b7c1f8" 10 | export const yellow = "#ffc107" 11 | export const green = "#4caf50" 12 | export const danger = "#ef5350" 13 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | vue-bookshelf 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue" 2 | 3 | import VueCompositionApi from "@vue/composition-api" 4 | import { Pagination } from "element-ui" 5 | import "@/styles/element-variables.scss" 6 | import { useProvider } from "@/context" 7 | 8 | import App from "./App.vue" 9 | import router from "./router" 10 | import * as colors from "./styles/colors" 11 | 12 | Vue.use(VueCompositionApi) 13 | 14 | Vue.component(Pagination.name, Pagination) 15 | 16 | Vue.config.productionTip = false 17 | 18 | Vue.prototype.colors = colors 19 | 20 | new Vue({ 21 | router, 22 | setup() { 23 | useProvider() 24 | return {} 25 | }, 26 | render: h => h(App) 27 | }).$mount("#app") 28 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | extends: ["plugin:vue/essential", "@vue/prettier", "@vue/typescript"], 7 | rules: { 8 | "no-console": process.env.NODE_ENV === "production" ? "error" : "off", 9 | "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off", 10 | "no-extra-semi": 2 //禁止多余的冒号 11 | }, 12 | parserOptions: { 13 | parser: "@typescript-eslint/parser" 14 | }, 15 | overrides: [ 16 | { 17 | files: [ 18 | "**/__tests__/*.{j,t}s?(x)", 19 | "**/tests/unit/**/*.spec.{j,t}s?(x)" 20 | ], 21 | env: { 22 | jest: true 23 | } 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /src/router/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue" 2 | import VueRouter from "vue-router" 3 | import AllBooks from "@/views/AllBooks.vue" 4 | import FinishedBooks from "@/views/FinishedBooks.vue" 5 | 6 | Vue.use(VueRouter) 7 | 8 | export const routeMaps = { 9 | all: { 10 | name: "all", 11 | path: "/" 12 | }, 13 | finished: { 14 | name: "finished", 15 | path: "/finished" 16 | } 17 | } 18 | 19 | const routes = [ 20 | { 21 | ...routeMaps.all, 22 | component: AllBooks 23 | }, 24 | { 25 | ...routeMaps.finished, 26 | component: FinishedBooks 27 | } 28 | ] 29 | 30 | // @ts-ignore 31 | const router = new VueRouter({ 32 | routes 33 | }) 34 | 35 | export default router 36 | -------------------------------------------------------------------------------- /src/views/FinishedBooks.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 26 | 27 | 33 | -------------------------------------------------------------------------------- /src/views/AllBooks.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 29 | 30 | 36 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | vue-bookshelf
-------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "importHelpers": true, 8 | "moduleResolution": "node", 9 | "experimentalDecorators": true, 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "resolveJsonModule": true, 13 | "sourceMap": true, 14 | "baseUrl": ".", 15 | "types": [ 16 | "webpack-env", 17 | "jest" 18 | ], 19 | "paths": { 20 | "@/*": [ 21 | "src/*" 22 | ] 23 | }, 24 | "lib": [ 25 | "esnext", 26 | "dom", 27 | "dom.iterable", 28 | "scripthost" 29 | ] 30 | }, 31 | "include": [ 32 | "src/**/*.ts", 33 | "src/**/*.tsx", 34 | "src/**/*.vue", 35 | "src/**/*.png", 36 | "tests/**/*.ts", 37 | "tests/**/*.tsx" 38 | ], 39 | "exclude": [ 40 | "node_modules" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /src/hooks/use-pages.ts: -------------------------------------------------------------------------------- 1 | import { watch, ref, reactive } from "@vue/composition-api" 2 | 3 | export interface PageOption { 4 | pageSize?: number 5 | } 6 | 7 | export function usePages(watchCallback: () => T[], pageOption?: PageOption) { 8 | const { pageSize = 10 } = pageOption || {} 9 | 10 | const rawData = ref([]) 11 | const data = ref([]) 12 | 13 | // 提供给el-pagination组件的参数 14 | const bindings = reactive({ 15 | current: 1, 16 | currentChange: (currnetPage: number) => { 17 | data.value = sliceData(rawData.value, currnetPage) 18 | } 19 | }) 20 | 21 | // 根据页数切分数据 22 | const sliceData = (rawData: T[], currentPage: number) => { 23 | return rawData.slice((currentPage - 1) * pageSize, currentPage * pageSize) 24 | } 25 | 26 | watch(watchCallback, values => { 27 | rawData.value = values 28 | bindings.currentChange(1) 29 | }) 30 | 31 | return { 32 | data, 33 | bindings 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/styles/global.css: -------------------------------------------------------------------------------- 1 | #app { 2 | color: #434449; 3 | margin: 0 auto; 4 | display: flex; 5 | flex-direction: column; 6 | align-items: center; 7 | justify-content: center; 8 | } 9 | 10 | [data-reach-dialog-content] { 11 | max-width: 450px; 12 | border-radius: 3px; 13 | padding-bottom: 3.5em; 14 | box-shadow: 0 10px 30px -5px rgba(0, 0, 0, 0.2); 15 | margin: 20vh auto; 16 | } 17 | @media (max-width: 991px) { 18 | [data-reach-dialog-content] { 19 | width: 100%; 20 | margin: 10vh auto; 21 | } 22 | } 23 | 24 | #app { 25 | color: #434449; 26 | margin: 0 auto; 27 | display: flex; 28 | flex-direction: column; 29 | align-items: center; 30 | justify-content: center; 31 | } 32 | 33 | textarea, 34 | input { 35 | border: 1px solid #f1f1f4; 36 | background: #f1f2f7; 37 | padding: 8px 12px; 38 | } 39 | 40 | button, 41 | input { 42 | border-radius: 3px; 43 | } 44 | 45 | .visually-hidden { 46 | border: 0; 47 | clip: rect(0 0 0 0); 48 | height: 1px; 49 | margin: -1px; 50 | overflow: hidden; 51 | padding: 0; 52 | position: absolute; 53 | width: 1px; 54 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-bookshelf", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "test:unit": "vue-cli-service test:unit", 9 | "lint": "vue-cli-service lint" 10 | }, 11 | "dependencies": { 12 | "@vue/composition-api": "^0.3.4", 13 | "core-js": "^3.4.3", 14 | "element-ui": "^2.13.1", 15 | "vue": "^2.6.10", 16 | "vue-class-component": "^7.0.2", 17 | "vue-property-decorator": "^8.3.0", 18 | "vue-router": "^3.1.3", 19 | "vuex": "^3.1.2" 20 | }, 21 | "devDependencies": { 22 | "@types/jest": "^24.0.19", 23 | "@vue/cli-plugin-babel": "^4.1.0", 24 | "@vue/cli-plugin-eslint": "^4.1.0", 25 | "@vue/cli-plugin-router": "^4.1.0", 26 | "@vue/cli-plugin-typescript": "^4.1.0", 27 | "@vue/cli-plugin-unit-jest": "^4.1.0", 28 | "@vue/cli-plugin-vuex": "^4.1.0", 29 | "@vue/cli-service": "^4.1.0", 30 | "@vue/eslint-config-prettier": "^5.0.0", 31 | "@vue/eslint-config-typescript": "^4.0.0", 32 | "@vue/test-utils": "1.0.0-beta.29", 33 | "babel-plugin-component": "^1.1.1", 34 | "eslint": "^5.16.0", 35 | "eslint-plugin-prettier": "^3.1.1", 36 | "eslint-plugin-vue": "^5.0.0", 37 | "node-sass": "^4.12.0", 38 | "prettier": "^1.19.1", 39 | "sass-loader": "^8.0.0", 40 | "typescript": "~3.5.3", 41 | "vue-template-compiler": "^2.6.10" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/components/StatusButtons.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 44 | 45 | 63 | -------------------------------------------------------------------------------- /src/components/Books.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 55 | 56 | 66 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 37 | 38 | 62 | -------------------------------------------------------------------------------- /src/context/books.ts: -------------------------------------------------------------------------------- 1 | import { provide, inject, computed, ref, Ref } from "@vue/composition-api" 2 | import { Book, Books } from "@/types" 3 | 4 | type BookContext = { 5 | books: Ref 6 | setBooks: (value: Books) => void 7 | finishedBooks: Ref 8 | addFinishedBooks: (book: Book) => void 9 | removeFinishedBooks: (book: Book) => void 10 | hasReadedBook: (book: Book) => boolean 11 | booksAvaluable: Ref 12 | } 13 | 14 | const BookSymbol = Symbol() 15 | 16 | export const useBookListProvide = () => { 17 | // 全部图书 18 | const books = ref([]) 19 | const setBooks = (value: Books) => (books.value = value) 20 | 21 | // 已完成图书 22 | const finishedBooks = ref([]) 23 | const addFinishedBooks = (book: Book) => { 24 | if (!finishedBooks.value.find(({ id }) => id === book.id)) { 25 | finishedBooks.value.push(book) 26 | } 27 | } 28 | const removeFinishedBooks = (book: Book) => { 29 | const removeIndex = finishedBooks.value.findIndex( 30 | ({ id }) => id === book.id 31 | ) 32 | if (removeIndex !== -1) { 33 | finishedBooks.value.splice(removeIndex, 1) 34 | } 35 | } 36 | 37 | // 可选图书 38 | const booksAvaluable = computed(() => { 39 | return books.value.filter( 40 | book => !finishedBooks.value.find(({ id }) => id === book.id) 41 | ) 42 | }) 43 | 44 | // 是否已阅 45 | const hasReadedBook = (book: Book) => finishedBooks.value.includes(book) 46 | 47 | provide(BookSymbol, { 48 | books, 49 | setBooks, 50 | finishedBooks, 51 | addFinishedBooks, 52 | removeFinishedBooks, 53 | hasReadedBook, 54 | booksAvaluable 55 | }) 56 | } 57 | 58 | export const useBookListInject = () => { 59 | const booksContext = inject(BookSymbol) 60 | 61 | if (!booksContext) { 62 | throw new Error(`useBookListInject must be used after useBookListProvide`) 63 | } 64 | 65 | return booksContext 66 | } 67 | -------------------------------------------------------------------------------- /src/components/Book.vue: -------------------------------------------------------------------------------- 1 | 66 | 67 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-bookshelf 2 | 使用vue-composition-api + TypeScript 实现小型图书应用。 3 | 4 | ## Project setup 5 | ``` 6 | npm install 7 | ``` 8 | 9 | ### Compiles and hot-reloads for development 10 | ``` 11 | npm run serve 12 | ``` 13 | 14 | ### Compiles and minifies for production 15 | ``` 16 | npm run build 17 | ``` 18 | 19 | ## 前言 20 | Vue3的热度还没过去,React Hook在社区的发展也是如火如荼。 21 | 22 | 一时间大家都觉得Redux很low,都在研究各种各样配合hook实现的新形状态管理模式。 23 | 24 | 在React社区中,Context + useReducer的新型状态管理模式广受好评。 25 | 26 | 这篇文章就从Vue3的角度出发,探索一下未来的Vue状态管理模式。 27 | 28 | vue-composition-api-rfc: 29 | https://vue-composition-api-rfc.netlify.com/api.html 30 | 31 | vue官方提供的尝鲜库: 32 | https://github.com/vuejs/composition-api 33 | 34 | 35 | ## api 36 | Vue3中有一对新增的api,`provide`和`inject`,熟悉Vue2的朋友应该明白, 37 | 38 | 在上层组件通过provide提供一些变量,在子组件中可以通过inject来拿到,但是必须在组件的对象里面声明,使用场景的也很少,所以之前我也并没有往状态管理的方向去想。 39 | 40 | 但是Vue3中新增了Hook,而Hook的特征之一就是可以在组件外去写一些自定义Hook,所以我们不光可以在.vue组件内部使用Vue的能力, 41 | 在任意的文件下(如context.ts)下也可以, 42 | 43 | 如果我们在context.ts中 44 | 1. 自定义并export一个hook叫`useProvide`,并且在这个hook中使用provide并且注册一些全局状态, 45 | 46 | 2. 再自定义并export一个hook叫`useInject`,并且在这个hook中使用inject返回刚刚provide的全局状态, 47 | 48 | 3. 然后在根组件的setup函数中调用`useProvide`。 49 | 50 | 4. 就可以在任意的子组件去共享这些全局状态了。 51 | 52 | 顺着这个思路,先看一下这两个api的介绍,然后一起慢慢探索这对api。 53 | 54 | ```js 55 | import { provide, inject } from 'vue' 56 | 57 | const ThemeSymbol = Symbol() 58 | 59 | const Ancestor = { 60 | setup() { 61 | provide(ThemeSymbol, 'dark') 62 | } 63 | } 64 | 65 | const Descendent = { 66 | setup() { 67 | const theme = inject(ThemeSymbol, 'light' /* optional default value */) 68 | return { 69 | theme 70 | } 71 | } 72 | } 73 | ``` 74 | 75 | ## 开始 76 | 77 | ### 项目介绍 78 | 这个项目是一个简单的图书管理应用,功能很简单: 79 | 1. 查看图书 80 | 2. 增加已阅图书 81 | 3. 删除已阅图书 82 | 83 | ### 项目搭建 84 | 首先使用vue-cli搭建一个项目,在选择依赖的时候手动选择,这个项目中我使用了TypeScript,各位小伙伴可以按需选择。 85 | 86 | 然后引入官方提供的vue-composition-api库,并且在main.ts里注册。 87 | ```ts 88 | import VueCompositionApi from '@vue/composition-api'; 89 | Vue.use(VueCompositionApi); 90 | ``` 91 | 92 | ### context编写 93 | 94 | 按照刚刚的思路,我建立了src/context/books.ts 95 | 96 | ```ts 97 | import { provide, inject, computed, ref, Ref } from '@vue/composition-api'; 98 | import { Book, Books } from '@/types'; 99 | 100 | type BookContext = { 101 | books: Ref; 102 | setBooks: (value: Books) => void; 103 | }; 104 | 105 | const BookSymbol = Symbol(); 106 | 107 | export const useBookListProvide = () => { 108 | // 全部图书 109 | const books = ref([]); 110 | const setBooks = (value: Books) => (books.value = value); 111 | 112 | provide(BookSymbol, { 113 | books, 114 | setBooks, 115 | }); 116 | }; 117 | 118 | export const useBookListInject = () => { 119 | const booksContext = inject(BookSymbol); 120 | 121 | if (!booksContext) { 122 | throw new Error(`useBookListInject must be used after useBookListProvide`); 123 | } 124 | 125 | return booksContext; 126 | }; 127 | 128 | ``` 129 | 130 | 全局状态肯定不止一个模块,所以在context/index.ts下做统一的导出 131 | ```ts 132 | import { useBookListProvide, useBookListInject } from './books'; 133 | 134 | export { useBookListInject }; 135 | 136 | export const useProvider = () => { 137 | useBookListProvide(); 138 | }; 139 | ``` 140 | 后续如果增加模块的话,就按照这个套路就好。 141 | 142 | 然后在main.ts的根组件里使用provide,在最上层的组件中注入全局状态。 143 | ```ts 144 | new Vue({ 145 | router, 146 | setup() { 147 | useProvider(); 148 | return {}; 149 | }, 150 | render: h => h(App), 151 | }).$mount('#app'); 152 | ``` 153 | 154 | 在组件view/books.vue中使用: 155 | ```js 156 | 159 | 160 | 184 | ``` 185 | 186 | 这个页面需要初始化books的数据,并且从inject中拿到setBooks的方法并调用,之后这份books数据就可以供所有组件使用了。 187 | 188 | 在setup里引入了一个`useAsync`函数,我编写它的目的是为了管理异步方法前后的loading状态,看一下它的实现。 189 | 190 | ```ts 191 | import { ref, onMounted } from '@vue/composition-api'; 192 | 193 | export const useAsync = (func: () => Promise) => { 194 | const loading = ref(false); 195 | 196 | onMounted(async () => { 197 | try { 198 | loading.value = true; 199 | await func(); 200 | } catch (error) { 201 | throw error; 202 | } finally { 203 | loading.value = false; 204 | } 205 | }); 206 | 207 | return loading; 208 | }; 209 | ``` 210 | 211 | 可以看出,这个hook的作用就是把外部传入的异步方法`func`在`onMounted`生命周期里调用 212 | 并且在调用的前后改变响应式变量`loading`的值,并且把loading返回出去,这样loading就可以在模板中自由使用,从而让loading这个变量和页面的渲染关联起来。 213 | 214 | Vue3的hooks让我们可以在组件外部调用Vue的所有能力, 215 | 包括onMounted,ref, reactive等等, 216 | 217 | 这使得自定义hook可以做非常多的事情, 218 | 并且在组件的setup函数把多个自定义hook组合起来完成逻辑, 219 | 220 | 这恐怕也是起名叫composition-api的初衷。 221 | 222 | ### 最终的books模块context 223 | ```ts 224 | import { provide, inject, computed, ref, Ref } from '@vue/composition-api'; 225 | import { Book, Books } from '@/types'; 226 | 227 | type BookContext = { 228 | books: Ref; 229 | setBooks: (value: Books) => void; 230 | finishedBooks: Ref; 231 | addFinishedBooks: (book: Book) => void; 232 | booksAvaluable: Ref; 233 | }; 234 | 235 | const BookSymbol = Symbol(); 236 | 237 | export const useBookListProvide = () => { 238 | // 全部图书 239 | const books = ref([]); 240 | const setBooks = (value: Books) => (books.value = value); 241 | 242 | // 已完成图书 243 | const finishedBooks = ref([]); 244 | const addFinishedBooks = (book: Book) => { 245 | if (!finishedBooks.value.find(({ id }) => id === book.id)) { 246 | finishedBooks.value.push(book); 247 | } 248 | }; 249 | const removeFinishedBooks = (book: Book) => { 250 | const removeIndex = finishedBooks.value.findIndex(({ id }) => id === book.id); 251 | if (removeIndex !== -1) { 252 | finishedBooks.value.splice(removeIndex, 1); 253 | } 254 | }; 255 | 256 | // 可选图书 257 | const booksAvaluable = computed(() => { 258 | return books.value.filter(book => !finishedBooks.value.find(({ id }) => id === book.id)); 259 | }); 260 | 261 | provide(BookSymbol, { 262 | books, 263 | setBooks, 264 | finishedBooks, 265 | addFinishedBooks, 266 | removeFinishedBooks, 267 | booksAvaluable, 268 | }); 269 | }; 270 | 271 | export const useBookListInject = () => { 272 | const booksContext = inject(BookSymbol); 273 | 274 | if (!booksContext) { 275 | throw new Error(`useBookListInject must be used after useBookListProvide`); 276 | } 277 | 278 | return booksContext; 279 | }; 280 | ``` 281 | 282 | 最终的books模块就是这个样子了,可以看到在hooks的模式下, 283 | 284 | 代码不再按照state, mutation和actions区分,而是按照逻辑关注点分隔, 285 | 286 | 这样的好处显而易见,我们想要维护某一个功能的时候更加方便的能找到所有相关的逻辑,而不再是在选项和文件之间跳来跳去。 287 | 288 | ## 总结 289 | 本文相关的所有代码都放在 290 | 291 | https://github.com/sl1673495/vue-bookshelf 292 | 293 | 这个仓库里了,感兴趣的同学可以去看, 294 | 295 | 在之前刚看到composition-api,还有尤大对于Vue3的Hook和React的Hook的区别对比的时候,我对于Vue3的Hook甚至有了一些盲目的崇拜,但是真正使用下来发现,虽然不需要我们再去手动管理依赖项,但是由于Vue的响应式机制始终需要非原始的数据类型来保持响应式,所带来的一些心智负担也是需要注意和适应的。 296 | 297 | 举个简单的例子 298 | ```ts 299 | setup() { 300 | const loading = useAsync(async () => { 301 | await getBooks(); 302 | }); 303 | 304 | return { 305 | isLoading: !!loading.value 306 | } 307 | }, 308 | ``` 309 | 310 | 这一段看似符合直觉的代码,却会让`isLoading`这个变量失去响应式,但是这也是性能和内部实现设计的一些取舍,我们选择了Vue,也需要去学习和习惯它。 311 | 312 | 总体来说,Vue3虽然也有一些自己的缺点,但是带给我们React Hook几乎所有的好处,而且还规避了React Hook的一些让人难以理解坑,在某些方面还优于它,期待Vue3正式版的发布! -------------------------------------------------------------------------------- /docs/css/chunk-vendors.d7b521e7.css: -------------------------------------------------------------------------------- 1 | .el-popper .popper__arrow,.el-popper .popper__arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.el-popper .popper__arrow{border-width:6px;-webkit-filter:drop-shadow(0 2px 12px rgba(0,0,0,.03));filter:drop-shadow(0 2px 12px rgba(0,0,0,.03))}.el-popper .popper__arrow:after{content:" ";border-width:6px}.el-popper[x-placement^=top]{margin-bottom:12px}.el-popper[x-placement^=top] .popper__arrow{bottom:-6px;left:50%;margin-right:3px;border-top-color:#ebeef5;border-bottom-width:0}.el-popper[x-placement^=top] .popper__arrow:after{bottom:1px;margin-left:-6px;border-top-color:#fff;border-bottom-width:0}.el-popper[x-placement^=bottom]{margin-top:12px}.el-popper[x-placement^=bottom] .popper__arrow{top:-6px;left:50%;margin-right:3px;border-top-width:0;border-bottom-color:#ebeef5}.el-popper[x-placement^=bottom] .popper__arrow:after{top:1px;margin-left:-6px;border-top-width:0;border-bottom-color:#fff}.el-popper[x-placement^=right]{margin-left:12px}.el-popper[x-placement^=right] .popper__arrow{top:50%;left:-6px;margin-bottom:3px;border-right-color:#ebeef5;border-left-width:0}.el-popper[x-placement^=right] .popper__arrow:after{bottom:-6px;left:1px;border-right-color:#fff;border-left-width:0}.el-popper[x-placement^=left]{margin-right:12px}.el-popper[x-placement^=left] .popper__arrow{top:50%;right:-6px;margin-bottom:3px;border-right-width:0;border-left-color:#ebeef5}.el-popper[x-placement^=left] .popper__arrow:after{right:1px;bottom:-6px;margin-left:-6px;border-right-width:0;border-left-color:#fff}.el-select-dropdown{position:absolute;z-index:1001;border:1px solid #e4e7ed;border-radius:4px;background-color:#fff;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);-webkit-box-sizing:border-box;box-sizing:border-box;margin:5px 0}.el-select-dropdown.is-multiple .el-select-dropdown__item.selected{color:#409eff;background-color:#fff}.el-select-dropdown.is-multiple .el-select-dropdown__item.selected.hover{background-color:#f5f7fa}.el-select-dropdown.is-multiple .el-select-dropdown__item.selected:after{position:absolute;right:20px;font-family:element-icons;content:"\e6da";font-size:12px;font-weight:700;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.el-select-dropdown .el-scrollbar.is-empty .el-select-dropdown__list{padding:0}.el-select-dropdown__empty{padding:10px 0;margin:0;text-align:center;color:#999;font-size:14px}.el-select-dropdown__wrap{max-height:274px}.el-select-dropdown__list{list-style:none;padding:6px 0;margin:0;-webkit-box-sizing:border-box;box-sizing:border-box}.el-textarea{position:relative;display:inline-block;width:100%;vertical-align:bottom;font-size:14px}.el-textarea__inner{display:block;resize:vertical;padding:5px 15px;line-height:1.5;box-sizing:border-box;width:100%;font-size:inherit;color:#606266;background-color:#fff;background-image:none;border:1px solid #dcdfe6;border-radius:4px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1)}.el-input__inner,.el-tag,.el-textarea__inner{-webkit-box-sizing:border-box}.el-textarea__inner::-webkit-input-placeholder{color:#c0c4cc}.el-textarea__inner:-ms-input-placeholder{color:#c0c4cc}.el-textarea__inner::-ms-input-placeholder{color:#c0c4cc}.el-textarea__inner::-moz-placeholder{color:#c0c4cc}.el-textarea__inner::placeholder{color:#c0c4cc}.el-textarea__inner:hover{border-color:#c0c4cc}.el-textarea__inner:focus{outline:0;border-color:#409eff}.el-textarea .el-input__count{color:#909399;background:#fff;position:absolute;font-size:12px;bottom:5px;right:10px}.el-textarea.is-disabled .el-textarea__inner{background-color:#f5f7fa;border-color:#e4e7ed;color:#c0c4cc;cursor:not-allowed}.el-textarea.is-disabled .el-textarea__inner::-webkit-input-placeholder{color:#c0c4cc}.el-textarea.is-disabled .el-textarea__inner:-ms-input-placeholder{color:#c0c4cc}.el-textarea.is-disabled .el-textarea__inner::-ms-input-placeholder{color:#c0c4cc}.el-textarea.is-disabled .el-textarea__inner::-moz-placeholder{color:#c0c4cc}.el-textarea.is-disabled .el-textarea__inner::placeholder{color:#c0c4cc}.el-textarea.is-exceed .el-textarea__inner{border-color:#f56c6c}.el-textarea.is-exceed .el-input__count{color:#f56c6c}.el-input{position:relative;font-size:14px;display:inline-block;width:100%}.el-input::-webkit-scrollbar{z-index:11;width:6px}.el-input::-webkit-scrollbar:horizontal{height:6px}.el-input::-webkit-scrollbar-thumb{border-radius:5px;width:6px;background:#b4bccc}.el-input::-webkit-scrollbar-corner,.el-input::-webkit-scrollbar-track{background:#fff}.el-input::-webkit-scrollbar-track-piece{background:#fff;width:6px}.el-input .el-input__clear{color:#c0c4cc;font-size:14px;cursor:pointer;-webkit-transition:color .2s cubic-bezier(.645,.045,.355,1);transition:color .2s cubic-bezier(.645,.045,.355,1)}.el-input .el-input__clear:hover{color:#909399}.el-input .el-input__count{height:100%;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;color:#909399;font-size:12px}.el-input .el-input__count .el-input__count-inner{background:#fff;line-height:normal;display:inline-block;padding:0 5px}.el-input__inner{-webkit-appearance:none;background-color:#fff;background-image:none;border-radius:4px;border:1px solid #dcdfe6;-webkit-box-sizing:border-box;box-sizing:border-box;color:#606266;display:inline-block;font-size:inherit;height:40px;line-height:40px;outline:0;padding:0 15px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}.el-input__prefix,.el-input__suffix{position:absolute;top:0;-webkit-transition:all .3s;height:100%;color:#c0c4cc;text-align:center}.el-input__inner::-webkit-input-placeholder{color:#c0c4cc}.el-input__inner:-ms-input-placeholder{color:#c0c4cc}.el-input__inner::-ms-input-placeholder{color:#c0c4cc}.el-input__inner::-moz-placeholder{color:#c0c4cc}.el-input__inner::placeholder{color:#c0c4cc}.el-input__inner:hover{border-color:#c0c4cc}.el-input.is-active .el-input__inner,.el-input__inner:focus{border-color:#409eff;outline:0}.el-input__suffix{right:5px;-webkit-transition:all .3s;transition:all .3s;pointer-events:none}.el-input__suffix-inner{pointer-events:all}.el-input__prefix{left:5px}.el-input__icon,.el-input__prefix{-webkit-transition:all .3s;transition:all .3s}.el-input__icon{height:100%;width:25px;text-align:center;line-height:40px}.el-input__icon:after{content:"";height:100%;width:0;display:inline-block;vertical-align:middle}.el-input__validateIcon{pointer-events:none}.el-input.is-disabled .el-input__inner{background-color:#f5f7fa;border-color:#e4e7ed;color:#c0c4cc;cursor:not-allowed}.el-input.is-disabled .el-input__inner::-webkit-input-placeholder{color:#c0c4cc}.el-input.is-disabled .el-input__inner:-ms-input-placeholder{color:#c0c4cc}.el-input.is-disabled .el-input__inner::-ms-input-placeholder{color:#c0c4cc}.el-input.is-disabled .el-input__inner::-moz-placeholder{color:#c0c4cc}.el-input.is-disabled .el-input__inner::placeholder{color:#c0c4cc}.el-input.is-disabled .el-input__icon{cursor:not-allowed}.el-input.is-exceed .el-input__inner{border-color:#f56c6c}.el-input.is-exceed .el-input__suffix .el-input__count{color:#f56c6c}.el-input--suffix .el-input__inner{padding-right:30px}.el-input--prefix .el-input__inner{padding-left:30px}.el-input--medium{font-size:14px}.el-input--medium .el-input__inner{height:36px;line-height:36px}.el-input--medium .el-input__icon{line-height:36px}.el-input--small{font-size:13px}.el-input--small .el-input__inner{height:32px;line-height:32px}.el-input--small .el-input__icon{line-height:32px}.el-input--mini{font-size:12px}.el-input--mini .el-input__inner{height:28px;line-height:28px}.el-input--mini .el-input__icon{line-height:28px}.el-input-group{line-height:normal;display:inline-table;width:100%;border-collapse:separate;border-spacing:0}.el-input-group>.el-input__inner{vertical-align:middle;display:table-cell}.el-input-group__append,.el-input-group__prepend{background-color:#f5f7fa;color:#909399;vertical-align:middle;display:table-cell;position:relative;border:1px solid #dcdfe6;border-radius:4px;padding:0 20px;width:1px;white-space:nowrap}.el-input-group--prepend .el-input__inner,.el-input-group__append{border-top-left-radius:0;border-bottom-left-radius:0}.el-input-group--append .el-input__inner,.el-input-group__prepend{border-top-right-radius:0;border-bottom-right-radius:0}.el-input-group__append:focus,.el-input-group__prepend:focus{outline:0}.el-input-group__append .el-button,.el-input-group__append .el-select,.el-input-group__prepend .el-button,.el-input-group__prepend .el-select{display:inline-block;margin:-10px -20px}.el-input-group__append button.el-button,.el-input-group__append div.el-select .el-input__inner,.el-input-group__append div.el-select:hover .el-input__inner,.el-input-group__prepend button.el-button,.el-input-group__prepend div.el-select .el-input__inner,.el-input-group__prepend div.el-select:hover .el-input__inner{border-color:transparent;background-color:transparent;color:inherit;border-top:0;border-bottom:0}.el-input-group__append .el-button,.el-input-group__append .el-input,.el-input-group__prepend .el-button,.el-input-group__prepend .el-input{font-size:inherit}.el-input-group__prepend{border-right:0}.el-input-group__append{border-left:0}.el-input-group--append .el-select .el-input.is-focus .el-input__inner,.el-input-group--prepend .el-select .el-input.is-focus .el-input__inner{border-color:transparent}.el-input__inner::-ms-clear{display:none;width:0;height:0}.el-tag{background-color:#ecf5ff;border-color:#d9ecff;display:inline-block;height:32px;padding:0 10px;line-height:30px;font-size:12px;color:#409eff;border-width:1px;border-style:solid;border-radius:4px;-webkit-box-sizing:border-box;box-sizing:border-box;white-space:nowrap}.el-tag.is-hit{border-color:#409eff}.el-tag .el-tag__close{color:#409eff}.el-tag .el-tag__close:hover{color:#fff;background-color:#409eff}.el-tag.el-tag--info{background-color:#f4f4f5;border-color:#e9e9eb;color:#909399}.el-tag.el-tag--info.is-hit{border-color:#909399}.el-tag.el-tag--info .el-tag__close{color:#909399}.el-tag.el-tag--info .el-tag__close:hover{color:#fff;background-color:#909399}.el-tag.el-tag--success{background-color:#f0f9eb;border-color:#e1f3d8;color:#67c23a}.el-tag.el-tag--success.is-hit{border-color:#67c23a}.el-tag.el-tag--success .el-tag__close{color:#67c23a}.el-tag.el-tag--success .el-tag__close:hover{color:#fff;background-color:#67c23a}.el-tag.el-tag--warning{background-color:#fdf6ec;border-color:#faecd8;color:#e6a23c}.el-tag.el-tag--warning.is-hit{border-color:#e6a23c}.el-tag.el-tag--warning .el-tag__close{color:#e6a23c}.el-tag.el-tag--warning .el-tag__close:hover{color:#fff;background-color:#e6a23c}.el-tag.el-tag--danger{background-color:#fef0f0;border-color:#fde2e2;color:#f56c6c}.el-tag.el-tag--danger.is-hit{border-color:#f56c6c}.el-tag.el-tag--danger .el-tag__close{color:#f56c6c}.el-tag.el-tag--danger .el-tag__close:hover{color:#fff;background-color:#f56c6c}.el-tag .el-icon-close{border-radius:50%;text-align:center;position:relative;cursor:pointer;font-size:12px;height:16px;width:16px;line-height:16px;vertical-align:middle;top:-1px;right:-5px}.el-tag .el-icon-close:before{display:block}.el-tag--dark{background-color:#409eff;color:#fff}.el-tag--dark,.el-tag--dark.is-hit{border-color:#409eff}.el-tag--dark .el-tag__close{color:#fff}.el-tag--dark .el-tag__close:hover{color:#fff;background-color:#66b1ff}.el-tag--dark.el-tag--info{background-color:#909399;border-color:#909399;color:#fff}.el-tag--dark.el-tag--info.is-hit{border-color:#909399}.el-tag--dark.el-tag--info .el-tag__close{color:#fff}.el-tag--dark.el-tag--info .el-tag__close:hover{color:#fff;background-color:#a6a9ad}.el-tag--dark.el-tag--success{background-color:#67c23a;border-color:#67c23a;color:#fff}.el-tag--dark.el-tag--success.is-hit{border-color:#67c23a}.el-tag--dark.el-tag--success .el-tag__close{color:#fff}.el-tag--dark.el-tag--success .el-tag__close:hover{color:#fff;background-color:#85ce61}.el-tag--dark.el-tag--warning{background-color:#e6a23c;border-color:#e6a23c;color:#fff}.el-tag--dark.el-tag--warning.is-hit{border-color:#e6a23c}.el-tag--dark.el-tag--warning .el-tag__close{color:#fff}.el-tag--dark.el-tag--warning .el-tag__close:hover{color:#fff;background-color:#ebb563}.el-tag--dark.el-tag--danger{background-color:#f56c6c;border-color:#f56c6c;color:#fff}.el-tag--dark.el-tag--danger.is-hit{border-color:#f56c6c}.el-tag--dark.el-tag--danger .el-tag__close{color:#fff}.el-tag--dark.el-tag--danger .el-tag__close:hover{color:#fff;background-color:#f78989}.el-tag--plain{background-color:#fff;border-color:#b3d8ff;color:#409eff}.el-tag--plain.is-hit{border-color:#409eff}.el-tag--plain .el-tag__close{color:#409eff}.el-tag--plain .el-tag__close:hover{color:#fff;background-color:#409eff}.el-tag--plain.el-tag--info{background-color:#fff;border-color:#d3d4d6;color:#909399}.el-tag--plain.el-tag--info.is-hit{border-color:#909399}.el-tag--plain.el-tag--info .el-tag__close{color:#909399}.el-tag--plain.el-tag--info .el-tag__close:hover{color:#fff;background-color:#909399}.el-tag--plain.el-tag--success{background-color:#fff;border-color:#c2e7b0;color:#67c23a}.el-tag--plain.el-tag--success.is-hit{border-color:#67c23a}.el-tag--plain.el-tag--success .el-tag__close{color:#67c23a}.el-tag--plain.el-tag--success .el-tag__close:hover{color:#fff;background-color:#67c23a}.el-tag--plain.el-tag--warning{background-color:#fff;border-color:#f5dab1;color:#e6a23c}.el-tag--plain.el-tag--warning.is-hit{border-color:#e6a23c}.el-tag--plain.el-tag--warning .el-tag__close{color:#e6a23c}.el-tag--plain.el-tag--warning .el-tag__close:hover{color:#fff;background-color:#e6a23c}.el-tag--plain.el-tag--danger{background-color:#fff;border-color:#fbc4c4;color:#f56c6c}.el-tag--plain.el-tag--danger.is-hit{border-color:#f56c6c}.el-tag--plain.el-tag--danger .el-tag__close{color:#f56c6c}.el-tag--plain.el-tag--danger .el-tag__close:hover{color:#fff;background-color:#f56c6c}.el-tag--medium{height:28px;line-height:26px}.el-tag--medium .el-icon-close{-webkit-transform:scale(.8);transform:scale(.8)}.el-tag--small{height:24px;padding:0 8px;line-height:22px}.el-tag--small .el-icon-close{-webkit-transform:scale(.8);transform:scale(.8)}.el-tag--mini{height:20px;padding:0 5px;line-height:19px}.el-tag--mini .el-icon-close{margin-left:-3px;-webkit-transform:scale(.7);transform:scale(.7)}.el-select-dropdown__item{font-size:14px;padding:0 20px;position:relative;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#606266;height:34px;line-height:34px;-webkit-box-sizing:border-box;box-sizing:border-box;cursor:pointer}.el-select-dropdown__item.is-disabled{color:#c0c4cc;cursor:not-allowed}.el-select-dropdown__item.is-disabled:hover{background-color:#fff}.el-select-dropdown__item.hover,.el-select-dropdown__item:hover{background-color:#f5f7fa}.el-select-dropdown__item.selected{color:#409eff;font-weight:700}.el-select-group{margin:0;padding:0}.el-select-group__wrap{position:relative;list-style:none;margin:0;padding:0}.el-select-group__wrap:not(:last-of-type){padding-bottom:24px}.el-select-group__wrap:not(:last-of-type):after{content:"";position:absolute;display:block;left:20px;right:20px;bottom:12px;height:1px;background:#e4e7ed}.el-select-group__title{padding-left:20px;font-size:12px;color:#909399;line-height:30px}.el-select-group .el-select-dropdown__item{padding-left:20px}.el-scrollbar{overflow:hidden;position:relative}.el-scrollbar:active>.el-scrollbar__bar,.el-scrollbar:focus>.el-scrollbar__bar,.el-scrollbar:hover>.el-scrollbar__bar{opacity:1;-webkit-transition:opacity .34s ease-out;transition:opacity .34s ease-out}.el-scrollbar__wrap{overflow:scroll;height:100%}.el-scrollbar__wrap--hidden-default{scrollbar-width:none}.el-scrollbar__wrap--hidden-default::-webkit-scrollbar{width:0;height:0}.el-scrollbar__thumb{position:relative;display:block;width:0;height:0;cursor:pointer;border-radius:inherit;background-color:rgba(144,147,153,.3);-webkit-transition:background-color .3s;transition:background-color .3s}.el-scrollbar__thumb:hover{background-color:rgba(144,147,153,.5)}.el-scrollbar__bar{position:absolute;right:2px;bottom:2px;z-index:1;border-radius:4px;opacity:0;-webkit-transition:opacity .12s ease-out;transition:opacity .12s ease-out}.el-scrollbar__bar.is-vertical{width:6px;top:2px}.el-scrollbar__bar.is-vertical>div{width:100%}.el-scrollbar__bar.is-horizontal{height:6px;left:2px}.el-scrollbar__bar.is-horizontal>div{height:100%}.el-select{display:inline-block;position:relative}.el-select .el-select__tags>span{display:contents}.el-select:hover .el-input__inner{border-color:#c0c4cc}.el-select .el-input__inner{cursor:pointer;padding-right:35px}.el-select .el-input__inner:focus{border-color:#409eff}.el-select .el-input .el-select__caret{color:#c0c4cc;font-size:14px;-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;-webkit-transform:rotate(180deg);transform:rotate(180deg);cursor:pointer}.el-select .el-input .el-select__caret.is-reverse{-webkit-transform:rotate(0);transform:rotate(0)}.el-select .el-input .el-select__caret.is-show-close{font-size:14px;text-align:center;-webkit-transform:rotate(180deg);transform:rotate(180deg);border-radius:100%;color:#c0c4cc;-webkit-transition:color .2s cubic-bezier(.645,.045,.355,1);transition:color .2s cubic-bezier(.645,.045,.355,1)}.el-select .el-input .el-select__caret.is-show-close:hover{color:#909399}.el-select .el-input.is-disabled .el-input__inner{cursor:not-allowed}.el-select .el-input.is-disabled .el-input__inner:hover{border-color:#e4e7ed}.el-select .el-input.is-focus .el-input__inner{border-color:#409eff}.el-select>.el-input{display:block}.el-select__input{border:none;outline:0;padding:0;margin-left:15px;color:#666;font-size:14px;-webkit-appearance:none;-moz-appearance:none;appearance:none;height:28px;background-color:transparent}.el-select__input.is-mini{height:14px}.el-select__close{cursor:pointer;position:absolute;top:8px;z-index:1000;right:25px;color:#c0c4cc;line-height:18px;font-size:14px}.el-select__close:hover{color:#909399}.el-select__tags{position:absolute;line-height:normal;white-space:normal;z-index:1;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-wrap:wrap;flex-wrap:wrap}.el-select .el-tag__close{margin-top:-2px}.el-select .el-tag{-webkit-box-sizing:border-box;box-sizing:border-box;border-color:transparent;margin:2px 0 2px 6px;background-color:#f0f2f5}.el-select .el-tag__close.el-icon-close{background-color:#c0c4cc;right:-7px;top:0;color:#fff}.el-select .el-tag__close.el-icon-close:hover{background-color:#909399}.el-select .el-tag__close.el-icon-close:before{display:block;-webkit-transform:translateY(.5px);transform:translateY(.5px)}.el-pagination{white-space:nowrap;padding:2px 5px;color:#303133;font-weight:700}.el-pagination:after,.el-pagination:before{display:table;content:""}.el-pagination:after{clear:both}.el-pagination button,.el-pagination span:not([class*=suffix]){display:inline-block;font-size:13px;min-width:35.5px;height:28px;line-height:28px;vertical-align:top;-webkit-box-sizing:border-box;box-sizing:border-box}.el-pagination .el-input__inner{text-align:center;-moz-appearance:textfield;line-height:normal}.el-pagination .el-input__suffix{right:0;-webkit-transform:scale(.8);transform:scale(.8)}.el-pagination .el-select .el-input{width:100px;margin:0 5px}.el-pagination .el-select .el-input .el-input__inner{padding-right:25px;border-radius:3px}.el-pagination button{border:none;padding:0 6px;background:0 0}.el-pagination button:focus{outline:0}.el-pagination button:hover{color:#409eff}.el-pagination button:disabled{color:#c0c4cc;background-color:#fff;cursor:not-allowed}.el-pagination .btn-next,.el-pagination .btn-prev{background:50% no-repeat #fff;background-size:16px;cursor:pointer;margin:0;color:#303133}.el-pagination .btn-next .el-icon,.el-pagination .btn-prev .el-icon{display:block;font-size:12px;font-weight:700}.el-pagination .btn-prev{padding-right:12px}.el-pagination .btn-next{padding-left:12px}.el-pagination .el-pager li.disabled{color:#c0c4cc;cursor:not-allowed}.el-pager li,.el-pager li.btn-quicknext:hover,.el-pager li.btn-quickprev:hover{cursor:pointer}.el-pagination--small .btn-next,.el-pagination--small .btn-prev,.el-pagination--small .el-pager li,.el-pagination--small .el-pager li.btn-quicknext,.el-pagination--small .el-pager li.btn-quickprev,.el-pagination--small .el-pager li:last-child{border-color:transparent;font-size:12px;line-height:22px;height:22px;min-width:22px}.el-pagination--small .arrow.disabled{visibility:hidden}.el-pagination--small .more:before,.el-pagination--small li.more:before{line-height:24px}.el-pagination--small button,.el-pagination--small span:not([class*=suffix]){height:22px;line-height:22px}.el-pagination--small .el-pagination__editor,.el-pagination--small .el-pagination__editor.el-input .el-input__inner{height:22px}.el-pagination__sizes{margin:0 10px 0 0;font-weight:400;color:#606266}.el-pagination__sizes .el-input .el-input__inner{font-size:13px;padding-left:8px}.el-pagination__sizes .el-input .el-input__inner:hover{border-color:#409eff}.el-pagination__total{margin-right:10px;font-weight:400;color:#606266}.el-pagination__jump{margin-left:24px;font-weight:400;color:#606266}.el-pagination__jump .el-input__inner{padding:0 3px}.el-pagination__rightwrapper{float:right}.el-pagination__editor{line-height:18px;padding:0 2px;height:28px;text-align:center;margin:0 2px;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:3px}.el-pager,.el-pagination.is-background .btn-next,.el-pagination.is-background .btn-prev{padding:0}.el-pagination__editor.el-input{width:50px}.el-pagination__editor.el-input .el-input__inner{height:28px}.el-pagination__editor .el-input__inner::-webkit-inner-spin-button,.el-pagination__editor .el-input__inner::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.el-pagination.is-background .btn-next,.el-pagination.is-background .btn-prev,.el-pagination.is-background .el-pager li{margin:0 5px;background-color:#f4f4f5;color:#606266;min-width:30px;border-radius:2px}.el-pagination.is-background .btn-next.disabled,.el-pagination.is-background .btn-next:disabled,.el-pagination.is-background .btn-prev.disabled,.el-pagination.is-background .btn-prev:disabled,.el-pagination.is-background .el-pager li.disabled{color:#c0c4cc}.el-pagination.is-background .el-pager li:not(.disabled):hover{color:#409eff}.el-pagination.is-background .el-pager li:not(.disabled).active{background-color:#409eff;color:#fff}.el-pagination.is-background.el-pagination--small .btn-next,.el-pagination.is-background.el-pagination--small .btn-prev,.el-pagination.is-background.el-pagination--small .el-pager li{margin:0 3px;min-width:22px}.el-pager,.el-pager li{vertical-align:top;display:inline-block;margin:0}.el-pager{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;list-style:none;font-size:0}.el-pager .more:before{line-height:30px}.el-pager li{padding:0 4px;background:#fff;font-size:13px;min-width:35.5px;height:28px;line-height:28px;-webkit-box-sizing:border-box;box-sizing:border-box;text-align:center}.el-pager li.btn-quicknext,.el-pager li.btn-quickprev{line-height:28px;color:#303133}.el-pager li.btn-quicknext.disabled,.el-pager li.btn-quickprev.disabled{color:#c0c4cc}.el-pager li.active+li{border-left:0}.el-pager li:hover{color:#409eff}.el-pager li.active{color:#409eff;cursor:default}.el-fade-in-enter,.el-fade-in-leave-active,.el-fade-in-linear-enter,.el-fade-in-linear-leave,.el-fade-in-linear-leave-active,.fade-in-linear-enter,.fade-in-linear-leave,.fade-in-linear-leave-active{opacity:0}.el-fade-in-linear-enter-active,.el-fade-in-linear-leave-active,.fade-in-linear-enter-active,.fade-in-linear-leave-active{-webkit-transition:opacity .2s linear;transition:opacity .2s linear}.el-fade-in-enter-active,.el-fade-in-leave-active,.el-zoom-in-center-enter-active,.el-zoom-in-center-leave-active{-webkit-transition:all .3s cubic-bezier(.55,0,.1,1);transition:all .3s cubic-bezier(.55,0,.1,1)}.el-zoom-in-center-enter,.el-zoom-in-center-leave-active{opacity:0;-webkit-transform:scaleX(0);transform:scaleX(0)}.el-zoom-in-top-enter-active,.el-zoom-in-top-leave-active{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);-webkit-transform-origin:center top;transform-origin:center top}.el-zoom-in-top-enter,.el-zoom-in-top-leave-active{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}.el-zoom-in-bottom-enter-active,.el-zoom-in-bottom-leave-active{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);-webkit-transform-origin:center bottom;transform-origin:center bottom}.el-zoom-in-bottom-enter,.el-zoom-in-bottom-leave-active{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}.el-zoom-in-left-enter-active,.el-zoom-in-left-leave-active{opacity:1;-webkit-transform:scale(1);transform:scale(1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);-webkit-transform-origin:top left;transform-origin:top left}.el-zoom-in-left-enter,.el-zoom-in-left-leave-active{opacity:0;-webkit-transform:scale(.45);transform:scale(.45)}.collapse-transition{-webkit-transition:height .3s ease-in-out,padding-top .3s ease-in-out,padding-bottom .3s ease-in-out;transition:height .3s ease-in-out,padding-top .3s ease-in-out,padding-bottom .3s ease-in-out}.horizontal-collapse-transition{-webkit-transition:width .3s ease-in-out,padding-left .3s ease-in-out,padding-right .3s ease-in-out;transition:width .3s ease-in-out,padding-left .3s ease-in-out,padding-right .3s ease-in-out}.el-list-enter-active,.el-list-leave-active{-webkit-transition:all 1s;transition:all 1s}.el-list-enter,.el-list-leave-active{opacity:0;-webkit-transform:translateY(-30px);transform:translateY(-30px)}.el-opacity-transition{-webkit-transition:opacity .3s cubic-bezier(.55,0,.1,1);transition:opacity .3s cubic-bezier(.55,0,.1,1)}@font-face{font-family:element-icons;src:url(../fonts/element-icons.535877f5.woff) format("woff"),url(../fonts/element-icons.732389de.ttf) format("truetype");font-weight:400;font-display:"auto";font-style:normal}[class*=" el-icon-"],[class^=el-icon-]{font-family:element-icons!important;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;vertical-align:baseline;display:inline-block;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.el-icon-ice-cream-round:before{content:"\e6a0"}.el-icon-ice-cream-square:before{content:"\e6a3"}.el-icon-lollipop:before{content:"\e6a4"}.el-icon-potato-strips:before{content:"\e6a5"}.el-icon-milk-tea:before{content:"\e6a6"}.el-icon-ice-drink:before{content:"\e6a7"}.el-icon-ice-tea:before{content:"\e6a9"}.el-icon-coffee:before{content:"\e6aa"}.el-icon-orange:before{content:"\e6ab"}.el-icon-pear:before{content:"\e6ac"}.el-icon-apple:before{content:"\e6ad"}.el-icon-cherry:before{content:"\e6ae"}.el-icon-watermelon:before{content:"\e6af"}.el-icon-grape:before{content:"\e6b0"}.el-icon-refrigerator:before{content:"\e6b1"}.el-icon-goblet-square-full:before{content:"\e6b2"}.el-icon-goblet-square:before{content:"\e6b3"}.el-icon-goblet-full:before{content:"\e6b4"}.el-icon-goblet:before{content:"\e6b5"}.el-icon-cold-drink:before{content:"\e6b6"}.el-icon-coffee-cup:before{content:"\e6b8"}.el-icon-water-cup:before{content:"\e6b9"}.el-icon-hot-water:before{content:"\e6ba"}.el-icon-ice-cream:before{content:"\e6bb"}.el-icon-dessert:before{content:"\e6bc"}.el-icon-sugar:before{content:"\e6bd"}.el-icon-tableware:before{content:"\e6be"}.el-icon-burger:before{content:"\e6bf"}.el-icon-knife-fork:before{content:"\e6c1"}.el-icon-fork-spoon:before{content:"\e6c2"}.el-icon-chicken:before{content:"\e6c3"}.el-icon-food:before{content:"\e6c4"}.el-icon-dish-1:before{content:"\e6c5"}.el-icon-dish:before{content:"\e6c6"}.el-icon-moon-night:before{content:"\e6ee"}.el-icon-moon:before{content:"\e6f0"}.el-icon-cloudy-and-sunny:before{content:"\e6f1"}.el-icon-partly-cloudy:before{content:"\e6f2"}.el-icon-cloudy:before{content:"\e6f3"}.el-icon-sunny:before{content:"\e6f6"}.el-icon-sunset:before{content:"\e6f7"}.el-icon-sunrise-1:before{content:"\e6f8"}.el-icon-sunrise:before{content:"\e6f9"}.el-icon-heavy-rain:before{content:"\e6fa"}.el-icon-lightning:before{content:"\e6fb"}.el-icon-light-rain:before{content:"\e6fc"}.el-icon-wind-power:before{content:"\e6fd"}.el-icon-baseball:before{content:"\e712"}.el-icon-soccer:before{content:"\e713"}.el-icon-football:before{content:"\e715"}.el-icon-basketball:before{content:"\e716"}.el-icon-ship:before{content:"\e73f"}.el-icon-truck:before{content:"\e740"}.el-icon-bicycle:before{content:"\e741"}.el-icon-mobile-phone:before{content:"\e6d3"}.el-icon-service:before{content:"\e6d4"}.el-icon-key:before{content:"\e6e2"}.el-icon-unlock:before{content:"\e6e4"}.el-icon-lock:before{content:"\e6e5"}.el-icon-watch:before{content:"\e6fe"}.el-icon-watch-1:before{content:"\e6ff"}.el-icon-timer:before{content:"\e702"}.el-icon-alarm-clock:before{content:"\e703"}.el-icon-map-location:before{content:"\e704"}.el-icon-delete-location:before{content:"\e705"}.el-icon-add-location:before{content:"\e706"}.el-icon-location-information:before{content:"\e707"}.el-icon-location-outline:before{content:"\e708"}.el-icon-location:before{content:"\e79e"}.el-icon-place:before{content:"\e709"}.el-icon-discover:before{content:"\e70a"}.el-icon-first-aid-kit:before{content:"\e70b"}.el-icon-trophy-1:before{content:"\e70c"}.el-icon-trophy:before{content:"\e70d"}.el-icon-medal:before{content:"\e70e"}.el-icon-medal-1:before{content:"\e70f"}.el-icon-stopwatch:before{content:"\e710"}.el-icon-mic:before{content:"\e711"}.el-icon-copy-document:before{content:"\e718"}.el-icon-full-screen:before{content:"\e719"}.el-icon-switch-button:before{content:"\e71b"}.el-icon-aim:before{content:"\e71c"}.el-icon-crop:before{content:"\e71d"}.el-icon-odometer:before{content:"\e71e"}.el-icon-time:before{content:"\e71f"}.el-icon-bangzhu:before{content:"\e724"}.el-icon-close-notification:before{content:"\e726"}.el-icon-microphone:before{content:"\e727"}.el-icon-turn-off-microphone:before{content:"\e728"}.el-icon-position:before{content:"\e729"}.el-icon-postcard:before{content:"\e72a"}.el-icon-message:before{content:"\e72b"}.el-icon-chat-line-square:before{content:"\e72d"}.el-icon-chat-dot-square:before{content:"\e72e"}.el-icon-chat-dot-round:before{content:"\e72f"}.el-icon-chat-square:before{content:"\e730"}.el-icon-chat-line-round:before{content:"\e731"}.el-icon-chat-round:before{content:"\e732"}.el-icon-set-up:before{content:"\e733"}.el-icon-turn-off:before{content:"\e734"}.el-icon-open:before{content:"\e735"}.el-icon-connection:before{content:"\e736"}.el-icon-link:before{content:"\e737"}.el-icon-cpu:before{content:"\e738"}.el-icon-thumb:before{content:"\e739"}.el-icon-female:before{content:"\e73a"}.el-icon-male:before{content:"\e73b"}.el-icon-guide:before{content:"\e73c"}.el-icon-news:before{content:"\e73e"}.el-icon-price-tag:before{content:"\e744"}.el-icon-discount:before{content:"\e745"}.el-icon-wallet:before{content:"\e747"}.el-icon-coin:before{content:"\e748"}.el-icon-money:before{content:"\e749"}.el-icon-bank-card:before{content:"\e74a"}.el-icon-box:before{content:"\e74b"}.el-icon-present:before{content:"\e74c"}.el-icon-sell:before{content:"\e6d5"}.el-icon-sold-out:before{content:"\e6d6"}.el-icon-shopping-bag-2:before{content:"\e74d"}.el-icon-shopping-bag-1:before{content:"\e74e"}.el-icon-shopping-cart-2:before{content:"\e74f"}.el-icon-shopping-cart-1:before{content:"\e750"}.el-icon-shopping-cart-full:before{content:"\e751"}.el-icon-smoking:before{content:"\e752"}.el-icon-no-smoking:before{content:"\e753"}.el-icon-house:before{content:"\e754"}.el-icon-table-lamp:before{content:"\e755"}.el-icon-school:before{content:"\e756"}.el-icon-office-building:before{content:"\e757"}.el-icon-toilet-paper:before{content:"\e758"}.el-icon-notebook-2:before{content:"\e759"}.el-icon-notebook-1:before{content:"\e75a"}.el-icon-files:before{content:"\e75b"}.el-icon-collection:before{content:"\e75c"}.el-icon-receiving:before{content:"\e75d"}.el-icon-suitcase-1:before{content:"\e760"}.el-icon-suitcase:before{content:"\e761"}.el-icon-film:before{content:"\e763"}.el-icon-collection-tag:before{content:"\e765"}.el-icon-data-analysis:before{content:"\e766"}.el-icon-pie-chart:before{content:"\e767"}.el-icon-data-board:before{content:"\e768"}.el-icon-data-line:before{content:"\e76d"}.el-icon-reading:before{content:"\e769"}.el-icon-magic-stick:before{content:"\e76a"}.el-icon-coordinate:before{content:"\e76b"}.el-icon-mouse:before{content:"\e76c"}.el-icon-brush:before{content:"\e76e"}.el-icon-headset:before{content:"\e76f"}.el-icon-umbrella:before{content:"\e770"}.el-icon-scissors:before{content:"\e771"}.el-icon-mobile:before{content:"\e773"}.el-icon-attract:before{content:"\e774"}.el-icon-monitor:before{content:"\e775"}.el-icon-search:before{content:"\e778"}.el-icon-takeaway-box:before{content:"\e77a"}.el-icon-paperclip:before{content:"\e77d"}.el-icon-printer:before{content:"\e77e"}.el-icon-document-add:before{content:"\e782"}.el-icon-document:before{content:"\e785"}.el-icon-document-checked:before{content:"\e786"}.el-icon-document-copy:before{content:"\e787"}.el-icon-document-delete:before{content:"\e788"}.el-icon-document-remove:before{content:"\e789"}.el-icon-tickets:before{content:"\e78b"}.el-icon-folder-checked:before{content:"\e77f"}.el-icon-folder-delete:before{content:"\e780"}.el-icon-folder-remove:before{content:"\e781"}.el-icon-folder-add:before{content:"\e783"}.el-icon-folder-opened:before{content:"\e784"}.el-icon-folder:before{content:"\e78a"}.el-icon-edit-outline:before{content:"\e764"}.el-icon-edit:before{content:"\e78c"}.el-icon-date:before{content:"\e78e"}.el-icon-c-scale-to-original:before{content:"\e7c6"}.el-icon-view:before{content:"\e6ce"}.el-icon-loading:before{content:"\e6cf"}.el-icon-rank:before{content:"\e6d1"}.el-icon-sort-down:before{content:"\e7c4"}.el-icon-sort-up:before{content:"\e7c5"}.el-icon-sort:before{content:"\e6d2"}.el-icon-finished:before{content:"\e6cd"}.el-icon-refresh-left:before{content:"\e6c7"}.el-icon-refresh-right:before{content:"\e6c8"}.el-icon-refresh:before{content:"\e6d0"}.el-icon-video-play:before{content:"\e7c0"}.el-icon-video-pause:before{content:"\e7c1"}.el-icon-d-arrow-right:before{content:"\e6dc"}.el-icon-d-arrow-left:before{content:"\e6dd"}.el-icon-arrow-up:before{content:"\e6e1"}.el-icon-arrow-down:before{content:"\e6df"}.el-icon-arrow-right:before{content:"\e6e0"}.el-icon-arrow-left:before{content:"\e6de"}.el-icon-top-right:before{content:"\e6e7"}.el-icon-top-left:before{content:"\e6e8"}.el-icon-top:before{content:"\e6e6"}.el-icon-bottom:before{content:"\e6eb"}.el-icon-right:before{content:"\e6e9"}.el-icon-back:before{content:"\e6ea"}.el-icon-bottom-right:before{content:"\e6ec"}.el-icon-bottom-left:before{content:"\e6ed"}.el-icon-caret-top:before{content:"\e78f"}.el-icon-caret-bottom:before{content:"\e790"}.el-icon-caret-right:before{content:"\e791"}.el-icon-caret-left:before{content:"\e792"}.el-icon-d-caret:before{content:"\e79a"}.el-icon-share:before{content:"\e793"}.el-icon-menu:before{content:"\e798"}.el-icon-s-grid:before{content:"\e7a6"}.el-icon-s-check:before{content:"\e7a7"}.el-icon-s-data:before{content:"\e7a8"}.el-icon-s-opportunity:before{content:"\e7aa"}.el-icon-s-custom:before{content:"\e7ab"}.el-icon-s-claim:before{content:"\e7ad"}.el-icon-s-finance:before{content:"\e7ae"}.el-icon-s-comment:before{content:"\e7af"}.el-icon-s-flag:before{content:"\e7b0"}.el-icon-s-marketing:before{content:"\e7b1"}.el-icon-s-shop:before{content:"\e7b4"}.el-icon-s-open:before{content:"\e7b5"}.el-icon-s-management:before{content:"\e7b6"}.el-icon-s-ticket:before{content:"\e7b7"}.el-icon-s-release:before{content:"\e7b8"}.el-icon-s-home:before{content:"\e7b9"}.el-icon-s-promotion:before{content:"\e7ba"}.el-icon-s-operation:before{content:"\e7bb"}.el-icon-s-unfold:before{content:"\e7bc"}.el-icon-s-fold:before{content:"\e7a9"}.el-icon-s-platform:before{content:"\e7bd"}.el-icon-s-order:before{content:"\e7be"}.el-icon-s-cooperation:before{content:"\e7bf"}.el-icon-bell:before{content:"\e725"}.el-icon-message-solid:before{content:"\e799"}.el-icon-video-camera:before{content:"\e772"}.el-icon-video-camera-solid:before{content:"\e796"}.el-icon-camera:before{content:"\e779"}.el-icon-camera-solid:before{content:"\e79b"}.el-icon-download:before{content:"\e77c"}.el-icon-upload2:before{content:"\e77b"}.el-icon-upload:before{content:"\e7c3"}.el-icon-picture-outline-round:before{content:"\e75f"}.el-icon-picture-outline:before{content:"\e75e"}.el-icon-picture:before{content:"\e79f"}.el-icon-close:before{content:"\e6db"}.el-icon-check:before{content:"\e6da"}.el-icon-plus:before{content:"\e6d9"}.el-icon-minus:before{content:"\e6d8"}.el-icon-help:before{content:"\e73d"}.el-icon-s-help:before{content:"\e7b3"}.el-icon-circle-close:before{content:"\e78d"}.el-icon-circle-check:before{content:"\e720"}.el-icon-circle-plus-outline:before{content:"\e723"}.el-icon-remove-outline:before{content:"\e722"}.el-icon-zoom-out:before{content:"\e776"}.el-icon-zoom-in:before{content:"\e777"}.el-icon-error:before{content:"\e79d"}.el-icon-success:before{content:"\e79c"}.el-icon-circle-plus:before{content:"\e7a0"}.el-icon-remove:before{content:"\e7a2"}.el-icon-info:before{content:"\e7a1"}.el-icon-question:before{content:"\e7a4"}.el-icon-warning-outline:before{content:"\e6c9"}.el-icon-warning:before{content:"\e7a3"}.el-icon-goods:before{content:"\e7c2"}.el-icon-s-goods:before{content:"\e7b2"}.el-icon-star-off:before{content:"\e717"}.el-icon-star-on:before{content:"\e797"}.el-icon-more-outline:before{content:"\e6cc"}.el-icon-more:before{content:"\e794"}.el-icon-phone-outline:before{content:"\e6cb"}.el-icon-phone:before{content:"\e795"}.el-icon-user:before{content:"\e6e3"}.el-icon-user-solid:before{content:"\e7a5"}.el-icon-setting:before{content:"\e6ca"}.el-icon-s-tools:before{content:"\e7ac"}.el-icon-delete:before{content:"\e6d7"}.el-icon-delete-solid:before{content:"\e7c9"}.el-icon-eleme:before{content:"\e7c7"}.el-icon-platform-eleme:before{content:"\e7ca"}.el-icon-loading{-webkit-animation:rotating 2s linear infinite;animation:rotating 2s linear infinite}.el-icon--right{margin-left:5px}.el-icon--left{margin-right:5px}@-webkit-keyframes rotating{0%{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes rotating{0%{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}} -------------------------------------------------------------------------------- /src/hacks/data/books.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | title: "The Lord of the Rings", 4 | author: "J. R. R. Tolkien", 5 | coverImageUrl: 6 | "https://images-na.ssl-images-amazon.com/images/I/51r6XIPWmoL._SX331_BO1,204,203,200_.jpg", 7 | id: "618645616", 8 | pageCount: 1178, 9 | publisher: "Houghton Mifflin Harcourt", 10 | synopsis: 11 | "In ancient times the Rings of Power were crafted by the Elven-smiths, and Sauron, the Dark Lord, forged the One Ring, filling it with his own power so that he could rule all others. But the One Ring was taken from him, and though he sought it throughout Middle-earth, it remained lost to him. After many ages it fell by chance into the hands of the hobbit Bilbo Baggins." 12 | }, 13 | { 14 | title: "The Hobbit", 15 | author: "J. R. R. Tolkien", 16 | coverImageUrl: 17 | "https://images-na.ssl-images-amazon.com/images/I/61VxEKq8B1L._SX365_BO1,204,203,200_.jpg", 18 | id: "547928246", 19 | pageCount: 320, 20 | publisher: "Houghton Mifflin Harcourt", 21 | synopsis: 22 | "Bilbo Baggins is a hobbit who enjoys a comfortable, unambitious life, rarely traveling any farther than his pantry or cellar. But his contentment is disturbed when the wizard Gandalf and a company of dwarves arrive on his doorstep one day to whisk him away on an adventure. They have launched a plot to raid the treasure hoard guarded by Smaug the Magnificent, a large and very dangerous dragon. Bilbo reluctantly joins their quest, unaware that on his journey to the Lonely Mountain he will encounter both a magic ring and a frightening creature known as Gollum." 23 | }, 24 | { 25 | title: "Harry Potter and the Sorcerer's Stone", 26 | author: "J. K. Rowling", 27 | coverImageUrl: 28 | "https://images-na.ssl-images-amazon.com/images/I/51HSkTKlauL._SX346_BO1,204,203,200_.jpg", 29 | id: "059035342X", 30 | pageCount: 309, 31 | publisher: "Scholastic", 32 | synopsis: 33 | "Harry Potter has no idea how famous he is. That's because he's being raised by his miserable aunt and uncle who are terrified Harry will learn that he's really a wizard, just as his parents were. But everything changes when Harry is summoned to attend an infamous school for wizards, and he begins to discover some clues about his illustrious birthright. From the surprising way he is greeted by a lovable giant, to the unique curriculum and colorful faculty at his unusual school, Harry finds himself drawn deep inside a mystical world he never knew existed and closer to his own noble destiny." 34 | }, 35 | { 36 | title: "Harry Potter and the Chamber of Secrets", 37 | author: "J. K. Rowling", 38 | coverImageUrl: 39 | "https://images-na.ssl-images-amazon.com/images/I/51jNORv6nQL._SX340_BO1,204,203,200_.jpg", 40 | id: "439064872", 41 | pageCount: 341, 42 | publisher: "Scholastic", 43 | synopsis: 44 | "Ever since Harry Potter had come home for the summer, the Dursleys had been so mean and hideous that all Harry wanted was to get back to the Hogwarts School for Witchcraft and Wizardry. But just as he's packing his bags, Harry receives a warning from a strange, impish creature who says that if Harry returns to Hogwarts, disaster will strike. And strike it does. For in Harry's second year at Hogwarts, fresh torments and horrors arise, including an outrageously stuck-up new professor and a spirit who haunts the girls' bathroom. But then the real trouble begins -- someone is turning Hogwarts students to stone. Could it be Draco Malfoy, a more poisonous rival than ever? Could it possibly be Hagrid, whose mysterious past is finally told? Or could it be the one everyone at Hogwarts most suspects...Harry Potter himself!" 45 | }, 46 | { 47 | title: "Harry Potter and the Prisoner of Azkaban", 48 | author: "J. K. Rowling", 49 | coverImageUrl: 50 | "https://images-na.ssl-images-amazon.com/images/I/51IiQ4r35LL._SX345_BO1,204,203,200_.jpg", 51 | id: "439136369", 52 | pageCount: 448, 53 | publisher: "Scholastic", 54 | synopsis: 55 | 'In this third installment in the projected seven-volume series, Sirius Black, imprisoned for killing 13 people with one curse, escapes from Azkaban. As he heads for Hogwarts, the chilling Dementors who trail him quickly descend upon the school. "Each successive volume expands upon its predecessor with dizzyingly well-planned plots and inventive surprises," said PW in a Best Books of 2001 citation. Ages 8-up.' 56 | }, 57 | { 58 | title: "Harry Potter and the Goblet of Fire", 59 | author: "J. K. Rowling", 60 | coverImageUrl: 61 | "https://images-na.ssl-images-amazon.com/images/I/51OORp1XD1L._SX421_BO1,204,203,200_.jpg", 62 | id: "439139600", 63 | pageCount: 752, 64 | publisher: "Scholastic", 65 | synopsis: 66 | "Harry Potter is midway through his training as a wizard and his coming of age. Harry wants to get away from the pernicious Dursleys and go to the International Quidditch Cup. He wants to find out about the mysterious event that's supposed to take place at Hogwarts this year, an event involving two other rival schools of magic, and a competition that hasn't happened for a hundred years. He wants to be a normal, fourteen-year-old wizard. But unfortunately for Harry Potter, he's not normal - even by wizarding standards. And in his case, different can be deadly." 67 | }, 68 | { 69 | title: "Harry Potter and the Order of the Phoenix", 70 | author: "J. K. Rowling", 71 | coverImageUrl: 72 | "https://images-na.ssl-images-amazon.com/images/I/51SfTd37PaL._SX415_BO1,204,203,200_.jpg", 73 | id: "439358078", 74 | pageCount: 896, 75 | publisher: "Scholastic", 76 | synopsis: 77 | "In his fifth year at Hogwart's, Harry faces challenges at every turn, from the dark threat of He-Who-Must-Not-Be-Named and the unreliability of the government of the magical world to the rise of Ron Weasley as the keeper of the Gryffindor Quidditch Team. Along the way he learns about the strength of his friends, the fierceness of his enemies, and the meaning of sacrifice" 78 | }, 79 | { 80 | title: "Harry Potter and the Half-Blood Prince", 81 | author: "J. K. Rowling", 82 | coverImageUrl: 83 | "https://images-na.ssl-images-amazon.com/images/I/51KV4CXARLL._SX342_BO1,204,203,200_.jpg", 84 | id: "439785960", 85 | pageCount: 652, 86 | publisher: "Scholastic", 87 | synopsis: 88 | "The war against Voldemort is not going well; even the Muggles have been affected. Dumbledore is absent from Hogwarts for long stretches of time, and the Order of the Phoenix has already suffered losses. And yet . . . as with all wars, life goes on. Sixth-year students learn to Apparate. Teenagers flirt and fight and fall in love. Harry receives some extraordinary help in Potions from the mysterious Half-Blood Prince. And with Dumbledore's guidance, he seeks out the full, complex story of the boy who became Lord Voldemort -- and thus finds what may be his only vulnerability." 89 | }, 90 | { 91 | title: "Harry Potter and the Deathly Hallows", 92 | author: "J. K. Rowling", 93 | coverImageUrl: 94 | "https://images-na.ssl-images-amazon.com/images/I/5128ATd9dSL._SX418_BO1,204,203,200_.jpg", 95 | id: "545139708", 96 | pageCount: 784, 97 | publisher: "Scholastic", 98 | synopsis: 99 | "A spectacular finish to a phenomenal series, Harry Potter and the Deathly Hallows is a bittersweet read for fans. The journey is hard, filled with events both tragic and triumphant, the battlefield littered with the bodies of the dearest and despised, but the final chapter is as brilliant and blinding as a phoenix's flame, and fans and skeptics alike will emerge from the confines of the story with full but heavy hearts, giddy and grateful for the experience." 100 | }, 101 | { 102 | title: "A Gentleman in Moscow", 103 | author: "Amor Towles", 104 | coverImageUrl: 105 | "https://images-na.ssl-images-amazon.com/images/I/51NricjmP4L._SX324_BO1,204,203,200_.jpg", 106 | id: "143110438", 107 | pageCount: 496, 108 | publisher: "Penguin Books", 109 | synopsis: 110 | "In 1922, Count Alexander Rostov is deemed an unrepentant aristocrat by a Bolshevik tribunal, and is sentenced to house arrest in the Metropol, a grand hotel across the street from the Kremlin. Rostov, an indomitable man of erudition and wit, has never worked a day in his life, and must now live in an attic room while some of the most tumultuous decades in Russian history are unfolding outside the hotel’s doors. Unexpectedly, his reduced circumstances provide him entry into a much larger world of emotional discovery." 111 | }, 112 | { 113 | title: "Anna Karenina", 114 | author: "Leo Tolstoy", 115 | coverImageUrl: 116 | "https://images-na.ssl-images-amazon.com/images/I/514tG8HMsrL._SX317_BO1,204,203,200_.jpg", 117 | id: "1853262714", 118 | pageCount: 848, 119 | publisher: "Wordsworth Editions", 120 | synopsis: 121 | "A famous legend surrounding the creation of Anna Karenina tells us that Tolstoy began writing a cautionary tale about adultery and ended up by falling in love with his magnificent heroine. It is rare to find a reader of the book who doesn't experience the same kind of emotional upheaval: Anna Karenina is filled with major and minor characters who exist in their own right and fully embody their mid-nineteenth-century Russian milieu, but it still belongs entirely to the woman whose name it bears, whose portrait is one of the truest ever made by a writer." 122 | }, 123 | { 124 | title: "A Bear Called Paddington", 125 | author: "Michael Bond", 126 | coverImageUrl: 127 | "https://images-na.ssl-images-amazon.com/images/I/51clgmTURAL._SX321_BO1,204,203,200_.jpg", 128 | id: "62312189", 129 | pageCount: 176, 130 | publisher: "HarperCollins", 131 | synopsis: 132 | "Paddington Bear had just traveled all the way from Peru when the Brown family first met him in Paddington Station. Since then, their lives have never been quite the same . . . for ordinary things become extraordinary when Paddington is involved." 133 | }, 134 | { 135 | title: "A Tale of Two Cities", 136 | author: "Charles Dickens", 137 | coverImageUrl: 138 | "https://images-na.ssl-images-amazon.com/images/I/51rVPckPtuL._SX311_BO1,204,203,200_.jpg", 139 | id: "486406512", 140 | pageCount: 304, 141 | publisher: "Dover Publications", 142 | synopsis: 143 | "It was the time of the French Revolution—a time of great change and great danger. It was a time when injustice was met by a lust for vengeance, and rarely was a distinction made between the innocent and the guilty. Against this tumultuous historical backdrop, Dickens' great story of unsurpassed adventure and courage unfolds.\nUnjustly imprisoned for 18 years in the Bastille, Dr. Alexandre Manette is reunited with his daughter, Lucie, and safely transported from France to England. It would seem that they could take up the threads of their lives in peace. As fate would have it though, the pair are summoned to the Old Bailey to testify against a young Frenchman—Charles Darnay—falsely accused of treason. Strangely enough, Darnay bears an uncanny resemblance to another man in the courtroom, the dissolute lawyer's clerk Sydney Carton. It is a coincidence that saves Darnay from certain doom more than once. Brilliantly plotted, the novel is rich in drama, romance, and heroics that culminate in a daring prison escape in the shadow of the guillotine." 144 | }, 145 | { 146 | title: "Oliver Twist", 147 | author: "Charles Dickens", 148 | coverImageUrl: 149 | "https://images-na.ssl-images-amazon.com/images/I/513w5abICIL._SX348_BO1,204,203,200_.jpg", 150 | id: "1514640376", 151 | pageCount: 234, 152 | publisher: "CreateSpace", 153 | synopsis: 154 | "Oliver Twist, or The Parish Boy's Progress, is the second novel by Charles Dickens, and was first published as a serial 1837–9. The story is of the orphan Oliver Twist, who starts his life in a workhouse and is then apprenticed with an undertaker. He escapes from there and travels to London where he meets the Artful Dodger, a member of a gang of juvenile pickpockets, which is led by the elderly criminal Fagin. Oliver Twist is notable for Dickens's unromantic portrayal of criminals and their sordid lives, as well as exposing the cruel treatment of the many orphans in London in the mid–nineteenth century. The alternate title, The Parish Boy's Progress, alludes to Bunyan's The Pilgrim's Progress, as well as the 18th-century caricature series by William Hogarth, A Rake's Progress and A Harlot's Progress. An early example of the social novel, Dickens satirizes the hypocrisies of his time, including child labour, the recruitment of children as criminals, and the presence of street children. The novel may have been inspired by the story of Robert Blincoe, an orphan whose account of working as a child labourer in a cotton mill was widely read in the 1830s. It is likely that Dickens's own youthful experiences contributed as well. Oliver Twist has been the subject of numerous adaptations, for various media, including a highly successful musical play, Oliver!, and the multiple Academy Award winning 1968 motion picture." 155 | }, 156 | { 157 | title: "The Way of Kings (Book 1 of the Stormlight Archive)", 158 | author: "Brandon Sanderson", 159 | coverImageUrl: 160 | "https://images-na.ssl-images-amazon.com/images/I/517Fo8m%2BgnL._SX331_BO1,204,203,200_.jpg", 161 | id: "765376679", 162 | pageCount: 1008, 163 | publisher: "Tor Books", 164 | synopsis: 165 | "Roshar is a world of stone and storms. Uncanny tempests of incredible power sweep across the rocky terrain so frequently that they have shaped ecology and civilization alike. Animals hide in shells, trees pull in branches, and grass retracts into the soilless ground. Cities are built only where the topography offers shelter.\n\nIt has been centuries since the fall of the ten consecrated orders known as the Knights Radiant, but their Shardblades and Shardplate remain: mystical swords and suits of armor that transform ordinary men into near-invincible warriors. Men trade kingdoms for Shardblades. Wars were fought for them, and won by them." 166 | }, 167 | { 168 | title: "Words of Radiance (Book 2 of the Stormlight Archive)", 169 | author: "Brandon Sanderson", 170 | coverImageUrl: 171 | "https://images-na.ssl-images-amazon.com/images/I/51jVA6k9AUL._SX329_BO1,204,203,200_.jpg", 172 | id: "1250166535", 173 | pageCount: 1104, 174 | publisher: "Tor Books", 175 | synopsis: 176 | 'Expected by his enemies to die the miserable death of a military slave, Kaladin survived to be given command of the royal bodyguards, a controversial first for a low-status "darkeyes." Now he must protect the king and Dalinar from every common peril as well as the distinctly uncommon threat of the Assassin, all while secretly struggling to master remarkable new powers that are somehow linked to his honorspren, Syl.' 177 | }, 178 | { 179 | title: "Oathbringer (Book 3 of the Stormlight Archive)", 180 | author: "Brandon Sanderson", 181 | coverImageUrl: 182 | "https://images-na.ssl-images-amazon.com/images/I/51G3r2QSvoL._SX327_BO1,204,203,200_.jpg", 183 | id: "1250297141", 184 | pageCount: 1264, 185 | publisher: "Tor Books", 186 | synopsis: 187 | "Dalinar Kholin’s Alethi armies won a fleeting victory at a terrible cost: The enemy Parshendi summoned the violent Everstorm, which now sweeps the world with destruction, and in its passing awakens the once peaceful and subservient parshmen to the horror of their millennia-long enslavement by humans. While on a desperate flight to warn his family of the threat, Kaladin Stormblessed must come to grips with the fact that the newly kindled anger of the parshmen may be wholly justified." 188 | }, 189 | { 190 | title: "The Iliad", 191 | author: "Homer", 192 | coverImageUrl: 193 | "https://images-na.ssl-images-amazon.com/images/I/51NXvaFyvIL._SX336_BO1,204,203,200_.jpg", 194 | id: "140275363", 195 | pageCount: 704, 196 | publisher: "Penguin Classics", 197 | synopsis: 198 | "Dating to the ninth century B.C., Homer’s timeless poem still vividly conveys the horror and heroism of men and gods wrestling with towering emotions and battling amidst devastation and destruction, as it moves inexorably to the wrenching, tragic conclusion of the Trojan War. Renowned classicist Bernard Knox observes in his superb introduction that although the violence of the Iliad is grim and relentless, it coexists with both images of civilized life and a poignant yearning for peace." 199 | }, 200 | { 201 | title: "The Odyssey", 202 | author: "Homer", 203 | coverImageUrl: 204 | "https://images-na.ssl-images-amazon.com/images/I/51FR8mSgqoL._SX346_BO1,204,203,200_.jpg", 205 | id: "140268863", 206 | pageCount: 560, 207 | publisher: "Penguin Classics", 208 | synopsis: 209 | 'Sing to me of the man, Muse, the man of twists and turns driven time and again off course, once he had plundered the hallowed heights of Troy." So begins Robert Fagles\' magnificent translation of the Odyssey, which Jasper Griffin in the New York Times Book Review hails as "a distinguished achievement.' 210 | }, 211 | { 212 | title: "To Kill a Mockingbird", 213 | author: "Harper Lee", 214 | coverImageUrl: 215 | "https://images-na.ssl-images-amazon.com/images/I/51IXWZzlgSL._SX330_BO1,204,203,200_.jpg", 216 | id: "60935464", 217 | pageCount: 336, 218 | publisher: "Harper Perennial", 219 | synopsis: 220 | "One of the best-loved stories of all time, To Kill a Mockingbird has been translated into more than forty languages, sold more than forty million copies worldwide, served as the basis for an enormously popular motion picture, and was voted one of the best novels of the twentieth century by librarians across the country. A gripping, heart-wrenching, and wholly remarkable tale of coming-of-age in a South poisoned by virulent prejudice, it views a world of great beauty and savage inequities through the eyes of a young girl, as her father—a crusading local lawyer—risks everything to defend a black man unjustly accused of a terrible crime." 221 | }, 222 | { 223 | title: 1984, 224 | author: "George Orwell", 225 | coverImageUrl: 226 | "https://images-na.ssl-images-amazon.com/images/I/31lWUHDG7uL._SX282_BO1,204,203,200_.jpg", 227 | id: "451524934", 228 | pageCount: 328, 229 | publisher: "Signet Classics", 230 | synopsis: 231 | "Winston Smith toes the Party line, rewriting history to satisfy the demands of the Ministry of Truth. With each lie he writes, Winston grows to hate the Party that seeks power for its own sake and persecutes those who dare to commit thoughtcrimes. But as he starts to think for himself, Winston can’t escape the fact that Big Brother is always watching...\n\nA startling and haunting vision of the world, 1984 is so powerful that it is completely convincing from start to finish. No one can deny the influence of this novel, its hold on the imaginations of multiple generations of readers, or the resiliency of its admonitions—a legacy that seems only to grow with the passage of time." 232 | }, 233 | { 234 | title: "The Great Gatsby", 235 | author: "F. Scott Fitzgerald", 236 | coverImageUrl: 237 | "https://images-na.ssl-images-amazon.com/images/I/51vv75oglyL._SX326_BO1,204,203,200_.jpg", 238 | id: "743273567", 239 | pageCount: 180, 240 | publisher: "Scribner", 241 | synopsis: 242 | "The Great Gatsby, F. Scott Fitzgerald’s third book, stands as the supreme achievement of his career. First published in 1925, this quintessential novel of the Jazz Age has been acclaimed by generations of readers. The story of the mysteriously wealthy Jay Gatsby and his love for the beautiful Daisy Buchanan, of lavish parties on Long Island at a time when The New York Times noted “gin was the national drink and sex the national obsession,” it is an exquisitely crafted tale of America in the 1920s." 243 | }, 244 | { 245 | title: "Pride and Prejudice", 246 | author: "Jane Austen", 247 | coverImageUrl: 248 | "https://images-na.ssl-images-amazon.com/images/I/41k29Fict8L._SX325_BO1,204,203,200_.jpg", 249 | id: "141439513", 250 | pageCount: 480, 251 | publisher: "Penguin Books", 252 | synopsis: 253 | "Few have failed to be charmed by the witty and independent spirit of Elizabeth Bennet in Austen’s beloved classic Pride and Prejudice. When Elizabeth Bennet first meets eligible bachelor Fitzwilliam Darcy, she thinks him arrogant and conceited; he is indifferent to her good looks and lively mind. When she later discovers that Darcy has involved himself in the troubled relationship between his friend Bingley and her beloved sister Jane, she is determined to dislike him more than ever. In the sparkling comedy of manners that follows, Jane Austen shows us the folly of judging by first impressions and superbly evokes the friendships, gossip and snobberies of provincial middle-class life. This Penguin Classics edition, based on Austen's first edition, contains the original Penguin Classics introduction by Tony Tanner and an updated introduction and notes by Viven Jones." 254 | }, 255 | { 256 | title: "Anne Frank: The Diary of a Young Girl", 257 | author: "Anne Frank", 258 | coverImageUrl: 259 | "https://images-na.ssl-images-amazon.com/images/I/51Vps1OxJPL._SX303_BO1,204,203,200_.jpg", 260 | id: "553296981", 261 | pageCount: 283, 262 | publisher: "Bantam", 263 | synopsis: 264 | "In 1942, with the Nazis occupying Holland, a thirteen-year-old Jewish girl and her family fled their home in Amsterdam and went into hiding. For the next two years, until their whereabouts were betrayed to the Gestapo, the Franks and another family lived cloistered in the “Secret Annexe” of an old office building. Cut off from the outside world, they faced hunger, boredom, the constant cruelties of living in confined quarters, and the ever-present threat of discovery and death. In her diary Anne Frank recorded vivid impressions of her experiences during this period. By turns thoughtful, moving, and surprisingly humorous, her account offers a fascinating commentary on human courage and frailty and a compelling self-portrait of a sensitive and spirited young woman whose promise was tragically cut short." 265 | }, 266 | { 267 | title: "The Book Thief", 268 | author: "Markus Zusak", 269 | coverImageUrl: 270 | "https://images-na.ssl-images-amazon.com/images/I/51C8Tg0TCaL._SX322_BO1,204,203,200_.jpg", 271 | id: "375842209", 272 | pageCount: 608, 273 | publisher: "Alfred A. Knopf", 274 | synopsis: 275 | "Liesel Meminger is a foster girl living outside of Munich, who scratches out a meager existence for herself by stealing when she encounters something she can’t resist–books. With the help of her accordion-playing foster father, she learns to read and shares her stolen books with her neighbors during bombing raids as well as with the Jewish man hidden in her basement." 276 | }, 277 | { 278 | title: "Little Women", 279 | author: "Louisa May Alcott", 280 | coverImageUrl: 281 | "https://images-na.ssl-images-amazon.com/images/I/513jSeoTzDL._SX331_BO1,204,203,200_.jpg", 282 | id: "1950435091", 283 | pageCount: 546, 284 | publisher: "SeaWolf Press", 285 | synopsis: 286 | "Grown-up Meg, tomboyish Jo, timid Beth, and precocious Amy. The four March sisters couldn't be more different. But with their father away at war, and their mother working to support the family, they have to rely on one another. Whether they're putting on a play, forming a secret society, or celebrating Christmas, there's one thing they can't help wondering: Will Father return home safely?" 287 | }, 288 | { 289 | title: "Farenheight 451", 290 | author: "Ray Bradbury", 291 | coverImageUrl: 292 | "https://images-na.ssl-images-amazon.com/images/I/41qI9quGIdL._SX324_BO1,204,203,200_.jpg", 293 | id: "1451673310", 294 | pageCount: 249, 295 | publisher: "Simon & Schuster", 296 | synopsis: 297 | "Ray Bradbury’s internationally acclaimed novel Fahrenheit 451 is a masterwork of twentieth-century literature set in a bleak, dystopian future." 298 | }, 299 | { 300 | title: "Jane Eyre", 301 | author: "Charlotte Bronte", 302 | coverImageUrl: 303 | "https://images-na.ssl-images-amazon.com/images/I/51poYFyvtWL._SX302_BO1,204,203,200_.jpg", 304 | id: "553211404", 305 | pageCount: 492, 306 | publisher: "Bantam Classics", 307 | synopsis: 308 | "Initially published under the pseudonym Currer Bell in 1847, Charlotte Brontë’s Jane Eyreerupted onto the English literary scene, immediately winning the devotion of many of the world’s most renowned writers, including William Makepeace Thackeray, who declared it a work “of great genius.” Widely regarded as a revolutionary novel, Brontë’s masterpiece introduced the world to a radical new type of heroine, one whose defiant virtue and moral courage departed sharply from the more acquiescent and malleable female characters of the day. Passionate, dramatic, and surprisingly modern, Jane Eyre endures as one of the world’s most beloved novels." 309 | }, 310 | { 311 | title: "Animal Farm", 312 | author: "George Orwell", 313 | coverImageUrl: 314 | "https://images-na.ssl-images-amazon.com/images/I/31wXW-2LRhL._SX292_BO1,204,203,200_.jpg", 315 | id: "451526341", 316 | pageCount: 140, 317 | publisher: "Signet Classics", 318 | synopsis: 319 | "A farm is taken over by its overworked, mistreated animals. With flaming idealism and stirring slogans, they set out to create a paradise of progress, justice, and equality. Thus the stage is set for one of the most telling satiric fables ever penned—a razor-edged fairy tale for grown-ups that records the evolution from revolution against tyranny to a totalitarianism just as terrible." 320 | }, 321 | { 322 | title: "Gone With the Wind", 323 | author: "Margaret Mitchell", 324 | coverImageUrl: 325 | "https://images-na.ssl-images-amazon.com/images/I/51y1omNT91L._SX321_BO1,204,203,200_.jpg", 326 | id: "1451635621", 327 | pageCount: 960, 328 | publisher: "Scribner", 329 | synopsis: 330 | "This is the tale of Scarlett O’Hara, the spoiled, manipulative daughter of a wealthy plantation owner, who arrives at young womanhood just in time to see the Civil War forever change her way of life. A sweeping story of tangled passion and courage, in the pages of Gone With the Wind, Margaret Mitchell brings to life the unforgettable characters that have captured readers for over seventy years." 331 | }, 332 | { 333 | title: "I Know Why the Caged Bird Sings", 334 | author: "Maya Angelou", 335 | coverImageUrl: 336 | "https://images-na.ssl-images-amazon.com/images/I/41SKsBaGXRL._SX301_BO1,204,203,200_.jpg", 337 | id: "345514408", 338 | pageCount: 304, 339 | publisher: "Ballantine Books", 340 | synopsis: 341 | "Here is a book as joyous and painful, as mysterious and memorable, as childhood itself. I Know Why the Caged Bird Sings captures the longing of lonely children, the brute insult of bigotry, and the wonder of words that can make the world right. Maya Angelou’s debut memoir is a modern American classic beloved worldwide." 342 | }, 343 | { 344 | title: "Becoming", 345 | author: "Michelle Obama", 346 | coverImageUrl: 347 | "https://images-na.ssl-images-amazon.com/images/I/414JfiBCutL._SX327_BO1,204,203,200_.jpg", 348 | id: "1524763136", 349 | pageCount: 448, 350 | publisher: "Crown Publishing Books", 351 | synopsis: 352 | "In her memoir, a work of deep reflection and mesmerizing storytelling, Michelle Obama invites readers into her world, chronicling the experiences that have shaped her—from her childhood on the South Side of Chicago to her years as an executive balancing the demands of motherhood and work, to her time spent at the world’s most famous address. With unerring honesty and lively wit, she describes her triumphs and her disappointments, both public and private, telling her full story as she has lived it—in her own words and on her own terms. Warm, wise, and revelatory, Becoming is the deeply personal reckoning of a woman of soul and substance who has steadily defied expectations—and whose story inspires us to do the same" 353 | }, 354 | { 355 | title: "Charlotte's Web", 356 | author: "E. B. White", 357 | coverImageUrl: 358 | "https://images-na.ssl-images-amazon.com/images/I/61%2B3z1o4oUL._SX334_BO1,204,203,200_.jpg", 359 | id: "61124958", 360 | pageCount: 192, 361 | publisher: "HarperCollins", 362 | synopsis: 363 | "E. B. White's Newbery Honor Book is a tender novel of friendship, love, life, and death that will continue to be enjoyed by generations to come. It contains illustrations by Garth Williams, the acclaimed illustrator of E. B. White's Stuart Little and Laura Ingalls Wilder's Little House series, among many other books." 364 | }, 365 | { 366 | title: "Pippi Longstocking", 367 | author: "Astrid Lindgren", 368 | coverImageUrl: 369 | "https://images-na.ssl-images-amazon.com/images/I/51AJzl2N0VL._SX349_BO1,204,203,200_.jpg", 370 | id: "142402494", 371 | pageCount: 160, 372 | publisher: "Puffin Books", 373 | synopsis: 374 | "Tommy and his sister Annika have a new neighbor, and her name is Pippi Longstocking. She has crazy red pigtails, no parents to tell her what to do, a horse that lives on her porch, and a flair for the outrageous that seems to lead to one adventure after another!" 375 | }, 376 | { 377 | title: "The Lion, the Witch, and the Wardrobe", 378 | author: "C. S. Lewis", 379 | coverImageUrl: 380 | "https://images-na.ssl-images-amazon.com/images/I/51erHMLhIzL._SX334_BO1,204,203,200_.jpg", 381 | id: "64404994", 382 | pageCount: 208, 383 | publisher: "HarperCollins", 384 | synopsis: 385 | "Four adventurous siblings—Peter, Susan, Edmund, and Lucy Pevensie—step through a wardrobe door and into the land of Narnia, a land frozen in eternal winter and enslaved by the power of the White Witch. But when almost all hope is lost, the return of the Great Lion, Aslan, signals a great change . . . and a great sacrifice." 386 | }, 387 | { 388 | title: "Grapes of Wrath", 389 | author: "John Steinback", 390 | coverImageUrl: 391 | "https://images-na.ssl-images-amazon.com/images/I/51QyaWppYJL._SX329_BO1,204,203,200_.jpg", 392 | id: "067001690X", 393 | pageCount: 496, 394 | publisher: "Viking", 395 | synopsis: 396 | "First published in 1939, Steinbeck’s Pulitzer Prize–winning epic of the Great Depression chronicles the Dust Bowl migration of the 1930s and tells the story of one Oklahoma farm family, the Joads, driven from their homestead and forced to travel west to the promised land of California. Out of their trials and their repeated collisions against the hard realities of an America divided into haves and have-nots evolves a drama that is intensely human yet majestic in its scale and moral vision, elemental yet plainspoken, tragic but ultimately stirring in its human dignity." 397 | }, 398 | { 399 | title: "The Kite Runner", 400 | author: "Khaled Hosseini", 401 | coverImageUrl: 402 | "https://images-na.ssl-images-amazon.com/images/I/51vRNqL61aL._SX318_BO1,204,203,200_.jpg", 403 | id: "159463193X", 404 | pageCount: 400, 405 | publisher: "Riverhead Books", 406 | synopsis: 407 | "The unforgettable, heartbreaking story of the unlikely friendship between a wealthy boy and the son of his father’s servant, caught in the tragic sweep of history, The Kite Runner transports readers to Afghanistan at a tense and crucial moment of change and destruction. A powerful story of friendship, it is also about the power of reading, the price of betrayal, and the possibility of redemption; and an exploration of the power of fathers over sons—their love, their sacrifices, their lies." 408 | }, 409 | { 410 | title: "Romeo and Juliet", 411 | author: "William Shakespeare", 412 | coverImageUrl: 413 | "https://images-na.ssl-images-amazon.com/images/I/51n97vsmHlL._SX307_BO1,204,203,200_.jpg", 414 | id: "743477111", 415 | pageCount: 336, 416 | publisher: "Simon & Schuster", 417 | synopsis: 418 | "In Romeo and Juliet, Shakespeare creates a violent world, in which two young people fall in love. It is not simply that their families disapprove; the Montagues and the Capulets are engaged in a blood feud.\n\nIn this death-filled setting, the movement from love at first sight to the lovers’ final union in death seems almost inevitable. And yet, this play set in an extraordinary world has become the quintessential story of young love. In part because of its exquisite language, it is easy to respond as if it were about all young lovers." 419 | }, 420 | { 421 | title: "The Hitchhiker's Guide to the Galaxy", 422 | author: "Douglas Adams", 423 | coverImageUrl: 424 | "https://images-na.ssl-images-amazon.com/images/I/51MzUz8rQcL._SX305_BO1,204,203,200_.jpg", 425 | id: "345391802", 426 | pageCount: 224, 427 | publisher: "Del Rey", 428 | synopsis: 429 | "Seconds before Earth is demolished to make way for a galactic freeway, Arthur Dent is plucked off the planet by his friend Ford Prefect, a researcher for the revised edition of The Hitchhiker’s Guide to the Galaxy who, for the last fifteen years, has been posing as an out-of-work actor.\n\nTogether, this dynamic pair began a journey through space aided by a galaxyful of fellow travelers: Zaphod Beeblebrox—the two-headed, three-armed ex-hippie and totally out-to-lunch president of the galaxy; Trillian (formerly Tricia McMillan), Zaphod’s girlfriend, whom Arthur tried to pick up at a cocktail party once upon a time zone; Marvin, a paranoid, brilliant, and chronically depressed robot; and Veet Voojagig, a former graduate student obsessed with the disappearance of all the ballpoint pens he’s bought over the years." 430 | }, 431 | { 432 | title: "Wuthering Heights", 433 | author: "Emily Bronte", 434 | coverImageUrl: 435 | "https://images-na.ssl-images-amazon.com/images/I/41dAOQBWAUL._SX325_BO1,204,203,200_.jpg", 436 | id: "141439556", 437 | pageCount: 416, 438 | publisher: "Penguin Classics", 439 | synopsis: 440 | "Lockwood, the new tenant of Thrushcross Grange, situated on the bleak Yorkshire moors, is forced to seek shelter one night at Wuthering Heights, the home of his landlord. There he discovers the history of the tempestuous events that took place years before. What unfolds is the tale of the intense love between the gypsy foundling Heathcliff and Catherine Earnshaw. Catherine, forced to choose between passionate, tortured Heathcliff and gentle, well-bred Edgar Linton, surrendered to the expectations of her class. As Heathcliff's bitterness and vengeance at his betrayal is visited upon the next generation, their innocent heirs must struggle to escape the legacy of the past." 441 | }, 442 | { 443 | title: "Frankenstein", 444 | author: "Mary Shelley", 445 | coverImageUrl: 446 | "https://images-na.ssl-images-amazon.com/images/I/51Ye-YRqLaL._SX312_BO1,204,203,200_.jpg", 447 | id: "486282112", 448 | pageCount: 166, 449 | publisher: "Dover Publications", 450 | synopsis: 451 | "Few creatures of horror have seized readers' imaginations and held them for so long as the anguished monster of Mary Shelley's Frankenstein. The story of Victor Frankenstein's terrible creation and the havoc it caused has enthralled generations of readers and inspired countless writers of horror and suspense." 452 | }, 453 | { 454 | title: "The Color Purple", 455 | author: "Alice Walker", 456 | coverImageUrl: 457 | "https://images-na.ssl-images-amazon.com/images/I/51u3siLE7hL._SX326_BO1,204,203,200_.jpg", 458 | id: "156028352", 459 | pageCount: 300, 460 | publisher: "Mariner Books", 461 | synopsis: 462 | "Taking place mostly in rural Georgia, the story focuses on the life of African-American women in the Southern United States in the 1930s, addressing numerous issues including their exceedingly low position in American social culture." 463 | }, 464 | { 465 | title: "Don Quixote", 466 | author: "Miguel de Cervantes", 467 | coverImageUrl: 468 | "https://images-na.ssl-images-amazon.com/images/I/51nBHIQv6zL._SX332_BO1,204,203,200_.jpg", 469 | id: "142437239", 470 | pageCount: 1072, 471 | publisher: "Penguin Classics", 472 | synopsis: 473 | "Don Quixote has become so entranced reading tales of chivalry that he decides to turn knight errant himself. In the company of his faithful squire, Sancho Panza, these exploits blossom in all sorts of wonderful ways. While Quixote's fancy often leads him astray—he tilts at windmills, imagining them to be giants—Sancho acquires cunning and a certain sagacity. Sane madman and wise fool, they roam the world together-and together they have haunted readers' imaginations for nearly four hundred years." 474 | }, 475 | { 476 | title: "Moby Dick", 477 | author: "Herman Melville", 478 | coverImageUrl: 479 | "https://images-na.ssl-images-amazon.com/images/I/41d8c5LhjtL._SX308_BO1,204,203,200_.jpg", 480 | id: "1853260088", 481 | pageCount: 544, 482 | publisher: "Wordsworth Editions", 483 | synopsis: 484 | "With an Introduction and Notes by David Herd. Lecturer in English and American Literature at the University of Kent at Canterbury Moby-Dick is the story of Captain Ahab's quest to avenge the whale that 'reaped' his leg. The quest is an obsession and the novel is a diabolical study of how a man becomes a fanatic. But it is also a hymn to democracy. Bent as the crew is on Ahab s appalling crusade, it is equally the image of a co-operative community at work: all hands dependent on all hands, each individual responsible for the security of each. Among the crew is Ishmael, the novel's narrator, ordinary sailor, and extraordinary reader. Digressive, allusive, vulgar, transcendent, the story Ishmael tells is above all an education: in the practice of whaling, in the art of writing." 485 | }, 486 | { 487 | title: "One Hundred Years of Solitude", 488 | author: "Gabriel Garcia Marquez", 489 | coverImageUrl: 490 | "https://images-na.ssl-images-amazon.com/images/I/618npmqoOsL._SX330_BO1,204,203,200_.jpg", 491 | id: "60883286", 492 | pageCount: 417, 493 | publisher: "Harper Perennial", 494 | synopsis: 495 | "One Hundred Years of Solitude tells the story of the rise and fall, birth and death of the mythical town of Macondo through the history of the Buendía family. Inventive, amusing, magnetic, sad, and alive with unforgettable men and women -- brimming with truth, compassion, and a lyrical magic that strikes the soul -- this novel is a masterpiece in the art of fiction." 496 | }, 497 | { 498 | title: "The Divine Comedy", 499 | author: "Dante Alighieri", 500 | coverImageUrl: 501 | "https://images-na.ssl-images-amazon.com/images/I/51RsV5hUtHL._SX348_BO1,204,203,200_.jpg", 502 | id: "451208633", 503 | pageCount: 928, 504 | publisher: "Berkley", 505 | synopsis: 506 | "Belonging in the immortal company of the great works of literature, Dante Alighieri’s poetic masterpiece, The Divine Comedy, is a moving human drama, an unforgettable visionary journey through the infinite torment of Hell, up the arduous slopes of Purgatory, and on to the glorious realm of Paradise—the sphere of universal harmony and eternal salvation." 507 | }, 508 | { 509 | title: "The Brothers Karamasov", 510 | author: "Fyodor Dostoevsky", 511 | coverImageUrl: 512 | "https://images-na.ssl-images-amazon.com/images/I/51FIyYKsCXL._SX333_BO1,204,203,200_.jpg", 513 | id: "374528373", 514 | pageCount: 824, 515 | publisher: "Farrar", 516 | synopsis: 517 | "The Brothers Karamasov is a murder mystery, a courtroom drama, and an exploration of erotic rivalry in a series of triangular love affairs involving the “wicked and sentimental” Fyodor Pavlovich Karamazov and his three sons―the impulsive and sensual Dmitri; the coldly rational Ivan; and the healthy, red-cheeked young novice Alyosha. Through the gripping events of their story, Dostoevsky portrays the whole of Russian life, is social and spiritual striving, in what was both the golden age and a tragic turning point in Russian culture." 518 | }, 519 | { 520 | title: "One Thousand Paper Cranes", 521 | author: "Ishii Takayuki", 522 | coverImageUrl: 523 | "https://images-na.ssl-images-amazon.com/images/I/41J5kJQ9W3L._SX305_BO1,204,203,200_.jpg", 524 | id: "440228433", 525 | pageCount: 112, 526 | publisher: "Laurel Leaf", 527 | synopsis: 528 | "Ten years after the atomic bomb was dropped on Hiroshima, Sadako Sasaki died as a result of atomic bomb disease. Sadako's determination to fold one thousand paper cranes and her courageous struggle with her illness inspired her classmates. After her death, they started a national campaign to build the Children's Peace Statue to remember Sadako and the many other children who were victims of the Hiroshima bombing. On top of the statue is a girl holding a large crane in her outstretched arms. Today in Hiroshima Peace Memorial Park, this statue of Sadako is beautifully decorated with thousands of paper cranes given by people throughout the world." 529 | }, 530 | { 531 | title: "Invisible Man", 532 | author: "Ralph Ellison", 533 | coverImageUrl: 534 | "https://images-na.ssl-images-amazon.com/images/I/51NIZqFLvJL._SX322_BO1,204,203,200_.jpg", 535 | id: "679732764", 536 | pageCount: 581, 537 | publisher: "Vintage Books", 538 | synopsis: 539 | 'A first novel by an unknown writer, it remained on the bestseller list for sixteen weeks, won the National Book Award for fiction, and established Ralph Ellison as one of the key writers of the century. The nameless narrator of the novel describes growing up in a black community in the South, attending a Negro college from which he is expelled, moving to New York and becoming the chief spokesman of the Harlem branch of "the Brotherhood", and retreating amid violence and confusion to the basement lair of the Invisible Man he imagines himself to be. The book is a passionate and witty tour de force of style, strongly influenced by T.S. Eliot\'s The Waste Land, Joyce, and Dostoevsky.' 540 | }, 541 | { 542 | title: "Roll of Thunder, Hear My Cry", 543 | author: "Mildred D. Taylor", 544 | coverImageUrl: 545 | "https://images-na.ssl-images-amazon.com/images/I/51hViZbiT3L._SX313_BO1,204,203,200_.jpg", 546 | id: "B012YXCQ6G", 547 | pageCount: 288, 548 | publisher: "Puffin Books", 549 | synopsis: 550 | "Set in Mississippi at the height of the Depression, this is the story of one family's struggle to maintain their integrity, pride, and independence in the face of racism and social injustice. And it is also Cassie's story—Cassie Logan, an independent girl who discovers over the course of an important year why having land of their own is so crucial to the Logan family, even as she learns to draw strength from her own sense of dignity and self-respect." 551 | }, 552 | { 553 | title: "The Guernsey Literary and Potato Peel Pie Society", 554 | author: "Mary Ann Shaffer and Annie Barrows", 555 | coverImageUrl: 556 | "https://images-na.ssl-images-amazon.com/images/I/51zSWUuPdTL._SX315_BO1,204,203,200_.jpg", 557 | id: "385341008", 558 | pageCount: 290, 559 | publisher: "Dial Press", 560 | synopsis: 561 | "As Juliet and her new correspondent exchange letters, Juliet is drawn into the world of this man and his friends—and what a wonderfully eccentric world it is. The Guernsey Literary and Potato Peel Pie Society—born as a spur-of-the-moment alibi when its members were discovered breaking curfew by the Germans occupying their island—boasts a charming, funny, deeply human cast of characters, from pig farmers to phrenologists, literature lovers all." 562 | }, 563 | { 564 | title: "Where the Crawdads Sing", 565 | author: "Delia Owens", 566 | coverImageUrl: 567 | "https://images-na.ssl-images-amazon.com/images/I/51j5p18mJNL._SX330_BO1,204,203,200_.jpg", 568 | id: "735219095", 569 | pageCount: 384, 570 | publisher: "G. P. Putnam & Sons", 571 | synopsis: 572 | 'For years, rumors of the "Marsh Girl" have haunted Barkley Cove, a quiet town on the North Carolina coast. So in late 1969, when handsome Chase Andrews is found dead, the locals immediately suspect Kya Clark, the so-called Marsh Girl. But Kya is not what they say. Sensitive and intelligent, she has survived for years alone in the marsh that she calls home, finding friends in the gulls and lessons in the sand. Then the time comes when she yearns to be touched and loved. When two young men from town become intrigued by her wild beauty, Kya opens herself to a new life--until the unthinkable happens.' 573 | }, 574 | { 575 | title: "The Great Alone", 576 | author: "Kristin Hannah", 577 | coverImageUrl: 578 | "https://images-na.ssl-images-amazon.com/images/I/511Dl74cE9L._SX328_BO1,204,203,200_.jpg", 579 | id: "312577230", 580 | pageCount: 448, 581 | publisher: "St. Martin's Press", 582 | synopsis: 583 | "Ernt Allbright, a former POW, comes home from the Vietnam war a changed and volatile man. When he loses yet another job, he makes an impulsive decision: he will move his family north, to Alaska, where they will live off the grid in America’s last true frontier" 584 | }, 585 | { 586 | title: "Before We Were Yours", 587 | author: "Lisa Wingate", 588 | coverImageUrl: 589 | "https://images-na.ssl-images-amazon.com/images/I/51Eqh97ZhFL._SX326_BO1,204,203,200_.jpg", 590 | id: "425284689", 591 | pageCount: 352, 592 | publisher: "Ballantine Books", 593 | synopsis: 594 | "Memphis, 1939. Twelve-year-old Rill Foss and her four younger siblings live a magical life aboard their family’s Mississippi River shantyboat. But when their father must rush their mother to the hospital one stormy night, Rill is left in charge—until strangers arrive in force. Wrenched from all that is familiar and thrown into a Tennessee Children’s Home Society orphanage, the Foss children are assured that they will soon be returned to their parents—but they quickly realize the dark truth. At the mercy of the facility’s cruel director, Rill fights to keep her sisters and brother together in a world of danger and uncertainty." 595 | }, 596 | { 597 | title: "Lost Roses", 598 | author: "Martha Hall Kelly", 599 | coverImageUrl: 600 | "https://images-na.ssl-images-amazon.com/images/I/51Tc9vMWawL._SX329_BO1,204,203,200_.jpg", 601 | id: "1524796379", 602 | pageCount: 448, 603 | publisher: "Ballantine Books", 604 | synopsis: 605 | "From the turbulent streets of St. Petersburg and aristocratic countryside estates to the avenues of Paris where a society of fallen Russian émigrés live to the mansions of Long Island, the lives of Eliza, Sofya, and Varinka will intersect in profound ways. In her newest powerful tale told through female-driven perspectives, Martha Hall Kelly celebrates the unbreakable bonds of women’s friendship, especially during the darkest days of history." 606 | }, 607 | { 608 | title: "When Breath Becomes Air", 609 | author: "Paul Kalanithi", 610 | coverImageUrl: 611 | "https://images-na.ssl-images-amazon.com/images/I/41csVfG7xxL._SX337_BO1,204,203,200_.jpg", 612 | id: "081298840X", 613 | pageCount: 228, 614 | publisher: "Random House", 615 | synopsis: 616 | "Paul Kalanithi died in March 2015, while working on this book, yet his words live on as a guide and a gift to us all. “I began to realize that coming face to face with my own mortality, in a sense, had changed nothing and everything,” he wrote. “Seven words from Samuel Beckett began to repeat in my head: ‘I can’t go on. I’ll go on.’” When Breath Becomes Air is an unforgettable, life-affirming reflection on the challenge of facing death and on the relationship between doctor and patient, from a brilliant writer who became both" 617 | }, 618 | { 619 | title: "Sapiens: A Brief History of Humankind", 620 | author: "Yuval Noah Harari", 621 | coverImageUrl: 622 | "https://images-na.ssl-images-amazon.com/images/I/41lZKXt1%2BML._SX332_BO1,204,203,200_.jpg", 623 | id: "62316117", 624 | pageCount: 464, 625 | publisher: "Harper Perennial", 626 | synopsis: 627 | "In Sapiens, Professor Yuval Noah Harari spans the whole of human history, from the very first humans to walk the earth to the radical—and sometimes devastating—breakthroughs of the cognitive, agricultural, and scientific revolutions. Drawing on insights from biology, anthropology, paleontology, and economics, and incorporating full-color illustrations throughout the text, Harari explores how the currents of history have shaped our human societies, the animals and plants around us, and even our personalities. Have we become happier as history has unfolded? Can we ever free our behavior from the legacy of our ancestors? And what, if anything, can we do to influence the course of the centuries to come?" 628 | }, 629 | { 630 | title: "Gun, Germs, and Steel", 631 | author: "Jared Diamond", 632 | coverImageUrl: 633 | "https://images-na.ssl-images-amazon.com/images/I/5101H2lhtXL._SX329_BO1,204,203,200_.jpg", 634 | id: "393354326", 635 | pageCount: 528, 636 | publisher: "W. W. Norton & Co.", 637 | synopsis: 638 | 'In this "artful, informative, and delightful" (William H. McNeill, New York Review of Books) book, Jared Diamond convincingly argues that geographical and environmental factors shaped the modern world. Societies that had had a head start in food production advanced beyond the hunter-gatherer stage, and then developed religion --as well as nasty germs and potent weapons of war --and adventured on sea and land to conquer and decimate preliterate cultures. A major advance in our understanding of human societies, Guns, Germs, and Steel chronicles the way that the modern world came to be and stunningly dismantles racially based theories of human history. Winner of the Pulitzer Prize, the Phi Beta Kappa Award in Science, the Rhone-Poulenc Prize, and the Commonwealth club of California\'s Gold Medal.' 639 | }, 640 | { 641 | title: "Mere Christianity", 642 | author: "C. S. Lewis", 643 | coverImageUrl: 644 | "https://images-na.ssl-images-amazon.com/images/I/51IxCmedZ%2BL._SX326_BO1,204,203,200_.jpg", 645 | id: "60652926", 646 | pageCount: 227, 647 | publisher: "HarperOne", 648 | synopsis: 649 | "In the classic Mere Christianity, C.S. Lewis, the most important writer of the 20th century, explores the common ground upon which all of those of Christian faith stand together. Bringing together Lewis’ legendary broadcast talks during World War Two from his three previous books The Case for Christianity, Christian Behavior, and Beyond Personality, Mere Christianity provides an unequaled opportunity for believers and nonbelievers alike to hear this powerful apologetic for the Christian faith." 650 | }, 651 | { 652 | title: "A Brief History of Time", 653 | author: "Stephen Hawking", 654 | coverImageUrl: 655 | "https://images-na.ssl-images-amazon.com/images/I/51%2BGySc8ExL._SX333_BO1,204,203,200_.jpg", 656 | id: "553380168", 657 | pageCount: 212, 658 | publisher: "Bantam", 659 | synopsis: 660 | "Told in language we all can understand, A Brief History of Time plunges into the exotic realms of black holes and quarks, of antimatter and “arrows of time,” of the big bang and a bigger God—where the possibilities are wondrous and unexpected. With exciting images and profound imagination, Stephen Hawking brings us closer to the ultimate secrets at the very heart of creation." 661 | }, 662 | { 663 | title: 664 | "This Is Water: Some Thoughts, Delivered on a Significant Occasion, about Living a Compassionate Life", 665 | author: "David Foster Wallace", 666 | coverImageUrl: 667 | "https://images-na.ssl-images-amazon.com/images/I/31sf4AgyQAL._SX340_BO1,204,203,200_.jpg", 668 | id: "316068225", 669 | pageCount: 137, 670 | publisher: "Little, Brown and Co.", 671 | synopsis: 672 | "Only once did David Foster Wallace give a public talk on his views on life, during a commencement address given in 2005 at Kenyon College. The speech is reprinted for the first time in book form in THIS IS WATER. How does one keep from going through their comfortable, prosperous adult life unconsciously? How do we get ourselves out of the foreground of our thoughts and achieve compassion? The speech captures Wallace's electric intellect as well as his grace in attention to others. After his death, it became a treasured piece of writing reprinted in The Wall Street Journal and the London Times, commented on endlessly in blogs, and emailed from friend to friend." 673 | }, 674 | { 675 | title: "Dear Friend, from My Life I Write to You in Your Life", 676 | author: "Yiyun Li", 677 | coverImageUrl: 678 | "https://images-na.ssl-images-amazon.com/images/I/51XSWDI%2BcmL._SX322_BO1,204,203,200_.jpg", 679 | id: "399589104", 680 | pageCount: 224, 681 | publisher: "Random House", 682 | synopsis: 683 | "Startlingly original and shining with quiet wisdom, this is a luminous account of a life lived with books. Written over two years while the author battled suicidal depression, Dear Friend, from My Life I Write to You in Your Life is a painful and yet richly affirming examination of what makes life worth living" 684 | }, 685 | { 686 | title: "The Best We Could Do", 687 | author: "Thi Bui", 688 | coverImageUrl: 689 | "https://images-na.ssl-images-amazon.com/images/I/51xQWI0Be9L._SX359_BO1,204,203,200_.jpg", 690 | id: "1419718789", 691 | pageCount: 336, 692 | publisher: "Harry N. Abrams", 693 | synopsis: 694 | "In what Pulitzer Prize–winning novelist Viet Thanh Nguyen calls “a book to break your heart and heal it,” The Best We Could Do brings to life Thi Bui’s journey of understanding, and provides inspiration to all of those who search for a better future while longing for a simpler past." 695 | }, 696 | { 697 | title: "The Little Prince", 698 | author: "Antoine de St. Exupéry", 699 | coverImageUrl: 700 | "https://images-na.ssl-images-amazon.com/images/I/4186P0mACWL._SX336_BO1,204,203,200_.jpg", 701 | id: "156012197", 702 | pageCount: 96, 703 | publisher: "Mariner Books", 704 | synopsis: 705 | "Few stories are as widely read and as universally cherished by children and adults alike as The Little Prince. Richard Howard's translation of the beloved classic beautifully reflects Saint-Exupéry's unique and gifted style. Howard, an acclaimed poet and one of the preeminent translators of our time, has excelled in bringing the English text as close as possible to the French, in language, style, and most important, spirit. The artwork in this edition has been restored to match in detail and in color Saint-Exupéry's original artwork. Combining Richard Howard's translation with restored original art, this definitive English-language edition of The Little Prince will capture the hearts of readers of all ages." 706 | }, 707 | { 708 | title: "The Phantom Tolbooth", 709 | author: "Norton Juster", 710 | coverImageUrl: 711 | "https://images-na.ssl-images-amazon.com/images/I/51QC8X%2BTDyL._SX340_BO1,204,203,200_.jpg", 712 | id: "394820371", 713 | pageCount: 272, 714 | publisher: "Bullseye Books", 715 | synopsis: 716 | "For Milo, everything’s a bore. When a tollbooth mysteriously appears in his room, he drives through only because he’s got nothing better to do. But on the other side, things seem different. Milo visits the Island of Conclusions (you get there by jumping), learns about time from a ticking watchdog named Tock, and even embarks on a quest to rescue Rhyme and Reason! Somewhere along the way, Milo realizes something astonishing. Life is far from dull. In fact, it’s exciting beyond his wildest dreams. . . ." 717 | }, 718 | { 719 | title: "Where the Red Fern Grows", 720 | author: "Wilson Rawls", 721 | coverImageUrl: 722 | "https://images-na.ssl-images-amazon.com/images/I/51%2BxGDb9JYL._SX338_BO1,204,203,200_.jpg", 723 | id: "440412676", 724 | pageCount: 304, 725 | publisher: "Yearling", 726 | synopsis: 727 | "Billy has long dreamt of owning not one, but two, dogs. So when he’s finally able to save up enough money for two pups to call his own—Old Dan and Little Ann—he’s ecstatic. It doesn’t matter that times are tough; together they’ll roam the hills of the Ozarks.\n Soon Billy and his hounds become the finest hunting team in the valley. Stories of their great achievements spread throughout the region, and the combination of Old Dan’s brawn, Little Ann’s brains, and Billy’s sheer will seems unbeatable. But tragedy awaits these determined hunters—now friends—and Billy learns that hope can grow out of despair, and that the seeds of the future can come from the scars of the past." 728 | }, 729 | { 730 | title: "Bridge to Terebithia", 731 | author: "Katherine Paterson", 732 | coverImageUrl: 733 | "https://images-na.ssl-images-amazon.com/images/I/51cYMLz-YQL._SX334_BO1,204,203,200_.jpg", 734 | id: "64401847", 735 | pageCount: 144, 736 | publisher: "HarperCollins", 737 | synopsis: 738 | "Jess Aarons has been practicing all summer so he can be the fastest runner in the fifth grade. And he almost is, until the new girl in school, Leslie Burke, outpaces him. The two become fast friends and spend most days in the woods behind Leslie’s house, where they invent an enchanted land called Terabithia. One morning, Leslie goes to Terabithia without Jess and a tragedy occurs. It will take the love of his family and the strength that Leslie has given him for Jess to be able to deal with his grief" 739 | }, 740 | { 741 | title: "Holes", 742 | author: "Louis Sachar", 743 | coverImageUrl: 744 | "https://images-na.ssl-images-amazon.com/images/I/51M-Bp5PcPL._SX338_BO1,204,203,200_.jpg", 745 | id: "440414806", 746 | pageCount: 233, 747 | publisher: "Yearling", 748 | synopsis: 749 | "Stanley Yelnats is under a curse. A curse that began with his no-good-dirty-rotten-pig-stealing-great-great-grandfather and has since followed generations of Yelnatses. Now Stanley has been unjustly sent to a boys’ detention center, Camp Green Lake, where the boys build character by spending all day, every day digging holes exactly five feet wide and five feet deep. There is no lake at Camp Green Lake. But there are an awful lot of holes." 750 | }, 751 | { 752 | title: "Tuck Everlasting", 753 | author: "Natalie Babbitt", 754 | coverImageUrl: 755 | "https://images-na.ssl-images-amazon.com/images/I/51pjcLaWOSL._SX337_BO1,204,203,200_.jpg", 756 | id: "312369816", 757 | pageCount: 160, 758 | publisher: "Square Fish", 759 | synopsis: 760 | "Is eternal life a blessing or a curse? That is what young Winnie Foster must decide when she discovers a spring on her family’s property whose waters grant immortality. Members of the Tuck family, having drunk from the spring, tell Winnie of their experiences watching life go by and never growing older.\n\nBut then Winnie must decide whether or not to keep the Tucks’ secret―and whether or not to join them on their never-ending journey." 761 | }, 762 | { 763 | title: "Winnie the Pooh", 764 | author: "A. A. Milne", 765 | coverImageUrl: 766 | "https://images-na.ssl-images-amazon.com/images/I/41O90KgELyL._SX345_BO1,204,203,200_.jpg", 767 | id: "525555315", 768 | pageCount: 176, 769 | publisher: "Dutton Books for Young Readers", 770 | synopsis: 771 | "For over ninety years, Winnie-the-Pooh and his friends—Piglet, Owl, Tigger, and the ever doleful Eeyore—have endured as the unforgettable creations of A.A. Milne, who wrote this book for his son, Christopher Robin, and Ernest H. Shepard, who lovingly gave Pooh and his companions shape through his illustrations." 772 | }, 773 | { 774 | title: "Learning React Native: Building Native Mobile Apps with JavaScript", 775 | author: "Bonnie Eisenman", 776 | coverImageUrl: 777 | "https://images-na.ssl-images-amazon.com/images/I/518ctEpEw1L._SX379_BO1,204,203,200_.jpg", 778 | id: "1491989149", 779 | pageCount: 242, 780 | publisher: "O'Reilly Media", 781 | synopsis: 782 | "Get a practical introduction to React Native, the JavaScript framework for writing and deploying fully featured mobile apps that render natively. The second edition of this hands-on guide shows you how to build applications that target iOS, Android, and other mobile platforms instead of browsers—apps that can access platform features such as the camera, user location, and local storage." 783 | } 784 | ] 785 | -------------------------------------------------------------------------------- /docs/js/app.fd4e1ef4.js: -------------------------------------------------------------------------------- 1 | (function(e){function t(t){for(var i,s,r=t[0],l=t[1],h=t[2],u=0,c=[];u