vue-cli更改默认下载方式(用什么包管理器)
找到C:\Users\Administrator/.vuerc文件 Yarn/npm
node.sass下载失败
npm install -g mirror-config-china
先执行 这个命令
然后再安装 npm i node-sass -D
字体图标使用
- 开发环境
- iconfont官网选图标 加购物车,添加到项目,生成在线css
- public/index.html 导入在线css
- 随用随添加,生成新的在线css链接,更改导入的在线css链接
<link rel="stylesheet" href="//at.alicdn.com/t/font_1866688_130ihed3lqw.css">
- 组件中使用(可以通过iconfont里更多操作,编辑项目,来修改字体图标前缀,但会覆盖vant里的一些图标,那vant里的一些组件就用不了了,通常用下面的方法)
<i class="iconfont icon-shouye"></i> <van-icon class="iconfont" class-prefix="icon" name="shouye" /> <!-- 自定义使用 --> <van-tabbar-item> <van-icon class="iconfont" slot="icon" class-prefix="icon" name="shouye" /> <span>首页</span> </van-tabbar-item>
- 生产环境
- iconfont 下载到本地,解压,将字体图标文件复制到静态资源目录assets/fonts下(iconfont.css/.eot/.svg)
- 在main.js导入import ‘@/assets/fonts/iconfont.css’
移动端适配布局
-
h5用的框架一般有自己的布局方式
-
自己布局 flex做布局,rem做单位,字体不能用rem做单位
- 原理:根据根元素字体大小为单位(rem),设置其他元素尺寸
- 根元素字体大小,随着屏幕尺寸地改变而改变 =>根据屏幕宽度动态的设置html的字体大小
方法一function setRem(){ document.documentElement.style.fontSize = document.documentElement.clientWidth/10 + 'px' } setRem() window.onresize = function(){ setRem() } window.onorientationchange = function(){ setRem() }
- 写在assets/js/rem.js
- main.js引入
import '@/assets/js/rem.js'
<!-- $rem:75rem;多少取决于设计图的宽度 --> <style lang="sass" scoped> $rem:75rem; #box{ width:500/$rem; height:100/$rem; } </style> <!-- 手机尺寸375x667 --> <!-- ui设计图750x1334 设计图测量500px--> <!-- html 1rem = 75px --> <!-- 使用500/75 rem * 75 = 500px -->
方法二
vue cli3.0 rem 使用
首先安装amfe-flexible插件,在main.js里引入1、
npm i amfe-flexible -D
2、
import 'amfe-flexible'
然后再,安装postcss-px2rem插件
npm i postcss-px2rem -D
在package.json中配置
"postcss": { "plugins": { "autoprefixer": {}, "postcss-px2rem": { <!-- 没设计图直接改37.5 --> "remUnit": 75 } } }
轮播图数据获取
- xx
npm i axios -S
- main.js引入
import axios from 'axios'
- 将方法继承到Vue上,可以在实例上使用
Vue.prototype.$http = axios;
- 组件内使用
methods(){this.$http.get()then()}
- **跨域问题–反向代理 **
https://cli.vuejs.org/zh/config/#devserver-proxy
ajax请求处理
-
上面xx方法使用场景:
- 小型项目,且公司里只有一个前端(你一个人写这个项目)
- 外包的,不需要做版本更新迭代
-
axios请求接口函数单独提取,方便复用、维护,axios还需要一些配置
- 在utils文件夹下创建request.js文件
import axios from 'axios' const request = axios.create({ baseURL: '/hundan/', // baseURL用于开发环境、测试环境以及线上环境的不同的源 timeout: 8000 })
- axios拦截
1. 全局loading实现
2. 接口token处理(有没有,有没有过期)// Add a request interceptor request.interceptors.request.use(function (config) { // Do something before request is sent return config; }, function (error) { // Do something with request error return Promise.reject(error); }); // Add a response interceptor request.interceptors.response.use(function (response) { // Do something with response data return response; }, function (error) { // Do something with response error return Promise.reject(error); });
scss嵌套
-
在嵌套中还有一个标识符是
&
我们可以使用 -
先来看一个例子
div { width: 100px; height: 100px; :hover { width: 200px; } } // 我想的是 div 被鼠标悬停的时候 width 变成 200 // 但是编译结果却是 div { width: 100px; height: 100px; } div :hover { width: 200px; }
-
和预想的结果不一样了
-
这个时候就要用到
&
来连接了div { width: 100px; height: 100px; &:hover { width: 200px; } } // 编译结果 div { width: 100px; height: 100px; } div:hover { width: 200px; }
-
这个时候就和我需要的一样了
post传参注意(axios)
- 问题产生原因:在使用
axios
时,POST
请求默认参数格式是application/json
,而后台接受的数据格式默认是application/x-www-form-urlenxoded
- 解决方法:
- 安装依赖
npm i qs -S qs.stringify()
富文本图片宽度超出屏幕问题
```javascript
computed: {
content () {
if (this.item.content) {
<!-- 正则匹配字符串替换手动设置width:100% -->
return this.item.content.replace(/<img/g, "<img style='width:100%'")
} else {
return ''
}
}
}
```
接口校验(携带token)
// Add a request interceptor
request.interceptors.request.use(function (config) {
// Do something before request is sent
// token请求头携带用于接口校验
config.headers.accessToken = localStorage.getItem('accessToken')
return config
}, function (error) {
// Do something with request error
return Promise.reject(error)
})
// Add a response interceptor
request.interceptors.response.use(function (response) {
// Do something with response data
if (response.data.code === 0) {
return response
} else if (response.data.code === 401) {
router.push('/login')
}
}, function (error) {
// Do something with response error
return Promise.reject(error)
})
prams传参和query传参的区别
prams 隐性传参,必须和name使用,刷新会造成数据丢失
query直接在地址栏上面拼接,显性的,一般和path一起使用,刷新不会造成数据丢失
禁止用户双击缩放
<meta name="viewport" content="width=device-width,initial-scale=1.0">
自定义插件(loading)
-
创建模板文件 src/plugins/loading/Loading.vue 状态isShow控制显隐
<template> <div class="bg" v-if="isShow"> <div class="dotBox"> <p> <span></span> <span></span> <span></span> <span></span> </p> <p> <span></span> <span></span> <span></span> <span></span> </p> </div> </div> </template> <script> export default { data () { return { isShow: true } } } </script> <style lang="scss" scoped> .bg{ position: fixed; top: 0; bottom: 0; left: 0; right: 0; z-index: 999; background:rgba($color: #000000, $alpha: 0.8); .dotBox{ width: 120px; height: 120px; top: 50%; left: 50%; margin-top: -60px; margin-left: -60px; position: absolute; p{ width: 120px; height: 120px; position: absolute; span{ display: block; width: 24px; height: 24px; border-radius: 12px; background: white; position: absolute; animation: twinkle 1.5s linear infinite; &:nth-of-type(2){ top: 0; right: 0; } &:nth-of-type(3){ bottom: 0; right: 0; } &:nth-of-type(4){ bottom: 0; left: 0; } } &:nth-of-type(1){ span{ &:nth-of-type(1){ animation-delay: -0.4s; } &:nth-of-type(4){ animation-delay: -0.8s; } &:nth-of-type(3){ animation-delay: -1.2s; } &:nth-of-type(2){ animation-delay: -1.6s; } } } &:nth-of-type(2){ transform: rotate(45deg); span{ &:nth-of-type(1){ animation-delay: -0.2s; } &:nth-of-type(4){ animation-delay: -0.6s; } &:nth-of-type(3){ animation-delay: -1s; } &:nth-of-type(2){ animation-delay: -1.4s; } } } } } } @keyframes twinkle{ from{transform: scale(0);} 50%{transform: scale(1);} to{transform: scale(0);} } </style>
-
创建插件导出文件 src/plugins/loading/index.js
// 导入模板文件 import LoadingTpl from './Loading' const loading = { install (Vue, options) { // 生成vue组件 let Load = Vue.extend(LoadingTpl) // 生成vue组件实例 let load = new Load() // 手动渲染到页面 document.body.appendChild(load.$mount().el) // 创建实例方法 // 控制显示loading效果 Vue.prototype.$showLoading = () => { load.isShow = true } // 控制隐藏loading效果 Vue.prototype.$hideLoading = () => { load.isShow = false } } } // 导出插件 export default loading
-
main.js入口文件中使用插件
import Vue from 'vue' import loading from '@/plugins/loading' Vue.use(loading)
-
在axios实例文件中,接口拦截加loading效果
import axios from 'axios' import Vue from 'vue' import router from '@/router' var bus = new Vue() const request = axios.create({ baseURL: '/hundan/', // baseURL用于开发环境、测试环境以及线上环境的不同的源 timeout: 10000 }) // Add a request interceptor request.interceptors.request.use(function (config) { // 发起请求时,出现loading bus.$showLoading() // Do something before request is sent // token请求头携带用于接口校验 config.headers.accessToken = localStorage.getItem('accessToken') return config }, function (error) { // Do something with request error return Promise.reject(error) }) // Add a response interceptor request.interceptors.response.use(function (response) { // Do something with response data // 接受响应时,隐藏loading bus.$hideLoading() if (response.data.code === 0) { return response } else if (response.data.code === 401) { router.push('/login') } }, function (error) { // Do something with response error return Promise.reject(error) }) export default request
搜索页面
- 移动端的页面的搜索栏都是假的,是点击跳转到一个搜索页面
- 历史记录可以存在localStorage里面,条数限制,数组api
unshift
pop
- 历史记录去重 数组去重
find
findIndex
[... new Set(oldArr)]
x
移动端滚动效果优化(better-scroll iScroll)
- 普通使用
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> * { margin: 0; padding: 0; } li { background: #13ac62; list-style-type: none; line-height: 30px; margin: 10px; color: salmon; } li:nth-of-type(2n) { background: #e222a2; color: sandybrown; } html, body { height: 100%; /* 不显示浏览器自带的滚动条 */ overflow: hidden; } .wrapper { height: 100%; } </style> </head> <body> <div class="wrapper"> <ul> <li onclick="window.alert(1)">惦记我</li> <li><a href="javascript:alert(2);">xxxx</a></li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> <li>7</li> <li>8</li> <li>9</li> <li>10</li> <li>11</li> <li>12</li> <li>13</li> <li>14</li> <li>15</li> <li>16</li> <li>17</li> <li>18</li> <li>19</li> <li>20</li> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> <li>7</li> <li>8</li> <li>9</li> <li>10</li> </ul> <div> 1222222222222222222 </div> </div> <!-- 去npmjs上下载,官网上的有点问题(下拉刷新没试出来)--> <script src="../../../../utils/bscroll/bscroll.js"></script> <script> let wrapper = document.querySelector('.wrapper') // DOM对象或选择器 let scroll = new BScroll(wrapper, { // 默认会阻止浏览器的原生 click 事件,当设置为 true,better-scroll 会派发一个 click 事件 click: true, // 配置下拉刷新功能,默认false,可配true,或对象 pullDownRefresh: { // 可以配置顶部下拉的距离来决定刷新的时机 threshold: 50, // 回弹停留的距离 (谷歌自带的模拟器回弹停留的距离有点问题,但真机上没问题) stop: 20 }, probeType:2, // 配置下拉刷新功能,默认false,可配true,或对象 pullUpLoad: { // 可以配置底部上拉的距离来决定加载的时机 threshold: 50 } }) // 监听下拉事件,在下拉刷新动作后,一般用来去后端请求数据(官网起步下的文件没用,npm下个) scroll.on('pullingDown',function(){ console.log('发送ajax请求了...') scroll.finishPullDown() }) // scroll.on('scroll',function(){ // console.log('我滚动了...') // }) </script> </body> </html>
- 项目中使用
写个展示组件bScroll.vuenpm i better-scroll -S
组件中使用<template> <div class="wrapper" :style="{height:typeof(height)===Number?height+'px':height, overflow: 'hidden'}"> <slot></slot> </div> </template> <script> import BScroll from 'better-scroll' import { setTimeout } from 'timers' export default { data () { return { scroll: '' } }, mounted () { // ***等到生成真实的dom元素时才能用选择器 setTimeout(() => { this.scroll = new BScroll('.wrapper', this.options) if (this.options.pullDownRefresh) { this.scroll.on('pullingDown', () => { // props接受的回调函数,用来请求数据 this.$emit('fn') this.scroll.finishPullDown() }) } }, 20) }, props: { height: { type: [Number, String], require: true }, lists: { type: Array, require: true }, options: { type: Object, default () { return { click: true, pullDownRefresh: false, probeType: 2, pullUpLoad: false } } } }, watch: { lists (n) { console.log('重置...') this.scroll.refresh() } } } </script> <style lang="scss" scoped> </style>
<template> <div> <page-header title='分类' :backIcon="false"></page-header> <div class="main"> <div class="left"> <van-tag @click="fn(index,cate.id)" :type="num===index?'primary':'default'" size="large" v-for="(cate, index) in cates" :key="cate.id">{{cate.name}}</van-tag> </div> <div class="right"> <b-scroll height="calc(100vh - 100px)" :options="{click: true,pullDownRefresh: {threshold: 50,stop: 20}}" :lists="items" @fn="pullingDownFn"> <!-- 注意!只对第一个子容器生效 --> <!-- 原理一定要搞清 wrapper高度定死、overflow:hidden去除浏览器默认滚动条,以及隐藏子容器超出自己的部分 --> <!-- 第一个子容器高度超过wrapper就可以滚动了 --> <div class="list"> <van-card v-for="item in items" :key="item.id" :price="item.minPrice" :desc="item.characteristic" :title="item.name" :thumb="item.pic" @click="enterDetail(item.id)" /> </div> <div v-if="!items.length" class="null_pic"> <img src="@/assets/images/data_null.jpg" alt="暂无数据"> </div> </b-scroll> </div> </div> <tabbar/> </div> </template> <script> import tabbar from '@/components/tabbar' import pageHeader from '@/components/pageHeader' import { getAllCates, getItemList } from '@/api' import bScroll from '@/components/bScroll.vue' export default { data () { return { cates: [], items: [], num: 0 } }, components: { tabbar, pageHeader, bScroll }, created () { this.fetchAllCates() }, methods: { // 下拉刷新事件 pullingDownFn () { // **:fn="pullingDownFn" // **不要写成这样:fn="fetchItems(cates[num].id)" // 这他妈的不是一个函数,而是函数调用,死循环,算了,是我眼瞎 // 错错错,哪有传函数的,用事件总线呀,子组件触发绑定自身上的父组件事件 console.log('下拉刷新了') this.fetchItems(this.cates[this.num].id) }, // 进入商品详情 enterDetail (goodsId) { this.$router.push('/detail/' + goodsId) }, fetchAllCates () { getAllCates().then(res => { if (res.data.code === 0) { this.cates = res.data.data this.fetchItems(this.cates[0].id) } }) }, fetchItems (id) { getItemList({ categoryId: id }).then(res => { if (res) { this.items = res.data.data } else { this.items = [] } }) }, fn (i, id) { this.num = i this.fetchItems(id) } } } </script> <style lang="scss" scoped> .main{ display: flex; height: calc(100vh - 100px); .left{ display: flex; flex: 1; flex-wrap: wrap; overflow: auto; background: #bbb; } .right{ display: flex; flex: 4; background: #f2f2f2; .list{ display: flex; flex-wrap: wrap; } .null_pic{ img{ width: 100%; } } } .van-tag{ font-size: 14px; width: 100%; margin: 5px 0; } .van-card{ width: 100%; } } </style>
其他注意
- 移动端边框圆角给确定值,最好不要用百分比
- vant全部引入记得引入css样式,不然会出错,比如search使用设置圆角设置不上
- index作条件判断时,注意可能为0
- 全选反选 个体判大,小用change,全用change出错,直接都用click就行
- *对象作为参数传递时是地址传递
let newObj = JSON.parse(JSON.stringify(oldObj))
- let解构的数据为只读的,用一个变量来接受它,更改这个变量不会更改原来的数组或对象
'carts' is assigned a value but never used no-unused-vars
,(如果想修改state.carts直接写,不用结构)var obj = { a:1, b:2 }; let { a } = obj; a = 3; console.log(obj) //{a: 1,b: 2}
- htmlcss中calc使用运算符加空格
height: calc(100vh - 100px)
- vant组件有些要穿透才能设置上css样式
/deep/.iconBox {xxxx}