Amy's Blog

notes


  • Home

  • Tags

  • Categories

  • Archives

  • Sitemap

  • Search

mac 安装nginx并配置SSL实现Https访问

Posted on 2020-03-03 | In nginx | 0 comments | Visitors:

一、安装

1
2
3
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
brew install nginx 安装nginx
nginx -v 显示版本号

找到Nginx文件夹

1
cd /usr/local/etc/nginx

启动nginx

1
2
3
sudo nginx 
or
nginx

关闭nginx

1
sudo nginx -s stop

重启nginx

1
sudo nginx -s reload

检查nginx配置

1
2
3
nginx -t nginx.conf
or
sudo nginx -t nginx.conf

查日志

1
2
3
cd /usr/local/var/log/nginx
tail -f access.log
tail -f error.log

二、配置

1、找到Nginx文件夹

1
cd /usr/local/etc/nginx

2、openssl生成自签名证书

创建服务器私钥,命令会让你输入一个口令

1
openssl genrsa -out server.key 1024

根据私钥生成证书申请,创建签名请求的证书(CSR)

1
openssl req -new -key server.key -out server.csr

下面的选项至少写一个,才可以生成证书成功

1
2
3
4
5
6
7
Country Name (2 letter code) []:ch
State or Province Name (full name) []:
Locality Name (eg, city) []:
Organization Name (eg, company) []:
Organizational Unit Name (eg, section) []:
Common Name (eg, fully qualified host name) []:
Email Address []:

在加载SSL支持的Nginx并使用上述私钥时除去必须的口令:

1
2
3
$ cp server.key server.key.org

$ openssl rsa -in server.key.org -out server.key

最后标记证书使用上述私钥和CSR

1
openssl x509 -req -in server.csr -out server.crt -signkey server.key -days 3650

4、配置nginx:修改/usr/local/etc/nginx/nginx.conf 文件

1
2
3
4
5
6
7
location / {

root html(当前静态文件的路径);

index index.html index.htm;

}

不过最好不要把配置写到/usr/local/etc/nginx/nginx.conf里面,而是写在当前目录下面的servers的文件夹下,创建不同的config更加清晰:
例如:servers下建立一个millet.conf
在millet.conf里面配置

1
2
3
4
5
6
7
8
9
10
11
12
server {
listen 443 ssl;
server_name static.millet.com;
#server.crt和server.key都在nginx下面
ssl_certificate server.crt;
ssl_certificate_key server.key;

location / {
root (当前静态文件的路径);
index index.html index.htm;
}
}

ihost

1
127.0.0.1 static.millet.com

启动nginx

1
2
3
sudo nginx
或者
sudo brew services start nginx

停止nginx

1
2
3
sudo nginx -s stop
或者
sudo brew services stop nginx

重新加载配置文件

1
2
3
sudo nginx -s reload
或者
sudo brew services restart nginx

安装常见问题:

安装Nginx输入命令brew install nginx报错

You have not agreed to the Xcode license. Please resolve this by running:

sudo xcodebuild -license

nginx设置本地跨域

在mac 终端运行命令的时候会被提示没有同意xcode 证书 ,这个时候需要在Terminal中同意license

此时 输入命令 sudo xcodebuild -license 空格到最后 输入agree 就可以了

HTML2canvas截图 解决 canvas 将图片转为base64报错 Uncaught DOMException Failed to execute 'toDataURL' on 'HTMLCanvasElement' Tainted canvases may not be exported

Posted on 2019-12-31 | In Frontend前端 , HTML2canvas-screenshot | 0 comments | Visitors:

最近在做一个分享生成海报的功能,用户进到页面,开始根据用户信息展示html网页,然后用html2canvas生成图片,用户长按图片保存到本地的功能

1、html2canvas 官网
html2canvas的详细介绍可以点击这里查看,其实简单来说就是通过canvas将HTML生成的DOM节点绘制到画布上,再可以通过自己的需求转换成图片.所以官方文档也说了,最后生成的效果不是100%相同的,这一点大家要有心理准备,无论怎样,一点点小瑕疵是肯定有的。

2、Code to Image github地址

3、dom-to-image 官网

html2canvas

html代码

1
<img src=""  crossorigin="Anonymous"></img>

js代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
document.querySelector(".main-picture")  中 main-picture=>是你要截图的最外层的选择器

html2canvas(document.querySelector(".main-picture"),{

useCORS:true,//设置可以素材可以跨域,移动端不兼容

allowTaint: false,//默认就是false,允许跨域

taintTest: true,//默认就是false,是否在渲染前测试图片

scale:window.devicePixelRatio,//解决清晰度的问题可以改变scale的值

}).then(canvas => { 

var dataUrl = canvas.toDataURL();

 var newImg = document.createElement("img");

newImg.src =  dataUrl;

document.body.appendChild(newImg);

});

但是发现一个问题,就是背景图片,或者说所有的图片都没有被截图截上,原因:

2、关于图片需要截图,所有的元素需要动态创建元素document.createElement('img') 这样图片就可以截图截出来了关于这一点,我可能被误导了,因为html2canvas里面有这段动态创建的代码,为什么我当时跨域也设置了但是没有截到加入的图片是因为,图片是一个静态资源,我通过nginx或者node服务器将资源指向本地方便测试,因为资源指向本地,而本地的nginx以及node配置的服务器上面没有配置header头加的access-control-allow-origion:*,所以产生了误解要动态创建

遇到的问题

跨域问题
就是背景图片,或者说所有的图片都没有被截图截上,原因:

如果页面上有跨域的图片需要做处理,同事html2canvas需要传允许跨域的字段,后端也要加允许跨域才可以
WechatIMG11.jpeg

1
2
3
4
5
6
7
8
9
     html2canvas(this.imgRef.current, {
useCORS: true // 开启跨域设置,需要后台设置cors
}).then((canvas) => {
// toDataURL函数生成img标签的可用数据 图片格式转成 base64
let dataURL = canvas.toDataURL("image/png")
console.log(dataURL)
})
<img crossOrigin='anonymous'
src="https://xxx.com/a.jpeg" className="w100" alt=""/>

这是为什么呢?
因为canvas将HTML生成的DOM节点绘制到画布上,然后用canvas.toDataURL导出图片,如果是读图片内容,是有浏览器同源限制,toDataURL是读取图片

2、返回图片是base64即可

接下来纯canvas绘制遇到的问题

用html2canvas转base64 ,但是在图片转base64的过程中遇到了两个问题

  • 1、图片无法绘制,转成的base64 用浏览器打开是空的透明画布,如图

image

代码片段如下:

1
2
3
4
5
6
7
8
var canvas=document.getElementById("canvas"),//获取canvas
ctx = canvas.getContext("2d"), //对应的CanvasRenderingContext2D对象(画笔)
img = new Image(),//创建新的图片对象
base64 = '' ;//base64

img.src = 'http://www.xxxx.png';
ctx.drawImage(img,0,0);
base64 = canvas.toDataURL("image/png");

这个时候我想到问题应该是 图片还没加载完毕 就已经绘制了,既然是这样,那么修改为以下:

1
2
3
4
5
6
7
8
9
var canvas=document.getElementById("canvas"),//获取canvas
ctx = canvas.getContext("2d"), //对应的CanvasRenderingContext2D对象(画笔)
img = new Image(),//创建新的图片对象
base64 = '' ;//base64
img.src = 'http://www.xxxx.png';
img.onload = function(){//图片加载完,再draw 和 toDataURL
ctx.drawImage(img,0,0);
base64 = canvas.toDataURL("image/png");
};

修改完毕我正要打算喝杯水庆祝一下,一刷新页面,一口老血喷了出来,chrome控制台又报错如下:

1
Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.

大概意思是canvas无法执行toDataURL方法:污染的画布无法输出,请原谅我的灵魂翻译。
经google 发现原来是受限于 CORS 策略,会存在跨域问题,虽然可以使用图像(比如append到页面上)但是绘制到画布上会污染画布,一旦一个画布被污染,就无法提取画布的数据,比如无法使用使用画布toBlob(),toDataURL(),或getImageData()方法;当使用这些方法的时候 会抛出一个安全错误,读图片内容,是有同源限制,canvas的drawimage应该是绘制,toBlob(),toDataURL(),或getImageData()读取图片内容

1
Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.

具体详情附上一个链接非常详细如下:https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image,非常值得一看,个人理解可能不到到位,还是建议读读这个链接。

解决方案:

图片设置 :crossOrigin属性
代码片段:img.setAttribute(“crossOrigin”,’Anonymous’)

完整代码:

1
2
3
4
5
6
7
8
9
10
11
``
var canvas=document.getElementById("canvas"),//获取canvas
ctx = canvas.getContext("2d"), //对应的CanvasRenderingContext2D对象(画笔)
img = new Image(),//创建新的图片对象
base64 = '' ;//base64
img.src = 'http://www.xxxx.png';
img.setAttribute("crossOrigin",'Anonymous')
img.onload = function(){//图片加载完,再draw 和 toDataURL
ctx.drawImage(img,0,0);
base64 = canvas.toDataURL("image/png");
};

stackoverflow上解决方案:
地址:https://stackoverflow.com/questions/20424279/canvas-todataurl-securityerror

Tips:如果遇到其他canvas 关于SecurityError 的问题,也可以尝试一下这个解决方法。
另外经过多次搜索,总结了几个小窍门

1:使用google 。baidu搜索的都是转来转去的几篇文章,干扰太大。
2:首先搜索bug之家 :stackoverflow,总有解决你的bug的方案,链接:https://stackoverflow.com/
3:web文档之家:mozilla的web文档 ,非常权威,非常详尽。链接:https://developer.mozilla.org/en-US/

html2canvas在微信浏览器中遇到的问题

微信浏览器中截不了图不会走then函数中

在真机ios的微信浏览器中截图会有问题,截不了图,这个时候将html2canvas版本回滚到1.0.0-rc.4即可,如果不行,记得清除微信的缓存再试试

1
“html2canvas”: “1.0.0-rc.4”
html2canvas截图不全的问题

image.png
原因:
我所知道的原因有两点,

第一点:在点击保存图片时,此时要保存的资源较多,造成模块并没有完全加载完毕,就已经生成了截图;
解决方案:(加上一个延时操作)

1
2
3
4
5
6
// 利用 html2canvas 下载 canvas
setTimeout(() => {
html2canvas(img, { canvas: canvas }).then(function(canvas) {
_this.photoUrl = canvas.toDataURL();
});
}, 500);

第二点:滚轮滑动造成的,主要是html2canvas是根据body进行截图,若内容高度高于body时,就会出现这样的问题(大概意思就是有滚动条时造成的)
解决方案:body内加一层div设置高度是100vh超出滚动,这样就不会高于body了

moker-api

Posted on 2019-12-31 | In Frontend前端 , team-norm团队规范 , mock-data-norm模拟数据规范 | 0 comments | Visitors:

mocker-api这里主要讲我在反向代理遇见的问题,其他的请看官方文档。
在我主要用来进行开发的公司的电脑上这样匹配接口就可以代理上,但是按照文档上的直接反向代理的写法却代理不成功

1
'/:owner/:repo/raw/:ref/*': 'http://127.0.0.1:2018'

但是在我自己的电脑上接口竟然没匹配到!
开始我以为是新版本的webpack-dev-server的问题导致我本地服务起的有问题,虽然爆出了这个警告,但是我可以访问我的页面,只是mock反向代理不成功而已,直接mock假数据就可以成功
WeChat3ccbcd8e228a5aa0ae7d7538fb46be26.png
最后发现按照文档上以正则匹配的形式去写才可以匹配上!

1
'/:owner/:repo/raw/:ref/(.*)': 'http://127.0.0.1:2018'
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 proxy = {
// Priority processing.
// apiMocker(app, path, option)
// This is the option parameter setting for apiMocker
_proxy: {
proxy: {
'/repos/(.*)': 'https://api.github.com/',
'/:owner/:repo/raw/:ref/(.*)': 'http://127.0.0.1:2018'
},
changeHost: true,
// modify the http-proxy options
httpProxy: {
options: {
ignorePath: true,
},
listeners: {
proxyReq: function (proxyReq, req, res, options) {
console.log('proxyReq');
},
},
},
},
}
module.exports = proxy;

当我的node版本变为v10.6.0,发现我的代理需要/repos/(.)变回为/repos/
mocker-api 的version为1.9.0我需要这样去匹配路由/repos/(.*)
mocker-api 的version不同版本是否为不同的匹配规则有待验证

埋点之眼球曝光

Posted on 2019-12-31 | In Frontend前端 , 埋点 | 0 comments | Visitors:

埋点曝光需求要求在页面在用户的视野中就发送一个曝光日志,一个页面的一个模块最多发送一次,也就是首次曝光才发送,之后在进入视野中就不用在发曝光日志了

一、当看到这个需求的时候我们第一点肯定想到要监听滚动事件

1
2
3
window.addEventListener('scroll', function(){
console.log('监听滚动')
});

二、然后优化下滚动事件,加上throttle。

1
2
3
4
5
6
7
8
9
10
11
window.addEventListener('scroll',newScroll(exposure,500));
const newScroll = function(realFunc,time){
let timeout = null;
return function (){
clearTimeout(timeout);
timeout = setTimeout(realFunc,time)
}
};
function exposure() {
console.log('监听滚动增加throttle')
}

三、然后优化下滚动事件,加上throttle。
注意scroll监听的匿名事件是不可以取消监听事件的,我想要的是页面中所有需要曝光的地方曝光结束后,取消监听事件

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
//需要曝光的点
const exposureList = [
{
selector: 'aaaa',
aid: '6790'
},
];
window.addEventListener('scroll',newScroll);
const newScroll = function(realFunc,time){
let timeout = null;
return function (){
clearTimeout(timeout);
timeout = setTimeout(realFunc,time)
}
}(exposure,500);
function exposure() {
if(exposureList.length == 0){
window.removeEventListener("scroll", newScroll);
}
exposureList.forEach(function(value,index) {
const currentDom = document.getElementById(value.selector);
if(currentDom){
const currentClientRect = currentDom.getBoundingClientRect();
const bottomToTopView = currentClientRect.top >0 && window.innerHeight/3*2>currentClientRect.top;
const topToBottomView = currentClientRect.top <0 && currentClientRect.top > - currentDom.offsetHeight/2;
if(bottomToTopView ||topToBottomView){
//发送曝光事件
exposureList.splice(index,1);
}
}
}

tips: 这里用到了getBoundingClientRect文章出处

getBoundingClientRect用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置。getBoundingClientRect是DOM元素到浏览器可视范围的距离(不包含文档卷起的部分)。

该函数返回一个Object对象,该对象有6个属性:top,lef,right,bottom,width,height;

1.兼容性
getBoundingClientRect()最先是IE的私有属性,现在已经是一个W3C标准。所以你不用当心浏览器兼容问题,不过还是有区别的:IE只返回top,lef,right,bottom四个值,不过可以通过以下方法来获取width,height的值

1
2
document.getElementById(value.selector).offsetHeight();
document.getElementById(value.selector). clientHeight();
1
2
3
4
5
6
7
8
9
10
11
12
13
var ro = object.getBoundingClientRect();
var Width = ro.right - ro.left;
var Height = ro.bottom - ro.top;

//兼容所有浏览器写法:

var ro = object.getBoundingClientRect();
var Top = ro.top;
var Bottom = ro.bottom;
var Left = ro.left;
var Right = ro.right;
var Width = ro.width||Right - Left;
var Height = ro.height||Bottom - Top;

Search Engine Optimization

Posted on 2019-12-19 | In Frontend前端 , PreRender预渲染 | 0 comments | Visitors:

解决SEO(Search Engine Optimization),首屏问题 , 页面较少,且预渲染相对于SSR比较简单,预渲染可以极大的提高网页访问速度。而且配合一些meat插件,完全可以满足SEO需求。
Prerender服务需要有NodeJs环境支持,如果之前服务器环境没有NodeJs需要先进行安装。

安装

1
npm install --save prerender-spa-plugin

prerender-spa-plugin中有puppeteer
可能会报错

1
2
ERROR: Failed to download Chromium r515411! Set "PUPPETEER_SKIP_CHROMIUM_DOWNLOA
D" env variable to skip download.

此时可以试试淘宝镜像安装

1
cnpm install --save prerender-spa-plugin

因为在执行安装的过程中需要执行install.js,这里会下载Chromium,官网建议是进行跳过,我们可以执行 —ignore-scripts 忽略这个js执行。也可以通过设置环境变量set PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1阻止下载 Chromium (因为封网,直接下载会失败)

1
npm i --save puppeteer --ignore-scripts

然后手动下载Chromium
解压到你当前项目中的node_modules/puppeteer/.local-chromium/mac-571375下就可以了

配置


webpack.base.conf.js

1
2
3
4
5
6
7
module.exports = {
context: path.resolve(__dirname, '../'),
//entry: {app: './src/main.js'}
entry: {
app: ['babel-polyfill','./src/main.js']//babel-polyfill会仿效一个完整的 ES2015+ 环境,并意图运行于一个应用中而不是一个库/工具。
}
}

webpack.prod.conf.js
开始我这样写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const PrerenderSpaPlugin = require('prerender-spa-plugin')
new PrerenderSpaPlugin({
// 生成文件的路径,也可以与webpakc打包的一致。
// 这个目录只能有一级,如果目录层次大于一级,在生成的时候不会有任何错误提示,在预渲染的时候只会卡着不动。
staticDir: path.join(__dirname, '../dist'),
// List of routes to prerender
// 对应自己的路由文件,比如index有参数,就需要写成 /index/param1。
routes: [ '/', '/team', '/analyst','/voter','/sponsor'],

// 这个很重要,如果没有配置这段,也不会进行预编译
renderer: new Renderer({
renderAfterTime: 5000,
// eg, with `document.dispatchEvent(new Event('render-event'))`
// 在 main.js 中 document.dispatchEvent(new Event('render-event')),两者的事件名称要对应上。
renderAfterDocumentEvent: 'render-event',//这句话会报错building for production...[prerender-spa-plugin] Unable to prerender all routes!
UnhandledPromiseRejectionWarning: Error: waiting for selector "render-event" failed: timeout 30000ms exceeded

// Optional - Wait to render until the specified element is detected using `document.querySelector`
// renderAfterElementExists: 'render-event',
})
}),

结果报错 :building for production…[prerender-spa-plugin] Unable to prerender all routes! UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Navigation Timeout Exceeded: 30000ms exceededrender AfterTime: 5000
偶然看到一个解决的办法 UnhandledPromiseRejectionWarning

1
2
3
4
5
6
7
8
9
10
const PrerenderSpaPlugin = require('prerender-spa-plugin')
const Renderer = PrerenderSpaPlugin.PuppeteerRenderer

new PrerenderSPAPlugin({
staticDir: path.join(__dirname, '../dist'),
routes: ['/home/homePage'],
renderer: new PrerenderSPAPlugin.PuppeteerRenderer({//这样写renderAfterTime生效了
renderAfterTime: 5000
})
})

开发目录main.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
i18n,
components: { App },
template: '<App/>',
render: h => h(App),
/* 这句非常重要,否则预渲染将不会启动 */
mounted () {
document.dispatchEvent(new Event('render-event'))
}
})

就可以在dist文件夹下生成对应路由的文件夹里面包含页面预渲染信息的html

1
2
3
4
5
6
7
8
9
10
//比如PrerenderSPAPlugin中的 routes: [ '/','/home/homePage']
就会生成下面这样的路径
``` bash
dist
│ ├── home
│ │ └── index.html
│ ├── homePage
│ └── index.html
├── index.html
└── static

结合管理头部标签插件vue-meta-info
main.js中加

1
2
import MetaInfo from 'vue-meta-info'
Vue.use(MetaInfo)

页面的vue中加metaInfo信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export default {
metaInfo: {
title: 'We Inc',
meta: [
{
name: 'keywords',
content: '关键字1,关键字2,关键字3'
},
{
name: 'description',
content: '这是一段网页的描述'
}
]
}
}

就可以将关键字预渲染到html的页面中去

渲染结束遇到的问题


一、Uncaught ReferenceError: webpackJsonp is not defined
发现是config/index.js

1
assetsPublicPath: '/', //路径是/不是./

这里要强调一点如果你的assetsPublicPath设置成’www.xxx.com',生成的html是没有内容的而且插件vue-meta-info的设置的信息也不会加载出来
生成html图片.png
如果是/找到js文件的话,生成这种带你写的结构的html
image.png
image.png

二、解决vuex requires a Promise polyfill in this browser问题
添加babel-polyfill插件
webpack.base.conf.js

1
2
3
4
5
6
7
module.exports = {
context: path.resolve(__dirname, '../'),
//entry: {app: './src/main.js'}
entry: {
app: ['babel-polyfill','./src/main.js']//babel-polyfill会仿效一个完整的 ES2015+ 环境,并意图运行于一个应用中而不是一个库/工具。
}
}

seo优化

Posted on 2019-10-24 | In Frontend前端 , seo | 0 comments | Visitors:

搜索引擎对您的内容进行排名之前,它首先需要知道它的存在。通常是通过”爬虫抓取”页面的数据。这里主要从技术方面去叙述我所知道的优化

代码优化

  • title 设置
  • keywords 设置
  • description 设置
  • meta属性设置
  • 页面H1-H3标签的使用
  • 图片设置Alt属性
  • 404页面设置、301重定向
  • Robot.txt
  • 删除页面无用代码,css、js优化

网站结构

  • 扁平化网站架构:扁平化的网站结构,意味着用户(和搜索引擎爬虫) 只需4次(或更少)点击, 就可以到达网站上的任何页面。
  • 导航和面包屑导航
  • 页面打开速度
  • 简化页面结构 重要的内容尽量排在从上到下从左到右的结构
  • sitemap.xml
  • 使用内部链接

    网站地图

    站点链接

  • 不良内部链接: Google依靠内链来抓取您网站上的所有页面。没有内部链接的页面通常不会被抓取。
  • robots.txt作用: Robots.txt是一个文本文件,告诉Google它的爬虫是否可以在您的网站上访问。如果你用了这个文件,则谷歌不会抓取。
  • 不编入索引的页面: 您可以使用noindex元标记或HTTP标头把页面排除于谷歌索引之外。如果您网站上的其他网页只有来自noindexed页面的内链,谷歌很有可能无法找到他们。
  • Nofollowed内链: 是HTML页面中a标签的属性值。这个标签的意义是告诉搜索引擎”不要追踪此网页上的链接或不要追踪此特定链接”。

nofollow的作用?

1、防止不可信的内容,最常见的是博客上的垃圾留言与评论中为了获取外链的垃圾链接,为了防止页面指向一些拉圾页面和站点。
2、为了防止付费链接影响Google的搜索结果排名。
3、引导爬虫抓取有效的页面,避免爬虫抓取一些无意义的页面,影响爬虫抓取的效率。

nofollow2个基本用法

  • 用于标签,告诉搜索引擎(爬虫)该页面上所有链接都无需追踪(这种方法一般用的较少)
    1
    <meta name="robots" content="nofollow" />
  • 用于标签,告诉搜索引擎(爬虫)加上此属性的链接无需追踪(一般推荐用这种方法)
    1
    <a href="login.aspx" rel="nofollow">登录</a>
<1…34
Amy Chen

Amy Chen

All problems in computer science can be solved by another level of indirection. -by David John Wheeler

36 posts
28 categories
31 tags
RSS
GitHub E-Mail Skype 简书
© 2021 Amy Chen
Powered by Hexo
|
Theme — NexT.Pisces v5.1.4