1 Star 0 Fork 11

coder_lw / wiki

forked from deepinwiki / wiki 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
tauri 开发.md 34.07 KB
一键复制 编辑 原始数据 按行查看 历史
htqx 提交于 2023-01-20 13:05 . tauri 开发

tauri 开发

前言

tauri 是一个基于系统 web 组件的界面开发框架。它的优势是将前端开发的经验,搬到桌面界面开发上来。个人认为是很有前途,很经济的一种界面开发方式。

tauri 前端依赖 webview 组件,可以使用任意 js 框架,后端是 rust 程序,但是后续会增加其他语言的后端,毕竟 rust 是相对比较复杂的编程语言。

架构

核心生态:

  1. tauri : 最终合集。
  2. tauri-runtime : 基层粘合库
  3. tauri-macros : 宏
  4. tauri-utils : 通用工具包
  5. tauri-build : 构建时辅助
  6. tauri-codegen : 资源编码
  7. tauri-runtime-wry : 系统交互层

工具:

  1. tauri-apps/api : cjs / esm 端点,监听后端活动。
  2. bundler : 程序包
  3. cli.rs
  4. cli.js
  5. create-tauri-app: 生成项目
  6. tauri-action : github 构建平台
  7. tauri-vscode : vs code 插件
  8. vue-cli-plugin-tauri : vue-cli 插件

上游项目:

  1. tao
  2. wry

前端开发

前端开发有各种框架,我这里选择 vue3 + vite 的方式。不了解前端开发的人,可能只了解一丢丢 js + html 的知识。现代前端开发,它已经不是打开一个编辑器,写html 里面的 js 那么原始(这类js 被称为 vanilla js),它已经变得非常复杂,组件化,工具化。

前端工具依赖 node.js 这个脱离浏览器可以执行 js 的引擎,它可以读写文件等操作,比在浏览器内部的 js 更加强大。所以很多前端工具本身就用 js 开发。

当然前端代码本身是不依赖 node.js 的,还是在浏览器中运行, node.js 只是构建工具。

然后是 js 的组件化。 js 开发者通过开发不同的 js 文件,共享给用户使用,这是一种最原始的组件化。现代 js 对这些 js 文件进行了组织。

import {xxx} from "xxx"; // 导入xxx 模块

导入模块会找寻绝对路径或者相对路径上的 js 文件,但是在 node.js 中,他会自动搜寻 node_modules 中的包。这是因为 node.js 支撑的构建工具,如 vite 等扩展了这个能力,它发现导入模块的时候,会自动绑定相关的 js 文件。

因此,第三方开发的组件,就通过构建工具自动关联起来,比如 yarn 这个工具,可以从网上下载相关组件,存放在 node_modules 文件夹中,进行统一管理。js 开发者不需要去管具体的文件,只需要填写正确的名称,其他由yarn 系统管理,比如组件的版本更新等等。

现代 js 组件就不需要用户自己手动下载,然后自己去匹配管理了。vite 还会在开发的时候,启动一个服务器,让代码可以随时编写,随时更新变化。最终发布的时候,vite 还会将相关的组件,关联实际的 js 文件,进行打包整合,从而成为一个静态的网站。也就是说,现代前端开发,它是高度依赖 node.js 和 vite 之类的构建工具的,而且相关的开发框架,如 vue 也是匹配这些工具来实施开发流程。只有最终发布的时候,才重新编译成传统的 js + html 源代码。

也就是说,前端开发是分成两个部分,一个是在开发阶段,vite 提供服务器和热更新,让修改和生效变得更加快捷。而 yarn(或 npm)管理相关的 js 组件的引用。等一切准备完毕,再完整的编译输出静态网站,这时候就能脱离 vite 和 nodejs 之类的工具,部署到真正的 web 服务器上了(对于 tauril 来说就是一起打包成桌面程序的安装器)。

理解现代前端开发的现状之后,下面介绍 vue 框架是怎么回事。vue 框架提供了一些抽象,标签的属性变化是响应式,页面也变成组件式,事件处理更加方便。它虚拟 dom 节点操作,让程序运行效率得到提高。

但是这点还不足以让传统桌面开发人员满意,因为我们都是习惯拖控件,写事件处理这种模式。因此还需要一个界面库,包含按钮什么的,它应该是基于 vue3 来开发的,不过相对而言,vue3 的控件库比较少,这里使用 naive-ui。

tauri 还包含后端,后端就负责一些高权限的业务,比如文件操作,前端的js 是做不到的。通过绑定后端接口,前端就能调用后端来执行一些具体的业务操作。

vanilla js

vanilla js (即基本 js) 最新版已经增加了不少模块化的技术,还有标准化的侦听技术,大大增强它作为动态语言的灵活性。js 语言最大的特色就是动态性,可以随意扩展属性。vue 等框架会大量使用这些特性。项目中也经常会用到 ts。ts (TypeScript) 语言是 js 的类型标注版本,它就是让变量类型更明确,静态分析的时候能提前发现类型错误,最终也会通过转换工具输出成 js。(有兴趣可以自行了解)

es6 (2015)

  1. 模块化
  2. 箭头函数
  3. 函数参数默认值
  4. 模板字符串
  5. 解构赋值
  6. 延展操作符
  7. 对象属性简写
  8. Promise
    1. Promise.all([...]) : 等待所有任务完成
    2. Promise.race([...]) : 等待最快完成的任务(如设置超时任务)
  9. let 与 const
  10. for ... of
  11. Symbol
  12. Iterator / Generator
  13. Set / WeakSet
  14. Map / WeakMap
  15. Proxy / Reflect
  16. Regex
// 1. 类
class A{
    name:'', // 数据成员
    constructor(name){ this.name =name}, // 构造函数, this 绑定当前对象
    toString(){console.log(name)}, // 重载原型对象函数
}
class B extends A { // 继承
    constructor(){super("B")}, // 调用父类构造函数
}

// 2. 模块化
export var name = 'abc' // 导出
import { age } from 'mymodule' // 导入

// 3. 箭头函数
let add = (a,b) => a + b // add 保存了箭头函数,箭头函数会捕获外部变量a,b。并且箭头函数不会自动像成员函数那样设置 this,所以只会捕获当前环境下的this(如果使用this)。

// 4. 函数参数默认值
function foo(a= 1, b = 2){return a + b}

// 5. 模板字符串
let name = `反引号包裹${add(1,2)} ${foo()}` // 对字符串进行简易拼接
getname`a ${b} ${c}` //标签函数,参数是字符串组和对应变量的方式传递到处理函数,即 ['a ', ' '], b, c

// 6. 解构赋值
let [a, ,b] = [1, 2, 3] // a = 1, b = 3

// 7. 延展操作符
[...'hello'] // 等于 ['h','e', 'l', 'l', 'o']
add(...[1,2]) // 等于 add(1, 2)

// 8. 对象属性简写
let adder = {a, b} // 相等于 adder = { a:a, b:b}

// 9. Promise 异步对象
// Promise((f,g)=>{}) , f 产生成功结果, g 产生失败结果
// promise.then(f2, g2), f2 处理成功结果, g2 处理失败结果
// promise.catch(err=>{}) 捕获期间产生的错误
// promise 只有成功或失败且无法变更结果!谨记!一个异步任务对应一个异步对象,一个异步对象对应一个结果,但是,可以在完成上一个任务时创建新的任务(即返回一个新的异步对象),这样就达成了链式操作。
// 参数可以通过 f, g 的参数或 f2,g2 的返回值来传递(当该返回值不是 promise 时)
let p = a =>new Promise(f=>setTimeout(f, 1000, a)) // 一秒后成功
let v = a =>{console.log(a);a+=1;return p(a)} // 下一个任务也是 p()
p(6).then(v).then(v).then(v).catch(err=>console.log(err)) // 链式调用,依次输出 6 7 8,相隔一秒

// 10. let 与 const
let a = 1 // 定义于块级作用域
const b = {} //类上,常量,不能修改
b.name = "jim" // 但能扩展成员

// 11. for ... of
for (let item of 'hello'){ // 遍历任意迭代器
    console.log(item)
}

// 12. Symbol
let s = Symbol('jim') // 全局唯一,jim 只是名称,无法创建相同 symbol
let p = {[s]:123} // 用于对象的键
p[s] // 123
p[Symbol.for('bob')] = 456 // Symbol.for 将会在全局注册一个 symbol
p[Symbol.for('bob')] // 获取同名 symbol,输出 456

// 13. Iterator / Generator
// 实现 Symbol.iterator 属性的对象
// 它将返回一个 {next()->{value, done}} 对象
let iter = 'hello'[Symbol.iterator]()
for (const item of iter){
    console.log(item)
}
let obj = { // 自己实现迭代器接口
    data: 'jim',
    [Symbol.iterator]:function *(){// 生成器函数,注意星号标志
    // 生成器将产生 {next()->{value,done}} 对象
        for (const item of this.data) {
            yield item // 暂停点,可再次调用 next() 重入
        }
    },
}
for (const item of obj){ // 每次循环等于调用 next() 获得 item 值
    console.log(item)  // 直到 next() 返回 {value, done:false}
}

// 14. Set / WeakSet
let s = new Set([1,1,2,3]) // 集合,元素不重复,即得到 Set(3){1,2,3}

// 15. Map / WeakMap
let m = new Map([]) // 键值对。因为object 的键会转换为字符串"[object Object]",所以无法区分不同对象。
let [a,b] = [{},{}]
m.set(a, 123) // 注意 m[a] 等于 m[b] 等于 m['[object Object]']
m.set(b, 456)
console.log(`a = ${m.get(a)}, b = ${m.get(b)}`)// a = 123, b = 456

// 16. Proxy / Reflect
// 代理对象是一个深度的包装,成员对象的变化也会侦测到
let person = {name:'jim', age:12}
let handler = { // 代理逻辑
    get(target, prop){ // 读取属性
        console.log(`get ${prop}`) // 访问代理对象将打印相关操作
        // return target[prop] // 正常逻辑
        Reflect.get(target, prop) // 同上,调用原始操作
    },
    set(target, prop, value){ // 设置属性,还包含更多可代理操作
        target[prop] =value
        console.log(`${prop} set ${value}`)
        return true
    },
}
let p = new Proxy(person, handler) // 返回代理对象
p.hello = 123 // hello set 123
p.hello // get hello

// 17. Regex

es7 (2016)

  1. 数组 includes() 方法,是否包含指定值
  2. a ** b, 等价 Math.pow(a,b)

es8 (2017)

  1. async / await
  2. Object.values() : 对象的值数组
  3. Object.entries() : 键值对,方便转换到 Map
  4. Object.getOwnPropertyDescriptors(): 获取属性描述符,改善 Object.assign() 无法正确复制 get 属性。
    1. Object.defineProperties(): 设置
  5. Atomics : 原子操作
// 1. async /await
// 该语法糖增强了 Promise 的易用性,用同步代码的方式写异步代码
// async 函数返回一个 promise 对象
// await 类似暂停点,将在异步完成后继续执行
// 可并行执行的任务,用 await Promise.all([...])
async function f(a){
    console.log('start')
    const p1 = await (() => new Promise(f=>setTimeout(()=>{console.log("g1"); f(a++)}, 3000)))()
    console.log(p1)
    const p2 = await (() => new Promise(f=>setTimeout(()=>{console.log("g2"); f(a++)}, 3000)))()
    console.log(p2)
    return a
}
f(6).then(x=>console.log(`end...${x}`)) // g1 6 g2 7 end...8

// 效果类似以下生成器
function *f2(a){
    console.log('start')
    const p1 = yield (() => new Promise(f=>setTimeout(()=>{console.log("g1"); f(a++)}, 3000)))()
    console.log(p1)
    const p2 = yield (() => new Promise(f=>setTimeout(()=>{console.log("g2"); f(a++)}, 3000)))()
    console.log(p2)
    return a
}

function co(generator){ // 执行生成器
    const handler = result =>{
        if (result.done) return console.log(`end...${result.value}`)
        result.value.then(data=>handler(generator.next(data)))
    }
    handler(generator.next())
}

co(f2(6))

递归生成器

注意生成器有两个关键字, yield 和 yield*

  1. yield :是中断点,一个生成器函数最关键的点就是要有中断点
  2. yield*:类似迭代器,只是每次迭代会产生一个中断点

因此,如果你的生成器函数没有 yield ,你用 yield* 去迭代它是没有用的 如果你希望递归生成器,那么你要注意递归函数的要点

  1. 递归函数必须要有至少一个的出口

普通函数是 return 而对应生成器就是先 yield 保存序列点,然后再 return

其二、该怎么递归调用自身?

普通的递归函数只返回一个值,而迭代器的返回是一个迭代器(序列值) 因此书写递归生成器要理解这里面的差异,因为大多数递归算法,并不需要你返回一个迭代器,而只需要一两个值 所以,有两种调用,一种是按个调用(像普通函数那样),取一两个迭代值 另一种是用 yield* 生成后续的结果

其三、递归出口条件该怎么写?

特别注意,递归的出口条件是算法的根基,虽然基础条件往往很简单,但基础错了,整个递归逻辑就是狗屎 对于普通函数是返回 return 对于生成器是增加一步,用 yield 加入到序列,再返回 return 基础情况至少提供能满足递归逻辑要求的参数(个数)

其四、最终结果

普通递归只是得到一个结果 而生成器是一系列结果(也许有人需要?) 比较违反直觉的是,生成器版本性能更高。


function *fib(n){
    // 定义出口条件
    // 因为递归函数本质是个回环,你得在中间 break 出去
    // 必须先保存两个数,因为算法后面需要n-1 n-2
    if (n < 2){ //1,0 时
        for (let i = n; i >= 0; i--){
            yield i
        }
        return
    }else if (n < 0){ 
        return
    }
    // 递归逻辑
    let g = fib(n-1) // 获取迭代器,但只返回两个值就够了
    let [a, b] = [g.next().value, g.next().value]
    yield a + b  // 本层出口
    yield a // 下一层
    yield b // 再下一层(因为前面获取了,没必要再递归)
    yield* g // 剩余的
}

// 请仔细对比经典递归改变的点:
function *fib(n){
    // if (n < 2) return n
    if (n == 1) {yield 1; yield 0; return}
    if (n == 0) {yield 0; return}
    // if (n < 0) return
    if (n < 0) return
    // return fib(n-1) + fib(n-2)
    let g = fib(n-1)
    let [a,b] = [g.next().value , g.next().value]
    yield a + b
    yield a
    yield b
    yield* g
    return
}

es9 (2018)

  1. 异步迭代 for await() / for await... of
  2. Promise.finally() : 必定执行段

es10 (2019)

  1. Array.prototype.flat() : 扁平化
  2. Array.prototype.flatMap() :

es11 (2020)

  1. ?? : 非空和未定义返回右侧,否则返回左侧
  2. ?. : 非空和未定义调用成员,否则返回 undefined
  3. BigInt : 大整数 x = 99n,支持超过15位整数

es12 (2021)

  1. Promise.any() :

vue 3

vue3 是一个前端开发框架。也就是说它实现了 web 前端需要的一些基础功能,程序员基于这个框架,可以更加快速的完成项目。假如不基于框架,那么我们就需要使用原始的操作 dom 的api,一点一点的操作 dom。

特性:

  1. 使用 Proxy 实现数据响应式
  2. 虚拟 DOM
  3. 摇树(Tree-Shaking),减少无用分支
  4. 组合式 api (Composition api)
  5. setup 组合式 api 入口函数
  6. 新模板语法
  7. 新组件

安装

yarn add vue # 添加 vue 包到当前项目

vue 组件(SFC)

<!-- App.vue 组件分三个部分 -->

<!-- 1. 脚本 -->
<script setup>  
    // 该脚本类似于 vue.defineComponent({.setup() 函数}) 定义组件
    // 但会对导入和返回值做一些自动化适配,模板可以自动使用定义的对象
    // 1. 可以用 import 导入系统组件,第三方组件或者自定义的组件
    import {reactive, ref, computed, watch, watchEffect,
        onBeforeUpdate, provide,inject} from 'vue'
    
    // 2. 响应式数据、侦听、事件处理等常用组合式 api
    let num = 123
    let count = ref(num) // 基本类型的响应式包装,返回{value}包装,模板解析时会自动调用 .value 解除包装
    let sum = reactive ({num}) // 对象的响应式包装,返回的是 es6 Proxy 代理对象
    let result = computed({ // 计算属性,缓存计算值,除非依赖的响应式数据发生变化
        get(){return '结果:' + count.value}, //get
        set(value){count.value = parseInt(value.match(/\d+/)[0])} // set
    })
    watch(count, (newCount, oldCount)=>{ // 侦听响应式数据变化,只有变化时才执行
        console.log(`更新count:${newCount} -- ${oldCount}`)
    })
    watchEffect(()=>{console.log(`更新sum:${sum.num}`)}) //自动执行一次,自动识别上下文的响应式数据
    const input = ref(null) // 保存标签上 ref 指令指定的 dom 对象
    onBeforeUpdate(()=>{ // 更新前的周期钩子
        if (input.value){ // 根据单双数设置 input 块是否显示
            input.value.style.display = count.value % 2 ? "none" : "inline"
        }
    })
    let onClick = event => {// 事件处理函数
        count.value+=1 // 更新内部数据
        sum.num+=2 // 更新内部数据
    }

    // 3. 组件对外接口
    defineProps({ // 定义组件的标签属性,也就是入口参数
        title:{type:String, default:"Demo:"} // 可配置类型和默认值,验证函数等
    })

    const emit = defineEmits(['demo-event']) // 自定义事件
    emit('demo-event', sum) // 激活事件,并传递参数

    provide("foo", sum) // 为子组件提供一个服务,它不需要通过标签层层传递
    // 只需要任何层级的子组件使用 inject 获取
    const bar = inject("bar") // 获取父组件链上最近的服务对象,注入本地
    
</script>

<!-- 2. 模板 -->
<template> 
<h1 v-html="title" />
<slot name="head" :bar="bar" v-bind="$attrs">插槽默认值</slot>
<h3>双大括号可以使用 setup 脚本返回的对象:{{num}}</h3>
<button @click="onClick">事件处理加 @ 在事件前,响应式数据变化会同步到 html,即自动更新页面: {{count}}</button>
<button @click="onClick">对象响应式:{{sum.num}}</button>
<div ref="input">请设置结果:<input v-model="result" type="text" /></div>
</template>

<!-- 3. 样式  -->
<style>
</style>

模板语法

  1. {{表达式}} : 插入标签内部,但不能用于标签属性
  2. v-html : 内部添加原生 html
  3. v-bind / (:属性) : 绑定标签属性,可以缩写为冒号+对应属性,v-bind 还能将一个对象的属性扩展为标签属性。当绑定是属性是布尔值,将决定该标签属性是否出现在最终标签上。
  4. v-if: 根据布尔值决定是否保留标签
    1. v-else-if
    2. v-else
  5. v-show: 隐藏(dom保留),类似设置 css display 属性
  6. v-for: 根据数据源成组渲染标签
    1. :key : 指定 id,从而保证渲染顺序和数据源一致
  7. v-on / (@事件) : 绑定标签事件
  8. v-model : 绑定表单,用在 <input> 标签时相当于 :value="x" @input="e=>x=e.target.value"(根据表单标签类型变化绑定的属性和事件),简化了写法
  9. ref : 引用标签对象(dom),并保存在指定响应式变量中
  10. is : 指定标签对象实际为哪个组件
  11. v-slot /(#名称) : 指定要设置的具名插槽,用于 <template>标签
  12. v-pre: 不编译该模板标签下的内容
  13. v-once: 只编译一次,即跳过更新(性能优化)
  14. v-memo: 根据数组的值是否变化来决定是否更新(性能优化)
  15. v-cloak: 防止编译模板时页面闪烁(只有依赖运行时编译器才需要)
  16. 指令参数
    1. v-bind:id : 指定绑定的是 id 属性
    2. v-bind:['id']: 动态指定绑定的属性
    3. v-bind="{id:22, title:'标题'}" : 不指定参数,根据对象绑定多个属性
  17. 指令修饰符
    1. @submit.prevent : 指定事件的引发模式

组件对象

  1. props: 组件接受参数,也可在 setup 中用 defineProps 宏来定义,它代表父组件单向的传递数据给当前组件
  2. emits: 自定义事件组,在setup 中用 defineEmits 宏定义
  3. setup(props, context) : 配置入口
    1. props: 对应 props 属性
    2. context.attrs : 组件标签上的其余非 props 属性,或用 useAtts 访问
    3. context.slots : 组件插槽(内部标签)
    4. context.emit: 事件分发器(激活组件上的任意事件)
  4. 生命周期(可通过 onXXX 注册周期钩子)
    1. setup : 组合式 api 入口点
    2. beforeCreate : 创建前
    3. created : 创建后,this 可用
    4. beforeMount : 挂载到 dom 前
    5. mounted : 挂载后,dom 可用
    6. beforeUpdate : 响应式数据更新前
    7. updated : 更新后
    8. beforeUnmount : 卸载前
    9. unmounted : 卸载后
  5. 响应式数据
    1. ref : 包装基本数据类型,当是对象会转化为 reactive 再包装
      1. unref : 获取原始对象
    2. reactive:用Proxy技术包装对象
      1. 解构后的属性是未包装数据,即非响应式数据
      2. toRefs : 将 reactive 的每个成员包装为 ref

用户函数(Composables)

vue2 使用的是选项式,即在组件对象上有相关的属性,用来分别保存数据成员,事件等等。vue3 使用的是组合式 api,将业务逻辑都封装到 setup 配置函数内。选项式分门别类各种内容,貌似更加条理清晰,易于理解。那么组合式 api 有什么优点?

组合式 api 的优点就是它可以将业务分段,然后在 setup 用引入即可。这种分段后的用户函数,也叫组合式函数 Composables。

// setup 内
useComposable1() // 用户片段,封装某种功能
useComposable2() // Composables 函数就相当于用户封装的 setup 的一部分,可以用 setup 的几乎所有功能(除几个宏),因为 setup 内都是组合式 api。 支持组合拼接,正是它最大的优势
// 最佳实践:以 use 开头命名,参数使用 ref 和返回使用 {ref...}
...
defineProps({...}) // 当前组件业务逻辑
onMounted(()=>...)

自定义指令

钩子函数参数:

  1. el : 绑定的元素
  2. binding : 上下文
    1. value : 值
    2. oldValue : 旧值
    3. arg : 指令参数
    4. modifiers: 修饰符
    5. instance:组件实例
    6. dir:指令对象
  3. vnode : 底层 vnode
  4. preVnode : 更新前的 vnode
// setup 中
// 指令目的偏向操作 dom
// 最佳实践: 不推荐在自定义组件上使用自定义指令,非要使用时组件必须是单根节点,因为最终会绑定到根上(dom)
const vMyDirective = { // 命名 v 开头
    mounted:(el, binding, vnode){ // 基于各种钩子。 不指定时为在 mounted 和 updated 执行

    }
}

插件

插件为 vue 添加功能,可以说是组件,用户函数,自定义指令的集合。

const myPlugin = {
    install(app, options) { // app.use(myPlugin,{}) 安装插件
        app.component(...) // 注册组件
        app.provide(...) // 提供服务(依赖注入)
        app.directive(...) // 注册自定义指令
        app.config.globalProperties // 配置全局实例属性和方法
    }
}

思路整理

vue 框架虽然易于理解,容易使用,但是也有一些值得思考的点,和应该注意的地方。

  1. 组件是树形结构组织的,上下级通信规则
    1. 上级通知下级,通过标签属性 props
    2. 上级跨层级通知下级,通过 provide/inject 依赖注入
    3. 下级通知上级,通过标签绑定事件处理函数 @click(onClick),下级通过 emit(click) 激活事件
    4. 透传机制,attrs / onXXX 即非定义 props/emits 的其余属性和事件处理会层层传递到子级,这类底层事件会层层向上传递,激活所有事件处理
    5. 插槽,安排子节点位置
    6. vue 虚拟节点和原生 dom 节点的异同需要理解,原生事件机制和节点访问机制
  2. 兄弟节点通信会很困难,为了满足通信需求,树形结构有转化为条形结构的趋势,所以组件模板的书写经常出现大型左倾倒的 v 字形
    1. 模板(类xml)定义方式,和函数定义方式有类似的树形逻辑,那么为何函数不会出现这种情况,因为函数的返回值,会被父级获取并可能传递到下一个子函数入口,所以函数调用模型下的兄弟节点,只有先后顺序,上一个函数容易传递信息到下一个函数
  3. 组合式函数没有模板,可以部分解决上诉问题,比如没有外观的服务类组件,做成组合式函数比做成组件插入到模板,增加倒 v 规模来得简洁。
    1. 那么函数该怎么和模板交互,因为当插入组件的树结构中,又不在当前组件位置时,首先要变为组件
    2. 第二个问题,怎么解决倒 v 结构,可以尝试让父子构造变为兄弟构造
    3. 第三个问题,组件入口和出口定义的规范
    4. 插槽规范,插槽类似未知子组件,所以是在当前组件定义下层需要什么入口的规范
  4. 技术细节
    1. 模板组件和 js 的交互,首先考虑 js 提供响应式数据绑定,js 是提供方,模板是接收放,但因为是响应式的,所以也能逆向操作。
    2. 不建议获取模板上的对象,因为它虽然看上去很近,实际很远,要考虑到渲染,生命周期等等复杂的内部机制问题。所以为什么响应式伟大,他就提供了一个友好的数据交互界面。
    3. 除了模板,还能直接使用渲染函数来生成界面。这对于零碎的,比如列表项之类的非常有意义
<!-- 左倾大 V -->
<a>
    <b>
        <c>
        </c>
    </b>
</a>
<brother> <!-- 同级化 -->
    <a></a>
    <b></b>
    <c></c>
</brother>

<provider :use="用户函数集合"> <!-- 服务节点 -->
</provider>

<sl>
    <template #header v-slot="{a,b,onClick}">
    {{a}},{{b}},{{$emit('click')}}
    </template>
    <template #default v-slot="{a,b,c}" />
    <template #footer v-slot="{a,b,c}" />
</sl>

控件

我们可以开发自己的控件,当然最简单的是使用别人开发的控件。vue 内置一些控件,同时这里介绍第三方控件库: naive-ui。

安装 naive-ui

yarn add naive-ui
# yarn add vfonts # 相关字体
yarn add @vicons/utils # 图标工具,先在 xicons.org 查找后使用
yarn add @vicons/fa # 图表库

使用方法:

// 1. 引入
<script setup>
    import {NButton, NConfigProvider, zhCN, 
        dateZhCN, darkTheme} from 'naive-ui'
    // import 'vfonts/Lato.css' // 通用字体
    // import 'vfonts/FiraCode.css' // 等宽字体

    // Icon{size, color, tag} 配置单个图标
    // naive-ui 也有图标组件 NIcon 
    // IconConfigProvider{size, color, tag} 配置默认值
    import {Icon, IconConfigProvider} from @vicons/utils

    /** 通过下面方式(JSDoc)可以让编辑器识别到对象结构,给出提示
     * js 代码没有类型检查,不会提前报错,有点麻烦
     * @type import('naive-ui').GlobalThemeOverrides
     */  
    const mytheme = { common: { fontWeightStrong: '600' } }
</script>

// 2. 在模板中使用
<template>
    <n-config-provider :theme="darkTheme" :theme-overrides="mytheme" :locale="zhCN"
    :date-locale="dateZhCN"> 配置主题,字体,中文等
        <n-button>click me!</n-button> 使用控件
    </n-config-provider>
</template>

<style>
</style>

vue 内置控件

  1. Transition : 过度效果,作用于 v-if、v-show、<component>
    1. css
      1. v-enter-from : 进入前
      2. v-enter-active : 进入中
      3. v-enter-to : 进入后
      4. v-leave-from : 离开前
      5. v-leave-active : 离开中
      6. v-leave-to : 离开后
    2. 动画事件 : 当 :css="false" 时可以通过脚本定义动画效果
      1. @before-enter
      2. @enter
      3. @after-enter
      4. @enter-cancelled
      5. @before-leave
      6. @leave
      7. @after-leave
      8. @leave-cancelled
  2. TransitionGroup : v-for 组过度效果
    1. tag : 指定标签容器
  3. KeepAlive : 保持内部组件状态,即当从 dom 移除时并不真正销毁
    1. 相关周期钩子
      1. activated : 激活组件(首次挂载或从缓存重新加载)
      2. deactivated: 移除或存入缓存
  4. Teleport : 将内部的标签渲染到指定 dom 节点,从而避免 css 和动画影响
    1. to : 渲染到指定节点下
  5. Suspense : 协调异步依赖,内部包含的异步组件的状态将托管给 Suspense 统一管理,注意只能是单根。
    1. #fallback : 定义正在加载中内容的插槽
    2. @pending : 挂起事件
    3. @resolve : 完成事件
    4. @fallback : 显示 #fallback 插槽时事件

naive-ui 控件

  1. NButton 按钮
    1. 样式级别: 默认 | secondary | tertiary | quaternary
    2. 形状: round 圆角 | circle 圆形 | 默认方型
    3. loading: 加载状态
    4. size: tiny | small | medium | large
    5. text-color
    6. color
    7. type: default | tertiary | primary | success | info | warning | error
    8. #icon
    9. @click
  2. NIcon 图标
    1. color
    2. size
    3. component : 图标组件,也可以放默认插槽
    4. NIconWarapper : 包含NIcon(用作背景和调整圆角)
      1. color
      2. icon-color
      3. border-radius : 圆角强度,默认 6
  3. NAvatar 头像
    1. size : small | medium | large | 数字
    2. src : 图片源
    3. round : 圆形
    4. #fallback : 加载失败
    5. #placeholder : 加载时
    6. @error : 加载失败
    7. NAvatarGroup 头像组
      1. max : 最大个数
      2. options : 参数对象
      3. vertical : 竖直排列
      4. #avatar: 头像组头像
      5. #rest : 溢出
  4. NTag 标签
    1. closable : 可关闭
    2. color
    3. @close
    4. #avatar :头像
    5. #icon
  5. NTooltip 弹出提示
    1. trigger(触发机制) : hover 悬浮 | click 点击 | manual 手动
    2. placement : bottom 底部
    3. #trigger : 目标
    4. @update:show
  6. NBadge 右上角标记
    1. value
    2. max
    3. type : error | info | success | warning
  7. useDialog 对话框: 外层需要 NDialogProvidedr 服务配置组件,或使用 createDiscreteApi 在 setup 外部创建(如事件处理函数内)
    1. 创建方法: create | error | info | success | warning
    2. 方法参数 DialogOptions 对象:(建议用参数设置值)
      1. content: 内容
      2. maskClosable: 点击对话框外部是否自动关闭
      3. closable: 是否有 close 图标
      4. closeOnEsc: 是否用 esc 关闭对话框
      5. title: 标题
      6. negativeText: 取消按钮文字
      7. positiveText: 确认按钮文字
    3. 返回对象 DialogReactive:(建议用返回对象设置事件处理)
      1. onNegativeClick: 取消按钮点击时
      2. onPositiveClick: 确认按钮点击时
      3. onClose: 关闭对话框时
      4. onMaskClick: 点击对话框外部时
      5. onEsc: 按 esc 关闭时
  8. useMessage 信息框:(类上配置方式)
    1. MessageProvider 注入函数: create | error | info | loading | success | warning
    2. 创建函数参数1 content : 内容
    3. 创建函数参数2 MessageOption:
      1. duration: 停留时长(毫秒)
      2. onClose: 点击关闭时
    4. 返回对象
      1. onLeave: 离开时
    5. 配置
      1. max: 最大个数
      2. placement 显示位置: top | top-left | top-right | bottom | bottom-left | bottom-right
      3. to 挂载位置: 默认'body'
  9. useNotification 通知栏:(类上配置方式)
    1. content: 内容
    2. description: 抬头
    3. duration: 停留时间 n 毫秒
    4. onClose: 手动关闭时
    5. onLeave: 离开时
    6. 配置
      1. max
      2. placement: 默认 top-right
      3. scrollable: 支持滚动
      4. to
  10. useLoadingBar 加载进度条: (类上配置方式)
    1. error(): 失败
    2. finish(): 成功
    3. start(): 开始进度条
    4. 配置
      1. to: TODO: 不知道为啥改变绑定对象也看不出变化
  11. NPopconfirm 上下文弹出框(类似非模态对话框)
    1. negative-text : 拒绝按钮文字
    2. positive-text : 确认按钮文字
    3. on-positive-click: 确定时
    4. on-negative-click: 拒绝时
    5. #icon
    6. #action
  12. NPopselect 弹出菜单
    1. SelectOption 选项
      1. value: 数字 | 字符串
      2. label : 显示内容
      3. disabled : 禁用该选项
    2. SelectGroupOption 选项组
      1. key
      2. label
      3. children : 选项的数组
    3. options : 选项的数组 | 选项组的数组
    4. size: small | medium | large
    5. value: 当前选择的值
    6. @update:value
    7. on-update:value: 更新值时 (value,option)=>void
    8. #empty : 选项数据源为空时的插槽

布局组件

导航组件

表单组件

内容展示组件

vite

rust

tauri

基本原理

tauri = 前端项目 + rust tauri 的组合。tauri 可以在前端项目中添加一个 src-tauri 的文件夹保存后端 rust 项目。前端通过 @tauri-apps/api 和后端交互,后端通过 src-tauri/tauri.conf.json 配置开启一些功能特性给前端使用。同时,也开放前端调用后端的 api 接口,和事件驱动接口,这样,原本的前端项目就能够具备桌面程序开发的能力。

配置

调用接口

事件驱动

开发调试

打包发行

参考

  1. tauri 官网:https://tauri.app/zh-cn
  2. Tauri 基础入门教程(b 站):https://www.bilibili.com/video/BV1Q14y1b7qU
  3. 「重学 JavaScript」之你不一定知道的 ES6,花 10 分钟好好看完 : https://www.bilibili.com/video/BV1j8411p795
  4. 「重学 JavaScript」之 JS 异步编程,瞎眼动画,包教包会,看完真不理解就改行吧:https://www.bilibili.com/video/BV1b14y1w7Mg
  5. mozilla 官网 js 标准: https://developer.mozilla.org/zh-CN/docs/Web/javascript
  6. vue3 新特性教程:https://www.bilibili.com/video/BV1JP4y1C7SS
  7. petite-vue 简化版 vue:https://github.com/vuejs/petite-vue
  8. Slot "default" invoked outside of the render function 错误原因:https://zelig880.com/how-to-fix-slot-invoked-outside-of-the-render-function-in-vue-3
  9. vnode 简介(vue2): https://www.jianshu.com/p/c6f53c5c8a8a
  10. 你和大神的距离可能就在这里,Vue3 的 patchFlags 超详细讲解: https://juejin.cn/post/6858955776992968712
  11. Vue3 编译优化之 PatchFlags 和 ShapeFlags: https://juejin.cn/post/7049358090445160462
  12. 使用 JSDoc 标注类型: https://juejin.cn/post/6844903630026309646
    1. 让 JS 拥有类型约束 JS Doc(B 站):https://www.bilibili.com/video/BV1iU4y1T7Ga/?vd_source=4c6cf408643a8daee4c906c80a51d4e8
  13. TypeScript 使用指南手册(如何书写声明文件): http://www.patrickzhong.com/TypeScript/zh/declaration-files/index.html
    1. JavaScript 文件里的类型检查:http://www.patrickzhong.com/TypeScript/zh/javascript/type-checking-javascript-files.html
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/coder_lw/wiki.git
git@gitee.com:coder_lw/wiki.git
coder_lw
wiki
wiki
master

搜索帮助