├── .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 | 9 | 10 | 17 | 18 | -------------------------------------------------------------------------------- /src/Cart.vue: -------------------------------------------------------------------------------- 1 | 8 | 20 | -------------------------------------------------------------------------------- /src/Category.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /src/Home.vue: -------------------------------------------------------------------------------- 1 | 32 | 54 | -------------------------------------------------------------------------------- /src/Me.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/images/cart.svg: -------------------------------------------------------------------------------- 1 | 7 | 13 | 16 | 17 | -------------------------------------------------------------------------------- /src/assets/images/category.svg: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 16 | 18 | 19 | 20 | 21 | 25 | 26 | -------------------------------------------------------------------------------- /src/assets/images/home.svg: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 16 | 18 | 19 | 20 | 21 | 26 | 27 | -------------------------------------------------------------------------------- /src/assets/images/me.svg: -------------------------------------------------------------------------------- 1 | 7 | 8 | 10 | 11 | 17 | 18 | 19 | 20 | 21 | 25 | 26 | -------------------------------------------------------------------------------- /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 | 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 | 22 | 23 | -------------------------------------------------------------------------------- /src/components/announcement.vue: -------------------------------------------------------------------------------- 1 | 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 | 13 | 14 | -------------------------------------------------------------------------------- /src/components/slider.vue: -------------------------------------------------------------------------------- 1 | 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 | 27 | 33 | -------------------------------------------------------------------------------- /src/fixtures/covers/2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Created with Sketch. 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/fixtures/covers/5.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Sketch. 5 | 6 | 7 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------