版本:

  1. node 14
  2. @vue/cli 4.0.5
  3. package.json配置 “@vue/cli-service”: “^4.1.0”,

html-loader - 解决模板字符串

下载 : npm install —save-dev html-loader
版本 : “html-loader”: “^0.5.5”,
解决在写视口标签时使用模板字符串和变量html-plugin无法处理的问题

  1. //window.devicePixelRatio告诉浏览器应使用多少屏幕实际像素来绘制单个CSS像素 返回1|2|3|?
  2. let scale = 1.0 / window.devicePixelRatio;
  3. //设置视口标签
  4. let text = `<meta name="viewport" content="width=device-width, initial-scale=${scale}, maximum-scale=${scale}, minimum-scale=${scale}, user-scalable=no">`;
  5. document.write(text);

由于是vue-cli创建的没有webpack.config,所以新建一个vue.config配置html-loader

  1. module.exports = {
  2. configureWebpack: {//自定义webpack配置
  3. module: {
  4. rules: [
  5. {
  6. test: /\.(html)$/,
  7. exclude: /node_modules/,
  8. use: {
  9. loader: 'html-loader',
  10. options: {
  11. minimize: true
  12. }
  13. }
  14. }
  15. ]
  16. }
  17. },
  18. }

postcss-pxtorem - 实现自动将px转换成rem

下载 : npm i -D postcss-pxtorem
版本 : “html-loader”: “^0.5.5”,
同时新增配置: postcss.config.js

  1. module.exports = {
  2. plugins: {
  3. autoprefixer: {},
  4. 'postcss-pxtorem': {
  5. rootValue: 100, // 根元素字体大小
  6. // propList: ['width', 'height'], //只对这两个属性转换rem,因为有很多标签 所以还是用 *
  7. //,如果指向排斥某一标签 则将px改为Px(variable.scss)
  8. propList: ['*']
  9. }
  10. }
  11. }

webpack - 实现CSS3/ES678语法的兼容

.browserslistrc 文件

  1. ie >= 8
  2. Firefox >= 3.5
  3. chrome >= 35
  4. opera >= 11.5

fastclick - 解决移动端100~300ms的点击事件延迟问题

下载: npm install fastclick
版本: @1.0.6

main.js

  1. import fastclick from 'fastclick'
  2. //解决移动端点击延迟
  3. fastclick.attach(document.body)

scss - 初始化默认的全局样式

  1. 1).base.scss - 全局标签属性配置(html, body) 该文件引入234 main.js引入该组件<br /> 2).mixin.scss - 各种函数(bg_color修改背景颜色)<br /> 3).variable.scss - 定义字体大小&颜色($font_samll:12Px;)<br /> 4).rest.scss - 初始化标签样式(ol,ul{list-style:none})

注意点: 在移动端开发中, 一般情况下我们不需要让字体大小随着屏幕尺寸的变化而变化(mixin.scss - font_dpr())
由于我们是通过视口缩放来适配移动端的, 所以我们不能直接设置字体大小, 否则字体大小就会随着屏幕尺寸的变化而变化

路径: assets/css/xx.scss

base.scss

@import "./reset.scss";
@import "./variable.scss";
@import "./mixin.scss";

html, body{
  width: 100%;
  height: 100%;
  overflow: hidden;
  // 解决IScroll拖拽卡顿问题
  touch-action: none;
}
body{
  @include font_size($font_medium);
  font-family: Helvetica,sans-serif,STHeiTi;
}
img{
  vertical-align: bottom;
}

mixin.scss

@import "./variable.scss";
/*根据dpr计算font-size*/
@mixin font_dpr($font-size){
  font-size: $font-size;
  // 根据像素比缩放字体大小 达到视口变化而字体不变
  [data-dpr="2"] & { font-size: $font-size * 2;}
  [data-dpr="3"] & { font-size: $font-size * 3;}
}
/*通过该函数设置字体大小,后期方便统一管理;*/
@mixin font_size($size){
  @include font_dpr($size);
}
// 不换行
@mixin no-wrap(){
  text-overflow: ellipsis;
  overflow: hidden;
  white-space: nowrap;
}
// 限制行数
@mixin clamp($row){
  overflow:hidden;
  text-overflow:ellipsis;
  display:-webkit-box;
  -webkit-box-orient:vertical;
  -webkit-line-clamp:$row;
}

//根据属性选择器设置背景颜色
@mixin bg_color(){
  background: $background-color-theme;
  [data-theme=theme1] & {
    background: $background-color-theme1;
  }
  [data-theme=theme2] & {
    background: $background-color-theme2;
  }
}

//不同主题和不同像素比下显示不同图片
//不同设置background:url() 这样background-size不起作用
@mixin bg_img($url) {
  [data-theme=theme] & {
    background-image: url($url + '_163.png');
  }
  [data-theme=theme1] & {
    background-image: url($url + '_qq.png');
  }
  [data-theme=theme2] & {
    background-image: url($url + '_it666.png');
  }
  background-size: cover;
  background-repeat: no-repeat;

  [data-theme=theme][data-dpr='2'] & {
    background-image: url($url + '_163@2x.png');
  }
  [data-theme=theme][data-dpr='3'] & {
    background-image: url($url + '_163@3x.png');
  }
  [data-theme=theme1][data-dpr='2'] & {
    background-image: url($url + '_qq@2x.png');
  }
  [data-theme=theme1][data-dpr='3'] & {
    background-image: url($url + '_qq@3x.png');
  }
  [data-theme=theme2][data-dpr='2'] & {
    background-image: url($url + '_it666@2x.png');
  }
  [data-theme=theme2][data-dpr='3'] & {
    background-image: url($url + '_it666@3x.png');
  }
}

//改变tabbar背景颜色
@mixin bg_sub_color(){
  background: $background-color-sub-theme;
  [data-theme=theme1] & {
    background: $background-color-sub-theme1;
  }
  [data-theme=theme2] & {
    background: $background-color-sub-theme2;
  }
}

//改变tabbar字体颜色
@mixin font_color(){
  color: $font-color-theme;
  [data-theme=theme1] & {
    color: $font-color-theme1;
  }
  [data-theme=theme2] & {
    color: $font-color-theme2;
  }
}

//改变tabbar字体激活线颜色
@mixin font_active_color(){
  color: $font-active-color-theme;
  [data-theme=theme1] & {
    color: $font-active-color-theme1;
  }
  [data-theme=theme2] & {
    color: $font-active-color-theme2;
  }
}

//tabbar下划线
@mixin border_color(){
  border-color: $border-color-theme;
  [data-theme=theme1] & {
    border-color: $border-color-theme1;
  }
  [data-theme=theme2] & {
    border-color: $border-color-theme2;
  }
}

variable.scss

//字体定义规范
$font_samll:12Px;
$font_medium_s:13Px;
$font_medium:15Px;
$font_large:17Px;

// 背景颜色规范(主要)
$background-color-theme: #d43c33;//背景主题颜色默认(网易红)
$background-color-theme1: rgba(34,213,156,1);//背景主题颜色1(QQ绿)
$background-color-theme2: #333;//背景主题颜色2(夜间模式)

// 背景颜色规范(次要)
$background-color-sub-theme: #f5f5f5;//背景主题颜色默认(网易红)
$background-color-sub-theme1: #f5f5f5;//背景主题颜色1(QQ绿)
$background-color-sub-theme2: #444;//背景主题颜色2(夜间模式)

// 字体颜色规范(默认)
$font-color-theme : #666;//字体主题颜色默认(网易)
$font-color-theme1 : #666;//字体主题颜色1(QQ)
$font-color-theme2 : #ddd;//字体主题颜色2(夜间模式)

// 字体颜色规范(激活)
$font-active-color-theme : #d43c33;//字体主题颜色默认(网易红)
$font-active-color-theme1 : rgba(34,213,156,1);//字体主题颜色1(QQ绿)
$font-active-color-theme2 : #ffcc33;//字体主题颜色2(夜间模式)

// 边框颜色
$border-color-theme : #d43c33;//边框主题颜色默认(网易)
$border-color-theme1 : rgba(34,213,156,1);//边框主题颜色1(QQ)
$border-color-theme2 : #ffcc33;//边框主题颜色2(夜间模式)

rest.scss

/*
YUI 3.18.1 (build f7e7bcb)
Copyright 2014 Yahoo! Inc. All rights reserved.
Licensed under the BSD License.
http://yuilibrary.com/license/
*/

html{color:#000;background:#FFF}
body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0}
table{border-collapse:collapse;border-spacing:0}
fieldset,img{border:0}
address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal}
ol,ul{list-style:none}
caption,th{text-align:left}
h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal}
q:before,q:after{content:''}
abbr,acronym{border:0;font-variant:normal}
sup{vertical-align:text-top}
sub{vertical-align:text-bottom}
input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit;*font-size:100%}
legend{color:#000}
a{text-decoration: none}
#yui3-css-stamp.cssreset{display:none}

axios

下载: npm install axios
版本: @0.19.0

network.js - 全局配置和调用方法定义

路径 : src/api/network.js

import axios from 'axios'
import Vue from 'vue'

// 进行一些全局配置
axios.defaults.baseURL = 'http://192.168.0.105:3000/'
axios.defaults.timeout = 5000

let count = 0 //记录请求次数
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
  // 在发送请求之前做些什么
  count++
  Vue.showLoading()
  return config
}, function (error) {
  // 对请求错误做些什么
  Vue.hiddenLoading()
  return Promise.reject(error)
})

// 添加响应拦截器
axios.interceptors.response.use(function (response) {
  // 对响应数据做点什么
  count--
  if (count === 0) {
    Vue.hiddenLoading()
  }
  return response
}, function (error) {
  // 对响应错误做点什么
  Vue.hiddenLoading()
  return Promise.reject(error)
})

// 封装自己的get/post方法
export default {
  get: function (path = '', data = {}) {
    return new Promise(function (resolve, reject) {
      axios.get(path, {
        params: data
      })
        .then(function (response) {
          resolve(response.data)
        })
        .catch(function (error) {
          reject(error)
        })
    })
  },
  post: function (path = '', data = {}) {
    return new Promise(function (resolve, reject) {
      axios.post(path, data)
        .then(function (response) {
          resolve(response.data)
        })
        .catch(function (error) {
          reject(error)
        })
    })
  },
  all: function (list) {
    return new Promise(function (resolve, reject) {
      axios.all(list)
        .then(axios.spread(function (...result) {
          // 两个请求现在都执行完成
          resolve(result)
        }))
        .catch(function (err) {
          reject(err)
        })
    })
  }
}

index.js - 封装接口

路径 : src/api/index.js

// 这个JS文件就是专门用于管理请求各种接口地址的
import Network from './network'

// 封装各种接口请求
export const getBanner = () => Network.get('banner?type=2'); //获取 banner( 轮播图 ) 数据
//获取歌手分类(欧美/华语...)
//letter A-Z
export const getLetterArtists = (letter) => {
    return new Promise(function (resolve, reject) {
        let letterArtists = []
        Network.all([
        Network.get(`artist/list?type=-1&limit=5&area=7&initial=${letter}`),
        Network.get(`artist/list?type=-1&limit=5&area=96&initial=${letter}`),
        ])
        .then(function (result) {
            //console.log(result)
            result.forEach(function (item) {
              letterArtists.push(...item.artists)
            })
             //console.log(letterArtists)
            resolve([...new Set(letterArtists)])
        })
        .catch(function (err) {
            reject(err)
        })
    })
  }

调用

import {getBanner} from '../api/index' 

created(){
      getBanner({ id:XX})
      .then((data) => {
        console.log(data)
      })
      .catch((err) => {
        console.log(err)
      })
}

vue-awesome-swiper - 轮播图

下载: npm install swiper vue-awesome-swiper —save
版本: @3.1.3
注意:

  1. 最新版本配置不同 - 按照官网
  2. v-if=”banners.length > 0”自动轮播到最后一页之后继续轮播
  3. 如果想覆盖swiper的样式, 那么style标签不能是scoped的, 否则无法覆盖 ```vue

<a name="sTAgB"></a>
# vue-lazyload - 图片懒加载 
下载: npm i vue-lazyload -S<br />版本: @1.3.3
<a name="sCYQZ"></a>
## main.js
```javascript
import VueLazyload from 'vue-lazyload'
import Loading from './plugin/loading/index'

Vue.use(Loading,{
  title:'正在加载...'
})

Vue.use(VueLazyload, {
  // 可以通过配置loading来设置图片还未加载好之前的占位图片
  loading: require('./assets/images/loading.png')
})

component

<img v-lazy="value.picUrl">

ScrollView - 拖拽功能组件(废弃)

下载: npm install iscroll
版本: @5.2.0

ScrollView.vue 配置 插槽slot / 方法

<template>
  <div id="wrapper" ref="wrapper">
    <slot></slot>
  </div>
</template>
<script>
import IScroll from 'iscroll/build/iscroll-probe'
export default {
  name: 'ScrollView',
  mounted () {
    this.iscroll = new IScroll(this.$refs.wrapper, {
      mouseWheel: true,
      scrollbars: false,//是否显示滚动条  
      probeType:3,//像素级XX 可以获取拖动距离像素
       // 解决拖拽卡顿问题
      scrollX: false,
      scrollY: true,
      // disablePointer: true,
      disableTouch: false,
      disableMouse: true
    });

    //MutationObserver元素JS,节点变化时(axios拿到数据)刷新iscroll 
    // 1.创建一个观察者对象
    /*
    MutationObserver构造函数只要监听到了指定内容发生了变化, 就会执行传入的回调函数
    mutationList: 发生变化的数组
    observer: 观察者对象
    * */
    let observer = new MutationObserver((mutationList, observer) => {
      this.iscroll.refresh()
    })
    // 2.告诉观察者对象我们需要观察什么内容
    let config = {
      childList: true, // 观察目标子节点的变化,添加或者删除
      subtree: true, // 默认为 false,设置为 true 可以观察后代节点
      attributeFilter: ['height', 'offsetHeight'] // 观察特定属性
    }
    // 3.告诉观察者对象, 我们需要观察谁, 需要观察什么内容
    /*
    config配置中变化则会触发observer函数
    第一个参数: 告诉观察者对象我们需要观察谁
    第二个参数: 告诉观察者对象我们需要观察什么内容
    * */
    observer.observe(this.$refs.wrapper, config)
  }, 
  methods:{
    //提供一个监听滚动距离的方法给外界
    scrolling (fn) {
      this.iscroll.on('scroll', function () {
        fn(this.y)
      })
    },
    //提供一个组件刷新方法
    refresh () {
      setTimeout(() => {
        this.iscroll.refresh()
      }, 100)
    },
    //提供一个组件滚动方法
    scrollTo (x, y, time) {
      this.iscroll.scrollTo(x, y, time)
    }
  }
}
</script>

<style lang="scss" scoped>
#wrapper{
  width: 100%;
  height: 100%;
}
</style>

component

  <template>
    <div class="recommend">
      <ScrollView>
        <div>
          //放组件
        </div>
      </ScrollView>
    </div>
  </template>

  <script>
  import ScrollView from '../components/ScrollView.vue'

  export default {
    name:"Recommend",
    components:{
      ScrollView
    }
  }
  </script>