vuecli4+vant移动端响应式项目踩坑记录
文章目录
- 关键词
- 参考链接
- 一. 使用vue/cli4
- 二. 使用vant
- 三. 加入响应式布局
- 1. rem适配插件
- 2. PostCSS配置
- 四. 图标库:封装svg图标组件
- 1. 建立如下目录结构:
- 2. components/SvgIcon.vue
- 3. icon/index.js
- 4. 配置vue.config.js
- 5. svg图标使用
- 五.axios+api封装
- 目录结构(示例)
- http.js封装
- api/index.js
- api/user.js
- api注册到全局(main.js文件)
- api接口调用示例
- App.vue(断网代码示例)
- refresh.vue
- 六. router全局守卫处理
- 七. 跨域代理proxy -> 配置vue.config.js文件
- 八. vscode中自定义配置prettier
- 九. 查看隐藏的webpack配置:
- 十. vantlist组件在ios手机中滑动无效
关键词
- @vue/cli4, vant, rem, svg, axios
参考链接
- vue移动端ui框架 - vant官网
- vue cli官网
- vue-cli4中配置移动端自适应postcss-pxtorem
- 掘金-vue中Axios的封装和API接口的管理
一. 使用vue/cli4
全局安装@vue/cli最新版本
yarn add -g @vue/cli
或者npm install @vue/cli -g
查看安装的vue/cli版本
vue --version
创建vue项目
vue create hello-world
创建项目时候让选择,默认or手动,一般选择手动,按照提示选择自己需要的即可。我选择了以下2个关键的。
- CSS Pre-processors
- scss(node scss)
- eslint(prettier)
- CSS Pre-processors
vue/cli有个小坑。如果删除了依赖,自己安装一遍,发现有警告⚠️:
warning " > sass-loader@8.0.2" has unmet peer dependency "webpack@^4.36.0 || ^5.0.0".
。应该是脚手架的坑,暂时不知怎么去改。
二. 使用vant
- 安装插件
yarn add vant
- 按需引入插件
yarn add babel-plugin-import --dev
(注:这个插件装到开发依赖) - 自动按需引入使用示例:
// template
<van-button type="default">默认按钮</van-button>
// script
import { Button } from "vant";components: {[Button.name]: Button
}
三. 加入响应式布局
1. rem适配插件
- postcss-pxtorem 是一款 postcss 插件,用于将单位转化为 rem 。(!安装到开发依赖 --dev)
- lib-flexible 用于动态改变根节点的font-size,设置 rem 基准值。(!安装到生产依赖 --save)
- 【小坑】lib-flexible按照官网提供的在html引入js会报错,改为在main.js中引入依赖
import "amfe-flexible/index.js";
就ok了。
2. PostCSS配置
- vue.config.js中配置
css: {loaderOptions: {postcss: {plugins: [require("autoprefixer")({// 配置使用 autoprefixeroverrideBrowserslist: ["last 15 versions"]}),require("postcss-pxtorem")({rootValue: 37.5, // 换算的基数// 忽略转换正则匹配项。插件会转化所有的样式的px。比如引入了三方UI,也会被转化。目前我使用 selectorBlackList字段,来过滤//如果个别地方不想转化px。可以简单的使用大写的 PX 或 Px 。selectorBlackList: ["ig"],propList: ["*"]})]} } }
- postcss.config.js中配置
module.exports = {plugins: {autoprefixer: {overrideBrowserslist: ['Android >= 4.0', 'iOS >= 8'],},'postcss-pxtorem': {rootValue: 37.5, // ⚠️这里是设计稿的1/10propList: ['*'],mediaQuery: true},}, };
在配置 postcss-loader 时,应避免 ignore node_modules 目录,否则将导致 Vant 样式无法被编译
四. 图标库:封装svg图标组件
- 原因:svg放大后不失真,可以像css一样设置颜色,非常方便。
- 使用步骤:
1. 建立如下目录结构:
iconindex.jssvgtest1.svg // (去阿里的iconfont随便下载一个来试验)test2.svgcomponentsSvgIcon.vue
2. components/SvgIcon.vue
<template><svg :class="svgClass" aria-hidden="true" v-on="$listeners"><use :xlink:href="iconName" /></svg>
</template><script>
export default {name: "SvgIcon",props: {iconClass: {type: String,required: true},className: {type: String,default: ""}},computed: {iconName() {return `#icon-${this.iconClass}`;},svgClass() {if (this.className) {return "svg-icon " + this.className;} else {return "svg-icon";}}}
};
</script><style scoped>
.svg-icon {width: 16px;height: 16px;vertical-align: -3px;fill: currentColor;overflow: hidden;
}
</style>
3. icon/index.js
import Vue from "vue";
import SvgIcon from "@/components/SvgIcon"; // svg组件// register globally
Vue.component("svg-icon", SvgIcon);const req = require.context("./svg", false, /\.svg$/);const requireAll = requireContext => requireContext.keys().map(requireContext);
requireAll(req);
4. 配置vue.config.js
// 添加svg-sprite-loader,同时不要忽略了其他不作为图片的svg文件,
// file-loader 用来处理除了icon/svg文件夹下其他地方的.svg文件chainWebpack: config => {const svgRule = config.module.rule("svg");// 清除已有的所有 loader。// 如果你不这样做,接下来的 loader 会附加在该规则现有的 loader 之后。svgRule.uses.clear();svgRule.test(/\.svg$/).include.add(path.resolve(__dirname, "./src/icons/svg")).end().use("svg-sprite-loader").loader("svg-sprite-loader").options({symbolId: "icon-[name]"});const fileRule = config.module.rule("file");fileRule.uses.clear();fileRule.test(/\.svg$/).exclude.add(path.resolve(__dirname, "./src/icons/svg")).end().use("file-loader").loader("file-loader");}
5. svg图标使用
// class="color-red" 可以添加自定义的样式,可以覆盖默认的fill<svg-iconclass="color-red"icon-class="arrow_bottom_solid"
></svg-icon>
五.axios+api封装
目录结构(示例)
requesthttp.jsapiindex.jsuser.js
http.js封装
import axios from "axios";
import router from "../router";
import store from "../store";/*** 提示函数* 禁止点击蒙层、显示一秒后关闭*/
const tip = msg => {Toast({message: msg,duration: 1000,forbidClick: true});
};/*** 跳转登录页* 携带当前页面路由,以期在登录页面完成登录后返回当前页面*/
const toLogin = async () => {router.replace({path: "/login",query: {redirect: router.currentRoute.fullPath}});
};/*** 请求失败后的错误统一处理* @param {Number} status 请求失败的状态码*/
const errorHandle = status => {// 状态码判断switch (status) {// 401: 未登录状态,跳转登录页case 401:toLogin();break;// 403 token过期// 清除token并跳转登录页case 403:tip("登录过期,请重新登录");localStorage.removeItem("token");setTimeout(() => {toLogin();}, 1000);break;// 404请求不存在case 404:tip("请求的资源不存在");break;default:tip(`其他未知错误,状态码:${status}`);}
};// 状态200时候, code码判断
const errorCodeHandle = ({ code, message }) => {switch (code) {case "000000": //系统交易成功break;case "999999": //系统异常tip(message);break;case "AUTH_x1": //用户未登陆store.commit("storeUser/clearUserInfo");toLogin();break;case "AUTH_x2": //用户无权限store.commit("storeUser/clearUserInfo");tip(message);break;case "LOGIN_x3": //用户已禁用store.commit("storeUser/clearUserInfo");tip(message);break;case "LOGIN_x4": //用户session失效store.commit("storeUser/clearUserInfo");toLogin();break;default:tip(message);break;}
};// 创建axios实例
var instance = axios.create({ timeout: 5000 });
// 设置post请求头
instance.defaults.headers.post["Content-Type"] ="application/json;charset=UTF-8;";
instance.defaults.baseURL = "api";// 请求拦截器
instance.interceptors.request.use(config => {// 对config做一些处理// ...// 加载弹窗Toast.loading({message: "加载中...",forbidClick: true});return config;},error => Promise.error(error)
);// 响应拦截器
instance.interceptors.response.use(// 请求成功res => {Toast.clear();if (!store.state.storeGlobal.network) {store.commit("storeGlobal/changeNetwork", true);}if (res.status === 200 && res.data.code === "000000") {return Promise.resolve(res.data);} else {errorCodeHandle(res.data);return Promise.reject(res);}},// 请求失败error => {const { response } = error;if (response) {// 请求已发出,但是不在2xx的范围errorHandle(response.status, response.data.message);return Promise.reject(response);} else {// 处理断网的情况// eg:请求超时或断网时,更新state的network状态// network状态在app.vue中控制着一个全局的断网提示组件的显示隐藏// 关于断网组件中的刷新重新获取数据,会在断网组件(refresh.vue)中说明if (!window.navigator.onLine) {store.commit("storeGlobal/changeNetwork", false);} else {return Promise.reject(error);}}}
);export default instance;
api/index.js
/*** api接口的统一出口*/
import user from "@/request/api/user";// 导出接口
export default {user
};
api/user.js
/*** user模块接口列表*/
import axios from "@/request/http"; // 导入http中创建的axios实例const user = {login(params) {return axios.post("/login", params);}
};export default user;
api注册到全局(main.js文件)
import api from '@/request/api';Vue.prototype.$api = api;
api接口调用示例
// Login.vuemethods: {async onSubmit() {let params = {loginName: '小美',password: '123'};const res = await this.$api.login(params);console.log("登录信息:", res)}
}
App.vue(断网代码示例)
使用一个全局的store状态存储网络状态
<template><div id="app"><div v-if="!network" class="offline">哎呀,网络开小差啦。<van-icon name="replay" @click.native="onRefresh" /></div><router-view /></div>
</template><script>
import { mapState } from "vuex";
import { Icon } from "vant";
export default {components: {[Icon.name]: Icon},computed: {...mapState("storeGlobal", ["network"])},methods: {onRefresh() {this.$router.replace("/refresh");}}
};
</script><style lang="scss">
#app {font-family: STHeitiSC-Medium, STHeitiSC, Avenir, Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;color: #2c3e50;background-color: #f5f5f5;height: 100vh;.offline {text-align: center;padding: 10px;background-color: #ffeeaa;font-size: 14px;display: flex;align-items: center;justify-content: center;}
}
</style>
refresh.vue
<template><div></div>
</template><script>
/* 从app.vue来,这里简单介绍一下断网。在http.js中介绍了,我们会在断网的时候,来更新vue中network的状态,* 那么这里我们根据network的状态来判断是否需要加载这个断网组件。断网情况下,加载断网组件,不加载对应页面的组件。* 当点击刷新的时候,我们通过跳转refesh页面然后立即返回的方式来实现重新获取数据的操作。* 因此我们需要新建一个refresh.vue页面,并在其beforeRouteEnter钩子中再返回当前页面。*/
export default {beforeRouteEnter(to, from, next) {next(vm => {vm.$router.replace(from.fullPath);});}
};
</script>
六. router全局守卫处理
// router/index.jsconst routes = [{path: "/login",name: "Login",component: Login,meta: {title: "登录"}},
]router.beforeEach((to, from, next) => {// 添加title, 无需每个页面设置if (to.meta && to.meta.title) {document.title = to.meta.title;}// 添加路由来源,无需每个页面添加路由守卫判断来自哪个页面to.params.last = from;next();
});
七. 跨域代理proxy -> 配置vue.config.js文件
从vue/cli3开始项目就看不到webpcak.config.js之类的配置文件了。需要添加前端代理需要自己在根目录下添加vue.config.js进行配置。
下面⬇️展示一个配置比较齐全的文件。
const path = require("path");module.exports = {/* 部署生产环境和开发环境下的URL:可对当前环境进行区分,baseUrl 从 Vue CLI 3.3 起已弃用,要使用publicPath *//* baseUrl: process.env.NODE_ENV === 'production' ? './' : '/' */publicPath: process.env.NODE_ENV === "production" ? "/public/" : "./",/* 输出文件目录:在npm run build时,生成文件的目录名称 */outputDir: "dist",/* 放置生成的静态资源 (js、css、img、fonts) 的 (相对于 outputDir 的) 目录 */assetsDir: "assets",/* 是否在构建生产包时生成 sourceMap 文件,false将提高构建速度 */productionSourceMap: false,/* 默认情况下,生成的静态资源在它们的文件名中包含了 hash 以便更好的控制缓存,你可以通过将这个选项设为 false 来关闭文件名哈希。(false的时候就是让原来的文件名不改变) */filenameHashing: false,/* 代码保存时进行eslint检测 */lintOnSave: true,/* webpack-dev-server 相关配置 */devServer: {/* 自动打开浏览器 */open: true,/* 设置为0.0.0.0则所有的地址均能访问 */host: "0.0.0.0",port: 8088,https: false,hotOnly: false,/* 使用代理 */proxy: {"/sunrise-gateway": {/* 目标代理服务器地址 */target: "http://xxx.com/",/* 允许跨域 */changeOrigin: true}}},css: {loaderOptions: {postcss: {plugins: [require("autoprefixer")({// 配置使用 autoprefixeroverrideBrowserslist: ["last 15 versions"]}),require("postcss-pxtorem")({rootValue: 37.5, // 换算的基数// 忽略转换正则匹配项。插件会转化所有的样式的px。比如引入了三方UI,也会被转化。目前我使用 selectorBlackList字段,来过滤//如果个别地方不想转化px。可以简单的使用大写的 PX 或 Px 。selectorBlackList: ["ig"],propList: ["*"]})]}}},chainWebpack: config => {const svgRule = config.module.rule("svg");// 清除已有的所有 loader。// 如果你不这样做,接下来的 loader 会附加在该规则现有的 loader 之后。svgRule.uses.clear();svgRule.test(/\.svg$/).include.add(path.resolve(__dirname, "./src/icons/svg")).end().use("svg-sprite-loader").loader("svg-sprite-loader").options({symbolId: "icon-[name]"});const fileRule = config.module.rule("file");fileRule.uses.clear();fileRule.test(/\.svg$/).exclude.add(path.resolve(__dirname, "./src/icons/svg")).end().use("file-loader").loader("file-loader");}
};
八. vscode中自定义配置prettier
- vscode安装插件:Prettier - Code formatter
- 问题:插件格式化的文件和vuecli要求的prettimer需要的不一致。所以需要自定义配置成vuecli要求的效果。
- 解决:代码(code) -> 首选项(preference) -> 设置(settings) -> extensions -> premitter
- 具体配置可以参考premitter配置文件官方网站
- 中文的找到一篇基本配置+解释的参考文章Prettier格式化配置
{// 使能每一种语言默认格式化规则"[html]": {"editor.defaultFormatter": "esbenp.prettier-vscode"},"[css]": {"editor.defaultFormatter": "esbenp.prettier-vscode"},"[less]": {"editor.defaultFormatter": "esbenp.prettier-vscode"},"[javascript]": {"editor.defaultFormatter": "esbenp.prettier-vscode"},/* prettier的配置 */"prettier.printWidth": 100, // 超过最大值换行"prettier.tabWidth": 4, // 缩进字节数"prettier.useTabs": false, // 缩进不使用tab,使用空格"prettier.semi": true, // 句尾添加分号"prettier.singleQuote": true, // 使用单引号代替双引号"prettier.proseWrap": "preserve", // 默认值。因为使用了一些折行敏感型的渲染器(如GitHub comment)而按照markdown文本样式进行折行"prettier.arrowParens": "avoid", // (x) => {} 箭头函数参数只有一个时是否要有小括号。avoid:省略括号"prettier.bracketSpacing": true, // 在对象,数组括号与文字之间加空格 "{ foo: bar }""prettier.disableLanguages": ["vue"], // 不格式化vue文件,vue文件的格式化单独设置"prettier.endOfLine": "auto", // 结尾是 \n \r \n\r auto"prettier.eslintIntegration": false, //不让prettier使用eslint的代码格式进行校验"prettier.htmlWhitespaceSensitivity": "ignore","prettier.ignorePath": ".prettierignore", // 不使用prettier格式化的文件填写在项目的.prettierignore文件中"prettier.jsxBracketSameLine": false, // 在jsx中把'>' 是否单独放一行"prettier.jsxSingleQuote": false, // 在jsx中使用单引号代替双引号"prettier.parser": "babylon", // 格式化的解析器,默认是babylon"prettier.requireConfig": false, // Require a 'prettierconfig' to format prettier"prettier.stylelintIntegration": false, //不让prettier使用stylelint的代码格式进行校验"prettier.trailingComma": "es5", // 在对象或数组最后一个元素后面是否加逗号(在ES5中加尾逗号)"prettier.tslintIntegration": false // 不让prettier使用tslint的代码格式进行校验
}
九. 查看隐藏的webpack配置:
- vue inspect 执行后,控制台会显示你的webpack所有的配置
- vue inspect --rules 显示所有的rule配置规则
- vue inspect --rule svg (我们在上面配置了svg)
十. vantlist组件在ios手机中滑动无效
发现设置了 高度为100vh的#app标签,没有设置overflow,其他浏览器都默认可以滑动。ios上的wkwebview中不能滑动,设置上overflow: auto就好啦
vuecli4+vant移动端响应式项目踩坑记录相关推荐
- 配合Opencv2.4.9,CMake3.12.1和VS2010在win10下构建项目踩坑记录
配合Opencv3,CMake和VS2010在win10下构建项目踩坑记录 参考https://blog.csdn.net/qq_26623659/article/details/78322782 博 ...
- vue开发PC端响应式项目
便于开发 目的: 设计稿1920尺寸,达到响应式.如果设计稿上是75px,那么代码中也直接写上75px 第一步,配置postcss-pxtorem 安装 # 安装 $ yarn add postcss ...
- Eclipse转IDEA开发java项目spring+mybaits项目踩坑记录
久了不用一个东西总有遗忘,记录是你快速找回状态之本.今天将原来eclipse写的spring+mybatis Demo在 idea上跑起来,花了不少时间.这里将坑记录下: 一.IDEA创建项目 1. ...
- 集成Emscripten+wasm至React项目踩坑记录
前言 需求是有一个C++写的工具包(负责大规模的数据运算). 需要用emscripten是把C/C++编译成WebAssembly,便于在JS环境之后执行. 最终在React项目中调用工具包. 数据类 ...
- vue创建项目踩坑记录 443 ECONNRESET
安装nodejs 并创建vue 项目的记录 首先安装nodejs 是傻瓜式操作,配置环境变量的可以参考这个博文: 点击跳转安装教程 查看node版本和npm 版本 使用淘宝镜像cnpm 第二步项目初始 ...
- Vue.js-Day06-AM【项目实战(附带 完整项目源码)-day01-am:移动端响应式(响应式尺寸、视口问题、实现rem变化、rem设计)、实战项目搭建(初始化项目、处理rem、搭建路由)】
Vue.js实训[基础理论(5天)+项目实战(5天)]博客汇总表[详细笔记] 实战项目源码[链接:https://pan.baidu.com/s/1r0Mje3Xnh8x4F1HyG4aQTA 提 ...
- 移动端响应式布局项目之阿里百秀首页
移动端响应式布局项目之阿里百秀首页 文章目录 移动端响应式布局项目之阿里百秀首页 前言 一.前期准备 1.1 建立文件夹 1.2 需求分析 1.2.1 页面布局分析 `判断每一部分占据栅格系统多少份` ...
- 移动Web开发--学习笔记三 响应式项目实战(微金所)
响应式项目实战(微金所) 响应式项目开发流程 选择一种屏幕格式进行开发 相应功能模块完成后,测试是否响应式效果 确保响应式效果满足要求 进行下一个功能模块开发 使用自定义字体图标 创建自己的字体图标h ...
- php博客手机版模板下载器,【织梦模板下载】高端响应式游艇租赁类网站模板(自适应手机端) PHP源码带数据...
模板名称: 响应式游艇租赁类网站织梦模板(自适应手机端)+PC+wap+利于SEO优化 模板介绍: 织梦最新内核开发的模板,该模板属于电缆.电线类企业都可使用, 这款模板使用范围极广,不仅仅局限于一类 ...
最新文章
- 对我而言Linux究竟有什么魅力
- 装机人员工具 - imsoft.cnblogs
- 10万字208道Java经典面试题总结(附答案)
- uniapp 即时通讯_在uni-app使用极光IM 开发一个聊天室
- Drools教程(基础篇)——Eclipse下Drools运行时安装
- 数学建模【开会总结】
- po层和vo层中po和vo是什么意思
- ubuntu修改开机密码
- 细胞自噬机制最新研究进展(2021年12月)
- 电脑开启后,进入桌面黑屏,任务管理器可以用(排除是硬件问题)
- php后台登录页,后台登录页面模板源码
- 【office2010】利用尾注添加参考文献
- Lua弱引用表处理普通的内存泄漏
- AMD提出的补丁使退出延迟降低21%左右
- 触摸按键PCB设计要点
- 读《金刚经》学心态,读《易经》学生存,读《道德经》学生活
- iOS系统字体如何使用
- Machine Learning with Graphs 之 Traditional Methods for Machine Learning in Graphs
- 树莓派开发笔记(十一):蓝牙的使用,BlueZ协议(双树莓探测rssi并通过蓝牙互传获取的rssi信号强度)
- 详解跨境电商企业信息化建设的关键点:业财一体化