https://seviche.cc/ <![CDATA[Seviche.cc]]> https://seviche.cc/favicon.png 2022-08-13T12:05:10.902Z <![CDATA[Vue.js 挑战练习]]> https://seviche.cc/2022-08-12-vue-challenges 2022-08-12T00:00:00.000Z 2022-08-13T08:21:08.878Z 最近做了一下这个Vue.js 挑战,其中的题目大多出自Vue3 文档,都不是很难,但涉及到的知识点比较琐碎,用来复习挺好的。

然后这是我的答案和题目涉及到的知识点,除了鼠标指针这个部分没通过单元测试之外,其他都都通过了,然后这个鼠标指针为什么没通过单元测试我也没弄明白,试了下其他人的也通过不了,好奇怪……

这里省去部分题目,主要写答案。

Built-ins

DOM 传送门

Vue.js 提供了一个内置组件,将其插槽内容渲染到另一个 DOM,成为该 DOM 的一部分。

vue
<script setup>
const msg = 'Hello World'
</script>
<template>
<teleport to="body">
<span>{{ msg }}</span>
</teleport>
</template>

相关知识点 :Teleport | Vue.js

有时组件模板的一部分逻辑上属于该组件,而从技术角度来看,最好将模板的这一部分移动到 DOM 中 Vue app 之外的其他位置1

  • 有点像传送门,将相应元素渲染到制定位置
  • to 后面写 css selector

优化性能的指令

Vue.js 提供了一个指令,以便只渲染一次元素和组件,并且跳过以后的更新。

vue
<script setup>
import { ref } from 'vue'
const count = ref(0)
setInterval(() => {
count.value++
}, 1000)
</script>
<template>
<span v-once>使它从不更新: {{ count }}</span>
</template>

相关知识点:Vue-事件修饰符

CSS Features

动态 CSS

Vue 单文件组件 <style> 模块支持给 CSS 绑定动态值。

vue
<script setup>
import { ref } from 'vue'
const theme = ref('red')
const colors = ['blue', 'yellow', 'red', 'green']
setInterval(() => {
theme.value = colors[Math.floor(Math.random() * 4)]
}, 1000)
</script>
<template>
<p>hello</p>
</template>
<style scoped>
/* Modify the code to bind the dynamic color */
p {
color: v-bind(theme);
}
</style>

相关知识点:v-bind Dynamic Styling动态绑定样式

全局 CSS

给具有 CSS 作用域的 Vue 单文件组件设置全局 CSS 样式

vue
<template>
<p>Hello Vue.js</p>
</template>
<style scoped>
p {
font-size: 20px;
color: red;
text-align: center;
line-height: 50px;
}
/* Make it work */
:global(body) {
width: 100vw;
height: 100vh;
background-color: burlywood;
}
</style>

或者

vue
<template>
<p>Hello Vue.js</p>
</template>
<style scoped>
p {
font-size: 20px;
color: red;
text-align: center;
line-height: 50px;
}
</style>
<style>
/* Make it work */
body {
width: 100vw;
height: 100vh;
background-color: burlywood;
}
</style>

相关知识点:单文件组件 CSS 功能 | Vue.js

Components

DOM 传送门

见上面

Props 验证

验证 Button 组件的 Prop 类型 ,使它只接收: primary | ghost | dashed | link | text | default ,且默认值为 default

vue
<script setup>
import Button from './Button.vue'
defineProps({
type: {
type: String,
default: 'default',
validator: value => {
;['primary', 'ghost', 'dashed', 'link', 'text', 'default'].includes(value)
}
}
})
</script>
<template>
<Button type="dashed" />
</template>

相关知识点:Props | Vue.js

函数式组件

这题我不是很懂,翻了一下大家的解决方案,感觉这个比较能看懂:21 - functional component · Issue #322 · webfansplz/vuejs-challenges · GitHub

vue
<script setup lang="ts">
import { ref, h } from 'vue'
/**
* Implement a functional component :
* 1. Render the list elements (ul/li) with the list data
* 2. Change the list item text color to red when clicked.
*/
const ListComponent = (props, { emit }) =>
h(
// 创建 ul
'ul',
// 根据传入的props创建li
props.list.map((item: { name: string }, index: number) =>
h(
'li',
{
// 点击时处罚toggle。并将当前index作为参数传入toggle
onClick: () => emit('toggle', index),
// 将当前点击的li颜色设置为红色
style: index === props.activeIndex ? { color: 'red' } : null
},
// li 默认值
item.name
)
)
)
ListComponent.props = ['list', 'activeIndex']
ListComponent.emits = ['toggle']
const list = [
{
name: 'John'
},
{
name: 'Doe'
},
{
name: 'Smith'
}
]
const activeIndex = ref(0)
function toggle(index: number) {
activeIndex.value = index
}
</script>
<template>
<list-component :list="list" :active-index="activeIndex" @toggle="toggle" />
</template>

相关知识点:

渲染函数[h()]

使用 h 渲染函数来实现一个组件。

vue
import { defineComponent, h } from 'vue'; export default defineComponent({ name: 'MyButton', props: { disabled: { type: Boolean,
default: false, }, }, emits: ['custom-click'], setup(props, { emit, slots }) { return () => h( 'button', { disabled:
props.disabled, onClick: () => emit('custom-click'), }, slots.default?.() ); }, });

树组件

实现一个树组件

vue
<script setup lang="ts">
import { defineComponent } from 'vue'
interface TreeData {
key: string
title: string
children: TreeData[]
}
defineProps<{ data: TreeData[] }>()
</script>
<template>
<ul v-for="node in data">
<li>{{ node.title }}</li>
<template v-if="node.children && node.children.length">
// 用递归的方法来实现
<TreeComponent :data="node.children" />
</template>
</ul>
</template>

参考:

Composable Function

本节相关知识点:组合式函数 | Vue.js

切换器

尝试编写可组合函数

vue
<script setup lang="ts">
import { ref } from 'vue'
/**
* Implement a composable function that toggles the state
* Make the function work correctly
*/
function useToggle(init: boolean) {
const state = ref(init)
const toggle = () => (state.value = !state.value)
return [state, toggle]
}
const [state, toggle] = useToggle(false)
</script>
<template>
<p>State: {{ state ? 'ON' : 'OFF' }}</p>
<p @click="toggle">Toggle state</p>
</template>

计数器

实现一个计数器

vue
<script setup lang="ts">
import { ref } from 'vue'
interface UseCounterOptions {
min?: number
max?: number
}
/**
* Implement the composable function
* Make sure the function works correctly
*/
function useCounter(initialValue = 0, options: UseCounterOptions = {}) {
const count = ref(initialValue)
const inc = () => {
if (count.value < options.max) count.value++
}
const dec = () => {
if (count.value > options.min) count.value--
}
const reset = () => (count.value = initialValue)
return { count, inc, dec, reset }
}
const { count, inc, dec, reset } = useCounter(0, { min: 0, max: 10 })
</script>

实现本地存储函数

封装一个localStorageAPI

vue
<script setup lang="ts">
import { ref, watchEffect } from 'vue'
/**
* Implement the composable function
* Make sure the function works correctly
*/
function useLocalStorage(key: string, initialValue: any) {
const value = ref(localStorage.getItem(key) || initialValue)
watchEffect(() => {
localStorage.setItem(key, value.value)
})
return value
}
const counter = useLocalStorage('counter', 0)
// We can get localStorage by triggering the getter:
console.log(counter.value)
// And we can also set localStorage by triggering the setter:
const update = () => counter.value++
</script>
<template>
<p>Counter: {{ counter }}</p>
<button @click="update">Update</button>
</template>

相关知识点:

鼠标坐标

这个没通过单元测试,不知道什么原因,试了下其他人的也没能通过……

vue
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
// Implement ...
function useEventListener(target, event, callback) {
onMounted(() => target.addEventListener(event, callback))
onUnmounted(() => target.removeEventListener(event, callback))
}
// Implement ...
function useMouse() {
const x = ref(0)
const y = ref(0)
useEventListener(window, 'mousemove', e => {
x.value = e.pageX
y.value = e.pageY
})
return { x, y }
}
const { x, y } = useMouse()
</script>
<template>Mouse position is at: {{ x }}, {{ y }}</template>

Composition API

生命周期钩子

vue
<script setup lang="ts">
import { onMounted, inject, onUnmounted } from 'vue'
const timer = inject('timer')
const count = inject('count')
onMounted(() => {
timer.value = window.setInterval(() => {
count.value++
}, 1000)
})
// 计时器要清除
onUnmounted(() => {
window.clearInterval(timer.value)
})
</script>
<template>
<div>
<p>Child Component: {{ count }}</p>
</div>
</template>

ref 全家桶

vue
<script setup lang="ts">
import { ref, Ref, reactive, isRef, unref, toRef } from 'vue'
const initial = ref(10)
const count = ref(0)
// Challenge 1: Update ref
function update(value) {
count.value = value
}
/**
* Challenge 2: Check if the `count` is a ref object.
* Make the output be 1
*/
console.log(isRef(count) ? 1 : 0)
/**
* Challenge 3: Unwrap ref
* Make the output be true
*/
function initialCount(value: number | Ref<number>) {
// Make the output be true
console.log(unref(value) === 10)
}
initialCount(initial)
/**
* Challenge 4:
* create a ref for a property on a source reactive object.
* The created ref is synced with its source property:
* mutating the source property will update the ref, and vice-versa.
* Make the output be true
*/
const state = reactive({
foo: 1,
bar: 2
})
const fooRef = toRef(state, 'foo') // change the impl...
// mutating the ref updates the original
fooRef.value++
console.log(state.foo === 2)
// mutating the original also updates the ref
state.foo++
console.log(fooRef.value === 3)
</script>
<template>
<div>
<p>
<span @click="update(count - 1)">-</span>
{{ count }}
<span @click="update(count + 1)">+</span>
</p>
</div>
</template>

相关知识点:

响应性丢失

保证解构/扩展不丢失响应性

vue
<script setup lang="ts">
import { reactive, toRefs } from 'vue'
function useCount() {
const state = reactive({
count: 0
})
function update(value: number) {
state.count = value
}
return {
state: toRefs(state),
update
}
}
// Ensure the destructured properties don't lose their reactivity
const {
state: { count },
update
} = useCount()
</script>
<template>
<div>
<p>
<span @click="update(count - 1)">-</span>
{{ count }}
<span @click="update(count + 1)">+</span>
</p>
</div>
</template>

相关知识点:toRefs

可写的计算属性

vue
<script setup lang="ts">
import { ref, computed } from 'vue'
const count = ref(1)
const plusOne = computed({
get() {
return count.value + 1
},
set(newValue) {
count.value = newValue - 1
}
})
/**
* Make the `plusOne` writable.
* So that we can get the result `plusOne` to be 3, and `count` to be 2.
*/
plusOne.value++
</script>
<template>
<div>
<p>{{ count }}</p>
<p>{{ plusOne }}</p>
</div>
</template>

相关知识点:可写的计算属性

watch 全家桶

vue
<script setup lang="ts">
import { ref, watch } from 'vue'
const count = ref(0)
/**
* Challenge 1: Watch once
* Make sure the watch callback only triggers once
*/
const watchOnce = watch(count, () => {
console.log('Only triggered once')
watchOnce()
})
count.value = 1
setTimeout(() => (count.value = 2))
/**
* Challenge 2: Watch object
* Make sure the watch callback is triggered
*/
const state = ref({
count: 0
})
watch(
state,
() => {
console.log('The state.count updated')
},
{ deep: true }
)
state.value.count = 2
/**
* Challenge 3: Callback Flush Timing
* Make sure visited the updated eleRef
*/
const eleRef = ref()
const age = ref(2)
watch(
age,
() => {
console.log(eleRef.value)
},
{
flush: 'post'
}
)
age.value = 18
</script>
<template>
<div>
<p>
{{ count }}
</p>
<p ref="eleRef">
{{ age }}
</p>
</div>
</template>

相关知识点:侦听器 | Vue.js

浅层 ref

响应式 API: shallowRef

vue
<script setup lang="ts">
import { shallowRef, watch } from 'vue'
const state = shallowRef({ count: 1 })
// Does NOT trigger
watch(
state,
() => {
console.log('State.count Updated')
},
{ deep: true }
)
/**
* Modify the code so that we can make the watch callback trigger.
*/
state.value = { count: 2 }
</script>
<template>
<div>
<p>
{{ state.count }}
</p>
</div>
</template>

相关知识点:shallowRef()

依赖注入

child.vue

vue
<script setup lang="ts">
import { inject } from 'vue'
const count = inject('count')
</script>
<template>
{{ count }}
</template>

相关知识点:组合式 API:依赖注入 | Vue.js

Effect 作用域 API

vue
<script setup lang="ts">
import { ref, computed, watch, watchEffect, effectScope } from 'vue'
const counter = ref(1)
const doubled = computed(() => counter.value * 2)
// use the `effectScope` API to make these effects stop together after being triggered once
const scope = effectScope()
scope.run(() => {
watch(doubled, () => console.log(doubled.value))
watchEffect(() => console.log(`Count: ${doubled.value}`))
counter.value = 2
})
setTimeout(() => {
counter.value = 4
scope.stop()
})
</script>
<template>
<div>
<p>
{{ doubled }}
</p>
</div>
</template>

相关知识点:effectScope

自定义 ref

vue
<script setup>
import { watch, customRef } from 'vue'
/**
* Implement the function
*/
function useDebouncedRef(value, delay = 200) {
let timeout
return customRef((track, trigger) => {
return {
get() {
track()
return value
},
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() => {
value = newValue
trigger()
}, delay)
}
}
})
}
const text = useDebouncedRef('hello')
/**
* Make sure the callback only gets triggered once when entered multiple times in a certain timeout
*/
watch(text, value => {
console.log(value)
})
</script>
<template>
<input v-model="text" />
</template>

相关知识点:customRef

Directives

大写

创建一个自定义的修饰符  capitalize,它会自动将  v-model  绑定输入的字符串值首字母转为大写:App.vue

vue
<script setup>
import { ref } from 'vue'
import Input from './Input.vue'
const value = ref('')
</script>
<template>
<Input type="text" v-model.capitalize="value" />
</template>

Input.vue

vue
<script setup>
import { defineProps, defineEmits } from 'vue'
const props = defineProps({
modelValue: String,
modelModifiers: {
default: () => ({})
}
})
const emit = defineEmits(['update:modelValue'])
function emitValue(e) {
let value = e.target.value
if (props.modelModifiers.capitalize) {
value = value.charAt(0).toUpperCase() + value.slice(1)
}
emit('update:modelValue', value)
}
</script>
<template>
<input type="text" :value="modelValue" @input="emitValue" />
</template>

相关知识点:处理 v-model 修饰符

优化性能的指令

见上面。v-once

切换焦点指令

vue
<script setup lang="ts">
import { ref } from 'vue'
const state = ref(false)
/**
* Implement the custom directive
* Make sure the input element focuses/blurs when the 'state' is toggled
*
*/
const VFocus = {
updated: (el, state) => (state.value ? el.focus() : el.blur())
}
setInterval(() => {
state.value = !state.value
}, 2000)
</script>
<template>
<input v-focus="state" type="text" />
</template>

相关知识点:自定义指令 | Vue.js

防抖点击指令

尝试实现一个防抖点击指令

vue
<script setup lang="ts">
/**
* Implement the custom directive
* Make sure the `onClick` method only gets triggered once when clicked many times quickly
* And you also need to support the debounce delay time option. e.g `v-debounce-click:ms`
*
*/
function debounce(fn, delay) {
let timeout
let count = 0
return (...args) => {
if (count === 0) {
count++
fn(...args)
}
clearTimeout(timeout)
timeout = setTimeout(() => {
fn(...args)
}, delay)
}
}
const VDebounceClick = {
mounted: (el, binding) => {
const { value, arg } = binding
el.addEventListener('click', debounce(value, arg))
}
}
function onClick() {
console.log('Only triggered once when clicked many times quickly')
}
</script>
<template>
<button v-debounce-click:200="onClick">Click on it many times quickly</button>
</template>

相关知识点:指令钩子

激活的样式-指令

vue
<script setup lang="ts">
import { ref, watchEffect } from 'vue'
/**
* Implement the custom directive
* Make sure the list item text color changes to red when the `toggleTab` is toggled
*
*/
const VActiveStyle = {
mounted: (el, binding) => {
const [styles, fn] = binding.value
watchEffect(() => {
Object.keys(styles).map(key => (el.style[key] = fn() ? styles[key] : ''))
})
}
}
const list = [1, 2, 3, 4, 5, 6, 7, 8]
const activeTab = ref(0)
function toggleTab(index: number) {
activeTab.value = index
}
</script>
<template>
<ul>
<li
v-for="(item, index) in list"
:key="index"
v-active-style="[{ color: 'red' }, () => activeTab === index]"
@click="toggleTab(index)">
{{ item }}
</li>
</ul>
</template>

实现简易版v-model指令

vue
<script setup lang="ts">
import { ref } from 'vue'
/**
* Implement a custom directive
* Create a two-way binding on a form input element
*
*/
const VOhModel = {
mounted: (el, binding) => {
el.value = binding.value
el.addEventListener('input', () => {
value.value = el.value
})
}
}
const value = ref('Hello Vue.js')
</script>
<template>
<input v-oh-model="value" type="text" />
<p>{{ value }}</p>
</template>

Event Handling

阻止事件冒泡

vue
<script setup lang="ts">
const click1 = () => {
console.log('click1')
}
const click2 = e => {
console.log('click2')
}
</script>
<template>
<div @click="click1()">
<div @click.stop="click2()">click me</div>
</div>
</template>

相关知识点:事件修饰符

按键修饰符

vue
<template>
<!-- Add key modifiers made this will fire even if Alt or Shift is also pressed -->
<button @click.alt="onClick1" @click.shift="onClick1">A</button>
<!-- Add key modifiers made this will only fire when Shift and no other keys are pressed -->
<button @click.shift.exact="onCtrlClick">A</button>
<!-- Add key modifiers made this will only fire when no system modifiers are pressed -->
<button @click.exact="onClick2">A</button>
</template>
<script setup>
function onClick1() {
console.log('onClick1')
}
function onCtrlClick() {
console.log('onCtrlClick')
}
function onClick2() {
console.log('onClick2')
}
</script>

相关知识点:按键修饰符

Global API:General

下一次 DOM 更新

Vue.js中改变响应式状态时,DOM 不会同步更新。 Vue.js  提供了一个用于等待下一次 DOM 更新的方法

vue
<script setup>
import { ref, nextTick } from 'vue'
const count = ref(0)
const counter = ref(null)
async function increment() {
count.value++
/**
* DOM is not yet updated, how can we make sure that the DOM gets updated
* Make the output be true
*/
await nextTick()
console.log(+counter.value.textContent === 1)
}
</script>
<template>
<button ref="counter" @click="increment">
{{ count }}
</button>
</template>

相关知识点:nextTick()

Lifecycle

生命周期钩子

同上:生命周期钩子

Reactivity:Advanced

浅层 ref

同上:浅层 ref

原始值 API

vue
<script setup lang="ts">
import { reactive, isReactive, toRaw, markRaw } from 'vue'
const state = { count: 1 }
const reactiveState = toRaw(reactive(state))
/**
* Modify the code so that we can make the output be true.
*/
console.log(reactiveState === state)
/**
* Modify the code so that we can make the output be false.
*/
const info = markRaw({ count: 1 })
const reactiveInfo = reactive(info)
console.log(isReactive(reactiveInfo))
</script>
<template>
<div>
<p>
{{ reactiveState.count }}
</p>
</div>
</template>

相关知识点:

Effect 作用域 API

同上:Effect 作用域 API

自定义 ref

同上:自定义 ref

Reactivity:Core

ref 全家桶

同上:ref 全家桶

可写的计算属性

同上:可写的计算属性

watch 全家桶

同上:watch 全家桶

Reactivity:Utilities

响应性丟失

同上:响应性丟失

Utility Function

until

vue
<script setup lang="ts">
import { ref } from 'vue'
const count = ref(0)
/**
* Implement the until function
*/
function until(initial) {
function toBe(value) {
return new Promise(resolve => {
initial.value = value
resolve(initial.value)
})
}
return {
toBe
}
}
async function increase() {
count.value = 0
setInterval(() => {
count.value++
}, 1000)
await until(count).toBe(3)
console.log(count.value === 3) // Make sure the output is true
}
</script>
<template>
<p @click="increase">Increase</p>
</template>

Web Components

自定义元素

vue
<script setup lang="ts">
import { onMounted, defineCustomElement } from 'vue'
/**
* Implement the code to create a custom element.
* Make the output of page show "Hello Vue.js".
*/
const VueJs = defineCustomElement({
props: { message: String },
template: '<span>{{message}}</span>'
})
customElements.define('vue-js', VueJs)
onMounted(() => {
document.getElementById('app')!.innerHTML = '<vue-js message="Hello Vue.js"></vue-js>'
})
</script>
<template>
<div id="app"></div>
</template>

并且 vite.config.js 里要做相关设置相关知识点:Vue 与 Web Components | Vue.js

]]>
<![CDATA[Monkey Patch]]> https://seviche.cc/2022-08-06-notes 2022-08-06T00:00:00.000Z 2022-08-07T04:36:02.907Z Monkey Patch - Wikipedia

A Monkey patch is a way for a program to extend or modify supporting system software locally (affecting only the running instance of the program).”

也可以可以理解为基于原生功能(API)的一些自定义拓展?这个概念常在 Python 里面出现,也叫鸭子双关

Checking if a JavaScript native function is monkey patched

Monkey patching is a powerful but dangerous technique because the code you’re overriding is not in your control: future updates to the JavaScript engine may break some assumptions made in your patch and cause serious bugs.Additionally, by monkey patching code you don’t own, you might override some code that has already been monkey patched by another developer, introducing a potential conflict.

对于 JS 来说,Monkey patch 的问题在于:如果 JS 引擎更新了,可能会带来一些难以预计的 bug,这篇文章说了几种怎么检测是否存在 monkey patch 函数的方法,比如用 toString、iframe、proxy 等等,但都不是很完美,可以根据使用场景来决定用哪种方法

]]>
undefined
<![CDATA[我在看什么 · 7 月]]> https://seviche.cc/2022-07-31-reading-7 2022-07-31T00:00:00.000Z 2022-08-06T11:32:41.119Z 前端

后端

什锦

  • 折腾火狐

  • 中文博客榜

  • git add .,git add -A,git add -u,git add* 的区别与联系

  • Delightful lists

    Delightful lists are an effort to help bring change to this trend. To make freedom more discoverable again. This top-level project will allow navigation to all high-quality curated delightful lists created and maintained all over the web.Anyone that wishes to do so can create their own list, and thus create an entrypoint to freedom.

  • Computer Programming

    偶然进入的网站,没有入口也没有出口,比较有年代感了

    A set of rules that provides a way of telling a computer what operations to perform is called a programming language. There is not, however, just one programming language; there are many. In this chapter you will learn about controlling a computer through the process of programming. You may even discover that you might want to become a programmer.

  • Mentality

    一点点鸡汤https://alearningadayblog.files.wordpress.com/2022/07/image-2.png

  • 白话开源和 Web3

    原文:Open source and web3, simplified​

    “并非所有这些平台本身都开源,但重要的是,与服务模式绑定的收益会直接反馈在其原生代币的价值上。有了这样的设定,随着时间的推移将源代码开放出来的激励是巨大的,因为它让开发者更具活力,让基于服务的生态系统得以存续,并让人们相信,项目将会以最符合网络利益的方式持续运作(因为倘若不这样,它可以被"分叉",即容易被复制)”

    “它也不仅仅是代码。想想维基百科、Facebook、Uber、Linux —— 所有这些项目都是由少数人定义平台的规则,然后大量的人参与价值构建。在这些社区中进行价值捕获和分配面临各种困境 ──Facebook 或 Uber 模式让少数人暴富,维基百科或 Linux 模式则几乎无利可图。我上面描述的结构能够让用户参与价值的创造和捕获,既适用于 Facebook 也可用于 Linux。”

  • How to lazy load images in Hugo

    Create the file layouts/_default/_markup/render-image.html in your theme with this content:

    html
    <img src="{{ .Destination | safeURL }}" alt="{{ .Text }}" loading="lazy" />
    This makes the resulting HTML have the loading="lazy" attribute, which lazily loads images.
  • How Do Dolphins Choose Their Name?

    Dolphins identify themselves with a unique whistle that scientists have likened to a human name. Here’s how they decide what to call themselves.

Commonplace Book

  1. Intentionality

    every idea matters

    just collect very succinct quotes from biographies that i’ve read from books that i’ve read or from articles from of the internet that i’ve read and over time i ended up with this collection of very succinct sentences that but basically every one of these sentences they mean something every one of these words they mean something every expression that i put into this book they’re all very deeply personal for me and also the act of writing for me is very personal because writing for me and journaling for me is a form of calming the brain because my brain does not shut up so point one keeping a commonplacebook on a physical notebook is a lot more deliberate

  2. better editing

    writing on the paper at leastfor me liberates me from that uh illusion ofperfection illusion of cleanliness that i get on a computer screen because i cancross everything out i can cross things out straight away and i can spotmistakes and you know weak sentences straight away

  3. Crossing Disciplines

]]>
<![CDATA[JS中的二进制数字]]> https://seviche.cc/2022-07-27-bin 2022-07-27T00:00:00.000Z 2022-08-11T17:12:21.128Z 参考:How to Represent Binary Numbers in JavaScript? - Designcise

ES6+

在 ES6 之后的版本,在二进制数字前加0b 或者0B来标识这是一个二进制数字,比如:

js
let number5 = ob101
let number5 = oB101

Before ES6

  • 通过字符串和 parseInt 来转换
  • parseInt 可以在字符串中提取数字,第一个参数是要提取的字符串,第二个是基准的计算进制
js
const number = '0101'
Number.parseInt(number, 2)

相关:JavaScript · 十进制数转二进制

]]>
<![CDATA[undefined]]> https://seviche.cc/2022-07-23-notes-xss 2022-07-26T00:00:00.000Z 2022-08-11T17:39:51.841Z XSS 学习

介绍文章:

练习:

相关文章:

]]>
<![CDATA[D3.js 基础笔记]]> https://seviche.cc/2022-07-23-d3 2022-07-23T00:00:00.000Z 2022-08-01T03:03:33.463Z 内容出自:FreeCodeCamp

D3 或 D3.js 表示数据驱动文档。它是一个用于在浏览器中创建动态和交互式数据视觉化的 JavaScript 库。1

学习资源

基础操作

修改元素

  • select()
    • 功能:从文档中选择一个元素。
    • 参数:它接受你想要选择的元素的名字作为参数,并返回文档中第一个与名字匹配的 HTML 节点。
  • selectAll()
    • 选择一组元素。 并以 HTML 节点数组的形式返回该文本中所有匹配所输入字符串的对象
  • append()
    • 功能:将 HTML 节点附加到选定项目,并返回该节点的句柄。
    • 参数:接收你希望添加到文档中的元素
  • text()
    • 功能:设置所选节点的文本,也可以获取当前文本。 也可以用来添加标签
    • 参数:字符串或者回调函数
js
const anchor = d3.select('a')

在 D3 中可以串联多个方法,连续执行一系列操作。->[[function chaining|链式调用]]

使用数据

d3-selection

  • data()
    • 将元素与数据绑定
    • 不需要写 for 循环或者用 forEach() 迭代数据集中的对象。 data() 方法会解析数据集,任何链接在 data() 后面的方法都会为数据集中的每个对象运行一次。
    • 可以使用方括号的方式,如 d[0],来访问数组中的值。
  • enter():获取需要插入的选择集(数据个数大于元素个数)的占位符.

当 enter() 和 data() 方法一起使用时,它把从页面中选择的元素和数据集中的元素作比较。 如果页面中选择的元素较少则创建缺少的元素。

可以理解为管理仓库的,选择的元素是货架,数据是货,如果货架不够了,就再补几个货架,如果多了就不管

html
<body>
<ul></ul>
<script>
const dataset = ['a', 'b', 'c']
d3.select('ul').selectAll('li').data(dataset).enter().append('li').text('New item')
</script>
</body>

使用动态数据

text 中设置回调处理数据如:

  • d3.json(): 从指定的 input URL 获取 JSON 文件。如果指定了 init 则会将其传递给底层的 fetch 方法
html
<body>
<script>
const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9]
d3.select('body')
.selectAll('h2')
.data(dataset)
.enter()
.append('h2')
.text(d => d + ' USD')
</script>
</body>

给元素添加内联样式

  • style()
    • 功能:在动态元素上添加内联 CSS 样式
    • 参数:以用逗号分隔的键值对作为参数
js
selection.style('color', 'blue')
//用回调过滤
selection.style('color', d => {
return d < 20 ? 'red' : 'green'
})
// 动态设置样式
selection.style('height', d => d + 'px') // 动态设置高度

添加 Class

  • attr()
    • 功能:可以给元素添加任何 HTML 属性,包括 class 名称。
    • 参数:
      • attr() 方法和 style() 的使用方法一样。 它使用逗号分隔值,并且可以使用回调函数
      • 可接收一个回调函数来动态设置属性。 这个回调函数有两个参数,一个是数据点本身(通常是 d),另一个是该数据点在数组中的下标 i, 这个参数是可选的
    • 当需要添加 class 时,class 参数保持不变,只有 container 参数会发生变化。
js
selection.attr('class', 'container')
// 回调
selection.attr('property', (d, i) => {})
// 比如改变间距
svg
.selectAll('rect')
.data(dataset)
.enter()
.append('rect')
.attr('x', (d, i) => {
return i * 30 //改变间距
})
.attr('y', (d, i) => {
return d * 3 //改变高度
})
//悬停效果
.attr('class', 'bar')

标签

  • 和 rect 元素类似,text 元素也需要 x 和 y 属性来指定其放置在 SVG 画布上的位置, 它也需要能够获取数据来显示数据值。
  • 标签样式
    • fill 属性为 text 节点设置文本颜色
    • style() 方法设置其它样式的 CSS 规则,例如 font-family 或 font-size。
js
//添加标签
svg
.selectAll('text')
.data(dataset)
.enter()
.append('text')
.attr('x', (d, i) => i * 30)
.attr('y', (d, i) => h - 3 * d - 3) // 高三个单位是减
.text(d => d)
// 添加样式
.attr('fill', 'red')
.style('font-size', '25px')
//悬停效果
.attr('class', 'bar')
/** css中
.bar:hover {
fill: brown;
}
**/

添加工具提示 tooltip

  • 当用户在一个对象上悬停时,提示框可以显示关于这个对象更多的信息
  1. title
js
svg
.selectAll('rect')
.data(dataset)
.enter()
.append('rect')
.attr('x', (d, i) => i * 30)
.attr('y', (d, i) => h - 3 * d)
.attr('width', 25)
.attr('height', (d, i) => d * 3)
// 每个 rect 节点下附加 title 元素。 然后用回调函数调用 text() 方法使它的文本显示数据值。
.append('title')
.text(d => d)

SVG

  • 坐标系:坐标轴的原点在左上角。 x 坐标为 0 将图形放在 SVG 区域的左边缘, y 坐标为 0 将图形放在 SVG 区域的上边缘。 x 值增大矩形将向右移动, y 值增大矩形将向下移动。

创建 SVG

js
//创建svg
selection.append('svg')

反转 SVG 元素

  • 为了使条形图向上,需要改变 y 坐标计算的方式

SVG 区域的高度为 100。 如果在集合中一个数据点的值为 0,那么条形将从 SVG 区域的最底端开始(而不是顶端)。 为此,y 坐标的值应为 100。 如果数据点的值为 1,你将从 y 坐标为 100 开始来将这个条形设置在底端, 然后需要考虑该条形的高度为 1,所以最终的 y 坐标将是 99。

(高度从下面开始计算,坐标轴从上面开始)

  • y 坐标为 y = heightOfSVG - heightOfBar 会将条形图向上放置。
  • 通常,关系是 y = h - m * d,其中 m 是缩放数据点的常数。

更改 SVG 元素的颜色

  • 在 SVG 中, rect 图形用 fill 属性着色。 它支持十六进制代码、颜色名称、rgb 值以及更复杂的选项,比如渐变和透明。
js
svg
.selectAll('rect')
.data(dataset)
.enter()
.append('rect')
.attr('x', (d, i) => i * 30)
.attr('y', (d, i) => h - 3 * d)
.attr('width', 25)
.attr('height', (d, i) => 3 * d)
//将所有条形图的 fill 设置为海军蓝。
.attr('fill', 'navy')

SVG 图形

  • SVG 支持多种图形,比如矩形和圆形, 并用它们来显示数据。

矩形

SVG 的 rect 有四个属性。 x 和 y 坐标指定图形放在 svg 区域的位置, height 和 width 指定图形大小

js
const svg = d3
.select('body')
.append('svg')
.attr('width', w)
.attr('height', h)
.append('rect') //rect矩形
.attr('width', 25)
.attr('height', 100)
.attr('x', 0)
.attr('y', 0)

圆形

  • SVG 用 circle 标签来创建圆形
  • circle 有三个主要的属性。
    • cx 和 cy 属性是坐标。 它们告诉 D3 将图形的中心放在 SVG 画布的何处。
    • 半径( r 属性)给出 circle 的大小。
  • 和 rect 的 y 坐标一样,circle 的 cy 属性是从 SVG 画布的顶端开始测量的,而不是从底端。
js
svg
.selectAll('circle')
.data(dataset)
.enter()
.append('circle')
//散点图
.attr('cx', d => d[0])
.attr('cy', d => h - d[1])
.attr('r', '5')

比例尺

创建线性比例

条形图和散点图都直接在 SVG 画布上绘制数据。 但是,如果一组的高或者其中一个数据点比 SVG 的高或宽更大,它将跑到 SVG 区域外。

D3 中,比例尺可帮助布局数据。 scales 是函数,它告诉程序如何将一组原始数据点映射到 SVG 画布上。

线性缩放

  • 通常使用于定量数据
  • 默认情况下,比例尺使用一对一关系(identity relationship)。 输入的值和输出的值相同。
js
const scale = d3.scaleLinear()

例子

js
const scale = d3.scaleLinear() // 在这里创建轴
const output = scale(50) // 调用 scale,传入一个参数
d3.select('body').append('h2').text(output)

按比例设置域和范围

  • 域 domain:假设有一个数据集范围为 50 到 480, 这是缩放的输入信息,也被称为域。
  • 范围 range:沿着 x 轴将这些点映射到 SVG 画布上,位置介于 10 个单位到 500 个单位之间。 这是输出信息,也被称为范围。
  • domain()range() 方法设置比例尺的值, 它们都接受一个至少有两个元素的数组作为参数。
    • domain() 方法给比例尺传递关于散点图原数据值的信息
    • range() 方法给出在页面上进行可视化的实际空间信息

例子:

js
scale.domain([50, 480]); //域
scale.range([10, 500]); //范围
scale(50) //10
scale(480) //500
scale(325) //323.37
scale(750)。// 807.。67
d3.scaleLinear()

按顺序,将在控制台中显示以下值:10、500、323.37 和 807.67。

注意,比例尺使用了域和范围之间的线性关系来找出给定数字的输出值。 域中的最小值(50)映射为范围中的最小值(10)。

(也就是给定范围,用线性关系缩小,比如图片放大缩小,给了原图大小和缩小后的图片大小,根据线性关系比例来计算每个像素的位置,元素的大小)

最小值最大值

  • d3.min:最小值
  • d3.max: 最大值
  • min()max() 都可以使用回调函数,下面例子中回调函数的参数 d 是当前的内部数组。
  • min()max() 方法在设置比例尺时十分有用

例子:找到二维数组的最大值和最小值

js
const locationData = [
[1, 7],
[6, 3],
[8, 3]
]
const minX = d3.min(locationData, d => d[0]) //查找在d[0]位置上最小的值

使用动态比例

  • min()max() 来确定比例尺范围和域
  • padding 将在散点图和 SVG 画布边缘之间添加空隙。
  • 保持绘图在右上角。 当为 y 坐标设置 range 时,大的值(height 减去 padding)是第一个参数,小的值是第二个参数。
js
const dataset = [
[34, 78],
[109, 280],
[310, 120],
[79, 411],
[420, 220],
[233, 145],
[333, 96],
[222, 333],
[78, 320],
[21, 123]
]
const w = 500
const h = 500
const padding = 30
const xScale = d3
.scaleLinear()
.domain([0, d3.max(dataset, d => d[0])])
.range([padding, w - padding])

使用预定义的比例放置元素

  • 用比例尺函数为 SVG 图形设置坐标属性值。
  • 当显示实际数据值时,不用使用比例尺,例如,在提示框或标签中的 text() 方法。
js
svg
.selectAll('circle')
.data(dataset)
.enter()
.append('circle')
.attr('cx', d => xScale(d[0]))
.attr('cy', d => yScale(d[1]))
.attr('r', '5')

添加坐标轴

  • 位置:axisLeft()axisBottom()
  • g 元素, g 是英文中组(group)的缩写,在渲染时,轴只是一条直线。 因为它是一个简单的图形,所以可以用 g
  • SVG 支持多种 transforms,但是定位轴需要使用 translate 属性

例子:

js
// X轴
const xAxis = d3.axisBottom(xScale)
svg
.append('g')
.attr('transform', 'translate(0, ' + (h - padding) + ')') // translate(0,x)
.call(xAxis) // x轴作为参数被传递给 call() 方法
// Y轴
const yAxis = d3.axisLeft(yScale)
svg
.append('g')
.attr('transform', 'translate(0,' + (h - padding) + ')')
.call(xAxis)
svg
.append('g')
.attr('transform', 'translate(' + padding + ',0)')
.call(yAxis)

常见图表

散点图

圆圈来映射数据点,每个数据点有两个值。 这两个值和 x 和 y 轴相关联,在可视化中用来给圆圈定位。

js
svg
.selectAll('circle')
.data(dataset)
.enter()
.append('circle')
.attr('cx', d => d[0])
//反转图像
.attr('cy', d => h - d[1])
.attr('r', '5')
// 散点图加标签
svg
.selectAll('text')
.data(dataset)
.enter()
.append('text')
// Add your code below this line
.attr('x', d => d[0] + 5)
.attr('y', d => h - d[1])
.text(d => d[0] + ', ' + d[1])
]]>
<![CDATA[css和js两种方式实现div右移1000px动画]]> https://seviche.cc/2022-07-20-animation 2022-07-20T00:00:00.000Z 2022-08-01T03:03:33.463Z See the Pen css和js两种方式实现div右移1000px动画 by Sowhere (@sevichee) on CodePen.

参考:JavaScript 动画

]]>
<![CDATA[我在看什么 · 5月]]> https://seviche.cc/2022-07-16-reading-5 2022-07-16T00:00:00.000Z 2022-08-11T16:29:56.272Z Productivity

有趣的

]]>
<![CDATA[我在看什么 · 6月]]> https://seviche.cc/2022-07-15-reading-6 2022-07-15T00:00:00.000Z 2022-08-06T11:32:41.091Z 编程相关

前端

  1. Learn Debounce And Throttle In 16 Minutes

    讲得挺好的一个视频。节流(debounce)和防抖(Throttle)是优化高频率执行代码的一种手段1 如果把执行代码比喻成下楼拿快递的话:

    • 不做任何处理时就是快递来了就下楼拿。
    • 节流是不管快递来没来,每隔 10 分钟下一次楼。
    • 防抖则是第一个快递来了,等上 10 分钟,要是在这个 10 分钟内第二个快递来了,就继续等 10 分钟看有没有第三个快递,如果 10 分钟内下一个快递没来就下楼拿前面的快递。
  2. 基于 Vue 实现一个简易 MVVM

  3. Websocket 原理及具体使用(ws+socket.io)

  4. String.prototype.localeCompare()

    • 一个用来比较字符串的方法,可以结合 sort 方法对字符串进行排序,因为 sort 方法是按 ASCII 值来排序的,很多时候使用表现并不好,结合localeCompare可以做到按大小写排序、对带有重音符号的字符排序……
    • localeCompare接受三个参数:compareString、locales、options,其中 compareString(即用来比较的字符串)是必选的,其他为可选。
    • localeCompare的返回值有三种,负数、正数、0,其中如果引用字符存在于比较字符之前则为负数; 如果引用字符存在于比较字符之后则为正数; 相等的时候返回 0 。
  5. Pinia 和 Optional API 的结合使用

  6. How to Migrate from Vue CLI to Vite

  7. 使用 pnpm 替换 npm 优化项目开发

  8. 剪贴板操作 Clipboard API 教程

  9. 如何做前端单元测试

  10. Vue-测试

  11. 前后端加密(一):前端请求加密封装

  12. 深入浅出 npm & yarn & pnpm 包管理机制

    很多人认为 npm 是 node package manager 的缩写,其实不是,而且 npm 根本也不是任何短语的缩写。它的前身其实是名为 pm(pkgmakeinst) 的 bash 工具,它可以在各种平台上安装各种东西。硬要说缩写的话,也应该是 node pm 或者 new pm。

  13. 过度使用懒加载对 Web 性能的影响

  14. Vue 项目性能优化 — 实践指南(网上最全 / 详细)

  15. Vue 打包 chunk-vendors.js 文件过大解决方案(compression-webpack-plugin)

  16. 解决 NPM 安装应用出现 ERESOLVE 错误

  17. Web 性能优化:FOUC

  18. 傻傻分不清之 Cookie、Session、Token、JWT

  19. [译文]为什么 Angular 比 React 更适合企业级应用程序

后端

  1. Docker 从入门到实践
  2. Ubuntu 安装新版本 nodejs 的 5 种姿势
  3. npm does not support Node.js v10.19.0

其他

  1. 详细的全自动追番教程:Sonarr+Jackett+qBittorrent+Jellyfin
  2. Open Source Alternative to
  3. 在 GitHub 公开仓库中隐藏自己的私人邮箱地址
  4. 各种开发者文档的以往版本
  5. Github 删除某个文件的所有提交记录

互联网哲学

  1. Who Actually Owns Your Device?

    What it means to truly own your device is having the power and ability to modify it at a system level should you choose to – even if you don’t know what you’re doing, you should have the right.

  2. Information forest

    在产品沉思录周刊看到的推荐

    What should the browser of the 2020s be?

    What will a browser built for research, analysis, rabbit-hole exploration, messy thinking, and collaboration look like? These features are listed in the order I thought of them in, not necessarily by importance.

    1. Graph visualization and mind mapping.
    2. Interactive history and version control.
    3. Predictive search paths.
    4. Super Command-F (Superf).
    5. Collaboration
    6. Automatic scraping and clusterin
    7. Built in word processing.
    8. Backlinks.
    9. An infinitely zoomable interface (ZUI)

    相关阅读:Next Browser

]]>
<![CDATA[SQL 基础笔记]]> https://seviche.cc/2022-07-13-sql 2022-07-13T00:00:00.000Z 2022-08-01T03:03:33.359Z
  • 课程:The Complete 2022 Web Development Bootcamp
  • 工具:SQL Online Compiler - for Data Science
  • 教程:SQL 教程 | 菜鸟教程
  • SQL vs. NOSQL

    https://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/06/b67c213a42175acc16073cbad16acaf4.png

    https://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/06/01b1f9ba940268cf5c4108d9a4a94dc0.png

    • SQL
      • 注重结构
    • NOSQL
      • 更灵活,也更稳健
      • MoogoDB

    CRUD

    Create

    sql
    CREATE TABLE product(
    id Int NOT NULL,
    name STRING,
    price MONEY,
    PRIMARY KEY(id)
    )
    • NOT NULL 当此值为 null 时,不创建列
    • PRIMARY KEY(id) 主键必须包含唯一的值,这个不能有重复的值

    插入数值第一种形式无需指定要插入数据的列名,只需提供被插入的值即可

    sql
    INSERT INTO _table_name_
    VALUES (_value1_,_value2_,_value3_,...);

    第二种形式需要指定列名及被插入的值:

    sql
    INSERT INTO _table_name_ (_column1_,_column2_,_column3_,...)
    VALUES (_value1_,_value2_,_value3_,...);

    Read

    SELECT 语句用于从数据库中选取数据。

    sql
    SELECT _column_name_,_column_name_
    FROM _table_name_;
    sql
    SELECT * FROM _table_name_;

    * 表示选择全部

    可以用WHERE 筛选选择结果,如:

    sql
    SELECT _column_name_,_column_name_
    FROM _table_name_
    WHERE _column_name operator value_;

    Update

    sql
    UPDATE _tablse_name_
    SET _column1_=_value1_,_column2_=_value2_,...
    WHERE _some_column_=_some_value_;

    ALTER TABLE 语句用于在已有的表中添加、删除或修改列。如需在表中添加列,请使用下面的语法:

    sql
    ALTER TABLE table_name
    ADD column_name datatype

    如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):

    sql
    ALTER TABLE table_name
    DROP COLUMN column_name

    Destory

    sql
    DELETE FROM _table_name_
    WHERE _some_column_=_some_value_;

    Understanding SQL Relationships, Foreign Keys and Inner Joins

    FOREIGN KEY

    • FOREIGN KEY 来和外部表单链接
    • 一个表中的 FOREIGN KEY 指向另一个表中的 UNIQUE KEY(唯一约束的键)。

    INNER JOIN

    选择相应列并合并表,ON后面写的是条件

    sql
    SELECT _column_name(s)_
    FROM _table1_
    INNER JOIN _table2_
    ON _table1.column_name_=_table2.column_name_;

    或:

    sql
    SELECT _column_name(s)_
    FROM _table1_
    JOIN _table2_
    ON _table1.column_name_=_table2.column_name_;
    ]]>
    <![CDATA[一些设计作品]]> https://seviche.cc/2022-07-13-work 2022-07-13T00:00:00.000Z 2022-08-01T03:03:33.433Z 二十四节气茶包

    2022-07-13-work/1.jpg2022-07-13-work/2.jpg2022-07-13-work/3.jpg2022-07-13-work/4.jpg

    UI 设计

    /2022-07-13-work/famic2.png/2022-07-13-work/famic1.png/2022-07-13-work/card.png

    饼干包装

    升级前/2022-07-13-work/%E5%8E%9F%E5%8C%85%E8%A3%85.JPG升级后(面对年轻人重新设计/2022-07-13-work/after.png

    /2022-07-13-work/%E6%A0%B7%E6%9C%BA3.jpg/2022-07-13-work/%E6%A0%B7%E6%9C%BA4.jpg

    排版装帧

    /2022-07-13-work/book1.png/2022-07-13-work/book2.png/2022-07-13-work/book3.png/2022-07-13-work/book4.png

    儿童玩具

    /2022-07-13-work/game.webp

    未完待续……(也不知道续不续)

    ]]>
    undefined
    <![CDATA[VPS · Jellyfin结合Cloudreve开启线上影院]]> https://seviche.cc/2022-06-15-jellyfin 2022-06-15T00:00:00.000Z 2022-08-06T11:32:41.091Z 昨天搭了一下 Pleroma,完全失败,完全没信心了,所以今天决定随便在Awesome Self Hosted | LibHunt找了一个排名很靠前的东西搭一下,给自己建立一点没有必要的信心……

    刚好最近朋友找电影资源很犯难,所以我决定拿排在 Media 第一的Jellyfin试试手。

    关于 Jellyfin

    Jellyfin 是一个在线的流媒体影音库,对电影、音乐、有声书都有比较好的支持。除了在 web 端观看之外,它还支持很多的客户端,几乎是随处可用,目前我只试过安卓端的,其功能与网页端无异,适配得很好,体验流畅。

    可以在这里试一下 Demo:Jellyfin

    (用户名 demo,密码留空)

    下面是我的成果:-Dhttps://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/06/f83e0e4eca39ffd0304f847c6ad7b7e2.png

    https://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/06/820aa9841a44a52a29436549463e3355.png

    其实我也是搭完才知道它有什么功能的

    Cloudrever

    Jellyfin 目前不支持 S3 存储,所以我需要一个网盘来存储和管理 VPS 上的媒体资源。看了塔塔的 音乐库搭建 文章后我决定试一下 Cloudreve ,具体的搭建过程这里按下不表,是完全照着塔塔的教程和 Cloudreve 文档 做的

    反代

    需要注意的是,配置 Nginx 反代时,与往常不同,需要设置一下最大的文件大小,以免后期上传失败:

    /etc/nginx/conf.d/jellyfin.conf
    bash
    location / {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://127.0.0.1:5212;
    # 如果您要使用本地存储策略,请将下一行注释符删除,并更改大小为理论最大文件尺寸
    client_max_body_size 20000m;
    }

    一些配置

    1. 最大容量

      管理面板-> 用户组 里可修改https://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/06/7aa6d1e4d5d539d725929075b4cf2c5a.png

    2. 配置离线下载

      配置好离线下载就可以用它在后台下载种子资源了。如果用的是 docker-compose 来安装,下面的应该这样配置:https://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/06/4a316f6d84f90c58af4d7da1c2480447.png

      • [不可修改] RPC 服务器地址 => http://aria2:6800
      • [可修改, 需保持和 docker-compose.yml 文件一致] RPC 授权令牌 => your_aria_rpc_token
      • [不可修改] Aria2 用作临时下载目录的 节点上的绝对路径 => /data

    最后在 cloudreve 面板里创建一个用来存放 jelly 用的文件夹,比如 jellyfin

    Jellyfin

    搭建

    jellyfin 的搭建非常简单,给了我做人的很大信心(没有),我这里依旧选择用 docker-compose 来搭建。

    官方文档:Installing Jellyfin | Documentation - Jellyfin Project

    首先创建一个配置文件夹并进入:

    bash
    sudo mkdir /opt/jellyfin && cd /opt/jellyfin

    创建配置文件

    bash
    sudo nano docker-compose.yml

    写入:

    docker-compose.yml
    yaml
    version: '3.5'
    services:
    jellyfin:
    image: jellyfin/jellyfin
    container_name: jellyfin
    user: root
    network_mode: 'host'
    volumes:
    - /path/to/config:/config
    - /path/to/cache:/cache
    - /path/to/media:/media #修改为cloudreve的文件夹
    - /path/to/media2:/media2:ro #修改为cloudreve的文件夹
    restart: 'unless-stopped'
    # Optional - alternative address used for autodiscovery
    environment:
    - JELLYFIN_PublishedServerUrl=http://你的域名

    其中需要修改 /path/to/media:/media 的前半部分为 cloudreve 中在 VPS 中的存储路径,比如改为/opt/drive/cloudreve/uploads/ ,如果有多个 cloudreve 用户则可以在后面加用户 id,比如 /opt/drive/cloudreve/uploads/1 这样

    然后/path/to/media2 也是同样的修改,改为 /opt/drive/cloudreve/uploads/

    改完域名之后,执行 sudo docker-compose up -d 就可以在端口 你的ip:8096 看到界面了。

    反代

    我用的是 Nginx 来进行反代,虽然每次都是一样的操作,但是直接写可以复制粘贴会比较简单,所以我现在准备说废话。

    首先,创建配置文件:

    bash
    sudo nano /etc/nginx/conf.d/jellyfin.conf

    写入:

    /etc/nginx/conf.d/jellyfin.conf
    bash
    server {
    listen 80;
    server_name 你的域名;
    location / {
    proxy_pass http://127.0.0.1:8096;
    proxy_set_header HOST $host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    }

    SSL/TLS 配置可以看我之前这篇:配置 Cloudflare 的免费 SSL 证书,或者用 certbot 配置

    最后 sudo nginx -t 以及 sudo systemctl reload nginx 一下就可以在相应域名看到初始界面啦。

    媒体库路径

    这个地方我摸了好一会才明白.

    路径选择 media 或者 media2 下面的文件,然后后面的跟 cloudreve 里面的是一样的,比如在 cloudreve 中用户 1 创建的文件夹叫 jellyfin,则媒体库中路径为 /media/1/jellyfin

    https://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/06/6fc496318737b099af362731dc5958c6.png

    插件安装

    Jellyfin 有很多实用的插件可以爬电影/音乐等元数据,可以在 控制台-> 插件 安装,需要注意的是,安装完插件需要重启一下才可以生效,也就是先docker-compose downdocker-compose up -d

    目前感觉比较好用的两个插件:

    主题和语言

    可以在display 里面更改界面语言和主题,我比较喜欢的主题是 Purple Haze, 感觉是有些克制的赛博朋克风格

    ]]>
    <![CDATA[VPS · 配置 Cloudflare 的免费 SSL 证书]]> https://seviche.cc/2022-06-12-cloudflare 2022-06-12T00:00:00.000Z 2022-08-01T03:03:33.353Z 声明:我不知道这样安不安全哈,It just works,个人笔记,操作有风险

    参考:申请 CloudFlare 免费 SSL 证书并应用到 nginx – 65536.io | 自娱自乐

    先设置加密方式为完全 ,否则之后可能会出现 526 错误

    https://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/06/45ff9edefcc93a134b8478525bf6767b.png

    1. 创建证书

    首先将主域名绑定到 Cloudflare,然后在SSL/TLS 下的源服务器证书处,点击创建证书https://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/06/b693acbf458e0efaf1c18037e563060e.png

    然后选择私钥和 CSR 生成方式,以及证书的有效期(也可以不改),点击创建https://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/06/6925f00b181e60af9b8c211942359b6d.png

    将下面的证书和私钥暂时复制到某个安全的地方,点击确定https://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/06/bc66277a7ebfce59a1cc67ab2947b948.png

    2. 密钥上传到 VPS

    连接 VPS 之后,创建一个文件夹存入密钥,我将其存到/etc/nginx/cert/ 路径下

    bash
    sudo mkdir /etc/nginx/cert && cd /etc/nginx/cert

    写入证书,粘贴入刚刚保存的证书 栏里面的内容

    bash
    sudo nano public.pem

    修改权限

    bash
    sudo chmod 644 public.pem

    写入私钥,粘贴入刚刚保存的私钥 栏里面的内容

    bash
    sudo nano private.key

    修改权限

    bash
    sudo chmod 600 private.key

    3. 修改 Nginx 配置

    参考:How to Redirect HTTP to HTTPS in Nginx

    如果有用防火墙,请先打开 80 端口和 443 端口,不然可能会像我一样,卡在一个毫无意义的 522 Error 上 ^ ^

    bash
    sudo ufw allow 80
    sudo ufw allow 443

    打开 Nginx 配置

    bash
    sudo nano /etc/nginx/nginx.conf

    在 http 块里面配置一个默认 server,将 http 重定向到 https

    bash
    # 默认server
    server {
    listen 80 default_server;
    server_name _;
    return 301 https://$host$request_uri;
    }
    # ssl配置
    server {
    listen 443 ssl default_server;
    server_name *.example1.com;
    ssl_certificate /etc/nginx/cert/public.pem;
    ssl_certificate_key /etc/nginx/cert/private.key;
    }
    # 如果有多个域名,可以这样配置,证书也要按之前的添加一下
    server {
    listen 443 ssl;
    server_name *.example2.com;
    ssl_certificate /etc/nginx/cert/example2/public.pem;
    ssl_certificate_key /etc/nginx/cert/example2/private.key;
    }

    然后sudo nginx -t 测试一下,没有问题的话就可以 Nginx 了:

    bash
    sudo systemctl reload nginx

    之后如果有域名要配置 ssl ,如 example.conf 中,可以直接将 listen 80 改为listen 443 ssl

    我常用的一个反代配置:

    server {listen 443 ssl;server_name 域名location / {proxy_pass http://127.0.0.1:反代端口;proxy_set_header HOST $host;proxy_set_header X-Forwarded-Proto $scheme;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;}}
    ]]>
    <![CDATA[实现一个返回页面顶部的 Vue3 组件]]> https://seviche.cc/2022-06-10-backtotop 2022-06-10T00:00:00.000Z 2022-08-01T11:38:16.754Z 主要参考:Simple Vue.js and Tailwind.css Scroll To Top Button | Adam Bailey

    CSS 库:Bootstrap V5.2

    • 按钮的布局方式为 sticky
    • 因为可能需要频繁切换显示状态,所以用v-show 而不是 v-if来控制按钮可见性
    • 使用 Vue 中内置的<transition>组件实现状态之间的平滑过渡
    BackToTop.vue
    vue
    <template>
    <div class="position-sticky bottom-0 end-0 w-100 d-flex justify-content-end b-0 pb-3 pe-5">
    <transition>
    <button class="btn btn-secondary btn-sm" v-show="isVisible" @click="scrollToTop" aria-label="scroll to top of the page">
    <img src="../assets/to-top.min.svg" alt="an arrow point to top" />
    </button>
    </transition>
    </div>
    </template>
    <script lang="ts">
    import { defineComponent, ref, onMounted, onBeforeUnmount } from 'vue'
    export default defineComponent({
    name: 'BackToTop',
    setup() {
    const isVisible = ref(false)
    const handleScroll = () => {
    isVisible.value = window.scrollY > 0
    }
    const scrollToTop = () => {
    window.scrollTo({
    top: 0,
    behavior: 'smooth'
    })
    }
    onMounted(() => {
    window.addEventListener('scroll', handleScroll)
    })
    onBeforeUnmount(() => {
    window.removeEventListener('scroll', handleScroll)
    })
    return {
    isVisible,
    handleScroll,
    scrollToTop
    }
    }
    })
    </script>
    <style>
    .v-enter-active,
    .v-leave-active {
    transition: opacity 0.2s ease;
    }
    .v-enter-from,
    .v-leave-to {
    opacity: 0;
    }
    </style>
    to-top.min.svg
    html
    <svg width="20" height="20" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
    <path
    d="M24.008 14.1V42M12 26l12-12 12 12M12 6h24"
    stroke="#fff"
    stroke-width="4"
    stroke-linecap="round"
    stroke-linejoin="round" />
    </svg>

    其他参考/实现方式:

    题外话:BootStrap 的文档写得好烂

    ]]>
    <![CDATA[Contabo OSS + PicGo 图床配置]]> https://seviche.cc/2022-05-30-contabo-oss 2022-05-30T00:00:00.000Z 2022-08-01T03:03:33.353Z 1. 购买套餐

    在官网购买 OSS 套餐,按月付费:Object Storage: S3-Compatible with Free Data Transfer

    我没有修改设置,选的 250G 的容量,位于美国

    2. 创建 Bucket

    进入控制面板:Contabo Object Storage Panel,然后点击Create Bucket 创建存储桶

    https://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/05/114bf9578ffa81fb28c0010cd1d159f1.png其中 Bucket Name 可以随便写,Select Region 不用选,默认是购买 OSS 时所选择的区域,如果换区域也可以另选https://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/05/d7e982a6b2e8ff37b7354d3d9b49a415.png创建好后如图:(这个 Public Access 应该默认是红色的,也就是没有打开,我这里打开了)https://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/05/dc654d35fe384d2e999f7a70d31cfd07.png

    3. 安装 PicGo 插件

    在插件里面搜s3,然后安装第一个:https://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/05/86c9fbcaf0882da8ae5c342dcbae1cad.png

    安装好后,在设置里选择打开,https://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/05/e0e2ae2026b3182f65e9f6be6535050e.png

    4. 配置插件

    https://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/05/9ca1d257b19056becef07d604e388376.png

    应用密钥 ID 和 应用密钥

    打开Contabo Object Storage PanelAcount > Security & Access 面板,找到最下面的 S3 Object Storage Credentials

    这里对应插件设置里的:

    • 应用密钥 IDAccess Key
    • 应用密钥Secret Keyhttps://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/05/f1370bd49b1b7b54022e1203e8937ff1.png

    桶 / 自定义节点 /自定义域名

    • Bucket Name
    • 自定义节点 → Bucket URL 中桶名字前的部分,比如这里就是https://usc1.contabostorage.comhttps://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/05/53480cca5166e226dd804c5a1653b579.png
    • 自定义域名,需要先点击 Quick Action 里面的 Share 标志,打开 Public Sharing,然后里面的链接对应的就是设置里的自定义域名:https://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/05/5661c75dcb1fac89fff6d8bb2f9e8083.png

    其他设置

    打开这两项:https://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/05/94d2791b84b8a9c9b801e4b39753a125.png

    然后文件路径对应的是 Bucket 里面存储文件的路径,具体的设置可以参照:GitHub - wayjam/picgo-plugin-s3: PicGo S3 插件

    5. 其他

    Obsidian 中的图床设置

    先安装这个Image auto upload Plugin插件:https://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/05/567e38f40af9758fb155ff7408f9261f.png

    然后在 PicGo 设置里面 → 设置 Server→ 打开 Server 开关https://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/05/815df1bbba1e0183518e0d7bb04589e5.png

    https://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/05/bacd83f2375bdad673ce7ac98ebdfe96.png

    然后 Obsidian 插件中这样设置:

    PicGo Server :http://127.0.0.1:36677/upload

    端口号不一定是 36677,只要一一对应就好。https://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/05/eb921dee9017d358df5d3458cab43be6.png

    设置好后,在后台保持 PicGo 开启,就可以在 Obsidian 里面粘贴一键上传图床了~

    更多 PicGO 插件:

    GitHub - PicGo/Awesome-PicGo: A collection of awesome projects using PicGo.

    有图床备份、图片压缩、图床转移等插件,不过我都没有试过……

    ]]>
    <![CDATA[为博客写一个Project showcase 页面]]> https://seviche.cc/2022-05-26-write-a-page-template 2022-05-26T00:00:00.000Z 2022-08-01T11:36:42.589Z 这两天为博客写了一个 Project 的页面用来放我的作品,这里记录一下我是怎么写(模仿)的,我对 Svelte 语法的了解不多,没有特别深入学习,只是在官方看了下文档和用了下他们的 交互式教程 ,编码的过程是一边学习一边模仿慢慢摸索的,虽然最终没有 merge 到 repo 中,但我觉得整个过程都蛮兴奋的。

    既然有了博客,那我肯定是要写一下这个过程的。

    1. 分析需求

    我想要的是一个独立的 Page,而不是一个 Post 页面,最后把它放在导航栏里面。想要有以下这几个功能:

    • 技术栈分类
    • 项目类别筛选
    • 项目展示

    主要有这些信息的展示:

    • 项目标题
    • 项目图片
    • 项目描述
    • 技术栈
    • 项目类别

    2. 画原型图

    明确了需求后,参考了几个项目平台的布局,在 Whimsical 上画了原型图如下:https://s2.loli.net/2022/05/26/8kMa6LPrgUEC7Xb.png

    目前还没有做上面 Tag 的分类功能,之后可能会做吧

    2. 创建组件样式 CSS

    为了统一风格,我在博客现有框架里四处搜寻可用的组件样式,想在这基础上修改,然后我找到了作者 藍 在 Tailwind Play 上的友链组件,感觉很适合,然后就直接在这个 Tailwind Play Demo 上进行了样式修改,不过此时填写的数据都是死数据,后面再进行修改。

    因为我之前没有怎么用过 Tailwind,所以是一边对照 Tailwind 文档修改的,然后 Tailwind Play 上的代码提示功能真的很新手友好,hover CSS class 的时候会显示具体的 CSS 原始参数,很直观。

    https://s2.loli.net/2022/05/27/lFwQ8T5YUcdjDfe.png最后我构建的 Demo 样式如下:Tailwind Play

    https://s2.loli.net/2022/05/27/g5aYxD9mzlqpj6c.png

    4. 编写组件代码

    整个页面的构建跟 Friend 页面很像,我分析了 Friend 页面所涉及到的代码和结构,然后一点点模仿构建 Project 页面。

    数据

    首先根据需求确定传入的数据及其格式,以便后面使用 TypeScript 的提示

    • 参考:/src/lib/config/friends.ts
    • 新建:/src/lib/config/projects.ts
    /src/lib/config/friends.ts
    ts
    export interface FriendOld {
    // hCard+XFN
    id: string // HTML id
    rel?: string // XFN, contact / acquaintance / friend
    link?: string // URL
    html?: string // HTML
    title?: string // 标题
    descr?: string // 描述
    avatar?: string // 头像
    name?: string // backwards compatibility
    }
     
    export type Friend = {
    id: string // HTML id
    rel?: string // XHTML Friends Network
    link?: string // URL
    html?: string // Custom HTML
     
    title?: string // 标题
    name?: string // 人名
    avatar?: string // 头像
    descr?: string // 描述
    class?: {
    avatar?: string // 头像类名
    img?: string // 图片类名
    }
    }
     
    export const friends: Friend[] = [
    {
    id: 'id',
    rel: '',
    title: '',
    name: '',
    link: '',
    descr: '',
    avatar: ''
    }
    ]
    /src/lib/config/projects.ts
    ts
    export type Project = {
    id: string
    name?: string
    tags?: string[]
    feature?: string
    description?: string
    img?: string
    link?: string
    }
     
    export const projects: Project[] = [
    {
    id: 'coach',
    name: 'Find a Coach',
    tags: ['Vue 3', 'Composition API'],
    feature: 'Vue3',
    description:
    '既然如何, 问题的关键究竟为何? 要想清楚,科学和人文谁更有意义,到底是一种怎么样的存在。 普列姆昌德曾经提到过,希望的灯一旦熄灭,生活刹那间变成了一片黑暗。这启发了我, 那么, 我认为, 总结的来说,',
    img: 'https://uneorange.oss-cn-guangzhou.aliyuncs.com/202205251801454.avif',
    link: 'https://seviche.cc'
    }
    ]

    组件

    将 CSS 复制进去,并注入数据

    • 参考:/src/lib/components/extra/friend.svelte
    • 新建:/src/lib/components/extra/project.svelte
    /src/lib/components/extra/friend.svelte
    html
    <script lang="ts">
    import type { Friend } from '$lib/config/friends'
    import Footer from '$lib/components/footer.svelte'
    export let item: unknown
    let friend = item as unknown as Friend
    </script>
    {#if friend.id === 'footer'}
    <footer rounded="{true}" class="p-4 md:p-8" />
    {:else if friend.html}
    <a id="{friend.id}" rel="{friend.rel}" href="{friend.link}" class="h-card u-url">{@html friend.html}</a>
    {:else}
    <a
    id="{friend.id}"
    rel="{friend.rel}"
    href="{friend.link}"
    class="card bg-base-100 shadow-xl hover:shadow-2xl transition-shadow h-card u-url">
    <div class="absolute text-4xl font-bold opacity-5 rotate-6 leading-tight top-4">{friend.name ?? ''}{friend.title ?? ''}</div>
    <div class="card-body p-4">
    <div class="flex items-center gap-4">
    {#if friend.avatar}
    <div class="avatar {friend.class?.avatar} shrink-0 w-16 mb-auto">
    <img class="{friend.class?.img ?? 'rounded-xl'} u-photo" src="{friend.avatar}" alt="{friend.title}" />
    </div>
    {:else}
    <div class="avatar {friend.class?.avatar} placeholder mb-auto">
    <div class="{friend.class?.img ?? 'bg-neutral-focus text-neutral-content shadow-inner rounded-xl'} w-16">
    <span class="text-3xl">{(friend.name ?? friend.title).charAt(0)}</span>
    </div>
    </div>
    {/if}
    <div class="card-title flex-col gap-0 flex-1 items-end">
    <span class="text-right p-name">{friend.name ?? ''}</span>
    <span class="opacity-50 text-right">{friend.title}</span>
    </div>
    </div>
    {#if friend.descr}
    <div class="prose opacity-70 p-note">{friend.descr}</div>
    {/if}
    </div>
    </a>
    {/if}

    根据具体的页面效果修改了 Demo 中的 CSS 样式

    /src/lib/components/extra/project.svelte
    html
    <script lang="ts">
    import type { Project } from '$lib/config/projects'
    import Footer from '$lib/components/footer.svelte'
    export let item: unknown
    let project = item as unknown as Project
    let tags = project.tags
    </script>
    {#if project.id === 'footer'}
    <footer rounded="{true}" class="max-w-4xl mx-auto p-4 md:p-8" />
    {:else}
    <a
    id="{project.id}"
    href="{project.link}"
    class="card mx-auto max-w-4xl bg-base-100 shadow-xl transition-shadow mb-7 h-card u-url hover:shadow-2xl">
    <div class="absolute text-5xl font-bold opacity-5 rotate-6 leading-tight top-2 right-0">{project.feature}</div>
    <div class="card-body p-4">
    <div class="flex flex-col md:flex-row items-start gap-4">
    <div class="mb-auto max-w-full shrink-0 md:max-w-xs">
    <img class="rounded-md" src="{project.img}" alt="{project.description}" />
    </div>
    <div class="card-title flex-1 flex-col items-start gap-4">
    <div>
    <h2 class="p-name text-left text-2xl mb-2">{project.name}</h2>
    <div class="mb-3 text-base font-normal">
    {#each tags as tag}
    <span class="btn btn-sm btn-ghost normal-case border-dotted border-base-content/20 border-2 my-1 mr-1">{tag}</span>
    {/each}
    </div>
    </div>
    <p class="text-left text-base font-normal opacity-70">{@html project.description}</p>
    </div>
    </div>
    </div>
    </a>
    {/if}

    页面

    • 参考:/urara/friends/index.svelte
    • 新建:/urara/projects/index.svelte
    /urara/friends/index.svelte
    html
    <script lang="ts">
    // @ts-nocheck
    import Masonry from 'svelte-bricks'
    import { Friend, friends as allFriends } from '$lib/config/friends'
    import Head from '$lib/components/head.svelte'
    import FriendComponent from '$lib/components/extra/friend.svelte'
    const rnd = Math.random()
    const fy = (a: Friend[], r = 0, c = a.length) => {
    while (c) (r = (rnd * c--) | 0), ([a[c], a[r]] = [a[r], a[c]])
    return a
    }
    let items: { id: string }[] = [...fy(allFriends as { id: string }[]), { id: 'footer' }]
    let width: number, height: number
    </script>
    <head />
    <Masonry
    {items}
    minColWidth="{384}"
    maxColWidth="{384}"
    gap="{32}"
    let:item
    class="mx-4 sm:mx-8 md:my-4 lg:mx-16 lg:my-8 xl:mx-32 xl:my-16"
    bind:width
    bind:height>
    <FriendComponent {item} />
    </Masonry>

    Projects 页面

    因为我没有用到瀑布流布局,所以删掉了一些组件和 function

    /urara/projects/index.svelte
    html
    <script lang="ts">
    import { projects as allProjects } from '$lib/config/projects'
    import Head from '$lib/components/head.svelte'
    import ProjectComponent from '$lib/components/extra/projects.svelte'
    let items: { id: string }[] = [...(allProjects as { id: string }[]), { id: 'footer' }]
    </script>
    <head />
    {#each items as item}
    <ProjectComponent {item} />
    {/each}

    响应式布局

    参考 Tailwind 的响应式设计指南 ,修改了卡片flex 的方向,以及图片的宽度,以适应小尺寸屏幕。

    5. 测试

    其实有错误的话 pnpm dev 以及 pnpm build 的时候都会提醒,但我后面发现也可以用 pnpm check 来检查。过程中我好像没有遇到什么 Bug。

    6. Pull request 到 Github

    先看了一下 Repo 作者写的 contributing docs,了解 Contribute 的规范。

    按照相应步骤做了之后,Google 了一下如何 pull request,照着 FreeCodeCamp 的这篇进行了操作:如何在 GitHub 提交第一个 pull request ,然后成功 Pull 了一个 Request,但后面发现有的文件没有改,造成了 bug,就删除了原 Request 重新 Pull。

    最后终于创建成功了我的第一个 Pull request!链接:feat: ✨ add project page by Sevichecc · Pull Request #19 · importantimport/urara · GitHub

    7. Last but not least

    写一篇这样的博文,并发表到互联网。

    好啦我知道这篇文章有点臭屁,但下次还敢……

    ]]>
    <![CDATA[Git · 常用操作笔记]]> https://seviche.cc/2022-05-25-git 2022-05-25T00:00:00.000Z 2022-08-01T03:03:33.322Z 资料:

    每次更新博客进行的操作

    1. 追踪所有文件

    (除了 gitignore 里面的),也可以单独加

    git add -A

    2. 提交上传信息

    git commit -m '一些信息,如fixed something etc'

    3. push 到 Github

    git push origin main

    等待一会儿就好了,如果不行,换个网或者关掉 VPN 看看

    常用 Git 操作

    • .gitignore: 放不想传到 git repo 的文件/文件夹
    • 当内容改动很多的时候,最好开一个 branch
    • VSCode 文件后面的字母:
      • U:untrack
      • M: modified
      • A : on track
    • 一般不在mainmaster 修改代码,而是开一个 branch,确定好后再 merge
    • 下载叫做 pull,上传是 push
    命令作用
    git config --global user.name 名字设置名字
    git config --global user.email 邮箱设置邮件
    git init初始化
    git add -A追踪所有文件(除了 gitignore 里面的),也可以单独加
    git commit -mm 代表信息,后面要写 commit 相关信息
    git status查看 git 状态/信息
    git log查看 commit 日志,按 Q 才可以退出
    git reset hard (commit的id)回到特定版本
    git reset hard --HEAD返回上一次改动 (还没有 commit)
    git branch列出现在有的 branch,按 Q 退出
    git branch (branch'name)创建新 branch
    git merge (branch'name)合并 branch 到 main
    git checkout (branch's name)切换 branch
    git remote add origin https://github.com/用户名/仓库名.git链接到 remote repo
    git pull拉更新
    git push origin (branch'name)push 到 remote repo

    常见问题

    下面是一些我看过的文章

    版本回滚

    报错处理

    博客相关

    其他

    ]]>
    <![CDATA[Hypothesis 使用小记]]> https://seviche.cc/2022-05-25-hypothesis 2022-05-25T00:00:00.000Z 2022-08-01T03:03:33.353Z Hypothesis 太好用了,方便我满世界乱画写屁话(?),用它在网页上高亮,就像用荧光笔在纸上标注一样轻松 ,写标注就写在 3M 便利贴上,哪里都好贴,而且还支持用 Markdown 写,真的越用越顺手

    它导出的笔记提供了导出的 API,可以轻松同步到 Obsidian / Logseq,真是平易近人呢!

    我的使用例子:/2022-05-25-hypothesis/657e30351066c4a3.png

    我在 Obsidian 中导出的笔记:/2022-05-25-hypothesis/0bf1f9770192c362.png

    一些基础的部分我会省略掉,详情可以看这篇文章:开源、可定制的网页批注工具——Hypothesis

    在下面这些操作之前,需要先注册一个 Hypotheis 账号,并安装浏览器扩展:

    同步到 Logseq

    Logseq 我用得不多,如果有写错的,欢迎给我提建议~

    1. 打开插件系统开关

    (如果打开了可以省略这个)

    /2022-05-25-hypothesis/Snipaste_2022-05-25_21-14-43.png

    如果在国内连接插件市场,最好设置一下代理,不然可能装不上,具体的设置需要看一下 VPN 端口号之类的。

    2. 安装 Hypothesis 插件

    打开 Logseq 的插件市场,找到如下名为Hypothesis的插件:/2022-05-25-hypothesis/Snipaste_2022-05-25_21-20-20.png如果一直下载不了的话,可以直接在 Github 下载:logseq-hypothesis

    然后在这里导入:/2022-05-25-hypothesis/Snipaste_2022-05-25_21-22-41.png

    3. 获取 API Token

    在这里生成一个 API Token:Regenerate your API token

    复制后点击 Logseq 右上角的这个 H 的标志:/2022-05-25-hypothesis/Snipaste_2022-05-25_21-26-37.png

    然后填入刚刚复制的 API Token 和用户名

    /2022-05-25-hypothesis/Snipaste_2022-05-25_21-45-33.png

    用户名跟 Hypothesis 这里显示的一样,比如我的就是 Sevicheeee/2022-05-25-hypothesis/Snipaste_2022-05-25_21-32-17.png

    4. 同步笔记

    点击 Fetch Latest Notes 会拉取最新的笔记

    如果选择了指定页面,然后点Add page notes to graph,会自动生成一篇相应的笔记,比如:/2022-05-25-hypothesis/Snipaste_2022-05-25_21-47-28.png

    5. 修改笔记模板

    如果想修改笔记模板的话,可以在setting中修改:/2022-05-25-hypothesis/Snipaste_2022-05-25_21-48-58.png

    同步到 Obsidian

    1. 安装 Hypothesis 插件

    如图所示:/2022-05-25-hypothesis/Snipaste_2022-05-25_21-51-44.png

    Github: obsidian-hypothesis-plugin

    2. API 配置

    打开插件设置,点击右上角的Connect, 输入你的 API Token 并保存,如果没有获取的话,请在这里获取:Regenerate your API token

    /2022-05-25-hypothesis/Snipaste_2022-05-25_21-53-41.png

    可以在这里选择笔记保存的位置:/2022-05-25-hypothesis/Snipaste_2022-05-25_21-56-38.png

    其中Use Domain Folders 是按域名来划分文件结构,如果没有打开的话就是默认一篇笔记一个 md 文档,如下图红框所示就是打开了这个,下面蓝色的就是没打开时的输出结构/2022-05-25-hypothesis/Snipaste_2022-05-25_21-58-17.png

    3. 笔记模板配置

    可以在右边的文本框内设置笔记输出格式/2022-05-25-hypothesis/Snipaste_2022-05-25_22-00-11.png说实话,我没有怎么看懂 Orz, 然后这是我的模板:

    js
    {% if is_new_article %}# {{title}}
    ## Metadata
    {% if author %}- Author: [{{author}}]({{authorUrl}}){% endif %}
    - Title: {{title}}
    {% if url %}- Reference: {{url}}{% endif %}
    - Category: #source/article🗞{% endif %}
    - Tags:
    {% if is_new_article -%}## Highlights{%- endif %}
    {% for highlight in highlights -%}- {{highlight.text}}
    {% if highlight.tags | length %} - Tags: {% for tag in highlight.tags -%}#{{tag| replace(" ", "-")}} {%- endfor %}{%- endif %}
    {% if highlight.annotation %} - Annotation: {{highlight.annotation}}{%- endif -%}{%- endfor -%}

    效果:/2022-05-25-hypothesis/Snipaste_2022-05-25_22-03-03.png

    4. 更新笔记

    点击右边这个标志就可以更新笔记了~也可以在设置里打开启动时自动抓取的设置/2022-05-25-hypothesis/Snipaste_2022-05-25_22-04-44.png

    订阅 Hypothesis 的 RSS

    其他参考

    ]]>
    <![CDATA[SQL · 在 Windows 10 上安装 sqlite]]> https://seviche.cc/2022-05-23-win-sql 2022-05-23T00:00:00.000Z 2022-08-01T03:03:33.303Z 参考: Site Unreachable

    1. 下载二进制文件

    • 请访问  SQLite 下载页面,从 Windows 区下载预编译的二进制文件。
    • 您需要下载  sqlite-tools-win32-*.zip  和  sqlite-dll-win32-*.zip  压缩文件。

    /2022-05-23-win-sql/2.png

    2. 创建安装文件夹

    • 在 C 盘内创建文件夹 sqlite(在别的地方应该也可以?),并在此文件夹下解压上面两个压缩文件,将得到 sqlite3.def、sqlite3.dll 和 sqlite3.exe 文件。如:/2022-05-23-win-sql/DMirANRL4FYs8Xx.png

    3. 添加环境变量

    参考:sqlite 在 windows 下载安装,配置环境变量

    首先,打开控制面板,如果找不到的话,直接搜索,例如:/2022-05-23-win-sql/Bl1JjI2A6HnfQVZ.png

    然后点击系统与安全,再点击 系统/2022-05-23-win-sql/q7RaUdAYHz2lpC3.png/2022-05-23-win-sql/V87O1wdLscPbvTC.png

    在新窗口中点击高级系统设置 -> 环境变量/2022-05-23-win-sql/HSD6iy9nUxCEkcQ.png

    /2022-05-23-win-sql/SnJePah46I7CyGF.png

    如上图所示,在蓝色区域新建一个环境变量,此处填写一开始创建 sqlite 文件的路径,比如这里就是C:\sqlite

    4. 命令提示符中查看 sqlite3 版本

    什么是命令提示符?

    命令提示符是大多数 Windows 操作系统中可用的命令行解释器应用程序。

    命令提示符用于执行输入的命令 。 大多数这些命令用于通过脚本和批处理文件自动执行任务,执行高级管理功能以及排除和解决某些类型的 Windows 问题。

    命令提示符被正式称为Windows 命令处理器,但有时也被称为命令外壳程序cmd 提示符 ,甚至称其为文件名cmd.exe 。——命令提示符(它是什么以及如何使用它)

    我的理解就是用代码的方式操作电脑系统

    如何打开命令提示符:/2022-05-23-win-sql/oL7n6rta35UAODl.png

    打开后输入sqlite3 ,出现下面的文字就是安装成功了/2022-05-23-win-sql/qNgBIvzLQJX126b.png

    常用命令行

    • 需要在文件夹里运行
    • cd 的时候按 tab 可以自动填充文件名 (VS Code 里面按 command)
    命令功能
    ls(win 用 dir)展示当前文件夹文件
    cd改变当前文件夹(change directory) 按 tab 可以自动填充
    cd..去到上个层级的文件夹
    clear清除命令行记录
    mkdir创建新文件夹(make directory)
    touch(win 用 edit)创建新文件(可以一次性创建多个)
    mv移动文件,第一个参数是要移动的文件,第二个是目的地,如 mv index.html ../ 为移动到上一层级
    rmdir删除空文件夹(remove directory)
    rm -R删除文件夹(R-recursively)
    pwd当前所在位置
    rm(win 用 del)删除文件
    ]]>
    <![CDATA[JavaScript · 数组中的copyWithin方法]]> https://seviche.cc/2022-05-09-copywithin 2022-05-08T17:13:47.671Z 2022-08-01T03:03:33.294Z

    copyWithin() 方法浅复制数组的一部分到同一数组中的另一个位置,并返回它,不会改变原数组的长度

    —— MDN

    简介

    以下部分内容来自 MDN

    语法:

    js
    arr.copyWithin(target[, start[, end]])

    参数

    target

    0 为基底的索引,复制序列到该位置。

    如果是负数,target  将从末尾开始计算。如果 target 大于等于 arr.length,将会不发生拷贝。如果 targetstart 之后,复制的序列将被修改以符合 arr.length

    start

    0 为基底的索引,开始复制元素的起始位置。

    如果是负数,start  将从末尾开始计算。如果  start  被忽略,copyWithin  将会从 0 开始复制。

    end

    0 为基底的索引,开始复制元素的结束位置。

    copyWithin  将会拷贝到该位置,但不包括 end 这个位置的元素

    如果是负数, end  将从末尾开始计算。如果 end 被忽略,copyWithin 方法将会一直复制至数组结尾(默认为 arr.length)。

    例子

    源码

    js
    const array1 = ['a', 'b', 'c', 'd', 'e']
    // copy to index 0 the element at index 3
    console.log(array1.copyWithin(0, 3, 4))
    // expected output: Array ["d", "b", "c", "d", "e"]
    // copy to index 1 all elements from index 3 to the end
    console.log(array1.copyWithin(1, 3))
    // expected output: Array ["d", "d", "e", "d", "e"]

    可视化

    上图将 target 位置用红色的部分表示,被复制的元素为蓝色。

    1. a = array1[0] ,所在位置是将要被替换的 target 位置。d = array1[3]是复制的起始元素,复制结束在array1[4]之前。复制后a的位置被 d所取代。
    2. b = array1[1] ,所在位置是将要被替换的 target 位置,d = array1[3]是复制的起始元素,复制没有指定结束位置,所以一直复制到数组末尾。复制后a的位置被 'd','e'所取代。
    ]]>
    <![CDATA[VPS 安全初始化]]> https://seviche.cc/2022-05-07-vps-init 2022-05-06T00:00:00.000Z 2022-08-01T03:03:33.291Z 前情提要:

    前段时间我所购买的 VPS 服务商 Contabo 发邮件来说,我用 VPS 攻击了其他的服务器,让我快点停止这种行为,要是不改就罚我的钱,但是我并没有在上面装什么奇怪的东西,就只装了一个聊胜于无的 WordPress,手足无措之余在 Mastodon 哀嚎了一下,得到了很多热心网友的帮助,才发现原来我一直在裸奔使用 VPS,什么安全措施都没采取:(

    鉴于 VPS 上本来就没有什么东西,我决定重新初始化机子,本文是初始化的笔记,我的系统是 Ubuntu 20.04,文中提到的 ufw 是内置的,没有额外安装, 有些步骤上有所省略,最好对照着提到的参考文章看。

    (再次感谢 Allen Zhong、糖喵、南狐、shrik3 等朋友的热心指导 o(≧v≦)o!)

    思路

    下面这两点都是 Contabo 客服发给我的防护建议,用 Deepl 翻译了一下

    日常防护

    • 检查你的服务器是否有可疑的进程并删除它们(例如使用以下命令:ps aux| grep stealth)
    • 检查错误日志,例如/var/log/apache2/error_log,找出是否有任何恶意脚本的错误信息或恶意软件下载的迹象。
    • 攻击者经常在以下目录中安装恶意软件。/tmp/ , /var/tmp/ - 请使用 find /tmp (find /var/tmp) 来检查隐藏的文件。
    • 扫描你的服务器以防止安装的 rootkits。
    • 运行一个防病毒软件,如 ClamAV 或 ClamWin。

    安全检查

    1. 保持定期备份。
    2. 保持你的整个系统一直是最新的,这意味着你必须定期安装使用的软件包和网络应用程序的更新和补丁。
    3. 安装并运行一个防病毒软件,如 ClamAV 或 ClamWin,以保持你的服务器不受恶意软件侵害。
    4. 设置一个防火墙,关闭所有你不需要的端口,并将 SSH 的 22 或 RDP 的 3389 等默认端口改为其他。
    5. 通过安装一个合适的软件,如 cPHulk 或 Fail2ban,阻止暴力攻击。
    6. 避免使用只在不安全的设置下工作的脚本。
    7. 不要点击电子邮件中的任何可疑附件或链接,或访问不安全的网站。
    8. 使用 SSH-Keys 而不是密码。

    最后我将 VPS 里的内容全删了,从 0 出发,下面是具体的操作步骤:

    1. 创建新用户

    参考: VPS 建站新手上路 - YOLO

    首先用 root 登陆,然后输入 adduser + 用户名 创建新用户,如添加用户jack

    shell
    adduser jack

    接着输入两遍密码,其他信息可以按 Enter 留空

    给这个用户 root 权限:

    shell
    sudo usermod -aG sudo jack

    其他参考: 如何在 Ubuntu 上添加和删除用户 | myfreax

    2. 配置 SSH-keys

    参考: 给 VPS 配置 SSH 密钥免密登录 - P3TERX ZONE

    本地生成 SSH 密钥对

    文中提到可以在远端 VPS 上,也可以在本地,这里我选择在本地生成。

    打开终端,输入 ssh-keygen ,连续按四次 Enter (密码设置为空),如果出现了 overwrite(y/n)? 就说明之前就有生成了,你可以选择 y 重新生成一个,或者就用已有的这个

    比如:

    bash
    root@p3ter:~# ssh-keygen # 输入命令,按 Enter 键
    Generating public/private rsa key pair.
    Enter file in which to save the key (/root/.ssh/id_rsa): # 保存位置,默认就行,按 Enter 键
    Enter passphrase (empty for no passphrase): # 输入密钥密码,按 Enter 键。填写后每次都会要求输入密码,留空则实现无密码登录。
    Enter same passphrase again: # 再次输入密钥密码,按 Enter 键
    Your identification has been saved in /root/.ssh/id_rsa.
    Your public key has been saved in /root/.ssh/id_rsa.pub.
    The key fingerprint is:
    SHA256:GYT9YqBV4gDIgzTYEWFs3oGZjp8FWXArBObfhPlPzIk root@p3ter
    The key's randomart image is:
    +---[RSA 2048]----+
    |*OO%+ .+o |
    |*=@.+++o. |
    | *o=.=.... |
    |. +.B + +o. |
    | . + E *S. |
    | o o |
    | . |
    | |
    | |
    +----[SHA256]-----+

    出现那个神秘的矩形就是生成好了

    安装公钥

    在本地终端:

    bash
    ssh-copy-id -pport user@remote

    user 为用户名,remote 为 IP 地址,port 为端口号。

    也可以不加端口号:

    bash
    ssh-copy-id user@remote

    然后按要求输入密码,如果是用 root 登陆的,就是用的初始密码,如果是用上面设置的新用户,那就跟之前设置的用户密码一样

    修改权限

    登入 VPS 后,在远程终端输入:

    bash
    chmod 600 .ssh/authorized_keys

    修改 sshd 配置文件

    打开配置文件:

    bash
    nano /etc/ssh/sshd_config

    找到下面这两行,并改成这样:

    bash
    PermitRootLogin no
    AllowUsers username #如果没有这一行就手动添加
    RSAAuthentication yes #这一行我找不到就没有配置
    PubkeyAuthentication yes

    记得 username 要换成自己设置的名字,也就是上面配置的 jack

    修改完按 Ctrl+o 保存,Enter 确认,Ctrl+X 退出编辑

    重启 ssh 服务

    bash
    systemctl reload sshd

    或者

    bash
    service sshd restart

    禁用密码登陆和改端口

    设置好后,试试看能不能用 ssh 登陆,如果可以,再 sudo nano /etc/ssh/sshd_config 修改配置,禁用密码登陆:

    bash
    PasswordAuthentication no

    修改默认登陆端口

    然后改默认登陆端口1,应该什么数都可以吧,什么 8080,9080,8888,3141……

    找到 Port 22 这行,在下面加你要开的口

    bash
    Port 22
    Port 8888

    加完了之后重启

    bash
    sudo service sshd restart

    打开防火墙并给你设置的端口放行

    bash
    sudo ufw allow 8888
    sudo ufw enable

    sudo ufw status 查看防火墙状态,比如:

    bash
    Status: active
    To Action From
    -- ------ ----
    8888 ALLOW Anywhere
    8888 (v6) ALLOW Anywhere (v6)

    然后重新连接一下 VPS,用设置好的端口登陆看看,如果没问题的话重新 sudo nano /etc/ssh/sshd_config ,注释掉 Port 22 那一行

    3. 安装 ClamAV

    参考:

    安装

    bash
    sudo apt update
    sudo apt install clamav clamav-daemon -y

    更新病毒数据库

    先停止 clamav-freshclam 服务

    bash
    sudo systemctl stop clamav-freshclam

    执行更新:

    bash
    sudo freshclam

    启动clamav-freshclam 服务

    bash
    sudo systemctl start clamav-freshclam

    开机启动

    bash
    sudo systemctl is-enabled clamav-freshclam

    下载 ClamAV 数据库

    先关掉 clamav-freshclam 再下载

    bash
    sudo systemctl stop clamav-freshclam
    sudo freshclam

    查看 clamav 的目录和文件的日期

    bash
    ls /var/lib/clamav/

    限制 Clamscan CPU 使用率

    nice:降低 clamscan 的优先级(限制相对 cpu 时间)。

    bash
    sudo nice -n 15 clamscan

    cpulimit:限制绝对的 CPU 时间。安装 cpulimit

    bash
    sudo apt-get install cpulimit

    使用 cpulimit 来限制 clamscan:

    bash
    cpulimit -z -e clamscan -l 20 & clamscan -ir /

    常见 CLI

    bash
    clamscan /home/filename.docx #扫描特定目录或文件
    clamscan --no-summary /home/ #扫描结束时不显示摘要
    clamscan -i / #打印受感染的文件
    clamscan --bell -i /home #警惕病毒检测
    clamscan -r --remove /home/USER #删除受感染的文件

    ClamAV 返回码

    • 0:未发现病毒。
    • 1:发现病毒。
    • 2:发生了一些错误。

    4. 安装 Fail2ban

    安装 fail2ban 以阻止重复登录尝试

    参考:准备你的机器 - Mastodon documentation

    准备

    更新软件包:

    bash
    sudo apt update
    sudo apt upgrade -y

    安装

    参考:如何在 Ubuntu 20.04 上安装和配置 Fail2ban

    bash
    sudo apt install fail2ban

    安装完后将自动启动,可以用sudo systemctl status fail2ban 查看运行状态

    修改配置:

    打开/etc/fail2ban/jail.local:

    bash
    sudo nano /etc/fail2ban/jail.local

    写入下面的内容,修改邮箱,如果端口改了,也要记得相应修改

    text
    [DEFAULT]
    destemail = your@email.here
    sendername = Fail2Ban
    [sshd]
    enabled = true
    port = 22
    [sshd-ddos]
    enabled = true
    port = 22

    重启 fail2ban:

    bash
    sudo systemctl restart fail2ban

    5. SSL 证书相关

    还没弄明白怎么回事,待更

    参考:

    ]]>
    <![CDATA[CSS · 解决 Chrome 中小于12px的字体不显示的问题]]> https://seviche.cc/2022-03-29-12px 2022-03-29T13:46:29.228Z 2022-08-06T10:50:25.374Z 如设置字体大小为 10.2px

    HTML

    html
    <p>
    I am a frontend developer with a particular interest in making things simple and automating daily tasks. I try to keep up with
    security and best practices, and am always looking for new things to learn.
    </p>

    CSS

    css
    p {
    color: #dcdcdc;
    /*缩小基准大小,也就是缩小后的字体应该是 10.2px=12px*0.85*/
    font-size: 12px;
    /* 缩小比例 10.2px/12px=0.85 */
    transform: scale(0.85);
    /*设置缩放中心*/
    transform-origin: 0 0;
    /*(1-0.85)+1,补上缩小的宽度,这里可以按视觉效果调整一点*/
    width: 118%;
    /*兼容IE*/
    *font-size: 10.2px;
    }

    参考:

    ]]>
    <![CDATA[Miniflux · 保存文章到 Pocket 以及 RSS]]> https://seviche.cc/2022-03-11-miniflux-to-pocket 2022-03-10T16:24:38.663Z 2022-08-06T10:51:23.859Z 将 Miniflux 上的文章到保存到 Pocket/Instapaper,以及 RSS 相关文章和资源

    Miniflux 文档: Integration with External Services

    1. 创建 Pocket Application

    这里 创建一个 Pocket 应用,以获取 Consumer Key

    我的设置如下:

    https://s2.loli.net/2022/02/24/yfXLehkWrisS9Hb.png

    2. 获取 Consumer Key 用户密钥

    My Apps下面找到刚刚创建的应用,复制 Consumer Key:

    https://s2.loli.net/2022/02/24/vO4jyhTfBaHRZ6n.png

    在 Miniflux 后台,设置 → 集成 → Pocket → Pocket 用户密钥(第一栏)中 填入刚刚复制的 Consumer key

    3. 获取 Access Token 访问密钥

    填好后,通过通过点击下面的 连接您的 Pocket 账户 自动获取 Access Token(访问密钥):

    https://s2.loli.net/2022/03/10/tzYeCNksmRaBIFj.png

    点击链接后按 授权https://s2.loli.net/2022/03/11/sZOU8tBpjAJW3ol.png

    这里可能会跳到 http://localhost/integration/pocket/callback 然后就无法访问页面了,解决办法很简单,把 localhost 改为你的服务器 ip 端口或者 miniflux 所在域名即可,如 http://miniflux.com/integration/pocket/callback,按回车会跳回到 miniflux 设置页面。

    出现这个提醒就连接成功了:https://s2.loli.net/2022/03/11/ktoi3lOGjpQHP9B.png

    然后就可以点击文章页面的保存测试看看。https://s2.loli.net/2022/03/11/uCBj6IAWxN149Xo.png

    其他

    1.为博客添加 Pocket 收藏按钮

    此处复制需要的 Pocket 收藏按钮样式,添加到主题的 layout 里面(具体要看不同主题的设置,wordpress 似乎有内置这功能,我不确定,有三种效果。

    2.用 Fever 同步到 Reeder

    1. 在 Miniflux 中创建 Fever 账户和密码
    2. 在 Reeder 中添加 Fever 账号,其中:
    • server:https://miniflux 的网址/fever
    • email:Fever 用户名
    • password:就是 Fever 密码

    3.连接到 Instapaper

    官网:Instapaper

    用户名为 Instapaper 的登录邮箱,设置好更新下就可以了~

    RSS 相关内容

    来都来了,整理一下最近看过的相关内容,因为隐私问题,长毛象上的嘟文暂时不贴(除了我自己的

    1. Miniflux 搭建

    2. 其他选择

    3. RSS 生成

    4. 看什么

    5. 关于 RSS

    ]]>
    <![CDATA[Forty页面仿写]]> https://seviche.cc/2022-03-10-forty 2022-03-10T08:38:17.227Z 2022-08-06T10:51:23.374Z 题目

    来源:百度前端学院|浮动实战任务

    通过 HTML 及 CSS 参考示例图实现页面开发,要求实现效果与示例图基本一致

    • 页面宽度固定(定宽), 请应用 CSS 浮动以及前几天所学的 CSS 样式来完成页面效果
    • 只需要完成 HTML、CSS 代码编写,不需要写 JavaScript

    示例图

    https://s2.loli.net/2022/03/10/V9ZpjQYFvxEDGXJ.jpg

    Demo

    https://forty-seviche.netlify.app/

    耗时:4 小时(还没有做自适应等很多东西……╮( ̄ ▽  ̄"")╭

    ]]>
    <![CDATA[CSS · Tab选项卡]]> https://seviche.cc/2022-03-09-css-tab 2022-03-09T07:42:25.299Z 2022-08-06T10:50:54.161Z 一个纯 CSS 实现的 Tab 选项卡

    原理

    通过隐藏的 input 和与之关联的 label 点击 label 触发 inputchecked 状态触发的,再配合使用元素状态的伪类 :checked 样式就可以实现不同状态的切换,中间的过度效果还可以配合 CSS3 的 transition过度效果实现 1

    代码

    • inputname 都一样,id不同

    HTML

    html
    <div class="tab-frame">
    <!--标签页标题栏-->
    <!-- 设置一个为check -->
    <input type="radio" name="tab" id="tab1" check />
    <label for="tab1">TAB1</label>
    <input type="radio" name="tab" id="tab2" />
    <label for="tab2">TAB2</label>
    <input type="radio" name="tab" id="tab3" />
    <label for="tab3">TAB2</label>
    <!--Tab内容-->
    <div class="tab-content">
    <p>THIS IS TAB1 CONTENT</p>
    <p>Notice the gap between the content and tab after applying background cololr</p>
    </div>
    <div class="tab-content">
    <p>THIS IS TAB2 CONTENT</p>
    <p>Notice the gap between the content and tab after applying background cololr</p>
    </div>
    <div class="tab-content">
    <p>THIS IS TAB3 CONTENT</p>
    <p>Notice the gap between the content and tab after applying background cololr</p>
    </div>
    </div>

    CSS

    css
    /* 隐藏input和tab内容 */
    .tab-frame input,
    .tab-content {
    display: none;
    }
    /* 导航栏样式:未选中时 */
    .tab-frame label {
    color: #555;
    padding: 10px 20px;
    border-bottom: 1px solid #555;
    cursor: pointer;
    float: left;
    }
    /* 导航栏样式:选中时 */
    .tab-frame input:checked + label {
    color: #0f71aa;
    border: 1px solid #555;
    border-bottom: none;
    border-radius: 4px 4px 0px 0px;
    cursor: default;
    }
    /* Tab内容样式 */
    .tab-frame .tab-content {
    color: #0f71aa;
    font-size: 1.5rem;
    font-weight: bold;
    padding-top: 40px;
    clear: left;
    }
    /* 点击的时候显示tab内容,即input checked的时候显示label*/
    .tab-frame input:nth-of-type(1):checked ~ .tab-content:nth-of-type(1),
    .tab-frame input:nth-of-type(2):checked ~ .tab-content:nth-of-type(2),
    .tab-frame input:nth-of-type(3):checked ~ .tab-content:nth-of-type(3) {
    display: block;
    }

    参考

    Demo:Tabs CSS & HTML, no JS & Jquery

    ]]>
    <![CDATA[JavaScript · 打字机效果生成器]]> https://seviche.cc/2022-03-09-typewriter 2022-03-08T16:19:05.137Z 2022-08-06T10:50:29.470Z 题目

    来源:百度前端学院

    参照 打字机效果 DEMO (opens new window),实现一个打字机效果生成器

    需求说明

    • 在输入框中输入需要实现打字机效果的文本
    • 实现原理使用定时器间隔一段时间递增地截取字符串的长度
    • 点击 button 实现打字机效果的生成,将文本输出到 id 为 showText 的标签中
    html
    <label>请输入文本:</label>
    <input type="text" />
    <button onclick="generateTypeEffect()">生成打字效果</button>
    <h2 id="showText"></h2>
    <script>
    function generateTypeEffect() {
    //这里实现打字机效果
    //将内容显示在h2中
    }
    </script>

    解法

    html
    <label>请输入文本:</label>
    <input type="text" />
    <button onclick="generateTypeEffect()">生成打字效果</button>
    <h2 id="showText"></h2>
    <script>
    let i = 0
    function generateTypeEffect() {
    const output = document.getElementById('showText')
    const input = document.querySelector('input').value
    if (i < input.length) {
    output.textContent += input[i]
    setTimeout(generateTypeEffect, 200, ++i)
    }
    }
    </script>

    参考

    How TO - Typing Effect

    ]]>
    <![CDATA[JavaScript · Caesar Cipher 凯撒加密]]> https://seviche.cc/2022-03-09-caesar-cipher 2022-03-08T16:01:08.850Z 2022-08-06T10:50:48.659Z 题目

    来源:操作字符串对象 | 百度前端技术学园

    编码实现凯撒加密算法,根据输入的偏移量,实现对字符串的加密和解密.

    恺撒加密(Caesar cipher),是一种最简单且最广为人知的替换加密技术。明文中的所有字母都在字母表上向后(或向前)按照一个固定数目进行偏移后被替换成密文。

    例如,当偏移量是 3 的时候,所有的字母 A 将被替换成 D,B 变成 E,以此类推。

    需求说明

    • 点击加密按钮,根据用户输入的偏移量,对明文进行加密,加密后的为密文,显示在密文输入框中
    • 点击解密按钮,根据用户输入的偏移量,对密文进行加密,解密出来的为明文,显示在明文输入框中

    解法

    HTML

    html
    <label>偏移:</label>
    <input type="text" name="offset" size="5" value="3" />
    <br />
    <label>
    明文:
    <label></label>
    <input type="text" name="plain" size="50" value="This is a test." />
    <br />
    <label>密文:</label>
    <input type="text" name="enc" size="50" />
    <br />
    <input type="button" value="加密" onClick="encrypt()" />
     
    <input type="button" value="解密" onClick="decrypt()" />
    </label>

    JS

    js
    let offsetInput = document.querySelector('input[name=offset]')
    let plain = document.querySelector('input[name=plain')
    let enc = document.querySelector('input[name=enc]')
    // 加密
    function encrypt() {
    let offset = Number(offsetInput.value)
    function conver(s) {
    let charCode = s.charCodeAt(0)
    // 替换大写字母 A-Z:65-90
    if (charCode <= 90 && charCode >= 65) {
    return String.fromCharCode(charCode + offset < 90 ? charCode + offset : charCode - offset)
    } else {
    //替换小写字母 a-z:97-122
    return String.fromCharCode(charCode + offset < 122 ? charCode + offset : charCode - offset)
    }
    }
    enc.value = plain.value.replace(/[A-Za-z]/g, conver)
    // 替换大写字母 A-Z:65-90
    // function transUpper(s) {
    // let charCode = s.charCodeAt();
    // return String.fromCharCode(
    // charCode + offset <= 90 ? charCode + offset : charCode - offset
    // );
    // }
    // //替换小写字母 a-z:97-122
    // function transLower(s) {
    // let charCode = s.charCodeAt();
    // return String.fromCharCode(
    // charCode + offset <= 122 ? charCode + offset : charCode - offset
    // );
    // }
    // let encUpper = plain.replace(/[A-Z]/g, transUpper);
    // enc.value = encUpper.replace(/[a-z]/g, transLower);
    }
    // 解密
    function decrypt() {
    let offset = Number(offsetInput.value)
    function conver(s) {
    let charCode = s.charCodeAt(0)
    // 替换大写字母 A-Z:65-90
    if (charCode <= 90 && charCode >= 65) {
    return String.fromCharCode(charCode - offset < 65 ? charCode + offset : charCode - offset)
    } else {
    //替换小写字母 a-z:97-122
    return String.fromCharCode(charCode - offset < 97 ? charCode + offset : charCode - offset)
    }
    }
    plain.value = enc.value.replace(/[A-Za-z]/g, conver)
    }

    参考

    ]]>
    <![CDATA[JavaScript · 字符串去重]]> https://seviche.cc/2022-03-07-filter 2022-03-07T13:55:21.090Z 2022-08-06T10:50:50.151Z 题目

    来源:百度前端学院

    js
    /*
    去掉字符串 str 中,连续重复的地方
    */
    function removeRepetition(str) {
    // do something
    }
    // 测试用例
    console.log(removeRepetition('aaa')) // ->a
    console.log(removeRepetition('abbba')) // ->aba
    console.log(removeRepetition('aabbaabb')) // ->abab
    console.log(removeRepetition('')) // ->
    console.log(removeRepetition('abc')) // ->abc

    解法

    js
    function removeRepetition(str) {
    let strArr = [...str]
    const result = strArr.filter((s, i, arr) => s !== arr[i + 1]).join('')
    return result
    }
    console.log(removeRepetition('aaa')) // ->a
    console.log(removeRepetition('abbba')) // ->aba
    console.log(removeRepetition('aabbaabb')) // ->abab
    console.log(removeRepetition('')) // ->
    console.log(removeRepetition('abc')) // ->abc

    如果没有限定条件说是“连续重复”,就可以用 Set

    js
    function removeRepetition(str) {
    let strArr = [...new Set(str)]
    return strArr.join('')
    }
    console.log(removeRepetition('aaa')) // ->a
    console.log(removeRepetition('abbba')) // ->ab
    console.log(removeRepetition('aabbaabb')) // ->ab
    console.log(removeRepetition('')) // ->
    console.log(removeRepetition('abc')) // ->abc

    其他解法

    ]]>
    <![CDATA[Obsidian · 网课学习笔记整理]]> https://seviche.cc/2022-03-07-obsidian-notes1 2022-03-06T16:23:33.118Z 2022-08-06T10:50:46.655Z 最近在用 obsidian 做网课学习笔记,感觉还挺好用的。简单记一下我记笔记的一些方法(其实也不算什么方法)需要用到的插件:Image Auto Upload,用来传图片。

    其实我的记笔记方法很简单,就是不断拆碎重组,方便后面查找。

    上课时,先按时间顺序书写笔记,就像传统的笔记本一样,上完课后再将那一页笔记拆碎重组到知识结构中。方法论大概是 MOC?就是用索引去整理笔记结构,而不是所处文件夹的层次,这里我们先不做深入探讨。

    下面以学习 JavaScript 为例子。

    我近期的笔记目录页面(用 Logseq 发布):JavaScript

    具体的方法

    我把几乎所有的笔记都放在一个叫 Zone 的文件夹内,常用的会打上星标,或者移到最外层文件夹,新笔记默认放在 Zone 文件夹下。

    Step1-构建地图

    MOC 是 Map of Contents,也就是内容地图,所以我们会从构建一张地图出发。刚开始地图不需要太完美,很精细,因为一个不识路的人是没办法认路的,何况是指路、画地图,反正后面也要调整,可以随意一点。

    我刚开始创建了一个叫 JavaScript 的索引页,里面用标题列了几项比较重要的内容,比如 OOP / DOM 之类的,然后在页面最上面列了几项常用的内容:https://s2.loli.net/2022/03/06/ybuxoSJmQGKcAV3.png

    之后会以这一页内容为目录索引,不断补充和修改,构建自己的知识结构

    Step2-写课堂笔记

    首先需要创建一个空白页面。我用 Obsidian 里自带的插件 ZK 卡片 创建,可以自动生成时间戳标题,这个功能可以在设置里打开:

    https://s2.loli.net/2022/03/06/ZXVqaIcS4xy6Evs.png

    然后点击左边功能栏就可以创建并打开了

    https://s2.loli.net/2022/03/06/VQZpj96GbzxhO7i.png

    创建好之后,把这页笔记添加到索引页中,方便后面查找:

    https://s2.loli.net/2022/03/06/Xr9CR7dfekTts5M.png

    然后就可以写课堂笔记了,如果需要在笔记中插入图片,可以使用 Image Auto Upload 这个插件,配合 PicGo 客户端,可以在 Obsidian 里上传图片到图床,非常好用,直接粘贴图片到页面就可以了,具体可以看插件描述。

    https://s2.loli.net/2022/03/06/V8SgsyWqONeYwjB.png

    记笔记的过程没什么特别的,如果提到了一些我还不了解,以后还想深入的话题,我会用 [[ ]] 先标出来, 后面整理笔记的时候看到会留意下。

    Step3-重组笔记

    做完笔记后,将笔记重组。

    布局

    先打开三个窗口,布局如下:

    https://s2.loli.net/2022/03/06/QwSIsWMHUlbZ71r.png

    其中课堂笔记和索引页面需要锁定,这样新打开的窗口就会一直在右下角那个地方,将在这个区域编辑笔记内容。

    结构编辑

    浏览课堂笔记大纲,看下本节课的知识点应该放在索引里的哪里,知识点之间应该是怎样的关系,在索引里用 [[]] 都列出来,简而言之就是画思维导图。我的一个比较粗糙的整理:

    https://s2.loli.net/2022/03/06/bHa4rFvRIB58jeT.png

    这样就可以比较直观地看到哪些内容整理了,哪些没有整理。

    拆分笔记

    然后就可以将左边的笔记拆分整合到右边的索引中了,按住快捷键 CMD,鼠标点击索引里的链接打开新页面,然后在右下部分复制整理。写完一个知识点后可以不用关闭窗口,按住 CMD 然后点击链接,继续在右下窗口编辑笔记。

    看到索引浅色链接(没有创建页面的)都没了,就基本整理完了,可以再看看课程笔记里有没有要补充的。

    然后就整理完啦!之后继续补充索引页面就好了……^_^

    ]]>
    <![CDATA[Airtable · 网页剪藏]]> https://seviche.cc/2022-03-06-airtable 2022-03-06T05:58:29.026Z 2022-08-06T10:50:42.370Z 书签这回事

    上回说到用 自建网页书签 Flare ,今天不小心把 SSH 链接弄坏了(也就是连不上了),因为搭载的服务不多,所以把整个服务器都重装了,Flare 网页书签也炸了。

    其实搭建之后我没有用过(一次都没有),平时的书签管理主要靠搜索,各个浏览器之间的书签互相导入后,直接在搜索栏搜,如果是常用的网址,我用 Chorme 扩展 eesel 来解决,它可以列出最近用过的网页,按站点分类,查找起来很方便

    essel 书签

    最近还推出了一个新功能,可以通过命令进行一下快捷操作,如创建新的 coda 文件、figma 文件等……有点像 Alfred

    https://s2.loli.net/2022/03/06/7ZzGSUrWKR1vm6k.png

    然后还有一个工具叫 Omni 可以做类似的事情,它还可以搜收藏夹,但不知道为什么我的 Chrome 用不了这个,所以也一直没用。

    关于 Airtable

    Airtable 是一个多功能的表格应用,它的表格跟 Notion 里的 Database 挺像的,不过功能更多,用来做网页收藏夹 Free Plan 完全够用。基本的操作可以看这个: 真· Airtable 3 分钟菜鸟入门 - 少数派 ,我没有什么要补充的。

    从去年开始,我开始用 Airtable 整理我的一些收藏夹。原因如下:

    1. 可以分享的表格链接,移动端网页适配也很好
    2. 提供可嵌入网页(如博客)的 <iframe> 代码,样式也可以调整
    3. 方便的 Chrome 拓展,可以智能抓取网页标题和截图、描述
    4. 可以给收藏打 Tag,更好整理,也可以写补充描述/评分等
    5. 多种表格视图(Gallery/Calender/Kanban……)
    6. 美丽: D

    当然这样做也有一些缺点,和其他专门做网页书签的应用不同,Airtable 只是一个「表格」,所以从表格到收藏的网页中去需要点两次,也就是需要打开条目再点一次链接。我把它定义为一个「收藏仓库」,而不是一个随用随取的「文具袋」,我会尽可能详细地描述收藏的条目,以备之后查找和辨识。

    我的书签例子:

    怎么用 Airtable 剪切网页

    我的收藏夹示例: Airtable - About Coding

    1. 创建表格

    至少包含三项内容:

    1. URL:用来放网页的链接
    2. LongText:网页描述
    3. Attachment:放网页截图

    如果需要打开 Markdown 格式支持,需要打开 Enable rich text formattinghttps://s2.loli.net/2022/03/06/7agleEFG5YyNSWU.png

    也可以增加 Tag 和 Categories 分类等其他内容,下面是我建的示例文件:

    https://s2.loli.net/2022/03/06/3IRug7QaOs46vBW.png

    2. 创建 app

    点击右上角的 App → 点击 App an app → 搜 Web clipperhttps://s2.loli.net/2022/03/06/ldpgQ9weHMJctUf.png

    点击 add 添加应用

    https://s2.loli.net/2022/03/06/v2TPpVXMnt4jYx8.png

    然后按提示安装 Chrome 拓展,你可以直接在这里安装: Airtable web clipper

    为剪切动作命名,如直接用表格名字:About Codinghttps://s2.loli.net/2022/03/06/69YEJzKCX5xntP7.png

    然后点击 Add to Extension, 你会看到它出现在了 Web clipper 里面,不过现在先不用管,点击左上角关掉。https://s2.loli.net/2022/03/06/1tiLkpEXqTKJw3o.png

    3. 配置剪切设置

    在 Web clipper 的设置页面(如下),可以调整表格里面各个单元格对应的网页数据,可以按需设置

    https://s2.loli.net/2022/03/06/6FMhjrZR2NSsqOG.png

    其中:

    • Page Title:页面标题
    • Page URL:页面链接
    • Selected text:打开 Web Clipper 时选中的文本
    • Meta tag:The field will be prefilled with the value of the matching meta tag. (不知道是什么
    • Text content by CSS selector:用 CSS 选择文本,会返回第一个符合选择器的文本内容,如 .page-description
    • HTML attribute by CSS selector :结合 HTML 属性选择

    我的设置是:

    • Name——Page title
    • URL——Page URL
    • Attachments——none
    • Description——Selected Text

    4. Web Clipper 剪切

    配置好后就可以开始使用了。在你需要剪切的网页,打开 Airtable web clipper,也就是先前安装的浏览器拓展,点击相应动作,比如刚才创建的 About Coding(如果这个面板有挡到页面内容,可以用鼠标拖动到别的地方)

    https://s2.loli.net/2022/03/06/RTu2xDNn5teqlQP.png

    在 Attachment 里选择附加图片的来源:https://s2.loli.net/2022/03/06/tyU87WD4jsdBHiN.png

    Description 里面的内容可以自己写,也可以在打开 Web clipper 之前先选中,打开后会自动填充进去,如图:https://s2.loli.net/2022/03/06/vPLMNaOlkotWV2Y.png

    最后点击 Add record 就完成啦

    ]]>
    <![CDATA[JavaScript · 十进制数转二进制]]> https://seviche.cc/2022-03-04-decbin 2022-03-04T14:57:48.683Z 2022-08-06T10:50:40.572Z 题目

    来源:“如果”可以“重来” | 百度前端技术学园

    验证工具:在线进制转换 | 进制转换器 — 在线工具

    Task1

    实现当点击转化按钮时,将输入的十进制数字转化为二进制,并显示在 resultp 标签内

    Task2

    • 转化显示后的二进制数为 bin-bit 中输入的数字宽度,例如 dec-number 为 5 ,bin-bit 为 5 ,则转化后数字为 00101
    • 如果 bin-bit 小于转化后的二进制本身位数,则使用原本的位数,如 dec-number 为 5 ,bin-bit 为 2 ,依然输出 101 ,但同时在 console 中报个错。

    解法

    html
    <input id="dec-number" type="number" placeholder="输入一个十进制非负整数" />
    <input id="bin-bit" type="number" placeholder="输入转化后二进制数字位数" />
    <button id="trans-btn">转化为二进制</button>
    <p id="result">运算结果</p>
    <script>
    /////// Task 1
    const btn = document.querySelector('#trans-btn')
    const result = document.querySelector('#result')
    function dec2bin() {
    let decNumber = Number(document.querySelector('#dec-number').value)
    // 判断输入必须为一个非负整数
    if (decNumber < 0 || !Number.isInteger(decNumber)) {
    alert('请输入一个非负整数!')
    }
    // 求余
    let bin = []
    let remainder
    while (decNumber !== 0) {
    remainder = decNumber % 2
    decNumber = parseInt(decNumber / 2)
    bin.unshift(remainder)
    }
    let binNumber = bin.join('')
    ////// Task2
    let binBit = Number(document.querySelector('#bin-bit').value)
    if (binNumber.length >= binBit) {
    binNumber = binNumber.slice(0, binBit + 1)
    } else {
    binNumber = binNumber.padStart(binBit, '0')
    }
    result.innerHTML = `运算结果:${binNumber}`
    }
    btn.addEventListener('click', dec2bin)
    </script>
    ]]>
    <![CDATA[JavaScript · 判断水仙花数]]> https://seviche.cc/2022-03-03 2022-03-03T15:07:14.533Z 2022-08-06T10:50:38.994Z 题目来源: “如果”可以“重来” | 百度前端技术学园

    题目

    根据用户输入的数据,判断水仙花数(三位数),水仙花数是指一个 n 位数 (n≥3),它的每个位上的数字的 n 次幂之和等于它本身。

    html
    <label>请输需要判断的水仙花数(三位数):</label>
    <input type="text" />
    <br />
    <button>开始判断</button>
    <script>
    function numDaffodils(num) {
    // 判断是否为水仙花数
    }
    </script>

    需求说明

    • 当点击 开始判断 按钮,就执行 numDaffodils 函数判断输入的数字是否为水仙花数.
    • 如果是,就弹出提示框提示是水仙花数,如果不是,就提示不是水仙花数
    • 例如输入 153,153=1* 1*1+5*5*5+3*3*3 , 是水仙花数,就提示 153 是水仙花数。
    • 请加入输入判断,必须输入数字,不能输入其他类型。

    解法

    html
    <label>请输需要判断的水仙花数(三位数):</label>
    <input type="text" />
    <br />
    <button>开始判断</button>
    <script>
    const btn = document.querySelector('button')
    function numDaffodils() {
    let num = document.querySelector('input').value
    //输入的是字符串
    // console.log(typeof num); =>string
    // 检查是否是有效数字
    if (num.startsWith('0') || num.length !== 3 || isNaN(num)) alert('请输入三位有效数字')
    // 拆分为数组
    let numArr = num.split('')
    //判断是不是水仙花数字!
    const numCheck = numArr.reduce((acc, value) => acc + Math.pow(value, 3), 0)
    numCheck == num ? alert('是水仙花数 ✅') : alert('不是水仙花数❗️')
    }
    btn.addEventListener('click', numDaffodils)
    </script>

    这里有一个坑:从 <input type="text"> 获取输入内容1,因为 type=text ,所以输出的是 string 而不是 number,不能直接用typeof ==='number'判断输入的是不是数字

    参考

    ]]>
    <![CDATA[自建网页书签Flare]]> https://seviche.cc/flare 2022-02-20T14:04:17.000Z 2022-08-06T10:49:53.655Z Flare 是一个自托管的网页书签导航 (个人理解),详细介绍可以看作者写的这篇:使用 Docker 搭建适用于 HomeLab 的书签导航

    效果如图:https://uneorange.oss-cn-guangzhou.aliyuncs.com/202202152046471.png

    这里分成了“应用”和“书签”两个栏目,但其实都是网页链接书签,看示例文档里的设置,应用里的是使用更为频繁的链接,书签栏则是一些参考链接/外链,或许“应用”命名为“常用”,“书签”则命名为“链接”或者“其他”更好一些。我之前还以为应用是本地应用……(我的问题╮( ̄ ▽  ̄"") ╭

    项目仓库:GitHub - soulteary/docker-flare

    1.创建 Flare 文件夹

    此处我将文件夹命名为 flare

    bash
    mkdir ~/flare && cd ~/flare

    2.下载包含示例的代码

    bash
    git clone https://github.com/soulteary/docker-flare.git
    cd docker-flare

    3.运行容器

    方法 1:直接启动

    bash
    # 可以使用最新镜像
    docker run --rm -it -p 5005:5005 -v `pwd`/app:/app soulteary/flare
    # 也可以追求明确,使用固定版本
    docker run --rm -it -p 5005:5005 -v `pwd`/app:/app soulteary/flare:0.2.10

    方法 2:通过 docker composer

    因为示例文件夹里面已经有一个 docker-compose.yml 文件了,所以我们不需要另外创建,如果需要修改的话可以用 nano docker-compose.yml 编辑 (如果需要设置用户登陆的话,需要在此修改)

    启动容器:

    bash
    docker-compose up -d

    这时我们可以通过 http://ip:5005 访问书签页面了。

    4.修改书签内容

    我用的是 Royal TSX 上的 File transfer 来查看文件夹内容。

    配置文件的路径如下:https://uneorange.oss-cn-guangzhou.aliyuncs.com/202202152023587.png各个文件的功能如下:

    • config.yml:基本应用设置/标题/组件……
    • apps.yml:设置“应用”栏的书签名字/链接/icon/描述
    • bookmarks.yml:设置“书签”栏的分类/链接名称/icon/链接

    作者贴心地内置了 @mdi/font 相关 icon,可以通过 http://ip:5005/resources/mdi-cheat-sheets/ 来访问图标列表,然后通过 Ctrl /CMD+ F 来进行页面内搜索。

    在书签页面,可以通过左下角的齿轮图标调整主题和其他设置,和 config.yml 里的选项几乎一致:https://uneorange.oss-cn-guangzhou.aliyuncs.com/202202152044727.png

    5.用 nginx 反代和设置域名

    安装 Nginx 并打开 flare.conf 文件

    bash
    apt install nginx
    cd /etc/nginx/sites-enabled/
    rm rf default
    cd /etc/nginx/sites-available/
    nano flare.conf

    flare.conf 的内容 (注意修改 example.com 为自己的域名)

    server { listen 80; listen [::]:80; server_name example.com; location / { proxy_pass http://127.0.0.1:5005; }}

    nginx -t 测试配置文件,如果最后有出现 suceessful,那测试就成功了。

    然后为这个配置文件增加一个链接

    bash
    cd /etc/nginx/sites-enabled/
    ln -s ../sites-available/flare.conf flare.conf

    重启 nginx

    bash
    systemctl reload nginx

    刚遇到一些问题,又回到了我的 Miniflux 主页^ ^,遂重启容器:

    bash
    cd ~/flare/docker-flare
    docker-compose down
    docker-compose up -d

    6.SSL 证书和其他书签

    安装证书,详情参考这篇:用 docker 安装 Halo 博客并用 Nginx 反代

    bash
    certbot --nginx -d example.com -d www.example.com

    其他好用书签:

    • Raindrop:可以共享的网页书签,也可以嵌入到网页中,我的白噪音书签:白噪音
    • Airtable:一个共享表格工具,详情可以看我的资源收藏夹:About Coding
    • Guardo:说是可以替代 Raindrop,但书签文件夹不能生成共享链接,所以一直没用。

    再次感谢云五的 WordPress 搭建教程,照葫芦画瓢套用了 Nginx 的设置。

    ]]>
    <![CDATA[用 docker 安装 Halo 博客]]> https://seviche.cc/halo 2022-02-16T14:04:17.000Z 2022-08-06T10:49:49.221Z 系统:ubuntn 20.04

    参考:

    Halo 官网:Halo

    建议大家先去Halo 官网主题仓库看看有没有喜欢的主题再决定要不要装,不然很可能像我一样装了之后又跑路了…… ^^

    准备

    解析域名

    域名生效需要时间,为了避免申请 SSL 证书时屡次失败导致超过申请次数限制,最好提前添加域名解析1,我这里用的是子域名。

    为域名添加一个 A 记录,指向服务器所在 IP。如 Namesile 中:https://uneorange.oss-cn-guangzhou.aliyuncs.com/202202151120134.png

    安装 docker

    在 Ubuntu 中安装 docker2

    zsh
    apt update
    apt install apt-transport-https ca-certificates curl software-properties-common
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
    add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"
    apt update

    其他系统可参考:Plume | 利用 Docker-compose 搭建 Fedi 开源博客平台 – Zoe’s Dumpster.中的安装方式,或者查看官方文档。有的 VPS 在装系统的时候也可以预装 docker,如 vultr/contabo 都有,不过 docker-compose 就需要自己装(这里似乎没有用到 docker-compose。

    检查是否装好,有版本就装好了:

    zsh
    docker -v

    检查端口开放情况

    参考:Ubuntu20.04 开放指定端口_哈-CSDN 博客

    我不太懂端口是怎么开放的,自动开放还是用了就开放。这里用的是 ufw 来设置防火墙开放端口,用 netstat 查看端口占用状况。

    Debian/Ubuntu 中安装 netstat3

    zsh
    apt install net-tools

    检查端口占用:

    bash
    netstat -aptn

    ufw 是 Ubuntu20.04 系统预装的 3,如未安装上,可以这样安装:

    bash
    sudo apt update
    sudo apt install ufw

    常用操作:

    bash
    ufw enable #打开防火墙
    ufw disable #关闭防火墙
    ufw status #查看防火墙规则
    ufw allow 22 #打开22端口
    ufw deny 22 #拒绝访问22端口

    打开防火墙之前最好打开常用的端口,如 22,不然可能会连不上服务器。

    安装 Halo

    参考:使用 Docker 部署 Halo | Halo Documents

    1.创建工作目录

    创建一个文件夹存放 Halo,我这里命名为.halo,当然也可以叫其他的。

    bash
    mkdir ~/.halo && cd ~/.halo

    2.下载示例配置文件到工作目录

    bash
    wget https://dl.halo.run/config/application-template.yaml -O ./application.yaml

    3. 编辑配置文件,配置数据库或者端口

    我不会用 vim 命令,所以这里用 nano 编辑

    打开配置文件 application.yaml

    bash
    nano application.yaml

    修改配置

    配置参考 | Halo Documents这里已经有刚下载好的配置文件了,我们可以根据自己的需要修改,Halo 数据库支持 H2 和 Mysql 数据库,因为我已经安装了一个 Wordpress 博客占用了 Mysql 数据库,虽然不知道有无影响,但为了避免出错,最后选择了按示例里的配置,使用 H2 数据库(主要还是懒得改

    我这里将端口放到 8090,因为原 80 端口已经被占用,大家可以选择其他开放端口,注意修改数据库用户名和密码。

    yaml
    server:
    port: 8090
    # Response data gzip.
    compression:
    enabled: false
    spring:
    datasource:
    # H2 database configuration.
    driver-class-name: org.h2.Driver
    url: jdbc:h2:file:~/.halo/db/halo
    username: admin #数据库用户名
    password: 123456 #数据库密码
    # MySQL database configuration.
    # driver-class-name: com.mysql.cj.jdbc.Driver
    # url: jdbc:mysql://127.0.0.1:3306/halodb?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
    # username: root
    # password: 123456
    # H2 database console configuration.
    h2:
    console:
    settings:
    web-allow-others: false
    path: /h2-console
    enabled: false
    halo:
    # Your admin client path is https://your-domain/{admin-path}
    admin-path: admin
    # memory or level
    cache: memory

    4. 拉取最新的 Halo 镜像

    bash
    docker pull halohub/halo:latest

    5. 创建容器

    bash
    docker run -it -d --name halo -p 8090:8090 -v ~/.halo:/root/.halo --restart=unless-stopped halohub/halo:latest

    6. 打开安装引导界面。

    访问 http://服务器ip:端口号https://uneorange.oss-cn-guangzhou.aliyuncs.com/202202151243869.png

    用 Nginx 反代

    说实话,我现在还没懂 Nginx 是干嘛的,反代又是什么,但好像一般都要有,那就做一下吧。幸好 Halo 还有现成的配置4可以抄一下,结合云五的 Wordpress 搭建教程2,把里面 wordpress 的部分改为 halo 就可以了。

    1.安装 Nginx

    bash
    apt install nginx
    cd /etc/nginx/sites-enabled/
    rm rf default
    cd /etc/nginx/sites-available/
    nano halo.conf

    2.配置 halo.conf

    注意修改www.yourdomain.com 为自己的域名

    upstream halo { server 127.0.0.1:8090; } server { listen 80; listen [::]:80; server_name www.yourdomain.com; client_max_body_size 1024m; location / { proxy_pass http://127.0.0.1:8090; proxy_set_header HOST $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }

    nginx -t测试配置文件,如果最后有出现suceessful ,那测试就成功了。

    然后为这个配置文件增加一个链接

    bash
    cd /etc/nginx/sites-enabled/
    ln -s ../sites-available/halo.conf halo.conf

    3.重启 nginx

    bash
    systemctl reload nginx

    现在访问域名就可以到达 halo 博客主页了,如果不行,可以 ping 一下域名看是不是解析还没生效:如 ping exampl.com

    我这里出现一个问题,是输入域名后到了我的 miniflux 主页,因为 halo.conf 里域名后缀输错了……

    安装 certbot,为域名获取免费 SSL 证书

    有 ssl 证书后,就不会被提示网站不安全了,也就是从 http->https

    1.安装 certbot

    bash
    apt install certbot python3-certbot-nginx

    2.配置证书

    修改 example.com 为自己的域名:

    bash
    certbot --nginx -d example.com -d www.example.com

    如果你不确定是否可以申请成功,或者还在修改/测试配置,可以在后面加--staging 测试,避免申请超过次数限制1

    一些选项,这里最好选择 1,如果选择 2,就没办法用 http 访问域名了:

    text
    1: No redirect - Make no further changes to the webserver configuration.
    2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
    new sites, or if you're confident your site works on HTTPS. You can undo this
    change by editing your web server's configuration.

    然后同意条款后问是否暴露邮箱时选 No2

    其他

    • 主题编辑:首页 / 外观 / 主题编辑 里修改具体文件
    • 进入后台:域名后面加/admin,如:https://yourdomain/admin

    ]]>
    <![CDATA[VS Code 技巧合集]]> https://seviche.cc/vscode 2022-01-27T13:40:28.920Z 2022-08-06T10:49:29.477Z 关闭 VS Code 中的 popup 面板

    如下图所示,在写 JavaScript 的时候,这个东西一直出来,还是蛮烦的。

    https://s2.loli.net/2022/01/17/eElzfuJkH2Tcwv6.png

    解决方法:在 setting 里搜 editor.parameterHints.enabled,取消勾

    https://s2.loli.net/2022/01/17/3fwBTnNZXRUDF8d.png

    参考来源:visual studio code - How do I get rid of this popup in VSCode? - Stack Overflow

    修改字体

    改了 VS Code 里面的字体,Obsidian 里 code block 的字体也一起变了,用了有连字符的字体,箭头变得好好看!

    现在用的是 Fira Code: https://github.com/tonsky/FiraCode

    VS Code 里面可以在 settings.json 里加这行打开连字符: “editor.fontLigatures”: true

    字体推荐:

    快捷键

    1. 按住 alt/option,可以在多个地方同时输入
    2. shift+option/alt+⬇️ 向下复制一行
    3. option/alt +⬇️/ ⬆️ 向下 / 上移动
    4. 在 html 中 输 lorem ,可以生成填充文本

    插件

    • prettier:自动美化格式
    • markdown editor:markdown 预览编辑器
    • foam:双链笔记
    • auto rename tag
    • color highlight
    • image preview:图片预览
    • live server:实时预览
    • front matter:可以用来设置 hugo/hexo 等博客的 frontmatter:

    设置

    • 缩进空格等于的字符数量:tab size
    • 保存:auto save /format on save

    其他

    • 全角半角符号区分很明显,输错全角会有波浪线提醒
    • obsidian 中有一个插件 [Open Valut in VSCode] 可以在 VS Code 里打开 obsidian 的库
    ]]>
    <![CDATA[两种免费发布Axure原型的方式]]> https://seviche.cc/axure 2022-01-21T00:11:17.000Z 2022-08-06T10:51:25.527Z 好像很少看到有人提,这里简单记一下思路,我有用 Vercel 成功试验过,跟本地预览效果是一样的,还可以改域名。不过了解过 Netlify 之后,我觉得用 Netlify 更简单,有兴趣的可以再研究一下。

    Netlify

    需要了解 Netlify 的使用方式

    1. 在 Axure 中导出文档的 HTML 文件:发布>生成 HTML 文件
    2. 注册并托管到 Netlify
    3. 上传 HTML 文件:site -> 拖动 HTML 文件夹到下面的虚线区域

    更新方式:再导入一次更新后的文件包

    Vercel +Github

    需要了解 Github 的基本使用方式和 Vercel

    1. 在 Axure 中导出文档的 HTML 文件:发布>生成 HTML 文件
    2. 创建一个 Github repo,并下载到本地
    3. 复制 HTML 文件到本地 Github repo 的文件夹中,并 commit->push 到云端
    4. 导入相应 repo 到 Vercel 中

    更新方式:复制更新后的 HTML 文件夹到相应的 Github repo 文件夹中,覆盖原来的

    ]]>
    <![CDATA[CSS · Argon主题的CSS修改]]> https://seviche.cc/argon 2022-01-16T14:04:17.000Z 2022-08-06T10:50:02.849Z 然后已经不用 Wordpress 了,这个主题加载太慢了(也可能是我自己的问题

    css
    /*删掉tag图标*/
    .fa-tags {
    display: none;
    }
    /*左侧菜单居中*/
    .leftbar-menu-item {
    text-align: center;
    }
    /*删掉日历图标*/
    .fa-calendar-o {
    display: none;
    }
    /*说说字体统一大小*/
    .shuoshuo-date-date {
    font-size: 15px;
    }
    .shuoshuo-date-month {
    font-size: 15px;
    }
    /*banner字体改为思源宋体*/
    .banner-title.text-white {
    font-family: 'Noto Serif SC', serif;
    }
    /*删掉左侧搜索栏*/
    .card-body.text-center.leftbar-search-button {
    display: none;
    }
    /*删掉左侧栏站点名称*/
    .leftbar-banner.card-body {
    display: none;
    }
    /*作者相关链接居中*/
    .site-author-links a {
    text-align: center;
    }
    /*去掉页脚卡片外形,并缩减边距*/
    #footer.card {
    background: none;
    padding: 0;
    box-shadow: none !important;
    }
    html.darkmode #footer {
    background: none !important;
    }
    ]]>
    <![CDATA[CSS · hover时图片放大的动效]]> https://seviche.cc/hover 2022-01-13T16:03:47.000Z 2022-08-06T10:49:47.292Z 思路:

    1. 设置放大
    2. 隐藏溢出
    css
    .gallery-item {
    overflow: hidden;
    }
    .gallery-item img:hover {
    transform: scale(1.1);
    }
    .gallery-item img {
    display: block;
    width: 100%;
    transition: all 0.4s;
    }

    效果:https://codepen.io/sevichee/pen/wvrRjjq

    ]]>
    <![CDATA[CSS · 三种布局方式]]> https://seviche.cc/css-layout 2022-01-13T10:07:00.000Z 2022-08-06T10:49:54.807Z 课程:Build Responsive Real-World Websites with HTML and CSS

    Overview

    https://s2.loli.net/2022/01/08/SjgmE2nl7TkUAqM.png

    Box-sizing

    https://s2.loli.net/2022/01/09/yCScJXVD9dYIjqK.png

    未定义之前:content-box

    reset

    css
    * {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    }

    Float Layout

    • 浮动在页面之上,对周围元素没有影响
    • 现在很少用了

    left

    • 浮动到页面左边

    https://s2.loli.net/2022/01/08/HZlqu9dyfmG5QYV.png

    css
    .author-img {
    float: left;
    }

    此时图片和文本不在同一个层面上

    浮动到页面右边

    https://s2.loli.net/2022/01/08/ESLcqpbH2VfuNXj.png

    css
    p {
    float: right;
    }

    ABSOLUTE POSITIONING VS. FLOATS

    https://s2.loli.net/2022/01/08/g6Zk2rDAWbxLeoI.png

    Clear Float

    • 当没有其他元素可以 clear float 时,在其后新建一个元素设置 clear
    • 当存在需要清除的元素时,直接在该元素上清除

    方法 1: empty div

    使用一个并列的空<div>元素来清除

    html
    <head>
    <h1>title</h1>
    <h2>title</h2>
    <div class="clear"></div>
    </head>
    css
    /*清除两者*/
    .clear {
    clear: both;
    }
    /*清除左边*/
    .clear {
    clear: left;
    }

    方法 2:clearfix hack

    原理和 empty div 的方式一样,但更简单,利用 pseudo element 在 parent element 后增加一个元素

    html
    <head class="clearfix">
    <h1>title</h1>
    <h2>title</h2>
    </head>
    css
    .clearfix::after {
    clear: both;
    content: '';
    display: block;
    }

    Flexbox

    https://s2.loli.net/2022/01/09/8HiwTDALGbCZQdf.png

    https://s2.loli.net/2022/01/09/RMmiLHxa78n4Atw.png

    加粗字体为默认状态

    css
    .container {
    display: flex;
    align-items: center; /*水平居中*/
    justify-content: center; /*垂直居中*/
    }

    Flex container

    • flex container takes the height of the tallest items

    常用:

    css
    .container {
    display: flex;
    align-items: stretch;
    justify-content: flex-start;
    }

    Flex items

    css
    .items {
    align-self: flex-start;
    }

    Propetities

    order

    数字越大越靠后

    flex-grow

    数字越大占比越大

    flex-basis

    item’s width

    shorthand

    flex:1 =

    css
    flex-grow: 1;
    flex-shrink: 1;
    flex-basis: 0%;

    CSS Grid

    https://s2.loli.net/2022/01/09/6ovHUJOilxqtkjb.png

    https://s2.loli.net/2022/01/09/1vLi2G6okjNzrut.png

    https://s2.loli.net/2022/01/09/krsGJ3AgQpR2Uf9.png

    grid container

    css
    .container {
    display: grid;
    grid-template-columns: 250px 200px; /*两列*/
    grid-template-rows: 200px 200px; /*两行*/
    }

    通常不定义 rows

    Gap

    用 gap 而不是 margin:

    css
    .container {
    /*gap:20px;*/
    columns-gap: 30px;
    row-gap: 20px;
    }

    align tracks inside containers

    when the content is smaller than the grid

    css
    justify-content: center;
    align-content: center;

    grid items

    css
    .items: {
    grid-column: 2/3; /*当前后数值相差只有1位时,可省去后面的数字*/
    grid-row: 2/3;
    /*占几个列/行时*/
    grid-column: 1/3;
    grid-row: 1/3;
    /*或者*/
    grid-column: 1 / span 3;
    /*自动填充剩余空间*/
    grid-column: 1/-1;
    }

    align items inside cells

    css
    .container {
    align-items: center;
    justify-items: center;
    }
    css
    .item {
    align-self: end;
    }

    fr

    • 1fr:自动填充剩余空间
    • 2:1:1:1 的四列
    css
    .container {
    grid-template-columns: 2fr 1fr 1fr 1fr;
    }
    /*shorthand*/
    .container {
    grid-template-columns: repeat(4, 1fr);
    }

    implicit row

    所定义的空间被用完后多出来的列

    ]]>
    <![CDATA[CSS · Add margin to buttons]]> https://seviche.cc/margin 2022-01-13T10:04:21.000Z 2022-08-06T10:49:40.186Z 用 helper class 为单个按钮加 margin,防止元素复用时产生不必要的 margin

    HTML

    html
    <button class="btn helper">Text</button>

    CSS

    css
    .helper {
    margin-right: 1.6rem;
    }

    为按钮增加内边距

    如果设置 border 数值为负,周边空间则会收到影响,但用 box-shadow 就不会:

    所以可以这样设置:

    box-shadow: inset 0 0 0 3px #fff;

    ]]>
    <![CDATA[CSS · Reusable Grid]]> https://seviche.cc/grid 2022-01-13T10:01:44.000Z 2022-08-06T10:49:51.803Z
  • 先设置一个 class 为 grid,并设定 gap
  • 子元素中再设置具体的列数
  • 如设一个上为 2 列和下为 3 列的栅格布局:

    CSS:

    css
    .grid {
    display: grid;
    gap: 1.2rem;
    }
    .grid--2--cols {
    grid-template-columns: repeat(2, 1fr);
    }
    .grid--3--cols {
    grid-template-columns: repeat(3, 1fr);
    }

    HTML:

    html
    <section class="grid grid--2--cols">
    <div>Text1</div>
    <div>Text2</div>
    </section>
    <section class="grid grid--3--cols">
    <div>Text1</div>
    <div>Text2</div>
    <div>Text3</div>
    </section>
    ]]>
    <![CDATA[CSS · Rem in CSS]]> https://seviche.cc/rem-css 2022-01-12T09:50:27.000Z 2022-08-06T10:49:34.711Z 默认为浏览器默认字体大小:

    1rem=16px

    改变 rem 为 10px:

    1. set px

    css
    html {
    font-size: 10px;
    }

    这种方法会让用户不能改变页面字体大小,不建议用

    2. percentage

    设置为用户浏览器字体的大小比例。

    html{
    /* 10px/16px =62.5%*/
    font-size:62.5%;
    }
    ]]>
    <![CDATA[翻译 · Material Design3]]> https://seviche.cc/material 2022-01-03T13:02:17.000Z 2022-08-06T10:49:37.342Z 上个月参与了 Material Design 3 的中文版翻译,我和搭档一起翻译了 FAB (浮动操作按钮) 的部分内容。

    这份翻译还存在一些问题,部分专有词汇还没统一翻译,但大概看看还是可以的。

    相关内容:

    ]]>
    <![CDATA[《用户体验要素》- 战略层(产品目标和用户需求)]]> https://seviche.cc/用户体验要素3 2022-01-02T13:08:29.000Z 2022-08-06T10:50:03.925Z
  • 战略层的基本问题?https://uneorange.oss-cn-guangzhou.aliyuncs.com/202201021315650.png
  • 知道企业与用户双方对产品的期许和目标,有助于促进用户体验各方面战略的确立和制定

    此处的关键词是“明确”

    产品目标

    • 产品目标的设定需要注意什么?

      要想在太具体和太宽泛之间取得一个平衡,我们就应该避免在尚未充分了解问题之前就试图得出结论。所以需要调研

    • 品牌识别为什么很重要?

      对于任何一个网站,它需要明确描述的基础目标之一就是品牌识别( brand identity)品牌识別——可以是概念系统,也可以是情绪反应它之所以重要是因为它无法不被用户注意。在用户与产品交互的同时,企业的品牌形象就不可避免地在用户的脑海中形成了

    • 成功标准( Success metrics)是什么?有什么用?

      即一些可追踪的指标,如印象数、转化率、日活等在产品上线以后用来显示它是否满足了我们自己的目标和用户的需求。好的成功标准不仅影响项目各阶段的决策,也为衡量用户体验工作价值提供了具体的依据。

      • 设定成功标准需要注意什么?对驱动用户体验决策而言有意义的成功标准,一定是可以明确地与用户行为绑定的标准,而这些用户行为也一定是可以通过设计来影响的行为。不是所有的成功标准必须直接由网站获得。你也可以衡量对网站的间接影响任何断章取义的标准都可能造成误导:请务必后退一步,看看除了网站之外发生了什么事,以确定你了解到事情的全貌。

    用户需求

    用户细分( user segmentation)

    • 什么是用户细分?

      将用户分成更小的群组(或细分用户群)每一群用户都是由具有某些共同关键特征的用户所组成。

    https://s2.loli.net/2022/01/02/aVewkFg5Cx1SHPq.png

    • 如何进行用户细分?

      经验或专业程度上的不同就形成了我们细分用户群的基本维度

      • 人口统计学 demographic)
        • 性别、年龄、教育水平、婚烟状况、收入等。
        • 这些人口统计的数据概况可以相当粗略(男性:18~49 岁) 也可以非常具体(未婚、女性、大学毕业、25~34 岁、年薪 5 万美元)。
      • 消费心态档案 psychographic profile描述用户对于这个世界,尤其是与你的产品有关的某个事物的观点和看法的心理分析方法。
    • 细分到什么程度?

      创建细分用户群只是一种用于“揭示用户最终需求的手段”。你真正只需要得到的是和你发现的“用户需求数目”样多的细分用户群。

    • 面对相互矛盾的用户需求应该怎么办?

      很明显,我们无法提供一种方案可以同时满足这两种用户的需求。此时,我们要么选择针对单一用户群设计而排除其他用户群,要么为执行相同任务的不同用户群提供不同的方式。

    用户研究(User Research)

    • 有什么研究工具?如何用?

      问卷调査、用户访谈,或焦点小组) 最适合用于收集用户的普遍观点与感知。用户测试或现场调査)则更适用于理解具体的用户行为以及用户在和产品交互时的表现。

    • 什么是现场调查( contextual inquiry)?

      现场调查是指一整套完整、有郊全面的方法,用于了解在日常生活情境中的用户行为(因而得来此名)又叫行为考古

    • 什么是任务分析( task analysis)?

      任务分析的概念是认为每个用户与产品的交互行为都发生在执行某一任务的环境中。有时任务非常具体(譬如买电影票),而有时任务比较宽泛( 如学习国际商务章程)

    • 可用性的最终目标是什么?

      所谓可用性的最终目标,都是寻找令产品更容易使用的途径

    • 什么是卡片排序法?( card sorting)用途

      对于由信息动的产品,卡片排序法( card sorting)用于探索用户如何分类或组织各种信息元素方法给用户一沓索引卡片,每一张卡片附有信息元素的名字、描述,一张图像或内容的类型。然后用户根据小组或类别,依照自己感到最自然的方式将卡片排列出来。分析几位用户的卡片排列结果,就可以帮助我们了解用户对产品信息的看法常用于信息架构

    团队角色和流程

    • 团队中怎样沟通传递产品目标和用户需求?

      产品目标和用户需求经常被定义在一个正式的战略文档 ( strategy document)或愿景文档( vision document)中。这文档不仅仅是列出目标清单一一它提供不同目标之间的关系分析,并且说明这些目标要如何融入更大的企业环境中去。这些目标和对它们的分析经常由决策者、普通员工,和用户自己的直接意见来支持。这些意见生动地说明了项目中的战略制定问题。用户需求有时被记录在一个独立的用户调研报告中(将所有信息集中在一个地方有某些好处)。

    ]]>
    <![CDATA[CSS · 基础笔记]]> https://seviche.cc/css-fundamental 2021-12-06T03:59:47.000Z 2022-08-06T10:49:56.359Z 一些随堂笔记。

    课程:Build Responsive Real-World Websites with HTML and CSS

    css 是什么

    • CascadingStyleSheets (层叠式样式表)
    • CSS describes the visual style and presentation of the content written in HTML
    • CSS consists of countless properties that developers use to format the content: properties about font, text, spacing, layout, etc.

    分类

    inline CSS

    html
    <p style="color:blue">text</p>

    最好不用

    internal CSS

    放在<head>里面的<style>,如:

    html
    <head>
    <style>
    h1 {
    color: blue;
    }
    </style>
    </head>
    • 代码很长的时候,整理很麻烦

    external CSS

    • 引用单独的 css 文件,如 style.css
    • 放在<head>里用<link>引用,如:
    html
    <head>
    <link href="style.css" ref="stylesheet" />
    </head>

    关于<link> ,可参考:MDN

    组成

    https://s2.loli.net/2022/01/06/Wvj8SKIwGrVaicJ.png

    Selectors

    descendent selector

    css
    footer p {
    font-family: sans;
    }
    article header p {
    color: blue;
    }

    line selector

    css
    h1,
    h2,
    h3 {
    color: blue;
    }

    定义特定元素样式的两种方式:CSS ID、class attributes

    id

    给每个元素一个 id,仅能用一次,尽量不要用

    在 HTML 中:

    html
    <p id="author">text</p>

    在 CSS 中:

    css
    #author {
    font-family: sans;
    }

    class attributes

    能无限复用

    在 HTML 中:

    html
    <p class="author">text</p>

    在 CSS 中:

    css
    .author {
    font-family: sans;
    }

    能无限复用

    在 HTML 中:

    html
    <p class="author">text</p>

    在 CSS 中:

    css
    .author {
    font-family: sans;
    }

    universal selector

    • 对所有元素生效
    • 优先级最低
    • 不可继承
    css
    * {
    color: #1098ab;
    }

    body

    • <body> 里的通常只应用于文本元素(text)
    • Not all properties get inherited. It’s mostly ones related to text: font-family, font-size, font-weight, font-style, color, line-height, letter-spacing, text-align, text-transform, text-shadow, list-style, etc.
    • 可继承

    优先级

    conflicting between selectors

    https://s2.loli.net/2022/01/07/Zl3GX9dhrO2nqMs.png

    • 尽量不要用!important
    css
    foot p {
    color: green !important;
    }
    • 所有 selector 都会被应用,只有冲突的按优先级应用
    • 当使用相同类别的 selector 时,应用最后一个

    inheritance

    • inheriting elements can easily be override
    • 优先级最低
    • body 可继承,universal selector 不可以

    颜色

    RGB/RGBA

    • 基本(r,g,b,alpha)
    • 白色(255,255,255)
    • 黑色(0,0,0)

    Hexadecimal Colors

    • 0 to ff (255 in hexadecimal numbers):#00ffff
    • Shorthand, when all colors are identical pairs#off当需要透明度的时候才用 rgb 颜色,一般用 hex

    Pseudo Class

    用来指定特定元素

    第一个元素/最后一个元素

    css
    li:first-child {
    font-weight: bold;
    }
    li:last-child {
    font-style: italic;
    }

    奇数/偶数 /特定次序

    css
    li:nth-child(odd) {
    font-style: italic;
    }
    li:nth-child(even) {
    font-style: italic;
    }
    li:nth-child(3) {
    font-style: italic;
    }

    多种元素时

    如下所示,当 HTML 中<p>并不是<article>里面的第一个元素时,不生效。

    即当母元素(parent element)里有多种元素时(child elements),不宜使用伪类(pseudo class),可以在列中使用,如<li>

    html
    <article>
    <head></head>
    <p></p>
    </article>
    css
    article p:fist-child {
    font-family: sans;
    }

    四个状态都应定义,并按顺序排列

    不进行交互的预览下

    css
    a:link {
    color: #1098ad;
    }

    visited

    点击后

    css
    a:visited {
    color: #777;
    }

    hover

    悬停时

    css
    a:hover {
    color: orangered;
    font-weight: bold;
    text-decoration: underline dotted orangered;
    }

    active

    点击时(通常和 hover 同时出现

    css
    a:active {
    background-color: black;
    font-style: italic;
    }

    Pseudo Elements

    any pseudo elements is actually an inline element

    在 HTML 中并不真实存在,但仍可在 CSS 中选择,如行内第一个字符:在 HTML 中并不真实存在,但仍可在 CSS 中选择,如行内第一个字符:

    css
    h1::first-letter {
    font-style: normal;
    }

    段内第一行:

    css
    p::first-line {
    color: red;
    }

    adjacent sibiling selector

    在同一 parent element 里,下面最临近的元素

    如 h3 标题下的 p:

    css
    h3 + p::first-line {
    color: red;
    }

    after

    • 就算没有文字内容,也要定义content
    • 位于行末

    https://s2.loli.net/2022/01/08/YZaXGonBRELSuvH.png

    css
    h2::after {
    content: 'TOP';
    font-size: 16px;
    font-weight: bold;
    color: black;
    padding: 5px 15px;
    display: inline-block;
    position: absolute;
    top: -10px;
    right: -25px;
    background-color: #ffe70e;
    }
    h2 {
    position: relative;
    }

    before

    • 位于行前
    css
    h2::before {
    }

    Box Model

    https://s2.loli.net/2022/01/07/hM1AKQwbp5u8gSJ.png

    https://s2.loli.net/2022/01/07/V9SoD43kBH1z5cp.png

    https://s2.loli.net/2022/01/07/pUJWb7iMqgI213G.png

    Type of boxes:

    inline boxes

    https://s2.loli.net/2022/01/08/HhPGpo83LnKMQCJ.png

    block-level boxes

    https://s2.loli.net/2022/01/08/kK4HD6YVsFIqx31.png

    inline-block boxes

    img其实是inline-block box

    img 其实是 inline-block box

    padding

    内边距

    css
    padding: 上下 左右;

    reset margin ane padding

    css
    * {
    margin: 0;
    padding: 0;
    }

    不要用 body

    margin

    外边距

    collapsing margins

    • 当两元素的 margin 重合时,大的会覆盖小的

    Dimensions

    width

    • percentage: percentage of the width of the parents containers

    position

    https://s2.loli.net/2022/01/08/u9G7IJyYWzbdRF4.png

    Normal Flow

    • 默认状态

    Absolute Position

    • 绝对位置
    • 常用于一些小元素
    • parent elements/containers should set to relative
    • 取决于最近层次的 relative parent elements/containers

    https://s2.loli.net/2022/01/08/NIga4hbQOHVfFCr.png

    centering page

    use the <div> element to create a container class, then set the margin-left and margin-right to auto

    css
    .container {
    width: 800px;
    margin: 0 auto;
    }

    基本操作

    CSS 注释

    • /*需要注释的内容*/
    • VS code 快捷键 :CMD+/ (mac) Control+/ (win)
    • 调出开发者工具cmd+i

    Fix bugs

    • 方法:

      • inspect
      • 检查刚开始变得不一样的地方
      • 如果有很多 selector,复杂的那个会被首先应用
      • 检查 css 文件引用链接是否正确
    • 工具:HTML 校对器:Markup Validation Service

      代码比对:Diffchecker

    ]]>
    <![CDATA[HTML · 基础笔记]]> https://seviche.cc/html-basis 2021-12-06T03:59:47.000Z 2022-08-06T10:49:44.906Z 课程:Build Responsive Real-World Websites with HTML and CSS

    HTML 是什么

    HyperTextMarkupLanguageHTML 元素参考 - HTML(超文本标记语言) | MDN

    基本组成

    <p>Hello!</p>为例子:

    1. opening tag:<p>
    2. content : Hello!
    3. closing tag: </p>

    框架

    html
    <!DOCTYPE html>
    <html>
    <head lang="语言代码">
    <meta charset="UTF-8" />
    <title>这是标题的位置,head里面的东西都看不见(一般来说)</title>
    </head>
    <body>
    <h1>一级标题</h1>
    </body>
    </html>

    attributes

    • 用来描述元素 elements
    • pieces of data
    attributes
    srcsources
    alt*alternative text (describe what the image are ) good for SEO/blind people
    width宽度
    height高度
    href超链接 #表示回到页首
    target在哪个窗口打开_blank 新窗口 _self 旧窗口 _top顶部 _parent 父级窗口,如果有窗口层级的话

    tags

    head在页面中不可见的元素,如页面标题、link to css files……
    main文章的主要内容(HTML5 中新增)
    body页面的主要内容
    section区块(semeantic HTMl
    h1标题,一个页面只能有一个 h1
    p段落
    span行内文本
    <!— —>注释
    b加粗,和<strong>不同,它没有语意,是过时的表述
    strong加粗,表示是页面中重要的元素
    i斜体,过时的表述,应用<em>
    em斜体,emphasize
    ol1. order list 数字排序
    ul. unorder list
    lilist item 列 ,用在 ol/ul 里面
    img图片 特殊类型,不需要包含内容,需要 attributes( src/alt/width/height)
    metadata about data meta charset=”UTF-8”
    aanchor 超链接(attribute:href)
    header页头(container
    nav导航 ( container
    menu菜单,web application 常用
    article内容(container
    footer页脚(container
    div无意义内容区块
    br/断行
    aside次级信息/额外信息
    figure常用于 feature cards/coding list……
    figcaption图片脚注(只能在 figure 里面用)
    form表单
    input输入框
    label表单输入标题(也许
    table表格
    thead表头
    tbody表体
    th表头单元格
    trtable row
    tdtable data
    address地址
    s删除
    blockquote引言
    • <button>< a> 的区别:
      • button :一种页面内操作
      • a: 页面跳转

    HTML entity

    特殊符号速查表

    Glyphs | CSS-Tricks

    semantic HTML

    • use semantic elements instead of others, such as <div> / <b>
    • semantic HTML good for SEO and accessibility

    空格

     

    当 div 中用 css 设置了图片时

    html
    <div class="image" role="img" arial-label="description about the image"></div>

    随堂练习

    商品卡片

    分页器 pagination

    ]]>
    <![CDATA[《用户体验要素》- 认识这些要素]]> https://seviche.cc/用户体验要素2 2021-12-06T03:59:47.000Z 2022-08-06T10:50:05.032Z 五个层面
    • 是哪五个层面?分别有什么内容?https://s2.loli.net/2021/12/06/6ghqsX3Bovb1S9N.png

    表现层 (surface)

    • 看得到,可操作
    • 组成: 在表现层( surface),你看到的是一系列的网页,由图片和文字组成。
    • 作用:一些可以点击的图片,从而执行某种功能,例如把你带到购物车里去的购物车图标。

    框架层 (skeleton)

    • 在表现层之下
    • 组成:是按钮、控件照片和文本区域的位置
    • 作用:框架层用于优化设计布局,以达到这些元素的最大的效果和效率一一使你在需要的时候,能记得标识并找到购物车的按钮。

    结构层(structure)

    • 框架是结构的具体表达方式
    • 和框架层的区别:
      • 框架层确定了在结账页面上交互元素的位置,而结构层则用来设计用户如何到达某个页面,并且在他们做完事情之后能去什么地方
      • 框架层定义了导航条上各要素的排列方式,允许用户可以浏览不同的商品分类
      • 结构层则确定哪些类别应该出现在那里。

    笔记:相当于结构层是信息架构,框架层是信息架构所决定的布局。 地图和坐标?

    范围层 (scope)

    特性和功能就构成了网站的范围层( scope)

    战略层 (strategy)

    • 网站的范围基本上是由网站战略层( strategy)所决定的
    • 内容:包括了经营者想从网站得到什么,还包括了用户想从网站得到什么。

    如何建设这五个层面?

    自下而上地建设

    https://s2.loli.net/2021/12/06/W53LrOJkURZT9S8.png

    在上一层完成之前开始下一层

    上下层之间不是互相隔断的让任何一个层面中的工作都不能在其下层面的工作完成之前结束

    https://s2.loli.net/2021/12/06/PTR5yX9H6qUZ3F7.png

    基本的双重性

    什么是网页基本的双重性质?

    网站既不能干脆地分类到应用程序,也不能分类到信息资源

    什么是信息型的媒介类产品?

    • 关注点是信息——网站应该提供哪些信息,这些信息对用户的意义是什么
    • 创建一个富信息( information-rich)的用户体验,就是提供给用户个可以寻找、理解,且有意义的信息组合。

    什么是功能型的平台类产品

    • 关注的是任务——令所在的操作都被纳入一个过程,去思考人们如何完成这个过程。
    • 在这里,我们把网站看成用户用于完成一个或多个任务的一个或一组工具。

    用户体验的基本要素是什么?(五个层次、两种类别的产品)

    https://s2.loli.net/2021/12/06/6xo2IgZcqNm8au1.png

    ]]>
    <![CDATA[《用户体验要素》- 用户体验为何如此重要]]> https://seviche.cc/用户体验要素1 2021-11-29T03:06:10.000Z 2022-08-06T10:50:06.494Z 在 MarginNote 里读了本书,现将其重新梳理一遍。本书共有 8 个章节,预计分 6 个文章写完,这是第一篇。https://i.loli.net/2021/11/29/KYZ2nPxXcCurWyL.png

    什么是用户体验

    用户体验并不是指一件产品本身是如何工作的

    用户体验是指“产品如何与外界发生联系并发挥作用”,也就是人们如何“接触”和“使用”它。

    用户体验设计通常要解决的是应用环境的综合问题

    为体验而设计:使用第一

    “设计一个用户体验良好的产品”作为明确的目标,意味着不仅仅是功能成外观那么简单。

    为什么小而美的产品,更容易成功?

    产品越复杂,确定如何向用户提供良好的使用体验就越困难。在使用产品的过程中,每一个新增的特性、功能或步骤, 都增加了导致用户体验失败的机会

    用户体验和商机

    为什么在网站上,用户体验比别的产品更重要?

    不管用户访问的是什么类型的网站,它都是一个“自助式”的产品。没有可以事先阅读的说明书、没有任何操作培训或讨论会、没有客户服务代表来帮助用户了解这个网站。

    用户所能依靠的只有自己的智慧和经验,来独自面对这个网站的信息一一那么这个网站的主要目标之,就是尽可能有效地传达那些信息

    https://i.loli.net/2021/11/29/RgLjEZAiOQCax8U.png

    为什么用户体验很重要?有什么作用吗

    商机、竞争优势

    提供优质的用户体验是一个重要的、可持续的竞争优势一一不仅仅对网站是这样, 对所有类型的产品和服务也是如此。

    用户体验形成了客户对企业的整体印象,界定了企业和争对手的差异,并且决定定了客户是否还会再次光顾

    什么是转化率?有什么用?

    衡量用户体验效果的指标https://i.loli.net/2021/11/29/scDoiWbHGtNIr4k.png

    转化率通过跟踪有百分之多少的用户被你“转化”到了下一个步骤,就能衡量你的网站在达到“商业目的”方面的效率有多高

    改善用户体验,是为了什么?通过什么方式达到?

    任何在用户体验上所做的努力,目的都是为了提高效率。

    1. “帮助人们工作得更快
    2. “减少他们犯错的几率

    以用户为中心的设计

    什么是“以用户为中心”的设计?

    创建吸引人的、高效的用户体验的方法称为“以用户为中心的设计(user- centered design)”。

    怎么做?

    在开发产品的每一个步骤中,都要把用户列入考虑范围。

    ]]>
    <![CDATA[Evaluation]]> https://seviche.cc/2021-11-18-ux5 2021-11-18T14:09:49.000Z 2022-08-06T10:47:38.841Z 评估
    • 可以确定我们正在改善用户的体验
    • 需要定性、定量的数据

    分类

    形成性评估

    • 在设计低保真原型的过程中进行的
    • 数据:( 例如:完成任务的时间或点击次数)(关注任务)
    • 环境:一个受控环境, 例如, 实验室或办公室

    总结性评估

    • 是在高保真原型或最终界面即将完成的情况下进行的。
    • 数据:设计者能够访问的和分析的数据, 它们可以告诉我们如何使用系统。 例如,用户开始和结束会话时可能会有时间戳, 并记录用户如何与系统交互的数据(真实场景)、
    • 场景:高保真原型可在野外进行,如真实的使用场景

    如何评估

    数据收集

    • 以问卷形式进行的定量数据,
    • 记录用户完成任务时所经过的路径的数据。
    • 用户访谈的形式获得的定性数据

    衡量

    • 衡量任务的目标所达到的程度(完成程度)
    • 包括完成任务的时间、单击次数或   执行任务时出现的错误数。

    可学习性

    • 定义:首次成功完成任务的简单程度。
    • 衡量:完成任务的点击次数、完成任务所需的时间量来获得客观的测量结果, 然后将这些数据与专家的表现进行比较。

    可记忆性

    • 定义:要记住产品用方法的难易程度(如何在反复试验之后在界面上执行给定的任务)
    • 衡量:时间量或点击次数(完成一项反复试验的任务, 以获得可记忆性的测量)

    主观情感评估

    /2021-11-18-ux5/1.png

    • 认知测量:心理负担
    • 情感测量:情感体验

    相关资源

    1. usability evaluation
    2. 15. Usability Evaluation
    3. more than ease of use
    4. Measuring Usability: Are Effectiveness, Efficiency, and Satisfaction Really Correlated?
    5. usability 101|introduction to usability/

    推荐教材

    Trade Book

    • The Design of Everyday Things2013 edition by Don Norman

    Text Books

    • Interaction Design
      • by Jennifer Preece, Helen Sharp and Yvonne Rogers (in alphabetical order).
      • There are four editions of this book. I believe any of the editions are a good purchase. This is the book I use in my undergraduate college course.
    • Human Computer Interaction
      • 2004 by Dix, Finlay, Abowd & Beale.
      • This is an advance text book.
    • Understanding Your Users
      • 2015 edition by Baxter, Courage & Caine.
      • I really like this book because it does what it claims, provides a “practical guide to User Research Methods

    参考网站

    ]]>
    <![CDATA[Prototyping]]> https://seviche.cc/2021-11-18-ux4 2021-11-18T13:26:41.000Z 2022-08-06T10:49:10.016Z 关于 Prototyping

    什么是原型开发

    • 原型开发可以被定义为一个新颖设计的早期模型
    • 通过原型来评估新设计的各个方面 并检查设计是否达到了预期的结果

    原型开发的作用

    • 经济(节约时间的金钱)
    • 快速构建、迭代

    水平原型

    /2021-11-18-ux4/1.png涵盖功能的广度,整合所含的功能(大概有什么)

    垂直原型

    /2021-11-18-ux4/2.png对一些特征进行深入的建模(多层级)

    低保真 Lofi Prototype

    低保真和高保真的区别

    • 低保真原型与最终设计几乎没有什么相似之处,无论是在形式或功能上(快速迭代、验证想法)
    • 高保真原型则与最终设计非常相似

    注意事项

    • 注重用户反馈
    • 也可以让利益相关者加入反馈过程
    • 避免完美主义,注重想法的表达和验证,关注核心路径

    三种形式

    1. 草图

    表明用户场景,痛点是怎么产生的(场景)/2021-11-18-ux4/3.png

    2. 故事版

    /2021-11-18-ux4/4.png

    • 一种以静态方式提供叙事的常用方法
    • 将设计放在上下文中,来考虑用户如何在给定的场景中使用我们的新设计。
    • 场景互动的迭代,简单的任务图形、场景互动

    3.卡片原型

    /2021-11-18-ux4/5.png

    • 表示我们与实际界面可能发生的交互序列。
    • 使用一组索引卡。逐一代表一个用户在尝试完成任务时,会遇到的一序列屏幕。

    其他原型方式

    绿野仙踪/奥茨法师

    Wizard of Oz

    /2021-11-18-ux4/6.png

    • 用来模拟产品的功能。
    • 通过让人执行通常由计算机执行的任务,在这里用户不知道该产品是不具备功能性的
    • 优点:能够节省建立功能性产品的时间和金钱
    • 缺点:
      • 需要相当长的时间来使其正常运转,并且它总是需要多人操作
      • 需要一个训练有素的巫师,最终用户可能对系统有不切实际的期望。
      • 基于欺骗性的功能,反过来可能会限制他们提供反馈,继而限制从收集的数据中得出的推论

    概念验证视频

    Proof of Concept Video

    • 一个能够在各种场景下展示各种用途和系统的功能的视频(系统是如何工作的 以及在什么情况下它会有用)

    隐喻技术

    Metaphor Development

    • 考虑用户如何看待设计
    • 帮助用户建立新设计如何运作的相关心理模型
    • 隐喻将新功能与用户以及熟悉的系统功能相比较对比。

    相关资源

    Resources and Tools for Protoyping 1

    1. prototyping.html
    2. high fidelity prototype/
    3. high fidelity vs low fidelity prototyping web design and app development
    4. Social Mirror

    Tools

    1. Uxrecorder
    2. Invision
    3. Marvel
    4. Axure

    Resources for Prototyping 2

    1. What a prototype is and is not
    2. the skeptics guide to low fidelity prototyping
    3. low fidelity prototype
    4. why every consumer internet startup should do more low fidelity prototyping
    5. lofi vs hifi prototyping how real does the real thing have to be
    6. prototyping types of prototypes
    7. horizontal and vertical prototypes
    8. introduction sketch ui design/
    9. the messy art of ux sketching/
    10. how to draw quick useful ui sketches
    11. User interface sketching tips part-1
    12. storyboarding in the software design process
    13. the 8 steps to creating a great storyboard
    14. users story ux storyboarding
    ]]>
    <![CDATA[Design Alternatives]]> https://seviche.cc/2021-11-14-ux3 2021-11-14T03:18:45.000Z 2022-08-06T10:49:15.281Z Review of Design Goals 审查设计目标

    用户体验的核心目标

    “用户使用界面来完成任务 ”

    通过理解用户以及他们要完成的任务 以便设计出最好的界面(interface)

    前提是,最好的界面 只能在我们了解用户和他们想要完成的任务之后才能被设计出

    设计的定义

    一种创新的开发用来满足一些需求。

    设计的目标

    • 要比现有的设计更好地满足用户的需要
    • 不是为了新奇本身,而是为了服务和改善用户体验
    • 设计新的界面是为了找到更好的方法来调解用户如何实现任务。 它可能需要我们创建一个全新的系统。 或者,我们可以简单地设计出新的输入和输出。

    设计时,需考虑的三个方面

    /2021-11-14-ux3/1.png

    • 个人层面 individual
      • 考虑用户特征。 这些包括用户的年龄,教育水平,和对科技的适应程度,
      • 考虑用户之间的差异。
    • 群体 group
      • 考虑用户之间的交互(一组用户)
    • 社会 society
      • 文化事件会影响系统的使用 和采纳情况。
      • 我们需要了解与用户所从事的任务相关的文化价值观, 并在设计时考虑这些问题

    Design Alternatives 设计替代方案

    目标

    开发一个比现在更能满足用户需求的界面(基于设计的问题空间)

    关键

    使用屏幕实践将设计基础概念化

    Road map 路线

    • 能用 useful

      • 目标

        改善用户完成目标任务的能力

      • 方法

        回顾第一阶段获得的数据 并且鉴别出用户的需求

        1. 显性需求

          /2021-11-14-ux3/2.png

        2. 隐形需求

          /2021-11-14-ux3/3.png

    • 好用 usable

      • 目标

        以功能性和在第一阶段所发现的非功能性的需求为基础,使用户更加有效高效和满意 地完成任务,从而改善他们的用户体验

      • 方法

        回顾第一阶段的数据,确定设计可替代品中功能性和非功能性的需求/2021-11-14-ux3/4.png

        1. 功能性需求

          ——面向用户的

          系统应该完成的,这些基于用户所期望的系统功能

        2. 非功能性需求

          ——面向商家的

          和系统开发有关的限制。它们也可以被看作质量特性( 这些特性包含了许多或后端功能,比如安全性,可执行性,可维护性 但是他们也包括了前端功能货可用特性,像布局、流程或者甚至是语言本地化需求)

    • 小组讨论

      • 头脑风暴
        • 学生或设计师的个人特征, 也会影响到他们发现的需求
        • 在头脑风暴中,最重要的原则是打开思路,不要错过任何点子
      • 想法整理使用类同图表有助于把头脑风暴数据设计成流线型
        1. 参与者是设计师或与设计师相关的人
        2. 在各自的便贴纸上写下想法。
        3. 然后设计师收起这些纸条
        4. 并把他们按照相同程度组织起来,以一些典型的概念为导向。
        5. 之后设计师们来决定什么样的界面   可以满足所有的功能性的需求   一类或者一个同类的需求
    • 界面类型/2021-11-14-ux3/5.png

    ]]>
    <![CDATA[Requirement Gathering]]> https://seviche.cc/2021-11-09-ux2 2021-11-09T14:26:49.000Z 2022-08-06T10:49:17.439Z Overview
    • 设计流程的第一步

      了解用户当前是如何完成任务的

    • 需求收集的目标是什么?

      理解问题域

    • 问题域(problem space)包含什么?

      1. 谁是我们的用户
      2. 什么时候使用产品、在什么地点、为什么、以及他们当前如何实现目标任务
      3. 用户所认为的当前存在的问题是什么
      4. 以及用户自己关于如何改进任务流程的心愿清单
    • 需求收集的陷井

      设计师可能会直接开始设计可供选择的设计品,尽管他们尚未完全了解任务,用户以及用户如何实现任务 换句话说,他们急于直奔主题了 他们在没有取得用户数据的基础上就开始设计了

    • 设计是一个系统性的数据驱动的过程

    Types of Users and Types of Data

    数据类型

    定量

    • 定量数据可以被认为是可以用数字转录的数据, 比如调查数据(用户年龄、性别等等)
    • 定量分析是依据统计数据,建立数学模型,并用数学模型计算出分析对象的各项指标及其数值的一种方法。

    定性

    • 定性数据更容易被认为是为我们提供专题的信息,可放到叙事语境中(如问卷中的开放性问题)
    • 定性分析则是主要凭分析者的直觉、经验,凭分析对象过去和现在的延续状况及最新的信息资料,对分析对象的性质、特点、发展变化规律作出判断的一种方法。

    设计师通常结合使用两种数据,这种方法称为 混合方法(Mixed method approach)

    将用户视为利益相关者的三个类型

    1.主要 Primary

    主要利益相关者是直接使用设计的人员。这些是设计者最常与之互动的用户,他们被称为最终用户

    2.二级 Secondary

    不直接使用设计,但可以间接地使用,因为他们从中可得到某种输出

    3.三级 Tertiary

    可能根本不使用设计 ,但直接受设计的影响, 无论是消极还是积极的方式

    了解利益相关者的意义

    考虑二级和三级利益相关者,也可以帮助我们创造具有创新性的设计,并为我们的客户提供竞争优势。从这个意义上讲了解利益相关者会带来更好的用户体验设计

    Discovery Technique Overview

    四种调研方法

    1. 自然观察 (Naturalistic observation)
    2. 问卷法( Surveys)
    3. 焦点小组( Focus groups)
    4. 访谈 (Interviews)

    交互程度

    /2021-11-09-ux2/1.png

    进行的场景

    /2021-11-09-ux2/2.png

    在设计周期中迭代

    /2021-11-09-ux2/3.png

    Naturalistic Observation 自然观察

    是什么

    是在用户自己的环境中,观察用户的行为,不需要询问用户何时何地以及怎样完成既定任务的 设计师会亲自到用户完成任务的地点,并观察他们的行为。

    如何做

    • 预期计划
    • 实地进行
    • 过程中设计师与用户无交谈

    数据

    定性、定量

    优点

    • 直接观察用户的行为,随时随地收集数据
    • 用户的行为习惯真实自然,不受设计师主观影响

    缺点

    • 数据收集受设计师主观影响。

      设计师所收集到的数据,局限于他们个人的收集方式和理解方式。无法核实设计师做出的 假设正确与否

    • 停留表面,看不到深层原因。

      我们并不知道为什么 用户采取这样的方式完成任务

    道德局限

    需要注意用户隐私、匿名化

    在设计流程中

    基于收集到的数据,进行下一步调研,如问卷、焦点小组等,使问题域更聚焦,探寻真正的问题所在,解释用户行为的原因

    Survey 问卷

    是什么

    • 问卷调查的目的是了解用户的观点。
    • 最常见的是让用户自我报告他们的行为、主观感觉、态度和感受。我们也可以获取他们对于他人的看法

    如何做

    • 实地、实验室
    • 几乎不需要直接和用户交流
    • 形式:纸质问卷、电子问卷、面对面调查

    数据

    1.定性-封闭性问题

    Closed-ended questions

    • 顺序回答
      • 二分型问题 (dichotomous )是/否
      • 李克特评分 (likert scales)评分 1-5
      • 排序 (rank)如按照喜好程度排序
    • 乱序回答
      • 给用户呈现了一系列的问题,这些问题并没有逻辑顺序
      • 例如,人种学问卷:如性别、 教育程度或者是一些列举项,供用户勾选适用于他们的选项

    2.定量-开放性问题

    Open-ended questions

    目的是获取用户简短的回答,包括他们的观点、偏好、态度等

    优点

    • 高效收集数据
    • 数据易于分析

    缺点

    • 信息表面化,不能提供深入洞察
    • 回忆偏倚、社会称许性偏见和样本偏差

    在设计流程中

    下一步组织焦点小组或自然观察,深入洞察

    Focus group 焦点小组

    是什么

    • 目的是让用户参与直接对话
    • 焦点小组在可控的环境中进行,通常是一个房间   在这个充满隐私性的房间里 用户可以自由地分享和讨论自己的观点
    • 焦点小组能够与参与者产生高度互动,这种程度高于   问卷调查,但是低于采访

    如何做

    组成

    • 用户

      • 5-10 个用户
      • 这些用户应能够代表设计师所关注的用户群体
    • 设计团队

      1. 主持人

      • 一个训练有素的主持人 他负责组织用户讨论的话题
      • 主持人必须非常擅长深入挖掘,在讨论过程中出现的有价值的主题。但同时也要引导用户的讨论 使之覆盖所需要的主题范围

      2. 记录员

      • 负责记录用户和主持人之间的谈话要点

      3. 媒介记录员(可选)

      • 使用录音或者   录像设备进行记录
    • 流程

      热身-创新训练-三主题讨论-用户最终意见总结

    数据

    • 定性

      • 对话过程中记录员写下的一系列笔记
      • 完整的焦点小组对话脚本
      • 对焦点小组的分析
    • 定量

      • 可能会在焦点小组一开始进行收集 (例如收集用户的人种学统计信息,或者捕捉关于用户态度的原始问卷调查信息,以及用户对于目标话题的观点)
      • 主持人应当善于运用这些数据快速开始对话

    优点

    • 能够在短时间内收集到关于所感兴趣话题的海量数据
    • 充满活力的小组讨论 或许能够激发出不一样的结果 这些结果是一对一访谈无法得到的

    缺点

    • 需要设计团队去执行
    • 数据收集的质量依赖主持人水平
    • 群体思维

    在设计流程中

    /2021-11-09-ux2/6.png

    • 设计流程中的高潮,应在进行其他与用户交互较少的调研方法后使用(如自然观察、问卷
    • 在焦点小组之后,设计师或许需要采访其他的用户   目的是获取更多关于焦点小组中发现的有趣话题的信息

    Interviews

    是什么

    目的是深度收集用户信息

    如何做

    • 实地、实验室
    • 足够的隐私程度 以确保用户可以自如地回答问题

    数据

    定性>定量

    • 定性
      • 设计师所作的一系列访谈笔记
      • 访谈记录的系统性分析
    • 定量
      • 可以在访谈的开始进行收集
      • 可以以简短问卷的形式进行(它可以用来迅速获取人种学信息,或者 快速了解用户对于访谈话题的态度,访谈者可以利用这些信息开始访谈环节)

    优点

    • 与用户一对一访谈(深度访谈)
    • 灵活的采访提纲

    缺点

    • 需要熟练的采访者

      需要知道用户将会继续说什么,以及 何时控制不再提供价值的对话。 采访者的技能在保持融洽关系方面也很重要, 这种关系应该是既灵活又平衡的。 它使用户舒适, 足以提供诚实的意见 但不能太舒适 而导致用户想用采访者的反应来取悦她。

    • 数据的收集和分析耗时
    • 定性数据分析费力

    在设计流程中

    /2021-11-09-ux2/7.png

    • 据收集策略达到极点时,访谈最有用。

    • 这意味着当问题得到很好的理解时, 设计人员在需求收集流程中处于关键位置, 需要的是用户最终的澄清或见解。

    User Results 用户数据处理

    假设:已对定性和对定量数据进行了适当的分析

    描述性统计信息

    Descriptive statistics

    描述性统计允许我们总结定量信息 这包括数据集的范围、平均值和中值

    1. 范围
    2. 平均值
    3. 中值 (减少极端数据的影响)

    用户特征表

    User characteristics table

    特征表以简单的形式提供了我们所有数据的快速总结,包括定量数据和定性数据

    /2021-11-09-ux2/8.png

    人物画像 Persona

    Presenting Task Findings

    Scenario

    场景

    场景使得我们能了解用户如何使用系统。

    Scenario例子

    是什么

    场景提供了定性和定量数据的描述。

    作用

    • 使我们有机会传达我们在故事线中 收集数据的丰富性
    • 故事线会突出所有任务的重要方面以及目前如何完成任务。
    • 它让任务变的真实,超越了 枯燥的数据图表和表格

    Essential Case Study

    基本案例场景

    帮助理解  用户的活动以及系统的要求

    案例

    三个要素

    1.用户的目标

    • 首先,它用户的目标进行命名。 这捕捉到了 X 的哪些方面。
    • 在本例中 用户可能想知道她去西印度群岛是否需要签证。

    2. 用户意向

    • 她进入系统需要进行的一系列步骤。

    3.系统的责任

    • 用户完成每个步骤后系统必须执行的操作。
    • 所以在这里,用户首先要找到 签证要求然后系统请求获得目的地和国籍。 一旦用户提供所需信息 系统将获得相应的签证等信息。

    Hierarchical Task Analysis

    分层任务分析

    最常见的任务分析技术。  使我们考虑用户当前如何完成任务。关键点是可观察的行为

    /2021-11-09-ux2/11.png

    • 简单的形式就是一个大纲
    • 从目标开始。 并对目标中的每个主要步骤进行编号。 其中一些步骤包含必须先完成的小步骤 然后才能继续下一步 这项技术是关于用户目标的。
    • 与基本案例不同。 我们没有考虑航空公司系统需要在每个用户步骤中做什么。
    • 另外请注意, 我没有将一些系统交互任务列入, 比如使用滚动条。 这只是与用户和完成目标的步骤有关。

    Current UI Critique

    当前 UI 评估

    /2021-11-09-ux2/12.png

    /2021-11-09-ux2/13.png

    • 需要与 UI 进行真实交互。
    • 系统地分析当前正在使用的界面。 你要确认你明确地传达出需求被满足以及 为了改善用户体验你能做的事情。

    步骤

    1.确定用户任务

    对于每个 UI 对应的每个 UI ,你想要确定任务或者用户使用的目的是什么

    2. 测定任务完成时间

    要客观的测定所需时间去 响应这个客户提交的任务

    在这个例子中 我选择了一些点击,它会让我使用这个应用程序,或者网站,或者通过终端完成任务。

    3. 评估操作

    4. 改进方法

    相关资源

    Resources for Requirement Gathering

    1. 25 Useful Brainstorming Techniques
    2. Non-Functional Requirements - Minimal Checklist
    3. Differentiating between Functional and Nonfunctional Requirements
    4. Facilitated Brainstorming
    5. 5 powerful ways to brainstorming with teams/
    6. ips for Structuring Better Brainstorming Sessions
    7. Collaborative Brainstorming for Better UX Workshop
    8. WHAT IS AN AFFINITY DIAGRAM?
    9. Affinity Diagrams
    10. Affinity diagramming
    ]]>
    <![CDATA[Overview of User Experience Design]]> https://seviche.cc/2021-11-08-ux1 2021-11-08T02:54:40.000Z 2022-08-06T10:49:20.887Z 课程简介

    这是一门在 Coursera 上的 UX 课程,共有五个星期的内容,讲了 UX 的基本概念和实际操作流程,主要侧重用户研究方面,也讲了很多和设计心理学有关的内容,很适合 UX 入门。

    我将按照课程内容的划分,分别发布五个 weeks 的笔记,这是第一篇。

    原笔记是在 Notion 上写的,我喜欢用 Notion 里面的 toggle 效果,用“提问”-“答案”的形式来启发思考,但是博客这里用折叠文本比较麻烦,所以直接列出来了

    Week 1 笔记

    什么是 ux?

    User Experience design is design that is user centered。The goal is to design artifacts that allow the users to meet their needs in the most effective efficient and satisfying manner。

    以用户为中心的设计。目标是轻松高效地满足用户的需求。

    用户体验设计的核心概念

    “用户使用界面来完成任务”

    通过理解用户以及他们要完成的任务,以便设计出最好的界面 (interface)

    什么是用户

    这里的“用户”是指使用一些技术来达到目的的个体

    什么是界面

    有输入、输出、系统,于此同时,每个输入都会导致一个期望的输出:有输入、输出、系统,于此同时,每个输入都会导致一个期望的输出个人使用界面的能力与个人特征、群体、社会有关个人使用界面的能力与个人特征、群体、社会有关

    用户体验设计的目标

    /2021-11-08-ux1/3.png

    设计“能用”并且“好用”的界面

    • 能用”的意思是:它可以让用户完成一个任务。在图中,这个系统 (S) 就产生了期望的成果 (Output)
    • 好用”的意思是:用户可以有效高效甚至满意地达成这项任务。在我们的图表中,这需要轻松快速地输入 (Input) 并且输出 (Output) 的结果有效的完成了任务

    界面设计环 (四个步骤)

    /2021-11-08-ux1/4.png

    Step1:Requirements Gathering 收集需求

    • Understanding the user and what her goals are,What are the current practices
    • This step can also be thought of as understanding the“problem space”- what is hindering the completion of the task how can the task or process be improved
    • A whole host of techniques are presented that allow the designer to collect data about the user,her goals and current practices

    Step 2:Design Alternatives 设计方案

    • Once you understand the users,their goals,and their current practices (e.i., the problem space) you are able to take this data and develop various design options that will improve the user experience

    Step 3:Prototyping 原型

    • Techniques for modeling the novel designs before a final version is produced

    Step 4:Evaluation 评估测试

    • A set of techniques for ascertaining that your design meets the needs of the user

    什么样的界面是“实用”的?

    “实用性”指的是有效、高效并且让用户觉得满意的。

    如果用户能够理解怎样的输入 (Input) 会带来所需的输出 (Output) 那么这个界面就是实用的

    好设计的三个特征

    https://i.loli.net/2021/11/08/MtmwlyuXHGgbJPZ.png

    1 - Affordance (示能)

    指一个物理对象与人之间的关系

    (无论是动物还是人类,甚至机器和机器人,他们之间发生的任何交互作用)

    是指可以感知到的事物的实际属性,主要是那些决定事物如何被使用的基本属性 (看看你的智能手机,我们会看到很多种功能,例如按键,可以感知到可以被按。)

    2 - Signifiers (意符)

    能告诉人们正确操作方式的任何可感知的标记或声音。

    示能决定可能进行哪些操作,意符则点名操作的位置。

    3 - Feedback (反馈)

    一些让你知道系统正在处理你的要求的方式

    反馈需要将用户信息发送回来其中包括哪些系统输入的信息已发生它跟我们沟通操作结果

    • 输入:示能、意符
    • 输出:示能

    如何与“用户”接洽

    注意礼貌、着装

    三个步骤

    1 - 介绍

    • 简要概述目标
    • 坦率:让用户知道该会话的目标是什么。询问他们坦率的意见。
    • 保密:说明交互内容是保密的。
    • 自愿:在介绍期间,向用户解释他们的参与完全是自愿的。他们可以随时停止参与。如果他们希望停止参与,这不会消极地影响他们与你的公司或机构的关系。这一部分是很重要的,它能让用户放心并能够不受约束,因为你可能代表一个在社区中具有较高知名度的实体
    • 开放:让他们知道,没有正确或错误的答案,他们应该简单直接地向你提供反馈和意见

    2 - 交流

    • 中立态度、语气轻松专业。
    • 如果形式允许,鼓励详细阐述意外或有趣的信息
    • 保持对交流过程的控制,尽量覆盖所有话题

    3 - 结束

    • 在会话结束时,提醒他们有关交互的目标和您打算如何处理他们提供的数据。
    • 询问他们是否还有需要补充的内容。
    ]]>
    <![CDATA[全球商业化中的设计]]> https://seviche.cc/设计心理学1-7 2021-11-03T03:59:18.000Z 2022-08-06T10:50:07.505Z 章前写了对本书的总结和本章的概要,提到了功能主义以及两种创新方式。

    1.竞争压力

    产品设计会受公司商业竞争压力的影响,这很可能会让设计偏离初衷,使市场产品变得同质化。设计受竞争压力影响的一个表现就是“功能蔓延”,它指的是为产品增加更多功能的倾向。

    面对竞争压力,好的策略是“有所为,有所不为”,专心于已经占据优势的地方,让强者更强。然后聚焦于市场和宣传,大力推广已有的优势。不要盲目跟随潮流,增加无用功能。其中最重要的是关注真正在使用产品的用户的需求。

    2.新技术推动变革

    科技是巨大的变革动力,技术的发展改变了人们做事的方式,但人们的基本需求没有改变。

    3.新产品上市需要多长时间

    那些对生活有重大影响的创新,从概念到上市通常需要数十年,而且有很多的创新在上市之前就已经失败。这是因为这些创新会影响到现有的产品,会产生冲突和矛盾,用户适应新的产品也需要时间,而时间的拉长则会消耗预算和资源。“从概念到成功的产品,好点子需要旷日持久地跨越漫漫长途。”

    4.创新的两种形式:渐进式和颠覆式

    • 渐进式创新:
      • 缓慢的、持续进行的、渐进的
      • 在现有模式上的“改良”
      • 随着时间的发展,可以带来显著的改变
      • “登山法”
      • 大部分创新都是
    • 颠覆式创新:
      • 颠覆式的全新开发
      • 完全改变现有模式
      • 通常来自于技术创新
      • 影响巨大
      • 少之又少,很难存活

    两种都需要

    5. 设计心理学:1988 ~ 2038

    • 人们的基本需求大体上不变,科技的变革提供了截然不同的方式满足人们的需求。
    • 科技的发展,会带来文化上的改变。
    • 人机协同工作才会更强大。新技术改变了人们承担的任务,让人们更专注于自己擅长的东西、关键和重要的东西,让大脑从琐碎中解放出来。

    6.书籍的未来

    新技术的出现,让书籍、视频、音频等交互媒体的制作更加简单,这让入门变得简单,让人们更容易分享、获得信息,但这也会带来信息的泛滥,高技术水平的信息内容仍需要专业的制作。

    7. 设计的道义和责任

    设计应承担社会责任。作者批判了产品的废止制度,还提到可以采取订阅的方式可持续发展。

    8. 设计思维和思考设计

    • 只有当最终产品成功,设计才是成功的。
    • 设计必须考虑用户的整体体验。
    • 设计需要考虑许多影响因素:市场、产品的生命周期、环境成本、技术等
    • 草根的崛起:个人的力量也可以带来改变,科技为此提供了便利。
    • 技术会被改变,但设计的基本原则(比如有关可视性及反馈的原则,示能、意符、映射和概念模型的作用),不会改变,历久弥新。
    ]]>
    <![CDATA[设计思维]]> https://seviche.cc/设计心理学1-6 2021-11-03T03:49:10.353Z 2022-08-06T10:50:08.975Z 这一章和第七章读得有些仓促,加上是比较熟悉的内容,所以笔记比较简短。之后梳理概念的时候再仔细写写。

    1. 解决正确的问题

    设计思维的关键是要解决真正的、根本的问题。可以通过“五个为什么”等方法去确定问题。“以人为本的设计”则是可以应用于解决问题的原则之一,它强调采用满足用户需求和能力的恰当方式去解决问题,这主要是通过四个步骤:观察->创意->打样和测试。

    2. 双钻设计模式

    描述了设计的两个阶段:找到正确的问题,和满足用户需求

    四个步骤(发散->聚焦)

    1. 发现
    2. 定义
    3. 开发
    4. 交付

    3. 以人为本的设计流程

    可以将以人为本的设计原则嵌入到双钻模型中,于是有“以人为本的设计流程https://raw.githubusercontent.com/norevi/image/main/img202110272332820.png

    1.观察

    • 即设计调研。通过调研深入洞察用户的真正需求,并分析用户在真实状况下的行为模式。

    2. 激发创意(构思)

    有很多激发创意的方法,核心的原则是:

    • 激发大量的创意
    • 不要批评,鼓励创意
    • 质疑每一件事

    3.打样(原型)

    奥兹向导”(Wizard of Oz):

    • 一种模拟产品功能的方法
    • 测试时让人执行通常由计算机执行的任务,且用户不知道该产品是不具备功能性的。

    4.测试

    汇集小部分目标用户进行原型测试,作者一般一次单独测试 5 个人,有测试结果后改进方案,接着选择另外五个人再次测试。

    • 原型测试的关键是:快速测试、快速失败

    另外一些提到的概念:A/B 测试、以活动为中心的设计、关键点评审

    4.我刚告诉你什么?那根本行不通

    实践双钻模式和以人为本的设计理念时,会遇到很多现实的冲突。比较好的解决方法是组建跨部门的联合团队,并正视挑战,精心规划设计流程。

    5.设计的挑战

    提到了现在设计过程中常遇到的一些问题,比如有多个互相冲突的需求、缺少团队沟通的设计改动等。还提到了包容性设计/通用设计/无障碍设计的概念和意义。

    6. 复杂是好事,混乱惹麻烦

    复杂跟混乱是不一样的,复杂是“内容多,但有条理”,混乱是“内容多,但没条理”。驯服复杂性最重要的原则之一,是建立一个良好的概念模型。

    7.标准化和技术

    建立统一的标准可以提升产品的易用性,降低人们的学习成本。但建立标准是困难的,在技术没有完善之前建立标准,会让标准容易过时,而过迟建立标准,则很难达成一致。

    8.故意制作困难

    在需要设计限制的产品,比如安全系统、危险设备等,需要故意制造一些使用上的困难。但这并不意味着完全放弃产品的易用性,设计需要基于具体的任务来分析,在需要限制的地方反向利用优良设计的原则,在不需要限制的地方仍然遵循原则。

    • 反向的规则:
    1. 隐藏关键的部位
    2. 在任务执行阶段利用不自然的匹配关系
    3. 增加操作的物理难度。
    4. 要求把握非常精确的操作时机和操作步骤。
    5. 不提供任何反馈信息。
    6. 在任务评估阶段利用不自然的匹配关系,使用户难以了解系统状态。

    9. 设计:为了人类发展科技

    设计是个非凡的学科

    • 产品要被用户使用,这才是对设计最大的奖赏
    ]]>
    <![CDATA[人为差错?不,拙劣的设计]]> https://seviche.cc/设计心理学1-5 2021-10-27T12:22:17.000Z 2022-08-06T10:50:11.112Z
  • 当事故发生时,人们往往会将设计问题归因在操作的人身上,但这并不能解决问题
  • 解决问题的方法:找到根本原因,重新设计系统
  • 1.何以出错

    出错的原因

    • 要求人们在任务和流程中做违背自然规律的事情。
    • 将系统问题归因为人的问题
    • 设计重点放在系统和设备上,忽略了使用者的需求。不了解人的身心特性,按对机器的标准要求人。
    • 时间压力

    解决错误的方法

    • 不要苛责用户,承认存在问题
    • 对问题进行根本原因分析
      • 调查事故,直到发现单一的、潜在的根本原因。
      • 确定是什么致使人们犯错
      • 方法:5 个为什么分析法

    差错之外:故意犯错

    • 有时人们会故意犯错以达到目标
    • 不恰当的规则和流程是违规行为的一个主要原因,它不仅诱使且鼓励了违规,因为没有违规行为就不能完成工作。

    本章主要讨论的是“无意识的犯错”

    2.差错的两种类型:失误和错误

    差错

    • 定义:与普遍接受的正确或合理的行为有所偏离
    • 是所有错误行为的总称
    • 两种类型:失误错误

    差错和行动的七个阶段

    https://uneorange.oss-cn-guangzhou.aliyuncs.com/202110232335757.png

    • 错误发生在高水平的认知(有意)
    • 失误发生在较低层次(无意)
    • 记忆失效可能发生在每个阶段之间的八个转换过程中

    失误

    • 目标、计划正确 ‣ 行动出错
    • 分类
      1. 行动失误
      2. 记忆失误

    行动失误

    • 执行了错误的动作
    • 分类:
      1. 撷取性失误(capture slips)
      2. 描述相似性失误(description-similarity slips)
      3. 功能状态失误(mode errors)
    1. 撷取性失误
    • 如何发生的
      • 某个曾经的动作挤占了需要完成的动作(陌生–更换成->熟悉)
      • 有经验和技巧的人比初学者更容易犯
    • 如何避免
      • 避免有相同的起始步骤,然后再发散的流程。
    2. 描述相似性失误
    • 如何发生的
      • 差错发生在与目标相似的对象上,在错误的对象上执行正确的动作
      • 如果对目标的描述含糊不清,就会发生描述
    • 如何避免
      • 在设计不同目的的控制和显示设备时,设计师需要确认它们之间具有明显差异
    3. 功能状态失误
    • 如何发生的
      • 在错误的模式/功能/状态下执行操作
      • 当设备有不同的状态,而相同的控件具有不同的含义,就可能发生
      • 设备不能显示可见模式时,尤其容易发生
    • 如何避免
      • 避免模式控制的设计
      • 若无法避免,使设备能够明显地显示所激活的功能模式。
      • 设计可以抵消干扰活动对已设定模式带来影响的系统。

    记忆失误

    原文又称:记忆失效性失误

    • 如何发生的
      • 原打算做的行动没有做,或者没有及时评估其行动结果
      • 因记忆问题引起的差错
    • 如何避免
      • 使用最少的步骤
      • 对需要完成的步骤提供生动有效的提醒
      • 使用的强制功能

    3.错误的分类

    错误

    • 目标/计划出错 ‣ 行动随之偏离目的。
    • 分类
      1. 违反规则:基于正确知识->正确分析情况->遵循错误规则->错误行动
      2. 缺乏知识:基于不正确或不完善的知识->错误分析情况->错误行动
      3. 记忆失效:在目标、计划或评价阶段有所遗漏

    1. 基于规则的错误(违反规则)

    • 如何发生的
      • 错误地理解了问题,从而采用错误的目标或计划,导致遵循不恰当的规则。
      • 采用了正确的规则,但规则本身就有问题
      • 采用了正确的规则,但不正确地评估行为的结果。

    在复杂的情况下,太多的信息就是问题所在:信息,既支持决策,也会排斥它

    • 如何避免
      • 将当前系统状态的信息,以易于理解和阐释的方式呈现出来,以及提供必要的说明和解释。

    2. 基于知识的错误(缺乏知识)

    • 处理问题时,人类过度依赖储存在记忆中的经验,而对事物并不进行系统地分析。
    • 如何发生的
      • 当碰到异常情况,没有足够的技能或规则去处理它,人们就会采取基于知识的行为
      • 人们缺乏应对相应情境操作所需知识
    • 如何避免
      • 深入地了解状况,提供程序手册指引
      • 借助适当的概念模型来解决问题。
      • 提供良好的合作解决问题的技能和工具,如机器辅助

    3. 记忆失效的错误

    • 记忆倾向于对一般事物进行过度概括和规范,并且过度强调事物之间的差异。
    • 如何发生的
      • 记忆出错导致遗忘了目标或行动计划,记忆的失误就会导致错误。
      • 某个中断导致人们忘记正在对目前环境状况所做的评判
    • 如何避免
      • 确保所有相关的信息连续可用,如目标、计划和对当前系统的评价
      • 假设人们在行动中可能被打断,在恢复操作时为他们提供需要的帮助。

    4.社会和习俗压力

    压力

    社会和习俗压力影响大,但却难观测。好的方法是“奖励安全“、培训等等

    永远不要低估社会压力对个人行为的影响力量,它可能促使原本理智的人们去做他们即使知道是错误或可能危险的事情。

    社会压力不断出现。它们通常很难被记录下来,因为大多数人和组织都不愿承认这些因素,所以即使在事故调查中发现有社会压力的因素,其结果也往往隐匿不见,得不到公众的仔细监督

    我们需要不同的培训;我们需要奖励安全,并将其置于经济压力之上

    检查清单

    检查清单是个功能强大的工具,经过验证,它可以增加行为的准确性和减少差错,特别是失误和记忆失效

    • 如何使用
      • 人数:通常有两人一起作为一个团队使用检查单:一个人阅读指令,同时另外一个执行命令。
      • 清单设计:不断调整列表直到它涵盖了基本的项目,却不会额外增加负担
    • 隐患
      • 增加更多的人来检查任务 ‣ 增加了出错的机会。
      • 一些专业人士将其被视为对自己专业能力的侮辱
      • 打印清单将顺序结构强加于任务实施->增加了记忆失效的几率 (利用电子清单解决)

    5、差错报告

    减少差错的唯一方法就是直面差错,承认差错存在,并为减少差错而作出改变。三个案例:

    自动化(JIDOKA)

    来源于汽车生产系统

    • 具体:
      • 立即报告差错:当生产线上发现事情出错时->工人立即报告
      • 持续关注:如果有故障的零件要继续移动到下一个工序->通过”安灯”停止装配线,并”报警”
      • 确定原因:技术专家聚集到问题发生区域,探寻差错发生的根本原因

    防呆(POKA-YOKE)

    • 措施之一是添加简单的工具、夹具或设备来限制操作,避免犯错。(简单的示意)
    • 需要遵循的原则:示能,意符,映射和约束,最重要的是强迫功能。

    航空安全报告体系

    主要是讲如何降低人们报告差错时的心理负担

    1. 匿名提交差错报告
    2. 差错真实则豁免处罚
    3. 通过第三方机构提交差错报告检查

    6.甄别差错

    为什么甄别差错是困难的

    甄别失误

    • 缺少反馈
    • 难以检测 (如记忆失效)

    甄别错误

    • 难以识别不恰当的目标->持续行动->行动和目标一致(增加信心)->接近不恰当的目标
    • 所处情境复杂

    记忆失效性(失误/错误)

    区别:

    • 失误:只有计划中的单一部分被漏掉 (做了不该做的)
    • 错误:整个计划都被遗忘了(没有做该做的)

    为错误辩解

    人们常常忽略单一的异常情况,并试图为其辩解。但他们的辩解是基于过去经验的,可能已不适用于现状,这些“简单处理”的辩解会让他们错失挽救错误的良机。

    事故分析要置身于真实情境

    事故发生时

    • 人们常常情绪波动、压力大
    • 所处的情境是复杂多变的
    • 没有明确的线索可以分辨关键的信息

    事故发生后

    • 知道到底发生了什么事,遂将重点放在相关的信息上并忽略不相关的信息。

    事故分析应

    • 调查人员应当想象自己置身于事故的参与者之中
    • 考虑操作者的所有信息,曾经接受的所有培训,以及类似的历史事件

    7.为差错设计

    基本原则

    1. 了解差错的根本原因,通过设计以尽量减少这些诱因。
    2. 进行合理性检验。检查操作行为是否能够通过“一般性常识”的测试
    3. 设计出可以“撤销”操作的功能——“返回”以前操作,或者如果操作不能“返回”,则增加该操作的难度。
    4. 让人们易于发现一定会出的差错,以便容易纠正。
    5. 不要把操作看成是一种差错;相反,帮助操作者正确地完成动作。应该将操作认为近似于预期目的。

    记忆中断

    • 是差错的主要来源,尤其是记忆失效性差错
    • 恢复成本大:必须记得准确的活动被打断之前的状态,目标是什么,被打断的活动处于行为周期的哪个阶段,以及当时系统的状态
    • 多任务处理:效率低,差错更多
    • 可能的方法:设置屏蔽期、自动保存、编辑记录(足迹)等

    警示信号存在的问题

    • 设备间缺乏协同,信号互相影响,让用户分神,从而干扰问题的解决
    • 语音播报:
      • 在用户视觉注意被占用时间,可以传递清楚的信息
      • 环境嘈杂时,很难听清楚
      • 干扰使用者之间的对话

    为差错设计的方法

    研究差错

    • 在差错发生前:研究如何设计预防措施
    • 是差错发生时:研究如何检测并纠正

    增加约束

    • 对操作行为施加特殊的约束。
    • 如:约束条件、强迫性功能和防呆措施、隔离操控、使用分离模块等。

    撤销

    • 减少差错带来的进一步影响
    • 应留有多步撤销

    差错信息确认

    • 突出显示所有即将采取的行动和对象.
    • 突出行动的后果
    • 方法
      • 使正在操作的对象更加显眼。
      • 让操作可逆

    合理性检查

    电子系统可以更方便的定位和确认不合理的操作,但用户并不一定能即时发现错误,在用户进行不合常规的操作时,给用户提醒、确认。

    如:大额转账金额确认

    减小失误

    • 撷取性失误
      • 避免中断
      • 提供恢复帮助(撤销机制)
      • 尽可能让操作流程前面几步不要相似
    • 描述相似性失误
      • 保证操作及其控制尽可能不同
      • 若相似,则在物理距离上越远越好
    • 功能状态失误
      • 去掉多余功能
      • 或 :让功能彼此容易区分和明确可见

    防范失误最好的办法是对正在实施的动作的特性,提供可以感受到的反馈,越是灵敏的反馈越能体现新的结果和状态,再伴之以能够撤销差错的机制

    从差错到事故——瑞士奶酪模型

    https://raw.githubusercontent.com/norevi/image/main/img202110260102695.png

    • 事故的发生往往有很多诱因,任何其中一个原因不出现,事故就不会发生。
    • 如何让系统更加有弹性的方法:冗余设计和多重保护措施
      • 设计额外的差错预防机制,减少失误、错误或设备失效的机会(奶酪上更少的孔),
      • 为系统中不同的零部件设计完全不同的运行机制(努力使奶酪上的孔不要排列起来)。

    我们应该好好思考系统,思考所有可能导致人为失误,进而酿成事故的交互因素,然后,策划出从总体上改进系统,使之更加可靠的方案。

    8.良好的设计还不够

    良好的设计还是难以防范人们故意犯错,差错并不全都因为设计

    9.修补回复工程

    resilience engineering

    是什么

    • 一种应用于工业的系统管理方式
    • 目标是遇到外部冲击时,能以最小的破坏和损失恢复运转
    • 将安全视为核心价值
    • 关注于帮助人们在压力下成功应付复杂的环境以取得成功。

    如何做

    • 设置漏洞、测试工厂的反应水平
    • 反复评估,测试和改进。
    • 要持续关注于预测故障的潜在变化

    10.自动化的悖论

    故障时结果难以估计

    原因:

    当自动化系统发生故障时,经常没有警告,人需要时间去注意问题、评估分析、解决问题。

    悖论

    能够执行那些枯燥乏味、令人厌烦的工作,但是不能做太复杂的工作。

    11.应对差错的设计原则

    人和机器协同应工作

    “人为差错”,往往只是一种人类特性与技术需求不相符的行动

    • 人和机器擅长的工作不同。人类是灵活的,多才多艺且具有创造力。机器是死板的,需要精密设置且相对局限于规定的操作。
    • 人类的能力和技术要求之间存在不匹配,差错不可避免。
    • 设计应正视人和机器之间的差异,考虑到有可能出现的每一个差错,然后想办法避免这些差错,
    • 设法使操作具有可逆性,以尽量减少差错可能造成的损失。

    关键设计原则

    • 将所需的操作知识储存在外部世界,而不是全部储存在人的头脑中,但是如果用户已经把操作步骤熟记在心,应该能够提高操作效率。
    • 利用自然和非自然的约束因素,例如物理约束、逻辑约束、语义约束和文化约束;利用强迫性功能和自然匹配的原则。
    • 缩小动作执行阶段和评估阶段的鸿沟。在执行方面,要让用户很容易看到哪些操作是可行的。在评估方面,要把每一个操作的结果显示出来,使用户能够方便、迅速、准确地判断系统的工作状态。
    ]]>
    <![CDATA[知晓:约束、可视性和反馈]]> https://seviche.cc/设计心理学1-4 2021-10-22T20:21:44.000Z 2022-08-06T10:50:12.946Z 章前

    外界的知识:

    • 示能、意符
    • 显示、操纵的位置
    • 操作和结果之间的匹配关系、
    • 物理约束

    头脑中的知识

    • 概念模型
    • 对行为的文化面向、语义面向和逻辑的约束
    • 现状与以往经验之间的类比

    1. 四种约束因素:物理、文化、语义和逻辑

    物理约束

    • 将可能的操作局限在一定范围内
    • 物品的外部特性决定了它的操作方法
    • 不需要专门培训用户如何使用
    • 例如:电池只有朝一个方向放才可以放进去
    • Tips:重要是解决用户的基本需求,而不是“约束”本身

    文化约束

    • 文化惯例、习俗
    • 以范式的形式存在

      范式(schemas):知识结构,由一般规则和信息组成,主要用于诠释状况,指导人们的行为

    • 会随时间变化
    • 例如:不同地区,打招呼的方式、尺度不同

    语义约束

    • 语义:关注意义
    • 语义约束:利用某种境况的特殊含义来限定可能的操作方法
    • 依据:对现实情况和外部世界的理解
    • 会随时间变化
    • 例如:在不同地方,颜色代表的意义不同

    逻辑约束

    • 如:空间或位置上的逻辑关系约束

    2. 示能、意符和约束在日常用品设计中的应用

    门的问题

    • 我们必须知道要做什么,在什么地方做(面对新事物时的操作问题)
    • 利用示能、约束限制可能的操作、规避不合理的行为
    • 设计不应违反直觉(即用户习惯、思维惯性)
    • 遵守文化规范

    开关的问题

    确定两类基本问题:

    1. 要控制的设备
    2. 映射

    任务分析

    通过对实际任务的细致观察,进行设计过程,从而设计出与实际任务最贴切的方法

    以用户为中心的设计

    基于对实际任务的细致观察,进行的设计过程

    以活动为中心的控制

    • 是什么:对开关所要完成的活动,进行充分仔细地分析和精心设计。以活动为划分功能/开关的依据。
    • 仍需保留手动控制、调整功能

    3. 引导行为的约束力

    强制功能

    是什么

    一种物理约束,是较强约束的极端情况

    作用

    • 防止不当行为
    • 在行动受到限制的情况下,确保出现在某个阶段的差错不会蔓延,能够防止产生进一步的后果

    例子

    需要用钥匙才可以开车

    三种强制方式

    互锁

    • 促使行动按正确的次序进行(先 1>2,互相关联的操作步骤)
    • 例如:必须先关闭洗衣机门,才可以开始启动洗衣机,在洗衣中途打开洗衣机门的话,会强制断电,停止洗衣。

    自锁

    • 自锁保持一个操作停留在激活状态,防止有人过早地停止操作
    • 使某人待在一个空间,或在所需操作完成前防止误操作。
    • (将用户圈定在安全的操作空间)
    • 例如:就像监狱的牢房或婴儿床的围栏,防止一个人离开那个区域。

    反锁

    • 防止某人进入那些危险的区域,或者阻止事情的发生
    • (锁定的是危险的区域,而不是用户)
    • 例如:封锁地下室的入口

    4. 惯例、约束和示能

    • 人们需要通过惯例来感知示能,对预设用途的解释是一个文化惯例
    • 遵循一致性的设计可以减轻用户的学习成本
    • 如果打破惯例得到的益处比遵循惯例得到的多,应选择打破惯例

    5. 水龙头:关于设计的历史案例

    • 标准化是设计的终极基本原则,统一的标准可以简化人们的生活,但达成一致的标准是困难的。
    • 标准应该反应用户心理上的概念模型,而不是实际的物理模型

    6. 利用声音作为意符

    为什么

    • 没有办法提供可视信息时,可以利用声音提供
    • 声音可以帮助提供有用的反馈信息

    应提供什么信息:

    • 声音的来源
    • 设备的工作状态
    • 不可见但对用户有用的信息

    如何提供

    • 使用自然的声音
    • 自然的声音可以反映出自然物体之间复杂的交互作用
    • 当使用人造声音时,使用丰富的声谱,同时小心提供不会恼人但信息丰富的细节
    • 设计标准
    • 报警功能
    • 定位功能
    • 减少骚扰
    • 标准化与个性化

    例子

    汽车的声音是重要的意符

    “拟真”(skeuomorphic)

    • 指将那些将过去的、熟悉的概念融入到新的技术里,即使它们已经不能再发挥功能了。
    • 帮助实现新旧技术之间的过渡
    • 也被翻译为“拟物化
    ]]>
    <![CDATA[头脑中的知识和外界知识]]> https://seviche.cc/设计心理学1-3 2021-10-20T17:08:47.371Z 2022-08-06T10:50:14.322Z 并非精确行为需要的所有知识都得储存在头脑里。它可以分布在不同地方——部分在头脑里,部分在外部世界,还有一部分存在于外界约束因素之中。

    1. 含糊的知识引导精确的行为

    人们不需要完全精确的知识来支撑引导他们的行为。

    原因:

    • 知识存储在头脑和外部世界中 (知识很多)
    • 行动不需要完全精确的知识 (要求不高)
    • 外界世界存在自然约束(选择少了)
    • 头脑中的文化规范与习俗知识减少了选择的范围。(选择少了)

    两种类型的知识:

    1. 陈述性(是什么)->未必是真的
    2. 程序性(怎么做)->通过练习来学习

    • 人们利用环境获得大量备用信息:便利贴、信号灯、指示器,以及利用物品空间位置来提醒事件。
    • 我们习惯寻找特色来区分事物,经验会影响我们如何区分

    2. 记忆是储存在头脑中的知识

    复杂的密码增加了人们记忆的难度,于是人们采用简单粗暴的方式来记下密码,这反而让密码不安全了。更好的方式是使用多种标识符。

    3. 记忆的结构

    两种记忆类型

    1. 短时记忆(工作记忆)

    • 与当下任务有关
    • 容易受干扰
    • 用保留记忆的时常来衡量
    • 记忆难度主要受条目数量影响,一般是 5 ~ 7 个,设计时则要缩减。
    • 设计时可以利用多感官传递信息,减少对用户短时记忆的干扰

    2. 长时记忆(LTM /long-term memory)

    • 存储的是过去的信息
    • 存储和还原需要花费更多的时间和精力
    • 解释信息的方式很重要,记忆的难点在于组织和管理信息
    • 可以被伪造,回溯记忆不是完全准确的,我们只关注自己在意的信息。

    • 合理的信息结构可以帮助理解、简化记忆,为信息的存储和提取建立了路径
    • 设计应提供有意义的信息结构,减少人们需要记忆的分量和难度。“帮助人们记忆的最有效方式就是使人们不需要记忆。”

    4.近似模型:现实世界里的记忆

    • 现实生活中,通常不需要绝对的真理,“差不多“就可以满足人们的需求。
    • 简化的概念模型可以减轻人们的思维负担

    5.头脑中的知识

    外界知识是帮助记忆的有力工具,关键是要在合适的场合、时间

    前瞻记忆

    仅仅指记住在未来某个时间要从事的一些活动这个记忆任务。(记住未来的某一件事)

    未来记忆

    指规划能力,想象未来的能力

    提醒的两个层面:

    1. 信号:有件事要做
    2. 信息:这件事是什么

    理想的提醒方法应兼顾两个层面

    6.外界知识和头脑中知识的此消彼长

    • 存储在外界和头脑中的知识,特性不同,使用的场所也不同
    • 外界的知识可以减少头脑的负担,但使用起来比较低效

    外界的知识

    查找低效、初次使用时易用性高->设计可优化信息查找效率

    头脑中的知识

    查找高效、初次使用时易用性低(需要学习)->设计可构建合理概念模型,简化学习过程

    https://raw.githubusercontent.com/norevi/image/main/img202110202356370.png

    7.多个大脑里和多个设备中的记忆

    • 外界知识结合头脑知识的两种记忆形式:交换记忆、数码记忆
    • 外界知识真假难辨,需要结合头脑中的知识进行辨别

    8.自然映射

    映射是结合外部世界与头脑里知识的最佳案例

    什么是自然映射

    作用于控制与被控制对象之间的,显而易见的映射关系,

    三个层次的映射

    1. 最佳映射:直接对应
    2. 次好映射:位置靠近
    3. 第三好的映射:空间分布一至

    糟糕的映射 VS 好的映射

    • 糟糕的映射:增加记忆负荷和使用者的出错几率(常见错误:缺乏标识)
    • 好的映射:不需要另外的图表和标注购买者通常不是最终用户

    9.文化与设计:自然映射随文化而异

    • 对文化模式的选择将指导相对应的交互设计
    • 受文化影响的一些映射关系:时间流动的方向、书写的方向、前后关系、物品的移动方向等
    • 不同的概念模式切换时,设计会比较困难,用户需要时间学习和适应新的模式
    ]]>
    <![CDATA[日常行为心理学]]> https://seviche.cc/设计心理学1-2 2021-10-20T16:14:47.371Z 2022-08-06T10:50:15.315Z 1.人们如何做事:执行与评估的鸿沟

    当人们使用物品时,会面对两个心理鸿沟:执行的鸿沟、评估的鸿沟

    https://raw.githubusercontent.com/norevi/image/main/img202110141357427.png

    执行的鸿沟:

    • 试图弄清楚如何操作
    • 当运行正常时,会有可见的要素以消除执行的鸿沟(即是容易看得明白是如何操作的)

    评估的鸿沟:

    • 试图弄清楚操作的结果
    • 容易弥合(即可以从多方面获得评估的结果?)
    • 评估的过程反映了努力的程度,人们必须对设备的物理状态做出解释,以便确定是否已经达到自己的期望和意图。
    • 当设备以方便的形式提供了它的状态信息,而且容易阐释,符合用户认知系统的方法,那么评估的沟壑就小。
    • 消除评价沟壑的主要的设计元素:反馈、概念模型。

    如何消除这两个心理鸿沟:

    • 执行的沟壑:意符、约束、映射、概念模型
    • 评价的沟壑:反馈、概念模型

    2.行动的七个阶段

    行动的两个步骤

    执行动作-> 评估结果(解释)

    七个阶段:

    https://raw.githubusercontent.com/norevi/image/main/img202110141358613.png

    执行桥 (目标->外部世界)

    1. 计划
    2. 确认
    3. 执行

    评估桥(外部世界->目标)

    1. 对比
    2. 诠释
    3. 感知

    大部分行动不需要按顺序经历所有阶段,事件行动之间相互影响,可能会有很多行动分支

    行动的分类:

    • 目标驱动型(目标->)
    • 事件驱动型(外部世界->)

    日常行动中许多是机会主义的行动,没有明确的目标

    “根本原因分析”

    反复思索,追问背后的真实原因,如 5w

    3.人的思想:潜意识主导

    https://raw.githubusercontent.com/norevi/image/main/img202110141357967.png

    潜意识

    • 人的行为大多数是潜意识的
    • 毫不费力
    • 基于过去的经验
    • 自动迅速进行
    • 偏重规则和结构
    • 无法区分常见与罕见
    • 不能被蓄意操纵

    有意识的思维

    • 缓慢而吃力
    • 比较、判断、解释

    认知和情感

    • 认知:提供理解
    • 情感:价值判断
    • 关系:
      • 认知引导情绪,情绪影响认知
      • 协同工作

    4.人的认知和情感

    三个层次:本能、行为、反思

    本能层

    • 最基本的处理层、直接的感知
    • 快速的、自发的、潜意识的
    • 对当前状况的反映,不涉及过去
    • 简单评估,不做价值判断例如:”悦耳“的声音,“冰冷”的水 (设计美学)

    行为层

    • 学习能力之本、交互之本,与行动,具体的操作有关
    • 潜意识的
    • 设计:行动应与期望相关联(正价反应、负价反应),方法是建立合理的反馈机制以管理预期。

    反思层

    • 有意识的认知之本,深度而缓慢的
    • 发展深层理解,推理、判断
    • 所产生的情绪最持久
    • 反思的回忆比现实更重要,反思的情感认知会掩盖行为层和本能层带来的影响。

    高层次的反思认知可以触发低层次的情绪。低层次的情绪会引发更高层次的反思认知。

    5.行动的七个阶段和大脑的三个层次

    https://raw.githubusercontent.com/norevi/image/main/img202110202321409.png

    心流

    是什么:完全沉浸在行动中的情感

    会如何:人们会忽略外部时间和空间,有身临其境的感觉

    特点

    • 人们热衷于的
    • 需要一定专注力
    • 适当难度
    • 目标清晰
    • 立即反馈
    • 控制感
    • 行动时,忧虑消失
    • 活动时,主观时间感知改变

    6.自说自话

    人们喜欢为事情建立因果关系

    概念模式

    • 是一种形式的故事
    • 来源于部分事实和经验,不是完全准确的
    • 当外部信息缺乏时,人们靠想象力辅佐建立概念模型

    7.责备错误之事

    • 人们习惯为相继发生的两件事情之间建立因果关系
    • 第一次尝试失败后,人们倾向于重复这个动作(反馈应迅速)

    给用户有保留的预测:

    • 显示最差的预计情况
    • 最长的预估时间

    习得性无助(自责循环)

    • 指在多次经历失败后,便认为自己无法做好某事,结果陷入无助的状态。
    • 人们将不再进行尝试。

    积极心理学

    即一种正面思考的并且自我感觉良好的文化

    tips:

    • 不要责怪用户的错误使用,反思产品问题,提升体验
    • 比“错误处理指南”更重要的是,消除产品本身错误。
    • 直接从帮助和指导信息中纠正问题,不要让用户重新开始。
    • 为用户纠正问题提供应有的帮助。

    8.不当的自责

    自责的原因

    系统差错被归因为人为差错

    设计师应如何

    • 错误发生之前:减少不当行为发生的机会->示能、意符、映射、约束等
    • 错误发生时:让人们发现错误并纠正->反馈、概念模型

    人擅长灵活的工作和创造,机器擅长准确的工作。

    9.行动的七个阶段:七个基本设计原则

    https://raw.githubusercontent.com/norevi/image/main/img202110202327901.png

    前馈

    有助于回答执行类(做)的信息->如何做?

    反馈

    有助于理解发生了什么的信息->发生了什么?

    设计的七个基本原则

    可见性、反馈、概念模型、示能、意符、映射、约束

    ]]>
    <![CDATA[日用品心理学]]> https://seviche.cc/设计心理学1-1 2021-10-20T12:28:00.000Z 2022-08-06T10:50:16.843Z 最近参加了一个知识星球的读书打卡活动,每天需要阅读三个小节并归纳。于是第二次翻开这本书,不过这次决定在微信读书上阅读,划线标注之后再在 flomo 里写打卡内容,等读完了再放到 obsidian 中进行概念梳理。

    以下在 flomo 中总结的内容

    1.复杂的现代设备

    1. 工程师思维强调逻辑而容易忽略用户
    2. 需要同时考虑人和机器
    3. 不同的设计学科有不同的侧重点,列举了工业设计、体验设计、交互设计

    2.以人为本的设计

    1. 以人为本的设计是一种设计理念,不是一门学科,和专业设计角色不同。
    2. 以人为本的设计通过分析用户的需求、能力和行为,用设计来满足用户的需求、能力和行为方式。
    3. 以人为本的设计需要通过观察来理解用户
    4. 快速测试可以方便地找到设计的问题所在。

    3.交互设计的基本原则

    • 五个心理学基本概念:示能,意符,约束,映射、反馈
    • 第六原则:概念模型

    示能

    • 物理对象和人之间的关系、物品的预设用途(能做什么)
    • 示能和反示能都应被揭示出来

    意符

    • 示能的外在表现、符号提示功能 (表明在哪里操作)
    • 简单的物品不应有过多的外在意符(如门)

    约束

    • 对用户操作的限制。(如死锁)

    映射

    • 两组要素之间的关系(如哪个开关开哪个灯)
    • 不同文化之间的映射也许会不同

    反馈

    • 一些让你知道系统正在处理你的要求的方式,即系统和用户的“沟通”(如电话接通之前的嘟嘟声,)
    • 需要即使反馈,只提供必要信息、避免过多反馈。
    • 需要精心策划、考虑优先权

    4.系统映像

    1. 是指设计师提供给用户的适用信息组合,如(说明书、操作说明视频等)

    2. 是一种沟通中介,设计师通过“系统映像”给用户传递产品预设的心理模型,并期望用户基于此建立一致的心理模型。

    5.科技的悖论

    产品的复杂性增加,更难学难用。作者认为最好的方式是建立统一的标准。

    6.设计的挑战

    设计需要跨学科合作,设计管理很重要,需要兼顾平衡多方目标

    ]]>
    <![CDATA[什么是设计洞察 · What is Insight]]> https://seviche.cc/insight 2021-10-15T15:07:14.533Z 2022-08-06T10:49:42.689Z 原文: thrivethinking.com

    insight 不是什么

    • 不是数据
    • 不是简单的事实观测
    • 不是用户的需求陈述(例如: 我需要、我想要)

    是什么

    1. 是揭示本质的

    An unrecognized fundamental human truth that reveals the inner nature of things

    一个未被承认的人类基本真理,揭示了事物的内在本质

    1. 是一种看待世界的新方式

    A new way of viewing the world that causes us to reexamine existing conventions and challenge the status quo.

    一种看待世界的新方式,使我们重新审视现有的惯例并挑战现状

    1. 是对人类行为的深入观察

    A penetrating observation about human behavior thaWhat Is Insight- The 5 Principles of Insight Definitiont results in seeing consumers from a fresh perspective.

    对人类行为的深入观察,导致从一个全新的角度看待消费者。

    1. 是一种对人们潜在行为的发现

    A discovery about the underlying motivations that drive people’s actions.

    对驱动人们行动的潜在动机的发现。

    洞察(Insight)让我们去思考人们为什么做事,是什么阻碍了人们做事情,它更深入问题根源,而不是停留在表面

    五大要素

    语境/困境/动机/原因/设想https://thrivethinking.com/wp-content/uploads/2016/03/WhatIsInsight_5.jpg

    定义 Insight 的步骤

    1.设定语境/场景

    A simple observation of how people behave in a given situation, what they think, what they feel, but most importantly explain what they are doing and trying to achieve

    一个关于人们在给定的情境内如何想、如何做、如何感受的观察陈述。重要的是要说明他们在做什么以及想做什么。

    2.沟通困境

    understanding the barriers that are stopping consumers from achieving what they want to achieve with a given product, service or experience

    了解消费者通过特定产品、服务或体验实现其愿望时遇到的障碍。

    3. 阐明原因

    An insight statement is a discovery of understanding and the identification of unmet needs to explain why something is happening the way it is

    洞察力声明是对理解的发现和对未满足需求的识别,以解释为什么事情会以这样的方式发生.

    You must know the reason a consumer is behaving in a particular way, and why it is happening if you are to develop a product or service that can in some way augment the behavior or change it.

    你必须知道消费者以特定方式行事的原因,以及为什么会发生这种情况,如果你要开发一种产品或服务,以某种方式增强这种行为或改变它的话。

    4. 捕捉动机

    Discovering the underlying motivations that drive people’s actions

    发现推动人们行动的潜在动机

    End-users of a product or service are motivated to change by the tensions that exist in their lives.

    产品或服务的终端用户被他们生活中存在的紧张关系所激励而改变

    Look for tensions in four key areas: the physiological, the emotional, the cognitive and the environmental to inform your insight statements.

    寻找四个关键领域的紧张关系:生理的、情感的、认知的和环境的,

    5.构想理想

    It is important to describe the desired end-state or situation the consumer is seeking.

    描述消费者所追求的理想的最终状态或情况是很重要的

    The key here is not to define a solution but clearly convey how the consumer would like the world to look and feel, what the ideal experience should be.

    此处的关键在于不需要定义一个清晰的解决方案,而需要清楚地传达消费者希望世界看起来和感觉如何,理想的体验应该是什么

    An excellent way to articulate this is to start with the statement “I wish there was,

    一个很好的表达方式是以 “我希望有 “开始

    如何表达

    Think of insight definition as a three sentence journey that takes the reader through the consumer’s situation, frustration, and future desires.

    将洞察定义视为一个三句话的旅程,带领读者了解消费者的情况、挫折和未来的愿望。

    第一句话——描述现状和用户行为。

    Having pictures around that instantly remind me of special moments and people, makes me feel good.”

    “身边有照片,能让我立即想起特别的时刻和人物,让我感觉很好。”

    第二句话——描述用户的困境,并说明造成困境的原因。

    “But I find that pictures from my digital camera often stay hidden on my devices because I never have time to print them.”

    “但是我发现我的数码相机里的照片经常被藏在我的设备里,因为我从来没有时间去打印它们。”

    第三句话——描述用户期望的最终理想状况。

    “I wish there was a way to enjoy them everyday without having to actively play them on my TV or computer.”

    “我希望有一种方法可以每天欣赏它们,而不必在电视或电脑上主动播放它们。”

    Tips

    • 用第一视角书写
    • 真实、人性化的口吻
    • 客观、真诚

    什么是一个好的’Insight’

    • 它在情感层面与消费者建立联系,并引起 “你显然理解我 “的反应。
    • 它重新审视现有的惯例并挑战现状。
    • 它解决了一个真正的问题,从而产生了新的客户。
    • 它通过给你一个明确的目标来激发行动。
    • 它清楚地说明了下一步该做什么以及如何为你的客户提供价值。
    • 它为团队带来了新的感知水平,并有助于做出明智的决策。

    Insight->How Might We?

    需要把 insight(洞察)转变为 HMW(我们如何?)

    例如:

    “我们可能如何每天享受我们的记忆,而不必花时间在设备上主动播放它们?”

    后记

    • insight 在作品集中如何表达?看到有 Insight 只写一句话的,总结为’Key insights’,内容上是关键的事实发现,而跟前文所述的这种深入的洞察不同,那是否存在两种 insight 的定义写法?
    • 打算把 Insight 放到 Persona 后面,一个 Persona 对应一个 insight 和一个 How Might We.
    ]]>