前端速成
这是一篇前端速成的文章,包含html,css,js,vue3等
前端三件套
前端主要由三个部分组成,html,css,js.它们分别负责页面,效果,动态处理
HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<!DOCTYPE html>
<html>
<body>
<div style="background-color: red;">
<h1>
这是一级标题
</h1>
<h2>
这是二级标题
</h2>
<p>这是一个文本</p>
<p>换行<br>换行</p>
</div>
<p><b>加粗</b><i>斜体</i><u>下划线</u></p>
<p><span style="background-color: aqua;">aaaaa</span><span style="background-color: violet;">bbbbb</span><span style="background-color: burlywood;">ccccc</span></p>
<img src="https://ts1.cn.mm.bing.net/th/id/R-C.3e42ad7c7744a7a612bfbaa480405611?rik=eX46Dun%2fFF1aDg&riu=http%3a%2f%2fpic.bizhi360.com%2fbbpic%2f25%2f6125.jpg&ehk=ukEQFHgNgClnZxjkosi%2bP3OoaZicxvcitCFcgJISxww%3d&risl=&pid=ImgRaw&r=0" width="500px">
<a href="https://www.bilibili.com">b站</a>
<ol>
<li>first item</li>
<li>second item</li>
</ol>
<ul>
<li>first</li>
<li>second</li>
</ul>
<table border="2">
<thead>
<tr>
<td>头部1</td>
<td>头部2</td>
<td>头部3</td>
</tr>
</thead>
<tbody>
<tr>
<td>111</td>
<td>222</td>
<td>333</td>
</tr>
<tr>
<td>444</td>
<td>555</td>
<td>666</td>
</tr>
<tr>
<td>777</td>
<td>888</td>
</tr>
</tbody>
</table>
</body>
</html>
css
css是对于html的一个补充,比如可以对于一段文字,直接写在html中平平无奇,我想将其改变颜色应该怎么办呢,这就用到css了.css会对html中的元素进行外观行为上的补充.
选择器
很容易想到的问题是,它们之间是怎么联系起来的呢,是用到一个类似标签的系统,也叫选择器.一共有三种选择器,元素,类与id选择器.其中类选择器是最常用的.
1
2
3
4
5
6
7
<h4>元素选择器</h4>
<p class="classSelector">类选择器</p>
<p id="idSelector">id选择器</p>
<div class = "parent">父类
<div class="child">子类</div>
</div>
在css中分别对其外观修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
h4{
color:red;
}
.classSelector{
color:#FF0000;
}
#idSelector
{
color:rgb(87, 21, 113);
}
.parent{
.child{
color:green;
}
}
元素选择器的判别标准是html标签,但是这样很难辨别具体想要链接的部分,所以很少使用 对于id选择器的搜索优先度最高,但是id不能重复
属性
- color:除了上面提到的几种,还有背景颜色,透明度等
- text:
- 字体:
font_family:'....'
- 字号:
font_size:24px
- 行高:
line_height:32px
- 字体:
- 盒子模型:每个元素都是一个盒子,由三层组成,每一层的上下左右的4个属性都是可以设置的
margin_right:20px
代表右侧20像素border:20px solid black
代表边框黑色实线
布局
- 独占一行的元素,
p div h
后者设置属性display:block
- 不会独占的
span
,或者`display:inline - 隐藏元素,不显示.
display:none
- 弹性布局,
flex
属性,可以完成居中等操作.待补充 - 网格布局,
grid
属性,和上面的弹性布局一样,需要副容器.即在类div内部嵌套多个div作为基础元素.这一布局就是规定这些元素的排列方式的
定位
比如我想让一个图片出现在我的容器的右下角,怎么办呢?这时就需要定位这一工具了 有三种定位方式.不写是static(静态)定位
- 相对位置: 相对自身的正常位置定位
1 2 3 4
.relative{ position:relative; height:200px; }
- 绝对位置:相对其最近的非静态定位的父元素
1
2
3
4
5
.absolute{
position:absolute;
top:45%;
left:50%;
}
- 固定位置:相对于浏览器窗口,因此一般用做导航栏,侧边栏.不随用户滑动而移动,看起来像是固定在窗口上那样
1
2
3
.fixed{
postion:fixed;
}
JavaScript
js与py很像,都是弱类型的语法.
- 变量定义前面需要加上
var let const
,其中var代表全局,一般不用 - 字符串的split等操作与py一致
- 循环语法与c一致
对于js的引用一般放到html的尾部,即后body标志前面,像这样
1
2
3
<script src=""></script>
</body>
</html>
与前端相关的部分语句
document
前端中,完成数据交换的主要通过客户端的js完成,而这当中主要通过document来完成
- 介绍 document是网页文档的根节点,只要网页创建,这个节点就存在了.
document.childNodes
返回所有子节点,一般有两个子节点. 第一个子节点是document.doctype,表示文档类型节点(DocumentType)。对于HTML5文档来说,该节点就代表<!DOCTYPE html>。
第二个子节点是document.documentElement,表示元素节点(Element),代表:<html lang="en">
。
- 属性
属性 | 说明 |
---|---|
title | 等价于html里的title |
bgcolor | 页面背景颜色 |
fgcolor | 文本颜色 |
fileSize | 文件大小,只读 |
URL | 设置URL属性从而在同一窗口打开另一网页 |
- 一些其他节点的属性
1 2 3 4
document.doctype // <!DOCTYPE html> document.documentElement //返回文档的根节点 <html>...</html> document.head // <head>...</head> document.body // <body>...</body>
- 指向特定元素集合的属性
1 2 3 4 5 6
document.all :文档中所有的元素,Firefox不支持该属性。 document.forms :所有的form元素。 document.images:所有的img元素。 document.links:所有的a元素。 document.scripts:所有的script元素。 document.styleSheets:所有的link或者style元素。
- 方法
方法 | 说明 |
---|---|
document.write() | 动态向页面写入内容 |
document.createElement(Tag) | 创建一个html标签对象 |
document.getElementById(ID) | 获得指定ID值的对象 |
document.getElementsByTagName(tagname) | 获得指定标签名的对象 |
document.getElementsByName(Name) | 获得指定Name值的对象 |
document.getElementsByClassName(classname) | 获得指定类名的对象(html5 API) |
VUE
前面的那些属于原生js,做出来的东西被师兄吐槽不好看,因此下面学习vue. vue是一种类似框架的东西,简单来说就是用一些约定换取开发效率,比如对于事件监听就不需要docm的监听方法,而是可以直接在html里绑定.下面采用选项式与单文件组件的格式进行记录
单文件组件
Vue 的单文件组件是网页开发中 HTML、CSS 和 JavaScript 三种语言经典组合的自然延伸。<template>
、<script>
和 <style>
三个块在同一个文件中封装、组合了组件的视图、逻辑和样式。示例如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script>
export default {
data() {
return {
greeting: 'Hello World!'
}
}
}
</script>
<template>
<p class="greeting"></p>
</template>
<style>
.greeting {
color: red;
font-weight: bold;
}
</style>
声明式渲染
vue的核心功能是声明式渲染,即html根据js的变化响应式的改变.即html里可以直接使用js里面的变量,格式为``
<script>
export default {
data() {
return {
message: 'Hello World!',
counter: {
count: 1
}
}
}
}
</script>
<template>
<h1></h1>
<p>Count is: </p>
</template>
- export的作用是可以被外部引用
- data的作用是声明响应式数据
- return的作用是为每一个实例创建副本.举个例子,如果data被复用到多个按钮组件上了,如果没有创建副本,那么每一个按钮实际上都是对同一个数据对象进行操作,影响逻辑的正确性
- 这里的message会随着data里的变化而变化,counter是一个类变量
Attribute绑定
我们上面讲到了可以将内容与js中的变量绑定,那么我们想将html块中的属性也进行绑定应该怎么做呢,这时候就需要v-bind指令,写作<div v-bind:id="dynamicId"></div>
简化写法可以是<div :id="dynamicId"></div>
,其中id代表想要绑定的属性,后面的双引号内容代表绑定的值的变量名.这里同样是绑定的data组件里的变量,就不加以演示了
事件监听
与原生js需要用docm的方法进行监听不同,vue的要简单许多,可以使用v-on指令进行监听. 写作<button v-on:click="func">9</button>
,简化版<button @click="func">9</button>
. func是绑定的函数,只要每次执行到click就会执行func. func需要再method中定义
export default {
data() {
return {
count: 0
}
},
methods: {
increment() {
// 更新组件状态
this.count++
}
}
}
表单绑定
假设这样一个情景,有一个输入框,我们根据用户输入动态调整输出.此时就需要将输入内容双向绑定到我们的变量.根据前面的知识点,可以这样写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script>
export default {
data() {
return {
text: ''
}
},
methods: {
onInput(e) {
this.text = e.target.value
}
}
}
</script>
<template>
<input :value="text" @input="onInput" placeholder="Type here">
<p></p>
</template>
即输入的时候调用onInput函数将值传递到text中去.不过这样写很费劲,有没有什么简单的写法? 有的兄弟有的,使用v-module指令就可以了.<input v-model="text">
这一条可以完全取代前面的<input :value="text" @input="onInput">
. v-model 会将被绑定的值与 <input>
的值自动同步,这样我们就不必再使用事件处理函数了。 v-model 不仅支持文本输入框,也支持诸如多选框、单选框、下拉框之类的输入类型
条件渲染
假设这样一个情景,需要根据条件动态显示不同的文字,这时候就需要用到条件渲染了,写作
1
2
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>
根据awesome的值的真假来确定渲染哪个
列表渲染
假设这样一个情景,我们想要实现一个待办列表,那么想要实现就需要一个能遍历数组的东西.可以使用v-for
指令
1
2
3
4
5
<ul>
<li v-for="todo in todos" :key="todo.id">
</li>
</ul>
代码的作用是遍历todos里面的元素,输出里面的text.将唯一的id绑定到key属性上,可以避免删除某一项引起的混乱. 这是必须使用且必须唯一的元素
计算属性
我们下一步想做的是隐藏上一步完成的todo,即需要根据是否完成动态的渲染列表. 这个时候需要一个新的属性,计算属性.我们可以使用 computed
选项声明一个响应式的属性,它的值由其他属性计算而来:
1
2
3
4
5
6
7
8
9
10
export default {
// ...
computed: {
filteredTodos() {
return this.hideCompleted
? this.todos.filter((t) => !t.done)
: this.todos
}
}
}
将遍历修改为
1
2
- <li v-for="todo in todos">
+ <li v-for="todo in filteredTodos">
就可以实现想要的功能了.
对于上面的计算属性,虽然method也可以达到类似的功能,但是,method需要每次都显示调用,但是计算属性只有他的依赖修改后,才会重新计算.这可以显著节约计算资源
生命周期与模版引用
生命周期:beforeCreate → created → beforeMount → mounted, mounted为挂载后,即在 mounted 之前(如 created 或 beforeMount),Vue 尚未将模板渲染为真实 DOM,此时所有对 DOM 的访问都是无效的。 这也意味着mounted是某种入口阶段,在组件首次渲染到 DOM 后立即触发(仅一次)。许多初始化操作都在这里运行
虽然vue可以解决大部分DOM更新,但是有一些还是需要我们手动解决,这就需要下面的工具模版引用, 写作<p ref="pElementRef">hello</p>
此元素将作为 this.$refs.pElementRef
暴露在 this.$refs
上。然而,你只能在组件挂载之后访问它。 也就是说,对于他的修改需要再mounted组件下修改,比如
1
2
3
4
5
export default {
mounted() {
this.$refs.pElementRef.textContent = 'mounted!'
}
}
这样对于html中的ref=’pElementRef’的元素的内容就会被修改,注意这个名字不要重复,不认会被后面的覆盖
倾听器
假设一个情况,当一个元素的ID改变时,执行fetch操作,这需要一个侦听操作.举例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<script>
export default {
data() {
return {
todoId: 1,
todoData: null
}
},
methods: {
async fetchData() {
this.todoData = null
const res = await fetch(
`https://jsonplaceholder.typicode.com/todos/${this.todoId}`
)
this.todoData = await res.json()
}
},
mounted() {
this.fetchData()
},
watch: {
todoId() {
this.fetchData()
}
}
}
</script>
<template>
<p>Todo id: </p>
<button @click="todoId++" :disabled="!todoData">Fetch next todo</button>
<p v-if="!todoData">Loading...</p>
<pre v-else></pre>
</template>
组件
作用是导入别的文件的组件作为子组件,导入方式和py很像
1
2
3
4
5
6
7
8
import ChildComp from './ChildComp.vue'
export default {
components: {
ChildComp
}
}
父组件可以通过ref属性调用子组件的method
1
2
3
4
5
6
7
8
9
10
11
12
<template>
<!-- 获取子组件实例 -->
<ChildComponent ref="childComp" />
</template>
<script>
export default {
mounted() {
this.$refs.childComp.someMethod() // 调用子组件方法
}
}
</script>
然后可以在模版中使用它了<ChildComp />
只需要这一块就能把子模块的整个模版导入进来
props
props是父组件向子组件传递数据的核心机制。 在父组件中声明要传递的变量
1
<ChildComp :msg="greeting" />
在子组件中声明传递过来的变量
1
2
3
4
5
6
// 在子组件中
export default {
props: {
msg: String
}
}
Emits
除了接受来自父组件的props,还可以像父组件触发事件 同样的,子组件要声明要触发的事件
1
2
3
4
5
6
7
8
export default {
// 声明触发的事件
emits: ['response'],
created() {
// 带参数触发
this.$emit('response', 'hello from child')
}
}
this.$emit() 的第一个参数是事件的名称。其他所有参数都将传递给事件监听器。
emits: ['response']
起一个声明的作用,虽然不是必须,但是写上最好
父组件可以使用 v-on 监听子组件触发的事件——这里的处理函数接收了子组件触发事件时的额外参数并将它赋值给了本地状态: <ChildComp @response="(msg) => childMsg = msg" />
插槽(slots)
除了上述两个功能,父组件还可以通过插槽将组件片段传递给子组件 首先在父组件向子组件传递 <ChildComp> This is some slot content! </ChildComp>
之后在子组件接受 <slot>Fallback content</slot>
,这一部分的内容是默认内容,如果在父组件有传递的消息,就会替换为那个消息
VUE与后端的交互
如何连接前后端服务器
以pycharm与webstrom为例: 同时将flask与VUE一起启动,在VUE的config文件中配置请求转发,比如这样
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
export default defineConfig({
plugins: [
vue(),
vueDevTools(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
server: {
proxy: {
'/api': {
target: 'http://localhost:5000', // Flask 后端地址
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '') // 路径重写
}
}
}
})
Axios
(感觉越学越多)
从后端获取数据
首先需要一个后端的GET方法的URL, 之后将这个URL作为参数请求数据
1
2
3
4
5
6
7
8
const fetchTime = async () => {
try {
const response = await axios.get('http://localhost:5000/api/time')
serverTime.value = response.data.time
} catch (error) {
console.error('请求失败:', error)
}
}
async
代表异步, await
与其成对出现. response
是后端返回的数据,结构如下
1
2
3
4
5
6
7
const response = {
data: {}, // 核心数据(服务器返回的业务数据)
status: 200, // HTTP 状态码(如 200、404 等)
statusText: 'OK', // 状态描述文本
headers: {}, // 响应头信息
config: {} // 本次请求的配置信息
}
向后端提交数据
同样的需要一个目标url
1
2
3
4
5
6
7
8
9
10
11
12
const submitData = async () => {
try {
const response = await axios.post(
'http://localhost:5000/api/submit',
{ content: inputText.value },
{ headers: { 'Content-Type': 'application/json' } }
)
responseResult.value = response.data.received_content
} catch (error) {
console.error('提交失败:', error)
}
}
axios.post
:发送 POST 请求的快捷方法
参数1:后端 API 地址
参数2:请求体数据(会被自动转换为 JSON 字符串)
参数3:配置对象,设置请求头为 JSON 格式
后端获取数据
1 2 3 4 5 6 7 8 9 10 11 12
# 添加 POST 接口 @app.route('/api/submit', methods=['POST']) def handle_submit(): data = request.get_json() if not data or 'content' not in data: return jsonify({"error": "无效数据"}), 400 return jsonify({ "status": "success", "received_content": data['content'], "method": request.method })
通过request获取前端提交的数据
总结
其实整个项目就是前端与后端,还有交互部分.前端负责显示内容,从用户获取数据. 后端负责计算,根据前端的数据进行计算并返回.最后就是两者的交互,因此只需要定义好两者的接口,即api的url.即可实现前后端分离的开发模式