本項目搭建適用于h5移動端的vue項目搭建,主體是基于vue-cli3腳手架黔酥,目的在于搭建個可用于快速啟動項目的基礎框架跪者。話不多說,馬上動手搭建:
技術桟:vue3逗概,pinia柜蜈,vant,vite隶垮,axios
注意vue3對node有要求
1.新建項目
npm init vue@latest
一些配置選項
項目名不要帶vant秘噪,圖寫多了
安裝less
npm i less less-loader -D
修改一下vite配置
引入const path = require('path');
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
const path = require('path');
// https://vitejs.dev/config/
// export default defineConfig({
// plugins: [vue(), vueJsx()],
// resolve: {
// alias: {
// '@': fileURLToPath(new URL('./src', import.meta.url))
// }
// }
// })
export default ({ mode }) => {
// https://vitejs.dev/config/
return defineConfig({
plugins: [vue(), vueJsx()],
resolve: {
alias: [
{
find: '@',
replacement: path.resolve(__dirname, 'src'),
},
],
// 支持vue后綴名
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
},
css: {
preprocessorOptions: {
less: {
javascriptEnabled: true,
additionalData: `@import "${path.resolve(__dirname, 'src/assets/config.less')}";`, // 全局引入less配置
},
}
},
});
}
添加公共less文件config.less或者直接改了base.css也行
// config.less
@primary-color: #1890ff; // 主色
// main.less
#app {
width: 100%;
min-height: 100%;
min-width: 320px;
max-width: 750px;
margin: 0 auto;
color: #333;
background: #f2f2f2;
}
@media screen and (min-width: 750px) {
html {
font-size: 352px !important;
/*no*/
}
}
main.css改main.less 其他無關樣式可以刪了 蹋偏,main.js記得也改后綴
@import './config.less';
安裝vant
npm i vant -S
// main.js
// vant樣式
// Toast
import 'vant/es/toast/style';
// Dialog
import 'vant/es/dialog/style';
// Notify
import 'vant/es/notify/style';
// ImagePreview
import 'vant/es/image-preview/style';
// 錯誤監(jiān)控
app.config.errorHandler = (err, vm) => {
let { message, name, stack } = err;
if (message || name) {
// errReport({
// target: vm?.$options?.__name,
// msg: stack ? stack.toString() : `${name}:${message}`,
// user: ''
// });
}
console.error(err);
};
安裝 unplugin-vue-components
可自動引入src/components的組件威始,以及vant組件
npm i unplugin-vue-components -D
// vite.config.js
import Components from 'unplugin-vue-components/vite';
import { VantResolver } from 'unplugin-vue-components/resolvers';
...
plugins: [
vue(),
vueJsx(),
Components({
resolvers: [VantResolver()],
extensions: ['vue'], //文件擴展
// 配置type文件生成位置
dts: 'src/components.d.ts',
}),
],
安裝unplugin-auto-import
自動導入vue3api
npm i -D unplugin-auto-import
安裝amfe-flexible + postcss-pxtorem 完成移動端的rem布局適配黎棠,Autoprefixer 瀏覽器前綴處理工具
npm i postcss postcss-pxtorem amfe-flexible autoprefixer -D
npm i amfe-flexible -S
修改vite.config.js
import autoprefixer from 'autoprefixer';
import postCssPxToRem from 'postcss-pxtorem';
...
css: {
preprocessorOptions: {
less: {
javascriptEnabled: true,
additionalData: `@import "${path.resolve(__dirname, 'src/assets/config.less')}";`,
},
},
postcss: {
plugins: [
autoprefixer({
overrideBrowserslist: ['Android 4.1', 'iOS 7.1', 'Chrome > 31', 'ff > 31', 'ie >= 8'],
}),
postCssPxToRem({
rootValue({ file }) {
return file.indexOf('vant') !== -1 ? 37.5 : 75; // vant框架
},
propList: ['*'], // 需要轉換的屬性,這里選擇全部都進行轉換
selectorBlackList: ['norem'], // 過濾掉norem-開頭的class木西,不進行rem轉換
}),
],
},
},
main.js引入import 'amfe-flexible';
安裝axios, qs
npm i axios qs -S
src下創(chuàng)建lib文件夾
創(chuàng)建request.js
import axios from 'axios';
import { showToast } from 'vant';
// 創(chuàng)建axios實例
const service = axios.create({
timeout: 60 * 1000, // 請求超時時間
});
const errorHandler = (e, report = true) => {
const res = e?.response?.data;
const msgStr = res?.msg || res?.errmsg || res?.message;
const errData = {
msg: msgStr,
};
const reqData = e.config.data;
reqData && Object.assign(errData, { data: reqData });
showErrMsg(msgStr);
throw e;
};
const showErrMsg = (msgStr) => {
showToast({
message: msgStr || '網(wǎng)絡錯誤',
icon: 'warning-o',
});
};
// request攔截器
service.interceptors.request.use(
(config) => {
return config;
},
(error) => {
errorHandler(error);
}
);
// response 攔截器
service.interceptors.response.use(
(response) => {
const res = response.data;
if (res?.code) {
if ([1, 200].includes(res.code)) {
return res?.data;
} else {
let msgStr = res?.msg || '抱歉八千,系統(tǒng)錯誤';
showErrMsg(msgStr);
}
} else {
// 響應體為數(shù)據(jù)燎猛,無code
return res;
}
},
(error) => {
errorHandler(error);
}
);
const get = (url, params, options) => {
return service.get(url, {
params,
...options,
});
};
const post = (url, body, headers = {}) => {
return service.post(url, body, {
headers,
});
};
const remove = (url, body) => {
return service.delete(url, { data: body });
};
const put = (url, body, headers = {}) => {
return service.put(url, body, {
headers,
});
};
export default { get, post, remove, put };
創(chuàng)建apiPath.js
const isProd = !/sandbox|localhost|127\.0.0.1|10.1./i.test(location.hostname);
const BASE_URL = isProd ? '' : '';
export default {
WELCOME: BASE_URL +'/api/扛门。。论寨。',
};
其他修改項
1.router/index.js
history: createWebHistory(import.meta.env.BASE_URL),
history: createWebHistory('/'),// 改這個
import.meta.env.BASE_URL雖然默認'/'但vite里的base配置會改動到他葬凳,所以還是寫死好。
2.移動端配置
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta
name="viewport"
content="minimum-scale=1.0, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
/>
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-touch-fullscreen" content="yes" />
3.package.json修改
"scripts": {
"dev": "vite",
"build": "vite build --mode development",
"prod": "vite build --mode production",
"preview": "vite preview"
},
根目錄添加環(huán)境文件
分別寫上
VITE_ENV = 'development'
VITE_ENV = 'production'
vite配置
import { defineConfig, loadEnv } from 'vite';
export default ({ mode }) => {
const isProd = loadEnv(mode, process.cwd()).VITE_ENV === 'production';
// https://vitejs.dev/config/
return defineConfig({
base: isProd ? '/' : '/',
build: {
chunkSizeWarningLimit: 1000, // 提高超大靜態(tài)資源警告大小
// sourcemap: true,
rollupOptions: {
input: 'index.html',
output: {
// 靜態(tài)資源打包做處理
chunkFileNames: 'static/js/[name]-[hash].js',
entryFileNames: 'static/js/[name]-[hash].js',
assetFileNames: 'static/[ext]/[name]-[hash].[ext]',
manualChunks: {
// 分包
vue: ['vue', 'vue-router', 'pinia'],
vant: ['vant'],
},
},
},
},
....
4.iPhoneX安全區(qū)間問題
<meta
name="viewport"
content="minimum-scale=1.0, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
/>
viewport-fit 默認有3個值
contain:可視窗口完全包含網(wǎng)頁內(nèi)容
cover:網(wǎng)頁內(nèi)容完全覆蓋可視窗口
auto:默認值,此值不影響初始布局視圖端口昌简,并且整個web頁面都是可查看的。
Webkit的css函數(shù)谦疾,env()和constant()犬金,是IOS11新增特性,用于設定安全區(qū)域與邊界的距離峰伙,有4個預定義變量
safe-area-inset-left:安全區(qū)域距離左邊邊界的距離 // 豎屏為0
safe-area-inset-right:安全區(qū)域距離右邊邊界的距離 // 豎屏為0
safe-area-inset-top:安全區(qū)域距離頂部邊界的距離 // 為導航欄+狀態(tài)欄的高度 88px
safe-area-inset-bottom :安全距離底部邊界的距離 // 34px
使用该默,constant和env按順序?qū)懀莎B加calc函數(shù)使用
height: constant(safe-area-inset-bottom);
height: env(safe-area-inset-bottom);
完整vite配置
import { defineConfig, loadEnv } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
import Components from 'unplugin-vue-components/vite';
import { VantResolver } from 'unplugin-vue-components/resolvers';
import AutoImport from 'unplugin-auto-import/vite';
import autoprefixer from 'autoprefixer';
import postCssPxToRem from 'postcss-pxtorem';
const path = require('path');
export default ({ mode }) => {
const isProd = loadEnv(mode, process.cwd()).VITE_ENV === 'production';
// https://vitejs.dev/config/
return defineConfig({
plugins: [
vue(),
vueJsx(),
Components({
resolvers: [VantResolver()],
extensions: ['vue'], //文件擴展
// 配置type文件生成位置
dts: 'src/components.d.ts',
}),
AutoImport({
include: [
/\.[tj]sx?$/, // .ts, .tsx, .js, .jsx
/\.vue$/,
/\.vue\?vue/, // .vue
/\.md$/, // .md
],
imports: [
'vue',
'vue-router',
'pinia',
],
}),
],
base: isProd ? '/' : '/',
server: {
host: '0.0.0.0',
port: 8080,
proxy: {
'/api': {
target: '',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
build: {
chunkSizeWarningLimit: 1000, // 提高超大靜態(tài)資源警告大小
// sourcemap: true,
rollupOptions: {
input: 'index.html',
output: {
// 靜態(tài)資源打包做處理
chunkFileNames: 'static/js/[name]-[hash].js',
entryFileNames: 'static/js/[name]-[hash].js',
assetFileNames: 'static/[ext]/[name]-[hash].[ext]',
manualChunks: {
// 分包
vue: ['vue', 'vue-router', 'pinia'],
vant: ['vant'],
},
},
},
},
resolve: {
alias: [
{
find: '@',
replacement: path.resolve(__dirname, 'src'),
},
],
// 支持vue后綴名
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
},
css: {
preprocessorOptions: {
less: {
javascriptEnabled: true,
additionalData: `@import "${path.resolve(__dirname, 'src/assets/config.less')}";`,
},
},
postcss: {
plugins: [
autoprefixer({
overrideBrowserslist: ['Android 4.1', 'iOS 7.1', 'Chrome > 31', 'ff > 31', 'ie >= 8'],
}),
postCssPxToRem({
rootValue({ file }) {
return file.indexOf('vant') !== -1 ? 37.5 : 75;
},
propList: ['*'], // 需要轉換的屬性,這里選擇全部都進行轉換
selectorBlackList: ['norem'], // 過濾掉norem-開頭的class恋沃,不進行rem轉換
}),
],
},
},
});
};