├── .circleci
└── config.yml
├── .gitignore
├── .prettierignore
├── LICENSE
├── README.md
├── commands
├── clear-commit-msg.sh
├── deploy.sh
└── preview.js
├── jest.config.js
├── package.json
├── public
├── favicon.ico
├── img
│ └── icons
│ │ ├── android-chrome-192x192.png
│ │ ├── android-chrome-512x512.png
│ │ ├── apple-touch-icon-120x120.png
│ │ ├── apple-touch-icon-152x152.png
│ │ ├── apple-touch-icon-180x180.png
│ │ ├── apple-touch-icon-60x60.png
│ │ ├── apple-touch-icon-76x76.png
│ │ ├── apple-touch-icon.png
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ ├── msapplication-icon-144x144.png
│ │ ├── mstile-150x150.png
│ │ └── safari-pinned-tab.svg
├── index.html
├── manifest.json
├── robots.txt
└── service-worker.js
├── src
├── App.vue
├── assets
│ ├── icons
│ │ ├── author.svg
│ │ ├── document.svg
│ │ ├── explore.svg
│ │ ├── github.svg
│ │ └── index.js
│ ├── images
│ │ ├── logo.png
│ │ ├── vue-cli-manage-cli-plugin.png
│ │ ├── vue-cli-manage-project-dependencies.png
│ │ └── vue-cli-npm.png
│ └── styles
│ │ ├── common.scss
│ │ ├── element.scss
│ │ ├── mixins.scss
│ │ ├── style.scss
│ │ └── variables.scss
├── components
│ ├── Advertisement.vue
│ ├── EditDialog.vue
│ ├── Icon.vue
│ ├── Instruction.vue
│ ├── icons
│ │ └── Arrow.vue
│ └── markdown
│ │ ├── Index.vue
│ │ ├── MarkdownPreview.vue
│ │ └── markdown.css
├── filters.js
├── global.js
├── helper
│ ├── ajax.js
│ ├── apis
│ │ └── index.js
│ ├── auth.js
│ ├── document.js
│ ├── index.js
│ ├── lodash.js
│ └── utils.js
├── main.js
├── mixins
│ └── metaMixin.js
├── pages
│ ├── ExploreMore.vue
│ ├── Homepage.vue
│ └── partials
│ │ └── NotFound.vue
├── registerServiceWorker.js
├── router
│ ├── beforeEachHooks.js
│ ├── commonRoutes.js
│ ├── index.js
│ └── routes
│ │ ├── index.js
│ │ └── main.js
└── store
│ ├── index.js
│ └── modules
│ ├── demo
│ ├── actions.js
│ ├── getters.js
│ ├── index.js
│ └── mutations.js
│ └── index.js
├── tests
└── unit
│ ├── .eslintrc.js
│ └── Arrow.spec.js
├── vue.config.js
└── yarn.lock
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | # Javascript Node CircleCI 2.0 configuration file
2 | #
3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details
4 | #
5 | version: 2
6 | jobs:
7 | build:
8 | docker:
9 | # specify the version you desire here
10 | - image: circleci/node:8.9.1
11 | # circleci branch only master
12 | branches:
13 | only:
14 | - master
15 | - /rc-.*/
16 |
17 | # Specify service dependencies here if necessary
18 | # CircleCI maintains a library of pre-built images
19 | # documented at https://circleci.com/docs/2.0/circleci-images/
20 | # - image: circleci/mongo:3.4.4
21 |
22 | working_directory: ~/repo
23 |
24 | steps:
25 | - checkout
26 |
27 | # Download and cache dependencies
28 | - restore_cache:
29 | keys:
30 | - v1-dependencies-{{ checksum "package.json" }}
31 | # fallback to using the latest cache if no exact match is found
32 | - v1-dependencies-
33 |
34 | - run: yarn install
35 |
36 | - save_cache:
37 | paths:
38 | - node_modules
39 | key: v1-dependencies-{{ checksum "package.json" }}
40 |
41 | # run tests!
42 | # - run: yarn test
43 |
44 | # run build
45 | # - run: git config --global user.email "jeffygisgreat@gmail.com"
46 | # - run: git config --global user.name "nicejade"
47 | # - run: yarn run deploy
--------------------------------------------------------------------------------
/.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 |
23 | # Test
24 | tests/coverage
25 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
3 | dist
4 |
5 | .circleci
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 JadeYang(杨琼璞)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |

2 |
3 | Awesome Vue-Cli3 Example
4 |
5 |
6 |
7 | 🦅 (Vue Webpack Vuex Vue-router Element-ui/...) out of the box
8 |
9 |
10 |
11 |
12 | 🦅 Awesome example for rapid Vue.js development using
vue-cli3.
13 |
14 |
15 |
16 |
17 |
37 |
38 | #### English | [中文](https://vue-cli3.lovejade.cn/??utm_source=awesome-vue-cli3-example)
39 |
40 | ## Goal and Philosophy
41 |
42 | To facilitate developers to use `Vue-cli3` more conveniently, and to build Web applications more efficiently and reasonably.
43 |
44 | ## Prerequisites
45 |
46 | [Node.js](https://nodejs.org/en/) (>= 8.*), Npm version 4+(Yarn preferred), and [Git](https://git-scm.com/).
47 |
48 | ## Online Demo
49 |
50 | Online Demo Page: https://vue-cli3.lovejade.cn
51 |
52 | ## Usage
53 |
54 | ```bash
55 | # 🎉 clone the project
56 | git clone https://github.com/nicejade/awesome-vue-cli3-example.git your-project-name
57 | cd your-project-name
58 |
59 | # ➕ install dependencies & start dev
60 | yarn && yarn start
61 | ```
62 |
63 | ## Advantage
64 |
65 | This boilerplate built on [Vue-Cli3](https://github.com/vuejs/vue-cli/) inheriting the previous [vue-boilerplate-template](https://github.com/nicejade/vue-boilerplate-template) project to explore the more efficient construction for high-quality web applications (recommended to read [开箱即用的 Vue Webpack 脚手架模版](https://www.jeffjade.com/2018/05/20/140-vue-webpack-boilerplate-template/)), Some optimization items are designed, the specific list as followed:
66 |
67 | - [x] Import & configure [Vue-router](https://router.vuejs.org/zh/) to make the building of a single-page application (SPA) breeze;
68 | - [x] Import & configure [Vuex](https://vuex.vuejs.org/zh/) to handle the management of status for application development;
69 | - [x] Import [Element-ui](http://element.eleme.io/#/zh-CN) to build a website quickly without paying too much attention to the UI;
70 | - [x] Import & encapsulating [Axios](https://github.com/axios/axios) to response the Http requests more elegant;
71 | - [x] Import [Dayjs](https://github.com/iamkun/dayjs) to handle date-time correlation in a slight cost;
72 | - [x] Import & encapsulate the [Marked]() plugin so that it can be used as a rich text editor,and it also can achieve `Markdown` to draw the page due to its parsing function.
73 | - [x] Import [vue-meta](https://github.com/declandewet/vue-meta) plugin so that allows you to manage your app's meta information, much like [react-helmet](https://github.com/nfl/react-helmet) does for React. It'm awesome for `SEO`.
74 | - [x] Making the optimization based on the new features of `Webepack 4.*`. Getting the details on `vue.config.js`;
75 | - [x] Opening & using [Jest](https://jestjs.io/) to test the component with the Demo;
76 | - [x] Integrate & configure the [Prettier](https://prettier.io/) plugin to enable the team to code with better and consistent style. Getting the details on the [使用 ESLint & Prettier美化Vue代码](https://www.jeffjade.com/2018/06/18/142-beautify-vue-by-eslint-and-prettier/);
77 | - [x] Import [cli-plugin-pwa](https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa) plugin,and configure in \`vue.config.js\` ,you can get started easily with [PWA](https://github.com/nicejade/nice-front-end-tutorial/blob/master/tutorial/pwa-tutorial.md) using Vue;
78 | - [x] Import the [prerender-spa-plugin](https://github.com/chrisvfritz/prerender-spa-plugin) plugin to pre-render static HTML, optimizing SEO and first-screen rendering in a single-page application .
79 | - [x] Import the [webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer) plugin so that to get the contents of the building packages with optimization while running `Yarn analyz`. Getting the details: [Webpack 打包优化之体积篇](https://jeffjade.com/2017/08/06/124-webpack-packge-optimization-for-volume/#%E5%AE%9A%E4%BD%8D-webpack-%E5%A4%A7%E7%9A%84%E5%8E%9F%E5%9B%A0).
80 | - [x] Import the [size-plugin](https://github.com/GoogleChromeLabs/size-plugin) plugin to print the Gzip size of the Webpack asset and the changes since the last building while building the app.
81 | - [x] Import the [hard-source-webpack-plugin](https://github.com/mzgoddard/hard-source-webpack-plugin) plugin for webpack to provide an intermediate caching step for modules. It make the second build will be signficantly faster.
82 | - [x] The combination of this scaffolding with Node.js (Koa2) Nginx MondoDb Redis is integrated into Docker to make Front-End Developer build easily the entire web application.Its currently open sourced in [Docker Vue Node Nginx Mongodb Redis](https://github.com/nicejade/docker-vue-node-nginx-mongodb-redis).
83 | - [ ] Optimizing the built-in `Icon` (Svg) component so that you can receive input in different ways and extract Svg into a separate file to avoid repeated loading of resources;
84 |
85 | >**TIP**: [prerender-spa-plugin](https://github.com/chrisvfritz/prerender-spa-plugin): Prerenders static HTML in a single-page application. But, it is not required. If you don't need it, you can remove it. Because it requires a lot of dependencies([puppeteer](https://github.com/GoogleChrome/puppeteer),Chromium: ~170MB Mac, ~282MB Linux, ~280MB Win) to download, this is time consuming.
86 |
87 | ## Recommended links
88 |
89 | - [**NICE LINKS**](https://nicelinks.site/?utm_source=awesome-vue-cli3-example)
90 | - [About Me](https://about.me/nicejade/?utm_source=awesome-vue-cli3-example)
91 | - [Latest Blog](https://quickapp.lovejade.cn/?utm_source=awesome-vue-cli3-example)
92 | - [First Blog](https://jeffjade.com/?utm_source=awesome-vue-cli3-example)
93 | - [Second Blog](https://nice.lovejade.cn/?utm_source=awesome-vue-cli3-example)
94 | - [Auxiliary blog](https://blog.lovejade.cn/?utm_source=awesome-vue-cli3-example)
95 | - [Weibo](http://weibo.com/jeffjade/)
96 | - [ZhiHu](https://www.zhihu.com/people/yang-qiong-pu/)
97 | - [SegmentFault](https://segmentfault.com/u/jeffjade)
98 | - [JianShu](http://www.jianshu.com/u/9aae3d8f4c3d)
99 | - [Twitter](https://twitter.com/nicejadeyang)
100 | - [Facebook](https://www.facebook.com/nice.jade.yang)
101 |
102 | ## License
103 |
104 | [MIT](http://opensource.org/licenses/MIT)
105 |
106 | Copyright (c) 2018-present, [nicejade](https://aboutme.lovejade.cn/?utm_source=awesome-vue-cli3-example).
--------------------------------------------------------------------------------
/commands/clear-commit-msg.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | set -e
4 |
5 | git checkout --orphan latest_branch
6 |
7 | git add -A
8 |
9 | git commit -am "✨ initial create & commit"
10 |
11 | git branch -D master
12 |
13 | git branch -m master
14 |
15 | git push -f origin master
16 |
--------------------------------------------------------------------------------
/commands/deploy.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | # 确保脚本抛出遇到的错误
4 | set -e
5 |
6 | # 生成静态文件
7 | yarn run build
8 |
9 | # 进入生成的文件夹
10 | cd ./dist
11 |
12 | # 如果是发布到自定义域名
13 | echo 'vue-cli3.lovejade.cn' > CNAME
14 |
15 | git init
16 | git add -A
17 | git commit -m '🎉 update release'
18 |
19 | # 如果发布到 https://.github.io
20 | # git push -f git@github.com:/.github.io.git master
21 |
22 | # 如果发布到 https://.github.io/
23 | git push -f https://github.com/nicejade/awesome-vue-cli3-example.git master:gh-pages
24 | cd -
--------------------------------------------------------------------------------
/commands/preview.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const chalk = require('chalk')
3 | const childProcess = require('child_process')
4 | const fs = require('fs')
5 | const path = require('path')
6 | const opn = require('opn')
7 |
8 | const app = express()
9 |
10 | const resolveRealPath = dir => {
11 | return path.join(__dirname, dir)
12 | }
13 |
14 | const entryFilePath = resolveRealPath('../dist/index.html')
15 |
16 | const openStaticServer = () => {
17 | app.use('/js', express.static(resolveRealPath('../dist/js/')))
18 | app.use('/css', express.static(resolveRealPath('../dist/css/')))
19 | app.use('/img', express.static(resolveRealPath('../dist/img/')))
20 | app.use('/fonts', express.static(resolveRealPath('../dist/fonts/')))
21 |
22 | app.get('*', function(req, res) {
23 | const content = fs.readFileSync(entryFilePath, 'utf8')
24 | res.send(content)
25 | })
26 |
27 | app.listen(3000, function() {
28 | console.log(chalk.cyan('Example app listening on port 3000!\n'))
29 | console.log(chalk.yellow('You Can Visit: ') + chalk.green('http://localhost:3000/'))
30 | opn('http://localhost:3000')
31 | })
32 | }
33 |
34 | const main = () => {
35 | const isExist = fs.existsSync(entryFilePath)
36 | if (isExist) {
37 | openStaticServer()
38 | } else {
39 | const commandStr = `vue-cli-service build`
40 | const output = childProcess.execSync(commandStr, {
41 | cwd: process.cwd(),
42 | timeout: 60000,
43 | encoding: 'utf8'
44 | })
45 | openStaticServer()
46 | console.log(output)
47 | }
48 | }
49 |
50 | main()
51 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | /** @format */
2 | const path = require('path')
3 |
4 | module.exports = {
5 | moduleFileExtensions: ['js', 'jsx', 'json', 'vue'],
6 | transform: {
7 | '^.+\\.vue$': 'vue-jest',
8 | '.+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
9 | '^.+\\.jsx?$': 'babel-jest'
10 | },
11 | moduleNameMapper: {
12 | '^@/(.*)$': '/src/$1'
13 | },
14 | snapshotSerializers: ['jest-serializer-vue'],
15 | testMatch: ['**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'],
16 | collectCoverage: true,
17 | collectCoverageFrom: ['src/**/*.{js,vue}', '!src/assets/**'],
18 | coverageReporters: ['html', 'text-summary'],
19 | coverageDirectory: path.resolve(__dirname, './tests/coverage')
20 | }
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-cli-overall-example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "yarn serve",
7 | "serve": "vue-cli-service serve --open",
8 | "build": "vue-cli-service build",
9 | "watcher": "onchange '**/*.md' \"**/**/*.{js,css,scss,vue}\" -- prettier --write {{changed}}",
10 | "prettier": "prettier --write \"**/**/*.{js,css,scss,vue}\"",
11 | "lint": "vue-cli-service lint",
12 | "test:unit": "vue-cli-service test:unit",
13 | "deploy": "bash ./commands/deploy.sh",
14 | "analyz": "NODE_ENV=production npm_config_report=true npm run build",
15 | "clear-commit-msg": "bash ./commands/clear-commit-msg.sh",
16 | "eslint-fix": "eslint src/**/**/*.vue --fix",
17 | "format-code": "prettier-eslint --write \"src/**/*.js\" \"src/**/*.vue\"",
18 | "precommit-msg": "echo '🐉 Start pre-commit checks...' && exit 0",
19 | "preview": "node ./commands/preview.js"
20 | },
21 | "dependencies": {
22 | "axios": "0.21.1",
23 | "dayjs": "^1.8.17",
24 | "element-ui": "2.4.6",
25 | "js-cookie": "^2.2.1",
26 | "lodash": "^4.17.15",
27 | "marked": "0.7.0",
28 | "node-sass": "4.9.0",
29 | "q": "^1.5.1",
30 | "register-service-worker": "^1.0.0",
31 | "vue": "^2.5.17",
32 | "vue-meta": "^2.3.1",
33 | "vue-router": "^3.1.3",
34 | "vuex": "^3.1.2"
35 | },
36 | "devDependencies": {
37 | "@vue/cli-plugin-babel": "^4.0.5",
38 | "@vue/cli-plugin-eslint": "^4.0.5",
39 | "@vue/cli-plugin-pwa": "^4.0.5",
40 | "@vue/cli-plugin-unit-jest": "^4.0.5",
41 | "@vue/cli-service": "^4.0.5",
42 | "@vue/test-utils": "^1.0.0-beta.29",
43 | "babel-core": "7.0.0-bridge.0",
44 | "babel-eslint": "^10.0.3",
45 | "babel-jest": "^24.9.0",
46 | "eslint-config-prettier": "^6.5.0",
47 | "eslint-plugin-prettier": "^3.1.1",
48 | "eslint-plugin-vue": "^6.0.1",
49 | "husky": "^3.0.9",
50 | "lint-staged": "^9.4.3",
51 | "onchange": "^6.1.0",
52 | "prettier-eslint-cli": "^5.0.0",
53 | "sass-loader": "7.0.1",
54 | "size-plugin": "^2.0.1",
55 | "svg-sprite-loader": "^4.1.6",
56 | "svgo-loader": "2.2.1",
57 | "vue-svg-loader": "^0.15.0",
58 | "vue-template-compiler": "^2.5.17",
59 | "webpack-bundle-analyzer": "3.6.0"
60 | },
61 | "babel": {
62 | "presets": [
63 | "@vue/app"
64 | ]
65 | },
66 | "eslintConfig": {
67 | "globals": {
68 | "L": true
69 | },
70 | "root": true,
71 | "env": {
72 | "node": true,
73 | "es6": true
74 | },
75 | "rules": {
76 | "no-console": 0,
77 | "no-useless-escape": 0,
78 | "no-multiple-empty-lines": [
79 | 2,
80 | {
81 | "max": 3
82 | }
83 | ],
84 | "prettier/prettier": [
85 | "error",
86 | {
87 | "singleQuote": true,
88 | "semi": false,
89 | "trailingComma": "none",
90 | "bracketSpacing": true,
91 | "jsxBracketSameLine": true,
92 | "insertPragma": true,
93 | "requirePragma": false
94 | }
95 | ]
96 | },
97 | "plugins": [],
98 | "extends": [
99 | "plugin:vue/essential",
100 | "plugin:prettier/recommended",
101 | "eslint:recommended"
102 | ],
103 | "parserOptions": {
104 | "parser": "babel-eslint"
105 | }
106 | },
107 | "prettier": {
108 | "singleQuote": true,
109 | "semi": false,
110 | "printWidth": 100,
111 | "proseWrap": "never"
112 | },
113 | "postcss": {
114 | "plugins": {
115 | "autoprefixer": {}
116 | }
117 | },
118 | "browserslist": [
119 | "> 1%",
120 | "last 2 versions",
121 | "not ie <= 8"
122 | ],
123 | "eslintIgnore": [
124 | "package.json"
125 | ],
126 | "husky": {
127 | "hooks": {
128 | "pre-commit": "yarn run precommit-msg && lint-staged"
129 | }
130 | },
131 | "lint-staged": {
132 | "**/**.{js,json,pcss,md,vue,css,scss}": [
133 | "prettier --write"
134 | ]
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicejade/awesome-vue-cli3-example/8217ef4b240ee6923dac6d7f94c7312a4b54b931/public/favicon.ico
--------------------------------------------------------------------------------
/public/img/icons/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicejade/awesome-vue-cli3-example/8217ef4b240ee6923dac6d7f94c7312a4b54b931/public/img/icons/android-chrome-192x192.png
--------------------------------------------------------------------------------
/public/img/icons/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicejade/awesome-vue-cli3-example/8217ef4b240ee6923dac6d7f94c7312a4b54b931/public/img/icons/android-chrome-512x512.png
--------------------------------------------------------------------------------
/public/img/icons/apple-touch-icon-120x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicejade/awesome-vue-cli3-example/8217ef4b240ee6923dac6d7f94c7312a4b54b931/public/img/icons/apple-touch-icon-120x120.png
--------------------------------------------------------------------------------
/public/img/icons/apple-touch-icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicejade/awesome-vue-cli3-example/8217ef4b240ee6923dac6d7f94c7312a4b54b931/public/img/icons/apple-touch-icon-152x152.png
--------------------------------------------------------------------------------
/public/img/icons/apple-touch-icon-180x180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicejade/awesome-vue-cli3-example/8217ef4b240ee6923dac6d7f94c7312a4b54b931/public/img/icons/apple-touch-icon-180x180.png
--------------------------------------------------------------------------------
/public/img/icons/apple-touch-icon-60x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicejade/awesome-vue-cli3-example/8217ef4b240ee6923dac6d7f94c7312a4b54b931/public/img/icons/apple-touch-icon-60x60.png
--------------------------------------------------------------------------------
/public/img/icons/apple-touch-icon-76x76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicejade/awesome-vue-cli3-example/8217ef4b240ee6923dac6d7f94c7312a4b54b931/public/img/icons/apple-touch-icon-76x76.png
--------------------------------------------------------------------------------
/public/img/icons/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicejade/awesome-vue-cli3-example/8217ef4b240ee6923dac6d7f94c7312a4b54b931/public/img/icons/apple-touch-icon.png
--------------------------------------------------------------------------------
/public/img/icons/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicejade/awesome-vue-cli3-example/8217ef4b240ee6923dac6d7f94c7312a4b54b931/public/img/icons/favicon-16x16.png
--------------------------------------------------------------------------------
/public/img/icons/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicejade/awesome-vue-cli3-example/8217ef4b240ee6923dac6d7f94c7312a4b54b931/public/img/icons/favicon-32x32.png
--------------------------------------------------------------------------------
/public/img/icons/msapplication-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicejade/awesome-vue-cli3-example/8217ef4b240ee6923dac6d7f94c7312a4b54b931/public/img/icons/msapplication-icon-144x144.png
--------------------------------------------------------------------------------
/public/img/icons/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicejade/awesome-vue-cli3-example/8217ef4b240ee6923dac6d7f94c7312a4b54b931/public/img/icons/mstile-150x150.png
--------------------------------------------------------------------------------
/public/img/icons/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
150 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Awesome Vue-Cli3 Example
9 |
10 |
11 |
12 |
13 |
19 |
20 |
21 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Vue 脚手架",
3 | "short_name": "Vue 脚手架",
4 | "icons": [
5 | {
6 | "src": "./img/icons/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "./img/icons/android-chrome-512x512.png",
12 | "sizes": "512x512",
13 | "type": "image/png"
14 | }
15 | ],
16 | "start_url": "https://vue-cli3.lovejade.cn/?from=device-screen",
17 | "display": "standalone",
18 | "background_color": "#000000",
19 | "theme_color": "#4DBA87"
20 | }
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/public/service-worker.js:
--------------------------------------------------------------------------------
1 | const HTMLToCache = '/'
2 | const version = 'v1.0.0'
3 |
4 | self.addEventListener('install', event => {
5 | event.waitUntil(
6 | caches.open(version).then(cache => {
7 | cache.add(HTMLToCache).then(self.skipWaiting())
8 | })
9 | )
10 | })
11 |
12 | self.addEventListener('activate', event => {
13 | event.waitUntil(
14 | caches
15 | .keys()
16 | .then(cacheNames =>
17 | Promise.all(
18 | cacheNames.map(cacheName => {
19 | if (version !== cacheName) return caches.delete(cacheName)
20 | })
21 | )
22 | )
23 | .then(self.clients.claim())
24 | )
25 | })
26 |
27 | self.addEventListener('fetch', event => {
28 | const requestToFetch = event.request.clone()
29 | event.respondWith(
30 | caches.match(event.request.clone()).then(cached => {
31 | // We don't return cached HTML (except if fetch failed)
32 | if (cached) {
33 | const resourceType = cached.headers.get('content-type')
34 | // We only return non css/js/html cached response e.g images
35 | if (!hasHash(event.request.url) && !/text\/html/.test(resourceType)) {
36 | return cached
37 | }
38 |
39 | // If the CSS/JS didn't change since it's been cached, return the cached version
40 | if (hasHash(event.request.url) && hasSameHash(event.request.url, cached.url)) {
41 | return cached
42 | }
43 | }
44 | return fetch(requestToFetch)
45 | .then(response => {
46 | const clonedResponse = response.clone()
47 | const contentType = clonedResponse.headers.get('content-type')
48 |
49 | if (
50 | !clonedResponse ||
51 | clonedResponse.status !== 200 ||
52 | clonedResponse.type !== 'basic' ||
53 | /\/sockjs\//.test(event.request.url)
54 | ) {
55 | return response
56 | }
57 |
58 | if (/html/.test(contentType)) {
59 | caches.open(version).then(cache => cache.put(HTMLToCache, clonedResponse))
60 | } else {
61 | // Delete old version of a file
62 | if (hasHash(event.request.url)) {
63 | caches.open(version).then(cache =>
64 | cache.keys().then(keys =>
65 | keys.forEach(asset => {
66 | if (new RegExp(removeHash(event.request.url)).test(removeHash(asset.url))) {
67 | cache.delete(asset)
68 | }
69 | })
70 | )
71 | )
72 | }
73 |
74 | caches.open(version).then(cache => cache.put(event.request, clonedResponse))
75 | }
76 | return response
77 | })
78 | .catch(() => {
79 | if (hasHash(event.request.url)) return caches.match(event.request.url)
80 | // If the request URL hasn't been served from cache and isn't sockjs we suppose it's HTML
81 | else if (!/\/sockjs\//.test(event.request.url)) return caches.match(HTMLToCache)
82 | // Only for sockjs
83 | return new Response('No connection to the server', {
84 | status: 503,
85 | statusText: 'No connection to the server',
86 | headers: new Headers({ 'Content-Type': 'text/plain' })
87 | })
88 | })
89 | })
90 | )
91 | })
92 |
93 | function removeHash(element) {
94 | if (typeof element === 'string') return element.split('?hash=')[0]
95 | }
96 |
97 | function hasHash(element) {
98 | if (typeof element === 'string') return /\?hash=.*/.test(element)
99 | }
100 |
101 | function hasSameHash(firstUrl, secondUrl) {
102 | if (typeof firstUrl === 'string' && typeof secondUrl === 'string') {
103 | return /\?hash=(.*)/.exec(firstUrl)[1] === /\?hash=(.*)/.exec(secondUrl)[1]
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
16 |
17 |
27 |
--------------------------------------------------------------------------------
/src/assets/icons/author.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
40 |
--------------------------------------------------------------------------------
/src/assets/icons/document.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
66 |
--------------------------------------------------------------------------------
/src/assets/icons/explore.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
111 |
--------------------------------------------------------------------------------
/src/assets/icons/github.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/index.js:
--------------------------------------------------------------------------------
1 | /** @format */
2 |
3 | const files = require.context('.', true, /\.svg$/)
4 | const modules = {}
5 |
6 | files.keys().forEach(key => {
7 | modules[key.replace(/(\.\/|\.svg)/g, '')] = files(key)
8 | })
9 |
10 | export default modules
11 |
--------------------------------------------------------------------------------
/src/assets/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicejade/awesome-vue-cli3-example/8217ef4b240ee6923dac6d7f94c7312a4b54b931/src/assets/images/logo.png
--------------------------------------------------------------------------------
/src/assets/images/vue-cli-manage-cli-plugin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicejade/awesome-vue-cli3-example/8217ef4b240ee6923dac6d7f94c7312a4b54b931/src/assets/images/vue-cli-manage-cli-plugin.png
--------------------------------------------------------------------------------
/src/assets/images/vue-cli-manage-project-dependencies.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicejade/awesome-vue-cli3-example/8217ef4b240ee6923dac6d7f94c7312a4b54b931/src/assets/images/vue-cli-manage-project-dependencies.png
--------------------------------------------------------------------------------
/src/assets/images/vue-cli-npm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicejade/awesome-vue-cli3-example/8217ef4b240ee6923dac6d7f94c7312a4b54b931/src/assets/images/vue-cli-npm.png
--------------------------------------------------------------------------------
/src/assets/styles/common.scss:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | box-sizing: border-box;
5 | font-family: Open Sans;
6 | }
7 |
8 | *:focus {
9 | outline: none !important;
10 | }
11 |
12 | a {
13 | color: $brand;
14 | text-decoration: none;
15 | }
16 |
--------------------------------------------------------------------------------
/src/assets/styles/element.scss:
--------------------------------------------------------------------------------
1 | /* 改变主题色变量 */
2 | $--color-primary: teal;
3 |
4 | /* 改变 icon 字体路径变量,必需 */
5 | $--font-path: '~element-ui/lib/theme-chalk/fonts';
6 |
7 | @import '~element-ui/packages/theme-chalk/src/index';
8 |
9 | .el-collapse-item__header {
10 | font-size: $font-medium;
11 | }
12 | .el-collapse-item__content {
13 | font-size: $font-small;
14 | }
15 | .el-collapse {
16 | max-width: 38rem;
17 | }
18 |
19 | @media (max-width: 768px) {
20 | .el-collapse {
21 | max-width: 320px;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/assets/styles/mixins.scss:
--------------------------------------------------------------------------------
1 | /* Clearfix
2 | For modern browsers
3 | 1. The space content is one way to avoid an Opera bug when the
4 | contenteditable attribute is included anywhere else in the document.
5 | Otherwise it causes space to appear at the top and bottom of elements
6 | that are clearfixed.
7 | 2. The use of `table` rather than `block` is only necessary if using
8 | `:before` to contain the top-margins of child elements.
9 | Source: http://nicolasgallagher.com/micro-clearfix-hack */
10 | @mixin clearfix() {
11 | &:before,
12 | &:after {
13 | content: ' ';
14 | display: table;
15 | }
16 | &:after {
17 | clear: both;
18 | }
19 | }
20 |
21 | // (Responsive image)Keep images from scaling beyond the width of their parents.
22 | @mixin img-responsive($display: block) {
23 | display: $display;
24 | max-width: 100%;
25 | height: auto;
26 | }
27 |
28 | // Resize anything
29 | @mixin resizable($direction) {
30 | resize: $direction;
31 | overflow: auto;
32 | }
33 |
34 | // Text overflow
35 | // Requires inline-block or block for proper styling
36 | @mixin text-overflow() {
37 | overflow: hidden;
38 | text-overflow: ellipsis;
39 | white-space: nowrap;
40 | }
41 |
42 | // Center-align a block level element
43 | @mixin center-block() {
44 | display: block;
45 | margin-left: auto;
46 | margin-right: auto;
47 | }
48 |
49 | @mixin absolute-center() {
50 | position: absolute;
51 | top: 50%;
52 | left: 50%;
53 | transform: translate(-50%, -50%);
54 | }
55 |
56 | @mixin fixed-center() {
57 | position: fixed;
58 | top: 50%;
59 | left: 50%;
60 | transform: translate(-50%, -50%);
61 | }
62 |
63 | @mixin flex-box-center($direction: row, $justify: center, $align-items: center) {
64 | flex-direction: $direction;
65 | display: -webkit-box;
66 | display: -moz-box;
67 | display: -webkit-flex;
68 | display: -ms-flex;
69 | display: flex;
70 | -webkit-box-align: center;
71 | -moz-box-align: center;
72 | -ms-box-align: center;
73 | -webkit-align-items: $align-items;
74 | -ms-align-items: $align-items;
75 | align-items: $align-items;
76 | -webkit-box-pack: center;
77 | -webkit-justify-content: center;
78 | justify-content: $justify;
79 | }
80 |
--------------------------------------------------------------------------------
/src/assets/styles/style.scss:
--------------------------------------------------------------------------------
1 | @import 'element.scss';
2 | @import 'variables.scss';
3 | @import 'mixins.scss';
4 | @import 'common.scss';
5 |
--------------------------------------------------------------------------------
/src/assets/styles/variables.scss:
--------------------------------------------------------------------------------
1 | $size-factor: 0.5rem;
2 |
3 | $brand: #03a9f4;
4 |
5 | $bg-grey: #fafcff;
6 | $input-grey: #f2f4f7;
7 | $text-grey: #6a8bad;
8 | $icon-grey: #707780;
9 |
10 | $border-grey: #d7dce5;
11 | $border-black: #7a8ba9;
12 |
13 | $white: #ffffff;
14 | $black: #333333;
15 | $red: #fa0101;
16 | $grey: #e8ebf1;
17 | $orange: #6b5900;
18 | $yellow: #ffe564;
19 |
20 | $font-large: 2.1rem;
21 | $font-medium: 1.2rem;
22 | $font-small: 1rem;
23 |
--------------------------------------------------------------------------------
/src/components/Advertisement.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
13 |
14 |
15 |
57 |
58 |
84 |
--------------------------------------------------------------------------------
/src/components/EditDialog.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 |
28 |
29 |
30 |
31 |
35 |
36 |
37 |
38 |
95 |
96 |
136 |
--------------------------------------------------------------------------------
/src/components/Icon.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
35 |
36 |
42 |
--------------------------------------------------------------------------------
/src/components/Instruction.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
功能详述
6 |
7 |
8 |
9 | Vue-Cli3:对 Babel、TypeScript、ESLint、PostCSS、PWA、单元测试和
10 | End-to-end 测试提供开箱即用的支持。
11 |
12 |
18 |
19 |
20 |
21 | Vue-Cli3:它的插件系统可以让社区根据常见需求构建和共享可复用的解决方案。
22 |
23 |
37 |
38 |
39 |
40 | Vue-Cli3:Vue CLI 完全是可配置的,无需
41 | eject。这样你的项目就可以长期保持更新了。
42 |
43 |
47 |
48 | 注意事项:在
49 | vue.config.js
50 | 中,已对全局样式做了预先注入,可以无需引用即可使用;对此,须要付出的代价是,一旦对预先注入的全局样式做了修改,需要重新运行项目,方可生效。
51 |
52 |
53 |
54 |
55 |
60 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
179 |
180 |
201 |
--------------------------------------------------------------------------------
/src/components/icons/Arrow.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
38 |
39 |
63 |
--------------------------------------------------------------------------------
/src/components/markdown/Index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
86 |
87 |
113 |
--------------------------------------------------------------------------------
/src/components/markdown/MarkdownPreview.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
42 |
--------------------------------------------------------------------------------
/src/components/markdown/markdown.css:
--------------------------------------------------------------------------------
1 | .markdown-body ol ol,
2 | .markdown-body ul ol,
3 | .markdown-body ol ul,
4 | .markdown-body ul ul,
5 | .markdown-body ol ul ol,
6 | .markdown-body ul ul ol,
7 | .markdown-body ol ul ul,
8 | .markdown-body ul ul ul {
9 | margin-top: 0;
10 | margin-bottom: 0;
11 | }
12 | .markdown-body {
13 | line-height: 1.6;
14 | word-wrap: break-word;
15 | padding: 0;
16 | -webkit-border-radius: 0 0 3px 3px;
17 | border-radius: 0 0 3px 3px;
18 | }
19 | .markdown-body > *:first-child {
20 | margin-top: 0 !important;
21 | }
22 | .markdown-body > *:last-child {
23 | margin-bottom: 0 !important;
24 | }
25 | .markdown-body * {
26 | -webkit-box-sizing: border-box;
27 | -moz-box-sizing: border-box;
28 | box-sizing: border-box;
29 | }
30 | .markdown-body h1,
31 | .markdown-body h2,
32 | .markdown-body h3,
33 | .markdown-body h4,
34 | .markdown-body h5,
35 | .markdown-body h6 {
36 | margin-top: 1em;
37 | margin-bottom: 16px;
38 | font-weight: bold;
39 | line-height: 1.4;
40 | }
41 | .markdown-body p,
42 | .markdown-body blockquote,
43 | .markdown-body ul,
44 | .markdown-body ol,
45 | .markdown-body dl,
46 | .markdown-body table,
47 | .markdown-body pre {
48 | margin-top: 0;
49 | margin-bottom: 16px;
50 | }
51 | .markdown-body h1 {
52 | margin: 0.67em 0;
53 | padding-bottom: 0.3em;
54 | font-size: 2.25em;
55 | line-height: 1.2;
56 | border-bottom: 1px solid #eee;
57 | }
58 | .markdown-body h2 {
59 | padding-bottom: 0.3em;
60 | font-size: 1.75em;
61 | line-height: 1.225;
62 | border-bottom: 1px solid #eee;
63 | }
64 | .markdown-body h3 {
65 | font-size: 1.5em;
66 | line-height: 1.43;
67 | }
68 | .markdown-body h4 {
69 | font-size: 1.25em;
70 | }
71 | .markdown-body h5 {
72 | font-size: 1em;
73 | }
74 | .markdown-body h6 {
75 | font-size: 1em;
76 | color: #777;
77 | }
78 | .markdown-body ol,
79 | .markdown-body ul {
80 | padding-left: 2em;
81 | }
82 | .markdown-body ol ol,
83 | .markdown-body ul ol {
84 | list-style-type: lower-roman;
85 | }
86 | .markdown-body ol ul,
87 | .markdown-body ul ul {
88 | list-style-type: circle;
89 | }
90 | .markdown-body ol ul ul,
91 | .markdown-body ul ul ul {
92 | list-style-type: square;
93 | }
94 | .markdown-body ol {
95 | list-style-type: decimal;
96 | }
97 | .markdown-body ul {
98 | list-style-type: disc;
99 | }
100 | .markdown-body blockquote {
101 | margin-left: 0;
102 | margin-right: 0;
103 | padding: 0 15px;
104 | color: #777;
105 | border-left: 4px solid #ddd;
106 | }
107 | .markdown-body table {
108 | display: block;
109 | width: 100%;
110 | overflow: auto;
111 | word-break: normal;
112 | word-break: keep-all;
113 | border-collapse: collapse;
114 | border-spacing: 0;
115 | }
116 | .markdown-body table tr {
117 | background-color: #fff;
118 | border-top: 1px solid #ccc;
119 | }
120 | .markdown-body table tr:nth-child(2n) {
121 | background-color: #f8f8f8;
122 | }
123 | .markdown-body table th,
124 | .markdown-body table td {
125 | padding: 6px 13px;
126 | border: 1px solid #ddd;
127 | }
128 | .markdown-body pre {
129 | word-wrap: normal;
130 | padding: 16px;
131 | overflow: auto;
132 | font-size: 85%;
133 | line-height: 1.45;
134 | background-color: #f7f7f7;
135 | -webkit-border-radius: 3px;
136 | border-radius: 3px;
137 | }
138 | .markdown-body pre code {
139 | display: inline;
140 | max-width: initial;
141 | padding: 0;
142 | margin: 0;
143 | overflow: initial;
144 | font-size: 100%;
145 | line-height: inherit;
146 | word-wrap: normal;
147 | white-space: pre;
148 | border: 0;
149 | -webkit-border-radius: 3px;
150 | border-radius: 3px;
151 | background-color: transparent;
152 | }
153 | .markdown-body pre code:before,
154 | .markdown-body pre code:after {
155 | content: normal;
156 | }
157 | .markdown-body code {
158 | font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
159 | padding: 0;
160 | padding-top: 0.2em;
161 | padding-bottom: 0.2em;
162 | margin: 0;
163 | font-size: 85%;
164 | background-color: rgba(0, 0, 0, 0.04);
165 | -webkit-border-radius: 3px;
166 | border-radius: 3px;
167 | }
168 | .markdown-body code:before,
169 | .markdown-body code:after {
170 | letter-spacing: -0.2em;
171 | content: '\00a0';
172 | }
173 | .markdown-body a {
174 | text-decoration: none;
175 | background: transparent;
176 | }
177 | .markdown-body img {
178 | max-width: 100%;
179 | max-height: 100%;
180 | -webkit-border-radius: 4px;
181 | border-radius: 4px;
182 | -webkit-box-shadow: 0 0 10px #555;
183 | box-shadow: 0 0 10px #555;
184 | }
185 | .markdown-body strong {
186 | font-weight: bold;
187 | }
188 | .markdown-body em {
189 | font-style: italic;
190 | }
191 | .markdown-body del {
192 | text-decoration: line-through;
193 | }
194 | .task-list-item {
195 | list-style-type: none;
196 | }
197 | .task-list-item input {
198 | font: 13px/1.4 Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif,
199 | 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
200 | margin: 0 0.35em 0.25em -1.6em;
201 | vertical-align: middle;
202 | }
203 | .task-list-item input[disabled] {
204 | cursor: default;
205 | }
206 | .task-list-item input[type='checkbox'] {
207 | -webkit-box-sizing: border-box;
208 | -moz-box-sizing: border-box;
209 | box-sizing: border-box;
210 | padding: 0;
211 | }
212 | .task-list-item input[type='radio'] {
213 | -webkit-box-sizing: border-box;
214 | -moz-box-sizing: border-box;
215 | box-sizing: border-box;
216 | padding: 0;
217 | }
218 |
--------------------------------------------------------------------------------
/src/filters.js:
--------------------------------------------------------------------------------
1 | /** @format */
2 |
3 | import dayjs from 'dayjs'
4 |
5 | export default {
6 | dateTimeConvert(time) {
7 | return time && dayjs(time).format('YYYY-MM-DD HH:mm:ss')
8 | },
9 | dateConvert(time) {
10 | return time && dayjs(time).format('YYYY-MM-DD')
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/global.js:
--------------------------------------------------------------------------------
1 | /** @format */
2 |
3 | import Vue from 'vue'
4 | import Cookies from 'js-cookie'
5 | import Filters from './filters'
6 |
7 | /* ------------------------Vue Global Config------------------------------ */
8 | Vue.config.productionTip = false
9 |
10 | const lang = Cookies.get('lang') || 'en'
11 | Vue.config.lang = lang
12 |
13 | /* ------------------------Vue Global Variable------------------------------ */
14 | import { $apis, $utils, $document, $auth, $lodash } from '@helper'
15 | Vue.prototype.$_ = $lodash
16 | Vue.prototype.$apis = $apis
17 | Vue.prototype.$utils = $utils
18 | Vue.prototype.$auth = $auth
19 | Vue.prototype.$document = $document
20 |
21 | for (let key in Filters) {
22 | Vue.filter(key, Filters[key])
23 | }
24 |
25 | /* ------------------------Vue Global Components------------------------------ */
26 | import ElementUI from 'element-ui'
27 | Vue.use(ElementUI)
28 |
29 | import Markdown from '@components/markdown/Index'
30 | Vue.component('Markdown', Markdown)
31 |
32 | import MarkdownPreview from '@components/markdown/MarkdownPreview'
33 | Vue.component('MarkdownPreview', MarkdownPreview)
34 |
35 | import VueMeta from 'vue-meta'
36 | Vue.use(VueMeta)
37 |
38 | import Icon from '@components/Icon'
39 | Vue.component('icon', Icon)
40 |
41 | import Arrow from '@components/icons/Arrow'
42 | Vue.component('arrow', Arrow)
43 |
--------------------------------------------------------------------------------
/src/helper/ajax.js:
--------------------------------------------------------------------------------
1 | /** @format */
2 |
3 | import axios from 'axios'
4 | import $q from 'q'
5 | import { $utils } from '@helper'
6 |
7 | function requestHandle(params) {
8 | const defer = $q.defer()
9 | axios(params)
10 | .then(res => {
11 | if (res && (res.unauthorized || res.statusCode === 401)) {
12 | window.location.href = '/login'
13 | }
14 | if (res.type === 'application/x-msdownload') {
15 | redirectToIframe(res.request.responseURL)
16 | } else if (res.data) {
17 | if (res.data.success) {
18 | defer.resolve(res.data.value)
19 | } else {
20 | defer.reject(res.data.message)
21 | }
22 | } else {
23 | defer.reject()
24 | }
25 | })
26 | .catch(err => {
27 | defer.reject(err)
28 | })
29 |
30 | return defer.promise
31 | }
32 |
33 | function redirectToIframe(url) {
34 | const iframe = document.createElement('iframe')
35 | iframe.style.display = 'none'
36 | iframe.src = url
37 | iframe.onload = function() {
38 | document.body.removeChild(iframe)
39 | }
40 | document.body.appendChild(iframe)
41 | }
42 |
43 | export default {
44 | post: function(url, params) {
45 | return requestHandle({
46 | method: 'post',
47 | url: url,
48 | data: params
49 | })
50 | },
51 | get: function(url, params) {
52 | return requestHandle({
53 | method: 'get',
54 | url: $utils.queryString(url, params)
55 | })
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/helper/apis/index.js:
--------------------------------------------------------------------------------
1 | /** @format */
2 |
3 | const files = require.context('.', true, /\.js/)
4 | const modules = {}
5 |
6 | files.keys().forEach(key => {
7 | if (key === './index.js') {
8 | return
9 | }
10 | modules[key.replace(/(^\.\/|\.js$)/g, '')] = files(key).default
11 | })
12 |
13 | export default modules
14 |
--------------------------------------------------------------------------------
/src/helper/auth.js:
--------------------------------------------------------------------------------
1 | /**
2 | * /*
3 | * 权限验证模块
4 | *
5 | * @format
6 | */
7 |
8 | import Cookies from 'js-cookie'
9 |
10 | export default {
11 | checkSession() {
12 | return Cookies.get('isLogin')
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/helper/document.js:
--------------------------------------------------------------------------------
1 | /** @format */
2 |
3 | export default {
4 | toggleClass(el, className) {
5 | if (el.classList) {
6 | el.classList.toggle(className)
7 | } else {
8 | var classes = el.className.split(' ')
9 | var existingIndex = classes.indexOf(className)
10 |
11 | if (existingIndex >= 0) {
12 | classes.splice(existingIndex, 1)
13 | } else {
14 | classes.push(className)
15 | }
16 | el.className = classes.join(' ')
17 | }
18 | },
19 |
20 | addClass(el, className) {
21 | if (el.classList) {
22 | el.classList.add(className)
23 | } else {
24 | el.className += ` ${className}`
25 | }
26 | },
27 |
28 | removeClass(el, className) {
29 | if (el.classList) {
30 | el.classList.remove(className)
31 | } else {
32 | el.className = el.className.replace(
33 | new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'),
34 | ' '
35 | )
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/helper/index.js:
--------------------------------------------------------------------------------
1 | /** @format */
2 |
3 | export const $ajax = require('./ajax').default
4 | export const $apis = require('./apis').default
5 | export const $auth = require('./auth').default
6 | export const $utils = require('./utils').default
7 | export const $lodash = require('./lodash').default
8 | export const $document = require('./document').default
9 |
--------------------------------------------------------------------------------
/src/helper/lodash.js:
--------------------------------------------------------------------------------
1 | /**
2 | * /*
3 | * https://www.npmjs.com/package/lodash-webpack-plugin
4 | * https://www.npmjs.com/package/babel-plugin-lodash
5 | *
6 | * @format
7 | * @date: 2017-08-01
8 | * @lastModify: 2018-05-16
9 | * @desc: Modular Lodash builds without the hassle. You can freely add the method you need
10 | * @detail:
11 | */
12 |
13 | import _ from 'lodash'
14 |
15 | export default {
16 | clone: _.clone,
17 | cloneDeep: _.cloneDeep,
18 | endsWith: _.endsWith,
19 | debounce: _.debounce,
20 | throttle: _.throttle,
21 | find: _.find,
22 | isEmpty: _.isEmpty,
23 | flatten: _.flatten,
24 | flattenDepth: _.flattenDepth
25 | }
26 |
--------------------------------------------------------------------------------
/src/helper/utils.js:
--------------------------------------------------------------------------------
1 | /** @format */
2 |
3 | import Vue from 'vue'
4 |
5 | if (typeof String.prototype.startsWith !== 'function') {
6 | Window.String.prototype.startsWith = function(prefix) {
7 | return this.slice(0, prefix.length) === prefix
8 | }
9 | }
10 |
11 | export default {
12 | resMsg(res) {
13 | let key = {
14 | zh: 'Chinese',
15 | en: 'English'
16 | }[Vue.config.lang]
17 | try {
18 | let obj = JSON.parse(res.Message)
19 | return obj[key] || obj.Chinese
20 | } catch (e) {
21 | return res && res.Message
22 | }
23 | },
24 |
25 | serverUrl(apiName) {
26 | return `app/${apiName}`
27 | },
28 |
29 | titleLang(zhStr, enStr) {
30 | return {
31 | zh: zhStr,
32 | en: enStr
33 | }
34 | },
35 |
36 | query(search) {
37 | let str = search || window.location.search
38 | let objURL = {}
39 |
40 | str.replace(new RegExp('([^?=&]+)(=([^&]*))?', 'g'), ($0, $1, $2, $3) => {
41 | objURL[$1] = $3
42 | })
43 | return objURL
44 | },
45 |
46 | queryString(url, query) {
47 | let str = []
48 | for (let key in query) {
49 | str.push(key + '=' + query[key])
50 | }
51 | let paramStr = str.join('&')
52 | return paramStr ? `${url}?${paramStr}` : url
53 | },
54 |
55 | /* -----------------------------localStorage------------------------------------ */
56 | /*
57 | * set localStorage
58 | */
59 | setStorage(name, content) {
60 | if (!name) return
61 | if (typeof content !== 'string') {
62 | content = JSON.stringify(content)
63 | }
64 | window.localStorage.setItem(name, content)
65 | },
66 |
67 | /**
68 | * get localStorage
69 | */
70 | getStorage(name) {
71 | if (!name) return
72 | let content = window.localStorage.getItem(name)
73 | return JSON.parse(content)
74 | },
75 |
76 | /**
77 | * delete localStorage
78 | */
79 | removeStorage(name) {
80 | if (!name) return
81 | window.localStorage.removeItem(name)
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | /** @format */
2 |
3 | import Vue from 'vue'
4 | import App from './App.vue'
5 | import router from './router'
6 | import store from './store'
7 | import './global.js'
8 | import './registerServiceWorker'
9 |
10 | new Vue({
11 | router,
12 | store,
13 | render: h => h(App)
14 | }).$mount('#app')
15 |
--------------------------------------------------------------------------------
/src/mixins/metaMixin.js:
--------------------------------------------------------------------------------
1 | /** @format */
2 |
3 | export default {
4 | data() {
5 | const vm = this
6 | return {
7 | title: '首页',
8 | siteTitle: 'Vue-Cli3 实践参考',
9 | titleTemplate: '%s - ' + vm.siteTitle,
10 | keywords:
11 | 'vue,vue-cli3,webpack,vuex,vue-router,element-ui,TypeScript,ESLint,Prettier,Dayjs,Markdown,Jest,PWA,开箱即用,脚手架,模板',
12 | description:
13 | '此为基于 Vue-Cli3 搭建的开箱即用 Vue 脚手架模版,致力于探究更高效地构建优质 Web 应用。'
14 | }
15 | },
16 |
17 | created() {},
18 |
19 | metaInfo() {
20 | const titleContent = this.title ? `${this.title} - ${this.siteTitle}` : `${this.siteTitle}`
21 | return {
22 | title: this.title,
23 | titleTemplate: titleChunk => {
24 | return titleChunk ? `${titleChunk} - ${this.siteTitle}` : `${this.siteTitle}`
25 | },
26 | meta: [
27 | { vmid: 'title', name: 'title', content: titleContent },
28 | { vmid: 'keywords', name: 'keywords', content: this.keywords },
29 | { vmid: 'description', name: 'description', content: this.description },
30 | { vmid: 'og:type', property: 'og:type', content: 'website' },
31 | { vmid: 'og:title', property: 'og:title', content: titleContent },
32 | {
33 | vmid: 'og:image',
34 | property: 'og:image',
35 | content: 'https://nice.lovejade.cn/logo.png'
36 | },
37 | {
38 | vmid: 'og:keywords',
39 | property: 'og:keywords',
40 | content: this.keywords
41 | },
42 | {
43 | vmid: 'og:description',
44 | property: 'og:description',
45 | content: this.description
46 | }
47 | ]
48 | }
49 | },
50 |
51 | mounted() {},
52 |
53 | methods: {}
54 | }
55 |
--------------------------------------------------------------------------------
/src/pages/ExploreMore.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | {{ scope.row.address }}
15 |
16 |
17 |
18 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
48 |
49 |
50 |
51 |
52 |
61 |
62 |
63 |
64 |
65 |
70 |
71 |
72 |
73 |
74 |
75 |
175 |
176 |
248 |
--------------------------------------------------------------------------------
/src/pages/Homepage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
{{ titleText }}
6 |

7 |
8 | 此为基于 Vue-Cli3 搭建的开箱即用 Vue 脚手架模版,
致力于探究更高效地构建优质 Web 应用。
11 |
12 |
29 |
30 |
31 |
32 |
33 |
54 |
55 |
114 |
--------------------------------------------------------------------------------
/src/pages/partials/NotFound.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 4
8 | 0
9 | 4
10 |
11 |
YOU LOOK LOST
12 |
16 |
17 |
18 |
19 |
20 |
34 |
35 |
156 |
--------------------------------------------------------------------------------
/src/registerServiceWorker.js:
--------------------------------------------------------------------------------
1 | /**
2 | * /* eslint-disable no-console
3 | *
4 | * @format
5 | */
6 |
7 | import { register } from 'register-service-worker'
8 |
9 | if (process.env.NODE_ENV === 'production') {
10 | register(`${process.env.BASE_URL}service-worker.js`, {
11 | ready() {
12 | console.log(
13 | 'App is being served from cache by a service worker.\n' +
14 | 'For more details, visit https://goo.gl/AFskqB'
15 | )
16 | },
17 | cached() {
18 | console.log('Content has been cached for offline use.')
19 | },
20 | updated() {
21 | console.log('New content is available; please refresh.')
22 | },
23 | offline() {
24 | console.log('No internet connection found. App is running in offline mode.')
25 | },
26 | error(error) {
27 | console.error('Error during service worker registration:', error)
28 | }
29 | })
30 | }
31 |
--------------------------------------------------------------------------------
/src/router/beforeEachHooks.js:
--------------------------------------------------------------------------------
1 | /**
2 | * /*
3 | * 路由跳转权限控制
4 | *
5 | * @format
6 | */
7 |
8 | import Vue from 'vue'
9 | import { $auth } from '@helper'
10 |
11 | export default {
12 | // Check the login status
13 | checkLoginAuth(to, from, next) {
14 | if (to.meta.title && to.meta.title[Vue.config.lang]) {
15 | document.title = to.meta.title[Vue.config.lang]
16 | }
17 |
18 | if (to.meta && to.meta.ignoreAuth) {
19 | next()
20 | } else {
21 | if ($auth.checkSession()) {
22 | next()
23 | } else {
24 | next({ path: '/login' })
25 | }
26 | }
27 | },
28 |
29 | // Check page permissions
30 | checkPageAuth(to, from, next) {
31 | if (to.meta && to.meta.ignoreAuth) {
32 | next()
33 | } else {
34 | // check user auth here....
35 | next()
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/router/commonRoutes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * /*
3 | * 通用路由配置,需要放在配置项数组的末端
4 | *
5 | * @format
6 | */
7 |
8 | import NotFound from '@pages/partials/NotFound'
9 |
10 | export default [
11 | {
12 | path: '/',
13 | meta: {
14 | title: 'Vue-cli Overall Example',
15 | ignoreAuth: true
16 | },
17 | component: resolve => require(['@pages/Homepage'], resolve)
18 | },
19 | {
20 | path: '/',
21 | redirect: '/homepage'
22 | },
23 | {
24 | path: '*',
25 | meta: {
26 | title: 'Page Not Found',
27 | ignoreAuth: true
28 | },
29 | component: NotFound
30 | }
31 | ]
32 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | /** @format */
2 |
3 | import Vue from 'vue'
4 | import Router from 'vue-router'
5 | import beforeEachHooks from './beforeEachHooks'
6 | import RoutesMapConfig from './routes'
7 | import commonRoutesMap from './commonRoutes'
8 |
9 | Vue.use(Router)
10 |
11 | const routerInstance = new Router({
12 | mode: 'history',
13 | /* ~~~~~~~~~~~~~~~~~~~~~~~~@CHANGE@~~~~~~~~~~~~~~~~~~~~~~~~ */
14 | /*
15 | @desc: base,应用的基路径;如整个单页应用服务在 /app/ 下,base 就应该设为 "/app/";
16 | @reference: https://router.vuejs.org/zh-cn/api/options.html#base
17 | */
18 | base: '/',
19 | linkActiveClass: 'active',
20 | scrollBehavior: () => ({ y: 0 }),
21 | routes: RoutesMapConfig.concat(commonRoutesMap)
22 | })
23 |
24 | Object.values(beforeEachHooks).forEach(hook => {
25 | routerInstance.beforeEach(hook)
26 | })
27 |
28 | export default routerInstance
29 |
--------------------------------------------------------------------------------
/src/router/routes/index.js:
--------------------------------------------------------------------------------
1 | /** @format */
2 |
3 | const files = require.context('.', true, /\.js$/)
4 |
5 | var configArray = []
6 |
7 | files.keys().forEach(key => {
8 | if (key === './index.js') return
9 | configArray = configArray.concat(files(key).default)
10 | })
11 | export default configArray
12 |
--------------------------------------------------------------------------------
/src/router/routes/main.js:
--------------------------------------------------------------------------------
1 | /** @format */
2 |
3 | export default [
4 | {
5 | path: '/explore',
6 | meta: {
7 | ignoreAuth: true,
8 | title: '发现更多'
9 | },
10 | component: resolve => require(['@pages/ExploreMore'], resolve)
11 | }
12 | ]
13 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | /** @format */
2 |
3 | import Vue from 'vue'
4 | import Vuex from 'vuex'
5 | import createLogger from 'vuex/dist/logger'
6 | import modules from './modules'
7 |
8 | Vue.use(Vuex)
9 |
10 | const debug = process.env.NODE_ENV !== 'production'
11 |
12 | export default new Vuex.Store({
13 | modules,
14 | strict: debug,
15 | plugins: debug ? [createLogger()] : []
16 | })
17 |
--------------------------------------------------------------------------------
/src/store/modules/demo/actions.js:
--------------------------------------------------------------------------------
1 | /** @format */
2 |
3 | export default {
4 | updateDefaultField({ commit }, value) {
5 | commit('UPDATE_DEFAULT_FIELD', value)
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/store/modules/demo/getters.js:
--------------------------------------------------------------------------------
1 | /** @format */
2 |
3 | export default {
4 | defaultField: state => state.defaultField
5 | }
6 |
--------------------------------------------------------------------------------
/src/store/modules/demo/index.js:
--------------------------------------------------------------------------------
1 | /** @format */
2 |
3 | import getters from './getters'
4 | import mutations from './mutations'
5 | import actions from './actions'
6 |
7 | const state = {
8 | defaultField: 1314
9 | }
10 |
11 | export default {
12 | namespaced: true,
13 | state,
14 | getters,
15 | mutations,
16 | actions
17 | }
18 |
--------------------------------------------------------------------------------
/src/store/modules/demo/mutations.js:
--------------------------------------------------------------------------------
1 | /** @format */
2 |
3 | export default {
4 | ['UPDATE_DEFAULT_FIELD'](state, value) {
5 | state.defaultField = value
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/store/modules/index.js:
--------------------------------------------------------------------------------
1 | /** @format */
2 |
3 | const files = require.context('.', true, /index.js$/)
4 | const modules = {}
5 |
6 | files.keys().forEach(key => {
7 | if (key === './index.js') {
8 | return
9 | }
10 | const moduleName = key.split('/')[1]
11 | modules[moduleName] = files(key).default
12 | })
13 |
14 | export default modules
15 |
--------------------------------------------------------------------------------
/tests/unit/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | jest: true
4 | },
5 | rules: {
6 | 'import/no-extraneous-dependencies': 'off'
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/tests/unit/Arrow.spec.js:
--------------------------------------------------------------------------------
1 | /** @format */
2 |
3 | import { shallowMount } from '@vue/test-utils'
4 | import Arrow from '@/components/icons/Arrow.vue'
5 |
6 | describe('Arrow.vue', () => {
7 | // ------------------------Test [props.direction]------------------------Start
8 | it('renders props.direction when passed', () => {
9 | const parpDirection = 'right'
10 | const wrapper = shallowMount(Arrow, {
11 | propsData: { direction: parpDirection }
12 | })
13 | expect(wrapper.classes()).toContain(parpDirection)
14 | })
15 |
16 | it('renders props.direction when NOT passed', () => {
17 | // direction default Value is [left]
18 | const parpDirection = '~~left'
19 | const defaultDirection = 'left'
20 | const wrapper = shallowMount(Arrow, {
21 | propsData: { direction: parpDirection }
22 | })
23 | expect(wrapper.classes()).toContain(defaultDirection)
24 | /* const compClassListStr = wrapper
25 | .find('.arrow-component')
26 | .element.classList.toString()
27 | expect(compClassListStr).toBe(`arrow-component left`) */
28 | })
29 |
30 | // ------------------------Test [props.color]------------------------Start
31 | it('renders props.color when passed', () => {
32 | // color default Vaule is [#479537]
33 | const propColor = '#666666'
34 | const wrapper = shallowMount(Arrow, {
35 | propsData: { color: propColor }
36 | })
37 | const compBorderColor = wrapper.find('.arrow-component').element.style.borderColor
38 | expect(compBorderColor).toBe(propColor)
39 | })
40 |
41 | it('renders props.color when Not passed', () => {
42 | // color default Vaule is [#479537]
43 | const propColor = '#666 666#'
44 | const defaultColor = '#479537'
45 | const wrapper = shallowMount(Arrow, {
46 | propsData: { color: propColor }
47 | })
48 | const compBorderColor = wrapper.find('.arrow-component').element.style.borderColor
49 | expect(compBorderColor).toBe(defaultColor)
50 | })
51 | })
52 |
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | /** @format */
2 |
3 | const path = require('path')
4 | const fs = require('fs')
5 | const SizePlugin = require('size-plugin')
6 |
7 | const isProductionEnvFlag = process.env.NODE_ENV === 'production'
8 |
9 | /* 是否开启 SPA 预渲染,如果要开启,则须另外安装 prerender-spa-plugin 插件 */
10 | const isOpenPrerenderSPA = false
11 |
12 | function resolveRealPath(dir) {
13 | return path.join(__dirname, dir)
14 | }
15 |
16 | function loadGlobalStyles() {
17 | const variables = fs.readFileSync('src/assets/styles/variables.scss', 'utf-8')
18 | const mixins = fs.readFileSync('src/assets/styles/mixins.scss', 'utf-8')
19 | return variables + mixins
20 | }
21 |
22 | // https://github.com/vuejs/vue-docs-zh-cn/blob/master/vue-cli/config.md
23 | module.exports = {
24 | // Project deployment base
25 | // By default we assume your app will be deployed at the root of a domain,
26 | // e.g. https://www.my-app.com/
27 | // If your app is deployed at a sub-path, you will need to specify that
28 | // sub-path here. For example, if your app is deployed at
29 | // https://www.foobar.com/my-app/
30 | // then change this to '/my-app/'
31 | publicPath: '/',
32 |
33 | // where to output built files
34 | outputDir: 'dist',
35 |
36 | // whether to use eslint-loader for lint on save.
37 | // valid values: true | false | 'error'
38 | // when set to 'error', lint errors will cause compilation to fail.
39 | lintOnSave: true,
40 |
41 | // https://cli.vuejs.org/config/#runtimecompiler
42 | runtimeCompiler: false,
43 |
44 | // babel-loader skips `node_modules` deps by default.
45 | // explicitly transpile a dependency with this option.
46 | transpileDependencies: [
47 | /* string or regex */
48 | ],
49 |
50 | // generate sourceMap for production build?
51 | productionSourceMap: process.env.NODE_ENV !== 'production',
52 |
53 | // https://github.com/vuejs/vue-cli/blob/dev/docs/css.md (#Need to put the top)
54 | css: {
55 | loaderOptions: {
56 | sass: {
57 | data: loadGlobalStyles()
58 | }
59 | }
60 | },
61 |
62 | // tweak internal webpack configuration.
63 | // see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
64 | chainWebpack: config => {
65 | config.resolve.alias
66 | .set('vue$', 'vue/dist/vue.esm.js')
67 | .set('@helper', resolveRealPath('src/helper'))
68 | .set('@pages', resolveRealPath('src/pages'))
69 | .set('@assets', resolveRealPath('src/assets'))
70 | .set('@router', resolveRealPath('src/router'))
71 | .set('@mixins', resolveRealPath('src/mixins'))
72 | .set('@components', resolveRealPath('src/components'))
73 |
74 | // remove the old loader & add new one
75 | config.module.rules.delete('svg')
76 | config.module
77 | .rule('svg')
78 | .test(/\.svg$/)
79 | .use('svg-sprite-loader')
80 | .loader('svg-sprite-loader')
81 | .options({
82 | name: '[name]-[hash:7]',
83 | prefixize: true
84 | })
85 |
86 | const splitOptions = config.optimization.get('splitChunks')
87 | config.optimization.splitChunks(
88 | Object.assign({}, splitOptions, {
89 | // (缺省值5)按需加载时的最大并行请求数
90 | maxAsyncRequests: 16,
91 | // (默认值3)入口点上的最大并行请求数
92 | maxInitialRequests: 16,
93 | // (默认值:1)分割前共享模块的最小块数
94 | minChunks: 1,
95 | // (默认值:30000)块的最小大小
96 | minSize: 30000,
97 | // webpack 将使用块的起源和名称来生成名称: `vendors~main.js`,如项目与"~"冲突,则可通过此值修改,Eg: '-'
98 | automaticNameDelimiter: '~',
99 | // cacheGroups is an object where keys are the cache group names.
100 | name: true,
101 | cacheGroups: {
102 | default: false,
103 | common: {
104 | name: `chunk-common`,
105 | minChunks: 2,
106 | priority: -20,
107 | chunks: 'initial',
108 | reuseExistingChunk: true
109 | },
110 | element: {
111 | name: 'element',
112 | test: /[\\/]node_modules[\\/]element-ui[\\/]/,
113 | chunks: 'initial',
114 | // 默认组的优先级为负数,以允许任何自定义缓存组具有更高的优先级(默认值为0)
115 | priority: -30
116 | }
117 | }
118 | })
119 | )
120 |
121 | // https://github.com/webpack-contrib/webpack-bundle-analyzer
122 | if (process.env.npm_config_report) {
123 | config
124 | .plugin('webpack-bundle-analyzer')
125 | .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin)
126 | }
127 | },
128 |
129 | configureWebpack: {
130 | plugins: [
131 | isProductionEnvFlag && isOpenPrerenderSPA
132 | ? new require('prerender-spa-plugin')({
133 | // Required - The path to the webpack-outputted app to prerender.
134 | staticDir: path.join(__dirname, 'dist'),
135 | // Required - Routes to render.
136 | routes: ['/', '/explore']
137 | })
138 | : () => {},
139 | isProductionEnvFlag ? new SizePlugin() : () => {}
140 | ]
141 | },
142 |
143 | // use thread-loader for babel & TS in production build
144 | // enabled by default if the machine has more than 1 cores
145 | parallel: require('os').cpus().length > 1,
146 |
147 | // options for the PWA plugin.
148 | // see => https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa
149 | // https://developers.google.com/web/tools/workbox/modules/workbox-webpack-plugin
150 | pwa: {
151 | name: 'Vue-Cli3 实践',
152 | themeColor: '#4DBA87',
153 | msTileColor: '#000000',
154 | appleMobileWebAppCapable: 'yes',
155 | appleMobileWebAppStatusBarStyle: 'black',
156 | iconPaths: {
157 | favicon32: 'img/icons/fuji-mountain-32x32.png',
158 | favicon16: 'img/icons/fuji-mountain-16x16.png',
159 | appleTouchIcon: 'img/icons/apple-touch-icon-152x152.png',
160 | maskIcon: 'img/icons/safari-pinned-tab.svg',
161 | msTileImage: 'img/icons/msapplication-icon-144x144.png'
162 | },
163 | // configure the workbox plugin (GenerateSW or InjectManifest)
164 | workboxPluginMode: 'InjectManifest',
165 | workboxOptions: {
166 | // swSrc is required in InjectManifest mode.
167 | swSrc: 'public/service-worker.js'
168 | // ...other Workbox options...
169 | }
170 | },
171 |
172 | // configure webpack-dev-server behavior
173 | devServer: {
174 | open: process.platform === 'darwin',
175 | host: '0.0.0.0',
176 | port: 8080,
177 | https: false,
178 | hotOnly: false,
179 | // See https://github.com/vuejs/vue-cli/blob/dev/docs/cli-service.md#configuring-proxy
180 | proxy: null, // string | Object
181 | before: app => {}
182 | },
183 |
184 | // options for 3rd party plugins
185 | pluginOptions: {}
186 | }
187 |
--------------------------------------------------------------------------------