SegmentFault 前端开发最新的文章
2022-06-15T09:37:27+08:00
https://segmentfault.com/feeds/blogs
https://creativecommons.org/licenses/by-nc-nd/4.0/
react useRef的两层用法,开眼界了
https://segmentfault.com/a/1190000041985694
2022-06-15T09:37:27+08:00
2022-06-15T09:37:27+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p>相信有过 <code>React</code> 使用经验的人对 <code>ref</code> 都会熟悉,它可以用来获取组件实例对象或者是DOM对象。</p><p>而 <code>useRef</code> 这个 <code>hooks</code> 函数,除了传统的用法之外,它还可以<strong>“跨渲染周期”</strong>保存数据。</p><p>首先来看一下它传统的用法:</p><pre><code class="jsx">import React, { useState, useEffect, useMemo, useRef } from 'react';
export default function App(props){
const [count, setCount] = useState(0);
const doubleCount = useMemo(() => {
return 2 * count;
}, [count]);
const couterRef = useRef();
useEffect(() => {
document.title = `The value is ${count}`;
console.log(couterRef.current);
}, [count]);
return (
<>
<button ref={couterRef} onClick={() => {setCount(count + 1)}}>Count: {count}, double: {doubleCount}</button>
</>
);
}</code></pre><p>代码中用 <code>useRef</code> 创建了 <code>couterRef</code> 对象,并将其赋给了 <code>button</code> 的 <code>ref</code> 属性。这样,通过访问 <code>couterRef.current</code> 就可以访问到 <code>button</code> 对应的 <code>DOM</code> 对象。</p><p>然后再来看看它保存数据的用法。</p><p>在一个组件中有什么东西可以跨渲染周期,也就是在组件被多次渲染之后依旧不变的属性?第一个想到的应该是 <code>state</code>。没错,一个组件的 <code>state</code> 可以在多次渲染之后依旧不变。但是,<code>state</code> 的问题在于一旦修改了它就会造成组件的重新渲染。</p><p>那么这个时候就可以使用useRef来跨越渲染周期存储数据,而且对它修改也不会引起组件渲染。</p><pre><code class="jsx">import React, { useState, useEffect, useMemo, useRef } from 'react';
export default function App(props){
const [count, setCount] = useState(0);
const doubleCount = useMemo(() => {
return 2 * count;
}, [count]);
const timerID = useRef();
useEffect(() => {
timerID.current = setInterval(()=>{
setCount(count => count + 1);
}, 1000);
}, []);
useEffect(()=>{
if(count > 10){
clearInterval(timerID.current);
}
});
return (
<>
<button ref={couterRef} onClick={() => {setCount(count + 1)}}>Count: {count}, double: {doubleCount}</button>
</>
);
}</code></pre><p>在上面的例子中,我用 <code>ref</code> 对象的 <code>current</code> 属性来存储定时器的ID,这样便可以在多次渲染之后依旧保存定时器ID,从而能正常清除定时器。</p><p>更多前端知识,请关注小程序,不定期有惊喜!<br><img src="/img/bVc0kyL" alt="image.png" title="image.png"></p>
ES6模块化改变前端的原生开发方式
https://segmentfault.com/a/1190000041922677
2022-05-31T16:18:32+08:00
2022-05-31T16:18:32+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p>ES6推出的模块化,使用方式有点类似 <code>require.js</code>,只需要引入一个入口文件即可,其他的js文件可以按功能创建及引入,<code>export</code> 导出方法属性,然后 <code>import</code> 引入使用,这个新特性可能在未来会引来原生开发的热潮,不需要三方框架(vue、react)即可自由使用模块化开发,而且目前浏览器市场快统一了,就连IE也放弃了自己的内核使用google webkit内核了,而且电脑也是默认配备了 IEAdge浏览器,想想就觉得爽。</p><blockquote>PS:使用模块化加载,需要服务器环境,也就是得通过 <code>localhost</code> 访问,而且 <code>script</code> 标签也得加上 <code>type="module"</code> 声明</blockquote><h2>一、基本示例</h2><p>a.js</p><pre><code>export default function a1() {
console.log('a1')
}</code></pre><p>index.html</p><pre><code class="html"><script type="module">
import a1 from './a.js'
a1() // a1
</script></code></pre><h2>二、解构赋值</h2><p>c.js</p><pre><code>export function c1() {
console.log('c1')
}
export function c2() {
console.log('c2')
}</code></pre><p>index.html</p><pre><code class="html"><script type="module">
import { c1, c2 } from './c.js'
c1() // c1
c2() // c2
</script></code></pre><h2>三、js文件引入</h2><p>a.js</p><pre><code>export default function a1() {
console.log('a1')
}</code></pre><p>b.js</p><pre><code>import a1 from './a.js'
export default function b1() {
a1()
console.log('b1')
}</code></pre><p>index.html</p><pre><code class="html"><script type="module">
import b1 from './b.js'
b1() // a1, b1
</script></code></pre><h2>四、标签引入</h2><p>d.js</p><pre><code>import a1 from './a.js'
a1()
console.log('d1')</code></pre><p>index.html</p><pre><code class="html"><script type="module" src="./d.js"></script></code></pre><p>综合上述几种情况,是不是感觉同 <code>vue</code> 或 <code>react</code> 开发使用方式一样,只不过这里需要通过 <code>script</code> 模块类型声明,但要知道这是原生的啊,不用搭建一套脚手架即可直接使用,如果是做一些系统项目话可以考虑直接“上马”。</p><p>更多前端知识,请关注小程序,不定期有惊喜!</p><p><img src="/img/bVcZ4ah" alt="image.png" title="image.png"></p>
vue3.x几个不看不知道的自动化加载技巧
https://segmentfault.com/a/1190000041884869
2022-05-23T17:24:54+08:00
2022-05-23T17:24:54+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<h2>前言</h2><p>最近鼓捣了一下 Vue3 + Vite2,遇到了不少问题,整理了5个可以提高开发效率的小知识,让你在 Vue3 的项目开发中更加丝滑、顺畅。</p><h2>一、setup name 增强</h2><p>Vue3的 <code>setup</code> 语法糖是个好东西,但使用 <code>setup</code> 语法带来的第一个问题就是无法自定义 <code>name</code>,而我们使用 <code>keep-alive</code> 往往是需要name的,解决这个问题通常是通过写两个 <code>script</code> 标签来解决,一个使用 <code>setup</code>,一个不使用,但这样必然是不够优雅的。</p><pre><code class="html"><script lang="ts">
import { defineComponent, onMounted } from 'vue'
export default defineComponent({
name: 'OrderList'
})
</script>
<script lang="ts" setup>
onMounted(() => {
console.log('mounted===')
})
</script></code></pre><p>这时候借助插件 <code>vite-plugin-vue-setup-extend</code> 可以让我们更优雅的解决这个问题,不用写两个 <code>script</code> 标签,可以直接在 <code>script</code> 标签上定义 <code>name</code>。</p><p>安装</p><pre><code class="cmd">npm i vite-plugin-vue-setup-extend -D</code></pre><p>配置</p><pre><code>// vite.config.ts
import { defineConfig } from 'vite'
import VueSetupExtend from 'vite-plugin-vue-setup-extend'
export default defineConfig({
plugins: [
VueSetupExtend()
]
})</code></pre><p>使用</p><pre><code class="html"><script lang="ts" setup name="OrderList">
import { onMounted } from 'vue'
onMounted(() => {
console.log('mounted===')
})
</script></code></pre><h2>二、API 自动导入</h2><p><code>setup</code> 语法让我们不用再一个一个的把变量和方法都 <code>return</code> 出去就能在模板上使用,大大的解放了我们的双手。然而对于一些常用的VueAPI,比如 <code>ref</code>、<code>computed</code>、<code>watch</code> 等,还是每次都需要我们在页面上手动进行 <code>import</code>。</p><p>我们可以通过 <code>unplugin-auto-import</code> 实现自动导入,无需 <code>import</code> 即可在文件里使用Vue的API。</p><p>安装</p><pre><code class="cmd">npm i unplugin-auto-import -D</code></pre><p>配置</p><pre><code>// vite.config.ts
import { defineConfig } from 'vite'
import AutoImport from 'unplugin-auto-import/vite'
export default defineConfig({
plugins: [
AutoImport({
// 可以自定义文件生成的位置,默认是根目录下,使用ts的建议放src目录下
dts: 'src/auto-imports.d.ts',
imports: ['vue']
})
]
})
安装配置完会自动生成auto-imports.d.ts文件。
// auto-imports.d.ts
// Generated by 'unplugin-auto-import'
// We suggest you to commit this file into source control
declare global {
const computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp']
const customRef: typeof import('vue')['customRef']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: typeof import('vue')['defineComponent']
const effectScope: typeof import('vue')['effectScope']
const EffectScope: typeof import('vue')['EffectScope']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope']
const h: typeof import('vue')['h']
const inject: typeof import('vue')['inject']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: t ypeof import('vue')['isRef']
// ...
}
export {}</code></pre><p>使用</p><pre><code class="html"><script lang="ts" setup name="OrderList">
// 不用import,直接使用ref
const count = ref(0)
onMounted(() => {
console.log('mounted===')
})
</script></code></pre><p>上面我们在 <code>vite.config.ts</code> 的配置里只导入了vue,<code>imports: ['vue']</code>,除了vue的你也可以根据文档导入其他的如<code>vue-router</code>、<code>vue-use</code> 等。<br>个人建议只对一些比较熟悉的API做自动导入,如vue的API我们在开发时都比较熟悉,闭着眼都能写出来,对于一些不大熟悉的像VueUse这种库,还是使用 <code>import</code> 更好一些,毕竟编辑器都有提示,不易写错。</p><p>解决eslint报错问题</p><p>在没有 <code>import</code> 的情况下使用会导致 <code>eslint</code> 提示报错,通过如下步骤解决:</p><pre><code>// vite.config.ts
AutoImport({
dts: 'types/auto-imports.d.ts',
imports: ['vue'],
// 解决eslint报错问题
eslintrc: {
enabled: true
}
})</code></pre><p>这时会自动生成 <code>.eslintrc-auto-import.json</code> 文件,将其导入 <code>eslintrc.js</code> 即可。</p><pre><code>// eslintrc.js
module.exports = {
extends: [
'./.eslintrc-auto-import.json'
]
}</code></pre><h2>三、告别.value</h2><p>众所周知,ref要求我们访问变量时需要加上 <code>.value</code>,这让很多开发者觉得难受.</p><pre><code>let count = ref(1)
const addCount = () => {
count.value += 1
}</code></pre><p>后来尤大大也提交了一份新的ref语法糖提案。</p><pre><code>ref: count = 1
const addCount = () => {
count += 1
}</code></pre><p>该提案一出便引起了社区的一片讨论,时间已经过去很久了,这里就不再废话这个话题了。<br>这里我介绍的是另外一种写法,也是官方后来出的一种方案,在 <code>ref</code> 前加上 <code>$</code>,该功能默认关闭,需要手动开启。</p><pre><code>// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [
vue({
refTransform: true // 开启ref转换
})
]
})</code></pre><p>开启之后可以这样写:</p><pre><code>let count = $ref(1)
const addCount = () => {
count++
}</code></pre><p>该语法糖根据不同的版本配置也略有不同,下面贴一下我自己所用相关插件的版本:</p><pre><code class="json">{
"vue": "^3.2.2",
"@vitejs/plugin-vue": "^1.9.0",
"@vue/compiler-sfc": "^3.2.5",
"vite": "^2.6.13"
}</code></pre><p>该属性仍处于实验性阶段,谨慎使用!!!</p><h2>四、自动导入图片</h2><p>在Vue2时我们经常会这样引用图片:</p><pre><code class="html"><img :src="require('@/assets/image/logo.png')" /></code></pre><p>但在Vite中不支持 <code>require</code> 了,引用图片变成了下面这样:</p><pre><code class="html"><template>
<img :src="Logo" />
</template>
<script lang="ts" setup>
import Logo from '@/assets/image/logo.png'
</script></code></pre><p>每次使用图片都得 <code>import</code>,显然耽误了大家摸鱼的时间,这时我们可以借助 <code>vite-plugin-vue-images</code> 来实现自动导入图片。</p><p>爽归爽,但容易发生变量冲突,慎用!</p><p>安装</p><pre><code class="cmd">npm i vite-plugin-vue-images -D</code></pre><p>配置</p><pre><code>// vite.config.ts
import { defineConfig } from 'vite'
import ViteImages from 'vite-plugin-vue-images'
export default defineConfig({
plugins: [
ViteImages({
dirs: ['src/assets/image'] // 指明图片存放目录
})
]
})</code></pre><p>使用</p><pre><code class="html"><template>
<!-- 直接使用 -->
<img :src="Logo" />
</template>
<script lang="ts" setup>
// import Logo from '@/assets/image/logo.png'
</script></code></pre><h2>五、忽略.vue后缀</h2><p>相信很多人在Vue2开发时,导入文件都是忽略 <code>.vue</code> 后缀的。但在Vite里,忽略 <code>.vue</code> 后缀会引起报错。</p><pre><code>import Home from '@/views/home' // error
import Home from '@/views/home.vue' // ok</code></pre><p>根据尤大大的回答,必须写后缀其实是故意这么设计的,也就是提倡大家这么去写。<br>但如果你真的不想写,官方也是提供了支持的。</p><pre><code>// vite.config.ts
import { defineConfig } from 'vite'
export default defineConfig({
resolve: {
extensions: ['.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
}
})</code></pre><p>这里要注意,手动配置 <code>extensions</code> 要记得把其他类型的文件后缀也加上,因为其他类型如js等文件默认是可以忽略后缀导入的,不写上的话其他类型文件的导入就变成需要加后缀了。</p><blockquote>PS:虽然可以这么做,不过毕竟官方文档说了不建议忽略 <code>.vue</code> 后缀,所以建议大家在实际开发中还是老老实实写上 <code>.vue</code>。</blockquote><p>更多前端知识,请关注小程序,不定期有惊喜!</p><p><img src="/img/bVcZOd4" alt="image.png" title="image.png"></p>
一个你想不到的小程序数组更新方式
https://segmentfault.com/a/1190000041861403
2022-05-18T12:39:14+08:00
2022-05-18T12:39:14+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p>首先这里不得不吐槽下小程序的数组更新,直接通过 <code>setData</code> 替换整个数组只在开发者工具上有效果,放到真机上就完全失灵,这是一件很蛋疼的事情,不知道小程序官方团队的更新机制是什么,也没有去细看,猜测可能与引用类型有关系。</p><p>看了网上大都是指定修改数组对象里的某个元素值,如这样:</p><pre><code>Page({
data: {
list: [{name: 'tom'}]
},
onLoad() {
this.setData({[`list[0].name`]: 'jack'})
}
})</code></pre><p>PS:感觉这样处理非常麻烦,需要一个个遍历去处理,而且新数组与旧数组的长度可能还不一至,还要对比,想想就放弃了,也没真正测试出来效果来。</p><p>因为要做的是一个简单的页面,只有地图和列表,所以就另辟蹊径通过 <code>redirectTo</code> 去处理了,其就是想通过刷新页面去实现视图更新的,因为小程序没有直接的刷新页面API,所以就想着通过 <code>reLaunch</code> 或 <code>redirectTo</code> 间接实现,不过 <code>reLaunch</code> 没效果,所以就确定了 <code>redirectTo</code> 加上参数实现的视图更新,说到这,其实这个和数组更新没有关系了,不过这确实是因为数组更新没效果引起的血案,只能通过其他方式来救场,相应的示例代码:</p><pre><code>Page({
data: {
activeTab: '',
list: []
},
onLoad(options) {
if(options.tab) {
this.setData({activeTab: options.tab})
}
this.getList()
},
changeTab(e) {
wx.redirectTo({url: `/pages/index/index?tab=${e.currentTarget.dataset.tab}`})
},
getList() {
wx.request({
url: 'xxx',
data: {
tab: this.data.activeTab
},
success: res => {
this.setData({list: res.data})
}
})
}
})</code></pre><p>最后虽然问题侧面解决了,但这不是正规的解决方式,而且有场景限制,所以还是希望官方看看能否优化数组的更新机制。</p><p>更多前端知识,请关注小程序,不定期有惊喜!</p><p><img src="/img/bVcZOd4" alt="image.png" title="image.png"></p>
vue3.x页面功能拆分方式
https://segmentfault.com/a/1190000041855981
2022-05-17T12:50:03+08:00
2022-05-17T12:50:03+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p>vue3.x相对比vue2.x主要的应用区别在于<code>setup</code>的使用,这个也是vue3.x的特色,所有的功能都得通过vue钩子引入使用,因为 <code>setup</code> 语法糖环境是不支持 <code>this</code> 的,这种开发方式有点回到原始的感觉,针对小项目还好,但如果页面模块功能复杂,如果都放到一个文件里堆叠,不仅会造成可读性差,而且时间长了难以维护,所以这就需要进行按功能拆分了,方式同vue2.x一样,一个是按照组件拆分,一个是混入处理,还有就是通过vuex或api分离功能</p><h2>一、 组件</h2><p>可以把一些新增/编辑、配置、日志及公共操作等写到组件里,然后引入使用,组件拆分是主要的减少页面代码量的解决方式,也是vue推荐的方式</p><blockquote>PS:组件拆分的方向,一是公共组件,在项目其他模块也能使用到,二是页面级私有组件</blockquote><h2>二、混入</h2><p>混入的场景主要是针对不需要模块且应用功能点过多,像这种可以通过混入的方式,把一些功能点拆分出来,引入使用,示例:</p><p>mixins/instuctLog.ts:</p><pre><code>export default function() {
const a = 123
function foo() {
console.log('foo')
}
return {
a,
foo
}
}</code></pre><p>页面:</p><pre><code class="html"><script lang="ts" setup>
import instructLogMixin from './mixins/instructLog'
const { a, foo } = instructLogMixin()
</script></code></pre><h2>三、api</h2><p>把页面模块中的一些api请求放到api目录里引入使用</p><h2>四、vuex</h2><p>按页面模块划分,把一些页面配置、枚举数据及数据改变多组件响应更新的逻辑放到vuex中处理</p><p>更多前端知识,请关注小程序,不定期有惊喜!</p><p><img src="/img/bVcZMOC" alt="image.png" title="image.png"></p>
Vue3.x项目开发常用知识点
https://segmentfault.com/a/1190000041834145
2022-05-12T12:41:53+08:00
2022-05-12T12:41:53+08:00
anchovy
https://segmentfault.com/u/anchovy
1
<p>PS:以下知识点都是基于 <code>vue3.x + typescript + element-plus + setup语法糖</code> 使用的。</p><h2>一、定义组件属性</h2><pre><code>const props = defineProps({
visible: {
type: Boolean,
default: false
}
})
console.log(props.visible)</code></pre><blockquote>[warning] 注意:<code>defineProps</code> 不用从vue引入,<code>setup</code> 语法糖环境会自动识别</blockquote><h2>二、formatter简写</h2><p>在 <code>el-table-column</code> 中使用 <code>formatter</code> 简写</p><pre><code class="html"><el-table-column label="时间" prop="createTime" :formatter="(...args: any[]) => formatTime(args[2])" /></code></pre><h2>三、子父组件通信</h2><p>子组件:</p><pre><code class="html"><script setup lang="ts">
const props = defineProps({
visible: {
type: Boolean,
default: false
}
})
const emit = defineEmits(['closeILdialog']) // 注册触发器,defineEmits不用从vue引入,setup语法糖环境会自动识别
function onDialogClose() {
emit('closeILdialog') // 触发
}
</script>
<template>
<el-dialog
v-model="visible"
width="900px"
@close="onDialogClose"
title="日志"
:close-on-click-modal="false"
>
</el-dialog>
</template></code></pre><p>父组件:</p><pre><code class="html"><script setup lang="ts">
let ILdialog = reactive({
visible: false
})
function closeILdialog() {
ILdialog.visible = false
}
</script>
<template>
<instruct-log :visible="ILdialog.visible" @closeILdialog="closeILdialog"></instruct-log>
</template></code></pre><h2>四、监听组件属性变化</h2><pre><code>const props = defineProps({
visible: {
type: Boolean,
default: false
}
})
// 监听visible
watch(() => props.visible, (newV) => {
if(newV) {
// ...
}
})</code></pre><h2>五、自定义指令</h2><p><img src="/img/bVcZG8n" alt="image.png" title="image.png"></p><p>局部指令:</p><pre><code class="html"><script setup lang="ts">
const vFoo = {
mounted(el: any, binding: any) {
console.log(binding.value) // 123
}
}
</script>
<template>
<div v-foo="123" v-auth="true"></div>
</template></code></pre><blockquote>[warning] 注意:局部指令定义需要 <code>v</code> 开头,如:<code>vFoo</code>,这样才能识别到 <code>v-foo</code> 指令</blockquote><p>全局指令:</p><pre><code>const app = createApp(App)
// 权限指令
app.directive('auth', {
mounted(el: any, binding: any) {
if(!binding.value) {
el.parentNode.removeChild(el)
}
}
})</code></pre><p>更多前端知识,请关注小程序,不定期有惊喜!</p><p><img src="/img/bVcZG8q" alt="image.png" title="image.png"></p>
如何有效的删除数组中符合条件的值?
https://segmentfault.com/a/1190000041776511
2022-04-29T11:37:19+08:00
2022-04-29T11:37:19+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p>项目开发比较常见的是删除数组中指定索引的值,代码如下:</p><pre><code>let arr = [1, 2, 3, 4, 5, 6, 7, 8]
for(let i=0,len=arr.length; i < len-1; i++) {
if(i === 2) { // 删除索引为2的值
arr.splice(i, 1)
break
}
}
console.log(arr) // [1, 2, 4, 5, 6, 7, 8]</code></pre><p>从上可以看出删除1个能通过 <code>break</code> 退出循序,但删除多个该如何处理呢?</p><p>这样实现?</p><pre><code>let arr = [1, 2, 3, 4, 5, 6, 7, 8]
for(let i=0,len=arr.length; i < len-1; i++) {
if(arr[i] < 5) {
arr.splice(i, 1)
}
}
console.log(arr) // ?</code></pre><p>结果:[2, 4, 5, 6, 7, 8],与预想的:[5, 6, 7, 8]对比明显不是想要的数据,出现这个原因是为什么呢?下面我们来剖析下循环执行步骤和输出结果:</p><p>第1次执行,把符合条件的数组元素 <code>arr[0]</code> 删除了,执行后,<code>i</code> 变成了 <code>1</code>,<code>arr</code> 变成了 <code>[2, 3, 4, 5, 6, 7, 8]</code><br>在第1次执行的结果上,再执行,就会把 <code>arr[1]</code> 删除,<code>i</code> 变成了 <code>2</code>,<code>arr</code> 变成了 <code>[2, 4, 5, 6, 7, 8]</code><br>接着在第2次执行的结果上执行,就没有符合条件的了,因为 <code>arr[2]</code> 为 <code>5</code>,不符合条件,arr最终还是<code>[2, 4, 5, 6, 7, 8]</code>,所以执行结果不对。</p><p>那么如何解决呢?我们可以采取 <code>i</code> 还原来处理,让 <code>i</code> 每次都从 <code>0</code> 开始,代码如下:</p><pre><code>let arr = [1, 2, 3, 4, 5, 6, 7, 8]
for(let i=0,len=arr.length; i < len-1; i++) {
if(arr[i] < 5) {
arr.splice(i, 1)
i--
}
}</code></pre><p><strong>总结:</strong></p><p>这是一个典型的原数组不断改变导致的执行结果不对问题,所以在处理数组的过程中要多思考,当前执行到数组的什么位置,这样可以有效的发现问题,予以解决。</p><p>更多前端知识,请关注小程序,不定期有惊喜!</p><p><img src="/img/bVcZr8Q" alt="image.png" title="image.png"></p>
vue3.x从打包、部署到上线
https://segmentfault.com/a/1190000041748505
2022-04-24T09:37:26+08:00
2022-04-24T09:37:26+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<h2>一、前言</h2><p>作者采用的项目架构:<code>vue3.x + typescript + element-plus + axios</code>,知道这一点很重要,因为目前 <code>vue3.x</code> 和 <code>element-plus</code> 还在不断更新迭代中,可能后面又会有所变化,导致具体的解决方案也会跟着改变。</p><p>具体版本:</p><pre><code class="json">{
"dependencies": {
"axios": "^0.26.1",
"element-plus": "^2.1.4",
"moment": "^2.29.1",
"qs": "^6.10.3",
"socket.io-client": "^2.3.0",
"vue": "^3.2.25",
"vue-router": "^4.0.14",
"vuex": "^4.0.2"
},
"devDependencies": {
"@types/node": "^17.0.22",
"@types/qs": "^6.9.7",
"@vitejs/plugin-vue": "^2.2.0",
"eslint": "^8.11.0",
"sass": "^1.49.10",
"typescript": "^4.5.4",
"unplugin-auto-import": "^0.6.6",
"unplugin-vue-components": "^0.18.3",
"vite": "^2.8.0",
"vue-tsc": "^0.29.8"
}
}</code></pre><h2>二、打包</h2><p>首次打包的话,还是常规的先<code>npm run build</code>一下,看看是否顺利,如果顺利最好,当然根据作者的经验,肯定会出些问题,这里就需要一一对应的解决了,</p><p>一般会遇到的问题如下:</p><h3>1、TS声明引用问题</h3><p><code>/// <reference path="../node_modules/socket.io-client/dist/socket.io.js" /></code></p><p>TS环境下使用<code>.js</code>文件,需要在编译配置里添加<a href="https://link.segmentfault.com/?enc=bgp6dB5AeyGi1TilztXYsA%3D%3D.SdPkks1LW06fWViJQ4OL6zw%2BZBsyPrS%2BcIS8d19VBbTJnu8GTEhDpy6p85yOgtgfrpLCVlZEcXyCJ6f1TlmIsw%3D%3D" rel="nofollow">allowJs</a>才行,不然会报错,具体修改如下:</p><p>打开 <code>tsconfig.json</code> 添加如下配置:</p><pre><code class="json">{
"compilerOptions": {
"allowJs": true
}
}</code></pre><h3>2、vue3 Cannot find name 'ComponentSize'问题</h3><p>出现这个问题主要是由<code>element-plus</code>引入的,具体解决方式如下:</p><p>打开 <code>tsconfig.json</code> 添加如下配置:</p><pre><code class="json">{
"compilerOptions": {
"skipLibCheck": true
}
}</code></pre><h3>3、This rule cannot come before a "@charset" rule问题</h3><p>出现这个问题主要是因为scss编译导致的,虽然只是个告警级别的问题,但看着也不舒服,索性就给处理掉,解决方式如下:</p><p>打开 <code>vite.config.js</code> 添加如下配置:</p><pre><code class="js">export default ({ mode }) => defineConfig({
css: {
preprocessorOptions: {
scss: {
charset: false,
additionalData: `@use "@/assets/styles/element/index.scss" as *;`,
},
},
}
})</code></pre><h2>三、部署</h2><p>这是一个集成到 <a href="https://link.segmentfault.com/?enc=vY4HOECd2ei06Vf0Tl61jA%3D%3D.susxm0bXtit585LrAlRlETBEDZHcFS1pXjtmKzn3ty8%3D" rel="nofollow">express</a> 框架上的单页应用,由于需要兼容以前的路由,所以这里需要特别处理,相关流程配置如下:</p><h3>第1步:修改配置,添加公共URL</h3><p>打开 <code>vite.config.js</code> 添加如下配置:</p><pre><code class="js">export default ({ mode }) => defineConfig({
base: '/vm/'
})</code></pre><p>配置后访问路径就由 <code>http://localhost/</code> 变成了 <code>http://localhost/vm/</code>。</p><h3>第2步:修改路由</h3><p>除了base配置还需要在路由中改变path路径,操作如下:</p><p>打开 <code>router/index.ts</code> 修改示例如下:</p><pre><code class="js">const routes: Array<RouteRecordRaw> = [
{
path: '/vm/login',
name: 'Login',
component: import('@/views/Login.vue')
}
]</code></pre><h3>第3步:修改菜单</h3><p>路由改完了,接下来就需要修改菜单链接了,修改示例如下:</p><pre><code class="html"><el-menu-item index="/vm/manage/department`">部门管理</el-menu-item></code></pre><h3>第4步:添加express路由</h3><p>vue的修改完了,下面就要修改web服务项目了,所以还需要到 express 项目添加通配路由配置,修改如下:</p><pre><code class="js">router.get("/vm/*", function (req, res, next) {
res.render("index");
});</code></pre><h3>第5步:移动文件</h3><p>把打包dist目录下的资源(assets、images等)放到 express 项目 <code>public/vm/</code> 目录下,然后把 <code>index.html</code> 放到 <code>views/</code> 目录下</p><h3>第6步:启动express项目</h3><pre><code>> npm start</code></pre><p>访问:<code>http://localhost/vm/login</code> 就可以打开登录页了</p><p>最后由于项目的复杂度,还需要在nginx中配置接口代理,相关配置如下:</p><pre><code class="nginx">location /vapi/ {
rewrite ^/vapi/(.*)$ /$1 break;
proxy_pass http://126.114.65.21:8080;
}</code></pre><p>这样凡是通过 <code>/vapi</code> 请求的接口都会代理到 <code>http://126.114.65.21:8080</code> 上。</p><h2>四、总结</h2><p>通篇从打包到部署的相关问题和修改都一一罗列,涉及到的技术点和修改稍微有点多,所以需要对vue3+ts技术栈有一个基本的掌握,然后这个是基于express实现的部署,所以可能和大家遇到的情况不一样,当然原理都差不多,一通百通,希望此篇对大家有参考价值。</p><p>更多前端知识,请关注小程序,不定期有惊喜!</p><p><img src="/img/bVcYH5x" alt="image.png" title="image.png"></p>
vue3.x路由404通配处理
https://segmentfault.com/a/1190000041738562
2022-04-21T17:25:42+08:00
2022-04-21T17:25:42+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<h2>vue-router3.x通配规则</h2><p>vue2.x对应的vue-router3.x的404路由通配方式:</p><pre><code>{
path: '*', // 会匹配所有路径
name: '404',
component: () => import('@/views/404/index.vue')
}</code></pre><h2>vue-router4.x通配规则</h2><p>上面的代码在vue3.x对应的vue-router4.x的路由通配中就没有效果了,需要改成这样才行:</p><pre><code>{
path: '/:error*', // /:error -> 匹配 /, /one, /one/two, /one/two/three, 等
name: '404',
component: () => import('@/views/404/index.vue')
}</code></pre><h2>匹配优先级</h2><p>有时候,同一个路径可以匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:路由定义得越早,优先级就越高。</p><blockquote>[success] PS:404通配路由一般放到路由定义的最底部</blockquote><p>更多前端知识,请关注小程序,不定期有惊喜!</p><p><img src="/img/bVcYH5x" alt="image.png" title="image.png"></p>
vue3.x+ts+vite2环境变量配置
https://segmentfault.com/a/1190000041733758
2022-04-20T20:30:06+08:00
2022-04-20T20:30:06+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p>在做项目环境变量配置前,可以先到官网回忆一下环境变量的基本使用,<a href="https://link.segmentfault.com/?enc=Lfe55qrxqtTC4r1EIPuUQA%3D%3D.YOfUQxO9Tbe%2B2mOdNiMmld3x5Q8HYxJ4yzE32%2F1BW6RGricdPO0UIgJkemUvqW3o" rel="nofollow">https://cn.vitejs.dev/guide/e...</a></p><h2>一、环境模式</h2><p>首先环境变量是可以分模式的,常用模式如下:</p><pre><code class="txt">.env # 所有情况下都会加载
.env.local # 所有情况下都会加载,但会被 git 忽略
.env.[mode] # 只在指定模式下加载
.env.[mode].local # 只在指定模式下加载,但会被 git 忽略</code></pre><p>默认 <code>dev</code> 环境下使用 <code>.env.development</code> 环境变量配置,<code>build</code> 环境下使用 <code>.env.production</code>,所以不需要在 <code>package.json</code> 中再指定模式了</p><pre><code>"scripts": {
"dev": "vite --mode development", // --mode development可以省略,运行 npm run dev 自动指定
"build": "vue-tsc --noEmit && vite build --mode production", // --mode production可以省略,运行 npm run build 自动指定
"preview": "vite preview"
},</code></pre><p><code>--mode</code> 一般在其他特殊自定义下指定使用。</p><h2>二、环境变量分类</h2><h3>2.1 默认环境变量</h3><ul><li>import.meta.env.MODE: {string} 应用运行的模式</li><li>import.meta.env.BASE_URL: {string} 部署应用时的基本 URL</li><li>import.meta.env.PROD: {boolean} 应用是否运行在生产环境</li><li>import.meta.env.DEV: {boolean} 应用是否运行在开发环境 (永远与 import.meta.env.PROD相反)</li></ul><h3>2.2 应用级环境变量</h3><p>以 <code>VITE_</code> 开头,这样会被vite处理,如下:</p><p>.env.developlent</p><pre><code>VITE_API_URL=/api/
VITE_LOCATION_ORIGIN=http://localhost:3000/</code></pre><p>另外自定义的环境变量,还需要在 <code>env.d.ts</code> 中声明变量类型</p><pre><code class="ts">/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_TITLE: string
readonly VITE_API_URL: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
declare module '*.vue' {
import type { DefineComponent } from 'vue'
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
const component: DefineComponent<{}, {}, any>
export default component
}</code></pre><h2>三、加载优先级</h2><p>模式覆盖通用,如:在生产环境下,<code>.env.production</code> 中的同名环境变量会覆盖 <code>.env</code> 中同名配置,其他同理</p><h2>四、环境变量使用</h2><p>Vite把环境变量通过 <code>import.meta.env</code> 暴露出来,在 <code>.vue</code> 中使用方式如下:</p><pre><code class="html"><script setup lang="ts">
console.log(import.meta.env)
</script></code></pre><p>但如果要在 axios 中使用就需要特别配置了,需要在 <code>vite.config.js</code> 中加载环境变量,我们可以像以下这种方式处理:</p><pre><code>import { defineConfig, loadEnv } from 'vite'
// https://vitejs.dev/config/
export default ({ mode }) => defineConfig({
define: {
'process.env': loadEnv(mode, process.cwd())
},
}</code></pre><p>这样配置完成后就可以在 plugins 下 axios.ts 中使用了</p><pre><code>const {
VITE_API_URL
} = process.env
const instance = axios.create({
baseURL: VITE_API_URL
});
export default instance</code></pre><p>更多前端知识,请关注小程序,不定期有惊喜!</p><p><img src="/img/bVcYH5x" alt="image.png" title="image.png"></p>
vue3+ts+vite2环境变量应该这样使用
https://segmentfault.com/a/1190000041599482
2022-03-24T10:08:34+08:00
2022-03-24T10:08:34+08:00
anchovy
https://segmentfault.com/u/anchovy
1
<p>在做项目环境变量配置前,可以先到官网回忆一下环境变量的基本使用,<a href="https://link.segmentfault.com/?enc=TUaVuN2f5nuRq8JDimwrlQ%3D%3D.Lk03HGPH23GGkFdzE6fhJKZ%2Fo7EekayLXniWopAXoS%2FYlpAmZ5I58G%2BKhCiER6DJ" rel="nofollow">https://cn.vitejs.dev/guide/e...</a></p><h2>一、环境模式</h2><p>首先环境变量是可以分模式的,常用模式如下:</p><pre><code class="txt">.env # 所有情况下都会加载
.env.local # 所有情况下都会加载,但会被 git 忽略
.env.[mode] # 只在指定模式下加载
.env.[mode].local # 只在指定模式下加载,但会被 git 忽略</code></pre><p>默认 <code>dev</code> 环境下使用 <code>.env.development</code> 环境变量配置,<code>build</code> 环境下使用 <code>.env.production</code>,所以不需要在 <code>package.json</code> 中再指定模式了</p><pre><code>"scripts": {
"dev": "vite --mode development", // --mode development可以省略,运行 npm run dev 自动指定
"build": "vue-tsc --noEmit && vite build --mode production", // --mode production可以省略,运行 npm run build 自动指定
"preview": "vite preview"
},</code></pre><p><code>--mode</code> 一般在其他特殊自定义下指定使用。</p><h2>二、环境变量分类</h2><h3>2.1 默认环境变量</h3><ul><li>import.meta.env.MODE: {string} 应用运行的模式</li><li>import.meta.env.BASE_URL: {string} 部署应用时的基本 URL</li><li>import.meta.env.PROD: {boolean} 应用是否运行在生产环境</li><li>import.meta.env.DEV: {boolean} 应用是否运行在开发环境 (永远与 import.meta.env.PROD相反)</li></ul><h3>2.2 应用级环境变量</h3><p>以 <code>VITE_</code> 开头,这样会被vite处理,如下:</p><p>.env.developlent</p><pre><code>VITE_API_URL=/api/
VITE_LOCATION_ORIGIN=http://localhost:3000/</code></pre><p>另外自定义的环境变量,还需要在 <code>env.d.ts</code> 中声明变量类型</p><pre><code class="ts">/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_TITLE: string
readonly VITE_API_URL: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
declare module '*.vue' {
import type { DefineComponent } from 'vue'
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
const component: DefineComponent<{}, {}, any>
export default component
}
</code></pre><h2>三、加载优先级</h2><p>模式覆盖通用,如:在生产环境下,<code>.env.production</code> 中的同名环境变量会覆盖 <code>.env</code> 中同名配置,其他同理</p><h2>四、环境变量使用</h2><p>Vite把环境变量通过 <code>import.meta.env</code> 暴露出来,在 <code>.vue</code> 中使用方式如下:</p><pre><code class="html"><script setup lang="ts">
console.log(import.meta.env)
</script></code></pre><p>但如果要在 axios 中使用就需要特别配置了,需要在 <code>vite.config.js</code> 中加载环境变量,我们可以像以下这种方式处理:</p><pre><code>import { defineConfig, loadEnv } from 'vite'
// https://vitejs.dev/config/
export default ({ mode }) => defineConfig({
define: {
'process.env': loadEnv(mode, process.cwd())
},
}</code></pre><p>这样配置完成后就可以在 plugins 下 axios.ts 中使用了</p><pre><code>const {
VITE_API_URL
} = process.env
const instance = axios.create({
baseURL: VITE_API_URL
});
export default instance</code></pre><p><a href="https://link.segmentfault.com/?enc=ZtiOBnQjO52ZVZOycRp0yg%3D%3D.PKtgUwvjuRO25VJRY6NZvxOTXI1STBLWu3IEcPP3wOU%3D" rel="nofollow">https://fenxianglu.cn/</a><br><img src="/img/bVcYH5x" alt="image.png" title="image.png"></p>
西瓜播放器前端代码
https://segmentfault.com/a/1190000040817744
2021-10-15T16:30:36+08:00
2021-10-15T16:30:36+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p>传送门:<a href="https://link.segmentfault.com/?enc=MzCsDYuDXdE6EPLjcbH0PA%3D%3D.Q3v9v0eA2TJp0dq%2FlGsopUSQSE%2B7J9zJ9NuMw24NK6%2BFRSbu%2FPxy9XCXH5pfP%2BS4" rel="nofollow">https://fenxianglu.cn/article...</a></p>
推荐两款买房计算器小程序
https://segmentfault.com/a/1190000039808053
2021-04-12T09:25:47+08:00
2021-04-12T09:25:47+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<h2>一、房贷计算器</h2><blockquote>专业计算房贷</blockquote><p><img src="/img/remote/1460000039808055" alt="" title=""></p><p><img src="/img/remote/1460000039808056" alt="" title=""></p><h2>二、房税计算器</h2><blockquote>专业计算房税(维修基金、契税等)</blockquote><p><img src="/img/remote/1460000039808057" alt="" title=""></p><p><img src="/img/remote/1460000039808058" alt="" title=""></p>
vuepress打包项目如何在express框架渲染
https://segmentfault.com/a/1190000039383046
2021-03-10T13:42:58+08:00
2021-03-10T13:42:58+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p>如果要在 <a href="https://link.segmentfault.com/?enc=RnIeguarzS2inSkWzrphmw%3D%3D.fTF6etvyvZidSI8WWvsQenIGjDB1a7cm%2F6pbSY9lC%2Bs%3D" rel="nofollow">express</a> 项目上加一个 <a href="https://link.segmentfault.com/?enc=4Yq56cxlQxKTZivS%2FTRYJg%3D%3D.syXRLt6AwlZPolc4h%2FKYo8agCCd9jyjpAn7eunoGc7A%3D" rel="nofollow">vuepress</a> 写的文档应如何渲染?</p><blockquote>Vuepress 是 Vue 驱动的静态网站生成器</blockquote><p><strong>1、<code>.vuepress/config.js</code> 修改 <code>base</code> 配置指定静态资源目录,如:test目录</strong></p><pre><code>base: "/test/",</code></pre><p><strong>2、打包去缓存配置</strong></p><p>package.json</p><pre><code>"scripts": {
"build": "vuepress build docs --no-cache"
},</code></pre><p><strong>3、把打包目录 <code>dist</code> 复制到 <code>express</code> 项目 <code>public</code> 目录下</strong></p><p><strong>4、路由配置</strong></p><pre><code>router.get('/test', function(req, res, next) {
res.render('test');
});</code></pre><p>视图目录:<code>views/test.html</code></p><p><strong>5、把 <code>dist</code> 目录下的 <code>index.html</code> 重命名为 <code>test.html</code> 然后剪切替换 <code>views/test.html</code> 即可</strong></p><p>访问:<a href="https://link.segmentfault.com/?enc=nWpWCay23lmY4pQsyYE0lQ%3D%3D.VE8%2FxVBNJaSpA%2BX7aOI6WqjxMDVhFjQ7jDESlJScouI%3D" rel="nofollow">http://localhost/test</a></p><p>欢迎关注:<a href="https://link.segmentfault.com/?enc=qlW0HSARVdsNNL%2B0Gbj4vA%3D%3D.1aRmZr8WCSLFQy321nSJMJ%2BPIMxMaNc%2B%2FeWHE1ZElk2O%2BHQg6U9KLf3SKxuGxm30xOE2ppEljq0NbZSUKhK3oQ%3D%3D" rel="nofollow">技术开发分享录</a></p><p><img src="/img/remote/1460000039258959" alt="" title=""></p>
webp图片的优劣势及生成
https://segmentfault.com/a/1190000039258957
2021-02-23T10:45:54+08:00
2021-02-23T10:45:54+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<h2>一、优劣势</h2><p><strong>优势</strong></p><p>WebP相比于JPG拥有更小的文件尺寸、更高的质量(相比于相同大小不同格式的压缩图片),抽取100张商品图片采用80%压缩,大约能减少60%的文件大小。</p><p><strong>劣势</strong></p><p>根据Google的测试,目前WebP与JPG相比较,编码速度慢10倍,解码速度慢1.5倍。 编码方面,一般来说,我们可以在图片上传时生成一份WebP图片或者在第一次访问JPG图片时生成WebP图片,对用户体验的影响基本忽略不计。 解码方面,WebP虽然会增加额外的解码时间,但由于减少了文件体积,缩短了加载的时间,页面的渲染速度加快了。同时,随着图片数量的增多,WebP页面加载的速度相对JPG页面增快了。</p><h2>二、在线生成</h2><ul><li><a href="https://link.segmentfault.com/?enc=sSgT%2FufVXxAHOOfcM6Pbew%3D%3D.PPdXzhT6tQFh3KK4Sv1j3xs4%2Fef%2BwTQTv3jAQlSES58%3D" rel="nofollow">智图</a></li><li><a href="https://link.segmentfault.com/?enc=1I2MHdJBjh58UNB9suYE4w%3D%3D.NqaIir%2FaJCuWlITUzUSMrX9ETaAiNEEEmQs59c5LSf4%3D" rel="nofollow">又拍云</a></li><li><a href="https://link.segmentfault.com/?enc=fjUADbHiWMeH77lr41hkfQ%3D%3D.SET6R7PQyEIELRNWk3h07uXoHxBn4flVmt4QHhKb95BJhF1YnhxsXDBkRiqo1u6%2B" rel="nofollow">CloudConvert</a></li><li><a href="https://link.segmentfault.com/?enc=NQflGZNJiAVGuj8ECO7a4A%3D%3D.u0bNf%2FYJZUPYTNZN3L%2FHBkFT4eZ%2FlE0E1ONm3IOnq%2FI5%2BmF0tafeh1wlpd2DK3kB" rel="nofollow">iSparta</a></li></ul><h2>三、代码生成</h2><h3>1、canvas生成</h3><pre><code>var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d'),
img = document.getElementById('img');
var loadImg = function(url, fn) {
var image = new Image();
image.src = url;
image.onload = function() {
fn(image);
}
}
loadImg('<image-url>', function(image) {
canvas.height = image.height;
canvas.width = image.width;
ctx.drawImage(image, 0, 0);
img.setAttribute('src', canvas.toDataURL('image/webp'));
});</code></pre><h3>2、gulp-WebP生成</h3><pre><code>var gulp = require('gulp');
var webp = require('gulp-webp');
gulp.task('default', () => {
gulp.src('./*.{png,jpg,jpeg}')
.pipe(webp({ quality: 80 }))
.pipe(gulp.dest('./dist'));
});</code></pre><h3>3、gulp-imageisux生成</h3><pre><code>var imageisux = require('gulp-imageisux');
gulp.task('default', () => {
gulp.src('./*.{png,jpg,jpeg}')
.pipe(imageisux('/dirpath/', enableWebp));
});</code></pre><ul><li>dirpath: 如果未定义,会自动生成两个目录:<code>/dest/</code> 目录放压缩后图片,<code>/webp/</code> 目录放对应的webp格式压缩图片。</li><li>enableWebp : 若为 <code>true</code>,则会同时输出webp图片;若为 <code>false</code>,则只会有压缩后原格式图片。</li></ul><h3>4、vue-webp-plugin生成</h3><pre><code class="shell">> npm install --save vue-webp-plugin</code></pre><p>在 <code>main.js</code> 中引入</p><pre><code>import WebpPlugun from 'vue-webp-plugin';
Vue.use(WebpPlugun);</code></pre><p>远程图片</p><pre><code class="html"><img v-webp="'https://h5.u51.com/99fenqi/vue//static/home_top_bg.png'"/></code></pre><p>本地图片</p><pre><code class="html"><img v-webp="require('static/home_top_bg.png')"/></code></pre><p>变量引入</p><pre><code class="html"><img v-webp="url" />
<script>
export default {
data() {
return {
url: require('static/home_top_bg.png')
}
}
}
</script></code></pre><p>数组遍历</p><pre><code class="html"><div class="foot2" v-for="(item,index) in imgList" :key="index">
<img :src="item.src" alt />
</div>
<script>
data() { //此处省略了很多代码
return {
imgList: [] //存储图片路径
}
},
mounted() { //通过mounted批量插入图片路径,这样就不用一个一个定义
let arr = this.imgList;
for (let i = 0; i < 9; i++) {
arr[i].src = require('../../image/' + (i + 1) + '.jpg'); //插入items1 9张图片路径
}
}
</script></code></pre><p>背景图片</p><pre><code class="html"><div v-webp:bg="require('static/home_top_bg.png')"></div></code></pre><h3>5、webp-loader生成</h3><p>安装</p><pre><code class="shell">> npm install webp-loader --save-dev</code></pre><p>配置</p><pre><code>{
test: /\.(jpe?g|png)$/i,
loaders: [
'file-loader',
'webp-loader'
]
}</code></pre><h3>6、webpack-react-webp生成</h3><p>安装</p><pre><code class="shell">> npm install webpack-react-webp --save</code></pre><p>配置(webpack.config.js)</p><pre><code>let webpackWebp = require('webpack-react-webp');
//不是开发环境必须要添加 webpackWebp.loader(),添加webp判断
let imgLoader = (env === 'dev' ? [] : [webpackWebp.loader()]).concat([
'file-loader?' + JSON.stringify({ name: imagePath + imgName + '.[ext]' }) //或者url-loader
]);
module.exports = {
module: {
loaders: [{
test: /\.(jpe?g|png|gif|svg)$/,
loaders: imgLoader
}]
},
plugins: [
new webpackWebp({
cssDomain: 'http://xxxxxxxxxxx', //支持 字符串与['http://11.xxxx','http://22.xxxx']
jsDomain: 'http://xxxxxxxxxxx'
imgPath: 'www/home/*', //*.{jpg,png,jpeg}
imgReg: ['jpg', 'png', 'jpeg'],
quality: 60
})
]
}</code></pre><p>欢迎关注:<a href="https://link.segmentfault.com/?enc=lRRbaVi0ct%2Fp00TOLFTcvw%3D%3D.tOT8AuXny%2FrliAyrfpmvNgvtes%2FhfZkWtPJD7QcuAOQqSkvzMrHzeIm4A%2BfFiZSIEWnnqEymHnphj79JQ6U%2FOA%3D%3D" rel="nofollow">技术开发分享录</a></p><p><img src="/img/remote/1460000039258959" alt="" title=""></p><p>参考链接:</p><ul><li><a href="https://link.segmentfault.com/?enc=Gg%2Bk%2B69qpiRNyAFqw9YhxQ%3D%3D.eH4Sz9fJ0CElfivYJ7ISRryvuWRifMh3BNn2xojWu74Son56%2FJROpmBWOX76VLt1" rel="nofollow">webp官网</a></li><li><a href="https://link.segmentfault.com/?enc=hcZU%2FP%2FV3bFaTz4eUatyZQ%3D%3D.1LOUfLvHBns81zogxngyvweBvvaYLVwGqEp1Q1VkCbyX6YEeXa3l2UHtkCADshmq" rel="nofollow">vue-webp-plugin</a></li><li><a href="https://link.segmentfault.com/?enc=fWM5IuA0Rmnga0je8hEJUg%3D%3D.1wXdGsvy3pETLseOHU3fI0VKpg6K5pjEMTi35ZDPXm0bcAefoPlf2lzXe3Ks0f5S" rel="nofollow">node-glob</a></li><li><a href="https://link.segmentfault.com/?enc=LFj4nCS9xqqeV0zbGTfS3w%3D%3D.QT5l%2BuncUu%2F4pfOQUeI%2FrefxT%2FGtwI5yl9NqSvicFIha98XnO%2Bry8j4svQaQZHnGUkDjMVcPrE0YK9GHcKpuFA%3D%3D" rel="nofollow">webpack-react-webp</a></li><li><a href="https://link.segmentfault.com/?enc=rwMK4POYNtDv61VHyAovYA%3D%3D.oCpPWgJBv3Kj%2BoxVxaWVieNmrL0JwwL49l9ET9lNZP0tuXPeCLZ7Zq1qTuLxvow5okJmIO0nretySYJOdnNL4Q%3D%3D" rel="nofollow">https://blog.csdn.net/weixin_...</a></li></ul>
动态polyfill和import
https://segmentfault.com/a/1190000039237060
2021-02-20T11:10:19+08:00
2021-02-20T11:10:19+08:00
anchovy
https://segmentfault.com/u/anchovy
2
<h2>一、动态polyfill</h2><p>以前都是通过 <code>babel-polyfill</code> 一把梭,不管能不能用到,都全量处理,这样虽然方便,但也会造成资源浪费,所以出来了一个动态 <code>polyfill</code> 概念,根据项目需要指定要 <code>polyfill</code> 的特性,而且会根据浏览器是否支持来决定要不要 <code>polyfill</code>,这样性能会更好,如何指定要 <code>polyfill</code> 的特性,直接到 <a href="https://link.segmentfault.com/?enc=nV46oXkHeCV%2FlWsfEV%2Fm1Q%3D%3D.WgqDYpnSefmTYiIbLV7EaXEFfdQtY6S9hi5MKAPJnc3IaKpEfh58rLDhcfcWAx2F" rel="nofollow">polyfill.io</a> 勾选生成即可</p><p>使用方式:</p><pre><code class="html"><script src="https://polyfill.io/v3/polyfill.min.js?features=Promise%2CReflect"></script></code></pre><p>基于安全考虑,阿里自己也搞了个</p><pre><code class="html"><script src="https://polyfill.alicdn.com/polyfill.min.js?features=Promise%2CReflect"></script></code></pre><h2>二、动态import</h2><pre><code>function async foo() {
const { formatTime }= await import('../utils/index');
let time = formatTime(Date.now(), 'YYYY-MM-DD');
}</code></pre><p>欢迎关注:<a href="https://link.segmentfault.com/?enc=QnCL0gkxSPSQVoW87TI6DA%3D%3D.WGnQqwe%2FOIW6E0Ojw02mPEwdMpOcKUrkcvb%2FYRs12WoVrtfrNbIIN0mQEKpANynO1h0AIGSUMnlzzW4Y%2BheA%2Bg%3D%3D" rel="nofollow">技术开发分享录</a></p><p><img src="/img/remote/1460000039237062" alt="wx-mp-qrcode-bdfx.jpg" title="wx-mp-qrcode-bdfx.jpg"></p>
免费字体下载
https://segmentfault.com/a/1190000039194198
2021-02-08T14:08:01+08:00
2021-02-08T14:08:01+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p>字体分享录:<a href="https://link.segmentfault.com/?enc=6umqtCcW9%2FRwiNQ8cWPl2A%3D%3D.AbHCpefUENHsvzjtgz3TC87qsUSU2WCIBrfgr2OMA4M%3D" rel="nofollow">https://ziti.fenxianglu.cn/</a></p><p><img src="/img/bVcOCmD" alt="QQ截图20210205135156.jpg" title="QQ截图20210205135156.jpg"></p>
react className变量使用方式
https://segmentfault.com/a/1190000039035264
2021-01-20T12:32:25+08:00
2021-01-20T12:32:25+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p><strong>index.module.scss</strong></p><pre><code class="scss">.select {
display: flex;
width: 148px;
height: 26px;
border-radius: 4px;
border: 1px solid #ffffff;
.active {
background-color: #ffffff;
color: #ac1d1d;
}
}
.select_item {
flex: 1;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
font-size: 13px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #ffffff;
}</code></pre><blockquote>[success] 这种命名规范主要是避免 <code>class</code> 作用域相互影响,在新版的 <code>React</code> 中命名为 <code>xxx.module.scss</code> 自动开启。</blockquote><p>在 typescript 里是这样的</p><pre><code>declare module '*.module.scss' {
export const style: any
}</code></pre><p><strong>index.js</strong></p><pre><code class="jsx">import styles from './index.module.scss'
class Demo extends Component {
return (
<div className={styles.select}>
<div className={`${styles.select_item} ${styles.active}`}>航班</div>
<div className={styles.select_item}>关注</div>
</div>
)
}</code></pre><blockquote>所有 <code>class</code> 直接通过 <code>styles.xxx</code> 一级的方式使用</blockquote><p><code>styles</code> 变量打印内容如下:</p><pre><code>{
active: "airList_active__G9PFh",
select: "airList_select__1T2lI",
select_item: "airList_select_item__1_MHW"
}</code></pre><p>最终在浏览器里的组合结果:</p><pre><code class="html">
<div class="airList_select__1T2lI">
<div class="airList_select_item__1_MHW airList_active__G9PFh">航班</div>
<div class="airList_select_item__1_MHW">关注</div>
</div></code></pre><p>从上可以看出 <code>index.module.scss</code> 里的样式,会被处理成一个一维 <code>json</code> 对象,然后通过层级组合添加到 <code>class</code> 里,从而实现最终样式效果,而且样式不会冲突,这个类似于 <code>vue</code> 的 <a href="https://link.segmentfault.com/?enc=5A4rS18MNYLFvOpXG65ejA%3D%3D.d6v3V%2B0Bb5VkO6OAl86XmiJ3uxJXHjommSPsL0xLQ5QLWuE1vHwXxbUJpm%2FTOSHRhZEfBYRd9BB55dFNYT9rPA%3D%3D" rel="nofollow">scoped</a></p><p><strong>附注:className多个值</strong></p><pre><code class="jsx"><div className={`${styles.foo} ${styles.bar}`}></div></code></pre><p>欢迎关注:<a href="https://link.segmentfault.com/?enc=%2Fwl16%2FAAXjbT4y99EwwF4g%3D%3D.GbHlZE6jKq0A1P4ZFNGzXFnjuEaisk%2FpuHAesJ3K52x7LuquGxA9wgdIExTdvQlJxGbddG%2BYxKtLSTNVvC9s%2Fg%3D%3D" rel="nofollow">技术开发分享录</a><br><img src="https://image-static.segmentfault.com/250/147/2501475532-6007b2379b470" alt="image" title="image"></p>
微信小程序轮播图高度自适应
https://segmentfault.com/a/1190000037715424
2020-11-04T09:38:40+08:00
2020-11-04T09:38:40+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p>图片自适应在网站上是内置好的,只需要设置宽度即可,但在微信小程序非要做一个封装,高度不是随宽度自适应,真是操蛋,不过谁叫在人家的平台的搞呢,还是不得不屈服于小马哥的淫威啊。。</p><p>在微信小程序上实现图片自适应需要配合javascript脚本,也就是需要动态计算才能实现,具体修改如下:</p><p>先看下view层是什么样的</p><pre><code class="html"><view class="zh-carousel" style="margin-top: {{carouselMarginTop}}rpx">
<swiper indicator-dots="true" autoplay="true" interval="4000" duration="500" class="zh-swiper" style="height: {{carouselHeight}}px">
<swiper-item class="zh-swiper-item" wx:for="{{carouselList}}" wx:key="index">
<image src="{{ item.image }}" mode="widthFix" bindload="adaptCarouselHeight"></image>
</swiper-item>
</swiper>
</view></code></pre><p>因为设置了导航样式为自定义,所以需要给轮播图加个margin-top值 ,不然会被小程序功能按钮遮挡</p><pre><code class="json">{
"usingComponents": {},
"navigationStyle": "custom"
}</code></pre><p><img src="/img/remote/1460000037715427" alt="image.png" title="image.png"></p><p>下面看下数据是如何计算的(注释很详细),如果不想细看的话,直接对着撸就行了</p><pre><code class="js">Page({
data: {
list: [],
carouselList: [
{image: '../../images/img-wx-gzh.png'},
],
carouselMarginTop: 0, // 这两个初始值必须要设置
carouselHeight: 0
},
onLoad: function() {
this.adaptCarouselMarginTop(); // 适配轮播图外间距
},
// 适配轮播图外间距
adaptCarouselMarginTop() {
let systemInfo = wx.getSystemInfoSync(),
pxToRpxScale = 750 / systemInfo.windowWidth, // px转换到rpx的比例
ktxStatusHeight = systemInfo.statusBarHeight * pxToRpxScale, // 状态栏高度
navigationHeight = 44 * pxToRpxScale; // 导航高度,44是大概估值
this.setData({
carouselMarginTop: navigationHeight + ktxStatusHeight + 10 // 10是一个预估值,可根据呈现效果修改
});
},
// 适应轮播图高度
adaptCarouselHeight(e) {
let imgWidth = e.detail.width, // 原图宽高
imgHeight = e.detail.height,
screenWidth = wx.getSystemInfoSync().screenWidth; // 手机屏幕宽度
let ratio = (screenWidth - screenWidth/750*60) / imgWidth;
this.setData({
carouselHeight: imgHeight * ratio
});
}
}</code></pre><p>欢迎关注:<a href="https://link.segmentfault.com/?enc=U%2BvMNtLFfU6yJ3e1Zu4s1g%3D%3D.QQk1UrqmGBWYvMnTvWiWV3iDxMLtz5OLIHn5yZXQE%2BM%3D" rel="nofollow">https://www.fenxianglu.cn/</a></p>
vuex-router-sync如何使用
https://segmentfault.com/a/1190000037680351
2020-10-31T20:12:44+08:00
2020-10-31T20:12:44+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p>简单来讲<code>vuex-router-sync</code>插件就是将<code>vue-router</code>的状态同步到<code>vuex</code>中</p><h2>一、安装</h2><ul><li>npm下载地址:<a href="https://link.segmentfault.com/?enc=pTPSwBIpuJWnh9dYfyeMWA%3D%3D.MgZsAjQU01EkFUq%2Bfz%2BRmCKeKDBLOzv7fAkXm2NIa6th3CicrYjfX7iHP9E%2FDZCY" rel="nofollow">https://www.npmjs.com/package...</a></li></ul><pre><code class="cli">> npm i vuex-router-sync --save</code></pre><h2>二、使用</h2><pre><code>import { sync } from 'vuex-router-sync'
import store from './vuex/store'
import router from './router'
sync(store, router, {moduleName: 'RouteModule'})
const app = new Vue({
router,
store,
}).$mount('#app');</code></pre><p>打印<code>store.state</code>即可看到当前路由状态</p><p><img src="/img/remote/1460000037680354" alt="image" title="image"></p><h2>三、使用场景</h2><p>假如您想在一个组件中显示一条消息,希望在几乎每一个页面上都显示“Have a nice day, Jack”,除了首页,因为首页要显示"Welcome back, Jack".<br>借助vuex-router-sync,您可以轻松实现</p><pre><code>const Top = {
template: '<div>{{message}}</div>',
computed: {
message() {
return this.$store.getters.getMessage;
}
},
};
const Bar = {
template: '<div>{{message}}</div>',
computed: {
message() {
return this.$store.getters.getMessage;
}
}
};
const routes = [{
path: '/top',
component: Top,
name: 'top'
},
{
path: '/bar',
component: Bar,
name: 'bar'
},
];
const router = new VueRouter({
routes
});
const store = new Vuex.Store({
state: {
username: 'Jack',
phrases: ['Welcome back', 'Have a nice day'],
},
getters: {
getMessage(state) {
return state.route.name === 'top' ?
`${state.phrases[0]}, ${state.username}` :
`${state.phrases[1]}, ${state.username}`;
},
},
});
// sync store and router by using `vuex-router-sync`
sync(store, router);
const app = new Vue({
router,
store,
}).$mount('#app');</code></pre><p>不然的话,你可能需要在<code>vue-router</code>的钩子函数里监听,或在<code>watch</code>里<code>$route</code>,然后修改<code>store</code>值来实现。</p><h2>四、原理</h2><p>在70多行的<code>vuex-router-sync</code>源代码里有以下几段代码</p><pre><code>store.registerModule(moduleName, {
namespaced: true,
state: cloneRoute(router.currentRoute),
mutations: {
'ROUTE_CHANGED': function ROUTE_CHANGED (state, transition) {
store.state[moduleName] = cloneRoute(transition.to, transition.from)
}
}
})</code></pre><p>首先是在我们的<code>store</code>中注册了一个<code>module</code>,名字默认为<code>route</code>:</p><p><code>module</code>中提供了一个叫<code>ROUTE_CHANGED</code>的<code>mutation</code>处理方法,然后还把<code>router</code>对象中的<code>currentRoute</code>保存在了<code>state</code>中,这也是我们为什么能够通过<code>this.$store.state.route</code>拿到<code>currentRoute</code>的原因。</p><p>然后就是监听<code>store</code>中的<code>route</code>对象的变化了,当<code>route</code>发生变化并且当前路由名字不等于需要跳转到路由的时候,直接通过<code>router</code>的<code>push</code>方法进行跳转页面:</p><pre><code>var storeUnwatch = store.watch(
function (state) { return state[moduleName]; },
function (route) {
var fullPath = route.fullPath;
if (fullPath === currentPath) {
return
}
if (currentPath != null) {
isTimeTraveling = true
router.push(route)
}
currentPath = fullPath
},
{ sync: true }
)</code></pre><p><code>store</code>的<code>watch</code>方法跟<code>vue</code>中的<code>watch</code>是一个概念,也就是检测某个属性的变化,然后回调。</p><p>最后通过<code>router</code>的全局后置钩子函数监听当前路由对象,修改<code>store</code>中的当前<code>state</code>(当前路由对象):</p><pre><code>// sync store on router navigation
var afterEachUnHook = router.afterEach(function (to, from) {
if (isTimeTraveling) {
isTimeTraveling = false
return
}
currentPath = to.fullPath
store.commit(moduleName + '/ROUTE_CHANGED', { to: to, from: from })
})</code></pre><p>欢迎关注:<a href="https://link.segmentfault.com/?enc=UPEg1PTFRDbKv8yfSaSyEA%3D%3D.mGkkLhSxLmXUo0PQHqAMY2Tnf3YUYFFIBzvIwPGgjXQ%3D" rel="nofollow">https://www.fenxianglu.cn/</a></p><p><img src="/img/bVcIagR" alt="image.png" title="image.png"></p><p>参考链接:</p><ul><li><a href="https://segmentfault.com/a/1190000019925019">https://segmentfault.com/a/11...</a></li><li><a href="https://link.segmentfault.com/?enc=z571KCpqK6Qka2T9uzm6lA%3D%3D.of922KHdo0wtYVXi0wXlWz3fe4%2BlANFSL0b9vD5of2QNXzHVb4AzyJxKgllbq%2Fr%2FhK7NkmI%2BPnlmf4RdjWuJTQ%3D%3D" rel="nofollow">https://blog.csdn.net/vv_bug/...</a></li></ul>
selenium之nodejs入门使用
https://segmentfault.com/a/1190000037672346
2020-10-30T17:41:13+08:00
2020-10-30T17:41:13+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p>selenium详解见此篇:<a href="https://link.segmentfault.com/?enc=ImvgjgZnJKHYcWOhOD2yOA%3D%3D.%2FFQvZZNvXWZ%2B4Hha%2BKbbf3gYUfoXqHa2qOJG4acQAbL9bhNIOQDp7DovDZjbRZX1" rel="nofollow">https://www.fenxianglu.cn/art...</a></p><p>有了对selenium文档的了解,这里还需要做一个hello world演示,不然不知道怎么跑起来的,心里总觉得不舒服,所以下面介绍下基于nodejs的selenium启动使用</p><p>首先nodejs是要安装的,这个直接到 <a href="https://link.segmentfault.com/?enc=xkRJAsZwGHXrXwRMYMRCkw%3D%3D.DvkaQ5NJyqu8c4p8Uku9jiIRu1y95GVELZMxBvD9DMU%3D" rel="nofollow">https://nodejs.org/en/</a> 上下载安装即可</p><p>然后创建 test/index.js 目录文件</p><p>根据selenium写官网下载页的指示,这里需要下载JavaScript的依赖包</p><p>下载地址:<a href="https://link.segmentfault.com/?enc=%2FXiqvvw2Y8zVkZ9Uk9xXGA%3D%3D.YkQzZm77TeR89%2BinlnMQYw%2BdWiqH%2BpaHazMXAblEU77F%2FEgHvptubSSpd%2BVmK4KmNRaZXyLhfQ%2Feg8%2B8YsDKOQ%3D%3D" rel="nofollow">https://www.npmjs.com/package...</a></p><p>npm下载</p><pre><code class="cli">test> npm install selenium-webdriver --save</code></pre><p>安装完成之后还需要下载浏览器驱动器,这里以firefox为例,下载操作系统对应版本,然后放到test目录下即可</p><p>相应浏览器驱动器列表</p><table><thead><tr><th>浏览器</th><th>可执行文件</th></tr></thead><tbody><tr><td>Chrome</td><td><a href="https://link.segmentfault.com/?enc=8aKpvsBNWPQkdWpvGEXbEg%3D%3D.hI3vGYTpv%2FF3jGs0fBFai4ARMONiwyBUbo0Yw9ok33rEs0xN6xcbEG%2FA6Tm53NnXflHzx3%2FEWrN9gnIE3xwaPw%3D%3D" rel="nofollow">chromedriver(.exe)</a> 64位的需要度娘找解决方案</td></tr><tr><td>Internet Explorer</td><td><a href="https://link.segmentfault.com/?enc=ntsgkb6o0CKeY%2B3J2tUBdg%3D%3D.6H%2BaDWRMNhssE65vkdaPE0qy7jmnust%2BflXPMQHHfqFEM85YRIZ1Fd%2FirzDt6egg7d4%2BUaG%2B7zqrIcsQnQwzug%3D%3D" rel="nofollow">IEDriverServer.exe</a></td></tr><tr><td>Edge</td><td><a href="https://link.segmentfault.com/?enc=S6OeE5DOk47jDYa822Nufg%3D%3D.hNWBZqLvEeNhjM8%2BC2hddC8GJy2C6%2Bk9yZu%2B7ocP8B1qs%2Ff%2BLa5So2OweNMgWKzh" rel="nofollow">MicrosoftWebDriver.msi</a></td></tr><tr><td>Firefox</td><td><a href="https://link.segmentfault.com/?enc=jKV0n%2FQVUwJOMdu1%2Bbrhsw%3D%3D.R2SqXSf%2FQu0MQlrsoYIAKjREi%2ByeA2ldH4FFrj9yfKXkpidkB54N%2FfWw47wedyawHAJTRUhpp41qUE%2FRIhF4Ag%3D%3D" rel="nofollow">geckodriver(.exe)</a></td></tr><tr><td>Safari</td><td><a href="https://link.segmentfault.com/?enc=odRGcxe6WjDQuQZ6tR5x%2Fg%3D%3D.NV3KsGQVNLkNQ%2FLQWXraTQPc0yWp6Z5aEF6M0XmsDDOz5xVn4ZdSKPh9dKgqkqP0YY%2BTeA21%2BmW%2F73rMMWiKraGN54nkuNd37ahUu2SfS2J1wFdxmK8E7om9BzA2kirWVjuVfnKwssi9THUcF%2BhOwDoAA7ZbaMOL3mCJEharSV3fQgwQc4yA3HvhhODggfG8CdwiWyIiHcO%2F%2BQG8ReAI1exEQofeLr6twblHfZzxyE8%3D" rel="nofollow">safaridriver</a></td></tr></tbody></table><p>index.js对应内容如下:</p><pre><code>const { Builder, By, Key, until } = require("selenium-webdriver");
(async function example() {
let driver = await new Builder().forBrowser("firefox").build(); // 以firefox浏览器为目标构建器
try {
await driver.get("https://www.baidu.com");
await driver.findElement(By.className("s_ipt")).sendKeys("selenium"); // 找到输入框,填充内容
await driver.findElement(By.className("s_btn")).sendKeys(Key.ENTER); // 触发enter键,执行搜索
await driver.wait(until.titleIs("百度一下,你就知道"), 1000); // 判断title是否为“百度一下,你就知道”,不是则报错,是则继续执行
console.log(222)
} finally {
// await driver.quit(); // 退出浏览器
}
})();</code></pre><p>执行脚本</p><pre><code class="cli">test> node index.js</code></pre><p>执行后会自动打开火狐浏览器,然后打开百度首页,自动填充搜索内容,进入搜索结果页面。</p><p><img src="https://upload-images.jianshu.io/upload_images/2114039-5113018f7f3a63cd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image.png" title="image.png"></p><p>send_keys相关使用如下:</p><pre><code>// 输入框输入内容
driver.find_element_by_id("kw").send_keys("seleniumm")
sleep(10)
// 删除多输入的一个 m
driver.find_element_by_id("kw").send_keys(Keys.BACK_SPACE)
sleep(10)
// 输入空格键+“教程”
driver.find_element_by_id("kw").send_keys(Keys.SPACE)
driver.find_element_by_id("kw").send_keys("教程")
sleep(10)
// ctrl+a 全选输入框内容
driver.find_element_by_id("kw").send_keys(Keys.CONTROL, 'a')
sleep(10)
// ctrl+x 剪切输入框内容
driver.find_element_by_id("kw").send_keys(Keys.CONTROL, 'x')
sleep(10)
// ctrl+v 粘贴内容到输入框
driver.find_element_by_id("kw").send_keys(Keys.CONTROL, 'v')
sleep(10)
// 通过回车键来代替单击操作
driver.find_element_by_id("su").send_keys(Keys.ENTER)
sleep(10)</code></pre><p>其他API请查看 <strong>selenium_webdriver</strong> 官方使用文档:<a href="https://link.segmentfault.com/?enc=KgSnhy%2BDGjpGbIM%2B0lZlmQ%3D%3D.xrNJWSCIuqVwNhoH6f7Or17MDBPSSbIYDIfedls2nACXcT%2FTgAFAjq7cuB%2FloZlOxBQooR1CUjDHmLv6baANPOUQ9nzP%2BboSFxJx9iRKvnI%3D" rel="nofollow">https://www.selenium.dev/sele...</a></p><p>欢迎关注:<a href="https://link.segmentfault.com/?enc=rKMkDaNvjFxU6kJaVwydIw%3D%3D.qUYSFbXME01puYRPzMD3QFE6LMWcrZgC9ScGtfNKcTE%3D" rel="nofollow">https://www.fenxianglu.cn/</a></p><p><img src="/img/bVcIagR" alt="image.png" title="image.png"></p>
做项目容易但运营真的好难
https://segmentfault.com/a/1190000037666073
2020-10-30T12:59:26+08:00
2020-10-30T12:59:26+08:00
anchovy
https://segmentfault.com/u/anchovy
1
<p>直到做了一个平台后,现在真的明白当初华为为什么没有推自己的手机操作系统,因为用户生态很难建立,用户已经习惯了现有的ios和android操作系统,再出来一个新的操作系统,可能就只有国内人买账了,因为对应操作系统上的应用真的太多了,一个个重头开发或兼容到新的操作系统上确实不是一两年能实现的,而以互联网的快速发展来看,恐怕等不急,时代变化太快。</p><p>自认做的平台还不错,扁平化清晰高亮(此时有种老王卖瓜的感觉,嘿嘿),各种SEO站长平台也做了,但一天的访问量确实没有多少,一方面可能是因为技术领域偏向及文章内容的问题,一方面现在市场上确实存在很多大的技术平台,如:思否、CSDN、简书等,肯定竞争不上,也没打算竞争,只是在争取一个小空间,做一个技术积累顺便赚点小钱而已。可结果对自己的打击太大了,由开始的几十人访问,到现在只有自己在默默输出,快成一个死站了,心里就想网站做的再好有什么用,没有流量不懂运营,做的再好也无人问津。</p><p><img src="/img/remote/1460000037666076" alt="image.png" title="image.png"></p><p>自己也没学过运营,也不知道如何运营,只是以自己的方式,在各大平台同步自己的文章引流而已,但这个效果不太理想,因为技术这行的粘性不是那么强,大家搜索查看大多以解决问题为主,很少有人专门停留。到了这个阶段,也不知道要如何运营了,准备买本书看看运营之道学习下,也是期待能够成长,给自己鼓励了。</p><p><img src="/img/remote/1460000037666077" alt="timg.gif" title="timg.gif"></p><p>欢迎关注:<a href="https://link.segmentfault.com/?enc=%2FGcQzldVd2PA3iB1%2F5jVCQ%3D%3D.j6O4dEDtX6anA438vyYCgY7dwgYcvVn9wpddd%2FBzdaM%3D" rel="nofollow">https://www.fenxianglu.cn/</a></p><p><img src="/img/bVcIagR" alt="image.png" title="image.png"></p>
微信小程序反编译
https://segmentfault.com/a/1190000037656105
2020-10-29T17:10:37+08:00
2020-10-29T17:10:37+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<h2>一、相关环境安装</h2><p>1、下载 <a href="https://link.segmentfault.com/?enc=iwo5%2FMgvyt1WaUG97u1F%2BA%3D%3D.koRp11TvXpqNPZKoGqm4k1GxFgp9UJjH6ZSj9apHY6k%3D" rel="nofollow">nodejs</a><br>2、下载反编译nodejs脚本 <a href="https://link.segmentfault.com/?enc=ns8AmkvuM7qbcoI3X4VEUg%3D%3D.HVLP0YddFXWTLZcN8GvmQfvTmvK%2BSmQvMnUSoIVEy38xZxVEe4GJo7fLiP%2FUAL%2Fd" rel="nofollow">wxappUnpacker</a> 提取码:mduo <br>3、下载 <a href="https://link.segmentfault.com/?enc=5EWc4Ut0fDtxWLKb9%2F9UEQ%3D%3D.mNqDCeVRp%2Bn6tjnI2088FQ6TPsObWnY1W6z45Kyf9yU%3D" rel="nofollow">夜神模拟器</a><br>4、下载 <a href="https://link.segmentfault.com/?enc=erpS9YvBSjJU%2BNucwsxWTg%3D%3D.TkgI3PWCbNNdEDoFiQxDhUVh1fSx%2FiMmedhj%2BMiDRygWHxdTJsBV1sHoIr9LZG0MhJdD5%2F6zX%2FiBzUt%2BMw7Xq27%2FiKltxH%2FzAXMvWHa%2BUTM%3D" rel="nofollow">RE文件管理器</a></p><blockquote>RE:Root Explore</blockquote><h2>二、获取.wxapkg文件</h2><p>在<strong>夜神模拟器</strong>里安装<strong>微信</strong>和<strong>RE文件管理器</strong></p><blockquote>PS:把前面下载好的<strong>RE文件管理器</strong>直接拖到<strong>夜神模拟器</strong>安装即可</blockquote><p>接下来设置模拟器的超级权限,如下图:</p><p><img src="/img/remote/1460000037656109" alt="image" title="image"></p><p><img src="/img/remote/1460000037656108" alt="image" title="image"></p><p>然后在模拟器微信里搜索想要获取的小程序,在微信中运行一下后,直接切回模拟器桌面运行<strong>RE文件管理器</strong>,按照下面的目录结构找文件:</p><blockquote>/data/data/com.tencent.mm/MicroMsg/{一串16进制字符}/appbrand/pkg/</blockquote><p><img src="/img/remote/1460000037656110" alt="image" title="image"></p><p>你会看到发现里面的一些.wxapkg后缀的文件,可以根据时间来判断那个是你需要小程序.wxapkg文件,右键长按文件,点击右上角压缩所选文件,然后再将压缩好的包通过微信/QQ发送给好友或者文件助手</p><p><img src="/img/remote/1460000037656111" alt="image" title="image"></p><p><img src="/img/remote/1460000037656112" alt="image" title="image"></p><p>准备好反编译包(前面下载的),安装相关依赖</p><p><img src="/img/remote/1460000037656113" alt="image" title="image"></p><pre><code class="cli">> npm install esprima css-tree cssbeautify vm2 uglify-es</code></pre><p>进行反编译</p><pre><code class="cli">> node wuWxapkg.js D:\_163200311_32.wxapkg</code></pre><p><img src="/img/remote/1460000037656114" alt="image" title="image"></p><blockquote>注意:使用node wuWxapkg.js 反编译命令时,如果报 Cannot find module 'xxx' 这种类型的错误,<br>就直接使用 npm install xxx 先安装。如果报未识别的错误,就换个小程序试试吧</blockquote><p>欢迎关注:<a href="https://link.segmentfault.com/?enc=gHCNOxte6oyQDFpiqBaVow%3D%3D.tA0tdEY93o994pEQCA7toESoQXw%2FNEHnJggERO2R3Dk%3D" rel="nofollow">https://www.fenxianglu.cn/</a></p><p><img src="/img/bVcIagR" alt="image.png" title="image.png"></p><p>参考链接:</p><ul><li><a href="https://link.segmentfault.com/?enc=%2BqV0P9oi57KgE3Y4l695%2Fw%3D%3D.U5iAezLsy2cRPcfLBpJrJQNTtI7BHCm3OJNzYmNA9eqI9A9mbuOJZVubRo2qeg6kQt4FDf%2FcvLCLs8vV72WPmg%3D%3D" rel="nofollow">https://blog.csdn.net/qq_4113...</a></li><li><a href="https://link.segmentfault.com/?enc=Mf34%2FXIFOEvDIYxT3pwidg%3D%3D.KyrSXfjbzI9ctF%2Fl2P4Terd22buXvk19OdpgrJh8VGauA8Hr1RDhO7y19uH%2BTd%2FCnMswJs%2FA0nRCtD7PGlmfHg%3D%3D" rel="nofollow">https://blog.csdn.net/qq_3793...</a></li></ul>
phantomjs入门使用
https://segmentfault.com/a/1190000037640978
2020-10-28T21:58:00+08:00
2020-10-28T21:58:00+08:00
anchovy
https://segmentfault.com/u/anchovy
1
<p>PhantomJS是一个命令行工具。确保您熟悉命令提示符或PowerShell(在Windows上)或终端(在macOS和Linux上)的使用。<br>这个指令假设PhantomJS已经安装并放置在路径的某个地方(例如,Windows用户请<a href="https://link.segmentfault.com/?enc=jNYNzipXio6JfXKlAzpXKw%3D%3D.TQHReGoOQsdhFEDHgW1w3xSmVOPk2VZh5w30azLGTUKjNf3pUSJPa0kQgq9cn28IDSV9tCiHvdxm98q90ZPWU7SL324l9znBt29DJOe%2F%2FPmK6G68HTlgGU5hNpruuKtN" rel="nofollow">参阅本教程</a>)。</p><ul><li>官网:<a href="https://link.segmentfault.com/?enc=2LAG9hQLnzYYjPHiqvTZ8g%3D%3D.KwFzRxOjYOHcZzJDpxkSHy%2FIusAzSwlmk8CQ2elzgYA%3D" rel="nofollow">https://phantomjs.org/</a></li><li>中文网:<a href="https://link.segmentfault.com/?enc=x9yARd%2FDLpPnxhEVW%2B90yA%3D%3D.U%2FlnmQiWQ39AqSVQ2FRARw0LVo3Y3pZY%2F9Ok8vDyO3%2BTP7FHVBFYctmj6SHbILIYOh9Q20oJ2yvmfMQgFV9lPw%3D%3D" rel="nofollow">http://wenku.kuryun.com/docs/...</a></li></ul><h2>一、下载</h2><p>地址:<a href="https://link.segmentfault.com/?enc=Q%2BOfDBd%2BXWAtElY%2FjbzdKw%3D%3D.DV6VeioH%2BeJ8V2EpGV6mFSTj7CVjITi6UC2qkoZ1XlDCf0F6Ya01tNUYO9KVZ6pc" rel="nofollow">https://phantomjs.org/downloa...</a></p><p>选择对应操作系统的下载即可,下载完成后需要添加到环境变量中,才能使用phantomjs命令</p><h2>二、Hello World!</h2><p>创建test.js文件,内容如下:</p><pre><code>console.log('Hello, world!');
phantom.exit();</code></pre><p>执行命令</p><pre><code class="cli">> phantomjs test.js
Hello, world!</code></pre><blockquote>注意:phantom需要退出,不然会一直执行,所以需要在脚本文件尾加上<code>phantom.exit()</code></blockquote><h2>三、加载网页打印快照</h2><p>test.js修改如下:</p><pre><code>var page = require('webpage').create();
page.open('https://www.baidu.com', function(status) {
console.log("Status: " + status);
if(status === "success") {
page.render('./baidu-snapshoot.png');
}
phantom.exit();
});</code></pre><p>上面的代码是尝试打开百度网站,成功返回:success 失败返回:fail。成功后会执行网站截图保存下来</p><h2>四、测试加载网站的速度</h2><p>test.js修改如下:</p><pre><code>var page = require('webpage').create(),
system = require('system'),
t, address;
if (system.args.length === 1) {
console.log('Usage: loadspeed.js [some URL]');
phantom.exit();
}
t = Date.now();
address = system.args[1];
page.open(address, function(status) {
if (status !== 'success') {
console.log('FAIL to load the address');
} else {
t = Date.now() - t;
console.log('Loading ' + system.args[1]);
console.log('Loading time ' + t + ' msec');
}
phantom.exit();
});</code></pre><p>执行命令</p><pre><code class="cli">> phantomjs test.js https://www.baidu.com
Loading https://www.baidu.com
Loading time 1485 msec</code></pre><h2>五、代码评审</h2><p>在可以沙盒模式下使用<code>evaluate()</code>方法返回简单的javascript对象,不支持函数或闭包</p><p>test.js修改下如:</p><pre><code>phantom.outputEncoding="gb2312"; // 解决命令行乱码
var page = require('webpage').create();
page.open('https://www.baidu.com', function(status) {
var title = page.evaluate(function() {
return document.title;
});
console.log('Page title is ' + title);
phantom.exit();
});</code></pre><p>执行命令</p><pre><code class="cli">> phantomjs test.js
Page title is 百度一下,你就知道</code></pre><p>如果想拦截console打印,需要使用<code>onConsoleMessage</code>回调</p><pre><code>phantom.outputEncoding="gb2312";
var page = require('webpage').create();
page.onConsoleMessage = function(msg) {
console.log('Page title is ' + msg);
};
page.open('https://www.baidu.com', function(status) {
page.evaluate(function() {
console.log(document.title);
});
phantom.exit();
});</code></pre><p>执行命令</p><pre><code class="cli">> phantomjs test.js
Page title is 每一个星球都有一个驱动核心,
每一种思想都有影响力的种子。
感受世界的温度,
年轻的你也能成为改变世界的动力,
百度珍惜你所有的潜力。
你的潜力,是改变世界的动力!
Page title is %c百度2021校园招聘简历投递:https://talent.baidu.com/external/baidu/campus.html color:red
Page title is 百度一下,你就知道</code></pre><p>除了上面一些简单的示例,还需要探索使用PhantomJS进行页面自动化、网络监控、屏幕捕获和headless测试。官方也提供了一些写好的 <a href="https://link.segmentfault.com/?enc=rENuP99aQHVwhnoz%2F0En4g%3D%3D.wRJOW%2BJIQmAyIMOBkCoqHshu2omlRqfIqZUxtTAKnMybmZWp0db5HLvbCnYT25A5" rel="nofollow">示例</a> 下载运行即可。</p><p>欢迎关注:<a href="https://link.segmentfault.com/?enc=6RlNkCMu%2BiMsyq7E1CDwXA%3D%3D.8FGuiyqVzXstTUVR9yN82gqCAcQHNdrxQgOLqSuYvvg%3D" rel="nofollow">https://www.fenxianglu.cn/</a></p><p><img src="/img/remote/1460000037640981" alt="image.png" title="image.png"></p>
android webview与原生交互
https://segmentfault.com/a/1190000037450700
2020-10-13T09:35:19+08:00
2020-10-13T09:35:19+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p>demo.html完整示例</p><pre><code class="markup"><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>demo</title>
</head>
<body>
<div id="js_content"></div>
<button type="button" onclick="window.injectedObject.sayHello()">点击调用android代码</button>
<button type="button" onclick="window.injectedObject.sayHello('张三')">点击调用android代码并传递参数</button>
<script>
function androidCallJs() {
document.getElementById('js_content').innerHTML = 'hello tom';
}
// 带参数
function androidCallJsWithArgs(name) {
document.getElementById('js_content').innerHTML = `hello ${name}`;
}
</script>
</body>
</html></code></pre><h2>一、android调用webview javascript</h2><p>首先要启用javascript,默认是false</p><pre><code>ws.setJavaScriptEnabled(true);</code></pre><p>加载调用</p><pre><code>webView.loadUrl("javascript:androidCallJs()"); // hello tom
// 带参数
webView.loadUrl("javascript:androidCallJsWithArgs('jack')"); // hello jack</code></pre><blockquote>如果要显示后调用,只需把代码放到 <code>onPageFinished</code> 生命周期里就可以了</blockquote><h2>二、webview javascript调用android</h2><pre><code>webView.addJavascriptInterface(new MyJavascriptInterface(this), "injectedObject");</code></pre><p>MyJavascriptInterface.java部分代码</p><pre><code>public class MyJavascriptInterface {
@JavascriptInterface
public void sayHello() {
Log.e("hello tom");
}
@JavascriptInterface
public void sayHello(String name) {
Log.e("hello" + name);
}
@JavascriptInterface
public void printImageSrc(String src) {
Log.e("src", src);
}
}</code></pre><h2>三、加载完直接调用</h2><pre><code>webView.loadUrl(`javascript:(function() {
var images = document.images;
for(var i=0; i<images.length; i++) {
images[i].onclick = function() {
window.injectedObject.printImageSrc(this.src);
}
}
})()`);</code></pre><blockquote>注意:上面用的是javascript模板字符串,主要是为了方面阅读,实际使用需要android代码拼装</blockquote><p>欢迎关注:<a href="https://link.segmentfault.com/?enc=y933SUNTHwiHmC9R9%2B2Tug%3D%3D.I58%2FCTCgNldwiLBLRXfe7o70O4Z4GxxvNYZ2d5T10NU%3D" rel="nofollow">https://www.fenxianglu.cn/</a></p><p>参考链接:</p><ul><li><a href="https://link.segmentfault.com/?enc=pcTUY6WBW8mfq2VDLaDpUw%3D%3D.KT1gqA9Z9r01QkNK6N5jA55KoR3B4T4x7ItR%2Bjt%2FyBekPZGI01jpsHSaPQ3SOqFT" rel="nofollow">https://www.jianshu.com/p/97f...</a></li></ul>
grpcwebproxy代理服务2步启动
https://segmentfault.com/a/1190000025178961
2020-09-30T14:30:45+08:00
2020-09-30T14:30:45+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p>在看这篇文章之前,先要做好<strong>前提工作</strong></p><p>1、搭建一个gRPC服务:<a href="https://link.segmentfault.com/?enc=bREFuXyxdIvAph9Ts1cqVQ%3D%3D.W75s%2FqZDRMraDKzaevRg1L%2BKeBlvNmxMhij8TGOHxZ85XIdXOU2V2OSzAY7SUrzY" rel="nofollow">http://fenxianglu.cn/article/238</a><br>2、解析<code>.proto</code>文件为js文件:<a href="https://link.segmentfault.com/?enc=fq1kGfF7iQ8xl0UHTwj59g%3D%3D.f9Z2Ny8mW2wFk68x2i5Lghniuidj%2FO0tJS621hSxq42eeJ9Q6pOgxeSu7w5V%2B%2F5u" rel="nofollow">http://fenxianglu.cn/article/246</a></p><p>上面两步处理完成后,再搭建代理服务,不然前端是不能直连grpc服务的</p><h2>一、安装启动</h2><h3>1、下载</h3><p>下载地址:<a href="https://link.segmentfault.com/?enc=hUxidZaFE6N3MdKoWLmrBA%3D%3D.RuwMv2WFCIVXQpJxs%2BvRbnFX8UcCR%2B5ptuy0td9H0jMxtnqhHvcYaom6L7u5NKfnaLfhABIpSb%2F39xsyPqyCMA%3D%3D" rel="nofollow">https://github.com/improbable...</a></p><p>按操作系统选择,这里以 <code>grpcwebproxy-v0.13.0-win64.exe.zip</code> 为例</p><p>下载完成后把 <code>grpcwebproxy.exe</code> 放到项目根目录即可</p><h3>2、启动</h3><p>到存放 <code>grpcwebproxy.exe</code> 目录,打开命令行,执行以下命令</p><pre><code class="cmd">> grpcwebproxy --allow_all_origins --backend_addr=localhost:50051 --run_tls_server=false --server_http_debug_port=5005
time="2020-09-09T11:16:09+08:00" level=info msg="parsed scheme: \"\"" system=system
time="2020-09-09T11:16:09+08:00" level=info msg="scheme \"\" not registered, fallback to default scheme" system=system
time="2020-09-09T11:16:09+08:00" level=info msg="ccResolverWrapper: sending update to cc: {[{localhost:50051 0 <nil>}] }" system=system</code></pre><p>这样服务就启动了。</p><p><a href="https://link.segmentfault.com/?enc=DSH362SeoigE4Nqfb2f83w%3D%3D.gLwrUZmcwh4pcEnbE8qtoMzwU3DQ%2B78o2zbsmc3FuIbkyiYzQ0J5fPGBCspn10qOs04bvnp0Toegyp34UBi21dgIZlexdf%2FA4unTTJk%2FSB0%3D" rel="nofollow">grpcwebproxy</a> 相应参数项:</p><table><thead><tr><th>参数</th><th>值</th><th>描述</th></tr></thead><tbody><tr><td>backend_addr</td><td>127.0.0.1:50051</td><td>要代理的服务ip和端口</td></tr><tr><td>run_tls_server</td><td>true或false</td><td>是否开启tls_server,默认为true</td></tr><tr><td>server_http_debug_port</td><td>5005</td><td>http调试端口</td></tr><tr><td>allow_all_origins</td><td>true或false</td><td>是否允许跨域,默认false</td></tr><tr><td>use_websockets</td><td>true或false</td><td>是否启用websockets,默认false</td></tr><tr><td>server_http_max_read_timeout</td><td>10s</td><td>http服务最大读取超时</td></tr><tr><td>server_http_max_write_timeout</td><td>10s</td><td>http服务最大写入超时</td></tr><tr><td>backend_max_call_recv_msg_size</td><td>4 <em> 1024 </em> 1024</td><td>传输消息最大内容限制,默认4MB</td></tr></tbody></table><p>查看代理服务是否启动成功,直接访问:<a href="https://link.segmentfault.com/?enc=kakfw%2BIEMG%2FqZ7ec1dsBFw%3D%3D.6Ut68aEd05%2BQkf8wgOUSqQYznCTMH4hRZRdMn8nGiNA%3D" rel="nofollow">http://localhost:5005/</a></p><p><img src="/img/remote/1460000025178965" alt="image.png" title="image.png"></p><p>最后依据第2步解析<code>.proto</code>文件的介绍,在<code>.vue</code> script中使用</p><pre><code>import {
GreeterClient
} from '@/assets/protos/HelloWorld_grpc_web_pb.js';
import { HelloRequest } from '@/assets/protos/HelloWorld_pb.js';
let client = new GreeterClient('http://localhost:5005');
let helloRequest = new HelloRequest();
helloRequest.setName('tom');
helloRequest.setCity('合肥');
client.sayHello(helloRequest, {}, (err, response) => {
console.log(err, response);
});</code></pre><p>启动vue项目访问即可看到</p><p><img src="/img/remote/1460000025178964" alt="image.png" title="image.png"></p>
proto语法说明
https://segmentfault.com/a/1190000025124359
2020-09-25T16:41:31+08:00
2020-09-25T16:41:31+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p>官方文档:<a href="https://link.segmentfault.com/?enc=lLNg2hphGvCefpWm0zVv8g%3D%3D.%2FrsuAj5SvSKPzGFFQ7QYnhXvlFWcqzRDS1xynwN8cFqFhMsnvWUg9ggEUdwqKzyvHGSYk30CElb9ilViXmXCmA%3D%3D" rel="nofollow">https://developers.google.cn/...</a></p><h2>一、基本语法示例</h2><pre><code class="protobuf">/*
头部相关声明
*/
syntax = "proto3"; // 语法版本为protobuf3.0
package = "com.xxx.foo"; // 定义包名
import "common.proto"; // 导入common.proto
option java_package = "com.xxx.foo"; // 指定java包
// 搜索请求
message SearchRequest{
int32 page = 1; // 当前页
int32 page_size = 2; // 一页多少条,使用下划线分隔,设置的时候使用驼峰命令法,如:setPageSize(10);
enum Type {
IN = 0; // 0需要是第一个,第一个也是默认值
OUT = 1;
}
Type type = 3; // 类型
}
// 搜索响应
message SearchResponse{
int32 code = 1; // 状态码
string message = 2; // 消息
SearchList data = 3; // 数据,类型为SearchList
}
// SearchList结构
message SearchList{
repeated Item data = 1; // 数据记录项,repeated类型用来存放N个相同类型的内容
int64 count = 2; // 总条数
int32 page_size = 3; // 一页条数
}
// Item结构
message Item{
int64 id = 1; // id
string title = 2; // 标题
int64 create_time = 3; // 创建时间
int64 update_time = 4; // 更新时间
}
// 服务
service SearchService{
rpc GetSearchList(SearchRequest) returns (SearchResponse); // rpc 方法
}</code></pre><blockquote>命令规范建议使用上面示例</blockquote><p>字段类型有:</p><p><img src="/img/remote/1460000025124362" alt="" title=""></p><h2>二、字段修饰符</h2><ul><li>singular:单个的,有0个或1个(默认)</li><li>repeated:重复的,重复任意次数</li><li>required:要求的</li><li>optional:可选的</li><li>reserved:保留的,保留字段名或字段号</li></ul><pre><code class="protobuf">message Foo {
reserved 2, 15, 9 to 11;
reserved "foo", "bar";
string foo = 3 // 编译报错,因为‘foo’已经被标为保留字段
}</code></pre><blockquote>[warning] 注意:不能在同一个<code>reserved</code>语句中同时使用字段名和字段号。</blockquote><h2>三、嵌套</h2><pre><code class="protobuf">message SearchResponse {
message Result {
string id = 1;
string title = 2;
}
repeated Result results = 1;
}</code></pre><h2>四、引用</h2><pre><code class="protobuf">message OtherMessage {
SearchResponse.Result result = 1;
}</code></pre><h2>五、使用Any类型</h2><p>需要导入<code>import google/protobuf/any.proto</code></p><pre><code class="protobuf">import "google/protobuf/any.proto";
message ErrorStatus {
string message = 1;
repeated google.protobuf.Any details = 2;
}</code></pre><h2>六、oneof</h2><p><code>oneof</code>除了共享内存中的所有字段外,<code>oneof</code>字段与常规字段类似,并且最多可以同时设置一个字段。</p><pre><code class="protobuf">message TestMessage {
oneof test_oneof {
int32 id = 1;
string title = 2;
}
}</code></pre><h2>七、系统默认值</h2><ul><li>string默认为空字符串</li><li>bool默认为false</li><li>数值默认为0</li><li>enum默认为第一个元素</li></ul><p>欢迎关注:<a href="https://link.segmentfault.com/?enc=aYh5K7gxOGve%2FvMgbZc%2FXA%3D%3D.TXPPYe%2B6kBIH1PJhDaQUzeYYe8swczW3n1ja5SWg%2BHo%3D" rel="nofollow">https://fenxianglu.cn/</a></p><p>参考链接:</p><ul><li><a href="https://link.segmentfault.com/?enc=JoHE87q4nXZ2lumH1b4dlw%3D%3D.tlYqVeLzTOQb%2ByGgxjzTzW3k7xzo4EOxPzmAH3A3cjNfcZMBPGSLZql%2BybCcXB1yH6tj59wB0adjcIg7wn1J7w%3D%3D" rel="nofollow">https://blog.csdn.net/baidu_3...</a></li><li><a href="https://link.segmentfault.com/?enc=nnwSPGcr6NpKhjKms74rgQ%3D%3D.iOi01UtLBUexrpJFi1VwbjaX2ghczP6EJYVCPVwv0EYf1S2Qea1dUXtZaxNAGPc8" rel="nofollow">https://www.jianshu.com/p/6a6...</a></li></ul>
typescript接口使用总结
https://segmentfault.com/a/1190000024541840
2020-09-22T12:17:25+08:00
2020-09-22T12:17:25+08:00
anchovy
https://segmentfault.com/u/anchovy
1
<p>接口是一系列抽象方法的声明,是一些方法特征的集合,这些方法都应该是抽象的,需要由具体的类去实现,然后第三方就可以通过这组抽象方法调用,让具体的类执行具体的方法。</p><h2>一、初识</h2><pre><code>interface Item { // 定义一个item接口
id: number,
title: string
}
function renderList(item: Item) {
console.log(item.id, item.title); // 1 hello
}
renderList({id: 1, title: 'hello'});</code></pre><blockquote>PS:为什么以函数为第一个示例呢?主要是接口在日常的使用大多用来做函数参数使用</blockquote><h2>二、接口数据类型演示</h2><pre><code>enum Type {
Yes = 1,
No = 0
}
interface Item {
id: number, // 数字
title: string, // 字符串
status: boolean, // 布尔值
gender: Object, // 对象
girlfriend: null, // null
future: undefined, // undefined
favorites: string[], // 字符串数组
plan: [string, number], // 元组
type: Type, // 枚举
callback: ()=>string, // 返回字符串的回调函数
// callback(): string, // 函数的另一种写法
content: any // 任意
}
function renderList(item: Item): void {
console.log(item, item.callback());
}
renderList({
id: 1,
title: 'hello',
status: true,
gender: {1: '男', 0: '女'},
girlfriend: null,
future: undefined,
favorites: ['game', 'movie'],
plan: ['得分', 100],
type: Type.Yes,
callback: () => {
return '123123';
},
content: '这是一个内容'
});</code></pre><h2>三、是否可选</h2><p>默认属性都是必须的,如果要改成可选的加个?就行了</p><pre><code>interface Item {
id: number
title?: string,
}
function renderList(item: Item): void {
console.log(item);
}
renderList({
id: 1
});</code></pre><h2>四、只读属性</h2><p>在属性前添加<code>readonly</code>修饰符即可表明只读</p><pre><code>interface Item {
id: number,
readonly title: string
}
let item: Item = {id: 1, title: 'hello'}
item.title = 'world' // 报错</code></pre><h2>五、接口继承</h2><p>使用关键字<code>extends</code>来实现接口继承</p><pre><code>interface Shape {
color: string;
}
interface Square extends Shape {
size: number;
}
let square = <Square>{};
square.color = "blue";
square.size = 4;
// 或
// let square: Square = {
// color: "green",
// size: 2
// }
console.log(square);</code></pre><h2>六、接口实现</h2><p>使用关键字<code>implements</code>来实现接口继承</p><pre><code>interface ClockInterface {
currentTime: Date;
}
class Clock implements ClockInterface {
currentTime: Date = new Date();
constructor(h: number, m: number) {
console.log(h, m, this.currentTime);
}
}
let clock = new Clock(8, 10);</code></pre><h2>七、多种属性类型</h2><p>如果想要一个属性有几个类型,可以加个<code>|</code>间隔</p><pre><code>interface Shape {
type: number|string;
}
let shape = <Shape>{};
// shape.type = 1;
shape.type = '1';
console.log(shape.type)</code></pre><h2>八、可索引</h2><p>主要用来针对数组接口实现</p><pre><code>interface StringArray {
[index: number]: string;
}
let myArray: StringArray;
myArray = ["Foo", "Bar"];
let myStr: string = myArray[0];
console.log(myStr)</code></pre><h2>九、混合类型</h2><p>现实生产可能需要一个对象可以同时做为函数和对象使用,并带有额外的属性,这时就可以使用混合类型</p><pre><code>interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
function getCounter(): Counter {
let counter = <Counter>function (start: number) {
return `开始时间为:${start}`;
};
counter.interval = 123;
counter.reset = function () { };
return counter;
}
let c = getCounter();
console.log(c(10)); // 开始时间为:10
c.reset();
c.interval = 5.0;</code></pre><p>欢迎关注:<a href="https://link.segmentfault.com/?enc=08aDgOhXJlHBZKTXxsCDrQ%3D%3D.m0APCkNQgWUMHozbUngqnCVpg7LOGk5osxPHpXssGCc%3D" rel="nofollow">https://fenxianglu.cn/</a></p><p>参考链接:</p><ul><li><a href="https://link.segmentfault.com/?enc=yBENwUuC2EqCNPPcri0GxQ%3D%3D.%2FH7n6j22k1n7WN1n%2FLQis7%2BFDpMiXrmmsIMTDpN%2F6fvNE8c2joNGnTXGOh5%2BWWnMk5DbrNDxHZKg%2FWUBCKvCfw%3D%3D" rel="nofollow">https://www.tslang.cn/docs/ha...</a></li></ul>
Autoprefixer配置详解
https://segmentfault.com/a/1190000023960072
2020-09-10T10:24:53+08:00
2020-09-10T10:24:53+08:00
anchovy
https://segmentfault.com/u/anchovy
7
<blockquote>说是Autoprefixer介绍,其实是Browserslist介绍,因为Autoprefixer使用的是Browserslist</blockquote><h2>一、Browserslist</h2><ul><li>github仓库:<a href="https://link.segmentfault.com/?enc=XV7S6SJkToqlImVEY9s6UA%3D%3D.uJkE0x2kIqf5TAvncBt%2F8LPV991C3TBcOOHLAzP5LEmyJb250wViDvs%2BY1ivnDVcUL4W0xuQ30RxFv1%2F344myQ%3D%3D" rel="nofollow">https://github.com/browsersli...</a></li></ul><p>适用于以下前端工具:</p><ul><li><a href="https://link.segmentfault.com/?enc=knBgHoX2EFN6nJIXiCtHKg%3D%3D.zB88u4dSEWfucHvRQbPZR8B%2FSwKa283QBcnUsRNirRUfp%2FT4kdEBx2NVXmOlN8wp" rel="nofollow">Autoprefixer</a></li><li><a href="https://link.segmentfault.com/?enc=Y%2FNRKxwgW3HFJrTgd6usAw%3D%3D.q4osdf9qNEFDmxIs3zWmsjKMBwDbmRZLthr%2B3Ok9QVPgbQ7%2Bkji0L8aRrudno1Ffu8c0ugxHLckKv5NZwE%2FZBjj%2FIckTNbL6r7sA%2Fs%2By%2F2s%3D" rel="nofollow">Babel</a></li><li><a href="https://link.segmentfault.com/?enc=gEV%2B2hj9l%2BXVjaK4BxavZg%3D%3D.xW0CENP3iJ31%2BztWh5R0ZUqm%2BYT6L9l8d3vtivoFIP7fyzXw2nGb5qkcf4KY81LQAa4WE8P%2BwFbDGVGpaIeILA%3D%3D" rel="nofollow">postcss-preset-env</a></li><li><a href="https://link.segmentfault.com/?enc=fRlQyVYidKn8FYF2mRBkXg%3D%3D.pRPVtqhwwxhE1%2BqmHS%2BY1NemX2604m2Sbn%2Fos4bql0%2FS%2Bt4FifvLWMXmvh%2BbQr%2Fwl9SviMExvUli4yN48wbyqw%3D%3D" rel="nofollow">eslint-plugin-compat</a></li><li><a href="https://link.segmentfault.com/?enc=5%2BnNTsInCSzHzEt2lwR8eA%3D%3D.9Qn71qfahi7vgk9OSKvpXy3KgmKXMWoonRVlXEgZ892C5xZ%2BO4MoacwQD%2BHXu7Yq5exLdBN5gQfmk12z8QEKqYlQIOmoOuqxo6MEKQ64zPU%3D" rel="nofollow">stylelint-no-unsupported-browser-features</a></li><li><a href="https://link.segmentfault.com/?enc=kxAuwZ7Mh3VFUw1QrnK44A%3D%3D.nCjb9smn%2Bp2Q9UnigHhSVqc%2BW47zCQG3h%2F4X2uFURy%2BMWuwM7caMQATZ8tmZlpk8em3oUM4%2F5iLwozvjFqYIGw%3D%3D" rel="nofollow">postcss-normalize</a></li><li><a href="https://link.segmentfault.com/?enc=tgCv%2FoRSSXS96cBeg%2BCELw%3D%3D.cRoUvYkQzIwOiCN%2B9DmvVgKdCKXl15aGmMdIUDgdsnKbAoWhU3L%2B%2FuZyfxmRWNPJXJlJJLUgTJDVsP24UtT34g%3D%3D" rel="nofollow">obsolete-webpack-plugin</a></li></ul><p>package.json配置示例:</p><pre><code class="json">"browserslist": [
"defaults",
"not IE 11",
"not IE_Mob 11",
"maintained node versions"
]</code></pre><p>或者在.browserslistrc文件配置示例:</p><pre><code class="cmd"># Browsers that we support
defaults
not IE 11
not IE_Mob 11
maintained node versions</code></pre><p>开发者可以指定最近2个版本如:<code>last 2 versions</code>,让Browserslist针对指定工具,如:<code>caniuse-lite</code> 去查询数据匹配相应浏览器版本。</p><h2>二、工具列表</h2><ul><li><a href="https://link.segmentfault.com/?enc=L6%2FIna6HTojvZqE5MAaXbw%3D%3D.U5Nu5NzxLecqtaQ2ZDi5elcvJXSvq6NYxN8%2F%2FEVbWYY%3D" rel="nofollow">browserl.ist</a> 是一个在线工具,用来检查某些查询会选择哪些浏览器。</li><li><a href="https://link.segmentfault.com/?enc=L9KLlLqbBcJCHWFK7GZgFg%3D%3D.vVH%2F9qvA%2BJsAmivNEQ6XvIPbXuDNLWLftZ3LA8PfAzWuiZuQpgViAPEkDbAlqOLi" rel="nofollow">browserslist-ga</a> 和 <a href="https://link.segmentfault.com/?enc=nnGEGiFLHl7hXNx2x1qtEg%3D%3D.ldBtBwyJi8aC9gDSnel1BZK%2BVxfdZijE80NSanjV96%2FsbGemnoWaN0GNcLNW9xj%2Ba1K09c%2BfFPaXafgVhyFI6g%3D%3D" rel="nofollow">browserslist-ga-export</a> 下载您的网站浏览器统计数据(>0.5%)</li><li><a href="https://link.segmentfault.com/?enc=SIGYfOaDXThQ0tCSkfXFOw%3D%3D.zSZA4j3f8buHl0iYfWlTWZ1bUjEcmodvc5dL4%2FMWuqr2AnQdwOXXBVrXfFv7itHGX1TcoWrvn9iA1lSDps3arQ%3D%3D" rel="nofollow">browserslist-useragent-regexp</a> 将Browserslist查询编译为RegExp以测试浏览器useragent。</li><li><a href="https://link.segmentfault.com/?enc=FfuFsi3B0jQ0kOAkzhRSCg%3D%3D.X7AwtFQ%2FlaWO%2FLsI5itL33QSEK8cviw97w5s5htztLV5XAcftn5XQHhGuwAN7hyZQ4np6OIpPxqHmQlqdcRxCA%3D%3D" rel="nofollow">browserslist-useragent-ruby</a> 是一个Ruby库,用于根据用户代理字符串检查浏览器是否匹配Browserslist。</li><li><a href="https://link.segmentfault.com/?enc=vOfgqq0ONW8AUkQR4D%2FPtw%3D%3D.NOUUvSy8uJl5qXH5YEnlWEDgNDnqDV61TG7un0KWaBZXABZhG74Ct5y34kNnCbAFiRX2rjao5ifbjCMHt0IMVw%3D%3D" rel="nofollow">browserslist-browserstack</a> 在Browserslist配置中为所有浏览器运行BrowserStack测试。</li><li><a href="https://link.segmentfault.com/?enc=cP0%2FkxbUCqjq4oxyaLURtA%3D%3D.tZEN3N%2B0OkuE3VygWzI1VeBkLd8nnU%2BhDLxjyvkXqkLD8ovB6zdhSVxHaDfcl3V35OZsnMR%2BeiJgofw7qZFTrRCjlbKETdSwkOEF16r7GaY%3D" rel="nofollow">browserslist-adobe-analytics</a> 使用Adobe Analytics数据来定位浏览器。</li><li><a href="https://link.segmentfault.com/?enc=nhsRk5NI2xTEFs5zjySXEA%3D%3D.2EJLfvjfBA82AceVGczenYSQTTXDXRAFAC6cTYnktHlD2nB%2Faa9B%2BgzsdN%2BbWO4y" rel="nofollow">caniuse-api</a> 返回支持某些特定特性的浏览器。</li></ul><p>在项目目录中运行<code>npx browserslist</code>以查看项目的目标浏览器。这个CLI工具是内置的,并且可以在带有Autoprefixer的任何项目中使用。</p><h2>三、最佳实践</h2><p>以下是一个默认查询,为大多数用户提供一个合理的配置:</p><pre><code class="json">"browserslist": [
"defaults"
]</code></pre><p>如果你想改变浏览器的默认设置,我们建议结合最近的两个版本,如>0.2%。这是因为过去的n个版本本身并没有添加流行的旧版本,而仅使用超过0.2%的百分比就会使流行的浏览器在长期运行中更加流行。我们可能会陷入垄断和停滞状态,就像我们在使用Internet Explorer 6时一样。请小心使用此设置。</p><p>不仅限于chrome浏览器,请尊重浏览器多样性,不要因为不认识浏览器就删除它们。像Opera Mini在非洲拥有1亿用户,在全球市场上比微软Edge更受欢迎。中国QQ浏览器的市场份额超过了Firefox和桌面Safari浏览器的总和。</p><h2>四、查询</h2><p>Browserslist会使用浏览器和Node.js版本查询这些来源:</p><ul><li>.browserslistrc 配置文件在当前或父目录</li><li>browserslist 配置文件在当前或父目录</li><li>BROWSERSLIST 环境变量</li><li>如果上面的方法没有产生有效的结果,浏览器会使用默认值:<code>> 0.5%, last 2 versions, Firefox ESR, not dead</code></li></ul><h2>五、查询组合</h2><p><code>or</code>方式支持<code>or</code>或<code>,</code>,如:<code>last 1 version or > 1%</code> 等于 <code>last 1 version, > 1%</code></p><p>查询组合也支持执行交叉所有以前的查询: <code>last 1 version or chrome > 75 and > 1%</code> 的人会选择(<code>browser last version</code> 或 <code>Chrome since 76</code>)和超过1%的市场份额。</p><p>有三种不同的方式组合查询,如下所示。首先从单个查询开始,然后将查询组合起来得到最终的列表。</p><table><thead><tr><th>查询组合器类型</th><th>插图</th><th>示例</th></tr></thead><tbody><tr><td>并集</td><td><img src="/img/bVbMHet" alt="image.png" title="image.png"></td><td><code>> .5% or last 2 versions</code><br/><code>> .5%, last 2 versions</code></td></tr><tr><td>交集</td><td><img src="/img/bVbMHeI" alt="image.png" title="image.png"></td><td><code>> .5% and last 2 versions</code></td></tr><tr><td>排除</td><td><img src="/img/bVbMHeL" alt="image.png" title="image.png"></td><td>以下三个相等<br/><code>> .5% and not last 2 versions</code><br/><code>> .5% or not last 2 versions</code><br/><code>> .5%, not last 2 versions</code></td></tr></tbody></table><p>测试查询的一种快速方法是:<code>npx browserslist '> 0.5%, not IE 11'</code></p><h2>六、完整列表</h2><p>你可以通过查询来指定浏览器和Node.js版本(不区分大小写):</p><ul><li><code>defaults</code>:Browserslist的默认浏览器(<code>> 0.5%, last 2 versions, Firefox ESR, not dead</code>)</li><li><p><code>> 5%</code>:根据全局使用统计数据选择浏览器版本,<code>>=</code>、<code><</code>和<code><=</code>处理</p><ul><li><code>> 5% in US</code>:使用美国的使用统计。它接受两个字母的<a href="https://link.segmentfault.com/?enc=rMF4bw0mZ4U46BPJJCj8Qg%3D%3D.%2BiPUSS4V0Wz4%2FWnuYU9fojmVfVULJyonneHINMrfkLlk2I9Lu5zC1EWOuhOeivO%2BcstCgu2hoYWSS%2BE2opDDMj5KRz2JUzEEJq5JObywBd1vsvNGvF9oFNCQKWedQVoI" rel="nofollow">国家代码</a></li><li><code>> 5% in alt-AS</code>:使用亚洲地区的使用统计数据。所有地区代码的列表可以在<a href="https://link.segmentfault.com/?enc=QeCDgHZptF8Lg34alVrXbQ%3D%3D.ckUquC2I3U1COwRMZpfoLk%2BzLeJ%2FAzvgBNSp0iZrLzlJevZq%2BNn%2BkKOU9mtFN0yoUu3%2Fu%2FbGU%2BKikYl09NaoAw%3D%3D" rel="nofollow">caniuse-lite/data/regions</a>找到</li><li><code>> 5% in my stats</code>:使用<a href="https://link.segmentfault.com/?enc=TIV7giYhBQtB5jnKXF%2FuWw%3D%3D.1UGVHosfsC2jls1Gdfk1pFEWB42rZ5iZxzTA9pSMF3Tc5L6q%2BktmudhIgupAL%2B%2F%2BD0K5hfV2l48x1Jhd6BwFZw%3D%3D" rel="nofollow">自定义使用数据</a></li><li><code>> 5% in browserslist-config-mycompany stats</code>:使用来自<code>browserslist-config-mycompany/browserslist-stats.json</code>的<a href="https://link.segmentfault.com/?enc=c7wWttiLvy0%2FNH1zxTDb6g%3D%3D.cxvs1nHni2z3PhNkWRfDGPMjOqM%2BxiFldbPb5962VRas%2BQybxIxMWGCTSTkm9QnMC06LKEI4uYvZG5FotLYZAw%3D%3D" rel="nofollow">自定义使用数据</a></li><li><code>cover 99.5%</code>:覆盖最流行的浏览器</li><li><code>cover 99.5% in US</code>:同上,不过限制了两个字母的<a href="https://link.segmentfault.com/?enc=JkVTl6d3bjkLgJ1HpRtJHA%3D%3D.3em9cHbJbp6jXYt1DTTJ0eL95f7lJ78JPZCS3saZ7XJTIpba6IP6ieN3Axo8dxBePVyQCqejJ3BuNl9%2Fs9cl0D9cv0BMWFrzngrI3N%2Fm%2B1noW06KaPKuK3K%2B3RvX9A00" rel="nofollow">国家代码</a></li><li><code>cover 99.5% in my stats</code>:覆盖最流行<a href="https://link.segmentfault.com/?enc=pYgbUdPs6Z8VNwC8DuZjXA%3D%3D.hyLs3a0HWMjp%2F0FKecJ8HbycD3oCtgBvxhpc%2B%2BJhMoeil5egum%2FHhOWlb7YNlbXFhaKhc2D9McajXOJTIfKEqA%3D%3D" rel="nofollow">自定义使用数据</a>的浏览器</li></ul></li><li><code>dead</code>:24个月内没有官方支持或更新的浏览器,目前有:<code>IE 10</code>、<code>IE_Mob 11</code>、<code>BlackBerry 10</code>、<code>BlackBerry 7</code>、<code>Samsung 4</code>和<code>OperaMobile 12.1</code></li><li><p><code>last 2 versions</code>:每个浏览器的最后两个版本</p><ul><li><code>last 2 Chrome versions</code>:最后两个版本的Chrome浏览器</li><li><code>last 2 major versions</code>或<code>last 2 iOS major versions</code>:前两个主要版本的所有次要/补丁版本</li></ul></li><li><p><code>node 10</code>和<code>node 10.4</code>选择最新的node.js<code>10.x.x</code>或<code>10.4.x</code>版本</p><ul><li><code>current node</code>:现在Browserslist使用的Node.js版本</li><li><code>maintained node versions</code>:所有Node.js版本,仍然由<a href="https://link.segmentfault.com/?enc=RPjDdMiAFJaQQlJGPOp%2BoA%3D%3D.%2BXxi8tfOSP07ds0ulHsmYRqPK4xBAmp1OKj%2Fxu67Ylik7QWHfXH5RXU5%2FZ56JLMG" rel="nofollow">Node.js Foundation</a>维护</li></ul></li><li><p><code>iOS 7</code>直接使用iOS浏览器7版</p><ul><li><code>Firefox > 20</code>:Firefox的更新版本超过20个,<code>>=</code>、<code><</code>和<code><=</code>处理,也可以使用node.js</li><li><code>ie 6-8</code>:选择包含的版本范围</li><li><code>Firefox ESR</code>:最新的[Firefox ESR]版本</li><li><code>PhantomJS 2.1</code>和<code>PhantomJS 1.9</code>:选择类似于PhantomJS运行时的Safari版本</li></ul></li><li><code>extends browserslist-config-mycompany</code>:从<code>browserslist-config-mycompany</code> npm包中获取查询</li><li><code>supports es6-module</code>:支持特定功能的浏览器。<code>es6-module</code>下面是可以使用页面的URL feat参数。所有可用特性的列表可以在<a href="https://link.segmentfault.com/?enc=eBfi82sjYBi4CWYh5cxU4Q%3D%3D.I8zQtRnRa4qK7MwdAZkD%2Bd4N%2Fp1%2BVhPXyxzc7b3OP3L%2Bh7z85FGKvWCnd8BwVmDfiy%2FZcS8ev7MuWB14ybBlq5sZYyP5fAV%2BgHx9gUTAt38%3D" rel="nofollow">caniuse-lite/data/features</a>中找到</li><li><code>since 2015</code>或<code>last 2 years</code>:所有版本均于2015年发布(<code>since 2015-03</code>和<code>since 2015-03-10</code>)。未发布的版本或未发布的Chrome版本:alpha和beta版本</li><li><code>unreleased versions</code>或<code>unreleased Chrome versions</code>:alpha和beta版本</li><li><code>not ie <= 8</code>:排除先前查询所选择的浏览器</li></ul><p>您可以将<code>not</code>添加到任何查询中。</p><h2>七、调试</h2><p>在项目目录中运行<code>npx browserslist</code>,查看您的查询选择了哪些浏览器</p><pre><code class="cmd">$ npx browserslist
and_chr 61
and_ff 56
and_qq 1.2
and_uc 11.4
android 56
baidu 7.12
bb 10
chrome 62
edge 16
firefox 56
ios_saf 11
opera 48
safari 11
samsung 5</code></pre><h2>八、浏览器</h2><ul><li>Android: Android WebView</li><li>Baidu:百度浏览器</li><li>BlackBerry or bb:黑莓浏览器</li><li>Chrome:谷歌浏览器</li><li>ChromeAndroid or and_chr:谷歌android浏览器</li><li>Edge:IE Edge.</li><li>Electron for Electron framework:将被转换为Chrome版本</li><li>Explorer or ie:IE浏览器.</li><li>ExplorerMobile or ie_mob:IE手机浏览器</li><li>Firefox or ff:火狐浏览器</li><li>FirefoxAndroid or and_ff:火狐android浏览器</li><li>iOS or ios_saf:IOS safari浏览器</li><li>Node</li><li>Opera:Opera浏览器</li><li>OperaMini or op_mini:Opera Mini浏览器</li><li>OperaMobile or op_mob:Opera 手机浏览器</li><li>QQAndroid or and_qq:QQ android浏览器</li><li>Safari:桌面safari浏览器</li><li>Samsung:三星浏览器</li><li>UCAndroid or and_uc:UC浏览器或UC android浏览器</li><li>kaios:KaiOS浏览器</li></ul><h2>九、配置文件</h2><h3>1、package.json</h3><pre><code class="json">{
"private": true,
"dependencies": {
"autoprefixer": "^6.5.4"
},
"browserslist": [
"last 1 version",
"> 1%",
"IE 10"
]
}</code></pre><h3>2、.browserslistrc</h3><pre><code class="cmd"># Browsers that we support
last 1 version
> 1%
IE 10 # sorry</code></pre><p>Browserslist会检查<code>path</code>中每个目录中的配置。你可以把配置放到根目录,<code>app/</code>或<code>app/styles</code>,如:<code>app/styles/main.css</code></p><p>可以在BROWSERSLIST_CONFIG环境变量中指定直接路径</p><h2>十、共享配置</h2><p>你可以使用下面的查询来引用从另一个包中导出的浏览器列表配置:</p><pre><code class="json">"browserslist": [
"extends browserslist-config-mycompany"
]</code></pre><p>出于安全原因,外部配置只支持带有<code>browserslist-config-</code>前缀的包。通过使用<code>@scope/browserslist-config</code>作为模块的命名或前缀,例如<code>@scope/browserslist-config</code>或<code>@scope/browserslist-config-mycompany</code>,也支持<code>npm</code>作用域包。</p><p>如果你不接受用户的Browserslist查询,也可以使用<code>BROWSERSLIST_DANGEROUS_EXTEND</code>环境变量或<code>dangerousExtend</code>选项来禁用验证。</p><pre><code class="cmd">BROWSERSLIST_DANGEROUS_EXTEND=1 npx webpack</code></pre><p>也可以在包中引用特定的文件:</p><pre><code class="json">"browserslist": [
"extends browserslist-config-mycompany/desktop",
"extends browserslist-config-mycompany/mobile"
]</code></pre><p>当编写共享的Browserslist包时,只需导出一个数组<code>browserslist-config-mycompany/index.js</code>:</p><pre><code class="json">module.exports = [
'last 1 version',
'> 1%',
'ie 10'
]</code></pre><p>还可以包含<code>browserslist-stats.json</code>文件作为你的共享配置的一部分在根和查询使用<code>> 5% in browserslist-config-mycompany stats</code>。它使用相同的格式<code>extends</code>和<code>dangerousExtend</code>属性如上。</p><p>您可以为不同的环境导出配置,并通过<code>BROWSERSLIST_ENV</code>或在工具中的<code>env</code>选项来选择环境:</p><pre><code>module.exports = {
development: [
'last 1 version'
],
production: [
'last 1 version',
'> 1%',
'ie 10'
]
}</code></pre><h2>十一、针对不同环境进行配置</h2><p>您还可以为各种环境指定不同的浏览器查询。<code>BROWSERSLIST_ENV</code>或<code>NODE_ENV</code>变量将选择查询。如果没有一个被声明,Browserslist会首先查找产品查询,然后使用默认值。</p><p>package.json:</p><pre><code>"browserslist": {
"production": [
"> 1%",
"ie 10"
],
"modern": [
"last 1 chrome version",
"last 1 firefox version"
],
"ssr": [
"node 12"
]
}</code></pre><p>.browserslistrc:</p><pre><code class="cmd">[production]
> 1%
ie 10
[modern]
last 1 chrome version
last 1 firefox version
[ssr]
node 12</code></pre><h2>十二、自定义使用数据</h2><p>如果你有一个网站,你可以查询网站的使用统计数据。<a href="https://link.segmentfault.com/?enc=zOwU5c3A8MwiQRjx%2F8N3QA%3D%3D.xzNtq1nv6YQTSC9W1q8RHsNk52uNb0y0nW%2BQpWV6bHgaHTEBZBEJtOsAUJhyvS1L" rel="nofollow">browserslist-ga</a>会询问访问谷歌分析,然后生成<code>browserslist-stats.json</code>:</p><pre><code class="cmd">npx browserslist-ga</code></pre><p>或者使用<a href="https://link.segmentfault.com/?enc=CfIN1xYhHbBZA9djxQzGCg%3D%3D.X4EUQlgdHfCPcW%2FmB6fi045v46o3OkXePxytj6nmcr3xY70txhXM0RLghaoXyxHZppAXJVCLq%2BAaOT3RhFm9Fw%3D%3D" rel="nofollow">browserslist-ga-export</a>来转换谷歌分析数据,而无需提供谷歌帐户的密码。</p><p>您可以通过任何其他方法生成使用统计信息文件。文件格式应如下:</p><pre><code class="json">{
"ie": {
"6": 0.01,
"7": 0.4,
"8": 1.5
},
"chrome": {
…
},
…
}</code></pre><p>注意,您可以查询自定义使用数据,同时还可以查询全局或区域数据。例如,查询<code>> 1% in my stats, > 5% in US, 10%</code>是允许的。</p><h2>十三、JS API</h2><pre><code>const browserslist = require('browserslist')
// Your CSS/JS build tool code
function process (source, opts) {
const browsers = browserslist(opts.overrideBrowserslist, {
stats: opts.stats,
path: opts.file,
env: opts.env
})
// Your code to add features for selected browsers
}</code></pre><p>查询可以是一个字符串<code>"> 1%, IE 10"</code>或一个数组<code>['> 1%', 'IE 10']</code></p><p>如果查询丢失,Browserslist会查找配置文件。您可以提供一个路径选项(可以是一个文件)来查找与配置文件相对的配置文件。</p><p>参数项:</p><ul><li><code>path</code>:文件或目录路径,以查找配置文件。默认是:<code>./</code></li><li><code>env</code>:从配置中使用什么环境。默认生产</li><li><code>stats</code>:自定义使用统计数据</li><li><code>config</code>:配置文件路径</li><li><code>ignoreUnknownVersions</code>:忽略未知版本。默认false</li><li><code>dangerousExtend</code>:禁用扩展查询的安全检查。默认false</li><li><code>mobileToDesktop</code>:没有手机端使用桌面端。默认false</li></ul><p>对于<code>non-JS</code>环境和调试目的,你可以使用CLI工具:</p><pre><code class="cmd">> browserslist "> 1%, IE 10"</code></pre><p>您可以通过JS API获得所选浏览器的用户覆盖率:</p><pre><code>browserslist.coverage(browserslist('> 1%'))
//=> 81.4</code></pre><pre><code>browserslist.coverage(browserslist('> 1% in US'), 'US')
//=> 83.1</code></pre><pre><code>browserslist.coverage(browserslist('> 1% in my stats'), 'my stats')
//=> 83.1</code></pre><pre><code>browserslist.coverage(browserslist('> 1% in my stats', { stats }), stats)
//=> 82.2</code></pre><p>通过命令行CLI:</p><pre><code class="cmd">$ browserslist --coverage "> 1%"
These browsers account for 81.4% of all users globally</code></pre><pre><code class="cmd">$ browserslist --coverage=US "> 1% in US"
These browsers account for 83.1% of all users in the US</code></pre><pre><code class="cmd">$ browserslist --coverage "> 1% in my stats"
These browsers account for 83.1% of all users in custom statistics</code></pre><pre><code class="cmd">$ browserslist --coverage "> 1% in my stats" --stats=./stats.json
These browsers account for 83.1% of all users in custom statistics</code></pre><h2>十四、环境变量</h2><p>可以用<a href="https://link.segmentfault.com/?enc=QCtDkOH2%2BPy8IQaQECj5pw%3D%3D.24fBVYda89VTDZ4yCZawhtvi47vs2KK4RbKG2dgf1TLpIC2Ub2k%2BvVkoypXgHoG5bBs%2BvOYhuXbLXn8VFFTvpQ%3D%3D" rel="nofollow">环境变量</a>来改变浏览器列表的设置</p><p>BROWSERSLIST</p><pre><code class="text">BROWSERSLIST="> 5%" npx webpack</code></pre><p>BROWSERSLIST_CONFIG 配置文件</p><pre><code class="text">BROWSERSLIST_CONFIG=./config/browserslist npx webpack</code></pre><p>BROWSERSLIST_ENV 环境变量</p><pre><code class="text">BROWSERSLIST_ENV="development" npx webpack</code></pre><p>BROWSERSLIST_STATS 自定义使用数据</p><pre><code class="text">BROWSERSLIST_STATS=./config/usage_data.json npx webpack</code></pre><p>BROWSERSLIST_DISABLE_CACHE 禁用缓存</p><pre><code class="text">BROWSERSLIST_DISABLE_CACHE=1 npx webpack</code></pre><p>BROWSERSLIST_DANGEROUS_EXTEND 禁用安全性可共享配置名检查</p><pre><code class="text">BROWSERSLIST_DANGEROUS_EXTEND=1 npx webpack</code></pre><h2>十五、缓存</h2><p>Browserslist从<code>package.json</code>和<code>browserslist</code>文件中读取缓存配置</p><p>也可以清楚缓存,示例:</p><pre><code>browserslist.clearCaches()</code></pre><p>要完全禁用缓存,请设置BROWSERSLIST_DISABLE_CACHE环境变量。</p><p>欢迎关注:<a href="https://link.segmentfault.com/?enc=N1740UkYDOc5g6OO5gUBZQ%3D%3D.zw7INcMEujEI7pNSr15Xyre251kl0R7RbBB6sLwffP2Cg56My2CwfxgoLeXfK2HyB%2BFmkcbkGCdY0HDNUt47Lg%3D%3D" rel="nofollow">技术开发分享录</a></p><p><img src="/img/remote/1460000039035283" alt="" title=""></p>
nginx location匹配规则
https://segmentfault.com/a/1190000023943726
2020-09-09T09:12:18+08:00
2020-09-09T09:12:18+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p><strong>语法:</strong></p><pre><code class="nginx">location [=|~|~*|^~] /uri/ {
# ...
}</code></pre><p><strong>规则:</strong></p><ul><li>/ 开头表示通用匹配(任何请求都会匹配到)</li><li>= 开头表示精确匹配</li><li>^~ 开头表示uri以某个常规字符串开头(如url路径)</li><li>~ 开头表示区分大小写</li><li>~* 开头表示不区分大小写</li><li>!~ 开头表示区分大小写不匹配</li><li>!~* 开头表示不区分大小写不匹配</li></ul><p><strong>优先级:</strong></p><p>首先精确匹配 = -> 其次以xx开头匹配^~ -> 然后是按文件中顺序的正则匹配 -> 最后是交给 / 通用匹配。</p><p>当有匹配成功时候,停止匹配,按当前匹配规则处理请求。</p><p><strong>示例:</strong></p><pre><code class="nginx">location = / {
#规则1
}
location = /user {
#规则2
}
location ^~ /static/ {
#规则3
}
location ~ \.(gif|jpg|png|js|css)$ {
#规则4,注意:是根据括号内的大小写进行匹配。括号内全是小写,只匹配小写
}
location ~* \.png$ {
#规则5
}
location !~ \.html$ {
#规则6
}
location !~* \.html$ {
#规则7
}
location / {
#规则8
}</code></pre><p>欢迎关注:<a href="https://link.segmentfault.com/?enc=uFUz%2Bi6vHgJisSzHm%2BLAoQ%3D%3D.OIv4CW5VgvOwQaTrFLMhFwzM3TI15WEHy0%2FZOvSFXsw%3D" rel="nofollow">http://fenxianglu.cn/</a></p>
QQ浏览器video标签不显示第一帧解决
https://segmentfault.com/a/1190000023929884
2020-09-08T10:24:07+08:00
2020-09-08T10:24:07+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p>因为在QQ和微信上打开网址链接都是用QQ浏览器打开的,但QQ浏览器做了很多限制还存在一些问题,导致一些功能不能正常使用,就像下面这个video标签一样显示不出来第一帧</p><p><img src="/img/bVbMzo6" alt="image.png" title="image.png"></p><h2>一、尝试前端解决</h2><p>视屏加载完成,获取第一帧作为poster海报</p><blockquote>当浏览器预计能够在不停下来进行缓冲的情况下持续播放指定的音频/视频时,会发生 canplaythrough 事件。</blockquote><pre><code class="html"><video controls width="270" src="./images/movie.mp4"></video>
<script type="text/javascript">
$('video').each(function() {
this.addEventListener('canplaythrough', function() {
setTimeout(function() {
var canvas = document.createElement('canvas');
canvas.width = this.videoWidth;
canvas.height = this.videoHeight;
canvas.getContext('2d').drawImage(this, 0, 0, canvas.width, canvas.height);
this.setAttribute('poster', canvas.toDataURL("image/png"));
// $('body').append(`<img src="${canvas.toDataURL("image/png")}" />`);
}.bind(this), 1000);
}.bind(this));
});
</script></code></pre><p>可惜的是QQ浏览器对canvas的toDataURL方法支持不够好,直接输出<code>data:,</code>就没了</p><h2>二、后端解决(推荐)</h2><p>后端可以劫持到上传的视频,然后对视屏进行截取,不过这又要增加后端的工作量了,还好现在都使用了云存储(OSS),<br>以阿里云为示例,截取上传视屏</p><p>阿里云视频截帧API文档:<a href="https://link.segmentfault.com/?enc=TzyA8UnEUYK%2BXHuCgbfSSA%3D%3D.0rDmOqT%2Fd1Bu0F2X5yHMYpDb5UypuAV7Eh%2BtDr4%2BYvMO5ItsbvWKuKcAkpPDEG3rvzY1dC1kAgesoKe%2FrCOROjSRuTk68g7IwunVLh4zlZnHJ%2BEYfYMRNw2q7v%2FKEJfn" rel="nofollow">https://help.aliyun.com/docum...</a></p><p>语法:</p><pre><code class="text"><原视频URL>?x-oss-process=video/snapshot,t_7000,f_jpg,w_800,h_600,m_fast</code></pre><p>需要注意的是这里的<strong>截取宽高指定0</strong>就行了,自动计算视屏宽高为截取图片的宽高</p><p>欢迎关注:<a href="https://link.segmentfault.com/?enc=x3hGwqhFmewCHVtPFKu%2Bjw%3D%3D.7QgMaT3c9Y6Fj7ij8O8A7EeE7%2BiokIOHpbx97MzXaos%3D" rel="nofollow">http://fenxianglu.cn/</a></p>
webpack externals配置方式介绍
https://segmentfault.com/a/1190000023914151
2020-09-07T09:38:38+08:00
2020-09-07T09:38:38+08:00
anchovy
https://segmentfault.com/u/anchovy
1
<p>webpack 中的 externals 配置提供了<strong>不从 bundle 中引用依赖</strong>的方式。简单理解就是不通过npm下载的类库,在html文件中以script引入,然后在页面中使用import导入的这种方式</p><p>以vue开发环境为示例</p><p>在public/index.html中引入moment.min.js类库</p><pre><code class="html"><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
<script src="https://cdn.bootcdn.net/ajax/libs/moment.js/1.0.0/moment.min.js"></script>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html></code></pre><p>通过externals配置,就可以在.vue文件中使用import引入</p><h2>一、数组方式</h2><p>vue.config.js</p><pre><code class="js">module.exports = {
configureWebpack: {
externals: ['moment'] // moment是全局变量名
}
}</code></pre><p>App.vue</p><pre><code class="html"><script>
import moment from 'moment '
</script></code></pre><h2>二、对象方式</h2><p>vue.config.js</p><pre><code class="js">module.exports = {
configureWebpack: {
externals: {
mnt: 'moment' // moment是全局变量名
}
}
}</code></pre><p>App.vue</p><pre><code class="html"><script>
import moment from 'mnt'
</script></code></pre><p>其他</p><pre><code class="js">externals : {
lodash : {
commonjs: "lodash",
amd: "lodash",
root: "_" // 指向全局变量
}
}</code></pre><p>这里lodash 这个外部 library 可以在 AMD 和 CommonJS 模块系统中通过 lodash 访问,但在全局变量形式下用 _ 访问。</p><h2>三、正则方式</h2><p>vue.config.js</p><pre><code class="js">module.exports = {
configureWebpack: {
externals: /^(moment)$/i // moment是全局变量名
}
}</code></pre><p>App.vue</p><pre><code class="html"><script>
import moment from 'moment '
</script></code></pre><h2>四、函数方式</h2><p>vue.config.js</p><pre><code class="js">module.exports = {
configureWebpack: {
externals: [
function(context, request, callback) {
if (/^moment$/.test(request)) { // moment是全局变量名
return callback(null, 'es6 ' + request);
}
callback();
}
]
}
}</code></pre><p>App.vue</p><pre><code class="html"><script>
import moment from 'moment '
</script></code></pre><p>欢迎关注:<a href="https://link.segmentfault.com/?enc=yRXVEz6tTPpt9cr%2B0CrEZA%3D%3D.LdUvYQKwGDxYvAwJpKiUy7G4JuQG3rmzvQKBZ8mSOdk%3D" rel="nofollow">http://fenxianglu.cn/</a></p>
vuepress搭建个人博客
https://segmentfault.com/a/1190000023724532
2020-08-22T18:00:01+08:00
2020-08-22T18:00:01+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p>Vuepress中文网:<a href="https://link.segmentfault.com/?enc=np7Qvf%2BNGWBNCO2FpzLrjA%3D%3D.xLelfkS3y1yrm3hhvQ3jQx9f8Ui8f%2BgWsPnaRpkoDoc%3D" rel="nofollow">https://www.vuepress.cn/</a><br>Vuepress官网:<a href="https://link.segmentfault.com/?enc=xpFEMymCay3N4JOV8GFS5g%3D%3D.3Wb2Hkn5PKPPpmzJevTh6WwGIp4hPFT0siqAAoMr8qY%3D" rel="nofollow">https://vuepress.vuejs.org/</a></p><blockquote>Vuepress 是 Vue 驱动的静态网站生成器</blockquote><p>可以用来搭建github主页,示例:<a href="https://link.segmentfault.com/?enc=v7MiO7WZLhBH9UZ2HnHhJQ%3D%3D.eZcgiNXW0aaqbJUdeaVZPSlywkBwMgv8C5mwWurD9cu%2F7KzQj4VyR%2FbZmeMUy9na" rel="nofollow">https://zhangqh22.github.io/f...</a></p><h2>一、起步</h2><p><strong>1、安装</strong></p><pre><code class="cli">> npm install -g vuepress</code></pre><p><img src="/img/remote/1460000023724535" alt="image.png" title="image.png"></p><p><strong>2、创建项目</strong></p><p>手动创建以下目录结构</p><pre><code class="dir">├─ vuepress-test
├─ docs
├─ ├─ README.md
└─ package.json</code></pre><p>README.md内容</p><pre><code># Hello Vuepress</code></pre><p>package.json内容</p><pre><code>{
"scripts": {
"docs:dev": "vuepress dev docs",
"docs:build": "vuepress build docs"
}
}</code></pre><p><strong>3、运行项目</strong></p><pre><code class="cli">vuepress-test> npm run docs:dev
VuePress dev server listening at http://localhost:8080/</code></pre><p>访问:<a href="https://link.segmentfault.com/?enc=izeAN4qVM%2BX2NvENZXKVHQ%3D%3D.at6qvYkFwnQ7yJqnS1ylM0vZa%2BOWQxVwVLzWZ8qgqxs%3D" rel="nofollow">http://localhost</a>:8080/ 会看到Hello Vuepress</p><h2>二、目录结构介绍</h2><p>传送门:<a href="https://link.segmentfault.com/?enc=gCaot8lVrWZ7JW3MOQcwqw%3D%3D.v3wwBjFYqSJFcLZqMC7Dg2%2BMktL77EKgZC2bg%2FsKPHOcXhx9otRq9ImHZ%2BP8jVKB7zeWKPiQhWiG%2FieBnqcHLw%3D%3D" rel="nofollow">https://www.vuepress.cn/guide...</a></p><h2>三、示例代码介绍</h2><p>npm run docs:dev 启动服务,运行效果</p><p><img src="/images/articles/image20200622111254.png" alt="image.png" title="image.png"></p><p>目录结构(部分)</p><pre><code class="dir">├─ QZHG
├─ docs
├─ ├─ .vuepress
├─ ├─ ├─ public # 公共输入目录
├─ ├─ ├─ ├─ images
├─ ├─ ├─ qzhg # 打包输出目录
├─ ├─ ├─ styles
├─ ├─ ├─ ├─ index.styl # 样式文件
├─ ├─ ├─ config.js # 配置文件
├─ ├─ ├─ enhanceApp.js # 增强应用
├─ ├─ en # 英文
├─ ├─ ├─ guide
├─ ├─ ├─ ├─ README.md
├─ ├─ guide # 引入页
├─ ├─ ├─ README.md
├─ ├─ pages # 页面
├─ ├─ ├─ share
├─ ├─ ├─ ├─ images
├─ ├─ ├─ ├─ data_view.md
├─ ├─ ├─ temp.md
├─ node_modules
├─ package.json
└─ README.md</code></pre><p>package.json</p><pre><code class="json">{
"scripts": {
"docs:dev": "vuepress dev docs", // npm run docs:dev 启动
"docs:build": "vuepress build docs" // npm run docs:build 构建
},
"license": "ISC"
}</code></pre><p>.vuepress/config.js</p><pre><code>module.exports = {
host: '0.0.0.0',
base: '/qzhg/',
dest: 'docs/.vuepress/qzhg',
head: [
['link', { rel: 'icon', type: "image/png", href: '/images/favicon.png' }] // 头部网址图标
],
locales: { // 语言配置
'/': {
lang: 'zh-CN',
title: '前瞻后顾',
description: '✍',
},
'/en/': {
lang: 'en-US',
title: 'qzhg',
description: '✍',
}
},
themeConfig: { // 主题配置
locales: {
'/': {
selectText: '选择语言',
label: '简体中文',
sidebar: [
{
title: '技术',
collapsable: true,
children: [
'/pages/technology/browser_works',
'/pages/technology/browser_render',
'/pages/technology/reg_exp',
'/pages/technology/interview_question',
'/pages/technology/npm',
]
},
{
title: '分享',
collapsable: true,
children: [
'/pages/share/xiecheng',
'/pages/share/data_view',
'/pages/share/vue_typescript',
]
},
{
title: '待学习',
collapsable: true,
children: [
'/pages/using_where/collection'
]
},
],
sidebarDepth: 2,
nav: [
// {text: '引导', link: '/guide/'},
]
},
'/en/': {
selectText: 'Languages',
label: 'English',
sidebar: [
'/en/guide/',
],
sidebarDepth: 2,
nav: [
{text: 'guide', link: '/en/guide/'},
]
}
}
}
}</code></pre><p>.vuepress/enhanceApp.js(增强应用)</p><pre><code>import './styles/index.styl'
// import './styles/palette.styl'
export default ({
Vue,
options,
router,
siteData
})=> {
// ...
}</code></pre><p>README.md(入口文件)</p><pre><code class="md">---
home: true
heroImage: /images/hero.png
actionText: Go →
actionLink: /guide/
footer: MIT Licensed | Copyright © 2018-present zhangqh22
---</code></pre><blockquote>vuepress开箱即用 <a href="https://link.segmentfault.com/?enc=3oeP7NU1SsbaMlCMs6%2FIbg%3D%3D.%2BTI7otPEL3byXFZDocYmo6XO4X%2B5dDcSDo6iyATss8WcPVLnpSOE1STGxyl2FvIY" rel="nofollow">Front Matter</a> 配置格式<br>markdown语法参考:<a href="https://link.segmentfault.com/?enc=JK%2BKNDjQws8v3UsNjLTrOQ%3D%3D.Q1x4neYMBm4O%2BkJMqlnjqx%2FAPkYZ1dFeTUUFPt%2F2gxJtwcGJqOEpNdiYFhyrXD2m" rel="nofollow">https://www.vuepress.cn/guide/markdown.html</a></blockquote><p>打包</p><pre><code class="cli">> npm run docs:build</code></pre><p>把.vuepress/qzhg目录放到服务器上即可,这个单面应用需要后端配合处理一下</p><h2>四、vuepress主题</h2><ul><li>vuepress-theme-reco:<a href="https://link.segmentfault.com/?enc=wLVfX1KqJEGcGWWMsr%2Bx%2FA%3D%3D.qtdsGSEcvbgXFSrFFO9GJxkbywSPkrDQhkT%2FXPj2Q0MicMCjzK3VysAK9rZkyona" rel="nofollow">https://vuepress-theme-reco.r...</a></li><li>AntDocs:<a href="https://link.segmentfault.com/?enc=SpZ4%2FEVgqno6Qs%2FSrio42g%3D%3D.YZtRkE2JkwoGorFV%2BQfAkVDH8NINIct7PZCE0waMhr4%3D" rel="nofollow">https://antdocs.seeyoz.cn/</a></li></ul><p>欢迎关注:<a href="https://link.segmentfault.com/?enc=LWr7sVOlnYakE7FEQZGrcQ%3D%3D.f80Fdqr5IEDNOeH%2FO6ZK8kI3eurMINAC8AzH7vA00UE%3D" rel="nofollow">http://fenxianglu.cn/</a></p>
vue总结
https://segmentfault.com/a/1190000022713405
2020-05-22T10:32:29+08:00
2020-05-22T10:32:29+08:00
bigtooth
https://segmentfault.com/u/bigtooth
0
<h2>vue</h2>
<p>vue是一套用于构建用户界面的渐进式框架(渐进式框架的大概意思就是你可以只用我的一部分,而不是用了我这一点就必须用我的所有部分)</p>
<h2>一、 v-if和v-show的区别</h2>
<p>v-show=false,元素存是display:none<br>v-if=false,元素不存在<br><img src="/img/remote/1460000022713410" alt="image.png" title="image.png"></p>
<h2>二、v-for使用key</h2>
<p>key是为了添加唯一标识,让Diff算法可以正确识别节点并把它插入正确的位置,提高Vue对虚拟DOM的更新效率。<br>比如:<br><img src="/img/remote/1460000022713411" alt="image.png" title="image.png"><br>无key,逐条更新值<br><img src="/img/remote/1460000022713409" alt="image.png" title="image.png"><br>有key,只需要增加个F节点,插入到B和C之间<br><img src="/img/remote/1460000022713408" alt="有key" title="有key"></p>
<h2>三、v-if不能和v-for不能放在同一标签上</h2>
<p>v-for优先级高于v-if,当v-for和v-if放在同一标签上时,会先执行v-for,再执行v-if,这样会导致不符合v-if条件的元素也会被v-for<br>如:</p>
<pre><code><div id="app">
<ul>
<li v-for="value in testData">
<div id="test" v-if="Array.isArray(value)" v-for="item in value">{{item}}</div>
<div id="test1" v-else>{{value}}</div>
</li>
</ul>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
testData: {
name: 'TESTOBJECT',
id: 5,
data: [1.67, 1.33, 0.98, 2.21]
}
}
})
</script></code></pre>
<p>打印结果:<code>TEST,TEST,TEST,TEST,3,3,3,1.67,1.33,0.98,2.21</code><br>TEST打印<code>4</code>次,是因为字符大小是<code>4</code>;3显示<code>3</code>次,是数字大小<br>上例是这个结果的原因:</p>
<ul>
<li>
<code>value</code>为<code>TEST</code>时,v-for先遍历TEST,结果为T,E,S,T(4次);执行Array.isArray(TEST) == false,执行v-else,所以显示4次TEST</li>
<li>
<code>value</code>为<code>3</code>时,v-for遍布3,结果为:1,2,3(3次);执行Array.isArray(3) == false;执行v-else,结果为3</li>
<li>
<code>value</code>为 [1.67, 1.33, 0.98, 2.21],遍历数组,Array.isArray([1.67, 1.33, 0.98, 2.21]) == true;结果是每一次的值</li>
</ul>
<h2>四、命令注意</h2>
<p>vue组件名和prop都可以用驼峰命名和短横线命名,</p>
<p>但html里面只能使用短横线。<br>(js中的驼峰命名会自动转换成短横线命名。)</p>
<p>字符串模板只能用驼峰。</p>
<h2>五、组件之间通信</h2>
<p>(1)父子组件之间通信</p>
<ul><li>父组件:通过v-bind动态绑定父组件传来的内容(count-val,html里要用短横线分隔命名法)</li></ul>
<p><code><Count :count-val="count" @change="changeFn"/></code></p>
<ul><li>子组件:通过props接收父组件传过来的值</li></ul>
<p><code>props: ['countVal']</code><br>父组件prop变化,子组件prop会跟着变,但是子组件变化,不会影响到父组件</p>
<ul><li>子组件通过&dollar;emit(事件名, 传递参数)触发父组件的自定义事件</li></ul>
<p><code><Count :count-val="count" @change="changeFn"/></code> 此处change就是自定义事件<br><code>this.$emit('change',this.count);</code>合作$emit触发自定义事件change<br>App.vue<br>(2)通过ref通信,</p>
<ul>
<li>ref在子组件上使用,相当于子组件的索引,通过$refs可以调用子组件的属性和方法</li>
<li>ref在普通的 DOM 元素上使用,通过$refs可以获取DOM元素的集体,作用与jquery选择器类似</li>
</ul>
<p>(3)非父子组件间的通信<br>main.js中</p>
<pre><code>new Vue({
render: h => h(App),
data: {
eventHub: new Vue() // 给data加个vue实例
}
}).$mount('#app')</code></pre>
<p>组件1</p>
<pre><code>methods: {
changeFn(count) {
console.log('count', count);
this.count = count;
}
},
mounted: function() {
// 绑定
this.$root.eventHub.$on('changeEvent', (count) => {
this.changeFn(count);
});
},
beforeDestory: function() {
// 解除绑定,避免内存泄漏
this.$root.eventHub.$off('changeEvent');
}</code></pre>
<p>组件2<br><code>this.$root.eventHub.$emit('changeEvent', this.count);</code></p>
<h2>六、vue响应式</h2>
<h5>1.双向绑定</h5>
<p>采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。就必须要实现以下几点:<br>1、实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者<br>2、实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数<br><img src="/img/remote/1460000022713413" alt="image.png" title="image.png"></p>
<p>3、实现一个Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图<br>4、mvvm入口函数,整合以上三者<br><img src="/img/remote/1460000022713412" alt="image.png" title="image.png"></p>
<blockquote><a href="https://link.segmentfault.com/?enc=26SJ4q9GGjwONGyOEsc40A%3D%3D.isBoLHEyqy5lIE3gKvdDQMMXHkkes%2BAAR7avdDlWiFCK8Z2ZlIlDfB0sR19s7sus" rel="nofollow">Vue2.0双向绑定原理</a></blockquote>
<h5>2.数组变异方法</h5>
<h5>3.存在的问题</h5>
<p>深度监听,如果对象很大的话,一次性监听到底<br>无法监听新增、删除属性(可以通过Vue.set,Vue.delete实现监听),这个问题在vue3中通过proxy解决</p>
<h2>七、diff算法</h2>
<p>虚拟dom就是用js模拟dom结构<br>diff算法<br>patch</p>
<ul>
<li>oldVnode是真实dom,创建空的vnode,关联dom元素</li>
<li>新不存在,旧存在,删除旧</li>
<li>新存在,旧不存在,新增vnode</li>
<li>新旧都存在,且是相同节点,调用patchVnode</li>
</ul>
<p>patchVnode</p>
<ul>
<li>新旧相同,return</li>
<li>新旧不同,但都是文本节点,替换文本</li>
<li>新有子节点,旧没有,新增子节点</li>
<li>旧有子节点,新没有,删除子节点</li>
<li>新旧都有子节点,且子节点不同,执行updateChildren</li>
</ul>
<p>updateChildren<br>开始是递增,结束是递减<br>新开始->旧开始<br>新结束->旧结束<br>新开始->旧结束<br>新结束->旧结束<br>有key就根据key,更新到指定位置<br>没有key就直接更新<br>如果旧开始>旧结束,说明旧先遍历完,新增vnode中新开始->新结束之间的节点<br>如果新开始>新结束,说明新先遍历完,移除oldVnode中旧开始->旧结束之间的节点</p>
<h2>八、computed和watch</h2>
<ul>
<li>computed有缓存,值不变不重新计算</li>
<li>watch</li>
</ul>
<p>watch不支持缓存,有数据变化直接触发<br>watch监听引用类型,拿不到oldVal</p>
<h2>九、vue样式穿透</h2>
<p>在组件作用域内的 CSS中,修改外部引入插件样式,需要用到样式穿透</p>
<ul>
<li>scss 使用 ::v-deep</li>
<li>stylus使用>>></li>
<li>sass和less使用/deep/</li>
</ul>
<pre><code><style lang="scss" scoped>
// scss
.wrapper::v-deep .el-input__inner{
padding-right: 5px!important;
}
// stylus
.wrapper >>> .el-input__inner{
padding-right: 5px!important;
}
// sass和less
.wrapper /deep/.el-input__inner{
padding-right: 5px!important;
}
<style></code></pre>
<h2>十、 vue生命周期</h2>
<ul>
<li>
<strong>beforecreate ->created</strong><br>初始化事件,进行数据的观测</li>
<li><strong>created</strong></li>
</ul>
<p>data和method已经初始化好了,如果调用method和data最早可以在这里调用</p>
<ul><li><strong>created -> beforeMount</strong></li></ul>
<p>el选项(el: '#app')<br>没有el选项,则停止编译,也就意味着停止了生命周期,直到在该vue实例上调用vm.$mount(el)。此时生命周期到created就停止了<br>有的话就继续向下编译</p>
<ul><li><strong>beforeMount</strong></li></ul>
<p>模板已经常在内存中编译好了,还未挂载到页面,页面还是模板语法</p>
<ul><li><strong>beforeMount -> mounted</strong></li></ul>
<p>添加$el,并将其挂载到DOM元素,在beforeMount之前,打印this.$el是undefined</p>
<ul><li><strong>mounted</strong></li></ul>
<p>页面渲染完成,最早操作dom,DOM中的模板语法替换成真实数据</p>
<ul><li><strong>beforeupdate</strong></li></ul>
<p>数据变化触发</p>
<ul><li><strong>updated</strong></li></ul>
<p>页面数据已经被更新</p>
<ul><li><strong>beforeDestory</strong></li></ul>
<p>vue实例进入销毁阶段,data,methods还都可用,还没有被销毁</p>
<ul><li><strong>destoryed</strong></li></ul>
<p>组件已经被销毁了</p>
<ul><li>父子组件生命周期执行顺序(加载渲染过各)</li></ul>
<p><code>父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted</code></p>
<ul><li>更新过程</li></ul>
<p><code>父beforeUpdate->子beforeUpdate->子updated->父updated</code></p>
<ul><li>销毁过程</li></ul>
<p><code>父beforeDestroy->子beforeDestroy->子destroyed->父destroyed</code></p>
<h2>十一、 data为什么用函数</h2>
<ul>
<li>不用function return每个组件的data都在内存的同一个地址,那一个data改变,就会影响其他组件的data</li>
<li>用function return相当于新建了个对象,分配新的内存地址,这样每个组件之间的data都是相互独立</li>
</ul>
<h2>十二、$event</h2>
<ul>
<li>原生 的event对象</li>
<li>事件被挂载到当前元素</li>
</ul>
<p>event.target在什么地方监听<br>event.currentTarget在什么地方触发的</p>
<h2>十三、v-model</h2>
<p>textarea,checkbox,radio也是用v-model</p>
SVG实现圆环进度
https://segmentfault.com/a/1190000022701532
2020-05-21T09:31:02+08:00
2020-05-21T09:31:02+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p>传送门:<a href="https://link.segmentfault.com/?enc=%2FH1P08uYDwSe5lzDOTmbmA%3D%3D.KoC1jtK6oAHNdpklC9GsGiElxLGYfATTZAzlhUQH4FNoYPMy9SY5AKRPX9rEacvM" rel="nofollow">http://fenxianglu.cn/article/202</a></p>
<p><img src="/img/remote/1460000022701535" alt="" title=""></p>
jquery $.when配合$.def处理多个请求
https://segmentfault.com/a/1190000022691270
2020-05-20T09:42:17+08:00
2020-05-20T09:42:17+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p>传送门:<a href="https://link.segmentfault.com/?enc=q%2FttzdlkhUxEqkLWsOdicg%3D%3D.atA4B6HWMZ%2BXGDZNG%2B853QZ9tj%2FMxNlHBKiX3nCXX7ZEnvW4yFbostSXpoykCJKl" rel="nofollow">http://fenxianglu.cn/article/201</a></p>
前端开发常用工具插件
https://segmentfault.com/a/1190000022637752
2020-05-14T09:49:43+08:00
2020-05-14T09:49:43+08:00
anchovy
https://segmentfault.com/u/anchovy
2
<h2>一、浏览器</h2>
<h3>1、chrome</h3>
<ul>
<li>JSON Viewer</li>
<li>Allow CORS</li>
<li>Axure(原型查看)</li>
<li>Vue devtools</li>
<li>Redux DevTools</li>
<li>React Developer Tools</li>
<li>React Perf(React性能监测工具)</li>
</ul>
<h2>二、编辑器</h2>
<h3>1、Sublime Text3</h3>
<blockquote>轻量级的开发工具,有强大的插件支持</blockquote>
<ul>
<li>Emmet</li>
<li>AutoFileName</li>
<li>Babel</li>
<li>HTML-CSS-JS Prettify</li>
<li>Local History</li>
<li>SFTP</li>
<li>TypeScript Syntex</li>
<li>Vue Syntex Highlight</li>
<li>LessImproved</li>
<li>LESS</li>
<li>Sass</li>
<li>MaterialTheme</li>
<li>CodeFormatter(php格式化)</li>
</ul>
<h3>2、VS Code</h3>
<blockquote>前端开发神器(vue/react/angular/typescript/小程序/公众号等)</blockquote>
<ul>
<li>Beautify css/sass/scss/less</li>
<li>Local History</li>
<li>One Dark Pro</li>
<li>Vetur(.vue文件格式化)</li>
<li>超越鼓励师</li>
<li>Live Server本地服务(可实时刷新)</li>
<li>Prettier+(5星 格式化样式)</li>
<li>Prettier - JavaScript formatter(5星 格式化脚本)</li>
<li>Power Mode(5星)</li>
<li>vscode-proto3(5星)</li>
</ul>
<h3>3、Atom</h3>
<blockquote>git开发的一个比较强大的IDE</blockquote>
<ul>
<li>activate-power-mode</li>
<li>emmet</li>
<li>local-history</li>
<li>minimap</li>
<li>autocomplete-paths</li>
<li>remote-ftp</li>
</ul>
<h3>4、HBuilderX</h3>
<blockquote>对vue友好,集小程序开发功能</blockquote>
<p><a href="https://link.segmentfault.com/?enc=9NKeP8sjZkvZ%2Fd0Fsj1Hvw%3D%3D.zVvrO2gEJcuUyzC51m4WoraykDgDO5Jz9YyBcbvGykeohhqEMq%2FTHzLy3o%2BHBFl8" rel="nofollow">https://www.dcloud.io/hbuilde...</a></p>
<h3>5、Typora</h3>
<blockquote>markdown编辑器,有强大的导出功能</blockquote>
<p><a href="https://link.segmentfault.com/?enc=GpKfZ5fFge4oM7MF%2BjdoLg%3D%3D.aXU%2BiM7lAm3lMbcm9sg6wuCj2upJy2%2BdY9vJCw%2BISPw%3D" rel="nofollow">https://www.typora.io/</a></p>
<h3>5、IntelliJ IDEA</h3>
<blockquote>java web开发神器</blockquote>
<ul>
<li>Material Theme UI</li>
<li>Rainbow Brackets(功能区域彩虹括号)</li>
<li>Codota(智能自动补全)</li>
<li>Alibaba Java Code Guideline(阿里编码规范)</li>
<li>CodeGlance(mini视图)</li>
<li>Restful Tookit(Restful接口必备)</li>
<li>CamelCase(快速切换字符串连接格式,如hello_world -> hello-world 或 helloWorld)</li>
</ul>
<p>参考链接:</p>
<ul><li><a href="https://segmentfault.com/a/1190000022552122">https://segmentfault.com/a/11...</a></li></ul>
<h2>三、FTP工具</h2>
<ul>
<li>MobaXterm:<a href="https://link.segmentfault.com/?enc=wtQxAvcZSEmgivyVxCWusg%3D%3D.CR6P7QkU3j9THggmYa%2FMlaRJ0Ye8DrIaO8NgXi84Zro%3D" rel="nofollow">https://mobaxterm.mobatek.net/</a>
</li>
<li>XShell:<a href="https://link.segmentfault.com/?enc=NwkA%2FbkORD7yb5UVDW1VcQ%3D%3D.ca2MwOWMjdRTZ2zjZPcp7ERgIpBiscqJjJow9eFEWT0%3D" rel="nofollow">https://xshell.en.softonic.com/</a>
</li>
</ul>
<h2>四、接口代理工具</h2>
<ul>
<li>Insomnia:<a href="https://link.segmentfault.com/?enc=cLlcxpxJYpKkPfwoQfUNdg%3D%3D.qoxeSNaUFXpq1qbkkKMGCjep%2FP%2BAJwhNMF1zfCNWtf8%3D" rel="nofollow">https://insomnia.rest/</a>
</li>
<li>postman:<a href="https://link.segmentfault.com/?enc=m2IaL8egSgFXtkn7LQEBpw%3D%3D.dct%2B1lQFWv57Z7wwj%2FtFlUHQrDZDiYi%2FPKbq9fWFgKM%3D" rel="nofollow">https://www.postman.com/</a>
</li>
</ul>
<h2>五、其他</h2>
<ul>
<li>在线文件对比:<a href="https://link.segmentfault.com/?enc=7lJChWiRWYFAnYmNc665%2Fg%3D%3D.gF2ot7g41FJ1ptz%2B2WPja%2F7cVsghv2A1vEHMxGDaBQOopJ8whBoGVFByQ4gosnbd" rel="nofollow">http://www.jq22.com/textDiffe...</a>
</li>
<li>beyond compare文件对比:链接:<a href="https://link.segmentfault.com/?enc=o1uoCCF%2Bii%2BCzqX%2FDfJcpw%3D%3D.lfaAa6SaRx23xxztBoPGK2FR5ydH3%2BxPvYgTFDaLlErNKAzCu0S3zZAMfRzyZA1i" rel="nofollow">https://pan.baidu.com/s/1Z93K...</a> 提取码:u61m</li>
</ul>
<p>最新内容请看这里:<a href="https://link.segmentfault.com/?enc=xFp0PqZefzTli5l%2BoJrhow%3D%3D.FxdyZ3GcH%2BYjh7OZQY4qx%2Ba8WaYUusVUCFRoFrtMc51N1e8veWwSVMPuqYFU8O%2Bm" rel="nofollow">http://fenxianglu.cn/noteDeta...</a></p>
gulp顺序执行+监听执行
https://segmentfault.com/a/1190000022611647
2020-05-11T20:55:25+08:00
2020-05-11T20:55:25+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p>原文链接:<a href="https://link.segmentfault.com/?enc=pSDXLRlXkhYQDBRC6X%2BGEA%3D%3D.R7QpMpOcFbh0avQuPzbtihnDs8bpFRwMbTetjup4XjsBFjNK5TXptfCVKNqsC1FU" rel="nofollow">http://fenxianglu.cn/article/187</a></p>
<p>由于老项目的原因,维护的时候不得不重拾gulp架手架功能,不过在处理的过程中总是出现各种不可思议的结果,在网上查找了很多资料调试都不行,最终还是因为多了一个return的原因导致在watch的时候总是只执行一次,不过还好找到并解决了,根本原因还是对gulp脚本架不够熟悉啊。</p>
<p>下面把相应的依赖包和gulp.js内容贴出来仅供参考。</p>
<p>package.json:</p>
<pre><code class="json">{
"devDependencies": {
"@babel/core": "^7.4.4",
"@babel/preset-env": "^7.4.4",
"chalk": "^2.4.2",
"gulp": "^3.9.1",
"gulp-babel": "^8.0.0",
"gulp-chinese2unicode": "^1.0.1",
"gulp-clean": "^0.4.0",
"gulp-replace": "^1.0.0",
"gulp-sequence": "^1.0.0",
"gulp-uglify": "^3.0.0"
}
}</code></pre>
<p>gulp.js</p>
<pre><code>const gulp = require('gulp');
const babel = require('gulp-babel');
const chinese2unicode = require('gulp-chinese2unicode'); // 中文转unicode
const clean = require('gulp-clean'); // 请理文件
const uglify = require('gulp-uglify'); // js压缩
const gulpSequence = require('gulp-sequence'); // 同步
const chalk = require('chalk'); // 粉笔
gulp.task('clean', function() {
return gulp.src('./public/dist', {read: false, allowEmpty: true}).pipe(clean({force: true})); // 这里要return不然会并行
});
gulp.task('js', function() {
gulp.src('./public/js/**/*.js') // 这里不能return,不然只会执行一次
.pipe(babel({
presets: ['@babel/preset-env']
}))
.pipe(uglify({mangle: true, compress: false}))
.pipe(chinese2unicode())
.on('error', function (err) {
console.log(err.toString());
})
.pipe(gulp.dest('./public/dist/js'))
});
gulp.task('build', function(callback) {
gulpSequence('clean', 'js', callback);
});
// 监听js变动
gulp.task('watch', function () {
// 方式1:
// var watcher = gulp.watch('./public/js/**/*.js', function() {
// gulp.run('build');
// });
// 方式2:
gulp.watch('./public/js/**/*.js', ['build']);
});</code></pre>
<p>命令行:</p>
<pre><code class="cli">> gulp watch</code></pre>
vue style穿透选择器
https://segmentfault.com/a/1190000022580769
2020-05-08T15:01:45+08:00
2020-05-08T15:01:45+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p>原文链接:<a href="https://link.segmentfault.com/?enc=eaxEzfbZka0mzREjvKyjoA%3D%3D.%2FEtcVB%2BFBGRtOP5iTInVq5W81D7OU1XaAnjHyW6cDvzNNLms66lfouSfawLeO5DL" rel="nofollow">http://fenxianglu.cn/article/183</a></p>
<p>如果要在scope作用域下修改外部样式,这里就需要穿透(深度)选择器</p>
<h2>一、CSS使用>>></h2>
<pre><code class="html"><style scoped>
.list >>> .item {
padding: 8px 0;
}
</style></code></pre>
<h2>二、less使用 /deep/</h2>
<pre><code class="html"><style lang="less" scoped>
/deep/ .list .item {
padding: 8px 0;
}
</style></code></pre>
<h2>三、scss使用::v-deep</h2>
<pre><code class="html"><style lang="scss" scoped>
::v-deep .list .item {
padding: 8px 0;
}
</style></code></pre>
<p>参考链接:</p>
<ul>
<li>官方说明:<a href="https://link.segmentfault.com/?enc=oiM%2Fdi4LH6w%2FqVAD3QqM6Q%3D%3D.VBtsctvi3CQu18mZobV3IWSMuicNJuMhfULSH6HRfdt6jowP6oJvD4iV4KIg4OM8Xe%2FfOAC71BKuqaIGSK9KjV6Lu0lcMMXGrGa46ppIdu0%3D" rel="nofollow">https://vue-loader.vuejs.org/...</a>
</li>
<li>官方说明(中文):<a href="https://link.segmentfault.com/?enc=SgBYlNSJWPRoh%2BuHQvaIWQ%3D%3D.qu7P5T6G1dmXP7qoBQUvTwWk6s3IpEQre%2FVWxIgruYBMe9Sg6f8SF%2BuqjA%2BBmlwUt0ZvuuzXkFR4iJvCXX99HbG9gzIxEV%2FIzZqpRk5iuiTDx6fVHUcReQkCfGbfZ4JU44OigrJ7uHcbUxiaIgjfvXaSy71hR1DSMq81104K2Ec%3D" rel="nofollow">https://vue-loader.vuejs.org/...</a>
</li>
</ul>
数据库数据递归拼装节点
https://segmentfault.com/a/1190000022570139
2020-05-07T16:58:36+08:00
2020-05-07T16:58:36+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p>原文链接:<a href="https://link.segmentfault.com/?enc=kNIMPJ5z9ZjCDONp%2BRIZHA%3D%3D.FAVz72frryrsEmK8XZMyL%2FvogTIFreWS35p2pbnjar7WJqm1aBj3%2B8TN2%2BadKLj1" rel="nofollow">http://fenxianglu.cn/noteDeta...</a></p>
<h2>一、拼装li节点</h2>
<p><img src="/img/remote/1460000022570142" alt="image.png" title="image.png"></p>
<p>数据</p>
<pre><code>var list = [
{ id: 1, pid: 0, name: '名称1' },
{ id: 2, pid: 1, name: '名称2' },
{ id: 3, pid: 2, name: '名称3' },
{ id: 4, pid: 2, name: '名称4' }
];</code></pre>
<p>递归函数</p>
<pre><code>function recursiveList(list, id) {
let html = '<ul>';
list.forEach(function(v) {
if(v.pid == id) {
html += '<li><span class="zh-name" data-id="'+ v.id +'">'+ v.name +'</span>'+ recursiveList(list, v.id) +'</ul></li>';
}
});
return html;
}</code></pre>
<p>调用(去除空的<ul></ul>标签,补上结束标签</ul>)</p>
<pre><code>console.log(recursiveList(list, 0).replace(/<ul><\/ul>/g, '') + '</ul>');</code></pre>
<p>结果</p>
<pre><code class="html"><ul>
<li>
<span class="zh-name">名称1</span>
<ul>
<li>
<span class="zh-name">名称2</span>
<ul>
<li>
<span class="zh-name">名称3</span></li>
<li>
<span class="zh-name">名称4</span></li>
</ul>
</li>
</ul>
</li>
</ul></code></pre>
<h2>二、拼装option节点</h2>
<p><img src="/img/remote/1460000022570143" alt="image.png" title="image.png"></p>
<p>数据</p>
<pre><code>var list = [
{ id: 1, pid: 0, name: "名称1" }
{ id: 2, pid: 1, name: "名称2" }
{ id: 3, pid: 2, name: "名称3" }
{ id: 4, pid: 2, name: "名称4" }
{ id: 5, pid: 1, name: "名称5" }
{ id: 6, pid: 0, name: "名称6" }
];</code></pre>
<p>递归</p>
<pre><code>function recursiveGenerateSectionNode(optionArr, list, id, indent) {
list.forEach(function(v) {
if(v.pid == id) {
optionArr.push('<option value="'+ v.id +'">'+ indent + v.name +'</option>');
recursiveGenerateSectionNode(optionArr, list, v.id, indent+' ┠ ');
}
});
return optionArr;
}</code></pre>
<p>调用</p>
<pre><code>var optionArr = [];
recursiveGenerateSectionNode(optionArr, list, 0, ' ┠ ');
console.log('<option value="0">顶级</option>' + optionArr.join(''));</code></pre>
<p>结果</p>
<pre><code class="html"><option value="0">顶级</option>
<option value="1"> ┠ 名称1</option>
<option value="2"> ┠ ┠ 名称2</option>
<option value="3"> ┠ ┠ ┠ 名称3</option>
<option value="4"> ┠ ┠ ┠ 名称4</option>
<option value="5"> ┠ ┠ 名称5</option>
<option value="6"> ┠ 名称6</option></code></pre>
返回上一页触发刷新
https://segmentfault.com/a/1190000022507340
2020-04-29T15:01:16+08:00
2020-04-29T15:01:16+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p>原文链接:<a href="https://link.segmentfault.com/?enc=evcjjbJQeauGVnHa4p35dQ%3D%3D.MmcdsHFmPx3iYAdvGpNWAmcG9IA5gJElLpej68kStd5MfY4uyXLLiGz5NWb5ETu0" rel="nofollow">http://fenxianglu.cn/noteDeta...</a></p>
<p>要知道history.back返回上一页是不会触发刷新的,这是浏览器的机制,但我们可以做些兼容处理,代码如下:</p>
<p>进入页面后,先把referrer保存到sessionStorage里</p>
<pre><code>window.addEventListener('DOMContentLoaded', function() {
// 保存referrer
if(location.href !== document.referrer) {
sessionStorage.setItem('referrer', document.referrer);
}
});</code></pre>
<p>在操作后调用下面代码返回上一页</p>
<pre><code>if(location.href !== document.referrer) {
location.href = document.referrer;
} else {
if(sessionStorage.getItem('referrer')) {
location.href = sessionStorage.getItem('referrer');
} else {
history.back();
}
}</code></pre>
<blockquote>注意:使用document.referrer时,不能设置meta头referrer为no-referrer,不然拿不到referrer数据</blockquote>
百度统计代码未生效解决
https://segmentfault.com/a/1190000022457659
2020-04-24T16:46:44+08:00
2020-04-24T16:46:44+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p>原文地址:<a href="https://link.segmentfault.com/?enc=43gXAqA0DyZFV22zxSYfXA%3D%3D.5uo05YUQSbvrOK3JVphbIOb7AkH1A1BJG3krpfMsdHgnewruLF01y3VRJ5%2BB6o0s" rel="nofollow">http://fenxianglu.cn/noteDetail/171</a></p>
<p>如果遇到代码未生效,首先看代码是否放到</head>结束标签前,如果代码放置没有问题</p>
<p>那么再检查下有没有以下的meta头</p>
<pre><code class="html"><meta name="referrer" content="no-referrer"></code></pre>
<p>如果有这个meta头的话,则会导致hm.js下载没有内容,也就是<strong>网站主页</strong>检查结果不生效,如果你切到<strong>其他网页</strong>检查会发现是生效的,但并没有什么卵用,因为据客服反馈是无法正确发出pv请求的,所以还是要解决</p>
<p>当然解决统计的方式有很多,这里讲三种方式:</p>
<h2>一、换统计平台</h2>
<p><a href="https://link.segmentfault.com/?enc=YMsL%2FusqSqyw9IVVXaNwLg%3D%3D.2coBrpsvjYX6EFXLjA5H%2Fqt05KyKZXJcIXc5USpcyN8%3D" rel="nofollow">CNZZ</a>统计不受no-referrer影响,如果想网站继续保持meta no-referrer设置,则放弃百度统计,选择使用CNZZ统计</p>
<h2>二、修改或删除meta no-referrer设置</h2>
<h2>三、删除meta no-referrer设置,使用其他方式</h2>
<p>可以使用referrer策略的标签有:a、img、iframe、link</p>
<p>使用方式有三种,以a标签为例:</p>
<p>方式1:</p>
<pre><code class="html"><a href="http://example.com" referrer="noreferrer">xxx</a></code></pre>
<p>方式2(推荐):</p>
<pre><code class="html"><a href="http://example.com" referrerpolicy="noreferrer">xxx</a></code></pre>
<p>方式3(推荐):</p>
<pre><code class="html"><a href="http://example.com" rel="noreferrer">xxx</a></code></pre>
<p>亲测修改img标签使用方式2有效,a标签使用方式3有效。</p>
vue项目优化
https://segmentfault.com/a/1190000022255569
2020-04-03T15:20:05+08:00
2020-04-03T15:20:05+08:00
anchovy
https://segmentfault.com/u/anchovy
1
<h2>一、组件按需加载</h2>
<pre><code>component: () => import('@/components/login')</code></pre>
<h2>二、大数据处理</h2>
<p>vxe-table(功能齐全):<a href="https://link.segmentfault.com/?enc=4ZFgV4wyfp8Vth0TkoOpLw%3D%3D.o6lN%2FEcTZexVTS6us17Drw7VCIBbTnbxpwSkCT8Gd16%2B7CwzjLmB456wpF5uZs1dgrUdaHIVLxNgEZ2Xv2%2BXSw%3D%3D" rel="nofollow">https://xuliangzhan_admin.git...</a></p>
<pre><code class="html"><vxe-table :data="tableData">
<vxe-table-column type="index" title="序号" width="80"></vxe-table-column>
<vxe-table-column field="name" title="名字"></vxe-table-column>
<vxe-table-column field="sex" title="性别"></vxe-table-column>
<vxe-table-column field="address" title="地址"></vxe-table-column>
</vxe-table></code></pre>
<h2>三、使用keep-alive</h2>
<p><keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。</p>
<pre><code class="html"><keep-alive>
<router-view/>
</keep-alive></code></pre>
<h2>四、图片懒加载</h2>
<p>安装地址:<a href="https://link.segmentfault.com/?enc=aplK71C78D%2BCpXBkf8J23A%3D%3D.q7KrHEmqmUlO7yYmd3LStf%2BGoEdZT55LBQuEO8wxHdIXmacmfO9HN1jiwsWWXryI" rel="nofollow">https://www.npmjs.com/package...</a></p>
<p>main.js</p>
<pre><code>import Vue from 'vue'
import App from './App.vue'
import VueLazyload from 'vue-lazyload'
Vue.use(VueLazyload)
// or with options
Vue.use(VueLazyload, {
preLoad: 1.3,
error: 'dist/error.png',
loading: 'dist/loading.gif',
attempt: 1
})
new Vue({
el: 'body',
components: {
App
}
})</code></pre>
<p>template</p>
<pre><code class="html"><ul>
<li v-for="img in list">
<img v-lazy="img.src" >
</li>
</ul></code></pre>
<h2>五、清除定时器</h2>
<p>组件中写在setTimeout、setInterval在不用的时候清除掉,清除方式请参考:<a href="https://link.segmentfault.com/?enc=dul8eDcfKWCIljAZdq0pQA%3D%3D.wGc5eAAof6gfRfHEdvUmqcLcg5W0KJP0QhowM5gG7DLwNwM%2BaKi%2Flu9hylw4GG7k" rel="nofollow">http://www.techshare100.com/a...</a></p>
<h2>六、vue3.x打包优化</h2>
<p>请看这里:<a href="https://link.segmentfault.com/?enc=4NGnljDjeXmWfqHKagopDA%3D%3D.z2FDTvtizN9dwbDx73I70c0ncwhTR1B%2FbaUa3m2mZ6C42NigOLLyuX5%2BHY%2BhGmdv" rel="nofollow">http://www.fenxianglu.cn/arti...</a></p>
<p>写到最后,欢迎关注作者:<a href="https://link.segmentfault.com/?enc=LZ3J7FJC9f1GwK0LvqTvKQ%3D%3D.yDcAKiGUHEGScykvIZKvv6WUyjvF8sBup3BlJULJd9s%3D" rel="nofollow">http://www.fenxianglu.cn/</a></p>
linux nodejs安装
https://segmentfault.com/a/1190000022224249
2020-04-01T10:19:03+08:00
2020-04-01T10:19:03+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<h2>一、下载安装</h2>
<p>到nodejs下载页(<a href="https://link.segmentfault.com/?enc=DV70KCdXntFySQV4G4TWeA%3D%3D.tcvHhmOqZ3AxW9M8c0k53XQDD0ReC1nTiaMEyqDYrq8Bb2bctDDfr8mAlDGNrmCi" rel="nofollow">https://nodejs.org/en/download/releases/</a>),找到对应的版本下载.gz文件下载</p>
<p>如:选择node-v10.15.3-linux-x64.tar.gz 版本下载</p>
<p>下载后连接sftp把压缩文件拖到服务器里,如:/opt/download/ 目录下</p>
<p><img src="https://upload-images.jianshu.io/upload_images/2114039-059f7a6aaf887b3b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image.png" title="image.png"></p>
<p>然后进入shell,切换到down目录执行解压命令,将其解压到/opt/software/目录下</p>
<pre><code class="cli">[download]# tar xvf node-v10.15.3-linux-x64.tar.gz -C ../software/</code></pre>
<h2>二、建立软链接</h2>
<p>这个类似于windows里的环境变量,设置后可以全局使用,软链接目标文件都是放在/usr/local/bin目录下的</p>
<blockquote>ln -s [源文件] [目标文件]</blockquote>
<p>查看软链接(一般软链接是绿色文字标识)</p>
<pre><code class="cli">[bin]# ls -L
cnpm node npm npx pm2</code></pre>
<p>删除软链接</p>
<pre><code class="cli">[bin]# rm -rf node
[bin]# rm -rf npm</code></pre>
<p>重新建立软链接</p>
<pre><code class="cli">[/]# ln -s /opt/software/node-v10.15.3-linux-x64/bin/node /usr/local/bin/node
[/]# ln -s /opt/software/node-v10.15.3-linux-x64/bin/npm /usr/local/bin/npm</code></pre>
<p>这时运行node命令就是新安装的这个版本了</p>
<pre><code>[/]# node -v
v10.15.3</code></pre>
<p>写到最后,欢迎关注作者:<a href="https://link.segmentfault.com/?enc=vG6d43F5uMXG7vXOIWjYjQ%3D%3D.AoAkPWdUkscevI2l%2BjAlBEKmlQZo5ezCKNkcu6Z0sWM%3D" rel="nofollow">http://www.fenxianglu.cn/</a></p>
前端面试题
https://segmentfault.com/a/1190000021553911
2020-01-11T11:59:28+08:00
2020-01-11T11:59:28+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p><img src="/img/bVbCBjH" alt="image.png" title="image.png"></p>
<p>传送门:<a href="https://link.segmentfault.com/?enc=CSik7v9BJwbk0g2FnVkYDg%3D%3D.FvSZO0HUHuw4IxH5IbrIPpEJgrEDpjNH88Vdpt%2FZRTE%3D" rel="nofollow">http://fenxianglu.cn/interview</a></p>
vue router动态追加路由
https://segmentfault.com/a/1190000021533523
2020-01-09T11:07:33+08:00
2020-01-09T11:07:33+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p>路由分为静态路由,如事先写好的,公共的路由,如:404、login等<br>还有一部分是根据权限接口返回,动态追加的一些页面模块路由,如:user</p>
<p>在处理权限的时候,可以先允许一些不需要权限认证的路由,如下:</p>
<pre><code>// 开放的路由,可以直接访问
const ALLOW_ROUTES = [
'/login',
'/buy',
// ...
];</code></pre>
<p>具体路由拦截全在<strong>router.beforeEach</strong>里处理</p>
<p>首先如果没有登录,直接跳转登录页</p>
<pre><code>if (!ALLOW_ROUTES.includes(to.path) && !isLogin) { // 未登录
next({
path: '/login',
replace: true
});
return;
}</code></pre>
<p>如果已登录,使用<strong>router.addRoutes</strong>追加路由</p>
<pre><code>if(isLogin && (sessionStorage.getItem('curPath') || to.matched[0].path === '*') ) {
sessionStorage.removeItem('curPath');
// 还原路由(防止路由重复)
router.matcher = new VueRouter({
// mode: 'history',
routes
}).matcher;
router.addRoutes(routes: Array<RouteConfig>); // 追加路由
// util.generateMenu(); // 生成菜单,视具体情况而定
next({
path: to.path
});
return;
}</code></pre>
<p>动态添加更多的路由规则。参数必须是一个符合 routes 选项要求的数组。</p>
<p>curPath用于辅助处理</p>
<pre><code>router.afterEach(to => {
// 保存路径
if (to.path !== '/login') {
sessionStorage.setItem('curPath', JSON.stringify({
path: to.path
}));
}
})</code></pre>
<p>动态路由主要是根据业务来的,不好写成一个公共的处理方式,具体在什么时候追加路由,要自己去判断,如:刷新、退出登录、重新登录、没有匹配到等操作是否满足</p>
<p>如果还遇到路由重复的问题,可能是在当前页push同样路由造成的,这里可以先判断一下</p>
<pre><code>if(router.currentRoute.name !== 'login') {
router.push({
name: 'login'
})
}</code></pre>
<p>写到最后,欢迎关注作者:<a href="https://link.segmentfault.com/?enc=2Wl6aazWnwr2iFRaCTe5Yw%3D%3D.Ak%2BFHncw4%2FYcrZV2B2OUqeW1sN25I4Tpt6WhecXE%2BVY%3D" rel="nofollow">http://www.techshare100.com/</a></p>
vue引入图片问题
https://segmentfault.com/a/1190000021485662
2020-01-04T10:46:23+08:00
2020-01-04T10:46:23+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<h2>一、img标签</h2>
<pre><code class="html"><img src="~@/views/weather_radar/images/img-map-satellite.png" alt=""></code></pre>
<h2>二、scss文件</h2>
<p>通过在vue文件引入scss样式文件</p>
<pre><code class="html"><style lang="scss">
@import "./scss/index.scss";
</style></code></pre>
<p>如果scss文件有引入背景图片,这里<strong>不能使用相对路径</strong>了,需要<strong>使用~开头</strong>的方式,如下:</p>
<pre><code class="scss">.box {
background: url(~@/views/system/image/login_bg.jpg) 0 0 no-repeat #297de1;
}</code></pre>
<blockquote>VUE CLI中的解释:如果 URL 以 ~ 开头,其后的任何内容都会作为一个模块请求被解析。</blockquote>
<p><a href="https://link.segmentfault.com/?enc=UzbFKe81xysBUDE2l83wiQ%3D%3D.XTiJefxfZ6GOvc8zNpluIQdauqvFf3eutSZtbhz0SI3YN1RCCmEfFPHyZRqD8JqKWomjY2Ho6A4nVkCV5ECPZXnV0rF2RuyEXNs%2BVTG67aFfeX3EmyWoggrHQcUkcV7EPCz4nxIYtkD5xsZFQwy4aJo9yVy8qMGKdj6zK8QPLHc%3D" rel="nofollow">VUE CLI静态资源引入</a></p>
<p>不然改成以下也是可以的</p>
<pre><code class="html"><style lang="scss">
// 使用相对路径引入静态图片是可以的
</style></code></pre>
<p>写到最后,欢迎关注作者:<a href="https://link.segmentfault.com/?enc=oUyQqfmRQmlx9zJy2tsGww%3D%3D.7o8Co0jZqSOnoFGfe5W7eJ1McFlqkGK1gEjaxYWlZZM%3D" rel="nofollow">http://www.techshare100.com/</a></p>
git常用命令
https://segmentfault.com/a/1190000021407952
2019-12-26T13:58:16+08:00
2019-12-26T13:58:16+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p>git文档:<a href="https://link.segmentfault.com/?enc=eCwzUvINh%2FqYiUVmCG6GGw%3D%3D.emiUACkLNN72S7FqasLj1ro0mko9Fay2wduY0tfuL5Y%3D" rel="nofollow">https://git-scm.com/book/zh/v2/</a></p>
<p>根据当前分支检出新分支</p>
<pre><code class="cli">> git checkout -b [分支名]</code></pre>
<p>根据某次提交创建新分支</p>
<pre><code class="cli">> git checkout [commit id] -b [分支名]</code></pre>
<p>把本地分支push到远程</p>
<pre><code class="cli">> git push origin [分支名]</code></pre>
<p>修改提交注释</p>
<pre><code class="cli">> git commit --amend -m [描述]</code></pre>
<p>撤销本地修改</p>
<pre><code class="cli">> git checkout -- [文件或目录]</code></pre>
<p>撤销add到暂存区的文件或目录</p>
<pre><code class="cli">> git reset HEAD [文件或目录]</code></pre>
<p>撤销上一次的commit提交</p>
<pre><code class="cli">> git reset HEAD~</code></pre>
<p>回退到某个历史版本</p>
<pre><code class="cli">> git reset --hard 139dcfaa558e3276b30b6b2e5cbbb9c00bbdca96 </code></pre>
<p>查看某个文件的日志</p>
<pre><code class="cli">> git log [文件名]</code></pre>
<p>查看某个作者的日志</p>
<pre><code class="cli">> git log --author=zhangqinghu --stat</code></pre>
<p>查看具体修改详情</p>
<pre><code class="cli">> git show <git提交版本号> <文件名></code></pre>
<p>强制提交</p>
<pre><code class="cli">> git push -f origin [分支名]</code></pre>
<p>清除所有新建的文件及文件夹</p>
<pre><code class="cli">> git clean -df</code></pre>
<p>暂存(在拉取的时候,如果需要先提交,可以先暂存再拉取)</p>
<pre><code class="cli">> git stash</code></pre>
<p>取出暂存</p>
<pre><code class="cli">> git stash pop</code></pre>
<p>清除缓存</p>
<pre><code class="cli">> git rm -r --cached .
> git add .gitignore
> git commit -m 'update .gitignore'</code></pre>
<p>写到最后,推广下一个不错的分享平台:<a href="https://link.segmentfault.com/?enc=kaVdKXKUlhzhZX39P8BhCQ%3D%3D.y8uhAr3KAi5SZ014XF3rcQcaTaSTG%2BH9ui%2BYh79hnyA%3D" rel="nofollow">http://www.techshare100.com/</a>,欢迎大家加入</p>
express之jwt使用
https://segmentfault.com/a/1190000021235208
2019-12-09T14:10:41+08:00
2019-12-09T14:10:41+08:00
anchovy
https://segmentfault.com/u/anchovy
3
<p>JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案。</p><p><strong>架构图:</strong></p><p><img src="https://upload-images.jianshu.io/upload_images/3383598-c82676bb8445bae9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1200/format/webp" alt="https://upload-images.jianshu.io/upload_images/3383598-c82676bb8445bae9.png?imageMogr2/auto-orient/strip|imageView2/2/w/1200/format/webp" title="https://upload-images.jianshu.io/upload_images/3383598-c82676bb8445bae9.png?imageMogr2/auto-orient/strip|imageView2/2/w/1200/format/webp"></p><p><strong>特点:</strong></p><ul><li>JWT默认不加密,但可以加密。生成原始令牌后,可以使用改令牌再次对其进行加密。</li><li>当JWT未加密方法是,一些私密数据无法通过JWT传输。</li><li>JWT不仅可用于认证,还可用于信息交换。善用JWT有助于减少服务器请求数据库的次数。</li></ul><h2>配合express使用</h2><h3>1、安装依赖</h3><ul><li>jsonwebtoken(用户签名和验证):<a href="https://link.segmentfault.com/?enc=eukniGmT40rzaJLKTr1Rbg%3D%3D.2xbyISDkpvPhX6XSZUBcprWhG5Te2fotWWv15BLrdNUISAPotod2bddaf5QPpBPI" rel="nofollow">https://www.npmjs.com/package...</a></li><li>express-jwt(对jsonwebtoken的封装,能够更好的搭配express):<a href="https://link.segmentfault.com/?enc=99mcKValGTxhhXvqvxFmvw%3D%3D.ykk%2BYsh5sif%2B%2BESiD6HkfMP3%2BQyK3SHUZVEETC7vhgtO%2FBwi%2Be5on73W5PoUPVru" rel="nofollow">https://www.npmjs.com/package...</a></li></ul><pre><code>> npm install jsonwebtoken express-jwt -S</code></pre><h3>2、服务端生成token</h3><pre><code>var jwt = require('jsonwebtoken');
// 生成token
function generateToken() {
return jwt.sign({
foo: 'bar',
}, 'hahaha', {
expiresIn: '1d' // 1天 https://github.com/zeit/ms
});
}</code></pre><p>如登录完成后返回给前端</p><pre><code>router.post('/xxx/login', function(req, res, next) {
res.json({
status: true,
data: {
token: generateToken()
},
message: '登录成功!'
});
});</code></pre><h3>前端传输token</h3><p>拿到服务端返回的token后,这里要存储起来,然后在需要验证的接口上添加token,传输给服务端,headers传输示例如下:</p><pre><code>$.ajax({
headers: {
token: localStorage.getItem('token') // 通过headers传输token到服务端
},
// ...
});</code></pre><h3>服务端路由拦截</h3><pre><code>var jwt = require('jsonwebtoken');
router.use(function(req, res, next) {
if(req.headers.hasOwnProperty('token')) {
jwt.verify(req.headers.token, 'hahaha', function(err, decoded) {
if(err) {
res.json({
status: false,
message: 'token不存在或已过期'
});
} else {
next();
}
});
} else {
next();
}
});</code></pre><p>上面代码是纯使用jsonwebtoken方式处理的,从生成传输到验证一条龙。</p><h3>那么express-jwt是如何使用的呢?</h3><p>整体流程:</p><p><img src="/img/bVcKxRZ" alt="image.png" title="image.png"></p><p>服务端生成方式不变,不过前端的传输方式变成这样了,这里不再是token了,而是约定好的authorization了</p><pre><code>$.ajax({
headers: {
authorization: 'Bearer ' + localStorage.getItem('token') // "Bearer "这个也是约定的,必须是这样的格式
},
// ...
});</code></pre><p>服务端使用</p><pre><code>router.post('/xxx', expressJWT({secret: 'hahaha'}), function (req, res, next) {
// ...
});</code></pre><p>前端验证</p><pre><code>$.ajaxSetup({
complete: function (res) { // 接口请求完成拦截
// 验证token,不存在 重新获取或退出登录
}
});</code></pre><p>整体步骤不多,由于是封装好的,使用起来也比较方便。</p><p><strong>JWT缺点:</strong></p><ul><li>JWT的最大缺点是服务器不保存会话状态,所以在使用期间不可能取消令牌或更改令牌的权限。也就是说,一旦JWT签发,在有效期内将会一直有效。</li><li>JWT本身包含认证信息,因此一旦信息泄露,任何人都可以获得令牌的所有权限。为了减少盗用,JWT的有效期不宜设置太长。对于某些重要操作,用户在使用时应该每次都进行进行身份验证。</li></ul><p>为了减少盗用和窃取,JWT不建议使用HTTP协议来传输代码,而是使用加密的HTTPS协议进行传输。</p><p>欢迎关注:<a href="https://link.segmentfault.com/?enc=alE44l69B90HCb8JKFeaiw%3D%3D.EgOzySEUlzBM9mcqossWg6y35V0COpkLb4HBCuAKstg%3D" rel="nofollow">https://www.fenxianglu.cn/</a></p><p><img src="https://upload-images.jianshu.io/upload_images/2114039-cc664f2680505679.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image.png" title="image.png"></p>
linux下mysql去除严格模式
https://segmentfault.com/a/1190000021234911
2019-12-09T13:41:54+08:00
2019-12-09T13:41:54+08:00
anchovy
https://segmentfault.com/u/anchovy
0
<p>1、查找mysqld</p>
<pre><code class="sh">$ which mysqld
/usr/sbin/mysqld</code></pre>
<p>2、查找my.cnf位置</p>
<pre><code class="sh">$ /usr/sbin/mysqld --verbose --help | grep -A 1 'Default options'
/etc/my.cnf /etc/mysql/my.cnf /usr/etc/my.cnf ~/.my.cnf</code></pre>
<p>3、使用vim打开/etc/my.cnf</p>
<pre><code class="sh">$ vim /etc/my.cnf</code></pre>
<p>4、修改配置,把,STRICT_TRANS_TABLES这个删除就行了</p>
<pre><code class="sh">sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES</code></pre>
<p>如果没有,就直接添加</p>
<pre><code class="sh">sql_mode=NO_ENGINE_SUBSTITUTION</code></pre>
<p>5、重启mysql</p>
<pre><code class="sh">$ service mysqld restart</code></pre>
<p>修改后,如有些提示,像:</p>
<pre><code class="text">ERROR 1364 (HY000): Field 'name' doesn't have a default value</code></pre>
<p>就不会提示了。</p>