
rem以及自定义vue头标签
原来我以为的头标签就只是原装的<head></head>,然后在里面balabala写,结果今天才知道,它也是可以“我想怎么样就怎么样”的~
来左边跟我一起放个logo,来右边放一个编辑~
效果初览

先来认识rem
为了适应不同的显示器,我们用px单位可想而知是不够的。所以我们用rem做到所有设备自适应。rem是css的相对单位。
在vue单页项目中,如何做到设备自适应?
肯定是不能写死的,用动态,如:16rem=100%,这个就是动态宽度的100%。
16其实就是引入了栅格系统,将一行分为16列,比如设为8:8,它其实就是两列左右对等布局,4:4:4其实就是三列式布局。那么要怎么做呢?要对html的fontSize进行一个计算。
创建一个vue项目ele
新建的项目只需要勾选babel和router即可。
先从rem开始
在main.js中,先导入我们要的rem。
import './config/rem'
大型项目中组件如何兼容? rem是第一项要考虑的。
项目配置目录cofig
src/config/rem.js,在这里手动计算rem。
(function(doc, win){
var docEl = doc.documentElement, //获取html
// docEl.style.fontSize = '23.5px'; //先写死一个值测试一下
})(document, window) // 建一个闭包, 立即执行函数,传两个实参
- 闭包区域,把document和window传进去。闭包里面定义的变量,不会污染外界。我们在闭包里面动态计算我们html的fondSize。
- 利用width/16,使用栅格能力(css中的等比例计算)。
- 写一个专门计算rem的函数recalc:
(function(doc, win){
var docEl = doc.documentElement, //获取html
recalc = function() {
// 设备无差异计算出 16rem = 100% width
var clientWidth = docEl.clientWidth; //html的整个窗口的宽度
// console.log(clientWidth);
if (!clientWidth) return;
docEl.style.fontSize = 20 * clientWidth/320 + 'px';
// console.log(clientWidth);
};
doc.addEventListener('DOMContentLoaded', recalc, false);
// docEl.style.fontSize = '23.5px'; //先写死一个值测试一下
})(document, window) // 建一个闭包, 立即执行函数,传两个实参
- 来解释一下
doc.addEventListener('DOMContentLoaded', recalc, false);监听DOMContentLoaded事件。在vue里面也有生命周期,DOMContentLoaded事件比loaded事件先发生,因为loaded要等到所有图片都加载完,整个页面都加载完了才触发,而DOMContentLoaded事件只要DOM结构加载完了(html结构)加载完成了,就会触发。此时我们就去计算fontSize的大小。,当它触发DOMContentLoaded事件的时候,我们去调用recalc函数,第三个参数为false。
关于第三个参数false:
false是事件监听中,到底是从外到内(捕获),还是从内到外(冒泡)的选择。为false是内层向外层执行(冒泡),为true是由外向内执行(捕获)。 - 为什么是
20 * clientWidth/320呢?
首先,通过clientWidth计算,整个宽度分成320份,再乘以20,刚好是16,也就是将clientWidth分成十六份。而对于320,是有可能在最初的设计稿中,是320的设计稿,(现在一般都是750的,它的1/2是375),之前的是640的,就是两倍于宽度,所以就是320,我们这里用老的设计稿的(640)。然后再凑出16。所以这里是20。 用一个盒子来检验一下效果:
<div style="width:16rem;height:2rem;background-color:red;"></div>

当设备变化的时候,如何实现自适应呢?
比如设备可能由纵向,变成横向,还有可能屏幕尺寸发生改变。那怎么做呢?
(function(doc, win){
var docEl = doc.documentElement, //获取html
// 是否换了方向,横屏? 调整窗口宽度->resize事件
resizeEvt = 'orientationchange' in win ? 'orientationchange': 'resize',
recalc = function() {
// 设备无差异计算出 16rem = 100% width
var clientWidth = docEl.clientWidth; //html的整个窗口的宽度
// console.log(clientWidth);
if (!clientWidth) return;
docEl.style.fontSize = 20 * clientWidth/320 + 'px';
// console.log(clientWidth);
// ?设备可能由纵向变模着拿 尺寸发生变化
win.addEventListener(resizeEvt, recalc, false);
};
doc.addEventListener('DOMContentLoaded', recalc, false);
// docEl.style.fontSize = '23.5px'; //先写死一个值测试一下
})(document, window) // 建一个闭包, 立即执行函数,传两个实参
- window对象中如果发生orientationchange窗口改变事件(横屏或竖屏),就返回orientationchange,否则返回resize事件(调整窗口宽度时)。
接下来看header组件
来到App.vue,先定义一个组件:
export default {
components: { //组件声明
// 不能与标签名字header重合,怎么解决?
// head-top是在页面上的标签名 Header是类名
"head-top": Header
}
}
将我们自定义的header封装:
import Header from './components/header/header'; //封装这个组件
构建components/header/header组件
在components下建一个header/header.vue 下面先展示一个错误的:
<template>
<header id="head_top">
header
</header>
</template>
- 我们的标签名字,不能跟html5的标签名字一样,因为会先解析html的标签。当名字不一样时,解析时它就不会去找html标签,而是去组件声明里面找组件,所以写成
"head-top": Header,其中"head-top"就是我们在页面上写的占位符(标签名)。而Header其实是类名。 - 所以修改一下:
<template>
<head-top id="编辑地址">
header
</head-top>
</template>
给组件传递一个标题和logo
<head-top headTitle="编辑地址">
<span class="head_logo" >ele.me</span>
</head-top>
进行header的封装
来到header.vue
- 先打理标题参数。
<script>
export default {
props: ['headTitle']
}
</script>
- 将logo放置在左边。使用span和slot,在组件之间插槽插入。 App.vue中先放好了span,现在直接在header.vue中:
<header id="head_top">
<!-- slot在{{headTitle}}前面,所以就插好了 用一个name -->
<slot name="logo"></slot>
<!-- section是一个区块 这里写了两个类名 -->
<!--用户传了headTitle我们才输出,否则不输出-->
<section class="title_head ellipsis" v-if="headTitle">
<span class="title_text">
{{headTitle}}
</span>
</section>
</header>
- 写点样式。 header.vue中:
<style lang="styl" scoped>
@import "../../style/mixin.styl";
/* $blue是定义的stylus变量
position fixed固定定位*/
#head_top
background-color $blue
position fixed
z-index 100
left 0
top 0
wh(100%, 1.95rem)
.title_head
center()
width 50%
color #fff
text-align center
.title_text
sc(0.8rem, #fff)
text-align center
font-weight bold
.head_goback
left 0.4rem
wh(0.6rem, 1rem)
line-height 2.2rem
margin-left 0.4rem
</style>
我们用的是stylus,所以记得先install一下,在终端中yarn add stylus stylus-loader。
import导入样式文件时一定一定记得加分号!
4. 在这里面我们复用了样式,写在了mixin.styl文件中,注意看哦!
<!--定义了一个超级变量blue-->
$blue = #3190e8;
wh($width, $height)
width $width
height $height
center()
top 50%
left 50%
position absolute
transform translate(-50%, -50%)
sc($size, $color)
font-size $size
color $color
ct()
top 50%
position absolute
transform translateY(-50%)
再加上右边的编辑
同样用slot方式插在标题右边:
<section class="title_head ellipsis" v-if="headTitle">
<span class="title_text">
{{headTitle}}
</span>
</section>
<slot name="edit"></slot>

- 解释一下slot,因为我们插入的slot比较多,放左边还是右边就容易被夹在一起。那么如何区分呢?给slot加上name就好啦~(具名插槽) 然后<span>中就写成这样: App.vue
<head-top head-title="编辑地址" go-back="true">
<span class="head_logo" slot="logo">ele.me</span>
<!-- slot的name来区分编辑和logo的不同位置 -->
<span class="edit" slot="edit">编辑</span>
</head-top>
最后加上一个返回上一步
header.vue中
<!-- section是一个区块 -->
<section class="head_goback" v-if="goBack" @click="$router.go(-1)">
<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" version="1.1">
<polyline points="12,18 4,9 12,0" style="fill:none;stroke:rgb(255,255,255);stroke-width:2"/>
</svg>
</section>

其中
@click="$router.go(-1)"可以实现单击后返回上一层。
这里是结尾
简单的一个关于rem,和header,以及css模块化的小例子就分享到这了,喜欢的点个start~
github戳这里