1、css 基础样式集

​ 浏览器支持和理解的CSS规范不同,导致渲染页面时效果不一致,会出现很多兼容性问题。有两种解决方案。

1.1、一种是 css reset

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/* KISSY CSS Reset
理念:清除和重置是紧密不可分的
特色:1.适应中文 2.基于最新主流浏览器
维护:玉伯(lifesinger@gmail.com), 正淳(ragecarrier@gmail.com)
*/

/* 清除内外边距 */
body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, /* structural elements 结构元素 */
dl, dt, dd, ul, ol, li, /* list elements 列表元素 */
pre, /* text formatting elements 文本格式元素 */
fieldset, lengend, button, input, textarea, /* form elements 表单元素 */
th, td { /* table elements 表格元素 */
margin: 0;
padding: 0;
}

/* 设置默认字体 */
body,
button, input, select, textarea { /* for ie */
/*font: 12px/1 Tahoma, Helvetica, Arial, "宋体", sans-serif;*/
font: 12px/1 Tahoma, Helvetica, Arial, "\5b8b\4f53", sans-serif;
/* 用 ascii 字符表示,使得在任何编码下都无问题 */
}

h1 { font-size: 18px; /* 18px / 12px = 1.5 */ }
h2 { font-size: 16px; }
h3 { font-size: 14px; }
h4, h5, h6 { font-size: 100%; }

address, cite, dfn, em, var { font-style: normal; } /* 将斜体扶正 */
code, kbd, pre, samp, tt { font-family: "Courier New", Courier, monospace; } /* 统一等宽字体 */
small { font-size: 12px; } /* 小于 12px 的中文很难阅读,让 small 正常化 */

/* 重置列表元素 */
ul, ol { list-style: none; }

/* 重置文本格式元素 */
a { text-decoration: none; }
a:hover { text-decoration: underline; }

abbr[title], acronym[title] { /* 注:1.ie6 不支持 abbr; 2.这里用了属性选择符,ie6 下无效果 */
border-bottom: 1px dotted;
cursor: help;
}

q:before, q:after { content: ''; }

/* 重置表单元素 */
legend { color: #000; } /* for ie6 */
fieldset, img { border: none; } /* img 搭车:让链接里的 img 无边框 */
/* 注:optgroup 无法扶正 */
button, input, select, textarea {
font-size: 100%; /* 使得表单元素在 ie 下能继承字体大小 */
}

/* 重置表格元素 */
table {
border-collapse: collapse;
border-spacing: 0;
}

/* 重置 hr */
hr {
border: none;
height: 1px;
}
/* 让非ie浏览器默认也显示垂直滚动条,防止因滚动条引起的闪烁 */
html { overflow-y: scroll; }

1.2、一种是 Normalize.css

  1. 对于普通的 H5 项目,我们可以到其官网下载最新的 Normalize.css,然后在页面中引入使用。

  2. 对于 Vue.js 项目,可以先进入项目文件夹中执行如下命令安装:

1
npm install --save normalize.css
  1. 然后在 vue 的主文件中引入即可:
1
import 'normalize.css/normalize.css'

2、rem和em总结

  1. rem 单位翻译为像素值的时候是由 html 元素的字体大小决定的。此字体大小会被浏览器中字体大小的设置影响,除非显式的在 html 为 font-size 重写一个单位;

  2. em 单位转换为像素值的时候,取决于使用它们的元素的 font-size 的大小,但是有因为继承关系,所以比较复杂;

    • 优缺点
      • em 可以让我们的页面更灵活,更健壮,比起到处写死的 px 值,em 似乎更有张力,改动父元素的字体大小,子元素会等比例变化,这一变化似乎预示了无限可能;
      • em 做弹性布局的缺点还在于牵一发而动全身,一旦某个节点的字体大小发生变化,那么其后代元素都得重新计算。

3、css BEM(block__element--modifier)命名规则

1
<div class="docker__item--active"></div>

4、如何设置fantasize值为10px

  1. 横向纵向均缩放50%;
  2. center top为缩放中心,最终缩放位置为正确位置;
1
2
3
4
5
&__title {
font-size: .2rem;
transform: scale(.5, .5);
transform-origin: center top;
}

5、页面加载抖动问题

  1. 在web开发中,经常会遇到这样一个问题,比如一个宽度百分百,高度自适应的图片,在网速慢的情况下加载过程中会出现抖动的问题(未加载图片前容器的高度为0,图片加载完成后下面的内容会被挤下去);

  2. 这种问题如果是图片有固定高度,就不会出现加载抖动。但一般情况下,为了使图片不被拉伸,高度一般设为自适应,那么为了防止加载抖动,我们需要给图片提前占个位,这里使用css的padding-bottom百分比进行占位

示例代码如下

1
2
3
4
5
6
7
8
.banner {
height: 0;
overflow: hidden;
padding-bottom: 25.4%;
&__img {
width: 100%;
}
}
  1. padding-bottom实际上是提前占位了,这个容器的高度始终是0,高度为0还之所以能够显示内容是因为内容溢出在了padding-bottom上,这里的25.4%是图片的高/宽比例,切记是相对于父元素宽度的25.4%(此处即.banner的上一级),不是相对于自己的width。

6、vue/style的scoped属性(坑)

  • scoped原理:https://blog.csdn.net/qq_39043923/article/details/88687046

    1
    2
    <style lang="scss" scoped>
    </style>
    1. scoped属性达到了css作用域约束的目的;

    2. 给HTML的DOM节点加一个不重复data属性(形如:data-v-2311c06a)来表示他的唯一性;

    3. 在每句css选择器的末尾(编译后的生成的css语句)加一个当前组件的data属性选择器(如[data-v-2311c06a])来私有化样式;

    4. 如果组件内部包含有其他组件,只会给其他组件的最外层标签加上当前组件的data属性

7、 beforeEach/beforeEnter实现路由校验

  1. beforeEach 的使用
  • 可以整体对每个路由进行权限校验后跳转;
  • 点击登陆并成功登陆,localStorage 中种下 isLogin:localStorage.isLogin = true;
  • router/index 中使用 beforeEach 且判断 localStorage 中 isLogin 状态,达到校验路由是否切换的目的;
    1
    2
    3
    4
    5
    6
    router.beforeEach((to, from ,next) => {
    const { isLogin } = localStorage;
    const { name } = to;
    const isLoginOrRegister = (name === "Login" || name === "Register");
    (isLogin || isLoginOrRegister) ? next() : next({ name: 'Login'});
    })
  1. beforeEnter 的使用
  • 可以精确到对哪一个路由进行权限校验后跳转
  • 点击登陆并成功登陆,localStorage 中种下 isLogin:localStorage.isLogin = true;
  • router 配置中直接使用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const routes = [{
    {
    path: '/login',
    name: 'Login',
    component: () => import(/* webpackChunkName: "login" */ '../views/login/Login'),
    beforeEnter(to, from, next) {
    const { isLogin } = localStorage;
    isLogin ? next({ name: 'Home'}): next();
    }
    },
    }]

8、?.optional chaining语法

9、axios普通封装/创建一次实例封装

9.1、普通封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import axios from 'axios'

const instance = axios.create({
baseURL: 'https://www.fastmock.site/mock/ae8e9031947a302fed5f92425995aa19/jd',
timeout: 10000
})

export const get = (url, params = {}) => {
return new Promise((resolve, reject) => {
instance.get(url, { params }).then((response) => {
resolve(response.data)
}, err => {
reject(err)
})
})
}

export const post = (url, data = {}) => {
return new Promise((resolve, reject) => {
instance.post(url, data, {
headers: {
'Content-Type': 'application/json'
}
}).then((response) => {
resolve(response.data)
}, err => {
reject(err)
})
})
}

export const patch = (url, data = {}) => {
return new Promise((resolve, reject) => {
instance.patch(url, data, {
headers: {
'Content-Type': 'application/json'
}
}).then(response => {
resolve(response.data)
}).catch(err => {
reject(err)
})
})
}
  • 使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const useLoginEffect = (showToast) => {
const router = useRouter()
const data = reactive({ username: '', password: '' })

const handleLogin = async () => {
try {
const result = await post('/api/user/login', {
username: data.username,
password: data.password
})
if (result?.errno === 0) {
localStorage.isLogin = true
router.push({ name: 'Home' })
} else {
showToast('登陆失败')
}
} catch (e) {
showToast('请求失败')
}
}

const { username, password } = toRefs(data)
return { username, password, handleLogin}
}

9.2、只创建一次实例封装

封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import axios from 'axios'
import config from './config'

class Http {
constructor() {
this.instance = axios.create({
baseURL: config.baseURL,
timeout: 60000
})
}

get({ url, params = {} }) {
return new Promise((resolve, reject) => {
this.instance.get(url, {
params
})
.then((result) => {
resolve(result.data)
})
.catch((err) => {
reject(err)
})
})
}
}

export default Http

在根目录下的main.js中全局引入

1
2
import Http from '@/utils/http'
Vue.prototype.$http = new Http()

在普通组件中使用

1
2
3
4
5
6
7
8
9
let result = await this.$http.get({
url: this.url,
params: {
limit: this.limit,
offset: this.offset,
ct: this.city.name,
ci: this.city.id
}
})

10、setup函数主调度功能

  • setup函数的主调度功能,具体的业务实现都应摘出去封装;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export default {
name: 'Login',
components: { Toast },
// 职责就是告诉你,代码执行的一个流程
setup () {
const { show, toastMessage, showToast } = useToastEffect()
const { username, password, handleLogin } = useLoginEffect(showToast)
const { handleRegisterClick } = useRegisterEffect()

return {
username, password, show, toastMessage,
handleLogin, handleRegisterClick,
}
}
}

11、图片未加载完成时避免出现裂图展示的优化

  • v-show="item.imgUrl":其中 item.imgUrl 为图片地址
1
<ShopInfo :item="item" :hideBorder="true" v-show="item.imgUrl"/>