- 🔭 I'm currently working on the crm web and low code in YMM.
- 🌱 I’m currently learning Node/Python/UE(user experience).
You can also read my articles in blog
My personal blog / 个人博客(小小鲁班的技术口袋)
Home Page: https://github.com/ly525/blog/issues
License: MIT License
You can also read my articles in blog
nginx 转发请求, django 如何获得真正的用户 IP?
127.0.0.1
, 是 nginx 所在的 IP 地址server {
listen 80;
server_name www.test.com;
default_type text/html;
location / {
proxy_pass http://127.0.0.1:8000;
}
location ~ ^/static/ {
root /opt/test-project;
}
}
真正的 IP 地址 nginx 可以取得, 然后转发给后端
方法: nginx 转发真正的 IP 地址给后端, 添加
location / {
proxy_set_header X-Real-IP $remote_addr; #加上这句话就可以了
proxy_pass http://127.0.0.1:8000;
}
I am writing...
问
:前端路由和 nginx location 哪个优先级更高?答
:当然是 nginx location 优先级更高因为 nginx location 优先级更高,所以会导致 history 模式的SPA(单页应用)在刷新的时候,会有 404 的问题
www.abc.com/dashboard
,点击logout
退出系统,前端路由处理,跳转到www.abc.com/login
nginx 404
了/login
的时候,没有报404
, 为何一刷新就 404 了呢?/login
逻辑:this.$router.push({name: 'login'});
routes: [
{
name: 'home',
path: '/',
component: Home,
meta: { requiresAuth: true },
},
{
path: '/login',
name: 'login',
component: Login,
},
{
path: '*',
component: NotFound,
}
]
现有nginx配置
location / {
root /var/data/static;
}
1. 为何点击退出可以正常显示登录页面?
- 因为点击退出,前端路由
this.$router.push({name: 'login'});
来实现跳转的,这时候已经有index.html 和相关的js了(也就是说这时候页面跳转的逻辑已经由 Vue-Router 或 React Router 接管了),可以直接使用前端路由跳转到/login
路由对应的组件
2. 为何刷新的时候显示: `nginx/404` ?
- 因为刷新的时候,会先向服务器请求
www.abc.com/login
> 2. 这时候服务器的nginx
配置中没有关于/login
路径的配置(即没有location /login {}
),因此 nginx 会直接报错nginx/404
3. 如何解决?
按照如下方式配置nginx
location / {
root /var/data/static;
# 意思是说,在nginx 没有找到 /login 路径的时候,不要报错,要把 index.html 返回给客户端
# 这样客户端(浏览器)就能加载到路由相关的js 文件了
# 这时候,路由由前端接管,前端路由会查看路由表里面是否由 /login 的配置,有的话,跳转即可
try_file /index.html;
}
4. 更多解释
1. nginx进行匹配路径的时候,发现没有`/login`的路径,便会转到`/`路径处理。
2. 发现`root`路径下(是静态文件的root目录,不是linux的root目录)没有`login`文件,就会`try_file`规则,返回index.html,
3. 这样浏览器拿到index.html 之后,开始加载其中的前端路由部分
4. 这时候/login就会在前端路由中找到匹配规则。
5. 同理`/404`也可以写在前端路由中了
window[xxxx] = 1
从而让代码在二次执行的时候提前退出。判断页面上广告位数量>0的时候才去加载数据。return str.replace(/^[\s\xa0]+|[\s\xa0]+$/g, "")
'ascii' codec can't encode character u'\xf4' in position 1: ordinal not in range(128)
I am writing...
networkRequests
变量function UrlRegEx(url) {
//如果加上/g参数,那么只返回$0匹配。也就是说arr.length = 0
var re = /(\w+):\/\/([^\:|\/]+)(\:\d*)?(.*\/)([^#|\?|\n]+)?(#.*)?(\?.*)?/i;
//re.exec(url);
var arr = url.match(re);
return arr;
}
function getHost(url) {
return UrlRegEx(url)[2];
}
var requests = networkRequest.log.entries
var all_urls = []
requests.forEach(item => {
all_urls.push(getHost(item.request.url))
})
var finalUrls = {}
all_urls.forEach(item=>{
finalUrls[item] = ''
})
JSON.stringify(Object.keys(finalUrls))
for (let i = 0; i < 100000; i ++) {
let $body = $('body');
$body.text();
}
box-size 是什么和应用场景?
输入123456789
,输出123,456,789
,其实就是千分位
function getThousands(number) {
return String(number).replace(/(^|\s)\d+/g, m =>
m.replace(/(?=(?!\b)(\d{3})+$)/g, ","))
}
I am writing...
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"
一般charset是怎么写的<meta charset="UTF-8">
< © 这种被称为unicode特殊符号,需使用对应的实体编码
_getDownloadUrl (text) {
const BOM = '\uFEFF';
// Add BOM to text for open in excel correctly
if (window.Blob && window.URL && window.URL.createObjectURL && !has('Safari')) {
const csvData = new Blob([BOM + text], { type: 'text/csv' });
return URL.createObjectURL(csvData);
} else {
return 'data:attachment/csv;charset=utf-8,' + BOM + encodeURIComponent(text);
}
},
download (filename, text) {
if (has('ie') && has('ie') < 10) {
// has module unable identify ie11 and Edge
const oWin = window.top.open('about:blank', '_blank');
oWin.document.charset = 'utf-8';
oWin.document.write(text);
oWin.document.close();
oWin.document.execCommand('SaveAs', filename);
oWin.close();
} else if (has('ie') === 10 || this._isIE11() || this._isEdge()) {
const BOM = '\uFEFF';
const csvData = new Blob([BOM + text], { type: 'text/csv' });
navigator.msSaveBlob(csvData, filename);
} else {
const link = document.createElement('a');
link.download = filename;
link.href = this._getDownloadUrl(text);
link.target = '_blank';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
http://stackoverflow.com/questions/19051411/css-padding-overrides-overflow/19051546#19051546
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
body {
border: 1px solid green;
}
.placeholder {
width: 50%;
padding-bottom: 50%;
background: rebeccapurple;
height: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div class="placeholder">
text
</div>
</body>
</html>
关键字: created(), vue-router , transition, route
Bug描述
修复
- Vue.js组件的生命周期中,
created()
在组件被创建之后, 如果判断组件可以重用, 便不会再执行- 如果切换不同的路由, 需要加载不同的数据, 可以把数据加载的操作放在
route 的data()
中执行
502 bad gateway
nginx开启了,但是反向代理的后端服务没有开启
。
400 bad request
Django settings allow hosts
没有允许该IP的访问302
Django中的login_required中是使用了302跳转来完成重定向操作
login_required('/login') => user_passes_test => redirect_to_login => HttpResponseRedirect
之前疑惑为什么后端可以使得前端页面跳转,后来明白了。浏览器在接收到302的status_code 之后,会自动跳转到新的url
# https://github.com/daoluan/decode-Django/blob/master/Django-1.5.1/django/http/response.py#L426
class HttpResponseRedirectBase(HttpResponse):
allowed_schemes = ['http', 'https', 'ftp']
def __init__(self, redirect_to, *args, **kwargs):
parsed = urlparse(redirect_to)
if parsed.scheme and parsed.scheme not in self.allowed_schemes:
raise SuspiciousOperation("Unsafe redirect to URL with protocol '%s'" % parsed.scheme)
super(HttpResponseRedirectBase, self).__init__(*args, **kwargs)
# 关键部分,可以用来做链接重定向的检测
self['Location'] = iri_to_uri(redirect_to)
class HttpResponseRedirect(HttpResponseRedirectBase):
status_code = 302
403
I am writing...
I am writing...
Find Out Which Process Is Listening Upon a Port
https://stackoverflow.com/questions/3855127/find-and-kill-process-locking-port-3000-on-mac
netstat -vanp tcp | grep 8081
tcp6 0 0 ::1.8081 ::1.54270 ESTABLISHED 407251 146808 88305 0
tcp6 0 0 ::1.54270 ::1.8081 ESTABLISHED 358716 146808 57219 0
tcp46 0 0 *.8081 *.* LISTEN 131072 131072 88305 0
tcp6 0 0 ::1.8081 ::1.54294 TIME_WAIT 407182 146808 88305 0
tcp6 0 0 ::1.8081 ::1.54265 TIME_WAIT 405456 458104 88305 0
tcp6 0 0 ::1.8081 ::1.54292 TIME_WAIT 407150 146808 88305 0
tcp6 0 0 ::1.8081 ::1.54293 TIME_WAIT 407197 146808 88305 0
kill -9 88305
所以,你可以把这个命令封装一下
alias portgrep='netstat -vanp tcp | grep'
以后这样用就OK了:
portgrep 8080
I am writing...
process.nextTick()
process.nextTick()
The event loop is what allows Node.js to perform non-blocking I/O operations
事件循环是允许Node.js 执行非阻塞I/O的关键
— despite the fact that JavaScript is single-threaded
尽管事实上JavaScript是单线程的
— by offloading operations to the system kernel whenever possible.
通过尽可能地卸载操作到操作系统内核
Since most modern kernels are multi-threaded, they can handle multiple operations executing in the background.
因为多数现代内核是多线程的, 它们可以在后台同时处理多个操作
When one of these operations completes, the kernel tells Node.js so that the appropriate callback
may be added to the poll queue to eventually be executed.
当其中一个操作完成时, 内核会通知Node.js, 这样, 合适的回调函数会被添加到poll队列中来保证最终执行
We'll explain this in further detail later in this topic.
接下来我们更详细解释这一点
When Node.js starts, it initializes the event loop, processes the provided input script (or drops into
the REPL, which is not covered in this document),
当Node.js启动时,它初始化事件循环,processes 处理入口脚本(或者REPL的输入)这个在此没有提及
which may make async API calls, schedule timers, or call process.nextTick()
, then begins processing the event loop.
这个脚本可能会调用异步API,设置定时任务 或者 调用process.nextTick()
然后开始处理事件循环
The following diagram shows a simplified overview of the event loop's
order of operations.
下面的流程图展示了简化版的事件循环操作
┌───────────────────────┐
┌─>│ timers │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ I/O callbacks │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ idle(休眠), prepare(准备) │
│ └──────────┬────────────┘ ┌───────────────┐
│ ┌──────────┴────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └──────────┬────────────┘ │ data, etc. │
│ ┌──────────┴────────────┐ └───────────────┘
│ │ check │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
└──┤ close callbacks │
└───────────────────────┘
note: each box will be referred to as a "phase" of the event loop.
*注: 每一个盒子都是事件循环的一个阶段
Each phase has a FIFO queue of callbacks to execute.
每一个阶段都会执行一个先进先出的回调函数队列(FIFO: first in, first out)
While each phase is special in its own way,
尽管每一个阶段的方式都很特殊,
generally, when the event loop enters a given phase, it will perform any operations specific to that phase,
通常,当事件循环进入一个阶段的之后,它会执行该阶段的任何操作
then execute callbacks in that phase's queue until the queue has been exhausted or the maximum number of callbacks has executed.
然后执行这个阶段的回调函数, 直到这回调函数队列为空 或者 已经执行了回调函数的最大次数
When the queue has been exhausted or the callback limit is reached, the event loop will move to the next phase, and so on.
当队列里面没有回调函数,或者到达了callbakcks的最大的执行次数,事件队列将会进入下一个阶段,以此类推
Since any of these operations may schedule more operations and new
events processed in the poll phase are queued by the kernel, poll
events can be queued while polling events are being processed. As a
result, long running callbacks can allow the poll phase to run much
longer than a timer's threshold. See the timers and
poll sections for more details.
NOTE: There is a slight discrepancy between the Windows and the
Unix/Linux implementation, but that's not important for this
demonstration. The most important parts are here. There are actually
seven or eight steps, but the ones we care about — ones that Node.js
actually uses - are those above.
setTimeout()
setInterval()
.setImmediate()
.setImmediate()
callbacks are invoked here.socket.on('close', ...)
.Between each run of the event loop, Node.js checks if it is waiting for
any asynchronous I/O or timers and shuts down cleanly if there are not
any.
A timer specifies the threshold after which a provided callback
may be executed rather than the exact time a person wants it to
be executed. Timers callbacks will run as early as they can be
scheduled after the specified amount of time has passed; however,
Operating System scheduling or the running of other callbacks may delay
them.
Note: Technically, the poll phase controls when timers
are executed.
For example, say you schedule a timeout to execute after a 100 ms
threshold, then your script starts asynchronously reading a file which
takes 95 ms:
const fs = require('fs');
function someAsyncOperation(callback) {
// Assume this takes 95ms to complete
fs.readFile('/path/to/file', callback);
}
const timeoutScheduled = Date.now();
setTimeout(() => {
const delay = Date.now() - timeoutScheduled;
console.log(`${delay}ms have passed since I was scheduled`);
}, 100);
// do someAsyncOperation which takes 95 ms to complete
someAsyncOperation(() => {
const startCallback = Date.now();
// do something that will take 10ms...
while (Date.now() - startCallback < 10) {
// do nothing
}
});
When the event loop enters the poll phase, it has an empty queue
(fs.readFile()
has not completed), so it will wait for the number of ms
remaining until the soonest timer's threshold is reached. While it is
waiting 95 ms pass, fs.readFile()
finishes reading the file and its
callback which takes 10 ms to complete is added to the poll queue and
executed. When the callback finishes, there are no more callbacks in the
queue, so the event loop will see that the threshold of the soonest
timer has been reached then wrap back to the timers phase to execute
the timer's callback. In this example, you will see that the total delay
between the timer being scheduled and its callback being executed will
be 105ms.
Note: To prevent the poll phase from starving the event loop, libuv
(the C library that implements the Node.js
event loop and all of the asynchronous behaviors of the platform)
also has a hard maximum (system dependent) before it stops polling for
more events.
This phase executes callbacks for some system operations such as types
of TCP errors. For example if a TCP socket receives ECONNREFUSED
when
attempting to connect, some *nix systems want to wait to report the
error. This will be queued to execute in the I/O callbacks phase.
The poll phase has two main functions:
When the event loop enters the poll phase and there are no timers
scheduled, one of two things will happen:
If the poll queue is not empty, the event loop will iterate
through its queue of callbacks executing them synchronously until
either the queue has been exhausted, or the system-dependent hard limit
is reached.
If the poll queue is empty, one of two more things will
happen:
If scripts have been scheduled by setImmediate()
, the event loop
will end the poll phase and continue to the check phase to
execute those scheduled scripts.
If scripts have not been scheduled by setImmediate()
, the
event loop will wait for callbacks to be added to the queue, then
execute them immediately.
Once the poll queue is empty the event loop will check for timers
whose time thresholds have been reached. If one or more timers are
ready, the event loop will wrap back to the timers phase to execute
those timers' callbacks.
This phase allows a person to execute callbacks immediately after the
poll phase has completed. If the poll phase becomes idle and
scripts have been queued with setImmediate()
, the event loop may
continue to the check phase rather than waiting.
setImmediate()
is actually a special timer that runs in a separate
phase of the event loop. It uses a libuv API that schedules callbacks to
execute after the poll phase has completed.
Generally, as the code is executed, the event loop will eventually hit
the poll phase where it will wait for an incoming connection, request,
etc. However, if a callback has been scheduled with setImmediate()
and the poll phase becomes idle, it will end and continue to the
check phase rather than waiting for poll events.
If a socket or handle is closed abruptly (e.g. socket.destroy()
), the
'close'
event will be emitted in this phase. Otherwise it will be
emitted via process.nextTick()
.
setImmediate()
vs setTimeout()
setImmediate
and setTimeout()
are similar, but behave in different
ways depending on when they are called.
setImmediate()
is designed to execute a script once the currentsetTimeout()
schedules a script to be run after a minimum thresholdThe order in which the timers are executed will vary depending on the
context in which they are called. If both are called from within the
main module, then timing will be bound by the performance of the process
(which can be impacted by other applications running on the machine).
For example, if we run the following script which is not within an I/O
cycle (i.e. the main module), the order in which the two timers are
executed is non-deterministic, as it is bound by the performance of the
process:
// timeout_vs_immediate.js
setTimeout(() => {
console.log('timeout');
}, 0);
setImmediate(() => {
console.log('immediate');
});
$ node timeout_vs_immediate.js
timeout
immediate
$ node timeout_vs_immediate.js
immediate
timeout
However, if you move the two calls within an I/O cycle, the immediate
callback is always executed first:
// timeout_vs_immediate.js
const fs = require('fs');
fs.readFile(__filename, () => {
setTimeout(() => {
console.log('timeout');
}, 0);
setImmediate(() => {
console.log('immediate');
});
});
$ node timeout_vs_immediate.js
immediate
timeout
$ node timeout_vs_immediate.js
immediate
timeout
The main advantage to using setImmediate()
over setTimeout()
is
setImmediate()
will always be executed before any timers if scheduled
within an I/O cycle, independently of how many timers are present.
process.nextTick()
process.nextTick()
You may have noticed that process.nextTick()
was not displayed in the
diagram, even though it's a part of the asynchronous API. This is because
process.nextTick()
is not technically part of the event loop. Instead,
the nextTickQueue
will be processed after the current operation
completes, regardless of the current phase of the event loop.
Looking back at our diagram, any time you call process.nextTick()
in a
given phase, all callbacks passed to process.nextTick()
will be
resolved before the event loop continues. This can create some bad
situations because it allows you to "starve" your I/O by making
recursive process.nextTick()
calls, which prevents the event loop
from reaching the poll phase.
Why would something like this be included in Node.js? Part of it is a
design philosophy where an API should always be asynchronous even where
it doesn't have to be. Take this code snippet for example:
function apiCall(arg, callback) {
if (typeof arg !== 'string')
return process.nextTick(callback,
new TypeError('argument should be string'));
}
The snippet does an argument check and if it's not correct, it will pass
the error to the callback. The API updated fairly recently to allow
passing arguments to process.nextTick()
allowing it to take any
arguments passed after the callback to be propagated as the arguments to
the callback so you don't have to nest functions.
What we're doing is passing an error back to the user but only after
we have allowed the rest of the user's code to execute. By using
process.nextTick()
we guarantee that apiCall()
always runs its
callback after the rest of the user's code and before the event loop
is allowed to proceed. To achieve this, the JS call stack is allowed to
unwind then immediately execute the provided callback which allows a
person to make recursive calls to process.nextTick()
without reaching a
RangeError: Maximum call stack size exceeded from v8
.
This philosophy can lead to some potentially problematic situations.
Take this snippet for example:
let bar;
// this has an asynchronous signature, but calls callback synchronously
function someAsyncApiCall(callback) { callback(); }
// the callback is called before `someAsyncApiCall` completes.
someAsyncApiCall(() => {
// since someAsyncApiCall has completed, bar hasn't been assigned any value
console.log('bar', bar); // undefined
});
bar = 1;
The user defines someAsyncApiCall()
to have an asynchronous signature,
but it actually operates synchronously. When it is called, the callback
provided to someAsyncApiCall()
is called in the same phase of the
event loop because someAsyncApiCall()
doesn't actually do anything
asynchronously. As a result, the callback tries to reference bar
even
though it may not have that variable in scope yet, because the script has not
been able to run to completion.
By placing the callback in a process.nextTick()
, the script still has the
ability to run to completion, allowing all the variables, functions,
etc., to be initialized prior to the callback being called. It also has
the advantage of not allowing the event loop to continue. It may be
useful for the user to be alerted to an error before the event loop is
allowed to continue. Here is the previous example using process.nextTick()
:
let bar;
function someAsyncApiCall(callback) {
process.nextTick(callback);
}
someAsyncApiCall(() => {
console.log('bar', bar); // 1
});
bar = 1;
Here's another real world example:
const server = net.createServer(() => {}).listen(8080);
server.on('listening', () => {});
When only a port is passed the port is bound immediately. So the
'listening'
callback could be called immediately. Problem is that the
.on('listening')
will not have been set by that time.
To get around this the 'listening'
event is queued in a nextTick()
to allow the script to run to completion. Which allows the user to set
any event handlers they want.
process.nextTick()
vs setImmediate()
We have two calls that are similar as far as users are concerned, but
their names are confusing.
process.nextTick()
fires immediately on the same phasesetImmediate()
fires on the following iteration or 'tick' of theIn essence, the names should be swapped. process.nextTick()
fires more
immediately than setImmediate()
but this is an artifact of the past
which is unlikely to change. Making this switch would break a large
percentage of the packages on npm. Every day more new modules are being
added, which mean every day we wait, more potential breakages occur.
While they are confusing, the names themselves won't change.
We recommend developers use setImmediate()
in all cases because it's
easier to reason about (and it leads to code that's compatible with a
wider variety of environments, like browser JS.)
process.nextTick()
?There are two main reasons:
Allow users to handle errors, cleanup any then unneeded resources, or
perhaps try the request again before the event loop continues.
At times it's necessary to allow a callback to run after the call
stack has unwound but before the event loop continues.
One example is to match the user's expectations. Simple example:
const server = net.createServer();
server.on('connection', (conn) => { });
server.listen(8080);
server.on('listening', () => { });
Say that listen()
is run at the beginning of the event loop, but the
listening callback is placed in a setImmediate()
. Now, unless a
hostname is passed binding to the port will happen immediately. Now for
the event loop to proceed it must hit the poll phase, which means
there is a non-zero chance that a connection could have been received
allowing the connection event to be fired before the listening event.
Another example is running a function constructor that was to, say,
inherit from EventEmitter
and it wanted to call an event within the
constructor:
const EventEmitter = require('events');
const util = require('util');
function MyEmitter() {
EventEmitter.call(this);
this.emit('event');
}
util.inherits(MyEmitter, EventEmitter);
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
console.log('an event occurred!');
});
You can't emit an event from the constructor immediately
because the script will not have processed to the point where the user
assigns a callback to that event. So, within the constructor itself,
you can use process.nextTick()
to set a callback to emit the event
after the constructor has finished, which provides the expected results:
const EventEmitter = require('events');
const util = require('util');
function MyEmitter() {
EventEmitter.call(this);
// use nextTick to emit the event once a handler is assigned
process.nextTick(() => {
this.emit('event');
});
}
util.inherits(MyEmitter, EventEmitter);
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
console.log('an event occurred!');
});
I am writing...
I am writing...
Python
使用HttpResponse 返回给前端二进制文件流, 前端如何在不刷新页面的情况下, 得到文件呢?form.submit()
提交表单的时候, 如果收到了response, 这时候页面在等待后端生成文件的过程中, 前端显示状态有点像刷新中
, 那么在后端出现问题, 就会进行页面的跳转,那么如何后端出错的情况下, 不进行页面的跳转, 还留在原来的页面呢(指的是url不变化)
, 因为使用form.submit()是向与当前URL1不同的URL2 POST数据的, 因为当后端失败之后, 也会跳转对应的URL2 , 问题是如何在失败的时候(1) 提示用户失败信息:如用户密码不正确 (2) 页面不跳转, 仍然在此页面Demo1
var myAlerts = [];
for (var i = 0; i < 5; i++) {
myAlerts.push(
function inner(i) {
alert(i); // 此时的i将会添加到inner函数自己的执行环境的变量对象上
}
);
}
myAlerts[0]();
myAlerts[1]();
myAlerts[2]();
myAlerts[3]();
myAlerts[4]();
Demo2
// 全局作用域global context
var myAlerts = [];
for (var i = 0; i < 5; i++) {
myAlerts.push(
function inner() {
alert(i); //此时的i会被添加到 global context的环境对象上
}
);
}
myAlerts[0]();
myAlerts[1]();
myAlerts[2]();
myAlerts[3]();
myAlerts[4]();
I am writing...
typeof name
,typeof 10
typeof null -> object
因为特殊值null被认为是一个空对象的引用(null值表示一个空对象指针)xxx is not defined
var name; // name没有进行初始化 typeof name -> undefined
typeof age -> undefined //age 没有声明
字符串的toString()方法返回一个字符串的副本
Uncaught TypeError: Cannot read property 'toString' of undefined
String()
可以转换所有类型数据为字符串; String(null) => "null"; String(undefined) => undefined;在if(condition)语句中,condition可以是任意表达式,
而且这个表达式的求值结果并不一定是布尔值,ECMAScript会自动调用Boolean()
转换函数将表达式结果转换为布尔值
forEach如何跳出循环???"
###类型转换(解释即可)
解释了下面的每一结果的原因,
对象的引用
就是栈内存中指向堆内存对象
的一个指针值值传递
typeof
基本类型检测: undefined, boolean, string, number, functioninstanceof
引用类型检测
- 所有引用类型值都是Object的实例
- instanceof 检测基本类型 return false, 因为基本类型不是变量
正则表达式也是对象
正则表达式也是对象
,
会在IE7以及opera中导致错误var person = {}
<=> var person = new Object()
在通过对象字面量定义对象时,实际上不会调用Object构造函数JavaScript var propertyName = 'name' person[propertyName] //在for循环中根据key值作为变量, 访问对象属性值 // 如果属性名中包含会导致语法错误的字符,或者属性名使用的是关键字或保留字,也可以使用方括号表示法。例如:person["first name"] = "Nicholas"; 由于"first name"中包含一个空格,所以不能使用点表示法来访问它。然而,属性名中是可以包含非字母非数字的,这时候就可以使用方括号表示法来访问它们。通常,除非必须使用变量来访问属性,否则我们建议使用点表示法
var colors = ["red", "blue", "green"];
colors[colors.length] = "black";
colors[colors.length] = "brown";
//由于数组最后一项的索引始终是length-1,因此下一个新项的位置就是length。每当在数组末尾添加一项后,其length属性都会自动更新以反应这一变化。
toString()将数组转换为字符串, [1,2,3,4,5].toString()会调用数组中每一项的toString()方法,之后将其拼接为一个字符串, 中间以逗号分隔
var person1 = {
toLocaleString: function() {
return 'john'
},
toString: function() {
return 'john2'
}
}
var person2 = {
toLocaleString: function() {
return 'zhangsan'
},
toString: function() {
return 'zhangsan2'
}
}
var people = [person1, person2]
console.log(people)
console.log(people.toString())
console.log(people.toLocaleString())
var colors = ["red", "green", "blue"];
colors.join('=') // "red=green=blue="
colors // ["red", "green", "blue"];
var colors = ["red", "green", "blue", undefined];
colors.toString() // "red,green,blue,"
sort
1. 默认情况下,sort()方法按升序排列数组项——即最小的值位于最前面,最大的值排在最后面。
为了实现排序,sort()方法会调用每个数组项的toString()转型方法,然后比较得到的字符串,以确定如何排序。
即使数组中的每一项都是数值,sort()方法比较的也是字符串,如下所示
var values = [0, 1, 5, 10, 15];
values.sort();
alert(values); //0,1,10,15,5
2. 自定义排序函数
> 比较函数接收两个参数,
> 如果第一个参数应该位于第二个之前则返回一个负数,
> 如果两个参数相等则返回0,
> 如果第一个参数应该位于第二个之后则返回一个正数。
> 以下就是一个简单的比较函数:
```JavaScript
function compare(value1, value2){
if (value1 < value2) { return -1}
else if (value1 > value2 ) {return 1}
else {return 0}
}
var values = [0, 1, 5, 10, 15];
values.sort(compare) // values => [0, 1, 5, 10, 15];
// 注意: 排序之后, 数组的值与之前不同
// reverse()和sort()方法的返回值是经过排序之后的数组
```
3. 对于数值类型或者其valueOf()方法会返回数值类型的对象类型,可以使用一个更简单的比较函数。这个函数只要用第二个值减第一个值即可(正序)
```JavaScript
function compare(value1, value2){
return value1 - value2;
}
```
===
JavaScript var arr = [{x:1}, {x:2}, {x:4}]; arr.reduce(function (prev, cur) { return prev + cur.x; }, 0); // 7
- The first time the anonymous function is called, it gets called with
(0, {x: 1})
and returns0 + 1 = 1
. 函数第一次调用的时候, 传入(prev, cur)
的值是(0, {x: 1})
- The next time, it gets called with
(1, {x: 2})
and returns `1 + 2 = 3 .- It's then called with (3, {x: 4}), finally returning
- 如果不传入最后的0, 函数第一次调用,传入
(prev, cur)
的值是({x:1}, {x: 2})
, 返回值就是"[object Object]2"
.- 如果有一个操作数是对象、数值或布尔值,则调用它们的toString()方法取得相应的字符串值,然后再应用前面关于字符串的规则。对于undefined和null,则分别调用String()函数并取得字符串"undefined"和"null - <JS高程 3.5.5节 加性操作符>"
alert(sum(10,10));
function sum(num1, num2){ return num1 + num2; }
将一个函数作为另一个函数的结果返回
function factorial(num){
if (num <=1) { return 1; }
else return num * arguments.callee(num-1)
}
function outer(){
inner();
}
function inner(){
alert(arguments.callee.caller);
}
outer();
对象不需要与方法有任何耦合关系
typeof new Number('10') => 'object'
typeof Number('10') => 'number'
位置
操作
- stringValue.substr(-3) => stringValue.substr(11-3) => stringValue.substr(8)
IE的JavaScript实现在处理向substr()方法传递负值的情况时存在问题,它会返回原始的字符串。IE9修复了这个问题。
- stringValue.slice(-3) => stringValue.slice(11-3) => stringValue.slice(8)
- stringValue.substring(-3) => stringValue.substring(0) => 返回值就是整个字符串了
- stringValue.slice(3, -4) => slice(3,7)
- stringValue.substring(3, -4) => stringValue.substring(3, 0) => stringValue.substring(0,3)
substring()方法会把第二个参数转换为0,使调用变成了substring(3,0),而由于这个方法会将较小的数作为开始位置,将较大的数作为结束位置,因此最终相当于调用了substring(0,3)
- substr()也会将第二个参数转换为0,这也就意味着返回包含零个字符的字符串,也就是一个空字符串.
因为substr第二个参数代表的是返回字符串的个数,既然第二参数变为0了, 因此也就返回0个字符, 也就是空字符串
var stringValue = "Lorem ipsum dolor sit amet, consectetur adipisicing elit";
var positions = new Array();
var pos = stringValue.indexOf("e");
while(pos > -1){
positions.push(pos);
pos = stringValue.indexOf("e", pos + 1);
}
alert(positions); //"3,24,32,35,52"
String.prototype.trim = function() {
return this.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
}
```JavaScript
function selectFrom(lowerValue, upperValue) {
var choices = upperValue - lowerValue + 1;
return Math.floor(Math.random() * choices + lowerValue);
}
var num = selectFrom(2, 10);
alert(num); // 介于2和10之间(包括2和10)的一个数值
var colors = ["red", "green", "blue", "yellow", "black", "purple", "brown"];
var color = colors[selectFrom(0, colors.length-1)];
alert(color); // 可能是数组中包含的任何一个字符串
```
这说明JavaScript实际上只有一个全局作用域。任何变量(函数也视为变量),如果没有在当前函数作用域中找到,就会继续往上查找,最后如果在全局作用域中也没有找到,则报ReferenceError错误。
'use strict';
var old_alert = window.alert;
window.alert = function(){ }
demo();
test();
function demo(){
console.log('demo');
}
var test = function(){
console.log('test');
};
/*结果: VM2970:3 Uncaught TypeError: test is not a function(…)*/
I am writing...
熟练掌握原⽣ JS 并能⼿写代码, 对 JS 闭包、 DOM有深刻地理解并在实际项目中运⽤
熟悉前端 MVVM 框架 Vue .js, 研究过其实现原理, 并了解其它框架如 React.js
掌握 ⼀ 些常⻅的设计模式, 追求开发高质量、 高可维护性的代码
熟悉后端语⾔如 Python、 Node.js
具有较强的学习能⼒、 团队合作精神
有技术总结的意识, 推动建⽴团队技术博客。 有⾃⼰的独⽴博客
热爱开源和构建复⽤性强的组件
工作职责:
带领团队主要负责
成果
中后台组件库:
带领业务前端团队0~1建设中后台组件业务组件库,并成功落地 & 演进成为大前端通用中后台组件库
– 组件: 40+PC端、H5端20+
– 覆盖项目 100+
动态表单:
引入动态表单框架,统一团队表单/查询表格场景最佳实践,大规模提高中后台迭代效率
团队管理
三年团队管理经验
大前端校招生训练营负责人
项目经历
2020 - ⾄今: 调研、 开发⼴告动态下发、 渲染⽅案
项目背景: 改造原有Native ⼴告渲染流程, 采⽤ JSBridge + JSRender实现⼴告下发之后 动态渲染
主要模块: JSBridge、 JSRender
主要成果:
2019.07 - 2020-01: 改造 DSP 投放平台, ADX 平台数据报表 (前端⼯程师)
项⽬背景: 重启 DSP + ADX, 改进 DSP 现有投放后台和流程, 增强 ADX 数据统计报表
主要模块: DSP 投放模块, ADX 数据报表
相关技术: Vue + Vuetify
主要成果:
- 改进现有 DSP 投放平台以及流程, 使之能够与 ⼀ 线 DSP 平台相媲美 完善 DSP 数据报表, 让运营能够查看更多纬度的数据
- 改进 ADX 平台, 增加数据对比报表, ⽀持分⼩时环比和同比, 协助运营快速查找、 定 位问题并决策
- 改进 ADX 平台, 提供⽀持多纬度查看、 数据下钻的报表可视化模块, 协助运营10分钟 内定位、 分析问题, 将其模块抽象为通⽤解决⽅案
2019.05 - 2018.07: 基于Marketing API 的⼴告投放平台 (前端⼯程师)
项⽬背景:对接 Google Ad words、 Facebook、 巨量引擎的Marketing-API, 提高⼴告优化师投放的效 率, 整合多平台数据报表
主要模块: Facebook 投放⻚⾯、 巨量引擎投放⻚⾯、 素材库、
相关技术: React、 Redux、 Ant Design
主要成果:
2017.08 - 2018. 12: 国内⼴告平台 (前端⼯程师)
项⽬背景: 构建国内版⼴告平台, 下发⼴告至移动端⽹⻚, 展示、 统计⼴告 主要模块: ⼴告SDK、 统计平台
相关技术: 原⽣JS、 iView
主要成果:
- 研发⼴告下发、 打点 SDK, 书写⽂档:
- 移动端⽹⻚通过简单的集成即可加载、 展示⼴告、 上报⼴告点击、 展示数据 研发⼴告投放平台:
- 让运营能够快速进⾏⼴告投放, 形成: 投放+下发+统计的闭环
- 与后端配合, 调研 Graf ana, 实现上报数据的实时监控、 报警机制, 降低⻛险。
2016.6 - 2018.7: SSP⼴告统计平台 (前端⼯程师)
项⽬背景: 为SSP⼴告系统, 搭建⼴告统计平台, ⽅便运营查看统计数据
主要模块: 报表统计、 多渠道趋势对比、 对账模块
相关技术: Vue .js & iView UI & Echarts
主要成果:
原文链接: #28
应用场景: 本地开发机器是mac或者Linux、windows,需要模拟发布到centos服务器的流程
注意执行该命令位置(pwd) /home/someone/
已有box, 位置: /home/someone/centos.box
vagrant box add --name centos_for_dev /home/someone/centos.box
vagrant init centos_for_dev
会发现在 /home/someone/有一个文件 VagrantFile
vagrant up && vagrant ssh
登录centos_for_dev 机器
自动登录, 用户名和密码都是 vagrant
端口映射
config.vm.network "forwarded_port", guest: 8000, host: 3306
其中host为宿主机(比如widdows或者mac机器),guest为vagrant内的虚拟机。mysql -u root -p
的时候,访问本地3306端口的mysql,实际上访问的是vagrant中的端口为8000的mysqlsudo vim /etc/sysconfig/iptables
编辑防火墙,开启8000端口,供外部访问-A INPUT -p tcp -m tcp --dport 3306 -m state --state NEW -j ACCEPT
service iptables restart
文件映射
config.vm.synced_folder "/home/someone/code/", "/opt/code"
/home/someone/code/
目录下的代码修改了之后,vagrant虚拟机的/opt/code
目录下的代码会同步更新,或者说二者指向同一份文件scp 同步文件
vagrant plugin install vagrant-scp
scp -P 2222 your_file [email protected]:/home/vagrant/
vagrnat up 开机
vagrant halt 关机
创建新用户centos
useradd centos
创建centos用户。config.ssh.username = "centos"
- node.vm.network "public_network", ip: "10.10.30.#{200+i}", bridge: "en0: Wi-Fi (AirPort)"
- ip 与 ifconfig 中主要的实际使用使用的网络在同一个网段即可,200+是因为200以后的 IP 多数没有被占用
- bridge
使用 networksetup -listallhardwareports
查看;关键字List All Network Hardware from the Command Line in OS X
node.vm.box = "/Users/someone/workspace/vagrant-boxs/centos7_test.box"
- 可以是 虚拟机 box 的绝对路径
- vagrant 市场中的名称
- 和 Vagrantfile 在同一个文件夹中
node.vm.box = "centos7_test.box"
在验证码的请求中,有如下 header,这个与正常的ajax请求有何异同?
'X-Requested-With': 'XMLHttpRequest'
element || iview 上传图片尺寸校验
- 在上传图片之前,验证图片大小、尺寸等信息,可以返回一个Promise。当验证成功的时候,可以
return file
。 这样,在upload中可以拿到该文件对象- 当验证失败的时候,需要使用reject,因此需要使用
return Promise.reject()
(无法直接返回false的原因是return 的内容需要是一个Promise)
// element ui 代码
// https://github.com/ElemeFE/element/blob/dev/packages/upload/src/upload.vue#L77
upload(rawFile, file) {
this.$refs.input.value = null;
if (!this.beforeUpload) {
return this.post(rawFile);
}
const before = this.beforeUpload(rawFile);
if (before && before.then) {
before.then(processedFile => {
if (Object.prototype.toString.call(processedFile) === '[object File]') {
this.post(processedFile);
} else {
this.post(rawFile);
}
}, () => {
this.onRemove(null, rawFile);
});
} else if (before !== false) {
this.post(rawFile);
} else {
this.onRemove(null, rawFile);
}
},
// 组件关键实现
checkWidthHeight(file) {
return new Promise((resolve, reject) => {
// 取限定尺寸
let width = this.limit.width
let height = this.limit.height
var _URL = window.URL || window.webkitURL;
var img = new Image()
img.onload = function() {
// this => img
let valid = width === this.width && height === this.height
valid ? resolve() : reject(this)
}
img.src = _URL.createObjectURL(file)
})
},
handleBeforeUpload(file) {
return this.checkWidthHeight(file).then(async () => {
let filename = `qiniu-filename-prefix-`
file.name.replace(/.(jpg|jpeg|png|gif)$/, (prefix, suffix) => {
// suffix => 取文件后缀
// 文件名添加时间戳随机数防止重复
filename += `${+new Date()}${suffix}`
})
this.fileName = filename
this.form.key = filename
this.form.token = await api.qiniuUploadToken() // ajax 取七牛上传 token
return file // 被element upload 中的 before.then(processedFile => {} ) 调用
}, (img) => {
this.$Notice.warning({
title: `文件尺寸不正确`,
desc: `文件尺寸 width: ${img.width}, height: ${img.height}`
});
// 返回 reject
// 从而调用 element upload 方法中reject 的 this.onRemove(null, rawFile);
return Promise.reject()
})
},
【?】
水平ul>li
, 可以设置display:inline
, 但是对于较为复杂的水平列表, 如果浮动
列表项, 然后使用margin
将列表项分开, 会更灵活inline
或者 line-block
会导致li
之间存在空白间隙, 解决方案: 给ul设置font-size=0,再给每个li设置对应的font-size
。写代码的时候格式化产生的,也就是 </li结尾>空白/换行导致的的空白间隙<li开始> 可以排在一起就不会有间隙了
ul.pagination li {
float: left;
margin-left: 0.6em;
}
让父元素包含浮动子元素
overflow:hidden;
,创建一个BFC
ul {
margin: 0; // 因为IE早期版本和Opera使用margin-left控制列表缩进, 而Safari和Firefox在内的大多数现代浏览器则选择使用padding-left。 因此需要去掉浏览器的默认行为
padding: 0;
list-style: none;
width: 72em; //
}
ul>li>a
display: block
这样可以让链接占满父元素, 从而让整个li
都可以点击, 否则就是只有<a>内容</a>
内容区域长度可以点击line-height
让链接文本竖直居中纯css工具提示
绝对定位
元素的定位相对于最近的已定位祖先元素
(如果没有, 就相对于祖先元素)
下面的例子中, 已经定位了a
, 因此span
相对于a
定位
<a href="xxxx" class="tooltip">Something <span>tooltip content</span></a>
a.tooltip {
position: relative;
}
a.tooltip span {
display: none;
}
a.tooltip:hover span {
display: block;
position: relative;
top: 1em;
left: 2em;
}
需要再看一下prototype
回答是的, 这个相当与给所有的实例增加方法或者属性
rsync -e "ssh -i ~/.ssh/abc.pem" -atzvhncP --exclude ".git" --exclude "node_modules" --exclude "*.log" ./* [email protected]:/opt/code
rsync -e "ssh -i ~/.ssh/abc.pem" -atzvhcP --exclude ".git" --exclude "node_modules" --exclude "*.log" ./* [email protected]:/opt/code
rsync
I am writing...
digitalocean 的教程
包含安装等等
array like �attributes and it's operation
let el = document.getElementById('app')
let attrs = el.attributes
Array.isArray(attrs) // => false
// iterate
[...attrs].map(attrs, attr => {
return {
name: attr.name,
value: attr.value
}
})
// 参见vuejs源码
[].forEach.map(attrs, attr => {
return {
name: attr.name,
value: attr.value
}
})
场景: 使用gunicorn运行项目的时候(在manage.py所在的目录, 运行gunicorn loga.wsgi:application
) 报错
- importError: No module named djcelery
- 但是使用pip 已经安装过了,因为djcelery的依赖包是
django-celery
- 使用 pip 重新安装django-celery, 得到结果是satisfied, 表明已经是重新安装了,仍然不理解
- 运行
python manage.py runserver
没有报错
后来在这个Issue的启发下, 发现了问题,并且解决了
- 因为我的gunicorn是通过pip在virtualenv环境下安装的, 因此运行gunicorn命令需要带上gunicorn的所在的虚拟环境中的绝对路径
- 即:
/home/someone/venv/bin/gunicorn loga.wsgi:application
同理, 运行一个python脚本 foo.py
的时候, 如果这个脚本中有安装在virtualenv中, 运行这个python脚本时候,需要加上虚拟环境的路径: /home/someone/venv/bin/python a.py
Permissions 0644 for 'XXX.pem' are too open.
rw-r--r--
.其中r=4;w=2;x=1
分别是读、写、执行。sudo chmod 400 ~/.ssh./abc.pem
600 也可以
原因参见man ssh
, 就是因为证书是用来登陆的,因此里面包含了敏感信息,因此应该只能被当前用户读取(600就可以编辑了,但是很少编辑,都是自动生成的),不能被其他用户编辑和读取。
(.pem) Contains the private key for authentication.
[These files contain sensitive data and should be readable by the user
but not accessible by others (read/write/execute).
ssh will simply ignore a private key file if it is accessible by others.]
It is possible to specify a
passphrase when generating the key which will be used to encrypt the sensitive
part of this file using 3DES.
new Vue(options)
=> this._init(options)
=>mergeOptions
=>initLifecycle(vm)
=>initEvents(vm)
=>initRender(vm)
=>callHook(vm, 'beforeCreate')
=>initInjections(vm)
=>initState(vm) =>
=> initProps()
=> initMethod() // 将method 挂载到 vm 上
=> initData() => observe(data, true) // 数据代理
=> initComputed()
=> initWatch()
=> vm.$mount(vm.$options.el) // 🌟🌟🌟🌟
=> Vue.prototype.$mount 「src/platforms/web/entry-runtime-with-compiler.js」
=> { render, staticRenderFns } = compileToFunctions(template, ...)
=> { compile, compileToFunctions } = createCompiler(baseOptions)
=> parse(template, options)
=> optimize(ast, options)
=> {render, staticRenderFns} = generate(ast, options)
=> code = genElement(ast, state) // code demo: _c('div',{attrs:{"id":"app"}},[_c('p',{on:{"click":show}},[_v(_s(text))])])
=> genData
=> data += `${genHandlers(el.events, false, state.warn)},`
=> mount.call()
=> Vue.prototype.$mount 「src/platforms/web/runtime/index.js」
=> mountComponent
=> updateComponent = function() { vm._update(vm._render(), hydrating) }
=> new Watcher(vm, updateComponent)
=> this.getter = updateComponent
=> this.get()
=> pushTarget
=> value = this.getter.call(vm, vm)
=> vm._update(vm._render(), hydrating)
=> vm._update(Vue.prototype._render())
=> Vue.prototype._update(Vue.prototype._render()) 「src/core/instance/lifecycle.js」
=> vm.__patch__
// 需要注意的是: 如果要匹配多个路径,include 传入为正则对象,不可是字符串
// include 正则匹配的是组件的名称,不是路由的名称。
<keep-alive :include=/xxx$/>
<router-view></router-view>
</keep-alive>
function fn() {
console.log('run test')
}
// 调用函数
fn()
// 其实fn() 是fn.call() 的简写形式,效果一样
fn.call()
I am writing...
从下面的这图开始说起,A 部门在 Echarts 上面踩了很多坑,做了很多定制化的样式,这些经验+样式 存在哪里呢?git 仓库?前端部门的博客吗?其实都没有,最终没有任何积累。
B 部门又有一个后台项目。做的时候。因为没有统一的设计规范或者视觉规范,产品和B部门的前端说,你按照A部门的样式来做,但是中间说不定又会改一些其它的内容,最终每个部门的图表都长的不一样。
如下图所示,比如去掉Y轴上面的 - 符号,这个坑A部门踩了,产品觉得A部门的这个OK,照抄,如果A部门的人没有做积累,或者说离职了呢,代码中也没有针对这个添加注释,该怎么办呢?
不过产品不在乎这种积累,直接把A部门页面直接贴过去给B部门的前端就可以了,既然A部门能做出来,B你做不出来,你能力不行 🐶
yAxis: {
splitLine: {
lineStyle: {
// 使用深浅的间隔色
color: ['#aaa', '#ddd'],
}
}
}
option: {
tooltip: {
trigger: 'axis',
position: function(point, params, dom, rect, size) {
// 鼠标在左侧时 tooltip 显示到右侧,鼠标在右侧时 tooltip 显示到左侧。
var obj = {};
// 鼠标是否在当前图标的中线的左侧
let inLeft = point[0] < size.viewSize[0] / 2
// 左侧的tooltip 的left就是pointX 的值,再右侧,则是图标宽度-pointX
let val = inLeft ? point[0] : size.viewSize[0] - point[0]
// 在左侧时候,为 obj.left = pointX; 在右侧时候为 obj.right = widthOfChart - pointX
// 效果就是 tooltip不会超出图表范围
obj[['left', 'right'][+!inLeft]] = val
return obj;
} // position function end
} // tootip end
} // option end
可以代码直接放在demo中运行
option = {
// 调色盘颜色列表。如果系列没有设置颜色,则会依次循环从该列表中取颜色作为系列颜色。
// 使用场景: 一个图表由很多条折线图构成,需要自定义这些颜色,可以写在这里
colors: ['blue', 'orange'],
title: {
text: '未来一周气温变化',
subtext: '纯属虚构'
},
// 工具提示,在鼠标移动到图表上时,显示该点的详细数据
// 图:https://ws1.sinaimg.cn/large/006tKfTcgy1flizeduk3gj31380j6aak.jpg
tooltip: {
trigger: 'axis'
},
legend: {
// 指示器,用于触发折线是否显示
// https://ws4.sinaimg.cn/large/006tKfTcgy1flizizs6dqj314o0z0aao.jpg
data:['最高气温','最低气温'] //这里要和下面series里面的每一个item的名字做对应
},
// 图 https://ws4.sinaimg.cn/large/006tKfTcgy1flizizs6dqj314o0z0aao.jpg
toolbox: {
show: true,
right: 30, // 控制位置
iconStyle: {
// 正常展示时候的样式
normal: {
borderColor: "blue" // 控制样式
},
// emphasis(强调)=> 鼠标悬浮和点击时候的样式
emphasis: {
borderColor: "#2486f0"
}
},
// 支持哪些特性,其实就是有哪些功能
feature: {
dataZoom: { // 图表选取扩大,还原
yAxisIndex: 'none'
},
dataView: {readOnly: false},// 将图形转换为数据视图
magicType: {type: ['line', 'bar']},
restore: {},// 重置
saveAsImage: {} // 将当前图表保存为图片
}
},
xAxis: {
type: 'category',
boundaryGap: false,
// x轴的数据
data: ['周一','周二','周三','周四','周五','周六','周日'],
axisLine:{
// // x轴数据的相关样式
lineStyle:{
color:'purple',
width:10, //x轴的宽度, 在这个图中, x轴位于0度处
}
},
},
yAxis: {
type: 'value',
name: '度',
// 修饰y轴顶部的名称
nameTextStyle: {
color: ['red'],
fontSize: 14,
fontWeight: 600
},
// 坐标轴刻度标签的相关设置
axisLabel: {
formatter: '{value} °C',
color: 'orange'
},
// 坐标轴轴线相关设置
axisLine:{
show: true, // false 不显示
lineStyle:{
color:'blue',
// opacity: 1 // opacity 透明度为0则不显示
}
},
},
// 每一条折线的数据
// 其实对应是legend
series: [
{
name:'最高气温',
type:'bar',
// 柱状图的一个柱子的底部宽度 占据x轴一个刻度宽度 的比例
barWidth: '19%',
// 折线图或者柱状图的数值
data:[3, 4, 3, 2, 2, 3, 7],
// 特殊标记点
markPoint: {
data: [
{type: 'max', name: '最大值'},
{type: 'min', name: '最小值'}
]
},
markLine: {
data: [
{type: 'average', name: '平均值'}
]
}
},
{
name:'最低气温',
type:'line',
data:[1, -2, 2, 5, 3, 2, 0],
markPoint: {
data: [
{name: '周最低', value: -2, xAxis: 1, yAxis: -1.5}
]
},
markLine: {
data: [
{type: 'average', name: '平均值'},
[{
symbol: 'none',
x: '90%',
yAxis: 'max'
}, {
symbol: 'circle',
label: {
normal: {
position: 'start',
formatter: '最大值'
}
},
type: 'max',
name: '最高点'
}]
]
}
}
]
};
export function genSeries(name, data, color) {
let currentUTCHour = new Date().getUTCHours()
let item = {
name: name,
type: 'line',
showSymbol: true,
data: data, // 每一条线上面的数据
// symbolSize: 3,
symbolSize: function (value, params) {
return 0
// if (params.dataIndex % 3 === 0) {
// return 3
// } else {
// return 0
// }
},
lineStyle: {
normal: {
// width: 1,
// type: 'solid' // solid or dotted
color: color,
}
},
itemStyle: {
normal: {
color: color
}
},
}
return item
}
module.exports = {
root: true,
parserOptions: {
parser: 'babel-eslint',
sourceType: 'module'
},
env: {
browser: true,
node: true,
es6: true,
},
extends: ['plugin:vue/recommended', 'eslint:recommended'],
// add your custom rules here
//it is base on https://github.com/vuejs/eslint-config-vue
rules: {
"vue/max-attributes-per-line": [2, {
"singleline": 10,
"multiline": {
"max": 1,
"allowFirstLine": false
}
}],
"vue/singleline-html-element-content-newline": "off",
"vue/multiline-html-element-content-newline":"off",
"vue/name-property-casing": ["error", "PascalCase"],
"vue/no-v-html": "off",
'accessor-pairs': 2,
'arrow-spacing': [2, {
'before': true,
'after': true
}],
'block-spacing': [2, 'always'],
'brace-style': [2, '1tbs', {
'allowSingleLine': true
}],
'camelcase': [0, {
'properties': 'always'
}],
// 'comma-dangle': [2, 'never'],
// 'comma-spacing': [2, {
// 'before': false,
// 'after': true
// }],
// 'comma-style': [2, 'last'],
'constructor-super': 2,
'curly': [2, 'multi-line'],
'dot-location': [2, 'property'],
'eol-last': 2,
'eqeqeq': ["error", "always", {"null": "ignore"}],
'generator-star-spacing': [2, {
'before': true,
'after': true
}],
'handle-callback-err': [2, '^(err|error)$'],
'indent': [2, 2, {
'SwitchCase': 1
}],
'jsx-quotes': [2, 'prefer-single'],
'key-spacing': [2, {
'beforeColon': false,
'afterColon': true
}],
'keyword-spacing': [2, {
'before': true,
'after': true
}],
'new-cap': [2, {
'newIsCap': true,
'capIsNew': false
}],
'new-parens': 2,
'no-array-constructor': 2,
'no-caller': 2,
'no-console': 'off',
'no-class-assign': 2,
'no-cond-assign': 2,
'no-const-assign': 2,
'no-control-regex': 0,
'no-delete-var': 2,
'no-dupe-args': 2,
'no-dupe-class-members': 2,
'no-dupe-keys': 2,
'no-duplicate-case': 2,
'no-empty-character-class': 2,
'no-empty-pattern': 2,
'no-eval': 2,
'no-ex-assign': 2,
'no-extend-native': 2,
'no-extra-bind': 2,
'no-extra-boolean-cast': 2,
'no-extra-parens': [2, 'functions'],
'no-fallthrough': 2,
'no-floating-decimal': 2,
'no-func-assign': 2,
'no-implied-eval': 2,
'no-inner-declarations': [2, 'functions'],
'no-invalid-regexp': 2,
'no-irregular-whitespace': 2,
'no-iterator': 2,
'no-label-var': 2,
'no-labels': [2, {
'allowLoop': false,
'allowSwitch': false
}],
'no-lone-blocks': 2,
'no-mixed-spaces-and-tabs': 2,
'no-multi-spaces': 2,
'no-multi-str': 2,
'no-multiple-empty-lines': [2, {
'max': 1
}],
'no-native-reassign': 2,
'no-negated-in-lhs': 2,
'no-new-object': 2,
'no-new-require': 2,
'no-new-symbol': 2,
'no-new-wrappers': 2,
'no-obj-calls': 2,
'no-octal': 2,
'no-octal-escape': 2,
'no-path-concat': 2,
'no-proto': 2,
'no-redeclare': 2,
'no-regex-spaces': 2,
'no-return-assign': [2, 'except-parens'],
'no-self-assign': 2,
'no-self-compare': 2,
'no-sequences': 2,
'no-shadow-restricted-names': 2,
'no-spaced-func': 2,
'no-sparse-arrays': 2,
'no-this-before-super': 2,
'no-throw-literal': 2,
'no-trailing-spaces': 2,
'no-undef': 2,
'no-undef-init': 2,
'no-unexpected-multiline': 2,
'no-unmodified-loop-condition': 2,
'no-unneeded-ternary': [2, {
'defaultAssignment': false
}],
'no-unreachable': 2,
'no-unsafe-finally': 2,
// 'no-unused-vars': [2, {
// 'vars': 'all',
// 'args': 'none'
// }],
'no-unused-vars': 'off',
'no-useless-call': 2,
'no-useless-computed-key': 2,
'no-useless-constructor': 2,
'no-useless-escape': 0,
'no-whitespace-before-property': 2,
'no-with': 2,
'one-var': [2, {
'initialized': 'never'
}],
'operator-linebreak': [2, 'after', {
'overrides': {
'?': 'before',
':': 'before'
}
}],
'padded-blocks': [2, 'never'],
'quotes': [2, 'single', {
'avoidEscape': true,
'allowTemplateLiterals': true
}],
'semi': [2, 'never'],
'semi-spacing': [2, {
'before': false,
'after': true
}],
'space-before-blocks': [2, 'always'],
'space-before-function-paren': [2, 'never'],
'space-in-parens': [2, 'never'],
'space-infix-ops': 2,
'space-unary-ops': [2, {
'words': true,
'nonwords': false
}],
'spaced-comment': [2, 'always', {
'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
}],
'template-curly-spacing': [2, 'never'],
'use-isnan': 2,
'valid-typeof': 2,
'wrap-iife': [2, 'any'],
'yield-star-spacing': [2, 'both'],
'yoda': [2, 'never'],
'prefer-const': 2,
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
'object-curly-spacing': [2, 'always', {
objectsInObjects: false
}],
'array-bracket-spacing': [2, 'never'],
"comma-dangle": ["error", {
"arrays": "never",
"objects": "always",
"imports": "never",
"exports": "always",
"functions": "never"
}]
}
}
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.