byte-fe / intern-study Goto Github PK
View Code? Open in Web Editor NEW实习生互助学习
License: MIT License
实习生互助学习
License: MIT License
##无限下拉或是上拉组件
<script src="http://www.bootcss.com/p/underscore/underscore-min.js"></script>
;(function() {
infiniteLoad({
scollDirection: 'up', //滚动方向,‘up’||‘down’
domString: '', //页尾栏 选择器,可以省略
fatherString: '.content', //父亲元素 选择器
childString: 'li', //标签名字
template: '<p><%= date %></p><p><%= desc %></p><img src=<%= pic %> >', //模版字符underscore写法
cb: async function({ limit, offset }) {
let res = await axios.get('/json', {
params: { limit, offset }
})
let { code, data } = res.data
if (parseInt(code, 10) !== 0) return
let mockData = Array.apply(null, { length: 50 }).map((item, index) => {
return data[index % 2]
})
return mockData
}, //页尾部栏可见时候,远程加载数据的函数,返回一个promise 数据是对象数组[{}]
options: {
//一开始的limit和offset限制
limit: 2,
offset: 0
}
})
})()
实现数据分页,数据 mock 一份即可
可自己搭建服务,自选框架,比如 koa2,不强制使用 fe
有 api 和静态资源分类的概念
列表数据格式可参考
[{
id: "hahahahahha",
index: 0,
title: "我是标题,要短",
desc: "我是描述,我很长。",
// pic: "http://xxx.xx.x/xxxxx.avi",
pic: "http://xxx.xx.x/xxxxx.png",
date:
},{
// ...
}]
Object.assign([1, 2, 3], [4, 5])
// [4, 5, 3] 数组覆盖
[].every(item=>item>2)//true . 空数组直接true
super关键字表示原型对象时,只能用在对象的方法之中,用在其他地方都会报错。
pm2 start --name=cms npm -- run dev
对象编辑
return {
guid: id,
...(name && {name}),
...(age && {age})
}
foo::bar(...arguments);
// 等同于
bar.apply(foo, arguments);
复制代码
如果双冒号左边为空,右边是一个对象的方法,则等于将该方法绑定在该对象上面。
const baz = obj?.foo?.bar?.baz; // 传导符 babel支持
// 没有用管道操作符
double(increment(double(5))); // 22
// 用上管道操作符之后
5 |> double |> increment |> double; // 22
function isClass(func) {
return typeof func === 'function'
&& /^class\s/.test(Function.prototype.toString.call(func));
}
(() => {}).prototype // undefined
(function() {}).prototype // {constructor: f}
new function Fun(){}如果一个函数的返回值 不是 一个对象,它会被 new 完全忽略。如果你返回了一个字符串或数字,就好像完全没有 return 一样。
function Person() {}
console.log(Person.prototype); // 🤪 不是 Person 的原型
console.log(Person.__proto__); // 😳 Person 的原型
“原型链”更像是 __proto__.__proto__.__proto__ 而不是 prototype.prototype.prototype
Array.from(document.querySelectorAll('div')) === $$('div')
在 ES6 中,词法 环境和 变量 环境的区别在于前者用于存储**函数声明和变量( let 和 const )绑定,而后者仅用于存储变量( var )**绑定。
在 ES6 中,词法 环境和 变量 环境的区别在于前者用于存储**函数声明和变量( let 和 const )绑定,而后者仅用于存储变量( var )**绑定。
登录用户的信息存与mysql,mysql上的操作基于objection做了一下封装,把对应的数据库表操作方法挂在fastify的sever实列上,用户是否已经登录通过cookie实现,只是cookie上存储的只是用户信息的一个映射key,也就是sessionId,通过sessionId作索引,在mysql上匹配到对应的用户信息。
'use strict'
//用户名要唯一
const fp = require('fastify-plugin')
const uid = require('uid-safe').sync
const sessionPlugin = async (app, options) => {
let sessionIdName = options.sessionName || 'sessionId_stock_level2'
let userModel = await app.getQueryModel({
psm: '11111111',
table: '222222'
})
let sessionModel = await app.getQueryModel({
psm: '33333333',
table: '4444444444'
})
app.addHook('preHandler', async (req, reply) => {
const sessionId = req.cookies[sessionIdName]
if (sessionId) {
const r = await sessionModel
.query()
.where({ sessionId })
.andWhere('expires', '<', new Date())
.catch(err => err)
if (Array.isArray(r) && r.length > 0) {
req.session = r[0]
}
}
})
app.post('/login', async (req, reply) => {
let { body } = req
let name = body.name
let password = body.password
const userName = await userModel
.query()
.where('name', name)
.catch(err => console.log(err))
if (userName.length === 0) {
return { code: 1, data: '用户名输入错误!' }
} else {
const userPassword = await userModel.query().where({ name, password })
if (userPassword.length === 0) {
return { code: 1, data: '密码输入错误!' }
} else {
const sessionId = uid(24)
let maxAge = options.maxAge || 60 * 60 * 1000
let expires = new Date(Date.now() + maxAge)
let resInsert = await sessionModel
.query()
.insert({ sessionId, expires, name })
.catch(err => err)
reply
.setCookie(sessionIdName, sessionId, {
domain: '',
path: '/',
expires: new Date(expires),
httpOnly: true
})
.send({ code: 0, data: '登陆成功!' })
}
}
})
}
module.exports = fp(sessionPlugin, {
fastify: '>=1.2.0',
name: '@fe/byted-auth'
})
附上一个基于json ,而非mysql的简单实现
'use strict'
//用户名要唯一
const fp = require('fastify-plugin')
const sessionPlugin = async (app, options = {}) => {
// console.log(app.CONFIG)
let maxAge = options.maxAge || 24 * 60 * 60 * 100
let sessionIdName = options.sessionName || 'sessionId_stock_level2'
const { WHITELIST } = app.CONFIG
app.addHook('preHandler', async (req, reply) => {
const sessionId = req.cookies[sessionIdName]
if (sessionId) {
let userInfos = {}
let time = new Date()
const isAvaiable = WHITELIST.some(item => {
if (
item.sessionId === sessionId &&
item.expires < time &&
item.group === 'SZ'
) {
userInfos = item
return true
}
return false
})
if (isAvaiable) {
req.session = userInfos
} else {
req.session = null
}
}
})
app.post('/szlevel2/login', async (req, reply) => {
let expires = new Date(Date.now() + maxAge)
let sessionId = ''
let { body } = req
let name = body.name
let password = body.password
const isAvaiable = WHITELIST.some(item => {
if (
item.name === name &&
item.password === password &&
item.group === 'SZ'
) {
sessionId = item.sessionId
return true
}
return false
})
if (isAvaiable) {
reply
.setCookie(sessionIdName, sessionId, {
domain: '',
expires: new Date(expires),
httpOnly: true
})
.send({ code: 0, data: { name, message: '登陆成功' } })
} else {
reply.send({ code: 1, data: '输入错误!' })
}
})
app.post('/szlevel2/logout', async (req, reply) => {
reply
.setCookie(sessionIdName, '')
.send({ code: 0, data: 'logout success!' })
})
}
module.exports = fp(sessionPlugin, {
fastify: '>=1.2.0',
name: '@fe/byted-auth'
})
背景关键字,不做介绍,只说sso。
往常一个网站就要登陆一次,同一用户再次登陆另一个网站还得再次登陆,也就是不用的网站在保留
session和cookie是各自独立的。既做session的创立也做用户登陆的校验。但如果这几个网站是同一个登
陆呢,比如公司好几个网站,你只要登陆一次sso认证,就可以访问各个页面。所以sso就是为了实现web
多系统下的登陆认证,多系统都接入同一个的sso认证,用户只要登陆过一次,就可以访问接入这个认证
的任何一个系统,不用重复登陆认证。
用户登录成功之后,会与sso认证中心及各个子系统建立会话,用户与sso认证中心建立的会话称为全局会话,用户与各个子系统建立的会话称为局部会话,局部会话建立之后,用户访问子系统受保护资源将不再通过sso认证中心,全局会话与局部会话有如下约束关系
基于 fe 完成前后端开发, 最终部署在自己测试机 (如果有的话 😄)
服务端可以配置奖项和权重(不考虑奖池)
weight: 1
意味着 1/100 几率抽中
[{name: "10元话费", weight: 1}, {name: "50积分", weight: 50}, ...]
前端动画适配 android 4.4+ / ios 6+
动画描述:
设计图可能部分不是很标准, 意会 😄
功能:Context 旨在共享一个组件树内可被视为 “全局” 的数据,例如当前经过身份验证的用户,主题或首选语言等。(ps:树中的许多组件以及不同的嵌套级别可以访问相同的数据。)
//.context.js文件中
import React, { Component } from 'react'
const tryContext = React.createContext({ aaa: 28288 })
export { tryContext }
主要是Provider Consumer这对生产者和消费者
import { tryContext } from '../components/context'
.
.
.
class A extends PureComponent {
render() {
const { bbb } = this.props
return (
<div>
<tryContext.Provider value={bbb}>
<tryContext.Consumer>
{state1 => <div className="try">{state1}</div>}
</tryContext.Consumer>
</tryContext.Provider>
<tryContext.Provider value={'BBBBBB'}>
<B />
</tryContext.Provider>
</div>
)
}
}
class B extends PureComponent {
static contextType = tryContext
render() {
return <div>{this.context}</div>
}
}
//B.contextType = tryContext
//父组件A
class A extends React.Component {
getChildContext() {
return { color: 'red' }
}
}
A.childContextTypes = {
color: PropTypes.string
}
//子组件B
class B extends React.Component {
render() {
return <p>{this.context.color}</p>
}
}
B.contextTypes = {
color: PropTypes.string
}
####老版弊端
####新版注意点
数年生产环境实践, redux 体系逐渐退出历史舞台, 对于 react 应用来说是时候反思更佳的状态管理实践了
react 团队近期推动创新了很多新的 patterns, 从 Render Props
/ HOC 到 new Context / Hooks
团队技术积累重心放在 react 方向
希望同学们持续学习, 理解社区技术选型背后的逻辑, 拥抱新的变革
兼容并包思考大中小型项目都能复用的状态管理架构理想情况下应该怎样, 权衡之下的分层策略, RN / SSR
/ 低配浏览器等平台环境下如何兼顾
简单考虑如下:
import Model from 'react-model'
export default Model({
state: {
count: 0
},
actions: {
increase: async (state, actions, payload) => {count: payload || state.count++}
},
middlewates: []
})
需要暴露出的核心 api 非常少, 就一个
Model
Model
接口签名为:const {Provider, connect, actions, subscribe, unsubscribe, getState, useState, useAction} from 'models/modelA.js'
简单起见, 我们这期作业只思考 Provider
/ connect
的实现, 使用方式如下:
const {Provider, connect, actions} from 'models/modelA.js'
@connect(({ count }) => ({ count }))
class Page extends Component {
render() {
<div onClick={e => actions.increase(10)}>{this.props.count}</div>
}
}
const Root = () => {
<Provider>
<Page />
</Provider>
}
这里沿用了 redux 的范式, Provider 包裹根节点, connect 连接 view 和注入 model state 到 this.props
connect 的实现需要理解: HOC / decoration,
数据变更方式可以考虑 new Context(推荐) / Proxy api / immer 方案
赶紧学习 Hooks, view 绑定的最终形态应该如下, 有兴趣的同学也可以思考下列实现:
import { useState, useAction } from 'models/modelA.js'
function Page() {
const count = useState(state => state.count)
const add = useAction(actions => actions.increase)
return (
<div onClick={e => actions.increase(10)}>{this.props.count}</div>
)
}
this.sector.setTransform(
Math.cos(s),
Math.sin(s),
-1 * Math.sin(s),
Math.cos(s),
600 - Math.cos(s) * 600 + Math.sin(s) * 600,
600 - Math.sin(s) * 600 - Math.cos(s) * 600
)
不改变旋转中心,
2. canvas适配,配合高清屏幕,可以将canvas的width和height放大,放大倍数看具体的设备的window.devicePixelRatio。最后在将canvas的dom的css . 大小width和height设置成ui要求的大小
使用的是webpack,在postcss.config.js中设置好,如下:
require('postcss-px-to-viewport')({
viewportWidth: 750,
viewportHeight: 1332,
unitPrecision: 3,
viewportUnit: 'vw',
selectorBlackList: ['.nvw', '.hairlines'],
minPixelValue: 1,
mediaQuery: false
}),
require('postcss-viewport-units')()
#VUE踩坑
1.对象和数组变化时候,ui没监视到,没有触发一定的更新。虽然平时看文档 有见到过 还是在实践中踩坑了。所以不能直接操作对象和数组的改变,可以如下和文档一样的操作:
Vue.set(vm.items, indexOfItem, newValue)
vm.$set(vm.items, indexOfItem, newValue)
vm.items.splice(indexOfItem, 1, newValue)
vm.userProfile = Object.assign({}, vm.userProfile, {
age: 27,
favoriteColor: 'Vue Green'
})
将本地的ssh key放入git上,可以链接该git,查看ssh key:
cat ~/.ssh/id_rsa.pub
将本地的ssh key添加入远程机子
cat ~/.ssh/authoried 具体按tab 忘记是单词了哈
连接远程机子:用户名和ip
ssh xiaoyanhui@ip
psm: 'toutiao.mysql.testdb',
alias: 'caijing.cms.stock.read',
在vue中 引入某个插件(自定义),其顶层对象是window吗,在mouted中给window添加原型链方法会 出现意外,引用错误。
div里面有一个img标签,然后按照设计图给把图片的宽度和高度赋给了父元素,给img设置宽度和高度为100%,这样子元素img的宽度和高度就等于父元素的宽度和高度,奇怪的是出现了下图所示的问题:
可以看到图1和图2的详情高度不一样,更加奇怪的是,两个img标签的宽度和高度也是一样的。
因为不同字体会造成不同的渲染方式,比如下面这样,给这三个span标签的字体都设置了字体大小为100px,但是设置了不同的font-family,而表现出所看见的不一样的字体高度。所以首先给font-size设置为0,消除这个因素的影响。深究起来,其实每个内联元素会有两个高度:
line-height
内容区域
每行内容可以由多个内联元素组成(内联标签或者是包含文本的匿名内联元素),每一行都叫做一个 line-box。line-height是实际的高度,这个高度用于计算 line-box 的高度,具体来说就是从子元素的最高点到最低点的高度。
vertical-align 属性也是计算 line-box 高度的重要因素之一,它的默认值是 baseline,也就是与其父元素基线相对齐;baseline 所处的高度跟字体也有关,浏览器认为每个 line-box 的起始位置都有一个宽度为 0 的字符(CSS 文档将其称为 strut),并将其纳入 line-box 的高度的计算中。下图所示为vertical-align的取值效果:
最终img标签的样式:
font-size:0;
vertical-align:top;
line-height:0;
这是从官方文档中记录,个人觉得比较需要留点心眼的知识,并不是为了介绍react。
所以不会介绍reacts生命周期、setState等知识。
<button onClick={this.handleClick}>
你必须谨慎对待 JSX 回调函数中的 this,类的方法默认是不会绑定 this 的:
// This binding is necessary to make this
work in the callback
this.handleClick = this.handleClick.bind(this);
<button onClick={(e) => this.handleClick(e)}>
<button onClick={this.deleteRow.bind(this, id)}>
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>
数组元素中使用的key在其兄弟之间应该是独一无二的。然而,它们不需要是全局唯一的。
key会作为给React的提示,但不会传递给你的组件。
如果您的组件中需要使用和key相同的值,请将其作为属性传递.
在HTML当中,<textarea> 元素通过子节点来定义它的文本内容:
在React中,<textarea>会用value属性来代替。
在React中,并不使用之前的selected属性,而在根select标签上用value属性来表示选中项。
当你有处理多个受控的input元素时,你可以通过给每个元素添加一个name属性,
来让处理函数根据 event.target.name的值来选择做什么。
Math.round(output * 1000) / 1000;
你应该在应用中保持 自上而下的数据流,而不是尝试在不同组件中同步状态。
属性上可以是组件:
<SplitPane
left={
<Contacts />
}
right={
<Chat />
} />
如果要在组件之间复用 UI 无关的功能,我们建议将其提取到单独的 JavaScript 模块中。
单一功能原则,在理想状况下,一个组件应该只做一件事情。如果这个组件功能不断丰富,它应该被分成更小的组件。
你能够根据组件中任何其他的 state 或 props 把它计算出来吗?如果是,它不是 state。
JSX 只是为 React.createElement(component, props, ...children)
React.createElement(CustomButton, {color: 'red'}, null);
点表示法:JSX 中的点表示法来引用 React 组件。你可以方便地从一个模块中导出许多 React 组件。
const MyComponents = {
DatePicker: function DatePicker(props) {
return <div>Imagine a {props.color} datepicker here.</div>;
}
}
<MyComponents.DatePicker color="blue" />
当元素类型以小写字母开头时,它表示一个内置的组件,如
可以使用 ... 作为扩展操作符来传递整个属性对象。
const props = {firstName: 'Ben', lastName: 'Hector'};
return <Greeting {...props} />;
JSX 会移除空行和开始与结尾处的空格。标签邻近的新行也会被移除,字符串常量内部的换行会被压缩成一个空格,所以下面这些都等价:
false、null、undefined 和 true 都是有效的子代,但它们不会直接被渲染。
JavaScript 中的一些 “falsy” 值(比如数字0),它们依然会被渲染。
为属性指定默认值:
Greeting.defaultProps = {
name: 'Stranger'
};
类型检查发生在 defaultProps 赋值之后,所以类型检查也会应用在 defaultProps 上面。
componentWillUpdate(object nextProps, object nextState)
React 提供一个工具方法 React.Children 来处理 this.props.children 。我们可以用 React.Children.map 来遍历子节点,而不用担心 this.props.children 的数据类型是 undefined 还是 object。
3步使用ref:
23 ref 注意点:
React 会在组件加载时将 DOM 元素传入 current 属性,在卸载时则会改回 null。ref 的更新会发生在componentDidMount 或 componentDidUpdate 生命周期钩子之前。
你不能在函数式组件上使用 ref 属性,因为它们没有实例。可以在函数式组件内部使用 ref
回调 Refs: 不同于传递 createRef() 创建的 ref 属性,你会传递一个函数。这个函数接受 React 组件的实例或 HTML DOM 元素作为参数,将他们存储在react上并使它们能被其他地方访问。React 将在组件挂载时将 DOM 元素传入ref 回调函数并调用,当卸载时传入 null 并调用它。ref 回调函数会在 componentDidMout 和 componentDidUpdate 生命周期函数前被调用。
受控组件中,表单数据由 React 组件处理
和 支持 defaultChecked, 和 <textarea> 支持 defaultValue. 在React中, 始终是一个不受控制的组件,因为它的值只能由用户设置,而不是以编程方式设置。
shouldComponentUpdate(nextProps, nextState) {
if (this.props.color !== nextProps.color) {
return true;
}
if (this.state.count !== nextState.count) {
return true;
}
return false;
}
为什么抖动还会有两种?
其实是我碰到过两种抖动的场景,第一个场景是native的抖动,第二个场景是h5的抖动。
前端开发人员会在app中打开webview,这个时候iOS中position:fixed吸底时的滑动出现抖动应该是native造成的抖动,整个viewport跟着动,所以可以在生成schema的时候将参数bounce_disable设置为1禁止native的弹性效果,然后加上h5的这个效果,-webkit-overflow-scrolling 属性可以帮我们实现这个效果,它控制元素在移动设备上是否使用滚动回弹效果。
//我是吸顶头部
.header{
width:100%;
height:50px;
position:fixed;
top:0px;
}
//我是中间要滑动的部分
.main{
width:100%;
height:auto;
position:absolute;
padding-top:50px;
padding-bottom:50px;
box-sizing:border-box;
overflow-y:scroll;
-webkit-overflow-scrolling :touch
}
//我是吸底尾部
.footer{
width:100%;
height:50px;
position:fixed;
bottom:0px;
}
解释:滑动部分overflow-y:scroll;所以在上下方向超出一屏的部分会变成滚动模式并且不溢出,然后这边吸顶和吸底设置的高度都是50,所以对应的中间滑动部分分别有padding-top:50px;和padding-bottom:50px;设置box-sizing:border-box;所以padding的增加不会增加.main的高度。
transform: translateZ(0);
-webkit-transform: translateZ(0);
解释:在使用position:fixed的元素上加上该属性。
起源于一个问题:promise的错误能不能被外层的try catch捕获?
try{
Promoise.reject('我就是要报错!')
}catch(err){console.log(err)}
try{
await Promoise.reject('我就是要报错!')
}catch(err){console.log(err)}
第一个是没办法捕获到这个错误的,而第2个是可以的。
catch 是 .then(null, rejection)的别名,用于指定发生错误时的回调函数。
所以常常见到下面这个写法:
promise.then().catch()
promise.then().then().catch()
promise.then().then().catch().then().catch()
async function tryPromise(){
await Promise.reject('错误')
await Promise.resolve('输出')
return
}
try{
await tryPromsie()}catch(err){
}
第一个await出错,错误给后面的catch。async函数直接结束,后面的await就不会执行。
如果想在第一个await出错时候,后面的await还执行,可以在第一个await外层套一个try catch。
后面再看看await是怎么处理错误的,后面在更新。
html的代码中经常会有这么一句
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
这句话的意思是当前viewport的宽度为设备宽度,初始缩放值和最大缩放值都为1,并禁止了用户缩放viewport通俗的讲是浏览器上可用来显示页面的区域。而这句话设置的意义是先不需要用户缩放和横向滚动条就能正常的查看网站的所有内容;第二,显示的文字的大小是合适,比如一段14px大小的文字,不会因为在一个高密度像素的屏幕里显示得太小而无法看清,理想的情况是这段14px的文字无论是在何种密度屏幕,何种分辨率下,显示出来的大小都是差不多的。
还有一个因素也会引起css中px的变化,那就是用户缩放。例如,当用户把页面放大一倍,那么css中1px所代表的物理像素也会增加一倍;反之把页面缩小一倍,css中1px所代表的物理像素也会减少一倍。
在早先的移动设备中,屏幕像素密度都比较低,如iphone3,它的分辨率为320x480,在iphone3上,一个css像素确实是等于一个屏幕物理像素的。后来随着技术的发展,移动设备的屏幕像素密度越来越高,从iphone4开始,苹果公司便推出了所谓的Retina屏,分辨率提高了一倍,变成640x960,但屏幕尺寸却没变化,这就意味着同样大小的屏幕上,像素却多了一倍,这时,一个css像素是等于两个物理像素的。其他品牌的移动设备也是这个道理。例如安卓设备根据屏幕像素密度可分为ldpi、mdpi、hdpi、xhdpi等不同的等级,分辨率也是五花八门,安卓设备上的一个css像素相当于多少个屏幕物理像素,也因设备的不同而不同,没有一个定论。
有一个devicePixelRatio属性,它的官方的定义为:设备物理像素和设备独立像素的比例,也就是 devicePixelRatio = 物理像素 / 独立像素。我们在css中使用的px就可以看做是设备的独立像素,所以通过devicePixelRatio,我们可以知道该设备上一个css像素代表多少个物理像素。例如,在Retina屏的iphone上,devicePixelRatio的值为2,也就是说1个css像素相当于2个物理像素。
所以这就能解释为什么我们在css中设置的1px看上去比较粗了,也就是比如说我们当前的设备设置了width=device-width, initial-scale=1.0并且当前的设备devicePixelRatio为2,那么在css设置的1px就相当于物理像素的2px啦。
viewport的宽度可以通过 document.documentElement.clientWidth 来获取
iOS8已经支持带小数的px,但是安卓和低版本的iOS不适用,所以在要考虑兼容性的情况下这个解决方案不太可取。
.border { border: 1px solid #999 }
@media screen and (-webkit-min-device-pixel-ratio: 2) {
.border { border: 0.5px solid #999 }
}
@media screen and (-webkit-min-device-pixel-ratio: 3) {
.border { border: 0.333333px solid #999 }
}
上文有提到devicePixelRatio,media query对应devicePixelRatio有个查询值-webkit-min-device-pixel-ratio,
版本1:
@mixin border1pxtop($color: rgb(232, 232, 232)) {
position: relative;
&:before {
content: '';
position: absolute;
background: $color;
left: 0;
top: 0;
width: 100%;
height: 1px;
// dpr 小于2.9视为2倍屏
@media screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-max-device-pixel-ratio: 2.9) {
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
}
// dpr 大于2.9视为3倍屏
@media screen and (-webkit-min-device-pixel-ratio: 2.9) {
-webkit-transform: scaleY((1/3));
transform: scaleY((1/3));
}
}
}
版本2:
解释一下,先给外面的元素定位position:relative,然后给伪元素定位position:absolute,这样伪元素就相对于外面元素定位,所以需要给伪元素加上left:0,top:0,另外给伪元素设置边框border:1px,这边可以定制自己想要的border,包括颜色,上边框或者右边框之类的,然后给width:200%,height:200%,这样就相对于定位的那个元素放大了两倍,此时再加上 transform: scale(0.5);就可以将宽度、高度都缩放到和相对定位的那个元素一样大小,另外边框的宽度也会缩放一倍,达到我们想要的效果。
@mixin border1pxtop2($color: rgb(232, 232, 223)) {
position: relative;
&::before {
pointer-events: none;
content: '';
box-sizing: border-box;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
border-top: 1px solid $color;
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 2dppx) {
width: 200%;
height: 200%;
-webkit-transform: scale(0.5);
transform: scale(0.5);
}
@media (-webkit-min-device-pixel-ratio: 3), (min-resolution: 3dppx) {
width: 300%;
height: 300%;
-webkit-transform: scale(0.333333);
transform: scale(0.333333);
}
}
}
@mixin border1pxleft2($color: rgb(232, 232, 232)) {
background-size: 1px 100%;
background-repeat: no-repeat;
background-position: left top;
background-image: linear-gradient(90deg, $color, $color 100%, transparent 0%);
@media screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-max-device-pixel-ratio: 2.99) {
background-image: linear-gradient(
90deg,
$color,
$color 50%,
transparent 50%
);
}
transform方法的版本1有在一些机型不奏效的情况,这个时候可以尝试加上will-change:transform属性。will-change是css3新增的属性。这个属性如果形象一点说,那就是在真正的行为触发之前告诉浏览器:注意咯,我一会儿要有变化啦,做好准备哦!作为回应,浏览器会把GPU给拉上,从容应对即将到来的变化。这样浏览器可以在元素属性真正发生变化之前提前做好对应的优化准备工作。 这种优化可以将一部分复杂的计算工作提前准备好,使页面的反应更为快速灵敏。这也应该是我们使用这个属性的初衷。
但是不得不说想要用好这个属性并不容易,试想那种全局都开启will-change的做法,等于是让浏览器的各个元素都随时GPU渲染加速待命,效果是适得其反的。
下图数据来自https://caniuse.com/#feat=will-change
will-change的几个取值:
will-change: auto;
表示没有特别指定哪些属性会变化,浏览器需要自己去猜,然后使用浏览器经常使用的一些常规方法优化。
will-change: scroll-position;
告诉浏览器优化改变滚动条的位置的场景,并且这个场景在将来会发生。
will-change: contents;
表示开发者希望在不久后改变元素内容中的某些东西,或者使它们产生动画。
will-change: ;
比如will-change: transform;will-change: opacity;
当线上出现bug,真的是手忙脚乱啊。所以只能线上调试fe(ps:下文只是口译潘导师操作流程),主要步骤是杀掉线上进程,用node --inspect 启动一个调试的node的进程。然后在本地浏览器进行调试
1 node start
改完后的启动脚本如下:
1 # fe start
2 node --inspect=0.0.0.0:8889 node start
0.0.0.0:8889端口其实不固定,不占用就可以。
4.杀点线上正在运行的进程,服务器会按照修改后的bootstrap进行重启,故只要杀掉进程即可
kill -s 9 640190
5.待进程重启完成后,进入浏览器inspect
6.本地浏览器配置调试的ip与端口,写入线上docker具体的ip与端口
7.进入到调试模式(断点,下一步,blablaba)
Docker是对Linux容器的一种封装,提供简单易用的容器使用接口。不是对操作系统的模拟,而是对进程的隔离。Docker相比虚拟机具有启动快,占用资源少,体积少的优点。
docker run image
,可以生成一个容器,容器在本质上是一个进程,但是每个容器都有自己的命名空间,容器之间是隔离的。容器运行时,以镜像为基础层,在其上创建容器存储层,这个容器存储层的生命周期和容器一样,容器消亡,容器存储层也会消亡,除非使用 docker commit
来生成一个新的镜像rootStore.registerModule(MODULE_NAME, moduleStore)
rootStore.unregisterModule(state.moduleName) // 卸载上一个路由对应的 module store
比如这个需求,看是多个页面其实只要写一个单页面,然后分别加一个v-if就可以。
使用js-xlsx 插件,具体使用可以看一下github js-xlsx
有搭配的工具,util 无论是转json或是array都比较方便
git clone
git checkout -b branch-name origin/branch-name //建立与远程分支关联都本地分支
git pull 与 git push //同步远程分支,与上传分支
store的mutation 是没有return,会有问题。
以及在使用mapMutations等引入子store时候加上mudule-name
computed: {
...mapState(MODULE_NAME, [
'userBasic',
'loanDetail',
'repayList',
'repayPlan',
'cardList'
]) //从store加入数据
在method上调用自身组件的method方法记得加上this(可执行上下文),关于ActiveObject与scope(作用域链)知识需要在加强一下
关于es5的声明提前,变量和函数的优先级,函数的优先级比变量高。
百闻不如一用。
Talk is cheap,show you the code.
使用前:
.lottery-sector li:nth-child(1) {
background-color: #fff;
transform: rotate(0deg) skew(54deg);
}
.lottery-sector li:nth-child(2) {
background-color: rgb(254, 246, 225);
transform: rotate(36deg) skew(54deg);
}
.lottery-sector li:nth-child(3) {
background-color: #fff;
transform: rotate(72deg) skew(54deg);
}
.lottery-sector li:nth-child(4) {
background-color: rgb(254, 246, 225);
transform: rotate(108deg) skew(54deg);
}
.lottery-sector li:nth-child(5) {
background-color: #FFF;
transform: rotate(144deg) skew(54deg);
}
.lottery-sector li:nth-child(6) {
background-color: rgb(254, 246, 225);
transform: rotate(180deg) skew(54deg);
}
.lottery-sector li:nth-child(7) {
background-color: #FFF;
transform: rotate(216deg) skew(54deg);
}
.lottery-sector li:nth-child(8) {
background-color: rgb(254, 246, 225);
transform: rotate(252deg) skew(54deg);
}
.lottery-sector li:nth-child(9) {
background-color: #FFF;
transform: rotate(288deg) skew(54deg);
}
.lottery-sector li:nth-child(10) {
background-color: rgb(254, 246, 225);
transform: rotate(324deg) skew(54deg);
}
使用后:
.lottery-sector
for row in 1..10
li:nth-child({row})
if (row % 2 == 0)
background-color: rgb(254, 246, 225);
else
background-color: #fff;
transform: rotate((row * 36)deg) skew(54deg);
是不是觉得清爽了很多!这里有用到stylus的几个语法:插值、嵌套、计算、条件、循环、省略花括号、取一段值的简便写法。
嵌套
lottery-sector的子元素li的样式直接定义在了它里面
插值
变量文本要作为内容的一部分,用{}括起来,比如 li:nth-child({row})
计算
比如row * 36,row % 2 == 0
这边我感觉是我踩的一个坑,不过也应该成为编写代码的习惯,变量和计算符号之间要有空格
条件
if和else语句的使用是允许的
循环
用for in循环,这边值得夸奖的是它的简便写法,循环1~10可以直接写成1..10(包含边界,即包含10),1...10(不包含边界,不包含10)。
#分享一波上周使用vue细节2018.06.04
vue style 加不加scoped区别:
当 <style> 标签有 scoped 属性时,它的 CSS 只作用于当前组件中的元素。机制是给dom添加一个data-*的自定义属性,然后style会这么操作.classNamep[data-],也就是属性选择器。注意,只要静态的dom和class才有效,动态添加的dom和class样式是无效的。解决办法是可以去掉scoped或是用内联样式dom.style......
vue watch是可以深度监听的(默认就是深度监听),也就是没有vue那个this.$set这个问题。ps:记得 这个watch是没有s 不是watchs.
methods方法不能用箭头函数绑定,因为箭头函数声明时候回绑定声明时父级的作用域。ps:vue会给methods等 函数执行时候绑定当前组件的实例
关于el-element中关于表格 el-table里面那个自定义scope.如果和表单一起使用,那个row绑定 也会改变以前的数值,属于引用绑定
vue的v-for。知道在es6中,有new Map()对象的 for of 循环 ,也就是迭代器。在vue中虽然也有v-for,有 for of和 for in。但是呢 我在使用过程中发现是不能用迭代器的。
props: {
params: {
type: Object,
default: () => {} //{jsonData:[],keyMap:{name:'名字'}}
}
},
这个组件输入,一个是待转化的对象数组,字段会是表格的表头,然后 keyMap,传入可将对应的英文字段 在 excel 用中文展示,以及表头的顺序。如果没传入 keyMap,默认使用 Object.key(jsonData)
对应的的组件链接outportXlsx
附上
props: {
onImport: {
type: Function,
default: () => {}
}
},
主要使用 frappe-chart 组件,对其做了一个简单封装。主要提供以下几个功能。
chartData: {}
chartConfig:{}
chartData与chartConfig设置和官网一样 主要增加如下配置
chartConfig: {
btnOptions: {
sizeBtn: false,//{ show: true,}
exportBtn: { show: true, type: 'data', xlabel: '时间' }
},
showMax_Min_Mean: {
max: false,
min: false,
mean: false
}}
<template>
<div :class="{component_chart:true,chartLarge:hasLarged}"
v-if="chartShow">
<div class="chartWrap"
ref="chartWrap">
<div class="chart"
ref="chart">
</div>
<!-- 扩展的按钮 -->
<div class="anatherBtn">
<slot>
</slot>
</div>
<div :class="{largeBtn:(chartConfig.btnOptions||{}).sizeBtn, chartBtn:true}"
@click="changeSize"
v-if="(chartConfig.btnOptions||{}).sizeBtn">
<i class="el-icon-rank"></i>
</div>
<div :class="{downloadBtn:(chartConfig.btnOptions||{}).exportBtn, chartBtn:true}"
@click="exportChart"
v-if="((chartConfig.btnOptions||{}).exportBtn||{}).show&&chartConfig.btnOptions.exportBtn.type=='pic'">
<i class="el-icon-download"></i>
</div>
<out-put :class="{downloadBtn:(chartConfig.btnOptions||{}).exportBtn, chartBtn:true}"
:params="outputParams"
v-if="((chartConfig.btnOptions||{}).exportBtn||{}).show&&chartConfig.btnOptions.exportBtn.type=='data'">
<i class="el-icon-download"></i>
</out-put>
</div>
</div>
</template>
<script>
import _ from 'lodash'
import OutPut from '~/components/common/outputXLSX'
import { Chart } from 'frappe-charts'
export default {
data() {
return {
chartShow: true,
chartDom: {},
hasLarged: false,
chart: {},
config: {}
}
},
props: {
chartData: { type: Object, default: () => ({}) },
chartConfig: { type: Object, default: () => ({}) }
},
computed: {
outputParams() {
let jsonData = []
let keyMap = {
xlabel: ((this.chartConfig.btnOptions || {}).exportBtn || {}).xlabel
}
//验证数据格式
if (
!(
Array.isArray(this.chartData.datasets) &&
this.chartData.datasets.length &&
Array.isArray(this.chartData.labels) &&
this.chartData.labels.length
)
) {
return { jsonData, keyMap }
}
this.chartData.datasets.forEach((line, index) => {
keyMap['key' + index] = line.name
})
this.chartData.labels.forEach((x, i) => {
let oneRow = {}
oneRow.xlabel = x
this.chartData.datasets.forEach((line, index) => {
oneRow['key' + index] = line.values[i]
})
jsonData.push(oneRow)
})
return { jsonData, keyMap, fileName: this.chartConfig.title }
}
},
watch: {
chartData(newValue, oldValue) {
let chartDom = this.$refs.chart
if (
Array.isArray(newValue.labels) &&
Array.isArray(oldValue.labels) &&
newValue.labels.length == oldValue.labels.length &&
newValue.datasets.length == oldValue.datasets.length
) {
this.chart.update(newValue)
} else {
this.initChart()
}
}
},
components: { OutPut },
methods: {
changeSize() {
this.hasLarged = !this.hasLarged
if (this.hasLarged) {
this.$set(this.config, 'height', this.config.height * 2)
} else {
this.$set(this.config, 'height', this.chartConfig.height)
}
this.initChart(true)
},
exportChart() {
this.chart.export()
},
initChart(isSize) {
if (!isSize) {
this.config = _.cloneDeep(this.chartConfig)
}
//验证数据格式
if (
!(
Array.isArray(this.chartData.datasets) &&
this.chartData.datasets.length &&
Array.isArray(this.chartData.labels) &&
this.chartData.labels.length
)
) {
return
}
//单点情况
if (this.chartData.labels.length == 1) {
this.chartData.labels.push('')
if (this.config.type == 'line') {
this.config.lineOptions = {
dotSize: 4,
hideDots: 0
}
} else if (this.config.type == 'bar') {
this.config.barOptions = {}
}
} else {
if (!isSize) {
this.config = _.cloneDeep(this.chartConfig)
}
}
let allData = []
this.chartData.datasets.forEach(element => {
allData.push(...element.values)
})
//确保坐标最大值和最小值能显示
if (this.chartConfig.showMax_Min_Mean) {
if (
!(this.chartData.yMarkers && Array.isArray(this.chartData.yMarkers))
) {
this.chartData.yMarkers = []
}
if (this.chartConfig.showMax_Min_Mean.max) {
this.chartData.yMarkers.push({
label: 'max',
value: Math.max(...allData),
options: { labelPos: 'left' }
})
}
if (this.chartConfig.showMax_Min_Mean.min) {
this.chartData.yMarkers.push({
label: 'min',
value: Math.min(...allData),
options: { labelPos: 'left' }
})
}
if (this.chartConfig.showMax_Min_Mean.mean) {
let sum = 0
allData.forEach(item => {
sum += item
})
this.chartData.yMarkers.push({
label: 'mean',
value: sum / allData.length,
options: { labelPos: 'left' }
})
}
}
if (
this.chartData.labels.length < 15 &&
this.config.lineOptions &&
!this.config.lineOptions.hideDots
) {
this.chartConfig.lineOptions.dotSize = 4
}
setTimeout(() => {
this.chart = new Chart(this.chartDom, {
data: this.chartData,
...this.config
})
}, 50)
}
},
mounted() {
let chartDom = this.$refs.chart
this.chartDom = chartDom
this.initChart()
},
beforeDestroy() {
//离开时候 注销chart
if (
this.chart.unbindWindowEvents &&
typeof this.chart.unbindWindowEvents == 'function'
) {
this.chart.unbindWindowEvents()
}
}
}
</script>
<style lang="stylus">
.chart
.frappe-chart.chart
text.title
font-size 14px
fill rgb(34 34 34)
</style>
<style scoped lang="stylus">
.component_chart
font-family 'PingFangSC-Regular'
display flex
flex-direction column
justify-content center
width 100%
overflow hidden
.chartWrap
position relative
text-align center
overflow hidden
.chart
width 100%
.chartBtn
font-size 18px
padding 2px 5px
position absolute
color rgb(182 192 226)
&:hover
cursor pointer
.largeBtn
top 10px
right 1px
.downloadBtn
bottom 0px
right 1px
.anatherBtn
position absolute
top 4px
left 115px
// top 10px
// right 60px
.chartLarge
width 970px
height 500px
.largeBtn
top 10px
right 20px!important
.downloadBtn
bottom 0px
right 20px!important
</style>
这里定义为获取终端 "用户" ip, 考虑到实际场景需要 bypass proxy / LB
plugin
形式产出组件单元测试的编写参考:@fe/byted-consul
直连或透明代理由 TCP 协议获取
proxy / LB 从 http 协议(header)获取
一些标准化的云服务 / LB 服务, 从 header 中标准定义获取
优先 header 关键字判定, 命中则为 proxy / LB
检查最标准无歧义的 header: X-Client-IP
一般国外云服务商(Amazon / Heroku)遵循的业界标准
检查记录代理信息的 header: x-forwarded-for
和 x-real-ip
都为约定速成的 header,
x-forwarded-for
目前应用最广泛,x-real-ip
是早期 fastcgi 等默认的 header
node 服务最常见的 proxy 和 LB 默认配置, 属性值为
[client_IP, proxy1_IP, proxy2_IP, ...]
如果是可控的 proxy 配置, 例如公司 TLB(nginx), 确保:
location
配置包含proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
或proxy_set_header X-Real-IP $remote_addr;
检查一些常见云服务商约定的 header:
cf-connecting-ip
(Cloudflare)true-client-ip
(Akamai)x-cluster-client-ip
(一些 LB 服务商标准)检查 x-forwarded-for
的常见变体:
x-forwarded
forwarded-for
forwarded
取 TCP 协议中的 remoteAddress
(只有两种情况下可以获取 client ip, 与 client 直连或是经过透明代理)
透明代理的 nginx 配置:
proxy_bind $remote_addr transparent;
按 node net 模块 实现, 依次检查:
req.connection.remoteAddress
req.socket.remoteAddress
req.connection.socket.remoteAddress
req.info.remoteAddress
兼容一些 serverless 服务商, 从 req
附加 context 获取
req.requestContext.identity.sourceIp
(AWS Lambda)A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.