├── .editorconfig
├── .gitignore
├── LICENSE
├── README.md
├── babel.config.js
├── package-lock.json
├── package.json
├── public
├── favicon.ico
└── index.html
├── src
├── App.vue
├── assets
│ └── logo.png
├── components
│ └── HelloWorld.vue
├── main.js
├── plugins
│ └── axios.js
├── router.js
├── store.js
└── views
│ ├── About.vue
│ └── Home.vue
└── vue.config.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.{js,jsx,ts,tsx,vue}]
2 | indent_style = space
3 | indent_size = 2
4 | trim_trailing_whitespace = true
5 | insert_final_newline = true
6 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 樊小书生
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 | # vue-cli-vant-starter #
2 |
3 | vue-cli 和 vant 结合的项目开发模板,主要内容如下(后续新增的内容会在其后使用方括号标识添加时间):
4 |
5 | 1. 将 `axios` 或者 `$axios` 添加到 `Vue.prototype` ,对 `axios` 的封装是基于 [vue-cli-plugin-axios](https://www.npmjs.com/package/vue-cli-plugin-axios) 插件。
6 | 2. 对 `axios` 的拦截器进行配置,调用 `axios` 发送请求的时候自动添加 `loading` 效果,请求返回的时候,判断请求内容的正确,错误的话直接提示错误信息。
7 | 3. 并发请求时,只有当最后一个请求完成的时候才会关闭 `loading` 效果。
8 | 4. 添加在路由切换的时候取消上个页面未完成的 `axios` 请求。
9 | 5. 添加本地代理 [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware) 及相应的配置说明。
10 | 6. 增加自定义参数:
11 | 1. `showLoading`: 是否需要 `loading` 加载,至于样式仍是有你自由控制。
12 | 2. 关于在短时间内发送多条相同请求处理逻辑的相关字段:
13 | 1. 本插件默认是按照仅保留最初的发送请求链接。
14 | 2. `needLast`: 需要最新的发送请求链接,以前发送的可以直接 `cancel` ,这种情况一般适用于搜索框发送请求。
15 | 3. `needAll`: 所有的发送请求链接都需要
16 |
17 | 7. vant 引入组件采用的是[自动按需引入插件](https://youzan.github.io/vant/#/zh-CN/quickstart),均已经配置完善。
18 | 8. Rem 适配,使用以下两个工具:
19 | 1. [postcss-pxtorem](https://github.com/cuth/postcss-pxtorem) 是一款 postcss 插件,用于将单位转化为 rem
20 | 2. [lib-flexible](https://github.com/amfe/lib-flexible) 用于设置 rem 基准值
21 | 3. 设计图标准按照 375 * 667 的尺寸给出,布局的时候除了 1px 其他全部按照 测量的距离 写就可以了
22 |
23 | ## 模板说明 ##
24 |
25 | 1. 本模板暂时的技术栈为: vue + vue-cli + vue-router + vuex + vant + axios + less(如果你使用的是其他的css预编译器请自行配置)
26 |
27 | ## 项目使用 ##
28 |
29 | 1. 先将本项目 Fork 到你的 github ,方便日后你对其进行修改。
30 | 2. 将你 github 上的远程库克隆到本地。
31 |
32 | 或者是将本项目克隆到本地,去除远程库,再添加你自己的远程库:
33 |
34 | ```
35 | git remote rm origin
36 | git remote add origin <远程库地址>
37 | ```
38 |
39 | ### 安装依赖及插件 ###
40 |
41 | ```
42 | npm install
43 | ```
44 |
45 | ### 启动开发模式(热更新) ###
46 |
47 | ```
48 | npm run serve
49 | ```
50 |
51 | ### 生产模式打包 ###
52 |
53 | ```
54 | npm run build
55 | ```
56 |
57 | ### 更多详细配置 ###
58 |
59 | 请看 [Configuration Reference](https://cli.vuejs.org/config/)。
60 |
61 | 如果你使用的是 `vue ui` ,会发现项目名称可能不是你需要的,这个时候只需要将 `package.json` 和 `package-lock.json` 文件中的 `name` 修改为你希望的名称,然后重启项目。
62 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['@vue/app'],
3 | plugins: [
4 | ['import', {
5 | libraryName: 'vant',
6 | libraryDirectory: 'es',
7 | style: true
8 | }, 'vant']
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-cli-vant-starter",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "dependencies": {
11 | "amfe-flexible": "^2.2.1",
12 | "core-js": "^2.6.5",
13 | "vant": "^2.0.0-beta.2",
14 | "vue": "^2.6.10",
15 | "vue-router": "^3.0.3",
16 | "vuex": "^3.0.1"
17 | },
18 | "devDependencies": {
19 | "@babel/plugin-syntax-dynamic-import": "^7.2.0",
20 | "@vue/cli-plugin-babel": "^3.8.0",
21 | "@vue/cli-plugin-eslint": "^3.8.0",
22 | "@vue/cli-service": "^3.8.0",
23 | "@vue/eslint-config-standard": "^4.0.0",
24 | "axios": "^0.18.0",
25 | "babel-eslint": "^10.0.1",
26 | "babel-plugin-import": "^1.11.2",
27 | "eslint": "^5.16.0",
28 | "eslint-plugin-vue": "^5.0.0",
29 | "http-proxy-middleware": "^0.19.1",
30 | "less": "^3.0.4",
31 | "less-loader": "^4.1.0",
32 | "postcss-pxtorem": "^4.0.1",
33 | "vue-cli-plugin-axios": "0.0.4",
34 | "vue-template-compiler": "^2.6.10"
35 | },
36 | "eslintConfig": {
37 | "root": true,
38 | "env": {
39 | "node": true
40 | },
41 | "extends": [
42 | "plugin:vue/essential",
43 | "@vue/standard"
44 | ],
45 | "rules": {},
46 | "parserOptions": {
47 | "parser": "babel-eslint"
48 | }
49 | },
50 | "postcss": {
51 | "plugins": {
52 | "autoprefixer": {}
53 | }
54 | },
55 | "browserslist": [
56 | "> 1%",
57 | "last 2 versions"
58 | ]
59 | }
60 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fxss5201/vue-cli-vant-starter/a7c6804d01dd8af278780055b7d932e0a41276bd/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | vue-cli-vant-starter
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Home |
5 | About
6 |
7 |
8 |
9 |
10 |
11 |
31 |
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fxss5201/vue-cli-vant-starter/a7c6804d01dd8af278780055b7d932e0a41276bd/src/assets/logo.png
--------------------------------------------------------------------------------
/src/components/HelloWorld.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ msg }}
4 |
5 | For a guide and recipes on how to configure / customize this project,
6 | check out the
7 | vue-cli documentation.
8 |
9 |
Installed CLI Plugins
10 |
14 |
Essential Links
15 |
22 |
Ecosystem
23 |
30 |
31 |
32 |
33 |
41 |
42 |
43 |
59 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import './plugins/axios'
3 | import 'amfe-flexible'
4 | import { Toast } from 'vant'
5 | import App from './App.vue'
6 | import router from './router'
7 | import store from './store'
8 |
9 | Vue.use(Toast)
10 |
11 | Vue.config.productionTip = false
12 |
13 | router.beforeEach((to, from, next) => {
14 | Vue.prototype.$toast.clear()
15 | const CancelToken = Vue.axios.CancelToken
16 | store.state.source.cancel && store.state.source.cancel()
17 | store.commit('setSource', CancelToken.source())
18 | next()
19 | })
20 |
21 | new Vue({
22 | router,
23 | store,
24 | render: h => h(App)
25 | }).$mount('#app')
26 |
--------------------------------------------------------------------------------
/src/plugins/axios.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | import Vue from 'vue'
4 | import axios from 'axios'
5 | import store from './../store'
6 |
7 | // Full config: https://github.com/axios/axios#request-config
8 | // axios.defaults.baseURL = process.env.baseURL || process.env.apiUrl || '';
9 | // axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
10 | // axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
11 |
12 | let config = {
13 | // baseURL: process.env.baseURL || process.env.apiUrl || ""
14 | // timeout: 60 * 1000, // Timeout
15 | // withCredentials: true, // Check cross-site Access-Control
16 | }
17 |
18 | const _axios = axios.create(config)
19 | _axios.CancelToken = axios.CancelToken
20 | _axios.isCancel = axios.isCancel
21 | _axios.all = axios.all
22 |
23 | let pageAxiosList = new Set() // 用于解决同时请求多个 service 接口时,必须等最慢的接口返回数据之后再关闭 loading
24 | let axiosSource // 需要最新的链接的保存参数的地方
25 |
26 | _axios.interceptors.request.use(
27 | function (config) {
28 | // Do something before request is sent
29 | if (config.showLoading && !pageAxiosList.size) {
30 | Vue.prototype.$toast.loading({
31 | duration: 0,
32 | mask: true,
33 | forbidClick: true,
34 | message: '加载中...',
35 | loadingType: 'spinner'
36 | })
37 | }
38 | // 检查 pageAxiosList 里面是否有已经发送相同接口 url ,如果有的话直接取消发送
39 | if (config.needLast) {
40 | // 请求链接需要最新的
41 | const CancelToken = Vue.axios.CancelToken
42 | axiosSource && axiosSource.cancel && axiosSource.cancel()
43 | axiosSource = CancelToken.source()
44 | config.cancelToken = axiosSource.token
45 | } else if (config.needAll) {
46 | // console.log("needAll");
47 | } else {
48 | if (pageAxiosList.has(config.url)) {
49 | return Promise.reject(new Error('alreadySent'))
50 | }
51 | pageAxiosList.add(config.url)
52 | config.cancelToken = store.state.source.token
53 | }
54 | return config
55 | },
56 | function (error) {
57 | // Do something with request error
58 | Vue.prototype.$toast('网络出错,请重试')
59 | return Promise.reject(error)
60 | }
61 | )
62 |
63 | // Add a response interceptor
64 | _axios.interceptors.response.use(
65 | function (response) {
66 | // Do something with response data
67 | let nowUrl = response.config.url
68 | if (pageAxiosList.has(nowUrl)) {
69 | pageAxiosList.delete(nowUrl)
70 | }
71 | if (response.config.showLoading && !pageAxiosList.size) {
72 | Vue.prototype.$toast.clear()
73 | }
74 | if (response.data.isok) {
75 | return response.data
76 | } else {
77 | Vue.prototype.$toast('网络出错,请重试')
78 | }
79 | return response
80 | },
81 | function (error) {
82 | // Do something with response error
83 | if (_axios.isCancel(error)) {
84 | // 判断是否是切换路由导致的取消,如果是的话还需要将 pageAxiosList 清空
85 | for (let item of pageAxiosList.keys()) {
86 | pageAxiosList.delete(item)
87 | }
88 | Vue.prototype.$toast.clear()
89 | } else if (error.message === 'alreadySent') {
90 | console.log('alreadySent')
91 | } else {
92 | Vue.prototype.$toast('网络出错,请重试')
93 | }
94 | return Promise.reject(error)
95 | }
96 | )
97 |
98 | Plugin.install = function (Vue, options) {
99 | Vue.axios = _axios
100 | window.axios = _axios
101 | Object.defineProperties(Vue.prototype, {
102 | axios: {
103 | get () {
104 | return _axios
105 | }
106 | },
107 | $axios: {
108 | get () {
109 | return _axios
110 | }
111 | }
112 | })
113 | }
114 |
115 | Vue.use(Plugin)
116 |
117 | export default Plugin
118 |
--------------------------------------------------------------------------------
/src/router.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 | const Home = () => import(/* webpackChunkName: "home" */ './views/Home.vue')
4 | const About = () => import(/* webpackChunkName: "about" */ './views/About.vue')
5 |
6 | Vue.use(Router)
7 |
8 | export default new Router({
9 | routes: [
10 | {
11 | path: '/',
12 | name: 'home',
13 | component: Home
14 | },
15 | {
16 | path: '/about',
17 | name: 'about',
18 | component: About
19 | }
20 | ]
21 | })
22 |
--------------------------------------------------------------------------------
/src/store.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 | import axios from 'axios'
4 |
5 | Vue.use(Vuex)
6 | const CancelToken = axios.CancelToken
7 |
8 | export default new Vuex.Store({
9 | state: {
10 | source: CancelToken.source()
11 | },
12 | mutations: {
13 | setSource (state, obj) {
14 | // 路由切换新建新建取消令牌,并写入store
15 | state.source = obj
16 | }
17 | },
18 | actions: {
19 |
20 | }
21 | })
22 |
--------------------------------------------------------------------------------
/src/views/About.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
This is an about page
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/views/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |

4 |
5 |
6 |
7 |
8 |
32 |
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | const autoprefixer = require('autoprefixer')
2 | const pxtorem = require('postcss-pxtorem')
3 |
4 | module.exports = {
5 | css: {
6 | sourceMap: true,
7 | loaderOptions: {
8 | postcss: {
9 | plugins: [
10 | autoprefixer({
11 | browsers: ['Android >= 4.0', 'iOS >= 7']
12 | }),
13 | pxtorem({
14 | rootValue: 37.5,
15 | propList: ['*'],
16 | minPixelValue: 1
17 | })
18 | ]
19 | }
20 | }
21 | },
22 | devServer: {
23 | proxy: {
24 | '/api': {
25 | target: '',
26 | ws: true,
27 | changeOrigin: true
28 | }
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------