A Vue.js project
项目github地址 https://github.com/houmao/vue-first-project.git
# 全局安装 vue-cli
$ npm install --global vue-cli
# 创建一个基于 webpack 模板的新项目
$ vue init webpack my-project
$ cd my-project
# 安装依赖
$ npm install
# 运行开发
$ npm run dev
问题:
npm install 的时候出错‘Failed at the chromedriver@2.35.0 install script. ’
解决方法:
npm install chromedriver --chromedriver_cdnurl=https://npm.taobao.org/mirrors/chromedriver
将svg文件导入到https://icomoon.io/进行图表转换
// 现代浏览器clearfix方案,不支持IE6/7
.clearfix:after {
display: table;
content: " ";
clear: both;
}
// 全浏览器通用的clearfix方案
// 引入了zoom以支持IE6/7
.clearfix:after {
display: table;
content: " ";
clear: both;
}
.clearfix{
*zoom: 1;
}
// 全浏览器通用的clearfix方案【推荐】
// 引入了zoom以支持IE6/7
// 同时加入:before以解决现代浏览器上边距折叠的问题
.clearfix:before,
.clearfix:after {
display: table;
content: " ";
}
.clearfix:after {
clear: both;
}
.clearfix{
*zoom: 1;
}
使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。
vue-router 能做到,而且更好,它让你可以自定义路由切换时页面如何滚动。
注意: 这个功能只在 HTML5 history 模式下可用。
scrollBehavior 方法接收 to 和 from 路由对象。第三个参数 savedPosition 当且仅当 popstate 导航 (通过浏览器的 前进/后退 按钮触发) 时才可用。
// 与keepAlive结合,如果keepAlive的话,保存停留的位置:
new Vue({
el: '#app',
mode: 'history',
router,
store,
components: {App},
template: '<App/>',
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
if (from.meta.keepAlive) {
from.meta.savedPosition = document.body.scrollTop;
}
return { x: 0, y: to.meta.savedPosition || 0 }
}
}
})
结合router,缓存部分页面
使用$route.meta的keepAlive属性:
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
需要在router中设置router的元信息meta:
//...router.js
export default new Router({
routes: [
{
path: '/',
name: 'home',
component: home,
meta: {
keepAlive: false // 不需要缓存
}
},
//所有商铺列表页
{
path: '/msite',
name:'msite'
component: msite,
meta: {
keepAlive: true // 需要缓存
},
},
]
})
参看header.vue页面,实现close关闭按钮在最底部
- 方案一:
当样式像素一定时,因手机有320px,640px等.各自的缩放比差异,所以设备显示像素就会有1Npx,2Npx.解决就是用media + scale.
设备上像素 = 样式像素 * 设备像素比(DPR)
屏幕宽度: 320px 480px 640px
设备像素比: 1 1.5 2
通过查询它的设备像素比 devicePixelRatio
在设备像素比为1.5倍时, round(1px 1.5 / 0.7) = 1px
在设备像素比为2倍时, round(1px 2 / 0.5) = 1px
// less样式
.border-1px(@color) {
position: relative;
&:after {
display: block;
position: absolute;
left: 0;
bottom: 0;
width: 100%;
border-top: 1px solid @color;
content: '';
}
}
@media (-webkit-min-device-pixel-ratio: 1.5),(min-device-pixel-ratio: 1.5) {
.border-1px{
&::after {
-webkit-transform: scaleY(0.7);
transform: scaleY(0.7);
}
}
}
@media (-webkit-min-device-pixel-ratio: 2),(min-device-pixel-ratio: 2) {
.border-1px{
&::after {
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
}
}
}
- 方案二
viewport + rem 方法,根据设备DPR调整页面的缩放值,动态设置 html 的font-size, 进而达到高清效果。《手机端页面自适应解决方案—rem布局》
const win = window;
export default win.flex = (normal, baseFontSize, fontscale) => {
const _baseFontSize = baseFontSize || 100;
const _fontscale = fontscale || 1;
const doc = win.document;
const ua = navigator.userAgent;
const matches = ua.match(/Android[\S\s]+AppleWebkit\/(\d{3})/i);
const UCversion = ua.match(/U3\/((\d+|\.){5,})/i);
const isUCHd = UCversion && parseInt(UCversion[1].split('.').join(''), 10) >= 80;
const isIos = navigator.appVersion.match(/(iphone|ipad|ipod)/gi);
let dpr = win.devicePixelRatio || 1;
if (!isIos && !(matches && matches[1] > 534) && !isUCHd) {
// 如果非iOS, 非Android4.3以上, 非UC内核, 就不执行高清, dpr设为1;
dpr = 1;
}
const scale = normal ? 1 : 1 / dpr;
let metaEl = doc.querySelector('meta[name="viewport"]');
if (!metaEl) {
metaEl = doc.createElement('meta');
metaEl.setAttribute('name', 'viewport');
doc.head.appendChild(metaEl);
}
metaEl.setAttribute('content', `width=device-width,user-scalable=no,initial-scale=${scale},maximum-scale=${scale},minimum-scale=${scale}`);
doc.documentElement.style.fontSize = normal ? '50px' : `${_baseFontSize / 2 * dpr * _fontscale}px`;
};
getBoundingClientRect用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置。
rect = el.getBoundingClientRect(); //或者元素相对位置
top = el.getBoundingClientRect().top // 获得元素距离顶部视窗顶部位置
注意:IE、Firefox3+、Opera9.5、Chrome、Safari支持,在IE中,默认坐标从(2,2)开始计算,导致最终距离比其他浏览器多出两个像素,我们需要做个兼容。
document.documentElement.clientTop; // 非IE为0,IE为2
document.documentElement.clientLeft; // 非IE为0,IE为2
functiongGetRect (element) {
var rect = element.getBoundingClientRect();
var top = document.documentElement.clientTop;
var left= document.documentElement.clientLeft;
return{
top : rect.top - top,
bottom : rect.bottom - top,
left : rect.left - left,
right : rect.right - left
}
}
例如:cartcontrol.vue子组件和goods.vue父组件
// 子组件
<template>
<div class="cartcontrol">
<transition name="move">
<div class="cart-decrease" v-show="food.count>0" @click.stop.prevent="decreaseCart">
<span class="inner icon-remove_circle_outline"></span>
</div>
</transition>
<div class="cart-count" v-show="food.count>0">{{food.count}}</div>
<div class="cart-add icon-add_circle" @click.stop.prevent="addCart"></div>
</div>
</template>
<script>
import Vue from 'vue'
export default {
name: 'carcontrol',
...
methods: {
addCart (event) {
if (!this.food.count) {
Vue.set(this.food, 'count', 1);
} else {
this.food.count++;
}
// 使用$emit使得父组件能够监听到子组件的click事件addCart,自定义cart-add事件
this.$emit('cart-add', event.target);
},
...
}
}
<script/>
// 父组件goods.vue部分代码
// 使用cart-add自定义事件来控制getAdd方法,可以理解为子组件操作父组件的方法。
<div class="cartcontrol-wrapper">
<cartcontrol :food="food" @cart-add="getAdd"></cartcontrol>
</div>
// goods.vue的js的methods中getAdd方法
getAdd (el) {
this.$nextTick(() => {
this.$refs.shopcart.drop(el);
});
}
说明:本项目中在headTop组件中使用具名插槽,作用是可以将不同的内容放入到已有的头部组件中。具体例子如下:
// 下面是headtop.vue文件中模板内容,下面有三个具名插槽,具体讲一下name="city"的插槽。
<template>
<header class='headTop'>
<slot name='logo'></slot>
<slot name='search'></slot>
<span class="headGoback" v-if="goBack" @click="$router.go(-1)">
<i class="icon-previewleft"></i>
</span>
<router-link :to="userInfo? '/profile':'/login'" v-if='signinUp' class="headLogin">
<span class="userAvatar" v-if="userInfo">
<i class="icon-denglu-copy"></i>
</span>
<span class="loginSpan" v-else>登录|注册</span>
</router-link>
<section class="titleHead ellipsis" v-if="headTitle">
<span class="titleText">{{headTitle}}</span>
</section>
<slot name="city"></slot>
</header>
</template>
在home.vue中的使用如下:
// 使用了headtop组件,同时将slot="city"的<router-link>组件插入到headtop组件的<slot name="city"></slot>位置,简单来说<slot>相当于一个占位标识一样。
<template>
<div>
<head-top>
<router-link slot="city" to="/citySelect">{{cityName}}</router-link>
</head-top>
</div>
</template>