路由是否使用history模式?选择 n
如果项目中存在要求就使用history(即:y),但是一般还是推荐大家使用hash模式,毕竟history模式需要依赖运维
选择一种CSS预处理类型,这个需要根据各个项目的要求使用那种css编译处理
是否保存当前选择的配置项
如果当前配置是经常用到的配置,建议选择y存储一下当前配置项。如果只是临时使用的话就不需要存储了
router / index.js
import { createRouter, createWebHashHistory } from 'vue-router'
// 路由规则
const routes = [
]
// 创建路由实例
const router = createRouter({
// 使用hash路由模式
history: createWebHashHistory(),
routes
})
export default router
store / index.js
import { createStore } from 'vuex'
// 创建仓库
export default createStore({
state: {
},
getters: {
},
mutations: {
},
actions: {
},
modules: {
}
})
. main.js
// 不再引入Vue构造函数,引入的是名为createApp的工厂函数,无需通过new调用
import { createApp } from 'vue'
// 引入根组件
import App from './App.vue'
// 引入路由实例
import router from './router'
// 引入仓库(vuex)实例
import store from './store'
// createApp(App):创建应用实例对象——app(类似于vue2中的vm,但app比vm更“轻”,即app没有vm那么多的方法属性等)
const app = createApp(App)
// 挂载
app.mount('#app')
src / jsconfig.json
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": [
"src/*"
]
}
},
"exclude": [
"node_modules",
"dist"
]
}
src / .eslintignore
/dist
/src/vender
通过 ESLint
+ Prettier
+ VSCode 配置
配合进行了处理。最终达到在保存代码时,自动规范化代码格式的目的
.prettierrc
注意:复制过去时,不能加注释!!!!
{
// 不尾随分号
"semi": false,
// 使用单引号
"singleQuote": true,
// 多行逗号分割的语法中,最后一行不加逗号
"trailingComma": "none"
}
.eslintrc.js
在 rules 下新增 'space-before-function-paren': 'off'
约定式提交
当使用 commitizen 进行代码提交(git commit)时,commitizen 会提交你在提交时填写所有必需的提交字段!
cnpm install -g commitizen
通过 cz-customizable 自定义提交规范
cnpm i --save-dev cz-customizable
"config": {
"commitizen": {
"path": "node_modules/cz-customizable"
}
}
.cz-config.js
自定义提示文件module.exports = {
// 可选类型
types: [
{ value: 'feat', name: 'feat: 新功能' },
{ value: 'fix', name: 'fix: 修复' },
{ value: 'docs', name: 'docs: 文档变更' },
{ value: 'style', name: 'style: 代码格式(不影响代码运行的变动)' },
{
value: 'refactor',
name: 'refactor: 重构(既不是增加feature,也不是修复bug)'
},
{ value: 'perf', name: 'perf: 性能优化' },
{ value: 'test', name: 'test: 增加测试' },
{ value: 'chore', name: 'chore: 构建过程或辅助工具的变动' },
{ value: 'revert', name: 'revert: 回退' },
{ value: 'build', name: 'build: 打包' }
],
// 消息步骤
messages: {
type: '请选择提交类型:',
customScope: '请输入修改范围(可选):',
subject: '请简要描述提交(必填):',
body: '请输入详细描述(可选):',
footer: '请输入要关闭的issue(可选):',
confirmCommit: '确认使用以上信息提交?(y/n/e/h)'
},
// 跳过问题
skipQuestions: ['body', 'footer'],
// subject文字长度默认是72
subjectLimit: 72
}
git cz
代替 git commit,即可看到提示内容用到的两个hooks
commit-msg
- 用来规范化标准格式
- 由 git commit 和 git merge 调用
- 可以按需指定是否要拒绝本次提交,使用 git commit --no-verify 绕过
pre-commit
- 会在提交前被调用,由
git commit
调用- 可以按需指定是否要拒绝本次提交,使用 git commit --no-verify 绕过
commitlint:用于检查提交信息
husky:是git hooks
工具
注意:npm
需要在 7.x 以上版本!!!!!
cnpm install --save-dev @commitlint/config-conventional @commitlint/cli
commitlint.config.js
文件module.exports = {
// 继承的规则
extends: ['@commitlint/config-conventional'],
// 定义规则类型
rules: {
// type 类型定义,表示 git 提交的 type 必须在以下类型范围内
'type-enum': [
// 当前验证的错误级别
2,
// 在什么情况下验证
'always',
// 泛型内容
[
'feat', // 新功能 feature
'fix', // 修复 bug
'docs', // 文档注释
'style', // 代码格式(不影响代码运行的变动)
'refactor', // 重构(既不增加新功能,也不是修复bug)
'perf', // 性能优化
'test', // 增加测试
'chore', // 构建过程或辅助工具的变动
'revert', // 回退
'build' // 打包
]
],
// subject 大小写不做校验
'subject-case': [0]
}
}
注意:确保保存为 UTF-8
的编码格式,否则可能报错
cnpm install --save-dev husky
hooks
, 生成 .husky
文件夹npx husky install
package.json
中生成 prepare
指令( 需要 npm > 7.0 版本 )npm set-script prepare "husky install"
prepare
指令npm run prepare
添加 commitlint
的 hook
到 husky
中,并指定在 commit-msg
的 hooks
下执行 npx --no-install commitlint --edit "$1"
指令
npx husky add .husky/commit-msg ‘npx --no-install commitlint --edit “$1”’
至此, 不符合规范的 commit 将不再可提交
强制规范化的提交要求,到现在 不符合规范的提交信息,将不可在被提交!
通过
husky
监测pre-commit
钩子,在该钩子下执行npx eslint --ext .js,.vue src
指令来去进行相关检测:
npx husky add .husky/pre-commit "npx eslint --ext .js,.vue src"
添加 commit
时的 hook
(npx eslint --ext .js,.vue src
会在执行到该 hook 时运行)npx husky add .husky/pre-commit "npx eslint --ext .js,.vue src"
lint-staged 可以让你当前的代码检查 只检查本次修改更新的代码,并在出现错误的时候,自动修复并且推送
lint-staged 无需单独安装,我们生成项目时,vue-cli
已经帮助我们安装过了,所以我们直接使用就可以了
修改 package.json
配置
"lint-staged": {
"src/**/*.{js,vue}": [
"eslint --fix",
"git add"
]
}
如上配置,每次它只会在你本地 commit
之前,校验你提交的内容是否符合你本地配置的 eslint
规则(这个见文档 ESLint ),校验会出现两种结果:
eslint --fix
尝试帮你自动修复
修改 .husky/pre-commit
文件
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged
再次执行提交代码
发现 暂存区中 不符合 ESlint
的内容,被自动修复
对于 git
提交规范 而言我们使用了 husky
来监测 Git hooks
钩子,并且通过以下插件完成了对应的配置:
pre-commit
: git hooks
钩子通过namespaced:true
开启命名空间
所有功能区分模块,更高封装度和复用
使用时要写模块名
// 直接读取
$store.state.模块名.要读取的state数据
// 直接读取
$store.getters['模块名/要读取的getters']
// 直接dispatch
$store.dispatch('模块名/actions方法名',数据)
// 直接commit
$store.commit('模块名/xxx',数据)
src / store / modules
下新建 cart.js 、user.js 、 category.js
user.js:用户模块
/** 用户模块 */
// 子模块state建议写成函数
const state = () => {
return {
// 用户信息
profile: {
id: '',
avatar: '',
nickname: '',
account: '',
mobile: '',
token: ''
}
}
}
const mutations = {
// 修改用户信息,payload代表要修改的用户信息对象
SETUSER (state, payload) {
state.profile = payload
}
}
const actions = {}
const getters = {}
export default {
namespaced: true, // 开启命名空间
state,
mutations,
actions,
getters
}
cart.js:购物车模块
/** 购物车模块 */
const state = () => {
return {
// 购物车商品列表
list: []
}
}
const mutations = {}
const actions = {}
const getters = {}
export default {
namespaced: true, // 开启命名空间
state,
mutations,
actions,
getters
}
category.js :分类模块,管理所有分类
/** 分类模块 */
const state = () => {
return {
// 分类信息
categoryList: []
}
}
const mutations = {}
const actions = {}
const getters = {}
export default {
namespaced: true, // 开启命名空间
state,
mutations,
actions,
getters
}
store / index.js中引入
import { createStore } from 'vuex'
// 导入模块
import user from './modules/user'
import cart from './modules/cart'
import category from './modules/category'
// 创建仓库实例
export default createStore({
modules: {
user,
cart,
category
}
})
cnpm i vuex-persistedstate --save
import { createStore } from 'vuex'
// 引入持久化插件
import createPersistedstate from 'vuex-persistedstate'
// 导入模块
import user from './modules/user'
import cart from './modules/cart'
import category from './modules/category'
// 创建仓库实例
export default createStore({
modules: {
user,
cart,
category
},
// 配置插件
plugins: [
// 默认存储在localStorage
createPersistedstate({
// 本地存储的名字
key: 'rabbit-client-pc-store',
// 指定需要存储的模块
paths: ['user', 'cart']
})
]
})
注意:
默认是存储在localStorage
中
key是存储数据的键名
paths是存储state中的那些数据,如果是模块下具体的数据需要加上模块名称,如user.token
修改state后触发才可以看到本地存储数据的的变化
目的:基于axios封装一个请求工具,调用接口时使用。封装请求拦截器、响应拦截器,进行一些业务处理
在后续需要调用接口时,要引入该工具
cnpm install --save axios
/** *****************axios的二次封装,主要是封装请求拦截器和响应拦截器 ************/
/** 实现步骤
* 1. 创建一个新的axios实例
* 2. 请求拦截器,如果有token进行头部携带
* 3. 响应拦截器,获取响应数据,拦截失效token
* 4. 定义函数,使用配置好的axios发送请求
**/
// 导入axios
import axios from 'axios'
// 导入store实例
import store from '@/store'
// 导入路由
import router from '@/router'
// 导出baseURL:一些不是axios发送的请求也会用到baseURL
export const baseURL = 'http://pcapi-xiaotuxian-front-devtest.itheima.net/'
/**
* TODO-1: 通过 axios.create() 创建axios实例
*/
const instance = axios.create({
// axios 的配置
// 基础路径,`baseURL` 将自动加在 `url` 前面
baseURL,
// 超时时间,指定请求超时的毫秒数(0 表示无超时时间)
// 如果请求话费了超过 `timeout` 的时间,请求将被中断
timeout: 5000
})
/**
* TODO-2:请求拦截器
*/
instance.interceptors.request.use(config => {
// 发送请求前要做的事情
// config:配置对象,其中含请求头header属性
const { profile } = store.state.user
// 如果存在token,就在请求头携带
if (profile.token) {
config.headers.token = `Bearer ${profile.token}`
}
// 最后必须返回config配置对象
return config
}, error => {
// 请求失败要做的事情
return Promise.reject(new Error(error))
})
/**
* TODO-3:响应拦截器
*/
instance.interceptors.response.use(response => {
// 响应成功,直接返回data数据
return response.data
}, error => {
// 响应失败
// 401 未登录状态
if (error.response && error.response.status === 401) {
// 清空本地无效信息:通过提交修改用户信息的mutation,并且传递一个空对象实现
store.commit('user/SETUSER', {})
/** 跳转到登录,且传递当前路由地址给登录页面 */
// js模块中获取当前路由信息地址:router.currentRoute.value.fullPath
// encodeURIComponent 转换uri编码,防止解析地址出问题
const fullPath = encodeURIComponent(router.currentRoute.value.fullPath)
router.push(`/login?redirectUrl=${fullPath}`)
}
return Promise.reject(new Error(error))
})
/**
* TODO-4:封装请求函数
* 负责发请求,传递的参数:请求地址、请求方式、提交的数据
*/
export default (url, method, submitData) => {
// instance 负责发请求,得到的是Promise对象,所以最后直接返回instance
return instance({
url,
method,
// 如果是get请求,需要使用params传参
// 如果不是get请求,需要使用data进行传参
[method.toLowerCase() === 'get' ? 'params' : 'data']: submitData
})
}
路径 | 组件(功能) | 嵌套级别 |
---|---|---|
/ | 首页布局容器 | 1级 |
/ | 首页 | 2级 |
/category/:id | 一级分类 | 2级 |
/category/sub/:id | 二级分类 | 2级 |
/product/:id | 商品详情 | 2级 |
/login | 登录 | 1级 |
/login/callback | 第三方登录回调 | 1级 |
/cart | 购物车 | 2级 |
/member/checkout | 填写订单 | 2级 |
/member/pay | 进行支付 | 2级 |
/member/pay/result | 支付结果 | 2级 |
/member | 个人中心布局容器 | 2级 |
/member | 个人中心 | 3级 |
/member/order | 订单管理 | 3级 |
/member/order/:id | 订单详情 | 3级 |
<template>
<router-view>router-view>
template>
src / views / Layout.vue
<template>
<div>顶部通栏div>
<div>顶部组件div>
<main class="main-box">
<router-view>router-view>
main>
<footer>底部组件footer>
template>
<script setup>
import {} from 'vue'
script>
<style lang="scss" scoped>style>
src /views / home / index.vue
<template>
<div>home首页div>
template>
<script setup>
import {} from 'vue'
script>
<style lang="scss" scoped>style>
import { createRouter, createWebHashHistory } from 'vue-router'
const routes = [
// 一级路由布局容器
{
path: '/',
component: () => import('@/views/Layout.vue'), // 路由懒加载
children: [
// 二级路由
{
path: '/',
component: () => import('@/views/home/index.vue')
}
]
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
src / assets / styles / variables.scss
// 主题
$xtxColor:#27BA9B;
// 辅助
$helpColor:#E26237;
// 成功
$sucColor:#1DC779;
// 警告
$warnColor:#FFB302;
// 价格
$priceColor:#CF4444;
src/assets/styles/mixins.scss
// 鼠标经过上移阴影动画
@mixin hoverShadow {
transition: all .5s;
&:hover {
transform: translate3d(0,-3px,0);
box-shadow: 0 3px 8px rgba(0,0,0,0.2);
}
}
<style lang="scss" scoped>
@import '~@/assets/styles/mixins.scss';
.box {
@include hoverShadow;
}
style>
解决方法: 使用 vue-cli 的 style-resoures-loader
插件来完成自动注入到每个scss文件或者vue组件中style标签中
在当前项目下执行命令 ,安装插件
cnpm i sass-resources-loader --save-dev
配置 vue.config.js
,配置完成后重启项目
const path = require('path')
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
pluginOptions: {
'style-resources-loader': {
preProcessor: 'scss',
// 需要自动引入的文件,要使用绝对路径
// 通过path.join()拼接完整路径
patterns: [
path.join(__dirname, './src/assets/styles/mixins.scss'),
path.join(__dirname, './src/assets/styles/variables.scss')
]
}
}
})
使用
<style lang='scss' scoped>
.home-container {
color: $themeColor;
@include hoverShadow
}
style>
准备网站所需的重置样式代码,以及一些公用样式代码
cnpm i --save normalize.css
安装重置样式的包,然后在 main.js
导入 normalize.css 即可// 重置样式
import 'normalize.css'
src/assets/styles/common.scss
在该文件写入常用的样式,然后在 main.js
导入即可。// common.scss
// 重置样式
* {
box-sizing: border-box;
}
html {
height: 100%;
font-size: 14px;
}
body {
height: 100%;
color: #333;
min-width: 1240px;
font: 1em/1.4 'Microsoft Yahei', 'PingFang SC', 'Avenir', 'Segoe UI', 'Hiragino Sans GB', 'STHeiti', 'Microsoft Sans Serif', 'WenQuanYi Micro Hei', sans-serif
}
ul,
h1,
h3,
h4,
p,
dl,
dd {
padding: 0;
margin: 0;
}
a {
text-decoration: none;
color: #333;
outline: none;
}
i {
font-style: normal;
}
input[type="text"],
input[type="search"],
input[type="password"],
input[type="checkbox"]{
padding: 0;
outline: none;
border: none;
-webkit-appearance: none;
&::placeholder{
color: #ccc;
}
}
img {
max-width: 100%;
max-height: 100%;
vertical-align: middle;
}
ul {
list-style: none;
}
#app {
background: #f5f5f5;
user-select: none;
}
.container {
width: 1240px;
margin: 0 auto;
position: relative;
}
.ellipsis {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.ellipsis-2 {
word-break: break-all;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
.fl {
float: left;
}
.fr {
float: right;
}
.clearfix:after {
content: ".";
display: block;
visibility: hidden;
height: 0;
line-height: 0;
clear: both;
}
//main.js
import '@/assets/styles/common.scss'
cnpm install --save nprogress
request.js
配置// 引入进度条
import nprogress from 'nprogress'
// 引入进度条样式
import 'nprogress/nprogress.css'
// 请求拦截器中
// 进度条开始
nprogress.start();
// 响应拦截器中
// 进度条结束
nprogress.done();