MacOS 解压软件 keka
2022-06-30 software mac 1 mins 181 字
用了10年Mac了,竟然还不知道怎么在 Mac 下解压rar,我一定是个假的果粉。keka 现在在用着,还不错,官网下载免费,在app store上下载收费。https://www.keka.io/
用了10年Mac了,竟然还不知道怎么在 Mac 下解压rar,我一定是个假的果粉。keka 现在在用着,还不错,官网下载免费,在app store上下载收费。https://www.keka.io/
作为一个本职工作是基础设施开发的后端人员,我将js比作linux系统,将vue比作docker/containerd,而nuxt就相当于kubernetes了。当然这个类比并不是非常准确。前端比容器大战要柔和的多,感觉大家都在底层上重复建设。对比容器技术,swarm/k8s/mesos也就5年左右就分出了胜负。前端目前主流还是react/vue/Angular这三家,基于这三家之上又有各自的服务端框架(更底层typescript倒是已经统一了),导致前端学习门槛较高,框架概念、设计模式其实也走了一遍后端框架的老路,很多概念后端开发可以平移过来。我比较相信Taylor Otwell(Laravel的创造者),他说vue好,所以我就跟着他一起用了。
前两篇记录了点vue2和vue3的笔记,这一篇记录下nuxtjs3,这是官网,2022 年 4 月 20 日正式发布 nuxt3,代号为“Mount Hope”。
Nuxt3是基于 Vue3 发布的 SSR 框架:
这是官网显示的一些优势说明:
npx
是npm从5.2版开始增加的命令
npx nuxi init nuxt3-test # 快速创建
npm install
npm run dev
生成文件如下:
这里要重点提的是 nuxt.config.js
文件,所有配置都在里面。
一些常用的目录如下:
- pages // 页面目录,Nuxt.js 框架读取该目录下所有的 .vue 文件并自动生成对应的路由配置。
- components // 组件目录
- assets // 静态资源目录
- public // 不需要 vue 处理的静态资源,此类文件不会被 Nuxt.js 调用 Webpack 进行构建编译处理。 服务器启动的时候,该目录下的文件会映射至应用的根路径 / 下。
- layouts // 项目布局目录
- common // 存储js文件
- plugins // nuxt扩展的js文件
- store // vuex的配置文件,Nuxt.js 框架集成了 Vuex 状态树 的相关功能配置,在 store 目录下创建一个 index.js 文件可激活这些配置。
配置参考链接:
删除根目录下的app.vue,然后pages目录下新建index.vue文件,就会自动关联vue-router,支持动态路由。
因为我是容器化环境开发的,所以改了一下运行端口到80端口,访问方便一些。如果发现配置不生效,可以尝试重启一卡容器(多个容器在同一个目录下启动npm run时感觉会冲突,就遇到有些配置不生效了)。
在setup
函数中,可以useHead
使用与元标记对应的键的元属性对象进行调用:title
, titleTemplate
, base
, script
, style
, meta
and link
, 以及htmlAttrs
and bodyAttrs
。还有两个速记属性charset
和viewport
。
Nuxt 提供useFetch
、useLazyFetch
和useAsyncData
来useLazyAsyncData
处理应用程序中的数据获取。
useState
是一个对 SSR 友好的ref
替代品。它的值将在服务器端渲染后(在客户端水化期间)被保留,并使用唯一键在所有组件之间共享。
Nuxt 会自动扫描 、 和 目录中的文件~/server/api
,~/server/routes
以~/server/middleware
注册具有 HMR 支持的 API 和服务器处理程序。
每个文件都应该导出一个用defineEventHandler()
.
Nuxt使用了自动加载路由的方案,他会根据你项目的目录来自动创建router对象;
一个简单的示例,pages下的目录示例及其对应的路由如下所示:
test:
a.vue -- 对应路径:/test/a
index.vue -- 对应路由:/test
index:
_id:
a.vue -- 对应路由:/{id}/a
index.vue -- 对应路由:/{id}
index.vue -- 对应路由:/,会被包含到上一级的index.vue的<nuxt-child>中
index.vue -- 对应路由:/,其中如果包含有nuxt-child元素,那么会将index目录下的index.vue加载到nuxt-child元素位置
_id
设置为必选的路由,需要在 users/_id 目录内创建一个 index.vue 文件。Nuxt.js 可以让你在动态路由组件中定义参数校验方法。
举个例子: pages/users/_id.vue
export default {
validate ({ params }) {
// 必须是number类型
return /^\d+$/.test(params.id)
}
}
如果校验方法返回的值不为 true或Promise中resolve 解析为false或抛出Error , Nuxt.js 将自动加载显示 404 错误页面或 500 错误页面。
如果您不知道URL结构的深度,您可以使用_.vue动态匹配嵌套路径。这将处理与更具体请求不匹配的情况。
文件结构:
pages/
--| people/
-----| _id.vue
-----| index.vue
--| _.vue
--| index.vue
一些代码只是为了做代码的结构备忘,不代表逻辑正确。
前端的语法糖太多了,我觉得这些语法糖是前端晦涩的一大原因。
不过,“无他,但手熟尔”,在前端也是一样。
命令行nuxi
npx nuxi init nuxt3-test # 快速创建
layout 布局 html(静态)
layout文件夹
page文件夹
引入 css
<style scope>
.abc {}
</style>
应用状态state
<script setup>
const options = reactive({
xxx: "xxx"
});
</script>
<template>
<button class="abc" :class="options.xxx === 'xxx' && 'option-active'">abc</button>
</template>
a标签替换为 NuxtLink 标签
<NuxtLink to="/">Home</NuxtLink>
简单的typescript
我觉得typescript的语法有点像golang。 看着也挺舒服的~
先定义个interface类型,一些选项/常量用enum定义,再const一个实例。
<script setup lang="ts">
interface OptionState {
xxx: string,
yyy: string
}
enum Xxx {
AAA = "aaa",
BBB = "bbb"
}
const obj := OptionState{
xxx: Xxx.AAA,
yyy: Xxx.BBB
}
const options = reactive<OptionState>({
xxx: Xxx.AAA,
yyy: Xxx.BBB
});
</script>
import 其他文件里定义的内容
import {OptionState} from "@/data" // 其他文件要 export特定的定义
教程有的为了简化内容,直接准备一个json文件,然后import数据进来:
<script setup lang="ts">
import {xxx} from "@/data.json"
const rest = {
first: [...xxx].splice(0,25),
second: [...xxx].spice(25,25),
};
</script>
<template>
<Row v-for="r in rest.first" :key="r.id" />
<Row v-for="r in rest.second" :key="r.id" />
</template>
更新state
<script setup lang="ts">
const computeSelectedNames = () => {
const filterNames = names
.filter((name) => name.xxx === options.xxx )
.filter((name) => name.yyy === options.yyy )
.filter((name) => {
return ...
})
selectdNames.value = filterdNames.map((name) => name.xxx )
};
const selectdNames = ref<string[]>{[]}
</script>
<template>
<button class="abc" @click="computeSelectName">Find</button>
{{ selectedNames }}
</template>
注意拆分component,以及通过props在父子组件中传值
<script setup lang="ts">
interface OptionProps{
option: {
id: string,
title: string,
catagory: string,
index: number
};
}
const props = defindProps<OptionProps>()
</script>
在父组件中传入:
大写开头的标签就是component,有一些是 `nuxt` 保留的,用来作为头部header内容的,类似于 `metadata`
10. 通过事件触发父子组件传值。
```vue
// 父:
<script setup lang="ts">
const removeName = (index: number) => {
const filteredNames = [...selectdNames.value]
filterNames.splice(index,1)
selectedName.value = filterNames
}
</script>
<template>
<CardName v-for="(name, index) in selectedNames" :key="name" :name="name" @remove="()=> removeName(index)" :index="index" />
</template>
// 子CardName:
<script setup lang="ts">
interface NameProps{
name: string;
index: number
}
const props = defindProps<NameProps>();
const emit = defindEmits(["remove"])
const removeName = () => {
emit("remove", props.index)
}
</script>
<template>
<p @click="removeName">xxx </p>
</template>
常用的循环语句
v-for
Pages, 基于文件的路由route
<script setup lang="ts">
// import {} from "#app"; // 用来让编辑器不报错的,其实没什么用。
import xxx from "@/data.json"
const route = useRoute();
const name = route.params.name;
const x = xxx.find(r => r.name === name)
</script>
<template>
{{ name }}
</template>
404页面
Layouts
创建 layouts
文件夹,并创建default.vue
文件。
在page中声明使用特定的layout
<template>
<div>
<NuxtLayout name="custom">
<Xxx />
</NuxtLayout>
</div>
</template>
在layout中插入slot(假设layout名error)
<template>
<div>
<slot name="xxx" />
<div>...</div>
</div>
</template>
在page中:
<template>
<div>
<NuxtLayout name="error">
<template #xxx>
<h1></h1>
</template>
</NuxtLayout>
<div>...</div>
</div>
</template>
metadata
为了方便控制,也可以写在script
里,
也可以使用 outside stylesheets
方式:
在nuxt.config.js里配置。
export default defineNuxtConfig({
charset: "utf-8",
viewport: "width=device-width, initial-scale=1, maximum-scale=1",
meta: {
link: [
{ rel: "stylesheet", href: "/assets/css/style.css" },
{ rel: "stylesheet", href: "/assets/css/font-awesome.min.css" },
],
script: [
{ src: '/assets/js/jquery.min.js' },
],
}
})
网页数据状态设计
composable,useState
用来保存状态。不能用 ref
做跟踪,因为这里没有render,会造成内存泄漏。
Global State
目前还是测试阶段,官方不建议用于生产。
上一篇记录了vue2的一些备忘,这一篇记录vue3和2的不同的地方。
2020.09.18发布vuejs 3.0.0,代号 one piece。官网:https://v3.cn.vuejs.org/
官方针对版本3的说明:https://github.com/vuejs/core/releases/tag/v3.0.0
配置下代理,备忘
npm init npm config set proxy http://10.101.1.1:8118 npm config set https-proxy http://10.101.1.1:8118 npm i -S vue
vue-cli
4.5.0版本以上
npm install -g @vue/cli # 安装/升级
vue create xxx
cd xxx
npm run serve
想代替 webpack,由vue团队打造的打包工具。
HMR
npm init vite@latest
npm init vite-app my-vue-app
npm init vite@latest my-vue-app -- --template vue
cd my-vue-app
npm install
npm run dev
代码结构对比
Vue2:
vue3:
createApp 工厂函数,返回的app对象比vm更轻
setup方法
组件中的数据、方法都要配置到setup中(不需要往props和data、func里塞了)。
创建独立的响应式值作为 refs
。
watch只能监听ref的基本类型,要监听对象就用reactive。watchEffect
也不错,但是感觉会影响性能?
ref类似vue2中get和set来实现响应式。
reactive是vue3中新增的proxy实例对象来实现响应式,搭配toRefs实现。
<template>
<div>
<span>8</span>
<button @click="count ++">Increment count</button>
<button @click="nested.count.value ++">Nested Increment count</button>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
return {
count,
nested: {
count
}
}
}
}
</script>
当 ref
作为响应式对象的 property 被访问或更改时,为使其行为类似于普通 property,它会自动解包内部值.
使用解构的两个 property 的响应性都会丢失。对于这种情况,我们需要将我们的响应式对象转换为一组 ref。这些 ref 将保留与源对象的响应式关联。
import { reactive, toRefs } from 'vue'
const book = reactive({
author: 'Vue Team',
year: '2020',
title: 'Vue 3 Guide',
description: 'You are reading this book right now ;)',
price: 'free'
})
let { author, title } = toRefs(book)
title.value = 'Vue 3 Detailed Guide' // 我们需要使用 .value 作为标题,现在是 ref
console.log(book.title) // 'Vue 3 Detailed Guide'
compute
vue3里可以按照vue2的compute使用。不过我们现在搞到setup里了。
只读computed,接受一个 getter 函数,并根据 getter 的返回值返回一个不可变的响应式 ref 对象。
const count = ref(1)
const plusOne = computed(() => count.value + 1)
console.log(plusOne.value) // 2
读写computed
const count = ref(1)
const plusOne = computed({
get: () => count.value + 1,
set: val => {
count.value = val - 1
}
})
plusOne.value = 1
console.log(count.value) // 0
生命周期
可以通过直接导入 onX
函数来注册生命周期钩子:
import { onMounted, onUpdated, onUnmounted } from 'vue'
const MyComponent = {
setup() {
onMounted(() => {
console.log('mounted!')
})
onUpdated(() => {
console.log('updated!')
})
onUnmounted(() => {
console.log('unmounted!')
})
}
}
自定义hook
一般放在hook文件夹里,感觉很不错啊,这种引用方式。
其它composition api
provide
选项来提供数据,子组件有一个 inject
选项来开始使用这些数据。composition api的优势
就我个人感觉,更像后端的一个class,定义变量和方法都在setup里,改起来舒服。
配合hook使用就起飞🛫️。
fragment
用来UI定位的,舒服舒服,直接把内容传送到任意一个元素去。
vue3如何做到交互性?
参考vue2的内容,原理是一样的,只不过换了一个方法实现——ES6的Proxy Reflect对象。
它们的区别仅在于:
通过ES6的 new Proxy()
,可以创建一个代理用来代替另一个对象,实现基本操作的拦截和自定义。这个代理与目标对象表面上可以被当作同一个对象来看待。
前端不是我的主业,不过太久不用,捡起来每次都觉得头疼,还是稍微做点笔记吧。
首先是官方文档:https://cn.vuejs.org/v2/guide/,虽然目前到3版本了,还是先了解下v2版本的知识,并不复杂。
基本就是官网内容。
格式
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
<div id="app">
</div>
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
数据绑定、css绑定、事件绑定和语法糖
v-bind:value=<expression>
单项绑定,语法糖 :value=<expression>
v-model:value=<expression>
双向绑定(只用表单),语法糖 v-model=v-bind:class=xxx
,语法糖 :class=<str>
v-on:click.stop=<expression>
事件绑定,语法糖 @click.stop=<expression>
.stop
.prevent
计算属性
func 里this指针指向的是vm
computed不吃性能,尽量用它
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
},
watch: {
// 如果 `question` 发生改变,这个函数就会运行
question: function (newQuestion, oldQuestion) {
this.answer = 'Waiting for you to stop typing...'
this.debouncedGetAnswer()
}
},
created: function () {
this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)
},
methods: {
getAnswer: function () {
}
},
mounted(){
}
})
由vue管理的method,要以普通方式定义。
否则应该用箭头函数,例如:
getAnswer: function (()=>{})
此时它的this为window。
另外,指令directive相关的 this 都是 window
循环语句和条件语句
循环:
<ul id="example-1">
<li v-for="item in items" :key="item.message">
</li>
</ul>
<ul id="example-2">
<li v-for="(item, index) in items">
- -
</li>
</ul>
<div v-for="(value, name, index) in object">
. :
</div>
条件:
<div v-if="type === 'A'"></div>
<div v-else-if="type === 'B'"></div>
<div v-else-if="type === 'C'"></div>
<div v-else></div>
Vue 无法监控数组内容的改变
https://cn.vuejs.org/v2/guide/list.html#数组更新检测
对象因为有setter和getter,修改model可以监控到,view也会变动。
但直接操作数组不奏效,因为数组没有这两个方法,引发view变动则需要调用如下方法:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
过滤器,用于一些常见的文本格式化
一旦 data 数据变化就会重新解析模版。
create
mounted做初始化,把初始DOM放入页面时,仅调用一次。
创建
<template>
</template>
<script>
import ComponentA from './ComponentA'
import ComponentC from './ComponentC'
export default {
name: ''
data(){
},
components: {
ComponentA,
ComponentC
},
// ...
}
</script>
<style>
</style>
或者
const vc = Vue.extend({ })
注册
new Vue({
el: '#app',
components: {
'component-a': ComponentA,
'component-b': ComponentB
}
})
使用
<div id="app">
<component-a></component-a>
<component-b></component-b>
<component-c></component-c>
</div>
原型对象prototype
实例的隐式原型属性 __prototype__
永远指向类(父类)的原型对象
国内代理备忘
npm init npm config set proxy http://10.101.1.1:8118 npm config set https-proxy http://10.101.1.1:8118 npm i -S vue
命令备忘
npm install -g @vue/cli
vue --version
vue create hello-world
修改配置,在vue.config.js
中添加(参考配置)
module.exports = {
devServer: {
host: '0.0.0.0',
port: 80
}
}
文件预览
render
vue.runtime.xxx.js 是运行版的vue,没有模版解析器,不能使用template配置项。
vue ref,vue用于获取元素,用来代替以前通过id获取元素的办法。
相当于后端中一个类class实例的只读属性,。
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // or any other constructor
}
有点类似于后端的基类和子类,mixin属于基类的方法,多个子类都可以使用基类的方法。
var vm = new Vue({
mixins: [mixin],
methods: {
...
}
})
可以全局混入,也可以局部混入。
Vue.mixin({
xxx: xxx
})
// 调用 `MyPlugin.install(Vue)`
Vue.use(MyPlugin)
scope
解决style类名相同,引入顺序不同,导致冲突的问题。
<style scoped>
</style>
不要在App里使用scoped
官网的一些内容和后端思维是互通的,比如状态提升
之类的,不要害怕,见人说人话,见鬼说鬼话。
拆分静态组件,按功能点拆分
静态组件,不考虑交互
数据交互设计
类似于后端,设计数据库和model。
组件通信的几种方式:
通过 props 存储数据,数据存在App上。
相当于内存存变量。
自定义事件
子组件给父组件传数据用。
在父组件中给子组件绑定自定义事件,事件回调在父组件。(父给子传函数,要求子返回数据)
全局事件总线
定义一个所有组件的子组件vc(也可以直接用vm)挂在prototype上,然后使用事件触发。相当于后端的redis。多读单写
消息订阅&发布
全局事件总线和消息订阅其实差不多的效果。多读单写
浏览器localstorage,相当于后端的数据库DB。
vuex
多读多写
动态组件
不同功能的增删查改+统计信息变动
this.$emit('myEvent')
<my-component v-on:my-event="doSomething"></my-component>
this.$off('myEvent')
this.$off(['myEvent','xxx'])
this.$off()
也适用于子给父组件传输数据。
自定义事件由父组件运行,props方式由子组件运行。
还可以通过ref绑定自定义事件。
全局事件总线
定义一个所有组件的子组件vc,然后赋值给prototype。
改良版:
把vm当成所有组件的子组件,这样子?真是奇妙。
消息订阅&发布
$ npm -i pubsub.js
import pubsub from 'pubsub-js'
pubsub.publish('hello',666)
pubsub.subscribe('hello',function(msgName,data){ })
pubsub.subscribe('hello',(msgName,data)=>{ })
nextTick
动画/过渡
其实就是封装了一个 transition 标签。
<transition name"xxx" appear></transition>
.xxx-enter-active{
animation: kelu 1s linear;
}
.xxx-leave-active{}
@keyframs kelu {
from{
transform: xxx
}
to{}
}
第三方动画:Animate.css:http://animate.style
跨域
axios请求,
cors(后端解决),代理服务器(前端解决)
module.exports = {
devServer: {
proxy: 'http://localhost:4000'
}
}
module.exports = {
devServer: {
proxy: {
'/api': {
target: '<url>',
ws: true,
changeOrigin: true
},
'/foo': {
target: '<other_url>'
}
}
}
}
# https://github.com/chimurai/http-proxy-middleware#proxycontext-config
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
npm i vuex
import Vuex from 'vuex'
Vue.use(Vuex)
import { createApp } from 'vue'
import { createStore } from 'vuex'
// 创建一个新的 store 实例
const store = createStore({
state () {
return {
count: 0
}
},
mutations: {
increment (state) {
state.count++
}
}
})
const app = createApp({ /* 根组件 */ })
// 将 store 实例作为插件安装
app.use(store)
dispatch -> commit ->
vc -> Action(ctx,value) -> mutation(state,value)
dispatch可以跳过,直接commit就行了。
// 在单独构建的版本中辅助函数为 Vuex.mapState
import { mapState } from 'vuex'
export default {
// ...
computed: mapState({
// 箭头函数可使代码更简练
count: state => state.count,
// 传字符串参数 'count' 等同于 `state => state.count`
countAlias: 'count',
// 为了能够使用 `this` 获取局部状态,必须使用常规函数
countPlusLocalState (state) {
return state.count + this.localCount
}
})
}
npm i vue-router@3
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
一般组件
路由组件
<router-view></router-view>
# 一级路由
const routes = [
{
path: '/user/:id',
component: User,
children: [
{
path: 'profile',
component: UserProfile,
},
{
path: 'posts',
component: UserPosts,
},
],
},
]
路由传参
query参数
params参数
props(对象/函数/布尔)
解构赋值,连续解构赋值:
路由命名
路由历史记录
声明式 | 编程式 |
---|---|
<router-link :to="..." replace> |
router.replace(...) |
<router-link :to="..."> |
router.push(...) |
路由缓存:
<keep-alive :include="['xx','yy']">
<router-view></router-view>
</keep-alive>
路由组件新增了两个生命周期
路由权限
数据存在meta里,
前置路由守卫,主要做鉴权
// GOOD
router.beforeEach((to, from, next) => {
if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
else next()
})
后置路由守卫作用,一般就是修改页面内容之类的。
为什么 vuejs 可以做到reactive
交互式
主要是通过 Object.defineProperty()
方法实现,参考https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty,直接在一个对象上定义一个新属性。
自定义一个setter(所以必须用object类型)
字面量赋值