项目描述
移动端项目,除登陆注册等少数页面外,大多数页面有公共的头部,部门页面有公共底部项目。 去年下半年,vue刚刚推出,可选UI框架都比较少。项目组多人开发,项目并非本人搭建。昨天刚好开启一个新的项目,按照各种教程完成基础的搭建,很少找到一次性说完项目搭建方法的教程。搭建过程中可能还有错的地方,写出搭建过程,一来可以留份文档,以后搭建可以使用,二来可以请大家及时指出错误的地方,共同学习。
技术栈
vue3 + vite + js + Vant + axios + pinia + scss + rem
注: 选择ts的技术栈大致相同,部分项目配置函数和函数调用略有不同,请参考网络博客教程(后续有时间会补上)。
项目搭建步骤
一、项目创建
1.vite安装
npm install -g vite
安装结束,查看vite的版本号正常,即安装完成。
2.项目初始化
npm init vite@latest my-vue-app -- --template vue
cd my-vue-app
npm install
npm run dev
安装选择js,完成以上四步安装后能够正常启动即完成项目初始化。
3.添加路由
npm install vue-router@4
1.添加完路由依赖后,在src文件夹里面创建一个router文件夹,并在router文件夹里面创建一个index.js文件。
// src/router/index.js 文件内容:
import { createRouter, createWebHashHistory } from 'vue-router'
export default createRouter({
history:createWebHashHistory(),
routes:[{
path: "/",
name: "Index",
redirect: "/Home",
component: ()=>import('@/components/Index.vue'),
children: [{
path:'/Home',
name:"Home",
component:()=>import('@/views/Home/index.vue')
},{
path:'/User',
name:"User",
component:()=>import('@/views/User/index.vue')
}]
}]
})
注: 路由中的.vue文件路径和实际文件路径要一致。
2.在main.js中添加路由
// main.js 文件内容:
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router/index'
const app = createApp(App)
app.use(router)
app.mount('#app')
3.app.vue
// app.vue 内容:
<template>
<router-view v-slot="{ Component }">
<transition name="el-fade-in" mode="out-in">
<component :is="Component" />
</transition>
</router-view>
</template>
-
src/components/Index.vue,加载一级路由,然后分发二级路由,实现公共头部。
注: 使用slot插槽也可以实现此功能,并非只有一种方式。
// src/components/Index.vue 文件内容:
<template>
<div class="project_container">
<div class="header_container">
<Header/>
</div>
<router-view></router-view>
</div>
</template>
<script>
import { reactive, toRefs } from "vue";
import Header from "@/components/Header.vue";
export default {
components:{Header},
setup() {
const state = reactive({});
return {
...toRefs(state),
};
},
};
</script>
<style lang="scss" scoped>
.project_container{
padding-top: 50px;
background-color: #f3f4f5;
}
</style>
4.安装vant
可以通过vant官网教程安装。
npm i vant
// Vue 3 项目,安装最新版 Vant
1.全局引用,(一般很少局部引用,基本都是全局引用)main.js中配置
// main.js 文件内容:
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router/index'
import Vant from 'vant' // 引用组件
import 'vant/lib/index.css' // 引用样式
const app = createApp(App)
app.use(router)
app.use(Vant) // 使用
app.mount('#app')
注: vite.config.js中不需要安装官网教程配置,vite已自动配置支持,如后续测试安装失败,则再按照官网教程配置。
完整的vite.config.js:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { loadEnv } from './src/assets/js/vite'
const viteEnv = loadEnv()
const { VITE_PORT } = viteEnv
import path from 'path'
export default defineConfig({
plugins: [vue()],
server: {
port: VITE_PORT
},
resolve: {
extensions: ['.vue', '.js', '.css'],
alias: {
'@': path.resolve(__dirname, './src')
},
},
build: {
outDir: 'dist',
assetsDir: "assets", //指定静态资源存放路径
}
})
2.在组件中使用vant,通过测试按钮是否显示来判断vant安装配置是否成功。
<template>
<van-button type="primary" />
</template>
5.安装pinia
pinia是vuex的升级版本,比vuex简单好用。此处只写简单存储取值应用,复杂情况请参考官网文档。
npm install pinia -S
1.在mian.js中引用
// main.js 文件内容:
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router/index'
import Vant from 'vant'
import 'vant/lib/index.css'
import { createPinia } from 'pinia' // 引用组件
const app = createApp(App)
app.use(router)
app.use(createPinia()) // 使用
app.use(Vant)
app.mount('#app')
2.在src文件夹下面创建store文件夹,在store文件夹下创建index.js文件
import { defineStore } from "pinia"
export const userStore = defineStore({
id: "mooc", // id是唯一的,如果有多个文件,ID不能重复
state: () => {
return {
customList: [{
id:1,
name:'马云',
phone:'13856784223',
img:'https://img2.baidu.com/it/u=4244269751,4000533845&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500',
sex:'男',
creatTime:'2022-09-18',
remark:"我不喜欢钱。"
},{
id:2,
name:'王健林',
phone:'13889657458',
img:'https://img1.baidu.com/it/u=1016138010,1907110459&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500',
sex:'男',
creatTime:'2022-09-18',
remark:"先赚他个小目标!"
},{
id:3,
name:'董明珠',
phone:'13885236548',
img:'https://img2.baidu.com/it/u=3062813899,1142128231&fm=253&fmt=auto&app=138&f=JPEG?w=479&h=500',
sex:'女',
creatTime:'2022-09-18',
remark:"我进来你们为什么不鼓掌?"
},{
id:4,
name:'马化腾',
phone:'13885468735',
img:'https://img2.baidu.com/it/u=4244269751,4000533845&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500',
sex:'男',
creatTime:'2022-09-18',
remark:"对不起,您的贞操碎了,一百万年后,您的贞操碎片将再次集齐!"
}],
}
},
actions: {
setInfo(data) {
this.customList = data
},
// 用户退出,清除本地数据
logout() {
this.customList = null
sessionStorage.clear()
localStorage.clear()
},
},
// 开启数据缓存,在 strategies 里自定义 key 值,并将存放位置由 sessionStorage 改为 localStorage
// 默认所有 state 都会进行缓存,你可以通过 paths 指定要持久化的字段,其他的则不会进行持久化,如:paths: ['userinfo'] 替换key的位置
persist: {
enabled: true,
strategies: [
{
key: "moocs",
storage: localStorage,
},
],
},
})
3.在组件中使用(测试一下是否能够正常读取数据)
// 引用
import { userStore } from "@/store/index";
// 使用
const store = userStore();
const customList = store.customList;// 即为存储数据,(此时为默认数据)
// 修改
const list = [1,2,3,4,5,6]
store.setInfo(list);
注: 使用方法有多种,请参考官网文档。此处仅为演示简单的使用。
6.添加rem配置
注: 实际项目本身自带rem,但是自带rem配置不好修改符合个人习惯的比例。介绍几种方法(方法很多,选择其中一个即可): 1.在index.html中直接添加
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="http://www.guoketest.com/uploads/20220818/0f63a4395ca47752ebbeccda90b0f5ce.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>vue3项目演示</title>
<script>
function htmlFontSize(){
var docEl = document.documentElement,
clientWidth = window.innerWidth || docEl.clientWidth || document.body.clientWidth;
if(!clientWidth) return;
docEl.style.fontSize = 100 * (clientWidth/1920) + 'px';
if(!document.addEventListener) return;
}
htmlFontSize();
</script>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
2.在assets/js路径的文件夹中创建rem.js文件
const baseSize = 36
// 设置 rem 函数
function setRem () {
// 当前页面宽度相对于 1920宽的缩放比例,可根据自己需要修改。
const scale = document.documentElement.clientWidth / 1500
// 设置页面根节点字体大小(“Math.min(scale, 2)” 指最高放大比例为2,可根据实际业务需求调整)
document.documentElement.style.fontSize = baseSize * Math.min(scale, 1) + 'px'
}
// 初始化
setRem()
// 改变窗口大小时重新设置 rem
window.onresize = function () {
setRem()
}
// 或者:
// 获取html元素
var html = document.documentElement;
//获取屏幕的宽度
var screenWidth = html.clientWidth;
var timer = null;
// 初始的设计图的大小
var uiWidth = 320;
// 初始的font-size的大小
var fonts = 16;
// 获取初始的比例
var bili = uiWidth/fonts;
// 根据当前屏幕大小动态去计算这个屏幕所对应font-size值
html.style.fontSize = screenWidth/bili + 'px';
// 上来的时候先执行一次
getSize();
window.onresize = getSize;
function getSize(){
clearTimeout(timer);
timer = setTimeout(function(){
// 重新得到屏幕的宽度
screenWidth = html.clientWidth;
// 针对屏幕宽度做限定
if(screenWidth <= 320){
html.style.fontSize = 320/bili + 'px';
}
else if(screenWidth >= 640){
html.style.fontSize = 640/bili + 'px';
}
else{
// 根据当前屏幕大小动态去计算这个屏幕所对应font-size值
html.style.fontSize = screenWidth/bili + 'px';
}
}, 100);
}
// 改变窗口大小时重新设置 rem
window.onresize = function () {
getSize()
}
// 千万注意:不要添加入口函数
// 同时引用的时候放到最前面
在main.js中配置引用
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router/index'
import Vant from 'vant'
import 'vant/lib/index.css'
import './assets/js/rem' // 引用样式
import { createPinia } from 'pinia'
const app = createApp(App)
app.use(router)
app.use(createPinia())
app.use(Vant)
app.mount('#app')
7.安装axios
npm install axios --save
1.在src目录下新建一个request文件夹,在里面新建index.js文件
import axios from 'axios'
// 创建一个 axios 实例
const service = axios.create({
baseURL: '/api', // 所有的请求地址前缀部分
timeout: 60000, // 请求超时时间毫秒
withCredentials: true, // 异步请求携带cookie
headers: {
// 设置后端需要的传参类型
'Content-Type': 'application/json',
'token': 'your token',
'X-Requested-With': 'XMLHttpRequest',
},
})
// 添加请求拦截器
service.interceptors.request.use(
function (config) {
// 在发送请求之前处理
return config
},
function (error) {
// 对请求错误处理
console.log(error)
return Promise.reject(error)
}
)
// 添加响应拦截器
service.interceptors.response.use(
function (response) {
console.log(response)
// 2xx 范围内的状态码都会触发该函数。
// 对响应数据做处理
// dataAxios 是 axios 返回数据中的 data
const dataAxios = response.data
// 后端给出的状态码
const code = dataAxios.reset
return dataAxios
},
function (error) {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做处理
console.log(error)
return Promise.reject(error)
}
)
export default service
2.在src目录下新建一个api文件夹,在api下面创建user.js文件
// 导入axios实例
import httpRequest from '@/request/index'
// 获取用户信息
export function getUserInfo(param) {
return httpRequest({
url: 'your api url',
method: 'post',
data: param,
})
}
3.具体使用
import { reactive, toRefs } from "vue";
import { getUserInfo } from '@/api/user'
export default {
setup() {
const state = reactive({});
getUserInfo({id:******,userName:******})
.then((res) => {
console.log(res)
})
return {
...toRefs(state),
};
},
};
若是没算错的话,此时你应该有个跨域错误。这个跨域与之前项目处理一样,实在不会处理的话就甩给后端处理。
二、项目打包
npm run build
打包后得到一个文件夹,文件夹里包含一个index.html,以及css和js各一个文件。将文件夹内容放到nginx服务器中,即完成上传。
建议使用Jenkins自动打包发布工具,可视化,一键完成项目打包、打包文件上传、重启前端服务器等全部工作。
三、开发注意事项
js写法
vue3 js有2中写法,如果使用ts的话,建议使用第二种。
第一种写法是vue3.2版本推出,在引入组件时会有缺少export问题,在ts的组件引用会抛错。
第二种写法是vue3.1版本推出,实践中未遇到问题。
<!-- 第一种写法: -->
<script setup>
import { reactive, toRefs } from "vue";
const state = reactive({
token: ''
});
const { token } = toRefs(state);
</script>
<!-- 此方法在部分情景引用状态会报错(已遇到坑,修改使用方法即解决),大部分场景无问题,使用时需要注意。 -->
// 第二种写法:
import { reactive, toRefs } from "vue";
export default {
setup() {
const state = reactive({});
return {
...toRefs(state)
};
},
};
// 此方法中规中矩,适合所有场景。
//部分骚操作需要多写几行代码才能实现(具体问题具体对待,遇到问题自然就知道怎么写了)。
函数定义
函数定义:setup中使用少使用箭头函数,减少不必要的麻烦,如:先定义再调用等。
setup() {
const state = reactive({
id:null,
status:null
});
changeId(1) // 可以调用
changeStatus(1) // 不可以调用
//推荐使用
function changeId(id){
state.id = id
}
//不推荐使用
const changeStatus = status => {
state.status = status
}
changeId(2) // 可以调用
changeStatus(2) // 可以调用
return {
...toRefs(state),
changeStatus,
changeId
};
}
组件的引用
组件的引用写法:建议使用大驼峰命名法。
import EditDialog from "./components/editDialog.vue";
<!-- 推荐使用 -->
<EditDialog />
<!-- 大驼峰比短横线写法更方便定位到使用地方,更方便多人开发维护 -->
<!-- 不推荐使用 -->
<edit-dialog />
<!-- 此方法不容易搜索,对不是自己开发的代码维护不友好。 -->
以上建议可以不用遵守,纯属于个人代码习惯。
有谬误地方,请各位大佬斧正。