关于javascript:50天用vue3完成了50个web项目我学到了什么

7次阅读

共计 23531 个字符,预计需要花费 59 分钟才能阅读完成。

通过本文的 50 个 web 示例你将学到:

  • Vue3 外围根底语法和进阶语法
  • less 外围根底语法和进阶语法
  • scss 外围根底语法和进阶语法

1.Expanding Cards

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

Javascript

  • Vue ref 办法定义根本响应式变量。如:
const currentIndex = ref(0);

ref 办法当然也能够定义一个对象,然而通常定义对象应该应用 reactive 办法。

  • Vue v-for 指令渲染列表。如:
v-for="(item,index) in imageItems" 

v-for 指令渲染时最好指定 key 属性,不便虚构 DOM Diff 算法比对。

  • Vue 动静绑定 class 与 style。如:
:class="index === currentIndex ?'active':''"
:style="{backgroundImage:`url(${ imageURL + item})`}"

:class:style 代表设置动静类名和动静行内款式,其中 :v-bind的简写,值能够是一个 javascript 表达式,如三元表达式,或者是对象,又或者是数组。

  • Vue 事件绑定。如:
@click="currentIndex = index"

vue 事件能够简写为 @ + 事件名,值为一个 javascript 表达式或者是一个函数。

less

  • 定义公共款式
.base-flex {
    display: flex;
    justify-content: center;
    align-items: center;
}
// 应用形式
.app {.base-flex;}
  • 嵌套语法
.app {
    //... 这里写外围款式
    .ec-panel {//... 这里写外围款式}
}

less 定义变量是 @ + 变量名,如:

@width: 100px;
  • &

该符号示意对父选择器的一个援用,例如:

.ec-panel {
    //... 外围款式
    &.active {//... 外围款式}
}

其中 &.active 其实就是.ec-panel.active 的写法。

2. Progress Steps

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

Javascript

通过 defineProps 定义 props,如:

const props = defineProps({
    width:{
        type:Number,
        default:350
    },
    progressWidth:{
        type:Number,
        default:0
    }
})

通过 computed 办法定义计算属性:

const styleWidth = computed(() => props.width+'px');

仍然是通过 slot 标签定义插槽:

<div class="ps-step-container">
    <div class="ps-step-progress"></div>
    <slot></slot>
</div>

通过 defineEmits 办法定义事件传递:

  • defineEmits 注册事件名,办法通常是一个数组,传递多个事件名
  • 调用办法的返回值,而后将该事件向上传递
const emit = defineEmits(["on-click"]);
const changeActive = () => {emit("on-click");
}

通过 reactive 定义响应式对象,它与 ref 办法的区别就是,通常咱们应用 ref 办法来定义根本数据类型,而 reactive 办法则是定义多个对象:

const bool = ref(false);
const state = reactive({
    status: false,
    list:[
        {
            label:"测试值",
            value:1
        }
    ]
})

浏览器控制台花式玩法:

console.log("%c" + consoleText,"background:#0ca6dc;padding:4px 10px;border-radius:3px;color:#fff");

less

动静绑定款式变量的写法有两种,第一种则是字符串属性名,第二种则是间接写变量名:

width:v-bind("styleWidth");
width:v-bind(styleProgressWidth);

::focus-visible 选择器,示意键盘伪类焦点选择器,在标准中定义为: 元素聚焦,同时浏览器认为聚焦轮廓应该显示。

有时候咱们心愿键盘触发聚焦轮廓,而鼠标关注焦点则不触发轮廓,此时用以下一行 CSS 代码搞定。

:focus:not(:focus-visible) {outline: 0;}

属性选择器语法:

[属性选择器] {//CSS 款式}
// 如:
[disabled] {
    background-color: @color;
    color: @font_color;
    cursor: not-allowed;
}

其它知识点同后面示例。

3. Rotating Navigation Animation

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

Javascript

v-if 与 v -else 指令,示意条件,v-html 示意渲染 html:

<div class="rna-page-content">
    <slot>
        <template v-if="props.isRenderHTML">
            <p v-html="content"></p>
        </template>
        <template v-else>{{props.content}}</template>
    </slot>
</div>

typescript

typescript 定义接口通过 interface 关键字,就像定义对象一样,其后跟属性名: 属性类型,如:

定义根本类型就是: 类型值,如:

const URL: string = "";
// 代表 URL 是一个字符串类型
interface NavItemType {
    url:string;
    text:string;
    icon:string;
}

typescript 泛型,如:

Array<NavItemType>

就代表是一个泛型,也就是数组类型,并且数组项的值应该是类型 NavItemType,所以数据格式就应该相似如下:

export const navList = [
  {
    url: "http://www.eveningwater.com/",
    text: "集体网页",
    icon:"Website"
  },
  {
    url: "https://www.eveningwater.com/my-web-projects/",
    text: "我的我的项目",
    icon:"project"
  },
  {
    url: "http://github.com/eveningwater",
    text: "github",
    icon:"github"
  },
];

再如:

Array<string> // 示意定义一个字符串数组

导出多个模块语法:

export {default as Content} from "./Content.vue";
export {default as Menu} from "./LeftMenu.vue";
export {default as NavList} from "./NavList.vue";

示意将三个组件的默认模块更名为对应的名字。

less

:deep 深度选择器,相似于 vue2 的 >>> 和 /deep/, 通常用于影响子组件的款式,也就是因为加了 scoped 组件只作用在以后组件,而无奈作用到子组件,也就须要应用该选择器:

:deep(p) {
    text-indent: 2em;
    color:@content_font_color;
    line-height: 2;
    margin-bottom: 15px;
    letter-spacing: 1px;
}

动静款式拜访数组变量,能够依据索引来拜访,就像拜访数组元素一样:

background-image: v-bind("beforeResourceURL[0]");

css 同级元素选择器,如:

& + .rna-nav-list ul {transform: translateX(15px);
    & .rna-nav-item {transform: translateX(0);
       transition-delay: .4s;
    }
}

示意选中以后选择器的兄弟选择器.rna-nav-list。

其它知识点同后面示例。

4. hidden-search-widget

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

Javascript

给元素或者是组件绑定 ref,能够拜访组件或者元素的理论 DOM 元素,例如:

const inputRef = ref(null);

watch 办法,承受三个参数,第一个参数示意监听的数据,能够是一个函数,也能够是一个变量,或者也能够是一个监听的字符串属性名,第二个参数则是一个回调函数,能够获取到监听的值,第三个参数示意监听的配置对象,如深度监听 deep 属性。如:

watch(isActive,val => {
    const inputElement = inputRef.value;
    if(val && inputElement){(inputElement as HTMLInputElement).focus();}
});

typescript

as 关键字能够将任意变量断言成任意类型,通常如果转换类型不对能够先强制转换成 unknown,而后再转换成对应的类型。例如:

// 假如 inputElement 不能被断言成 HTMLInputElement 类型,就须要先转换成 unknown
inputElement as unknown as HTMLInputElement

或者也能够间接断言成 any。如:

inputElement as any 

尽管这样做能够回避 ts 的类型查看,但如无必要,尽量少应用 as 关键字断言变量的类型。

模板绑定:

<input type="text" class="hsw-input" ref="inputRef" :placeholder="props.placeHolder" />

其它知识点同后面示例。

5. blurry loading

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

Javascript

一个工具函数,示意转换数字范畴的工具函数,感觉很罕用,能够记下来:

// https://stackoverflow.com/questions/10756313/javascript-jquery-map-a-range-of-numbers-to-another-range-of-numbers
export const scale = (n:number,inMin:number,inMax:number,outerMin:number,outerMax:number) => (n - inMin) * (outerMax - outerMin) / (inMax - inMin) + outerMin;

typescript

Ref 类型也是一个泛型,当然这里其实应该如此来定义 setTimeout 的返回值,而不应该应用 any:

const timer:Ref<any> = ref(null);

应该批改成:

const timer:Ref<ReturnType<typeof setTimeout>> = ref(null);

typeof setTimeout 示意获取 setTimeout 函数的类型,而 ReturnType 是 typescript 的内置类型,示意返回值的类型,联合起来就代表是 setTimeout 函数的返回值。

onMounted 和 onUnmounted 是 vue 生命周期的钩子函数,别离代表组件挂载后和组件卸载后,承受一个回调函数作为参数,而后在回调函数当中执行相应的操作。

onMounted(() => {// 这里写逻辑});
onUnmounted(() => {// 这里写逻辑})

less

获取对象值的变量作为款式,能够应用字符串和点语法获取,如:

opacity: v-bind("props.number");

其它知识点同后面示例。

6. scroll animation

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

javascript

函数节流,就是利用提早定时器,联合工夫戳来调用给定的函数。在这个函数外面也波及到了 typescript 泛型,任意类型,函数类型,并且也波及到了 apply 办法的应用,apply 传入一个 this 对象,后续的参数则是一个数组。

export function throttle(fn:(...args:any []) => any,delay:number){
    let timer:ReturnType<typeof setTimeout> | null = null,last:number = 0;
    return function<T>(this:(...args:any []) => any,...args:Array<T>) {let now = +new Date();
        if(last && now < last + delay){clearTimeout(timer);
            timer = setTimeout(() => {
                last = now;
                fn.apply(this,args);
            },delay);
        }else{
            last = now;
            fn.apply(this,args);
        }
    }
}

typescript

PropType 类型,是一个泛型,用于定义 prop 的类型。如:

props:{
    title:{type:String as PropType<string>},
    content:{type:String as PropType<string>}
},

setup 函数为 vue3 的入口,也是一个生命周期钩子函数,相当于 vue2 的 beforeCreate 和 created 的合并。第一个参数能够获取到 props,第二个参数是以后上下文,能够通过对象构造的形式获取 emit,slots 等。如:

setup(props,{ slots}){const renderTitle = () => slots.title ? slots.title() : props.title;
    const renderBoxContent = () => slots.default ? slots.default() : props.content;
    return () => (
        <div class="scroll-ani-box">
            <Title level="3" class="scroll-ani-box-title">{renderTitle() }</Title>
            {renderBoxContent() }
        </div>
    )
}

defineComponent 示意定义一个组件,也是 Vue3 提供的一个定义组件的办法,该办法接管一个对象作为参数,在这个对象外面有点像 vue2 的 new Vue 的参数,写 vue 组件的配置参数,例如 props,methods 等。

在这里封装了一个 Title.tsx,也就是将 h1 ~ h6 封装成一个组件,也叫题目组件。代码如下:

import {defineComponent, PropType} from "@vue/runtime-core";

export const TitleNumberCollection = [1,2,3,4,5,6];

export default defineComponent({
    props:{
        level:{type:[Number,String] as PropType<number|string>,
            default:1,
            // 在这里查看 level 的类型
            validator:(value:string | number) => {return TitleNumberCollection.indexOf(Number(value)) > -1;
            }
        },
        content:{type:String as PropType<string>}
    },
    setup(props,{ slots}){const { level,content,...rest} = props;
        const TitleName = "h" + level;
        const renderChildren = () => slots.default ? slots.default() : props.content;
        return () => (<TitleName {...rest}>
                {renderChildren() }
            </TitleName>
        )
    }
})

这个组件的实现思路就是定义一个 level 属性和一个 content 属性,前者代表应用的是 h1 ~ h6 之中的元素,这也是为什么 TitleNumberCollection 数组是 [1,2,3,4,5,6] 的起因。

这里为了谨严,对 level 做了验证,只容许传入 1 ~ 6 的值,也能够是字符串和数字。须要留神的就是 validator 是一个函数,返回的是一个布尔值,并且函数的参数必须要指定类型,否则会呈现意料之外的谬误,导致程序无奈执行。

如果组件没有写默认的插槽内容,就会应用 content 属性字符串作为承载的内容。应用这个组件的形式很简略,如下:

<Title level="3"> 这是 h3 元素的内容 </Title>
<Title :level="4"> 这是 h4 元素的内容 </Title>

PS:这个组件在前面的示例中简直都有用到。

能够在 onMounted 钩子函数中监听页面滚动事件,如:

onMounted(() => {
    triggerBottom.value = window.innerHeight * 4 / 5;
    window.addEventListener("scroll",throttle(onScrollHandler,20))
});

UnwrapNestedRefs 类型也是 Vue3 定义的类型,是一个泛型,能够定义 reactive 办法的返回值类型,如:

interface BoxType {
  active:string;
  content:string;
}
const state:UnwrapNestedRefs<{boxList:Array<BoxType>}> = reactive({boxList:[]
});

v-slot 指令,其后跟指定的插槽名,也就是具名插槽。

less

定义 Mixin 实际上就是写 CSS 款式。如下:

.flex-center {
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
}

应用形式就是把它当成函数调用,如:

// 应用形式
.app {.flex-center();
}

svg-gradient 函数,是 less 的外围函数,生成一个 svg 渐变色,它至多有三个参数,第一个参数指定突变类型和方向,其余参数列出色彩和地位,第一个和最初一个指定色彩的地位是可选的,其余色彩必须指定地位。该函数最终会被渲染成 svg 图片,如下图所示:

percentage 函数,承受一个浮点值作为参数,如:

percentage(.5); // 渲染成 50%

less 当中的 map 对象,格局如下:

.(map 名字) {属性名: 属性值(能够是变量);
}

例如:

@boxBgColor-1:#f1bd81;
@boxBgColor-2:#e07e0e;
.boxColors() {
    lightColor:@boxBgColor-1;
    darkColor:@boxBgColor-2;
}

应用的时候,就像读取 javascript 对象那样应用中括号语法:

.test {color:.boxColors[lightColor];
}

min 函数,顾名思义,求最小值,如:

min(8px,9px,10px) // => 返回 8px

range 函数,求范畴值, 如:

range(10px, 30px, 10); // => 10px 20px 30px
padding:range(10px, 30px, 10); // => padding: 10px 20px 30px;

fade 函数,对色彩进行透明度的相加,例如:

fade(#fff,40%); // => rgba(255,255,255,.4);

相似的还有 fadeout 以及 fadein 函数。

extract 函数,有 2 个参数,第一个参数是一个列表,第二个参数是一个索引值,也就是说该函数示意从列表当中取出列表项,如:

@textTransform:uppercase,capitalize,none,lowercase,inherit;
text-transform: extract(@textTransform,1);

escape 示意能够将任意的字符串转成属性或者值,格局就是~ + 字符串值,例如:

@min768: ~"(min-width: 768px)";
.element {
  @media @min768 {font-size: 1.2rem;}
}

将会渲染成:

@media (min-width: 768px) {
  .element {font-size: 1.2rem;}
}

这个示例用到了很多 less 外围语法,对 less 的用法也就更深一步。

其它知识点同后面示例。

7. split panel

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

javascript

知识点同后面示例。

less

unit 函数,也就是增加单位的函数,如:

unit(100,vh) // =>100vh

covert 函数,转换单位,如:

covert(1s,'ms'); // => 1000ms

其它知识点同后面示例。

8. form wave

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

javascript

defineAsyncComponent 函数,传入一个回调函数作为参数,回调函数的返回值必须是 import(‘ 组件门路 ’), 示意异步导入组件。如:

const AsyncFormComponent = defineAsyncComponent(() => {return import("./components/Form.vue");
});

less

pi 函数,示意获取数学上的 π 值。即 3.14…..

  • ceil 函数(四舍五入),
  • round 函数(向上取整),
  • sqrt 函数(求绝对值),
  • floor 函数(向下取整),
  • length 函数(获取列表的长度),
  • mod 函数(求余)。

其它知识点同后面示例。

9. sound board

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

javascript

ref 函数不仅能够用来定义数据,也能够用来存储一个 dom 元素的援用。如:

const audioRef = ref<HTMLAudioHTMLElement | null>(null);

vue template 模板代码如下:

<audio ref={audioRef}></audio>

less

css 选择器前缀名,能够通过定义变量,而后.@{变量名}的形式来增加 css 选择器前缀。如:

@prefix:sb-;
.@{prefix}button-container {// 款式}
// => 渲染成
// .sb-button-container {
//     // 款式
// }

sin 函数,求正弦的函数。

其它知识点同后面示例。

10. dad jokes

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

Javascript

Fragment 元素,也就是一个占位标签,理论不会渲染该元素,相似于 vue 的 template 元素。

window.open 函数关上一个窗口,在关上之后,通过设置 opener = null,能够防止 window.open 带来的平安问题。

如:

const win = window.open("");
win && (win.opener = null);

less

color 函数,用于将属性转换成色彩值字符串,如:

color(#eb6b44); // => '#eb6b44';

max 函数,与 min 函数相同,取最大值,如:

max(600,700,800) // => 800

pow 函数,幂函数,等同于 javascript 的 Math.pow 函数,传入 2 个参数,第一个参数为数,第二个参数为幂数。如:

pow(2,2); // => 4

:focus-visible 选择器,这个是关注焦点选择器。

其它知识点同后面示例。

11. Event keyCodes

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

javascript

useCssModule 办法,顾名思义,就是款式模块化,咱们能够给 vue 的单文件组件的 style 标签加上 module 属性,而后在 setup 函数当中就能够通过该办法来拜访 style。例如:

<style module lang="less">
.test {// 外围款式}
</style>
<script>
    //setup 函数中
    const style = useCssModule();
</script>
<template>
    <div :class="style.test"></div>
</template>

该办法还能够接一个具名参数,也就是代表获取的 style 模块名,比方:

<style module="name" lang="less">
.test {// 外围款式}
</style>
<script>
    //setup 函数中
    const style = useCssModule('name');
</script>
<template>
    <div :class="style.test"></div>
</template>

typescript

KeyboardEvent 类型,也就是键盘事件对象的类型。

less

less 定义 mixin(混合)与 sass 还是有很大的区别的,比方定义一个一般的 mixin 就是写一个 css 选择器,而后再写款式,如:

.test {// 款式代码}

而后咱们通过调用函数的形式来应用这个 mixin,即:

.common {.test();
}

如果不心愿 mixin 被输入编译在 css 当中,能够给 mixin 退出括号,即:

.test() {// 外围款式}

应用形式依然是通过调用函数的形式,同上。

mixin 同样有命名空间的概念,如:

#test(){
    .test {// 这里写款式}
}

这里的 #test 实际上也就是命名空间,当然咱们也能够应用.test 来代替#test,也是能够代表是一个命名空间名称的。

命名空间也是受爱护的,咱们能够应用 when 关键字,联合判断条件来让这个命名空间受爱护,如:

.mt-(@num) when (default()) {margin-top:unit(@num,px);
}

意思就是当满足默认条件的时候,咱们的.mt 类名,其后跟数字前缀就会渲染出 margin-top 多少 px 的款式。例如:

.mt-(10); // => 渲染成 margin-top:10px;

其它知识点同后面示例。

12. faq-collapse

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

javascript

readonly 办法,能够让一个通过 ref 或者是 reactive 定义的响应式数据变成只读。如:

const state = readonly(reactive({
    faqList: [
        {
            title:"Why shouldn't we trust atoms?",
            text:"They make up everything."
        },
        {
            title:"What do you call someone with no body and no nose?",
            text:"Nobody knows."
        },
        {
            title:"What's the object-oriented way to become wealthy?",
            text:"Inheritance."
        },
        {
            title:"How many tickles does it take to tickle an octopus?",
            text:"Ten-tickles!"
        },
        {
            title:"What is: 1 + 1?",
            text:"Depends on who are you asking."
        }
    ]
}));

此时的 faqList 就是只读的,无奈被批改。

less

luma 函数,计算一个色彩对象的亮度。如:

luma(rgb(100, 200, 30)) // => 44%

其它知识点同后面示例。

13. random-choice-picker

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

javascript

通过 withDefaults 能够将 defineProps 包裹住,不便定义类型,并且提供 props 的默认值,如:

interface PropsType {placeholder:string;}
const props = withDefaults(defineProps<PropsType>(),{placeholder:""});

计算属性的 getter 函数与 setter 函数。如:

type ListType = Array<{text?:string,isActive?:boolean}>;
const computedChoices = computed<ListType>({get(){return choices.value;},
   set(newValue){if(newValue !== choices.value){choices.value = newValue;}
   }
});

setTimeout 提早函数的应用。

而后就是这个函数略微难以了解一些:

const changeChoices = (v:string) => v.split(choice.value.indexOf(",") > -1 ? "," : "").filter(v => v.trim()).map(v => ({ text:v,isActive:false}));

实际上也很好了解,将每个函数拆分一下,就能了解了。

其它知识点同后面示例。

14. Animated Navigation

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

javascript

知识点同后面示例。

scss

scss 定义变量是 $ + 变量名,而 less 则是 @ + 变量名,scss 是 sass 的下一版本,sass 的语法有些相似于 stylus,去掉了括号,然而在 scss 当中却保留了括号。scss 中定义 mixin 通过 @mixin。如:

@mixin common-bg {background: linear-gradient(135deg,$bgColor-1 10%,$bgColor-2 90%);
}

应用 mixin 就是通过 @include 关键字加 mixin 的名字。如:

.test {@include common-bg;}

同样的 scss 中在选择器中应用前缀应该是 #{变量名}的形式。如:

$baseSelector:an-;
.#{$baseSelector}nav {// 外围款式}

scss 中能够应用 for 循环,构造为:for 变量名 from 起始值 through 完结值。如:

@for $i from 0 through 100 {.mt-#{$i} {margin-top:$i + px;}
}

opacify 函数,传入 2 个参数,第一个参数为色彩值,第二个参数为一个浮点数,示意相加的透明度,这个函数也就是为色彩值增加透明度。如:

opacify(rgba(#036, 0.7), 0.3) // => #036
opacify(rgba(#6b717f, 0.5), 0.2); // rgba(107, 113, 127, 0.7)

transparentize 函数,传入两个参数,同 opacify 函数统一,作用同 opacify 函数相同。如:

transparentize(rgba(#6b717f, 0.5), 0.2)  // rgba(107, 113, 127, 0.3)

opacify 与 transparentize 函数就相似于相似于 less 的 fadein 和 fadeout 函数

其它知识点同后面示例。

15. incrementing-counter

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

javascript

toRefs 函数,当咱们在 vue 中应用解构获取 props 时,props 会失去响应式,此时咱们就须要应用 toRefs 将 props 包裹一下,让 props 不失去响应式,这在 Counter.vue 文件当中是正文了的。

scss

知识点后面示例讲过。

其它知识点同后面示例。

16. Drink Water

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

javascript

javascript 没什么好说的,倒是 typescript 有 2 个类型须要解释一下。

StyleValue 类型,顾名思义,就是款式值的类型,是 typescript 内置类型。

keyof 关键字,前面跟一个类型,示意一个类型属于前面类型的一个子类型。如:

interface DataType {
    name: string;
    age: number;
}
const a: keyof DataType = 1;

scss

继承的语法,首先写一个公共的款式,而后通过 @extend 关键字应用,并且能够写多个继承,通过 , 分隔。如:

.test-1 {// 外围款式}
.test-2 {// 外围款式}
.common {@extend .test-1,.test-2;}

其它知识点同后面示例。

17. Movie App

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

javascript

javascript 也没有什么新知识点,次要是 typescript 新增了 3 个类型,FocusEvent,HTMLInputElement 和 Partial 类型

FocusEvent 就是关注焦点事件对象类型,HTMLInputElement 为 input 元素的类型,Partial 为 typescript 内置类型,将所有类型变成可选的,也是一个泛型。

scss

percentage 函数,原理同 less 的 percentage 没什么好说的。

mixin 同样也能够传参数,并且在 @include 的时候传入即可。

相似 border 这样的,还能够写成嵌套语法。如:

border: {
    width:1px;
    style:solid;
    color:ffefefe;
}
// => 渲染 border-width: 1px;border-style:solid;border-color:#fefefe;

其它知识点同后面示例。

18. background slider

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

javascript

nextTick 函数,传入一个回调函数作为参数,该函数为期待下一次 DOM 更新刷新的工具办法,返回值是一个 promise。如示例:

<script setup>
import {ref, nextTick} from 'vue'

const count = ref(0)

async function increment() {
  count.value++

  // DOM 还未更新
  console.log(document.getElementById('counter').textContent) // 0

  await nextTick()
  // DOM 此时曾经更新
  console.log(document.getElementById('counter').textContent) // 1
}
</script>

<template>
  <button id="counter" @click="increment">{{count}}</button>
</template>

scss

clamp 函数,传入 3 个参数,返回第二个参数的值,第一个参数为最小值,第 3 个参数为最大值。语法:

clamp($min,$number,$max) // => $number

三个参数都必须有单位或者无单位,如果 $number 小于 $min 的值,则返回 $min, 如果 $number 大于 $max 的值,则返回 $max。

map-get 函数,获取 map 类型的值,来看一个示例:

$font-weights: ("regular": 400, "medium": 500, "bold": 700);
map-get($font-weights, "medium"); // 500
map-get($font-weights, "extra-bold"); // null

其它知识点同后面示例。

19. theme clock

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

javascript

知识点同后面示例。

可选链操作符? 的应用。

typescript

Readonly 类型,示意数据类型只读。

CSSProperties 类型,就是 css 属性名的类型。

scss

@if…@else… 判断语句。@function 定义函数,@return 返回函数值。如:

@function setMargin($i,$j,$unit){
    $result:0;
    @for $_ from $i through $j {
      @if $unit {$result:$_ + $unit;} @else {$result:$_;}
    }
    @return $result;
}

这个函数示意设置间距,传入开始值,完结值以及单位。

max 函数,同 less 的 max 函数一样。如:

$maxWidth:250px,300px,350px;
max-width: max($maxWidth...); // => 350px

其它知识点同后面示例。

20. button-ripple-effect

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

javascript

没什么知识点好说的,有个 MouseEvent 示意鼠标事件对象的类型,以及 HTMLButtonElement 示意 button 元素的类型。

scss

能够在 scss 当中引入命名空间,而后应用 sass 内置的函数。如:

@use "sass:math";
// 能够应用 math 函数
// 如 math.atan2,math.pow

其它知识点同后面示例。

21. drawing-app

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

javascript

动静组件,component,传入一个 is 关键字,is 关键字作为组件名。如:

<component is="div" ref="container"></component> // => 渲染一个 div 元素

本示例中封装了一个基于我的 js 实现的色彩选择器色彩选择器组件,代码如下:

<script setup lang="ts">
    import {nextTick, onMounted, PropType} from '@vue/runtime-core';
    import {ref} from '@vue/reactivity';
    import ewColorPicker from 'ew-color-picker';
    import "ew-color-picker/dist/ew-color-picker.min.css";
    const props = defineProps({
        name:{
            type:String as PropType<string>,
            default:"div"
        },
        option:{
            type:Object as PropType<object>,
            default:() => ({})
        }
    });
    const container = ref(null);
    onMounted(() => {nextTick(() => {new ewColorPicker({ el:container.value,...props.option}) as any;
        })
    })
</script>
<template>
    <component :is="props.name" ref="container"></component>
</template>

自定义组件 v -model 指令的实现,首先是定义一个叫 modelValue 的 props,接着在组件外部提交自定义事件 emit(‘update:modelValue’)即可,当然也能够批改名字,不过应用形式就要相似:v-model:[属性名]。例如:

const props = defineProps({modelValue:[String,Number] as PropType<string | number>
});
const emit = defineEmits(["update:modelValue"]);

// 模板元素
// <input @input="onInputHandler" :value="props.modelValue" />

scss

list.nth 能够获取列表中对应渲染的值,传入 2 个参数,第一个是列表,第二个则是列表索引值。如:

// 导入 list 命名空间
@use "sass:list";
// 定义列表
$display:block,flex,inline-block,inline,inline-flex,none;
.el-block {display: list.nth($display,1); //=> 渲染 block
}

以上代码定义一个类名为 el-block,渲染 display:block 的款式。

其它知识点同后面示例。

22. drag-n-drop

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

javascript

拖拽事件的应用。

scss

@each…in 循环,同 @for 循环相似, 如:

$font-weight:normal,lighter,bold,bolder,100,200,300,400,500,600,700,800,900;
@each $value in $font-weight {.fw-#{$value} {font-weight: $value;}
}

以下这个函数也有点意思:

@use "sass:string";
/*
* 截取属性字符
*/
@function propSlice($prop, $start, $end) {@return string.unquote(string.slice(string.quote($prop), $start, $end));
}

其它知识点同后面示例。

23. content-placeholder

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

这个示例展现了如何封装一个骨架屏卡片组件,其中波及到的知识点后面总结过,所以这里不再总结。

24. kinetic-loader

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

知识点同后面示例。

25. sticky-navbar

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

一个 SVG Icon 组件的写法:

<template>
    <svg 
        viewBox="0 0 1024 1024" 
        version="1.1" 
        xmlns="http://www.w3.org/2000/svg" 
        :width="width"
        :height="height"
    >
        <template v-if="Array.isArray(iconComponent)">
            <path 
                v-for="(item,index) in iconComponent" 
                :key="item.prop + index" 
                :fill="item.color ? item.color : color" 
                :d="item.prop"
            ></path>
        </template>
        <template v-else-if="isString(iconComponent)">
             <path :fill="color" :d="iconComponent"></path>
        </template>
    </svg>
</template>
<script lang="ts" setup>
    import {iconPathData} from "@/utils/iconPathData";
    import type {IconType} from "@/utils/iconPathData";
    import {isString} from "@/utils/utils";
    import {computed} from "vue";
    const props = defineProps({
        width:{
            type:Number,
            default:30
        },
        height:{
            type:Number,
            default:30
        },
        type:String,
        color:{
            type:String,
            default:"#fff"
        }
    });
    const iconComponent = computed(() => iconPathData[props.type as keyof IconType]);
</script>

仿佛这样封装不太好。

transition 组件,用于增加过渡成果的组件。

Promise.allSettled 这个能够参考文档。

其它知识点同后面示例。

26. double-slider

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

知识点同后面示例。

27. toast-notification

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

typescript

这个函数有点意思:

interface ConsoleType {
    black: string;
    red: string;
    green: string;
    yellow: string;
    blue: string;
    magenta: string;
    cyan: string;
    white: string;
    bgBlack:string;
    bgRed: string;
    bgGreen:string;
    bgYellow:string;
    bgBlue: string;
    bgMagenta:string;
    bgCyan: string;
    bgWhite:string;
}
export function colorize<T>(...args:Array<T>){
    return {black: `\x1b[30m${args.join(' ')}`,
        red: `\x1b[31m${args.join(' ')}`,
        green: `\x1b[32m${args.join(' ')}`,
        yellow: `\x1b[33m${args.join(' ')}`,
        blue: `\x1b[34m${args.join(' ')}`,
        magenta: `\x1b[35m${args.join(' ')}`,
        cyan: `\x1b[36m${args.join(' ')}`,
        white: `\x1b[37m${args.join(' ')}`,
        bgBlack: `\x1b[40m${args.join(' ')}\x1b[0m`,
        bgRed: `\x1b[41m${args.join(' ')}\x1b[0m`,
        bgGreen: `\x1b[42m${args.join(' ')}\x1b[0m`,
        bgYellow: `\x1b[43m${args.join(' ')}\x1b[0m`,
        bgBlue: `\x1b[44m${args.join(' ')}\x1b[0m`,
        bgMagenta: `\x1b[45m${args.join(' ')}\x1b[0m`,
        bgCyan: `\x1b[46m${args.join(' ')}\x1b[0m`,
        bgWhite: `\x1b[47m${args.join(' ')}\x1b[0m`};
}

能够参考这里具体理解这个函数。

接口的继承,如:

export function consoleByColorKey<T,U extends keyof ConsoleType>(str:string,key="black"){return console.log(colorize(str)[key as U]);
}

createVNode 办法,vue 外部办法,提供一个创立 vNode 节点的办法,这也是这个示例的难点,而后是 render 函数。

其它知识点同后面示例。

28. github-profiles

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

知识点同后面示例。

29. double-click-heart

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

知识点同后面示例。

30. auto-text-effect

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

知识点同后面示例。

31. password generator

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

获取自定义属性

const target = e.target as HTMLDivElement;
const lang = target.dataset.lang;

实现复制文本的办法:

const confirm = () => {(window as any).ewConfirm({
        title:data.value.confirmTitle,
        content:data.value.confirmContent,
        showCancel:false
    })
}
// `navigator.clipboard.writeText` not working in wechat browser.
if(navigator.userAgent.toLowerCase().indexOf('micromessenger') === -1){navigator.clipboard.writeText(password.value).then(() => confirm())
}else{const input = document.createElement("input");
    input.value = password.value;
    document.body.appendChild(input);
    input.select();
    document.execCommand("copy");
    input.remove();
    confirm();}

这里用了两个插件。

其它知识点同后面示例。

32. good-cheap-fast

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

知识点同后面示例。

33. Notes App

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

classnames 函数的实现,源码值得细细品读:

export type Value = boolean | string | number | undefined | null | Symbol;
export type Mapping = Record<string,unknown>;
export interface ArgumentArray extends Array<Argument> {}
export type Argument = Mapping | Mapping | ArgumentArray;
const hasOwn = Object.prototype.hasOwnProperty;
export default function classnames(...args:ArgumentArray):string{const classes = [];
    for(let i = 0,len = args.length;i < len;i++){const arg = args[i];
        if(!arg){continue;}
        if(typeof arg === "string" || typeof arg === "number"){classes.push(arg);
        }else if(Array.isArray(arg)){if(arg.length){const __class = classnames.apply(null,arg);
                if(__class){classes.push(__class)
                }
            }
        }else if(typeof arg === "object"){if(arg.toString === Object.prototype.toString){for(let key in arg){if(hasOwn.call(arg,key) && arg[key]){classes.push(key);
                    }
                }
            }else{classes.push(String(arg));
            }
        }
    }
    return classes.join(" ");
}

而后就是这 3 个工具函数,如下:

export function createUUID(){return Math.floor(Math.random() * 10000) + '-' + Date.now().toString().slice(0, 4) + '-' + Math.floor(Math.random() * 10000);
}
export function getNotesData(key:string = "notes"){
    try {return JSON.parse(localStorage.getItem(key) as string);
    } catch (error) {return [];
    }
}
export function setNotesData(key:string,value:Array<any>){return localStorage.setItem(key,JSON.stringify(value));
}

创立一个 uuid 以及存储笔记数据的会话存储办法的一个封装。

其它知识点同后面示例。

34. animated-countdown

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

javascript

defineExpose 办法,用于向父组件传递办法,能够容许父组件通过 ref 来拜访子组件的办法。如:

defineExpose({startRunAnimation});
const startRunAnimation = () => {//...}

InstanceType 类型为 typescript 内置的一个类型,如下:

InstanceType<typeof AsyncNumberGroup> | null> // => 代表返回这个组件 AsyncNumberGroup 的实例类型

其它知识点同后面示例。

35. image-carousel

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

这个示例展现了一个轮播组件的封装,但这个示例仿佛还有点小 bug,源码波及到的知识点在后面的示例当中也可能找到,不用赘述。

36. hover board

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

知识点同后面示例。

37. pokedex

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

创立一个 index.d.ts,能够定义命名空间,而后定义接口返回的类型,如下:

declare namespace 命名空间名 {// 定义接口}

这样做的益处就是我能够间接在组件当中定义该类型,vsCode 会主动帮咱们显示拜访的属性,而后咱们就不必要去打印数据看看数据结构再来决定获得什么属性了,这在理论开发当中是一个很罕用的晋升开发效率的技巧,这也是为什么 typescript 在后续保护当中十分好保护的起因之一。

其它知识点同后面示例。

38. mobile-tab-navigation

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

知识点同后面示例。

39. password-strength-background

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

这个示例次要展现了如何将 tailwindcss 增加到 vue 我的项目当中去,跟着官网提供的教程就能够了。

知识点同后面示例。

40. 3d-background-boxes

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

知识点同后面示例。

41. verify-account-ui

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

知识点同后面示例。

42. live-user-filter

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

这里有个预加载组件和加载组件值得钻研一下,预加载组件的实现思路就是利用 Image 构造函数来提前加载图片,如果图片未申请实现,则显示 loader 组件。对于 loader 组件的实现能够参看源码。

watchEffect 函数,与 watch 函数有所不同,该函数是一个副作用函数,能够间接传入一个回调函数,外部会主动监听数据的变动,当数据扭转后,该函数外部就会执行。

其它知识点同后面示例。

43. feedback-ui-design

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

这个示例值得学习的是应用 css 做了一个翻转成果,其实也就是利用了 rotate 函数。

其它知识点同后面示例。

44. custom-range-slider

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

知识点同后面示例。

45. netflix-mobile-navigation

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

这个示例展现了一个递归组件的实现,其实参考官网示例代码就能够学习到思路。

其它知识点同后面示例。

46. quiz-app

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

知识点同后面示例。

47. testimonial-box-switcher

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

知识点同后面示例。

48. random-image-feed

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

这个示例预览组件的实现还是值得学习的,如下:

<template>
    <Teleport to="body">
        <div class="preview-mask" :class="{show: modelValue}" @click="emit('update:modelValue', false)">
            <async-pre-load-image :src="src" class="preview-mask-image"></async-pre-load-image>
        </div>
    </Teleport>
</template>
<script lang="ts" setup>
import {defineAsyncComponent, PropType, toRefs} from 'vue';
const AsyncPreLoadImage = defineAsyncComponent(() => import('./PreLoadImage.vue'));
const props = defineProps({
    src: String as PropType<string>,
    modelValue: Boolean as PropType<boolean>
});
const emit = defineEmits(['update:modelValue']);
const {src, modelValue} = toRefs(props);
emit('update:modelValue');
</script>

波及到了 Teleport 组件的应用。

其它知识点同后面示例。

49. todo-list

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

本示例实现了一个 Modal 弹框组件,并且实现了一个自定义 clickoutside 指令。

其它知识点同后面示例。

50. insect-catch-game

成果如图所示:

  • 源码
  • 在线示例

学到了什么?

这是一个比拟综合的示例了,简直涵盖了后面所有示例的语法,自己集体也感觉这个示例值得学习,细细品味一番。

看完本文,你对 vue3 以及 scss 和 less 是否有了更深刻的理解呢?当然兴许还有我没有总结到的知识点,更粗疏的就要去通读源码了,我集体是认为每一个示例都波及到了相应的知识点,至多亲手实现了这 50 个示例,我对 vue3 的应用更加纯熟了。

如果感觉本文有任何谬误,欢送批评指正。

正文完
 0