-
Notifications
You must be signed in to change notification settings - Fork 0
/
content.json
1 lines (1 loc) · 90.9 KB
/
content.json
1
{"meta":{"title":"睡不醒的黑客","subtitle":"😏2333","description":"兴趣使然的前端博客","author":"HanQ","url":"https://hq229075284.github.io/blog"},"pages":[{"title":"About","date":"2022-08-31T01:14:17.716Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"about/index.html","permalink":"https://hq229075284.github.io/blog/about/index.html","excerpt":"","text":""},{"title":"","date":"2022-08-31T01:14:17.716Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"github/index.html","permalink":"https://hq229075284.github.io/blog/github/index.html","excerpt":"","text":"@keyframes rotate { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } @keyframes slideUpFadeIn { 0% { opacity: 0; transform: translateY(80px); } 100% { opacity: 1; transform: translateY(0px); } } .loading { text-align: center; padding-top: 20px; } .loading::before { content: ''; vertical-align: middle; border: 1px solid #bec0c1; width: 14px; height: 14px; box-sizing: border-box; display: inline-block; border-radius: 50%; border-right-color: transparent; animation: rotate 1s linear infinite both; margin-right: 8px; } .slideUpFadeIn { animation: slideUpFadeIn 0.5s ease-in both; } loading... let page = 1 const pageSize = 100 const token = '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' function getList(prevData = []) { return axios({ url: `https://api.github.com/user/repos`, method: 'get', params: { accept: 'application/vnd.github.v3+json', sort: 'created', direction: 'desc', page: page, per_page: pageSize, since:new Date('2010-01-01 00:00:00').toISOString(), before:new Date().toISOString() }, headers: { Authorization: 'token '+token } }).then(({ data }) => { prevData = prevData.concat(data) if (prevData.length < page * pageSize) { return Promise.resolve(prevData) } page += 1 return getList(prevData) }) } function setStyle(dom, styles) { for (let property in styles) { const attribute = styles[property] dom.style[property] = attribute } } const classifyMap = { // 最新添加 new: { title: '最新添加', list: [] }, // 维护中 maintenance: { title: '维护中', list: [] }, // fork fork: { title: 'fork', list: [] }, // other other: { title: '未分类', list: [] } } function generateContentBySort(data) { const ul = document.createElement('ul') if (data.length === 0) { const div = document.createElement('div') div.textContent = '暂无数据' setStyle(div, { 'text-align': 'center', padding: '10px' }) return div } data.map(one => { const li = document.createElement('li') setStyle(li, { display: 'inline-block', width: '33%', padding: '4px', 'box-sizing': 'border-box', 'word-break': 'break-all', 'vertical-align': 'top', height: '160px' }) const a = document.createElement('a') setStyle(a, { borderRadius: '10px', backgroundColor: '#fff', height: '100%', 'box-shadow': '0 0 2px #eceff2', display: 'block' }) a.href = one.html_url a.target = '_blank' li.append(a) let div div = document.createElement('div') div.textContent = one.name div.setAttribute('title', one.name) setStyle(div, { padding: '10px', 'font-size': '24px', 'font-weight': 700, 'border-bottom': '1px solid #eceff2', 'white-space': 'nowrap', 'text-overflow': 'ellipsis', 'overflow': 'hidden' }) a.append(div) div = document.createElement('div') div.textContent = one.description setStyle(div, { padding: '10px', 'font-size': '14px', 'color': '#585858' }) a.append(div) ul.append(li) }) return ul } function isMatch(regList, str) { for (r of regList) { const reg = new RegExp(r) if (reg.test(str)) { return true } } return false } const maintenanceRegs = [ 'eslint-config', 'file-split', 'blog', 'comet-test', 'react-ssr-test', 'test-component', 'image-view-component', 'usualAlgorithm', 'about-common-logic', 'blog', 'blog-assets', ] getList().then(data => { classifyMap.new.list = data.splice(0, 12) data.forEach(one => { if (one.fork) { classifyMap.fork.list.push(one) return } if (isMatch(maintenanceRegs, one.name)) { classifyMap.maintenance.list.push(one) return } classifyMap.other.list.push(one) }) $repositoryContainer = document.querySelector('.repository-container') $repositoryContainer.children[0].remove() for (let key in classifyMap) { const h2 = document.createElement('h2') // const a=document.createElement('a') // a.setAttribute('id',key) // a.href=\"#\"+key // a.textContent = classifyMap[key].title+'('+classifyMap[key].list.length+')' // h2.append(a) h2.textContent = classifyMap[key].title + ' (' + classifyMap[key].list.length + ')' setStyle(h2, { padding: '10px', 'margin-bottom': '4px', 'border-bottom': '1px solid #eceff2', 'font-size': '24px' }) $repositoryContainer.append(h2) const contentHtmlNode = generateContentBySort(classifyMap[key].list) $repositoryContainer.append(contentHtmlNode) } $repositoryContainer.classList.add('slideUpFadeIn') }) void 0"}],"posts":[{"title":"notes","slug":"notes","date":"2022-08-31T01:14:17.716Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"2022/08/notes/","link":"","permalink":"https://hq229075284.github.io/blog/2022/08/notes/","excerpt":"杂记 杂记 white-space表现形式 terset配置 MutationObserver注意点 容器出现滚动条时,子容器的宽度是否因为滚动条而改变 webpack 多线程和cache优化 其他记录-以后分类 white-space表现形式 white-space👇 空格( ) 非断空格(&nbsp;) 换行符(\\n) normal 保留1个空格,或者在多空格的时候仅保留1个空格 全部非断空格都保留 表现为1个空格,不换行 pre 保留全部的空格 全部非断空格都保留 会换行,但当文本溢出容器时不会自动换行 pre-line 保留1个空格,或者在多空格的时候仅保留1个空格 全部非断空格都保留 会换行 pre-wrap 保留全部的空格 全部非断空格都保留 会换行 no-wrap 保留1个空格,或者在多空格的时候仅保留1个空格 全部非断空格都保留 表现为1个空格,不换行","text":"杂记 杂记 white-space表现形式 terset配置 MutationObserver注意点 容器出现滚动条时,子容器的宽度是否因为滚动条而改变 webpack 多线程和cache优化 其他记录-以后分类 white-space表现形式 white-space👇 空格( ) 非断空格(&nbsp;) 换行符(\\n) normal 保留1个空格,或者在多空格的时候仅保留1个空格 全部非断空格都保留 表现为1个空格,不换行 pre 保留全部的空格 全部非断空格都保留 会换行,但当文本溢出容器时不会自动换行 pre-line 保留1个空格,或者在多空格的时候仅保留1个空格 全部非断空格都保留 会换行 pre-wrap 保留全部的空格 全部非断空格都保留 会换行 no-wrap 保留1个空格,或者在多空格的时候仅保留1个空格 全部非断空格都保留 表现为1个空格,不换行 terset配置12345678910111213141516171819202122232425262728293031323334new TerserPlugin({ // test: /\\.js(\\?.*)?$/i, // exclude: /node_modules/, terserOptions: { // ecma: undefined, warnings: false, parse: { ecma: 8 }, compress: { ecma: 5,// 默认值,防止es5代码被替换成es6+的代码 passes: 3, pure_funcs: ['console.log', 'console.info'], toplevel: true // https://github.com/terser/terser/issues/120(已修复) // inline: 2 }, mangle: true, // Note `mangle.properties` is `false` by default. output: { ecma: 5, // 默认值,防止es5代码被替换成es6+的代码 // Turned on because emoji and regex is not minified properly using default // https://github.com/facebook/create-react-app/issues/2488 ascii_only: true, }, module: false, toplevel: false, nameCache: null, ie8: false, keep_classnames: undefined, keep_fnames: false, safari10: false, }, // parallel: getCpuCount()}) MutationObserver注意点1234567891011121314151617181920212223242526observer=new MutationObserver(mutations=>{console.log('1',mutations)})observer2=new MutationObserver(mutations=>{console.log('2',mutations)})observer3=new MutationObserver(mutations=>{console.log('3',mutations)})const input=document.querySelector('input')observer3.observe(input,{attributes: true})observer2.observe(input,{attributes: true})observer.observe(input,{attributes: true})setTimeout(()=>{ input.classList.add('a1')},1000)// 当监听的元素发生改变时会按new的顺序执行监听回调// 所以结果为:// 1 [MutationRecord]// 2 [MutationRecord]// 3 [MutationRecord] 容器出现滚动条时,子容器的宽度是否因为滚动条而改变 TODO:增加测试数据表格 仅当初次渲染时,子元素的高度超过父元素的高度(此时父元素的overflow-y值为auto),导致父元素出现滚动条,如果子元素的宽度是一个固定的大小,且在视图上与滚动条有重合,此时滚动条会覆盖在子元素的视图上,当再次触发渲染时,恢复正常(出现横向滚动条),此现象在水平和垂直方向上都存在 webpack 多线程和cache优化 第一次pack(ms) 第二次pack(ms) 无优化 54561 53825 cache-loader 52237 48472 thread-loader 52445 52049 cache-loader & thread-loader 55297 52036 hard-source-webpack-plugin(缺乏维护) 54447 42747 cache-loader、thread-loader优化不明显,后期再看看 其他记录-以后分类 absolute定位将relative定位的父级的padding区域左上角视为原点 在无relaitve父级的情况下,将window窗口的左上角视为原点 DOM中ELEMENT元素在动画和过渡的过程中,通过getClientRects获取的ELEMENT元素位置信息是实时的,在变化的过程中会持续改变 vue组件实例的$vnode对象等同于相应插槽中的相应插槽位置的对象,比如:1exampleVueComponent.$vnode === this.$slots.default[0] // true vue实例的$destroy函数不会删除对应组件的节点,仅用于同步且按顺序触发beforeDestroy 和destroyed这两个hook,,示例: 1<div id=\"app\"></div> 123456789101112131415161718192021222324252627282930const comp1 = { mounted() { console.log(window.b = this.$slots.default[0]) }, render(h) { return h('div', {}, this.$slots.default) }}const comp2 = { beforeDestroy() { console.log('beforeDestroy'); return new Promise(() => { }) }, destroyed() { console.log('destroyed') }, render(h) { return h('h2', {}, ['has a h2']) }}new Vue({ el: '#app', render(h) { return h('comp1', {}, [h('comp2', { ref: 'comp2' })]) }, mounted() { console.log(window.a = this.$refs.comp2) this.$refs.comp2.$destroy(); console.log('h2 remove') }, components: { comp1, comp2, },})","categories":[],"tags":[]},{"title":"Grid","slug":"css-grid","date":"2022-04-15T16:11:20.000Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"2022/04/css-grid/","link":"","permalink":"https://hq229075284.github.io/blog/2022/04/css-grid/","excerpt":"","text":"css-grid(手机自带浏览器和webview,以及非IE的主流桌面浏览器)demo 网格 ⚠👇 以下显式网格、隐式网格暂不考虑溢出的情况 显式网格 grid-template-columns:指定显式网格列的数量和列宽 gird-template-rows:指定显式网格行的数量和行高 grid-template-columns和gird-template-rows所构成的网格为显式网格,其它额外创建的网格为隐式网格 网格容器宽度固定时 网格宽度为固定值,该列的列宽等于这个固定值 网格宽度为nfr时,该列的列宽为容器宽度-固定网格宽度后被列等分后的n份 网格容器高度固定时, 网格高度为固定值,该行的行高等于这个固定值 网格高度为nfr时,网格的行高为容器高度-固定网格高度后被行等分后的n份 网格容器未指定高度时,显式网格内的网格高度由同行内容高度最高的网格决定 网格容器未指定宽度且不超过父级元素宽度时,所有网格宽度由所有网格中内容宽度最大的网格决定 隐式网格 grid-auto-columns:指定隐式网格的列宽 grid-auto-rows:指定隐式网格的行高 容器大小固定时,网格大小按网格容器等分计算,同显式网格的1和2 容器大小不固定时,同显式网格的3和4 显示/隐式混合同时存在于网格容器中若网格容器内同时存在隐式网格和显式网格: 容器大小固定时,所有网格的宽度计算同显式网格的1,隐式网格高度计算同显式网格的3,显式网格的高度计算是在网格容器高度减去隐式网格占用高度后剩余高度的基础上,进行同显式网格的2 容器大小不固定时,同显式网格的3和4 frfr单位表示网格布局前,扣除明确的使用空间(如:500px)后,剩余可用空间被等分后的一份 123.grid{ grid-template-columns: 500px 1fr 2fr; // 计算方式 => (width - 500px)/(1fr + 2fr)} minmax网格行高或列宽的大小限定区间 参考 网格布局的基本概念","categories":[{"name":"css","slug":"css","permalink":"https://hq229075284.github.io/blog/categories/css/"}],"tags":[{"name":"css","slug":"css","permalink":"https://hq229075284.github.io/blog/tags/css/"}]},{"title":"css选择器和style属性的权值计算和比较","slug":"css优先级","date":"2022-04-15T16:11:10.000Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"2022/04/css优先级/","link":"","permalink":"https://hq229075284.github.io/blog/2022/04/css%E4%BC%98%E5%85%88%E7%BA%A7/","excerpt":"16. Calculating a selector’s specificity 6.4.3 Calculating a selector’s specificity 误区在学习过程中,你可能发现给选择器加权值的说法,即 ID 选择器权值为 100,类选择器权值为 10,标签选择器权值为 1,当一个选择器由多个 ID 选择器、类选择器或标签选择器组成时,则将所有权值相加,然后再比较权值。这种说法其实是有问题的。比如一个由 11 个类选择器组成的选择器和一个由 1 个 ID 选择器组成的选择器指向同一个标签,按理说 110 > 100,应该应用前者的样式,然而事实是应用后者的样式。错误的原因是:权重的进制是并不是十进制,CSS 权重进制在 IE6 为 256,后来扩大到了 65536,现代浏览器则采用更大的数量。。还是拿刚刚的例子说明。11 个类选择器组成的选择器的总权值为 110,但因为 11 个均为类选择器,所以其实总权值最多不能超过 100, 你可以理解为 99.99,所以最终应用后者样式。","text":"16. Calculating a selector’s specificity 6.4.3 Calculating a selector’s specificity 误区在学习过程中,你可能发现给选择器加权值的说法,即 ID 选择器权值为 100,类选择器权值为 10,标签选择器权值为 1,当一个选择器由多个 ID 选择器、类选择器或标签选择器组成时,则将所有权值相加,然后再比较权值。这种说法其实是有问题的。比如一个由 11 个类选择器组成的选择器和一个由 1 个 ID 选择器组成的选择器指向同一个标签,按理说 110 > 100,应该应用前者的样式,然而事实是应用后者的样式。错误的原因是:权重的进制是并不是十进制,CSS 权重进制在 IE6 为 256,后来扩大到了 65536,现代浏览器则采用更大的数量。。还是拿刚刚的例子说明。11 个类选择器组成的选择器的总权值为 110,但因为 11 个均为类选择器,所以其实总权值最多不能超过 100, 你可以理解为 99.99,所以最终应用后者样式。 其他参考","categories":[{"name":"css","slug":"css","permalink":"https://hq229075284.github.io/blog/categories/css/"}],"tags":[{"name":"css","slug":"css","permalink":"https://hq229075284.github.io/blog/tags/css/"}]},{"title":"border-image-slice","slug":"border-image-slice","date":"2022-01-13T16:55:16.000Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"2022/01/border-image-slice/","link":"","permalink":"https://hq229075284.github.io/blog/2022/01/border-image-slice/","excerpt":"","text":"border-image-slice将元素区域(border-box)分割成9个区域,其中8个区域分别对应构成border的8个区域。每个由borderr-image-slice划分的区域内的图像将被渲染到对应的border区域中。 如下图: 假如border-image-slice的值为40 60,表示垂直距离为40px,水平距离为60px,区域1的大小为60x40(长*宽),内中渲染对应区域的图像。 假如border的值为20,表示border-top-width为20px,border-left-width为20px,区域1的大小为20x20(长*宽),内中渲染的图像为border-image-slice划分区域中的区域1内的图像。","categories":[{"name":"css","slug":"css","permalink":"https://hq229075284.github.io/blog/categories/css/"}],"tags":[{"name":"css","slug":"css","permalink":"https://hq229075284.github.io/blog/tags/css/"}]},{"title":"css主题化","slug":"css主题化","date":"2021-12-01T16:10:24.000Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"2021/12/css主题化/","link":"","permalink":"https://hq229075284.github.io/blog/2021/12/css%E4%B8%BB%E9%A2%98%E5%8C%96/","excerpt":"css主题化(use Less) 使用css var(全局变量) 123456789101112131415161718// 当给定值未定义时将会用备用值替换@font-size:var(--theme-font-size,36px);:root{}/* https://developer.mozilla.org/zh-CN/docs/Web/CSS/Using_CSS_custom_properties#%E6%9C%89%E6%95%88%E6%80%A7%E5%92%8C%E5%80%BC计算时有效性1. 检查属性 color 是否为继承属性。是,但是 <p> 没有任何父元素定义了 color 属性。转到下一步。2. 将该值设置为它的默认初始值,比如 black。*/body{ font-size:20PX;}#app{ font-size:@font-size !important;} 脚本改变css变量的值 123function changeTheme(n){ document.documentElement.style.setProperty('--theme-font-size',n+'px')}","text":"css主题化(use Less) 使用css var(全局变量) 123456789101112131415161718// 当给定值未定义时将会用备用值替换@font-size:var(--theme-font-size,36px);:root{}/* https://developer.mozilla.org/zh-CN/docs/Web/CSS/Using_CSS_custom_properties#%E6%9C%89%E6%95%88%E6%80%A7%E5%92%8C%E5%80%BC计算时有效性1. 检查属性 color 是否为继承属性。是,但是 <p> 没有任何父元素定义了 color 属性。转到下一步。2. 将该值设置为它的默认初始值,比如 black。*/body{ font-size:20PX;}#app{ font-size:@font-size !important;} 脚本改变css变量的值 123function changeTheme(n){ document.documentElement.style.setProperty('--theme-font-size',n+'px')} 多套样式 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465// 公共部分,theme.less// #region 主题变量@themes: primary, dark, light;@colors: #00f, #000, #fff;@primary-color: #00f;@dark-color: #000;@light-color: #fff;// #endregion// 在调用该mixin的地方,循环每个主题,生成每个主题对应的样式.createThemeStyle(@index) when (@index <= length(@themes)) { @theme: extract(@themes, @index); .themeStyle(@theme, @index); .createThemeStyle(@index+1);}// 开始创建.createThemeStyle(1);// 创建主题下的变量.createThemeVar(@theme) { @_theme-color: \"@{theme}-color\"; @theme-color: @@_theme-color;}// --------------------------// 业务部分// 引入主题变量@import \"theme.less\";// 模式匹配,生成对应的主题样式.style-mixin(primary,@index) { &.module1 { background-color: extract(@colors, @index); }}.style-mixin(dark,@index) { &.module2 { background-color: extract(@colors, @index); }}.style-mixin(light,@index) { &.module3 { background-color: extract(@colors, @index); }}// 公共样式.style-common() { font-size: 16px;}.themeStyle(@theme,@index) { // 此mixin在createThemeStyle中会循环调用 .@{theme} { // 注入主题变量 .createThemeVar(@theme); // 使用主题变量 color: @theme-color; // 注入公共样式 .style-common(); // 调用对应主题的样式生成mixin .style-mixin(@theme, @index); }} 精简版 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970// -----------------------------// 公共部分,theme.less// #region 主题变量@themes: primary, dark, light;@colors: #00f, #000, #fff;@primary-color: #00f;@dark-color: #000;@light-color: #fff;// #endregion// 在调用该mixin的地方,循环每个主题,生成每个主题对应的样式.createThemeStyle(@index) when (@index <= length(@themes)) { @theme: extract(@themes, @index); .themeStyle(); .createThemeStyle(@index+1);}// 开始创建.createThemeStyle(1);// 创建主题下的变量.createThemeVar() { @_theme-color: \"@{theme}-color\"; @theme-color: @@_theme-color;}// --------------------------// 业务部分// 注入公共样式.common { font-size: 16px;}// 引入主题变量@import \"./theme.less\";// 模式匹配,生成对应的主题样式.style-mixin(primary) { &.module1 { font-size: 14px; background-color: @theme-color; }}.style-mixin(dark) { &.module2 { font-size: 15px; background-color: extract(@colors, @index); }}.style-mixin(light) { &.module3 { font-size: 16px; background-color: extract(@colors, @index); }}.themeStyle() { // 此mixin在createThemeStyle中会循环调用 .@{theme} { // 注入主题变量 .createThemeVar(); // 使用主题变量 color: @theme-color; // 调用对应主题的样式生成mixin .style-mixin(@theme); }}","categories":[],"tags":[]},{"title":"glob_match","slug":"glob-match","date":"2021-05-13T12:50:08.000Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"2021/05/glob-match/","link":"","permalink":"https://hq229075284.github.io/blog/2021/05/glob-match/","excerpt":"","text":"glob match总结(bash下) *匹配任意数量的非/字符 ? 仅匹配一个非/字符 **匹配任意数量的非/字符,而且当它独立构成匹配模式中的一段时,也可以匹配/,在bash下要开启globstar,shopt -s globstar [...]匹配[]之间的字符,存在范围匹配时,需要在bash下需开启globasciiranges,保证大小写匹配的正常;[^…] 或 [!…]匹配不包括在[]内的字符;可以通过[:class:]使用POSIX标准的匹配关键词:alnum 、alpha、ascii 、blank、cntrl、digit、graph、lower、print、punct、space、upper、word、xdigit,word 匹配字符、数字和_ 多模式匹配及反选 ?(pattern|pattern|…):匹配0个或1个pattern *(pattern|pattern|…):匹配0个或以上的pattern +(pattern|pattern|…):匹配1个或以上pattern @(pattern|pattern|…):匹配其中1个pattern,测试效果和+(pattern|pattern|...)相似(需进一步研究区别) !(pattern|pattern|…):不匹配任一pattern 参考:man bash下的Pattern Matching部分","categories":[{"name":"shell","slug":"shell","permalink":"https://hq229075284.github.io/blog/categories/shell/"}],"tags":[{"name":"glob","slug":"glob","permalink":"https://hq229075284.github.io/blog/tags/glob/"}]},{"title":"原码、反码、补码","slug":"原码、反码、补码","date":"2021-04-23T17:35:26.000Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"2021/04/原码、反码、补码/","link":"","permalink":"https://hq229075284.github.io/blog/2021/04/%E5%8E%9F%E7%A0%81%E3%80%81%E5%8F%8D%E7%A0%81%E3%80%81%E8%A1%A5%E7%A0%81/","excerpt":"原码、反码、补码原码1231 => 000000011 => 00000010-1 => 10000001 当一个正数与一个负数的原码做加法时: 1234567891011121314151 + (-1) 00000001+ 10000001----------- 10000010 10000010 => -2 // 按原码的方式转化为十进制2 + (-1) 00000010+ 10000001----------- 10000011 10000011 => -3 // 按原码的方式转化为十进制 异号数字的原码相加,原码的符号位一定为1,和实际情况不服,比如: 2 + -1。","text":"原码、反码、补码原码1231 => 000000011 => 00000010-1 => 10000001 当一个正数与一个负数的原码做加法时: 1234567891011121314151 + (-1) 00000001+ 10000001----------- 10000010 10000010 => -2 // 按原码的方式转化为十进制2 + (-1) 00000010+ 10000001----------- 10000011 10000011 => -3 // 按原码的方式转化为十进制 异号数字的原码相加,原码的符号位一定为1,和实际情况不服,比如: 2 + -1。 反码123456// 正数 100000001 //原码00000001 //反码// 负数 -110000001 //原码11111110 //反码 正数的反码就是原码本身,负数的反码就是原码除符号位外,按位取反。 12345671 + (-1) 00000001 // 1的反码+ 11111110 // -1的反码----------- 11111111 11111111 =反码转原码=> 10000000 =转十进制=> -0 // 按原码的方式转化为十进制 反码相加,解决了相反数之间相加的问题,但 12345672 + (-1) 00000010 // 2的反码+ 11111110 // -1的反码----------- 00000000 00000000 =反码转原码=> 00000000 =转十进制=> +0 // 按原码的方式转化为十进制 计算结果仍有问题 补码(仍需完善)正数的补码等于原码,负数的补码等于原码自低位向高位,第一个出现的‘1’及其右边的‘0’保持不变,左边的各位按位取反,符号位不变。 比如在4位系统下: 12342 + (-1) 0010+ 1001 将原码计算转换为补码计算: 2 + (-1)=2 + (2^4-1) 二进制编码 十进制 描述 0000 0 0001 1 << 结果位置 0010 2 << 被减数位置 0011 3 0100 4 0101 5 0110 6 0111 7 1000 -8 1001 -1 1010 -2 1011 -3 1100 -4 1101 -5 1110 -6 1111 -7 从上面的表格可以看出,被减数-减数可以看成被减数+减数的同余数,因为在计算机中,最大值溢出后会从0开始重新计数,所以做减法时,可以看成从被减数一直加,加到溢出后,从0开始再加,直到加到结果值位置。累加的总值就是补码的编码(可以理解为补码没有符合位,只是个偏移量),即通过补码=2^n-减数算出补码在十进制的数值,再将补码转成对应的二进制编码。 当被减数+补码的值为正数时,因为都是从0累加,所以计算的结果和预期结果值一致,不需要转换。 而当被减数+补码的值为负数时,比如:2 - 4 = -2 => 2 + (16 - 4) = -2=> 2 + 12 = -2 二进制编码 十进制 描述 0000 0 0001 1 0010 2 << 被减数位置 0011 3 0100 4 0101 5 0110 6 0111 7 1000 -8 1001 -1 1010 -2 << 结果位置,记作R2 1011 -3 1100 -4 1101 -5 << R2的反码,记作R3 1110 -6 << 被减数+补码 位置,记作R1 1111 -7 10000(仅用作参考,无符号位,无位数限制) 2^4 模 2-3=-1 但实际计算出来的编码转化为十进制为-7 2-4=-2 但实际计算出来的编码转化为十进制为-6 可以看出,将预期值的绝对值作为偏移量,实际计算出的二进制值(1110)加上偏移量(0010),刚好等于模(10000) 因为补码+负数的绝对值等于模,且R1+|R2|=模,所以R1是R2的补码,所以两数相减为负数时实际计算出来的是结果的补码,要得到原码,需要将补码转换为原码,但1000(补)没有原码,直接就表示原值-2^n。 -2可以看成1010=1000+0010(正向偏移),-2的反码可以看成1101=1111-0010(反向偏移) 补码等于反码+1:R3+|R2|+1=模=R1+|R2| => R3+1=R1 类似-2-4这类的,可以看作是(0-2)-4,先算出-2位置的值,再算出-2-4位置的值。 1234567891011122 + (2^4-1) // 在8位系统下,2^4-1 就是 -1 的补码,负数的绝对值+补码=2^n(n为位数) 0010+ 1111 ---------- 00012^4-11111 // -1的补码-11001 // -1的原码1110 // -1的反码","categories":[],"tags":[{"name":"计算器原理","slug":"计算器原理","permalink":"https://hq229075284.github.io/blog/tags/%E8%AE%A1%E7%AE%97%E5%99%A8%E5%8E%9F%E7%90%86/"}]},{"title":"es6-es11","slug":"es6-es11","date":"2020-11-11T13:28:10.000Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"2020/11/es6-es11/","link":"","permalink":"https://hq229075284.github.io/blog/2020/11/es6-es11/","excerpt":"","text":"ECMAScript版本 编年版 简版 描述 ECMAScript2015 ECMAScript6 - ECMAScript2016 ECMAScript7 - ECMAScript2017 ECMAScript8 - ECMAScript2018 ECMAScript9 - ECMAScript2019 ECMAScript10 - ECMAScript2020 ECMAScript11 - ECMAScript2021 ECMAScript12 - ECMAScript2016 Array.prototype.includes **替代Math.pow实现指数的计算 ECMAScript2017 Object.values() Object.entries() String.prototype.padStart String.prototype.padEnd(⚠️ padStart and padEnd on Emojis and other double-byte chars) Object.getOwnPropertyDescriptors(参考) 允许在函数入参的末尾添加,,比如function a(a1,a2,){...} Async/Await ECMAScript2018 js引擎主进程和worker进程间可使用SharedArrayBuffer进行数据共享,并通过Atomics来管理共享内容数据 允许模板字符串和函数组合使用 12345function fn(staticStr,...dynamicParams){ console.log(staticStr)// ['static1 ',' static2 ',''] console.log(dynamicParams)// [1,2]}fn`static1 ${1} static2 ${2}` 正则表达式中.可以在s模式下匹配所有字符(包括\\n、\\r、\\f等) 123456//Before/first.second/.test('first\\nsecond'); //false//ECMAScript 2018/first.second/s.test('first\\nsecond'); //true// Notice: /s 正则表达式中可以对匹配组合进行命名,格式为:(?<name>...) 1234567891011121314151617181920(/(?<cname>abc)/.exec('testabc'))/* 输出: [ 'abc', 'abc', groups:{ cname: 'abc' }, index: 4, input: 'testabc' ]*/// \\k使用之前组合名称匹配到的内容let sameWords=/(?<fruit>apple|orange)===\\k<fruit>/sameWords.test('apple===apple') // truesameWords.test('apple===orange') // false// 在replace函数中使用组合命名let replaceReg=/(?<firstName>[a-zA-z]+) (?<secondName>[a-zA-z]+)/'abc def'.replace(replaceReg,'$<secondName> $<firstName>') // 'def abc' 在解构中用...varName的方式提取对象中未被指定的属性,重组为一个新的对象,记为varName 对象可使用...进行扩展 正则表达式可以使用向前查找,查找时消耗查询字符 1234567891011// ['winning',groups:undefined,index:1,input:'#winning'](/(?<=#).*/.exec('#winning'))// 反向// ['inning',groups:undefined,index:2,input:'#winning'](/(?<!#)[a-z]+/.exec('#winning'))// ['',groups:undefined,index:0,input:'#winning'](/(?<!#)[a-z]*/.exec('#winning'))// PS: (?<!...)会去匹配行首空字符,当匹配到第一个符合匹配规则的,就直接输出// 比如:`/(?<!#)[a-z]*/.exec('#winning')`中,(?<!#)匹配的是行首空字符,[a-z]*匹配的是空字符,因为规则无法匹配首字符`#` 在正则表达式中添加Script或Script_Extensions来扩展Unicode的匹配范围 1234567//The following matches a single Greek character/\\p{Script_Extensions=Greek}/u.test('π'); // true// 等价于/\\p{Script=Greek}/u.test('π'); // true// 反向/\\P{Script=Greek}/u.test('π'); // false Promise.prototype.finally() for-of中使用await,在for循环中,上一个promise resolve后,才会执行下一个promise 1234567const promises=[...] // Promise数组async function a(){ for await (const p of promises){ console.log(p) }}a() ECMAScript2019 String.prototype.trimStart() String.prototype.trimEnd() Object.fromEntries() Array.prototype.flat() Array.prototype.flatMap() try-catch中catch的参数改为可选 Symbol.prototype.description输出Symbol类型变量的描述 Array.prototype.sort(),在原来的基础上使用稳定的排序算法,尤其在对象数组中,会根据给定的键之间比较进行排序,同键值的多个对象的顺序与这些对象在原数组中的顺序一致 Function.prototype.toString(),将返回函数的注释、空格和语法详细信息 JSON成为ECMAScript的完全子集,可解析之前解析不了的JSON字符串:行分隔符(\\u2028)和段分隔符(\\u2029) JSON.stringify()输出进行了优化,原来显示未知的字符,现在在字符代码之前插入\\字符后仍能保持可读性 12345// beforeJSON.stringify('\\uD83D');// '\"�\"'// afterJSON.stringify('\\uD83D');// '\"\\\\ud83d\"' ECMAScript2020 在变量名前加#,来定义类私有变量 123456789101112131415class a{ #p='private' print(){ console.log(this.#p) }}var aa=new a// 正常aa.print() // 'private// 异常// Uncaught SyntaxError: Private field '#p' must be declared in an enclosing classconsole.log(aa.#p) ?.可选链运算符,符号左侧的值为undefined或null时,运算表达式返回undefined,否则继续执行后续的操作 12345// 形式:obj?.propobj?.[expr]arr?.[index]func?.(args) ??空值合并运算符,运算符左侧为undefined或null时,取符号右侧的值,否则取左侧的值 动态引入,import('some.js').then(...) Promise.allSettled,处理多个Promise对象的集合,无论这些Promise对象的状态怎么变化,都会被记录下来,并在所有Promise状态发生变化后,执行then回调,入参为每个Promise状态的集合 globalThis BigInt,通过在数字后面添加n来指定数的类型为BigInt,此类型用于处理大于(2^53 - 1)的整数,不可与普通number类型的数据进行操作,但可比较大小 111n+11 // Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions String.prototype.matchAll,返回的是一个可迭代对象,可以通过for-of访问每一个匹配组的结果(包含使用括号定义的捕获组信息) ECMAScript2021 String.prototype.replaceAll Promise.any 12345678Promise.any(promises).then( (first) => { // 任何一个 Promise 完成了 }, (error) => { // 所有的 Promise 都拒绝了 }) 数字分隔符_,let x=2_3333等价于let x=23333 Intl.ListFormat,国际化格式化","categories":[],"tags":[{"name":"es","slug":"es","permalink":"https://hq229075284.github.io/blog/tags/es/"}]},{"title":"es6相关","slug":"es6相关","date":"2020-11-10T10:14:44.000Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"2020/11/es6相关/","link":"","permalink":"https://hq229075284.github.io/blog/2020/11/es6%E7%9B%B8%E5%85%B3/","excerpt":"","text":"super在babel中的实现 迭代器规范 可迭代协议 可迭代对象特征 迭代器协议 迭代器对象特征 super在babel中的实现1234567891011class a { constructor(){ this.a=1 }}class b extends a { constructor(){ super() }} 编译后 1234567891011121314151617181920212223242526272829303132333435363738// ...some helper functionfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct() return function _createSuperInternal() { // 在调用前,_inherits函数中已将父类函数挂到子类函数的原型链上,即b.__proto__===a // 所以这个的Super就是b.__proto__,即父类a var Super = _getPrototypeOf(Derived), result if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor result = Reflect.construct(Super, arguments, NewTarget) } else { result = Super.apply(this, arguments) } return _possibleConstructorReturn(this, result) }}var a = function a() { _classCallCheck(this, a); this.a = 1;};var b = /*#__PURE__*/function (_a) { // b的prototype的__proto__指向a的prototype,属性调用时:检查b实例的属性=>检查b的prototype的属性=>检查a的prototype的属性 // 其中会调用_setPrototypeOf函数,将a挂到b的原型链上,b.__proto__===a _inherits(b, _a); // 创建super函数,内部存储b为Derived参数 var _super = _createSuper(b); function b() { _classCallCheck(this, b); return _super.call(this); } return b;}(a); 迭代器规范 参考https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols 可迭代协议要成为可迭代对象,一个对象(或原型链上)必须实现@@iterator方法,当一个对象需要被迭代的时候(比如被置入一个 for…of 循环时),首先,会不带参数调用它的@@iterator方法,然后使用此方法返回的迭代器获得要迭代的值。 内置类型拥有默认的迭代器行为: Array.prototype[@@iterator]() TypedArray.prototype[@@iterator]() String.prototype[@@iterator]() Map.prototype[@@iterator]() Set.prototype[@@iterator]() 可迭代对象特征 此对象实现了可迭代协议——拥有@@iterator函数(即typeof fn[Symbol.iterator]==='function'),且@@iterator函数执行后返回一个迭代器对象。 @@iterator函数的this默认指向该可迭代对象 迭代器协议迭代器对象特征 此对象实现了迭代器协议——拥有next函数,且执行next函数后返回{done:Boolean,value:any}这样的对象(可在其上加另外的对象属性) for-of中done为true时,value属性的值会被忽略 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950const obj2={ [Symbol.iterator](){ let i=-1 const txt=[ 'this will output in both case', 'just output in call `next` by hand' ] return { next(){ i++ return {done:i===txt.length-1,value:txt[} } } }}const obj1={ [Symbol.iterator]:function* (){ yield 'this will output in both case' return 'just output in call `next` by hand' }}console.log('obj1 output')var g=obj1[Symbol.iterator]()console.log(g.next().value)console.log(g.next().value)console.log('----')for(let i of obj1){ console.log(i)}console.log('obj2 output')var g=obj2[Symbol.iterator]()console.log(g.next().value)console.log(g.next().value)console.log('----')for(let i of obj2){ console.log(i)}// => 输出结果:// obj1 output// this will output in both case// just output in call `next` by hand// ----// this will output in both case// obj2 output// this will output in both case// just output in call `next` by hand// ----// this will output in both case","categories":[],"tags":[{"name":"es6","slug":"es6","permalink":"https://hq229075284.github.io/blog/tags/es6/"}]},{"title":"元素resize","slug":"元素resize","date":"2020-06-27T23:38:44.000Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"2020/06/元素resize/","link":"","permalink":"https://hq229075284.github.io/blog/2020/06/%E5%85%83%E7%B4%A0resize/","excerpt":"监听元素尺寸的变化传统的,通过resize来监听尺寸大的变化(以高度变化为例)由于resize事件仅在某些元素和window上触发,对于其他元素需要借助这些元素来监听尺寸的变化。 首先保持iframe的高度与变化元素的高度一致,当元素的高度变化时,iframe内的窗口大小也变化,会触发iframe内window的resize事件,以此来监听元素尺寸的变化。 123456789101112131415161718<!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"UTF-8\"> <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"> <title>Document</title></head><body> <button>change div's height</button> <div class=\"size-div\" style=\"height:100px;\"> <iframe></iframe> the div can change size </div></body></html> 12345678910document.querySelector(\"button\").addEventListener(\"click\", function () { var target = document.querySelector(\".size-div\"); var rand = Math.floor(Math.random() * 200 + 50); target.setAttribute(\"style\", \"height:\" + rand + \"px;border:1px solid;\");});document .querySelector(\"iframe\") .contentWindow.addEventListener(\"resize\", function () { console.log(\"div changed size\"); }); 123456789101112.size-div { border: 1px solid; position: relative;}iframe { position: absolute; left: 0; top: 0; height: 100%; width: 0; border:0;}","text":"监听元素尺寸的变化传统的,通过resize来监听尺寸大的变化(以高度变化为例)由于resize事件仅在某些元素和window上触发,对于其他元素需要借助这些元素来监听尺寸的变化。 首先保持iframe的高度与变化元素的高度一致,当元素的高度变化时,iframe内的窗口大小也变化,会触发iframe内window的resize事件,以此来监听元素尺寸的变化。 123456789101112131415161718<!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"UTF-8\"> <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"> <title>Document</title></head><body> <button>change div's height</button> <div class=\"size-div\" style=\"height:100px;\"> <iframe></iframe> the div can change size </div></body></html> 12345678910document.querySelector(\"button\").addEventListener(\"click\", function () { var target = document.querySelector(\".size-div\"); var rand = Math.floor(Math.random() * 200 + 50); target.setAttribute(\"style\", \"height:\" + rand + \"px;border:1px solid;\");});document .querySelector(\"iframe\") .contentWindow.addEventListener(\"resize\", function () { console.log(\"div changed size\"); }); 123456789101112.size-div { border: 1px solid; position: relative;}iframe { position: absolute; left: 0; top: 0; height: 100%; width: 0; border:0;} 通过ResizeObserver Api来监听元素大小的变化通过借助其他元素来实现监听元素尺寸变化比较繁琐,web api中提供了ResizeObserver来监听任何元素的尺寸变化 resizeObserver.observe执行后会异步(macrotask)触发1次ResizeObserver的回调函数 1234567891011121314151617<!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"UTF-8\"> <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"> <title>Document</title></head><body> <button>change div's height</button> <div class=\"size-div\" style=\"height:100px;\"> the div can change size </div></body></html> 1234567891011document.querySelector(\"button\").addEventListener(\"click\", function () { var target = document.querySelector(\".size-div\"); var rand = Math.floor(Math.random() * 200 + 50); target.setAttribute(\"style\", \"height:\" + rand + \"px;border:1px solid;\");});var resizeObserver = new ResizeObserver((entries) => { for (let entry of entries) { console.log(entry); }});resizeObserver.observe(document.querySelector(\".size-div\")); 123456789101112.size-div { border: 1px solid; position: relative;}iframe { position: absolute; left: 0; top: 0; height: 100%; width: 0; border:0;}","categories":[{"name":"web-api","slug":"web-api","permalink":"https://hq229075284.github.io/blog/categories/web-api/"}],"tags":[{"name":"web-api","slug":"web-api","permalink":"https://hq229075284.github.io/blog/tags/web-api/"}]},{"title":"移动端兼容性问题记录","slug":"移动端兼容性问题记录","date":"2020-05-31T16:41:04.000Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"2020/05/移动端兼容性问题记录/","link":"","permalink":"https://hq229075284.github.io/blog/2020/05/%E7%A7%BB%E5%8A%A8%E7%AB%AF%E5%85%BC%E5%AE%B9%E6%80%A7%E9%97%AE%E9%A2%98%E8%AE%B0%E5%BD%95/","excerpt":"","text":"仅在android版钉钉中嵌入的网页里,全屏api的调用会失效,所以点击视频无法默认将video元素全屏来进行播放 仅在ios版钉钉中嵌入的网页里,上传文件时,xhr.upload.onprogress不会触发 video元素在ios上默认是调用原生的播放器全屏播放,而在android上则默认是在video元素中内联播放 在ios上为了让video能内联播放,需要在video元素上设置playsinline(ios10及之后系统版本的safari中有效)和webkit-playsinline(ios10之前的safari中有效)属性 隐藏系统默认的视频播放按钮 123456789101112*::-webkit-media-controls-start-playback-button { display: none; -webkit-appearance: none;}*::-webkit-media-controls-panel { display: none; -webkit-appearance: none;}*::--webkit-media-controls-play-button { display: none; -webkit-appearance: none;} 钉钉中要改变网页标题,需要调用钉钉的api,引用dingtalk-jsapi/api/biz/navigation/setTitle 在ios下的网页中,input输入框内的内容溢出输入框的宽度时,无法通过移动光标来查看超出部分的内容,不管这个输入框是否只读。因为在移动光标时,输入框中显示的内容不会随着改变","categories":[{"name":"兼容性","slug":"兼容性","permalink":"https://hq229075284.github.io/blog/categories/%E5%85%BC%E5%AE%B9%E6%80%A7/"}],"tags":[{"name":"兼容性","slug":"兼容性","permalink":"https://hq229075284.github.io/blog/tags/%E5%85%BC%E5%AE%B9%E6%80%A7/"}]},{"title":"基于docker搭建gitlab-runner","slug":"基于docker搭建gitlab-runner","date":"2020-05-31T14:13:25.000Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"2020/05/基于docker搭建gitlab-runner/","link":"","permalink":"https://hq229075284.github.io/blog/2020/05/%E5%9F%BA%E4%BA%8Edocker%E6%90%AD%E5%BB%BAgitlab-runner/","excerpt":"基于docker搭建gitlab-runner 下载gitlab镜像 1docker pull gitlab/gitlab-runner 注册runner 1234567891011121314151617181920212223242526272829# ps:在注册过程中直接以人机交互的方式一步一步进行注册的话,可能会出现ssl验证不通过的问题,所以在这种情况下直接用命令行参数进行注册# --rm 临时创建一个容器,用来运行gitlab/gitlab-runner镜像sudo docker run --rm \\# 挂载宿主的文件目录到容器内,用以存储注册之后生成的config.toml文件# 当以root用户运行gitlab-runner时,/etc/gitlab-runner为默认存储配置文件的地方-v /home/dcxx/gitlab-runner:/etc/gitlab-runner \\# 指定镜像gitlab/gitlab-runner \\# 执行gitlab-runner register命令register \\# 不需要交互式--non-interactive \\# gitlab web服务的https的证书--tls-ca-file '/etc/gitlab-runner/git.dcyun.com.crt' \\# gitlab web服务地址--url \"https://git.dcyun.com/\" \\# gitlab网站上添加runner时提供的token--registration-token \"T3V4vNvQrAMZDyNVFrg7\" \\# 指定在什么环节跑构建脚本--executor \"docker\" \\# 指定跑构建脚本时的docker环境,即镜像--docker-image ubuntu:latest \\# runner的名称,主要显示在job详情中pipeline选择runner时的下拉项中--name '前端自动部署' \\# 运行没有配置tag的任务执行--run-untagged=\"true\" \\# runner的tag--tag-list 'docker,font-end,common'","text":"基于docker搭建gitlab-runner 下载gitlab镜像 1docker pull gitlab/gitlab-runner 注册runner 1234567891011121314151617181920212223242526272829# ps:在注册过程中直接以人机交互的方式一步一步进行注册的话,可能会出现ssl验证不通过的问题,所以在这种情况下直接用命令行参数进行注册# --rm 临时创建一个容器,用来运行gitlab/gitlab-runner镜像sudo docker run --rm \\# 挂载宿主的文件目录到容器内,用以存储注册之后生成的config.toml文件# 当以root用户运行gitlab-runner时,/etc/gitlab-runner为默认存储配置文件的地方-v /home/dcxx/gitlab-runner:/etc/gitlab-runner \\# 指定镜像gitlab/gitlab-runner \\# 执行gitlab-runner register命令register \\# 不需要交互式--non-interactive \\# gitlab web服务的https的证书--tls-ca-file '/etc/gitlab-runner/git.dcyun.com.crt' \\# gitlab web服务地址--url \"https://git.dcyun.com/\" \\# gitlab网站上添加runner时提供的token--registration-token \"T3V4vNvQrAMZDyNVFrg7\" \\# 指定在什么环节跑构建脚本--executor \"docker\" \\# 指定跑构建脚本时的docker环境,即镜像--docker-image ubuntu:latest \\# runner的名称,主要显示在job详情中pipeline选择runner时的下拉项中--name '前端自动部署' \\# 运行没有配置tag的任务执行--run-untagged=\"true\" \\# runner的tag--tag-list 'docker,font-end,common' gitlab-runner注册完之后会产生config.toml文件 # 用gitlab/gitlab-runner镜像,跑起一个gitlab-runner的容器 # 启动之后,之前注册的runner就会处于在线且启用状态 docker run -it --name gitlab-runner --restart always \\ -v /home/dcxx/gitlab-runner:/etc/gitlab-runner \\ -v /var/run/docker.sock:/var/run/docker.sock \\ gitlab/gitlab-runner 1234567893. gitlab-runner拉取代码时,会存在ssl验证不通过的问题,需要在config.toml中添加配置: ```toml # """用于包裹多行 pre_clone_script = """ # 关闭git的ssl验证 git config --global http.sslverify false """ 从宿主机上挂载node环境和软件源配置文件到容器中,方便容器中的脚步执行和软件源配置 12[runners.docker] volumes = [\"/cache\",\"/home/dcxx/gitlab-runner/node:/node\",\"/etc/apt/sources.http.list:/sources.list\"] 在项目中创建.gitlab-ci.yml before_script: # 添加node到环境中 - PATH=/node/bin:$PATH # 由于Ubuntu官方的软件源部分被墙 # 需要更改Ubuntu的软件源 - cat /sources.list - cp -f /sources.list /etc/apt/sources.list # 安装ssh相关的依赖,用于scp发布 - 'which ssh-agent || ( apt update -y && apt install openssh-client -y )' # 创建.ssh文件夹用于存储id_rsa私钥 - mkdir -p ~/.ssh # 读取gitlab设置下Variables中配置的SSH_PRIVATE_KEY变量,将其写入id_rsa中 - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa # 设置仅当前用户可用(ssh硬性要求) - chmod -R 700 ~/.ssh - eval $(ssh-agent -s) # 用于解决 - ssh-agent bash # 添加密钥 - ssh-add ~/.ssh/id_rsa # 当ssh第一次连接远程服务器的时候,会先询问是否连接这台主机,因为是在ci中跑,无法进行人机交互,所以设置成不检查host key,否则会报`host key verification failed`的错误 - echo -e "Host *\\\\n\\\\tStrictHostKeyChecking no\\\\n" > ~/.ssh/config dev: script: # 跑构建代码的脚本 - npm run serve after_script: # 将构建后的代码发布到项目服务器 - scp -r ./dist dcxx@192.168.2.130:~/web/dist 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849> OpenSSH将访问过计算机的公钥(public key)都记录在~/.ssh/known_hosts中。当下次访问相同计算机时,OpenSSH会核对公钥。如果公钥不同,OpenSSH会发出警告,避免你受到DNS Hijack之类的攻击。>> SSH对主机的public_key的检查等级是根据StrictHostKeyChecking变量来配置的。默认情况下,StrictHostKeyChecking=ask。简单所下它的三种配置值:>> 1. StrictHostKeyChecking=no >> 最不安全的级别,当然也没有那么多烦人的提示了,相对安全的内网时建议使用。如果连接server的key在本地不存在,那么就自动添加到文件中(默认是known_hosts),并且给出一个警告。>> 2. StrictHostKeyChecking=ask >> 默认的级别,就是出现刚才的提示了。如果连接和key不匹配,给出提示,并拒绝登录。>> 3. StrictHostKeyChecking=yes >> 最安全的级别,如果连接与key不匹配,就拒绝连接,不会提示详细信息。8. 之后提交代码就可以看到runner的效果了 > 在gitlab-runner执行过程中,config.toml的pre_clone_script和.gitlab-ci.yml的before_script、script、after_script是在相互隔离的环境下运行的(大概猜测是在不同的docker容器下)```toml# config.toml大致的结构concurrent = 2check_interval = 0[session_server] session_timeout = 1800[[runners]] name = "test" url = "https://git.dcyun.com/" token = "q7pn15zhaZyJQzuHxyCC" tls-ca-file = "/etc/gitlab-runner/certs/git.dcyun.com.crt" pre_clone_script = """ git config --global http.sslverify false """ executor = "docker" [runners.custom_build_dir] [runners.cache] [runners.cache.s3] [runners.cache.gcs] [runners.docker] tls_verify = false image = "ubuntu" privileged = false disable_entrypoint_overwrite = false oom_kill_disable = false disable_cache = false volumes = ["/cache","/home/dcxx/gitlab-runner/node:/node","/etc/apt/sources.http.list:/sources.list"] shm_size = 0","categories":[{"name":"运维","slug":"运维","permalink":"https://hq229075284.github.io/blog/categories/%E8%BF%90%E7%BB%B4/"}],"tags":[{"name":"docker","slug":"docker","permalink":"https://hq229075284.github.io/blog/tags/docker/"}]},{"title":"docker环境配置","slug":"docker环境配置","date":"2020-05-31T13:52:28.000Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"2020/05/docker环境配置/","link":"","permalink":"https://hq229075284.github.io/blog/2020/05/docker%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/","excerpt":"","text":"宿主机配置 docker-ce 国内镜像下载 设置Ubuntu软件源为清华镜像 设置docker hub国内镜像 1234567// /etc/docker/daemon.json{ \"registry-mirrors\" : [ // 网易 \"https://hub-mirror.c.163.com\", ]} 或者使用阿里的镜像","categories":[{"name":"运维","slug":"运维","permalink":"https://hq229075284.github.io/blog/categories/%E8%BF%90%E7%BB%B4/"}],"tags":[{"name":"docker","slug":"docker","permalink":"https://hq229075284.github.io/blog/tags/docker/"}]},{"title":"CSRF","slug":"CSRF","date":"2020-05-31T13:50:03.000Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"2020/05/CSRF/","link":"","permalink":"https://hq229075284.github.io/blog/2020/05/CSRF/","excerpt":"","text":"CSRF防范措施: 检测请求头中的Referer字段,判断请求的来源是否是来自合法(意料之中)的网站 在token无法被他人窃取的情况下,客户端和服务端对同一用户在两端都保留一个相同的token,通过检测token是否一致来确定是否为同一用户 token不可以放在cookie中,防止在访问服务端时浏览器自动给请求带上cookie(浏览器cookie设置的行为) JWT(含有少量信息的token,在一定程度上后端代码可以减少与数据库的交互) 将token放在页面的url上有时是不安全的,因为跳转后的页面也可能会获取到上一个页面的url内容","categories":[{"name":"web安全","slug":"web安全","permalink":"https://hq229075284.github.io/blog/categories/web%E5%AE%89%E5%85%A8/"}],"tags":[]},{"title":"typescript学习记录","slug":"typescript学习记录","date":"2020-01-20T16:27:58.000Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"2020/01/typescript学习记录/","link":"","permalink":"https://hq229075284.github.io/blog/2020/01/typescript%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/","excerpt":"","text":"typescript配置相关的在vscode中,通过配置typescript,让vscode可以识别css-module,进而提供css-module相关的代码补全在tsconfig.json中配置typescript-plugin-css-modules插件 vscode中workaround的setting配置为"typescript.tsdk": "node_modules\\\\typescript\\\\lib"来使用工作区下的node_modules中的typescript来加载插件。reference eslint中行末结束符无法自动修复Expected linebreaks to be 'LF' but found 'CRLF'.eslint(linebreak-style)在eslint可识别但由于bug暂不可自动修复 暂时使用EditorConfig这个vscode插件来解决行末IF和CRLF的问题 typescript规定相关的ts中对对象字面量做参数过多检测的情况,以如何绕过ts中对对象字面量做检测的时候,在特定情况下(字面量直接赋值给其它变量、字面量直接作为函数入参)会额外做参数过多(excess property checking)的检测 However, TypeScript takes the stance that there’s probably a bug in this code. Object literals get special treatment and undergo excess property checking when assigning them to other variables, or passing them as arguments. If an object literal has any properties that the “target type” doesn’t have, you’ll get an error: 绕过excess property checking的方法 1231. use a type assertion(类型断言)2. add a string index signature(定义string型的索引签名)3. One final way to get around these checks, which might be a bitsurprising, is to assign the object to another variable ts文件中类型的申明作用域如何决定ts根据该文件是否存在import或者export来决定当前文件是个模块还是运行在全局环境下,以此来推断类型申明是否存在于全局中 参考: https://stackoverflow.com/questions/40900791/cannot-redeclare-block-scoped-variable-in-unrelated-files github-issus","categories":[{"name":"typescript","slug":"typescript","permalink":"https://hq229075284.github.io/blog/categories/typescript/"}],"tags":[]},{"title":"react-hooks","slug":"react-hooks","date":"2019-06-06T16:53:34.000Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"2019/06/react-hooks/","link":"","permalink":"https://hq229075284.github.io/blog/2019/06/react-hooks/","excerpt":"","text":"React Hookshooks的调用顺序 Basic Hooks useState useEffect useContext Additional Hooks useReducer useCallback useMemo useRef useImperativeHandle useLayoutEffect useDebugValue","categories":[],"tags":[]},{"title":"webApi","slug":"webApi","date":"2019-06-06T15:46:24.000Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"2019/06/webApi/","link":"","permalink":"https://hq229075284.github.io/blog/2019/06/webApi/","excerpt":"","text":"IntersectionObserver 定义的视口和目标元素之间相交的关系 1var observer = new IntersectionObserver(callback[, options]) 当目标元素在定义的视口中的可见百分比超过阈值时,callback会被调用,入参有两个: entries:由IntersectionObserverEntry对象构成的数组,仅包含触发阈值的目标元素的IntersectionObserverEntry对象,每个IntersectionObserverEntry对象对应一个目标元素,顺序和目标元素被监听的先后一致 observer:当前的IntersectionObserver实例 options包含3个属性: root:定义视口元素,当options未指定或者root为null时,默认未document rootMargin:值的语法与css的margin一样,正值为在root视口的边缘向外扩展,即视口变大,反之则变小 threshold:值为数组,数组中的每个元素的取值范围为[0,1],当目标元素在视口中的可见百分比越过数组中某个元素的值时,则callback会被调用。比如: 原来目标元素可见百分比 滚动之后目标元素可见百分比 数组中的某一阈值 结果 0.45 0.51 0.5 callback被触发 0.51 0.45 0.5 callback被触发 observer实例对象上含有4个函数: disconnect:停止此observer对象中监听的所有目标元素 observer:用于指定observer需要监听的目标元素,入参为1个目标元素,且为root的子孙元素 takeRecords:获取observer内部保存的由IntersectionObserverEntry对象构成的数组 unobserver:用于停止监听某一目标元素,入参为1个对应需要停止监听的目标元素, 附:IntersectionObserverEntry","categories":[],"tags":[]},{"title":"cookie总结","slug":"cookie总结","date":"2019-02-15T10:52:59.000Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"2019/02/cookie总结/","link":"","permalink":"https://hq229075284.github.io/blog/2019/02/cookie%E6%80%BB%E7%BB%93/","excerpt":"","text":"Cookie知识点记录 参考:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Set-Cookiehttps://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies Expires:cookie 的最长有效时间,形式为符合 HTTP-date 规范的时间戳,比如:Date: Wed, 21 Oct 2015 07:28:00 GMT,如果没有设置这个属性,那么表示这是一个会话期 cookie 。一个会话结束于客户端被关闭时,这意味着会话期 cookie 在彼时会被移除。(https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#Session_cookies) Max-Age:在 cookie 失效之前需要经过的秒数。一位或多位非零(1-9)数字。假如二者 (指 Expires 和Max-Age) 均存在,那么 Max-Age 优先级更高 Domain:指定 cookie 可以送达的目标。假如没有指定,那么默认值为当前文档访问地址中的主机部分(但是不包含子域名)。后端在设置set-Cookie时,不指定Domain,则默认是当前接口地址的主域名+port或者ip+port Path:指定一个 URL 路径,这个路径必须出现在要请求的资源的路径中才可以发送此Cookie。 Secure:一个带有安全属性的 cookie 只有在请求使用SSL和HTTPS协议的时候才会被发送到服务器 HttpOnly:设置了 HttpOnly 属性的 cookie 不能使用 JavaScript 经由 Document.cookie 属性、XMLHttpRequest 和 Request APIs 进行访问,以防范跨站脚本攻击(XSS)。 SameSite(实验属性):允许服务器设定一则 cookie 不随着跨域请求一起发送,这样可以在一定程度上防范跨站请求伪造攻击(CSRF)","categories":[],"tags":[]},{"title":"mvc与mvp与mvvm","slug":"mvc与mvp与mvvm","date":"2019-02-12T13:58:44.000Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"2019/02/mvc与mvp与mvvm/","link":"","permalink":"https://hq229075284.github.io/blog/2019/02/mvc%E4%B8%8Emvp%E4%B8%8Emvvm/","excerpt":"MVC123456789101112131415161718192021222324252627282930313233343536373839404142434445464748// viewfunction View(controller) { var target = $('#some-element')[0] this.render = function (model) { target.innerText = model.getValue() } target.addEventListener('click', controller.click)}// modelfunction Model() { var _value = 0 var registerViews = [] this.register = function (viewInstance) { registerViews.push(viewInstance) } this.notify = function () { var self = this registerViews.forEach(function (viewInstance) { viewInstance.render(self) }) } this.add = function () { _value += 1 } this.getValue = function () { return _value }}// controllerfunction Controller() { var model, view this.init = function () { model = new Model() view = new View(this) model.register(view) model.notify() setTimeout(function () { view.render({ getValue: function () { return 1000 } }) }, 1000) } this.click = function () { model.add() model.notify() }}// 调用var controller = new Controller()controller.init() 总结 mvc模式中model层可以直接通知view层重新渲染视图 仅由controller层发起model层的修改 代码的执行入口是controller controller内部维护着model层和view层之间的关系——同步视图和数据 controller可以通过model层使view层重新渲染,也可以直接使view层重新渲染","text":"MVC123456789101112131415161718192021222324252627282930313233343536373839404142434445464748// viewfunction View(controller) { var target = $('#some-element')[0] this.render = function (model) { target.innerText = model.getValue() } target.addEventListener('click', controller.click)}// modelfunction Model() { var _value = 0 var registerViews = [] this.register = function (viewInstance) { registerViews.push(viewInstance) } this.notify = function () { var self = this registerViews.forEach(function (viewInstance) { viewInstance.render(self) }) } this.add = function () { _value += 1 } this.getValue = function () { return _value }}// controllerfunction Controller() { var model, view this.init = function () { model = new Model() view = new View(this) model.register(view) model.notify() setTimeout(function () { view.render({ getValue: function () { return 1000 } }) }, 1000) } this.click = function () { model.add() model.notify() }}// 调用var controller = new Controller()controller.init() 总结 mvc模式中model层可以直接通知view层重新渲染视图 仅由controller层发起model层的修改 代码的执行入口是controller controller内部维护着model层和view层之间的关系——同步视图和数据 controller可以通过model层使view层重新渲染,也可以直接使view层重新渲染 MVP1234567891011121314151617181920212223242526272829303132333435363738394041// viewfunction View() { var target = $(\"#some-element\")[0]; this.init = function() { const presenter = new Presenter(this); target.addEventListener(\"click\", presenter.click); }; this.render = function(model) { target.innerText = model.getValue(); };}// modelfunction Model() { var _value = 0; this.add = function() { _value += 1; }; this.getValue = function() { return _value; };}// presenterfunction Presenter(view) { var model = new Model(); view.render(model); this.click = function() { model.add(); view.render(model); }; setTimeout(function() { view.render({ getValue: function() { return 1000; } }); }, 1000);}// 调用var view = new View();view.init(); 总结 MVP模式中model层很纯粹,仅修改和存储数据,在内部不与view层有牵连 仅由presenter层发起view层的重新渲染 仅由presenter层发起model层的修改 代码的执行入口是view presenter内部维护着model层和view层之间的关系——同步视图和数据 MVVM123456789101112131415161718192021222324252627282930313233343536373839404142434445464748// viewfunction View(vm) { var target = $('#some-element')[0] this.render = function (model) { target.innerText = model.getValue() } target.addEventListener('input', vm.input)}// modelfunction Model(vm) { var _value = 0 var data = {} Object.defineProperty(data, 'value', { get: function () { return _value }, set: function (nextValue) { vm.notify(nextValue) _value = nextValue } }) this.change = function (nextValue) { data.value = nextValue } this.getValue = function () { return _value }}// viewmodelfunction ViewModel() { var model, view, needRespond = true this.init = function () { view = new View(this) model = new Model(this) view.render(model) } this.input = function (e) { model.change(e.target.value) } this.notify = function (nextValue) { if (needRespond) { console.log('new value:', nextValue) view.render(model) } }}// 调用var vm = new ViewModel()vm.init() 总结 MVVM模式中model层和view层通过viewmodel层关联,通过viewmodel层同步视图和数据 仅由viewmodel层发起view层的重新渲染 view层的修改可以被viewmodel层感知到 model层的修改可以被viewmodel层感知到 代码的执行入口是viewmodel","categories":[],"tags":[]},{"title":"基本排序法概览","slug":"基本排序法概览","date":"2018-11-08T14:12:51.000Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"2018/11/基本排序法概览/","link":"","permalink":"https://hq229075284.github.io/blog/2018/11/%E5%9F%BA%E6%9C%AC%E6%8E%92%E5%BA%8F%E6%B3%95%E6%A6%82%E8%A7%88/","excerpt":"","text":"冒泡排序 选择排序 插入排序 本文所有测试及排序算法 公用测试部分 1234567function createTestData() { var testData = [] for (var i = 0; i < 10; i++) { testData.push(Math.floor(Math.random() * 100)) } return testData} 冒泡排序可以先找大的,也可以先找小的 含有两层循环,外层循环决定冒泡排序的结束位置索引,内层循环决定当前这一项冒泡排序的开始位置索引 每次内层循环,仅比较当前项和下一项的大小,如果当前项较大,则将两项位置互换。 保证每次内层循环结束后,内层循环的数列的末尾值是该数列的最大值 12345678910111213function sort(data = []) { data = Object.assign([], data) for (sp = data.length - 2; sp >= 1; sp--) { for (mp = 0; mp <= sp; mp++) { if (data[mp] > data[mp + 1]) { var temp = data[mp] data[mp] = data[mp + 1] data[mp + 1] = temp } } } return data} 选择排序可以先找大的,也可以先找小的 含有两层循环,外层循环决定\u001d选择排序当前比较数列的开始索引,同时假定当前开始索引中存储的值最小,内层循环决定选择排序仍然需要比较的数列范围 当需要比较的数列范围内有比当前假定的最小值小的值时,将两个值的位置互换 这种算法保证每次内层循环后都找到当前内层循环队列中的最小值,并且处于当前数列的头部 这是一种循环找极值的排序算法 12345678910111213function sort(data=[]) { data = Object.assign([], data) for (var sp = 0; sp < data.length - 1; sp++) { for (var mp = sp + 1; mp < data.length; mp++) { if (data[mp] < data[sp]) { var temp = data[sp] data[sp] = data[mp] data[mp] = temp } } } return data} 插入排序可以先找大的,也可以先找小的 从第二项开始遍历,从当前项往前检查之前的项是否比当前项大,如果大则两项位置互换,然后重复此过程,直到检查完所有之前的项或者碰到小于当前项的项,即在之前的项中找到适合当前项的位置,并将项插入到这个合适的位置上 由于每一次项插入后的数列都是有序的,保证了当前项之前的项一定是有序的 12345678910111213function sort(data = []) { data = Object.assign([], data) for (let i = 1; i < data.length; i++) { const temp = data[i] let j = i while (j > 0 && data[j - 1] > temp) { data[j] = data[j - 1] j -= 1 } data[j] = temp } return data}","categories":[],"tags":[]},{"title":"位置属性总结","slug":"位置属性总结","date":"2018-11-06T15:11:18.000Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"2018/11/位置属性总结/","link":"","permalink":"https://hq229075284.github.io/blog/2018/11/%E4%BD%8D%E7%BD%AE%E5%B1%9E%E6%80%A7%E6%80%BB%E7%BB%93/","excerpt":"位置属性总结 window element mouseEvent 位置属性总结 window 无offsetParent属性 innerHeight浏览器窗口的视口(viewport)高度(以像素为单位),如果存在水平滚动条,则包括它 outerHeightWindow.outerHeight 获取整个浏览器窗口的高度(单位:像素),包括侧边栏(如果存在)、窗口镶边(window chrome)和窗口调正边框(window resizing borders/handles) innerWidth浏览器视口(viewport)宽度(单位:像素),如果存在垂直滚动条则包括它。 outerWidthWindow.outerWidth 获取浏览器窗口外部的宽度。表示整个浏览器窗口的宽度,包括侧边栏(如果存在)、窗口镶边(window chrome)和调正窗口大小的边框(window resizing borders/handles) screen availHeight屏幕中非固定占用空间^固定占用空间的高度 availLeft屏幕中非固定占用空间的左边\b\b\b离显示器左边的距离 availTop屏幕中非固定占用空间的顶部离显示器\b顶部的距离 availWidth屏幕中非固定占用空间的宽度 height\b显示器的纵向分辨率 width\b显示器的横向分辨率 screenLeft同screen.availLeft screenTop同screen.availTop screenX返回浏览器左边界到操作系统桌面左边界的水平距离 screenY返回浏览器顶部距离系统桌面顶部的垂直距离 scrollX返回文档/页面水平方向滚动的像素值 scrollY返回文档在垂直方向已滚动的像素值","text":"位置属性总结 window element mouseEvent 位置属性总结 window 无offsetParent属性 innerHeight浏览器窗口的视口(viewport)高度(以像素为单位),如果存在水平滚动条,则包括它 outerHeightWindow.outerHeight 获取整个浏览器窗口的高度(单位:像素),包括侧边栏(如果存在)、窗口镶边(window chrome)和窗口调正边框(window resizing borders/handles) innerWidth浏览器视口(viewport)宽度(单位:像素),如果存在垂直滚动条则包括它。 outerWidthWindow.outerWidth 获取浏览器窗口外部的宽度。表示整个浏览器窗口的宽度,包括侧边栏(如果存在)、窗口镶边(window chrome)和调正窗口大小的边框(window resizing borders/handles) screen availHeight屏幕中非固定占用空间^固定占用空间的高度 availLeft屏幕中非固定占用空间的左边\b\b\b离显示器左边的距离 availTop屏幕中非固定占用空间的顶部离显示器\b顶部的距离 availWidth屏幕中非固定占用空间的宽度 height\b显示器的纵向分辨率 width\b显示器的横向分辨率 screenLeft同screen.availLeft screenTop同screen.availTop screenX返回浏览器左边界到操作系统桌面左边界的水平距离 screenY返回浏览器顶部距离系统桌面顶部的垂直距离 scrollX返回文档/页面水平方向滚动的像素值 scrollY返回文档在垂直方向已滚动的像素值 element offsetParent:返回一个指向最近的(closest,指包含层级上的最近)包含该元素的定位元素。如果没有定位的元素,则 offsetParent 为最近的 table, table cell 或根元素(标准模式下为 html;quirks 模式下为 body)。当元素的 style.display 设置为 “none” 时,offsetParent 返回 null clientHeight内部无其它有高度的元素且本身没有设置与高度有关的CSS的外部元素或者display:inline的元素值为0,其他情况下,它是元素内部的高度(单位像素),包含内边距,但不包括水平滚动条、边框和外边距 clientWidth元素的内部宽度,以像素计。该属性包括内边距,但不包括垂直滚动条(如果有)、边框和外边距 clientLeft表示一个元素的左边框的宽度,以像素表示。如果元素的文本方向是从右向左(RTL, right-to-left),并且由于内容溢出导致左边出现了一个垂直滚动条,则该属性包括滚动条的宽度。clientLeft 不包括左外边距和左内边距 clientTop一个元素顶部边框的宽度(以像素表示)。不包括顶部外边距或内边距 offsetLeft当前元素左上角相对于 HTMLElement.offsetParent 节点的左边界偏移的像素值 offsetTop返回当前元素相对于其 offsetParent 元素的顶部的距离 offsetHeight返回该元素的像素高度,高度包含该元素的垂直内边距和边框,且是一个整数 offsetWidth返回一个元素的布局宽度,offsetWidth是测量包含元素的边框(border)、水平线上的内边距(padding)、竖直方向滚动条(scrollbar)(如果存在的话)、以及CSS设置的宽度(width)的值 scrollHeight返回元素内容高度的度量,包括由于溢出导致的视图中不可见内容。scrollHeight 的值等于该元素在不使用滚动条的情况下为了适应视口中所用内容所需的最小高度 scrollLeft读取或设置元素滚动条到元素左边的距离 scrollTop获取或设置一个元素的内容垂直滚动的像素数 scrollWidth返回元素的内容区域宽度或元素的本身的宽度中更大的那个值–若元素的宽度大于其内容的区域(例如,元素存在滚动条时) mouseEvent clientX事件发生时的应用客户端区域的水平坐标 clientY提供事件发生时的应用客户端区域的垂直坐标 layerX(非标准) layerY(非标准) movementX当前事件和上一个mousemove事件之间鼠标在水平方向上的移动值 movementY当前事件和上一个 mousemove 事件之间鼠标在水平方向上的移动值 offsetX(实验中的功能)事件对象与目标节点的内填充边(padding edge)在 X 轴方向上的偏移量 offsetY(实验中的功能)事件对象与目标节点的内填充边(padding edge)在 Y 轴方向上的偏移量 pageX返回事件对象相对于整个文档左侧的距离(考虑页面的水平方向上的滚动) pageY返回事件对象相对于相对于整个文档上侧距离(考虑页面的垂直方向上的滚动) screenX事件对象相对于屏幕坐标系的水平偏移量 screenY事件对象相对于屏幕坐标系的垂直偏移量 x(实验中的功能)MouseEvent.clientX 属性的别名 y(实验中的功能)MouseEvent.clientY 属性的别名","categories":[],"tags":[]},{"title":"页面缩放、逻辑像素与物理像素转化","slug":"页面缩放、逻辑像素与物理像素转化","date":"2018-10-01T13:56:00.000Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"2018/10/页面缩放、逻辑像素与物理像素转化/","link":"","permalink":"https://hq229075284.github.io/blog/2018/10/%E9%A1%B5%E9%9D%A2%E7%BC%A9%E6%94%BE%E3%80%81%E9%80%BB%E8%BE%91%E5%83%8F%E7%B4%A0%E4%B8%8E%E7%89%A9%E7%90%86%E5%83%8F%E7%B4%A0%E8%BD%AC%E5%8C%96/","excerpt":"","text":"页面缩放<meta name=”viewport” content=”width=device-width, initial-scale=scale“/> 物理屏:真正展现图像的屏幕,此屏上的像素称为物理像素逻辑屏:css层面上布局的屏幕,此屏上的像素称为逻辑像素 以上\b的scale指的就是页面的缩放倍数,符合公式: 元素在物理屏上布局的宽度 = 元素在逻辑屏上布局的宽度 * scale * devicePixelRatio 逻辑像素 * devicePixelRatio = 物理像素(即实际用户看到的画面) 所以scale的计算公式:scale = 元素在物理屏上布局的宽度 / (元素在逻辑屏上布局的宽度 * devicePixelRatio) 一般来说devicePixelRatio在pc上为1,手机端上常见的是2和3","categories":[],"tags":[]},{"title":"浮点数的表示与计算","slug":"浮点数的表示与计算","date":"2018-08-31T13:12:06.000Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"2018/08/浮点数的表示与计算/","link":"","permalink":"https://hq229075284.github.io/blog/2018/08/%E6%B5%AE%E7%82%B9%E6%95%B0%E7%9A%84%E8%A1%A8%E7%A4%BA%E4%B8%8E%E8%AE%A1%E7%AE%97/","excerpt":"","text":"[TOC] 浮点数的表示与计算IEEE754中规定了64位浮点数的格式: 符号位(1bit),取值范围:[0,1] 指数位(11bit),取值范围:[1,2046] 尾数位(52bit),取值范围:[0,2^53-1]","categories":[],"tags":[]},{"title":"git-flow-introduction","slug":"git-flow-introduction","date":"2018-05-23T01:44:24.000Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"2018/05/git-flow-introduction/","link":"","permalink":"https://hq229075284.github.io/blog/2018/05/git-flow-introduction/","excerpt":"","text":"Git 开发流程 开始一个新功能的开发 (Work on a Feature) 1. 建立新的分支 $ git checkout -b feature/- 例如: feature/chb-add-wishlist 如果是bugfix, branch命名以bugfix开始 例如: bugfix/chb-some-user-cannot-login 2. 开发功能并测试3. 提交修改$ git push origin 4.在github上创建pull request, 找人做代码审核(可跳过)特别提醒:创建的pull request应是功能分支到develop分支 5. 审核(或自审)通过后,清理提交记录12345git checkout develop git pull origin develop git checkout <feature branch name> git rebase -i develop git push origin <feature branch name> -f 目标:将所有的commits整理成一个或者几个比较重要的commit,并确保新的代码会出现在develop分支现有代码之后,以保持提交历史的整洁性 在这一步可能会出现conflicts, 需要人工解决 注:前三步操作可以用git alias来简化, 需要在~/.gitconfig配置一下git的alias 12$ ~/.gitconfig [alias] sync = \"!f() { echo Syncing $1 with develop && git checkout develop && git pull origin develop && git checkout $1 && git rebase -i develop; }; f\"$ git sync <feature branch name> 6. 在github上merge pull request并删除分支7. 终结这个功能分支 $ git checkout develop && git pull origin develop && git branch -d","categories":[],"tags":[]},{"title":"使用过的工具和库","slug":"使用过的工具和库","date":"2018-05-21T20:57:22.000Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"2018/05/使用过的工具和库/","link":"","permalink":"https://hq229075284.github.io/blog/2018/05/%E4%BD%BF%E7%94%A8%E8%BF%87%E7%9A%84%E5%B7%A5%E5%85%B7%E5%92%8C%E5%BA%93/","excerpt":"","text":"工具 名称/地址 描述 版本 minzipped sortable 前端拖拽功能组件 1.9.0 9.3KB yapi 提供mock服务的系统 – handsontable 类Excel sheet视图工具 7.0.3 310.5KB scrollbooster 点击拖拽表格左右滑动,可以优化window下横向滚动条的用户体验 2.7KB eruda 在手机端模拟browser的devtool进行调试 1.5.5 123.8KB docute 通过markdown生成单页应用 – infinite-scroll jQuery无限滚动 3.0.6 5.2KB Puppeteer chrome出品的headless Node Api – granim 背景渐变插件 2.0.0 4.7KB nanoid 唯一id生成器 2.0.3 286B superstruct 简易可自定义数据类型验证器 0.6.1 3.2KB draggable 拖拽库 1.0.0-beta.8 11.8KB dragula 拖拽库 3.7.2 3.7KB webpackmonitor webpack打包后的资源查看器 – webpack-bundle-analyzer webpack打包后的资源查看器 – lozad 图片和iframe懒加载 1.9.0 972B workbox Chrome出品的PWA库 4.3.1 802B commitizen git commit 格式化message – conf 在本地磁盘中创建配置文件 – Numeral-js 对数字进行格式化及简单的加减操作 2.0.6 3.8KB hammer.js 手势库 2.0.8 6.9KB anime 动画库 3.0.1 6.8KB marked markdown转码器 0.6.3 7.7kB","categories":[],"tags":[]},{"title":"webpack针对循环引用以及赋值export的处理注意事项","slug":"webpack针对循环引用以及赋值export的处理注意事项","date":"2018-03-08T22:04:19.000Z","updated":"2022-08-31T01:14:17.716Z","comments":true,"path":"2018/03/webpack针对循环引用以及赋值export的处理注意事项/","link":"","permalink":"https://hq229075284.github.io/blog/2018/03/webpack%E9%92%88%E5%AF%B9%E5%BE%AA%E7%8E%AF%E5%BC%95%E7%94%A8%E4%BB%A5%E5%8F%8A%E8%B5%8B%E5%80%BCexport%E7%9A%84%E5%A4%84%E7%90%86%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9/","excerpt":"webpack打包相互引用的模块 示例项目 遇到的问题 为什么会出现这样的问题 如何解决 webpack打包相互引用的模块示例项目(github) 遇到的问题在有2个或2个以上的文件之间的相互依赖关系构成闭环的时候,有时会出现Can't read Property 'xxx' of undefined或者(0,xxx) is not a function这类的错误,比如: 1示例项目中的src/index.js引用src/a.js,而src/a.js中也引用了src/index.js 为什么会出现这样的问题","text":"webpack打包相互引用的模块 示例项目 遇到的问题 为什么会出现这样的问题 如何解决 webpack打包相互引用的模块示例项目(github) 遇到的问题在有2个或2个以上的文件之间的相互依赖关系构成闭环的时候,有时会出现Can't read Property 'xxx' of undefined或者(0,xxx) is not a function这类的错误,比如: 1示例项目中的src/index.js引用src/a.js,而src/a.js中也引用了src/index.js 为什么会出现这样的问题 这就跟webpack打包后的代码执行逻辑有关 webpack的头部启动代码中,通过闭包中的installedModules对象,将模块名或者id作为对象的key来缓存各个模块的export的值,通过判断installedModules上是否缓存了对应模块的key来判断是否已经加载了模块 123456789101112// Check if module is in cacheif(installedModules[moduleId]) { return installedModules[moduleId].exports;}// Create a new module (and put it into the cache)var module = installedModules[moduleId] = { i: moduleId, l: false, exports: {}};// Execute the module functionmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__) 但存在一个问题:当模块还处于第一次执行中的状态时,如果碰到相互引用的情况的话,webpack可能会认为一个没有完全加载完成的模块已经加载完了 就拿export function.js中的代码和export const _var.js中的代码为例: export function.js 12345678910111213141516171819202122/***/ (function(module, exports, __webpack_require__) { \"use strict\"; Object.defineProperty(exports, \"__esModule\", { value: true }); exports._console = _console;// <- 📢注意这里 var _a = __webpack_require__(2); var _a2 = _interopRequireDefault(_a); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _console() { console.log('this is index.js'); } /***/ }), export const _var.js 12345678910111213141516171819202122/***/ (function(module, exports, __webpack_require__) {\"use strict\";Object.defineProperty(exports, \"__esModule\", { value: true});exports._console = undefined;// <- 📢注意这里var _a = __webpack_require__(2);var _a2 = _interopRequireDefault(_a);function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }var _console = exports._console = function _console() { console.log('this is index.js');};/***/ }), 从上面两段代码的📢处代码行可以看到,使用赋值语句export的代码打包后,对exports上的属性的赋值将在import(也就是webpack_require__)后,另一种使用申明函数语句export的代码打包后,对exports上的属性的赋值将在import(也就是__webpack_require)前。 这点细微的区别在执行相互引用的代码时会导致执行结果和你想的不一样,试想一下以下的代码执行过程: 在installedModules对象上设置index.js的key,加载index.js并执行 遇到import a.js 在installedModules对象上设置a.js的key,加载a.js并执行 遇到import index.js 检查,发现installedModules上已经存在index.js的key,直接读对象上缓存的exports(其实这里可能只在exports声明了属性名,并没有赋值) 执行exports上的_console函数(如果属性还没有被赋值就会出错) export的方式会影响以上过程的5、6步骤 如何解决 打破文件间的依赖关系的闭环 依赖关系闭环的情况下,只使用export function funcName(){}","categories":[],"tags":[]},{"title":"react中Context的传递","slug":"react中Context的传递","date":"2017-08-22T00:58:52.000Z","updated":"2022-08-31T01:14:17.712Z","comments":true,"path":"2017/08/react中Context的传递/","link":"","permalink":"https://hq229075284.github.io/blog/2017/08/react%E4%B8%ADContext%E7%9A%84%E4%BC%A0%E9%80%92/","excerpt":"ccontext的传递方向是由定义了根数据源的组件向子组件传递,父级 通过getChildContext(Class)在context上定义要传递给子级的数据 通过组件(Class)的childContextTypes定义传递给子级数据的数据类型 上上级组件传递的context数据可以被上级组件同key的context数据所替换,比如: 12345678910111213141516171819202122232425262728293031323334// Appimport React from \"react\";import PropTypes from \"prop-types\";import Parent from \"./Parent\";import Child from \"./Child\";class App extends React.Component { getChildContext() { return { from: \"this is from App\" }; } render() { console.log(\"App_Context-->\", this.context); return ( <div className=\"container\"> <Parent> <Child /> </Parent> </div> ); }}App.childContextTypes = { from: PropTypes.string};App.contextTypes = { from: PropTypes.string,};export default App","text":"ccontext的传递方向是由定义了根数据源的组件向子组件传递,父级 通过getChildContext(Class)在context上定义要传递给子级的数据 通过组件(Class)的childContextTypes定义传递给子级数据的数据类型 上上级组件传递的context数据可以被上级组件同key的context数据所替换,比如: 12345678910111213141516171819202122232425262728293031323334// Appimport React from \"react\";import PropTypes from \"prop-types\";import Parent from \"./Parent\";import Child from \"./Child\";class App extends React.Component { getChildContext() { return { from: \"this is from App\" }; } render() { console.log(\"App_Context-->\", this.context); return ( <div className=\"container\"> <Parent> <Child /> </Parent> </div> ); }}App.childContextTypes = { from: PropTypes.string};App.contextTypes = { from: PropTypes.string,};export default App 12345678910111213141516171819202122232425262728293031// Parentimport React from \"react\";import PropTypes from \"prop-types\";class Parent extends React.Component { getChildContext() { return { from: \"this is from Parent\", }; } render() { console.log(\"Parent_Context-->\", this.context); return ( <div className=\"parent\"> this is parent <hr /> {this.props.children} </div> ); }}Parent.childContextTypes = { from: PropTypes.string,};Parent.contextTypes = { from: PropTypes.string,};export default Parent; 12345678910111213141516// Childimport React from \"react\";import PropTypes from \"prop-types\";class Child extends React.Component { render() { console.log(\"Child_Context-->\", this.context); return <div className=\"children\">this is children</div>; }}Child.contextTypes = { from: PropTypes.string,};export default Child; 输出: 如果要将Child组件的父级组件直接挂载在Dom上,Child组件是拿不到App或者Parent组件传递过来的context,比如将上述的Parent.js改写为: 123456789101112131415161718192021222324252627282930313233343536373839// Parentimport React from \"react\";import ReactDOM from \"react-dom\";import PropTypes from \"prop-types\";class Parent extends React.Component { getChildContext() { return { from: \"this is from Parent\" }; } componentDidMount() { var div = document.createElement(\"div\"); document.body.appendChild(div); ReactDOM.render( <div className=\"parent\"> this is parent <hr /> {this.props.children} </div>, div ); } render() { console.log(\"Parent_Context-->\", this.context); return null; }}Parent.childContextTypes = { from: PropTypes.string};Parent.contextTypes = { from: PropTypes.string};export default Parent; 输出: 此时在Child组件中就获取不到父级传递下来的context,组件的构成如下: 如图可知App中申明的context,只能在其子组件中被访问,而Parent组件中通过ReactDom.render的方式挂载到body上,此时挂载的组件已经不再是App的子组件了,所以也就不能访问到App的context了 虽然无法直接获取App中的context,但render中返回null的Parent组件,可以访问App的context,所以可以借助Parent组件,将其作为一个中间件,比如: 12345678910111213141516171819202122232425262728293031323334353637383940414243444546// Parentimport React from \"react\";import ReactDOM from \"react-dom\";import PropTypes from \"prop-types\";class Parent extends React.Component { componentDidMount() { var div = document.createElement(\"div\"); document.body.appendChild(div); ReactDOM.render( <div className=\"parent\"> this is parent <hr /> <MiddleWare context={this.context}>{this.props.children}</MiddleWare> </div>, div ); } render() { console.log(\"Parent_Context-->\", this.context); return null; }}Parent.contextTypes = { from: PropTypes.string};export default Parent;class MiddleWare extends React.Component { getChildContext() { return { from: this.props.context.from, desc: \"deal with middleware\" }; } render() { return this.props.children; }}MiddleWare.childContextTypes = { from: PropTypes.string, desc: PropTypes.string}; 输出:","categories":[],"tags":[]}]}