小白学习多多指教~
vue3 官网
Vite 官网
一个 Vue 3 UI 框架 | Element Plus
//查看vue版本 必须在4.5.0以上
vue --version
//升级vue
npm install -g @vue/cli
yarn global add @vue/cli
//创建项目
vue create my-project
回顾vue2 对比 vue3
Vue3 新特性介绍
//vue2
//defineProperty只有知道key,才能读取拦截,所以新增属性v2表示无能为力
Object.defineProperty(data,'count',{
get(){},
set(){},
})
//vue3 Proxy
//知道对象名就可以读取或拦截data上任意的key
new Proxy(data,{
get(key){},
set(key,value){}
})
相比有以下优势:
丢掉麻烦的备份数据
省去for in 循环
可以监听数组变化
代码更简化
可以监听动态新增的属性;
可以监听删除的属性 ;
可以监听数组的索引和 length 属性;
代码中没有用到的部分代码中不会打包到dist目录中
{{ name}}
{{ message == 0 ? '我是neek0' : '我不是neeko' }}
{{ message + 1 }}
{{ name.split('')}}
v- 开头都是vue 的指令
在自定义组件中,使用v-model,可以达到父子双向传值的效果
元素的 value
attribute 绑定到 modelValue
propinput
事件触发时,触发一个携带了新值的 update:modelValue
自定义事件//父组件
//子组件
//父组件
//子组件
通过JS来生成一个AST节点树。
为什么不直接操作真实dom? 真实dom上面的属性是非常多的,直接操作DOM非常浪费性能。
如何解决? 通过js计算性能来换取操作dom所消耗的性能,当然我们不可避免要操作DOM
,但是我们尽可能少的操作真实dom,而操作js是非常快的
diff算法可以看作是一种对比算法,对比的对象是新旧虚拟Dom
十分详细的diff算法原理解析_Dddusty的博客-CSDN博客
相同点:都是实现响应式数据
const test = reactive({
id: 1,
name: 'neeko',
})
console.log(test.id) // 1
注意:
计算属性, 当依赖项发生改变时,值也会发生改变。
watch函数与watchEffect函数都是监听器,在写法和用法上有一定区别,属于是同一功能的两种不同形态,底层都是一样的。
watch
显式指定依赖数据(第一个参数),依赖数据更新时执行回调函数immediate: true
时可以变为非惰性,页面首次加载就会执行)const mesage1 = ref({
nav: {
tit: {
name: "neeko",
},
},
});
//监听单个ref对象
watch(
mesage1,
(newV, oldV) => {
console.log(newV, oldV);
},
{
immediate: true, //是否立即调用一次
deep: true, //是否开启深度监听 只有深度监听才能收听到ref对象类型
}
);
//多个监听ref对象
watch(
[mesage1,mesage],
(newV, oldV) => {
console.log(newV, oldV);
},
{
immediate: true, //是否立即调用一次
deep: true, //是否开启深度监听 只有深度监听才能收听到ref对象
}
);
reactive与deep深度监听
//reactive
const mesage1 = reactive({
nav: {
tit: {
name: "neeko",
},
},
});
watch(
mesage1,
(newV, oldV) => {
//若监视的是reactive定义的响应式数据,则无法正确获得oldValue
console.log(newV, oldV);
},
{
immediate: true, //是否立即调用一次
deep: false, //是否开启深度监听 即时关闭深度监听,reactive作为监听深层对象也能监听到
}
);
watchEffect自动
收集函数中依赖数据,依赖项更新时重新执行,不用像watch前面指定依赖项watchEffect((oninvalidate) => {
oninvalidate(() => {
//触发监听之前会调用这个函数
//可以进行一些逻辑,例如防抖
});
console.log(mesage);
console.log(mesage1);
});
停止更新
const stop = watchEffect(
() => {
console.log(mesage);
console.log(mesage1);
}
);
//调用函数名,停止更新
stop()
配置项
const stop = watchEffect(
() => {
const btn: HTMLElement = document.getElementById("btn") as HTMLElement;
console.log(btn);
},
{
flush:"post",
onTrigger () {
debugger
}
}
);
flush属性 刷新时机
onTrigger 调试watchEffect
updated
的方法即会调用//added
onRenderTracked
onRenderTriggered //注册一个调试钩子,当响应式依赖的变更触发了组件渲染时调用。
//父组件
let title: string = "关于 Watch";
//子组件
const props=defineProps({
title: { type: String, default: "默认值" },
});
接收props ts方式
//无默认值
const props = defineProps<{
title:string;
}>();
console.log(props.title);
//有默认值
type Props = {
title?: string,
data?: number[]
}
withDefaults(defineProps(), {
title: "张三",
data: () => [1, 2, 3]
})
//子组件
const emit=defineEmits(['value']) //注册
const send=()=>{ //发送
emit('value','neeko')
}
//父组件
const getChildValue=(v:string)=>{
console.log(v);
}
//子组件
const props=defineProps({
flag: { type: boolean, default: "false" },
});
const emit=defineEmits(['update:flag']) //注册
const send=()=>{ //发送
emit('update:modelValue',!flag)
}
//父组件
const flag=ref(true)
const getChildValue=(v:string)=>{
console.log(v);
}
Vue3中使用 EventBus 实现兄弟组件传参_Ac的博客-CSDN博客
小满Vue3(Mitt)_小满zs的博客-CSDN博客
//mian.ts
import Index from "./views/index.vue";
export const app=createApp(App)
app.component('Index',Index)
//.vue 页面直接使用
通过循环的方式注册组件
// require.context() 1.指定目录 2.是否加载子目录 3.加载的文件名(正则匹配)
const components= require.context('./', false, /\.vue$/)
export default {
install (app) {
// 根据keys批量注册
components.keys().forEach(path => {
// 导入组件
const component = importFn(path).default
// 组件全局注册
app.component(component.name, component)
})
}
}
//引入main.js
import components from './components/myComponents'
app.use(components)
//父组件传入数据
import Tree from "./views/Tree.vue";
import { reactive } from "vue";
interface Tree {
name: string;
checked: boolean;
children?: Tree[];
}
const data = reactive([
{
name: "1",
checked: false,
children: [
{
name: "1.1",
checked: false,
},
{ name: "1.2", checked: false },
{
name: "1.3",
checked: false,
children: [
{ name: "1.3.1", checked: false },
{
name: "1.3.2",
checked: false,
children: [{ name: "1.3.3.1", checked: false }],
},
{ name: "1.3.3", checked: false },
],
},
],
},
{ name: "2", checked: false },
{ name: "3", checked: false },
]);
//Tree 组件
{{
v.name
}}
?. 可选链操作符
var a={}
a.children //undefined
a.children.length //报错
a.children?.length //undefined 防止报错
?? 双问号表达式
a.children?.length ?? []
??的作用:当左边为null或undefined(不包括0,false哦!!!),才会执行右边
多个组件使用一个挂载点,并且可以做到动态切换
第一种方法
{{ v.name }}
import A from "./components/tab/A.vue";
import B from "./components/tab/B.vue";
import C from "./components/tab/C.vue";
const coms = ref(A);
const com = reactive([
{
name: "组件A",
con: A,
},
{
name: "组件B",
con: B,
},
{
name: "组件C",
con: C,
},
]);
//通过点击事件切换组件
const boxTabClick = (con: any) => {
coms.value = con;
};
会产生报错
原因:比如ref(A)打印出来会有大量属性,会将组件信息做劫持,但是没必要且浪费性能。
解决办法:v3提供两个跳过属性的api
import { ref, reactive,markRaw,shallowRef } from "vue";
import A from "./components/tab/A.vue";
import B from "./components/tab/B.vue";
import C from "./components/tab/C.vue";
const coms = shallowRef(A);
const com = reactive([
{
name: "组件A",
con: markRaw(A),
},
{
name: "组件B",
con: markRaw(B),
},
{
name: "组件C",
con: markRaw(C),
},
]);
const boxTabClick = (con: any) => {
coms.value = con;
};
第二种方法
{{ v.name }}
Suspense | Vue.js
实验性功能,稳定之前相关 API 也可能会发生变化
使用 ,那么顶层
await
表达式会自动让该组件成为一个异步依赖
//异步组件
{{data}}
//使用 插入组件
2023.6.8
//组件放入插槽
//使用 插入 组件
//简写
我是A
我是B
我是C
//组件放入插槽
//父组件接收数据并使用
{{ data.name }} -- {{ data.age }}
//子组件传入数据
//父组件切换插槽名称
我在这里
const slotName=ref('C') //通过切换名字,插入不同的插槽中
//子组件命名插槽
用于组件传值,根组件注册Provide传递值,所有的后代组件通过Inject接收值
父组件和子组件任意方改动数据时都会同步更新,但建于后续的维护,官方的推荐做法是由调用 provide
函数的组件负责维护,这里便是由父组件A.vue
负责管理 provide
的数据, 子组件只需 inject
进来调用即可,遵循自顶向下规则,这里我们按照官方的规范稍作修改:
//父组件
vue3新增内置组件,可以将模板代码,渲染至任何一个dom节点,不受父级style、v-show等,影响。类似react的create portal
子组件使用
当我们不希望组件重新渲染影响体验;处于暂时消失但是内容处于保存状态;或处于性能考虑避免重新渲染。
举个栗子:当使用v-if使组件卸载时,使用keep-alive可以保存组件的状态,input框(输入型dom)的内容也不会消失,下次开启能正常使用
//父组件
//子组件 son1
son1:
//子组件 son2
son2:
include属性 指定缓存组件的名称,支持字符串、正则、数组
max属性 指定缓存组件的数量
限制可被缓存的最大组件实例数。类似 LRU 缓存 缓存的实例数量即将超过指定的那个最大数量,则最久没有被访问的缓存实例将被销毁,以便为新的实例腾出空间。
开启keep-alive,生命周期有影响:
//onMounted初始化只运行一次,多次初始化操作放在onActivated中
onMounted(()=>{/*初始化*/})
onActivated(()=>{/*keep-alive 初始化*/})
//做一些卸载的操作可以写到onDeactivated,不会走onUnmounted生命周期
onDeactivated(()=>{/*keep-alive 卸载*/})
onUnmounted(()=>{/*卸载 */})
学习Vue3 第二十五章(TSX)_.tsx_小满zs的博客-CSDN博客
vue3中$on,$off 和 $once 实例方法已被移除,组件实例不再实现事件触发接口;习惯了使用EventBus,可以使用Mitt库
小满Vue3(Mitt)_小满zs的博客-CSDN博客
解决在开发中的导入问题,会在根目录生成一个auto-import.d.ts,这个文件会将所有的插件导入到global中,直接使用就可。
unplugin-auto-import的使用_香菜+的博客-CSDN博客
小满Vue3(彩蛋)_哔哩哔哩_bilibili
Less 快速入门 | Less.js 中文文档 - Less 中文网
Sass教程 Sass中文文档 | Sass中文网
npm install less -D 安装即可
npm install sass -D 安装即可
什么是scoped?
什么是bem?
block
块层、element
元素层、modifier
修饰符层.block {} 块级
.block-element {} 块级-元素级
.block-element--modifier {} 块级-元素级__修饰符级
挖个坑 sass 编写bem结构 p15 3:14
项目中 util文件夹创建 getIcon.ts文件
// 获取assets静态资源
function getIcon(name: string) {
return new URL(`../assets/images/${name}`, import.meta.url).href;
}
export default getIcon;
引入和使用
//引入在组件中
import getIcon from "../../util/getIcon";
//使用
项目的根目录下创建一个xxx.d.ts文件,文件名自定义即可,后缀必须是.d.ts
//写入内容
declare module '*.vue' {
import { ComponentOptions } from 'vue'
const componentOptions: ComponentOptions
export default componentOptions
}
保存代码,并且重启项目