使用 vue-cli 脚手架创建项目
预设: vue2 / node-sass / babel / router / vuex / eslint (Use config files)
在 vue ui 图形界面中启动一次项目
配置 ESLint(可以根据个人的喜好配置)(-- .eslintrc.js)
module.exports = {
root: true, // 当前项目使用这个配置文件, 不会往父级目录找 .eslintrc.js 文件
parserOptions: { // 对新语法使用eslint
parser: 'babel-eslint', // 使用 babel-eslint 来解析新语法 ES6
sourceType: 'module'
},
env: { // 指定 eslint 启动环境
browser: true,
node: true,
es6: true,
},
extends: ['plugin:vue/recommended', 'eslint:recommended'],
// add your custom rules here
//it is base on https://github.com/vuejs/eslint-config-vue
rules: { // 自定义规则
/* value 值:
(1)"off"/0:关闭规则
(2)"warn"/1:将规则视为一个警告(不会影响退出码), 只警告,不会退出程序
(3)"error"/2:将规则视为一个错误 (退出码为1),报错并退出程序
*/
"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-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': 1,
'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,
'object-curly-spacing': [2, 'always', {
objectsInObjects: false
}],
'array-bracket-spacing': [2, 'never'],
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', // 上线环境用打印就报警告, 开发环境关闭此规则
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', // debugger 可以终止代码执行
// 要求使用骆驼拼写法
// 'camelcase': 'none',
// 要求使用 === 和 !==
'eqeqeq': 0,
// 函数和圆括号之间没有空格
'space-before-function-paren': 0,
// 要求或禁止使用拖尾逗号
// "comma-dangle": ["error", {
// "arrays": "always-multiline",
// "objects": "always-multiline",
// "imports": "never",
// "exports": "always",
// "functions": "never"
// }],
// 要求或禁止文件末尾保留一行空行
'eol-last': ['error', 'always'],
// 要求对象字面量属性名称用引号括起来:"consistent" 要求对象字面量属性名称使用一致的引号,要么全部用引号,要么都不用
'quote-props': ['error', 'consistent'],
// 要求注释后要有一个空格
'spaced-comment': ['error', 'always'],
// 要求操作符前后都要有一个空格
'space-infix-ops': 'error',
// 要求圆括号中的前后都要有一个空格
/* "space-in-parens": ["error", "always"], */
// 要求花括号中的前后都要有一个空格
'object-curly-spacing': ['error', 'always'],
// 要求数组中逗号后要使用一个空格
'comma-spacing': ['error', { 'before': false, 'after': true }],
// 自动补充分号
// "semi": [2, "always"],
// 禁止多个空格
'no-multi-spaces': 'error',
// 使用单引号
'quotes': ['error', 'single'],
//在computed properties中禁用异步actions
'vue/no-async-in-computed-properties': 'error',
//不允许重复的keys
'vue/no-dupe-keys': 'error',
//不允许重复的attributes
'vue/no-duplicate-attributes': 'warn',
//在 <template> 标签下不允许解析错误
'vue/no-parsing-error': [
'error',
{
'x-invalid-end-tag': false,
},
],
//不允许覆盖保留关键字
'vue/no-reserved-keys': 'error',
//强制data必须是一个带返回值的函数
// 'vue/no-shared-component-data': 'error',
//不允许在computed properties中出现副作用。
'vue/no-side-effects-in-computed-properties': 'error',
//<template>不允许key属性
'vue/no-template-key': 'warn',
//在 <textarea> 中不允许mustaches
'vue/no-textarea-mustache': 'error',
//不允许在v-for或者范围内的属性出现未使用的变量定义
'vue/no-unused-vars': 'warn',
//<component>标签需要v-bind:is属性
'vue/require-component-is': 'error',
// render 函数必须有一个返回值
'vue/require-render-return': 'error',
//保证 v-bind:key 和 v-for 指令成对出现
'vue/require-v-for-key': 'error',
// 检查默认的prop值是否有效
'vue/require-valid-default-prop': 'error',
// 保证computed属性中有return语句
'vue/return-in-computed-property': 'error',
// 强制校验 template 根节点
'vue/valid-template-root': 'error',
// 强制校验 v-bind 指令
'vue/valid-v-bind': 'error',
// 强制校验 v-cloak 指令
'vue/valid-v-cloak': 'error',
// 强制校验 v-else-if 指令
'vue/valid-v-else-if': 'error',
// 强制校验 v-else 指令
'vue/valid-v-else': 'error',
// 强制校验 v-for 指令
'vue/valid-v-for': 'error',
// 强制校验 v-html 指令
'vue/valid-v-html': 'error',
// 强制校验 v-if 指令
'vue/valid-v-if': 'error',
// 强制校验 v-model 指令
'vue/valid-v-model': 'error',
// 强制校验 v-on 指令
'vue/valid-v-on': 'error',
// 强制校验 v-once 指令
'vue/valid-v-once': 'error',
// 强制校验 v-pre 指令
'vue/valid-v-pre': 'error',
// 强制校验 v-show 指令
'vue/valid-v-show': 'error',
// 强制校验 v-text 指令
'vue/valid-v-text': 'error',
'vue/comment-directive': 0,
// 标签内没有内容时,删除结尾标签
"vue/html-self-closing": ["off", {
"html": {
"void": "never",
"normal": "always",
"component": "always"
},
"svg": "always",
"math": "always"
}]
}
}
项目结构调整:
- 删除 assets/logo.png
- 删除 components 目录里的所有文件
- 删除 router 里的 index.js 里的 routes 数组中的所有路由
- 删除 views 目录中的所有文件
- 删除 App.vue 中的示例代码
创建目录、文件:
node_modules // 存放 第三方包
public // 存放 页面图标、index.html
src // 存放 项目源代码
api 【手动创建】 // 存放 可复用的 api 接口
assets // 存放 静态资源
product 【手动创建】 // 存放 商品图片(大图片)
images 【手动创建】 // 存放 小图标(小图片)(该目录里的图片会被转换为 base64)
components // 存放 组件
router // 存放 路由模块
store // vuex(状态管理)
modules 【手动创建】 // 存储数据模块。让 vuex 中的数据持久化
styles 【手动创建】 // 存放 CSS样式表
utils 【手动创建】 // 存放 工具性质的函数和模块
request.js 【手动创建】 // 请求相关配置
vender 【手动创建】 // 手动添加的第三方 js 库(npm 下载不到的)
views // 存放 通过路由动态切换的组件
storage 【手动创建】 // 数据存储工具箱
App.vue // 项目 根组件
main.js // 项目 入口文件(主要职责:创建 vue 应用)
store.js // vuex 配置文件
theme.scss 【手动创建】 // 主题定制
.browserslistrc // 将 css 转换为浏览器兼容的版本(在 CSS 前面添加浏览器对应的兼容前缀)
.editorconfig // 编辑器配置文件(配置缩进、空格、字体大小 等)
.eslintrc.js // ESLint 配置文件
.gitignore // 忽略清单
babel.config.js // babel 配置文件(ES降级配置文件)
package.json // 包管理配置文件
vue.config.js 【手动创建】 // 项目配置文件
为空目录添加 .gitkeep(解决远程仓库不能上传空目录的问题)
初始化样式
1、定义公共代码(-- style/新建 index.scss)
body {
height: 100%;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
font-family: Helvetica Neue, Helvetica, Arial, Microsoft Yahei, Hiragino Sans GB, Heiti SC, WenQuanYi Micro Hei, sans-serif;
font-weight: bold;
}
label {
font-weight: 700;
}
html {
height: 100%;
box-sizing: border-box;
}
#app {
height: 100%;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
a:focus,
a:active {
outline: none;
}
a,
a:focus,
a:hover {
cursor: pointer;
color: inherit;
text-decoration: none;
}
div:focus {
outline: none;
}
.clearfix {
&:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
}
// main-container global css
.app-container {
padding: 20px;
}
input,
button {
border: 0;
font-weight: bold;
}
button {
cursor: pointer;
}
input {
outline: none;
}
li {
list-style: none;
}
.w {
width: 1300px;
margin: 0 auto;
}
ul,
li,
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0;
padding: 0;
}
img {
vertical-align: bottom;
}
2、导入公共代码(-- main.js)
import '@/styles/index.scss'
定义 Demo 路由【可选】(个人喜好,用于测试代码)
下载插件
1、规范化 css 代码 2、axios 数据请求的库 3、element-ui 组件库 4、vuex 数据持久化 5、cookie 库
npm i normalize.css@8.0.1 axios@0.25.0 element-ui@2.15.6 vuex-persistedstate@4.1.0 js-cookie@2.2.0
导入 规范化 css 代码(-- main.js)
import 'normalize.css/normalize.css'
配置 element-ui
1、完整引入 element ui 组件(-- main.js)
import ElementUI from 'element-ui'
2、导入 element-ui 组件的样式(-- 同上)
import 'element-ui/lib/theme-chalk/index.css'
3、把 Element 注册为 vue 的插件(-- 同上)
Vue.use(ElementUI)
配置 vuex-persistedstate 数据持久化
1、配置 vuex-persistedstate(重构)(-- store/index.js)
import Vue from 'vue'
import Vuex from 'vuex'
import createPersistedState from 'vuex-persistedstate'
import demo from '@/store/modules/demo.js'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: { // 注册模块
demo
},
plugins: [
createPersistedState({ // 数据持久化插件 配置
key: 'store', // 本地仓库名字
paths: ['demo'] // 指定需要持久化的模块
})
]
})
export default store
2、模块骨架示例(-- store/modules/新建 demo.js)
import { getUsernameLogin } from '@/api/user-management.js'
const state = { // 公共数据
token: ''
}
const mutations = { // 修改 store 中的数据
setToken(state, token) {
state.token = token
}
}
const actions = { // 处理异步任务
async getUsernameLogin(context, data) { // 用户登录[用户名]
const result = await getUsernameLogin(data)
context.commit('setToken', result)
}
}
const modules = { // 计算属性。监听 state 数据
}
export default {
namespaced: true,
state,
mutations,
actions,
modules
}
配置 axios(-- utils/request.js)
import axios from 'axios'
import { Message } from 'element-ui'
// 创建 axios 实例
const service = axios.create({
baseURL: '/api', // 请求根路径
timeout: 8000 // 请求超时时间
})
// 请求 拦截器
service.interceptors.request.use(request => {
return request
}, error => {
return Promise.reject(error)
})
// 响应 拦截器
service.interceptors.response.use(response => {
// 返回响应结果
if (response.data.msg === 'success') {
return response.data.data
} else {
Message.error(response.data.msg)
// 把异步API执行失败的结果传递出去
return Promise.reject(response.data.msg)
}
}, error => {
return Promise.reject(error)
})
export default service
【可选】解决请求参数格式和服务端参数格式不一致的问题(-- main.jx)
解决方法: 将请求参数转换成字符串格式。因为 content-type 会根据请求参数的格式自动变换请求格式
- application/x-www-form-urlencoded 格式:categoryId=263919
- application/json 格式:{"categoryId": 263919}
1、下载 qs 包
作用: 将 application/json 格式转换为 application/x-www-form-urlencoded 格式
npm i qs@6.10.3
2、导入 qs 包(-- utils/request.js)
import qs from 'qs'
2、将 application/json 格式转换为 application/x-www-form-urlencoded 格式
// 请求 拦截器
axios.interceptors.request.use(request => {
-- 增
if (request.data) {
request.data = qs.stringify(request.data) // 将 application/json 格式转换为 application/x-www-form-urlencoded 格式
}
--
return request
})
配置 cookie 库
1、导入以下代码(-- utils/新建 auth.js)
import Cookies from 'js-cookie'
const TokenKey = 'vue_admin_template_token'
export function getToken() {
return Cookies.get(TokenKey)
}
export function setToken(token) {
return Cookies.set(TokenKey, token)
}
export function removeToken() {
return Cookies.remove(TokenKey)
}
2、按需导入(-- store/modules/新建 user.js)
import { getToken, setToken, removeToken, setTimeStamp } from '@/utils/auth'
配置 开发 环境下的接口代理(-- vue.config.js)
module.exports = {
devServer: { // 开发环境下的服务器配置
host: 'localhost',
port: 8080,
// 代理配置
proxy:{
// 拦截携带 /api 的接口
'/api': {
// 代理到的目标地址
target: 'https://api.it120.cc/zcr',
// 是否开启跨域
changeOrigin: true,
// 路径重写
pathRewrite:{
// 将 /api 转换为 空
'^/api': ''
}
}
}
},
configureWebpack: {
devtool: 'source-map'
},
}