CSS 布局及技巧

布局

奇技

吸顶

1
2
3
4
5
.sticky-wrapper {
   position: sticky;
   top0;
   z-index99;
}

加载骨架屏

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
69
70
71
/*
* Variables
*/
:root {
--card-padding: 24px;
--card-height: 340px;
--card-skeleton: linear-gradient(lightgrey var(--card-height), transparent 0);
--avatar-size: 32px;
--avatar-position: var(--card-padding) var(--card-padding);
--avatar-skeleton: radial-gradient(circle 16px at center, white 99%, transparent 0
);
--title-height: 32px;
--title-width: 200px;
--title-position: var(--card-padding) 180px;
--title-skeleton: linear-gradient(white var(--title-height), transparent 0);
--desc-line-height: 16px;
--desc-line-skeleton: linear-gradient(white var(--desc-line-height), transparent 0);
--desc-line-1-width:230px;
--desc-line-1-position: var(--card-padding) 242px;
--desc-line-2-width:180px;
--desc-line-2-position: var(--card-padding) 265px;
--footer-height: 40px;
--footer-position: 0 calc(var(--card-height) - var(--footer-height));
--footer-skeleton: linear-gradient(white var(--footer-height), transparent 0);
--blur-width: 200px;
--blur-size: var(--blur-width) calc(var(--card-height) - var(--footer-height));
}

/*
* Card Skeleton for Loading
*/
.card {
width: 280px;
height: var(--card-height);
}
.card:empty::after {
content: "";
display: block;
width: 100%;
height: 100%;
border-radius: 6px;
box-shadow: 0 10px 45px rgba(0, 0, 0, 0.1);
background-image: linear-gradient(90deg, rgba(211, 211, 211, 0) 0, rgba(211, 211, 211, 0.8) 50%, rgba(211, 211, 211, 0) 100%), var(--title-skeleton), var(--desc-line-skeleton), var(--desc-line-skeleton), var(--avatar-skeleton), var(--footer-skeleton), var(--card-skeleton);
background-size: var(--blur-size), var(--title-width) var(--title-height), var(--desc-line-1-width) var(--desc-line-height), var(--desc-line-2-width) var(--desc-line-height), var(--avatar-size) var(--avatar-size), 100% var(--footer-height), 100% 100%;
background-position: -200% 0, var(--title-position), var(--desc-line-1-position), var(--desc-line-2-position), var(--avatar-position), var(--footer-position), 0 0;
background-repeat: no-repeat;
-webkit-animation: loading 1.5s infinite;
animation: loading 1.5s infinite;
}

@-webkit-keyframes loading {
to {
background-position: 350% 0, var(--title-position), var(--desc-line-1-position), var(--desc-line-2-position), var(--avatar-position), var(--footer-position), 0 0;
}
}

@keyframes loading {
to {
background-position: 350% 0, var(--title-position), var(--desc-line-1-position), var(--desc-line-2-position), var(--avatar-position), var(--footer-position), 0 0;
}
}
/*
* Demo Stuff
*/
body {
min-height: 100vh;
background-color: #FFF;
display: flex;
justify-content: center;
align-items: center;
}

利用 CSS 穿透覆盖默认样式

1
2
3
img {
pointer-events: none;
}

实现自定义原生 select 控件的样式

1
2
3
4
select {
/* 禁用原生的样式 */
-webkit-appearance: none;
}

文本溢出处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//单行
.single {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
//多行
.more {
display: -webkit-box !important;
overflow: hidden;
text-overflow: ellipsis;
work-break: break-all;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2; //指定行数
}

开启弹性滚动

1
2
3
4
body {
overflow: scroll;
-webkit-overflow-scrolling: touch;
}

注意:Android 不支持原生的弹性滚动,但可以借助第三方库 iScroll 来实现。

一像素边框设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.folder li {
position: relative;
padding: 5px;
}
.folder li + li:before {
position: absolute;
top: -1px;
left: 0;
content: " ";
width: 100%;
height: 1px;
border-top: 1px solid #ccc;
-webkit-transform: scaleY(0.5);
}

兼容 IE 浏览器的透明度处理

1
2
3
4
5
6
.ui {
width: 100%;
height: 100%;
opacity: 0.4;
filter: Alpha(opacity=40); //兼容IE浏览器的处理
}

常用的全屏居中 CSS 函数

1
2
3
4
5
body {
height: 100vh;
text-align: center;
line-height: 100vh;
}

Flex 布局 justify-content space-around 时 单个左居中

1
2
3
4
.container:after {
content: "";
flex: auto;
}

固定定位滚动

1
2
3
4
.container{
position: fixed;
overflow:scroll
}

扩大可点击区域

利用伪元素和定位达到鼠标移到边缘时候出现手型且可点击

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.expand-range {
position: relative;
}
.expand-range:after {
content: '';
position: absolute;
top: -10px; right: -10px; bottom: -10px; left: -10px;
}

/* 推荐使用scss */
@mixin expand-range($top: -10px, $right: $top, $bottom: $top, $left: $right, $position: relative) {
position: $position;
&:after {
content: '';
position: absolute;
top: $top;
right: $right;
bottom: $bottom;
left: $left;
}
}
//使用:.test { @include expand-range($top: -5px, $position: absolute) }

巧用层叠上下文

利用层叠上下文和 z-index: -1 特性实现伪元素覆盖背景同时又不会遮挡文字。
梯形、菱形、平行四边形

1
2
3
4
5
6
7
8
9
10
div:after {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: -1;
background: cyan;
}

边框内圆角

利用伪元素实现圆角矩形并叠加在父元素的背景之上文字之下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
div {
position: relative;
z-index: 1;
height: 200px;
padding: 10px;
background: #333;
}

div::after {
content: '';
position: absolute;
left: 10px;
top: 10px;
right: 10px;
bottom: 10px;
z-index: -1;
border-radius: 5px;
background: cyan;
}

自适应宽度

折角

切角

高度坍塌

1
2
3
4
5
6
7
8
.container{
display:block;
content:'';
height:0;
clear:both;
overflow:hidden;
visibility: hidden;
}

注意外边距折叠

只使用一个方向的 margin

更好的表格边框

1
2
/* 删除所有的双倍边框 */
border-collapse: collapse;

短横线命名

1
.footer-column-left {}

不要重复设置

1
2
3
4
/* font 会被继承 */
html {
font: normal 16px/1.4 sans-serif;
}

使用transform属性来创建动画

1
2
3
4
5
6
7
8
9
.ball {
left: 50px;
transition: 0.4s ease-out;
}

/* 建议 */
.ball.slide-out {
transform: translateX(450px);
}

使用AutoPrefixer达到更好的兼容性

autoprefixer

压缩CSS文件

Caniuse

检查使用的属性是否得到了广泛的支持

验证

Stylelint


相关资料
12 个实用的前端开发技巧总结
【CSS】470- 是时候开始用 CSS 自定义属性了
响应式布局新方案
20个让你效率更高的CSS代码技巧
深入理解CSS background-blend-mode的作用机制
一文梳理CSS必会知识点
收藏!40 个 CSS 布局技巧
【第2022期】不定宽溢出文本适配滚动
奇妙的 CSS MASK
【CSS】333- 使用CSS自定义属性做一个前端加载骨架
《CSS揭秘》实用技巧总结
工作中常用的css整理

云开发 Serverless 从入门到实战

Serverless

指构建和运行不需要服务器管理的应用程序的概念。
Serverless 是 FaaS 和 BaaS 的结合。Serverless = FaaS + BaaS。

FaaS(Function as a Service) 就是一些运行函数的平台,比如阿里云的函数计算、AWS 的 Lambda 等。
BaaS(Backend as a Service)则是一些后端云服务,比如云数据库、对象存储、消息队列等。利用 BaaS,可以极大简化我们的应用开发难度。

Serverless 的主要特点有:

  • 事件驱动
  • 函数在 FaaS 平台中,需要通过一系列的事件来驱动函数执行。
  • 无状态
  • 因为每次函数执行,可能使用的都是不同的容器,无法进行内存或数据共享。如果要共享数据,则只能通过第三方服务,比如 Redis 等。
  • 无运维
  • 使用 Serverless 我们不需要关心服务器,不需要关心运维。这也是 Serverless 思想的核心。
  • 低成本
  • 使用 Serverless 成本很低,因为我们只需要为每次函数的运行付费。函数不运行,则不花钱,也不会浪费服务器资源

前端工程师基于 Serverless 去写后端,最好也需要具备一定的后端知识。涉及复杂的后端系统或者 Serverless 不适用的场景,还是需要后端开发,后端变得更靠后了。

Serverless 开发最佳实践

将业务逻辑和函数依赖的 FaaS(如函数计算) 和 BaaS (如云数据库)分离。

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
class Users {
constructor(db, mailer) {
this.db = db;
this.mailer = mailer;
}
save(email, callback) {
const user = {
email: email,
created_at: Date.now()
}
this.db.saveUser(user, function (err) {
if (err) {
callback(err);
} else {
this.mailer.sendWelcomeEmail(email);
callback();
}
});
}
}
module.exports = Users;

const db = require('db').connect();
const mailer = require('mailer');
const Users = require('users');
let users = new Users(db, mailer);
module.exports.saveUser = (event, context, callback) => {
users.save(event.email, callback);
};

将业务逻辑全都放在了 Users 这个类里面,Users 不依赖任何外部服务。

函数的性能

当驱动函数执行的事件到来的时候,首先需要下载代码,然后启动一个容器,在容器里面再启动一个运行环境,最后才是执行代码。前几步统称为冷启动(Cold Start)。传统的应用没有冷启动的过程。

当第一次请求(驱动函数执行的事件)来临,成功启动运行环境并执行函数之后,运行环境会保留一段时间,以便用于下一次函数执行。这样就能减少冷启动的次数,从而缩短函数运行时间。当请求达到一个运行环境的限制时,FaaS 平台会自动扩展下一个运行环境。

执行上下文重用

1
2
3
4
5
6
const mysql = require('mysql');
// 初始化数据库连接 const connection = mysql.createConnection({ /* ... */ });
connection.connect();
module.exports.saveUser = (event, context, callback) => {
connection.query('...');
};

这样就只有第一次运行环境启动的时候,才会初始化数据库连接。后续请求来临、执行函数的时候,就可以直接利用执行上下文中的 connection,从而提高后续高函数的性能。
大部分情况下,通过牺牲一个请求的性能,换取大部分请求的性能,是完全可以够接受的。

给函数预热

通过主动调用函数的方式,隔一段时间就冷启动一个运行环境,这样就能使得其他正常的请求都是热启动,从而避免冷启动时间对函数性能的影响。

需要注意:

  • 不要过于频繁调用函数,至少频率要大于 5 分钟
  • 直接调用函数,而不是通过网关等间接调用
  • 创建专门处理这种预热调用的函数,而不是正常业务函数

如果你的业务允许“牺牲第一个请求的性能换取大部分性能”,那也完全不必使用该方案,

阅读更多

JavaScript 函数及开发技巧

生成一周时间

1
2
3
4
5
function getWeekTime(){
return [...new Array(7)].map((j,i)=> new Date(Date.now()+i*8.64e7).toLocaleDateString())
}
getWeekTime();
// 输出效果 ["2020/7/21", "2020/7/22", "2020/7/23", "2020/7/24", "2020/7/25", "2020/7/26", "2020/7/27"]

判断空对象

1
2
let a = {}
JSON.stringify(a) == '{}';

类型判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* @param {any} target
* @param {string} type
* @return {boolean}
*/
function isType(target, type) {
let targetType = Object.prototype.toString.call(target).slice(8, -1).toLowerCase()
return targetType === type.toLowerCase()
}
isType([], 'Array') // true
isType(/\d/, 'RegExp') // true
isType(new Date(), 'Date') // true
isType(function(){}, 'Function') // true
isType(Symbol(1), 'Symbol') // true
阅读更多

Nuxt 从入门到实践

Nuxt 常用配置项

配置 IP 和端口

1
2
3
4
5
6
7
8
9
10
//  package.json
"scripts": {
// ...
},
"config":{
"nuxt":{
"host":"127.0.0.1",
"port":"1818"
}
},

配置全局CSS

1
2
3
4
5
6
7
//  nuxt.config.js
head: {
// ...
},
css: [
'~assets/css/normailze.css',
],

配置webpack的loader

在nuxt.config.js里是可以对webpack的基本配置进行覆盖的

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
//  nuxt.config.js
build: {
loaders:[
{
test:/\.(png|jpe?g|gif|svg)$/,
loader:"url-loader",
query:{
limit:10000,
name:'img/[name].[hash].[ext]'
}
}
],

/*
** Run ESLint on save
*/
extend (config, { isDev, isClient }) {
if (isDev && isClient) {
config.module.rules.push({
enforce: 'pre',
test: /\.(js|vue)$/,
loader: 'eslint-loader',
exclude: /(node_modules)/
})
}
}
}

Nuxt 路由

Nuxt.js 路由会根据 pages 目录中提供的Vue文件自动为您生成配置。

路由跳转传参

1
2
3
4
5
6
7
<template>
<!--路由跳转传参-->
<nuxt-link :to="{name:'news',params:{newsId:3306}}">NEWS</nuxt-link>

<!--路由接收参数-->
<p>NewsID:{{$route.params.newsId}}</p>
</template>

动态路由跳转传参

以下画线为前缀的Vue文件就是动态路由,然后在文件里边有 $route.params.id来接收参数

1
2
3
<template>
<nuxt-link :to="{name:'news-id',params:{id:123}}">News-1</nuxt-link>
</template>

动态参数校验

1
2
3
4
5
6
7
8
<script>
export default {
validate ({ params }) {
// Must be a number
return /^\d+$/.test(params.id)
}
}
</script>

路由切换动画

全局动画

1
2
3
4
5
6
7
/* assets/css/main.css */
.page-enter-active, .page-leave-active {
transition: opacity 2s;
}
.page-enter, .page-leave-active {
opacity: 0;
}
1
2
// nuxt.config.js
css:['assets/css/main.css'],

单页动画

1
2
3
4
5
6
7
8
9
/* assets/css/main.css */
.test-enter-active, .test-leave-active {
transition: all 2s;
font-size:12px;
}
.test-enter, .test-leave-active {
opacity: 0;
font-size:40px;
}
1
2
3
4
5
<script>
export default {
transition:'test'
}
</script>

Views 布局

默认模板

根目录下创建一个 app.html 就可实现

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html lang="en">
<head>
<!-- 读取的是nuxt.config.js里的信息 -->
{{ HEAD }}
</head>
<body>
<p>jspang.com 技术胖的博客</p>
<!-- pages文件夹下的主体页面 -->
{{ APP }}
</body>
</html>

默认布局

1
2
3
4
5
6
7
<!-- layouts/default.vue -->
<template>
<div>
<p>JSPang.com 技术胖的博客</p>
<nuxt/>
</div>
</template>

自定义布局

例如:创建博客布局 在layouts 新建一个 blog.vue。

1
2
3
4
5
6
7
<!--layouts/blog.vue-->
<template>
<div>
<div>My blog navigation bar here</div>
<Nuxt />
</div>
</template>

使用博客布局

1
2
3
4
5
6
7
8
9
10
<!--pages/posts.vue-->
<template>
<!-- Your template -->
</template>
<script>
export default {
layout: 'blog'
// page component definitions
}
</script>

自定义错误页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!--layouts/error.vue-->
<template>
<div>
<h1 v-if="error.statusCode === 404">Page not found</h1>
<h1 v-else>An error occurred</h1>
<NuxtLink to="/">Home page</NuxtLink>
</div>
</template>

<script>
export default {
props: ['error'],
layout: 'error' // you can set a custom layout for the error page
}
</script>

SEO meta、title

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
export default {
data(){
return{
title:this.$route.params.title,
}
},
// 独立设置 head 信息
head(){
return{
title:this.title,
meta:[
{hid:'description',name:'news',content:'This is news page'}
]
}
}
}
</script>
注意:为了避免子组件中的meta标签不能正确覆盖父组件中相同的标签而产生重复的现象,建议利用 hid 键为meta标签配一个唯一的标识编号。

数据请求

安装 Axios

1
npm install axios --save
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
<template>
<div>
<h1>姓名:{{info.name}}</h1>
<h2>年龄:{{info.age}}</h2>
<h2>兴趣:{{info.interest}}</h2>
</div>
</template>
<script>
import axios from 'axios'
export default {
data(){
return {
name:'hello World',
}
},
// promise
asyncData(){
return axios.get('https://api.myjson.com/bins/8gdmr')
.then((res)=>{
console.log(res)
return {info:res.data}
})
}
// await
async asyncData(){
let {data}=await axios.get('https://api.myjson.com/bins/8gdmr')
return {info: data}
}
}
</script>

静态资源的引入及打包

直接引入图片

1
2
3
<template>
<div><img src="~static/logo.png" /></div>
</template>

css 图片的引入

1
2
3
4
5
6
7
<style>
.diss{
width: 300px;
height: 100px;
background-image: url('~static/logo.png')
}
</style>

打包静态HTML并运行

1
npm run generate

live-server 可以查看打包后的文件


相关资料
[代码沙盒]路由
[代码沙盒]Views布局使用
技术胖Nuxt.js教程开启SSR渲染
[视频]技术胖Nuxt.js免费视频教程 开启SSR渲染

uniapp 从入门到实战

uniapp

uniapp 基础

运行项目

第一次在微信开发者工具中运行需要先配置小程序的相关路径、小程序开发者工具中安全中服务端口开启

项目目录及文件

pages 所有的页面存放目录
static 静态资源目录,例如图片等
unpackage 就是打包目录,在这里有各个平台的打包文件
components 组件存放目录
App.vue 是我们的根组件,所有页面都是在 App.vue 下进行切换的,是页面入口文件,可以调用应用的生命周期函数。
manifest.json 文件是应用的配置文件,用于指定应用的名称、图标、权限等。
pages.json 文件用来对 uni-app 进行全局配置,决定页面文件的路径、窗口样式、原生的导航栏、底部的原生tabbar 等
main.js 是我们的项目入口文件,主要作用是初始化 vue 实例并使用需要的插件。
uni.scss 文件的用途是为了方便整体控制应用的风格。比如按钮颜色、边框风格,uni.scss 文件里预置了一批scss变量预置。

为了实现多端兼容,综合考虑编译速度、运行性能等因素,uni-app 约定了如下开发规范:

  • 页面文件遵循 Vue 单文件组件 (SFC) 规范
  • 组件标签靠近小程序规范,详见uni-app 组件规范
  • 接口能力(JS API)靠近微信小程序规范,但需将前缀 wx 替换为 uni,详见uni-app接口规范
  • 数据绑定及事件处理同 Vue.js 规范,同时补充了App及页面的生命周期
  • 为兼容多端运行,建议使用flex布局进行开发
阅读更多

前端存储

HTTP cookies

HTTP Cookie(Web Cookie 或者浏览器 Cookie)
是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登陆状态。Cookie 是基于无状态的HTTP协议记录稳定的状态信息成为了可能。

Cookie 的本职工作并非本地存储,而是“维持状态”
HTTP协议是无状态的,HTTP协议自身不对请求和响应之间的通信状态进行保存。
Cookie指某些网站为了辨别用户身份而储存在用户本地终端上的数据(通常经过加密)。 cookie是服务端生成,客户端进行维护和存储。

服务器 → 浏览器 → 存储在本地
浏览器再发起请求会携带 cookie → 服务器

阅读更多

Vuex 基础入门

状态管理模式

问题:当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏并产生以下问题:

  • 多个视图依赖于同一状态
  • 来自不同视图的行为需要变更同一状态

解决方法:

  • 将数据以及操作数据的行为都定义在父组件;
  • 将数据以及操作数据的行为传递给需要的各个子组件(有可能需要多级传递)

上述的解决方法并未根本上解决问题,随着项目的复杂会暴露出以下问题:

  • 传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。
  • 我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。

因此,为什么不把组件的共享状态抽取出来,以全局单例模式管理呢?通过定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性,我们的代码将会变得更结构化且易维护。这就是 Vuex 背后的基本思想

Vuex

Vue 简介

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

阅读更多
You need to set client_id and slot_id to show this AD unit. Please set it in _config.yml.