跳到主要内容位置
Hello! 我是张旭乾

致力于帮助你以最直观、最快速的方式学会前端开发,并希望我的个人经历对你有所启发。点击查看最新技术视频教程、实战课程、技术博客、前端资源导航、以及我的想法和生活点滴

“峰华前端工程师”账号创作者/《JavaScript 基础语法详解》作者

最新视频

CSS Grid 布局到底有多强大?除了规整方正的布局外,还能用于不规则的布局。这个视频我们就看一个不规则布局的例子。

假如我们要实现这样的布局:

  • 页面上有 3 张图,其中第 2 张和第 3 张有重叠
  • 上面大标题和第 2 张图有重叠
  • 标题下面的文字和第 1 张图片同属 1 列。
  • 最下面的文字从第 1 张图开始,一直跨越到第 2 张图和第 3 张图重叠的空白部分。

img 那使用 Grid 布局该如何实现呢?其实再不规则的布局都能够使用网格来划分,因为网页设计都是基于网格的。这里我们可以把这个布局分成这样的区域: img 加上 CSS Grid 布局的编号之后,就能清楚的知道网页上的元素分别占据的是那部分区域:

代码实现

那么这样,我们用代码来实现一下。首先看 HTML 结构:

  • main 是页面整体的容器
  • 然后要实现的这部分属于是页面的头部,所以这里我们用一个 header 标签来组织内容
  • 用一个 h1 展示大标题
  • 在下面用 p 元素展示标题下方的描述
  • 之后页面的 3 张图片使用 3 个 img 标签加载出来
  • 图片素材可以在视频简介中的源码里找到
  • 最后的 p 元素是底部的描述信息
<main>
  <header>
    <h1>Give yourself a chance to see the world</h1>
    <p class="description1">
      Lorem ipsum dolor sit amet consectetur. Eget amet lacus egestas amet porta
      sagittis pulvinar magna pretium. Lorem mauris vitae pellentesque platea
      velit volutpat magna sem. Elit eu sit facilisi nullam neque tincidunt sed
      volutpat metus. Amet sed at sed ante senectus sit bibendum tincidunt eu.
    </p>
    <img src="./images/travel1.png" alt="travel" class="image1" />
    <img src="./images/travel2.png" alt="travel" class="image2" />
    <img src="./images/travel3.png" alt="travel" class="image3" />
    <p class="description2">
      Lorem ipsum dolor sit amet consectetur. Eget amet lacus egestas amet porta
      sagittis pulvinar magna pretium. Lorem mauris vitae pellentesque platea
      velit volutpat magna sem. Elit eu sit facilisi nullam neque tincidunt sed
      volutpat metus. Amet sed at sed ante senectus sit bibendum tincidunt eu.
    </p>
  </header>
</main>

之后看各个部分的 CSS。

对于一些通用的样式这里就不详细介绍了,主要是设置了下页面的宽度,字体和字体大小、间距。

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  font-family: "Raleway", sans-serif;
}

main {
  width: 80vw;
  margin: 0 auto;
}

p {
  font-size: 16px;
  padding: 24px 0;
  line-height: 1.5em;
}

接下来我们设置 header 标签样式,它用来布局里边的所有元素,按照之前我们划分的表格区域,使用 grid 布局,设置 6 列,每列按照设计稿的尺寸计算需要占用的比例,或者根据需要进行调整,然后设置 7 行,第一行固定 40px,就是标题和最高的图片的空白,剩下的都设置为自动:

header {
  display: grid;
  grid-template-columns: 6fr 1fr 1fr 3fr 5fr 1fr;
  grid-template-rows: 40px repeat(6, auto);
}

接着,使用 grid-column 和 grid-row 来指定各个部分所占的表格区域,这里 h1 占据第一列,然后跨 3 列,一直到编号为 4 的列表格线。然后把它放到第 2 行,也就是编号 2 和 3 的行表格线之间。之后有一点注意,需要设置较大的一点的 z-index,因为它悬浮在图片上方:

h1 {
  grid-column: 1 / span 3;
  grid-row: 2 / 3;
  font-size: 65px;
  font-weight: 900;
  z-index: 10;
}

同样的设置标题下方描述所占据的区域:

.description1 {
  grid-column: 1 / 2;
  grid-row: 3 / 4;
}

在设置图片占据的区域,可以看到,第二张图片和大标题的列有重叠,它的列表格线编号从 3 开始,而大标题到 4 才结束。第二张和第三张图片也有重叠,就是设计稿上的效果:

.image1 {
  grid-column: 1 / 2;
  grid-row: 4 / span 2;
}

.image2 {
  grid-column: 3 / span 3;
  grid-row: 1 / span 6;
}

.image3 {
  grid-column: 5 / span 2;
  grid-row: 5 / span 3;
}

最后设置底部描述的位置:

.description2 {
  grid-column: 1 / span 4;
  grid-row: 7 / 8;
  padding-right: 48px;
}

这样我们就实现了设计稿中的布局。可以看到 CSS Grid 布局十分强大。

小结

好了,这个就是使用 CSS Grid 布局实现不规则布局的过程,你学会了吗?如果有帮助请三连并关注,想学更多的开发知识,可以在评论区留言,感谢观看!

CSS Grid Layout 实现复杂布局

冒泡排序是最简单的,也是效率最差的一种排序算法。我们学习冒泡排序一般都是为了练习编程语言中的双层循环,虽然没有什么实际的意义,但是有时候面试中可能还会考到,另外对于一些数据量比较小的数组排序,也可以使用它。

原理

冒泡排序的原理是,两两对比数组中的每个元素,如果后面的一个元素比前面的大,那么就交换它们的顺序,直到所有的元素都比对一遍。

因为在每次遍历后,都能找到最大的元素,并放到最右边,就像是冒泡,所以就有了冒泡排序这个名字。

例如,一个数组,它的元素是 [3, 1, 5, 9, 6, 2 ]。那么第一遍循环:

  • 先比对 3 和 1,3 比 1 大,所以交换顺序,变成了 1 和 3 。
  • 然后 3 和 5 再对比, 3 没有 5 大,不用交换位置。
  • 在比对 5 和 9 , 5 没有 9 大,也不用交换位置。
  • 9 再和 6 对比,9 大于 6 所以这两个交换位置。
  • 最后 9 再和 2 对比,9 比 2 大,把他们两个交换顺序。

这样第一遍最大的元素就找出来了,放到了最后一位,数组变成了 [1, 3, 5, 6, 2, 9]。

然后第 2 遍,

  • 比对 1 和 3,这两个不用交换。
  • 再比对 3 和 5,也不用交换。
  • 5 和 6 也不用交换。
  • 6 和 2 需要交换变成 2 和 6。
  • 最后 6 和 9 就不用对比了,因为 9 我们知道他是已经排序好的最大的元素。

这样我们的结果就变成了,135269。 好,按照这个顺序对比下去,我们下一次会得到 132569,再下一次得到 123569,这样全部遍历完成之后,排序的结果就是 123569。 我们来看一下代码:

function bubbleSort(arr) {
  for (let i = 0; i < arr.length; i++) {
    for (let j = 0; j < arr.length - i - 1; j++) {
      if (arr[j] > arr[j + 1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
      }
    }
  }
  return arr;
}

这里我定义了 bubbleSort函数:

好,最后排序完之后我们返回这个数组,我们用 315962 这个数组来测试一下,打印一下调用 bubbleSort 的函数之后,数组的排序结果,我们执行一下这个文件:

node bubble_sort.js

可以看到它打印出来了排序好后的数组,123569,

优化代码

上边的代码还可以继续优化一下:

function bubbleSort(arr) {
  let swapped;
  for (let i = 0; i < arr.length; i++) {
    swapped = false;
    for (let j = 0; j < arr.length - i - 1; j++) {
      if (arr[j] > arr[j + 1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
        swapped = true;
      }
    }
    if (!swapped) break;
  }
  return arr;
}

现在,我们的算法,如果内层循环已经排好序了,但是因为外层循环仍然需要遍历完所有次数,那么最后几次循环其实什么都没操作,所以我们可以在内部循环排好序之后,就停止外层的循环。判断是否排好序的依据是,内层循环没有发生元素交换。那么这样,我们可以先定义一个指示变量,swapped,在外层循环中,把它设置为 false,在内层循环中,如果发生了交换就把它设置为 true,当内层没有发生交换时,那么 swapped 就保持为 false,这样就可以中断外层循环了。

这时,我们在旧的排序算法函数中,外层循环的最后,打印一下 i 的值,可以看到它执行了 6 次:

function bubbleSort(arr) {
  for (let i = 0; i < arr.length; i++) {
    for (let j = 0; j < arr.length - i - 1; j++) {
      if (arr[j] > arr[j + 1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
      }
    }
    console.log(i);
  }
  return arr;
}

0;
1;
2;
3;
4;
(5)[(1, 2, 3, 5, 6, 9)];

在优化后的算法中,我们也打印一下 i 的值,可以看到它执行了 5 次:

function bubbleSort(arr) {
  let swapped;
  for (let i = 0; i < arr.length; i++) {
    swapped = false;
    for (let j = 0; j < arr.length - i - 1; j++) {
      if (arr[j] > arr[j + 1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
        swapped = true;
      }
    }
    console.log(i);
    if (!swapped) break;
  }
  return arr;
}

0;
1;
2;
3;
(4)[(1, 2, 3, 5, 6, 9)];

时间复杂度

冒泡排序在最坏情况下,时间复杂度是 O(n2),也就是说,当原数组完全是反向排序的,从大到小排列的,那么算法需要对每个元素都过一遍并且交换一次,第 1 遍需要比对和交换 n 个元素,第 2 遍是 n-1 第三遍是 n-2,以此类推一直到 0,加起来就是 n(n-1)/2,大 O 取得是近似值,所以算法复杂度就是 O(n2)。

小结

好了这个就是冒泡排序的简介和实现方式,你学会了吗?如果有帮助请三连,想学更多的前端开发知识,请关注峰华前端工程师,感谢观看!

冒泡排序

React 作为一个流行了很久的框架,在使用方式上,仍然需要有很多注意的地方,这个视频整理了 6 个推荐的 React 代码写法,来帮助你编写效率更高,更方便复用的组件。

使用函数式组件

第一个是使用函数式组件。React 在 16.8 版本的时候发布了 Hooks,在同一时间,class 组件就不推荐使用了。因为 class 组件的代码写起来比较繁琐,例如必须使用构造函数来获取 props 和定义 state,使用 render() 方法来返回 JSX。并且 class 组件有多种生命周期,相关的代码会分散在不同的方法中,不方便统一管理:

import React, { Component } from "react";

class MyComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      // Initial state
    };
  }

  componentDidMount() {
    // componentDidMount lifecycle method
  }

  componentDidUpdate(prevProps, prevState) {
    // componentDidUpdate lifecycle method
  }

  componentWillUnmount() {
    // componentWillUnmount lifecycle method
  }

  render() {
    return <div>{/* component JSX */}</div>;
  }
}

export default MyComponent;

使用函数式组件后,就和写普通 JavaScript 函数差不多,props 通过函数的参数获取,在返回语句中返回 JSX,其他的逻辑在函数体中直接编写就可以了,并且函数式组件没有生命周期,只需要使用 useEffect() hook 来控制组件的重新渲染就可以了:

import React from "react";

const MyComponent = (props) => {
  // Use hooks here, if needed
  useState();
  useEffect();
  // ...

  return <div>{/* component JSX */}</div>;
};

export default MyComponent;

利用 hooks 复用逻辑

第二个是利用 hooks 复用逻辑。hooks 的出现就是为了复用组件的业务逻辑的,大体上组件函数里所有的代码都可以放到 hooks 中:

import { useState } from "react";

export const useCounter = (initialCount = 0) => {
  const [count, setCount] = useState(initialCount);

  const increment = () => setCount(count + 1);
  const decrement = () => setCount(count - 1);
  const reset = () => setCount(initialCount);

  return { count, increment, decrement, reset };
};

在其他组件可以直接导入这些 hooks 并使用:

import React from "react";
import { useCounter } from "./useCounter";

const MyComponent = () => {
  const { count, increment, decrement, reset } = useCounter();

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
      <button onClick={reset}>Reset</button>
    </div>
  );
};

export default MyComponent;
import React from "react";
import { useCounter } from "./useCounter";

const MyOtherComponent = () => {
  const { count, increment, decrement, reset } = useCounter();

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
      <button onClick={reset}>Reset</button>
    </div>
  );
};

export default MyOtherComponent;

hooks 的出现也代替了旧版本中的 higher order components 和 render props 的写法。

少使用 state

第三是尽量少使用 state:

  • state 的变化会引起整个组件树的刷新,会带来一些性能上的损失,虽然 React 18 优化了 state 的批量更新,但同样有性能消耗。
  • state 的变化也比较难追踪到,调试起来比较困难。
  • 另外 state 会分散在不同的组件中,也不利于代码的维护

在编写组件时,只有在必须使用 state 的情况下再定义,我们可以通过 hooks 来减少状态的分散,例如使用 useReducer 结合 context 来集中管理状态,对于能经过其他状态计算而得出的数据,也不必定义为单独的状态。

定义可复用的组件

第四是定义可复用的组件来减少代码量,通过 props 来实现组件的自定义,利用 children 来方便组件之间的组合。组件里边可以根据 props 来展示不同的内容,或者处理不同的事件:

import React from "react";

const Button = ({ onClick, children }) => (
  <button onClick={onClick}>{children}</button>
);

export default Button;
import React from "react";
import Button from "./Button";

const MyComponent = () => (
  <div>
    <Button onClick={() => alert("Button clicked!")}>Click me</Button>
  </div>
);

export default MyComponent;

如果把这些都写在组件内部,那么这个组件就不能被其他组件复用,从而变成了一个只能处理一种情况的特殊组件。

import React from "react";

const Button = () => (
  <button onClick={() => alert("Button clicked")}>Click me</button>
);

export default Button;

利用 children 减少嵌套

第五个是利用 children 来减少组件之间的嵌套。当组件嵌套层次过多的时候,我们需要层层传递 props 到最深处的组件,虽然可以利用全局状态管理或者是 context 来传递,但毕竟需要额外的代码和配置:

import React from "react";

const CardContent = ({ content }) => <p>{content}</p>;

const SimpleCard = ({ content }) => (
  <div>
    <h1>Simple Card</h1>
    <CardContent content={content} />
  </div>
);

const App = () => <SimpleCard content="This is a card." />;

export default App;

更简单的解决方法就是利用 children。这样我们可以直接在最上层的组件来组装它们,之后传递 props 或者是 state 等等之类的就很方便了:

import React from "react";

const CardContent = ({ content }) => <p>{content}</p>;

const SimpleCard = ({ children }) => (
  <div>
    <h1>Simple Card</h1>
    <div>{children}</div>
  </div>
);

const App = () => (
  <SimpleCard>
    <CardContent content="This is a card." />
  </SimpleCard>
);

export default App;

避免不必要的 props

第六个是避免传递不必要的 props。尤其是当传递一个数据量比较大的 prop 的时候,它会占据很多的内存空间,并且影响性能。所以应该只传递子组件能够接收的 props:

const MyComponent = ({ prop1, prop2 }) => {
  return (
    <div>
      {prop1}
      {prop2}
    </div>
  );
};

const App = () => <MyComponent prop1="Hello" prop2="World" />;
// 传递不必要的大数据量:
const App = () => (
  <MyComponent prop1="Hello" prop2="World" prop3={ /* large data */ } />
);

小结

这个就是在编写 react 项目时的 6 个推荐的实践。你学会了吗?如果有帮助请三连并关注,想学更多的开发知识,可以在评论区留言,感谢观看!

React 开发 6 个需要注意的地方

JavaScript Temporal 日期和时间 API 是用来替代 Date 对象的,现在处于 stage 3 阶段,浏览器和 Node.js 正在对它进行实现。 Temporal API 解决了 Date 对象的一些缺点,例如:

  • 不支持时区,需要手动进行转换。
  • 只支持公历,不支持其他日历,例如我国的农历。
  • 不同浏览器对于同一个时间的计算方式不同,尤其是对于有夏令时的地区。
  • 日期的展示方式对于用户来说不够友好。
  • Date 对象是可变的,容易造成 bug。

这些问题在 Temporal API 中都得到了解决,接下来我们看一下 Temporal API 的常见的使用方法。

配置项目安装 polyfill

因为截止到目前 Temporal API 还没正式发布,我们需要个 Polyfill 来使用它。这里创建了一个 Node.js 项目,然后添加了 @js-temporal/polyfill这个 polyfill 依赖。 之后在 index.js 中导入它提供的 Temporal 对象:

const { Temporal } = require("@js-temporal/polyfill");

获取当前日期

获取当前时间可以通过 Temporal.Now.instance() 方法,这里注意这个 polyfill 需要我们手动调用 toString() 来获取日期字符串,并且这个日期是无关时区的:

console.log(
  "Current date and time(without a specific time zone): ",
  currentTime.toString()
);

我们也可以分别通过 epochMilliseconds 和 epochNanoseconds 来分别获取当前时间的毫秒数和纳秒数。Temporal 的时间精度可以精确到纳秒:

console.log("milliseconds: ", currentTime.epochMilliseconds);
console.log("nanoseconds: ", currentTime.epochNanoseconds);

创建自定义日期

Temporal 也支持创建自定义日期,可以调用 Temporal.PlainDateTime.from() 方法,然后传递一个对象参数,里边可以通过 year、 month、day、hour、minute 和 second 属性来分别指定年月日和时分秒:

const dateTime = Temporal.PlainDateTime.from({
  year: 2018,
  month: 2, // no longer need to plus 1
  day: 10,
  hour: 8,
  minute: 12,
  second: 48,
});

console.log(dateTime.toString());

另外也可以通过 Temporal.PlainDate 和 Temporal.PlainTime 来分别创建指定日期或时间。

修改日期

要修改日期的话,Temporal 提供了很方便的方法,可以调用 add() 方法来向后推移日期,也可以调用 substract() 向前推移,这两个方法都接收一个对象,里边的参数和创建日期时的一样,可以对年月日时分秒进行增加或减少,需要注意的是 temporal 对象是不可变的,需要把返回值保存到新的变量里边来获取修改后的日期:

const newDateTime = dateTime.add({ months: 1 });
console.log(newDateTime.toString());

比较日期

Temporal 还提供了 since() 方法来比较两个日期,比较的结果是一个对象,可以通过它可以获取相差的天数、月数、小时数等不同维度的时间:

const dt1 = Temporal.PlainDate.from("2023-02-01");
const dt2 = Temporal.PlainDate.from("2023-02-05");
const diff = dt2.since(dt1);
console.log(diff.days);

格式化日期

Temporal API 可以直接获取指定地区日期的特定格式,如果要完全自定义日期格式,还是需要分别获取年月日时分秒,来自行组装:

console.log(`${dt1.year}/${dt1.month}/${dt1.day}`);

获取指定时区的时间

使用 Temporal 获取的时间是无关时区的,如果要获取对应时区的时间,可以在获取当前时间后,调用 toZonedDateTimeISO() 方法,它接收 Temporal.TimeZone 对象实例作为参数,我们可以通过 Temporal.TimZone 对象来指定时区,例如传递 Asia/Shanghai 来获取中国时区:

console.log(
  "With time zone: ",
  currentTime
    .toZonedDateTimeISO(new Temporal.TimeZone("Asia/Shanghai"))
    .toString()
);

小结

好了,这个就是 JavaScript Temporal API 的简介和 6 中常见用法,你学会了吗?如果有帮助请三连并关注,想学更多的开发知识,可以在评论区留言,感谢观看!

JavaScript Temporal 日期 API 的 6 种常见用法

最新博客 

浅谈前端开发的现状和一些想法

张旭乾

最近一年多因为身体原因,有好长时间都没有好好的做视频、写东西了,只是拼着劲把课录完了,给已经付费的同学一个交代。现在感觉身体好一些了,看了看前端的现状,似乎和疫情时期没有什么变化,工作依旧不好找,网上的抱怨也越来越多。

前端死了吗

好多人都在说前端已经死了,当然本意肯定是指的工作死了,因为前端本身是不会死的,只要需要和人打交道,一个程序就必须有前端,没有的话,那么电脑和手机就只是一块黑屏。

10 个 CSS 1 行代码技巧

张旭乾

在前端开发过程中,利用 CSS 一行代码技巧可以解决一些常见问题,提高编码效率,使样式设置更加简洁。本文将分享 10 个 CSS 一行代码技巧。

1. 居中对齐

居中对齐是 CSS 中的常见需求。通过一行 CSS 代码,可以实现元素的水平垂直居中。

display: grid; place-items: center;

12个 JavaScript 一行代码技巧

张旭乾

JavaScript 是一种功能强大的编程语言,我们可以通过使用一行代码实现很多功能。在日常开发中,我们经常会遇到需要快速实现某些功能的情况。下面让我们一起看看 12个有用的 JavaScript 一行代码技巧。

1. 生成随机颜色

JavaScript 中可以通过以下方式快速生成一个随机颜色:

let randomColor = "#" + Math.floor(Math.random()*16777215).toString(16);

这行代码会生成一个六位的十六进制数,可以用作 CSS 颜色值。

2. 获取 URL 参数

如果你想获取 URL 参数,可以通过以下一行代码实现:

Vite 和 Webpack 的比较与区别

张旭乾

前言

在现代前端开发中,打包工具已经成为不可或缺的一部分。它们可以将多个文件合并为一个或多个文件,使前端应用的加载和运行速度更快。目前,Vite 和 Webpack 是最受欢迎的打包工具之一。本文将介绍它们之间的比较和区别,以帮助你选择适合自己项目的工具。

什么是 Vite?

Vite 是一种新型的前端构建工具,旨在提高开发过程中的开发体验和构建速度。它支持所有的现代前端框架,如 Vue、React、Angular等,以及原生的 HTML/CSS/JS 应用。

Vite 的核心理念是“快速开发”。它采用了一种新的开发方式,即在开发过程中不需要事先打包应用程序,而是在应用程序运行时即时编译和构建。这种方式使得开发者可以更快地编写代码,而无需等待长时间的编译和构建过程。

什么是 Webpack?

Webpack 是一个广泛使用的打包工具,为现代 Web 应用程序提供了强大的静态资源管理功能。Webpack 可以将多个文件打包成一个或多个文件,并使它们在浏览器中快速加载。Webpack 是一个高度可配置的工具,因此可以根据项目的需要进行定制。

Webpack 的核心理念是“模块化”。它支持使用各种语言和框架编写模块,并将它们打包成可在浏览器中使用的 JavaScript 文件。Webpack 还提供了强大的插件系统,可以扩展其功能。

如何使用 JSX 编写 React 组件

张旭乾

在 React 中,我们通常使用 JSX 来编写组件。JSX 是一种类似 HTML 的语法,它可以帮助我们更方便地编写组件,并且可以使代码更加易读和易于维护。在本文中,我们将介绍如何使用 JSX 编写 React 组件,并且将介绍 JSX 与 HTML 的区别、渲染列表、条件渲染、注册事件和传递 Props 相关知识。

React 框架简介

React 是一个由 Facebook 开发的用于构建用户界面的 JavaScript 库。它采用组件化的开发模式,使得开发者可以将一个 Web 应用程序拆分成多个组件,每个组件负责不同的功能,从而使得开发、测试和维护变得更加容易。

React 的主要特点是高效、可重用和灵活。它采用了 Virtual DOM 技术,可以最小化 DOM 操作,从而提高性能。另外,React 还拥有丰富的生态系统,包括 React Router、Redux、React Native 等等,可以帮助开发者更加高效地构建 Web 应用程序、移动应用程序和桌面应用程序等等。

10 种方法使用 CSS 水平居中一个元素

张旭乾

我们在前端开发中,经常会有居中某个元素的需求。因为 CSS 对于居中的方式有多种多样,在不同场景下有不同的效果,需要特别记住它们的应用场景才能够正常的居中元素。那么这篇文章,我们就看一下在 CSS 中,水平居中一个元素的不同方法和技巧,帮助你在前端开发中更加游刃有余。

使用 text-align 居中行内元素

text-align 不仅可以居中文本,还可以居中行内元素(如文本或图片)。把父元素的 'text-align' 设置为 center,然后把子元素的 display 属性设置为 inline 就可以了。

.parent-element {
text-align: center;
}

配置 Vite alias 别名导入,避免冗长的相对路径

张旭乾

在开发大型 Vue 项目的时候,有的组件会嵌套的很深,如果需要引入外层其他目录的组件,需要编写很长一段相对路径。假如有如下组件目录结构:

/Layout/Header/NavBar.vue
/Base/BaseLink.vue

如果在 NavBar 中引入 BaseLink 组件,需要使用下面的路径进行导入:

NavBar.vue
<script>
import BaseLink from '../../Base/BaseLink.vue';
</script>

这里使用了两个 ../ 才访问到 Base 目录,如果这个时候,我们给 ../../ 设置一个别名,例如 @,那么就可以使导入路径更简洁,变成:

@/Base/BaseLink.vue

Vite 支持设置路径别名,来支持这种形式的导入。

Vue3 表单提交事件处理

张旭乾

这篇文章我们看一下对于表单整体的提交事件应该如何处理,并引入一个事件修饰符的概念。 我们继续使用上篇文章的项目示例,这里我把 html 中,class 为 form 的 div 改成了 form 元素,CSS 的样式也作了对应的调整。

<div class="form">
->
<form></form>
</div>

现在,我们在 form 元素的结束标签之前,添加一个 button 按钮元素,作为表单的提交按钮,点击它表单就会提交。

<textarea id="intro" rows="10" v-model="intro"></textarea>
<!-- 在这里添加 -->
<button type="submit">提交</button>

Vue 3 常见表单控件事件处理

张旭乾

上篇文章我们学习了如何使用 v-model 绑定 input 输入框和 data 中的属性,这篇文章我们来看一下其它表单控件的绑定方法。

示例

这里示例大体的样式和结构我已经写好了,你可以从视频里附带的源代码示例里,直接编写代码。用户名这个我们上篇文章介绍了,这个就先不管它了。

单选按钮

我们先看一下单选框的数据绑定。假如我们让用户选择一下性别,男和女。我们在 HTML 模板中:

  • 定义一个 type 为 radio 的 input,name 设置为 gender,这里的 name 相同的 radio 单选框才能形成一个单选按钮组,这样选择是互斥的。
  • value 设置为 male,表示男性,这个是单选按钮选中之后,实际获取到的值。这里之所以用英文,是遵循了软件开发的一些最佳实践,方便后端程序或数据库,用枚举的形式存储数据,而枚举的属性名一般用英文。
  • 在 input 后面,我们用一个 span 元素显示给用户看的值,这里写上『男』。
  • 这里用 v-model 绑定一个名为 gender 的属性,这个我们稍后再在 data 里定义。

Vue 3 表单输入控件数据处理

张旭乾

在 Vue 中处理表单输入数据处理有两种方式,一种是使用传统的事件监听方式,另一种是使用 v-model,这篇文章我们看一下如何使用事件监听的方式处理表单输入数据。

input 事件处理

要监听 input 输入框的事件,我们使用 @input 事件,在用户输入每个字符的时候,都会触发这个事件。

<input id="username" type="text" @input="handleInput" />

我们可以在事件处理函数中,直接访问到 event 这个 javascript 原生事件对象,这样可以像普通 js 一样,使用 event.target.value 来访问输入框的值: