readme
@ -1,2 +0,0 @@
|
|||||||
> 1%
|
|
||||||
last 2 versions
|
|
@ -1,14 +0,0 @@
|
|||||||
# http://editorconfig.org
|
|
||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
charset = utf-8
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
end_of_line = lf
|
|
||||||
insert_final_newline = true
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
|
|
||||||
[*.md]
|
|
||||||
insert_final_newline = false
|
|
||||||
trim_trailing_whitespace = false
|
|
@ -1,4 +0,0 @@
|
|||||||
NODE_ENV='development'
|
|
||||||
# must start with VUE_APP_
|
|
||||||
VUE_APP_ENV = 'development'
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
|||||||
NODE_ENV='production'
|
|
||||||
# must start with VUE_APP_
|
|
||||||
VUE_APP_ENV = 'production'
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
|||||||
NODE_ENV='production'
|
|
||||||
# must start with VUE_APP_
|
|
||||||
VUE_APP_ENV = 'staging'
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
|||||||
build/*.js
|
|
||||||
src/assets
|
|
||||||
public
|
|
||||||
dist
|
|
192
.eslintrc.js
@ -1,192 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
env: {
|
|
||||||
node: true
|
|
||||||
},
|
|
||||||
extends: ['plugin:vue/essential', 'eslint:recommended', '@vue/prettier'],
|
|
||||||
parserOptions: {
|
|
||||||
parser: 'babel-eslint'
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
"vue/max-attributes-per-line": [2, {
|
|
||||||
"singleline": 10,
|
|
||||||
"multiline": {
|
|
||||||
"max": 1,
|
|
||||||
"allowFirstLine": false
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
"vue/singleline-html-element-content-newline": "off",
|
|
||||||
"vue/multiline-html-element-content-newline":"off",
|
|
||||||
"vue/name-property-casing": ["error", "PascalCase"],
|
|
||||||
"vue/no-v-html": "off",
|
|
||||||
'accessor-pairs': 2,
|
|
||||||
'arrow-spacing': [2, {
|
|
||||||
'before': true,
|
|
||||||
'after': true
|
|
||||||
}],
|
|
||||||
'block-spacing': [2, 'always'],
|
|
||||||
'brace-style': [2, '1tbs', {
|
|
||||||
'allowSingleLine': true
|
|
||||||
}],
|
|
||||||
'camelcase': [0, {
|
|
||||||
'properties': 'always'
|
|
||||||
}],
|
|
||||||
'comma-dangle': [2, 'never'],
|
|
||||||
'comma-spacing': [2, {
|
|
||||||
'before': false,
|
|
||||||
'after': true
|
|
||||||
}],
|
|
||||||
'comma-style': [2, 'last'],
|
|
||||||
'constructor-super': 2,
|
|
||||||
'curly': [2, 'multi-line'],
|
|
||||||
'dot-location': [2, 'property'],
|
|
||||||
'eol-last': 2,
|
|
||||||
'eqeqeq': ["error", "always", {"null": "ignore"}],
|
|
||||||
'generator-star-spacing': [2, {
|
|
||||||
'before': true,
|
|
||||||
'after': true
|
|
||||||
}],
|
|
||||||
'handle-callback-err': [2, '^(err|error)$'],
|
|
||||||
'indent': [2, 2, {
|
|
||||||
'SwitchCase': 1
|
|
||||||
}],
|
|
||||||
'jsx-quotes': [2, 'prefer-single'],
|
|
||||||
'key-spacing': [2, {
|
|
||||||
'beforeColon': false,
|
|
||||||
'afterColon': true
|
|
||||||
}],
|
|
||||||
'keyword-spacing': [2, {
|
|
||||||
'before': true,
|
|
||||||
'after': true
|
|
||||||
}],
|
|
||||||
'new-cap': [2, {
|
|
||||||
'newIsCap': true,
|
|
||||||
'capIsNew': false
|
|
||||||
}],
|
|
||||||
'new-parens': 2,
|
|
||||||
'no-array-constructor': 2,
|
|
||||||
'no-caller': 2,
|
|
||||||
'no-console': 'off',
|
|
||||||
'no-class-assign': 2,
|
|
||||||
'no-cond-assign': 2,
|
|
||||||
'no-const-assign': 2,
|
|
||||||
'no-control-regex': 0,
|
|
||||||
'no-delete-var': 2,
|
|
||||||
'no-dupe-args': 2,
|
|
||||||
'no-dupe-class-members': 2,
|
|
||||||
'no-dupe-keys': 2,
|
|
||||||
'no-duplicate-case': 2,
|
|
||||||
'no-empty-character-class': 2,
|
|
||||||
'no-empty-pattern': 2,
|
|
||||||
'no-eval': 2,
|
|
||||||
'no-ex-assign': 2,
|
|
||||||
'no-extend-native': 2,
|
|
||||||
'no-extra-bind': 2,
|
|
||||||
'no-extra-boolean-cast': 2,
|
|
||||||
'no-extra-parens': [2, 'functions'],
|
|
||||||
'no-fallthrough': 2,
|
|
||||||
'no-floating-decimal': 2,
|
|
||||||
'no-func-assign': 2,
|
|
||||||
'no-implied-eval': 2,
|
|
||||||
'no-inner-declarations': [2, 'functions'],
|
|
||||||
'no-invalid-regexp': 2,
|
|
||||||
'no-irregular-whitespace': 2,
|
|
||||||
'no-iterator': 2,
|
|
||||||
'no-label-var': 2,
|
|
||||||
'no-labels': [2, {
|
|
||||||
'allowLoop': false,
|
|
||||||
'allowSwitch': false
|
|
||||||
}],
|
|
||||||
'no-lone-blocks': 2,
|
|
||||||
'no-mixed-spaces-and-tabs': 2,
|
|
||||||
'no-multi-spaces': 2,
|
|
||||||
'no-multi-str': 2,
|
|
||||||
'no-multiple-empty-lines': [2, {
|
|
||||||
'max': 1
|
|
||||||
}],
|
|
||||||
'no-native-reassign': 2,
|
|
||||||
'no-negated-in-lhs': 2,
|
|
||||||
'no-new-object': 2,
|
|
||||||
'no-new-require': 2,
|
|
||||||
'no-new-symbol': 2,
|
|
||||||
'no-new-wrappers': 2,
|
|
||||||
'no-obj-calls': 2,
|
|
||||||
'no-octal': 2,
|
|
||||||
'no-octal-escape': 2,
|
|
||||||
'no-path-concat': 2,
|
|
||||||
'no-proto': 2,
|
|
||||||
'no-redeclare': 2,
|
|
||||||
'no-regex-spaces': 2,
|
|
||||||
'no-return-assign': [2, 'except-parens'],
|
|
||||||
'no-self-assign': 2,
|
|
||||||
'no-self-compare': 2,
|
|
||||||
'no-sequences': 2,
|
|
||||||
'no-shadow-restricted-names': 2,
|
|
||||||
'no-spaced-func': 2,
|
|
||||||
'no-sparse-arrays': 2,
|
|
||||||
'no-this-before-super': 2,
|
|
||||||
'no-throw-literal': 2,
|
|
||||||
'no-trailing-spaces': 2,
|
|
||||||
'no-undef': 2,
|
|
||||||
'no-undef-init': 2,
|
|
||||||
'no-unexpected-multiline': 2,
|
|
||||||
'no-unmodified-loop-condition': 2,
|
|
||||||
'no-unneeded-ternary': [2, {
|
|
||||||
'defaultAssignment': false
|
|
||||||
}],
|
|
||||||
'no-unreachable': 2,
|
|
||||||
'no-unsafe-finally': 2,
|
|
||||||
'no-unused-vars': [2, {
|
|
||||||
'vars': 'all',
|
|
||||||
'args': 'none'
|
|
||||||
}],
|
|
||||||
'no-useless-call': 2,
|
|
||||||
'no-useless-computed-key': 2,
|
|
||||||
'no-useless-constructor': 2,
|
|
||||||
'no-useless-escape': 0,
|
|
||||||
'no-whitespace-before-property': 2,
|
|
||||||
'no-with': 2,
|
|
||||||
'one-var': [2, {
|
|
||||||
'initialized': 'never'
|
|
||||||
}],
|
|
||||||
'operator-linebreak': [2, 'after', {
|
|
||||||
'overrides': {
|
|
||||||
'?': 'before',
|
|
||||||
':': 'before'
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
'padded-blocks': [2, 'never'],
|
|
||||||
'quotes': [2, 'single', {
|
|
||||||
'avoidEscape': true,
|
|
||||||
'allowTemplateLiterals': true
|
|
||||||
}],
|
|
||||||
'semi': [2, 'never'],
|
|
||||||
'semi-spacing': [2, {
|
|
||||||
'before': false,
|
|
||||||
'after': true
|
|
||||||
}],
|
|
||||||
'space-before-blocks': [2, 'always'],
|
|
||||||
'space-before-function-paren': [2, 'never'],
|
|
||||||
'space-in-parens': [2, 'never'],
|
|
||||||
'space-infix-ops': 2,
|
|
||||||
'space-unary-ops': [2, {
|
|
||||||
'words': true,
|
|
||||||
'nonwords': false
|
|
||||||
}],
|
|
||||||
'spaced-comment': [2, 'always', {
|
|
||||||
'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
|
|
||||||
}],
|
|
||||||
'template-curly-spacing': [2, 'never'],
|
|
||||||
'use-isnan': 2,
|
|
||||||
'valid-typeof': 2,
|
|
||||||
'wrap-iife': [2, 'any'],
|
|
||||||
'yield-star-spacing': [2, 'both'],
|
|
||||||
'yoda': [2, 'never'],
|
|
||||||
'prefer-const': 2,
|
|
||||||
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
|
|
||||||
'object-curly-spacing': [2, 'always', {
|
|
||||||
objectsInObjects: false
|
|
||||||
}],
|
|
||||||
'array-bracket-spacing': [2, 'never']
|
|
||||||
}
|
|
||||||
}
|
|
24
.gitignore
vendored
@ -1,24 +0,0 @@
|
|||||||
.DS_Store
|
|
||||||
node_modules
|
|
||||||
/dist
|
|
||||||
/docs
|
|
||||||
# local env files
|
|
||||||
.env.local
|
|
||||||
.env.*.local
|
|
||||||
|
|
||||||
# Log files
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
|
|
||||||
# Editor directories and files
|
|
||||||
.idea
|
|
||||||
.vscode
|
|
||||||
*.suo
|
|
||||||
*.ntvs*
|
|
||||||
*.njsproj
|
|
||||||
*.sln
|
|
||||||
*.sw?
|
|
||||||
|
|
||||||
package-lock.json
|
|
||||||
yarn.lock
|
|
@ -1,13 +0,0 @@
|
|||||||
// https://github.com/michael-ciniawsky/postcss-load-config
|
|
||||||
module.exports = {
|
|
||||||
plugins: {
|
|
||||||
autoprefixer: {
|
|
||||||
overrideBrowserslist: ['Android 4.1', 'iOS 7.1', 'Chrome > 31', 'ff > 31', 'ie >= 8']
|
|
||||||
},
|
|
||||||
'postcss-pxtorem': {
|
|
||||||
rootValue: 37.5,
|
|
||||||
propList: ['*'],
|
|
||||||
//selectorBlackList: ['van-']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
24
.prettierrc
@ -1,24 +0,0 @@
|
|||||||
{
|
|
||||||
"printWidth": 120,
|
|
||||||
"tabWidth": 2,
|
|
||||||
"singleQuote": true,
|
|
||||||
"trailingComma": "none",
|
|
||||||
"semi": false,
|
|
||||||
"wrap_line_length": 120,
|
|
||||||
"wrap_attributes": "auto",
|
|
||||||
"proseWrap": "always",
|
|
||||||
"arrowParens": "avoid",
|
|
||||||
"bracketSpacing": true,
|
|
||||||
"jsxBracketSameLine": true,
|
|
||||||
"useTabs": false,
|
|
||||||
"eslintIntegration":true,
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": ".prettierrc",
|
|
||||||
"options": {
|
|
||||||
"parser": "json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"endOfLine": "auto"
|
|
||||||
}
|
|
45
README.en.md
@ -1,45 +0,0 @@
|
|||||||
# vue-h5-template
|
|
||||||
|
|
||||||
基于vue-cli3.0+webpack 4+vant ui + sass+ rem适配方案+axios封装,构建手机端模板脚手架
|
|
||||||
|
|
||||||
#### 介绍
|
|
||||||
[关于项目介绍](https://segmentfault.com/a/1190000019275330)
|
|
||||||
|
|
||||||
1. vuecli3.0
|
|
||||||
2. 多环境开发
|
|
||||||
3. axios封装
|
|
||||||
4. rem适配方案
|
|
||||||
5. 生产环境cdn优化首屏加速
|
|
||||||
6. babel低版本浏览器兼容
|
|
||||||
7. 环境发布脚本
|
|
||||||
|
|
||||||
#### 多环境
|
|
||||||
|
|
||||||
|
|
||||||
之前写过一个多环境的方案,是基于vue-cli2.0的 [vue多环境配置方案-传送门](https://segmentfault.com/a/1190000019136606)
|
|
||||||
最近新的项目采用了vuecli3.0开始了一番折腾
|
|
||||||
|
|
||||||
这里参考了[vue-admin-template](https://github.com/PanJiaChen/vue-admin-template) 基本思路不变
|
|
||||||
在src的文件里新建config 根据不同的环境去引用不同的配置文件,不同的是在根目录下有三个设置环境变量的文件
|
|
||||||
这里可以参考vue-admin-template
|
|
||||||
|
|
||||||
#### rem适配方案
|
|
||||||
|
|
||||||
还是那句话,用vw还是用rem,这是个问题?
|
|
||||||
|
|
||||||
选用rem的原因是因为vant直接给到了这个适配方案,个人也比较喜欢这个方案
|
|
||||||
|
|
||||||
[vant](https://youzan.github.io/vant/#/zh-CN/quickstart)
|
|
||||||
|
|
||||||
#### 总结
|
|
||||||
|
|
||||||
因为项目刚刚构建起来,后面还会持续更新,实际使用过程中一定还有很多问题,如果文章中有错误希望能够被指正,一起成长
|
|
||||||
|
|
||||||
# 关于我
|
|
||||||
|
|
||||||
您可以扫描添加下方的微信并备注 Soul 加交流群,给我提意见,交流学习。
|
|
||||||
<p>
|
|
||||||
<img src="https://tweapp.top1buyer.com/mine.jpg" width="256" style="display:inline;">
|
|
||||||
</p>
|
|
||||||
|
|
||||||
如果对你有帮助送我一颗小星星(づ ̄3 ̄)づ╭❤~
|
|
948
README.md
@ -1,949 +1,33 @@
|
|||||||
# vue-h5-template
|
# Vue H5 Template 🎉
|
||||||
|
|
||||||
基于 vue-cli4.0 + webpack 4 + vant ui + sass+ rem 适配方案+axios 封装,构建手机端模板脚手架
|
> [Vue H5 Template](https://github.com/sunnie1992/sol-weapp) 是基于 vue-cli4.0+webpack 4+vant ui + sass+ rem 适配方案+axios 封装,构建手机端模板脚手架。
|
||||||
|
|
||||||
掘金: [vue-cli4 vant rem 移动端框架方案](https://juejin.im/post/5cfefc73f265da1bba58f9f7)
|
#### 预览
|
||||||
|
|
||||||
[查看 demo](https://solui.cn/vue-h5-template/#/) 建议手机端查看
|
[查看 demo](https://solui.cn/vue-h5-template/#/) 建议手机端查看
|
||||||
|
|
||||||
<p>
|
手机扫码查看
|
||||||
<img src="./static/demo.png" width="320" style="display:inline;">
|
|
||||||
</p>
|
|
||||||
|
|
||||||
### Node 版本要求
|

|
||||||
|
|
||||||
`Vue CLI` 需要 Node.js 8.9 或更高版本 (推荐 8.11.0+)。你可以使用 [nvm](https://github.com/nvm-sh/nvm) 或
|
#### 关注我的知乎,掘金
|
||||||
[nvm-windows](https://github.com/coreybutler/nvm-windows) 在同一台电脑中管理多个 Node 版本。
|
|
||||||
|
|
||||||
本示例 Node.js 12.14.1
|
知乎: [开箱即用 vue 全家桶+vant 移动端解决方案](https://zhuanlan.zhihu.com/p/134289924)
|
||||||
|
掘金: [vue-cli4 vant rem 移动端框架方案](https://juejin.im/post/5cfefc73f265da1bba58f9f7)
|
||||||
|
|
||||||
### 启动项目
|
#### 鸣谢
|
||||||
|
|
||||||
```bash
|
|
||||||
|
|
||||||
git clone https://github.com/sunniejs/vue-h5-template.git
|
|
||||||
|
|
||||||
cd vue-h5-template
|
|
||||||
|
|
||||||
npm install
|
|
||||||
|
|
||||||
npm run serve
|
|
||||||
```
|
|
||||||
|
|
||||||
<span id="top">目录</span>
|
|
||||||
|
|
||||||
- √ Vue-cli4
|
|
||||||
- [√ 配置多环境变量](#env)
|
|
||||||
- [√ rem 适配方案](#rem)
|
|
||||||
- [√ VantUI 组件按需加载](#vant)
|
|
||||||
- [√ Sass 全局样式](#sass)
|
|
||||||
- [√ Vuex 状态管理](#vuex)
|
|
||||||
- [√ Vue-router](#router)
|
|
||||||
- [√ Axios 封装及接口管理](#axios)
|
|
||||||
- [√ Webpack 4 vue.config.js 基础配置](#base)
|
|
||||||
- [√ 配置 alias 别名](#alias)
|
|
||||||
- [√ 配置 proxy 跨域](#proxy)
|
|
||||||
- [√ 配置 打包分析](#bundle)
|
|
||||||
- [√ 配置 externals 引入 cdn 资源 ](#externals)
|
|
||||||
- [√ 去掉 console.log ](#console)
|
|
||||||
- [√ splitChunks 单独打包第三方模块](#chunks)
|
|
||||||
- [√ 添加 IE 兼容 ](#ie)
|
|
||||||
- [√ Eslint+Pettier 统一开发规范 ](#pettier)
|
|
||||||
|
|
||||||
### <span id="env">✅ 配置多环境变量 </span>
|
|
||||||
|
|
||||||
`package.json` 里的 `scripts` 配置 `serve` `stage` `build`,通过 `--mode xxx` 来执行不同环境
|
|
||||||
|
|
||||||
- 通过 `npm run serve` 启动本地 , 执行 `development`
|
|
||||||
- 通过 `npm run stage` 打包测试 , 执行 `staging`
|
|
||||||
- 通过 `npm run build` 打包正式 , 执行 `production`
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
"scripts": {
|
|
||||||
"serve": "vue-cli-service serve --open",
|
|
||||||
"stage": "vue-cli-service build --mode staging",
|
|
||||||
"build": "vue-cli-service build",
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
##### 配置介绍
|
|
||||||
|
|
||||||
  以 `VUE_APP_` 开头的变量,在代码中可以通过 `process.env.VUE_APP_` 访问。
|
|
||||||
  比如,`VUE_APP_ENV = 'development'` 通过`process.env.VUE_APP_ENV` 访问。
|
|
||||||
  除了 `VUE_APP_*` 变量之外,在你的应用代码中始终可用的还有两个特殊的变量`NODE_ENV` 和`BASE_URL`
|
|
||||||
|
|
||||||
在项目根目录中新建`.env.*`
|
|
||||||
|
|
||||||
- .env.development 本地开发环境配置
|
|
||||||
|
|
||||||
```bash
|
|
||||||
NODE_ENV='development'
|
|
||||||
# must start with VUE_APP_
|
|
||||||
VUE_APP_ENV = 'development'
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
- .env.staging 测试环境配置
|
|
||||||
|
|
||||||
```bash
|
|
||||||
NODE_ENV='production'
|
|
||||||
# must start with VUE_APP_
|
|
||||||
VUE_APP_ENV = 'staging'
|
|
||||||
```
|
|
||||||
|
|
||||||
- .env.production 正式环境配置
|
|
||||||
|
|
||||||
```bash
|
|
||||||
NODE_ENV='production'
|
|
||||||
# must start with VUE_APP_
|
|
||||||
VUE_APP_ENV = 'production'
|
|
||||||
```
|
|
||||||
|
|
||||||
这里我们并没有定义很多变量,只定义了基础的 VUE_APP_ENV `development` `staging` `production`
|
|
||||||
变量我们统一在 `src/config/env.*.js` 里进行管理。
|
|
||||||
|
|
||||||
这里有个问题,既然这里有了根据不同环境设置变量的文件,为什么还要去 config 下新建三个对应的文件呢?
|
|
||||||
**修改起来方便,不需
|
|
||||||
要重启项目,符合开发习惯。**
|
|
||||||
|
|
||||||
config/index.js
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// 根据环境引入不同配置 process.env.NODE_ENV
|
|
||||||
const config = require('./env.' + process.env.VUE_APP_ENV)
|
|
||||||
module.exports = config
|
|
||||||
```
|
|
||||||
|
|
||||||
配置对应环境的变量,拿本地环境文件 `env.development.js` 举例,用户可以根据需求修改
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// 本地环境配置
|
|
||||||
module.exports = {
|
|
||||||
title: 'vue-h5-template',
|
|
||||||
baseUrl: 'http://localhost:9018', // 项目地址
|
|
||||||
baseApi: 'https://test.xxx.com/api', // 本地api请求地址
|
|
||||||
APPID: 'xxx',
|
|
||||||
APPSECRET: 'xxx'
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
根据环境不同,变量就会不同了
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// 根据环境不同引入不同baseApi地址
|
|
||||||
import {baseApi} from '@/config'
|
|
||||||
console.log(baseApi)
|
|
||||||
```
|
|
||||||
|
|
||||||
[▲ 回顶部](#top)
|
|
||||||
|
|
||||||
### <span id="rem">✅ rem 适配方案 </span>
|
|
||||||
|
|
||||||
不用担心,项目已经配置好了 `rem` 适配, 下面仅做介绍:
|
|
||||||
|
|
||||||
Vant 中的样式默认使用`px`作为单位,如果需要使用`rem`单位,推荐使用以下两个工具:
|
|
||||||
|
|
||||||
- [postcss-pxtorem](https://github.com/cuth/postcss-pxtorem) 是一款 `postcss` 插件,用于将单位转化为 `rem`
|
|
||||||
- [lib-flexible](https://github.com/amfe/lib-flexible) 用于设置 `rem` 基准值
|
|
||||||
|
|
||||||
##### PostCSS 配置
|
|
||||||
|
|
||||||
下面提供了一份基本的 `postcss` 配置,可以在此配置的基础上根据项目需求进行修改
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// https://github.com/michael-ciniawsky/postcss-load-config
|
|
||||||
module.exports = {
|
|
||||||
plugins: {
|
|
||||||
autoprefixer: {
|
|
||||||
overrideBrowserslist: ['Android 4.1', 'iOS 7.1', 'Chrome > 31', 'ff > 31', 'ie >= 8']
|
|
||||||
},
|
|
||||||
'postcss-pxtorem': {
|
|
||||||
rootValue: 37.5,
|
|
||||||
propList: ['*']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
更多详细信息: [vant](https://youzan.github.io/vant/#/zh-CN/quickstart#jin-jie-yong-fa)
|
|
||||||
|
|
||||||
**新手必看,老鸟跳过**
|
|
||||||
|
|
||||||
很多小伙伴会问我,适配的问题。
|
|
||||||
|
|
||||||
我们知道 `1rem` 等于`html` 根元素设定的 `font-size` 的 `px` 值。Vant UI 设置 `rootValue: 37.5`,你可以看到在 iPhone 6 下
|
|
||||||
看到 (`1rem 等于 37.5px`):
|
|
||||||
|
|
||||||
```html
|
|
||||||
<html data-dpr="1" style="font-size: 37.5px;"></html>
|
|
||||||
```
|
|
||||||
|
|
||||||
切换不同的机型,根元素可能会有不同的`font-size`。当你写 css px 样式时,会被程序换算成 `rem` 达到适配。
|
|
||||||
|
|
||||||
因为我们用了 Vant 的组件,需要按照 `rootValue: 37.5` 来写样式。
|
|
||||||
|
|
||||||
举个例子:设计给了你一张 750px \* 1334px 图片,在 iPhone6 上铺满屏幕,其他机型适配。
|
|
||||||
|
|
||||||
- 当`rootValue: 70` , 样式 `width: 750px;height: 1334px;` 图片会撑满 iPhone6 屏幕,这个时候切换其他机型,图片也会跟着撑
|
|
||||||
满。
|
|
||||||
- 当`rootValue: 37.5` 的时候,样式 `width: 375px;height: 667px;` 图片会撑满 iPhone6 屏幕。
|
|
||||||
|
|
||||||
也就是 iphone 6 下 375px 宽度写 CSS。其他的你就可以根据你设计图,去写对应的样式就可以了。
|
|
||||||
|
|
||||||
当然,想要撑满屏幕你可以使用 100%,这里只是举例说明。
|
|
||||||
|
|
||||||
```html
|
|
||||||
<img class="image" src="https://imgs.solui.cn/weapp/logo.png" />
|
|
||||||
|
|
||||||
<style>
|
|
||||||
/* rootValue: 75 */
|
|
||||||
.image {
|
|
||||||
width: 750px;
|
|
||||||
height: 1334px;
|
|
||||||
}
|
|
||||||
/* rootValue: 37.5 */
|
|
||||||
.image {
|
|
||||||
width: 375px;
|
|
||||||
height: 667px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
```
|
|
||||||
|
|
||||||
[▲ 回顶部](#top)
|
|
||||||
|
|
||||||
### <span id="vant">✅ VantUI 组件按需加载 </span>
|
|
||||||
|
|
||||||
项目采
|
|
||||||
用[Vant 自动按需引入组件 (推荐)](https://youzan.github.io/vant/#/zh-CN/quickstart#fang-shi-yi.-zi-dong-an-xu-yin-ru-zu-jian-tui-jian)下
|
|
||||||
面安装插件介绍:
|
|
||||||
|
|
||||||
[babel-plugin-import](https://github.com/ant-design/babel-plugin-import) 是一款 `babel` 插件,它会在编译过程中将
|
|
||||||
`import` 的写法自动转换为按需引入的方式
|
|
||||||
|
|
||||||
#### 安装插件
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm i babel-plugin-import -D
|
|
||||||
```
|
|
||||||
|
|
||||||
在` babel.config.js` 设置
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
|
|
||||||
// 对于使用 babel7 的用户,可以在 babel.config.js 中配置
|
|
||||||
const plugins = [
|
|
||||||
[
|
|
||||||
'import',
|
|
||||||
{
|
|
||||||
libraryName: 'vant',
|
|
||||||
libraryDirectory: 'es',
|
|
||||||
style: true
|
|
||||||
},
|
|
||||||
'vant'
|
|
||||||
]
|
|
||||||
]
|
|
||||||
module.exports = {
|
|
||||||
presets: [['@vue/cli-plugin-babel/preset', {useBuiltIns: 'usage', corejs: 3}]],
|
|
||||||
plugins
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 使用组件
|
|
||||||
|
|
||||||
项目在 `src/plugins/vant.js` 下统一管理组件,用哪个引入哪个,无需在页面里重复引用
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// 按需全局引入 vant组件
|
|
||||||
import Vue from 'vue'
|
|
||||||
import {Button, List, Cell, Tabbar, TabbarItem} from 'vant'
|
|
||||||
Vue.use(Button)
|
|
||||||
Vue.use(Cell)
|
|
||||||
Vue.use(List)
|
|
||||||
Vue.use(Tabbar).use(TabbarItem)
|
|
||||||
```
|
|
||||||
|
|
||||||
[▲ 回顶部](#top)
|
|
||||||
|
|
||||||
### <span id="sass">✅ Sass 全局样式</span>
|
|
||||||
|
|
||||||
首先 你可能会遇到 `node-sass` 安装不成功,别放弃多试几次!!!
|
|
||||||
|
|
||||||
目录结构,在 `src/assets/css/`文件夹下包含了三个文件
|
|
||||||
|
|
||||||
```bash
|
|
||||||
├── assets
|
|
||||||
│ ├── css
|
|
||||||
│ │ ├── index.scss # 全局通用样式
|
|
||||||
│ │ ├── mixin.scss # 全局mixin
|
|
||||||
│ │ └── variables.scss # 全局变量
|
|
||||||
```
|
|
||||||
|
|
||||||
每个页面自己对应的样式都写在自己的 .vue 文件之中
|
|
||||||
|
|
||||||
```html
|
|
||||||
<style lang="scss">
|
|
||||||
/* global styles */
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
/* local styles */
|
|
||||||
</style>
|
|
||||||
```
|
|
||||||
|
|
||||||
`vue.config.js` 配置注入 `sass` 的 `mixin` `variables` 到全局,不需要手动引入 ,配置`$cdn`通过变量形式引入 cdn 地址
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)
|
|
||||||
const defaultSettings = require('./src/config/index.js')
|
|
||||||
module.exports = {
|
|
||||||
css: {
|
|
||||||
extract: IS_PROD,
|
|
||||||
sourceMap: false,
|
|
||||||
loaderOptions: {
|
|
||||||
scss: {
|
|
||||||
// 注入 `sass` 的 `mixin` `variables` 到全局, $cdn可以配置图片cdn
|
|
||||||
// 详情: https://cli.vuejs.org/guide/css.html#passing-options-to-pre-processor-loaders
|
|
||||||
prependData: `
|
|
||||||
@import "assets/css/mixin.scss";
|
|
||||||
@import "assets/css/variables.scss";
|
|
||||||
$cdn: "${defaultSettings.$cdn}";
|
|
||||||
`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
在 `main.js` 中引用全局样式(发现在上面的,prependData 里设置`@import "assets/css/index.scss";`并没有应用全局样式这里在
|
|
||||||
main.js 引入)
|
|
||||||
|
|
||||||
设置 js 中可以访问 `$cdn`,`.vue` 文件中使用`this.$cdn`访问
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// 引入全局样式
|
|
||||||
import '@/assets/css/index.scss'
|
|
||||||
|
|
||||||
// 设置 js中可以访问 $cdn
|
|
||||||
// 引入cdn
|
|
||||||
import {$cdn} from '@/config'
|
|
||||||
Vue.prototype.$cdn = $cdn
|
|
||||||
```
|
|
||||||
|
|
||||||
在 css 和 js 使用
|
|
||||||
|
|
||||||
```html
|
|
||||||
<script>
|
|
||||||
console.log(this.$cdn)
|
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.logo {
|
|
||||||
width: 120px;
|
|
||||||
height: 120px;
|
|
||||||
background: url($cdn+'/weapp/logo.png') center / contain no-repeat;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
```
|
|
||||||
|
|
||||||
[▲ 回顶部](#top)
|
|
||||||
|
|
||||||
### <span id="vuex">✅ Vuex 状态管理</span>
|
|
||||||
|
|
||||||
目录结构
|
|
||||||
|
|
||||||
```bash
|
|
||||||
├── store
|
|
||||||
│ ├── modules
|
|
||||||
│ │ └── app.js
|
|
||||||
│ ├── index.js
|
|
||||||
│ ├── getters.js
|
|
||||||
```
|
|
||||||
|
|
||||||
`main.js` 引入
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
import Vue from 'vue'
|
|
||||||
import App from './App.vue'
|
|
||||||
import store from './store'
|
|
||||||
new Vue({
|
|
||||||
el: '#app',
|
|
||||||
router,
|
|
||||||
store,
|
|
||||||
render: h => h(App)
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
使用
|
|
||||||
|
|
||||||
```html
|
|
||||||
<script>
|
|
||||||
import {mapGetters} from 'vuex'
|
|
||||||
export default {
|
|
||||||
computed: {
|
|
||||||
...mapGetters(['userName'])
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
// Action 通过 store.dispatch 方法触发
|
|
||||||
doDispatch() {
|
|
||||||
this.$store.dispatch('setUserName', '真乖,赶紧关注公众号,组织都在等你~')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
[▲ 回顶部](#top)
|
|
||||||
|
|
||||||
### <span id="router">✅ Vue-router </span>
|
|
||||||
|
|
||||||
本案例采用 `hash` 模式,开发者根据需求修改 `mode` `base`
|
|
||||||
|
|
||||||
**注意**:如果你使用了 `history` 模式,`vue.config.js` 中的 `publicPath` 要做对应的**修改**
|
|
||||||
|
|
||||||
前往:[vue.config.js 基础配置](#base)
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
import Vue from 'vue'
|
|
||||||
import Router from 'vue-router'
|
|
||||||
|
|
||||||
Vue.use(Router)
|
|
||||||
export const router = [
|
|
||||||
{
|
|
||||||
path: '/',
|
|
||||||
name: 'index',
|
|
||||||
component: () => import('@/views/home/index'), // 路由懒加载
|
|
||||||
meta: {
|
|
||||||
title: '首页', // 页面标题
|
|
||||||
keepAlive: false // keep-alive 标识
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
const createRouter = () =>
|
|
||||||
new Router({
|
|
||||||
// mode: 'history', // 如果你是 history模式 需要配置 vue.config.js publicPath
|
|
||||||
// base: '/app/',
|
|
||||||
scrollBehavior: () => ({y: 0}),
|
|
||||||
routes: router
|
|
||||||
})
|
|
||||||
|
|
||||||
export default createRouter()
|
|
||||||
```
|
|
||||||
|
|
||||||
更多:[Vue Router](https://router.vuejs.org/zh/)
|
|
||||||
|
|
||||||
[▲ 回顶部](#top)
|
|
||||||
|
|
||||||
### <span id="axios">✅ Axios 封装及接口管理</span>
|
|
||||||
|
|
||||||
`utils/request.js` 封装 axios ,开发者需要根据后台接口做修改。
|
|
||||||
|
|
||||||
- `service.interceptors.request.use` 里可以设置请求头,比如设置 `token`
|
|
||||||
- `config.hideloading` 是在 api 文件夹下的接口参数里设置,下文会讲
|
|
||||||
- `service.interceptors.response.use` 里可以对接口返回数据处理,比如 401 删除本地信息,重新登录
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
import axios from 'axios'
|
|
||||||
import store from '@/store'
|
|
||||||
import {Toast} from 'vant'
|
|
||||||
// 根据环境不同引入不同api地址
|
|
||||||
import {baseApi} from '@/config'
|
|
||||||
// create an axios instance
|
|
||||||
const service = axios.create({
|
|
||||||
baseURL: baseApi, // url = base api url + request url
|
|
||||||
withCredentials: true, // send cookies when cross-domain requests
|
|
||||||
timeout: 5000 // request timeout
|
|
||||||
})
|
|
||||||
|
|
||||||
// request 拦截器 request interceptor
|
|
||||||
service.interceptors.request.use(
|
|
||||||
config => {
|
|
||||||
// 不传递默认开启loading
|
|
||||||
if (!config.hideloading) {
|
|
||||||
// loading
|
|
||||||
Toast.loading({
|
|
||||||
forbidClick: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (store.getters.token) {
|
|
||||||
config.headers['X-Token'] = ''
|
|
||||||
}
|
|
||||||
return config
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
// do something with request error
|
|
||||||
console.log(error) // for debug
|
|
||||||
return Promise.reject(error)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
// respone拦截器
|
|
||||||
service.interceptors.response.use(
|
|
||||||
response => {
|
|
||||||
Toast.clear()
|
|
||||||
const res = response.data
|
|
||||||
if (res.status && res.status !== 200) {
|
|
||||||
// 登录超时,重新登录
|
|
||||||
if (res.status === 401) {
|
|
||||||
store.dispatch('FedLogOut').then(() => {
|
|
||||||
location.reload()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return Promise.reject(res || 'error')
|
|
||||||
} else {
|
|
||||||
return Promise.resolve(res)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
Toast.clear()
|
|
||||||
console.log('err' + error) // for debug
|
|
||||||
return Promise.reject(error)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
export default service
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 接口管理
|
|
||||||
|
|
||||||
在`src/api` 文件夹下统一管理接口
|
|
||||||
|
|
||||||
- 你可以建立多个模块对接接口, 比如 `home.js` 里是首页的接口这里讲解 `user.js`
|
|
||||||
- `url` 接口地址,请求的时候会拼接上 `config` 下的 `baseApi`
|
|
||||||
- `method` 请求方法
|
|
||||||
- `data` 请求参数 `qs.stringify(params)` 是对数据系列化操作
|
|
||||||
- `hideloading` 默认 `false`,设置为 `true` 后,不显示 loading ui 交互中有些接口不需要让用户感知
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
import qs from 'qs'
|
|
||||||
// axios
|
|
||||||
import request from '@/utils/request'
|
|
||||||
//user api
|
|
||||||
|
|
||||||
// 用户信息
|
|
||||||
export function getUserInfo(params) {
|
|
||||||
return request({
|
|
||||||
url: '/user/userinfo',
|
|
||||||
method: 'post',
|
|
||||||
data: qs.stringify(params),
|
|
||||||
hideloading: true // 隐藏 loading 组件
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 如何调用
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// 请求接口
|
|
||||||
import {getUserInfo} from '@/api/user.js'
|
|
||||||
|
|
||||||
const params = {user: 'sunnie'}
|
|
||||||
getUserInfo(params)
|
|
||||||
.then(() => {})
|
|
||||||
.catch(() => {})
|
|
||||||
```
|
|
||||||
|
|
||||||
[▲ 回顶部](#top)
|
|
||||||
|
|
||||||
### <span id="base">✅ Webpack 4 vue.config.js 基础配置 </span>
|
|
||||||
|
|
||||||
如果你的 `Vue Router` 模式是 hash
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
publicPath: './',
|
|
||||||
```
|
|
||||||
|
|
||||||
如果你的 `Vue Router` 模式是 history 这里的 publicPath 和你的 `Vue Router` `base` **保持一直**
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
publicPath: '/app/',
|
|
||||||
```
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
publicPath: './', // 署应用包时的基本 URL。 vue-router hash 模式使用
|
|
||||||
// publicPath: '/app/', // 署应用包时的基本 URL。 vue-router history模式使用
|
|
||||||
outputDir: 'dist', // 生产环境构建文件的目录
|
|
||||||
assetsDir: 'static', // outputDir的静态资源(js、css、img、fonts)目录
|
|
||||||
lintOnSave: process.env.NODE_ENV !== IS_PROD,
|
|
||||||
productionSourceMap: false, // 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
|
|
||||||
devServer: {
|
|
||||||
port: 9020, // 端口号
|
|
||||||
open: false, // 启动后打开浏览器
|
|
||||||
overlay: {
|
|
||||||
// 当出现编译器错误或警告时,在浏览器中显示全屏覆盖层
|
|
||||||
warnings: false,
|
|
||||||
errors: true
|
|
||||||
}
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
[▲ 回顶部](#top)
|
|
||||||
|
|
||||||
### <span id="alias">✅ 配置 alias 别名 </span>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const path = require('path')
|
|
||||||
const resolve = dir => path.join(__dirname, dir)
|
|
||||||
const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
chainWebpack: config => {
|
|
||||||
// 添加别名
|
|
||||||
config.resolve.alias
|
|
||||||
.set('@', resolve('src'))
|
|
||||||
.set('assets', resolve('src/assets'))
|
|
||||||
.set('api', resolve('src/api'))
|
|
||||||
.set('views', resolve('src/views'))
|
|
||||||
.set('components', resolve('src/components'))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
[▲ 回顶部](#top)
|
|
||||||
|
|
||||||
### <span id="proxy">✅ 配置 proxy 跨域 </span>
|
|
||||||
|
|
||||||
如果你的项目需要跨域设置,你需要打来 `vue.config.js` `proxy` 注释 并且配置相应参数
|
|
||||||
|
|
||||||
<u>**!!!注意:你还需要将 `src/config/env.development.js` 里的 `baseApi` 设置成 '/'**</u>
|
|
||||||
```javascript
|
|
||||||
module.exports = {
|
|
||||||
devServer: {
|
|
||||||
// ....
|
|
||||||
proxy: {
|
|
||||||
//配置跨域
|
|
||||||
'/api': {
|
|
||||||
target: 'https://test.xxx.com', // 接口的域名
|
|
||||||
// ws: true, // 是否启用websockets
|
|
||||||
changOrigin: true, // 开启代理,在本地创建一个虚拟服务端
|
|
||||||
pathRewrite: {
|
|
||||||
'^/api': '/'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
使用 例如: `src/api/home.js`
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
export function getUserInfo(params) {
|
|
||||||
return request({
|
|
||||||
url: '/api/userinfo',
|
|
||||||
method: 'post',
|
|
||||||
data: qs.stringify(params)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
[▲ 回顶部](#top)
|
|
||||||
|
|
||||||
|
|
||||||
### <span id="bundle">✅ 配置 打包分析 </span>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
chainWebpack: config => {
|
|
||||||
// 打包分析
|
|
||||||
if (IS_PROD) {
|
|
||||||
config.plugin('webpack-report').use(BundleAnalyzerPlugin, [
|
|
||||||
{
|
|
||||||
analyzerMode: 'static'
|
|
||||||
}
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
[▲ 回顶部](#top)
|
|
||||||
|
|
||||||
### <span id="externals">✅ 配置 externals 引入 cdn 资源 </span>
|
|
||||||
|
|
||||||
这个版本 CDN 不再引入,我测试了一下使用引入 CDN 和不使用,不使用会比使用时间少。网上不少文章测试 CDN 速度块,这个开发者可
|
|
||||||
以实际测试一下。
|
|
||||||
|
|
||||||
另外项目中使用的是公共 CDN 不稳定,域名解析也是需要时间的(如果你要使用请尽量使用同一个域名)
|
|
||||||
|
|
||||||
因为页面每次遇到`<script>`标签都会停下来解析执行,所以应该尽可能减少`<script>`标签的数量 `HTTP`请求存在一定的开销,100K
|
|
||||||
的文件比 5 个 20K 的文件下载的更快,所以较少脚本数量也是很有必要的
|
|
||||||
|
|
||||||
暂时还没有研究放到自己的 cdn 服务器上。
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const defaultSettings = require('./src/config/index.js')
|
|
||||||
const name = defaultSettings.title || 'vue mobile template'
|
|
||||||
const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)
|
|
||||||
|
|
||||||
// externals
|
|
||||||
const externals = {
|
|
||||||
vue: 'Vue',
|
|
||||||
'vue-router': 'VueRouter',
|
|
||||||
vuex: 'Vuex',
|
|
||||||
vant: 'vant',
|
|
||||||
axios: 'axios'
|
|
||||||
}
|
|
||||||
// CDN外链,会插入到index.html中
|
|
||||||
const cdn = {
|
|
||||||
// 开发环境
|
|
||||||
dev: {
|
|
||||||
css: [],
|
|
||||||
js: []
|
|
||||||
},
|
|
||||||
// 生产环境
|
|
||||||
build: {
|
|
||||||
css: ['https://cdn.jsdelivr.net/npm/vant@2.4.7/lib/index.css'],
|
|
||||||
js: [
|
|
||||||
'https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js',
|
|
||||||
'https://cdn.jsdelivr.net/npm/vue-router@3.1.5/dist/vue-router.min.js',
|
|
||||||
'https://cdn.jsdelivr.net/npm/axios@0.19.2/dist/axios.min.js',
|
|
||||||
'https://cdn.jsdelivr.net/npm/vuex@3.1.2/dist/vuex.min.js',
|
|
||||||
'https://cdn.jsdelivr.net/npm/vant@2.4.7/lib/index.min.js'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
module.exports = {
|
|
||||||
configureWebpack: config => {
|
|
||||||
config.name = name
|
|
||||||
// 为生产环境修改配置...
|
|
||||||
if (IS_PROD) {
|
|
||||||
// externals
|
|
||||||
config.externals = externals
|
|
||||||
}
|
|
||||||
},
|
|
||||||
chainWebpack: config => {
|
|
||||||
/**
|
|
||||||
* 添加CDN参数到htmlWebpackPlugin配置中
|
|
||||||
*/
|
|
||||||
config.plugin('html').tap(args => {
|
|
||||||
if (IS_PROD) {
|
|
||||||
args[0].cdn = cdn.build
|
|
||||||
} else {
|
|
||||||
args[0].cdn = cdn.dev
|
|
||||||
}
|
|
||||||
return args
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
在 public/index.html 中添加
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
<!-- 使用CDN的CSS文件 -->
|
|
||||||
<% for (var i in
|
|
||||||
htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.css) { %>
|
|
||||||
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style" />
|
|
||||||
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet" />
|
|
||||||
<% } %>
|
|
||||||
<!-- 使用CDN加速的JS文件,配置在vue.config.js下 -->
|
|
||||||
<% for (var i in
|
|
||||||
htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
|
|
||||||
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
|
|
||||||
<% } %>
|
|
||||||
```
|
|
||||||
|
|
||||||
[▲ 回顶部](#top)
|
|
||||||
|
|
||||||
### <span id="console">✅ 去掉 console.log </span>
|
|
||||||
|
|
||||||
保留了测试环境和本地环境的 `console.log`
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm i -D babel-plugin-transform-remove-console
|
|
||||||
```
|
|
||||||
|
|
||||||
在 babel.config.js 中配置
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// 获取 VUE_APP_ENV 非 NODE_ENV,测试环境依然 console
|
|
||||||
const IS_PROD = ['production', 'prod'].includes(process.env.VUE_APP_ENV)
|
|
||||||
const plugins = [
|
|
||||||
[
|
|
||||||
'import',
|
|
||||||
{
|
|
||||||
libraryName: 'vant',
|
|
||||||
libraryDirectory: 'es',
|
|
||||||
style: true
|
|
||||||
},
|
|
||||||
'vant'
|
|
||||||
]
|
|
||||||
]
|
|
||||||
// 去除 console.log
|
|
||||||
if (IS_PROD) {
|
|
||||||
plugins.push('transform-remove-console')
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
presets: [['@vue/cli-plugin-babel/preset', {useBuiltIns: 'entry'}]],
|
|
||||||
plugins
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
[▲ 回顶部](#top)
|
|
||||||
|
|
||||||
### <span id="chunks">✅ splitChunks 单独打包第三方模块</span>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
module.exports = {
|
|
||||||
chainWebpack: config => {
|
|
||||||
config.when(IS_PROD, config => {
|
|
||||||
config
|
|
||||||
.plugin('ScriptExtHtmlWebpackPlugin')
|
|
||||||
.after('html')
|
|
||||||
.use('script-ext-html-webpack-plugin', [
|
|
||||||
{
|
|
||||||
// 将 runtime 作为内联引入不单独存在
|
|
||||||
inline: /runtime\..*\.js$/
|
|
||||||
}
|
|
||||||
])
|
|
||||||
.end()
|
|
||||||
config.optimization.splitChunks({
|
|
||||||
chunks: 'all',
|
|
||||||
cacheGroups: {
|
|
||||||
// cacheGroups 下可以可以配置多个组,每个组根据test设置条件,符合test条件的模块
|
|
||||||
commons: {
|
|
||||||
name: 'chunk-commons',
|
|
||||||
test: resolve('src/components'),
|
|
||||||
minChunks: 3, // 被至少用三次以上打包分离
|
|
||||||
priority: 5, // 优先级
|
|
||||||
reuseExistingChunk: true // 表示是否使用已有的 chunk,如果为 true 则表示如果当前的 chunk 包含的模块已经被抽取出去了,那么将不会重新生成新的。
|
|
||||||
},
|
|
||||||
node_vendors: {
|
|
||||||
name: 'chunk-libs',
|
|
||||||
chunks: 'initial', // 只打包初始时依赖的第三方
|
|
||||||
test: /[\\/]node_modules[\\/]/,
|
|
||||||
priority: 10
|
|
||||||
},
|
|
||||||
vantUI: {
|
|
||||||
name: 'chunk-vantUI', // 单独将 vantUI 拆包
|
|
||||||
priority: 20, // 数字大权重到,满足多个 cacheGroups 的条件时候分到权重高的
|
|
||||||
test: /[\\/]node_modules[\\/]_?vant(.*)/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
config.optimization.runtimeChunk('single')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
[▲ 回顶部](#top)
|
|
||||||
|
|
||||||
### <span id="ie">✅ 添加 IE 兼容 </span>
|
|
||||||
|
|
||||||
之前的方式 会报 `@babel/polyfill` is deprecated. Please, use required parts of `core-js` and
|
|
||||||
`regenerator-runtime/runtime` separately
|
|
||||||
|
|
||||||
`@babel/polyfill` 废弃,使用 `core-js` 和 `regenerator-runtime`
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm i --save core-js regenerator-runtime
|
|
||||||
```
|
|
||||||
|
|
||||||
在 `main.js` 中添加
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// 兼容 IE
|
|
||||||
// https://github.com/zloirock/core-js/blob/master/docs/2019-03-19-core-js-3-babel-and-a-look-into-the-future.md#babelpolyfill
|
|
||||||
import 'core-js/stable'
|
|
||||||
import 'regenerator-runtime/runtime'
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
配置 `babel.config.js`
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const plugins = []
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
presets: [['@vue/cli-plugin-babel/preset', {useBuiltIns: 'usage', corejs: 3}]],
|
|
||||||
plugins
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
[▲ 回顶部](#top)
|
|
||||||
|
|
||||||
### <span id="pettier">✅ Eslint + Pettier 统一开发规范 </span>
|
|
||||||
|
|
||||||
VScode 安装 `eslint` `prettier` `vetur` 插件
|
|
||||||
|
|
||||||
在文件 `.prettierrc` 里写 属于你的 pettier 规则
|
|
||||||
|
|
||||||
```bash
|
|
||||||
{
|
|
||||||
"printWidth": 120,
|
|
||||||
"tabWidth": 2,
|
|
||||||
"singleQuote": true,
|
|
||||||
"trailingComma": "none",
|
|
||||||
"semi": false,
|
|
||||||
"wrap_line_length": 120,
|
|
||||||
"wrap_attributes": "auto",
|
|
||||||
"proseWrap": "always",
|
|
||||||
"arrowParens": "avoid",
|
|
||||||
"bracketSpacing": false,
|
|
||||||
"jsxBracketSameLine": true,
|
|
||||||
"useTabs": false,
|
|
||||||
"overrides": [{
|
|
||||||
"files": ".prettierrc",
|
|
||||||
"options": {
|
|
||||||
"parser": "json"
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Vscode setting.json 设置
|
|
||||||
|
|
||||||
```bash
|
|
||||||
"[vue]": {
|
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
||||||
},
|
|
||||||
"[javascript]": {
|
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
||||||
},
|
|
||||||
// 保存时用eslint格式化
|
|
||||||
"editor.codeActionsOnSave": {
|
|
||||||
"source.fixAll.eslint": true
|
|
||||||
},
|
|
||||||
// 两者会在格式化js时冲突,所以需要关闭默认js格式化程序
|
|
||||||
"javascript.format.enable": false,
|
|
||||||
"typescript.format.enable": false,
|
|
||||||
"vetur.format.defaultFormatter.html": "none",
|
|
||||||
// js/ts程序用eslint,防止vetur中的prettier与eslint格式化冲突
|
|
||||||
"vetur.format.defaultFormatter.js": "none",
|
|
||||||
"vetur.format.defaultFormatter.ts": "none",
|
|
||||||
```
|
|
||||||
[▲ 回顶部](#top)
|
|
||||||
|
|
||||||
# 鸣谢
|
|
||||||
|
|
||||||
[vue-cli4-config](https://github.com/staven630/vue-cli4-config)
|
[vue-cli4-config](https://github.com/staven630/vue-cli4-config)
|
||||||
[vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)
|
[vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)
|
||||||
|
|
||||||
# 关于我
|
#### 贡献代码
|
||||||
|
|
||||||
获取更多技术相关文章,关注公众号”前端女塾“。
|
使用过程中发现任何问题都可以提[Issue](https://github.com/sunniejs/vue-h5-template/issues) 给我,也非常欢迎 PR
|
||||||
|
或 [Pull Request ](https://github.com/sunniejs/vue-h5-template/pulls)
|
||||||
|
|
||||||
回复加群,即可加入”前端仙女群“
|
#### 如何找到失散已久的组织?
|
||||||
|
|
||||||
<p>
|
扫描下方二维码:point_down::point_down:关注“前端女塾”
|
||||||
<img src="./static/gognzhonghao.jpg" width="256" style="display:inline;">
|
|
||||||
</p>
|
|
||||||
|
|
||||||
扫描添加下方的微信并备注 Sol 加交流群,交流学习,及时获取代码最新动态。
|

|
||||||
|
关注公众号:回复“加群”即可加 前端仙女群
|
||||||
<p>
|
|
||||||
<img src="./static/me.png" width="256" style="display:inline;">
|
|
||||||
</p>
|
|
||||||
|
|
||||||
如果对你有帮助送我一颗小星星(づ ̄3 ̄)づ╭❤~
|
|
||||||
|
12
_coverpage.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Vue H5 Template
|
||||||
|
|
||||||
|
## 移动端解决方案
|
||||||
|
|
||||||
|
基于 vue-cli4.0+webpack 4+vant ui + sass+ rem 适配方案+axios 封装,构建手机端模板脚手架
|
||||||
|
|
||||||
|
[<i class="iconfont icon-github"></i> GitHub](https://github.com/sunniejs/vue-h5-template)
|
||||||
|
[马上开始 <i class="iconfont icon-down"></i>](/README)
|
||||||
|
|
||||||
|
<!-- background image -->
|
||||||
|
|
||||||
|

|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
BIN
_images/logo.png
Normal file
After Width: | Height: | Size: 8.0 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
BIN
_images/qrcode.jpg
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
_images/qrcode.png
Normal file
After Width: | Height: | Size: 6.6 KiB |
25
_media/custom.css
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
section.cover.has-mask .mask {
|
||||||
|
background-image: linear-gradient(hsla(0, 0%, 100%, 0.25),hsla(0, 0%, 100%, 0.25));
|
||||||
|
background-color: transparent;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
section.cover h1 .anchor span{
|
||||||
|
font-family: 'Lobster', cursive;
|
||||||
|
color: var(--theme-color);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
section.cover h2 .anchor span{
|
||||||
|
font-family: 'Lobster', cursive;
|
||||||
|
font-size:24px;
|
||||||
|
color: var(--theme-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
section.cover .cover-main>p:last-child a .iconfont {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar>h1 a {
|
||||||
|
font-family: 'Lobster', cursive;
|
||||||
|
}
|
0
_navbar.md
Normal file
18
_sidebar.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
- [介绍](/README.md)
|
||||||
|
- [快速上手](/zh-cn/quickstart.md)
|
||||||
|
- [配置多环境变量](/zh-cn/env.md)
|
||||||
|
- [rem 适配方案](/zh-cn/rem.md)
|
||||||
|
- [VantUI 组件按需加载](/zh-cn/vant.md)
|
||||||
|
- [Sass 全局样式](/zh-cn/sass.md)
|
||||||
|
- [Vuex 状态管理](/zh-cn/vuex.md)
|
||||||
|
- [Vue-router](/zh-cn/router.md)
|
||||||
|
- [Axios 封装及接口管理](/zh-cn/axios.md)
|
||||||
|
- [Webpack 4 vue.config.js 基础配置](/zh-cn/base.md)
|
||||||
|
- [配置 alias 别名](/zh-cn/alias.md)
|
||||||
|
- [配置 proxy 跨域](/zh-cn/proxy.md)
|
||||||
|
- [配置 打包分析](/zh-cn/bundle.md)
|
||||||
|
- [配置 externals 引入 cdn 资源 ](/zh-cn/externals.md)
|
||||||
|
- [去掉 console.log ](/zh-cn/console.md)
|
||||||
|
- [splitChunks 单独打包第三方模块](/zh-cn/chunks.md)
|
||||||
|
- [添加 IE 兼容 ](/zh-cn/ie.md)
|
||||||
|
- [Eslint+Pettier 统一开发规范 ](/zh-cn/pettier.md)
|
@ -1,22 +0,0 @@
|
|||||||
// 获取 VUE_APP_ENV 非 NODE_ENV,测试环境依然 console
|
|
||||||
const IS_PROD = ['production', 'prod'].includes(process.env.VUE_APP_ENV)
|
|
||||||
const plugins = [
|
|
||||||
[
|
|
||||||
'import',
|
|
||||||
{
|
|
||||||
libraryName: 'vant',
|
|
||||||
libraryDirectory: 'es',
|
|
||||||
style: true
|
|
||||||
},
|
|
||||||
'vant'
|
|
||||||
]
|
|
||||||
]
|
|
||||||
// 去除 console.log
|
|
||||||
if (IS_PROD) {
|
|
||||||
plugins.push('transform-remove-console')
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
presets: [['@vue/cli-plugin-babel/preset', {useBuiltIns: 'usage', corejs: 3}]],
|
|
||||||
plugins
|
|
||||||
}
|
|
91
index.html
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Vue H5 Template</title>
|
||||||
|
<meta name="keywords" content="Vue H5 Template,Vue,移动端开发, 脚手架, 框架" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||||
|
<meta name="description" content="Vue搭建移动端开发,基于vue-cli4.0+webpack 4+vant ui + sass+ rem适配方案+axios封装,构建手机端模板脚手架。" />
|
||||||
|
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" />
|
||||||
|
|
||||||
|
<!-- Favicons -->
|
||||||
|
<link rel="shortcut icon" href="_images/favicon.ico" />
|
||||||
|
<link rel="icon" type="image/x-icon" sizes="16x16 32x32" href="_images/favicon.ico" />
|
||||||
|
<meta name="msapplication-TileColor" content="#FFFFFF" />
|
||||||
|
<link rel="stylesheet" href="//unpkg.com/docsify/lib/themes/vue.css" />
|
||||||
|
<link rel="stylesheet" href="./_media/custom.css" />
|
||||||
|
<link rel="stylesheet" href="//at.alicdn.com/t/font_539333_ah8wb2hv6yknvcxr.css" />
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" />
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
pre:after {
|
||||||
|
padding: 1em;
|
||||||
|
right: 0.5em;
|
||||||
|
}
|
||||||
|
nav.app-nav li ul {
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
.sidebar ul li a {
|
||||||
|
white-space: pre-line;
|
||||||
|
padding-right: 0.5em;
|
||||||
|
}
|
||||||
|
.markdown-section {
|
||||||
|
max-width: 90%;
|
||||||
|
}
|
||||||
|
.markdown-section code {
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
.markdown-section pre > code {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
.markdown-section ol,
|
||||||
|
.markdown-section ul {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<a href="https://github.com/sunniejs/vue-h5-template" class="github-corner" aria-label="View source on Github">
|
||||||
|
<svg viewBox="0 0 250 250" aria-hidden="true">
|
||||||
|
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
|
||||||
|
<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
|
||||||
|
<path
|
||||||
|
d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
|
||||||
|
fill="currentColor"
|
||||||
|
class="octo-body"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script>
|
||||||
|
window.$docsify = {
|
||||||
|
auto2top: true,
|
||||||
|
loadSidebar: true,
|
||||||
|
coverpage: true,
|
||||||
|
loadNavbar: true,
|
||||||
|
themeColor: '#25798A',
|
||||||
|
name: 'Vue H5 Template',
|
||||||
|
repo: '',
|
||||||
|
// search: {
|
||||||
|
// paths: 'auto',
|
||||||
|
// placeholder: '搜索 🔍',
|
||||||
|
// noData: '只有小仙女! 😞'
|
||||||
|
// }
|
||||||
|
pagination: {
|
||||||
|
previousText: '上一章节',
|
||||||
|
nextText: '下一章节',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script src="//unpkg.com/docsify/lib/docsify.min.js"></script>
|
||||||
|
<script src="//unpkg.com/docsify/lib/plugins/search.min.js"></script>
|
||||||
|
<script src="//unpkg.com/docsify-pagination/dist/docsify-pagination.min.js"></script>
|
||||||
|
<script src="//unpkg.com/prismjs/components/prism-bash.min.js"></script>
|
||||||
|
<script src="//unpkg.com/prismjs/components/prism-json.min.js"></script>
|
||||||
|
<script src="//unpkg.com/prismjs/components/prism-javascript.min.js"></script>
|
||||||
|
<script src="//unpkg.com/prismjs/components/prism-diff.min.js"></script>
|
||||||
|
<script src="//unpkg.com/docsify/lib/plugins/ga.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
13266
package-lock.json
generated
43
package.json
@ -1,43 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "vue-h5-template",
|
|
||||||
"version": "2.0.0",
|
|
||||||
"description": "A vue h5 template with Vant UI",
|
|
||||||
"author": "Sunnie <sunniejs@163.com>",
|
|
||||||
"private": true,
|
|
||||||
"scripts": {
|
|
||||||
"serve": "vue-cli-service serve --open",
|
|
||||||
"build": "vue-cli-service build",
|
|
||||||
"stage": "vue-cli-service build --mode staging",
|
|
||||||
"lint": "vue-cli-service lint"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"axios": "^0.19.2",
|
|
||||||
"core-js": "^3.6.4",
|
|
||||||
"lib-flexible": "^0.3.2",
|
|
||||||
"lodash": "^4.17.15",
|
|
||||||
"regenerator-runtime": "^0.13.5",
|
|
||||||
"vant": "^2.4.7",
|
|
||||||
"vue": "^2.6.11",
|
|
||||||
"vue-router": "^3.1.5",
|
|
||||||
"vuex": "^3.1.2"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@vue/cli-plugin-babel": "~4.3.0",
|
|
||||||
"@vue/cli-plugin-eslint": "~4.3.0",
|
|
||||||
"@vue/cli-service": "~4.3.0",
|
|
||||||
"@vue/eslint-config-prettier": "^6.0.0",
|
|
||||||
"babel-eslint": "^10.0.3",
|
|
||||||
"babel-plugin-import": "^1.13.0",
|
|
||||||
"babel-plugin-transform-remove-console": "^6.9.4",
|
|
||||||
"eslint": "^6.7.2",
|
|
||||||
"eslint-plugin-prettier": "^3.1.1",
|
|
||||||
"eslint-plugin-vue": "^6.2.2",
|
|
||||||
"node-sass": "^4.13.1",
|
|
||||||
"postcss-pxtorem": "^4.0.1",
|
|
||||||
"prettier": "^1.19.1",
|
|
||||||
"sass-loader": "^8.0.2",
|
|
||||||
"script-ext-html-webpack-plugin": "^2.1.4",
|
|
||||||
"vue-template-compiler": "^2.6.11",
|
|
||||||
"webpack-bundle-analyzer": "^3.7.0"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
|
||||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
|
||||||
<!-- <% for (var i in
|
|
||||||
htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.css) { %>
|
|
||||||
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style" />
|
|
||||||
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet" />
|
|
||||||
<% } %> -->
|
|
||||||
<title><%= webpackConfig.name %></title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<noscript>
|
|
||||||
<strong>We're sorry but <%= webpackConfig.name %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
|
||||||
</noscript>
|
|
||||||
<div id="app"></div>
|
|
||||||
<!-- 使用CDN加速的JS文件,配置在vue.config.js下 -->
|
|
||||||
<!-- <% for (var i in
|
|
||||||
htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
|
|
||||||
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
|
|
||||||
<% } %> -->
|
|
||||||
<!-- built files will be auto injected -->
|
|
||||||
</body>
|
|
||||||
</html>
|
|
21
src/App.vue
@ -1,21 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="app" id="app">
|
|
||||||
<keep-alive>
|
|
||||||
<router-view v-if="$route.meta.keepAlive"></router-view>
|
|
||||||
</keep-alive>
|
|
||||||
<router-view v-if="!$route.meta.keepAlive"></router-view>
|
|
||||||
<!-- tabbar -->
|
|
||||||
<TabBar></TabBar>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import TabBar from '@/components/TabBar'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'App',
|
|
||||||
components: {
|
|
||||||
TabBar
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style lang="scss"></style>
|
|
@ -1,6 +0,0 @@
|
|||||||
// import qs from 'qs'
|
|
||||||
// axios
|
|
||||||
// import request from '@/utils/request'
|
|
||||||
//home api
|
|
||||||
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
|||||||
import qs from 'qs'
|
|
||||||
// axios
|
|
||||||
import request from '@/utils/request'
|
|
||||||
// user api
|
|
||||||
|
|
||||||
// 登录
|
|
||||||
export function login(params) {
|
|
||||||
return request({
|
|
||||||
url: '/user/login',
|
|
||||||
method: 'post',
|
|
||||||
data: qs.stringify(params)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// 用户信息 post 方法
|
|
||||||
export function getUserInfo(params) {
|
|
||||||
return request({
|
|
||||||
url: '/user/userinfo',
|
|
||||||
method: 'post',
|
|
||||||
data: qs.stringify(params),
|
|
||||||
hideloading: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 用户名称 get 方法
|
|
||||||
export function getUserName(params) {
|
|
||||||
return request({
|
|
||||||
url: '/user/name?' + qs.stringify(params),
|
|
||||||
method: 'get',
|
|
||||||
hideloading: true
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
@import './variables.scss';
|
|
||||||
@import './mixin.scss';
|
|
||||||
|
|
||||||
html,
|
|
||||||
body
|
|
||||||
.app {
|
|
||||||
color: #333333;
|
|
||||||
font-family: Arial, Helvetica, 'STHeiti STXihei', 'Microsoft YaHei', Tohoma, sans-serif;
|
|
||||||
background-color: $background-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-container{
|
|
||||||
padding-bottom:50px;
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
// mixin
|
|
||||||
// 清除浮动
|
|
||||||
@mixin clearfix {
|
|
||||||
&:after {
|
|
||||||
content: "";
|
|
||||||
display: table;
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 多行隐藏
|
|
||||||
@mixin textoverflow($clamp:1) {
|
|
||||||
display: block;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
-o-text-overflow: ellipsis;
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-line-clamp: $clamp;
|
|
||||||
/*! autoprefixer: ignore next */
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
//flex box
|
|
||||||
@mixin flexbox($jc:space-between, $ai:center, $fd:row, $fw:nowrap) {
|
|
||||||
display: flex;
|
|
||||||
display: -webkit-flex;
|
|
||||||
flex: 1;
|
|
||||||
justify-content: $jc;
|
|
||||||
-webkit-justify-content: $jc;
|
|
||||||
align-items: $ai;
|
|
||||||
-webkit-align-items: $ai;
|
|
||||||
flex-direction: $fd;
|
|
||||||
-webkit-flex-direction: $fd;
|
|
||||||
flex-wrap: $fw;
|
|
||||||
-webkit-flex-wrap: $fw;
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
|
|
||||||
// variables
|
|
||||||
$background-color: #f8f8f8;
|
|
Before Width: | Height: | Size: 6.7 KiB |
@ -1,45 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<van-tabbar fixed route>
|
|
||||||
<van-tabbar-item to="/" icon="home-o">
|
|
||||||
首页
|
|
||||||
</van-tabbar-item>
|
|
||||||
<van-tabbar-item to="/about" icon="user-o">
|
|
||||||
关于我
|
|
||||||
</van-tabbar-item>
|
|
||||||
</van-tabbar>
|
|
||||||
<!-- <van-tabbar fixed v-model="active" @change="onChange">
|
|
||||||
<van-tabbar-item to="/home" icon="home-o">首页</van-tabbar-item>
|
|
||||||
<van-tabbar-item to="/about" icon="user-o">关于我</van-tabbar-item>
|
|
||||||
</van-tabbar> -->
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'TabBar',
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
active: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
|
||||||
<style scoped>
|
|
||||||
h3 {
|
|
||||||
margin: 40px 0 0;
|
|
||||||
}
|
|
||||||
ul {
|
|
||||||
list-style-type: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
li {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0 10px;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: #42b983;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,9 +0,0 @@
|
|||||||
// 本地环境配置
|
|
||||||
module.exports = {
|
|
||||||
title: 'vue-h5-template',
|
|
||||||
baseUrl: 'http://localhost:9018', // 项目地址
|
|
||||||
baseApi: 'https://test.xxx.com/api', // 本地api请求地址,注意:如果你使用了代理,请设置成'/'
|
|
||||||
APPID: 'xxx',
|
|
||||||
APPSECRET: 'xxx',
|
|
||||||
$cdn: 'https://imgs.solui.cn'
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
// 正式
|
|
||||||
module.exports = {
|
|
||||||
title: 'vue-h5-template',
|
|
||||||
baseUrl: 'https://www.xxx.com/', // 正式项目地址
|
|
||||||
baseApi: 'https://www.xxx.com/api', // 正式api请求地址
|
|
||||||
APPID: 'xxx',
|
|
||||||
APPSECRET: 'xxx',
|
|
||||||
$cdn:'https://imgs.solui.cn'
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
title: 'vue-h5-template',
|
|
||||||
baseUrl: 'https://test.xxx.com', // 测试项目地址
|
|
||||||
baseApi: 'https://test.xxx.com/api', // 测试api请求地址
|
|
||||||
APPID: 'xxx',
|
|
||||||
APPSECRET: 'xxx',
|
|
||||||
$cdn:'https://imgs.solui.cn'
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
// 根据环境引入不同配置 process.env.NODE_ENV
|
|
||||||
const config = require('./env.' + process.env.VUE_APP_ENV)
|
|
||||||
module.exports = config
|
|
@ -1,37 +0,0 @@
|
|||||||
/**
|
|
||||||
*格式化时间
|
|
||||||
*yyyy-MM-dd hh:mm:ss
|
|
||||||
*/
|
|
||||||
export function formatDate(time, fmt) {
|
|
||||||
if (time === undefined || '') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const date = new Date(time)
|
|
||||||
if (/(y+)/.test(fmt)) {
|
|
||||||
fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
|
|
||||||
}
|
|
||||||
const o = {
|
|
||||||
'M+': date.getMonth() + 1,
|
|
||||||
'd+': date.getDate(),
|
|
||||||
'h+': date.getHours(),
|
|
||||||
'm+': date.getMinutes(),
|
|
||||||
's+': date.getSeconds()
|
|
||||||
}
|
|
||||||
for (const k in o) {
|
|
||||||
if (new RegExp(`(${k})`).test(fmt)) {
|
|
||||||
const str = o[k] + ''
|
|
||||||
fmt = fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? str : padLeftZero(str))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fmt
|
|
||||||
}
|
|
||||||
|
|
||||||
function padLeftZero(str) {
|
|
||||||
return ('00' + str).substr(str.length)
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* 隐藏用户手机号中间四位
|
|
||||||
*/
|
|
||||||
export function hidePhone(phone) {
|
|
||||||
return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
import Vue from 'vue'
|
|
||||||
import * as filter from './filter'
|
|
||||||
|
|
||||||
Object.keys(filter).forEach(k => Vue.filter(k, filter[k]))
|
|
||||||
|
|
||||||
Vue.prototype.$formatDate = Vue.filter('formatDate')
|
|
||||||
Vue.prototype.$hidePhone = Vue.filter('hidePhone')
|
|
32
src/main.js
@ -1,32 +0,0 @@
|
|||||||
// 兼容 IE
|
|
||||||
// https://github.com/zloirock/core-js/blob/master/docs/2019-03-19-core-js-3-babel-and-a-look-into-the-future.md#babelpolyfill
|
|
||||||
import 'core-js/stable'
|
|
||||||
import 'regenerator-runtime/runtime'
|
|
||||||
|
|
||||||
import Vue from 'vue'
|
|
||||||
import App from './App.vue'
|
|
||||||
import router from './router'
|
|
||||||
import store from './store'
|
|
||||||
|
|
||||||
// 引入全局样式
|
|
||||||
import '@/assets/css/index.scss'
|
|
||||||
// 设置 js中可以访问 $cdn
|
|
||||||
import { $cdn } from '@/config'
|
|
||||||
Vue.prototype.$cdn = $cdn
|
|
||||||
|
|
||||||
// 全局引入按需引入UI库 vant
|
|
||||||
import '@/plugins/vant'
|
|
||||||
|
|
||||||
// 移动端适配
|
|
||||||
import 'lib-flexible/flexible.js'
|
|
||||||
|
|
||||||
// filters
|
|
||||||
import './filters'
|
|
||||||
Vue.config.productionTip = false
|
|
||||||
|
|
||||||
new Vue({
|
|
||||||
el: '#app',
|
|
||||||
router,
|
|
||||||
store,
|
|
||||||
render: h => h(App)
|
|
||||||
})
|
|
@ -1,7 +0,0 @@
|
|||||||
// 按需全局引入 vant组件
|
|
||||||
import Vue from 'vue'
|
|
||||||
import { Button, List, Cell, Tabbar, TabbarItem } from 'vant'
|
|
||||||
Vue.use(Button)
|
|
||||||
Vue.use(Cell)
|
|
||||||
Vue.use(List)
|
|
||||||
Vue.use(Tabbar).use(TabbarItem)
|
|
@ -1,34 +0,0 @@
|
|||||||
import Vue from 'vue'
|
|
||||||
import Router from 'vue-router'
|
|
||||||
|
|
||||||
Vue.use(Router)
|
|
||||||
export const router = [
|
|
||||||
{
|
|
||||||
path: '/',
|
|
||||||
name: 'index',
|
|
||||||
component: () => import('@/views/home/index'), // 路由懒加载
|
|
||||||
meta: {
|
|
||||||
title: '首页', // 页面标题
|
|
||||||
keepAlive: false // keep-alive 标识
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/about',
|
|
||||||
name: 'about',
|
|
||||||
component: () => import('@/views/home/about'),
|
|
||||||
meta: {
|
|
||||||
title: '关于我',
|
|
||||||
keepAlive: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const createRouter = () =>
|
|
||||||
new Router({
|
|
||||||
// mode: 'history', // 如果你是 history模式 需要配置vue.config.js publicPath
|
|
||||||
// base: '/app/',
|
|
||||||
scrollBehavior: () => ({ y: 0 }),
|
|
||||||
routes: router
|
|
||||||
})
|
|
||||||
|
|
||||||
export default createRouter()
|
|
@ -1,4 +0,0 @@
|
|||||||
const getters = {
|
|
||||||
userName: state => state.app.userName
|
|
||||||
}
|
|
||||||
export default getters
|
|
@ -1,15 +0,0 @@
|
|||||||
import Vue from 'vue'
|
|
||||||
import Vuex from 'vuex'
|
|
||||||
import getters from './getters'
|
|
||||||
import app from './modules/app'
|
|
||||||
|
|
||||||
Vue.use(Vuex)
|
|
||||||
|
|
||||||
const store = new Vuex.Store({
|
|
||||||
modules: {
|
|
||||||
app
|
|
||||||
},
|
|
||||||
getters
|
|
||||||
})
|
|
||||||
|
|
||||||
export default store
|
|
@ -1,19 +0,0 @@
|
|||||||
const state = {
|
|
||||||
userName: ''
|
|
||||||
}
|
|
||||||
const mutations = {
|
|
||||||
SET_USER_NAME(state, name) {
|
|
||||||
state.userName = name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const actions = {
|
|
||||||
// 设置name
|
|
||||||
setUserName({ commit }, name) {
|
|
||||||
commit('SET_USER_NAME', name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export default {
|
|
||||||
state,
|
|
||||||
mutations,
|
|
||||||
actions
|
|
||||||
}
|
|
@ -1,110 +0,0 @@
|
|||||||
/**
|
|
||||||
* Created by PanJiaChen on 16/11/18.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse the time to string
|
|
||||||
* @param {(Object|string|number)} time
|
|
||||||
* @param {string} cFormat
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
export function parseTime(time, cFormat) {
|
|
||||||
if (arguments.length === 0) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
|
|
||||||
let date
|
|
||||||
if (typeof time === 'object') {
|
|
||||||
date = time
|
|
||||||
} else {
|
|
||||||
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
|
|
||||||
time = parseInt(time)
|
|
||||||
}
|
|
||||||
if ((typeof time === 'number') && (time.toString().length === 10)) {
|
|
||||||
time = time * 1000
|
|
||||||
}
|
|
||||||
date = new Date(time)
|
|
||||||
}
|
|
||||||
const formatObj = {
|
|
||||||
y: date.getFullYear(),
|
|
||||||
m: date.getMonth() + 1,
|
|
||||||
d: date.getDate(),
|
|
||||||
h: date.getHours(),
|
|
||||||
i: date.getMinutes(),
|
|
||||||
s: date.getSeconds(),
|
|
||||||
a: date.getDay()
|
|
||||||
}
|
|
||||||
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
|
|
||||||
let value = formatObj[key]
|
|
||||||
// Note: getDay() returns 0 on Sunday
|
|
||||||
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] }
|
|
||||||
if (result.length > 0 && value < 10) {
|
|
||||||
value = '0' + value
|
|
||||||
}
|
|
||||||
return value || 0
|
|
||||||
})
|
|
||||||
return time_str
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {number} time
|
|
||||||
* @param {string} option
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
export function formatTime(time, option) {
|
|
||||||
if (('' + time).length === 10) {
|
|
||||||
time = parseInt(time) * 1000
|
|
||||||
} else {
|
|
||||||
time = +time
|
|
||||||
}
|
|
||||||
const d = new Date(time)
|
|
||||||
const now = Date.now()
|
|
||||||
|
|
||||||
const diff = (now - d) / 1000
|
|
||||||
|
|
||||||
if (diff < 30) {
|
|
||||||
return '刚刚'
|
|
||||||
} else if (diff < 3600) {
|
|
||||||
// less 1 hour
|
|
||||||
return Math.ceil(diff / 60) + '分钟前'
|
|
||||||
} else if (diff < 3600 * 24) {
|
|
||||||
return Math.ceil(diff / 3600) + '小时前'
|
|
||||||
} else if (diff < 3600 * 24 * 2) {
|
|
||||||
return '1天前'
|
|
||||||
}
|
|
||||||
if (option) {
|
|
||||||
return parseTime(time, option)
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
d.getMonth() +
|
|
||||||
1 +
|
|
||||||
'月' +
|
|
||||||
d.getDate() +
|
|
||||||
'日' +
|
|
||||||
d.getHours() +
|
|
||||||
'时' +
|
|
||||||
d.getMinutes() +
|
|
||||||
'分'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} url
|
|
||||||
* @returns {Object}
|
|
||||||
*/
|
|
||||||
export function param2Obj(url) {
|
|
||||||
const search = url.split('?')[1]
|
|
||||||
if (!search) {
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
return JSON.parse(
|
|
||||||
'{"' +
|
|
||||||
decodeURIComponent(search)
|
|
||||||
.replace(/"/g, '\\"')
|
|
||||||
.replace(/&/g, '","')
|
|
||||||
.replace(/=/g, '":"')
|
|
||||||
.replace(/\+/g, ' ') +
|
|
||||||
'"}'
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
import axios from 'axios'
|
|
||||||
import store from '@/store'
|
|
||||||
import { Toast } from 'vant'
|
|
||||||
// 根据环境不同引入不同api地址
|
|
||||||
import { baseApi } from '@/config'
|
|
||||||
// create an axios instance
|
|
||||||
const service = axios.create({
|
|
||||||
baseURL: baseApi, // url = base api url + request url
|
|
||||||
withCredentials: true, // send cookies when cross-domain requests
|
|
||||||
timeout: 5000 // request timeout
|
|
||||||
})
|
|
||||||
|
|
||||||
// request拦截器 request interceptor
|
|
||||||
service.interceptors.request.use(
|
|
||||||
config => {
|
|
||||||
// 不传递默认开启loading
|
|
||||||
if (!config.hideloading) {
|
|
||||||
// loading
|
|
||||||
Toast.loading({
|
|
||||||
forbidClick: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (store.getters.token) {
|
|
||||||
config.headers['X-Token'] = ''
|
|
||||||
}
|
|
||||||
return config
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
// do something with request error
|
|
||||||
console.log(error) // for debug
|
|
||||||
return Promise.reject(error)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
// respone拦截器
|
|
||||||
service.interceptors.response.use(
|
|
||||||
response => {
|
|
||||||
Toast.clear()
|
|
||||||
const res = response.data
|
|
||||||
if (res.status && res.status !== 200) {
|
|
||||||
// 登录超时,重新登录
|
|
||||||
if (res.status === 401) {
|
|
||||||
store.dispatch('FedLogOut').then(() => {
|
|
||||||
location.reload()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return Promise.reject(res || 'error')
|
|
||||||
} else {
|
|
||||||
return Promise.resolve(res)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
Toast.clear()
|
|
||||||
console.log('err' + error) // for debug
|
|
||||||
return Promise.reject(error)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
export default service
|
|
@ -1,20 +0,0 @@
|
|||||||
/**
|
|
||||||
* Created by Sunnie on 19/06/04.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} path
|
|
||||||
* @returns {Boolean}
|
|
||||||
*/
|
|
||||||
export function isExternal(path) {
|
|
||||||
return /^(https?:|mailto:|tel:)/.test(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} str
|
|
||||||
* @returns {Boolean}
|
|
||||||
*/
|
|
||||||
export function validUsername(str) {
|
|
||||||
const valid_map = ['admin', 'editor']
|
|
||||||
return valid_map.indexOf(str.trim()) >= 0
|
|
||||||
}
|
|
@ -1,108 +0,0 @@
|
|||||||
<!-- home -->
|
|
||||||
<template>
|
|
||||||
<div class="app-container">
|
|
||||||
<div class="warpper">
|
|
||||||
<div class="list">
|
|
||||||
<div class="logo"></div>
|
|
||||||
<div class="demo-home__title">VUE H5开发模板</div>
|
|
||||||
<div class="item">
|
|
||||||
项目地址:
|
|
||||||
<a href="https://github.com/sunniejs/vue-h5-template">https://github.com/sunniejs/vue-h5-template</a>
|
|
||||||
</div>
|
|
||||||
<div class="item">项目作者: sunnie</div>
|
|
||||||
<div class="item"></div>
|
|
||||||
<div class="wechat">
|
|
||||||
<img :src="this.wechat" alt="" />
|
|
||||||
</div>
|
|
||||||
<div class="item">关注公众号:回复“加群”即可加 前端仙女群</div>
|
|
||||||
<div class="item">
|
|
||||||
{{ userName }}
|
|
||||||
<van-button v-if="userName == ''" type="warning" size="small" @click="doDispatch">快点我~</van-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// 请求接口
|
|
||||||
import { getUserInfo } from '@/api/user.js'
|
|
||||||
import { mapGetters } from 'vuex'
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
wechat: `${this.$cdn}/wx/640.gif`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapGetters(['userName'])
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.initData()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// 请求数据案例
|
|
||||||
initData() {
|
|
||||||
// 请求接口数据,仅作为展示,需要配置src->config下环境文件
|
|
||||||
const params = { user: 'sunnie' }
|
|
||||||
getUserInfo(params)
|
|
||||||
.then(() => {})
|
|
||||||
.catch(() => {})
|
|
||||||
},
|
|
||||||
// Action 通过 store.dispatch 方法触发
|
|
||||||
doDispatch() {
|
|
||||||
this.$store.dispatch('setUserName', '真乖,赶紧关注公众号,组织都在等你~')
|
|
||||||
},
|
|
||||||
goGithub(index) {
|
|
||||||
window.location.href = 'https://github.com/sunniejs/vue-h5-template'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.app-container {
|
|
||||||
background: #fff;
|
|
||||||
height: 100vh;
|
|
||||||
box-sizing: border-box;
|
|
||||||
.warpper {
|
|
||||||
padding: 50px 12px 12px 12px;
|
|
||||||
.list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
color: #666;
|
|
||||||
font-size: 14px;
|
|
||||||
.demo-home__title {
|
|
||||||
margin: 0 0 6px;
|
|
||||||
font-size: 32px;
|
|
||||||
.demo-home__title img,
|
|
||||||
.demo-home__title span {
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.item {
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 34px;
|
|
||||||
a {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
width: 120px;
|
|
||||||
height: 120px;
|
|
||||||
background: url($cdn+'/weapp/logo.png') center / contain no-repeat;
|
|
||||||
}
|
|
||||||
.wechat {
|
|
||||||
width: 200px;
|
|
||||||
height: 200px;
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,75 +0,0 @@
|
|||||||
<!-- home -->
|
|
||||||
<template>
|
|
||||||
<div class="app-container">
|
|
||||||
<div class="warpper">
|
|
||||||
<h1 class="demo-home__title"><img src="https://imgs.solui.cn/weapp/logo.png" /><span> VUE H5开发模板</span></h1>
|
|
||||||
<h2 class="demo-home__desc">
|
|
||||||
A vue h5 template with Vant UI
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
<van-cell icon="success" v-for="item in list" :key="item" :title="item" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
list: [
|
|
||||||
'Vue-cli4',
|
|
||||||
'配置多环境变量',
|
|
||||||
'VantUI 组件按需加载',
|
|
||||||
'Sass 全局样式',
|
|
||||||
'Webpack 4',
|
|
||||||
'Vuex 状态管理',
|
|
||||||
'Axios 封装及接口管理',
|
|
||||||
'Vue-router',
|
|
||||||
'Webpack 4 vue.config.js 基础配置',
|
|
||||||
'配置 proxy 跨域',
|
|
||||||
'配置 alias 别名',
|
|
||||||
'配置 打包分析',
|
|
||||||
'配置 externals 引入 cdn 资源',
|
|
||||||
'去掉 console.log',
|
|
||||||
'splitChunks 单独打包第三方模块',
|
|
||||||
'添加 IE 兼容',
|
|
||||||
'Eslint+Pettier 统一开发规范'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {},
|
|
||||||
|
|
||||||
mounted() {},
|
|
||||||
|
|
||||||
methods: {}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.app-container {
|
|
||||||
.warpper {
|
|
||||||
padding: 12px;
|
|
||||||
background: #fff;
|
|
||||||
.demo-home__title {
|
|
||||||
margin: 0 0 6px;
|
|
||||||
font-size: 32px;
|
|
||||||
.demo-home__title img,
|
|
||||||
.demo-home__title span {
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
width: 32px;
|
|
||||||
}
|
|
||||||
span {
|
|
||||||
margin-left: 16px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.demo-home__desc {
|
|
||||||
margin: 0 0 20px;
|
|
||||||
color: rgba(69, 90, 100, 0.6);
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
BIN
static/demo.png
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 22 KiB |
180
vue.config.js
@ -1,180 +0,0 @@
|
|||||||
'use strict'
|
|
||||||
const path = require('path')
|
|
||||||
const defaultSettings = require('./src/config/index.js')
|
|
||||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
|
|
||||||
|
|
||||||
const resolve = dir => path.join(__dirname, dir)
|
|
||||||
// page title
|
|
||||||
const name = defaultSettings.title || 'vue mobile template'
|
|
||||||
const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)
|
|
||||||
|
|
||||||
// externals
|
|
||||||
// const externals = {
|
|
||||||
// vue: 'Vue',
|
|
||||||
// 'vue-router': 'VueRouter',
|
|
||||||
// vuex: 'Vuex',
|
|
||||||
// vant: 'vant',
|
|
||||||
// axios: 'axios'
|
|
||||||
// }
|
|
||||||
// CDN外链,会插入到index.html中
|
|
||||||
// const cdn = {
|
|
||||||
// // 开发环境
|
|
||||||
// dev: {
|
|
||||||
// css: [],
|
|
||||||
// js: []
|
|
||||||
// },
|
|
||||||
// // 生产环境
|
|
||||||
// build: {
|
|
||||||
// css: ['https://cdn.jsdelivr.net/npm/vant@2.4.7/lib/index.css'],
|
|
||||||
// js: [
|
|
||||||
// 'https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js',
|
|
||||||
// 'https://cdn.jsdelivr.net/npm/vue-router@3.1.5/dist/vue-router.min.js',
|
|
||||||
// 'https://cdn.jsdelivr.net/npm/axios@0.19.2/dist/axios.min.js',
|
|
||||||
// 'https://cdn.jsdelivr.net/npm/vuex@3.1.2/dist/vuex.min.js',
|
|
||||||
// 'https://cdn.jsdelivr.net/npm/vant@2.4.7/lib/index.min.js'
|
|
||||||
// ]
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
publicPath: './', // 署应用包时的基本 URL。 vue-router hash 模式使用
|
|
||||||
// publicPath: '/app/', //署应用包时的基本 URL。 vue-router history模式使用
|
|
||||||
outputDir: 'dist', // 生产环境构建文件的目录
|
|
||||||
assetsDir: 'static', // outputDir的静态资源(js、css、img、fonts)目录
|
|
||||||
lintOnSave: !IS_PROD,
|
|
||||||
productionSourceMap: false, // 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
|
|
||||||
devServer: {
|
|
||||||
port: 9020, // 端口
|
|
||||||
open: false, // 启动后打开浏览器
|
|
||||||
overlay: {
|
|
||||||
// 当出现编译器错误或警告时,在浏览器中显示全屏覆盖层
|
|
||||||
warnings: false,
|
|
||||||
errors: true
|
|
||||||
}
|
|
||||||
// proxy: {
|
|
||||||
// //配置跨域
|
|
||||||
// '/api': {
|
|
||||||
// target: "https://test.xxx.com",
|
|
||||||
// // ws:true,
|
|
||||||
// changOrigin:true,
|
|
||||||
// pathRewrite:{
|
|
||||||
// '^/api':'/'
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
},
|
|
||||||
css: {
|
|
||||||
extract: IS_PROD,
|
|
||||||
sourceMap: false,
|
|
||||||
loaderOptions: {
|
|
||||||
scss: {
|
|
||||||
// 向全局sass样式传入共享的全局变量, $src可以配置图片cdn前缀
|
|
||||||
// 详情: https://cli.vuejs.org/guide/css.html#passing-options-to-pre-processor-loaders
|
|
||||||
prependData: `
|
|
||||||
@import "assets/css/index.scss";
|
|
||||||
@import "assets/css/mixin.scss";
|
|
||||||
@import "assets/css/variables.scss";
|
|
||||||
$cdn: "${defaultSettings.$cdn}";
|
|
||||||
`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
configureWebpack: config => {
|
|
||||||
config.name = name
|
|
||||||
|
|
||||||
// 为生产环境修改配置...
|
|
||||||
// if (IS_PROD) {
|
|
||||||
// // externals
|
|
||||||
// config.externals = externals
|
|
||||||
// }
|
|
||||||
},
|
|
||||||
|
|
||||||
chainWebpack: config => {
|
|
||||||
config.plugins.delete('preload') // TODO: need test
|
|
||||||
config.plugins.delete('prefetch') // TODO: need test
|
|
||||||
|
|
||||||
// 别名 alias
|
|
||||||
config.resolve.alias
|
|
||||||
.set('@', resolve('src'))
|
|
||||||
.set('assets', resolve('src/assets'))
|
|
||||||
.set('api', resolve('src/api'))
|
|
||||||
.set('views', resolve('src/views'))
|
|
||||||
.set('components', resolve('src/components'))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加CDN参数到htmlWebpackPlugin配置中
|
|
||||||
*/
|
|
||||||
// config.plugin('html').tap(args => {
|
|
||||||
// if (IS_PROD) {
|
|
||||||
// args[0].cdn = cdn.build
|
|
||||||
// } else {
|
|
||||||
// args[0].cdn = cdn.dev
|
|
||||||
// }
|
|
||||||
// return args
|
|
||||||
// })
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置保留空格
|
|
||||||
*/
|
|
||||||
config.module
|
|
||||||
.rule('vue')
|
|
||||||
.use('vue-loader')
|
|
||||||
.loader('vue-loader')
|
|
||||||
.tap(options => {
|
|
||||||
options.compilerOptions.preserveWhitespace = true
|
|
||||||
return options
|
|
||||||
})
|
|
||||||
.end()
|
|
||||||
/**
|
|
||||||
* 打包分析
|
|
||||||
*/
|
|
||||||
if (IS_PROD) {
|
|
||||||
config.plugin('webpack-report').use(BundleAnalyzerPlugin, [
|
|
||||||
{
|
|
||||||
analyzerMode: 'static'
|
|
||||||
}
|
|
||||||
])
|
|
||||||
}
|
|
||||||
config
|
|
||||||
// https://webpack.js.org/configuration/devtool/#development
|
|
||||||
.when(!IS_PROD, config => config.devtool('cheap-source-map'))
|
|
||||||
|
|
||||||
config.when(IS_PROD, config => {
|
|
||||||
config
|
|
||||||
.plugin('ScriptExtHtmlWebpackPlugin')
|
|
||||||
.after('html')
|
|
||||||
.use('script-ext-html-webpack-plugin', [
|
|
||||||
{
|
|
||||||
// 将 runtime 作为内联引入不单独存在
|
|
||||||
inline: /runtime\..*\.js$/
|
|
||||||
}
|
|
||||||
])
|
|
||||||
.end()
|
|
||||||
config.optimization.splitChunks({
|
|
||||||
chunks: 'all',
|
|
||||||
cacheGroups: {
|
|
||||||
// cacheGroups 下可以可以配置多个组,每个组根据test设置条件,符合test条件的模块
|
|
||||||
commons: {
|
|
||||||
name: 'chunk-commons',
|
|
||||||
test: resolve('src/components'),
|
|
||||||
minChunks: 3, // 被至少用三次以上打包分离
|
|
||||||
priority: 5, // 优先级
|
|
||||||
reuseExistingChunk: true // 表示是否使用已有的 chunk,如果为 true 则表示如果当前的 chunk 包含的模块已经被抽取出去了,那么将不会重新生成新的。
|
|
||||||
},
|
|
||||||
node_vendors: {
|
|
||||||
name: 'chunk-libs',
|
|
||||||
chunks: 'initial', // 只打包初始时依赖的第三方
|
|
||||||
test: /[\\/]node_modules[\\/]/,
|
|
||||||
priority: 10
|
|
||||||
},
|
|
||||||
vantUI: {
|
|
||||||
name: 'chunk-vantUI', // 单独将 vantUI 拆包
|
|
||||||
priority: 20, // 数字大权重到,满足多个 cacheGroups 的条件时候分到权重高的
|
|
||||||
test: /[\\/]node_modules[\\/]_?vant(.*)/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
config.optimization.runtimeChunk('single')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
19
zh-cn/alias.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
### <span id="alias">✅ 配置 alias 别名 </span>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const path = require('path')
|
||||||
|
const resolve = dir => path.join(__dirname, dir)
|
||||||
|
const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
chainWebpack: config => {
|
||||||
|
// 添加别名
|
||||||
|
config.resolve.alias
|
||||||
|
.set('@', resolve('src'))
|
||||||
|
.set('assets', resolve('src/assets'))
|
||||||
|
.set('api', resolve('src/api'))
|
||||||
|
.set('views', resolve('src/views'))
|
||||||
|
.set('components', resolve('src/components'))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
106
zh-cn/axios.md
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
### <span id="axios">✅ Axios 封装及接口管理</span>
|
||||||
|
|
||||||
|
`utils/request.js` 封装 axios ,开发者需要根据后台接口做修改。
|
||||||
|
|
||||||
|
- `service.interceptors.request.use` 里可以设置请求头,比如设置 `token`
|
||||||
|
- `config.hideloading` 是在 api 文件夹下的接口参数里设置,下文会讲
|
||||||
|
- `service.interceptors.response.use` 里可以对接口返回数据处理,比如 401 删除本地信息,重新登录
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import axios from 'axios'
|
||||||
|
import store from '@/store'
|
||||||
|
import { Toast } from 'vant'
|
||||||
|
// 根据环境不同引入不同api地址
|
||||||
|
import { baseApi } from '@/config'
|
||||||
|
// create an axios instance
|
||||||
|
const service = axios.create({
|
||||||
|
baseURL: baseApi, // url = base api url + request url
|
||||||
|
withCredentials: true, // send cookies when cross-domain requests
|
||||||
|
timeout: 5000, // request timeout
|
||||||
|
})
|
||||||
|
|
||||||
|
// request 拦截器 request interceptor
|
||||||
|
service.interceptors.request.use(
|
||||||
|
config => {
|
||||||
|
// 不传递默认开启loading
|
||||||
|
if (!config.hideloading) {
|
||||||
|
// loading
|
||||||
|
Toast.loading({
|
||||||
|
forbidClick: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (store.getters.token) {
|
||||||
|
config.headers['X-Token'] = ''
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
// do something with request error
|
||||||
|
console.log(error) // for debug
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// respone拦截器
|
||||||
|
service.interceptors.response.use(
|
||||||
|
response => {
|
||||||
|
Toast.clear()
|
||||||
|
const res = response.data
|
||||||
|
if (res.status && res.status !== 200) {
|
||||||
|
// 登录超时,重新登录
|
||||||
|
if (res.status === 401) {
|
||||||
|
store.dispatch('FedLogOut').then(() => {
|
||||||
|
location.reload()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return Promise.reject(res || 'error')
|
||||||
|
} else {
|
||||||
|
return Promise.resolve(res)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
Toast.clear()
|
||||||
|
console.log('err' + error) // for debug
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
export default service
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 接口管理
|
||||||
|
|
||||||
|
在`src/api` 文件夹下统一管理接口
|
||||||
|
|
||||||
|
- 你可以建立多个模块对接接口, 比如 `home.js` 里是首页的接口这里讲解 `user.js`
|
||||||
|
- `url` 接口地址,请求的时候会拼接上 `config` 下的 `baseApi`
|
||||||
|
- `method` 请求方法
|
||||||
|
- `data` 请求参数 `qs.stringify(params)` 是对数据系列化操作
|
||||||
|
- `hideloading` 默认 `false`,设置为 `true` 后,不显示 loading ui 交互中有些接口不需要让用户感知
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import qs from 'qs'
|
||||||
|
// axios
|
||||||
|
import request from '@/utils/request'
|
||||||
|
//user api
|
||||||
|
|
||||||
|
// 用户信息
|
||||||
|
export function getUserInfo(params) {
|
||||||
|
return request({
|
||||||
|
url: '/user/userinfo',
|
||||||
|
method: 'post',
|
||||||
|
data: qs.stringify(params),
|
||||||
|
hideloading: true, // 隐藏 loading 组件
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 如何调用
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 请求接口
|
||||||
|
import { getUserInfo } from '@/api/user.js'
|
||||||
|
|
||||||
|
const params = { user: 'sunnie' }
|
||||||
|
getUserInfo(params)
|
||||||
|
.then(() => {})
|
||||||
|
.catch(() => {})
|
||||||
|
```
|
36
zh-cn/base.md
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
### <span id="base">✅ Webpack 4 vue.config.js 基础配置 </span>
|
||||||
|
|
||||||
|
如果你的 `Vue Router` 模式是 hash
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
publicPath: './',
|
||||||
|
```
|
||||||
|
|
||||||
|
如果你的 `Vue Router` 模式是 history 这里的 publicPath 和你的 `Vue Router` `base` **保持一直**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
publicPath: '/app/',
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
publicPath: './', // 署应用包时的基本 URL。 vue-router hash 模式使用
|
||||||
|
// publicPath: '/app/', // 署应用包时的基本 URL。 vue-router history模式使用
|
||||||
|
outputDir: 'dist', // 生产环境构建文件的目录
|
||||||
|
assetsDir: 'static', // outputDir的静态资源(js、css、img、fonts)目录
|
||||||
|
lintOnSave: process.env.NODE_ENV !== IS_PROD,
|
||||||
|
productionSourceMap: false, // 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
|
||||||
|
devServer: {
|
||||||
|
port: 9020, // 端口号
|
||||||
|
open: false, // 启动后打开浏览器
|
||||||
|
overlay: {
|
||||||
|
// 当出现编译器错误或警告时,在浏览器中显示全屏覆盖层
|
||||||
|
warnings: false,
|
||||||
|
errors: true,
|
||||||
|
},
|
||||||
|
// ...
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
22
zh-cn/bundle.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
### <span id="bundle">✅ 配置 打包分析 </span>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
chainWebpack: config => {
|
||||||
|
// 打包分析
|
||||||
|
if (IS_PROD) {
|
||||||
|
config.plugin('webpack-report').use(BundleAnalyzerPlugin, [
|
||||||
|
{
|
||||||
|
analyzerMode: 'static',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
```
|
45
zh-cn/chunks.md
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
### <span id="chunks">✅ splitChunks 单独打包第三方模块</span>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
module.exports = {
|
||||||
|
chainWebpack: config => {
|
||||||
|
config.when(IS_PROD, config => {
|
||||||
|
config
|
||||||
|
.plugin('ScriptExtHtmlWebpackPlugin')
|
||||||
|
.after('html')
|
||||||
|
.use('script-ext-html-webpack-plugin', [
|
||||||
|
{
|
||||||
|
// 将 runtime 作为内联引入不单独存在
|
||||||
|
inline: /runtime\..*\.js$/,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.end()
|
||||||
|
config.optimization.splitChunks({
|
||||||
|
chunks: 'all',
|
||||||
|
cacheGroups: {
|
||||||
|
// cacheGroups 下可以可以配置多个组,每个组根据test设置条件,符合test条件的模块
|
||||||
|
commons: {
|
||||||
|
name: 'chunk-commons',
|
||||||
|
test: resolve('src/components'),
|
||||||
|
minChunks: 3, // 被至少用三次以上打包分离
|
||||||
|
priority: 5, // 优先级
|
||||||
|
reuseExistingChunk: true, // 表示是否使用已有的 chunk,如果为 true 则表示如果当前的 chunk 包含的模块已经被抽取出去了,那么将不会重新生成新的。
|
||||||
|
},
|
||||||
|
node_vendors: {
|
||||||
|
name: 'chunk-libs',
|
||||||
|
chunks: 'initial', // 只打包初始时依赖的第三方
|
||||||
|
test: /[\\/]node_modules[\\/]/,
|
||||||
|
priority: 10,
|
||||||
|
},
|
||||||
|
vantUI: {
|
||||||
|
name: 'chunk-vantUI', // 单独将 vantUI 拆包
|
||||||
|
priority: 20, // 数字大权重到,满足多个 cacheGroups 的条件时候分到权重高的
|
||||||
|
test: /[\\/]node_modules[\\/]_?vant(.*)/,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
config.optimization.runtimeChunk('single')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
34
zh-cn/console.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
### <span id="console">✅ 去掉 console.log </span>
|
||||||
|
|
||||||
|
保留了测试环境和本地环境的 `console.log`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm i -D babel-plugin-transform-remove-console
|
||||||
|
```
|
||||||
|
|
||||||
|
在 babel.config.js 中配置
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 获取 VUE_APP_ENV 非 NODE_ENV,测试环境依然 console
|
||||||
|
const IS_PROD = ['production', 'prod'].includes(process.env.VUE_APP_ENV)
|
||||||
|
const plugins = [
|
||||||
|
[
|
||||||
|
'import',
|
||||||
|
{
|
||||||
|
libraryName: 'vant',
|
||||||
|
libraryDirectory: 'es',
|
||||||
|
style: true,
|
||||||
|
},
|
||||||
|
'vant',
|
||||||
|
],
|
||||||
|
]
|
||||||
|
// 去除 console.log
|
||||||
|
if (IS_PROD) {
|
||||||
|
plugins.push('transform-remove-console')
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
presets: [['@vue/cli-plugin-babel/preset', { useBuiltIns: 'entry' }]],
|
||||||
|
plugins,
|
||||||
|
}
|
||||||
|
```
|
84
zh-cn/env.md
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
### <span id="env">✅ 配置多环境变量 </span>
|
||||||
|
|
||||||
|
`package.json` 里的 `scripts` 配置 `serve` `stage` `build`,通过 `--mode xxx` 来执行不同环境
|
||||||
|
|
||||||
|
- 通过 `npm run serve` 启动本地 , 执行 `development`
|
||||||
|
- 通过 `npm run stage` 打包测试 , 执行 `staging`
|
||||||
|
- 通过 `npm run build` 打包正式 , 执行 `production`
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
"scripts": {
|
||||||
|
"serve": "vue-cli-service serve --open",
|
||||||
|
"stage": "vue-cli-service build --mode staging",
|
||||||
|
"build": "vue-cli-service build",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### 配置介绍
|
||||||
|
|
||||||
|
  以 `VUE_APP_` 开头的变量,在代码中可以通过 `process.env.VUE_APP_` 访问。
|
||||||
|
  比如,`VUE_APP_ENV = 'development'` 通过`process.env.VUE_APP_ENV` 访问。
|
||||||
|
  除了 `VUE_APP_*` 变量之外,在你的应用代码中始终可用的还有两个特殊的变量`NODE_ENV` 和`BASE_URL`
|
||||||
|
|
||||||
|
在项目根目录中新建`.env.*`
|
||||||
|
|
||||||
|
- .env.development 本地开发环境配置
|
||||||
|
|
||||||
|
```bash
|
||||||
|
NODE_ENV='development'
|
||||||
|
# must start with VUE_APP_
|
||||||
|
VUE_APP_ENV = 'development'
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
- .env.staging 测试环境配置
|
||||||
|
|
||||||
|
```bash
|
||||||
|
NODE_ENV='production'
|
||||||
|
# must start with VUE_APP_
|
||||||
|
VUE_APP_ENV = 'staging'
|
||||||
|
```
|
||||||
|
|
||||||
|
- .env.production 正式环境配置
|
||||||
|
|
||||||
|
```bash
|
||||||
|
NODE_ENV='production'
|
||||||
|
# must start with VUE_APP_
|
||||||
|
VUE_APP_ENV = 'production'
|
||||||
|
```
|
||||||
|
|
||||||
|
这里我们并没有定义很多变量,只定义了基础的 VUE_APP_ENV `development` `staging` `production`
|
||||||
|
变量我们统一在 `src/config/env.*.js` 里进行管理。
|
||||||
|
|
||||||
|
这里有个问题,既然这里有了根据不同环境设置变量的文件,为什么还要去 config 下新建三个对应的文件呢?
|
||||||
|
**修改起来方便,不需
|
||||||
|
要重启项目,符合开发习惯。**
|
||||||
|
|
||||||
|
config/index.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 根据环境引入不同配置 process.env.NODE_ENV
|
||||||
|
const config = require('./env.' + process.env.VUE_APP_ENV)
|
||||||
|
module.exports = config
|
||||||
|
```
|
||||||
|
|
||||||
|
配置对应环境的变量,拿本地环境文件 `env.development.js` 举例,用户可以根据需求修改
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 本地环境配置
|
||||||
|
module.exports = {
|
||||||
|
title: 'vue-h5-template',
|
||||||
|
baseUrl: 'http://localhost:9018', // 项目地址
|
||||||
|
baseApi: 'https://test.xxx.com/api', // 本地api请求地址
|
||||||
|
APPID: 'xxx',
|
||||||
|
APPSECRET: 'xxx',
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
根据环境不同,变量就会不同了
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 根据环境不同引入不同baseApi地址
|
||||||
|
import { baseApi } from '@/config'
|
||||||
|
console.log(baseApi)
|
||||||
|
```
|
84
zh-cn/externals.md
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
### <span id="externals">✅ 配置 externals 引入 cdn 资源 </span>
|
||||||
|
|
||||||
|
这个版本 CDN 不再引入,我测试了一下使用引入 CDN 和不使用,不使用会比使用时间少。网上不少文章测试 CDN 速度块,这个开发者可
|
||||||
|
以实际测试一下。
|
||||||
|
|
||||||
|
另外项目中使用的是公共 CDN 不稳定,域名解析也是需要时间的(如果你要使用请尽量使用同一个域名)
|
||||||
|
|
||||||
|
因为页面每次遇到`<script>`标签都会停下来解析执行,所以应该尽可能减少`<script>`标签的数量 `HTTP`请求存在一定的开销,100K
|
||||||
|
的文件比 5 个 20K 的文件下载的更快,所以较少脚本数量也是很有必要的
|
||||||
|
|
||||||
|
暂时还没有研究放到自己的 cdn 服务器上。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const defaultSettings = require('./src/config/index.js')
|
||||||
|
const name = defaultSettings.title || 'vue mobile template'
|
||||||
|
const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)
|
||||||
|
|
||||||
|
// externals
|
||||||
|
const externals = {
|
||||||
|
vue: 'Vue',
|
||||||
|
'vue-router': 'VueRouter',
|
||||||
|
vuex: 'Vuex',
|
||||||
|
vant: 'vant',
|
||||||
|
axios: 'axios',
|
||||||
|
}
|
||||||
|
// CDN外链,会插入到index.html中
|
||||||
|
const cdn = {
|
||||||
|
// 开发环境
|
||||||
|
dev: {
|
||||||
|
css: [],
|
||||||
|
js: [],
|
||||||
|
},
|
||||||
|
// 生产环境
|
||||||
|
build: {
|
||||||
|
css: ['https://cdn.jsdelivr.net/npm/vant@2.4.7/lib/index.css'],
|
||||||
|
js: [
|
||||||
|
'https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js',
|
||||||
|
'https://cdn.jsdelivr.net/npm/vue-router@3.1.5/dist/vue-router.min.js',
|
||||||
|
'https://cdn.jsdelivr.net/npm/axios@0.19.2/dist/axios.min.js',
|
||||||
|
'https://cdn.jsdelivr.net/npm/vuex@3.1.2/dist/vuex.min.js',
|
||||||
|
'https://cdn.jsdelivr.net/npm/vant@2.4.7/lib/index.min.js',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
module.exports = {
|
||||||
|
configureWebpack: config => {
|
||||||
|
config.name = name
|
||||||
|
// 为生产环境修改配置...
|
||||||
|
if (IS_PROD) {
|
||||||
|
// externals
|
||||||
|
config.externals = externals
|
||||||
|
}
|
||||||
|
},
|
||||||
|
chainWebpack: config => {
|
||||||
|
/**
|
||||||
|
* 添加CDN参数到htmlWebpackPlugin配置中
|
||||||
|
*/
|
||||||
|
config.plugin('html').tap(args => {
|
||||||
|
if (IS_PROD) {
|
||||||
|
args[0].cdn = cdn.build
|
||||||
|
} else {
|
||||||
|
args[0].cdn = cdn.dev
|
||||||
|
}
|
||||||
|
return args
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
在 public/index.html 中添加
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
<!-- 使用CDN的CSS文件 -->
|
||||||
|
<% for (var i in
|
||||||
|
htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.css) { %>
|
||||||
|
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style" />
|
||||||
|
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet" />
|
||||||
|
<% } %>
|
||||||
|
<!-- 使用CDN加速的JS文件,配置在vue.config.js下 -->
|
||||||
|
<% for (var i in
|
||||||
|
htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
|
||||||
|
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
|
||||||
|
<% } %>
|
||||||
|
```
|
30
zh-cn/ie.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
### <span id="ie">✅ 添加 IE 兼容 </span>
|
||||||
|
|
||||||
|
之前的方式 会报 `@babel/polyfill` is deprecated. Please, use required parts of `core-js` and
|
||||||
|
`regenerator-runtime/runtime` separately
|
||||||
|
|
||||||
|
`@babel/polyfill` 废弃,使用 `core-js` 和 `regenerator-runtime`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm i --save core-js regenerator-runtime
|
||||||
|
```
|
||||||
|
|
||||||
|
在 `main.js` 中添加
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 兼容 IE
|
||||||
|
// https://github.com/zloirock/core-js/blob/master/docs/2019-03-19-core-js-3-babel-and-a-look-into-the-future.md#babelpolyfill
|
||||||
|
import 'core-js/stable'
|
||||||
|
import 'regenerator-runtime/runtime'
|
||||||
|
```
|
||||||
|
|
||||||
|
配置 `babel.config.js`
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const plugins = []
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
presets: [['@vue/cli-plugin-babel/preset', { useBuiltIns: 'usage', corejs: 3 }]],
|
||||||
|
plugins,
|
||||||
|
}
|
||||||
|
```
|
50
zh-cn/pettier.md
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
### <span id="pettier">✅ Eslint + Pettier 统一开发规范 </span>
|
||||||
|
|
||||||
|
VScode 安装 `eslint` `prettier` `vetur` 插件
|
||||||
|
|
||||||
|
在文件 `.prettierrc` 里写 属于你的 pettier 规则
|
||||||
|
|
||||||
|
```bash
|
||||||
|
{
|
||||||
|
"printWidth": 120,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"semi": false,
|
||||||
|
"wrap_line_length": 120,
|
||||||
|
"wrap_attributes": "auto",
|
||||||
|
"proseWrap": "always",
|
||||||
|
"arrowParens": "avoid",
|
||||||
|
"bracketSpacing": false,
|
||||||
|
"jsxBracketSameLine": true,
|
||||||
|
"useTabs": false,
|
||||||
|
"overrides": [{
|
||||||
|
"files": ".prettierrc",
|
||||||
|
"options": {
|
||||||
|
"parser": "json"
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Vscode setting.json 设置
|
||||||
|
|
||||||
|
```bash
|
||||||
|
"[vue]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
},
|
||||||
|
"[javascript]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
},
|
||||||
|
// 保存时用eslint格式化
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll.eslint": true
|
||||||
|
},
|
||||||
|
// 两者会在格式化js时冲突,所以需要关闭默认js格式化程序
|
||||||
|
"javascript.format.enable": false,
|
||||||
|
"typescript.format.enable": false,
|
||||||
|
"vetur.format.defaultFormatter.html": "none",
|
||||||
|
// js/ts程序用eslint,防止vetur中的prettier与eslint格式化冲突
|
||||||
|
"vetur.format.defaultFormatter.js": "none",
|
||||||
|
"vetur.format.defaultFormatter.ts": "none",
|
||||||
|
```
|
36
zh-cn/proxy.md
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
### <span id="proxy">✅ 配置 proxy 跨域 </span>
|
||||||
|
|
||||||
|
如果你的项目需要跨域设置,你需要打来 `vue.config.js` `proxy` 注释 并且配置相应参数
|
||||||
|
|
||||||
|
<u>**!!!注意:你还需要将 `src/config/env.development.js` 里的 `baseApi` 设置成 '/'**</u>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
module.exports = {
|
||||||
|
devServer: {
|
||||||
|
// ....
|
||||||
|
proxy: {
|
||||||
|
//配置跨域
|
||||||
|
'/api': {
|
||||||
|
target: 'https://test.xxx.com', // 接口的域名
|
||||||
|
// ws: true, // 是否启用websockets
|
||||||
|
changOrigin: true, // 开启代理,在本地创建一个虚拟服务端
|
||||||
|
pathRewrite: {
|
||||||
|
'^/api': '/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
使用 例如: `src/api/home.js`
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
export function getUserInfo(params) {
|
||||||
|
return request({
|
||||||
|
url: '/api/userinfo',
|
||||||
|
method: 'post',
|
||||||
|
data: qs.stringify(params),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
19
zh-cn/quickstart.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
### Node 版本要求
|
||||||
|
|
||||||
|
`Vue CLI` 需要 Node.js 8.9 或更高版本 (推荐 8.11.0+)。你可以使用 [nvm](https://github.com/nvm-sh/nvm) 或
|
||||||
|
[nvm-windows](https://github.com/coreybutler/nvm-windows) 在同一台电脑中管理多个 Node 版本。
|
||||||
|
|
||||||
|
本示例 Node.js 12.14.1
|
||||||
|
|
||||||
|
### 启动项目
|
||||||
|
|
||||||
|
```bash
|
||||||
|
|
||||||
|
git clone https://github.com/sunniejs/vue-h5-template.git
|
||||||
|
|
||||||
|
cd vue-h5-template
|
||||||
|
|
||||||
|
npm install
|
||||||
|
|
||||||
|
npm run serve
|
||||||
|
```
|
71
zh-cn/rem.md
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
### <span id="rem">✅ rem 适配方案 </span>
|
||||||
|
|
||||||
|
不用担心,项目已经配置好了 `rem` 适配, 下面仅做介绍:
|
||||||
|
|
||||||
|
Vant 中的样式默认使用`px`作为单位,如果需要使用`rem`单位,推荐使用以下两个工具:
|
||||||
|
|
||||||
|
- [postcss-pxtorem](https://github.com/cuth/postcss-pxtorem) 是一款 `postcss` 插件,用于将单位转化为 `rem`
|
||||||
|
- [lib-flexible](https://github.com/amfe/lib-flexible) 用于设置 `rem` 基准值
|
||||||
|
|
||||||
|
##### PostCSS 配置
|
||||||
|
|
||||||
|
下面提供了一份基本的 `postcss` 配置,可以在此配置的基础上根据项目需求进行修改
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// https://github.com/michael-ciniawsky/postcss-load-config
|
||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
autoprefixer: {
|
||||||
|
overrideBrowserslist: ['Android 4.1', 'iOS 7.1', 'Chrome > 31', 'ff > 31', 'ie >= 8'],
|
||||||
|
},
|
||||||
|
'postcss-pxtorem': {
|
||||||
|
rootValue: 37.5,
|
||||||
|
propList: ['*'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
更多详细信息: [vant](https://youzan.github.io/vant/#/zh-CN/quickstart#jin-jie-yong-fa)
|
||||||
|
|
||||||
|
**新手必看,老鸟跳过**
|
||||||
|
|
||||||
|
很多小伙伴会问我,适配的问题。
|
||||||
|
|
||||||
|
我们知道 `1rem` 等于`html` 根元素设定的 `font-size` 的 `px` 值。Vant UI 设置 `rootValue: 37.5`,你可以看到在 iPhone 6 下
|
||||||
|
看到 (`1rem 等于 37.5px`):
|
||||||
|
|
||||||
|
```html
|
||||||
|
<html data-dpr="1" style="font-size: 37.5px;"></html>
|
||||||
|
```
|
||||||
|
|
||||||
|
切换不同的机型,根元素可能会有不同的`font-size`。当你写 css px 样式时,会被程序换算成 `rem` 达到适配。
|
||||||
|
|
||||||
|
因为我们用了 Vant 的组件,需要按照 `rootValue: 37.5` 来写样式。
|
||||||
|
|
||||||
|
举个例子:设计给了你一张 750px \* 1334px 图片,在 iPhone6 上铺满屏幕,其他机型适配。
|
||||||
|
|
||||||
|
- 当`rootValue: 70` , 样式 `width: 750px;height: 1334px;` 图片会撑满 iPhone6 屏幕,这个时候切换其他机型,图片也会跟着撑
|
||||||
|
满。
|
||||||
|
- 当`rootValue: 37.5` 的时候,样式 `width: 375px;height: 667px;` 图片会撑满 iPhone6 屏幕。
|
||||||
|
|
||||||
|
也就是 iphone 6 下 375px 宽度写 CSS。其他的你就可以根据你设计图,去写对应的样式就可以了。
|
||||||
|
|
||||||
|
当然,想要撑满屏幕你可以使用 100%,这里只是举例说明。
|
||||||
|
|
||||||
|
```html
|
||||||
|
<img class="image" src="https://imgs.solui.cn/weapp/logo.png" />
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* rootValue: 75 */
|
||||||
|
.image {
|
||||||
|
width: 750px;
|
||||||
|
height: 1334px;
|
||||||
|
}
|
||||||
|
/* rootValue: 37.5 */
|
||||||
|
.image {
|
||||||
|
width: 375px;
|
||||||
|
height: 667px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
```
|
34
zh-cn/router.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
### <span id="router">✅ Vue-router </span>
|
||||||
|
|
||||||
|
本案例采用 `hash` 模式,开发者根据需求修改 `mode` `base`
|
||||||
|
|
||||||
|
**注意**:如果你使用了 `history` 模式,`vue.config.js` 中的 `publicPath` 要做对应的**修改**
|
||||||
|
|
||||||
|
前往:[vue.config.js 基础配置](#base)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import Vue from 'vue'
|
||||||
|
import Router from 'vue-router'
|
||||||
|
|
||||||
|
Vue.use(Router)
|
||||||
|
export const router = [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
name: 'index',
|
||||||
|
component: () => import('@/views/home/index'), // 路由懒加载
|
||||||
|
meta: {
|
||||||
|
title: '首页', // 页面标题
|
||||||
|
keepAlive: false, // keep-alive 标识
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
const createRouter = () =>
|
||||||
|
new Router({
|
||||||
|
// mode: 'history', // 如果你是 history模式 需要配置 vue.config.js publicPath
|
||||||
|
// base: '/app/',
|
||||||
|
scrollBehavior: () => ({ y: 0 }),
|
||||||
|
routes: router,
|
||||||
|
})
|
||||||
|
|
||||||
|
export default createRouter()
|
||||||
|
```
|
79
zh-cn/sass.md
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
### <span id="sass">✅ Sass 全局样式</span>
|
||||||
|
|
||||||
|
首先 你可能会遇到 `node-sass` 安装不成功,别放弃多试几次!!!
|
||||||
|
|
||||||
|
目录结构,在 `src/assets/css/`文件夹下包含了三个文件
|
||||||
|
|
||||||
|
```bash
|
||||||
|
├── assets
|
||||||
|
│ ├── css
|
||||||
|
│ │ ├── index.scss # 全局通用样式
|
||||||
|
│ │ ├── mixin.scss # 全局mixin
|
||||||
|
│ │ └── variables.scss # 全局变量
|
||||||
|
```
|
||||||
|
|
||||||
|
每个页面自己对应的样式都写在自己的 .vue 文件之中
|
||||||
|
|
||||||
|
```html
|
||||||
|
<style lang="scss">
|
||||||
|
/* global styles */
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
/* local styles */
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
|
`vue.config.js` 配置注入 `sass` 的 `mixin` `variables` 到全局,不需要手动引入 ,配置`$cdn`通过变量形式引入 cdn 地址
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)
|
||||||
|
const defaultSettings = require('./src/config/index.js')
|
||||||
|
module.exports = {
|
||||||
|
css: {
|
||||||
|
extract: IS_PROD,
|
||||||
|
sourceMap: false,
|
||||||
|
loaderOptions: {
|
||||||
|
scss: {
|
||||||
|
// 注入 `sass` 的 `mixin` `variables` 到全局, $cdn可以配置图片cdn
|
||||||
|
// 详情: https://cli.vuejs.org/guide/css.html#passing-options-to-pre-processor-loaders
|
||||||
|
prependData: `
|
||||||
|
@import "assets/css/mixin.scss";
|
||||||
|
@import "assets/css/variables.scss";
|
||||||
|
$cdn: "${defaultSettings.$cdn}";
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
在 `main.js` 中引用全局样式(发现在上面的,prependData 里设置`@import "assets/css/index.scss";`并没有应用全局样式这里在
|
||||||
|
main.js 引入)
|
||||||
|
|
||||||
|
设置 js 中可以访问 `$cdn`,`.vue` 文件中使用`this.$cdn`访问
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 引入全局样式
|
||||||
|
import '@/assets/css/index.scss'
|
||||||
|
|
||||||
|
// 设置 js中可以访问 $cdn
|
||||||
|
// 引入cdn
|
||||||
|
import { $cdn } from '@/config'
|
||||||
|
Vue.prototype.$cdn = $cdn
|
||||||
|
```
|
||||||
|
|
||||||
|
在 css 和 js 使用
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script>
|
||||||
|
console.log(this.$cdn)
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.logo {
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
background: url($cdn+'/weapp/logo.png') center / contain no-repeat;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
```
|
49
zh-cn/vant.md
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
### <span id="vant">✅ VantUI 组件按需加载 </span>
|
||||||
|
|
||||||
|
项目采
|
||||||
|
用[Vant 自动按需引入组件 (推荐)](https://youzan.github.io/vant/#/zh-CN/quickstart#fang-shi-yi.-zi-dong-an-xu-yin-ru-zu-jian-tui-jian)下
|
||||||
|
面安装插件介绍:
|
||||||
|
|
||||||
|
[babel-plugin-import](https://github.com/ant-design/babel-plugin-import) 是一款 `babel` 插件,它会在编译过程中将
|
||||||
|
`import` 的写法自动转换为按需引入的方式
|
||||||
|
|
||||||
|
#### 安装插件
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm i babel-plugin-import -D
|
||||||
|
```
|
||||||
|
|
||||||
|
在`babel.config.js` 设置
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 对于使用 babel7 的用户,可以在 babel.config.js 中配置
|
||||||
|
const plugins = [
|
||||||
|
[
|
||||||
|
'import',
|
||||||
|
{
|
||||||
|
libraryName: 'vant',
|
||||||
|
libraryDirectory: 'es',
|
||||||
|
style: true,
|
||||||
|
},
|
||||||
|
'vant',
|
||||||
|
],
|
||||||
|
]
|
||||||
|
module.exports = {
|
||||||
|
presets: [['@vue/cli-plugin-babel/preset', { useBuiltIns: 'usage', corejs: 3 }]],
|
||||||
|
plugins,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 使用组件
|
||||||
|
|
||||||
|
项目在 `src/plugins/vant.js` 下统一管理组件,用哪个引入哪个,无需在页面里重复引用
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 按需全局引入 vant组件
|
||||||
|
import Vue from 'vue'
|
||||||
|
import { Button, List, Cell, Tabbar, TabbarItem } from 'vant'
|
||||||
|
Vue.use(Button)
|
||||||
|
Vue.use(Cell)
|
||||||
|
Vue.use(List)
|
||||||
|
Vue.use(Tabbar).use(TabbarItem)
|
||||||
|
```
|
45
zh-cn/vuex.md
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
### <span id="vuex">✅ Vuex 状态管理</span>
|
||||||
|
|
||||||
|
目录结构
|
||||||
|
|
||||||
|
```bash
|
||||||
|
├── store
|
||||||
|
│ ├── modules
|
||||||
|
│ │ └── app.js
|
||||||
|
│ ├── index.js
|
||||||
|
│ ├── getters.js
|
||||||
|
```
|
||||||
|
|
||||||
|
`main.js` 引入
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import Vue from 'vue'
|
||||||
|
import App from './App.vue'
|
||||||
|
import store from './store'
|
||||||
|
new Vue({
|
||||||
|
el: '#app',
|
||||||
|
router,
|
||||||
|
store,
|
||||||
|
render: h => h(App),
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
使用
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from 'vuex'
|
||||||
|
export default {
|
||||||
|
computed: {
|
||||||
|
...mapGetters(['userName']),
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
// Action 通过 store.dispatch 方法触发
|
||||||
|
doDispatch() {
|
||||||
|
this.$store.dispatch('setUserName', '真乖,赶紧关注公众号,组织都在等你~')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|