关于踩坑日记,以及七七八八知识点联想

//注:此文语法vue

关于踩坑日记,以及七七八八知识点联想

  • 1、vue中图片
  • 2、对象构解
  • 3、location
  • 4、浅拷贝
  • 5、深拷贝
  • 6、滚动条位置scroll.passive
  • 7、vue中computed和watch区别
  • 8、vue双向绑定的原理
  • 9、vue $listeners $attrs 使用
  • 10、javaScript单线程问题
  • 11、promise.all //promise.race
  • 12、store理解
  • 13、let、var、const
  • 14、vue概述
  • 15、作用域、变量提升、闭包、原型链、构造函数、原型
  • 16、await、async
  • 17、vue-router路由history、hash区别
  • 18、浏览器兼容
  • 19、继承
  • 20、冒泡事件
  • 21、js定义对象
  • 22、数组去重
  • 23、如何解决跨域
  • 24、跨域三要素
  • 25、防抖 节流 使用场景
  • 26、created和mounted区别
  • 27、钩子函数
  • 28、回流 重绘
  • 29、一个完整的url解析
  • 30、前端可以做的性能优化
  • 31、vue2与vue3区别
  • 32、v-if、v-for优先级
  • 33、vue2、vue3
    • 生命周期
    • Fragment多根节点
    • Composition API
    • Suspense 异步组件
    • Teleport瞬移组件
    • 响应式原理
    • watch、watchEffect
      • watch
      • watchEffect
    • 插槽
    • 自定义指令
    • v-model
    • v-if、v-for
    • v-bind
    • 组件通信
      • props、$emit
    • attrs
    • v-model
      • ref、expose
      • provide、inject
    • mitt
    • 状态管理pinia
    • 数据持久化
    • 路由
    • css
      • 样式穿透
      • 绑定变量
    • Options API 与 Composition API
      • Options API
      • Composition API
  • 链接

1、vue中图片

:src="三元判断条件 ? ****(图片路径) : ****(图片路径)"
将图片路径放入data中,如 变量名: require("../../相关路径")  注require要加,web端可显示,但是h5部分手机不显示

2、对象构解

let select = { id: 123, name: 'ab' };
let old = { id: 123, name: '123' };
//当select的值赋值给old时候
this.old = { ...this.select }  //构解赋值:不使用该方法,如果select每次改变,old里的值都会跟着改变

//以此类推 如果是数组
let select = [{},{},{}];
let old = [];
this.old = [ ...this.select ];


//对象构解
//例子:
function getData() {
	return { a: 42, b: 'foo' }
}
let { a,b } = this.getData();
console.log(a,b);    //42,'foo'
	//相当于
	let res = getData();
	let a = res.a;
	let b = res.b;
//使用场景:接口
const { data } =  api.接口名();
//{data}应用时data, 接口返回数据 { data: {}, result: {}, code: 200}

3、location

//window.location.search 截取的url路径 ?nToken=……&id=……
//decodeURL 解决的是url地址传参中文乱码处理
decodeURL(window.location.search)  

location对象

属性 描述
hash 设置或返回从井号(#)开始的URL
host 设置或返回主机名合当前URL的端口号
hostname 设置或返回完整的URL
href 设置或返回完整的URL
pathname 设置或返回当前URL的路劲部分
port 设置或返回当前URL的端口号
protocol 设置或返回当前URL的协议
search 设置或返回从问好?开始的URL(查询部分)

4、浅拷贝

object.assign(目标对象,源对象):将所有可枚举属性值从一个或多个源对象复制到目标对象,同事返回目标对象
浅拷贝就是拷贝第一层的基本类型值,以及第一层的引用类型地址
*总结:*object.assign()拷贝的是属性值,假如源对象的属性值是一个指向对象的引用,只拷贝哪个引用值
基本类型值直接拷贝,而引用类型值拷贝地址!!!
引用类型值指的是obj等

let a = {name: 'ab', age: 18};
let b = {name: 'wu', book: {title:'123'}};
let c = object.assign(a, b);
console.log(c);
// {name: 'wu',age: 18, book: {title: '123'}}

console.log(a === c)  //true
//原因:object.assign把源对象b的值复制到目标对象a中,把返回值定义为对象c[返回对象c,就是目标对象a]

b.name = 'change';
b.book.title = '55';
console.log(b)  
//{name: 'change',book: {title: '55'}}
//修改对象b的基本类型值(name)和引用类型值(book)

console.log(a)
//{name: 'wu',book: {title: '55'}}
//原因浅拷贝之后目标对象a的基本类型值没有变,但引用类型值发生改变

String类型和Symbol类型的属性都会被拷贝,不会跳过值为null和undefined的源对象

let a = {name:'muyiy', age: 18}
let b = {b1: Symbol('muyiy'), b2: null, b3: undefined}
let c = object.assign(a, b);
console.log(c)
//{name: 'muyiy', age: 18, b1: Symbol('muyiy'), b2: null, b3: undefined}

console.lgo(a === c)  //true
// searchData = {}, type = null,将两者结合在一起
params = {...this.searchData,type: this.type }  //方法一
params = object.assign(this.searchData,{type: this.type})  //方法二
//注意 用方法二的时候,对params里的数据或类型发生改变时,很有可能使this.searchData里的数据或者类型跟着改变

5、深拷贝

(1)、JSON方法(数组)

var obj2 = JSON.parse(JSON.stringify(obj1))

//拓展
//JSON.parse()将JSON转一个对象
//JSON.stringify()将对象改成字符串
let arr = [1,2,3]
JSON.stringify(arr);  //'[1,2,3]'
typeof JSON.stringify(arr);  //string

let string = '[1,2,3]'
JSON.parse(string)  //[1,2,3]
typeof JSON.parse(string)  //object

//JSON.parse字符串必须双引号包裹不然出错

//引用场景(一)
//判断数组是否包含某对象(1)
let data = [
	{name: 'echo'},
	{name: 'tingfeng'},
	{name: '23'}
],
val = {name: 'wuhaha'}
JSON.stringify(data).indexOf(JSON.stringify(val)) !== -1  //true
//判断两数组、对象是否相同(2)
let a = [1,2,3];
let b = [1,2,3];
JSON.stringify(a) === JSON.stringify(b)  //true

//引用场景(二)
//localStorage/sessionStorage可以存储对象
//默认只能存储字符串,需要用到JSON.stringify将对象转化为字符串,在取出缓存时,需要配合JSON.parse()转回即可
//存
window.localStorage.setItem(key, JSON.stringify(val))
//取
JSON.parse(window.localStorage.getItem(key))

//引用场景(三)
//开发过程中,怕影响原始数据,所以深拷贝一份数据作为操作数据
let arr = [1,2,3]
let _arr = JSON.parse(JSON.stringify(arr));
arr[0] = 2;
console.log(arr, _arr) //[2,2,3] [1,2,3]

//JSON.stringify()与toString()区别
let arr = [1,2,3]
JSON.stringify(arr);  //'[1,2,3]'
arr.toString();  //1,2,3

6、滚动条位置scroll.passive

//关于进入父页面滚动条滚动页面,点击进入子页面后返回父页面,要求定位道之前滚动条滚动得位置

<div style="overflow: auto; height: 100%;" id="flowDetail" @scroll.passive="getScroll"></div>
methods: {
//(1)实时监测位置
getScroll(event) {
	this.curscrolltop = event.target.scrollTop;  //记录当前得高度
}
//(2)在点击事件时将curscrolltop 放入vuex中
//(3)在初始加载数据created时,将查询接口数据,渲染完成后(可以使用async\await)
this.$nextTick(() => {
//1
document.querySelector("#flowDetail").scrollTop = (存放在vuex中得curscrolltop);
//2并将vuex中得curscrolltop数据清空
})
}

7、vue中computed和watch区别

//(1)功能上computed是计算属性、watch是监听一个值得变化,然后执行对应的回调
//(2)缓存: computed 中的函数所依赖的属性没有发生变化,那么嗲用的函数时会从缓存中读取,而watch每次监听的值发生变化时都会执行回调
//(3)return:computed必有return,watch不是必须的
//(4)computed默认第一次加载的时候就开始监听,watch第一次加载并不监听如需第一次监听得到immediate:true
//使用场景:computed一个值受多个值得影响 --购物车(商品结算)watch值会影响多条数据  --搜索框

8、vue双向绑定的原理

//访问属性 object.defineProperty()
var obj = {};
Object.defineProperty(obj,"hello",{
	get: function() { return sth },
	set: function() { //to do sth }
})
obj.hello;   //可以想普通属性一样读取访问属性
//访问属性,读取或设置访问属性的值用get和set函数
obj.hello;  //用的是get函数并返回get函数的返回值
obj.hello = "abl";  //用set函数

9、vue $listeners $attrs 使用

//$attrs 父传孙专用 (子组件中 v-bind="$attrs")值
//$listeners 孙传父(子组件时 v-on="$listeners")事件
//具体实例看之前博客,有图片解释,有例子

10、javaScript单线程问题

//同步任务
//异步任务:宏任务、微任务
//宏任务:srcript外层同步代码入口?、setTimeout、setInterval
//微任务:nextTick、promise
//nextTick优先级高于promise(new promise构造函数,这个过程同步的,then是异步的)
//优先级: 同步任务>微任务>宏任务

11、promise.all //promise.race

//promise.all处理多个异步处理,获得的成功结果的数组里面的数据顺序和promise.all接收到的数组顺序一致(p1结果在前,哪怕p1执行顺序的时间比p2晚)
//promise.race 赛跑的意思,promise.race([p1,p2])里面那个结果快,就返回哪个结果,不管结果本身是否成功还是失败状态
//详细例子看之前博客

12、store理解

//vuex 全局状态对象,存放在内存中,一旦刷新vuex的值变成默认值
//localStorage是当前状态永久存放浏览器中,刷新仍存在

//state  保存一些变量
//getters  相当于compued,用于监听state的变化,并返回计算后的结果(读取)
//mutations  修改state的变量值
//actions 实现对mutations中的方法提交
//modules

//vuex解决刷新数据丢失的问题?
//监听beforeunload事件,离开时将store数据写入localStorage存储中,刷新时将数据读出并写入store

13、let、var、const

//let(变量) 
//支持函数和块作用域(for,if)*
//不支持声明前置
//不支持重复定义
//解决循环中得变量

//var(变量)
//只支持函数作用域
//支持前置声明
//支持重复定义

//const(常量)
//一旦定义无法修改变量
//支持函数作用域和块作用域(for if)
//不支持声明前置
//不能用于定义循环变量
//数据值只能是值类型,引用类型时特性失效

14、vue概述

//技术特点: (1)单页面应用——好处:减少对服务器的请求次数,提高用户体验;(2)vuex管理;(3)数据绑定——目的:简化DOM操作;(4)组件化开发
//mvvm 视图和DOM数据(标签)——view 、module(data数据)、view module(methods数据)

15、作用域、变量提升、闭包、原型链、构造函数、原型

//作用域: 使用函数变量的范围——减少全局污染
//作用域: 在项目methods里定义一个函数,函数内部使用变量,变量在data实例对象里(场景*)
//作用域链: 嵌套函数之间的作用域的关联现象

//变量提升:js运行预处理——记录所在作用域中声明的变量、声明的函数;当函数和变量同名时,以函数为准

//闭包 定义:函数内部定义,可以访问外部变量的函数
//闭包 作用:通过内部函数获取外部函数定义的变量;实现对象的成员变量的私有化,减少全局污染
//闭包 应用(1)减少全局变量的定义 (2)封装对象的成员
//闭包 缺点(1)内存损耗 (2)逻辑错误

//原型链:从实例到父类到object对象——体现了js继承性
//构造函数:用new关键字调用的函数。构造函数首字母大写
funciton Person() {}  //这是一个非常普通的构造函数
var per = new Person()  //构造函数实例化一个对象
console.dir(Person)
console.dir(per)
//(通过后台输出可以看出)构造函数里有一个prototype属性,实例对象有一个_proto_属性,这两个属性叫原型,也叫原型对象

//原型指向
//实例对象的原型_proto_的指向
function Person() {}
var per = new Person()
per._proto_ = Person.prototype;  //true
//实例对象per的原型_proto_,指向构造函数Person的prototype
//结论:实例对象_proto_指向构造函数的原型对象prototype

//原型作用(1)共享数据;(2)实现继承
//原型链:是一种关系,实例对象和原型对象prototype之间的关系,关系通过_proto_来关联.
//原型链最终指向:object的prototype的_proto_是null。

16、await、async

//await必须在async内部使用

//async 异步;该方法返回的是一个promise对象
async function f() {
	return result;
}
f().then(result){
	//处理异步代码返回结果
}.catch(e){
	console.log(e);
}

//await 同步;同步修饰的方法执行时将会阻塞,等异步方法的结果返回后才能继续往下执行
async function() f{
	//await 修饰的函数即便是异步处理,也要等待结束后在往下执行
}

17、vue-router路由history、hash区别

//hash url中带有#,但http请求的时候不包括在其中,对后端无影响
//history html5中新增了pushState()和replaceState()方法(特定浏览器支持),这种模式需要后端配合

18、浏览器兼容

//老版本浏览器用webkit前缀
//新版本浏览器用hack技术

//hack:条件hack;属性hack;选择器hack

19、继承

//类继承:通过call和apply实现;父类名.call(this,params1,params);父类名.apply(this,[params1,params2])
//原型继承:子类名.prototype = 父类名.prototype
//组合继承

20、冒泡事件

//事件由具有dom节点接收,然后逐级向上传播到不具体的节点。是一种由内而外的传播

//冒泡:由内而外(从下往上)
//捕获:由外道内(从上往下)
//document
//element html
//element body
//element div

//如何阻止冒泡?
if(event) {
	event.stopPropagation();  //w3c
} else {
	window.cancelBubble = true;  //IE8
}

21、js定义对象

//字面量方式
//object方式
//构造方式
//原型方式
//混合方式
//对象验证方法: instanceof

22、数组去重

let arr1 = [10, 5,3,7,8,9]
let arr2 = []
for(let i = 0; i < arr1.length; i++) {
	if(arr2.indexOf(arr1[i] !== -1) {
		arr2.push(arr1[i])
	}
}

for(let i = 0; i < arr1.length; i++) {
	for(let j = 0;j < arr1.length; j++) {
		if(arr1[i] === arr2[j]) {
			arr1.splice(i,1)在这里插入代码片
		}
	}
}

23、如何解决跨域

//浏览器为了安全的权限
//vue通过proxy来跨域(线下)
//nginx和cors(线上),cors后台做的、nginx前后台都可以
//jsonp之前的方式

24、跨域三要素

//端口 域名 协议(只要有任何一个不同就会产生跨域问题)

25、防抖 节流 使用场景

//节流:在短时时间内不进行频繁触发
//连续触发事件但在n秒内只执行一次
//使用场景:时间戳;定时器;页面上的某些按钮有可能会连续的点击
//定时器实现模式: 定时器在延时时间执行过后,重置为null,定时器为null时,重新设置定时器
function throttle(fn, delay) {
	let valid = true;
	return function() {
		if(!valid) { return false; }   //休息时间,暂不接客人
		valid = false;
		setTimeout(() => {  //工作时间,执行函数并且在间隔期把状态改成无效
			fn();
			valid = true;  
		}, delay)		
	}
}


//防抖:简单理解就是不管函数在执行了多少次,最终只能执行成功一次就是最后一次。
//触发事件后在n秒内函数只能执行一次,如果在n秒内又触发了事件,则会重新计算函数执行时间
function debounce(fn, delay) {
	let timer = null;
	return fucntion() {
		if(timer) {
			clearTimeOut(timer)
		}
		timer = setTimeout(fn, delay)
	}
}

26、created和mounted区别

//created: 在模板渲染html前调用,即通常初始化某些属性值,然后再渲染成视图。
//mounted: 在模板渲染htmL后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作。

27、钩子函数

//生命周期
//beforeCreate
//create
//beforeMounted
//mounted
//beforeUpdate
//update
//beforeDestroy
//destroy

//自定义指令
//bind()  绑定指令到元素上,只执行一次
//inserted() 绑定了指令的元素插入到页面中展示时调用,很常用
//update() 所有组件节点更新时调用
//componentUpdate() 指令所在的节点及其子节点全部更新完成调用
//unbind() 解除指令和元素的绑定,只执行一次

//路由守卫 导航守卫
//全局守卫
(1)前置: router.beforeEach((to, from, next) => {})
(2)后置:router.afterEach((to, from) => {})
//路由独享守卫 
	beforeEnter:(to, from, next) => {}
//导航守卫
(1)beforeRouteEnter(to, from, next) {}
(2)beforeRouterLeave(to, from, next) {}

//watch computed也是钩子函数

28、回流 重绘

//重绘:当我们对 DOM 的修改导致了样式的变化、却并未影响其几何属性(比如修改了颜色或背景色)时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式(跳过了上图所示的回流环节)。
//回流:当我们对 DOM 的修改引发了 DOM 几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性(其他元素的几何属性和位置也会因此受到影响),然后再将计算的结果绘制出来。这个过程就是回流(也叫重排)

//回流必定引发重绘,重绘不一定引发回流。回流代价比重绘高

//哪些操作引发回流1DOM的添加和删除;
(2)页面的加载;
(3)元素尺寸改变——边距、填充、边框、宽度和高度;
(4)元素位置的改变;
(5)内容变化,比如用户在input框中输入文字;
(6)浏览器窗口尺寸改变——resize事件发生时;
(7)获取某些属性:offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、(8)scrollLeft、scrollWidth、scrollHeight、 clientTop、clientLeft、clientWidth、(9)clientHeight。(浏览器为了返回最精确的值,需要flush队列,因为队列中可能会有影响到这些值的操作)

https://www.cnblogs.com/charliePU/p/10749730.html

29、一个完整的url解析

关于踩坑日记,以及七七八八知识点联想_第1张图片

// 协议 auth host(hostname+port端口号)path(pathname+query) hash

30、前端可以做的性能优化

参考链接
前端优化几个方向:

  • 网络优化
  • 页面渲染优化
  • JS优化
  • 图片优化
  • webpack打包优化
  • React优化
  • Vue优化

31、vue2与vue3区别

参考链接

32、v-if、v-for优先级

vue2里v-for优先,vue3里v-if优先

33、vue2、vue3

生命周期

整体变化不大,大部分生命周期名称上+‘on’,功能上是类似的,vue3在组合式API(composition API)中使用生命周期钩子时需要先引入,而vue2在选项API(Options API)中可以直接调用生命周期

// vue3
<script setup>
import { onMounted } from 'vue'; // 使用前需引入生命周期钩子
onMounted(() => {
	// ...
})
// 可将不同的逻辑拆开多个onMounted,依然按顺序执行,不会被覆盖
onMounted(() => {
	// ...
})
</script>

// vue2
<script>
export default {
	mounted() { // 直接调用生命周期钩子
		// ...
	}
}
</script>

生命周期
vue2 vue3
beforeCreate
created
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeDestroy onBeforeUnmount
destroyed onUnmounted

setup是围绕beforeCreate和created生命周期钩子运行的,所以不需要显式地去定义

Fragment多根节点

vue2中使用多个根节点会报错

// vue2在template里存在多个根节点会报错
<template>
	<header></header>
	<main></main>
	<footer></footer>
</template>

// 只能存在一个根节点,需要一个div来包裹
<template>
	<div>
		<header></header>
		<main></main>
		<footer></footer>
	</div>
</template>

vue3支持多个根节点,也就是fragment。

<template>
	<header></header>
	<main></main>
	<footer></footer>
</template>

Composition API

vue2是选项API(Options API),一个逻辑会散乱在文件不同位置(data、props、computed、watch、生命周期钩子等),导致代码可读性变差。当需要修改某个逻辑时,需要上下文来回跳转文件位置。
vue3组合式API(Composition API)则很好的解决这个问题,可将同一逻辑的内容写到一起,增强代码的可读性、内聚性,其还提供了较为完美的逻辑复用性方案。

Suspense 异步组件

Vue3提供Suspense组件,允许程序在等待异步组件加载完成前渲染兜底的内容,如loading,使用户的体验更平滑。使用它,需在模板中声明,并包括两个命名插槽: default 和 fallback。Suspense确保加载完异步内容时显示默认插槽,并将fallback插槽用作加载状态。

<template>
	<suspense>
		<template #default>
			<List />
		</template>
		<template #fallback>
			<div>Loading...</div>
		</template>
	</suspense>
</template>

在list组件(有可能是异步组件,也有可能是组件内部处理逻辑或者操作过多导致加载过程慢等)未加载完成前,显示Loading…(即fallback插槽内容),加载完成时显示自身(即default插槽内容)。

Teleport瞬移组件

vue3提供Teleport组件可将部分DOM移动到Vue app之外的位置。比如项目中常见的Dialog弹窗。

<button @click="dialogVisible = true">显示弹窗</button>
<teleport to="body">
	<div v-if="dialogVisible">我是弹窗,移动到body标签下</div>
</teleport>

响应式原理

vue2响应式基于Object.defineProperty
vue3响应式基于Proxy

  • Object.defineProperty基本用法:直接在一个对象上定义新的属性或修改现有的属性,并返回对象。
let obj = {}
let name = 'leo'
Object.defineProperty(obj, 'name', {
	enumerable: true,  // 可枚举(是否可通过for...in或Object.keys()进行访问)
	configurable: true,  // 可配置(是否可使用delete删除,是否可再次设置属性)
	// value: '',  // 任意类型的值,默认undefined
	// writable: true,  // 可重写
	get() {
		return name
	},
	set(value) {
		name = value
	}
})

writable、value与getter、setter不共存
vue2无法监听对象或数组新增、删除的元素。
vue2解决:常用数组原型方法push、pop、shift、unshift、splice、sort、reverse进行了hack处理;提供Vue.set监听对象、数组新增属性。

  • Proxy是ES6新特性,通过第二个参数handler拦截目标对象的行为。相较于Object.defineProperty提供语言全范围的响应能力,消除了局限性。
    局限性:
  1. 对象、数组的新增、删除
  2. 监测.length修改
  3. Map、Set、WeakMap、WeakSet的支持
    基本用法:创建对象的代理,从而实现基本操作的拦截和自定义操作。
let handler = {
	get(obj, prop) {
		return prop in obj ? obj[prop] : ''
	},
	set() {
		// ...
	}
}

watch、watchEffect

watch

// vue2用法
watch: {
	// 第一种
	flag(newVal, oldVal) {},
		
	// 第二种
	user: {
		handler(newVal, oldVal) {},
		immediate: true,
		deep: true
	}
}
// vue3
<script setup>
const count = ref(0)
const status = ref(false)

// 监听一个
watch(count, (newVal, oldVal) => {})
// 监听多个
watch([count, status],([newCount, oldCount], [newStatus, oldStatus]) => {})

const user = reactive({
	name: 'aa',
	age: 20,
	sex: 'man',
	hobbies: []
})

// 监听一个
watch(() => user.age, (newVal, oldVal) => {})
// 监听多个
watch([() => user.name,() => user.sex],(newVal,oldVal) => {})

// 添加配置参数
watch(() => user.hobbies,(newVal,oldVal) => {},{
	immediate:true,
	deep:true,
	// 回调函数的执行时机,默认在组件更新之前执行,更新之后执行参数为‘post’
	flush:'pre'
})
</script>

watchEffect

// 正常情况组件销毁自动停止监听
watchEffect(() => {})

// 异步方式手动停止监听
const stopWatch = watch (() => user.hobbies, (newVal, oldVal) => {}, {deep:true})
setTimeout(() => {
	stopWatch()
}, 3000)

watch和watchEffect区别

  • watch对传入的一个或多个值进行监听,触发时会返回新值和旧值,且默认第一次不会执行
  • watchEffect是传入一个立即执行函数,默认第一次会执行,且不需要传入监听的内容,会自动收集函数内的数据源作为依赖,当依赖发生变化时会重新执行函数(类似computed),并且不会返回旧值
  • 正常情况下,组件销毁、卸载后这两种方式都会停止监听,但是异步方式,例如setTimeout里创建的监听需要手动停止

插槽

  • 具名插槽使用方式不同:vue2中使用slot=‘插槽名称’,vue3使用v-slot:插槽名称
  • 作用域插槽使用方式不同:vue2中在父组件中使用slot-scope=“data"从子组件获取数据,vue3在负组件中使用#data或者#default=”{data}"获取
<template>
	<div>
		// 默认
		<slot />
		// 具名
		<slot name="slotName" />
		// 作用域
		<slot :data="user" name="propsSlot" />
	</div>
</template>
<script>
const user = reactive({
	name: 'con',
	age: 20
})
</script>
<template>
	<Son>
		<template #default><div>默认插槽内容</div></template>
		<template #slotName><div>具名插槽内容</div></template>
		<template #propsSlot="scope">
			<div>
				作用域插槽内容:name,{{scope.data.name}};age,{{scope.data.age}}
			</div>
		</template>
	</Son>
</template>
<script setup>
import Son from './Son.vue'
</script>

自定义指令

  • 全局自定义指令在main.js中定义
  • 局部自定义指令在当前组件中定义
app.directive("focus",{
	mounted(el,bingings,vnode,preVnode){
		el.focus()
	}
})
<template>
	<div>
		<input type="text" v-focus />
	</div>
</template>
<script setup>
const vFocus = {
	mounted:(el) => el.focus()
}
</script>

v-model

  • vue2中.sync和v-model是语法糖,都可实现父子组件中数据的双向通信
  • vue2两种格式差别:v-model=“num”, :num.sync=“num”
  • vue2中v-model只能用一次,.sync可以有多个
  • vue3中取消了.sync,合并到v-model,vue3中v-model可以用多个
<template>
	<p>name:{{name}}</p>
	<p>age:{{age}}</p>
	<Son v-model:name="name" v-mode:age="age" />
</template>
<script setup>
import Son from './Son.vue'
const user = reactive({
	name: 'con',
	age: 20
})
const {name,age} = toRefs(user)
</script>
<template>
	<input type="text"  :value="name" @input="onNameInput"  />
	<input type="number" :value="age" @change="onAgeInput" />
</template>
<script setup>
defineProps({
	name: {
		type: String,
		default: () => ""
	},
	age: {
		type: String,
		default: () => ""
	}
})
const emit = defineEmits(["update:name"],["update:age"])
const onNameInput = (e) => emit("update:name",e.target.value)
const onAgeInput = (e) => emit("update:age",e.target.value)
</script>

v-if、v-for

  • 不建议v-for与v-if一起使用
  • vue2中优先级v-for高于v-if
  • vue3中优先级v-if高于v-for
<template>
	<div v-if="flag">
		<div v-for="item in dataList" :key="item.id">{{item.label}}</div>
	</div>
</template>
<script setup>
const flag = ref(true)
const dataList = reactive([
	{
		id: 1,
		label: 'list-01'
	},
	{
		id: 2,
		label: 'list-02'
	}
])
</script>

v-bind

  • vue2中单独声明优先,并且重复定义会发出警告
  • vue3中绑定值是否生效遵循就近原则
<template>
	<div>
		<input type="text" v-bind:disabled="false" :disabled="disabled" />
		<input type="text" :disabled="disabled" v-bind:disabled="false" />
	</div>
</template>
<script setup>
const disabled = ref(true)
</script>

组件通信

props、$emit

父组件传值,子组件通过props接受;子组件想改变父组件中数值,通过$emit调用父组件中方法

// 父组件
<template>
	<Child :count="count" :name="name" :age="age" @add="add" @sub="sub" />
</template>
<script setup>
import Child from "./Child.vue"
const count = ref(0)
const user = reactive({
	name: 'con',
	age: 20
})
const add = () => count.value++
const sub = () => count.value--
const {name, age} = toRefs(user)
</script>
// 子组件
<template>
	<p>接收到的参数:name{{name}},age{{age}},count{{count}}</p>
	<el-button type="primary" size="small" @click="add">count++</el-button>
	<el-button type="primary" size="small" @click="sub">count--</el-button>
</template>
<script setup>
defineProps({
	name: {
		type: String,
		default: () => ""
	},
	age: {
		type: Number,
		default: () => 0
	},
	count: {
		type: Number,
		default: () => 0
	}
})
const emits = defineEmits(["add", "sub"])
const add = () => emits("add")
const sub = () => emits("sub")
</script>

attrs

传递属性或方法给子组件下级组件,传递子组件中没有被props定义的属性,传递子组件中没有被emit定义的方法

// 父组件
<template>
	<Child :count="count" :name="name" :age="age" @add="add" @sub="sub" />
</template>
<script setup>
import Child from './Child.vue'
const count = ref(0)
const user = reactive({
	name: 'con',
	age: 20
})
const add = () => count.value++
const sub = () => count.value--
const {name,age} = toRefs(user)
</script>
// 子组件
<template>
	<p>子组件接收:{{count}}</p>
	<el-button type="primary" size="small" @click="add">count++</el-button>
	<GrandChild v-bind="$attrs" />
</template>
<script setup>
import GrandChild from "./GrandChild.vue"
defineProps({
	count: {
		type: Number,
		default: () => 0
	}
})
const emits = defineEmits(["add"])
const add = () => emits("add")
</script>
// 孙组件
<template>
	<p>孙组件接受:name{{name}},age{{age}}</p>
	<el-button type="primary" size="small" @click="sub">count--</el-button>
</template>
<script setup>
defineProps({
	name: {
		type: String,
		default: () => ""
	},
	age: {
		type: Number,
		default: () => 0
	}
})
const emits = defineEmits(["sub"])
const sub = () => emits("sub")
</script>

v-model

// 父组件
<template>
	<Child v-model:name="name" v-model:count="count" v-model:salary="salary" />
</template>
<script setup>
import Child from './Child.vue'
const name = ref('con')
const count = ref(0)
const salary = ref(3000)
</script>
// 子组件
<template>
	<p>子组件接受的v-model参数:name{{name}},count{{count}},salary{{salary}}</p>
	<el-button type="primary" size="small" @click="changeCount">count++</el-button>
	<el-button @click="changeSalary">salary改变</el-button>
</template>
<script setup>
const props = defineProps({
	name: {
		type: String,
		default: () => ""
	},
	count: {
		type: Number,
		default: () => ""
	},
	salary: {
		type: Number,
		default: () => ""
	}
})
const emits = defineEmits(["update:count","update:salary"])
const changeCount = () => emits("update:count", props.count+1)
const changSalary = () => emits("update:salary",props.salary + 1000)
</script>

ref、expose

通过ref获取指定的DOM元素或组件,结合defineExpose暴露出来的属性和方法实现通信

// 父组件
<template>
	<div>title:{{title}}</div>
	<Child ref="child" />
	<el-button @click="add">count++</el-button>
	<el-button @click="sub">count--</el-button>
	<el-button @click="receive">receive msg</el-button>
</template>
<script setup>
import Child from './Child.vue'
const child = ref(null)
const title = ref('暂无数据')
const add = () => child.value.add()
const sub = () => child.value.sub()
const receive = () => (title.value = child.value.msg)
</script>
// 子组件
<template>
	<p>子组件:count{{count}}</p>
</template>
<script setup>
const count = ref(0)
const msg = 'expose message'
const add = () => count.value++
const sub = () => count.value--
defineExpose({
	msg,
	add,
	sub
})
</script>

provide、inject

祖先向下级传递参数,无论层级多深,都可传递

// 父组件
<template>
	<Child />
</template>
<script setup>
import Child from './Child.vue'
const user = reactive({
	name: 'con',
	age: 20
})
provide("user",user)
</script>
// 子组件
<template>
	<p>子组件接收:name{{user.name}}</p>
	<GrandChild />
</template>
<script setup>
import GrandChild from './GrandChild.vue'
const user = inject("user")
</script>
// 孙组件
<template>
	<p>孙组件接收:age{{user.age}}</p>
</template>
<script setup>
const user = inject("user")
</script>

mitt

vue3中废除api:on、once、off;不再支持Event Bus,选用替代方案mitt.js,原理还是Event Bus

// bus.js
import mitt from 'mitt'
export default mitt()
// 父组件
<template>
	<Brother1 />
	<Brother2 />
</template>
<script setup>
import Brother1 from './Brother1 .vue'
import Brother2 from './Brother2.vue'
</script>
// 兄弟组件1
<template>
	<p>brother1发送事件</p>
	<el-button @click="handleClick">发送事件</el-button>
</template>
<script setup>
import mybus from './bus.js'
const handleClick = () => {
	mybus.emit("title", {title: "hello world"})
	mybus.emit("user", {user:{name:'con',age:20}})
}
</script>
// 兄弟组件2
<template>
	<p>brother2接收事件</p>
	<p>title:{{title}}</p>
	<p>user:name{{name}};age:{{age}}</p>
</template>
<script setup>
import mybus from './bus.js'
const title = ref("")
const user = reactive({
	name: "",
	age: null
})
mybus.on("title",(data)=>{
	title.value = data.title
})
mybus.on("user",(data)=>{
	user.name = data.name
	user.age = data.age
})
</script>

状态管理pinia

(之前写过,可翻阅已往记录)

数据持久化

pinia的数据是存储在内存中的,页面刷新厚数据会丢失;可以支持扩展插件,实现数据持久化

  • npm i pinia-plugin-persist,默认使用sessionStorage
persist:{
	enabled:true,
	strategies:[
		{
			storage:localStorage,
			paths: ["num","user"]
		}
	]
}

路由

  • query传参配置path,params传参配置name,且params中配置path无效
  • query传参显示在地址栏,params传参不会
  • query传参刷新页面数据不会消失,params传参页面数据消失
  • params可以使用动态参数(“/path/:params”),动态参数会显示在地址栏中,且刷新页面数据不会消失
  • name为路由中定义的name属性,严格区分大小写
  • 路由跳转:前进router.go(1)、后退router.go(-1)、刷新router.go(0)
<template>
	<el-button @click="transByQuery">通过query传参</el-button>
	<el-button @click="transByParams">通过params传参</el-button>
	<el-button @click="transByDynamic">动态传递参数</el-button>
</template>
<script setup>
const queryParams = reactive({
	name: 'con',
	age: 20
})
const id = ref('2023')
const router = useRouter()
const transByQuery = () => {
	router.push({
		path: '/basic/querydemo',
		query: queryParams
	})
}
const transByParams = () => {
	router.push({
		name: 'paramsDemo',
		params: queryParams
	})
}
const transByDynamic = () => {
	router.push({
		name: 'dynamicDemo',
		params: {id: id.value}
	})
}
</script>
// query接收参数
const route = useRoute()
console.log(route.query.name,route.query.age)
// params接收参数
const route = useRoute()
console.log(route.params.name,route.params.age)
// 动态传递 接收参数
const route = useRoute()
console.log(route.params.id)
 {
  name: "QueryDemo",
  path: "querydemo",
  redirect: null,
  component: "basic/userouter/querydemo",
  hidden: true,
  meta: {
    title: "query样例",
    icon: null,
  },
},
{
  name: "ParamsDemo",
  path: "paramsdemo",
  redirect: null,
  component: "basic/userouter/paramsdemo",
  hidden: true,
  meta: {
    title: "params样例",
    icon: null,
  },
},
{
  name: "DynamicDemo",
  path: "dynamicdemo/:id",
  redirect: null,
  component: "basic/userouter/dynamicdemo",
  hidden: true,
  meta: {
    title: "dynamic样例",
    icon: null,
  },
},

css

样式穿透

  • css >>> className, less /deep/ className, scss ::v-deep className
  • vue3中css使用 :deep(className)

绑定变量

<template>
	<div class="name">con</div>
</template>
<script setup>
const str = ref('#f00')
</script>
<style lang="scss" scoped>
.name {
	background-color:v-bind(str)
}
</style>

Options API 与 Composition API

vue组件可以用两种不同的API风格编写:Options API和Composition API

Options API

使用Options API,我们使用选项对象定义组件的逻辑,例如data、methods和mounted。由选项定义的属性在this内部函数中公开,指向组件实例,如下

<template>
	<button @click="increment">count is: {{ count }}</button>
</template>
<script>
export default {
	data() {
		return {
			count: 0
		}
	},
	methods: {
		increment() {
			this.count++
		}
	},
	mounted() {
		console.log(`The count value is ${this.count}.`)
	}
}
</script>

Composition API

使用Composition API,使用导入的API函数定义组件的逻辑。在SFC中,Composition API通常使用

<template>
	<button @click="increment">count is: {{ count }}</button>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const count = ref(0)
function increment() {
	count.value++
}
onMounted(() => {
	console.log(`The count value is ${this.count}.`)
})
</script>

链接

https://juejin.cn/post/7073869980411887652

https://juejin.cn/post/7144881028661723167

https://blog.csdn.net/weixin_59519449/article/details/123636668?spm=1001.2100.3001.7377&utm_medium=distribute.pc_feed_blog_category.none-task-blog-classify_tag-8-123636668-null-null.nonecase&depth_1-utm_source=distribute.pc_feed_blog_category.none-task-blog-classify_tag-8-123636668-null-null.nonecase

https://juejin.cn/post/6869678827845451790

https://www.cnblogs.com/Yellow-ice/p/15127392.html

https://blog.csdn.net/monoplasty/article/details/122633713

https://blog.csdn.net/weixin_49487698/article/details/124793415

https://blog.csdn.net/qq_39055970/article/details/126502302?spm=1000.2115.3001.6382&utm_medium=distribute.pc_feed_v2.none-task-blog-personrec_tag-10-126502302-null-null.pc_personrec&depth_1-utm_source=distribute.pc_feed_v2.none-task-blog-personrec_tag-10-126502302-null-null.pc_personrec

https://juejin.cn/post/7079447275755274254

https://blog.csdn.net/qq_33957603/article/details/124510636?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%89%8D%E7%AB%AF%E6%B5%81%E7%A8%8B%E5%9B%BE&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-5-124510636.142v70control,201v4add_ask&spm=1018.2226.3001.4187

https://juejin.cn/post/7129412947931037732

https://es6.ruanyifeng.com/#docs/array

https://juejin.cn/post/6996841019094335519

你可能感兴趣的:(javascript,vue.js,前端)