Zou-Wang
返回顶部

个人技术博客——vue的响应式布局

技术描述

  • 这个技术用来干嘛?
    • 响应式布局用于使页面在不同的设备上都能有一个正常的样式显示,支持用户不同显示规格的设备上访问页面且仍有良好体验。
  • 为什么要学这个?
    • 随着移动端的普及,现在的页面最基础也需要达到PC端和手机端的样式能够让人在接受的程度。如果用户手机打开页面整个样式崩溃了,那他也没有理由继续选用你的应用了。
  • 技术难点在哪?
    • 页面的CSS不能再写死了,要使用百分比,或者直接使用flex弹性布局(我自己采用了这种方式)
    • 要多写好多类的样式,搞清楚类的包含关系,这里我推荐可以简单学一下CSS的预处理语言包括.less和.sass,这样写CSS的嵌套关系、参数关系都会更加清楚

技术详述

背景以及环境

  • 当前使用方法仅限于vue项目开发时使用(由于vue要求使用webpack所以下面不再赘述)
  • 当前方法是vueCLI4.0版本下使用,vue没有保证完全向前兼容,但是基础的方法应该不会改变。
  • 当前方法写CSS的时候使用了.less的预处理语言,如果查看代码有困难画3-5分钟熟悉一下预处理语言即可看懂。
  • 当前方法使用vuex本地化保存窗口大小,如果你想使用localstorage保存原理是类似的。
  • 当前方法使用了v-bind的绑定样式逻辑编写,如果还有其他实现方法欢迎讨论。

流程图

1.基础类的CSS样式

<div>
    <div :class="personInfoClass">
        <div class="Avatar">
            <el-avatar
                    :size="180"
                    :src="avatarUrl">
            </el-avatar>
        </div>
        
……………………

这里只展示一个基础的div。此处将外围DIV的class通过v-bind 动态绑定了一个personInfoClass的类名。此处建议将personInforClass作为computed属性配置,否则每次都重新判断会导致页面效率降低。

computed:{
  personInfoClass(){
    if(this.windowWidth > 500) {
      return "PersonInfo"
    } else {
      return "mobile_person_info_content"
    }
  },
}

这里可以看到配置的personInfoClass的类会返回两种情况,一种是在窗口大于500px的时候返回PersonInfo类,一种是在窗口小于500px的时候调用我们移动端的类。那么显然的,在一般情况下都会调用PersonInfo类,所以我们在vue对应组件内使用局部性CSS写好这部分基础类的样式

<style lang="less" scoped>
    @import "~@/CSS/Common.less";
    .PersonInfo {
        .setSize(925px, 230px);
        padding: 20px 20px;
        background-color: white;
        display: flex;
        justify-content: right;
        .Avatar {
            display: flex;
            flex-direction: column;
            justify-content: center;
        }
        .UserName {
            display: flex;
            font-size: 32px;
        }
    }
}

这样项目就保证了这个div在窗口>500px的时候使用该类的样式。

2.编写其他情况的CSS样式

那么另一个移动端的CSS样式写在哪里呢?

我们新建一个CSS文件下存放我们的mobile状态的CSS,命名为mobile.less(我这里还有小窗的样式,所以还有smallWIndow.less)

我们刚才命名的小窗下的类名为mobile_person_info_content,所以我们这里直接写一个对应的类样式

.mobile_person_info_content {
  width: calc(100% - 20px);
  margin: 20px auto;
  background-color: white;
  display: flex;
  flex-direction: column;
  justify-content: center;
  .Avatar{
    display: flex;
    justify-content: center;
    flex-direction: row;
  }
  .UserName {
    display: flex;
    justify-content: center;
    flex-direction: row;
    font-size: 32px;
  }
}

在你的需要引用的地方引入这个.less文件即可,我这里由于整个程序都需要适应配置,所以全局引入在了最顶端的根组件CSS内。当然既然是全局引入,这里不需要加scoped限定范围。

<style lang="less">
    @import "~@/CSS/mobile.less";
    @import "~@/CSS/smallWindow.less";
    #app {
        font-family: Avenir, Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
    }
}

这样在全局范围内的vue就可以使用我们在这两个文件内写的类样式了。

3.监控窗口大小

当我们完成了类的配置之后,我们需要监控窗口大小,让computed属性中的类可以进行切换,来使得div的绑定类切换,进而通过改变类的样式实现不同情况下的样式显示。

首先在vuex中增加窗口大小的state。(关于vuex的引入和属性的操作不做说明了)

const state = {
  screenWidth:document.documentElement.clientWidth, //屏幕宽度
  screenHeight:document.documentElement.clientHeight, //屏幕高度
}

与上面类似的,如果需要局部引入则在对应的组件内使用下述方法即可,如果要全局引入,按部就班就行。

在最顶层的根组件下使用mounted生命周期函数,用dom原生的window.onresize监听窗口的大小变化。(为什么全局只在根组件引入呢?因为整个程序周期中只允许一个window.onresize监听,此外多余的都是没有反应的,所以如果你在多个组件重复引入window.onresize是没有作用的,只能通过根目录的监听使得整个项目都能监听到窗口大小)

mounted() {
  let that = this;
  window.onresize=function() {
    that.$store.state.screenWidth = document.documentElement.clientWidth; //窗口宽度
    that.$store.state.screenHeight = document.documentElement.clientHeight; //窗口高度
  }
}

随后的,就需要监听这个vuex里的窗口大小参数了,在需要的组件内使用watch函数监听参数,比如我这里就是刚才的div组件中。这里的windowWidth已经定义在data中,这里不做演示了

watch: {
  '$store.state.screenWidth': function (val) { //监听屏幕宽度变化
    this.windowWidth = val;
  }
},

可能会遇到的问题

  1. 窗口一打开样式不正确

可能是因为你在data中的windowWidth没有赋初值,需要在窗口建立的时候就让它获得初始的窗口大小数值,否则无法进入判断循环。

data() {
  return {
    windowWidth:document.documentElement.clientWidth
  }
}
  1. 切换时只有一部分样式切换了,另一部分样式没有切换

要注意你的样式的嵌套,有可能切换的时候只有被嵌套包含的部分完成了切换,而在嵌套外的没有完成。

.mobile_person_info_content {
  width: calc(100% - 20px);
  margin: 20px auto;
  background-color: white;
  display: flex;
  flex-direction: column;
  justify-content: center;
  .Avatar{
    display: flex;
    justify-content: center;
    flex-direction: row;
  }
}
.UserName {
    display: flex;
    justify-content: center;
    flex-direction: row;
    font-size: 32px;
  }

比如上文的样式如果写成这样。则.UserName的类的样式是不会改变的,因为优先级的原因,vue会优先使用局部的同名CSS样式。类似的,如果你还有其他的全局.CSS样式,你也需要保证把所有要修改样式的类都嵌套进去。否则全局的样式也会被局部的同名CSS样式覆盖。

3.样式完全没有改变

不要用严格的属性比如px来限定窗口,使用百分比属性或者相对属性来进行样式编写。尽量使用flex的弹性布局,它本身就包含相对的布局。如果使用了px来进行限定也不要影响到相对的布局。

总结

最终响应式配置的逻辑链就形成了,根组件事实监听窗口大小->将窗口大小保存在本地->组件监听本地的窗口大小数据变化->赋值给组件参数->组件参数变化,进入切换类的判断->根据判断决定返回的类名->通过类名在CSS中找到对应的样式->样式改变。

参考博客

怎样能在大小不同的屏幕上显示不同的样式?

概述:简单介绍了一下如何显示样式的理论知识。

如何在vue中监听窗口大小?

概述:实际操作了vue中监听窗口大小的方法,包括了局部引入和全局引入

vue框架响应式布局

概述:实现时提供了一个思路,按照v-if或者v-show控制,后续经过思考采取了绑定类的方式进行切换。

posted @ 2020-06-15 13:37  Pcy潘晨宇  阅读(19633)  评论(1编辑  收藏  举报