vue笔记
- 不定时更新说明
- Vue: 给 src 文件夹配置别名,通常用“@”
- Vue:v-on(@)事件修饰符
- Vue: 异步编程的一种解决方案 Promise
- Vue: 路由懒加载
- Vue2:获取元素距浏览器上边框和左边框的位置
- Vue2:ctrl+s/z/y 保存/上一步/下一步
- Vue2:v-bind="$attrs"多层级组件传参
- Vue2:调用父组件或子组件的属性以及方法 $parent / $refs
- Vue2:数字切换滚动样式
- vue2: 父子组件生命周期函数执行顺序
- Vue3:toRaw 方法和 markRow 方法
- vue3: i18n语言包的配置使用
- Vue3: 自定义指令 + ResizeObserver 实现对元素的宽高以及显隐监听
不定时更新说明
本文记录自己部分所学,将会不定时更新,对待技术,俺是认真的
Vue: 给 src 文件夹配置别名,通常用“@”
在 jsconfig.json 文件中添加如下代码:
{
"ocmpilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
},
"exclude": ["node_modules", "dist"]
}
Vue:v-on(@)事件修饰符
.native // 主要是给自定义组件(标签上)添加原生事件
.top // 阻止冒泡事件
.prevent // 阻止默认事件
.once // 监听事件只执行一次
@keydown // 任意的键盘键按下
@keyup // 任意键盘键抬起
@keyup.enter // enter键抬起
@keydown.b // b键按下
Vue: 异步编程的一种解决方案 Promise
3种状态:
Pending:进行中
Resolved:已完成
Rejected:失败
开始创建都是pending,只要执行相应的resolve或者reject都要相应改变状态,状态改完后是改不回来的
链式调用以及语法糖
var oPromise=new Promise(function(resolve,reject){
resolve(1);
})
oPromise.then(function(value){
console.log(value); //1
return value*2;//return new Promise(function(resolve,reject){ resolve(value*2)}
}).then(function(value){
console.log(value); //2
}).then(function(value){
console.log(value); //undefined
//return Promise.resolve('resolve') //return new Promise(function(resolve,reject){ resolve('resolve')}
return 'resolve'
}).then(function(value){
console.log(value); //resolve
return Promise.reject('reject') //return new Promise(function(resolve,reject){ reject('reject')}
}).then(function(value){
console.log(value)
},function(err){
console.log(err) //reject
})
all 和 race
all:全部执行完成才停止
race:有一个执行完成就停止
//all(全部执行完成才停止)
function task(delay) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(delay);
}, delay);
});
}
var startDate = Date.now();
// 所有promise变为resolve后程序退出
Promise.all([ //里面的是一个个promise的任务
task(1), //这个任务是1秒钟后变为resolve状态
task(32), //这是32
task(64),
task(128)
]).then(function (values) {
console.log(Date.now() - startDate + 'ms');
// >= 128ms
console.log(values); // [1,32,64,128]
});
-------------------------------------------------
//race(只要有一个执行完就停止)
function task(delay) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(delay);
}, delay);
});
}
// 任何一个promise变为resolve或reject 的话程序就停止运行
Promise.race([
task(1),
task(32),
task(64),
task(128)
]).then(function (value) {
console.log(value); //1
});
Vue: 路由懒加载
解决白屏问题,优化用户体验
const HelloWorld = () => import('@/components/HelloWorld.vue')
export default new Router({
routers:[
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
}
]
})
Vue2:获取元素距浏览器上边框和左边框的位置
const element = document.getElementById('...')
let posTop = getElementTop(element)
let posLeft = getElementLeft(element)
let position = getElementPosition(element)
// 计算盒子离浏览器上边框的距离
getElementTop (element) {
let actualTop = element.offsetTop
let current = element.offsetParent
while (current !== null) {
actualTop += current.offsetTop
current = current.offsetParent
}
return actualTop
},
// 计算盒子离浏览器左边框的距离
getElementLeft (element) {
let actualLeft = element.offsetLeft
let current = element.offsetParent
while (current !== null) {
actualLeft += current.offsetLeft
current = current.offsetParent
}
return actualLeft
},
// 合在一起
getElementPosition (element) {
let actualTop = element.offsetTop
let actualLeft = element.offsetLeft
let current = element.offsetParent
while (current !== null) {
actualTop += current.offsetTop
actualLeft += current.offsetLeft
current = current.offsetParent
}
let elementPosition = {
top: actualTop,
left: actualLeft
}
return elementPosition
},
Vue2:ctrl+s/z/y 保存/上一步/下一步
1. 在 mouted 中设置监听事件
mouted(){
// 设置监听, ctrl + s 保存, ctrl + z 上一步, ctrl + y 下一步
document.addEventListener('keydown', this.onCtrlEvent)
}
2. 编写执行函数
onCtrlEvent(e) {
if (e.ctrlKey && e.code === 'KeyS') { // ctrl+s 保存
// 保存 fun()
e.preventDefault()
} else if (e.ctrlKey && e.code === 'KeyZ') { // ctrl+z 上一步
// 上一步 fun()
e.preventDefault()
} else if (e.ctrlKey && e.code === 'KeyY') { // ctrl+y 下一步
// 下一步 fun()
e.preventDefault()
}
}
3. 在 destroyed 中移除监听事件,以免全局作用
destroyed() {
document.removeEventListener('keydown', this.onCtrlEvent)
}
Vue2:v-bind="$attrs"多层级组件传参
第一层组件正常传参
中间组件使用 v-bind=“$attrs”,可以简化代码
接受参数组件正常接收参数
来自大佬的评论补充:
v-bind="$attrs"如果要全部传到第三层,那么需要中间组件不能在 props 中接收参数,如果在中间组件的 props 里面接收到了一部分参数,那么传到下一层的应该只是剩余参数了,并且默认中间组件未使用的参数显示在中间件的dom元素上
Vue2:调用父组件或子组件的属性以及方法 $parent / $refs
例如组件层级为 A ====> B ====> C
B 组件调用父组件 A 的方法以及属性
this.$parent.方法名()
this.$parent.属性名
B 组件调用子组件 C 的方法以及属性
this.$refs.子组件的ref名.方法名()
this.$refs.子组件的ref名.属性名
Vue2:数字切换滚动样式
依赖组件于 vue-digital-transform
1. 下载组件
npm i vue-digital-transform --save
2. 引用组件
import DigitalTransform from 'vue-digital-transform'
3. 使用组件
<DigitalTransform
:value="number"
:interval="interval"
:dislocation="dislocation">
</DigitalTransform>
4. 全部代码
<template>
<div class="hello-world">
<div class="number">
<DigitalTransform
:value="number"
:interval="interval"
:dislocation="dislocation"
></DigitalTransform>
</div>
<div class="control">
<button @click="changeNumber">change</button>
</div>
</div>
</template>
<script>
import DigitalTransform from 'vue-digital-transform';
export default {
components: {
DigitalTransform,
},
data() {
return {
number: 1000, // 展示数字
interval: 500, // 切换时间
dislocation: false, // 错位
};
},
methods: {
// 随机切换数字
changeNumber() {
this.number = Math.round(Math.random() * 1000);
},
},
};
</script>
<style lang="less" scoped>
.hello-world {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.number {
width: 100%;
height: 80px;
line-height: 80px;
font-size: 80px;
color: green;
display: flex;
justify-content: center;
}
}
</style>
vue2: 父子组件生命周期函数执行顺序
-
加载渲染过程
-
子组件更新过程
-
父组件更新过程
-
销毁过程
Vue3:toRaw 方法和 markRow 方法
在 setup 函数中,我们使用 ref 函数和 reactive 函数将基本数据或者对象数据创建成 Proxy 代理对象(响应式数据),以达到双向绑定的目的,但是某些情况下我们不需要对其进行双向绑定,这导致性能的消耗,尤其是当对象的内容很多的时候,会导致页面的卡顿。
toRaw函数: 通过该方法我们获取 Proxy 代理对象包装前的原始数据,下面是简单的例子
import { reactive, toRaw } from "vue";
export default {
setup() {
// 获取 reactive 包装的原始数据
const obj1= {name:'崔堂袁'};
const user = reactive(obj1);
const obj2 = toRaw(user);
console.log(obj1 === obj2); //true
// 获取 ref 包装的原始数据
const ctyAge1 = ref(24);
const ctyAge2 = toRaw(ctyAge.value); // ctyAge === 24 ? true
return {..................};
},
};
注意:ref 包装的为什么要用 .value ?因为 ref 的本质还是 reactive,即 ref( obj ) 相当于 reactive( { value: obj } )
markRaw 函数: 用该方法标记的对象永远不会被追踪,简单理解就是不会给被 markRaw 标记的对象创建响应式数据,就算标记之后用 reactive 将其包装成 Proxy,不报错,也不会被创建成 Proxy 代理对象。该方法本质是为源对象增加一个属性标志__v_skip: true,意思是告诉 vue,若要代理该对象,请跳过处理,简单的例子在下面。
import { reactive, markRaw } from "vue";
export default {
setup() {
const obj = markRaw( { name: 'cty', age: 24 } )
const objUser = reactive(obj)
console.log(objUser) // {name: 'cty', age: 24, __v_skip: true}
return {..................};
},
};
vue3: i18n语言包的配置使用
1. 下载最新版本的 i18n 模块
npm install vue-i18n@next
目前使用 npm install vue-i18n 下载的版本是不支持 vue3.x 的,所以使用上面的代码获取最新的版本
2. 在 src 目录下创建一个文件夹 language,并在 language 下创建配置文件 index.js、中文包文件 cn.js、英文包文件 en.js,当然还可以创建更多,这里就使用中英两种语言举例
3. 编辑三个文件内容
language/index.js
import { createI18n } from 'vue-i18n' //引入 vue-i18n 组件
//注册i8n实例并引入语言文件
const i18n = createI18n({
locale: 'ch', //默认显示的语言
messages: {
ch:require('./ch.js'), //引入语言文件
en:require('./en.js')
}
})
export default i18n; //将i18n暴露出去,在main.js中引入挂载
language/cn.js
export const lang = {
welcomeTitle: '欢迎来到基于Vue.js和webGL的三维场景开发网!',
loginModal: {
modalTitle: '请登录',
loginTitle: '请输入您的账号和密码!',
account: '账号',
inputAccountTitle: '请输入账号',
accountErrorTitle: '该账号不存在!',
passWord: '密码',
inputPassWordTitle: '请输入密码',
passWordErrorTitle: '密码错误!',
loginSuccess: '登陆成功!',
registerLink: '没有账号?注册一个',
login: '登录',
cancel: '取消',
unknownexception: '未知异常'
}
}
language/en.js
export const lang = {
welcomeTitle: 'Welcome to the 3D scene development network based on Vue.js and webGL!',
loginModal: {
modalTitle: 'Please Sign In',
loginTitle: 'Please enter your account and password!',
account: 'Account',
inputAccountTitle: 'Please input your account',
accountErrorTitle: 'The account does not exist!',
passWord: 'Password',
inputPassWordTitle: 'Please input your password',
passWordErrorTitle: 'Wrong password!',
loginSuccess: 'login successful!',
registerLink: 'Don\'t have an account? Register one',
login: 'Sign In',
cancel: 'Cancel',
unknownexception: 'Unknown exception'
}
}
4. 在 main.js 中全局挂载
import { createApp } from 'vue'
import App from './App.vue'
import VueI18n from './language'
const app = createApp(App) // 创建 app 实例
app
.use(VueI18n)
.mount('#app')
5. 在 vue 文件中使用
<div class="title">{{$t('lang.welcomeTitle')}}</div>
import { getCurrentInstance } from 'vue'
import { useI18n } from 'vue-i18n'
import { message } from 'ant-design-vue'
export default {
setup () {
const { proxy } = getCurrentInstance() // 获取全局对象
const { t } = useI18n()
const aaa = () => {
// 可通过全局对象找到 $t
message.success(proxy.$t('lang.welcomeTitle'))
// 也可直接使用 useI18n 的方式
message.success(t('lang.welcomeTitle'))
}
}
}
6. 切换语言
import { useI18n } from 'vue-i18n'
export default {
setup () {
const { locale } = useI18n()
const changeLaguage = (value) => {
locale.value = value // value='cn'||'en'
}
}
}
Vue3: 自定义指令 + ResizeObserver 实现对元素的宽高以及显隐监听
ResizeObserver API 介绍
ResizeObserver 接口避免了自身回调中调整大小从而触发的无限回调和循环依赖,相对于 window 的 resize 事件,这个 API 在开发过程中可以极大的提高性能,并且 window 的 resize 事件只能监听浏览器的窗口大小变化,不能直接监听具体的某个元素(至少我没找到它的实现方法),所以ResizeObserver 就发挥它的作用了。
ResizeObserver 可以帮助我们:监听一个Dom节点的变化,这种变化包括但不仅限于:
1. 某个节点的出现和隐藏
2. 某个节点的大小变化
ResizeObserver API 是一个新的 JavaScript API,与 IntersectionObserver API 非常相似,它们都允许我们去监听某个元素的变化。
实际上,ResizeObserver API 使用了观察者模式,也就是我们常说的发布-订阅模式。发布-订阅模式是 JavaScript 中典型的设计模式,在很多地方都有使用到。如 Nodejs 的 Event 模块,Vue 的父子组件通信等等。
ResizeObserver API 用法
使用 ResizeObserver API 非常简单。ResizeObserver 是个构造函数。在使用 new 关键字调用构造函数,返回实例对象时,需要传入一个回调函数,这个回调用于监听实例对象某个 DOM 节点的变化。如:
const myObserver = new ResizeObserver(entries => {
entries.forEach(entry => {
console.log('大小位置', entry.contentRect)
console.log('监听的DOM', entry.target)
})
})
myObserver.observe(document.body)
以上,调用实例对象的 observe 方法,监听整个 body 节点的变化,当改变窗口大小或者某个 DOM 节点出现或隐藏时时,就会触发回调。
如果想在监听多个 DOM 节点的变化,直接在一个实例对象上调用多次 observe 方法就好了。如:
const myObserver = new ResizeObserver(entries => {
// 注意,entres是个数组,数组项为每个需要监听的DOM节点
entries.forEach(entry => {
console.log('大小位置 contentRect', entry.contentRect)
console.log('监听的DOM target', entry.target)
})
})
myObserver.observe(document.body)
myObserver.observe(document.querySelector('#app'))
实例对象 myObserver 方法除了有 observe 方法之外,还有 disconnect 方法和 unobserve 方法。
unobserve 方法,顾名思义了,就是取消监听某个DOM节点。
disconnect 方法,取消对所有节点的监听。
在使用 API 的时候,有些情况下可能会出现一秒钟调用很多次回调函数,如果想要进一步优化性能,加上节流函数即可。
Vue3 自定义指令介绍
额,这个。。。这不需要我介绍吧,这个很基础的啊,这个只有菜鸡才不会的好不啦😎。为了摆脱菜鸡这个称号,俺在写这篇文章的前半个小时一不小心学会了这个技能。。。话不多说,上链接:
Vue官网:自定义指令的基本使用
我用八千匹垂垂老矣、将死的老牛把话题拉回来,用文人雅士的话来说就是言归正传,回到我们结合 自定义指令 + ResizeObserver API 实监听某个元素的大小变化上面:
首先?其次?最后???哪有那么多过场,直接上代码:
<template>
<div class="hello" v-resize @click="e => chengeElWidth(e)">
{{title}}
</div>
</template>
<script>
import { reactive, toRefs, onMounted } from 'vue'
export default {
components: {},
// 自定义局部指令
directives: {
resize: {
// 在绑定元素的父组件被挂载后调用
mounted (el, binding) {
let resizeObserver = new ResizeObserver(() => {
binding.instance.resizeFun(el)
})
// 开始观察指定的 Element
resizeObserver.observe(el)
}
}
},
setup () {
const state = reactive({
title: '我是崔堂袁',
boxWidth: 100
})
onMounted(() => { })
const chengeElWidth = (e) => {
e.target.style.width = `${state.boxWidth}px`
state.boxWidth += 50
}
const resizeFun = (el) => {
console.log(el.clientWidth)
}
return {
...toRefs(state),
chengeElWidth,
resizeFun
}
}
}
</script>
搞定!!!