├── .babelrc
├── .gitignore
├── README.md
├── index.html
├── karma.conf.js
├── package.json
├── src
├── App.vue
├── Cart.vue
├── Category.vue
├── Home.vue
├── Me.vue
├── assets
│ ├── images
│ │ ├── cart.svg
│ │ ├── category.svg
│ │ ├── home.svg
│ │ └── me.svg
│ └── less
│ │ └── site.less
├── books
│ ├── detail.vue
│ ├── list.less
│ └── list.vue
├── components
│ ├── announcement.vue
│ ├── dialog.less
│ ├── dialog.vue
│ ├── slider.vue
│ ├── tabs.less
│ └── tabs.vue
├── fixtures
│ ├── covers
│ │ ├── 1.svg
│ │ ├── 2.svg
│ │ ├── 3.svg
│ │ ├── 4.svg
│ │ ├── 5.svg
│ │ └── 6.svg
│ ├── faker.js
│ ├── home.json
│ └── sliders
│ │ ├── t1.svg
│ │ └── t2.svg
└── main.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["es2015", { "modules": false }]
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | dist/
4 | npm-debug.log
5 | .idea/
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 《Vue2实践揭秘》 - 第3~5章示例源码
2 |
3 |
4 | > 2017-5-28 据不少读者的来信说之前这个示例源码一直无法运行,这是由于本书经历出版周期后WebPack和vue的相关工具都进行了升级,新版本的WebPack配置发生了不少的变更所至。
5 | > 我会在简书中对新变更的内容进行一些更新说明以作为本书的迭代,新的内容会在后续的发行版本中更新到书中。
6 |
7 |
8 | ## 环境说明
9 |
10 | - NodeJS v7.10.0
11 | - NPM v4.6.1
12 | - Vue v2.3.3
13 | - vue-cli v2.8.2
14 |
15 |
16 | ## 安装依赖包
17 |
18 | ``` bash
19 | npm i
20 | ```
21 |
22 | ## 运行本示例
23 |
24 | ```
25 | npm run dev
26 | ```
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | vue-bookstore
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // Generated on Thu Oct 13 2016 16:51:27 GMT+0800 (CST)
3 |
4 | module.exports = function(config) {
5 |
6 | config.set({
7 |
8 | // frameworks to use
9 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
10 | frameworks: ['mocha','sinon-chai'],
11 |
12 |
13 | // list of files / patterns to load in the browser
14 | files: [
15 | '"test/**/*spec.js"'
16 | ],
17 |
18 | preprocessors: {
19 | // add webpack as preprocessor
20 | 'test/**/*spec.js': ['webpack', 'sourcemap']
21 | },
22 |
23 | webpack: {
24 | devtool: '#eval-source-map',
25 | module: {
26 | loaders: [
27 | {test: /\.vue$/, loader: "vue"},
28 | {test: /\.js/, loader: "babel"},
29 | {test: /\.html$/, loader: "vue-html"}
30 | ]
31 | }
32 | },
33 |
34 | reporters: ['spec'],
35 |
36 | // enable / disable watching file and executing tests whenever any file changes
37 | autoWatch: true,
38 |
39 | // start these browsers
40 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
41 | browsers: ['PhantomJS']
42 | })
43 | }
44 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-bookstore",
3 | "description": "The bookstore example for Vue2 in Action",
4 | "author": "csharp2002@hotmail.com",
5 | "private": true,
6 | "scripts": {
7 | "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
8 | "build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
9 | },
10 | "dependencies": {
11 | "swiper": "^3.4.0",
12 | "vue": "^2.3.3",
13 | "vue-router": "^2.0.1"
14 | },
15 | "devDependencies": {
16 | "babel-core": "^6.0.0",
17 | "babel-loader": "^6.0.0",
18 | "babel-plugin-transform-runtime": "^6.0.0",
19 | "babel-preset-es2015": "^6.0.0",
20 | "babel-preset-stage-2": "^6.0.0",
21 | "babel-register": "^6.0.0",
22 | "chai": "^3.5.0",
23 | "cross-env": "^3.0.0",
24 | "css-loader": "^0.25.0",
25 | "file-loader": "^0.9.0",
26 | "json-loader": "^0.5.4",
27 | "karma": "^1.3.0",
28 | "karma-chrome-launcher": "^2.0.0",
29 | "karma-coffee-preprocessor": "^1.0.1",
30 | "karma-coverage": "^1.1.1",
31 | "karma-mocha": "^1.2.0",
32 | "karma-phantomjs-launcher": "^1.0.2",
33 | "karma-sinon-chai": "^1.2.0",
34 | "karma-sourcemap-loader": "^0.3.7",
35 | "karma-spec-reporter": "0.0.26",
36 | "karma-webpack": "^1.8.0",
37 | "less": "^2.7.1",
38 | "less-loader": "^2.2.3",
39 | "mocha": "^3.1.0",
40 | "sinon": "^2.3.2",
41 | "sinon-chai": "^2.8.0",
42 | "style-loader": "^0.13.1",
43 | "url-loader": "^0.5.7",
44 | "vue-hot-reload-api": "^1.2.0",
45 | "vue-html-loader": "^1.0.0",
46 | "vue-loader": "^12.1.0",
47 | "vue-resource": "^1.0.3",
48 | "vue-style-loader": "^1.0.0",
49 | "vue-template-compiler": "^2.3.3",
50 | "webpack": "^2.6.1",
51 | "webpack-dev-server": "^2.4.5"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
17 |
18 |
--------------------------------------------------------------------------------
/src/Cart.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 您的购物车空空如也
5 |
6 |
7 |
8 |
20 |
--------------------------------------------------------------------------------
/src/Category.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
分类
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 今日上架的图书全部8折
8 |
9 |
10 |
11 |
14 |
15 |
16 |
17 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
{{ selected.title }}
27 |
28 |
29 |
30 |
31 |
32 |
54 |
--------------------------------------------------------------------------------
/src/Me.vue:
--------------------------------------------------------------------------------
1 |
2 | 我
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/images/cart.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/images/category.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/images/home.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/images/me.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/less/site.less:
--------------------------------------------------------------------------------
1 | html, body {
2 | height: 100%;
3 | width: 100%;
4 | margin: 0;
5 | padding: 0;
6 | }
7 |
8 | body {
9 | font-family: Helvetica, sans-serif;
10 | background-color: #EFEFEF;
11 | }
12 |
13 | .content {
14 | height: 100%;
15 | margin: 0 auto 60px auto;
16 | }
17 |
18 | .section {
19 | background: #fff;
20 | margin-bottom: 5px;
21 | }
--------------------------------------------------------------------------------
/src/books/detail.vue:
--------------------------------------------------------------------------------
1 |
2 | 书目详情
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/books/list.less:
--------------------------------------------------------------------------------
1 | .book-list {
2 | margin: 0 14px;
3 | & > .header {
4 | display: table;
5 | width: 100%;
6 | border-bottom: 1px solid #efefef;
7 |
8 | & > .heading {
9 | display: table-cell;
10 | padding: 12px;
11 | }
12 |
13 | & > .more {
14 | display: table-cell;
15 | text-align: right;
16 | vertical-align: middle;
17 | font-size: 10px;
18 | color: #cccccc;
19 | padding-right: 14px;
20 | }
21 | }
22 |
23 | & .book-items {
24 | display: table;
25 | width: 100%;
26 | padding-top: 12px;
27 | }
28 |
29 | & .book {
30 | font-size: 10px;
31 | text-align: center;
32 | float: left;
33 | width: 33%;
34 | margin: 12px auto;
35 | & .cover {
36 | }
37 |
38 | & .title, & .authors {
39 | margin: 5px auto;
40 | width: 100px;
41 | text-overflow: ellipsis;
42 | overflow: hidden;
43 | white-space: nowrap;
44 | }
45 |
46 | & .authors {
47 | color: #898989;
48 | margin-top: 10px;
49 | text-align: left;
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/src/books/list.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
13 |
14 |
![]()
15 |
16 |
{{ book.title }}
17 |
{{ book.authors | join }}
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/components/announcement.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{content}}
5 |
6 |
7 |
10 |
--------------------------------------------------------------------------------
/src/components/dialog.less:
--------------------------------------------------------------------------------
1 | .dialog-wrapper {
2 | &.open {
3 | display: block;
4 | }
5 | height: 100%;
6 | display: none;
7 | & > .overlay {
8 | background: rgba(0, 0, 0, 0.3);
9 | z-index: 1;
10 | position: absolute;
11 | left: 0px;
12 | top: 0;
13 | right: 0;
14 | bottom: 0;
15 | }
16 |
17 | & > .dialog {
18 | z-index: 10;
19 | background: #fff;
20 | position: fixed;
21 | top: 24px;
22 | left: 24px;
23 | right: 24px;
24 | bottom: 24px;
25 | padding: 24px 14px;
26 | box-shadow: 0 0 10px rgba(0, 0, 0, .8);
27 | & heading {
28 | padding: 12px;
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/src/components/dialog.vue:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/components/slider.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
![]()
8 |
9 |
10 |
12 |
13 |
14 |
47 |
--------------------------------------------------------------------------------
/src/components/tabs.less:
--------------------------------------------------------------------------------
1 | .tabs {
2 | position: fixed;
3 | bottom: 0;
4 | left: 0;
5 | background-color: #fff;
6 | box-shadow: 0 2px 4px #000;
7 | width: 100%;
8 |
9 | & > ul, & > ul > li {
10 | margin: 0;
11 | padding: 0;
12 | }
13 |
14 | ul {
15 | display: table;
16 | width: 100%;
17 | & > li {
18 | text-align: center;
19 | font-size: 10px;
20 | display: table-cell;
21 | padding: 8px 12px;
22 | &.active {
23 | color: #D0021B;
24 | }
25 |
26 | }
27 | }
28 | }
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/components/tabs.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |

7 |
8 | 首页
9 |
10 |
11 |
12 |

13 |
14 | 分类
15 |
16 |
17 |
18 | 购物车
19 |
20 |
21 |
22 | 我
23 |
24 |
25 |
26 |
27 |
33 |
--------------------------------------------------------------------------------
/src/fixtures/covers/2.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/fixtures/covers/5.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/fixtures/faker.js:
--------------------------------------------------------------------------------
1 | import HomePageData from "./home.json"
2 |
3 | var slider_images = require.context('./sliders', false, /\.(png|jpg|gif|svg)$/)
4 | var cover_images = require.context('./covers', false, /\.(png|jpg|gif|svg)$/)
5 |
6 |
7 | HomePageData.top.forEach((x)=> {
8 | x.img_url = slider_images('./' + x.img_url)
9 | })
10 |
11 | HomePageData.promotions.forEach((x)=> {
12 | x.img_url = cover_images('./' + x.img_url)
13 | })
14 |
15 | export default {
16 | getHomeData() {
17 | return HomePageData
18 | }
19 | }
--------------------------------------------------------------------------------
/src/fixtures/home.json:
--------------------------------------------------------------------------------
1 | {
2 | "announcement": {
3 | "text": "看看大神们都在用什么前端工具吧",
4 | "book_id": 1
5 | },
6 | "top": [
7 | {
8 | "id": 7,
9 | "img_url": "t1.svg"
10 | },
11 | {
12 | "id": 8,
13 | "img_url": "t2.svg"
14 | }
15 | ],
16 | "promotions": [
17 | {
18 | "id": 1,
19 | "title": "揭开数据真相:从小白到数据分析达人",
20 | "authors": [
21 | "Edward Zaccaro, Daniel Zaccaro"
22 | ],
23 | "img_url": "1.svg"
24 | },
25 | {
26 | "id": 2,
27 | "title": "Android 高级进阶",
28 | "authors": [
29 | "顾浩鑫"
30 | ],
31 | "img_url": "2.svg"
32 | },
33 | {
34 | "id": 3,
35 | "title": "淘宝天猫电商运营与数据化选品完全手册",
36 | "authors": [
37 | "老夏"
38 | ],
39 | "img_url": "3.svg"
40 | },
41 | {
42 | "id": 4,
43 | "title": "大数据架构详解:从数据获取到深度学习",
44 | "authors": [
45 | "朱洁",
46 | "罗华霖"
47 | ],
48 | "img_url": "4.svg"
49 | },
50 | {
51 | "id": 5,
52 | "title": "Meteor全栈开发",
53 | "authors": [
54 | "杜亦舒"
55 | ],
56 | "img_url": "5.svg"
57 | },
58 | {
59 | "id": 6,
60 | "title": "Kubernetes权威指南:从Docker到Kubernetes实践全接触(第2版)",
61 | "authors": [
62 | "龚正",
63 | "吴治辉",
64 | "王伟",
65 | "崔秀龙",
66 | "闫健勇"
67 | ],
68 | "img_url": "6.svg"
69 | }
70 | ],
71 | "recommended": [
72 | ]
73 | }
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import VueRouter from 'vue-router'
3 | import VueResource from 'vue-resource'
4 | import App from './App.vue'
5 |
6 | //import Home from './components/home.vue'
7 | import Category from './Category.vue'
8 | import ShoppingCart from './Cart.vue'
9 | import Me from './Me.vue'
10 | import BookDetail from "./books/Detail.vue"
11 |
12 | const Home = resolve => require(['./Home.vue'], resolve)
13 |
14 |
15 | Vue.use(VueRouter)
16 | Vue.use(VueResource)
17 |
18 | const router = new VueRouter({
19 | mode: 'history',
20 | base: __dirname,
21 | linkActiveClass: "active",
22 | routes: [
23 | {name:'Home', path: '/', component: Home},
24 | {name:'Categories',path: '/categories', component: Category},
25 | {name:'ShoppingCart',path: '/shopping-cart', component: ShoppingCart},
26 | {name:'Me',path: '/me', component: Me},
27 | {name:'BookDetail',path: '/books/:id', component: BookDetail}
28 | ]
29 | })
30 |
31 | new Vue({
32 | el: '#app',
33 | router,
34 | render: h => h(App)
35 | })
36 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | entry: './src/main.js',
6 | output: {
7 | path: path.resolve(__dirname, './dist'),
8 | publicPath: '/dist/',
9 | filename: 'build.js'
10 | },
11 | module: {
12 | rules: [
13 | {
14 | test: /\.vue$/,
15 | loader: 'vue-loader'
16 | },
17 | {
18 | test: /\.js$/,
19 | loader: 'babel-loader',
20 | exclude: /node_modules/
21 | },
22 | {
23 | test: /\.css/,
24 | loader: "style-loader!css-loader"
25 | },
26 | {
27 | test: /\.less$/,
28 | loader: "style-loader!css-loader!less-loader"
29 | },
30 | {
31 | test: /\.json$/,
32 | loader: "json-loader"
33 | },
34 | {
35 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
36 | loader: 'url-loader'
37 | },
38 | {
39 | test: /\.(png|jpg|gif|svg)$/,
40 | loader: 'file-loader',
41 | query: {
42 | name: '[name].[ext]?[hash]'
43 | }
44 | }
45 | ]
46 | },
47 | resolve: {
48 | alias: {
49 | 'vue$': 'vue/dist/vue.esm.js'
50 | }
51 | },
52 | devServer: {
53 | historyApiFallback: true,
54 | noInfo: true
55 | },
56 | devtool: '#eval-source-map'
57 | };
58 |
59 | if (process.env.NODE_ENV === 'production') {
60 | module.exports.devtool = '#source-map';
61 |
62 | // http://vue-loader.vuejs.org/en/workflow/production.html
63 | module.exports.plugins = (module.exports.plugins || []).concat([
64 | new webpack.DefinePlugin({
65 | 'process.env': {
66 | NODE_ENV: '"production"'
67 | }
68 | }),
69 | new webpack.optimize.UglifyJsPlugin({
70 | compress: {
71 | warnings: false
72 | }
73 | })
74 | ])
75 | }
76 |
--------------------------------------------------------------------------------