文章目录

  • 关键词
  • 参考链接
  • 一. 使用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

  1. 全局安装@vue/cli最新版本
    yarn add -g @vue/cli 或者 npm install @vue/cli -g

  2. 查看安装的vue/cli版本 vue --version

  3. 创建vue项目 vue create hello-world

  4. 创建项目时候让选择,默认or手动,一般选择手动,按照提示选择自己需要的即可。我选择了以下2个关键的。

    • CSS Pre-processors

      • scss(node scss)
    • eslint(prettier)
  5. vue/cli有个小坑。如果删除了依赖,自己安装一遍,发现有警告⚠️:warning " > sass-loader@8.0.2" has unmet peer dependency "webpack@^4.36.0 || ^5.0.0".。应该是脚手架的坑,暂时不知怎么去改。

二. 使用vant

  1. 安装插件yarn add vant
  2. 按需引入插件yarn add babel-plugin-import --dev(注:这个插件装到开发依赖)
  3. 自动按需引入使用示例:
// 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配置

  1. 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: ["*"]})]}
    }
    }
    
  2. 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移动端响应式项目踩坑记录相关推荐

  1. 配合Opencv2.4.9,CMake3.12.1和VS2010在win10下构建项目踩坑记录

    配合Opencv3,CMake和VS2010在win10下构建项目踩坑记录 参考https://blog.csdn.net/qq_26623659/article/details/78322782 博 ...

  2. vue开发PC端响应式项目

    便于开发 目的: 设计稿1920尺寸,达到响应式.如果设计稿上是75px,那么代码中也直接写上75px 第一步,配置postcss-pxtorem 安装 # 安装 $ yarn add postcss ...

  3. Eclipse转IDEA开发java项目spring+mybaits项目踩坑记录

    久了不用一个东西总有遗忘,记录是你快速找回状态之本.今天将原来eclipse写的spring+mybatis Demo在 idea上跑起来,花了不少时间.这里将坑记录下: 一.IDEA创建项目 1. ...

  4. 集成Emscripten+wasm至React项目踩坑记录

    前言 需求是有一个C++写的工具包(负责大规模的数据运算). 需要用emscripten是把C/C++编译成WebAssembly,便于在JS环境之后执行. 最终在React项目中调用工具包. 数据类 ...

  5. vue创建项目踩坑记录 443 ECONNRESET

    安装nodejs 并创建vue 项目的记录 首先安装nodejs 是傻瓜式操作,配置环境变量的可以参考这个博文: 点击跳转安装教程 查看node版本和npm 版本 使用淘宝镜像cnpm 第二步项目初始 ...

  6. Vue.js-Day06-AM【项目实战(附带 完整项目源码)-day01-am:移动端响应式(响应式尺寸、视口问题、实现rem变化、rem设计)、实战项目搭建(初始化项目、处理rem、搭建路由)】

    Vue.js实训[基础理论(5天)+项目实战(5天)]博客汇总表[详细笔记] 实战项目源码[链接:https://pan.baidu.com/s/1r0Mje3Xnh8x4F1HyG4aQTA   提 ...

  7. 移动端响应式布局项目之阿里百秀首页

    移动端响应式布局项目之阿里百秀首页 文章目录 移动端响应式布局项目之阿里百秀首页 前言 一.前期准备 1.1 建立文件夹 1.2 需求分析 1.2.1 页面布局分析 `判断每一部分占据栅格系统多少份` ...

  8. 移动Web开发--学习笔记三 响应式项目实战(微金所)

    响应式项目实战(微金所) 响应式项目开发流程 选择一种屏幕格式进行开发 相应功能模块完成后,测试是否响应式效果 确保响应式效果满足要求 进行下一个功能模块开发 使用自定义字体图标 创建自己的字体图标h ...

  9. php博客手机版模板下载器,【织梦模板下载】高端响应式游艇租赁类网站模板(自适应手机端) PHP源码带数据...

    模板名称: 响应式游艇租赁类网站织梦模板(自适应手机端)+PC+wap+利于SEO优化 模板介绍: 织梦最新内核开发的模板,该模板属于电缆.电线类企业都可使用, 这款模板使用范围极广,不仅仅局限于一类 ...

最新文章

  1. 对我而言Linux究竟有什么魅力
  2. 装机人员工具 - imsoft.cnblogs
  3. 10万字208道Java经典面试题总结(附答案)
  4. uniapp 即时通讯_在uni-app使用极光IM 开发一个聊天室
  5. Drools教程(基础篇)——Eclipse下Drools运行时安装
  6. 数学建模【开会总结】
  7. po层和vo层中po和vo是什么意思
  8. ubuntu修改开机密码
  9. 细胞自噬机制最新研究进展(2021年12月)
  10. 电脑开启后,进入桌面黑屏,任务管理器可以用(排除是硬件问题)
  11. php后台登录页,后台登录页面模板源码
  12. 【office2010】利用尾注添加参考文献
  13. Lua弱引用表处理普通的内存泄漏
  14. AMD提出的补丁使退出延迟降低21%左右
  15. 触摸按键PCB设计要点
  16. 读《金刚经》学心态,读《易经》学生存,读《道德经》学生活
  17. iOS系统字体如何使用
  18. Machine Learning with Graphs 之 Traditional Methods for Machine Learning in Graphs
  19. 树莓派开发笔记(十一):蓝牙的使用,BlueZ协议(双树莓探测rssi并通过蓝牙互传获取的rssi信号强度)
  20. 详解跨境电商企业信息化建设的关键点:业财一体化

热门文章

  1. 最优化学习 无约束优化问题的最优性条件
  2. CentOS禁止Ping方法
  3. linux桌面 任务栏,状态栏消失恢复
  4. Matlab符号微积分练习
  5. chrome所有页面崩溃【转载】
  6. iphone panic故障对照表_苹果 AirPods 新维修工具上线:可区分是污垢堵塞还是故障 - AirPods...
  7. Android 10.0 Launcher3 单层app列表页排序功能实现
  8. ajax contentType 设置
  9. 【JAVA】Java中switch的用法。
  10. MVC模式 与 如何实现struts MVC模式